@dexterai/x402 3.17.0 → 3.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +125 -454
- package/dist/client/index.d.cts +2 -2
- package/dist/client/index.d.ts +2 -2
- package/dist/tab/adapters/solana/index.d.cts +1 -1
- package/dist/tab/adapters/solana/index.d.ts +1 -1
- package/dist/tab/index.d.cts +4 -4
- package/dist/tab/index.d.ts +4 -4
- package/dist/tab/seller/index.cjs +4 -4
- package/dist/tab/seller/index.d.cts +174 -8
- package/dist/tab/seller/index.d.ts +174 -8
- package/dist/tab/seller/index.js +4 -4
- package/dist/{types-ZjcxOAbW.d.ts → types-BL9QW1gf.d.ts} +1 -1
- package/dist/{types-B1wGPP7B.d.cts → types-DMzS_Rh2.d.cts} +1 -1
- package/dist/{types-DEnVPFxF.d.cts → types-DuoL3s8n.d.cts} +1 -1
- package/dist/{types-DEnVPFxF.d.ts → types-DuoL3s8n.d.ts} +1 -1
- package/package.json +1 -1
|
@@ -1,7 +1,123 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { SignedVoucher, AtomicAmount, TabNetworkId, HumanAmount } from '@dexterai/vault/types';
|
|
2
2
|
import { RequestHandler, Request, Response } from 'express';
|
|
3
3
|
import { Connection, PublicKey } from '@solana/web3.js';
|
|
4
4
|
|
|
5
|
+
/**
|
|
6
|
+
* Durable per-channel seller ledger for OTS tab streaming.
|
|
7
|
+
*
|
|
8
|
+
* Supersedes VoucherStore: it persists the latest accepted voucher AND the
|
|
9
|
+
* one quantity the chain never sees — `deliveredCumulativeAtomic`, the
|
|
10
|
+
* cumulative service the meter has actually delivered on this channel across
|
|
11
|
+
* ALL requests. Monotonic, never reset. This is what closes the channel-reuse
|
|
12
|
+
* metering leak: the meter budgets each request against
|
|
13
|
+
* `signedCumulative − deliveredCumulative`, not the lifetime cumulative.
|
|
14
|
+
*
|
|
15
|
+
* Shape mirrors the on-chain SessionRegistration money ledger
|
|
16
|
+
* (spent / crystallized_cumulative / current_outstanding / last_locked_sequence)
|
|
17
|
+
* that already ships in V6, via the optional `onChain` snapshot. That field is
|
|
18
|
+
* RESERVED for the Step-4 lock/LockedClaim model (lock_voucher reads/writes
|
|
19
|
+
* those on-chain) — the off-chain meter does not populate it today. Reserving
|
|
20
|
+
* it here keeps the ledger forward-compatible without a later breaking change.
|
|
21
|
+
*
|
|
22
|
+
* The same durable state is the substrate resumeTab / stranded-tab recovery
|
|
23
|
+
* needs (last voucher + delivered baseline per channel).
|
|
24
|
+
*
|
|
25
|
+
* Single-stream lease (multi-instance boundary): the per-channel `lease`
|
|
26
|
+
* (tryAcquireLease/releaseLease) enforces ONE live stream per channel, the
|
|
27
|
+
* defense against the concurrent-same-channel over-delivery rug. The default
|
|
28
|
+
* InMemoryChannelLedger / FileChannelLedger acquire it atomically WITHIN one
|
|
29
|
+
* seller process (via the per-channel async lock). A seller running MULTIPLE
|
|
30
|
+
* instances behind a load balancer MUST either back ChannelLedger with a store
|
|
31
|
+
* that makes acquire atomic across processes (Redis `SET NX PX`, Postgres
|
|
32
|
+
* advisory lock / `INSERT ... ON CONFLICT`) or route a channel's requests to a
|
|
33
|
+
* consistent instance — otherwise two instances can each acquire the lease and
|
|
34
|
+
* the rug reopens.
|
|
35
|
+
*/
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Read-through cache of the on-chain SessionRegistration money ledger.
|
|
39
|
+
* RESERVED for Step 4 (lock_voucher / LockedClaim). Not populated by the
|
|
40
|
+
* off-chain meter today. All amounts are atomic (base units) strings.
|
|
41
|
+
*/
|
|
42
|
+
interface OnChainLedgerSnapshot {
|
|
43
|
+
spentAtomic: AtomicAmount;
|
|
44
|
+
crystallizedCumulativeAtomic: AtomicAmount;
|
|
45
|
+
currentOutstandingAtomic: AtomicAmount;
|
|
46
|
+
lastLockedSequence: number;
|
|
47
|
+
/** Unix seconds when this snapshot was read from chain. */
|
|
48
|
+
fetchedAtUnixSec: number;
|
|
49
|
+
}
|
|
50
|
+
interface ChannelLedgerEntry {
|
|
51
|
+
/**
|
|
52
|
+
* Latest accepted voucher (`payload.cumulativeAmount` is the signedCumulative),
|
|
53
|
+
* or `null` for a lease-only entry created when the first request on a channel
|
|
54
|
+
* acquires its single-stream lease BEFORE any voucher is persisted. The
|
|
55
|
+
* middleware writes the real voucher immediately after acquiring the lease, so
|
|
56
|
+
* a null `lastVoucher` is only ever a transient pre-first-voucher state.
|
|
57
|
+
*/
|
|
58
|
+
lastVoucher: SignedVoucher | null;
|
|
59
|
+
/**
|
|
60
|
+
* Off-chain cumulative the meter has DELIVERED on this channel across all
|
|
61
|
+
* requests. Monotonic; never reset. The leak-fix field.
|
|
62
|
+
*/
|
|
63
|
+
deliveredCumulativeAtomic: AtomicAmount;
|
|
64
|
+
/**
|
|
65
|
+
* Delivered cumulative (atomic) that the seller has already crystallized into
|
|
66
|
+
* an on-chain LockedClaim via the keyless `/tab/lock` cadence (Step-4). The
|
|
67
|
+
* crystallization cadence fires when `deliveredCumulativeAtomic −
|
|
68
|
+
* lastCrystallizedCumulativeAtomic` crosses the configured threshold, then
|
|
69
|
+
* advances this on a successful lock so it can't double-fire. Treated as
|
|
70
|
+
* `'0'` when absent (older entries / lease-only entries). Optional so
|
|
71
|
+
* pre-Step-4 ledger constructors remain valid without a breaking change.
|
|
72
|
+
*/
|
|
73
|
+
lastCrystallizedCumulativeAtomic?: AtomicAmount;
|
|
74
|
+
/** RESERVED (Step 4): on-chain money ledger snapshot. Unset today. */
|
|
75
|
+
onChain?: OnChainLedgerSnapshot;
|
|
76
|
+
/**
|
|
77
|
+
* Active-stream lease. Set while a meter is live on this channel; cleared on
|
|
78
|
+
* the meter's terminal path. `heldUntilUnixMs` is a TTL so a crashed holder's
|
|
79
|
+
* lease auto-expires (a stuck lease would otherwise block the buyer's own
|
|
80
|
+
* next request on this tab). Enforces one live stream per channel — the
|
|
81
|
+
* defense against the concurrent-same-channel over-delivery rug.
|
|
82
|
+
*/
|
|
83
|
+
lease?: {
|
|
84
|
+
heldUntilUnixMs: number;
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
interface ChannelLedger {
|
|
88
|
+
get(channelId: string): Promise<ChannelLedgerEntry | null>;
|
|
89
|
+
set(channelId: string, entry: ChannelLedgerEntry): Promise<void>;
|
|
90
|
+
delete(channelId: string): Promise<void>;
|
|
91
|
+
/**
|
|
92
|
+
* Atomically acquire the channel's single-stream lease if free or expired.
|
|
93
|
+
* Returns true if acquired, false if another live stream holds it. The
|
|
94
|
+
* in-process/file impls serialize via the per-channel lock (correct for a
|
|
95
|
+
* single seller process). A multi-instance seller MUST back this with a store
|
|
96
|
+
* that makes acquire atomic across processes (Redis SETNX, Postgres, ...).
|
|
97
|
+
*/
|
|
98
|
+
tryAcquireLease(channelId: string, ttlMs: number): Promise<boolean>;
|
|
99
|
+
/** Release the channel's lease (no-op if not held). */
|
|
100
|
+
releaseLease(channelId: string): Promise<void>;
|
|
101
|
+
}
|
|
102
|
+
declare class InMemoryChannelLedger implements ChannelLedger {
|
|
103
|
+
private map;
|
|
104
|
+
get(channelId: string): Promise<ChannelLedgerEntry | null>;
|
|
105
|
+
set(channelId: string, entry: ChannelLedgerEntry): Promise<void>;
|
|
106
|
+
delete(channelId: string): Promise<void>;
|
|
107
|
+
tryAcquireLease(channelId: string, ttlMs: number): Promise<boolean>;
|
|
108
|
+
releaseLease(channelId: string): Promise<void>;
|
|
109
|
+
}
|
|
110
|
+
declare class FileChannelLedger implements ChannelLedger {
|
|
111
|
+
private readonly dir;
|
|
112
|
+
constructor(dir: string);
|
|
113
|
+
private pathFor;
|
|
114
|
+
get(channelId: string): Promise<ChannelLedgerEntry | null>;
|
|
115
|
+
set(channelId: string, entry: ChannelLedgerEntry): Promise<void>;
|
|
116
|
+
delete(channelId: string): Promise<void>;
|
|
117
|
+
tryAcquireLease(channelId: string, ttlMs: number): Promise<boolean>;
|
|
118
|
+
releaseLease(channelId: string): Promise<void>;
|
|
119
|
+
}
|
|
120
|
+
|
|
5
121
|
/**
|
|
6
122
|
* @dexterai/x402/tab/seller — types for the seller side of OTS tab streaming.
|
|
7
123
|
*
|
|
@@ -17,6 +133,7 @@ import { Connection, PublicKey } from '@solana/web3.js';
|
|
|
17
133
|
* loses at most the last in-flight voucher's worth of revenue. Pluggable to
|
|
18
134
|
* match `batch-settlement/store`'s ChannelStore pattern.
|
|
19
135
|
*/
|
|
136
|
+
/** @deprecated Superseded by ChannelLedger (channel-ledger.ts), which also persists deliveredCumulative. */
|
|
20
137
|
interface VoucherStore {
|
|
21
138
|
get(channelId: string): Promise<SignedVoucher | null>;
|
|
22
139
|
set(channelId: string, voucher: SignedVoucher): Promise<void>;
|
|
@@ -39,6 +156,19 @@ interface SellerTab {
|
|
|
39
156
|
* monotonicity check fails. The middleware persists on success.
|
|
40
157
|
*/
|
|
41
158
|
charge(incrementHuman: HumanAmount): Promise<void>;
|
|
159
|
+
/**
|
|
160
|
+
* Off-chain cumulative (human amount) the meter has DELIVERED on this
|
|
161
|
+
* channel across ALL requests, read from the ChannelLedger at request start.
|
|
162
|
+
* The meter's per-request budget is `cumulative() − deliveredCumulative()`.
|
|
163
|
+
*/
|
|
164
|
+
deliveredCumulative(): HumanAmount;
|
|
165
|
+
/**
|
|
166
|
+
* Add `incrementAtomic` (this request's delivered amount, atomic) to the
|
|
167
|
+
* channel's durable lifetime delivered total, under a per-channel lock.
|
|
168
|
+
* Monotonic — a non-positive increment is a no-op. Called by the meter once
|
|
169
|
+
* per request on the terminal path (end / cap-reject / disconnect).
|
|
170
|
+
*/
|
|
171
|
+
recordDelivered(incrementAtomic: AtomicAmount): Promise<void>;
|
|
42
172
|
}
|
|
43
173
|
/** Options for `tabMiddleware`. */
|
|
44
174
|
interface TabMiddlewareOptions {
|
|
@@ -48,16 +178,42 @@ interface TabMiddlewareOptions {
|
|
|
48
178
|
network: TabNetworkId;
|
|
49
179
|
/** When to settle on chain: at tab close (the common case) vs periodically. */
|
|
50
180
|
settle: 'on-close' | 'periodic';
|
|
51
|
-
/** Facilitator base URL. Default: https://
|
|
181
|
+
/** Facilitator base URL. Default: DEFAULT_FACILITATOR_URL (https://x402.dexter.cash). */
|
|
52
182
|
facilitatorUrl?: string;
|
|
53
|
-
/**
|
|
54
|
-
|
|
183
|
+
/**
|
|
184
|
+
* Durable per-channel state (latest voucher + delivered cumulative).
|
|
185
|
+
* Default: in-memory (loses state on restart). Pass a FileChannelLedger or
|
|
186
|
+
* your own ChannelLedger for restart-safe revenue + resumeTab support.
|
|
187
|
+
*/
|
|
188
|
+
ledger?: ChannelLedger;
|
|
189
|
+
/**
|
|
190
|
+
* Max single-stream duration before a crashed holder's lease auto-expires.
|
|
191
|
+
* Default 300000 (5 min).
|
|
192
|
+
*/
|
|
193
|
+
leaseTtlMs?: number;
|
|
55
194
|
/**
|
|
56
195
|
* Hard cap on a single voucher's incremental amount. Protects the seller's
|
|
57
196
|
* middleware from accepting a buyer trying to slip in a giant single
|
|
58
197
|
* voucher. Default: 100x `perUnit`.
|
|
59
198
|
*/
|
|
60
199
|
maxPerVoucherAtomic?: AtomicAmount;
|
|
200
|
+
/**
|
|
201
|
+
* Keyless crystallization cadence (Step-4 lock-mode). On the configured
|
|
202
|
+
* delivered-amount threshold — and at tab close — the meter POSTs the
|
|
203
|
+
* buyer's already-stored signed voucher to `${facilitatorUrl}/tab/lock`,
|
|
204
|
+
* crystallizing it into an on-chain LockedClaim. BEST-EFFORT: a failed
|
|
205
|
+
* crystallize never blocks or errors the seller's response; a missed lock
|
|
206
|
+
* just widens the seller's unsecured window (their risk dial).
|
|
207
|
+
*
|
|
208
|
+
* Defaults when omitted: `{ thresholdAtomic: humanToAtomic('0.10'),
|
|
209
|
+
* onClose: true }`. Set `thresholdAtomic` higher to crystallize less often
|
|
210
|
+
* (cheaper, wider window) or lower to lock more aggressively. Set
|
|
211
|
+
* `onClose: false` to skip the close-time lock.
|
|
212
|
+
*/
|
|
213
|
+
lockCadence?: {
|
|
214
|
+
thresholdAtomic?: string;
|
|
215
|
+
onClose?: boolean;
|
|
216
|
+
};
|
|
61
217
|
}
|
|
62
218
|
/**
|
|
63
219
|
* Options for `openSse` — the Express response → SSE stream helper. Returns
|
|
@@ -76,12 +232,12 @@ interface OpenSseOptions {
|
|
|
76
232
|
interface SseMeter {
|
|
77
233
|
charge(units?: number): Promise<void>;
|
|
78
234
|
send(chunk: string | Uint8Array): void;
|
|
79
|
-
end(): void
|
|
235
|
+
end(): Promise<void>;
|
|
80
236
|
}
|
|
81
237
|
/** Errors thrown by the seller middleware on bad vouchers. */
|
|
82
238
|
declare class InvalidVoucherError extends Error {
|
|
83
|
-
readonly reason: 'signature_invalid' | 'registration_invalid' | 'cap_exceeded' | 'session_expired' | 'wrong_counterparty' | 'non_monotonic';
|
|
84
|
-
constructor(reason: 'signature_invalid' | 'registration_invalid' | 'cap_exceeded' | 'session_expired' | 'wrong_counterparty' | 'non_monotonic', detail?: string);
|
|
239
|
+
readonly reason: 'signature_invalid' | 'registration_invalid' | 'cap_exceeded' | 'session_expired' | 'wrong_counterparty' | 'non_monotonic' | 'channel_busy';
|
|
240
|
+
constructor(reason: 'signature_invalid' | 'registration_invalid' | 'cap_exceeded' | 'session_expired' | 'wrong_counterparty' | 'non_monotonic' | 'channel_busy', detail?: string);
|
|
85
241
|
}
|
|
86
242
|
|
|
87
243
|
/**
|
|
@@ -148,6 +304,16 @@ declare function requireTab(req: Request): SellerTab;
|
|
|
148
304
|
* left for Phase 4+; the v3 meter ships the simpler "one voucher bounds
|
|
149
305
|
* the whole request" model, which is correct for any reasonable chunk
|
|
150
306
|
* count under a single per-request increment.
|
|
307
|
+
*
|
|
308
|
+
* Concurrency note: delivered accounting is exact for requests that run
|
|
309
|
+
* sequentially per channel (the normal case — an agent streams one request at
|
|
310
|
+
* a time per tab). Two GENUINELY concurrent streams on the SAME channel each
|
|
311
|
+
* read the same delivered baseline, so they can over-deliver in-flight up to
|
|
312
|
+
* the sum of their budgets before either persists. The lifetime ledger stays
|
|
313
|
+
* correct (additive under a per-channel lock), so the over-delivery is bounded
|
|
314
|
+
* to the overlap and never compounds across future requests. Sellers needing
|
|
315
|
+
* exact metering under parallel same-channel streams should serialize requests
|
|
316
|
+
* per channel.
|
|
151
317
|
*/
|
|
152
318
|
|
|
153
319
|
declare function openSse(res: Response, options: OpenSseOptions): SseMeter;
|
|
@@ -341,4 +507,4 @@ interface TabOrExactConfig {
|
|
|
341
507
|
}
|
|
342
508
|
declare function tabOrExactMiddleware(config: TabOrExactConfig): RequestHandler;
|
|
343
509
|
|
|
344
|
-
export { FileVoucherStore, InMemoryVoucherStore, InvalidRegistrationError, InvalidVoucherError, InvalidVoucherSignatureError, OnChainVerificationError, type OpenSseOptions, type ParsedRegistration, ScopeViolationError, type SellerTab, type SseMeter, TAB_VOUCHER_HEADER, type TabChallengeConfig, type TabMiddlewareConfig, type TabMiddlewareOptions, type TabOrExactConfig, type VoucherStore, enforceScope, openSse, parseRegistration, requireTab, tabChallengeMiddleware, tabMiddleware, tabOrExactMiddleware, verifyRegistrationOnChain, verifyVoucherSignature };
|
|
510
|
+
export { type ChannelLedger, type ChannelLedgerEntry, FileChannelLedger, FileVoucherStore, InMemoryChannelLedger, InMemoryVoucherStore, InvalidRegistrationError, InvalidVoucherError, InvalidVoucherSignatureError, type OnChainLedgerSnapshot, OnChainVerificationError, type OpenSseOptions, type ParsedRegistration, ScopeViolationError, type SellerTab, type SseMeter, TAB_VOUCHER_HEADER, type TabChallengeConfig, type TabMiddlewareConfig, type TabMiddlewareOptions, type TabOrExactConfig, type VoucherStore, enforceScope, openSse, parseRegistration, requireTab, tabChallengeMiddleware, tabMiddleware, tabOrExactMiddleware, verifyRegistrationOnChain, verifyVoucherSignature };
|
package/dist/tab/seller/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
var A=class extends Error{constructor(r,n){super(`Invalid voucher: ${r}${n?` (${n})`:""}`);this.reason=r;this.name="InvalidVoucherError"}};import{PublicKey as $e}from"@solana/web3.js";import Re from"tweetnacl";import{sha256 as ft}from"@noble/hashes/sha256";import{p256 as bt}from"@noble/curves/p256";import{PublicKey as te}from"@solana/web3.js";import{sessionRegisterMessage as tt,sessionRevokeMessage as rt,voucherPayloadMessage as Q,buildVoucherMessage as nt}from"@dexterai/vault/messages";import{buildRegisterSessionKeyInstruction as it,buildRevokeSessionKeyInstruction as at,deriveSwigWalletAddress as ct}from"@dexterai/vault/instructions";import{buildSecp256r1VerifyInstruction as lt}from"@dexterai/vault/precompile";import{DEXTER_VAULT_PROGRAM_ID as ee,SECP256R1_PROGRAM_ID as mt,INSTRUCTIONS_SYSVAR_ID as dt}from"@dexterai/vault/constants";import{fetchSessionAccount as Ce,isSessionLive as Ne}from"@dexterai/vault/session";var re="OTS_SESSION_REGISTER_V2",S=class extends Error{constructor(r,n){super(`Invalid registration: ${r}${n?` (${n})`:""}`);this.reason=r;this.name="InvalidRegistrationError"}};function ne(e){if(e.length!==188)throw new S("wrong_length",`expected 188, got ${e.length}`);let t=new TextDecoder().decode(e.slice(0,re.length));if(t!==re)throw new S("wrong_domain",`got "${t}"`);for(let u=re.length;u<32;u++)if(e[u]!==0)throw new S("wrong_domain",`non-NUL padding at byte ${u}`);let r=new DataView(e.buffer,e.byteOffset,e.byteLength),n=new te(e.slice(32,64)),s=new te(e.slice(64,96)),i=e.slice(96,128),m=r.getBigUint64(128,!0),c=r.getBigInt64(136,!0),o=new te(e.slice(144,176)),l=r.getUint32(176,!0),g=r.getBigUint64(180,!0);if(!n.equals(ee))throw new S("wrong_program",`${n.toBase58()} is not ${ee.toBase58()}`);if(m===0n)throw new S("cap_zero");let d=BigInt(Math.floor(Date.now()/1e3));if(c<=d)throw new S("expiry_in_past",`expires_at=${c}, now=${d}`);return{programId:n,vaultPda:s,sessionPubkey:new Uint8Array(i),maxAmount:m,expiresAt:c,allowedCounterparty:o,nonce:l,maxRevolvingCapacity:g}}var T=class extends Error{constructor(r,n){super(`On-chain verification failed: ${r}${n?` (${n})`:""}`);this.reason=r;this.name="OnChainVerificationError"}};async function se(e,t){let r=await Ce(e,t.vaultPda,t.allowedCounterparty);if(!r||r.version===0)throw new T("session_not_active","no live SessionAccount PDA for this (vault, counterparty) \u2014 revoked, expiry-swept, or never registered");if(!Ne(r))throw new T("session_not_active","SessionAccount PDA is present but expired");if(!Ie(r.session.sessionPubkey,t.sessionPubkey))throw new T("session_pubkey_mismatch",`on-chain ${he(r.session.sessionPubkey)} != registration ${he(t.sessionPubkey)}`)}var w=class extends Error{constructor(t){super(`Invalid voucher signature${t?`: ${t}`:""}`),this.name="InvalidVoucherSignatureError"}};function oe(e,t){if(t.length!==32)throw new w(`channelIdBytes must be 32 bytes, got ${t.length}`);if(e.sessionPublicKey.length!==32)throw new w(`sessionPublicKey must be 32 bytes, got ${e.sessionPublicKey.length}`);if(e.sessionSignature.length!==64)throw new w(`sessionSignature must be 64 bytes, got ${e.sessionSignature.length}`);let r=Q({channelId:t,cumulativeAmount:BigInt(e.payload.cumulativeAmount),sequenceNumber:e.payload.sequenceNumber});if(!Re.sign.detached.verify(r,e.sessionSignature,e.sessionPublicKey))throw new w("ed25519 verify rejected")}var b=class extends Error{constructor(r,n){super(`Scope violation: ${r}${n?` (${n})`:""}`);this.reason=r;this.name="ScopeViolationError"}};function ie(e){let t=BigInt(e.voucher.payload.cumulativeAmount);if(t>e.registration.maxAmount)throw new b("cumulative_exceeds_cap",`${t} > ${e.registration.maxAmount}`);let r=BigInt(Math.floor(Date.now()/1e3));if(r>=e.registration.expiresAt)throw new b("session_expired",`now=${r} >= expiresAt=${e.registration.expiresAt}`);if(!e.registration.allowedCounterparty.equals(e.expectedCounterparty))throw new b("wrong_counterparty",`${e.registration.allowedCounterparty.toBase58()} != ${e.expectedCounterparty.toBase58()}`);if(e.previousCumulativeAtomic!==void 0){let n=BigInt(e.previousCumulativeAtomic);if(t<=n)throw new b("non_monotonic",`cumulative=${t} not > previous=${n}`)}}function Ie(e,t){if(e.length!==t.length)return!1;for(let r=0;r<e.length;r++)if(e[r]!==t[r])return!1;return!0}function he(e){let t="";for(let r of e)t+=r.toString(16).padStart(2,"0");return t}import{promises as N}from"fs";import{join as Ue,dirname as Oe}from"path";function ke(e){return{payload:e.payload,sessionPublicKey:ae(e.sessionPublicKey),sessionRegistration:ae(e.sessionRegistration),sessionSignature:ae(e.sessionSignature)}}function Ve(e){return{payload:e.payload,sessionPublicKey:ce(e.sessionPublicKey),sessionRegistration:ce(e.sessionRegistration),sessionSignature:ce(e.sessionSignature)}}function ae(e){let t="";for(let r of e)t+=r.toString(16).padStart(2,"0");return t}function ce(e){if(e.length%2!==0)throw new Error(`hex length must be even, got ${e.length}`);let t=new Uint8Array(e.length/2);for(let r=0;r<t.length;r++)t[r]=parseInt(e.substr(r*2,2),16);return t}var I=class{map=new Map;async get(t){return this.map.get(t)??null}async set(t,r){this.map.set(t,r)}async delete(t){this.map.delete(t)}},ue=class{constructor(t){this.dir=t}pathFor(t){if(!/^[a-z0-9_-]+$/i.test(t))throw new Error(`unsafe channelId for filesystem: ${t}`);return Ue(this.dir,`${t}.json`)}async get(t){try{let r=await N.readFile(this.pathFor(t),"utf8");return Ve(JSON.parse(r))}catch(r){if(r?.code==="ENOENT")return null;throw r}}async set(t,r){let n=this.pathFor(t);await N.mkdir(Oe(n),{recursive:!0});let s=`${n}.tmp`;await N.writeFile(s,JSON.stringify(ke(r))),await N.rename(s,n)}async delete(t){try{await N.unlink(this.pathFor(t))}catch(r){if(r?.code!=="ENOENT")throw r}}};import{PublicKey as Dt}from"@solana/web3.js";import{bytesToHex as qt}from"@noble/hashes/utils";import Nt from"tweetnacl";import{sha256 as Ot}from"@noble/hashes/sha256";var fe=6;function x(e,t=fe){if(!/^\d+(\.\d+)?$/.test(e))throw new Error(`amount must be a non-negative decimal string, got "${e}"`);let[r,n=""]=e.split(".");if(n.length>t)throw new Error(`amount "${e}" has more than ${t} decimals`);let s=n.padEnd(t,"0"),i=`${r}${s}`.replace(/^0+(?=\d)/,"");return i===""?"0":i}function U(e,t=fe){if(!/^\d+$/.test(e))throw new Error(`atomic must be a non-negative integer string, got "${e}"`);let r=e.padStart(t+1,"0"),n=r.slice(0,-t).replace(/^0+(?=\d)/,"")||"0",s=r.slice(-t).replace(/0+$/,"");return s?`${n}.${s}`:n}var _="x-tab-voucher",le=class{map=new Map;get(t){return this.map.get(t)}set(t,r){this.map.set(t,r)}update(t,r){let n=this.map.get(t);n&&(n.lastCumulativeAtomic=r)}delete(t){this.map.delete(t)}},pe=class{constructor(t,r,n,s){this.chargeImpl=s;this.channelId=t,this.network=r,this.cumulativeAtomic=n}channelId;network;sessionPublicKey=null;cumulativeAtomic;cumulative(){return U(this.cumulativeAtomic.toString())}bumpCumulative(t){this.cumulativeAtomic=t}setSessionPublicKey(t){this.sessionPublicKey=t}async charge(t){return this.chargeImpl(t)}};function Ke(e){if(typeof e!="string"||e.length===0)throw new A("signature_invalid",`missing ${_} header`);let t;try{t=Buffer.from(e,"base64").toString("utf8")}catch{throw new A("signature_invalid","malformed base64")}let r;try{r=JSON.parse(t)}catch{throw new A("signature_invalid","malformed JSON")}if(!r||typeof r!="object"||!r.payload||!r.sessionPublicKey)throw new A("signature_invalid","missing required fields");return{payload:r.payload,sessionPublicKey:M(r.sessionPublicKey),sessionRegistration:M(r.sessionRegistration),sessionSignature:M(r.sessionSignature)}}function M(e){if(typeof e!="string"||e.length%2!==0)throw new A("signature_invalid",`bad hex: ${typeof e}`);let t=new Uint8Array(e.length/2);for(let r=0;r<t.length;r++)t[r]=parseInt(e.substr(r*2,2),16);return t}function Be(e){if(!/^[0-9a-f]{64}$/i.test(e))throw new A("signature_invalid",`channelId must be 64-char hex, got "${e}"`);return M(e)}function me(e){let t=e.store??new I,r=new le,n=typeof e.sellerPubkey=="string"?new $e(e.sellerPubkey):e.sellerPubkey,s=e.maxPerVoucherAtomic?BigInt(e.maxPerVoucherAtomic):BigInt(x(e.perUnit))*100n;return async(i,m,c)=>{try{let o=Ke(i.headers[_]),l=o.payload.channelId,g=Be(l),d=r.get(l);if(!d){let P=ne(o.sessionRegistration);await se(e.connection,P),d={registration:P,lastCumulativeAtomic:"0"},r.set(l,d)}oe(o,g),ie({registration:d.registration,voucher:o,expectedCounterparty:n,previousCumulativeAtomic:d.lastCumulativeAtomic});let u=BigInt(o.payload.cumulativeAmount),h=BigInt(d.lastCumulativeAtomic),f=u-h;if(f>s)throw new b("cumulative_exceeds_cap",`single voucher increment ${f} exceeds maxPerVoucherAtomic ${s}`);await t.set(l,o),r.update(l,o.payload.cumulativeAmount);let E=new pe(l,e.network,u,async P=>{throw new Error("SellerTab.charge() is not driven by the route handler; the buyer presents a fresh voucher per chunk. Use openSse(res, tab) for the metered-stream pattern.")});E.setSessionPublicKey(o.sessionPublicKey),i.tab=E,c()}catch(o){if(o instanceof A||o instanceof S||o instanceof T||o instanceof w||o instanceof b){m.status(402).json({error:"invalid_voucher",reason:o.reason??"unknown",detail:o.message});return}c(o)}}}function Me(e){if(!e.tab)throw new Error("req.tab is missing \u2014 did tabMiddleware run on this route?");return e.tab}function De(e,t){if(!t.tab)throw new Error("openSse requires options.tab");e.headersSent||(e.setHeader("Content-Type","text/event-stream"),e.setHeader("Cache-Control","no-cache"),e.setHeader("Connection","keep-alive"),typeof e.flushHeaders=="function"&&e.flushHeaders());let r=t.tab,n=BigInt(x(r.cumulative())),s=t.perUnit?BigInt(x(t.perUnit)):null,i=0n,m=!1;function c(g=1){if(m)return Promise.reject(new Error("meter ended"));if(s===null)return Promise.reject(new Error("charge() needs options.perUnit"));let d=s*BigInt(g),u=i+d;return u>n?Promise.reject(new b("cumulative_exceeds_cap",`chunk would push request total to ${U(u.toString())} beyond voucher-authorized budget ${U(n.toString())}`)):(i=u,Promise.resolve())}function o(g){if(m)throw new Error("meter ended");let u=(typeof g=="string"?g:Buffer.from(g).toString("utf8")).replace(/\n/g,"\\n");e.write(`data: ${u}
|
|
1
|
+
var S=class extends Error{constructor(r,n){super(`Invalid voucher: ${r}${n?` (${n})`:""}`);this.reason=r;this.name="InvalidVoucherError"}};import{promises as V}from"fs";import{join as qe,dirname as ze}from"path";var Ne=new Map;function P(e,t){let n=(Ne.get(e)??Promise.resolve()).then(()=>t(),()=>t());return Ne.set(e,n.then(()=>{},()=>{})),n}var $=class{map=new Map;async get(t){return this.map.get(t)??null}async set(t,r){this.map.set(t,r)}async delete(t){this.map.delete(t)}async tryAcquireLease(t,r){return P(t,async()=>{let n=this.map.get(t),s=Date.now();if(n?.lease&&n.lease.heldUntilUnixMs>s)return!1;let i=n??{lastVoucher:null,deliveredCumulativeAtomic:"0",lastCrystallizedCumulativeAtomic:"0"};return this.map.set(t,{...i,lease:{heldUntilUnixMs:s+r}}),!0})}async releaseLease(t){await P(t,async()=>{let r=this.map.get(t);r&&this.map.set(t,{...r,lease:void 0})})}};function ce(e){let t="";for(let r of e)t+=r.toString(16).padStart(2,"0");return t}function le(e){if(e.length%2!==0)throw new Error(`hex length must be even, got ${e.length}`);let t=new Uint8Array(e.length/2);for(let r=0;r<t.length;r++)t[r]=parseInt(e.substr(r*2,2),16);return t}function We(e){return{lastVoucher:e.lastVoucher?{payload:e.lastVoucher.payload,sessionPublicKey:ce(e.lastVoucher.sessionPublicKey),sessionRegistration:ce(e.lastVoucher.sessionRegistration),sessionSignature:ce(e.lastVoucher.sessionSignature)}:null,deliveredCumulativeAtomic:e.deliveredCumulativeAtomic,lastCrystallizedCumulativeAtomic:e.lastCrystallizedCumulativeAtomic,onChain:e.onChain,lease:e.lease}}function Fe(e){return{lastVoucher:e.lastVoucher?{payload:e.lastVoucher.payload,sessionPublicKey:le(e.lastVoucher.sessionPublicKey),sessionRegistration:le(e.lastVoucher.sessionRegistration),sessionSignature:le(e.lastVoucher.sessionSignature)}:null,deliveredCumulativeAtomic:e.deliveredCumulativeAtomic,lastCrystallizedCumulativeAtomic:e.lastCrystallizedCumulativeAtomic??"0",onChain:e.onChain,lease:e.lease}}var ue=class{constructor(t){this.dir=t}pathFor(t){if(!/^[a-z0-9_-]+$/i.test(t))throw new Error(`unsafe channelId for filesystem: ${t}`);return qe(this.dir,`${t}.json`)}async get(t){try{let r=await V.readFile(this.pathFor(t),"utf8");return Fe(JSON.parse(r))}catch(r){if(r?.code==="ENOENT")return null;throw r}}async set(t,r){let n=this.pathFor(t);await V.mkdir(ze(n),{recursive:!0});let s=`${n}.tmp`;await V.writeFile(s,JSON.stringify(We(r))),await V.rename(s,n)}async delete(t){try{await V.unlink(this.pathFor(t))}catch(r){if(r?.code!=="ENOENT")throw r}}async tryAcquireLease(t,r){return P(t,async()=>{let n=await this.get(t),s=Date.now();if(n?.lease&&n.lease.heldUntilUnixMs>s)return!1;let i=n??{lastVoucher:null,deliveredCumulativeAtomic:"0",lastCrystallizedCumulativeAtomic:"0"};return await this.set(t,{...i,lease:{heldUntilUnixMs:s+r}}),!0})}async releaseLease(t){await P(t,async()=>{let r=await this.get(t);r&&await this.set(t,{...r,lease:void 0})})}};import{PublicKey as Ze}from"@solana/web3.js";import je from"tweetnacl";import{sha256 as Mt}from"@noble/hashes/sha256";import{p256 as Dt}from"@noble/curves/p256";import{PublicKey as de}from"@solana/web3.js";import{sessionRegisterMessage as Et,sessionRevokeMessage as Ct,voucherPayloadMessage as me,buildVoucherMessage as Pt}from"@dexterai/vault/messages";import{buildRegisterSessionKeyInstruction as _t,buildRevokeSessionKeyInstruction as Nt,deriveSwigWalletAddress as It}from"@dexterai/vault/instructions";import{buildSecp256r1VerifyInstruction as Ot}from"@dexterai/vault/precompile";import{DEXTER_VAULT_PROGRAM_ID as pe,SECP256R1_PROGRAM_ID as Vt,INSTRUCTIONS_SYSVAR_ID as $t}from"@dexterai/vault/constants";import{fetchSessionAccount as Xe,isSessionLive as Je}from"@dexterai/vault/session";var ge="OTS_SESSION_REGISTER_V2",E=class extends Error{constructor(r,n){super(`Invalid registration: ${r}${n?` (${n})`:""}`);this.reason=r;this.name="InvalidRegistrationError"}};function he(e){if(e.length!==188)throw new E("wrong_length",`expected 188, got ${e.length}`);let t=new TextDecoder().decode(e.slice(0,ge.length));if(t!==ge)throw new E("wrong_domain",`got "${t}"`);for(let y=ge.length;y<32;y++)if(e[y]!==0)throw new E("wrong_domain",`non-NUL padding at byte ${y}`);let r=new DataView(e.buffer,e.byteOffset,e.byteLength),n=new de(e.slice(32,64)),s=new de(e.slice(64,96)),i=e.slice(96,128),p=r.getBigUint64(128,!0),u=r.getBigInt64(136,!0),o=new de(e.slice(144,176)),d=r.getUint32(176,!0),c=r.getBigUint64(180,!0);if(!n.equals(pe))throw new E("wrong_program",`${n.toBase58()} is not ${pe.toBase58()}`);if(p===0n)throw new E("cap_zero");let a=BigInt(Math.floor(Date.now()/1e3));if(u<=a)throw new E("expiry_in_past",`expires_at=${u}, now=${a}`);return{programId:n,vaultPda:s,sessionPubkey:new Uint8Array(i),maxAmount:p,expiresAt:u,allowedCounterparty:o,nonce:d,maxRevolvingCapacity:c}}var _=class extends Error{constructor(r,n){super(`On-chain verification failed: ${r}${n?` (${n})`:""}`);this.reason=r;this.name="OnChainVerificationError"}};async function ye(e,t){let r=await Xe(e,t.vaultPda,t.allowedCounterparty);if(!r||r.version===0)throw new _("session_not_active","no live SessionAccount PDA for this (vault, counterparty) \u2014 revoked, expiry-swept, or never registered");if(!Je(r))throw new _("session_not_active","SessionAccount PDA is present but expired");if(!Ye(r.session.sessionPubkey,t.sessionPubkey))throw new _("session_pubkey_mismatch",`on-chain ${Ie(r.session.sessionPubkey)} != registration ${Ie(t.sessionPubkey)}`)}var T=class extends Error{constructor(t){super(`Invalid voucher signature${t?`: ${t}`:""}`),this.name="InvalidVoucherSignatureError"}};function fe(e,t){if(t.length!==32)throw new T(`channelIdBytes must be 32 bytes, got ${t.length}`);if(e.sessionPublicKey.length!==32)throw new T(`sessionPublicKey must be 32 bytes, got ${e.sessionPublicKey.length}`);if(e.sessionSignature.length!==64)throw new T(`sessionSignature must be 64 bytes, got ${e.sessionSignature.length}`);let r=me({channelId:t,cumulativeAmount:BigInt(e.payload.cumulativeAmount),sequenceNumber:e.payload.sequenceNumber});if(!je.sign.detached.verify(r,e.sessionSignature,e.sessionPublicKey))throw new T("ed25519 verify rejected")}var x=class extends Error{constructor(r,n){super(`Scope violation: ${r}${n?` (${n})`:""}`);this.reason=r;this.name="ScopeViolationError"}};function Ae(e){let t=BigInt(e.voucher.payload.cumulativeAmount);if(t>e.registration.maxAmount)throw new x("cumulative_exceeds_cap",`${t} > ${e.registration.maxAmount}`);let r=BigInt(Math.floor(Date.now()/1e3));if(r>=e.registration.expiresAt)throw new x("session_expired",`now=${r} >= expiresAt=${e.registration.expiresAt}`);if(!e.registration.allowedCounterparty.equals(e.expectedCounterparty))throw new x("wrong_counterparty",`${e.registration.allowedCounterparty.toBase58()} != ${e.expectedCounterparty.toBase58()}`);if(e.previousCumulativeAtomic!==void 0){let n=BigInt(e.previousCumulativeAtomic);if(t<=n)throw new x("non_monotonic",`cumulative=${t} not > previous=${n}`)}}function Ye(e,t){if(e.length!==t.length)return!1;for(let r=0;r<e.length;r++)if(e[r]!==t[r])return!1;return!0}function Ie(e){let t="";for(let r of e)t+=r.toString(16).padStart(2,"0");return t}import{PublicKey as or}from"@solana/web3.js";import{bytesToHex as cr}from"@noble/hashes/utils";import Yt from"tweetnacl";import{sha256 as Qt}from"@noble/hashes/sha256";var Ue="https://x402.dexter.cash",Oe=6;function C(e,t=Oe){if(!/^\d+(\.\d+)?$/.test(e))throw new Error(`amount must be a non-negative decimal string, got "${e}"`);let[r,n=""]=e.split(".");if(n.length>t)throw new Error(`amount "${e}" has more than ${t} decimals`);let s=n.padEnd(t,"0"),i=`${r}${s}`.replace(/^0+(?=\d)/,"");return i===""?"0":i}function N(e,t=Oe){if(!/^\d+$/.test(e))throw new Error(`atomic must be a non-negative integer string, got "${e}"`);let r=e.padStart(t+1,"0"),n=r.slice(0,-t).replace(/^0+(?=\d)/,"")||"0",s=r.slice(-t).replace(/0+$/,"");return s?`${n}.${s}`:n}import{bytesToHex as be}from"@noble/hashes/utils";var Ge=15e3;async function we(e,t,r,n,s=fetch){let i=e.lastVoucher;if(!i)return{crystallized:!1};if(i.payload.channelId!==t)return{crystallized:!1,error:"channel_id_mismatch"};try{let p=`${r.replace(/\/$/,"")}/tab/lock`,u={channelId:i.payload.channelId,cumulativeAmount:i.payload.cumulativeAmount,sequenceNumber:i.payload.sequenceNumber,sessionPublicKey:be(i.sessionPublicKey),sessionSignature:be(i.sessionSignature),sessionRegistration:be(i.sessionRegistration),network:n},o=await s(p,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(u),signal:AbortSignal.timeout(Ge)}),d=await o.text();if(!o.ok)return{crystallized:!1,error:`tab lock ${o.status}: ${d.slice(0,200)}`};let c;try{let a=JSON.parse(d);a&&typeof a.claimPda=="string"&&(c=a.claimPda)}catch{}return{crystallized:!0,claimPda:c}}catch(p){return{crystallized:!1,error:String(p?.message??p)}}}async function ke(e,t,r,n,s,i={}){let p=BigInt(e.deliveredCumulativeAtomic),u=BigInt(e.lastCrystallizedCumulativeAtomic??"0"),o=BigInt(s.thresholdAtomic);if(p-u<o)return{crystallized:!1};let d=e.lastVoucher?.payload.cumulativeAmount,c=await we(e,t,r,n,i.fetchImpl);return c.crystallized&&d!==void 0&&(e.lastCrystallizedCumulativeAtomic=d),c}var I="x-tab-voucher",ve=class{map=new Map;get(t){return this.map.get(t)}set(t,r){this.map.set(t,r)}update(t,r){let n=this.map.get(t);n&&(n.lastCumulativeAtomic=r)}delete(t){this.map.delete(t)}},Se=class{constructor(t,r,n,s,i,p){this.recordDeliveredImpl=i;this.chargeImpl=p;this.channelId=t,this.network=r,this.cumulativeAtomic=n,this.deliveredBaselineAtomic=s}channelId;network;sessionPublicKey=null;cumulativeAtomic;deliveredBaselineAtomic;cumulative(){return N(this.cumulativeAtomic.toString())}deliveredCumulative(){return N(this.deliveredBaselineAtomic.toString())}async recordDelivered(t){return this.recordDeliveredImpl(t)}bumpCumulative(t){this.cumulativeAtomic=t}setSessionPublicKey(t){this.sessionPublicKey=t}async charge(t){return this.chargeImpl(t)}};function Qe(e){if(typeof e!="string"||e.length===0)throw new S("signature_invalid",`missing ${I} header`);let t;try{t=Buffer.from(e,"base64").toString("utf8")}catch{throw new S("signature_invalid","malformed base64")}let r;try{r=JSON.parse(t)}catch{throw new S("signature_invalid","malformed JSON")}if(!r||typeof r!="object"||!r.payload||!r.sessionPublicKey)throw new S("signature_invalid","missing required fields");return{payload:r.payload,sessionPublicKey:j(r.sessionPublicKey),sessionRegistration:j(r.sessionRegistration),sessionSignature:j(r.sessionSignature)}}function j(e){if(typeof e!="string"||e.length%2!==0)throw new S("signature_invalid",`bad hex: ${typeof e}`);let t=new Uint8Array(e.length/2);for(let r=0;r<t.length;r++)t[r]=parseInt(e.substr(r*2,2),16);return t}function et(e){if(!/^[0-9a-f]{64}$/i.test(e))throw new S("signature_invalid",`channelId must be 64-char hex, got "${e}"`);return j(e)}function xe(e){let t=e.ledger??new $,r=new ve,n=typeof e.sellerPubkey=="string"?new Ze(e.sellerPubkey):e.sellerPubkey,s=e.maxPerVoucherAtomic?BigInt(e.maxPerVoucherAtomic):BigInt(C(e.perUnit))*100n,i=e.facilitatorUrl??Ue,p={thresholdAtomic:e.lockCadence?.thresholdAtomic??C("0.10"),onClose:e.lockCadence?.onClose??!0};return async(u,o,d)=>{try{let c=Qe(u.headers[I]),a=c.payload.channelId,y=et(a),m=r.get(a);if(!m){let g=he(c.sessionRegistration);await ye(e.connection,g),m={registration:g,lastCumulativeAtomic:"0"},r.set(a,m)}fe(c,y),Ae({registration:m.registration,voucher:c,expectedCounterparty:n,previousCumulativeAtomic:m.lastCumulativeAtomic});let f=BigInt(c.payload.cumulativeAmount),w=BigInt(m.lastCumulativeAtomic),U=f-w;if(U>s)throw new x("cumulative_exceeds_cap",`single voucher increment ${U} exceeds maxPerVoucherAtomic ${s}`);let q=e.leaseTtlMs??3e5;if(!await t.tryAcquireLease(a,q))throw new S("channel_busy","another stream is live on this channel; tabs serve one stream at a time");let z=!1,O=()=>{z||(z=!0,t.releaseLease(a).catch(g=>{console.error("[tab/seller] failed to release channel lease:",g)}))};o.on("close",O),o.on("finish",O);let ae=async g=>{let v=g.lastCrystallizedCumulativeAtomic??"0";await ke(g,a,i,e.network,p),g.lastCrystallizedCumulativeAtomic!==v&&await P(a,async()=>{let b=await t.get(a);b&&await t.set(a,{...b,lastCrystallizedCumulativeAtomic:g.lastCrystallizedCumulativeAtomic})}).catch(()=>{})},W=!1,F=()=>{!p.onClose||W||(W=!0,(async()=>{let g=await t.get(a);if(!g||!g.lastVoucher)return;let v=g.lastVoucher.payload.cumulativeAmount;(await we(g,a,i,e.network)).crystallized&&await P(a,async()=>{let k=await t.get(a);k&&await t.set(a,{...k,lastCrystallizedCumulativeAtomic:v})})})().catch(()=>{}))};o.on("close",F),o.on("finish",F);let l=await t.get(a),h=l?BigInt(l.deliveredCumulativeAtomic):0n;await P(a,async()=>{let g=await t.get(a);await t.set(a,{...g,lastVoucher:c,deliveredCumulativeAtomic:g?g.deliveredCumulativeAtomic:"0"})}),r.update(a,c.payload.cumulativeAmount);let A=new Se(a,e.network,f,h,async g=>{let v=null;await P(a,async()=>{let b=await t.get(a),k=b?BigInt(b.deliveredCumulativeAtomic):0n,Re=BigInt(g),He=Re>0n?k+Re:k,_e={...b,lastVoucher:b?.lastVoucher??c,deliveredCumulativeAtomic:He.toString(),lastCrystallizedCumulativeAtomic:b?.lastCrystallizedCumulativeAtomic??"0"};await t.set(a,_e),v=_e}),v&&ae(v).catch(()=>{})},async g=>{throw new Error("SellerTab.charge() is not driven by the route handler; the buyer presents a fresh voucher per chunk. Use openSse(res, tab) for the metered-stream pattern.")});A.setSessionPublicKey(c.sessionPublicKey),u.tab=A,d()}catch(c){if(c instanceof S||c instanceof E||c instanceof _||c instanceof T||c instanceof x){o.status(402).json({error:"invalid_voucher",reason:c.reason??"unknown",detail:c.message});return}d(c)}}}function tt(e){if(!e.tab)throw new Error("req.tab is missing \u2014 did tabMiddleware run on this route?");return e.tab}function rt(e,t){if(!t.tab)throw new Error("openSse requires options.tab");e.headersSent||(e.setHeader("Content-Type","text/event-stream"),e.setHeader("Cache-Control","no-cache"),e.setHeader("Connection","keep-alive"),typeof e.flushHeaders=="function"&&e.flushHeaders());let r=t.tab,n=BigInt(C(r.cumulative())),s=BigInt(C(r.deliveredCumulative())),i=n-s;i<0n&&(i=0n);let p=t.perUnit?BigInt(C(t.perUnit)):null,u=0n,o=!1;async function d(){await r.recordDelivered(u.toString())}e.on("close",()=>{o||(o=!0,d().catch(m=>{console.error("[tab/seller] terminal persist failed on disconnect:",m)}))});async function c(m=1){if(o)throw new Error("meter ended");if(p===null)throw new Error("charge() needs options.perUnit");let f=p*BigInt(m),w=u+f;if(w>i)throw o=!0,await d(),new x("cumulative_exceeds_cap",`chunk would push delivered to ${N((s+w).toString())} beyond signed cumulative ${N(n.toString())} (per-request budget ${N(i.toString())})`);u=w}function a(m){if(o)throw new Error("meter ended");let w=(typeof m=="string"?m:Buffer.from(m).toString("utf8")).replace(/\n/g,"\\n");e.write(`data: ${w}
|
|
2
2
|
|
|
3
|
-
`)}function
|
|
4
|
-
data: {"chargedAtomic":"${
|
|
3
|
+
`)}async function y(){o||(o=!0,await d(),e.write(`event: end
|
|
4
|
+
data: {"chargedAtomic":"${u}"}
|
|
5
5
|
|
|
6
|
-
`),e.end())}return{charge:c,send:o,end:l}}import{PublicKey as je}from"@solana/web3.js";var v="solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp",ye="solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1",be="solana:4uhcVJyU9pJkvQyS88uRDiswHXSCkY3z";var O="eip155:8453",D="eip155:84532",H="eip155:42161",q="eip155:137",L="eip155:10",W="eip155:43114",F="eip155:56",j="eip155:1187947933",X="eip155:324705682",J="eip155:1";var z="EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v";var Ae="0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",He="0x55d398326f99059fF775485246999027B3197955",Se="0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d",we={[F]:Se,[O]:Ae,[D]:"0x036CbD53842c5426634e7929541eC2318f3dCF7e",[H]:"0xaf88d065e77c8cC2239327C5EDb3A432268e5831",[q]:"0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359",[L]:"0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85",[W]:"0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E",[j]:"0x85889c8c714505E0c94b30fcfcF64fE3Ac8FCb20",[X]:"0x2e08028E3C4c2356572E096d8EF835cD5C6030bD",[J]:"0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"},xe={[He]:{symbol:"USDT",decimals:18},[Se]:{symbol:"USDC",decimals:18}};var rr={[F]:56,[O]:8453,[D]:84532,[H]:42161,[q]:137,[L]:10,[W]:43114,[j]:1187947933,[X]:324705682,[J]:1},nr={[v]:"https://api.dexter.cash/api/solana/rpc",[ye]:"https://api.devnet.solana.com",[be]:"https://api.testnet.solana.com"},sr={[F]:"https://api.dexter.cash/api/evm/bsc/rpc",[O]:"https://api.dexter.cash/api/base/rpc",[D]:"https://sepolia.base.org",[H]:"https://api.dexter.cash/api/evm/arbitrum/rpc",[q]:"https://api.dexter.cash/api/evm/polygon/rpc",[L]:"https://api.dexter.cash/api/evm/optimism/rpc",[W]:"https://api.dexter.cash/api/evm/avalanche/rpc",[j]:"https://skale-base.skalenodes.com/v1/base",[X]:"https://base-sepolia-testnet.skalenodes.com/v1/jubilant-horrible-ancha",[J]:"https://eth.llamarpc.com"},k="https://x402.dexter.cash";function Y(e){return e.startsWith("solana:")||e==="solana"}function qe(e){if(typeof Buffer<"u")return Buffer.from(e,"utf-8").toString("base64");let t=new TextEncoder().encode(e),r="";for(let n=0;n<t.length;n++)r+=String.fromCharCode(t[n]);return btoa(r)}function Le(e){if(typeof Buffer<"u")return Buffer.from(e,"base64").toString("utf-8");let t=atob(e),r=new Uint8Array(t.length);for(let n=0;n<t.length;n++)r[n]=t.charCodeAt(n);return new TextDecoder().decode(r)}function G(e){return qe(JSON.stringify(e))}function V(e){return JSON.parse(Le(e))}function We(e){if(e instanceof TypeError)return!0;if(e&&typeof e=="object"&&"status"in e){let t=e.status;return t>=500&&t<600}return!1}var $=class extends Error{status;body;constructor(t,r){super(`HTTP ${t}`),this.status=t,this.body=r}},Z=class{facilitatorUrl;cachedSupported=null;cacheTime=0;CACHE_TTL_MS=6e4;timeoutMs;maxRetries;retryBaseMs;constructor(t=k,r){this.facilitatorUrl=t.replace(/\/$/,""),this.timeoutMs=r?.timeoutMs??1e4,this.maxRetries=r?.maxRetries??3,this.retryBaseMs=r?.retryBaseMs??500}async fetchWithTimeout(t,r){let n=new AbortController,s=setTimeout(()=>n.abort(),this.timeoutMs);try{return await fetch(t,{...r,signal:n.signal})}finally{clearTimeout(s)}}async fetchWithRetry(t,r){let n;for(let s=0;s<this.maxRetries;s++)try{let i=await this.fetchWithTimeout(t,r);if(!i.ok&&i.status>=500)throw new $(i.status,await i.text());return i}catch(i){if(n=i,s<this.maxRetries-1&&We(i)){let m=this.retryBaseMs*Math.pow(2,s);await new Promise(c=>setTimeout(c,m));continue}throw i}throw n}async getSupported(){let t=Date.now();if(this.cachedSupported&&t-this.cacheTime<this.CACHE_TTL_MS)return this.cachedSupported;let r=await this.fetchWithTimeout(`${this.facilitatorUrl}/supported`);if(!r.ok)throw new Error(`Facilitator /supported returned ${r.status}`);return this.cachedSupported=await r.json(),this.cacheTime=t,this.cachedSupported}async getFeePayer(t){let n=(await this.getSupported()).kinds.find(s=>s.x402Version===2&&(s.scheme==="exact"||s.scheme==="exact-approval")&&s.network===t);if(!n)throw new Error(`Facilitator does not support network "${t}" with a recognized scheme`);return n.extra?.feePayer}async getNetworkExtra(t){return(await this.getSupported()).kinds.find(s=>s.x402Version===2&&(s.scheme==="exact"||s.scheme==="exact-approval"||s.scheme==="batch-settlement")&&s.network===t)?.extra}async verifyPayment(t,r){try{let n=V(t),s=await this.fetchWithRetry(`${this.facilitatorUrl}/verify`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({x402Version:2,paymentPayload:n,paymentRequirements:r})});return s.ok?await s.json():{isValid:!1,invalidReason:`facilitator_error_${s.status}`}}catch(n){return{isValid:!1,invalidReason:n instanceof $?`facilitator_error_${n.status}`:n instanceof Error&&n.name==="AbortError"?"facilitator_timeout":n instanceof Error?n.message:"unexpected_verify_error"}}}async settlePayment(t,r){try{let n=V(t),s=await this.fetchWithRetry(`${this.facilitatorUrl}/settle`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({x402Version:2,paymentPayload:n,paymentRequirements:r})});return s.ok?{...await s.json(),network:r.network}:{success:!1,network:r.network,errorReason:`facilitator_error_${s.status}`}}catch(n){let s=n instanceof $?`facilitator_error_${n.status}`:n instanceof Error&&n.name==="AbortError"?"facilitator_timeout":n instanceof Error?n.message:"unexpected_settle_error";return{success:!1,network:r.network,errorReason:s}}}};function Fe(e){if(Y(e))return{address:z,decimals:6};let t=we[e];if(t){let r=xe[t]?.decimals??6;return{address:t,decimals:r}}return{address:z,decimals:6}}function ve(e){try{let t=V(e);return t?.accepted?.amount??t?.accepted?.maxAmountRequired}catch{return}}function K(e){let{payTo:t,facilitatorUrl:r=k,network:n=v,defaultTimeoutSeconds:s=60}=e,i=e.asset??Fe(n),m=e.scheme??"exact";if(m==="tab"&&!Y(n))throw new Error(`scheme 'tab' is SVM-only; got network "${n}"`);let c=new Z(r),o=null,l=new Map,g=3e4,d=Date.now();function u(a){let p=(a.maxTimeoutSeconds||s)*1e3;if(l.set(a.payTo,{accept:a,expiresAt:Date.now()+p}),Date.now()-d>g){let y=Date.now();for(let[R,C]of l)C.expiresAt<y&&l.delete(R);d=y}}function h(a){let p=l.get(a);if(p){if(p.expiresAt<Date.now()){l.delete(a);return}return p.accept}}async function f(a){return typeof t=="string"?t:t(a||{})}async function E(){if(o||(o=await c.getNetworkExtra(n)),Y(n)&&!o?.feePayer)throw new Error(`Facilitator does not provide feePayer for network "${n}"`);return{...o?.feePayer?{feePayer:o.feePayer}:{},decimals:o?.decimals??i.decimals,name:o?.name,version:o?.version,...m==="batch-settlement"&&o?.receiverAuthorizer?{receiverAuthorizer:o.receiverAuthorizer}:{},...m==="tab"?{voucherHeader:"x-tab-voucher",registrationEncoding:"base64(188-byte sessionRegisterMessage)"}:{}}}async function P(a,p){let{amountAtomic:y,timeoutSeconds:R=s}=p,C=await E(),B={scheme:m,network:n,amount:y,maxAmountRequired:y,asset:i.address,payTo:a,maxTimeoutSeconds:R,extra:C};return u(B),B}async function de(a){let p=await f({amountAtomic:a.amountAtomic,resourceUrl:a.resourceUrl});return P(p,a)}async function Ee(a){let{resourceUrl:p,description:y,mimeType:R="application/json"}=a,C={url:p,description:y,mimeType:R},B=await de(a);return{x402Version:2,resource:C,accepts:[B],error:"Payment required"}}function ge(a){return G(a)}function Te(a){return{status:402,headers:{"PAYMENT-REQUIRED":ge(a)},body:{}}}async function Pe(a,p){if(!p){let y=await f({paymentHeader:a});p=h(y),p||(p=await P(y,{amountAtomic:ve(a)??"0",resourceUrl:""}))}return c.verifyPayment(a,p)}async function _e(a,p){if(!p){let y=await f({paymentHeader:a});p=h(y),p||(p=await P(y,{amountAtomic:ve(a)??"0",resourceUrl:""}))}return c.settlePayment(a,p)}return{buildRequirements:Ee,encodeRequirements:ge,create402Response:Te,verifyPayment:Pe,settlePayment:_e,getPaymentAccept:de,network:n,assetDecimals:i.decimals,facilitator:c}}var Xe={"solana:mainnet":v};function Je(e){let t=Xe[e.network];if(!t)throw new Error(`tabChallengeMiddleware: unsupported network "${e.network}"`);let r=typeof e.sellerPubkey=="string"?e.sellerPubkey:e.sellerPubkey.toBase58();new je(r);let n=K({payTo:r,network:t,scheme:"tab",facilitatorUrl:e.facilitatorUrl}),s=x(e.perUnit);return async(i,m,c)=>{if(i.headers[_])return c();try{let o=`${i.protocol}://${i.get("host")}${i.originalUrl}`,l=await n.buildRequirements({amountAtomic:s,resourceUrl:o,description:e.description}),g=n.create402Response(l);m.set(g.headers).status(g.status).json(g.body)}catch(o){let l=o?.message??String(o);m.status(503).set({"Retry-After":"5"}).json({error:"challenge_unavailable",detail:l})}}}import{PublicKey as ze}from"@solana/web3.js";var Ye={"solana:mainnet":v};function Ge(e){let t=Ye[e.network];if(!t)throw new Error(`tabOrExactMiddleware: unsupported network "${e.network}"`);let r=typeof e.sellerPubkey=="string"?e.sellerPubkey:e.sellerPubkey.toBase58();new ze(r);let n=K({payTo:r,network:t,scheme:"tab",facilitatorUrl:e.facilitatorUrl}),s=K({payTo:r,network:t,scheme:"exact",facilitatorUrl:e.facilitatorUrl}),i=x(e.perUnit),m=me({connection:e.connection,sellerPubkey:r,network:e.network,perUnit:e.perUnit,settle:"on-close",facilitatorUrl:e.facilitatorUrl});return async(c,o,l)=>{if(c.headers[_])return m(c,o,l);let g=`${c.protocol}://${c.get("host")}${c.originalUrl}`,d=c.headers["payment-signature"];if(d){let u;try{u=await s.getPaymentAccept({amountAtomic:i,resourceUrl:g,description:e.description})}catch(h){let f=h?.message??String(h);o.status(503).set({"Retry-After":"5"}).json({error:"challenge_unavailable",detail:f});return}try{let h=await s.verifyPayment(d,u);if(!h.isValid){o.status(402).json({error:"Payment verification failed",reason:h.invalidReason});return}let f=await s.settlePayment(d,u);if(!f.success){o.status(402).json({error:"Payment settlement failed",reason:f.errorReason});return}return c.x402={transaction:f.transaction,payer:h.payer??"",network:f.network||t},o.setHeader("PAYMENT-RESPONSE",G({success:!0,transaction:f.transaction,network:f.network||t,payer:h.payer??""})),l()}catch{o.status(500).json({error:"Payment processing error"});return}}try{let u={amountAtomic:i,resourceUrl:g,description:e.description},[h,f]=await Promise.all([n.buildRequirements(u),s.buildRequirements(u)]),E={...h,accepts:[...h.accepts,...f.accepts]};o.set({"PAYMENT-REQUIRED":n.encodeRequirements(E)}).status(402).json({error:"Payment required",accepts:E.accepts,resource:E.resource})}catch(u){let h=u?.message??String(u);o.status(503).set({"Retry-After":"5"}).json({error:"challenge_unavailable",detail:h})}}}export{ue as FileVoucherStore,I as InMemoryVoucherStore,S as InvalidRegistrationError,A as InvalidVoucherError,w as InvalidVoucherSignatureError,T as OnChainVerificationError,b as ScopeViolationError,_ as TAB_VOUCHER_HEADER,ie as enforceScope,De as openSse,ne as parseRegistration,Me as requireTab,Je as tabChallengeMiddleware,me as tabMiddleware,Ge as tabOrExactMiddleware,se as verifyRegistrationOnChain,oe as verifyVoucherSignature};
|
|
6
|
+
`),e.end())}return{charge:c,send:a,end:y}}import{promises as B}from"fs";import{join as nt,dirname as st}from"path";function it(e){return{payload:e.payload,sessionPublicKey:Ee(e.sessionPublicKey),sessionRegistration:Ee(e.sessionRegistration),sessionSignature:Ee(e.sessionSignature)}}function ot(e){return{payload:e.payload,sessionPublicKey:Ce(e.sessionPublicKey),sessionRegistration:Ce(e.sessionRegistration),sessionSignature:Ce(e.sessionSignature)}}function Ee(e){let t="";for(let r of e)t+=r.toString(16).padStart(2,"0");return t}function Ce(e){if(e.length%2!==0)throw new Error(`hex length must be even, got ${e.length}`);let t=new Uint8Array(e.length/2);for(let r=0;r<t.length;r++)t[r]=parseInt(e.substr(r*2,2),16);return t}var Pe=class{map=new Map;async get(t){return this.map.get(t)??null}async set(t,r){this.map.set(t,r)}async delete(t){this.map.delete(t)}},Te=class{constructor(t){this.dir=t}pathFor(t){if(!/^[a-z0-9_-]+$/i.test(t))throw new Error(`unsafe channelId for filesystem: ${t}`);return nt(this.dir,`${t}.json`)}async get(t){try{let r=await B.readFile(this.pathFor(t),"utf8");return ot(JSON.parse(r))}catch(r){if(r?.code==="ENOENT")return null;throw r}}async set(t,r){let n=this.pathFor(t);await B.mkdir(st(n),{recursive:!0});let s=`${n}.tmp`;await B.writeFile(s,JSON.stringify(it(r))),await B.rename(s,n)}async delete(t){try{await B.unlink(this.pathFor(t))}catch(r){if(r?.code!=="ENOENT")throw r}}};import{PublicKey as pt}from"@solana/web3.js";var R="solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp",Ve="solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1",$e="solana:4uhcVJyU9pJkvQyS88uRDiswHXSCkY3z";var K="eip155:8453",X="eip155:84532",J="eip155:42161",Y="eip155:137",G="eip155:10",Z="eip155:43114",Q="eip155:56",ee="eip155:1187947933",te="eip155:324705682",re="eip155:1";var ne="EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v";var Be="0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",at="0x55d398326f99059fF775485246999027B3197955",Ke="0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d",Me={[Q]:Ke,[K]:Be,[X]:"0x036CbD53842c5426634e7929541eC2318f3dCF7e",[J]:"0xaf88d065e77c8cC2239327C5EDb3A432268e5831",[Y]:"0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359",[G]:"0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85",[Z]:"0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E",[ee]:"0x85889c8c714505E0c94b30fcfcF64fE3Ac8FCb20",[te]:"0x2e08028E3C4c2356572E096d8EF835cD5C6030bD",[re]:"0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"},Le={[at]:{symbol:"USDT",decimals:18},[Ke]:{symbol:"USDC",decimals:18}};var Rr={[Q]:56,[K]:8453,[X]:84532,[J]:42161,[Y]:137,[G]:10,[Z]:43114,[ee]:1187947933,[te]:324705682,[re]:1},_r={[R]:"https://api.dexter.cash/api/solana/rpc",[Ve]:"https://api.devnet.solana.com",[$e]:"https://api.testnet.solana.com"},Nr={[Q]:"https://api.dexter.cash/api/evm/bsc/rpc",[K]:"https://api.dexter.cash/api/base/rpc",[X]:"https://sepolia.base.org",[J]:"https://api.dexter.cash/api/evm/arbitrum/rpc",[Y]:"https://api.dexter.cash/api/evm/polygon/rpc",[G]:"https://api.dexter.cash/api/evm/optimism/rpc",[Z]:"https://api.dexter.cash/api/evm/avalanche/rpc",[ee]:"https://skale-base.skalenodes.com/v1/base",[te]:"https://base-sepolia-testnet.skalenodes.com/v1/jubilant-horrible-ancha",[re]:"https://eth.llamarpc.com"},M="https://x402.dexter.cash";function se(e){return e.startsWith("solana:")||e==="solana"}function ct(e){if(typeof Buffer<"u")return Buffer.from(e,"utf-8").toString("base64");let t=new TextEncoder().encode(e),r="";for(let n=0;n<t.length;n++)r+=String.fromCharCode(t[n]);return btoa(r)}function lt(e){if(typeof Buffer<"u")return Buffer.from(e,"base64").toString("utf-8");let t=atob(e),r=new Uint8Array(t.length);for(let n=0;n<t.length;n++)r[n]=t.charCodeAt(n);return new TextDecoder().decode(r)}function ie(e){return ct(JSON.stringify(e))}function L(e){return JSON.parse(lt(e))}function ut(e){if(e instanceof TypeError)return!0;if(e&&typeof e=="object"&&"status"in e){let t=e.status;return t>=500&&t<600}return!1}var D=class extends Error{status;body;constructor(t,r){super(`HTTP ${t}`),this.status=t,this.body=r}},oe=class{facilitatorUrl;cachedSupported=null;cacheTime=0;CACHE_TTL_MS=6e4;timeoutMs;maxRetries;retryBaseMs;constructor(t=M,r){this.facilitatorUrl=t.replace(/\/$/,""),this.timeoutMs=r?.timeoutMs??1e4,this.maxRetries=r?.maxRetries??3,this.retryBaseMs=r?.retryBaseMs??500}async fetchWithTimeout(t,r){let n=new AbortController,s=setTimeout(()=>n.abort(),this.timeoutMs);try{return await fetch(t,{...r,signal:n.signal})}finally{clearTimeout(s)}}async fetchWithRetry(t,r){let n;for(let s=0;s<this.maxRetries;s++)try{let i=await this.fetchWithTimeout(t,r);if(!i.ok&&i.status>=500)throw new D(i.status,await i.text());return i}catch(i){if(n=i,s<this.maxRetries-1&&ut(i)){let p=this.retryBaseMs*Math.pow(2,s);await new Promise(u=>setTimeout(u,p));continue}throw i}throw n}async getSupported(){let t=Date.now();if(this.cachedSupported&&t-this.cacheTime<this.CACHE_TTL_MS)return this.cachedSupported;let r=await this.fetchWithTimeout(`${this.facilitatorUrl}/supported`);if(!r.ok)throw new Error(`Facilitator /supported returned ${r.status}`);return this.cachedSupported=await r.json(),this.cacheTime=t,this.cachedSupported}async getFeePayer(t){let n=(await this.getSupported()).kinds.find(s=>s.x402Version===2&&(s.scheme==="exact"||s.scheme==="exact-approval")&&s.network===t);if(!n)throw new Error(`Facilitator does not support network "${t}" with a recognized scheme`);return n.extra?.feePayer}async getNetworkExtra(t){return(await this.getSupported()).kinds.find(s=>s.x402Version===2&&(s.scheme==="exact"||s.scheme==="exact-approval"||s.scheme==="batch-settlement")&&s.network===t)?.extra}async verifyPayment(t,r){try{let n=L(t),s=await this.fetchWithRetry(`${this.facilitatorUrl}/verify`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({x402Version:2,paymentPayload:n,paymentRequirements:r})});return s.ok?await s.json():{isValid:!1,invalidReason:`facilitator_error_${s.status}`}}catch(n){return{isValid:!1,invalidReason:n instanceof D?`facilitator_error_${n.status}`:n instanceof Error&&n.name==="AbortError"?"facilitator_timeout":n instanceof Error?n.message:"unexpected_verify_error"}}}async settlePayment(t,r){try{let n=L(t),s=await this.fetchWithRetry(`${this.facilitatorUrl}/settle`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({x402Version:2,paymentPayload:n,paymentRequirements:r})});return s.ok?{...await s.json(),network:r.network}:{success:!1,network:r.network,errorReason:`facilitator_error_${s.status}`}}catch(n){let s=n instanceof D?`facilitator_error_${n.status}`:n instanceof Error&&n.name==="AbortError"?"facilitator_timeout":n instanceof Error?n.message:"unexpected_settle_error";return{success:!1,network:r.network,errorReason:s}}}};function mt(e){if(se(e))return{address:ne,decimals:6};let t=Me[e];if(t){let r=Le[t]?.decimals??6;return{address:t,decimals:r}}return{address:ne,decimals:6}}function De(e){try{let t=L(e);return t?.accepted?.amount??t?.accepted?.maxAmountRequired}catch{return}}function H(e){let{payTo:t,facilitatorUrl:r=M,network:n=R,defaultTimeoutSeconds:s=60}=e,i=e.asset??mt(n),p=e.scheme??"exact";if(p==="tab"&&!se(n))throw new Error(`scheme 'tab' is SVM-only; got network "${n}"`);let u=new oe(r),o=null,d=new Map,c=3e4,a=Date.now();function y(l){let h=(l.maxTimeoutSeconds||s)*1e3;if(d.set(l.payTo,{accept:l,expiresAt:Date.now()+h}),Date.now()-a>c){let A=Date.now();for(let[g,v]of d)v.expiresAt<A&&d.delete(g);a=A}}function m(l){let h=d.get(l);if(h){if(h.expiresAt<Date.now()){d.delete(l);return}return h.accept}}async function f(l){return typeof t=="string"?t:t(l||{})}async function w(){if(o||(o=await u.getNetworkExtra(n)),se(n)&&!o?.feePayer)throw new Error(`Facilitator does not provide feePayer for network "${n}"`);return{...o?.feePayer?{feePayer:o.feePayer}:{},decimals:o?.decimals??i.decimals,name:o?.name,version:o?.version,...p==="batch-settlement"&&o?.receiverAuthorizer?{receiverAuthorizer:o.receiverAuthorizer}:{},...p==="tab"?{voucherHeader:"x-tab-voucher",registrationEncoding:"base64(188-byte sessionRegisterMessage)"}:{}}}async function U(l,h){let{amountAtomic:A,timeoutSeconds:g=s}=h,v=await w(),b={scheme:p,network:n,amount:A,maxAmountRequired:A,asset:i.address,payTo:l,maxTimeoutSeconds:g,extra:v};return y(b),b}async function q(l){let h=await f({amountAtomic:l.amountAtomic,resourceUrl:l.resourceUrl});return U(h,l)}async function z(l){let{resourceUrl:h,description:A,mimeType:g="application/json"}=l,v={url:h,description:A,mimeType:g},b=await q(l);return{x402Version:2,resource:v,accepts:[b],error:"Payment required"}}function O(l){return ie(l)}function ae(l){return{status:402,headers:{"PAYMENT-REQUIRED":O(l)},body:{}}}async function W(l,h){if(!h){let A=await f({paymentHeader:l});h=m(A),h||(h=await U(A,{amountAtomic:De(l)??"0",resourceUrl:""}))}return u.verifyPayment(l,h)}async function F(l,h){if(!h){let A=await f({paymentHeader:l});h=m(A),h||(h=await U(A,{amountAtomic:De(l)??"0",resourceUrl:""}))}return u.settlePayment(l,h)}return{buildRequirements:z,encodeRequirements:O,create402Response:ae,verifyPayment:W,settlePayment:F,getPaymentAccept:q,network:n,assetDecimals:i.decimals,facilitator:u}}var dt={"solana:mainnet":R};function gt(e){let t=dt[e.network];if(!t)throw new Error(`tabChallengeMiddleware: unsupported network "${e.network}"`);let r=typeof e.sellerPubkey=="string"?e.sellerPubkey:e.sellerPubkey.toBase58();new pt(r);let n=H({payTo:r,network:t,scheme:"tab",facilitatorUrl:e.facilitatorUrl}),s=C(e.perUnit);return async(i,p,u)=>{if(i.headers[I])return u();try{let o=`${i.protocol}://${i.get("host")}${i.originalUrl}`,d=await n.buildRequirements({amountAtomic:s,resourceUrl:o,description:e.description}),c=n.create402Response(d);p.set(c.headers).status(c.status).json(c.body)}catch(o){let d=o?.message??String(o);p.status(503).set({"Retry-After":"5"}).json({error:"challenge_unavailable",detail:d})}}}import{PublicKey as ht}from"@solana/web3.js";var yt={"solana:mainnet":R};function ft(e){let t=yt[e.network];if(!t)throw new Error(`tabOrExactMiddleware: unsupported network "${e.network}"`);let r=typeof e.sellerPubkey=="string"?e.sellerPubkey:e.sellerPubkey.toBase58();new ht(r);let n=H({payTo:r,network:t,scheme:"tab",facilitatorUrl:e.facilitatorUrl}),s=H({payTo:r,network:t,scheme:"exact",facilitatorUrl:e.facilitatorUrl}),i=C(e.perUnit),p=xe({connection:e.connection,sellerPubkey:r,network:e.network,perUnit:e.perUnit,settle:"on-close",facilitatorUrl:e.facilitatorUrl});return async(u,o,d)=>{if(u.headers[I])return p(u,o,d);let c=`${u.protocol}://${u.get("host")}${u.originalUrl}`,a=u.headers["payment-signature"];if(a){let y;try{y=await s.getPaymentAccept({amountAtomic:i,resourceUrl:c,description:e.description})}catch(m){let f=m?.message??String(m);o.status(503).set({"Retry-After":"5"}).json({error:"challenge_unavailable",detail:f});return}try{let m=await s.verifyPayment(a,y);if(!m.isValid){o.status(402).json({error:"Payment verification failed",reason:m.invalidReason});return}let f=await s.settlePayment(a,y);if(!f.success){o.status(402).json({error:"Payment settlement failed",reason:f.errorReason});return}return u.x402={transaction:f.transaction,payer:m.payer??"",network:f.network||t},o.setHeader("PAYMENT-RESPONSE",ie({success:!0,transaction:f.transaction,network:f.network||t,payer:m.payer??""})),d()}catch{o.status(500).json({error:"Payment processing error"});return}}try{let y={amountAtomic:i,resourceUrl:c,description:e.description},[m,f]=await Promise.all([n.buildRequirements(y),s.buildRequirements(y)]),w={...m,accepts:[...m.accepts,...f.accepts]};o.set({"PAYMENT-REQUIRED":n.encodeRequirements(w)}).status(402).json({error:"Payment required",accepts:w.accepts,resource:w.resource})}catch(y){let m=y?.message??String(y);o.status(503).set({"Retry-After":"5"}).json({error:"challenge_unavailable",detail:m})}}}export{ue as FileChannelLedger,Te as FileVoucherStore,$ as InMemoryChannelLedger,Pe as InMemoryVoucherStore,E as InvalidRegistrationError,S as InvalidVoucherError,T as InvalidVoucherSignatureError,_ as OnChainVerificationError,x as ScopeViolationError,I as TAB_VOUCHER_HEADER,Ae as enforceScope,rt as openSse,he as parseRegistration,tt as requireTab,gt as tabChallengeMiddleware,xe as tabMiddleware,ft as tabOrExactMiddleware,ye as verifyRegistrationOnChain,fe as verifyVoucherSignature};
|
|
@@ -148,7 +148,7 @@ interface OpenTabOptions {
|
|
|
148
148
|
revolvingCapacity?: HumanAmount;
|
|
149
149
|
/** Session expiry, seconds from now. Default: 3600 (1 hour). */
|
|
150
150
|
sessionDuration?: number;
|
|
151
|
-
/** Facilitator base URL. Default: https://
|
|
151
|
+
/** Facilitator base URL. Default: DEFAULT_FACILITATOR_URL (https://x402.dexter.cash), overridable. */
|
|
152
152
|
facilitatorUrl?: string;
|
|
153
153
|
}
|
|
154
154
|
/**
|
|
@@ -148,7 +148,7 @@ interface OpenTabOptions {
|
|
|
148
148
|
revolvingCapacity?: HumanAmount;
|
|
149
149
|
/** Session expiry, seconds from now. Default: 3600 (1 hour). */
|
|
150
150
|
sessionDuration?: number;
|
|
151
|
-
/** Facilitator base URL. Default: https://
|
|
151
|
+
/** Facilitator base URL. Default: DEFAULT_FACILITATOR_URL (https://x402.dexter.cash), overridable. */
|
|
152
152
|
facilitatorUrl?: string;
|
|
153
153
|
}
|
|
154
154
|
/**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dexterai/x402",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.18.0",
|
|
4
4
|
"description": "Full-stack x402 SDK - add paid API monetization to any endpoint. Express middleware, React hooks, Access Pass, dynamic pricing. Solana, Base, Polygon, Arbitrum, Optimism, Avalanche, SKALE.",
|
|
5
5
|
"author": "Dexter",
|
|
6
6
|
"license": "MIT",
|