@dexterai/x402 3.9.1 → 3.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- "use strict";var nt=Object.create;var k=Object.defineProperty;var at=Object.getOwnPropertyDescriptor;var rt=Object.getOwnPropertyNames;var it=Object.getPrototypeOf,ot=Object.prototype.hasOwnProperty;var st=(t,e)=>{for(var n in e)k(t,n,{get:e[n],enumerable:!0})},$=(t,e,n,a)=>{if(e&&typeof e=="object"||typeof e=="function")for(let r of rt(e))!ot.call(t,r)&&r!==n&&k(t,r,{get:()=>e[r],enumerable:!(a=at(e,r))||a.enumerable});return t};var lt=(t,e,n)=>(n=t!=null?nt(it(t)):{},$(e||!t||!t.__esModule?k(n,"default",{value:t,enumerable:!0}):n,t)),ct=t=>$(k({},"__esModule",{value:!0}),t);var ft={};st(ft,{InsufficientBalanceError:()=>A,UnsupportedNetworkError:()=>f,WithdrawNotReadyError:()=>y,createFileChannelStore:()=>P,createLocalStorageChannelStore:()=>B,getDefaultChannelStore:()=>W,openBatchChannel:()=>Q,resumeBatchChannel:()=>Y});module.exports=ct(ft);var s=require("viem"),C=require("viem/chains"),X=require("@x402/evm"),I=require("@x402/evm/batch-settlement/client"),T=require("@x402/core/client");var O=require("os"),N=require("path"),j=require("@x402/evm/batch-settlement/client");function P(t){return new j.FileClientChannelStorage({directory:t})}var E="dexter-x402-channel:";function B(t){return{async get(e){let n=t.getItem(E+e);return n?JSON.parse(n):void 0},async set(e,n){t.setItem(E+e,JSON.stringify(n))},async delete(e){t.removeItem(E+e)}}}function W(){let t=globalThis.localStorage;return t?B(t):P((0,N.join)((0,O.homedir)(),".dexter-x402","channels"))}var A=class extends Error{constructor(e){super(e),this.name="InsufficientBalanceError"}},f=class extends Error{constructor(e){super(e),this.name="UnsupportedNetworkError"}};var L=require("viem"),z=require("@x402/evm/batch-settlement/client"),g=require("@x402/evm"),y=class extends Error{constructor(e){super(e),this.name="WithdrawNotReadyError"}};function M(t,e){return t===0?0:t+e}var dt=6,H=[{name:"payer",type:"address"},{name:"payerAuthorizer",type:"address"},{name:"receiver",type:"address"},{name:"receiverAuthorizer",type:"address"},{name:"token",type:"address"},{name:"withdrawDelay",type:"uint40"},{name:"salt",type:"bytes32"}],R=[{type:"function",name:"initiateWithdraw",inputs:[{name:"config",type:"tuple",components:H},{name:"amount",type:"uint128"}],outputs:[],stateMutability:"nonpayable"},{type:"function",name:"finalizeWithdraw",inputs:[{name:"config",type:"tuple",components:H}],outputs:[],stateMutability:"nonpayable"},{type:"function",name:"pendingWithdrawals",inputs:[{name:"channelId",type:"bytes32"}],outputs:[{name:"amount",type:"uint128"},{name:"initiatedAt",type:"uint40"}],stateMutability:"view"},{type:"function",name:"channels",inputs:[{name:"channelId",type:"bytes32"}],outputs:[{name:"balance",type:"uint128"},{name:"totalClaimed",type:"uint128"}],stateMutability:"view"}];function ht(t){if(Array.isArray(t))return{balance:BigInt(t[0]),totalClaimed:BigInt(t[1])};let e=t;return{balance:BigInt(e.balance),totalClaimed:BigInt(e.totalClaimed)}}function q(t){if(Array.isArray(t))return{amount:BigInt(t[0]),initiatedAt:Number(t[1])};let e=t;return{amount:BigInt(e.amount),initiatedAt:Number(e.initiatedAt)}}async function _(t){let{config:e,network:n,client:a,withdrawDelaySecs:r}=t,i=(0,z.computeChannelId)(e,n),l=t.amountAtomic;l===void 0&&(l=ht(await a.readContract({address:g.BATCH_SETTLEMENT_ADDRESS,abi:R,functionName:"channels",args:[i]})).balance);let c=await a.writeContract({address:g.BATCH_SETTLEMENT_ADDRESS,abi:R,functionName:"initiateWithdraw",args:[e,l]});await a.waitForTransactionReceipt({hash:c});let d=q(await a.readContract({address:g.BATCH_SETTLEMENT_ADDRESS,abi:R,functionName:"pendingWithdrawals",args:[i]}));return{initiateTx:c,finalizableAt:M(d.initiatedAt,r)}}async function J(t){let{config:e,network:n,client:a,withdrawDelaySecs:r}=t,i=(0,z.computeChannelId)(e,n),l=q(await a.readContract({address:g.BATCH_SETTLEMENT_ADDRESS,abi:R,functionName:"pendingWithdrawals",args:[i]}));if(l.initiatedAt===0)throw new y(`no withdrawal is pending for channel ${i} \u2014 call forceWithdraw first`);let c=M(l.initiatedAt,r),d=Math.floor(Date.now()/1e3);if(d<c)throw new y(`withdrawal for channel ${i} is not finalizable until ${c} (unix seconds) \u2014 ${c-d}s remaining`);let o=await a.writeContract({address:g.BATCH_SETTLEMENT_ADDRESS,abi:R,functionName:"finalizeWithdraw",args:[e]});return await a.waitForTransactionReceipt({hash:o}),{finalizeTx:o,withdrawnAmount:(0,L.formatUnits)(l.amount,dt)}}var V={"eip155:8453":{chain:C.base,defaultRpc:"https://mainnet.base.org"},"eip155:42161":{chain:C.arbitrum,defaultRpc:"https://arb1.arbitrum.io/rpc"},"eip155:137":{chain:C.polygon,defaultRpc:"https://polygon-rpc.com"}};function G(t){let e=V[t.network];if(!e)throw new f(`batch-settlement is not available on network "${t.network}" \u2014 supported: ${Object.keys(V).join(", ")}`);let n=t.rpcUrl??e.defaultRpc,a=t.wallet.signTypedData;if(typeof a!="function")throw new Error("batch-settlement requires an EvmWallet that supports signTypedData (EIP-712)");let r=(0,s.createWalletClient)({account:{address:t.wallet.address,type:"json-rpc"},chain:e.chain,transport:(0,s.http)(n)}).extend(s.publicActions),i=t.wallet.sendTransaction,l=typeof i=="function",c=async u=>{let b=u.to??void 0;if(!b)throw new Error("batch-settlement escape hatch: transaction is missing a `to` address");let m=u.data??"0x";if(typeof i=="function")return await i.call(t.wallet,{to:b,data:m,value:u.value});throw new Error("batch-settlement: wallet has no sendTransaction")},d=Object.assign(r,{address:t.wallet.address,signTypedData:u=>a.call(t.wallet,u),sendTransaction:c}),o=(0,X.toClientEvmSigner)(d),p=new I.BatchSettlementEvmScheme(o,{storage:t.store,depositPolicy:{depositMultiplier:4},depositStrategy:()=>t.depositAtomic,salt:t.salt}),h=new T.x402Client;h.register(t.network,p);let w=new T.x402HTTPClient(h);return{scheme:p,x402Cli:h,httpClient:w,rpcUrl:n,withdrawClient:d,canSubmitTransactions:l}}var v=6;function ut(t,e){let n=e>t?0n:t-e;return{deposited:(0,s.formatUnits)(t,v),spent:(0,s.formatUnits)(e,v),remaining:(0,s.formatUnits)(n,v)}}function mt(t){let e=t.payload;if(!e||typeof e!="object")return"";let n=e.voucher;if(!n||typeof n!="object")return"";let a=n.channelId;return typeof a=="string"?a:""}function pt(t){let e=t.payload;if(!e||typeof e!="object")return;let n=e.channelConfig;if(!(!n||typeof n!="object"))return n}function K(t){let{stack:e,store:n,network:a}=t,r="",i,l=t.depositedAtomic,c=0n;async function d(){if(!r)return;let o=await(0,I.getChannel)(n,r);o&&(o.chargedCumulativeAmount!==void 0&&(c=BigInt(o.chargedCumulativeAmount)),l===0n&&o.balance!==void 0&&(l=BigInt(o.balance)))}return{get channelId(){return r},get salt(){return t.salt},get network(){return a},get state(){return ut(l,c)},async fetch(o,p){let{httpClient:h}=e,w=await fetch(o,p);if(w.status!==402)return w;let F=h.getPaymentRequiredResponse(m=>w.headers.get(m),await w.clone().json().catch(()=>{})),u=2,b;for(let m=1;m<=u;m+=1){let S=await h.createPaymentPayload(F),D=mt(S);D&&(r=D);let U=pt(S);U&&(i=U);let Z=h.encodePaymentSignatureHeader(S),x=await fetch(o,{...p,headers:{...p?.headers??{},...Z}});b=x;let{recovered:tt}=await h.processPaymentResult(S,et=>x.headers.get(et),x.status);if(await d(),!(x.status===402&&tt&&m<u))return x}return b},async close(){return r&&await n.delete(r.toLowerCase()),{closed:!0}},async forceWithdraw(){if(!e.canSubmitTransactions)throw new Error("batch-settlement forceWithdraw requires a wallet with a sendTransaction method (the withdrawal escape hatch submits an on-chain transaction and the buyer pays gas; a signature-only wallet cannot use it).");if(!i)throw new Error("forceWithdraw is unavailable until the channel has resolved on-chain \u2014 make at least one fetch() against the channel first");return _({config:i,network:a,client:e.withdrawClient,withdrawDelaySecs:i.withdrawDelay})},async finalizeWithdraw(){if(!e.canSubmitTransactions)throw new Error("batch-settlement finalizeWithdraw requires a wallet with a sendTransaction method (the withdrawal escape hatch submits an on-chain transaction and the buyer pays gas; a signature-only wallet cannot use it).");if(!i)throw new Error("finalizeWithdraw is unavailable until the channel has resolved on-chain \u2014 make at least one fetch() against the channel first");return J({config:i,network:a,client:e.withdrawClient,withdrawDelaySecs:i.withdrawDelay})}}}async function wt(){let t=globalThis.crypto??(await import("crypto")).webcrypto,e=new Uint8Array(32);return t.getRandomValues(e),(0,s.toHex)(e)}async function Q(t){let e=t.store??W(),n=t.salt??await wt(),a;try{a=(0,s.parseUnits)(t.deposit,v)}catch{throw new Error(`deposit must be a valid USDC amount in decimal units (e.g. "0.30"), got "${t.deposit}"`)}if(a<=0n)throw new Error(`deposit must be a positive amount, got "${t.deposit}"`);let r=G({wallet:t.wallet,network:t.network,rpcUrl:t.rpcUrl,store:e,depositAtomic:a.toString(),salt:n});return K({stack:r,store:e,network:t.network,depositedAtomic:a,salt:n})}async function Y(t){let e=t.store??W(),n=G({wallet:t.wallet,network:t.network,rpcUrl:t.rpcUrl,store:e,depositAtomic:"0",salt:t.salt});return K({stack:n,store:e,network:t.network,depositedAtomic:0n,salt:t.salt})}0&&(module.exports={InsufficientBalanceError,UnsupportedNetworkError,WithdrawNotReadyError,createFileChannelStore,createLocalStorageChannelStore,getDefaultChannelStore,openBatchChannel,resumeBatchChannel});
1
+ "use strict";var nt=Object.create;var k=Object.defineProperty;var at=Object.getOwnPropertyDescriptor;var rt=Object.getOwnPropertyNames;var it=Object.getPrototypeOf,ot=Object.prototype.hasOwnProperty;var st=(t,e)=>{for(var n in e)k(t,n,{get:e[n],enumerable:!0})},$=(t,e,n,a)=>{if(e&&typeof e=="object"||typeof e=="function")for(let r of rt(e))!ot.call(t,r)&&r!==n&&k(t,r,{get:()=>e[r],enumerable:!(a=at(e,r))||a.enumerable});return t};var lt=(t,e,n)=>(n=t!=null?nt(it(t)):{},$(e||!t||!t.__esModule?k(n,"default",{value:t,enumerable:!0}):n,t)),ct=t=>$(k({},"__esModule",{value:!0}),t);var ft={};st(ft,{InsufficientBalanceError:()=>A,UnsupportedNetworkError:()=>f,WithdrawNotReadyError:()=>y,createFileChannelStore:()=>P,createLocalStorageChannelStore:()=>B,getDefaultChannelStore:()=>W,openBatchChannel:()=>Q,resumeBatchChannel:()=>Y});module.exports=ct(ft);var s=require("viem"),C=require("viem/chains"),X=require("@x402/evm"),I=require("@x402/evm/batch-settlement/client"),T=require("@x402/core/client");var O=require("os"),N=require("path"),j=require("@x402/evm/batch-settlement/client");function P(t){return new j.FileClientChannelStorage({directory:t})}var E="dexter-x402-channel:";function B(t){return{async get(e){let n=t.getItem(E+e);return n?JSON.parse(n):void 0},async set(e,n){t.setItem(E+e,JSON.stringify(n))},async delete(e){t.removeItem(E+e)}}}function W(){let t=globalThis.localStorage;return t?B(t):P((0,N.join)((0,O.homedir)(),".dexter-x402","channels"))}var A=class extends Error{constructor(e){super(e),this.name="InsufficientBalanceError"}},f=class extends Error{constructor(e){super(e),this.name="UnsupportedNetworkError"}};var L=require("viem"),z=require("@x402/evm/batch-settlement/client"),g=require("@x402/evm"),y=class extends Error{constructor(e){super(e),this.name="WithdrawNotReadyError"}};function M(t,e){return t===0?0:t+e}var dt=6,H=[{name:"payer",type:"address"},{name:"payerAuthorizer",type:"address"},{name:"receiver",type:"address"},{name:"receiverAuthorizer",type:"address"},{name:"token",type:"address"},{name:"withdrawDelay",type:"uint40"},{name:"salt",type:"bytes32"}],R=[{type:"function",name:"initiateWithdraw",inputs:[{name:"config",type:"tuple",components:H},{name:"amount",type:"uint128"}],outputs:[],stateMutability:"nonpayable"},{type:"function",name:"finalizeWithdraw",inputs:[{name:"config",type:"tuple",components:H}],outputs:[],stateMutability:"nonpayable"},{type:"function",name:"pendingWithdrawals",inputs:[{name:"channelId",type:"bytes32"}],outputs:[{name:"amount",type:"uint128"},{name:"initiatedAt",type:"uint40"}],stateMutability:"view"},{type:"function",name:"channels",inputs:[{name:"channelId",type:"bytes32"}],outputs:[{name:"balance",type:"uint128"},{name:"totalClaimed",type:"uint128"}],stateMutability:"view"}];function ht(t){if(Array.isArray(t))return{balance:BigInt(t[0]),totalClaimed:BigInt(t[1])};let e=t;return{balance:BigInt(e.balance),totalClaimed:BigInt(e.totalClaimed)}}function q(t){if(Array.isArray(t))return{amount:BigInt(t[0]),initiatedAt:Number(t[1])};let e=t;return{amount:BigInt(e.amount),initiatedAt:Number(e.initiatedAt)}}async function _(t){let{config:e,network:n,client:a,withdrawDelaySecs:r}=t,i=(0,z.computeChannelId)(e,n),l=t.amountAtomic;l===void 0&&(l=ht(await a.readContract({address:g.BATCH_SETTLEMENT_ADDRESS,abi:R,functionName:"channels",args:[i]})).balance);let c=await a.writeContract({address:g.BATCH_SETTLEMENT_ADDRESS,abi:R,functionName:"initiateWithdraw",args:[e,l]});await a.waitForTransactionReceipt({hash:c});let d=q(await a.readContract({address:g.BATCH_SETTLEMENT_ADDRESS,abi:R,functionName:"pendingWithdrawals",args:[i]}));return{initiateTx:c,finalizableAt:M(d.initiatedAt,r)}}async function J(t){let{config:e,network:n,client:a,withdrawDelaySecs:r}=t,i=(0,z.computeChannelId)(e,n),l=q(await a.readContract({address:g.BATCH_SETTLEMENT_ADDRESS,abi:R,functionName:"pendingWithdrawals",args:[i]}));if(l.initiatedAt===0)throw new y(`no withdrawal is pending for channel ${i} \u2014 call forceWithdraw first`);let c=M(l.initiatedAt,r),d=Math.floor(Date.now()/1e3);if(d<c)throw new y(`withdrawal for channel ${i} is not finalizable until ${c} (unix seconds) \u2014 ${c-d}s remaining`);let o=await a.writeContract({address:g.BATCH_SETTLEMENT_ADDRESS,abi:R,functionName:"finalizeWithdraw",args:[e]});return await a.waitForTransactionReceipt({hash:o}),{finalizeTx:o,withdrawnAmount:(0,L.formatUnits)(l.amount,dt)}}var V={"eip155:8453":{chain:C.base,defaultRpc:"https://api.dexter.cash/api/evm/base/rpc"},"eip155:42161":{chain:C.arbitrum,defaultRpc:"https://api.dexter.cash/api/evm/arbitrum/rpc"},"eip155:137":{chain:C.polygon,defaultRpc:"https://api.dexter.cash/api/evm/polygon/rpc"}};function G(t){let e=V[t.network];if(!e)throw new f(`batch-settlement is not available on network "${t.network}" \u2014 supported: ${Object.keys(V).join(", ")}`);let n=t.rpcUrl??e.defaultRpc,a=t.wallet.signTypedData;if(typeof a!="function")throw new Error("batch-settlement requires an EvmWallet that supports signTypedData (EIP-712)");let r=(0,s.createWalletClient)({account:{address:t.wallet.address,type:"json-rpc"},chain:e.chain,transport:(0,s.http)(n)}).extend(s.publicActions),i=t.wallet.sendTransaction,l=typeof i=="function",c=async u=>{let b=u.to??void 0;if(!b)throw new Error("batch-settlement escape hatch: transaction is missing a `to` address");let p=u.data??"0x";if(typeof i=="function")return await i.call(t.wallet,{to:b,data:p,value:u.value});throw new Error("batch-settlement: wallet has no sendTransaction")},d=Object.assign(r,{address:t.wallet.address,signTypedData:u=>a.call(t.wallet,u),sendTransaction:c}),o=(0,X.toClientEvmSigner)(d),m=new I.BatchSettlementEvmScheme(o,{storage:t.store,depositPolicy:{depositMultiplier:4},depositStrategy:()=>t.depositAtomic,salt:t.salt}),h=new T.x402Client;h.register(t.network,m);let w=new T.x402HTTPClient(h);return{scheme:m,x402Cli:h,httpClient:w,rpcUrl:n,withdrawClient:d,canSubmitTransactions:l}}var v=6;function ut(t,e){let n=e>t?0n:t-e;return{deposited:(0,s.formatUnits)(t,v),spent:(0,s.formatUnits)(e,v),remaining:(0,s.formatUnits)(n,v)}}function pt(t){let e=t.payload;if(!e||typeof e!="object")return"";let n=e.voucher;if(!n||typeof n!="object")return"";let a=n.channelId;return typeof a=="string"?a:""}function mt(t){let e=t.payload;if(!e||typeof e!="object")return;let n=e.channelConfig;if(!(!n||typeof n!="object"))return n}function K(t){let{stack:e,store:n,network:a}=t,r="",i,l=t.depositedAtomic,c=0n;async function d(){if(!r)return;let o=await(0,I.getChannel)(n,r);o&&(o.chargedCumulativeAmount!==void 0&&(c=BigInt(o.chargedCumulativeAmount)),l===0n&&o.balance!==void 0&&(l=BigInt(o.balance)))}return{get channelId(){return r},get salt(){return t.salt},get network(){return a},get state(){return ut(l,c)},async fetch(o,m){let{httpClient:h}=e,w=await fetch(o,m);if(w.status!==402)return w;let F=h.getPaymentRequiredResponse(p=>w.headers.get(p),await w.clone().json().catch(()=>{})),u=2,b;for(let p=1;p<=u;p+=1){let S=await h.createPaymentPayload(F),D=pt(S);D&&(r=D);let U=mt(S);U&&(i=U);let Z=h.encodePaymentSignatureHeader(S),x=await fetch(o,{...m,headers:{...m?.headers??{},...Z}});b=x;let{recovered:tt}=await h.processPaymentResult(S,et=>x.headers.get(et),x.status);if(await d(),!(x.status===402&&tt&&p<u))return x}return b},async close(){return r&&await n.delete(r.toLowerCase()),{closed:!0}},async forceWithdraw(){if(!e.canSubmitTransactions)throw new Error("batch-settlement forceWithdraw requires a wallet with a sendTransaction method (the withdrawal escape hatch submits an on-chain transaction and the buyer pays gas; a signature-only wallet cannot use it).");if(!i)throw new Error("forceWithdraw is unavailable until the channel has resolved on-chain \u2014 make at least one fetch() against the channel first");return _({config:i,network:a,client:e.withdrawClient,withdrawDelaySecs:i.withdrawDelay})},async finalizeWithdraw(){if(!e.canSubmitTransactions)throw new Error("batch-settlement finalizeWithdraw requires a wallet with a sendTransaction method (the withdrawal escape hatch submits an on-chain transaction and the buyer pays gas; a signature-only wallet cannot use it).");if(!i)throw new Error("finalizeWithdraw is unavailable until the channel has resolved on-chain \u2014 make at least one fetch() against the channel first");return J({config:i,network:a,client:e.withdrawClient,withdrawDelaySecs:i.withdrawDelay})}}}async function wt(){let t=globalThis.crypto??(await import("crypto")).webcrypto,e=new Uint8Array(32);return t.getRandomValues(e),(0,s.toHex)(e)}async function Q(t){let e=t.store??W(),n=t.salt??await wt(),a;try{a=(0,s.parseUnits)(t.deposit,v)}catch{throw new Error(`deposit must be a valid USDC amount in decimal units (e.g. "0.30"), got "${t.deposit}"`)}if(a<=0n)throw new Error(`deposit must be a positive amount, got "${t.deposit}"`);let r=G({wallet:t.wallet,network:t.network,rpcUrl:t.rpcUrl,store:e,depositAtomic:a.toString(),salt:n});return K({stack:r,store:e,network:t.network,depositedAtomic:a,salt:n})}async function Y(t){let e=t.store??W(),n=G({wallet:t.wallet,network:t.network,rpcUrl:t.rpcUrl,store:e,depositAtomic:"0",salt:t.salt});return K({stack:n,store:e,network:t.network,depositedAtomic:0n,salt:t.salt})}0&&(module.exports={InsufficientBalanceError,UnsupportedNetworkError,WithdrawNotReadyError,createFileChannelStore,createLocalStorageChannelStore,getDefaultChannelStore,openBatchChannel,resumeBatchChannel});
@@ -1 +1 @@
1
- import{createWalletClient as K,http as Q,publicActions as Y,parseUnits as Z,formatUnits as A,toHex as tt}from"viem";import{base as et,arbitrum as nt,polygon as at}from"viem/chains";import{toClientEvmSigner as rt}from"@x402/evm";import{BatchSettlementEvmScheme as it,getChannel as ot}from"@x402/evm/batch-settlement/client";import{x402Client as st,x402HTTPClient as lt}from"@x402/core/client";import{homedir as q}from"os";import{join as _}from"path";import{FileClientChannelStorage as J}from"@x402/evm/batch-settlement/client";function E(t){return new J({directory:t})}var R="dexter-x402-channel:";function P(t){return{async get(e){let n=t.getItem(R+e);return n?JSON.parse(n):void 0},async set(e,n){t.setItem(R+e,JSON.stringify(n))},async delete(e){t.removeItem(R+e)}}}function x(){let t=globalThis.localStorage;return t?P(t):E(_(q(),".dexter-x402","channels"))}var k=class extends Error{constructor(e){super(e),this.name="InsufficientBalanceError"}},y=class extends Error{constructor(e){super(e),this.name="UnsupportedNetworkError"}};import{formatUnits as V}from"viem";import{computeChannelId as z}from"@x402/evm/batch-settlement/client";import{BATCH_SETTLEMENT_ADDRESS as C}from"@x402/evm";var S=class extends Error{constructor(e){super(e),this.name="WithdrawNotReadyError"}};function F(t,e){return t===0?0:t+e}var X=6,B=[{name:"payer",type:"address"},{name:"payerAuthorizer",type:"address"},{name:"receiver",type:"address"},{name:"receiverAuthorizer",type:"address"},{name:"token",type:"address"},{name:"withdrawDelay",type:"uint40"},{name:"salt",type:"bytes32"}],b=[{type:"function",name:"initiateWithdraw",inputs:[{name:"config",type:"tuple",components:B},{name:"amount",type:"uint128"}],outputs:[],stateMutability:"nonpayable"},{type:"function",name:"finalizeWithdraw",inputs:[{name:"config",type:"tuple",components:B}],outputs:[],stateMutability:"nonpayable"},{type:"function",name:"pendingWithdrawals",inputs:[{name:"channelId",type:"bytes32"}],outputs:[{name:"amount",type:"uint128"},{name:"initiatedAt",type:"uint40"}],stateMutability:"view"},{type:"function",name:"channels",inputs:[{name:"channelId",type:"bytes32"}],outputs:[{name:"balance",type:"uint128"},{name:"totalClaimed",type:"uint128"}],stateMutability:"view"}];function G(t){if(Array.isArray(t))return{balance:BigInt(t[0]),totalClaimed:BigInt(t[1])};let e=t;return{balance:BigInt(e.balance),totalClaimed:BigInt(e.totalClaimed)}}function D(t){if(Array.isArray(t))return{amount:BigInt(t[0]),initiatedAt:Number(t[1])};let e=t;return{amount:BigInt(e.amount),initiatedAt:Number(e.initiatedAt)}}async function U(t){let{config:e,network:n,client:a,withdrawDelaySecs:i}=t,r=z(e,n),s=t.amountAtomic;s===void 0&&(s=G(await a.readContract({address:C,abi:b,functionName:"channels",args:[r]})).balance);let l=await a.writeContract({address:C,abi:b,functionName:"initiateWithdraw",args:[e,s]});await a.waitForTransactionReceipt({hash:l});let c=D(await a.readContract({address:C,abi:b,functionName:"pendingWithdrawals",args:[r]}));return{initiateTx:l,finalizableAt:F(c.initiatedAt,i)}}async function $(t){let{config:e,network:n,client:a,withdrawDelaySecs:i}=t,r=z(e,n),s=D(await a.readContract({address:C,abi:b,functionName:"pendingWithdrawals",args:[r]}));if(s.initiatedAt===0)throw new S(`no withdrawal is pending for channel ${r} \u2014 call forceWithdraw first`);let l=F(s.initiatedAt,i),c=Math.floor(Date.now()/1e3);if(c<l)throw new S(`withdrawal for channel ${r} is not finalizable until ${l} (unix seconds) \u2014 ${l-c}s remaining`);let o=await a.writeContract({address:C,abi:b,functionName:"finalizeWithdraw",args:[e]});return await a.waitForTransactionReceipt({hash:o}),{finalizeTx:o,withdrawnAmount:V(s.amount,X)}}var O={"eip155:8453":{chain:et,defaultRpc:"https://mainnet.base.org"},"eip155:42161":{chain:nt,defaultRpc:"https://arb1.arbitrum.io/rpc"},"eip155:137":{chain:at,defaultRpc:"https://polygon-rpc.com"}};function N(t){let e=O[t.network];if(!e)throw new y(`batch-settlement is not available on network "${t.network}" \u2014 supported: ${Object.keys(O).join(", ")}`);let n=t.rpcUrl??e.defaultRpc,a=t.wallet.signTypedData;if(typeof a!="function")throw new Error("batch-settlement requires an EvmWallet that supports signTypedData (EIP-712)");let i=K({account:{address:t.wallet.address,type:"json-rpc"},chain:e.chain,transport:Q(n)}).extend(Y),r=t.wallet.sendTransaction,s=typeof r=="function",l=async h=>{let w=h.to??void 0;if(!w)throw new Error("batch-settlement escape hatch: transaction is missing a `to` address");let u=h.data??"0x";if(typeof r=="function")return await r.call(t.wallet,{to:w,data:u,value:h.value});throw new Error("batch-settlement: wallet has no sendTransaction")},c=Object.assign(i,{address:t.wallet.address,signTypedData:h=>a.call(t.wallet,h),sendTransaction:l}),o=rt(c),m=new it(o,{storage:t.store,depositPolicy:{depositMultiplier:4},depositStrategy:()=>t.depositAtomic,salt:t.salt}),d=new st;d.register(t.network,m);let p=new lt(d);return{scheme:m,x402Cli:d,httpClient:p,rpcUrl:n,withdrawClient:c,canSubmitTransactions:s}}var W=6;function ct(t,e){let n=e>t?0n:t-e;return{deposited:A(t,W),spent:A(e,W),remaining:A(n,W)}}function dt(t){let e=t.payload;if(!e||typeof e!="object")return"";let n=e.voucher;if(!n||typeof n!="object")return"";let a=n.channelId;return typeof a=="string"?a:""}function ht(t){let e=t.payload;if(!e||typeof e!="object")return;let n=e.channelConfig;if(!(!n||typeof n!="object"))return n}function j(t){let{stack:e,store:n,network:a}=t,i="",r,s=t.depositedAtomic,l=0n;async function c(){if(!i)return;let o=await ot(n,i);o&&(o.chargedCumulativeAmount!==void 0&&(l=BigInt(o.chargedCumulativeAmount)),s===0n&&o.balance!==void 0&&(s=BigInt(o.balance)))}return{get channelId(){return i},get salt(){return t.salt},get network(){return a},get state(){return ct(s,l)},async fetch(o,m){let{httpClient:d}=e,p=await fetch(o,m);if(p.status!==402)return p;let v=d.getPaymentRequiredResponse(u=>p.headers.get(u),await p.clone().json().catch(()=>{})),h=2,w;for(let u=1;u<=h;u+=1){let f=await d.createPaymentPayload(v),I=dt(f);I&&(i=I);let T=ht(f);T&&(r=T);let H=d.encodePaymentSignatureHeader(f),g=await fetch(o,{...m,headers:{...m?.headers??{},...H}});w=g;let{recovered:L}=await d.processPaymentResult(f,M=>g.headers.get(M),g.status);if(await c(),!(g.status===402&&L&&u<h))return g}return w},async close(){return i&&await n.delete(i.toLowerCase()),{closed:!0}},async forceWithdraw(){if(!e.canSubmitTransactions)throw new Error("batch-settlement forceWithdraw requires a wallet with a sendTransaction method (the withdrawal escape hatch submits an on-chain transaction and the buyer pays gas; a signature-only wallet cannot use it).");if(!r)throw new Error("forceWithdraw is unavailable until the channel has resolved on-chain \u2014 make at least one fetch() against the channel first");return U({config:r,network:a,client:e.withdrawClient,withdrawDelaySecs:r.withdrawDelay})},async finalizeWithdraw(){if(!e.canSubmitTransactions)throw new Error("batch-settlement finalizeWithdraw requires a wallet with a sendTransaction method (the withdrawal escape hatch submits an on-chain transaction and the buyer pays gas; a signature-only wallet cannot use it).");if(!r)throw new Error("finalizeWithdraw is unavailable until the channel has resolved on-chain \u2014 make at least one fetch() against the channel first");return $({config:r,network:a,client:e.withdrawClient,withdrawDelaySecs:r.withdrawDelay})}}}async function ut(){let t=globalThis.crypto??(await import("crypto")).webcrypto,e=new Uint8Array(32);return t.getRandomValues(e),tt(e)}async function mt(t){let e=t.store??x(),n=t.salt??await ut(),a;try{a=Z(t.deposit,W)}catch{throw new Error(`deposit must be a valid USDC amount in decimal units (e.g. "0.30"), got "${t.deposit}"`)}if(a<=0n)throw new Error(`deposit must be a positive amount, got "${t.deposit}"`);let i=N({wallet:t.wallet,network:t.network,rpcUrl:t.rpcUrl,store:e,depositAtomic:a.toString(),salt:n});return j({stack:i,store:e,network:t.network,depositedAtomic:a,salt:n})}async function pt(t){let e=t.store??x(),n=N({wallet:t.wallet,network:t.network,rpcUrl:t.rpcUrl,store:e,depositAtomic:"0",salt:t.salt});return j({stack:n,store:e,network:t.network,depositedAtomic:0n,salt:t.salt})}export{k as InsufficientBalanceError,y as UnsupportedNetworkError,S as WithdrawNotReadyError,E as createFileChannelStore,P as createLocalStorageChannelStore,x as getDefaultChannelStore,mt as openBatchChannel,pt as resumeBatchChannel};
1
+ import{createWalletClient as K,http as Q,publicActions as Y,parseUnits as Z,formatUnits as A,toHex as tt}from"viem";import{base as et,arbitrum as nt,polygon as at}from"viem/chains";import{toClientEvmSigner as rt}from"@x402/evm";import{BatchSettlementEvmScheme as it,getChannel as ot}from"@x402/evm/batch-settlement/client";import{x402Client as st,x402HTTPClient as lt}from"@x402/core/client";import{homedir as q}from"os";import{join as _}from"path";import{FileClientChannelStorage as J}from"@x402/evm/batch-settlement/client";function E(t){return new J({directory:t})}var R="dexter-x402-channel:";function P(t){return{async get(e){let n=t.getItem(R+e);return n?JSON.parse(n):void 0},async set(e,n){t.setItem(R+e,JSON.stringify(n))},async delete(e){t.removeItem(R+e)}}}function x(){let t=globalThis.localStorage;return t?P(t):E(_(q(),".dexter-x402","channels"))}var k=class extends Error{constructor(e){super(e),this.name="InsufficientBalanceError"}},y=class extends Error{constructor(e){super(e),this.name="UnsupportedNetworkError"}};import{formatUnits as V}from"viem";import{computeChannelId as z}from"@x402/evm/batch-settlement/client";import{BATCH_SETTLEMENT_ADDRESS as C}from"@x402/evm";var S=class extends Error{constructor(e){super(e),this.name="WithdrawNotReadyError"}};function F(t,e){return t===0?0:t+e}var X=6,B=[{name:"payer",type:"address"},{name:"payerAuthorizer",type:"address"},{name:"receiver",type:"address"},{name:"receiverAuthorizer",type:"address"},{name:"token",type:"address"},{name:"withdrawDelay",type:"uint40"},{name:"salt",type:"bytes32"}],b=[{type:"function",name:"initiateWithdraw",inputs:[{name:"config",type:"tuple",components:B},{name:"amount",type:"uint128"}],outputs:[],stateMutability:"nonpayable"},{type:"function",name:"finalizeWithdraw",inputs:[{name:"config",type:"tuple",components:B}],outputs:[],stateMutability:"nonpayable"},{type:"function",name:"pendingWithdrawals",inputs:[{name:"channelId",type:"bytes32"}],outputs:[{name:"amount",type:"uint128"},{name:"initiatedAt",type:"uint40"}],stateMutability:"view"},{type:"function",name:"channels",inputs:[{name:"channelId",type:"bytes32"}],outputs:[{name:"balance",type:"uint128"},{name:"totalClaimed",type:"uint128"}],stateMutability:"view"}];function G(t){if(Array.isArray(t))return{balance:BigInt(t[0]),totalClaimed:BigInt(t[1])};let e=t;return{balance:BigInt(e.balance),totalClaimed:BigInt(e.totalClaimed)}}function D(t){if(Array.isArray(t))return{amount:BigInt(t[0]),initiatedAt:Number(t[1])};let e=t;return{amount:BigInt(e.amount),initiatedAt:Number(e.initiatedAt)}}async function U(t){let{config:e,network:n,client:a,withdrawDelaySecs:i}=t,r=z(e,n),s=t.amountAtomic;s===void 0&&(s=G(await a.readContract({address:C,abi:b,functionName:"channels",args:[r]})).balance);let l=await a.writeContract({address:C,abi:b,functionName:"initiateWithdraw",args:[e,s]});await a.waitForTransactionReceipt({hash:l});let c=D(await a.readContract({address:C,abi:b,functionName:"pendingWithdrawals",args:[r]}));return{initiateTx:l,finalizableAt:F(c.initiatedAt,i)}}async function $(t){let{config:e,network:n,client:a,withdrawDelaySecs:i}=t,r=z(e,n),s=D(await a.readContract({address:C,abi:b,functionName:"pendingWithdrawals",args:[r]}));if(s.initiatedAt===0)throw new S(`no withdrawal is pending for channel ${r} \u2014 call forceWithdraw first`);let l=F(s.initiatedAt,i),c=Math.floor(Date.now()/1e3);if(c<l)throw new S(`withdrawal for channel ${r} is not finalizable until ${l} (unix seconds) \u2014 ${l-c}s remaining`);let o=await a.writeContract({address:C,abi:b,functionName:"finalizeWithdraw",args:[e]});return await a.waitForTransactionReceipt({hash:o}),{finalizeTx:o,withdrawnAmount:V(s.amount,X)}}var O={"eip155:8453":{chain:et,defaultRpc:"https://api.dexter.cash/api/evm/base/rpc"},"eip155:42161":{chain:nt,defaultRpc:"https://api.dexter.cash/api/evm/arbitrum/rpc"},"eip155:137":{chain:at,defaultRpc:"https://api.dexter.cash/api/evm/polygon/rpc"}};function N(t){let e=O[t.network];if(!e)throw new y(`batch-settlement is not available on network "${t.network}" \u2014 supported: ${Object.keys(O).join(", ")}`);let n=t.rpcUrl??e.defaultRpc,a=t.wallet.signTypedData;if(typeof a!="function")throw new Error("batch-settlement requires an EvmWallet that supports signTypedData (EIP-712)");let i=K({account:{address:t.wallet.address,type:"json-rpc"},chain:e.chain,transport:Q(n)}).extend(Y),r=t.wallet.sendTransaction,s=typeof r=="function",l=async h=>{let w=h.to??void 0;if(!w)throw new Error("batch-settlement escape hatch: transaction is missing a `to` address");let u=h.data??"0x";if(typeof r=="function")return await r.call(t.wallet,{to:w,data:u,value:h.value});throw new Error("batch-settlement: wallet has no sendTransaction")},c=Object.assign(i,{address:t.wallet.address,signTypedData:h=>a.call(t.wallet,h),sendTransaction:l}),o=rt(c),p=new it(o,{storage:t.store,depositPolicy:{depositMultiplier:4},depositStrategy:()=>t.depositAtomic,salt:t.salt}),d=new st;d.register(t.network,p);let m=new lt(d);return{scheme:p,x402Cli:d,httpClient:m,rpcUrl:n,withdrawClient:c,canSubmitTransactions:s}}var W=6;function ct(t,e){let n=e>t?0n:t-e;return{deposited:A(t,W),spent:A(e,W),remaining:A(n,W)}}function dt(t){let e=t.payload;if(!e||typeof e!="object")return"";let n=e.voucher;if(!n||typeof n!="object")return"";let a=n.channelId;return typeof a=="string"?a:""}function ht(t){let e=t.payload;if(!e||typeof e!="object")return;let n=e.channelConfig;if(!(!n||typeof n!="object"))return n}function j(t){let{stack:e,store:n,network:a}=t,i="",r,s=t.depositedAtomic,l=0n;async function c(){if(!i)return;let o=await ot(n,i);o&&(o.chargedCumulativeAmount!==void 0&&(l=BigInt(o.chargedCumulativeAmount)),s===0n&&o.balance!==void 0&&(s=BigInt(o.balance)))}return{get channelId(){return i},get salt(){return t.salt},get network(){return a},get state(){return ct(s,l)},async fetch(o,p){let{httpClient:d}=e,m=await fetch(o,p);if(m.status!==402)return m;let v=d.getPaymentRequiredResponse(u=>m.headers.get(u),await m.clone().json().catch(()=>{})),h=2,w;for(let u=1;u<=h;u+=1){let f=await d.createPaymentPayload(v),I=dt(f);I&&(i=I);let T=ht(f);T&&(r=T);let H=d.encodePaymentSignatureHeader(f),g=await fetch(o,{...p,headers:{...p?.headers??{},...H}});w=g;let{recovered:L}=await d.processPaymentResult(f,M=>g.headers.get(M),g.status);if(await c(),!(g.status===402&&L&&u<h))return g}return w},async close(){return i&&await n.delete(i.toLowerCase()),{closed:!0}},async forceWithdraw(){if(!e.canSubmitTransactions)throw new Error("batch-settlement forceWithdraw requires a wallet with a sendTransaction method (the withdrawal escape hatch submits an on-chain transaction and the buyer pays gas; a signature-only wallet cannot use it).");if(!r)throw new Error("forceWithdraw is unavailable until the channel has resolved on-chain \u2014 make at least one fetch() against the channel first");return U({config:r,network:a,client:e.withdrawClient,withdrawDelaySecs:r.withdrawDelay})},async finalizeWithdraw(){if(!e.canSubmitTransactions)throw new Error("batch-settlement finalizeWithdraw requires a wallet with a sendTransaction method (the withdrawal escape hatch submits an on-chain transaction and the buyer pays gas; a signature-only wallet cannot use it).");if(!r)throw new Error("finalizeWithdraw is unavailable until the channel has resolved on-chain \u2014 make at least one fetch() against the channel first");return $({config:r,network:a,client:e.withdrawClient,withdrawDelaySecs:r.withdrawDelay})}}}async function ut(){let t=globalThis.crypto??(await import("crypto")).webcrypto,e=new Uint8Array(32);return t.getRandomValues(e),tt(e)}async function pt(t){let e=t.store??x(),n=t.salt??await ut(),a;try{a=Z(t.deposit,W)}catch{throw new Error(`deposit must be a valid USDC amount in decimal units (e.g. "0.30"), got "${t.deposit}"`)}if(a<=0n)throw new Error(`deposit must be a positive amount, got "${t.deposit}"`);let i=N({wallet:t.wallet,network:t.network,rpcUrl:t.rpcUrl,store:e,depositAtomic:a.toString(),salt:n});return j({stack:i,store:e,network:t.network,depositedAtomic:a,salt:n})}async function mt(t){let e=t.store??x(),n=N({wallet:t.wallet,network:t.network,rpcUrl:t.rpcUrl,store:e,depositAtomic:"0",salt:t.salt});return j({stack:n,store:e,network:t.network,depositedAtomic:0n,salt:t.salt})}export{k as InsufficientBalanceError,y as UnsupportedNetworkError,S as WithdrawNotReadyError,E as createFileChannelStore,P as createLocalStorageChannelStore,x as getDefaultChannelStore,pt as openBatchChannel,mt as resumeBatchChannel};
@@ -1 +1 @@
1
- "use strict";var z=Object.create;var h=Object.defineProperty;var F=Object.getOwnPropertyDescriptor;var G=Object.getOwnPropertyNames;var W=Object.getPrototypeOf,Y=Object.prototype.hasOwnProperty;var H=(e,t)=>{for(var n in t)h(e,n,{get:t[n],enumerable:!0})},x=(e,t,n,i)=>{if(t&&typeof t=="object"||typeof t=="function")for(let r of G(t))!Y.call(e,r)&&r!==n&&h(e,r,{get:()=>t[r],enumerable:!(i=F(t,r))||i.enumerable});return e};var v=(e,t,n)=>(n=e!=null?z(W(e)):{},x(t||!e||!e.__esModule?h(n,"default",{value:e,enumerable:!0}):n,e)),Z=e=>x(h({},"__esModule",{value:!0}),e);var me={};H(me,{createSolanaVaultAdapter:()=>ue,deriveChannelId:()=>N,passkeySignerFromP256Keypair:()=>ce});module.exports=Z(me);var m=require("@solana/web3.js");var l=require("@solana/web3.js"),g=new l.PublicKey("Hg3wRaydFtJhYrdvYrKECacpJYDsC9Px7yKmpncj2fhc"),q=new l.PublicKey("Secp256r1SigVerify1111111111111111111111111"),k=new l.PublicKey("Sysvar1nstructions1111111111111111111111111"),j=new Uint8Array([69,94,60,44,49,199,183,233]),X=new Uint8Array([81,192,32,110,104,116,144,151]);function Q(e){let t=new Uint8Array(8);return new DataView(t.buffer).setBigUint64(0,e,!0),t}function ee(e){let t=new Uint8Array(8);return new DataView(t.buffer).setBigInt64(0,e,!0),t}function te(e){let t=new Uint8Array(4);return new DataView(t.buffer).setUint32(0,e>>>0,!0),t}function A(e){let t=new Uint8Array(4+e.length);return new DataView(t.buffer).setUint32(0,e.length>>>0,!0),t.set(e,4),t}function I(...e){let t=e.reduce((r,s)=>r+s.length,0),n=new Uint8Array(t),i=0;for(let r of e)n.set(r,i),i+=r.length;return n}var b=64,w=33,ne=14,y=2;function S(e,t,n){if(e.length!==w)throw new Error(`expected ${w}-byte compressed pubkey`);if(t.length!==b)throw new Error(`expected ${b}-byte signature`);let i=y+ne,r=i+b,s=r+w,c=n.length,u=s+c,o=new Uint8Array(u);o[0]=1,o[1]=0;let a=new DataView(o.buffer);return a.setUint16(y+0,i,!0),a.setUint16(y+2,65535,!0),a.setUint16(y+4,r,!0),a.setUint16(y+6,65535,!0),a.setUint16(y+8,s,!0),a.setUint16(y+10,c,!0),a.setUint16(y+12,65535,!0),o.set(t,i),o.set(e,r),o.set(n,s),new l.TransactionInstruction({keys:[],programId:q,data:Buffer.from(o)})}function E(e){if(e.sessionPubkey.length!==32)throw new Error(`sessionPubkey must be 32 bytes, got ${e.sessionPubkey.length}`);let t=I(j,e.sessionPubkey,Q(e.maxAmount),ee(e.expiresAt),e.allowedCounterparty.toBytes(),te(e.nonce),A(e.clientDataJSON),A(e.authenticatorData));return new l.TransactionInstruction({keys:[{pubkey:e.vaultPda,isSigner:!1,isWritable:!0},{pubkey:k,isSigner:!1,isWritable:!1}],programId:g,data:Buffer.from(t)})}function O(e){let t=I(X,A(e.clientDataJSON),A(e.authenticatorData));return new l.TransactionInstruction({keys:[{pubkey:e.vaultPda,isSigner:!1,isWritable:!0},{pubkey:k,isSigner:!1,isWritable:!1}],programId:g,data:Buffer.from(t)})}var ie=(()=>{let e=new Uint8Array(32);return e.set(new TextEncoder().encode("OTS_SESSION_REGISTER_V1"),0),e})(),re=(()=>{let e=new Uint8Array(32);return e.set(new TextEncoder().encode("OTS_SESSION_REVOKE_V1"),0),e})();function D(e){if(e.sessionPubkey.length!==32)throw new Error(`sessionPubkey must be 32 bytes, got ${e.sessionPubkey.length}`);let t=new Uint8Array(180),n=new DataView(t.buffer),i=0;if(t.set(ie,i),i+=32,t.set(e.programId.toBytes(),i),i+=32,t.set(e.vaultPda.toBytes(),i),i+=32,t.set(e.sessionPubkey,i),i+=32,n.setBigUint64(i,e.maxAmount,!0),i+=8,n.setBigInt64(i,e.expiresAt,!0),i+=8,t.set(e.allowedCounterparty.toBytes(),i),i+=32,n.setUint32(i,e.nonce>>>0,!0),i+=4,i!==180)throw new Error(`internal: session register message wrong length ${i}, expected 180`);return t}function R(e){if(e.sessionPubkey.length!==32)throw new Error(`sessionPubkey must be 32 bytes, got ${e.sessionPubkey.length}`);let t=new Uint8Array(128),n=0;if(t.set(re,n),n+=32,t.set(e.programId.toBytes(),n),n+=32,t.set(e.vaultPda.toBytes(),n),n+=32,t.set(e.sessionPubkey,n),n+=32,n!==128)throw new Error(`internal: session revoke message wrong length ${n}, expected 128`);return t}function B(e){if(e.channelId.length!==32)throw new Error(`channelId must be 32 bytes, got ${e.channelId.length}`);let t=new Uint8Array(44),n=new DataView(t.buffer),i=0;if(t.set(e.channelId,i),i+=32,n.setBigUint64(i,e.cumulativeAmount,!0),i+=8,n.setUint32(i,e.sequenceNumber>>>0,!0),i+=4,i!==44)throw new Error(`internal: voucher payload wrong length ${i}, expected 44`);return t}var P=v(require("tweetnacl"),1);var C=require("@noble/hashes/sha256");function V(){let e=P.default.sign.keyPair();return{publicKey:e.publicKey,privateKey:e.secretKey}}function T(e,t,n){return{publicKey:e.publicKey,privateKey:e.privateKey,scope:t,registration:n}}function _(e,t,n){if(n.length!==32)throw new Error(`channelIdBytes must be 32 bytes, got ${n.length}`);let i=BigInt(t.cumulativeAmount),r=BigInt(e.scope.maxAmountAtomic);if(i>r)throw new Error(`voucher cumulative ${i} exceeds session cap ${r}`);let s=Math.floor(Date.now()/1e3);if(s>=e.scope.expiresAtUnix)throw new Error(`session expired at ${e.scope.expiresAtUnix}, now ${s}`);let c=B({channelId:n,cumulativeAmount:i,sequenceNumber:t.sequenceNumber}),u=P.default.sign.detached(c,e.privateKey);return{payload:t,sessionPublicKey:e.publicKey,sessionRegistration:e.registration,sessionSignature:u}}function U(e){if(!/^\d+$/.test(e))throw new Error(`atomic amount must be a non-negative integer string, got "${e}"`);return BigInt(e)}function N(e){let t=new Uint8Array(8);new DataView(t.buffer).setBigUint64(0,e.nonce,!0);let n=new TextEncoder().encode(e.sellerUrl),i=C.sha256.create();return i.update(e.vaultPda.toBytes()),i.update(n),i.update(t),i.digest()}var M=require("@noble/curves/p256"),d=require("@noble/hashes/sha256"),$="dexter.cash";function se(e){return Buffer.from(e).toString("base64").replace(/\+/g,"-").replace(/\//g,"_").replace(/=+$/,"")}function oe(e,t=`https://${$}`){let i={type:"webauthn.get",challenge:se(e),origin:t,crossOrigin:!1};return new TextEncoder().encode(JSON.stringify(i))}function ae(e=1){let t=(0,d.sha256)(new TextEncoder().encode($)),n=new Uint8Array(37);return n.set(t,0),n[32]=5,new DataView(n.buffer).setUint32(33,e,!1),n}function J(e,t){let n=(0,d.sha256)(t),i=oe(n),r=ae(1),s=new Uint8Array(r.length+32);s.set(r,0),s.set((0,d.sha256)(i),r.length);let c=(0,d.sha256)(s),o=M.p256.sign(c,e.privateKey,{lowS:!0}).toCompactRawBytes();return{clientDataJSON:i,authenticatorData:r,precompileMessage:s,signature:o}}function ce(e){return{publicKey:e.publicKey,signOperation:async t=>J(e,t)}}var K=class{network="solana:mainnet";swigAddress;vaultPda;connection;vaultPdaKey;passkey;feePayer;confirmOptions;constructor(t){this.connection=t.connection,this.swigAddress=typeof t.swigAddress=="string"?t.swigAddress:t.swigAddress.toBase58(),this.vaultPdaKey=typeof t.vaultPda=="string"?new m.PublicKey(t.vaultPda):t.vaultPda,this.vaultPda=this.vaultPdaKey.toBase58(),this.passkey=t.passkeySigner,this.feePayer=t.feePayer,this.confirmOptions=t.confirmOptions??{commitment:"confirmed"}}async authorizeSession(t){let n=new m.PublicKey(t.allowedCounterparty),i=V(),r=ye(),s=D({programId:g,vaultPda:this.vaultPdaKey,sessionPubkey:i.publicKey,maxAmount:U(t.maxAmountAtomic),expiresAt:BigInt(t.expiresAtUnix),allowedCounterparty:n,nonce:r}),c=await this.passkey.signOperation(s),u=S(this.passkey.publicKey,c.signature,c.precompileMessage),o=E({vaultPda:this.vaultPdaKey,sessionPubkey:i.publicKey,maxAmount:U(t.maxAmountAtomic),expiresAt:BigInt(t.expiresAtUnix),allowedCounterparty:n,nonce:r,clientDataJSON:c.clientDataJSON,authenticatorData:c.authenticatorData}),a=new m.Transaction().add(u,o);a.feePayer=this.feePayer.publicKey;let{blockhash:p}=await this.connection.getLatestBlockhash(this.confirmOptions.commitment);a.recentBlockhash=p,a.sign(this.feePayer);let f=await this.connection.sendRawTransaction(a.serialize(),{skipPreflight:!1,preflightCommitment:this.confirmOptions.preflightCommitment??this.confirmOptions.commitment});return await this.connection.confirmTransaction({signature:f,blockhash:p,lastValidBlockHeight:(await this.connection.getLatestBlockhash(this.confirmOptions.commitment)).lastValidBlockHeight},this.confirmOptions.commitment),await this.waitForActiveSessionFinalized(i.publicKey),T(i,t,s)}async signWithSession(t,n){let i=await le(n.channelId);return _(t,n,i)}async signOpenTab(t,n){return t.registration}async signCloseTab(t,n,i){let r=R({programId:g,vaultPda:this.vaultPdaKey,sessionPubkey:t.publicKey}),s=await this.passkey.signOperation(r),c=S(this.passkey.publicKey,s.signature,s.precompileMessage),u=O({vaultPda:this.vaultPdaKey,clientDataJSON:s.clientDataJSON,authenticatorData:s.authenticatorData}),o=new m.Transaction().add(c,u);o.feePayer=this.feePayer.publicKey;let{blockhash:a,lastValidBlockHeight:p}=await this.connection.getLatestBlockhash(this.confirmOptions.commitment);o.recentBlockhash=a,o.sign(this.feePayer);let f=await this.connection.sendRawTransaction(o.serialize(),{skipPreflight:!1,preflightCommitment:this.confirmOptions.preflightCommitment??this.confirmOptions.commitment});return await this.connection.confirmTransaction({signature:f,blockhash:a,lastValidBlockHeight:p},this.confirmOptions.commitment),r}async waitForActiveSessionFinalized(t,n=2e4){let i=Date.now()+n;for(;Date.now()<i;){let r=await this.connection.getAccountInfo(this.vaultPdaKey,"finalized");if(r){let s=r.data,p=84+(s[83]===1?48:0)+32+32;if(s[p]===1){let f=p+1,L=s.slice(f,f+32);if(pe(L,t))return}}await new Promise(s=>setTimeout(s,500))}throw new Error(`register_session_key did not become finalized-visible within ${n}ms`)}};function ue(e){return new K(e)}function ye(){return Math.floor(Math.random()*4294967295)>>>0}async function le(e){if(/^[0-9a-f]{64}$/i.test(e))return fe(e);let{sha256:t}=await import("@noble/hashes/sha256");return t(new TextEncoder().encode(e))}function pe(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 fe(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}0&&(module.exports={createSolanaVaultAdapter,deriveChannelId,passkeySignerFromP256Keypair});
1
+ "use strict";var D=Object.create;var f=Object.defineProperty;var B=Object.getOwnPropertyDescriptor;var T=Object.getOwnPropertyNames;var M=Object.getPrototypeOf,_=Object.prototype.hasOwnProperty;var E=(t,e)=>{for(var n in e)f(t,n,{get:e[n],enumerable:!0})},b=(t,e,n,i)=>{if(e&&typeof e=="object"||typeof e=="function")for(let r of T(e))!_.call(t,r)&&r!==n&&f(t,r,{get:()=>e[r],enumerable:!(i=B(e,r))||i.enumerable});return t};var w=(t,e,n)=>(n=t!=null?D(M(t)):{},b(e||!t||!t.__esModule?f(n,"default",{value:t,enumerable:!0}):n,t)),N=t=>b(f({},"__esModule",{value:!0}),t);var j={};E(j,{createSolanaVaultAdapter:()=>H,deriveChannelId:()=>O,passkeySignerFromP256Keypair:()=>F});module.exports=N(j);var d=require("@solana/web3.js");var m=require("@dexterai/vault/instructions"),A=require("@dexterai/vault/precompile"),g=require("@dexterai/vault/constants");var a=require("@dexterai/vault/messages");var P=w(require("tweetnacl"),1);var U=require("@noble/hashes/sha256");function v(){let t=P.default.sign.keyPair();return{publicKey:t.publicKey,privateKey:t.secretKey}}function x(t,e,n){return{publicKey:t.publicKey,privateKey:t.privateKey,scope:e,registration:n}}function k(t,e,n){if(n.length!==32)throw new Error(`channelIdBytes must be 32 bytes, got ${n.length}`);let i=BigInt(e.cumulativeAmount),r=BigInt(t.scope.maxAmountAtomic);if(i>r)throw new Error(`voucher cumulative ${i} exceeds session cap ${r}`);let s=Math.floor(Date.now()/1e3);if(s>=t.scope.expiresAtUnix)throw new Error(`session expired at ${t.scope.expiresAtUnix}, now ${s}`);let o=(0,a.voucherPayloadMessage)({channelId:n,cumulativeAmount:i,sequenceNumber:e.sequenceNumber}),y=P.default.sign.detached(o,t.privateKey);return{payload:e,sessionPublicKey:t.publicKey,sessionRegistration:t.registration,sessionSignature:y}}function S(t){if(!/^\d+$/.test(t))throw new Error(`atomic amount must be a non-negative integer string, got "${t}"`);return BigInt(t)}function O(t){let e=new Uint8Array(8);new DataView(e.buffer).setBigUint64(0,t.nonce,!0);let n=new TextEncoder().encode(t.sellerUrl),i=U.sha256.create();return i.update(t.vaultPda.toBytes()),i.update(n),i.update(e),i.digest()}var I=require("@noble/curves/p256"),h=require("@noble/hashes/sha256"),R="dexter.cash";function $(t){return Buffer.from(t).toString("base64").replace(/\+/g,"-").replace(/\//g,"_").replace(/=+$/,"")}function z(t,e=`https://${R}`){let i={type:"webauthn.get",challenge:$(t),origin:e,crossOrigin:!1};return new TextEncoder().encode(JSON.stringify(i))}function J(t=1){let e=(0,h.sha256)(new TextEncoder().encode(R)),n=new Uint8Array(37);return n.set(e,0),n[32]=5,new DataView(n.buffer).setUint32(33,t,!1),n}function V(t,e){let n=(0,h.sha256)(e),i=z(n),r=J(1),s=new Uint8Array(r.length+32);s.set(r,0),s.set((0,h.sha256)(i),r.length);let o=(0,h.sha256)(s),c=I.p256.sign(o,t.privateKey,{lowS:!0}).toCompactRawBytes();return{clientDataJSON:i,authenticatorData:r,precompileMessage:s,signature:c}}function F(t){return{publicKey:t.publicKey,signOperation:async e=>V(t,e)}}var K=class{network="solana:mainnet";swigAddress;vaultPda;connection;vaultPdaKey;passkey;feePayer;confirmOptions;constructor(e){this.connection=e.connection,this.swigAddress=typeof e.swigAddress=="string"?e.swigAddress:e.swigAddress.toBase58(),this.vaultPdaKey=typeof e.vaultPda=="string"?new d.PublicKey(e.vaultPda):e.vaultPda,this.vaultPda=this.vaultPdaKey.toBase58(),this.passkey=e.passkeySigner,this.feePayer=e.feePayer,this.confirmOptions=e.confirmOptions??{commitment:"confirmed"}}async authorizeSession(e){let n=new d.PublicKey(e.allowedCounterparty),i=v(),r=L(),s=(0,a.sessionRegisterMessage)({programId:g.DEXTER_VAULT_PROGRAM_ID,vaultPda:this.vaultPdaKey,sessionPubkey:i.publicKey,maxAmount:S(e.maxAmountAtomic),expiresAt:BigInt(e.expiresAtUnix),allowedCounterparty:n,nonce:r}),o=await this.passkey.signOperation(s),y=(0,A.buildSecp256r1VerifyInstruction)(this.passkey.publicKey,o.signature,o.precompileMessage),c=(0,m.buildRegisterSessionKeyInstruction)({vaultPda:this.vaultPdaKey,sessionPubkey:i.publicKey,maxAmount:S(e.maxAmountAtomic),expiresAt:BigInt(e.expiresAtUnix),allowedCounterparty:n,nonce:r,clientDataJSON:o.clientDataJSON,authenticatorData:o.authenticatorData}),u=new d.Transaction().add(y,c);u.feePayer=this.feePayer.publicKey;let{blockhash:l}=await this.connection.getLatestBlockhash(this.confirmOptions.commitment);u.recentBlockhash=l,u.sign(this.feePayer);let p=await this.connection.sendRawTransaction(u.serialize(),{skipPreflight:!1,preflightCommitment:this.confirmOptions.preflightCommitment??this.confirmOptions.commitment});return await this.connection.confirmTransaction({signature:p,blockhash:l,lastValidBlockHeight:(await this.connection.getLatestBlockhash(this.confirmOptions.commitment)).lastValidBlockHeight},this.confirmOptions.commitment),await this.waitForActiveSessionFinalized(i.publicKey),x(i,e,s)}async signWithSession(e,n){let i=await q(n.channelId);return k(e,n,i)}async signOpenTab(e,n){return e.registration}async signCloseTab(e,n,i){let r=(0,a.sessionRevokeMessage)({programId:g.DEXTER_VAULT_PROGRAM_ID,vaultPda:this.vaultPdaKey,sessionPubkey:e.publicKey}),s=await this.passkey.signOperation(r),o=(0,A.buildSecp256r1VerifyInstruction)(this.passkey.publicKey,s.signature,s.precompileMessage),y=(0,m.buildRevokeSessionKeyInstruction)({vaultPda:this.vaultPdaKey,clientDataJSON:s.clientDataJSON,authenticatorData:s.authenticatorData}),c=new d.Transaction().add(o,y);c.feePayer=this.feePayer.publicKey;let{blockhash:u,lastValidBlockHeight:l}=await this.connection.getLatestBlockhash(this.confirmOptions.commitment);c.recentBlockhash=u,c.sign(this.feePayer);let p=await this.connection.sendRawTransaction(c.serialize(),{skipPreflight:!1,preflightCommitment:this.confirmOptions.preflightCommitment??this.confirmOptions.commitment});return await this.connection.confirmTransaction({signature:p,blockhash:u,lastValidBlockHeight:l},this.confirmOptions.commitment),r}async waitForActiveSessionFinalized(e,n=2e4){let i=Date.now()+n;for(;Date.now()<i;){let r=await this.connection.getAccountInfo(this.vaultPdaKey,"finalized");if(r){let s=r.data,l=84+(s[83]===1?48:0)+32+32;if(s[l]===1){let p=l+1,C=s.slice(p,p+32);if(G(C,e))return}}await new Promise(s=>setTimeout(s,500))}throw new Error(`register_session_key did not become finalized-visible within ${n}ms`)}};function H(t){return new K(t)}function L(){return Math.floor(Math.random()*4294967295)>>>0}async function q(t){if(/^[0-9a-f]{64}$/i.test(t))return W(t);let{sha256:e}=await import("@noble/hashes/sha256");return e(new TextEncoder().encode(t))}function G(t,e){if(t.length!==e.length)return!1;for(let n=0;n<t.length;n++)if(t[n]!==e[n])return!1;return!0}function W(t){let e=new Uint8Array(t.length/2);for(let n=0;n<e.length;n++)e[n]=parseInt(t.substr(n*2,2),16);return e}0&&(module.exports={createSolanaVaultAdapter,deriveChannelId,passkeySignerFromP256Keypair});
@@ -1,5 +1,6 @@
1
1
  import { PublicKey, Connection, Signer, ConfirmOptions } from '@solana/web3.js';
2
- import { f as VaultAdapter } from '../../../types-DIrmhiD-.cjs';
2
+ import { V as VaultAdapter } from '../../../types-BtibqM00.cjs';
3
+ import '@dexterai/vault/types';
3
4
 
4
5
  /**
5
6
  * Session-key lifecycle: generation, voucher signing, in-memory hygiene.
@@ -78,10 +79,11 @@ interface SignedPasskeyPayload {
78
79
  * at close.
79
80
  *
80
81
  * The adapter does NOT touch pending_voucher_count. That counter belongs
81
- * to the facilitator's dexter_authority and is moved via settle_voucher
82
- * during seller settlement. The SDK's `Tab.close()` will hand a cumulative
83
- * voucher to the facilitator in Phase 3; Phase 2 stops at the session
84
- * register/revoke layer.
82
+ * to the facilitator's dexter_authority and is decremented inside the
83
+ * facilitator's `POST /tab/settle` tx (via the new vault.settle_tab_voucher
84
+ * instruction) atomically with the USDC transfer. The SDK's `Tab.close()`
85
+ * POSTs the final voucher to the facilitator; this adapter only owns the
86
+ * passkey-signed session register/revoke layer.
85
87
  */
86
88
 
87
89
  interface PasskeySigner {
@@ -1,5 +1,6 @@
1
1
  import { PublicKey, Connection, Signer, ConfirmOptions } from '@solana/web3.js';
2
- import { f as VaultAdapter } from '../../../types-DIrmhiD-.js';
2
+ import { V as VaultAdapter } from '../../../types-BtibqM00.js';
3
+ import '@dexterai/vault/types';
3
4
 
4
5
  /**
5
6
  * Session-key lifecycle: generation, voucher signing, in-memory hygiene.
@@ -78,10 +79,11 @@ interface SignedPasskeyPayload {
78
79
  * at close.
79
80
  *
80
81
  * The adapter does NOT touch pending_voucher_count. That counter belongs
81
- * to the facilitator's dexter_authority and is moved via settle_voucher
82
- * during seller settlement. The SDK's `Tab.close()` will hand a cumulative
83
- * voucher to the facilitator in Phase 3; Phase 2 stops at the session
84
- * register/revoke layer.
82
+ * to the facilitator's dexter_authority and is decremented inside the
83
+ * facilitator's `POST /tab/settle` tx (via the new vault.settle_tab_voucher
84
+ * instruction) atomically with the USDC transfer. The SDK's `Tab.close()`
85
+ * POSTs the final voucher to the facilitator; this adapter only owns the
86
+ * passkey-signed session register/revoke layer.
85
87
  */
86
88
 
87
89
  interface PasskeySigner {
@@ -1 +1 @@
1
- import{PublicKey as _,Transaction as C}from"@solana/web3.js";import{PublicKey as A,TransactionInstruction as b}from"@solana/web3.js";var f=new A("Hg3wRaydFtJhYrdvYrKECacpJYDsC9Px7yKmpncj2fhc"),M=new A("Secp256r1SigVerify1111111111111111111111111"),U=new A("Sysvar1nstructions1111111111111111111111111"),$=new Uint8Array([69,94,60,44,49,199,183,233]),J=new Uint8Array([81,192,32,110,104,116,144,151]);function L(e){let t=new Uint8Array(8);return new DataView(t.buffer).setBigUint64(0,e,!0),t}function z(e){let t=new Uint8Array(8);return new DataView(t.buffer).setBigInt64(0,e,!0),t}function F(e){let t=new Uint8Array(4);return new DataView(t.buffer).setUint32(0,e>>>0,!0),t}function m(e){let t=new Uint8Array(4+e.length);return new DataView(t.buffer).setUint32(0,e.length>>>0,!0),t.set(e,4),t}function K(...e){let t=e.reduce((s,r)=>s+r.length,0),n=new Uint8Array(t),i=0;for(let s of e)n.set(s,i),i+=s.length;return n}var d=64,h=33,G=14,y=2;function w(e,t,n){if(e.length!==h)throw new Error(`expected ${h}-byte compressed pubkey`);if(t.length!==d)throw new Error(`expected ${d}-byte signature`);let i=y+G,s=i+d,r=s+h,c=n.length,u=r+c,o=new Uint8Array(u);o[0]=1,o[1]=0;let a=new DataView(o.buffer);return a.setUint16(y+0,i,!0),a.setUint16(y+2,65535,!0),a.setUint16(y+4,s,!0),a.setUint16(y+6,65535,!0),a.setUint16(y+8,r,!0),a.setUint16(y+10,c,!0),a.setUint16(y+12,65535,!0),o.set(t,i),o.set(e,s),o.set(n,r),new b({keys:[],programId:M,data:Buffer.from(o)})}function x(e){if(e.sessionPubkey.length!==32)throw new Error(`sessionPubkey must be 32 bytes, got ${e.sessionPubkey.length}`);let t=K($,e.sessionPubkey,L(e.maxAmount),z(e.expiresAt),e.allowedCounterparty.toBytes(),F(e.nonce),m(e.clientDataJSON),m(e.authenticatorData));return new b({keys:[{pubkey:e.vaultPda,isSigner:!1,isWritable:!0},{pubkey:U,isSigner:!1,isWritable:!1}],programId:f,data:Buffer.from(t)})}function v(e){let t=K(J,m(e.clientDataJSON),m(e.authenticatorData));return new b({keys:[{pubkey:e.vaultPda,isSigner:!1,isWritable:!0},{pubkey:U,isSigner:!1,isWritable:!1}],programId:f,data:Buffer.from(t)})}var W=(()=>{let e=new Uint8Array(32);return e.set(new TextEncoder().encode("OTS_SESSION_REGISTER_V1"),0),e})(),Y=(()=>{let e=new Uint8Array(32);return e.set(new TextEncoder().encode("OTS_SESSION_REVOKE_V1"),0),e})();function k(e){if(e.sessionPubkey.length!==32)throw new Error(`sessionPubkey must be 32 bytes, got ${e.sessionPubkey.length}`);let t=new Uint8Array(180),n=new DataView(t.buffer),i=0;if(t.set(W,i),i+=32,t.set(e.programId.toBytes(),i),i+=32,t.set(e.vaultPda.toBytes(),i),i+=32,t.set(e.sessionPubkey,i),i+=32,n.setBigUint64(i,e.maxAmount,!0),i+=8,n.setBigInt64(i,e.expiresAt,!0),i+=8,t.set(e.allowedCounterparty.toBytes(),i),i+=32,n.setUint32(i,e.nonce>>>0,!0),i+=4,i!==180)throw new Error(`internal: session register message wrong length ${i}, expected 180`);return t}function I(e){if(e.sessionPubkey.length!==32)throw new Error(`sessionPubkey must be 32 bytes, got ${e.sessionPubkey.length}`);let t=new Uint8Array(128),n=0;if(t.set(Y,n),n+=32,t.set(e.programId.toBytes(),n),n+=32,t.set(e.vaultPda.toBytes(),n),n+=32,t.set(e.sessionPubkey,n),n+=32,n!==128)throw new Error(`internal: session revoke message wrong length ${n}, expected 128`);return t}function E(e){if(e.channelId.length!==32)throw new Error(`channelId must be 32 bytes, got ${e.channelId.length}`);let t=new Uint8Array(44),n=new DataView(t.buffer),i=0;if(t.set(e.channelId,i),i+=32,n.setBigUint64(i,e.cumulativeAmount,!0),i+=8,n.setUint32(i,e.sequenceNumber>>>0,!0),i+=4,i!==44)throw new Error(`internal: voucher payload wrong length ${i}, expected 44`);return t}import O from"tweetnacl";import{sha256 as H}from"@noble/hashes/sha256";function D(){let e=O.sign.keyPair();return{publicKey:e.publicKey,privateKey:e.secretKey}}function R(e,t,n){return{publicKey:e.publicKey,privateKey:e.privateKey,scope:t,registration:n}}function B(e,t,n){if(n.length!==32)throw new Error(`channelIdBytes must be 32 bytes, got ${n.length}`);let i=BigInt(t.cumulativeAmount),s=BigInt(e.scope.maxAmountAtomic);if(i>s)throw new Error(`voucher cumulative ${i} exceeds session cap ${s}`);let r=Math.floor(Date.now()/1e3);if(r>=e.scope.expiresAtUnix)throw new Error(`session expired at ${e.scope.expiresAtUnix}, now ${r}`);let c=E({channelId:n,cumulativeAmount:i,sequenceNumber:t.sequenceNumber}),u=O.sign.detached(c,e.privateKey);return{payload:t,sessionPublicKey:e.publicKey,sessionRegistration:e.registration,sessionSignature:u}}function S(e){if(!/^\d+$/.test(e))throw new Error(`atomic amount must be a non-negative integer string, got "${e}"`);return BigInt(e)}function Z(e){let t=new Uint8Array(8);new DataView(t.buffer).setBigUint64(0,e.nonce,!0);let n=new TextEncoder().encode(e.sellerUrl),i=H.create();return i.update(e.vaultPda.toBytes()),i.update(n),i.update(t),i.digest()}import{p256 as q}from"@noble/curves/p256";import{sha256 as g}from"@noble/hashes/sha256";var V="dexter.cash";function j(e){return Buffer.from(e).toString("base64").replace(/\+/g,"-").replace(/\//g,"_").replace(/=+$/,"")}function X(e,t=`https://${V}`){let i={type:"webauthn.get",challenge:j(e),origin:t,crossOrigin:!1};return new TextEncoder().encode(JSON.stringify(i))}function Q(e=1){let t=g(new TextEncoder().encode(V)),n=new Uint8Array(37);return n.set(t,0),n[32]=5,new DataView(n.buffer).setUint32(33,e,!1),n}function T(e,t){let n=g(t),i=X(n),s=Q(1),r=new Uint8Array(s.length+32);r.set(s,0),r.set(g(i),s.length);let c=g(r),o=q.sign(c,e.privateKey,{lowS:!0}).toCompactRawBytes();return{clientDataJSON:i,authenticatorData:s,precompileMessage:r,signature:o}}function we(e){return{publicKey:e.publicKey,signOperation:async t=>T(e,t)}}var P=class{network="solana:mainnet";swigAddress;vaultPda;connection;vaultPdaKey;passkey;feePayer;confirmOptions;constructor(t){this.connection=t.connection,this.swigAddress=typeof t.swigAddress=="string"?t.swigAddress:t.swigAddress.toBase58(),this.vaultPdaKey=typeof t.vaultPda=="string"?new _(t.vaultPda):t.vaultPda,this.vaultPda=this.vaultPdaKey.toBase58(),this.passkey=t.passkeySigner,this.feePayer=t.feePayer,this.confirmOptions=t.confirmOptions??{commitment:"confirmed"}}async authorizeSession(t){let n=new _(t.allowedCounterparty),i=D(),s=ee(),r=k({programId:f,vaultPda:this.vaultPdaKey,sessionPubkey:i.publicKey,maxAmount:S(t.maxAmountAtomic),expiresAt:BigInt(t.expiresAtUnix),allowedCounterparty:n,nonce:s}),c=await this.passkey.signOperation(r),u=w(this.passkey.publicKey,c.signature,c.precompileMessage),o=x({vaultPda:this.vaultPdaKey,sessionPubkey:i.publicKey,maxAmount:S(t.maxAmountAtomic),expiresAt:BigInt(t.expiresAtUnix),allowedCounterparty:n,nonce:s,clientDataJSON:c.clientDataJSON,authenticatorData:c.authenticatorData}),a=new C().add(u,o);a.feePayer=this.feePayer.publicKey;let{blockhash:l}=await this.connection.getLatestBlockhash(this.confirmOptions.commitment);a.recentBlockhash=l,a.sign(this.feePayer);let p=await this.connection.sendRawTransaction(a.serialize(),{skipPreflight:!1,preflightCommitment:this.confirmOptions.preflightCommitment??this.confirmOptions.commitment});return await this.connection.confirmTransaction({signature:p,blockhash:l,lastValidBlockHeight:(await this.connection.getLatestBlockhash(this.confirmOptions.commitment)).lastValidBlockHeight},this.confirmOptions.commitment),await this.waitForActiveSessionFinalized(i.publicKey),R(i,t,r)}async signWithSession(t,n){let i=await te(n.channelId);return B(t,n,i)}async signOpenTab(t,n){return t.registration}async signCloseTab(t,n,i){let s=I({programId:f,vaultPda:this.vaultPdaKey,sessionPubkey:t.publicKey}),r=await this.passkey.signOperation(s),c=w(this.passkey.publicKey,r.signature,r.precompileMessage),u=v({vaultPda:this.vaultPdaKey,clientDataJSON:r.clientDataJSON,authenticatorData:r.authenticatorData}),o=new C().add(c,u);o.feePayer=this.feePayer.publicKey;let{blockhash:a,lastValidBlockHeight:l}=await this.connection.getLatestBlockhash(this.confirmOptions.commitment);o.recentBlockhash=a,o.sign(this.feePayer);let p=await this.connection.sendRawTransaction(o.serialize(),{skipPreflight:!1,preflightCommitment:this.confirmOptions.preflightCommitment??this.confirmOptions.commitment});return await this.connection.confirmTransaction({signature:p,blockhash:a,lastValidBlockHeight:l},this.confirmOptions.commitment),s}async waitForActiveSessionFinalized(t,n=2e4){let i=Date.now()+n;for(;Date.now()<i;){let s=await this.connection.getAccountInfo(this.vaultPdaKey,"finalized");if(s){let r=s.data,l=84+(r[83]===1?48:0)+32+32;if(r[l]===1){let p=l+1,N=r.slice(p,p+32);if(ne(N,t))return}}await new Promise(r=>setTimeout(r,500))}throw new Error(`register_session_key did not become finalized-visible within ${n}ms`)}};function Se(e){return new P(e)}function ee(){return Math.floor(Math.random()*4294967295)>>>0}async function te(e){if(/^[0-9a-f]{64}$/i.test(e))return ie(e);let{sha256:t}=await import("@noble/hashes/sha256");return t(new TextEncoder().encode(e))}function ne(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 ie(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}export{Se as createSolanaVaultAdapter,Z as deriveChannelId,we as passkeySignerFromP256Keypair};
1
+ import{PublicKey as O,Transaction as I}from"@solana/web3.js";import{buildRegisterSessionKeyInstruction as f,buildRevokeSessionKeyInstruction as A}from"@dexterai/vault/instructions";import{buildSecp256r1VerifyInstruction as m}from"@dexterai/vault/precompile";import{DEXTER_VAULT_PROGRAM_ID as g,SECP256R1_PROGRAM_ID as L,INSTRUCTIONS_SYSVAR_ID as q}from"@dexterai/vault/constants";import{sessionRegisterMessage as P,sessionRevokeMessage as S,voucherPayloadMessage as K,buildVoucherMessage as j}from"@dexterai/vault/messages";import b from"tweetnacl";import{sha256 as V}from"@noble/hashes/sha256";function w(){let t=b.sign.keyPair();return{publicKey:t.publicKey,privateKey:t.secretKey}}function v(t,e,n){return{publicKey:t.publicKey,privateKey:t.privateKey,scope:e,registration:n}}function x(t,e,n){if(n.length!==32)throw new Error(`channelIdBytes must be 32 bytes, got ${n.length}`);let i=BigInt(e.cumulativeAmount),s=BigInt(t.scope.maxAmountAtomic);if(i>s)throw new Error(`voucher cumulative ${i} exceeds session cap ${s}`);let r=Math.floor(Date.now()/1e3);if(r>=t.scope.expiresAtUnix)throw new Error(`session expired at ${t.scope.expiresAtUnix}, now ${r}`);let o=K({channelId:n,cumulativeAmount:i,sequenceNumber:e.sequenceNumber}),u=b.sign.detached(o,t.privateKey);return{payload:e,sessionPublicKey:t.publicKey,sessionRegistration:t.registration,sessionSignature:u}}function d(t){if(!/^\d+$/.test(t))throw new Error(`atomic amount must be a non-negative integer string, got "${t}"`);return BigInt(t)}function C(t){let e=new Uint8Array(8);new DataView(e.buffer).setBigUint64(0,t.nonce,!0);let n=new TextEncoder().encode(t.sellerUrl),i=V.create();return i.update(t.vaultPda.toBytes()),i.update(n),i.update(e),i.digest()}import{p256 as D}from"@noble/curves/p256";import{sha256 as p}from"@noble/hashes/sha256";var k="dexter.cash";function B(t){return Buffer.from(t).toString("base64").replace(/\+/g,"-").replace(/\//g,"_").replace(/=+$/,"")}function T(t,e=`https://${k}`){let i={type:"webauthn.get",challenge:B(t),origin:e,crossOrigin:!1};return new TextEncoder().encode(JSON.stringify(i))}function M(t=1){let e=p(new TextEncoder().encode(k)),n=new Uint8Array(37);return n.set(e,0),n[32]=5,new DataView(n.buffer).setUint32(33,t,!1),n}function U(t,e){let n=p(e),i=T(n),s=M(1),r=new Uint8Array(s.length+32);r.set(s,0),r.set(p(i),s.length);let o=p(r),a=D.sign(o,t.privateKey,{lowS:!0}).toCompactRawBytes();return{clientDataJSON:i,authenticatorData:s,precompileMessage:r,signature:a}}function ue(t){return{publicKey:t.publicKey,signOperation:async e=>U(t,e)}}var h=class{network="solana:mainnet";swigAddress;vaultPda;connection;vaultPdaKey;passkey;feePayer;confirmOptions;constructor(e){this.connection=e.connection,this.swigAddress=typeof e.swigAddress=="string"?e.swigAddress:e.swigAddress.toBase58(),this.vaultPdaKey=typeof e.vaultPda=="string"?new O(e.vaultPda):e.vaultPda,this.vaultPda=this.vaultPdaKey.toBase58(),this.passkey=e.passkeySigner,this.feePayer=e.feePayer,this.confirmOptions=e.confirmOptions??{commitment:"confirmed"}}async authorizeSession(e){let n=new O(e.allowedCounterparty),i=w(),s=_(),r=P({programId:g,vaultPda:this.vaultPdaKey,sessionPubkey:i.publicKey,maxAmount:d(e.maxAmountAtomic),expiresAt:BigInt(e.expiresAtUnix),allowedCounterparty:n,nonce:s}),o=await this.passkey.signOperation(r),u=m(this.passkey.publicKey,o.signature,o.precompileMessage),a=f({vaultPda:this.vaultPdaKey,sessionPubkey:i.publicKey,maxAmount:d(e.maxAmountAtomic),expiresAt:BigInt(e.expiresAtUnix),allowedCounterparty:n,nonce:s,clientDataJSON:o.clientDataJSON,authenticatorData:o.authenticatorData}),c=new I().add(u,a);c.feePayer=this.feePayer.publicKey;let{blockhash:y}=await this.connection.getLatestBlockhash(this.confirmOptions.commitment);c.recentBlockhash=y,c.sign(this.feePayer);let l=await this.connection.sendRawTransaction(c.serialize(),{skipPreflight:!1,preflightCommitment:this.confirmOptions.preflightCommitment??this.confirmOptions.commitment});return await this.connection.confirmTransaction({signature:l,blockhash:y,lastValidBlockHeight:(await this.connection.getLatestBlockhash(this.confirmOptions.commitment)).lastValidBlockHeight},this.confirmOptions.commitment),await this.waitForActiveSessionFinalized(i.publicKey),v(i,e,r)}async signWithSession(e,n){let i=await E(n.channelId);return x(e,n,i)}async signOpenTab(e,n){return e.registration}async signCloseTab(e,n,i){let s=S({programId:g,vaultPda:this.vaultPdaKey,sessionPubkey:e.publicKey}),r=await this.passkey.signOperation(s),o=m(this.passkey.publicKey,r.signature,r.precompileMessage),u=A({vaultPda:this.vaultPdaKey,clientDataJSON:r.clientDataJSON,authenticatorData:r.authenticatorData}),a=new I().add(o,u);a.feePayer=this.feePayer.publicKey;let{blockhash:c,lastValidBlockHeight:y}=await this.connection.getLatestBlockhash(this.confirmOptions.commitment);a.recentBlockhash=c,a.sign(this.feePayer);let l=await this.connection.sendRawTransaction(a.serialize(),{skipPreflight:!1,preflightCommitment:this.confirmOptions.preflightCommitment??this.confirmOptions.commitment});return await this.connection.confirmTransaction({signature:l,blockhash:c,lastValidBlockHeight:y},this.confirmOptions.commitment),s}async waitForActiveSessionFinalized(e,n=2e4){let i=Date.now()+n;for(;Date.now()<i;){let s=await this.connection.getAccountInfo(this.vaultPdaKey,"finalized");if(s){let r=s.data,y=84+(r[83]===1?48:0)+32+32;if(r[y]===1){let l=y+1,R=r.slice(l,l+32);if(N(R,e))return}}await new Promise(r=>setTimeout(r,500))}throw new Error(`register_session_key did not become finalized-visible within ${n}ms`)}};function ye(t){return new h(t)}function _(){return Math.floor(Math.random()*4294967295)>>>0}async function E(t){if(/^[0-9a-f]{64}$/i.test(t))return $(t);let{sha256:e}=await import("@noble/hashes/sha256");return e(new TextEncoder().encode(t))}function N(t,e){if(t.length!==e.length)return!1;for(let n=0;n<t.length;n++)if(t[n]!==e[n])return!1;return!0}function $(t){let e=new Uint8Array(t.length/2);for(let n=0;n<e.length;n++)e[n]=parseInt(t.substr(n*2,2),16);return e}export{ye as createSolanaVaultAdapter,C as deriveChannelId,ue as passkeySignerFromP256Keypair};
@@ -1,6 +1,6 @@
1
- "use strict";var P=Object.create;var y=Object.defineProperty;var k=Object.getOwnPropertyDescriptor;var E=Object.getOwnPropertyNames;var K=Object.getPrototypeOf,C=Object.prototype.hasOwnProperty;var $=(e,t)=>{for(var n in t)y(e,n,{get:t[n],enumerable:!0})},f=(e,t,n,r)=>{if(t&&typeof t=="object"||typeof t=="function")for(let o of E(t))!C.call(e,o)&&o!==n&&y(e,o,{get:()=>t[o],enumerable:!(r=k(t,o))||r.enumerable});return e};var R=(e,t,n)=>(n=e!=null?P(K(e)):{},f(t||!e||!e.__esModule?y(n,"default",{value:e,enumerable:!0}):n,e)),N=e=>f(y({},"__esModule",{value:!0}),e);var D={};$(D,{DEFAULT_FACILITATOR_URL:()=>w,SessionScopeExceededError:()=>u,TabClosedError:()=>a,UnsupportedNetworkError:()=>c,atomicToHuman:()=>p,humanToAtomic:()=>h,openTab:()=>U,resumeTab:()=>T});module.exports=N(D);var c=class extends Error{constructor(n){super(`Network ${n} is not yet supported by @dexterai/x402/tab`);this.network=n;this.name="UnsupportedNetworkError"}},u=class extends Error{constructor(n,r){super(`Session scope exceeded: ${n}${r?` (${r})`:""}`);this.reason=n;this.name="SessionScopeExceededError"}},a=class extends Error{constructor(n){super(`Tab ${n} is already closed`);this.channelId=n;this.name="TabClosedError"}};var A=require("@solana/web3.js"),d=require("@noble/hashes/utils");var V=R(require("tweetnacl"),1);var L=(()=>{let e=new Uint8Array(32);return e.set(new TextEncoder().encode("OTS_SESSION_REGISTER_V1"),0),e})(),F=(()=>{let e=new Uint8Array(32);return e.set(new TextEncoder().encode("OTS_SESSION_REVOKE_V1"),0),e})();var x=require("@noble/hashes/sha256");function S(e){let t=new Uint8Array(8);new DataView(t.buffer).setBigUint64(0,e.nonce,!0);let n=new TextEncoder().encode(e.sellerUrl),r=x.sha256.create();return r.update(e.vaultPda.toBytes()),r.update(n),r.update(t),r.digest()}var O=3600,w="https://x402.dexter.cash",v=6;function h(e,t=v){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 o=r.padEnd(t,"0"),s=`${n}${o}`.replace(/^0+(?=\d)/,"");return s===""?"0":s}function p(e,t=v){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",o=n.slice(-t).replace(/0+$/,"");return o?`${r}.${o}`:r}var g=class{channelId;network;internals;cumulativeAtomic=0n;sequenceNumber=0;closed=!1;constructor(t){this.internals=t,this.channelId=t.channelIdHex,this.network=t.network}get state(){let t=this.internals.totalCapAtomic-this.cumulativeAtomic,n=Math.floor(Date.now()/1e3);return{isOpen:!this.closed,spent:p(this.cumulativeAtomic.toString()),remaining:p(t.toString()),expiresInSec:Math.max(0,this.internals.expiresAtUnix-n)}}async signNextVoucher(t){if(this.closed)throw new a(this.channelId);let n=BigInt(t);if(n<=0n)throw new Error(`voucher increment must be > 0, got ${t}`);if(n>this.internals.perUnitCapAtomic)throw new u("cap_exceeded",`single voucher increment ${n} exceeds perUnitCap ${this.internals.perUnitCapAtomic}`);let r=this.cumulativeAtomic+n;if(r>this.internals.totalCapAtomic)throw new u("cap_exceeded",`cumulative ${r} would exceed totalCap ${this.internals.totalCapAtomic}`);this.sequenceNumber+=1,this.cumulativeAtomic=r;let o={channelId:this.channelId,cumulativeAmount:this.cumulativeAtomic.toString(),sequenceNumber:this.sequenceNumber};return await this.internals.vault.signWithSession(this.internals.session,o)}async stream(t,n){if(this.closed)throw new a(this.channelId);let r=await this.signNextVoucher(this.internals.perUnitCapAtomic.toString()),o=Buffer.from(JSON.stringify({payload:r.payload,sessionPublicKey:(0,d.bytesToHex)(r.sessionPublicKey),sessionRegistration:(0,d.bytesToHex)(r.sessionRegistration),sessionSignature:(0,d.bytesToHex)(r.sessionSignature)}),"utf8").toString("base64"),s=new Headers(n?.headers);s.set("X-Tab-Voucher",o),s.set("Accept","text/event-stream");let i=await fetch(t,{...n,headers:s});if(!i.ok){let m=await i.text().catch(()=>"");throw new Error(`tab.stream HTTP ${i.status}: ${m.slice(0,500)}`)}if(!i.body)throw new Error("tab.stream response has no body");return H(i.body)}async close(){if(this.closed)throw new a(this.channelId);return await this.internals.vault.signCloseTab(this.internals.session,this.channelId,this.cumulativeAtomic.toString()),this.closed=!0,this.internals.session.privateKey.fill(0),{settledAmount:p(this.cumulativeAtomic.toString()),settleTx:""}}};async function U(e){if(e.network!==e.vault.network)throw new c(`options.network (${e.network}) doesn't match vault.network (${e.vault.network})`);if(e.network!=="solana:mainnet")throw new c(e.network);let t=BigInt(Math.floor(Math.random()*4294967295)),n=new A.PublicKey(e.vault.vaultPda),r=S({vaultPda:n,sellerUrl:e.seller,nonce:BigInt(t)}),o=(0,d.bytesToHex)(r),s=BigInt(h(e.perUnitCap)),i=BigInt(h(e.totalCap));if(s<=0n)throw new Error("perUnitCap must be > 0");if(i<s)throw new Error("totalCap must be >= perUnitCap");let m=e.sessionDuration??O,l=Math.floor(Date.now()/1e3)+m,b={channelId:o,maxAmountAtomic:i.toString(),expiresAtUnix:l,allowedCounterparty:B(e.seller)},I=await e.vault.authorizeSession(b);return new g({vault:e.vault,network:e.network,seller:e.seller,session:I,channelIdHex:o,channelIdBytes:r,perUnitCapAtomic:s,totalCapAtomic:i,expiresAtUnix:l,facilitatorUrl:e.facilitatorUrl??w})}async function T(e){throw new Error("resumeTab is Phase 3 work. Session keys are memory-only by design; recovery requires reading active_session on chain and re-authorizing. Tracked in dexter-vault roadmap.")}function B(e){if(/^[1-9A-HJ-NP-Za-km-z]{32,44}$/.test(e))try{return new A.PublicKey(e),e}catch{}throw new Error(`seller must be a base58 Solana pubkey for Phase 2 (got "${e}"). URL-based counterparty resolution lands in Phase 3 (seller middleware).`)}async function*H(e){let t=e.getReader(),n=new TextDecoder,r="";try{for(;;){let{done:o,value:s}=await t.read();if(o)break;r+=n.decode(s,{stream:!0});let i;for(;(i=r.indexOf(`
1
+ "use strict";var P=Object.create;var y=Object.defineProperty;var C=Object.getOwnPropertyDescriptor;var $=Object.getOwnPropertyNames;var E=Object.getPrototypeOf,K=Object.prototype.hasOwnProperty;var V=(e,t)=>{for(var n in t)y(e,n,{get:t[n],enumerable:!0})},S=(e,t,n,r)=>{if(t&&typeof t=="object"||typeof t=="function")for(let o of $(t))!K.call(e,o)&&o!==n&&y(e,o,{get:()=>t[o],enumerable:!(r=C(t,o))||r.enumerable});return e};var N=(e,t,n)=>(n=e!=null?P(E(e)):{},S(t||!e||!e.__esModule?y(n,"default",{value:e,enumerable:!0}):n,e)),R=e=>S(y({},"__esModule",{value:!0}),e);var q={};V(q,{DEFAULT_FACILITATOR_URL:()=>f,SessionScopeExceededError:()=>m,TabClosedError:()=>c,UnsupportedNetworkError:()=>l,atomicToHuman:()=>h,humanToAtomic:()=>g,openTab:()=>I,resumeTab:()=>U});module.exports=R(q);var l=class extends Error{constructor(n){super(`Network ${n} is not yet supported by @dexterai/x402/tab`);this.network=n;this.name="UnsupportedNetworkError"}},m=class extends Error{constructor(n,r){super(`Session scope exceeded: ${n}${r?` (${r})`:""}`);this.reason=n;this.name="SessionScopeExceededError"}},c=class extends Error{constructor(n){super(`Tab ${n} is already closed`);this.channelId=n;this.name="TabClosedError"}};var A=require("@solana/web3.js"),u=require("@noble/hashes/utils");var O=N(require("tweetnacl"),1);var p=require("@dexterai/vault/messages");var x=require("@noble/hashes/sha256");function T(e){let t=new Uint8Array(8);new DataView(t.buffer).setBigUint64(0,e.nonce,!0);let n=new TextEncoder().encode(e.sellerUrl),r=x.sha256.create();return r.update(e.vaultPda.toBytes()),r.update(n),r.update(t),r.digest()}var H=3600,f="https://x402.dexter.cash",v=6;function g(e,t=v){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 o=r.padEnd(t,"0"),s=`${n}${o}`.replace(/^0+(?=\d)/,"");return s===""?"0":s}function h(e,t=v){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",o=n.slice(-t).replace(/0+$/,"");return o?`${r}.${o}`:r}var w=class{channelId;network;internals;cumulativeAtomic=0n;sequenceNumber=0;closed=!1;lastSignedVoucher=null;constructor(t){this.internals=t,this.channelId=t.channelIdHex,this.network=t.network}get state(){let t=this.internals.totalCapAtomic-this.cumulativeAtomic,n=Math.floor(Date.now()/1e3);return{isOpen:!this.closed,spent:h(this.cumulativeAtomic.toString()),remaining:h(t.toString()),expiresInSec:Math.max(0,this.internals.expiresAtUnix-n)}}async signNextVoucher(t){if(this.closed)throw new c(this.channelId);let n=BigInt(t);if(n<=0n)throw new Error(`voucher increment must be > 0, got ${t}`);if(n>this.internals.perUnitCapAtomic)throw new m("cap_exceeded",`single voucher increment ${n} exceeds perUnitCap ${this.internals.perUnitCapAtomic}`);let r=this.cumulativeAtomic+n;if(r>this.internals.totalCapAtomic)throw new m("cap_exceeded",`cumulative ${r} would exceed totalCap ${this.internals.totalCapAtomic}`);this.sequenceNumber+=1,this.cumulativeAtomic=r;let o={channelId:this.channelId,cumulativeAmount:this.cumulativeAtomic.toString(),sequenceNumber:this.sequenceNumber},s=await this.internals.vault.signWithSession(this.internals.session,o);return this.lastSignedVoucher=s,s}async stream(t,n){if(this.closed)throw new c(this.channelId);let r=await this.signNextVoucher(this.internals.perUnitCapAtomic.toString()),o=Buffer.from(JSON.stringify({payload:r.payload,sessionPublicKey:(0,u.bytesToHex)(r.sessionPublicKey),sessionRegistration:(0,u.bytesToHex)(r.sessionRegistration),sessionSignature:(0,u.bytesToHex)(r.sessionSignature)}),"utf8").toString("base64"),s=new Headers(n?.headers);s.set("X-Tab-Voucher",o),s.set("Accept","text/event-stream");let i=await fetch(t,{...n,headers:s});if(!i.ok){let a=await i.text().catch(()=>"");throw new Error(`tab.stream HTTP ${i.status}: ${a.slice(0,500)}`)}if(!i.body)throw new Error("tab.stream response has no body");return M(i.body)}async close(){if(this.closed)throw new c(this.channelId);let t="";return this.lastSignedVoucher&&this.cumulativeAtomic>0n&&(t=await B(this.internals.facilitatorUrl,this.lastSignedVoucher,this.internals.network)),await this.internals.vault.signCloseTab(this.internals.session,this.channelId,this.cumulativeAtomic.toString()),this.closed=!0,this.internals.session.privateKey.fill(0),{settledAmount:h(this.cumulativeAtomic.toString()),settleTx:t}}};async function B(e,t,n){let r=`${e.replace(/\/$/,"")}/tab/settle`,o={channelId:t.payload.channelId,cumulativeAmount:t.payload.cumulativeAmount,sequenceNumber:t.payload.sequenceNumber,sessionPublicKey:(0,u.bytesToHex)(t.sessionPublicKey),sessionSignature:(0,u.bytesToHex)(t.sessionSignature),sessionRegistration:(0,u.bytesToHex)(t.sessionRegistration),network:n},s=await fetch(r,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(o)}),i=await s.text();if(!s.ok)throw new Error(`tab settle ${s.status}: ${i.slice(0,500)}`);let a;try{a=JSON.parse(i)}catch{throw new Error(`tab settle returned non-JSON: ${i.slice(0,200)}`)}if(!a.settleTx)throw new Error(`tab settle returned no settleTx: ${i.slice(0,200)}`);return a.settleTx}async function I(e){if(e.network!==e.vault.network)throw new l(`options.network (${e.network}) doesn't match vault.network (${e.vault.network})`);if(e.network!=="solana:mainnet")throw new l(e.network);let t=BigInt(Math.floor(Math.random()*4294967295)),n=new A.PublicKey(e.vault.vaultPda),r=T({vaultPda:n,sellerUrl:e.seller,nonce:BigInt(t)}),o=(0,u.bytesToHex)(r),s=BigInt(g(e.perUnitCap)),i=BigInt(g(e.totalCap));if(s<=0n)throw new Error("perUnitCap must be > 0");if(i<s)throw new Error("totalCap must be >= perUnitCap");let a=e.sessionDuration??H,d=Math.floor(Date.now()/1e3)+a,b={channelId:o,maxAmountAtomic:i.toString(),expiresAtUnix:d,allowedCounterparty:D(e.seller)},k=await e.vault.authorizeSession(b);return new w({vault:e.vault,network:e.network,seller:e.seller,session:k,channelIdHex:o,channelIdBytes:r,perUnitCapAtomic:s,totalCapAtomic:i,expiresAtUnix:d,facilitatorUrl:e.facilitatorUrl??f})}async function U(e){throw new Error("resumeTab is Phase 3 work. Session keys are memory-only by design; recovery requires reading active_session on chain and re-authorizing. Tracked in dexter-vault roadmap.")}function D(e){if(/^[1-9A-HJ-NP-Za-km-z]{32,44}$/.test(e))try{return new A.PublicKey(e),e}catch{}throw new Error(`seller must be a base58 Solana pubkey for Phase 2 (got "${e}"). URL-based counterparty resolution lands in Phase 3 (seller middleware).`)}async function*M(e){let t=e.getReader(),n=new TextDecoder,r="";try{for(;;){let{done:o,value:s}=await t.read();if(o)break;r+=n.decode(s,{stream:!0});let i;for(;(i=r.indexOf(`
2
2
 
3
- `))!==-1;){let m=r.slice(0,i);r=r.slice(i+2);let l=_(m);if(l.eventName==="end")return;if(l.data!==null){let b=l.data.replace(/\\n/g,`
3
+ `))!==-1;){let a=r.slice(0,i);r=r.slice(i+2);let d=_(a);if(d.eventName==="end")return;if(d.data!==null){let b=d.data.replace(/\\n/g,`
4
4
  `);yield new TextEncoder().encode(b)}}}}finally{t.releaseLock()}}function _(e){let t=null,n=[];for(let r of e.split(`
5
5
  `))r.startsWith("event:")?t=r.slice(6).trim():r.startsWith("data:")&&n.push(r.slice(5).trimStart());return{eventName:t,data:n.length?n.join(`
6
6
  `):null}}0&&(module.exports={DEFAULT_FACILITATOR_URL,SessionScopeExceededError,TabClosedError,UnsupportedNetworkError,atomicToHuman,humanToAtomic,openTab,resumeTab});
@@ -1,17 +1,21 @@
1
- import { O as OpenTabOptions, T as Tab, R as ResumeTabOptions, H as HumanAmount, A as AtomicAmount } from '../types-DIrmhiD-.cjs';
2
- export { d as SessionKey, S as SessionScope, g as SessionScopeExceededError, e as SignedVoucher, b as TabCloseResult, h as TabClosedError, c as TabNetworkId, a as TabState, U as UnsupportedNetworkError, f as VaultAdapter, V as VoucherPayload } from '../types-DIrmhiD-.cjs';
1
+ import { O as OpenTabOptions, T as Tab, R as ResumeTabOptions } from '../types-BtibqM00.cjs';
2
+ export { S as SessionScopeExceededError, b as TabCloseResult, c as TabClosedError, a as TabState, U as UnsupportedNetworkError, V as VaultAdapter } from '../types-BtibqM00.cjs';
3
+ import { HumanAmount, AtomicAmount } from '@dexterai/vault/types';
4
+ export { AtomicAmount, HumanAmount, SessionKey, SessionScope, SignedVoucher, TabNetworkId, VoucherPayload } from '@dexterai/vault/types';
3
5
 
4
6
  /**
5
7
  * The `Tab` runtime — the live object returned by `openTab()`.
6
8
  *
7
9
  * Owns: the session key, the channel id, the cumulative-amount counter,
8
- * the voucher sequence counter. Exposes: `stream()` for paid streamed
9
- * requests and `close()` for revocation + settlement.
10
+ * the voucher sequence counter, the last signed voucher. Exposes:
11
+ * `stream()` for paid streamed requests and `close()` for on-chain
12
+ * settle + session revocation.
10
13
  *
11
- * Phase 2 ships `open` and `close` against the live program; `stream()`
12
- * is the seller-facing surface that lands fully in Phase 3 once the
13
- * seller middleware is built. For Phase 2, `stream()` throws a clear
14
- * "phase 3" error but the open/close round-trip works end to end.
14
+ * As of `@dexterai/x402@3.10.0`, `close()` POSTs the final voucher to
15
+ * the facilitator's `POST /tab/settle` endpoint BEFORE revoking the
16
+ * session, so `TabCloseResult.settleTx` is the real on-chain settlement
17
+ * signature (USDC swig seller ATA, atomic with the session's `spent`
18
+ * advance + `pending_voucher_count` decrement).
15
19
  */
16
20
 
17
21
  /** Live Dexter x402 facilitator API. NOT facilitator.dexter.cash —
@@ -26,4 +30,4 @@ declare function atomicToHuman(atomic: AtomicAmount, decimals?: number): HumanAm
26
30
  declare function openTab(options: OpenTabOptions): Promise<Tab>;
27
31
  declare function resumeTab(_options: ResumeTabOptions): Promise<Tab>;
28
32
 
29
- export { AtomicAmount, DEFAULT_FACILITATOR_URL, HumanAmount, OpenTabOptions, ResumeTabOptions, Tab, atomicToHuman, humanToAtomic, openTab, resumeTab };
33
+ export { DEFAULT_FACILITATOR_URL, OpenTabOptions, ResumeTabOptions, Tab, atomicToHuman, humanToAtomic, openTab, resumeTab };
@@ -1,17 +1,21 @@
1
- import { O as OpenTabOptions, T as Tab, R as ResumeTabOptions, H as HumanAmount, A as AtomicAmount } from '../types-DIrmhiD-.js';
2
- export { d as SessionKey, S as SessionScope, g as SessionScopeExceededError, e as SignedVoucher, b as TabCloseResult, h as TabClosedError, c as TabNetworkId, a as TabState, U as UnsupportedNetworkError, f as VaultAdapter, V as VoucherPayload } from '../types-DIrmhiD-.js';
1
+ import { O as OpenTabOptions, T as Tab, R as ResumeTabOptions } from '../types-BtibqM00.js';
2
+ export { S as SessionScopeExceededError, b as TabCloseResult, c as TabClosedError, a as TabState, U as UnsupportedNetworkError, V as VaultAdapter } from '../types-BtibqM00.js';
3
+ import { HumanAmount, AtomicAmount } from '@dexterai/vault/types';
4
+ export { AtomicAmount, HumanAmount, SessionKey, SessionScope, SignedVoucher, TabNetworkId, VoucherPayload } from '@dexterai/vault/types';
3
5
 
4
6
  /**
5
7
  * The `Tab` runtime — the live object returned by `openTab()`.
6
8
  *
7
9
  * Owns: the session key, the channel id, the cumulative-amount counter,
8
- * the voucher sequence counter. Exposes: `stream()` for paid streamed
9
- * requests and `close()` for revocation + settlement.
10
+ * the voucher sequence counter, the last signed voucher. Exposes:
11
+ * `stream()` for paid streamed requests and `close()` for on-chain
12
+ * settle + session revocation.
10
13
  *
11
- * Phase 2 ships `open` and `close` against the live program; `stream()`
12
- * is the seller-facing surface that lands fully in Phase 3 once the
13
- * seller middleware is built. For Phase 2, `stream()` throws a clear
14
- * "phase 3" error but the open/close round-trip works end to end.
14
+ * As of `@dexterai/x402@3.10.0`, `close()` POSTs the final voucher to
15
+ * the facilitator's `POST /tab/settle` endpoint BEFORE revoking the
16
+ * session, so `TabCloseResult.settleTx` is the real on-chain settlement
17
+ * signature (USDC swig seller ATA, atomic with the session's `spent`
18
+ * advance + `pending_voucher_count` decrement).
15
19
  */
16
20
 
17
21
  /** Live Dexter x402 facilitator API. NOT facilitator.dexter.cash —
@@ -26,4 +30,4 @@ declare function atomicToHuman(atomic: AtomicAmount, decimals?: number): HumanAm
26
30
  declare function openTab(options: OpenTabOptions): Promise<Tab>;
27
31
  declare function resumeTab(_options: ResumeTabOptions): Promise<Tab>;
28
32
 
29
- export { AtomicAmount, DEFAULT_FACILITATOR_URL, HumanAmount, OpenTabOptions, ResumeTabOptions, Tab, atomicToHuman, humanToAtomic, openTab, resumeTab };
33
+ export { DEFAULT_FACILITATOR_URL, OpenTabOptions, ResumeTabOptions, Tab, atomicToHuman, humanToAtomic, openTab, resumeTab };
package/dist/tab/index.js CHANGED
@@ -1,6 +1,6 @@
1
- var u=class extends Error{constructor(r){super(`Network ${r} is not yet supported by @dexterai/x402/tab`);this.network=r;this.name="UnsupportedNetworkError"}},l=class extends Error{constructor(r,t){super(`Session scope exceeded: ${r}${t?` (${t})`:""}`);this.reason=r;this.name="SessionScopeExceededError"}},a=class extends Error{constructor(r){super(`Tab ${r} is already closed`);this.channelId=r;this.name="TabClosedError"}};import{PublicKey as A}from"@solana/web3.js";import{bytesToHex as d}from"@noble/hashes/utils";import V from"tweetnacl";var K=(()=>{let e=new Uint8Array(32);return e.set(new TextEncoder().encode("OTS_SESSION_REGISTER_V1"),0),e})(),C=(()=>{let e=new Uint8Array(32);return e.set(new TextEncoder().encode("OTS_SESSION_REVOKE_V1"),0),e})();import{sha256 as S}from"@noble/hashes/sha256";function g(e){let n=new Uint8Array(8);new DataView(n.buffer).setBigUint64(0,e.nonce,!0);let r=new TextEncoder().encode(e.sellerUrl),t=S.create();return t.update(e.vaultPda.toBytes()),t.update(r),t.update(n),t.digest()}var v=3600,w="https://x402.dexter.cash",f=6;function h(e,n=f){if(!/^\d+(\.\d+)?$/.test(e))throw new Error(`amount must be a non-negative decimal string, got "${e}"`);let[r,t=""]=e.split(".");if(t.length>n)throw new Error(`amount "${e}" has more than ${n} decimals`);let o=t.padEnd(n,"0"),s=`${r}${o}`.replace(/^0+(?=\d)/,"");return s===""?"0":s}function p(e,n=f){if(!/^\d+$/.test(e))throw new Error(`atomic must be a non-negative integer string, got "${e}"`);let r=e.padStart(n+1,"0"),t=r.slice(0,-n).replace(/^0+(?=\d)/,"")||"0",o=r.slice(-n).replace(/0+$/,"");return o?`${t}.${o}`:t}var b=class{channelId;network;internals;cumulativeAtomic=0n;sequenceNumber=0;closed=!1;constructor(n){this.internals=n,this.channelId=n.channelIdHex,this.network=n.network}get state(){let n=this.internals.totalCapAtomic-this.cumulativeAtomic,r=Math.floor(Date.now()/1e3);return{isOpen:!this.closed,spent:p(this.cumulativeAtomic.toString()),remaining:p(n.toString()),expiresInSec:Math.max(0,this.internals.expiresAtUnix-r)}}async signNextVoucher(n){if(this.closed)throw new a(this.channelId);let r=BigInt(n);if(r<=0n)throw new Error(`voucher increment must be > 0, got ${n}`);if(r>this.internals.perUnitCapAtomic)throw new l("cap_exceeded",`single voucher increment ${r} exceeds perUnitCap ${this.internals.perUnitCapAtomic}`);let t=this.cumulativeAtomic+r;if(t>this.internals.totalCapAtomic)throw new l("cap_exceeded",`cumulative ${t} would exceed totalCap ${this.internals.totalCapAtomic}`);this.sequenceNumber+=1,this.cumulativeAtomic=t;let o={channelId:this.channelId,cumulativeAmount:this.cumulativeAtomic.toString(),sequenceNumber:this.sequenceNumber};return await this.internals.vault.signWithSession(this.internals.session,o)}async stream(n,r){if(this.closed)throw new a(this.channelId);let t=await this.signNextVoucher(this.internals.perUnitCapAtomic.toString()),o=Buffer.from(JSON.stringify({payload:t.payload,sessionPublicKey:d(t.sessionPublicKey),sessionRegistration:d(t.sessionRegistration),sessionSignature:d(t.sessionSignature)}),"utf8").toString("base64"),s=new Headers(r?.headers);s.set("X-Tab-Voucher",o),s.set("Accept","text/event-stream");let i=await fetch(n,{...r,headers:s});if(!i.ok){let m=await i.text().catch(()=>"");throw new Error(`tab.stream HTTP ${i.status}: ${m.slice(0,500)}`)}if(!i.body)throw new Error("tab.stream response has no body");return P(i.body)}async close(){if(this.closed)throw new a(this.channelId);return await this.internals.vault.signCloseTab(this.internals.session,this.channelId,this.cumulativeAtomic.toString()),this.closed=!0,this.internals.session.privateKey.fill(0),{settledAmount:p(this.cumulativeAtomic.toString()),settleTx:""}}};async function U(e){if(e.network!==e.vault.network)throw new u(`options.network (${e.network}) doesn't match vault.network (${e.vault.network})`);if(e.network!=="solana:mainnet")throw new u(e.network);let n=BigInt(Math.floor(Math.random()*4294967295)),r=new A(e.vault.vaultPda),t=g({vaultPda:r,sellerUrl:e.seller,nonce:BigInt(n)}),o=d(t),s=BigInt(h(e.perUnitCap)),i=BigInt(h(e.totalCap));if(s<=0n)throw new Error("perUnitCap must be > 0");if(i<s)throw new Error("totalCap must be >= perUnitCap");let m=e.sessionDuration??v,c=Math.floor(Date.now()/1e3)+m,y={channelId:o,maxAmountAtomic:i.toString(),expiresAtUnix:c,allowedCounterparty:I(e.seller)},x=await e.vault.authorizeSession(y);return new b({vault:e.vault,network:e.network,seller:e.seller,session:x,channelIdHex:o,channelIdBytes:t,perUnitCapAtomic:s,totalCapAtomic:i,expiresAtUnix:c,facilitatorUrl:e.facilitatorUrl??w})}async function T(e){throw new Error("resumeTab is Phase 3 work. Session keys are memory-only by design; recovery requires reading active_session on chain and re-authorizing. Tracked in dexter-vault roadmap.")}function I(e){if(/^[1-9A-HJ-NP-Za-km-z]{32,44}$/.test(e))try{return new A(e),e}catch{}throw new Error(`seller must be a base58 Solana pubkey for Phase 2 (got "${e}"). URL-based counterparty resolution lands in Phase 3 (seller middleware).`)}async function*P(e){let n=e.getReader(),r=new TextDecoder,t="";try{for(;;){let{done:o,value:s}=await n.read();if(o)break;t+=r.decode(s,{stream:!0});let i;for(;(i=t.indexOf(`
1
+ var m=class extends Error{constructor(r){super(`Network ${r} is not yet supported by @dexterai/x402/tab`);this.network=r;this.name="UnsupportedNetworkError"}},d=class extends Error{constructor(r,n){super(`Session scope exceeded: ${r}${n?` (${n})`:""}`);this.reason=r;this.name="SessionScopeExceededError"}},c=class extends Error{constructor(r){super(`Tab ${r} is already closed`);this.channelId=r;this.name="TabClosedError"}};import{PublicKey as w}from"@solana/web3.js";import{bytesToHex as u}from"@noble/hashes/utils";import B from"tweetnacl";import{sessionRegisterMessage as N,sessionRevokeMessage as R,voucherPayloadMessage as x,buildVoucherMessage as O}from"@dexterai/vault/messages";import{sha256 as T}from"@noble/hashes/sha256";function b(e){let t=new Uint8Array(8);new DataView(t.buffer).setBigUint64(0,e.nonce,!0);let r=new TextEncoder().encode(e.sellerUrl),n=T.create();return n.update(e.vaultPda.toBytes()),n.update(r),n.update(t),n.digest()}var v=3600,A="https://x402.dexter.cash",f=6;function y(e,t=f){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"),o=`${r}${i}`.replace(/^0+(?=\d)/,"");return o===""?"0":o}function p(e,t=f){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 g=class{channelId;network;internals;cumulativeAtomic=0n;sequenceNumber=0;closed=!1;lastSignedVoucher=null;constructor(t){this.internals=t,this.channelId=t.channelIdHex,this.network=t.network}get state(){let t=this.internals.totalCapAtomic-this.cumulativeAtomic,r=Math.floor(Date.now()/1e3);return{isOpen:!this.closed,spent:p(this.cumulativeAtomic.toString()),remaining:p(t.toString()),expiresInSec:Math.max(0,this.internals.expiresAtUnix-r)}}async signNextVoucher(t){if(this.closed)throw new c(this.channelId);let r=BigInt(t);if(r<=0n)throw new Error(`voucher increment must be > 0, got ${t}`);if(r>this.internals.perUnitCapAtomic)throw new d("cap_exceeded",`single voucher increment ${r} exceeds perUnitCap ${this.internals.perUnitCapAtomic}`);let n=this.cumulativeAtomic+r;if(n>this.internals.totalCapAtomic)throw new d("cap_exceeded",`cumulative ${n} would exceed totalCap ${this.internals.totalCapAtomic}`);this.sequenceNumber+=1,this.cumulativeAtomic=n;let i={channelId:this.channelId,cumulativeAmount:this.cumulativeAtomic.toString(),sequenceNumber:this.sequenceNumber},o=await this.internals.vault.signWithSession(this.internals.session,i);return this.lastSignedVoucher=o,o}async stream(t,r){if(this.closed)throw new c(this.channelId);let n=await this.signNextVoucher(this.internals.perUnitCapAtomic.toString()),i=Buffer.from(JSON.stringify({payload:n.payload,sessionPublicKey:u(n.sessionPublicKey),sessionRegistration:u(n.sessionRegistration),sessionSignature:u(n.sessionSignature)}),"utf8").toString("base64"),o=new Headers(r?.headers);o.set("X-Tab-Voucher",i),o.set("Accept","text/event-stream");let s=await fetch(t,{...r,headers:o});if(!s.ok){let a=await s.text().catch(()=>"");throw new Error(`tab.stream HTTP ${s.status}: ${a.slice(0,500)}`)}if(!s.body)throw new Error("tab.stream response has no body");return C(s.body)}async close(){if(this.closed)throw new c(this.channelId);let t="";return this.lastSignedVoucher&&this.cumulativeAtomic>0n&&(t=await I(this.internals.facilitatorUrl,this.lastSignedVoucher,this.internals.network)),await this.internals.vault.signCloseTab(this.internals.session,this.channelId,this.cumulativeAtomic.toString()),this.closed=!0,this.internals.session.privateKey.fill(0),{settledAmount:p(this.cumulativeAtomic.toString()),settleTx:t}}};async function I(e,t,r){let n=`${e.replace(/\/$/,"")}/tab/settle`,i={channelId:t.payload.channelId,cumulativeAmount:t.payload.cumulativeAmount,sequenceNumber:t.payload.sequenceNumber,sessionPublicKey:u(t.sessionPublicKey),sessionSignature:u(t.sessionSignature),sessionRegistration:u(t.sessionRegistration),network:r},o=await fetch(n,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(i)}),s=await o.text();if(!o.ok)throw new Error(`tab settle ${o.status}: ${s.slice(0,500)}`);let a;try{a=JSON.parse(s)}catch{throw new Error(`tab settle returned non-JSON: ${s.slice(0,200)}`)}if(!a.settleTx)throw new Error(`tab settle returned no settleTx: ${s.slice(0,200)}`);return a.settleTx}async function U(e){if(e.network!==e.vault.network)throw new m(`options.network (${e.network}) doesn't match vault.network (${e.vault.network})`);if(e.network!=="solana:mainnet")throw new m(e.network);let t=BigInt(Math.floor(Math.random()*4294967295)),r=new w(e.vault.vaultPda),n=b({vaultPda:r,sellerUrl:e.seller,nonce:BigInt(t)}),i=u(n),o=BigInt(y(e.perUnitCap)),s=BigInt(y(e.totalCap));if(o<=0n)throw new Error("perUnitCap must be > 0");if(s<o)throw new Error("totalCap must be >= perUnitCap");let a=e.sessionDuration??v,l=Math.floor(Date.now()/1e3)+a,h={channelId:i,maxAmountAtomic:s.toString(),expiresAtUnix:l,allowedCounterparty:P(e.seller)},S=await e.vault.authorizeSession(h);return new g({vault:e.vault,network:e.network,seller:e.seller,session:S,channelIdHex:i,channelIdBytes:n,perUnitCapAtomic:o,totalCapAtomic:s,expiresAtUnix:l,facilitatorUrl:e.facilitatorUrl??A})}async function k(e){throw new Error("resumeTab is Phase 3 work. Session keys are memory-only by design; recovery requires reading active_session on chain and re-authorizing. Tracked in dexter-vault roadmap.")}function P(e){if(/^[1-9A-HJ-NP-Za-km-z]{32,44}$/.test(e))try{return new w(e),e}catch{}throw new Error(`seller must be a base58 Solana pubkey for Phase 2 (got "${e}"). URL-based counterparty resolution lands in Phase 3 (seller middleware).`)}async function*C(e){let t=e.getReader(),r=new TextDecoder,n="";try{for(;;){let{done:i,value:o}=await t.read();if(i)break;n+=r.decode(o,{stream:!0});let s;for(;(s=n.indexOf(`
2
2
 
3
- `))!==-1;){let m=t.slice(0,i);t=t.slice(i+2);let c=k(m);if(c.eventName==="end")return;if(c.data!==null){let y=c.data.replace(/\\n/g,`
4
- `);yield new TextEncoder().encode(y)}}}}finally{n.releaseLock()}}function k(e){let n=null,r=[];for(let t of e.split(`
5
- `))t.startsWith("event:")?n=t.slice(6).trim():t.startsWith("data:")&&r.push(t.slice(5).trimStart());return{eventName:n,data:r.length?r.join(`
6
- `):null}}export{w as DEFAULT_FACILITATOR_URL,l as SessionScopeExceededError,a as TabClosedError,u as UnsupportedNetworkError,p as atomicToHuman,h as humanToAtomic,U as openTab,T as resumeTab};
3
+ `))!==-1;){let a=n.slice(0,s);n=n.slice(s+2);let l=$(a);if(l.eventName==="end")return;if(l.data!==null){let h=l.data.replace(/\\n/g,`
4
+ `);yield new TextEncoder().encode(h)}}}}finally{t.releaseLock()}}function $(e){let t=null,r=[];for(let n of e.split(`
5
+ `))n.startsWith("event:")?t=n.slice(6).trim():n.startsWith("data:")&&r.push(n.slice(5).trimStart());return{eventName:t,data:r.length?r.join(`
6
+ `):null}}export{A as DEFAULT_FACILITATOR_URL,d as SessionScopeExceededError,c as TabClosedError,m as UnsupportedNetworkError,p as atomicToHuman,y as humanToAtomic,U as openTab,k as resumeTab};
@@ -1,6 +1,6 @@
1
- "use strict";var te=Object.create;var x=Object.defineProperty;var ne=Object.getOwnPropertyDescriptor;var re=Object.getOwnPropertyNames;var oe=Object.getPrototypeOf,ie=Object.prototype.hasOwnProperty;var se=(e,t)=>{for(var n in t)x(e,n,{get:t[n],enumerable:!0})},z=(e,t,n,r)=>{if(t&&typeof t=="object"||typeof t=="function")for(let o of re(t))!ie.call(e,o)&&o!==n&&x(e,o,{get:()=>t[o],enumerable:!(r=ne(t,o))||r.enumerable});return e};var F=(e,t,n)=>(n=e!=null?te(oe(e)):{},z(t||!e||!e.__esModule?x(n,"default",{value:e,enumerable:!0}):n,e)),ae=e=>z(x({},"__esModule",{value:!0}),e);var be={};se(be,{FileVoucherStore:()=>_,InMemoryVoucherStore:()=>b,InvalidRegistrationError:()=>m,InvalidVoucherError:()=>l,InvalidVoucherSignatureError:()=>g,OnChainVerificationError:()=>h,ScopeViolationError:()=>u,TAB_VOUCHER_HEADER:()=>C,enforceScope:()=>k,openSse:()=>Q,parseRegistration:()=>I,readVaultState:()=>B,requireTab:()=>X,tabMiddleware:()=>Z,verifyRegistrationOnChain:()=>E,verifyVoucherSignature:()=>T});module.exports=ae(be);var l=class extends Error{constructor(n,r){super(`Invalid voucher: ${n}${r?` (${r})`:""}`);this.reason=n;this.name="InvalidVoucherError"}};var W=require("@solana/web3.js");var G=F(require("tweetnacl"),1),ue=require("@noble/hashes/sha256"),ce=require("@noble/curves/p256"),U=require("@solana/web3.js");var ve=(()=>{let e=new Uint8Array(32);return e.set(new TextEncoder().encode("OTS_SESSION_REGISTER_V1"),0),e})(),xe=(()=>{let e=new Uint8Array(32);return e.set(new TextEncoder().encode("OTS_SESSION_REVOKE_V1"),0),e})();function K(e){if(e.channelId.length!==32)throw new Error(`channelId must be 32 bytes, got ${e.channelId.length}`);let t=new Uint8Array(44),n=new DataView(t.buffer),r=0;if(t.set(e.channelId,r),r+=32,n.setBigUint64(r,e.cumulativeAmount,!0),r+=8,n.setUint32(r,e.sequenceNumber>>>0,!0),r+=4,r!==44)throw new Error(`internal: voucher payload wrong length ${r}, expected 44`);return t}var A=require("@solana/web3.js"),P=new A.PublicKey("Hg3wRaydFtJhYrdvYrKECacpJYDsC9Px7yKmpncj2fhc"),Ue=new A.PublicKey("Secp256r1SigVerify1111111111111111111111111"),Ie=new A.PublicKey("Sysvar1nstructions1111111111111111111111111"),Ee=new Uint8Array([69,94,60,44,49,199,183,233]),Te=new Uint8Array([81,192,32,110,104,116,144,151]);var O="OTS_SESSION_REGISTER_V1",m=class extends Error{constructor(n,r){super(`Invalid registration: ${n}${r?` (${r})`:""}`);this.reason=n;this.name="InvalidRegistrationError"}};function I(e){if(e.length!==180)throw new m("wrong_length",`expected 180, got ${e.length}`);let t=new TextDecoder().decode(e.slice(0,O.length));if(t!==O)throw new m("wrong_domain",`got "${t}"`);for(let s=O.length;s<32;s++)if(e[s]!==0)throw new m("wrong_domain",`non-NUL padding at byte ${s}`);let n=new DataView(e.buffer,e.byteOffset,e.byteLength),r=new U.PublicKey(e.slice(32,64)),o=new U.PublicKey(e.slice(64,96)),c=e.slice(96,128),p=n.getBigUint64(128,!0),f=n.getBigInt64(136,!0),i=new U.PublicKey(e.slice(144,176)),a=n.getUint32(176,!0);if(!r.equals(P))throw new m("wrong_program",`${r.toBase58()} is not ${P.toBase58()}`);if(p===0n)throw new m("cap_zero");let d=BigInt(Math.floor(Date.now()/1e3));if(f<=d)throw new m("expiry_in_past",`expires_at=${f}, now=${d}`);return{programId:r,vaultPda:o,sessionPubkey:new Uint8Array(c),maxAmount:p,expiresAt:f,allowedCounterparty:i,nonce:a}}var j=10,h=class extends Error{constructor(n,r){super(`On-chain verification failed: ${n}${r?` (${r})`:""}`);this.reason=n;this.name="OnChainVerificationError"}};async function B(e,t){let n=await e.getAccountInfo(t,"finalized");if(!n)throw new h("vault_not_found",t.toBase58());if(!n.owner.equals(P))throw new h("wrong_program",`owner ${n.owner.toBase58()} is not the vault program`);let r=n.data,o=new Uint8Array(r.slice(j,j+33)),a=84+(r[83]===1?48:0)+32+32;if(r[a]!==1)return{passkeyPubkey:o,activeSessionPubkey:null};let s=a+1,y=new Uint8Array(r.slice(s,s+32));return{passkeyPubkey:o,activeSessionPubkey:y}}async function E(e,t){let n=await B(e,t.vaultPda);if(n.activeSessionPubkey===null)throw new h("session_not_active","vault has no active_session \u2014 was it revoked?");if(!le(n.activeSessionPubkey,t.sessionPubkey))throw new h("session_pubkey_mismatch",`on-chain ${J(n.activeSessionPubkey)} != registration ${J(t.sessionPubkey)}`);return{passkeyPubkey:n.passkeyPubkey}}var g=class extends Error{constructor(t){super(`Invalid voucher signature${t?`: ${t}`:""}`),this.name="InvalidVoucherSignatureError"}};function T(e,t){if(t.length!==32)throw new g(`channelIdBytes must be 32 bytes, got ${t.length}`);if(e.sessionPublicKey.length!==32)throw new g(`sessionPublicKey must be 32 bytes, got ${e.sessionPublicKey.length}`);if(e.sessionSignature.length!==64)throw new g(`sessionSignature must be 64 bytes, got ${e.sessionSignature.length}`);let n=K({channelId:t,cumulativeAmount:BigInt(e.payload.cumulativeAmount),sequenceNumber:e.payload.sequenceNumber});if(!G.default.sign.detached.verify(n,e.sessionSignature,e.sessionPublicKey))throw new g("ed25519 verify rejected")}var u=class extends Error{constructor(n,r){super(`Scope violation: ${n}${r?` (${r})`:""}`);this.reason=n;this.name="ScopeViolationError"}};function k(e){let t=BigInt(e.voucher.payload.cumulativeAmount);if(t>e.registration.maxAmount)throw new u("cumulative_exceeds_cap",`${t} > ${e.registration.maxAmount}`);let n=BigInt(Math.floor(Date.now()/1e3));if(n>=e.registration.expiresAt)throw new u("session_expired",`now=${n} >= expiresAt=${e.registration.expiresAt}`);if(!e.registration.allowedCounterparty.equals(e.expectedCounterparty))throw new u("wrong_counterparty",`${e.registration.allowedCounterparty.toBase58()} != ${e.expectedCounterparty.toBase58()}`);if(e.previousCumulativeAtomic!==void 0){let r=BigInt(e.previousCumulativeAtomic);if(t<=r)throw new u("non_monotonic",`cumulative=${t} not > previous=${r}`)}}function le(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 J(e){let t="";for(let n of e)t+=n.toString(16).padStart(2,"0");return t}var w=require("fs"),R=require("path");function me(e){return{payload:e.payload,sessionPublicKey:N(e.sessionPublicKey),sessionRegistration:N(e.sessionRegistration),sessionSignature:N(e.sessionSignature)}}function de(e){return{payload:e.payload,sessionPublicKey:D(e.sessionPublicKey),sessionRegistration:D(e.sessionRegistration),sessionSignature:D(e.sessionSignature)}}function N(e){let t="";for(let n of e)t+=n.toString(16).padStart(2,"0");return t}function D(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 b=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)}},_=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,R.join)(this.dir,`${t}.json`)}async get(t){try{let n=await w.promises.readFile(this.pathFor(t),"utf8");return de(JSON.parse(n))}catch(n){if(n?.code==="ENOENT")return null;throw n}}async set(t,n){let r=this.pathFor(t);await w.promises.mkdir((0,R.dirname)(r),{recursive:!0});let o=`${r}.tmp`;await w.promises.writeFile(o,JSON.stringify(me(n))),await w.promises.rename(o,r)}async delete(t){try{await w.promises.unlink(this.pathFor(t))}catch(n){if(n?.code!=="ENOENT")throw n}}};var ge=require("@solana/web3.js"),he=require("@noble/hashes/utils");var pe=F(require("tweetnacl"),1);var ye=require("@noble/hashes/sha256");var Y=6;function S(e,t=Y){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 o=r.padEnd(t,"0"),c=`${n}${o}`.replace(/^0+(?=\d)/,"");return c===""?"0":c}function v(e,t=Y){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",o=n.slice(-t).replace(/0+$/,"");return o?`${r}.${o}`:r}var C="x-tab-voucher",H=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)}},M=class{constructor(t,n,r,o){this.chargeImpl=o;this.channelId=t,this.network=n,this.cumulativeAtomic=r}channelId;network;sessionPublicKey=null;cumulativeAtomic;cumulative(){return v(this.cumulativeAtomic.toString())}bumpCumulative(t){this.cumulativeAtomic=t}setSessionPublicKey(t){this.sessionPublicKey=t}async charge(t){return this.chargeImpl(t)}};function fe(e){if(typeof e!="string"||e.length===0)throw new l("signature_invalid",`missing ${C} header`);let t;try{t=Buffer.from(e,"base64").toString("utf8")}catch{throw new l("signature_invalid","malformed base64")}let n;try{n=JSON.parse(t)}catch{throw new l("signature_invalid","malformed JSON")}if(!n||typeof n!="object"||!n.payload||!n.sessionPublicKey)throw new l("signature_invalid","missing required fields");return{payload:n.payload,sessionPublicKey:V(n.sessionPublicKey),sessionRegistration:V(n.sessionRegistration),sessionSignature:V(n.sessionSignature)}}function V(e){if(typeof e!="string"||e.length%2!==0)throw new l("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 we(e){if(!/^[0-9a-f]{64}$/i.test(e))throw new l("signature_invalid",`channelId must be 64-char hex, got "${e}"`);return V(e)}function Z(e){let t=e.store??new b,n=new H,r=typeof e.sellerPubkey=="string"?new W.PublicKey(e.sellerPubkey):e.sellerPubkey,o=e.maxPerVoucherAtomic?BigInt(e.maxPerVoucherAtomic):BigInt(S(e.perUnit))*100n;return async(c,p,f)=>{try{let i=fe(c.headers[C]),a=i.payload.channelId,d=we(a),s=n.get(a);if(!s){let $=I(i.sessionRegistration);await E(e.connection,$),s={registration:$,lastCumulativeAtomic:"0"},n.set(a,s)}T(i,d),k({registration:s.registration,voucher:i,expectedCounterparty:r,previousCumulativeAtomic:s.lastCumulativeAtomic});let y=BigInt(i.payload.cumulativeAmount),ee=BigInt(s.lastCumulativeAtomic),q=y-ee;if(q>o)throw new u("cumulative_exceeds_cap",`single voucher increment ${q} exceeds maxPerVoucherAtomic ${o}`);await t.set(a,i),n.update(a,i.payload.cumulativeAmount);let L=new M(a,e.network,y,async $=>{throw new Error("SellerTab.charge() is not driven by the route handler; the buyer presents a fresh voucher per chunk. Use openSse(res, tab) for the metered-stream pattern.")});L.setSessionPublicKey(i.sessionPublicKey),c.tab=L,f()}catch(i){if(i instanceof l||i instanceof m||i instanceof h||i instanceof g||i instanceof u){p.status(402).json({error:"invalid_voucher",reason:i.reason??"unknown",detail:i.message});return}f(i)}}}function X(e){if(!e.tab)throw new Error("req.tab is missing \u2014 did tabMiddleware run on this route?");return e.tab}function Q(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(S(n.cumulative())),o=t.perUnit?BigInt(S(t.perUnit)):null,c=0n,p=!1;function f(d=1){if(p)return Promise.reject(new Error("meter ended"));if(o===null)return Promise.reject(new Error("charge() needs options.perUnit"));let s=o*BigInt(d),y=c+s;return y>r?Promise.reject(new u("cumulative_exceeds_cap",`chunk would push request total to ${v(y.toString())} beyond voucher-authorized budget ${v(r.toString())}`)):(c=y,Promise.resolve())}function i(d){if(p)throw new Error("meter ended");let y=(typeof d=="string"?d:Buffer.from(d).toString("utf8")).replace(/\n/g,"\\n");e.write(`data: ${y}
1
+ "use strict";var te=Object.create;var P=Object.defineProperty;var ne=Object.getOwnPropertyDescriptor;var re=Object.getOwnPropertyNames;var oe=Object.getPrototypeOf,ie=Object.prototype.hasOwnProperty;var se=(e,t)=>{for(var n in t)P(e,n,{get:t[n],enumerable:!0})},z=(e,t,n,r)=>{if(t&&typeof t=="object"||typeof t=="function")for(let o of re(t))!ie.call(e,o)&&o!==n&&P(e,o,{get:()=>t[o],enumerable:!(r=ne(t,o))||r.enumerable});return e};var L=(e,t,n)=>(n=e!=null?te(oe(e)):{},z(t||!e||!e.__esModule?P(n,"default",{value:e,enumerable:!0}):n,e)),ae=e=>z(P({},"__esModule",{value:!0}),e);var Se={};se(Se,{FileVoucherStore:()=>_,InMemoryVoucherStore:()=>A,InvalidRegistrationError:()=>m,InvalidVoucherError:()=>l,InvalidVoucherSignatureError:()=>g,OnChainVerificationError:()=>y,ScopeViolationError:()=>c,TAB_VOUCHER_HEADER:()=>R,enforceScope:()=>U,openSse:()=>Q,parseRegistration:()=>I,readVaultState:()=>O,requireTab:()=>Z,tabMiddleware:()=>Y,verifyRegistrationOnChain:()=>k,verifyVoucherSignature:()=>V});module.exports=ae(Se);var l=class extends Error{constructor(n,r){super(`Invalid voucher: ${n}${r?` (${r})`:""}`);this.reason=n;this.name="InvalidVoucherError"}};var X=require("@solana/web3.js");var G=L(require("tweetnacl"),1),ue=require("@noble/hashes/sha256"),le=require("@noble/curves/p256"),T=require("@solana/web3.js");var w=require("@dexterai/vault/messages");var F=require("@dexterai/vault/instructions"),ce=require("@dexterai/vault/precompile"),b=require("@dexterai/vault/constants");var K="OTS_SESSION_REGISTER_V1",m=class extends Error{constructor(n,r){super(`Invalid registration: ${n}${r?` (${r})`:""}`);this.reason=n;this.name="InvalidRegistrationError"}};function I(e){if(e.length!==180)throw new m("wrong_length",`expected 180, got ${e.length}`);let t=new TextDecoder().decode(e.slice(0,K.length));if(t!==K)throw new m("wrong_domain",`got "${t}"`);for(let s=K.length;s<32;s++)if(e[s]!==0)throw new m("wrong_domain",`non-NUL padding at byte ${s}`);let n=new DataView(e.buffer,e.byteOffset,e.byteLength),r=new T.PublicKey(e.slice(32,64)),o=new T.PublicKey(e.slice(64,96)),u=e.slice(96,128),p=n.getBigUint64(128,!0),f=n.getBigInt64(136,!0),i=new T.PublicKey(e.slice(144,176)),a=n.getUint32(176,!0);if(!r.equals(b.DEXTER_VAULT_PROGRAM_ID))throw new m("wrong_program",`${r.toBase58()} is not ${b.DEXTER_VAULT_PROGRAM_ID.toBase58()}`);if(p===0n)throw new m("cap_zero");let d=BigInt(Math.floor(Date.now()/1e3));if(f<=d)throw new m("expiry_in_past",`expires_at=${f}, now=${d}`);return{programId:r,vaultPda:o,sessionPubkey:new Uint8Array(u),maxAmount:p,expiresAt:f,allowedCounterparty:i,nonce:a}}var j=10,y=class extends Error{constructor(n,r){super(`On-chain verification failed: ${n}${r?` (${r})`:""}`);this.reason=n;this.name="OnChainVerificationError"}};async function O(e,t){let n=await e.getAccountInfo(t,"finalized");if(!n)throw new y("vault_not_found",t.toBase58());if(!n.owner.equals(b.DEXTER_VAULT_PROGRAM_ID))throw new y("wrong_program",`owner ${n.owner.toBase58()} is not the vault program`);let r=n.data,o=new Uint8Array(r.slice(j,j+33)),a=84+(r[83]===1?48:0)+32+32;if(r[a]!==1)return{passkeyPubkey:o,activeSessionPubkey:null};let s=a+1,h=new Uint8Array(r.slice(s,s+32));return{passkeyPubkey:o,activeSessionPubkey:h}}async function k(e,t){let n=await O(e,t.vaultPda);if(n.activeSessionPubkey===null)throw new y("session_not_active","vault has no active_session \u2014 was it revoked?");if(!me(n.activeSessionPubkey,t.sessionPubkey))throw new y("session_pubkey_mismatch",`on-chain ${J(n.activeSessionPubkey)} != registration ${J(t.sessionPubkey)}`);return{passkeyPubkey:n.passkeyPubkey}}var g=class extends Error{constructor(t){super(`Invalid voucher signature${t?`: ${t}`:""}`),this.name="InvalidVoucherSignatureError"}};function V(e,t){if(t.length!==32)throw new g(`channelIdBytes must be 32 bytes, got ${t.length}`);if(e.sessionPublicKey.length!==32)throw new g(`sessionPublicKey must be 32 bytes, got ${e.sessionPublicKey.length}`);if(e.sessionSignature.length!==64)throw new g(`sessionSignature must be 64 bytes, got ${e.sessionSignature.length}`);let n=(0,w.voucherPayloadMessage)({channelId:t,cumulativeAmount:BigInt(e.payload.cumulativeAmount),sequenceNumber:e.payload.sequenceNumber});if(!G.default.sign.detached.verify(n,e.sessionSignature,e.sessionPublicKey))throw new g("ed25519 verify rejected")}var c=class extends Error{constructor(n,r){super(`Scope violation: ${n}${r?` (${r})`:""}`);this.reason=n;this.name="ScopeViolationError"}};function U(e){let t=BigInt(e.voucher.payload.cumulativeAmount);if(t>e.registration.maxAmount)throw new c("cumulative_exceeds_cap",`${t} > ${e.registration.maxAmount}`);let n=BigInt(Math.floor(Date.now()/1e3));if(n>=e.registration.expiresAt)throw new c("session_expired",`now=${n} >= expiresAt=${e.registration.expiresAt}`);if(!e.registration.allowedCounterparty.equals(e.expectedCounterparty))throw new c("wrong_counterparty",`${e.registration.allowedCounterparty.toBase58()} != ${e.expectedCounterparty.toBase58()}`);if(e.previousCumulativeAtomic!==void 0){let r=BigInt(e.previousCumulativeAtomic);if(t<=r)throw new c("non_monotonic",`cumulative=${t} not > previous=${r}`)}}function me(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 J(e){let t="";for(let n of e)t+=n.toString(16).padStart(2,"0");return t}var S=require("fs"),$=require("path");function de(e){return{payload:e.payload,sessionPublicKey:N(e.sessionPublicKey),sessionRegistration:N(e.sessionRegistration),sessionSignature:N(e.sessionSignature)}}function pe(e){return{payload:e.payload,sessionPublicKey:H(e.sessionPublicKey),sessionRegistration:H(e.sessionRegistration),sessionSignature:H(e.sessionSignature)}}function N(e){let t="";for(let n of e)t+=n.toString(16).padStart(2,"0");return t}function H(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 A=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)}},_=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,$.join)(this.dir,`${t}.json`)}async get(t){try{let n=await S.promises.readFile(this.pathFor(t),"utf8");return pe(JSON.parse(n))}catch(n){if(n?.code==="ENOENT")return null;throw n}}async set(t,n){let r=this.pathFor(t);await S.promises.mkdir((0,$.dirname)(r),{recursive:!0});let o=`${r}.tmp`;await S.promises.writeFile(o,JSON.stringify(de(n))),await S.promises.rename(o,r)}async delete(t){try{await S.promises.unlink(this.pathFor(t))}catch(n){if(n?.code!=="ENOENT")throw n}}};var ye=require("@solana/web3.js"),fe=require("@noble/hashes/utils");var he=L(require("tweetnacl"),1);var ge=require("@noble/hashes/sha256");var W=6;function v(e,t=W){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 o=r.padEnd(t,"0"),u=`${n}${o}`.replace(/^0+(?=\d)/,"");return u===""?"0":u}function x(e,t=W){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",o=n.slice(-t).replace(/0+$/,"");return o?`${r}.${o}`:r}var R="x-tab-voucher",B=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)}},M=class{constructor(t,n,r,o){this.chargeImpl=o;this.channelId=t,this.network=n,this.cumulativeAtomic=r}channelId;network;sessionPublicKey=null;cumulativeAtomic;cumulative(){return x(this.cumulativeAtomic.toString())}bumpCumulative(t){this.cumulativeAtomic=t}setSessionPublicKey(t){this.sessionPublicKey=t}async charge(t){return this.chargeImpl(t)}};function we(e){if(typeof e!="string"||e.length===0)throw new l("signature_invalid",`missing ${R} header`);let t;try{t=Buffer.from(e,"base64").toString("utf8")}catch{throw new l("signature_invalid","malformed base64")}let n;try{n=JSON.parse(t)}catch{throw new l("signature_invalid","malformed JSON")}if(!n||typeof n!="object"||!n.payload||!n.sessionPublicKey)throw new l("signature_invalid","missing required fields");return{payload:n.payload,sessionPublicKey:E(n.sessionPublicKey),sessionRegistration:E(n.sessionRegistration),sessionSignature:E(n.sessionSignature)}}function E(e){if(typeof e!="string"||e.length%2!==0)throw new l("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 be(e){if(!/^[0-9a-f]{64}$/i.test(e))throw new l("signature_invalid",`channelId must be 64-char hex, got "${e}"`);return E(e)}function Y(e){let t=e.store??new A,n=new B,r=typeof e.sellerPubkey=="string"?new X.PublicKey(e.sellerPubkey):e.sellerPubkey,o=e.maxPerVoucherAtomic?BigInt(e.maxPerVoucherAtomic):BigInt(v(e.perUnit))*100n;return async(u,p,f)=>{try{let i=we(u.headers[R]),a=i.payload.channelId,d=be(a),s=n.get(a);if(!s){let C=I(i.sessionRegistration);await k(e.connection,C),s={registration:C,lastCumulativeAtomic:"0"},n.set(a,s)}V(i,d),U({registration:s.registration,voucher:i,expectedCounterparty:r,previousCumulativeAtomic:s.lastCumulativeAtomic});let h=BigInt(i.payload.cumulativeAmount),ee=BigInt(s.lastCumulativeAtomic),q=h-ee;if(q>o)throw new c("cumulative_exceeds_cap",`single voucher increment ${q} exceeds maxPerVoucherAtomic ${o}`);await t.set(a,i),n.update(a,i.payload.cumulativeAmount);let D=new M(a,e.network,h,async C=>{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.")});D.setSessionPublicKey(i.sessionPublicKey),u.tab=D,f()}catch(i){if(i instanceof l||i instanceof m||i instanceof y||i instanceof g||i instanceof c){p.status(402).json({error:"invalid_voucher",reason:i.reason??"unknown",detail:i.message});return}f(i)}}}function Z(e){if(!e.tab)throw new Error("req.tab is missing \u2014 did tabMiddleware run on this route?");return e.tab}function Q(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(v(n.cumulative())),o=t.perUnit?BigInt(v(t.perUnit)):null,u=0n,p=!1;function f(d=1){if(p)return Promise.reject(new Error("meter ended"));if(o===null)return Promise.reject(new Error("charge() needs options.perUnit"));let s=o*BigInt(d),h=u+s;return h>r?Promise.reject(new c("cumulative_exceeds_cap",`chunk would push request total to ${x(h.toString())} beyond voucher-authorized budget ${x(r.toString())}`)):(u=h,Promise.resolve())}function i(d){if(p)throw new Error("meter ended");let h=(typeof d=="string"?d:Buffer.from(d).toString("utf8")).replace(/\n/g,"\\n");e.write(`data: ${h}
2
2
 
3
3
  `)}function a(){p||(p=!0,e.write(`event: end
4
- data: {"chargedAtomic":"${c}"}
4
+ data: {"chargedAtomic":"${u}"}
5
5
 
6
6
  `),e.end())}return{charge:f,send:i,end:a}}0&&(module.exports={FileVoucherStore,InMemoryVoucherStore,InvalidRegistrationError,InvalidVoucherError,InvalidVoucherSignatureError,OnChainVerificationError,ScopeViolationError,TAB_VOUCHER_HEADER,enforceScope,openSse,parseRegistration,readVaultState,requireTab,tabMiddleware,verifyRegistrationOnChain,verifyVoucherSignature});
@@ -1,4 +1,4 @@
1
- import { c as TabNetworkId, H as HumanAmount, e as SignedVoucher, A as AtomicAmount } from '../../types-DIrmhiD-.cjs';
1
+ import { TabNetworkId, HumanAmount, SignedVoucher, AtomicAmount } from '@dexterai/vault/types';
2
2
  import { RequestHandler, Request, Response } from 'express';
3
3
  import { Connection, PublicKey } from '@solana/web3.js';
4
4
 
@@ -1,4 +1,4 @@
1
- import { c as TabNetworkId, H as HumanAmount, e as SignedVoucher, A as AtomicAmount } from '../../types-DIrmhiD-.js';
1
+ import { TabNetworkId, HumanAmount, SignedVoucher, AtomicAmount } from '@dexterai/vault/types';
2
2
  import { RequestHandler, Request, Response } from 'express';
3
3
  import { Connection, PublicKey } from '@solana/web3.js';
4
4
 
@@ -1,6 +1,6 @@
1
- var m=class extends Error{constructor(n,r){super(`Invalid voucher: ${n}${r?` (${r})`:""}`);this.reason=n;this.name="InvalidVoucherError"}};import{PublicKey as X}from"@solana/web3.js";import j from"tweetnacl";import{sha256 as fe}from"@noble/hashes/sha256";import{p256 as be}from"@noble/curves/p256";import{PublicKey as E}from"@solana/web3.js";var ie=(()=>{let e=new Uint8Array(32);return e.set(new TextEncoder().encode("OTS_SESSION_REGISTER_V1"),0),e})(),se=(()=>{let e=new Uint8Array(32);return e.set(new TextEncoder().encode("OTS_SESSION_REVOKE_V1"),0),e})();function U(e){if(e.channelId.length!==32)throw new Error(`channelId must be 32 bytes, got ${e.channelId.length}`);let t=new Uint8Array(44),n=new DataView(t.buffer),r=0;if(t.set(e.channelId,r),r+=32,n.setBigUint64(r,e.cumulativeAmount,!0),r+=8,n.setUint32(r,e.sequenceNumber>>>0,!0),r+=4,r!==44)throw new Error(`internal: voucher payload wrong length ${r}, expected 44`);return t}import{PublicKey as I,TransactionInstruction as ce}from"@solana/web3.js";var v=new I("Hg3wRaydFtJhYrdvYrKECacpJYDsC9Px7yKmpncj2fhc"),le=new I("Secp256r1SigVerify1111111111111111111111111"),me=new I("Sysvar1nstructions1111111111111111111111111"),de=new Uint8Array([69,94,60,44,49,199,183,233]),pe=new Uint8Array([81,192,32,110,104,116,144,151]);var T="OTS_SESSION_REGISTER_V1",d=class extends Error{constructor(n,r){super(`Invalid registration: ${n}${r?` (${r})`:""}`);this.reason=n;this.name="InvalidRegistrationError"}};function k(e){if(e.length!==180)throw new d("wrong_length",`expected 180, got ${e.length}`);let t=new TextDecoder().decode(e.slice(0,T.length));if(t!==T)throw new d("wrong_domain",`got "${t}"`);for(let s=T.length;s<32;s++)if(e[s]!==0)throw new d("wrong_domain",`non-NUL padding at byte ${s}`);let n=new DataView(e.buffer,e.byteOffset,e.byteLength),r=new E(e.slice(32,64)),i=new E(e.slice(64,96)),u=e.slice(96,128),p=n.getBigUint64(128,!0),g=n.getBigInt64(136,!0),o=new E(e.slice(144,176)),a=n.getUint32(176,!0);if(!r.equals(v))throw new d("wrong_program",`${r.toBase58()} is not ${v.toBase58()}`);if(p===0n)throw new d("cap_zero");let l=BigInt(Math.floor(Date.now()/1e3));if(g<=l)throw new d("expiry_in_past",`expires_at=${g}, now=${l}`);return{programId:r,vaultPda:i,sessionPubkey:new Uint8Array(u),maxAmount:p,expiresAt:g,allowedCounterparty:o,nonce:a}}var M=10,f=class extends Error{constructor(n,r){super(`On-chain verification failed: ${n}${r?` (${r})`:""}`);this.reason=n;this.name="OnChainVerificationError"}};async function L(e,t){let n=await e.getAccountInfo(t,"finalized");if(!n)throw new f("vault_not_found",t.toBase58());if(!n.owner.equals(v))throw new f("wrong_program",`owner ${n.owner.toBase58()} is not the vault program`);let r=n.data,i=new Uint8Array(r.slice(M,M+33)),a=84+(r[83]===1?48:0)+32+32;if(r[a]!==1)return{passkeyPubkey:i,activeSessionPubkey:null};let s=a+1,y=new Uint8Array(r.slice(s,s+32));return{passkeyPubkey:i,activeSessionPubkey:y}}async function _(e,t){let n=await L(e,t.vaultPda);if(n.activeSessionPubkey===null)throw new f("session_not_active","vault has no active_session \u2014 was it revoked?");if(!J(n.activeSessionPubkey,t.sessionPubkey))throw new f("session_pubkey_mismatch",`on-chain ${q(n.activeSessionPubkey)} != registration ${q(t.sessionPubkey)}`);return{passkeyPubkey:n.passkeyPubkey}}var h=class extends Error{constructor(t){super(`Invalid voucher signature${t?`: ${t}`:""}`),this.name="InvalidVoucherSignatureError"}};function R(e,t){if(t.length!==32)throw new h(`channelIdBytes must be 32 bytes, got ${t.length}`);if(e.sessionPublicKey.length!==32)throw new h(`sessionPublicKey must be 32 bytes, got ${e.sessionPublicKey.length}`);if(e.sessionSignature.length!==64)throw new h(`sessionSignature must be 64 bytes, got ${e.sessionSignature.length}`);let n=U({channelId:t,cumulativeAmount:BigInt(e.payload.cumulativeAmount),sequenceNumber:e.payload.sequenceNumber});if(!j.sign.detached.verify(n,e.sessionSignature,e.sessionPublicKey))throw new h("ed25519 verify rejected")}var c=class extends Error{constructor(n,r){super(`Scope violation: ${n}${r?` (${r})`:""}`);this.reason=n;this.name="ScopeViolationError"}};function V(e){let t=BigInt(e.voucher.payload.cumulativeAmount);if(t>e.registration.maxAmount)throw new c("cumulative_exceeds_cap",`${t} > ${e.registration.maxAmount}`);let n=BigInt(Math.floor(Date.now()/1e3));if(n>=e.registration.expiresAt)throw new c("session_expired",`now=${n} >= expiresAt=${e.registration.expiresAt}`);if(!e.registration.allowedCounterparty.equals(e.expectedCounterparty))throw new c("wrong_counterparty",`${e.registration.allowedCounterparty.toBase58()} != ${e.expectedCounterparty.toBase58()}`);if(e.previousCumulativeAtomic!==void 0){let r=BigInt(e.previousCumulativeAtomic);if(t<=r)throw new c("non_monotonic",`cumulative=${t} not > previous=${r}`)}}function J(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 q(e){let t="";for(let n of e)t+=n.toString(16).padStart(2,"0");return t}import{promises as w}from"fs";import{join as G,dirname as Y}from"path";function W(e){return{payload:e.payload,sessionPublicKey:C(e.sessionPublicKey),sessionRegistration:C(e.sessionRegistration),sessionSignature:C(e.sessionSignature)}}function Z(e){return{payload:e.payload,sessionPublicKey:$(e.sessionPublicKey),sessionRegistration:$(e.sessionRegistration),sessionSignature:$(e.sessionSignature)}}function C(e){let t="";for(let n of e)t+=n.toString(16).padStart(2,"0");return t}function $(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 b=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)}},K=class{constructor(t){this.dir=t}pathFor(t){if(!/^[a-z0-9_-]+$/i.test(t))throw new Error(`unsafe channelId for filesystem: ${t}`);return G(this.dir,`${t}.json`)}async get(t){try{let n=await w.readFile(this.pathFor(t),"utf8");return Z(JSON.parse(n))}catch(n){if(n?.code==="ENOENT")return null;throw n}}async set(t,n){let r=this.pathFor(t);await w.mkdir(Y(r),{recursive:!0});let i=`${r}.tmp`;await w.writeFile(i,JSON.stringify(W(n))),await w.rename(i,r)}async delete(t){try{await w.unlink(this.pathFor(t))}catch(n){if(n?.code!=="ENOENT")throw n}}};import{PublicKey as He}from"@solana/web3.js";import{bytesToHex as qe}from"@noble/hashes/utils";import _e from"tweetnacl";import{sha256 as Ce}from"@noble/hashes/sha256";var z=6;function A(e,t=z){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"),u=`${n}${i}`.replace(/^0+(?=\d)/,"");return u===""?"0":u}function S(e,t=z){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 N="x-tab-voucher",O=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)}},B=class{constructor(t,n,r,i){this.chargeImpl=i;this.channelId=t,this.network=n,this.cumulativeAtomic=r}channelId;network;sessionPublicKey=null;cumulativeAtomic;cumulative(){return S(this.cumulativeAtomic.toString())}bumpCumulative(t){this.cumulativeAtomic=t}setSessionPublicKey(t){this.sessionPublicKey=t}async charge(t){return this.chargeImpl(t)}};function Q(e){if(typeof e!="string"||e.length===0)throw new m("signature_invalid",`missing ${N} header`);let t;try{t=Buffer.from(e,"base64").toString("utf8")}catch{throw new m("signature_invalid","malformed base64")}let n;try{n=JSON.parse(t)}catch{throw new m("signature_invalid","malformed JSON")}if(!n||typeof n!="object"||!n.payload||!n.sessionPublicKey)throw new m("signature_invalid","missing required fields");return{payload:n.payload,sessionPublicKey:x(n.sessionPublicKey),sessionRegistration:x(n.sessionRegistration),sessionSignature:x(n.sessionSignature)}}function x(e){if(typeof e!="string"||e.length%2!==0)throw new m("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 ee(e){if(!/^[0-9a-f]{64}$/i.test(e))throw new m("signature_invalid",`channelId must be 64-char hex, got "${e}"`);return x(e)}function te(e){let t=e.store??new b,n=new O,r=typeof e.sellerPubkey=="string"?new X(e.sellerPubkey):e.sellerPubkey,i=e.maxPerVoucherAtomic?BigInt(e.maxPerVoucherAtomic):BigInt(A(e.perUnit))*100n;return async(u,p,g)=>{try{let o=Q(u.headers[N]),a=o.payload.channelId,l=ee(a),s=n.get(a);if(!s){let P=k(o.sessionRegistration);await _(e.connection,P),s={registration:P,lastCumulativeAtomic:"0"},n.set(a,s)}R(o,l),V({registration:s.registration,voucher:o,expectedCounterparty:r,previousCumulativeAtomic:s.lastCumulativeAtomic});let y=BigInt(o.payload.cumulativeAmount),F=BigInt(s.lastCumulativeAtomic),D=y-F;if(D>i)throw new c("cumulative_exceeds_cap",`single voucher increment ${D} exceeds maxPerVoucherAtomic ${i}`);await t.set(a,o),n.update(a,o.payload.cumulativeAmount);let H=new B(a,e.network,y,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.")});H.setSessionPublicKey(o.sessionPublicKey),u.tab=H,g()}catch(o){if(o instanceof m||o instanceof d||o instanceof f||o instanceof h||o instanceof c){p.status(402).json({error:"invalid_voucher",reason:o.reason??"unknown",detail:o.message});return}g(o)}}}function ne(e){if(!e.tab)throw new Error("req.tab is missing \u2014 did tabMiddleware run on this route?");return e.tab}function re(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(A(n.cumulative())),i=t.perUnit?BigInt(A(t.perUnit)):null,u=0n,p=!1;function g(l=1){if(p)return Promise.reject(new Error("meter ended"));if(i===null)return Promise.reject(new Error("charge() needs options.perUnit"));let s=i*BigInt(l),y=u+s;return y>r?Promise.reject(new c("cumulative_exceeds_cap",`chunk would push request total to ${S(y.toString())} beyond voucher-authorized budget ${S(r.toString())}`)):(u=y,Promise.resolve())}function o(l){if(p)throw new Error("meter ended");let y=(typeof l=="string"?l:Buffer.from(l).toString("utf8")).replace(/\n/g,"\\n");e.write(`data: ${y}
1
+ var m=class extends Error{constructor(n,r){super(`Invalid voucher: ${n}${r?` (${r})`:""}`);this.reason=n;this.name="InvalidVoucherError"}};import{PublicKey as Y}from"@solana/web3.js";import F from"tweetnacl";import{sha256 as Se}from"@noble/hashes/sha256";import{p256 as ve}from"@noble/curves/p256";import{PublicKey as I}from"@solana/web3.js";import{sessionRegisterMessage as se,sessionRevokeMessage as ae,voucherPayloadMessage as T,buildVoucherMessage as ce}from"@dexterai/vault/messages";import{buildRegisterSessionKeyInstruction as me,buildRevokeSessionKeyInstruction as de}from"@dexterai/vault/instructions";import{buildSecp256r1VerifyInstruction as he}from"@dexterai/vault/precompile";import{DEXTER_VAULT_PROGRAM_ID as v,SECP256R1_PROGRAM_ID as ye,INSTRUCTIONS_SYSVAR_ID as fe}from"@dexterai/vault/constants";var k="OTS_SESSION_REGISTER_V1",d=class extends Error{constructor(n,r){super(`Invalid registration: ${n}${r?` (${r})`:""}`);this.reason=n;this.name="InvalidRegistrationError"}};function V(e){if(e.length!==180)throw new d("wrong_length",`expected 180, got ${e.length}`);let t=new TextDecoder().decode(e.slice(0,k.length));if(t!==k)throw new d("wrong_domain",`got "${t}"`);for(let s=k.length;s<32;s++)if(e[s]!==0)throw new d("wrong_domain",`non-NUL padding at byte ${s}`);let n=new DataView(e.buffer,e.byteOffset,e.byteLength),r=new I(e.slice(32,64)),i=new I(e.slice(64,96)),c=e.slice(96,128),p=n.getBigUint64(128,!0),g=n.getBigInt64(136,!0),o=new I(e.slice(144,176)),a=n.getUint32(176,!0);if(!r.equals(v))throw new d("wrong_program",`${r.toBase58()} is not ${v.toBase58()}`);if(p===0n)throw new d("cap_zero");let l=BigInt(Math.floor(Date.now()/1e3));if(g<=l)throw new d("expiry_in_past",`expires_at=${g}, now=${l}`);return{programId:r,vaultPda:i,sessionPubkey:new Uint8Array(c),maxAmount:p,expiresAt:g,allowedCounterparty:o,nonce:a}}var M=10,f=class extends Error{constructor(n,r){super(`On-chain verification failed: ${n}${r?` (${r})`:""}`);this.reason=n;this.name="OnChainVerificationError"}};async function D(e,t){let n=await e.getAccountInfo(t,"finalized");if(!n)throw new f("vault_not_found",t.toBase58());if(!n.owner.equals(v))throw new f("wrong_program",`owner ${n.owner.toBase58()} is not the vault program`);let r=n.data,i=new Uint8Array(r.slice(M,M+33)),a=84+(r[83]===1?48:0)+32+32;if(r[a]!==1)return{passkeyPubkey:i,activeSessionPubkey:null};let s=a+1,h=new Uint8Array(r.slice(s,s+32));return{passkeyPubkey:i,activeSessionPubkey:h}}async function U(e,t){let n=await D(e,t.vaultPda);if(n.activeSessionPubkey===null)throw new f("session_not_active","vault has no active_session \u2014 was it revoked?");if(!j(n.activeSessionPubkey,t.sessionPubkey))throw new f("session_pubkey_mismatch",`on-chain ${q(n.activeSessionPubkey)} != registration ${q(t.sessionPubkey)}`);return{passkeyPubkey:n.passkeyPubkey}}var y=class extends Error{constructor(t){super(`Invalid voucher signature${t?`: ${t}`:""}`),this.name="InvalidVoucherSignatureError"}};function _(e,t){if(t.length!==32)throw new y(`channelIdBytes must be 32 bytes, got ${t.length}`);if(e.sessionPublicKey.length!==32)throw new y(`sessionPublicKey must be 32 bytes, got ${e.sessionPublicKey.length}`);if(e.sessionSignature.length!==64)throw new y(`sessionSignature must be 64 bytes, got ${e.sessionSignature.length}`);let n=T({channelId:t,cumulativeAmount:BigInt(e.payload.cumulativeAmount),sequenceNumber:e.payload.sequenceNumber});if(!F.sign.detached.verify(n,e.sessionSignature,e.sessionPublicKey))throw new y("ed25519 verify rejected")}var u=class extends Error{constructor(n,r){super(`Scope violation: ${n}${r?` (${r})`:""}`);this.reason=n;this.name="ScopeViolationError"}};function $(e){let t=BigInt(e.voucher.payload.cumulativeAmount);if(t>e.registration.maxAmount)throw new u("cumulative_exceeds_cap",`${t} > ${e.registration.maxAmount}`);let n=BigInt(Math.floor(Date.now()/1e3));if(n>=e.registration.expiresAt)throw new u("session_expired",`now=${n} >= expiresAt=${e.registration.expiresAt}`);if(!e.registration.allowedCounterparty.equals(e.expectedCounterparty))throw new u("wrong_counterparty",`${e.registration.allowedCounterparty.toBase58()} != ${e.expectedCounterparty.toBase58()}`);if(e.previousCumulativeAtomic!==void 0){let r=BigInt(e.previousCumulativeAtomic);if(t<=r)throw new u("non_monotonic",`cumulative=${t} not > previous=${r}`)}}function j(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 q(e){let t="";for(let n of e)t+=n.toString(16).padStart(2,"0");return t}import{promises as w}from"fs";import{join as J,dirname as G}from"path";function W(e){return{payload:e.payload,sessionPublicKey:E(e.sessionPublicKey),sessionRegistration:E(e.sessionRegistration),sessionSignature:E(e.sessionSignature)}}function X(e){return{payload:e.payload,sessionPublicKey:R(e.sessionPublicKey),sessionRegistration:R(e.sessionRegistration),sessionSignature:R(e.sessionSignature)}}function E(e){let t="";for(let n of e)t+=n.toString(16).padStart(2,"0");return t}function R(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 b=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)}},C=class{constructor(t){this.dir=t}pathFor(t){if(!/^[a-z0-9_-]+$/i.test(t))throw new Error(`unsafe channelId for filesystem: ${t}`);return J(this.dir,`${t}.json`)}async get(t){try{let n=await w.readFile(this.pathFor(t),"utf8");return X(JSON.parse(n))}catch(n){if(n?.code==="ENOENT")return null;throw n}}async set(t,n){let r=this.pathFor(t);await w.mkdir(G(r),{recursive:!0});let i=`${r}.tmp`;await w.writeFile(i,JSON.stringify(W(n))),await w.rename(i,r)}async delete(t){try{await w.unlink(this.pathFor(t))}catch(n){if(n?.code!=="ENOENT")throw n}}};import{PublicKey as ze}from"@solana/web3.js";import{bytesToHex as Fe}from"@noble/hashes/utils";import Re from"tweetnacl";import{sha256 as Oe}from"@noble/hashes/sha256";var z=6;function S(e,t=z){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"),c=`${n}${i}`.replace(/^0+(?=\d)/,"");return c===""?"0":c}function A(e,t=z){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 N="x-tab-voucher",K=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)}},O=class{constructor(t,n,r,i){this.chargeImpl=i;this.channelId=t,this.network=n,this.cumulativeAtomic=r}channelId;network;sessionPublicKey=null;cumulativeAtomic;cumulative(){return A(this.cumulativeAtomic.toString())}bumpCumulative(t){this.cumulativeAtomic=t}setSessionPublicKey(t){this.sessionPublicKey=t}async charge(t){return this.chargeImpl(t)}};function Z(e){if(typeof e!="string"||e.length===0)throw new m("signature_invalid",`missing ${N} header`);let t;try{t=Buffer.from(e,"base64").toString("utf8")}catch{throw new m("signature_invalid","malformed base64")}let n;try{n=JSON.parse(t)}catch{throw new m("signature_invalid","malformed JSON")}if(!n||typeof n!="object"||!n.payload||!n.sessionPublicKey)throw new m("signature_invalid","missing required fields");return{payload:n.payload,sessionPublicKey:x(n.sessionPublicKey),sessionRegistration:x(n.sessionRegistration),sessionSignature:x(n.sessionSignature)}}function x(e){if(typeof e!="string"||e.length%2!==0)throw new m("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 Q(e){if(!/^[0-9a-f]{64}$/i.test(e))throw new m("signature_invalid",`channelId must be 64-char hex, got "${e}"`);return x(e)}function ee(e){let t=e.store??new b,n=new K,r=typeof e.sellerPubkey=="string"?new Y(e.sellerPubkey):e.sellerPubkey,i=e.maxPerVoucherAtomic?BigInt(e.maxPerVoucherAtomic):BigInt(S(e.perUnit))*100n;return async(c,p,g)=>{try{let o=Z(c.headers[N]),a=o.payload.channelId,l=Q(a),s=n.get(a);if(!s){let P=V(o.sessionRegistration);await U(e.connection,P),s={registration:P,lastCumulativeAtomic:"0"},n.set(a,s)}_(o,l),$({registration:s.registration,voucher:o,expectedCounterparty:r,previousCumulativeAtomic:s.lastCumulativeAtomic});let h=BigInt(o.payload.cumulativeAmount),L=BigInt(s.lastCumulativeAtomic),H=h-L;if(H>i)throw new u("cumulative_exceeds_cap",`single voucher increment ${H} exceeds maxPerVoucherAtomic ${i}`);await t.set(a,o),n.update(a,o.payload.cumulativeAmount);let B=new O(a,e.network,h,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.")});B.setSessionPublicKey(o.sessionPublicKey),c.tab=B,g()}catch(o){if(o instanceof m||o instanceof d||o instanceof f||o instanceof y||o instanceof u){p.status(402).json({error:"invalid_voucher",reason:o.reason??"unknown",detail:o.message});return}g(o)}}}function te(e){if(!e.tab)throw new Error("req.tab is missing \u2014 did tabMiddleware run on this route?");return e.tab}function ne(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(S(n.cumulative())),i=t.perUnit?BigInt(S(t.perUnit)):null,c=0n,p=!1;function g(l=1){if(p)return Promise.reject(new Error("meter ended"));if(i===null)return Promise.reject(new Error("charge() needs options.perUnit"));let s=i*BigInt(l),h=c+s;return h>r?Promise.reject(new u("cumulative_exceeds_cap",`chunk would push request total to ${A(h.toString())} beyond voucher-authorized budget ${A(r.toString())}`)):(c=h,Promise.resolve())}function o(l){if(p)throw new Error("meter ended");let h=(typeof l=="string"?l:Buffer.from(l).toString("utf8")).replace(/\n/g,"\\n");e.write(`data: ${h}
2
2
 
3
3
  `)}function a(){p||(p=!0,e.write(`event: end
4
- data: {"chargedAtomic":"${u}"}
4
+ data: {"chargedAtomic":"${c}"}
5
5
 
6
- `),e.end())}return{charge:g,send:o,end:a}}export{K as FileVoucherStore,b as InMemoryVoucherStore,d as InvalidRegistrationError,m as InvalidVoucherError,h as InvalidVoucherSignatureError,f as OnChainVerificationError,c as ScopeViolationError,N as TAB_VOUCHER_HEADER,V as enforceScope,re as openSse,k as parseRegistration,L as readVaultState,ne as requireTab,te as tabMiddleware,_ as verifyRegistrationOnChain,R as verifyVoucherSignature};
6
+ `),e.end())}return{charge:g,send:o,end:a}}export{C as FileVoucherStore,b as InMemoryVoucherStore,d as InvalidRegistrationError,m as InvalidVoucherError,y as InvalidVoucherSignatureError,f as OnChainVerificationError,u as ScopeViolationError,N as TAB_VOUCHER_HEADER,$ as enforceScope,ne as openSse,V as parseRegistration,D as readVaultState,te as requireTab,ee as tabMiddleware,U as verifyRegistrationOnChain,_ as verifyVoucherSignature};
@@ -1,87 +1,14 @@
1
+ import { TabNetworkId, HumanAmount, SessionScope, SessionKey, VoucherPayload, SignedVoucher, AtomicAmount } from '@dexterai/vault/types';
2
+
1
3
  /**
2
4
  * @dexterai/x402/tab — type contract for the OTS-backed streaming payment module.
3
5
  *
4
- * Peer of `batch-settlement`. Where `batch-settlement` is for N discrete paid
5
- * requests against one escrow channel, `tab` is for *continuous metered
6
- * consumption* tokens, bytes, frames, seconds settled on close.
7
- *
8
- * Full design: see `docs/DESIGN-tab-streaming.md`. This file is the contract
9
- * lock for Phase 1: the public types and option shapes that downstream phases
10
- * must implement against without drift.
11
- */
12
- /**
13
- * CAIP-2-style network identifier. The buyer-side `openTab` accepts a string
14
- * here so future networks (EVM L2s) require no API change — only a new
15
- * VaultAdapter implementation.
16
- */
17
- type TabNetworkId = 'solana:mainnet' | (string & {});
18
- /**
19
- * Atomic-unit cumulative amount the seller is asking the buyer to authorize
20
- * for a single voucher. Strings to avoid bigint JSON-serialization headaches
21
- * across language boundaries.
6
+ * Session/voucher protocol types moved to @dexterai/vault/types in the v0.1
7
+ * extract. Re-exported here so existing consumers of `@dexterai/x402/tab`
8
+ * see zero import-path changes. Tab-runtime types (VaultAdapter, OpenTabOptions,
9
+ * Tab, errors) stay local — they're HTTP/SSE-shaped, not protocol-shaped.
22
10
  */
23
- type AtomicAmount = string;
24
- /**
25
- * Human-readable amount (e.g. "0.001" USDC). Used at SDK boundaries; converted
26
- * to atomic units internally per the vault's token decimals.
27
- */
28
- type HumanAmount = string;
29
- /**
30
- * Scope of a session key — the limits the passkey embeds into its
31
- * registration signature. The on-chain program (via Swig) and the seller's
32
- * middleware (locally) both enforce these.
33
- */
34
- interface SessionScope {
35
- /** The specific tab this session is bound to. */
36
- channelId: string;
37
- /** Cumulative cap, atomic units. The session-key cannot sign beyond this. */
38
- maxAmountAtomic: AtomicAmount;
39
- /** Wall-clock expiry (unix seconds). Hard deadline regardless of usage. */
40
- expiresAtUnix: number;
41
- /** Counterparty restriction — typically the seller's address. */
42
- allowedCounterparty: string;
43
- }
44
- /**
45
- * In-memory session key. NEVER persisted to disk. A crashed process forfeits
46
- * the session and re-prompts the passkey on the next attempt — this is the
47
- * right default because a session key on disk is a real attack surface.
48
- */
49
- interface SessionKey {
50
- /** Public key the seller verifies signatures against. */
51
- publicKey: Uint8Array;
52
- /** Private key — in-memory only. */
53
- privateKey: Uint8Array;
54
- /** Limits this session may operate within. */
55
- scope: SessionScope;
56
- /** The passkey signature authorizing this session. The seller verifies it
57
- * against the vault's registered passkey on every voucher. */
58
- registration: Uint8Array;
59
- }
60
- /**
61
- * What the buyer signs per stream chunk, and what the seller verifies before
62
- * delivering. Cumulative-amount semantics: each voucher represents the TOTAL
63
- * owed so far, not the incremental amount. Replay-resistant because vouchers
64
- * monotonically increase.
65
- */
66
- interface VoucherPayload {
67
- channelId: string;
68
- /** Total owed so far, atomic units. Must strictly exceed the prior voucher. */
69
- cumulativeAmount: AtomicAmount;
70
- /** Monotonic sequence number. Replay protection within a tab. */
71
- sequenceNumber: number;
72
- }
73
- /**
74
- * The full voucher as sent over the wire: payload + session signature +
75
- * the registration that authorizes the signing session key. The seller's
76
- * middleware verifies the registration's passkey signature once per session,
77
- * caches the result, and verifies only the session-key signature per chunk.
78
- */
79
- interface SignedVoucher {
80
- payload: VoucherPayload;
81
- sessionPublicKey: Uint8Array;
82
- sessionRegistration: Uint8Array;
83
- sessionSignature: Uint8Array;
84
- }
11
+
85
12
  /**
86
13
  * Adapter for a specific chain's OTS vault implementation. The SDK calls into
87
14
  * `authorizeSession()` ONCE per tab and `signWithSession()` many times. The
@@ -231,4 +158,4 @@ declare class TabClosedError extends Error {
231
158
  constructor(channelId: string);
232
159
  }
233
160
 
234
- export { type AtomicAmount as A, type HumanAmount as H, type OpenTabOptions as O, type ResumeTabOptions as R, type SessionScope as S, type Tab as T, UnsupportedNetworkError as U, type VoucherPayload as V, type TabState as a, type TabCloseResult as b, type TabNetworkId as c, type SessionKey as d, type SignedVoucher as e, type VaultAdapter as f, SessionScopeExceededError as g, TabClosedError as h };
161
+ export { type OpenTabOptions as O, type ResumeTabOptions as R, SessionScopeExceededError as S, type Tab as T, UnsupportedNetworkError as U, type VaultAdapter as V, type TabState as a, type TabCloseResult as b, TabClosedError as c };
@@ -1,87 +1,14 @@
1
+ import { TabNetworkId, HumanAmount, SessionScope, SessionKey, VoucherPayload, SignedVoucher, AtomicAmount } from '@dexterai/vault/types';
2
+
1
3
  /**
2
4
  * @dexterai/x402/tab — type contract for the OTS-backed streaming payment module.
3
5
  *
4
- * Peer of `batch-settlement`. Where `batch-settlement` is for N discrete paid
5
- * requests against one escrow channel, `tab` is for *continuous metered
6
- * consumption* tokens, bytes, frames, seconds settled on close.
7
- *
8
- * Full design: see `docs/DESIGN-tab-streaming.md`. This file is the contract
9
- * lock for Phase 1: the public types and option shapes that downstream phases
10
- * must implement against without drift.
11
- */
12
- /**
13
- * CAIP-2-style network identifier. The buyer-side `openTab` accepts a string
14
- * here so future networks (EVM L2s) require no API change — only a new
15
- * VaultAdapter implementation.
16
- */
17
- type TabNetworkId = 'solana:mainnet' | (string & {});
18
- /**
19
- * Atomic-unit cumulative amount the seller is asking the buyer to authorize
20
- * for a single voucher. Strings to avoid bigint JSON-serialization headaches
21
- * across language boundaries.
6
+ * Session/voucher protocol types moved to @dexterai/vault/types in the v0.1
7
+ * extract. Re-exported here so existing consumers of `@dexterai/x402/tab`
8
+ * see zero import-path changes. Tab-runtime types (VaultAdapter, OpenTabOptions,
9
+ * Tab, errors) stay local — they're HTTP/SSE-shaped, not protocol-shaped.
22
10
  */
23
- type AtomicAmount = string;
24
- /**
25
- * Human-readable amount (e.g. "0.001" USDC). Used at SDK boundaries; converted
26
- * to atomic units internally per the vault's token decimals.
27
- */
28
- type HumanAmount = string;
29
- /**
30
- * Scope of a session key — the limits the passkey embeds into its
31
- * registration signature. The on-chain program (via Swig) and the seller's
32
- * middleware (locally) both enforce these.
33
- */
34
- interface SessionScope {
35
- /** The specific tab this session is bound to. */
36
- channelId: string;
37
- /** Cumulative cap, atomic units. The session-key cannot sign beyond this. */
38
- maxAmountAtomic: AtomicAmount;
39
- /** Wall-clock expiry (unix seconds). Hard deadline regardless of usage. */
40
- expiresAtUnix: number;
41
- /** Counterparty restriction — typically the seller's address. */
42
- allowedCounterparty: string;
43
- }
44
- /**
45
- * In-memory session key. NEVER persisted to disk. A crashed process forfeits
46
- * the session and re-prompts the passkey on the next attempt — this is the
47
- * right default because a session key on disk is a real attack surface.
48
- */
49
- interface SessionKey {
50
- /** Public key the seller verifies signatures against. */
51
- publicKey: Uint8Array;
52
- /** Private key — in-memory only. */
53
- privateKey: Uint8Array;
54
- /** Limits this session may operate within. */
55
- scope: SessionScope;
56
- /** The passkey signature authorizing this session. The seller verifies it
57
- * against the vault's registered passkey on every voucher. */
58
- registration: Uint8Array;
59
- }
60
- /**
61
- * What the buyer signs per stream chunk, and what the seller verifies before
62
- * delivering. Cumulative-amount semantics: each voucher represents the TOTAL
63
- * owed so far, not the incremental amount. Replay-resistant because vouchers
64
- * monotonically increase.
65
- */
66
- interface VoucherPayload {
67
- channelId: string;
68
- /** Total owed so far, atomic units. Must strictly exceed the prior voucher. */
69
- cumulativeAmount: AtomicAmount;
70
- /** Monotonic sequence number. Replay protection within a tab. */
71
- sequenceNumber: number;
72
- }
73
- /**
74
- * The full voucher as sent over the wire: payload + session signature +
75
- * the registration that authorizes the signing session key. The seller's
76
- * middleware verifies the registration's passkey signature once per session,
77
- * caches the result, and verifies only the session-key signature per chunk.
78
- */
79
- interface SignedVoucher {
80
- payload: VoucherPayload;
81
- sessionPublicKey: Uint8Array;
82
- sessionRegistration: Uint8Array;
83
- sessionSignature: Uint8Array;
84
- }
11
+
85
12
  /**
86
13
  * Adapter for a specific chain's OTS vault implementation. The SDK calls into
87
14
  * `authorizeSession()` ONCE per tab and `signWithSession()` many times. The
@@ -231,4 +158,4 @@ declare class TabClosedError extends Error {
231
158
  constructor(channelId: string);
232
159
  }
233
160
 
234
- export { type AtomicAmount as A, type HumanAmount as H, type OpenTabOptions as O, type ResumeTabOptions as R, type SessionScope as S, type Tab as T, UnsupportedNetworkError as U, type VoucherPayload as V, type TabState as a, type TabCloseResult as b, type TabNetworkId as c, type SessionKey as d, type SignedVoucher as e, type VaultAdapter as f, SessionScopeExceededError as g, TabClosedError as h };
161
+ export { type OpenTabOptions as O, type ResumeTabOptions as R, SessionScopeExceededError as S, type Tab as T, UnsupportedNetworkError as U, type VaultAdapter as V, type TabState as a, type TabCloseResult as b, TabClosedError as c };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dexterai/x402",
3
- "version": "3.9.1",
3
+ "version": "3.11.0",
4
4
  "description": "Full-stack x402 SDK - add paid API monetization to any endpoint. Express middleware, React hooks, Access Pass, dynamic pricing. Solana, Base, Polygon, Arbitrum, Optimism, Avalanche, SKALE.",
5
5
  "author": "Dexter",
6
6
  "license": "MIT",
@@ -76,6 +76,7 @@
76
76
  "release:major": "npm version major && npm publish --access public"
77
77
  },
78
78
  "dependencies": {
79
+ "@dexterai/vault": "^0.1.3",
79
80
  "@dexterai/x402-ads-types": "^0.2.0",
80
81
  "@dexterai/x402-core": "^1.4.0",
81
82
  "@noble/curves": "^1.9.7",