@shroud-fi/x402 0.1.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/LICENSE +21 -0
- package/README.md +77 -0
- package/dist/cjs/client.d.ts +43 -0
- package/dist/cjs/client.d.ts.map +1 -0
- package/dist/cjs/client.js +199 -0
- package/dist/cjs/client.js.map +1 -0
- package/dist/cjs/constants.d.ts +16 -0
- package/dist/cjs/constants.d.ts.map +1 -0
- package/dist/cjs/constants.js +19 -0
- package/dist/cjs/constants.js.map +1 -0
- package/dist/cjs/errors.d.ts +61 -0
- package/dist/cjs/errors.d.ts.map +1 -0
- package/dist/cjs/errors.js +82 -0
- package/dist/cjs/errors.js.map +1 -0
- package/dist/cjs/facilitator.d.ts +50 -0
- package/dist/cjs/facilitator.d.ts.map +1 -0
- package/dist/cjs/facilitator.js +106 -0
- package/dist/cjs/facilitator.js.map +1 -0
- package/dist/cjs/index.d.ts +8 -0
- package/dist/cjs/index.d.ts.map +1 -0
- package/dist/cjs/index.js +29 -0
- package/dist/cjs/index.js.map +1 -0
- package/dist/cjs/package.json +1 -0
- package/dist/cjs/protocol.d.ts +117 -0
- package/dist/cjs/protocol.d.ts.map +1 -0
- package/dist/cjs/protocol.js +39 -0
- package/dist/cjs/protocol.js.map +1 -0
- package/dist/cjs/server.d.ts +49 -0
- package/dist/cjs/server.d.ts.map +1 -0
- package/dist/cjs/server.js +237 -0
- package/dist/cjs/server.js.map +1 -0
- package/dist/cjs/signing.d.ts +93 -0
- package/dist/cjs/signing.d.ts.map +1 -0
- package/dist/cjs/signing.js +170 -0
- package/dist/cjs/signing.js.map +1 -0
- package/dist/cjs/types.d.ts +93 -0
- package/dist/cjs/types.d.ts.map +1 -0
- package/dist/cjs/types.js +6 -0
- package/dist/cjs/types.js.map +1 -0
- package/dist/esm/client.d.ts +43 -0
- package/dist/esm/client.d.ts.map +1 -0
- package/dist/esm/client.js +196 -0
- package/dist/esm/client.js.map +1 -0
- package/dist/esm/constants.d.ts +16 -0
- package/dist/esm/constants.d.ts.map +1 -0
- package/dist/esm/constants.js +16 -0
- package/dist/esm/constants.js.map +1 -0
- package/dist/esm/errors.d.ts +61 -0
- package/dist/esm/errors.d.ts.map +1 -0
- package/dist/esm/errors.js +73 -0
- package/dist/esm/errors.js.map +1 -0
- package/dist/esm/facilitator.d.ts +50 -0
- package/dist/esm/facilitator.d.ts.map +1 -0
- package/dist/esm/facilitator.js +100 -0
- package/dist/esm/facilitator.js.map +1 -0
- package/dist/esm/index.d.ts +8 -0
- package/dist/esm/index.d.ts.map +1 -0
- package/dist/esm/index.js +12 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/protocol.d.ts +117 -0
- package/dist/esm/protocol.d.ts.map +1 -0
- package/dist/esm/protocol.js +36 -0
- package/dist/esm/protocol.js.map +1 -0
- package/dist/esm/server.d.ts +49 -0
- package/dist/esm/server.d.ts.map +1 -0
- package/dist/esm/server.js +234 -0
- package/dist/esm/server.js.map +1 -0
- package/dist/esm/signing.d.ts +93 -0
- package/dist/esm/signing.d.ts.map +1 -0
- package/dist/esm/signing.js +131 -0
- package/dist/esm/signing.js.map +1 -0
- package/dist/esm/types.d.ts +93 -0
- package/dist/esm/types.d.ts.map +1 -0
- package/dist/esm/types.js +5 -0
- package/dist/esm/types.js.map +1 -0
- package/dist/tsconfig.cjs.tsbuildinfo +1 -0
- package/dist/tsconfig.esm.tsbuildinfo +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/package.json +64 -0
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* EIP-3009 `TransferWithAuthorization` typed-data signing for x402.
|
|
3
|
+
*
|
|
4
|
+
* Privacy invariants:
|
|
5
|
+
* - The private key never leaves the viem `account` object. We do not
|
|
6
|
+
* extract it, log it, or attach it to errors.
|
|
7
|
+
* - The signed payload (`signature`, `nonce`, `value`) is never logged,
|
|
8
|
+
* stringified into errors, or stored. Caller is responsible for sending
|
|
9
|
+
* it directly to the server in the `X-PAYMENT` header.
|
|
10
|
+
* - `nonce` is a fresh 32 bytes per call via `crypto.getRandomValues`,
|
|
11
|
+
* never reused.
|
|
12
|
+
*/
|
|
13
|
+
import type { Account, Address, Chain, Hex } from 'viem';
|
|
14
|
+
/**
|
|
15
|
+
* EIP-3009 `TransferWithAuthorization` typed-data types.
|
|
16
|
+
* Exported for tests; the runtime signer constructs the same object inline.
|
|
17
|
+
*/
|
|
18
|
+
export declare const TransferWithAuthorizationTypes: {
|
|
19
|
+
readonly TransferWithAuthorization: readonly [{
|
|
20
|
+
readonly name: "from";
|
|
21
|
+
readonly type: "address";
|
|
22
|
+
}, {
|
|
23
|
+
readonly name: "to";
|
|
24
|
+
readonly type: "address";
|
|
25
|
+
}, {
|
|
26
|
+
readonly name: "value";
|
|
27
|
+
readonly type: "uint256";
|
|
28
|
+
}, {
|
|
29
|
+
readonly name: "validAfter";
|
|
30
|
+
readonly type: "uint256";
|
|
31
|
+
}, {
|
|
32
|
+
readonly name: "validBefore";
|
|
33
|
+
readonly type: "uint256";
|
|
34
|
+
}, {
|
|
35
|
+
readonly name: "nonce";
|
|
36
|
+
readonly type: "bytes32";
|
|
37
|
+
}];
|
|
38
|
+
};
|
|
39
|
+
/**
|
|
40
|
+
* The cleartext authorization fields. Caller passes them in; signer hashes,
|
|
41
|
+
* signs, and returns both signature + the authorization (so the caller can
|
|
42
|
+
* forward the unsigned data alongside the signature).
|
|
43
|
+
*/
|
|
44
|
+
export interface TransferWithAuthorizationInput {
|
|
45
|
+
readonly from: Address;
|
|
46
|
+
readonly to: Address;
|
|
47
|
+
readonly value: bigint;
|
|
48
|
+
readonly validAfter: bigint;
|
|
49
|
+
readonly validBefore: bigint;
|
|
50
|
+
readonly nonce: Hex;
|
|
51
|
+
}
|
|
52
|
+
export interface SignedTransferWithAuthorization {
|
|
53
|
+
readonly signature: Hex;
|
|
54
|
+
readonly nonce: Hex;
|
|
55
|
+
readonly from: Address;
|
|
56
|
+
readonly to: Address;
|
|
57
|
+
readonly value: bigint;
|
|
58
|
+
readonly validAfter: bigint;
|
|
59
|
+
readonly validBefore: bigint;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Generate a fresh 32-byte nonce for EIP-3009. Uses Web Crypto (available in
|
|
63
|
+
* Node 20+ and all browsers). Never reuse — each authorization needs a
|
|
64
|
+
* unique nonce to avoid replay.
|
|
65
|
+
*/
|
|
66
|
+
export declare function generateAuthorizationNonce(): Hex;
|
|
67
|
+
/**
|
|
68
|
+
* Sign an EIP-3009 `TransferWithAuthorization` over USDC. Returns the
|
|
69
|
+
* 65-byte signature + the authorization fields the server will replay.
|
|
70
|
+
*
|
|
71
|
+
* @throws X402AssetNotSupportedError if USDC is not configured for this chain.
|
|
72
|
+
*/
|
|
73
|
+
export declare function signTransferWithAuthorization(args: {
|
|
74
|
+
readonly account: Account;
|
|
75
|
+
readonly chain: Chain;
|
|
76
|
+
readonly verifyingContract: Address;
|
|
77
|
+
readonly input: TransferWithAuthorizationInput;
|
|
78
|
+
}): Promise<SignedTransferWithAuthorization>;
|
|
79
|
+
/**
|
|
80
|
+
* Verify a `TransferWithAuthorization` signature on the server side.
|
|
81
|
+
* Returns the recovered signer address (`from`) or `null` on failure.
|
|
82
|
+
*
|
|
83
|
+
* NOTE: we do NOT throw on failure — caller maps `null` → verify.valid=false
|
|
84
|
+
* with an error tag. Throwing here would risk leaking signature bytes via
|
|
85
|
+
* thrown error stacks.
|
|
86
|
+
*/
|
|
87
|
+
export declare function verifyTransferWithAuthorizationSignature(args: {
|
|
88
|
+
readonly chainId: number;
|
|
89
|
+
readonly verifyingContract: Address;
|
|
90
|
+
readonly authorization: TransferWithAuthorizationInput;
|
|
91
|
+
readonly signature: Hex;
|
|
92
|
+
}): Promise<Address | null>;
|
|
93
|
+
//# sourceMappingURL=signing.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"signing.d.ts","sourceRoot":"","sources":["../../src/signing.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,MAAM,CAAC;AAIzD;;;GAGG;AACH,eAAO,MAAM,8BAA8B;;;;;;;;;;;;;;;;;;;;CASjC,CAAC;AAEX;;;;GAIG;AACH,MAAM,WAAW,8BAA8B;IAC7C,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IACvB,QAAQ,CAAC,EAAE,EAAE,OAAO,CAAC;IACrB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,KAAK,EAAE,GAAG,CAAC;CACrB;AAED,MAAM,WAAW,+BAA+B;IAC9C,QAAQ,CAAC,SAAS,EAAE,GAAG,CAAC;IACxB,QAAQ,CAAC,KAAK,EAAE,GAAG,CAAC;IACpB,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IACvB,QAAQ,CAAC,EAAE,EAAE,OAAO,CAAC;IACrB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;CAC9B;AAED;;;;GAIG;AACH,wBAAgB,0BAA0B,IAAI,GAAG,CAUhD;AAED;;;;;GAKG;AACH,wBAAsB,6BAA6B,CAAC,IAAI,EAAE;IACxD,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC;IACtB,QAAQ,CAAC,iBAAiB,EAAE,OAAO,CAAC;IACpC,QAAQ,CAAC,KAAK,EAAE,8BAA8B,CAAC;CAChD,GAAG,OAAO,CAAC,+BAA+B,CAAC,CA4C3C;AAED;;;;;;;GAOG;AACH,wBAAsB,wCAAwC,CAAC,IAAI,EAAE;IACnE,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,iBAAiB,EAAE,OAAO,CAAC;IACpC,QAAQ,CAAC,aAAa,EAAE,8BAA8B,CAAC;IACvD,QAAQ,CAAC,SAAS,EAAE,GAAG,CAAC;CACzB,GAAG,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CA6B1B"}
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* EIP-3009 `TransferWithAuthorization` typed-data signing for x402.
|
|
4
|
+
*
|
|
5
|
+
* Privacy invariants:
|
|
6
|
+
* - The private key never leaves the viem `account` object. We do not
|
|
7
|
+
* extract it, log it, or attach it to errors.
|
|
8
|
+
* - The signed payload (`signature`, `nonce`, `value`) is never logged,
|
|
9
|
+
* stringified into errors, or stored. Caller is responsible for sending
|
|
10
|
+
* it directly to the server in the `X-PAYMENT` header.
|
|
11
|
+
* - `nonce` is a fresh 32 bytes per call via `crypto.getRandomValues`,
|
|
12
|
+
* never reused.
|
|
13
|
+
*/
|
|
14
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
15
|
+
if (k2 === undefined) k2 = k;
|
|
16
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
17
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
18
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
19
|
+
}
|
|
20
|
+
Object.defineProperty(o, k2, desc);
|
|
21
|
+
}) : (function(o, m, k, k2) {
|
|
22
|
+
if (k2 === undefined) k2 = k;
|
|
23
|
+
o[k2] = m[k];
|
|
24
|
+
}));
|
|
25
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
26
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
27
|
+
}) : function(o, v) {
|
|
28
|
+
o["default"] = v;
|
|
29
|
+
});
|
|
30
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
31
|
+
var ownKeys = function(o) {
|
|
32
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
33
|
+
var ar = [];
|
|
34
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
35
|
+
return ar;
|
|
36
|
+
};
|
|
37
|
+
return ownKeys(o);
|
|
38
|
+
};
|
|
39
|
+
return function (mod) {
|
|
40
|
+
if (mod && mod.__esModule) return mod;
|
|
41
|
+
var result = {};
|
|
42
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
43
|
+
__setModuleDefault(result, mod);
|
|
44
|
+
return result;
|
|
45
|
+
};
|
|
46
|
+
})();
|
|
47
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
48
|
+
exports.TransferWithAuthorizationTypes = void 0;
|
|
49
|
+
exports.generateAuthorizationNonce = generateAuthorizationNonce;
|
|
50
|
+
exports.signTransferWithAuthorization = signTransferWithAuthorization;
|
|
51
|
+
exports.verifyTransferWithAuthorizationSignature = verifyTransferWithAuthorizationSignature;
|
|
52
|
+
const transport_1 = require("@shroud-fi/transport");
|
|
53
|
+
const errors_js_1 = require("./errors.js");
|
|
54
|
+
/**
|
|
55
|
+
* EIP-3009 `TransferWithAuthorization` typed-data types.
|
|
56
|
+
* Exported for tests; the runtime signer constructs the same object inline.
|
|
57
|
+
*/
|
|
58
|
+
exports.TransferWithAuthorizationTypes = {
|
|
59
|
+
TransferWithAuthorization: [
|
|
60
|
+
{ name: 'from', type: 'address' },
|
|
61
|
+
{ name: 'to', type: 'address' },
|
|
62
|
+
{ name: 'value', type: 'uint256' },
|
|
63
|
+
{ name: 'validAfter', type: 'uint256' },
|
|
64
|
+
{ name: 'validBefore', type: 'uint256' },
|
|
65
|
+
{ name: 'nonce', type: 'bytes32' },
|
|
66
|
+
],
|
|
67
|
+
};
|
|
68
|
+
/**
|
|
69
|
+
* Generate a fresh 32-byte nonce for EIP-3009. Uses Web Crypto (available in
|
|
70
|
+
* Node 20+ and all browsers). Never reuse — each authorization needs a
|
|
71
|
+
* unique nonce to avoid replay.
|
|
72
|
+
*/
|
|
73
|
+
function generateAuthorizationNonce() {
|
|
74
|
+
const bytes = new Uint8Array(32);
|
|
75
|
+
// Web Crypto is globally available on Node 20+; deliberately not using
|
|
76
|
+
// `node:crypto.randomBytes` so the module stays runtime-agnostic.
|
|
77
|
+
globalThis.crypto.getRandomValues(bytes);
|
|
78
|
+
let hex = '0x';
|
|
79
|
+
for (const b of bytes) {
|
|
80
|
+
hex += b.toString(16).padStart(2, '0');
|
|
81
|
+
}
|
|
82
|
+
return hex;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Sign an EIP-3009 `TransferWithAuthorization` over USDC. Returns the
|
|
86
|
+
* 65-byte signature + the authorization fields the server will replay.
|
|
87
|
+
*
|
|
88
|
+
* @throws X402AssetNotSupportedError if USDC is not configured for this chain.
|
|
89
|
+
*/
|
|
90
|
+
async function signTransferWithAuthorization(args) {
|
|
91
|
+
const { account, chain, verifyingContract, input } = args;
|
|
92
|
+
const usdcDomain = (0, transport_1.getUSDCDomain)(chain.id);
|
|
93
|
+
if (usdcDomain === undefined) {
|
|
94
|
+
throw new errors_js_1.X402AssetNotSupportedError();
|
|
95
|
+
}
|
|
96
|
+
const domain = {
|
|
97
|
+
name: usdcDomain.name,
|
|
98
|
+
version: usdcDomain.version,
|
|
99
|
+
chainId: chain.id,
|
|
100
|
+
verifyingContract,
|
|
101
|
+
};
|
|
102
|
+
if (account.signTypedData === undefined) {
|
|
103
|
+
// Local account; viem provides .signTypedData for privateKeyToAccount.
|
|
104
|
+
// For JSON-RPC accounts the caller must provide a wrapper. Treat as
|
|
105
|
+
// unsupported configuration rather than leaking the underlying error.
|
|
106
|
+
throw new errors_js_1.X402AssetNotSupportedError();
|
|
107
|
+
}
|
|
108
|
+
const signature = await account.signTypedData({
|
|
109
|
+
domain,
|
|
110
|
+
types: exports.TransferWithAuthorizationTypes,
|
|
111
|
+
primaryType: 'TransferWithAuthorization',
|
|
112
|
+
message: {
|
|
113
|
+
from: input.from,
|
|
114
|
+
to: input.to,
|
|
115
|
+
value: input.value,
|
|
116
|
+
validAfter: input.validAfter,
|
|
117
|
+
validBefore: input.validBefore,
|
|
118
|
+
nonce: input.nonce,
|
|
119
|
+
},
|
|
120
|
+
});
|
|
121
|
+
return {
|
|
122
|
+
signature,
|
|
123
|
+
nonce: input.nonce,
|
|
124
|
+
from: input.from,
|
|
125
|
+
to: input.to,
|
|
126
|
+
value: input.value,
|
|
127
|
+
validAfter: input.validAfter,
|
|
128
|
+
validBefore: input.validBefore,
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Verify a `TransferWithAuthorization` signature on the server side.
|
|
133
|
+
* Returns the recovered signer address (`from`) or `null` on failure.
|
|
134
|
+
*
|
|
135
|
+
* NOTE: we do NOT throw on failure — caller maps `null` → verify.valid=false
|
|
136
|
+
* with an error tag. Throwing here would risk leaking signature bytes via
|
|
137
|
+
* thrown error stacks.
|
|
138
|
+
*/
|
|
139
|
+
async function verifyTransferWithAuthorizationSignature(args) {
|
|
140
|
+
const { chainId, verifyingContract, authorization, signature } = args;
|
|
141
|
+
const usdcDomain = (0, transport_1.getUSDCDomain)(chainId);
|
|
142
|
+
if (usdcDomain === undefined)
|
|
143
|
+
return null;
|
|
144
|
+
const { recoverTypedDataAddress } = await Promise.resolve().then(() => __importStar(require('viem')));
|
|
145
|
+
try {
|
|
146
|
+
return await recoverTypedDataAddress({
|
|
147
|
+
domain: {
|
|
148
|
+
name: usdcDomain.name,
|
|
149
|
+
version: usdcDomain.version,
|
|
150
|
+
chainId,
|
|
151
|
+
verifyingContract,
|
|
152
|
+
},
|
|
153
|
+
types: exports.TransferWithAuthorizationTypes,
|
|
154
|
+
primaryType: 'TransferWithAuthorization',
|
|
155
|
+
message: {
|
|
156
|
+
from: authorization.from,
|
|
157
|
+
to: authorization.to,
|
|
158
|
+
value: authorization.value,
|
|
159
|
+
validAfter: authorization.validAfter,
|
|
160
|
+
validBefore: authorization.validBefore,
|
|
161
|
+
nonce: authorization.nonce,
|
|
162
|
+
},
|
|
163
|
+
signature,
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
catch {
|
|
167
|
+
return null;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
//# sourceMappingURL=signing.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"signing.js","sourceRoot":"","sources":["../../src/signing.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;GAWG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAkDH,gEAUC;AAQD,sEAiDC;AAUD,4FAkCC;AA9JD,oDAAqD;AACrD,2CAAyD;AAEzD;;;GAGG;AACU,QAAA,8BAA8B,GAAG;IAC5C,yBAAyB,EAAE;QACzB,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE;QACjC,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,SAAS,EAAE;QAC/B,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE;QAClC,EAAE,IAAI,EAAE,YAAY,EAAE,IAAI,EAAE,SAAS,EAAE;QACvC,EAAE,IAAI,EAAE,aAAa,EAAE,IAAI,EAAE,SAAS,EAAE;QACxC,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE;KACnC;CACO,CAAC;AA0BX;;;;GAIG;AACH,SAAgB,0BAA0B;IACxC,MAAM,KAAK,GAAG,IAAI,UAAU,CAAC,EAAE,CAAC,CAAC;IACjC,uEAAuE;IACvE,kEAAkE;IAClE,UAAU,CAAC,MAAM,CAAC,eAAe,CAAC,KAAK,CAAC,CAAC;IACzC,IAAI,GAAG,GAAG,IAAI,CAAC;IACf,KAAK,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC;QACtB,GAAG,IAAI,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IACzC,CAAC;IACD,OAAO,GAAU,CAAC;AACpB,CAAC;AAED;;;;;GAKG;AACI,KAAK,UAAU,6BAA6B,CAAC,IAKnD;IACC,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,iBAAiB,EAAE,KAAK,EAAE,GAAG,IAAI,CAAC;IAC1D,MAAM,UAAU,GAAG,IAAA,yBAAa,EAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IAC3C,IAAI,UAAU,KAAK,SAAS,EAAE,CAAC;QAC7B,MAAM,IAAI,sCAA0B,EAAE,CAAC;IACzC,CAAC;IAED,MAAM,MAAM,GAAG;QACb,IAAI,EAAE,UAAU,CAAC,IAAI;QACrB,OAAO,EAAE,UAAU,CAAC,OAAO;QAC3B,OAAO,EAAE,KAAK,CAAC,EAAE;QACjB,iBAAiB;KACT,CAAC;IAEX,IAAI,OAAO,CAAC,aAAa,KAAK,SAAS,EAAE,CAAC;QACxC,uEAAuE;QACvE,oEAAoE;QACpE,sEAAsE;QACtE,MAAM,IAAI,sCAA0B,EAAE,CAAC;IACzC,CAAC;IAED,MAAM,SAAS,GAAG,MAAM,OAAO,CAAC,aAAa,CAAC;QAC5C,MAAM;QACN,KAAK,EAAE,sCAA8B;QACrC,WAAW,EAAE,2BAA2B;QACxC,OAAO,EAAE;YACP,IAAI,EAAE,KAAK,CAAC,IAAI;YAChB,EAAE,EAAE,KAAK,CAAC,EAAE;YACZ,KAAK,EAAE,KAAK,CAAC,KAAK;YAClB,UAAU,EAAE,KAAK,CAAC,UAAU;YAC5B,WAAW,EAAE,KAAK,CAAC,WAAW;YAC9B,KAAK,EAAE,KAAK,CAAC,KAAK;SACnB;KACF,CAAC,CAAC;IAEH,OAAO;QACL,SAAS;QACT,KAAK,EAAE,KAAK,CAAC,KAAK;QAClB,IAAI,EAAE,KAAK,CAAC,IAAI;QAChB,EAAE,EAAE,KAAK,CAAC,EAAE;QACZ,KAAK,EAAE,KAAK,CAAC,KAAK;QAClB,UAAU,EAAE,KAAK,CAAC,UAAU;QAC5B,WAAW,EAAE,KAAK,CAAC,WAAW;KAC/B,CAAC;AACJ,CAAC;AAED;;;;;;;GAOG;AACI,KAAK,UAAU,wCAAwC,CAAC,IAK9D;IACC,MAAM,EAAE,OAAO,EAAE,iBAAiB,EAAE,aAAa,EAAE,SAAS,EAAE,GAAG,IAAI,CAAC;IACtE,MAAM,UAAU,GAAG,IAAA,yBAAa,EAAC,OAAO,CAAC,CAAC;IAC1C,IAAI,UAAU,KAAK,SAAS;QAAE,OAAO,IAAI,CAAC;IAE1C,MAAM,EAAE,uBAAuB,EAAE,GAAG,wDAAa,MAAM,GAAC,CAAC;IACzD,IAAI,CAAC;QACH,OAAO,MAAM,uBAAuB,CAAC;YACnC,MAAM,EAAE;gBACN,IAAI,EAAE,UAAU,CAAC,IAAI;gBACrB,OAAO,EAAE,UAAU,CAAC,OAAO;gBAC3B,OAAO;gBACP,iBAAiB;aAClB;YACD,KAAK,EAAE,sCAA8B;YACrC,WAAW,EAAE,2BAA2B;YACxC,OAAO,EAAE;gBACP,IAAI,EAAE,aAAa,CAAC,IAAI;gBACxB,EAAE,EAAE,aAAa,CAAC,EAAE;gBACpB,KAAK,EAAE,aAAa,CAAC,KAAK;gBAC1B,UAAU,EAAE,aAAa,CAAC,UAAU;gBACpC,WAAW,EAAE,aAAa,CAAC,WAAW;gBACtC,KAAK,EAAE,aAAa,CAAC,KAAK;aAC3B;YACD,SAAS;SACV,CAAC,CAAC;IACL,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared types used by both server and client.
|
|
3
|
+
*/
|
|
4
|
+
import type { Address, Hex } from 'viem';
|
|
5
|
+
import type { X402FacilitatorConfig, X402PaymentRequirements } from './protocol.js';
|
|
6
|
+
/**
|
|
7
|
+
* Hint object returned alongside a freshly generated challenge.
|
|
8
|
+
*
|
|
9
|
+
* The server uses these values to emit an ERC-5564 Announcement event AFTER
|
|
10
|
+
* the payment settles (out-of-scope for this milestone — operator wires the
|
|
11
|
+
* on-chain emit). Carrying it on the challenge object lets the server
|
|
12
|
+
* correlate "this challenge → this announcement" without re-derivation.
|
|
13
|
+
*
|
|
14
|
+
* Privacy: contains only the ephemeral public key + view tag + the freshly
|
|
15
|
+
* derived stealth address. None of these leak the recipient's main wallet.
|
|
16
|
+
*/
|
|
17
|
+
export interface X402StealthAnnouncementHint {
|
|
18
|
+
readonly ephemeralPubKey: Hex;
|
|
19
|
+
readonly viewTag: number;
|
|
20
|
+
readonly stealthAddress: Address;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* The full server-side challenge object. Comes out of `x402.challenge(...)`.
|
|
24
|
+
*
|
|
25
|
+
* `status` + `headers` + `body` are wire-shaped — operator's HTTP framework
|
|
26
|
+
* adapter simply spreads them onto its response. `announcement` is internal
|
|
27
|
+
* metadata for the operator's scanner integration.
|
|
28
|
+
*/
|
|
29
|
+
export interface X402Challenge {
|
|
30
|
+
readonly status: 402;
|
|
31
|
+
readonly headers: Readonly<Record<string, string>>;
|
|
32
|
+
readonly body: {
|
|
33
|
+
readonly x402Version: 2;
|
|
34
|
+
readonly error: string;
|
|
35
|
+
readonly accepts: readonly X402PaymentRequirements[];
|
|
36
|
+
};
|
|
37
|
+
readonly announcement: X402StealthAnnouncementHint;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Result of `x402.verify(...)`.
|
|
41
|
+
*
|
|
42
|
+
* Privacy: no amount, no signature, no nonce. `error` is a short tag string
|
|
43
|
+
* only ('signature_invalid', 'amount_mismatch', 'expired', etc) — never the
|
|
44
|
+
* full underlying error or any bytes.
|
|
45
|
+
*/
|
|
46
|
+
export interface X402PaymentVerification {
|
|
47
|
+
readonly valid: boolean;
|
|
48
|
+
readonly settledTxHash?: Hex;
|
|
49
|
+
readonly error?: string;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* `createX402Server` config.
|
|
53
|
+
*
|
|
54
|
+
* Privacy: the `recipientMetaAddress` is the server agent's PUBLIC meta-address
|
|
55
|
+
* (`st:base:0x...`). No private keys ever appear in this config.
|
|
56
|
+
*/
|
|
57
|
+
export interface X402ServerConfig {
|
|
58
|
+
/** Transport for reads + chain id lookups. */
|
|
59
|
+
readonly transport: import('@shroud-fi/transport').ShroudFiTransport;
|
|
60
|
+
/** ERC-6538 meta-address string (`st:base:0x...`) for the receiving agent. */
|
|
61
|
+
readonly recipientMetaAddress: string;
|
|
62
|
+
/** ERC-20 asset address (must be the canonical USDC for the chain). */
|
|
63
|
+
readonly asset: Address;
|
|
64
|
+
/** EVM chain id (must match the asset's deployment). */
|
|
65
|
+
readonly chainId: number;
|
|
66
|
+
/** Default price in token base units (uint256). */
|
|
67
|
+
readonly defaultPriceAtomic: bigint;
|
|
68
|
+
/** Optional facilitator override; defaults to PayAI free-tier. */
|
|
69
|
+
readonly facilitator?: X402FacilitatorConfig;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* `createX402Client` config.
|
|
73
|
+
*
|
|
74
|
+
* Privacy: the client uses the transport's `walletClient.account` for
|
|
75
|
+
* signing. The private key never appears here directly — it lives inside
|
|
76
|
+
* the viem account, scoped to the transport's lifetime.
|
|
77
|
+
*/
|
|
78
|
+
export interface X402ClientConfig {
|
|
79
|
+
/** Transport with a configured `walletClient` (account required). */
|
|
80
|
+
readonly transport: import('@shroud-fi/transport').ShroudFiTransport;
|
|
81
|
+
/** Optional facilitator override; defaults to PayAI free-tier. */
|
|
82
|
+
readonly facilitator?: X402FacilitatorConfig;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* What a client `.fetch(...)` call surfaces beyond the standard Response.
|
|
86
|
+
* Non-standard `x402Settlement` property is attached when the server returned
|
|
87
|
+
* `X-PAYMENT-RESPONSE`. Optional — present only on successful auto-pay.
|
|
88
|
+
*/
|
|
89
|
+
export interface X402PaymentResult {
|
|
90
|
+
readonly txHash: Hex;
|
|
91
|
+
readonly settledAt: number;
|
|
92
|
+
}
|
|
93
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,MAAM,CAAC;AACzC,OAAO,KAAK,EACV,qBAAqB,EACrB,uBAAuB,EACxB,MAAM,eAAe,CAAC;AAEvB;;;;;;;;;;GAUG;AACH,MAAM,WAAW,2BAA2B;IAC1C,QAAQ,CAAC,eAAe,EAAE,GAAG,CAAC;IAC9B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,cAAc,EAAE,OAAO,CAAC;CAClC;AAED;;;;;;GAMG;AACH,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,MAAM,EAAE,GAAG,CAAC;IACrB,QAAQ,CAAC,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IACnD,QAAQ,CAAC,IAAI,EAAE;QACb,QAAQ,CAAC,WAAW,EAAE,CAAC,CAAC;QACxB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;QACvB,QAAQ,CAAC,OAAO,EAAE,SAAS,uBAAuB,EAAE,CAAC;KACtD,CAAC;IACF,QAAQ,CAAC,YAAY,EAAE,2BAA2B,CAAC;CACpD;AAED;;;;;;GAMG;AACH,MAAM,WAAW,uBAAuB;IACtC,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,aAAa,CAAC,EAAE,GAAG,CAAC;IAC7B,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;CACzB;AAED;;;;;GAKG;AACH,MAAM,WAAW,gBAAgB;IAC/B,8CAA8C;IAC9C,QAAQ,CAAC,SAAS,EAAE,OAAO,sBAAsB,EAAE,iBAAiB,CAAC;IACrE,8EAA8E;IAC9E,QAAQ,CAAC,oBAAoB,EAAE,MAAM,CAAC;IACtC,uEAAuE;IACvE,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IACxB,wDAAwD;IACxD,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,mDAAmD;IACnD,QAAQ,CAAC,kBAAkB,EAAE,MAAM,CAAC;IACpC,kEAAkE;IAClE,QAAQ,CAAC,WAAW,CAAC,EAAE,qBAAqB,CAAC;CAC9C;AAED;;;;;;GAMG;AACH,MAAM,WAAW,gBAAgB;IAC/B,qEAAqE;IACrE,QAAQ,CAAC,SAAS,EAAE,OAAO,sBAAsB,EAAE,iBAAiB,CAAC;IACrE,kEAAkE;IAClE,QAAQ,CAAC,WAAW,CAAC,EAAE,qBAAqB,CAAC;CAC9C;AAED;;;;GAIG;AACH,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,MAAM,EAAE,GAAG,CAAC;IACrB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":";AAAA;;GAEG"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* x402 client — fetch wrapper that auto-handles 402 challenges.
|
|
3
|
+
*
|
|
4
|
+
* Flow:
|
|
5
|
+
* 1. Issue initial fetch.
|
|
6
|
+
* 2. If status !== 402, return as-is.
|
|
7
|
+
* 3. Parse X-PAYMENT-REQUIRED, validate scheme + asset + safety cap.
|
|
8
|
+
* 4. Sign EIP-3009 TransferWithAuthorization for USDC.
|
|
9
|
+
* 5. Re-issue request with `X-PAYMENT: base64(JSON(payload))`.
|
|
10
|
+
* 6. On 2xx, surface `x402Settlement` on the returned Response if the
|
|
11
|
+
* server attached X-PAYMENT-RESPONSE.
|
|
12
|
+
*
|
|
13
|
+
* Privacy invariants:
|
|
14
|
+
* - The signed payload is constructed in memory and forwarded directly to
|
|
15
|
+
* the server. We never log or serialize the signature, nonce, or value.
|
|
16
|
+
* - The client wallet address is unavoidably visible (it IS the `from`).
|
|
17
|
+
* This is the agent's stealth-payer EOA, not a persistent identity if
|
|
18
|
+
* used by ShroudAgent.
|
|
19
|
+
* - Errors carry no amount, no signature, no nonce bytes.
|
|
20
|
+
*/
|
|
21
|
+
import type { X402ClientConfig, X402PaymentResult } from './types.js';
|
|
22
|
+
/**
|
|
23
|
+
* The augmented Response surface. We can't subclass `Response` in a portable
|
|
24
|
+
* way, so we attach `x402Settlement` as a non-standard own-property.
|
|
25
|
+
*/
|
|
26
|
+
export interface X402Response extends Response {
|
|
27
|
+
x402Settlement?: X402PaymentResult;
|
|
28
|
+
}
|
|
29
|
+
export interface X402Client {
|
|
30
|
+
/**
|
|
31
|
+
* Wrap `fetch` with 402 auto-pay. The returned Response is augmented with
|
|
32
|
+
* `x402Settlement` when a payment was made.
|
|
33
|
+
*
|
|
34
|
+
* @param maxPriceAtomic — safety cap. If the server demands more, the
|
|
35
|
+
* client throws X402AmountMismatchError without signing anything.
|
|
36
|
+
*/
|
|
37
|
+
fetch(input: string | URL | Request, init?: RequestInit & {
|
|
38
|
+
maxPriceAtomic?: bigint;
|
|
39
|
+
}): Promise<X402Response>;
|
|
40
|
+
}
|
|
41
|
+
export declare function createX402Client(config: X402ClientConfig): X402Client;
|
|
42
|
+
export type { X402ClientConfig, X402PaymentResult } from './types.js';
|
|
43
|
+
//# sourceMappingURL=client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAkBH,OAAO,KAAK,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAMtE;;;GAGG;AACH,MAAM,WAAW,YAAa,SAAQ,QAAQ;IAC5C,cAAc,CAAC,EAAE,iBAAiB,CAAC;CACpC;AAED,MAAM,WAAW,UAAU;IACzB;;;;;;OAMG;IACH,KAAK,CACH,KAAK,EAAE,MAAM,GAAG,GAAG,GAAG,OAAO,EAC7B,IAAI,CAAC,EAAE,WAAW,GAAG;QAAE,cAAc,CAAC,EAAE,MAAM,CAAA;KAAE,GAC/C,OAAO,CAAC,YAAY,CAAC,CAAC;CAC1B;AAyED,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,gBAAgB,GAAG,UAAU,CA6HrE;AAED,YAAY,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC"}
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* x402 client — fetch wrapper that auto-handles 402 challenges.
|
|
3
|
+
*
|
|
4
|
+
* Flow:
|
|
5
|
+
* 1. Issue initial fetch.
|
|
6
|
+
* 2. If status !== 402, return as-is.
|
|
7
|
+
* 3. Parse X-PAYMENT-REQUIRED, validate scheme + asset + safety cap.
|
|
8
|
+
* 4. Sign EIP-3009 TransferWithAuthorization for USDC.
|
|
9
|
+
* 5. Re-issue request with `X-PAYMENT: base64(JSON(payload))`.
|
|
10
|
+
* 6. On 2xx, surface `x402Settlement` on the returned Response if the
|
|
11
|
+
* server attached X-PAYMENT-RESPONSE.
|
|
12
|
+
*
|
|
13
|
+
* Privacy invariants:
|
|
14
|
+
* - The signed payload is constructed in memory and forwarded directly to
|
|
15
|
+
* the server. We never log or serialize the signature, nonce, or value.
|
|
16
|
+
* - The client wallet address is unavoidably visible (it IS the `from`).
|
|
17
|
+
* This is the agent's stealth-payer EOA, not a persistent identity if
|
|
18
|
+
* used by ShroudAgent.
|
|
19
|
+
* - Errors carry no amount, no signature, no nonce bytes.
|
|
20
|
+
*/
|
|
21
|
+
import { getUSDC } from '@shroud-fi/transport';
|
|
22
|
+
import { X402_PAYMENT_REQUIRED_HEADER, X402_PAYMENT_RESPONSE_HEADER, X402_PAYMENT_SIGNATURE_HEADER, X402_SCHEME_EXACT, } from './protocol.js';
|
|
23
|
+
import { X402_VERSION, DEFAULT_AUTHORIZATION_TTL_SECS } from './constants.js';
|
|
24
|
+
import { X402AmountMismatchError, X402AssetNotSupportedError, X402InvalidChallengeError, } from './errors.js';
|
|
25
|
+
import { generateAuthorizationNonce, signTransferWithAuthorization, } from './signing.js';
|
|
26
|
+
/** Decode a 402 challenge body from the response. Tries header, then body. */
|
|
27
|
+
async function readChallenge(res) {
|
|
28
|
+
// Header path (spec-canonical) first.
|
|
29
|
+
const header = res.headers.get(X402_PAYMENT_REQUIRED_HEADER);
|
|
30
|
+
if (header !== null && header.length > 0) {
|
|
31
|
+
try {
|
|
32
|
+
const parsed = JSON.parse(header);
|
|
33
|
+
if (Array.isArray(parsed.accepts) && parsed.accepts.length > 0) {
|
|
34
|
+
return parsed;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
// fall through to body parse
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
// Body fallback — some servers put the envelope in the body only.
|
|
42
|
+
try {
|
|
43
|
+
const text = await res.clone().text();
|
|
44
|
+
const parsed = JSON.parse(text);
|
|
45
|
+
if (Array.isArray(parsed.accepts) && parsed.accepts.length > 0) {
|
|
46
|
+
return parsed;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
// fall through
|
|
51
|
+
}
|
|
52
|
+
throw new X402InvalidChallengeError();
|
|
53
|
+
}
|
|
54
|
+
/** Decode the X-PAYMENT-RESPONSE header into a settlement record. */
|
|
55
|
+
function readSettlement(res) {
|
|
56
|
+
const header = res.headers.get(X402_PAYMENT_RESPONSE_HEADER);
|
|
57
|
+
if (header === null || header.length === 0)
|
|
58
|
+
return undefined;
|
|
59
|
+
try {
|
|
60
|
+
// Header is base64-encoded JSON per the spec.
|
|
61
|
+
const decoded = Buffer.from(header, 'base64').toString('utf-8');
|
|
62
|
+
const parsed = JSON.parse(decoded);
|
|
63
|
+
const txHash = (parsed.transaction ?? parsed.txHash);
|
|
64
|
+
if (txHash === undefined)
|
|
65
|
+
return undefined;
|
|
66
|
+
return {
|
|
67
|
+
txHash,
|
|
68
|
+
settledAt: typeof parsed.settledAt === 'number'
|
|
69
|
+
? parsed.settledAt
|
|
70
|
+
: Math.floor(Date.now() / 1000),
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
catch {
|
|
74
|
+
return undefined;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
/** Convert a value into a bigint safely. */
|
|
78
|
+
function safeBigInt(v) {
|
|
79
|
+
try {
|
|
80
|
+
if (typeof v === 'bigint')
|
|
81
|
+
return v;
|
|
82
|
+
if (typeof v === 'string' || typeof v === 'number')
|
|
83
|
+
return BigInt(v);
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
catch {
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
export function createX402Client(config) {
|
|
91
|
+
const transport = config.transport;
|
|
92
|
+
const walletClient = transport.walletClient;
|
|
93
|
+
if (walletClient === undefined || walletClient.account === undefined) {
|
|
94
|
+
// We deliberately throw at construction time so a misconfigured client
|
|
95
|
+
// can't ship to production without a wallet.
|
|
96
|
+
throw new X402AssetNotSupportedError();
|
|
97
|
+
}
|
|
98
|
+
const account = walletClient.account;
|
|
99
|
+
const chain = transport.chain;
|
|
100
|
+
return {
|
|
101
|
+
async fetch(input, init) {
|
|
102
|
+
const fetchImpl = globalThis.fetch;
|
|
103
|
+
const maxPriceAtomic = init?.maxPriceAtomic;
|
|
104
|
+
// Strip our non-standard option before passing init to fetch.
|
|
105
|
+
const passThroughInit = { ...(init ?? {}) };
|
|
106
|
+
delete passThroughInit.maxPriceAtomic;
|
|
107
|
+
// 1. First attempt.
|
|
108
|
+
const first = (await fetchImpl(input, passThroughInit));
|
|
109
|
+
if (first.status !== 402) {
|
|
110
|
+
return first;
|
|
111
|
+
}
|
|
112
|
+
// 2. Decode challenge.
|
|
113
|
+
const challenge = await readChallenge(first);
|
|
114
|
+
const requirements = challenge.accepts[0];
|
|
115
|
+
if (requirements === undefined) {
|
|
116
|
+
throw new X402InvalidChallengeError();
|
|
117
|
+
}
|
|
118
|
+
// 3. Scheme + network + asset checks.
|
|
119
|
+
if (requirements.scheme !== X402_SCHEME_EXACT) {
|
|
120
|
+
throw new X402InvalidChallengeError();
|
|
121
|
+
}
|
|
122
|
+
const canonicalUsdc = getUSDC(chain.id);
|
|
123
|
+
if (canonicalUsdc === undefined) {
|
|
124
|
+
throw new X402AssetNotSupportedError();
|
|
125
|
+
}
|
|
126
|
+
if (requirements.asset.toLowerCase() !==
|
|
127
|
+
canonicalUsdc.toLowerCase()) {
|
|
128
|
+
throw new X402AssetNotSupportedError();
|
|
129
|
+
}
|
|
130
|
+
const expectedNetwork = `eip155:${chain.id}`;
|
|
131
|
+
if (requirements.network !== expectedNetwork) {
|
|
132
|
+
throw new X402AssetNotSupportedError();
|
|
133
|
+
}
|
|
134
|
+
// 4. Safety cap.
|
|
135
|
+
const required = safeBigInt(requirements.maxAmountRequired);
|
|
136
|
+
if (required === null) {
|
|
137
|
+
throw new X402InvalidChallengeError();
|
|
138
|
+
}
|
|
139
|
+
if (maxPriceAtomic !== undefined && required > maxPriceAtomic) {
|
|
140
|
+
throw new X402AmountMismatchError();
|
|
141
|
+
}
|
|
142
|
+
// 5. Build authorization. validAfter=0n, validBefore=now+ttl.
|
|
143
|
+
const nowSecs = BigInt(Math.floor(Date.now() / 1000));
|
|
144
|
+
const ttl = BigInt(Math.min(requirements.maxTimeoutSeconds || DEFAULT_AUTHORIZATION_TTL_SECS, DEFAULT_AUTHORIZATION_TTL_SECS));
|
|
145
|
+
const validBefore = nowSecs + ttl;
|
|
146
|
+
const nonce = generateAuthorizationNonce();
|
|
147
|
+
const signed = await signTransferWithAuthorization({
|
|
148
|
+
account,
|
|
149
|
+
chain,
|
|
150
|
+
verifyingContract: requirements.asset,
|
|
151
|
+
input: {
|
|
152
|
+
from: account.address,
|
|
153
|
+
to: requirements.payTo,
|
|
154
|
+
value: required,
|
|
155
|
+
validAfter: 0n,
|
|
156
|
+
validBefore,
|
|
157
|
+
nonce,
|
|
158
|
+
},
|
|
159
|
+
});
|
|
160
|
+
// 6. Build the PaymentPayload + base64 the header.
|
|
161
|
+
const payload = {
|
|
162
|
+
x402Version: X402_VERSION,
|
|
163
|
+
scheme: X402_SCHEME_EXACT,
|
|
164
|
+
network: requirements.network,
|
|
165
|
+
payload: {
|
|
166
|
+
signature: signed.signature,
|
|
167
|
+
authorization: {
|
|
168
|
+
from: signed.from,
|
|
169
|
+
to: signed.to,
|
|
170
|
+
value: signed.value.toString(),
|
|
171
|
+
validAfter: signed.validAfter.toString(),
|
|
172
|
+
validBefore: signed.validBefore.toString(),
|
|
173
|
+
nonce: signed.nonce,
|
|
174
|
+
},
|
|
175
|
+
},
|
|
176
|
+
};
|
|
177
|
+
const encoded = Buffer.from(JSON.stringify(payload), 'utf-8').toString('base64');
|
|
178
|
+
// 7. Re-issue. Mutating the headers via a new object so the caller's
|
|
179
|
+
// original init stays untouched.
|
|
180
|
+
const retryHeaders = new Headers(passThroughInit.headers ?? undefined);
|
|
181
|
+
retryHeaders.set(X402_PAYMENT_SIGNATURE_HEADER, encoded);
|
|
182
|
+
const retryInit = {
|
|
183
|
+
...passThroughInit,
|
|
184
|
+
headers: retryHeaders,
|
|
185
|
+
};
|
|
186
|
+
const second = (await fetchImpl(input, retryInit));
|
|
187
|
+
const settlement = readSettlement(second);
|
|
188
|
+
if (settlement !== undefined) {
|
|
189
|
+
// Attach as a non-standard own-property.
|
|
190
|
+
second.x402Settlement = settlement;
|
|
191
|
+
}
|
|
192
|
+
return second;
|
|
193
|
+
},
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/client.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Defaults for the @shroud-fi/x402 package.
|
|
3
|
+
*
|
|
4
|
+
* Tight defaults reduce blast radius if a signed payload leaks:
|
|
5
|
+
* - 5-minute validity window matches Coinbase CDP recommendation and the
|
|
6
|
+
* PayAI free tier facilitator's default maxTimeoutSeconds (300).
|
|
7
|
+
* - x402Version pinned to 2 (current shipping spec). We deliberately do NOT
|
|
8
|
+
* implement v1 backward-compat per LOCKED-DECISIONS (whitepaper field
|
|
9
|
+
* names are deprecated).
|
|
10
|
+
*/
|
|
11
|
+
export declare const X402_VERSION: 2;
|
|
12
|
+
/** Default EIP-3009 authorization validity window (seconds). */
|
|
13
|
+
export declare const DEFAULT_AUTHORIZATION_TTL_SECS = 300;
|
|
14
|
+
/** Default facilitator `maxTimeoutSeconds` echoed back in PaymentRequired. */
|
|
15
|
+
export declare const DEFAULT_MAX_TIMEOUT_SECS = 300;
|
|
16
|
+
//# sourceMappingURL=constants.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../src/constants.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,eAAO,MAAM,YAAY,EAAG,CAAU,CAAC;AAEvC,gEAAgE;AAChE,eAAO,MAAM,8BAA8B,MAAM,CAAC;AAElD,8EAA8E;AAC9E,eAAO,MAAM,wBAAwB,MAAM,CAAC"}
|