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
);
});