@bosonprotocol/x402-client 0.1.0-alpha-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.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/errors.ts","../../../src/core-sdk-factory.ts","../../../src/token-auth/deadline.ts","../../../src/token-auth/erc3009.ts","../../../src/token-auth/permit.ts","../../../src/utils/crypto.ts","../../../src/token-auth/permit2.ts","../../../src/token-auth/index.ts"],"names":["encodeFunctionData","decodeFunctionResult","PERMIT2_ADDRESS"],"mappings":"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","file":"index.js","sourcesContent":["// Typed error classes for the x402-client. Each is a leaf `Error` subclass\n// with a stable `name` so callers can branch on `instanceof` or string-match\n// without parsing messages.\n\nexport class UnsupportedSchemeError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"UnsupportedSchemeError\";\n }\n}\n\nexport class UnsupportedTokenAuthError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"UnsupportedTokenAuthError\";\n }\n}\n\nexport class NoCompatibleActionError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"NoCompatibleActionError\";\n }\n}\n\nexport class FulfillmentValidationError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"FulfillmentValidationError\";\n }\n}\n\nexport class MaxAmountExceededError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"MaxAmountExceededError\";\n }\n}\n","// Construct (and cache) the `CoreSDK` instance the client signs through.\n//\n// The buyer never queries Boson's subgraph in MVP — only the EIP-712 meta-tx\n// signing path is invoked — but `CoreSDK`'s base constructor requires a\n// `subgraphUrl` string. Callers can configure real URLs per chain via\n// `subgraphUrls`, or omit it and rely on the placeholder sentinel below;\n// either way no HTTP call to that URL is made during signing.\n\nimport { CoreSDK } from \"@bosonprotocol/core-sdk\";\nimport type { Address } from \"viem\";\n\nimport { signerToWeb3LibAdapter } from \"./internal/web3lib-adapter.js\";\nimport type { Signer, X402bClientConfig } from \"./types.js\";\n\n// Sentinel used when the caller did not configure a real subgraph URL for\n// the chain. The signing path never reads from it; the value just needs to\n// be a non-empty string the constructor accepts.\nconst PLACEHOLDER_SUBGRAPH_URL = \"https://x402-client.placeholder.invalid/subgraph\";\n\nexport interface CoreSdkContext {\n coreSdk: CoreSDK;\n chainId: number;\n}\n\n/**\n * Parse a CAIP-2 EVM network identifier (e.g. `\"eip155:8453\"`) into its\n * numeric chain id. Throws if the network is not a well-formed `eip155:<N>`.\n */\nexport function parseChainId(network: string): number {\n const match = network.match(/^eip155:(\\d+)$/);\n if (!match) {\n throw new Error(\n `x402-client: unsupported network '${network}' (expected CAIP-2 'eip155:<chainId>')`,\n );\n }\n const chainId = Number(match[1]);\n if (!Number.isSafeInteger(chainId) || chainId <= 0) {\n throw new Error(\n `x402-client: unsupported network '${network}' (chainId must be a positive safe integer)`,\n );\n }\n return chainId;\n}\n\n/**\n * Lazy factory that returns a `CoreSDK` instance configured against the\n * escrow contract advertised in the network. Instances are cached per\n * `(chainId, escrowAddress)` so repeated calls on the same network — for\n * commit and subsequent post-commit actions on the same exchange — reuse a\n * single SDK.\n *\n * The SDK's `web3Lib.call(...)` is wired through `config.publicClients[chainId]`\n * when one is configured; this enables read-only RPC reads (e.g. the EIP-2612\n * Permit strategy's `nonces(owner)` lookup). Chains without a PublicClient\n * configured can still sign meta-transactions and ERC-3009 / Permit2\n * token-auths.\n */\nexport function createCoreSdkFactory(\n signer: Signer,\n config: Pick<X402bClientConfig, \"subgraphUrls\" | \"publicClients\">,\n) {\n const cache = new Map<string, CoreSDK>();\n\n return function buildCoreSdk(network: string, escrowAddress: Address): CoreSdkContext {\n const chainId = parseChainId(network);\n const cacheKey = `${chainId}:${escrowAddress.toLowerCase()}`;\n const cached = cache.get(cacheKey);\n if (cached) {\n return { coreSdk: cached, chainId };\n }\n\n const subgraphUrl = config.subgraphUrls?.[chainId] ?? PLACEHOLDER_SUBGRAPH_URL;\n const publicClient = config.publicClients?.[chainId];\n const web3Lib = signerToWeb3LibAdapter(signer, chainId, publicClient);\n\n const coreSdk = new CoreSDK({\n web3Lib,\n subgraphUrl,\n protocolDiamond: escrowAddress,\n chainId,\n });\n\n cache.set(cacheKey, coreSdk);\n return { coreSdk, chainId };\n };\n}\n","// Client-side deadline for token-auth signatures (ERC-3009 `validBefore`,\n// EIP-2612 `deadline`, Permit2 `deadline`).\n//\n// The facilitator validates strictly: `deadline − facilitatorNow ≤\n// requirements.maxTimeoutSeconds`. If the client signs at the boundary\n// (`floor(clientNow) + maxTimeoutSeconds`), any moment where the\n// verifier's wall-clock lags the signer's by ≥ 1 s pushes the gap\n// over `maxTimeoutSeconds` and the facilitator rejects with\n// BAD_TOKEN_AUTH_SIGNATURE. Docker Desktop's container clock drifting\n// behind the host clock makes this happen randomly in the e2e suite.\n//\n// We pull the deadline closer to `clientNow` by a small margin so the\n// facilitator's check tolerates that backward skew. 60 s comfortably\n// covers Docker drift and leaves a useful validity window even at the\n// spec's example `maxTimeoutSeconds = 300`.\n\nexport const TOKEN_AUTH_DEADLINE_SAFETY_MARGIN_SECONDS = 60;\n\nexport function computeTokenAuthDeadline(\n maxTimeoutSeconds: number,\n now: () => number = Date.now,\n): number {\n // Clamp so the margin can never make the deadline retroactive — if\n // the caller's `maxTimeoutSeconds < 60`, subtracting the full margin\n // would emit an already-expired signature.\n const timeoutSeconds = Math.max(1, Math.floor(maxTimeoutSeconds));\n const safetyMarginSeconds = Math.min(\n TOKEN_AUTH_DEADLINE_SAFETY_MARGIN_SECONDS,\n Math.max(0, timeoutSeconds - 1),\n );\n return Math.floor(now() / 1000) + timeoutSeconds - safetyMarginSeconds;\n}\n","// Build and sign the buyer's ERC-3009 `ReceiveWithAuthorization` for the\n// escrow contract via `CoreSDK.signReceiveWithErc3009Authorization`.\n//\n// The SDK generates a 32-byte random nonce internally, builds the EIP-712\n// typed-data against the token's domain (taken from `tokenDomainResolver`),\n// signs through the configured `Web3LibAdapter`, and returns a\n// `TransferAuthorization` with `{ strategy, data: { validAfter, validBefore,\n// nonce }, r, s, v, signature }`. We re-shape the result into the\n// `Erc3009AuthData` slot of the escrow payment payload.\n\nimport type { CoreSDK } from \"@bosonprotocol/core-sdk\";\nimport type {\n Erc3009AuthData,\n EscrowPaymentRequirements,\n} from \"@bosonprotocol/x402-core/schemes/escrow\";\nimport type { Address, Hex } from \"viem\";\n\nimport { parseChainId } from \"../core-sdk-factory.js\";\nimport type { TokenDomainResolver } from \"../types.js\";\n\nimport { computeTokenAuthDeadline } from \"./deadline.js\";\n\nexport interface SignErc3009Args {\n requirements: EscrowPaymentRequirements;\n buyer: Address;\n coreSdk: CoreSDK;\n tokenDomainResolver: TokenDomainResolver;\n /** Override the wall-clock; injected by tests for determinism. Defaults to `Date.now()`. */\n now?: () => number;\n}\n\n/**\n * Build the ERC-3009 typed-data, sign it via core-sdk, and shape the result\n * into the `Erc3009AuthData` slot of the escrow payment payload.\n */\nexport async function signErc3009({\n requirements,\n buyer,\n coreSdk,\n tokenDomainResolver,\n now = Date.now,\n}: SignErc3009Args): Promise<Erc3009AuthData> {\n const chainId = parseChainId(requirements.network);\n const domain = await tokenDomainResolver(requirements.asset as Address, chainId);\n\n const validAfter = 0;\n const validBefore = computeTokenAuthDeadline(requirements.maxTimeoutSeconds, now);\n\n const result = await coreSdk.signReceiveWithErc3009Authorization(\n requirements.asset,\n { name: domain.name, version: domain.version },\n requirements.amount,\n validAfter,\n validBefore,\n { spender: requirements.escrowAddress as `0x${string}` },\n );\n\n return {\n from: buyer,\n to: requirements.escrowAddress,\n value: requirements.amount,\n validAfter: Number(result.data.validAfter),\n validBefore: Number(result.data.validBefore),\n nonce: result.data.nonce as Hex,\n v: result.v,\n r: result.r as Hex,\n s: result.s as Hex,\n };\n}\n","// Build and sign the buyer's EIP-2612 `Permit` for the escrow contract via\n// `CoreSDK.signReceiveWithErc2612Permit`.\n//\n// The SDK fetches the token's `nonces(owner)` via `web3Lib.call(...)` before\n// signing — that path requires the client's `signerToWeb3LibAdapter` to have\n// been built with a configured `PublicClient`. Without one, the underlying\n// `eth_call` rejects with a clear \"no PublicClient configured\" error,\n// surfaced to the caller as a sign-time failure.\n//\n// The SDK returns `{ strategy: \"EIP2612\", data: { deadline }, r, s, v,\n// signature }` — note that the nonce *is not* part of `data`. The wire\n// format `PermitAuthData` carries the nonce because the facilitator's\n// verification path needs it to rebuild the digest the buyer signed (the\n// on-chain `permit()` call refetches the current nonce, but the signature\n// is locked to the value at sign time). We recover it from the SDK's\n// signed typed-data by paying a second on-chain read of `nonces(owner)`;\n// in practice the value the SDK signed against is the one we observe\n// (only an interleaved successful `permit()` between sign and read would\n// shift it, which would then have invalidated the signature anyway).\n\nimport type { CoreSDK } from \"@bosonprotocol/core-sdk\";\nimport type {\n EscrowPaymentRequirements,\n PermitAuthData,\n} from \"@bosonprotocol/x402-core/schemes/escrow\";\nimport {\n encodeFunctionData,\n decodeFunctionResult,\n type Address,\n type Hex,\n type PublicClient,\n} from \"viem\";\n\nimport { parseChainId } from \"../core-sdk-factory.js\";\nimport { UnsupportedTokenAuthError } from \"../errors.js\";\nimport type { TokenDomainResolver } from \"../types.js\";\n\nimport { computeTokenAuthDeadline } from \"./deadline.js\";\n\nconst NONCES_ABI = [\n {\n type: \"function\",\n name: \"nonces\",\n stateMutability: \"view\",\n inputs: [{ name: \"owner\", type: \"address\" }],\n outputs: [{ type: \"uint256\" }],\n },\n] as const;\n\nexport interface SignPermitArgs {\n requirements: EscrowPaymentRequirements;\n buyer: Address;\n coreSdk: CoreSDK;\n tokenDomainResolver: TokenDomainResolver;\n /** PublicClient for the chain — required to recover the nonce that the SDK signed against. */\n publicClient: PublicClient;\n /** Override the wall-clock; injected by tests for determinism. Defaults to `Date.now()`. */\n now?: () => number;\n}\n\n/**\n * Build the EIP-2612 Permit typed-data, sign it via core-sdk, look up the\n * token nonce the SDK signed against, and shape the result into the\n * `PermitAuthData` slot of the escrow payment payload.\n */\nexport async function signPermit({\n requirements,\n buyer,\n coreSdk,\n tokenDomainResolver,\n publicClient,\n now = Date.now,\n}: SignPermitArgs): Promise<PermitAuthData> {\n const chainId = parseChainId(requirements.network);\n const domain = await tokenDomainResolver(requirements.asset as Address, chainId);\n\n const noncePreSign = await readNonce(publicClient, requirements.asset as Address, buyer);\n const deadline = computeTokenAuthDeadline(requirements.maxTimeoutSeconds, now);\n\n const result = await coreSdk.signReceiveWithErc2612Permit(\n requirements.asset,\n { name: domain.name, version: domain.version },\n requirements.amount,\n deadline,\n { spender: requirements.escrowAddress as `0x${string}` },\n );\n\n const noncePostSign = await readNonce(publicClient, requirements.asset as Address, buyer);\n if (noncePreSign !== noncePostSign) {\n // A successful `permit()` for this owner landed on-chain between our\n // two reads, which means the SDK signed against either nonce. We can't\n // tell which without re-signing, so fail loudly.\n throw new UnsupportedTokenAuthError(\n `x402-client: token nonce shifted during permit signing (was ${noncePreSign}, now ${noncePostSign}); retry`,\n );\n }\n\n return {\n owner: buyer,\n spender: requirements.escrowAddress,\n value: requirements.amount,\n deadline,\n nonce: noncePreSign.toString(),\n v: result.v,\n r: result.r as Hex,\n s: result.s as Hex,\n };\n}\n\nasync function readNonce(\n publicClient: PublicClient,\n token: Address,\n owner: Address,\n): Promise<bigint> {\n const result = await publicClient.call({\n to: token as `0x${string}`,\n data: encodeFunctionData({\n abi: NONCES_ABI,\n functionName: \"nonces\",\n args: [owner as `0x${string}`],\n }),\n });\n if (!result.data) {\n throw new UnsupportedTokenAuthError(\n `x402-client: token ${token} did not return data for nonces(${owner}) — does it implement EIP-2612?`,\n );\n }\n return decodeFunctionResult({\n abi: NONCES_ABI,\n functionName: \"nonces\",\n data: result.data,\n });\n}\n","// Cross-platform random helpers.\n//\n// The client is framework-agnostic: it must run in browsers (where\n// `node:crypto` is unavailable) as well as Node. The Web Crypto API\n// (`globalThis.crypto.getRandomValues`) covers both — it's been a Node\n// global since 19 and is universally available in modern browsers.\n// The package's `engines.node` already requires Node ≥ 22, so we can\n// rely on the global without a `node:crypto` fallback.\n//\n// Centralizing the helpers here keeps `pre-commit.ts`, `post-commit.ts`,\n// and `token-auth/erc3009.ts` from each importing their own crypto\n// primitive and re-deriving the same byte → bigint / byte → hex\n// conversion (CLAUDE.md \"Move duplicated constants ... into a shared\n// file the moment they appear in two places\").\n\nimport type { Hex } from \"viem\";\n\n/** Fill a fresh `Uint8Array` of `size` bytes with cryptographically random bytes. */\nexport function randomBytes(size: number): Uint8Array {\n const bytes = new Uint8Array(size);\n if (typeof globalThis.crypto?.getRandomValues !== \"function\") {\n throw new Error(\n \"x402-client: globalThis.crypto.getRandomValues is unavailable — environment lacks the Web Crypto API\",\n );\n }\n globalThis.crypto.getRandomValues(bytes);\n return bytes;\n}\n\n/** 32-byte cryptographically random `0x`-prefixed hex string. */\nexport function randomBytes32(): Hex {\n const bytes = randomBytes(32);\n let hex = \"0x\";\n for (const b of bytes) {\n hex += b.toString(16).padStart(2, \"0\");\n }\n return hex as Hex;\n}\n\n/** Cryptographically random `uint256`. Used for protocol meta-tx replay-protection nonces. */\nexport function randomUint256(): bigint {\n const bytes = randomBytes(32);\n let n = 0n;\n for (const byte of bytes) {\n n = (n << 8n) | BigInt(byte);\n }\n return n;\n}\n","// Build and sign the buyer's Uniswap Permit2 `PermitTransferFrom` for the\n// escrow contract via `CoreSDK.signReceiveWithPermit2`.\n//\n// The SDK accepts an optional `permit2Nonce` override; if omitted it\n// generates a random uint256. We pass one explicitly via `randomUint256()`\n// so tests can stub the RNG and so the value never depends on the SDK's\n// internal nonce-generation surface. The Permit2 contract address comes\n// from the wire-format `requirements.onchainHints?.permit2` when present,\n// or falls back to the canonical CREATE2 vanity address re-exported from\n// `@bosonprotocol/x402-core/eip712/token-auth`.\n//\n// Result shape from core-sdk:\n// { strategy: \"Permit2\", data: { nonce, deadline }, r, s, v, signature }\n// — we lift it into the wire-format `Permit2AuthData`.\n\nimport type { CoreSDK } from \"@bosonprotocol/core-sdk\";\nimport { PERMIT2_ADDRESS } from \"@bosonprotocol/x402-core/eip712/token-auth\";\nimport type {\n EscrowPaymentRequirements,\n Permit2AuthData,\n} from \"@bosonprotocol/x402-core/schemes/escrow\";\nimport type { Address, Hex } from \"viem\";\n\nimport { randomUint256 } from \"../utils/crypto.js\";\n\nimport { computeTokenAuthDeadline } from \"./deadline.js\";\n\nexport interface SignPermit2Args {\n requirements: EscrowPaymentRequirements;\n buyer: Address;\n coreSdk: CoreSDK;\n /** Override the wall-clock; injected by tests for determinism. Defaults to `Date.now()`. */\n now?: () => number;\n}\n\n/**\n * Build the Permit2 `PermitTransferFrom` typed-data, sign it via core-sdk,\n * and shape the result into the `Permit2AuthData` slot of the escrow\n * payment payload.\n */\nexport async function signPermit2({\n requirements,\n buyer,\n coreSdk,\n now = Date.now,\n}: SignPermit2Args): Promise<Permit2AuthData> {\n const deadline = computeTokenAuthDeadline(requirements.maxTimeoutSeconds, now);\n const permit2Nonce = randomUint256();\n\n // `buyer` is exposed in the call site for symmetry with the other\n // strategies and to keep the function signature stable as we add\n // explicit-`user` overrides. Today the SDK derives `user` internally\n // from `coreSdk._web3Lib.getSignerAddress()`, which we route through the\n // configured `signer.getAddress()` in `signerToWeb3LibAdapter`.\n void buyer;\n\n const result = await coreSdk.signReceiveWithPermit2(\n requirements.asset,\n requirements.amount,\n deadline,\n {\n spender: requirements.escrowAddress as `0x${string}`,\n permit2Address: PERMIT2_ADDRESS,\n permit2Nonce,\n },\n );\n\n return {\n permitted: { token: requirements.asset, amount: requirements.amount },\n spender: requirements.escrowAddress,\n nonce: permit2Nonce.toString(),\n deadline,\n signature: result.signature as Hex,\n };\n}\n","// Token-authorization strategy dispatcher.\n//\n// Picks one of the buyer-side strategies (`erc3009`, `permit`, `permit2`)\n// from `requirements.tokenAuthStrategies`, signs the corresponding payload\n// via `CoreSDK`, and returns the wire-format `BosonTokenAuth` slot. The\n// `none` strategy is encoded by the absence of `tokenAuth` on the payload\n// — callers handle it outside this module.\n//\n// Preference order when the server advertises multiple strategies:\n// `erc3009` → `permit2` → `permit`. ERC-3009 is preferred for tokens that\n// support it (USDC, USDP, EUROC, etc.) because the receive-variant\n// eliminates relayer front-running. Permit2 is preferred over Permit for\n// tokens behind the canonical Permit2 contract because it carries a\n// caller-controllable nonce. Permit is the fallback.\n\nimport type { CoreSDK } from \"@bosonprotocol/core-sdk\";\nimport type {\n BosonTokenAuth,\n EscrowPaymentRequirements,\n TokenAuthStrategy,\n} from \"@bosonprotocol/x402-core/schemes/escrow\";\nimport type { Address, PublicClient } from \"viem\";\n\nimport { UnsupportedTokenAuthError } from \"../errors.js\";\nimport type { TokenDomainResolver } from \"../types.js\";\n\nimport { signErc3009 } from \"./erc3009.js\";\nimport { signPermit } from \"./permit.js\";\nimport { signPermit2 } from \"./permit2.js\";\n\nexport interface BuildTokenAuthArgs {\n requirements: EscrowPaymentRequirements;\n buyer: Address;\n coreSdk: CoreSDK;\n /** Required for ERC-3009 and EIP-2612 Permit; not used by Permit2. */\n tokenDomainResolver?: TokenDomainResolver;\n /** Optional PublicClient for the requirements' chain. Required for Permit. */\n publicClient?: PublicClient;\n now?: () => number;\n}\n\nexport interface BuiltTokenAuth {\n strategy: TokenAuthStrategy;\n tokenAuth: BosonTokenAuth;\n}\n\nconst STRATEGY_PREFERENCE: readonly TokenAuthStrategy[] = [\"erc3009\", \"permit2\", \"permit\"];\n\nfunction strategyIsAvailable(s: TokenAuthStrategy, args: BuildTokenAuthArgs): boolean {\n switch (s) {\n case \"erc3009\":\n return args.tokenDomainResolver !== undefined;\n case \"permit\":\n return args.tokenDomainResolver !== undefined && args.publicClient !== undefined;\n case \"permit2\":\n return true;\n case \"none\":\n return false;\n default: {\n const _exhaustive: never = s;\n void _exhaustive;\n return false;\n }\n }\n}\n\n/**\n * Pick the highest-preference strategy from `requirements.tokenAuthStrategies`,\n * sign the corresponding payload via core-sdk, and return the wire-format\n * `BosonTokenAuth` slot.\n *\n * The picker is capability-aware: a strategy is only chosen when the runtime\n * config has the prerequisites for it (e.g. `tokenDomainResolver` for\n * ERC-3009, both `tokenDomainResolver` and `publicClient` for Permit). This\n * lets a deployment without `tokenDomainResolver` still satisfy servers that\n * advertise `[\"erc3009\", \"permit2\"]` by falling back to Permit2.\n */\nexport async function buildAndSignTokenAuth(args: BuildTokenAuthArgs): Promise<BuiltTokenAuth> {\n const advertised = args.requirements.tokenAuthStrategies;\n const chosen = STRATEGY_PREFERENCE.find(\n (s) => advertised.includes(s) && strategyIsAvailable(s, args),\n );\n if (!chosen) {\n throw new UnsupportedTokenAuthError(\n `server advertises tokenAuthStrategies=[${advertised.join(\", \")}]; ` +\n `client supports [${STRATEGY_PREFERENCE.join(\", \")}] but none are usable ` +\n `with the current X402bClientConfig ` +\n `(ERC-3009 and Permit require tokenDomainResolver; Permit also requires ` +\n `a PublicClient for the requirements' chain via publicClients)`,\n );\n }\n\n switch (chosen) {\n case \"erc3009\": {\n if (!args.tokenDomainResolver) {\n throw new UnsupportedTokenAuthError(\n \"server advertises 'erc3009' but no tokenDomainResolver is configured — pass tokenDomainResolver in X402bClientConfig\",\n );\n }\n const data = await signErc3009({\n ...args,\n tokenDomainResolver: args.tokenDomainResolver,\n });\n return { strategy: \"erc3009\", tokenAuth: { kind: \"erc3009\", data } };\n }\n case \"permit\": {\n if (!args.tokenDomainResolver) {\n throw new UnsupportedTokenAuthError(\n \"server advertises 'permit' but no tokenDomainResolver is configured — pass tokenDomainResolver in X402bClientConfig\",\n );\n }\n if (!args.publicClient) {\n throw new UnsupportedTokenAuthError(\n \"server advertises 'permit' but no PublicClient is configured for this chain — pass publicClients in X402bClientConfig\",\n );\n }\n const data = await signPermit({\n ...args,\n tokenDomainResolver: args.tokenDomainResolver,\n publicClient: args.publicClient,\n });\n return { strategy: \"permit\", tokenAuth: { kind: \"permit\", data } };\n }\n case \"permit2\": {\n const data = await signPermit2(args);\n return { strategy: \"permit2\", tokenAuth: { kind: \"permit2\", data } };\n }\n case \"none\": {\n // Should never reach: `STRATEGY_PREFERENCE` doesn't include \"none\",\n // so a server advertising only \"none\" falls through to the error\n // above. Keep the case for exhaustiveness against\n // `TokenAuthStrategy`.\n throw new UnsupportedTokenAuthError(\n \"tokenAuthStrategy='none' has no client-side signing — callers handle it without invoking this dispatcher\",\n );\n }\n default: {\n const _exhaustive: never = chosen;\n throw new UnsupportedTokenAuthError(\n `unrecognised tokenAuthStrategy '${(_exhaustive as { toString(): string }).toString()}'`,\n );\n }\n }\n}\n"]}
@@ -0,0 +1,104 @@
1
+ import { ClientState } from '@bosonprotocol/x402-core/state-machine';
2
+ import { TokenEip712Domain } from '@bosonprotocol/x402-core/eip712/token-auth';
3
+ import { TokenAuthStrategy } from '@bosonprotocol/x402-core/schemes/escrow';
4
+ import { Address, TypedDataDomain, TypedDataParameter, Hex, PublicClient } from 'viem';
5
+
6
+ /**
7
+ * Decision the buyer's policy makes about the on-chain redemption phase, **independent
8
+ * of when the resource is delivered off-chain**.
9
+ *
10
+ * - `"auto"` — default. Prefers the deferred `boson-createOfferAndCommit`
11
+ * path when advertised; falls back to atomic commit+redeem.
12
+ * - `"commit-only"` — explicitly want the deferred path; buyer (or another agent)
13
+ * will redeem later.
14
+ * - `"commit-and-redeem"` — explicitly want atomic on-chain commit+redeem;
15
+ * requires the server to advertise
16
+ * `boson-createOfferCommitAndRedeem` on the server channel,
17
+ * otherwise throws `NoCompatibleActionError`.
18
+ */
19
+ type RedeemMode = "auto" | "commit-only" | "commit-and-redeem";
20
+ interface Policy {
21
+ redeemMode?: RedeemMode;
22
+ /** Atomic-units cap. If set, the client rejects requirements whose `amount` exceeds it. */
23
+ maxAmount?: string;
24
+ /**
25
+ * Force a specific token-auth strategy instead of letting the
26
+ * dispatcher pick from `STRATEGY_PREFERENCE`. The chosen value MUST
27
+ * be in the server's advertised `tokenAuthStrategies` set;
28
+ * otherwise `handle402` throws `UnsupportedTokenAuthError`.
29
+ *
30
+ * Use `"none"` when the buyer has already approved the escrow off-band
31
+ * (e.g. via a standing ERC-20 `approve`) and wants the payment to ride
32
+ * the protocol's `safeTransferFrom` fallback — no token-auth payload is
33
+ * sent. The dispatcher's normal preference order (`erc3009` →
34
+ * `permit2` → `permit`) skips `"none"`, so this override is the only
35
+ * way to opt in.
36
+ */
37
+ tokenAuthStrategy?: TokenAuthStrategy;
38
+ }
39
+ interface FulfillmentConfig {
40
+ /** `id` of the option the buyer wants from `requirements.fulfillment.options[]`. */
41
+ option: string;
42
+ /**
43
+ * Buyer-supplied data; validated against the chosen option's JSON Schema.
44
+ * Use `null` for schemaless options such as `inline`.
45
+ */
46
+ data: Record<string, unknown> | null;
47
+ }
48
+ /**
49
+ * Resolves the EIP-712 domain a given ERC-20 publishes for ERC-3009 /
50
+ * EIP-2612 signatures. Callers usually provide a small in-memory lookup
51
+ * table keyed by `(chainId, asset)`. Permit2 does not use the token's
52
+ * EIP-712 domain and can be signed without this resolver.
53
+ */
54
+ type TokenDomainResolver = (asset: Address, chainId: number) => Promise<TokenEip712Domain> | TokenEip712Domain;
55
+ /**
56
+ * Minimal wallet abstraction the client signs through. Matches the shape of
57
+ * viem's `Account.signTypedData` so a viem `LocalAccount` can be passed
58
+ * directly (its `signTypedData` and `address` already line up); a viem
59
+ * `WalletClient`-bound account or an external signer needs a 4-line
60
+ * inline wrapper of the same shape.
61
+ */
62
+ interface Signer {
63
+ getAddress(): Promise<Address>;
64
+ signTypedData(args: {
65
+ domain: TypedDataDomain;
66
+ types: Record<string, readonly TypedDataParameter[]>;
67
+ primaryType: string;
68
+ message: Record<string, unknown>;
69
+ }): Promise<Hex>;
70
+ }
71
+ interface X402bClientConfig {
72
+ signer: Signer;
73
+ /**
74
+ * Per-chain Boson subgraph URLs. `CoreSDK`'s base constructor requires one;
75
+ * for signing-only flows the URL may not actually be queried, but it must
76
+ * be present. Keyed by EIP-155 chain id (e.g. `8453` for Base mainnet).
77
+ */
78
+ subgraphUrls?: Record<number, string>;
79
+ /**
80
+ * Per-chain viem `PublicClient`s. Required for the EIP-2612 Permit
81
+ * token-auth strategy, which fetches the token's `nonces(owner)` before
82
+ * signing. If the buyer signs only on chains with no Permit support, this
83
+ * may be omitted; attempting to sign a Permit payload without a configured
84
+ * PublicClient throws a clear error. Keyed by EIP-155 chain id.
85
+ */
86
+ publicClients?: Record<number, PublicClient>;
87
+ tokenDomainResolver?: TokenDomainResolver;
88
+ policy?: Policy;
89
+ /** Default fulfillment selection. Required when `requirements.fulfillment.required` is true. */
90
+ fulfillment?: FulfillmentConfig;
91
+ }
92
+ /**
93
+ * Whatever the server returns in `X-PAYMENT-RESPONSE` (if anything) after a
94
+ * successful settle. Permissive — the server-side contract for this header
95
+ * isn't pinned yet; the client surfaces the raw payload and best-effort
96
+ * lifts `exchangeId` / `state` from common property paths.
97
+ */
98
+ interface ExchangeSummary {
99
+ raw?: unknown;
100
+ exchangeId?: string;
101
+ state?: ClientState;
102
+ }
103
+
104
+ export type { ExchangeSummary as E, FulfillmentConfig as F, Policy as P, RedeemMode as R, Signer as S, TokenDomainResolver as T, X402bClientConfig as X };
@@ -0,0 +1,359 @@
1
+ import { CoreSDK } from '@bosonprotocol/core-sdk';
2
+ import { encodeFunctionData, decodeFunctionResult } from 'viem';
3
+ import { PERMIT2_ADDRESS } from '@bosonprotocol/x402-core/eip712/token-auth';
4
+
5
+ // src/errors.ts
6
+ var UnsupportedSchemeError = class extends Error {
7
+ constructor(message) {
8
+ super(message);
9
+ this.name = "UnsupportedSchemeError";
10
+ }
11
+ };
12
+ var UnsupportedTokenAuthError = class extends Error {
13
+ constructor(message) {
14
+ super(message);
15
+ this.name = "UnsupportedTokenAuthError";
16
+ }
17
+ };
18
+ var NoCompatibleActionError = class extends Error {
19
+ constructor(message) {
20
+ super(message);
21
+ this.name = "NoCompatibleActionError";
22
+ }
23
+ };
24
+ var FulfillmentValidationError = class extends Error {
25
+ constructor(message) {
26
+ super(message);
27
+ this.name = "FulfillmentValidationError";
28
+ }
29
+ };
30
+ var MaxAmountExceededError = class extends Error {
31
+ constructor(message) {
32
+ super(message);
33
+ this.name = "MaxAmountExceededError";
34
+ }
35
+ };
36
+
37
+ // src/internal/web3lib-adapter.ts
38
+ function signerToWeb3LibAdapter(signer, chainId, publicClient) {
39
+ return {
40
+ uuid: "x402-client:signer-adapter",
41
+ getSignerAddress: () => signer.getAddress(),
42
+ isSignerContract: async () => false,
43
+ getChainId: async () => chainId,
44
+ send: async (method, params) => {
45
+ if (method !== "eth_signTypedData_v4") {
46
+ throw new Error(
47
+ `x402-client: signer adapter does not support RPC method '${method}'; only eth_signTypedData_v4 is implemented`
48
+ );
49
+ }
50
+ const raw = params[1];
51
+ if (typeof raw !== "string") {
52
+ throw new Error(
53
+ "x402-client: eth_signTypedData_v4 payload[1] is not a JSON string \u2014 core-sdk internals may have changed"
54
+ );
55
+ }
56
+ const typedData = JSON.parse(raw);
57
+ return signer.signTypedData(typedData);
58
+ },
59
+ call: async (req) => {
60
+ if (!publicClient) {
61
+ throw new Error(
62
+ "x402-client: signer adapter has no PublicClient configured; eth_call cannot be forwarded. Pass `publicClients` in X402bClientConfig to enable the Permit token-auth strategy."
63
+ );
64
+ }
65
+ const result = await publicClient.call({
66
+ to: req.to,
67
+ data: req.data
68
+ });
69
+ return result.data ?? "0x";
70
+ },
71
+ getBalance: () => Promise.reject(unreachable("getBalance")),
72
+ estimateGas: () => Promise.reject(unreachable("estimateGas")),
73
+ sendTransaction: () => Promise.reject(unreachable("sendTransaction")),
74
+ getTransactionReceipt: () => Promise.reject(unreachable("getTransactionReceipt")),
75
+ getCurrentTimeMs: () => Promise.reject(unreachable("getCurrentTimeMs"))
76
+ };
77
+ }
78
+ function unreachable(method) {
79
+ return new Error(
80
+ `x402-client: stub Web3LibAdapter.${method}() is not implemented \u2014 the client never goes on-chain in MVP`
81
+ );
82
+ }
83
+
84
+ // src/core-sdk-factory.ts
85
+ var PLACEHOLDER_SUBGRAPH_URL = "https://x402-client.placeholder.invalid/subgraph";
86
+ function parseChainId(network) {
87
+ const match = network.match(/^eip155:(\d+)$/);
88
+ if (!match) {
89
+ throw new Error(
90
+ `x402-client: unsupported network '${network}' (expected CAIP-2 'eip155:<chainId>')`
91
+ );
92
+ }
93
+ const chainId = Number(match[1]);
94
+ if (!Number.isSafeInteger(chainId) || chainId <= 0) {
95
+ throw new Error(
96
+ `x402-client: unsupported network '${network}' (chainId must be a positive safe integer)`
97
+ );
98
+ }
99
+ return chainId;
100
+ }
101
+ function createCoreSdkFactory(signer, config) {
102
+ const cache = /* @__PURE__ */ new Map();
103
+ return function buildCoreSdk(network, escrowAddress) {
104
+ const chainId = parseChainId(network);
105
+ const cacheKey = `${chainId}:${escrowAddress.toLowerCase()}`;
106
+ const cached = cache.get(cacheKey);
107
+ if (cached) {
108
+ return { coreSdk: cached, chainId };
109
+ }
110
+ const subgraphUrl = config.subgraphUrls?.[chainId] ?? PLACEHOLDER_SUBGRAPH_URL;
111
+ const publicClient = config.publicClients?.[chainId];
112
+ const web3Lib = signerToWeb3LibAdapter(signer, chainId, publicClient);
113
+ const coreSdk = new CoreSDK({
114
+ web3Lib,
115
+ subgraphUrl,
116
+ protocolDiamond: escrowAddress,
117
+ chainId
118
+ });
119
+ cache.set(cacheKey, coreSdk);
120
+ return { coreSdk, chainId };
121
+ };
122
+ }
123
+
124
+ // src/token-auth/deadline.ts
125
+ var TOKEN_AUTH_DEADLINE_SAFETY_MARGIN_SECONDS = 60;
126
+ function computeTokenAuthDeadline(maxTimeoutSeconds, now = Date.now) {
127
+ const timeoutSeconds = Math.max(1, Math.floor(maxTimeoutSeconds));
128
+ const safetyMarginSeconds = Math.min(
129
+ TOKEN_AUTH_DEADLINE_SAFETY_MARGIN_SECONDS,
130
+ Math.max(0, timeoutSeconds - 1)
131
+ );
132
+ return Math.floor(now() / 1e3) + timeoutSeconds - safetyMarginSeconds;
133
+ }
134
+
135
+ // src/token-auth/erc3009.ts
136
+ async function signErc3009({
137
+ requirements,
138
+ buyer,
139
+ coreSdk,
140
+ tokenDomainResolver,
141
+ now = Date.now
142
+ }) {
143
+ const chainId = parseChainId(requirements.network);
144
+ const domain = await tokenDomainResolver(requirements.asset, chainId);
145
+ const validAfter = 0;
146
+ const validBefore = computeTokenAuthDeadline(requirements.maxTimeoutSeconds, now);
147
+ const result = await coreSdk.signReceiveWithErc3009Authorization(
148
+ requirements.asset,
149
+ { name: domain.name, version: domain.version },
150
+ requirements.amount,
151
+ validAfter,
152
+ validBefore,
153
+ { spender: requirements.escrowAddress }
154
+ );
155
+ return {
156
+ from: buyer,
157
+ to: requirements.escrowAddress,
158
+ value: requirements.amount,
159
+ validAfter: Number(result.data.validAfter),
160
+ validBefore: Number(result.data.validBefore),
161
+ nonce: result.data.nonce,
162
+ v: result.v,
163
+ r: result.r,
164
+ s: result.s
165
+ };
166
+ }
167
+ var NONCES_ABI = [
168
+ {
169
+ type: "function",
170
+ name: "nonces",
171
+ stateMutability: "view",
172
+ inputs: [{ name: "owner", type: "address" }],
173
+ outputs: [{ type: "uint256" }]
174
+ }
175
+ ];
176
+ async function signPermit({
177
+ requirements,
178
+ buyer,
179
+ coreSdk,
180
+ tokenDomainResolver,
181
+ publicClient,
182
+ now = Date.now
183
+ }) {
184
+ const chainId = parseChainId(requirements.network);
185
+ const domain = await tokenDomainResolver(requirements.asset, chainId);
186
+ const noncePreSign = await readNonce(publicClient, requirements.asset, buyer);
187
+ const deadline = computeTokenAuthDeadline(requirements.maxTimeoutSeconds, now);
188
+ const result = await coreSdk.signReceiveWithErc2612Permit(
189
+ requirements.asset,
190
+ { name: domain.name, version: domain.version },
191
+ requirements.amount,
192
+ deadline,
193
+ { spender: requirements.escrowAddress }
194
+ );
195
+ const noncePostSign = await readNonce(publicClient, requirements.asset, buyer);
196
+ if (noncePreSign !== noncePostSign) {
197
+ throw new UnsupportedTokenAuthError(
198
+ `x402-client: token nonce shifted during permit signing (was ${noncePreSign}, now ${noncePostSign}); retry`
199
+ );
200
+ }
201
+ return {
202
+ owner: buyer,
203
+ spender: requirements.escrowAddress,
204
+ value: requirements.amount,
205
+ deadline,
206
+ nonce: noncePreSign.toString(),
207
+ v: result.v,
208
+ r: result.r,
209
+ s: result.s
210
+ };
211
+ }
212
+ async function readNonce(publicClient, token, owner) {
213
+ const result = await publicClient.call({
214
+ to: token,
215
+ data: encodeFunctionData({
216
+ abi: NONCES_ABI,
217
+ functionName: "nonces",
218
+ args: [owner]
219
+ })
220
+ });
221
+ if (!result.data) {
222
+ throw new UnsupportedTokenAuthError(
223
+ `x402-client: token ${token} did not return data for nonces(${owner}) \u2014 does it implement EIP-2612?`
224
+ );
225
+ }
226
+ return decodeFunctionResult({
227
+ abi: NONCES_ABI,
228
+ functionName: "nonces",
229
+ data: result.data
230
+ });
231
+ }
232
+
233
+ // src/utils/crypto.ts
234
+ function randomBytes(size) {
235
+ const bytes = new Uint8Array(size);
236
+ if (typeof globalThis.crypto?.getRandomValues !== "function") {
237
+ throw new Error(
238
+ "x402-client: globalThis.crypto.getRandomValues is unavailable \u2014 environment lacks the Web Crypto API"
239
+ );
240
+ }
241
+ globalThis.crypto.getRandomValues(bytes);
242
+ return bytes;
243
+ }
244
+ function randomUint256() {
245
+ const bytes = randomBytes(32);
246
+ let n = 0n;
247
+ for (const byte of bytes) {
248
+ n = n << 8n | BigInt(byte);
249
+ }
250
+ return n;
251
+ }
252
+
253
+ // src/token-auth/permit2.ts
254
+ async function signPermit2({
255
+ requirements,
256
+ buyer,
257
+ coreSdk,
258
+ now = Date.now
259
+ }) {
260
+ const deadline = computeTokenAuthDeadline(requirements.maxTimeoutSeconds, now);
261
+ const permit2Nonce = randomUint256();
262
+ const result = await coreSdk.signReceiveWithPermit2(
263
+ requirements.asset,
264
+ requirements.amount,
265
+ deadline,
266
+ {
267
+ spender: requirements.escrowAddress,
268
+ permit2Address: PERMIT2_ADDRESS,
269
+ permit2Nonce
270
+ }
271
+ );
272
+ return {
273
+ permitted: { token: requirements.asset, amount: requirements.amount },
274
+ spender: requirements.escrowAddress,
275
+ nonce: permit2Nonce.toString(),
276
+ deadline,
277
+ signature: result.signature
278
+ };
279
+ }
280
+
281
+ // src/token-auth/index.ts
282
+ var STRATEGY_PREFERENCE = ["erc3009", "permit2", "permit"];
283
+ function strategyIsAvailable(s, args) {
284
+ switch (s) {
285
+ case "erc3009":
286
+ return args.tokenDomainResolver !== void 0;
287
+ case "permit":
288
+ return args.tokenDomainResolver !== void 0 && args.publicClient !== void 0;
289
+ case "permit2":
290
+ return true;
291
+ case "none":
292
+ return false;
293
+ default: {
294
+ return false;
295
+ }
296
+ }
297
+ }
298
+ async function buildAndSignTokenAuth(args) {
299
+ const advertised = args.requirements.tokenAuthStrategies;
300
+ const chosen = STRATEGY_PREFERENCE.find(
301
+ (s) => advertised.includes(s) && strategyIsAvailable(s, args)
302
+ );
303
+ if (!chosen) {
304
+ throw new UnsupportedTokenAuthError(
305
+ `server advertises tokenAuthStrategies=[${advertised.join(", ")}]; client supports [${STRATEGY_PREFERENCE.join(", ")}] but none are usable with the current X402bClientConfig (ERC-3009 and Permit require tokenDomainResolver; Permit also requires a PublicClient for the requirements' chain via publicClients)`
306
+ );
307
+ }
308
+ switch (chosen) {
309
+ case "erc3009": {
310
+ if (!args.tokenDomainResolver) {
311
+ throw new UnsupportedTokenAuthError(
312
+ "server advertises 'erc3009' but no tokenDomainResolver is configured \u2014 pass tokenDomainResolver in X402bClientConfig"
313
+ );
314
+ }
315
+ const data = await signErc3009({
316
+ ...args,
317
+ tokenDomainResolver: args.tokenDomainResolver
318
+ });
319
+ return { strategy: "erc3009", tokenAuth: { kind: "erc3009", data } };
320
+ }
321
+ case "permit": {
322
+ if (!args.tokenDomainResolver) {
323
+ throw new UnsupportedTokenAuthError(
324
+ "server advertises 'permit' but no tokenDomainResolver is configured \u2014 pass tokenDomainResolver in X402bClientConfig"
325
+ );
326
+ }
327
+ if (!args.publicClient) {
328
+ throw new UnsupportedTokenAuthError(
329
+ "server advertises 'permit' but no PublicClient is configured for this chain \u2014 pass publicClients in X402bClientConfig"
330
+ );
331
+ }
332
+ const data = await signPermit({
333
+ ...args,
334
+ tokenDomainResolver: args.tokenDomainResolver,
335
+ publicClient: args.publicClient
336
+ });
337
+ return { strategy: "permit", tokenAuth: { kind: "permit", data } };
338
+ }
339
+ case "permit2": {
340
+ const data = await signPermit2(args);
341
+ return { strategy: "permit2", tokenAuth: { kind: "permit2", data } };
342
+ }
343
+ case "none": {
344
+ throw new UnsupportedTokenAuthError(
345
+ "tokenAuthStrategy='none' has no client-side signing \u2014 callers handle it without invoking this dispatcher"
346
+ );
347
+ }
348
+ default: {
349
+ const _exhaustive = chosen;
350
+ throw new UnsupportedTokenAuthError(
351
+ `unrecognised tokenAuthStrategy '${_exhaustive.toString()}'`
352
+ );
353
+ }
354
+ }
355
+ }
356
+
357
+ export { FulfillmentValidationError, MaxAmountExceededError, NoCompatibleActionError, UnsupportedSchemeError, UnsupportedTokenAuthError, buildAndSignTokenAuth, createCoreSdkFactory, parseChainId, randomUint256 };
358
+ //# sourceMappingURL=chunk-JTTWAQGC.js.map
359
+ //# sourceMappingURL=chunk-JTTWAQGC.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/errors.ts","../../src/internal/web3lib-adapter.ts","../../src/core-sdk-factory.ts","../../src/token-auth/deadline.ts","../../src/token-auth/erc3009.ts","../../src/token-auth/permit.ts","../../src/utils/crypto.ts","../../src/token-auth/permit2.ts","../../src/token-auth/index.ts"],"names":[],"mappings":"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","file":"chunk-JTTWAQGC.js","sourcesContent":["// Typed error classes for the x402-client. Each is a leaf `Error` subclass\n// with a stable `name` so callers can branch on `instanceof` or string-match\n// without parsing messages.\n\nexport class UnsupportedSchemeError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"UnsupportedSchemeError\";\n }\n}\n\nexport class UnsupportedTokenAuthError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"UnsupportedTokenAuthError\";\n }\n}\n\nexport class NoCompatibleActionError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"NoCompatibleActionError\";\n }\n}\n\nexport class FulfillmentValidationError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"FulfillmentValidationError\";\n }\n}\n\nexport class MaxAmountExceededError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"MaxAmountExceededError\";\n }\n}\n","// Translator that wraps a `Signer` (plus an optional viem `PublicClient`\n// for read-only RPC calls) into a `Web3LibAdapter` that `CoreSDK` can be\n// constructed against.\n//\n// Two RPC methods are forwarded:\n//\n// - `send(\"eth_signTypedData_v4\", ...)` — buyer signs via `signer.signTypedData`.\n// - `call({ to, data })` — read-only `eth_call`, forwarded to the\n// `PublicClient` when one is configured. Required by the EIP-2612\n// Permit token-auth path, which fetches the token's `nonces(owner)`\n// before signing. Throws a clear error when invoked without a\n// PublicClient so the configuration mistake surfaces fast.\n//\n// Transaction-flavour methods (`sendTransaction`, `estimateGas`,\n// `getBalance`, `getTransactionReceipt`, `getCurrentTimeMs`) reject — the\n// buyer never goes on-chain in MVP.\n\nimport type { Web3LibAdapter } from \"@bosonprotocol/common\";\nimport type { PublicClient, TypedDataDomain, TypedDataParameter } from \"viem\";\n\nimport type { Signer } from \"../types.js\";\n\n/**\n * Build the `Web3LibAdapter` that `@bosonprotocol/core-sdk`'s `CoreSDK`\n * accepts. `send(\"eth_signTypedData_v4\", [address, jsonString])` delegates\n * to `signer.signTypedData(...)`. `call({ to, data })` is forwarded to\n * `publicClient.call(...)` when one is provided; otherwise it throws (the\n * Permit strategy is the only path that exercises `call`).\n */\nexport function signerToWeb3LibAdapter(\n signer: Signer,\n chainId: number,\n publicClient?: PublicClient,\n): Web3LibAdapter {\n return {\n uuid: \"x402-client:signer-adapter\",\n getSignerAddress: () => signer.getAddress(),\n isSignerContract: async () => false,\n getChainId: async () => chainId,\n send: async (method, params) => {\n if (method !== \"eth_signTypedData_v4\") {\n throw new Error(\n `x402-client: signer adapter does not support RPC method '${method}'; only eth_signTypedData_v4 is implemented`,\n );\n }\n const raw = (params as unknown[])[1];\n if (typeof raw !== \"string\") {\n throw new Error(\n \"x402-client: eth_signTypedData_v4 payload[1] is not a JSON string — core-sdk internals may have changed\",\n );\n }\n const typedData = JSON.parse(raw) as {\n domain: TypedDataDomain;\n types: Record<string, readonly TypedDataParameter[]>;\n primaryType: string;\n message: Record<string, unknown>;\n };\n return signer.signTypedData(typedData);\n },\n call: async (req) => {\n if (!publicClient) {\n throw new Error(\n \"x402-client: signer adapter has no PublicClient configured; eth_call cannot be forwarded. \" +\n \"Pass `publicClients` in X402bClientConfig to enable the Permit token-auth strategy.\",\n );\n }\n const result = await publicClient.call({\n to: req.to as `0x${string}`,\n data: req.data as `0x${string}` | undefined,\n });\n return result.data ?? \"0x\";\n },\n getBalance: () => Promise.reject(unreachable(\"getBalance\")),\n estimateGas: () => Promise.reject(unreachable(\"estimateGas\")),\n sendTransaction: () => Promise.reject(unreachable(\"sendTransaction\")),\n getTransactionReceipt: () => Promise.reject(unreachable(\"getTransactionReceipt\")),\n getCurrentTimeMs: () => Promise.reject(unreachable(\"getCurrentTimeMs\")),\n };\n}\n\nfunction unreachable(method: string): Error {\n return new Error(\n `x402-client: stub Web3LibAdapter.${method}() is not implemented — the client never goes on-chain in MVP`,\n );\n}\n","// Construct (and cache) the `CoreSDK` instance the client signs through.\n//\n// The buyer never queries Boson's subgraph in MVP — only the EIP-712 meta-tx\n// signing path is invoked — but `CoreSDK`'s base constructor requires a\n// `subgraphUrl` string. Callers can configure real URLs per chain via\n// `subgraphUrls`, or omit it and rely on the placeholder sentinel below;\n// either way no HTTP call to that URL is made during signing.\n\nimport { CoreSDK } from \"@bosonprotocol/core-sdk\";\nimport type { Address } from \"viem\";\n\nimport { signerToWeb3LibAdapter } from \"./internal/web3lib-adapter.js\";\nimport type { Signer, X402bClientConfig } from \"./types.js\";\n\n// Sentinel used when the caller did not configure a real subgraph URL for\n// the chain. The signing path never reads from it; the value just needs to\n// be a non-empty string the constructor accepts.\nconst PLACEHOLDER_SUBGRAPH_URL = \"https://x402-client.placeholder.invalid/subgraph\";\n\nexport interface CoreSdkContext {\n coreSdk: CoreSDK;\n chainId: number;\n}\n\n/**\n * Parse a CAIP-2 EVM network identifier (e.g. `\"eip155:8453\"`) into its\n * numeric chain id. Throws if the network is not a well-formed `eip155:<N>`.\n */\nexport function parseChainId(network: string): number {\n const match = network.match(/^eip155:(\\d+)$/);\n if (!match) {\n throw new Error(\n `x402-client: unsupported network '${network}' (expected CAIP-2 'eip155:<chainId>')`,\n );\n }\n const chainId = Number(match[1]);\n if (!Number.isSafeInteger(chainId) || chainId <= 0) {\n throw new Error(\n `x402-client: unsupported network '${network}' (chainId must be a positive safe integer)`,\n );\n }\n return chainId;\n}\n\n/**\n * Lazy factory that returns a `CoreSDK` instance configured against the\n * escrow contract advertised in the network. Instances are cached per\n * `(chainId, escrowAddress)` so repeated calls on the same network — for\n * commit and subsequent post-commit actions on the same exchange — reuse a\n * single SDK.\n *\n * The SDK's `web3Lib.call(...)` is wired through `config.publicClients[chainId]`\n * when one is configured; this enables read-only RPC reads (e.g. the EIP-2612\n * Permit strategy's `nonces(owner)` lookup). Chains without a PublicClient\n * configured can still sign meta-transactions and ERC-3009 / Permit2\n * token-auths.\n */\nexport function createCoreSdkFactory(\n signer: Signer,\n config: Pick<X402bClientConfig, \"subgraphUrls\" | \"publicClients\">,\n) {\n const cache = new Map<string, CoreSDK>();\n\n return function buildCoreSdk(network: string, escrowAddress: Address): CoreSdkContext {\n const chainId = parseChainId(network);\n const cacheKey = `${chainId}:${escrowAddress.toLowerCase()}`;\n const cached = cache.get(cacheKey);\n if (cached) {\n return { coreSdk: cached, chainId };\n }\n\n const subgraphUrl = config.subgraphUrls?.[chainId] ?? PLACEHOLDER_SUBGRAPH_URL;\n const publicClient = config.publicClients?.[chainId];\n const web3Lib = signerToWeb3LibAdapter(signer, chainId, publicClient);\n\n const coreSdk = new CoreSDK({\n web3Lib,\n subgraphUrl,\n protocolDiamond: escrowAddress,\n chainId,\n });\n\n cache.set(cacheKey, coreSdk);\n return { coreSdk, chainId };\n };\n}\n","// Client-side deadline for token-auth signatures (ERC-3009 `validBefore`,\n// EIP-2612 `deadline`, Permit2 `deadline`).\n//\n// The facilitator validates strictly: `deadline − facilitatorNow ≤\n// requirements.maxTimeoutSeconds`. If the client signs at the boundary\n// (`floor(clientNow) + maxTimeoutSeconds`), any moment where the\n// verifier's wall-clock lags the signer's by ≥ 1 s pushes the gap\n// over `maxTimeoutSeconds` and the facilitator rejects with\n// BAD_TOKEN_AUTH_SIGNATURE. Docker Desktop's container clock drifting\n// behind the host clock makes this happen randomly in the e2e suite.\n//\n// We pull the deadline closer to `clientNow` by a small margin so the\n// facilitator's check tolerates that backward skew. 60 s comfortably\n// covers Docker drift and leaves a useful validity window even at the\n// spec's example `maxTimeoutSeconds = 300`.\n\nexport const TOKEN_AUTH_DEADLINE_SAFETY_MARGIN_SECONDS = 60;\n\nexport function computeTokenAuthDeadline(\n maxTimeoutSeconds: number,\n now: () => number = Date.now,\n): number {\n // Clamp so the margin can never make the deadline retroactive — if\n // the caller's `maxTimeoutSeconds < 60`, subtracting the full margin\n // would emit an already-expired signature.\n const timeoutSeconds = Math.max(1, Math.floor(maxTimeoutSeconds));\n const safetyMarginSeconds = Math.min(\n TOKEN_AUTH_DEADLINE_SAFETY_MARGIN_SECONDS,\n Math.max(0, timeoutSeconds - 1),\n );\n return Math.floor(now() / 1000) + timeoutSeconds - safetyMarginSeconds;\n}\n","// Build and sign the buyer's ERC-3009 `ReceiveWithAuthorization` for the\n// escrow contract via `CoreSDK.signReceiveWithErc3009Authorization`.\n//\n// The SDK generates a 32-byte random nonce internally, builds the EIP-712\n// typed-data against the token's domain (taken from `tokenDomainResolver`),\n// signs through the configured `Web3LibAdapter`, and returns a\n// `TransferAuthorization` with `{ strategy, data: { validAfter, validBefore,\n// nonce }, r, s, v, signature }`. We re-shape the result into the\n// `Erc3009AuthData` slot of the escrow payment payload.\n\nimport type { CoreSDK } from \"@bosonprotocol/core-sdk\";\nimport type {\n Erc3009AuthData,\n EscrowPaymentRequirements,\n} from \"@bosonprotocol/x402-core/schemes/escrow\";\nimport type { Address, Hex } from \"viem\";\n\nimport { parseChainId } from \"../core-sdk-factory.js\";\nimport type { TokenDomainResolver } from \"../types.js\";\n\nimport { computeTokenAuthDeadline } from \"./deadline.js\";\n\nexport interface SignErc3009Args {\n requirements: EscrowPaymentRequirements;\n buyer: Address;\n coreSdk: CoreSDK;\n tokenDomainResolver: TokenDomainResolver;\n /** Override the wall-clock; injected by tests for determinism. Defaults to `Date.now()`. */\n now?: () => number;\n}\n\n/**\n * Build the ERC-3009 typed-data, sign it via core-sdk, and shape the result\n * into the `Erc3009AuthData` slot of the escrow payment payload.\n */\nexport async function signErc3009({\n requirements,\n buyer,\n coreSdk,\n tokenDomainResolver,\n now = Date.now,\n}: SignErc3009Args): Promise<Erc3009AuthData> {\n const chainId = parseChainId(requirements.network);\n const domain = await tokenDomainResolver(requirements.asset as Address, chainId);\n\n const validAfter = 0;\n const validBefore = computeTokenAuthDeadline(requirements.maxTimeoutSeconds, now);\n\n const result = await coreSdk.signReceiveWithErc3009Authorization(\n requirements.asset,\n { name: domain.name, version: domain.version },\n requirements.amount,\n validAfter,\n validBefore,\n { spender: requirements.escrowAddress as `0x${string}` },\n );\n\n return {\n from: buyer,\n to: requirements.escrowAddress,\n value: requirements.amount,\n validAfter: Number(result.data.validAfter),\n validBefore: Number(result.data.validBefore),\n nonce: result.data.nonce as Hex,\n v: result.v,\n r: result.r as Hex,\n s: result.s as Hex,\n };\n}\n","// Build and sign the buyer's EIP-2612 `Permit` for the escrow contract via\n// `CoreSDK.signReceiveWithErc2612Permit`.\n//\n// The SDK fetches the token's `nonces(owner)` via `web3Lib.call(...)` before\n// signing — that path requires the client's `signerToWeb3LibAdapter` to have\n// been built with a configured `PublicClient`. Without one, the underlying\n// `eth_call` rejects with a clear \"no PublicClient configured\" error,\n// surfaced to the caller as a sign-time failure.\n//\n// The SDK returns `{ strategy: \"EIP2612\", data: { deadline }, r, s, v,\n// signature }` — note that the nonce *is not* part of `data`. The wire\n// format `PermitAuthData` carries the nonce because the facilitator's\n// verification path needs it to rebuild the digest the buyer signed (the\n// on-chain `permit()` call refetches the current nonce, but the signature\n// is locked to the value at sign time). We recover it from the SDK's\n// signed typed-data by paying a second on-chain read of `nonces(owner)`;\n// in practice the value the SDK signed against is the one we observe\n// (only an interleaved successful `permit()` between sign and read would\n// shift it, which would then have invalidated the signature anyway).\n\nimport type { CoreSDK } from \"@bosonprotocol/core-sdk\";\nimport type {\n EscrowPaymentRequirements,\n PermitAuthData,\n} from \"@bosonprotocol/x402-core/schemes/escrow\";\nimport {\n encodeFunctionData,\n decodeFunctionResult,\n type Address,\n type Hex,\n type PublicClient,\n} from \"viem\";\n\nimport { parseChainId } from \"../core-sdk-factory.js\";\nimport { UnsupportedTokenAuthError } from \"../errors.js\";\nimport type { TokenDomainResolver } from \"../types.js\";\n\nimport { computeTokenAuthDeadline } from \"./deadline.js\";\n\nconst NONCES_ABI = [\n {\n type: \"function\",\n name: \"nonces\",\n stateMutability: \"view\",\n inputs: [{ name: \"owner\", type: \"address\" }],\n outputs: [{ type: \"uint256\" }],\n },\n] as const;\n\nexport interface SignPermitArgs {\n requirements: EscrowPaymentRequirements;\n buyer: Address;\n coreSdk: CoreSDK;\n tokenDomainResolver: TokenDomainResolver;\n /** PublicClient for the chain — required to recover the nonce that the SDK signed against. */\n publicClient: PublicClient;\n /** Override the wall-clock; injected by tests for determinism. Defaults to `Date.now()`. */\n now?: () => number;\n}\n\n/**\n * Build the EIP-2612 Permit typed-data, sign it via core-sdk, look up the\n * token nonce the SDK signed against, and shape the result into the\n * `PermitAuthData` slot of the escrow payment payload.\n */\nexport async function signPermit({\n requirements,\n buyer,\n coreSdk,\n tokenDomainResolver,\n publicClient,\n now = Date.now,\n}: SignPermitArgs): Promise<PermitAuthData> {\n const chainId = parseChainId(requirements.network);\n const domain = await tokenDomainResolver(requirements.asset as Address, chainId);\n\n const noncePreSign = await readNonce(publicClient, requirements.asset as Address, buyer);\n const deadline = computeTokenAuthDeadline(requirements.maxTimeoutSeconds, now);\n\n const result = await coreSdk.signReceiveWithErc2612Permit(\n requirements.asset,\n { name: domain.name, version: domain.version },\n requirements.amount,\n deadline,\n { spender: requirements.escrowAddress as `0x${string}` },\n );\n\n const noncePostSign = await readNonce(publicClient, requirements.asset as Address, buyer);\n if (noncePreSign !== noncePostSign) {\n // A successful `permit()` for this owner landed on-chain between our\n // two reads, which means the SDK signed against either nonce. We can't\n // tell which without re-signing, so fail loudly.\n throw new UnsupportedTokenAuthError(\n `x402-client: token nonce shifted during permit signing (was ${noncePreSign}, now ${noncePostSign}); retry`,\n );\n }\n\n return {\n owner: buyer,\n spender: requirements.escrowAddress,\n value: requirements.amount,\n deadline,\n nonce: noncePreSign.toString(),\n v: result.v,\n r: result.r as Hex,\n s: result.s as Hex,\n };\n}\n\nasync function readNonce(\n publicClient: PublicClient,\n token: Address,\n owner: Address,\n): Promise<bigint> {\n const result = await publicClient.call({\n to: token as `0x${string}`,\n data: encodeFunctionData({\n abi: NONCES_ABI,\n functionName: \"nonces\",\n args: [owner as `0x${string}`],\n }),\n });\n if (!result.data) {\n throw new UnsupportedTokenAuthError(\n `x402-client: token ${token} did not return data for nonces(${owner}) — does it implement EIP-2612?`,\n );\n }\n return decodeFunctionResult({\n abi: NONCES_ABI,\n functionName: \"nonces\",\n data: result.data,\n });\n}\n","// Cross-platform random helpers.\n//\n// The client is framework-agnostic: it must run in browsers (where\n// `node:crypto` is unavailable) as well as Node. The Web Crypto API\n// (`globalThis.crypto.getRandomValues`) covers both — it's been a Node\n// global since 19 and is universally available in modern browsers.\n// The package's `engines.node` already requires Node ≥ 22, so we can\n// rely on the global without a `node:crypto` fallback.\n//\n// Centralizing the helpers here keeps `pre-commit.ts`, `post-commit.ts`,\n// and `token-auth/erc3009.ts` from each importing their own crypto\n// primitive and re-deriving the same byte → bigint / byte → hex\n// conversion (CLAUDE.md \"Move duplicated constants ... into a shared\n// file the moment they appear in two places\").\n\nimport type { Hex } from \"viem\";\n\n/** Fill a fresh `Uint8Array` of `size` bytes with cryptographically random bytes. */\nexport function randomBytes(size: number): Uint8Array {\n const bytes = new Uint8Array(size);\n if (typeof globalThis.crypto?.getRandomValues !== \"function\") {\n throw new Error(\n \"x402-client: globalThis.crypto.getRandomValues is unavailable — environment lacks the Web Crypto API\",\n );\n }\n globalThis.crypto.getRandomValues(bytes);\n return bytes;\n}\n\n/** 32-byte cryptographically random `0x`-prefixed hex string. */\nexport function randomBytes32(): Hex {\n const bytes = randomBytes(32);\n let hex = \"0x\";\n for (const b of bytes) {\n hex += b.toString(16).padStart(2, \"0\");\n }\n return hex as Hex;\n}\n\n/** Cryptographically random `uint256`. Used for protocol meta-tx replay-protection nonces. */\nexport function randomUint256(): bigint {\n const bytes = randomBytes(32);\n let n = 0n;\n for (const byte of bytes) {\n n = (n << 8n) | BigInt(byte);\n }\n return n;\n}\n","// Build and sign the buyer's Uniswap Permit2 `PermitTransferFrom` for the\n// escrow contract via `CoreSDK.signReceiveWithPermit2`.\n//\n// The SDK accepts an optional `permit2Nonce` override; if omitted it\n// generates a random uint256. We pass one explicitly via `randomUint256()`\n// so tests can stub the RNG and so the value never depends on the SDK's\n// internal nonce-generation surface. The Permit2 contract address comes\n// from the wire-format `requirements.onchainHints?.permit2` when present,\n// or falls back to the canonical CREATE2 vanity address re-exported from\n// `@bosonprotocol/x402-core/eip712/token-auth`.\n//\n// Result shape from core-sdk:\n// { strategy: \"Permit2\", data: { nonce, deadline }, r, s, v, signature }\n// — we lift it into the wire-format `Permit2AuthData`.\n\nimport type { CoreSDK } from \"@bosonprotocol/core-sdk\";\nimport { PERMIT2_ADDRESS } from \"@bosonprotocol/x402-core/eip712/token-auth\";\nimport type {\n EscrowPaymentRequirements,\n Permit2AuthData,\n} from \"@bosonprotocol/x402-core/schemes/escrow\";\nimport type { Address, Hex } from \"viem\";\n\nimport { randomUint256 } from \"../utils/crypto.js\";\n\nimport { computeTokenAuthDeadline } from \"./deadline.js\";\n\nexport interface SignPermit2Args {\n requirements: EscrowPaymentRequirements;\n buyer: Address;\n coreSdk: CoreSDK;\n /** Override the wall-clock; injected by tests for determinism. Defaults to `Date.now()`. */\n now?: () => number;\n}\n\n/**\n * Build the Permit2 `PermitTransferFrom` typed-data, sign it via core-sdk,\n * and shape the result into the `Permit2AuthData` slot of the escrow\n * payment payload.\n */\nexport async function signPermit2({\n requirements,\n buyer,\n coreSdk,\n now = Date.now,\n}: SignPermit2Args): Promise<Permit2AuthData> {\n const deadline = computeTokenAuthDeadline(requirements.maxTimeoutSeconds, now);\n const permit2Nonce = randomUint256();\n\n // `buyer` is exposed in the call site for symmetry with the other\n // strategies and to keep the function signature stable as we add\n // explicit-`user` overrides. Today the SDK derives `user` internally\n // from `coreSdk._web3Lib.getSignerAddress()`, which we route through the\n // configured `signer.getAddress()` in `signerToWeb3LibAdapter`.\n void buyer;\n\n const result = await coreSdk.signReceiveWithPermit2(\n requirements.asset,\n requirements.amount,\n deadline,\n {\n spender: requirements.escrowAddress as `0x${string}`,\n permit2Address: PERMIT2_ADDRESS,\n permit2Nonce,\n },\n );\n\n return {\n permitted: { token: requirements.asset, amount: requirements.amount },\n spender: requirements.escrowAddress,\n nonce: permit2Nonce.toString(),\n deadline,\n signature: result.signature as Hex,\n };\n}\n","// Token-authorization strategy dispatcher.\n//\n// Picks one of the buyer-side strategies (`erc3009`, `permit`, `permit2`)\n// from `requirements.tokenAuthStrategies`, signs the corresponding payload\n// via `CoreSDK`, and returns the wire-format `BosonTokenAuth` slot. The\n// `none` strategy is encoded by the absence of `tokenAuth` on the payload\n// — callers handle it outside this module.\n//\n// Preference order when the server advertises multiple strategies:\n// `erc3009` → `permit2` → `permit`. ERC-3009 is preferred for tokens that\n// support it (USDC, USDP, EUROC, etc.) because the receive-variant\n// eliminates relayer front-running. Permit2 is preferred over Permit for\n// tokens behind the canonical Permit2 contract because it carries a\n// caller-controllable nonce. Permit is the fallback.\n\nimport type { CoreSDK } from \"@bosonprotocol/core-sdk\";\nimport type {\n BosonTokenAuth,\n EscrowPaymentRequirements,\n TokenAuthStrategy,\n} from \"@bosonprotocol/x402-core/schemes/escrow\";\nimport type { Address, PublicClient } from \"viem\";\n\nimport { UnsupportedTokenAuthError } from \"../errors.js\";\nimport type { TokenDomainResolver } from \"../types.js\";\n\nimport { signErc3009 } from \"./erc3009.js\";\nimport { signPermit } from \"./permit.js\";\nimport { signPermit2 } from \"./permit2.js\";\n\nexport interface BuildTokenAuthArgs {\n requirements: EscrowPaymentRequirements;\n buyer: Address;\n coreSdk: CoreSDK;\n /** Required for ERC-3009 and EIP-2612 Permit; not used by Permit2. */\n tokenDomainResolver?: TokenDomainResolver;\n /** Optional PublicClient for the requirements' chain. Required for Permit. */\n publicClient?: PublicClient;\n now?: () => number;\n}\n\nexport interface BuiltTokenAuth {\n strategy: TokenAuthStrategy;\n tokenAuth: BosonTokenAuth;\n}\n\nconst STRATEGY_PREFERENCE: readonly TokenAuthStrategy[] = [\"erc3009\", \"permit2\", \"permit\"];\n\nfunction strategyIsAvailable(s: TokenAuthStrategy, args: BuildTokenAuthArgs): boolean {\n switch (s) {\n case \"erc3009\":\n return args.tokenDomainResolver !== undefined;\n case \"permit\":\n return args.tokenDomainResolver !== undefined && args.publicClient !== undefined;\n case \"permit2\":\n return true;\n case \"none\":\n return false;\n default: {\n const _exhaustive: never = s;\n void _exhaustive;\n return false;\n }\n }\n}\n\n/**\n * Pick the highest-preference strategy from `requirements.tokenAuthStrategies`,\n * sign the corresponding payload via core-sdk, and return the wire-format\n * `BosonTokenAuth` slot.\n *\n * The picker is capability-aware: a strategy is only chosen when the runtime\n * config has the prerequisites for it (e.g. `tokenDomainResolver` for\n * ERC-3009, both `tokenDomainResolver` and `publicClient` for Permit). This\n * lets a deployment without `tokenDomainResolver` still satisfy servers that\n * advertise `[\"erc3009\", \"permit2\"]` by falling back to Permit2.\n */\nexport async function buildAndSignTokenAuth(args: BuildTokenAuthArgs): Promise<BuiltTokenAuth> {\n const advertised = args.requirements.tokenAuthStrategies;\n const chosen = STRATEGY_PREFERENCE.find(\n (s) => advertised.includes(s) && strategyIsAvailable(s, args),\n );\n if (!chosen) {\n throw new UnsupportedTokenAuthError(\n `server advertises tokenAuthStrategies=[${advertised.join(\", \")}]; ` +\n `client supports [${STRATEGY_PREFERENCE.join(\", \")}] but none are usable ` +\n `with the current X402bClientConfig ` +\n `(ERC-3009 and Permit require tokenDomainResolver; Permit also requires ` +\n `a PublicClient for the requirements' chain via publicClients)`,\n );\n }\n\n switch (chosen) {\n case \"erc3009\": {\n if (!args.tokenDomainResolver) {\n throw new UnsupportedTokenAuthError(\n \"server advertises 'erc3009' but no tokenDomainResolver is configured — pass tokenDomainResolver in X402bClientConfig\",\n );\n }\n const data = await signErc3009({\n ...args,\n tokenDomainResolver: args.tokenDomainResolver,\n });\n return { strategy: \"erc3009\", tokenAuth: { kind: \"erc3009\", data } };\n }\n case \"permit\": {\n if (!args.tokenDomainResolver) {\n throw new UnsupportedTokenAuthError(\n \"server advertises 'permit' but no tokenDomainResolver is configured — pass tokenDomainResolver in X402bClientConfig\",\n );\n }\n if (!args.publicClient) {\n throw new UnsupportedTokenAuthError(\n \"server advertises 'permit' but no PublicClient is configured for this chain — pass publicClients in X402bClientConfig\",\n );\n }\n const data = await signPermit({\n ...args,\n tokenDomainResolver: args.tokenDomainResolver,\n publicClient: args.publicClient,\n });\n return { strategy: \"permit\", tokenAuth: { kind: \"permit\", data } };\n }\n case \"permit2\": {\n const data = await signPermit2(args);\n return { strategy: \"permit2\", tokenAuth: { kind: \"permit2\", data } };\n }\n case \"none\": {\n // Should never reach: `STRATEGY_PREFERENCE` doesn't include \"none\",\n // so a server advertising only \"none\" falls through to the error\n // above. Keep the case for exhaustiveness against\n // `TokenAuthStrategy`.\n throw new UnsupportedTokenAuthError(\n \"tokenAuthStrategy='none' has no client-side signing — callers handle it without invoking this dispatcher\",\n );\n }\n default: {\n const _exhaustive: never = chosen;\n throw new UnsupportedTokenAuthError(\n `unrecognised tokenAuthStrategy '${(_exhaustive as { toString(): string }).toString()}'`,\n );\n }\n }\n}\n"]}