@dexterai/x402 5.4.2 → 6.0.0-rc.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +19 -4
- package/REFERENCE.md +81 -8
- package/dist/adapters/index.d.cts +3 -3
- package/dist/adapters/index.d.ts +3 -3
- package/dist/batch-settlement/index.d.cts +3 -3
- package/dist/batch-settlement/index.d.ts +3 -3
- package/dist/batch-settlement/seller/index.d.cts +4 -4
- package/dist/batch-settlement/seller/index.d.ts +4 -4
- package/dist/client/index.cjs +1 -1
- package/dist/client/index.d.cts +6 -6
- package/dist/client/index.d.ts +6 -6
- package/dist/client/index.js +1 -1
- package/dist/{constants-Icc4CXNG.d.cts → constants-BLvApMMl.d.cts} +1 -1
- package/dist/{constants-Icc4CXNG.d.ts → constants-BLvApMMl.d.ts} +1 -1
- package/dist/react/index.cjs +1 -1
- package/dist/react/index.d.cts +4 -4
- package/dist/react/index.d.ts +4 -4
- package/dist/react/index.js +1 -1
- package/dist/server/index.cjs +1 -1
- package/dist/server/index.d.cts +5 -5
- package/dist/server/index.d.ts +5 -5
- package/dist/server/index.js +1 -1
- package/dist/{sessions-BG2OYLBi.d.cts → sessions-rPdO1CgK.d.cts} +35 -3
- package/dist/{sessions-BG2OYLBi.d.ts → sessions-rPdO1CgK.d.ts} +35 -3
- package/dist/tab/adapters/solana/index.cjs +1 -1
- package/dist/tab/adapters/solana/index.d.cts +181 -26
- package/dist/tab/adapters/solana/index.d.ts +181 -26
- package/dist/tab/adapters/solana/index.js +1 -1
- package/dist/tab/index.cjs +5 -5
- package/dist/tab/index.d.cts +126 -21
- package/dist/tab/index.d.ts +126 -21
- package/dist/tab/index.js +4 -4
- package/dist/tab/seller/index.cjs +4 -4
- package/dist/tab/seller/index.d.cts +34 -26
- package/dist/tab/seller/index.d.ts +34 -26
- package/dist/tab/seller/index.js +4 -4
- package/dist/{types-BdS_uvNs.d.cts → types-BbvuPNXl.d.cts} +2 -2
- package/dist/{types-xQu1U4xk.d.cts → types-BvQpNtFw.d.cts} +9 -1
- package/dist/{types-xQu1U4xk.d.ts → types-BvQpNtFw.d.ts} +9 -1
- package/dist/{types-DY8xKheW.d.ts → types-CkcNYx1Q.d.ts} +2 -2
- package/dist/{types-Co5YYhVB.d.ts → types-CwLPKV8T.d.ts} +3 -3
- package/dist/{types-CZHPdVkg.d.cts → types-DnhCOjDx.d.cts} +3 -3
- package/dist/{types-_wdHzVG-.d.ts → types-DwVhPqOZ.d.ts} +2 -2
- package/dist/{types-WMqs5xII.d.cts → types-JpkrtFbZ.d.cts} +2 -2
- package/dist/{types-CSuISjXA.d.cts → types-OHNNM1J2.d.cts} +122 -7
- package/dist/{types-CSuISjXA.d.ts → types-OHNNM1J2.d.ts} +122 -7
- package/dist/utils/index.d.cts +1 -1
- package/dist/utils/index.d.ts +1 -1
- package/dist/{x402-client-BQKDeNxq.d.cts → x402-client-B8zdDeat.d.cts} +2 -2
- package/dist/{x402-client-BQKDeNxq.d.ts → x402-client-B8zdDeat.d.ts} +2 -2
- package/package.json +7 -6
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
"use strict";var At=Object.create;var re=Object.defineProperty;var bt=Object.getOwnPropertyDescriptor;var vt=Object.getOwnPropertyNames;var wt=Object.getPrototypeOf,St=Object.prototype.hasOwnProperty;var xt=(e,t)=>{for(var r in t)re(e,r,{get:t[r],enumerable:!0})},Fe=(e,t,r,n)=>{if(t&&typeof t=="object"||typeof t=="function")for(let i of vt(t))!St.call(e,i)&&i!==r&&re(e,i,{get:()=>t[i],enumerable:!(n=bt(t,i))||n.enumerable});return e};var We=(e,t,r)=>(r=e!=null?At(wt(e)):{},Fe(t||!e||!e.__esModule?re(r,"default",{value:e,enumerable:!0}):r,e)),Ct=e=>Fe(re({},"__esModule",{value:!0}),e);var Yt={};xt(Yt,{FileChannelLedger:()=>ne,FileVoucherStore:()=>be,InMemoryChannelLedger:()=>B,InMemoryVoucherStore:()=>Ae,InvalidRegistrationError:()=>E,InvalidVoucherError:()=>S,InvalidVoucherSignatureError:()=>_,OnChainVerificationError:()=>N,RedisChannelLedger:()=>ae,ScopeViolationError:()=>x,TAB_VOUCHER_HEADER:()=>I,deserializeChannelLedgerEntry:()=>oe,enforceScope:()=>pe,openSse:()=>st,parseRegistration:()=>ue,requireTab:()=>it,serializeChannelLedgerEntry:()=>se,tabChallengeMiddleware:()=>gt,tabMiddleware:()=>fe,tabOrExactMiddleware:()=>yt,verifyRegistrationOnChain:()=>me,verifyVoucherSignature:()=>de});module.exports=Ct(Yt);var S=class extends Error{constructor(r,n){super(`Invalid voucher: ${r}${n?` (${n})`:""}`);this.reason=r;this.name="InvalidVoucherError"}};var L=require("fs"),ie=require("path"),je=new Map;function P(e,t){let n=(je.get(e)??Promise.resolve()).then(()=>t(),()=>t());return je.set(e,n.then(()=>{},()=>{})),n}var B=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),i=Date.now();if(n?.lease&&n.lease.heldUntilUnixMs>i)return!1;let s=n??{lastVoucher:null,deliveredCumulativeAtomic:"0",lastCrystallizedCumulativeAtomic:"0"};return this.map.set(t,{...s,lease:{heldUntilUnixMs:i+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 Ue(e){let t="";for(let r of e)t+=r.toString(16).padStart(2,"0");return t}function $e(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 se(e){return Xe(e)}function oe(e){return Je(e)}function Xe(e){return{lastVoucher:e.lastVoucher?{payload:e.lastVoucher.payload,sessionPublicKey:Ue(e.lastVoucher.sessionPublicKey),sessionRegistration:Ue(e.lastVoucher.sessionRegistration),sessionSignature:Ue(e.lastVoucher.sessionSignature)}:null,deliveredCumulativeAtomic:e.deliveredCumulativeAtomic,lastCrystallizedCumulativeAtomic:e.lastCrystallizedCumulativeAtomic,gateRefusedCumulativeAtomic:e.gateRefusedCumulativeAtomic,onChain:e.onChain,lease:e.lease}}function Je(e){return{lastVoucher:e.lastVoucher?{payload:e.lastVoucher.payload,sessionPublicKey:$e(e.lastVoucher.sessionPublicKey),sessionRegistration:$e(e.lastVoucher.sessionRegistration),sessionSignature:$e(e.lastVoucher.sessionSignature)}:null,deliveredCumulativeAtomic:e.deliveredCumulativeAtomic,lastCrystallizedCumulativeAtomic:e.lastCrystallizedCumulativeAtomic??"0",gateRefusedCumulativeAtomic:e.gateRefusedCumulativeAtomic,onChain:e.onChain,lease:e.lease}}var ne=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,ie.join)(this.dir,`${t}.json`)}async get(t){try{let r=await L.promises.readFile(this.pathFor(t),"utf8");return Je(JSON.parse(r))}catch(r){if(r?.code==="ENOENT")return null;throw r}}async set(t,r){let n=this.pathFor(t);await L.promises.mkdir((0,ie.dirname)(n),{recursive:!0});let i=`${n}.tmp`;await L.promises.writeFile(i,JSON.stringify(Xe(r))),await L.promises.rename(i,n)}async delete(t){try{await L.promises.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),i=Date.now();if(n?.lease&&n.lease.heldUntilUnixMs>i)return!1;let s=n??{lastVoucher:null,deliveredCumulativeAtomic:"0",lastCrystallizedCumulativeAtomic:"0"};return await this.set(t,{...s,lease:{heldUntilUnixMs:i+r}}),!0})}async releaseLease(t){await P(t,async()=>{let r=await this.get(t);r&&await this.set(t,{...r,lease:void 0})})}};var Ge=require("crypto");var Et='if redis.call("get", KEYS[1]) == ARGV[1] then return redis.call("del", KEYS[1]) else return 0 end',ae=class{constructor(t,r="tab:"){this.client=t;this.keyPrefix=r}leaseTokens=new Map;ledgerKey(t){return`${this.keyPrefix}ledger:${t}`}leaseKey(t){return`${this.keyPrefix}lease:${t}`}async get(t){let r=await this.client.get(this.ledgerKey(t));return r===null?null:oe(JSON.parse(r))}async set(t,r){let n=se({...r,lease:void 0});await this.client.set(this.ledgerKey(t),JSON.stringify(n))}async delete(t){this.leaseTokens.delete(t),await this.client.del(this.ledgerKey(t),this.leaseKey(t))}async tryAcquireLease(t,r){let n=(0,Ge.randomUUID)();return await this.client.set(this.leaseKey(t),n,"PX",Math.max(1,Math.floor(r)),"NX")!=="OK"?!1:(this.leaseTokens.set(t,n),!0)}async releaseLease(t){let r=this.leaseTokens.get(t);r!==void 0&&(this.leaseTokens.delete(t),await this.client.eval(Et,1,this.leaseKey(t),r))}};var nt=require("@solana/web3.js");var Ze=We(require("tweetnacl"),1),Tt=require("@noble/hashes/sha256"),Pt=require("@noble/curves/p256"),ce=require("@solana/web3.js");var $=require("@dexterai/vault/messages");var Ve=require("@dexterai/vault/instructions"),Rt=require("@dexterai/vault/precompile"),M=require("@dexterai/vault/constants");var le=require("@dexterai/vault/session"),Le="OTS_SESSION_REGISTER_V2",E=class extends Error{constructor(r,n){super(`Invalid registration: ${r}${n?` (${n})`:""}`);this.reason=r;this.name="InvalidRegistrationError"}};function ue(e){if(e.length!==188)throw new E("wrong_length",`expected 188, got ${e.length}`);let t=new TextDecoder().decode(e.slice(0,Le.length));if(t!==Le)throw new E("wrong_domain",`got "${t}"`);for(let y=Le.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 ce.PublicKey(e.slice(32,64)),i=new ce.PublicKey(e.slice(64,96)),s=e.slice(96,128),h=r.getBigUint64(128,!0),c=r.getBigInt64(136,!0),a=new ce.PublicKey(e.slice(144,176)),m=r.getUint32(176,!0),l=r.getBigUint64(180,!0);if(!n.equals(M.DEXTER_VAULT_PROGRAM_ID))throw new E("wrong_program",`${n.toBase58()} is not ${M.DEXTER_VAULT_PROGRAM_ID.toBase58()}`);if(h===0n)throw new E("cap_zero");let o=BigInt(Math.floor(Date.now()/1e3));if(c<=o)throw new E("expiry_in_past",`expires_at=${c}, now=${o}`);return{programId:n,vaultPda:i,sessionPubkey:new Uint8Array(s),maxAmount:h,expiresAt:c,allowedCounterparty:a,nonce:m,maxRevolvingCapacity:l}}var N=class extends Error{constructor(r,n){super(`On-chain verification failed: ${r}${n?` (${n})`:""}`);this.reason=r;this.name="OnChainVerificationError"}};async function me(e,t){let r=await(0,le.fetchSessionAccount)(e,t.vaultPda,t.allowedCounterparty);if(!r||r.version===0)throw new N("session_not_active","no live SessionAccount PDA for this (vault, counterparty) \u2014 revoked, expiry-swept, or never registered");if(!(0,le.isSessionLive)(r))throw new N("session_not_active","SessionAccount PDA is present but expired");if(!_t(r.session.sessionPubkey,t.sessionPubkey))throw new N("session_pubkey_mismatch",`on-chain ${Ye(r.session.sessionPubkey)} != registration ${Ye(t.sessionPubkey)}`);let n=r.session.spent,i=r.session.crystallizedCumulative;return{frontierAtomic:(n>i?n:i).toString(),spentAtomic:n.toString(),crystallizedCumulativeAtomic:i.toString()}}var _=class extends Error{constructor(t){super(`Invalid voucher signature${t?`: ${t}`:""}`),this.name="InvalidVoucherSignatureError"}};function de(e,t){if(t.length!==32)throw new _(`channelIdBytes must be 32 bytes, got ${t.length}`);if(e.sessionPublicKey.length!==32)throw new _(`sessionPublicKey must be 32 bytes, got ${e.sessionPublicKey.length}`);if(e.sessionSignature.length!==64)throw new _(`sessionSignature must be 64 bytes, got ${e.sessionSignature.length}`);let r=(0,$.voucherPayloadMessage)({channelId:t,cumulativeAmount:BigInt(e.payload.cumulativeAmount),sequenceNumber:e.payload.sequenceNumber});if(!Ze.default.sign.detached.verify(r,e.sessionSignature,e.sessionPublicKey))throw new _("ed25519 verify rejected")}var x=class extends Error{constructor(r,n){super(`Scope violation: ${r}${n?` (${n})`:""}`);this.reason=r;this.name="ScopeViolationError"}};function pe(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 _t(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 Ye(e){let t="";for(let r of e)t+=r.toString(16).padStart(2,"0");return t}var It=require("@solana/web3.js"),Ot=require("@noble/hashes/utils");var Nt=We(require("tweetnacl"),1);var kt=require("@noble/hashes/sha256");var Qe="https://x402.dexter.cash",et=6;function R(e,t=et){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 i=n.padEnd(t,"0"),s=`${r}${i}`.replace(/^0+(?=\d)/,"");return s===""?"0":s}function V(e,t=et){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",i=r.slice(-t).replace(/0+$/,"");return i?`${n}.${i}`:n}var ge=require("@noble/hashes/utils"),Ut=15e3,$t=["claim_already_exists","non_monotonic_cumulative"],Vt="below_lock_cadence";function he(e){return typeof e=="string"&&e.includes(Vt)}function Lt(e){return`${e.slice(0,16)}\u2026`}function tt(e,t,r,n){let i=Lt(e);if(n.crystallized){console.info(`[tab/seller] crystallize OK channel=${i} cumulative=${t} seq=${r}${n.claimPda?` claimPda=${n.claimPda}`:""} \u2014 LockVoucher landed`);return}if(n.error){if(he(n.error)){console.warn(`[tab/seller] crystallize refused below the facilitator's cadence gate channel=${i} cumulative=${t} seq=${r}: ${n.error} \u2014 benign: this facilitator's cadence engine owns the protection cadence for the span; gate-refused watermark advanced, re-attempts after the next signed voucher`);return}if($t.some(s=>n.error.includes(s))){console.warn(`[tab/seller] crystallize duplicate channel=${i} cumulative=${t} seq=${r}: ${n.error} \u2014 voucher already secured on-chain (benign, no action)`);return}console.error(`[tab/seller] crystallize FAILED channel=${i} cumulative=${t} seq=${r}: ${n.error} \u2014 lock NOT secured (degraded: best-effort path, no watermark advance, retries at next threshold/close; seller exposure stays unsecured until it lands)`)}}async function Be(e,t,r,n,i=fetch){let s=e.lastVoucher;if(!s)return{crystallized:!1};if(s.payload.channelId!==t){let c={crystallized:!1,error:"channel_id_mismatch"};return tt(t,s.payload.cumulativeAmount,s.payload.sequenceNumber,c),c}let h=c=>(tt(t,s.payload.cumulativeAmount,s.payload.sequenceNumber,c),c);try{let c=`${r.replace(/\/$/,"")}/tab/lock`,a={channelId:s.payload.channelId,cumulativeAmount:s.payload.cumulativeAmount,sequenceNumber:s.payload.sequenceNumber,sessionPublicKey:(0,ge.bytesToHex)(s.sessionPublicKey),sessionSignature:(0,ge.bytesToHex)(s.sessionSignature),sessionRegistration:(0,ge.bytesToHex)(s.sessionRegistration),network:n},m=await i(c,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(a),signal:AbortSignal.timeout(Ut)}),l=await m.text();if(!m.ok)return h({crystallized:!1,error:`tab lock ${m.status}: ${l.slice(0,200)}`});let o;try{let y=JSON.parse(l);y&&typeof y.claimPda=="string"&&(o=y.claimPda)}catch{}return h({crystallized:!0,claimPda:o})}catch(c){return h({crystallized:!1,error:String(c?.message??c)})}}async function rt(e,t,r,n,i,s={}){let h=BigInt(e.deliveredCumulativeAtomic),c=BigInt(e.lastCrystallizedCumulativeAtomic??"0"),a=BigInt(i.thresholdAtomic);if(h-c<a)return{crystallized:!1};let m=e.lastVoucher?.payload.cumulativeAmount,l=e.gateRefusedCumulativeAtomic;if(m!==void 0&&l!==void 0&&BigInt(m)<=BigInt(l))return{crystallized:!1};let o=await Be(e,t,r,n,s.fetchImpl);return o.crystallized&&m!==void 0?(e.lastCrystallizedCumulativeAtomic=m,e.gateRefusedCumulativeAtomic=void 0):he(o.error)&&m!==void 0&&(e.gateRefusedCumulativeAtomic=m),o}var I="x-tab-voucher",Me=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)}},De=class{constructor(t,r,n,i,s,h){this.recordDeliveredImpl=s;this.chargeImpl=h;this.channelId=t,this.network=r,this.cumulativeAtomic=n,this.deliveredBaselineAtomic=i}channelId;network;sessionPublicKey=null;cumulativeAtomic;deliveredBaselineAtomic;cumulative(){return V(this.cumulativeAtomic.toString())}deliveredCumulative(){return V(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 Bt(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:ye(r.sessionPublicKey),sessionRegistration:ye(r.sessionRegistration),sessionSignature:ye(r.sessionSignature)}}function ye(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 Mt(e){if(!/^[0-9a-f]{64}$/i.test(e))throw new S("signature_invalid",`channelId must be 64-char hex, got "${e}"`);return ye(e)}function fe(e){let t=e.ledger??new B,r=new Me,n=typeof e.sellerPubkey=="string"?new nt.PublicKey(e.sellerPubkey):e.sellerPubkey,i=e.maxPerVoucherAtomic?BigInt(e.maxPerVoucherAtomic):BigInt(R(e.perUnit))*100n,s=e.facilitatorUrl??Qe,h={thresholdAtomic:e.lockCadence?.thresholdAtomic??R("0.10"),onClose:e.lockCadence?.onClose??!0};return async(c,a,m)=>{try{let l=Bt(c.headers[I]),o=l.payload.channelId,y=Mt(o),d=r.get(o),f=null;if(!d){let p=ue(l.sessionRegistration);f=(await me(e.connection,p))?.frontierAtomic??null;let v=await t.get(o);d={registration:p,lastCumulativeAtomic:v?.lastVoucher?.payload.cumulativeAmount??"0"},r.set(o,d)}de(l,y),pe({registration:d.registration,voucher:l,expectedCounterparty:n,previousCumulativeAtomic:d.lastCumulativeAtomic});let b=BigInt(l.payload.cumulativeAmount),K=BigInt(d.lastCumulativeAtomic),z=b-K;if(z>i)throw new x("cumulative_exceeds_cap",`single voucher increment ${z} exceeds maxPerVoucherAtomic ${i}`);let Ie=e.leaseTtlMs??3e5;if(!await t.tryAcquireLease(o,Ie))throw new S("channel_busy","another stream is live on this channel; tabs serve one stream at a time");let H=!1,ee=()=>{H||(H=!0,t.releaseLease(o).catch(p=>{console.error("[tab/seller] failed to release channel lease:",p)}))};a.on("close",ee),a.on("finish",ee);let Oe=async p=>{let T=p.lastCrystallizedCumulativeAtomic??"0",v=p.gateRefusedCumulativeAtomic;await rt(p,o,s,e.network,h),(p.lastCrystallizedCumulativeAtomic!==T||p.gateRefusedCumulativeAtomic!==v)&&await P(o,async()=>{let w=await t.get(o);w&&await t.set(o,{...w,lastCrystallizedCumulativeAtomic:p.lastCrystallizedCumulativeAtomic,gateRefusedCumulativeAtomic:p.gateRefusedCumulativeAtomic})}).catch(w=>{console.error(`[tab/seller] crystallize watermark persist FAILED channel=${o.slice(0,16)}\u2026 (expect a benign duplicate/below-cadence warn on the next request):`,w)})},te=!1,u=()=>{!h.onClose||te||(te=!0,(async()=>{let p=await t.get(o);if(!p||!p.lastVoucher)return;let T=p.lastVoucher.payload.cumulativeAmount;if(p.gateRefusedCumulativeAtomic!==void 0&&BigInt(T)<=BigInt(p.gateRefusedCumulativeAtomic))return;let v=await Be(p,o,s,e.network);v.crystallized?await P(o,async()=>{let w=await t.get(o);w&&await t.set(o,{...w,lastCrystallizedCumulativeAtomic:T,gateRefusedCumulativeAtomic:void 0})}):he(v.error)&&await P(o,async()=>{let w=await t.get(o);w&&await t.set(o,{...w,gateRefusedCumulativeAtomic:T})})})().catch(p=>{console.error(`[tab/seller] close-path crystallize CRASHED channel=${o.slice(0,16)}\u2026 (final voucher NOT locked; exposure unsecured until the next request retries):`,p)}))};a.on("close",u),a.on("finish",u);let g=await t.get(o),A=!g?.lastVoucher&&BigInt(g?.deliveredCumulativeAtomic??"0")===0n,C=f!==null&&A&&BigInt(f)>0n?f:null;C!==null&&console.info(`[tab/seller] channel ${o.slice(0,16)}\u2026 resumed: seeding delivered baseline from chain frontier ${C} (session already settled/locked up to it \u2014 that span is not deliverable budget)`);let O=C!==null?BigInt(C):g?BigInt(g.deliveredCumulativeAtomic):0n;await P(o,async()=>{let p=await t.get(o);await t.set(o,{...p,lastVoucher:l,deliveredCumulativeAtomic:C??(p?p.deliveredCumulativeAtomic:"0"),lastCrystallizedCumulativeAtomic:C??p?.lastCrystallizedCumulativeAtomic??"0"})}),r.update(o,l.payload.cumulativeAmount);let U=new De(o,e.network,b,O,async p=>{let T=null;await P(o,async()=>{let v=await t.get(o),w=v?BigInt(v.deliveredCumulativeAtomic):0n,He=BigInt(p),ft=He>0n?w+He:w,qe={...v,lastVoucher:v?.lastVoucher??l,deliveredCumulativeAtomic:ft.toString(),lastCrystallizedCumulativeAtomic:v?.lastCrystallizedCumulativeAtomic??"0"};await t.set(o,qe),T=qe}),T&&Oe(T).catch(v=>{console.error(`[tab/seller] crystallize cadence CRASHED channel=${o.slice(0,16)}\u2026 (threshold lock not attempted; retries at the next delivery):`,v)})},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.")});U.setSessionPublicKey(l.sessionPublicKey),c.tab=U,m()}catch(l){if(l instanceof S||l instanceof E||l instanceof N||l instanceof _||l instanceof x){a.status(402).json({error:"invalid_voucher",reason:l.reason??"unknown",detail:l.message});return}m(l)}}}function it(e){if(!e.tab)throw new Error("req.tab is missing \u2014 did tabMiddleware run on this route?");return e.tab}function st(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(R(r.cumulative())),i=BigInt(R(r.deliveredCumulative())),s=n-i;s<0n&&(s=0n);let h=t.perUnit?BigInt(R(t.perUnit)):null,c=0n,a=!1;async function m(){await r.recordDelivered(c.toString())}e.on("close",()=>{a||(a=!0,m().catch(d=>{console.error("[tab/seller] terminal persist failed on disconnect:",d)}))});async function l(d=1){if(a)throw new Error("meter ended");if(h===null)throw new Error("charge() needs options.perUnit");let f=h*BigInt(d),b=c+f;if(b>s)throw a=!0,await m(),new x("cumulative_exceeds_cap",`chunk would push delivered to ${V((i+b).toString())} beyond signed cumulative ${V(n.toString())} (per-request budget ${V(s.toString())})`);c=b}function o(d){if(a)throw new Error("meter ended");let b=(typeof d=="string"?d:Buffer.from(d).toString("utf8")).replace(/\\/g,"\\\\").replace(/\n/g,"\\n");e.write(`data: ${b}
|
|
1
|
+
"use strict";var gn=Object.create;var Ae=Object.defineProperty;var hn=Object.getOwnPropertyDescriptor;var fn=Object.getOwnPropertyNames;var yn=Object.getPrototypeOf,vn=Object.prototype.hasOwnProperty;var An=(e,t)=>{for(var n in t)Ae(e,n,{get:t[n],enumerable:!0})},bt=(e,t,n,r)=>{if(t&&typeof t=="object"||typeof t=="function")for(let i of fn(t))!vn.call(e,i)&&i!==n&&Ae(e,i,{get:()=>t[i],enumerable:!(r=hn(t,i))||r.enumerable});return e};var wt=(e,t,n)=>(n=e!=null?gn(yn(e)):{},bt(t||!e||!e.__esModule?Ae(n,"default",{value:e,enumerable:!0}):n,e)),bn=e=>bt(Ae({},"__esModule",{value:!0}),e);var mr={};An(mr,{FileChannelLedger:()=>be,FileVoucherStore:()=>De,InMemoryChannelLedger:()=>Q,InMemoryVoucherStore:()=>Ke,InvalidRegistrationError:()=>B,InvalidVoucherError:()=>w,InvalidVoucherSignatureError:()=>V,OnChainVerificationError:()=>E,RedisChannelLedger:()=>xe,ScopeViolationError:()=>T,TAB_VOUCHER_HEADER:()=>F,deserializeChannelLedgerEntry:()=>Se,enforceScope:()=>Ce,openSse:()=>Gt,parseRegistration:()=>oe,requireTab:()=>Wt,serializeChannelLedgerEntry:()=>_e,tabChallengeMiddleware:()=>an,tabMiddleware:()=>Ue,tabOrExactMiddleware:()=>ln,verifyRegistrationOnChain:()=>se,verifyVoucherSignature:()=>Ee});module.exports=bn(mr);var w=class extends Error{constructor(n,r){super(`Invalid voucher: ${n}${r?` (${r})`:""}`);this.reason=n;this.name="InvalidVoucherError"}reason};var Z=require("fs"),we=require("path"),_t=new Map;function K(e,t){let r=(_t.get(e)??Promise.resolve()).then(()=>t(),()=>t());return _t.set(e,r.then(()=>{},()=>{})),r}var Q=class{map=new Map;async get(t){return this.map.get(t)??null}async set(t,n){this.map.set(t,n)}async delete(t){this.map.delete(t)}async tryAcquireLease(t,n){return K(t,async()=>{let r=this.map.get(t),i=Date.now();if(r?.lease&&r.lease.heldUntilUnixMs>i)return!1;let o=r??{lastVoucher:null,deliveredCumulativeAtomic:"0",lastCrystallizedCumulativeAtomic:"0"};return this.map.set(t,{...o,lease:{heldUntilUnixMs:i+n}}),!0})}async releaseLease(t){await K(t,async()=>{let n=this.map.get(t);n&&this.map.set(t,{...n,lease:void 0})})}};function Qe(e){let t="";for(let n of e)t+=n.toString(16).padStart(2,"0");return t}function et(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 n=0;n<t.length;n++)t[n]=parseInt(e.substr(n*2,2),16);return t}function _e(e){return St(e)}function Se(e){return xt(e)}function St(e){return{lastVoucher:e.lastVoucher?{payload:e.lastVoucher.payload,sessionPublicKey:Qe(e.lastVoucher.sessionPublicKey),sessionRegistration:Qe(e.lastVoucher.sessionRegistration),sessionSignature:Qe(e.lastVoucher.sessionSignature)}:null,deliveredCumulativeAtomic:e.deliveredCumulativeAtomic,lastCrystallizedCumulativeAtomic:e.lastCrystallizedCumulativeAtomic,gateRefusedCumulativeAtomic:e.gateRefusedCumulativeAtomic,onChain:e.onChain,lease:e.lease}}function xt(e){return{lastVoucher:e.lastVoucher?{payload:e.lastVoucher.payload,sessionPublicKey:et(e.lastVoucher.sessionPublicKey),sessionRegistration:et(e.lastVoucher.sessionRegistration),sessionSignature:et(e.lastVoucher.sessionSignature)}:null,deliveredCumulativeAtomic:e.deliveredCumulativeAtomic,lastCrystallizedCumulativeAtomic:e.lastCrystallizedCumulativeAtomic??"0",gateRefusedCumulativeAtomic:e.gateRefusedCumulativeAtomic,onChain:e.onChain,lease:e.lease}}var be=class{constructor(t){this.dir=t}dir;pathFor(t){if(!/^[a-z0-9_-]+$/i.test(t))throw new Error(`unsafe channelId for filesystem: ${t}`);return(0,we.join)(this.dir,`${t}.json`)}async get(t){try{let n=await Z.promises.readFile(this.pathFor(t),"utf8");return xt(JSON.parse(n))}catch(n){if(n?.code==="ENOENT")return null;throw n}}async set(t,n){let r=this.pathFor(t);await Z.promises.mkdir((0,we.dirname)(r),{recursive:!0});let i=`${r}.tmp`;await Z.promises.writeFile(i,JSON.stringify(St(n))),await Z.promises.rename(i,r)}async delete(t){try{await Z.promises.unlink(this.pathFor(t))}catch(n){if(n?.code!=="ENOENT")throw n}}async tryAcquireLease(t,n){return K(t,async()=>{let r=await this.get(t),i=Date.now();if(r?.lease&&r.lease.heldUntilUnixMs>i)return!1;let o=r??{lastVoucher:null,deliveredCumulativeAtomic:"0",lastCrystallizedCumulativeAtomic:"0"};return await this.set(t,{...o,lease:{heldUntilUnixMs:i+n}}),!0})}async releaseLease(t){await K(t,async()=>{let n=await this.get(t);n&&await this.set(t,{...n,lease:void 0})})}};var Rt=require("crypto");var wn='if redis.call("get", KEYS[1]) == ARGV[1] then return redis.call("del", KEYS[1]) else return 0 end',xe=class{constructor(t,n="tab:"){this.client=t;this.keyPrefix=n}client;keyPrefix;leaseTokens=new Map;ledgerKey(t){return`${this.keyPrefix}ledger:${t}`}leaseKey(t){return`${this.keyPrefix}lease:${t}`}async get(t){let n=await this.client.get(this.ledgerKey(t));return n===null?null:Se(JSON.parse(n))}async set(t,n){let r=_e({...n,lease:void 0});await this.client.set(this.ledgerKey(t),JSON.stringify(r))}async delete(t){this.leaseTokens.delete(t),await this.client.del(this.ledgerKey(t),this.leaseKey(t))}async tryAcquireLease(t,n){let r=(0,Rt.randomUUID)();return await this.client.set(this.leaseKey(t),r,"PX",Math.max(1,Math.floor(n)),"NX")!=="OK"?!1:(this.leaseTokens.set(t,r),!0)}async releaseLease(t){let n=this.leaseTokens.get(t);n!==void 0&&(this.leaseTokens.delete(t),await this.client.eval(wn,1,this.leaseKey(t),n))}};var zt=require("@solana/web3.js");var Ct=wt(require("tweetnacl"),1),Sn=require("@noble/hashes/sha256"),xn=require("@noble/curves/p256"),Re=require("@solana/web3.js");var P=require("@dexterai/vault/messages");var tt=require("@dexterai/vault/instructions"),_n=require("@dexterai/vault/precompile"),W=require("@dexterai/vault/constants");var X=require("@dexterai/vault/session"),ee=require("@dexterai/vault/messages"),nt="OTS_SESSION_REGISTER_V2";function G(e){return e>=2147483648?2:1}var B=class extends Error{constructor(n,r){super(`Invalid registration: ${n}${r?` (${r})`:""}`);this.reason=n;this.name="InvalidRegistrationError"}reason};function oe(e){if(e.length!==188)throw new B("wrong_length",`expected 188, got ${e.length}`);let t=new TextDecoder().decode(e.slice(0,nt.length));if(t!==nt)throw new B("wrong_domain",`got "${t}"`);for(let g=nt.length;g<32;g++)if(e[g]!==0)throw new B("wrong_domain",`non-NUL padding at byte ${g}`);let n=new DataView(e.buffer,e.byteOffset,e.byteLength),r=new Re.PublicKey(e.slice(32,64)),i=new Re.PublicKey(e.slice(64,96)),o=e.slice(96,128),c=n.getBigUint64(128,!0),a=n.getBigInt64(136,!0),u=new Re.PublicKey(e.slice(144,176)),h=n.getUint32(176,!0),d=n.getBigUint64(180,!0);if(!r.equals(W.DEXTER_VAULT_PROGRAM_ID))throw new B("wrong_program",`${r.toBase58()} is not ${W.DEXTER_VAULT_PROGRAM_ID.toBase58()}`);if(c===0n)throw new B("cap_zero");return{programId:r,vaultPda:i,sessionPubkey:new Uint8Array(o),maxAmount:c,expiresAt:a,allowedCounterparty:u,nonce:h,maxRevolvingCapacity:d}}var E=class extends Error{constructor(n,r){super(`On-chain verification failed: ${n}${r?` (${r})`:""}`);this.reason=n;this.name="OnChainVerificationError"}reason};async function se(e,t){let[n]=(0,X.deriveSessionPda)(t.vaultPda,t.allowedCounterparty,t.programId),r=await e.getAccountInfo(n,"finalized");if(!r)throw new E("session_not_active","no finalized SessionAccount PDA for this (vault, counterparty) \u2014 reservation may be confirmed-only, revoked, expiry-swept, or never registered");if(!r.owner.equals(t.programId))throw new E("wrong_program",`SessionAccount owner ${r.owner.toBase58()} != ${t.programId.toBase58()}`);let i;try{i=(0,X.decodeSessionAccount)(n,r.data)}catch(s){throw new E("registration_state_mismatch",`finalized SessionAccount decode failed: ${s instanceof Error?s.message:String(s)}`)}if(i.version===0)throw new E("session_not_active","finalized SessionAccount is cleared");if(!(0,X.isSessionLive)(i))throw new E("session_not_active","SessionAccount PDA is present but expired");if(!Pt(i.session.sessionPubkey,t.sessionPubkey))throw new E("session_pubkey_mismatch",`on-chain ${Et(i.session.sessionPubkey)} != registration ${Et(t.sessionPubkey)}`);let o=G(t.nonce),c=G(i.session.nonce);if(c!==o)throw new E("wire_version_mismatch",`active SessionAccount is V${c}, voucher registration is V${o}`);let a=Number.isSafeInteger(i.session.expiresAt)&&BigInt(i.session.expiresAt)===t.expiresAt;if(!(i.address===n.toBase58()&&i.vault===t.vaultPda.toBase58()&&i.session.maxAmount===t.maxAmount&&a&&i.session.allowedCounterparty===t.allowedCounterparty.toBase58()&&i.session.nonce===t.nonce&&i.session.maxRevolvingCapacity===t.maxRevolvingCapacity))throw new E("registration_state_mismatch","188-byte registration does not exactly match the active SessionAccount immutable fields");let h=i.session.spent,d=i.session.crystallizedCumulative,g=h>d?h:d;return{sessionAccountVersion:i.version,wireVersion:c,frontierAtomic:g.toString(),spentAtomic:h.toString(),currentOutstandingAtomic:i.session.currentOutstanding.toString(),crystallizedCumulativeAtomic:d.toString()}}var V=class extends Error{constructor(t){super(`Invalid voucher signature${t?`: ${t}`:""}`),this.name="InvalidVoucherSignatureError"}};function Ee(e,t){if(t.length!==32)throw new V(`channelIdBytes must be 32 bytes, got ${t.length}`);if(e.sessionPublicKey.length!==32)throw new V(`sessionPublicKey must be 32 bytes, got ${e.sessionPublicKey.length}`);if(e.sessionSignature.length!==64)throw new V(`sessionSignature must be 64 bytes, got ${e.sessionSignature.length}`);let n=oe(e.sessionRegistration);if(!Pt(n.sessionPubkey,e.sessionPublicKey))throw new V("registration session key does not match voucher session key");let r=n.nonce>=2147483648,i=G(n.nonce),o=G(e.payload.sequenceNumber);if(i!==o)throw new V(`voucher wire V${o} does not match registration wire V${i}`);let c;if(r){(0,ee.sessionVoucherV2AuthorizationNonce)(n.nonce),(0,ee.voucherV2SequenceOrdinal)(e.payload.sequenceNumber);let[u]=(0,X.deriveSessionPda)(n.vaultPda,n.allowedCounterparty,n.programId);c=(0,ee.contextBoundVoucherV2Message)({programId:n.programId,vaultPda:n.vaultPda,sessionPda:u,seller:n.allowedCounterparty,sessionNonce:n.nonce,channelId:t,cumulativeAmount:BigInt(e.payload.cumulativeAmount),sequenceNumber:e.payload.sequenceNumber})}else c=(0,P.voucherPayloadMessage)({channelId:t,cumulativeAmount:BigInt(e.payload.cumulativeAmount),sequenceNumber:e.payload.sequenceNumber});if(!Ct.default.sign.detached.verify(c,e.sessionSignature,e.sessionPublicKey))throw new V("ed25519 verify rejected")}var T=class extends Error{constructor(n,r){super(`Scope violation: ${n}${r?` (${r})`:""}`);this.reason=n;this.name="ScopeViolationError"}reason};function Ce(e){let t=BigInt(e.voucher.payload.cumulativeAmount);if(t>e.registration.maxAmount)throw new T("cumulative_exceeds_cap",`${t} > ${e.registration.maxAmount}`);let n=BigInt(Math.floor(Date.now()/1e3));if(n>=e.registration.expiresAt)throw new T("session_expired",`now=${n} >= expiresAt=${e.registration.expiresAt}`);if(!e.registration.allowedCounterparty.equals(e.expectedCounterparty))throw new T("wrong_counterparty",`${e.registration.allowedCounterparty.toBase58()} != ${e.expectedCounterparty.toBase58()}`);if(e.previousCumulativeAtomic!==void 0){let r=BigInt(e.previousCumulativeAtomic);if(t<=r)throw new T("non_monotonic",`cumulative=${t} not > previous=${r}`)}}function Pt(e,t){if(e.length!==t.length)return!1;for(let n=0;n<e.length;n++)if(e[n]!==t[n])return!1;return!0}function Et(e){let t="";for(let n of e)t+=n.toString(16).padStart(2,"0");return t}var On=require("@solana/web3.js"),kn=require("@noble/hashes/utils");var Rn=wt(require("tweetnacl"),1),En=require("@solana/web3.js"),Pe=require("@dexterai/vault/messages"),Cn=require("@dexterai/vault/session");var Pn=require("@noble/hashes/sha256");var Rr=(1n<<64n)-1n;var ot=require("@noble/hashes/sha256"),j=require("@noble/hashes/utils"),Vt=/^[0-9a-f]{64}$/,Tt=/^[A-Za-z0-9][A-Za-z0-9._:-]{0,199}$/,Vn=/^[1-9A-HJ-NP-Za-km-z]{32,100}$/,It="solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp",Nt="dexter-native-tab-v2-reservation/v1",Tn=`${Nt}:`;function rt(e){if(e===null||typeof e=="string"||typeof e=="boolean")return e;if(typeof e=="number"){if(!Number.isFinite(e))throw new Error("canonical_non_finite_number");return e}if(typeof e=="bigint")return e.toString();if(Array.isArray(e))return e.map(rt);if(typeof e=="object")return Object.fromEntries(Object.entries(e).filter(([,t])=>t!==void 0).sort(([t],[n])=>t.localeCompare(n)).map(([t,n])=>[t,rt(n)]));throw new Error("canonical_value_unsupported")}function Ot(e){return JSON.stringify(rt(e))}function it(e){if(e==="solana:mainnet"||e===It)return It;throw new Error("native_tab_v2_reservation_network_unsupported")}function st(e){return(0,j.bytesToHex)((0,ot.sha256)(new TextEncoder().encode(Ot({channelId:e.payload.channelId,cumulativeAmount:e.payload.cumulativeAmount,sequenceNumber:e.payload.sequenceNumber,sessionPublicKey:(0,j.bytesToHex)(e.sessionPublicKey),sessionSignature:(0,j.bytesToHex)(e.sessionSignature),sessionRegistration:(0,j.bytesToHex)(e.sessionRegistration)}))))}function at(e){let t=st(e);return{voucherDigest:t,idempotencyKey:`native-tab-v2:${t}`}}function In(e){return(0,j.bytesToHex)((0,ot.sha256)(new TextEncoder().encode(Ot({contract:Nt,network:it(e.network),programId:e.programId,buyerSwigAddress:e.buyerSwigAddress,vaultPda:e.vaultPda,sessionPda:e.sessionPda,seller:e.seller,channelId:e.channelId,sessionNonce:e.sessionNonce,reservationAmountAtomic:e.reservationAmountAtomic,previousCumulativeAtomic:e.previousCumulativeAtomic,voucherDigest:e.voucherDigest,cumulativeAmountAtomic:e.cumulativeAmountAtomic,sequenceNumber:e.sequenceNumber}))))}function Nn(e){let t=st(e.voucher);return In({network:e.network,programId:e.programId,buyerSwigAddress:e.buyerSwigAddress,vaultPda:e.vaultPda,sessionPda:e.sessionPda,seller:e.seller,channelId:e.channelId,sessionNonce:e.sessionNonce,reservationAmountAtomic:e.reservationAmountAtomic,previousCumulativeAtomic:e.previousCumulativeAtomic,voucherDigest:t,cumulativeAmountAtomic:e.voucher.payload.cumulativeAmount,sequenceNumber:e.voucher.payload.sequenceNumber})}function kt(e){return`${Tn}${Nn(e)}`}function x(e){throw new Error(`native_tab_v2_reservation_receipt_invalid:${e}`)}function Ve(e,t){(!Number.isSafeInteger(e)||e<0)&&x(t)}function ct(e,t){(!t||typeof t!="object")&&x("missing");let n=st(e.voucher);(!Vt.test(e.voucherDigest)||e.voucherDigest!==n)&&x("input_voucher_digest"),e.idempotencyKey!==`native-tab-v2:${n}`&&x("input_idempotency_identity");let r=BigInt(e.voucher.payload.cumulativeAmount),i=BigInt(e.previousCumulativeAtomic),o=BigInt(e.reservationAmountAtomic);(r<=i||r-i!==o||o<=0n)&&x("input_reservation_delta"),(e.voucher.payload.channelId!==e.channelId||(e.voucher.payload.sequenceNumber&2147483648)===0||(e.sessionNonce&2147483648)===0||e.voucher.sessionPublicKey.length!==32||e.voucher.sessionSignature.length!==64||e.voucher.sessionRegistration.length!==188)&&x("input_voucher_identity"),t.contract!=="dexter-native-tab-open-receipt/v1"&&t.contract!=="dexter-native-tab-open-receipt/v2"&&x("contract"),Vt.test(t.operationId)||x("operation_id"),Tt.test(t.callerOperationId)||x("caller_operation_id"),Vn.test(t.transaction)||x("transaction"),t.commitment!=="finalized"&&x("commitment"),Ve(t.confirmationSlot,"confirmation_slot"),Ve(t.postStateSlot,"post_state_slot"),(t.confirmationSlot===0||t.postStateSlot<t.confirmationSlot)&&x("post_state_slot");let c,a;try{c=it(e.network),a=it(t.network)}catch{return x("network")}(a!==c||t.buyerSwigAddress!==e.buyerSwigAddress||t.vaultPda!==e.vaultPda||t.sessionPda!==e.sessionPda||t.seller!==e.seller||t.channelId!==e.channelId||t.sessionPublicKey!==(0,j.bytesToHex)(e.voucher.sessionPublicKey)||t.voucherDigest!==n||t.cumulativeAmountAtomic!==e.voucher.payload.cumulativeAmount||t.sequenceNumber!==e.voucher.payload.sequenceNumber||!Tt.test(t.providerReceiptId)||t.reservationAmountAtomic!==e.reservationAmountAtomic||t.currentOutstandingBeforeAtomic!=="0"||t.currentOutstandingAfterAtomic!==e.reservationAmountAtomic)&&x("economic_identity"),Ve(t.pendingVoucherCountBefore,"pending_voucher_count_before"),Ve(t.pendingVoucherCountAfter,"pending_voucher_count_after"),t.contract==="dexter-native-tab-open-receipt/v1"?t.callerOperationId!==e.idempotencyKey&&x("idempotency_key"):(t.rootOperationId!==e.idempotencyKey||!Number.isSafeInteger(t.generation)||Number(t.generation)<2||!t.predecessorCallerOperationId||!t.predecessorLifecycleOperationId||!t.predecessorReleaseDigest||!t.stableReservationId||!t.economicEffectDigest)&&x("successor_identity")}var Bn=require("@dexterai/vault/session");var Bt="https://x402.dexter.cash",Ut=6;function U(e,t=Ut){if(!/^\d+(\.\d+)?$/.test(e))throw new Error(`amount must be a non-negative decimal string, got "${e}"`);let[n,r=""]=e.split(".");if(r.length>t)throw new Error(`amount "${e}" has more than ${t} decimals`);let i=r.padEnd(t,"0"),o=`${n}${i}`.replace(/^0+(?=\d)/,"");return o===""?"0":o}function J(e,t=Ut){if(!/^\d+$/.test(e))throw new Error(`atomic must be a non-negative integer string, got "${e}"`);let n=e.padStart(t+1,"0"),r=n.slice(0,-t).replace(/^0+(?=\d)/,"")||"0",i=n.slice(-t).replace(/0+$/,"");return i?`${r}.${i}`:r}var $=require("@solana/web3.js"),Ie=require("@noble/hashes/utils"),ne=require("@dexterai/vault/credit"),Ne=require("@dexterai/vault/constants"),Kt=require("@dexterai/vault/instructions"),Dt=require("@dexterai/vault/reader"),re=require("@dexterai/vault/session");var Un=Uint8Array.from([56,67,4,209,238,143,0,129]),Kn=74,$t=new Set([1,2]),Dn=188,$n=new $.PublicKey("MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr"),Mn=(()=>{let e=Buffer.alloc(32);return e.write("OTS_SESSION_REGISTER_V2","ascii"),e})(),ae=class extends Error{constructor(n,r){super(`native_tab_v2_solana_reservation_invalid:${n}`+(r?`:${r}`:""));this.code=n;this.name="SolanaFinalVoucherV2ReservationError"}code};function m(e,t){throw new ae(e,t)}function I(e,t){try{return new $.PublicKey(e)}catch{return m(t)}}function Te(e,t){return e.length===t.length&&e.every((n,r)=>n===t[r])}async function Ln(e){let t=await e.connection.getTransaction(e.signature,{commitment:e.commitment,maxSupportedTransactionVersion:0});if(!t)return null;t.meta||m("transaction_meta_missing");let n=t.transaction.message,r=n.version===0?n.getAccountKeys({accountKeysFromLookups:t.meta.loadedAddresses??null}):n.getAccountKeys(),i=(o,c)=>{let a=r.get(o);return a||m(c),a};return{slot:t.slot,err:t.meta.err,signatures:[...t.transaction.signatures],accountKeys:Array.from({length:r.length},(o,c)=>({address:i(c,"transaction_account_key").toBase58(),signer:n.isAccountSigner(c),writable:n.isAccountWritable(c)})),instructions:n.compiledInstructions.map(o=>({programId:i(o.programIdIndex,"transaction_program_id").toBase58(),accounts:o.accountKeyIndexes.map(c=>i(c,"transaction_instruction_account").toBase58()),data:Uint8Array.from(o.data)}))}}function Fn(e,t,n,r){r.length!==Kn&&m("binding_length"),Te(r.subarray(0,8),Un)||m("binding_discriminator");let i=r.readUInt8(8),o=r.readUInt8(9);return $t.has(i)||m("binding_version"),o!==t&&m("binding_bump"),{address:e.toBase58(),owner:n.toBase58(),version:i,bump:o,swig:new $.PublicKey(r.subarray(10,42)).toBase58(),vault:new $.PublicKey(r.subarray(42,74)).toBase58()}}async function qn(e){let t=I(e.input.programId,"program_id"),n=I(e.input.buyerSwigAddress,"buyer_swig"),r=I(e.input.vaultPda,"vault_pda"),i=I(e.input.seller,"seller"),[o,c]=(0,ne.deriveSwigVaultBindingPda)(n,t),[a]=(0,re.deriveSessionPda)(r,i,t),u=await e.connection.getMultipleAccountsInfoAndContext([o,r,a],{commitment:"finalized",minContextSlot:e.minimumSlot}),[h,d,g]=u.value;h||m("binding_missing"),d||m("vault_missing"),g||m("session_missing");let s,y;try{s=(0,Dt.decodeVaultFull)(Buffer.from(d.data)),y=(0,re.decodeSessionAccount)(a,g.data)}catch(A){return m("post_state_decode",A instanceof Error?A.message:String(A))}let l=y.session;return{contextSlot:u.context.slot,binding:Fn(o,c,h.owner,Buffer.from(h.data)),vault:{owner:d.owner.toBase58(),exists:s.exists,version:s.version,swigAddress:s.swigAddress,dexterAuthority:s.dexterAuthority,pendingVoucherCount:s.pendingVoucherCount},session:{owner:g.owner.toBase58(),version:y.version,bump:y.bump,vault:y.vault,sessionPublicKey:(0,Ie.bytesToHex)(l.sessionPubkey),maxAmount:l.maxAmount,expiresAt:l.expiresAt,seller:l.allowedCounterparty,nonce:l.nonce,spent:l.spent,currentOutstanding:l.currentOutstanding,maxRevolvingCapacity:l.maxRevolvingCapacity,crystallizedCumulative:l.crystallizedCumulative}}}function zn(e){let t=Buffer.from(e.voucher.sessionRegistration);return t.length!==Dn&&m("registration_length"),Te(t.subarray(0,32),Mn)||m("registration_domain"),{programId:new $.PublicKey(t.subarray(32,64)).toBase58(),vaultPda:new $.PublicKey(t.subarray(64,96)).toBase58(),sessionPublicKey:(0,Ie.bytesToHex)(t.subarray(96,128)),maxAmount:t.readBigUInt64LE(128),expiresAt:t.readBigInt64LE(136),seller:new $.PublicKey(t.subarray(144,176)).toBase58(),nonce:t.readUInt32LE(176),maxRevolvingCapacity:t.readBigUInt64LE(180)}}function te(e,t,n){let r=e.accountKeys.filter(i=>i.address===t);return r.length!==1&&m(`${n}_account_key`),r[0]}function Hn(e,t,n){(!Number.isSafeInteger(n.slot)||n.slot<=0)&&m("transaction_slot"),n.slot!==t.confirmationSlot&&m("transaction_confirmation_slot"),n.err!==null&&m("transaction_failed"),n.signatures[0]!==t.transaction&&m("transaction_signature"),n.instructions.length!==2&&m("transaction_instruction_count");let r=I(e.programId,"program_id"),i=I(e.buyerSwigAddress,"buyer_swig"),o=I(e.vaultPda,"vault_pda"),c=I(e.seller,"seller"),a=I(e.sessionPda,"session_pda"),[u]=(0,re.deriveSessionPda)(o,c,r),h=(0,ne.deriveSwigVaultBindingPda)(i,r)[0],d=(0,Kt.deriveSwigWalletAddress)(i),g=(0,ne.deriveGraphConfigPda)(r)[0];a.equals(u)||m("session_pda_derivation");let s=Buffer.from(Ne.DISCRIMINATORS.settle_voucher),y=n.instructions.filter(p=>{if(p.programId!==e.programId)return!1;let b=p.data;return b.length>=s.length&&Te(b.subarray(0,s.length),s)});y.length!==1&&m("settle_voucher_instruction_count");let l=y[0];n.instructions[0]!==l&&m("settle_voucher_instruction_order");let A=Buffer.from(l.data);A.length!==49&&m("settle_voucher_instruction_length"),A.readBigUInt64LE(8).toString()!==e.reservationAmountAtomic&&m("settle_voucher_amount"),A.readUInt8(16)!==1&&m("settle_voucher_increment"),new $.PublicKey(A.subarray(17,49)).equals(c)||m("settle_voucher_seller"),l.accounts.length<8&&m("settle_voucher_accounts"),[o.toBase58(),null,a.toBase58(),null,i.toBase58(),d.toBase58(),h.toBase58(),g.toBase58()].forEach((p,b)=>{p!==null&&l.accounts[b]!==p&&m(`settle_voucher_account_${b}`)});let N=l.accounts[1];N||m("dexter_authority");let O=te(n,l.accounts[0],"vault"),q=te(n,N,"dexter_authority"),D=te(n,l.accounts[2],"session");(!O.writable||O.signer)&&m("vault_privileges"),q.signer||m("dexter_authority_signature"),(!D.writable||D.signer)&&m("session_privileges");for(let p=3;p<Math.min(l.accounts.length,8);p+=1)te(n,l.accounts[p],`account_${p}`).signer&&m(`settle_voucher_account_${p}_signer`);for(let p=8;p<l.accounts.length;p+=1){let b=te(n,l.accounts[p],`sibling_${p-8}`);(!b.writable||b.signer)&&m("settle_voucher_sibling_privileges")}let k=n.instructions[1];k.programId!==$n.toBase58()&&m("reservation_memo_program"),(k.accounts.length!==1||k.accounts[0]!==N)&&m("reservation_memo_authority"),te(n,N,"reservation_memo_authority").signer||m("reservation_memo_authority_signature");let f=new TextEncoder().encode(kt(e));return Te(k.data,f)||m("reservation_memo_digest"),N}function Wn(e,t,n,r){let i=I(e.programId,"program_id"),o=I(e.buyerSwigAddress,"buyer_swig"),c=I(e.vaultPda,"vault_pda"),a=I(e.seller,"seller"),[u,h]=(0,ne.deriveSwigVaultBindingPda)(o,i),[d,g]=(0,re.deriveSessionPda)(c,a,i);(!Number.isSafeInteger(r.contextSlot)||r.contextSlot<t)&&m("post_state_slot"),(r.binding.address!==u.toBase58()||r.binding.owner!==e.programId||r.binding.bump!==h||!$t.has(r.binding.version)||r.binding.swig!==e.buyerSwigAddress||r.binding.vault!==e.vaultPda)&&m("binding_identity"),(r.vault.owner!==e.programId||!r.vault.exists||r.vault.version!==7||r.vault.swigAddress!==e.buyerSwigAddress||r.vault.dexterAuthority!==n)&&m("vault_post_state"),(e.sessionPda!==d.toBase58()||r.session.owner!==e.programId||r.session.version!==1||r.session.bump!==g||r.session.vault!==e.vaultPda||r.session.sessionPublicKey!==(0,Ie.bytesToHex)(e.voucher.sessionPublicKey)||r.session.seller!==e.seller||r.session.nonce!==e.sessionNonce||r.session.currentOutstanding.toString()!==e.reservationAmountAtomic)&&m("session_post_state");let s=zn(e);(s.programId!==e.programId||s.vaultPda!==e.vaultPda||s.sessionPublicKey!==r.session.sessionPublicKey||s.seller!==e.seller||s.nonce!==e.sessionNonce||s.maxAmount!==r.session.maxAmount||s.expiresAt!==BigInt(r.session.expiresAt)||s.maxRevolvingCapacity!==r.session.maxRevolvingCapacity)&&m("registration_post_state"),(r.session.spent>r.session.crystallizedCumulative?r.session.spent:r.session.crystallizedCumulative).toString()!==e.previousCumulativeAtomic&&m("session_frontier")}async function Mt(e,t,n,r={}){try{ct(t,n)}catch(a){return m("provider_receipt",a instanceof Error?a.message:String(a))}t.network!=="solana:mainnet"&&m("network"),t.programId!==Ne.DEXTER_VAULT_PROGRAM_ID.toBase58()&&m("program_id");let i=await(r.readTransaction??Ln)({connection:e,signature:n.transaction,commitment:n.commitment});i||m("transaction_missing");let o=Hn(t,n,i),c=await(r.readPostState??qn)({connection:e,input:t,minimumSlot:i.slot});Wn(t,i.slot,o,c)}var Ht=require("@dexterai/vault/session");var Oe=require("@noble/hashes/utils"),Gn=15e3,Xn=["claim_already_exists","non_monotonic_cumulative"],jn="below_lock_cadence";function ke(e){return typeof e=="string"&&e.includes(jn)}function Jn(e){return`${e.slice(0,16)}\u2026`}function Lt(e,t,n,r){let i=Jn(e);if(r.crystallized){console.info(`[tab/seller] crystallize OK channel=${i} cumulative=${t} seq=${n}${r.claimPda?` claimPda=${r.claimPda}`:""} \u2014 LockVoucher landed`);return}if(r.error){if(ke(r.error)){console.warn(`[tab/seller] crystallize refused below the facilitator's cadence gate channel=${i} cumulative=${t} seq=${n}: ${r.error} \u2014 benign: this facilitator's cadence engine owns the protection cadence for the span; gate-refused watermark advanced, re-attempts after the next signed voucher`);return}if(Xn.some(o=>r.error.includes(o))){console.warn(`[tab/seller] crystallize duplicate channel=${i} cumulative=${t} seq=${n}: ${r.error} \u2014 voucher already secured on-chain (benign, no action)`);return}console.error(`[tab/seller] crystallize FAILED channel=${i} cumulative=${t} seq=${n}: ${r.error} \u2014 lock NOT secured (degraded: best-effort path, no watermark advance, retries at next threshold/close; seller exposure stays unsecured until it lands)`)}}async function lt(e,t,n,r,i=fetch){let o=e.lastVoucher;if(!o)return{crystallized:!1};if(o.payload.channelId!==t){let a={crystallized:!1,error:"channel_id_mismatch"};return Lt(t,o.payload.cumulativeAmount,o.payload.sequenceNumber,a),a}let c=a=>(Lt(t,o.payload.cumulativeAmount,o.payload.sequenceNumber,a),a);try{let a=`${n.replace(/\/$/,"")}/tab/lock`,u={channelId:o.payload.channelId,cumulativeAmount:o.payload.cumulativeAmount,sequenceNumber:o.payload.sequenceNumber,sessionPublicKey:(0,Oe.bytesToHex)(o.sessionPublicKey),sessionSignature:(0,Oe.bytesToHex)(o.sessionSignature),sessionRegistration:(0,Oe.bytesToHex)(o.sessionRegistration),network:r},h=await i(a,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(u),signal:AbortSignal.timeout(Gn)}),d=await h.text();if(!h.ok)return c({crystallized:!1,error:`tab lock ${h.status}: ${d.slice(0,200)}`});let g;try{let s=JSON.parse(d);s&&typeof s.claimPda=="string"&&(g=s.claimPda)}catch{}return c({crystallized:!0,claimPda:g})}catch(a){return c({crystallized:!1,error:String(a?.message??a)})}}async function Ft(e,t,n,r,i,o={}){let c=BigInt(e.deliveredCumulativeAtomic),a=BigInt(e.lastCrystallizedCumulativeAtomic??"0"),u=BigInt(i.thresholdAtomic);if(c-a<u)return{crystallized:!1};let h=e.lastVoucher?.payload.cumulativeAmount,d=e.gateRefusedCumulativeAtomic;if(h!==void 0&&d!==void 0&&BigInt(h)<=BigInt(d))return{crystallized:!1};let g=await lt(e,t,n,r,o.fetchImpl);return g.crystallized&&h!==void 0?(e.lastCrystallizedCumulativeAtomic=h,e.gateRefusedCumulativeAtomic=void 0):ke(g.error)&&h!==void 0&&(e.gateRefusedCumulativeAtomic=h),g}var F="x-tab-voucher",ut=class{map=new Map;get(t){return this.map.get(t)}set(t,n){this.map.set(t,n)}update(t,n){let r=this.map.get(t);r&&(r.lastCumulativeAtomic=n)}delete(t){this.map.delete(t)}},mt=class{constructor(t,n,r,i,o,c){this.recordDeliveredImpl=o;this.chargeImpl=c;this.channelId=t,this.network=n,this.cumulativeAtomic=r,this.deliveredBaselineAtomic=i}recordDeliveredImpl;chargeImpl;channelId;network;sessionPublicKey=null;cumulativeAtomic;deliveredBaselineAtomic;cumulative(){return J(this.cumulativeAtomic.toString())}deliveredCumulative(){return J(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 Yn(e){if(typeof e!="string"||e.length===0)throw new w("signature_invalid",`missing ${F} header`);let t;try{t=Buffer.from(e,"base64").toString("utf8")}catch{throw new w("signature_invalid","malformed base64")}let n;try{n=JSON.parse(t)}catch{throw new w("signature_invalid","malformed JSON")}if(!n||typeof n!="object"||!n.payload||!n.sessionPublicKey)throw new w("signature_invalid","missing required fields");return{voucher:{payload:n.payload,sessionPublicKey:Be(n.sessionPublicKey),sessionRegistration:Be(n.sessionRegistration),sessionSignature:Be(n.sessionSignature)},reservationReceipt:n.reservationReceipt}}function Be(e){if(typeof e!="string"||e.length%2!==0)throw new w("signature_invalid",`bad hex: ${typeof e}`);let t=new Uint8Array(e.length/2);for(let n=0;n<t.length;n++)t[n]=parseInt(e.substr(n*2,2),16);return t}function Zn(e){if(!/^[0-9a-f]{64}$/i.test(e))throw new w("signature_invalid",`channelId must be 64-char hex, got "${e}"`);return Be(e)}function Ue(e){let t=e.ledger??new Q,n=new ut,r=typeof e.sellerPubkey=="string"?new zt.PublicKey(e.sellerPubkey):e.sellerPubkey,i=e.maxPerVoucherAtomic?BigInt(e.maxPerVoucherAtomic):BigInt(U(e.perUnit))*100n,o=e.facilitatorUrl??Bt,c={thresholdAtomic:e.lockCadence?.thresholdAtomic??U("0.10"),onClose:e.lockCadence?.onClose??!0};return async(a,u,h)=>{try{let d=Yn(a.headers[F]),g=d.voucher,s=g.payload.channelId,y=Zn(s),l=n.get(s),A=null,R,N=!1;if(l){if(!Qn(l.registrationBytes,g.sessionRegistration))throw new V("voucher registration does not match the channel's verified registration")}else{let v=oe(g.sessionRegistration);R=await se(e.connection,v),N=!0,A=R?.frontierAtomic??null;let _=await t.get(s);l={registration:v,registrationBytes:g.sessionRegistration.slice(),lastCumulativeAtomic:_?.lastVoucher?.payload.cumulativeAmount??"0"},n.set(s,l)}let O=G(l.registration.nonce),q=G(g.payload.sequenceNumber);if(q!==O)throw new w("wire_version_mismatch",`voucher is V${q}, active registration is V${O}`);let D;if(O===2){if(d.reservationReceipt===void 0)throw new w("reservation_proof_missing","Native Tab V2 requires the verified reservation receipt in the voucher envelope");if(d.reservationReceipt===null||typeof d.reservationReceipt!="object"||Array.isArray(d.reservationReceipt))throw new w("reservation_proof_invalid","reservationReceipt must be an object");D=d.reservationReceipt}O===2&&!N&&(R=await se(e.connection,l.registration),N=!0,A=R?.frontierAtomic??null),Ee(g,y);let k=BigInt(g.payload.cumulativeAmount),f=BigInt(l.lastCumulativeAtomic),p=null;if(O===2){if(R?.wireVersion!==2)throw new w("wire_version_mismatch",`active SessionAccount is V${R?.wireVersion??"unknown"}, voucher is V2`);if(R.sessionAccountVersion!==1||A===null||R.currentOutstandingAtomic===void 0)throw new w("reservation_mismatch","authoritative V2 SessionAccount reservation evidence is incomplete");let v=qt(A,"frontierAtomic");if(v>f&&(f=v),k<=f)throw new w("voucher_already_covered",`cumulative ${k} is not above covered frontier ${f}`);p=k-f;let _=qt(R.currentOutstandingAtomic,"currentOutstandingAtomic");if(_!==p)throw new w("reservation_mismatch",`currentOutstanding ${_} does not equal voucher delta ${p}`)}Ce({registration:l.registration,voucher:g,expectedCounterparty:r,previousCumulativeAtomic:f.toString()});let b=k-f;if(b>i)throw new T("cumulative_exceeds_cap",`single voucher increment ${b} exceeds maxPerVoucherAtomic ${i}`);if(O===2){if(!D||p===null)throw new w("reservation_proof_missing","Native Tab V2 reservation proof was not carried through admission");let[v]=(0,Ht.deriveSessionPda)(l.registration.vaultPda,l.registration.allowedCounterparty,l.registration.programId),_=at(g);try{await Mt(e.connection,{network:e.network,programId:l.registration.programId.toBase58(),buyerSwigAddress:D.buyerSwigAddress,vaultPda:l.registration.vaultPda.toBase58(),sessionPda:v.toBase58(),seller:l.registration.allowedCounterparty.toBase58(),channelId:s,sessionNonce:l.registration.nonce,reservationAmountAtomic:p.toString(),previousCumulativeAtomic:f.toString(),..._,voucher:g},D)}catch(S){throw S instanceof ae?new w("reservation_proof_invalid",S.message):S}}let z=e.leaseTtlMs??3e5;if(!await t.tryAcquireLease(s,z))throw new w("channel_busy","another stream is live on this channel; tabs serve one stream at a time");let L=!1,H=()=>{L||(L=!0,t.releaseLease(s).catch(v=>{console.error("[tab/seller] failed to release channel lease:",v)}))};u.on("close",H),u.on("finish",H);let un=async v=>{let _=v.lastCrystallizedCumulativeAtomic??"0",S=v.gateRefusedCumulativeAtomic;await Ft(v,s,o,e.network,c),(v.lastCrystallizedCumulativeAtomic!==_||v.gateRefusedCumulativeAtomic!==S)&&await K(s,async()=>{let C=await t.get(s);C&&await t.set(s,{...C,lastCrystallizedCumulativeAtomic:v.lastCrystallizedCumulativeAtomic,gateRefusedCumulativeAtomic:v.gateRefusedCumulativeAtomic})}).catch(C=>{console.error(`[tab/seller] crystallize watermark persist FAILED channel=${s.slice(0,16)}\u2026 (expect a benign duplicate/below-cadence warn on the next request):`,C)})},gt=!1,ht=()=>{!c.onClose||gt||(gt=!0,(async()=>{let v=await t.get(s);if(!v||!v.lastVoucher)return;let _=v.lastVoucher.payload.cumulativeAmount;if(v.gateRefusedCumulativeAtomic!==void 0&&BigInt(_)<=BigInt(v.gateRefusedCumulativeAtomic))return;let S=await lt(v,s,o,e.network);S.crystallized?await K(s,async()=>{let C=await t.get(s);C&&await t.set(s,{...C,lastCrystallizedCumulativeAtomic:_,gateRefusedCumulativeAtomic:void 0})}):ke(S.error)&&await K(s,async()=>{let C=await t.get(s);C&&await t.set(s,{...C,gateRefusedCumulativeAtomic:_})})})().catch(v=>{console.error(`[tab/seller] close-path crystallize CRASHED channel=${s.slice(0,16)}\u2026 (final voucher NOT locked; exposure unsecured until the next request retries):`,v)}))};u.on("close",ht),u.on("finish",ht);let ve=await t.get(s),ft=BigInt(ve?.deliveredCumulativeAtomic??"0"),mn=!ve?.lastVoucher&&ft===0n,Ze=A===null?null:BigInt(A),Y=Ze!==null&&Ze>ft&&(O===2||mn)?Ze.toString():null;Y!==null&&console.info(`[tab/seller] channel ${s.slice(0,16)}\u2026 resumed: seeding delivered baseline from chain frontier ${Y} (session already settled/locked up to it \u2014 that span is not deliverable budget)`);let dn=Y!==null?BigInt(Y):ve?BigInt(ve.deliveredCumulativeAtomic):0n;await K(s,async()=>{let v=await t.get(s);await t.set(s,{...v,lastVoucher:g,deliveredCumulativeAtomic:Y??(v?v.deliveredCumulativeAtomic:"0"),lastCrystallizedCumulativeAtomic:Y??v?.lastCrystallizedCumulativeAtomic??"0"})}),n.update(s,g.payload.cumulativeAmount);let yt=new mt(s,e.network,k,dn,async v=>{let _=null;await K(s,async()=>{let S=await t.get(s),C=S?BigInt(S.deliveredCumulativeAtomic):0n,vt=BigInt(v),pn=vt>0n?C+vt:C,At={...S,lastVoucher:S?.lastVoucher??g,deliveredCumulativeAtomic:pn.toString(),lastCrystallizedCumulativeAtomic:S?.lastCrystallizedCumulativeAtomic??"0"};await t.set(s,At),_=At}),_&&un(_).catch(S=>{console.error(`[tab/seller] crystallize cadence CRASHED channel=${s.slice(0,16)}\u2026 (threshold lock not attempted; retries at the next delivery):`,S)})},async v=>{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.")});yt.setSessionPublicKey(g.sessionPublicKey),a.tab=yt,h()}catch(d){if(d instanceof w||d instanceof B||d instanceof E||d instanceof V||d instanceof T){u.status(402).json({error:"invalid_voucher",reason:d.reason??"unknown",detail:d.message});return}h(d)}}}function Qn(e,t){if(e.length!==t.length)return!1;let n=0;for(let r=0;r<e.length;r+=1)n|=e[r]^t[r];return n===0}function qt(e,t){if(!/^(0|[1-9][0-9]*)$/.test(e))throw new w("reservation_mismatch",`authoritative ${t} is not a canonical atomic amount`);return BigInt(e)}function Wt(e){if(!e.tab)throw new Error("req.tab is missing \u2014 did tabMiddleware run on this route?");return e.tab}function Gt(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 n=t.tab,r=BigInt(U(n.cumulative())),i=BigInt(U(n.deliveredCumulative())),o=r-i;o<0n&&(o=0n);let c=t.perUnit?BigInt(U(t.perUnit)):null,a=0n,u=!1;async function h(){await n.recordDelivered(a.toString())}e.on("close",()=>{u||(u=!0,h().catch(y=>{console.error("[tab/seller] terminal persist failed on disconnect:",y)}))});async function d(y=1){if(u)throw new Error("meter ended");if(c===null)throw new Error("charge() needs options.perUnit");let l=c*BigInt(y),A=a+l;if(A>o)throw u=!0,await h(),new T("cumulative_exceeds_cap",`chunk would push delivered to ${J((i+A).toString())} beyond signed cumulative ${J(r.toString())} (per-request budget ${J(o.toString())})`);a=A}function g(y){if(u)throw new Error("meter ended");let A=(typeof y=="string"?y:Buffer.from(y).toString("utf8")).replace(/\\/g,"\\\\").replace(/\n/g,"\\n");e.write(`data: ${A}
|
|
2
2
|
|
|
3
|
-
`)}async function
|
|
4
|
-
data: {"chargedAtomic":"${
|
|
3
|
+
`)}async function s(){u||(u=!0,await h(),e.write(`event: end
|
|
4
|
+
data: {"chargedAtomic":"${a}"}
|
|
5
5
|
|
|
6
|
-
`),e.end())}return{charge:l,send:o,end:y}}var D=require("fs"),ve=require("path");function Dt(e){return{payload:e.payload,sessionPublicKey:Ke(e.sessionPublicKey),sessionRegistration:Ke(e.sessionRegistration),sessionSignature:Ke(e.sessionSignature)}}function Kt(e){return{payload:e.payload,sessionPublicKey:ze(e.sessionPublicKey),sessionRegistration:ze(e.sessionRegistration),sessionSignature:ze(e.sessionSignature)}}function Ke(e){let t="";for(let r of e)t+=r.toString(16).padStart(2,"0");return t}function ze(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)}},be=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,ve.join)(this.dir,`${t}.json`)}async get(t){try{let r=await D.promises.readFile(this.pathFor(t),"utf8");return Kt(JSON.parse(r))}catch(r){if(r?.code==="ENOENT")return null;throw r}}async set(t,r){let n=this.pathFor(t);await D.promises.mkdir((0,ve.dirname)(n),{recursive:!0});let i=`${n}.tmp`;await D.promises.writeFile(i,JSON.stringify(Dt(r))),await D.promises.rename(i,n)}async delete(t){try{await D.promises.unlink(this.pathFor(t))}catch(r){if(r?.code!=="ENOENT")throw r}}};var pt=require("@solana/web3.js");var k="solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp",ot="solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1",at="solana:4uhcVJyU9pJkvQyS88uRDiswHXSCkY3z";var q="eip155:8453",we="eip155:84532",Se="eip155:42161",xe="eip155:137",Ce="eip155:10",Ee="eip155:43114",Re="eip155:56",F="eip155:480",W="eip155:143",j="eip155:4663",X="eip155:1187947933",J="eip155:324705682",Te="eip155:1";var Pe="EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v";var ct="0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",zt="0x55d398326f99059fF775485246999027B3197955",lt="0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d",Ht="0x5fc5360D0400a0Fd4f2af552ADD042D716F1d168",ut={[Re]:lt,[q]:ct,[we]:"0x036CbD53842c5426634e7929541eC2318f3dCF7e",[Se]:"0xaf88d065e77c8cC2239327C5EDb3A432268e5831",[xe]:"0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359",[Ce]:"0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85",[Ee]:"0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E",[F]:"0x79A02482A880bCE3F13e09Da970dC34db4CD24d1",[W]:"0x754704Bc059F8C67012fEd69BC8A327a5aafb603",[j]:Ht,[X]:"0x85889c8c714505E0c94b30fcfcF64fE3Ac8FCb20",[J]:"0x2e08028E3C4c2356572E096d8EF835cD5C6030bD",[Te]:"0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"},mt={[zt]:{symbol:"USDT",decimals:18},[lt]:{symbol:"USDC",decimals:18}};var kr={[F]:{name:"USDC",version:"2"},[W]:{name:"USDC",version:"2"},[j]:{name:"Global Dollar",version:"1"},[X]:{name:"Bridged USDC (SKALE Bridge)",version:"2"},[J]:{name:"Bridged USDC (SKALE Bridge)",version:"2"}};var Ir={[Re]:56,[q]:8453,[we]:84532,[Se]:42161,[xe]:137,[Ce]:10,[Ee]:43114,[F]:480,[W]:143,[j]:4663,[X]:1187947933,[J]:324705682,[Te]:1},Or={[k]:"https://api.dexter.cash/api/solana/rpc",[ot]:"https://api.devnet.solana.com",[at]:"https://api.testnet.solana.com"},qt={[Re]:"https://api.dexter.cash/api/evm/bsc/rpc",[q]:"https://api.dexter.cash/api/base/rpc",[we]:"https://sepolia.base.org",[Se]:"https://api.dexter.cash/api/evm/arbitrum/rpc",[xe]:"https://api.dexter.cash/api/evm/polygon/rpc",[Ce]:"https://api.dexter.cash/api/evm/optimism/rpc",[Ee]:"https://api.dexter.cash/api/evm/avalanche/rpc",[F]:"https://worldchain-mainnet.g.alchemy.com/public",[W]:"https://rpc.monad.xyz",[j]:"https://rpc.mainnet.chain.robinhood.com",[X]:"https://skale-base.skalenodes.com/v1/base",[J]:"https://base-sepolia-testnet.skalenodes.com/v1/jubilant-horrible-ancha",[Te]:"https://eth.llamarpc.com"},G="https://x402.dexter.cash";function _e(e){return e.startsWith("solana:")||e==="solana"}function Ft(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 Wt(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 Ne(e){return Ft(JSON.stringify(e))}function Y(e){return JSON.parse(Wt(e))}function jt(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 Z=class extends Error{status;body;constructor(t,r){super(`HTTP ${t}`),this.status=t,this.body=r}},ke=class{facilitatorUrl;cachedSupported=null;cacheTime=0;CACHE_TTL_MS=6e4;timeoutMs;maxRetries;retryBaseMs;constructor(t=G,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,i=setTimeout(()=>n.abort(),this.timeoutMs);try{return await fetch(t,{...r,signal:n.signal})}finally{clearTimeout(i)}}async fetchWithRetry(t,r){let n;for(let i=0;i<this.maxRetries;i++)try{let s=await this.fetchWithTimeout(t,r);if(!s.ok&&s.status>=500)throw new Z(s.status,await s.text());return s}catch(s){if(n=s,i<this.maxRetries-1&&jt(s)){let h=this.retryBaseMs*Math.pow(2,i);await new Promise(c=>setTimeout(c,h));continue}throw s}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(i=>i.x402Version===2&&(i.scheme==="exact"||i.scheme==="exact-approval")&&i.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(i=>i.x402Version===2&&(i.scheme==="exact"||i.scheme==="exact-approval"||i.scheme==="batch-settlement")&&i.network===t)?.extra}async verifyPayment(t,r){try{let n=Y(t),i=await this.fetchWithRetry(`${this.facilitatorUrl}/verify`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({x402Version:2,paymentPayload:n,paymentRequirements:r})});return i.ok?await i.json():{isValid:!1,invalidReason:`facilitator_error_${i.status}`}}catch(n){return{isValid:!1,invalidReason:n instanceof Z?`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=Y(t),i=await this.fetchWithRetry(`${this.facilitatorUrl}/settle`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({x402Version:2,paymentPayload:n,paymentRequirements:r})});return i.ok?{...await i.json(),network:r.network}:{success:!1,network:r.network,errorReason:`facilitator_error_${i.status}`}}catch(n){let i=n instanceof Z?`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:i}}}};function Xt(e){if(_e(e))return{address:Pe,decimals:6};let t=ut[e];if(t){let r=mt[t]?.decimals??6;return{address:t,decimals:r}}return{address:Pe,decimals:6}}function dt(e){try{let t=Y(e);return t?.accepted?.amount??t?.accepted?.maxAmountRequired}catch{return}}function Q(e){let{payTo:t,facilitatorUrl:r=G,network:n=k,defaultTimeoutSeconds:i=60}=e,s=e.asset??Xt(n),h=e.scheme??"exact";if(h==="tab"&&!_e(n))throw new Error(`scheme 'tab' is SVM-only; got network "${n}"`);let c=new ke(r),a=null,m=new Map,l=3e4,o=Date.now();function y(u){let g=(u.maxTimeoutSeconds||i)*1e3;if(m.set(u.payTo,{accept:u,expiresAt:Date.now()+g}),Date.now()-o>l){let A=Date.now();for(let[C,O]of m)O.expiresAt<A&&m.delete(C);o=A}}function d(u){let g=m.get(u);if(g){if(g.expiresAt<Date.now()){m.delete(u);return}return g.accept}}async function f(u){return typeof t=="string"?t:t(u||{})}async function b(){if(a||(a=await c.getNetworkExtra(n)),_e(n)&&!a?.feePayer)throw new Error(`Facilitator does not provide feePayer for network "${n}"`);return{...a?.feePayer?{feePayer:a.feePayer}:{},decimals:a?.decimals??s.decimals,name:a?.name,version:a?.version,...h==="batch-settlement"&&a?.receiverAuthorizer?{receiverAuthorizer:a.receiverAuthorizer}:{},...h==="tab"?{voucherHeader:"x-tab-voucher",registrationEncoding:"base64(188-byte sessionRegisterMessage)"}:{}}}async function K(u,g){let{amountAtomic:A,timeoutSeconds:C=i}=g,O=await b(),U={scheme:h,network:n,amount:A,maxAmountRequired:A,asset:s.address,payTo:u,maxTimeoutSeconds:C,extra:O};return y(U),U}async function z(u){let g=await f({amountAtomic:u.amountAtomic,resourceUrl:u.resourceUrl});return K(g,u)}async function Ie(u){let{resourceUrl:g,description:A,mimeType:C="application/json"}=u,O={url:g,description:A,mimeType:C},U=await z(u);return{x402Version:2,resource:O,accepts:[U],error:"Payment required"}}function H(u){return Ne(u)}function ee(u){return{status:402,headers:{"PAYMENT-REQUIRED":H(u)},body:{}}}async function Oe(u,g){if(!g){let A=await f({paymentHeader:u});g=d(A),g||(g=await K(A,{amountAtomic:dt(u)??"0",resourceUrl:""}))}return c.verifyPayment(u,g)}async function te(u,g){if(!g){let A=await f({paymentHeader:u});g=d(A),g||(g=await K(A,{amountAtomic:dt(u)??"0",resourceUrl:""}))}return c.settlePayment(u,g)}return{buildRequirements:Ie,encodeRequirements:H,create402Response:ee,verifyPayment:Oe,settlePayment:te,getPaymentAccept:z,network:n,assetDecimals:s.decimals,facilitator:c}}var Jt={"solana:mainnet":k};function gt(e){let t=Jt[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.PublicKey(r);let n=Q({payTo:r,network:t,scheme:"tab",facilitatorUrl:e.facilitatorUrl}),i=R(e.perUnit);return async(s,h,c)=>{if(s.headers[I])return c();try{let a=`${s.protocol}://${s.get("host")}${s.originalUrl}`,m=await n.buildRequirements({amountAtomic:i,resourceUrl:a,description:e.description}),l=n.create402Response(m);h.set(l.headers).status(l.status).json(l.body)}catch(a){let m=a?.message??String(a);h.status(503).set({"Retry-After":"5"}).json({error:"challenge_unavailable",detail:m})}}}var ht=require("@solana/web3.js");var Gt={"solana:mainnet":k};function yt(e){let t=Gt[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.PublicKey(r);let n=Q({payTo:r,network:t,scheme:"tab",facilitatorUrl:e.facilitatorUrl}),i=Q({payTo:r,network:t,scheme:"exact",facilitatorUrl:e.facilitatorUrl}),s=R(e.perUnit),h=fe({connection:e.connection,sellerPubkey:r,network:e.network,perUnit:e.perUnit,settle:"on-close",facilitatorUrl:e.facilitatorUrl,lockCadence:e.lockCadence});return async(c,a,m)=>{if(c.headers[I])return h(c,a,m);let l=`${c.protocol}://${c.get("host")}${c.originalUrl}`,o=c.headers["payment-signature"];if(o){let y;try{y=await i.getPaymentAccept({amountAtomic:s,resourceUrl:l,description:e.description})}catch(d){let f=d?.message??String(d);a.status(503).set({"Retry-After":"5"}).json({error:"challenge_unavailable",detail:f});return}try{let d=await i.verifyPayment(o,y);if(!d.isValid){a.status(402).json({error:"Payment verification failed",reason:d.invalidReason});return}let f=await i.settlePayment(o,y);if(!f.success){a.status(402).json({error:"Payment settlement failed",reason:f.errorReason});return}return c.x402={transaction:f.transaction,payer:d.payer??"",network:f.network||t},a.setHeader("PAYMENT-RESPONSE",Ne({success:!0,transaction:f.transaction,network:f.network||t,payer:d.payer??""})),m()}catch{a.status(500).json({error:"Payment processing error"});return}}try{let y={amountAtomic:s,resourceUrl:l,description:e.description},[d,f]=await Promise.all([n.buildRequirements(y),i.buildRequirements(y)]),b={...d,accepts:[...d.accepts,...f.accepts]};a.set({"PAYMENT-REQUIRED":n.encodeRequirements(b)}).status(402).json({error:"Payment required",accepts:b.accepts,resource:b.resource})}catch(y){let d=y?.message??String(y);a.status(503).set({"Retry-After":"5"}).json({error:"challenge_unavailable",detail:d})}}}0&&(module.exports={FileChannelLedger,FileVoucherStore,InMemoryChannelLedger,InMemoryVoucherStore,InvalidRegistrationError,InvalidVoucherError,InvalidVoucherSignatureError,OnChainVerificationError,RedisChannelLedger,ScopeViolationError,TAB_VOUCHER_HEADER,deserializeChannelLedgerEntry,enforceScope,openSse,parseRegistration,requireTab,serializeChannelLedgerEntry,tabChallengeMiddleware,tabMiddleware,tabOrExactMiddleware,verifyRegistrationOnChain,verifyVoucherSignature});
|
|
6
|
+
`),e.end())}return{charge:d,send:g,end:s}}var ie=require("fs"),$e=require("path");function er(e){return{payload:e.payload,sessionPublicKey:dt(e.sessionPublicKey),sessionRegistration:dt(e.sessionRegistration),sessionSignature:dt(e.sessionSignature)}}function tr(e){return{payload:e.payload,sessionPublicKey:pt(e.sessionPublicKey),sessionRegistration:pt(e.sessionRegistration),sessionSignature:pt(e.sessionSignature)}}function dt(e){let t="";for(let n of e)t+=n.toString(16).padStart(2,"0");return t}function pt(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 n=0;n<t.length;n++)t[n]=parseInt(e.substr(n*2,2),16);return t}var Ke=class{map=new Map;async get(t){return this.map.get(t)??null}async set(t,n){this.map.set(t,n)}async delete(t){this.map.delete(t)}},De=class{constructor(t){this.dir=t}dir;pathFor(t){if(!/^[a-z0-9_-]+$/i.test(t))throw new Error(`unsafe channelId for filesystem: ${t}`);return(0,$e.join)(this.dir,`${t}.json`)}async get(t){try{let n=await ie.promises.readFile(this.pathFor(t),"utf8");return tr(JSON.parse(n))}catch(n){if(n?.code==="ENOENT")return null;throw n}}async set(t,n){let r=this.pathFor(t);await ie.promises.mkdir((0,$e.dirname)(r),{recursive:!0});let i=`${r}.tmp`;await ie.promises.writeFile(i,JSON.stringify(er(n))),await ie.promises.rename(i,r)}async delete(t){try{await ie.promises.unlink(this.pathFor(t))}catch(n){if(n?.code!=="ENOENT")throw n}}};var sn=require("@solana/web3.js");var M="solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp",Xt="solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1",jt="solana:4uhcVJyU9pJkvQyS88uRDiswHXSCkY3z";var ce="eip155:8453",Me="eip155:84532",Le="eip155:42161",Fe="eip155:137",qe="eip155:10",ze="eip155:43114",He="eip155:56",le="eip155:480",ue="eip155:143",me="eip155:4663",de="eip155:1187947933",pe="eip155:324705682",We="eip155:1";var Ge="EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v";var Jt="0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",nr="0x55d398326f99059fF775485246999027B3197955",Yt="0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d",rr="0x5fc5360D0400a0Fd4f2af552ADD042D716F1d168",Zt={[He]:Yt,[ce]:Jt,[Me]:"0x036CbD53842c5426634e7929541eC2318f3dCF7e",[Le]:"0xaf88d065e77c8cC2239327C5EDb3A432268e5831",[Fe]:"0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359",[qe]:"0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85",[ze]:"0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E",[le]:"0x79A02482A880bCE3F13e09Da970dC34db4CD24d1",[ue]:"0x754704Bc059F8C67012fEd69BC8A327a5aafb603",[me]:rr,[de]:"0x85889c8c714505E0c94b30fcfcF64fE3Ac8FCb20",[pe]:"0x2e08028E3C4c2356572E096d8EF835cD5C6030bD",[We]:"0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"},Qt={[nr]:{symbol:"USDT",decimals:18},[Yt]:{symbol:"USDC",decimals:18}};var ni={[le]:{name:"USDC",version:"2"},[ue]:{name:"USDC",version:"2"},[me]:{name:"Global Dollar",version:"1"},[de]:{name:"Bridged USDC (SKALE Bridge)",version:"2"},[pe]:{name:"Bridged USDC (SKALE Bridge)",version:"2"}};var ri={[He]:56,[ce]:8453,[Me]:84532,[Le]:42161,[Fe]:137,[qe]:10,[ze]:43114,[le]:480,[ue]:143,[me]:4663,[de]:1187947933,[pe]:324705682,[We]:1},ii={[M]:"https://api.dexter.cash/api/solana/rpc",[Xt]:"https://api.devnet.solana.com",[jt]:"https://api.testnet.solana.com"},ir={[He]:"https://api.dexter.cash/api/evm/bsc/rpc",[ce]:"https://api.dexter.cash/api/base/rpc",[Me]:"https://sepolia.base.org",[Le]:"https://api.dexter.cash/api/evm/arbitrum/rpc",[Fe]:"https://api.dexter.cash/api/evm/polygon/rpc",[qe]:"https://api.dexter.cash/api/evm/optimism/rpc",[ze]:"https://api.dexter.cash/api/evm/avalanche/rpc",[le]:"https://worldchain-mainnet.g.alchemy.com/public",[ue]:"https://rpc.monad.xyz",[me]:"https://rpc.mainnet.chain.robinhood.com",[de]:"https://skale-base.skalenodes.com/v1/base",[pe]:"https://base-sepolia-testnet.skalenodes.com/v1/jubilant-horrible-ancha",[We]:"https://eth.llamarpc.com"},ge="https://x402.dexter.cash";var en="dexter-tab-hosted-pay-before-delivery/v1",tn="pay_before_delivery_seller_2xx",nn="x-tab-voucher",rn="base64(188-byte sessionRegisterMessage)";function Xe(e){return e.startsWith("solana:")||e==="solana"}function or(e){if(typeof Buffer<"u")return Buffer.from(e,"utf-8").toString("base64");let t=new TextEncoder().encode(e),n="";for(let r=0;r<t.length;r++)n+=String.fromCharCode(t[r]);return btoa(n)}function sr(e){if(typeof Buffer<"u")return Buffer.from(e,"base64").toString("utf-8");let t=atob(e),n=new Uint8Array(t.length);for(let r=0;r<t.length;r++)n[r]=t.charCodeAt(r);return new TextDecoder().decode(n)}function je(e){return or(JSON.stringify(e))}function he(e){return JSON.parse(sr(e))}function ar(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 fe=class extends Error{status;body;constructor(t,n){super(`HTTP ${t}`),this.status=t,this.body=n}},Je=class{facilitatorUrl;cachedSupported=null;cacheTime=0;CACHE_TTL_MS=6e4;timeoutMs;maxRetries;retryBaseMs;constructor(t=ge,n){this.facilitatorUrl=t.replace(/\/$/,""),this.timeoutMs=n?.timeoutMs??1e4,this.maxRetries=n?.maxRetries??3,this.retryBaseMs=n?.retryBaseMs??500}async fetchWithTimeout(t,n){let r=new AbortController,i=setTimeout(()=>r.abort(),this.timeoutMs);try{return await fetch(t,{...n,signal:r.signal})}finally{clearTimeout(i)}}async fetchWithRetry(t,n){let r;for(let i=0;i<this.maxRetries;i++)try{let o=await this.fetchWithTimeout(t,n);if(!o.ok&&o.status>=500)throw new fe(o.status,await o.text());return o}catch(o){if(r=o,i<this.maxRetries-1&&ar(o)){let c=this.retryBaseMs*Math.pow(2,i);await new Promise(a=>setTimeout(a,c));continue}throw o}throw r}async getSupported(){let t=Date.now();if(this.cachedSupported&&t-this.cacheTime<this.CACHE_TTL_MS)return this.cachedSupported;let n=await this.fetchWithTimeout(`${this.facilitatorUrl}/supported`);if(!n.ok)throw new Error(`Facilitator /supported returned ${n.status}`);return this.cachedSupported=await n.json(),this.cacheTime=t,this.cachedSupported}async getFeePayer(t){let r=(await this.getSupported()).kinds.find(i=>i.x402Version===2&&(i.scheme==="exact"||i.scheme==="exact-approval")&&i.network===t);if(!r)throw new Error(`Facilitator does not support network "${t}" with a recognized scheme`);return r.extra?.feePayer}async getNetworkExtra(t){return(await this.getSupported()).kinds.find(i=>i.x402Version===2&&(i.scheme==="exact"||i.scheme==="exact-approval"||i.scheme==="batch-settlement")&&i.network===t)?.extra}async verifyPayment(t,n){try{let r=he(t),i=await this.fetchWithRetry(`${this.facilitatorUrl}/verify`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({x402Version:2,paymentPayload:r,paymentRequirements:n})});return i.ok?await i.json():{isValid:!1,invalidReason:`facilitator_error_${i.status}`}}catch(r){return{isValid:!1,invalidReason:r instanceof fe?`facilitator_error_${r.status}`:r instanceof Error&&r.name==="AbortError"?"facilitator_timeout":r instanceof Error?r.message:"unexpected_verify_error"}}}async settlePayment(t,n){try{let r=he(t),i=await this.fetchWithRetry(`${this.facilitatorUrl}/settle`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({x402Version:2,paymentPayload:r,paymentRequirements:n})});return i.ok?{...await i.json(),network:n.network}:{success:!1,network:n.network,errorReason:`facilitator_error_${i.status}`}}catch(r){let i=r instanceof fe?`facilitator_error_${r.status}`:r instanceof Error&&r.name==="AbortError"?"facilitator_timeout":r instanceof Error?r.message:"unexpected_settle_error";return{success:!1,network:n.network,errorReason:i}}}};function cr(e){if(Xe(e))return{address:Ge,decimals:6};let t=Zt[e];if(t){let n=Qt[t]?.decimals??6;return{address:t,decimals:n}}return{address:Ge,decimals:6}}function on(e){try{let t=he(e);return t?.accepted?.amount??t?.accepted?.maxAmountRequired}catch{return}}function ye(e){let{payTo:t,facilitatorUrl:n=ge,network:r=M,defaultTimeoutSeconds:i=60}=e,o=e.asset??cr(r),c=e.scheme??"exact";if(c==="tab"&&!Xe(r))throw new Error(`scheme 'tab' is SVM-only; got network "${r}"`);let a=new Je(n),u=null,h=new Map,d=3e4,g=Date.now();function s(f){let p=(f.maxTimeoutSeconds||i)*1e3;if(h.set(f.payTo,{accept:f,expiresAt:Date.now()+p}),Date.now()-g>d){let b=Date.now();for(let[z,L]of h)L.expiresAt<b&&h.delete(z);g=b}}function y(f){let p=h.get(f);if(p){if(p.expiresAt<Date.now()){h.delete(f);return}return p.accept}}async function l(f){return typeof t=="string"?t:t(f||{})}async function A(){if(u||(u=await a.getNetworkExtra(r)),Xe(r)&&!u?.feePayer)throw new Error(`Facilitator does not provide feePayer for network "${r}"`);return{...u?.feePayer?{feePayer:u.feePayer}:{},decimals:u?.decimals??o.decimals,name:u?.name,version:u?.version,...c==="batch-settlement"&&u?.receiverAuthorizer?{receiverAuthorizer:u.receiverAuthorizer}:{},...c==="tab"?{voucherHeader:nn,registrationEncoding:rn,termsVersion:en,acceptanceRule:tn}:{}}}async function R(f,p){let{amountAtomic:b,timeoutSeconds:z=i}=p,L=await A(),H={scheme:c,network:r,amount:b,maxAmountRequired:b,asset:o.address,payTo:f,maxTimeoutSeconds:z,extra:L};return s(H),H}async function N(f){let p=await l({amountAtomic:f.amountAtomic,resourceUrl:f.resourceUrl});return R(p,f)}async function O(f){let{resourceUrl:p,description:b,mimeType:z="application/json"}=f,L={url:p,description:b,mimeType:z},H=await N(f);return{x402Version:2,resource:L,accepts:[H],error:"Payment required"}}function q(f){return je(f)}function D(f){return{status:402,headers:{"PAYMENT-REQUIRED":q(f)},body:{}}}async function k(f,p){if(!p){let b=await l({paymentHeader:f});p=y(b),p||(p=await R(b,{amountAtomic:on(f)??"0",resourceUrl:""}))}return a.verifyPayment(f,p)}async function Ye(f,p){if(!p){let b=await l({paymentHeader:f});p=y(b),p||(p=await R(b,{amountAtomic:on(f)??"0",resourceUrl:""}))}return a.settlePayment(f,p)}return{buildRequirements:O,encodeRequirements:q,create402Response:D,verifyPayment:k,settlePayment:Ye,getPaymentAccept:N,network:r,assetDecimals:o.decimals,facilitator:a}}var lr={"solana:mainnet":M};function an(e){let t=lr[e.network];if(!t)throw new Error(`tabChallengeMiddleware: unsupported network "${e.network}"`);let n=typeof e.sellerPubkey=="string"?e.sellerPubkey:e.sellerPubkey.toBase58();new sn.PublicKey(n);let r=ye({payTo:n,network:t,scheme:"tab",facilitatorUrl:e.facilitatorUrl}),i=U(e.perUnit);return async(o,c,a)=>{if(o.headers[F])return a();try{let u=`${o.protocol}://${o.get("host")}${o.originalUrl}`,h=await r.buildRequirements({amountAtomic:i,resourceUrl:u,description:e.description}),d=r.create402Response(h);c.set(d.headers).status(d.status).json(d.body)}catch(u){let h=u?.message??String(u);c.status(503).set({"Retry-After":"5"}).json({error:"challenge_unavailable",detail:h})}}}var cn=require("@solana/web3.js");var ur={"solana:mainnet":M};function ln(e){let t=ur[e.network];if(!t)throw new Error(`tabOrExactMiddleware: unsupported network "${e.network}"`);let n=typeof e.sellerPubkey=="string"?e.sellerPubkey:e.sellerPubkey.toBase58();new cn.PublicKey(n);let r=ye({payTo:n,network:t,scheme:"tab",facilitatorUrl:e.facilitatorUrl}),i=ye({payTo:n,network:t,scheme:"exact",facilitatorUrl:e.facilitatorUrl}),o=U(e.perUnit),c=Ue({connection:e.connection,sellerPubkey:n,network:e.network,perUnit:e.perUnit,settle:"on-close",facilitatorUrl:e.facilitatorUrl,lockCadence:e.lockCadence});return async(a,u,h)=>{if(a.headers[F])return c(a,u,h);let d=`${a.protocol}://${a.get("host")}${a.originalUrl}`,g=a.headers["payment-signature"];if(g){let s;try{s=await i.getPaymentAccept({amountAtomic:o,resourceUrl:d,description:e.description})}catch(y){let l=y?.message??String(y);u.status(503).set({"Retry-After":"5"}).json({error:"challenge_unavailable",detail:l});return}try{let y=await i.verifyPayment(g,s);if(!y.isValid){u.status(402).json({error:"Payment verification failed",reason:y.invalidReason});return}let l=await i.settlePayment(g,s);if(!l.success){u.status(402).json({error:"Payment settlement failed",reason:l.errorReason});return}return a.x402={transaction:l.transaction,payer:y.payer??"",network:l.network||t},u.setHeader("PAYMENT-RESPONSE",je({success:!0,transaction:l.transaction,network:l.network||t,payer:y.payer??""})),h()}catch{u.status(500).json({error:"Payment processing error"});return}}try{let s={amountAtomic:o,resourceUrl:d,description:e.description},[y,l]=await Promise.all([r.buildRequirements(s),i.buildRequirements(s)]),A={...y,accepts:[...y.accepts,...l.accepts]};u.set({"PAYMENT-REQUIRED":r.encodeRequirements(A)}).status(402).json({error:"Payment required",accepts:A.accepts,resource:A.resource})}catch(s){let y=s?.message??String(s);u.status(503).set({"Retry-After":"5"}).json({error:"challenge_unavailable",detail:y})}}}0&&(module.exports={FileChannelLedger,FileVoucherStore,InMemoryChannelLedger,InMemoryVoucherStore,InvalidRegistrationError,InvalidVoucherError,InvalidVoucherSignatureError,OnChainVerificationError,RedisChannelLedger,ScopeViolationError,TAB_VOUCHER_HEADER,deserializeChannelLedgerEntry,enforceScope,openSse,parseRegistration,requireTab,serializeChannelLedgerEntry,tabChallengeMiddleware,tabMiddleware,tabOrExactMiddleware,verifyRegistrationOnChain,verifyVoucherSignature});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { SignedVoucher, AtomicAmount, TabNetworkId, HumanAmount } from '@dexterai/vault/types';
|
|
2
|
-
import {
|
|
2
|
+
import { Request, RequestHandler, Response } from 'express';
|
|
3
3
|
import { Connection, PublicKey } from '@solana/web3.js';
|
|
4
4
|
|
|
5
5
|
/**
|
|
@@ -276,8 +276,8 @@ interface SseMeter {
|
|
|
276
276
|
}
|
|
277
277
|
/** Errors thrown by the seller middleware on bad vouchers. */
|
|
278
278
|
declare class InvalidVoucherError extends Error {
|
|
279
|
-
readonly reason: 'signature_invalid' | 'registration_invalid' | 'cap_exceeded' | 'session_expired' | 'wrong_counterparty' | 'non_monotonic' | 'channel_busy';
|
|
280
|
-
constructor(reason: 'signature_invalid' | 'registration_invalid' | 'cap_exceeded' | 'session_expired' | 'wrong_counterparty' | 'non_monotonic' | 'channel_busy', detail?: string);
|
|
279
|
+
readonly reason: 'signature_invalid' | 'registration_invalid' | 'cap_exceeded' | 'session_expired' | 'wrong_counterparty' | 'non_monotonic' | 'wire_version_mismatch' | 'reservation_mismatch' | 'voucher_already_covered' | 'reservation_proof_missing' | 'reservation_proof_invalid' | 'channel_busy';
|
|
280
|
+
constructor(reason: 'signature_invalid' | 'registration_invalid' | 'cap_exceeded' | 'session_expired' | 'wrong_counterparty' | 'non_monotonic' | 'wire_version_mismatch' | 'reservation_mismatch' | 'voucher_already_covered' | 'reservation_proof_missing' | 'reservation_proof_invalid' | 'channel_busy', detail?: string);
|
|
281
281
|
}
|
|
282
282
|
|
|
283
283
|
/**
|
|
@@ -336,18 +336,21 @@ declare class RedisChannelLedger implements ChannelLedger {
|
|
|
336
336
|
*
|
|
337
337
|
* Wire shape:
|
|
338
338
|
* - Buyer sends each paid request with header `X-Tab-Voucher: <base64-json>`
|
|
339
|
-
* - The
|
|
340
|
-
* registration + signature)
|
|
341
|
-
*
|
|
339
|
+
* - The envelope contains the SignedVoucher fields (payload + session pubkey
|
|
340
|
+
* + registration + signature). V2 additionally carries the complete
|
|
341
|
+
* FinalVoucherV2ReservationReceipt used only as an untrusted proof locator.
|
|
342
|
+
* - On the FIRST V1 voucher of a session, the middleware parses the
|
|
342
343
|
* registration, verifies it against the on-chain vault (one RPC call),
|
|
343
|
-
* and caches the result
|
|
344
|
+
* and caches the result. V2 re-reads the SessionAccount on EVERY voucher
|
|
345
|
+
* because the live reservation is the seller's delivery authorization.
|
|
344
346
|
* - On EVERY voucher, the middleware verifies the session-key signature
|
|
345
347
|
* and enforces scope (cap, expiry, counterparty, monotonicity)
|
|
346
348
|
* - The route handler reads `req.tab` and either runs a stream against it
|
|
347
349
|
* or rejects with 402 Payment Required
|
|
348
350
|
*
|
|
349
|
-
*
|
|
350
|
-
*
|
|
351
|
+
* Historical V1 keeps the cached, no-chain hot path. Native Tab V2 deliberately
|
|
352
|
+
* performs finalized registration/reservation admission on every voucher and
|
|
353
|
+
* independently proves the exact authority-signed settle + Memo before delivery.
|
|
351
354
|
*/
|
|
352
355
|
|
|
353
356
|
declare module 'express-serve-static-core' {
|
|
@@ -361,7 +364,7 @@ interface TabMiddlewareConfig extends TabMiddlewareOptions {
|
|
|
361
364
|
/** The seller's pubkey — used as allowed_counterparty for scope check. */
|
|
362
365
|
sellerPubkey: string | PublicKey;
|
|
363
366
|
}
|
|
364
|
-
/** Header the buyer sends with each paid request.
|
|
367
|
+
/** Header the buyer sends with each paid request. Base64 JSON voucher envelope. */
|
|
365
368
|
declare const TAB_VOUCHER_HEADER = "x-tab-voucher";
|
|
366
369
|
declare function tabMiddleware(config: TabMiddlewareConfig): RequestHandler;
|
|
367
370
|
/** Pull the SellerTab off a request. Throws if the middleware didn't run. */
|
|
@@ -448,16 +451,16 @@ declare class FileVoucherStore implements VoucherStore {
|
|
|
448
451
|
* Two-layer verification:
|
|
449
452
|
*
|
|
450
453
|
* 1. parseRegistration(registrationBytes)
|
|
451
|
-
* Parses the
|
|
454
|
+
* Parses the 188-byte V2 registration message into a scope. Synchronous,
|
|
452
455
|
* no I/O. This gives the seller everything they need to enforce limits
|
|
453
456
|
* LOCALLY (cap, expiry, counterparty) and to know which vault to read
|
|
454
457
|
* for passkey verification.
|
|
455
458
|
*
|
|
456
459
|
* 2. verifyRegistrationOnChain(connection, registration, programId)
|
|
457
|
-
* Reads the
|
|
458
|
-
*
|
|
459
|
-
*
|
|
460
|
-
*
|
|
460
|
+
* Reads the authoritative SessionAccount and verifies that the complete
|
|
461
|
+
* registration still matches it. Historical V1 sellers may cache this;
|
|
462
|
+
* Native Tab V2 sellers must repeat it for every reserved voucher because
|
|
463
|
+
* currentOutstanding is the per-voucher delivery fence.
|
|
461
464
|
*
|
|
462
465
|
* 3. verifyVoucherSignature(voucher, sessionPublicKey, channelIdBytes)
|
|
463
466
|
* Verifies the session-key signature over the 44-byte voucher payload.
|
|
@@ -478,6 +481,7 @@ interface ParsedRegistration {
|
|
|
478
481
|
nonce: number;
|
|
479
482
|
maxRevolvingCapacity: bigint;
|
|
480
483
|
}
|
|
484
|
+
type NativeTabWireVersion = 1 | 2;
|
|
481
485
|
declare class InvalidRegistrationError extends Error {
|
|
482
486
|
readonly reason: 'wrong_length' | 'wrong_domain' | 'wrong_program' | 'expiry_in_past' | 'cap_zero';
|
|
483
487
|
constructor(reason: 'wrong_length' | 'wrong_domain' | 'wrong_program' | 'expiry_in_past' | 'cap_zero', detail?: string);
|
|
@@ -489,22 +493,27 @@ declare class InvalidRegistrationError extends Error {
|
|
|
489
493
|
*/
|
|
490
494
|
declare function parseRegistration(registration: Uint8Array): ParsedRegistration;
|
|
491
495
|
declare class OnChainVerificationError extends Error {
|
|
492
|
-
readonly reason: 'vault_not_found' | 'session_not_active' | 'session_pubkey_mismatch' | 'wrong_program';
|
|
493
|
-
constructor(reason: 'vault_not_found' | 'session_not_active' | 'session_pubkey_mismatch' | 'wrong_program', detail?: string);
|
|
496
|
+
readonly reason: 'vault_not_found' | 'session_not_active' | 'session_pubkey_mismatch' | 'registration_state_mismatch' | 'wire_version_mismatch' | 'wrong_program';
|
|
497
|
+
constructor(reason: 'vault_not_found' | 'session_not_active' | 'session_pubkey_mismatch' | 'registration_state_mismatch' | 'wire_version_mismatch' | 'wrong_program', detail?: string);
|
|
494
498
|
}
|
|
495
|
-
/** The session's on-chain
|
|
496
|
-
*
|
|
497
|
-
*
|
|
498
|
-
*
|
|
499
|
-
* session can't re-consume budget that already settled/crystallized. */
|
|
499
|
+
/** The session's authoritative on-chain delivery evidence.
|
|
500
|
+
* `frontierAtomic` = max(spent, crystallized), the floor below which no voucher
|
|
501
|
+
* can ever settle or lock again. V2 additionally requires
|
|
502
|
+
* `currentOutstandingAtomic` to equal the voucher's uncovered delta. */
|
|
500
503
|
interface OnChainSessionFrontier {
|
|
504
|
+
/** Program account layout version. A live SessionAccount is currently 1. */
|
|
505
|
+
sessionAccountVersion: number;
|
|
506
|
+
/** V1/V2 authorization wire selected by the active registration nonce. */
|
|
507
|
+
wireVersion: NativeTabWireVersion;
|
|
501
508
|
frontierAtomic: AtomicAmount;
|
|
502
509
|
spentAtomic: AtomicAmount;
|
|
510
|
+
currentOutstandingAtomic: AtomicAmount;
|
|
503
511
|
crystallizedCumulativeAtomic: AtomicAmount;
|
|
504
512
|
}
|
|
505
513
|
/**
|
|
506
514
|
* Verify a registration against on-chain state. Throws on any mismatch;
|
|
507
|
-
* on success returns the session's terminal
|
|
515
|
+
* on success returns the session's live version, terminal frontier, and exact
|
|
516
|
+
* outstanding reservation.
|
|
508
517
|
*
|
|
509
518
|
* V6: a session is its own PDA ([b"session", vault, allowed_counterparty]),
|
|
510
519
|
* NOT an inline field on the vault. We read that SessionAccount and confirm it
|
|
@@ -514,9 +523,8 @@ interface OnChainSessionFrontier {
|
|
|
514
523
|
* the secp256r1 precompile inside that tx — the seller just confirms the
|
|
515
524
|
* on-chain witness still holds.
|
|
516
525
|
*
|
|
517
|
-
*
|
|
518
|
-
*
|
|
519
|
-
* adapter waits for visibility before openTab returns).
|
|
526
|
+
* The seller reads the exact derived SessionAccount PDA at `finalized`; a
|
|
527
|
+
* confirmed-only reservation is intentionally insufficient for delivery.
|
|
520
528
|
*/
|
|
521
529
|
declare function verifyRegistrationOnChain(connection: Connection, registration: ParsedRegistration): Promise<OnChainSessionFrontier>;
|
|
522
530
|
declare class InvalidVoucherSignatureError extends Error {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { SignedVoucher, AtomicAmount, TabNetworkId, HumanAmount } from '@dexterai/vault/types';
|
|
2
|
-
import {
|
|
2
|
+
import { Request, RequestHandler, Response } from 'express';
|
|
3
3
|
import { Connection, PublicKey } from '@solana/web3.js';
|
|
4
4
|
|
|
5
5
|
/**
|
|
@@ -276,8 +276,8 @@ interface SseMeter {
|
|
|
276
276
|
}
|
|
277
277
|
/** Errors thrown by the seller middleware on bad vouchers. */
|
|
278
278
|
declare class InvalidVoucherError extends Error {
|
|
279
|
-
readonly reason: 'signature_invalid' | 'registration_invalid' | 'cap_exceeded' | 'session_expired' | 'wrong_counterparty' | 'non_monotonic' | 'channel_busy';
|
|
280
|
-
constructor(reason: 'signature_invalid' | 'registration_invalid' | 'cap_exceeded' | 'session_expired' | 'wrong_counterparty' | 'non_monotonic' | 'channel_busy', detail?: string);
|
|
279
|
+
readonly reason: 'signature_invalid' | 'registration_invalid' | 'cap_exceeded' | 'session_expired' | 'wrong_counterparty' | 'non_monotonic' | 'wire_version_mismatch' | 'reservation_mismatch' | 'voucher_already_covered' | 'reservation_proof_missing' | 'reservation_proof_invalid' | 'channel_busy';
|
|
280
|
+
constructor(reason: 'signature_invalid' | 'registration_invalid' | 'cap_exceeded' | 'session_expired' | 'wrong_counterparty' | 'non_monotonic' | 'wire_version_mismatch' | 'reservation_mismatch' | 'voucher_already_covered' | 'reservation_proof_missing' | 'reservation_proof_invalid' | 'channel_busy', detail?: string);
|
|
281
281
|
}
|
|
282
282
|
|
|
283
283
|
/**
|
|
@@ -336,18 +336,21 @@ declare class RedisChannelLedger implements ChannelLedger {
|
|
|
336
336
|
*
|
|
337
337
|
* Wire shape:
|
|
338
338
|
* - Buyer sends each paid request with header `X-Tab-Voucher: <base64-json>`
|
|
339
|
-
* - The
|
|
340
|
-
* registration + signature)
|
|
341
|
-
*
|
|
339
|
+
* - The envelope contains the SignedVoucher fields (payload + session pubkey
|
|
340
|
+
* + registration + signature). V2 additionally carries the complete
|
|
341
|
+
* FinalVoucherV2ReservationReceipt used only as an untrusted proof locator.
|
|
342
|
+
* - On the FIRST V1 voucher of a session, the middleware parses the
|
|
342
343
|
* registration, verifies it against the on-chain vault (one RPC call),
|
|
343
|
-
* and caches the result
|
|
344
|
+
* and caches the result. V2 re-reads the SessionAccount on EVERY voucher
|
|
345
|
+
* because the live reservation is the seller's delivery authorization.
|
|
344
346
|
* - On EVERY voucher, the middleware verifies the session-key signature
|
|
345
347
|
* and enforces scope (cap, expiry, counterparty, monotonicity)
|
|
346
348
|
* - The route handler reads `req.tab` and either runs a stream against it
|
|
347
349
|
* or rejects with 402 Payment Required
|
|
348
350
|
*
|
|
349
|
-
*
|
|
350
|
-
*
|
|
351
|
+
* Historical V1 keeps the cached, no-chain hot path. Native Tab V2 deliberately
|
|
352
|
+
* performs finalized registration/reservation admission on every voucher and
|
|
353
|
+
* independently proves the exact authority-signed settle + Memo before delivery.
|
|
351
354
|
*/
|
|
352
355
|
|
|
353
356
|
declare module 'express-serve-static-core' {
|
|
@@ -361,7 +364,7 @@ interface TabMiddlewareConfig extends TabMiddlewareOptions {
|
|
|
361
364
|
/** The seller's pubkey — used as allowed_counterparty for scope check. */
|
|
362
365
|
sellerPubkey: string | PublicKey;
|
|
363
366
|
}
|
|
364
|
-
/** Header the buyer sends with each paid request.
|
|
367
|
+
/** Header the buyer sends with each paid request. Base64 JSON voucher envelope. */
|
|
365
368
|
declare const TAB_VOUCHER_HEADER = "x-tab-voucher";
|
|
366
369
|
declare function tabMiddleware(config: TabMiddlewareConfig): RequestHandler;
|
|
367
370
|
/** Pull the SellerTab off a request. Throws if the middleware didn't run. */
|
|
@@ -448,16 +451,16 @@ declare class FileVoucherStore implements VoucherStore {
|
|
|
448
451
|
* Two-layer verification:
|
|
449
452
|
*
|
|
450
453
|
* 1. parseRegistration(registrationBytes)
|
|
451
|
-
* Parses the
|
|
454
|
+
* Parses the 188-byte V2 registration message into a scope. Synchronous,
|
|
452
455
|
* no I/O. This gives the seller everything they need to enforce limits
|
|
453
456
|
* LOCALLY (cap, expiry, counterparty) and to know which vault to read
|
|
454
457
|
* for passkey verification.
|
|
455
458
|
*
|
|
456
459
|
* 2. verifyRegistrationOnChain(connection, registration, programId)
|
|
457
|
-
* Reads the
|
|
458
|
-
*
|
|
459
|
-
*
|
|
460
|
-
*
|
|
460
|
+
* Reads the authoritative SessionAccount and verifies that the complete
|
|
461
|
+
* registration still matches it. Historical V1 sellers may cache this;
|
|
462
|
+
* Native Tab V2 sellers must repeat it for every reserved voucher because
|
|
463
|
+
* currentOutstanding is the per-voucher delivery fence.
|
|
461
464
|
*
|
|
462
465
|
* 3. verifyVoucherSignature(voucher, sessionPublicKey, channelIdBytes)
|
|
463
466
|
* Verifies the session-key signature over the 44-byte voucher payload.
|
|
@@ -478,6 +481,7 @@ interface ParsedRegistration {
|
|
|
478
481
|
nonce: number;
|
|
479
482
|
maxRevolvingCapacity: bigint;
|
|
480
483
|
}
|
|
484
|
+
type NativeTabWireVersion = 1 | 2;
|
|
481
485
|
declare class InvalidRegistrationError extends Error {
|
|
482
486
|
readonly reason: 'wrong_length' | 'wrong_domain' | 'wrong_program' | 'expiry_in_past' | 'cap_zero';
|
|
483
487
|
constructor(reason: 'wrong_length' | 'wrong_domain' | 'wrong_program' | 'expiry_in_past' | 'cap_zero', detail?: string);
|
|
@@ -489,22 +493,27 @@ declare class InvalidRegistrationError extends Error {
|
|
|
489
493
|
*/
|
|
490
494
|
declare function parseRegistration(registration: Uint8Array): ParsedRegistration;
|
|
491
495
|
declare class OnChainVerificationError extends Error {
|
|
492
|
-
readonly reason: 'vault_not_found' | 'session_not_active' | 'session_pubkey_mismatch' | 'wrong_program';
|
|
493
|
-
constructor(reason: 'vault_not_found' | 'session_not_active' | 'session_pubkey_mismatch' | 'wrong_program', detail?: string);
|
|
496
|
+
readonly reason: 'vault_not_found' | 'session_not_active' | 'session_pubkey_mismatch' | 'registration_state_mismatch' | 'wire_version_mismatch' | 'wrong_program';
|
|
497
|
+
constructor(reason: 'vault_not_found' | 'session_not_active' | 'session_pubkey_mismatch' | 'registration_state_mismatch' | 'wire_version_mismatch' | 'wrong_program', detail?: string);
|
|
494
498
|
}
|
|
495
|
-
/** The session's on-chain
|
|
496
|
-
*
|
|
497
|
-
*
|
|
498
|
-
*
|
|
499
|
-
* session can't re-consume budget that already settled/crystallized. */
|
|
499
|
+
/** The session's authoritative on-chain delivery evidence.
|
|
500
|
+
* `frontierAtomic` = max(spent, crystallized), the floor below which no voucher
|
|
501
|
+
* can ever settle or lock again. V2 additionally requires
|
|
502
|
+
* `currentOutstandingAtomic` to equal the voucher's uncovered delta. */
|
|
500
503
|
interface OnChainSessionFrontier {
|
|
504
|
+
/** Program account layout version. A live SessionAccount is currently 1. */
|
|
505
|
+
sessionAccountVersion: number;
|
|
506
|
+
/** V1/V2 authorization wire selected by the active registration nonce. */
|
|
507
|
+
wireVersion: NativeTabWireVersion;
|
|
501
508
|
frontierAtomic: AtomicAmount;
|
|
502
509
|
spentAtomic: AtomicAmount;
|
|
510
|
+
currentOutstandingAtomic: AtomicAmount;
|
|
503
511
|
crystallizedCumulativeAtomic: AtomicAmount;
|
|
504
512
|
}
|
|
505
513
|
/**
|
|
506
514
|
* Verify a registration against on-chain state. Throws on any mismatch;
|
|
507
|
-
* on success returns the session's terminal
|
|
515
|
+
* on success returns the session's live version, terminal frontier, and exact
|
|
516
|
+
* outstanding reservation.
|
|
508
517
|
*
|
|
509
518
|
* V6: a session is its own PDA ([b"session", vault, allowed_counterparty]),
|
|
510
519
|
* NOT an inline field on the vault. We read that SessionAccount and confirm it
|
|
@@ -514,9 +523,8 @@ interface OnChainSessionFrontier {
|
|
|
514
523
|
* the secp256r1 precompile inside that tx — the seller just confirms the
|
|
515
524
|
* on-chain witness still holds.
|
|
516
525
|
*
|
|
517
|
-
*
|
|
518
|
-
*
|
|
519
|
-
* adapter waits for visibility before openTab returns).
|
|
526
|
+
* The seller reads the exact derived SessionAccount PDA at `finalized`; a
|
|
527
|
+
* confirmed-only reservation is intentionally insufficient for delivery.
|
|
520
528
|
*/
|
|
521
529
|
declare function verifyRegistrationOnChain(connection: Connection, registration: ParsedRegistration): Promise<OnChainSessionFrontier>;
|
|
522
530
|
declare class InvalidVoucherSignatureError extends Error {
|