@dexterai/x402 3.8.1 → 3.9.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.
Files changed (50) hide show
  1. package/README.md +313 -702
  2. package/dist/adapters/index.cjs +1 -1
  3. package/dist/adapters/index.d.cts +3 -3
  4. package/dist/adapters/index.d.ts +3 -3
  5. package/dist/adapters/index.js +1 -1
  6. package/dist/batch-settlement/index.d.cts +3 -3
  7. package/dist/batch-settlement/index.d.ts +3 -3
  8. package/dist/batch-settlement/seller/index.d.cts +4 -4
  9. package/dist/batch-settlement/seller/index.d.ts +4 -4
  10. package/dist/client/index.cjs +1 -1
  11. package/dist/client/index.d.cts +308 -238
  12. package/dist/client/index.d.ts +308 -238
  13. package/dist/client/index.js +1 -1
  14. package/dist/{constants-qU-4U3L-.d.cts → constants-D41hDAG6.d.cts} +13 -1
  15. package/dist/{constants-qU-4U3L-.d.ts → constants-D41hDAG6.d.ts} +13 -1
  16. package/dist/react/index.cjs +1 -1
  17. package/dist/react/index.d.cts +12 -4
  18. package/dist/react/index.d.ts +12 -4
  19. package/dist/react/index.js +1 -1
  20. package/dist/server/index.cjs +3 -3
  21. package/dist/server/index.d.cts +63 -5
  22. package/dist/server/index.d.ts +63 -5
  23. package/dist/server/index.js +3 -3
  24. package/dist/tab/adapters/solana/index.cjs +1 -0
  25. package/dist/tab/adapters/solana/index.d.cts +123 -0
  26. package/dist/tab/adapters/solana/index.d.ts +123 -0
  27. package/dist/tab/adapters/solana/index.js +1 -0
  28. package/dist/tab/index.cjs +6 -0
  29. package/dist/tab/index.d.cts +29 -0
  30. package/dist/tab/index.d.ts +29 -0
  31. package/dist/tab/index.js +6 -0
  32. package/dist/tab/seller/index.cjs +6 -0
  33. package/dist/tab/seller/index.d.cts +291 -0
  34. package/dist/tab/seller/index.d.ts +291 -0
  35. package/dist/tab/seller/index.js +6 -0
  36. package/dist/{types-XG8QvfyL.d.cts → types-C8lyIOmX.d.cts} +1 -1
  37. package/dist/{types-DllrEG_Z.d.ts → types-CTl7yVq6.d.ts} +1 -1
  38. package/dist/{types-pOwQlGEV.d.cts → types-CiPcPs0w.d.cts} +1 -1
  39. package/dist/{types-DDBPREEu.d.ts → types-D9VMq7In.d.ts} +1 -1
  40. package/dist/types-DIrmhiD-.d.cts +234 -0
  41. package/dist/types-DIrmhiD-.d.ts +234 -0
  42. package/dist/{types-htvWHuW3.d.cts → types-RxdlGPaG.d.cts} +122 -1
  43. package/dist/{types-htvWHuW3.d.ts → types-RxdlGPaG.d.ts} +122 -1
  44. package/dist/utils/index.cjs +1 -1
  45. package/dist/utils/index.d.cts +10 -1
  46. package/dist/utils/index.d.ts +10 -1
  47. package/dist/utils/index.js +1 -1
  48. package/dist/{sponsored-access-D96FgkQK.d.ts → x402-client-CHrU2Bs6.d.cts} +114 -63
  49. package/dist/{sponsored-access-D7H-womP.d.cts → x402-client-CzseAnIt.d.ts} +114 -63
  50. package/package.json +18 -1
@@ -0,0 +1,234 @@
1
+ /**
2
+ * @dexterai/x402/tab — type contract for the OTS-backed streaming payment module.
3
+ *
4
+ * Peer of `batch-settlement`. Where `batch-settlement` is for N discrete paid
5
+ * requests against one escrow channel, `tab` is for *continuous metered
6
+ * consumption* — tokens, bytes, frames, seconds — settled on close.
7
+ *
8
+ * Full design: see `docs/DESIGN-tab-streaming.md`. This file is the contract
9
+ * lock for Phase 1: the public types and option shapes that downstream phases
10
+ * must implement against without drift.
11
+ */
12
+ /**
13
+ * CAIP-2-style network identifier. The buyer-side `openTab` accepts a string
14
+ * here so future networks (EVM L2s) require no API change — only a new
15
+ * VaultAdapter implementation.
16
+ */
17
+ type TabNetworkId = 'solana:mainnet' | (string & {});
18
+ /**
19
+ * Atomic-unit cumulative amount the seller is asking the buyer to authorize
20
+ * for a single voucher. Strings to avoid bigint JSON-serialization headaches
21
+ * across language boundaries.
22
+ */
23
+ type AtomicAmount = string;
24
+ /**
25
+ * Human-readable amount (e.g. "0.001" USDC). Used at SDK boundaries; converted
26
+ * to atomic units internally per the vault's token decimals.
27
+ */
28
+ type HumanAmount = string;
29
+ /**
30
+ * Scope of a session key — the limits the passkey embeds into its
31
+ * registration signature. The on-chain program (via Swig) and the seller's
32
+ * middleware (locally) both enforce these.
33
+ */
34
+ interface SessionScope {
35
+ /** The specific tab this session is bound to. */
36
+ channelId: string;
37
+ /** Cumulative cap, atomic units. The session-key cannot sign beyond this. */
38
+ maxAmountAtomic: AtomicAmount;
39
+ /** Wall-clock expiry (unix seconds). Hard deadline regardless of usage. */
40
+ expiresAtUnix: number;
41
+ /** Counterparty restriction — typically the seller's address. */
42
+ allowedCounterparty: string;
43
+ }
44
+ /**
45
+ * In-memory session key. NEVER persisted to disk. A crashed process forfeits
46
+ * the session and re-prompts the passkey on the next attempt — this is the
47
+ * right default because a session key on disk is a real attack surface.
48
+ */
49
+ interface SessionKey {
50
+ /** Public key the seller verifies signatures against. */
51
+ publicKey: Uint8Array;
52
+ /** Private key — in-memory only. */
53
+ privateKey: Uint8Array;
54
+ /** Limits this session may operate within. */
55
+ scope: SessionScope;
56
+ /** The passkey signature authorizing this session. The seller verifies it
57
+ * against the vault's registered passkey on every voucher. */
58
+ registration: Uint8Array;
59
+ }
60
+ /**
61
+ * What the buyer signs per stream chunk, and what the seller verifies before
62
+ * delivering. Cumulative-amount semantics: each voucher represents the TOTAL
63
+ * owed so far, not the incremental amount. Replay-resistant because vouchers
64
+ * monotonically increase.
65
+ */
66
+ interface VoucherPayload {
67
+ channelId: string;
68
+ /** Total owed so far, atomic units. Must strictly exceed the prior voucher. */
69
+ cumulativeAmount: AtomicAmount;
70
+ /** Monotonic sequence number. Replay protection within a tab. */
71
+ sequenceNumber: number;
72
+ }
73
+ /**
74
+ * The full voucher as sent over the wire: payload + session signature +
75
+ * the registration that authorizes the signing session key. The seller's
76
+ * middleware verifies the registration's passkey signature once per session,
77
+ * caches the result, and verifies only the session-key signature per chunk.
78
+ */
79
+ interface SignedVoucher {
80
+ payload: VoucherPayload;
81
+ sessionPublicKey: Uint8Array;
82
+ sessionRegistration: Uint8Array;
83
+ sessionSignature: Uint8Array;
84
+ }
85
+ /**
86
+ * Adapter for a specific chain's OTS vault implementation. The SDK calls into
87
+ * `authorizeSession()` ONCE per tab and `signWithSession()` many times. The
88
+ * adapter owns the chain-specific wiring (Solana passkey via WebAuthn or
89
+ * noble-curves, EVM passkey via 7212-aware smart account, etc.).
90
+ *
91
+ * IMPORTANT: this interface must remain stable across vault chains. New
92
+ * chains add adapters; the SDK call site does not change.
93
+ */
94
+ interface VaultAdapter {
95
+ /** Which chain this adapter operates on. */
96
+ network: TabNetworkId;
97
+ /** Wallet holding the buyer's funds. */
98
+ swigAddress: string;
99
+ /** Program/contract account holding the OTS gate state. */
100
+ vaultPda: string;
101
+ /**
102
+ * Use the ROOT signer (passkey) to authorize a fresh session key. This is
103
+ * the only call that prompts the user. Returns a session that can be passed
104
+ * to `signWithSession` freely until the scope's cap or expiry is reached.
105
+ */
106
+ authorizeSession(scope: SessionScope): Promise<SessionKey>;
107
+ /**
108
+ * Use the session key to sign a voucher. Cheap. Never prompts. The seller
109
+ * verifies against the session's registration; the session's registration
110
+ * was passkey-signed by `authorizeSession`.
111
+ */
112
+ signWithSession(session: SessionKey, payload: VoucherPayload): Promise<SignedVoucher>;
113
+ /**
114
+ * Authorize tab open on chain. Posted through the facilitator, which calls
115
+ * `settle_voucher(amount: 0, increment: true)` with the recorded authority.
116
+ */
117
+ signOpenTab(session: SessionKey, channelId: string): Promise<Uint8Array>;
118
+ /**
119
+ * Authorize tab close on chain. Carries the final cumulative amount; the
120
+ * facilitator settles via `settle_voucher(amount, increment: false)`.
121
+ */
122
+ signCloseTab(session: SessionKey, channelId: string, cumulativeAmount: AtomicAmount): Promise<Uint8Array>;
123
+ }
124
+ /** Live state of a buyer's tab. All amounts human units. */
125
+ interface TabState {
126
+ /** Whether the tab is currently open (on chain) and accepting vouchers. */
127
+ isOpen: boolean;
128
+ /** Cumulative amount spent against this tab so far. */
129
+ spent: HumanAmount;
130
+ /** Remaining headroom under the session's cap. */
131
+ remaining: HumanAmount;
132
+ /** Seconds until session expiry. May be 0 even if isOpen — close ASAP. */
133
+ expiresInSec: number;
134
+ }
135
+ /**
136
+ * The buyer's handle to an open tab. Returned by `openTab`; the buyer drives
137
+ * one or more `stream()` calls against it, then `close()`.
138
+ *
139
+ * Mental model: this is to `tab` what `BatchSettlementChannel` is to
140
+ * `batch-settlement` — a per-session live object that owns the buyer's
141
+ * accounting and exposes a streaming I/O primitive.
142
+ */
143
+ interface Tab {
144
+ /** Deterministic channel id derived from buyer/seller/scope/salt. */
145
+ readonly channelId: string;
146
+ /** Which network the underlying vault lives on. */
147
+ readonly network: TabNetworkId;
148
+ /** Live state. Re-reads after every voucher exchange. */
149
+ readonly state: TabState;
150
+ /**
151
+ * Streamed paid request. Returns an async iterable of chunks. Voucher
152
+ * signing is internal: the seller demands a fresh session-signed voucher
153
+ * before delivering each chunk, so the buyer is paid up exactly to what
154
+ * they've received.
155
+ *
156
+ * The async iterable break-on-throw on cap-exceeded, expiry, or signature
157
+ * rejection — the SDK never silently keeps streaming after a failure.
158
+ */
159
+ stream(input: string | URL | Request, init?: RequestInit): Promise<AsyncIterable<Uint8Array>>;
160
+ /**
161
+ * Close the tab. Posts the cumulative voucher through the facilitator;
162
+ * facilitator calls `settle_voucher(amount, increment: false)` on chain.
163
+ * The session key is discarded after this resolves.
164
+ *
165
+ * After close(), the buyer's vault `request_withdrawal` is unblocked (the
166
+ * on-chain gate sees pending_voucher_count return to 0).
167
+ */
168
+ close(): Promise<TabCloseResult>;
169
+ }
170
+ /** Result of `Tab.close()`. */
171
+ interface TabCloseResult {
172
+ /** Cumulative human amount settled on chain. */
173
+ settledAmount: HumanAmount;
174
+ /** Facilitator's on-chain settlement signature. */
175
+ settleTx: string;
176
+ }
177
+ /**
178
+ * Options for `openTab` — the buyer's session-opening call.
179
+ *
180
+ * The shape deliberately mirrors `openBatchChannel` so users coming from
181
+ * `batch-settlement` need only learn the new fields (`perUnitCap`,
182
+ * `sessionDuration`).
183
+ */
184
+ interface OpenTabOptions {
185
+ /** OTS vault adapter — Solana adapter ships first; future EVM adapter slots in here. */
186
+ vault: VaultAdapter;
187
+ /** CAIP-2-style network the vault lives on; cross-checked against vault.network. */
188
+ network: TabNetworkId;
189
+ /** Seller's endpoint host (used for the counterparty binding + voucher routing). */
190
+ seller: string;
191
+ /** Max amount per voucher — caps how aggressive a single charge can be. */
192
+ perUnitCap: HumanAmount;
193
+ /** Max cumulative for the WHOLE tab — the session-key cap. */
194
+ totalCap: HumanAmount;
195
+ /** Session expiry, seconds from now. Default: 3600 (1 hour). */
196
+ sessionDuration?: number;
197
+ /** Facilitator base URL. Default: https://facilitator.dexter.cash, overridable. */
198
+ facilitatorUrl?: string;
199
+ }
200
+ /**
201
+ * Options for `resumeTab` — open a handle to a tab that was opened by a
202
+ * previous process. Recovery surface for crashed buyers.
203
+ *
204
+ * NOTE: a resumed tab requires a fresh session key, because session keys are
205
+ * memory-only by design. The first call after resume prompts the passkey
206
+ * once to authorize a new session bound to the same channelId.
207
+ */
208
+ interface ResumeTabOptions {
209
+ vault: VaultAdapter;
210
+ network: TabNetworkId;
211
+ seller: string;
212
+ channelId: string;
213
+ perUnitCap: HumanAmount;
214
+ totalCap: HumanAmount;
215
+ sessionDuration?: number;
216
+ facilitatorUrl?: string;
217
+ }
218
+ /** Thrown when the SDK is invoked against a chain it does not yet support. */
219
+ declare class UnsupportedNetworkError extends Error {
220
+ readonly network: string;
221
+ constructor(network: string);
222
+ }
223
+ /** Thrown by a buyer call when the session-key cap or expiry would be exceeded. */
224
+ declare class SessionScopeExceededError extends Error {
225
+ readonly reason: 'cap_exceeded' | 'expired' | 'wrong_counterparty';
226
+ constructor(reason: 'cap_exceeded' | 'expired' | 'wrong_counterparty', detail?: string);
227
+ }
228
+ /** Thrown when the buyer tries to operate against a tab that has been closed. */
229
+ declare class TabClosedError extends Error {
230
+ readonly channelId: string;
231
+ constructor(channelId: string);
232
+ }
233
+
234
+ export { type AtomicAmount as A, type HumanAmount as H, type OpenTabOptions as O, type ResumeTabOptions as R, type SessionScope as S, type Tab as T, UnsupportedNetworkError as U, type VoucherPayload as V, type TabState as a, type TabCloseResult as b, type TabNetworkId as c, type SessionKey as d, type SignedVoucher as e, type VaultAdapter as f, SessionScopeExceededError as g, TabClosedError as h };
@@ -0,0 +1,234 @@
1
+ /**
2
+ * @dexterai/x402/tab — type contract for the OTS-backed streaming payment module.
3
+ *
4
+ * Peer of `batch-settlement`. Where `batch-settlement` is for N discrete paid
5
+ * requests against one escrow channel, `tab` is for *continuous metered
6
+ * consumption* — tokens, bytes, frames, seconds — settled on close.
7
+ *
8
+ * Full design: see `docs/DESIGN-tab-streaming.md`. This file is the contract
9
+ * lock for Phase 1: the public types and option shapes that downstream phases
10
+ * must implement against without drift.
11
+ */
12
+ /**
13
+ * CAIP-2-style network identifier. The buyer-side `openTab` accepts a string
14
+ * here so future networks (EVM L2s) require no API change — only a new
15
+ * VaultAdapter implementation.
16
+ */
17
+ type TabNetworkId = 'solana:mainnet' | (string & {});
18
+ /**
19
+ * Atomic-unit cumulative amount the seller is asking the buyer to authorize
20
+ * for a single voucher. Strings to avoid bigint JSON-serialization headaches
21
+ * across language boundaries.
22
+ */
23
+ type AtomicAmount = string;
24
+ /**
25
+ * Human-readable amount (e.g. "0.001" USDC). Used at SDK boundaries; converted
26
+ * to atomic units internally per the vault's token decimals.
27
+ */
28
+ type HumanAmount = string;
29
+ /**
30
+ * Scope of a session key — the limits the passkey embeds into its
31
+ * registration signature. The on-chain program (via Swig) and the seller's
32
+ * middleware (locally) both enforce these.
33
+ */
34
+ interface SessionScope {
35
+ /** The specific tab this session is bound to. */
36
+ channelId: string;
37
+ /** Cumulative cap, atomic units. The session-key cannot sign beyond this. */
38
+ maxAmountAtomic: AtomicAmount;
39
+ /** Wall-clock expiry (unix seconds). Hard deadline regardless of usage. */
40
+ expiresAtUnix: number;
41
+ /** Counterparty restriction — typically the seller's address. */
42
+ allowedCounterparty: string;
43
+ }
44
+ /**
45
+ * In-memory session key. NEVER persisted to disk. A crashed process forfeits
46
+ * the session and re-prompts the passkey on the next attempt — this is the
47
+ * right default because a session key on disk is a real attack surface.
48
+ */
49
+ interface SessionKey {
50
+ /** Public key the seller verifies signatures against. */
51
+ publicKey: Uint8Array;
52
+ /** Private key — in-memory only. */
53
+ privateKey: Uint8Array;
54
+ /** Limits this session may operate within. */
55
+ scope: SessionScope;
56
+ /** The passkey signature authorizing this session. The seller verifies it
57
+ * against the vault's registered passkey on every voucher. */
58
+ registration: Uint8Array;
59
+ }
60
+ /**
61
+ * What the buyer signs per stream chunk, and what the seller verifies before
62
+ * delivering. Cumulative-amount semantics: each voucher represents the TOTAL
63
+ * owed so far, not the incremental amount. Replay-resistant because vouchers
64
+ * monotonically increase.
65
+ */
66
+ interface VoucherPayload {
67
+ channelId: string;
68
+ /** Total owed so far, atomic units. Must strictly exceed the prior voucher. */
69
+ cumulativeAmount: AtomicAmount;
70
+ /** Monotonic sequence number. Replay protection within a tab. */
71
+ sequenceNumber: number;
72
+ }
73
+ /**
74
+ * The full voucher as sent over the wire: payload + session signature +
75
+ * the registration that authorizes the signing session key. The seller's
76
+ * middleware verifies the registration's passkey signature once per session,
77
+ * caches the result, and verifies only the session-key signature per chunk.
78
+ */
79
+ interface SignedVoucher {
80
+ payload: VoucherPayload;
81
+ sessionPublicKey: Uint8Array;
82
+ sessionRegistration: Uint8Array;
83
+ sessionSignature: Uint8Array;
84
+ }
85
+ /**
86
+ * Adapter for a specific chain's OTS vault implementation. The SDK calls into
87
+ * `authorizeSession()` ONCE per tab and `signWithSession()` many times. The
88
+ * adapter owns the chain-specific wiring (Solana passkey via WebAuthn or
89
+ * noble-curves, EVM passkey via 7212-aware smart account, etc.).
90
+ *
91
+ * IMPORTANT: this interface must remain stable across vault chains. New
92
+ * chains add adapters; the SDK call site does not change.
93
+ */
94
+ interface VaultAdapter {
95
+ /** Which chain this adapter operates on. */
96
+ network: TabNetworkId;
97
+ /** Wallet holding the buyer's funds. */
98
+ swigAddress: string;
99
+ /** Program/contract account holding the OTS gate state. */
100
+ vaultPda: string;
101
+ /**
102
+ * Use the ROOT signer (passkey) to authorize a fresh session key. This is
103
+ * the only call that prompts the user. Returns a session that can be passed
104
+ * to `signWithSession` freely until the scope's cap or expiry is reached.
105
+ */
106
+ authorizeSession(scope: SessionScope): Promise<SessionKey>;
107
+ /**
108
+ * Use the session key to sign a voucher. Cheap. Never prompts. The seller
109
+ * verifies against the session's registration; the session's registration
110
+ * was passkey-signed by `authorizeSession`.
111
+ */
112
+ signWithSession(session: SessionKey, payload: VoucherPayload): Promise<SignedVoucher>;
113
+ /**
114
+ * Authorize tab open on chain. Posted through the facilitator, which calls
115
+ * `settle_voucher(amount: 0, increment: true)` with the recorded authority.
116
+ */
117
+ signOpenTab(session: SessionKey, channelId: string): Promise<Uint8Array>;
118
+ /**
119
+ * Authorize tab close on chain. Carries the final cumulative amount; the
120
+ * facilitator settles via `settle_voucher(amount, increment: false)`.
121
+ */
122
+ signCloseTab(session: SessionKey, channelId: string, cumulativeAmount: AtomicAmount): Promise<Uint8Array>;
123
+ }
124
+ /** Live state of a buyer's tab. All amounts human units. */
125
+ interface TabState {
126
+ /** Whether the tab is currently open (on chain) and accepting vouchers. */
127
+ isOpen: boolean;
128
+ /** Cumulative amount spent against this tab so far. */
129
+ spent: HumanAmount;
130
+ /** Remaining headroom under the session's cap. */
131
+ remaining: HumanAmount;
132
+ /** Seconds until session expiry. May be 0 even if isOpen — close ASAP. */
133
+ expiresInSec: number;
134
+ }
135
+ /**
136
+ * The buyer's handle to an open tab. Returned by `openTab`; the buyer drives
137
+ * one or more `stream()` calls against it, then `close()`.
138
+ *
139
+ * Mental model: this is to `tab` what `BatchSettlementChannel` is to
140
+ * `batch-settlement` — a per-session live object that owns the buyer's
141
+ * accounting and exposes a streaming I/O primitive.
142
+ */
143
+ interface Tab {
144
+ /** Deterministic channel id derived from buyer/seller/scope/salt. */
145
+ readonly channelId: string;
146
+ /** Which network the underlying vault lives on. */
147
+ readonly network: TabNetworkId;
148
+ /** Live state. Re-reads after every voucher exchange. */
149
+ readonly state: TabState;
150
+ /**
151
+ * Streamed paid request. Returns an async iterable of chunks. Voucher
152
+ * signing is internal: the seller demands a fresh session-signed voucher
153
+ * before delivering each chunk, so the buyer is paid up exactly to what
154
+ * they've received.
155
+ *
156
+ * The async iterable break-on-throw on cap-exceeded, expiry, or signature
157
+ * rejection — the SDK never silently keeps streaming after a failure.
158
+ */
159
+ stream(input: string | URL | Request, init?: RequestInit): Promise<AsyncIterable<Uint8Array>>;
160
+ /**
161
+ * Close the tab. Posts the cumulative voucher through the facilitator;
162
+ * facilitator calls `settle_voucher(amount, increment: false)` on chain.
163
+ * The session key is discarded after this resolves.
164
+ *
165
+ * After close(), the buyer's vault `request_withdrawal` is unblocked (the
166
+ * on-chain gate sees pending_voucher_count return to 0).
167
+ */
168
+ close(): Promise<TabCloseResult>;
169
+ }
170
+ /** Result of `Tab.close()`. */
171
+ interface TabCloseResult {
172
+ /** Cumulative human amount settled on chain. */
173
+ settledAmount: HumanAmount;
174
+ /** Facilitator's on-chain settlement signature. */
175
+ settleTx: string;
176
+ }
177
+ /**
178
+ * Options for `openTab` — the buyer's session-opening call.
179
+ *
180
+ * The shape deliberately mirrors `openBatchChannel` so users coming from
181
+ * `batch-settlement` need only learn the new fields (`perUnitCap`,
182
+ * `sessionDuration`).
183
+ */
184
+ interface OpenTabOptions {
185
+ /** OTS vault adapter — Solana adapter ships first; future EVM adapter slots in here. */
186
+ vault: VaultAdapter;
187
+ /** CAIP-2-style network the vault lives on; cross-checked against vault.network. */
188
+ network: TabNetworkId;
189
+ /** Seller's endpoint host (used for the counterparty binding + voucher routing). */
190
+ seller: string;
191
+ /** Max amount per voucher — caps how aggressive a single charge can be. */
192
+ perUnitCap: HumanAmount;
193
+ /** Max cumulative for the WHOLE tab — the session-key cap. */
194
+ totalCap: HumanAmount;
195
+ /** Session expiry, seconds from now. Default: 3600 (1 hour). */
196
+ sessionDuration?: number;
197
+ /** Facilitator base URL. Default: https://facilitator.dexter.cash, overridable. */
198
+ facilitatorUrl?: string;
199
+ }
200
+ /**
201
+ * Options for `resumeTab` — open a handle to a tab that was opened by a
202
+ * previous process. Recovery surface for crashed buyers.
203
+ *
204
+ * NOTE: a resumed tab requires a fresh session key, because session keys are
205
+ * memory-only by design. The first call after resume prompts the passkey
206
+ * once to authorize a new session bound to the same channelId.
207
+ */
208
+ interface ResumeTabOptions {
209
+ vault: VaultAdapter;
210
+ network: TabNetworkId;
211
+ seller: string;
212
+ channelId: string;
213
+ perUnitCap: HumanAmount;
214
+ totalCap: HumanAmount;
215
+ sessionDuration?: number;
216
+ facilitatorUrl?: string;
217
+ }
218
+ /** Thrown when the SDK is invoked against a chain it does not yet support. */
219
+ declare class UnsupportedNetworkError extends Error {
220
+ readonly network: string;
221
+ constructor(network: string);
222
+ }
223
+ /** Thrown by a buyer call when the session-key cap or expiry would be exceeded. */
224
+ declare class SessionScopeExceededError extends Error {
225
+ readonly reason: 'cap_exceeded' | 'expired' | 'wrong_counterparty';
226
+ constructor(reason: 'cap_exceeded' | 'expired' | 'wrong_counterparty', detail?: string);
227
+ }
228
+ /** Thrown when the buyer tries to operate against a tab that has been closed. */
229
+ declare class TabClosedError extends Error {
230
+ readonly channelId: string;
231
+ constructor(channelId: string);
232
+ }
233
+
234
+ export { type AtomicAmount as A, type HumanAmount as H, type OpenTabOptions as O, type ResumeTabOptions as R, type SessionScope as S, type Tab as T, UnsupportedNetworkError as U, type VoucherPayload as V, type TabState as a, type TabCloseResult as b, type TabNetworkId as c, type SessionKey as d, type SignedVoucher as e, type VaultAdapter as f, SessionScopeExceededError as g, TabClosedError as h };
@@ -302,6 +302,25 @@ declare class EvmAdapter implements ChainAdapter {
302
302
  private getChainId;
303
303
  getBalance(accept: PaymentAccept, wallet: unknown, rpcUrl?: string): Promise<number>;
304
304
  private encodeBalanceOf;
305
+ /**
306
+ * Confirm an EVM payment settled on-chain, after a post-payment timeout.
307
+ *
308
+ * EIP-3009 (`exact`): calls the token contract's
309
+ * `authorizationState(authorizer, nonce)` view — `true` means our exact
310
+ * signed authorization was consumed, i.e. the transfer settled. The nonce
311
+ * is the 32-byte value the SDK itself generated, so this is a definitive,
312
+ * surgical yes/no.
313
+ *
314
+ * Permit2: calls `Permit2.nonceBitmap(owner, wordPos)` and tests the bit
315
+ * for our nonce. A set bit means the Permit2 transfer was executed.
316
+ *
317
+ * For any other scheme (e.g. exact-approval) there is no clean
318
+ * nonce-consumed check — this throws, which the strategy reads as
319
+ * "unknown" and maps to `payment_unconfirmed`.
320
+ */
321
+ confirmSettlement(probe: SettlementProbe, rpcUrl: string): Promise<SettlementConfirmation>;
322
+ /** Raw `eth_call` returning the result hex word. Throws on RPC error. */
323
+ private ethCall;
305
324
  buildTransaction(accept: PaymentAccept, wallet: unknown, rpcUrl?: string): Promise<SignedTransaction>;
306
325
  /**
307
326
  * Build a payment transaction for chains that use the approval-based scheme.
@@ -392,6 +411,26 @@ declare class SolanaAdapter implements ChainAdapter {
392
411
  isConnected(wallet: unknown): boolean;
393
412
  getBalance(accept: PaymentAccept, wallet: unknown, rpcUrl?: string): Promise<number>;
394
413
  buildTransaction(accept: PaymentAccept, wallet: unknown, rpcUrl?: string): Promise<SignedTransaction>;
414
+ /**
415
+ * Confirm a Solana payment settled on-chain, after a post-payment timeout.
416
+ *
417
+ * Solana has no EIP-3009-style "was this nonce consumed" view. Instead we
418
+ * scan recent signatures on the merchant's destination ATA and look for a
419
+ * transfer of exactly the expected amount. The window is naturally bounded:
420
+ * the payment transaction is only valid for ~150 slots (~60s) after the
421
+ * blockhash it was built against, so a settling transfer — if it happened —
422
+ * is among the most recent signatures on that account. We cap the scan at
423
+ * the 25 most recent signatures.
424
+ *
425
+ * This is strong but not surgical: it matches on (destination ATA, amount),
426
+ * not a unique nonce. A same-amount transfer to the same merchant inside
427
+ * the window from an unrelated payer could in principle match. In practice
428
+ * the blockhash window makes that vanishingly unlikely, and a false
429
+ * positive here only means we tell the caller "paid" when they were not —
430
+ * which is the safe direction (it discourages a retry; the caller verifies
431
+ * against their own wallet). A false negative maps to `payment_unconfirmed`.
432
+ */
433
+ confirmSettlement(probe: SettlementProbe, rpcUrl: string): Promise<SettlementConfirmation>;
395
434
  }
396
435
  /**
397
436
  * Create a Solana adapter instance
@@ -408,6 +447,50 @@ interface GenericWallet {
408
447
  /** Whether the wallet is connected and ready */
409
448
  connected: boolean;
410
449
  }
450
+ /**
451
+ * Everything an adapter needs to ask the chain "did this exact payment
452
+ * settle?" after the fact — without trusting the facilitator or the merchant.
453
+ *
454
+ * `buildTransaction` populates this on the {@link SignedTransaction} it
455
+ * returns. If a post-payment timeout fires, the strategy hands it back to
456
+ * {@link ChainAdapter.confirmSettlement}. A scheme that has no clean on-chain
457
+ * "was this consumed" check (e.g. EVM exact-approval) leaves it `undefined`,
458
+ * and the strategy falls back to reporting `payment_unconfirmed`.
459
+ */
460
+ type SettlementProbe = {
461
+ /** EVM EIP-3009 `transferWithAuthorization` — the default `exact` scheme. */
462
+ kind: 'eip3009';
463
+ /** The authorizer (payer) address — `authorization.from`. */
464
+ from: string;
465
+ /** The 32-byte authorization nonce the SDK generated. The unique key. */
466
+ nonce: string;
467
+ /** The token contract (USDC) — also the EIP-3009 contract exposing `authorizationState`. */
468
+ asset: string;
469
+ /** EVM chain id, decimal. */
470
+ chainId: number;
471
+ } | {
472
+ /** EVM Permit2 `permitWitnessTransferFrom`. */
473
+ kind: 'permit2';
474
+ /** The payer address — Permit2 nonces are namespaced per owner. */
475
+ from: string;
476
+ /** The Permit2 nonce (256-bit, decimal string). */
477
+ nonce: string;
478
+ /** EVM chain id, decimal. */
479
+ chainId: number;
480
+ } | {
481
+ /** Solana SPL transfer. No nonce-consumed view — confirmed by a windowed scan. */
482
+ kind: 'solana';
483
+ /** The buyer's source associated-token-account. */
484
+ sourceAta: string;
485
+ /** The merchant's destination associated-token-account — the address we scan. */
486
+ destinationAta: string;
487
+ /** The token mint. */
488
+ asset: string;
489
+ /** Atomic amount, as a string. */
490
+ amount: string;
491
+ /** The transaction's recent blockhash — bounds how far back the scan need look. */
492
+ blockhash: string;
493
+ };
411
494
  /**
412
495
  * Result of building and signing a transaction.
413
496
  *
@@ -423,6 +506,21 @@ interface SignedTransaction {
423
506
  signature?: string;
424
507
  /** Protocol extensions (e.g., erc20ApprovalGasSponsoring) to attach to the payment payload */
425
508
  extensions?: Record<string, unknown>;
509
+ /**
510
+ * Data needed to confirm settlement on-chain after a post-payment timeout.
511
+ * `undefined` for schemes with no clean on-chain confirmation check.
512
+ * See {@link SettlementProbe} and {@link ChainAdapter.confirmSettlement}.
513
+ */
514
+ settlementProbe?: SettlementProbe;
515
+ }
516
+ /**
517
+ * Outcome of an on-chain settlement check.
518
+ */
519
+ interface SettlementConfirmation {
520
+ /** True if the payment is confirmed settled on-chain. */
521
+ settled: boolean;
522
+ /** The settling transaction hash, when the check can recover it. */
523
+ txSignature?: string;
426
524
  }
427
525
  /**
428
526
  * Chain adapter interface - each chain implements this
@@ -476,6 +574,29 @@ interface ChainAdapter {
476
574
  * @param network - CAIP-2 network identifier
477
575
  */
478
576
  getDefaultRpcUrl(network: string): string;
577
+ /**
578
+ * Ask the chain whether a specific payment settled.
579
+ *
580
+ * Called after a post-payment timeout: the SDK sent the payment
581
+ * authorization, the merchant never responded, and we need to know whether
582
+ * the money actually moved before deciding what to tell the caller. This
583
+ * consults the chain directly via `rpcUrl` — it does not trust the
584
+ * facilitator or the merchant.
585
+ *
586
+ * Optional: an adapter that cannot confirm a given scheme may omit this
587
+ * method (or return `{ settled: false }` is wrong — see below). When the
588
+ * method is absent, or the {@link SettlementProbe} was `undefined`, the
589
+ * strategy falls back to reporting `payment_unconfirmed`.
590
+ *
591
+ * @param probe - The {@link SettlementProbe} captured at build time.
592
+ * @param rpcUrl - The RPC endpoint to query.
593
+ * @returns settled true/false, plus the tx hash when recoverable. An
594
+ * implementation that genuinely cannot tell should THROW rather than
595
+ * return `{ settled: false }` — a thrown error is treated as "unknown"
596
+ * (→ `payment_unconfirmed`), whereas `{ settled: false }` is treated as
597
+ * a definitive "the money did not move."
598
+ */
599
+ confirmSettlement?(probe: SettlementProbe, rpcUrl: string): Promise<SettlementConfirmation>;
479
600
  }
480
601
  /**
481
602
  * Configuration for creating a chain adapter
@@ -514,4 +635,4 @@ interface BalanceInfo {
514
635
  asset: string;
515
636
  }
516
637
 
517
- export { type AccessPassClientConfig as A, type BalanceInfo as B, type ChainAdapter as C, type EvmWallet as E, type GenericWallet as G, type PaymentAccept as P, type SolanaWallet as S, type VerifyResponse as V, type WalletSet as W, X402Error as X, createEvmAdapter as a, type AccessPassTier as b, createSolanaAdapter as c, type AccessPassInfo as d, type SettleResponse as e, type PayToProvider as f, type PaymentRequired as g, type PayToContext as h, type PayToProviderDefaults as i, type AccessPassClaims as j, SolanaAdapter as k, EvmAdapter as l, type AdapterConfig as m, type SignedTransaction as n, isSolanaWallet as o, isEvmWallet as p };
638
+ export { type AccessPassClientConfig as A, type BalanceInfo as B, type ChainAdapter as C, type EvmWallet as E, type GenericWallet as G, type PaymentAccept as P, type SettlementProbe as S, type VerifyResponse as V, type WalletSet as W, X402Error as X, type SolanaWallet as a, createEvmAdapter as b, createSolanaAdapter as c, type AccessPassTier as d, type AccessPassInfo as e, type SettleResponse as f, type PayToProvider as g, type PaymentRequired as h, type PayToContext as i, type PayToProviderDefaults as j, type AccessPassClaims as k, SolanaAdapter as l, EvmAdapter as m, type AdapterConfig as n, type SignedTransaction as o, isSolanaWallet as p, isEvmWallet as q };