@anywalletkit/protocol-swap2-core 0.6.1 → 0.8.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.
Files changed (53) hide show
  1. package/README.md +1 -1
  2. package/dist/chunk-2KC734UX.js +117 -0
  3. package/dist/chunk-2KC734UX.js.map +1 -0
  4. package/dist/{chunk-KIAQXIBG.js → chunk-5FPDKWBG.js} +21 -2
  5. package/dist/chunk-5FPDKWBG.js.map +1 -0
  6. package/dist/{chunk-ZMWK3RQ4.js → chunk-6MTZNYKM.js} +40 -5
  7. package/dist/chunk-6MTZNYKM.js.map +1 -0
  8. package/dist/{chunk-IGTMUTNJ.js → chunk-CRCDWV3L.js} +130 -7
  9. package/dist/chunk-CRCDWV3L.js.map +1 -0
  10. package/dist/{chunk-JFGMF6FO.js → chunk-FHEU7E5A.js} +9 -3
  11. package/dist/chunk-FHEU7E5A.js.map +1 -0
  12. package/dist/index.cjs +256 -10
  13. package/dist/index.cjs.map +1 -1
  14. package/dist/index.d.cts +4 -4
  15. package/dist/index.d.ts +4 -4
  16. package/dist/index.js +10 -6
  17. package/dist/remote.cjs +38 -3
  18. package/dist/remote.cjs.map +1 -1
  19. package/dist/remote.d.cts +33 -1
  20. package/dist/remote.d.ts +33 -1
  21. package/dist/remote.js +2 -2
  22. package/dist/server.cjs +23 -4
  23. package/dist/server.cjs.map +1 -1
  24. package/dist/server.js +6 -5
  25. package/dist/server.js.map +1 -1
  26. package/dist/swap2/base.cjs +187 -16
  27. package/dist/swap2/base.cjs.map +1 -1
  28. package/dist/swap2/base.d.cts +19 -1
  29. package/dist/swap2/base.d.ts +19 -1
  30. package/dist/swap2/base.js +2 -1
  31. package/dist/swap2/single.cjs +210 -16
  32. package/dist/swap2/single.cjs.map +1 -1
  33. package/dist/swap2/single.js +4 -3
  34. package/dist/types.cjs.map +1 -1
  35. package/dist/types.d.cts +49 -1
  36. package/dist/types.d.ts +49 -1
  37. package/dist/util.cjs +70 -2
  38. package/dist/util.cjs.map +1 -1
  39. package/dist/util.d.cts +13 -2
  40. package/dist/util.d.ts +13 -2
  41. package/dist/util.js +7 -3
  42. package/dist/validate.cjs +19 -0
  43. package/dist/validate.cjs.map +1 -1
  44. package/dist/validate.d.cts +12 -2
  45. package/dist/validate.d.ts +12 -2
  46. package/dist/validate.js +3 -1
  47. package/package.json +5 -5
  48. package/dist/chunk-IGTMUTNJ.js.map +0 -1
  49. package/dist/chunk-JFGMF6FO.js.map +0 -1
  50. package/dist/chunk-KIAQXIBG.js.map +0 -1
  51. package/dist/chunk-MNJTMSFN.js +0 -49
  52. package/dist/chunk-MNJTMSFN.js.map +0 -1
  53. package/dist/chunk-ZMWK3RQ4.js.map +0 -1
@@ -1,9 +1,10 @@
1
1
  import {
2
2
  SingleProviderSwap2
3
- } from "../chunk-JFGMF6FO.js";
4
- import "../chunk-IGTMUTNJ.js";
3
+ } from "../chunk-FHEU7E5A.js";
4
+ import "../chunk-CRCDWV3L.js";
5
5
  import "../chunk-IVR3SREQ.js";
6
- import "../chunk-KIAQXIBG.js";
6
+ import "../chunk-5FPDKWBG.js";
7
+ import "../chunk-2KC734UX.js";
7
8
  export {
8
9
  SingleProviderSwap2
9
10
  };
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/types.ts"],"sourcesContent":["/**\n * Cross-chain asset delivery types. The vault is single-chain; cross-chain is\n * delivery only, via a router over pluggable providers (NEAR Intents + others).\n * A provider can only delay/refund delivery — it can never affect vault safety.\n */\nimport type {ActivityItem, ChainTx, DescribeActivityContext} from \"@anywalletkit/core\";\n\n/** A token on a chain, as providers see it. */\nexport interface Asset {\n chain: string; // CAIP-2, e.g. \"tron:728126428\"\n token: string; // native-format token id/address on that chain\n}\n\n/** What the engine asks a provider to quote. Amounts are base-unit strings; exact input only. */\nexport interface QuoteParams {\n /** Local-only (never serialized): called with the reason when the provider\n * refuses to quote — a shared-instance \"last refusal\" field raced under\n * concurrent quotes, so the reason rides per-call. */\n onRefusal?: (reason: string) => void;\n sourceChain: string; // CAIP-2\n sourceToken: string; // token SYMBOL on source (resolved via the asset catalog), or an unlisted token's contract / mint\n /**\n * Set only for a source token the catalog does not list: `sourceToken` is then its contract\n * (EVM) or mint (Solana) and this its decimals. Providers that can trade any token resolve it with\n * `resolveAsset(chain, sourceToken, {allowUnlisted: true, decimals})`; the others refuse it.\n */\n sourceDecimals?: number;\n destChain: string; // CAIP-2 (the vault's chain)\n destToken: string; // ERC-20 address on dest (zero address = native ETH)\n amountIn: string; // what the user sends on source (base units, EXACT_INPUT)\n /** Caller-supplied output floor in destination base units. */\n minAmountOut?: string;\n /** Maximum slippage accepted by the caller, expressed in basis points. */\n slippageBps?: number;\n destAddress: string; // recipient on dest (the vault, or a payout address)\n /** Where a failed swap refunds on the source chain (the sender's address). */\n refundAddress?: string;\n /** Pricing preview only — no deposit address is created upstream. */\n dry?: boolean;\n /**\n * Amount-flexible deposit (exchange flow): `amountIn` is only a pricing\n * notional — the user may send any amount to the deposit address (1Click\n * FLEX_INPUT). Providers that can't do this return no quote.\n */\n flex?: boolean;\n}\n\n/**\n * Provider-built transaction for the CALLDATA execution model (LI.FI): the\n * user's wallet must execute this exact payload — a plain transfer to `to`\n * would LOSE funds. Either an EVM request or a pre-serialized Solana tx.\n */\nexport interface TxRequest {\n to?: string;\n data?: string; // EVM calldata\n value?: string; // wei, decimal string\n chainId?: number;\n gasLimit?: string;\n /** ERC-20 sources must approve this spender before executing. */\n approvalAddress?: string;\n /**\n * Native coin the route itself charges on top of the input (bridge\n * messaging fees — Stargate/LayerZero-style), base units. A token source\n * then sends exactly this as `value`; a native source sends input + this.\n * Declared by the provider from the quote, sealed with it, and capped by\n * the engine per chain.\n */\n nativeFee?: string;\n /** Solana: base64-serialized transaction to sign & broadcast as-is. */\n serializedTx?: string;\n}\n\n/** A normalized quote from one provider. */\nexport interface Quote {\n /** Provider name. */\n provider: string;\n /** Provider's id for this swap; tracked with `trackIntent`. */\n intentId: string;\n depositAddress: string; // where the user sends source funds (\"\" on dry quotes)\n depositMemo: string | null; // MUST accompany the deposit when present (TON)\n /** Present instead of a usable depositAddress for calldata providers. */\n txRequest?: TxRequest;\n sourceAmount: string; // what the user pays on source (base units)\n destAmount: string; // what arrives on dest (base units)\n minDestAmount: string; // worst case at the quoted slippage (base units)\n feeUsd: number; // normalized fee for scoring\n /** Honest itemized fees when the provider exposes base-unit amounts. */\n fees?: {type: \"network\" | \"protocol\" | \"affiliate\" | \"other\"; amount: string; token: string; included: boolean; description?: string}[];\n /** Estimated time to delivery. */\n etaSeconds: number; // estimated settlement time\n /** Provider's reliability hint, 0 to 1. */\n reliability: number; // 0..1 provider/route reliability hint\n /** Quote expiry, unix milliseconds. */\n validUntilMs: number;\n /** Provider-owned, JSON-safe facts required to revalidate execution. */\n providerData?: Record<string, string>;\n}\n\n/** What trackIntent gets to work with (LI.FI tracks by the source tx hash). */\nexport interface IntentRef {\n intentId: string;\n sourceTxHash?: string | null;\n /** Two-phase routes: the destination-chain claim transaction, once sent. */\n destTxHash?: string | null;\n}\n\n/** The router's chosen quote, tagged with how it scored. */\nexport interface RoutedQuote extends Quote {\n routeScore: {\n score: number;\n weights: ScoreWeights;\n /** Every validated route: `score` is its value against the best (0..1). */\n candidates: {provider: string; sourceAmount: string; etaSeconds: number; score: number; minDestAmount?: string; netValueUsd?: number | null}[];\n };\n}\n\n/** How the router weighs price, speed and reliability when it scores routes. */\nexport interface ScoreWeights {\n price: number;\n speed: number;\n reliability: number;\n}\n\n/** A swap's state as the engine reports it; maps to `Swap2Status`. */\nexport type IntentState =\n | \"pending\"\n | \"action-required\"\n | \"refund-pending\"\n | \"partial\"\n | \"completed\"\n | \"failed\"\n | \"refunded\"\n | \"cancelled\"\n | \"expired\";\n\n/** A provider's answer to `trackIntent`. */\nexport interface IntentStatus {\n state: IntentState;\n /** Still pending, but the source deposit has been observed (PROCESSING). */\n depositReceived?: boolean;\n destTxHash?: string;\n /**\n * Two-phase routes (CCTP-style burn-and-mint): the delivery needs a second,\n * user-signed transaction on the destination chain. `ready` means the\n * provider can build it now (`buildClaim`); `state` is then\n * \"action-required\". Before that the route is \"pending\" with `ready: false`.\n */\n claim?: {chain: string; ready: boolean};\n}\n\n/** A provider's source-leg transaction, before the engine checks and signs it. */\nexport interface UnsignedTx {\n chain: string;\n to: string;\n value: string;\n data?: string;\n chainId?: number;\n gasLimit?: string;\n /** ERC-20 spender that must be approved before the calldata is sent. */\n approvalAddress?: string;\n /** Provider-native serialized transaction (for accounts that expose a hook). */\n serializedTx?: string;\n /** Native-chain memo/body required by the deposit protocol. */\n memo?: string;\n /** How `memo` becomes bytes on chains whose memo is a byte field (Tron `raw_data.data`): 'utf8' text (default) or\n * 'hex' — a binary payload given as 0x-hex (a Chainflip vault-swap note). */\n memoEncoding?: \"utf8\" | \"hex\";\n /**\n * TON: serialized message-body CELL (base64 BoC) — e.g. an Omniston escrow\n * payload. NOT interchangeable with `memo`: a memo is a text comment, while\n * sending a BoC as text strands the swap. Executors must decode it to a Cell.\n */\n bodyBoc?: string;\n /** TON: contract stateInit (base64 BoC) the message must carry, when present. */\n stateInitBoc?: string;\n /**\n * GASLESS typed-order execution (Omniston EVM-source): the JSON EIP-712\n * payload the account must sign — there is NO source transaction. After\n * signing, complete via `CrossChainProvider.registerSignedOrder`. When set,\n * `approvalAddress` names the settlement contract that needs allowance.\n */\n typedData?: string;\n}\n\n/**\n * Pluggable cross-chain provider. Adding a provider is \"add a module\"; the rest\n * of the app references only this interface (never a specific provider).\n */\nexport interface CrossChainProvider {\n /** Provider name, unique in a router. */\n readonly name: string;\n /** Master flag — providers ship \"dark\" (off) until validated with real funds. */\n enabled(): boolean;\n /** Cheap synchronous check: can this provider route `from` to `to`? No I/O. */\n supportsRoute(from: Asset, to: Asset): boolean;\n /** A quote, or `null` with the reason passed to `params.onRefusal`. */\n getQuote(params: QuoteParams, signal?: AbortSignal): Promise<Quote | null>;\n /** The source-leg transaction for a quote. */\n buildDeposit(quote: Quote): Promise<UnsignedTx>;\n /**\n * Last fail-closed check after any approval and immediately before the\n * wallet signs/broadcasts the source leg. Providers whose execution target\n * can rotate or halt (THORChain vaults/routers) must revalidate it here.\n */\n preflightDeposit?(quote: Quote, tx: UnsignedTx): Promise<void>;\n /** Current status of a swap. */\n trackIntent(ref: IntentRef): Promise<IntentStatus>;\n /**\n * Tell the provider the source funds were sent (tx hash / BoC). Required for\n * NEAR Intents TON deposits — the HOT-bridge oracle can't observe SIMPLE-mode\n * deposits on uninitialized addresses without it. Best-effort elsewhere.\n */\n notifyDeposit?(intentId: string, txHash: string): Promise<void>;\n /** Gasless typed-order providers: register the wallet's EIP-712 signature. */\n registerSignedOrder?(intentId: string, signature: string): Promise<void>;\n /**\n * Two-phase routes: the destination-chain transaction that completes the\n * delivery (CCTP `receiveMessage(message, attestation)`), or `null` while\n * the attestation is not available yet. The engine executes it through a\n * destination-chain account (`claimSwap2`); `tx.chain` names that chain.\n */\n buildClaim?(ref: IntentRef): Promise<UnsignedTx | null>;\n /**\n * The contracts a claim from {@link buildClaim} may call on `chain` (CAIP-2) — CCTP's\n * MessageTransmitter. `claimSwap2` refuses a claim to anything else, and a provider with a claim\n * step must declare them (round 3, M24). Kept on the client: for a remote provider this is the\n * app's list, never the server's.\n */\n claimTargets?(chain: string): readonly string[];\n close?(): void;\n /** Optional discovery hook for custom providers. */\n supportsChain?(caip2: string): boolean;\n /** Optional pair-level discovery for aggregators. */\n supportsChainPair?(fromChain: string, toChain: string): boolean;\n /** Explicit opt-in: provider can execute a same-chain swap. */\n supportsSameChain?: boolean;\n /**\n * Recognize one of this provider's own transactions in an address's history\n * (contract + selector on EVM, settlement counterparty elsewhere) and\n * describe it for the activity feed; null when it is not this provider's.\n * Pure and synchronous. The single-provider protocol classes and the router\n * delegate `describeActivity` here.\n */\n describeActivity?(tx: ChainTx, ctx: DescribeActivityContext): ActivityItem | null;\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAAA;AAAA;","names":[]}
1
+ {"version":3,"sources":["../src/types.ts"],"sourcesContent":["/**\n * Cross-chain asset delivery types. The vault is single-chain; cross-chain is\n * delivery only, via a router over pluggable providers (NEAR Intents + others).\n * A provider can only delay/refund delivery — it can never affect vault safety.\n */\nimport type {ActivityItem, ChainTx, DescribeActivityContext} from \"@anywalletkit/core\";\n\n/** A token on a chain, as providers see it. */\nexport interface Asset {\n chain: string; // CAIP-2, e.g. \"tron:728126428\"\n token: string; // native-format token id/address on that chain\n}\n\n/** What the engine asks a provider to quote. Amounts are base-unit strings; exact input only. */\nexport interface QuoteParams {\n /** Local-only (never serialized): called with the reason when the provider\n * refuses to quote — a shared-instance \"last refusal\" field raced under\n * concurrent quotes, so the reason rides per-call. */\n onRefusal?: (reason: string) => void;\n sourceChain: string; // CAIP-2\n sourceToken: string; // token SYMBOL on source (resolved via the asset catalog), or an unlisted token's contract / mint\n /**\n * Set only for a source token the catalog does not list: `sourceToken` is then its contract\n * (EVM) or mint (Solana) and this its decimals. Providers that can trade any token resolve it with\n * `resolveAsset(chain, sourceToken, {allowUnlisted: true, decimals})`; the others refuse it.\n */\n sourceDecimals?: number;\n destChain: string; // CAIP-2 (the vault's chain)\n destToken: string; // ERC-20 address on dest (zero address = native ETH)\n amountIn: string; // what the user sends on source (base units, EXACT_INPUT)\n /** Caller-supplied output floor in destination base units. */\n minAmountOut?: string;\n /** Maximum slippage accepted by the caller, expressed in basis points. */\n slippageBps?: number;\n destAddress: string; // recipient on dest (the vault, or a payout address)\n /** Where a failed swap refunds on the source chain (the sender's address). */\n refundAddress?: string;\n /** Pricing preview only — no deposit address is created upstream. */\n dry?: boolean;\n /**\n * Amount-flexible deposit (exchange flow): `amountIn` is only a pricing\n * notional — the user may send any amount to the deposit address (1Click\n * FLEX_INPUT). Providers that can't do this return no quote.\n */\n flex?: boolean;\n}\n\n/**\n * Provider-built transaction for the CALLDATA execution model (LI.FI): the\n * user's wallet must execute this exact payload — a plain transfer to `to`\n * would LOSE funds. Either an EVM request or a pre-serialized Solana tx.\n */\nexport interface TxRequest {\n to?: string;\n data?: string; // EVM calldata\n value?: string; // wei, decimal string\n chainId?: number;\n gasLimit?: string;\n /** ERC-20 sources must approve this spender before executing. */\n approvalAddress?: string;\n /**\n * Native coin the route itself charges on top of the input (bridge\n * messaging fees — Stargate/LayerZero-style), base units. A token source\n * then sends exactly this as `value`; a native source sends input + this.\n * Declared by the provider from the quote, sealed with it, and capped by\n * the engine per chain.\n */\n nativeFee?: string;\n /** Solana: base64-serialized transaction to sign & broadcast as-is. */\n serializedTx?: string;\n}\n\n/** A normalized quote from one provider. */\nexport interface Quote {\n /** Provider name. */\n provider: string;\n /** Provider's id for this swap; tracked with `trackIntent`. */\n intentId: string;\n depositAddress: string; // where the user sends source funds (\"\" on dry quotes)\n depositMemo: string | null; // MUST accompany the deposit when present (TON)\n /** Present instead of a usable depositAddress for calldata providers. */\n txRequest?: TxRequest;\n sourceAmount: string; // what the user pays on source (base units)\n destAmount: string; // what arrives on dest (base units)\n minDestAmount: string; // worst case at the quoted slippage (base units)\n feeUsd: number; // normalized fee for scoring\n /** Honest itemized fees when the provider exposes base-unit amounts. */\n fees?: {type: \"network\" | \"protocol\" | \"affiliate\" | \"other\"; amount: string; token: string; included: boolean; description?: string}[];\n /** Estimated time to delivery. */\n etaSeconds: number; // estimated settlement time\n /** Provider's reliability hint, 0 to 1. */\n reliability: number; // 0..1 provider/route reliability hint\n /** Quote expiry, unix milliseconds. */\n validUntilMs: number;\n /** Provider-owned, JSON-safe facts required to revalidate execution. */\n providerData?: Record<string, string>;\n}\n\n/** What trackIntent gets to work with (LI.FI tracks by the source tx hash). */\nexport interface IntentRef {\n intentId: string;\n sourceTxHash?: string | null;\n /** Two-phase routes: the destination-chain claim transaction, once sent. */\n destTxHash?: string | null;\n}\n\n/** The router's chosen quote, tagged with how it scored. */\nexport interface RoutedQuote extends Quote {\n routeScore: {\n score: number;\n weights: ScoreWeights;\n /** Every validated route: `score` is its value against the best (0..1). */\n candidates: {provider: string; sourceAmount: string; etaSeconds: number; score: number; minDestAmount?: string; netValueUsd?: number | null}[];\n };\n}\n\n/** How the router weighs price, speed and reliability when it scores routes. */\nexport interface ScoreWeights {\n price: number;\n speed: number;\n reliability: number;\n}\n\n/** A swap's state as the engine reports it; maps to `Swap2Status`. */\nexport type IntentState =\n | \"pending\"\n | \"action-required\"\n | \"refund-pending\"\n | \"partial\"\n | \"completed\"\n | \"failed\"\n | \"refunded\"\n | \"cancelled\"\n | \"expired\";\n\n/** A provider's answer to `trackIntent`. */\nexport interface IntentStatus {\n state: IntentState;\n /** Still pending, but the source deposit has been observed (PROCESSING). */\n depositReceived?: boolean;\n destTxHash?: string;\n /**\n * Two-phase routes (CCTP-style burn-and-mint): the delivery needs a second,\n * user-signed transaction on the destination chain. `ready` means the\n * provider can build it now (`buildClaim`); `state` is then\n * \"action-required\". Before that the route is \"pending\" with `ready: false`.\n */\n claim?: {chain: string; ready: boolean};\n}\n\n/** A provider's source-leg transaction, before the engine checks and signs it. */\nexport interface UnsignedTx {\n chain: string;\n to: string;\n value: string;\n data?: string;\n chainId?: number;\n gasLimit?: string;\n /** ERC-20 spender that must be approved before the calldata is sent. */\n approvalAddress?: string;\n /** Provider-native serialized transaction (for accounts that expose a hook). */\n serializedTx?: string;\n /** Native-chain memo/body required by the deposit protocol. */\n memo?: string;\n /** How `memo` becomes bytes on chains whose memo is a byte field (Tron `raw_data.data`): 'utf8' text (default) or\n * 'hex' — a binary payload given as 0x-hex (a Chainflip vault-swap note). */\n memoEncoding?: \"utf8\" | \"hex\";\n /**\n * TON: serialized message-body CELL (base64 BoC) — e.g. an Omniston escrow\n * payload. NOT interchangeable with `memo`: a memo is a text comment, while\n * sending a BoC as text strands the swap. Executors must decode it to a Cell.\n */\n bodyBoc?: string;\n /** TON: contract stateInit (base64 BoC) the message must carry, when present. */\n stateInitBoc?: string;\n /**\n * GASLESS typed-order execution (Omniston EVM-source): the JSON EIP-712\n * payload the account must sign — there is NO source transaction. After\n * signing, complete via `CrossChainProvider.registerSignedOrder`. When set,\n * `approvalAddress` names the settlement contract that needs allowance.\n */\n typedData?: string;\n /**\n * Typed-order execution: the order extension (0x-hex) the order's salt commits to — a 1inch Limit Order Protocol\n * order's amount getters and post-interaction, where an HTLC order's escrow terms live. Carried so an app can open\n * a remote build on the client (`omnistonVerifyBuild`, scan 2026-10-28 F6); the signature covers it through the salt.\n */\n orderExtension?: string;\n}\n\n/**\n * The contracts a provider's EVM swaps call and approve on a chain, shipped by the provider package\n * (`lifiPins`, `fyndPins`, `uniswapPins`…) and handed to `remoteProvider({...pins})`: a backend's\n * build is checked against the app's addresses, never against its own (scan 2026-10-27 modules,\n * F8/F9/F16). An empty list means the provider has no EVM calldata route on that chain.\n */\nexport interface RemotePins {\n callTargets(chain: string): readonly string[];\n spenders(chain: string): readonly string[];\n /**\n * A client-side check of a remote quote's transaction against the user's own request — the\n * provider's local validator run again on this side (Fynd: `checkFyndTransaction`): the reason it\n * fails, or null.\n */\n verifyQuote?(params: QuoteParams, quote: Quote): string | null;\n /** A client-side check of a remote build (typed order, cell body) against its quote: the reason it fails, or null. */\n verifyBuild?(quote: Quote, tx: UnsignedTx, params?: QuoteParams): string | null | Promise<string | null>;\n /**\n * The provider's routes pay an address the backend names — a deposit address, or a plain value send — instead of\n * calling a pinned contract (`thorchainPins` for a Tron vault, NEAR Intents' per-quote deposit addresses). Without\n * it a remote stub's deposit-address and plain-value routes are refused (scan 2026-10-28, F9).\n */\n depositAddressRoutes?: boolean;\n}\n\n/**\n * Pluggable cross-chain provider. Adding a provider is \"add a module\"; the rest\n * of the app references only this interface (never a specific provider).\n */\nexport interface CrossChainProvider {\n /** Provider name, unique in a router. */\n readonly name: string;\n /** Master flag — providers ship \"dark\" (off) until validated with real funds. */\n enabled(): boolean;\n /** Cheap synchronous check: can this provider route `from` to `to`? No I/O. */\n supportsRoute(from: Asset, to: Asset): boolean;\n /** A quote, or `null` with the reason passed to `params.onRefusal`. */\n getQuote(params: QuoteParams, signal?: AbortSignal): Promise<Quote | null>;\n /** The source-leg transaction for a quote. */\n buildDeposit(quote: Quote): Promise<UnsignedTx>;\n /**\n * Last fail-closed check after any approval and immediately before the\n * wallet signs/broadcasts the source leg. Providers whose execution target\n * can rotate or halt (THORChain vaults/routers) must revalidate it here.\n */\n preflightDeposit?(quote: Quote, tx: UnsignedTx): Promise<void>;\n /** Current status of a swap. */\n trackIntent(ref: IntentRef): Promise<IntentStatus>;\n /**\n * Tell the provider the source funds were sent (tx hash / BoC). Required for\n * NEAR Intents TON deposits — the HOT-bridge oracle can't observe SIMPLE-mode\n * deposits on uninitialized addresses without it. Best-effort elsewhere.\n */\n notifyDeposit?(intentId: string, txHash: string): Promise<void>;\n /** Gasless typed-order providers: register the wallet's EIP-712 signature. */\n registerSignedOrder?(intentId: string, signature: string): Promise<void>;\n /**\n * Two-phase routes: the destination-chain transaction that completes the\n * delivery (CCTP `receiveMessage(message, attestation)`), or `null` while\n * the attestation is not available yet. The engine executes it through a\n * destination-chain account (`claimSwap2`); `tx.chain` names that chain.\n */\n buildClaim?(ref: IntentRef): Promise<UnsignedTx | null>;\n /**\n * The contracts a claim from {@link buildClaim} may call on `chain` (CAIP-2) — CCTP's\n * MessageTransmitter. `claimSwap2` refuses a claim to anything else, and a provider with a claim\n * step must declare them (round 3, M24). Kept on the client: for a remote provider this is the\n * app's list, never the server's.\n */\n claimTargets?(chain: string): readonly string[];\n /**\n * Set by `remoteProvider`: the quotes and builds come from a backend, not from code the app\n * ships. The engine then takes the addresses an EVM transaction may call or approve only from\n * {@link callTargets} / {@link spenders} — the app's pins — and refuses a calldata route or an\n * approval it has no pin for (scan 2026-10-27 modules, F8/F9/F16).\n */\n readonly remote?: boolean;\n /** Set by `remoteProvider` when the app gave it `verifyBuild`: its builds are checked on the client. */\n readonly verifiesBuilds?: boolean;\n /**\n * Set by `remoteProvider` when the app opted in (`depositAddressRoutes`): this stub may send the input to an address\n * its backend names. Without it the engine refuses a remote stub's deposit-address and plain-value builds.\n */\n readonly depositAddressRoutes?: boolean;\n /** Remote stubs: the contracts this provider's EVM swaps may call on `chain` (the app's list, e.g. `lifiPins`). */\n callTargets?(chain: string): readonly string[];\n /** Remote stubs: the spenders this provider's EVM swaps may be approved for on `chain` (the app's list). */\n spenders?(chain: string): readonly string[];\n close?(): void;\n /** Optional discovery hook for custom providers. */\n supportsChain?(caip2: string): boolean;\n /** Optional pair-level discovery for aggregators. */\n supportsChainPair?(fromChain: string, toChain: string): boolean;\n /** Explicit opt-in: provider can execute a same-chain swap. */\n supportsSameChain?: boolean;\n /**\n * Recognize one of this provider's own transactions in an address's history\n * (contract + selector on EVM, settlement counterparty elsewhere) and\n * describe it for the activity feed; null when it is not this provider's.\n * Pure and synchronous. The single-provider protocol classes and the router\n * delegate `describeActivity` here.\n */\n describeActivity?(tx: ChainTx, ctx: DescribeActivityContext): ActivityItem | null;\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAAA;AAAA;","names":[]}
package/dist/types.d.cts CHANGED
@@ -179,6 +179,36 @@ interface UnsignedTx {
179
179
  * `approvalAddress` names the settlement contract that needs allowance.
180
180
  */
181
181
  typedData?: string;
182
+ /**
183
+ * Typed-order execution: the order extension (0x-hex) the order's salt commits to — a 1inch Limit Order Protocol
184
+ * order's amount getters and post-interaction, where an HTLC order's escrow terms live. Carried so an app can open
185
+ * a remote build on the client (`omnistonVerifyBuild`, scan 2026-10-28 F6); the signature covers it through the salt.
186
+ */
187
+ orderExtension?: string;
188
+ }
189
+ /**
190
+ * The contracts a provider's EVM swaps call and approve on a chain, shipped by the provider package
191
+ * (`lifiPins`, `fyndPins`, `uniswapPins`…) and handed to `remoteProvider({...pins})`: a backend's
192
+ * build is checked against the app's addresses, never against its own (scan 2026-10-27 modules,
193
+ * F8/F9/F16). An empty list means the provider has no EVM calldata route on that chain.
194
+ */
195
+ interface RemotePins {
196
+ callTargets(chain: string): readonly string[];
197
+ spenders(chain: string): readonly string[];
198
+ /**
199
+ * A client-side check of a remote quote's transaction against the user's own request — the
200
+ * provider's local validator run again on this side (Fynd: `checkFyndTransaction`): the reason it
201
+ * fails, or null.
202
+ */
203
+ verifyQuote?(params: QuoteParams, quote: Quote): string | null;
204
+ /** A client-side check of a remote build (typed order, cell body) against its quote: the reason it fails, or null. */
205
+ verifyBuild?(quote: Quote, tx: UnsignedTx, params?: QuoteParams): string | null | Promise<string | null>;
206
+ /**
207
+ * The provider's routes pay an address the backend names — a deposit address, or a plain value send — instead of
208
+ * calling a pinned contract (`thorchainPins` for a Tron vault, NEAR Intents' per-quote deposit addresses). Without
209
+ * it a remote stub's deposit-address and plain-value routes are refused (scan 2026-10-28, F9).
210
+ */
211
+ depositAddressRoutes?: boolean;
182
212
  }
183
213
  /**
184
214
  * Pluggable cross-chain provider. Adding a provider is "add a module"; the rest
@@ -225,6 +255,24 @@ interface CrossChainProvider {
225
255
  * app's list, never the server's.
226
256
  */
227
257
  claimTargets?(chain: string): readonly string[];
258
+ /**
259
+ * Set by `remoteProvider`: the quotes and builds come from a backend, not from code the app
260
+ * ships. The engine then takes the addresses an EVM transaction may call or approve only from
261
+ * {@link callTargets} / {@link spenders} — the app's pins — and refuses a calldata route or an
262
+ * approval it has no pin for (scan 2026-10-27 modules, F8/F9/F16).
263
+ */
264
+ readonly remote?: boolean;
265
+ /** Set by `remoteProvider` when the app gave it `verifyBuild`: its builds are checked on the client. */
266
+ readonly verifiesBuilds?: boolean;
267
+ /**
268
+ * Set by `remoteProvider` when the app opted in (`depositAddressRoutes`): this stub may send the input to an address
269
+ * its backend names. Without it the engine refuses a remote stub's deposit-address and plain-value builds.
270
+ */
271
+ readonly depositAddressRoutes?: boolean;
272
+ /** Remote stubs: the contracts this provider's EVM swaps may call on `chain` (the app's list, e.g. `lifiPins`). */
273
+ callTargets?(chain: string): readonly string[];
274
+ /** Remote stubs: the spenders this provider's EVM swaps may be approved for on `chain` (the app's list). */
275
+ spenders?(chain: string): readonly string[];
228
276
  close?(): void;
229
277
  /** Optional discovery hook for custom providers. */
230
278
  supportsChain?(caip2: string): boolean;
@@ -242,4 +290,4 @@ interface CrossChainProvider {
242
290
  describeActivity?(tx: ChainTx, ctx: DescribeActivityContext): ActivityItem | null;
243
291
  }
244
292
 
245
- export type { Asset, CrossChainProvider, IntentRef, IntentState, IntentStatus, Quote, QuoteParams, RoutedQuote, ScoreWeights, TxRequest, UnsignedTx };
293
+ export type { Asset, CrossChainProvider, IntentRef, IntentState, IntentStatus, Quote, QuoteParams, RemotePins, RoutedQuote, ScoreWeights, TxRequest, UnsignedTx };
package/dist/types.d.ts CHANGED
@@ -179,6 +179,36 @@ interface UnsignedTx {
179
179
  * `approvalAddress` names the settlement contract that needs allowance.
180
180
  */
181
181
  typedData?: string;
182
+ /**
183
+ * Typed-order execution: the order extension (0x-hex) the order's salt commits to — a 1inch Limit Order Protocol
184
+ * order's amount getters and post-interaction, where an HTLC order's escrow terms live. Carried so an app can open
185
+ * a remote build on the client (`omnistonVerifyBuild`, scan 2026-10-28 F6); the signature covers it through the salt.
186
+ */
187
+ orderExtension?: string;
188
+ }
189
+ /**
190
+ * The contracts a provider's EVM swaps call and approve on a chain, shipped by the provider package
191
+ * (`lifiPins`, `fyndPins`, `uniswapPins`…) and handed to `remoteProvider({...pins})`: a backend's
192
+ * build is checked against the app's addresses, never against its own (scan 2026-10-27 modules,
193
+ * F8/F9/F16). An empty list means the provider has no EVM calldata route on that chain.
194
+ */
195
+ interface RemotePins {
196
+ callTargets(chain: string): readonly string[];
197
+ spenders(chain: string): readonly string[];
198
+ /**
199
+ * A client-side check of a remote quote's transaction against the user's own request — the
200
+ * provider's local validator run again on this side (Fynd: `checkFyndTransaction`): the reason it
201
+ * fails, or null.
202
+ */
203
+ verifyQuote?(params: QuoteParams, quote: Quote): string | null;
204
+ /** A client-side check of a remote build (typed order, cell body) against its quote: the reason it fails, or null. */
205
+ verifyBuild?(quote: Quote, tx: UnsignedTx, params?: QuoteParams): string | null | Promise<string | null>;
206
+ /**
207
+ * The provider's routes pay an address the backend names — a deposit address, or a plain value send — instead of
208
+ * calling a pinned contract (`thorchainPins` for a Tron vault, NEAR Intents' per-quote deposit addresses). Without
209
+ * it a remote stub's deposit-address and plain-value routes are refused (scan 2026-10-28, F9).
210
+ */
211
+ depositAddressRoutes?: boolean;
182
212
  }
183
213
  /**
184
214
  * Pluggable cross-chain provider. Adding a provider is "add a module"; the rest
@@ -225,6 +255,24 @@ interface CrossChainProvider {
225
255
  * app's list, never the server's.
226
256
  */
227
257
  claimTargets?(chain: string): readonly string[];
258
+ /**
259
+ * Set by `remoteProvider`: the quotes and builds come from a backend, not from code the app
260
+ * ships. The engine then takes the addresses an EVM transaction may call or approve only from
261
+ * {@link callTargets} / {@link spenders} — the app's pins — and refuses a calldata route or an
262
+ * approval it has no pin for (scan 2026-10-27 modules, F8/F9/F16).
263
+ */
264
+ readonly remote?: boolean;
265
+ /** Set by `remoteProvider` when the app gave it `verifyBuild`: its builds are checked on the client. */
266
+ readonly verifiesBuilds?: boolean;
267
+ /**
268
+ * Set by `remoteProvider` when the app opted in (`depositAddressRoutes`): this stub may send the input to an address
269
+ * its backend names. Without it the engine refuses a remote stub's deposit-address and plain-value builds.
270
+ */
271
+ readonly depositAddressRoutes?: boolean;
272
+ /** Remote stubs: the contracts this provider's EVM swaps may call on `chain` (the app's list, e.g. `lifiPins`). */
273
+ callTargets?(chain: string): readonly string[];
274
+ /** Remote stubs: the spenders this provider's EVM swaps may be approved for on `chain` (the app's list). */
275
+ spenders?(chain: string): readonly string[];
228
276
  close?(): void;
229
277
  /** Optional discovery hook for custom providers. */
230
278
  supportsChain?(caip2: string): boolean;
@@ -242,4 +290,4 @@ interface CrossChainProvider {
242
290
  describeActivity?(tx: ChainTx, ctx: DescribeActivityContext): ActivityItem | null;
243
291
  }
244
292
 
245
- export type { Asset, CrossChainProvider, IntentRef, IntentState, IntentStatus, Quote, QuoteParams, RoutedQuote, ScoreWeights, TxRequest, UnsignedTx };
293
+ export type { Asset, CrossChainProvider, IntentRef, IntentState, IntentStatus, Quote, QuoteParams, RemotePins, RoutedQuote, ScoreWeights, TxRequest, UnsignedTx };
package/dist/util.cjs CHANGED
@@ -21,12 +21,14 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
21
21
  var util_exports = {};
22
22
  __export(util_exports, {
23
23
  anySignal: () => anySignal,
24
+ base64Bytes: () => base64Bytes,
24
25
  evmRef: () => evmRef,
25
26
  isEvm: () => isEvm,
26
27
  namespace: () => namespace,
27
28
  redactUrls: () => redactUrls,
28
29
  sourceAssetOf: () => sourceAssetOf,
29
- timeoutSignal: () => timeoutSignal
30
+ timeoutSignal: () => timeoutSignal,
31
+ tonRawAddress: () => tonRawAddress
30
32
  });
31
33
  module.exports = __toCommonJS(util_exports);
32
34
  var import_swap2_assets = require("@anywalletkit/swap2-assets");
@@ -59,11 +61,77 @@ function anySignal(signals) {
59
61
  }
60
62
  return ctrl.signal;
61
63
  }
64
+ var SCHEME_MAX = 32;
65
+ var isLetter = (c) => c >= 97 && c <= 122 || c >= 65 && c <= 90;
66
+ var isSchemeChar = (c) => isLetter(c) || c >= 48 && c <= 57 || c === 43 || c === 45 || c === 46;
67
+ var endsUrl = (c) => c === 34 || c === 39 || c === 60 || c === 62 || c >= 9 && c <= 13 || c === 32 || c === 160 || c === 5760 || c >= 8192 && c <= 8202 || c === 8232 || c === 8233 || c === 8239 || c === 8287 || c === 12288 || c === 65279;
62
68
  function redactUrls(text) {
63
- return text.replace(/\b[a-z][a-z0-9+.-]*:\/\/[^\s"'<>]+/gi, "<url>");
69
+ let out = "";
70
+ let done = 0;
71
+ for (let at = text.indexOf("://"); at !== -1; at = text.indexOf("://", at + 3)) {
72
+ let start = at;
73
+ while (start > done && at - start < SCHEME_MAX && isSchemeChar(text.charCodeAt(start - 1))) start--;
74
+ while (start < at && !isLetter(text.charCodeAt(start))) start++;
75
+ if (start === at) continue;
76
+ let end = at + 3;
77
+ while (end < text.length && !endsUrl(text.charCodeAt(end))) end++;
78
+ if (end === at + 3) continue;
79
+ out += `${text.slice(done, start)}<url>`;
80
+ done = end;
81
+ at = end - 3;
82
+ }
83
+ return done === 0 ? text : out + text.slice(done);
64
84
  }
65
85
  function sourceAssetOf(params) {
66
86
  const decimals = params.sourceDecimals;
67
87
  return (0, import_swap2_assets.resolveAsset)(params.sourceChain, params.sourceToken, decimals === void 0 ? {} : { allowUnlisted: true, decimals });
68
88
  }
89
+ var B64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
90
+ function base64Bytes(text) {
91
+ const s = text.replace(/-/g, "+").replace(/_/g, "/").replace(/=+$/, "");
92
+ if (!/^[A-Za-z0-9+/]*$/.test(s) || s.length % 4 === 1) return null;
93
+ const out = new Uint8Array(Math.floor(s.length * 3 / 4));
94
+ let bits = 0, acc = 0, n = 0;
95
+ for (const ch of s) {
96
+ acc = acc << 6 | B64.indexOf(ch);
97
+ bits += 6;
98
+ if (bits >= 8) {
99
+ bits -= 8;
100
+ out[n++] = acc >> bits & 255;
101
+ }
102
+ }
103
+ return out.subarray(0, n);
104
+ }
105
+ function crc16(bytes) {
106
+ let crc = 0;
107
+ for (const b of bytes) {
108
+ crc ^= b << 8;
109
+ for (let i = 0; i < 8; i++) crc = crc & 32768 ? (crc << 1 ^ 4129) & 65535 : crc << 1 & 65535;
110
+ }
111
+ return crc;
112
+ }
113
+ function tonRawAddress(address) {
114
+ if (address && typeof address === "object") {
115
+ const o = address;
116
+ if (typeof o.toRawString === "function") {
117
+ try {
118
+ return tonRawAddress(o.toRawString());
119
+ } catch {
120
+ return null;
121
+ }
122
+ }
123
+ if (typeof o.workChain === "number" && o.hash instanceof Uint8Array && o.hash.length === 32) {
124
+ return `${o.workChain}:${Array.from(o.hash, (b) => b.toString(16).padStart(2, "0")).join("")}`;
125
+ }
126
+ return null;
127
+ }
128
+ if (typeof address !== "string") return null;
129
+ const raw = /^(-?[0-9]{1,3}):([0-9a-fA-F]{64})$/.exec(address.trim());
130
+ if (raw) return `${Number(raw[1])}:${raw[2].toLowerCase()}`;
131
+ if (!/^[A-Za-z0-9+/_-]{48}$/.test(address.trim())) return null;
132
+ const bytes = base64Bytes(address.trim());
133
+ if (!bytes || bytes.length !== 36 || crc16(bytes.subarray(0, 34)) !== (bytes[34] << 8 | bytes[35])) return null;
134
+ const wc = bytes[1] === 255 ? -1 : bytes[1];
135
+ return `${wc}:${Array.from(bytes.subarray(2, 34), (b) => b.toString(16).padStart(2, "0")).join("")}`;
136
+ }
69
137
  //# sourceMappingURL=util.cjs.map
package/dist/util.cjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/util.ts"],"sourcesContent":["import {resolveAsset, type CcAsset} from \"@anywalletkit/swap2-assets\";\nimport type {QuoteParams} from \"./types.js\";\n/** Shared CAIP-2 helpers. Contribution packages never read deployment env. */\nexport function namespace(chain: string): string {\n return chain.split(\":\")[0] ?? \"\";\n}\n/** True for an `eip155:` chain id. */\nexport function isEvm(chain: string): boolean {\n return namespace(chain) === \"eip155\";\n}\n/** Numeric chain id of an `eip155:<id>` CAIP-2 id. */\nexport function evmRef(chain: string): number {\n return Number(chain.split(\":\")[1] ?? \"0\");\n}\n\n/**\n * `AbortSignal.timeout`, portably: Hermes (React Native) ships AbortController\n * but not the static helpers, and the unguarded call threw\n * \"AbortSignal.timeout is not a function\" on every remote execution — which\n * the auction then read as \"no route\". Falls back to a manual controller.\n */\nexport function timeoutSignal(ms: number): AbortSignal {\n const native = (AbortSignal as unknown as {timeout?: (ms: number) => AbortSignal}).timeout;\n if (typeof native === \"function\") return native.call(AbortSignal, ms);\n const ctrl = new AbortController();\n setTimeout(() => ctrl.abort(), ms);\n return ctrl.signal;\n}\n\n/** `AbortSignal.any`, portably (see timeoutSignal). */\nexport function anySignal(signals: readonly AbortSignal[]): AbortSignal {\n const native = (AbortSignal as unknown as {any?: (s: readonly AbortSignal[]) => AbortSignal}).any;\n if (typeof native === \"function\") return native.call(AbortSignal, signals);\n const ctrl = new AbortController();\n for (const s of signals) {\n if (s.aborted) {\n ctrl.abort();\n break;\n }\n s.addEventListener(\"abort\", () => ctrl.abort(), {once: true});\n }\n return ctrl.signal;\n}\n\n/**\n * Remove URLs from text that may travel to a client (a refusal reason, a build error):\n * transport errors carry the endpoint — and with a keyed RPC (QuickNode, Alchemy,\n * Infura…) the key sits in its path (scan 2026-10-23 F8).\n */\nexport function redactUrls(text: string): string {\n return text.replace(/\\b[a-z][a-z0-9+.-]*:\\/\\/[^\\s\"'<>]+/gi, \"<url>\");\n}\n\n/**\n * The source asset a provider is asked to sell: the catalog's entry for `params.sourceToken`, or —\n * only when the engine sent `sourceDecimals` (an unlisted token the user added) — an unlisted asset\n * named by its contract / mint. Providers that can trade any token use this instead of the catalog\n * lookup; the others keep refusing what the catalog does not list.\n */\nexport function sourceAssetOf(params: Pick<QuoteParams, \"sourceChain\" | \"sourceToken\" | \"sourceDecimals\">): CcAsset | undefined {\n const decimals = params.sourceDecimals;\n return resolveAsset(params.sourceChain, params.sourceToken, decimals === undefined ? {} : {allowUnlisted: true, decimals});\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,0BAAyC;AAGlC,SAAS,UAAU,OAAuB;AAC/C,SAAO,MAAM,MAAM,GAAG,EAAE,CAAC,KAAK;AAChC;AAEO,SAAS,MAAM,OAAwB;AAC5C,SAAO,UAAU,KAAK,MAAM;AAC9B;AAEO,SAAS,OAAO,OAAuB;AAC5C,SAAO,OAAO,MAAM,MAAM,GAAG,EAAE,CAAC,KAAK,GAAG;AAC1C;AAQO,SAAS,cAAc,IAAyB;AACrD,QAAM,SAAU,YAAmE;AACnF,MAAI,OAAO,WAAW,WAAY,QAAO,OAAO,KAAK,aAAa,EAAE;AACpE,QAAM,OAAO,IAAI,gBAAgB;AACjC,aAAW,MAAM,KAAK,MAAM,GAAG,EAAE;AACjC,SAAO,KAAK;AACd;AAGO,SAAS,UAAU,SAA8C;AACtE,QAAM,SAAU,YAA8E;AAC9F,MAAI,OAAO,WAAW,WAAY,QAAO,OAAO,KAAK,aAAa,OAAO;AACzE,QAAM,OAAO,IAAI,gBAAgB;AACjC,aAAW,KAAK,SAAS;AACvB,QAAI,EAAE,SAAS;AACb,WAAK,MAAM;AACX;AAAA,IACF;AACA,MAAE,iBAAiB,SAAS,MAAM,KAAK,MAAM,GAAG,EAAC,MAAM,KAAI,CAAC;AAAA,EAC9D;AACA,SAAO,KAAK;AACd;AAOO,SAAS,WAAW,MAAsB;AAC/C,SAAO,KAAK,QAAQ,wCAAwC,OAAO;AACrE;AAQO,SAAS,cAAc,QAAkG;AAC9H,QAAM,WAAW,OAAO;AACxB,aAAO,kCAAa,OAAO,aAAa,OAAO,aAAa,aAAa,SAAY,CAAC,IAAI,EAAC,eAAe,MAAM,SAAQ,CAAC;AAC3H;","names":[]}
1
+ {"version":3,"sources":["../src/util.ts"],"sourcesContent":["import {resolveAsset, type CcAsset} from \"@anywalletkit/swap2-assets\";\nimport type {QuoteParams} from \"./types.js\";\n/** Shared CAIP-2 helpers. Contribution packages never read deployment env. */\nexport function namespace(chain: string): string {\n return chain.split(\":\")[0] ?? \"\";\n}\n/** True for an `eip155:` chain id. */\nexport function isEvm(chain: string): boolean {\n return namespace(chain) === \"eip155\";\n}\n/** Numeric chain id of an `eip155:<id>` CAIP-2 id. */\nexport function evmRef(chain: string): number {\n return Number(chain.split(\":\")[1] ?? \"0\");\n}\n\n/**\n * `AbortSignal.timeout`, portably: Hermes (React Native) ships AbortController\n * but not the static helpers, and the unguarded call threw\n * \"AbortSignal.timeout is not a function\" on every remote execution — which\n * the auction then read as \"no route\". Falls back to a manual controller.\n */\nexport function timeoutSignal(ms: number): AbortSignal {\n const native = (AbortSignal as unknown as {timeout?: (ms: number) => AbortSignal}).timeout;\n if (typeof native === \"function\") return native.call(AbortSignal, ms);\n const ctrl = new AbortController();\n setTimeout(() => ctrl.abort(), ms);\n return ctrl.signal;\n}\n\n/** `AbortSignal.any`, portably (see timeoutSignal). */\nexport function anySignal(signals: readonly AbortSignal[]): AbortSignal {\n const native = (AbortSignal as unknown as {any?: (s: readonly AbortSignal[]) => AbortSignal}).any;\n if (typeof native === \"function\") return native.call(AbortSignal, signals);\n const ctrl = new AbortController();\n for (const s of signals) {\n if (s.aborted) {\n ctrl.abort();\n break;\n }\n s.addEventListener(\"abort\", () => ctrl.abort(), {once: true});\n }\n return ctrl.signal;\n}\n\n/** Longest URL scheme taken out (`https`, `wss`, `git+ssh`…): a letter and up to 31 of `[A-Za-z0-9+.-]`. */\nconst SCHEME_MAX = 32;\nconst isLetter = (c: number): boolean => (c >= 97 && c <= 122) || (c >= 65 && c <= 90);\nconst isSchemeChar = (c: number): boolean => isLetter(c) || (c >= 48 && c <= 57) || c === 43 || c === 45 || c === 46;\n/** What ends a URL in text: `\\s` as regexes define it, and `\"` `'` `<` `>`. */\nconst endsUrl = (c: number): boolean =>\n c === 34 || c === 39 || c === 60 || c === 62 || (c >= 9 && c <= 13) || c === 32 || c === 0xa0 || c === 0x1680 ||\n (c >= 0x2000 && c <= 0x200a) || c === 0x2028 || c === 0x2029 || c === 0x202f || c === 0x205f || c === 0x3000 || c === 0xfeff;\n\n/**\n * Remove URLs from text that may travel to a client (a refusal reason, a build error):\n * transport errors carry the endpoint — and with a keyed RPC (QuickNode, Alchemy,\n * Infura…) the key sits in its path (scan 2026-10-23 F8). What `/[a-z][a-z0-9+.-]{0,31}:\\/\\/[^\\s\"'<>]+/gi`\n * would replace, found from each `://` instead of tried at every position: linear in the length of\n * `text` whatever an API or a client puts in it (\"a.a.a…\" made the regex this replaced quadratic;\n * scan 2026-10-27 modules, F34). A URL glued to a word loses its last 32 scheme characters and the rest.\n */\nexport function redactUrls(text: string): string {\n let out = \"\";\n let done = 0; // text[0, done) is in `out`\n for (let at = text.indexOf(\"://\"); at !== -1; at = text.indexOf(\"://\", at + 3)) {\n let start = at;\n while (start > done && at - start < SCHEME_MAX && isSchemeChar(text.charCodeAt(start - 1))) start--;\n while (start < at && !isLetter(text.charCodeAt(start))) start++;\n if (start === at) continue; // no scheme: not a URL (and its tail is not scanned, so \"://://…\" stays linear)\n let end = at + 3;\n while (end < text.length && !endsUrl(text.charCodeAt(end))) end++;\n if (end === at + 3) continue; // nothing after \"://\"\n out += `${text.slice(done, start)}<url>`;\n done = end;\n at = end - 3;\n }\n return done === 0 ? text : out + text.slice(done);\n}\n\n/**\n * The source asset a provider is asked to sell: the catalog's entry for `params.sourceToken`, or —\n * only when the engine sent `sourceDecimals` (an unlisted token the user added) — an unlisted asset\n * named by its contract / mint. Providers that can trade any token use this instead of the catalog\n * lookup; the others keep refusing what the catalog does not list.\n */\nexport function sourceAssetOf(params: Pick<QuoteParams, \"sourceChain\" | \"sourceToken\" | \"sourceDecimals\">): CcAsset | undefined {\n const decimals = params.sourceDecimals;\n return resolveAsset(params.sourceChain, params.sourceToken, decimals === undefined ? {} : {allowUnlisted: true, decimals});\n}\n\nconst B64 = \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/\";\n/** Standard or URL-safe base64 → bytes, without Buffer or atob (Hermes); null for anything else. */\nexport function base64Bytes(text: string): Uint8Array | null {\n const s = text.replace(/-/g, \"+\").replace(/_/g, \"/\").replace(/=+$/, \"\");\n if (!/^[A-Za-z0-9+/]*$/.test(s) || s.length % 4 === 1) return null;\n const out = new Uint8Array(Math.floor((s.length * 3) / 4));\n let bits = 0, acc = 0, n = 0;\n for (const ch of s) {\n acc = (acc << 6) | B64.indexOf(ch);\n bits += 6;\n if (bits >= 8) { bits -= 8; out[n++] = (acc >> bits) & 0xff; }\n }\n return out.subarray(0, n);\n}\n\n/** CRC16-XMODEM, the checksum of a user-friendly TON address. */\nfunction crc16(bytes: Uint8Array): number {\n let crc = 0;\n for (const b of bytes) {\n crc ^= b << 8;\n for (let i = 0; i < 8; i++) crc = crc & 0x8000 ? ((crc << 1) ^ 0x1021) & 0xffff : (crc << 1) & 0xffff;\n }\n return crc;\n}\n\n/**\n * A TON address in raw form (`<workchain>:<64 hex>`, lowercase) from a raw string, a user-friendly one (bounceable or\n * not, checksum verified), or an address object (`toRawString()` / `workChain` + `hash`); null for anything else. Two\n * spellings of one address compare equal in this form.\n */\nexport function tonRawAddress(address: unknown): string | null {\n if (address && typeof address === \"object\") {\n const o = address as {toRawString?: () => string; workChain?: unknown; hash?: unknown};\n if (typeof o.toRawString === \"function\") { try { return tonRawAddress(o.toRawString()); } catch { return null; } }\n if (typeof o.workChain === \"number\" && o.hash instanceof Uint8Array && o.hash.length === 32) {\n return `${o.workChain}:${Array.from(o.hash, (b) => b.toString(16).padStart(2, \"0\")).join(\"\")}`;\n }\n return null;\n }\n if (typeof address !== \"string\") return null;\n const raw = /^(-?[0-9]{1,3}):([0-9a-fA-F]{64})$/.exec(address.trim());\n if (raw) return `${Number(raw[1])}:${raw[2]!.toLowerCase()}`;\n if (!/^[A-Za-z0-9+/_-]{48}$/.test(address.trim())) return null;\n const bytes = base64Bytes(address.trim());\n if (!bytes || bytes.length !== 36 || crc16(bytes.subarray(0, 34)) !== ((bytes[34]! << 8) | bytes[35]!)) return null;\n const wc = bytes[1] === 0xff ? -1 : bytes[1]!;\n return `${wc}:${Array.from(bytes.subarray(2, 34), (b) => b.toString(16).padStart(2, \"0\")).join(\"\")}`;\n}\n"],"mappings":"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","names":[]}
package/dist/util.d.cts CHANGED
@@ -20,7 +20,10 @@ declare function anySignal(signals: readonly AbortSignal[]): AbortSignal;
20
20
  /**
21
21
  * Remove URLs from text that may travel to a client (a refusal reason, a build error):
22
22
  * transport errors carry the endpoint — and with a keyed RPC (QuickNode, Alchemy,
23
- * Infura…) the key sits in its path (scan 2026-10-23 F8).
23
+ * Infura…) the key sits in its path (scan 2026-10-23 F8). What `/[a-z][a-z0-9+.-]{0,31}:\/\/[^\s"'<>]+/gi`
24
+ * would replace, found from each `://` instead of tried at every position: linear in the length of
25
+ * `text` whatever an API or a client puts in it ("a.a.a…" made the regex this replaced quadratic;
26
+ * scan 2026-10-27 modules, F34). A URL glued to a word loses its last 32 scheme characters and the rest.
24
27
  */
25
28
  declare function redactUrls(text: string): string;
26
29
  /**
@@ -30,5 +33,13 @@ declare function redactUrls(text: string): string;
30
33
  * lookup; the others keep refusing what the catalog does not list.
31
34
  */
32
35
  declare function sourceAssetOf(params: Pick<QuoteParams, "sourceChain" | "sourceToken" | "sourceDecimals">): CcAsset | undefined;
36
+ /** Standard or URL-safe base64 → bytes, without Buffer or atob (Hermes); null for anything else. */
37
+ declare function base64Bytes(text: string): Uint8Array | null;
38
+ /**
39
+ * A TON address in raw form (`<workchain>:<64 hex>`, lowercase) from a raw string, a user-friendly one (bounceable or
40
+ * not, checksum verified), or an address object (`toRawString()` / `workChain` + `hash`); null for anything else. Two
41
+ * spellings of one address compare equal in this form.
42
+ */
43
+ declare function tonRawAddress(address: unknown): string | null;
33
44
 
34
- export { anySignal, evmRef, isEvm, namespace, redactUrls, sourceAssetOf, timeoutSignal };
45
+ export { anySignal, base64Bytes, evmRef, isEvm, namespace, redactUrls, sourceAssetOf, timeoutSignal, tonRawAddress };
package/dist/util.d.ts CHANGED
@@ -20,7 +20,10 @@ declare function anySignal(signals: readonly AbortSignal[]): AbortSignal;
20
20
  /**
21
21
  * Remove URLs from text that may travel to a client (a refusal reason, a build error):
22
22
  * transport errors carry the endpoint — and with a keyed RPC (QuickNode, Alchemy,
23
- * Infura…) the key sits in its path (scan 2026-10-23 F8).
23
+ * Infura…) the key sits in its path (scan 2026-10-23 F8). What `/[a-z][a-z0-9+.-]{0,31}:\/\/[^\s"'<>]+/gi`
24
+ * would replace, found from each `://` instead of tried at every position: linear in the length of
25
+ * `text` whatever an API or a client puts in it ("a.a.a…" made the regex this replaced quadratic;
26
+ * scan 2026-10-27 modules, F34). A URL glued to a word loses its last 32 scheme characters and the rest.
24
27
  */
25
28
  declare function redactUrls(text: string): string;
26
29
  /**
@@ -30,5 +33,13 @@ declare function redactUrls(text: string): string;
30
33
  * lookup; the others keep refusing what the catalog does not list.
31
34
  */
32
35
  declare function sourceAssetOf(params: Pick<QuoteParams, "sourceChain" | "sourceToken" | "sourceDecimals">): CcAsset | undefined;
36
+ /** Standard or URL-safe base64 → bytes, without Buffer or atob (Hermes); null for anything else. */
37
+ declare function base64Bytes(text: string): Uint8Array | null;
38
+ /**
39
+ * A TON address in raw form (`<workchain>:<64 hex>`, lowercase) from a raw string, a user-friendly one (bounceable or
40
+ * not, checksum verified), or an address object (`toRawString()` / `workChain` + `hash`); null for anything else. Two
41
+ * spellings of one address compare equal in this form.
42
+ */
43
+ declare function tonRawAddress(address: unknown): string | null;
33
44
 
34
- export { anySignal, evmRef, isEvm, namespace, redactUrls, sourceAssetOf, timeoutSignal };
45
+ export { anySignal, base64Bytes, evmRef, isEvm, namespace, redactUrls, sourceAssetOf, timeoutSignal, tonRawAddress };
package/dist/util.js CHANGED
@@ -1,19 +1,23 @@
1
1
  import {
2
2
  anySignal,
3
+ base64Bytes,
3
4
  evmRef,
4
5
  isEvm,
5
6
  namespace,
6
7
  redactUrls,
7
8
  sourceAssetOf,
8
- timeoutSignal
9
- } from "./chunk-MNJTMSFN.js";
9
+ timeoutSignal,
10
+ tonRawAddress
11
+ } from "./chunk-2KC734UX.js";
10
12
  export {
11
13
  anySignal,
14
+ base64Bytes,
12
15
  evmRef,
13
16
  isEvm,
14
17
  namespace,
15
18
  redactUrls,
16
19
  sourceAssetOf,
17
- timeoutSignal
20
+ timeoutSignal,
21
+ tonRawAddress
18
22
  };
19
23
  //# sourceMappingURL=util.js.map
package/dist/validate.cjs CHANGED
@@ -20,6 +20,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  // src/validate.ts
21
21
  var validate_exports = {};
22
22
  __export(validate_exports, {
23
+ remotePinRefusal: () => remotePinRefusal,
23
24
  validQuote: () => validQuote
24
25
  });
25
26
  module.exports = __toCommonJS(validate_exports);
@@ -60,4 +61,22 @@ function validQuote(q, params, provider) {
60
61
  return false;
61
62
  }
62
63
  }
64
+ function remotePinRefusal(provider, q, chain) {
65
+ if (provider.remote !== true) return null;
66
+ const tx = q.txRequest;
67
+ const plain = tx ? tx.data === void 0 && tx.serializedTx === void 0 : typeof q.depositAddress === "string" && q.depositAddress !== "";
68
+ if (plain && provider.depositAddressRoutes !== true) {
69
+ 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)`;
70
+ }
71
+ if (!chain.startsWith("eip155:")) return null;
72
+ if (!tx || tx.serializedTx !== void 0) return null;
73
+ const pinned = (list, address) => (list ?? []).some((a) => typeof a === "string" && a.toLowerCase() === address.toLowerCase());
74
+ if (tx.approvalAddress && !pinned(provider.spenders?.(chain), tx.approvalAddress)) {
75
+ return `${provider.name}: the route approves ${tx.approvalAddress}, which the app did not pin as a spender on ${chain} (remoteProvider spenders)`;
76
+ }
77
+ if (tx.data !== void 0 && tx.to && !pinned(provider.callTargets?.(chain), tx.to)) {
78
+ return `${provider.name}: the route calls ${tx.to}, which the app did not pin as a call target on ${chain} (remoteProvider callTargets)`;
79
+ }
80
+ return null;
81
+ }
63
82
  //# sourceMappingURL=validate.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/validate.ts"],"sourcesContent":["import type {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"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAGO,SAAS,WAAW,GAAU,QAAqB,UAA2B;AACnF,MAAI;AACF,QAAI,EAAE,aAAa,YAAY,EAAE,iBAAiB,OAAO,SAAU,QAAO;AAC1E,QAAI,OAAO,EAAE,aAAa,YAAY,CAAC,EAAE,YAAY,OAAO,EAAE,mBAAmB,YAAa,EAAE,gBAAgB,QAAQ,OAAO,EAAE,gBAAgB,SAAW,QAAO;AACnK,QAAI,CAAC,CAAC,EAAE,cAAc,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,CAAC,UAAU,WAAW,KAAK,KAAK,CAAC,EAAG,QAAO;AACtG,UAAM,SAAS,OAAO,EAAE,YAAY;AACpC,UAAM,OAAO,OAAO,EAAE,UAAU;AAChC,UAAM,MAAM,OAAO,EAAE,aAAa;AAClC,QAAI,UAAU,MAAM,QAAQ,MAAM,OAAO,MAAM,MAAM,KAAM,QAAO;AAClE,QAAI,OAAO,iBAAiB,QAAW;AACrC,UAAI,CAAC,WAAW,KAAK,OAAO,YAAY,KAAK,OAAO,OAAO,YAAY,KAAK,MAAM,MAAM,OAAO,OAAO,YAAY,EAAG,QAAO;AAAA,IAC9H;AACA,QAAI,OAAO,gBAAgB,QAAW;AACpC,UAAI,CAAC,OAAO,UAAU,OAAO,WAAW,KAAK,OAAO,cAAc,KAAK,OAAO,eAAe,IAAQ,QAAO;AAG5G,YAAM,WAAY,OAAO,OAAO,MAAS,OAAO,WAAW,IAAK;AAChE,UAAI,MAAM,SAAU,QAAO;AAAA,IAC7B;AACA,QAAI,CAAC,OAAO,SAAS,EAAE,UAAU,KAAK,EAAE,aAAa,EAAG,QAAO;AAC/D,QAAI,CAAC,OAAO,SAAS,EAAE,WAAW,KAAK,EAAE,cAAc,KAAK,EAAE,cAAc,EAAG,QAAO;AACtF,QAAI,CAAC,OAAO,SAAS,EAAE,MAAM,KAAK,EAAE,SAAS,EAAG,QAAO;AACvD,QAAI,EAAE,SAAS,CAAC,MAAM,QAAQ,EAAE,IAAI,KAAK,CAAC,EAAE,KAAK;AAAA,MAAM,CAAC,QACtD,CAAC,WAAW,YAAY,aAAa,OAAO,EAAE,SAAS,IAAI,IAAI,KAC/D,WAAW,KAAK,IAAI,MAAM,KAC1B,OAAO,IAAI,UAAU,YAAY,IAAI,MAAM,SAAS,KACpD,OAAO,IAAI,aAAa,cACvB,IAAI,gBAAgB,UAAa,OAAO,IAAI,gBAAgB;AAAA,IAC/D,GAAI,QAAO;AACX,QAAI,EAAE,iBAAiB,MAAM,QAAQ,EAAE,YAAY,KAAK,OAAO,EAAE,iBAAiB,YAAY,CAAC,OAAO,OAAO,EAAE,YAAY,EAAE,MAAM,CAAC,UAAU,OAAO,UAAU,QAAQ,GAAI,QAAO;AAClL,UAAM,KAAK,EAAE;AACb,QAAI,IAAI;AACN,UAAI,GAAG,UAAU,UAAa,CAAC,WAAW,KAAK,GAAG,KAAK,EAAG,QAAO;AACjE,UAAI,GAAG,aAAa,UAAa,CAAC,WAAW,KAAK,GAAG,QAAQ,EAAG,QAAO;AACvE,UAAI,GAAG,YAAY,WAAc,CAAC,OAAO,cAAc,GAAG,OAAO,KAAK,GAAG,WAAW,GAAI,QAAO;AAC/F,UAAI,CAAC,GAAG,IAAI,GAAG,MAAM,GAAG,iBAAiB,GAAG,YAAY,EAAE,KAAK,CAAC,UAAU,UAAU,UAAa,OAAO,UAAU,QAAQ,EAAG,QAAO;AAAA,IACtI;AAGA,QAAI,CAAC,OAAO,SAAS,EAAE,YAAY,KAAK,EAAE,gBAAgB,KAAK,IAAI,EAAG,QAAO;AAC7E,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;","names":[]}
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). 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\">, 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 return null;\n}\n"],"mappings":"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","names":[]}
@@ -1,7 +1,17 @@
1
- import { Quote, QuoteParams } from './types.cjs';
1
+ import { CrossChainProvider, Quote, QuoteParams } from './types.cjs';
2
2
  import '@anywalletkit/core';
3
3
 
4
4
  /** Shared quote validator used by both the aggregate and single-provider paths. */
5
5
  declare function validQuote(q: Quote, params: QuoteParams, provider: string): boolean;
6
+ /**
7
+ * Why a remote provider's quote cannot execute in this app, or null: its EVM transaction calls or
8
+ * approves an address the app did not pin (`remoteProvider` `callTargets` / `spenders`), or it pays
9
+ * an address the backend named — a deposit address or a plain value send — on a stub the app did not
10
+ * open to such routes (`depositAddressRoutes`, scan 2026-10-28 F9). The router and the
11
+ * single-provider path drop such a quote with this reason, so the auction never picks a route
12
+ * execution would refuse (scan 2026-10-27 modules, F8/F9/F16); execution checks the build again, in
13
+ * full. Local providers are not judged here.
14
+ */
15
+ declare function remotePinRefusal(provider: Pick<CrossChainProvider, "name" | "remote" | "callTargets" | "spenders" | "depositAddressRoutes">, q: Quote, chain: string): string | null;
6
16
 
7
- export { validQuote };
17
+ export { remotePinRefusal, validQuote };
@@ -1,7 +1,17 @@
1
- import { Quote, QuoteParams } from './types.js';
1
+ import { CrossChainProvider, Quote, QuoteParams } from './types.js';
2
2
  import '@anywalletkit/core';
3
3
 
4
4
  /** Shared quote validator used by both the aggregate and single-provider paths. */
5
5
  declare function validQuote(q: Quote, params: QuoteParams, provider: string): boolean;
6
+ /**
7
+ * Why a remote provider's quote cannot execute in this app, or null: its EVM transaction calls or
8
+ * approves an address the app did not pin (`remoteProvider` `callTargets` / `spenders`), or it pays
9
+ * an address the backend named — a deposit address or a plain value send — on a stub the app did not
10
+ * open to such routes (`depositAddressRoutes`, scan 2026-10-28 F9). The router and the
11
+ * single-provider path drop such a quote with this reason, so the auction never picks a route
12
+ * execution would refuse (scan 2026-10-27 modules, F8/F9/F16); execution checks the build again, in
13
+ * full. Local providers are not judged here.
14
+ */
15
+ declare function remotePinRefusal(provider: Pick<CrossChainProvider, "name" | "remote" | "callTargets" | "spenders" | "depositAddressRoutes">, q: Quote, chain: string): string | null;
6
16
 
7
- export { validQuote };
17
+ export { remotePinRefusal, validQuote };
package/dist/validate.js CHANGED
@@ -1,7 +1,9 @@
1
1
  import {
2
+ remotePinRefusal,
2
3
  validQuote
3
- } from "./chunk-KIAQXIBG.js";
4
+ } from "./chunk-5FPDKWBG.js";
4
5
  export {
6
+ remotePinRefusal,
5
7
  validQuote
6
8
  };
7
9
  //# sourceMappingURL=validate.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@anywalletkit/protocol-swap2-core",
3
- "version": "0.6.1",
3
+ "version": "0.8.0",
4
4
  "description": "The swap2 engine for Any Wallet Kit: one interface for swaps and bridges, quote binding, approvals, review gate, fee caps and status resume; providers plug in as separate packages.",
5
5
  "license": "Apache-2.0",
6
6
  "homepage": "https://docs.anywallet.tech",
@@ -84,17 +84,17 @@
84
84
  ],
85
85
  "dependencies": {
86
86
  "viem": "^2.45.0",
87
- "@anywalletkit/tx-preview-evm": "0.2.0",
87
+ "@anywalletkit/tx-preview-evm": "0.4.0",
88
88
  "@anywalletkit/swap2-assets": "0.2.2"
89
89
  },
90
90
  "peerDependencies": {
91
- "@anywalletkit/core": "0.6.1"
91
+ "@anywalletkit/core": "0.7.0"
92
92
  },
93
93
  "devDependencies": {
94
94
  "@types/node": "^22",
95
95
  "vitest": "^5.0.3",
96
- "@anywalletkit/core": "0.6.1",
97
- "@anywalletkit/conformance": "0.4.4"
96
+ "@anywalletkit/core": "0.7.0",
97
+ "@anywalletkit/conformance": "0.4.6"
98
98
  },
99
99
  "keywords": [
100
100
  "anywalletkit"