@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.
@@ -1,4 +1,4 @@
1
- import { P as PayAndFetchOptions, a as PayResult, b as PaymentStrategy, N as NetworkRef, c as PaymentChallenge, C as ChallengeOption } from '../types-B1wGPP7B.cjs';
1
+ import { P as PayAndFetchOptions, a as PayResult, b as PaymentStrategy, N as NetworkRef, c as PaymentChallenge, C as ChallengeOption } from '../types-DMzS_Rh2.cjs';
2
2
  import { W as WalletSet, S as SettlementProbe, a as SolanaWallet, E as EvmWallet, A as AccessPassClientConfig, P as PaymentAccept } from '../types-xQu1U4xk.cjs';
3
3
  export { e as AccessPassInfo, d as AccessPassTier, C as ChainAdapter, X as X402Error, b as createEvmAdapter, c as createSolanaAdapter } from '../types-xQu1U4xk.cjs';
4
4
  import { SIWxSigner } from '@x402/extensions/sign-in-with-x';
@@ -7,7 +7,7 @@ export { P as PaymentReceipt, d as X402Client, X as X402ClientConfig, c as creat
7
7
  export { FormattedResource as CapabilityAPI, CapabilitySearchOptions, CapabilitySearchResult, NoMatchReason, capabilitySearch } from '@dexterai/x402-core';
8
8
  export { B as BASE_MAINNET, D as DEXTER_FACILITATOR_URL, S as SOLANA_MAINNET, U as USDC_MINT } from '../constants-D41hDAG6.cjs';
9
9
  export { SponsoredAccessSettlementInfo, SponsoredRecommendation } from '@dexterai/x402-ads-types';
10
- import '../types-DEnVPFxF.cjs';
10
+ import '../types-DuoL3s8n.cjs';
11
11
  import '@dexterai/vault/types';
12
12
 
13
13
  /**
@@ -1,4 +1,4 @@
1
- import { P as PayAndFetchOptions, a as PayResult, b as PaymentStrategy, N as NetworkRef, c as PaymentChallenge, C as ChallengeOption } from '../types-ZjcxOAbW.js';
1
+ import { P as PayAndFetchOptions, a as PayResult, b as PaymentStrategy, N as NetworkRef, c as PaymentChallenge, C as ChallengeOption } from '../types-BL9QW1gf.js';
2
2
  import { W as WalletSet, S as SettlementProbe, a as SolanaWallet, E as EvmWallet, A as AccessPassClientConfig, P as PaymentAccept } from '../types-xQu1U4xk.js';
3
3
  export { e as AccessPassInfo, d as AccessPassTier, C as ChainAdapter, X as X402Error, b as createEvmAdapter, c as createSolanaAdapter } from '../types-xQu1U4xk.js';
4
4
  import { SIWxSigner } from '@x402/extensions/sign-in-with-x';
@@ -7,7 +7,7 @@ export { P as PaymentReceipt, d as X402Client, X as X402ClientConfig, c as creat
7
7
  export { FormattedResource as CapabilityAPI, CapabilitySearchOptions, CapabilitySearchResult, NoMatchReason, capabilitySearch } from '@dexterai/x402-core';
8
8
  export { B as BASE_MAINNET, D as DEXTER_FACILITATOR_URL, S as SOLANA_MAINNET, U as USDC_MINT } from '../constants-D41hDAG6.js';
9
9
  export { SponsoredAccessSettlementInfo, SponsoredRecommendation } from '@dexterai/x402-ads-types';
10
- import '../types-DEnVPFxF.js';
10
+ import '../types-DuoL3s8n.js';
11
11
  import '@dexterai/vault/types';
12
12
 
13
13
  /**
@@ -1,6 +1,6 @@
1
1
  import * as _solana_web3_js from '@solana/web3.js';
2
2
  import { PublicKey, Connection, Signer, ConfirmOptions } from '@solana/web3.js';
3
- import { V as VaultAdapter } from '../../../types-DEnVPFxF.cjs';
3
+ import { V as VaultAdapter } from '../../../types-DuoL3s8n.cjs';
4
4
  import '@dexterai/vault/types';
5
5
 
6
6
  /**
@@ -1,6 +1,6 @@
1
1
  import * as _solana_web3_js from '@solana/web3.js';
2
2
  import { PublicKey, Connection, Signer, ConfirmOptions } from '@solana/web3.js';
3
- import { V as VaultAdapter } from '../../../types-DEnVPFxF.js';
3
+ import { V as VaultAdapter } from '../../../types-DuoL3s8n.js';
4
4
  import '@dexterai/vault/types';
5
5
 
6
6
  /**
@@ -1,8 +1,8 @@
1
- import { O as OpenTabOptions, T as Tab, R as ResumeTabOptions, V as VaultAdapter } from '../types-DEnVPFxF.cjs';
2
- export { S as SessionScopeExceededError, b as TabCloseResult, c as TabClosedError, a as TabState, U as UnsupportedNetworkError } from '../types-DEnVPFxF.cjs';
1
+ import { O as OpenTabOptions, T as Tab, R as ResumeTabOptions, V as VaultAdapter } from '../types-DuoL3s8n.cjs';
2
+ export { S as SessionScopeExceededError, b as TabCloseResult, c as TabClosedError, a as TabState, U as UnsupportedNetworkError } from '../types-DuoL3s8n.cjs';
3
3
  import { HumanAmount, AtomicAmount, SignedVoucher } from '@dexterai/vault/types';
4
4
  export { AtomicAmount, HumanAmount, SessionKey, SessionScope, SignedVoucher, TabNetworkId, VoucherPayload } from '@dexterai/vault/types';
5
- import { a as PayResult } from '../types-B1wGPP7B.cjs';
5
+ import { a as PayResult } from '../types-DMzS_Rh2.cjs';
6
6
  export { SessionRegisterMessageArgs, SessionRevokeMessageArgs, VoucherPayloadBytes, buildVoucherMessage, sessionRegisterMessage, sessionRevokeMessage, voucherPayloadMessage } from '@dexterai/vault/messages';
7
7
  export { BuildRegisterSessionKeyArgs, BuildRevokeSessionKeyArgs, buildRegisterSessionKeyInstruction, buildRevokeSessionKeyInstruction } from '@dexterai/vault/instructions';
8
8
  export { buildSecp256r1VerifyInstruction } from '@dexterai/vault/precompile';
@@ -25,7 +25,7 @@ import '../types-xQu1U4xk.cjs';
25
25
  */
26
26
 
27
27
  /** Live Dexter x402 facilitator API. NOT facilitator.dexter.cash —
28
- * that's a marketing redirect. See reference_dexter_facilitator_url.md. */
28
+ * that's a marketing redirect. */
29
29
  declare const DEFAULT_FACILITATOR_URL = "https://x402.dexter.cash";
30
30
  /**
31
31
  * Convert a human decimal string ("0.001") to atomic-unit string ("1000")
@@ -1,8 +1,8 @@
1
- import { O as OpenTabOptions, T as Tab, R as ResumeTabOptions, V as VaultAdapter } from '../types-DEnVPFxF.js';
2
- export { S as SessionScopeExceededError, b as TabCloseResult, c as TabClosedError, a as TabState, U as UnsupportedNetworkError } from '../types-DEnVPFxF.js';
1
+ import { O as OpenTabOptions, T as Tab, R as ResumeTabOptions, V as VaultAdapter } from '../types-DuoL3s8n.js';
2
+ export { S as SessionScopeExceededError, b as TabCloseResult, c as TabClosedError, a as TabState, U as UnsupportedNetworkError } from '../types-DuoL3s8n.js';
3
3
  import { HumanAmount, AtomicAmount, SignedVoucher } from '@dexterai/vault/types';
4
4
  export { AtomicAmount, HumanAmount, SessionKey, SessionScope, SignedVoucher, TabNetworkId, VoucherPayload } from '@dexterai/vault/types';
5
- import { a as PayResult } from '../types-ZjcxOAbW.js';
5
+ import { a as PayResult } from '../types-BL9QW1gf.js';
6
6
  export { SessionRegisterMessageArgs, SessionRevokeMessageArgs, VoucherPayloadBytes, buildVoucherMessage, sessionRegisterMessage, sessionRevokeMessage, voucherPayloadMessage } from '@dexterai/vault/messages';
7
7
  export { BuildRegisterSessionKeyArgs, BuildRevokeSessionKeyArgs, buildRegisterSessionKeyInstruction, buildRevokeSessionKeyInstruction } from '@dexterai/vault/instructions';
8
8
  export { buildSecp256r1VerifyInstruction } from '@dexterai/vault/precompile';
@@ -25,7 +25,7 @@ import '../types-xQu1U4xk.js';
25
25
  */
26
26
 
27
27
  /** Live Dexter x402 facilitator API. NOT facilitator.dexter.cash —
28
- * that's a marketing redirect. See reference_dexter_facilitator_url.md. */
28
+ * that's a marketing redirect. */
29
29
  declare const DEFAULT_FACILITATOR_URL = "https://x402.dexter.cash";
30
30
  /**
31
31
  * Convert a human decimal string ("0.001") to atomic-unit string ("1000")
@@ -1,6 +1,6 @@
1
- "use strict";var qe=Object.create;var H=Object.defineProperty;var Le=Object.getOwnPropertyDescriptor;var We=Object.getOwnPropertyNames;var Fe=Object.getPrototypeOf,je=Object.prototype.hasOwnProperty;var Xe=(e,t)=>{for(var r in t)H(e,r,{get:t[r],enumerable:!0})},Ae=(e,t,r,n)=>{if(t&&typeof t=="object"||typeof t=="function")for(let s of We(t))!je.call(e,s)&&s!==r&&H(e,s,{get:()=>t[s],enumerable:!(n=Le(t,s))||n.enumerable});return e};var Se=(e,t,r)=>(r=e!=null?qe(Fe(e)):{},Ae(t||!e||!e.__esModule?H(r,"default",{value:e,enumerable:!0}):r,e)),Je=e=>Ae(H({},"__esModule",{value:!0}),e);var gt={};Xe(gt,{FileVoucherStore:()=>J,InMemoryVoucherStore:()=>I,InvalidRegistrationError:()=>S,InvalidVoucherError:()=>A,InvalidVoucherSignatureError:()=>w,OnChainVerificationError:()=>x,ScopeViolationError:()=>b,TAB_VOUCHER_HEADER:()=>P,enforceScope:()=>X,openSse:()=>Pe,parseRegistration:()=>W,requireTab:()=>Te,tabChallengeMiddleware:()=>Ve,tabMiddleware:()=>G,tabOrExactMiddleware:()=>Ke,verifyRegistrationOnChain:()=>F,verifyVoucherSignature:()=>j});module.exports=Je(gt);var A=class extends Error{constructor(r,n){super(`Invalid voucher: ${r}${n?` (${n})`:""}`);this.reason=r;this.name="InvalidVoucherError"}};var Ee=require("@solana/web3.js");var xe=Se(require("tweetnacl"),1),Ye=require("@noble/hashes/sha256"),Ge=require("@noble/curves/p256"),q=require("@solana/web3.js");var R=require("@dexterai/vault/messages");var pe=require("@dexterai/vault/instructions"),ze=require("@dexterai/vault/precompile"),C=require("@dexterai/vault/constants");var L=require("@dexterai/vault/session"),me="OTS_SESSION_REGISTER_V2",S=class extends Error{constructor(r,n){super(`Invalid registration: ${r}${n?` (${n})`:""}`);this.reason=r;this.name="InvalidRegistrationError"}};function W(e){if(e.length!==188)throw new S("wrong_length",`expected 188, got ${e.length}`);let t=new TextDecoder().decode(e.slice(0,me.length));if(t!==me)throw new S("wrong_domain",`got "${t}"`);for(let u=me.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 q.PublicKey(e.slice(32,64)),s=new q.PublicKey(e.slice(64,96)),i=e.slice(96,128),m=r.getBigUint64(128,!0),c=r.getBigInt64(136,!0),o=new q.PublicKey(e.slice(144,176)),l=r.getUint32(176,!0),g=r.getBigUint64(180,!0);if(!n.equals(C.DEXTER_VAULT_PROGRAM_ID))throw new S("wrong_program",`${n.toBase58()} is not ${C.DEXTER_VAULT_PROGRAM_ID.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 x=class extends Error{constructor(r,n){super(`On-chain verification failed: ${r}${n?` (${n})`:""}`);this.reason=r;this.name="OnChainVerificationError"}};async function F(e,t){let r=await(0,L.fetchSessionAccount)(e,t.vaultPda,t.allowedCounterparty);if(!r||r.version===0)throw new x("session_not_active","no live SessionAccount PDA for this (vault, counterparty) \u2014 revoked, expiry-swept, or never registered");if(!(0,L.isSessionLive)(r))throw new x("session_not_active","SessionAccount PDA is present but expired");if(!Ze(r.session.sessionPubkey,t.sessionPubkey))throw new x("session_pubkey_mismatch",`on-chain ${we(r.session.sessionPubkey)} != registration ${we(t.sessionPubkey)}`)}var w=class extends Error{constructor(t){super(`Invalid voucher signature${t?`: ${t}`:""}`),this.name="InvalidVoucherSignatureError"}};function j(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=(0,R.voucherPayloadMessage)({channelId:t,cumulativeAmount:BigInt(e.payload.cumulativeAmount),sequenceNumber:e.payload.sequenceNumber});if(!xe.default.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 X(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 Ze(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 we(e){let t="";for(let r of e)t+=r.toString(16).padStart(2,"0");return t}var N=require("fs"),z=require("path");function Qe(e){return{payload:e.payload,sessionPublicKey:de(e.sessionPublicKey),sessionRegistration:de(e.sessionRegistration),sessionSignature:de(e.sessionSignature)}}function et(e){return{payload:e.payload,sessionPublicKey:ge(e.sessionPublicKey),sessionRegistration:ge(e.sessionRegistration),sessionSignature:ge(e.sessionSignature)}}function de(e){let t="";for(let r of e)t+=r.toString(16).padStart(2,"0");return t}function ge(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)}},J=class{constructor(t){this.dir=t}pathFor(t){if(!/^[a-z0-9_-]+$/i.test(t))throw new Error(`unsafe channelId for filesystem: ${t}`);return(0,z.join)(this.dir,`${t}.json`)}async get(t){try{let r=await N.promises.readFile(this.pathFor(t),"utf8");return et(JSON.parse(r))}catch(r){if(r?.code==="ENOENT")return null;throw r}}async set(t,r){let n=this.pathFor(t);await N.promises.mkdir((0,z.dirname)(n),{recursive:!0});let s=`${n}.tmp`;await N.promises.writeFile(s,JSON.stringify(Qe(r))),await N.promises.rename(s,n)}async delete(t){try{await N.promises.unlink(this.pathFor(t))}catch(r){if(r?.code!=="ENOENT")throw r}}};var nt=require("@solana/web3.js"),st=require("@noble/hashes/utils");var tt=Se(require("tweetnacl"),1);var rt=require("@noble/hashes/sha256");var ve=6;function v(e,t=ve){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 k(e,t=ve){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 P="x-tab-voucher",he=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)}},fe=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 k(this.cumulativeAtomic.toString())}bumpCumulative(t){this.cumulativeAtomic=t}setSessionPublicKey(t){this.sessionPublicKey=t}async charge(t){return this.chargeImpl(t)}};function ot(e){if(typeof e!="string"||e.length===0)throw new A("signature_invalid",`missing ${P} 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:Y(r.sessionPublicKey),sessionRegistration:Y(r.sessionRegistration),sessionSignature:Y(r.sessionSignature)}}function Y(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 it(e){if(!/^[0-9a-f]{64}$/i.test(e))throw new A("signature_invalid",`channelId must be 64-char hex, got "${e}"`);return Y(e)}function G(e){let t=e.store??new I,r=new he,n=typeof e.sellerPubkey=="string"?new Ee.PublicKey(e.sellerPubkey):e.sellerPubkey,s=e.maxPerVoucherAtomic?BigInt(e.maxPerVoucherAtomic):BigInt(v(e.perUnit))*100n;return async(i,m,c)=>{try{let o=ot(i.headers[P]),l=o.payload.channelId,g=it(l),d=r.get(l);if(!d){let _=W(o.sessionRegistration);await F(e.connection,_),d={registration:_,lastCumulativeAtomic:"0"},r.set(l,d)}j(o,g),X({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 T=new fe(l,e.network,u,async _=>{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.")});T.setSessionPublicKey(o.sessionPublicKey),i.tab=T,c()}catch(o){if(o instanceof A||o instanceof S||o instanceof x||o instanceof w||o instanceof b){m.status(402).json({error:"invalid_voucher",reason:o.reason??"unknown",detail:o.message});return}c(o)}}}function Te(e){if(!e.tab)throw new Error("req.tab is missing \u2014 did tabMiddleware run on this route?");return e.tab}function Pe(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(v(r.cumulative())),s=t.perUnit?BigInt(v(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 ${k(u.toString())} beyond voucher-authorized budget ${k(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
+ "use strict";var st=Object.create;var J=Object.defineProperty;var it=Object.getOwnPropertyDescriptor;var ot=Object.getOwnPropertyNames;var at=Object.getPrototypeOf,ct=Object.prototype.hasOwnProperty;var lt=(e,t)=>{for(var r in t)J(e,r,{get:t[r],enumerable:!0})},Ve=(e,t,r,n)=>{if(t&&typeof t=="object"||typeof t=="function")for(let s of ot(t))!ct.call(e,s)&&s!==r&&J(e,s,{get:()=>t[s],enumerable:!(n=it(t,s))||n.enumerable});return e};var $e=(e,t,r)=>(r=e!=null?st(at(e)):{},Ve(t||!e||!e.__esModule?J(r,"default",{value:e,enumerable:!0}):r,e)),ut=e=>Ve(J({},"__esModule",{value:!0}),e);var Ot={};lt(Ot,{FileChannelLedger:()=>Y,FileVoucherStore:()=>ce,InMemoryChannelLedger:()=>V,InMemoryVoucherStore:()=>ae,InvalidRegistrationError:()=>E,InvalidVoucherError:()=>S,InvalidVoucherSignatureError:()=>P,OnChainVerificationError:()=>R,ScopeViolationError:()=>x,TAB_VOUCHER_HEADER:()=>N,enforceScope:()=>ne,openSse:()=>We,parseRegistration:()=>ee,requireTab:()=>ze,tabChallengeMiddleware:()=>et,tabMiddleware:()=>oe,tabOrExactMiddleware:()=>rt,verifyRegistrationOnChain:()=>te,verifyVoucherSignature:()=>re});module.exports=ut(Ot);var S=class extends Error{constructor(r,n){super(`Invalid voucher: ${r}${n?` (${n})`:""}`);this.reason=r;this.name="InvalidVoucherError"}};var k=require("fs"),G=require("path"),Be=new Map;function T(e,t){let n=(Be.get(e)??Promise.resolve()).then(()=>t(),()=>t());return Be.set(e,n.then(()=>{},()=>{})),n}var V=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 T(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 T(t,async()=>{let r=this.map.get(t);r&&this.map.set(t,{...r,lease:void 0})})}};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}function mt(e){return{lastVoucher:e.lastVoucher?{payload:e.lastVoucher.payload,sessionPublicKey:Ee(e.lastVoucher.sessionPublicKey),sessionRegistration:Ee(e.lastVoucher.sessionRegistration),sessionSignature:Ee(e.lastVoucher.sessionSignature)}:null,deliveredCumulativeAtomic:e.deliveredCumulativeAtomic,lastCrystallizedCumulativeAtomic:e.lastCrystallizedCumulativeAtomic,onChain:e.onChain,lease:e.lease}}function pt(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??"0",onChain:e.onChain,lease:e.lease}}var Y=class{constructor(t){this.dir=t}pathFor(t){if(!/^[a-z0-9_-]+$/i.test(t))throw new Error(`unsafe channelId for filesystem: ${t}`);return(0,G.join)(this.dir,`${t}.json`)}async get(t){try{let r=await k.promises.readFile(this.pathFor(t),"utf8");return pt(JSON.parse(r))}catch(r){if(r?.code==="ENOENT")return null;throw r}}async set(t,r){let n=this.pathFor(t);await k.promises.mkdir((0,G.dirname)(n),{recursive:!0});let s=`${n}.tmp`;await k.promises.writeFile(s,JSON.stringify(mt(r))),await k.promises.rename(s,n)}async delete(t){try{await k.promises.unlink(this.pathFor(t))}catch(r){if(r?.code!=="ENOENT")throw r}}async tryAcquireLease(t,r){return T(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 T(t,async()=>{let r=await this.get(t);r&&await this.set(t,{...r,lease:void 0})})}};var qe=require("@solana/web3.js");var Me=$e(require("tweetnacl"),1),gt=require("@noble/hashes/sha256"),ht=require("@noble/curves/p256"),Z=require("@solana/web3.js");var I=require("@dexterai/vault/messages");var Pe=require("@dexterai/vault/instructions"),dt=require("@dexterai/vault/precompile"),$=require("@dexterai/vault/constants");var Q=require("@dexterai/vault/session"),Te="OTS_SESSION_REGISTER_V2",E=class extends Error{constructor(r,n){super(`Invalid registration: ${r}${n?` (${n})`:""}`);this.reason=r;this.name="InvalidRegistrationError"}};function ee(e){if(e.length!==188)throw new E("wrong_length",`expected 188, got ${e.length}`);let t=new TextDecoder().decode(e.slice(0,Te.length));if(t!==Te)throw new E("wrong_domain",`got "${t}"`);for(let y=Te.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 Z.PublicKey(e.slice(32,64)),s=new Z.PublicKey(e.slice(64,96)),i=e.slice(96,128),p=r.getBigUint64(128,!0),u=r.getBigInt64(136,!0),o=new Z.PublicKey(e.slice(144,176)),d=r.getUint32(176,!0),c=r.getBigUint64(180,!0);if(!n.equals($.DEXTER_VAULT_PROGRAM_ID))throw new E("wrong_program",`${n.toBase58()} is not ${$.DEXTER_VAULT_PROGRAM_ID.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 R=class extends Error{constructor(r,n){super(`On-chain verification failed: ${r}${n?` (${n})`:""}`);this.reason=r;this.name="OnChainVerificationError"}};async function te(e,t){let r=await(0,Q.fetchSessionAccount)(e,t.vaultPda,t.allowedCounterparty);if(!r||r.version===0)throw new R("session_not_active","no live SessionAccount PDA for this (vault, counterparty) \u2014 revoked, expiry-swept, or never registered");if(!(0,Q.isSessionLive)(r))throw new R("session_not_active","SessionAccount PDA is present but expired");if(!yt(r.session.sessionPubkey,t.sessionPubkey))throw new R("session_pubkey_mismatch",`on-chain ${Ke(r.session.sessionPubkey)} != registration ${Ke(t.sessionPubkey)}`)}var P=class extends Error{constructor(t){super(`Invalid voucher signature${t?`: ${t}`:""}`),this.name="InvalidVoucherSignatureError"}};function re(e,t){if(t.length!==32)throw new P(`channelIdBytes must be 32 bytes, got ${t.length}`);if(e.sessionPublicKey.length!==32)throw new P(`sessionPublicKey must be 32 bytes, got ${e.sessionPublicKey.length}`);if(e.sessionSignature.length!==64)throw new P(`sessionSignature must be 64 bytes, got ${e.sessionSignature.length}`);let r=(0,I.voucherPayloadMessage)({channelId:t,cumulativeAmount:BigInt(e.payload.cumulativeAmount),sequenceNumber:e.payload.sequenceNumber});if(!Me.default.sign.detached.verify(r,e.sessionSignature,e.sessionPublicKey))throw new P("ed25519 verify rejected")}var x=class extends Error{constructor(r,n){super(`Scope violation: ${r}${n?` (${n})`:""}`);this.reason=r;this.name="ScopeViolationError"}};function ne(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 yt(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 Ke(e){let t="";for(let r of e)t+=r.toString(16).padStart(2,"0");return t}var bt=require("@solana/web3.js"),wt=require("@noble/hashes/utils");var ft=$e(require("tweetnacl"),1);var At=require("@noble/hashes/sha256");var Le="https://x402.dexter.cash",De=6;function C(e,t=De){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=De){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 se=require("@noble/hashes/utils"),vt=15e3;async function Re(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:(0,se.bytesToHex)(i.sessionPublicKey),sessionSignature:(0,se.bytesToHex)(i.sessionSignature),sessionRegistration:(0,se.bytesToHex)(i.sessionRegistration),network:n},o=await s(p,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(u),signal:AbortSignal.timeout(vt)}),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 He(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 Re(e,t,r,n,i.fetchImpl);return c.crystallized&&d!==void 0&&(e.lastCrystallizedCumulativeAtomic=d),c}var N="x-tab-voucher",_e=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)}},Ne=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 U(this.cumulativeAtomic.toString())}deliveredCumulative(){return U(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 St(e){if(typeof e!="string"||e.length===0)throw new S("signature_invalid",`missing ${N} 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:ie(r.sessionPublicKey),sessionRegistration:ie(r.sessionRegistration),sessionSignature:ie(r.sessionSignature)}}function ie(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 xt(e){if(!/^[0-9a-f]{64}$/i.test(e))throw new S("signature_invalid",`channelId must be 64-char hex, got "${e}"`);return ie(e)}function oe(e){let t=e.ledger??new V,r=new _e,n=typeof e.sellerPubkey=="string"?new qe.PublicKey(e.sellerPubkey):e.sellerPubkey,s=e.maxPerVoucherAtomic?BigInt(e.maxPerVoucherAtomic):BigInt(C(e.perUnit))*100n,i=e.facilitatorUrl??Le,p={thresholdAtomic:e.lockCadence?.thresholdAtomic??C("0.10"),onClose:e.lockCadence?.onClose??!0};return async(u,o,d)=>{try{let c=St(u.headers[N]),a=c.payload.channelId,y=xt(a),m=r.get(a);if(!m){let g=ee(c.sessionRegistration);await te(e.connection,g),m={registration:g,lastCumulativeAtomic:"0"},r.set(a,m)}re(c,y),ne({registration:m.registration,voucher:c,expectedCounterparty:n,previousCumulativeAtomic:m.lastCumulativeAtomic});let f=BigInt(c.payload.cumulativeAmount),w=BigInt(m.lastCumulativeAtomic),O=f-w;if(O>s)throw new x("cumulative_exceeds_cap",`single voucher increment ${O} exceeds maxPerVoucherAtomic ${s}`);let W=e.leaseTtlMs??3e5;if(!await t.tryAcquireLease(a,W))throw new S("channel_busy","another stream is live on this channel; tabs serve one stream at a time");let F=!1,K=()=>{F||(F=!0,t.releaseLease(a).catch(g=>{console.error("[tab/seller] failed to release channel lease:",g)}))};o.on("close",K),o.on("finish",K);let xe=async g=>{let v=g.lastCrystallizedCumulativeAtomic??"0";await He(g,a,i,e.network,p),g.lastCrystallizedCumulativeAtomic!==v&&await T(a,async()=>{let b=await t.get(a);b&&await t.set(a,{...b,lastCrystallizedCumulativeAtomic:g.lastCrystallizedCumulativeAtomic})}).catch(()=>{})},j=!1,X=()=>{!p.onClose||j||(j=!0,(async()=>{let g=await t.get(a);if(!g||!g.lastVoucher)return;let v=g.lastVoucher.payload.cumulativeAmount;(await Re(g,a,i,e.network)).crystallized&&await T(a,async()=>{let M=await t.get(a);M&&await t.set(a,{...M,lastCrystallizedCumulativeAtomic:v})})})().catch(()=>{}))};o.on("close",X),o.on("finish",X);let l=await t.get(a),h=l?BigInt(l.deliveredCumulativeAtomic):0n;await T(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 Ne(a,e.network,f,h,async g=>{let v=null;await T(a,async()=>{let b=await t.get(a),M=b?BigInt(b.deliveredCumulativeAtomic):0n,Oe=BigInt(g),nt=Oe>0n?M+Oe:M,ke={...b,lastVoucher:b?.lastVoucher??c,deliveredCumulativeAtomic:nt.toString(),lastCrystallizedCumulativeAtomic:b?.lastCrystallizedCumulativeAtomic??"0"};await t.set(a,ke),v=ke}),v&&xe(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 R||c instanceof P||c instanceof x){o.status(402).json({error:"invalid_voucher",reason:c.reason??"unknown",detail:c.message});return}d(c)}}}function ze(e){if(!e.tab)throw new Error("req.tab is missing \u2014 did tabMiddleware run on this route?");return e.tab}function We(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 ${U((s+w).toString())} beyond signed cumulative ${U(n.toString())} (per-request budget ${U(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 l(){m||(m=!0,e.write(`event: end
4
- data: {"chargedAtomic":"${i}"}
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}}var ke=require("@solana/web3.js");var E="solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp",_e="solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1",Re="solana:4uhcVJyU9pJkvQyS88uRDiswHXSCkY3z";var V="eip155:8453",Z="eip155:84532",Q="eip155:42161",ee="eip155:137",te="eip155:10",re="eip155:43114",ne="eip155:56",se="eip155:1187947933",oe="eip155:324705682",ie="eip155:1";var ae="EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v";var Ce="0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",at="0x55d398326f99059fF775485246999027B3197955",Ne="0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d",Ie={[ne]:Ne,[V]:Ce,[Z]:"0x036CbD53842c5426634e7929541eC2318f3dCF7e",[Q]:"0xaf88d065e77c8cC2239327C5EDb3A432268e5831",[ee]:"0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359",[te]:"0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85",[re]:"0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E",[se]:"0x85889c8c714505E0c94b30fcfcF64fE3Ac8FCb20",[oe]:"0x2e08028E3C4c2356572E096d8EF835cD5C6030bD",[ie]:"0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"},Ue={[at]:{symbol:"USDT",decimals:18},[Ne]:{symbol:"USDC",decimals:18}};var Lt={[ne]:56,[V]:8453,[Z]:84532,[Q]:42161,[ee]:137,[te]:10,[re]:43114,[se]:1187947933,[oe]:324705682,[ie]:1},Wt={[E]:"https://api.dexter.cash/api/solana/rpc",[_e]:"https://api.devnet.solana.com",[Re]:"https://api.testnet.solana.com"},Ft={[ne]:"https://api.dexter.cash/api/evm/bsc/rpc",[V]:"https://api.dexter.cash/api/base/rpc",[Z]:"https://sepolia.base.org",[Q]:"https://api.dexter.cash/api/evm/arbitrum/rpc",[ee]:"https://api.dexter.cash/api/evm/polygon/rpc",[te]:"https://api.dexter.cash/api/evm/optimism/rpc",[re]:"https://api.dexter.cash/api/evm/avalanche/rpc",[se]:"https://skale-base.skalenodes.com/v1/base",[oe]:"https://base-sepolia-testnet.skalenodes.com/v1/jubilant-horrible-ancha",[ie]:"https://eth.llamarpc.com"},$="https://x402.dexter.cash";function ce(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 ut(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 ue(e){return ct(JSON.stringify(e))}function K(e){return JSON.parse(ut(e))}function lt(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 B=class extends Error{status;body;constructor(t,r){super(`HTTP ${t}`),this.status=t,this.body=r}},le=class{facilitatorUrl;cachedSupported=null;cacheTime=0;CACHE_TTL_MS=6e4;timeoutMs;maxRetries;retryBaseMs;constructor(t=$,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 B(i.status,await i.text());return i}catch(i){if(n=i,s<this.maxRetries-1&&lt(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=K(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 B?`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=K(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 B?`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 pt(e){if(ce(e))return{address:ae,decimals:6};let t=Ie[e];if(t){let r=Ue[t]?.decimals??6;return{address:t,decimals:r}}return{address:ae,decimals:6}}function Oe(e){try{let t=K(e);return t?.accepted?.amount??t?.accepted?.maxAmountRequired}catch{return}}function M(e){let{payTo:t,facilitatorUrl:r=$,network:n=E,defaultTimeoutSeconds:s=60}=e,i=e.asset??pt(n),m=e.scheme??"exact";if(m==="tab"&&!ce(n))throw new Error(`scheme 'tab' is SVM-only; got network "${n}"`);let c=new le(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[U,O]of l)O.expiresAt<y&&l.delete(U);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 T(){if(o||(o=await c.getNetworkExtra(n)),ce(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 _(a,p){let{amountAtomic:y,timeoutSeconds:U=s}=p,O=await T(),D={scheme:m,network:n,amount:y,maxAmountRequired:y,asset:i.address,payTo:a,maxTimeoutSeconds:U,extra:O};return u(D),D}async function ye(a){let p=await f({amountAtomic:a.amountAtomic,resourceUrl:a.resourceUrl});return _(p,a)}async function Be(a){let{resourceUrl:p,description:y,mimeType:U="application/json"}=a,O={url:p,description:y,mimeType:U},D=await ye(a);return{x402Version:2,resource:O,accepts:[D],error:"Payment required"}}function be(a){return ue(a)}function Me(a){return{status:402,headers:{"PAYMENT-REQUIRED":be(a)},body:{}}}async function De(a,p){if(!p){let y=await f({paymentHeader:a});p=h(y),p||(p=await _(y,{amountAtomic:Oe(a)??"0",resourceUrl:""}))}return c.verifyPayment(a,p)}async function He(a,p){if(!p){let y=await f({paymentHeader:a});p=h(y),p||(p=await _(y,{amountAtomic:Oe(a)??"0",resourceUrl:""}))}return c.settlePayment(a,p)}return{buildRequirements:Be,encodeRequirements:be,create402Response:Me,verifyPayment:De,settlePayment:He,getPaymentAccept:ye,network:n,assetDecimals:i.decimals,facilitator:c}}var mt={"solana:mainnet":E};function Ve(e){let t=mt[e.network];if(!t)throw new Error(`tabChallengeMiddleware: unsupported network "${e.network}"`);let r=typeof e.sellerPubkey=="string"?e.sellerPubkey:e.sellerPubkey.toBase58();new ke.PublicKey(r);let n=M({payTo:r,network:t,scheme:"tab",facilitatorUrl:e.facilitatorUrl}),s=v(e.perUnit);return async(i,m,c)=>{if(i.headers[P])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})}}}var $e=require("@solana/web3.js");var dt={"solana:mainnet":E};function Ke(e){let t=dt[e.network];if(!t)throw new Error(`tabOrExactMiddleware: unsupported network "${e.network}"`);let r=typeof e.sellerPubkey=="string"?e.sellerPubkey:e.sellerPubkey.toBase58();new $e.PublicKey(r);let n=M({payTo:r,network:t,scheme:"tab",facilitatorUrl:e.facilitatorUrl}),s=M({payTo:r,network:t,scheme:"exact",facilitatorUrl:e.facilitatorUrl}),i=v(e.perUnit),m=G({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[P])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",ue({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)]),T={...h,accepts:[...h.accepts,...f.accepts]};o.set({"PAYMENT-REQUIRED":n.encodeRequirements(T)}).status(402).json({error:"Payment required",accepts:T.accepts,resource:T.resource})}catch(u){let h=u?.message??String(u);o.status(503).set({"Retry-After":"5"}).json({error:"challenge_unavailable",detail:h})}}}0&&(module.exports={FileVoucherStore,InMemoryVoucherStore,InvalidRegistrationError,InvalidVoucherError,InvalidVoucherSignatureError,OnChainVerificationError,ScopeViolationError,TAB_VOUCHER_HEADER,enforceScope,openSse,parseRegistration,requireTab,tabChallengeMiddleware,tabMiddleware,tabOrExactMiddleware,verifyRegistrationOnChain,verifyVoucherSignature});
6
+ `),e.end())}return{charge:c,send:a,end:y}}var B=require("fs"),le=require("path");function Et(e){return{payload:e.payload,sessionPublicKey:Ie(e.sessionPublicKey),sessionRegistration:Ie(e.sessionRegistration),sessionSignature:Ie(e.sessionSignature)}}function Ct(e){return{payload:e.payload,sessionPublicKey:Ue(e.sessionPublicKey),sessionRegistration:Ue(e.sessionRegistration),sessionSignature:Ue(e.sessionSignature)}}function Ie(e){let t="";for(let r of e)t+=r.toString(16).padStart(2,"0");return t}function Ue(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 ae=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)}},ce=class{constructor(t){this.dir=t}pathFor(t){if(!/^[a-z0-9_-]+$/i.test(t))throw new Error(`unsafe channelId for filesystem: ${t}`);return(0,le.join)(this.dir,`${t}.json`)}async get(t){try{let r=await B.promises.readFile(this.pathFor(t),"utf8");return Ct(JSON.parse(r))}catch(r){if(r?.code==="ENOENT")return null;throw r}}async set(t,r){let n=this.pathFor(t);await B.promises.mkdir((0,le.dirname)(n),{recursive:!0});let s=`${n}.tmp`;await B.promises.writeFile(s,JSON.stringify(Et(r))),await B.promises.rename(s,n)}async delete(t){try{await B.promises.unlink(this.pathFor(t))}catch(r){if(r?.code!=="ENOENT")throw r}}};var Qe=require("@solana/web3.js");var _="solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp",Fe="solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1",je="solana:4uhcVJyU9pJkvQyS88uRDiswHXSCkY3z";var L="eip155:8453",ue="eip155:84532",me="eip155:42161",pe="eip155:137",de="eip155:10",ge="eip155:43114",he="eip155:56",ye="eip155:1187947933",fe="eip155:324705682",Ae="eip155:1";var be="EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v";var Xe="0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",Pt="0x55d398326f99059fF775485246999027B3197955",Je="0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d",Ye={[he]:Je,[L]:Xe,[ue]:"0x036CbD53842c5426634e7929541eC2318f3dCF7e",[me]:"0xaf88d065e77c8cC2239327C5EDb3A432268e5831",[pe]:"0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359",[de]:"0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85",[ge]:"0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E",[ye]:"0x85889c8c714505E0c94b30fcfcF64fE3Ac8FCb20",[fe]:"0x2e08028E3C4c2356572E096d8EF835cD5C6030bD",[Ae]:"0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"},Ge={[Pt]:{symbol:"USDT",decimals:18},[Je]:{symbol:"USDC",decimals:18}};var mr={[he]:56,[L]:8453,[ue]:84532,[me]:42161,[pe]:137,[de]:10,[ge]:43114,[ye]:1187947933,[fe]:324705682,[Ae]:1},pr={[_]:"https://api.dexter.cash/api/solana/rpc",[Fe]:"https://api.devnet.solana.com",[je]:"https://api.testnet.solana.com"},dr={[he]:"https://api.dexter.cash/api/evm/bsc/rpc",[L]:"https://api.dexter.cash/api/base/rpc",[ue]:"https://sepolia.base.org",[me]:"https://api.dexter.cash/api/evm/arbitrum/rpc",[pe]:"https://api.dexter.cash/api/evm/polygon/rpc",[de]:"https://api.dexter.cash/api/evm/optimism/rpc",[ge]:"https://api.dexter.cash/api/evm/avalanche/rpc",[ye]:"https://skale-base.skalenodes.com/v1/base",[fe]:"https://base-sepolia-testnet.skalenodes.com/v1/jubilant-horrible-ancha",[Ae]:"https://eth.llamarpc.com"},D="https://x402.dexter.cash";function we(e){return e.startsWith("solana:")||e==="solana"}function Tt(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 Rt(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 ve(e){return Tt(JSON.stringify(e))}function H(e){return JSON.parse(Rt(e))}function _t(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 q=class extends Error{status;body;constructor(t,r){super(`HTTP ${t}`),this.status=t,this.body=r}},Se=class{facilitatorUrl;cachedSupported=null;cacheTime=0;CACHE_TTL_MS=6e4;timeoutMs;maxRetries;retryBaseMs;constructor(t=D,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 q(i.status,await i.text());return i}catch(i){if(n=i,s<this.maxRetries-1&&_t(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=H(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 q?`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=H(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 q?`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 Nt(e){if(we(e))return{address:be,decimals:6};let t=Ye[e];if(t){let r=Ge[t]?.decimals??6;return{address:t,decimals:r}}return{address:be,decimals:6}}function Ze(e){try{let t=H(e);return t?.accepted?.amount??t?.accepted?.maxAmountRequired}catch{return}}function z(e){let{payTo:t,facilitatorUrl:r=D,network:n=_,defaultTimeoutSeconds:s=60}=e,i=e.asset??Nt(n),p=e.scheme??"exact";if(p==="tab"&&!we(n))throw new Error(`scheme 'tab' is SVM-only; got network "${n}"`);let u=new Se(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)),we(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 O(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 W(l){let h=await f({amountAtomic:l.amountAtomic,resourceUrl:l.resourceUrl});return O(h,l)}async function F(l){let{resourceUrl:h,description:A,mimeType:g="application/json"}=l,v={url:h,description:A,mimeType:g},b=await W(l);return{x402Version:2,resource:v,accepts:[b],error:"Payment required"}}function K(l){return ve(l)}function xe(l){return{status:402,headers:{"PAYMENT-REQUIRED":K(l)},body:{}}}async function j(l,h){if(!h){let A=await f({paymentHeader:l});h=m(A),h||(h=await O(A,{amountAtomic:Ze(l)??"0",resourceUrl:""}))}return u.verifyPayment(l,h)}async function X(l,h){if(!h){let A=await f({paymentHeader:l});h=m(A),h||(h=await O(A,{amountAtomic:Ze(l)??"0",resourceUrl:""}))}return u.settlePayment(l,h)}return{buildRequirements:F,encodeRequirements:K,create402Response:xe,verifyPayment:j,settlePayment:X,getPaymentAccept:W,network:n,assetDecimals:i.decimals,facilitator:u}}var It={"solana:mainnet":_};function et(e){let t=It[e.network];if(!t)throw new Error(`tabChallengeMiddleware: unsupported network "${e.network}"`);let r=typeof e.sellerPubkey=="string"?e.sellerPubkey:e.sellerPubkey.toBase58();new Qe.PublicKey(r);let n=z({payTo:r,network:t,scheme:"tab",facilitatorUrl:e.facilitatorUrl}),s=C(e.perUnit);return async(i,p,u)=>{if(i.headers[N])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})}}}var tt=require("@solana/web3.js");var Ut={"solana:mainnet":_};function rt(e){let t=Ut[e.network];if(!t)throw new Error(`tabOrExactMiddleware: unsupported network "${e.network}"`);let r=typeof e.sellerPubkey=="string"?e.sellerPubkey:e.sellerPubkey.toBase58();new tt.PublicKey(r);let n=z({payTo:r,network:t,scheme:"tab",facilitatorUrl:e.facilitatorUrl}),s=z({payTo:r,network:t,scheme:"exact",facilitatorUrl:e.facilitatorUrl}),i=C(e.perUnit),p=oe({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[N])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",ve({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})}}}0&&(module.exports={FileChannelLedger,FileVoucherStore,InMemoryChannelLedger,InMemoryVoucherStore,InvalidRegistrationError,InvalidVoucherError,InvalidVoucherSignatureError,OnChainVerificationError,ScopeViolationError,TAB_VOUCHER_HEADER,enforceScope,openSse,parseRegistration,requireTab,tabChallengeMiddleware,tabMiddleware,tabOrExactMiddleware,verifyRegistrationOnChain,verifyVoucherSignature});
@@ -1,7 +1,123 @@
1
- import { TabNetworkId, HumanAmount, SignedVoucher, AtomicAmount } from '@dexterai/vault/types';
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://facilitator.dexter.cash. */
181
+ /** Facilitator base URL. Default: DEFAULT_FACILITATOR_URL (https://x402.dexter.cash). */
52
182
  facilitatorUrl?: string;
53
- /** Voucher persistence. Default: file-backed. */
54
- store?: VoucherStore;
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 };