@dexterai/x402 4.1.0 → 5.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md
CHANGED
|
@@ -38,9 +38,11 @@ The closest familiar shape is an auth-and-capture card hold, with the hold enfor
|
|
|
38
38
|
## Install
|
|
39
39
|
|
|
40
40
|
```bash
|
|
41
|
-
npm install @dexterai/x402
|
|
41
|
+
npm install @dexterai/x402 @dexterai/vault
|
|
42
42
|
```
|
|
43
43
|
|
|
44
|
+
`@dexterai/vault` is a **peer dependency** (`>=0.19`): the tab adapter and your app share ONE vault instance — the passkey signer it consumes (`signOperation`) is the same type your app builds, with no bridge shim. Install it alongside.
|
|
45
|
+
|
|
44
46
|
One install is both sides: the buyer surface at `@dexterai/x402/tab`, the seller surface at `@dexterai/x402/tab/seller`.
|
|
45
47
|
|
|
46
48
|
## Open a tab and pay (buyer)
|
|
@@ -54,7 +56,7 @@ const vault = createSolanaVaultAdapter({
|
|
|
54
56
|
connection, // your Solana Connection (any RPC)
|
|
55
57
|
swigAddress, // the vault's Swig state account, from enrollment
|
|
56
58
|
vaultPda, // the vault's gate PDA, from enrollment
|
|
57
|
-
passkeySigner, // browser:
|
|
59
|
+
passkeySigner, // signOperation signer — browser: vault's DexterApiBrowserPasskeySigner; server agent: passkeySignerFromP256Keypair(kp)
|
|
58
60
|
feePayer, // lamport fee payer (a Signer)
|
|
59
61
|
});
|
|
60
62
|
```
|
|
@@ -144,7 +146,7 @@ The full threat model and trust assumptions live in the program's [`SECURITY.md`
|
|
|
144
146
|
|
|
145
147
|
When a partner's app opens a tab for a user, the approval runs on one Dexter-hosted consent screen, deep-linked from the partner's app. The user sees the counterparty, the cap, and the expiry, taps their passkey once, and control returns to the app. The partner builds no approval UI and never handles a passkey.
|
|
146
148
|
|
|
147
|
-
The screen is hosted by Dexter for a structural reason, not a stylistic one: the vault's passkey can only sign on Dexter's own origin, so a user cannot be phished into approving on a look-alike page. The safety is a property of where the key will sign. Flow and routing: [docs.dexter.cash
|
|
149
|
+
The screen is hosted by Dexter for a structural reason, not a stylistic one: the vault's passkey can only sign on Dexter's own origin, so a user cannot be phished into approving on a look-alike page. The safety is a property of where the key will sign. Flow and routing: [docs.dexter.cash](https://docs.dexter.cash).
|
|
148
150
|
|
|
149
151
|
---
|
|
150
152
|
|
|
@@ -310,6 +312,15 @@ State auto-persists and resumes with `resumeBatchChannel({ wallet, network, salt
|
|
|
310
312
|
|
|
311
313
|
`bazaarExtension()` plus `declareDiscoveryExtension(config)` attach a spec-compliant `extensions.bazaar` block to a route's 402; extensions are opt-in and failure-isolated, so the payment path is never affected. `sponsoredAccess` injects `_x402_sponsored` into responses; read it with `getSponsoredRecommendations(response)`. Campaign creation is x402-gated at `x402ads.io`.
|
|
312
314
|
|
|
315
|
+
### Migrating to 5.0.0 (breaking)
|
|
316
|
+
|
|
317
|
+
Two changes, both about packaging and the passkey signer — the payment path itself is unchanged.
|
|
318
|
+
|
|
319
|
+
1. **`@dexterai/vault` is now a peer dependency** (`>=0.19`), not bundled. The tab adapter and your app share ONE vault instance, so there are no duplicate copies and no `instanceof`/type mismatches across packages. Install it alongside: `npm install @dexterai/x402 @dexterai/vault`.
|
|
320
|
+
2. **The passkey signer contract is `signOperation(operationMessage)`**, replacing the old `sign(challenge)`. The adapter now hands the signer the RAW operation message and the signer hashes it internally (`challenge = sha256(op)`, the on-chain `webauthn.rs` law). If you wrote a custom signer against `sign(challenge)`, rename the method to `signOperation` and delete your pre-hash — pass the message straight through. Vault's `DexterApiBrowserPasskeySigner` (browser) and this package's `passkeySignerFromP256Keypair` (node/agent) already conform.
|
|
321
|
+
|
|
322
|
+
To pin the old surface, stay on `@dexterai/x402@^4`.
|
|
323
|
+
|
|
313
324
|
### Removed in v4.0.0 (migration)
|
|
314
325
|
|
|
315
326
|
The v1-era helpers were removed in `4.0.0`. The payment engine is unchanged — the canonical entrypoints have done their jobs since 3.x:
|
|
@@ -334,7 +345,7 @@ The v1-era helpers were removed in `4.0.0`. The payment engine is unchanged —
|
|
|
334
345
|
npm run build # ESM + CJS
|
|
335
346
|
npm run dev # Watch mode
|
|
336
347
|
npm run typecheck
|
|
337
|
-
npm test #
|
|
348
|
+
npm test # 364 vitest tests
|
|
338
349
|
```
|
|
339
350
|
|
|
340
351
|
## License
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";var
|
|
1
|
+
"use strict";var ee=Object.create;var x=Object.defineProperty;var te=Object.getOwnPropertyDescriptor;var ne=Object.getOwnPropertyNames;var ie=Object.getPrototypeOf,re=Object.prototype.hasOwnProperty;var se=(e,t)=>{for(var n in t)x(e,n,{get:t[n],enumerable:!0})},M=(e,t,n,i)=>{if(t&&typeof t=="object"||typeof t=="function")for(let r of ne(t))!re.call(e,r)&&r!==n&&x(e,r,{get:()=>t[r],enumerable:!(i=te(t,r))||i.enumerable});return e};var V=(e,t,n)=>(n=e!=null?ee(ie(e)):{},M(t||!e||!e.__esModule?x(n,"default",{value:e,enumerable:!0}):n,e)),oe=e=>M(x({},"__esModule",{value:!0}),e);var fe={};se(fe,{buildAdapterRegisterInstruction:()=>Z,createSolanaVaultAdapter:()=>Ae,deriveChannelId:()=>$,passkeySignerFromP256Keypair:()=>Y});module.exports=oe(fe);var c=require("@solana/web3.js"),j=require("@solana/spl-token");var ae="solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp",ce="solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1",pe="solana:4uhcVJyU9pJkvQyS88uRDiswHXSCkY3z";var K="eip155:8453",v="eip155:84532",w="eip155:42161",C="eip155:137",R="eip155:10",O="eip155:43114",U="eip155:56",T="eip155:1187947933",_="eip155:324705682",N="eip155:1";var L="EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v";var ue="0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",le="0x55d398326f99059fF775485246999027B3197955",W="0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d",be={[U]:W,[K]:ue,[v]:"0x036CbD53842c5426634e7929541eC2318f3dCF7e",[w]:"0xaf88d065e77c8cC2239327C5EDb3A432268e5831",[C]:"0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359",[R]:"0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85",[O]:"0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E",[T]:"0x85889c8c714505E0c94b30fcfcF64fE3Ac8FCb20",[_]:"0x2e08028E3C4c2356572E096d8EF835cD5C6030bD",[N]:"0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"},Ee={[le]:{symbol:"USDT",decimals:18},[W]:{symbol:"USDC",decimals:18}};var Ke={[U]:56,[K]:8453,[v]:84532,[w]:42161,[C]:137,[R]:10,[O]:43114,[T]:1187947933,[_]:324705682,[N]:1},ve={[ae]:"https://api.dexter.cash/api/solana/rpc",[ce]:"https://api.devnet.solana.com",[pe]:"https://api.testnet.solana.com"},we={[U]:"https://api.dexter.cash/api/evm/bsc/rpc",[K]:"https://api.dexter.cash/api/base/rpc",[v]:"https://sepolia.base.org",[w]:"https://api.dexter.cash/api/evm/arbitrum/rpc",[C]:"https://api.dexter.cash/api/evm/polygon/rpc",[R]:"https://api.dexter.cash/api/evm/optimism/rpc",[O]:"https://api.dexter.cash/api/evm/avalanche/rpc",[T]:"https://skale-base.skalenodes.com/v1/base",[_]:"https://base-sepolia-testnet.skalenodes.com/v1/jubilant-horrible-ancha",[N]:"https://eth.llamarpc.com"};var a=require("@dexterai/vault/instructions"),f=require("@dexterai/vault/precompile"),m=require("@dexterai/vault/constants");var s=require("@dexterai/vault/messages");var D=V(require("tweetnacl"),1);var G=require("@noble/hashes/sha256");function F(){let e=D.default.sign.keyPair();return{publicKey:e.publicKey,privateKey:e.secretKey}}function J(e,t,n){return{publicKey:e.publicKey,privateKey:e.privateKey,scope:t,registration:n}}function H(e,t,n){if(n.length!==32)throw new Error(`channelIdBytes must be 32 bytes, got ${n.length}`);let i=BigInt(t.cumulativeAmount),r=BigInt(e.scope.maxAmountAtomic);if(i>r)throw new Error(`voucher cumulative ${i} exceeds session cap ${r}`);let o=Math.floor(Date.now()/1e3);if(o>=e.scope.expiresAtUnix)throw new Error(`session expired at ${e.scope.expiresAtUnix}, now ${o}`);let p=(0,s.voucherPayloadMessage)({channelId:n,cumulativeAmount:i,sequenceNumber:t.sequenceNumber}),u=D.default.sign.detached(p,e.privateKey);return{payload:t,sessionPublicKey:e.publicKey,sessionRegistration:e.registration,sessionSignature:u}}function P(e){if(!/^\d+$/.test(e))throw new Error(`atomic amount must be a non-negative integer string, got "${e}"`);return BigInt(e)}function $(e){let t=new Uint8Array(8);new DataView(t.buffer).setBigUint64(0,e.nonce,!0);let n=new TextEncoder().encode(e.sellerUrl),i=G.sha256.create();return i.update(e.vaultPda.toBytes()),i.update(n),i.update(t),i.digest()}var d=require("@dexterai/vault/session"),I=require("@noble/hashes/sha256");var q=require("@noble/curves/p256"),g=require("@noble/hashes/sha256"),X="dexter.cash";function ye(e){return Buffer.from(e).toString("base64").replace(/\+/g,"-").replace(/\//g,"_").replace(/=+$/,"")}function me(e,t=`https://${X}`){let i={type:"webauthn.get",challenge:ye(e),origin:t,crossOrigin:!1};return new TextEncoder().encode(JSON.stringify(i))}function de(e=1){let t=(0,g.sha256)(new TextEncoder().encode(X)),n=new Uint8Array(37);return n.set(t,0),n[32]=5,new DataView(n.buffer).setUint32(33,e,!1),n}function ge(e,t){let n=me(t),i=de(1),r=new Uint8Array(i.length+32);r.set(i,0),r.set((0,g.sha256)(n),i.length);let o=(0,g.sha256)(r);return{signature:q.p256.sign(o,e.privateKey,{lowS:!0}).toCompactRawBytes(),clientDataJSON:n,authenticatorData:i}}function Y(e){return{credentialId:new Uint8Array(0),publicKey:e.publicKey,signOperation:async t=>ge(e,(0,g.sha256)(t))}}function Z(e){let t=(0,a.deriveSwigWalletAddress)(e.swigAddress),n=(0,j.getAssociatedTokenAddressSync)(new c.PublicKey(L),t,!0);return(0,a.buildRegisterSessionKeyInstruction)({vaultPda:e.vaultPda,sessionPubkey:e.sessionPubkey,maxAmount:e.maxAmount,maxRevolvingCapacity:e.maxRevolvingCapacity,expiresAt:e.expiresAt,allowedCounterparty:e.allowedCounterparty,nonce:e.nonce,swigAddress:e.swigAddress,vaultUsdcAta:n,clientDataJSON:e.clientDataJSON,authenticatorData:e.authenticatorData,payer:e.payer,siblingSessionPdas:e.siblingSessionPdas})}var k=class{network="solana:mainnet";swigAddress;vaultPda;connection;vaultPdaKey;passkey;feePayer;confirmOptions;constructor(t){this.connection=t.connection,this.swigAddress=typeof t.swigAddress=="string"?t.swigAddress:t.swigAddress.toBase58(),this.vaultPdaKey=typeof t.vaultPda=="string"?new c.PublicKey(t.vaultPda):t.vaultPda,this.vaultPda=this.vaultPdaKey.toBase58(),this.passkey=t.passkeySigner,this.feePayer=t.feePayer,this.confirmOptions=t.confirmOptions??{commitment:"confirmed"}}async authorizeSession(t){let n=new c.PublicKey(t.allowedCounterparty),i=P(t.revolvingCapacityAtomic??t.maxAmountAtomic),r=F(),o=he(),p=(0,s.sessionRegisterMessage)({programId:m.DEXTER_VAULT_PROGRAM_ID,vaultPda:this.vaultPdaKey,sessionPubkey:r.publicKey,maxAmount:P(t.maxAmountAtomic),maxRevolvingCapacity:i,expiresAt:BigInt(t.expiresAtUnix),allowedCounterparty:n,nonce:o}),{signature:u,clientDataJSON:A,authenticatorData:h}=await this.passkey.signOperation(p),b=z(h,(0,I.sha256)(A)),l=(0,f.buildSecp256r1VerifyInstruction)(this.passkey.publicKey,u,b),S=(0,d.sessionPdasOf)(await(0,d.fetchVaultSessionAccounts)(this.connection,this.vaultPdaKey)),E=Z({vaultPda:this.vaultPdaKey,swigAddress:new c.PublicKey(this.swigAddress),sessionPubkey:r.publicKey,maxAmount:P(t.maxAmountAtomic),maxRevolvingCapacity:i,expiresAt:BigInt(t.expiresAtUnix),allowedCounterparty:n,nonce:o,clientDataJSON:A,authenticatorData:h,payer:this.feePayer.publicKey,siblingSessionPdas:S}),y=new c.Transaction().add(l,E);y.feePayer=this.feePayer.publicKey;let{blockhash:B}=await this.connection.getLatestBlockhash(this.confirmOptions.commitment);y.recentBlockhash=B,y.sign(this.feePayer);let Q=await this.connection.sendRawTransaction(y.serialize(),{skipPreflight:!1,preflightCommitment:this.confirmOptions.preflightCommitment??this.confirmOptions.commitment});return await this.connection.confirmTransaction({signature:Q,blockhash:B,lastValidBlockHeight:(await this.connection.getLatestBlockhash(this.confirmOptions.commitment)).lastValidBlockHeight},this.confirmOptions.commitment),await(0,d.waitForSession)(this.connection,this.vaultPdaKey,n,{expectedSessionPubkey:r.publicKey,timeoutMs:2e4}),J(r,t,p)}async signWithSession(t,n){let i=await Se(n.channelId);return H(t,n,i)}async signOpenTab(t,n){return t.registration}async signCloseTab(t,n,i){let r=(0,s.sessionRevokeMessage)({programId:m.DEXTER_VAULT_PROGRAM_ID,vaultPda:this.vaultPdaKey,sessionPubkey:t.publicKey}),{signature:o,clientDataJSON:p,authenticatorData:u}=await this.passkey.signOperation(r),A=z(u,(0,I.sha256)(p)),h=(0,f.buildSecp256r1VerifyInstruction)(this.passkey.publicKey,o,A),b=(0,a.buildRevokeSessionKeyInstruction)({vaultPda:this.vaultPdaKey,allowedCounterparty:new c.PublicKey(t.scope.allowedCounterparty),clientDataJSON:p,authenticatorData:u}),l=new c.Transaction().add(h,b);l.feePayer=this.feePayer.publicKey;let{blockhash:S,lastValidBlockHeight:E}=await this.connection.getLatestBlockhash(this.confirmOptions.commitment);l.recentBlockhash=S,l.sign(this.feePayer);let y=await this.connection.sendRawTransaction(l.serialize(),{skipPreflight:!1,preflightCommitment:this.confirmOptions.preflightCommitment??this.confirmOptions.commitment});return await this.connection.confirmTransaction({signature:y,blockhash:S,lastValidBlockHeight:E},this.confirmOptions.commitment),r}};function Ae(e){return new k(e)}function z(e,t){let n=new Uint8Array(e.length+t.length);return n.set(e,0),n.set(t,e.length),n}function he(){return Math.floor(Math.random()*4294967295)>>>0}async function Se(e){if(/^[0-9a-f]{64}$/i.test(e))return xe(e);let{sha256:t}=await import("@noble/hashes/sha256");return t(new TextEncoder().encode(e))}function xe(e){let t=new Uint8Array(e.length/2);for(let n=0;n<t.length;n++)t[n]=parseInt(e.substr(n*2,2),16);return t}0&&(module.exports={buildAdapterRegisterInstruction,createSolanaVaultAdapter,deriveChannelId,passkeySignerFromP256Keypair});
|
|
@@ -49,6 +49,7 @@ declare function deriveChannelId(args: {
|
|
|
49
49
|
* dexter-vault/tests/helpers/secp256r1.ts (signOperationWithPasskey).
|
|
50
50
|
* Keep them in lockstep.
|
|
51
51
|
*/
|
|
52
|
+
|
|
52
53
|
interface P256Keypair {
|
|
53
54
|
/** 33-byte SEC1 compressed public key (the form the vault stores). */
|
|
54
55
|
publicKey: Uint8Array;
|
|
@@ -56,20 +57,20 @@ interface P256Keypair {
|
|
|
56
57
|
privateKey: Uint8Array;
|
|
57
58
|
}
|
|
58
59
|
/**
|
|
59
|
-
* Build a unified `PasskeySignerWithPublicKey` (vault's canonical shape)
|
|
60
|
+
* Build a unified `PasskeySignerWithPublicKey` (vault's canonical 0.19 shape)
|
|
60
61
|
* from a locally-held P-256 keypair — the node/CLI path. Returns
|
|
61
|
-
* `{ publicKey,
|
|
62
|
-
*
|
|
63
|
-
* DexterApiBrowserPasskeySigner.
|
|
62
|
+
* `{ credentialId, publicKey, signOperation(operationMessage) }`. The signer
|
|
63
|
+
* owns the hashing locus: it computes `challenge = sha256(operationMessage)`
|
|
64
|
+
* internally (mirroring vault's `DexterApiBrowserPasskeySigner.signOperation`),
|
|
65
|
+
* so the adapter hands it the RAW operation message and never pre-hashes.
|
|
66
|
+
* The adapter still rebuilds the precompile message from the returned bytes.
|
|
67
|
+
*
|
|
68
|
+
* `credentialId` is empty for the node path — there is no platform
|
|
69
|
+
* authenticator credential; the on-chain verifier authenticates via the
|
|
70
|
+
* secp256r1 precompile over the clientDataJSON/authenticatorData, not the
|
|
71
|
+
* credentialId, so an empty value is correct here.
|
|
64
72
|
*/
|
|
65
|
-
declare function passkeySignerFromP256Keypair(kp: P256Keypair):
|
|
66
|
-
publicKey: Uint8Array;
|
|
67
|
-
sign(challenge: Uint8Array): Promise<{
|
|
68
|
-
signature: Uint8Array;
|
|
69
|
-
clientDataJSON: Uint8Array;
|
|
70
|
-
authenticatorData: Uint8Array;
|
|
71
|
-
}>;
|
|
72
|
-
};
|
|
73
|
+
declare function passkeySignerFromP256Keypair(kp: P256Keypair): PasskeySignerWithPublicKey;
|
|
73
74
|
|
|
74
75
|
interface CreateSolanaVaultAdapterOptions {
|
|
75
76
|
/** RPC the adapter uses to submit txs. The buyer can pass their own
|
|
@@ -49,6 +49,7 @@ declare function deriveChannelId(args: {
|
|
|
49
49
|
* dexter-vault/tests/helpers/secp256r1.ts (signOperationWithPasskey).
|
|
50
50
|
* Keep them in lockstep.
|
|
51
51
|
*/
|
|
52
|
+
|
|
52
53
|
interface P256Keypair {
|
|
53
54
|
/** 33-byte SEC1 compressed public key (the form the vault stores). */
|
|
54
55
|
publicKey: Uint8Array;
|
|
@@ -56,20 +57,20 @@ interface P256Keypair {
|
|
|
56
57
|
privateKey: Uint8Array;
|
|
57
58
|
}
|
|
58
59
|
/**
|
|
59
|
-
* Build a unified `PasskeySignerWithPublicKey` (vault's canonical shape)
|
|
60
|
+
* Build a unified `PasskeySignerWithPublicKey` (vault's canonical 0.19 shape)
|
|
60
61
|
* from a locally-held P-256 keypair — the node/CLI path. Returns
|
|
61
|
-
* `{ publicKey,
|
|
62
|
-
*
|
|
63
|
-
* DexterApiBrowserPasskeySigner.
|
|
62
|
+
* `{ credentialId, publicKey, signOperation(operationMessage) }`. The signer
|
|
63
|
+
* owns the hashing locus: it computes `challenge = sha256(operationMessage)`
|
|
64
|
+
* internally (mirroring vault's `DexterApiBrowserPasskeySigner.signOperation`),
|
|
65
|
+
* so the adapter hands it the RAW operation message and never pre-hashes.
|
|
66
|
+
* The adapter still rebuilds the precompile message from the returned bytes.
|
|
67
|
+
*
|
|
68
|
+
* `credentialId` is empty for the node path — there is no platform
|
|
69
|
+
* authenticator credential; the on-chain verifier authenticates via the
|
|
70
|
+
* secp256r1 precompile over the clientDataJSON/authenticatorData, not the
|
|
71
|
+
* credentialId, so an empty value is correct here.
|
|
64
72
|
*/
|
|
65
|
-
declare function passkeySignerFromP256Keypair(kp: P256Keypair):
|
|
66
|
-
publicKey: Uint8Array;
|
|
67
|
-
sign(challenge: Uint8Array): Promise<{
|
|
68
|
-
signature: Uint8Array;
|
|
69
|
-
clientDataJSON: Uint8Array;
|
|
70
|
-
authenticatorData: Uint8Array;
|
|
71
|
-
}>;
|
|
72
|
-
};
|
|
73
|
+
declare function passkeySignerFromP256Keypair(kp: P256Keypair): PasskeySignerWithPublicKey;
|
|
73
74
|
|
|
74
75
|
interface CreateSolanaVaultAdapterOptions {
|
|
75
76
|
/** RPC the adapter uses to submit txs. The buyer can pass their own
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{PublicKey as
|
|
1
|
+
import{PublicKey as u,Transaction as G}from"@solana/web3.js";import{getAssociatedTokenAddressSync as ce}from"@solana/spl-token";var Y="solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp",z="solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1",j="solana:4uhcVJyU9pJkvQyS88uRDiswHXSCkY3z";var S="eip155:8453",x="eip155:84532",f="eip155:42161",P="eip155:137",b="eip155:10",E="eip155:43114",K="eip155:56",v="eip155:1187947933",w="eip155:324705682",C="eip155:1";var _="EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v";var Z="0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",Q="0x55d398326f99059fF775485246999027B3197955",N="0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d",Ae={[K]:N,[S]:Z,[x]:"0x036CbD53842c5426634e7929541eC2318f3dCF7e",[f]:"0xaf88d065e77c8cC2239327C5EDb3A432268e5831",[P]:"0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359",[b]:"0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85",[E]:"0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E",[v]:"0x85889c8c714505E0c94b30fcfcF64fE3Ac8FCb20",[w]:"0x2e08028E3C4c2356572E096d8EF835cD5C6030bD",[C]:"0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"},he={[Q]:{symbol:"USDT",decimals:18},[N]:{symbol:"USDC",decimals:18}};var Se={[K]:56,[S]:8453,[x]:84532,[f]:42161,[P]:137,[b]:10,[E]:43114,[v]:1187947933,[w]:324705682,[C]:1},xe={[Y]:"https://api.dexter.cash/api/solana/rpc",[z]:"https://api.devnet.solana.com",[j]:"https://api.testnet.solana.com"},fe={[K]:"https://api.dexter.cash/api/evm/bsc/rpc",[S]:"https://api.dexter.cash/api/base/rpc",[x]:"https://sepolia.base.org",[f]:"https://api.dexter.cash/api/evm/arbitrum/rpc",[P]:"https://api.dexter.cash/api/evm/polygon/rpc",[b]:"https://api.dexter.cash/api/evm/optimism/rpc",[E]:"https://api.dexter.cash/api/evm/avalanche/rpc",[v]:"https://skale-base.skalenodes.com/v1/base",[w]:"https://base-sepolia-testnet.skalenodes.com/v1/jubilant-horrible-ancha",[C]:"https://eth.llamarpc.com"};import{buildRegisterSessionKeyInstruction as D,buildRevokeSessionKeyInstruction as I,deriveSwigWalletAddress as k}from"@dexterai/vault/instructions";import{buildSecp256r1VerifyInstruction as R}from"@dexterai/vault/precompile";import{DEXTER_VAULT_PROGRAM_ID as O,SECP256R1_PROGRAM_ID as we,INSTRUCTIONS_SYSVAR_ID as Ce}from"@dexterai/vault/constants";import{sessionRegisterMessage as B,sessionRevokeMessage as M,voucherPayloadMessage as V,buildVoucherMessage as Ue}from"@dexterai/vault/messages";import L from"tweetnacl";import{sha256 as ee}from"@noble/hashes/sha256";function W(){let e=L.sign.keyPair();return{publicKey:e.publicKey,privateKey:e.secretKey}}function F(e,t,n){return{publicKey:e.publicKey,privateKey:e.privateKey,scope:t,registration:n}}function J(e,t,n){if(n.length!==32)throw new Error(`channelIdBytes must be 32 bytes, got ${n.length}`);let i=BigInt(t.cumulativeAmount),r=BigInt(e.scope.maxAmountAtomic);if(i>r)throw new Error(`voucher cumulative ${i} exceeds session cap ${r}`);let s=Math.floor(Date.now()/1e3);if(s>=e.scope.expiresAtUnix)throw new Error(`session expired at ${e.scope.expiresAtUnix}, now ${s}`);let o=V({channelId:n,cumulativeAmount:i,sequenceNumber:t.sequenceNumber}),a=L.sign.detached(o,e.privateKey);return{payload:t,sessionPublicKey:e.publicKey,sessionRegistration:e.registration,sessionSignature:a}}function d(e){if(!/^\d+$/.test(e))throw new Error(`atomic amount must be a non-negative integer string, got "${e}"`);return BigInt(e)}function te(e){let t=new Uint8Array(8);new DataView(t.buffer).setBigUint64(0,e.nonce,!0);let n=new TextEncoder().encode(e.sellerUrl),i=ee.create();return i.update(e.vaultPda.toBytes()),i.update(n),i.update(t),i.digest()}import{fetchVaultSessionAccounts as pe,sessionPdasOf as ue,waitForSession as le}from"@dexterai/vault/session";import{sha256 as $}from"@noble/hashes/sha256";import{p256 as ne}from"@noble/curves/p256";import{sha256 as g}from"@noble/hashes/sha256";var H="dexter.cash";function ie(e){return Buffer.from(e).toString("base64").replace(/\+/g,"-").replace(/\//g,"_").replace(/=+$/,"")}function re(e,t=`https://${H}`){let i={type:"webauthn.get",challenge:ie(e),origin:t,crossOrigin:!1};return new TextEncoder().encode(JSON.stringify(i))}function se(e=1){let t=g(new TextEncoder().encode(H)),n=new Uint8Array(37);return n.set(t,0),n[32]=5,new DataView(n.buffer).setUint32(33,e,!1),n}function oe(e,t){let n=re(t),i=se(1),r=new Uint8Array(i.length+32);r.set(i,0),r.set(g(n),i.length);let s=g(r);return{signature:ne.sign(s,e.privateKey,{lowS:!0}).toCompactRawBytes(),clientDataJSON:n,authenticatorData:i}}function ae(e){return{credentialId:new Uint8Array(0),publicKey:e.publicKey,signOperation:async t=>oe(e,g(t))}}function ye(e){let t=k(e.swigAddress),n=ce(new u(_),t,!0);return D({vaultPda:e.vaultPda,sessionPubkey:e.sessionPubkey,maxAmount:e.maxAmount,maxRevolvingCapacity:e.maxRevolvingCapacity,expiresAt:e.expiresAt,allowedCounterparty:e.allowedCounterparty,nonce:e.nonce,swigAddress:e.swigAddress,vaultUsdcAta:n,clientDataJSON:e.clientDataJSON,authenticatorData:e.authenticatorData,payer:e.payer,siblingSessionPdas:e.siblingSessionPdas})}var U=class{network="solana:mainnet";swigAddress;vaultPda;connection;vaultPdaKey;passkey;feePayer;confirmOptions;constructor(t){this.connection=t.connection,this.swigAddress=typeof t.swigAddress=="string"?t.swigAddress:t.swigAddress.toBase58(),this.vaultPdaKey=typeof t.vaultPda=="string"?new u(t.vaultPda):t.vaultPda,this.vaultPda=this.vaultPdaKey.toBase58(),this.passkey=t.passkeySigner,this.feePayer=t.feePayer,this.confirmOptions=t.confirmOptions??{commitment:"confirmed"}}async authorizeSession(t){let n=new u(t.allowedCounterparty),i=d(t.revolvingCapacityAtomic??t.maxAmountAtomic),r=W(),s=me(),o=B({programId:O,vaultPda:this.vaultPdaKey,sessionPubkey:r.publicKey,maxAmount:d(t.maxAmountAtomic),maxRevolvingCapacity:i,expiresAt:BigInt(t.expiresAtUnix),allowedCounterparty:n,nonce:s}),{signature:a,clientDataJSON:l,authenticatorData:y}=await this.passkey.signOperation(o),A=q(y,$(l)),c=R(this.passkey.publicKey,a,A),m=ue(await pe(this.connection,this.vaultPdaKey)),h=ye({vaultPda:this.vaultPdaKey,swigAddress:new u(this.swigAddress),sessionPubkey:r.publicKey,maxAmount:d(t.maxAmountAtomic),maxRevolvingCapacity:i,expiresAt:BigInt(t.expiresAtUnix),allowedCounterparty:n,nonce:s,clientDataJSON:l,authenticatorData:y,payer:this.feePayer.publicKey,siblingSessionPdas:m}),p=new G().add(c,h);p.feePayer=this.feePayer.publicKey;let{blockhash:T}=await this.connection.getLatestBlockhash(this.confirmOptions.commitment);p.recentBlockhash=T,p.sign(this.feePayer);let X=await this.connection.sendRawTransaction(p.serialize(),{skipPreflight:!1,preflightCommitment:this.confirmOptions.preflightCommitment??this.confirmOptions.commitment});return await this.connection.confirmTransaction({signature:X,blockhash:T,lastValidBlockHeight:(await this.connection.getLatestBlockhash(this.confirmOptions.commitment)).lastValidBlockHeight},this.confirmOptions.commitment),await le(this.connection,this.vaultPdaKey,n,{expectedSessionPubkey:r.publicKey,timeoutMs:2e4}),F(r,t,o)}async signWithSession(t,n){let i=await de(n.channelId);return J(t,n,i)}async signOpenTab(t,n){return t.registration}async signCloseTab(t,n,i){let r=M({programId:O,vaultPda:this.vaultPdaKey,sessionPubkey:t.publicKey}),{signature:s,clientDataJSON:o,authenticatorData:a}=await this.passkey.signOperation(r),l=q(a,$(o)),y=R(this.passkey.publicKey,s,l),A=I({vaultPda:this.vaultPdaKey,allowedCounterparty:new u(t.scope.allowedCounterparty),clientDataJSON:o,authenticatorData:a}),c=new G().add(y,A);c.feePayer=this.feePayer.publicKey;let{blockhash:m,lastValidBlockHeight:h}=await this.connection.getLatestBlockhash(this.confirmOptions.commitment);c.recentBlockhash=m,c.sign(this.feePayer);let p=await this.connection.sendRawTransaction(c.serialize(),{skipPreflight:!1,preflightCommitment:this.confirmOptions.preflightCommitment??this.confirmOptions.commitment});return await this.connection.confirmTransaction({signature:p,blockhash:m,lastValidBlockHeight:h},this.confirmOptions.commitment),r}};function qe(e){return new U(e)}function q(e,t){let n=new Uint8Array(e.length+t.length);return n.set(e,0),n.set(t,e.length),n}function me(){return Math.floor(Math.random()*4294967295)>>>0}async function de(e){if(/^[0-9a-f]{64}$/i.test(e))return ge(e);let{sha256:t}=await import("@noble/hashes/sha256");return t(new TextEncoder().encode(e))}function ge(e){let t=new Uint8Array(e.length/2);for(let n=0;n<t.length;n++)t[n]=parseInt(e.substr(n*2,2),16);return t}export{ye as buildAdapterRegisterInstruction,qe as createSolanaVaultAdapter,te as deriveChannelId,ae as passkeySignerFromP256Keypair};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dexterai/x402",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "5.0.0",
|
|
4
4
|
"description": "Full-stack x402 SDK - add paid API monetization to any endpoint. Express middleware, React hooks, Access Pass, dynamic pricing. Solana, Base, Polygon, Arbitrum, Optimism, Avalanche, SKALE.",
|
|
5
5
|
"author": "Dexter",
|
|
6
6
|
"license": "MIT",
|
|
@@ -76,7 +76,6 @@
|
|
|
76
76
|
"release:major": "npm version major && npm publish --access public"
|
|
77
77
|
},
|
|
78
78
|
"dependencies": {
|
|
79
|
-
"@dexterai/vault": "^0.13.0",
|
|
80
79
|
"@dexterai/x402-ads-types": "^0.2.0",
|
|
81
80
|
"@dexterai/x402-core": "^1.4.0",
|
|
82
81
|
"@noble/curves": "^1.9.7",
|
|
@@ -89,6 +88,7 @@
|
|
|
89
88
|
"tweetnacl": "^1.0.3"
|
|
90
89
|
},
|
|
91
90
|
"devDependencies": {
|
|
91
|
+
"@dexterai/vault": "^0.19.0",
|
|
92
92
|
"@types/aws-lambda": "^8.10.161",
|
|
93
93
|
"@types/express": "^5.0.6",
|
|
94
94
|
"@types/node": "^22.10.0",
|
|
@@ -103,6 +103,7 @@
|
|
|
103
103
|
"vitest": "^2.1.8"
|
|
104
104
|
},
|
|
105
105
|
"peerDependencies": {
|
|
106
|
+
"@dexterai/vault": ">=0.19.0",
|
|
106
107
|
"@solana/wallet-adapter-base": "^0.9.0",
|
|
107
108
|
"react": "^18.0.0 || ^19.0.0",
|
|
108
109
|
"stripe": "^20.0.0",
|