@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.
@@ -0,0 +1,1313 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/client/index.ts
31
+ var client_exports = {};
32
+ __export(client_exports, {
33
+ BatchEvmScheme: () => BatchEvmScheme,
34
+ CHAIN_CONFIGS: () => CHAIN_CONFIGS,
35
+ CompositeEvmScheme: () => CompositeEvmScheme,
36
+ GATEWAY_DOMAINS: () => GATEWAY_DOMAINS,
37
+ GatewayClient: () => GatewayClient,
38
+ registerBatchScheme: () => registerBatchScheme
39
+ });
40
+ module.exports = __toCommonJS(client_exports);
41
+
42
+ // src/client/BatchEvmScheme.ts
43
+ var import_viem = require("viem");
44
+
45
+ // src/constants.ts
46
+ var CIRCLE_BATCHING_NAME = "GatewayWalletBatched";
47
+ var CIRCLE_BATCHING_VERSION = "1";
48
+ var CIRCLE_BATCHING_SCHEME = "exact";
49
+
50
+ // src/detection.ts
51
+ function supportsBatching(requirements) {
52
+ const extra = requirements.extra;
53
+ if (!extra) {
54
+ return false;
55
+ }
56
+ return extra.name === CIRCLE_BATCHING_NAME && extra.version === CIRCLE_BATCHING_VERSION;
57
+ }
58
+ function getVerifyingContract(requirements) {
59
+ if (!supportsBatching(requirements)) {
60
+ return void 0;
61
+ }
62
+ const verifyingContract = requirements.extra?.verifyingContract;
63
+ if (typeof verifyingContract === "string") {
64
+ return verifyingContract;
65
+ }
66
+ return void 0;
67
+ }
68
+
69
+ // src/client/BatchEvmScheme.ts
70
+ var authorizationTypes = {
71
+ TransferWithAuthorization: [
72
+ { name: "from", type: "address" },
73
+ { name: "to", type: "address" },
74
+ { name: "value", type: "uint256" },
75
+ { name: "validAfter", type: "uint256" },
76
+ { name: "validBefore", type: "uint256" },
77
+ { name: "nonce", type: "bytes32" }
78
+ ]
79
+ };
80
+ function createNonce() {
81
+ const bytes = new Uint8Array(32);
82
+ crypto.getRandomValues(bytes);
83
+ return `0x${Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("")}`;
84
+ }
85
+ var BatchEvmScheme = class {
86
+ /**
87
+ * Creates a new BatchEvmScheme.
88
+ *
89
+ * @param signer - The EVM signer for signing payment authorizations
90
+ */
91
+ constructor(signer) {
92
+ this.signer = signer;
93
+ }
94
+ scheme = CIRCLE_BATCHING_SCHEME;
95
+ /**
96
+ * Creates a payment payload for Circle batching.
97
+ *
98
+ * @param x402Version - The x402 protocol version
99
+ * @param paymentRequirements - The payment requirements (must be a Circle batching option)
100
+ * @returns Promise resolving to the payment payload
101
+ * @throws Error if requirements are not a Circle batching option
102
+ */
103
+ async createPaymentPayload(x402Version, paymentRequirements) {
104
+ if (!supportsBatching(paymentRequirements)) {
105
+ throw new Error(
106
+ `BatchEvmScheme can only handle Circle batching options. Expected extra.name="${CIRCLE_BATCHING_NAME}" and extra.version="${CIRCLE_BATCHING_VERSION}"`
107
+ );
108
+ }
109
+ const verifyingContract = getVerifyingContract(paymentRequirements);
110
+ if (!verifyingContract) {
111
+ throw new Error(
112
+ "Circle batching option missing extra.verifyingContract (GatewayWallet address)"
113
+ );
114
+ }
115
+ const nonce = createNonce();
116
+ const now = Math.floor(Date.now() / 1e3);
117
+ const authorization = {
118
+ from: this.signer.address,
119
+ to: (0, import_viem.getAddress)(paymentRequirements.payTo),
120
+ value: paymentRequirements.amount,
121
+ validAfter: (now - 600).toString(),
122
+ // 10 minutes before
123
+ validBefore: (now + paymentRequirements.maxTimeoutSeconds).toString(),
124
+ nonce
125
+ };
126
+ const signature = await this.signAuthorization(
127
+ authorization,
128
+ paymentRequirements,
129
+ verifyingContract
130
+ );
131
+ const payload = {
132
+ authorization,
133
+ signature
134
+ };
135
+ return {
136
+ x402Version,
137
+ payload
138
+ };
139
+ }
140
+ /**
141
+ * Sign the EIP-3009 authorization using EIP-712.
142
+ * Uses the GatewayWallet contract as verifyingContract instead of USDC.
143
+ */
144
+ async signAuthorization(authorization, requirements, verifyingContract) {
145
+ if (!requirements.network.startsWith("eip155:")) {
146
+ throw new Error(
147
+ `BatchEvmScheme: unsupported network format "${requirements.network}". Expected "eip155:<chainId>"`
148
+ );
149
+ }
150
+ const chainId = parseInt(requirements.network.split(":")[1]);
151
+ const domain = {
152
+ name: CIRCLE_BATCHING_NAME,
153
+ version: CIRCLE_BATCHING_VERSION,
154
+ chainId,
155
+ verifyingContract: (0, import_viem.getAddress)(verifyingContract)
156
+ };
157
+ const message = {
158
+ from: (0, import_viem.getAddress)(authorization.from),
159
+ to: (0, import_viem.getAddress)(authorization.to),
160
+ value: BigInt(authorization.value),
161
+ validAfter: BigInt(authorization.validAfter),
162
+ validBefore: BigInt(authorization.validBefore),
163
+ nonce: authorization.nonce
164
+ };
165
+ return await this.signer.signTypedData({
166
+ domain,
167
+ types: authorizationTypes,
168
+ primaryType: "TransferWithAuthorization",
169
+ message
170
+ });
171
+ }
172
+ };
173
+
174
+ // src/client/CompositeEvmScheme.ts
175
+ var CompositeEvmScheme = class {
176
+ constructor(batchScheme, fallbackScheme) {
177
+ this.batchScheme = batchScheme;
178
+ this.fallbackScheme = fallbackScheme;
179
+ if (fallbackScheme.scheme !== CIRCLE_BATCHING_SCHEME) {
180
+ throw new Error(
181
+ `CompositeEvmScheme: fallbackScheme.scheme must be "${CIRCLE_BATCHING_SCHEME}", got "${fallbackScheme.scheme}"`
182
+ );
183
+ }
184
+ }
185
+ scheme = CIRCLE_BATCHING_SCHEME;
186
+ async createPaymentPayload(x402Version, paymentRequirements) {
187
+ if (supportsBatching(paymentRequirements)) {
188
+ return this.batchScheme.createPaymentPayload(x402Version, paymentRequirements);
189
+ }
190
+ return this.fallbackScheme.createPaymentPayload(x402Version, paymentRequirements);
191
+ }
192
+ };
193
+
194
+ // src/client/register.ts
195
+ function registerBatchScheme(client, config) {
196
+ const batchScheme = new BatchEvmScheme(config.signer);
197
+ const schemeToRegister = config.fallbackScheme ? new CompositeEvmScheme(batchScheme, config.fallbackScheme) : batchScheme;
198
+ const networks = config.networks ?? ["eip155:*"];
199
+ for (const network of networks) {
200
+ client.register(network, schemeToRegister);
201
+ }
202
+ return schemeToRegister;
203
+ }
204
+
205
+ // src/client/GatewayClient.ts
206
+ var import_viem2 = require("viem");
207
+ var import_accounts = require("viem/accounts");
208
+ var chains = __toESM(require("viem/chains"));
209
+ var import_crypto = require("crypto");
210
+ var sonicTestnet = (0, import_viem2.defineChain)({
211
+ id: 14601,
212
+ name: "Sonic Testnet",
213
+ nativeCurrency: { decimals: 18, name: "Sonic", symbol: "S" },
214
+ rpcUrls: {
215
+ default: { http: ["https://rpc.testnet.soniclabs.com"] }
216
+ },
217
+ blockExplorers: {
218
+ default: { name: "Sonic Testnet Explorer", url: "https://testnet.soniclabs.com/" }
219
+ },
220
+ testnet: true
221
+ });
222
+ var GATEWAY_API_TESTNET = "https://gateway-api-testnet.circle.com/v1";
223
+ var GATEWAY_API_MAINNET = "https://gateway-api.circle.com/v1";
224
+ var GATEWAY_MINTER_ABI = [
225
+ {
226
+ name: "gatewayMint",
227
+ type: "function",
228
+ stateMutability: "nonpayable",
229
+ inputs: [
230
+ { name: "attestationPayload", type: "bytes" },
231
+ { name: "signature", type: "bytes" }
232
+ ],
233
+ outputs: []
234
+ }
235
+ ];
236
+ var GATEWAY_WALLET_ABI = [
237
+ {
238
+ name: "deposit",
239
+ type: "function",
240
+ stateMutability: "nonpayable",
241
+ inputs: [
242
+ { name: "token", type: "address" },
243
+ { name: "value", type: "uint256" }
244
+ ],
245
+ outputs: []
246
+ },
247
+ {
248
+ name: "depositFor",
249
+ type: "function",
250
+ stateMutability: "nonpayable",
251
+ inputs: [
252
+ { name: "token", type: "address" },
253
+ { name: "depositor", type: "address" },
254
+ { name: "value", type: "uint256" }
255
+ ],
256
+ outputs: []
257
+ },
258
+ {
259
+ name: "totalBalance",
260
+ type: "function",
261
+ stateMutability: "view",
262
+ inputs: [
263
+ { name: "token", type: "address" },
264
+ { name: "depositor", type: "address" }
265
+ ],
266
+ outputs: [{ name: "", type: "uint256" }]
267
+ },
268
+ {
269
+ name: "availableBalance",
270
+ type: "function",
271
+ stateMutability: "view",
272
+ inputs: [
273
+ { name: "token", type: "address" },
274
+ { name: "depositor", type: "address" }
275
+ ],
276
+ outputs: [{ name: "", type: "uint256" }]
277
+ },
278
+ {
279
+ name: "withdrawingBalance",
280
+ type: "function",
281
+ stateMutability: "view",
282
+ inputs: [
283
+ { name: "token", type: "address" },
284
+ { name: "depositor", type: "address" }
285
+ ],
286
+ outputs: [{ name: "", type: "uint256" }]
287
+ },
288
+ {
289
+ name: "withdrawableBalance",
290
+ type: "function",
291
+ stateMutability: "view",
292
+ inputs: [
293
+ { name: "token", type: "address" },
294
+ { name: "depositor", type: "address" }
295
+ ],
296
+ outputs: [{ name: "", type: "uint256" }]
297
+ },
298
+ {
299
+ name: "withdrawalDelay",
300
+ type: "function",
301
+ stateMutability: "view",
302
+ inputs: [],
303
+ outputs: [{ name: "", type: "uint256" }]
304
+ },
305
+ {
306
+ name: "withdrawalBlock",
307
+ type: "function",
308
+ stateMutability: "view",
309
+ inputs: [
310
+ { name: "token", type: "address" },
311
+ { name: "depositor", type: "address" }
312
+ ],
313
+ outputs: [{ name: "", type: "uint256" }]
314
+ },
315
+ {
316
+ name: "initiateWithdrawal",
317
+ type: "function",
318
+ stateMutability: "nonpayable",
319
+ inputs: [
320
+ { name: "token", type: "address" },
321
+ { name: "value", type: "uint256" }
322
+ ],
323
+ outputs: []
324
+ },
325
+ {
326
+ name: "withdraw",
327
+ type: "function",
328
+ stateMutability: "nonpayable",
329
+ inputs: [{ name: "token", type: "address" }],
330
+ outputs: []
331
+ }
332
+ ];
333
+ var GATEWAY_DOMAINS = {
334
+ // Testnet
335
+ arbitrumSepolia: 3,
336
+ arcTestnet: 26,
337
+ avalancheFuji: 1,
338
+ baseSepolia: 6,
339
+ sepolia: 0,
340
+ hyperEvmTestnet: 19,
341
+ optimismSepolia: 2,
342
+ polygonAmoy: 7,
343
+ seiAtlantic: 16,
344
+ sonicTestnet: 13,
345
+ unichainSepolia: 10,
346
+ worldChainSepolia: 14,
347
+ // Mainnet
348
+ arbitrum: 3,
349
+ avalanche: 1,
350
+ base: 6,
351
+ mainnet: 0,
352
+ hyperEvm: 19,
353
+ optimism: 2,
354
+ polygon: 7,
355
+ sei: 16,
356
+ sonic: 13,
357
+ unichain: 10,
358
+ worldChain: 14
359
+ };
360
+ var TESTNET_GATEWAY_WALLET = "0x0077777d7EBA4688BDeF3E311b846F25870A19B9";
361
+ var TESTNET_GATEWAY_MINTER = "0x0022222ABE238Cc2C7Bb1f21003F0a260052475B";
362
+ var MAINNET_GATEWAY_WALLET = "0x77777777Dcc4d5A8B6E418Fd04D8997ef11000eE";
363
+ var MAINNET_GATEWAY_MINTER = "0x2222222d7164433c4C09B0b0D809a9b52C04C205";
364
+ var CHAIN_CONFIGS = {
365
+ // Testnet chains
366
+ arbitrumSepolia: {
367
+ chain: chains.arbitrumSepolia,
368
+ domain: GATEWAY_DOMAINS.arbitrumSepolia,
369
+ usdc: "0x75faf114eafb1BDbe2F0316DF893fd58CE46AA4d",
370
+ gatewayWallet: TESTNET_GATEWAY_WALLET,
371
+ gatewayMinter: TESTNET_GATEWAY_MINTER
372
+ },
373
+ arcTestnet: {
374
+ chain: chains.arcTestnet,
375
+ domain: GATEWAY_DOMAINS.arcTestnet,
376
+ usdc: "0x3600000000000000000000000000000000000000",
377
+ gatewayWallet: TESTNET_GATEWAY_WALLET,
378
+ gatewayMinter: TESTNET_GATEWAY_MINTER,
379
+ rpcUrl: "https://rpc.testnet.arc.network"
380
+ },
381
+ avalancheFuji: {
382
+ chain: chains.avalancheFuji,
383
+ domain: GATEWAY_DOMAINS.avalancheFuji,
384
+ usdc: "0x5425890298aed601595a70AB815c96711a31Bc65",
385
+ gatewayWallet: TESTNET_GATEWAY_WALLET,
386
+ gatewayMinter: TESTNET_GATEWAY_MINTER
387
+ },
388
+ baseSepolia: {
389
+ chain: chains.baseSepolia,
390
+ domain: GATEWAY_DOMAINS.baseSepolia,
391
+ usdc: "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
392
+ gatewayWallet: TESTNET_GATEWAY_WALLET,
393
+ gatewayMinter: TESTNET_GATEWAY_MINTER,
394
+ rpcUrl: "https://sepolia-preconf.base.org"
395
+ },
396
+ sepolia: {
397
+ chain: chains.sepolia,
398
+ domain: GATEWAY_DOMAINS.sepolia,
399
+ usdc: "0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238",
400
+ gatewayWallet: TESTNET_GATEWAY_WALLET,
401
+ gatewayMinter: TESTNET_GATEWAY_MINTER
402
+ },
403
+ hyperEvmTestnet: {
404
+ chain: chains.hyperliquidEvmTestnet,
405
+ domain: GATEWAY_DOMAINS.hyperEvmTestnet,
406
+ usdc: "0x2B3370eE501B4a559b57D449569354196457D8Ab",
407
+ gatewayWallet: TESTNET_GATEWAY_WALLET,
408
+ gatewayMinter: TESTNET_GATEWAY_MINTER
409
+ },
410
+ optimismSepolia: {
411
+ chain: chains.optimismSepolia,
412
+ domain: GATEWAY_DOMAINS.optimismSepolia,
413
+ usdc: "0x5fd84259d66Cd46123540766Be93DFE6D43130D7",
414
+ gatewayWallet: TESTNET_GATEWAY_WALLET,
415
+ gatewayMinter: TESTNET_GATEWAY_MINTER
416
+ },
417
+ polygonAmoy: {
418
+ chain: chains.polygonAmoy,
419
+ domain: GATEWAY_DOMAINS.polygonAmoy,
420
+ usdc: "0x41E94Eb019C0762f9Bfcf9Fb1E58725BfB0e7582",
421
+ gatewayWallet: TESTNET_GATEWAY_WALLET,
422
+ gatewayMinter: TESTNET_GATEWAY_MINTER
423
+ },
424
+ seiAtlantic: {
425
+ chain: chains.seiTestnet,
426
+ domain: GATEWAY_DOMAINS.seiAtlantic,
427
+ usdc: "0x4fCF1784B31630811181f670Aea7A7bEF803eaED",
428
+ gatewayWallet: TESTNET_GATEWAY_WALLET,
429
+ gatewayMinter: TESTNET_GATEWAY_MINTER
430
+ },
431
+ sonicTestnet: {
432
+ chain: sonicTestnet,
433
+ domain: GATEWAY_DOMAINS.sonicTestnet,
434
+ usdc: "0x0BA304580ee7c9a980CF72e55f5Ed2E9fd30Bc51",
435
+ gatewayWallet: TESTNET_GATEWAY_WALLET,
436
+ gatewayMinter: TESTNET_GATEWAY_MINTER
437
+ },
438
+ unichainSepolia: {
439
+ chain: chains.unichainSepolia,
440
+ domain: GATEWAY_DOMAINS.unichainSepolia,
441
+ usdc: "0x31d0220469e10c4E71834a79b1f276d740d3768F",
442
+ gatewayWallet: TESTNET_GATEWAY_WALLET,
443
+ gatewayMinter: TESTNET_GATEWAY_MINTER
444
+ },
445
+ worldChainSepolia: {
446
+ chain: chains.worldchainSepolia,
447
+ domain: GATEWAY_DOMAINS.worldChainSepolia,
448
+ usdc: "0x66145f38cBAC35Ca6F1Dfb4914dF98F1614aeA88",
449
+ gatewayWallet: TESTNET_GATEWAY_WALLET,
450
+ gatewayMinter: TESTNET_GATEWAY_MINTER
451
+ },
452
+ // Mainnet chains
453
+ arbitrum: {
454
+ chain: chains.arbitrum,
455
+ domain: GATEWAY_DOMAINS.arbitrum,
456
+ usdc: "0xaf88d065e77c8cC2239327C5EDb3A432268e5831",
457
+ gatewayWallet: MAINNET_GATEWAY_WALLET,
458
+ gatewayMinter: MAINNET_GATEWAY_MINTER
459
+ },
460
+ avalanche: {
461
+ chain: chains.avalanche,
462
+ domain: GATEWAY_DOMAINS.avalanche,
463
+ usdc: "0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E",
464
+ gatewayWallet: MAINNET_GATEWAY_WALLET,
465
+ gatewayMinter: MAINNET_GATEWAY_MINTER
466
+ },
467
+ base: {
468
+ chain: chains.base,
469
+ domain: GATEWAY_DOMAINS.base,
470
+ usdc: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
471
+ gatewayWallet: MAINNET_GATEWAY_WALLET,
472
+ gatewayMinter: MAINNET_GATEWAY_MINTER
473
+ },
474
+ mainnet: {
475
+ chain: chains.mainnet,
476
+ domain: GATEWAY_DOMAINS.mainnet,
477
+ usdc: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
478
+ gatewayWallet: MAINNET_GATEWAY_WALLET,
479
+ gatewayMinter: MAINNET_GATEWAY_MINTER
480
+ },
481
+ hyperEvm: {
482
+ chain: chains.hyperEvm,
483
+ domain: GATEWAY_DOMAINS.hyperEvm,
484
+ usdc: "0xb88339CB7199b77E23DB6E890353E22632Ba630f",
485
+ gatewayWallet: MAINNET_GATEWAY_WALLET,
486
+ gatewayMinter: MAINNET_GATEWAY_MINTER
487
+ },
488
+ optimism: {
489
+ chain: chains.optimism,
490
+ domain: GATEWAY_DOMAINS.optimism,
491
+ usdc: "0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85",
492
+ gatewayWallet: MAINNET_GATEWAY_WALLET,
493
+ gatewayMinter: MAINNET_GATEWAY_MINTER
494
+ },
495
+ polygon: {
496
+ chain: chains.polygon,
497
+ domain: GATEWAY_DOMAINS.polygon,
498
+ usdc: "0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359",
499
+ gatewayWallet: MAINNET_GATEWAY_WALLET,
500
+ gatewayMinter: MAINNET_GATEWAY_MINTER
501
+ },
502
+ sei: {
503
+ chain: chains.sei,
504
+ domain: GATEWAY_DOMAINS.sei,
505
+ usdc: "0xe15fC38F6D8c56aF07bbCBe3BAf5708A2Bf42392",
506
+ gatewayWallet: MAINNET_GATEWAY_WALLET,
507
+ gatewayMinter: MAINNET_GATEWAY_MINTER
508
+ },
509
+ sonic: {
510
+ chain: chains.sonic,
511
+ domain: GATEWAY_DOMAINS.sonic,
512
+ usdc: "0x29219dd400f2Bf60E5a23d13Be72B486D4038894",
513
+ gatewayWallet: MAINNET_GATEWAY_WALLET,
514
+ gatewayMinter: MAINNET_GATEWAY_MINTER
515
+ },
516
+ unichain: {
517
+ chain: chains.unichain,
518
+ domain: GATEWAY_DOMAINS.unichain,
519
+ usdc: "0x078D782b760474a361dDA0AF3839290b0EF57AD6",
520
+ gatewayWallet: MAINNET_GATEWAY_WALLET,
521
+ gatewayMinter: MAINNET_GATEWAY_MINTER
522
+ },
523
+ worldChain: {
524
+ chain: chains.worldchain,
525
+ domain: GATEWAY_DOMAINS.worldChain,
526
+ usdc: "0x79A02482A880bCe3F13E09da970dC34dB4cD24D1",
527
+ gatewayWallet: MAINNET_GATEWAY_WALLET,
528
+ gatewayMinter: MAINNET_GATEWAY_MINTER
529
+ }
530
+ };
531
+ var GatewayClient = class {
532
+ chainConfig;
533
+ account;
534
+ publicClient;
535
+ walletClient;
536
+ /**
537
+ * Creates a new GatewayClient.
538
+ *
539
+ * @param config - Configuration including chain and private key
540
+ */
541
+ constructor(config) {
542
+ const chainConfig = CHAIN_CONFIGS[config.chain];
543
+ if (!chainConfig) {
544
+ throw new Error(`Unsupported chain: ${config.chain}`);
545
+ }
546
+ this.chainConfig = chainConfig;
547
+ this.account = (0, import_accounts.privateKeyToAccount)(config.privateKey);
548
+ const rpcUrl = config.rpcUrl ?? chainConfig.rpcUrl;
549
+ const transport = rpcUrl ? (0, import_viem2.http)(rpcUrl) : (0, import_viem2.http)();
550
+ this.publicClient = (0, import_viem2.createPublicClient)({
551
+ chain: chainConfig.chain,
552
+ transport
553
+ });
554
+ this.walletClient = (0, import_viem2.createWalletClient)({
555
+ account: this.account,
556
+ chain: chainConfig.chain,
557
+ transport
558
+ });
559
+ }
560
+ /**
561
+ * Get the account address.
562
+ */
563
+ get address() {
564
+ return this.account.address;
565
+ }
566
+ /**
567
+ * Get the chain name.
568
+ */
569
+ get chainName() {
570
+ return this.chainConfig.chain.name;
571
+ }
572
+ /**
573
+ * Get the Gateway domain for this chain.
574
+ */
575
+ get domain() {
576
+ return this.chainConfig.domain;
577
+ }
578
+ /**
579
+ * Get the chain name key for this client.
580
+ */
581
+ getChainName() {
582
+ for (const [name, config] of Object.entries(CHAIN_CONFIGS)) {
583
+ if (config.domain === this.chainConfig.domain && config.chain.id === this.chainConfig.chain.id) {
584
+ return name;
585
+ }
586
+ }
587
+ throw new Error("Unknown chain configuration");
588
+ }
589
+ /**
590
+ * Get the USDC balance of the account (not in Gateway).
591
+ *
592
+ * @param address - Optional address to check (defaults to account address)
593
+ * @returns USDC balance in atomic units and formatted
594
+ */
595
+ async getUsdcBalance(address) {
596
+ const target = address ?? this.account.address;
597
+ const balance = await this.publicClient.readContract({
598
+ address: this.chainConfig.usdc,
599
+ abi: import_viem2.erc20Abi,
600
+ functionName: "balanceOf",
601
+ args: [target]
602
+ });
603
+ return {
604
+ balance,
605
+ formatted: (0, import_viem2.formatUnits)(balance, 6)
606
+ };
607
+ }
608
+ /**
609
+ * Get all balances (wallet + gateway) in one call.
610
+ *
611
+ * @param address - Optional address to check (defaults to account address)
612
+ * @returns All balances
613
+ */
614
+ async getBalances(address) {
615
+ const target = address ?? this.account.address;
616
+ const [wallet, gateway] = await Promise.all([
617
+ this.getUsdcBalance(target),
618
+ this.getGatewayBalance(target)
619
+ ]);
620
+ return {
621
+ wallet,
622
+ gateway
623
+ };
624
+ }
625
+ /**
626
+ * Get the Gateway balance for an address.
627
+ *
628
+ * @param address - Optional address to check (defaults to account address)
629
+ * @returns Gateway balance information
630
+ * @deprecated Use `getBalances()` instead for a unified view
631
+ */
632
+ async getBalance(address) {
633
+ return this.getGatewayBalance(address);
634
+ }
635
+ /**
636
+ * Get the Gateway balance for an address via Gateway API.
637
+ */
638
+ async getGatewayBalance(address) {
639
+ const target = address ?? this.account.address;
640
+ const apiBaseUrl = this.isTestnet() ? GATEWAY_API_TESTNET : GATEWAY_API_MAINNET;
641
+ const response = await fetch(`${apiBaseUrl}/balances`, {
642
+ method: "POST",
643
+ headers: { "Content-Type": "application/json" },
644
+ body: JSON.stringify({
645
+ token: "USDC",
646
+ sources: [{ depositor: target, domain: this.chainConfig.domain }]
647
+ })
648
+ });
649
+ const data = await response.json();
650
+ if (!response.ok) {
651
+ throw new Error(
652
+ `Gateway API balance fetch failed: ${data.message ?? response.statusText}`
653
+ );
654
+ }
655
+ if (!data.balances || data.balances.length === 0) {
656
+ throw new Error("Gateway API returned no balances for the depositor.");
657
+ }
658
+ const balanceData = data.balances[0];
659
+ const available = (0, import_viem2.parseUnits)(balanceData.balance, 6);
660
+ const withdrawing = (0, import_viem2.parseUnits)(balanceData.withdrawing ?? "0", 6);
661
+ const withdrawable = (0, import_viem2.parseUnits)(balanceData.withdrawable ?? "0", 6);
662
+ const total = available + withdrawing;
663
+ return {
664
+ total,
665
+ available,
666
+ withdrawing,
667
+ withdrawable,
668
+ formattedTotal: (0, import_viem2.formatUnits)(total, 6),
669
+ formattedAvailable: (0, import_viem2.formatUnits)(available, 6),
670
+ formattedWithdrawing: (0, import_viem2.formatUnits)(withdrawing, 6),
671
+ formattedWithdrawable: (0, import_viem2.formatUnits)(withdrawable, 6)
672
+ };
673
+ }
674
+ /**
675
+ * Deposit USDC into the Gateway Wallet.
676
+ *
677
+ * This method first approves the Gateway Wallet contract to spend USDC,
678
+ * then deposits the specified amount.
679
+ *
680
+ * @param amount - Amount of USDC to deposit (as a decimal string, e.g., "10.5")
681
+ * @param options - Optional deposit options
682
+ * @returns Deposit result with transaction hashes
683
+ */
684
+ async deposit(amount, options) {
685
+ const depositAmount = (0, import_viem2.parseUnits)(amount, 6);
686
+ const approveAmount = (0, import_viem2.parseUnits)(options?.approveAmount ?? amount, 6);
687
+ const { balance } = await this.getUsdcBalance();
688
+ if (balance < depositAmount) {
689
+ throw new Error(
690
+ `Insufficient USDC balance. Have: ${(0, import_viem2.formatUnits)(balance, 6)}, Need: ${amount}`
691
+ );
692
+ }
693
+ let approvalTxHash;
694
+ if (!options?.skipApprovalCheck) {
695
+ const allowance = await this.publicClient.readContract({
696
+ address: this.chainConfig.usdc,
697
+ abi: import_viem2.erc20Abi,
698
+ functionName: "allowance",
699
+ args: [this.account.address, this.chainConfig.gatewayWallet]
700
+ });
701
+ if (allowance < depositAmount) {
702
+ approvalTxHash = await this.walletClient.writeContract({
703
+ address: this.chainConfig.usdc,
704
+ abi: import_viem2.erc20Abi,
705
+ functionName: "approve",
706
+ args: [this.chainConfig.gatewayWallet, approveAmount]
707
+ });
708
+ try {
709
+ await this.publicClient.waitForTransactionReceipt({ hash: approvalTxHash });
710
+ } catch (err) {
711
+ throw new Error(`Approval transaction failed: ${approvalTxHash}`, {
712
+ cause: err
713
+ });
714
+ }
715
+ }
716
+ }
717
+ const depositTxHash = await this.walletClient.writeContract({
718
+ address: this.chainConfig.gatewayWallet,
719
+ abi: GATEWAY_WALLET_ABI,
720
+ functionName: "deposit",
721
+ args: [this.chainConfig.usdc, depositAmount],
722
+ gas: 120000n
723
+ });
724
+ try {
725
+ await this.publicClient.waitForTransactionReceipt({ hash: depositTxHash });
726
+ } catch (err) {
727
+ throw new Error(`Deposit transaction failed: ${depositTxHash}`, {
728
+ cause: err
729
+ });
730
+ }
731
+ return {
732
+ approvalTxHash,
733
+ depositTxHash,
734
+ amount: depositAmount,
735
+ formattedAmount: amount,
736
+ depositor: this.account.address
737
+ };
738
+ }
739
+ /**
740
+ * Deposit USDC into the Gateway Wallet on behalf of another address.
741
+ *
742
+ * The resulting balance belongs to the specified depositor address,
743
+ * not the caller.
744
+ *
745
+ * @param amount - Amount of USDC to deposit (as a decimal string)
746
+ * @param depositor - Address that will own the resulting balance
747
+ * @param options - Optional deposit options
748
+ * @returns Deposit result with transaction hashes
749
+ */
750
+ async depositFor(amount, depositor, options) {
751
+ const depositAmount = (0, import_viem2.parseUnits)(amount, 6);
752
+ const approveAmount = (0, import_viem2.parseUnits)(options?.approveAmount ?? amount, 6);
753
+ const { balance } = await this.getUsdcBalance();
754
+ if (balance < depositAmount) {
755
+ throw new Error(
756
+ `Insufficient USDC balance. Have: ${(0, import_viem2.formatUnits)(balance, 6)}, Need: ${amount}`
757
+ );
758
+ }
759
+ let approvalTxHash;
760
+ if (!options?.skipApprovalCheck) {
761
+ const allowance = await this.publicClient.readContract({
762
+ address: this.chainConfig.usdc,
763
+ abi: import_viem2.erc20Abi,
764
+ functionName: "allowance",
765
+ args: [this.account.address, this.chainConfig.gatewayWallet]
766
+ });
767
+ if (allowance < depositAmount) {
768
+ approvalTxHash = await this.walletClient.writeContract({
769
+ address: this.chainConfig.usdc,
770
+ abi: import_viem2.erc20Abi,
771
+ functionName: "approve",
772
+ args: [this.chainConfig.gatewayWallet, approveAmount]
773
+ });
774
+ try {
775
+ await this.publicClient.waitForTransactionReceipt({ hash: approvalTxHash });
776
+ } catch (err) {
777
+ throw new Error(`Approval transaction failed: ${approvalTxHash}`, {
778
+ cause: err
779
+ });
780
+ }
781
+ }
782
+ }
783
+ const depositTxHash = await this.walletClient.writeContract({
784
+ address: this.chainConfig.gatewayWallet,
785
+ abi: GATEWAY_WALLET_ABI,
786
+ functionName: "depositFor",
787
+ args: [this.chainConfig.usdc, depositor, depositAmount],
788
+ gas: 120000n
789
+ });
790
+ try {
791
+ await this.publicClient.waitForTransactionReceipt({ hash: depositTxHash });
792
+ } catch (err) {
793
+ throw new Error(`Deposit transaction failed: ${depositTxHash}`, {
794
+ cause: err
795
+ });
796
+ }
797
+ return {
798
+ approvalTxHash,
799
+ depositTxHash,
800
+ amount: depositAmount,
801
+ formattedAmount: amount,
802
+ depositor
803
+ };
804
+ }
805
+ // ============================================================================
806
+ // PAYMENT (Pay for x402 resources)
807
+ // ============================================================================
808
+ /**
809
+ * Check if a URL supports Gateway batching before paying.
810
+ *
811
+ * @param url - The URL to check
812
+ * @returns Whether batching is supported and payment requirements
813
+ */
814
+ async supports(url) {
815
+ try {
816
+ const response = await fetch(url);
817
+ if (response.status !== 402) {
818
+ return { supported: false, error: "Resource does not require payment (not 402)" };
819
+ }
820
+ const paymentRequiredHeader = response.headers.get("PAYMENT-REQUIRED");
821
+ if (!paymentRequiredHeader) {
822
+ return {
823
+ supported: false,
824
+ error: "Missing PAYMENT-REQUIRED header in 402 response"
825
+ };
826
+ }
827
+ const data = JSON.parse(
828
+ Buffer.from(paymentRequiredHeader, "base64").toString("utf-8")
829
+ );
830
+ const accepts = data.accepts;
831
+ if (!accepts || accepts.length === 0) {
832
+ return { supported: false, error: "No payment options in 402 response" };
833
+ }
834
+ const expectedNetwork = `eip155:${this.chainConfig.chain.id}`;
835
+ const batchingOption = accepts.find((opt) => {
836
+ const extra = opt.extra;
837
+ return opt.network === expectedNetwork && extra?.name === "GatewayWalletBatched" && extra?.version === "1" && typeof extra?.verifyingContract === "string";
838
+ });
839
+ if (!batchingOption) {
840
+ return {
841
+ supported: false,
842
+ error: `No Gateway batching option available for network ${expectedNetwork} (${this.chainConfig.chain.name})`
843
+ };
844
+ }
845
+ return { supported: true, requirements: batchingOption };
846
+ } catch (error) {
847
+ return { supported: false, error: error.message };
848
+ }
849
+ }
850
+ /**
851
+ * Pay for an x402-protected resource.
852
+ *
853
+ * This method handles the full 402 payment flow automatically:
854
+ * 1. Makes initial request
855
+ * 2. If 402, finds Gateway batching option
856
+ * 3. Signs payment authorization
857
+ * 4. Retries with Payment-Signature header
858
+ *
859
+ * @param url - The URL to pay for
860
+ * @param options - Optional request options
861
+ * @returns The response data and payment info
862
+ *
863
+ * @example
864
+ * ```typescript
865
+ * const { data, amount } = await gateway.pay('https://api.example.com/resource');
866
+ * console.log('Paid', amount, 'USDC for:', data);
867
+ * ```
868
+ */
869
+ async pay(url, options) {
870
+ const method = options?.method ?? "GET";
871
+ const headers = {
872
+ "Content-Type": "application/json",
873
+ ...options?.headers
874
+ };
875
+ const serializedBody = options?.body !== void 0 ? typeof options.body === "string" ? options.body : JSON.stringify(options.body) : void 0;
876
+ const initialResponse = await fetch(url, {
877
+ method,
878
+ headers,
879
+ body: serializedBody
880
+ });
881
+ if (initialResponse.status !== 402) {
882
+ if (initialResponse.ok) {
883
+ const data2 = await initialResponse.json();
884
+ return {
885
+ data: data2,
886
+ amount: 0n,
887
+ formattedAmount: "0",
888
+ transaction: "",
889
+ status: initialResponse.status
890
+ };
891
+ }
892
+ throw new Error(`Request failed with status ${initialResponse.status}`);
893
+ }
894
+ const paymentRequiredHeader = initialResponse.headers.get("PAYMENT-REQUIRED");
895
+ if (!paymentRequiredHeader) {
896
+ throw new Error("Missing PAYMENT-REQUIRED header in 402 response");
897
+ }
898
+ const paymentRequired = JSON.parse(
899
+ Buffer.from(paymentRequiredHeader, "base64").toString("utf-8")
900
+ );
901
+ const accepts = paymentRequired.accepts;
902
+ if (!accepts || accepts.length === 0) {
903
+ throw new Error("No payment options in 402 response");
904
+ }
905
+ const expectedNetwork = `eip155:${this.chainConfig.chain.id}`;
906
+ const batchingOption = accepts.find((opt) => {
907
+ const extra = opt.extra;
908
+ return opt.network === expectedNetwork && extra?.name === "GatewayWalletBatched" && extra?.version === "1" && typeof extra?.verifyingContract === "string";
909
+ });
910
+ if (!batchingOption) {
911
+ throw new Error(
912
+ `No Gateway batching option available for network ${expectedNetwork} (${this.chainConfig.chain.name}). The seller may not support this chain. Use supports() to check first.`
913
+ );
914
+ }
915
+ const paymentPayload = await this.createPaymentPayload(
916
+ paymentRequired.x402Version ?? 2,
917
+ batchingOption
918
+ );
919
+ const paymentHeader = Buffer.from(
920
+ JSON.stringify({
921
+ ...paymentPayload,
922
+ resource: paymentRequired.resource,
923
+ accepted: batchingOption
924
+ })
925
+ ).toString("base64");
926
+ const paidResponse = await fetch(url, {
927
+ method,
928
+ headers: {
929
+ ...headers,
930
+ "Payment-Signature": paymentHeader
931
+ },
932
+ body: serializedBody
933
+ });
934
+ if (!paidResponse.ok) {
935
+ const error = await paidResponse.json().catch(() => ({}));
936
+ throw new Error(
937
+ `Payment failed: ${error.error || paidResponse.statusText}`
938
+ );
939
+ }
940
+ const data = await paidResponse.json();
941
+ const amount = BigInt(batchingOption.amount);
942
+ let transaction = "";
943
+ const paymentResponseHeader = paidResponse.headers.get("PAYMENT-RESPONSE");
944
+ if (paymentResponseHeader) {
945
+ const settleResponse = JSON.parse(
946
+ Buffer.from(paymentResponseHeader, "base64").toString("utf-8")
947
+ );
948
+ transaction = settleResponse.transaction ?? "";
949
+ }
950
+ return {
951
+ data,
952
+ amount,
953
+ formattedAmount: (0, import_viem2.formatUnits)(amount, 6),
954
+ transaction,
955
+ status: paidResponse.status
956
+ };
957
+ }
958
+ /**
959
+ * Create a payment payload for x402 (low-level).
960
+ */
961
+ async createPaymentPayload(x402Version, requirements) {
962
+ const extra = requirements.extra;
963
+ const verifyingContract = extra.verifyingContract;
964
+ const network = requirements.network;
965
+ const chainId = parseInt(network.split(":")[1]);
966
+ const nonce = `0x${(0, import_crypto.randomBytes)(32).toString("hex")}`;
967
+ const now = Math.floor(Date.now() / 1e3);
968
+ const authorization = {
969
+ from: this.account.address,
970
+ to: requirements.payTo,
971
+ value: requirements.amount,
972
+ validAfter: (now - 600).toString(),
973
+ validBefore: (now + requirements.maxTimeoutSeconds).toString(),
974
+ nonce
975
+ };
976
+ const signature = await this.account.signTypedData({
977
+ domain: {
978
+ name: "GatewayWalletBatched",
979
+ version: "1",
980
+ chainId,
981
+ verifyingContract
982
+ },
983
+ types: {
984
+ TransferWithAuthorization: [
985
+ { name: "from", type: "address" },
986
+ { name: "to", type: "address" },
987
+ { name: "value", type: "uint256" },
988
+ { name: "validAfter", type: "uint256" },
989
+ { name: "validBefore", type: "uint256" },
990
+ { name: "nonce", type: "bytes32" }
991
+ ]
992
+ },
993
+ primaryType: "TransferWithAuthorization",
994
+ message: {
995
+ from: authorization.from,
996
+ to: authorization.to,
997
+ value: BigInt(authorization.value),
998
+ validAfter: BigInt(authorization.validAfter),
999
+ validBefore: BigInt(authorization.validBefore),
1000
+ nonce: authorization.nonce
1001
+ }
1002
+ });
1003
+ return {
1004
+ x402Version,
1005
+ payload: { authorization, signature }
1006
+ };
1007
+ }
1008
+ // ============================================================================
1009
+ // WITHDRAW (Instant way to get USDC out)
1010
+ // ============================================================================
1011
+ /**
1012
+ * Withdraw USDC from Gateway to your wallet.
1013
+ *
1014
+ * By default, withdraws to the same chain (instant, no 7-day delay).
1015
+ * Optionally, withdraw to a different chain (requires gas on destination).
1016
+ *
1017
+ * @param amount - Amount of USDC to withdraw (as a decimal string)
1018
+ * @param options - Optional withdrawal options
1019
+ * @returns Withdrawal result with transaction hash
1020
+ *
1021
+ * @example
1022
+ * ```typescript
1023
+ * // Withdraw to same chain (instant!)
1024
+ * await gateway.withdraw('50');
1025
+ *
1026
+ * // Withdraw to Base Sepolia (requires ETH on Base for gas)
1027
+ * await gateway.withdraw('25', { chain: 'baseSepolia' });
1028
+ * ```
1029
+ */
1030
+ async withdraw(amount, options) {
1031
+ const destinationChainName = options?.chain ?? this.getChainName();
1032
+ const destConfig = CHAIN_CONFIGS[destinationChainName];
1033
+ if (!destConfig) {
1034
+ throw new Error(`Unsupported destination chain: ${destinationChainName}`);
1035
+ }
1036
+ const withdrawAmount = (0, import_viem2.parseUnits)(amount, 6);
1037
+ const recipient = options?.recipient ?? this.account.address;
1038
+ const maxFee = (0, import_viem2.parseUnits)(options?.maxFee ?? "2.01", 6);
1039
+ const balances = await this.getBalances();
1040
+ if (balances.gateway.available < withdrawAmount) {
1041
+ throw new Error(
1042
+ `Insufficient available balance. Have: ${balances.gateway.formattedAvailable}, Need: ${amount}`
1043
+ );
1044
+ }
1045
+ const burnIntent = this.createBurnIntent(
1046
+ this.chainConfig,
1047
+ destConfig,
1048
+ withdrawAmount,
1049
+ recipient,
1050
+ maxFee
1051
+ );
1052
+ const signature = await this.account.signTypedData({
1053
+ domain: { name: "GatewayWallet", version: "1" },
1054
+ types: {
1055
+ EIP712Domain: [
1056
+ { name: "name", type: "string" },
1057
+ { name: "version", type: "string" }
1058
+ ],
1059
+ TransferSpec: [
1060
+ { name: "version", type: "uint32" },
1061
+ { name: "sourceDomain", type: "uint32" },
1062
+ { name: "destinationDomain", type: "uint32" },
1063
+ { name: "sourceContract", type: "bytes32" },
1064
+ { name: "destinationContract", type: "bytes32" },
1065
+ { name: "sourceToken", type: "bytes32" },
1066
+ { name: "destinationToken", type: "bytes32" },
1067
+ { name: "sourceDepositor", type: "bytes32" },
1068
+ { name: "destinationRecipient", type: "bytes32" },
1069
+ { name: "sourceSigner", type: "bytes32" },
1070
+ { name: "destinationCaller", type: "bytes32" },
1071
+ { name: "value", type: "uint256" },
1072
+ { name: "salt", type: "bytes32" },
1073
+ { name: "hookData", type: "bytes" }
1074
+ ],
1075
+ BurnIntent: [
1076
+ { name: "maxBlockHeight", type: "uint256" },
1077
+ { name: "maxFee", type: "uint256" },
1078
+ { name: "spec", type: "TransferSpec" }
1079
+ ]
1080
+ },
1081
+ primaryType: "BurnIntent",
1082
+ message: burnIntent
1083
+ });
1084
+ const apiUrl = this.isTestnet() ? GATEWAY_API_TESTNET : GATEWAY_API_MAINNET;
1085
+ const response = await fetch(`${apiUrl}/transfer`, {
1086
+ method: "POST",
1087
+ headers: { "Content-Type": "application/json" },
1088
+ body: JSON.stringify(
1089
+ [{ burnIntent, signature }],
1090
+ (_, v) => typeof v === "bigint" ? v.toString() : v
1091
+ )
1092
+ });
1093
+ const result = await response.json();
1094
+ if (result.success === false || result.error || !result.attestation || !result.signature) {
1095
+ throw new Error(
1096
+ `Gateway API error: ${result.message || result.error || JSON.stringify(result)}`
1097
+ );
1098
+ }
1099
+ const destTransport = destConfig.rpcUrl ? (0, import_viem2.http)(destConfig.rpcUrl) : (0, import_viem2.http)();
1100
+ const destWalletClient = (0, import_viem2.createWalletClient)({
1101
+ account: this.account,
1102
+ chain: destConfig.chain,
1103
+ transport: destTransport
1104
+ });
1105
+ const destPublicClient = (0, import_viem2.createPublicClient)({
1106
+ chain: destConfig.chain,
1107
+ transport: destTransport
1108
+ });
1109
+ const mintTxHash = await destWalletClient.writeContract({
1110
+ address: destConfig.gatewayMinter,
1111
+ abi: GATEWAY_MINTER_ABI,
1112
+ functionName: "gatewayMint",
1113
+ args: [result.attestation, result.signature]
1114
+ });
1115
+ try {
1116
+ await destPublicClient.waitForTransactionReceipt({ hash: mintTxHash });
1117
+ } catch (err) {
1118
+ throw new Error(`Mint transaction failed: ${mintTxHash}`, { cause: err });
1119
+ }
1120
+ return {
1121
+ mintTxHash,
1122
+ amount: withdrawAmount,
1123
+ formattedAmount: amount,
1124
+ sourceChain: this.chainConfig.chain.name,
1125
+ destinationChain: destConfig.chain.name,
1126
+ recipient
1127
+ };
1128
+ }
1129
+ /**
1130
+ * Transfer USDC to any supported chain (alias for withdraw with destination chain).
1131
+ * @deprecated Use `withdraw({ chain })` instead
1132
+ */
1133
+ async transfer(amount, destinationChain, recipient) {
1134
+ return this.withdraw(amount, { chain: destinationChain, recipient });
1135
+ }
1136
+ /**
1137
+ * Create a burn intent for a transfer.
1138
+ */
1139
+ createBurnIntent(from, to, value, recipient, maxFee) {
1140
+ const addressToBytes32 = (addr) => (0, import_viem2.pad)(addr.toLowerCase(), { size: 32 });
1141
+ return {
1142
+ maxBlockHeight: import_viem2.maxUint256,
1143
+ maxFee,
1144
+ spec: {
1145
+ version: 1,
1146
+ sourceDomain: from.domain,
1147
+ destinationDomain: to.domain,
1148
+ sourceContract: addressToBytes32(from.gatewayWallet),
1149
+ destinationContract: addressToBytes32(to.gatewayMinter),
1150
+ sourceToken: addressToBytes32(from.usdc),
1151
+ destinationToken: addressToBytes32(to.usdc),
1152
+ sourceDepositor: addressToBytes32(this.account.address),
1153
+ destinationRecipient: addressToBytes32(recipient),
1154
+ sourceSigner: addressToBytes32(this.account.address),
1155
+ destinationCaller: addressToBytes32(import_viem2.zeroAddress),
1156
+ value,
1157
+ salt: `0x${(0, import_crypto.randomBytes)(32).toString("hex")}`,
1158
+ hookData: "0x"
1159
+ }
1160
+ };
1161
+ }
1162
+ /**
1163
+ * Check if this client is connected to a testnet.
1164
+ */
1165
+ isTestnet() {
1166
+ const testnetChains = [
1167
+ "arbitrumSepolia",
1168
+ "arcTestnet",
1169
+ "avalancheFuji",
1170
+ "baseSepolia",
1171
+ "sepolia",
1172
+ "hyperEvmTestnet",
1173
+ "seiAtlantic",
1174
+ "sonicTestnet",
1175
+ "worldChainSepolia"
1176
+ ];
1177
+ return testnetChains.some(
1178
+ (name) => CHAIN_CONFIGS[name].domain === this.chainConfig.domain
1179
+ );
1180
+ }
1181
+ // ============================================================================
1182
+ // TRUSTLESS WITHDRAWAL (Emergency use only)
1183
+ // ============================================================================
1184
+ /**
1185
+ * Get the trustless withdrawal delay in blocks (~7 days).
1186
+ *
1187
+ * NOTE: This is for the emergency trustless withdrawal flow only.
1188
+ * For normal withdrawals, use `transfer()` to the same chain.
1189
+ *
1190
+ * @returns Number of blocks to wait after initiating trustless withdrawal
1191
+ */
1192
+ async getTrustlessWithdrawalDelay() {
1193
+ return this.publicClient.readContract({
1194
+ address: this.chainConfig.gatewayWallet,
1195
+ abi: GATEWAY_WALLET_ABI,
1196
+ functionName: "withdrawalDelay"
1197
+ });
1198
+ }
1199
+ /**
1200
+ * Get the block at which a pending trustless withdrawal becomes available.
1201
+ *
1202
+ * @param address - Optional address to check (defaults to account address)
1203
+ * @returns Block number, or 0n if no pending withdrawal
1204
+ */
1205
+ async getTrustlessWithdrawalBlock(address) {
1206
+ const target = address ?? this.account.address;
1207
+ return this.publicClient.readContract({
1208
+ address: this.chainConfig.gatewayWallet,
1209
+ abi: GATEWAY_WALLET_ABI,
1210
+ functionName: "withdrawalBlock",
1211
+ args: [this.chainConfig.usdc, target]
1212
+ });
1213
+ }
1214
+ /**
1215
+ * Initiate a trustless withdrawal from the Gateway Wallet.
1216
+ *
1217
+ * ⚠️ **WARNING: This is for emergency use only!**
1218
+ *
1219
+ * Use this only when Circle's Gateway API is unavailable. For normal
1220
+ * withdrawals, use `transfer()` to the same chain - it's instant.
1221
+ *
1222
+ * After calling this, you must wait ~7 days (`withdrawalDelay` blocks)
1223
+ * before calling `completeTrustlessWithdrawal()`.
1224
+ *
1225
+ * @param amount - Amount of USDC to withdraw (as a decimal string)
1226
+ * @returns Withdrawal initiation result
1227
+ */
1228
+ async initiateTrustlessWithdrawal(amount) {
1229
+ const withdrawAmount = (0, import_viem2.parseUnits)(amount, 6);
1230
+ const balance = await this.getBalance();
1231
+ if (balance.available < withdrawAmount) {
1232
+ throw new Error(
1233
+ `Insufficient available balance. Have: ${balance.formattedAvailable}, Need: ${amount}`
1234
+ );
1235
+ }
1236
+ const txHash = await this.walletClient.writeContract({
1237
+ address: this.chainConfig.gatewayWallet,
1238
+ abi: GATEWAY_WALLET_ABI,
1239
+ functionName: "initiateWithdrawal",
1240
+ args: [this.chainConfig.usdc, withdrawAmount],
1241
+ gas: 100000n
1242
+ });
1243
+ try {
1244
+ await this.publicClient.waitForTransactionReceipt({ hash: txHash });
1245
+ } catch (err) {
1246
+ throw new Error(`Trustless withdrawal initiation failed: ${txHash}`, {
1247
+ cause: err
1248
+ });
1249
+ }
1250
+ const withdrawalBlock = await this.getTrustlessWithdrawalBlock();
1251
+ return {
1252
+ txHash,
1253
+ amount: withdrawAmount,
1254
+ formattedAmount: amount,
1255
+ withdrawalBlock
1256
+ };
1257
+ }
1258
+ /**
1259
+ * Complete a trustless withdrawal after the ~7-day delay period.
1260
+ *
1261
+ * ⚠️ **WARNING: This is for emergency use only!**
1262
+ *
1263
+ * Use this only when Circle's Gateway API is unavailable. For normal
1264
+ * withdrawals, use `transfer()` to the same chain - it's instant.
1265
+ *
1266
+ * @returns Withdrawal result
1267
+ */
1268
+ async completeTrustlessWithdrawal() {
1269
+ const balance = await this.getBalance();
1270
+ if (balance.withdrawable === 0n) {
1271
+ const withdrawalBlock = await this.getTrustlessWithdrawalBlock();
1272
+ const currentBlock = await this.publicClient.getBlockNumber();
1273
+ if (withdrawalBlock > 0n && currentBlock < withdrawalBlock) {
1274
+ throw new Error(
1275
+ `Trustless withdrawal not yet available. Current block: ${currentBlock}, Available at: ${withdrawalBlock}`
1276
+ );
1277
+ }
1278
+ throw new Error(
1279
+ "No withdrawable balance. Call initiateTrustlessWithdrawal() first."
1280
+ );
1281
+ }
1282
+ const withdrawAmount = balance.withdrawable;
1283
+ const txHash = await this.walletClient.writeContract({
1284
+ address: this.chainConfig.gatewayWallet,
1285
+ abi: GATEWAY_WALLET_ABI,
1286
+ functionName: "withdraw",
1287
+ args: [this.chainConfig.usdc],
1288
+ gas: 100000n
1289
+ });
1290
+ try {
1291
+ await this.publicClient.waitForTransactionReceipt({ hash: txHash });
1292
+ } catch (err) {
1293
+ throw new Error(`Trustless withdrawal completion failed: ${txHash}`, {
1294
+ cause: err
1295
+ });
1296
+ }
1297
+ return {
1298
+ txHash,
1299
+ amount: withdrawAmount,
1300
+ formattedAmount: (0, import_viem2.formatUnits)(withdrawAmount, 6)
1301
+ };
1302
+ }
1303
+ };
1304
+ // Annotate the CommonJS export names for ESM import in node:
1305
+ 0 && (module.exports = {
1306
+ BatchEvmScheme,
1307
+ CHAIN_CONFIGS,
1308
+ CompositeEvmScheme,
1309
+ GATEWAY_DOMAINS,
1310
+ GatewayClient,
1311
+ registerBatchScheme
1312
+ });
1313
+ //# sourceMappingURL=index.js.map