Advance ALL Mainnet production readiness planning

This commit is contained in:
defiQUG
2026-04-30 01:58:31 -07:00
parent 191aa20af1
commit cfbc7ce8cb
7 changed files with 1152 additions and 124 deletions

View File

@@ -90,6 +90,80 @@
"notes": [
"Tiny live canary swap executed on Polygon DODO PMM cWUSDT/USDT."
]
},
{
"poolId": "651940-uniswap_v2-usdt-ausdc",
"generatedAt": "2026-04-30T07:30:00Z",
"canaryTransactions": [
{
"direction": "base_to_quote",
"txHash": "0xa5479400c203922b0e29a4c438daeeeef6f99b847d617f029e7978c1beba6b2b",
"approvalTxHash": "0xe4e73ff7d9c4a998e4cca1fcbf847d351171c0c026b11e798a0c1dc2bb6b4f12",
"fundingSwapTxHash": "0x39bddcca6160e2df5c7595350f9d911c73e0e7bfe59595fae2c3248576962d5b",
"fundingApprovalTxHash": "0x238bae3818d16a95711c19657dbd08bc076ef3fde7fedb7291ed514d2e090684",
"amountInRaw": "1000000",
"tokenIn": "USDT",
"tokenOut": "AUSDC",
"executor": "UniswapV2Router.swapExactTokensForTokens"
}
],
"notes": [
"Tiny live canary swap executed on ALL Mainnet Uniswap V2 USDT/AUSDC after acquiring USDT inventory via AUSDC/USDT."
]
},
{
"poolId": "651940-uniswap_v2-wall-usdt",
"generatedAt": "2026-04-30T07:30:00Z",
"canaryTransactions": [
{
"direction": "base_to_quote",
"txHash": "0x7b6a3d5dedc775e9b0f73de4e0a89bdd004f82c451d7cccf1e2178e5893d487c",
"approvalTxHash": "0xdd7985b4535915523831f989e054e6333af6b84e589c67791e73cce7acbff6a6",
"amountInRaw": "1000000",
"tokenIn": "WALL",
"tokenOut": "USDT",
"executor": "UniswapV2Router.swapExactTokensForTokens"
}
],
"notes": [
"Tiny live canary swap executed on ALL Mainnet Uniswap V2 WALL/USDT."
]
},
{
"poolId": "42220-dodo_pmm-cwusdc-usdc",
"generatedAt": "2026-04-30T00:00:00.000-07:00",
"canaryTransactions": [
{
"direction": "base_to_quote",
"txHash": "0x32d3869f987d558e392fd01aab77968d9cad1d90da71be1db90061f58d5c14b1",
"fundingTransferTxHash": "0x8ecd2a68f5dc3f2ab471ccc7ea8fdce16064029b6792bce7a387d943326fa1d6",
"amountInRaw": "100",
"tokenIn": "cWUSDC",
"tokenOut": "USDC",
"executor": "DODO_DVM.transfer_then_sellBase"
}
],
"notes": [
"Tiny live canary swap executed on Celo DODO PMM cWUSDC/USDC after deployer-funded seed liquidity."
]
},
{
"poolId": "42220-dodo_pmm-cwusdt-usdt",
"generatedAt": "2026-04-30T00:00:00.000-07:00",
"canaryTransactions": [
{
"direction": "base_to_quote",
"txHash": "0x0498a0484f6707e0f8019b8803a8aed932f33f587a57186cc37bcdc594393094",
"fundingTransferTxHash": "0xd4182a6e116df30544097851892f379d67af2b33fc2374bb0204a6450dcecea4",
"amountInRaw": "100",
"tokenIn": "cWUSDT",
"tokenOut": "USDT",
"executor": "DODO_DVM.transfer_then_sellBase"
}
],
"notes": [
"Tiny live canary swap executed on Celo DODO PMM cWUSDT/USDT after deployer-funded seed liquidity."
]
}
]
}

File diff suppressed because it is too large Load Diff

View File

@@ -16,6 +16,7 @@ const readinessPath = resolve(repoRoot, "reports/status/all-mainnet-deployment-r
const reservePath = resolve(repoRoot, "reports/status/all-mainnet-required-pool-balances-latest.json");
const canaryPath = resolve(repoRoot, "reports/status/all-mainnet-canary-preflight-latest.json");
const rebalancePath = resolve(repoRoot, "reports/status/all-mainnet-rebalance-plan-latest.json");
const dodoFundingPath = resolve(repoRoot, "reports/status/all-mainnet-dodo-funding-preflight-latest.json");
const jsonOut = resolve(repoRoot, "reports/status/all-mainnet-full-deployment-funding-packet-latest.json");
const mdOut = resolve(repoRoot, "reports/status/all-mainnet-full-deployment-funding-packet-latest.md");
@@ -49,17 +50,20 @@ const readiness = readJson(readinessPath, { blockers: [] });
const reserve = readJson(reservePath, { results: [] });
const canary = readJson(canaryPath, { results: [] });
const rebalance = readJson(rebalancePath, { intents: [] });
const dodoFunding = readJson(dodoFundingPath, { results: [] });
const generatedAt = new Date().toISOString();
const reserveByPoolId = new Map(reserve.results.map((row) => [row.poolId, row]));
const canaryByPoolId = new Map(canary.results.map((row) => [row.poolId, row]));
const rebalanceByPoolId = new Map(rebalance.intents.map((row) => [row.poolId, row]));
const dodoFundingByPoolId = new Map(dodoFunding.results.map((row) => [row.poolId, row]));
const requiredRows = matrix.rows.filter((row) => row.requiredForSpend === true);
const rows = requiredRows.map((row) => {
const reserveRow = reserveByPoolId.get(row.poolId);
const canaryRow = canaryByPoolId.get(row.poolId);
const rebalanceRow = rebalanceByPoolId.get(row.poolId);
const dodoFundingRow = dodoFundingByPoolId.get(row.poolId);
const blockers = new Set(poolBlockers(row));
const productionBlockers = new Set();
const actions = [];
@@ -67,11 +71,26 @@ const rows = requiredRows.map((row) => {
if (row.status === "planned") {
actions.push(row.protocol === "dodo_pmm" ? "create_or_import_dodo_pool" : "create_or_import_pool");
}
if (reserveRow?.liveReadStatus === "zero_balances") actions.push("fund_base_and_quote");
if (reserveRow?.liveReadStatus === "partial_balance") actions.push("fund_low_side");
if (reserveRow?.liveReadStatus === "zero_balances") {
if (row.protocol === "dodo_pmm" && dodoFundingRow?.status !== "ready") {
for (const blocker of dodoFundingRow?.blockers || ["dodo_funding_preflight_missing"]) blockers.add(`funding_${blocker}`);
} else {
actions.push("fund_base_and_quote");
}
}
if (reserveRow?.liveReadStatus === "partial_balance") {
if (row.protocol === "dodo_pmm" && dodoFundingRow?.status !== "ready") {
for (const blocker of dodoFundingRow?.blockers || ["dodo_funding_preflight_missing"]) blockers.add(`funding_${blocker}`);
} else {
actions.push("fund_low_side");
}
}
if (["live_read"].includes(row.status)) actions.push("run_canary");
if (canaryRow?.canaryPreflight === "ready") actions.push("execute_canary_swap");
if (rebalanceRow?.executionStatus === "operator_review_required") actions.push(...(rebalanceRow.actions || []));
if (rebalanceRow?.executionStatus === "operator_review_required") {
actions.push(...(rebalanceRow.actions || []));
blockers.add("rebalance_operator_review_required");
}
for (const blocker of canaryRow?.blockers || []) blockers.add(blocker);
if (["live_read", "canary_passed", "production"].includes(row.status)) {
@@ -90,6 +109,7 @@ const rows = requiredRows.map((row) => {
publicRoutingEnabled: Boolean(row.publicRoutingEnabled),
reserveStatus: reserveRow?.liveReadStatus || "not_checked",
canaryPreflight: canaryRow?.canaryPreflight || "not_checked",
dodoFundingPreflight: dodoFundingRow?.status || "not_checked",
rebalanceStatus: rebalanceRow?.executionStatus || "not_planned",
actions: [...new Set(actions)],
blockers: [...blockers],

View File

@@ -38,6 +38,29 @@ function ratioBps(a, b) {
return Number(((high - low) * 10_000n) / high);
}
function rebalanceHint(row, base, quote, imbalanceBps) {
if (base === 0n && quote === 0n) return null;
const highSide = base > quote ? "base" : quote > base ? "quote" : "balanced";
const lowSide = base > quote ? "quote" : quote > base ? "base" : "balanced";
const highSymbol = highSide === "base" ? row.baseToken?.symbol : row.quoteToken?.symbol;
const lowSymbol = lowSide === "base" ? row.baseToken?.symbol : row.quoteToken?.symbol;
return {
highSide,
lowSide,
highSymbol,
lowSymbol,
imbalanceBps,
conservativeAction:
highSide === "balanced"
? "monitor_no_rebalance_required"
: `add_or_swap_into_${lowSide}_side`,
note:
highSide === "balanced"
? "Reserves are balanced in raw units."
: `Reserve skew is ${imbalanceBps} bps by raw token units; correct toward ${lowSymbol} before increasing ${highSymbol} exposure.`,
};
}
function reserveEvidenceFor(row, reserveByPoolId) {
const live = reserveByPoolId.get(row.poolId);
if (live) {
@@ -78,6 +101,7 @@ function planForRow(row, evidence) {
const base = asBigInt(evidence?.baseBalanceRaw);
const quote = asBigInt(evidence?.quoteBalanceRaw);
const imbalanceBps = ratioBps(base, quote);
const hint = rebalanceHint(row, base, quote, imbalanceBps);
if (evidence) {
if (evidence.poolHasCode !== true) blockers.push("pool_code_not_confirmed");
@@ -113,6 +137,7 @@ function planForRow(row, evidence) {
},
reserveEvidence: evidence,
imbalanceBps,
rebalanceHint: hint,
actions: [...new Set(actions)],
blockers: [...new Set(blockers)],
executionStatus: blockers.length ? "blocked" : actions.includes("monitor_no_rebalance_required") ? "no_action" : "operator_review_required",

View File

@@ -7,7 +7,8 @@
* promoted to live_read.
*/
import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
import { homedir } from "node:os";
import { resolve } from "node:path";
import { ethers } from "ethers";
@@ -16,17 +17,45 @@ const matrixPath = resolve(repoRoot, "config/all-mainnet-pool-creation-matrix.js
const outDir = resolve(repoRoot, "reports/status");
const reportPath = resolve(outDir, "all-mainnet-canary-preflight-latest.json");
function loadDotenvFile(path) {
if (!existsSync(path)) return;
const content = readFileSync(path, "utf8");
for (const line of content.split(/\r?\n/)) {
const trimmed = line.trim();
if (!trimmed || trimmed.startsWith("#")) continue;
const match = trimmed.match(/^(?:export\s+)?([A-Za-z_][A-Za-z0-9_]*)=(.*)$/);
if (!match) continue;
const [, key, rawValue] = match;
if (process.env[key] !== undefined) continue;
let value = rawValue.trim();
if ((value.startsWith('"') && value.endsWith('"')) || (value.startsWith("'") && value.endsWith("'"))) {
value = value.slice(1, -1);
} else {
value = value.replace(/\s+#.*$/, "");
}
if (value.includes("${")) continue;
process.env[key] = value.trim();
}
}
loadDotenvFile(resolve(repoRoot, ".env"));
loadDotenvFile(resolve(repoRoot, "smom-dbis-138/.env"));
loadDotenvFile(resolve(homedir(), ".secure-secrets/private-keys.env"));
if (!process.env.PRIVATE_KEY && process.env.DEPLOYER_PRIVATE_KEY) {
process.env.PRIVATE_KEY = process.env.DEPLOYER_PRIVATE_KEY;
}
const rpcByChain = {
1: process.env.ETHEREUM_MAINNET_RPC || process.env.RPC_URL_1 || "https://ethereum.publicnode.com",
10: process.env.OPTIMISM_RPC || process.env.RPC_URL_10 || "https://optimism.publicnode.com",
25: process.env.CRONOS_RPC || process.env.RPC_URL_25 || "https://cronos-evm-rpc.publicnode.com",
56: process.env.BSC_RPC || process.env.RPC_URL_56 || "https://bsc-rpc.publicnode.com",
100: process.env.GNOSIS_RPC || process.env.RPC_URL_100 || "https://gnosis.publicnode.com",
137: process.env.POLYGON_RPC || process.env.RPC_URL_137 || "https://polygon-bor-rpc.publicnode.com",
8453: process.env.BASE_RPC || process.env.RPC_URL_8453 || "https://base-rpc.publicnode.com",
42161: process.env.ARBITRUM_RPC || process.env.RPC_URL_42161 || "https://arbitrum-one-rpc.publicnode.com",
42220: process.env.CELO_RPC || process.env.RPC_URL_42220 || "https://celo-rpc.publicnode.com",
43114: process.env.AVALANCHE_RPC || process.env.RPC_URL_43114 || "https://avalanche-c-chain-rpc.publicnode.com",
10: process.env.OPTIMISM_RPC || process.env.OPTIMISM_MAINNET_RPC || process.env.RPC_URL_10 || "https://optimism.publicnode.com",
25: process.env.CRONOS_RPC || process.env.CRONOS_RPC_URL || process.env.RPC_URL_25 || "https://cronos-evm-rpc.publicnode.com",
56: process.env.BSC_RPC || process.env.BSC_RPC_URL || process.env.RPC_URL_56 || "https://bsc-rpc.publicnode.com",
100: process.env.GNOSIS_RPC || process.env.GNOSIS_MAINNET_RPC || process.env.RPC_URL_100 || "https://gnosis.publicnode.com",
137: process.env.POLYGON_RPC || process.env.POLYGON_MAINNET_RPC || process.env.RPC_URL_137 || "https://polygon-bor-rpc.publicnode.com",
8453: process.env.BASE_RPC || process.env.BASE_MAINNET_RPC || process.env.RPC_URL_8453 || "https://base-rpc.publicnode.com",
42161: process.env.ARBITRUM_RPC || process.env.ARBITRUM_MAINNET_RPC || process.env.RPC_URL_42161 || "https://arbitrum-one-rpc.publicnode.com",
42220: process.env.CELO_RPC || process.env.CELO_MAINNET_RPC || process.env.RPC_URL_42220 || "https://celo-rpc.publicnode.com",
43114: process.env.AVALANCHE_RPC || process.env.AVALANCHE_RPC_URL || process.env.RPC_URL_43114 || "https://avalanche-c-chain-rpc.publicnode.com",
651940: process.env.ALL_MAINNET_RPC || process.env.CHAIN_651940_RPC_URL || "https://mainnet-rpc.alltra.global",
};
@@ -39,8 +68,13 @@ const uniV2RouterAbi = [
"function getAmountsOut(uint256,address[]) view returns (uint256[])",
];
const dodoDvmAbi = [
"function querySellBase(address,uint256) view returns (uint256)",
"function querySellQuote(address,uint256) view returns (uint256)",
"function querySellBase(address,uint256) view returns (uint256,uint256)",
"function querySellQuote(address,uint256) view returns (uint256,uint256)",
"function getMidPrice() view returns (uint256)",
"function getPMMStateForCall() view returns (uint256,uint256,uint256,uint256,uint256,uint256,uint256)",
"function getVaultReserve() view returns (uint256,uint256)",
"function _BASE_RESERVE_() view returns (uint112)",
"function _QUOTE_RESERVE_() view returns (uint112)",
];
function operatorAddress() {
@@ -123,19 +157,57 @@ async function quoteDodo(provider, row, base, quote, operator) {
const pool = new ethers.Contract(normalizedAddress(row.poolAddress), dodoDvmAbi, provider);
const baseAmount = smallQuoteAmount(base.decimals ?? 18);
const quoteAmount = smallQuoteAmount(quote.decimals ?? 18);
const [sellBase, sellQuote] = await Promise.all([
const [sellBase, sellQuote, midPrice, pmmState, vaultReserve, baseReserve, quoteReserve] = await Promise.all([
callOrError(() => pool.querySellBase(operator, baseAmount)),
callOrError(() => pool.querySellQuote(operator, quoteAmount)),
callOrError(() => pool.getMidPrice()),
callOrError(() => pool.getPMMStateForCall()),
callOrError(() => pool.getVaultReserve()),
callOrError(() => pool._BASE_RESERVE_()),
callOrError(() => pool._QUOTE_RESERVE_()),
]);
return {
supported: sellBase.ok || sellQuote.ok,
poolAddress: row.poolAddress,
sellBaseOutRaw: sellBase.ok ? sellBase.value.toString() : null,
sellQuoteOutRaw: sellQuote.ok ? sellQuote.value.toString() : null,
sampleAmounts: {
baseRaw: baseAmount.toString(),
quoteRaw: quoteAmount.toString(),
},
midPriceRaw: midPrice.ok ? midPrice.value.toString() : null,
pmmState: pmmState.ok
? {
iRaw: pmmState.value[0].toString(),
kRaw: pmmState.value[1].toString(),
baseReserveRaw: pmmState.value[2].toString(),
quoteReserveRaw: pmmState.value[3].toString(),
baseTargetRaw: pmmState.value[4].toString(),
quoteTargetRaw: pmmState.value[5].toString(),
rState: pmmState.value[6].toString(),
}
: null,
vaultReserve: vaultReserve.ok
? {
baseRaw: vaultReserve.value[0].toString(),
quoteRaw: vaultReserve.value[1].toString(),
}
: null,
storedReserve: {
baseRaw: baseReserve.ok ? baseReserve.value.toString() : null,
quoteRaw: quoteReserve.ok ? quoteReserve.value.toString() : null,
},
sellBaseOutRaw: sellBase.ok ? sellBase.value[0].toString() : null,
sellBaseFeeRaw: sellBase.ok ? sellBase.value[1].toString() : null,
sellQuoteOutRaw: sellQuote.ok ? sellQuote.value[0].toString() : null,
sellQuoteFeeRaw: sellQuote.ok ? sellQuote.value[1].toString() : null,
errors: [
...(sellBase.ok ? [] : [`query_sell_base:${sellBase.error}`]),
...(sellQuote.ok ? [] : [`query_sell_quote:${sellQuote.error}`]),
...(midPrice.ok ? [] : [`get_mid_price:${midPrice.error}`]),
...(pmmState.ok ? [] : [`get_pmm_state:${pmmState.error}`]),
...(vaultReserve.ok ? [] : [`get_vault_reserve:${vaultReserve.error}`]),
...(baseReserve.ok ? [] : [`get_base_reserve:${baseReserve.error}`]),
...(quoteReserve.ok ? [] : [`get_quote_reserve:${quoteReserve.error}`]),
],
};
}

View File

@@ -0,0 +1,182 @@
#!/usr/bin/env node
/**
* Read-only funding preflight for required ALL Mainnet DODO rows.
*
* It checks whether created zero-reserve DODO pools can be seeded safely with
* the operator's current base/quote inventory. No transactions are executed.
*/
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
import { homedir } from "node:os";
import { resolve } from "node:path";
import { ethers } from "ethers";
const repoRoot = resolve(new URL("../..", import.meta.url).pathname);
const matrixPath = resolve(repoRoot, "config/all-mainnet-pool-creation-matrix.json");
const outDir = resolve(repoRoot, "reports/status");
const outPath = resolve(outDir, "all-mainnet-dodo-funding-preflight-latest.json");
function loadDotenvFile(path) {
if (!existsSync(path)) return;
for (const line of readFileSync(path, "utf8").split(/\r?\n/)) {
const trimmed = line.trim();
if (!trimmed || trimmed.startsWith("#")) continue;
const match = trimmed.match(/^(?:export\s+)?([A-Za-z_][A-Za-z0-9_]*)=(.*)$/);
if (!match) continue;
const [, key, rawValue] = match;
if (process.env[key] !== undefined) continue;
let value = rawValue.trim();
if ((value.startsWith('"') && value.endsWith('"')) || (value.startsWith("'") && value.endsWith("'"))) {
value = value.slice(1, -1);
} else {
value = value.replace(/\s+#.*$/, "");
}
if (value.includes("${")) continue;
process.env[key] = value.trim();
}
}
loadDotenvFile(resolve(repoRoot, ".env"));
loadDotenvFile(resolve(repoRoot, "smom-dbis-138/.env"));
loadDotenvFile(resolve(homedir(), ".secure-secrets/private-keys.env"));
if (!process.env.PRIVATE_KEY && process.env.DEPLOYER_PRIVATE_KEY) process.env.PRIVATE_KEY = process.env.DEPLOYER_PRIVATE_KEY;
const rpcByChain = {
10: process.env.OPTIMISM_RPC || process.env.OPTIMISM_MAINNET_RPC || "https://optimism.publicnode.com",
25: process.env.CRONOS_RPC || process.env.CRONOS_RPC_URL || "https://cronos-evm-rpc.publicnode.com",
56: process.env.BSC_RPC || process.env.BSC_RPC_URL || "https://bsc-rpc.publicnode.com",
100: process.env.GNOSIS_RPC || process.env.GNOSIS_MAINNET_RPC || "https://gnosis.publicnode.com",
8453: process.env.BASE_RPC || process.env.BASE_MAINNET_RPC || "https://base-rpc.publicnode.com",
42161: process.env.ARBITRUM_RPC || process.env.ARBITRUM_MAINNET_RPC || "https://arbitrum-one-rpc.publicnode.com",
42220: process.env.CELO_RPC || process.env.CELO_MAINNET_RPC || "https://celo-rpc.publicnode.com",
43114: process.env.AVALANCHE_RPC || process.env.AVALANCHE_RPC_URL || "https://avalanche-c-chain-rpc.publicnode.com",
};
const erc20Abi = [
"function balanceOf(address) view returns (uint256)",
"function decimals() view returns (uint8)",
];
const dvmAbi = [
"function totalSupply() view returns (uint256)",
"function getVaultReserve() view returns (uint256,uint256)",
"function _BASE_TOKEN_() view returns (address)",
"function _QUOTE_TOKEN_() view returns (address)",
"function buyShares(address) returns (uint256,uint256,uint256)",
];
function operatorAddress() {
if (process.env.DEPLOYER_ADDRESS && ethers.isAddress(process.env.DEPLOYER_ADDRESS)) return ethers.getAddress(process.env.DEPLOYER_ADDRESS);
if (process.env.SIGNER_ADDRESS && ethers.isAddress(process.env.SIGNER_ADDRESS)) return ethers.getAddress(process.env.SIGNER_ADDRESS);
if (process.env.PRIVATE_KEY) return new ethers.Wallet(process.env.PRIVATE_KEY).address;
throw new Error("Set DEPLOYER_ADDRESS, SIGNER_ADDRESS, or PRIVATE_KEY for DODO funding preflight.");
}
function normalized(address) {
return ethers.getAddress(String(address).toLowerCase());
}
function seedAmount(decimals) {
return 10n ** BigInt(Math.min(Number(decimals ?? 18), 6));
}
async function callOrError(fn) {
try {
return { ok: true, value: await fn() };
} catch (error) {
return { ok: false, error: error.shortMessage || error.message };
}
}
const matrix = JSON.parse(readFileSync(matrixPath, "utf8"));
const operator = operatorAddress();
const rows = matrix.rows.filter((row) => row.requiredForSpend === true && row.protocol === "dodo_pmm" && row.status === "created");
const results = [];
for (const row of rows) {
const result = {
poolId: row.poolId,
chainId: row.chainId,
network: row.network,
poolAddress: row.poolAddress,
operator,
baseToken: row.baseToken,
quoteToken: row.quoteToken,
status: "blocked",
fundingPlan: null,
blockers: [],
};
const rpcUrl = rpcByChain[row.chainId];
if (!rpcUrl) result.blockers.push("missing_rpc");
if (!row.poolAddress) result.blockers.push("missing_pool_address");
if (!row.baseToken?.address) result.blockers.push(`missing_base_address:${row.baseToken?.symbol || "unknown"}`);
if (!row.quoteToken?.address) result.blockers.push(`missing_quote_address:${row.quoteToken?.symbol || "unknown"}`);
if (result.blockers.length) {
results.push(result);
continue;
}
const provider = new ethers.JsonRpcProvider(rpcUrl, row.chainId, { staticNetwork: true });
const base = new ethers.Contract(normalized(row.baseToken.address), erc20Abi, provider);
const quote = new ethers.Contract(normalized(row.quoteToken.address), erc20Abi, provider);
const pool = new ethers.Contract(normalized(row.poolAddress), dvmAbi, provider);
const [baseDecimals, quoteDecimals, baseBalance, quoteBalance, totalSupply, reserves, actualBase, actualQuote, dryRunBuyShares] = await Promise.all([
callOrError(() => base.decimals()),
callOrError(() => quote.decimals()),
callOrError(() => base.balanceOf(operator)),
callOrError(() => quote.balanceOf(operator)),
callOrError(() => pool.totalSupply()),
callOrError(() => pool.getVaultReserve()),
callOrError(() => pool._BASE_TOKEN_()),
callOrError(() => pool._QUOTE_TOKEN_()),
callOrError(() => pool.buyShares.staticCall(operator)),
]);
if (!baseDecimals.ok) result.blockers.push(`base_decimals:${baseDecimals.error}`);
if (!quoteDecimals.ok) result.blockers.push(`quote_decimals:${quoteDecimals.error}`);
if (!baseBalance.ok) result.blockers.push(`base_balance:${baseBalance.error}`);
if (!quoteBalance.ok) result.blockers.push(`quote_balance:${quoteBalance.error}`);
if (!totalSupply.ok) result.blockers.push(`dvm_total_supply:${totalSupply.error}`);
if (!reserves.ok) result.blockers.push(`dvm_reserves:${reserves.error}`);
if (!actualBase.ok || normalized(actualBase.value) !== normalized(row.baseToken.address)) result.blockers.push("dvm_base_token_mismatch");
if (!actualQuote.ok || normalized(actualQuote.value) !== normalized(row.quoteToken.address)) result.blockers.push("dvm_quote_token_mismatch");
const baseSeed = baseDecimals.ok ? seedAmount(baseDecimals.value) : 0n;
const quoteSeed = quoteDecimals.ok ? seedAmount(quoteDecimals.value) : 0n;
if (baseBalance.ok && baseBalance.value < baseSeed) result.blockers.push(`insufficient_operator_base:${row.baseToken.symbol}`);
if (quoteBalance.ok && quoteBalance.value < quoteSeed) result.blockers.push(`insufficient_operator_quote:${row.quoteToken.symbol}`);
result.fundingPlan = {
method: "transfer_base_and_quote_then_buyShares",
baseSeedRaw: baseSeed.toString(),
quoteSeedRaw: quoteSeed.toString(),
operatorBaseRaw: baseBalance.ok ? baseBalance.value.toString() : null,
operatorQuoteRaw: quoteBalance.ok ? quoteBalance.value.toString() : null,
totalSupplyRaw: totalSupply.ok ? totalSupply.value.toString() : null,
reserveBaseRaw: reserves.ok ? reserves.value[0].toString() : null,
reserveQuoteRaw: reserves.ok ? reserves.value[1].toString() : null,
buySharesBeforeTransferStaticCall: dryRunBuyShares.ok
? dryRunBuyShares.value.map((value) => value.toString())
: null,
buySharesBeforeTransferError: dryRunBuyShares.ok ? null : dryRunBuyShares.error,
};
result.status = result.blockers.length === 0 ? "ready" : "blocked";
results.push(result);
}
const statusCounts = results.reduce((counts, result) => {
counts[result.status] = (counts[result.status] || 0) + 1;
return counts;
}, {});
mkdirSync(outDir, { recursive: true });
writeFileSync(outPath, `${JSON.stringify({
generatedAt: new Date().toISOString(),
sourceMatrix: "config/all-mainnet-pool-creation-matrix.json",
mode: "read_only_funding_preflight",
operator,
statusCounts,
results,
}, null, 2)}\n`);
console.log(`[OK] ALL Mainnet DODO funding preflight written: ${statusCounts.ready || 0} ready, ${statusCounts.blocked || 0} blocked.`);

View File

@@ -0,0 +1,73 @@
#!/usr/bin/env node
/**
* Promote already-proven ALL Mainnet required rows to production.
*
* This script does not execute transactions. It only advances rows that already
* have a pool address, live nonzero reserves, complete vault assignments, public
* routing enabled, and recorded canary evidence.
*/
import { readFileSync, writeFileSync } from "node:fs";
import { resolve } from "node:path";
const repoRoot = resolve(new URL("../..", import.meta.url).pathname);
const matrixPath = resolve(repoRoot, "config/all-mainnet-pool-creation-matrix.json");
const dryRun = process.argv.includes("--dry-run");
function eligible(row) {
const reserve = row.reserveEvidence || {};
return (
row.requiredForSpend === true &&
row.status === "canary_passed" &&
row.publicRoutingEnabled === true &&
Boolean(row.poolAddress) &&
Boolean(row.canaryEvidence) &&
(row.missingRequiredVaultRoles || []).length === 0 &&
row.vaultAssignmentStatus === "ready" &&
reserve.poolHasCode === true &&
reserve.liveReadStatus === "nonzero_base_and_quote" &&
BigInt(reserve.baseBalanceRaw || "0") > 0n &&
BigInt(reserve.quoteBalanceRaw || "0") > 0n
);
}
const matrix = JSON.parse(readFileSync(matrixPath, "utf8"));
const promoted = [];
const generatedAt = new Date().toISOString();
for (const row of matrix.rows) {
if (!eligible(row)) continue;
row.status = "production";
row.productionEvidence = {
generatedAt,
basis: [
"requiredForSpend=true",
"publicRoutingEnabled=true",
"poolAddress present",
"vaultAssignmentStatus=ready",
"reserveEvidence.liveReadStatus=nonzero_base_and_quote",
"canaryEvidence present",
],
};
if (!row.notes.includes("Promoted to production after reserve and canary evidence checks.")) {
row.notes.push("Promoted to production after reserve and canary evidence checks.");
}
promoted.push(row.poolId);
}
matrix.generatedAt = generatedAt;
matrix.statusCounts = matrix.rows.reduce((counts, row) => {
counts[row.status] = (counts[row.status] || 0) + 1;
return counts;
}, {});
matrix.protocolCounts = matrix.rows.reduce((counts, row) => {
counts[row.protocol] = (counts[row.protocol] || 0) + 1;
return counts;
}, {});
if (!dryRun) {
writeFileSync(matrixPath, `${JSON.stringify(matrix, null, 2)}\n`);
}
console.log(`[OK] ALL Mainnet production promotion ${dryRun ? "dry run" : "complete"}: ${promoted.length} row(s) promoted.`);
for (const poolId of promoted) console.log(` - ${poolId}`);