@anywalletkit/protocol-swap2-balancer 0.3.5 → 0.3.7
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/dist/activity.cjs +25 -22
- package/dist/activity.cjs.map +1 -1
- package/dist/activity.js +1 -1
- package/dist/{chunk-JFRZJ6N5.js → chunk-AENUP77U.js} +2 -2
- package/dist/{chunk-YF3UW4GH.js → chunk-XFRLZ25H.js} +75 -57
- package/dist/chunk-XFRLZ25H.js.map +1 -0
- package/dist/index.cjs +74 -56
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +2 -2
- package/dist/protocol.cjs +74 -56
- package/dist/protocol.cjs.map +1 -1
- package/dist/protocol.js +2 -2
- package/dist/provider.cjs +74 -56
- package/dist/provider.cjs.map +1 -1
- package/dist/provider.d.cts +1 -1
- package/dist/provider.d.ts +1 -1
- package/dist/provider.js +1 -1
- package/package.json +6 -6
- package/dist/chunk-YF3UW4GH.js.map +0 -1
- /package/dist/{chunk-JFRZJ6N5.js.map → chunk-AENUP77U.js.map} +0 -0
package/dist/index.cjs.map
CHANGED
|
@@ -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 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 batch a Balancer quote's Vault call makes, opened on the client against the app's own request (scan 2026-10-28,\n * F2): only `batchSwap` of kind GIVEN_IN on the Vault, with no internal balances and no pool `userData`, at most\n * {@link BALANCER_MAX_SWAPS} hops; funds taken from the sender the provider names (the refund address, else the\n * recipient) and paid to the recipient; every swap that brings in an amount sells the source token (the Vault's ETH\n * sentinel for a native input), the amounts adding up to exactly the input; one hop delivers the destination token;\n * and the limits allow paying exactly the input and receiving at least the quote's minimum, nothing of anything else.\n * `manageUserBalance`, `swap`, `setRelayerApproval` and every other Vault call are refused. The reason it fails, or null.\n */\nexport function balancerVerifyQuote(params: QuoteParams, quote: Quote): string | null {\n const id = /^eip155:[0-9]+$/.test(params.sourceChain) ? Number(params.sourceChain.slice(7)) : NaN;\n const chain = Object.prototype.hasOwnProperty.call(BALANCER_V2_CHAINS, id) ? BALANCER_V2_CHAINS[id] : undefined;\n if (!chain || params.destChain !== params.sourceChain) return \"not a same-chain swap on a Balancer v2 chain\";\n const tx = quote.txRequest;\n if (!tx?.data || !tx.to || tx.serializedTx !== undefined) return \"the quote carries no Vault call\";\n if (getAddress(tx.to) !== BALANCER_V2_VAULT) return `the call targets ${tx.to}, not the Vault`;\n const src = sourceAssetOf(params);\n if (!src) return `unknown source token ${params.sourceToken}`;\n const nativeIn = !src.contract;\n const nativeOut = /^0x0+$/.test(params.destToken);\n let tokenIn: Hex, tokenOut: Hex, sender: Hex, recipient: Hex, amountIn: bigint, minOut: bigint;\n try {\n tokenIn = getAddress(nativeIn ? ZERO : src.contract!);\n tokenOut = getAddress(nativeOut ? ZERO : params.destToken);\n recipient = getAddress(params.destAddress);\n sender = getAddress(params.refundAddress && /^0x[0-9a-fA-F]{40}$/.test(params.refundAddress) ? params.refundAddress : params.destAddress);\n amountIn = BigInt(params.amountIn);\n minOut = BigInt(quote.minDestAmount);\n } catch {\n return \"the request does not name EVM tokens, a recipient and amounts\";\n }\n if (nativeIn ? BigInt(tx.value ?? \"0\") !== amountIn || tx.approvalAddress !== undefined\n : BigInt(tx.value ?? \"0\") !== 0n || !tx.approvalAddress || getAddress(tx.approvalAddress) !== BALANCER_V2_VAULT) {\n return nativeIn ? \"a native input is paid as exactly the input in value, with no approval\" : \"a token input attaches no value and approves the Vault\";\n }\n let call: ReturnType<typeof decodeFunctionData<typeof VAULT_ABI>>;\n try { call = decodeFunctionData({abi: VAULT_ABI, data: tx.data as Hex}); } catch { return \"the Vault call is not batchSwap\"; }\n if (call.functionName !== \"batchSwap\") return `${call.functionName} is not batchSwap`;\n const [kind, swaps, assets, funds, limits] = call.args;\n if (kind !== 0) return \"the batch is not GIVEN_IN\";\n if (swaps.length === 0 || swaps.length > BALANCER_MAX_SWAPS) return `the batch has ${swaps.length} pool hops, not 1–${BALANCER_MAX_SWAPS}`;\n if (funds.fromInternalBalance || funds.toInternalBalance) return \"the batch uses Vault internal balances\";\n if (getAddress(funds.sender) !== sender || getAddress(funds.recipient) !== recipient) return `the batch takes from ${funds.sender} and pays ${funds.recipient}, not this account and the recipient`;\n const list = assets.map((a) => getAddress(a));\n if (new Set(list).size !== list.length) return \"the batch names an asset twice\";\n if ((nativeIn || nativeOut) && list.includes(getAddress(chain.weth))) return \"a native swap passes through the wrapped native\";\n const inIdx = list.indexOf(tokenIn);\n const outIdx = list.indexOf(tokenOut);\n if (inIdx < 0 || outIdx < 0 || inIdx === outIdx) return \"the batch does not trade the requested tokens\";\n if (limits.length !== list.length || limits.some((l, i) => l !== (i === inIdx ? amountIn : i === outIdx ? -minOut : 0n))) {\n return \"the limits are not exactly the input in and the quote's minimum out\";\n }\n let given = 0n;\n for (const step of swaps) {\n if (step.userData !== \"0x\") return \"the batch carries pool userData this module never sends\";\n if (step.assetInIndex >= BigInt(list.length) || step.assetOutIndex >= BigInt(list.length) || step.assetInIndex === step.assetOutIndex) return \"a hop names no valid asset pair\";\n if (step.amount > 0n) {\n if (step.assetInIndex !== BigInt(inIdx)) return \"a hop brings in an amount of another asset than the input\";\n given += step.amount;\n }\n }\n if (given !== amountIn) return `the hops sell ${given}, not the ${amountIn} input`;\n if (!swaps.some((step) => step.assetOutIndex === BigInt(outIdx))) return \"no hop delivers the destination token\";\n if (call.args[5] <= BigInt(Math.floor(Date.now() / 1000))) return \"the batch deadline has passed\";\n return null;\n}\n\n/**\n * The Balancer V2 Vault a swap calls and approves on the chains this provider serves, and {@link balancerVerifyQuote}\n * — the app's pins for `remoteProvider({name: \"balancer\", baseUrl, ...balancerPins})` (scan 2026-10-27 modules,\n * F8/F16; scan 2026-10-28, F2).\n */\nexport const balancerPins: RemotePins = Object.freeze({\n callTargets: (chain: string): readonly string[] => balancerVaultFor(chain),\n spenders: (chain: string): readonly string[] => balancerVaultFor(chain),\n verifyQuote: balancerVerifyQuote,\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"]}
|
|
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 {httpRefusal, refusal, refusalNotes, refusalOf, withReason, type Swap2RefusalInfo, type Swap2RefusalReason} from \"@anywalletkit/protocol-swap2-core/errors\";\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/** The reasons of the texts {@link balancerVerifyQuote} returned, for `balancerPins.reasonOf`. */\nconst notes = refusalNotes();\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) {\n const error = new Error(`HTTP ${res.status}`);\n const info = httpRefusal(res.status, res.headers?.get?.(\"retry-after\"));\n throw info ? withReason(error, info) : error;\n }\n const body = (await res.json()) as {result?: unknown; error?: {code?: unknown; message?: string}};\n if (body.error) throw Object.assign(new Error(body.error.message ?? \"rpc error\"), {rpc: true, rpcCode: body.error.code});\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 // Every endpoint failed: the last one's own classification (a 429, a rejected key), else unavailable.\n throw withReason(new Error(`balancer: no RPC answered on chain ${chainId} (${lastErr instanceof Error ? lastErr.message : String(lastErr)})`), refusalOf(lastErr) ?? \"upstream_unavailable\");\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 refusal(\"vault_mismatch\", `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 /**\n * Vault.queryBatchSwap on chain: the asset deltas this exact batch would produce. A JSON-RPC error that says the\n * call reverted (code 3, Nethermind's -32015, a \"revert\" message) is the Vault reverting the batch\n * (`simulation_failed`); any other JSON-RPC error (\"header not found\", \"missing trie node\", a limit, an internal\n * error) and an answer that does not decode are the node's (`upstream_unavailable`).\n */\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 let raw: Hex;\n try {\n raw = (await rpc(chainId, \"eth_call\", [{to: BALANCER_V2_VAULT, data}, \"latest\"])) as Hex;\n } catch (e) {\n if (!(e instanceof Error) || !(e as {rpc?: boolean}).rpc) throw e;\n const code = (e as {rpcCode?: unknown}).rpcCode;\n throw withReason(e, code === 3 || code === -32015 || /revert/i.test(e.message) ? \"simulation_failed\" : \"upstream_unavailable\");\n }\n try {\n return decodeFunctionResult({abi: VAULT_ABI, functionName: \"queryBatchSwap\", data: raw});\n } catch (e) {\n throw e instanceof Error ? withReason(e, \"upstream_unavailable\") : e;\n }\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, info?: Swap2RefusalReason | Swap2RefusalInfo): null => {\n params.onRefusal?.(`balancer: ${why}`, typeof info === \"string\" ? {reason: info} : info);\n return null;\n };\n if (params.flex) return refuse(\"amount-flexible deposits are not supported\", \"unsupported_pair\");\n if (params.sourceChain !== params.destChain) return refuse(\"same-chain swaps only\", \"unsupported_pair\");\n const chainId = chainIdOf(params.sourceChain);\n if (chainId === null) return refuse(`no Balancer v2 Vault or RPC for ${params.sourceChain}`, \"unsupported_pair\");\n const chain = BALANCER_V2_CHAINS[chainId]!;\n const src = sourceAssetOf(params);\n if (!src) return refuse(`unknown source token ${params.sourceToken}`, \"unsupported_pair\");\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\", \"unsupported_pair\");\n if (!/^0x[0-9a-fA-F]{40}$/.test(params.destAddress)) return refuse(\"recipient must be an EVM address\", \"recipient_mismatch\");\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\", \"unsupported_pair\");\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 // 429 / 401 / 403 / 408 / 425 / 5xx by status; another 4xx says nothing without the body (a 404 is a misconfigured apiUrl).\n if (!res.ok) return refuse(`SOR answered HTTP ${res.status}`, httpRefusal(res.status, res.headers?.get?.(\"retry-after\")));\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\"}`, \"upstream_unavailable\");\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\", \"no_route\");\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\", \"price_check_failed\");\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\", \"policy_refused\");\n p.pools.forEach((pool, i) => {\n if (!/^0x[0-9a-fA-F]{64}$/.test(pool)) throw refusal(\"upstream_unavailable\", \"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}`, \"policy_refused\");\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\", \"unsupported_pair\");\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}`, \"price_check_failed\");\n if (out <= 0n) return refuse(\"the Vault prices this route at zero output\", \"amount_too_small\");\n if (deltas.some((d, i) => i !== inIdx && i !== outIdx && d !== 0n)) return refuse(\"the batch leaves an intermediate balance — refusing\", \"policy_refused\");\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})`, \"price_check_failed\");\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 // Classified where thrown (the Vault check, the node, a revert); anything else is the SOR or the node\n // answering in another shape, or the network — never the caller's own abort.\n return refuse(e instanceof Error ? e.message.replace(/^balancer: /, \"\") : String(e), refusalOf(e) ?? (signal?.aborted ? undefined : \"upstream_unavailable\"));\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 refusal(\"policy_refused\", \"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 refusal(\"policy_refused\", \"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 refusal(\"policy_refused\", `balancer: the batch has ${swaps.length} pool hops, not 1–${BALANCER_MAX_SWAPS}`);\n if (swaps.some((step) => step.userData !== \"0x\")) throw refusal(\"policy_refused\", \"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 refusal(\"price_check_failed\", `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 refusal(\"policy_refused\", \"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 batch a Balancer quote's Vault call makes, opened on the client against the app's own request (scan 2026-10-28,\n * F2): only `batchSwap` of kind GIVEN_IN on the Vault, with no internal balances and no pool `userData`, at most\n * {@link BALANCER_MAX_SWAPS} hops; funds taken from the sender the provider names (the refund address, else the\n * recipient) and paid to the recipient; every swap that brings in an amount sells the source token (the Vault's ETH\n * sentinel for a native input), the amounts adding up to exactly the input; one hop delivers the destination token;\n * and the limits allow paying exactly the input and receiving at least the quote's minimum, nothing of anything else.\n * `manageUserBalance`, `swap`, `setRelayerApproval` and every other Vault call are refused. The reason it fails, or null.\n */\nexport function balancerVerifyQuote(params: QuoteParams, quote: Quote): string | null {\n const id = /^eip155:[0-9]+$/.test(params.sourceChain) ? Number(params.sourceChain.slice(7)) : NaN;\n const chain = Object.prototype.hasOwnProperty.call(BALANCER_V2_CHAINS, id) ? BALANCER_V2_CHAINS[id] : undefined;\n if (!chain || params.destChain !== params.sourceChain) return notes.note(\"not a same-chain swap on a Balancer v2 chain\", \"unsupported_pair\");\n const tx = quote.txRequest;\n if (!tx?.data || !tx.to || tx.serializedTx !== undefined) return notes.note(\"the quote carries no Vault call\", \"policy_refused\");\n if (getAddress(tx.to) !== BALANCER_V2_VAULT) return notes.note(`the call targets ${tx.to}, not the Vault`, \"vault_mismatch\");\n const src = sourceAssetOf(params);\n if (!src) return notes.note(`unknown source token ${params.sourceToken}`, \"unsupported_pair\");\n const nativeIn = !src.contract;\n const nativeOut = /^0x0+$/.test(params.destToken);\n let tokenIn: Hex, tokenOut: Hex, sender: Hex, recipient: Hex, amountIn: bigint, minOut: bigint;\n try {\n tokenIn = getAddress(nativeIn ? ZERO : src.contract!);\n tokenOut = getAddress(nativeOut ? ZERO : params.destToken);\n recipient = getAddress(params.destAddress);\n sender = getAddress(params.refundAddress && /^0x[0-9a-fA-F]{40}$/.test(params.refundAddress) ? params.refundAddress : params.destAddress);\n amountIn = BigInt(params.amountIn);\n minOut = BigInt(quote.minDestAmount);\n } catch {\n return \"the request does not name EVM tokens, a recipient and amounts\";\n }\n if (nativeIn ? BigInt(tx.value ?? \"0\") !== amountIn || tx.approvalAddress !== undefined\n : BigInt(tx.value ?? \"0\") !== 0n || !tx.approvalAddress || getAddress(tx.approvalAddress) !== BALANCER_V2_VAULT) {\n return notes.note(nativeIn ? \"a native input is paid as exactly the input in value, with no approval\" : \"a token input attaches no value and approves the Vault\", \"policy_refused\");\n }\n let call: ReturnType<typeof decodeFunctionData<typeof VAULT_ABI>>;\n try { call = decodeFunctionData({abi: VAULT_ABI, data: tx.data as Hex}); } catch { return notes.note(\"the Vault call is not batchSwap\", \"policy_refused\"); }\n if (call.functionName !== \"batchSwap\") return notes.note(`${call.functionName} is not batchSwap`, \"policy_refused\");\n const [kind, swaps, assets, funds, limits] = call.args;\n if (kind !== 0) return notes.note(\"the batch is not GIVEN_IN\", \"policy_refused\");\n if (swaps.length === 0 || swaps.length > BALANCER_MAX_SWAPS) return notes.note(`the batch has ${swaps.length} pool hops, not 1–${BALANCER_MAX_SWAPS}`, \"policy_refused\");\n if (funds.fromInternalBalance || funds.toInternalBalance) return notes.note(\"the batch uses Vault internal balances\", \"policy_refused\");\n // One text for two checks: paying another recipient is recipient_mismatch; debiting another account only is a batch shape this module never signs.\n if (getAddress(funds.sender) !== sender || getAddress(funds.recipient) !== recipient) return notes.note(`the batch takes from ${funds.sender} and pays ${funds.recipient}, not this account and the recipient`, getAddress(funds.recipient) !== recipient ? \"recipient_mismatch\" : \"policy_refused\");\n const list = assets.map((a) => getAddress(a));\n if (new Set(list).size !== list.length) return notes.note(\"the batch names an asset twice\", \"policy_refused\");\n if ((nativeIn || nativeOut) && list.includes(getAddress(chain.weth))) return notes.note(\"a native swap passes through the wrapped native\", \"policy_refused\");\n const inIdx = list.indexOf(tokenIn);\n const outIdx = list.indexOf(tokenOut);\n if (inIdx < 0 || outIdx < 0 || inIdx === outIdx) return notes.note(\"the batch does not trade the requested tokens\", \"policy_refused\");\n if (limits.length !== list.length || limits.some((l, i) => l !== (i === inIdx ? amountIn : i === outIdx ? -minOut : 0n))) {\n return notes.note(\"the limits are not exactly the input in and the quote's minimum out\", \"price_check_failed\");\n }\n let given = 0n;\n for (const step of swaps) {\n if (step.userData !== \"0x\") return notes.note(\"the batch carries pool userData this module never sends\", \"policy_refused\");\n if (step.assetInIndex >= BigInt(list.length) || step.assetOutIndex >= BigInt(list.length) || step.assetInIndex === step.assetOutIndex) return notes.note(\"a hop names no valid asset pair\", \"policy_refused\");\n if (step.amount > 0n) {\n if (step.assetInIndex !== BigInt(inIdx)) return notes.note(\"a hop brings in an amount of another asset than the input\", \"policy_refused\");\n given += step.amount;\n }\n }\n if (given !== amountIn) return notes.note(`the hops sell ${given}, not the ${amountIn} input`, \"price_check_failed\");\n if (!swaps.some((step) => step.assetOutIndex === BigInt(outIdx))) return notes.note(\"no hop delivers the destination token\", \"policy_refused\");\n if (call.args[5] <= BigInt(Math.floor(Date.now() / 1000))) return notes.note(\"the batch deadline has passed\", \"quote_expired\");\n return null;\n}\n\n/**\n * The Balancer V2 Vault a swap calls and approves on the chains this provider serves, and {@link balancerVerifyQuote}\n * — the app's pins for `remoteProvider({name: \"balancer\", baseUrl, ...balancerPins})` (scan 2026-10-27 modules,\n * F8/F16; scan 2026-10-28, F2). `reasonOf` classifies the texts `balancerVerifyQuote` returned.\n */\nexport const balancerPins: RemotePins = Object.freeze({\n callTargets: (chain: string): readonly string[] => balancerVaultFor(chain),\n spenders: (chain: string): readonly string[] => balancerVaultFor(chain),\n verifyQuote: balancerVerifyQuote,\n reasonOf: (refusal: string) => notes.reasonOf(refusal),\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","refusal","import_util"]}
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
BalancerSwap2Protocol
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-AENUP77U.js";
|
|
4
4
|
import {
|
|
5
5
|
BALANCER_MAX_SWAPS,
|
|
6
6
|
BALANCER_MODULE,
|
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
balancerProvider,
|
|
12
12
|
balancerVerifyQuote,
|
|
13
13
|
describeBalancerActivity
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-XFRLZ25H.js";
|
|
15
15
|
export {
|
|
16
16
|
BALANCER_MAX_SWAPS,
|
|
17
17
|
BALANCER_MODULE,
|
package/dist/protocol.cjs
CHANGED
|
@@ -47,6 +47,7 @@ function describeBalancerActivity(tx, ctx) {
|
|
|
47
47
|
|
|
48
48
|
// src/provider.ts
|
|
49
49
|
var import_util2 = require("@anywalletkit/protocol-swap2-core/util");
|
|
50
|
+
var import_errors = require("@anywalletkit/protocol-swap2-core/errors");
|
|
50
51
|
var import_core = require("@anywalletkit/core");
|
|
51
52
|
var ZERO = "0x0000000000000000000000000000000000000000";
|
|
52
53
|
var BALANCER_V2_VAULT = "0xBA12222222228d8Ba445958a75a0704d566BF2C8";
|
|
@@ -55,6 +56,7 @@ var DEFAULT_SLIPPAGE_BPS = 50;
|
|
|
55
56
|
var DEFAULT_TTL_MS = 3e5;
|
|
56
57
|
var DEFAULT_DEADLINE_S = 600;
|
|
57
58
|
var BALANCER_MAX_SWAPS = 16;
|
|
59
|
+
var notes = (0, import_errors.refusalNotes)();
|
|
58
60
|
var BALANCER_V2_CHAINS = Object.freeze({
|
|
59
61
|
1: { api: "MAINNET", weth: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2" },
|
|
60
62
|
10: { api: "OPTIMISM", weth: "0x4200000000000000000000000000000000000006" },
|
|
@@ -95,16 +97,20 @@ function balancerProvider(config) {
|
|
|
95
97
|
for (const url of config.rpcUrls[chainId] ?? []) {
|
|
96
98
|
try {
|
|
97
99
|
const res = await fetch(url, { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ jsonrpc: "2.0", id: 1, method, params }), signal: (0, import_util2.timeoutSignal)(12e3) });
|
|
98
|
-
if (!res.ok)
|
|
100
|
+
if (!res.ok) {
|
|
101
|
+
const error = new Error(`HTTP ${res.status}`);
|
|
102
|
+
const info = (0, import_errors.httpRefusal)(res.status, res.headers?.get?.("retry-after"));
|
|
103
|
+
throw info ? (0, import_errors.withReason)(error, info) : error;
|
|
104
|
+
}
|
|
99
105
|
const body = await res.json();
|
|
100
|
-
if (body.error) throw Object.assign(new Error(body.error.message ?? "rpc error"), { rpc: true });
|
|
106
|
+
if (body.error) throw Object.assign(new Error(body.error.message ?? "rpc error"), { rpc: true, rpcCode: body.error.code });
|
|
101
107
|
return body.result ?? null;
|
|
102
108
|
} catch (e) {
|
|
103
109
|
if (e.rpc) throw e;
|
|
104
110
|
lastErr = e;
|
|
105
111
|
}
|
|
106
112
|
}
|
|
107
|
-
throw new Error(`balancer: no RPC answered on chain ${chainId} (${lastErr instanceof Error ? lastErr.message : String(lastErr)})`);
|
|
113
|
+
throw (0, import_errors.withReason)(new Error(`balancer: no RPC answered on chain ${chainId} (${lastErr instanceof Error ? lastErr.message : String(lastErr)})`), (0, import_errors.refusalOf)(lastErr) ?? "upstream_unavailable");
|
|
108
114
|
}
|
|
109
115
|
function verify(chainId) {
|
|
110
116
|
let p = verified.get(chainId);
|
|
@@ -112,7 +118,7 @@ function balancerProvider(config) {
|
|
|
112
118
|
p = (async () => {
|
|
113
119
|
const raw = await rpc(chainId, "eth_call", [{ to: BALANCER_V2_VAULT, data: (0, import_viem.encodeFunctionData)({ abi: VAULT_ABI, functionName: "WETH" }) }, "latest"]);
|
|
114
120
|
const weth = (0, import_viem.decodeFunctionResult)({ abi: VAULT_ABI, functionName: "WETH", data: raw });
|
|
115
|
-
if ((0, import_viem.getAddress)(weth) !== (0, import_viem.getAddress)(BALANCER_V2_CHAINS[chainId].weth)) throw
|
|
121
|
+
if ((0, import_viem.getAddress)(weth) !== (0, import_viem.getAddress)(BALANCER_V2_CHAINS[chainId].weth)) throw (0, import_errors.refusal)("vault_mismatch", `balancer: the Vault on chain ${chainId} is not the pinned deployment \u2014 refusing`);
|
|
116
122
|
})();
|
|
117
123
|
p.catch(() => verified.delete(chainId));
|
|
118
124
|
verified.set(chainId, p);
|
|
@@ -121,8 +127,19 @@ function balancerProvider(config) {
|
|
|
121
127
|
}
|
|
122
128
|
async function query(chainId, swaps, assets, funds) {
|
|
123
129
|
const data = (0, import_viem.encodeFunctionData)({ abi: VAULT_ABI, functionName: "queryBatchSwap", args: [0, swaps, assets, funds] });
|
|
124
|
-
|
|
125
|
-
|
|
130
|
+
let raw;
|
|
131
|
+
try {
|
|
132
|
+
raw = await rpc(chainId, "eth_call", [{ to: BALANCER_V2_VAULT, data }, "latest"]);
|
|
133
|
+
} catch (e) {
|
|
134
|
+
if (!(e instanceof Error) || !e.rpc) throw e;
|
|
135
|
+
const code = e.rpcCode;
|
|
136
|
+
throw (0, import_errors.withReason)(e, code === 3 || code === -32015 || /revert/i.test(e.message) ? "simulation_failed" : "upstream_unavailable");
|
|
137
|
+
}
|
|
138
|
+
try {
|
|
139
|
+
return (0, import_viem.decodeFunctionResult)({ abi: VAULT_ABI, functionName: "queryBatchSwap", data: raw });
|
|
140
|
+
} catch (e) {
|
|
141
|
+
throw e instanceof Error ? (0, import_errors.withReason)(e, "upstream_unavailable") : e;
|
|
142
|
+
}
|
|
126
143
|
}
|
|
127
144
|
return {
|
|
128
145
|
name: "balancer",
|
|
@@ -133,21 +150,21 @@ function balancerProvider(config) {
|
|
|
133
150
|
supportsChainPair: (from, to) => from === to && chainIdOf(from) !== null,
|
|
134
151
|
supportsRoute: (from, to) => from.chain === to.chain && chainIdOf(from.chain) !== null,
|
|
135
152
|
async getQuote(params, signal) {
|
|
136
|
-
const refuse = (why) => {
|
|
137
|
-
params.onRefusal?.(`balancer: ${why}
|
|
153
|
+
const refuse = (why, info) => {
|
|
154
|
+
params.onRefusal?.(`balancer: ${why}`, typeof info === "string" ? { reason: info } : info);
|
|
138
155
|
return null;
|
|
139
156
|
};
|
|
140
|
-
if (params.flex) return refuse("amount-flexible deposits are not supported");
|
|
141
|
-
if (params.sourceChain !== params.destChain) return refuse("same-chain swaps only");
|
|
157
|
+
if (params.flex) return refuse("amount-flexible deposits are not supported", "unsupported_pair");
|
|
158
|
+
if (params.sourceChain !== params.destChain) return refuse("same-chain swaps only", "unsupported_pair");
|
|
142
159
|
const chainId = chainIdOf(params.sourceChain);
|
|
143
|
-
if (chainId === null) return refuse(`no Balancer v2 Vault or RPC for ${params.sourceChain}
|
|
160
|
+
if (chainId === null) return refuse(`no Balancer v2 Vault or RPC for ${params.sourceChain}`, "unsupported_pair");
|
|
144
161
|
const chain = BALANCER_V2_CHAINS[chainId];
|
|
145
162
|
const src = (0, import_util2.sourceAssetOf)(params);
|
|
146
|
-
if (!src) return refuse(`unknown source token ${params.sourceToken}
|
|
163
|
+
if (!src) return refuse(`unknown source token ${params.sourceToken}`, "unsupported_pair");
|
|
147
164
|
const nativeIn = !src.contract;
|
|
148
165
|
const nativeOut = /^0x0+$/.test(params.destToken);
|
|
149
|
-
if (!nativeOut && !/^0x[0-9a-fA-F]{40}$/.test(params.destToken)) return refuse("destination token must be an EVM address");
|
|
150
|
-
if (!/^0x[0-9a-fA-F]{40}$/.test(params.destAddress)) return refuse("recipient must be an EVM address");
|
|
166
|
+
if (!nativeOut && !/^0x[0-9a-fA-F]{40}$/.test(params.destToken)) return refuse("destination token must be an EVM address", "unsupported_pair");
|
|
167
|
+
if (!/^0x[0-9a-fA-F]{40}$/.test(params.destAddress)) return refuse("recipient must be an EVM address", "recipient_mismatch");
|
|
151
168
|
let amountIn;
|
|
152
169
|
try {
|
|
153
170
|
amountIn = BigInt(params.amountIn);
|
|
@@ -157,7 +174,7 @@ function balancerProvider(config) {
|
|
|
157
174
|
if (amountIn <= 0n) return refuse("amount must be positive");
|
|
158
175
|
const tokenIn = (0, import_viem.getAddress)(nativeIn ? chain.weth : src.contract);
|
|
159
176
|
const tokenOut = (0, import_viem.getAddress)(nativeOut ? chain.weth : params.destToken);
|
|
160
|
-
if (tokenIn === tokenOut) return refuse(nativeIn || nativeOut ? "that is a wrap/unwrap, not a swap" : "same token in and out");
|
|
177
|
+
if (tokenIn === tokenOut) return refuse(nativeIn || nativeOut ? "that is a wrap/unwrap, not a swap" : "same token in and out", "unsupported_pair");
|
|
161
178
|
try {
|
|
162
179
|
await verify(chainId);
|
|
163
180
|
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 } } }`;
|
|
@@ -167,14 +184,14 @@ function balancerProvider(config) {
|
|
|
167
184
|
body: JSON.stringify({ query: query_ }),
|
|
168
185
|
signal: signal ?? (0, import_util2.timeoutSignal)(12e3)
|
|
169
186
|
});
|
|
170
|
-
if (!res.ok) return refuse(`SOR answered HTTP ${res.status}
|
|
187
|
+
if (!res.ok) return refuse(`SOR answered HTTP ${res.status}`, (0, import_errors.httpRefusal)(res.status, res.headers?.get?.("retry-after")));
|
|
171
188
|
const body = await res.json();
|
|
172
189
|
const paths = body.data?.sorGetSwapPaths?.paths ?? [];
|
|
173
|
-
if (body.errors?.length) return refuse(`SOR: ${body.errors[0]?.message?.slice(0, 120) ?? "error"}
|
|
190
|
+
if (body.errors?.length) return refuse(`SOR: ${body.errors[0]?.message?.slice(0, 120) ?? "error"}`, "upstream_unavailable");
|
|
174
191
|
if (!paths.length || paths.some((p) => p.protocolVersion !== 2 || p.pools.length === 0 || p.tokens.length !== p.pools.length + 1)) {
|
|
175
|
-
return refuse("no Balancer v2 pool route for this pair and amount");
|
|
192
|
+
return refuse("no Balancer v2 pool route for this pair and amount", "no_route");
|
|
176
193
|
}
|
|
177
|
-
if (paths.reduce((s, p) => s + BigInt(p.inputAmountRaw), 0n) !== amountIn) return refuse("the SOR split does not add up to the amount");
|
|
194
|
+
if (paths.reduce((s, p) => s + BigInt(p.inputAmountRaw), 0n) !== amountIn) return refuse("the SOR split does not add up to the amount", "price_check_failed");
|
|
178
195
|
const assets = [];
|
|
179
196
|
const indexOf = (a) => {
|
|
180
197
|
const addr = (0, import_viem.getAddress)(a);
|
|
@@ -184,18 +201,18 @@ function balancerProvider(config) {
|
|
|
184
201
|
};
|
|
185
202
|
const swaps = [];
|
|
186
203
|
for (const p of paths) {
|
|
187
|
-
if ((0, import_viem.getAddress)(p.tokens[0].address) !== tokenIn || (0, import_viem.getAddress)(p.tokens[p.tokens.length - 1].address) !== tokenOut) return refuse("the SOR path does not start/end at the requested tokens");
|
|
204
|
+
if ((0, import_viem.getAddress)(p.tokens[0].address) !== tokenIn || (0, import_viem.getAddress)(p.tokens[p.tokens.length - 1].address) !== tokenOut) return refuse("the SOR path does not start/end at the requested tokens", "policy_refused");
|
|
188
205
|
p.pools.forEach((pool, i) => {
|
|
189
|
-
if (!/^0x[0-9a-fA-F]{64}$/.test(pool)) throw
|
|
206
|
+
if (!/^0x[0-9a-fA-F]{64}$/.test(pool)) throw (0, import_errors.refusal)("upstream_unavailable", "balancer: malformed pool id from the SOR");
|
|
190
207
|
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" });
|
|
191
208
|
});
|
|
192
209
|
}
|
|
193
|
-
if (swaps.length > BALANCER_MAX_SWAPS) return refuse(`the SOR route has ${swaps.length} pool hops, more than ${BALANCER_MAX_SWAPS}
|
|
210
|
+
if (swaps.length > BALANCER_MAX_SWAPS) return refuse(`the SOR route has ${swaps.length} pool hops, more than ${BALANCER_MAX_SWAPS}`, "policy_refused");
|
|
194
211
|
const inIdx = (0, import_core.bigintToNumber)(indexOf(tokenIn));
|
|
195
212
|
const outIdx = (0, import_core.bigintToNumber)(indexOf(tokenOut));
|
|
196
213
|
if (nativeIn || nativeOut) {
|
|
197
214
|
const wethIdx = assets.findIndex((a) => a === (0, import_viem.getAddress)(chain.weth));
|
|
198
|
-
if (paths.some((p) => p.tokens.slice(1, -1).some((t) => (0, import_viem.getAddress)(t.address) === (0, import_viem.getAddress)(chain.weth)))) return refuse("this route passes through the wrapped native \u2014 pick the wrapped token instead");
|
|
215
|
+
if (paths.some((p) => p.tokens.slice(1, -1).some((t) => (0, import_viem.getAddress)(t.address) === (0, import_viem.getAddress)(chain.weth)))) return refuse("this route passes through the wrapped native \u2014 pick the wrapped token instead", "unsupported_pair");
|
|
199
216
|
if (wethIdx >= 0) assets[wethIdx] = ZERO;
|
|
200
217
|
}
|
|
201
218
|
const sender = (0, import_viem.getAddress)(params.refundAddress && /^0x[0-9a-fA-F]{40}$/.test(params.refundAddress) ? params.refundAddress : params.destAddress);
|
|
@@ -204,12 +221,12 @@ function balancerProvider(config) {
|
|
|
204
221
|
const deltas = await query(chainId, swaps, assets, funds);
|
|
205
222
|
const paid = deltas[inIdx];
|
|
206
223
|
const out = -deltas[outIdx];
|
|
207
|
-
if (paid !== amountIn) return refuse(`the Vault would take ${paid}, not ${amountIn}
|
|
208
|
-
if (out <= 0n) return refuse("the Vault prices this route at zero output");
|
|
209
|
-
if (deltas.some((d, i) => i !== inIdx && i !== outIdx && d !== 0n)) return refuse("the batch leaves an intermediate balance \u2014 refusing");
|
|
224
|
+
if (paid !== amountIn) return refuse(`the Vault would take ${paid}, not ${amountIn}`, "price_check_failed");
|
|
225
|
+
if (out <= 0n) return refuse("the Vault prices this route at zero output", "amount_too_small");
|
|
226
|
+
if (deltas.some((d, i) => i !== inIdx && i !== outIdx && d !== 0n)) return refuse("the batch leaves an intermediate balance \u2014 refusing", "policy_refused");
|
|
210
227
|
const bps = params.slippageBps ?? slippageDefault;
|
|
211
228
|
const minOut = out * BigInt(1e4 - bps) / 10000n;
|
|
212
|
-
if (params.minAmountOut !== void 0 && minOut < BigInt(params.minAmountOut)) return refuse(`floor breach (min=${minOut} < floor=${params.minAmountOut})
|
|
229
|
+
if (params.minAmountOut !== void 0 && minOut < BigInt(params.minAmountOut)) return refuse(`floor breach (min=${minOut} < floor=${params.minAmountOut})`, "price_check_failed");
|
|
213
230
|
const limits = assets.map((_, i) => i === inIdx ? amountIn : i === outIdx ? -minOut : 0n);
|
|
214
231
|
const deadline = BigInt(Math.floor(Date.now() / 1e3) + deadlineS);
|
|
215
232
|
const data = (0, import_viem.encodeFunctionData)({ abi: VAULT_ABI, functionName: "batchSwap", args: [0, swaps, assets, funds, limits, deadline] });
|
|
@@ -238,7 +255,7 @@ function balancerProvider(config) {
|
|
|
238
255
|
providerData: { paths: String(paths.length), hops: String(swaps.length), minOut: minOut.toString(), poolFee: "pools" }
|
|
239
256
|
};
|
|
240
257
|
} catch (e) {
|
|
241
|
-
return refuse(e instanceof Error ? e.message.replace(/^balancer: /, "") : String(e));
|
|
258
|
+
return refuse(e instanceof Error ? e.message.replace(/^balancer: /, "") : String(e), (0, import_errors.refusalOf)(e) ?? (signal?.aborted ? void 0 : "upstream_unavailable"));
|
|
242
259
|
}
|
|
243
260
|
},
|
|
244
261
|
/**
|
|
@@ -253,21 +270,21 @@ function balancerProvider(config) {
|
|
|
253
270
|
if (!BALANCER_V2_CHAINS[chainId] || !minOut || !tx.data) return;
|
|
254
271
|
const quoted = quote.txRequest?.data;
|
|
255
272
|
if (!quoted || tx.data.length !== quoted.length || tx.data.toLowerCase() !== quoted.toLowerCase()) {
|
|
256
|
-
throw
|
|
273
|
+
throw (0, import_errors.refusal)("policy_refused", "balancer: the transaction is not the quoted batch. Nothing was sent; refresh the quote.");
|
|
257
274
|
}
|
|
258
275
|
const call = (0, import_viem.decodeFunctionData)({ abi: VAULT_ABI, data: quoted });
|
|
259
|
-
if (call.functionName !== "batchSwap") throw
|
|
276
|
+
if (call.functionName !== "batchSwap") throw (0, import_errors.refusal)("policy_refused", "balancer: the transaction is not a Vault batchSwap");
|
|
260
277
|
const [, swaps, assets, funds, limits] = call.args;
|
|
261
|
-
if (swaps.length === 0 || swaps.length > BALANCER_MAX_SWAPS) throw
|
|
262
|
-
if (swaps.some((step) => step.userData !== "0x")) throw
|
|
278
|
+
if (swaps.length === 0 || swaps.length > BALANCER_MAX_SWAPS) throw (0, import_errors.refusal)("policy_refused", `balancer: the batch has ${swaps.length} pool hops, not 1\u2013${BALANCER_MAX_SWAPS}`);
|
|
279
|
+
if (swaps.some((step) => step.userData !== "0x")) throw (0, import_errors.refusal)("policy_refused", "balancer: the batch carries pool userData this module never sends");
|
|
263
280
|
const outIdx = limits.findIndex((l) => l < 0n);
|
|
264
281
|
const deltas = await query(chainId, swaps, assets, funds);
|
|
265
282
|
const out = -deltas[outIdx];
|
|
266
|
-
if (out < BigInt(minOut)) throw
|
|
283
|
+
if (out < BigInt(minOut)) throw (0, import_errors.refusal)("price_check_failed", `balancer: price moved \u2014 the pools now pay ${out}, under your minimum ${minOut}. Nothing was sent; refresh the quote.`);
|
|
267
284
|
},
|
|
268
285
|
async buildDeposit(quote) {
|
|
269
286
|
const tx = quote.txRequest;
|
|
270
|
-
if (!tx?.to || !tx.data) throw
|
|
287
|
+
if (!tx?.to || !tx.data) throw (0, import_errors.refusal)("policy_refused", "swap2: Balancer quote has no transaction request");
|
|
271
288
|
return { chain: "", to: tx.to, value: tx.value ?? "0", data: tx.data, ...tx.chainId !== void 0 ? { chainId: tx.chainId } : {}, ...tx.gasLimit ? { gasLimit: tx.gasLimit } : {}, ...tx.approvalAddress ? { approvalAddress: tx.approvalAddress } : {} };
|
|
272
289
|
},
|
|
273
290
|
async trackIntent(ref) {
|
|
@@ -287,12 +304,12 @@ function balancerProvider(config) {
|
|
|
287
304
|
function balancerVerifyQuote(params, quote) {
|
|
288
305
|
const id = /^eip155:[0-9]+$/.test(params.sourceChain) ? Number(params.sourceChain.slice(7)) : NaN;
|
|
289
306
|
const chain = Object.prototype.hasOwnProperty.call(BALANCER_V2_CHAINS, id) ? BALANCER_V2_CHAINS[id] : void 0;
|
|
290
|
-
if (!chain || params.destChain !== params.sourceChain) return "not a same-chain swap on a Balancer v2 chain";
|
|
307
|
+
if (!chain || params.destChain !== params.sourceChain) return notes.note("not a same-chain swap on a Balancer v2 chain", "unsupported_pair");
|
|
291
308
|
const tx = quote.txRequest;
|
|
292
|
-
if (!tx?.data || !tx.to || tx.serializedTx !== void 0) return "the quote carries no Vault call";
|
|
293
|
-
if ((0, import_viem.getAddress)(tx.to) !== BALANCER_V2_VAULT) return `the call targets ${tx.to}, not the Vault
|
|
309
|
+
if (!tx?.data || !tx.to || tx.serializedTx !== void 0) return notes.note("the quote carries no Vault call", "policy_refused");
|
|
310
|
+
if ((0, import_viem.getAddress)(tx.to) !== BALANCER_V2_VAULT) return notes.note(`the call targets ${tx.to}, not the Vault`, "vault_mismatch");
|
|
294
311
|
const src = (0, import_util2.sourceAssetOf)(params);
|
|
295
|
-
if (!src) return `unknown source token ${params.sourceToken}
|
|
312
|
+
if (!src) return notes.note(`unknown source token ${params.sourceToken}`, "unsupported_pair");
|
|
296
313
|
const nativeIn = !src.contract;
|
|
297
314
|
const nativeOut = /^0x0+$/.test(params.destToken);
|
|
298
315
|
let tokenIn, tokenOut, sender, recipient, amountIn, minOut;
|
|
@@ -307,47 +324,48 @@ function balancerVerifyQuote(params, quote) {
|
|
|
307
324
|
return "the request does not name EVM tokens, a recipient and amounts";
|
|
308
325
|
}
|
|
309
326
|
if (nativeIn ? BigInt(tx.value ?? "0") !== amountIn || tx.approvalAddress !== void 0 : BigInt(tx.value ?? "0") !== 0n || !tx.approvalAddress || (0, import_viem.getAddress)(tx.approvalAddress) !== BALANCER_V2_VAULT) {
|
|
310
|
-
return nativeIn ? "a native input is paid as exactly the input in value, with no approval" : "a token input attaches no value and approves the Vault";
|
|
327
|
+
return notes.note(nativeIn ? "a native input is paid as exactly the input in value, with no approval" : "a token input attaches no value and approves the Vault", "policy_refused");
|
|
311
328
|
}
|
|
312
329
|
let call;
|
|
313
330
|
try {
|
|
314
331
|
call = (0, import_viem.decodeFunctionData)({ abi: VAULT_ABI, data: tx.data });
|
|
315
332
|
} catch {
|
|
316
|
-
return "the Vault call is not batchSwap";
|
|
333
|
+
return notes.note("the Vault call is not batchSwap", "policy_refused");
|
|
317
334
|
}
|
|
318
|
-
if (call.functionName !== "batchSwap") return `${call.functionName} is not batchSwap
|
|
335
|
+
if (call.functionName !== "batchSwap") return notes.note(`${call.functionName} is not batchSwap`, "policy_refused");
|
|
319
336
|
const [kind, swaps, assets, funds, limits] = call.args;
|
|
320
|
-
if (kind !== 0) return "the batch is not GIVEN_IN";
|
|
321
|
-
if (swaps.length === 0 || swaps.length > BALANCER_MAX_SWAPS) return `the batch has ${swaps.length} pool hops, not 1\u2013${BALANCER_MAX_SWAPS}
|
|
322
|
-
if (funds.fromInternalBalance || funds.toInternalBalance) return "the batch uses Vault internal balances";
|
|
323
|
-
if ((0, import_viem.getAddress)(funds.sender) !== sender || (0, import_viem.getAddress)(funds.recipient) !== recipient) return `the batch takes from ${funds.sender} and pays ${funds.recipient}, not this account and the recipient
|
|
337
|
+
if (kind !== 0) return notes.note("the batch is not GIVEN_IN", "policy_refused");
|
|
338
|
+
if (swaps.length === 0 || swaps.length > BALANCER_MAX_SWAPS) return notes.note(`the batch has ${swaps.length} pool hops, not 1\u2013${BALANCER_MAX_SWAPS}`, "policy_refused");
|
|
339
|
+
if (funds.fromInternalBalance || funds.toInternalBalance) return notes.note("the batch uses Vault internal balances", "policy_refused");
|
|
340
|
+
if ((0, import_viem.getAddress)(funds.sender) !== sender || (0, import_viem.getAddress)(funds.recipient) !== recipient) return notes.note(`the batch takes from ${funds.sender} and pays ${funds.recipient}, not this account and the recipient`, (0, import_viem.getAddress)(funds.recipient) !== recipient ? "recipient_mismatch" : "policy_refused");
|
|
324
341
|
const list = assets.map((a) => (0, import_viem.getAddress)(a));
|
|
325
|
-
if (new Set(list).size !== list.length) return "the batch names an asset twice";
|
|
326
|
-
if ((nativeIn || nativeOut) && list.includes((0, import_viem.getAddress)(chain.weth))) return "a native swap passes through the wrapped native";
|
|
342
|
+
if (new Set(list).size !== list.length) return notes.note("the batch names an asset twice", "policy_refused");
|
|
343
|
+
if ((nativeIn || nativeOut) && list.includes((0, import_viem.getAddress)(chain.weth))) return notes.note("a native swap passes through the wrapped native", "policy_refused");
|
|
327
344
|
const inIdx = list.indexOf(tokenIn);
|
|
328
345
|
const outIdx = list.indexOf(tokenOut);
|
|
329
|
-
if (inIdx < 0 || outIdx < 0 || inIdx === outIdx) return "the batch does not trade the requested tokens";
|
|
346
|
+
if (inIdx < 0 || outIdx < 0 || inIdx === outIdx) return notes.note("the batch does not trade the requested tokens", "policy_refused");
|
|
330
347
|
if (limits.length !== list.length || limits.some((l, i) => l !== (i === inIdx ? amountIn : i === outIdx ? -minOut : 0n))) {
|
|
331
|
-
return "the limits are not exactly the input in and the quote's minimum out";
|
|
348
|
+
return notes.note("the limits are not exactly the input in and the quote's minimum out", "price_check_failed");
|
|
332
349
|
}
|
|
333
350
|
let given = 0n;
|
|
334
351
|
for (const step of swaps) {
|
|
335
|
-
if (step.userData !== "0x") return "the batch carries pool userData this module never sends";
|
|
336
|
-
if (step.assetInIndex >= BigInt(list.length) || step.assetOutIndex >= BigInt(list.length) || step.assetInIndex === step.assetOutIndex) return "a hop names no valid asset pair";
|
|
352
|
+
if (step.userData !== "0x") return notes.note("the batch carries pool userData this module never sends", "policy_refused");
|
|
353
|
+
if (step.assetInIndex >= BigInt(list.length) || step.assetOutIndex >= BigInt(list.length) || step.assetInIndex === step.assetOutIndex) return notes.note("a hop names no valid asset pair", "policy_refused");
|
|
337
354
|
if (step.amount > 0n) {
|
|
338
|
-
if (step.assetInIndex !== BigInt(inIdx)) return "a hop brings in an amount of another asset than the input";
|
|
355
|
+
if (step.assetInIndex !== BigInt(inIdx)) return notes.note("a hop brings in an amount of another asset than the input", "policy_refused");
|
|
339
356
|
given += step.amount;
|
|
340
357
|
}
|
|
341
358
|
}
|
|
342
|
-
if (given !== amountIn) return `the hops sell ${given}, not the ${amountIn} input
|
|
343
|
-
if (!swaps.some((step) => step.assetOutIndex === BigInt(outIdx))) return "no hop delivers the destination token";
|
|
344
|
-
if (call.args[5] <= BigInt(Math.floor(Date.now() / 1e3))) return "the batch deadline has passed";
|
|
359
|
+
if (given !== amountIn) return notes.note(`the hops sell ${given}, not the ${amountIn} input`, "price_check_failed");
|
|
360
|
+
if (!swaps.some((step) => step.assetOutIndex === BigInt(outIdx))) return notes.note("no hop delivers the destination token", "policy_refused");
|
|
361
|
+
if (call.args[5] <= BigInt(Math.floor(Date.now() / 1e3))) return notes.note("the batch deadline has passed", "quote_expired");
|
|
345
362
|
return null;
|
|
346
363
|
}
|
|
347
364
|
var balancerPins = Object.freeze({
|
|
348
365
|
callTargets: (chain) => balancerVaultFor(chain),
|
|
349
366
|
spenders: (chain) => balancerVaultFor(chain),
|
|
350
|
-
verifyQuote: balancerVerifyQuote
|
|
367
|
+
verifyQuote: balancerVerifyQuote,
|
|
368
|
+
reasonOf: (refusal2) => notes.reasonOf(refusal2)
|
|
351
369
|
});
|
|
352
370
|
function balancerVaultFor(chain) {
|
|
353
371
|
const id = /^eip155:[0-9]+$/.test(chain) ? Number(chain.slice(7)) : NaN;
|