@anywalletkit/protocol-swap2-core 0.6.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/chunk-2KC734UX.js +117 -0
- package/dist/chunk-2KC734UX.js.map +1 -0
- package/dist/{chunk-IGTMUTNJ.js → chunk-5E5FWYZQ.js} +123 -6
- package/dist/chunk-5E5FWYZQ.js.map +1 -0
- package/dist/{chunk-ZMWK3RQ4.js → chunk-CT2REW2V.js} +39 -5
- package/dist/chunk-CT2REW2V.js.map +1 -0
- package/dist/{chunk-JFGMF6FO.js → chunk-U653ZREM.js} +9 -3
- package/dist/chunk-U653ZREM.js.map +1 -0
- package/dist/{chunk-KIAQXIBG.js → chunk-WBYCNN6S.js} +16 -2
- package/dist/chunk-WBYCNN6S.js.map +1 -0
- package/dist/index.cjs +243 -9
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +4 -4
- package/dist/index.d.ts +4 -4
- package/dist/index.js +10 -6
- package/dist/remote.cjs +37 -3
- package/dist/remote.cjs.map +1 -1
- package/dist/remote.d.cts +26 -1
- package/dist/remote.d.ts +26 -1
- package/dist/remote.js +2 -2
- package/dist/server.cjs +23 -4
- package/dist/server.cjs.map +1 -1
- package/dist/server.js +6 -5
- package/dist/server.js.map +1 -1
- package/dist/swap2/base.cjs +180 -15
- package/dist/swap2/base.cjs.map +1 -1
- package/dist/swap2/base.d.cts +19 -1
- package/dist/swap2/base.d.ts +19 -1
- package/dist/swap2/base.js +2 -1
- package/dist/swap2/single.cjs +198 -15
- package/dist/swap2/single.cjs.map +1 -1
- package/dist/swap2/single.js +4 -3
- package/dist/types.cjs.map +1 -1
- package/dist/types.d.cts +32 -1
- package/dist/types.d.ts +32 -1
- package/dist/util.cjs +70 -2
- package/dist/util.cjs.map +1 -1
- package/dist/util.d.cts +13 -2
- package/dist/util.d.ts +13 -2
- package/dist/util.js +7 -3
- package/dist/validate.cjs +14 -0
- package/dist/validate.cjs.map +1 -1
- package/dist/validate.d.cts +10 -2
- package/dist/validate.d.ts +10 -2
- package/dist/validate.js +3 -1
- package/package.json +9 -7
- package/dist/chunk-IGTMUTNJ.js.map +0 -1
- package/dist/chunk-JFGMF6FO.js.map +0 -1
- package/dist/chunk-KIAQXIBG.js.map +0 -1
- package/dist/chunk-MNJTMSFN.js +0 -49
- package/dist/chunk-MNJTMSFN.js.map +0 -1
- package/dist/chunk-ZMWK3RQ4.js.map +0 -1
package/dist/server.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/server.ts","../src/util.ts","../src/validate.ts"],"sourcesContent":["import type {Asset, CrossChainProvider, IntentRef, Quote, QuoteParams, TxRequest, UnsignedTx} from \"./types.js\";\nimport {redactUrls} from \"./util.js\";\nimport {validQuote} from \"./validate.js\";\n\n/**\n * The server side of {@link remoteProvider}: runs real providers behind your backend so API\n * keys, affiliate settings and non-CORS endpoints never ship in the app.\n *\n * Framework-free: wire `handle(action, body, context)` into any HTTP route that receives\n * `POST {baseUrl}/{action}` with a JSON body, and send back `{status, body}` as JSON.\n *\n * What it guarantees:\n * - every field of every request is validated (types, lengths, digit-only amounts) before a\n * provider sees it; unknown actions, providers and malformed bodies are refused;\n * - `quote` answers are sealed with HMAC-SHA256 under `sealSecret`; `build` and `preflight`\n * refuse a quote that was not sealed here, was changed in transit, or has expired — so a\n * client cannot make your server build a deposit for terms the provider never quoted;\n * - every quote's `intentId` leaves with an intent token appended (`<id>~<tag>`): `track`,\n * `claim`, `notify` and `register` refuse an id without a valid one (403), and the provider\n * only ever sees its own id. With `subject`, quotes and tokens are bound to the caller, so\n * another user's sealed quote or swap id is refused too; without it the token is a bearer\n * capability for that one swap (share a transaction hash with support, not the swap id);\n * - a provider's named refusal travels in-band (`{noQuote}`) so the app can show WHY;\n * - error text crossing the boundary has URLs redacted (no upstream endpoints or keys).\n *\n * What it does NOT do, and you must: authenticate the caller and rate-limit per user/IP in\n * `guard` (see {@link Swap2HandlerOptions.guard}), cap the raw request size before parsing\n * (`serializedTx` may legitimately be ~2 MB), and log without request bodies.\n *\n * Server-only (WebCrypto HMAC; Node ≥ 20, Deno, Bun, edge runtimes). Never import it into an app.\n */\n\n/** One call, as `guard` sees it. `dry` quotes are display quotes: give them their own, larger bucket. */\nexport interface Swap2Call {\n action: Swap2Action;\n provider: string;\n dry: boolean;\n /**\n * The validated request as the provider will get it — amounts, recipients, the provider's own\n * intent id (token removed) — for limits that depend on what is asked. Not yet checked against\n * the seal or the intent token: those run after the guard.\n */\n request: Swap2Request;\n}\n\n/** The validated body of each action. */\nexport type Swap2Request =\n | {params: QuoteParams}\n | {quote: Quote; tx?: UnsignedTx}\n | {ref: IntentRef}\n | {intentId: string; txHash: string}\n | {intentId: string; signature: string}\n | {from: Asset; to: Asset};\n\nexport type Swap2Action = \"quote\" | \"build\" | \"preflight\" | \"track\" | \"claim\" | \"notify\" | \"register\" | \"supports\";\n\nexport interface Swap2HandlerOptions<C> {\n /** The real providers, by `name` (the same names the app's `remoteProvider`s use). */\n providers: readonly CrossChainProvider[];\n /** HMAC key for quote seals: at least 32 bytes of randomness, kept server-side, rotated with care. */\n sealSecret: string | Uint8Array;\n /**\n * Authentication and rate limiting — required, because this endpoint spends your provider\n * quotas and builds deposits. Throw {@link Swap2HandlerError} (401 / 403 / 429) to refuse.\n * `context` is whatever your route passes as the third argument (the request, a session…).\n */\n guard: (call: Swap2Call, context: C) => void | Promise<void>;\n /**\n * Who is calling: a stable id of the authenticated user — the Sign-In with Ethereum address,\n * not an email — or undefined. Quotes are sealed for, and intent tokens issued to, this subject:\n * another caller's quote or swap id is refused (403). Without it, the intent token in a swap\n * id is a bearer capability. Runs after `guard`.\n */\n subject?: (context: C) => string | undefined | Promise<string | undefined>;\n /**\n * Moving to this handler from another backend: until this time (ms since epoch), `track`\n * accepts a swap id without an intent token, so swaps started before the move can still be\n * followed. Read-only: `claim`, `notify` and `register` always require the token.\n */\n acceptUntokenedTrackUntil?: number;\n /**\n * Quote tokens the swap catalog does not list (`params.sourceToken` an EVM contract or Solana\n * mint, with `params.sourceDecimals`), for apps whose users add their own tokens. Off by default:\n * `sourceDecimals` is then dropped and the providers refuse an unlisted source. Destination tokens\n * are addresses either way.\n */\n allowUnlistedTokens?: boolean;\n /** Clock for expiry checks (tests). */\n now?: () => number;\n}\n\nexport interface Swap2Response {\n status: number;\n body: unknown;\n}\n\n/** Throw from `guard` (or anywhere) to answer with this status and message. */\nexport class Swap2HandlerError extends Error {\n readonly status: number;\n constructor(status: number, message: string) {\n super(message);\n this.name = \"Swap2HandlerError\";\n this.status = status;\n }\n}\n\nconst ACTIONS: ReadonlySet<string> = new Set([\"quote\", \"build\", \"preflight\", \"track\", \"claim\", \"notify\", \"register\", \"supports\"]);\nconst SEAL_FIELD = \"__remoteSeal\";\nconst SEAL_PREFIX = \"swap2-remote-quote:v1:\";\nconst INTENT_PREFIX = \"swap2-remote-intent:v1\";\n/** `<provider's intent id>~<128-bit HMAC tag>`. */\nconst TOKENED = /^([\\s\\S]+)~([0-9a-f]{32})$/;\nconst INTENT_MAX = 2000;\nconst WIRE_INTENT_MAX = INTENT_MAX + 33;\n\nexport function createSwap2Handler<C = unknown>(opts: Swap2HandlerOptions<C>): (action: string, body: unknown, context: C) => Promise<Swap2Response> {\n if (typeof opts.guard !== \"function\") throw new Error(\"swap2-server: guard is required (authentication + rate limiting)\");\n const secret = typeof opts.sealSecret === \"string\" ? new TextEncoder().encode(opts.sealSecret) : opts.sealSecret;\n if (!(secret instanceof Uint8Array) || secret.length < 32) throw new Error(\"swap2-server: sealSecret must be at least 32 bytes\");\n const byName = new Map<string, CrossChainProvider>();\n for (const p of opts.providers) {\n if (byName.has(p.name)) throw new Error(`swap2-server: duplicate provider '${p.name}'`);\n byName.set(p.name, p);\n }\n const now = opts.now ?? Date.now;\n const keyPromise = crypto.subtle.importKey(\"raw\", secret as BufferSource, {name: \"HMAC\", hash: \"SHA-256\"}, false, [\"sign\", \"verify\"]);\n\n // The subject is JSON-quoted, so it cannot run into the canonical quote that follows (\"{\").\n const sealInput = (provider: string, subject: string | undefined, quote: Quote) =>\n new TextEncoder().encode(SEAL_PREFIX + provider + \":\" + (subject === undefined ? \"\" : JSON.stringify(subject) + \":\") + canonical(withoutSeal(quote)));\n async function seal(provider: string, subject: string | undefined, quote: Quote): Promise<Quote> {\n const tag = new Uint8Array(await crypto.subtle.sign(\"HMAC\", await keyPromise, sealInput(provider, subject, quote)));\n return {...quote, providerData: {...(quote.providerData ?? {}), [SEAL_FIELD]: toHex(tag)}};\n }\n async function verify(provider: string, subject: string | undefined, quote: Quote): Promise<boolean> {\n if (quote.provider !== provider) return false;\n const sealHex = quote.providerData?.[SEAL_FIELD];\n if (!sealHex || !/^[0-9a-f]{64}$/.test(sealHex)) return false;\n return crypto.subtle.verify(\"HMAC\", await keyPromise, fromHex(sealHex) as BufferSource, sealInput(provider, subject, quote)); // constant-time\n }\n /** The intent token: HMAC over (provider, subject, the provider's intent id), domain-separated from the seal. */\n async function intentTag(provider: string, subject: string | undefined, intentId: string): Promise<string> {\n const data = new TextEncoder().encode(JSON.stringify([INTENT_PREFIX, provider, subject ?? null, intentId]));\n return toHex(new Uint8Array(await crypto.subtle.sign(\"HMAC\", await keyPromise, data)).subarray(0, 16));\n }\n /** A swap id from the app → the provider's own id, or 403. */\n async function untoken(provider: string, subject: string | undefined, wireId: string, action: Swap2Action): Promise<string> {\n const m = TOKENED.exec(wireId);\n if (!m) {\n if (action === \"track\" && opts.acceptUntokenedTrackUntil !== undefined && now() < opts.acceptUntokenedTrackUntil) return wireId;\n throw new Swap2HandlerError(403, \"swap id carries no intent token from this server\");\n }\n if (!sameHex(await intentTag(provider, subject, m[1]!), m[2]!)) {\n throw new Swap2HandlerError(403, subject === undefined ? \"swap id carries an invalid intent token\" : \"swap id was not issued to this caller\");\n }\n return m[1]!;\n }\n /** The quote as the provider issued it: no seal, its own intent id. */\n const forProvider = (quote: Quote): Quote => ({...stripSeal(quote), intentId: TOKENED.exec(quote.intentId)?.[1] ?? quote.intentId});\n\n return async function handle(action: string, body: unknown, context: C): Promise<Swap2Response> {\n try {\n if (!ACTIONS.has(action)) return fail(404, `unknown action '${String(action).slice(0, 40)}'`);\n const b = record(body, \"body\");\n const providerName = str(b.provider, \"provider\", 40);\n const act = action as Swap2Action;\n const request = validated(act, b, opts.allowUnlistedTokens === true);\n const dry = \"params\" in request && request.params.dry === true;\n // The guard sees the provider's own intent id, as the provider will.\n const shown: Swap2Request = \"ref\" in request ? {ref: {...request.ref, intentId: TOKENED.exec(request.ref.intentId)?.[1] ?? request.ref.intentId}}\n : \"quote\" in request ? {...request, quote: forProvider(request.quote)}\n : \"intentId\" in request ? {...request, intentId: TOKENED.exec(request.intentId)?.[1] ?? request.intentId}\n : request;\n await opts.guard({action: act, provider: providerName, dry, request: shown}, context);\n const subject = opts.subject ? await opts.subject(context) : undefined;\n\n const provider = byName.get(providerName);\n if (!provider) return fail(404, `unknown provider '${providerName}'`);\n if (!provider.enabled()) return fail(403, `provider '${providerName}' is disabled`);\n\n switch (act) {\n case \"quote\": {\n const {params} = request as {params: QuoteParams};\n let refusal: string | null = null; // per request: a shared field races under parallel quotes\n const quote = await provider.getQuote({...params, onRefusal: (reason) => { refusal = reason; }});\n if (quote) {\n let normalized: Quote;\n try { normalized = normalizeQuote(quote, INTENT_MAX); } catch { return fail(502, `${providerName} returned a quote this server refuses to seal`); }\n // A seal vouches for the quote: hold it to the engine's own checks (provider, input, floor,\n // slippage, expiry) so a client that does not re-validate cannot be handed a bad one.\n if (!validQuote(normalized, params, providerName) || normalized.validUntilMs <= now()) {\n return fail(502, `${providerName} returned a quote the swap engine would reject`);\n }\n const tokened = {...normalized, intentId: `${normalized.intentId}~${await intentTag(providerName, subject, normalized.intentId)}`};\n return ok(await seal(providerName, subject, tokened));\n }\n if (refusal) return ok({noQuote: redactUrls(String(refusal)).slice(0, 300)});\n return ok(params.dry === false ? {noQuote: `${providerName} refused without naming a reason`} : null);\n }\n case \"build\":\n case \"preflight\": {\n const {quote, tx} = request as {quote: Quote; tx?: UnsignedTx};\n if (!(await verify(providerName, subject, quote))) {\n return subject === undefined ? fail(400, \"quote integrity check failed\") : fail(403, \"quote was not sealed for this caller\");\n }\n if (quote.validUntilMs < now()) return fail(410, \"quote expired: ask for a new one\");\n if (act === \"build\") return ok(await refused(() => provider.buildDeposit(forProvider(quote))));\n if (!provider.preflightDeposit) return ok({});\n await refused(() => provider.preflightDeposit!(forProvider(quote), tx!));\n return ok({});\n }\n case \"track\":\n case \"claim\": {\n const {ref} = request as {ref: IntentRef};\n const own = {...ref, intentId: await untoken(providerName, subject, ref.intentId, act)};\n if (act === \"track\") return ok(await provider.trackIntent(own));\n if (!provider.buildClaim) return ok({});\n return ok(await refused(() => provider.buildClaim!(own)));\n }\n case \"notify\": {\n const {intentId, txHash} = request as {intentId: string; txHash: string};\n await provider.notifyDeposit?.(await untoken(providerName, subject, intentId, act), txHash);\n return ok({});\n }\n case \"register\": {\n if (!provider.registerSignedOrder) return fail(400, `provider '${providerName}' has no signed-order flow`);\n const {intentId, signature} = request as {intentId: string; signature: string};\n await provider.registerSignedOrder(await untoken(providerName, subject, intentId, act), signature);\n return ok({});\n }\n case \"supports\": {\n const {from, to} = request as {from: Asset; to: Asset};\n return ok({supported: provider.supportsRoute(from, to)});\n }\n }\n } catch (err) {\n if (err instanceof Swap2HandlerError) return fail(err.status, err.message);\n return fail(500, \"internal error\"); // never echo provider internals or upstream URLs\n }\n };\n}\n\n/** Each action's body, validated field by field before anyone (guard, provider) sees it. */\nfunction validated(action: Swap2Action, b: Record<string, unknown>, allowUnlisted: boolean): Swap2Request {\n switch (action) {\n case \"quote\": return {params: quoteParams(b.params, allowUnlisted)};\n case \"build\": return {quote: normalizeQuote(b.quote, WIRE_INTENT_MAX)};\n case \"preflight\": return {quote: normalizeQuote(b.quote, WIRE_INTENT_MAX), tx: unsignedTx(b.tx)};\n case \"track\":\n case \"claim\": return {ref: intentRef(b.ref)};\n case \"notify\": return {intentId: str(b.intentId, \"intentId\", WIRE_INTENT_MAX), txHash: str(b.txHash, \"txHash\", 200)};\n case \"register\": {\n const signature = str(b.signature, \"signature\", 302);\n if (!/^0x[0-9a-fA-F]{2,300}$/.test(signature)) throw new Swap2HandlerError(400, \"signature: not a hex signature\");\n return {intentId: str(b.intentId, \"intentId\", WIRE_INTENT_MAX), signature};\n }\n case \"supports\": return {from: asset(b.from, \"from\"), to: asset(b.to, \"to\")};\n }\n}\n\n/** Constant-time comparison of two lowercase hex strings. */\nfunction sameHex(a: string, b: string): boolean {\n if (a.length !== b.length) return false;\n let diff = 0;\n for (let i = 0; i < a.length; i++) diff |= a.charCodeAt(i) ^ b.charCodeAt(i);\n return diff === 0;\n}\n\n// ---- refusals -------------------------------------------------------------------------------\n\n/** A provider's own refusal (\"lifi: …\") is actionable: 422 with the redacted text. Anything else is a 500. */\nasync function refused<T>(fn: () => Promise<T>): Promise<T> {\n try {\n return await fn();\n } catch (err) {\n if (err instanceof Error && /^[a-z0-9_-]+: \\S/i.test(err.message)) {\n throw new Swap2HandlerError(422, redactUrls(err.message).slice(0, 300));\n }\n throw err;\n }\n}\n\nconst ok = (body: unknown): Swap2Response => ({status: 200, body: body === undefined ? null : body});\nconst fail = (status: number, error: string): Swap2Response => ({status, body: {error: redactUrls(error).slice(0, 300)}});\n\n// ---- validation (the limits mirror a production backend's schema) --------------------------\n\nconst DIGITS = (max: number) => new RegExp(`^\\\\d{1,${max}}$`);\n\nfunction bad(field: string, why: string): never {\n throw new Swap2HandlerError(400, `${field}: ${why}`);\n}\nfunction record(v: unknown, field: string): Record<string, unknown> {\n if (!v || typeof v !== \"object\" || Array.isArray(v)) bad(field, \"expected an object\");\n return v as Record<string, unknown>;\n}\nfunction str(v: unknown, field: string, max: number): string {\n if (typeof v !== \"string\") bad(field, \"expected a string\");\n if (v.length > max) bad(field, `longer than ${max}`);\n return v;\n}\nfunction optStr(v: unknown, field: string, max: number): string | undefined {\n return v === undefined ? undefined : str(v, field, max);\n}\nfunction digits(v: unknown, field: string, max: number): string {\n const s = str(v, field, max);\n if (!DIGITS(max).test(s)) bad(field, \"expected base units (digits only)\");\n return s;\n}\nfunction num(v: unknown, field: string, min = 0, max = Number.MAX_SAFE_INTEGER): number {\n if (typeof v !== \"number\" || !Number.isFinite(v) || v < min || v > max) bad(field, \"expected a finite number in range\");\n return v;\n}\nfunction bool(v: unknown, field: string): boolean | undefined {\n if (v === undefined) return undefined;\n if (typeof v !== \"boolean\") bad(field, \"expected a boolean\");\n return v;\n}\n\nfunction quoteParams(v: unknown, allowUnlisted = false): QuoteParams {\n const p = record(v, \"params\");\n const sourceDecimals = !allowUnlisted || p.sourceDecimals === undefined ? undefined : num(p.sourceDecimals, \"params.sourceDecimals\", 0, 77);\n if (sourceDecimals !== undefined && !Number.isInteger(sourceDecimals)) bad(\"params.sourceDecimals\", \"expected an integer\");\n const slippage = p.slippageBps === undefined ? undefined : num(p.slippageBps, \"params.slippageBps\", 0, 10_000);\n if (slippage !== undefined && !Number.isInteger(slippage)) bad(\"params.slippageBps\", \"expected an integer\");\n const refundAddress = optStr(p.refundAddress, \"params.refundAddress\", 120);\n const minAmountOut = p.minAmountOut === undefined ? undefined : digits(p.minAmountOut, \"params.minAmountOut\", 60);\n const dry = bool(p.dry, \"params.dry\");\n const flex = bool(p.flex, \"params.flex\");\n return {\n sourceChain: str(p.sourceChain, \"params.sourceChain\", 64),\n sourceToken: str(p.sourceToken, \"params.sourceToken\", 64),\n ...(sourceDecimals !== undefined ? {sourceDecimals} : {}),\n destChain: str(p.destChain, \"params.destChain\", 64),\n destToken: str(p.destToken, \"params.destToken\", 120),\n amountIn: digits(p.amountIn, \"params.amountIn\", 40),\n destAddress: str(p.destAddress, \"params.destAddress\", 120),\n ...(refundAddress !== undefined ? {refundAddress} : {}),\n ...(minAmountOut !== undefined ? {minAmountOut} : {}),\n ...(slippage !== undefined ? {slippageBps: slippage} : {}),\n ...(dry !== undefined ? {dry} : {}),\n ...(flex !== undefined ? {flex} : {}),\n };\n}\n\nconst FEE_TYPES = new Set([\"network\", \"protocol\", \"affiliate\", \"other\"]);\n\n/** Keeps only the known Quote fields, each checked: what is sealed is exactly what is verified. */\nfunction normalizeQuote(v: unknown, intentMax: number): Quote {\n const q = record(v, \"quote\");\n const out: Quote = {\n provider: str(q.provider, \"quote.provider\", 40),\n intentId: str(q.intentId, \"quote.intentId\", intentMax),\n depositAddress: str(q.depositAddress, \"quote.depositAddress\", 200),\n depositMemo: q.depositMemo === null ? null : str(q.depositMemo, \"quote.depositMemo\", 400),\n sourceAmount: digits(q.sourceAmount, \"quote.sourceAmount\", 60),\n destAmount: digits(q.destAmount, \"quote.destAmount\", 60),\n minDestAmount: digits(q.minDestAmount, \"quote.minDestAmount\", 60),\n feeUsd: num(q.feeUsd, \"quote.feeUsd\"),\n etaSeconds: num(q.etaSeconds, \"quote.etaSeconds\"),\n reliability: num(q.reliability, \"quote.reliability\", 0, 1),\n validUntilMs: num(q.validUntilMs, \"quote.validUntilMs\"),\n };\n if (q.txRequest !== undefined) out.txRequest = txRequest(q.txRequest);\n if (q.fees !== undefined) {\n if (!Array.isArray(q.fees) || q.fees.length > 32) bad(\"quote.fees\", \"expected up to 32 entries\");\n out.fees = q.fees.map((f, i) => {\n const fee = record(f, `quote.fees[${i}]`);\n const type = str(fee.type, `quote.fees[${i}].type`, 20);\n if (!FEE_TYPES.has(type)) bad(`quote.fees[${i}].type`, \"unknown fee type\");\n const description = optStr(fee.description, `quote.fees[${i}].description`, 500);\n const included = bool(fee.included, `quote.fees[${i}].included`);\n if (included === undefined) bad(`quote.fees[${i}].included`, \"expected a boolean\");\n return {\n type: type as \"network\" | \"protocol\" | \"affiliate\" | \"other\",\n amount: digits(fee.amount, `quote.fees[${i}].amount`, 80),\n token: str(fee.token, `quote.fees[${i}].token`, 200),\n included,\n ...(description !== undefined ? {description} : {}),\n };\n });\n }\n if (q.providerData !== undefined) {\n const data = record(q.providerData, \"quote.providerData\");\n const keys = Object.keys(data);\n if (keys.length > 64) bad(\"quote.providerData\", \"more than 64 entries\");\n out.providerData = Object.fromEntries(keys.map((k) => [str(k, \"quote.providerData key\", 200), str(data[k], `quote.providerData.${k.slice(0, 40)}`, 10_000)]));\n }\n return out;\n}\n\nfunction txRequest(v: unknown): TxRequest {\n const t = record(v, \"quote.txRequest\");\n const out: TxRequest = {};\n if (t.to !== undefined) out.to = str(t.to, \"quote.txRequest.to\", 200);\n if (t.data !== undefined) out.data = str(t.data, \"quote.txRequest.data\", 1_000_000);\n if (t.value !== undefined) out.value = digits(t.value, \"quote.txRequest.value\", 80);\n if (t.chainId !== undefined) {\n const id = num(t.chainId, \"quote.txRequest.chainId\", 1);\n if (!Number.isInteger(id)) bad(\"quote.txRequest.chainId\", \"expected an integer\");\n out.chainId = id;\n }\n if (t.gasLimit !== undefined) out.gasLimit = digits(t.gasLimit, \"quote.txRequest.gasLimit\", 80);\n if (t.approvalAddress !== undefined) out.approvalAddress = str(t.approvalAddress, \"quote.txRequest.approvalAddress\", 200);\n if (t.serializedTx !== undefined) out.serializedTx = str(t.serializedTx, \"quote.txRequest.serializedTx\", 2_000_000);\n if (t.nativeFee !== undefined) out.nativeFee = digits(t.nativeFee, \"quote.txRequest.nativeFee\", 80);\n return out;\n}\n\n/** The transaction the client built from `build` (bigints arrive as decimal strings). */\nfunction unsignedTx(v: unknown): UnsignedTx {\n const t = record(v, \"tx\");\n const out: UnsignedTx = {chain: str(t.chain, \"tx.chain\", 64), to: str(t.to, \"tx.to\", 200), value: digits(t.value, \"tx.value\", 80)};\n const optional: [keyof UnsignedTx, number][] = [[\"data\", 1_000_000], [\"gasLimit\", 80], [\"approvalAddress\", 200], [\"serializedTx\", 2_000_000], [\"memo\", 2_000], [\"bodyBoc\", 200_000], [\"stateInitBoc\", 200_000], [\"typedData\", 200_000]];\n for (const [k, max] of optional) if (t[k] !== undefined) (out as unknown as Record<string, unknown>)[k] = k === \"gasLimit\" ? digits(t[k], `tx.${k}`, max) : str(t[k], `tx.${k}`, max);\n if (t.memoEncoding !== undefined) {\n if (t.memoEncoding !== \"utf8\" && t.memoEncoding !== \"hex\") bad(\"tx.memoEncoding\", \"expected utf8 or hex\");\n out.memoEncoding = t.memoEncoding;\n }\n if (t.chainId !== undefined) out.chainId = num(t.chainId, \"tx.chainId\", 1);\n return out;\n}\n\nfunction intentRef(v: unknown): IntentRef {\n const r = record(v, \"ref\");\n const nullable = (x: unknown, f: string) => (x === null || x === undefined ? undefined : str(x, f, 200));\n const sourceTxHash = nullable(r.sourceTxHash, \"ref.sourceTxHash\");\n const destTxHash = nullable(r.destTxHash, \"ref.destTxHash\");\n return {\n intentId: str(r.intentId, \"ref.intentId\", WIRE_INTENT_MAX),\n ...(sourceTxHash !== undefined ? {sourceTxHash} : {}),\n ...(destTxHash !== undefined ? {destTxHash} : {}),\n };\n}\n\nfunction asset(v: unknown, field: string): Asset {\n const a = record(v, field);\n return {chain: str(a.chain, `${field}.chain`, 64), token: str(a.token, `${field}.token`, 120)};\n}\n\n// ---- seal ------------------------------------------------------------------------------------\n\nfunction withoutSeal(quote: Quote): Quote {\n const {providerData, ...rest} = quote;\n if (!providerData) return rest;\n const {[SEAL_FIELD]: _seal, ...data} = providerData;\n return Object.keys(data).length ? {...rest, providerData: data} : rest;\n}\n/** The provider gets its quote back exactly as it produced it. */\nconst stripSeal = withoutSeal;\n\n/** Deterministic JSON: sorted keys, `undefined` dropped, non-finite numbers refused. */\nfunction canonical(value: unknown): string {\n if (value === null || typeof value === \"string\" || typeof value === \"boolean\") return JSON.stringify(value);\n if (typeof value === \"number\") {\n if (!Number.isFinite(value)) throw new Swap2HandlerError(400, \"quote contains a non-finite number\");\n return JSON.stringify(value);\n }\n if (Array.isArray(value)) return `[${value.map(canonical).join(\",\")}]`;\n if (typeof value === \"object\") {\n const r = value as Record<string, unknown>;\n return `{${Object.keys(r).filter((k) => r[k] !== undefined).sort().map((k) => `${JSON.stringify(k)}:${canonical(r[k])}`).join(\",\")}}`;\n }\n throw new Swap2HandlerError(400, \"quote is not JSON-safe\");\n}\n\nfunction toHex(bytes: Uint8Array): string {\n return Array.from(bytes, (b) => b.toString(16).padStart(2, \"0\")).join(\"\");\n}\nfunction fromHex(hex: string): Uint8Array {\n const out = new Uint8Array(hex.length / 2);\n for (let i = 0; i < out.length; i++) out[i] = parseInt(hex.slice(i * 2, i * 2 + 2), 16);\n return out;\n}\n","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","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;AAAA;;;ACAA,0BAAyC;AAiDlC,SAAS,WAAW,MAAsB;AAC/C,SAAO,KAAK,QAAQ,wCAAwC,OAAO;AACrE;;;AChDO,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;;;AFkDO,IAAM,oBAAN,cAAgC,MAAM;AAAA,EAClC;AAAA,EACT,YAAY,QAAgB,SAAiB;AAC3C,UAAM,OAAO;AACb,SAAK,OAAO;AACZ,SAAK,SAAS;AAAA,EAChB;AACF;AAEA,IAAM,UAA+B,oBAAI,IAAI,CAAC,SAAS,SAAS,aAAa,SAAS,SAAS,UAAU,YAAY,UAAU,CAAC;AAChI,IAAM,aAAa;AACnB,IAAM,cAAc;AACpB,IAAM,gBAAgB;AAEtB,IAAM,UAAU;AAChB,IAAM,aAAa;AACnB,IAAM,kBAAkB,aAAa;AAE9B,SAAS,mBAAgC,MAAqG;AACnJ,MAAI,OAAO,KAAK,UAAU,WAAY,OAAM,IAAI,MAAM,kEAAkE;AACxH,QAAM,SAAS,OAAO,KAAK,eAAe,WAAW,IAAI,YAAY,EAAE,OAAO,KAAK,UAAU,IAAI,KAAK;AACtG,MAAI,EAAE,kBAAkB,eAAe,OAAO,SAAS,GAAI,OAAM,IAAI,MAAM,oDAAoD;AAC/H,QAAM,SAAS,oBAAI,IAAgC;AACnD,aAAW,KAAK,KAAK,WAAW;AAC9B,QAAI,OAAO,IAAI,EAAE,IAAI,EAAG,OAAM,IAAI,MAAM,qCAAqC,EAAE,IAAI,GAAG;AACtF,WAAO,IAAI,EAAE,MAAM,CAAC;AAAA,EACtB;AACA,QAAM,MAAM,KAAK,OAAO,KAAK;AAC7B,QAAM,aAAa,OAAO,OAAO,UAAU,OAAO,QAAwB,EAAC,MAAM,QAAQ,MAAM,UAAS,GAAG,OAAO,CAAC,QAAQ,QAAQ,CAAC;AAGpI,QAAM,YAAY,CAAC,UAAkB,SAA6B,UAChE,IAAI,YAAY,EAAE,OAAO,cAAc,WAAW,OAAO,YAAY,SAAY,KAAK,KAAK,UAAU,OAAO,IAAI,OAAO,UAAU,YAAY,KAAK,CAAC,CAAC;AACtJ,iBAAe,KAAK,UAAkB,SAA6B,OAA8B;AAC/F,UAAM,MAAM,IAAI,WAAW,MAAM,OAAO,OAAO,KAAK,QAAQ,MAAM,YAAY,UAAU,UAAU,SAAS,KAAK,CAAC,CAAC;AAClH,WAAO,EAAC,GAAG,OAAO,cAAc,EAAC,GAAI,MAAM,gBAAgB,CAAC,GAAI,CAAC,UAAU,GAAG,MAAM,GAAG,EAAC,EAAC;AAAA,EAC3F;AACA,iBAAe,OAAO,UAAkB,SAA6B,OAAgC;AACnG,QAAI,MAAM,aAAa,SAAU,QAAO;AACxC,UAAM,UAAU,MAAM,eAAe,UAAU;AAC/C,QAAI,CAAC,WAAW,CAAC,iBAAiB,KAAK,OAAO,EAAG,QAAO;AACxD,WAAO,OAAO,OAAO,OAAO,QAAQ,MAAM,YAAY,QAAQ,OAAO,GAAmB,UAAU,UAAU,SAAS,KAAK,CAAC;AAAA,EAC7H;AAEA,iBAAe,UAAU,UAAkB,SAA6B,UAAmC;AACzG,UAAM,OAAO,IAAI,YAAY,EAAE,OAAO,KAAK,UAAU,CAAC,eAAe,UAAU,WAAW,MAAM,QAAQ,CAAC,CAAC;AAC1G,WAAO,MAAM,IAAI,WAAW,MAAM,OAAO,OAAO,KAAK,QAAQ,MAAM,YAAY,IAAI,CAAC,EAAE,SAAS,GAAG,EAAE,CAAC;AAAA,EACvG;AAEA,iBAAe,QAAQ,UAAkB,SAA6B,QAAgB,QAAsC;AAC1H,UAAM,IAAI,QAAQ,KAAK,MAAM;AAC7B,QAAI,CAAC,GAAG;AACN,UAAI,WAAW,WAAW,KAAK,8BAA8B,UAAa,IAAI,IAAI,KAAK,0BAA2B,QAAO;AACzH,YAAM,IAAI,kBAAkB,KAAK,kDAAkD;AAAA,IACrF;AACA,QAAI,CAAC,QAAQ,MAAM,UAAU,UAAU,SAAS,EAAE,CAAC,CAAE,GAAG,EAAE,CAAC,CAAE,GAAG;AAC9D,YAAM,IAAI,kBAAkB,KAAK,YAAY,SAAY,4CAA4C,uCAAuC;AAAA,IAC9I;AACA,WAAO,EAAE,CAAC;AAAA,EACZ;AAEA,QAAM,cAAc,CAAC,WAAyB,EAAC,GAAG,UAAU,KAAK,GAAG,UAAU,QAAQ,KAAK,MAAM,QAAQ,IAAI,CAAC,KAAK,MAAM,SAAQ;AAEjI,SAAO,eAAe,OAAO,QAAgB,MAAe,SAAoC;AAC9F,QAAI;AACF,UAAI,CAAC,QAAQ,IAAI,MAAM,EAAG,QAAO,KAAK,KAAK,mBAAmB,OAAO,MAAM,EAAE,MAAM,GAAG,EAAE,CAAC,GAAG;AAC5F,YAAM,IAAI,OAAO,MAAM,MAAM;AAC7B,YAAM,eAAe,IAAI,EAAE,UAAU,YAAY,EAAE;AACnD,YAAM,MAAM;AACZ,YAAM,UAAU,UAAU,KAAK,GAAG,KAAK,wBAAwB,IAAI;AACnE,YAAM,MAAM,YAAY,WAAW,QAAQ,OAAO,QAAQ;AAE1D,YAAM,QAAsB,SAAS,UAAU,EAAC,KAAK,EAAC,GAAG,QAAQ,KAAK,UAAU,QAAQ,KAAK,QAAQ,IAAI,QAAQ,IAAI,CAAC,KAAK,QAAQ,IAAI,SAAQ,EAAC,IAC5I,WAAW,UAAU,EAAC,GAAG,SAAS,OAAO,YAAY,QAAQ,KAAK,EAAC,IACnE,cAAc,UAAU,EAAC,GAAG,SAAS,UAAU,QAAQ,KAAK,QAAQ,QAAQ,IAAI,CAAC,KAAK,QAAQ,SAAQ,IACtG;AACJ,YAAM,KAAK,MAAM,EAAC,QAAQ,KAAK,UAAU,cAAc,KAAK,SAAS,MAAK,GAAG,OAAO;AACpF,YAAM,UAAU,KAAK,UAAU,MAAM,KAAK,QAAQ,OAAO,IAAI;AAE7D,YAAM,WAAW,OAAO,IAAI,YAAY;AACxC,UAAI,CAAC,SAAU,QAAO,KAAK,KAAK,qBAAqB,YAAY,GAAG;AACpE,UAAI,CAAC,SAAS,QAAQ,EAAG,QAAO,KAAK,KAAK,aAAa,YAAY,eAAe;AAElF,cAAQ,KAAK;AAAA,QACX,KAAK,SAAS;AACZ,gBAAM,EAAC,OAAM,IAAI;AACjB,cAAI,UAAyB;AAC7B,gBAAM,QAAQ,MAAM,SAAS,SAAS,EAAC,GAAG,QAAQ,WAAW,CAAC,WAAW;AAAE,sBAAU;AAAA,UAAQ,EAAC,CAAC;AAC/F,cAAI,OAAO;AACT,gBAAI;AACJ,gBAAI;AAAE,2BAAa,eAAe,OAAO,UAAU;AAAA,YAAG,QAAQ;AAAE,qBAAO,KAAK,KAAK,GAAG,YAAY,+CAA+C;AAAA,YAAG;AAGlJ,gBAAI,CAAC,WAAW,YAAY,QAAQ,YAAY,KAAK,WAAW,gBAAgB,IAAI,GAAG;AACrF,qBAAO,KAAK,KAAK,GAAG,YAAY,gDAAgD;AAAA,YAClF;AACA,kBAAM,UAAU,EAAC,GAAG,YAAY,UAAU,GAAG,WAAW,QAAQ,IAAI,MAAM,UAAU,cAAc,SAAS,WAAW,QAAQ,CAAC,GAAE;AACjI,mBAAO,GAAG,MAAM,KAAK,cAAc,SAAS,OAAO,CAAC;AAAA,UACtD;AACA,cAAI,QAAS,QAAO,GAAG,EAAC,SAAS,WAAW,OAAO,OAAO,CAAC,EAAE,MAAM,GAAG,GAAG,EAAC,CAAC;AAC3E,iBAAO,GAAG,OAAO,QAAQ,QAAQ,EAAC,SAAS,GAAG,YAAY,mCAAkC,IAAI,IAAI;AAAA,QACtG;AAAA,QACA,KAAK;AAAA,QACL,KAAK,aAAa;AAChB,gBAAM,EAAC,OAAO,GAAE,IAAI;AACpB,cAAI,CAAE,MAAM,OAAO,cAAc,SAAS,KAAK,GAAI;AACjD,mBAAO,YAAY,SAAY,KAAK,KAAK,8BAA8B,IAAI,KAAK,KAAK,sCAAsC;AAAA,UAC7H;AACA,cAAI,MAAM,eAAe,IAAI,EAAG,QAAO,KAAK,KAAK,kCAAkC;AACnF,cAAI,QAAQ,QAAS,QAAO,GAAG,MAAM,QAAQ,MAAM,SAAS,aAAa,YAAY,KAAK,CAAC,CAAC,CAAC;AAC7F,cAAI,CAAC,SAAS,iBAAkB,QAAO,GAAG,CAAC,CAAC;AAC5C,gBAAM,QAAQ,MAAM,SAAS,iBAAkB,YAAY,KAAK,GAAG,EAAG,CAAC;AACvE,iBAAO,GAAG,CAAC,CAAC;AAAA,QACd;AAAA,QACA,KAAK;AAAA,QACL,KAAK,SAAS;AACZ,gBAAM,EAAC,IAAG,IAAI;AACd,gBAAM,MAAM,EAAC,GAAG,KAAK,UAAU,MAAM,QAAQ,cAAc,SAAS,IAAI,UAAU,GAAG,EAAC;AACtF,cAAI,QAAQ,QAAS,QAAO,GAAG,MAAM,SAAS,YAAY,GAAG,CAAC;AAC9D,cAAI,CAAC,SAAS,WAAY,QAAO,GAAG,CAAC,CAAC;AACtC,iBAAO,GAAG,MAAM,QAAQ,MAAM,SAAS,WAAY,GAAG,CAAC,CAAC;AAAA,QAC1D;AAAA,QACA,KAAK,UAAU;AACb,gBAAM,EAAC,UAAU,OAAM,IAAI;AAC3B,gBAAM,SAAS,gBAAgB,MAAM,QAAQ,cAAc,SAAS,UAAU,GAAG,GAAG,MAAM;AAC1F,iBAAO,GAAG,CAAC,CAAC;AAAA,QACd;AAAA,QACA,KAAK,YAAY;AACf,cAAI,CAAC,SAAS,oBAAqB,QAAO,KAAK,KAAK,aAAa,YAAY,4BAA4B;AACzG,gBAAM,EAAC,UAAU,UAAS,IAAI;AAC9B,gBAAM,SAAS,oBAAoB,MAAM,QAAQ,cAAc,SAAS,UAAU,GAAG,GAAG,SAAS;AACjG,iBAAO,GAAG,CAAC,CAAC;AAAA,QACd;AAAA,QACA,KAAK,YAAY;AACf,gBAAM,EAAC,MAAM,GAAE,IAAI;AACnB,iBAAO,GAAG,EAAC,WAAW,SAAS,cAAc,MAAM,EAAE,EAAC,CAAC;AAAA,QACzD;AAAA,MACF;AAAA,IACF,SAAS,KAAK;AACZ,UAAI,eAAe,kBAAmB,QAAO,KAAK,IAAI,QAAQ,IAAI,OAAO;AACzE,aAAO,KAAK,KAAK,gBAAgB;AAAA,IACnC;AAAA,EACF;AACF;AAGA,SAAS,UAAU,QAAqB,GAA4B,eAAsC;AACxG,UAAQ,QAAQ;AAAA,IACd,KAAK;AAAS,aAAO,EAAC,QAAQ,YAAY,EAAE,QAAQ,aAAa,EAAC;AAAA,IAClE,KAAK;AAAS,aAAO,EAAC,OAAO,eAAe,EAAE,OAAO,eAAe,EAAC;AAAA,IACrE,KAAK;AAAa,aAAO,EAAC,OAAO,eAAe,EAAE,OAAO,eAAe,GAAG,IAAI,WAAW,EAAE,EAAE,EAAC;AAAA,IAC/F,KAAK;AAAA,IACL,KAAK;AAAS,aAAO,EAAC,KAAK,UAAU,EAAE,GAAG,EAAC;AAAA,IAC3C,KAAK;AAAU,aAAO,EAAC,UAAU,IAAI,EAAE,UAAU,YAAY,eAAe,GAAG,QAAQ,IAAI,EAAE,QAAQ,UAAU,GAAG,EAAC;AAAA,IACnH,KAAK,YAAY;AACf,YAAM,YAAY,IAAI,EAAE,WAAW,aAAa,GAAG;AACnD,UAAI,CAAC,yBAAyB,KAAK,SAAS,EAAG,OAAM,IAAI,kBAAkB,KAAK,gCAAgC;AAChH,aAAO,EAAC,UAAU,IAAI,EAAE,UAAU,YAAY,eAAe,GAAG,UAAS;AAAA,IAC3E;AAAA,IACA,KAAK;AAAY,aAAO,EAAC,MAAM,MAAM,EAAE,MAAM,MAAM,GAAG,IAAI,MAAM,EAAE,IAAI,IAAI,EAAC;AAAA,EAC7E;AACF;AAGA,SAAS,QAAQ,GAAW,GAAoB;AAC9C,MAAI,EAAE,WAAW,EAAE,OAAQ,QAAO;AAClC,MAAI,OAAO;AACX,WAAS,IAAI,GAAG,IAAI,EAAE,QAAQ,IAAK,SAAQ,EAAE,WAAW,CAAC,IAAI,EAAE,WAAW,CAAC;AAC3E,SAAO,SAAS;AAClB;AAKA,eAAe,QAAW,IAAkC;AAC1D,MAAI;AACF,WAAO,MAAM,GAAG;AAAA,EAClB,SAAS,KAAK;AACZ,QAAI,eAAe,SAAS,oBAAoB,KAAK,IAAI,OAAO,GAAG;AACjE,YAAM,IAAI,kBAAkB,KAAK,WAAW,IAAI,OAAO,EAAE,MAAM,GAAG,GAAG,CAAC;AAAA,IACxE;AACA,UAAM;AAAA,EACR;AACF;AAEA,IAAM,KAAK,CAAC,UAAkC,EAAC,QAAQ,KAAK,MAAM,SAAS,SAAY,OAAO,KAAI;AAClG,IAAM,OAAO,CAAC,QAAgB,WAAkC,EAAC,QAAQ,MAAM,EAAC,OAAO,WAAW,KAAK,EAAE,MAAM,GAAG,GAAG,EAAC,EAAC;AAIvH,IAAM,SAAS,CAAC,QAAgB,IAAI,OAAO,UAAU,GAAG,IAAI;AAE5D,SAAS,IAAI,OAAe,KAAoB;AAC9C,QAAM,IAAI,kBAAkB,KAAK,GAAG,KAAK,KAAK,GAAG,EAAE;AACrD;AACA,SAAS,OAAO,GAAY,OAAwC;AAClE,MAAI,CAAC,KAAK,OAAO,MAAM,YAAY,MAAM,QAAQ,CAAC,EAAG,KAAI,OAAO,oBAAoB;AACpF,SAAO;AACT;AACA,SAAS,IAAI,GAAY,OAAe,KAAqB;AAC3D,MAAI,OAAO,MAAM,SAAU,KAAI,OAAO,mBAAmB;AACzD,MAAI,EAAE,SAAS,IAAK,KAAI,OAAO,eAAe,GAAG,EAAE;AACnD,SAAO;AACT;AACA,SAAS,OAAO,GAAY,OAAe,KAAiC;AAC1E,SAAO,MAAM,SAAY,SAAY,IAAI,GAAG,OAAO,GAAG;AACxD;AACA,SAAS,OAAO,GAAY,OAAe,KAAqB;AAC9D,QAAM,IAAI,IAAI,GAAG,OAAO,GAAG;AAC3B,MAAI,CAAC,OAAO,GAAG,EAAE,KAAK,CAAC,EAAG,KAAI,OAAO,mCAAmC;AACxE,SAAO;AACT;AACA,SAAS,IAAI,GAAY,OAAe,MAAM,GAAG,MAAM,OAAO,kBAA0B;AACtF,MAAI,OAAO,MAAM,YAAY,CAAC,OAAO,SAAS,CAAC,KAAK,IAAI,OAAO,IAAI,IAAK,KAAI,OAAO,mCAAmC;AACtH,SAAO;AACT;AACA,SAAS,KAAK,GAAY,OAAoC;AAC5D,MAAI,MAAM,OAAW,QAAO;AAC5B,MAAI,OAAO,MAAM,UAAW,KAAI,OAAO,oBAAoB;AAC3D,SAAO;AACT;AAEA,SAAS,YAAY,GAAY,gBAAgB,OAAoB;AACnE,QAAM,IAAI,OAAO,GAAG,QAAQ;AAC5B,QAAM,iBAAiB,CAAC,iBAAiB,EAAE,mBAAmB,SAAY,SAAY,IAAI,EAAE,gBAAgB,yBAAyB,GAAG,EAAE;AAC1I,MAAI,mBAAmB,UAAa,CAAC,OAAO,UAAU,cAAc,EAAG,KAAI,yBAAyB,qBAAqB;AACzH,QAAM,WAAW,EAAE,gBAAgB,SAAY,SAAY,IAAI,EAAE,aAAa,sBAAsB,GAAG,GAAM;AAC7G,MAAI,aAAa,UAAa,CAAC,OAAO,UAAU,QAAQ,EAAG,KAAI,sBAAsB,qBAAqB;AAC1G,QAAM,gBAAgB,OAAO,EAAE,eAAe,wBAAwB,GAAG;AACzE,QAAM,eAAe,EAAE,iBAAiB,SAAY,SAAY,OAAO,EAAE,cAAc,uBAAuB,EAAE;AAChH,QAAM,MAAM,KAAK,EAAE,KAAK,YAAY;AACpC,QAAM,OAAO,KAAK,EAAE,MAAM,aAAa;AACvC,SAAO;AAAA,IACL,aAAa,IAAI,EAAE,aAAa,sBAAsB,EAAE;AAAA,IACxD,aAAa,IAAI,EAAE,aAAa,sBAAsB,EAAE;AAAA,IACxD,GAAI,mBAAmB,SAAY,EAAC,eAAc,IAAI,CAAC;AAAA,IACvD,WAAW,IAAI,EAAE,WAAW,oBAAoB,EAAE;AAAA,IAClD,WAAW,IAAI,EAAE,WAAW,oBAAoB,GAAG;AAAA,IACnD,UAAU,OAAO,EAAE,UAAU,mBAAmB,EAAE;AAAA,IAClD,aAAa,IAAI,EAAE,aAAa,sBAAsB,GAAG;AAAA,IACzD,GAAI,kBAAkB,SAAY,EAAC,cAAa,IAAI,CAAC;AAAA,IACrD,GAAI,iBAAiB,SAAY,EAAC,aAAY,IAAI,CAAC;AAAA,IACnD,GAAI,aAAa,SAAY,EAAC,aAAa,SAAQ,IAAI,CAAC;AAAA,IACxD,GAAI,QAAQ,SAAY,EAAC,IAAG,IAAI,CAAC;AAAA,IACjC,GAAI,SAAS,SAAY,EAAC,KAAI,IAAI,CAAC;AAAA,EACrC;AACF;AAEA,IAAM,YAAY,oBAAI,IAAI,CAAC,WAAW,YAAY,aAAa,OAAO,CAAC;AAGvE,SAAS,eAAe,GAAY,WAA0B;AAC5D,QAAM,IAAI,OAAO,GAAG,OAAO;AAC3B,QAAM,MAAa;AAAA,IACjB,UAAU,IAAI,EAAE,UAAU,kBAAkB,EAAE;AAAA,IAC9C,UAAU,IAAI,EAAE,UAAU,kBAAkB,SAAS;AAAA,IACrD,gBAAgB,IAAI,EAAE,gBAAgB,wBAAwB,GAAG;AAAA,IACjE,aAAa,EAAE,gBAAgB,OAAO,OAAO,IAAI,EAAE,aAAa,qBAAqB,GAAG;AAAA,IACxF,cAAc,OAAO,EAAE,cAAc,sBAAsB,EAAE;AAAA,IAC7D,YAAY,OAAO,EAAE,YAAY,oBAAoB,EAAE;AAAA,IACvD,eAAe,OAAO,EAAE,eAAe,uBAAuB,EAAE;AAAA,IAChE,QAAQ,IAAI,EAAE,QAAQ,cAAc;AAAA,IACpC,YAAY,IAAI,EAAE,YAAY,kBAAkB;AAAA,IAChD,aAAa,IAAI,EAAE,aAAa,qBAAqB,GAAG,CAAC;AAAA,IACzD,cAAc,IAAI,EAAE,cAAc,oBAAoB;AAAA,EACxD;AACA,MAAI,EAAE,cAAc,OAAW,KAAI,YAAY,UAAU,EAAE,SAAS;AACpE,MAAI,EAAE,SAAS,QAAW;AACxB,QAAI,CAAC,MAAM,QAAQ,EAAE,IAAI,KAAK,EAAE,KAAK,SAAS,GAAI,KAAI,cAAc,2BAA2B;AAC/F,QAAI,OAAO,EAAE,KAAK,IAAI,CAAC,GAAG,MAAM;AAC9B,YAAM,MAAM,OAAO,GAAG,cAAc,CAAC,GAAG;AACxC,YAAM,OAAO,IAAI,IAAI,MAAM,cAAc,CAAC,UAAU,EAAE;AACtD,UAAI,CAAC,UAAU,IAAI,IAAI,EAAG,KAAI,cAAc,CAAC,UAAU,kBAAkB;AACzE,YAAM,cAAc,OAAO,IAAI,aAAa,cAAc,CAAC,iBAAiB,GAAG;AAC/E,YAAM,WAAW,KAAK,IAAI,UAAU,cAAc,CAAC,YAAY;AAC/D,UAAI,aAAa,OAAW,KAAI,cAAc,CAAC,cAAc,oBAAoB;AACjF,aAAO;AAAA,QACL;AAAA,QACA,QAAQ,OAAO,IAAI,QAAQ,cAAc,CAAC,YAAY,EAAE;AAAA,QACxD,OAAO,IAAI,IAAI,OAAO,cAAc,CAAC,WAAW,GAAG;AAAA,QACnD;AAAA,QACA,GAAI,gBAAgB,SAAY,EAAC,YAAW,IAAI,CAAC;AAAA,MACnD;AAAA,IACF,CAAC;AAAA,EACH;AACA,MAAI,EAAE,iBAAiB,QAAW;AAChC,UAAM,OAAO,OAAO,EAAE,cAAc,oBAAoB;AACxD,UAAM,OAAO,OAAO,KAAK,IAAI;AAC7B,QAAI,KAAK,SAAS,GAAI,KAAI,sBAAsB,sBAAsB;AACtE,QAAI,eAAe,OAAO,YAAY,KAAK,IAAI,CAAC,MAAM,CAAC,IAAI,GAAG,0BAA0B,GAAG,GAAG,IAAI,KAAK,CAAC,GAAG,sBAAsB,EAAE,MAAM,GAAG,EAAE,CAAC,IAAI,GAAM,CAAC,CAAC,CAAC;AAAA,EAC9J;AACA,SAAO;AACT;AAEA,SAAS,UAAU,GAAuB;AACxC,QAAM,IAAI,OAAO,GAAG,iBAAiB;AACrC,QAAM,MAAiB,CAAC;AACxB,MAAI,EAAE,OAAO,OAAW,KAAI,KAAK,IAAI,EAAE,IAAI,sBAAsB,GAAG;AACpE,MAAI,EAAE,SAAS,OAAW,KAAI,OAAO,IAAI,EAAE,MAAM,wBAAwB,GAAS;AAClF,MAAI,EAAE,UAAU,OAAW,KAAI,QAAQ,OAAO,EAAE,OAAO,yBAAyB,EAAE;AAClF,MAAI,EAAE,YAAY,QAAW;AAC3B,UAAM,KAAK,IAAI,EAAE,SAAS,2BAA2B,CAAC;AACtD,QAAI,CAAC,OAAO,UAAU,EAAE,EAAG,KAAI,2BAA2B,qBAAqB;AAC/E,QAAI,UAAU;AAAA,EAChB;AACA,MAAI,EAAE,aAAa,OAAW,KAAI,WAAW,OAAO,EAAE,UAAU,4BAA4B,EAAE;AAC9F,MAAI,EAAE,oBAAoB,OAAW,KAAI,kBAAkB,IAAI,EAAE,iBAAiB,mCAAmC,GAAG;AACxH,MAAI,EAAE,iBAAiB,OAAW,KAAI,eAAe,IAAI,EAAE,cAAc,gCAAgC,GAAS;AAClH,MAAI,EAAE,cAAc,OAAW,KAAI,YAAY,OAAO,EAAE,WAAW,6BAA6B,EAAE;AAClG,SAAO;AACT;AAGA,SAAS,WAAW,GAAwB;AAC1C,QAAM,IAAI,OAAO,GAAG,IAAI;AACxB,QAAM,MAAkB,EAAC,OAAO,IAAI,EAAE,OAAO,YAAY,EAAE,GAAG,IAAI,IAAI,EAAE,IAAI,SAAS,GAAG,GAAG,OAAO,OAAO,EAAE,OAAO,YAAY,EAAE,EAAC;AACjI,QAAM,WAAyC,CAAC,CAAC,QAAQ,GAAS,GAAG,CAAC,YAAY,EAAE,GAAG,CAAC,mBAAmB,GAAG,GAAG,CAAC,gBAAgB,GAAS,GAAG,CAAC,QAAQ,GAAK,GAAG,CAAC,WAAW,GAAO,GAAG,CAAC,gBAAgB,GAAO,GAAG,CAAC,aAAa,GAAO,CAAC;AACtO,aAAW,CAAC,GAAG,GAAG,KAAK,SAAU,KAAI,EAAE,CAAC,MAAM,OAAW,CAAC,IAA2C,CAAC,IAAI,MAAM,aAAa,OAAO,EAAE,CAAC,GAAG,MAAM,CAAC,IAAI,GAAG,IAAI,IAAI,EAAE,CAAC,GAAG,MAAM,CAAC,IAAI,GAAG;AACpL,MAAI,EAAE,iBAAiB,QAAW;AAChC,QAAI,EAAE,iBAAiB,UAAU,EAAE,iBAAiB,MAAO,KAAI,mBAAmB,sBAAsB;AACxG,QAAI,eAAe,EAAE;AAAA,EACvB;AACA,MAAI,EAAE,YAAY,OAAW,KAAI,UAAU,IAAI,EAAE,SAAS,cAAc,CAAC;AACzE,SAAO;AACT;AAEA,SAAS,UAAU,GAAuB;AACxC,QAAM,IAAI,OAAO,GAAG,KAAK;AACzB,QAAM,WAAW,CAAC,GAAY,MAAe,MAAM,QAAQ,MAAM,SAAY,SAAY,IAAI,GAAG,GAAG,GAAG;AACtG,QAAM,eAAe,SAAS,EAAE,cAAc,kBAAkB;AAChE,QAAM,aAAa,SAAS,EAAE,YAAY,gBAAgB;AAC1D,SAAO;AAAA,IACL,UAAU,IAAI,EAAE,UAAU,gBAAgB,eAAe;AAAA,IACzD,GAAI,iBAAiB,SAAY,EAAC,aAAY,IAAI,CAAC;AAAA,IACnD,GAAI,eAAe,SAAY,EAAC,WAAU,IAAI,CAAC;AAAA,EACjD;AACF;AAEA,SAAS,MAAM,GAAY,OAAsB;AAC/C,QAAM,IAAI,OAAO,GAAG,KAAK;AACzB,SAAO,EAAC,OAAO,IAAI,EAAE,OAAO,GAAG,KAAK,UAAU,EAAE,GAAG,OAAO,IAAI,EAAE,OAAO,GAAG,KAAK,UAAU,GAAG,EAAC;AAC/F;AAIA,SAAS,YAAY,OAAqB;AACxC,QAAM,EAAC,cAAc,GAAG,KAAI,IAAI;AAChC,MAAI,CAAC,aAAc,QAAO;AAC1B,QAAM,EAAC,CAAC,UAAU,GAAG,OAAO,GAAG,KAAI,IAAI;AACvC,SAAO,OAAO,KAAK,IAAI,EAAE,SAAS,EAAC,GAAG,MAAM,cAAc,KAAI,IAAI;AACpE;AAEA,IAAM,YAAY;AAGlB,SAAS,UAAU,OAAwB;AACzC,MAAI,UAAU,QAAQ,OAAO,UAAU,YAAY,OAAO,UAAU,UAAW,QAAO,KAAK,UAAU,KAAK;AAC1G,MAAI,OAAO,UAAU,UAAU;AAC7B,QAAI,CAAC,OAAO,SAAS,KAAK,EAAG,OAAM,IAAI,kBAAkB,KAAK,oCAAoC;AAClG,WAAO,KAAK,UAAU,KAAK;AAAA,EAC7B;AACA,MAAI,MAAM,QAAQ,KAAK,EAAG,QAAO,IAAI,MAAM,IAAI,SAAS,EAAE,KAAK,GAAG,CAAC;AACnE,MAAI,OAAO,UAAU,UAAU;AAC7B,UAAM,IAAI;AACV,WAAO,IAAI,OAAO,KAAK,CAAC,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC,MAAM,MAAS,EAAE,KAAK,EAAE,IAAI,CAAC,MAAM,GAAG,KAAK,UAAU,CAAC,CAAC,IAAI,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,KAAK,GAAG,CAAC;AAAA,EACpI;AACA,QAAM,IAAI,kBAAkB,KAAK,wBAAwB;AAC3D;AAEA,SAAS,MAAM,OAA2B;AACxC,SAAO,MAAM,KAAK,OAAO,CAAC,MAAM,EAAE,SAAS,EAAE,EAAE,SAAS,GAAG,GAAG,CAAC,EAAE,KAAK,EAAE;AAC1E;AACA,SAAS,QAAQ,KAAyB;AACxC,QAAM,MAAM,IAAI,WAAW,IAAI,SAAS,CAAC;AACzC,WAAS,IAAI,GAAG,IAAI,IAAI,QAAQ,IAAK,KAAI,CAAC,IAAI,SAAS,IAAI,MAAM,IAAI,GAAG,IAAI,IAAI,CAAC,GAAG,EAAE;AACtF,SAAO;AACT;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/server.ts","../src/util.ts","../src/validate.ts"],"sourcesContent":["import type {Asset, CrossChainProvider, IntentRef, Quote, QuoteParams, TxRequest, UnsignedTx} from \"./types.js\";\nimport {redactUrls} from \"./util.js\";\nimport {validQuote} from \"./validate.js\";\n\n/**\n * The server side of {@link remoteProvider}: runs real providers behind your backend so API\n * keys, affiliate settings and non-CORS endpoints never ship in the app.\n *\n * Framework-free: wire `handle(action, body, context)` into any HTTP route that receives\n * `POST {baseUrl}/{action}` with a JSON body, and send back `{status, body}` as JSON.\n *\n * What it guarantees:\n * - every field of every request is validated (types, lengths, digit-only amounts) before a\n * provider sees it; unknown actions, providers and malformed bodies are refused;\n * - `quote` answers are sealed with HMAC-SHA256 under `sealSecret`; `build` and `preflight`\n * refuse a quote that was not sealed here, was changed in transit, or has expired — so a\n * client cannot make your server build a deposit for terms the provider never quoted;\n * - every quote's `intentId` leaves with an intent token appended (`<id>~<tag>`): `track`,\n * `claim`, `notify` and `register` refuse an id without a valid one (403), and the provider\n * only ever sees its own id. With `subject`, quotes and tokens are bound to the caller, so\n * another user's sealed quote or swap id is refused too; without it the token is a bearer\n * capability for that one swap (share a transaction hash with support, not the swap id);\n * - a provider's named refusal travels in-band (`{noQuote}`) so the app can show WHY;\n * - error text crossing the boundary has URLs redacted (no upstream endpoints or keys).\n *\n * What it does NOT do, and you must: authenticate the caller and rate-limit per user/IP in\n * `guard` (see {@link Swap2HandlerOptions.guard}), cap the raw request size before parsing\n * (`serializedTx` may legitimately be ~2 MB), and log without request bodies.\n *\n * Server-only (WebCrypto HMAC; Node ≥ 20, Deno, Bun, edge runtimes). Never import it into an app.\n */\n\n/** One call, as `guard` sees it. `dry` quotes are display quotes: give them their own, larger bucket. */\nexport interface Swap2Call {\n action: Swap2Action;\n provider: string;\n dry: boolean;\n /**\n * The validated request as the provider will get it — amounts, recipients, the provider's own\n * intent id (token removed) — for limits that depend on what is asked. Not yet checked against\n * the seal or the intent token: those run after the guard.\n */\n request: Swap2Request;\n}\n\n/** The validated body of each action. */\nexport type Swap2Request =\n | {params: QuoteParams}\n | {quote: Quote; tx?: UnsignedTx}\n | {ref: IntentRef}\n | {intentId: string; txHash: string}\n | {intentId: string; signature: string}\n | {from: Asset; to: Asset};\n\nexport type Swap2Action = \"quote\" | \"build\" | \"preflight\" | \"track\" | \"claim\" | \"notify\" | \"register\" | \"supports\";\n\nexport interface Swap2HandlerOptions<C> {\n /** The real providers, by `name` (the same names the app's `remoteProvider`s use). */\n providers: readonly CrossChainProvider[];\n /** HMAC key for quote seals: at least 32 bytes of randomness, kept server-side, rotated with care. */\n sealSecret: string | Uint8Array;\n /**\n * Authentication and rate limiting — required, because this endpoint spends your provider\n * quotas and builds deposits. Throw {@link Swap2HandlerError} (401 / 403 / 429) to refuse.\n * `context` is whatever your route passes as the third argument (the request, a session…).\n */\n guard: (call: Swap2Call, context: C) => void | Promise<void>;\n /**\n * Who is calling: a stable id of the authenticated user — the Sign-In with Ethereum address,\n * not an email — or undefined. Quotes are sealed for, and intent tokens issued to, this subject:\n * another caller's quote or swap id is refused (403). Without it, the intent token in a swap\n * id is a bearer capability. Runs after `guard`.\n */\n subject?: (context: C) => string | undefined | Promise<string | undefined>;\n /**\n * Moving to this handler from another backend: until this time (ms since epoch), `track`\n * accepts a swap id without an intent token, so swaps started before the move can still be\n * followed. Read-only: `claim`, `notify` and `register` always require the token.\n */\n acceptUntokenedTrackUntil?: number;\n /**\n * Quote tokens the swap catalog does not list (`params.sourceToken` an EVM contract or Solana\n * mint, with `params.sourceDecimals`), for apps whose users add their own tokens. Off by default:\n * `sourceDecimals` is then dropped and the providers refuse an unlisted source. Destination tokens\n * are addresses either way.\n */\n allowUnlistedTokens?: boolean;\n /** Clock for expiry checks (tests). */\n now?: () => number;\n}\n\nexport interface Swap2Response {\n status: number;\n body: unknown;\n}\n\n/** Throw from `guard` (or anywhere) to answer with this status and message. */\nexport class Swap2HandlerError extends Error {\n readonly status: number;\n constructor(status: number, message: string) {\n super(message);\n this.name = \"Swap2HandlerError\";\n this.status = status;\n }\n}\n\nconst ACTIONS: ReadonlySet<string> = new Set([\"quote\", \"build\", \"preflight\", \"track\", \"claim\", \"notify\", \"register\", \"supports\"]);\nconst SEAL_FIELD = \"__remoteSeal\";\nconst SEAL_PREFIX = \"swap2-remote-quote:v1:\";\nconst INTENT_PREFIX = \"swap2-remote-intent:v1\";\n/** `<provider's intent id>~<128-bit HMAC tag>`. */\nconst TOKENED = /^([\\s\\S]+)~([0-9a-f]{32})$/;\nconst INTENT_MAX = 2000;\nconst WIRE_INTENT_MAX = INTENT_MAX + 33;\n\nexport function createSwap2Handler<C = unknown>(opts: Swap2HandlerOptions<C>): (action: string, body: unknown, context: C) => Promise<Swap2Response> {\n if (typeof opts.guard !== \"function\") throw new Error(\"swap2-server: guard is required (authentication + rate limiting)\");\n const secret = typeof opts.sealSecret === \"string\" ? new TextEncoder().encode(opts.sealSecret) : opts.sealSecret;\n if (!(secret instanceof Uint8Array) || secret.length < 32) throw new Error(\"swap2-server: sealSecret must be at least 32 bytes\");\n const byName = new Map<string, CrossChainProvider>();\n for (const p of opts.providers) {\n if (byName.has(p.name)) throw new Error(`swap2-server: duplicate provider '${p.name}'`);\n byName.set(p.name, p);\n }\n const now = opts.now ?? Date.now;\n const keyPromise = crypto.subtle.importKey(\"raw\", secret as BufferSource, {name: \"HMAC\", hash: \"SHA-256\"}, false, [\"sign\", \"verify\"]);\n\n // The subject is JSON-quoted, so it cannot run into the canonical quote that follows (\"{\").\n const sealInput = (provider: string, subject: string | undefined, quote: Quote) =>\n new TextEncoder().encode(SEAL_PREFIX + provider + \":\" + (subject === undefined ? \"\" : JSON.stringify(subject) + \":\") + canonical(withoutSeal(quote)));\n async function seal(provider: string, subject: string | undefined, quote: Quote): Promise<Quote> {\n const tag = new Uint8Array(await crypto.subtle.sign(\"HMAC\", await keyPromise, sealInput(provider, subject, quote)));\n return {...quote, providerData: {...(quote.providerData ?? {}), [SEAL_FIELD]: toHex(tag)}};\n }\n async function verify(provider: string, subject: string | undefined, quote: Quote): Promise<boolean> {\n if (quote.provider !== provider) return false;\n const sealHex = quote.providerData?.[SEAL_FIELD];\n if (!sealHex || !/^[0-9a-f]{64}$/.test(sealHex)) return false;\n return crypto.subtle.verify(\"HMAC\", await keyPromise, fromHex(sealHex) as BufferSource, sealInput(provider, subject, quote)); // constant-time\n }\n /** The intent token: HMAC over (provider, subject, the provider's intent id), domain-separated from the seal. */\n async function intentTag(provider: string, subject: string | undefined, intentId: string): Promise<string> {\n const data = new TextEncoder().encode(JSON.stringify([INTENT_PREFIX, provider, subject ?? null, intentId]));\n return toHex(new Uint8Array(await crypto.subtle.sign(\"HMAC\", await keyPromise, data)).subarray(0, 16));\n }\n /** A swap id from the app → the provider's own id, or 403. */\n async function untoken(provider: string, subject: string | undefined, wireId: string, action: Swap2Action): Promise<string> {\n const m = TOKENED.exec(wireId);\n if (!m) {\n if (action === \"track\" && opts.acceptUntokenedTrackUntil !== undefined && now() < opts.acceptUntokenedTrackUntil) return wireId;\n throw new Swap2HandlerError(403, \"swap id carries no intent token from this server\");\n }\n if (!sameHex(await intentTag(provider, subject, m[1]!), m[2]!)) {\n throw new Swap2HandlerError(403, subject === undefined ? \"swap id carries an invalid intent token\" : \"swap id was not issued to this caller\");\n }\n return m[1]!;\n }\n /** The quote as the provider issued it: no seal, its own intent id. */\n const forProvider = (quote: Quote): Quote => ({...stripSeal(quote), intentId: TOKENED.exec(quote.intentId)?.[1] ?? quote.intentId});\n\n return async function handle(action: string, body: unknown, context: C): Promise<Swap2Response> {\n try {\n if (!ACTIONS.has(action)) return fail(404, `unknown action '${String(action).slice(0, 40)}'`);\n const b = record(body, \"body\");\n const providerName = str(b.provider, \"provider\", 40);\n const act = action as Swap2Action;\n const request = validated(act, b, opts.allowUnlistedTokens === true);\n const dry = \"params\" in request && request.params.dry === true;\n // The guard sees the provider's own intent id, as the provider will.\n const shown: Swap2Request = \"ref\" in request ? {ref: {...request.ref, intentId: TOKENED.exec(request.ref.intentId)?.[1] ?? request.ref.intentId}}\n : \"quote\" in request ? {...request, quote: forProvider(request.quote)}\n : \"intentId\" in request ? {...request, intentId: TOKENED.exec(request.intentId)?.[1] ?? request.intentId}\n : request;\n await opts.guard({action: act, provider: providerName, dry, request: shown}, context);\n const subject = opts.subject ? await opts.subject(context) : undefined;\n\n const provider = byName.get(providerName);\n if (!provider) return fail(404, `unknown provider '${providerName}'`);\n if (!provider.enabled()) return fail(403, `provider '${providerName}' is disabled`);\n\n switch (act) {\n case \"quote\": {\n const {params} = request as {params: QuoteParams};\n let refusal: string | null = null; // per request: a shared field races under parallel quotes\n const quote = await provider.getQuote({...params, onRefusal: (reason) => { refusal = reason; }});\n if (quote) {\n let normalized: Quote;\n try { normalized = normalizeQuote(quote, INTENT_MAX); } catch { return fail(502, `${providerName} returned a quote this server refuses to seal`); }\n // A seal vouches for the quote: hold it to the engine's own checks (provider, input, floor,\n // slippage, expiry) so a client that does not re-validate cannot be handed a bad one.\n if (!validQuote(normalized, params, providerName) || normalized.validUntilMs <= now()) {\n return fail(502, `${providerName} returned a quote the swap engine would reject`);\n }\n const tokened = {...normalized, intentId: `${normalized.intentId}~${await intentTag(providerName, subject, normalized.intentId)}`};\n return ok(await seal(providerName, subject, tokened));\n }\n if (refusal) return ok({noQuote: clientText(String(refusal))});\n return ok(params.dry === false ? {noQuote: `${providerName} refused without naming a reason`} : null);\n }\n case \"build\":\n case \"preflight\": {\n const {quote, tx} = request as {quote: Quote; tx?: UnsignedTx};\n if (!(await verify(providerName, subject, quote))) {\n return subject === undefined ? fail(400, \"quote integrity check failed\") : fail(403, \"quote was not sealed for this caller\");\n }\n if (quote.validUntilMs < now()) return fail(410, \"quote expired: ask for a new one\");\n if (act === \"build\") return ok(await refused(() => provider.buildDeposit(forProvider(quote))));\n if (!provider.preflightDeposit) return ok({});\n await refused(() => provider.preflightDeposit!(forProvider(quote), tx!));\n return ok({});\n }\n case \"track\":\n case \"claim\": {\n const {ref} = request as {ref: IntentRef};\n const own = {...ref, intentId: await untoken(providerName, subject, ref.intentId, act)};\n if (act === \"track\") return ok(await provider.trackIntent(own));\n if (!provider.buildClaim) return ok({});\n return ok(await refused(() => provider.buildClaim!(own)));\n }\n case \"notify\": {\n const {intentId, txHash} = request as {intentId: string; txHash: string};\n await provider.notifyDeposit?.(await untoken(providerName, subject, intentId, act), txHash);\n return ok({});\n }\n case \"register\": {\n if (!provider.registerSignedOrder) return fail(400, `provider '${providerName}' has no signed-order flow`);\n const {intentId, signature} = request as {intentId: string; signature: string};\n await provider.registerSignedOrder(await untoken(providerName, subject, intentId, act), signature);\n return ok({});\n }\n case \"supports\": {\n const {from, to} = request as {from: Asset; to: Asset};\n return ok({supported: provider.supportsRoute(from, to)});\n }\n }\n } catch (err) {\n if (err instanceof Swap2HandlerError) return fail(err.status, err.message);\n return fail(500, \"internal error\"); // never echo provider internals or upstream URLs\n }\n };\n}\n\n/** Each action's body, validated field by field before anyone (guard, provider) sees it. */\nfunction validated(action: Swap2Action, b: Record<string, unknown>, allowUnlisted: boolean): Swap2Request {\n switch (action) {\n case \"quote\": return {params: quoteParams(b.params, allowUnlisted)};\n case \"build\": return {quote: normalizeQuote(b.quote, WIRE_INTENT_MAX)};\n case \"preflight\": return {quote: normalizeQuote(b.quote, WIRE_INTENT_MAX), tx: unsignedTx(b.tx)};\n case \"track\":\n case \"claim\": return {ref: intentRef(b.ref)};\n case \"notify\": return {intentId: str(b.intentId, \"intentId\", WIRE_INTENT_MAX), txHash: str(b.txHash, \"txHash\", 200)};\n case \"register\": {\n const signature = str(b.signature, \"signature\", 302);\n if (!/^0x[0-9a-fA-F]{2,300}$/.test(signature)) throw new Swap2HandlerError(400, \"signature: not a hex signature\");\n return {intentId: str(b.intentId, \"intentId\", WIRE_INTENT_MAX), signature};\n }\n case \"supports\": return {from: asset(b.from, \"from\"), to: asset(b.to, \"to\")};\n }\n}\n\n/** Constant-time comparison of two lowercase hex strings. */\nfunction sameHex(a: string, b: string): boolean {\n if (a.length !== b.length) return false;\n let diff = 0;\n for (let i = 0; i < a.length; i++) diff |= a.charCodeAt(i) ^ b.charCodeAt(i);\n return diff === 0;\n}\n\n// ---- refusals -------------------------------------------------------------------------------\n\n/** A provider's own refusal (\"lifi: …\") is actionable: 422 with the redacted text. Anything else is a 500. */\nasync function refused<T>(fn: () => Promise<T>): Promise<T> {\n try {\n return await fn();\n } catch (err) {\n if (err instanceof Error && /^[a-z0-9_-]+: \\S/i.test(err.message)) {\n throw new Swap2HandlerError(422, clientText(err.message));\n }\n throw err;\n }\n}\n\n/** What a client is shown of a provider's text: cut before the URL pass runs (scan 2026-10-27 modules, F34), no URLs, 300 characters. */\nconst clientText = (text: string): string => redactUrls(text.slice(0, 2_048)).slice(0, 300);\n\nconst ok = (body: unknown): Swap2Response => ({status: 200, body: body === undefined ? null : body});\nconst fail = (status: number, error: string): Swap2Response => ({status, body: {error: clientText(error)}});\n\n// ---- validation (the limits mirror a production backend's schema) --------------------------\n\nconst DIGITS = (max: number) => new RegExp(`^\\\\d{1,${max}}$`);\n\nfunction bad(field: string, why: string): never {\n throw new Swap2HandlerError(400, `${field}: ${why}`);\n}\nfunction record(v: unknown, field: string): Record<string, unknown> {\n if (!v || typeof v !== \"object\" || Array.isArray(v)) bad(field, \"expected an object\");\n return v as Record<string, unknown>;\n}\nfunction str(v: unknown, field: string, max: number): string {\n if (typeof v !== \"string\") bad(field, \"expected a string\");\n if (v.length > max) bad(field, `longer than ${max}`);\n return v;\n}\nfunction optStr(v: unknown, field: string, max: number): string | undefined {\n return v === undefined ? undefined : str(v, field, max);\n}\nfunction digits(v: unknown, field: string, max: number): string {\n const s = str(v, field, max);\n if (!DIGITS(max).test(s)) bad(field, \"expected base units (digits only)\");\n return s;\n}\nfunction num(v: unknown, field: string, min = 0, max = Number.MAX_SAFE_INTEGER): number {\n if (typeof v !== \"number\" || !Number.isFinite(v) || v < min || v > max) bad(field, \"expected a finite number in range\");\n return v;\n}\nfunction bool(v: unknown, field: string): boolean | undefined {\n if (v === undefined) return undefined;\n if (typeof v !== \"boolean\") bad(field, \"expected a boolean\");\n return v;\n}\n\nfunction quoteParams(v: unknown, allowUnlisted = false): QuoteParams {\n const p = record(v, \"params\");\n const sourceDecimals = !allowUnlisted || p.sourceDecimals === undefined ? undefined : num(p.sourceDecimals, \"params.sourceDecimals\", 0, 77);\n if (sourceDecimals !== undefined && !Number.isInteger(sourceDecimals)) bad(\"params.sourceDecimals\", \"expected an integer\");\n const slippage = p.slippageBps === undefined ? undefined : num(p.slippageBps, \"params.slippageBps\", 0, 10_000);\n if (slippage !== undefined && !Number.isInteger(slippage)) bad(\"params.slippageBps\", \"expected an integer\");\n const refundAddress = optStr(p.refundAddress, \"params.refundAddress\", 120);\n const minAmountOut = p.minAmountOut === undefined ? undefined : digits(p.minAmountOut, \"params.minAmountOut\", 60);\n const dry = bool(p.dry, \"params.dry\");\n const flex = bool(p.flex, \"params.flex\");\n return {\n sourceChain: str(p.sourceChain, \"params.sourceChain\", 64),\n sourceToken: str(p.sourceToken, \"params.sourceToken\", 64),\n ...(sourceDecimals !== undefined ? {sourceDecimals} : {}),\n destChain: str(p.destChain, \"params.destChain\", 64),\n destToken: str(p.destToken, \"params.destToken\", 120),\n amountIn: digits(p.amountIn, \"params.amountIn\", 40),\n destAddress: str(p.destAddress, \"params.destAddress\", 120),\n ...(refundAddress !== undefined ? {refundAddress} : {}),\n ...(minAmountOut !== undefined ? {minAmountOut} : {}),\n ...(slippage !== undefined ? {slippageBps: slippage} : {}),\n ...(dry !== undefined ? {dry} : {}),\n ...(flex !== undefined ? {flex} : {}),\n };\n}\n\nconst FEE_TYPES = new Set([\"network\", \"protocol\", \"affiliate\", \"other\"]);\n\n/** Keeps only the known Quote fields, each checked: what is sealed is exactly what is verified. */\nfunction normalizeQuote(v: unknown, intentMax: number): Quote {\n const q = record(v, \"quote\");\n const out: Quote = {\n provider: str(q.provider, \"quote.provider\", 40),\n intentId: str(q.intentId, \"quote.intentId\", intentMax),\n depositAddress: str(q.depositAddress, \"quote.depositAddress\", 200),\n depositMemo: q.depositMemo === null ? null : str(q.depositMemo, \"quote.depositMemo\", 400),\n sourceAmount: digits(q.sourceAmount, \"quote.sourceAmount\", 60),\n destAmount: digits(q.destAmount, \"quote.destAmount\", 60),\n minDestAmount: digits(q.minDestAmount, \"quote.minDestAmount\", 60),\n feeUsd: num(q.feeUsd, \"quote.feeUsd\"),\n etaSeconds: num(q.etaSeconds, \"quote.etaSeconds\"),\n reliability: num(q.reliability, \"quote.reliability\", 0, 1),\n validUntilMs: num(q.validUntilMs, \"quote.validUntilMs\"),\n };\n if (q.txRequest !== undefined) out.txRequest = txRequest(q.txRequest);\n if (q.fees !== undefined) {\n if (!Array.isArray(q.fees) || q.fees.length > 32) bad(\"quote.fees\", \"expected up to 32 entries\");\n out.fees = q.fees.map((f, i) => {\n const fee = record(f, `quote.fees[${i}]`);\n const type = str(fee.type, `quote.fees[${i}].type`, 20);\n if (!FEE_TYPES.has(type)) bad(`quote.fees[${i}].type`, \"unknown fee type\");\n const description = optStr(fee.description, `quote.fees[${i}].description`, 500);\n const included = bool(fee.included, `quote.fees[${i}].included`);\n if (included === undefined) bad(`quote.fees[${i}].included`, \"expected a boolean\");\n return {\n type: type as \"network\" | \"protocol\" | \"affiliate\" | \"other\",\n amount: digits(fee.amount, `quote.fees[${i}].amount`, 80),\n token: str(fee.token, `quote.fees[${i}].token`, 200),\n included,\n ...(description !== undefined ? {description} : {}),\n };\n });\n }\n if (q.providerData !== undefined) {\n const data = record(q.providerData, \"quote.providerData\");\n const keys = Object.keys(data);\n if (keys.length > 64) bad(\"quote.providerData\", \"more than 64 entries\");\n out.providerData = Object.fromEntries(keys.map((k) => [str(k, \"quote.providerData key\", 200), str(data[k], `quote.providerData.${k.slice(0, 40)}`, 10_000)]));\n }\n return out;\n}\n\nfunction txRequest(v: unknown): TxRequest {\n const t = record(v, \"quote.txRequest\");\n const out: TxRequest = {};\n if (t.to !== undefined) out.to = str(t.to, \"quote.txRequest.to\", 200);\n if (t.data !== undefined) out.data = str(t.data, \"quote.txRequest.data\", 1_000_000);\n if (t.value !== undefined) out.value = digits(t.value, \"quote.txRequest.value\", 80);\n if (t.chainId !== undefined) {\n const id = num(t.chainId, \"quote.txRequest.chainId\", 1);\n if (!Number.isInteger(id)) bad(\"quote.txRequest.chainId\", \"expected an integer\");\n out.chainId = id;\n }\n if (t.gasLimit !== undefined) out.gasLimit = digits(t.gasLimit, \"quote.txRequest.gasLimit\", 80);\n if (t.approvalAddress !== undefined) out.approvalAddress = str(t.approvalAddress, \"quote.txRequest.approvalAddress\", 200);\n if (t.serializedTx !== undefined) out.serializedTx = str(t.serializedTx, \"quote.txRequest.serializedTx\", 2_000_000);\n if (t.nativeFee !== undefined) out.nativeFee = digits(t.nativeFee, \"quote.txRequest.nativeFee\", 80);\n return out;\n}\n\n/** The transaction the client built from `build` (bigints arrive as decimal strings). */\nfunction unsignedTx(v: unknown): UnsignedTx {\n const t = record(v, \"tx\");\n const out: UnsignedTx = {chain: str(t.chain, \"tx.chain\", 64), to: str(t.to, \"tx.to\", 200), value: digits(t.value, \"tx.value\", 80)};\n const optional: [keyof UnsignedTx, number][] = [[\"data\", 1_000_000], [\"gasLimit\", 80], [\"approvalAddress\", 200], [\"serializedTx\", 2_000_000], [\"memo\", 2_000], [\"bodyBoc\", 200_000], [\"stateInitBoc\", 200_000], [\"typedData\", 200_000]];\n for (const [k, max] of optional) if (t[k] !== undefined) (out as unknown as Record<string, unknown>)[k] = k === \"gasLimit\" ? digits(t[k], `tx.${k}`, max) : str(t[k], `tx.${k}`, max);\n if (t.memoEncoding !== undefined) {\n if (t.memoEncoding !== \"utf8\" && t.memoEncoding !== \"hex\") bad(\"tx.memoEncoding\", \"expected utf8 or hex\");\n out.memoEncoding = t.memoEncoding;\n }\n if (t.chainId !== undefined) out.chainId = num(t.chainId, \"tx.chainId\", 1);\n return out;\n}\n\nfunction intentRef(v: unknown): IntentRef {\n const r = record(v, \"ref\");\n const nullable = (x: unknown, f: string) => (x === null || x === undefined ? undefined : str(x, f, 200));\n const sourceTxHash = nullable(r.sourceTxHash, \"ref.sourceTxHash\");\n const destTxHash = nullable(r.destTxHash, \"ref.destTxHash\");\n return {\n intentId: str(r.intentId, \"ref.intentId\", WIRE_INTENT_MAX),\n ...(sourceTxHash !== undefined ? {sourceTxHash} : {}),\n ...(destTxHash !== undefined ? {destTxHash} : {}),\n };\n}\n\nfunction asset(v: unknown, field: string): Asset {\n const a = record(v, field);\n return {chain: str(a.chain, `${field}.chain`, 64), token: str(a.token, `${field}.token`, 120)};\n}\n\n// ---- seal ------------------------------------------------------------------------------------\n\nfunction withoutSeal(quote: Quote): Quote {\n const {providerData, ...rest} = quote;\n if (!providerData) return rest;\n const {[SEAL_FIELD]: _seal, ...data} = providerData;\n return Object.keys(data).length ? {...rest, providerData: data} : rest;\n}\n/** The provider gets its quote back exactly as it produced it. */\nconst stripSeal = withoutSeal;\n\n/** Deterministic JSON: sorted keys, `undefined` dropped, non-finite numbers refused. */\nfunction canonical(value: unknown): string {\n if (value === null || typeof value === \"string\" || typeof value === \"boolean\") return JSON.stringify(value);\n if (typeof value === \"number\") {\n if (!Number.isFinite(value)) throw new Swap2HandlerError(400, \"quote contains a non-finite number\");\n return JSON.stringify(value);\n }\n if (Array.isArray(value)) return `[${value.map(canonical).join(\",\")}]`;\n if (typeof value === \"object\") {\n const r = value as Record<string, unknown>;\n return `{${Object.keys(r).filter((k) => r[k] !== undefined).sort().map((k) => `${JSON.stringify(k)}:${canonical(r[k])}`).join(\",\")}}`;\n }\n throw new Swap2HandlerError(400, \"quote is not JSON-safe\");\n}\n\nfunction toHex(bytes: Uint8Array): string {\n return Array.from(bytes, (b) => b.toString(16).padStart(2, \"0\")).join(\"\");\n}\nfunction fromHex(hex: string): Uint8Array {\n const out = new Uint8Array(hex.length / 2);\n for (let i = 0; i < out.length; i++) out[i] = parseInt(hex.slice(i * 2, i * 2 + 2), 16);\n return out;\n}\n","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","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`). The router\n * and the single-provider path drop such a quote with this reason, so the auction never picks a\n * route execution would refuse (scan 2026-10-27 modules, F8/F9/F16); execution checks the build\n * again, in full. Local providers and deposit-address routes are not judged here.\n */\nexport function remotePinRefusal(provider: Pick<CrossChainProvider, \"name\" | \"remote\" | \"callTargets\" | \"spenders\">, q: Quote, chain: string): string | null {\n if (provider.remote !== true || !chain.startsWith(\"eip155:\")) return null;\n const tx = q.txRequest;\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":[]}
|
package/dist/server.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
validQuote
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-WBYCNN6S.js";
|
|
4
4
|
import {
|
|
5
5
|
redactUrls
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-2KC734UX.js";
|
|
7
7
|
|
|
8
8
|
// src/server.ts
|
|
9
9
|
var Swap2HandlerError = class extends Error {
|
|
@@ -93,7 +93,7 @@ function createSwap2Handler(opts) {
|
|
|
93
93
|
const tokened = { ...normalized, intentId: `${normalized.intentId}~${await intentTag(providerName, subject, normalized.intentId)}` };
|
|
94
94
|
return ok(await seal(providerName, subject, tokened));
|
|
95
95
|
}
|
|
96
|
-
if (refusal) return ok({ noQuote:
|
|
96
|
+
if (refusal) return ok({ noQuote: clientText(String(refusal)) });
|
|
97
97
|
return ok(params.dry === false ? { noQuote: `${providerName} refused without naming a reason` } : null);
|
|
98
98
|
}
|
|
99
99
|
case "build":
|
|
@@ -171,13 +171,14 @@ async function refused(fn) {
|
|
|
171
171
|
return await fn();
|
|
172
172
|
} catch (err) {
|
|
173
173
|
if (err instanceof Error && /^[a-z0-9_-]+: \S/i.test(err.message)) {
|
|
174
|
-
throw new Swap2HandlerError(422,
|
|
174
|
+
throw new Swap2HandlerError(422, clientText(err.message));
|
|
175
175
|
}
|
|
176
176
|
throw err;
|
|
177
177
|
}
|
|
178
178
|
}
|
|
179
|
+
var clientText = (text) => redactUrls(text.slice(0, 2048)).slice(0, 300);
|
|
179
180
|
var ok = (body) => ({ status: 200, body: body === void 0 ? null : body });
|
|
180
|
-
var fail = (status, error) => ({ status, body: { error:
|
|
181
|
+
var fail = (status, error) => ({ status, body: { error: clientText(error) } });
|
|
181
182
|
var DIGITS = (max) => new RegExp(`^\\d{1,${max}}$`);
|
|
182
183
|
function bad(field, why) {
|
|
183
184
|
throw new Swap2HandlerError(400, `${field}: ${why}`);
|
package/dist/server.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/server.ts"],"sourcesContent":["import type {Asset, CrossChainProvider, IntentRef, Quote, QuoteParams, TxRequest, UnsignedTx} from \"./types.js\";\nimport {redactUrls} from \"./util.js\";\nimport {validQuote} from \"./validate.js\";\n\n/**\n * The server side of {@link remoteProvider}: runs real providers behind your backend so API\n * keys, affiliate settings and non-CORS endpoints never ship in the app.\n *\n * Framework-free: wire `handle(action, body, context)` into any HTTP route that receives\n * `POST {baseUrl}/{action}` with a JSON body, and send back `{status, body}` as JSON.\n *\n * What it guarantees:\n * - every field of every request is validated (types, lengths, digit-only amounts) before a\n * provider sees it; unknown actions, providers and malformed bodies are refused;\n * - `quote` answers are sealed with HMAC-SHA256 under `sealSecret`; `build` and `preflight`\n * refuse a quote that was not sealed here, was changed in transit, or has expired — so a\n * client cannot make your server build a deposit for terms the provider never quoted;\n * - every quote's `intentId` leaves with an intent token appended (`<id>~<tag>`): `track`,\n * `claim`, `notify` and `register` refuse an id without a valid one (403), and the provider\n * only ever sees its own id. With `subject`, quotes and tokens are bound to the caller, so\n * another user's sealed quote or swap id is refused too; without it the token is a bearer\n * capability for that one swap (share a transaction hash with support, not the swap id);\n * - a provider's named refusal travels in-band (`{noQuote}`) so the app can show WHY;\n * - error text crossing the boundary has URLs redacted (no upstream endpoints or keys).\n *\n * What it does NOT do, and you must: authenticate the caller and rate-limit per user/IP in\n * `guard` (see {@link Swap2HandlerOptions.guard}), cap the raw request size before parsing\n * (`serializedTx` may legitimately be ~2 MB), and log without request bodies.\n *\n * Server-only (WebCrypto HMAC; Node ≥ 20, Deno, Bun, edge runtimes). Never import it into an app.\n */\n\n/** One call, as `guard` sees it. `dry` quotes are display quotes: give them their own, larger bucket. */\nexport interface Swap2Call {\n action: Swap2Action;\n provider: string;\n dry: boolean;\n /**\n * The validated request as the provider will get it — amounts, recipients, the provider's own\n * intent id (token removed) — for limits that depend on what is asked. Not yet checked against\n * the seal or the intent token: those run after the guard.\n */\n request: Swap2Request;\n}\n\n/** The validated body of each action. */\nexport type Swap2Request =\n | {params: QuoteParams}\n | {quote: Quote; tx?: UnsignedTx}\n | {ref: IntentRef}\n | {intentId: string; txHash: string}\n | {intentId: string; signature: string}\n | {from: Asset; to: Asset};\n\nexport type Swap2Action = \"quote\" | \"build\" | \"preflight\" | \"track\" | \"claim\" | \"notify\" | \"register\" | \"supports\";\n\nexport interface Swap2HandlerOptions<C> {\n /** The real providers, by `name` (the same names the app's `remoteProvider`s use). */\n providers: readonly CrossChainProvider[];\n /** HMAC key for quote seals: at least 32 bytes of randomness, kept server-side, rotated with care. */\n sealSecret: string | Uint8Array;\n /**\n * Authentication and rate limiting — required, because this endpoint spends your provider\n * quotas and builds deposits. Throw {@link Swap2HandlerError} (401 / 403 / 429) to refuse.\n * `context` is whatever your route passes as the third argument (the request, a session…).\n */\n guard: (call: Swap2Call, context: C) => void | Promise<void>;\n /**\n * Who is calling: a stable id of the authenticated user — the Sign-In with Ethereum address,\n * not an email — or undefined. Quotes are sealed for, and intent tokens issued to, this subject:\n * another caller's quote or swap id is refused (403). Without it, the intent token in a swap\n * id is a bearer capability. Runs after `guard`.\n */\n subject?: (context: C) => string | undefined | Promise<string | undefined>;\n /**\n * Moving to this handler from another backend: until this time (ms since epoch), `track`\n * accepts a swap id without an intent token, so swaps started before the move can still be\n * followed. Read-only: `claim`, `notify` and `register` always require the token.\n */\n acceptUntokenedTrackUntil?: number;\n /**\n * Quote tokens the swap catalog does not list (`params.sourceToken` an EVM contract or Solana\n * mint, with `params.sourceDecimals`), for apps whose users add their own tokens. Off by default:\n * `sourceDecimals` is then dropped and the providers refuse an unlisted source. Destination tokens\n * are addresses either way.\n */\n allowUnlistedTokens?: boolean;\n /** Clock for expiry checks (tests). */\n now?: () => number;\n}\n\nexport interface Swap2Response {\n status: number;\n body: unknown;\n}\n\n/** Throw from `guard` (or anywhere) to answer with this status and message. */\nexport class Swap2HandlerError extends Error {\n readonly status: number;\n constructor(status: number, message: string) {\n super(message);\n this.name = \"Swap2HandlerError\";\n this.status = status;\n }\n}\n\nconst ACTIONS: ReadonlySet<string> = new Set([\"quote\", \"build\", \"preflight\", \"track\", \"claim\", \"notify\", \"register\", \"supports\"]);\nconst SEAL_FIELD = \"__remoteSeal\";\nconst SEAL_PREFIX = \"swap2-remote-quote:v1:\";\nconst INTENT_PREFIX = \"swap2-remote-intent:v1\";\n/** `<provider's intent id>~<128-bit HMAC tag>`. */\nconst TOKENED = /^([\\s\\S]+)~([0-9a-f]{32})$/;\nconst INTENT_MAX = 2000;\nconst WIRE_INTENT_MAX = INTENT_MAX + 33;\n\nexport function createSwap2Handler<C = unknown>(opts: Swap2HandlerOptions<C>): (action: string, body: unknown, context: C) => Promise<Swap2Response> {\n if (typeof opts.guard !== \"function\") throw new Error(\"swap2-server: guard is required (authentication + rate limiting)\");\n const secret = typeof opts.sealSecret === \"string\" ? new TextEncoder().encode(opts.sealSecret) : opts.sealSecret;\n if (!(secret instanceof Uint8Array) || secret.length < 32) throw new Error(\"swap2-server: sealSecret must be at least 32 bytes\");\n const byName = new Map<string, CrossChainProvider>();\n for (const p of opts.providers) {\n if (byName.has(p.name)) throw new Error(`swap2-server: duplicate provider '${p.name}'`);\n byName.set(p.name, p);\n }\n const now = opts.now ?? Date.now;\n const keyPromise = crypto.subtle.importKey(\"raw\", secret as BufferSource, {name: \"HMAC\", hash: \"SHA-256\"}, false, [\"sign\", \"verify\"]);\n\n // The subject is JSON-quoted, so it cannot run into the canonical quote that follows (\"{\").\n const sealInput = (provider: string, subject: string | undefined, quote: Quote) =>\n new TextEncoder().encode(SEAL_PREFIX + provider + \":\" + (subject === undefined ? \"\" : JSON.stringify(subject) + \":\") + canonical(withoutSeal(quote)));\n async function seal(provider: string, subject: string | undefined, quote: Quote): Promise<Quote> {\n const tag = new Uint8Array(await crypto.subtle.sign(\"HMAC\", await keyPromise, sealInput(provider, subject, quote)));\n return {...quote, providerData: {...(quote.providerData ?? {}), [SEAL_FIELD]: toHex(tag)}};\n }\n async function verify(provider: string, subject: string | undefined, quote: Quote): Promise<boolean> {\n if (quote.provider !== provider) return false;\n const sealHex = quote.providerData?.[SEAL_FIELD];\n if (!sealHex || !/^[0-9a-f]{64}$/.test(sealHex)) return false;\n return crypto.subtle.verify(\"HMAC\", await keyPromise, fromHex(sealHex) as BufferSource, sealInput(provider, subject, quote)); // constant-time\n }\n /** The intent token: HMAC over (provider, subject, the provider's intent id), domain-separated from the seal. */\n async function intentTag(provider: string, subject: string | undefined, intentId: string): Promise<string> {\n const data = new TextEncoder().encode(JSON.stringify([INTENT_PREFIX, provider, subject ?? null, intentId]));\n return toHex(new Uint8Array(await crypto.subtle.sign(\"HMAC\", await keyPromise, data)).subarray(0, 16));\n }\n /** A swap id from the app → the provider's own id, or 403. */\n async function untoken(provider: string, subject: string | undefined, wireId: string, action: Swap2Action): Promise<string> {\n const m = TOKENED.exec(wireId);\n if (!m) {\n if (action === \"track\" && opts.acceptUntokenedTrackUntil !== undefined && now() < opts.acceptUntokenedTrackUntil) return wireId;\n throw new Swap2HandlerError(403, \"swap id carries no intent token from this server\");\n }\n if (!sameHex(await intentTag(provider, subject, m[1]!), m[2]!)) {\n throw new Swap2HandlerError(403, subject === undefined ? \"swap id carries an invalid intent token\" : \"swap id was not issued to this caller\");\n }\n return m[1]!;\n }\n /** The quote as the provider issued it: no seal, its own intent id. */\n const forProvider = (quote: Quote): Quote => ({...stripSeal(quote), intentId: TOKENED.exec(quote.intentId)?.[1] ?? quote.intentId});\n\n return async function handle(action: string, body: unknown, context: C): Promise<Swap2Response> {\n try {\n if (!ACTIONS.has(action)) return fail(404, `unknown action '${String(action).slice(0, 40)}'`);\n const b = record(body, \"body\");\n const providerName = str(b.provider, \"provider\", 40);\n const act = action as Swap2Action;\n const request = validated(act, b, opts.allowUnlistedTokens === true);\n const dry = \"params\" in request && request.params.dry === true;\n // The guard sees the provider's own intent id, as the provider will.\n const shown: Swap2Request = \"ref\" in request ? {ref: {...request.ref, intentId: TOKENED.exec(request.ref.intentId)?.[1] ?? request.ref.intentId}}\n : \"quote\" in request ? {...request, quote: forProvider(request.quote)}\n : \"intentId\" in request ? {...request, intentId: TOKENED.exec(request.intentId)?.[1] ?? request.intentId}\n : request;\n await opts.guard({action: act, provider: providerName, dry, request: shown}, context);\n const subject = opts.subject ? await opts.subject(context) : undefined;\n\n const provider = byName.get(providerName);\n if (!provider) return fail(404, `unknown provider '${providerName}'`);\n if (!provider.enabled()) return fail(403, `provider '${providerName}' is disabled`);\n\n switch (act) {\n case \"quote\": {\n const {params} = request as {params: QuoteParams};\n let refusal: string | null = null; // per request: a shared field races under parallel quotes\n const quote = await provider.getQuote({...params, onRefusal: (reason) => { refusal = reason; }});\n if (quote) {\n let normalized: Quote;\n try { normalized = normalizeQuote(quote, INTENT_MAX); } catch { return fail(502, `${providerName} returned a quote this server refuses to seal`); }\n // A seal vouches for the quote: hold it to the engine's own checks (provider, input, floor,\n // slippage, expiry) so a client that does not re-validate cannot be handed a bad one.\n if (!validQuote(normalized, params, providerName) || normalized.validUntilMs <= now()) {\n return fail(502, `${providerName} returned a quote the swap engine would reject`);\n }\n const tokened = {...normalized, intentId: `${normalized.intentId}~${await intentTag(providerName, subject, normalized.intentId)}`};\n return ok(await seal(providerName, subject, tokened));\n }\n if (refusal) return ok({noQuote: redactUrls(String(refusal)).slice(0, 300)});\n return ok(params.dry === false ? {noQuote: `${providerName} refused without naming a reason`} : null);\n }\n case \"build\":\n case \"preflight\": {\n const {quote, tx} = request as {quote: Quote; tx?: UnsignedTx};\n if (!(await verify(providerName, subject, quote))) {\n return subject === undefined ? fail(400, \"quote integrity check failed\") : fail(403, \"quote was not sealed for this caller\");\n }\n if (quote.validUntilMs < now()) return fail(410, \"quote expired: ask for a new one\");\n if (act === \"build\") return ok(await refused(() => provider.buildDeposit(forProvider(quote))));\n if (!provider.preflightDeposit) return ok({});\n await refused(() => provider.preflightDeposit!(forProvider(quote), tx!));\n return ok({});\n }\n case \"track\":\n case \"claim\": {\n const {ref} = request as {ref: IntentRef};\n const own = {...ref, intentId: await untoken(providerName, subject, ref.intentId, act)};\n if (act === \"track\") return ok(await provider.trackIntent(own));\n if (!provider.buildClaim) return ok({});\n return ok(await refused(() => provider.buildClaim!(own)));\n }\n case \"notify\": {\n const {intentId, txHash} = request as {intentId: string; txHash: string};\n await provider.notifyDeposit?.(await untoken(providerName, subject, intentId, act), txHash);\n return ok({});\n }\n case \"register\": {\n if (!provider.registerSignedOrder) return fail(400, `provider '${providerName}' has no signed-order flow`);\n const {intentId, signature} = request as {intentId: string; signature: string};\n await provider.registerSignedOrder(await untoken(providerName, subject, intentId, act), signature);\n return ok({});\n }\n case \"supports\": {\n const {from, to} = request as {from: Asset; to: Asset};\n return ok({supported: provider.supportsRoute(from, to)});\n }\n }\n } catch (err) {\n if (err instanceof Swap2HandlerError) return fail(err.status, err.message);\n return fail(500, \"internal error\"); // never echo provider internals or upstream URLs\n }\n };\n}\n\n/** Each action's body, validated field by field before anyone (guard, provider) sees it. */\nfunction validated(action: Swap2Action, b: Record<string, unknown>, allowUnlisted: boolean): Swap2Request {\n switch (action) {\n case \"quote\": return {params: quoteParams(b.params, allowUnlisted)};\n case \"build\": return {quote: normalizeQuote(b.quote, WIRE_INTENT_MAX)};\n case \"preflight\": return {quote: normalizeQuote(b.quote, WIRE_INTENT_MAX), tx: unsignedTx(b.tx)};\n case \"track\":\n case \"claim\": return {ref: intentRef(b.ref)};\n case \"notify\": return {intentId: str(b.intentId, \"intentId\", WIRE_INTENT_MAX), txHash: str(b.txHash, \"txHash\", 200)};\n case \"register\": {\n const signature = str(b.signature, \"signature\", 302);\n if (!/^0x[0-9a-fA-F]{2,300}$/.test(signature)) throw new Swap2HandlerError(400, \"signature: not a hex signature\");\n return {intentId: str(b.intentId, \"intentId\", WIRE_INTENT_MAX), signature};\n }\n case \"supports\": return {from: asset(b.from, \"from\"), to: asset(b.to, \"to\")};\n }\n}\n\n/** Constant-time comparison of two lowercase hex strings. */\nfunction sameHex(a: string, b: string): boolean {\n if (a.length !== b.length) return false;\n let diff = 0;\n for (let i = 0; i < a.length; i++) diff |= a.charCodeAt(i) ^ b.charCodeAt(i);\n return diff === 0;\n}\n\n// ---- refusals -------------------------------------------------------------------------------\n\n/** A provider's own refusal (\"lifi: …\") is actionable: 422 with the redacted text. Anything else is a 500. */\nasync function refused<T>(fn: () => Promise<T>): Promise<T> {\n try {\n return await fn();\n } catch (err) {\n if (err instanceof Error && /^[a-z0-9_-]+: \\S/i.test(err.message)) {\n throw new Swap2HandlerError(422, redactUrls(err.message).slice(0, 300));\n }\n throw err;\n }\n}\n\nconst ok = (body: unknown): Swap2Response => ({status: 200, body: body === undefined ? null : body});\nconst fail = (status: number, error: string): Swap2Response => ({status, body: {error: redactUrls(error).slice(0, 300)}});\n\n// ---- validation (the limits mirror a production backend's schema) --------------------------\n\nconst DIGITS = (max: number) => new RegExp(`^\\\\d{1,${max}}$`);\n\nfunction bad(field: string, why: string): never {\n throw new Swap2HandlerError(400, `${field}: ${why}`);\n}\nfunction record(v: unknown, field: string): Record<string, unknown> {\n if (!v || typeof v !== \"object\" || Array.isArray(v)) bad(field, \"expected an object\");\n return v as Record<string, unknown>;\n}\nfunction str(v: unknown, field: string, max: number): string {\n if (typeof v !== \"string\") bad(field, \"expected a string\");\n if (v.length > max) bad(field, `longer than ${max}`);\n return v;\n}\nfunction optStr(v: unknown, field: string, max: number): string | undefined {\n return v === undefined ? undefined : str(v, field, max);\n}\nfunction digits(v: unknown, field: string, max: number): string {\n const s = str(v, field, max);\n if (!DIGITS(max).test(s)) bad(field, \"expected base units (digits only)\");\n return s;\n}\nfunction num(v: unknown, field: string, min = 0, max = Number.MAX_SAFE_INTEGER): number {\n if (typeof v !== \"number\" || !Number.isFinite(v) || v < min || v > max) bad(field, \"expected a finite number in range\");\n return v;\n}\nfunction bool(v: unknown, field: string): boolean | undefined {\n if (v === undefined) return undefined;\n if (typeof v !== \"boolean\") bad(field, \"expected a boolean\");\n return v;\n}\n\nfunction quoteParams(v: unknown, allowUnlisted = false): QuoteParams {\n const p = record(v, \"params\");\n const sourceDecimals = !allowUnlisted || p.sourceDecimals === undefined ? undefined : num(p.sourceDecimals, \"params.sourceDecimals\", 0, 77);\n if (sourceDecimals !== undefined && !Number.isInteger(sourceDecimals)) bad(\"params.sourceDecimals\", \"expected an integer\");\n const slippage = p.slippageBps === undefined ? undefined : num(p.slippageBps, \"params.slippageBps\", 0, 10_000);\n if (slippage !== undefined && !Number.isInteger(slippage)) bad(\"params.slippageBps\", \"expected an integer\");\n const refundAddress = optStr(p.refundAddress, \"params.refundAddress\", 120);\n const minAmountOut = p.minAmountOut === undefined ? undefined : digits(p.minAmountOut, \"params.minAmountOut\", 60);\n const dry = bool(p.dry, \"params.dry\");\n const flex = bool(p.flex, \"params.flex\");\n return {\n sourceChain: str(p.sourceChain, \"params.sourceChain\", 64),\n sourceToken: str(p.sourceToken, \"params.sourceToken\", 64),\n ...(sourceDecimals !== undefined ? {sourceDecimals} : {}),\n destChain: str(p.destChain, \"params.destChain\", 64),\n destToken: str(p.destToken, \"params.destToken\", 120),\n amountIn: digits(p.amountIn, \"params.amountIn\", 40),\n destAddress: str(p.destAddress, \"params.destAddress\", 120),\n ...(refundAddress !== undefined ? {refundAddress} : {}),\n ...(minAmountOut !== undefined ? {minAmountOut} : {}),\n ...(slippage !== undefined ? {slippageBps: slippage} : {}),\n ...(dry !== undefined ? {dry} : {}),\n ...(flex !== undefined ? {flex} : {}),\n };\n}\n\nconst FEE_TYPES = new Set([\"network\", \"protocol\", \"affiliate\", \"other\"]);\n\n/** Keeps only the known Quote fields, each checked: what is sealed is exactly what is verified. */\nfunction normalizeQuote(v: unknown, intentMax: number): Quote {\n const q = record(v, \"quote\");\n const out: Quote = {\n provider: str(q.provider, \"quote.provider\", 40),\n intentId: str(q.intentId, \"quote.intentId\", intentMax),\n depositAddress: str(q.depositAddress, \"quote.depositAddress\", 200),\n depositMemo: q.depositMemo === null ? null : str(q.depositMemo, \"quote.depositMemo\", 400),\n sourceAmount: digits(q.sourceAmount, \"quote.sourceAmount\", 60),\n destAmount: digits(q.destAmount, \"quote.destAmount\", 60),\n minDestAmount: digits(q.minDestAmount, \"quote.minDestAmount\", 60),\n feeUsd: num(q.feeUsd, \"quote.feeUsd\"),\n etaSeconds: num(q.etaSeconds, \"quote.etaSeconds\"),\n reliability: num(q.reliability, \"quote.reliability\", 0, 1),\n validUntilMs: num(q.validUntilMs, \"quote.validUntilMs\"),\n };\n if (q.txRequest !== undefined) out.txRequest = txRequest(q.txRequest);\n if (q.fees !== undefined) {\n if (!Array.isArray(q.fees) || q.fees.length > 32) bad(\"quote.fees\", \"expected up to 32 entries\");\n out.fees = q.fees.map((f, i) => {\n const fee = record(f, `quote.fees[${i}]`);\n const type = str(fee.type, `quote.fees[${i}].type`, 20);\n if (!FEE_TYPES.has(type)) bad(`quote.fees[${i}].type`, \"unknown fee type\");\n const description = optStr(fee.description, `quote.fees[${i}].description`, 500);\n const included = bool(fee.included, `quote.fees[${i}].included`);\n if (included === undefined) bad(`quote.fees[${i}].included`, \"expected a boolean\");\n return {\n type: type as \"network\" | \"protocol\" | \"affiliate\" | \"other\",\n amount: digits(fee.amount, `quote.fees[${i}].amount`, 80),\n token: str(fee.token, `quote.fees[${i}].token`, 200),\n included,\n ...(description !== undefined ? {description} : {}),\n };\n });\n }\n if (q.providerData !== undefined) {\n const data = record(q.providerData, \"quote.providerData\");\n const keys = Object.keys(data);\n if (keys.length > 64) bad(\"quote.providerData\", \"more than 64 entries\");\n out.providerData = Object.fromEntries(keys.map((k) => [str(k, \"quote.providerData key\", 200), str(data[k], `quote.providerData.${k.slice(0, 40)}`, 10_000)]));\n }\n return out;\n}\n\nfunction txRequest(v: unknown): TxRequest {\n const t = record(v, \"quote.txRequest\");\n const out: TxRequest = {};\n if (t.to !== undefined) out.to = str(t.to, \"quote.txRequest.to\", 200);\n if (t.data !== undefined) out.data = str(t.data, \"quote.txRequest.data\", 1_000_000);\n if (t.value !== undefined) out.value = digits(t.value, \"quote.txRequest.value\", 80);\n if (t.chainId !== undefined) {\n const id = num(t.chainId, \"quote.txRequest.chainId\", 1);\n if (!Number.isInteger(id)) bad(\"quote.txRequest.chainId\", \"expected an integer\");\n out.chainId = id;\n }\n if (t.gasLimit !== undefined) out.gasLimit = digits(t.gasLimit, \"quote.txRequest.gasLimit\", 80);\n if (t.approvalAddress !== undefined) out.approvalAddress = str(t.approvalAddress, \"quote.txRequest.approvalAddress\", 200);\n if (t.serializedTx !== undefined) out.serializedTx = str(t.serializedTx, \"quote.txRequest.serializedTx\", 2_000_000);\n if (t.nativeFee !== undefined) out.nativeFee = digits(t.nativeFee, \"quote.txRequest.nativeFee\", 80);\n return out;\n}\n\n/** The transaction the client built from `build` (bigints arrive as decimal strings). */\nfunction unsignedTx(v: unknown): UnsignedTx {\n const t = record(v, \"tx\");\n const out: UnsignedTx = {chain: str(t.chain, \"tx.chain\", 64), to: str(t.to, \"tx.to\", 200), value: digits(t.value, \"tx.value\", 80)};\n const optional: [keyof UnsignedTx, number][] = [[\"data\", 1_000_000], [\"gasLimit\", 80], [\"approvalAddress\", 200], [\"serializedTx\", 2_000_000], [\"memo\", 2_000], [\"bodyBoc\", 200_000], [\"stateInitBoc\", 200_000], [\"typedData\", 200_000]];\n for (const [k, max] of optional) if (t[k] !== undefined) (out as unknown as Record<string, unknown>)[k] = k === \"gasLimit\" ? digits(t[k], `tx.${k}`, max) : str(t[k], `tx.${k}`, max);\n if (t.memoEncoding !== undefined) {\n if (t.memoEncoding !== \"utf8\" && t.memoEncoding !== \"hex\") bad(\"tx.memoEncoding\", \"expected utf8 or hex\");\n out.memoEncoding = t.memoEncoding;\n }\n if (t.chainId !== undefined) out.chainId = num(t.chainId, \"tx.chainId\", 1);\n return out;\n}\n\nfunction intentRef(v: unknown): IntentRef {\n const r = record(v, \"ref\");\n const nullable = (x: unknown, f: string) => (x === null || x === undefined ? undefined : str(x, f, 200));\n const sourceTxHash = nullable(r.sourceTxHash, \"ref.sourceTxHash\");\n const destTxHash = nullable(r.destTxHash, \"ref.destTxHash\");\n return {\n intentId: str(r.intentId, \"ref.intentId\", WIRE_INTENT_MAX),\n ...(sourceTxHash !== undefined ? {sourceTxHash} : {}),\n ...(destTxHash !== undefined ? {destTxHash} : {}),\n };\n}\n\nfunction asset(v: unknown, field: string): Asset {\n const a = record(v, field);\n return {chain: str(a.chain, `${field}.chain`, 64), token: str(a.token, `${field}.token`, 120)};\n}\n\n// ---- seal ------------------------------------------------------------------------------------\n\nfunction withoutSeal(quote: Quote): Quote {\n const {providerData, ...rest} = quote;\n if (!providerData) return rest;\n const {[SEAL_FIELD]: _seal, ...data} = providerData;\n return Object.keys(data).length ? {...rest, providerData: data} : rest;\n}\n/** The provider gets its quote back exactly as it produced it. */\nconst stripSeal = withoutSeal;\n\n/** Deterministic JSON: sorted keys, `undefined` dropped, non-finite numbers refused. */\nfunction canonical(value: unknown): string {\n if (value === null || typeof value === \"string\" || typeof value === \"boolean\") return JSON.stringify(value);\n if (typeof value === \"number\") {\n if (!Number.isFinite(value)) throw new Swap2HandlerError(400, \"quote contains a non-finite number\");\n return JSON.stringify(value);\n }\n if (Array.isArray(value)) return `[${value.map(canonical).join(\",\")}]`;\n if (typeof value === \"object\") {\n const r = value as Record<string, unknown>;\n return `{${Object.keys(r).filter((k) => r[k] !== undefined).sort().map((k) => `${JSON.stringify(k)}:${canonical(r[k])}`).join(\",\")}}`;\n }\n throw new Swap2HandlerError(400, \"quote is not JSON-safe\");\n}\n\nfunction toHex(bytes: Uint8Array): string {\n return Array.from(bytes, (b) => b.toString(16).padStart(2, \"0\")).join(\"\");\n}\nfunction fromHex(hex: string): Uint8Array {\n const out = new Uint8Array(hex.length / 2);\n for (let i = 0; i < out.length; i++) out[i] = parseInt(hex.slice(i * 2, i * 2 + 2), 16);\n return out;\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/server.ts"],"sourcesContent":["import type {Asset, CrossChainProvider, IntentRef, Quote, QuoteParams, TxRequest, UnsignedTx} from \"./types.js\";\nimport {redactUrls} from \"./util.js\";\nimport {validQuote} from \"./validate.js\";\n\n/**\n * The server side of {@link remoteProvider}: runs real providers behind your backend so API\n * keys, affiliate settings and non-CORS endpoints never ship in the app.\n *\n * Framework-free: wire `handle(action, body, context)` into any HTTP route that receives\n * `POST {baseUrl}/{action}` with a JSON body, and send back `{status, body}` as JSON.\n *\n * What it guarantees:\n * - every field of every request is validated (types, lengths, digit-only amounts) before a\n * provider sees it; unknown actions, providers and malformed bodies are refused;\n * - `quote` answers are sealed with HMAC-SHA256 under `sealSecret`; `build` and `preflight`\n * refuse a quote that was not sealed here, was changed in transit, or has expired — so a\n * client cannot make your server build a deposit for terms the provider never quoted;\n * - every quote's `intentId` leaves with an intent token appended (`<id>~<tag>`): `track`,\n * `claim`, `notify` and `register` refuse an id without a valid one (403), and the provider\n * only ever sees its own id. With `subject`, quotes and tokens are bound to the caller, so\n * another user's sealed quote or swap id is refused too; without it the token is a bearer\n * capability for that one swap (share a transaction hash with support, not the swap id);\n * - a provider's named refusal travels in-band (`{noQuote}`) so the app can show WHY;\n * - error text crossing the boundary has URLs redacted (no upstream endpoints or keys).\n *\n * What it does NOT do, and you must: authenticate the caller and rate-limit per user/IP in\n * `guard` (see {@link Swap2HandlerOptions.guard}), cap the raw request size before parsing\n * (`serializedTx` may legitimately be ~2 MB), and log without request bodies.\n *\n * Server-only (WebCrypto HMAC; Node ≥ 20, Deno, Bun, edge runtimes). Never import it into an app.\n */\n\n/** One call, as `guard` sees it. `dry` quotes are display quotes: give them their own, larger bucket. */\nexport interface Swap2Call {\n action: Swap2Action;\n provider: string;\n dry: boolean;\n /**\n * The validated request as the provider will get it — amounts, recipients, the provider's own\n * intent id (token removed) — for limits that depend on what is asked. Not yet checked against\n * the seal or the intent token: those run after the guard.\n */\n request: Swap2Request;\n}\n\n/** The validated body of each action. */\nexport type Swap2Request =\n | {params: QuoteParams}\n | {quote: Quote; tx?: UnsignedTx}\n | {ref: IntentRef}\n | {intentId: string; txHash: string}\n | {intentId: string; signature: string}\n | {from: Asset; to: Asset};\n\nexport type Swap2Action = \"quote\" | \"build\" | \"preflight\" | \"track\" | \"claim\" | \"notify\" | \"register\" | \"supports\";\n\nexport interface Swap2HandlerOptions<C> {\n /** The real providers, by `name` (the same names the app's `remoteProvider`s use). */\n providers: readonly CrossChainProvider[];\n /** HMAC key for quote seals: at least 32 bytes of randomness, kept server-side, rotated with care. */\n sealSecret: string | Uint8Array;\n /**\n * Authentication and rate limiting — required, because this endpoint spends your provider\n * quotas and builds deposits. Throw {@link Swap2HandlerError} (401 / 403 / 429) to refuse.\n * `context` is whatever your route passes as the third argument (the request, a session…).\n */\n guard: (call: Swap2Call, context: C) => void | Promise<void>;\n /**\n * Who is calling: a stable id of the authenticated user — the Sign-In with Ethereum address,\n * not an email — or undefined. Quotes are sealed for, and intent tokens issued to, this subject:\n * another caller's quote or swap id is refused (403). Without it, the intent token in a swap\n * id is a bearer capability. Runs after `guard`.\n */\n subject?: (context: C) => string | undefined | Promise<string | undefined>;\n /**\n * Moving to this handler from another backend: until this time (ms since epoch), `track`\n * accepts a swap id without an intent token, so swaps started before the move can still be\n * followed. Read-only: `claim`, `notify` and `register` always require the token.\n */\n acceptUntokenedTrackUntil?: number;\n /**\n * Quote tokens the swap catalog does not list (`params.sourceToken` an EVM contract or Solana\n * mint, with `params.sourceDecimals`), for apps whose users add their own tokens. Off by default:\n * `sourceDecimals` is then dropped and the providers refuse an unlisted source. Destination tokens\n * are addresses either way.\n */\n allowUnlistedTokens?: boolean;\n /** Clock for expiry checks (tests). */\n now?: () => number;\n}\n\nexport interface Swap2Response {\n status: number;\n body: unknown;\n}\n\n/** Throw from `guard` (or anywhere) to answer with this status and message. */\nexport class Swap2HandlerError extends Error {\n readonly status: number;\n constructor(status: number, message: string) {\n super(message);\n this.name = \"Swap2HandlerError\";\n this.status = status;\n }\n}\n\nconst ACTIONS: ReadonlySet<string> = new Set([\"quote\", \"build\", \"preflight\", \"track\", \"claim\", \"notify\", \"register\", \"supports\"]);\nconst SEAL_FIELD = \"__remoteSeal\";\nconst SEAL_PREFIX = \"swap2-remote-quote:v1:\";\nconst INTENT_PREFIX = \"swap2-remote-intent:v1\";\n/** `<provider's intent id>~<128-bit HMAC tag>`. */\nconst TOKENED = /^([\\s\\S]+)~([0-9a-f]{32})$/;\nconst INTENT_MAX = 2000;\nconst WIRE_INTENT_MAX = INTENT_MAX + 33;\n\nexport function createSwap2Handler<C = unknown>(opts: Swap2HandlerOptions<C>): (action: string, body: unknown, context: C) => Promise<Swap2Response> {\n if (typeof opts.guard !== \"function\") throw new Error(\"swap2-server: guard is required (authentication + rate limiting)\");\n const secret = typeof opts.sealSecret === \"string\" ? new TextEncoder().encode(opts.sealSecret) : opts.sealSecret;\n if (!(secret instanceof Uint8Array) || secret.length < 32) throw new Error(\"swap2-server: sealSecret must be at least 32 bytes\");\n const byName = new Map<string, CrossChainProvider>();\n for (const p of opts.providers) {\n if (byName.has(p.name)) throw new Error(`swap2-server: duplicate provider '${p.name}'`);\n byName.set(p.name, p);\n }\n const now = opts.now ?? Date.now;\n const keyPromise = crypto.subtle.importKey(\"raw\", secret as BufferSource, {name: \"HMAC\", hash: \"SHA-256\"}, false, [\"sign\", \"verify\"]);\n\n // The subject is JSON-quoted, so it cannot run into the canonical quote that follows (\"{\").\n const sealInput = (provider: string, subject: string | undefined, quote: Quote) =>\n new TextEncoder().encode(SEAL_PREFIX + provider + \":\" + (subject === undefined ? \"\" : JSON.stringify(subject) + \":\") + canonical(withoutSeal(quote)));\n async function seal(provider: string, subject: string | undefined, quote: Quote): Promise<Quote> {\n const tag = new Uint8Array(await crypto.subtle.sign(\"HMAC\", await keyPromise, sealInput(provider, subject, quote)));\n return {...quote, providerData: {...(quote.providerData ?? {}), [SEAL_FIELD]: toHex(tag)}};\n }\n async function verify(provider: string, subject: string | undefined, quote: Quote): Promise<boolean> {\n if (quote.provider !== provider) return false;\n const sealHex = quote.providerData?.[SEAL_FIELD];\n if (!sealHex || !/^[0-9a-f]{64}$/.test(sealHex)) return false;\n return crypto.subtle.verify(\"HMAC\", await keyPromise, fromHex(sealHex) as BufferSource, sealInput(provider, subject, quote)); // constant-time\n }\n /** The intent token: HMAC over (provider, subject, the provider's intent id), domain-separated from the seal. */\n async function intentTag(provider: string, subject: string | undefined, intentId: string): Promise<string> {\n const data = new TextEncoder().encode(JSON.stringify([INTENT_PREFIX, provider, subject ?? null, intentId]));\n return toHex(new Uint8Array(await crypto.subtle.sign(\"HMAC\", await keyPromise, data)).subarray(0, 16));\n }\n /** A swap id from the app → the provider's own id, or 403. */\n async function untoken(provider: string, subject: string | undefined, wireId: string, action: Swap2Action): Promise<string> {\n const m = TOKENED.exec(wireId);\n if (!m) {\n if (action === \"track\" && opts.acceptUntokenedTrackUntil !== undefined && now() < opts.acceptUntokenedTrackUntil) return wireId;\n throw new Swap2HandlerError(403, \"swap id carries no intent token from this server\");\n }\n if (!sameHex(await intentTag(provider, subject, m[1]!), m[2]!)) {\n throw new Swap2HandlerError(403, subject === undefined ? \"swap id carries an invalid intent token\" : \"swap id was not issued to this caller\");\n }\n return m[1]!;\n }\n /** The quote as the provider issued it: no seal, its own intent id. */\n const forProvider = (quote: Quote): Quote => ({...stripSeal(quote), intentId: TOKENED.exec(quote.intentId)?.[1] ?? quote.intentId});\n\n return async function handle(action: string, body: unknown, context: C): Promise<Swap2Response> {\n try {\n if (!ACTIONS.has(action)) return fail(404, `unknown action '${String(action).slice(0, 40)}'`);\n const b = record(body, \"body\");\n const providerName = str(b.provider, \"provider\", 40);\n const act = action as Swap2Action;\n const request = validated(act, b, opts.allowUnlistedTokens === true);\n const dry = \"params\" in request && request.params.dry === true;\n // The guard sees the provider's own intent id, as the provider will.\n const shown: Swap2Request = \"ref\" in request ? {ref: {...request.ref, intentId: TOKENED.exec(request.ref.intentId)?.[1] ?? request.ref.intentId}}\n : \"quote\" in request ? {...request, quote: forProvider(request.quote)}\n : \"intentId\" in request ? {...request, intentId: TOKENED.exec(request.intentId)?.[1] ?? request.intentId}\n : request;\n await opts.guard({action: act, provider: providerName, dry, request: shown}, context);\n const subject = opts.subject ? await opts.subject(context) : undefined;\n\n const provider = byName.get(providerName);\n if (!provider) return fail(404, `unknown provider '${providerName}'`);\n if (!provider.enabled()) return fail(403, `provider '${providerName}' is disabled`);\n\n switch (act) {\n case \"quote\": {\n const {params} = request as {params: QuoteParams};\n let refusal: string | null = null; // per request: a shared field races under parallel quotes\n const quote = await provider.getQuote({...params, onRefusal: (reason) => { refusal = reason; }});\n if (quote) {\n let normalized: Quote;\n try { normalized = normalizeQuote(quote, INTENT_MAX); } catch { return fail(502, `${providerName} returned a quote this server refuses to seal`); }\n // A seal vouches for the quote: hold it to the engine's own checks (provider, input, floor,\n // slippage, expiry) so a client that does not re-validate cannot be handed a bad one.\n if (!validQuote(normalized, params, providerName) || normalized.validUntilMs <= now()) {\n return fail(502, `${providerName} returned a quote the swap engine would reject`);\n }\n const tokened = {...normalized, intentId: `${normalized.intentId}~${await intentTag(providerName, subject, normalized.intentId)}`};\n return ok(await seal(providerName, subject, tokened));\n }\n if (refusal) return ok({noQuote: clientText(String(refusal))});\n return ok(params.dry === false ? {noQuote: `${providerName} refused without naming a reason`} : null);\n }\n case \"build\":\n case \"preflight\": {\n const {quote, tx} = request as {quote: Quote; tx?: UnsignedTx};\n if (!(await verify(providerName, subject, quote))) {\n return subject === undefined ? fail(400, \"quote integrity check failed\") : fail(403, \"quote was not sealed for this caller\");\n }\n if (quote.validUntilMs < now()) return fail(410, \"quote expired: ask for a new one\");\n if (act === \"build\") return ok(await refused(() => provider.buildDeposit(forProvider(quote))));\n if (!provider.preflightDeposit) return ok({});\n await refused(() => provider.preflightDeposit!(forProvider(quote), tx!));\n return ok({});\n }\n case \"track\":\n case \"claim\": {\n const {ref} = request as {ref: IntentRef};\n const own = {...ref, intentId: await untoken(providerName, subject, ref.intentId, act)};\n if (act === \"track\") return ok(await provider.trackIntent(own));\n if (!provider.buildClaim) return ok({});\n return ok(await refused(() => provider.buildClaim!(own)));\n }\n case \"notify\": {\n const {intentId, txHash} = request as {intentId: string; txHash: string};\n await provider.notifyDeposit?.(await untoken(providerName, subject, intentId, act), txHash);\n return ok({});\n }\n case \"register\": {\n if (!provider.registerSignedOrder) return fail(400, `provider '${providerName}' has no signed-order flow`);\n const {intentId, signature} = request as {intentId: string; signature: string};\n await provider.registerSignedOrder(await untoken(providerName, subject, intentId, act), signature);\n return ok({});\n }\n case \"supports\": {\n const {from, to} = request as {from: Asset; to: Asset};\n return ok({supported: provider.supportsRoute(from, to)});\n }\n }\n } catch (err) {\n if (err instanceof Swap2HandlerError) return fail(err.status, err.message);\n return fail(500, \"internal error\"); // never echo provider internals or upstream URLs\n }\n };\n}\n\n/** Each action's body, validated field by field before anyone (guard, provider) sees it. */\nfunction validated(action: Swap2Action, b: Record<string, unknown>, allowUnlisted: boolean): Swap2Request {\n switch (action) {\n case \"quote\": return {params: quoteParams(b.params, allowUnlisted)};\n case \"build\": return {quote: normalizeQuote(b.quote, WIRE_INTENT_MAX)};\n case \"preflight\": return {quote: normalizeQuote(b.quote, WIRE_INTENT_MAX), tx: unsignedTx(b.tx)};\n case \"track\":\n case \"claim\": return {ref: intentRef(b.ref)};\n case \"notify\": return {intentId: str(b.intentId, \"intentId\", WIRE_INTENT_MAX), txHash: str(b.txHash, \"txHash\", 200)};\n case \"register\": {\n const signature = str(b.signature, \"signature\", 302);\n if (!/^0x[0-9a-fA-F]{2,300}$/.test(signature)) throw new Swap2HandlerError(400, \"signature: not a hex signature\");\n return {intentId: str(b.intentId, \"intentId\", WIRE_INTENT_MAX), signature};\n }\n case \"supports\": return {from: asset(b.from, \"from\"), to: asset(b.to, \"to\")};\n }\n}\n\n/** Constant-time comparison of two lowercase hex strings. */\nfunction sameHex(a: string, b: string): boolean {\n if (a.length !== b.length) return false;\n let diff = 0;\n for (let i = 0; i < a.length; i++) diff |= a.charCodeAt(i) ^ b.charCodeAt(i);\n return diff === 0;\n}\n\n// ---- refusals -------------------------------------------------------------------------------\n\n/** A provider's own refusal (\"lifi: …\") is actionable: 422 with the redacted text. Anything else is a 500. */\nasync function refused<T>(fn: () => Promise<T>): Promise<T> {\n try {\n return await fn();\n } catch (err) {\n if (err instanceof Error && /^[a-z0-9_-]+: \\S/i.test(err.message)) {\n throw new Swap2HandlerError(422, clientText(err.message));\n }\n throw err;\n }\n}\n\n/** What a client is shown of a provider's text: cut before the URL pass runs (scan 2026-10-27 modules, F34), no URLs, 300 characters. */\nconst clientText = (text: string): string => redactUrls(text.slice(0, 2_048)).slice(0, 300);\n\nconst ok = (body: unknown): Swap2Response => ({status: 200, body: body === undefined ? null : body});\nconst fail = (status: number, error: string): Swap2Response => ({status, body: {error: clientText(error)}});\n\n// ---- validation (the limits mirror a production backend's schema) --------------------------\n\nconst DIGITS = (max: number) => new RegExp(`^\\\\d{1,${max}}$`);\n\nfunction bad(field: string, why: string): never {\n throw new Swap2HandlerError(400, `${field}: ${why}`);\n}\nfunction record(v: unknown, field: string): Record<string, unknown> {\n if (!v || typeof v !== \"object\" || Array.isArray(v)) bad(field, \"expected an object\");\n return v as Record<string, unknown>;\n}\nfunction str(v: unknown, field: string, max: number): string {\n if (typeof v !== \"string\") bad(field, \"expected a string\");\n if (v.length > max) bad(field, `longer than ${max}`);\n return v;\n}\nfunction optStr(v: unknown, field: string, max: number): string | undefined {\n return v === undefined ? undefined : str(v, field, max);\n}\nfunction digits(v: unknown, field: string, max: number): string {\n const s = str(v, field, max);\n if (!DIGITS(max).test(s)) bad(field, \"expected base units (digits only)\");\n return s;\n}\nfunction num(v: unknown, field: string, min = 0, max = Number.MAX_SAFE_INTEGER): number {\n if (typeof v !== \"number\" || !Number.isFinite(v) || v < min || v > max) bad(field, \"expected a finite number in range\");\n return v;\n}\nfunction bool(v: unknown, field: string): boolean | undefined {\n if (v === undefined) return undefined;\n if (typeof v !== \"boolean\") bad(field, \"expected a boolean\");\n return v;\n}\n\nfunction quoteParams(v: unknown, allowUnlisted = false): QuoteParams {\n const p = record(v, \"params\");\n const sourceDecimals = !allowUnlisted || p.sourceDecimals === undefined ? undefined : num(p.sourceDecimals, \"params.sourceDecimals\", 0, 77);\n if (sourceDecimals !== undefined && !Number.isInteger(sourceDecimals)) bad(\"params.sourceDecimals\", \"expected an integer\");\n const slippage = p.slippageBps === undefined ? undefined : num(p.slippageBps, \"params.slippageBps\", 0, 10_000);\n if (slippage !== undefined && !Number.isInteger(slippage)) bad(\"params.slippageBps\", \"expected an integer\");\n const refundAddress = optStr(p.refundAddress, \"params.refundAddress\", 120);\n const minAmountOut = p.minAmountOut === undefined ? undefined : digits(p.minAmountOut, \"params.minAmountOut\", 60);\n const dry = bool(p.dry, \"params.dry\");\n const flex = bool(p.flex, \"params.flex\");\n return {\n sourceChain: str(p.sourceChain, \"params.sourceChain\", 64),\n sourceToken: str(p.sourceToken, \"params.sourceToken\", 64),\n ...(sourceDecimals !== undefined ? {sourceDecimals} : {}),\n destChain: str(p.destChain, \"params.destChain\", 64),\n destToken: str(p.destToken, \"params.destToken\", 120),\n amountIn: digits(p.amountIn, \"params.amountIn\", 40),\n destAddress: str(p.destAddress, \"params.destAddress\", 120),\n ...(refundAddress !== undefined ? {refundAddress} : {}),\n ...(minAmountOut !== undefined ? {minAmountOut} : {}),\n ...(slippage !== undefined ? {slippageBps: slippage} : {}),\n ...(dry !== undefined ? {dry} : {}),\n ...(flex !== undefined ? {flex} : {}),\n };\n}\n\nconst FEE_TYPES = new Set([\"network\", \"protocol\", \"affiliate\", \"other\"]);\n\n/** Keeps only the known Quote fields, each checked: what is sealed is exactly what is verified. */\nfunction normalizeQuote(v: unknown, intentMax: number): Quote {\n const q = record(v, \"quote\");\n const out: Quote = {\n provider: str(q.provider, \"quote.provider\", 40),\n intentId: str(q.intentId, \"quote.intentId\", intentMax),\n depositAddress: str(q.depositAddress, \"quote.depositAddress\", 200),\n depositMemo: q.depositMemo === null ? null : str(q.depositMemo, \"quote.depositMemo\", 400),\n sourceAmount: digits(q.sourceAmount, \"quote.sourceAmount\", 60),\n destAmount: digits(q.destAmount, \"quote.destAmount\", 60),\n minDestAmount: digits(q.minDestAmount, \"quote.minDestAmount\", 60),\n feeUsd: num(q.feeUsd, \"quote.feeUsd\"),\n etaSeconds: num(q.etaSeconds, \"quote.etaSeconds\"),\n reliability: num(q.reliability, \"quote.reliability\", 0, 1),\n validUntilMs: num(q.validUntilMs, \"quote.validUntilMs\"),\n };\n if (q.txRequest !== undefined) out.txRequest = txRequest(q.txRequest);\n if (q.fees !== undefined) {\n if (!Array.isArray(q.fees) || q.fees.length > 32) bad(\"quote.fees\", \"expected up to 32 entries\");\n out.fees = q.fees.map((f, i) => {\n const fee = record(f, `quote.fees[${i}]`);\n const type = str(fee.type, `quote.fees[${i}].type`, 20);\n if (!FEE_TYPES.has(type)) bad(`quote.fees[${i}].type`, \"unknown fee type\");\n const description = optStr(fee.description, `quote.fees[${i}].description`, 500);\n const included = bool(fee.included, `quote.fees[${i}].included`);\n if (included === undefined) bad(`quote.fees[${i}].included`, \"expected a boolean\");\n return {\n type: type as \"network\" | \"protocol\" | \"affiliate\" | \"other\",\n amount: digits(fee.amount, `quote.fees[${i}].amount`, 80),\n token: str(fee.token, `quote.fees[${i}].token`, 200),\n included,\n ...(description !== undefined ? {description} : {}),\n };\n });\n }\n if (q.providerData !== undefined) {\n const data = record(q.providerData, \"quote.providerData\");\n const keys = Object.keys(data);\n if (keys.length > 64) bad(\"quote.providerData\", \"more than 64 entries\");\n out.providerData = Object.fromEntries(keys.map((k) => [str(k, \"quote.providerData key\", 200), str(data[k], `quote.providerData.${k.slice(0, 40)}`, 10_000)]));\n }\n return out;\n}\n\nfunction txRequest(v: unknown): TxRequest {\n const t = record(v, \"quote.txRequest\");\n const out: TxRequest = {};\n if (t.to !== undefined) out.to = str(t.to, \"quote.txRequest.to\", 200);\n if (t.data !== undefined) out.data = str(t.data, \"quote.txRequest.data\", 1_000_000);\n if (t.value !== undefined) out.value = digits(t.value, \"quote.txRequest.value\", 80);\n if (t.chainId !== undefined) {\n const id = num(t.chainId, \"quote.txRequest.chainId\", 1);\n if (!Number.isInteger(id)) bad(\"quote.txRequest.chainId\", \"expected an integer\");\n out.chainId = id;\n }\n if (t.gasLimit !== undefined) out.gasLimit = digits(t.gasLimit, \"quote.txRequest.gasLimit\", 80);\n if (t.approvalAddress !== undefined) out.approvalAddress = str(t.approvalAddress, \"quote.txRequest.approvalAddress\", 200);\n if (t.serializedTx !== undefined) out.serializedTx = str(t.serializedTx, \"quote.txRequest.serializedTx\", 2_000_000);\n if (t.nativeFee !== undefined) out.nativeFee = digits(t.nativeFee, \"quote.txRequest.nativeFee\", 80);\n return out;\n}\n\n/** The transaction the client built from `build` (bigints arrive as decimal strings). */\nfunction unsignedTx(v: unknown): UnsignedTx {\n const t = record(v, \"tx\");\n const out: UnsignedTx = {chain: str(t.chain, \"tx.chain\", 64), to: str(t.to, \"tx.to\", 200), value: digits(t.value, \"tx.value\", 80)};\n const optional: [keyof UnsignedTx, number][] = [[\"data\", 1_000_000], [\"gasLimit\", 80], [\"approvalAddress\", 200], [\"serializedTx\", 2_000_000], [\"memo\", 2_000], [\"bodyBoc\", 200_000], [\"stateInitBoc\", 200_000], [\"typedData\", 200_000]];\n for (const [k, max] of optional) if (t[k] !== undefined) (out as unknown as Record<string, unknown>)[k] = k === \"gasLimit\" ? digits(t[k], `tx.${k}`, max) : str(t[k], `tx.${k}`, max);\n if (t.memoEncoding !== undefined) {\n if (t.memoEncoding !== \"utf8\" && t.memoEncoding !== \"hex\") bad(\"tx.memoEncoding\", \"expected utf8 or hex\");\n out.memoEncoding = t.memoEncoding;\n }\n if (t.chainId !== undefined) out.chainId = num(t.chainId, \"tx.chainId\", 1);\n return out;\n}\n\nfunction intentRef(v: unknown): IntentRef {\n const r = record(v, \"ref\");\n const nullable = (x: unknown, f: string) => (x === null || x === undefined ? undefined : str(x, f, 200));\n const sourceTxHash = nullable(r.sourceTxHash, \"ref.sourceTxHash\");\n const destTxHash = nullable(r.destTxHash, \"ref.destTxHash\");\n return {\n intentId: str(r.intentId, \"ref.intentId\", WIRE_INTENT_MAX),\n ...(sourceTxHash !== undefined ? {sourceTxHash} : {}),\n ...(destTxHash !== undefined ? {destTxHash} : {}),\n };\n}\n\nfunction asset(v: unknown, field: string): Asset {\n const a = record(v, field);\n return {chain: str(a.chain, `${field}.chain`, 64), token: str(a.token, `${field}.token`, 120)};\n}\n\n// ---- seal ------------------------------------------------------------------------------------\n\nfunction withoutSeal(quote: Quote): Quote {\n const {providerData, ...rest} = quote;\n if (!providerData) return rest;\n const {[SEAL_FIELD]: _seal, ...data} = providerData;\n return Object.keys(data).length ? {...rest, providerData: data} : rest;\n}\n/** The provider gets its quote back exactly as it produced it. */\nconst stripSeal = withoutSeal;\n\n/** Deterministic JSON: sorted keys, `undefined` dropped, non-finite numbers refused. */\nfunction canonical(value: unknown): string {\n if (value === null || typeof value === \"string\" || typeof value === \"boolean\") return JSON.stringify(value);\n if (typeof value === \"number\") {\n if (!Number.isFinite(value)) throw new Swap2HandlerError(400, \"quote contains a non-finite number\");\n return JSON.stringify(value);\n }\n if (Array.isArray(value)) return `[${value.map(canonical).join(\",\")}]`;\n if (typeof value === \"object\") {\n const r = value as Record<string, unknown>;\n return `{${Object.keys(r).filter((k) => r[k] !== undefined).sort().map((k) => `${JSON.stringify(k)}:${canonical(r[k])}`).join(\",\")}}`;\n }\n throw new Swap2HandlerError(400, \"quote is not JSON-safe\");\n}\n\nfunction toHex(bytes: Uint8Array): string {\n return Array.from(bytes, (b) => b.toString(16).padStart(2, \"0\")).join(\"\");\n}\nfunction fromHex(hex: string): Uint8Array {\n const out = new Uint8Array(hex.length / 2);\n for (let i = 0; i < out.length; i++) out[i] = parseInt(hex.slice(i * 2, i * 2 + 2), 16);\n return out;\n}\n"],"mappings":"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","names":[]}
|