Advance official DODO migration routing evidence
All checks were successful
Deploy to Phoenix / validate (push) Successful in 1m21s
Deploy to Phoenix / deploy (push) Successful in 48s
Deploy to Phoenix / deploy-atomic-swap-dapp (push) Successful in 1m20s
phoenix-deploy Deployed to cloudflare-sync
Deploy to Phoenix / cloudflare (push) Successful in 40s

This commit is contained in:
defiQUG
2026-04-30 03:48:53 -07:00
parent 9f50dd4c55
commit ccab738ae2
8 changed files with 581 additions and 13 deletions

View File

@@ -21,6 +21,11 @@
"label": "DODO docs: Ethereum contract addresses",
"url": "https://docs.dodoex.io/en/developer/contracts/dodo-v1-v2/contracts-address/ethereum",
"retrievedAt": "2026-04-30"
},
{
"label": "DODO docs: Base contract addresses",
"url": "https://docs.dodoex.io/en/developer/contracts/dodo-v1-v2/contracts-address/base",
"retrievedAt": "2026-04-30"
}
],
"poolDiscovery": {
@@ -63,6 +68,11 @@
"network": "Avalanche C-Chain",
"dvmFactory": "0xfF133A6D335b50bDAa6612D19E1352B049A8aE6a",
"source": "DODO docs"
},
"8453": {
"network": "Base",
"dvmFactory": "0x0226fCE8c969604C3A0AD19c37d1FAFac73e13c2",
"source": "DODO docs"
}
},
"unsupportedOrUnverifiedChains": {
@@ -74,10 +84,6 @@
"network": "Gnosis Chain",
"status": "official_dodo_dvm_factory_not_committed"
},
"8453": {
"network": "Base",
"status": "official_dodo_dvm_factory_not_committed"
},
"42220": {
"network": "Celo",
"status": "official_dodo_dvm_factory_not_committed"
@@ -202,6 +208,16 @@
"label": "1inch developer portal",
"url": "https://portal.1inch.dev/",
"retrievedAt": "2026-04-30"
},
{
"label": "1inch Help Center: supported networks",
"url": "https://help.1inch.com/en/articles/5528619-how-to-use-different-networks-on-1inch",
"retrievedAt": "2026-04-30"
},
{
"label": "1inch Business: Swap API supported chains",
"url": "https://business.1inch.com/products/swap",
"retrievedAt": "2026-04-30"
}
],
"poolDiscovery": {
@@ -210,6 +226,26 @@
"Use 1inch official API quote/swap response for current router tx data.",
"Do not create pools; this protocol wires route access to external liquidity."
]
},
"chainSupport": {
"8453": {
"network": "Base",
"status": "supported_by_official_1inch_network_list",
"source": "1inch Help Center / 1inch Business chain pages",
"supportedUse": "aggregated quote and swap route access for cW*/official stable pairs; requires token import/listing or direct contract-address query."
},
"100": {
"network": "Gnosis Chain",
"status": "supported_by_official_1inch_network_list",
"source": "1inch Help Center / 1inch Business chain pages",
"supportedUse": "aggregated quote and swap route access for cW*/official stable pairs; requires token import/listing or direct contract-address query."
},
"25": {
"network": "Cronos",
"status": "supported_by_official_1inch_business_swap_list",
"source": "1inch Business Swap API supported chains",
"supportedUse": "aggregated quote and swap route access for cW*/official stable pairs; requires token import/listing or direct contract-address query."
}
}
},
"sushiswap_v2": {

View File

@@ -53,7 +53,9 @@
"all-mainnet:record-canaries": "node scripts/status/record-all-mainnet-canary-evidence.mjs",
"all-mainnet:official-protocol-plan": "node scripts/status/build-official-protocol-integration-plan.mjs",
"all-mainnet:official-dodo-discovery": "node scripts/status/discover-official-dodo-pools.mjs",
"all-mainnet:official-dodo-migration": "node scripts/status/execute-official-dodo-migration.mjs"
"all-mainnet:official-dodo-migration": "node scripts/status/execute-official-dodo-migration.mjs",
"all-mainnet:remaining-routing-tasks": "node scripts/status/build-remaining-official-routing-tasks.mjs",
"all-mainnet:remaining-balances": "node scripts/status/check-remaining-deployer-balances.mjs"
},
"keywords": [
"proxmox",

View File

@@ -0,0 +1,27 @@
# ALL Mainnet Official DODO Migration Execution - 2026-04-30
## Completed Broadcasts
| Chain | Pool | Official Pool | Atomic Seeder | Transactions |
| --- | --- | --- | --- | --- |
| Optimism `10` | `10-dodo_pmm-cwusdc-usdc` | `0x2F27480932AF9Dc889668453522F988D0F16215A` | `0x2Bb4d9B882de6E7F1Cc6C437AD036324324fD0c6` | deploy seeder `0x5e4d9ee103aadd5aa4582436ccc0567db2dd5bc3c684604b4bb8f553940b32a1`; approve base `0xc4b0b4013925fb5c83a73b9e1e8ff95a70e732f788e5b78f032836408947f636`; approve quote `0x41a25d6445f17d83d23287d1e3c572fee1f36a1194cd0a6cd6ead0022dc263eb`; seed `0x48b983d72c78f2034a040d4d105d2b0dd9086e4ea54b0c72d34dcc27a7d4ebaa` |
| Optimism `10` | `10-dodo_pmm-cwusdt-usdt` | `0xf7DB4284D71B869d73924F8059fD241D0b31aa43` | `0x2Bb4d9B882de6E7F1Cc6C437AD036324324fD0c6` | approve base `0x89b531766835647133cea2cfcdb6af14b25e23b6b2217e9db7b39ed69e9a3681`; approve quote `0x0f8578bd3d3a11e9dc97c66b0df70379d0a96224d78fba843798b51796d70da2`; seed `0x08ed7e4bc2aea8d736a77a592f7179b0f8e30bcdd7b18691a6bbd130ceb11ffe` |
| Base `8453` | `8453-dodo_pmm-cwusdc-usdc` | `0xF28Ca783A7b8Ed44ca19f45F1EF0b8D7F6E7bdc6` | `0xFc2e6eED941D18998dAC029b9FD567744F4e8D86` | create pool `0xc150eb438b24882611e9b6bea649ae89e0f6d19547af26648d2c75af5d010618`; deploy seeder `0x8031e2488b07f0564fe4cc5040de3eb207276a22c88541c4728ba92d502c6652`; approve base `0xec9b1d58782bb4f8d155bda72c3f82bd1a74bb94f77312a395ebdaac42c11e9f`; approve quote `0x35d09e4c8d732a06667f6224faf4ef7e19c4bb8ff8847f0337dfccf7175dd5f0`; seed `0x1c01be26a6e103c88449ac4c3eb338cb0ee13025453ca1aa80e213da0ce68032` |
| Base `8453` | `8453-dodo_pmm-cwusdt-usdt` | `0xf6729883CbB93b10a1a1fc00EF5647819b65f65a` | `0xFc2e6eED941D18998dAC029b9FD567744F4e8D86` | create pool `0x3256cdef0a88323159bd91770dda72a28cd2f6d62fe0f12506c800fa2e802f2c`; approve base `0x1b7b1f3580699ae0a217e83482f4c67394789ed7e7aa4e3895f3ef66c65b0874`; approve quote `0x57903f95d5982fbd1295c67ba6c0d9f50503d3190410d117f3f61f16c600bd0b`; seed `0x5a98c991ac23f2eb806f8311b3014b2f5c8b2e1daa7fda993ce328873e180d9e` |
Each completed pool was seeded through `DODOAtomicSeeder` with `0.01` raw token-unit depth per side and then rediscovered with nonzero base reserve, quote reserve, and LP total supply.
## Remaining Blockers
| Chain | Pool | Blocker |
| --- | --- | --- |
| Arbitrum `42161` | `42161-dodo_pmm-cwusdc-usdc` | Official DODO pool exists at `0x370F464B6e37978a0F838c41cd9EA73732bf25BE`, but seeding reverted with `MINT_AMOUNT_NOT_ENOUGH` at the deployer wallet's available `0.002545` USDC. Top up at least `0.007455` USDC to reach the current `0.01` minimum seed target before retrying. |
| Cronos `25` | `25-dodo_pmm-cwusdc-usdc`, `25-dodo_pmm-cwusdt-usdt` | No committed official DODO DVM factory source. 1inch official Swap support is recorded as the alternate official route profile. |
| Gnosis `100` | `100-dodo_pmm-cwusdc-usdc`, `100-dodo_pmm-cwusdt-usdt` | No committed official DODO DVM factory source. 1inch official network support is recorded as the alternate official route profile. |
## Current Summary
- Seeded official DODO replacement rows: `11`
- Remaining official migration rows: `5`
- Remaining unsupported-DODO rows with official 1inch alternate route path: `4`
- Remaining zero/unusable official DODO pools: `1`

View File

@@ -8,12 +8,13 @@
* unwind/repeg/depth work needed before treating liquidity as deep production.
*/
import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
import { existsSync, mkdirSync, 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 sourcePath = resolve(repoRoot, "config/official-protocol-integration-sources.json");
const discoveryPath = resolve(repoRoot, "reports/status/all-mainnet-official-dodo-discovery-latest.json");
const outJson = resolve(repoRoot, "reports/status/all-mainnet-official-protocol-integration-plan-latest.json");
const outMd = resolve(repoRoot, "reports/status/all-mainnet-official-protocol-integration-plan-latest.md");
@@ -59,12 +60,25 @@ function dodoOfficialFactory(source, chainId) {
return source?.chainFactories?.[key]?.dvmFactory || null;
}
function replacementMigration(row, sources) {
function discoverySeeded(discoveryRow) {
return (discoveryRow?.poolEvidence || []).some((pool) => pool.seeded === true);
}
function replacementMigration(row, sources, discoveryByPoolId) {
const source = protocolSource(sources, row.protocol);
const officialFactory = row.protocol === "dodo_pmm" ? dodoOfficialFactory(source, row.chainId) : null;
const unsupported = row.protocol === "dodo_pmm"
? source?.unsupportedOrUnverifiedChains?.[String(row.chainId)]?.status || null
: null;
const discovery = discoveryByPoolId.get(row.poolId) || null;
const seededOfficialPool = discoverySeeded(discovery);
const officialPools = (discovery?.poolEvidence || []).map((pool) => ({
poolAddress: pool.poolAddress,
baseReserveRaw: pool.baseReserveRaw || null,
quoteReserveRaw: pool.quoteReserveRaw || null,
totalSupplyRaw: pool.totalSupplyRaw || null,
seeded: pool.seeded === true,
}));
return {
poolId: row.poolId,
chainId: row.chainId,
@@ -75,11 +89,15 @@ function replacementMigration(row, sources) {
temporaryReplacement: Boolean(row.replacementEvidence),
oldPoolAddress: row.replacementEvidence?.oldPoolAddress || null,
officialFactory,
officialSourceStatus: officialFactory ? "official_factory_available" : unsupported || "official_source_not_committed",
officialSourceStatus: seededOfficialPool
? "official_pool_seeded"
: officialFactory ? (discovery?.status || "official_factory_available") : unsupported || "official_source_not_committed",
officialPools,
migrationCompleteThroughSeed: seededOfficialPool,
actions: [
officialFactory ? "discover_existing_official_pool" : "confirm_official_factory_or_choose_alternate_protocol",
officialFactory ? "create_official_pool_if_absent" : "keep_replacement_pool_read_only_until_supported",
"seed_official_pool_to_target_depth",
seededOfficialPool ? "record_official_seed_evidence" : officialFactory ? "discover_existing_official_pool" : "confirm_official_factory_or_choose_alternate_protocol",
seededOfficialPool ? "run_protocol_native_canaries" : officialFactory ? "create_official_pool_if_absent" : "keep_replacement_pool_read_only_until_supported",
seededOfficialPool ? "switch_public_routing_to_official_pool_after_canary" : "seed_official_pool_to_target_depth",
"run_oracle_normalized_repeg_check",
"run_10_100_1000_canaries",
"switch_public_routing_to_official_pool",
@@ -146,11 +164,13 @@ function depthIntent(row) {
const matrix = readJson(matrixPath);
const sources = readJson(sourcePath);
const discovery = existsSync(discoveryPath) ? readJson(discoveryPath) : { rows: [] };
const discoveryByPoolId = new Map((discovery.rows || []).map((row) => [row.poolId, row]));
const generatedAt = new Date().toISOString();
const requiredRows = matrix.rows.filter((row) => row.requiredForSpend === true);
const replacementRows = requiredRows.filter((row) => row.replacementEvidence);
const optionalRows = matrix.rows.filter((row) => row.requiredForSpend !== true);
const officialMigrations = replacementRows.map((row) => replacementMigration(row, sources));
const officialMigrations = replacementRows.map((row) => replacementMigration(row, sources, discoveryByPoolId));
const optionalProtocolRows = optionalRows
.filter((row) => ["sushiswap_v2", "curve_stable", "balancer_weighted", "aave_backstop", "oneinch_aggregator", "uniswap_v2", "dodo_pmm"].includes(row.protocol))
.map((row) => optionalProtocolAction(row, sources));
@@ -179,7 +199,8 @@ const report = {
matrixFile: "config/all-mainnet-pool-creation-matrix.json",
summary: {
requiredRows: requiredRows.length,
replacementRowsNeedingOfficialMigration: replacementRows.length,
replacementRowsNeedingOfficialMigration: officialMigrations.filter((row) => !row.migrationCompleteThroughSeed).length,
replacementRowsWithSeededOfficialPool: officialMigrations.filter((row) => row.migrationCompleteThroughSeed).length,
optionalProtocolRowsNeedingOfficialEvidence: optionalProtocolRows.length,
depthRows: depthIntents.length,
unwindRows: unwindRows.length,

View File

@@ -0,0 +1,158 @@
#!/usr/bin/env node
/**
* Build the remaining task list for unsupported DODO rows and zero/unusable
* official DODO pools.
*/
import { mkdirSync, 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 sourcesPath = resolve(repoRoot, "config/official-protocol-integration-sources.json");
const discoveryPath = resolve(repoRoot, "reports/status/all-mainnet-official-dodo-discovery-latest.json");
const outJson = resolve(repoRoot, "reports/status/all-mainnet-remaining-official-routing-tasks-latest.json");
const outMd = resolve(repoRoot, "reports/status/all-mainnet-remaining-official-routing-tasks-latest.md");
function readJson(path) {
return JSON.parse(readFileSync(path, "utf8"));
}
function table(headers, rows) {
return [
`| ${headers.join(" | ")} |`,
`| ${headers.map(() => "---").join(" | ")} |`,
...rows.map((row) => `| ${row.map((cell) => String(cell ?? "").replace(/\|/g, "\\|")).join(" | ")} |`),
].join("\n");
}
function pair(row) {
return `${row.baseToken?.symbol || "?"}/${row.quoteToken?.symbol || "?"}`;
}
const matrix = readJson(matrixPath);
const sources = readJson(sourcesPath);
const discovery = readJson(discoveryPath);
const generatedAt = new Date().toISOString();
const unsupportedDodoRows = matrix.rows.filter((row) => (
row.requiredForSpend === true &&
row.protocol === "dodo_pmm" &&
[25, 100].includes(Number(row.chainId))
));
const oneInchSupport = sources.protocols?.oneinch_aggregator?.chainSupport || {};
const unsupportedRoutingTasks = unsupportedDodoRows.map((row) => {
const support = oneInchSupport[String(row.chainId)];
const hasAggregatorSupport = Boolean(support);
return {
poolId: row.poolId,
chainId: row.chainId,
network: row.network,
pair: pair(row),
currentReplacementPool: row.poolAddress,
targetSupportProtocol: hasAggregatorSupport ? "oneinch_aggregator" : "official_alternate_required",
supportStatus: support?.status || "needs_official_source",
tasks: hasAggregatorSupport ? [
"create_or_promote matching oneinch_aggregator row for this pair",
"wire official 1inch quote/swap API chain support evidence",
"verify token address import/direct contract-address quote for both directions",
"run dry-run quote and minimum-output route simulation",
"execute tiny canary only after route quote is nonzero and compliance policy passes",
"switch public routing from replacement DODO row to aggregator-backed route",
"deprecate replacement DODO row after canary and fallback route are recorded",
] : [
"select an official native DEX or aggregator source with public factory/router/API documentation",
"add protocol profile and official source registry entry",
"add or promote matrix rows for cWUSDC/USDC and cWUSDT/USDT",
"discover or create official pools through that protocol",
"seed with ISO-4217 oracle-normalized amounts",
"run canaries and switch routing only after nonzero reserves are proven",
"deprecate replacement DODO row after supported route is live",
],
};
});
const zeroPoolTasks = discovery.rows
.flatMap((row) => (row.poolEvidence || [])
.filter((pool) => pool.seeded === false)
.map((pool) => ({
poolId: row.poolId,
chainId: row.chainId,
network: row.network,
pair: row.pair,
officialPool: pool.poolAddress,
baseReserveRaw: pool.baseReserveRaw,
quoteReserveRaw: pool.quoteReserveRaw,
totalSupplyRaw: pool.totalSupplyRaw,
tasks: [
"keep publicRoutingEnabled=false for the official pool until reserves are nonzero and canary passes",
"read pool token balances and reserves from the same block to identify stranded transfers versus empty pool",
"confirm deployer quote balance meets DODO minimum mint threshold for the token decimals",
"top up short-side quote inventory by swap/bridge before retrying seed",
"seed through DODOAtomicSeeder only; do not use separate token transfers",
"rerun official DODO discovery and reserve evidence",
"run tiny canary in both directions where token transfer behavior allows",
"promote route only after ISO-4217 value-normalized peg and nonzero LP supply are recorded",
],
})));
const report = {
generatedAt,
sources: {
matrix: "config/all-mainnet-pool-creation-matrix.json",
officialSources: "config/official-protocol-integration-sources.json",
dodoDiscovery: "reports/status/all-mainnet-official-dodo-discovery-latest.json",
},
summary: {
unsupportedDodoRows: unsupportedRoutingTasks.length,
oneInchSupportableRows: unsupportedRoutingTasks.filter((row) => row.targetSupportProtocol === "oneinch_aggregator").length,
cronosRowsNeedingNativeDexProfile: unsupportedRoutingTasks.filter((row) => row.targetSupportProtocol === "cronos_native_dex_profile_required").length,
zeroOrUnusableOfficialPools: zeroPoolTasks.length,
},
unsupportedRoutingTasks,
zeroPoolTasks,
};
const md = [
"# ALL Mainnet Remaining Official Routing Tasks",
"",
`- Generated: \`${generatedAt}\``,
"",
table(["Metric", "Count"], Object.entries(report.summary)),
"",
"## Unsupported DODO Rows",
"",
table(
["Pool", "Chain", "Pair", "Target Support", "Status", "First Task"],
unsupportedRoutingTasks.map((row) => [
row.poolId,
row.chainId,
row.pair,
row.targetSupportProtocol,
row.supportStatus,
row.tasks[0],
]),
),
"",
"## Zero / Unusable Official Pools",
"",
table(
["Pool", "Chain", "Pair", "Official Pool", "Base Reserve", "Quote Reserve", "First Task"],
zeroPoolTasks.map((row) => [
row.poolId,
row.chainId,
row.pair,
row.officialPool,
row.baseReserveRaw,
row.quoteReserveRaw,
row.tasks[0],
]),
),
"",
].join("\n");
mkdirSync(resolve(repoRoot, "reports/status"), { recursive: true });
writeFileSync(outJson, `${JSON.stringify(report, null, 2)}\n`);
writeFileSync(outMd, `${md}\n`);
console.log(`[OK] Remaining official routing tasks written: ${outJson}`);

View File

@@ -0,0 +1,291 @@
#!/usr/bin/env node
/**
* Read-only deployer balance report for the remaining official-routing blockers.
*/
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
import { homedir } from "node:os";
import { resolve } from "node:path";
import { Contract, JsonRpcProvider, Wallet, formatEther, formatUnits, parseUnits } from "ethers";
const repoRoot = resolve(new URL("../..", import.meta.url).pathname);
const matrixPath = resolve(repoRoot, "config/all-mainnet-pool-creation-matrix.json");
const tasksPath = resolve(repoRoot, "reports/status/all-mainnet-remaining-official-routing-tasks-latest.json");
const discoveryPath = resolve(repoRoot, "reports/status/all-mainnet-official-dodo-discovery-latest.json");
const outJson = resolve(repoRoot, "reports/status/all-mainnet-remaining-deployer-balances-latest.json");
const outMd = resolve(repoRoot, "reports/status/all-mainnet-remaining-deployer-balances-latest.md");
const ERC20_ABI = [
"function balanceOf(address) view returns (uint256)",
"function decimals() view returns (uint8)",
"function symbol() view returns (string)",
];
const chainRpcCandidates = {
10: ["OPTIMISM_MAINNET_RPC", "OPTIMISM_RPC_URL", "OPTIMISM_RPC"],
25: ["CRONOS_RPC_URL", "CRONOS_RPC", "CRONOS_MAINNET_RPC"],
100: ["GNOSIS_MAINNET_RPC", "GNOSIS_RPC_URL", "GNOSIS_RPC"],
8453: ["BASE_MAINNET_RPC", "BASE_RPC_URL", "BASE_RPC"],
42161: ["ARBITRUM_MAINNET_RPC", "ARBITRUM_RPC_URL", "ARBITRUM_RPC"],
};
function readJson(path) {
return JSON.parse(readFileSync(path, "utf8"));
}
function loadEnvFile(path, env) {
if (!existsSync(path)) return;
for (const line of readFileSync(path, "utf8").split(/\r?\n/)) {
const trimmed = line.trim();
if (!trimmed || trimmed.startsWith("#") || !trimmed.includes("=")) continue;
const index = trimmed.indexOf("=");
const key = trimmed.slice(0, index).replace(/^export\s+/, "").trim();
let value = trimmed.slice(index + 1).trim();
if ((value.startsWith('"') && value.endsWith('"')) || (value.startsWith("'") && value.endsWith("'"))) value = value.slice(1, -1);
value = value.replace(/\s+#.*$/, "");
value = value.replace(/\$\{([^}:]+)(:-([^}]*))?\}/g, (_, name, _fallback, fallbackValue) => (
env[name] ?? process.env[name] ?? fallbackValue ?? ""
));
if (!value || value === "0x" || value.includes("${")) continue;
env[key] ??= value;
}
}
function loadEnv() {
const env = { ...process.env };
loadEnvFile(resolve(repoRoot, ".env"), env);
loadEnvFile(resolve(repoRoot, "smom-dbis-138/.env"), env);
loadEnvFile(resolve(homedir(), ".secure-secrets/private-keys.env"), env);
if (!env.PRIVATE_KEY && env.DEPLOYER_PRIVATE_KEY) env.PRIVATE_KEY = env.DEPLOYER_PRIVATE_KEY;
return env;
}
function rpcForChain(chainId, env) {
for (const key of chainRpcCandidates[chainId] || []) {
if (env[key]) return { key, url: env[key] };
}
return { key: null, url: null };
}
function table(headers, rows) {
return [
`| ${headers.join(" | ")} |`,
`| ${headers.map(() => "---").join(" | ")} |`,
...rows.map((row) => `| ${row.map((cell) => String(cell ?? "").replace(/\|/g, "\\|")).join(" | ")} |`),
].join("\n");
}
function addToken(tokensByChain, chainId, token) {
if (!token?.address) return;
const key = String(chainId);
tokensByChain[key] ??= new Map();
tokensByChain[key].set(token.address.toLowerCase(), {
symbol: token.symbol,
address: token.address,
});
}
function pair(row) {
return `${row.baseToken?.symbol || "?"}/${row.quoteToken?.symbol || "?"}`;
}
function minDodoSeedRaw(decimals) {
// DODO V2 DVM rejected 1_000/2_000 raw but accepted 10_000 raw on 6-decimal quote rows.
return parseUnits("0.01", Number(decimals));
}
const env = loadEnv();
const operator = env.DEPLOYER_ADDRESS || env.SIGNER_ADDRESS || (env.PRIVATE_KEY ? new Wallet(env.PRIVATE_KEY).address : null);
if (!operator) throw new Error("Set DEPLOYER_ADDRESS, SIGNER_ADDRESS, or PRIVATE_KEY.");
const matrix = readJson(matrixPath);
const tasks = existsSync(tasksPath) ? readJson(tasksPath) : { unsupportedRoutingTasks: [], zeroPoolTasks: [] };
const discovery = existsSync(discoveryPath) ? readJson(discoveryPath) : { rows: [] };
const unsupportedRows = matrix.rows.filter((row) => (
row.requiredForSpend === true &&
row.protocol === "dodo_pmm" &&
[25, 100].includes(Number(row.chainId))
));
const zeroRows = discovery.rows.filter((row) => (
(row.poolEvidence || []).some((pool) => pool.seeded === false)
));
const tokensByChain = {};
for (const row of [...unsupportedRows, ...zeroRows.map((zero) => matrix.rows.find((row) => row.poolId === zero.poolId)).filter(Boolean)]) {
addToken(tokensByChain, row.chainId, row.baseToken);
addToken(tokensByChain, row.chainId, row.quoteToken);
}
const chainReports = [];
for (const chainId of Object.keys(tokensByChain).map(Number).sort((a, b) => a - b)) {
const rpc = rpcForChain(chainId, env);
const chainReport = {
chainId,
rpcEnvKey: rpc.key,
nativeBalanceWei: null,
nativeBalance: null,
tokenBalances: [],
errors: [],
};
if (!rpc.url) {
chainReport.errors.push("missing_rpc_url");
chainReports.push(chainReport);
continue;
}
const provider = new JsonRpcProvider(rpc.url, chainId, { staticNetwork: true });
try {
const native = await provider.getBalance(operator);
chainReport.nativeBalanceWei = native.toString();
chainReport.nativeBalance = formatEther(native);
} catch (error) {
chainReport.errors.push(`native_balance:${error.shortMessage || error.message}`);
}
for (const token of tokensByChain[String(chainId)].values()) {
const entry = {
symbol: token.symbol,
address: token.address,
balanceRaw: null,
decimals: null,
balance: null,
minDodoSeedRaw: null,
minDodoSeed: null,
shortfallToMinDodoSeedRaw: null,
shortfallToMinDodoSeed: null,
error: null,
};
try {
const contract = new Contract(token.address, ERC20_ABI, provider);
const [decimals, balance] = await Promise.all([
contract.decimals(),
contract.balanceOf(operator),
]);
const minSeed = minDodoSeedRaw(decimals);
const shortfall = balance >= minSeed ? 0n : minSeed - balance;
entry.decimals = Number(decimals);
entry.balanceRaw = balance.toString();
entry.balance = formatUnits(balance, decimals);
entry.minDodoSeedRaw = minSeed.toString();
entry.minDodoSeed = formatUnits(minSeed, decimals);
entry.shortfallToMinDodoSeedRaw = shortfall.toString();
entry.shortfallToMinDodoSeed = formatUnits(shortfall, decimals);
} catch (error) {
entry.error = error.shortMessage || error.message;
}
chainReport.tokenBalances.push(entry);
}
chainReports.push(chainReport);
}
const unsupportedTaskRows = unsupportedRows.map((row) => {
const task = (tasks.unsupportedRoutingTasks || []).find((item) => item.poolId === row.poolId);
return {
poolId: row.poolId,
chainId: row.chainId,
network: row.network,
pair: pair(row),
currentReplacementPool: row.poolAddress,
targetSupportProtocol: task?.targetSupportProtocol || "pending_official_route_profile",
minimumSpendPlan: task?.targetSupportProtocol === "oneinch_aggregator"
? "No pool seed required; minimum spend is gas plus a tiny route canary after nonzero official 1inch quote is proven."
: "Needs official protocol selection first; no safe spend amount can be calculated until factory/router is confirmed.",
};
});
const zeroTaskRows = zeroRows.map((zero) => {
const matrixRow = matrix.rows.find((row) => row.poolId === zero.poolId);
const evidence = (zero.poolEvidence || []).find((pool) => pool.seeded === false);
const chainReport = chainReports.find((chain) => chain.chainId === zero.chainId);
const quoteBalance = chainReport?.tokenBalances.find((token) => token.symbol === matrixRow?.quoteToken?.symbol);
return {
poolId: zero.poolId,
chainId: zero.chainId,
network: zero.network,
pair: zero.pair,
officialPool: evidence?.poolAddress || null,
baseReserveRaw: evidence?.baseReserveRaw || null,
quoteReserveRaw: evidence?.quoteReserveRaw || null,
totalSupplyRaw: evidence?.totalSupplyRaw || null,
quoteSymbol: matrixRow?.quoteToken?.symbol || null,
deployerQuoteBalance: quoteBalance?.balance || null,
minimumQuoteNeeded: quoteBalance?.minDodoSeed || "0.01",
quoteShortfall: quoteBalance?.shortfallToMinDodoSeed || null,
minimumSpendPlan: "Top up quote to at least 0.01 token units, then seed equal base/quote through DODOAtomicSeeder.",
};
});
const report = {
generatedAt: new Date().toISOString(),
mode: "read_only_deployer_balance_inventory",
operator,
summary: {
unsupportedDodoRows: unsupportedTaskRows.length,
zeroOrUnusableOfficialPools: zeroTaskRows.length,
chainsChecked: chainReports.length,
},
chainReports,
unsupportedTaskRows,
zeroTaskRows,
};
const md = [
"# ALL Mainnet Remaining Deployer Balances",
"",
`- Generated: \`${report.generatedAt}\``,
`- Operator: \`${operator}\``,
"",
table(["Metric", "Count"], Object.entries(report.summary)),
"",
"## Chain Balances",
"",
table(
["Chain", "Native", "Token", "Balance", "Min DODO Seed", "Shortfall"],
chainReports.flatMap((chain) => chain.tokenBalances.map((token) => [
chain.chainId,
chain.nativeBalance,
token.symbol,
token.balance ?? token.error,
token.minDodoSeed,
token.shortfallToMinDodoSeed,
])),
),
"",
"## Unsupported Rows",
"",
table(
["Pool", "Chain", "Pair", "Target", "Minimum Spend Plan"],
unsupportedTaskRows.map((row) => [
row.poolId,
row.chainId,
row.pair,
row.targetSupportProtocol,
row.minimumSpendPlan,
]),
),
"",
"## Zero / Unusable Official Pools",
"",
table(
["Pool", "Chain", "Pair", "Official Pool", "Quote Balance", "Quote Shortfall", "Minimum Spend Plan"],
zeroTaskRows.map((row) => [
row.poolId,
row.chainId,
row.pair,
row.officialPool,
row.deployerQuoteBalance,
row.quoteShortfall,
row.minimumSpendPlan,
]),
),
"",
].join("\n");
mkdirSync(resolve(repoRoot, "reports/status"), { recursive: true });
writeFileSync(outJson, `${JSON.stringify(report, null, 2)}\n`);
writeFileSync(outMd, `${md}\n`);
console.log(`[OK] Remaining deployer balance report written: ${outJson}`);

View File

@@ -22,6 +22,11 @@ const DVM_FACTORY_ABI = [
"function getDODOPoolBidirection(address token0,address token1) view returns (address[] baseToken0Machines,address[] baseToken1Machines)",
"function getDODOPool(address baseToken,address quoteToken) view returns (address[] machines)",
];
const DVM_POOL_ABI = [
"function getVaultReserve() view returns (uint256,uint256)",
"function totalSupply() view returns (uint256)",
"function getMidPrice() view returns (uint256)",
];
const ZERO = "0x0000000000000000000000000000000000000000";
@@ -30,6 +35,7 @@ const chainRpcCandidates = {
10: ["OPTIMISM_MAINNET_RPC", "OPTIMISM_RPC_URL"],
56: ["BSC_MAINNET_RPC", "BSC_RPC_URL"],
137: ["POLYGON_MAINNET_RPC", "POLYGON_RPC_URL"],
8453: ["BASE_MAINNET_RPC", "BASE_RPC_URL", "BASE_RPC"],
42161: ["ARBITRUM_MAINNET_RPC", "ARBITRUM_RPC_URL"],
43114: ["AVALANCHE_MAINNET_RPC", "AVALANCHE_RPC_URL"],
};
@@ -160,6 +166,31 @@ async function discoverRow(row, source, env) {
const [forward, reverse] = await factoryContract.getDODOPoolBidirection(baseToken, quoteToken);
const existingPools = [...forward].filter((address) => address !== ZERO);
const reversePools = [...reverse].filter((address) => address !== ZERO);
const poolEvidence = [];
for (const address of [...existingPools, ...reversePools]) {
const pool = new Contract(address, DVM_POOL_ABI, provider);
try {
const [reserves, totalSupply, midPrice] = await Promise.all([
pool.getVaultReserve(),
pool.totalSupply(),
pool.getMidPrice().catch(() => null),
]);
poolEvidence.push({
poolAddress: address,
baseReserveRaw: reserves[0].toString(),
quoteReserveRaw: reserves[1].toString(),
totalSupplyRaw: totalSupply.toString(),
midPriceRaw: midPrice?.toString() || null,
seeded: reserves[0] > 0n && reserves[1] > 0n && totalSupply > 0n,
});
} catch (error) {
poolEvidence.push({
poolAddress: address,
error: String(error?.shortMessage || error?.message || error),
seeded: false,
});
}
}
return {
poolId: row.poolId,
chainId,
@@ -172,6 +203,7 @@ async function discoverRow(row, source, env) {
status: existingPools.length || reversePools.length ? "official_pool_exists" : "official_pool_missing",
existingPools,
reversePools,
poolEvidence,
recommendedAction: existingPools.length || reversePools.length
? "validate_reserves_canary_then_switch_routes"
: "create_official_dvm_pool_then_seed_and_canary",

View File

@@ -66,6 +66,7 @@ const chainRpcCandidates = {
10: ["OPTIMISM_MAINNET_RPC", "OPTIMISM_RPC_URL", "OPTIMISM_RPC"],
56: ["BSC_MAINNET_RPC", "BSC_RPC_URL", "BSC_RPC"],
137: ["POLYGON_MAINNET_RPC", "POLYGON_RPC_URL", "POLYGON_RPC"],
8453: ["BASE_MAINNET_RPC", "BASE_RPC_URL", "BASE_RPC"],
42161: ["ARBITRUM_MAINNET_RPC", "ARBITRUM_RPC_URL", "ARBITRUM_RPC"],
43114: ["AVALANCHE_MAINNET_RPC", "AVALANCHE_RPC_URL", "AVALANCHE_RPC"],
};