@t402/wdk 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,2252 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ BalanceCache: () => BalanceCache,
24
+ BalanceError: () => BalanceError,
25
+ BridgeError: () => BridgeError,
26
+ CHAIN_TOKENS: () => CHAIN_TOKENS,
27
+ ChainError: () => ChainError,
28
+ DEFAULT_BALANCE_CACHE_CONFIG: () => DEFAULT_BALANCE_CACHE_CONFIG,
29
+ DEFAULT_CACHE_CONFIG: () => DEFAULT_CACHE_CONFIG,
30
+ DEFAULT_CHAINS: () => DEFAULT_CHAINS,
31
+ DEFAULT_RETRY_CONFIG: () => DEFAULT_RETRY_CONFIG,
32
+ DEFAULT_RPC_ENDPOINTS: () => DEFAULT_RPC_ENDPOINTS,
33
+ LAYERZERO_ENDPOINT_IDS: () => import_evm3.LAYERZERO_ENDPOINT_IDS,
34
+ MockWDKSigner: () => MockWDKSigner,
35
+ RPCError: () => RPCError,
36
+ SignerError: () => SignerError,
37
+ SigningError: () => SigningError,
38
+ T402WDK: () => T402WDK,
39
+ TTLCache: () => TTLCache,
40
+ TransactionError: () => TransactionError,
41
+ USDC_ADDRESSES: () => USDC_ADDRESSES,
42
+ USDT0_ADDRESSES: () => USDT0_ADDRESSES,
43
+ USDT0_OFT_ADDRESSES: () => import_evm3.USDT0_OFT_ADDRESSES,
44
+ USDT_LEGACY_ADDRESSES: () => USDT_LEGACY_ADDRESSES,
45
+ Usdt0Bridge: () => import_evm3.Usdt0Bridge,
46
+ WDKError: () => WDKError,
47
+ WDKErrorCode: () => WDKErrorCode,
48
+ WDKInitializationError: () => WDKInitializationError,
49
+ WDKSigner: () => WDKSigner,
50
+ WdkBridge: () => WdkBridge,
51
+ createDirectBridge: () => createDirectBridge,
52
+ createWDKSigner: () => createWDKSigner,
53
+ getBridgeableChains: () => import_evm3.getBridgeableChains,
54
+ getChainFromNetwork: () => getChainFromNetwork,
55
+ getChainId: () => getChainId,
56
+ getNetworkFromChain: () => getNetworkFromChain,
57
+ getPreferredToken: () => getPreferredToken,
58
+ getUsdt0Chains: () => getUsdt0Chains,
59
+ hasErrorCode: () => hasErrorCode,
60
+ isWDKError: () => isWDKError,
61
+ normalizeChainConfig: () => normalizeChainConfig,
62
+ supportsBridging: () => import_evm3.supportsBridging,
63
+ withRetry: () => withRetry,
64
+ withTimeout: () => withTimeout,
65
+ wrapError: () => wrapError
66
+ });
67
+ module.exports = __toCommonJS(index_exports);
68
+
69
+ // src/cache.ts
70
+ var DEFAULT_CACHE_CONFIG = {
71
+ defaultTTL: 3e4,
72
+ // 30 seconds
73
+ maxSize: 1e3,
74
+ refreshOnAccess: false
75
+ };
76
+ var TTLCache = class {
77
+ constructor(config = {}) {
78
+ this._cache = /* @__PURE__ */ new Map();
79
+ this._cleanupInterval = null;
80
+ this._config = { ...DEFAULT_CACHE_CONFIG, ...config };
81
+ if (this._config.maxSize > 0) {
82
+ this._startCleanup();
83
+ }
84
+ }
85
+ /**
86
+ * Get a value from the cache
87
+ *
88
+ * @param key - Cache key
89
+ * @returns The cached value or undefined if not found/expired
90
+ */
91
+ get(key) {
92
+ const entry = this._cache.get(key);
93
+ if (!entry) {
94
+ return void 0;
95
+ }
96
+ if (Date.now() > entry.expiresAt) {
97
+ this._cache.delete(key);
98
+ return void 0;
99
+ }
100
+ if (this._config.refreshOnAccess) {
101
+ entry.expiresAt = Date.now() + this._config.defaultTTL;
102
+ }
103
+ return entry.value;
104
+ }
105
+ /**
106
+ * Set a value in the cache
107
+ *
108
+ * @param key - Cache key
109
+ * @param value - Value to cache
110
+ * @param ttl - TTL in milliseconds (optional, uses default if not provided)
111
+ */
112
+ set(key, value, ttl) {
113
+ if (this._cache.size >= this._config.maxSize) {
114
+ this._evictOldest();
115
+ }
116
+ const expiresAt = Date.now() + (ttl ?? this._config.defaultTTL);
117
+ this._cache.set(key, { value, expiresAt });
118
+ }
119
+ /**
120
+ * Check if a key exists and is not expired
121
+ *
122
+ * @param key - Cache key
123
+ * @returns true if key exists and is not expired
124
+ */
125
+ has(key) {
126
+ const entry = this._cache.get(key);
127
+ if (!entry) {
128
+ return false;
129
+ }
130
+ if (Date.now() > entry.expiresAt) {
131
+ this._cache.delete(key);
132
+ return false;
133
+ }
134
+ return true;
135
+ }
136
+ /**
137
+ * Delete a key from the cache
138
+ *
139
+ * @param key - Cache key
140
+ * @returns true if key was deleted
141
+ */
142
+ delete(key) {
143
+ return this._cache.delete(key);
144
+ }
145
+ /**
146
+ * Delete all keys matching a prefix
147
+ *
148
+ * @param prefix - Key prefix to match
149
+ * @returns Number of keys deleted
150
+ */
151
+ deleteByPrefix(prefix) {
152
+ let count = 0;
153
+ for (const key of this._cache.keys()) {
154
+ if (key.startsWith(prefix)) {
155
+ this._cache.delete(key);
156
+ count++;
157
+ }
158
+ }
159
+ return count;
160
+ }
161
+ /**
162
+ * Clear all entries from the cache
163
+ */
164
+ clear() {
165
+ this._cache.clear();
166
+ }
167
+ /**
168
+ * Get the number of entries in the cache (including expired)
169
+ */
170
+ get size() {
171
+ return this._cache.size;
172
+ }
173
+ /**
174
+ * Get the number of valid (non-expired) entries
175
+ */
176
+ get validSize() {
177
+ const now = Date.now();
178
+ let count = 0;
179
+ for (const entry of this._cache.values()) {
180
+ if (now <= entry.expiresAt) {
181
+ count++;
182
+ }
183
+ }
184
+ return count;
185
+ }
186
+ /**
187
+ * Get all valid keys
188
+ */
189
+ keys() {
190
+ const now = Date.now();
191
+ const validKeys = [];
192
+ for (const [key, entry] of this._cache.entries()) {
193
+ if (now <= entry.expiresAt) {
194
+ validKeys.push(key);
195
+ }
196
+ }
197
+ return validKeys;
198
+ }
199
+ /**
200
+ * Get remaining TTL for a key in milliseconds
201
+ *
202
+ * @param key - Cache key
203
+ * @returns Remaining TTL in ms, or -1 if not found/expired
204
+ */
205
+ getTTL(key) {
206
+ const entry = this._cache.get(key);
207
+ if (!entry) {
208
+ return -1;
209
+ }
210
+ const remaining = entry.expiresAt - Date.now();
211
+ if (remaining <= 0) {
212
+ this._cache.delete(key);
213
+ return -1;
214
+ }
215
+ return remaining;
216
+ }
217
+ /**
218
+ * Update TTL for an existing key
219
+ *
220
+ * @param key - Cache key
221
+ * @param ttl - New TTL in milliseconds
222
+ * @returns true if key exists and TTL was updated
223
+ */
224
+ touch(key, ttl) {
225
+ const entry = this._cache.get(key);
226
+ if (!entry) {
227
+ return false;
228
+ }
229
+ if (Date.now() > entry.expiresAt) {
230
+ this._cache.delete(key);
231
+ return false;
232
+ }
233
+ entry.expiresAt = Date.now() + (ttl ?? this._config.defaultTTL);
234
+ return true;
235
+ }
236
+ /**
237
+ * Get or set a value using a factory function
238
+ *
239
+ * @param key - Cache key
240
+ * @param factory - Function to create value if not cached
241
+ * @param ttl - TTL in milliseconds (optional)
242
+ * @returns The cached or newly created value
243
+ */
244
+ async getOrSet(key, factory, ttl) {
245
+ const cached = this.get(key);
246
+ if (cached !== void 0) {
247
+ return cached;
248
+ }
249
+ const value = await factory();
250
+ this.set(key, value, ttl);
251
+ return value;
252
+ }
253
+ /**
254
+ * Get cache statistics
255
+ */
256
+ getStats() {
257
+ const now = Date.now();
258
+ let validCount = 0;
259
+ let expiredCount = 0;
260
+ let oldestExpiry = Infinity;
261
+ let newestExpiry = 0;
262
+ for (const entry of this._cache.values()) {
263
+ if (now <= entry.expiresAt) {
264
+ validCount++;
265
+ if (entry.expiresAt < oldestExpiry) oldestExpiry = entry.expiresAt;
266
+ if (entry.expiresAt > newestExpiry) newestExpiry = entry.expiresAt;
267
+ } else {
268
+ expiredCount++;
269
+ }
270
+ }
271
+ return {
272
+ totalSize: this._cache.size,
273
+ validSize: validCount,
274
+ expiredSize: expiredCount,
275
+ maxSize: this._config.maxSize,
276
+ defaultTTL: this._config.defaultTTL,
277
+ oldestExpiryMs: oldestExpiry === Infinity ? 0 : oldestExpiry - now,
278
+ newestExpiryMs: newestExpiry === 0 ? 0 : newestExpiry - now
279
+ };
280
+ }
281
+ /**
282
+ * Stop the cleanup interval
283
+ */
284
+ dispose() {
285
+ if (this._cleanupInterval) {
286
+ clearInterval(this._cleanupInterval);
287
+ this._cleanupInterval = null;
288
+ }
289
+ this._cache.clear();
290
+ }
291
+ /**
292
+ * Start periodic cleanup of expired entries
293
+ */
294
+ _startCleanup() {
295
+ this._cleanupInterval = setInterval(() => {
296
+ this._removeExpired();
297
+ }, 6e4);
298
+ if (this._cleanupInterval.unref) {
299
+ this._cleanupInterval.unref();
300
+ }
301
+ }
302
+ /**
303
+ * Remove all expired entries
304
+ */
305
+ _removeExpired() {
306
+ const now = Date.now();
307
+ let count = 0;
308
+ for (const [key, entry] of this._cache.entries()) {
309
+ if (now > entry.expiresAt) {
310
+ this._cache.delete(key);
311
+ count++;
312
+ }
313
+ }
314
+ return count;
315
+ }
316
+ /**
317
+ * Evict oldest entries to make room
318
+ */
319
+ _evictOldest() {
320
+ this._removeExpired();
321
+ if (this._cache.size >= this._config.maxSize) {
322
+ let oldestKey = null;
323
+ let oldestExpiry = Infinity;
324
+ for (const [key, entry] of this._cache.entries()) {
325
+ if (entry.expiresAt < oldestExpiry) {
326
+ oldestExpiry = entry.expiresAt;
327
+ oldestKey = key;
328
+ }
329
+ }
330
+ if (oldestKey) {
331
+ this._cache.delete(oldestKey);
332
+ }
333
+ }
334
+ }
335
+ };
336
+ var DEFAULT_BALANCE_CACHE_CONFIG = {
337
+ enabled: true,
338
+ nativeBalanceTTL: 15e3,
339
+ // 15 seconds
340
+ tokenBalanceTTL: 3e4,
341
+ // 30 seconds
342
+ aggregatedBalanceTTL: 6e4,
343
+ // 60 seconds
344
+ maxSize: 500
345
+ };
346
+ var BalanceCache = class {
347
+ constructor(config = {}) {
348
+ this._config = { ...DEFAULT_BALANCE_CACHE_CONFIG, ...config };
349
+ this._cache = new TTLCache({
350
+ defaultTTL: this._config.tokenBalanceTTL,
351
+ maxSize: this._config.maxSize
352
+ });
353
+ this._aggregatedCache = new TTLCache({
354
+ defaultTTL: this._config.aggregatedBalanceTTL,
355
+ maxSize: 100
356
+ // Fewer aggregated balance entries
357
+ });
358
+ }
359
+ /**
360
+ * Check if caching is enabled
361
+ */
362
+ get enabled() {
363
+ return this._config.enabled;
364
+ }
365
+ /**
366
+ * Get cache configuration
367
+ */
368
+ get config() {
369
+ return { ...this._config };
370
+ }
371
+ // ========== Native Balance Methods ==========
372
+ /**
373
+ * Get cached native balance
374
+ */
375
+ getNativeBalance(chain, address) {
376
+ if (!this._config.enabled) return void 0;
377
+ return this._cache.get(this._nativeKey(chain, address));
378
+ }
379
+ /**
380
+ * Set native balance in cache
381
+ */
382
+ setNativeBalance(chain, address, balance) {
383
+ if (!this._config.enabled) return;
384
+ this._cache.set(this._nativeKey(chain, address), balance, this._config.nativeBalanceTTL);
385
+ }
386
+ /**
387
+ * Get or fetch native balance
388
+ */
389
+ async getOrFetchNativeBalance(chain, address, fetcher) {
390
+ if (!this._config.enabled) {
391
+ return fetcher();
392
+ }
393
+ return this._cache.getOrSet(
394
+ this._nativeKey(chain, address),
395
+ fetcher,
396
+ this._config.nativeBalanceTTL
397
+ );
398
+ }
399
+ // ========== Token Balance Methods ==========
400
+ /**
401
+ * Get cached token balance
402
+ */
403
+ getTokenBalance(chain, token, address) {
404
+ if (!this._config.enabled) return void 0;
405
+ return this._cache.get(this._tokenKey(chain, token, address));
406
+ }
407
+ /**
408
+ * Set token balance in cache
409
+ */
410
+ setTokenBalance(chain, token, address, balance) {
411
+ if (!this._config.enabled) return;
412
+ this._cache.set(this._tokenKey(chain, token, address), balance, this._config.tokenBalanceTTL);
413
+ }
414
+ /**
415
+ * Get or fetch token balance
416
+ */
417
+ async getOrFetchTokenBalance(chain, token, address, fetcher) {
418
+ if (!this._config.enabled) {
419
+ return fetcher();
420
+ }
421
+ return this._cache.getOrSet(
422
+ this._tokenKey(chain, token, address),
423
+ fetcher,
424
+ this._config.tokenBalanceTTL
425
+ );
426
+ }
427
+ // ========== Aggregated Balance Methods ==========
428
+ /**
429
+ * Get cached aggregated balance
430
+ */
431
+ getAggregatedBalance(key) {
432
+ if (!this._config.enabled) return void 0;
433
+ return this._aggregatedCache.get(this._aggregatedKey(key));
434
+ }
435
+ /**
436
+ * Set aggregated balance in cache
437
+ */
438
+ setAggregatedBalance(key, value) {
439
+ if (!this._config.enabled) return;
440
+ this._aggregatedCache.set(this._aggregatedKey(key), value, this._config.aggregatedBalanceTTL);
441
+ }
442
+ /**
443
+ * Get or fetch aggregated balance
444
+ */
445
+ async getOrFetchAggregatedBalance(key, fetcher) {
446
+ if (!this._config.enabled) {
447
+ return fetcher();
448
+ }
449
+ return this._aggregatedCache.getOrSet(
450
+ this._aggregatedKey(key),
451
+ fetcher,
452
+ this._config.aggregatedBalanceTTL
453
+ );
454
+ }
455
+ // ========== Cache Management ==========
456
+ /**
457
+ * Invalidate all balances for a chain
458
+ */
459
+ invalidateChain(chain) {
460
+ const count1 = this._cache.deleteByPrefix(`native:${chain}:`);
461
+ const count2 = this._cache.deleteByPrefix(`token:${chain}:`);
462
+ const count3 = this._aggregatedCache.deleteByPrefix(`agg:`);
463
+ return count1 + count2 + count3;
464
+ }
465
+ /**
466
+ * Invalidate all balances for an address
467
+ */
468
+ invalidateAddress(address) {
469
+ let count = 0;
470
+ const lowerAddress = address.toLowerCase();
471
+ for (const key of this._cache.keys()) {
472
+ if (key.toLowerCase().includes(lowerAddress)) {
473
+ this._cache.delete(key);
474
+ count++;
475
+ }
476
+ }
477
+ this._aggregatedCache.clear();
478
+ return count;
479
+ }
480
+ /**
481
+ * Invalidate specific token balance
482
+ */
483
+ invalidateTokenBalance(chain, token, address) {
484
+ return this._cache.delete(this._tokenKey(chain, token, address));
485
+ }
486
+ /**
487
+ * Clear all caches
488
+ */
489
+ clear() {
490
+ this._cache.clear();
491
+ this._aggregatedCache.clear();
492
+ }
493
+ /**
494
+ * Get cache statistics
495
+ */
496
+ getStats() {
497
+ return {
498
+ balanceCache: this._cache.getStats(),
499
+ aggregatedCache: this._aggregatedCache.getStats(),
500
+ config: this.config
501
+ };
502
+ }
503
+ /**
504
+ * Dispose of cache resources
505
+ */
506
+ dispose() {
507
+ this._cache.dispose();
508
+ this._aggregatedCache.dispose();
509
+ }
510
+ // ========== Private Key Generators ==========
511
+ _nativeKey(chain, address) {
512
+ return `native:${chain}:${address.toLowerCase()}`;
513
+ }
514
+ _tokenKey(chain, token, address) {
515
+ return `token:${chain}:${token.toLowerCase()}:${address.toLowerCase()}`;
516
+ }
517
+ _aggregatedKey(key) {
518
+ return `agg:${key}`;
519
+ }
520
+ };
521
+
522
+ // src/chains.ts
523
+ var DEFAULT_CHAINS = {
524
+ ethereum: {
525
+ chainId: 1,
526
+ network: "eip155:1",
527
+ name: "ethereum"
528
+ },
529
+ arbitrum: {
530
+ chainId: 42161,
531
+ network: "eip155:42161",
532
+ name: "arbitrum"
533
+ },
534
+ base: {
535
+ chainId: 8453,
536
+ network: "eip155:8453",
537
+ name: "base"
538
+ },
539
+ ink: {
540
+ chainId: 57073,
541
+ network: "eip155:57073",
542
+ name: "ink"
543
+ },
544
+ berachain: {
545
+ chainId: 80094,
546
+ network: "eip155:80094",
547
+ name: "berachain"
548
+ },
549
+ unichain: {
550
+ chainId: 130,
551
+ network: "eip155:130",
552
+ name: "unichain"
553
+ },
554
+ polygon: {
555
+ chainId: 137,
556
+ network: "eip155:137",
557
+ name: "polygon"
558
+ }
559
+ };
560
+ var DEFAULT_RPC_ENDPOINTS = {
561
+ ethereum: "https://eth.drpc.org",
562
+ arbitrum: "https://arb1.arbitrum.io/rpc",
563
+ base: "https://mainnet.base.org",
564
+ ink: "https://rpc-gel.inkonchain.com",
565
+ polygon: "https://polygon-rpc.com"
566
+ };
567
+ var USDT0_ADDRESSES = {
568
+ ethereum: "0x6C96dE32CEa08842dcc4058c14d3aaAD7Fa41dee",
569
+ arbitrum: "0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9",
570
+ ink: "0x0200C29006150606B650577BBE7B6248F58470c1",
571
+ berachain: "0x779Ded0c9e1022225f8E0630b35a9b54bE713736",
572
+ unichain: "0x588ce4F028D8e7B53B687865d6A67b3A54C75518"
573
+ };
574
+ var USDC_ADDRESSES = {
575
+ ethereum: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
576
+ arbitrum: "0xaf88d065e77c8cC2239327C5EDb3A432268e5831",
577
+ base: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
578
+ polygon: "0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359"
579
+ };
580
+ var USDT_LEGACY_ADDRESSES = {
581
+ ethereum: "0xdAC17F958D2ee523a2206206994597C13D831ec7",
582
+ polygon: "0xc2132D05D31c914a87C6611C10748AEb04B58e8F"
583
+ };
584
+ var CHAIN_TOKENS = {
585
+ ethereum: [
586
+ {
587
+ address: USDT0_ADDRESSES.ethereum,
588
+ symbol: "USDT0",
589
+ name: "TetherToken",
590
+ decimals: 6,
591
+ supportsEIP3009: true
592
+ },
593
+ {
594
+ address: USDC_ADDRESSES.ethereum,
595
+ symbol: "USDC",
596
+ name: "USD Coin",
597
+ decimals: 6,
598
+ supportsEIP3009: true
599
+ },
600
+ {
601
+ address: USDT_LEGACY_ADDRESSES.ethereum,
602
+ symbol: "USDT",
603
+ name: "Tether USD",
604
+ decimals: 6,
605
+ supportsEIP3009: false
606
+ }
607
+ ],
608
+ arbitrum: [
609
+ {
610
+ address: USDT0_ADDRESSES.arbitrum,
611
+ symbol: "USDT0",
612
+ name: "TetherToken",
613
+ decimals: 6,
614
+ supportsEIP3009: true
615
+ },
616
+ {
617
+ address: USDC_ADDRESSES.arbitrum,
618
+ symbol: "USDC",
619
+ name: "USD Coin",
620
+ decimals: 6,
621
+ supportsEIP3009: true
622
+ }
623
+ ],
624
+ base: [
625
+ {
626
+ address: USDC_ADDRESSES.base,
627
+ symbol: "USDC",
628
+ name: "USD Coin",
629
+ decimals: 6,
630
+ supportsEIP3009: true
631
+ }
632
+ ],
633
+ ink: [
634
+ {
635
+ address: USDT0_ADDRESSES.ink,
636
+ symbol: "USDT0",
637
+ name: "TetherToken",
638
+ decimals: 6,
639
+ supportsEIP3009: true
640
+ }
641
+ ],
642
+ berachain: [
643
+ {
644
+ address: USDT0_ADDRESSES.berachain,
645
+ symbol: "USDT0",
646
+ name: "TetherToken",
647
+ decimals: 6,
648
+ supportsEIP3009: true
649
+ }
650
+ ],
651
+ unichain: [
652
+ {
653
+ address: USDT0_ADDRESSES.unichain,
654
+ symbol: "USDT0",
655
+ name: "TetherToken",
656
+ decimals: 6,
657
+ supportsEIP3009: true
658
+ }
659
+ ],
660
+ polygon: [
661
+ {
662
+ address: USDC_ADDRESSES.polygon,
663
+ symbol: "USDC",
664
+ name: "USD Coin",
665
+ decimals: 6,
666
+ supportsEIP3009: true
667
+ },
668
+ {
669
+ address: USDT_LEGACY_ADDRESSES.polygon,
670
+ symbol: "USDT",
671
+ name: "Tether USD",
672
+ decimals: 6,
673
+ supportsEIP3009: false
674
+ }
675
+ ]
676
+ };
677
+ function normalizeChainConfig(chainName, config) {
678
+ const defaultConfig = DEFAULT_CHAINS[chainName];
679
+ if (typeof config === "string") {
680
+ return {
681
+ provider: config,
682
+ chainId: defaultConfig?.chainId ?? 1,
683
+ network: defaultConfig?.network ?? `eip155:1`,
684
+ name: chainName
685
+ };
686
+ }
687
+ return {
688
+ provider: config.provider,
689
+ chainId: config.chainId ?? defaultConfig?.chainId ?? 1,
690
+ network: config.network ?? defaultConfig?.network ?? `eip155:${config.chainId}`,
691
+ name: chainName
692
+ };
693
+ }
694
+ function getNetworkFromChain(chain) {
695
+ return DEFAULT_CHAINS[chain]?.network ?? `eip155:1`;
696
+ }
697
+ function getChainFromNetwork(network) {
698
+ for (const [chain, config] of Object.entries(DEFAULT_CHAINS)) {
699
+ if (config.network === network) {
700
+ return chain;
701
+ }
702
+ }
703
+ return void 0;
704
+ }
705
+ function getChainId(chain) {
706
+ return DEFAULT_CHAINS[chain]?.chainId ?? 1;
707
+ }
708
+ function getUsdt0Chains() {
709
+ return Object.keys(USDT0_ADDRESSES);
710
+ }
711
+ function getPreferredToken(chain) {
712
+ const tokens = CHAIN_TOKENS[chain];
713
+ if (!tokens || tokens.length === 0) return void 0;
714
+ return tokens.find((t) => t.symbol === "USDT0") ?? tokens.find((t) => t.symbol === "USDC") ?? tokens[0];
715
+ }
716
+
717
+ // src/errors.ts
718
+ var WDKErrorCode = /* @__PURE__ */ ((WDKErrorCode2) => {
719
+ WDKErrorCode2[WDKErrorCode2["WDK_NOT_REGISTERED"] = 1001] = "WDK_NOT_REGISTERED";
720
+ WDKErrorCode2[WDKErrorCode2["WDK_NOT_INITIALIZED"] = 1002] = "WDK_NOT_INITIALIZED";
721
+ WDKErrorCode2[WDKErrorCode2["INVALID_SEED_PHRASE"] = 1003] = "INVALID_SEED_PHRASE";
722
+ WDKErrorCode2[WDKErrorCode2["WALLET_MANAGER_NOT_REGISTERED"] = 1004] = "WALLET_MANAGER_NOT_REGISTERED";
723
+ WDKErrorCode2[WDKErrorCode2["CHAIN_NOT_CONFIGURED"] = 2001] = "CHAIN_NOT_CONFIGURED";
724
+ WDKErrorCode2[WDKErrorCode2["CHAIN_NOT_SUPPORTED"] = 2002] = "CHAIN_NOT_SUPPORTED";
725
+ WDKErrorCode2[WDKErrorCode2["INVALID_CHAIN_CONFIG"] = 2003] = "INVALID_CHAIN_CONFIG";
726
+ WDKErrorCode2[WDKErrorCode2["UNKNOWN_CHAIN_ID"] = 2004] = "UNKNOWN_CHAIN_ID";
727
+ WDKErrorCode2[WDKErrorCode2["SIGNER_NOT_INITIALIZED"] = 3001] = "SIGNER_NOT_INITIALIZED";
728
+ WDKErrorCode2[WDKErrorCode2["ACCOUNT_FETCH_FAILED"] = 3002] = "ACCOUNT_FETCH_FAILED";
729
+ WDKErrorCode2[WDKErrorCode2["ADDRESS_FETCH_FAILED"] = 3003] = "ADDRESS_FETCH_FAILED";
730
+ WDKErrorCode2[WDKErrorCode2["SIGN_TYPED_DATA_FAILED"] = 4001] = "SIGN_TYPED_DATA_FAILED";
731
+ WDKErrorCode2[WDKErrorCode2["SIGN_MESSAGE_FAILED"] = 4002] = "SIGN_MESSAGE_FAILED";
732
+ WDKErrorCode2[WDKErrorCode2["INVALID_TYPED_DATA"] = 4003] = "INVALID_TYPED_DATA";
733
+ WDKErrorCode2[WDKErrorCode2["INVALID_MESSAGE"] = 4004] = "INVALID_MESSAGE";
734
+ WDKErrorCode2[WDKErrorCode2["USER_REJECTED_SIGNATURE"] = 4005] = "USER_REJECTED_SIGNATURE";
735
+ WDKErrorCode2[WDKErrorCode2["BALANCE_FETCH_FAILED"] = 5001] = "BALANCE_FETCH_FAILED";
736
+ WDKErrorCode2[WDKErrorCode2["TOKEN_BALANCE_FETCH_FAILED"] = 5002] = "TOKEN_BALANCE_FETCH_FAILED";
737
+ WDKErrorCode2[WDKErrorCode2["INVALID_TOKEN_ADDRESS"] = 5003] = "INVALID_TOKEN_ADDRESS";
738
+ WDKErrorCode2[WDKErrorCode2["TRANSACTION_FAILED"] = 6001] = "TRANSACTION_FAILED";
739
+ WDKErrorCode2[WDKErrorCode2["GAS_ESTIMATION_FAILED"] = 6002] = "GAS_ESTIMATION_FAILED";
740
+ WDKErrorCode2[WDKErrorCode2["INSUFFICIENT_BALANCE"] = 6003] = "INSUFFICIENT_BALANCE";
741
+ WDKErrorCode2[WDKErrorCode2["TRANSACTION_REVERTED"] = 6004] = "TRANSACTION_REVERTED";
742
+ WDKErrorCode2[WDKErrorCode2["TRANSACTION_TIMEOUT"] = 6005] = "TRANSACTION_TIMEOUT";
743
+ WDKErrorCode2[WDKErrorCode2["BRIDGE_NOT_AVAILABLE"] = 7001] = "BRIDGE_NOT_AVAILABLE";
744
+ WDKErrorCode2[WDKErrorCode2["BRIDGE_NOT_SUPPORTED"] = 7002] = "BRIDGE_NOT_SUPPORTED";
745
+ WDKErrorCode2[WDKErrorCode2["BRIDGE_FAILED"] = 7003] = "BRIDGE_FAILED";
746
+ WDKErrorCode2[WDKErrorCode2["INSUFFICIENT_BRIDGE_FEE"] = 7004] = "INSUFFICIENT_BRIDGE_FEE";
747
+ WDKErrorCode2[WDKErrorCode2["RPC_ERROR"] = 8001] = "RPC_ERROR";
748
+ WDKErrorCode2[WDKErrorCode2["RPC_TIMEOUT"] = 8002] = "RPC_TIMEOUT";
749
+ WDKErrorCode2[WDKErrorCode2["RPC_RATE_LIMITED"] = 8003] = "RPC_RATE_LIMITED";
750
+ WDKErrorCode2[WDKErrorCode2["RPC_CONNECTION_FAILED"] = 8004] = "RPC_CONNECTION_FAILED";
751
+ WDKErrorCode2[WDKErrorCode2["UNKNOWN_ERROR"] = 9999] = "UNKNOWN_ERROR";
752
+ return WDKErrorCode2;
753
+ })(WDKErrorCode || {});
754
+ var WDKError = class _WDKError extends Error {
755
+ constructor(code, message, options) {
756
+ super(message);
757
+ this.name = "WDKError";
758
+ this.code = code;
759
+ this.cause = options?.cause;
760
+ this.context = options?.context;
761
+ if (Error.captureStackTrace) {
762
+ Error.captureStackTrace(this, _WDKError);
763
+ }
764
+ }
765
+ /**
766
+ * Create a JSON-serializable representation
767
+ */
768
+ toJSON() {
769
+ return {
770
+ name: this.name,
771
+ code: this.code,
772
+ message: this.message,
773
+ context: this.context,
774
+ cause: this.cause?.message,
775
+ stack: this.stack
776
+ };
777
+ }
778
+ /**
779
+ * Check if error is retryable
780
+ */
781
+ isRetryable() {
782
+ return [
783
+ 8002 /* RPC_TIMEOUT */,
784
+ 8003 /* RPC_RATE_LIMITED */,
785
+ 8004 /* RPC_CONNECTION_FAILED */,
786
+ 5001 /* BALANCE_FETCH_FAILED */,
787
+ 5002 /* TOKEN_BALANCE_FETCH_FAILED */,
788
+ 6002 /* GAS_ESTIMATION_FAILED */
789
+ ].includes(this.code);
790
+ }
791
+ };
792
+ var WDKInitializationError = class extends WDKError {
793
+ constructor(message, options) {
794
+ super(1002 /* WDK_NOT_INITIALIZED */, message, options);
795
+ this.name = "WDKInitializationError";
796
+ }
797
+ };
798
+ var ChainError = class extends WDKError {
799
+ constructor(code, message, options) {
800
+ super(code, message, {
801
+ cause: options?.cause,
802
+ context: { ...options?.context, chain: options?.chain }
803
+ });
804
+ this.name = "ChainError";
805
+ this.chain = options?.chain;
806
+ }
807
+ };
808
+ var SignerError = class extends WDKError {
809
+ constructor(code, message, options) {
810
+ super(code, message, {
811
+ cause: options?.cause,
812
+ context: { ...options?.context, chain: options?.chain, address: options?.address }
813
+ });
814
+ this.name = "SignerError";
815
+ this.chain = options?.chain;
816
+ this.address = options?.address;
817
+ }
818
+ };
819
+ var SigningError = class extends WDKError {
820
+ constructor(code, message, options) {
821
+ super(code, message, {
822
+ cause: options.cause,
823
+ context: { ...options.context, operation: options.operation }
824
+ });
825
+ this.name = "SigningError";
826
+ this.operation = options.operation;
827
+ }
828
+ };
829
+ var BalanceError = class extends WDKError {
830
+ constructor(code, message, options) {
831
+ super(code, message, {
832
+ cause: options?.cause,
833
+ context: { ...options?.context, chain: options?.chain, token: options?.token }
834
+ });
835
+ this.name = "BalanceError";
836
+ this.chain = options?.chain;
837
+ this.token = options?.token;
838
+ }
839
+ };
840
+ var TransactionError = class extends WDKError {
841
+ constructor(code, message, options) {
842
+ super(code, message, {
843
+ cause: options?.cause,
844
+ context: { ...options?.context, chain: options?.chain, txHash: options?.txHash }
845
+ });
846
+ this.name = "TransactionError";
847
+ this.chain = options?.chain;
848
+ this.txHash = options?.txHash;
849
+ }
850
+ };
851
+ var BridgeError = class extends WDKError {
852
+ constructor(code, message, options) {
853
+ super(code, message, {
854
+ cause: options?.cause,
855
+ context: {
856
+ ...options?.context,
857
+ fromChain: options?.fromChain,
858
+ toChain: options?.toChain
859
+ }
860
+ });
861
+ this.name = "BridgeError";
862
+ this.fromChain = options?.fromChain;
863
+ this.toChain = options?.toChain;
864
+ }
865
+ };
866
+ var RPCError = class extends WDKError {
867
+ constructor(code, message, options) {
868
+ super(code, message, {
869
+ cause: options?.cause,
870
+ context: {
871
+ ...options?.context,
872
+ endpoint: options?.endpoint,
873
+ rpcCode: options?.rpcCode
874
+ }
875
+ });
876
+ this.name = "RPCError";
877
+ this.endpoint = options?.endpoint;
878
+ this.rpcCode = options?.rpcCode;
879
+ }
880
+ };
881
+ function wrapError(error, defaultCode = 9999 /* UNKNOWN_ERROR */, defaultMessage = "An unknown error occurred", context) {
882
+ if (error instanceof WDKError) {
883
+ return error;
884
+ }
885
+ if (error instanceof Error) {
886
+ const msg = error.message.toLowerCase();
887
+ if (msg.includes("timeout") || msg.includes("timed out")) {
888
+ return new RPCError(8002 /* RPC_TIMEOUT */, `Request timeout: ${error.message}`, {
889
+ cause: error,
890
+ context
891
+ });
892
+ }
893
+ if (msg.includes("rate limit") || msg.includes("too many requests") || msg.includes("429")) {
894
+ return new RPCError(
895
+ 8003 /* RPC_RATE_LIMITED */,
896
+ `Rate limited: ${error.message}`,
897
+ { cause: error, context }
898
+ );
899
+ }
900
+ if (msg.includes("connection") || msg.includes("network") || msg.includes("econnrefused") || msg.includes("enotfound")) {
901
+ return new RPCError(
902
+ 8004 /* RPC_CONNECTION_FAILED */,
903
+ `Connection failed: ${error.message}`,
904
+ { cause: error, context }
905
+ );
906
+ }
907
+ if (msg.includes("insufficient funds") || msg.includes("insufficient balance")) {
908
+ return new TransactionError(
909
+ 6003 /* INSUFFICIENT_BALANCE */,
910
+ `Insufficient balance: ${error.message}`,
911
+ { cause: error, context }
912
+ );
913
+ }
914
+ if (msg.includes("user rejected") || msg.includes("user denied")) {
915
+ return new SigningError(
916
+ 4005 /* USER_REJECTED_SIGNATURE */,
917
+ "User rejected the signature request",
918
+ { operation: "signTypedData", cause: error, context }
919
+ );
920
+ }
921
+ if (msg.includes("reverted") || msg.includes("revert")) {
922
+ return new TransactionError(
923
+ 6004 /* TRANSACTION_REVERTED */,
924
+ `Transaction reverted: ${error.message}`,
925
+ { cause: error, context }
926
+ );
927
+ }
928
+ return new WDKError(defaultCode, error.message || defaultMessage, {
929
+ cause: error,
930
+ context
931
+ });
932
+ }
933
+ return new WDKError(defaultCode, String(error) || defaultMessage, { context });
934
+ }
935
+ function isWDKError(error) {
936
+ return error instanceof WDKError;
937
+ }
938
+ function hasErrorCode(error, code) {
939
+ return isWDKError(error) && error.code === code;
940
+ }
941
+ var DEFAULT_RETRY_CONFIG = {
942
+ maxRetries: 3,
943
+ baseDelay: 1e3,
944
+ maxDelay: 1e4,
945
+ exponentialBackoff: true
946
+ };
947
+ async function withRetry(fn, config = {}) {
948
+ const { maxRetries, baseDelay, maxDelay, exponentialBackoff } = {
949
+ ...DEFAULT_RETRY_CONFIG,
950
+ ...config
951
+ };
952
+ let lastError;
953
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
954
+ try {
955
+ return await fn();
956
+ } catch (error) {
957
+ lastError = error instanceof Error ? error : new Error(String(error));
958
+ const wdkError = wrapError(error);
959
+ if (!wdkError.isRetryable() || attempt >= maxRetries) {
960
+ throw wdkError;
961
+ }
962
+ const delay = exponentialBackoff ? Math.min(baseDelay * Math.pow(2, attempt), maxDelay) : baseDelay;
963
+ const jitter = Math.random() * delay * 0.1;
964
+ await sleep(delay + jitter);
965
+ }
966
+ }
967
+ throw lastError ?? new Error("Retry failed with unknown error");
968
+ }
969
+ function sleep(ms) {
970
+ return new Promise((resolve) => setTimeout(resolve, ms));
971
+ }
972
+ async function withTimeout(promise, timeoutMs, operation = "Operation") {
973
+ let timeoutId;
974
+ const timeoutPromise = new Promise((_, reject) => {
975
+ timeoutId = setTimeout(() => {
976
+ reject(
977
+ new RPCError(
978
+ 8002 /* RPC_TIMEOUT */,
979
+ `${operation} timed out after ${timeoutMs}ms`
980
+ )
981
+ );
982
+ }, timeoutMs);
983
+ });
984
+ try {
985
+ return await Promise.race([promise, timeoutPromise]);
986
+ } finally {
987
+ clearTimeout(timeoutId);
988
+ }
989
+ }
990
+
991
+ // src/signer.ts
992
+ var DEFAULT_TIMEOUT_MS = 3e4;
993
+ var DEFAULT_BALANCE_RETRY = {
994
+ maxRetries: 2,
995
+ baseDelay: 500
996
+ };
997
+ var WDKSigner = class {
998
+ /**
999
+ * Create a new WDK signer
1000
+ *
1001
+ * @param wdk - The WDK instance
1002
+ * @param chain - Chain name (e.g., "arbitrum", "ethereum")
1003
+ * @param accountIndex - HD wallet account index (default: 0)
1004
+ * @param timeoutMs - Timeout for operations in milliseconds (default: 30000)
1005
+ */
1006
+ constructor(wdk, chain, accountIndex = 0, timeoutMs = DEFAULT_TIMEOUT_MS) {
1007
+ this._account = null;
1008
+ this._address = null;
1009
+ if (!wdk) {
1010
+ throw new SignerError(
1011
+ 1002 /* WDK_NOT_INITIALIZED */,
1012
+ "WDK instance is required",
1013
+ { chain }
1014
+ );
1015
+ }
1016
+ if (!chain || typeof chain !== "string") {
1017
+ throw new SignerError(
1018
+ 2001 /* CHAIN_NOT_CONFIGURED */,
1019
+ "Chain name is required and must be a string",
1020
+ { chain }
1021
+ );
1022
+ }
1023
+ if (accountIndex < 0 || !Number.isInteger(accountIndex)) {
1024
+ throw new SignerError(
1025
+ 3001 /* SIGNER_NOT_INITIALIZED */,
1026
+ "Account index must be a non-negative integer",
1027
+ { chain, context: { accountIndex } }
1028
+ );
1029
+ }
1030
+ this._wdk = wdk;
1031
+ this._chain = chain;
1032
+ this._accountIndex = accountIndex;
1033
+ this._timeoutMs = timeoutMs;
1034
+ }
1035
+ /**
1036
+ * Get the wallet address
1037
+ * Throws if signer is not initialized
1038
+ */
1039
+ get address() {
1040
+ if (!this._address) {
1041
+ throw new SignerError(
1042
+ 3001 /* SIGNER_NOT_INITIALIZED */,
1043
+ `Signer not initialized for chain "${this._chain}". Call initialize() first or use createWDKSigner() async factory.`,
1044
+ { chain: this._chain }
1045
+ );
1046
+ }
1047
+ return this._address;
1048
+ }
1049
+ /**
1050
+ * Check if the signer is initialized
1051
+ */
1052
+ get isInitialized() {
1053
+ return this._account !== null && this._address !== null;
1054
+ }
1055
+ /**
1056
+ * Initialize the signer by fetching the account
1057
+ * Must be called before using the signer
1058
+ *
1059
+ * @throws {SignerError} If account fetch fails
1060
+ */
1061
+ async initialize() {
1062
+ if (this._account) return;
1063
+ try {
1064
+ const accountPromise = this._wdk.getAccount(this._chain, this._accountIndex);
1065
+ this._account = await withTimeout(
1066
+ accountPromise,
1067
+ this._timeoutMs,
1068
+ `Fetching account for ${this._chain}`
1069
+ );
1070
+ const addressPromise = this._account.getAddress();
1071
+ const addressString = await withTimeout(
1072
+ addressPromise,
1073
+ this._timeoutMs,
1074
+ `Fetching address for ${this._chain}`
1075
+ );
1076
+ if (!addressString || !addressString.startsWith("0x")) {
1077
+ throw new SignerError(
1078
+ 3003 /* ADDRESS_FETCH_FAILED */,
1079
+ `Invalid address format received: ${addressString}`,
1080
+ { chain: this._chain }
1081
+ );
1082
+ }
1083
+ this._address = addressString;
1084
+ } catch (error) {
1085
+ this._account = null;
1086
+ this._address = null;
1087
+ if (error instanceof SignerError) {
1088
+ throw error;
1089
+ }
1090
+ throw new SignerError(
1091
+ 3002 /* ACCOUNT_FETCH_FAILED */,
1092
+ `Failed to initialize signer for chain "${this._chain}": ${error instanceof Error ? error.message : String(error)}`,
1093
+ {
1094
+ chain: this._chain,
1095
+ cause: error instanceof Error ? error : void 0,
1096
+ context: { accountIndex: this._accountIndex }
1097
+ }
1098
+ );
1099
+ }
1100
+ }
1101
+ /**
1102
+ * Get the underlying WDK account
1103
+ * Initializes if not already done
1104
+ *
1105
+ * @throws {SignerError} If initialization fails
1106
+ */
1107
+ async getAccount() {
1108
+ if (!this._account) {
1109
+ await this.initialize();
1110
+ }
1111
+ return this._account;
1112
+ }
1113
+ /**
1114
+ * Sign EIP-712 typed data for T402 payments
1115
+ *
1116
+ * This is the primary signing method used by T402 for EIP-3009
1117
+ * transferWithAuthorization payments.
1118
+ *
1119
+ * @throws {SigningError} If signing fails
1120
+ */
1121
+ async signTypedData(message) {
1122
+ if (!message || typeof message !== "object") {
1123
+ throw new SigningError(
1124
+ 4003 /* INVALID_TYPED_DATA */,
1125
+ "Invalid typed data: message object is required",
1126
+ { operation: "signTypedData", context: { chain: this._chain } }
1127
+ );
1128
+ }
1129
+ if (!message.domain || !message.types || !message.primaryType || !message.message) {
1130
+ throw new SigningError(
1131
+ 4003 /* INVALID_TYPED_DATA */,
1132
+ "Invalid typed data: domain, types, primaryType, and message are required",
1133
+ {
1134
+ operation: "signTypedData",
1135
+ context: {
1136
+ chain: this._chain,
1137
+ hasFields: {
1138
+ domain: !!message.domain,
1139
+ types: !!message.types,
1140
+ primaryType: !!message.primaryType,
1141
+ message: !!message.message
1142
+ }
1143
+ }
1144
+ }
1145
+ );
1146
+ }
1147
+ try {
1148
+ const account = await this.getAccount();
1149
+ const signPromise = account.signTypedData({
1150
+ domain: message.domain,
1151
+ types: message.types,
1152
+ primaryType: message.primaryType,
1153
+ message: message.message
1154
+ });
1155
+ const signature = await withTimeout(
1156
+ signPromise,
1157
+ this._timeoutMs,
1158
+ "Signing typed data"
1159
+ );
1160
+ if (!signature || !signature.startsWith("0x")) {
1161
+ throw new SigningError(
1162
+ 4001 /* SIGN_TYPED_DATA_FAILED */,
1163
+ `Invalid signature format received: ${signature?.substring(0, 10)}...`,
1164
+ { operation: "signTypedData", context: { chain: this._chain } }
1165
+ );
1166
+ }
1167
+ return signature;
1168
+ } catch (error) {
1169
+ if (error instanceof SigningError) {
1170
+ throw error;
1171
+ }
1172
+ const wrapped = wrapError(error, 4001 /* SIGN_TYPED_DATA_FAILED */, "Failed to sign typed data", {
1173
+ chain: this._chain,
1174
+ primaryType: message.primaryType
1175
+ });
1176
+ throw new SigningError(
1177
+ wrapped.code,
1178
+ wrapped.message,
1179
+ {
1180
+ operation: "signTypedData",
1181
+ cause: wrapped.cause,
1182
+ context: wrapped.context
1183
+ }
1184
+ );
1185
+ }
1186
+ }
1187
+ /**
1188
+ * Sign a personal message
1189
+ *
1190
+ * @throws {SigningError} If signing fails
1191
+ */
1192
+ async signMessage(message) {
1193
+ if (message === void 0 || message === null) {
1194
+ throw new SigningError(
1195
+ 4004 /* INVALID_MESSAGE */,
1196
+ "Message is required for signing",
1197
+ { operation: "signMessage", context: { chain: this._chain } }
1198
+ );
1199
+ }
1200
+ if (typeof message !== "string" && !(message instanceof Uint8Array)) {
1201
+ throw new SigningError(
1202
+ 4004 /* INVALID_MESSAGE */,
1203
+ "Message must be a string or Uint8Array",
1204
+ { operation: "signMessage", context: { chain: this._chain, type: typeof message } }
1205
+ );
1206
+ }
1207
+ try {
1208
+ const account = await this.getAccount();
1209
+ const messageStr = typeof message === "string" ? message : Buffer.from(message).toString("utf-8");
1210
+ const signPromise = account.signMessage(messageStr);
1211
+ const signature = await withTimeout(
1212
+ signPromise,
1213
+ this._timeoutMs,
1214
+ "Signing message"
1215
+ );
1216
+ if (!signature || !signature.startsWith("0x")) {
1217
+ throw new SigningError(
1218
+ 4002 /* SIGN_MESSAGE_FAILED */,
1219
+ `Invalid signature format received: ${signature?.substring(0, 10)}...`,
1220
+ { operation: "signMessage", context: { chain: this._chain } }
1221
+ );
1222
+ }
1223
+ return signature;
1224
+ } catch (error) {
1225
+ if (error instanceof SigningError) {
1226
+ throw error;
1227
+ }
1228
+ const wrapped = wrapError(error, 4002 /* SIGN_MESSAGE_FAILED */, "Failed to sign message", {
1229
+ chain: this._chain
1230
+ });
1231
+ throw new SigningError(
1232
+ wrapped.code,
1233
+ wrapped.message,
1234
+ {
1235
+ operation: "signMessage",
1236
+ cause: wrapped.cause,
1237
+ context: wrapped.context
1238
+ }
1239
+ );
1240
+ }
1241
+ }
1242
+ /**
1243
+ * Get the chain name
1244
+ */
1245
+ getChain() {
1246
+ return this._chain;
1247
+ }
1248
+ /**
1249
+ * Get the chain ID
1250
+ */
1251
+ getChainId() {
1252
+ return getChainId(this._chain);
1253
+ }
1254
+ /**
1255
+ * Get the account index
1256
+ */
1257
+ getAccountIndex() {
1258
+ return this._accountIndex;
1259
+ }
1260
+ /**
1261
+ * Get native token balance (ETH, etc.)
1262
+ *
1263
+ * @throws {BalanceError} If balance fetch fails
1264
+ */
1265
+ async getBalance() {
1266
+ try {
1267
+ const account = await this.getAccount();
1268
+ return await withRetry(
1269
+ async () => {
1270
+ const balancePromise = account.getBalance();
1271
+ return withTimeout(balancePromise, this._timeoutMs, "Fetching native balance");
1272
+ },
1273
+ DEFAULT_BALANCE_RETRY
1274
+ );
1275
+ } catch (error) {
1276
+ if (error instanceof BalanceError) {
1277
+ throw error;
1278
+ }
1279
+ throw new BalanceError(
1280
+ 5001 /* BALANCE_FETCH_FAILED */,
1281
+ `Failed to get native balance for ${this._chain}: ${error instanceof Error ? error.message : String(error)}`,
1282
+ {
1283
+ chain: this._chain,
1284
+ cause: error instanceof Error ? error : void 0
1285
+ }
1286
+ );
1287
+ }
1288
+ }
1289
+ /**
1290
+ * Get ERC20 token balance
1291
+ *
1292
+ * @throws {BalanceError} If balance fetch fails
1293
+ */
1294
+ async getTokenBalance(tokenAddress) {
1295
+ if (!tokenAddress || !tokenAddress.startsWith("0x")) {
1296
+ throw new BalanceError(
1297
+ 5003 /* INVALID_TOKEN_ADDRESS */,
1298
+ `Invalid token address: ${tokenAddress}`,
1299
+ { chain: this._chain, token: tokenAddress }
1300
+ );
1301
+ }
1302
+ try {
1303
+ const account = await this.getAccount();
1304
+ return await withRetry(
1305
+ async () => {
1306
+ const balancePromise = account.getTokenBalance(tokenAddress);
1307
+ return withTimeout(balancePromise, this._timeoutMs, "Fetching token balance");
1308
+ },
1309
+ DEFAULT_BALANCE_RETRY
1310
+ );
1311
+ } catch (error) {
1312
+ if (error instanceof BalanceError) {
1313
+ throw error;
1314
+ }
1315
+ throw new BalanceError(
1316
+ 5002 /* TOKEN_BALANCE_FETCH_FAILED */,
1317
+ `Failed to get token balance for ${tokenAddress} on ${this._chain}: ${error instanceof Error ? error.message : String(error)}`,
1318
+ {
1319
+ chain: this._chain,
1320
+ token: tokenAddress,
1321
+ cause: error instanceof Error ? error : void 0
1322
+ }
1323
+ );
1324
+ }
1325
+ }
1326
+ /**
1327
+ * Estimate gas for a transaction
1328
+ *
1329
+ * @throws {TransactionError} If gas estimation fails
1330
+ */
1331
+ async estimateGas(params) {
1332
+ if (!params.to || !params.to.startsWith("0x")) {
1333
+ throw new TransactionError(
1334
+ 6002 /* GAS_ESTIMATION_FAILED */,
1335
+ `Invalid 'to' address: ${params.to}`,
1336
+ { chain: this._chain, context: params }
1337
+ );
1338
+ }
1339
+ try {
1340
+ const account = await this.getAccount();
1341
+ return await withRetry(
1342
+ async () => {
1343
+ const estimatePromise = account.estimateGas({
1344
+ to: params.to,
1345
+ value: params.value,
1346
+ data: params.data
1347
+ });
1348
+ return withTimeout(estimatePromise, this._timeoutMs, "Estimating gas");
1349
+ },
1350
+ DEFAULT_BALANCE_RETRY
1351
+ );
1352
+ } catch (error) {
1353
+ if (error instanceof TransactionError) {
1354
+ throw error;
1355
+ }
1356
+ throw new TransactionError(
1357
+ 6002 /* GAS_ESTIMATION_FAILED */,
1358
+ `Failed to estimate gas on ${this._chain}: ${error instanceof Error ? error.message : String(error)}`,
1359
+ {
1360
+ chain: this._chain,
1361
+ cause: error instanceof Error ? error : void 0,
1362
+ context: { to: params.to, value: params.value?.toString() }
1363
+ }
1364
+ );
1365
+ }
1366
+ }
1367
+ /**
1368
+ * Send a transaction (for advanced use cases)
1369
+ *
1370
+ * @throws {TransactionError} If transaction fails
1371
+ */
1372
+ async sendTransaction(params) {
1373
+ if (!params.to || !params.to.startsWith("0x")) {
1374
+ throw new TransactionError(
1375
+ 6001 /* TRANSACTION_FAILED */,
1376
+ `Invalid 'to' address: ${params.to}`,
1377
+ { chain: this._chain, context: params }
1378
+ );
1379
+ }
1380
+ try {
1381
+ const account = await this.getAccount();
1382
+ const sendPromise = account.sendTransaction({
1383
+ to: params.to,
1384
+ value: params.value,
1385
+ data: params.data
1386
+ });
1387
+ const hash = await withTimeout(
1388
+ sendPromise,
1389
+ this._timeoutMs * 2,
1390
+ // Double timeout for transaction submission
1391
+ "Sending transaction"
1392
+ );
1393
+ if (!hash || !hash.startsWith("0x")) {
1394
+ throw new TransactionError(
1395
+ 6001 /* TRANSACTION_FAILED */,
1396
+ `Invalid transaction hash received: ${hash?.substring(0, 10)}...`,
1397
+ { chain: this._chain }
1398
+ );
1399
+ }
1400
+ return { hash };
1401
+ } catch (error) {
1402
+ if (error instanceof TransactionError) {
1403
+ throw error;
1404
+ }
1405
+ const wrapped = wrapError(error, 6001 /* TRANSACTION_FAILED */, "Transaction failed", {
1406
+ chain: this._chain,
1407
+ to: params.to
1408
+ });
1409
+ throw new TransactionError(
1410
+ wrapped.code,
1411
+ wrapped.message,
1412
+ {
1413
+ chain: this._chain,
1414
+ cause: wrapped.cause,
1415
+ context: wrapped.context
1416
+ }
1417
+ );
1418
+ }
1419
+ }
1420
+ };
1421
+ async function createWDKSigner(wdk, chain, accountIndex = 0, timeoutMs = DEFAULT_TIMEOUT_MS) {
1422
+ const signer = new WDKSigner(wdk, chain, accountIndex, timeoutMs);
1423
+ await signer.initialize();
1424
+ return signer;
1425
+ }
1426
+ var MockWDKSigner = class {
1427
+ constructor(address, privateKey) {
1428
+ this.address = address;
1429
+ this._privateKey = privateKey;
1430
+ }
1431
+ async signTypedData(_message) {
1432
+ return "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000";
1433
+ }
1434
+ async signMessage(_message) {
1435
+ return "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000";
1436
+ }
1437
+ };
1438
+
1439
+ // src/t402wdk.ts
1440
+ var import_evm = require("@t402/evm");
1441
+ var _T402WDK = class _T402WDK {
1442
+ /**
1443
+ * Create a new T402WDK instance
1444
+ *
1445
+ * @param seedPhrase - BIP-39 mnemonic seed phrase
1446
+ * @param config - Chain configuration (RPC endpoints)
1447
+ * @param options - Additional options (cache configuration, etc.)
1448
+ * @throws {WDKInitializationError} If seed phrase is invalid
1449
+ */
1450
+ constructor(seedPhrase, config = {}, options = {}) {
1451
+ this._wdk = null;
1452
+ this._normalizedChains = /* @__PURE__ */ new Map();
1453
+ this._signerCache = /* @__PURE__ */ new Map();
1454
+ this._initializationError = null;
1455
+ if (!seedPhrase || typeof seedPhrase !== "string") {
1456
+ throw new WDKInitializationError("Seed phrase is required and must be a string");
1457
+ }
1458
+ const words = seedPhrase.trim().split(/\s+/);
1459
+ const validWordCounts = [12, 15, 18, 21, 24];
1460
+ if (!validWordCounts.includes(words.length)) {
1461
+ throw new WDKInitializationError(
1462
+ `Invalid seed phrase: expected 12, 15, 18, 21, or 24 words, got ${words.length}`,
1463
+ { context: { wordCount: words.length } }
1464
+ );
1465
+ }
1466
+ this._seedPhrase = seedPhrase;
1467
+ this._config = config;
1468
+ this._options = options;
1469
+ this._balanceCache = new BalanceCache(options.cache);
1470
+ for (const [chain, chainConfig] of Object.entries(config)) {
1471
+ if (chainConfig) {
1472
+ try {
1473
+ this._normalizedChains.set(chain, normalizeChainConfig(chain, chainConfig));
1474
+ } catch (error) {
1475
+ throw new ChainError(
1476
+ 2003 /* INVALID_CHAIN_CONFIG */,
1477
+ `Invalid configuration for chain "${chain}": ${error instanceof Error ? error.message : String(error)}`,
1478
+ { chain, cause: error instanceof Error ? error : void 0 }
1479
+ );
1480
+ }
1481
+ }
1482
+ }
1483
+ this._addDefaultChainsIfNeeded();
1484
+ if (_T402WDK._WDK) {
1485
+ this._initializeWDK();
1486
+ }
1487
+ }
1488
+ /**
1489
+ * Register the Tether WDK modules
1490
+ *
1491
+ * This must be called before creating T402WDK instances if you want
1492
+ * to use the actual WDK. Otherwise, a mock implementation is used.
1493
+ *
1494
+ * @throws {WDKInitializationError} If registration fails
1495
+ *
1496
+ * @example
1497
+ * ```typescript
1498
+ * import WDK from '@tetherto/wdk';
1499
+ * import WalletManagerEvm from '@tetherto/wdk-wallet-evm';
1500
+ * import BridgeUsdt0Evm from '@tetherto/wdk-protocol-bridge-usdt0-evm';
1501
+ *
1502
+ * T402WDK.registerWDK(WDK, WalletManagerEvm, BridgeUsdt0Evm);
1503
+ * ```
1504
+ */
1505
+ static registerWDK(WDK, WalletManagerEvm, BridgeUsdt0Evm) {
1506
+ if (!WDK) {
1507
+ throw new WDKInitializationError("WDK constructor is required");
1508
+ }
1509
+ if (typeof WDK !== "function") {
1510
+ throw new WDKInitializationError("WDK must be a constructor function");
1511
+ }
1512
+ _T402WDK._WDK = WDK;
1513
+ _T402WDK._WalletManagerEvm = WalletManagerEvm ?? null;
1514
+ _T402WDK._BridgeUsdt0Evm = BridgeUsdt0Evm ?? null;
1515
+ }
1516
+ /**
1517
+ * Check if WDK is registered
1518
+ */
1519
+ static isWDKRegistered() {
1520
+ return _T402WDK._WDK !== null;
1521
+ }
1522
+ /**
1523
+ * Check if wallet manager is registered
1524
+ */
1525
+ static isWalletManagerRegistered() {
1526
+ return _T402WDK._WalletManagerEvm !== null;
1527
+ }
1528
+ /**
1529
+ * Check if bridge protocol is registered
1530
+ */
1531
+ static isBridgeRegistered() {
1532
+ return _T402WDK._BridgeUsdt0Evm !== null;
1533
+ }
1534
+ /**
1535
+ * Generate a new random seed phrase
1536
+ *
1537
+ * @throws {WDKInitializationError} If WDK is not registered
1538
+ * @returns A new BIP-39 mnemonic seed phrase
1539
+ */
1540
+ static generateSeedPhrase() {
1541
+ if (!_T402WDK._WDK) {
1542
+ throw new WDKInitializationError(
1543
+ "WDK not registered. Call T402WDK.registerWDK() first, or use a mock seed phrase for testing."
1544
+ );
1545
+ }
1546
+ try {
1547
+ return _T402WDK._WDK.getRandomSeedPhrase();
1548
+ } catch (error) {
1549
+ throw new WDKInitializationError(
1550
+ `Failed to generate seed phrase: ${error instanceof Error ? error.message : String(error)}`,
1551
+ { cause: error instanceof Error ? error : void 0 }
1552
+ );
1553
+ }
1554
+ }
1555
+ /**
1556
+ * Add default chain configurations for common chains
1557
+ */
1558
+ _addDefaultChainsIfNeeded() {
1559
+ if (this._normalizedChains.size === 0) {
1560
+ const defaultEndpoint = DEFAULT_RPC_ENDPOINTS.arbitrum;
1561
+ if (defaultEndpoint) {
1562
+ this._normalizedChains.set(
1563
+ "arbitrum",
1564
+ normalizeChainConfig("arbitrum", defaultEndpoint)
1565
+ );
1566
+ }
1567
+ }
1568
+ }
1569
+ /**
1570
+ * Initialize the underlying WDK instance
1571
+ */
1572
+ _initializeWDK() {
1573
+ if (!_T402WDK._WDK) {
1574
+ this._initializationError = new WDKInitializationError("WDK not registered");
1575
+ return;
1576
+ }
1577
+ if (!_T402WDK._WalletManagerEvm) {
1578
+ this._initializationError = new WDKInitializationError(
1579
+ "WalletManagerEvm not registered. Call T402WDK.registerWDK(WDK, WalletManagerEvm) to enable wallet functionality."
1580
+ );
1581
+ return;
1582
+ }
1583
+ try {
1584
+ let wdk = new _T402WDK._WDK(this._seedPhrase);
1585
+ for (const [chain, config] of this._normalizedChains) {
1586
+ try {
1587
+ wdk = wdk.registerWallet(chain, _T402WDK._WalletManagerEvm, {
1588
+ provider: config.provider,
1589
+ chainId: config.chainId
1590
+ });
1591
+ } catch (error) {
1592
+ throw new ChainError(
1593
+ 2002 /* CHAIN_NOT_SUPPORTED */,
1594
+ `Failed to register wallet for chain "${chain}": ${error instanceof Error ? error.message : String(error)}`,
1595
+ { chain, cause: error instanceof Error ? error : void 0 }
1596
+ );
1597
+ }
1598
+ }
1599
+ if (_T402WDK._BridgeUsdt0Evm) {
1600
+ try {
1601
+ wdk = wdk.registerProtocol("bridge-usdt0", _T402WDK._BridgeUsdt0Evm);
1602
+ } catch (error) {
1603
+ console.warn(
1604
+ `Failed to register USDT0 bridge protocol: ${error instanceof Error ? error.message : String(error)}`
1605
+ );
1606
+ }
1607
+ }
1608
+ this._wdk = wdk;
1609
+ this._initializationError = null;
1610
+ } catch (error) {
1611
+ this._initializationError = error instanceof Error ? error : new Error(String(error));
1612
+ this._wdk = null;
1613
+ }
1614
+ }
1615
+ /**
1616
+ * Get the underlying WDK instance
1617
+ *
1618
+ * @throws {WDKInitializationError} If WDK is not initialized
1619
+ */
1620
+ get wdk() {
1621
+ if (this._initializationError) {
1622
+ throw this._initializationError instanceof WDKError ? this._initializationError : new WDKInitializationError(
1623
+ `WDK initialization failed: ${this._initializationError.message}`,
1624
+ { cause: this._initializationError }
1625
+ );
1626
+ }
1627
+ if (!this._wdk) {
1628
+ throw new WDKInitializationError(
1629
+ "WDK not initialized. Call T402WDK.registerWDK() before creating instances."
1630
+ );
1631
+ }
1632
+ return this._wdk;
1633
+ }
1634
+ /**
1635
+ * Check if WDK is properly initialized
1636
+ */
1637
+ get isInitialized() {
1638
+ return this._wdk !== null && this._initializationError === null;
1639
+ }
1640
+ /**
1641
+ * Get initialization error if any
1642
+ */
1643
+ get initializationError() {
1644
+ return this._initializationError;
1645
+ }
1646
+ /**
1647
+ * Get all configured chains
1648
+ */
1649
+ getConfiguredChains() {
1650
+ return Array.from(this._normalizedChains.keys());
1651
+ }
1652
+ /**
1653
+ * Get chain configuration
1654
+ */
1655
+ getChainConfig(chain) {
1656
+ return this._normalizedChains.get(chain);
1657
+ }
1658
+ /**
1659
+ * Check if a chain is configured
1660
+ */
1661
+ isChainConfigured(chain) {
1662
+ return this._normalizedChains.has(chain);
1663
+ }
1664
+ /**
1665
+ * Get a T402-compatible signer for a chain
1666
+ *
1667
+ * @param chain - Chain name (e.g., "arbitrum", "ethereum")
1668
+ * @param accountIndex - HD wallet account index (default: 0)
1669
+ * @throws {ChainError} If chain is not configured
1670
+ * @throws {SignerError} If signer creation fails
1671
+ * @returns An initialized WDKSigner
1672
+ */
1673
+ async getSigner(chain, accountIndex = 0) {
1674
+ if (!chain || typeof chain !== "string") {
1675
+ throw new ChainError(
1676
+ 2001 /* CHAIN_NOT_CONFIGURED */,
1677
+ "Chain name is required and must be a string",
1678
+ { chain }
1679
+ );
1680
+ }
1681
+ const cacheKey = `${chain}:${accountIndex}`;
1682
+ const cached = this._signerCache.get(cacheKey);
1683
+ if (cached) {
1684
+ return cached;
1685
+ }
1686
+ if (!this._normalizedChains.has(chain)) {
1687
+ const availableChains = this.getConfiguredChains();
1688
+ throw new ChainError(
1689
+ 2001 /* CHAIN_NOT_CONFIGURED */,
1690
+ `Chain "${chain}" not configured. Available chains: ${availableChains.length > 0 ? availableChains.join(", ") : "(none)"}`,
1691
+ { chain, context: { availableChains } }
1692
+ );
1693
+ }
1694
+ try {
1695
+ const signer = await createWDKSigner(this.wdk, chain, accountIndex);
1696
+ this._signerCache.set(cacheKey, signer);
1697
+ return signer;
1698
+ } catch (error) {
1699
+ if (isWDKError(error)) {
1700
+ throw error;
1701
+ }
1702
+ throw wrapError(
1703
+ error,
1704
+ 3001 /* SIGNER_NOT_INITIALIZED */,
1705
+ `Failed to create signer for chain "${chain}"`,
1706
+ { chain, accountIndex }
1707
+ );
1708
+ }
1709
+ }
1710
+ /**
1711
+ * Clear the signer cache
1712
+ * Useful for forcing re-initialization of signers
1713
+ */
1714
+ clearSignerCache() {
1715
+ this._signerCache.clear();
1716
+ }
1717
+ /**
1718
+ * Get wallet address for a chain
1719
+ *
1720
+ * @param chain - Chain name
1721
+ * @param accountIndex - HD wallet account index (default: 0)
1722
+ * @throws {ChainError} If chain is not configured
1723
+ * @throws {SignerError} If address fetch fails
1724
+ */
1725
+ async getAddress(chain, accountIndex = 0) {
1726
+ const signer = await this.getSigner(chain, accountIndex);
1727
+ return signer.address;
1728
+ }
1729
+ /**
1730
+ * Get USDT0 balance for a chain
1731
+ *
1732
+ * Uses cache if enabled to reduce RPC calls.
1733
+ *
1734
+ * @throws {BalanceError} If balance fetch fails
1735
+ */
1736
+ async getUsdt0Balance(chain, accountIndex = 0) {
1737
+ const usdt0Address = USDT0_ADDRESSES[chain];
1738
+ if (!usdt0Address) {
1739
+ return 0n;
1740
+ }
1741
+ try {
1742
+ const signer = await this.getSigner(chain, accountIndex);
1743
+ const address = signer.address;
1744
+ return await this._balanceCache.getOrFetchTokenBalance(
1745
+ chain,
1746
+ usdt0Address,
1747
+ address,
1748
+ async () => signer.getTokenBalance(usdt0Address)
1749
+ );
1750
+ } catch (error) {
1751
+ if (isWDKError(error) && error.code === 5002 /* TOKEN_BALANCE_FETCH_FAILED */) {
1752
+ return 0n;
1753
+ }
1754
+ throw error;
1755
+ }
1756
+ }
1757
+ /**
1758
+ * Get USDC balance for a chain
1759
+ *
1760
+ * Uses cache if enabled to reduce RPC calls.
1761
+ *
1762
+ * @throws {BalanceError} If balance fetch fails
1763
+ */
1764
+ async getUsdcBalance(chain, accountIndex = 0) {
1765
+ const usdcAddress = USDC_ADDRESSES[chain];
1766
+ if (!usdcAddress) {
1767
+ return 0n;
1768
+ }
1769
+ try {
1770
+ const signer = await this.getSigner(chain, accountIndex);
1771
+ const address = signer.address;
1772
+ return await this._balanceCache.getOrFetchTokenBalance(
1773
+ chain,
1774
+ usdcAddress,
1775
+ address,
1776
+ async () => signer.getTokenBalance(usdcAddress)
1777
+ );
1778
+ } catch (error) {
1779
+ if (isWDKError(error) && error.code === 5002 /* TOKEN_BALANCE_FETCH_FAILED */) {
1780
+ return 0n;
1781
+ }
1782
+ throw error;
1783
+ }
1784
+ }
1785
+ /**
1786
+ * Get all token balances for a chain
1787
+ *
1788
+ * Uses cache if enabled to reduce RPC calls.
1789
+ *
1790
+ * @throws {ChainError} If chain is not configured
1791
+ * @throws {BalanceError} If balance fetch fails
1792
+ */
1793
+ async getChainBalances(chain, accountIndex = 0) {
1794
+ const config = this._normalizedChains.get(chain);
1795
+ if (!config) {
1796
+ throw new ChainError(
1797
+ 2001 /* CHAIN_NOT_CONFIGURED */,
1798
+ `Chain "${chain}" not configured`,
1799
+ { chain }
1800
+ );
1801
+ }
1802
+ try {
1803
+ const signer = await this.getSigner(chain, accountIndex);
1804
+ const address = signer.address;
1805
+ const tokens = CHAIN_TOKENS[chain] || [];
1806
+ const tokenBalanceResults = await Promise.allSettled(
1807
+ tokens.map(async (token) => {
1808
+ const balance = await this._balanceCache.getOrFetchTokenBalance(
1809
+ chain,
1810
+ token.address,
1811
+ address,
1812
+ async () => signer.getTokenBalance(token.address)
1813
+ );
1814
+ return {
1815
+ token: token.address,
1816
+ symbol: token.symbol,
1817
+ balance,
1818
+ formatted: formatTokenAmount(balance, token.decimals),
1819
+ decimals: token.decimals
1820
+ };
1821
+ })
1822
+ );
1823
+ const tokenBalances = tokenBalanceResults.map((result, index) => {
1824
+ if (result.status === "fulfilled") {
1825
+ return result.value;
1826
+ }
1827
+ const token = tokens[index];
1828
+ return {
1829
+ token: token.address,
1830
+ symbol: token.symbol,
1831
+ balance: 0n,
1832
+ formatted: "0",
1833
+ decimals: token.decimals
1834
+ };
1835
+ });
1836
+ let nativeBalance;
1837
+ try {
1838
+ nativeBalance = await this._balanceCache.getOrFetchNativeBalance(
1839
+ chain,
1840
+ address,
1841
+ async () => signer.getBalance()
1842
+ );
1843
+ } catch {
1844
+ nativeBalance = 0n;
1845
+ }
1846
+ return {
1847
+ chain,
1848
+ network: config.network,
1849
+ native: nativeBalance,
1850
+ tokens: tokenBalances
1851
+ };
1852
+ } catch (error) {
1853
+ if (isWDKError(error)) {
1854
+ throw error;
1855
+ }
1856
+ throw new BalanceError(
1857
+ 5001 /* BALANCE_FETCH_FAILED */,
1858
+ `Failed to get balances for chain "${chain}": ${error instanceof Error ? error.message : String(error)}`,
1859
+ { chain, cause: error instanceof Error ? error : void 0 }
1860
+ );
1861
+ }
1862
+ }
1863
+ /**
1864
+ * Get aggregated balances across all configured chains
1865
+ *
1866
+ * @param accountIndex - HD wallet account index (default: 0)
1867
+ * @param options - Options for balance aggregation
1868
+ */
1869
+ async getAggregatedBalances(accountIndex = 0, options = {}) {
1870
+ const { continueOnError = true } = options;
1871
+ const chains = this.getConfiguredChains();
1872
+ const results = await Promise.allSettled(
1873
+ chains.map((chain) => this.getChainBalances(chain, accountIndex))
1874
+ );
1875
+ const chainBalances = [];
1876
+ const errors = [];
1877
+ for (let i = 0; i < results.length; i++) {
1878
+ const result = results[i];
1879
+ if (result.status === "fulfilled") {
1880
+ chainBalances.push(result.value);
1881
+ } else {
1882
+ errors.push(result.reason);
1883
+ if (!continueOnError) {
1884
+ throw result.reason;
1885
+ }
1886
+ const config = this._normalizedChains.get(chains[i]);
1887
+ if (config) {
1888
+ chainBalances.push({
1889
+ chain: chains[i],
1890
+ network: config.network,
1891
+ native: 0n,
1892
+ tokens: []
1893
+ });
1894
+ }
1895
+ }
1896
+ }
1897
+ let totalUsdt0 = 0n;
1898
+ let totalUsdc = 0n;
1899
+ for (const chainBalance of chainBalances) {
1900
+ for (const token of chainBalance.tokens) {
1901
+ if (token.symbol === "USDT0") {
1902
+ totalUsdt0 += token.balance;
1903
+ } else if (token.symbol === "USDC") {
1904
+ totalUsdc += token.balance;
1905
+ }
1906
+ }
1907
+ }
1908
+ return {
1909
+ totalUsdt0,
1910
+ totalUsdc,
1911
+ chains: chainBalances
1912
+ };
1913
+ }
1914
+ /**
1915
+ * Find the best chain for a payment
1916
+ *
1917
+ * Looks for the chain with sufficient balance, prioritizing USDT0.
1918
+ *
1919
+ * @param amount - Required amount in smallest units
1920
+ * @param preferredToken - Preferred token ("USDT0" | "USDC")
1921
+ * @throws {BalanceError} If balance aggregation fails
1922
+ */
1923
+ async findBestChainForPayment(amount, preferredToken = "USDT0") {
1924
+ if (amount <= 0n) {
1925
+ return null;
1926
+ }
1927
+ try {
1928
+ const balances = await this.getAggregatedBalances(0, { continueOnError: true });
1929
+ const tokenPriority = preferredToken === "USDT0" ? ["USDT0", "USDC"] : ["USDC", "USDT0"];
1930
+ for (const tokenSymbol of tokenPriority) {
1931
+ for (const chainBalance of balances.chains) {
1932
+ const tokenBalance = chainBalance.tokens.find((t) => t.symbol === tokenSymbol);
1933
+ if (tokenBalance && tokenBalance.balance >= amount) {
1934
+ return {
1935
+ chain: chainBalance.chain,
1936
+ token: tokenSymbol,
1937
+ balance: tokenBalance.balance
1938
+ };
1939
+ }
1940
+ }
1941
+ }
1942
+ return null;
1943
+ } catch (error) {
1944
+ if (isWDKError(error)) {
1945
+ throw error;
1946
+ }
1947
+ throw new BalanceError(
1948
+ 5001 /* BALANCE_FETCH_FAILED */,
1949
+ `Failed to find best chain for payment: ${error instanceof Error ? error.message : String(error)}`,
1950
+ { cause: error instanceof Error ? error : void 0, context: { amount: amount.toString() } }
1951
+ );
1952
+ }
1953
+ }
1954
+ /**
1955
+ * Bridge USDT0 between chains
1956
+ *
1957
+ * Uses LayerZero OFT for cross-chain transfers.
1958
+ *
1959
+ * @param params - Bridge parameters
1960
+ * @throws {BridgeError} If bridge is not available or fails
1961
+ * @returns Bridge result with transaction hash
1962
+ */
1963
+ async bridgeUsdt0(params) {
1964
+ if (!_T402WDK._BridgeUsdt0Evm) {
1965
+ throw new BridgeError(
1966
+ 7001 /* BRIDGE_NOT_AVAILABLE */,
1967
+ "USDT0 bridge not available. Register BridgeUsdt0Evm with T402WDK.registerWDK().",
1968
+ { fromChain: params.fromChain, toChain: params.toChain }
1969
+ );
1970
+ }
1971
+ if (!params.fromChain || !params.toChain) {
1972
+ throw new BridgeError(
1973
+ 7003 /* BRIDGE_FAILED */,
1974
+ "Both fromChain and toChain are required",
1975
+ { fromChain: params.fromChain, toChain: params.toChain }
1976
+ );
1977
+ }
1978
+ if (params.fromChain === params.toChain) {
1979
+ throw new BridgeError(
1980
+ 7002 /* BRIDGE_NOT_SUPPORTED */,
1981
+ "Cannot bridge to the same chain",
1982
+ { fromChain: params.fromChain, toChain: params.toChain }
1983
+ );
1984
+ }
1985
+ if (!params.amount || params.amount <= 0n) {
1986
+ throw new BridgeError(
1987
+ 7003 /* BRIDGE_FAILED */,
1988
+ "Amount must be greater than 0",
1989
+ { fromChain: params.fromChain, toChain: params.toChain, context: { amount: params.amount?.toString() } }
1990
+ );
1991
+ }
1992
+ if (!this.canBridge(params.fromChain, params.toChain)) {
1993
+ throw new BridgeError(
1994
+ 7002 /* BRIDGE_NOT_SUPPORTED */,
1995
+ `Bridging from "${params.fromChain}" to "${params.toChain}" is not supported`,
1996
+ { fromChain: params.fromChain, toChain: params.toChain }
1997
+ );
1998
+ }
1999
+ try {
2000
+ const recipient = params.recipient ?? await this.getAddress(params.toChain);
2001
+ const result = await this.wdk.executeProtocol("bridge-usdt0", {
2002
+ fromChain: params.fromChain,
2003
+ toChain: params.toChain,
2004
+ amount: params.amount,
2005
+ recipient
2006
+ });
2007
+ if (!result || !result.txHash) {
2008
+ throw new BridgeError(
2009
+ 7003 /* BRIDGE_FAILED */,
2010
+ "Bridge transaction did not return a transaction hash",
2011
+ { fromChain: params.fromChain, toChain: params.toChain }
2012
+ );
2013
+ }
2014
+ return {
2015
+ txHash: result.txHash,
2016
+ estimatedTime: 300
2017
+ // ~5 minutes typical for LayerZero
2018
+ };
2019
+ } catch (error) {
2020
+ if (error instanceof BridgeError) {
2021
+ throw error;
2022
+ }
2023
+ throw new BridgeError(
2024
+ 7003 /* BRIDGE_FAILED */,
2025
+ `Bridge operation failed: ${error instanceof Error ? error.message : String(error)}`,
2026
+ {
2027
+ fromChain: params.fromChain,
2028
+ toChain: params.toChain,
2029
+ cause: error instanceof Error ? error : void 0,
2030
+ context: { amount: params.amount.toString() }
2031
+ }
2032
+ );
2033
+ }
2034
+ }
2035
+ /**
2036
+ * Get chains that support USDT0
2037
+ */
2038
+ getUsdt0Chains() {
2039
+ return this.getConfiguredChains().filter((chain) => USDT0_ADDRESSES[chain]);
2040
+ }
2041
+ /**
2042
+ * Get chains that support USDT0 bridging
2043
+ *
2044
+ * Returns configured chains that have LayerZero OFT bridge support.
2045
+ */
2046
+ getBridgeableChains() {
2047
+ return this.getConfiguredChains().filter((chain) => (0, import_evm.supportsBridging)(chain));
2048
+ }
2049
+ /**
2050
+ * Check if bridging is supported between two chains
2051
+ */
2052
+ canBridge(fromChain, toChain) {
2053
+ return fromChain !== toChain && (0, import_evm.supportsBridging)(fromChain) && (0, import_evm.supportsBridging)(toChain) && this._normalizedChains.has(fromChain);
2054
+ }
2055
+ /**
2056
+ * Get all possible bridge destinations from a chain
2057
+ */
2058
+ getBridgeDestinations(fromChain) {
2059
+ if (!(0, import_evm.supportsBridging)(fromChain)) {
2060
+ return [];
2061
+ }
2062
+ return (0, import_evm.getBridgeableChains)().filter((chain) => chain !== fromChain);
2063
+ }
2064
+ // ========== Cache Management ==========
2065
+ /**
2066
+ * Check if balance caching is enabled
2067
+ */
2068
+ get isCacheEnabled() {
2069
+ return this._balanceCache.enabled;
2070
+ }
2071
+ /**
2072
+ * Get cache configuration
2073
+ */
2074
+ getCacheConfig() {
2075
+ return this._balanceCache.config;
2076
+ }
2077
+ /**
2078
+ * Get cache statistics
2079
+ */
2080
+ getCacheStats() {
2081
+ return this._balanceCache.getStats();
2082
+ }
2083
+ /**
2084
+ * Invalidate all cached balances
2085
+ *
2086
+ * Call this after sending transactions to ensure fresh balance data.
2087
+ */
2088
+ invalidateBalanceCache() {
2089
+ this._balanceCache.clear();
2090
+ }
2091
+ /**
2092
+ * Invalidate cached balances for a specific chain
2093
+ *
2094
+ * @param chain - Chain name to invalidate
2095
+ * @returns Number of cache entries invalidated
2096
+ */
2097
+ invalidateChainCache(chain) {
2098
+ return this._balanceCache.invalidateChain(chain);
2099
+ }
2100
+ /**
2101
+ * Invalidate cached balances for a specific address
2102
+ *
2103
+ * @param address - Address to invalidate (case-insensitive)
2104
+ * @returns Number of cache entries invalidated
2105
+ */
2106
+ invalidateAddressCache(address) {
2107
+ return this._balanceCache.invalidateAddress(address);
2108
+ }
2109
+ /**
2110
+ * Dispose of cache resources
2111
+ *
2112
+ * Call this when the T402WDK instance is no longer needed.
2113
+ */
2114
+ dispose() {
2115
+ this._balanceCache.dispose();
2116
+ this._signerCache.clear();
2117
+ }
2118
+ };
2119
+ // WDK module references (set via registerWDK)
2120
+ _T402WDK._WDK = null;
2121
+ _T402WDK._WalletManagerEvm = null;
2122
+ _T402WDK._BridgeUsdt0Evm = null;
2123
+ var T402WDK = _T402WDK;
2124
+ function formatTokenAmount(amount, decimals) {
2125
+ if (amount === 0n) {
2126
+ return "0";
2127
+ }
2128
+ const divisor = BigInt(10 ** decimals);
2129
+ const whole = amount / divisor;
2130
+ const fraction = amount % divisor;
2131
+ if (fraction === 0n) {
2132
+ return whole.toString();
2133
+ }
2134
+ const fractionStr = fraction.toString().padStart(decimals, "0");
2135
+ const trimmed = fractionStr.replace(/0+$/, "");
2136
+ return `${whole}.${trimmed}`;
2137
+ }
2138
+
2139
+ // src/bridge.ts
2140
+ var import_evm2 = require("@t402/evm");
2141
+ var WdkBridge = class {
2142
+ constructor() {
2143
+ this.bridges = /* @__PURE__ */ new Map();
2144
+ }
2145
+ /**
2146
+ * Create bridge signer adapter from WDK signer
2147
+ */
2148
+ createBridgeSigner(signer) {
2149
+ return {
2150
+ address: signer.address,
2151
+ readContract: async (args) => {
2152
+ throw new Error(
2153
+ "readContract not available on WDKSigner. Use T402WDK.bridgeUsdt0() instead."
2154
+ );
2155
+ },
2156
+ writeContract: async (args) => {
2157
+ throw new Error(
2158
+ "writeContract not available on WDKSigner. Use T402WDK.bridgeUsdt0() instead."
2159
+ );
2160
+ },
2161
+ waitForTransactionReceipt: async (args) => {
2162
+ throw new Error(
2163
+ "waitForTransactionReceipt not available on WDKSigner. Use T402WDK.bridgeUsdt0() instead."
2164
+ );
2165
+ }
2166
+ };
2167
+ }
2168
+ /**
2169
+ * Get or create a bridge instance for a chain
2170
+ */
2171
+ getBridge(chain, signer) {
2172
+ const cached = this.bridges.get(chain);
2173
+ if (cached) {
2174
+ return cached;
2175
+ }
2176
+ const bridgeSigner = this.createBridgeSigner(signer);
2177
+ const bridge = new import_evm2.Usdt0Bridge(bridgeSigner, chain);
2178
+ this.bridges.set(chain, bridge);
2179
+ return bridge;
2180
+ }
2181
+ /**
2182
+ * Check if a chain supports USDT0 bridging
2183
+ */
2184
+ static supportsBridging(chain) {
2185
+ return (0, import_evm2.supportsBridging)(chain);
2186
+ }
2187
+ /**
2188
+ * Get all chains that support USDT0 bridging
2189
+ */
2190
+ static getBridgeableChains() {
2191
+ return (0, import_evm2.getBridgeableChains)();
2192
+ }
2193
+ /**
2194
+ * Get supported destinations from a source chain
2195
+ */
2196
+ static getSupportedDestinations(fromChain) {
2197
+ return (0, import_evm2.getBridgeableChains)().filter((chain) => chain !== fromChain);
2198
+ }
2199
+ };
2200
+ function createDirectBridge(signer, chain) {
2201
+ return new import_evm2.Usdt0Bridge(signer, chain);
2202
+ }
2203
+
2204
+ // src/index.ts
2205
+ var import_evm3 = require("@t402/evm");
2206
+ // Annotate the CommonJS export names for ESM import in node:
2207
+ 0 && (module.exports = {
2208
+ BalanceCache,
2209
+ BalanceError,
2210
+ BridgeError,
2211
+ CHAIN_TOKENS,
2212
+ ChainError,
2213
+ DEFAULT_BALANCE_CACHE_CONFIG,
2214
+ DEFAULT_CACHE_CONFIG,
2215
+ DEFAULT_CHAINS,
2216
+ DEFAULT_RETRY_CONFIG,
2217
+ DEFAULT_RPC_ENDPOINTS,
2218
+ LAYERZERO_ENDPOINT_IDS,
2219
+ MockWDKSigner,
2220
+ RPCError,
2221
+ SignerError,
2222
+ SigningError,
2223
+ T402WDK,
2224
+ TTLCache,
2225
+ TransactionError,
2226
+ USDC_ADDRESSES,
2227
+ USDT0_ADDRESSES,
2228
+ USDT0_OFT_ADDRESSES,
2229
+ USDT_LEGACY_ADDRESSES,
2230
+ Usdt0Bridge,
2231
+ WDKError,
2232
+ WDKErrorCode,
2233
+ WDKInitializationError,
2234
+ WDKSigner,
2235
+ WdkBridge,
2236
+ createDirectBridge,
2237
+ createWDKSigner,
2238
+ getBridgeableChains,
2239
+ getChainFromNetwork,
2240
+ getChainId,
2241
+ getNetworkFromChain,
2242
+ getPreferredToken,
2243
+ getUsdt0Chains,
2244
+ hasErrorCode,
2245
+ isWDKError,
2246
+ normalizeChainConfig,
2247
+ supportsBridging,
2248
+ withRetry,
2249
+ withTimeout,
2250
+ wrapError
2251
+ });
2252
+ //# sourceMappingURL=index.js.map