@circle-fin/x402-batching 2.0.3
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 +123 -0
- package/dist/client/index.d.mts +653 -0
- package/dist/client/index.d.ts +653 -0
- package/dist/client/index.js +1313 -0
- package/dist/client/index.js.map +1 -0
- package/dist/client/index.mjs +1282 -0
- package/dist/client/index.mjs.map +1 -0
- package/dist/index.d.mts +62 -0
- package/dist/index.d.ts +62 -0
- package/dist/index.js +65 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +33 -0
- package/dist/index.mjs.map +1 -0
- package/dist/server/index.d.mts +318 -0
- package/dist/server/index.d.ts +318 -0
- package/dist/server/index.js +697 -0
- package/dist/server/index.js.map +1 -0
- package/dist/server/index.mjs +668 -0
- package/dist/server/index.mjs.map +1 -0
- package/dist/types-DnHgU28a.d.mts +90 -0
- package/dist/types-DnHgU28a.d.ts +90 -0
- package/package.json +95 -0
|
@@ -0,0 +1,668 @@
|
|
|
1
|
+
// src/server/BatchFacilitatorClient.ts
|
|
2
|
+
var BatchFacilitatorClient = class {
|
|
3
|
+
url;
|
|
4
|
+
_createAuthHeaders;
|
|
5
|
+
/**
|
|
6
|
+
* Creates a new BatchFacilitatorClient.
|
|
7
|
+
*
|
|
8
|
+
* @param config - Configuration including Gateway URL and optional auth headers
|
|
9
|
+
*/
|
|
10
|
+
constructor(config = {}) {
|
|
11
|
+
this.url = (config.url ?? "https://gateway-api-testnet.circle.com").replace(
|
|
12
|
+
/\/$/,
|
|
13
|
+
""
|
|
14
|
+
);
|
|
15
|
+
this._createAuthHeaders = config.createAuthHeaders;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Verify a payment with Circle Gateway.
|
|
19
|
+
*
|
|
20
|
+
* @param paymentPayload - The payment payload to verify
|
|
21
|
+
* @param paymentRequirements - The payment requirements
|
|
22
|
+
* @returns Verification response
|
|
23
|
+
*/
|
|
24
|
+
async verify(paymentPayload, paymentRequirements) {
|
|
25
|
+
let headers = {
|
|
26
|
+
"Content-Type": "application/json"
|
|
27
|
+
};
|
|
28
|
+
if (this._createAuthHeaders) {
|
|
29
|
+
const authHeaders = await this._createAuthHeaders();
|
|
30
|
+
headers = { ...headers, ...authHeaders.verify };
|
|
31
|
+
}
|
|
32
|
+
const response = await fetch(`${this.url}/v1/x402/verify`, {
|
|
33
|
+
method: "POST",
|
|
34
|
+
headers,
|
|
35
|
+
body: JSON.stringify({
|
|
36
|
+
paymentPayload: this.toJsonSafe(paymentPayload),
|
|
37
|
+
paymentRequirements: this.toJsonSafe(paymentRequirements)
|
|
38
|
+
})
|
|
39
|
+
});
|
|
40
|
+
const data = await response.json();
|
|
41
|
+
if (typeof data === "object" && data !== null && "isValid" in data) {
|
|
42
|
+
return data;
|
|
43
|
+
}
|
|
44
|
+
throw new Error(
|
|
45
|
+
`Circle Gateway verify failed (${response.status}): ${JSON.stringify(data)}`
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Settle a payment with Circle Gateway.
|
|
50
|
+
*
|
|
51
|
+
* @param paymentPayload - The payment payload to settle
|
|
52
|
+
* @param paymentRequirements - The payment requirements
|
|
53
|
+
* @returns Settlement response
|
|
54
|
+
*/
|
|
55
|
+
async settle(paymentPayload, paymentRequirements) {
|
|
56
|
+
let headers = {
|
|
57
|
+
"Content-Type": "application/json"
|
|
58
|
+
};
|
|
59
|
+
if (this._createAuthHeaders) {
|
|
60
|
+
const authHeaders = await this._createAuthHeaders();
|
|
61
|
+
headers = { ...headers, ...authHeaders.settle };
|
|
62
|
+
}
|
|
63
|
+
const response = await fetch(`${this.url}/v1/x402/settle`, {
|
|
64
|
+
method: "POST",
|
|
65
|
+
headers,
|
|
66
|
+
body: JSON.stringify({
|
|
67
|
+
paymentPayload: this.toJsonSafe(paymentPayload),
|
|
68
|
+
paymentRequirements: this.toJsonSafe(paymentRequirements)
|
|
69
|
+
})
|
|
70
|
+
});
|
|
71
|
+
const text = await response.text();
|
|
72
|
+
if (!text) {
|
|
73
|
+
throw new Error(
|
|
74
|
+
`Circle Gateway settle returned empty response (${response.status})`
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
const data = JSON.parse(text);
|
|
78
|
+
if (typeof data === "object" && data !== null && "success" in data) {
|
|
79
|
+
return data;
|
|
80
|
+
}
|
|
81
|
+
throw new Error(`Circle Gateway settle failed (${response.status}): ${text}`);
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Get supported payment kinds from Circle Gateway.
|
|
85
|
+
*
|
|
86
|
+
* This fetches the supported networks and their GatewayWallet contract addresses.
|
|
87
|
+
* The response includes `extra.verifyingContract` for each supported network.
|
|
88
|
+
*
|
|
89
|
+
* @returns Supported payment kinds and extensions
|
|
90
|
+
*/
|
|
91
|
+
async getSupported() {
|
|
92
|
+
let headers = {
|
|
93
|
+
"Content-Type": "application/json"
|
|
94
|
+
};
|
|
95
|
+
if (this._createAuthHeaders) {
|
|
96
|
+
const authHeaders = await this._createAuthHeaders();
|
|
97
|
+
headers = { ...headers, ...authHeaders.supported };
|
|
98
|
+
}
|
|
99
|
+
const response = await fetch(`${this.url}/v1/x402/supported`, {
|
|
100
|
+
method: "GET",
|
|
101
|
+
headers
|
|
102
|
+
});
|
|
103
|
+
if (!response.ok) {
|
|
104
|
+
const errorText = await response.text().catch(() => response.statusText);
|
|
105
|
+
throw new Error(
|
|
106
|
+
`Circle Gateway getSupported failed (${response.status}): ${errorText}`
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
return await response.json();
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Helper to convert objects to JSON-safe format.
|
|
113
|
+
* Handles BigInt and other non-JSON types.
|
|
114
|
+
*/
|
|
115
|
+
toJsonSafe(obj) {
|
|
116
|
+
return JSON.parse(
|
|
117
|
+
JSON.stringify(
|
|
118
|
+
obj,
|
|
119
|
+
(_, value) => typeof value === "bigint" ? value.toString() : value
|
|
120
|
+
)
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
// src/server/GatewayEvmScheme.ts
|
|
126
|
+
import { ExactEvmScheme } from "@x402/evm/exact/server";
|
|
127
|
+
|
|
128
|
+
// src/client/GatewayClient.ts
|
|
129
|
+
import {
|
|
130
|
+
createPublicClient,
|
|
131
|
+
createWalletClient,
|
|
132
|
+
http,
|
|
133
|
+
parseUnits,
|
|
134
|
+
formatUnits,
|
|
135
|
+
pad,
|
|
136
|
+
zeroAddress,
|
|
137
|
+
maxUint256,
|
|
138
|
+
defineChain,
|
|
139
|
+
erc20Abi
|
|
140
|
+
} from "viem";
|
|
141
|
+
import { privateKeyToAccount } from "viem/accounts";
|
|
142
|
+
import * as chains from "viem/chains";
|
|
143
|
+
var sonicTestnet = defineChain({
|
|
144
|
+
id: 14601,
|
|
145
|
+
name: "Sonic Testnet",
|
|
146
|
+
nativeCurrency: { decimals: 18, name: "Sonic", symbol: "S" },
|
|
147
|
+
rpcUrls: {
|
|
148
|
+
default: { http: ["https://rpc.testnet.soniclabs.com"] }
|
|
149
|
+
},
|
|
150
|
+
blockExplorers: {
|
|
151
|
+
default: { name: "Sonic Testnet Explorer", url: "https://testnet.soniclabs.com/" }
|
|
152
|
+
},
|
|
153
|
+
testnet: true
|
|
154
|
+
});
|
|
155
|
+
var GATEWAY_DOMAINS = {
|
|
156
|
+
// Testnet
|
|
157
|
+
arbitrumSepolia: 3,
|
|
158
|
+
arcTestnet: 26,
|
|
159
|
+
avalancheFuji: 1,
|
|
160
|
+
baseSepolia: 6,
|
|
161
|
+
sepolia: 0,
|
|
162
|
+
hyperEvmTestnet: 19,
|
|
163
|
+
optimismSepolia: 2,
|
|
164
|
+
polygonAmoy: 7,
|
|
165
|
+
seiAtlantic: 16,
|
|
166
|
+
sonicTestnet: 13,
|
|
167
|
+
unichainSepolia: 10,
|
|
168
|
+
worldChainSepolia: 14,
|
|
169
|
+
// Mainnet
|
|
170
|
+
arbitrum: 3,
|
|
171
|
+
avalanche: 1,
|
|
172
|
+
base: 6,
|
|
173
|
+
mainnet: 0,
|
|
174
|
+
hyperEvm: 19,
|
|
175
|
+
optimism: 2,
|
|
176
|
+
polygon: 7,
|
|
177
|
+
sei: 16,
|
|
178
|
+
sonic: 13,
|
|
179
|
+
unichain: 10,
|
|
180
|
+
worldChain: 14
|
|
181
|
+
};
|
|
182
|
+
var TESTNET_GATEWAY_WALLET = "0x0077777d7EBA4688BDeF3E311b846F25870A19B9";
|
|
183
|
+
var TESTNET_GATEWAY_MINTER = "0x0022222ABE238Cc2C7Bb1f21003F0a260052475B";
|
|
184
|
+
var MAINNET_GATEWAY_WALLET = "0x77777777Dcc4d5A8B6E418Fd04D8997ef11000eE";
|
|
185
|
+
var MAINNET_GATEWAY_MINTER = "0x2222222d7164433c4C09B0b0D809a9b52C04C205";
|
|
186
|
+
var CHAIN_CONFIGS = {
|
|
187
|
+
// Testnet chains
|
|
188
|
+
arbitrumSepolia: {
|
|
189
|
+
chain: chains.arbitrumSepolia,
|
|
190
|
+
domain: GATEWAY_DOMAINS.arbitrumSepolia,
|
|
191
|
+
usdc: "0x75faf114eafb1BDbe2F0316DF893fd58CE46AA4d",
|
|
192
|
+
gatewayWallet: TESTNET_GATEWAY_WALLET,
|
|
193
|
+
gatewayMinter: TESTNET_GATEWAY_MINTER
|
|
194
|
+
},
|
|
195
|
+
arcTestnet: {
|
|
196
|
+
chain: chains.arcTestnet,
|
|
197
|
+
domain: GATEWAY_DOMAINS.arcTestnet,
|
|
198
|
+
usdc: "0x3600000000000000000000000000000000000000",
|
|
199
|
+
gatewayWallet: TESTNET_GATEWAY_WALLET,
|
|
200
|
+
gatewayMinter: TESTNET_GATEWAY_MINTER,
|
|
201
|
+
rpcUrl: "https://rpc.testnet.arc.network"
|
|
202
|
+
},
|
|
203
|
+
avalancheFuji: {
|
|
204
|
+
chain: chains.avalancheFuji,
|
|
205
|
+
domain: GATEWAY_DOMAINS.avalancheFuji,
|
|
206
|
+
usdc: "0x5425890298aed601595a70AB815c96711a31Bc65",
|
|
207
|
+
gatewayWallet: TESTNET_GATEWAY_WALLET,
|
|
208
|
+
gatewayMinter: TESTNET_GATEWAY_MINTER
|
|
209
|
+
},
|
|
210
|
+
baseSepolia: {
|
|
211
|
+
chain: chains.baseSepolia,
|
|
212
|
+
domain: GATEWAY_DOMAINS.baseSepolia,
|
|
213
|
+
usdc: "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
|
|
214
|
+
gatewayWallet: TESTNET_GATEWAY_WALLET,
|
|
215
|
+
gatewayMinter: TESTNET_GATEWAY_MINTER,
|
|
216
|
+
rpcUrl: "https://sepolia-preconf.base.org"
|
|
217
|
+
},
|
|
218
|
+
sepolia: {
|
|
219
|
+
chain: chains.sepolia,
|
|
220
|
+
domain: GATEWAY_DOMAINS.sepolia,
|
|
221
|
+
usdc: "0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238",
|
|
222
|
+
gatewayWallet: TESTNET_GATEWAY_WALLET,
|
|
223
|
+
gatewayMinter: TESTNET_GATEWAY_MINTER
|
|
224
|
+
},
|
|
225
|
+
hyperEvmTestnet: {
|
|
226
|
+
chain: chains.hyperliquidEvmTestnet,
|
|
227
|
+
domain: GATEWAY_DOMAINS.hyperEvmTestnet,
|
|
228
|
+
usdc: "0x2B3370eE501B4a559b57D449569354196457D8Ab",
|
|
229
|
+
gatewayWallet: TESTNET_GATEWAY_WALLET,
|
|
230
|
+
gatewayMinter: TESTNET_GATEWAY_MINTER
|
|
231
|
+
},
|
|
232
|
+
optimismSepolia: {
|
|
233
|
+
chain: chains.optimismSepolia,
|
|
234
|
+
domain: GATEWAY_DOMAINS.optimismSepolia,
|
|
235
|
+
usdc: "0x5fd84259d66Cd46123540766Be93DFE6D43130D7",
|
|
236
|
+
gatewayWallet: TESTNET_GATEWAY_WALLET,
|
|
237
|
+
gatewayMinter: TESTNET_GATEWAY_MINTER
|
|
238
|
+
},
|
|
239
|
+
polygonAmoy: {
|
|
240
|
+
chain: chains.polygonAmoy,
|
|
241
|
+
domain: GATEWAY_DOMAINS.polygonAmoy,
|
|
242
|
+
usdc: "0x41E94Eb019C0762f9Bfcf9Fb1E58725BfB0e7582",
|
|
243
|
+
gatewayWallet: TESTNET_GATEWAY_WALLET,
|
|
244
|
+
gatewayMinter: TESTNET_GATEWAY_MINTER
|
|
245
|
+
},
|
|
246
|
+
seiAtlantic: {
|
|
247
|
+
chain: chains.seiTestnet,
|
|
248
|
+
domain: GATEWAY_DOMAINS.seiAtlantic,
|
|
249
|
+
usdc: "0x4fCF1784B31630811181f670Aea7A7bEF803eaED",
|
|
250
|
+
gatewayWallet: TESTNET_GATEWAY_WALLET,
|
|
251
|
+
gatewayMinter: TESTNET_GATEWAY_MINTER
|
|
252
|
+
},
|
|
253
|
+
sonicTestnet: {
|
|
254
|
+
chain: sonicTestnet,
|
|
255
|
+
domain: GATEWAY_DOMAINS.sonicTestnet,
|
|
256
|
+
usdc: "0x0BA304580ee7c9a980CF72e55f5Ed2E9fd30Bc51",
|
|
257
|
+
gatewayWallet: TESTNET_GATEWAY_WALLET,
|
|
258
|
+
gatewayMinter: TESTNET_GATEWAY_MINTER
|
|
259
|
+
},
|
|
260
|
+
unichainSepolia: {
|
|
261
|
+
chain: chains.unichainSepolia,
|
|
262
|
+
domain: GATEWAY_DOMAINS.unichainSepolia,
|
|
263
|
+
usdc: "0x31d0220469e10c4E71834a79b1f276d740d3768F",
|
|
264
|
+
gatewayWallet: TESTNET_GATEWAY_WALLET,
|
|
265
|
+
gatewayMinter: TESTNET_GATEWAY_MINTER
|
|
266
|
+
},
|
|
267
|
+
worldChainSepolia: {
|
|
268
|
+
chain: chains.worldchainSepolia,
|
|
269
|
+
domain: GATEWAY_DOMAINS.worldChainSepolia,
|
|
270
|
+
usdc: "0x66145f38cBAC35Ca6F1Dfb4914dF98F1614aeA88",
|
|
271
|
+
gatewayWallet: TESTNET_GATEWAY_WALLET,
|
|
272
|
+
gatewayMinter: TESTNET_GATEWAY_MINTER
|
|
273
|
+
},
|
|
274
|
+
// Mainnet chains
|
|
275
|
+
arbitrum: {
|
|
276
|
+
chain: chains.arbitrum,
|
|
277
|
+
domain: GATEWAY_DOMAINS.arbitrum,
|
|
278
|
+
usdc: "0xaf88d065e77c8cC2239327C5EDb3A432268e5831",
|
|
279
|
+
gatewayWallet: MAINNET_GATEWAY_WALLET,
|
|
280
|
+
gatewayMinter: MAINNET_GATEWAY_MINTER
|
|
281
|
+
},
|
|
282
|
+
avalanche: {
|
|
283
|
+
chain: chains.avalanche,
|
|
284
|
+
domain: GATEWAY_DOMAINS.avalanche,
|
|
285
|
+
usdc: "0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E",
|
|
286
|
+
gatewayWallet: MAINNET_GATEWAY_WALLET,
|
|
287
|
+
gatewayMinter: MAINNET_GATEWAY_MINTER
|
|
288
|
+
},
|
|
289
|
+
base: {
|
|
290
|
+
chain: chains.base,
|
|
291
|
+
domain: GATEWAY_DOMAINS.base,
|
|
292
|
+
usdc: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
|
|
293
|
+
gatewayWallet: MAINNET_GATEWAY_WALLET,
|
|
294
|
+
gatewayMinter: MAINNET_GATEWAY_MINTER
|
|
295
|
+
},
|
|
296
|
+
mainnet: {
|
|
297
|
+
chain: chains.mainnet,
|
|
298
|
+
domain: GATEWAY_DOMAINS.mainnet,
|
|
299
|
+
usdc: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
|
|
300
|
+
gatewayWallet: MAINNET_GATEWAY_WALLET,
|
|
301
|
+
gatewayMinter: MAINNET_GATEWAY_MINTER
|
|
302
|
+
},
|
|
303
|
+
hyperEvm: {
|
|
304
|
+
chain: chains.hyperEvm,
|
|
305
|
+
domain: GATEWAY_DOMAINS.hyperEvm,
|
|
306
|
+
usdc: "0xb88339CB7199b77E23DB6E890353E22632Ba630f",
|
|
307
|
+
gatewayWallet: MAINNET_GATEWAY_WALLET,
|
|
308
|
+
gatewayMinter: MAINNET_GATEWAY_MINTER
|
|
309
|
+
},
|
|
310
|
+
optimism: {
|
|
311
|
+
chain: chains.optimism,
|
|
312
|
+
domain: GATEWAY_DOMAINS.optimism,
|
|
313
|
+
usdc: "0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85",
|
|
314
|
+
gatewayWallet: MAINNET_GATEWAY_WALLET,
|
|
315
|
+
gatewayMinter: MAINNET_GATEWAY_MINTER
|
|
316
|
+
},
|
|
317
|
+
polygon: {
|
|
318
|
+
chain: chains.polygon,
|
|
319
|
+
domain: GATEWAY_DOMAINS.polygon,
|
|
320
|
+
usdc: "0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359",
|
|
321
|
+
gatewayWallet: MAINNET_GATEWAY_WALLET,
|
|
322
|
+
gatewayMinter: MAINNET_GATEWAY_MINTER
|
|
323
|
+
},
|
|
324
|
+
sei: {
|
|
325
|
+
chain: chains.sei,
|
|
326
|
+
domain: GATEWAY_DOMAINS.sei,
|
|
327
|
+
usdc: "0xe15fC38F6D8c56aF07bbCBe3BAf5708A2Bf42392",
|
|
328
|
+
gatewayWallet: MAINNET_GATEWAY_WALLET,
|
|
329
|
+
gatewayMinter: MAINNET_GATEWAY_MINTER
|
|
330
|
+
},
|
|
331
|
+
sonic: {
|
|
332
|
+
chain: chains.sonic,
|
|
333
|
+
domain: GATEWAY_DOMAINS.sonic,
|
|
334
|
+
usdc: "0x29219dd400f2Bf60E5a23d13Be72B486D4038894",
|
|
335
|
+
gatewayWallet: MAINNET_GATEWAY_WALLET,
|
|
336
|
+
gatewayMinter: MAINNET_GATEWAY_MINTER
|
|
337
|
+
},
|
|
338
|
+
unichain: {
|
|
339
|
+
chain: chains.unichain,
|
|
340
|
+
domain: GATEWAY_DOMAINS.unichain,
|
|
341
|
+
usdc: "0x078D782b760474a361dDA0AF3839290b0EF57AD6",
|
|
342
|
+
gatewayWallet: MAINNET_GATEWAY_WALLET,
|
|
343
|
+
gatewayMinter: MAINNET_GATEWAY_MINTER
|
|
344
|
+
},
|
|
345
|
+
worldChain: {
|
|
346
|
+
chain: chains.worldchain,
|
|
347
|
+
domain: GATEWAY_DOMAINS.worldChain,
|
|
348
|
+
usdc: "0x79A02482A880bCe3F13E09da970dC34dB4cD24D1",
|
|
349
|
+
gatewayWallet: MAINNET_GATEWAY_WALLET,
|
|
350
|
+
gatewayMinter: MAINNET_GATEWAY_MINTER
|
|
351
|
+
}
|
|
352
|
+
};
|
|
353
|
+
|
|
354
|
+
// src/server/GatewayEvmScheme.ts
|
|
355
|
+
var GatewayEvmScheme = class extends ExactEvmScheme {
|
|
356
|
+
constructor() {
|
|
357
|
+
super();
|
|
358
|
+
this.registerGatewayMoneyParsers();
|
|
359
|
+
}
|
|
360
|
+
/**
|
|
361
|
+
* Enhances payment requirements by merging the facilitator's extra data.
|
|
362
|
+
*
|
|
363
|
+
* The base `ExactEvmScheme.enhancePaymentRequirements` returns requirements
|
|
364
|
+
* unchanged, dropping `supportedKind.extra`. Gateway payments require
|
|
365
|
+
* `extra.verifyingContract` (and other fields like `name`, `version`) to
|
|
366
|
+
* be present for clients to construct the correct signing domain.
|
|
367
|
+
*/
|
|
368
|
+
async enhancePaymentRequirements(paymentRequirements, supportedKind, extensionKeys) {
|
|
369
|
+
void extensionKeys;
|
|
370
|
+
return {
|
|
371
|
+
...paymentRequirements,
|
|
372
|
+
maxTimeoutSeconds: 345600,
|
|
373
|
+
// 4 days — Gateway batches settlements asynchronously
|
|
374
|
+
extra: {
|
|
375
|
+
...paymentRequirements.extra,
|
|
376
|
+
...supportedKind.extra
|
|
377
|
+
}
|
|
378
|
+
};
|
|
379
|
+
}
|
|
380
|
+
/**
|
|
381
|
+
* Registers money parsers for all Gateway-supported networks.
|
|
382
|
+
*
|
|
383
|
+
* Builds a `network → USDC address` lookup from `CHAIN_CONFIGS` and
|
|
384
|
+
* registers a single parser that converts dollar amounts to USDC atomic
|
|
385
|
+
* units (6 decimals) for any Gateway-supported chain.
|
|
386
|
+
*
|
|
387
|
+
* Returns `null` for unknown networks so `ExactEvmScheme`'s built-in
|
|
388
|
+
* parser handles them.
|
|
389
|
+
*/
|
|
390
|
+
registerGatewayMoneyParsers() {
|
|
391
|
+
const networkUsdcMap = /* @__PURE__ */ new Map();
|
|
392
|
+
for (const config of Object.values(CHAIN_CONFIGS)) {
|
|
393
|
+
const network = `eip155:${config.chain.id}`;
|
|
394
|
+
networkUsdcMap.set(network, config.usdc);
|
|
395
|
+
}
|
|
396
|
+
this.registerMoneyParser(async (amount, network) => {
|
|
397
|
+
const usdcAddress = networkUsdcMap.get(network);
|
|
398
|
+
if (!usdcAddress) {
|
|
399
|
+
return null;
|
|
400
|
+
}
|
|
401
|
+
return {
|
|
402
|
+
amount: Math.round(amount * 1e6).toString(),
|
|
403
|
+
asset: usdcAddress
|
|
404
|
+
};
|
|
405
|
+
});
|
|
406
|
+
}
|
|
407
|
+
};
|
|
408
|
+
|
|
409
|
+
// src/constants.ts
|
|
410
|
+
var CIRCLE_BATCHING_NAME = "GatewayWalletBatched";
|
|
411
|
+
var CIRCLE_BATCHING_VERSION = "1";
|
|
412
|
+
var CIRCLE_BATCHING_SCHEME = "exact";
|
|
413
|
+
|
|
414
|
+
// src/detection.ts
|
|
415
|
+
function supportsBatching(requirements) {
|
|
416
|
+
const extra = requirements.extra;
|
|
417
|
+
if (!extra) {
|
|
418
|
+
return false;
|
|
419
|
+
}
|
|
420
|
+
return extra.name === CIRCLE_BATCHING_NAME && extra.version === CIRCLE_BATCHING_VERSION;
|
|
421
|
+
}
|
|
422
|
+
var isBatchPayment = supportsBatching;
|
|
423
|
+
|
|
424
|
+
// src/server/middleware.ts
|
|
425
|
+
function getUsdcAddress(kind) {
|
|
426
|
+
const assets = kind.extra?.assets;
|
|
427
|
+
if (!assets || assets.length === 0) return null;
|
|
428
|
+
const usdc = assets.find((a) => a.symbol === "USDC");
|
|
429
|
+
return usdc?.address ?? null;
|
|
430
|
+
}
|
|
431
|
+
function parsePrice(price) {
|
|
432
|
+
const numericPrice = price.replace(/[$]/g, "");
|
|
433
|
+
const amount = parseFloat(numericPrice);
|
|
434
|
+
if (isNaN(amount) || amount <= 0) {
|
|
435
|
+
throw new Error(`Invalid price: ${price}`);
|
|
436
|
+
}
|
|
437
|
+
return Math.round(amount * 1e6).toString();
|
|
438
|
+
}
|
|
439
|
+
function createGatewayMiddleware(config) {
|
|
440
|
+
const facilitator = new BatchFacilitatorClient({
|
|
441
|
+
url: config.facilitatorUrl
|
|
442
|
+
});
|
|
443
|
+
const configuredNetworks = config.networks ? Array.isArray(config.networks) ? config.networks : [config.networks] : null;
|
|
444
|
+
let cachedSupportedKinds = null;
|
|
445
|
+
async function getSupportedKinds() {
|
|
446
|
+
if (cachedSupportedKinds) {
|
|
447
|
+
return cachedSupportedKinds;
|
|
448
|
+
}
|
|
449
|
+
const supported = await facilitator.getSupported();
|
|
450
|
+
cachedSupportedKinds = supported.kinds;
|
|
451
|
+
return cachedSupportedKinds;
|
|
452
|
+
}
|
|
453
|
+
async function getAcceptedNetworks() {
|
|
454
|
+
const allKinds = await getSupportedKinds();
|
|
455
|
+
if (configuredNetworks) {
|
|
456
|
+
return allKinds.filter(
|
|
457
|
+
(k) => configuredNetworks.includes(k.network) && k.extra?.verifyingContract
|
|
458
|
+
);
|
|
459
|
+
}
|
|
460
|
+
return allKinds.filter((k) => k.extra?.verifyingContract);
|
|
461
|
+
}
|
|
462
|
+
async function createAllPaymentRequirements(price) {
|
|
463
|
+
const networks = await getAcceptedNetworks();
|
|
464
|
+
const amount = parsePrice(price);
|
|
465
|
+
return networks.filter((kind) => getUsdcAddress(kind)).map((kind) => ({
|
|
466
|
+
scheme: CIRCLE_BATCHING_SCHEME,
|
|
467
|
+
// Must be 'exact' for Gateway API
|
|
468
|
+
network: kind.network,
|
|
469
|
+
asset: getUsdcAddress(kind),
|
|
470
|
+
// Use actual USDC address
|
|
471
|
+
amount,
|
|
472
|
+
payTo: config.sellerAddress,
|
|
473
|
+
maxTimeoutSeconds: 345600,
|
|
474
|
+
// 4 days (same as digital-dungeon)
|
|
475
|
+
extra: {
|
|
476
|
+
name: CIRCLE_BATCHING_NAME,
|
|
477
|
+
version: CIRCLE_BATCHING_VERSION,
|
|
478
|
+
verifyingContract: kind.extra.verifyingContract
|
|
479
|
+
}
|
|
480
|
+
}));
|
|
481
|
+
}
|
|
482
|
+
async function createPaymentRequirements(price, network) {
|
|
483
|
+
const networks = await getAcceptedNetworks();
|
|
484
|
+
const kind = networks.find((k) => k.network === network);
|
|
485
|
+
if (!kind || !kind.extra?.verifyingContract) {
|
|
486
|
+
return null;
|
|
487
|
+
}
|
|
488
|
+
const usdcAddress = getUsdcAddress(kind);
|
|
489
|
+
if (!usdcAddress) {
|
|
490
|
+
return null;
|
|
491
|
+
}
|
|
492
|
+
return {
|
|
493
|
+
scheme: CIRCLE_BATCHING_SCHEME,
|
|
494
|
+
// Must be 'exact' for Gateway API
|
|
495
|
+
network: kind.network,
|
|
496
|
+
asset: usdcAddress,
|
|
497
|
+
// Use actual USDC address
|
|
498
|
+
amount: parsePrice(price),
|
|
499
|
+
payTo: config.sellerAddress,
|
|
500
|
+
maxTimeoutSeconds: 345600,
|
|
501
|
+
// 4 days
|
|
502
|
+
extra: {
|
|
503
|
+
name: CIRCLE_BATCHING_NAME,
|
|
504
|
+
version: CIRCLE_BATCHING_VERSION,
|
|
505
|
+
verifyingContract: kind.extra.verifyingContract
|
|
506
|
+
}
|
|
507
|
+
};
|
|
508
|
+
}
|
|
509
|
+
return {
|
|
510
|
+
require: (price) => {
|
|
511
|
+
return async (req, res, next) => {
|
|
512
|
+
try {
|
|
513
|
+
const paymentHeader = req.headers["payment-signature"];
|
|
514
|
+
if (!paymentHeader) {
|
|
515
|
+
const allRequirements = await createAllPaymentRequirements(price);
|
|
516
|
+
const url = req.url ?? "/";
|
|
517
|
+
const description = config.description ?? "Paid resource";
|
|
518
|
+
if (allRequirements.length === 0) {
|
|
519
|
+
res.statusCode = 503;
|
|
520
|
+
res.setHeader("Content-Type", "application/json");
|
|
521
|
+
res.end(JSON.stringify({ error: "No payment networks available" }));
|
|
522
|
+
return;
|
|
523
|
+
}
|
|
524
|
+
const paymentRequired = {
|
|
525
|
+
x402Version: 2,
|
|
526
|
+
// Version 2 required by Gateway API
|
|
527
|
+
resource: {
|
|
528
|
+
url,
|
|
529
|
+
description,
|
|
530
|
+
mimeType: "application/json"
|
|
531
|
+
},
|
|
532
|
+
accepts: allRequirements
|
|
533
|
+
};
|
|
534
|
+
const paymentRequiredHeader = Buffer.from(
|
|
535
|
+
JSON.stringify(paymentRequired)
|
|
536
|
+
).toString("base64");
|
|
537
|
+
res.statusCode = 402;
|
|
538
|
+
res.setHeader("PAYMENT-REQUIRED", paymentRequiredHeader);
|
|
539
|
+
res.setHeader("Content-Type", "application/json");
|
|
540
|
+
res.end(JSON.stringify({}));
|
|
541
|
+
return;
|
|
542
|
+
}
|
|
543
|
+
const paymentPayload = JSON.parse(
|
|
544
|
+
Buffer.from(paymentHeader, "base64").toString("utf-8")
|
|
545
|
+
);
|
|
546
|
+
const acceptedRequirements = paymentPayload.accepted;
|
|
547
|
+
if (!acceptedRequirements?.network) {
|
|
548
|
+
res.statusCode = 400;
|
|
549
|
+
res.setHeader("Content-Type", "application/json");
|
|
550
|
+
res.end(
|
|
551
|
+
JSON.stringify({ error: "Missing accepted requirements in payment" })
|
|
552
|
+
);
|
|
553
|
+
return;
|
|
554
|
+
}
|
|
555
|
+
const requirements = await createPaymentRequirements(
|
|
556
|
+
price,
|
|
557
|
+
acceptedRequirements.network
|
|
558
|
+
);
|
|
559
|
+
if (!requirements) {
|
|
560
|
+
res.statusCode = 400;
|
|
561
|
+
res.setHeader("Content-Type", "application/json");
|
|
562
|
+
res.end(
|
|
563
|
+
JSON.stringify({
|
|
564
|
+
error: `Network ${acceptedRequirements.network} not accepted`
|
|
565
|
+
})
|
|
566
|
+
);
|
|
567
|
+
return;
|
|
568
|
+
}
|
|
569
|
+
const verifyResult = await facilitator.verify(paymentPayload, requirements);
|
|
570
|
+
if (!verifyResult.isValid) {
|
|
571
|
+
res.statusCode = 402;
|
|
572
|
+
res.setHeader("Content-Type", "application/json");
|
|
573
|
+
res.end(
|
|
574
|
+
JSON.stringify({
|
|
575
|
+
error: "Payment verification failed",
|
|
576
|
+
reason: verifyResult.invalidReason
|
|
577
|
+
})
|
|
578
|
+
);
|
|
579
|
+
return;
|
|
580
|
+
}
|
|
581
|
+
const settleResult = await facilitator.settle(paymentPayload, requirements);
|
|
582
|
+
if (!settleResult.success) {
|
|
583
|
+
res.statusCode = 402;
|
|
584
|
+
res.setHeader("Content-Type", "application/json");
|
|
585
|
+
res.end(
|
|
586
|
+
JSON.stringify({
|
|
587
|
+
error: "Payment settlement failed",
|
|
588
|
+
reason: settleResult.errorReason
|
|
589
|
+
})
|
|
590
|
+
);
|
|
591
|
+
return;
|
|
592
|
+
}
|
|
593
|
+
req.payment = {
|
|
594
|
+
verified: true,
|
|
595
|
+
payer: settleResult.payer ?? verifyResult.payer ?? "",
|
|
596
|
+
amount: parsePrice(price),
|
|
597
|
+
network: requirements.network,
|
|
598
|
+
transaction: settleResult.transaction
|
|
599
|
+
};
|
|
600
|
+
const settleResponseHeader = Buffer.from(
|
|
601
|
+
JSON.stringify({
|
|
602
|
+
success: true,
|
|
603
|
+
transaction: settleResult.transaction,
|
|
604
|
+
network: requirements.network,
|
|
605
|
+
payer: settleResult.payer ?? verifyResult.payer ?? ""
|
|
606
|
+
})
|
|
607
|
+
).toString("base64");
|
|
608
|
+
res.setHeader("PAYMENT-RESPONSE", settleResponseHeader);
|
|
609
|
+
next();
|
|
610
|
+
} catch (error) {
|
|
611
|
+
res.statusCode = 500;
|
|
612
|
+
res.setHeader("Content-Type", "application/json");
|
|
613
|
+
res.end(
|
|
614
|
+
JSON.stringify({
|
|
615
|
+
error: "Payment processing error",
|
|
616
|
+
message: error.message
|
|
617
|
+
})
|
|
618
|
+
);
|
|
619
|
+
}
|
|
620
|
+
};
|
|
621
|
+
},
|
|
622
|
+
verify: async (payment) => {
|
|
623
|
+
try {
|
|
624
|
+
const paymentPayload = payment;
|
|
625
|
+
const result = await facilitator.verify(
|
|
626
|
+
paymentPayload.paymentPayload,
|
|
627
|
+
paymentPayload.paymentRequirements
|
|
628
|
+
);
|
|
629
|
+
return {
|
|
630
|
+
valid: result.isValid,
|
|
631
|
+
payer: result.payer,
|
|
632
|
+
error: result.invalidReason
|
|
633
|
+
};
|
|
634
|
+
} catch (error) {
|
|
635
|
+
return {
|
|
636
|
+
valid: false,
|
|
637
|
+
error: error.message
|
|
638
|
+
};
|
|
639
|
+
}
|
|
640
|
+
},
|
|
641
|
+
settle: async (payment) => {
|
|
642
|
+
try {
|
|
643
|
+
const paymentPayload = payment;
|
|
644
|
+
const result = await facilitator.settle(
|
|
645
|
+
paymentPayload.paymentPayload,
|
|
646
|
+
paymentPayload.paymentRequirements
|
|
647
|
+
);
|
|
648
|
+
return {
|
|
649
|
+
success: result.success,
|
|
650
|
+
transaction: result.transaction,
|
|
651
|
+
error: result.errorReason
|
|
652
|
+
};
|
|
653
|
+
} catch (error) {
|
|
654
|
+
return {
|
|
655
|
+
success: false,
|
|
656
|
+
error: error.message
|
|
657
|
+
};
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
};
|
|
661
|
+
}
|
|
662
|
+
export {
|
|
663
|
+
BatchFacilitatorClient,
|
|
664
|
+
GatewayEvmScheme,
|
|
665
|
+
createGatewayMiddleware,
|
|
666
|
+
isBatchPayment
|
|
667
|
+
};
|
|
668
|
+
//# sourceMappingURL=index.mjs.map
|