@anywalletkit/protocol-swap2-curve 0.0.0-stage → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/activity.ts","../src/abi.ts","../src/deployments.ts"],"sourcesContent":["/**\n * Activity recognition for Curve: a call into the chain's pinned Router NG — or, on Ethereum, the older v1.1 / v1.0\n * routers (history only) — with one of the family's `exchange` entry points → `swap`, \"Swapped A → B\" from the owner's\n * net transfers. A swap whose receiver is not one of the owner's addresses (an explicit `_receiver`, read from the\n * calldata or the router's `Exchange` log) says so in the subtitle and names that receiver as the counterparty.\n */\nimport type {ActivityItem, ChainTx, DescribeActivityContext} from \"@anywalletkit/core\";\nimport {calledContract, chainLabel, describeSwapLike, shortAddress} from \"@anywalletkit/protocol-swap2-core/activity\";\nimport {evmRef, isEvm} from \"@anywalletkit/protocol-swap2-core/util\";\nimport {EXCHANGE_TOPIC, ROUTER_SELECTORS, decodeExchange} from \"./abi.js\";\nimport {CURVE_DEPLOYMENTS, CURVE_LEGACY_ROUTERS, deploymentOf, type CurveDeployment, type CurveRouterFamily} from \"./deployments.js\";\n\nexport const CURVE_MODULE = \"@anywalletkit/protocol-swap2-curve\";\n\n/** The router family `to` speaks on `chainId`, when it is a router this module knows there. */\nfunction familyOf(deployments: Readonly<Record<number, CurveDeployment>>, chainId: number, to: string): CurveRouterFamily | null {\n const d = deploymentOf(deployments, chainId);\n if (d && d.router.toLowerCase() === to) return d.family;\n const legacy = Object.prototype.hasOwnProperty.call(CURVE_LEGACY_ROUTERS, chainId) ? CURVE_LEGACY_ROUTERS[chainId]! : [];\n return legacy.some((r) => r.router.toLowerCase() === to) ? \"ng5\" : null;\n}\n\n/** Who the swap paid: the calldata's `_receiver`, else the router's `Exchange` log (topic 2), else the sender. */\nfunction receiverOf(tx: ChainTx, family: CurveRouterFamily): string {\n const data = tx.call?.data;\n if (data) {\n const call = decodeExchange(family, data);\n if (call?.receiver) return call.receiver.toLowerCase();\n if (call) return tx.from.toLowerCase();\n }\n const logs = (tx.raw as {logs?: unknown} | undefined)?.logs ?? (tx.raw as {receipt?: {logs?: unknown}} | undefined)?.receipt?.logs;\n if (Array.isArray(logs)) {\n const to = calledContract(tx);\n for (const l of logs as {address?: string; topics?: string[]}[]) {\n if (l?.address?.toLowerCase() !== to || l.topics?.[0]?.toLowerCase() !== EXCHANGE_TOPIC[family]) continue;\n const t = l.topics[2];\n if (typeof t === \"string\" && /^0x[0-9a-fA-F]{64}$/.test(t)) return `0x${t.slice(-40)}`.toLowerCase();\n }\n }\n return tx.from.toLowerCase();\n}\n\nexport function describeCurveActivity(\n tx: ChainTx,\n ctx: DescribeActivityContext,\n deployments: Readonly<Record<number, CurveDeployment>> = CURVE_DEPLOYMENTS,\n): ActivityItem | null {\n if (!isEvm(tx.chain)) return null;\n const to = calledContract(tx);\n if (!to) return null;\n const family = familyOf(deployments, evmRef(tx.chain), to);\n if (!family) return null;\n const sel = tx.call?.selector?.toLowerCase() ?? tx.call?.data?.slice(0, 10).toLowerCase();\n if (!sel || !ROUTER_SELECTORS[family].has(sel)) return null;\n const receiver = receiverOf(tx, family);\n const ownReceiver = ctx.own.has(receiver) || [...ctx.own].some((a) => a.toLowerCase() === receiver);\n const subtitle = `Curve · ${chainLabel(tx.chain)}${ownReceiver ? \"\" : ` · paid to ${shortAddress(receiver)}`}`;\n return describeSwapLike(tx, ctx, {protocol: CURVE_MODULE, subtitle, kind: \"swap\", ...(ownReceiver ? {} : {counterparty: receiver})});\n}\n","import {decodeFunctionData, encodeFunctionData, parseAbi} from \"viem\";\nimport type {CurveRouterFamily} from \"./deployments.js\";\nimport {ZERO_ADDRESS} from \"./deployments.js\";\nimport type {Hex} from \"./pools/types.js\";\n\n/**\n * Router NG entry points, both ABI families (selectors with `viem toFunctionSelector`, checked against every pinned\n * router's code 2026-11-05). This module writes only the explicit-receiver `exchange` of each family and reads\n * `get_dy`; the other forms are listed for history recognition.\n */\nexport const ROUTER_NG5_ABI = parseAbi([\n \"function exchange(address[11] _route, uint256[5][5] _swap_params, uint256 _amount, uint256 _min_dy) payable returns (uint256)\",\n \"function exchange(address[11] _route, uint256[5][5] _swap_params, uint256 _amount, uint256 _min_dy, address[5] _pools) payable returns (uint256)\",\n \"function exchange(address[11] _route, uint256[5][5] _swap_params, uint256 _amount, uint256 _min_dy, address[5] _pools, address _receiver) payable returns (uint256)\",\n \"function get_dy(address[11] _route, uint256[5][5] _swap_params, uint256 _amount) view returns (uint256)\",\n \"function version() view returns (string)\",\n]);\nexport const ROUTER_LITE4_ABI = parseAbi([\n \"function exchange(address[11] _route, uint256[4][5] _swap_params, uint256 _amount, uint256 _min_dy) payable returns (uint256)\",\n \"function exchange(address[11] _route, uint256[4][5] _swap_params, uint256 _amount, uint256 _min_dy, address _receiver) payable returns (uint256)\",\n \"function get_dy(address[11] _route, uint256[4][5] _swap_params, uint256 _amount) view returns (uint256)\",\n \"function version() view returns (string)\",\n]);\nexport const VERSION_ABI = parseAbi([\"function version() view returns (string)\"]);\n/** `coins(uint256)` (every NG pool and most plain ones) and `coins(int128)` (the oldest stable pools). */\nexport const COINS_UINT: Hex = \"0xc6610657\";\nexport const COINS_INT: Hex = \"0x23746eb8\";\nexport const ADDRESS_PROVIDER_ABI = parseAbi([\"function get_address(uint256 _id) view returns (address)\"]);\n\n/** The `exchange` this module builds: with an explicit receiver. */\nexport const EXCHANGE_SELECTOR: Readonly<Record<CurveRouterFamily, Hex>> = Object.freeze({ng5: \"0xc872a3c5\", lite4: \"0xfd44959c\"});\nexport const GET_DY_SELECTOR: Readonly<Record<CurveRouterFamily, Hex>> = Object.freeze({ng5: \"0x81889a2c\", lite4: \"0x11e21386\"});\n/** Every swap entry point per family, for history: `[5][5]` 4-, 5- and 6-argument forms; `[4][5]` 4- and 5-argument. */\nexport const ROUTER_SELECTORS: Readonly<Record<CurveRouterFamily, ReadonlySet<string>>> = Object.freeze({\n ng5: new Set([\"0x371dc447\", \"0x5c9c18e2\", \"0xc872a3c5\"]),\n lite4: new Set([\"0xaad348a2\", \"0xfd44959c\"]),\n});\n/** topic0 of the router's `Exchange` event per family (`[5][5]` logs `pools` too). */\nexport const EXCHANGE_TOPIC: Readonly<Record<CurveRouterFamily, Hex>> = Object.freeze({\n ng5: \"0x56d0661e240dfb199ef196e16e6f42473990366314f0226ac978f7be3cd9ee83\",\n lite4: \"0x6324485a21bc5138ec352d4949d0adc75d8e1382a687e1aa1ce6863f40c9e463\",\n});\n\n/** A route as the router takes it: 11 addresses and one swap-params row per step (5 rows; 5 or 4 columns). */\nexport interface RouterArgs {\n route: readonly Hex[];\n params: readonly (readonly bigint[])[];\n}\n\n/** One router call, decoded. `receiver` is null when the call names none (the router then pays `msg.sender`). */\nexport interface DecodedExchange extends RouterArgs {\n amount: bigint;\n minDy: bigint;\n pools: readonly Hex[] | null;\n receiver: Hex | null;\n selector: Hex;\n}\n\nconst ZERO_POOLS: readonly Hex[] = Object.freeze([ZERO_ADDRESS, ZERO_ADDRESS, ZERO_ADDRESS, ZERO_ADDRESS, ZERO_ADDRESS]);\ntype Row5 = readonly [bigint, bigint, bigint, bigint, bigint];\ntype Row4 = readonly [bigint, bigint, bigint, bigint];\nconst tuple11 = (a: readonly Hex[]) => a as unknown as readonly [Hex, Hex, Hex, Hex, Hex, Hex, Hex, Hex, Hex, Hex, Hex];\nconst rows5 = (p: readonly (readonly bigint[])[]) => p as unknown as readonly [Row5, Row5, Row5, Row5, Row5];\nconst rows4 = (p: readonly (readonly bigint[])[]) => p as unknown as readonly [Row4, Row4, Row4, Row4, Row4];\nconst pools5 = (a: readonly Hex[]) => a as unknown as readonly [Hex, Hex, Hex, Hex, Hex];\n\nfunction checkShape(family: CurveRouterFamily, args: RouterArgs): void {\n const cols = family === \"ng5\" ? 5 : 4;\n if (args.route.length !== 11 || args.params.length !== 5 || args.params.some((r) => r.length !== cols)) {\n throw new Error(`curve: a ${family} route is address[11] and uint256[${cols}][5]`);\n }\n}\n\n/** `exchange(route, params, amount, minDy, [zero pools,] receiver)` for the router family. */\nexport function encodeExchange(family: CurveRouterFamily, args: RouterArgs, amount: bigint, minDy: bigint, receiver: Hex): Hex {\n checkShape(family, args);\n return family === \"ng5\"\n ? encodeFunctionData({abi: ROUTER_NG5_ABI, functionName: \"exchange\", args: [tuple11(args.route), rows5(args.params), amount, minDy, pools5(ZERO_POOLS), receiver]})\n : encodeFunctionData({abi: ROUTER_LITE4_ABI, functionName: \"exchange\", args: [tuple11(args.route), rows4(args.params), amount, minDy, receiver]});\n}\n\n/** `get_dy(route, params, amount)` for the router family. */\nexport function encodeGetDy(family: CurveRouterFamily, args: RouterArgs, amount: bigint): Hex {\n checkShape(family, args);\n return family === \"ng5\"\n ? encodeFunctionData({abi: ROUTER_NG5_ABI, functionName: \"get_dy\", args: [tuple11(args.route), rows5(args.params), amount]})\n : encodeFunctionData({abi: ROUTER_LITE4_ABI, functionName: \"get_dy\", args: [tuple11(args.route), rows4(args.params), amount]});\n}\n\n/** Any of the family's `exchange` forms, decoded; null for anything else (another selector, malformed data). */\nexport function decodeExchange(family: CurveRouterFamily, data: string): DecodedExchange | null {\n if (!/^0x[0-9a-fA-F]*$/.test(data) || data.length < 10) return null;\n const selector = data.slice(0, 10).toLowerCase() as Hex;\n if (!ROUTER_SELECTORS[family].has(selector)) return null;\n try {\n if (family === \"ng5\") {\n const c = decodeFunctionData({abi: ROUTER_NG5_ABI, data: data as Hex});\n if (c.functionName !== \"exchange\") return null;\n const a = c.args as readonly unknown[];\n return {\n selector,\n route: a[0] as readonly Hex[],\n params: a[1] as readonly (readonly bigint[])[],\n amount: a[2] as bigint,\n minDy: a[3] as bigint,\n pools: a.length >= 5 ? (a[4] as readonly Hex[]) : null,\n receiver: a.length >= 6 ? (a[5] as Hex) : null,\n };\n }\n const c = decodeFunctionData({abi: ROUTER_LITE4_ABI, data: data as Hex});\n if (c.functionName !== \"exchange\") return null;\n const a = c.args as readonly unknown[];\n return {\n selector,\n route: a[0] as readonly Hex[],\n params: a[1] as readonly (readonly bigint[])[],\n amount: a[2] as bigint,\n minDy: a[3] as bigint,\n pools: null,\n receiver: a.length >= 5 ? (a[4] as Hex) : null,\n };\n } catch {\n return null;\n }\n}\n","import type {Hex} from \"./pools/types.js\";\n\n/**\n * Curve Router NG deployments this module trusts, one per chain, keyed by chain id (the same address means a different\n * contract on another chain: `0x4f37…fc1F` is a `[5][5]` router on Base and a `[4][5]` one on Mantle). Every entry was\n * read on-chain on 2026-11-05: the runtime code's keccak (the router has no owner and no proxy; its immutables — the\n * wrapped native, and on `[5][5]` routers the two calc zaps — are inside the code, so the hash pins them too),\n * `version()`, the wrapped native read from the code's immutable tail, and what the chain's AddressProvider lists as\n * id 2. The provider re-checks the code hash and the version once per chain before quoting (`verifyDeployment`).\n *\n * The router address is never resolved at runtime: the AddressProvider is an informational registry its admin (an\n * individual, not the DAO) can change at any time, so its id 2 is only compared and a difference only warned about.\n */\n\n/** ABI family of a router: `ng5` = `uint256[5][5]` swap params `[i, j, swap_type, pool_type, n_coins]` plus a\n * `_pools[5]` argument (curve-router-ng `Router.vy`); `lite4` = `uint256[4][5]` `[i, j, swap_type, pool_type]`, no\n * `_pools` (curve-core `router_v_110.vy` / `router_v_111.vy`, NG pools only). */\nexport type CurveRouterFamily = \"ng5\" | \"lite4\";\n\nexport interface CurveDeployment {\n /** Human label for logs and subtitles. */\n label: string;\n family: CurveRouterFamily;\n router: Hex;\n /** `version()` as the router reports it. */\n version: \"1.2.0\" | \"1.1.0\" | \"1.1.1\";\n /** keccak256 of the router's runtime code. */\n codeHash: Hex;\n /** The wrapped native the router's swap_type 8 hop wraps / unwraps (its `WETH_ADDRESS` immutable). */\n weth: Hex;\n /** The router's native sentinel (`ETH_ADDRESS`; a constructor immutable on v1.1.1). */\n eth: Hex;\n /** The chain's Curve AddressProvider (watch signal only) and the address its id 2 listed when pinned. */\n addressProvider: Hex;\n addressProviderRouter: Hex;\n /** ERC-4626 pseudo-hop (swap_type 9) the router may use — only on router v1.2 (Ethereum: crvUSD ↔ scrvUSD). */\n erc4626?: {vault: Hex; asset: Hex};\n}\n\n/** The native sentinel every pinned router uses (checked against the v1.1.1 routers' immutable). */\nexport const CURVE_ETH: Hex = \"0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE\";\nexport const ZERO_ADDRESS: Hex = \"0x0000000000000000000000000000000000000000\";\n/** Most pool hops one route crosses (pseudo-hops — wrap, ERC-4626 — come on top). */\nexport const MAX_ROUTE_POOLS = 2;\n/** Most router steps one route may have: two pools, a wrap in front and an unwrap or an ERC-4626 hop behind. */\nexport const MAX_ROUTE_STEPS = 4;\n\n/** Savings crvUSD (Yearn V3 vault, ERC-4626 over crvUSD) on Ethereum, and crvUSD itself. */\nexport const SCRVUSD: Hex = \"0x0655977FEb2f289A4aB78af67BAB0d17aAb84367\";\nexport const CRVUSD_ETHEREUM: Hex = \"0xf939E0A03FB07F59A73314E73794Be0E57ac1b4E\";\n\nconst AP: Hex = \"0x5ffe7FB82894076ECB99A30D6A32e969e6e35E98\";\nconst R_OP_POLY_GNO_AVAX: Hex = \"0x0DCDED3545D565bA3B19E683431381007245d983\";\nconst R_UNI_HYPER: Hex = \"0xd2002373543Ce3527023C75e7518C274A51ce712\";\nconst R_MONAD_PLASMA_RH: Hex = \"0xFF5Cb29241F002fFeD2eAa224e3e996D24A6E8d1\";\nconst R_BASE_MANTLE: Hex = \"0x4f37A9d177470499A2dD084621020b023fcffc1F\";\nconst AP_UNI_HYPER: Hex = \"0x1764ee18e8B3ccA4787249Ceb249356192594585\";\nconst AP_MONAD_PLASMA_RH: Hex = \"0x4574921eb950d3Fd5B01562162EC566Cb8bc3648\";\nconst OP_WETH: Hex = \"0x4200000000000000000000000000000000000006\";\n\nconst d = (o: Omit<CurveDeployment, \"eth\"> & {eth?: Hex}): CurveDeployment => Object.freeze({eth: CURVE_ETH, ...o});\n\nexport const CURVE_DEPLOYMENTS: Readonly<Record<number, CurveDeployment>> = Object.freeze({\n // [5][5] routers. Ethereum runs v1.2 (the router Curve's UI, curve-js and the curve-router-ng README use; the only one\n // with the ERC-4626 hop) — the AddressProvider still lists v1.1 there.\n 1: d({label: \"Ethereum\", family: \"ng5\", router: \"0x45312ea0eFf7E09C83CBE249fa1d7598c4C8cd4e\", version: \"1.2.0\", codeHash: \"0xbe5f7301254e95d27c6aa1be0fedc0aa04d6c36097e089bc9d2cccaa9d26b164\", weth: \"0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2\", addressProvider: AP, addressProviderRouter: \"0x16C6521Dff6baB339122a0FE25a9116693265353\", erc4626: {vault: SCRVUSD, asset: CRVUSD_ETHEREUM}}),\n 42161: d({label: \"Arbitrum\", family: \"ng5\", router: \"0x2191718CD32d02B8E60BAdFFeA33E4B5DD9A0A0D\", version: \"1.1.0\", codeHash: \"0x9b387b5dc8fe161c7af8e850a67662e93d7116b0e66ab6bc7761d5910093bcef\", weth: \"0x82aF49447D8a07e3bd95BD0d56f35241523fBab1\", addressProvider: AP, addressProviderRouter: \"0x2191718CD32d02B8E60BAdFFeA33E4B5DD9A0A0D\"}),\n 8453: d({label: \"Base\", family: \"ng5\", router: R_BASE_MANTLE, version: \"1.1.0\", codeHash: \"0x3abb7fe467b6c925ab1b25771cf1c75cc507aec2f873a8d03ee95160c5a83579\", weth: OP_WETH, addressProvider: AP, addressProviderRouter: R_BASE_MANTLE}),\n 10: d({label: \"OP Mainnet\", family: \"ng5\", router: R_OP_POLY_GNO_AVAX, version: \"1.1.0\", codeHash: \"0x12ae037c1e9a21aeb6ba1b08863217de2babebbee58dbe8860a01ded6a974825\", weth: OP_WETH, addressProvider: AP, addressProviderRouter: R_OP_POLY_GNO_AVAX}),\n 137: d({label: \"Polygon\", family: \"ng5\", router: R_OP_POLY_GNO_AVAX, version: \"1.1.0\", codeHash: \"0x340f3ba3f30181c50e694f1a4da2f6ec670fc7eb0adce50d66a733f17ab150f4\", weth: \"0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270\", addressProvider: AP, addressProviderRouter: R_OP_POLY_GNO_AVAX}),\n 100: d({label: \"Gnosis\", family: \"ng5\", router: R_OP_POLY_GNO_AVAX, version: \"1.1.0\", codeHash: \"0x38dc1cc456be010539e596d2ee0aa82c271316bdc6daaaffea7990a0c47176ee\", weth: \"0xe91D153E0b41518A2Ce8Dd3D7944Fa863463a97d\", addressProvider: AP, addressProviderRouter: R_OP_POLY_GNO_AVAX}),\n 43114: d({label: \"Avalanche\", family: \"ng5\", router: R_OP_POLY_GNO_AVAX, version: \"1.1.0\", codeHash: \"0xf1f679ec6382cd9c0138870e320e6128193f75144501691e67dcd5831edaed2b\", weth: \"0xB31f66AA3C1e785363F0875A1B74E27b85FD66c7\", addressProvider: AP, addressProviderRouter: R_OP_POLY_GNO_AVAX}),\n 56: d({label: \"BNB Chain\", family: \"ng5\", router: \"0xA72C85C258A81761433B4e8da60505Fe3Dd551CC\", version: \"1.1.0\", codeHash: \"0x19db989a1cca08711ff68049326a5a7a8d1f64d87f356d35243150215c722120\", weth: \"0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c\", addressProvider: AP, addressProviderRouter: \"0xA72C85C258A81761433B4e8da60505Fe3Dd551CC\"}),\n // [4][5] routers (curve-core, NG pools only, no swap_type 9).\n 146: d({label: \"Sonic\", family: \"lite4\", router: \"0x5eeE3091f747E60a045a2E715a4c71e600e31F6E\", version: \"1.1.0\", codeHash: \"0xc5f238aee45c18efc927708fe57821cca4a84ec1bff4f22e778dfcd108411b99\", weth: \"0x039e2fB66102314Ce7b64Ce5Ce3E5183bc94aD38\", addressProvider: \"0x87FE17697D0f14A222e8bEf386a0860eCffDD617\", addressProviderRouter: \"0x5eeE3091f747E60a045a2E715a4c71e600e31F6E\"}),\n 130: d({label: \"Unichain\", family: \"lite4\", router: R_UNI_HYPER, version: \"1.1.0\", codeHash: \"0xe911928fab7efb72e10ee3a918dcdfb44ac5bae1c4d46eb5534bea0259d2594c\", weth: OP_WETH, addressProvider: AP_UNI_HYPER, addressProviderRouter: R_UNI_HYPER}),\n 999: d({label: \"HyperEVM\", family: \"lite4\", router: R_UNI_HYPER, version: \"1.1.0\", codeHash: \"0x1040124f623348643d919029ed513670cdb825bd0135f846ce9028329d8eb77d\", weth: \"0x5555555555555555555555555555555555555555\", addressProvider: AP_UNI_HYPER, addressProviderRouter: R_UNI_HYPER}),\n 5000: d({label: \"Mantle\", family: \"lite4\", router: R_BASE_MANTLE, version: \"1.1.0\", codeHash: \"0xe76d585fb4163cd886c99f1c12361c5644e18b8dab2d86e3fe223307e7a16f29\", weth: \"0x78c1b0C915c4FAA5FffA6CAbf0219DA63d7f4cb8\", addressProvider: AP, addressProviderRouter: R_BASE_MANTLE}),\n 143: d({label: \"Monad\", family: \"lite4\", router: R_MONAD_PLASMA_RH, version: \"1.1.0\", codeHash: \"0x7668fc406894fb811ab7f9a1099f2e33ef8d80f58327e623bbe4c4d51e406f58\", weth: \"0x3bd359C1119dA7Da1D913D1C4D2B7c461115433A\", addressProvider: AP_MONAD_PLASMA_RH, addressProviderRouter: R_MONAD_PLASMA_RH}),\n 9745: d({label: \"Plasma\", family: \"lite4\", router: R_MONAD_PLASMA_RH, version: \"1.1.0\", codeHash: \"0xeede894a379cb95c73ece834e836ff9bbaa1ff613fb7913d0cbc3a48b2897120\", weth: \"0x6100E367285b01F48D07953803A2d8dCA5D19873\", addressProvider: AP_MONAD_PLASMA_RH, addressProviderRouter: R_MONAD_PLASMA_RH}),\n // v1.1.1: ETH_ADDRESS is a constructor immutable — 0xEeee… here, read from the code tail with WETH_ADDRESS.\n 4663: d({label: \"Robinhood Chain\", family: \"lite4\", router: R_MONAD_PLASMA_RH, version: \"1.1.1\", codeHash: \"0x716ad3720f54c96effe415598d3a62b9907e53c58c7e3d3004cba6cdb5c297a6\", weth: \"0x0Bd7D308f8E1639FAb988df18A8011f41EAcAD73\", eth: CURVE_ETH, addressProvider: AP_MONAD_PLASMA_RH, addressProviderRouter: R_MONAD_PLASMA_RH}),\n 57073: d({label: \"Ink\", family: \"lite4\", router: \"0xd7E72f3615aa65b92A4DBdC211E296a35512988B\", version: \"1.1.0\", codeHash: \"0x511c1733063263ba386b0e6b01b51d777deb02ea1b6e75fcde1e572e47f22436\", weth: OP_WETH, addressProvider: \"0x604388Bb1159AFd21eB5191cE22b4DeCdEE2Ae22\", addressProviderRouter: \"0xd7E72f3615aa65b92A4DBdC211E296a35512988B\"}),\n 196: d({label: \"X Layer\", family: \"lite4\", router: \"0xBFab8ebc836E1c4D81837798FC076D219C9a1855\", version: \"1.1.0\", codeHash: \"0xfc0a3b12868c10a8a46e33f17504f8751c83d3842f715c665c3d827bfc4ed625\", weth: \"0xe538905cf8410324e03A5A23C1c177a474D59b2b\", addressProvider: AP, addressProviderRouter: \"0xBFab8ebc836E1c4D81837798FC076D219C9a1855\"}),\n 98866: d({label: \"Plume\", family: \"lite4\", router: \"0x3F5A41B922a76759b9C77D36b3d337E88cD1dc5e\", version: \"1.1.0\", codeHash: \"0x0819b95039f1e0ac56aefffc8a727e996b3afa831fd3bb2047a3737bf23ab5e4\", weth: \"0xEa237441c92CAe6FC17Caaf9a7acB3f953be4bd1\", addressProvider: \"0x907CE289E9Ee0BBcb0F69132B1C28A9Affc29FCA\", addressProviderRouter: \"0x3F5A41B922a76759b9C77D36b3d337E88cD1dc5e\"}),\n});\n\n/**\n * Older Ethereum routers recognized in history only (never quoted or approved): Router NG v1.1 (what the\n * AddressProvider lists as id 2) and v1.0. Both `[5][5]`, both immutable (checked 2026-11-05).\n */\nexport const CURVE_LEGACY_ROUTERS: Readonly<Record<number, readonly {router: Hex; version: string; codeHash: Hex}[]>> = Object.freeze({\n 1: Object.freeze([\n {router: \"0x16C6521Dff6baB339122a0FE25a9116693265353\" as Hex, version: \"1.1.0\", codeHash: \"0xf041e5b17dc8b2c417a9561eb265145e442ab25b789d9bd3527c6b68a840577b\" as Hex},\n {router: \"0xF0d4c12A5768D806021F80a262B4d39d26C58b8D\" as Hex, version: \"1.0.0\", codeHash: \"0xdfc206a29f04b4cee5b7b8d5b60ffcd2574fc9ff17cc5ec00c58b17013ea3f3b\" as Hex},\n ]),\n});\n\n/**\n * Pools a route never crosses, whatever an allowlist says (defense in depth; the allowlist generator excludes the same\n * addresses). Ethereum: a registered stable-ng pool pairing real USDT with a token that calls itself \"USDT\"\n * (`0x810e…ae9`); the four pools drained by the 2023 Vyper reentrancy-lock bug (alETH, pETH, msETH, CRV/ETH — still\n * registered on-chain); the Iron Bank lending pool cyDAI/cyUSDC/cyUSDT, whose underlying route redeems Iron Bank\n * cTokens. Polygon: amWBTC/renBTC (renBTC is dead).\n */\nexport const CURVE_DENIED_POOLS: Readonly<Record<number, ReadonlySet<string>>> = Object.freeze({\n 1: new Set([\n \"0xa6d9f4f3a67b35e81dfa560b0fcde9b0751f1f53\",\n \"0xc4c319e2d4d66cca4464c0c2b32c9bd23ebe784e\",\n \"0x9848482da3ee3076165ce6497eda906e66bb85c5\",\n \"0xc897b98272aa23714464ea2a0bd5180f1b8c0025\",\n \"0x8301ae4fc9c624d1d396cbdaa1ed877821d7c511\",\n \"0x2dded6da1bf5dbdf597c45fcfaa3194e53ecfeaf\",\n ]),\n 137: new Set([\n \"0xc2d95eef97ec6c17551d45e77b590dc1f9117c67\",\n ]),\n});\n\n/** The chain id of an `eip155:<id>` CAIP-2 string, or NaN. */\nexport function chainIdOf(caip2: string): number {\n return /^eip155:[0-9]+$/.test(caip2) ? Number(caip2.slice(7)) : NaN;\n}\n\n/** The deployment pinned for `chainId` in `table` (own keys only), or undefined. */\nexport function deploymentOf(table: Readonly<Record<number, CurveDeployment>>, chainId: number): CurveDeployment | undefined {\n return Object.prototype.hasOwnProperty.call(table, chainId) ? table[chainId] : undefined;\n}\n"],"mappings":"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","names":["c","a","d"]}
@@ -0,0 +1,73 @@
1
+ import { Hex } from 'viem';
2
+
3
+ /**
4
+ * Curve Router NG deployments this module trusts, one per chain, keyed by chain id (the same address means a different
5
+ * contract on another chain: `0x4f37…fc1F` is a `[5][5]` router on Base and a `[4][5]` one on Mantle). Every entry was
6
+ * read on-chain on 2026-11-05: the runtime code's keccak (the router has no owner and no proxy; its immutables — the
7
+ * wrapped native, and on `[5][5]` routers the two calc zaps — are inside the code, so the hash pins them too),
8
+ * `version()`, the wrapped native read from the code's immutable tail, and what the chain's AddressProvider lists as
9
+ * id 2. The provider re-checks the code hash and the version once per chain before quoting (`verifyDeployment`).
10
+ *
11
+ * The router address is never resolved at runtime: the AddressProvider is an informational registry its admin (an
12
+ * individual, not the DAO) can change at any time, so its id 2 is only compared and a difference only warned about.
13
+ */
14
+ /** ABI family of a router: `ng5` = `uint256[5][5]` swap params `[i, j, swap_type, pool_type, n_coins]` plus a
15
+ * `_pools[5]` argument (curve-router-ng `Router.vy`); `lite4` = `uint256[4][5]` `[i, j, swap_type, pool_type]`, no
16
+ * `_pools` (curve-core `router_v_110.vy` / `router_v_111.vy`, NG pools only). */
17
+ type CurveRouterFamily = "ng5" | "lite4";
18
+ interface CurveDeployment {
19
+ /** Human label for logs and subtitles. */
20
+ label: string;
21
+ family: CurveRouterFamily;
22
+ router: Hex;
23
+ /** `version()` as the router reports it. */
24
+ version: "1.2.0" | "1.1.0" | "1.1.1";
25
+ /** keccak256 of the router's runtime code. */
26
+ codeHash: Hex;
27
+ /** The wrapped native the router's swap_type 8 hop wraps / unwraps (its `WETH_ADDRESS` immutable). */
28
+ weth: Hex;
29
+ /** The router's native sentinel (`ETH_ADDRESS`; a constructor immutable on v1.1.1). */
30
+ eth: Hex;
31
+ /** The chain's Curve AddressProvider (watch signal only) and the address its id 2 listed when pinned. */
32
+ addressProvider: Hex;
33
+ addressProviderRouter: Hex;
34
+ /** ERC-4626 pseudo-hop (swap_type 9) the router may use — only on router v1.2 (Ethereum: crvUSD ↔ scrvUSD). */
35
+ erc4626?: {
36
+ vault: Hex;
37
+ asset: Hex;
38
+ };
39
+ }
40
+ /** The native sentinel every pinned router uses (checked against the v1.1.1 routers' immutable). */
41
+ declare const CURVE_ETH: Hex;
42
+ declare const ZERO_ADDRESS: Hex;
43
+ /** Most pool hops one route crosses (pseudo-hops — wrap, ERC-4626 — come on top). */
44
+ declare const MAX_ROUTE_POOLS = 2;
45
+ /** Most router steps one route may have: two pools, a wrap in front and an unwrap or an ERC-4626 hop behind. */
46
+ declare const MAX_ROUTE_STEPS = 4;
47
+ /** Savings crvUSD (Yearn V3 vault, ERC-4626 over crvUSD) on Ethereum, and crvUSD itself. */
48
+ declare const SCRVUSD: Hex;
49
+ declare const CRVUSD_ETHEREUM: Hex;
50
+ declare const CURVE_DEPLOYMENTS: Readonly<Record<number, CurveDeployment>>;
51
+ /**
52
+ * Older Ethereum routers recognized in history only (never quoted or approved): Router NG v1.1 (what the
53
+ * AddressProvider lists as id 2) and v1.0. Both `[5][5]`, both immutable (checked 2026-11-05).
54
+ */
55
+ declare const CURVE_LEGACY_ROUTERS: Readonly<Record<number, readonly {
56
+ router: Hex;
57
+ version: string;
58
+ codeHash: Hex;
59
+ }[]>>;
60
+ /**
61
+ * Pools a route never crosses, whatever an allowlist says (defense in depth; the allowlist generator excludes the same
62
+ * addresses). Ethereum: a registered stable-ng pool pairing real USDT with a token that calls itself "USDT"
63
+ * (`0x810e…ae9`); the four pools drained by the 2023 Vyper reentrancy-lock bug (alETH, pETH, msETH, CRV/ETH — still
64
+ * registered on-chain); the Iron Bank lending pool cyDAI/cyUSDC/cyUSDT, whose underlying route redeems Iron Bank
65
+ * cTokens. Polygon: amWBTC/renBTC (renBTC is dead).
66
+ */
67
+ declare const CURVE_DENIED_POOLS: Readonly<Record<number, ReadonlySet<string>>>;
68
+ /** The chain id of an `eip155:<id>` CAIP-2 string, or NaN. */
69
+ declare function chainIdOf(caip2: string): number;
70
+ /** The deployment pinned for `chainId` in `table` (own keys only), or undefined. */
71
+ declare function deploymentOf(table: Readonly<Record<number, CurveDeployment>>, chainId: number): CurveDeployment | undefined;
72
+
73
+ export { type CurveRouterFamily as C, MAX_ROUTE_POOLS as M, SCRVUSD as S, ZERO_ADDRESS as Z, type CurveDeployment as a, CRVUSD_ETHEREUM as b, CURVE_DENIED_POOLS as c, CURVE_DEPLOYMENTS as d, CURVE_ETH as e, CURVE_LEGACY_ROUTERS as f, MAX_ROUTE_STEPS as g, chainIdOf as h, deploymentOf as i };
@@ -0,0 +1,73 @@
1
+ import { Hex } from 'viem';
2
+
3
+ /**
4
+ * Curve Router NG deployments this module trusts, one per chain, keyed by chain id (the same address means a different
5
+ * contract on another chain: `0x4f37…fc1F` is a `[5][5]` router on Base and a `[4][5]` one on Mantle). Every entry was
6
+ * read on-chain on 2026-11-05: the runtime code's keccak (the router has no owner and no proxy; its immutables — the
7
+ * wrapped native, and on `[5][5]` routers the two calc zaps — are inside the code, so the hash pins them too),
8
+ * `version()`, the wrapped native read from the code's immutable tail, and what the chain's AddressProvider lists as
9
+ * id 2. The provider re-checks the code hash and the version once per chain before quoting (`verifyDeployment`).
10
+ *
11
+ * The router address is never resolved at runtime: the AddressProvider is an informational registry its admin (an
12
+ * individual, not the DAO) can change at any time, so its id 2 is only compared and a difference only warned about.
13
+ */
14
+ /** ABI family of a router: `ng5` = `uint256[5][5]` swap params `[i, j, swap_type, pool_type, n_coins]` plus a
15
+ * `_pools[5]` argument (curve-router-ng `Router.vy`); `lite4` = `uint256[4][5]` `[i, j, swap_type, pool_type]`, no
16
+ * `_pools` (curve-core `router_v_110.vy` / `router_v_111.vy`, NG pools only). */
17
+ type CurveRouterFamily = "ng5" | "lite4";
18
+ interface CurveDeployment {
19
+ /** Human label for logs and subtitles. */
20
+ label: string;
21
+ family: CurveRouterFamily;
22
+ router: Hex;
23
+ /** `version()` as the router reports it. */
24
+ version: "1.2.0" | "1.1.0" | "1.1.1";
25
+ /** keccak256 of the router's runtime code. */
26
+ codeHash: Hex;
27
+ /** The wrapped native the router's swap_type 8 hop wraps / unwraps (its `WETH_ADDRESS` immutable). */
28
+ weth: Hex;
29
+ /** The router's native sentinel (`ETH_ADDRESS`; a constructor immutable on v1.1.1). */
30
+ eth: Hex;
31
+ /** The chain's Curve AddressProvider (watch signal only) and the address its id 2 listed when pinned. */
32
+ addressProvider: Hex;
33
+ addressProviderRouter: Hex;
34
+ /** ERC-4626 pseudo-hop (swap_type 9) the router may use — only on router v1.2 (Ethereum: crvUSD ↔ scrvUSD). */
35
+ erc4626?: {
36
+ vault: Hex;
37
+ asset: Hex;
38
+ };
39
+ }
40
+ /** The native sentinel every pinned router uses (checked against the v1.1.1 routers' immutable). */
41
+ declare const CURVE_ETH: Hex;
42
+ declare const ZERO_ADDRESS: Hex;
43
+ /** Most pool hops one route crosses (pseudo-hops — wrap, ERC-4626 — come on top). */
44
+ declare const MAX_ROUTE_POOLS = 2;
45
+ /** Most router steps one route may have: two pools, a wrap in front and an unwrap or an ERC-4626 hop behind. */
46
+ declare const MAX_ROUTE_STEPS = 4;
47
+ /** Savings crvUSD (Yearn V3 vault, ERC-4626 over crvUSD) on Ethereum, and crvUSD itself. */
48
+ declare const SCRVUSD: Hex;
49
+ declare const CRVUSD_ETHEREUM: Hex;
50
+ declare const CURVE_DEPLOYMENTS: Readonly<Record<number, CurveDeployment>>;
51
+ /**
52
+ * Older Ethereum routers recognized in history only (never quoted or approved): Router NG v1.1 (what the
53
+ * AddressProvider lists as id 2) and v1.0. Both `[5][5]`, both immutable (checked 2026-11-05).
54
+ */
55
+ declare const CURVE_LEGACY_ROUTERS: Readonly<Record<number, readonly {
56
+ router: Hex;
57
+ version: string;
58
+ codeHash: Hex;
59
+ }[]>>;
60
+ /**
61
+ * Pools a route never crosses, whatever an allowlist says (defense in depth; the allowlist generator excludes the same
62
+ * addresses). Ethereum: a registered stable-ng pool pairing real USDT with a token that calls itself "USDT"
63
+ * (`0x810e…ae9`); the four pools drained by the 2023 Vyper reentrancy-lock bug (alETH, pETH, msETH, CRV/ETH — still
64
+ * registered on-chain); the Iron Bank lending pool cyDAI/cyUSDC/cyUSDT, whose underlying route redeems Iron Bank
65
+ * cTokens. Polygon: amWBTC/renBTC (renBTC is dead).
66
+ */
67
+ declare const CURVE_DENIED_POOLS: Readonly<Record<number, ReadonlySet<string>>>;
68
+ /** The chain id of an `eip155:<id>` CAIP-2 string, or NaN. */
69
+ declare function chainIdOf(caip2: string): number;
70
+ /** The deployment pinned for `chainId` in `table` (own keys only), or undefined. */
71
+ declare function deploymentOf(table: Readonly<Record<number, CurveDeployment>>, chainId: number): CurveDeployment | undefined;
72
+
73
+ export { type CurveRouterFamily as C, MAX_ROUTE_POOLS as M, SCRVUSD as S, ZERO_ADDRESS as Z, type CurveDeployment as a, CRVUSD_ETHEREUM as b, CURVE_DENIED_POOLS as c, CURVE_DEPLOYMENTS as d, CURVE_ETH as e, CURVE_LEGACY_ROUTERS as f, MAX_ROUTE_STEPS as g, chainIdOf as h, deploymentOf as i };