Below is a sample volume-based tier plugin.
It automatically upgrades or downgrades users based on their trading volume over a rolling 30-day period.
1. Tiers are defined statically
const TIERS = [
{ level: 1, min: 0 },
{ level: 2, min: 1000 },
{ level: 3, min: 10000 },
{ level: 4, min: 50000 }
];
Each tier sets:
You can adjust these thresholds as needed.
2. Rolling window
const VOLUME_WINDOW_DAYS = 30;
The plugin calculates total trading volume for each user within this period.
3. Automatic update
For each user:
-
It sums their trades (converted to native currency).
-
It determines the correct tier.
-
If their current level differs, it updates it.
The cron job runs daily, but you can change the schedule if needed.
This provides a simple, fully customizable way to implement volume-based tiering.
'use strict';
/**
* Simple Static Volume-Based Tier Plugin
*
* - Calculates rolling 30-day trading volume
* - Converts to native currency using oracle prices
* - Automatically upgrades/downgrades users
* - Runs once per day
*/
module.exports = async function () {
const { loggerPlugin, toolsLib, moment, cron } = this;
// -----------------------------
// Static Configuration
// -----------------------------
const VOLUME_WINDOW_DAYS = 30;
// Define tier thresholds here
// Ordered from lowest to highest
const TIERS = [
{ level: 1, min: 0 },
{ level: 2, min: 1000 },
{ level: 3, min: 10000 },
{ level: 4, min: 50000 }
];
// -----------------------------
// Helpers
// -----------------------------
const determineLevelFromVolume = (volume) => {
let level = TIERS[0].level;
for (const tier of TIERS) {
if (volume >= tier.min) {
level = tier.level;
}
}
return level;
};
const getOracleIndex = async () => {
const coins = toolsLib.getKitCoins();
return toolsLib.getAssetsPrices(
coins,
toolsLib.getKitConfig().native_currency
);
};
const calculateUserTradeVolume = async (user_id, from, to) => {
let volume = 0;
const oracleIndex = await getOracleIndex();
const native = toolsLib.getKitConfig().native_currency;
const trades = await toolsLib.order.getAllUserTradesByKitId(
user_id,
null, null, null, null, null,
from,
to,
'all'
);
for (const trade of (trades.data || [])) {
let size = Number(trade.size) || 0;
const base = trade.symbol.split('-')[0];
if (base !== native) {
const price = oracleIndex[base];
if (!price || price <= 0) continue;
size = size * price;
}
volume += size;
}
return volume;
};
// -----------------------------
// Main Tier Logic
// -----------------------------
const runVolumeTiering = async () => {
try {
const now = moment().seconds(0).milliseconds(0);
const from = now.clone().subtract(VOLUME_WINDOW_DAYS, 'days').toISOString();
const to = now.toISOString();
const users = await toolsLib.database.findAll('user', {
where: { activated: true, flagged: false },
raw: true,
attributes: ['id', 'verification_level']
});
for (const user of users) {
const volume = await calculateUserTradeVolume(user.id, from, to);
const newLevel = determineLevelFromVolume(volume);
if (user.verification_level !== newLevel) {
loggerPlugin.info(
`volume-tier update: user=${user.id} volume=${volume} ${user.verification_level} -> ${newLevel}`
);
await toolsLib.user.changeUserVerificationLevelById(
user.id,
newLevel
);
}
}
} catch (err) {
loggerPlugin.error('runVolumeTiering error', err.message);
}
};
// -----------------------------
// Daily Execution (00:00 UTC)
// -----------------------------
cron.schedule('0 0 * * *', runVolumeTiering, {
timezone: 'UTC'
}).start();
loggerPlugin.info('static volume-based tier plugin initialized');
};