Manually Review Large Deposit Transactions

First, you’ll need to disable the automatic deposit processing feature in your Operator Control Panel (introduced in v2.17). You can find this setting under:

General → Security

Once auto-deposit is turned off, all incoming crypto deposits will remain in an “on hold” state, meaning they will require manual approval unless you automate the process yourself.

From there, you can create a simple plugin, similar to the example below that retrieves all deposits where onhold: true, checks their value, and processes them accordingly.
For example:

  • If the deposit amount is under $1,000, the plugin can automatically approve and process it.
  • If the amount is over $1,000, the deposit will remain on hold, allowing you to manually review and verify it through the Operator Control Panel.

This setup gives you full control while still allowing smaller deposits to be handled automatically.

'use strict';

const { publicMeta, meta } = this.configValues;
const {
	app,
	loggerPlugin,
	toolsLib
} = this.pluginLibraries;

const init = async () => {
	loggerPlugin.info(
		'/plugins/txanalysor/init/initializing'
	);
};

const parseNumber = (v, fallback = 0) => {
	const n = Number(v);
	return Number.isFinite(n) ? n : fallback;
};

const valueInQuote = async (currency, amount, quote) => {
	const sym = String(currency || '').toLowerCase();
	const tgt = String(quote || '').toLowerCase();
	if (!sym || !amount || !tgt) return null;
	try {
		if (sym === tgt) return parseNumber(amount, null);
		const prices = await toolsLib.getAssetsPrices([sym], tgt);
		const rate = prices && prices[sym];
		if (typeof rate !== 'number' || !(rate > 0)) return null;
		return parseNumber(amount, null) * rate;
	} catch (err) {
		loggerPlugin.warn('/plugins/txanalysor/valueInQuote price fetch failed', sym, '->', tgt, err.message);
		return null;
	}
};

const processOnHoldDeposits = async () => {
	const threshold = parseNumber(meta.THRESHOLD_USDT.value, 1000);
	const quote = (meta.QUOTE_CURRENCY.value || 'usdt').toLowerCase();

	let scanned = 0;
	let validated = 0;
	let skipped = 0;
	const res = await toolsLib.wallet.getExchangeDeposits(
		null,   // currency
		false,  // status (pending)
		false,  // dismissed
		false,  // rejected
		false,  // processing
		false,  // waiting
		null,   // limit
		null,      // page
		null,   // orderBy
		null,   // order
		null,   // startDate
		null,   // endDate
		null,   // transactionId
		null,   // address
		null,   // format
		{ onhold: true } // opts
	);
	const list = res && Array.isArray(res.data) ? res.data : [];
	if (!list.length) {
		loggerPlugin.info('/plugins/txanalysor/process complete', { scanned, validated, skipped });
		return { scanned, validated, skipped };
	}

	for (const d of list) {
		// Defensive parsing
		if (!d || !d.transaction_id) continue;

		// Only pending + onhold deposits
		const isOnHold = !!d.onhold;
		if (!isOnHold) {
			skipped += 1;
			continue;
		}

		const currency = String((d.currency || d.symbol) || '').toLowerCase();
		const amount = parseNumber(d.amount, null);
		if (!currency || !(amount > 0)) {
			skipped += 1;
			continue;
		}

		const valueQuote = await valueInQuote(currency, amount, quote);
		if (valueQuote === null) {
			skipped += 1;
			continue;
		}

		if (valueQuote < threshold) {
			try {
				await toolsLib.wallet.updatePendingMint(d.transaction_id, {
					status: true,
					onhold: false
				});
				loggerPlugin.info('/plugins/txanalysor/validated', {
					transaction_id: d.transaction_id,
					currency,
					amount,
					value_in_quote: valueQuote,
					quote,
					threshold
				});
				validated += 1;
			} catch (err) {
				loggerPlugin.error('/plugins/txanalysor/updatePendingMint failed', d.transaction_id, err.message);
				skipped += 1;
			}
		} else {
			skipped += 1;
		}
		scanned += 1;
	}

	loggerPlugin.info('/plugins/txanalysor/process complete', { scanned, validated, skipped });
	return { scanned, validated, skipped };
};

init()
	.then(() => {
		setTimeout(() => {
			processOnHoldDeposits().catch((err) => {
				loggerPlugin.error('/plugins/txanalysor/initial run error', err.message);
			});
		}, 10000);
			// Run every minute
			setInterval(() => {
				processOnHoldDeposits().catch((err) => {
					loggerPlugin.error('/plugins/txanalysor/scheduled run error', err.message);
				});
			}, 60 * 1000);
	})
	.catch((err) => {
		loggerPlugin.error(
			'/plugins/txanalysor/init/error during initialization',
			err.message
		);
	});


2 Likes