@fatstack/x402 0.0.1 → 0.1.1
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 +114 -6
- package/dist/chunk-C4BASAUA.js +111 -0
- package/dist/chunk-C4BASAUA.js.map +1 -0
- package/dist/chunk-PY25VKHS.js +219 -0
- package/dist/chunk-PY25VKHS.js.map +1 -0
- package/dist/chunk-ZGVPNFNS.js +163 -0
- package/dist/chunk-ZGVPNFNS.js.map +1 -0
- package/dist/client-BBsrY6gC.d.cts +111 -0
- package/dist/client-BBsrY6gC.d.ts +111 -0
- package/dist/client.cjs +243 -0
- package/dist/client.cjs.map +1 -0
- package/dist/client.d.cts +3 -0
- package/dist/client.d.ts +3 -0
- package/dist/client.js +17 -0
- package/dist/client.js.map +1 -0
- package/dist/index.cjs +516 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +117 -0
- package/dist/index.d.ts +117 -0
- package/dist/index.js +61 -0
- package/dist/index.js.map +1 -0
- package/dist/provider.cjs +289 -0
- package/dist/provider.cjs.map +1 -0
- package/dist/provider.d.cts +99 -0
- package/dist/provider.d.ts +99 -0
- package/dist/provider.js +8 -0
- package/dist/provider.js.map +1 -0
- package/dist/testing.cjs +73 -0
- package/dist/testing.cjs.map +1 -0
- package/dist/testing.d.cts +22 -0
- package/dist/testing.d.ts +22 -0
- package/dist/testing.js +48 -0
- package/dist/testing.js.map +1 -0
- package/package.json +84 -4
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/client.ts","../src/store.ts"],"sourcesContent":["import { x402Client } from '@x402/core/client';\nimport { decodePaymentRequiredHeader } from '@x402/core/http';\nimport type { PaymentRequirements } from '@x402/core/types';\nimport { ExactEvmScheme } from '@x402/evm/exact/client';\nimport { wrapFetchWithPayment } from '@x402/fetch';\n\nimport { NETWORKS, PAYMENT_REF_HEADER, readSettlementHeader } from './constants.js';\nimport type { NetworkName } from './constants.js';\nimport { SpendGuardError, UnreadableQuoteError } from './errors.js';\nimport { quoteToUsd } from './money.js';\nimport { createMemorySpendStore } from './store.js';\nimport type { SpendStore } from './store.js';\n\n/** The signer the official EVM scheme expects: address + signTypedData, no key handling. */\nexport type EvmWallet = ConstructorParameters<typeof ExactEvmScheme>[0];\n\nexport interface SpendGuards {\n /** Hard USD ceiling for a single call. */\n maxPerCall?: number;\n /** Rolling 60-minute USD ceiling. */\n maxPerHour?: number;\n /** Rolling 24-hour USD ceiling. */\n maxPerDay?: number;\n /** Hostnames this agent may pay. Checked before any network call. */\n allowedHosts?: string[];\n}\n\nexport interface PayFetchOptions {\n wallet: EvmWallet;\n guards?: SpendGuards;\n /** Defaults to a process-local in-memory store. */\n store?: SpendStore;\n networks?: NetworkName[];\n fetch?: typeof globalThis.fetch;\n now?: () => number;\n}\n\nconst HOUR_MS = 60 * 60 * 1000;\nconst DAY_MS = 24 * HOUR_MS;\n\nfunction hostOf(input: RequestInfo | URL): string | null {\n try {\n const raw =\n typeof input === 'string'\n ? input\n : input instanceof URL\n ? input.toString()\n : (input as Request).url;\n return new URL(raw).hostname.toLowerCase();\n } catch {\n return null;\n }\n}\n\n/** `example.com` in allowedHosts also authorises `tool.example.com`. */\nfunction hostAllowed(host: string, allowed: readonly string[]): boolean {\n return allowed.some((entry) => {\n const candidate = entry.trim().toLowerCase().replace(/^\\*\\./, '');\n return host === candidate || host.endsWith(`.${candidate}`);\n });\n}\n\n/**\n * Checks every guard against a quote. Returns the USD value if the call may proceed,\n * throws SpendGuardError otherwise. Called before a payment payload exists.\n */\nexport async function evaluateGuards(\n quoteUsd: number,\n guards: SpendGuards,\n store: SpendStore,\n now: number,\n): Promise<void> {\n if (guards.maxPerCall !== undefined && quoteUsd > guards.maxPerCall) {\n throw new SpendGuardError(\n 'maxPerCall',\n `Call costs $${quoteUsd} which exceeds the maxPerCall limit of $${guards.maxPerCall}`,\n { limitUsd: guards.maxPerCall, attemptedUsd: quoteUsd },\n );\n }\n\n if (guards.maxPerHour !== undefined) {\n const spent = await store.totalSince(now - HOUR_MS);\n if (spent + quoteUsd > guards.maxPerHour) {\n throw new SpendGuardError(\n 'maxPerHour',\n `Call costs $${quoteUsd} and $${spent} was already spent this hour, exceeding the maxPerHour limit of $${guards.maxPerHour}`,\n { limitUsd: guards.maxPerHour, attemptedUsd: spent + quoteUsd },\n );\n }\n }\n\n if (guards.maxPerDay !== undefined) {\n const spent = await store.totalSince(now - DAY_MS);\n if (spent + quoteUsd > guards.maxPerDay) {\n throw new SpendGuardError(\n 'maxPerDay',\n `Call costs $${quoteUsd} and $${spent} was already spent today, exceeding the maxPerDay limit of $${guards.maxPerDay}`,\n { limitUsd: guards.maxPerDay, attemptedUsd: spent + quoteUsd },\n );\n }\n }\n}\n\n/**\n * Reads the offered requirements from a 402.\n *\n * x402 v2 carries them in the base64 `payment-required` header, not the body — the body\n * is the resource's own, and may be anything. v1 put them in the body, so both are read.\n */\nexport async function readQuotedRequirements(response: Response): Promise<PaymentRequirements[]> {\n const header = response.headers.get('payment-required');\n if (header) {\n try {\n const decoded = decodePaymentRequiredHeader(header);\n if (Array.isArray(decoded.accepts) && decoded.accepts.length > 0) return decoded.accepts;\n } catch {\n // Fall through to the body form.\n }\n }\n\n const body: unknown = await response\n .clone()\n .json()\n .catch(() => null);\n const accepts = (body as { accepts?: PaymentRequirements[] } | null)?.accepts;\n return Array.isArray(accepts) ? accepts : [];\n}\n\n/** Reads the quoted amount out of x402 payment requirements. */\ninterface PriceableRequirement {\n maxAmountRequired?: string;\n amount?: string;\n asset?: string;\n extra?: { decimals?: number };\n}\n\nexport function quoteUsdOf(requirements: PaymentRequirements): number {\n const asRecord: PriceableRequirement = requirements;\n const amountAtomic = asRecord.maxAmountRequired ?? asRecord.amount;\n if (!amountAtomic || !asRecord.asset) {\n throw new RangeError('Payment requirements carry no priceable amount; refusing to pay.');\n }\n return quoteToUsd({\n amountAtomic,\n asset: asRecord.asset,\n decimals: asRecord.extra?.decimals,\n });\n}\n\n/**\n * A `fetch` that pays for 402 responses, under spend guards.\n *\n * The 402 handling, signing and retry are the official x402 packages' work. What this adds\n * is refusal: guards are evaluated inside the payment-policy hook, which runs after the\n * quote is known and before any payload is signed, so exceeding a guard throws\n * SpendGuardError with nothing signed and nothing spent.\n */\nexport async function payFetch(\n url: RequestInfo | URL,\n init: RequestInit | undefined,\n options: PayFetchOptions,\n): Promise<Response> {\n const guards = options.guards ?? {};\n const store = options.store ?? createMemorySpendStore();\n const now = options.now ?? Date.now;\n const baseFetch = options.fetch ?? globalThis.fetch;\n const networks = options.networks ?? (['base'] as NetworkName[]);\n\n // Host allowlist is checked first: an unauthorised host should cost no request at all.\n if (guards.allowedHosts) {\n const host = hostOf(url);\n if (!host || !hostAllowed(host, guards.allowedHosts)) {\n throw new SpendGuardError(\n 'allowedHosts',\n `Host ${host ?? '<unparseable>'} is not in allowedHosts`,\n { host: host ?? undefined },\n );\n }\n }\n\n let quotedUsd: number | null = null;\n\n const client = new x402Client((_version, requirements) => {\n // Runs before the payment payload is created. Throwing here means no signature.\n const affordable = requirements.filter((requirement) => {\n const usd = quoteUsdOf(requirement);\n return guards.maxPerCall === undefined || usd <= guards.maxPerCall;\n });\n\n const chosen = (affordable.length > 0 ? affordable : requirements)[0];\n if (!chosen) throw new RangeError('Resource offered no payment requirements');\n\n quotedUsd = quoteUsdOf(chosen);\n return chosen;\n });\n\n for (const name of networks) {\n client.register(NETWORKS[name].caip2, new ExactEvmScheme(options.wallet));\n }\n\n const guarded: typeof globalThis.fetch = async (input, requestInit) => {\n const response = await baseFetch(input, requestInit);\n if (response.status !== 402) return response;\n\n // Peek at the quote and apply guards before the wrapper signs anything.\n const accepts = await readQuotedRequirements(response);\n if (accepts.length === 0) {\n // A 402 whose requirements we cannot read is a 402 we cannot price, and an\n // unpriced call cannot be checked against a spend cap. Refuse rather than let it\n // through unguarded.\n throw new UnreadableQuoteError(\n 'Received a 402 with no readable payment requirements (no payment-required header, no accepts body). Refusing to pay blind.',\n );\n }\n\n const cheapest = accepts.map((requirement) => quoteUsdOf(requirement)).sort((a, b) => a - b)[0];\n if (cheapest !== undefined) {\n await evaluateGuards(cheapest, guards, store, now());\n }\n return response;\n };\n\n const paying = wrapFetchWithPayment(guarded, client);\n const response = await paying(url as RequestInfo, init);\n\n // Record the spend only once a payment actually settled.\n if (readSettlementHeader(response.headers) && quotedUsd !== null) {\n await store.record({ at: now(), usd: quotedUsd });\n }\n\n return response;\n}\n\nexport { PAYMENT_REF_HEADER };\n","/** One recorded spend, in USD, at a wall-clock millisecond. */\nexport interface SpendRecord {\n at: number;\n usd: number;\n}\n\n/**\n * Where rolling spend totals live. The default is in-memory and per-process; swap in a\n * shared implementation (Redis, Durable Object, Postgres) when an agent runs as more\n * than one process, otherwise each process enforces its own separate budget.\n */\nexport interface SpendStore {\n record(entry: SpendRecord): Promise<void> | void;\n /** Total USD recorded at or after `sinceMs`. */\n totalSince(sinceMs: number): Promise<number> | number;\n}\n\n/** Process-local store. Entries older than the longest window are dropped on write. */\nexport function createMemorySpendStore(retentionMs = 24 * 60 * 60 * 1000): SpendStore {\n let entries: SpendRecord[] = [];\n\n return {\n record(entry) {\n entries.push(entry);\n const cutoff = entry.at - retentionMs;\n if (entries.length > 64) entries = entries.filter((e) => e.at >= cutoff);\n },\n totalSince(sinceMs) {\n let total = 0;\n for (const entry of entries) if (entry.at >= sinceMs) total += entry.usd;\n return total;\n },\n };\n}\n"],"mappings":"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","names":["response"]}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { PaymentRequirements } from '@x402/core/types';
|
|
2
|
+
import { ExactEvmScheme } from '@x402/evm/exact/client';
|
|
3
|
+
|
|
4
|
+
/** Base networks in CAIP-2 form, which is what x402 v2 speaks. */
|
|
5
|
+
/**
|
|
6
|
+
* The EIP-712 domain a payer signs the EIP-3009 authorisation against is **not** listed
|
|
7
|
+
* here on purpose: it differs per network (mainnet USDC is "USD Coin", the Sepolia
|
|
8
|
+
* deployment is "USDC"), and the EVM scheme owns the authoritative table. Quoting a
|
|
9
|
+
* dollar price lets it resolve the asset and publish that domain in the 402; naming an
|
|
10
|
+
* explicit asset bypasses the lookup and emits `extra: {}`, which no payer can sign
|
|
11
|
+
* against.
|
|
12
|
+
*/
|
|
13
|
+
declare const NETWORKS: {
|
|
14
|
+
readonly base: {
|
|
15
|
+
readonly caip2: "eip155:8453";
|
|
16
|
+
readonly chainId: 8453;
|
|
17
|
+
readonly usdc: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913";
|
|
18
|
+
};
|
|
19
|
+
readonly 'base-sepolia': {
|
|
20
|
+
readonly caip2: "eip155:84532";
|
|
21
|
+
readonly chainId: 84532;
|
|
22
|
+
readonly usdc: "0x036CbD53842c5426634e7929541eC2318f3dCF7e";
|
|
23
|
+
};
|
|
24
|
+
};
|
|
25
|
+
type NetworkName = keyof typeof NETWORKS;
|
|
26
|
+
/** USDC is 6 decimals on Base. */
|
|
27
|
+
declare const USDC_DECIMALS = 6;
|
|
28
|
+
/**
|
|
29
|
+
* Required on every 402 body and every payment-facing doc page. A payment is a direct
|
|
30
|
+
* on-chain transfer between two wallets: once settled nobody, Fatstack included, can
|
|
31
|
+
* reverse it.
|
|
32
|
+
*/
|
|
33
|
+
declare const NO_REFUNDS_NOTICE = "Payments are final. Once settled on-chain the transfer cannot be reversed, and there are no refunds.";
|
|
34
|
+
declare const DOCS_URL = "https://fatstack.net/docs/payments";
|
|
35
|
+
/** Correlates a settled payment with the indexer's view of the on-chain transfer. */
|
|
36
|
+
declare const PAYMENT_REF_HEADER = "X-Fatstack-Payment-Ref";
|
|
37
|
+
/**
|
|
38
|
+
* x402 v2 dropped the `X-` prefix: the settlement receipt is `PAYMENT-RESPONSE` and the
|
|
39
|
+
* payer's payload is `PAYMENT-SIGNATURE`. The v1 spellings are still read so a v1 payer
|
|
40
|
+
* or resource keeps working. Header names are case-insensitive; these are lowercase
|
|
41
|
+
* because `Headers.get` normalises.
|
|
42
|
+
*/
|
|
43
|
+
declare const SETTLEMENT_HEADERS: readonly ["payment-response", "x-payment-response"];
|
|
44
|
+
declare const PAYMENT_SIGNATURE_HEADERS: readonly ["payment-signature", "x-payment"];
|
|
45
|
+
/** First settlement receipt present on a response, in either spelling. */
|
|
46
|
+
declare function readSettlementHeader(headers: Headers): string | null;
|
|
47
|
+
declare const DEFAULT_FACILITATOR_URL = "https://x402.org/facilitator";
|
|
48
|
+
|
|
49
|
+
/** One recorded spend, in USD, at a wall-clock millisecond. */
|
|
50
|
+
interface SpendRecord {
|
|
51
|
+
at: number;
|
|
52
|
+
usd: number;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Where rolling spend totals live. The default is in-memory and per-process; swap in a
|
|
56
|
+
* shared implementation (Redis, Durable Object, Postgres) when an agent runs as more
|
|
57
|
+
* than one process, otherwise each process enforces its own separate budget.
|
|
58
|
+
*/
|
|
59
|
+
interface SpendStore {
|
|
60
|
+
record(entry: SpendRecord): Promise<void> | void;
|
|
61
|
+
/** Total USD recorded at or after `sinceMs`. */
|
|
62
|
+
totalSince(sinceMs: number): Promise<number> | number;
|
|
63
|
+
}
|
|
64
|
+
/** Process-local store. Entries older than the longest window are dropped on write. */
|
|
65
|
+
declare function createMemorySpendStore(retentionMs?: number): SpendStore;
|
|
66
|
+
|
|
67
|
+
/** The signer the official EVM scheme expects: address + signTypedData, no key handling. */
|
|
68
|
+
type EvmWallet = ConstructorParameters<typeof ExactEvmScheme>[0];
|
|
69
|
+
interface SpendGuards {
|
|
70
|
+
/** Hard USD ceiling for a single call. */
|
|
71
|
+
maxPerCall?: number;
|
|
72
|
+
/** Rolling 60-minute USD ceiling. */
|
|
73
|
+
maxPerHour?: number;
|
|
74
|
+
/** Rolling 24-hour USD ceiling. */
|
|
75
|
+
maxPerDay?: number;
|
|
76
|
+
/** Hostnames this agent may pay. Checked before any network call. */
|
|
77
|
+
allowedHosts?: string[];
|
|
78
|
+
}
|
|
79
|
+
interface PayFetchOptions {
|
|
80
|
+
wallet: EvmWallet;
|
|
81
|
+
guards?: SpendGuards;
|
|
82
|
+
/** Defaults to a process-local in-memory store. */
|
|
83
|
+
store?: SpendStore;
|
|
84
|
+
networks?: NetworkName[];
|
|
85
|
+
fetch?: typeof globalThis.fetch;
|
|
86
|
+
now?: () => number;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Checks every guard against a quote. Returns the USD value if the call may proceed,
|
|
90
|
+
* throws SpendGuardError otherwise. Called before a payment payload exists.
|
|
91
|
+
*/
|
|
92
|
+
declare function evaluateGuards(quoteUsd: number, guards: SpendGuards, store: SpendStore, now: number): Promise<void>;
|
|
93
|
+
/**
|
|
94
|
+
* Reads the offered requirements from a 402.
|
|
95
|
+
*
|
|
96
|
+
* x402 v2 carries them in the base64 `payment-required` header, not the body — the body
|
|
97
|
+
* is the resource's own, and may be anything. v1 put them in the body, so both are read.
|
|
98
|
+
*/
|
|
99
|
+
declare function readQuotedRequirements(response: Response): Promise<PaymentRequirements[]>;
|
|
100
|
+
declare function quoteUsdOf(requirements: PaymentRequirements): number;
|
|
101
|
+
/**
|
|
102
|
+
* A `fetch` that pays for 402 responses, under spend guards.
|
|
103
|
+
*
|
|
104
|
+
* The 402 handling, signing and retry are the official x402 packages' work. What this adds
|
|
105
|
+
* is refusal: guards are evaluated inside the payment-policy hook, which runs after the
|
|
106
|
+
* quote is known and before any payload is signed, so exceeding a guard throws
|
|
107
|
+
* SpendGuardError with nothing signed and nothing spent.
|
|
108
|
+
*/
|
|
109
|
+
declare function payFetch(url: RequestInfo | URL, init: RequestInit | undefined, options: PayFetchOptions): Promise<Response>;
|
|
110
|
+
|
|
111
|
+
export { DEFAULT_FACILITATOR_URL as D, type EvmWallet as E, NETWORKS as N, PAYMENT_REF_HEADER as P, SETTLEMENT_HEADERS as S, USDC_DECIMALS as U, DOCS_URL as a, NO_REFUNDS_NOTICE as b, type NetworkName as c, PAYMENT_SIGNATURE_HEADERS as d, type PayFetchOptions as e, type SpendGuards as f, type SpendRecord as g, type SpendStore as h, createMemorySpendStore as i, evaluateGuards as j, readQuotedRequirements as k, payFetch as p, quoteUsdOf as q, readSettlementHeader as r };
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { PaymentRequirements } from '@x402/core/types';
|
|
2
|
+
import { ExactEvmScheme } from '@x402/evm/exact/client';
|
|
3
|
+
|
|
4
|
+
/** Base networks in CAIP-2 form, which is what x402 v2 speaks. */
|
|
5
|
+
/**
|
|
6
|
+
* The EIP-712 domain a payer signs the EIP-3009 authorisation against is **not** listed
|
|
7
|
+
* here on purpose: it differs per network (mainnet USDC is "USD Coin", the Sepolia
|
|
8
|
+
* deployment is "USDC"), and the EVM scheme owns the authoritative table. Quoting a
|
|
9
|
+
* dollar price lets it resolve the asset and publish that domain in the 402; naming an
|
|
10
|
+
* explicit asset bypasses the lookup and emits `extra: {}`, which no payer can sign
|
|
11
|
+
* against.
|
|
12
|
+
*/
|
|
13
|
+
declare const NETWORKS: {
|
|
14
|
+
readonly base: {
|
|
15
|
+
readonly caip2: "eip155:8453";
|
|
16
|
+
readonly chainId: 8453;
|
|
17
|
+
readonly usdc: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913";
|
|
18
|
+
};
|
|
19
|
+
readonly 'base-sepolia': {
|
|
20
|
+
readonly caip2: "eip155:84532";
|
|
21
|
+
readonly chainId: 84532;
|
|
22
|
+
readonly usdc: "0x036CbD53842c5426634e7929541eC2318f3dCF7e";
|
|
23
|
+
};
|
|
24
|
+
};
|
|
25
|
+
type NetworkName = keyof typeof NETWORKS;
|
|
26
|
+
/** USDC is 6 decimals on Base. */
|
|
27
|
+
declare const USDC_DECIMALS = 6;
|
|
28
|
+
/**
|
|
29
|
+
* Required on every 402 body and every payment-facing doc page. A payment is a direct
|
|
30
|
+
* on-chain transfer between two wallets: once settled nobody, Fatstack included, can
|
|
31
|
+
* reverse it.
|
|
32
|
+
*/
|
|
33
|
+
declare const NO_REFUNDS_NOTICE = "Payments are final. Once settled on-chain the transfer cannot be reversed, and there are no refunds.";
|
|
34
|
+
declare const DOCS_URL = "https://fatstack.net/docs/payments";
|
|
35
|
+
/** Correlates a settled payment with the indexer's view of the on-chain transfer. */
|
|
36
|
+
declare const PAYMENT_REF_HEADER = "X-Fatstack-Payment-Ref";
|
|
37
|
+
/**
|
|
38
|
+
* x402 v2 dropped the `X-` prefix: the settlement receipt is `PAYMENT-RESPONSE` and the
|
|
39
|
+
* payer's payload is `PAYMENT-SIGNATURE`. The v1 spellings are still read so a v1 payer
|
|
40
|
+
* or resource keeps working. Header names are case-insensitive; these are lowercase
|
|
41
|
+
* because `Headers.get` normalises.
|
|
42
|
+
*/
|
|
43
|
+
declare const SETTLEMENT_HEADERS: readonly ["payment-response", "x-payment-response"];
|
|
44
|
+
declare const PAYMENT_SIGNATURE_HEADERS: readonly ["payment-signature", "x-payment"];
|
|
45
|
+
/** First settlement receipt present on a response, in either spelling. */
|
|
46
|
+
declare function readSettlementHeader(headers: Headers): string | null;
|
|
47
|
+
declare const DEFAULT_FACILITATOR_URL = "https://x402.org/facilitator";
|
|
48
|
+
|
|
49
|
+
/** One recorded spend, in USD, at a wall-clock millisecond. */
|
|
50
|
+
interface SpendRecord {
|
|
51
|
+
at: number;
|
|
52
|
+
usd: number;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Where rolling spend totals live. The default is in-memory and per-process; swap in a
|
|
56
|
+
* shared implementation (Redis, Durable Object, Postgres) when an agent runs as more
|
|
57
|
+
* than one process, otherwise each process enforces its own separate budget.
|
|
58
|
+
*/
|
|
59
|
+
interface SpendStore {
|
|
60
|
+
record(entry: SpendRecord): Promise<void> | void;
|
|
61
|
+
/** Total USD recorded at or after `sinceMs`. */
|
|
62
|
+
totalSince(sinceMs: number): Promise<number> | number;
|
|
63
|
+
}
|
|
64
|
+
/** Process-local store. Entries older than the longest window are dropped on write. */
|
|
65
|
+
declare function createMemorySpendStore(retentionMs?: number): SpendStore;
|
|
66
|
+
|
|
67
|
+
/** The signer the official EVM scheme expects: address + signTypedData, no key handling. */
|
|
68
|
+
type EvmWallet = ConstructorParameters<typeof ExactEvmScheme>[0];
|
|
69
|
+
interface SpendGuards {
|
|
70
|
+
/** Hard USD ceiling for a single call. */
|
|
71
|
+
maxPerCall?: number;
|
|
72
|
+
/** Rolling 60-minute USD ceiling. */
|
|
73
|
+
maxPerHour?: number;
|
|
74
|
+
/** Rolling 24-hour USD ceiling. */
|
|
75
|
+
maxPerDay?: number;
|
|
76
|
+
/** Hostnames this agent may pay. Checked before any network call. */
|
|
77
|
+
allowedHosts?: string[];
|
|
78
|
+
}
|
|
79
|
+
interface PayFetchOptions {
|
|
80
|
+
wallet: EvmWallet;
|
|
81
|
+
guards?: SpendGuards;
|
|
82
|
+
/** Defaults to a process-local in-memory store. */
|
|
83
|
+
store?: SpendStore;
|
|
84
|
+
networks?: NetworkName[];
|
|
85
|
+
fetch?: typeof globalThis.fetch;
|
|
86
|
+
now?: () => number;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Checks every guard against a quote. Returns the USD value if the call may proceed,
|
|
90
|
+
* throws SpendGuardError otherwise. Called before a payment payload exists.
|
|
91
|
+
*/
|
|
92
|
+
declare function evaluateGuards(quoteUsd: number, guards: SpendGuards, store: SpendStore, now: number): Promise<void>;
|
|
93
|
+
/**
|
|
94
|
+
* Reads the offered requirements from a 402.
|
|
95
|
+
*
|
|
96
|
+
* x402 v2 carries them in the base64 `payment-required` header, not the body — the body
|
|
97
|
+
* is the resource's own, and may be anything. v1 put them in the body, so both are read.
|
|
98
|
+
*/
|
|
99
|
+
declare function readQuotedRequirements(response: Response): Promise<PaymentRequirements[]>;
|
|
100
|
+
declare function quoteUsdOf(requirements: PaymentRequirements): number;
|
|
101
|
+
/**
|
|
102
|
+
* A `fetch` that pays for 402 responses, under spend guards.
|
|
103
|
+
*
|
|
104
|
+
* The 402 handling, signing and retry are the official x402 packages' work. What this adds
|
|
105
|
+
* is refusal: guards are evaluated inside the payment-policy hook, which runs after the
|
|
106
|
+
* quote is known and before any payload is signed, so exceeding a guard throws
|
|
107
|
+
* SpendGuardError with nothing signed and nothing spent.
|
|
108
|
+
*/
|
|
109
|
+
declare function payFetch(url: RequestInfo | URL, init: RequestInit | undefined, options: PayFetchOptions): Promise<Response>;
|
|
110
|
+
|
|
111
|
+
export { DEFAULT_FACILITATOR_URL as D, type EvmWallet as E, NETWORKS as N, PAYMENT_REF_HEADER as P, SETTLEMENT_HEADERS as S, USDC_DECIMALS as U, DOCS_URL as a, NO_REFUNDS_NOTICE as b, type NetworkName as c, PAYMENT_SIGNATURE_HEADERS as d, type PayFetchOptions as e, type SpendGuards as f, type SpendRecord as g, type SpendStore as h, createMemorySpendStore as i, evaluateGuards as j, readQuotedRequirements as k, payFetch as p, quoteUsdOf as q, readSettlementHeader as r };
|
package/dist/client.cjs
ADDED
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/client.ts
|
|
21
|
+
var client_exports = {};
|
|
22
|
+
__export(client_exports, {
|
|
23
|
+
PAYMENT_REF_HEADER: () => PAYMENT_REF_HEADER,
|
|
24
|
+
evaluateGuards: () => evaluateGuards,
|
|
25
|
+
payFetch: () => payFetch,
|
|
26
|
+
quoteUsdOf: () => quoteUsdOf,
|
|
27
|
+
readQuotedRequirements: () => readQuotedRequirements
|
|
28
|
+
});
|
|
29
|
+
module.exports = __toCommonJS(client_exports);
|
|
30
|
+
var import_client = require("@x402/core/client");
|
|
31
|
+
var import_http = require("@x402/core/http");
|
|
32
|
+
var import_client2 = require("@x402/evm/exact/client");
|
|
33
|
+
var import_fetch = require("@x402/fetch");
|
|
34
|
+
|
|
35
|
+
// src/constants.ts
|
|
36
|
+
var NETWORKS = {
|
|
37
|
+
base: {
|
|
38
|
+
caip2: "eip155:8453",
|
|
39
|
+
chainId: 8453,
|
|
40
|
+
usdc: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"
|
|
41
|
+
},
|
|
42
|
+
"base-sepolia": {
|
|
43
|
+
caip2: "eip155:84532",
|
|
44
|
+
chainId: 84532,
|
|
45
|
+
usdc: "0x036CbD53842c5426634e7929541eC2318f3dCF7e"
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
var USDC_DECIMALS = 6;
|
|
49
|
+
var PAYMENT_REF_HEADER = "X-Fatstack-Payment-Ref";
|
|
50
|
+
var SETTLEMENT_HEADERS = ["payment-response", "x-payment-response"];
|
|
51
|
+
function readSettlementHeader(headers) {
|
|
52
|
+
for (const name of SETTLEMENT_HEADERS) {
|
|
53
|
+
const value = headers.get(name);
|
|
54
|
+
if (value) return value;
|
|
55
|
+
}
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// src/errors.ts
|
|
60
|
+
var SpendGuardError = class extends Error {
|
|
61
|
+
constructor(guard, message, detail = {}) {
|
|
62
|
+
super(message);
|
|
63
|
+
this.guard = guard;
|
|
64
|
+
this.detail = detail;
|
|
65
|
+
}
|
|
66
|
+
guard;
|
|
67
|
+
detail;
|
|
68
|
+
name = "SpendGuardError";
|
|
69
|
+
};
|
|
70
|
+
var UnreadableQuoteError = class extends Error {
|
|
71
|
+
name = "UnreadableQuoteError";
|
|
72
|
+
constructor(message) {
|
|
73
|
+
super(message);
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
// src/money.ts
|
|
78
|
+
var UNITS_PER_USDC = 10n ** BigInt(USDC_DECIMALS);
|
|
79
|
+
var USDC_ADDRESSES = new Set(
|
|
80
|
+
Object.values(NETWORKS).map((network) => network.usdc.toLowerCase())
|
|
81
|
+
);
|
|
82
|
+
function quoteToUsd(quote) {
|
|
83
|
+
const decimals = USDC_ADDRESSES.has(quote.asset.toLowerCase()) ? USDC_DECIMALS : quote.decimals;
|
|
84
|
+
if (decimals === void 0 || !Number.isInteger(decimals) || decimals < 0 || decimals > 36) {
|
|
85
|
+
throw new RangeError(
|
|
86
|
+
`Cannot price asset ${quote.asset}: unknown decimals. Refusing to evaluate spend guards against an unpriceable quote.`
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
if (!/^\d+$/.test(quote.amountAtomic)) {
|
|
90
|
+
throw new RangeError(`Quoted amount is not an integer atomic value: ${quote.amountAtomic}`);
|
|
91
|
+
}
|
|
92
|
+
return Number(quote.amountAtomic) / 10 ** decimals;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// src/store.ts
|
|
96
|
+
function createMemorySpendStore(retentionMs = 24 * 60 * 60 * 1e3) {
|
|
97
|
+
let entries = [];
|
|
98
|
+
return {
|
|
99
|
+
record(entry) {
|
|
100
|
+
entries.push(entry);
|
|
101
|
+
const cutoff = entry.at - retentionMs;
|
|
102
|
+
if (entries.length > 64) entries = entries.filter((e) => e.at >= cutoff);
|
|
103
|
+
},
|
|
104
|
+
totalSince(sinceMs) {
|
|
105
|
+
let total = 0;
|
|
106
|
+
for (const entry of entries) if (entry.at >= sinceMs) total += entry.usd;
|
|
107
|
+
return total;
|
|
108
|
+
}
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// src/client.ts
|
|
113
|
+
var HOUR_MS = 60 * 60 * 1e3;
|
|
114
|
+
var DAY_MS = 24 * HOUR_MS;
|
|
115
|
+
function hostOf(input) {
|
|
116
|
+
try {
|
|
117
|
+
const raw = typeof input === "string" ? input : input instanceof URL ? input.toString() : input.url;
|
|
118
|
+
return new URL(raw).hostname.toLowerCase();
|
|
119
|
+
} catch {
|
|
120
|
+
return null;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
function hostAllowed(host, allowed) {
|
|
124
|
+
return allowed.some((entry) => {
|
|
125
|
+
const candidate = entry.trim().toLowerCase().replace(/^\*\./, "");
|
|
126
|
+
return host === candidate || host.endsWith(`.${candidate}`);
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
async function evaluateGuards(quoteUsd, guards, store, now) {
|
|
130
|
+
if (guards.maxPerCall !== void 0 && quoteUsd > guards.maxPerCall) {
|
|
131
|
+
throw new SpendGuardError(
|
|
132
|
+
"maxPerCall",
|
|
133
|
+
`Call costs $${quoteUsd} which exceeds the maxPerCall limit of $${guards.maxPerCall}`,
|
|
134
|
+
{ limitUsd: guards.maxPerCall, attemptedUsd: quoteUsd }
|
|
135
|
+
);
|
|
136
|
+
}
|
|
137
|
+
if (guards.maxPerHour !== void 0) {
|
|
138
|
+
const spent = await store.totalSince(now - HOUR_MS);
|
|
139
|
+
if (spent + quoteUsd > guards.maxPerHour) {
|
|
140
|
+
throw new SpendGuardError(
|
|
141
|
+
"maxPerHour",
|
|
142
|
+
`Call costs $${quoteUsd} and $${spent} was already spent this hour, exceeding the maxPerHour limit of $${guards.maxPerHour}`,
|
|
143
|
+
{ limitUsd: guards.maxPerHour, attemptedUsd: spent + quoteUsd }
|
|
144
|
+
);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
if (guards.maxPerDay !== void 0) {
|
|
148
|
+
const spent = await store.totalSince(now - DAY_MS);
|
|
149
|
+
if (spent + quoteUsd > guards.maxPerDay) {
|
|
150
|
+
throw new SpendGuardError(
|
|
151
|
+
"maxPerDay",
|
|
152
|
+
`Call costs $${quoteUsd} and $${spent} was already spent today, exceeding the maxPerDay limit of $${guards.maxPerDay}`,
|
|
153
|
+
{ limitUsd: guards.maxPerDay, attemptedUsd: spent + quoteUsd }
|
|
154
|
+
);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
async function readQuotedRequirements(response) {
|
|
159
|
+
const header = response.headers.get("payment-required");
|
|
160
|
+
if (header) {
|
|
161
|
+
try {
|
|
162
|
+
const decoded = (0, import_http.decodePaymentRequiredHeader)(header);
|
|
163
|
+
if (Array.isArray(decoded.accepts) && decoded.accepts.length > 0) return decoded.accepts;
|
|
164
|
+
} catch {
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
const body = await response.clone().json().catch(() => null);
|
|
168
|
+
const accepts = body?.accepts;
|
|
169
|
+
return Array.isArray(accepts) ? accepts : [];
|
|
170
|
+
}
|
|
171
|
+
function quoteUsdOf(requirements) {
|
|
172
|
+
const asRecord = requirements;
|
|
173
|
+
const amountAtomic = asRecord.maxAmountRequired ?? asRecord.amount;
|
|
174
|
+
if (!amountAtomic || !asRecord.asset) {
|
|
175
|
+
throw new RangeError("Payment requirements carry no priceable amount; refusing to pay.");
|
|
176
|
+
}
|
|
177
|
+
return quoteToUsd({
|
|
178
|
+
amountAtomic,
|
|
179
|
+
asset: asRecord.asset,
|
|
180
|
+
decimals: asRecord.extra?.decimals
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
async function payFetch(url, init, options) {
|
|
184
|
+
const guards = options.guards ?? {};
|
|
185
|
+
const store = options.store ?? createMemorySpendStore();
|
|
186
|
+
const now = options.now ?? Date.now;
|
|
187
|
+
const baseFetch = options.fetch ?? globalThis.fetch;
|
|
188
|
+
const networks = options.networks ?? ["base"];
|
|
189
|
+
if (guards.allowedHosts) {
|
|
190
|
+
const host = hostOf(url);
|
|
191
|
+
if (!host || !hostAllowed(host, guards.allowedHosts)) {
|
|
192
|
+
throw new SpendGuardError(
|
|
193
|
+
"allowedHosts",
|
|
194
|
+
`Host ${host ?? "<unparseable>"} is not in allowedHosts`,
|
|
195
|
+
{ host: host ?? void 0 }
|
|
196
|
+
);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
let quotedUsd = null;
|
|
200
|
+
const client = new import_client.x402Client((_version, requirements) => {
|
|
201
|
+
const affordable = requirements.filter((requirement) => {
|
|
202
|
+
const usd = quoteUsdOf(requirement);
|
|
203
|
+
return guards.maxPerCall === void 0 || usd <= guards.maxPerCall;
|
|
204
|
+
});
|
|
205
|
+
const chosen = (affordable.length > 0 ? affordable : requirements)[0];
|
|
206
|
+
if (!chosen) throw new RangeError("Resource offered no payment requirements");
|
|
207
|
+
quotedUsd = quoteUsdOf(chosen);
|
|
208
|
+
return chosen;
|
|
209
|
+
});
|
|
210
|
+
for (const name of networks) {
|
|
211
|
+
client.register(NETWORKS[name].caip2, new import_client2.ExactEvmScheme(options.wallet));
|
|
212
|
+
}
|
|
213
|
+
const guarded = async (input, requestInit) => {
|
|
214
|
+
const response2 = await baseFetch(input, requestInit);
|
|
215
|
+
if (response2.status !== 402) return response2;
|
|
216
|
+
const accepts = await readQuotedRequirements(response2);
|
|
217
|
+
if (accepts.length === 0) {
|
|
218
|
+
throw new UnreadableQuoteError(
|
|
219
|
+
"Received a 402 with no readable payment requirements (no payment-required header, no accepts body). Refusing to pay blind."
|
|
220
|
+
);
|
|
221
|
+
}
|
|
222
|
+
const cheapest = accepts.map((requirement) => quoteUsdOf(requirement)).sort((a, b) => a - b)[0];
|
|
223
|
+
if (cheapest !== void 0) {
|
|
224
|
+
await evaluateGuards(cheapest, guards, store, now());
|
|
225
|
+
}
|
|
226
|
+
return response2;
|
|
227
|
+
};
|
|
228
|
+
const paying = (0, import_fetch.wrapFetchWithPayment)(guarded, client);
|
|
229
|
+
const response = await paying(url, init);
|
|
230
|
+
if (readSettlementHeader(response.headers) && quotedUsd !== null) {
|
|
231
|
+
await store.record({ at: now(), usd: quotedUsd });
|
|
232
|
+
}
|
|
233
|
+
return response;
|
|
234
|
+
}
|
|
235
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
236
|
+
0 && (module.exports = {
|
|
237
|
+
PAYMENT_REF_HEADER,
|
|
238
|
+
evaluateGuards,
|
|
239
|
+
payFetch,
|
|
240
|
+
quoteUsdOf,
|
|
241
|
+
readQuotedRequirements
|
|
242
|
+
});
|
|
243
|
+
//# sourceMappingURL=client.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/client.ts","../src/constants.ts","../src/errors.ts","../src/money.ts","../src/store.ts"],"sourcesContent":["import { x402Client } from '@x402/core/client';\nimport { decodePaymentRequiredHeader } from '@x402/core/http';\nimport type { PaymentRequirements } from '@x402/core/types';\nimport { ExactEvmScheme } from '@x402/evm/exact/client';\nimport { wrapFetchWithPayment } from '@x402/fetch';\n\nimport { NETWORKS, PAYMENT_REF_HEADER, readSettlementHeader } from './constants.js';\nimport type { NetworkName } from './constants.js';\nimport { SpendGuardError, UnreadableQuoteError } from './errors.js';\nimport { quoteToUsd } from './money.js';\nimport { createMemorySpendStore } from './store.js';\nimport type { SpendStore } from './store.js';\n\n/** The signer the official EVM scheme expects: address + signTypedData, no key handling. */\nexport type EvmWallet = ConstructorParameters<typeof ExactEvmScheme>[0];\n\nexport interface SpendGuards {\n /** Hard USD ceiling for a single call. */\n maxPerCall?: number;\n /** Rolling 60-minute USD ceiling. */\n maxPerHour?: number;\n /** Rolling 24-hour USD ceiling. */\n maxPerDay?: number;\n /** Hostnames this agent may pay. Checked before any network call. */\n allowedHosts?: string[];\n}\n\nexport interface PayFetchOptions {\n wallet: EvmWallet;\n guards?: SpendGuards;\n /** Defaults to a process-local in-memory store. */\n store?: SpendStore;\n networks?: NetworkName[];\n fetch?: typeof globalThis.fetch;\n now?: () => number;\n}\n\nconst HOUR_MS = 60 * 60 * 1000;\nconst DAY_MS = 24 * HOUR_MS;\n\nfunction hostOf(input: RequestInfo | URL): string | null {\n try {\n const raw =\n typeof input === 'string'\n ? input\n : input instanceof URL\n ? input.toString()\n : (input as Request).url;\n return new URL(raw).hostname.toLowerCase();\n } catch {\n return null;\n }\n}\n\n/** `example.com` in allowedHosts also authorises `tool.example.com`. */\nfunction hostAllowed(host: string, allowed: readonly string[]): boolean {\n return allowed.some((entry) => {\n const candidate = entry.trim().toLowerCase().replace(/^\\*\\./, '');\n return host === candidate || host.endsWith(`.${candidate}`);\n });\n}\n\n/**\n * Checks every guard against a quote. Returns the USD value if the call may proceed,\n * throws SpendGuardError otherwise. Called before a payment payload exists.\n */\nexport async function evaluateGuards(\n quoteUsd: number,\n guards: SpendGuards,\n store: SpendStore,\n now: number,\n): Promise<void> {\n if (guards.maxPerCall !== undefined && quoteUsd > guards.maxPerCall) {\n throw new SpendGuardError(\n 'maxPerCall',\n `Call costs $${quoteUsd} which exceeds the maxPerCall limit of $${guards.maxPerCall}`,\n { limitUsd: guards.maxPerCall, attemptedUsd: quoteUsd },\n );\n }\n\n if (guards.maxPerHour !== undefined) {\n const spent = await store.totalSince(now - HOUR_MS);\n if (spent + quoteUsd > guards.maxPerHour) {\n throw new SpendGuardError(\n 'maxPerHour',\n `Call costs $${quoteUsd} and $${spent} was already spent this hour, exceeding the maxPerHour limit of $${guards.maxPerHour}`,\n { limitUsd: guards.maxPerHour, attemptedUsd: spent + quoteUsd },\n );\n }\n }\n\n if (guards.maxPerDay !== undefined) {\n const spent = await store.totalSince(now - DAY_MS);\n if (spent + quoteUsd > guards.maxPerDay) {\n throw new SpendGuardError(\n 'maxPerDay',\n `Call costs $${quoteUsd} and $${spent} was already spent today, exceeding the maxPerDay limit of $${guards.maxPerDay}`,\n { limitUsd: guards.maxPerDay, attemptedUsd: spent + quoteUsd },\n );\n }\n }\n}\n\n/**\n * Reads the offered requirements from a 402.\n *\n * x402 v2 carries them in the base64 `payment-required` header, not the body — the body\n * is the resource's own, and may be anything. v1 put them in the body, so both are read.\n */\nexport async function readQuotedRequirements(response: Response): Promise<PaymentRequirements[]> {\n const header = response.headers.get('payment-required');\n if (header) {\n try {\n const decoded = decodePaymentRequiredHeader(header);\n if (Array.isArray(decoded.accepts) && decoded.accepts.length > 0) return decoded.accepts;\n } catch {\n // Fall through to the body form.\n }\n }\n\n const body: unknown = await response\n .clone()\n .json()\n .catch(() => null);\n const accepts = (body as { accepts?: PaymentRequirements[] } | null)?.accepts;\n return Array.isArray(accepts) ? accepts : [];\n}\n\n/** Reads the quoted amount out of x402 payment requirements. */\ninterface PriceableRequirement {\n maxAmountRequired?: string;\n amount?: string;\n asset?: string;\n extra?: { decimals?: number };\n}\n\nexport function quoteUsdOf(requirements: PaymentRequirements): number {\n const asRecord: PriceableRequirement = requirements;\n const amountAtomic = asRecord.maxAmountRequired ?? asRecord.amount;\n if (!amountAtomic || !asRecord.asset) {\n throw new RangeError('Payment requirements carry no priceable amount; refusing to pay.');\n }\n return quoteToUsd({\n amountAtomic,\n asset: asRecord.asset,\n decimals: asRecord.extra?.decimals,\n });\n}\n\n/**\n * A `fetch` that pays for 402 responses, under spend guards.\n *\n * The 402 handling, signing and retry are the official x402 packages' work. What this adds\n * is refusal: guards are evaluated inside the payment-policy hook, which runs after the\n * quote is known and before any payload is signed, so exceeding a guard throws\n * SpendGuardError with nothing signed and nothing spent.\n */\nexport async function payFetch(\n url: RequestInfo | URL,\n init: RequestInit | undefined,\n options: PayFetchOptions,\n): Promise<Response> {\n const guards = options.guards ?? {};\n const store = options.store ?? createMemorySpendStore();\n const now = options.now ?? Date.now;\n const baseFetch = options.fetch ?? globalThis.fetch;\n const networks = options.networks ?? (['base'] as NetworkName[]);\n\n // Host allowlist is checked first: an unauthorised host should cost no request at all.\n if (guards.allowedHosts) {\n const host = hostOf(url);\n if (!host || !hostAllowed(host, guards.allowedHosts)) {\n throw new SpendGuardError(\n 'allowedHosts',\n `Host ${host ?? '<unparseable>'} is not in allowedHosts`,\n { host: host ?? undefined },\n );\n }\n }\n\n let quotedUsd: number | null = null;\n\n const client = new x402Client((_version, requirements) => {\n // Runs before the payment payload is created. Throwing here means no signature.\n const affordable = requirements.filter((requirement) => {\n const usd = quoteUsdOf(requirement);\n return guards.maxPerCall === undefined || usd <= guards.maxPerCall;\n });\n\n const chosen = (affordable.length > 0 ? affordable : requirements)[0];\n if (!chosen) throw new RangeError('Resource offered no payment requirements');\n\n quotedUsd = quoteUsdOf(chosen);\n return chosen;\n });\n\n for (const name of networks) {\n client.register(NETWORKS[name].caip2, new ExactEvmScheme(options.wallet));\n }\n\n const guarded: typeof globalThis.fetch = async (input, requestInit) => {\n const response = await baseFetch(input, requestInit);\n if (response.status !== 402) return response;\n\n // Peek at the quote and apply guards before the wrapper signs anything.\n const accepts = await readQuotedRequirements(response);\n if (accepts.length === 0) {\n // A 402 whose requirements we cannot read is a 402 we cannot price, and an\n // unpriced call cannot be checked against a spend cap. Refuse rather than let it\n // through unguarded.\n throw new UnreadableQuoteError(\n 'Received a 402 with no readable payment requirements (no payment-required header, no accepts body). Refusing to pay blind.',\n );\n }\n\n const cheapest = accepts.map((requirement) => quoteUsdOf(requirement)).sort((a, b) => a - b)[0];\n if (cheapest !== undefined) {\n await evaluateGuards(cheapest, guards, store, now());\n }\n return response;\n };\n\n const paying = wrapFetchWithPayment(guarded, client);\n const response = await paying(url as RequestInfo, init);\n\n // Record the spend only once a payment actually settled.\n if (readSettlementHeader(response.headers) && quotedUsd !== null) {\n await store.record({ at: now(), usd: quotedUsd });\n }\n\n return response;\n}\n\nexport { PAYMENT_REF_HEADER };\n","/** Base networks in CAIP-2 form, which is what x402 v2 speaks. */\n/**\n * The EIP-712 domain a payer signs the EIP-3009 authorisation against is **not** listed\n * here on purpose: it differs per network (mainnet USDC is \"USD Coin\", the Sepolia\n * deployment is \"USDC\"), and the EVM scheme owns the authoritative table. Quoting a\n * dollar price lets it resolve the asset and publish that domain in the 402; naming an\n * explicit asset bypasses the lookup and emits `extra: {}`, which no payer can sign\n * against.\n */\nexport const NETWORKS = {\n base: {\n caip2: 'eip155:8453',\n chainId: 8453,\n usdc: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913',\n },\n 'base-sepolia': {\n caip2: 'eip155:84532',\n chainId: 84532,\n usdc: '0x036CbD53842c5426634e7929541eC2318f3dCF7e',\n },\n} as const;\n\nexport type NetworkName = keyof typeof NETWORKS;\n\n/** USDC is 6 decimals on Base. */\nexport const USDC_DECIMALS = 6;\n\n/**\n * Required on every 402 body and every payment-facing doc page. A payment is a direct\n * on-chain transfer between two wallets: once settled nobody, Fatstack included, can\n * reverse it.\n */\nexport const NO_REFUNDS_NOTICE =\n 'Payments are final. Once settled on-chain the transfer cannot be reversed, and there are no refunds.';\n\nexport const DOCS_URL = 'https://fatstack.net/docs/payments';\n\n/** Correlates a settled payment with the indexer's view of the on-chain transfer. */\nexport const PAYMENT_REF_HEADER = 'X-Fatstack-Payment-Ref';\n\n/**\n * x402 v2 dropped the `X-` prefix: the settlement receipt is `PAYMENT-RESPONSE` and the\n * payer's payload is `PAYMENT-SIGNATURE`. The v1 spellings are still read so a v1 payer\n * or resource keeps working. Header names are case-insensitive; these are lowercase\n * because `Headers.get` normalises.\n */\nexport const SETTLEMENT_HEADERS = ['payment-response', 'x-payment-response'] as const;\nexport const PAYMENT_SIGNATURE_HEADERS = ['payment-signature', 'x-payment'] as const;\n\n/** First settlement receipt present on a response, in either spelling. */\nexport function readSettlementHeader(headers: Headers): string | null {\n for (const name of SETTLEMENT_HEADERS) {\n const value = headers.get(name);\n if (value) return value;\n }\n return null;\n}\n\nexport const DEFAULT_FACILITATOR_URL = 'https://x402.org/facilitator';\n","/** Names of the spend guards an agent can set. */\nexport type GuardName = 'maxPerCall' | 'maxPerHour' | 'maxPerDay' | 'allowedHosts';\n\n/**\n * Thrown before anything is signed when a call would breach a spend guard.\n *\n * Payments are final, so the only place to stop an unwanted spend is before the\n * signature exists. Every guard raises this, and it is never thrown after a payment\n * payload has been created.\n */\nexport class SpendGuardError extends Error {\n override readonly name = 'SpendGuardError';\n\n constructor(\n readonly guard: GuardName,\n message: string,\n readonly detail: { limitUsd?: number; attemptedUsd?: number; host?: string } = {},\n ) {\n super(message);\n }\n}\n\n/** Thrown by seams that are typed and reachable but deliberately not built yet. */\nexport class NotImplementedError extends Error {\n override readonly name = 'NotImplementedError';\n\n constructor(feature: string) {\n super(`${feature} is not implemented in this build.`);\n }\n}\n\n/** Thrown when an optional framework adapter is used without its package installed. */\nexport class MissingAdapterError extends Error {\n override readonly name = 'MissingAdapterError';\n\n constructor(pkg: string) {\n super(`${pkg} is not installed. Add it to use this adapter: pnpm add ${pkg}`);\n }\n}\n\n/**\n * A 402 arrived whose payment requirements could not be read, so the call cannot be\n * priced and therefore cannot be checked against a spend cap. Refusing is the safe\n * outcome: paying blind is irreversible.\n */\nexport class UnreadableQuoteError extends Error {\n override readonly name = 'UnreadableQuoteError';\n\n constructor(message: string) {\n super(message);\n }\n}\n","import { NETWORKS, USDC_DECIMALS } from './constants.js';\n\nconst UNITS_PER_USDC = 10n ** BigInt(USDC_DECIMALS);\n\n/** \"1.25\" -> 1250000n. Rejects anything that is not a plain non-negative decimal. */\nexport function parseUsdc(input: string): bigint {\n const match = /^(\\d+)(?:\\.(\\d{1,6}))?$/.exec(input.trim());\n if (!match) {\n throw new RangeError(`Not a USDC amount with at most ${USDC_DECIMALS} decimals: ${input}`);\n }\n const whole = BigInt(match[1] ?? '0');\n const fraction = BigInt((match[2] ?? '').padEnd(USDC_DECIMALS, '0') || '0');\n return whole * UNITS_PER_USDC + fraction;\n}\n\n/** 1250000n -> \"1.25\". Trailing zeros trimmed. */\nexport function formatUsdc(atomic: bigint): string {\n if (atomic < 0n) throw new RangeError('USDC amounts are never negative');\n const whole = atomic / UNITS_PER_USDC;\n const fraction = (atomic % UNITS_PER_USDC).toString().padStart(USDC_DECIMALS, '0');\n const trimmed = fraction.replace(/0+$/, '');\n return trimmed ? `${whole}.${trimmed}` : whole.toString();\n}\n\nconst USDC_ADDRESSES = new Set(\n Object.values(NETWORKS).map((network) => network.usdc.toLowerCase()),\n);\n\n/**\n * Converts a quoted amount to USD for guard evaluation.\n *\n * Fails closed: if the asset is not a USDC contract we recognise and the quote carries no\n * usable `decimals`, this throws rather than guessing. Guessing here would let an\n * unrecognised token slip past a spend cap, and the payment is irreversible.\n */\nexport function quoteToUsd(quote: {\n amountAtomic: string;\n asset: string;\n decimals?: number | undefined;\n}): number {\n const decimals = USDC_ADDRESSES.has(quote.asset.toLowerCase()) ? USDC_DECIMALS : quote.decimals;\n\n if (decimals === undefined || !Number.isInteger(decimals) || decimals < 0 || decimals > 36) {\n throw new RangeError(\n `Cannot price asset ${quote.asset}: unknown decimals. Refusing to evaluate spend guards against an unpriceable quote.`,\n );\n }\n\n if (!/^\\d+$/.test(quote.amountAtomic)) {\n throw new RangeError(`Quoted amount is not an integer atomic value: ${quote.amountAtomic}`);\n }\n\n return Number(quote.amountAtomic) / 10 ** decimals;\n}\n","/** One recorded spend, in USD, at a wall-clock millisecond. */\nexport interface SpendRecord {\n at: number;\n usd: number;\n}\n\n/**\n * Where rolling spend totals live. The default is in-memory and per-process; swap in a\n * shared implementation (Redis, Durable Object, Postgres) when an agent runs as more\n * than one process, otherwise each process enforces its own separate budget.\n */\nexport interface SpendStore {\n record(entry: SpendRecord): Promise<void> | void;\n /** Total USD recorded at or after `sinceMs`. */\n totalSince(sinceMs: number): Promise<number> | number;\n}\n\n/** Process-local store. Entries older than the longest window are dropped on write. */\nexport function createMemorySpendStore(retentionMs = 24 * 60 * 60 * 1000): SpendStore {\n let entries: SpendRecord[] = [];\n\n return {\n record(entry) {\n entries.push(entry);\n const cutoff = entry.at - retentionMs;\n if (entries.length > 64) entries = entries.filter((e) => e.at >= cutoff);\n },\n totalSince(sinceMs) {\n let total = 0;\n for (const entry of entries) if (entry.at >= sinceMs) total += entry.usd;\n return total;\n },\n };\n}\n"],"mappings":"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","names":["import_client","response"]}
|
package/dist/client.d.ts
ADDED
package/dist/client.js
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import {
|
|
2
|
+
evaluateGuards,
|
|
3
|
+
payFetch,
|
|
4
|
+
quoteUsdOf,
|
|
5
|
+
readQuotedRequirements
|
|
6
|
+
} from "./chunk-ZGVPNFNS.js";
|
|
7
|
+
import {
|
|
8
|
+
PAYMENT_REF_HEADER
|
|
9
|
+
} from "./chunk-C4BASAUA.js";
|
|
10
|
+
export {
|
|
11
|
+
PAYMENT_REF_HEADER,
|
|
12
|
+
evaluateGuards,
|
|
13
|
+
payFetch,
|
|
14
|
+
quoteUsdOf,
|
|
15
|
+
readQuotedRequirements
|
|
16
|
+
};
|
|
17
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|