107 lines
4.1 KiB
JavaScript
107 lines
4.1 KiB
JavaScript
#!/usr/bin/env node
|
|
/**
|
|
* Inventory sizing tool: reads simulation-params.json, optional per-chain V_epoch,
|
|
* outputs I_T^* (inventory target), suggested D_0, and assumptions per chain per cW token.
|
|
* Keeps configs honest and PRs reviewable.
|
|
*
|
|
* Usage:
|
|
* node scripts/size-inventory.cjs # use scenario defaults for V_epoch
|
|
* node scripts/size-inventory.cjs --sigma 1.8 # override sigma (default 1.5)
|
|
* node scripts/size-inventory.cjs --refill-ratio 0.33 # T_refill/T_epoch (default 0.33)
|
|
* node scripts/size-inventory.cjs --v-epoch '{"1":100000,"56":80000,"137":60000}' # per-chain V_epoch
|
|
* EUR tokens use eurDefaults from simulation-params (higher sigma, k).
|
|
*/
|
|
|
|
const fs = require('fs');
|
|
const path = require('path');
|
|
|
|
const CONFIG_DIR = path.join(__dirname, '..', 'config');
|
|
const PARAMS_PATH = path.join(CONFIG_DIR, 'simulation-params.json');
|
|
const TOKEN_MAP_PATH = path.join(CONFIG_DIR, 'token-map.json');
|
|
|
|
const CW_USD = ['cWUSDT', 'cWUSDC', 'cWAUSDT', 'cWUSDW'];
|
|
const CW_EUR = ['cWEURC', 'cWEURT'];
|
|
const CW_ALL = [...CW_USD, ...CW_EUR];
|
|
|
|
function loadJson(p) {
|
|
return JSON.parse(fs.readFileSync(p, 'utf8'));
|
|
}
|
|
|
|
function parseArg(name, def) {
|
|
const i = process.argv.indexOf(name);
|
|
if (i === -1) return def;
|
|
const v = process.argv[i + 1];
|
|
if (name === '--v-epoch') return v ? JSON.parse(v) : null;
|
|
if (name === '--sigma' || name === '--refill-ratio' || name === '--depth-mult') return v ? Number(v) : def;
|
|
return def;
|
|
}
|
|
|
|
function main() {
|
|
const params = loadJson(PARAMS_PATH);
|
|
const tokenMap = fs.existsSync(TOKEN_MAP_PATH) ? loadJson(TOKEN_MAP_PATH) : { bridgedSymbols: CW_ALL };
|
|
const cwTokens = tokenMap.bridgedSymbols || CW_ALL;
|
|
const chains = params.chains || {};
|
|
const eurDefaults = params.eurDefaults || { sigma: 2, k: 0.2, feeBps: 35 };
|
|
const sigmaUsd = parseArg('--sigma', 1.5);
|
|
const refillRatio = parseArg('--refill-ratio', 0.33);
|
|
const depthMult = parseArg('--depth-mult', 0.75); // D_0 = depthMult * I_T^*
|
|
const vEpochOverride = parseArg('--v-epoch', null);
|
|
|
|
const assumptions = {
|
|
sigma_usd: sigmaUsd,
|
|
sigma_eur: eurDefaults.sigma,
|
|
T_refill_T_epoch: refillRatio,
|
|
depth_multiplier: depthMult,
|
|
formula: 'I_T^* >= V_epoch * sigma * (1 + T_refill/T_epoch) / (1 - beta) + gamma_buffer',
|
|
depth_rule: `D_0 = ${depthMult} * I_T^*`,
|
|
};
|
|
|
|
const out = { assumptions, chains: {}, generatedAt: new Date().toISOString() };
|
|
|
|
for (const [chainId, chain] of Object.entries(chains)) {
|
|
const beta = chain.bridgeBeta ?? 0.001;
|
|
const gamma = Number(chain.bridgeGammaUnits || 0) || 0;
|
|
const gammaBuffer = gamma * 2; // optional: 2 refill batches
|
|
const currentTarget = Number(chain.inventoryTargetUnits || 0) || 0;
|
|
|
|
// V_epoch: override > env per chain > 10% of current target as scenario default
|
|
let vEpoch = vEpochOverride && vEpochOverride[chainId] != null
|
|
? Number(vEpochOverride[chainId])
|
|
: (process.env[`V_EPOCH_${chainId}`] ? Number(process.env[`V_EPOCH_${chainId}`]) : null);
|
|
if (vEpoch == null || isNaN(vEpoch)) {
|
|
vEpoch = currentTarget > 0 ? Math.round(currentTarget * 0.1) : 100000;
|
|
}
|
|
|
|
out.chains[chainId] = { name: chain.name, hubStable: chain.hubStable, tokens: {} };
|
|
|
|
for (const symbol of cwTokens) {
|
|
const isEur = CW_EUR.includes(symbol);
|
|
const sigma = isEur ? eurDefaults.sigma : sigmaUsd;
|
|
const denom = 1 - beta;
|
|
if (denom <= 0) throw new Error(`Chain ${chainId}: invalid beta ${beta}`);
|
|
const iTStar = Math.ceil((vEpoch * sigma * (1 + refillRatio)) / denom + gammaBuffer);
|
|
const d0 = Math.ceil(depthMult * iTStar);
|
|
const k = isEur ? (eurDefaults.k ?? 0.2) : (chain.k ?? 0.1);
|
|
const feeBps = isEur ? (eurDefaults.feeBps ?? 35) : (chain.feeBps ?? 25);
|
|
|
|
out.chains[chainId].tokens[symbol] = {
|
|
I_T_star: iTStar,
|
|
D_0: d0,
|
|
V_epoch: vEpoch,
|
|
sigma,
|
|
beta,
|
|
gamma,
|
|
gamma_buffer: gammaBuffer,
|
|
k,
|
|
feeBps,
|
|
stress_band_eur: isEur ? 'wider band; use higher sigma' : null,
|
|
};
|
|
}
|
|
}
|
|
|
|
console.log(JSON.stringify(out, null, 2));
|
|
return out;
|
|
}
|
|
|
|
main();
|