@piprail/sdk 2.7.0 → 2.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/CHANGELOG.md +96 -0
  2. package/README.md +5 -3
  3. package/dist/{algorand-WB6PBJU4.js → algorand-AA3WXKW4.js} +1 -1
  4. package/dist/{algorand-TMA62DN2.cjs → algorand-FCEECDG6.cjs} +32 -32
  5. package/dist/{aptos-JQMZNTMD.cjs → aptos-GHJPO6JJ.cjs} +31 -31
  6. package/dist/{aptos-QAAXIUY3.js → aptos-LY67Q6QF.js} +1 -1
  7. package/dist/{chunk-JG6KRAW6.cjs → chunk-MWBT7MCE.cjs} +13 -1
  8. package/dist/chunk-O4UQOZ4Z.cjs +14 -0
  9. package/dist/{chunk-7XK22JSQ.js → chunk-SC2ZYDHD.js} +12 -0
  10. package/dist/chunk-SK3CB7UA.js +14 -0
  11. package/dist/index.cjs +754 -238
  12. package/dist/index.d.cts +2386 -2577
  13. package/dist/index.d.ts +2386 -2577
  14. package/dist/index.js +595 -79
  15. package/dist/ledger-BtzrfO-3.d.cts +658 -0
  16. package/dist/ledger-BtzrfO-3.d.ts +658 -0
  17. package/dist/{near-H5AQ253I.cjs → near-6KAQVVG2.cjs} +26 -26
  18. package/dist/{near-OTPQD6BI.js → near-FZBUICCS.js} +1 -1
  19. package/dist/node.cjs +38 -0
  20. package/dist/node.d.cts +16 -0
  21. package/dist/node.d.ts +16 -0
  22. package/dist/node.js +38 -0
  23. package/dist/{solana-3FMCWSEE.js → solana-ELUWO6N5.js} +33 -2
  24. package/dist/{solana-M3VOHCMO.cjs → solana-MYF4HBO4.cjs} +64 -33
  25. package/dist/{stellar-U5NCRIOJ.js → stellar-BEMT7UYF.js} +1 -1
  26. package/dist/{stellar-FW6C6FBE.cjs → stellar-SUKASK4N.cjs} +20 -20
  27. package/dist/{sui-Y53M4GUM.js → sui-F5JQ2N6I.js} +1 -1
  28. package/dist/{sui-47C2KEZI.cjs → sui-VE5LT7BL.cjs} +16 -16
  29. package/dist/{ton-5ZPT5PSP.js → ton-7GKCTC5H.js} +11 -7
  30. package/dist/{ton-MMPKWT6N.cjs → ton-AOR3EURW.cjs} +25 -21
  31. package/dist/{tron-JOT4STIG.cjs → tron-EMFXDFHW.cjs} +24 -24
  32. package/dist/{tron-WYS4X2I5.js → tron-ZZZS3FNN.js} +1 -1
  33. package/dist/{xrpl-2MZEOIFY.js → xrpl-DD7TJL5L.js} +1 -1
  34. package/dist/{xrpl-PECT4IMX.cjs → xrpl-Y6SQNYLC.cjs} +20 -20
  35. package/package.json +11 -1
@@ -0,0 +1,658 @@
1
+ /**
2
+ * The on-the-wire payment protocol. Self-contained — it runs entirely
3
+ * between the agent and your server, with nothing hosted in between.
4
+ *
5
+ * Lowercase headers, no `X-` prefix:
6
+ * - payment-required (server → client, base64 JSON challenge)
7
+ * - payment-signature (client → server, base64 JSON proof)
8
+ * - payment-response (server → client, base64 JSON receipt on 200)
9
+ *
10
+ * Scheme is `onchain-proof`: the agent pays on-chain and hands back a proof
11
+ * reference (an EVM tx hash, a Solana signature, …); the server verifies
12
+ * that transaction itself, locally, against its own RPC. No third party.
13
+ *
14
+ * This file is CHAIN-AGNOSTIC. Identifiers are plain strings in CAIP-2 /
15
+ * base-unit form so any family (EVM, Solana, …) round-trips through the same
16
+ * envelopes. Each PaymentDriver interprets them for its own chain.
17
+ *
18
+ * Wire format targets x402 v2 — github.com/coinbase/x402, specs/x402-specification-v2.md
19
+ * (v2.0, 2025-12-9) + specs/transports-v2/http.md. The ENVELOPE is v2-conformant
20
+ * (PaymentPayload carries `accepted`; SettlementResponse has `success` + `transaction`).
21
+ * The SETTLEMENT SCHEME is our own `onchain-proof` (client pays on-chain first, proves
22
+ * with a tx ref, server verifies locally) — permitted by spec §6 (a scheme owns its
23
+ * `payload`) + §7 (self-hosted verification), but it does NOT interoperate with the
24
+ * built-in `exact` scheme (which is signature + facilitator-broadcast). Deliberate.
25
+ */
26
+ /** A CAIP-2 network id, e.g. `eip155:8453` or `solana:5eykt4Us…`. */
27
+ type Caip2 = `${string}:${string}`;
28
+ /** An asset id — chain-specific: an EVM `0x…` address, a Solana base58 mint, a
29
+ * TON jetton master, a Stellar `CODE:ISSUER`, or `'native'`. */
30
+ type AssetId = string;
31
+ /** An account id — chain-specific: an EVM `0x…` address, a Solana base58 pubkey,
32
+ * a TON address, or a Stellar `G…` account. */
33
+ type AddressId = string;
34
+ interface X402ResourceObject {
35
+ url: string;
36
+ description?: string;
37
+ /** The resource's response content-type, e.g. 'application/json' (v2 ResourceInfo, optional). */
38
+ mimeType?: string;
39
+ }
40
+ interface X402AcceptEntry {
41
+ scheme: 'onchain-proof';
42
+ network: Caip2;
43
+ /** Amount in the token's base units (already scaled by decimals). */
44
+ amount: string;
45
+ /** ERC-20 address / SPL mint, or 'native' for the chain's native coin. */
46
+ asset: AssetId;
47
+ payTo: AddressId;
48
+ /** Payment is only accepted if mined within this many seconds of now. */
49
+ maxTimeoutSeconds: number;
50
+ extra: {
51
+ /** Single-use id echoed back in the proof. */
52
+ nonce: string;
53
+ /** Token decimals, so the client can render the amount. */
54
+ decimals: number;
55
+ /** Confirmations the client should wait before retrying. */
56
+ minConfirmations: number;
57
+ /** Human-readable amount, e.g. "0.05". */
58
+ amountFormatted: string;
59
+ symbol?: string;
60
+ };
61
+ }
62
+ /**
63
+ * A standard x402 `exact` rail (EVM / EIP-3009) — the interop rail a PipRail gate
64
+ * advertises ALONGSIDE its `onchain-proof` rail (dual-advertise) so any standard
65
+ * x402 client can pay it. Same v2 PaymentRequirements skeleton as
66
+ * {@link X402AcceptEntry}; only `scheme` and `extra` differ. The `extra` carries
67
+ * the EIP-712 domain a payer signs over — `name`/`version` are READ from the token
68
+ * contract by the gate (never assumed), since e.g. USDC's domain name is "USD Coin",
69
+ * not the "USDC" symbol.
70
+ */
71
+ interface X402ExactAcceptEntry {
72
+ scheme: 'exact';
73
+ network: Caip2;
74
+ amount: string;
75
+ asset: AssetId;
76
+ payTo: AddressId;
77
+ maxTimeoutSeconds: number;
78
+ extra: {
79
+ /** The exact transfer method. EVM: `'eip3009'` for tokens with native
80
+ * `transferWithAuthorization`, or `'permit2'` for tokens WITHOUT it (e.g.
81
+ * Binance-Peg USDC on BNB) — the payer signs a Permit2 witness transfer whose
82
+ * `spender` is the canonical x402ExactPermit2Proxy and whose `witness.to` binds the
83
+ * recipient. **Solana (SVM): `'svm'`** — the payer partial-signs an SPL
84
+ * `TransferChecked` transaction whose fee payer is the merchant (`feePayer` below),
85
+ * and the gate co-signs as fee payer + broadcasts. **Algorand: `'algorand'`** — the payer
86
+ * signs an ASA `axfer` to `payTo` at fee 0, atomically grouped with a 0-ALGO `pay` from
87
+ * the `feePayer` that pools the group fee (per `scheme_exact_algo.md`); the gate (or a
88
+ * keyless facilitator) signs that fee txn + submits. **Aptos: `'aptos'`** — the payer signs a
89
+ * fee-payer (sponsored) `primary_fungible_store::transfer` to `payTo` (per
90
+ * `scheme_exact_aptos.md`); the gate (or a keyless facilitator) adds the fee-payer signature
91
+ * + submits, paying gas. **NEAR: `'near'`** — the payer signs a NEP-366 `SignedDelegateAction`
92
+ * authorizing exactly one NEP-141 `ft_transfer` to `payTo` (per `scheme_exact_near.md`); a
93
+ * facilitator-selected relayer (`feePayer` below) prepays gas + the 1 yoctoNEAR and submits, so
94
+ * the buyer holds zero NEAR. PipRail self-settles ALL. */
95
+ assetTransferMethod: 'eip3009' | 'permit2' | 'svm' | 'algorand' | 'aptos' | 'near';
96
+ /** EIP-712 domain name of the token. OPTIONAL per the exact-EVM scheme (only
97
+ * `assetTransferMethod` is required) — a foreign rail may omit it. NEVER assumed
98
+ * from the symbol (USDC's on-chain name() is "USD Coin", not "USDC"); a PipRail gate
99
+ * READS it on-chain, and the PipRail buyer RE-DERIVES it on-chain and ignores this. */
100
+ name?: string;
101
+ /** EIP-712 domain version of the token (USDC: "2"). OPTIONAL (see `name`); read/re-derived on-chain. */
102
+ version?: string;
103
+ /** **SVM / Algorand / Aptos** — the fee-payer (gas sponsor) address. The buyer builds the
104
+ * transaction with this account as the gas payer (so the buyer spends ZERO native coin),
105
+ * leaving its signature for whoever sponsors; the gate (self mode) or a keyless facilitator
106
+ * fills it and submits. On **SVM** it must differ from `payTo` (the fee payer must never
107
+ * appear in an instruction — a MUST-rule); on **Algorand/Aptos** the fee txn/signature is
108
+ * separate from the transfer, so `feePayer === payTo` is allowed. */
109
+ feePayer?: string;
110
+ /** **SVM only, OPTIONAL** — a ≤256-byte reconciliation memo the buyer attaches to the
111
+ * transaction (the SVM scheme's optional `extra.memo`). */
112
+ memo?: string;
113
+ /** **SVM only** — which SPL token program the mint belongs to, so both the buyer and
114
+ * the gate derive the SAME associated-token-account address (an ATA's address depends
115
+ * on the token program). Defaults to `'spl-token'` (classic) when absent — the
116
+ * built-in USDC/USDT are classic. */
117
+ tokenProgram?: 'spl-token' | 'token-2022';
118
+ /** Confirmations the gate waits for before granting access — mirrors the gate's
119
+ * `minConfirmations`, so the exact rail honours the same reorg safety as onchain-proof.
120
+ * A PipRail convenience (standard clients ignore unknown keys). */
121
+ minConfirmations?: number;
122
+ /** Token decimals — a PipRail convenience (standard clients ignore unknown keys). */
123
+ decimals?: number;
124
+ /** Human-readable amount, e.g. "0.05" — a PipRail convenience. */
125
+ amountFormatted?: string;
126
+ symbol?: string;
127
+ };
128
+ }
129
+ /** A challenge `accepts[]` entry — either PipRail's `onchain-proof` rail or a standard `exact` rail. */
130
+ type X402AnyAccept = X402AcceptEntry | X402ExactAcceptEntry;
131
+ interface X402Challenge {
132
+ x402Version: 2;
133
+ /**
134
+ * Optional human-readable reason (v2 `error?: string`). PipRail EMITS it only on a
135
+ * rejected-proof re-challenge (omitted on a fresh challenge). Typed to also tolerate
136
+ * `null` when PARSING a foreign challenge — some deployed servers send `error: null`.
137
+ */
138
+ error?: string | null;
139
+ resource: X402ResourceObject;
140
+ accepts: X402AnyAccept[];
141
+ /** v2 optional extensions. PipRail stamps the machine-readable rejection reason here on a
142
+ * rejected-proof re-challenge: `{ piprail: { code, detail } }`. Omitted otherwise. */
143
+ extensions?: Record<string, unknown>;
144
+ }
145
+ interface X402PaymentSignature {
146
+ x402Version: 2;
147
+ /**
148
+ * x402 v2 PaymentPayload: the full PaymentRequirements entry the client chose
149
+ * (carries `scheme` + `network`), echoed back from the challenge's `accepts[]`.
150
+ */
151
+ accepted: X402AcceptEntry;
152
+ /**
153
+ * Scheme-defined payload. For `onchain-proof`: the challenge nonce + the proof
154
+ * ref (`txHash` — a chain-specific id: an EVM tx hash, a Solana signature, a
155
+ * TON locator, or a Stellar tx hash).
156
+ */
157
+ payload: {
158
+ nonce: string;
159
+ txHash: string;
160
+ };
161
+ }
162
+ /**
163
+ * The EIP-3009 authorization a payer signs for a standard `exact` rail. All
164
+ * numeric fields are DECIMAL strings on the wire (value, validAfter, validBefore);
165
+ * `nonce` is a 0x-prefixed 32-byte hex. Identical shape across x402 v1 and v2.
166
+ */
167
+ interface ExactAuthorizationWire {
168
+ from: string;
169
+ to: string;
170
+ value: string;
171
+ validAfter: string;
172
+ validBefore: string;
173
+ nonce: string;
174
+ }
175
+ /** The `payload` a client sends for an `exact` rail: an EIP-3009 signature + its authorization. */
176
+ interface ExactPaymentPayload {
177
+ signature: string;
178
+ authorization: ExactAuthorizationWire;
179
+ }
180
+ /**
181
+ * The `permit2Authorization` a payer signs for the `permit2` variant of the x402
182
+ * `exact` EVM scheme (tokens without EIP-3009 — e.g. Binance-Peg USDC on BNB). It is
183
+ * an EIP-712 `PermitWitnessTransferFrom` over the canonical Permit2 contract, whose
184
+ * `spender` is the canonical **x402ExactPermit2Proxy** and whose **witness** binds the
185
+ * recipient (`to`) + an activation time (`validAfter`). All numeric fields are DECIMAL
186
+ * strings on the wire (`permitted.amount`, `nonce`, `deadline`, `witness.validAfter`).
187
+ */
188
+ interface Permit2Authorization {
189
+ /** What may be pulled: the ERC-20 token + the exact base-unit amount. */
190
+ permitted: {
191
+ token: string;
192
+ amount: string;
193
+ };
194
+ /** The payer (token owner). */
195
+ from: string;
196
+ /** The signature's allowed spender — the canonical x402ExactPermit2Proxy. */
197
+ spender: string;
198
+ /** Permit2 unordered nonce (a uint256, decimal string). Single-use via its bitmap. */
199
+ nonce: string;
200
+ /** Unix-seconds signature expiry. */
201
+ deadline: string;
202
+ /** The proxy-enforced witness: funds go ONLY to `to`, and not before `validAfter`. */
203
+ witness: {
204
+ to: string;
205
+ validAfter: string;
206
+ };
207
+ }
208
+ /** The `payload` a client sends for the `permit2` exact variant: a signature + its Permit2 authorization. */
209
+ interface Permit2PaymentPayload {
210
+ signature: string;
211
+ permit2Authorization: Permit2Authorization;
212
+ }
213
+ /**
214
+ * The `payload` a client sends for the **SVM (Solana) `exact`** variant: a base64-encoded,
215
+ * serialized, **partially-signed** versioned Solana transaction (the buyer's `TransferChecked`
216
+ * with the merchant as fee payer; the fee-payer signature slot is left empty for the gate to
217
+ * fill). Per `scheme_exact_svm.md`. The transaction itself IS the proof — there's no separate
218
+ * authorization object (the SVM analogue of EIP-3009's `authorization`).
219
+ */
220
+ interface ExactSvmPaymentPayload {
221
+ transaction: string;
222
+ }
223
+ /**
224
+ * The `payload` a client sends for the **Algorand `exact`** variant, per
225
+ * `scheme_exact_algo.md`: an atomically-grouped set of base64-encoded msgpack transactions,
226
+ * and the index within it of the transaction that pays the resource server. The buyer's ASA
227
+ * `axfer` (to `payTo`, fee 0) is SIGNED; a 0-ALGO `pay` from the `feePayer` that pools the
228
+ * group fee is left UNSIGNED for whoever sponsors (the gate's relayer in self mode, or a
229
+ * keyless facilitator). The group itself IS the proof — there's no separate authorization
230
+ * object (the Algorand analogue of EIP-3009's `authorization` / SVM's `transaction`).
231
+ */
232
+ interface ExactAlgorandPaymentPayload {
233
+ /** Index into `paymentGroup` of the txn that pays the resource server (the buyer's `axfer`). */
234
+ paymentIndex: number;
235
+ /** The atomic group: each element is a base64-encoded, msgpack-encoded (signed or unsigned)
236
+ * Algorand transaction. ≤ 16 elements (the protocol's atomic-group cap). */
237
+ paymentGroup: string[];
238
+ }
239
+ /**
240
+ * The `payload` a client sends for the **Aptos `exact`** variant, per `scheme_exact_aptos.md`:
241
+ * a fee-payer (sponsored, AIP-39) `primary_fungible_store::transfer`. `transaction` is the base64
242
+ * BCS-serialized `SimpleTransaction` (raw tx + the bound `feePayerAddress`); `senderAuth` is the
243
+ * base64 BCS-serialized buyer (sender) authenticator. The buyer leaves the fee-payer signature for
244
+ * whoever sponsors (the gate's relayer in self mode, or a keyless facilitator), who adds it +
245
+ * submits. The (tx + sender authenticator) IS the proof — there's no separate `authorization`
246
+ * object (the Aptos analogue of EIP-3009's `authorization` / SVM's `transaction`). The two-field
247
+ * shape (a `senderAuth` alongside `transaction`) also distinguishes it from the SVM payload.
248
+ */
249
+ interface ExactAptosPaymentPayload {
250
+ /** Base64 BCS-serialized `SimpleTransaction` (raw transaction + the bound `feePayerAddress`). */
251
+ transaction: string;
252
+ /** Base64 BCS-serialized buyer (sender) `AccountAuthenticator`. */
253
+ senderAuth: string;
254
+ }
255
+ /**
256
+ * The `payload` a client sends for the **NEAR `exact`** variant, per `scheme_exact_near.md`:
257
+ * a base64-encoded, Borsh-serialized NEP-366 `SignedDelegateAction` whose single delegated action
258
+ * is one NEP-141 `ft_transfer` (to `payTo`, the exact `amount`, `deposit: 1` yoctoNEAR). The buyer
259
+ * signs the delegate action with a FULL-ACCESS key (a function-call key can't attach the 1 yocto and
260
+ * is rejected); a facilitator-selected relayer wraps it, prepays gas + the yocto, and submits. The
261
+ * signed delegate action IS the proof — there's no separate authorization object (the NEAR analogue
262
+ * of EIP-3009's `authorization` / SVM's `transaction`). Its single self-contained string field also
263
+ * distinguishes it from every other family's payload shape.
264
+ */
265
+ interface ExactNearPaymentPayload {
266
+ /** Base64 of the Borsh-encoded NEP-366 `SignedDelegateAction` (one `ft_transfer`). */
267
+ signedDelegateAction: string;
268
+ }
269
+ /** Any `exact`-rail payload shape — EIP-3009 (`authorization`), Permit2 (`permit2Authorization`),
270
+ * SVM (`transaction`), Algorand (`paymentGroup`), Aptos (`transaction` + `senderAuth`), or NEAR
271
+ * (`signedDelegateAction`). */
272
+ type ExactPaymentPayloadAny = ExactPaymentPayload | Permit2PaymentPayload | ExactSvmPaymentPayload | ExactAlgorandPaymentPayload | ExactAptosPaymentPayload | ExactNearPaymentPayload;
273
+ interface ParsedExactBase {
274
+ x402Version: number;
275
+ /** The client's claimed network (slug or CAIP-2) — for matching, not trust. */
276
+ network: string;
277
+ /** The client's claimed asset, if present (v2 `accepted.asset`). */
278
+ asset?: string;
279
+ /** The full decoded PaymentPayload, for verbatim forwarding to a facilitator (Mode B). */
280
+ raw: Record<string, unknown>;
281
+ }
282
+ /**
283
+ * What {@link parseExactPaymentHeader} extracts from an inbound `exact` payment,
284
+ * normalised across the v1 (`X-PAYMENT`, flat `{scheme,network,payload}`, network slug)
285
+ * and v2 (`PAYMENT-SIGNATURE`, `{accepted,payload}`, CAIP-2 network) wire shapes.
286
+ * `network`/`asset` are the CLIENT's claim — used only to MATCH an offered rail; the gate
287
+ * re-derives every verified field from its own trusted rail. A discriminated union on
288
+ * `method`, so narrowing on `method` narrows `payload`: `'eip3009'` → {@link ExactPaymentPayload}
289
+ * (`authorization`), `'permit2'` → {@link Permit2PaymentPayload} (`permit2Authorization`),
290
+ * `'svm'` → {@link ExactSvmPaymentPayload} (`transaction`), `'algorand'` →
291
+ * {@link ExactAlgorandPaymentPayload} (`paymentGroup`); `'aptos'` →
292
+ * {@link ExactAptosPaymentPayload} (`transaction` + `senderAuth`); `'near'` →
293
+ * {@link ExactNearPaymentPayload} (`signedDelegateAction`).
294
+ */
295
+ type ParsedExactPayment = (ParsedExactBase & {
296
+ method: 'eip3009';
297
+ payload: ExactPaymentPayload;
298
+ }) | (ParsedExactBase & {
299
+ method: 'permit2';
300
+ payload: Permit2PaymentPayload;
301
+ }) | (ParsedExactBase & {
302
+ method: 'svm';
303
+ payload: ExactSvmPaymentPayload;
304
+ }) | (ParsedExactBase & {
305
+ method: 'algorand';
306
+ payload: ExactAlgorandPaymentPayload;
307
+ }) | (ParsedExactBase & {
308
+ method: 'aptos';
309
+ payload: ExactAptosPaymentPayload;
310
+ }) | (ParsedExactBase & {
311
+ method: 'near';
312
+ payload: ExactNearPaymentPayload;
313
+ });
314
+ interface X402Receipt {
315
+ scheme: 'onchain-proof' | 'exact';
316
+ /**
317
+ * x402 v2 SettlementResponse: settlement succeeded. Always `true` here — a
318
+ * failed verification returns a 402, never a receipt.
319
+ */
320
+ success: true;
321
+ network: Caip2;
322
+ /**
323
+ * x402 v2 SettlementResponse: the on-chain transaction id of the SETTLED
324
+ * payment — a chain-specific id (an EVM/Tron/Stellar/XRPL/NEAR tx hash, a
325
+ * Solana signature, or a Sui digest). This is the verified tx itself, NOT the
326
+ * submit-time proof ref in `payload.txHash` (which can be a composite locator
327
+ * on TON/NEAR). (Was `txHash` before v2 envelope conformance.)
328
+ */
329
+ transaction: string;
330
+ asset: AssetId;
331
+ amount: string;
332
+ payer: AddressId;
333
+ payTo: AddressId;
334
+ verifiedAt: string;
335
+ }
336
+ /**
337
+ * The settled-payment record handed to a gate's `onPaid` hook — the wire
338
+ * {@link X402Receipt} plus the merchant-facing extras the gate already computed
339
+ * for the challenge, so a receipt handler never needs a second lookup to display
340
+ * or reconcile it: the token's `decimals`/`symbol`, the human `amountFormatted`
341
+ * (derived from the SETTLED base-unit `amount`, not the requested price), and a
342
+ * stable `idempotencyKey`.
343
+ *
344
+ * **Delivery contract — read this before persisting receipts.** `onPaid` is
345
+ * **at-least-once**: with a single in-memory replay store it fires exactly once
346
+ * per proof, but across instances sharing a custom `isUsed`/`markUsed` store two
347
+ * nodes can settle the same proof in a race and each fire once. Always **dedupe on
348
+ * `idempotencyKey`** (a unique index / upsert). It is also fire-and-forget by
349
+ * default — the gate does not block the response on it and a process crash between
350
+ * settlement and your side-effect drops that receipt. For durability either set
351
+ * `awaitOnPaid` (record before the 200) or push to a durable queue inside the hook;
352
+ * for a webhook, use {@link deliverReceipt} (signed, retried, idempotent).
353
+ */
354
+ interface PaidReceipt extends X402Receipt {
355
+ /** The token's on-chain decimals — pairs with `amount` so you can format without a lookup. */
356
+ decimals: number;
357
+ /** The token symbol when the gate knows it (e.g. `USDC`, `FDUSD`). */
358
+ symbol?: string;
359
+ /** Human-readable settled amount, e.g. `"0.05"` — `amount` / 10**`decimals`. */
360
+ amountFormatted: string;
361
+ /**
362
+ * A stable, unique key for this settlement (the settled `transaction` id). `onPaid`
363
+ * is at-least-once across instances — dedupe persistence and webhook delivery on this.
364
+ */
365
+ idempotencyKey: string;
366
+ }
367
+ /**
368
+ * Why a verification failed — a closed, chain-agnostic vocabulary. Every code a
369
+ * driver returns is in this union; a client/agent branches on it rather than
370
+ * parsing prose. Surfaced to the agent in the 402 body's `error` field with a
371
+ * human-readable `detail`. Some codes are family-specific (annotated below):
372
+ * e.g. account-watch chains (TON, Stellar) can't distinguish "wrong recipient"
373
+ * from "no payment", so both collapse to `transfer_not_found`.
374
+ *
375
+ * `transient` = the proof may simply not have propagated to the server's RPC
376
+ * node yet; `definitive` = retrying won't change it. These labels are
377
+ * informational for consumers — the built-in client retries EVERY code up to
378
+ * `maxPaymentRetries` (a short backoff absorbs RPC lag); it does not branch on
379
+ * the code.
380
+ */
381
+ type VerifyErrorCode = 'tx_not_found' | 'insufficient_confirmations' | 'tx_reverted' | 'no_meta' | 'wrong_recipient' | 'amount_too_low' | 'transfer_not_found' | 'payment_expired' | 'tx_already_used' | 'signature_invalid';
382
+ /** The shape every driver's `verify()` returns. Shared by drivers + protocol. */
383
+ type VerifyResult = {
384
+ ok: true;
385
+ receipt: X402Receipt;
386
+ } | {
387
+ ok: false;
388
+ error: VerifyErrorCode;
389
+ detail: string;
390
+ };
391
+ declare const HEADER_REQUIRED = "payment-required";
392
+ declare const HEADER_SIGNATURE = "payment-signature";
393
+ declare const HEADER_RESPONSE = "payment-response";
394
+ declare const HEADER_SIGNATURE_V1 = "x-payment";
395
+ declare const HEADER_RESPONSE_V1 = "x-payment-response";
396
+ declare function buildChallengeHeader(challenge: X402Challenge): string;
397
+ declare function buildReceiptHeader(receipt: X402Receipt): string;
398
+ declare function buildSignatureHeader(signature: X402PaymentSignature): string;
399
+ /**
400
+ * Build the v2 PAYMENT-SIGNATURE header value for a standard x402 `exact` payment:
401
+ * base64 of `{ x402Version: 2, accepted, payload }`. `accepted` is the chosen rail
402
+ * echoed back VERBATIM from the challenge's `accepts[]` (preserving any extra keys a
403
+ * facilitator needs); `payload` is the EIP-3009 `{ signature, authorization }` the
404
+ * buyer's EVM driver produced. Chain-agnostic (pure JSON/base64) — the driver owns
405
+ * the signing, this only frames it for the wire. Round-trips through
406
+ * {@link parseExactPaymentHeader}. (The `onchain-proof` counterpart is
407
+ * {@link buildSignatureHeader}; the v1 flat-shape utility is `encodeXPaymentHeader`.)
408
+ */
409
+ declare function buildExactSignatureHeader(input: {
410
+ accepted: X402ExactAcceptEntry;
411
+ payload: ExactPaymentPayloadAny;
412
+ }): string;
413
+ /**
414
+ * Parse the PAYMENT-REQUIRED challenge from a 402 response. Prefers the
415
+ * `payment-required` header, falls back to the JSON body.
416
+ */
417
+ declare function parseChallenge(response: Response): Promise<X402Challenge | null>;
418
+ /** Parse the PAYMENT-RESPONSE receipt header on a 200 settlement. Reads the v2
419
+ * `payment-response` header, falling back to the v1 `x-payment-response` a foreign
420
+ * server may set. Returns a fully-formed {@link X402Receipt} only (a bare foreign
421
+ * exact SettleResponse without a `payer` is read by {@link parseSettleResponse}). */
422
+ declare function parseReceipt(response: Response): X402Receipt | null;
423
+ /**
424
+ * A standard x402 SettleResponse as the BUYER reads it off a settled (non-402)
425
+ * response. The `success` flag is authoritative: `false` is an EXPLICIT facilitator/
426
+ * server REJECTION (the buyer must NOT record a spend), `true` is an affirmative
427
+ * settlement. `transaction` is the on-chain settle tx the facilitator broadcast.
428
+ */
429
+ interface SettleOutcome {
430
+ success: boolean;
431
+ transaction?: string;
432
+ network?: string;
433
+ payer?: string;
434
+ errorReason?: string;
435
+ }
436
+ /**
437
+ * Read a standard x402 SettleResponse for the BUYER, from the v2 `payment-response`
438
+ * header (or the v1 `x-payment-response` fallback). Returns `null` when neither
439
+ * header is present, unparseable, or carries no boolean `success` — i.e. when the
440
+ * server served the resource WITHOUT echoing a settle result, which the exact buyer
441
+ * treats as an affirmative 2xx settlement (receipt-less). When a body IS present with
442
+ * a boolean `success`, that flag is returned verbatim: ONLY an explicit
443
+ * `success: false` is a rejection. Used by the exact pay path to tell a real
444
+ * settlement from a phantom one (never record a spend on `success:false`).
445
+ */
446
+ declare function parseSettleResponse(response: Response): SettleOutcome | null;
447
+ /** Parse a PAYMENT-SIGNATURE header value (server side). */
448
+ declare function parseSignatureHeader(value: string): X402PaymentSignature | null;
449
+ /**
450
+ * Parse an inbound `exact` payment from a base64 header value (`PAYMENT-SIGNATURE`
451
+ * v2 or `X-PAYMENT` v1). Tolerant of BOTH wire shapes — the inner
452
+ * `{ signature, authorization }` payload is identical across versions, so we read
453
+ * `scheme`/`network` from either the v2 `accepted` object or the v1 flat fields.
454
+ * Returns null when the value isn't a recognisable `exact` payment (e.g. it's an
455
+ * `onchain-proof` proof, or malformed).
456
+ */
457
+ declare function parseExactPaymentHeader(value: string): ParsedExactPayment | null;
458
+ /**
459
+ * Pick the first accepts[] entry on the `onchain-proof` scheme whose network
460
+ * satisfies `matches` (any chain family). Returns null if none match.
461
+ */
462
+ declare function pickAccept(challenge: X402Challenge, matches: (network: string) => boolean): X402AcceptEntry | null;
463
+
464
+ /**
465
+ * Durable spend store — the pluggable seam that lets a client's budget SURVIVE a
466
+ * restart, mirroring the gate's replay-protection `isUsed`/`markUsed` hook.
467
+ *
468
+ * The {@link SpendLedger} is in-memory by default (the session IS the process). Pass
469
+ * a `SpendStore` to {@link PipRailClientOptions.spendStore} (or to
470
+ * `MultiChainPayer.fromWallets`) and the ledger HYDRATES from `load()` at
471
+ * construction and `append()`s every settled payment — so `maxTotal`,
472
+ * `maxTotalPerDenom`, and the payment-count caps resume where they left off after a
473
+ * crash or redeploy, with NO PipRail backend (you own the store, exactly like the
474
+ * replay set).
475
+ *
476
+ * Contract:
477
+ * - `load()` is read ONCE, synchronously, at ledger construction (the log is small
478
+ * — one line per payment). Return `[]` for a fresh store.
479
+ * - `append(record)` persists ONE settled payment. It is called on the hot path, so
480
+ * it MUST NOT throw and SHOULD NOT block (ERRORS.md §: store I/O never throws — a
481
+ * failed append is swallowed so a disk hiccup can't abort a confirmed payment).
482
+ * - Round-trip the WHOLE `SpendRecord` (incl. `decimals` + `denom`) so totals and
483
+ * the grand-total rebuild exactly on reload — the built-in stores below do.
484
+ *
485
+ * PURE + browser-safe: this module has zero Node/chain imports. The Node-only
486
+ * {@link fileSpendStore} (a one-line local JSONL log) lives in `@piprail/sdk/node`.
487
+ */
488
+
489
+ interface SpendStore {
490
+ /** Hydrate the ledger at construction — every previously-settled payment, in order.
491
+ * Read once, synchronously. Return `[]` for a fresh store. */
492
+ load(): SpendRecord[];
493
+ /** Persist one settled payment. MUST NOT throw (failures are swallowed by the ledger). */
494
+ append(record: SpendRecord): void;
495
+ }
496
+ /**
497
+ * An in-memory {@link SpendStore} — useful for tests and for sharing a seed across
498
+ * clients in one process. Not durable (it's the default behaviour made explicit);
499
+ * for restart-survival use `fileSpendStore` from `@piprail/sdk/node` or your own.
500
+ */
501
+ declare function memorySpendStore(seed?: SpendRecord[]): SpendStore;
502
+
503
+ interface SpendRecord {
504
+ url: string;
505
+ host: string;
506
+ network: Caip2;
507
+ asset: string;
508
+ /** Base units paid (already scaled by decimals). */
509
+ amountBase: string;
510
+ /** Human-readable amount, e.g. '0.05'. */
511
+ amountFormatted: string;
512
+ symbol?: string;
513
+ /** TRUE token decimals. Carried on the record so a {@link SpendStore} can rebuild
514
+ * exact totals + the grand total on reload (the client stamps it on every settle). */
515
+ decimals?: number;
516
+ /** The DENOMINATION this payment counts toward in the cross-token grand total
517
+ * (e.g. `'USD'`), or absent for a token with no denomination. Stamped by the
518
+ * client from the policy at settle time so it round-trips through persistence. */
519
+ denom?: string;
520
+ /** Proof ref (EVM tx hash, Solana signature, TON locator, Stellar tx hash). */
521
+ ref: string;
522
+ /** ISO timestamp of settlement. */
523
+ at: string;
524
+ }
525
+ interface SpendAssetTotal {
526
+ network: Caip2;
527
+ asset: string;
528
+ symbol?: string;
529
+ decimals: number;
530
+ totalBase: string;
531
+ totalFormatted: string;
532
+ count: number;
533
+ }
534
+ /** Cumulative spend in one DENOMINATION (a unit of account), summed across every
535
+ * token of that denomination and every chain — the cross-token grand total. */
536
+ interface SpendDenomTotal {
537
+ /** The denomination, e.g. `'USD'`. */
538
+ denom: string;
539
+ /** Total spent, scaled to {@link DENOM_PRECISION} (the accumulator's base), as a string. */
540
+ totalScaled: string;
541
+ /** Human-readable denomination total, e.g. '12.34'. */
542
+ totalFormatted: string;
543
+ /** Number of payments (across all tokens + chains) that contributed to this denom. */
544
+ count: number;
545
+ }
546
+ interface SpendSummary {
547
+ /** Total number of settled payments (across every chain + token). */
548
+ count: number;
549
+ /** Cumulative spend per distinct (network, asset). */
550
+ byAsset: SpendAssetTotal[];
551
+ /** Cumulative spend per DENOMINATION — the cross-token grand total (empty when no
552
+ * payment carried a denomination). NEVER a price-converted figure: a sum of tokens
553
+ * grouped as one unit, each 1:1. */
554
+ byDenom: SpendDenomTotal[];
555
+ /** Every settled payment, in order. */
556
+ records: SpendRecord[];
557
+ }
558
+ declare class SpendLedger {
559
+ private readonly records;
560
+ private readonly buckets;
561
+ /** Per-denomination running total, scaled to {@link DENOM_PRECISION}. Keyed by the
562
+ * UPPERCASE denomination so lookups are case-insensitive. */
563
+ private readonly denomTotals;
564
+ /** Threshold keys already warned (`warnAtFraction` fires once per crossing per cap).
565
+ * Lives on the LEDGER — not the client — so clients SHARING one (a cross-chain
566
+ * MultiChainPayer) dedupe together: a denomination/count threshold fires once across the
567
+ * whole shared budget, not once per chain. */
568
+ private readonly warned;
569
+ private readonly store?;
570
+ /**
571
+ * Session clock origin (epoch-ms) — process/session start = ledger
572
+ * construction. In-memory; a new process is a new session. The client reads it
573
+ * to compute the `ttlSeconds` deadline and the rolling-window slice.
574
+ */
575
+ readonly sessionStart: number;
576
+ /**
577
+ * @param store Optional durable {@link SpendStore}. When supplied, the ledger
578
+ * HYDRATES from `store.load()` here (so prior spend resumes after a restart) and
579
+ * `append()`s every settled payment. A throwing/absent store fails SAFE to an
580
+ * empty in-memory ledger — it never blocks construction (ERRORS.md: never throw).
581
+ */
582
+ constructor(store?: SpendStore);
583
+ /**
584
+ * A record is safe to tally iff its `amountBase` is a non-negative integer STRING and its
585
+ * `decimals` is an integer in `[0, MAX_DECIMALS]`. The live `record()` path always passes
586
+ * (the client validated the quote), but a hydrated record comes from an UNTRUSTED store
587
+ * (a tampered/corrupt/future-version JSONL line), so we gate it here — a bad record is
588
+ * dropped rather than allowed to throw `BigInt(...)`/`formatUnits(...)` later and brick a
589
+ * read or the constructor.
590
+ */
591
+ private isTallyable;
592
+ /** Apply a record to the in-memory tallies (records + per-asset bucket + denom total).
593
+ * Shared by {@link record} and constructor hydration; does NOT persist. Returns the
594
+ * stored record, or `null` when the record is corrupt and was skipped. */
595
+ private ingest;
596
+ /** Record a settled payment. `decimals` is the TRUE token decimals (for the
597
+ * per-asset running total + the formatted summary). `denom` is the unit-of-account
598
+ * the payment counts toward in the grand total (or omit for none). Persists to the
599
+ * {@link SpendStore} when one is configured (a failed append never throws). */
600
+ record(r: SpendRecord, decimals: number, denom?: string): void;
601
+ /** Running total (base units) already spent on this (network, asset). */
602
+ totalFor(network: string, asset: string): bigint;
603
+ /**
604
+ * Running grand total for a DENOMINATION, scaled to {@link DENOM_PRECISION} (so
605
+ * tokens of different decimals add up exactly). Summed across every token of that
606
+ * denomination and every chain this ledger has seen. Powers `maxTotalPerDenom`.
607
+ * `0n` for a denomination never spent on. Case-insensitive.
608
+ */
609
+ totalForDenom(denom: string): bigint;
610
+ /** Total number of settled payments (across every chain + token). Powers `maxPayments`. */
611
+ count(): number;
612
+ /** Mark a `warnAtFraction` threshold key as fired; returns `true` the FIRST time (so the
613
+ * caller emits the `budget-threshold` event once) and `false` thereafter. Shared across
614
+ * every client on this ledger, so a cross-chain threshold fires once for the whole budget. */
615
+ markWarned(key: string): boolean;
616
+ /**
617
+ * Number of settled payments whose `at` (ISO) is at or after `sinceMs` (epoch-ms),
618
+ * across every chain + token. Backs the rolling payment-count cap
619
+ * (`maxPaymentsPerWindow`, `sinceMs = now - windowSeconds*1000`). Linear scan —
620
+ * negligible at agent-session cardinality and only when a window count cap is set.
621
+ */
622
+ countSince(sinceMs: number): number;
623
+ /**
624
+ * Sum of base-unit amounts for (network, asset) whose record `at` (ISO
625
+ * timestamp) is at or after `sinceMs` (epoch-ms). Backs the rolling window
626
+ * (`sinceMs = now - windowSeconds*1000`). A linear scan of `records` —
627
+ * agent-session cardinality is small (tens), and it only runs when a window
628
+ * policy is set, so it's negligible against the network round-trip.
629
+ */
630
+ totalSince(network: string, asset: string, sinceMs: number): bigint;
631
+ /**
632
+ * The per-(network, asset) buckets, as read-only tuples — `network`, `asset`,
633
+ * `symbol`, the TRUE `decimals` (frozen from the first record), and the running
634
+ * `totalBase`. Lets the client compose a budget view WITHOUT coupling the ledger
635
+ * to the policy (the cap math lives in the client). Decimals only exist for a
636
+ * pair once it's been spent on — a never-spent pair simply isn't a bucket.
637
+ */
638
+ assetBuckets(): {
639
+ network: Caip2;
640
+ asset: string;
641
+ symbol?: string;
642
+ decimals: number;
643
+ totalBase: bigint;
644
+ }[];
645
+ /**
646
+ * The per-denomination grand totals, as read-only tuples — `denom` and the running
647
+ * `totalScaled` (at {@link DENOM_PRECISION}). Lets the client compose the grand-total
648
+ * budget view; the cap math lives in the client. A never-spent denomination is absent.
649
+ */
650
+ denomBuckets(): {
651
+ denom: string;
652
+ totalScaled: bigint;
653
+ }[];
654
+ /** An immutable snapshot of all spend so far. */
655
+ summary(): SpendSummary;
656
+ }
657
+
658
+ export { type AddressId as A, buildSignatureHeader as B, type Caip2 as C, memorySpendStore as D, type ExactAuthorizationWire as E, parseChallenge as F, parseExactPaymentHeader as G, HEADER_REQUIRED as H, parseReceipt as I, parseSettleResponse as J, parseSignatureHeader as K, pickAccept as L, type PaidReceipt as P, type SettleOutcome as S, type VerifyErrorCode as V, type X402AcceptEntry as X, type AssetId as a, type ExactPaymentPayload as b, type ExactPaymentPayloadAny as c, HEADER_RESPONSE as d, HEADER_RESPONSE_V1 as e, HEADER_SIGNATURE as f, HEADER_SIGNATURE_V1 as g, type ParsedExactPayment as h, type Permit2Authorization as i, type Permit2PaymentPayload as j, type SpendAssetTotal as k, type SpendDenomTotal as l, SpendLedger as m, type SpendRecord as n, type SpendStore as o, type SpendSummary as p, type VerifyResult as q, type X402AnyAccept as r, type X402Challenge as s, type X402ExactAcceptEntry as t, type X402PaymentSignature as u, type X402Receipt as v, type X402ResourceObject as w, buildChallengeHeader as x, buildExactSignatureHeader as y, buildReceiptHeader as z };