HollaEx®
⚙️ DashboardStart →
  • HollaEx® — The Crypto Exchange Solution
  • ☁️Cloud Operators
    • Launching the Exchange
    • Setting Domain for Cloud Exchanges
    • Easy SMTP for Cloud Exchanges
    • SEO Settings for Cloud Exchanges
      • SEO Advanced Settings
  • ⚙️Operating Your Exchange
    • Operator Control Panel
      • General
      • Users
      • User Profile
      • Assets
      • Markets
      • Stakes
      • Sessions
      • Plugins Apps
      • Trading Fees & Account Tiers
      • Roles
      • Chat
      • Billing
    • Customize Exchange
      • Browser Tools
        • Enter Edit Mode
        • Operator Controls (Visuals)
        • Console
      • Plugins
      • Forked Repo
    • Fiat Controls
      • Initial Setup
      • Setting Up Fiat On/ Off Ramp
      • Editing Deposit & Withdrawal Fees
      • Users Making Fiat Deposit
      • Users Trading With Fiat
      • User Making Fiat Withdrawal
    • Staking
    • OTC Broker
    • P2P
      • P2P Overview
      • P2P Setup
      • P2P Troubleshooting
      • P2P Vendor Flow
    • Smart Chain Trading
    • Assets & Trading Pairs
      • Add New Assets & Trading Pairs
      • Configure Pair Parameters
    • Set up the SMTP Email
      • Set up SMTP with AWS SES
      • Set up SMTP with Mailgun
      • Set up SMTP with SendGrid
      • Test the SMTP with Gmail
    • Enabling reCAPTCHA
    • Email Customization & Audit
    • DeFi Asset Staking Process
  • 🧩Plugins
    • HollaEx Plugins
      • Announcements
      • Bank
      • AWS SNS (Text Messages - SMS)
      • KYC
      • Automatic KYC
      • Messente
      • Advanced Referral
      • CoinMarketCap
      • Guardarian
    • Install Plugins
    • Developing Plugins
      • Development Walkthrough: Hello-Plugin
        • Initialization
        • Configuration
        • Scripting
        • Web View
        • The Final Product & Installation
      • Advanced
        • Initialization
        • Config
        • Server Script
        • Plugin Libraries
        • Web View
        • Final Plugin Product
        • Advanced Tutorial: Using the user meta field
        • Advanced Tutorial: Adding a new database table column
        • Advanced Tutorial: Creating a new database table
      • Simple Wallet Example
      • Web View Development
        • Overview
        • External dependencies
        • Getting started
        • Basic Tutorial: Hello Exchange Plugin web view
        • Advanced Tutorial: KYC Plugin web views
    • Bank Integration
      • Handling Deposits
      • Handling Withdrawals
  • 👷Developers
    • API Guide
      • API Example Scripts
    • Run Dev Mode
    • Build a New Front-end Interface
  • 🧰On-Premise Operators (Advanced Only)
    • On-Premise Exchange Setup
      • Getting Started — Requirements
      • Installation
      • Server Setup
      • Web Setup
      • Production
    • CLI How-Tos
      • Start Exchange
      • Stop Exchange
      • Upgrade Exchange
        • Build and Apply the Code Changes
      • Get Exchange Logs
      • Get a Backup and Restore
      • Exchange Migration
      • Command List
    • Run Exchange on Kubernetes
    • Troubleshooting Guide
  • 🚀Advanced
    • SEO Optimization
    • Nginx
    • Rate Limits
    • Database
      • Upgrade Database
    • Dependencies
    • Contents Delivery Network
      • Cloudflare CDN for HollaEx
      • CloudFront CDN for HollaEx
    • Load Balancer
      • AWS ELB
      • DigitalOcean LB
    • Customize Kubenretes Ingress
    • Exchange Keys
      • Exchange API Keys Troubleshoot
    • HollaEx on non-Linux
      • HollaEx on Windows
      • HollaEx on macOS
    • The Network Tool Library
      • Accessing the Network Tool Library
      • Functions
        • WebSocket
      • Simple Example: Creating a User and Wallet
      • Getting More Interesting: Orders with the Tools
        • Setup: Using the transferAsset function
        • Creating and Monitoring a Sell Order
        • Settling Fees
      • Private HollaEx Network
    • Docker Content Trust (DCT)
    • Revenue Sharing
  • 📦Releases
    • Release Notes
    • Side Notes
  • ➡️External Links
  • Blogs
  • Forum
  • Videos
  • Twitter X
  • Telegram
  • Interactive Demo
  • Discord Community
  • API Documentation
  • Tools Library Documentation
  • Node Library Documentation
  • Plugins Documentation
Powered by GitBook
On this page
  • Script
  • Breakdown
  • Import Requirements
  • Set meta field name
  • Check if meta field exists in init
  • Create an endpoint for counting user trades
  1. Plugins
  2. Developing Plugins
  3. Advanced

Advanced Tutorial: Using the user meta field

In this tutorial, we will create a plugin that allows an admin to check the number of trades a user has made. Once checked, the plugin will store the timestamp of the last trade completed by the user in meta. The next time the plugin checks the user's trade amount, it will only count trades made after the user's stored trade timestamp.

We will focus on the script section for this plugin.

The user meta field is a JSON object in the User DB model that we can use to add additional fields for users.

Script

'use strict';

const {
	app,
	loggerPlugin,
	toolsLib
} = this.pluginLibraries;
const { query, validationResult } = require('express-validator');
const moment = require('moment');

const USER_META_FIELD_NAME = 'trade_count_plugin-latest_trade';

const init = async () => {
	loggerPlugin.info(
		'PLUGIN TRADE COUNT initializing...'
	);

	if (!toolsLib.getKitConfig().user_meta[USER_META_FIELD_NAME]) {
		loggerPlugin.verbose(
			'PLUGIN TRADE COUNT',
			`User meta field ${USER_META_FIELD_NAME} not found. Creating field.`
		);

		await toolsLib.addKitUserMeta(
			USER_META_FIELD_NAME,
			'date-time',
			'Last trade timestamp used for trade-count plugin',
			false
		);
	}

	loggerPlugin.info(
		'PLUGIN TRADE COUNT initialized'
	);
};

init()
	.then(() => {
		app.get(
			'/plugins/trade-count/check',
			[
				toolsLib.security.verifyBearerTokenExpressMiddleware(['admin']),
				query('user_id').isInt({ min: 1 }).toInt().optional()
			],
			async (req, res) => {
				const errors = validationResult(req);
				if (!errors.isEmpty()) {
					return res.status(400).json({ errors: errors.array() });
				}

				const { user_id } = req.query;

				loggerPlugin.verbose(
					req.uuid,
					'GET /plugins/trade-count/check auth',
					req.auth.sub,
					'user_id:',
					user_id
				);

				try {
					const user = await toolsLib.user.getUserByKitId(user_id, false);

					if (!user) {
						throw new Error('User not found');
					}

					const lastTradeTimestamp = user.meta[USER_META_FIELD_NAME];
					const queryStartDate = toolsLib.isDatetime(lastTradeTimestamp)
						? moment(lastTradeTimestamp).add(1, 'ms').toISOString()
						: null;

					loggerPlugin.verbose(
						req.uuid,
						'GET /plugins/trade-count/check',
						'user last trade timestamp',
						lastTradeTimestamp,
						'trades query start date',
						queryStartDate
					);

					const trades = await toolsLib.order.getAllUserTradesByKitId(
						user.id,
						null,
						1,
						1,
						'timestamp',
						'desc',
						queryStartDate
					);

					loggerPlugin.verbose(
						req.uuid,
						'GET /plugins/trade-count/check',
						'trade count',
						trades.count
					);

					if (trades.count === 0) {
						return res.json({
							count: 0
						});
					}

					const lastTrade = trades.data[0];

					loggerPlugin.verbose(
						req.uuid,
						'GET /plugins/trade-count/check',
						'last trade timestamp',
						lastTrade.timestamp
					);

					await user.update({
						meta: {
							...user.meta,
							[USER_META_FIELD_NAME]: lastTrade.timestamp
						}
					});

					return res.json({
						count: trades.count
					});

				} catch (err) {
					loggerPlugin.error(
						req.uuid,
						'GET /plugins/trade-count/check err',
						err.message
					);

					return res.status(400).json({ message: err.message });
				}
			}
		);
	})
	.catch((err) => {
		loggerPlugin.error(
			'PLUGIN TRADE COUNT err during initialization',
			err.message
		);
	});

Breakdown

Import Requirements

const {
	app,
	loggerPlugin,
	toolsLib
} = this.pluginLibraries;
const { query, validationResult } = require('express-validator');
const moment = require('moment');

First, import all the libraries that are required for this plugin.

Set meta field name

const USER_META_FIELD_NAME = 'trade_count_plugin-latest_trade';

All user meta fields require a name. Here, we are calling the meta field trade_count_plugin-latest_trade. We recommend formatting meta field names for plugins as <PLUGIN_NAME>-<FIELD_NAME>

Check if meta field exists in init

const init = async () => {
	loggerPlugin.info(
		'PLUGIN TRADE COUNT initializing...'
	);

	if (!toolsLib.getKitConfig().user_meta[USER_META_FIELD_NAME]) {
		loggerPlugin.verbose(
			'PLUGIN TRADE COUNT',
			`User meta field ${USER_META_FIELD_NAME} not found. Creating field.`
		);

		await toolsLib.addKitUserMeta(
			USER_META_FIELD_NAME,
			'date-time',
			'Last trade timestamp used for trade-count plugin',
			false
		);
	}

	loggerPlugin.info(
		'PLUGIN TRADE COUNT initialized'
	);
};

init()
	.then(() => {...})
	.catch((err) => {
		loggerPlugin.error(
			'PLUGIN TRADE COUNT err during initialization',
			err.message
		);
	});

Next, we create an init function that creates the new user meta field if it doesn't already exist. All user meta fields can be found in the Kit config object user_meta. Each field in user_meta has a type, required and description value.

// KIT CONFIG

{
    ...,
    "user_meta": {
        "trade_count_plugin-latest_trade": {
            "type": "date-time",
            "required": false,
            "description": "Last trade timestamp used for trade-count plugin"
        }
    },
    ...
}

We are first checking if the user meta field exists in the Kit config returned from toolsLib.getKitConfig. If not, we are creating the new field using the tools library function toolsLib.addKitUserMeta. Please refer to the tools library documentation for more information regarding the tools library. Once initialized, the main script will be executed.

Create an endpoint for counting user trades

app.get(
	'/plugins/trade-count/check',
	[
		toolsLib.security.verifyBearerTokenExpressMiddleware(['admin']),
		query('user_id').isInt({ min: 1 }).toInt().optional()
	],
	async (req, res) => {
		const errors = validationResult(req);
		if (!errors.isEmpty()) {
			return res.status(400).json({ errors: errors.array() });
		}
		
		const { user_id } = req.query;

		loggerPlugin.verbose(
			req.uuid,
			'GET /plugins/trade-count/check auth',
			req.auth.sub,
			'user_id:',
			user_id
		);

For this plugin, we need an endpoint GET /plugins/trade-count/check that returns the number of trades a user has made.

try {
	const user = await toolsLib.user.getUserByKitId(user_id, false);

	if (!user) {
		throw new Error('User not found');
	}

	const lastTradeTimestamp = user.meta[USER_META_FIELD_NAME];
	const queryStartDate = toolsLib.isDatetime(lastTradeTimestamp)
		? moment(lastTradeTimestamp).add(1, 'ms').toISOString()
		: null;

	loggerPlugin.verbose(
		req.uuid,
		'GET /plugins/trade-count/check',
		'user last trade timestamp',
		lastTradeTimestamp,
		'trades query start date',
		queryStartDate
	);

	const trades = await toolsLib.order.getAllUserTradesByKitId(
		user.id,
		null,
		1,
		1,
		'timestamp',
		'desc',
		queryStartDate
	);

	loggerPlugin.verbose(
		req.uuid,
		'GET /plugins/trade-count/check',
		'trade count',
		trades.count
	);

	if (trades.count === 0) {
		return res.json({
			count: 0
		});
	}

	const lastTrade = trades.data[0];

	loggerPlugin.verbose(
		req.uuid,
		'GET /plugins/trade-count/check',
		'last trade timestamp',
		lastTrade.timestamp
	);

	await user.update({
		meta: {
			...user.meta,
			[USER_META_FIELD_NAME]: lastTrade.timestamp
		}
	});

	return res.json({
		count: trades.count
	});

} catch (err) {
	loggerPlugin.error(
		req.uuid,
		'GET /plugins/trade-count/check err',
		err.message
	);

	return res.status(400).json({ message: err.message });
}

We are first checking to see if a user with the given user_id exists. If so, we check to see if the user's meta.trade_count_plugin-latest_trade value exists. If it doesn't that means we haven't checked the user's trade count using this plugin before. If it does, that means we have previously checked the user's trade count.

Using the found trade_count_plugin-latest_trade value, we can determine which date to query the user's trades from. If trade_count_plugin-latest_trade was null, then we will query all of the user's trades.

Once we get the trades response, we can read the number of trades made and the timestamp of the last trade completed. If the count is 0, then we can just return 0 without updating the user's trade_count_plugin-latest_trade value. Otherwise, we will get the latest trade's timestamp, update the user's trade_count_plugin-latest_trade value to that timestamp, and return the count. The next time we check the user's trade count, all trades starting from the updated trade_count_plugin-latest_trade will be counted.

PreviousFinal Plugin ProductNextAdvanced Tutorial: Adding a new database table column

Last updated 3 years ago

🧩