@anywalletkit/protocol-swap2-core 0.8.0 → 0.9.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.
@@ -1,10 +1,10 @@
1
1
  import {
2
2
  BaseSwap2Protocol
3
- } from "./chunk-CRCDWV3L.js";
3
+ } from "./chunk-PU5NM3Y6.js";
4
4
  import {
5
5
  remotePinRefusal,
6
6
  validQuote
7
- } from "./chunk-5FPDKWBG.js";
7
+ } from "./chunk-ZEN2IXRC.js";
8
8
 
9
9
  // src/swap2/single.ts
10
10
  var SingleProviderSwap2 = class extends BaseSwap2Protocol {
@@ -68,4 +68,4 @@ var SingleProviderSwap2 = class extends BaseSwap2Protocol {
68
68
  export {
69
69
  SingleProviderSwap2
70
70
  };
71
- //# sourceMappingURL=chunk-FHEU7E5A.js.map
71
+ //# sourceMappingURL=chunk-REWM3GRK.js.map
@@ -52,6 +52,9 @@ function remotePinRefusal(provider, q, chain) {
52
52
  if (tx.data !== void 0 && tx.to && !pinned(provider.callTargets?.(chain), tx.to)) {
53
53
  return `${provider.name}: the route calls ${tx.to}, which the app did not pin as a call target on ${chain} (remoteProvider callTargets)`;
54
54
  }
55
+ if (tx.data !== void 0 && provider.verifiesQuotes !== true && provider.verifiesBuilds !== true) {
56
+ return `${provider.name}: the route's calldata is not checked by the app (remoteProvider verifyQuote / verifyBuild) \u2014 a pinned target alone does not say whom it pays`;
57
+ }
55
58
  return null;
56
59
  }
57
60
 
@@ -59,4 +62,4 @@ export {
59
62
  validQuote,
60
63
  remotePinRefusal
61
64
  };
62
- //# sourceMappingURL=chunk-5FPDKWBG.js.map
65
+ //# sourceMappingURL=chunk-ZEN2IXRC.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/validate.ts"],"sourcesContent":["import type {CrossChainProvider, Quote, QuoteParams} from \"./types.js\";\n\n/** Shared quote validator used by both the aggregate and single-provider paths. */\nexport function validQuote(q: Quote, params: QuoteParams, provider: string): boolean {\n try {\n if (q.provider !== provider || q.sourceAmount !== params.amountIn) return false;\n if (typeof q.intentId !== \"string\" || !q.intentId || typeof q.depositAddress !== \"string\" || (q.depositMemo !== null && typeof q.depositMemo !== \"string\")) return false;\n if (![q.sourceAmount, q.destAmount, q.minDestAmount].every((value) => /^[0-9]+$/.test(value))) return false;\n const source = BigInt(q.sourceAmount);\n const dest = BigInt(q.destAmount);\n const min = BigInt(q.minDestAmount);\n if (source <= 0n || dest <= 0n || min <= 0n || min > dest) return false;\n if (params.minAmountOut !== undefined) {\n if (!/^[0-9]+$/.test(params.minAmountOut) || BigInt(params.minAmountOut) <= 0n || min < BigInt(params.minAmountOut)) return false;\n }\n if (params.slippageBps !== undefined) {\n if (!Number.isInteger(params.slippageBps) || params.slippageBps < 0 || params.slippageBps >= 10_000) return false;\n // Providers round the floor DOWN (1Click, LI.FI: floor(dest × (1 − s))).\n // A ceil here refused nearly every slippage-bounded quote by one base unit.\n const required = (dest * BigInt(10_000 - params.slippageBps)) / 10_000n;\n if (min < required) return false;\n }\n if (!Number.isFinite(q.etaSeconds) || q.etaSeconds < 0) return false;\n if (!Number.isFinite(q.reliability) || q.reliability < 0 || q.reliability > 1) return false;\n if (!Number.isFinite(q.feeUsd) || q.feeUsd < 0) return false;\n if (q.fees && (!Array.isArray(q.fees) || !q.fees.every((fee) =>\n [\"network\", \"protocol\", \"affiliate\", \"other\"].includes(fee.type) &&\n /^[0-9]+$/.test(fee.amount) &&\n typeof fee.token === \"string\" && fee.token.length > 0 &&\n typeof fee.included === \"boolean\" &&\n (fee.description === undefined || typeof fee.description === \"string\")\n ))) return false;\n if (q.providerData && (Array.isArray(q.providerData) || typeof q.providerData !== \"object\" || !Object.values(q.providerData).every((value) => typeof value === \"string\"))) return false;\n const tx = q.txRequest;\n if (tx) {\n if (tx.value !== undefined && !/^[0-9]+$/.test(tx.value)) return false;\n if (tx.gasLimit !== undefined && !/^[0-9]+$/.test(tx.gasLimit)) return false;\n if (tx.chainId !== undefined && (!Number.isSafeInteger(tx.chainId) || tx.chainId <= 0)) return false;\n if ([tx.to, tx.data, tx.approvalAddress, tx.serializedTx].some((value) => value !== undefined && typeof value !== \"string\")) return false;\n }\n // Execution is always re-quoted. An unknown/zero expiry cannot prove that\n // the transaction is still safe to sign, so contribution packages reject it.\n if (!Number.isFinite(q.validUntilMs) || q.validUntilMs <= Date.now()) return false;\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Why a remote provider's quote cannot execute in this app, or null: its EVM transaction calls or\n * approves an address the app did not pin (`remoteProvider` `callTargets` / `spenders`), or it pays\n * an address the backend named — a deposit address or a plain value send — on a stub the app did not\n * open to such routes (`depositAddressRoutes`, scan 2026-10-28 F9), or it is EVM calldata on a stub without the\n * app's own check of what that calldata does (`verifyQuote` / `verifyBuild`, scan 2026-10-30 F3). The router and the\n * single-provider path drop such a quote with this reason, so the auction never picks a route\n * execution would refuse (scan 2026-10-27 modules, F8/F9/F16); execution checks the build again, in\n * full. Local providers are not judged here.\n */\nexport function remotePinRefusal(provider: Pick<CrossChainProvider, \"name\" | \"remote\" | \"callTargets\" | \"spenders\" | \"depositAddressRoutes\" | \"verifiesQuotes\" | \"verifiesBuilds\">, q: Quote, chain: string): string | null {\n if (provider.remote !== true) return null;\n const tx = q.txRequest;\n const plain = tx ? tx.data === undefined && tx.serializedTx === undefined : typeof q.depositAddress === \"string\" && q.depositAddress !== \"\";\n if (plain && provider.depositAddressRoutes !== true) {\n return `${provider.name}: the route pays ${tx?.to || q.depositAddress}, an address the backend named, and the app did not allow deposit-address routes for this provider (remoteProvider depositAddressRoutes)`;\n }\n if (!chain.startsWith(\"eip155:\")) return null;\n if (!tx || tx.serializedTx !== undefined) return null;\n const pinned = (list: readonly string[] | undefined, address: string) =>\n (list ?? []).some((a) => typeof a === \"string\" && a.toLowerCase() === address.toLowerCase());\n if (tx.approvalAddress && !pinned(provider.spenders?.(chain), tx.approvalAddress)) {\n return `${provider.name}: the route approves ${tx.approvalAddress}, which the app did not pin as a spender on ${chain} (remoteProvider spenders)`;\n }\n if (tx.data !== undefined && tx.to && !pinned(provider.callTargets?.(chain), tx.to)) {\n return `${provider.name}: the route calls ${tx.to}, which the app did not pin as a call target on ${chain} (remoteProvider callTargets)`;\n }\n if (tx.data !== undefined && provider.verifiesQuotes !== true && provider.verifiesBuilds !== true) {\n return `${provider.name}: the route's calldata is not checked by the app (remoteProvider verifyQuote / verifyBuild) — a pinned target alone does not say whom it pays`;\n }\n return null;\n}\n"],"mappings":"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","names":[]}
package/dist/index.cjs CHANGED
@@ -175,6 +175,9 @@ function remotePinRefusal(provider, q, chain) {
175
175
  if (tx.data !== void 0 && tx.to && !pinned(provider.callTargets?.(chain), tx.to)) {
176
176
  return `${provider.name}: the route calls ${tx.to}, which the app did not pin as a call target on ${chain} (remoteProvider callTargets)`;
177
177
  }
178
+ if (tx.data !== void 0 && provider.verifiesQuotes !== true && provider.verifiesBuilds !== true) {
179
+ return `${provider.name}: the route's calldata is not checked by the app (remoteProvider verifyQuote / verifyBuild) \u2014 a pinned target alone does not say whom it pays`;
180
+ }
178
181
  return null;
179
182
  }
180
183
 
@@ -329,6 +332,7 @@ function remoteProvider(opts) {
329
332
  name,
330
333
  remote: true,
331
334
  ...opts.verifyBuild ? { verifiesBuilds: true } : {},
335
+ ...opts.verifyQuote ? { verifiesQuotes: true } : {},
332
336
  ...opts.depositAddressRoutes === true ? { depositAddressRoutes: true } : {},
333
337
  // The browser side has no dark-flag env; the backend enforces its own
334
338
  // flags — a disabled provider simply quotes null / rejects build.
@@ -1164,6 +1168,8 @@ var BaseSwap2Protocol = class extends import_core.Swap2Protocol {
1164
1168
  }
1165
1169
  } else if (remote && !pinnedTargets.includes(unsigned.to.toLowerCase())) {
1166
1170
  throw new Swap2ValidationError(`swap2: remote provider '${q.provider}' calls ${(0, import_viem.getAddress)(unsigned.to)} on ${this.fromChain}, which the app did not pin as a call target (remoteProvider callTargets) \u2014 refused`);
1171
+ } else if (remote && provider.verifiesQuotes !== true && provider.verifiesBuilds !== true) {
1172
+ throw new Swap2ValidationError(`swap2: remote provider '${q.provider}' calls ${(0, import_viem.getAddress)(unsigned.to)} with calldata the app does not check (remoteProvider verifyQuote / verifyBuild) \u2014 refused`);
1167
1173
  }
1168
1174
  }
1169
1175
  const needsApproval = !!unsigned.approvalAddress && !!source.contract;