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