@anywalletkit/protocol-swap2-balancer 0.1.9 → 0.2.0

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.
package/README.md CHANGED
@@ -17,6 +17,13 @@ npm install @anywalletkit/protocol-swap2-balancer @anywalletkit/core
17
17
 
18
18
  On npm: [@anywalletkit/protocol-swap2-balancer](https://www.npmjs.com/package/@anywalletkit/protocol-swap2-balancer), published from the [release workflow](https://github.com/ultratestbro/anywalletkit/blob/main/RELEASING.md) with provenance.
19
19
 
20
+ ## Behind `remoteProvider`
21
+
22
+ When the provider runs on your server, spread its pins into the app's stub:
23
+ `remoteProvider({name: "balancer", baseUrl, ...balancerPins})`. The engine then lets the EVM route call and
24
+ approve only the Balancer V2 Vault, never an address the backend names; without the pins the stub gets no
25
+ EVM calldata route (fail closed).
26
+
20
27
  ## Activity
21
28
 
22
29
  `describeActivity(tx, ctx)` recognizes `batchSwap` `0x945bcec9` and `swap`
package/dist/activity.cjs CHANGED
@@ -51,6 +51,14 @@ var VAULT_ABI = (0, import_viem.parseAbi)([
51
51
  `function batchSwap(uint8 kind, ${SWAP_STEP}[] swaps, address[] assets, ${FUNDS} funds, int256[] limits, uint256 deadline) payable returns (int256[] assetDeltas)`,
52
52
  "function WETH() view returns (address)"
53
53
  ]);
54
+ var balancerPins = Object.freeze({
55
+ callTargets: (chain) => balancerVaultFor(chain),
56
+ spenders: (chain) => balancerVaultFor(chain)
57
+ });
58
+ function balancerVaultFor(chain) {
59
+ const id = /^eip155:[0-9]+$/.test(chain) ? Number(chain.slice(7)) : NaN;
60
+ return Object.prototype.hasOwnProperty.call(BALANCER_V2_CHAINS, id) ? [BALANCER_V2_VAULT] : [];
61
+ }
54
62
 
55
63
  // src/activity.ts
56
64
  var BALANCER_MODULE = "@anywalletkit/protocol-swap2-balancer";
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/activity.ts","../src/provider.ts"],"sourcesContent":["/**\n * Activity recognition for Balancer v2: `swap` / `batchSwap` on the Vault\n * (one address on every chain) on a chain the module serves → `swap`,\n * \"Swapped A → B\" from the owner's net transfers.\n */\nimport type {ActivityItem, ChainTx, DescribeActivityContext} from \"@anywalletkit/core\";\nimport {chainLabel, describeSwapLike, matchesCall} from \"@anywalletkit/protocol-swap2-core/activity\";\nimport {evmRef, isEvm} from \"@anywalletkit/protocol-swap2-core/util\";\nimport {BALANCER_V2_CHAINS, BALANCER_V2_VAULT} from \"./provider.js\";\n\nexport const BALANCER_MODULE = \"@anywalletkit/protocol-swap2-balancer\";\n\n/** Vault swap entry points (`viem toFunctionSelector`, 2026-10-14). */\nexport const BALANCER_VAULT_SELECTORS: ReadonlySet<string> = new Set([\n \"0x945bcec9\", // batchSwap(uint8,(bytes32,uint256,uint256,uint256,bytes)[],address[],(address,bool,address,bool),int256[],uint256)\n \"0x52bbbe29\", // swap((bytes32,uint8,address,address,uint256,bytes),(address,bool,address,bool),uint256,uint256)\n]);\n\nexport function describeBalancerActivity(tx: ChainTx, ctx: DescribeActivityContext): ActivityItem | null {\n if (!isEvm(tx.chain) || !BALANCER_V2_CHAINS[evmRef(tx.chain)]) return null;\n if (!matchesCall(tx, [BALANCER_V2_VAULT], BALANCER_VAULT_SELECTORS)) return null;\n return describeSwapLike(tx, ctx, {protocol: BALANCER_MODULE, subtitle: `Balancer v2 · ${chainLabel(tx.chain)}`, kind: \"swap\"});\n}\n","import {decodeFunctionData, decodeFunctionResult, encodeFunctionData, getAddress, parseAbi, type Hex} from \"viem\";\nimport {describeBalancerActivity} from \"./activity.js\";\nimport {evmRef, isEvm, timeoutSignal, sourceAssetOf} from \"@anywalletkit/protocol-swap2-core/util\";\nimport type {Asset, CrossChainProvider, IntentRef, IntentStatus, Quote, QuoteParams, UnsignedTx} from \"@anywalletkit/protocol-swap2-core/types\";\nimport {bigintToNumber} from \"@anywalletkit/core\";\n\n/**\n * Balancer v2 — the cp0x permissionless Balancer venue (balancer.cp0x.com).\n *\n * Discovery only from the API: Balancer's SOR (`sorGetSwapPaths`, v2 pools)\n * suggests the path. Everything on the money path is on-chain: the Vault\n * prices that exact path (`queryBatchSwap` via eth_call), the quote uses the\n * Vault's number, and `batchSwap` carries `limits` (in ≤ amountIn, out ≥ min)\n * plus a deadline — the Vault itself refuses anything worse. Native coins use\n * the Vault's own ETH sentinel (address 0): it wraps/unwraps through WETH.\n */\n\nconst ZERO = \"0x0000000000000000000000000000000000000000\";\n/** The same Balancer v2 Vault address on every chain it is deployed on. */\nexport const BALANCER_V2_VAULT = \"0xBA12222222228d8Ba445958a75a0704d566BF2C8\";\nconst DEFAULT_API = \"https://api-v3.balancer.fi/\";\nconst DEFAULT_SLIPPAGE_BPS = 50;\nconst DEFAULT_TTL_MS = 300_000;\nconst DEFAULT_DEADLINE_S = 600;\n\n/** Chains where the v2 Vault is live, with the SOR API name and the Vault's WETH() (verified on-chain 2026-10-13). */\nexport const BALANCER_V2_CHAINS: Readonly<Record<number, {api: string; weth: `0x${string}`}>> = Object.freeze({\n 1: {api: \"MAINNET\", weth: \"0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2\"},\n 10: {api: \"OPTIMISM\", weth: \"0x4200000000000000000000000000000000000006\"},\n 100: {api: \"GNOSIS\", weth: \"0xe91D153E0b41518A2Ce8Dd3D7944Fa863463a97d\"},\n 137: {api: \"POLYGON\", weth: \"0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270\"},\n 146: {api: \"SONIC\", weth: \"0x039e2fB66102314Ce7b64Ce5Ce3E5183bc94aD38\"},\n 8453: {api: \"BASE\", weth: \"0x4200000000000000000000000000000000000006\"},\n 9745: {api: \"PLASMA\", weth: \"0x6100E367285b01F48D07953803A2d8dCA5D19873\"},\n 42161: {api: \"ARBITRUM\", weth: \"0x82aF49447D8a07e3bd95BD0d56f35241523fBab1\"},\n 43114: {api: \"AVALANCHE\", weth: \"0xB31f66AA3C1e785363F0875A1B74E27b85FD66c7\"},\n});\n\nconst SWAP_STEP = \"(bytes32 poolId, uint256 assetInIndex, uint256 assetOutIndex, uint256 amount, bytes userData)\";\nconst FUNDS = \"(address sender, bool fromInternalBalance, address recipient, bool toInternalBalance)\";\nconst VAULT_ABI = parseAbi([\n `function queryBatchSwap(uint8 kind, ${SWAP_STEP}[] swaps, address[] assets, ${FUNDS} funds) returns (int256[] assetDeltas)`,\n `function batchSwap(uint8 kind, ${SWAP_STEP}[] swaps, address[] assets, ${FUNDS} funds, int256[] limits, uint256 deadline) payable returns (int256[] assetDeltas)`,\n \"function WETH() view returns (address)\",\n]);\n\ntype Step = {poolId: Hex; assetInIndex: bigint; assetOutIndex: bigint; amount: bigint; userData: Hex};\ntype Funds = {sender: Hex; fromInternalBalance: boolean; recipient: Hex; toInternalBalance: boolean};\n\nexport interface BalancerProviderConfig {\n enabled?: boolean;\n /** JSON-RPC endpoints per EVM chain id (on-chain pricing + receipts). */\n rpcUrls: Readonly<Record<number, readonly string[]>>;\n /** Balancer API (SOR) base URL — discovery only. */\n apiUrl?: string;\n defaultSlippageBps?: number;\n quoteTtlMs?: number;\n deadlineSeconds?: number;\n}\n\ninterface SorPath {\n pools: string[];\n tokens: {address: string; decimals: number}[];\n inputAmountRaw: string;\n outputAmountRaw: string;\n protocolVersion: number;\n}\n\n/** Base units → the decimal string the SOR expects (\"1.5\"). */\nfunction human(amount: bigint, decimals: number): string {\n const s = amount.toString().padStart(decimals + 1, \"0\");\n const whole = s.slice(0, s.length - decimals);\n const frac = s.slice(s.length - decimals).replace(/0+$/, \"\");\n return frac ? `${whole}.${frac}` : whole;\n}\n\nexport function balancerProvider(config: BalancerProviderConfig): CrossChainProvider {\n const api = config.apiUrl ?? DEFAULT_API;\n const slippageDefault = config.defaultSlippageBps ?? DEFAULT_SLIPPAGE_BPS;\n const ttl = config.quoteTtlMs ?? DEFAULT_TTL_MS;\n const deadlineS = config.deadlineSeconds ?? DEFAULT_DEADLINE_S;\n const verified = new Map<number, Promise<void>>();\n\n const chainIdOf = (caip2: string): number | null => {\n if (!isEvm(caip2)) return null;\n const id = evmRef(caip2);\n return BALANCER_V2_CHAINS[id] && config.rpcUrls[id]?.length ? id : null;\n };\n\n async function rpc(chainId: number, method: string, params: unknown[]): Promise<unknown> {\n let lastErr: unknown;\n for (const url of config.rpcUrls[chainId] ?? []) {\n try {\n const res = await fetch(url, {method: \"POST\", headers: {\"content-type\": \"application/json\"}, body: JSON.stringify({jsonrpc: \"2.0\", id: 1, method, params}), signal: timeoutSignal(12_000)});\n if (!res.ok) throw new Error(`HTTP ${res.status}`);\n const body = (await res.json()) as {result?: unknown; error?: {message?: string}};\n if (body.error) throw Object.assign(new Error(body.error.message ?? \"rpc error\"), {rpc: true});\n return body.result ?? null;\n } catch (e) {\n if ((e as {rpc?: boolean}).rpc) throw e; // a revert is an answer, not a dead endpoint\n lastErr = e;\n }\n }\n throw new Error(`balancer: no RPC answered on chain ${chainId} (${lastErr instanceof Error ? lastErr.message : String(lastErr)})`);\n }\n\n /** The Vault is deployed here and wraps through the WETH we pinned. */\n function verify(chainId: number): Promise<void> {\n let p = verified.get(chainId);\n if (!p) {\n p = (async () => {\n const raw = (await rpc(chainId, \"eth_call\", [{to: BALANCER_V2_VAULT, data: encodeFunctionData({abi: VAULT_ABI, functionName: \"WETH\"})}, \"latest\"])) as Hex;\n const weth = decodeFunctionResult({abi: VAULT_ABI, functionName: \"WETH\", data: raw});\n if (getAddress(weth) !== getAddress(BALANCER_V2_CHAINS[chainId]!.weth)) throw new Error(`balancer: the Vault on chain ${chainId} is not the pinned deployment — refusing`);\n })();\n p.catch(() => verified.delete(chainId));\n verified.set(chainId, p);\n }\n return p;\n }\n\n /** Vault.queryBatchSwap on chain: the asset deltas this exact batch would produce. */\n async function query(chainId: number, swaps: readonly Step[], assets: readonly Hex[], funds: Funds): Promise<readonly bigint[]> {\n const data = encodeFunctionData({abi: VAULT_ABI, functionName: \"queryBatchSwap\", args: [0, swaps, assets, funds]});\n const raw = (await rpc(chainId, \"eth_call\", [{to: BALANCER_V2_VAULT, data}, \"latest\"])) as Hex;\n return decodeFunctionResult({abi: VAULT_ABI, functionName: \"queryBatchSwap\", data: raw});\n }\n\n return {\n name: \"balancer\",\n describeActivity: describeBalancerActivity,\n supportsSameChain: true,\n enabled: () => config.enabled ?? true,\n supportsChain: (caip2: string) => chainIdOf(caip2) !== null,\n supportsChainPair: (from: string, to: string) => from === to && chainIdOf(from) !== null,\n supportsRoute: (from: Asset, to: Asset) => from.chain === to.chain && chainIdOf(from.chain) !== null,\n\n async getQuote(params: QuoteParams, signal?: AbortSignal): Promise<Quote | null> {\n const refuse = (why: string): null => {\n params.onRefusal?.(`balancer: ${why}`);\n return null;\n };\n if (params.flex) return refuse(\"amount-flexible deposits are not supported\");\n if (params.sourceChain !== params.destChain) return refuse(\"same-chain swaps only\");\n const chainId = chainIdOf(params.sourceChain);\n if (chainId === null) return refuse(`no Balancer v2 Vault or RPC for ${params.sourceChain}`);\n const chain = BALANCER_V2_CHAINS[chainId]!;\n const src = sourceAssetOf(params);\n if (!src) return refuse(`unknown source token ${params.sourceToken}`);\n const nativeIn = !src.contract;\n const nativeOut = /^0x0+$/.test(params.destToken);\n if (!nativeOut && !/^0x[0-9a-fA-F]{40}$/.test(params.destToken)) return refuse(\"destination token must be an EVM address\");\n if (!/^0x[0-9a-fA-F]{40}$/.test(params.destAddress)) return refuse(\"recipient must be an EVM address\");\n let amountIn: bigint;\n try {\n amountIn = BigInt(params.amountIn);\n } catch {\n return refuse(\"amount is not an integer\");\n }\n if (amountIn <= 0n) return refuse(\"amount must be positive\");\n const tokenIn = getAddress(nativeIn ? chain.weth : src.contract!);\n const tokenOut = getAddress(nativeOut ? chain.weth : params.destToken);\n if (tokenIn === tokenOut) return refuse(nativeIn || nativeOut ? \"that is a wrap/unwrap, not a swap\" : \"same token in and out\");\n\n try {\n await verify(chainId);\n // 1) Discovery: the SOR's v2 paths (never trusted for price).\n // Values are inlined (this API ignores GraphQL variables); all are validated\n // addresses / a decimal number / a fixed enum, so nothing user-shaped is spliced.\n const query_ = `query { sorGetSwapPaths(chain: ${chain.api}, swapAmount: \"${human(amountIn, src.decimals)}\", swapType: EXACT_IN, tokenIn: \"${tokenIn}\", tokenOut: \"${tokenOut}\", useProtocolVersion: 2) { protocolVersion paths { pools tokens { address decimals } inputAmountRaw outputAmountRaw protocolVersion } } }`;\n const res = await fetch(api, {\n method: \"POST\",\n headers: {\"content-type\": \"application/json\"},\n body: JSON.stringify({query: query_}),\n signal: signal ?? timeoutSignal(12_000),\n });\n if (!res.ok) return refuse(`SOR answered HTTP ${res.status}`);\n const body = (await res.json()) as {data?: {sorGetSwapPaths?: {protocolVersion?: number; paths?: SorPath[]}}; errors?: {message?: string}[]};\n const paths = body.data?.sorGetSwapPaths?.paths ?? [];\n if (body.errors?.length) return refuse(`SOR: ${body.errors[0]?.message?.slice(0, 120) ?? \"error\"}`);\n if (!paths.length || paths.some((p) => p.protocolVersion !== 2 || p.pools.length === 0 || p.tokens.length !== p.pools.length + 1)) {\n return refuse(\"no Balancer v2 pool route for this pair and amount\");\n }\n if (paths.reduce((s, p) => s + BigInt(p.inputAmountRaw), 0n) !== amountIn) return refuse(\"the SOR split does not add up to the amount\");\n\n // 2) Build the batch from the suggested paths.\n const assets: Hex[] = [];\n const indexOf = (a: string) => {\n const addr = getAddress(a);\n let i = assets.findIndex((x) => x === addr);\n if (i < 0) i = assets.push(addr) - 1;\n return BigInt(i);\n };\n const swaps: Step[] = [];\n for (const p of paths) {\n if (getAddress(p.tokens[0]!.address) !== tokenIn || getAddress(p.tokens[p.tokens.length - 1]!.address) !== tokenOut) return refuse(\"the SOR path does not start/end at the requested tokens\");\n p.pools.forEach((pool, i) => {\n if (!/^0x[0-9a-fA-F]{64}$/.test(pool)) throw new Error(\"balancer: malformed pool id from the SOR\");\n swaps.push({poolId: pool as Hex, assetInIndex: indexOf(p.tokens[i]!.address), assetOutIndex: indexOf(p.tokens[i + 1]!.address), amount: i === 0 ? BigInt(p.inputAmountRaw) : 0n, userData: \"0x\"});\n });\n }\n const inIdx = bigintToNumber(indexOf(tokenIn));\n const outIdx = bigintToNumber(indexOf(tokenOut));\n // The Vault's ETH sentinel: native in/out wraps/unwraps through its WETH.\n if (nativeIn || nativeOut) {\n const wethIdx = assets.findIndex((a) => a === getAddress(chain.weth));\n if (paths.some((p) => p.tokens.slice(1, -1).some((t) => getAddress(t.address) === getAddress(chain.weth)))) return refuse(\"this route passes through the wrapped native — pick the wrapped token instead\");\n if (wethIdx >= 0) assets[wethIdx] = ZERO as Hex;\n }\n const sender = getAddress(params.refundAddress && /^0x[0-9a-fA-F]{40}$/.test(params.refundAddress) ? params.refundAddress : params.destAddress);\n const recipient = getAddress(params.destAddress);\n const funds: Funds = {sender, fromInternalBalance: false, recipient, toInternalBalance: false};\n\n // 3) Price it on-chain — this, not the SOR's number, is the quote.\n const deltas = await query(chainId, swaps, assets, funds);\n const paid = deltas[inIdx]!;\n const out = -deltas[outIdx]!;\n if (paid !== amountIn) return refuse(`the Vault would take ${paid}, not ${amountIn}`);\n if (out <= 0n) return refuse(\"the Vault prices this route at zero output\");\n if (deltas.some((d, i) => i !== inIdx && i !== outIdx && d !== 0n)) return refuse(\"the batch leaves an intermediate balance — refusing\");\n\n const bps = params.slippageBps ?? slippageDefault;\n const minOut = (out * BigInt(10_000 - bps)) / 10_000n;\n if (params.minAmountOut !== undefined && minOut < BigInt(params.minAmountOut)) return refuse(`floor breach (min=${minOut} < floor=${params.minAmountOut})`);\n const limits = assets.map((_, i) => (i === inIdx ? amountIn : i === outIdx ? -minOut : 0n));\n const deadline = BigInt(Math.floor(Date.now() / 1000) + deadlineS);\n const data = encodeFunctionData({abi: VAULT_ABI, functionName: \"batchSwap\", args: [0, swaps, assets, funds, limits, deadline]});\n const gasLimit = 160_000n + 95_000n * BigInt(swaps.length);\n return {\n provider: \"balancer\",\n intentId: `balancer:${chainId}`,\n depositAddress: \"\",\n depositMemo: null,\n txRequest: {\n to: BALANCER_V2_VAULT,\n data,\n value: nativeIn ? amountIn.toString() : \"0\",\n chainId,\n gasLimit: gasLimit.toString(),\n ...(nativeIn ? {} : {approvalAddress: BALANCER_V2_VAULT}),\n },\n sourceAmount: amountIn.toString(),\n destAmount: out.toString(),\n minDestAmount: minOut.toString(),\n feeUsd: 0,\n etaSeconds: 15,\n reliability: 0.97,\n validUntilMs: Date.now() + ttl,\n // Pool swap fees are inside the Vault-priced output; no fee is added on top.\n providerData: {paths: String(paths.length), hops: String(swaps.length), minOut: minOut.toString(), poolFee: \"pools\"},\n };\n } catch (e) {\n return refuse(e instanceof Error ? e.message.replace(/^balancer: /, \"\") : String(e));\n }\n },\n\n /** Re-price the exact signed batch right before broadcast; refuse when the pools now pay under the minimum. */\n async preflightDeposit(quote: Quote, tx: UnsignedTx): Promise<void> {\n const chainId = Number(quote.intentId.split(\":\")[1]);\n const minOut = quote.providerData?.minOut;\n if (!BALANCER_V2_CHAINS[chainId] || !minOut || !tx.data) return;\n const call = decodeFunctionData({abi: VAULT_ABI, data: tx.data as Hex});\n if (call.functionName !== \"batchSwap\") throw new Error(\"balancer: the transaction is not a Vault batchSwap\");\n const [, swaps, assets, funds, limits] = call.args;\n const outIdx = limits.findIndex((l) => l < 0n);\n const deltas = await query(chainId, swaps, assets, funds);\n const out = -deltas[outIdx]!;\n if (out < BigInt(minOut)) throw new Error(`balancer: price moved — the pools now pay ${out}, under your minimum ${minOut}. Nothing was sent; refresh the quote.`);\n },\n\n async buildDeposit(quote: Quote): Promise<UnsignedTx> {\n const tx = quote.txRequest;\n if (!tx?.to || !tx.data) throw new Error(\"swap2: Balancer quote has no transaction request\");\n return {chain: \"\", to: tx.to, value: tx.value ?? \"0\", data: tx.data, ...(tx.chainId !== undefined ? {chainId: tx.chainId} : {}), ...(tx.gasLimit ? {gasLimit: tx.gasLimit} : {}), ...(tx.approvalAddress ? {approvalAddress: tx.approvalAddress} : {})};\n },\n\n async trackIntent(ref: IntentRef): Promise<IntentStatus> {\n if (!ref.sourceTxHash) return {state: \"pending\"};\n const chainId = Number(ref.intentId.split(\":\")[1]);\n if (!BALANCER_V2_CHAINS[chainId] || !config.rpcUrls[chainId]?.length) return {state: \"pending\"};\n try {\n const receipt = (await rpc(chainId, \"eth_getTransactionReceipt\", [ref.sourceTxHash])) as {status?: string} | null;\n if (!receipt) return {state: \"pending\"};\n return receipt.status === \"0x1\" ? {state: \"completed\", destTxHash: ref.sourceTxHash} : {state: \"failed\"};\n } catch {\n return {state: \"pending\"};\n }\n },\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAMA,sBAAwD;AACxD,IAAAA,eAA4B;;;ACP5B,kBAA2G;AAE3G,kBAA0D;AAE1D,kBAA6B;AAetB,IAAM,oBAAoB;AAO1B,IAAM,qBAAmF,OAAO,OAAO;AAAA,EAC5G,GAAG,EAAC,KAAK,WAAW,MAAM,6CAA4C;AAAA,EACtE,IAAI,EAAC,KAAK,YAAY,MAAM,6CAA4C;AAAA,EACxE,KAAK,EAAC,KAAK,UAAU,MAAM,6CAA4C;AAAA,EACvE,KAAK,EAAC,KAAK,WAAW,MAAM,6CAA4C;AAAA,EACxE,KAAK,EAAC,KAAK,SAAS,MAAM,6CAA4C;AAAA,EACtE,MAAM,EAAC,KAAK,QAAQ,MAAM,6CAA4C;AAAA,EACtE,MAAM,EAAC,KAAK,UAAU,MAAM,6CAA4C;AAAA,EACxE,OAAO,EAAC,KAAK,YAAY,MAAM,6CAA4C;AAAA,EAC3E,OAAO,EAAC,KAAK,aAAa,MAAM,6CAA4C;AAC9E,CAAC;AAED,IAAM,YAAY;AAClB,IAAM,QAAQ;AACd,IAAM,gBAAY,sBAAS;AAAA,EACzB,uCAAuC,SAAS,+BAA+B,KAAK;AAAA,EACpF,kCAAkC,SAAS,+BAA+B,KAAK;AAAA,EAC/E;AACF,CAAC;;;ADlCM,IAAM,kBAAkB;AAGxB,IAAM,2BAAgD,oBAAI,IAAI;AAAA,EACnE;AAAA;AAAA,EACA;AAAA;AACF,CAAC;AAEM,SAAS,yBAAyB,IAAa,KAAmD;AACvG,MAAI,KAAC,oBAAM,GAAG,KAAK,KAAK,CAAC,uBAAmB,qBAAO,GAAG,KAAK,CAAC,EAAG,QAAO;AACtE,MAAI,KAAC,6BAAY,IAAI,CAAC,iBAAiB,GAAG,wBAAwB,EAAG,QAAO;AAC5E,aAAO,kCAAiB,IAAI,KAAK,EAAC,UAAU,iBAAiB,UAAU,wBAAiB,4BAAW,GAAG,KAAK,CAAC,IAAI,MAAM,OAAM,CAAC;AAC/H;","names":["import_util"]}
1
+ {"version":3,"sources":["../src/activity.ts","../src/provider.ts"],"sourcesContent":["/**\n * Activity recognition for Balancer v2: `swap` / `batchSwap` on the Vault\n * (one address on every chain) on a chain the module serves → `swap`,\n * \"Swapped A → B\" from the owner's net transfers.\n */\nimport type {ActivityItem, ChainTx, DescribeActivityContext} from \"@anywalletkit/core\";\nimport {chainLabel, describeSwapLike, matchesCall} from \"@anywalletkit/protocol-swap2-core/activity\";\nimport {evmRef, isEvm} from \"@anywalletkit/protocol-swap2-core/util\";\nimport {BALANCER_V2_CHAINS, BALANCER_V2_VAULT} from \"./provider.js\";\n\nexport const BALANCER_MODULE = \"@anywalletkit/protocol-swap2-balancer\";\n\n/** Vault swap entry points (`viem toFunctionSelector`, 2026-10-14). */\nexport const BALANCER_VAULT_SELECTORS: ReadonlySet<string> = new Set([\n \"0x945bcec9\", // batchSwap(uint8,(bytes32,uint256,uint256,uint256,bytes)[],address[],(address,bool,address,bool),int256[],uint256)\n \"0x52bbbe29\", // swap((bytes32,uint8,address,address,uint256,bytes),(address,bool,address,bool),uint256,uint256)\n]);\n\nexport function describeBalancerActivity(tx: ChainTx, ctx: DescribeActivityContext): ActivityItem | null {\n if (!isEvm(tx.chain) || !BALANCER_V2_CHAINS[evmRef(tx.chain)]) return null;\n if (!matchesCall(tx, [BALANCER_V2_VAULT], BALANCER_VAULT_SELECTORS)) return null;\n return describeSwapLike(tx, ctx, {protocol: BALANCER_MODULE, subtitle: `Balancer v2 · ${chainLabel(tx.chain)}`, kind: \"swap\"});\n}\n","import type {RemotePins} from \"@anywalletkit/protocol-swap2-core/types\";\nimport {decodeFunctionData, decodeFunctionResult, encodeFunctionData, getAddress, parseAbi, type Hex} from \"viem\";\nimport {describeBalancerActivity} from \"./activity.js\";\nimport {evmRef, isEvm, timeoutSignal, sourceAssetOf} from \"@anywalletkit/protocol-swap2-core/util\";\nimport type {Asset, CrossChainProvider, IntentRef, IntentStatus, Quote, QuoteParams, UnsignedTx} from \"@anywalletkit/protocol-swap2-core/types\";\nimport {bigintToNumber} from \"@anywalletkit/core\";\n\n/**\n * Balancer v2 — the cp0x permissionless Balancer venue (balancer.cp0x.com).\n *\n * Discovery only from the API: Balancer's SOR (`sorGetSwapPaths`, v2 pools)\n * suggests the path. Everything on the money path is on-chain: the Vault\n * prices that exact path (`queryBatchSwap` via eth_call), the quote uses the\n * Vault's number, and `batchSwap` carries `limits` (in ≤ amountIn, out ≥ min)\n * plus a deadline — the Vault itself refuses anything worse. Native coins use\n * the Vault's own ETH sentinel (address 0): it wraps/unwraps through WETH.\n */\n\nconst ZERO = \"0x0000000000000000000000000000000000000000\";\n/** The same Balancer v2 Vault address on every chain it is deployed on. */\nexport const BALANCER_V2_VAULT = \"0xBA12222222228d8Ba445958a75a0704d566BF2C8\";\nconst DEFAULT_API = \"https://api-v3.balancer.fi/\";\nconst DEFAULT_SLIPPAGE_BPS = 50;\nconst DEFAULT_TTL_MS = 300_000;\nconst DEFAULT_DEADLINE_S = 600;\n/** Most pool hops one batch may carry: the SOR's splits are a handful; a batch past this is refused at quote and at preflight. */\nexport const BALANCER_MAX_SWAPS = 16;\n\n/** Chains where the v2 Vault is live, with the SOR API name and the Vault's WETH() (verified on-chain 2026-10-13). */\nexport const BALANCER_V2_CHAINS: Readonly<Record<number, {api: string; weth: `0x${string}`}>> = Object.freeze({\n 1: {api: \"MAINNET\", weth: \"0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2\"},\n 10: {api: \"OPTIMISM\", weth: \"0x4200000000000000000000000000000000000006\"},\n 100: {api: \"GNOSIS\", weth: \"0xe91D153E0b41518A2Ce8Dd3D7944Fa863463a97d\"},\n 137: {api: \"POLYGON\", weth: \"0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270\"},\n 146: {api: \"SONIC\", weth: \"0x039e2fB66102314Ce7b64Ce5Ce3E5183bc94aD38\"},\n 8453: {api: \"BASE\", weth: \"0x4200000000000000000000000000000000000006\"},\n 9745: {api: \"PLASMA\", weth: \"0x6100E367285b01F48D07953803A2d8dCA5D19873\"},\n 42161: {api: \"ARBITRUM\", weth: \"0x82aF49447D8a07e3bd95BD0d56f35241523fBab1\"},\n 43114: {api: \"AVALANCHE\", weth: \"0xB31f66AA3C1e785363F0875A1B74E27b85FD66c7\"},\n});\n\nconst SWAP_STEP = \"(bytes32 poolId, uint256 assetInIndex, uint256 assetOutIndex, uint256 amount, bytes userData)\";\nconst FUNDS = \"(address sender, bool fromInternalBalance, address recipient, bool toInternalBalance)\";\nconst VAULT_ABI = parseAbi([\n `function queryBatchSwap(uint8 kind, ${SWAP_STEP}[] swaps, address[] assets, ${FUNDS} funds) returns (int256[] assetDeltas)`,\n `function batchSwap(uint8 kind, ${SWAP_STEP}[] swaps, address[] assets, ${FUNDS} funds, int256[] limits, uint256 deadline) payable returns (int256[] assetDeltas)`,\n \"function WETH() view returns (address)\",\n]);\n\ntype Step = {poolId: Hex; assetInIndex: bigint; assetOutIndex: bigint; amount: bigint; userData: Hex};\ntype Funds = {sender: Hex; fromInternalBalance: boolean; recipient: Hex; toInternalBalance: boolean};\n\nexport interface BalancerProviderConfig {\n enabled?: boolean;\n /** JSON-RPC endpoints per EVM chain id (on-chain pricing + receipts). */\n rpcUrls: Readonly<Record<number, readonly string[]>>;\n /** Balancer API (SOR) base URL — discovery only. */\n apiUrl?: string;\n defaultSlippageBps?: number;\n quoteTtlMs?: number;\n deadlineSeconds?: number;\n}\n\ninterface SorPath {\n pools: string[];\n tokens: {address: string; decimals: number}[];\n inputAmountRaw: string;\n outputAmountRaw: string;\n protocolVersion: number;\n}\n\n/** Base units → the decimal string the SOR expects (\"1.5\"). */\nfunction human(amount: bigint, decimals: number): string {\n const s = amount.toString().padStart(decimals + 1, \"0\");\n const whole = s.slice(0, s.length - decimals);\n const frac = s.slice(s.length - decimals).replace(/0+$/, \"\");\n return frac ? `${whole}.${frac}` : whole;\n}\n\nexport function balancerProvider(config: BalancerProviderConfig): CrossChainProvider {\n const api = config.apiUrl ?? DEFAULT_API;\n const slippageDefault = config.defaultSlippageBps ?? DEFAULT_SLIPPAGE_BPS;\n const ttl = config.quoteTtlMs ?? DEFAULT_TTL_MS;\n const deadlineS = config.deadlineSeconds ?? DEFAULT_DEADLINE_S;\n const verified = new Map<number, Promise<void>>();\n\n const chainIdOf = (caip2: string): number | null => {\n if (!isEvm(caip2)) return null;\n const id = evmRef(caip2);\n return BALANCER_V2_CHAINS[id] && config.rpcUrls[id]?.length ? id : null;\n };\n\n async function rpc(chainId: number, method: string, params: unknown[]): Promise<unknown> {\n let lastErr: unknown;\n for (const url of config.rpcUrls[chainId] ?? []) {\n try {\n const res = await fetch(url, {method: \"POST\", headers: {\"content-type\": \"application/json\"}, body: JSON.stringify({jsonrpc: \"2.0\", id: 1, method, params}), signal: timeoutSignal(12_000)});\n if (!res.ok) throw new Error(`HTTP ${res.status}`);\n const body = (await res.json()) as {result?: unknown; error?: {message?: string}};\n if (body.error) throw Object.assign(new Error(body.error.message ?? \"rpc error\"), {rpc: true});\n return body.result ?? null;\n } catch (e) {\n if ((e as {rpc?: boolean}).rpc) throw e; // a revert is an answer, not a dead endpoint\n lastErr = e;\n }\n }\n throw new Error(`balancer: no RPC answered on chain ${chainId} (${lastErr instanceof Error ? lastErr.message : String(lastErr)})`);\n }\n\n /** The Vault is deployed here and wraps through the WETH we pinned. */\n function verify(chainId: number): Promise<void> {\n let p = verified.get(chainId);\n if (!p) {\n p = (async () => {\n const raw = (await rpc(chainId, \"eth_call\", [{to: BALANCER_V2_VAULT, data: encodeFunctionData({abi: VAULT_ABI, functionName: \"WETH\"})}, \"latest\"])) as Hex;\n const weth = decodeFunctionResult({abi: VAULT_ABI, functionName: \"WETH\", data: raw});\n if (getAddress(weth) !== getAddress(BALANCER_V2_CHAINS[chainId]!.weth)) throw new Error(`balancer: the Vault on chain ${chainId} is not the pinned deployment — refusing`);\n })();\n p.catch(() => verified.delete(chainId));\n verified.set(chainId, p);\n }\n return p;\n }\n\n /** Vault.queryBatchSwap on chain: the asset deltas this exact batch would produce. */\n async function query(chainId: number, swaps: readonly Step[], assets: readonly Hex[], funds: Funds): Promise<readonly bigint[]> {\n const data = encodeFunctionData({abi: VAULT_ABI, functionName: \"queryBatchSwap\", args: [0, swaps, assets, funds]});\n const raw = (await rpc(chainId, \"eth_call\", [{to: BALANCER_V2_VAULT, data}, \"latest\"])) as Hex;\n return decodeFunctionResult({abi: VAULT_ABI, functionName: \"queryBatchSwap\", data: raw});\n }\n\n return {\n name: \"balancer\",\n describeActivity: describeBalancerActivity,\n supportsSameChain: true,\n enabled: () => config.enabled ?? true,\n supportsChain: (caip2: string) => chainIdOf(caip2) !== null,\n supportsChainPair: (from: string, to: string) => from === to && chainIdOf(from) !== null,\n supportsRoute: (from: Asset, to: Asset) => from.chain === to.chain && chainIdOf(from.chain) !== null,\n\n async getQuote(params: QuoteParams, signal?: AbortSignal): Promise<Quote | null> {\n const refuse = (why: string): null => {\n params.onRefusal?.(`balancer: ${why}`);\n return null;\n };\n if (params.flex) return refuse(\"amount-flexible deposits are not supported\");\n if (params.sourceChain !== params.destChain) return refuse(\"same-chain swaps only\");\n const chainId = chainIdOf(params.sourceChain);\n if (chainId === null) return refuse(`no Balancer v2 Vault or RPC for ${params.sourceChain}`);\n const chain = BALANCER_V2_CHAINS[chainId]!;\n const src = sourceAssetOf(params);\n if (!src) return refuse(`unknown source token ${params.sourceToken}`);\n const nativeIn = !src.contract;\n const nativeOut = /^0x0+$/.test(params.destToken);\n if (!nativeOut && !/^0x[0-9a-fA-F]{40}$/.test(params.destToken)) return refuse(\"destination token must be an EVM address\");\n if (!/^0x[0-9a-fA-F]{40}$/.test(params.destAddress)) return refuse(\"recipient must be an EVM address\");\n let amountIn: bigint;\n try {\n amountIn = BigInt(params.amountIn);\n } catch {\n return refuse(\"amount is not an integer\");\n }\n if (amountIn <= 0n) return refuse(\"amount must be positive\");\n const tokenIn = getAddress(nativeIn ? chain.weth : src.contract!);\n const tokenOut = getAddress(nativeOut ? chain.weth : params.destToken);\n if (tokenIn === tokenOut) return refuse(nativeIn || nativeOut ? \"that is a wrap/unwrap, not a swap\" : \"same token in and out\");\n\n try {\n await verify(chainId);\n // 1) Discovery: the SOR's v2 paths (never trusted for price).\n // Values are inlined (this API ignores GraphQL variables); all are validated\n // addresses / a decimal number / a fixed enum, so nothing user-shaped is spliced.\n const query_ = `query { sorGetSwapPaths(chain: ${chain.api}, swapAmount: \"${human(amountIn, src.decimals)}\", swapType: EXACT_IN, tokenIn: \"${tokenIn}\", tokenOut: \"${tokenOut}\", useProtocolVersion: 2) { protocolVersion paths { pools tokens { address decimals } inputAmountRaw outputAmountRaw protocolVersion } } }`;\n const res = await fetch(api, {\n method: \"POST\",\n headers: {\"content-type\": \"application/json\"},\n body: JSON.stringify({query: query_}),\n signal: signal ?? timeoutSignal(12_000),\n });\n if (!res.ok) return refuse(`SOR answered HTTP ${res.status}`);\n const body = (await res.json()) as {data?: {sorGetSwapPaths?: {protocolVersion?: number; paths?: SorPath[]}}; errors?: {message?: string}[]};\n const paths = body.data?.sorGetSwapPaths?.paths ?? [];\n if (body.errors?.length) return refuse(`SOR: ${body.errors[0]?.message?.slice(0, 120) ?? \"error\"}`);\n if (!paths.length || paths.some((p) => p.protocolVersion !== 2 || p.pools.length === 0 || p.tokens.length !== p.pools.length + 1)) {\n return refuse(\"no Balancer v2 pool route for this pair and amount\");\n }\n if (paths.reduce((s, p) => s + BigInt(p.inputAmountRaw), 0n) !== amountIn) return refuse(\"the SOR split does not add up to the amount\");\n\n // 2) Build the batch from the suggested paths.\n const assets: Hex[] = [];\n const indexOf = (a: string) => {\n const addr = getAddress(a);\n let i = assets.findIndex((x) => x === addr);\n if (i < 0) i = assets.push(addr) - 1;\n return BigInt(i);\n };\n const swaps: Step[] = [];\n for (const p of paths) {\n if (getAddress(p.tokens[0]!.address) !== tokenIn || getAddress(p.tokens[p.tokens.length - 1]!.address) !== tokenOut) return refuse(\"the SOR path does not start/end at the requested tokens\");\n p.pools.forEach((pool, i) => {\n if (!/^0x[0-9a-fA-F]{64}$/.test(pool)) throw new Error(\"balancer: malformed pool id from the SOR\");\n swaps.push({poolId: pool as Hex, assetInIndex: indexOf(p.tokens[i]!.address), assetOutIndex: indexOf(p.tokens[i + 1]!.address), amount: i === 0 ? BigInt(p.inputAmountRaw) : 0n, userData: \"0x\"});\n });\n }\n if (swaps.length > BALANCER_MAX_SWAPS) return refuse(`the SOR route has ${swaps.length} pool hops, more than ${BALANCER_MAX_SWAPS}`);\n const inIdx = bigintToNumber(indexOf(tokenIn));\n const outIdx = bigintToNumber(indexOf(tokenOut));\n // The Vault's ETH sentinel: native in/out wraps/unwraps through its WETH.\n if (nativeIn || nativeOut) {\n const wethIdx = assets.findIndex((a) => a === getAddress(chain.weth));\n if (paths.some((p) => p.tokens.slice(1, -1).some((t) => getAddress(t.address) === getAddress(chain.weth)))) return refuse(\"this route passes through the wrapped native — pick the wrapped token instead\");\n if (wethIdx >= 0) assets[wethIdx] = ZERO as Hex;\n }\n const sender = getAddress(params.refundAddress && /^0x[0-9a-fA-F]{40}$/.test(params.refundAddress) ? params.refundAddress : params.destAddress);\n const recipient = getAddress(params.destAddress);\n const funds: Funds = {sender, fromInternalBalance: false, recipient, toInternalBalance: false};\n\n // 3) Price it on-chain — this, not the SOR's number, is the quote.\n const deltas = await query(chainId, swaps, assets, funds);\n const paid = deltas[inIdx]!;\n const out = -deltas[outIdx]!;\n if (paid !== amountIn) return refuse(`the Vault would take ${paid}, not ${amountIn}`);\n if (out <= 0n) return refuse(\"the Vault prices this route at zero output\");\n if (deltas.some((d, i) => i !== inIdx && i !== outIdx && d !== 0n)) return refuse(\"the batch leaves an intermediate balance — refusing\");\n\n const bps = params.slippageBps ?? slippageDefault;\n const minOut = (out * BigInt(10_000 - bps)) / 10_000n;\n if (params.minAmountOut !== undefined && minOut < BigInt(params.minAmountOut)) return refuse(`floor breach (min=${minOut} < floor=${params.minAmountOut})`);\n const limits = assets.map((_, i) => (i === inIdx ? amountIn : i === outIdx ? -minOut : 0n));\n const deadline = BigInt(Math.floor(Date.now() / 1000) + deadlineS);\n const data = encodeFunctionData({abi: VAULT_ABI, functionName: \"batchSwap\", args: [0, swaps, assets, funds, limits, deadline]});\n const gasLimit = 160_000n + 95_000n * BigInt(swaps.length);\n return {\n provider: \"balancer\",\n intentId: `balancer:${chainId}`,\n depositAddress: \"\",\n depositMemo: null,\n txRequest: {\n to: BALANCER_V2_VAULT,\n data,\n value: nativeIn ? amountIn.toString() : \"0\",\n chainId,\n gasLimit: gasLimit.toString(),\n ...(nativeIn ? {} : {approvalAddress: BALANCER_V2_VAULT}),\n },\n sourceAmount: amountIn.toString(),\n destAmount: out.toString(),\n minDestAmount: minOut.toString(),\n feeUsd: 0,\n etaSeconds: 15,\n reliability: 0.97,\n validUntilMs: Date.now() + ttl,\n // Pool swap fees are inside the Vault-priced output; no fee is added on top.\n providerData: {paths: String(paths.length), hops: String(swaps.length), minOut: minOut.toString(), poolFee: \"pools\"},\n };\n } catch (e) {\n return refuse(e instanceof Error ? e.message.replace(/^balancer: /, \"\") : String(e));\n }\n },\n\n /**\n * Re-price the exact signed batch right before broadcast; refuse when the pools now pay under the minimum.\n * Only the quote's own batch is decoded and priced: behind a backend, `tx` is the client's and only the\n * quote is sealed, so a client's batch (any size, any pools) never reaches the decoder or the RPC\n * (scan 2026-10-27 modules, F24).\n */\n async preflightDeposit(quote: Quote, tx: UnsignedTx): Promise<void> {\n const chainId = Number(quote.intentId.split(\":\")[1]);\n const minOut = quote.providerData?.minOut;\n if (!BALANCER_V2_CHAINS[chainId] || !minOut || !tx.data) return;\n const quoted = quote.txRequest?.data;\n if (!quoted || tx.data.length !== quoted.length || tx.data.toLowerCase() !== quoted.toLowerCase()) {\n throw new Error(\"balancer: the transaction is not the quoted batch. Nothing was sent; refresh the quote.\");\n }\n const call = decodeFunctionData({abi: VAULT_ABI, data: quoted as Hex});\n if (call.functionName !== \"batchSwap\") throw new Error(\"balancer: the transaction is not a Vault batchSwap\");\n const [, swaps, assets, funds, limits] = call.args;\n if (swaps.length === 0 || swaps.length > BALANCER_MAX_SWAPS) throw new Error(`balancer: the batch has ${swaps.length} pool hops, not 1–${BALANCER_MAX_SWAPS}`);\n if (swaps.some((step) => step.userData !== \"0x\")) throw new Error(\"balancer: the batch carries pool userData this module never sends\");\n const outIdx = limits.findIndex((l) => l < 0n);\n const deltas = await query(chainId, swaps, assets, funds);\n const out = -deltas[outIdx]!;\n if (out < BigInt(minOut)) throw new Error(`balancer: price moved — the pools now pay ${out}, under your minimum ${minOut}. Nothing was sent; refresh the quote.`);\n },\n\n async buildDeposit(quote: Quote): Promise<UnsignedTx> {\n const tx = quote.txRequest;\n if (!tx?.to || !tx.data) throw new Error(\"swap2: Balancer quote has no transaction request\");\n return {chain: \"\", to: tx.to, value: tx.value ?? \"0\", data: tx.data, ...(tx.chainId !== undefined ? {chainId: tx.chainId} : {}), ...(tx.gasLimit ? {gasLimit: tx.gasLimit} : {}), ...(tx.approvalAddress ? {approvalAddress: tx.approvalAddress} : {})};\n },\n\n async trackIntent(ref: IntentRef): Promise<IntentStatus> {\n if (!ref.sourceTxHash) return {state: \"pending\"};\n const chainId = Number(ref.intentId.split(\":\")[1]);\n if (!BALANCER_V2_CHAINS[chainId] || !config.rpcUrls[chainId]?.length) return {state: \"pending\"};\n try {\n const receipt = (await rpc(chainId, \"eth_getTransactionReceipt\", [ref.sourceTxHash])) as {status?: string} | null;\n if (!receipt) return {state: \"pending\"};\n return receipt.status === \"0x1\" ? {state: \"completed\", destTxHash: ref.sourceTxHash} : {state: \"failed\"};\n } catch {\n return {state: \"pending\"};\n }\n },\n };\n}\n\n/**\n * The Balancer V2 Vault a swap calls and approves on the chains this provider serves — the app's pins\n * for `remoteProvider({name: \"balancer\", baseUrl, ...balancerPins})` (scan 2026-10-27 modules, F8/F16).\n */\nexport const balancerPins: RemotePins = Object.freeze({\n callTargets: (chain: string): readonly string[] => balancerVaultFor(chain),\n spenders: (chain: string): readonly string[] => balancerVaultFor(chain),\n});\nfunction balancerVaultFor(chain: string): readonly string[] {\n const id = /^eip155:[0-9]+$/.test(chain) ? Number(chain.slice(7)) : NaN;\n return Object.prototype.hasOwnProperty.call(BALANCER_V2_CHAINS, id) ? [BALANCER_V2_VAULT] : [];\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAMA,sBAAwD;AACxD,IAAAA,eAA4B;;;ACN5B,kBAA2G;AAE3G,kBAA0D;AAE1D,kBAA6B;AAetB,IAAM,oBAAoB;AAS1B,IAAM,qBAAmF,OAAO,OAAO;AAAA,EAC5G,GAAG,EAAC,KAAK,WAAW,MAAM,6CAA4C;AAAA,EACtE,IAAI,EAAC,KAAK,YAAY,MAAM,6CAA4C;AAAA,EACxE,KAAK,EAAC,KAAK,UAAU,MAAM,6CAA4C;AAAA,EACvE,KAAK,EAAC,KAAK,WAAW,MAAM,6CAA4C;AAAA,EACxE,KAAK,EAAC,KAAK,SAAS,MAAM,6CAA4C;AAAA,EACtE,MAAM,EAAC,KAAK,QAAQ,MAAM,6CAA4C;AAAA,EACtE,MAAM,EAAC,KAAK,UAAU,MAAM,6CAA4C;AAAA,EACxE,OAAO,EAAC,KAAK,YAAY,MAAM,6CAA4C;AAAA,EAC3E,OAAO,EAAC,KAAK,aAAa,MAAM,6CAA4C;AAC9E,CAAC;AAED,IAAM,YAAY;AAClB,IAAM,QAAQ;AACd,IAAM,gBAAY,sBAAS;AAAA,EACzB,uCAAuC,SAAS,+BAA+B,KAAK;AAAA,EACpF,kCAAkC,SAAS,+BAA+B,KAAK;AAAA,EAC/E;AACF,CAAC;AAuQM,IAAM,eAA2B,OAAO,OAAO;AAAA,EACpD,aAAa,CAAC,UAAqC,iBAAiB,KAAK;AAAA,EACzE,UAAU,CAAC,UAAqC,iBAAiB,KAAK;AACxE,CAAC;AACD,SAAS,iBAAiB,OAAkC;AAC1D,QAAM,KAAK,kBAAkB,KAAK,KAAK,IAAI,OAAO,MAAM,MAAM,CAAC,CAAC,IAAI;AACpE,SAAO,OAAO,UAAU,eAAe,KAAK,oBAAoB,EAAE,IAAI,CAAC,iBAAiB,IAAI,CAAC;AAC/F;;;ADnTO,IAAM,kBAAkB;AAGxB,IAAM,2BAAgD,oBAAI,IAAI;AAAA,EACnE;AAAA;AAAA,EACA;AAAA;AACF,CAAC;AAEM,SAAS,yBAAyB,IAAa,KAAmD;AACvG,MAAI,KAAC,oBAAM,GAAG,KAAK,KAAK,CAAC,uBAAmB,qBAAO,GAAG,KAAK,CAAC,EAAG,QAAO;AACtE,MAAI,KAAC,6BAAY,IAAI,CAAC,iBAAiB,GAAG,wBAAwB,EAAG,QAAO;AAC5E,aAAO,kCAAiB,IAAI,KAAK,EAAC,UAAU,iBAAiB,UAAU,wBAAiB,4BAAW,GAAG,KAAK,CAAC,IAAI,MAAM,OAAM,CAAC;AAC/H;","names":["import_util"]}
package/dist/activity.js CHANGED
@@ -2,7 +2,7 @@ import {
2
2
  BALANCER_MODULE,
3
3
  BALANCER_VAULT_SELECTORS,
4
4
  describeBalancerActivity
5
- } from "./chunk-3YTDA5OS.js";
5
+ } from "./chunk-P4BZP7R4.js";
6
6
  export {
7
7
  BALANCER_MODULE,
8
8
  BALANCER_VAULT_SELECTORS,
@@ -12,6 +12,7 @@ var DEFAULT_API = "https://api-v3.balancer.fi/";
12
12
  var DEFAULT_SLIPPAGE_BPS = 50;
13
13
  var DEFAULT_TTL_MS = 3e5;
14
14
  var DEFAULT_DEADLINE_S = 600;
15
+ var BALANCER_MAX_SWAPS = 16;
15
16
  var BALANCER_V2_CHAINS = Object.freeze({
16
17
  1: { api: "MAINNET", weth: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2" },
17
18
  10: { api: "OPTIMISM", weth: "0x4200000000000000000000000000000000000006" },
@@ -147,6 +148,7 @@ function balancerProvider(config) {
147
148
  swaps.push({ poolId: pool, assetInIndex: indexOf(p.tokens[i].address), assetOutIndex: indexOf(p.tokens[i + 1].address), amount: i === 0 ? BigInt(p.inputAmountRaw) : 0n, userData: "0x" });
148
149
  });
149
150
  }
151
+ if (swaps.length > BALANCER_MAX_SWAPS) return refuse(`the SOR route has ${swaps.length} pool hops, more than ${BALANCER_MAX_SWAPS}`);
150
152
  const inIdx = bigintToNumber(indexOf(tokenIn));
151
153
  const outIdx = bigintToNumber(indexOf(tokenOut));
152
154
  if (nativeIn || nativeOut) {
@@ -197,14 +199,25 @@ function balancerProvider(config) {
197
199
  return refuse(e instanceof Error ? e.message.replace(/^balancer: /, "") : String(e));
198
200
  }
199
201
  },
200
- /** Re-price the exact signed batch right before broadcast; refuse when the pools now pay under the minimum. */
202
+ /**
203
+ * Re-price the exact signed batch right before broadcast; refuse when the pools now pay under the minimum.
204
+ * Only the quote's own batch is decoded and priced: behind a backend, `tx` is the client's and only the
205
+ * quote is sealed, so a client's batch (any size, any pools) never reaches the decoder or the RPC
206
+ * (scan 2026-10-27 modules, F24).
207
+ */
201
208
  async preflightDeposit(quote, tx) {
202
209
  const chainId = Number(quote.intentId.split(":")[1]);
203
210
  const minOut = quote.providerData?.minOut;
204
211
  if (!BALANCER_V2_CHAINS[chainId] || !minOut || !tx.data) return;
205
- const call = decodeFunctionData({ abi: VAULT_ABI, data: tx.data });
212
+ const quoted = quote.txRequest?.data;
213
+ if (!quoted || tx.data.length !== quoted.length || tx.data.toLowerCase() !== quoted.toLowerCase()) {
214
+ throw new Error("balancer: the transaction is not the quoted batch. Nothing was sent; refresh the quote.");
215
+ }
216
+ const call = decodeFunctionData({ abi: VAULT_ABI, data: quoted });
206
217
  if (call.functionName !== "batchSwap") throw new Error("balancer: the transaction is not a Vault batchSwap");
207
218
  const [, swaps, assets, funds, limits] = call.args;
219
+ if (swaps.length === 0 || swaps.length > BALANCER_MAX_SWAPS) throw new Error(`balancer: the batch has ${swaps.length} pool hops, not 1\u2013${BALANCER_MAX_SWAPS}`);
220
+ if (swaps.some((step) => step.userData !== "0x")) throw new Error("balancer: the batch carries pool userData this module never sends");
208
221
  const outIdx = limits.findIndex((l) => l < 0n);
209
222
  const deltas = await query(chainId, swaps, assets, funds);
210
223
  const out = -deltas[outIdx];
@@ -229,6 +242,14 @@ function balancerProvider(config) {
229
242
  }
230
243
  };
231
244
  }
245
+ var balancerPins = Object.freeze({
246
+ callTargets: (chain) => balancerVaultFor(chain),
247
+ spenders: (chain) => balancerVaultFor(chain)
248
+ });
249
+ function balancerVaultFor(chain) {
250
+ const id = /^eip155:[0-9]+$/.test(chain) ? Number(chain.slice(7)) : NaN;
251
+ return Object.prototype.hasOwnProperty.call(BALANCER_V2_CHAINS, id) ? [BALANCER_V2_VAULT] : [];
252
+ }
232
253
 
233
254
  // src/activity.ts
234
255
  var BALANCER_MODULE = "@anywalletkit/protocol-swap2-balancer";
@@ -249,7 +270,9 @@ export {
249
270
  BALANCER_VAULT_SELECTORS,
250
271
  describeBalancerActivity,
251
272
  BALANCER_V2_VAULT,
273
+ BALANCER_MAX_SWAPS,
252
274
  BALANCER_V2_CHAINS,
253
- balancerProvider
275
+ balancerProvider,
276
+ balancerPins
254
277
  };
255
- //# sourceMappingURL=chunk-3YTDA5OS.js.map
278
+ //# sourceMappingURL=chunk-P4BZP7R4.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/activity.ts","../src/provider.ts"],"sourcesContent":["/**\n * Activity recognition for Balancer v2: `swap` / `batchSwap` on the Vault\n * (one address on every chain) on a chain the module serves → `swap`,\n * \"Swapped A → B\" from the owner's net transfers.\n */\nimport type {ActivityItem, ChainTx, DescribeActivityContext} from \"@anywalletkit/core\";\nimport {chainLabel, describeSwapLike, matchesCall} from \"@anywalletkit/protocol-swap2-core/activity\";\nimport {evmRef, isEvm} from \"@anywalletkit/protocol-swap2-core/util\";\nimport {BALANCER_V2_CHAINS, BALANCER_V2_VAULT} from \"./provider.js\";\n\nexport const BALANCER_MODULE = \"@anywalletkit/protocol-swap2-balancer\";\n\n/** Vault swap entry points (`viem toFunctionSelector`, 2026-10-14). */\nexport const BALANCER_VAULT_SELECTORS: ReadonlySet<string> = new Set([\n \"0x945bcec9\", // batchSwap(uint8,(bytes32,uint256,uint256,uint256,bytes)[],address[],(address,bool,address,bool),int256[],uint256)\n \"0x52bbbe29\", // swap((bytes32,uint8,address,address,uint256,bytes),(address,bool,address,bool),uint256,uint256)\n]);\n\nexport function describeBalancerActivity(tx: ChainTx, ctx: DescribeActivityContext): ActivityItem | null {\n if (!isEvm(tx.chain) || !BALANCER_V2_CHAINS[evmRef(tx.chain)]) return null;\n if (!matchesCall(tx, [BALANCER_V2_VAULT], BALANCER_VAULT_SELECTORS)) return null;\n return describeSwapLike(tx, ctx, {protocol: BALANCER_MODULE, subtitle: `Balancer v2 · ${chainLabel(tx.chain)}`, kind: \"swap\"});\n}\n","import type {RemotePins} from \"@anywalletkit/protocol-swap2-core/types\";\nimport {decodeFunctionData, decodeFunctionResult, encodeFunctionData, getAddress, parseAbi, type Hex} from \"viem\";\nimport {describeBalancerActivity} from \"./activity.js\";\nimport {evmRef, isEvm, timeoutSignal, sourceAssetOf} from \"@anywalletkit/protocol-swap2-core/util\";\nimport type {Asset, CrossChainProvider, IntentRef, IntentStatus, Quote, QuoteParams, UnsignedTx} from \"@anywalletkit/protocol-swap2-core/types\";\nimport {bigintToNumber} from \"@anywalletkit/core\";\n\n/**\n * Balancer v2 — the cp0x permissionless Balancer venue (balancer.cp0x.com).\n *\n * Discovery only from the API: Balancer's SOR (`sorGetSwapPaths`, v2 pools)\n * suggests the path. Everything on the money path is on-chain: the Vault\n * prices that exact path (`queryBatchSwap` via eth_call), the quote uses the\n * Vault's number, and `batchSwap` carries `limits` (in ≤ amountIn, out ≥ min)\n * plus a deadline — the Vault itself refuses anything worse. Native coins use\n * the Vault's own ETH sentinel (address 0): it wraps/unwraps through WETH.\n */\n\nconst ZERO = \"0x0000000000000000000000000000000000000000\";\n/** The same Balancer v2 Vault address on every chain it is deployed on. */\nexport const BALANCER_V2_VAULT = \"0xBA12222222228d8Ba445958a75a0704d566BF2C8\";\nconst DEFAULT_API = \"https://api-v3.balancer.fi/\";\nconst DEFAULT_SLIPPAGE_BPS = 50;\nconst DEFAULT_TTL_MS = 300_000;\nconst DEFAULT_DEADLINE_S = 600;\n/** Most pool hops one batch may carry: the SOR's splits are a handful; a batch past this is refused at quote and at preflight. */\nexport const BALANCER_MAX_SWAPS = 16;\n\n/** Chains where the v2 Vault is live, with the SOR API name and the Vault's WETH() (verified on-chain 2026-10-13). */\nexport const BALANCER_V2_CHAINS: Readonly<Record<number, {api: string; weth: `0x${string}`}>> = Object.freeze({\n 1: {api: \"MAINNET\", weth: \"0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2\"},\n 10: {api: \"OPTIMISM\", weth: \"0x4200000000000000000000000000000000000006\"},\n 100: {api: \"GNOSIS\", weth: \"0xe91D153E0b41518A2Ce8Dd3D7944Fa863463a97d\"},\n 137: {api: \"POLYGON\", weth: \"0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270\"},\n 146: {api: \"SONIC\", weth: \"0x039e2fB66102314Ce7b64Ce5Ce3E5183bc94aD38\"},\n 8453: {api: \"BASE\", weth: \"0x4200000000000000000000000000000000000006\"},\n 9745: {api: \"PLASMA\", weth: \"0x6100E367285b01F48D07953803A2d8dCA5D19873\"},\n 42161: {api: \"ARBITRUM\", weth: \"0x82aF49447D8a07e3bd95BD0d56f35241523fBab1\"},\n 43114: {api: \"AVALANCHE\", weth: \"0xB31f66AA3C1e785363F0875A1B74E27b85FD66c7\"},\n});\n\nconst SWAP_STEP = \"(bytes32 poolId, uint256 assetInIndex, uint256 assetOutIndex, uint256 amount, bytes userData)\";\nconst FUNDS = \"(address sender, bool fromInternalBalance, address recipient, bool toInternalBalance)\";\nconst VAULT_ABI = parseAbi([\n `function queryBatchSwap(uint8 kind, ${SWAP_STEP}[] swaps, address[] assets, ${FUNDS} funds) returns (int256[] assetDeltas)`,\n `function batchSwap(uint8 kind, ${SWAP_STEP}[] swaps, address[] assets, ${FUNDS} funds, int256[] limits, uint256 deadline) payable returns (int256[] assetDeltas)`,\n \"function WETH() view returns (address)\",\n]);\n\ntype Step = {poolId: Hex; assetInIndex: bigint; assetOutIndex: bigint; amount: bigint; userData: Hex};\ntype Funds = {sender: Hex; fromInternalBalance: boolean; recipient: Hex; toInternalBalance: boolean};\n\nexport interface BalancerProviderConfig {\n enabled?: boolean;\n /** JSON-RPC endpoints per EVM chain id (on-chain pricing + receipts). */\n rpcUrls: Readonly<Record<number, readonly string[]>>;\n /** Balancer API (SOR) base URL — discovery only. */\n apiUrl?: string;\n defaultSlippageBps?: number;\n quoteTtlMs?: number;\n deadlineSeconds?: number;\n}\n\ninterface SorPath {\n pools: string[];\n tokens: {address: string; decimals: number}[];\n inputAmountRaw: string;\n outputAmountRaw: string;\n protocolVersion: number;\n}\n\n/** Base units → the decimal string the SOR expects (\"1.5\"). */\nfunction human(amount: bigint, decimals: number): string {\n const s = amount.toString().padStart(decimals + 1, \"0\");\n const whole = s.slice(0, s.length - decimals);\n const frac = s.slice(s.length - decimals).replace(/0+$/, \"\");\n return frac ? `${whole}.${frac}` : whole;\n}\n\nexport function balancerProvider(config: BalancerProviderConfig): CrossChainProvider {\n const api = config.apiUrl ?? DEFAULT_API;\n const slippageDefault = config.defaultSlippageBps ?? DEFAULT_SLIPPAGE_BPS;\n const ttl = config.quoteTtlMs ?? DEFAULT_TTL_MS;\n const deadlineS = config.deadlineSeconds ?? DEFAULT_DEADLINE_S;\n const verified = new Map<number, Promise<void>>();\n\n const chainIdOf = (caip2: string): number | null => {\n if (!isEvm(caip2)) return null;\n const id = evmRef(caip2);\n return BALANCER_V2_CHAINS[id] && config.rpcUrls[id]?.length ? id : null;\n };\n\n async function rpc(chainId: number, method: string, params: unknown[]): Promise<unknown> {\n let lastErr: unknown;\n for (const url of config.rpcUrls[chainId] ?? []) {\n try {\n const res = await fetch(url, {method: \"POST\", headers: {\"content-type\": \"application/json\"}, body: JSON.stringify({jsonrpc: \"2.0\", id: 1, method, params}), signal: timeoutSignal(12_000)});\n if (!res.ok) throw new Error(`HTTP ${res.status}`);\n const body = (await res.json()) as {result?: unknown; error?: {message?: string}};\n if (body.error) throw Object.assign(new Error(body.error.message ?? \"rpc error\"), {rpc: true});\n return body.result ?? null;\n } catch (e) {\n if ((e as {rpc?: boolean}).rpc) throw e; // a revert is an answer, not a dead endpoint\n lastErr = e;\n }\n }\n throw new Error(`balancer: no RPC answered on chain ${chainId} (${lastErr instanceof Error ? lastErr.message : String(lastErr)})`);\n }\n\n /** The Vault is deployed here and wraps through the WETH we pinned. */\n function verify(chainId: number): Promise<void> {\n let p = verified.get(chainId);\n if (!p) {\n p = (async () => {\n const raw = (await rpc(chainId, \"eth_call\", [{to: BALANCER_V2_VAULT, data: encodeFunctionData({abi: VAULT_ABI, functionName: \"WETH\"})}, \"latest\"])) as Hex;\n const weth = decodeFunctionResult({abi: VAULT_ABI, functionName: \"WETH\", data: raw});\n if (getAddress(weth) !== getAddress(BALANCER_V2_CHAINS[chainId]!.weth)) throw new Error(`balancer: the Vault on chain ${chainId} is not the pinned deployment — refusing`);\n })();\n p.catch(() => verified.delete(chainId));\n verified.set(chainId, p);\n }\n return p;\n }\n\n /** Vault.queryBatchSwap on chain: the asset deltas this exact batch would produce. */\n async function query(chainId: number, swaps: readonly Step[], assets: readonly Hex[], funds: Funds): Promise<readonly bigint[]> {\n const data = encodeFunctionData({abi: VAULT_ABI, functionName: \"queryBatchSwap\", args: [0, swaps, assets, funds]});\n const raw = (await rpc(chainId, \"eth_call\", [{to: BALANCER_V2_VAULT, data}, \"latest\"])) as Hex;\n return decodeFunctionResult({abi: VAULT_ABI, functionName: \"queryBatchSwap\", data: raw});\n }\n\n return {\n name: \"balancer\",\n describeActivity: describeBalancerActivity,\n supportsSameChain: true,\n enabled: () => config.enabled ?? true,\n supportsChain: (caip2: string) => chainIdOf(caip2) !== null,\n supportsChainPair: (from: string, to: string) => from === to && chainIdOf(from) !== null,\n supportsRoute: (from: Asset, to: Asset) => from.chain === to.chain && chainIdOf(from.chain) !== null,\n\n async getQuote(params: QuoteParams, signal?: AbortSignal): Promise<Quote | null> {\n const refuse = (why: string): null => {\n params.onRefusal?.(`balancer: ${why}`);\n return null;\n };\n if (params.flex) return refuse(\"amount-flexible deposits are not supported\");\n if (params.sourceChain !== params.destChain) return refuse(\"same-chain swaps only\");\n const chainId = chainIdOf(params.sourceChain);\n if (chainId === null) return refuse(`no Balancer v2 Vault or RPC for ${params.sourceChain}`);\n const chain = BALANCER_V2_CHAINS[chainId]!;\n const src = sourceAssetOf(params);\n if (!src) return refuse(`unknown source token ${params.sourceToken}`);\n const nativeIn = !src.contract;\n const nativeOut = /^0x0+$/.test(params.destToken);\n if (!nativeOut && !/^0x[0-9a-fA-F]{40}$/.test(params.destToken)) return refuse(\"destination token must be an EVM address\");\n if (!/^0x[0-9a-fA-F]{40}$/.test(params.destAddress)) return refuse(\"recipient must be an EVM address\");\n let amountIn: bigint;\n try {\n amountIn = BigInt(params.amountIn);\n } catch {\n return refuse(\"amount is not an integer\");\n }\n if (amountIn <= 0n) return refuse(\"amount must be positive\");\n const tokenIn = getAddress(nativeIn ? chain.weth : src.contract!);\n const tokenOut = getAddress(nativeOut ? chain.weth : params.destToken);\n if (tokenIn === tokenOut) return refuse(nativeIn || nativeOut ? \"that is a wrap/unwrap, not a swap\" : \"same token in and out\");\n\n try {\n await verify(chainId);\n // 1) Discovery: the SOR's v2 paths (never trusted for price).\n // Values are inlined (this API ignores GraphQL variables); all are validated\n // addresses / a decimal number / a fixed enum, so nothing user-shaped is spliced.\n const query_ = `query { sorGetSwapPaths(chain: ${chain.api}, swapAmount: \"${human(amountIn, src.decimals)}\", swapType: EXACT_IN, tokenIn: \"${tokenIn}\", tokenOut: \"${tokenOut}\", useProtocolVersion: 2) { protocolVersion paths { pools tokens { address decimals } inputAmountRaw outputAmountRaw protocolVersion } } }`;\n const res = await fetch(api, {\n method: \"POST\",\n headers: {\"content-type\": \"application/json\"},\n body: JSON.stringify({query: query_}),\n signal: signal ?? timeoutSignal(12_000),\n });\n if (!res.ok) return refuse(`SOR answered HTTP ${res.status}`);\n const body = (await res.json()) as {data?: {sorGetSwapPaths?: {protocolVersion?: number; paths?: SorPath[]}}; errors?: {message?: string}[]};\n const paths = body.data?.sorGetSwapPaths?.paths ?? [];\n if (body.errors?.length) return refuse(`SOR: ${body.errors[0]?.message?.slice(0, 120) ?? \"error\"}`);\n if (!paths.length || paths.some((p) => p.protocolVersion !== 2 || p.pools.length === 0 || p.tokens.length !== p.pools.length + 1)) {\n return refuse(\"no Balancer v2 pool route for this pair and amount\");\n }\n if (paths.reduce((s, p) => s + BigInt(p.inputAmountRaw), 0n) !== amountIn) return refuse(\"the SOR split does not add up to the amount\");\n\n // 2) Build the batch from the suggested paths.\n const assets: Hex[] = [];\n const indexOf = (a: string) => {\n const addr = getAddress(a);\n let i = assets.findIndex((x) => x === addr);\n if (i < 0) i = assets.push(addr) - 1;\n return BigInt(i);\n };\n const swaps: Step[] = [];\n for (const p of paths) {\n if (getAddress(p.tokens[0]!.address) !== tokenIn || getAddress(p.tokens[p.tokens.length - 1]!.address) !== tokenOut) return refuse(\"the SOR path does not start/end at the requested tokens\");\n p.pools.forEach((pool, i) => {\n if (!/^0x[0-9a-fA-F]{64}$/.test(pool)) throw new Error(\"balancer: malformed pool id from the SOR\");\n swaps.push({poolId: pool as Hex, assetInIndex: indexOf(p.tokens[i]!.address), assetOutIndex: indexOf(p.tokens[i + 1]!.address), amount: i === 0 ? BigInt(p.inputAmountRaw) : 0n, userData: \"0x\"});\n });\n }\n if (swaps.length > BALANCER_MAX_SWAPS) return refuse(`the SOR route has ${swaps.length} pool hops, more than ${BALANCER_MAX_SWAPS}`);\n const inIdx = bigintToNumber(indexOf(tokenIn));\n const outIdx = bigintToNumber(indexOf(tokenOut));\n // The Vault's ETH sentinel: native in/out wraps/unwraps through its WETH.\n if (nativeIn || nativeOut) {\n const wethIdx = assets.findIndex((a) => a === getAddress(chain.weth));\n if (paths.some((p) => p.tokens.slice(1, -1).some((t) => getAddress(t.address) === getAddress(chain.weth)))) return refuse(\"this route passes through the wrapped native — pick the wrapped token instead\");\n if (wethIdx >= 0) assets[wethIdx] = ZERO as Hex;\n }\n const sender = getAddress(params.refundAddress && /^0x[0-9a-fA-F]{40}$/.test(params.refundAddress) ? params.refundAddress : params.destAddress);\n const recipient = getAddress(params.destAddress);\n const funds: Funds = {sender, fromInternalBalance: false, recipient, toInternalBalance: false};\n\n // 3) Price it on-chain — this, not the SOR's number, is the quote.\n const deltas = await query(chainId, swaps, assets, funds);\n const paid = deltas[inIdx]!;\n const out = -deltas[outIdx]!;\n if (paid !== amountIn) return refuse(`the Vault would take ${paid}, not ${amountIn}`);\n if (out <= 0n) return refuse(\"the Vault prices this route at zero output\");\n if (deltas.some((d, i) => i !== inIdx && i !== outIdx && d !== 0n)) return refuse(\"the batch leaves an intermediate balance — refusing\");\n\n const bps = params.slippageBps ?? slippageDefault;\n const minOut = (out * BigInt(10_000 - bps)) / 10_000n;\n if (params.minAmountOut !== undefined && minOut < BigInt(params.minAmountOut)) return refuse(`floor breach (min=${minOut} < floor=${params.minAmountOut})`);\n const limits = assets.map((_, i) => (i === inIdx ? amountIn : i === outIdx ? -minOut : 0n));\n const deadline = BigInt(Math.floor(Date.now() / 1000) + deadlineS);\n const data = encodeFunctionData({abi: VAULT_ABI, functionName: \"batchSwap\", args: [0, swaps, assets, funds, limits, deadline]});\n const gasLimit = 160_000n + 95_000n * BigInt(swaps.length);\n return {\n provider: \"balancer\",\n intentId: `balancer:${chainId}`,\n depositAddress: \"\",\n depositMemo: null,\n txRequest: {\n to: BALANCER_V2_VAULT,\n data,\n value: nativeIn ? amountIn.toString() : \"0\",\n chainId,\n gasLimit: gasLimit.toString(),\n ...(nativeIn ? {} : {approvalAddress: BALANCER_V2_VAULT}),\n },\n sourceAmount: amountIn.toString(),\n destAmount: out.toString(),\n minDestAmount: minOut.toString(),\n feeUsd: 0,\n etaSeconds: 15,\n reliability: 0.97,\n validUntilMs: Date.now() + ttl,\n // Pool swap fees are inside the Vault-priced output; no fee is added on top.\n providerData: {paths: String(paths.length), hops: String(swaps.length), minOut: minOut.toString(), poolFee: \"pools\"},\n };\n } catch (e) {\n return refuse(e instanceof Error ? e.message.replace(/^balancer: /, \"\") : String(e));\n }\n },\n\n /**\n * Re-price the exact signed batch right before broadcast; refuse when the pools now pay under the minimum.\n * Only the quote's own batch is decoded and priced: behind a backend, `tx` is the client's and only the\n * quote is sealed, so a client's batch (any size, any pools) never reaches the decoder or the RPC\n * (scan 2026-10-27 modules, F24).\n */\n async preflightDeposit(quote: Quote, tx: UnsignedTx): Promise<void> {\n const chainId = Number(quote.intentId.split(\":\")[1]);\n const minOut = quote.providerData?.minOut;\n if (!BALANCER_V2_CHAINS[chainId] || !minOut || !tx.data) return;\n const quoted = quote.txRequest?.data;\n if (!quoted || tx.data.length !== quoted.length || tx.data.toLowerCase() !== quoted.toLowerCase()) {\n throw new Error(\"balancer: the transaction is not the quoted batch. Nothing was sent; refresh the quote.\");\n }\n const call = decodeFunctionData({abi: VAULT_ABI, data: quoted as Hex});\n if (call.functionName !== \"batchSwap\") throw new Error(\"balancer: the transaction is not a Vault batchSwap\");\n const [, swaps, assets, funds, limits] = call.args;\n if (swaps.length === 0 || swaps.length > BALANCER_MAX_SWAPS) throw new Error(`balancer: the batch has ${swaps.length} pool hops, not 1–${BALANCER_MAX_SWAPS}`);\n if (swaps.some((step) => step.userData !== \"0x\")) throw new Error(\"balancer: the batch carries pool userData this module never sends\");\n const outIdx = limits.findIndex((l) => l < 0n);\n const deltas = await query(chainId, swaps, assets, funds);\n const out = -deltas[outIdx]!;\n if (out < BigInt(minOut)) throw new Error(`balancer: price moved — the pools now pay ${out}, under your minimum ${minOut}. Nothing was sent; refresh the quote.`);\n },\n\n async buildDeposit(quote: Quote): Promise<UnsignedTx> {\n const tx = quote.txRequest;\n if (!tx?.to || !tx.data) throw new Error(\"swap2: Balancer quote has no transaction request\");\n return {chain: \"\", to: tx.to, value: tx.value ?? \"0\", data: tx.data, ...(tx.chainId !== undefined ? {chainId: tx.chainId} : {}), ...(tx.gasLimit ? {gasLimit: tx.gasLimit} : {}), ...(tx.approvalAddress ? {approvalAddress: tx.approvalAddress} : {})};\n },\n\n async trackIntent(ref: IntentRef): Promise<IntentStatus> {\n if (!ref.sourceTxHash) return {state: \"pending\"};\n const chainId = Number(ref.intentId.split(\":\")[1]);\n if (!BALANCER_V2_CHAINS[chainId] || !config.rpcUrls[chainId]?.length) return {state: \"pending\"};\n try {\n const receipt = (await rpc(chainId, \"eth_getTransactionReceipt\", [ref.sourceTxHash])) as {status?: string} | null;\n if (!receipt) return {state: \"pending\"};\n return receipt.status === \"0x1\" ? {state: \"completed\", destTxHash: ref.sourceTxHash} : {state: \"failed\"};\n } catch {\n return {state: \"pending\"};\n }\n },\n };\n}\n\n/**\n * The Balancer V2 Vault a swap calls and approves on the chains this provider serves — the app's pins\n * for `remoteProvider({name: \"balancer\", baseUrl, ...balancerPins})` (scan 2026-10-27 modules, F8/F16).\n */\nexport const balancerPins: RemotePins = Object.freeze({\n callTargets: (chain: string): readonly string[] => balancerVaultFor(chain),\n spenders: (chain: string): readonly string[] => balancerVaultFor(chain),\n});\nfunction balancerVaultFor(chain: string): readonly string[] {\n const id = /^eip155:[0-9]+$/.test(chain) ? Number(chain.slice(7)) : NaN;\n return Object.prototype.hasOwnProperty.call(BALANCER_V2_CHAINS, id) ? [BALANCER_V2_VAULT] : [];\n}\n"],"mappings":"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","names":["evmRef","isEvm","isEvm","evmRef"]}
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  BALANCER_V2_CHAINS,
3
3
  balancerProvider
4
- } from "./chunk-3YTDA5OS.js";
4
+ } from "./chunk-P4BZP7R4.js";
5
5
 
6
6
  // src/protocol.ts
7
7
  import { SingleProviderSwap2 } from "@anywalletkit/protocol-swap2-core/swap2/single";
@@ -25,4 +25,4 @@ var BalancerSwap2Protocol = class extends SingleProviderSwap2 {
25
25
  export {
26
26
  BalancerSwap2Protocol
27
27
  };
28
- //# sourceMappingURL=chunk-E2XTZEXA.js.map
28
+ //# sourceMappingURL=chunk-W33N5DLO.js.map
package/dist/index.cjs CHANGED
@@ -20,11 +20,13 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  // src/index.ts
21
21
  var src_exports = {};
22
22
  __export(src_exports, {
23
+ BALANCER_MAX_SWAPS: () => BALANCER_MAX_SWAPS,
23
24
  BALANCER_MODULE: () => BALANCER_MODULE,
24
25
  BALANCER_V2_CHAINS: () => BALANCER_V2_CHAINS,
25
26
  BALANCER_V2_VAULT: () => BALANCER_V2_VAULT,
26
27
  BALANCER_VAULT_SELECTORS: () => BALANCER_VAULT_SELECTORS,
27
28
  BalancerSwap2Protocol: () => BalancerSwap2Protocol,
29
+ balancerPins: () => balancerPins,
28
30
  balancerProvider: () => balancerProvider,
29
31
  default: () => BalancerSwap2Protocol,
30
32
  describeBalancerActivity: () => describeBalancerActivity
@@ -59,6 +61,7 @@ var DEFAULT_API = "https://api-v3.balancer.fi/";
59
61
  var DEFAULT_SLIPPAGE_BPS = 50;
60
62
  var DEFAULT_TTL_MS = 3e5;
61
63
  var DEFAULT_DEADLINE_S = 600;
64
+ var BALANCER_MAX_SWAPS = 16;
62
65
  var BALANCER_V2_CHAINS = Object.freeze({
63
66
  1: { api: "MAINNET", weth: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2" },
64
67
  10: { api: "OPTIMISM", weth: "0x4200000000000000000000000000000000000006" },
@@ -194,6 +197,7 @@ function balancerProvider(config) {
194
197
  swaps.push({ poolId: pool, assetInIndex: indexOf(p.tokens[i].address), assetOutIndex: indexOf(p.tokens[i + 1].address), amount: i === 0 ? BigInt(p.inputAmountRaw) : 0n, userData: "0x" });
195
198
  });
196
199
  }
200
+ if (swaps.length > BALANCER_MAX_SWAPS) return refuse(`the SOR route has ${swaps.length} pool hops, more than ${BALANCER_MAX_SWAPS}`);
197
201
  const inIdx = (0, import_core.bigintToNumber)(indexOf(tokenIn));
198
202
  const outIdx = (0, import_core.bigintToNumber)(indexOf(tokenOut));
199
203
  if (nativeIn || nativeOut) {
@@ -244,14 +248,25 @@ function balancerProvider(config) {
244
248
  return refuse(e instanceof Error ? e.message.replace(/^balancer: /, "") : String(e));
245
249
  }
246
250
  },
247
- /** Re-price the exact signed batch right before broadcast; refuse when the pools now pay under the minimum. */
251
+ /**
252
+ * Re-price the exact signed batch right before broadcast; refuse when the pools now pay under the minimum.
253
+ * Only the quote's own batch is decoded and priced: behind a backend, `tx` is the client's and only the
254
+ * quote is sealed, so a client's batch (any size, any pools) never reaches the decoder or the RPC
255
+ * (scan 2026-10-27 modules, F24).
256
+ */
248
257
  async preflightDeposit(quote, tx) {
249
258
  const chainId = Number(quote.intentId.split(":")[1]);
250
259
  const minOut = quote.providerData?.minOut;
251
260
  if (!BALANCER_V2_CHAINS[chainId] || !minOut || !tx.data) return;
252
- const call = (0, import_viem.decodeFunctionData)({ abi: VAULT_ABI, data: tx.data });
261
+ const quoted = quote.txRequest?.data;
262
+ if (!quoted || tx.data.length !== quoted.length || tx.data.toLowerCase() !== quoted.toLowerCase()) {
263
+ throw new Error("balancer: the transaction is not the quoted batch. Nothing was sent; refresh the quote.");
264
+ }
265
+ const call = (0, import_viem.decodeFunctionData)({ abi: VAULT_ABI, data: quoted });
253
266
  if (call.functionName !== "batchSwap") throw new Error("balancer: the transaction is not a Vault batchSwap");
254
267
  const [, swaps, assets, funds, limits] = call.args;
268
+ if (swaps.length === 0 || swaps.length > BALANCER_MAX_SWAPS) throw new Error(`balancer: the batch has ${swaps.length} pool hops, not 1\u2013${BALANCER_MAX_SWAPS}`);
269
+ if (swaps.some((step) => step.userData !== "0x")) throw new Error("balancer: the batch carries pool userData this module never sends");
255
270
  const outIdx = limits.findIndex((l) => l < 0n);
256
271
  const deltas = await query(chainId, swaps, assets, funds);
257
272
  const out = -deltas[outIdx];
@@ -276,6 +291,14 @@ function balancerProvider(config) {
276
291
  }
277
292
  };
278
293
  }
294
+ var balancerPins = Object.freeze({
295
+ callTargets: (chain) => balancerVaultFor(chain),
296
+ spenders: (chain) => balancerVaultFor(chain)
297
+ });
298
+ function balancerVaultFor(chain) {
299
+ const id = /^eip155:[0-9]+$/.test(chain) ? Number(chain.slice(7)) : NaN;
300
+ return Object.prototype.hasOwnProperty.call(BALANCER_V2_CHAINS, id) ? [BALANCER_V2_VAULT] : [];
301
+ }
279
302
 
280
303
  // src/protocol.ts
281
304
  var import_single = require("@anywalletkit/protocol-swap2-core/swap2/single");
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/provider.ts","../src/activity.ts","../src/protocol.ts"],"sourcesContent":["export * from \"./provider.js\";\nexport {BalancerSwap2Protocol} from \"./protocol.js\";\nexport {BalancerSwap2Protocol as default} from \"./protocol.js\";\nexport * from \"./activity.js\";\nexport type { BalancerSwap2Config } from \"./protocol.js\"\n","import {decodeFunctionData, decodeFunctionResult, encodeFunctionData, getAddress, parseAbi, type Hex} from \"viem\";\nimport {describeBalancerActivity} from \"./activity.js\";\nimport {evmRef, isEvm, timeoutSignal, sourceAssetOf} from \"@anywalletkit/protocol-swap2-core/util\";\nimport type {Asset, CrossChainProvider, IntentRef, IntentStatus, Quote, QuoteParams, UnsignedTx} from \"@anywalletkit/protocol-swap2-core/types\";\nimport {bigintToNumber} from \"@anywalletkit/core\";\n\n/**\n * Balancer v2 — the cp0x permissionless Balancer venue (balancer.cp0x.com).\n *\n * Discovery only from the API: Balancer's SOR (`sorGetSwapPaths`, v2 pools)\n * suggests the path. Everything on the money path is on-chain: the Vault\n * prices that exact path (`queryBatchSwap` via eth_call), the quote uses the\n * Vault's number, and `batchSwap` carries `limits` (in ≤ amountIn, out ≥ min)\n * plus a deadline — the Vault itself refuses anything worse. Native coins use\n * the Vault's own ETH sentinel (address 0): it wraps/unwraps through WETH.\n */\n\nconst ZERO = \"0x0000000000000000000000000000000000000000\";\n/** The same Balancer v2 Vault address on every chain it is deployed on. */\nexport const BALANCER_V2_VAULT = \"0xBA12222222228d8Ba445958a75a0704d566BF2C8\";\nconst DEFAULT_API = \"https://api-v3.balancer.fi/\";\nconst DEFAULT_SLIPPAGE_BPS = 50;\nconst DEFAULT_TTL_MS = 300_000;\nconst DEFAULT_DEADLINE_S = 600;\n\n/** Chains where the v2 Vault is live, with the SOR API name and the Vault's WETH() (verified on-chain 2026-10-13). */\nexport const BALANCER_V2_CHAINS: Readonly<Record<number, {api: string; weth: `0x${string}`}>> = Object.freeze({\n 1: {api: \"MAINNET\", weth: \"0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2\"},\n 10: {api: \"OPTIMISM\", weth: \"0x4200000000000000000000000000000000000006\"},\n 100: {api: \"GNOSIS\", weth: \"0xe91D153E0b41518A2Ce8Dd3D7944Fa863463a97d\"},\n 137: {api: \"POLYGON\", weth: \"0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270\"},\n 146: {api: \"SONIC\", weth: \"0x039e2fB66102314Ce7b64Ce5Ce3E5183bc94aD38\"},\n 8453: {api: \"BASE\", weth: \"0x4200000000000000000000000000000000000006\"},\n 9745: {api: \"PLASMA\", weth: \"0x6100E367285b01F48D07953803A2d8dCA5D19873\"},\n 42161: {api: \"ARBITRUM\", weth: \"0x82aF49447D8a07e3bd95BD0d56f35241523fBab1\"},\n 43114: {api: \"AVALANCHE\", weth: \"0xB31f66AA3C1e785363F0875A1B74E27b85FD66c7\"},\n});\n\nconst SWAP_STEP = \"(bytes32 poolId, uint256 assetInIndex, uint256 assetOutIndex, uint256 amount, bytes userData)\";\nconst FUNDS = \"(address sender, bool fromInternalBalance, address recipient, bool toInternalBalance)\";\nconst VAULT_ABI = parseAbi([\n `function queryBatchSwap(uint8 kind, ${SWAP_STEP}[] swaps, address[] assets, ${FUNDS} funds) returns (int256[] assetDeltas)`,\n `function batchSwap(uint8 kind, ${SWAP_STEP}[] swaps, address[] assets, ${FUNDS} funds, int256[] limits, uint256 deadline) payable returns (int256[] assetDeltas)`,\n \"function WETH() view returns (address)\",\n]);\n\ntype Step = {poolId: Hex; assetInIndex: bigint; assetOutIndex: bigint; amount: bigint; userData: Hex};\ntype Funds = {sender: Hex; fromInternalBalance: boolean; recipient: Hex; toInternalBalance: boolean};\n\nexport interface BalancerProviderConfig {\n enabled?: boolean;\n /** JSON-RPC endpoints per EVM chain id (on-chain pricing + receipts). */\n rpcUrls: Readonly<Record<number, readonly string[]>>;\n /** Balancer API (SOR) base URL — discovery only. */\n apiUrl?: string;\n defaultSlippageBps?: number;\n quoteTtlMs?: number;\n deadlineSeconds?: number;\n}\n\ninterface SorPath {\n pools: string[];\n tokens: {address: string; decimals: number}[];\n inputAmountRaw: string;\n outputAmountRaw: string;\n protocolVersion: number;\n}\n\n/** Base units → the decimal string the SOR expects (\"1.5\"). */\nfunction human(amount: bigint, decimals: number): string {\n const s = amount.toString().padStart(decimals + 1, \"0\");\n const whole = s.slice(0, s.length - decimals);\n const frac = s.slice(s.length - decimals).replace(/0+$/, \"\");\n return frac ? `${whole}.${frac}` : whole;\n}\n\nexport function balancerProvider(config: BalancerProviderConfig): CrossChainProvider {\n const api = config.apiUrl ?? DEFAULT_API;\n const slippageDefault = config.defaultSlippageBps ?? DEFAULT_SLIPPAGE_BPS;\n const ttl = config.quoteTtlMs ?? DEFAULT_TTL_MS;\n const deadlineS = config.deadlineSeconds ?? DEFAULT_DEADLINE_S;\n const verified = new Map<number, Promise<void>>();\n\n const chainIdOf = (caip2: string): number | null => {\n if (!isEvm(caip2)) return null;\n const id = evmRef(caip2);\n return BALANCER_V2_CHAINS[id] && config.rpcUrls[id]?.length ? id : null;\n };\n\n async function rpc(chainId: number, method: string, params: unknown[]): Promise<unknown> {\n let lastErr: unknown;\n for (const url of config.rpcUrls[chainId] ?? []) {\n try {\n const res = await fetch(url, {method: \"POST\", headers: {\"content-type\": \"application/json\"}, body: JSON.stringify({jsonrpc: \"2.0\", id: 1, method, params}), signal: timeoutSignal(12_000)});\n if (!res.ok) throw new Error(`HTTP ${res.status}`);\n const body = (await res.json()) as {result?: unknown; error?: {message?: string}};\n if (body.error) throw Object.assign(new Error(body.error.message ?? \"rpc error\"), {rpc: true});\n return body.result ?? null;\n } catch (e) {\n if ((e as {rpc?: boolean}).rpc) throw e; // a revert is an answer, not a dead endpoint\n lastErr = e;\n }\n }\n throw new Error(`balancer: no RPC answered on chain ${chainId} (${lastErr instanceof Error ? lastErr.message : String(lastErr)})`);\n }\n\n /** The Vault is deployed here and wraps through the WETH we pinned. */\n function verify(chainId: number): Promise<void> {\n let p = verified.get(chainId);\n if (!p) {\n p = (async () => {\n const raw = (await rpc(chainId, \"eth_call\", [{to: BALANCER_V2_VAULT, data: encodeFunctionData({abi: VAULT_ABI, functionName: \"WETH\"})}, \"latest\"])) as Hex;\n const weth = decodeFunctionResult({abi: VAULT_ABI, functionName: \"WETH\", data: raw});\n if (getAddress(weth) !== getAddress(BALANCER_V2_CHAINS[chainId]!.weth)) throw new Error(`balancer: the Vault on chain ${chainId} is not the pinned deployment — refusing`);\n })();\n p.catch(() => verified.delete(chainId));\n verified.set(chainId, p);\n }\n return p;\n }\n\n /** Vault.queryBatchSwap on chain: the asset deltas this exact batch would produce. */\n async function query(chainId: number, swaps: readonly Step[], assets: readonly Hex[], funds: Funds): Promise<readonly bigint[]> {\n const data = encodeFunctionData({abi: VAULT_ABI, functionName: \"queryBatchSwap\", args: [0, swaps, assets, funds]});\n const raw = (await rpc(chainId, \"eth_call\", [{to: BALANCER_V2_VAULT, data}, \"latest\"])) as Hex;\n return decodeFunctionResult({abi: VAULT_ABI, functionName: \"queryBatchSwap\", data: raw});\n }\n\n return {\n name: \"balancer\",\n describeActivity: describeBalancerActivity,\n supportsSameChain: true,\n enabled: () => config.enabled ?? true,\n supportsChain: (caip2: string) => chainIdOf(caip2) !== null,\n supportsChainPair: (from: string, to: string) => from === to && chainIdOf(from) !== null,\n supportsRoute: (from: Asset, to: Asset) => from.chain === to.chain && chainIdOf(from.chain) !== null,\n\n async getQuote(params: QuoteParams, signal?: AbortSignal): Promise<Quote | null> {\n const refuse = (why: string): null => {\n params.onRefusal?.(`balancer: ${why}`);\n return null;\n };\n if (params.flex) return refuse(\"amount-flexible deposits are not supported\");\n if (params.sourceChain !== params.destChain) return refuse(\"same-chain swaps only\");\n const chainId = chainIdOf(params.sourceChain);\n if (chainId === null) return refuse(`no Balancer v2 Vault or RPC for ${params.sourceChain}`);\n const chain = BALANCER_V2_CHAINS[chainId]!;\n const src = sourceAssetOf(params);\n if (!src) return refuse(`unknown source token ${params.sourceToken}`);\n const nativeIn = !src.contract;\n const nativeOut = /^0x0+$/.test(params.destToken);\n if (!nativeOut && !/^0x[0-9a-fA-F]{40}$/.test(params.destToken)) return refuse(\"destination token must be an EVM address\");\n if (!/^0x[0-9a-fA-F]{40}$/.test(params.destAddress)) return refuse(\"recipient must be an EVM address\");\n let amountIn: bigint;\n try {\n amountIn = BigInt(params.amountIn);\n } catch {\n return refuse(\"amount is not an integer\");\n }\n if (amountIn <= 0n) return refuse(\"amount must be positive\");\n const tokenIn = getAddress(nativeIn ? chain.weth : src.contract!);\n const tokenOut = getAddress(nativeOut ? chain.weth : params.destToken);\n if (tokenIn === tokenOut) return refuse(nativeIn || nativeOut ? \"that is a wrap/unwrap, not a swap\" : \"same token in and out\");\n\n try {\n await verify(chainId);\n // 1) Discovery: the SOR's v2 paths (never trusted for price).\n // Values are inlined (this API ignores GraphQL variables); all are validated\n // addresses / a decimal number / a fixed enum, so nothing user-shaped is spliced.\n const query_ = `query { sorGetSwapPaths(chain: ${chain.api}, swapAmount: \"${human(amountIn, src.decimals)}\", swapType: EXACT_IN, tokenIn: \"${tokenIn}\", tokenOut: \"${tokenOut}\", useProtocolVersion: 2) { protocolVersion paths { pools tokens { address decimals } inputAmountRaw outputAmountRaw protocolVersion } } }`;\n const res = await fetch(api, {\n method: \"POST\",\n headers: {\"content-type\": \"application/json\"},\n body: JSON.stringify({query: query_}),\n signal: signal ?? timeoutSignal(12_000),\n });\n if (!res.ok) return refuse(`SOR answered HTTP ${res.status}`);\n const body = (await res.json()) as {data?: {sorGetSwapPaths?: {protocolVersion?: number; paths?: SorPath[]}}; errors?: {message?: string}[]};\n const paths = body.data?.sorGetSwapPaths?.paths ?? [];\n if (body.errors?.length) return refuse(`SOR: ${body.errors[0]?.message?.slice(0, 120) ?? \"error\"}`);\n if (!paths.length || paths.some((p) => p.protocolVersion !== 2 || p.pools.length === 0 || p.tokens.length !== p.pools.length + 1)) {\n return refuse(\"no Balancer v2 pool route for this pair and amount\");\n }\n if (paths.reduce((s, p) => s + BigInt(p.inputAmountRaw), 0n) !== amountIn) return refuse(\"the SOR split does not add up to the amount\");\n\n // 2) Build the batch from the suggested paths.\n const assets: Hex[] = [];\n const indexOf = (a: string) => {\n const addr = getAddress(a);\n let i = assets.findIndex((x) => x === addr);\n if (i < 0) i = assets.push(addr) - 1;\n return BigInt(i);\n };\n const swaps: Step[] = [];\n for (const p of paths) {\n if (getAddress(p.tokens[0]!.address) !== tokenIn || getAddress(p.tokens[p.tokens.length - 1]!.address) !== tokenOut) return refuse(\"the SOR path does not start/end at the requested tokens\");\n p.pools.forEach((pool, i) => {\n if (!/^0x[0-9a-fA-F]{64}$/.test(pool)) throw new Error(\"balancer: malformed pool id from the SOR\");\n swaps.push({poolId: pool as Hex, assetInIndex: indexOf(p.tokens[i]!.address), assetOutIndex: indexOf(p.tokens[i + 1]!.address), amount: i === 0 ? BigInt(p.inputAmountRaw) : 0n, userData: \"0x\"});\n });\n }\n const inIdx = bigintToNumber(indexOf(tokenIn));\n const outIdx = bigintToNumber(indexOf(tokenOut));\n // The Vault's ETH sentinel: native in/out wraps/unwraps through its WETH.\n if (nativeIn || nativeOut) {\n const wethIdx = assets.findIndex((a) => a === getAddress(chain.weth));\n if (paths.some((p) => p.tokens.slice(1, -1).some((t) => getAddress(t.address) === getAddress(chain.weth)))) return refuse(\"this route passes through the wrapped native — pick the wrapped token instead\");\n if (wethIdx >= 0) assets[wethIdx] = ZERO as Hex;\n }\n const sender = getAddress(params.refundAddress && /^0x[0-9a-fA-F]{40}$/.test(params.refundAddress) ? params.refundAddress : params.destAddress);\n const recipient = getAddress(params.destAddress);\n const funds: Funds = {sender, fromInternalBalance: false, recipient, toInternalBalance: false};\n\n // 3) Price it on-chain — this, not the SOR's number, is the quote.\n const deltas = await query(chainId, swaps, assets, funds);\n const paid = deltas[inIdx]!;\n const out = -deltas[outIdx]!;\n if (paid !== amountIn) return refuse(`the Vault would take ${paid}, not ${amountIn}`);\n if (out <= 0n) return refuse(\"the Vault prices this route at zero output\");\n if (deltas.some((d, i) => i !== inIdx && i !== outIdx && d !== 0n)) return refuse(\"the batch leaves an intermediate balance — refusing\");\n\n const bps = params.slippageBps ?? slippageDefault;\n const minOut = (out * BigInt(10_000 - bps)) / 10_000n;\n if (params.minAmountOut !== undefined && minOut < BigInt(params.minAmountOut)) return refuse(`floor breach (min=${minOut} < floor=${params.minAmountOut})`);\n const limits = assets.map((_, i) => (i === inIdx ? amountIn : i === outIdx ? -minOut : 0n));\n const deadline = BigInt(Math.floor(Date.now() / 1000) + deadlineS);\n const data = encodeFunctionData({abi: VAULT_ABI, functionName: \"batchSwap\", args: [0, swaps, assets, funds, limits, deadline]});\n const gasLimit = 160_000n + 95_000n * BigInt(swaps.length);\n return {\n provider: \"balancer\",\n intentId: `balancer:${chainId}`,\n depositAddress: \"\",\n depositMemo: null,\n txRequest: {\n to: BALANCER_V2_VAULT,\n data,\n value: nativeIn ? amountIn.toString() : \"0\",\n chainId,\n gasLimit: gasLimit.toString(),\n ...(nativeIn ? {} : {approvalAddress: BALANCER_V2_VAULT}),\n },\n sourceAmount: amountIn.toString(),\n destAmount: out.toString(),\n minDestAmount: minOut.toString(),\n feeUsd: 0,\n etaSeconds: 15,\n reliability: 0.97,\n validUntilMs: Date.now() + ttl,\n // Pool swap fees are inside the Vault-priced output; no fee is added on top.\n providerData: {paths: String(paths.length), hops: String(swaps.length), minOut: minOut.toString(), poolFee: \"pools\"},\n };\n } catch (e) {\n return refuse(e instanceof Error ? e.message.replace(/^balancer: /, \"\") : String(e));\n }\n },\n\n /** Re-price the exact signed batch right before broadcast; refuse when the pools now pay under the minimum. */\n async preflightDeposit(quote: Quote, tx: UnsignedTx): Promise<void> {\n const chainId = Number(quote.intentId.split(\":\")[1]);\n const minOut = quote.providerData?.minOut;\n if (!BALANCER_V2_CHAINS[chainId] || !minOut || !tx.data) return;\n const call = decodeFunctionData({abi: VAULT_ABI, data: tx.data as Hex});\n if (call.functionName !== \"batchSwap\") throw new Error(\"balancer: the transaction is not a Vault batchSwap\");\n const [, swaps, assets, funds, limits] = call.args;\n const outIdx = limits.findIndex((l) => l < 0n);\n const deltas = await query(chainId, swaps, assets, funds);\n const out = -deltas[outIdx]!;\n if (out < BigInt(minOut)) throw new Error(`balancer: price moved — the pools now pay ${out}, under your minimum ${minOut}. Nothing was sent; refresh the quote.`);\n },\n\n async buildDeposit(quote: Quote): Promise<UnsignedTx> {\n const tx = quote.txRequest;\n if (!tx?.to || !tx.data) throw new Error(\"swap2: Balancer quote has no transaction request\");\n return {chain: \"\", to: tx.to, value: tx.value ?? \"0\", data: tx.data, ...(tx.chainId !== undefined ? {chainId: tx.chainId} : {}), ...(tx.gasLimit ? {gasLimit: tx.gasLimit} : {}), ...(tx.approvalAddress ? {approvalAddress: tx.approvalAddress} : {})};\n },\n\n async trackIntent(ref: IntentRef): Promise<IntentStatus> {\n if (!ref.sourceTxHash) return {state: \"pending\"};\n const chainId = Number(ref.intentId.split(\":\")[1]);\n if (!BALANCER_V2_CHAINS[chainId] || !config.rpcUrls[chainId]?.length) return {state: \"pending\"};\n try {\n const receipt = (await rpc(chainId, \"eth_getTransactionReceipt\", [ref.sourceTxHash])) as {status?: string} | null;\n if (!receipt) return {state: \"pending\"};\n return receipt.status === \"0x1\" ? {state: \"completed\", destTxHash: ref.sourceTxHash} : {state: \"failed\"};\n } catch {\n return {state: \"pending\"};\n }\n },\n };\n}\n","/**\n * Activity recognition for Balancer v2: `swap` / `batchSwap` on the Vault\n * (one address on every chain) on a chain the module serves → `swap`,\n * \"Swapped A → B\" from the owner's net transfers.\n */\nimport type {ActivityItem, ChainTx, DescribeActivityContext} from \"@anywalletkit/core\";\nimport {chainLabel, describeSwapLike, matchesCall} from \"@anywalletkit/protocol-swap2-core/activity\";\nimport {evmRef, isEvm} from \"@anywalletkit/protocol-swap2-core/util\";\nimport {BALANCER_V2_CHAINS, BALANCER_V2_VAULT} from \"./provider.js\";\n\nexport const BALANCER_MODULE = \"@anywalletkit/protocol-swap2-balancer\";\n\n/** Vault swap entry points (`viem toFunctionSelector`, 2026-10-14). */\nexport const BALANCER_VAULT_SELECTORS: ReadonlySet<string> = new Set([\n \"0x945bcec9\", // batchSwap(uint8,(bytes32,uint256,uint256,uint256,bytes)[],address[],(address,bool,address,bool),int256[],uint256)\n \"0x52bbbe29\", // swap((bytes32,uint8,address,address,uint256,bytes),(address,bool,address,bool),uint256,uint256)\n]);\n\nexport function describeBalancerActivity(tx: ChainTx, ctx: DescribeActivityContext): ActivityItem | null {\n if (!isEvm(tx.chain) || !BALANCER_V2_CHAINS[evmRef(tx.chain)]) return null;\n if (!matchesCall(tx, [BALANCER_V2_VAULT], BALANCER_VAULT_SELECTORS)) return null;\n return describeSwapLike(tx, ctx, {protocol: BALANCER_MODULE, subtitle: `Balancer v2 · ${chainLabel(tx.chain)}`, kind: \"swap\"});\n}\n","import {SingleProviderSwap2} from \"@anywalletkit/protocol-swap2-core/swap2/single\";\nimport type {Swap2Account, Swap2EngineConfig} from \"@anywalletkit/protocol-swap2-core/swap2/base\";\nimport {evmRef, isEvm} from \"@anywalletkit/protocol-swap2-core/util\";\nimport {BALANCER_V2_CHAINS, balancerProvider, type BalancerProviderConfig} from \"./provider.js\";\n\nexport interface BalancerSwap2Config extends Swap2EngineConfig, BalancerProviderConfig {}\n\n/** Balancer v2 as a standalone swap2 protocol (same-chain swaps on one account). */\nexport class BalancerSwap2Protocol extends SingleProviderSwap2 {\n protected readonly provider;\n private readonly _rpcUrls: BalancerProviderConfig[\"rpcUrls\"];\n constructor(account: Swap2Account | undefined, config: BalancerSwap2Config) {\n super(account, config);\n this.provider = balancerProvider(config);\n this._rpcUrls = config.rpcUrls ?? {};\n }\n protected override chainSupported(caip2: string): boolean {\n return isEvm(caip2) && !!BALANCER_V2_CHAINS[evmRef(caip2)] && !!this._rpcUrls[evmRef(caip2)]?.length;\n }\n protected override pairSupported(fromChain: string, toChain: string): boolean {\n return fromChain === toChain && this.chainSupported(fromChain);\n }\n}\n"],"mappings":"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","names":["import_util","import_util"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/provider.ts","../src/activity.ts","../src/protocol.ts"],"sourcesContent":["export * from \"./provider.js\";\nexport {BalancerSwap2Protocol} from \"./protocol.js\";\nexport {BalancerSwap2Protocol as default} from \"./protocol.js\";\nexport * from \"./activity.js\";\nexport type { BalancerSwap2Config } from \"./protocol.js\"\n","import type {RemotePins} from \"@anywalletkit/protocol-swap2-core/types\";\nimport {decodeFunctionData, decodeFunctionResult, encodeFunctionData, getAddress, parseAbi, type Hex} from \"viem\";\nimport {describeBalancerActivity} from \"./activity.js\";\nimport {evmRef, isEvm, timeoutSignal, sourceAssetOf} from \"@anywalletkit/protocol-swap2-core/util\";\nimport type {Asset, CrossChainProvider, IntentRef, IntentStatus, Quote, QuoteParams, UnsignedTx} from \"@anywalletkit/protocol-swap2-core/types\";\nimport {bigintToNumber} from \"@anywalletkit/core\";\n\n/**\n * Balancer v2 — the cp0x permissionless Balancer venue (balancer.cp0x.com).\n *\n * Discovery only from the API: Balancer's SOR (`sorGetSwapPaths`, v2 pools)\n * suggests the path. Everything on the money path is on-chain: the Vault\n * prices that exact path (`queryBatchSwap` via eth_call), the quote uses the\n * Vault's number, and `batchSwap` carries `limits` (in ≤ amountIn, out ≥ min)\n * plus a deadline — the Vault itself refuses anything worse. Native coins use\n * the Vault's own ETH sentinel (address 0): it wraps/unwraps through WETH.\n */\n\nconst ZERO = \"0x0000000000000000000000000000000000000000\";\n/** The same Balancer v2 Vault address on every chain it is deployed on. */\nexport const BALANCER_V2_VAULT = \"0xBA12222222228d8Ba445958a75a0704d566BF2C8\";\nconst DEFAULT_API = \"https://api-v3.balancer.fi/\";\nconst DEFAULT_SLIPPAGE_BPS = 50;\nconst DEFAULT_TTL_MS = 300_000;\nconst DEFAULT_DEADLINE_S = 600;\n/** Most pool hops one batch may carry: the SOR's splits are a handful; a batch past this is refused at quote and at preflight. */\nexport const BALANCER_MAX_SWAPS = 16;\n\n/** Chains where the v2 Vault is live, with the SOR API name and the Vault's WETH() (verified on-chain 2026-10-13). */\nexport const BALANCER_V2_CHAINS: Readonly<Record<number, {api: string; weth: `0x${string}`}>> = Object.freeze({\n 1: {api: \"MAINNET\", weth: \"0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2\"},\n 10: {api: \"OPTIMISM\", weth: \"0x4200000000000000000000000000000000000006\"},\n 100: {api: \"GNOSIS\", weth: \"0xe91D153E0b41518A2Ce8Dd3D7944Fa863463a97d\"},\n 137: {api: \"POLYGON\", weth: \"0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270\"},\n 146: {api: \"SONIC\", weth: \"0x039e2fB66102314Ce7b64Ce5Ce3E5183bc94aD38\"},\n 8453: {api: \"BASE\", weth: \"0x4200000000000000000000000000000000000006\"},\n 9745: {api: \"PLASMA\", weth: \"0x6100E367285b01F48D07953803A2d8dCA5D19873\"},\n 42161: {api: \"ARBITRUM\", weth: \"0x82aF49447D8a07e3bd95BD0d56f35241523fBab1\"},\n 43114: {api: \"AVALANCHE\", weth: \"0xB31f66AA3C1e785363F0875A1B74E27b85FD66c7\"},\n});\n\nconst SWAP_STEP = \"(bytes32 poolId, uint256 assetInIndex, uint256 assetOutIndex, uint256 amount, bytes userData)\";\nconst FUNDS = \"(address sender, bool fromInternalBalance, address recipient, bool toInternalBalance)\";\nconst VAULT_ABI = parseAbi([\n `function queryBatchSwap(uint8 kind, ${SWAP_STEP}[] swaps, address[] assets, ${FUNDS} funds) returns (int256[] assetDeltas)`,\n `function batchSwap(uint8 kind, ${SWAP_STEP}[] swaps, address[] assets, ${FUNDS} funds, int256[] limits, uint256 deadline) payable returns (int256[] assetDeltas)`,\n \"function WETH() view returns (address)\",\n]);\n\ntype Step = {poolId: Hex; assetInIndex: bigint; assetOutIndex: bigint; amount: bigint; userData: Hex};\ntype Funds = {sender: Hex; fromInternalBalance: boolean; recipient: Hex; toInternalBalance: boolean};\n\nexport interface BalancerProviderConfig {\n enabled?: boolean;\n /** JSON-RPC endpoints per EVM chain id (on-chain pricing + receipts). */\n rpcUrls: Readonly<Record<number, readonly string[]>>;\n /** Balancer API (SOR) base URL — discovery only. */\n apiUrl?: string;\n defaultSlippageBps?: number;\n quoteTtlMs?: number;\n deadlineSeconds?: number;\n}\n\ninterface SorPath {\n pools: string[];\n tokens: {address: string; decimals: number}[];\n inputAmountRaw: string;\n outputAmountRaw: string;\n protocolVersion: number;\n}\n\n/** Base units → the decimal string the SOR expects (\"1.5\"). */\nfunction human(amount: bigint, decimals: number): string {\n const s = amount.toString().padStart(decimals + 1, \"0\");\n const whole = s.slice(0, s.length - decimals);\n const frac = s.slice(s.length - decimals).replace(/0+$/, \"\");\n return frac ? `${whole}.${frac}` : whole;\n}\n\nexport function balancerProvider(config: BalancerProviderConfig): CrossChainProvider {\n const api = config.apiUrl ?? DEFAULT_API;\n const slippageDefault = config.defaultSlippageBps ?? DEFAULT_SLIPPAGE_BPS;\n const ttl = config.quoteTtlMs ?? DEFAULT_TTL_MS;\n const deadlineS = config.deadlineSeconds ?? DEFAULT_DEADLINE_S;\n const verified = new Map<number, Promise<void>>();\n\n const chainIdOf = (caip2: string): number | null => {\n if (!isEvm(caip2)) return null;\n const id = evmRef(caip2);\n return BALANCER_V2_CHAINS[id] && config.rpcUrls[id]?.length ? id : null;\n };\n\n async function rpc(chainId: number, method: string, params: unknown[]): Promise<unknown> {\n let lastErr: unknown;\n for (const url of config.rpcUrls[chainId] ?? []) {\n try {\n const res = await fetch(url, {method: \"POST\", headers: {\"content-type\": \"application/json\"}, body: JSON.stringify({jsonrpc: \"2.0\", id: 1, method, params}), signal: timeoutSignal(12_000)});\n if (!res.ok) throw new Error(`HTTP ${res.status}`);\n const body = (await res.json()) as {result?: unknown; error?: {message?: string}};\n if (body.error) throw Object.assign(new Error(body.error.message ?? \"rpc error\"), {rpc: true});\n return body.result ?? null;\n } catch (e) {\n if ((e as {rpc?: boolean}).rpc) throw e; // a revert is an answer, not a dead endpoint\n lastErr = e;\n }\n }\n throw new Error(`balancer: no RPC answered on chain ${chainId} (${lastErr instanceof Error ? lastErr.message : String(lastErr)})`);\n }\n\n /** The Vault is deployed here and wraps through the WETH we pinned. */\n function verify(chainId: number): Promise<void> {\n let p = verified.get(chainId);\n if (!p) {\n p = (async () => {\n const raw = (await rpc(chainId, \"eth_call\", [{to: BALANCER_V2_VAULT, data: encodeFunctionData({abi: VAULT_ABI, functionName: \"WETH\"})}, \"latest\"])) as Hex;\n const weth = decodeFunctionResult({abi: VAULT_ABI, functionName: \"WETH\", data: raw});\n if (getAddress(weth) !== getAddress(BALANCER_V2_CHAINS[chainId]!.weth)) throw new Error(`balancer: the Vault on chain ${chainId} is not the pinned deployment — refusing`);\n })();\n p.catch(() => verified.delete(chainId));\n verified.set(chainId, p);\n }\n return p;\n }\n\n /** Vault.queryBatchSwap on chain: the asset deltas this exact batch would produce. */\n async function query(chainId: number, swaps: readonly Step[], assets: readonly Hex[], funds: Funds): Promise<readonly bigint[]> {\n const data = encodeFunctionData({abi: VAULT_ABI, functionName: \"queryBatchSwap\", args: [0, swaps, assets, funds]});\n const raw = (await rpc(chainId, \"eth_call\", [{to: BALANCER_V2_VAULT, data}, \"latest\"])) as Hex;\n return decodeFunctionResult({abi: VAULT_ABI, functionName: \"queryBatchSwap\", data: raw});\n }\n\n return {\n name: \"balancer\",\n describeActivity: describeBalancerActivity,\n supportsSameChain: true,\n enabled: () => config.enabled ?? true,\n supportsChain: (caip2: string) => chainIdOf(caip2) !== null,\n supportsChainPair: (from: string, to: string) => from === to && chainIdOf(from) !== null,\n supportsRoute: (from: Asset, to: Asset) => from.chain === to.chain && chainIdOf(from.chain) !== null,\n\n async getQuote(params: QuoteParams, signal?: AbortSignal): Promise<Quote | null> {\n const refuse = (why: string): null => {\n params.onRefusal?.(`balancer: ${why}`);\n return null;\n };\n if (params.flex) return refuse(\"amount-flexible deposits are not supported\");\n if (params.sourceChain !== params.destChain) return refuse(\"same-chain swaps only\");\n const chainId = chainIdOf(params.sourceChain);\n if (chainId === null) return refuse(`no Balancer v2 Vault or RPC for ${params.sourceChain}`);\n const chain = BALANCER_V2_CHAINS[chainId]!;\n const src = sourceAssetOf(params);\n if (!src) return refuse(`unknown source token ${params.sourceToken}`);\n const nativeIn = !src.contract;\n const nativeOut = /^0x0+$/.test(params.destToken);\n if (!nativeOut && !/^0x[0-9a-fA-F]{40}$/.test(params.destToken)) return refuse(\"destination token must be an EVM address\");\n if (!/^0x[0-9a-fA-F]{40}$/.test(params.destAddress)) return refuse(\"recipient must be an EVM address\");\n let amountIn: bigint;\n try {\n amountIn = BigInt(params.amountIn);\n } catch {\n return refuse(\"amount is not an integer\");\n }\n if (amountIn <= 0n) return refuse(\"amount must be positive\");\n const tokenIn = getAddress(nativeIn ? chain.weth : src.contract!);\n const tokenOut = getAddress(nativeOut ? chain.weth : params.destToken);\n if (tokenIn === tokenOut) return refuse(nativeIn || nativeOut ? \"that is a wrap/unwrap, not a swap\" : \"same token in and out\");\n\n try {\n await verify(chainId);\n // 1) Discovery: the SOR's v2 paths (never trusted for price).\n // Values are inlined (this API ignores GraphQL variables); all are validated\n // addresses / a decimal number / a fixed enum, so nothing user-shaped is spliced.\n const query_ = `query { sorGetSwapPaths(chain: ${chain.api}, swapAmount: \"${human(amountIn, src.decimals)}\", swapType: EXACT_IN, tokenIn: \"${tokenIn}\", tokenOut: \"${tokenOut}\", useProtocolVersion: 2) { protocolVersion paths { pools tokens { address decimals } inputAmountRaw outputAmountRaw protocolVersion } } }`;\n const res = await fetch(api, {\n method: \"POST\",\n headers: {\"content-type\": \"application/json\"},\n body: JSON.stringify({query: query_}),\n signal: signal ?? timeoutSignal(12_000),\n });\n if (!res.ok) return refuse(`SOR answered HTTP ${res.status}`);\n const body = (await res.json()) as {data?: {sorGetSwapPaths?: {protocolVersion?: number; paths?: SorPath[]}}; errors?: {message?: string}[]};\n const paths = body.data?.sorGetSwapPaths?.paths ?? [];\n if (body.errors?.length) return refuse(`SOR: ${body.errors[0]?.message?.slice(0, 120) ?? \"error\"}`);\n if (!paths.length || paths.some((p) => p.protocolVersion !== 2 || p.pools.length === 0 || p.tokens.length !== p.pools.length + 1)) {\n return refuse(\"no Balancer v2 pool route for this pair and amount\");\n }\n if (paths.reduce((s, p) => s + BigInt(p.inputAmountRaw), 0n) !== amountIn) return refuse(\"the SOR split does not add up to the amount\");\n\n // 2) Build the batch from the suggested paths.\n const assets: Hex[] = [];\n const indexOf = (a: string) => {\n const addr = getAddress(a);\n let i = assets.findIndex((x) => x === addr);\n if (i < 0) i = assets.push(addr) - 1;\n return BigInt(i);\n };\n const swaps: Step[] = [];\n for (const p of paths) {\n if (getAddress(p.tokens[0]!.address) !== tokenIn || getAddress(p.tokens[p.tokens.length - 1]!.address) !== tokenOut) return refuse(\"the SOR path does not start/end at the requested tokens\");\n p.pools.forEach((pool, i) => {\n if (!/^0x[0-9a-fA-F]{64}$/.test(pool)) throw new Error(\"balancer: malformed pool id from the SOR\");\n swaps.push({poolId: pool as Hex, assetInIndex: indexOf(p.tokens[i]!.address), assetOutIndex: indexOf(p.tokens[i + 1]!.address), amount: i === 0 ? BigInt(p.inputAmountRaw) : 0n, userData: \"0x\"});\n });\n }\n if (swaps.length > BALANCER_MAX_SWAPS) return refuse(`the SOR route has ${swaps.length} pool hops, more than ${BALANCER_MAX_SWAPS}`);\n const inIdx = bigintToNumber(indexOf(tokenIn));\n const outIdx = bigintToNumber(indexOf(tokenOut));\n // The Vault's ETH sentinel: native in/out wraps/unwraps through its WETH.\n if (nativeIn || nativeOut) {\n const wethIdx = assets.findIndex((a) => a === getAddress(chain.weth));\n if (paths.some((p) => p.tokens.slice(1, -1).some((t) => getAddress(t.address) === getAddress(chain.weth)))) return refuse(\"this route passes through the wrapped native — pick the wrapped token instead\");\n if (wethIdx >= 0) assets[wethIdx] = ZERO as Hex;\n }\n const sender = getAddress(params.refundAddress && /^0x[0-9a-fA-F]{40}$/.test(params.refundAddress) ? params.refundAddress : params.destAddress);\n const recipient = getAddress(params.destAddress);\n const funds: Funds = {sender, fromInternalBalance: false, recipient, toInternalBalance: false};\n\n // 3) Price it on-chain — this, not the SOR's number, is the quote.\n const deltas = await query(chainId, swaps, assets, funds);\n const paid = deltas[inIdx]!;\n const out = -deltas[outIdx]!;\n if (paid !== amountIn) return refuse(`the Vault would take ${paid}, not ${amountIn}`);\n if (out <= 0n) return refuse(\"the Vault prices this route at zero output\");\n if (deltas.some((d, i) => i !== inIdx && i !== outIdx && d !== 0n)) return refuse(\"the batch leaves an intermediate balance — refusing\");\n\n const bps = params.slippageBps ?? slippageDefault;\n const minOut = (out * BigInt(10_000 - bps)) / 10_000n;\n if (params.minAmountOut !== undefined && minOut < BigInt(params.minAmountOut)) return refuse(`floor breach (min=${minOut} < floor=${params.minAmountOut})`);\n const limits = assets.map((_, i) => (i === inIdx ? amountIn : i === outIdx ? -minOut : 0n));\n const deadline = BigInt(Math.floor(Date.now() / 1000) + deadlineS);\n const data = encodeFunctionData({abi: VAULT_ABI, functionName: \"batchSwap\", args: [0, swaps, assets, funds, limits, deadline]});\n const gasLimit = 160_000n + 95_000n * BigInt(swaps.length);\n return {\n provider: \"balancer\",\n intentId: `balancer:${chainId}`,\n depositAddress: \"\",\n depositMemo: null,\n txRequest: {\n to: BALANCER_V2_VAULT,\n data,\n value: nativeIn ? amountIn.toString() : \"0\",\n chainId,\n gasLimit: gasLimit.toString(),\n ...(nativeIn ? {} : {approvalAddress: BALANCER_V2_VAULT}),\n },\n sourceAmount: amountIn.toString(),\n destAmount: out.toString(),\n minDestAmount: minOut.toString(),\n feeUsd: 0,\n etaSeconds: 15,\n reliability: 0.97,\n validUntilMs: Date.now() + ttl,\n // Pool swap fees are inside the Vault-priced output; no fee is added on top.\n providerData: {paths: String(paths.length), hops: String(swaps.length), minOut: minOut.toString(), poolFee: \"pools\"},\n };\n } catch (e) {\n return refuse(e instanceof Error ? e.message.replace(/^balancer: /, \"\") : String(e));\n }\n },\n\n /**\n * Re-price the exact signed batch right before broadcast; refuse when the pools now pay under the minimum.\n * Only the quote's own batch is decoded and priced: behind a backend, `tx` is the client's and only the\n * quote is sealed, so a client's batch (any size, any pools) never reaches the decoder or the RPC\n * (scan 2026-10-27 modules, F24).\n */\n async preflightDeposit(quote: Quote, tx: UnsignedTx): Promise<void> {\n const chainId = Number(quote.intentId.split(\":\")[1]);\n const minOut = quote.providerData?.minOut;\n if (!BALANCER_V2_CHAINS[chainId] || !minOut || !tx.data) return;\n const quoted = quote.txRequest?.data;\n if (!quoted || tx.data.length !== quoted.length || tx.data.toLowerCase() !== quoted.toLowerCase()) {\n throw new Error(\"balancer: the transaction is not the quoted batch. Nothing was sent; refresh the quote.\");\n }\n const call = decodeFunctionData({abi: VAULT_ABI, data: quoted as Hex});\n if (call.functionName !== \"batchSwap\") throw new Error(\"balancer: the transaction is not a Vault batchSwap\");\n const [, swaps, assets, funds, limits] = call.args;\n if (swaps.length === 0 || swaps.length > BALANCER_MAX_SWAPS) throw new Error(`balancer: the batch has ${swaps.length} pool hops, not 1–${BALANCER_MAX_SWAPS}`);\n if (swaps.some((step) => step.userData !== \"0x\")) throw new Error(\"balancer: the batch carries pool userData this module never sends\");\n const outIdx = limits.findIndex((l) => l < 0n);\n const deltas = await query(chainId, swaps, assets, funds);\n const out = -deltas[outIdx]!;\n if (out < BigInt(minOut)) throw new Error(`balancer: price moved — the pools now pay ${out}, under your minimum ${minOut}. Nothing was sent; refresh the quote.`);\n },\n\n async buildDeposit(quote: Quote): Promise<UnsignedTx> {\n const tx = quote.txRequest;\n if (!tx?.to || !tx.data) throw new Error(\"swap2: Balancer quote has no transaction request\");\n return {chain: \"\", to: tx.to, value: tx.value ?? \"0\", data: tx.data, ...(tx.chainId !== undefined ? {chainId: tx.chainId} : {}), ...(tx.gasLimit ? {gasLimit: tx.gasLimit} : {}), ...(tx.approvalAddress ? {approvalAddress: tx.approvalAddress} : {})};\n },\n\n async trackIntent(ref: IntentRef): Promise<IntentStatus> {\n if (!ref.sourceTxHash) return {state: \"pending\"};\n const chainId = Number(ref.intentId.split(\":\")[1]);\n if (!BALANCER_V2_CHAINS[chainId] || !config.rpcUrls[chainId]?.length) return {state: \"pending\"};\n try {\n const receipt = (await rpc(chainId, \"eth_getTransactionReceipt\", [ref.sourceTxHash])) as {status?: string} | null;\n if (!receipt) return {state: \"pending\"};\n return receipt.status === \"0x1\" ? {state: \"completed\", destTxHash: ref.sourceTxHash} : {state: \"failed\"};\n } catch {\n return {state: \"pending\"};\n }\n },\n };\n}\n\n/**\n * The Balancer V2 Vault a swap calls and approves on the chains this provider serves — the app's pins\n * for `remoteProvider({name: \"balancer\", baseUrl, ...balancerPins})` (scan 2026-10-27 modules, F8/F16).\n */\nexport const balancerPins: RemotePins = Object.freeze({\n callTargets: (chain: string): readonly string[] => balancerVaultFor(chain),\n spenders: (chain: string): readonly string[] => balancerVaultFor(chain),\n});\nfunction balancerVaultFor(chain: string): readonly string[] {\n const id = /^eip155:[0-9]+$/.test(chain) ? Number(chain.slice(7)) : NaN;\n return Object.prototype.hasOwnProperty.call(BALANCER_V2_CHAINS, id) ? [BALANCER_V2_VAULT] : [];\n}\n","/**\n * Activity recognition for Balancer v2: `swap` / `batchSwap` on the Vault\n * (one address on every chain) on a chain the module serves → `swap`,\n * \"Swapped A → B\" from the owner's net transfers.\n */\nimport type {ActivityItem, ChainTx, DescribeActivityContext} from \"@anywalletkit/core\";\nimport {chainLabel, describeSwapLike, matchesCall} from \"@anywalletkit/protocol-swap2-core/activity\";\nimport {evmRef, isEvm} from \"@anywalletkit/protocol-swap2-core/util\";\nimport {BALANCER_V2_CHAINS, BALANCER_V2_VAULT} from \"./provider.js\";\n\nexport const BALANCER_MODULE = \"@anywalletkit/protocol-swap2-balancer\";\n\n/** Vault swap entry points (`viem toFunctionSelector`, 2026-10-14). */\nexport const BALANCER_VAULT_SELECTORS: ReadonlySet<string> = new Set([\n \"0x945bcec9\", // batchSwap(uint8,(bytes32,uint256,uint256,uint256,bytes)[],address[],(address,bool,address,bool),int256[],uint256)\n \"0x52bbbe29\", // swap((bytes32,uint8,address,address,uint256,bytes),(address,bool,address,bool),uint256,uint256)\n]);\n\nexport function describeBalancerActivity(tx: ChainTx, ctx: DescribeActivityContext): ActivityItem | null {\n if (!isEvm(tx.chain) || !BALANCER_V2_CHAINS[evmRef(tx.chain)]) return null;\n if (!matchesCall(tx, [BALANCER_V2_VAULT], BALANCER_VAULT_SELECTORS)) return null;\n return describeSwapLike(tx, ctx, {protocol: BALANCER_MODULE, subtitle: `Balancer v2 · ${chainLabel(tx.chain)}`, kind: \"swap\"});\n}\n","import {SingleProviderSwap2} from \"@anywalletkit/protocol-swap2-core/swap2/single\";\nimport type {Swap2Account, Swap2EngineConfig} from \"@anywalletkit/protocol-swap2-core/swap2/base\";\nimport {evmRef, isEvm} from \"@anywalletkit/protocol-swap2-core/util\";\nimport {BALANCER_V2_CHAINS, balancerProvider, type BalancerProviderConfig} from \"./provider.js\";\n\nexport interface BalancerSwap2Config extends Swap2EngineConfig, BalancerProviderConfig {}\n\n/** Balancer v2 as a standalone swap2 protocol (same-chain swaps on one account). */\nexport class BalancerSwap2Protocol extends SingleProviderSwap2 {\n protected readonly provider;\n private readonly _rpcUrls: BalancerProviderConfig[\"rpcUrls\"];\n constructor(account: Swap2Account | undefined, config: BalancerSwap2Config) {\n super(account, config);\n this.provider = balancerProvider(config);\n this._rpcUrls = config.rpcUrls ?? {};\n }\n protected override chainSupported(caip2: string): boolean {\n return isEvm(caip2) && !!BALANCER_V2_CHAINS[evmRef(caip2)] && !!this._rpcUrls[evmRef(caip2)]?.length;\n }\n protected override pairSupported(fromChain: string, toChain: string): boolean {\n return fromChain === toChain && this.chainSupported(fromChain);\n }\n}\n"],"mappings":"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","names":["import_util","import_util"]}
package/dist/index.d.cts CHANGED
@@ -1,4 +1,4 @@
1
- export { BALANCER_V2_CHAINS, BALANCER_V2_VAULT, BalancerProviderConfig, balancerProvider } from './provider.cjs';
1
+ export { BALANCER_MAX_SWAPS, BALANCER_V2_CHAINS, BALANCER_V2_VAULT, BalancerProviderConfig, balancerPins, balancerProvider } from './provider.cjs';
2
2
  export { BalancerSwap2Config, BalancerSwap2Protocol, BalancerSwap2Protocol as default } from './protocol.cjs';
3
3
  export { BALANCER_MODULE, BALANCER_VAULT_SELECTORS, describeBalancerActivity } from './activity.cjs';
4
4
  import '@anywalletkit/protocol-swap2-core/types';
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export { BALANCER_V2_CHAINS, BALANCER_V2_VAULT, BalancerProviderConfig, balancerProvider } from './provider.js';
1
+ export { BALANCER_MAX_SWAPS, BALANCER_V2_CHAINS, BALANCER_V2_VAULT, BalancerProviderConfig, balancerPins, balancerProvider } from './provider.js';
2
2
  export { BalancerSwap2Config, BalancerSwap2Protocol, BalancerSwap2Protocol as default } from './protocol.js';
3
3
  export { BALANCER_MODULE, BALANCER_VAULT_SELECTORS, describeBalancerActivity } from './activity.js';
4
4
  import '@anywalletkit/protocol-swap2-core/types';
package/dist/index.js CHANGED
@@ -1,20 +1,24 @@
1
1
  import {
2
2
  BalancerSwap2Protocol
3
- } from "./chunk-E2XTZEXA.js";
3
+ } from "./chunk-W33N5DLO.js";
4
4
  import {
5
+ BALANCER_MAX_SWAPS,
5
6
  BALANCER_MODULE,
6
7
  BALANCER_V2_CHAINS,
7
8
  BALANCER_V2_VAULT,
8
9
  BALANCER_VAULT_SELECTORS,
10
+ balancerPins,
9
11
  balancerProvider,
10
12
  describeBalancerActivity
11
- } from "./chunk-3YTDA5OS.js";
13
+ } from "./chunk-P4BZP7R4.js";
12
14
  export {
15
+ BALANCER_MAX_SWAPS,
13
16
  BALANCER_MODULE,
14
17
  BALANCER_V2_CHAINS,
15
18
  BALANCER_V2_VAULT,
16
19
  BALANCER_VAULT_SELECTORS,
17
20
  BalancerSwap2Protocol,
21
+ balancerPins,
18
22
  balancerProvider,
19
23
  BalancerSwap2Protocol as default,
20
24
  describeBalancerActivity
package/dist/protocol.cjs CHANGED
@@ -54,6 +54,7 @@ var DEFAULT_API = "https://api-v3.balancer.fi/";
54
54
  var DEFAULT_SLIPPAGE_BPS = 50;
55
55
  var DEFAULT_TTL_MS = 3e5;
56
56
  var DEFAULT_DEADLINE_S = 600;
57
+ var BALANCER_MAX_SWAPS = 16;
57
58
  var BALANCER_V2_CHAINS = Object.freeze({
58
59
  1: { api: "MAINNET", weth: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2" },
59
60
  10: { api: "OPTIMISM", weth: "0x4200000000000000000000000000000000000006" },
@@ -189,6 +190,7 @@ function balancerProvider(config) {
189
190
  swaps.push({ poolId: pool, assetInIndex: indexOf(p.tokens[i].address), assetOutIndex: indexOf(p.tokens[i + 1].address), amount: i === 0 ? BigInt(p.inputAmountRaw) : 0n, userData: "0x" });
190
191
  });
191
192
  }
193
+ if (swaps.length > BALANCER_MAX_SWAPS) return refuse(`the SOR route has ${swaps.length} pool hops, more than ${BALANCER_MAX_SWAPS}`);
192
194
  const inIdx = (0, import_core.bigintToNumber)(indexOf(tokenIn));
193
195
  const outIdx = (0, import_core.bigintToNumber)(indexOf(tokenOut));
194
196
  if (nativeIn || nativeOut) {
@@ -239,14 +241,25 @@ function balancerProvider(config) {
239
241
  return refuse(e instanceof Error ? e.message.replace(/^balancer: /, "") : String(e));
240
242
  }
241
243
  },
242
- /** Re-price the exact signed batch right before broadcast; refuse when the pools now pay under the minimum. */
244
+ /**
245
+ * Re-price the exact signed batch right before broadcast; refuse when the pools now pay under the minimum.
246
+ * Only the quote's own batch is decoded and priced: behind a backend, `tx` is the client's and only the
247
+ * quote is sealed, so a client's batch (any size, any pools) never reaches the decoder or the RPC
248
+ * (scan 2026-10-27 modules, F24).
249
+ */
243
250
  async preflightDeposit(quote, tx) {
244
251
  const chainId = Number(quote.intentId.split(":")[1]);
245
252
  const minOut = quote.providerData?.minOut;
246
253
  if (!BALANCER_V2_CHAINS[chainId] || !minOut || !tx.data) return;
247
- const call = (0, import_viem.decodeFunctionData)({ abi: VAULT_ABI, data: tx.data });
254
+ const quoted = quote.txRequest?.data;
255
+ if (!quoted || tx.data.length !== quoted.length || tx.data.toLowerCase() !== quoted.toLowerCase()) {
256
+ throw new Error("balancer: the transaction is not the quoted batch. Nothing was sent; refresh the quote.");
257
+ }
258
+ const call = (0, import_viem.decodeFunctionData)({ abi: VAULT_ABI, data: quoted });
248
259
  if (call.functionName !== "batchSwap") throw new Error("balancer: the transaction is not a Vault batchSwap");
249
260
  const [, swaps, assets, funds, limits] = call.args;
261
+ if (swaps.length === 0 || swaps.length > BALANCER_MAX_SWAPS) throw new Error(`balancer: the batch has ${swaps.length} pool hops, not 1\u2013${BALANCER_MAX_SWAPS}`);
262
+ if (swaps.some((step) => step.userData !== "0x")) throw new Error("balancer: the batch carries pool userData this module never sends");
250
263
  const outIdx = limits.findIndex((l) => l < 0n);
251
264
  const deltas = await query(chainId, swaps, assets, funds);
252
265
  const out = -deltas[outIdx];
@@ -271,6 +284,14 @@ function balancerProvider(config) {
271
284
  }
272
285
  };
273
286
  }
287
+ var balancerPins = Object.freeze({
288
+ callTargets: (chain) => balancerVaultFor(chain),
289
+ spenders: (chain) => balancerVaultFor(chain)
290
+ });
291
+ function balancerVaultFor(chain) {
292
+ const id = /^eip155:[0-9]+$/.test(chain) ? Number(chain.slice(7)) : NaN;
293
+ return Object.prototype.hasOwnProperty.call(BALANCER_V2_CHAINS, id) ? [BALANCER_V2_VAULT] : [];
294
+ }
274
295
 
275
296
  // src/protocol.ts
276
297
  var BalancerSwap2Protocol = class extends import_single.SingleProviderSwap2 {
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/protocol.ts","../src/provider.ts","../src/activity.ts"],"sourcesContent":["import {SingleProviderSwap2} from \"@anywalletkit/protocol-swap2-core/swap2/single\";\nimport type {Swap2Account, Swap2EngineConfig} from \"@anywalletkit/protocol-swap2-core/swap2/base\";\nimport {evmRef, isEvm} from \"@anywalletkit/protocol-swap2-core/util\";\nimport {BALANCER_V2_CHAINS, balancerProvider, type BalancerProviderConfig} from \"./provider.js\";\n\nexport interface BalancerSwap2Config extends Swap2EngineConfig, BalancerProviderConfig {}\n\n/** Balancer v2 as a standalone swap2 protocol (same-chain swaps on one account). */\nexport class BalancerSwap2Protocol extends SingleProviderSwap2 {\n protected readonly provider;\n private readonly _rpcUrls: BalancerProviderConfig[\"rpcUrls\"];\n constructor(account: Swap2Account | undefined, config: BalancerSwap2Config) {\n super(account, config);\n this.provider = balancerProvider(config);\n this._rpcUrls = config.rpcUrls ?? {};\n }\n protected override chainSupported(caip2: string): boolean {\n return isEvm(caip2) && !!BALANCER_V2_CHAINS[evmRef(caip2)] && !!this._rpcUrls[evmRef(caip2)]?.length;\n }\n protected override pairSupported(fromChain: string, toChain: string): boolean {\n return fromChain === toChain && this.chainSupported(fromChain);\n }\n}\n","import {decodeFunctionData, decodeFunctionResult, encodeFunctionData, getAddress, parseAbi, type Hex} from \"viem\";\nimport {describeBalancerActivity} from \"./activity.js\";\nimport {evmRef, isEvm, timeoutSignal, sourceAssetOf} from \"@anywalletkit/protocol-swap2-core/util\";\nimport type {Asset, CrossChainProvider, IntentRef, IntentStatus, Quote, QuoteParams, UnsignedTx} from \"@anywalletkit/protocol-swap2-core/types\";\nimport {bigintToNumber} from \"@anywalletkit/core\";\n\n/**\n * Balancer v2 — the cp0x permissionless Balancer venue (balancer.cp0x.com).\n *\n * Discovery only from the API: Balancer's SOR (`sorGetSwapPaths`, v2 pools)\n * suggests the path. Everything on the money path is on-chain: the Vault\n * prices that exact path (`queryBatchSwap` via eth_call), the quote uses the\n * Vault's number, and `batchSwap` carries `limits` (in ≤ amountIn, out ≥ min)\n * plus a deadline — the Vault itself refuses anything worse. Native coins use\n * the Vault's own ETH sentinel (address 0): it wraps/unwraps through WETH.\n */\n\nconst ZERO = \"0x0000000000000000000000000000000000000000\";\n/** The same Balancer v2 Vault address on every chain it is deployed on. */\nexport const BALANCER_V2_VAULT = \"0xBA12222222228d8Ba445958a75a0704d566BF2C8\";\nconst DEFAULT_API = \"https://api-v3.balancer.fi/\";\nconst DEFAULT_SLIPPAGE_BPS = 50;\nconst DEFAULT_TTL_MS = 300_000;\nconst DEFAULT_DEADLINE_S = 600;\n\n/** Chains where the v2 Vault is live, with the SOR API name and the Vault's WETH() (verified on-chain 2026-10-13). */\nexport const BALANCER_V2_CHAINS: Readonly<Record<number, {api: string; weth: `0x${string}`}>> = Object.freeze({\n 1: {api: \"MAINNET\", weth: \"0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2\"},\n 10: {api: \"OPTIMISM\", weth: \"0x4200000000000000000000000000000000000006\"},\n 100: {api: \"GNOSIS\", weth: \"0xe91D153E0b41518A2Ce8Dd3D7944Fa863463a97d\"},\n 137: {api: \"POLYGON\", weth: \"0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270\"},\n 146: {api: \"SONIC\", weth: \"0x039e2fB66102314Ce7b64Ce5Ce3E5183bc94aD38\"},\n 8453: {api: \"BASE\", weth: \"0x4200000000000000000000000000000000000006\"},\n 9745: {api: \"PLASMA\", weth: \"0x6100E367285b01F48D07953803A2d8dCA5D19873\"},\n 42161: {api: \"ARBITRUM\", weth: \"0x82aF49447D8a07e3bd95BD0d56f35241523fBab1\"},\n 43114: {api: \"AVALANCHE\", weth: \"0xB31f66AA3C1e785363F0875A1B74E27b85FD66c7\"},\n});\n\nconst SWAP_STEP = \"(bytes32 poolId, uint256 assetInIndex, uint256 assetOutIndex, uint256 amount, bytes userData)\";\nconst FUNDS = \"(address sender, bool fromInternalBalance, address recipient, bool toInternalBalance)\";\nconst VAULT_ABI = parseAbi([\n `function queryBatchSwap(uint8 kind, ${SWAP_STEP}[] swaps, address[] assets, ${FUNDS} funds) returns (int256[] assetDeltas)`,\n `function batchSwap(uint8 kind, ${SWAP_STEP}[] swaps, address[] assets, ${FUNDS} funds, int256[] limits, uint256 deadline) payable returns (int256[] assetDeltas)`,\n \"function WETH() view returns (address)\",\n]);\n\ntype Step = {poolId: Hex; assetInIndex: bigint; assetOutIndex: bigint; amount: bigint; userData: Hex};\ntype Funds = {sender: Hex; fromInternalBalance: boolean; recipient: Hex; toInternalBalance: boolean};\n\nexport interface BalancerProviderConfig {\n enabled?: boolean;\n /** JSON-RPC endpoints per EVM chain id (on-chain pricing + receipts). */\n rpcUrls: Readonly<Record<number, readonly string[]>>;\n /** Balancer API (SOR) base URL — discovery only. */\n apiUrl?: string;\n defaultSlippageBps?: number;\n quoteTtlMs?: number;\n deadlineSeconds?: number;\n}\n\ninterface SorPath {\n pools: string[];\n tokens: {address: string; decimals: number}[];\n inputAmountRaw: string;\n outputAmountRaw: string;\n protocolVersion: number;\n}\n\n/** Base units → the decimal string the SOR expects (\"1.5\"). */\nfunction human(amount: bigint, decimals: number): string {\n const s = amount.toString().padStart(decimals + 1, \"0\");\n const whole = s.slice(0, s.length - decimals);\n const frac = s.slice(s.length - decimals).replace(/0+$/, \"\");\n return frac ? `${whole}.${frac}` : whole;\n}\n\nexport function balancerProvider(config: BalancerProviderConfig): CrossChainProvider {\n const api = config.apiUrl ?? DEFAULT_API;\n const slippageDefault = config.defaultSlippageBps ?? DEFAULT_SLIPPAGE_BPS;\n const ttl = config.quoteTtlMs ?? DEFAULT_TTL_MS;\n const deadlineS = config.deadlineSeconds ?? DEFAULT_DEADLINE_S;\n const verified = new Map<number, Promise<void>>();\n\n const chainIdOf = (caip2: string): number | null => {\n if (!isEvm(caip2)) return null;\n const id = evmRef(caip2);\n return BALANCER_V2_CHAINS[id] && config.rpcUrls[id]?.length ? id : null;\n };\n\n async function rpc(chainId: number, method: string, params: unknown[]): Promise<unknown> {\n let lastErr: unknown;\n for (const url of config.rpcUrls[chainId] ?? []) {\n try {\n const res = await fetch(url, {method: \"POST\", headers: {\"content-type\": \"application/json\"}, body: JSON.stringify({jsonrpc: \"2.0\", id: 1, method, params}), signal: timeoutSignal(12_000)});\n if (!res.ok) throw new Error(`HTTP ${res.status}`);\n const body = (await res.json()) as {result?: unknown; error?: {message?: string}};\n if (body.error) throw Object.assign(new Error(body.error.message ?? \"rpc error\"), {rpc: true});\n return body.result ?? null;\n } catch (e) {\n if ((e as {rpc?: boolean}).rpc) throw e; // a revert is an answer, not a dead endpoint\n lastErr = e;\n }\n }\n throw new Error(`balancer: no RPC answered on chain ${chainId} (${lastErr instanceof Error ? lastErr.message : String(lastErr)})`);\n }\n\n /** The Vault is deployed here and wraps through the WETH we pinned. */\n function verify(chainId: number): Promise<void> {\n let p = verified.get(chainId);\n if (!p) {\n p = (async () => {\n const raw = (await rpc(chainId, \"eth_call\", [{to: BALANCER_V2_VAULT, data: encodeFunctionData({abi: VAULT_ABI, functionName: \"WETH\"})}, \"latest\"])) as Hex;\n const weth = decodeFunctionResult({abi: VAULT_ABI, functionName: \"WETH\", data: raw});\n if (getAddress(weth) !== getAddress(BALANCER_V2_CHAINS[chainId]!.weth)) throw new Error(`balancer: the Vault on chain ${chainId} is not the pinned deployment — refusing`);\n })();\n p.catch(() => verified.delete(chainId));\n verified.set(chainId, p);\n }\n return p;\n }\n\n /** Vault.queryBatchSwap on chain: the asset deltas this exact batch would produce. */\n async function query(chainId: number, swaps: readonly Step[], assets: readonly Hex[], funds: Funds): Promise<readonly bigint[]> {\n const data = encodeFunctionData({abi: VAULT_ABI, functionName: \"queryBatchSwap\", args: [0, swaps, assets, funds]});\n const raw = (await rpc(chainId, \"eth_call\", [{to: BALANCER_V2_VAULT, data}, \"latest\"])) as Hex;\n return decodeFunctionResult({abi: VAULT_ABI, functionName: \"queryBatchSwap\", data: raw});\n }\n\n return {\n name: \"balancer\",\n describeActivity: describeBalancerActivity,\n supportsSameChain: true,\n enabled: () => config.enabled ?? true,\n supportsChain: (caip2: string) => chainIdOf(caip2) !== null,\n supportsChainPair: (from: string, to: string) => from === to && chainIdOf(from) !== null,\n supportsRoute: (from: Asset, to: Asset) => from.chain === to.chain && chainIdOf(from.chain) !== null,\n\n async getQuote(params: QuoteParams, signal?: AbortSignal): Promise<Quote | null> {\n const refuse = (why: string): null => {\n params.onRefusal?.(`balancer: ${why}`);\n return null;\n };\n if (params.flex) return refuse(\"amount-flexible deposits are not supported\");\n if (params.sourceChain !== params.destChain) return refuse(\"same-chain swaps only\");\n const chainId = chainIdOf(params.sourceChain);\n if (chainId === null) return refuse(`no Balancer v2 Vault or RPC for ${params.sourceChain}`);\n const chain = BALANCER_V2_CHAINS[chainId]!;\n const src = sourceAssetOf(params);\n if (!src) return refuse(`unknown source token ${params.sourceToken}`);\n const nativeIn = !src.contract;\n const nativeOut = /^0x0+$/.test(params.destToken);\n if (!nativeOut && !/^0x[0-9a-fA-F]{40}$/.test(params.destToken)) return refuse(\"destination token must be an EVM address\");\n if (!/^0x[0-9a-fA-F]{40}$/.test(params.destAddress)) return refuse(\"recipient must be an EVM address\");\n let amountIn: bigint;\n try {\n amountIn = BigInt(params.amountIn);\n } catch {\n return refuse(\"amount is not an integer\");\n }\n if (amountIn <= 0n) return refuse(\"amount must be positive\");\n const tokenIn = getAddress(nativeIn ? chain.weth : src.contract!);\n const tokenOut = getAddress(nativeOut ? chain.weth : params.destToken);\n if (tokenIn === tokenOut) return refuse(nativeIn || nativeOut ? \"that is a wrap/unwrap, not a swap\" : \"same token in and out\");\n\n try {\n await verify(chainId);\n // 1) Discovery: the SOR's v2 paths (never trusted for price).\n // Values are inlined (this API ignores GraphQL variables); all are validated\n // addresses / a decimal number / a fixed enum, so nothing user-shaped is spliced.\n const query_ = `query { sorGetSwapPaths(chain: ${chain.api}, swapAmount: \"${human(amountIn, src.decimals)}\", swapType: EXACT_IN, tokenIn: \"${tokenIn}\", tokenOut: \"${tokenOut}\", useProtocolVersion: 2) { protocolVersion paths { pools tokens { address decimals } inputAmountRaw outputAmountRaw protocolVersion } } }`;\n const res = await fetch(api, {\n method: \"POST\",\n headers: {\"content-type\": \"application/json\"},\n body: JSON.stringify({query: query_}),\n signal: signal ?? timeoutSignal(12_000),\n });\n if (!res.ok) return refuse(`SOR answered HTTP ${res.status}`);\n const body = (await res.json()) as {data?: {sorGetSwapPaths?: {protocolVersion?: number; paths?: SorPath[]}}; errors?: {message?: string}[]};\n const paths = body.data?.sorGetSwapPaths?.paths ?? [];\n if (body.errors?.length) return refuse(`SOR: ${body.errors[0]?.message?.slice(0, 120) ?? \"error\"}`);\n if (!paths.length || paths.some((p) => p.protocolVersion !== 2 || p.pools.length === 0 || p.tokens.length !== p.pools.length + 1)) {\n return refuse(\"no Balancer v2 pool route for this pair and amount\");\n }\n if (paths.reduce((s, p) => s + BigInt(p.inputAmountRaw), 0n) !== amountIn) return refuse(\"the SOR split does not add up to the amount\");\n\n // 2) Build the batch from the suggested paths.\n const assets: Hex[] = [];\n const indexOf = (a: string) => {\n const addr = getAddress(a);\n let i = assets.findIndex((x) => x === addr);\n if (i < 0) i = assets.push(addr) - 1;\n return BigInt(i);\n };\n const swaps: Step[] = [];\n for (const p of paths) {\n if (getAddress(p.tokens[0]!.address) !== tokenIn || getAddress(p.tokens[p.tokens.length - 1]!.address) !== tokenOut) return refuse(\"the SOR path does not start/end at the requested tokens\");\n p.pools.forEach((pool, i) => {\n if (!/^0x[0-9a-fA-F]{64}$/.test(pool)) throw new Error(\"balancer: malformed pool id from the SOR\");\n swaps.push({poolId: pool as Hex, assetInIndex: indexOf(p.tokens[i]!.address), assetOutIndex: indexOf(p.tokens[i + 1]!.address), amount: i === 0 ? BigInt(p.inputAmountRaw) : 0n, userData: \"0x\"});\n });\n }\n const inIdx = bigintToNumber(indexOf(tokenIn));\n const outIdx = bigintToNumber(indexOf(tokenOut));\n // The Vault's ETH sentinel: native in/out wraps/unwraps through its WETH.\n if (nativeIn || nativeOut) {\n const wethIdx = assets.findIndex((a) => a === getAddress(chain.weth));\n if (paths.some((p) => p.tokens.slice(1, -1).some((t) => getAddress(t.address) === getAddress(chain.weth)))) return refuse(\"this route passes through the wrapped native — pick the wrapped token instead\");\n if (wethIdx >= 0) assets[wethIdx] = ZERO as Hex;\n }\n const sender = getAddress(params.refundAddress && /^0x[0-9a-fA-F]{40}$/.test(params.refundAddress) ? params.refundAddress : params.destAddress);\n const recipient = getAddress(params.destAddress);\n const funds: Funds = {sender, fromInternalBalance: false, recipient, toInternalBalance: false};\n\n // 3) Price it on-chain — this, not the SOR's number, is the quote.\n const deltas = await query(chainId, swaps, assets, funds);\n const paid = deltas[inIdx]!;\n const out = -deltas[outIdx]!;\n if (paid !== amountIn) return refuse(`the Vault would take ${paid}, not ${amountIn}`);\n if (out <= 0n) return refuse(\"the Vault prices this route at zero output\");\n if (deltas.some((d, i) => i !== inIdx && i !== outIdx && d !== 0n)) return refuse(\"the batch leaves an intermediate balance — refusing\");\n\n const bps = params.slippageBps ?? slippageDefault;\n const minOut = (out * BigInt(10_000 - bps)) / 10_000n;\n if (params.minAmountOut !== undefined && minOut < BigInt(params.minAmountOut)) return refuse(`floor breach (min=${minOut} < floor=${params.minAmountOut})`);\n const limits = assets.map((_, i) => (i === inIdx ? amountIn : i === outIdx ? -minOut : 0n));\n const deadline = BigInt(Math.floor(Date.now() / 1000) + deadlineS);\n const data = encodeFunctionData({abi: VAULT_ABI, functionName: \"batchSwap\", args: [0, swaps, assets, funds, limits, deadline]});\n const gasLimit = 160_000n + 95_000n * BigInt(swaps.length);\n return {\n provider: \"balancer\",\n intentId: `balancer:${chainId}`,\n depositAddress: \"\",\n depositMemo: null,\n txRequest: {\n to: BALANCER_V2_VAULT,\n data,\n value: nativeIn ? amountIn.toString() : \"0\",\n chainId,\n gasLimit: gasLimit.toString(),\n ...(nativeIn ? {} : {approvalAddress: BALANCER_V2_VAULT}),\n },\n sourceAmount: amountIn.toString(),\n destAmount: out.toString(),\n minDestAmount: minOut.toString(),\n feeUsd: 0,\n etaSeconds: 15,\n reliability: 0.97,\n validUntilMs: Date.now() + ttl,\n // Pool swap fees are inside the Vault-priced output; no fee is added on top.\n providerData: {paths: String(paths.length), hops: String(swaps.length), minOut: minOut.toString(), poolFee: \"pools\"},\n };\n } catch (e) {\n return refuse(e instanceof Error ? e.message.replace(/^balancer: /, \"\") : String(e));\n }\n },\n\n /** Re-price the exact signed batch right before broadcast; refuse when the pools now pay under the minimum. */\n async preflightDeposit(quote: Quote, tx: UnsignedTx): Promise<void> {\n const chainId = Number(quote.intentId.split(\":\")[1]);\n const minOut = quote.providerData?.minOut;\n if (!BALANCER_V2_CHAINS[chainId] || !minOut || !tx.data) return;\n const call = decodeFunctionData({abi: VAULT_ABI, data: tx.data as Hex});\n if (call.functionName !== \"batchSwap\") throw new Error(\"balancer: the transaction is not a Vault batchSwap\");\n const [, swaps, assets, funds, limits] = call.args;\n const outIdx = limits.findIndex((l) => l < 0n);\n const deltas = await query(chainId, swaps, assets, funds);\n const out = -deltas[outIdx]!;\n if (out < BigInt(minOut)) throw new Error(`balancer: price moved — the pools now pay ${out}, under your minimum ${minOut}. Nothing was sent; refresh the quote.`);\n },\n\n async buildDeposit(quote: Quote): Promise<UnsignedTx> {\n const tx = quote.txRequest;\n if (!tx?.to || !tx.data) throw new Error(\"swap2: Balancer quote has no transaction request\");\n return {chain: \"\", to: tx.to, value: tx.value ?? \"0\", data: tx.data, ...(tx.chainId !== undefined ? {chainId: tx.chainId} : {}), ...(tx.gasLimit ? {gasLimit: tx.gasLimit} : {}), ...(tx.approvalAddress ? {approvalAddress: tx.approvalAddress} : {})};\n },\n\n async trackIntent(ref: IntentRef): Promise<IntentStatus> {\n if (!ref.sourceTxHash) return {state: \"pending\"};\n const chainId = Number(ref.intentId.split(\":\")[1]);\n if (!BALANCER_V2_CHAINS[chainId] || !config.rpcUrls[chainId]?.length) return {state: \"pending\"};\n try {\n const receipt = (await rpc(chainId, \"eth_getTransactionReceipt\", [ref.sourceTxHash])) as {status?: string} | null;\n if (!receipt) return {state: \"pending\"};\n return receipt.status === \"0x1\" ? {state: \"completed\", destTxHash: ref.sourceTxHash} : {state: \"failed\"};\n } catch {\n return {state: \"pending\"};\n }\n },\n };\n}\n","/**\n * Activity recognition for Balancer v2: `swap` / `batchSwap` on the Vault\n * (one address on every chain) on a chain the module serves → `swap`,\n * \"Swapped A → B\" from the owner's net transfers.\n */\nimport type {ActivityItem, ChainTx, DescribeActivityContext} from \"@anywalletkit/core\";\nimport {chainLabel, describeSwapLike, matchesCall} from \"@anywalletkit/protocol-swap2-core/activity\";\nimport {evmRef, isEvm} from \"@anywalletkit/protocol-swap2-core/util\";\nimport {BALANCER_V2_CHAINS, BALANCER_V2_VAULT} from \"./provider.js\";\n\nexport const BALANCER_MODULE = \"@anywalletkit/protocol-swap2-balancer\";\n\n/** Vault swap entry points (`viem toFunctionSelector`, 2026-10-14). */\nexport const BALANCER_VAULT_SELECTORS: ReadonlySet<string> = new Set([\n \"0x945bcec9\", // batchSwap(uint8,(bytes32,uint256,uint256,uint256,bytes)[],address[],(address,bool,address,bool),int256[],uint256)\n \"0x52bbbe29\", // swap((bytes32,uint8,address,address,uint256,bytes),(address,bool,address,bool),uint256,uint256)\n]);\n\nexport function describeBalancerActivity(tx: ChainTx, ctx: DescribeActivityContext): ActivityItem | null {\n if (!isEvm(tx.chain) || !BALANCER_V2_CHAINS[evmRef(tx.chain)]) return null;\n if (!matchesCall(tx, [BALANCER_V2_VAULT], BALANCER_VAULT_SELECTORS)) return null;\n return describeSwapLike(tx, ctx, {protocol: BALANCER_MODULE, subtitle: `Balancer v2 · ${chainLabel(tx.chain)}`, kind: \"swap\"});\n}\n"],"mappings":"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","names":["import_util","import_util"]}
1
+ {"version":3,"sources":["../src/protocol.ts","../src/provider.ts","../src/activity.ts"],"sourcesContent":["import {SingleProviderSwap2} from \"@anywalletkit/protocol-swap2-core/swap2/single\";\nimport type {Swap2Account, Swap2EngineConfig} from \"@anywalletkit/protocol-swap2-core/swap2/base\";\nimport {evmRef, isEvm} from \"@anywalletkit/protocol-swap2-core/util\";\nimport {BALANCER_V2_CHAINS, balancerProvider, type BalancerProviderConfig} from \"./provider.js\";\n\nexport interface BalancerSwap2Config extends Swap2EngineConfig, BalancerProviderConfig {}\n\n/** Balancer v2 as a standalone swap2 protocol (same-chain swaps on one account). */\nexport class BalancerSwap2Protocol extends SingleProviderSwap2 {\n protected readonly provider;\n private readonly _rpcUrls: BalancerProviderConfig[\"rpcUrls\"];\n constructor(account: Swap2Account | undefined, config: BalancerSwap2Config) {\n super(account, config);\n this.provider = balancerProvider(config);\n this._rpcUrls = config.rpcUrls ?? {};\n }\n protected override chainSupported(caip2: string): boolean {\n return isEvm(caip2) && !!BALANCER_V2_CHAINS[evmRef(caip2)] && !!this._rpcUrls[evmRef(caip2)]?.length;\n }\n protected override pairSupported(fromChain: string, toChain: string): boolean {\n return fromChain === toChain && this.chainSupported(fromChain);\n }\n}\n","import type {RemotePins} from \"@anywalletkit/protocol-swap2-core/types\";\nimport {decodeFunctionData, decodeFunctionResult, encodeFunctionData, getAddress, parseAbi, type Hex} from \"viem\";\nimport {describeBalancerActivity} from \"./activity.js\";\nimport {evmRef, isEvm, timeoutSignal, sourceAssetOf} from \"@anywalletkit/protocol-swap2-core/util\";\nimport type {Asset, CrossChainProvider, IntentRef, IntentStatus, Quote, QuoteParams, UnsignedTx} from \"@anywalletkit/protocol-swap2-core/types\";\nimport {bigintToNumber} from \"@anywalletkit/core\";\n\n/**\n * Balancer v2 — the cp0x permissionless Balancer venue (balancer.cp0x.com).\n *\n * Discovery only from the API: Balancer's SOR (`sorGetSwapPaths`, v2 pools)\n * suggests the path. Everything on the money path is on-chain: the Vault\n * prices that exact path (`queryBatchSwap` via eth_call), the quote uses the\n * Vault's number, and `batchSwap` carries `limits` (in ≤ amountIn, out ≥ min)\n * plus a deadline — the Vault itself refuses anything worse. Native coins use\n * the Vault's own ETH sentinel (address 0): it wraps/unwraps through WETH.\n */\n\nconst ZERO = \"0x0000000000000000000000000000000000000000\";\n/** The same Balancer v2 Vault address on every chain it is deployed on. */\nexport const BALANCER_V2_VAULT = \"0xBA12222222228d8Ba445958a75a0704d566BF2C8\";\nconst DEFAULT_API = \"https://api-v3.balancer.fi/\";\nconst DEFAULT_SLIPPAGE_BPS = 50;\nconst DEFAULT_TTL_MS = 300_000;\nconst DEFAULT_DEADLINE_S = 600;\n/** Most pool hops one batch may carry: the SOR's splits are a handful; a batch past this is refused at quote and at preflight. */\nexport const BALANCER_MAX_SWAPS = 16;\n\n/** Chains where the v2 Vault is live, with the SOR API name and the Vault's WETH() (verified on-chain 2026-10-13). */\nexport const BALANCER_V2_CHAINS: Readonly<Record<number, {api: string; weth: `0x${string}`}>> = Object.freeze({\n 1: {api: \"MAINNET\", weth: \"0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2\"},\n 10: {api: \"OPTIMISM\", weth: \"0x4200000000000000000000000000000000000006\"},\n 100: {api: \"GNOSIS\", weth: \"0xe91D153E0b41518A2Ce8Dd3D7944Fa863463a97d\"},\n 137: {api: \"POLYGON\", weth: \"0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270\"},\n 146: {api: \"SONIC\", weth: \"0x039e2fB66102314Ce7b64Ce5Ce3E5183bc94aD38\"},\n 8453: {api: \"BASE\", weth: \"0x4200000000000000000000000000000000000006\"},\n 9745: {api: \"PLASMA\", weth: \"0x6100E367285b01F48D07953803A2d8dCA5D19873\"},\n 42161: {api: \"ARBITRUM\", weth: \"0x82aF49447D8a07e3bd95BD0d56f35241523fBab1\"},\n 43114: {api: \"AVALANCHE\", weth: \"0xB31f66AA3C1e785363F0875A1B74E27b85FD66c7\"},\n});\n\nconst SWAP_STEP = \"(bytes32 poolId, uint256 assetInIndex, uint256 assetOutIndex, uint256 amount, bytes userData)\";\nconst FUNDS = \"(address sender, bool fromInternalBalance, address recipient, bool toInternalBalance)\";\nconst VAULT_ABI = parseAbi([\n `function queryBatchSwap(uint8 kind, ${SWAP_STEP}[] swaps, address[] assets, ${FUNDS} funds) returns (int256[] assetDeltas)`,\n `function batchSwap(uint8 kind, ${SWAP_STEP}[] swaps, address[] assets, ${FUNDS} funds, int256[] limits, uint256 deadline) payable returns (int256[] assetDeltas)`,\n \"function WETH() view returns (address)\",\n]);\n\ntype Step = {poolId: Hex; assetInIndex: bigint; assetOutIndex: bigint; amount: bigint; userData: Hex};\ntype Funds = {sender: Hex; fromInternalBalance: boolean; recipient: Hex; toInternalBalance: boolean};\n\nexport interface BalancerProviderConfig {\n enabled?: boolean;\n /** JSON-RPC endpoints per EVM chain id (on-chain pricing + receipts). */\n rpcUrls: Readonly<Record<number, readonly string[]>>;\n /** Balancer API (SOR) base URL — discovery only. */\n apiUrl?: string;\n defaultSlippageBps?: number;\n quoteTtlMs?: number;\n deadlineSeconds?: number;\n}\n\ninterface SorPath {\n pools: string[];\n tokens: {address: string; decimals: number}[];\n inputAmountRaw: string;\n outputAmountRaw: string;\n protocolVersion: number;\n}\n\n/** Base units → the decimal string the SOR expects (\"1.5\"). */\nfunction human(amount: bigint, decimals: number): string {\n const s = amount.toString().padStart(decimals + 1, \"0\");\n const whole = s.slice(0, s.length - decimals);\n const frac = s.slice(s.length - decimals).replace(/0+$/, \"\");\n return frac ? `${whole}.${frac}` : whole;\n}\n\nexport function balancerProvider(config: BalancerProviderConfig): CrossChainProvider {\n const api = config.apiUrl ?? DEFAULT_API;\n const slippageDefault = config.defaultSlippageBps ?? DEFAULT_SLIPPAGE_BPS;\n const ttl = config.quoteTtlMs ?? DEFAULT_TTL_MS;\n const deadlineS = config.deadlineSeconds ?? DEFAULT_DEADLINE_S;\n const verified = new Map<number, Promise<void>>();\n\n const chainIdOf = (caip2: string): number | null => {\n if (!isEvm(caip2)) return null;\n const id = evmRef(caip2);\n return BALANCER_V2_CHAINS[id] && config.rpcUrls[id]?.length ? id : null;\n };\n\n async function rpc(chainId: number, method: string, params: unknown[]): Promise<unknown> {\n let lastErr: unknown;\n for (const url of config.rpcUrls[chainId] ?? []) {\n try {\n const res = await fetch(url, {method: \"POST\", headers: {\"content-type\": \"application/json\"}, body: JSON.stringify({jsonrpc: \"2.0\", id: 1, method, params}), signal: timeoutSignal(12_000)});\n if (!res.ok) throw new Error(`HTTP ${res.status}`);\n const body = (await res.json()) as {result?: unknown; error?: {message?: string}};\n if (body.error) throw Object.assign(new Error(body.error.message ?? \"rpc error\"), {rpc: true});\n return body.result ?? null;\n } catch (e) {\n if ((e as {rpc?: boolean}).rpc) throw e; // a revert is an answer, not a dead endpoint\n lastErr = e;\n }\n }\n throw new Error(`balancer: no RPC answered on chain ${chainId} (${lastErr instanceof Error ? lastErr.message : String(lastErr)})`);\n }\n\n /** The Vault is deployed here and wraps through the WETH we pinned. */\n function verify(chainId: number): Promise<void> {\n let p = verified.get(chainId);\n if (!p) {\n p = (async () => {\n const raw = (await rpc(chainId, \"eth_call\", [{to: BALANCER_V2_VAULT, data: encodeFunctionData({abi: VAULT_ABI, functionName: \"WETH\"})}, \"latest\"])) as Hex;\n const weth = decodeFunctionResult({abi: VAULT_ABI, functionName: \"WETH\", data: raw});\n if (getAddress(weth) !== getAddress(BALANCER_V2_CHAINS[chainId]!.weth)) throw new Error(`balancer: the Vault on chain ${chainId} is not the pinned deployment — refusing`);\n })();\n p.catch(() => verified.delete(chainId));\n verified.set(chainId, p);\n }\n return p;\n }\n\n /** Vault.queryBatchSwap on chain: the asset deltas this exact batch would produce. */\n async function query(chainId: number, swaps: readonly Step[], assets: readonly Hex[], funds: Funds): Promise<readonly bigint[]> {\n const data = encodeFunctionData({abi: VAULT_ABI, functionName: \"queryBatchSwap\", args: [0, swaps, assets, funds]});\n const raw = (await rpc(chainId, \"eth_call\", [{to: BALANCER_V2_VAULT, data}, \"latest\"])) as Hex;\n return decodeFunctionResult({abi: VAULT_ABI, functionName: \"queryBatchSwap\", data: raw});\n }\n\n return {\n name: \"balancer\",\n describeActivity: describeBalancerActivity,\n supportsSameChain: true,\n enabled: () => config.enabled ?? true,\n supportsChain: (caip2: string) => chainIdOf(caip2) !== null,\n supportsChainPair: (from: string, to: string) => from === to && chainIdOf(from) !== null,\n supportsRoute: (from: Asset, to: Asset) => from.chain === to.chain && chainIdOf(from.chain) !== null,\n\n async getQuote(params: QuoteParams, signal?: AbortSignal): Promise<Quote | null> {\n const refuse = (why: string): null => {\n params.onRefusal?.(`balancer: ${why}`);\n return null;\n };\n if (params.flex) return refuse(\"amount-flexible deposits are not supported\");\n if (params.sourceChain !== params.destChain) return refuse(\"same-chain swaps only\");\n const chainId = chainIdOf(params.sourceChain);\n if (chainId === null) return refuse(`no Balancer v2 Vault or RPC for ${params.sourceChain}`);\n const chain = BALANCER_V2_CHAINS[chainId]!;\n const src = sourceAssetOf(params);\n if (!src) return refuse(`unknown source token ${params.sourceToken}`);\n const nativeIn = !src.contract;\n const nativeOut = /^0x0+$/.test(params.destToken);\n if (!nativeOut && !/^0x[0-9a-fA-F]{40}$/.test(params.destToken)) return refuse(\"destination token must be an EVM address\");\n if (!/^0x[0-9a-fA-F]{40}$/.test(params.destAddress)) return refuse(\"recipient must be an EVM address\");\n let amountIn: bigint;\n try {\n amountIn = BigInt(params.amountIn);\n } catch {\n return refuse(\"amount is not an integer\");\n }\n if (amountIn <= 0n) return refuse(\"amount must be positive\");\n const tokenIn = getAddress(nativeIn ? chain.weth : src.contract!);\n const tokenOut = getAddress(nativeOut ? chain.weth : params.destToken);\n if (tokenIn === tokenOut) return refuse(nativeIn || nativeOut ? \"that is a wrap/unwrap, not a swap\" : \"same token in and out\");\n\n try {\n await verify(chainId);\n // 1) Discovery: the SOR's v2 paths (never trusted for price).\n // Values are inlined (this API ignores GraphQL variables); all are validated\n // addresses / a decimal number / a fixed enum, so nothing user-shaped is spliced.\n const query_ = `query { sorGetSwapPaths(chain: ${chain.api}, swapAmount: \"${human(amountIn, src.decimals)}\", swapType: EXACT_IN, tokenIn: \"${tokenIn}\", tokenOut: \"${tokenOut}\", useProtocolVersion: 2) { protocolVersion paths { pools tokens { address decimals } inputAmountRaw outputAmountRaw protocolVersion } } }`;\n const res = await fetch(api, {\n method: \"POST\",\n headers: {\"content-type\": \"application/json\"},\n body: JSON.stringify({query: query_}),\n signal: signal ?? timeoutSignal(12_000),\n });\n if (!res.ok) return refuse(`SOR answered HTTP ${res.status}`);\n const body = (await res.json()) as {data?: {sorGetSwapPaths?: {protocolVersion?: number; paths?: SorPath[]}}; errors?: {message?: string}[]};\n const paths = body.data?.sorGetSwapPaths?.paths ?? [];\n if (body.errors?.length) return refuse(`SOR: ${body.errors[0]?.message?.slice(0, 120) ?? \"error\"}`);\n if (!paths.length || paths.some((p) => p.protocolVersion !== 2 || p.pools.length === 0 || p.tokens.length !== p.pools.length + 1)) {\n return refuse(\"no Balancer v2 pool route for this pair and amount\");\n }\n if (paths.reduce((s, p) => s + BigInt(p.inputAmountRaw), 0n) !== amountIn) return refuse(\"the SOR split does not add up to the amount\");\n\n // 2) Build the batch from the suggested paths.\n const assets: Hex[] = [];\n const indexOf = (a: string) => {\n const addr = getAddress(a);\n let i = assets.findIndex((x) => x === addr);\n if (i < 0) i = assets.push(addr) - 1;\n return BigInt(i);\n };\n const swaps: Step[] = [];\n for (const p of paths) {\n if (getAddress(p.tokens[0]!.address) !== tokenIn || getAddress(p.tokens[p.tokens.length - 1]!.address) !== tokenOut) return refuse(\"the SOR path does not start/end at the requested tokens\");\n p.pools.forEach((pool, i) => {\n if (!/^0x[0-9a-fA-F]{64}$/.test(pool)) throw new Error(\"balancer: malformed pool id from the SOR\");\n swaps.push({poolId: pool as Hex, assetInIndex: indexOf(p.tokens[i]!.address), assetOutIndex: indexOf(p.tokens[i + 1]!.address), amount: i === 0 ? BigInt(p.inputAmountRaw) : 0n, userData: \"0x\"});\n });\n }\n if (swaps.length > BALANCER_MAX_SWAPS) return refuse(`the SOR route has ${swaps.length} pool hops, more than ${BALANCER_MAX_SWAPS}`);\n const inIdx = bigintToNumber(indexOf(tokenIn));\n const outIdx = bigintToNumber(indexOf(tokenOut));\n // The Vault's ETH sentinel: native in/out wraps/unwraps through its WETH.\n if (nativeIn || nativeOut) {\n const wethIdx = assets.findIndex((a) => a === getAddress(chain.weth));\n if (paths.some((p) => p.tokens.slice(1, -1).some((t) => getAddress(t.address) === getAddress(chain.weth)))) return refuse(\"this route passes through the wrapped native — pick the wrapped token instead\");\n if (wethIdx >= 0) assets[wethIdx] = ZERO as Hex;\n }\n const sender = getAddress(params.refundAddress && /^0x[0-9a-fA-F]{40}$/.test(params.refundAddress) ? params.refundAddress : params.destAddress);\n const recipient = getAddress(params.destAddress);\n const funds: Funds = {sender, fromInternalBalance: false, recipient, toInternalBalance: false};\n\n // 3) Price it on-chain — this, not the SOR's number, is the quote.\n const deltas = await query(chainId, swaps, assets, funds);\n const paid = deltas[inIdx]!;\n const out = -deltas[outIdx]!;\n if (paid !== amountIn) return refuse(`the Vault would take ${paid}, not ${amountIn}`);\n if (out <= 0n) return refuse(\"the Vault prices this route at zero output\");\n if (deltas.some((d, i) => i !== inIdx && i !== outIdx && d !== 0n)) return refuse(\"the batch leaves an intermediate balance — refusing\");\n\n const bps = params.slippageBps ?? slippageDefault;\n const minOut = (out * BigInt(10_000 - bps)) / 10_000n;\n if (params.minAmountOut !== undefined && minOut < BigInt(params.minAmountOut)) return refuse(`floor breach (min=${minOut} < floor=${params.minAmountOut})`);\n const limits = assets.map((_, i) => (i === inIdx ? amountIn : i === outIdx ? -minOut : 0n));\n const deadline = BigInt(Math.floor(Date.now() / 1000) + deadlineS);\n const data = encodeFunctionData({abi: VAULT_ABI, functionName: \"batchSwap\", args: [0, swaps, assets, funds, limits, deadline]});\n const gasLimit = 160_000n + 95_000n * BigInt(swaps.length);\n return {\n provider: \"balancer\",\n intentId: `balancer:${chainId}`,\n depositAddress: \"\",\n depositMemo: null,\n txRequest: {\n to: BALANCER_V2_VAULT,\n data,\n value: nativeIn ? amountIn.toString() : \"0\",\n chainId,\n gasLimit: gasLimit.toString(),\n ...(nativeIn ? {} : {approvalAddress: BALANCER_V2_VAULT}),\n },\n sourceAmount: amountIn.toString(),\n destAmount: out.toString(),\n minDestAmount: minOut.toString(),\n feeUsd: 0,\n etaSeconds: 15,\n reliability: 0.97,\n validUntilMs: Date.now() + ttl,\n // Pool swap fees are inside the Vault-priced output; no fee is added on top.\n providerData: {paths: String(paths.length), hops: String(swaps.length), minOut: minOut.toString(), poolFee: \"pools\"},\n };\n } catch (e) {\n return refuse(e instanceof Error ? e.message.replace(/^balancer: /, \"\") : String(e));\n }\n },\n\n /**\n * Re-price the exact signed batch right before broadcast; refuse when the pools now pay under the minimum.\n * Only the quote's own batch is decoded and priced: behind a backend, `tx` is the client's and only the\n * quote is sealed, so a client's batch (any size, any pools) never reaches the decoder or the RPC\n * (scan 2026-10-27 modules, F24).\n */\n async preflightDeposit(quote: Quote, tx: UnsignedTx): Promise<void> {\n const chainId = Number(quote.intentId.split(\":\")[1]);\n const minOut = quote.providerData?.minOut;\n if (!BALANCER_V2_CHAINS[chainId] || !minOut || !tx.data) return;\n const quoted = quote.txRequest?.data;\n if (!quoted || tx.data.length !== quoted.length || tx.data.toLowerCase() !== quoted.toLowerCase()) {\n throw new Error(\"balancer: the transaction is not the quoted batch. Nothing was sent; refresh the quote.\");\n }\n const call = decodeFunctionData({abi: VAULT_ABI, data: quoted as Hex});\n if (call.functionName !== \"batchSwap\") throw new Error(\"balancer: the transaction is not a Vault batchSwap\");\n const [, swaps, assets, funds, limits] = call.args;\n if (swaps.length === 0 || swaps.length > BALANCER_MAX_SWAPS) throw new Error(`balancer: the batch has ${swaps.length} pool hops, not 1–${BALANCER_MAX_SWAPS}`);\n if (swaps.some((step) => step.userData !== \"0x\")) throw new Error(\"balancer: the batch carries pool userData this module never sends\");\n const outIdx = limits.findIndex((l) => l < 0n);\n const deltas = await query(chainId, swaps, assets, funds);\n const out = -deltas[outIdx]!;\n if (out < BigInt(minOut)) throw new Error(`balancer: price moved — the pools now pay ${out}, under your minimum ${minOut}. Nothing was sent; refresh the quote.`);\n },\n\n async buildDeposit(quote: Quote): Promise<UnsignedTx> {\n const tx = quote.txRequest;\n if (!tx?.to || !tx.data) throw new Error(\"swap2: Balancer quote has no transaction request\");\n return {chain: \"\", to: tx.to, value: tx.value ?? \"0\", data: tx.data, ...(tx.chainId !== undefined ? {chainId: tx.chainId} : {}), ...(tx.gasLimit ? {gasLimit: tx.gasLimit} : {}), ...(tx.approvalAddress ? {approvalAddress: tx.approvalAddress} : {})};\n },\n\n async trackIntent(ref: IntentRef): Promise<IntentStatus> {\n if (!ref.sourceTxHash) return {state: \"pending\"};\n const chainId = Number(ref.intentId.split(\":\")[1]);\n if (!BALANCER_V2_CHAINS[chainId] || !config.rpcUrls[chainId]?.length) return {state: \"pending\"};\n try {\n const receipt = (await rpc(chainId, \"eth_getTransactionReceipt\", [ref.sourceTxHash])) as {status?: string} | null;\n if (!receipt) return {state: \"pending\"};\n return receipt.status === \"0x1\" ? {state: \"completed\", destTxHash: ref.sourceTxHash} : {state: \"failed\"};\n } catch {\n return {state: \"pending\"};\n }\n },\n };\n}\n\n/**\n * The Balancer V2 Vault a swap calls and approves on the chains this provider serves — the app's pins\n * for `remoteProvider({name: \"balancer\", baseUrl, ...balancerPins})` (scan 2026-10-27 modules, F8/F16).\n */\nexport const balancerPins: RemotePins = Object.freeze({\n callTargets: (chain: string): readonly string[] => balancerVaultFor(chain),\n spenders: (chain: string): readonly string[] => balancerVaultFor(chain),\n});\nfunction balancerVaultFor(chain: string): readonly string[] {\n const id = /^eip155:[0-9]+$/.test(chain) ? Number(chain.slice(7)) : NaN;\n return Object.prototype.hasOwnProperty.call(BALANCER_V2_CHAINS, id) ? [BALANCER_V2_VAULT] : [];\n}\n","/**\n * Activity recognition for Balancer v2: `swap` / `batchSwap` on the Vault\n * (one address on every chain) on a chain the module serves → `swap`,\n * \"Swapped A → B\" from the owner's net transfers.\n */\nimport type {ActivityItem, ChainTx, DescribeActivityContext} from \"@anywalletkit/core\";\nimport {chainLabel, describeSwapLike, matchesCall} from \"@anywalletkit/protocol-swap2-core/activity\";\nimport {evmRef, isEvm} from \"@anywalletkit/protocol-swap2-core/util\";\nimport {BALANCER_V2_CHAINS, BALANCER_V2_VAULT} from \"./provider.js\";\n\nexport const BALANCER_MODULE = \"@anywalletkit/protocol-swap2-balancer\";\n\n/** Vault swap entry points (`viem toFunctionSelector`, 2026-10-14). */\nexport const BALANCER_VAULT_SELECTORS: ReadonlySet<string> = new Set([\n \"0x945bcec9\", // batchSwap(uint8,(bytes32,uint256,uint256,uint256,bytes)[],address[],(address,bool,address,bool),int256[],uint256)\n \"0x52bbbe29\", // swap((bytes32,uint8,address,address,uint256,bytes),(address,bool,address,bool),uint256,uint256)\n]);\n\nexport function describeBalancerActivity(tx: ChainTx, ctx: DescribeActivityContext): ActivityItem | null {\n if (!isEvm(tx.chain) || !BALANCER_V2_CHAINS[evmRef(tx.chain)]) return null;\n if (!matchesCall(tx, [BALANCER_V2_VAULT], BALANCER_VAULT_SELECTORS)) return null;\n return describeSwapLike(tx, ctx, {protocol: BALANCER_MODULE, subtitle: `Balancer v2 · ${chainLabel(tx.chain)}`, kind: \"swap\"});\n}\n"],"mappings":"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","names":["import_util","import_util"]}
package/dist/protocol.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  BalancerSwap2Protocol
3
- } from "./chunk-E2XTZEXA.js";
4
- import "./chunk-3YTDA5OS.js";
3
+ } from "./chunk-W33N5DLO.js";
4
+ import "./chunk-P4BZP7R4.js";
5
5
  export {
6
6
  BalancerSwap2Protocol
7
7
  };
package/dist/provider.cjs CHANGED
@@ -20,8 +20,10 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  // src/provider.ts
21
21
  var provider_exports = {};
22
22
  __export(provider_exports, {
23
+ BALANCER_MAX_SWAPS: () => BALANCER_MAX_SWAPS,
23
24
  BALANCER_V2_CHAINS: () => BALANCER_V2_CHAINS,
24
25
  BALANCER_V2_VAULT: () => BALANCER_V2_VAULT,
26
+ balancerPins: () => balancerPins,
25
27
  balancerProvider: () => balancerProvider
26
28
  });
27
29
  module.exports = __toCommonJS(provider_exports);
@@ -52,6 +54,7 @@ var DEFAULT_API = "https://api-v3.balancer.fi/";
52
54
  var DEFAULT_SLIPPAGE_BPS = 50;
53
55
  var DEFAULT_TTL_MS = 3e5;
54
56
  var DEFAULT_DEADLINE_S = 600;
57
+ var BALANCER_MAX_SWAPS = 16;
55
58
  var BALANCER_V2_CHAINS = Object.freeze({
56
59
  1: { api: "MAINNET", weth: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2" },
57
60
  10: { api: "OPTIMISM", weth: "0x4200000000000000000000000000000000000006" },
@@ -187,6 +190,7 @@ function balancerProvider(config) {
187
190
  swaps.push({ poolId: pool, assetInIndex: indexOf(p.tokens[i].address), assetOutIndex: indexOf(p.tokens[i + 1].address), amount: i === 0 ? BigInt(p.inputAmountRaw) : 0n, userData: "0x" });
188
191
  });
189
192
  }
193
+ if (swaps.length > BALANCER_MAX_SWAPS) return refuse(`the SOR route has ${swaps.length} pool hops, more than ${BALANCER_MAX_SWAPS}`);
190
194
  const inIdx = (0, import_core.bigintToNumber)(indexOf(tokenIn));
191
195
  const outIdx = (0, import_core.bigintToNumber)(indexOf(tokenOut));
192
196
  if (nativeIn || nativeOut) {
@@ -237,14 +241,25 @@ function balancerProvider(config) {
237
241
  return refuse(e instanceof Error ? e.message.replace(/^balancer: /, "") : String(e));
238
242
  }
239
243
  },
240
- /** Re-price the exact signed batch right before broadcast; refuse when the pools now pay under the minimum. */
244
+ /**
245
+ * Re-price the exact signed batch right before broadcast; refuse when the pools now pay under the minimum.
246
+ * Only the quote's own batch is decoded and priced: behind a backend, `tx` is the client's and only the
247
+ * quote is sealed, so a client's batch (any size, any pools) never reaches the decoder or the RPC
248
+ * (scan 2026-10-27 modules, F24).
249
+ */
241
250
  async preflightDeposit(quote, tx) {
242
251
  const chainId = Number(quote.intentId.split(":")[1]);
243
252
  const minOut = quote.providerData?.minOut;
244
253
  if (!BALANCER_V2_CHAINS[chainId] || !minOut || !tx.data) return;
245
- const call = (0, import_viem.decodeFunctionData)({ abi: VAULT_ABI, data: tx.data });
254
+ const quoted = quote.txRequest?.data;
255
+ if (!quoted || tx.data.length !== quoted.length || tx.data.toLowerCase() !== quoted.toLowerCase()) {
256
+ throw new Error("balancer: the transaction is not the quoted batch. Nothing was sent; refresh the quote.");
257
+ }
258
+ const call = (0, import_viem.decodeFunctionData)({ abi: VAULT_ABI, data: quoted });
246
259
  if (call.functionName !== "batchSwap") throw new Error("balancer: the transaction is not a Vault batchSwap");
247
260
  const [, swaps, assets, funds, limits] = call.args;
261
+ if (swaps.length === 0 || swaps.length > BALANCER_MAX_SWAPS) throw new Error(`balancer: the batch has ${swaps.length} pool hops, not 1\u2013${BALANCER_MAX_SWAPS}`);
262
+ if (swaps.some((step) => step.userData !== "0x")) throw new Error("balancer: the batch carries pool userData this module never sends");
248
263
  const outIdx = limits.findIndex((l) => l < 0n);
249
264
  const deltas = await query(chainId, swaps, assets, funds);
250
265
  const out = -deltas[outIdx];
@@ -269,4 +284,12 @@ function balancerProvider(config) {
269
284
  }
270
285
  };
271
286
  }
287
+ var balancerPins = Object.freeze({
288
+ callTargets: (chain) => balancerVaultFor(chain),
289
+ spenders: (chain) => balancerVaultFor(chain)
290
+ });
291
+ function balancerVaultFor(chain) {
292
+ const id = /^eip155:[0-9]+$/.test(chain) ? Number(chain.slice(7)) : NaN;
293
+ return Object.prototype.hasOwnProperty.call(BALANCER_V2_CHAINS, id) ? [BALANCER_V2_VAULT] : [];
294
+ }
272
295
  //# sourceMappingURL=provider.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/provider.ts","../src/activity.ts"],"sourcesContent":["import {decodeFunctionData, decodeFunctionResult, encodeFunctionData, getAddress, parseAbi, type Hex} from \"viem\";\nimport {describeBalancerActivity} from \"./activity.js\";\nimport {evmRef, isEvm, timeoutSignal, sourceAssetOf} from \"@anywalletkit/protocol-swap2-core/util\";\nimport type {Asset, CrossChainProvider, IntentRef, IntentStatus, Quote, QuoteParams, UnsignedTx} from \"@anywalletkit/protocol-swap2-core/types\";\nimport {bigintToNumber} from \"@anywalletkit/core\";\n\n/**\n * Balancer v2 — the cp0x permissionless Balancer venue (balancer.cp0x.com).\n *\n * Discovery only from the API: Balancer's SOR (`sorGetSwapPaths`, v2 pools)\n * suggests the path. Everything on the money path is on-chain: the Vault\n * prices that exact path (`queryBatchSwap` via eth_call), the quote uses the\n * Vault's number, and `batchSwap` carries `limits` (in ≤ amountIn, out ≥ min)\n * plus a deadline — the Vault itself refuses anything worse. Native coins use\n * the Vault's own ETH sentinel (address 0): it wraps/unwraps through WETH.\n */\n\nconst ZERO = \"0x0000000000000000000000000000000000000000\";\n/** The same Balancer v2 Vault address on every chain it is deployed on. */\nexport const BALANCER_V2_VAULT = \"0xBA12222222228d8Ba445958a75a0704d566BF2C8\";\nconst DEFAULT_API = \"https://api-v3.balancer.fi/\";\nconst DEFAULT_SLIPPAGE_BPS = 50;\nconst DEFAULT_TTL_MS = 300_000;\nconst DEFAULT_DEADLINE_S = 600;\n\n/** Chains where the v2 Vault is live, with the SOR API name and the Vault's WETH() (verified on-chain 2026-10-13). */\nexport const BALANCER_V2_CHAINS: Readonly<Record<number, {api: string; weth: `0x${string}`}>> = Object.freeze({\n 1: {api: \"MAINNET\", weth: \"0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2\"},\n 10: {api: \"OPTIMISM\", weth: \"0x4200000000000000000000000000000000000006\"},\n 100: {api: \"GNOSIS\", weth: \"0xe91D153E0b41518A2Ce8Dd3D7944Fa863463a97d\"},\n 137: {api: \"POLYGON\", weth: \"0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270\"},\n 146: {api: \"SONIC\", weth: \"0x039e2fB66102314Ce7b64Ce5Ce3E5183bc94aD38\"},\n 8453: {api: \"BASE\", weth: \"0x4200000000000000000000000000000000000006\"},\n 9745: {api: \"PLASMA\", weth: \"0x6100E367285b01F48D07953803A2d8dCA5D19873\"},\n 42161: {api: \"ARBITRUM\", weth: \"0x82aF49447D8a07e3bd95BD0d56f35241523fBab1\"},\n 43114: {api: \"AVALANCHE\", weth: \"0xB31f66AA3C1e785363F0875A1B74E27b85FD66c7\"},\n});\n\nconst SWAP_STEP = \"(bytes32 poolId, uint256 assetInIndex, uint256 assetOutIndex, uint256 amount, bytes userData)\";\nconst FUNDS = \"(address sender, bool fromInternalBalance, address recipient, bool toInternalBalance)\";\nconst VAULT_ABI = parseAbi([\n `function queryBatchSwap(uint8 kind, ${SWAP_STEP}[] swaps, address[] assets, ${FUNDS} funds) returns (int256[] assetDeltas)`,\n `function batchSwap(uint8 kind, ${SWAP_STEP}[] swaps, address[] assets, ${FUNDS} funds, int256[] limits, uint256 deadline) payable returns (int256[] assetDeltas)`,\n \"function WETH() view returns (address)\",\n]);\n\ntype Step = {poolId: Hex; assetInIndex: bigint; assetOutIndex: bigint; amount: bigint; userData: Hex};\ntype Funds = {sender: Hex; fromInternalBalance: boolean; recipient: Hex; toInternalBalance: boolean};\n\nexport interface BalancerProviderConfig {\n enabled?: boolean;\n /** JSON-RPC endpoints per EVM chain id (on-chain pricing + receipts). */\n rpcUrls: Readonly<Record<number, readonly string[]>>;\n /** Balancer API (SOR) base URL — discovery only. */\n apiUrl?: string;\n defaultSlippageBps?: number;\n quoteTtlMs?: number;\n deadlineSeconds?: number;\n}\n\ninterface SorPath {\n pools: string[];\n tokens: {address: string; decimals: number}[];\n inputAmountRaw: string;\n outputAmountRaw: string;\n protocolVersion: number;\n}\n\n/** Base units → the decimal string the SOR expects (\"1.5\"). */\nfunction human(amount: bigint, decimals: number): string {\n const s = amount.toString().padStart(decimals + 1, \"0\");\n const whole = s.slice(0, s.length - decimals);\n const frac = s.slice(s.length - decimals).replace(/0+$/, \"\");\n return frac ? `${whole}.${frac}` : whole;\n}\n\nexport function balancerProvider(config: BalancerProviderConfig): CrossChainProvider {\n const api = config.apiUrl ?? DEFAULT_API;\n const slippageDefault = config.defaultSlippageBps ?? DEFAULT_SLIPPAGE_BPS;\n const ttl = config.quoteTtlMs ?? DEFAULT_TTL_MS;\n const deadlineS = config.deadlineSeconds ?? DEFAULT_DEADLINE_S;\n const verified = new Map<number, Promise<void>>();\n\n const chainIdOf = (caip2: string): number | null => {\n if (!isEvm(caip2)) return null;\n const id = evmRef(caip2);\n return BALANCER_V2_CHAINS[id] && config.rpcUrls[id]?.length ? id : null;\n };\n\n async function rpc(chainId: number, method: string, params: unknown[]): Promise<unknown> {\n let lastErr: unknown;\n for (const url of config.rpcUrls[chainId] ?? []) {\n try {\n const res = await fetch(url, {method: \"POST\", headers: {\"content-type\": \"application/json\"}, body: JSON.stringify({jsonrpc: \"2.0\", id: 1, method, params}), signal: timeoutSignal(12_000)});\n if (!res.ok) throw new Error(`HTTP ${res.status}`);\n const body = (await res.json()) as {result?: unknown; error?: {message?: string}};\n if (body.error) throw Object.assign(new Error(body.error.message ?? \"rpc error\"), {rpc: true});\n return body.result ?? null;\n } catch (e) {\n if ((e as {rpc?: boolean}).rpc) throw e; // a revert is an answer, not a dead endpoint\n lastErr = e;\n }\n }\n throw new Error(`balancer: no RPC answered on chain ${chainId} (${lastErr instanceof Error ? lastErr.message : String(lastErr)})`);\n }\n\n /** The Vault is deployed here and wraps through the WETH we pinned. */\n function verify(chainId: number): Promise<void> {\n let p = verified.get(chainId);\n if (!p) {\n p = (async () => {\n const raw = (await rpc(chainId, \"eth_call\", [{to: BALANCER_V2_VAULT, data: encodeFunctionData({abi: VAULT_ABI, functionName: \"WETH\"})}, \"latest\"])) as Hex;\n const weth = decodeFunctionResult({abi: VAULT_ABI, functionName: \"WETH\", data: raw});\n if (getAddress(weth) !== getAddress(BALANCER_V2_CHAINS[chainId]!.weth)) throw new Error(`balancer: the Vault on chain ${chainId} is not the pinned deployment — refusing`);\n })();\n p.catch(() => verified.delete(chainId));\n verified.set(chainId, p);\n }\n return p;\n }\n\n /** Vault.queryBatchSwap on chain: the asset deltas this exact batch would produce. */\n async function query(chainId: number, swaps: readonly Step[], assets: readonly Hex[], funds: Funds): Promise<readonly bigint[]> {\n const data = encodeFunctionData({abi: VAULT_ABI, functionName: \"queryBatchSwap\", args: [0, swaps, assets, funds]});\n const raw = (await rpc(chainId, \"eth_call\", [{to: BALANCER_V2_VAULT, data}, \"latest\"])) as Hex;\n return decodeFunctionResult({abi: VAULT_ABI, functionName: \"queryBatchSwap\", data: raw});\n }\n\n return {\n name: \"balancer\",\n describeActivity: describeBalancerActivity,\n supportsSameChain: true,\n enabled: () => config.enabled ?? true,\n supportsChain: (caip2: string) => chainIdOf(caip2) !== null,\n supportsChainPair: (from: string, to: string) => from === to && chainIdOf(from) !== null,\n supportsRoute: (from: Asset, to: Asset) => from.chain === to.chain && chainIdOf(from.chain) !== null,\n\n async getQuote(params: QuoteParams, signal?: AbortSignal): Promise<Quote | null> {\n const refuse = (why: string): null => {\n params.onRefusal?.(`balancer: ${why}`);\n return null;\n };\n if (params.flex) return refuse(\"amount-flexible deposits are not supported\");\n if (params.sourceChain !== params.destChain) return refuse(\"same-chain swaps only\");\n const chainId = chainIdOf(params.sourceChain);\n if (chainId === null) return refuse(`no Balancer v2 Vault or RPC for ${params.sourceChain}`);\n const chain = BALANCER_V2_CHAINS[chainId]!;\n const src = sourceAssetOf(params);\n if (!src) return refuse(`unknown source token ${params.sourceToken}`);\n const nativeIn = !src.contract;\n const nativeOut = /^0x0+$/.test(params.destToken);\n if (!nativeOut && !/^0x[0-9a-fA-F]{40}$/.test(params.destToken)) return refuse(\"destination token must be an EVM address\");\n if (!/^0x[0-9a-fA-F]{40}$/.test(params.destAddress)) return refuse(\"recipient must be an EVM address\");\n let amountIn: bigint;\n try {\n amountIn = BigInt(params.amountIn);\n } catch {\n return refuse(\"amount is not an integer\");\n }\n if (amountIn <= 0n) return refuse(\"amount must be positive\");\n const tokenIn = getAddress(nativeIn ? chain.weth : src.contract!);\n const tokenOut = getAddress(nativeOut ? chain.weth : params.destToken);\n if (tokenIn === tokenOut) return refuse(nativeIn || nativeOut ? \"that is a wrap/unwrap, not a swap\" : \"same token in and out\");\n\n try {\n await verify(chainId);\n // 1) Discovery: the SOR's v2 paths (never trusted for price).\n // Values are inlined (this API ignores GraphQL variables); all are validated\n // addresses / a decimal number / a fixed enum, so nothing user-shaped is spliced.\n const query_ = `query { sorGetSwapPaths(chain: ${chain.api}, swapAmount: \"${human(amountIn, src.decimals)}\", swapType: EXACT_IN, tokenIn: \"${tokenIn}\", tokenOut: \"${tokenOut}\", useProtocolVersion: 2) { protocolVersion paths { pools tokens { address decimals } inputAmountRaw outputAmountRaw protocolVersion } } }`;\n const res = await fetch(api, {\n method: \"POST\",\n headers: {\"content-type\": \"application/json\"},\n body: JSON.stringify({query: query_}),\n signal: signal ?? timeoutSignal(12_000),\n });\n if (!res.ok) return refuse(`SOR answered HTTP ${res.status}`);\n const body = (await res.json()) as {data?: {sorGetSwapPaths?: {protocolVersion?: number; paths?: SorPath[]}}; errors?: {message?: string}[]};\n const paths = body.data?.sorGetSwapPaths?.paths ?? [];\n if (body.errors?.length) return refuse(`SOR: ${body.errors[0]?.message?.slice(0, 120) ?? \"error\"}`);\n if (!paths.length || paths.some((p) => p.protocolVersion !== 2 || p.pools.length === 0 || p.tokens.length !== p.pools.length + 1)) {\n return refuse(\"no Balancer v2 pool route for this pair and amount\");\n }\n if (paths.reduce((s, p) => s + BigInt(p.inputAmountRaw), 0n) !== amountIn) return refuse(\"the SOR split does not add up to the amount\");\n\n // 2) Build the batch from the suggested paths.\n const assets: Hex[] = [];\n const indexOf = (a: string) => {\n const addr = getAddress(a);\n let i = assets.findIndex((x) => x === addr);\n if (i < 0) i = assets.push(addr) - 1;\n return BigInt(i);\n };\n const swaps: Step[] = [];\n for (const p of paths) {\n if (getAddress(p.tokens[0]!.address) !== tokenIn || getAddress(p.tokens[p.tokens.length - 1]!.address) !== tokenOut) return refuse(\"the SOR path does not start/end at the requested tokens\");\n p.pools.forEach((pool, i) => {\n if (!/^0x[0-9a-fA-F]{64}$/.test(pool)) throw new Error(\"balancer: malformed pool id from the SOR\");\n swaps.push({poolId: pool as Hex, assetInIndex: indexOf(p.tokens[i]!.address), assetOutIndex: indexOf(p.tokens[i + 1]!.address), amount: i === 0 ? BigInt(p.inputAmountRaw) : 0n, userData: \"0x\"});\n });\n }\n const inIdx = bigintToNumber(indexOf(tokenIn));\n const outIdx = bigintToNumber(indexOf(tokenOut));\n // The Vault's ETH sentinel: native in/out wraps/unwraps through its WETH.\n if (nativeIn || nativeOut) {\n const wethIdx = assets.findIndex((a) => a === getAddress(chain.weth));\n if (paths.some((p) => p.tokens.slice(1, -1).some((t) => getAddress(t.address) === getAddress(chain.weth)))) return refuse(\"this route passes through the wrapped native — pick the wrapped token instead\");\n if (wethIdx >= 0) assets[wethIdx] = ZERO as Hex;\n }\n const sender = getAddress(params.refundAddress && /^0x[0-9a-fA-F]{40}$/.test(params.refundAddress) ? params.refundAddress : params.destAddress);\n const recipient = getAddress(params.destAddress);\n const funds: Funds = {sender, fromInternalBalance: false, recipient, toInternalBalance: false};\n\n // 3) Price it on-chain — this, not the SOR's number, is the quote.\n const deltas = await query(chainId, swaps, assets, funds);\n const paid = deltas[inIdx]!;\n const out = -deltas[outIdx]!;\n if (paid !== amountIn) return refuse(`the Vault would take ${paid}, not ${amountIn}`);\n if (out <= 0n) return refuse(\"the Vault prices this route at zero output\");\n if (deltas.some((d, i) => i !== inIdx && i !== outIdx && d !== 0n)) return refuse(\"the batch leaves an intermediate balance — refusing\");\n\n const bps = params.slippageBps ?? slippageDefault;\n const minOut = (out * BigInt(10_000 - bps)) / 10_000n;\n if (params.minAmountOut !== undefined && minOut < BigInt(params.minAmountOut)) return refuse(`floor breach (min=${minOut} < floor=${params.minAmountOut})`);\n const limits = assets.map((_, i) => (i === inIdx ? amountIn : i === outIdx ? -minOut : 0n));\n const deadline = BigInt(Math.floor(Date.now() / 1000) + deadlineS);\n const data = encodeFunctionData({abi: VAULT_ABI, functionName: \"batchSwap\", args: [0, swaps, assets, funds, limits, deadline]});\n const gasLimit = 160_000n + 95_000n * BigInt(swaps.length);\n return {\n provider: \"balancer\",\n intentId: `balancer:${chainId}`,\n depositAddress: \"\",\n depositMemo: null,\n txRequest: {\n to: BALANCER_V2_VAULT,\n data,\n value: nativeIn ? amountIn.toString() : \"0\",\n chainId,\n gasLimit: gasLimit.toString(),\n ...(nativeIn ? {} : {approvalAddress: BALANCER_V2_VAULT}),\n },\n sourceAmount: amountIn.toString(),\n destAmount: out.toString(),\n minDestAmount: minOut.toString(),\n feeUsd: 0,\n etaSeconds: 15,\n reliability: 0.97,\n validUntilMs: Date.now() + ttl,\n // Pool swap fees are inside the Vault-priced output; no fee is added on top.\n providerData: {paths: String(paths.length), hops: String(swaps.length), minOut: minOut.toString(), poolFee: \"pools\"},\n };\n } catch (e) {\n return refuse(e instanceof Error ? e.message.replace(/^balancer: /, \"\") : String(e));\n }\n },\n\n /** Re-price the exact signed batch right before broadcast; refuse when the pools now pay under the minimum. */\n async preflightDeposit(quote: Quote, tx: UnsignedTx): Promise<void> {\n const chainId = Number(quote.intentId.split(\":\")[1]);\n const minOut = quote.providerData?.minOut;\n if (!BALANCER_V2_CHAINS[chainId] || !minOut || !tx.data) return;\n const call = decodeFunctionData({abi: VAULT_ABI, data: tx.data as Hex});\n if (call.functionName !== \"batchSwap\") throw new Error(\"balancer: the transaction is not a Vault batchSwap\");\n const [, swaps, assets, funds, limits] = call.args;\n const outIdx = limits.findIndex((l) => l < 0n);\n const deltas = await query(chainId, swaps, assets, funds);\n const out = -deltas[outIdx]!;\n if (out < BigInt(minOut)) throw new Error(`balancer: price moved — the pools now pay ${out}, under your minimum ${minOut}. Nothing was sent; refresh the quote.`);\n },\n\n async buildDeposit(quote: Quote): Promise<UnsignedTx> {\n const tx = quote.txRequest;\n if (!tx?.to || !tx.data) throw new Error(\"swap2: Balancer quote has no transaction request\");\n return {chain: \"\", to: tx.to, value: tx.value ?? \"0\", data: tx.data, ...(tx.chainId !== undefined ? {chainId: tx.chainId} : {}), ...(tx.gasLimit ? {gasLimit: tx.gasLimit} : {}), ...(tx.approvalAddress ? {approvalAddress: tx.approvalAddress} : {})};\n },\n\n async trackIntent(ref: IntentRef): Promise<IntentStatus> {\n if (!ref.sourceTxHash) return {state: \"pending\"};\n const chainId = Number(ref.intentId.split(\":\")[1]);\n if (!BALANCER_V2_CHAINS[chainId] || !config.rpcUrls[chainId]?.length) return {state: \"pending\"};\n try {\n const receipt = (await rpc(chainId, \"eth_getTransactionReceipt\", [ref.sourceTxHash])) as {status?: string} | null;\n if (!receipt) return {state: \"pending\"};\n return receipt.status === \"0x1\" ? {state: \"completed\", destTxHash: ref.sourceTxHash} : {state: \"failed\"};\n } catch {\n return {state: \"pending\"};\n }\n },\n };\n}\n","/**\n * Activity recognition for Balancer v2: `swap` / `batchSwap` on the Vault\n * (one address on every chain) on a chain the module serves → `swap`,\n * \"Swapped A → B\" from the owner's net transfers.\n */\nimport type {ActivityItem, ChainTx, DescribeActivityContext} from \"@anywalletkit/core\";\nimport {chainLabel, describeSwapLike, matchesCall} from \"@anywalletkit/protocol-swap2-core/activity\";\nimport {evmRef, isEvm} from \"@anywalletkit/protocol-swap2-core/util\";\nimport {BALANCER_V2_CHAINS, BALANCER_V2_VAULT} from \"./provider.js\";\n\nexport const BALANCER_MODULE = \"@anywalletkit/protocol-swap2-balancer\";\n\n/** Vault swap entry points (`viem toFunctionSelector`, 2026-10-14). */\nexport const BALANCER_VAULT_SELECTORS: ReadonlySet<string> = new Set([\n \"0x945bcec9\", // batchSwap(uint8,(bytes32,uint256,uint256,uint256,bytes)[],address[],(address,bool,address,bool),int256[],uint256)\n \"0x52bbbe29\", // swap((bytes32,uint8,address,address,uint256,bytes),(address,bool,address,bool),uint256,uint256)\n]);\n\nexport function describeBalancerActivity(tx: ChainTx, ctx: DescribeActivityContext): ActivityItem | null {\n if (!isEvm(tx.chain) || !BALANCER_V2_CHAINS[evmRef(tx.chain)]) return null;\n if (!matchesCall(tx, [BALANCER_V2_VAULT], BALANCER_VAULT_SELECTORS)) return null;\n return describeSwapLike(tx, ctx, {protocol: BALANCER_MODULE, subtitle: `Balancer v2 · ${chainLabel(tx.chain)}`, kind: \"swap\"});\n}\n"],"mappings":"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","names":["import_util"]}
1
+ {"version":3,"sources":["../src/provider.ts","../src/activity.ts"],"sourcesContent":["import type {RemotePins} from \"@anywalletkit/protocol-swap2-core/types\";\nimport {decodeFunctionData, decodeFunctionResult, encodeFunctionData, getAddress, parseAbi, type Hex} from \"viem\";\nimport {describeBalancerActivity} from \"./activity.js\";\nimport {evmRef, isEvm, timeoutSignal, sourceAssetOf} from \"@anywalletkit/protocol-swap2-core/util\";\nimport type {Asset, CrossChainProvider, IntentRef, IntentStatus, Quote, QuoteParams, UnsignedTx} from \"@anywalletkit/protocol-swap2-core/types\";\nimport {bigintToNumber} from \"@anywalletkit/core\";\n\n/**\n * Balancer v2 — the cp0x permissionless Balancer venue (balancer.cp0x.com).\n *\n * Discovery only from the API: Balancer's SOR (`sorGetSwapPaths`, v2 pools)\n * suggests the path. Everything on the money path is on-chain: the Vault\n * prices that exact path (`queryBatchSwap` via eth_call), the quote uses the\n * Vault's number, and `batchSwap` carries `limits` (in ≤ amountIn, out ≥ min)\n * plus a deadline — the Vault itself refuses anything worse. Native coins use\n * the Vault's own ETH sentinel (address 0): it wraps/unwraps through WETH.\n */\n\nconst ZERO = \"0x0000000000000000000000000000000000000000\";\n/** The same Balancer v2 Vault address on every chain it is deployed on. */\nexport const BALANCER_V2_VAULT = \"0xBA12222222228d8Ba445958a75a0704d566BF2C8\";\nconst DEFAULT_API = \"https://api-v3.balancer.fi/\";\nconst DEFAULT_SLIPPAGE_BPS = 50;\nconst DEFAULT_TTL_MS = 300_000;\nconst DEFAULT_DEADLINE_S = 600;\n/** Most pool hops one batch may carry: the SOR's splits are a handful; a batch past this is refused at quote and at preflight. */\nexport const BALANCER_MAX_SWAPS = 16;\n\n/** Chains where the v2 Vault is live, with the SOR API name and the Vault's WETH() (verified on-chain 2026-10-13). */\nexport const BALANCER_V2_CHAINS: Readonly<Record<number, {api: string; weth: `0x${string}`}>> = Object.freeze({\n 1: {api: \"MAINNET\", weth: \"0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2\"},\n 10: {api: \"OPTIMISM\", weth: \"0x4200000000000000000000000000000000000006\"},\n 100: {api: \"GNOSIS\", weth: \"0xe91D153E0b41518A2Ce8Dd3D7944Fa863463a97d\"},\n 137: {api: \"POLYGON\", weth: \"0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270\"},\n 146: {api: \"SONIC\", weth: \"0x039e2fB66102314Ce7b64Ce5Ce3E5183bc94aD38\"},\n 8453: {api: \"BASE\", weth: \"0x4200000000000000000000000000000000000006\"},\n 9745: {api: \"PLASMA\", weth: \"0x6100E367285b01F48D07953803A2d8dCA5D19873\"},\n 42161: {api: \"ARBITRUM\", weth: \"0x82aF49447D8a07e3bd95BD0d56f35241523fBab1\"},\n 43114: {api: \"AVALANCHE\", weth: \"0xB31f66AA3C1e785363F0875A1B74E27b85FD66c7\"},\n});\n\nconst SWAP_STEP = \"(bytes32 poolId, uint256 assetInIndex, uint256 assetOutIndex, uint256 amount, bytes userData)\";\nconst FUNDS = \"(address sender, bool fromInternalBalance, address recipient, bool toInternalBalance)\";\nconst VAULT_ABI = parseAbi([\n `function queryBatchSwap(uint8 kind, ${SWAP_STEP}[] swaps, address[] assets, ${FUNDS} funds) returns (int256[] assetDeltas)`,\n `function batchSwap(uint8 kind, ${SWAP_STEP}[] swaps, address[] assets, ${FUNDS} funds, int256[] limits, uint256 deadline) payable returns (int256[] assetDeltas)`,\n \"function WETH() view returns (address)\",\n]);\n\ntype Step = {poolId: Hex; assetInIndex: bigint; assetOutIndex: bigint; amount: bigint; userData: Hex};\ntype Funds = {sender: Hex; fromInternalBalance: boolean; recipient: Hex; toInternalBalance: boolean};\n\nexport interface BalancerProviderConfig {\n enabled?: boolean;\n /** JSON-RPC endpoints per EVM chain id (on-chain pricing + receipts). */\n rpcUrls: Readonly<Record<number, readonly string[]>>;\n /** Balancer API (SOR) base URL — discovery only. */\n apiUrl?: string;\n defaultSlippageBps?: number;\n quoteTtlMs?: number;\n deadlineSeconds?: number;\n}\n\ninterface SorPath {\n pools: string[];\n tokens: {address: string; decimals: number}[];\n inputAmountRaw: string;\n outputAmountRaw: string;\n protocolVersion: number;\n}\n\n/** Base units → the decimal string the SOR expects (\"1.5\"). */\nfunction human(amount: bigint, decimals: number): string {\n const s = amount.toString().padStart(decimals + 1, \"0\");\n const whole = s.slice(0, s.length - decimals);\n const frac = s.slice(s.length - decimals).replace(/0+$/, \"\");\n return frac ? `${whole}.${frac}` : whole;\n}\n\nexport function balancerProvider(config: BalancerProviderConfig): CrossChainProvider {\n const api = config.apiUrl ?? DEFAULT_API;\n const slippageDefault = config.defaultSlippageBps ?? DEFAULT_SLIPPAGE_BPS;\n const ttl = config.quoteTtlMs ?? DEFAULT_TTL_MS;\n const deadlineS = config.deadlineSeconds ?? DEFAULT_DEADLINE_S;\n const verified = new Map<number, Promise<void>>();\n\n const chainIdOf = (caip2: string): number | null => {\n if (!isEvm(caip2)) return null;\n const id = evmRef(caip2);\n return BALANCER_V2_CHAINS[id] && config.rpcUrls[id]?.length ? id : null;\n };\n\n async function rpc(chainId: number, method: string, params: unknown[]): Promise<unknown> {\n let lastErr: unknown;\n for (const url of config.rpcUrls[chainId] ?? []) {\n try {\n const res = await fetch(url, {method: \"POST\", headers: {\"content-type\": \"application/json\"}, body: JSON.stringify({jsonrpc: \"2.0\", id: 1, method, params}), signal: timeoutSignal(12_000)});\n if (!res.ok) throw new Error(`HTTP ${res.status}`);\n const body = (await res.json()) as {result?: unknown; error?: {message?: string}};\n if (body.error) throw Object.assign(new Error(body.error.message ?? \"rpc error\"), {rpc: true});\n return body.result ?? null;\n } catch (e) {\n if ((e as {rpc?: boolean}).rpc) throw e; // a revert is an answer, not a dead endpoint\n lastErr = e;\n }\n }\n throw new Error(`balancer: no RPC answered on chain ${chainId} (${lastErr instanceof Error ? lastErr.message : String(lastErr)})`);\n }\n\n /** The Vault is deployed here and wraps through the WETH we pinned. */\n function verify(chainId: number): Promise<void> {\n let p = verified.get(chainId);\n if (!p) {\n p = (async () => {\n const raw = (await rpc(chainId, \"eth_call\", [{to: BALANCER_V2_VAULT, data: encodeFunctionData({abi: VAULT_ABI, functionName: \"WETH\"})}, \"latest\"])) as Hex;\n const weth = decodeFunctionResult({abi: VAULT_ABI, functionName: \"WETH\", data: raw});\n if (getAddress(weth) !== getAddress(BALANCER_V2_CHAINS[chainId]!.weth)) throw new Error(`balancer: the Vault on chain ${chainId} is not the pinned deployment — refusing`);\n })();\n p.catch(() => verified.delete(chainId));\n verified.set(chainId, p);\n }\n return p;\n }\n\n /** Vault.queryBatchSwap on chain: the asset deltas this exact batch would produce. */\n async function query(chainId: number, swaps: readonly Step[], assets: readonly Hex[], funds: Funds): Promise<readonly bigint[]> {\n const data = encodeFunctionData({abi: VAULT_ABI, functionName: \"queryBatchSwap\", args: [0, swaps, assets, funds]});\n const raw = (await rpc(chainId, \"eth_call\", [{to: BALANCER_V2_VAULT, data}, \"latest\"])) as Hex;\n return decodeFunctionResult({abi: VAULT_ABI, functionName: \"queryBatchSwap\", data: raw});\n }\n\n return {\n name: \"balancer\",\n describeActivity: describeBalancerActivity,\n supportsSameChain: true,\n enabled: () => config.enabled ?? true,\n supportsChain: (caip2: string) => chainIdOf(caip2) !== null,\n supportsChainPair: (from: string, to: string) => from === to && chainIdOf(from) !== null,\n supportsRoute: (from: Asset, to: Asset) => from.chain === to.chain && chainIdOf(from.chain) !== null,\n\n async getQuote(params: QuoteParams, signal?: AbortSignal): Promise<Quote | null> {\n const refuse = (why: string): null => {\n params.onRefusal?.(`balancer: ${why}`);\n return null;\n };\n if (params.flex) return refuse(\"amount-flexible deposits are not supported\");\n if (params.sourceChain !== params.destChain) return refuse(\"same-chain swaps only\");\n const chainId = chainIdOf(params.sourceChain);\n if (chainId === null) return refuse(`no Balancer v2 Vault or RPC for ${params.sourceChain}`);\n const chain = BALANCER_V2_CHAINS[chainId]!;\n const src = sourceAssetOf(params);\n if (!src) return refuse(`unknown source token ${params.sourceToken}`);\n const nativeIn = !src.contract;\n const nativeOut = /^0x0+$/.test(params.destToken);\n if (!nativeOut && !/^0x[0-9a-fA-F]{40}$/.test(params.destToken)) return refuse(\"destination token must be an EVM address\");\n if (!/^0x[0-9a-fA-F]{40}$/.test(params.destAddress)) return refuse(\"recipient must be an EVM address\");\n let amountIn: bigint;\n try {\n amountIn = BigInt(params.amountIn);\n } catch {\n return refuse(\"amount is not an integer\");\n }\n if (amountIn <= 0n) return refuse(\"amount must be positive\");\n const tokenIn = getAddress(nativeIn ? chain.weth : src.contract!);\n const tokenOut = getAddress(nativeOut ? chain.weth : params.destToken);\n if (tokenIn === tokenOut) return refuse(nativeIn || nativeOut ? \"that is a wrap/unwrap, not a swap\" : \"same token in and out\");\n\n try {\n await verify(chainId);\n // 1) Discovery: the SOR's v2 paths (never trusted for price).\n // Values are inlined (this API ignores GraphQL variables); all are validated\n // addresses / a decimal number / a fixed enum, so nothing user-shaped is spliced.\n const query_ = `query { sorGetSwapPaths(chain: ${chain.api}, swapAmount: \"${human(amountIn, src.decimals)}\", swapType: EXACT_IN, tokenIn: \"${tokenIn}\", tokenOut: \"${tokenOut}\", useProtocolVersion: 2) { protocolVersion paths { pools tokens { address decimals } inputAmountRaw outputAmountRaw protocolVersion } } }`;\n const res = await fetch(api, {\n method: \"POST\",\n headers: {\"content-type\": \"application/json\"},\n body: JSON.stringify({query: query_}),\n signal: signal ?? timeoutSignal(12_000),\n });\n if (!res.ok) return refuse(`SOR answered HTTP ${res.status}`);\n const body = (await res.json()) as {data?: {sorGetSwapPaths?: {protocolVersion?: number; paths?: SorPath[]}}; errors?: {message?: string}[]};\n const paths = body.data?.sorGetSwapPaths?.paths ?? [];\n if (body.errors?.length) return refuse(`SOR: ${body.errors[0]?.message?.slice(0, 120) ?? \"error\"}`);\n if (!paths.length || paths.some((p) => p.protocolVersion !== 2 || p.pools.length === 0 || p.tokens.length !== p.pools.length + 1)) {\n return refuse(\"no Balancer v2 pool route for this pair and amount\");\n }\n if (paths.reduce((s, p) => s + BigInt(p.inputAmountRaw), 0n) !== amountIn) return refuse(\"the SOR split does not add up to the amount\");\n\n // 2) Build the batch from the suggested paths.\n const assets: Hex[] = [];\n const indexOf = (a: string) => {\n const addr = getAddress(a);\n let i = assets.findIndex((x) => x === addr);\n if (i < 0) i = assets.push(addr) - 1;\n return BigInt(i);\n };\n const swaps: Step[] = [];\n for (const p of paths) {\n if (getAddress(p.tokens[0]!.address) !== tokenIn || getAddress(p.tokens[p.tokens.length - 1]!.address) !== tokenOut) return refuse(\"the SOR path does not start/end at the requested tokens\");\n p.pools.forEach((pool, i) => {\n if (!/^0x[0-9a-fA-F]{64}$/.test(pool)) throw new Error(\"balancer: malformed pool id from the SOR\");\n swaps.push({poolId: pool as Hex, assetInIndex: indexOf(p.tokens[i]!.address), assetOutIndex: indexOf(p.tokens[i + 1]!.address), amount: i === 0 ? BigInt(p.inputAmountRaw) : 0n, userData: \"0x\"});\n });\n }\n if (swaps.length > BALANCER_MAX_SWAPS) return refuse(`the SOR route has ${swaps.length} pool hops, more than ${BALANCER_MAX_SWAPS}`);\n const inIdx = bigintToNumber(indexOf(tokenIn));\n const outIdx = bigintToNumber(indexOf(tokenOut));\n // The Vault's ETH sentinel: native in/out wraps/unwraps through its WETH.\n if (nativeIn || nativeOut) {\n const wethIdx = assets.findIndex((a) => a === getAddress(chain.weth));\n if (paths.some((p) => p.tokens.slice(1, -1).some((t) => getAddress(t.address) === getAddress(chain.weth)))) return refuse(\"this route passes through the wrapped native — pick the wrapped token instead\");\n if (wethIdx >= 0) assets[wethIdx] = ZERO as Hex;\n }\n const sender = getAddress(params.refundAddress && /^0x[0-9a-fA-F]{40}$/.test(params.refundAddress) ? params.refundAddress : params.destAddress);\n const recipient = getAddress(params.destAddress);\n const funds: Funds = {sender, fromInternalBalance: false, recipient, toInternalBalance: false};\n\n // 3) Price it on-chain — this, not the SOR's number, is the quote.\n const deltas = await query(chainId, swaps, assets, funds);\n const paid = deltas[inIdx]!;\n const out = -deltas[outIdx]!;\n if (paid !== amountIn) return refuse(`the Vault would take ${paid}, not ${amountIn}`);\n if (out <= 0n) return refuse(\"the Vault prices this route at zero output\");\n if (deltas.some((d, i) => i !== inIdx && i !== outIdx && d !== 0n)) return refuse(\"the batch leaves an intermediate balance — refusing\");\n\n const bps = params.slippageBps ?? slippageDefault;\n const minOut = (out * BigInt(10_000 - bps)) / 10_000n;\n if (params.minAmountOut !== undefined && minOut < BigInt(params.minAmountOut)) return refuse(`floor breach (min=${minOut} < floor=${params.minAmountOut})`);\n const limits = assets.map((_, i) => (i === inIdx ? amountIn : i === outIdx ? -minOut : 0n));\n const deadline = BigInt(Math.floor(Date.now() / 1000) + deadlineS);\n const data = encodeFunctionData({abi: VAULT_ABI, functionName: \"batchSwap\", args: [0, swaps, assets, funds, limits, deadline]});\n const gasLimit = 160_000n + 95_000n * BigInt(swaps.length);\n return {\n provider: \"balancer\",\n intentId: `balancer:${chainId}`,\n depositAddress: \"\",\n depositMemo: null,\n txRequest: {\n to: BALANCER_V2_VAULT,\n data,\n value: nativeIn ? amountIn.toString() : \"0\",\n chainId,\n gasLimit: gasLimit.toString(),\n ...(nativeIn ? {} : {approvalAddress: BALANCER_V2_VAULT}),\n },\n sourceAmount: amountIn.toString(),\n destAmount: out.toString(),\n minDestAmount: minOut.toString(),\n feeUsd: 0,\n etaSeconds: 15,\n reliability: 0.97,\n validUntilMs: Date.now() + ttl,\n // Pool swap fees are inside the Vault-priced output; no fee is added on top.\n providerData: {paths: String(paths.length), hops: String(swaps.length), minOut: minOut.toString(), poolFee: \"pools\"},\n };\n } catch (e) {\n return refuse(e instanceof Error ? e.message.replace(/^balancer: /, \"\") : String(e));\n }\n },\n\n /**\n * Re-price the exact signed batch right before broadcast; refuse when the pools now pay under the minimum.\n * Only the quote's own batch is decoded and priced: behind a backend, `tx` is the client's and only the\n * quote is sealed, so a client's batch (any size, any pools) never reaches the decoder or the RPC\n * (scan 2026-10-27 modules, F24).\n */\n async preflightDeposit(quote: Quote, tx: UnsignedTx): Promise<void> {\n const chainId = Number(quote.intentId.split(\":\")[1]);\n const minOut = quote.providerData?.minOut;\n if (!BALANCER_V2_CHAINS[chainId] || !minOut || !tx.data) return;\n const quoted = quote.txRequest?.data;\n if (!quoted || tx.data.length !== quoted.length || tx.data.toLowerCase() !== quoted.toLowerCase()) {\n throw new Error(\"balancer: the transaction is not the quoted batch. Nothing was sent; refresh the quote.\");\n }\n const call = decodeFunctionData({abi: VAULT_ABI, data: quoted as Hex});\n if (call.functionName !== \"batchSwap\") throw new Error(\"balancer: the transaction is not a Vault batchSwap\");\n const [, swaps, assets, funds, limits] = call.args;\n if (swaps.length === 0 || swaps.length > BALANCER_MAX_SWAPS) throw new Error(`balancer: the batch has ${swaps.length} pool hops, not 1–${BALANCER_MAX_SWAPS}`);\n if (swaps.some((step) => step.userData !== \"0x\")) throw new Error(\"balancer: the batch carries pool userData this module never sends\");\n const outIdx = limits.findIndex((l) => l < 0n);\n const deltas = await query(chainId, swaps, assets, funds);\n const out = -deltas[outIdx]!;\n if (out < BigInt(minOut)) throw new Error(`balancer: price moved — the pools now pay ${out}, under your minimum ${minOut}. Nothing was sent; refresh the quote.`);\n },\n\n async buildDeposit(quote: Quote): Promise<UnsignedTx> {\n const tx = quote.txRequest;\n if (!tx?.to || !tx.data) throw new Error(\"swap2: Balancer quote has no transaction request\");\n return {chain: \"\", to: tx.to, value: tx.value ?? \"0\", data: tx.data, ...(tx.chainId !== undefined ? {chainId: tx.chainId} : {}), ...(tx.gasLimit ? {gasLimit: tx.gasLimit} : {}), ...(tx.approvalAddress ? {approvalAddress: tx.approvalAddress} : {})};\n },\n\n async trackIntent(ref: IntentRef): Promise<IntentStatus> {\n if (!ref.sourceTxHash) return {state: \"pending\"};\n const chainId = Number(ref.intentId.split(\":\")[1]);\n if (!BALANCER_V2_CHAINS[chainId] || !config.rpcUrls[chainId]?.length) return {state: \"pending\"};\n try {\n const receipt = (await rpc(chainId, \"eth_getTransactionReceipt\", [ref.sourceTxHash])) as {status?: string} | null;\n if (!receipt) return {state: \"pending\"};\n return receipt.status === \"0x1\" ? {state: \"completed\", destTxHash: ref.sourceTxHash} : {state: \"failed\"};\n } catch {\n return {state: \"pending\"};\n }\n },\n };\n}\n\n/**\n * The Balancer V2 Vault a swap calls and approves on the chains this provider serves — the app's pins\n * for `remoteProvider({name: \"balancer\", baseUrl, ...balancerPins})` (scan 2026-10-27 modules, F8/F16).\n */\nexport const balancerPins: RemotePins = Object.freeze({\n callTargets: (chain: string): readonly string[] => balancerVaultFor(chain),\n spenders: (chain: string): readonly string[] => balancerVaultFor(chain),\n});\nfunction balancerVaultFor(chain: string): readonly string[] {\n const id = /^eip155:[0-9]+$/.test(chain) ? Number(chain.slice(7)) : NaN;\n return Object.prototype.hasOwnProperty.call(BALANCER_V2_CHAINS, id) ? [BALANCER_V2_VAULT] : [];\n}\n","/**\n * Activity recognition for Balancer v2: `swap` / `batchSwap` on the Vault\n * (one address on every chain) on a chain the module serves → `swap`,\n * \"Swapped A → B\" from the owner's net transfers.\n */\nimport type {ActivityItem, ChainTx, DescribeActivityContext} from \"@anywalletkit/core\";\nimport {chainLabel, describeSwapLike, matchesCall} from \"@anywalletkit/protocol-swap2-core/activity\";\nimport {evmRef, isEvm} from \"@anywalletkit/protocol-swap2-core/util\";\nimport {BALANCER_V2_CHAINS, BALANCER_V2_VAULT} from \"./provider.js\";\n\nexport const BALANCER_MODULE = \"@anywalletkit/protocol-swap2-balancer\";\n\n/** Vault swap entry points (`viem toFunctionSelector`, 2026-10-14). */\nexport const BALANCER_VAULT_SELECTORS: ReadonlySet<string> = new Set([\n \"0x945bcec9\", // batchSwap(uint8,(bytes32,uint256,uint256,uint256,bytes)[],address[],(address,bool,address,bool),int256[],uint256)\n \"0x52bbbe29\", // swap((bytes32,uint8,address,address,uint256,bytes),(address,bool,address,bool),uint256,uint256)\n]);\n\nexport function describeBalancerActivity(tx: ChainTx, ctx: DescribeActivityContext): ActivityItem | null {\n if (!isEvm(tx.chain) || !BALANCER_V2_CHAINS[evmRef(tx.chain)]) return null;\n if (!matchesCall(tx, [BALANCER_V2_VAULT], BALANCER_VAULT_SELECTORS)) return null;\n return describeSwapLike(tx, ctx, {protocol: BALANCER_MODULE, subtitle: `Balancer v2 · ${chainLabel(tx.chain)}`, kind: \"swap\"});\n}\n"],"mappings":"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","names":["import_util"]}
@@ -1,7 +1,9 @@
1
- import { CrossChainProvider } from '@anywalletkit/protocol-swap2-core/types';
1
+ import { RemotePins, CrossChainProvider } from '@anywalletkit/protocol-swap2-core/types';
2
2
 
3
3
  /** The same Balancer v2 Vault address on every chain it is deployed on. */
4
4
  declare const BALANCER_V2_VAULT = "0xBA12222222228d8Ba445958a75a0704d566BF2C8";
5
+ /** Most pool hops one batch may carry: the SOR's splits are a handful; a batch past this is refused at quote and at preflight. */
6
+ declare const BALANCER_MAX_SWAPS = 16;
5
7
  /** Chains where the v2 Vault is live, with the SOR API name and the Vault's WETH() (verified on-chain 2026-10-13). */
6
8
  declare const BALANCER_V2_CHAINS: Readonly<Record<number, {
7
9
  api: string;
@@ -18,5 +20,10 @@ interface BalancerProviderConfig {
18
20
  deadlineSeconds?: number;
19
21
  }
20
22
  declare function balancerProvider(config: BalancerProviderConfig): CrossChainProvider;
23
+ /**
24
+ * The Balancer V2 Vault a swap calls and approves on the chains this provider serves — the app's pins
25
+ * for `remoteProvider({name: "balancer", baseUrl, ...balancerPins})` (scan 2026-10-27 modules, F8/F16).
26
+ */
27
+ declare const balancerPins: RemotePins;
21
28
 
22
- export { BALANCER_V2_CHAINS, BALANCER_V2_VAULT, type BalancerProviderConfig, balancerProvider };
29
+ export { BALANCER_MAX_SWAPS, BALANCER_V2_CHAINS, BALANCER_V2_VAULT, type BalancerProviderConfig, balancerPins, balancerProvider };
@@ -1,7 +1,9 @@
1
- import { CrossChainProvider } from '@anywalletkit/protocol-swap2-core/types';
1
+ import { RemotePins, CrossChainProvider } from '@anywalletkit/protocol-swap2-core/types';
2
2
 
3
3
  /** The same Balancer v2 Vault address on every chain it is deployed on. */
4
4
  declare const BALANCER_V2_VAULT = "0xBA12222222228d8Ba445958a75a0704d566BF2C8";
5
+ /** Most pool hops one batch may carry: the SOR's splits are a handful; a batch past this is refused at quote and at preflight. */
6
+ declare const BALANCER_MAX_SWAPS = 16;
5
7
  /** Chains where the v2 Vault is live, with the SOR API name and the Vault's WETH() (verified on-chain 2026-10-13). */
6
8
  declare const BALANCER_V2_CHAINS: Readonly<Record<number, {
7
9
  api: string;
@@ -18,5 +20,10 @@ interface BalancerProviderConfig {
18
20
  deadlineSeconds?: number;
19
21
  }
20
22
  declare function balancerProvider(config: BalancerProviderConfig): CrossChainProvider;
23
+ /**
24
+ * The Balancer V2 Vault a swap calls and approves on the chains this provider serves — the app's pins
25
+ * for `remoteProvider({name: "balancer", baseUrl, ...balancerPins})` (scan 2026-10-27 modules, F8/F16).
26
+ */
27
+ declare const balancerPins: RemotePins;
21
28
 
22
- export { BALANCER_V2_CHAINS, BALANCER_V2_VAULT, type BalancerProviderConfig, balancerProvider };
29
+ export { BALANCER_MAX_SWAPS, BALANCER_V2_CHAINS, BALANCER_V2_VAULT, type BalancerProviderConfig, balancerPins, balancerProvider };
package/dist/provider.js CHANGED
@@ -1,11 +1,15 @@
1
1
  import {
2
+ BALANCER_MAX_SWAPS,
2
3
  BALANCER_V2_CHAINS,
3
4
  BALANCER_V2_VAULT,
5
+ balancerPins,
4
6
  balancerProvider
5
- } from "./chunk-3YTDA5OS.js";
7
+ } from "./chunk-P4BZP7R4.js";
6
8
  export {
9
+ BALANCER_MAX_SWAPS,
7
10
  BALANCER_V2_CHAINS,
8
11
  BALANCER_V2_VAULT,
12
+ balancerPins,
9
13
  balancerProvider
10
14
  };
11
15
  //# sourceMappingURL=provider.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@anywalletkit/protocol-swap2-balancer",
3
- "version": "0.1.9",
3
+ "version": "0.2.0",
4
4
  "description": "Balancer v2 same-chain swaps: routes suggested by the Balancer SOR API, priced and enforced on-chain by the Vault (queryBatchSwap → batchSwap with limits). The cp0x permissionless Balancer venue.",
5
5
  "license": "Apache-2.0",
6
6
  "homepage": "https://docs.anywallet.tech",
@@ -45,7 +45,7 @@
45
45
  ],
46
46
  "dependencies": {
47
47
  "viem": "^2.57.2",
48
- "@anywalletkit/protocol-swap2-core": "0.6.1",
48
+ "@anywalletkit/protocol-swap2-core": "0.7.0",
49
49
  "@anywalletkit/swap2-assets": "0.2.2"
50
50
  },
51
51
  "peerDependencies": {
@@ -54,8 +54,8 @@
54
54
  "devDependencies": {
55
55
  "@types/node": "^22",
56
56
  "vitest": "^5.0.3",
57
- "@anywalletkit/conformance": "0.4.4",
58
- "@anywalletkit/core": "0.6.1"
57
+ "@anywalletkit/core": "0.6.1",
58
+ "@anywalletkit/conformance": "0.4.5"
59
59
  },
60
60
  "keywords": [
61
61
  "balancer",
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/activity.ts","../src/provider.ts"],"sourcesContent":["/**\n * Activity recognition for Balancer v2: `swap` / `batchSwap` on the Vault\n * (one address on every chain) on a chain the module serves → `swap`,\n * \"Swapped A → B\" from the owner's net transfers.\n */\nimport type {ActivityItem, ChainTx, DescribeActivityContext} from \"@anywalletkit/core\";\nimport {chainLabel, describeSwapLike, matchesCall} from \"@anywalletkit/protocol-swap2-core/activity\";\nimport {evmRef, isEvm} from \"@anywalletkit/protocol-swap2-core/util\";\nimport {BALANCER_V2_CHAINS, BALANCER_V2_VAULT} from \"./provider.js\";\n\nexport const BALANCER_MODULE = \"@anywalletkit/protocol-swap2-balancer\";\n\n/** Vault swap entry points (`viem toFunctionSelector`, 2026-10-14). */\nexport const BALANCER_VAULT_SELECTORS: ReadonlySet<string> = new Set([\n \"0x945bcec9\", // batchSwap(uint8,(bytes32,uint256,uint256,uint256,bytes)[],address[],(address,bool,address,bool),int256[],uint256)\n \"0x52bbbe29\", // swap((bytes32,uint8,address,address,uint256,bytes),(address,bool,address,bool),uint256,uint256)\n]);\n\nexport function describeBalancerActivity(tx: ChainTx, ctx: DescribeActivityContext): ActivityItem | null {\n if (!isEvm(tx.chain) || !BALANCER_V2_CHAINS[evmRef(tx.chain)]) return null;\n if (!matchesCall(tx, [BALANCER_V2_VAULT], BALANCER_VAULT_SELECTORS)) return null;\n return describeSwapLike(tx, ctx, {protocol: BALANCER_MODULE, subtitle: `Balancer v2 · ${chainLabel(tx.chain)}`, kind: \"swap\"});\n}\n","import {decodeFunctionData, decodeFunctionResult, encodeFunctionData, getAddress, parseAbi, type Hex} from \"viem\";\nimport {describeBalancerActivity} from \"./activity.js\";\nimport {evmRef, isEvm, timeoutSignal, sourceAssetOf} from \"@anywalletkit/protocol-swap2-core/util\";\nimport type {Asset, CrossChainProvider, IntentRef, IntentStatus, Quote, QuoteParams, UnsignedTx} from \"@anywalletkit/protocol-swap2-core/types\";\nimport {bigintToNumber} from \"@anywalletkit/core\";\n\n/**\n * Balancer v2 — the cp0x permissionless Balancer venue (balancer.cp0x.com).\n *\n * Discovery only from the API: Balancer's SOR (`sorGetSwapPaths`, v2 pools)\n * suggests the path. Everything on the money path is on-chain: the Vault\n * prices that exact path (`queryBatchSwap` via eth_call), the quote uses the\n * Vault's number, and `batchSwap` carries `limits` (in ≤ amountIn, out ≥ min)\n * plus a deadline — the Vault itself refuses anything worse. Native coins use\n * the Vault's own ETH sentinel (address 0): it wraps/unwraps through WETH.\n */\n\nconst ZERO = \"0x0000000000000000000000000000000000000000\";\n/** The same Balancer v2 Vault address on every chain it is deployed on. */\nexport const BALANCER_V2_VAULT = \"0xBA12222222228d8Ba445958a75a0704d566BF2C8\";\nconst DEFAULT_API = \"https://api-v3.balancer.fi/\";\nconst DEFAULT_SLIPPAGE_BPS = 50;\nconst DEFAULT_TTL_MS = 300_000;\nconst DEFAULT_DEADLINE_S = 600;\n\n/** Chains where the v2 Vault is live, with the SOR API name and the Vault's WETH() (verified on-chain 2026-10-13). */\nexport const BALANCER_V2_CHAINS: Readonly<Record<number, {api: string; weth: `0x${string}`}>> = Object.freeze({\n 1: {api: \"MAINNET\", weth: \"0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2\"},\n 10: {api: \"OPTIMISM\", weth: \"0x4200000000000000000000000000000000000006\"},\n 100: {api: \"GNOSIS\", weth: \"0xe91D153E0b41518A2Ce8Dd3D7944Fa863463a97d\"},\n 137: {api: \"POLYGON\", weth: \"0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270\"},\n 146: {api: \"SONIC\", weth: \"0x039e2fB66102314Ce7b64Ce5Ce3E5183bc94aD38\"},\n 8453: {api: \"BASE\", weth: \"0x4200000000000000000000000000000000000006\"},\n 9745: {api: \"PLASMA\", weth: \"0x6100E367285b01F48D07953803A2d8dCA5D19873\"},\n 42161: {api: \"ARBITRUM\", weth: \"0x82aF49447D8a07e3bd95BD0d56f35241523fBab1\"},\n 43114: {api: \"AVALANCHE\", weth: \"0xB31f66AA3C1e785363F0875A1B74E27b85FD66c7\"},\n});\n\nconst SWAP_STEP = \"(bytes32 poolId, uint256 assetInIndex, uint256 assetOutIndex, uint256 amount, bytes userData)\";\nconst FUNDS = \"(address sender, bool fromInternalBalance, address recipient, bool toInternalBalance)\";\nconst VAULT_ABI = parseAbi([\n `function queryBatchSwap(uint8 kind, ${SWAP_STEP}[] swaps, address[] assets, ${FUNDS} funds) returns (int256[] assetDeltas)`,\n `function batchSwap(uint8 kind, ${SWAP_STEP}[] swaps, address[] assets, ${FUNDS} funds, int256[] limits, uint256 deadline) payable returns (int256[] assetDeltas)`,\n \"function WETH() view returns (address)\",\n]);\n\ntype Step = {poolId: Hex; assetInIndex: bigint; assetOutIndex: bigint; amount: bigint; userData: Hex};\ntype Funds = {sender: Hex; fromInternalBalance: boolean; recipient: Hex; toInternalBalance: boolean};\n\nexport interface BalancerProviderConfig {\n enabled?: boolean;\n /** JSON-RPC endpoints per EVM chain id (on-chain pricing + receipts). */\n rpcUrls: Readonly<Record<number, readonly string[]>>;\n /** Balancer API (SOR) base URL — discovery only. */\n apiUrl?: string;\n defaultSlippageBps?: number;\n quoteTtlMs?: number;\n deadlineSeconds?: number;\n}\n\ninterface SorPath {\n pools: string[];\n tokens: {address: string; decimals: number}[];\n inputAmountRaw: string;\n outputAmountRaw: string;\n protocolVersion: number;\n}\n\n/** Base units → the decimal string the SOR expects (\"1.5\"). */\nfunction human(amount: bigint, decimals: number): string {\n const s = amount.toString().padStart(decimals + 1, \"0\");\n const whole = s.slice(0, s.length - decimals);\n const frac = s.slice(s.length - decimals).replace(/0+$/, \"\");\n return frac ? `${whole}.${frac}` : whole;\n}\n\nexport function balancerProvider(config: BalancerProviderConfig): CrossChainProvider {\n const api = config.apiUrl ?? DEFAULT_API;\n const slippageDefault = config.defaultSlippageBps ?? DEFAULT_SLIPPAGE_BPS;\n const ttl = config.quoteTtlMs ?? DEFAULT_TTL_MS;\n const deadlineS = config.deadlineSeconds ?? DEFAULT_DEADLINE_S;\n const verified = new Map<number, Promise<void>>();\n\n const chainIdOf = (caip2: string): number | null => {\n if (!isEvm(caip2)) return null;\n const id = evmRef(caip2);\n return BALANCER_V2_CHAINS[id] && config.rpcUrls[id]?.length ? id : null;\n };\n\n async function rpc(chainId: number, method: string, params: unknown[]): Promise<unknown> {\n let lastErr: unknown;\n for (const url of config.rpcUrls[chainId] ?? []) {\n try {\n const res = await fetch(url, {method: \"POST\", headers: {\"content-type\": \"application/json\"}, body: JSON.stringify({jsonrpc: \"2.0\", id: 1, method, params}), signal: timeoutSignal(12_000)});\n if (!res.ok) throw new Error(`HTTP ${res.status}`);\n const body = (await res.json()) as {result?: unknown; error?: {message?: string}};\n if (body.error) throw Object.assign(new Error(body.error.message ?? \"rpc error\"), {rpc: true});\n return body.result ?? null;\n } catch (e) {\n if ((e as {rpc?: boolean}).rpc) throw e; // a revert is an answer, not a dead endpoint\n lastErr = e;\n }\n }\n throw new Error(`balancer: no RPC answered on chain ${chainId} (${lastErr instanceof Error ? lastErr.message : String(lastErr)})`);\n }\n\n /** The Vault is deployed here and wraps through the WETH we pinned. */\n function verify(chainId: number): Promise<void> {\n let p = verified.get(chainId);\n if (!p) {\n p = (async () => {\n const raw = (await rpc(chainId, \"eth_call\", [{to: BALANCER_V2_VAULT, data: encodeFunctionData({abi: VAULT_ABI, functionName: \"WETH\"})}, \"latest\"])) as Hex;\n const weth = decodeFunctionResult({abi: VAULT_ABI, functionName: \"WETH\", data: raw});\n if (getAddress(weth) !== getAddress(BALANCER_V2_CHAINS[chainId]!.weth)) throw new Error(`balancer: the Vault on chain ${chainId} is not the pinned deployment — refusing`);\n })();\n p.catch(() => verified.delete(chainId));\n verified.set(chainId, p);\n }\n return p;\n }\n\n /** Vault.queryBatchSwap on chain: the asset deltas this exact batch would produce. */\n async function query(chainId: number, swaps: readonly Step[], assets: readonly Hex[], funds: Funds): Promise<readonly bigint[]> {\n const data = encodeFunctionData({abi: VAULT_ABI, functionName: \"queryBatchSwap\", args: [0, swaps, assets, funds]});\n const raw = (await rpc(chainId, \"eth_call\", [{to: BALANCER_V2_VAULT, data}, \"latest\"])) as Hex;\n return decodeFunctionResult({abi: VAULT_ABI, functionName: \"queryBatchSwap\", data: raw});\n }\n\n return {\n name: \"balancer\",\n describeActivity: describeBalancerActivity,\n supportsSameChain: true,\n enabled: () => config.enabled ?? true,\n supportsChain: (caip2: string) => chainIdOf(caip2) !== null,\n supportsChainPair: (from: string, to: string) => from === to && chainIdOf(from) !== null,\n supportsRoute: (from: Asset, to: Asset) => from.chain === to.chain && chainIdOf(from.chain) !== null,\n\n async getQuote(params: QuoteParams, signal?: AbortSignal): Promise<Quote | null> {\n const refuse = (why: string): null => {\n params.onRefusal?.(`balancer: ${why}`);\n return null;\n };\n if (params.flex) return refuse(\"amount-flexible deposits are not supported\");\n if (params.sourceChain !== params.destChain) return refuse(\"same-chain swaps only\");\n const chainId = chainIdOf(params.sourceChain);\n if (chainId === null) return refuse(`no Balancer v2 Vault or RPC for ${params.sourceChain}`);\n const chain = BALANCER_V2_CHAINS[chainId]!;\n const src = sourceAssetOf(params);\n if (!src) return refuse(`unknown source token ${params.sourceToken}`);\n const nativeIn = !src.contract;\n const nativeOut = /^0x0+$/.test(params.destToken);\n if (!nativeOut && !/^0x[0-9a-fA-F]{40}$/.test(params.destToken)) return refuse(\"destination token must be an EVM address\");\n if (!/^0x[0-9a-fA-F]{40}$/.test(params.destAddress)) return refuse(\"recipient must be an EVM address\");\n let amountIn: bigint;\n try {\n amountIn = BigInt(params.amountIn);\n } catch {\n return refuse(\"amount is not an integer\");\n }\n if (amountIn <= 0n) return refuse(\"amount must be positive\");\n const tokenIn = getAddress(nativeIn ? chain.weth : src.contract!);\n const tokenOut = getAddress(nativeOut ? chain.weth : params.destToken);\n if (tokenIn === tokenOut) return refuse(nativeIn || nativeOut ? \"that is a wrap/unwrap, not a swap\" : \"same token in and out\");\n\n try {\n await verify(chainId);\n // 1) Discovery: the SOR's v2 paths (never trusted for price).\n // Values are inlined (this API ignores GraphQL variables); all are validated\n // addresses / a decimal number / a fixed enum, so nothing user-shaped is spliced.\n const query_ = `query { sorGetSwapPaths(chain: ${chain.api}, swapAmount: \"${human(amountIn, src.decimals)}\", swapType: EXACT_IN, tokenIn: \"${tokenIn}\", tokenOut: \"${tokenOut}\", useProtocolVersion: 2) { protocolVersion paths { pools tokens { address decimals } inputAmountRaw outputAmountRaw protocolVersion } } }`;\n const res = await fetch(api, {\n method: \"POST\",\n headers: {\"content-type\": \"application/json\"},\n body: JSON.stringify({query: query_}),\n signal: signal ?? timeoutSignal(12_000),\n });\n if (!res.ok) return refuse(`SOR answered HTTP ${res.status}`);\n const body = (await res.json()) as {data?: {sorGetSwapPaths?: {protocolVersion?: number; paths?: SorPath[]}}; errors?: {message?: string}[]};\n const paths = body.data?.sorGetSwapPaths?.paths ?? [];\n if (body.errors?.length) return refuse(`SOR: ${body.errors[0]?.message?.slice(0, 120) ?? \"error\"}`);\n if (!paths.length || paths.some((p) => p.protocolVersion !== 2 || p.pools.length === 0 || p.tokens.length !== p.pools.length + 1)) {\n return refuse(\"no Balancer v2 pool route for this pair and amount\");\n }\n if (paths.reduce((s, p) => s + BigInt(p.inputAmountRaw), 0n) !== amountIn) return refuse(\"the SOR split does not add up to the amount\");\n\n // 2) Build the batch from the suggested paths.\n const assets: Hex[] = [];\n const indexOf = (a: string) => {\n const addr = getAddress(a);\n let i = assets.findIndex((x) => x === addr);\n if (i < 0) i = assets.push(addr) - 1;\n return BigInt(i);\n };\n const swaps: Step[] = [];\n for (const p of paths) {\n if (getAddress(p.tokens[0]!.address) !== tokenIn || getAddress(p.tokens[p.tokens.length - 1]!.address) !== tokenOut) return refuse(\"the SOR path does not start/end at the requested tokens\");\n p.pools.forEach((pool, i) => {\n if (!/^0x[0-9a-fA-F]{64}$/.test(pool)) throw new Error(\"balancer: malformed pool id from the SOR\");\n swaps.push({poolId: pool as Hex, assetInIndex: indexOf(p.tokens[i]!.address), assetOutIndex: indexOf(p.tokens[i + 1]!.address), amount: i === 0 ? BigInt(p.inputAmountRaw) : 0n, userData: \"0x\"});\n });\n }\n const inIdx = bigintToNumber(indexOf(tokenIn));\n const outIdx = bigintToNumber(indexOf(tokenOut));\n // The Vault's ETH sentinel: native in/out wraps/unwraps through its WETH.\n if (nativeIn || nativeOut) {\n const wethIdx = assets.findIndex((a) => a === getAddress(chain.weth));\n if (paths.some((p) => p.tokens.slice(1, -1).some((t) => getAddress(t.address) === getAddress(chain.weth)))) return refuse(\"this route passes through the wrapped native — pick the wrapped token instead\");\n if (wethIdx >= 0) assets[wethIdx] = ZERO as Hex;\n }\n const sender = getAddress(params.refundAddress && /^0x[0-9a-fA-F]{40}$/.test(params.refundAddress) ? params.refundAddress : params.destAddress);\n const recipient = getAddress(params.destAddress);\n const funds: Funds = {sender, fromInternalBalance: false, recipient, toInternalBalance: false};\n\n // 3) Price it on-chain — this, not the SOR's number, is the quote.\n const deltas = await query(chainId, swaps, assets, funds);\n const paid = deltas[inIdx]!;\n const out = -deltas[outIdx]!;\n if (paid !== amountIn) return refuse(`the Vault would take ${paid}, not ${amountIn}`);\n if (out <= 0n) return refuse(\"the Vault prices this route at zero output\");\n if (deltas.some((d, i) => i !== inIdx && i !== outIdx && d !== 0n)) return refuse(\"the batch leaves an intermediate balance — refusing\");\n\n const bps = params.slippageBps ?? slippageDefault;\n const minOut = (out * BigInt(10_000 - bps)) / 10_000n;\n if (params.minAmountOut !== undefined && minOut < BigInt(params.minAmountOut)) return refuse(`floor breach (min=${minOut} < floor=${params.minAmountOut})`);\n const limits = assets.map((_, i) => (i === inIdx ? amountIn : i === outIdx ? -minOut : 0n));\n const deadline = BigInt(Math.floor(Date.now() / 1000) + deadlineS);\n const data = encodeFunctionData({abi: VAULT_ABI, functionName: \"batchSwap\", args: [0, swaps, assets, funds, limits, deadline]});\n const gasLimit = 160_000n + 95_000n * BigInt(swaps.length);\n return {\n provider: \"balancer\",\n intentId: `balancer:${chainId}`,\n depositAddress: \"\",\n depositMemo: null,\n txRequest: {\n to: BALANCER_V2_VAULT,\n data,\n value: nativeIn ? amountIn.toString() : \"0\",\n chainId,\n gasLimit: gasLimit.toString(),\n ...(nativeIn ? {} : {approvalAddress: BALANCER_V2_VAULT}),\n },\n sourceAmount: amountIn.toString(),\n destAmount: out.toString(),\n minDestAmount: minOut.toString(),\n feeUsd: 0,\n etaSeconds: 15,\n reliability: 0.97,\n validUntilMs: Date.now() + ttl,\n // Pool swap fees are inside the Vault-priced output; no fee is added on top.\n providerData: {paths: String(paths.length), hops: String(swaps.length), minOut: minOut.toString(), poolFee: \"pools\"},\n };\n } catch (e) {\n return refuse(e instanceof Error ? e.message.replace(/^balancer: /, \"\") : String(e));\n }\n },\n\n /** Re-price the exact signed batch right before broadcast; refuse when the pools now pay under the minimum. */\n async preflightDeposit(quote: Quote, tx: UnsignedTx): Promise<void> {\n const chainId = Number(quote.intentId.split(\":\")[1]);\n const minOut = quote.providerData?.minOut;\n if (!BALANCER_V2_CHAINS[chainId] || !minOut || !tx.data) return;\n const call = decodeFunctionData({abi: VAULT_ABI, data: tx.data as Hex});\n if (call.functionName !== \"batchSwap\") throw new Error(\"balancer: the transaction is not a Vault batchSwap\");\n const [, swaps, assets, funds, limits] = call.args;\n const outIdx = limits.findIndex((l) => l < 0n);\n const deltas = await query(chainId, swaps, assets, funds);\n const out = -deltas[outIdx]!;\n if (out < BigInt(minOut)) throw new Error(`balancer: price moved — the pools now pay ${out}, under your minimum ${minOut}. Nothing was sent; refresh the quote.`);\n },\n\n async buildDeposit(quote: Quote): Promise<UnsignedTx> {\n const tx = quote.txRequest;\n if (!tx?.to || !tx.data) throw new Error(\"swap2: Balancer quote has no transaction request\");\n return {chain: \"\", to: tx.to, value: tx.value ?? \"0\", data: tx.data, ...(tx.chainId !== undefined ? {chainId: tx.chainId} : {}), ...(tx.gasLimit ? {gasLimit: tx.gasLimit} : {}), ...(tx.approvalAddress ? {approvalAddress: tx.approvalAddress} : {})};\n },\n\n async trackIntent(ref: IntentRef): Promise<IntentStatus> {\n if (!ref.sourceTxHash) return {state: \"pending\"};\n const chainId = Number(ref.intentId.split(\":\")[1]);\n if (!BALANCER_V2_CHAINS[chainId] || !config.rpcUrls[chainId]?.length) return {state: \"pending\"};\n try {\n const receipt = (await rpc(chainId, \"eth_getTransactionReceipt\", [ref.sourceTxHash])) as {status?: string} | null;\n if (!receipt) return {state: \"pending\"};\n return receipt.status === \"0x1\" ? {state: \"completed\", destTxHash: ref.sourceTxHash} : {state: \"failed\"};\n } catch {\n return {state: \"pending\"};\n }\n },\n };\n}\n"],"mappings":";AAMA,SAAQ,YAAY,kBAAkB,mBAAkB;AACxD,SAAQ,UAAAA,SAAQ,SAAAC,cAAY;;;ACP5B,SAAQ,oBAAoB,sBAAsB,oBAAoB,YAAY,gBAAyB;AAE3G,SAAQ,QAAQ,OAAO,eAAe,qBAAoB;AAE1D,SAAQ,sBAAqB;AAa7B,IAAM,OAAO;AAEN,IAAM,oBAAoB;AACjC,IAAM,cAAc;AACpB,IAAM,uBAAuB;AAC7B,IAAM,iBAAiB;AACvB,IAAM,qBAAqB;AAGpB,IAAM,qBAAmF,OAAO,OAAO;AAAA,EAC5G,GAAG,EAAC,KAAK,WAAW,MAAM,6CAA4C;AAAA,EACtE,IAAI,EAAC,KAAK,YAAY,MAAM,6CAA4C;AAAA,EACxE,KAAK,EAAC,KAAK,UAAU,MAAM,6CAA4C;AAAA,EACvE,KAAK,EAAC,KAAK,WAAW,MAAM,6CAA4C;AAAA,EACxE,KAAK,EAAC,KAAK,SAAS,MAAM,6CAA4C;AAAA,EACtE,MAAM,EAAC,KAAK,QAAQ,MAAM,6CAA4C;AAAA,EACtE,MAAM,EAAC,KAAK,UAAU,MAAM,6CAA4C;AAAA,EACxE,OAAO,EAAC,KAAK,YAAY,MAAM,6CAA4C;AAAA,EAC3E,OAAO,EAAC,KAAK,aAAa,MAAM,6CAA4C;AAC9E,CAAC;AAED,IAAM,YAAY;AAClB,IAAM,QAAQ;AACd,IAAM,YAAY,SAAS;AAAA,EACzB,uCAAuC,SAAS,+BAA+B,KAAK;AAAA,EACpF,kCAAkC,SAAS,+BAA+B,KAAK;AAAA,EAC/E;AACF,CAAC;AAyBD,SAAS,MAAM,QAAgB,UAA0B;AACvD,QAAM,IAAI,OAAO,SAAS,EAAE,SAAS,WAAW,GAAG,GAAG;AACtD,QAAM,QAAQ,EAAE,MAAM,GAAG,EAAE,SAAS,QAAQ;AAC5C,QAAM,OAAO,EAAE,MAAM,EAAE,SAAS,QAAQ,EAAE,QAAQ,OAAO,EAAE;AAC3D,SAAO,OAAO,GAAG,KAAK,IAAI,IAAI,KAAK;AACrC;AAEO,SAAS,iBAAiB,QAAoD;AACnF,QAAM,MAAM,OAAO,UAAU;AAC7B,QAAM,kBAAkB,OAAO,sBAAsB;AACrD,QAAM,MAAM,OAAO,cAAc;AACjC,QAAM,YAAY,OAAO,mBAAmB;AAC5C,QAAM,WAAW,oBAAI,IAA2B;AAEhD,QAAM,YAAY,CAAC,UAAiC;AAClD,QAAI,CAAC,MAAM,KAAK,EAAG,QAAO;AAC1B,UAAM,KAAK,OAAO,KAAK;AACvB,WAAO,mBAAmB,EAAE,KAAK,OAAO,QAAQ,EAAE,GAAG,SAAS,KAAK;AAAA,EACrE;AAEA,iBAAe,IAAI,SAAiB,QAAgB,QAAqC;AACvF,QAAI;AACJ,eAAW,OAAO,OAAO,QAAQ,OAAO,KAAK,CAAC,GAAG;AAC/C,UAAI;AACF,cAAM,MAAM,MAAM,MAAM,KAAK,EAAC,QAAQ,QAAQ,SAAS,EAAC,gBAAgB,mBAAkB,GAAG,MAAM,KAAK,UAAU,EAAC,SAAS,OAAO,IAAI,GAAG,QAAQ,OAAM,CAAC,GAAG,QAAQ,cAAc,IAAM,EAAC,CAAC;AAC1L,YAAI,CAAC,IAAI,GAAI,OAAM,IAAI,MAAM,QAAQ,IAAI,MAAM,EAAE;AACjD,cAAM,OAAQ,MAAM,IAAI,KAAK;AAC7B,YAAI,KAAK,MAAO,OAAM,OAAO,OAAO,IAAI,MAAM,KAAK,MAAM,WAAW,WAAW,GAAG,EAAC,KAAK,KAAI,CAAC;AAC7F,eAAO,KAAK,UAAU;AAAA,MACxB,SAAS,GAAG;AACV,YAAK,EAAsB,IAAK,OAAM;AACtC,kBAAU;AAAA,MACZ;AAAA,IACF;AACA,UAAM,IAAI,MAAM,sCAAsC,OAAO,KAAK,mBAAmB,QAAQ,QAAQ,UAAU,OAAO,OAAO,CAAC,GAAG;AAAA,EACnI;AAGA,WAAS,OAAO,SAAgC;AAC9C,QAAI,IAAI,SAAS,IAAI,OAAO;AAC5B,QAAI,CAAC,GAAG;AACN,WAAK,YAAY;AACf,cAAM,MAAO,MAAM,IAAI,SAAS,YAAY,CAAC,EAAC,IAAI,mBAAmB,MAAM,mBAAmB,EAAC,KAAK,WAAW,cAAc,OAAM,CAAC,EAAC,GAAG,QAAQ,CAAC;AACjJ,cAAM,OAAO,qBAAqB,EAAC,KAAK,WAAW,cAAc,QAAQ,MAAM,IAAG,CAAC;AACnF,YAAI,WAAW,IAAI,MAAM,WAAW,mBAAmB,OAAO,EAAG,IAAI,EAAG,OAAM,IAAI,MAAM,gCAAgC,OAAO,+CAA0C;AAAA,MAC3K,GAAG;AACH,QAAE,MAAM,MAAM,SAAS,OAAO,OAAO,CAAC;AACtC,eAAS,IAAI,SAAS,CAAC;AAAA,IACzB;AACA,WAAO;AAAA,EACT;AAGA,iBAAe,MAAM,SAAiB,OAAwB,QAAwB,OAA0C;AAC9H,UAAM,OAAO,mBAAmB,EAAC,KAAK,WAAW,cAAc,kBAAkB,MAAM,CAAC,GAAG,OAAO,QAAQ,KAAK,EAAC,CAAC;AACjH,UAAM,MAAO,MAAM,IAAI,SAAS,YAAY,CAAC,EAAC,IAAI,mBAAmB,KAAI,GAAG,QAAQ,CAAC;AACrF,WAAO,qBAAqB,EAAC,KAAK,WAAW,cAAc,kBAAkB,MAAM,IAAG,CAAC;AAAA,EACzF;AAEA,SAAO;AAAA,IACL,MAAM;AAAA,IACN,kBAAkB;AAAA,IAClB,mBAAmB;AAAA,IACnB,SAAS,MAAM,OAAO,WAAW;AAAA,IACjC,eAAe,CAAC,UAAkB,UAAU,KAAK,MAAM;AAAA,IACvD,mBAAmB,CAAC,MAAc,OAAe,SAAS,MAAM,UAAU,IAAI,MAAM;AAAA,IACpF,eAAe,CAAC,MAAa,OAAc,KAAK,UAAU,GAAG,SAAS,UAAU,KAAK,KAAK,MAAM;AAAA,IAEhG,MAAM,SAAS,QAAqB,QAA6C;AAC/E,YAAM,SAAS,CAAC,QAAsB;AACpC,eAAO,YAAY,aAAa,GAAG,EAAE;AACrC,eAAO;AAAA,MACT;AACA,UAAI,OAAO,KAAM,QAAO,OAAO,4CAA4C;AAC3E,UAAI,OAAO,gBAAgB,OAAO,UAAW,QAAO,OAAO,uBAAuB;AAClF,YAAM,UAAU,UAAU,OAAO,WAAW;AAC5C,UAAI,YAAY,KAAM,QAAO,OAAO,mCAAmC,OAAO,WAAW,EAAE;AAC3F,YAAM,QAAQ,mBAAmB,OAAO;AACxC,YAAM,MAAM,cAAc,MAAM;AAChC,UAAI,CAAC,IAAK,QAAO,OAAO,wBAAwB,OAAO,WAAW,EAAE;AACpE,YAAM,WAAW,CAAC,IAAI;AACtB,YAAM,YAAY,SAAS,KAAK,OAAO,SAAS;AAChD,UAAI,CAAC,aAAa,CAAC,sBAAsB,KAAK,OAAO,SAAS,EAAG,QAAO,OAAO,0CAA0C;AACzH,UAAI,CAAC,sBAAsB,KAAK,OAAO,WAAW,EAAG,QAAO,OAAO,kCAAkC;AACrG,UAAI;AACJ,UAAI;AACF,mBAAW,OAAO,OAAO,QAAQ;AAAA,MACnC,QAAQ;AACN,eAAO,OAAO,0BAA0B;AAAA,MAC1C;AACA,UAAI,YAAY,GAAI,QAAO,OAAO,yBAAyB;AAC3D,YAAM,UAAU,WAAW,WAAW,MAAM,OAAO,IAAI,QAAS;AAChE,YAAM,WAAW,WAAW,YAAY,MAAM,OAAO,OAAO,SAAS;AACrE,UAAI,YAAY,SAAU,QAAO,OAAO,YAAY,YAAY,sCAAsC,uBAAuB;AAE7H,UAAI;AACF,cAAM,OAAO,OAAO;AAIpB,cAAM,SAAS,kCAAkC,MAAM,GAAG,kBAAkB,MAAM,UAAU,IAAI,QAAQ,CAAC,oCAAoC,OAAO,iBAAiB,QAAQ;AAC7K,cAAM,MAAM,MAAM,MAAM,KAAK;AAAA,UAC3B,QAAQ;AAAA,UACR,SAAS,EAAC,gBAAgB,mBAAkB;AAAA,UAC5C,MAAM,KAAK,UAAU,EAAC,OAAO,OAAM,CAAC;AAAA,UACpC,QAAQ,UAAU,cAAc,IAAM;AAAA,QACxC,CAAC;AACD,YAAI,CAAC,IAAI,GAAI,QAAO,OAAO,qBAAqB,IAAI,MAAM,EAAE;AAC5D,cAAM,OAAQ,MAAM,IAAI,KAAK;AAC7B,cAAM,QAAQ,KAAK,MAAM,iBAAiB,SAAS,CAAC;AACpD,YAAI,KAAK,QAAQ,OAAQ,QAAO,OAAO,QAAQ,KAAK,OAAO,CAAC,GAAG,SAAS,MAAM,GAAG,GAAG,KAAK,OAAO,EAAE;AAClG,YAAI,CAAC,MAAM,UAAU,MAAM,KAAK,CAAC,MAAM,EAAE,oBAAoB,KAAK,EAAE,MAAM,WAAW,KAAK,EAAE,OAAO,WAAW,EAAE,MAAM,SAAS,CAAC,GAAG;AACjI,iBAAO,OAAO,oDAAoD;AAAA,QACpE;AACA,YAAI,MAAM,OAAO,CAAC,GAAG,MAAM,IAAI,OAAO,EAAE,cAAc,GAAG,EAAE,MAAM,SAAU,QAAO,OAAO,6CAA6C;AAGtI,cAAM,SAAgB,CAAC;AACvB,cAAM,UAAU,CAAC,MAAc;AAC7B,gBAAM,OAAO,WAAW,CAAC;AACzB,cAAI,IAAI,OAAO,UAAU,CAAC,MAAM,MAAM,IAAI;AAC1C,cAAI,IAAI,EAAG,KAAI,OAAO,KAAK,IAAI,IAAI;AACnC,iBAAO,OAAO,CAAC;AAAA,QACjB;AACA,cAAM,QAAgB,CAAC;AACvB,mBAAW,KAAK,OAAO;AACrB,cAAI,WAAW,EAAE,OAAO,CAAC,EAAG,OAAO,MAAM,WAAW,WAAW,EAAE,OAAO,EAAE,OAAO,SAAS,CAAC,EAAG,OAAO,MAAM,SAAU,QAAO,OAAO,yDAAyD;AAC5L,YAAE,MAAM,QAAQ,CAAC,MAAM,MAAM;AAC3B,gBAAI,CAAC,sBAAsB,KAAK,IAAI,EAAG,OAAM,IAAI,MAAM,0CAA0C;AACjG,kBAAM,KAAK,EAAC,QAAQ,MAAa,cAAc,QAAQ,EAAE,OAAO,CAAC,EAAG,OAAO,GAAG,eAAe,QAAQ,EAAE,OAAO,IAAI,CAAC,EAAG,OAAO,GAAG,QAAQ,MAAM,IAAI,OAAO,EAAE,cAAc,IAAI,IAAI,UAAU,KAAI,CAAC;AAAA,UAClM,CAAC;AAAA,QACH;AACA,cAAM,QAAQ,eAAe,QAAQ,OAAO,CAAC;AAC7C,cAAM,SAAS,eAAe,QAAQ,QAAQ,CAAC;AAE/C,YAAI,YAAY,WAAW;AACzB,gBAAM,UAAU,OAAO,UAAU,CAAC,MAAM,MAAM,WAAW,MAAM,IAAI,CAAC;AACpE,cAAI,MAAM,KAAK,CAAC,MAAM,EAAE,OAAO,MAAM,GAAG,EAAE,EAAE,KAAK,CAAC,MAAM,WAAW,EAAE,OAAO,MAAM,WAAW,MAAM,IAAI,CAAC,CAAC,EAAG,QAAO,OAAO,oFAA+E;AACzM,cAAI,WAAW,EAAG,QAAO,OAAO,IAAI;AAAA,QACtC;AACA,cAAM,SAAS,WAAW,OAAO,iBAAiB,sBAAsB,KAAK,OAAO,aAAa,IAAI,OAAO,gBAAgB,OAAO,WAAW;AAC9I,cAAM,YAAY,WAAW,OAAO,WAAW;AAC/C,cAAM,QAAe,EAAC,QAAQ,qBAAqB,OAAO,WAAW,mBAAmB,MAAK;AAG7F,cAAM,SAAS,MAAM,MAAM,SAAS,OAAO,QAAQ,KAAK;AACxD,cAAM,OAAO,OAAO,KAAK;AACzB,cAAM,MAAM,CAAC,OAAO,MAAM;AAC1B,YAAI,SAAS,SAAU,QAAO,OAAO,wBAAwB,IAAI,SAAS,QAAQ,EAAE;AACpF,YAAI,OAAO,GAAI,QAAO,OAAO,4CAA4C;AACzE,YAAI,OAAO,KAAK,CAAC,GAAG,MAAM,MAAM,SAAS,MAAM,UAAU,MAAM,EAAE,EAAG,QAAO,OAAO,0DAAqD;AAEvI,cAAM,MAAM,OAAO,eAAe;AAClC,cAAM,SAAU,MAAM,OAAO,MAAS,GAAG,IAAK;AAC9C,YAAI,OAAO,iBAAiB,UAAa,SAAS,OAAO,OAAO,YAAY,EAAG,QAAO,OAAO,qBAAqB,MAAM,YAAY,OAAO,YAAY,GAAG;AAC1J,cAAM,SAAS,OAAO,IAAI,CAAC,GAAG,MAAO,MAAM,QAAQ,WAAW,MAAM,SAAS,CAAC,SAAS,EAAG;AAC1F,cAAM,WAAW,OAAO,KAAK,MAAM,KAAK,IAAI,IAAI,GAAI,IAAI,SAAS;AACjE,cAAM,OAAO,mBAAmB,EAAC,KAAK,WAAW,cAAc,aAAa,MAAM,CAAC,GAAG,OAAO,QAAQ,OAAO,QAAQ,QAAQ,EAAC,CAAC;AAC9H,cAAM,WAAW,UAAW,SAAU,OAAO,MAAM,MAAM;AACzD,eAAO;AAAA,UACL,UAAU;AAAA,UACV,UAAU,YAAY,OAAO;AAAA,UAC7B,gBAAgB;AAAA,UAChB,aAAa;AAAA,UACb,WAAW;AAAA,YACT,IAAI;AAAA,YACJ;AAAA,YACA,OAAO,WAAW,SAAS,SAAS,IAAI;AAAA,YACxC;AAAA,YACA,UAAU,SAAS,SAAS;AAAA,YAC5B,GAAI,WAAW,CAAC,IAAI,EAAC,iBAAiB,kBAAiB;AAAA,UACzD;AAAA,UACA,cAAc,SAAS,SAAS;AAAA,UAChC,YAAY,IAAI,SAAS;AAAA,UACzB,eAAe,OAAO,SAAS;AAAA,UAC/B,QAAQ;AAAA,UACR,YAAY;AAAA,UACZ,aAAa;AAAA,UACb,cAAc,KAAK,IAAI,IAAI;AAAA;AAAA,UAE3B,cAAc,EAAC,OAAO,OAAO,MAAM,MAAM,GAAG,MAAM,OAAO,MAAM,MAAM,GAAG,QAAQ,OAAO,SAAS,GAAG,SAAS,QAAO;AAAA,QACrH;AAAA,MACF,SAAS,GAAG;AACV,eAAO,OAAO,aAAa,QAAQ,EAAE,QAAQ,QAAQ,eAAe,EAAE,IAAI,OAAO,CAAC,CAAC;AAAA,MACrF;AAAA,IACF;AAAA;AAAA,IAGA,MAAM,iBAAiB,OAAc,IAA+B;AAClE,YAAM,UAAU,OAAO,MAAM,SAAS,MAAM,GAAG,EAAE,CAAC,CAAC;AACnD,YAAM,SAAS,MAAM,cAAc;AACnC,UAAI,CAAC,mBAAmB,OAAO,KAAK,CAAC,UAAU,CAAC,GAAG,KAAM;AACzD,YAAM,OAAO,mBAAmB,EAAC,KAAK,WAAW,MAAM,GAAG,KAAW,CAAC;AACtE,UAAI,KAAK,iBAAiB,YAAa,OAAM,IAAI,MAAM,oDAAoD;AAC3G,YAAM,CAAC,EAAE,OAAO,QAAQ,OAAO,MAAM,IAAI,KAAK;AAC9C,YAAM,SAAS,OAAO,UAAU,CAAC,MAAM,IAAI,EAAE;AAC7C,YAAM,SAAS,MAAM,MAAM,SAAS,OAAO,QAAQ,KAAK;AACxD,YAAM,MAAM,CAAC,OAAO,MAAM;AAC1B,UAAI,MAAM,OAAO,MAAM,EAAG,OAAM,IAAI,MAAM,kDAA6C,GAAG,wBAAwB,MAAM,wCAAwC;AAAA,IAClK;AAAA,IAEA,MAAM,aAAa,OAAmC;AACpD,YAAM,KAAK,MAAM;AACjB,UAAI,CAAC,IAAI,MAAM,CAAC,GAAG,KAAM,OAAM,IAAI,MAAM,kDAAkD;AAC3F,aAAO,EAAC,OAAO,IAAI,IAAI,GAAG,IAAI,OAAO,GAAG,SAAS,KAAK,MAAM,GAAG,MAAM,GAAI,GAAG,YAAY,SAAY,EAAC,SAAS,GAAG,QAAO,IAAI,CAAC,GAAI,GAAI,GAAG,WAAW,EAAC,UAAU,GAAG,SAAQ,IAAI,CAAC,GAAI,GAAI,GAAG,kBAAkB,EAAC,iBAAiB,GAAG,gBAAe,IAAI,CAAC,EAAE;AAAA,IACxP;AAAA,IAEA,MAAM,YAAY,KAAuC;AACvD,UAAI,CAAC,IAAI,aAAc,QAAO,EAAC,OAAO,UAAS;AAC/C,YAAM,UAAU,OAAO,IAAI,SAAS,MAAM,GAAG,EAAE,CAAC,CAAC;AACjD,UAAI,CAAC,mBAAmB,OAAO,KAAK,CAAC,OAAO,QAAQ,OAAO,GAAG,OAAQ,QAAO,EAAC,OAAO,UAAS;AAC9F,UAAI;AACF,cAAM,UAAW,MAAM,IAAI,SAAS,6BAA6B,CAAC,IAAI,YAAY,CAAC;AACnF,YAAI,CAAC,QAAS,QAAO,EAAC,OAAO,UAAS;AACtC,eAAO,QAAQ,WAAW,QAAQ,EAAC,OAAO,aAAa,YAAY,IAAI,aAAY,IAAI,EAAC,OAAO,SAAQ;AAAA,MACzG,QAAQ;AACN,eAAO,EAAC,OAAO,UAAS;AAAA,MAC1B;AAAA,IACF;AAAA,EACF;AACF;;;ADvRO,IAAM,kBAAkB;AAGxB,IAAM,2BAAgD,oBAAI,IAAI;AAAA,EACnE;AAAA;AAAA,EACA;AAAA;AACF,CAAC;AAEM,SAAS,yBAAyB,IAAa,KAAmD;AACvG,MAAI,CAACC,OAAM,GAAG,KAAK,KAAK,CAAC,mBAAmBC,QAAO,GAAG,KAAK,CAAC,EAAG,QAAO;AACtE,MAAI,CAAC,YAAY,IAAI,CAAC,iBAAiB,GAAG,wBAAwB,EAAG,QAAO;AAC5E,SAAO,iBAAiB,IAAI,KAAK,EAAC,UAAU,iBAAiB,UAAU,oBAAiB,WAAW,GAAG,KAAK,CAAC,IAAI,MAAM,OAAM,CAAC;AAC/H;","names":["evmRef","isEvm","isEvm","evmRef"]}