@dexterai/x402 3.9.1 → 3.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/batch-settlement/index.cjs +1 -1
- package/dist/batch-settlement/index.js +1 -1
- package/dist/tab/adapters/solana/index.d.cts +5 -4
- package/dist/tab/adapters/solana/index.d.ts +5 -4
- package/dist/tab/index.cjs +3 -3
- package/dist/tab/index.d.cts +8 -6
- package/dist/tab/index.d.ts +8 -6
- package/dist/tab/index.js +5 -5
- package/dist/tab/seller/index.cjs +3 -3
- package/dist/tab/seller/index.js +3 -3
- package/package.json +1 -1
|
@@ -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://
|
|
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://
|
|
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};
|
|
@@ -78,10 +78,11 @@ interface SignedPasskeyPayload {
|
|
|
78
78
|
* at close.
|
|
79
79
|
*
|
|
80
80
|
* The adapter does NOT touch pending_voucher_count. That counter belongs
|
|
81
|
-
* to the facilitator's dexter_authority and is
|
|
82
|
-
*
|
|
83
|
-
*
|
|
84
|
-
*
|
|
81
|
+
* to the facilitator's dexter_authority and is decremented inside the
|
|
82
|
+
* facilitator's `POST /tab/settle` tx (via the new vault.settle_tab_voucher
|
|
83
|
+
* instruction) atomically with the USDC transfer. The SDK's `Tab.close()`
|
|
84
|
+
* POSTs the final voucher to the facilitator; this adapter only owns the
|
|
85
|
+
* passkey-signed session register/revoke layer.
|
|
85
86
|
*/
|
|
86
87
|
|
|
87
88
|
interface PasskeySigner {
|
|
@@ -78,10 +78,11 @@ interface SignedPasskeyPayload {
|
|
|
78
78
|
* at close.
|
|
79
79
|
*
|
|
80
80
|
* The adapter does NOT touch pending_voucher_count. That counter belongs
|
|
81
|
-
* to the facilitator's dexter_authority and is
|
|
82
|
-
*
|
|
83
|
-
*
|
|
84
|
-
*
|
|
81
|
+
* to the facilitator's dexter_authority and is decremented inside the
|
|
82
|
+
* facilitator's `POST /tab/settle` tx (via the new vault.settle_tab_voucher
|
|
83
|
+
* instruction) atomically with the USDC transfer. The SDK's `Tab.close()`
|
|
84
|
+
* POSTs the final voucher to the facilitator; this adapter only owns the
|
|
85
|
+
* passkey-signed session register/revoke layer.
|
|
85
86
|
*/
|
|
86
87
|
|
|
87
88
|
interface PasskeySigner {
|
package/dist/tab/index.cjs
CHANGED
|
@@ -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
|
|
1
|
+
"use strict";var P=Object.create;var y=Object.defineProperty;var k=Object.getOwnPropertyDescriptor;var E=Object.getOwnPropertyNames;var K=Object.getPrototypeOf,$=Object.prototype.hasOwnProperty;var C=(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))!$.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 M={};C(M,{DEFAULT_FACILITATOR_URL:()=>A,SessionScopeExceededError:()=>m,TabClosedError:()=>c,UnsupportedNetworkError:()=>l,atomicToHuman:()=>p,humanToAtomic:()=>h,openTab:()=>T,resumeTab:()=>U});module.exports=N(M);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 w=require("@solana/web3.js"),u=require("@noble/hashes/utils");var V=R(require("tweetnacl"),1);var F=(()=>{let e=new Uint8Array(32);return e.set(new TextEncoder().encode("OTS_SESSION_REGISTER_V1"),0),e})(),J=(()=>{let e=new Uint8Array(32);return e.set(new TextEncoder().encode("OTS_SESSION_REVOKE_V1"),0),e})();var S=require("@noble/hashes/sha256");function x(e){let t=new Uint8Array(8);new DataView(t.buffer).setBigUint64(0,e.nonce,!0);let n=new TextEncoder().encode(e.sellerUrl),r=S.sha256.create();return r.update(e.vaultPda.toBytes()),r.update(n),r.update(t),r.digest()}var O=3600,A="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;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:p(this.cumulativeAtomic.toString()),remaining:p(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 _(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:p(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 T(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 w.PublicKey(e.vault.vaultPda),r=x({vaultPda:n,sellerUrl:e.seller,nonce:BigInt(t)}),o=(0,u.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 a=e.sessionDuration??O,d=Math.floor(Date.now()/1e3)+a,b={channelId:o,maxAmountAtomic:i.toString(),expiresAtUnix:d,allowedCounterparty:H(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:d,facilitatorUrl:e.facilitatorUrl??A})}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 H(e){if(/^[1-9A-HJ-NP-Za-km-z]{32,44}$/.test(e))try{return new w.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*_(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
|
|
4
|
-
`);yield new TextEncoder().encode(b)}}}}finally{t.releaseLock()}}function
|
|
3
|
+
`))!==-1;){let a=r.slice(0,i);r=r.slice(i+2);let d=D(a);if(d.eventName==="end")return;if(d.data!==null){let b=d.data.replace(/\\n/g,`
|
|
4
|
+
`);yield new TextEncoder().encode(b)}}}}finally{t.releaseLock()}}function D(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});
|
package/dist/tab/index.d.cts
CHANGED
|
@@ -5,13 +5,15 @@ export { d as SessionKey, S as SessionScope, g as SessionScopeExceededError, e a
|
|
|
5
5
|
* The `Tab` runtime — the live object returned by `openTab()`.
|
|
6
6
|
*
|
|
7
7
|
* Owns: the session key, the channel id, the cumulative-amount counter,
|
|
8
|
-
* the voucher sequence counter
|
|
9
|
-
* requests and `close()` for
|
|
8
|
+
* the voucher sequence counter, the last signed voucher. Exposes:
|
|
9
|
+
* `stream()` for paid streamed requests and `close()` for on-chain
|
|
10
|
+
* settle + session revocation.
|
|
10
11
|
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
12
|
+
* As of `@dexterai/x402@3.10.0`, `close()` POSTs the final voucher to
|
|
13
|
+
* the facilitator's `POST /tab/settle` endpoint BEFORE revoking the
|
|
14
|
+
* session, so `TabCloseResult.settleTx` is the real on-chain settlement
|
|
15
|
+
* signature (USDC swig → seller ATA, atomic with the session's `spent`
|
|
16
|
+
* advance + `pending_voucher_count` decrement).
|
|
15
17
|
*/
|
|
16
18
|
|
|
17
19
|
/** Live Dexter x402 facilitator API. NOT facilitator.dexter.cash —
|
package/dist/tab/index.d.ts
CHANGED
|
@@ -5,13 +5,15 @@ export { d as SessionKey, S as SessionScope, g as SessionScopeExceededError, e a
|
|
|
5
5
|
* The `Tab` runtime — the live object returned by `openTab()`.
|
|
6
6
|
*
|
|
7
7
|
* Owns: the session key, the channel id, the cumulative-amount counter,
|
|
8
|
-
* the voucher sequence counter
|
|
9
|
-
* requests and `close()` for
|
|
8
|
+
* the voucher sequence counter, the last signed voucher. Exposes:
|
|
9
|
+
* `stream()` for paid streamed requests and `close()` for on-chain
|
|
10
|
+
* settle + session revocation.
|
|
10
11
|
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
12
|
+
* As of `@dexterai/x402@3.10.0`, `close()` POSTs the final voucher to
|
|
13
|
+
* the facilitator's `POST /tab/settle` endpoint BEFORE revoking the
|
|
14
|
+
* session, so `TabCloseResult.settleTx` is the real on-chain settlement
|
|
15
|
+
* signature (USDC swig → seller ATA, atomic with the session's `spent`
|
|
16
|
+
* advance + `pending_voucher_count` decrement).
|
|
15
17
|
*/
|
|
16
18
|
|
|
17
19
|
/** Live Dexter x402 facilitator API. NOT facilitator.dexter.cash —
|
package/dist/tab/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
var
|
|
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 O from"tweetnacl";var $=(()=>{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 x}from"@noble/hashes/sha256";function g(e){let t=new Uint8Array(8);new DataView(t.buffer).setBigUint64(0,e.nonce,!0);let r=new TextEncoder().encode(e.sellerUrl),n=x.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 h(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 b=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 k(s.body)}async close(){if(this.closed)throw new c(this.channelId);let t="";return this.lastSignedVoucher&&this.cumulativeAtomic>0n&&(t=await T(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 T(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=g({vaultPda:r,sellerUrl:e.seller,nonce:BigInt(t)}),i=u(n),o=BigInt(h(e.perUnitCap)),s=BigInt(h(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,y={channelId:i,maxAmountAtomic:s.toString(),expiresAtUnix:l,allowedCounterparty:P(e.seller)},S=await e.vault.authorizeSession(y);return new b({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 I(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*k(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
|
|
4
|
-
`);yield new TextEncoder().encode(y)}}}}finally{
|
|
5
|
-
`))
|
|
6
|
-
`):null}}export{
|
|
3
|
+
`))!==-1;){let a=n.slice(0,s);n=n.slice(s+2);let l=E(a);if(l.eventName==="end")return;if(l.data!==null){let y=l.data.replace(/\\n/g,`
|
|
4
|
+
`);yield new TextEncoder().encode(y)}}}}finally{t.releaseLock()}}function E(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,h as humanToAtomic,U as openTab,I 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 x=Object.defineProperty;var ne=Object.getOwnPropertyDescriptor;var re=Object.getOwnPropertyNames;var ie=Object.getPrototypeOf,oe=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 i of re(t))!oe.call(e,i)&&i!==n&&x(e,i,{get:()=>t[i],enumerable:!(r=ne(t,i))||r.enumerable});return e};var F=(e,t,n)=>(n=e!=null?te(ie(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:()=>d,InvalidVoucherError:()=>l,InvalidVoucherSignatureError:()=>g,OnChainVerificationError:()=>h,ScopeViolationError:()=>c,TAB_VOUCHER_HEADER:()=>$,enforceScope:()=>k,openSse:()=>Q,parseRegistration:()=>I,readVaultState:()=>N,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),ce=require("@noble/hashes/sha256"),ue=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",d=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 d("wrong_length",`expected 180, got ${e.length}`);let t=new TextDecoder().decode(e.slice(0,O.length));if(t!==O)throw new d("wrong_domain",`got "${t}"`);for(let s=O.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 U.PublicKey(e.slice(32,64)),i=new U.PublicKey(e.slice(64,96)),u=e.slice(96,128),p=n.getBigUint64(128,!0),f=n.getBigInt64(136,!0),o=new U.PublicKey(e.slice(144,176)),a=n.getUint32(176,!0);if(!r.equals(P))throw new d("wrong_program",`${r.toBase58()} is not ${P.toBase58()}`);if(p===0n)throw new d("cap_zero");let m=BigInt(Math.floor(Date.now()/1e3));if(f<=m)throw new d("expiry_in_past",`expires_at=${f}, now=${m}`);return{programId:r,vaultPda:i,sessionPubkey:new Uint8Array(u),maxAmount:p,expiresAt:f,allowedCounterparty:o,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 N(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,i=new Uint8Array(r.slice(J,J+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(e,t){let n=await N(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 c=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 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 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 de(e){return{payload:e.payload,sessionPublicKey:B(e.sessionPublicKey),sessionRegistration:B(e.sessionRegistration),sessionSignature:B(e.sessionSignature)}}function me(e){return{payload:e.payload,sessionPublicKey:D(e.sessionPublicKey),sessionRegistration:D(e.sessionRegistration),sessionSignature:D(e.sessionSignature)}}function B(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 me(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 i=`${r}.tmp`;await w.promises.writeFile(i,JSON.stringify(de(n))),await w.promises.rename(i,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 i=r.padEnd(t,"0"),u=`${n}${i}`.replace(/^0+(?=\d)/,"");return u===""?"0":u}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",i=n.slice(-t).replace(/0+$/,"");return i?`${r}.${i}`:r}var $="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,i){this.chargeImpl=i;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 ${$} 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,i=e.maxPerVoucherAtomic?BigInt(e.maxPerVoucherAtomic):BigInt(S(e.perUnit))*100n;return async(u,p,f)=>{try{let o=fe(u.headers[$]),a=o.payload.channelId,m=we(a),s=n.get(a);if(!s){let C=I(o.sessionRegistration);await E(e.connection,C),s={registration:C,lastCumulativeAtomic:"0"},n.set(a,s)}T(o,m),k({registration:s.registration,voucher:o,expectedCounterparty:r,previousCumulativeAtomic:s.lastCumulativeAtomic});let y=BigInt(o.payload.cumulativeAmount),ee=BigInt(s.lastCumulativeAtomic),q=y-ee;if(q>i)throw new c("cumulative_exceeds_cap",`single voucher increment ${q} exceeds maxPerVoucherAtomic ${i}`);await t.set(a,o),n.update(a,o.payload.cumulativeAmount);let L=new M(a,e.network,y,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.")});L.setSessionPublicKey(o.sessionPublicKey),u.tab=L,f()}catch(o){if(o instanceof l||o instanceof d||o instanceof h||o instanceof g||o instanceof c){p.status(402).json({error:"invalid_voucher",reason:o.reason??"unknown",detail:o.message});return}f(o)}}}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())),i=t.perUnit?BigInt(S(t.perUnit)):null,u=0n,p=!1;function f(m=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(m),y=u+s;return y>r?Promise.reject(new c("cumulative_exceeds_cap",`chunk would push request total to ${v(y.toString())} beyond voucher-authorized budget ${v(r.toString())}`)):(u=y,Promise.resolve())}function o(m){if(p)throw new Error("meter ended");let y=(typeof m=="string"?m:Buffer.from(m).toString("utf8")).replace(/\n/g,"\\n");e.write(`data: ${y}
|
|
2
2
|
|
|
3
3
|
`)}function a(){p||(p=!0,e.write(`event: end
|
|
4
|
-
data: {"chargedAtomic":"${
|
|
4
|
+
data: {"chargedAtomic":"${u}"}
|
|
5
5
|
|
|
6
|
-
`),e.end())}return{charge:f,send:
|
|
6
|
+
`),e.end())}return{charge:f,send:o,end:a}}0&&(module.exports={FileVoucherStore,InMemoryVoucherStore,InvalidRegistrationError,InvalidVoucherError,InvalidVoucherSignatureError,OnChainVerificationError,ScopeViolationError,TAB_VOUCHER_HEADER,enforceScope,openSse,parseRegistration,readVaultState,requireTab,tabMiddleware,verifyRegistrationOnChain,verifyVoucherSignature});
|
package/dist/tab/seller/index.js
CHANGED
|
@@ -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 d=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 oe=(()=>{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 ue}from"@solana/web3.js";var v=new I("Hg3wRaydFtJhYrdvYrKECacpJYDsC9Px7yKmpncj2fhc"),le=new I("Secp256r1SigVerify1111111111111111111111111"),de=new I("Sysvar1nstructions1111111111111111111111111"),me=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",m=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 m("wrong_length",`expected 180, got ${e.length}`);let t=new TextDecoder().decode(e.slice(0,T.length));if(t!==T)throw new m("wrong_domain",`got "${t}"`);for(let s=T.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 E(e.slice(32,64)),o=new E(e.slice(64,96)),c=e.slice(96,128),p=n.getBigUint64(128,!0),g=n.getBigInt64(136,!0),i=new E(e.slice(144,176)),a=n.getUint32(176,!0);if(!r.equals(v))throw new m("wrong_program",`${r.toBase58()} is not ${v.toBase58()}`);if(p===0n)throw new m("cap_zero");let l=BigInt(Math.floor(Date.now()/1e3));if(g<=l)throw new m("expiry_in_past",`expires_at=${g}, now=${l}`);return{programId:r,vaultPda:o,sessionPubkey:new Uint8Array(c),maxAmount:p,expiresAt:g,allowedCounterparty:i,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,o=new Uint8Array(r.slice(M,M+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,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 u=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 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 G,dirname as Y}from"path";function W(e){return{payload:e.payload,sessionPublicKey:$(e.sessionPublicKey),sessionRegistration:$(e.sessionRegistration),sessionSignature:$(e.sessionSignature)}}function Z(e){return{payload:e.payload,sessionPublicKey:C(e.sessionPublicKey),sessionRegistration:C(e.sessionRegistration),sessionSignature:C(e.sessionSignature)}}function $(e){let t="";for(let n of e)t+=n.toString(16).padStart(2,"0");return t}function C(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 o=`${r}.tmp`;await w.writeFile(o,JSON.stringify(W(n))),await w.rename(o,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 $e}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 o=r.padEnd(t,"0"),c=`${n}${o}`.replace(/^0+(?=\d)/,"");return c===""?"0":c}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",o=n.slice(-t).replace(/0+$/,"");return o?`${r}.${o}`:r}var B="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)}},N=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 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 d("signature_invalid",`missing ${B} header`);let t;try{t=Buffer.from(e,"base64").toString("utf8")}catch{throw new d("signature_invalid","malformed base64")}let n;try{n=JSON.parse(t)}catch{throw new d("signature_invalid","malformed JSON")}if(!n||typeof n!="object"||!n.payload||!n.sessionPublicKey)throw new d("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 d("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 d("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,o=e.maxPerVoucherAtomic?BigInt(e.maxPerVoucherAtomic):BigInt(A(e.perUnit))*100n;return async(c,p,g)=>{try{let i=Q(c.headers[B]),a=i.payload.channelId,l=ee(a),s=n.get(a);if(!s){let P=k(i.sessionRegistration);await _(e.connection,P),s={registration:P,lastCumulativeAtomic:"0"},n.set(a,s)}R(i,l),V({registration:s.registration,voucher:i,expectedCounterparty:r,previousCumulativeAtomic:s.lastCumulativeAtomic});let y=BigInt(i.payload.cumulativeAmount),F=BigInt(s.lastCumulativeAtomic),D=y-F;if(D>o)throw new u("cumulative_exceeds_cap",`single voucher increment ${D} exceeds maxPerVoucherAtomic ${o}`);await t.set(a,i),n.update(a,i.payload.cumulativeAmount);let H=new N(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(i.sessionPublicKey),c.tab=H,g()}catch(i){if(i instanceof d||i instanceof m||i instanceof f||i instanceof h||i instanceof u){p.status(402).json({error:"invalid_voucher",reason:i.reason??"unknown",detail:i.message});return}g(i)}}}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())),o=t.perUnit?BigInt(A(t.perUnit)):null,c=0n,p=!1;function g(l=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(l),y=c+s;return y>r?Promise.reject(new u("cumulative_exceeds_cap",`chunk would push request total to ${S(y.toString())} beyond voucher-authorized budget ${S(r.toString())}`)):(c=y,Promise.resolve())}function i(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}
|
|
2
2
|
|
|
3
3
|
`)}function a(){p||(p=!0,e.write(`event: end
|
|
4
|
-
data: {"chargedAtomic":"${
|
|
4
|
+
data: {"chargedAtomic":"${c}"}
|
|
5
5
|
|
|
6
|
-
`),e.end())}return{charge:g,send:
|
|
6
|
+
`),e.end())}return{charge:g,send:i,end:a}}export{K as FileVoucherStore,b as InMemoryVoucherStore,m as InvalidRegistrationError,d as InvalidVoucherError,h as InvalidVoucherSignatureError,f as OnChainVerificationError,u as ScopeViolationError,B 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};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dexterai/x402",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.10.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",
|