@circle-fin/bridge-kit 1.5.0 → 1.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +28 -0
- package/README.md +107 -10
- package/chains.cjs +175 -3
- package/chains.d.ts +151 -3
- package/chains.mjs +175 -3
- package/index.cjs +1925 -145
- package/index.d.ts +4082 -2171
- package/index.mjs +1919 -148
- package/package.json +4 -3
package/index.mjs
CHANGED
|
@@ -21,6 +21,7 @@ import '@ethersproject/bytes';
|
|
|
21
21
|
import '@ethersproject/address';
|
|
22
22
|
import 'bs58';
|
|
23
23
|
import { formatUnits as formatUnits$1, parseUnits as parseUnits$1 } from '@ethersproject/units';
|
|
24
|
+
import pino from 'pino';
|
|
24
25
|
import { CCTPV2BridgingProvider } from '@circle-fin/provider-cctp-v2';
|
|
25
26
|
|
|
26
27
|
/**
|
|
@@ -653,6 +654,18 @@ const NetworkError = {
|
|
|
653
654
|
name: 'NETWORK_TIMEOUT',
|
|
654
655
|
type: 'NETWORK',
|
|
655
656
|
},
|
|
657
|
+
/** Circle relayer failed to process the forwarding/mint transaction */
|
|
658
|
+
RELAYER_FORWARD_FAILED: {
|
|
659
|
+
code: 3003,
|
|
660
|
+
name: 'NETWORK_RELAYER_FORWARD_FAILED',
|
|
661
|
+
type: 'NETWORK',
|
|
662
|
+
},
|
|
663
|
+
/** Relayer mint is pending - waiting for confirmation */
|
|
664
|
+
RELAYER_PENDING: {
|
|
665
|
+
code: 3004,
|
|
666
|
+
name: 'NETWORK_RELAYER_PENDING',
|
|
667
|
+
type: 'NETWORK',
|
|
668
|
+
},
|
|
656
669
|
};
|
|
657
670
|
|
|
658
671
|
/**
|
|
@@ -1213,6 +1226,10 @@ const Aptos = defineChain({
|
|
|
1213
1226
|
confirmations: 1,
|
|
1214
1227
|
},
|
|
1215
1228
|
},
|
|
1229
|
+
forwarderSupported: {
|
|
1230
|
+
source: false,
|
|
1231
|
+
destination: false,
|
|
1232
|
+
},
|
|
1216
1233
|
},
|
|
1217
1234
|
});
|
|
1218
1235
|
|
|
@@ -1246,6 +1263,10 @@ const AptosTestnet = defineChain({
|
|
|
1246
1263
|
confirmations: 1,
|
|
1247
1264
|
},
|
|
1248
1265
|
},
|
|
1266
|
+
forwarderSupported: {
|
|
1267
|
+
source: false,
|
|
1268
|
+
destination: false,
|
|
1269
|
+
},
|
|
1249
1270
|
},
|
|
1250
1271
|
});
|
|
1251
1272
|
|
|
@@ -1305,6 +1326,10 @@ const ArcTestnet = defineChain({
|
|
|
1305
1326
|
fastConfirmations: 1,
|
|
1306
1327
|
},
|
|
1307
1328
|
},
|
|
1329
|
+
forwarderSupported: {
|
|
1330
|
+
source: true,
|
|
1331
|
+
destination: true,
|
|
1332
|
+
},
|
|
1308
1333
|
},
|
|
1309
1334
|
kitContracts: {
|
|
1310
1335
|
bridge: BRIDGE_CONTRACT_EVM_TESTNET,
|
|
@@ -1349,6 +1374,10 @@ const Arbitrum = defineChain({
|
|
|
1349
1374
|
fastConfirmations: 1,
|
|
1350
1375
|
},
|
|
1351
1376
|
},
|
|
1377
|
+
forwarderSupported: {
|
|
1378
|
+
source: true,
|
|
1379
|
+
destination: true,
|
|
1380
|
+
},
|
|
1352
1381
|
},
|
|
1353
1382
|
kitContracts: {
|
|
1354
1383
|
bridge: BRIDGE_CONTRACT_EVM_MAINNET,
|
|
@@ -1393,6 +1422,10 @@ const ArbitrumSepolia = defineChain({
|
|
|
1393
1422
|
fastConfirmations: 1,
|
|
1394
1423
|
},
|
|
1395
1424
|
},
|
|
1425
|
+
forwarderSupported: {
|
|
1426
|
+
source: true,
|
|
1427
|
+
destination: true,
|
|
1428
|
+
},
|
|
1396
1429
|
},
|
|
1397
1430
|
kitContracts: {
|
|
1398
1431
|
bridge: BRIDGE_CONTRACT_EVM_TESTNET,
|
|
@@ -1437,6 +1470,10 @@ const Avalanche = defineChain({
|
|
|
1437
1470
|
fastConfirmations: 1,
|
|
1438
1471
|
},
|
|
1439
1472
|
},
|
|
1473
|
+
forwarderSupported: {
|
|
1474
|
+
source: true,
|
|
1475
|
+
destination: true,
|
|
1476
|
+
},
|
|
1440
1477
|
},
|
|
1441
1478
|
kitContracts: {
|
|
1442
1479
|
bridge: BRIDGE_CONTRACT_EVM_MAINNET,
|
|
@@ -1480,6 +1517,10 @@ const AvalancheFuji = defineChain({
|
|
|
1480
1517
|
fastConfirmations: 1,
|
|
1481
1518
|
},
|
|
1482
1519
|
},
|
|
1520
|
+
forwarderSupported: {
|
|
1521
|
+
source: true,
|
|
1522
|
+
destination: true,
|
|
1523
|
+
},
|
|
1483
1524
|
},
|
|
1484
1525
|
rpcEndpoints: ['https://api.avax-test.network/ext/bc/C/rpc'],
|
|
1485
1526
|
kitContracts: {
|
|
@@ -1525,6 +1566,10 @@ const Base = defineChain({
|
|
|
1525
1566
|
fastConfirmations: 1,
|
|
1526
1567
|
},
|
|
1527
1568
|
},
|
|
1569
|
+
forwarderSupported: {
|
|
1570
|
+
source: true,
|
|
1571
|
+
destination: true,
|
|
1572
|
+
},
|
|
1528
1573
|
},
|
|
1529
1574
|
kitContracts: {
|
|
1530
1575
|
bridge: BRIDGE_CONTRACT_EVM_MAINNET,
|
|
@@ -1569,6 +1614,10 @@ const BaseSepolia = defineChain({
|
|
|
1569
1614
|
fastConfirmations: 1,
|
|
1570
1615
|
},
|
|
1571
1616
|
},
|
|
1617
|
+
forwarderSupported: {
|
|
1618
|
+
source: true,
|
|
1619
|
+
destination: true,
|
|
1620
|
+
},
|
|
1572
1621
|
},
|
|
1573
1622
|
kitContracts: {
|
|
1574
1623
|
bridge: BRIDGE_CONTRACT_EVM_TESTNET,
|
|
@@ -1655,6 +1704,10 @@ const Codex = defineChain({
|
|
|
1655
1704
|
fastConfirmations: 1,
|
|
1656
1705
|
},
|
|
1657
1706
|
},
|
|
1707
|
+
forwarderSupported: {
|
|
1708
|
+
source: true,
|
|
1709
|
+
destination: false,
|
|
1710
|
+
},
|
|
1658
1711
|
},
|
|
1659
1712
|
kitContracts: {
|
|
1660
1713
|
bridge: BRIDGE_CONTRACT_EVM_MAINNET,
|
|
@@ -1693,6 +1746,10 @@ const CodexTestnet = defineChain({
|
|
|
1693
1746
|
fastConfirmations: 1,
|
|
1694
1747
|
},
|
|
1695
1748
|
},
|
|
1749
|
+
forwarderSupported: {
|
|
1750
|
+
source: true,
|
|
1751
|
+
destination: false,
|
|
1752
|
+
},
|
|
1696
1753
|
},
|
|
1697
1754
|
kitContracts: {
|
|
1698
1755
|
bridge: BRIDGE_CONTRACT_EVM_TESTNET,
|
|
@@ -1737,6 +1794,10 @@ const Ethereum = defineChain({
|
|
|
1737
1794
|
fastConfirmations: 2,
|
|
1738
1795
|
},
|
|
1739
1796
|
},
|
|
1797
|
+
forwarderSupported: {
|
|
1798
|
+
source: true,
|
|
1799
|
+
destination: true,
|
|
1800
|
+
},
|
|
1740
1801
|
},
|
|
1741
1802
|
kitContracts: {
|
|
1742
1803
|
bridge: BRIDGE_CONTRACT_EVM_MAINNET,
|
|
@@ -1781,6 +1842,10 @@ const EthereumSepolia = defineChain({
|
|
|
1781
1842
|
fastConfirmations: 2,
|
|
1782
1843
|
},
|
|
1783
1844
|
},
|
|
1845
|
+
forwarderSupported: {
|
|
1846
|
+
source: true,
|
|
1847
|
+
destination: true,
|
|
1848
|
+
},
|
|
1784
1849
|
},
|
|
1785
1850
|
kitContracts: {
|
|
1786
1851
|
bridge: BRIDGE_CONTRACT_EVM_TESTNET,
|
|
@@ -1867,6 +1932,10 @@ const HyperEVM = defineChain({
|
|
|
1867
1932
|
fastConfirmations: 1,
|
|
1868
1933
|
},
|
|
1869
1934
|
},
|
|
1935
|
+
forwarderSupported: {
|
|
1936
|
+
source: true,
|
|
1937
|
+
destination: true,
|
|
1938
|
+
},
|
|
1870
1939
|
},
|
|
1871
1940
|
kitContracts: {
|
|
1872
1941
|
bridge: BRIDGE_CONTRACT_EVM_MAINNET,
|
|
@@ -1906,6 +1975,10 @@ const HyperEVMTestnet = defineChain({
|
|
|
1906
1975
|
fastConfirmations: 1,
|
|
1907
1976
|
},
|
|
1908
1977
|
},
|
|
1978
|
+
forwarderSupported: {
|
|
1979
|
+
source: true,
|
|
1980
|
+
destination: true,
|
|
1981
|
+
},
|
|
1909
1982
|
},
|
|
1910
1983
|
kitContracts: {
|
|
1911
1984
|
bridge: BRIDGE_CONTRACT_EVM_TESTNET,
|
|
@@ -1949,6 +2022,10 @@ const Ink = defineChain({
|
|
|
1949
2022
|
fastConfirmations: 1,
|
|
1950
2023
|
},
|
|
1951
2024
|
},
|
|
2025
|
+
forwarderSupported: {
|
|
2026
|
+
source: true,
|
|
2027
|
+
destination: true,
|
|
2028
|
+
},
|
|
1952
2029
|
},
|
|
1953
2030
|
kitContracts: {
|
|
1954
2031
|
bridge: BRIDGE_CONTRACT_EVM_MAINNET,
|
|
@@ -1991,6 +2068,10 @@ const InkTestnet = defineChain({
|
|
|
1991
2068
|
fastConfirmations: 1,
|
|
1992
2069
|
},
|
|
1993
2070
|
},
|
|
2071
|
+
forwarderSupported: {
|
|
2072
|
+
source: true,
|
|
2073
|
+
destination: true,
|
|
2074
|
+
},
|
|
1994
2075
|
},
|
|
1995
2076
|
kitContracts: {
|
|
1996
2077
|
bridge: BRIDGE_CONTRACT_EVM_TESTNET,
|
|
@@ -2029,6 +2110,10 @@ const Linea = defineChain({
|
|
|
2029
2110
|
fastConfirmations: 1,
|
|
2030
2111
|
},
|
|
2031
2112
|
},
|
|
2113
|
+
forwarderSupported: {
|
|
2114
|
+
source: true,
|
|
2115
|
+
destination: true,
|
|
2116
|
+
},
|
|
2032
2117
|
},
|
|
2033
2118
|
kitContracts: {
|
|
2034
2119
|
bridge: BRIDGE_CONTRACT_EVM_MAINNET,
|
|
@@ -2067,6 +2152,10 @@ const LineaSepolia = defineChain({
|
|
|
2067
2152
|
fastConfirmations: 1,
|
|
2068
2153
|
},
|
|
2069
2154
|
},
|
|
2155
|
+
forwarderSupported: {
|
|
2156
|
+
source: true,
|
|
2157
|
+
destination: true,
|
|
2158
|
+
},
|
|
2070
2159
|
},
|
|
2071
2160
|
kitContracts: {
|
|
2072
2161
|
bridge: BRIDGE_CONTRACT_EVM_TESTNET,
|
|
@@ -2107,6 +2196,10 @@ const Monad = defineChain({
|
|
|
2107
2196
|
fastConfirmations: 1,
|
|
2108
2197
|
},
|
|
2109
2198
|
},
|
|
2199
|
+
forwarderSupported: {
|
|
2200
|
+
source: true,
|
|
2201
|
+
destination: true,
|
|
2202
|
+
},
|
|
2110
2203
|
},
|
|
2111
2204
|
kitContracts: {
|
|
2112
2205
|
bridge: BRIDGE_CONTRACT_EVM_MAINNET,
|
|
@@ -2147,6 +2240,10 @@ const MonadTestnet = defineChain({
|
|
|
2147
2240
|
fastConfirmations: 1,
|
|
2148
2241
|
},
|
|
2149
2242
|
},
|
|
2243
|
+
forwarderSupported: {
|
|
2244
|
+
source: true,
|
|
2245
|
+
destination: true,
|
|
2246
|
+
},
|
|
2150
2247
|
},
|
|
2151
2248
|
kitContracts: {
|
|
2152
2249
|
bridge: BRIDGE_CONTRACT_EVM_TESTNET,
|
|
@@ -2228,6 +2325,10 @@ const Noble = defineChain({
|
|
|
2228
2325
|
confirmations: 1,
|
|
2229
2326
|
},
|
|
2230
2327
|
},
|
|
2328
|
+
forwarderSupported: {
|
|
2329
|
+
source: false,
|
|
2330
|
+
destination: false,
|
|
2331
|
+
},
|
|
2231
2332
|
},
|
|
2232
2333
|
});
|
|
2233
2334
|
|
|
@@ -2260,6 +2361,10 @@ const NobleTestnet = defineChain({
|
|
|
2260
2361
|
confirmations: 1,
|
|
2261
2362
|
},
|
|
2262
2363
|
},
|
|
2364
|
+
forwarderSupported: {
|
|
2365
|
+
source: false,
|
|
2366
|
+
destination: false,
|
|
2367
|
+
},
|
|
2263
2368
|
},
|
|
2264
2369
|
});
|
|
2265
2370
|
|
|
@@ -2301,6 +2406,10 @@ const Optimism = defineChain({
|
|
|
2301
2406
|
fastConfirmations: 1,
|
|
2302
2407
|
},
|
|
2303
2408
|
},
|
|
2409
|
+
forwarderSupported: {
|
|
2410
|
+
source: true,
|
|
2411
|
+
destination: true,
|
|
2412
|
+
},
|
|
2304
2413
|
},
|
|
2305
2414
|
kitContracts: {
|
|
2306
2415
|
bridge: BRIDGE_CONTRACT_EVM_MAINNET,
|
|
@@ -2345,6 +2454,10 @@ const OptimismSepolia = defineChain({
|
|
|
2345
2454
|
fastConfirmations: 1,
|
|
2346
2455
|
},
|
|
2347
2456
|
},
|
|
2457
|
+
forwarderSupported: {
|
|
2458
|
+
source: true,
|
|
2459
|
+
destination: true,
|
|
2460
|
+
},
|
|
2348
2461
|
},
|
|
2349
2462
|
kitContracts: {
|
|
2350
2463
|
bridge: BRIDGE_CONTRACT_EVM_TESTNET,
|
|
@@ -2385,6 +2498,10 @@ const Plume = defineChain({
|
|
|
2385
2498
|
fastConfirmations: 1,
|
|
2386
2499
|
},
|
|
2387
2500
|
},
|
|
2501
|
+
forwarderSupported: {
|
|
2502
|
+
source: true,
|
|
2503
|
+
destination: false,
|
|
2504
|
+
},
|
|
2388
2505
|
},
|
|
2389
2506
|
kitContracts: {
|
|
2390
2507
|
bridge: BRIDGE_CONTRACT_EVM_MAINNET,
|
|
@@ -2424,6 +2541,10 @@ const PlumeTestnet = defineChain({
|
|
|
2424
2541
|
fastConfirmations: 1,
|
|
2425
2542
|
},
|
|
2426
2543
|
},
|
|
2544
|
+
forwarderSupported: {
|
|
2545
|
+
source: true,
|
|
2546
|
+
destination: false,
|
|
2547
|
+
},
|
|
2427
2548
|
},
|
|
2428
2549
|
kitContracts: {
|
|
2429
2550
|
bridge: BRIDGE_CONTRACT_EVM_TESTNET,
|
|
@@ -2514,6 +2635,10 @@ const Polygon = defineChain({
|
|
|
2514
2635
|
fastConfirmations: 13,
|
|
2515
2636
|
},
|
|
2516
2637
|
},
|
|
2638
|
+
forwarderSupported: {
|
|
2639
|
+
source: true,
|
|
2640
|
+
destination: true,
|
|
2641
|
+
},
|
|
2517
2642
|
},
|
|
2518
2643
|
kitContracts: {
|
|
2519
2644
|
bridge: BRIDGE_CONTRACT_EVM_MAINNET,
|
|
@@ -2558,6 +2683,10 @@ const PolygonAmoy = defineChain({
|
|
|
2558
2683
|
fastConfirmations: 13,
|
|
2559
2684
|
},
|
|
2560
2685
|
},
|
|
2686
|
+
forwarderSupported: {
|
|
2687
|
+
source: true,
|
|
2688
|
+
destination: true,
|
|
2689
|
+
},
|
|
2561
2690
|
},
|
|
2562
2691
|
kitContracts: {
|
|
2563
2692
|
bridge: BRIDGE_CONTRACT_EVM_TESTNET,
|
|
@@ -2598,6 +2727,10 @@ const Sei = defineChain({
|
|
|
2598
2727
|
fastConfirmations: 1,
|
|
2599
2728
|
},
|
|
2600
2729
|
},
|
|
2730
|
+
forwarderSupported: {
|
|
2731
|
+
source: true,
|
|
2732
|
+
destination: true,
|
|
2733
|
+
},
|
|
2601
2734
|
},
|
|
2602
2735
|
kitContracts: {
|
|
2603
2736
|
bridge: BRIDGE_CONTRACT_EVM_MAINNET,
|
|
@@ -2637,6 +2770,10 @@ const SeiTestnet = defineChain({
|
|
|
2637
2770
|
fastConfirmations: 1,
|
|
2638
2771
|
},
|
|
2639
2772
|
},
|
|
2773
|
+
forwarderSupported: {
|
|
2774
|
+
source: true,
|
|
2775
|
+
destination: true,
|
|
2776
|
+
},
|
|
2640
2777
|
},
|
|
2641
2778
|
kitContracts: {
|
|
2642
2779
|
bridge: BRIDGE_CONTRACT_EVM_TESTNET,
|
|
@@ -2675,6 +2812,10 @@ const Sonic = defineChain({
|
|
|
2675
2812
|
fastConfirmations: 1,
|
|
2676
2813
|
},
|
|
2677
2814
|
},
|
|
2815
|
+
forwarderSupported: {
|
|
2816
|
+
source: true,
|
|
2817
|
+
destination: true,
|
|
2818
|
+
},
|
|
2678
2819
|
},
|
|
2679
2820
|
kitContracts: {
|
|
2680
2821
|
bridge: BRIDGE_CONTRACT_EVM_MAINNET,
|
|
@@ -2713,6 +2854,10 @@ const SonicTestnet = defineChain({
|
|
|
2713
2854
|
fastConfirmations: 1,
|
|
2714
2855
|
},
|
|
2715
2856
|
},
|
|
2857
|
+
forwarderSupported: {
|
|
2858
|
+
source: true,
|
|
2859
|
+
destination: true,
|
|
2860
|
+
},
|
|
2716
2861
|
},
|
|
2717
2862
|
kitContracts: {
|
|
2718
2863
|
bridge: BRIDGE_CONTRACT_EVM_TESTNET,
|
|
@@ -2756,6 +2901,10 @@ const Solana = defineChain({
|
|
|
2756
2901
|
fastConfirmations: 3,
|
|
2757
2902
|
},
|
|
2758
2903
|
},
|
|
2904
|
+
forwarderSupported: {
|
|
2905
|
+
source: true,
|
|
2906
|
+
destination: false,
|
|
2907
|
+
},
|
|
2759
2908
|
},
|
|
2760
2909
|
kitContracts: {
|
|
2761
2910
|
bridge: 'DFaauJEjmiHkPs1JG89A4p95hDWi9m9SAEERY1LQJiC3',
|
|
@@ -2798,6 +2947,10 @@ const SolanaDevnet = defineChain({
|
|
|
2798
2947
|
fastConfirmations: 3,
|
|
2799
2948
|
},
|
|
2800
2949
|
},
|
|
2950
|
+
forwarderSupported: {
|
|
2951
|
+
source: true,
|
|
2952
|
+
destination: false,
|
|
2953
|
+
},
|
|
2801
2954
|
},
|
|
2802
2955
|
kitContracts: {
|
|
2803
2956
|
bridge: 'DFaauJEjmiHkPs1JG89A4p95hDWi9m9SAEERY1LQJiC3',
|
|
@@ -2881,6 +3034,10 @@ const Sui = defineChain({
|
|
|
2881
3034
|
confirmations: 1,
|
|
2882
3035
|
},
|
|
2883
3036
|
},
|
|
3037
|
+
forwarderSupported: {
|
|
3038
|
+
source: false,
|
|
3039
|
+
destination: false,
|
|
3040
|
+
},
|
|
2884
3041
|
},
|
|
2885
3042
|
});
|
|
2886
3043
|
|
|
@@ -2914,6 +3071,10 @@ const SuiTestnet = defineChain({
|
|
|
2914
3071
|
confirmations: 1,
|
|
2915
3072
|
},
|
|
2916
3073
|
},
|
|
3074
|
+
forwarderSupported: {
|
|
3075
|
+
source: false,
|
|
3076
|
+
destination: false,
|
|
3077
|
+
},
|
|
2917
3078
|
},
|
|
2918
3079
|
});
|
|
2919
3080
|
|
|
@@ -2935,7 +3096,7 @@ const Unichain = defineChain({
|
|
|
2935
3096
|
chainId: 130,
|
|
2936
3097
|
isTestnet: false,
|
|
2937
3098
|
explorerUrl: 'https://unichain.blockscout.com/tx/{hash}',
|
|
2938
|
-
rpcEndpoints: ['https://
|
|
3099
|
+
rpcEndpoints: ['https://mainnet.unichain.org'],
|
|
2939
3100
|
eurcAddress: null,
|
|
2940
3101
|
usdcAddress: '0x078D782b760474a361dDA0AF3839290b0EF57AD6',
|
|
2941
3102
|
cctp: {
|
|
@@ -2955,6 +3116,10 @@ const Unichain = defineChain({
|
|
|
2955
3116
|
fastConfirmations: 1,
|
|
2956
3117
|
},
|
|
2957
3118
|
},
|
|
3119
|
+
forwarderSupported: {
|
|
3120
|
+
source: true,
|
|
3121
|
+
destination: true,
|
|
3122
|
+
},
|
|
2958
3123
|
},
|
|
2959
3124
|
kitContracts: {
|
|
2960
3125
|
bridge: BRIDGE_CONTRACT_EVM_MAINNET,
|
|
@@ -2999,6 +3164,10 @@ const UnichainSepolia = defineChain({
|
|
|
2999
3164
|
fastConfirmations: 1,
|
|
3000
3165
|
},
|
|
3001
3166
|
},
|
|
3167
|
+
forwarderSupported: {
|
|
3168
|
+
source: true,
|
|
3169
|
+
destination: true,
|
|
3170
|
+
},
|
|
3002
3171
|
},
|
|
3003
3172
|
kitContracts: {
|
|
3004
3173
|
bridge: BRIDGE_CONTRACT_EVM_TESTNET,
|
|
@@ -3025,7 +3194,7 @@ const WorldChain = defineChain({
|
|
|
3025
3194
|
explorerUrl: 'https://worldscan.org/tx/{hash}',
|
|
3026
3195
|
rpcEndpoints: ['https://worldchain-mainnet.g.alchemy.com/public'],
|
|
3027
3196
|
eurcAddress: null,
|
|
3028
|
-
usdcAddress: '
|
|
3197
|
+
usdcAddress: '0x79A02482A880bCE3F13e09Da970dC34db4CD24d1',
|
|
3029
3198
|
cctp: {
|
|
3030
3199
|
domain: 14,
|
|
3031
3200
|
contracts: {
|
|
@@ -3037,6 +3206,10 @@ const WorldChain = defineChain({
|
|
|
3037
3206
|
fastConfirmations: 1,
|
|
3038
3207
|
},
|
|
3039
3208
|
},
|
|
3209
|
+
forwarderSupported: {
|
|
3210
|
+
source: true,
|
|
3211
|
+
destination: true,
|
|
3212
|
+
},
|
|
3040
3213
|
},
|
|
3041
3214
|
kitContracts: {
|
|
3042
3215
|
bridge: BRIDGE_CONTRACT_EVM_MAINNET,
|
|
@@ -3078,6 +3251,10 @@ const WorldChainSepolia = defineChain({
|
|
|
3078
3251
|
fastConfirmations: 1,
|
|
3079
3252
|
},
|
|
3080
3253
|
},
|
|
3254
|
+
forwarderSupported: {
|
|
3255
|
+
source: true,
|
|
3256
|
+
destination: true,
|
|
3257
|
+
},
|
|
3081
3258
|
},
|
|
3082
3259
|
kitContracts: {
|
|
3083
3260
|
bridge: BRIDGE_CONTRACT_EVM_TESTNET,
|
|
@@ -3104,7 +3281,7 @@ const XDC = defineChain({
|
|
|
3104
3281
|
chainId: 50,
|
|
3105
3282
|
isTestnet: false,
|
|
3106
3283
|
explorerUrl: 'https://xdcscan.io/tx/{hash}',
|
|
3107
|
-
rpcEndpoints: ['https://erpc.xinfin.network'],
|
|
3284
|
+
rpcEndpoints: ['https://erpc.xdcrpc.com', 'https://erpc.xinfin.network'],
|
|
3108
3285
|
eurcAddress: null,
|
|
3109
3286
|
usdcAddress: '0xfA2958CB79b0491CC627c1557F441eF849Ca8eb1',
|
|
3110
3287
|
cctp: {
|
|
@@ -3118,6 +3295,10 @@ const XDC = defineChain({
|
|
|
3118
3295
|
fastConfirmations: 3,
|
|
3119
3296
|
},
|
|
3120
3297
|
},
|
|
3298
|
+
forwarderSupported: {
|
|
3299
|
+
source: true,
|
|
3300
|
+
destination: false,
|
|
3301
|
+
},
|
|
3121
3302
|
},
|
|
3122
3303
|
kitContracts: {
|
|
3123
3304
|
bridge: BRIDGE_CONTRACT_EVM_MAINNET,
|
|
@@ -3156,6 +3337,10 @@ const XDCApothem = defineChain({
|
|
|
3156
3337
|
fastConfirmations: 1,
|
|
3157
3338
|
},
|
|
3158
3339
|
},
|
|
3340
|
+
forwarderSupported: {
|
|
3341
|
+
source: true,
|
|
3342
|
+
destination: false,
|
|
3343
|
+
},
|
|
3159
3344
|
},
|
|
3160
3345
|
kitContracts: {
|
|
3161
3346
|
bridge: BRIDGE_CONTRACT_EVM_TESTNET,
|
|
@@ -3392,7 +3577,7 @@ const nonEvmChainDefinitionSchema = baseChainDefinitionSchema
|
|
|
3392
3577
|
* })
|
|
3393
3578
|
* ```
|
|
3394
3579
|
*/
|
|
3395
|
-
const chainDefinitionSchema$
|
|
3580
|
+
const chainDefinitionSchema$2 = z.discriminatedUnion('type', [
|
|
3396
3581
|
evmChainDefinitionSchema,
|
|
3397
3582
|
nonEvmChainDefinitionSchema,
|
|
3398
3583
|
]);
|
|
@@ -3417,7 +3602,7 @@ z.union([
|
|
|
3417
3602
|
.string()
|
|
3418
3603
|
.refine((val) => val in Blockchain, 'Must be a valid Blockchain enum value as string'),
|
|
3419
3604
|
z.nativeEnum(Blockchain),
|
|
3420
|
-
chainDefinitionSchema$
|
|
3605
|
+
chainDefinitionSchema$2,
|
|
3421
3606
|
]);
|
|
3422
3607
|
/**
|
|
3423
3608
|
* Zod schema for validating bridge chain identifiers.
|
|
@@ -3453,7 +3638,7 @@ const bridgeChainIdentifierSchema = z.union([
|
|
|
3453
3638
|
z.string().refine((val) => val in BridgeChain, (val) => ({
|
|
3454
3639
|
message: `Chain "${val}" is not supported for bridging. Only chains in the BridgeChain enum support CCTPv2 bridging.`,
|
|
3455
3640
|
})),
|
|
3456
|
-
chainDefinitionSchema$
|
|
3641
|
+
chainDefinitionSchema$2.refine((chainDef) => chainDef.chain in BridgeChain, (chainDef) => ({
|
|
3457
3642
|
message: `Chain "${chainDef.name}" (${chainDef.chain}) is not supported for bridging. Only chains in the BridgeChain enum support CCTPv2 bridging.`,
|
|
3458
3643
|
})),
|
|
3459
3644
|
]);
|
|
@@ -3940,6 +4125,62 @@ function getErrorMessage(error) {
|
|
|
3940
4125
|
function getErrorCode(error) {
|
|
3941
4126
|
return isKitError(error) ? error.code : null;
|
|
3942
4127
|
}
|
|
4128
|
+
/**
|
|
4129
|
+
* Extract structured error information for logging and events.
|
|
4130
|
+
*
|
|
4131
|
+
* @remarks
|
|
4132
|
+
* Safely extracts error information from any thrown value, handling:
|
|
4133
|
+
* - KitError objects (message preserved - safe to log)
|
|
4134
|
+
* - Standard Error objects (message redacted for security)
|
|
4135
|
+
* - Plain objects with name/message/code (message redacted)
|
|
4136
|
+
* - Primitive values (logged as-is since they contain no structured data)
|
|
4137
|
+
*
|
|
4138
|
+
* For security, only KitError messages are included. Other error messages are
|
|
4139
|
+
* redacted to prevent logging sensitive data like tokens or PII.
|
|
4140
|
+
*
|
|
4141
|
+
* @param error - The error to extract info from.
|
|
4142
|
+
* @returns Structured error information.
|
|
4143
|
+
*
|
|
4144
|
+
* @example
|
|
4145
|
+
* ```typescript
|
|
4146
|
+
* import { extractErrorInfo } from '@core/errors'
|
|
4147
|
+
*
|
|
4148
|
+
* // Standard Error - message is redacted for security
|
|
4149
|
+
* const info1 = extractErrorInfo(new Error('Something went wrong'))
|
|
4150
|
+
* // { name: 'Error', message: 'An error occurred. See error name and code for details.' }
|
|
4151
|
+
*
|
|
4152
|
+
* // KitError - message is preserved (safe type)
|
|
4153
|
+
* const error = createNetworkConnectionError('Ethereum')
|
|
4154
|
+
* const info2 = extractErrorInfo(error)
|
|
4155
|
+
* // { name: 'NETWORK_CONNECTION_FAILED', message: 'Network connection failed for Ethereum', code: 3001 }
|
|
4156
|
+
*
|
|
4157
|
+
* // Primitive value - logged as-is
|
|
4158
|
+
* const info3 = extractErrorInfo('string error')
|
|
4159
|
+
* // { name: 'UnknownError', message: 'string error' }
|
|
4160
|
+
* ```
|
|
4161
|
+
*/
|
|
4162
|
+
function extractErrorInfo(error) {
|
|
4163
|
+
if (error === null || typeof error !== 'object') {
|
|
4164
|
+
return {
|
|
4165
|
+
name: 'UnknownError',
|
|
4166
|
+
message: String(error),
|
|
4167
|
+
};
|
|
4168
|
+
}
|
|
4169
|
+
const err = error;
|
|
4170
|
+
// Only preserve messages for KitError instances (safe type)
|
|
4171
|
+
// For other errors, redact message for security
|
|
4172
|
+
const errorMessage = isKitError(error)
|
|
4173
|
+
? getErrorMessage(error)
|
|
4174
|
+
: 'An error occurred. See error name and code for details.';
|
|
4175
|
+
const info = {
|
|
4176
|
+
name: err.name ?? 'UnknownError',
|
|
4177
|
+
message: errorMessage,
|
|
4178
|
+
};
|
|
4179
|
+
if (typeof err.code === 'number') {
|
|
4180
|
+
info.code = err.code;
|
|
4181
|
+
}
|
|
4182
|
+
return info;
|
|
4183
|
+
}
|
|
3943
4184
|
|
|
3944
4185
|
/**
|
|
3945
4186
|
* Validates if an address format is correct for the specified chain.
|
|
@@ -4484,7 +4725,7 @@ function validateWithStateTracking(value, schema, context, validatorName) {
|
|
|
4484
4725
|
* Zod schema for validating chain definition objects used in buildExplorerUrl.
|
|
4485
4726
|
* This schema ensures the chain definition has the required properties for URL generation.
|
|
4486
4727
|
*/
|
|
4487
|
-
const chainDefinitionSchema = z.object({
|
|
4728
|
+
const chainDefinitionSchema$1 = z.object({
|
|
4488
4729
|
name: z
|
|
4489
4730
|
.string({
|
|
4490
4731
|
required_error: 'Chain name is required',
|
|
@@ -4508,15 +4749,14 @@ const transactionHashSchema = z
|
|
|
4508
4749
|
required_error: 'Transaction hash is required',
|
|
4509
4750
|
invalid_type_error: 'Transaction hash must be a string',
|
|
4510
4751
|
})
|
|
4511
|
-
.
|
|
4512
|
-
.
|
|
4513
|
-
.refine((hash) => hash.length > 0, 'Transaction hash must not be empty or whitespace-only');
|
|
4752
|
+
.transform((hash) => hash.trim())
|
|
4753
|
+
.refine((hash) => hash.length > 0, 'Transaction hash cannot be empty');
|
|
4514
4754
|
/**
|
|
4515
4755
|
* Zod schema for validating buildExplorerUrl function parameters.
|
|
4516
4756
|
* This schema validates both the chain definition and transaction hash together.
|
|
4517
4757
|
*/
|
|
4518
4758
|
z.object({
|
|
4519
|
-
chainDef: chainDefinitionSchema,
|
|
4759
|
+
chainDef: chainDefinitionSchema$1,
|
|
4520
4760
|
txHash: transactionHashSchema,
|
|
4521
4761
|
});
|
|
4522
4762
|
/**
|
|
@@ -4527,6 +4767,69 @@ z
|
|
|
4527
4767
|
.string()
|
|
4528
4768
|
.url('Generated explorer URL is invalid');
|
|
4529
4769
|
|
|
4770
|
+
/**
|
|
4771
|
+
* Parses and validates data against a Zod schema, returning the transformed result.
|
|
4772
|
+
*
|
|
4773
|
+
* Unlike `validate` and `validateOrThrow` which use type assertions (`asserts value is T`),
|
|
4774
|
+
* this function **returns the parsed data** from Zod. This is important when your schema
|
|
4775
|
+
* includes transformations like defaults, coercion, or custom transforms.
|
|
4776
|
+
*
|
|
4777
|
+
* @typeParam T - The expected output type (caller must specify).
|
|
4778
|
+
* @param value - The value to parse and validate.
|
|
4779
|
+
* @param schema - The Zod schema to validate against.
|
|
4780
|
+
* @param context - Context string to include in error messages (e.g., 'user input', 'config').
|
|
4781
|
+
* @returns The parsed and transformed data of type T.
|
|
4782
|
+
* @throws KitError with INPUT_VALIDATION_FAILED code (1098) if validation fails.
|
|
4783
|
+
*
|
|
4784
|
+
* @remarks
|
|
4785
|
+
* This function uses `ZodTypeAny` for the schema parameter to avoid TypeScript's
|
|
4786
|
+
* "excessively deep type instantiation" error in generic contexts. The caller
|
|
4787
|
+
* must provide the expected type `T` explicitly.
|
|
4788
|
+
*
|
|
4789
|
+
* Use this instead of `validate`/`validateOrThrow` when:
|
|
4790
|
+
* - Your schema has `.default()` values
|
|
4791
|
+
* - Your schema uses `.coerce` (e.g., `z.coerce.number()`)
|
|
4792
|
+
* - Your schema has `.transform()` functions
|
|
4793
|
+
* - You need the actual parsed output, not just type narrowing
|
|
4794
|
+
*
|
|
4795
|
+
* @example
|
|
4796
|
+
* ```typescript
|
|
4797
|
+
* import { parseOrThrow } from '@core/utils'
|
|
4798
|
+
* import { z } from 'zod'
|
|
4799
|
+
*
|
|
4800
|
+
* const userSchema = z.object({
|
|
4801
|
+
* name: z.string().default('Anonymous'),
|
|
4802
|
+
* age: z.coerce.number(), // Transforms "25" → 25
|
|
4803
|
+
* })
|
|
4804
|
+
*
|
|
4805
|
+
* type User = z.infer<typeof userSchema>
|
|
4806
|
+
*
|
|
4807
|
+
* // Explicit type parameter
|
|
4808
|
+
* const user = parseOrThrow<User>({ age: '25' }, userSchema, 'user data')
|
|
4809
|
+
* console.log(user) // { name: 'Anonymous', age: 25 }
|
|
4810
|
+
* ```
|
|
4811
|
+
*
|
|
4812
|
+
* @example
|
|
4813
|
+
* ```typescript
|
|
4814
|
+
* // Usage in generic adapter functions (no deep type instantiation)
|
|
4815
|
+
* function validateInput<TInput>(
|
|
4816
|
+
* input: unknown,
|
|
4817
|
+
* schema: z.ZodTypeAny,
|
|
4818
|
+
* name: string,
|
|
4819
|
+
* ): TInput {
|
|
4820
|
+
* return parseOrThrow<TInput>(input, schema, `${name} input`)
|
|
4821
|
+
* }
|
|
4822
|
+
* ```
|
|
4823
|
+
*/
|
|
4824
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-parameters -- Intentional: T is caller-provided to avoid deep type instantiation from schema inference
|
|
4825
|
+
function parseOrThrow(value, schema, context) {
|
|
4826
|
+
const result = schema.safeParse(value);
|
|
4827
|
+
if (!result.success) {
|
|
4828
|
+
throw createValidationErrorFromZod(result.error, context);
|
|
4829
|
+
}
|
|
4830
|
+
return result.data;
|
|
4831
|
+
}
|
|
4832
|
+
|
|
4530
4833
|
/**
|
|
4531
4834
|
* A type-safe event emitter for managing action-based event subscriptions.
|
|
4532
4835
|
*
|
|
@@ -4801,7 +5104,7 @@ const parseAmount = (params) => {
|
|
|
4801
5104
|
};
|
|
4802
5105
|
|
|
4803
5106
|
var name = "@circle-fin/bridge-kit";
|
|
4804
|
-
var version = "1.
|
|
5107
|
+
var version = "1.6.0";
|
|
4805
5108
|
var pkg = {
|
|
4806
5109
|
name: name,
|
|
4807
5110
|
version: version};
|
|
@@ -5227,7 +5530,7 @@ const createDecimalStringValidator = (options) => (schema) => {
|
|
|
5227
5530
|
* console.log(result.success) // true
|
|
5228
5531
|
* ```
|
|
5229
5532
|
*/
|
|
5230
|
-
z.object({
|
|
5533
|
+
const chainDefinitionSchema = z.object({
|
|
5231
5534
|
name: z.string().min(1, 'Chain name is required'),
|
|
5232
5535
|
type: z.string().min(1, 'Chain type is required'),
|
|
5233
5536
|
});
|
|
@@ -5274,6 +5577,53 @@ const walletContextSchema = z.object({
|
|
|
5274
5577
|
isTestnet: z.boolean(),
|
|
5275
5578
|
}),
|
|
5276
5579
|
});
|
|
5580
|
+
/**
|
|
5581
|
+
* Schema for validating destination wallet contexts.
|
|
5582
|
+
* Extends walletContextSchema with optional useForwarder flag.
|
|
5583
|
+
*
|
|
5584
|
+
* @throws KitError if validation fails
|
|
5585
|
+
*/
|
|
5586
|
+
const destinationContextSchema = walletContextSchema.extend({
|
|
5587
|
+
useForwarder: z.boolean().optional(),
|
|
5588
|
+
recipientAddress: z.string().optional(),
|
|
5589
|
+
});
|
|
5590
|
+
/**
|
|
5591
|
+
* Schema for validating forwarder-only destination contexts.
|
|
5592
|
+
* Used when useForwarder is true and no adapter is provided.
|
|
5593
|
+
* Requires chain definition and recipientAddress.
|
|
5594
|
+
*
|
|
5595
|
+
* Validates that recipientAddress format is correct for the destination chain
|
|
5596
|
+
* (EVM hex address or Solana base58 address).
|
|
5597
|
+
*
|
|
5598
|
+
* @throws KitError if validation fails
|
|
5599
|
+
*/
|
|
5600
|
+
const forwarderOnlyDestinationSchema = z
|
|
5601
|
+
.object({
|
|
5602
|
+
chain: chainDefinitionSchema.extend({
|
|
5603
|
+
isTestnet: z.boolean(),
|
|
5604
|
+
}),
|
|
5605
|
+
recipientAddress: z
|
|
5606
|
+
.string()
|
|
5607
|
+
.min(1, 'Recipient address is required for forwarder-only destination'),
|
|
5608
|
+
useForwarder: z.literal(true),
|
|
5609
|
+
})
|
|
5610
|
+
.superRefine((data, ctx) => {
|
|
5611
|
+
// Pass chain name as string - isValidAddressForChain will use name-based matching
|
|
5612
|
+
if (!isValidAddressForChain(data.recipientAddress, data.chain.name)) {
|
|
5613
|
+
ctx.addIssue({
|
|
5614
|
+
code: z.ZodIssueCode.custom,
|
|
5615
|
+
message: `Invalid recipient address format for chain ${data.chain.name}`,
|
|
5616
|
+
path: ['recipientAddress'],
|
|
5617
|
+
});
|
|
5618
|
+
}
|
|
5619
|
+
});
|
|
5620
|
+
/**
|
|
5621
|
+
* Schema for validating any destination - either with adapter or forwarder-only.
|
|
5622
|
+
*/
|
|
5623
|
+
const bridgeDestinationSchema$1 = z.union([
|
|
5624
|
+
destinationContextSchema,
|
|
5625
|
+
forwarderOnlyDestinationSchema,
|
|
5626
|
+
]);
|
|
5277
5627
|
/**
|
|
5278
5628
|
* Schema for validating a custom fee configuration.
|
|
5279
5629
|
* Validates the simplified CustomFee interface which includes:
|
|
@@ -5371,7 +5721,7 @@ z.object({
|
|
|
5371
5721
|
maxDecimals: 6,
|
|
5372
5722
|
})(z.string())),
|
|
5373
5723
|
source: walletContextSchema,
|
|
5374
|
-
destination:
|
|
5724
|
+
destination: bridgeDestinationSchema$1,
|
|
5375
5725
|
token: z.literal('USDC'),
|
|
5376
5726
|
config: z.object({
|
|
5377
5727
|
transferSpeed: z.nativeEnum(TransferSpeed).optional(),
|
|
@@ -5388,6 +5738,28 @@ z.object({
|
|
|
5388
5738
|
}),
|
|
5389
5739
|
});
|
|
5390
5740
|
|
|
5741
|
+
/**
|
|
5742
|
+
* Creates a Zod superRefine validator for recipient address format validation.
|
|
5743
|
+
* Validates that the address format matches the expected format for the chain type.
|
|
5744
|
+
*
|
|
5745
|
+
* @returns A superRefine function that validates recipientAddress against chain type
|
|
5746
|
+
*/
|
|
5747
|
+
function createRecipientAddressValidator() {
|
|
5748
|
+
return (data, ctx) => {
|
|
5749
|
+
const chain = data.chain;
|
|
5750
|
+
if (chain === null) {
|
|
5751
|
+
return;
|
|
5752
|
+
}
|
|
5753
|
+
if (!isValidAddressForChain(data.recipientAddress, chain)) {
|
|
5754
|
+
const chainInfo = extractChainInfo(chain);
|
|
5755
|
+
ctx.addIssue({
|
|
5756
|
+
code: z.ZodIssueCode.custom,
|
|
5757
|
+
path: ['recipientAddress'],
|
|
5758
|
+
message: `Invalid address format for ${String(chainInfo.name)}. Expected ${chainInfo.expectedAddressFormat}, but received: ${data.recipientAddress}`,
|
|
5759
|
+
});
|
|
5760
|
+
}
|
|
5761
|
+
};
|
|
5762
|
+
}
|
|
5391
5763
|
/**
|
|
5392
5764
|
* Schema for validating AdapterContext for bridge operations.
|
|
5393
5765
|
* Must always contain both adapter and chain explicitly.
|
|
@@ -5407,32 +5779,52 @@ const adapterContextSchema = z.object({
|
|
|
5407
5779
|
const bridgeDestinationWithAddressSchema = adapterContextSchema
|
|
5408
5780
|
.extend({
|
|
5409
5781
|
recipientAddress: z.string().min(1, 'Recipient address is required'),
|
|
5782
|
+
useForwarder: z.boolean().optional(),
|
|
5410
5783
|
})
|
|
5411
|
-
.superRefine((
|
|
5412
|
-
|
|
5413
|
-
|
|
5414
|
-
|
|
5415
|
-
|
|
5416
|
-
|
|
5417
|
-
|
|
5418
|
-
ctx.addIssue({
|
|
5419
|
-
code: z.ZodIssueCode.custom,
|
|
5420
|
-
path: ['recipientAddress'],
|
|
5421
|
-
message: `Invalid address format for ${String(chainInfo.name)}. Expected ${chainInfo.expectedAddressFormat}, but received: ${data.recipientAddress}`,
|
|
5422
|
-
});
|
|
5423
|
-
}
|
|
5784
|
+
.superRefine(createRecipientAddressValidator());
|
|
5785
|
+
/**
|
|
5786
|
+
* Schema for validating AdapterContext with optional useForwarder.
|
|
5787
|
+
* Extends adapterContextSchema with the useForwarder flag.
|
|
5788
|
+
*/
|
|
5789
|
+
const adapterContextWithForwarderSchema = adapterContextSchema.extend({
|
|
5790
|
+
useForwarder: z.boolean().optional(),
|
|
5424
5791
|
});
|
|
5792
|
+
/**
|
|
5793
|
+
* Schema for validating ForwarderDestination objects.
|
|
5794
|
+
* Used when useForwarder is true and no adapter is provided.
|
|
5795
|
+
* Requires chain, recipientAddress, and useForwarder: true.
|
|
5796
|
+
*
|
|
5797
|
+
* When using this destination type:
|
|
5798
|
+
* - The mint step completes when the IRIS API confirms forwardState === 'CONFIRMED'
|
|
5799
|
+
* - No on-chain transaction confirmation is performed (no adapter available)
|
|
5800
|
+
* - The mint step's data field will be undefined (no transaction receipt)
|
|
5801
|
+
*/
|
|
5802
|
+
const forwarderDestinationSchema = z
|
|
5803
|
+
.object({
|
|
5804
|
+
chain: bridgeChainIdentifierSchema,
|
|
5805
|
+
recipientAddress: z.string().min(1, 'Recipient address is required'),
|
|
5806
|
+
useForwarder: z.literal(true),
|
|
5807
|
+
})
|
|
5808
|
+
.strict()
|
|
5809
|
+
.superRefine(createRecipientAddressValidator());
|
|
5425
5810
|
/**
|
|
5426
5811
|
* Schema for validating BridgeDestination union type.
|
|
5427
|
-
*
|
|
5812
|
+
* Supports three destination configurations:
|
|
5813
|
+
* - BridgeDestinationWithAddress (adapter with explicit recipient)
|
|
5814
|
+
* - ForwarderDestination (no adapter, requires useForwarder: true and recipientAddress)
|
|
5815
|
+
* - AdapterContext with optional useForwarder (adapter for default recipient)
|
|
5816
|
+
*
|
|
5817
|
+
* When using ForwarderDestination (no adapter):
|
|
5818
|
+
* - The mint step completes when the IRIS API confirms forwardState === 'CONFIRMED'
|
|
5819
|
+
* - No on-chain transaction confirmation is performed
|
|
5428
5820
|
*
|
|
5429
|
-
* The order matters: we check the more specific
|
|
5430
|
-
* This ensures that objects with
|
|
5431
|
-
* than silently treated as AdapterContext with the field ignored.
|
|
5821
|
+
* The order matters: we check the more specific schemas first.
|
|
5822
|
+
* This ensures that objects with specific fields are matched correctly.
|
|
5432
5823
|
*/
|
|
5433
5824
|
const bridgeDestinationSchema = z.union([
|
|
5434
5825
|
bridgeDestinationWithAddressSchema,
|
|
5435
|
-
|
|
5826
|
+
forwarderDestinationSchema,
|
|
5827
|
+
adapterContextWithForwarderSchema.strict(),
|
|
5436
5828
|
]);
|
|
5437
5829
|
/**
|
|
5438
5830
|
* Schema for validating bridge parameters with chain identifiers.
|
|
@@ -5506,121 +5898,1413 @@ const bridgeParamsWithChainIdentifierSchema = z.object({
|
|
|
5506
5898
|
});
|
|
5507
5899
|
|
|
5508
5900
|
/**
|
|
5509
|
-
*
|
|
5510
|
-
*
|
|
5511
|
-
* Both AdapterContext and BridgeDestinationWithAddress have the chain property
|
|
5512
|
-
* at the top level, so we can directly access it from either type.
|
|
5901
|
+
* Default clock implementation using `Date.now()`.
|
|
5513
5902
|
*
|
|
5514
|
-
* @
|
|
5515
|
-
*
|
|
5516
|
-
*
|
|
5903
|
+
* @remarks
|
|
5904
|
+
* Use this in production code. For testing, inject a mock clock
|
|
5905
|
+
* that returns controlled timestamps.
|
|
5517
5906
|
*
|
|
5518
5907
|
* @example
|
|
5519
5908
|
* ```typescript
|
|
5520
|
-
* import {
|
|
5521
|
-
*
|
|
5522
|
-
* // AdapterContext
|
|
5523
|
-
* const chain1 = resolveChainDefinition({
|
|
5524
|
-
* adapter: mockAdapter,
|
|
5525
|
-
* chain: 'Ethereum'
|
|
5526
|
-
* })
|
|
5909
|
+
* import { defaultClock } from '@core/runtime'
|
|
5527
5910
|
*
|
|
5528
|
-
*
|
|
5529
|
-
*
|
|
5530
|
-
*
|
|
5531
|
-
* chain: 'Base',
|
|
5532
|
-
* recipientAddress: '0x123...'
|
|
5533
|
-
* })
|
|
5911
|
+
* const start = defaultClock.now()
|
|
5912
|
+
* // ... do work ...
|
|
5913
|
+
* const elapsed = defaultClock.since(start)
|
|
5534
5914
|
* ```
|
|
5535
5915
|
*/
|
|
5536
|
-
|
|
5537
|
-
|
|
5538
|
-
|
|
5916
|
+
const defaultClock = {
|
|
5917
|
+
now: () => Date.now(),
|
|
5918
|
+
since: (start) => Date.now() - start,
|
|
5919
|
+
};
|
|
5920
|
+
|
|
5539
5921
|
/**
|
|
5540
|
-
*
|
|
5922
|
+
* ID generation utilities for distributed tracing.
|
|
5541
5923
|
*
|
|
5542
|
-
*
|
|
5543
|
-
*
|
|
5924
|
+
* @remarks
|
|
5925
|
+
* | Function | Format | Use Case |
|
|
5926
|
+
* |----------|--------|----------|
|
|
5927
|
+
* | {@link createTraceId} | 32-char hex | Standard traceId (OpenTelemetry) |
|
|
5928
|
+
* | {@link createShortOpId} | 8-char hex | Standard opId |
|
|
5929
|
+
* | {@link createOpId} | `op-{ts}-{rand}` | Timestamped operation IDs |
|
|
5930
|
+
* | {@link createUuidV4} | RFC 4122 UUID | External system compatibility |
|
|
5931
|
+
* | {@link createId} | `{prefix}-{ts}-{rand}` | Custom prefixed IDs |
|
|
5544
5932
|
*
|
|
5545
|
-
*
|
|
5546
|
-
|
|
5547
|
-
|
|
5933
|
+
* @packageDocumentation
|
|
5934
|
+
*/
|
|
5935
|
+
// ============================================================================
|
|
5936
|
+
// Crypto Utilities (Internal)
|
|
5937
|
+
// ============================================================================
|
|
5938
|
+
/** @internal */
|
|
5939
|
+
function hasGetRandomValues() {
|
|
5940
|
+
return (typeof crypto !== 'undefined' &&
|
|
5941
|
+
typeof crypto.getRandomValues === 'function');
|
|
5942
|
+
}
|
|
5943
|
+
/**
|
|
5944
|
+
* Create a W3C/OpenTelemetry-compatible trace ID.
|
|
5548
5945
|
*
|
|
5549
|
-
* @
|
|
5550
|
-
*
|
|
5946
|
+
* @returns 32-character lowercase hex string (128-bit).
|
|
5947
|
+
*
|
|
5948
|
+
* @remarks
|
|
5949
|
+
* **Standard function for generating `traceId` values.** Compatible with
|
|
5950
|
+
* OpenTelemetry, Jaeger, Zipkin, and AWS X-Ray.
|
|
5551
5951
|
*
|
|
5552
5952
|
* @example
|
|
5553
5953
|
* ```typescript
|
|
5554
|
-
* //
|
|
5555
|
-
* const addr1 = await resolveAddress({
|
|
5556
|
-
* adapter: devAdapter,
|
|
5557
|
-
* chain: 'Ethereum',
|
|
5558
|
-
* address: '0x1234567890123456789012345678901234567890'
|
|
5559
|
-
* }) // Returns: '0x1234567890123456789012345678901234567890'
|
|
5560
|
-
*
|
|
5561
|
-
* // User-controlled adapter
|
|
5562
|
-
* const addr2 = await resolveAddress({
|
|
5563
|
-
* adapter: userAdapter,
|
|
5564
|
-
* chain: 'Ethereum'
|
|
5565
|
-
* }) // Returns adapter's connected address
|
|
5954
|
+
* const traceId = createTraceId() // "a1b2c3d4e5f6789012345678abcdef00"
|
|
5566
5955
|
* ```
|
|
5567
5956
|
*/
|
|
5568
|
-
|
|
5569
|
-
|
|
5570
|
-
if (
|
|
5571
|
-
|
|
5572
|
-
if ('address' in ctx && ctx.address) {
|
|
5573
|
-
return ctx.address;
|
|
5574
|
-
}
|
|
5575
|
-
throw new Error('Address is required in context for developer-controlled adapters. ' +
|
|
5576
|
-
'Please provide: { adapter, chain, address: "0x..." }');
|
|
5957
|
+
function createTraceId() {
|
|
5958
|
+
const bytes = new Uint8Array(16);
|
|
5959
|
+
if (hasGetRandomValues()) {
|
|
5960
|
+
crypto.getRandomValues(bytes);
|
|
5577
5961
|
}
|
|
5578
5962
|
else {
|
|
5579
|
-
|
|
5580
|
-
|
|
5581
|
-
throw new Error('Address should not be provided for user-controlled adapters. ' +
|
|
5582
|
-
'The address is automatically resolved from the connected wallet.');
|
|
5963
|
+
for (let i = 0; i < 16; i++) {
|
|
5964
|
+
bytes[i] = Math.floor(Math.random() * 256); // NOSONAR:
|
|
5583
5965
|
}
|
|
5584
|
-
// Derive address from adapter
|
|
5585
|
-
const chain = resolveChainDefinition(ctx);
|
|
5586
|
-
return await ctx.adapter.getAddress(chain);
|
|
5587
5966
|
}
|
|
5967
|
+
return Array.from(bytes, (b) => b.toString(16).padStart(2, '0')).join('');
|
|
5588
5968
|
}
|
|
5969
|
+
|
|
5589
5970
|
/**
|
|
5590
|
-
*
|
|
5971
|
+
* Clean tags by removing keys with undefined values.
|
|
5591
5972
|
*
|
|
5592
|
-
*
|
|
5593
|
-
*
|
|
5594
|
-
* USDC (6 decimals) and falls back to the raw amount for other tokens.
|
|
5973
|
+
* @param tags - Tags object, may contain undefined values. Accepts `undefined` or `null`.
|
|
5974
|
+
* @returns A new object with undefined values removed, or empty object if input is nullish.
|
|
5595
5975
|
*
|
|
5596
|
-
* @
|
|
5597
|
-
*
|
|
5976
|
+
* @remarks
|
|
5977
|
+
* This helper ensures tags are safe for serialization and logging.
|
|
5978
|
+
* Similar to the internal `cleanUndefined` in the logger module, but
|
|
5979
|
+
* typed specifically for {@link Tags}.
|
|
5598
5980
|
*
|
|
5599
5981
|
* @example
|
|
5600
5982
|
* ```typescript
|
|
5601
|
-
* import {
|
|
5983
|
+
* import { cleanTags } from '@core/runtime'
|
|
5602
5984
|
*
|
|
5603
|
-
* const
|
|
5604
|
-
*
|
|
5605
|
-
*
|
|
5606
|
-
*
|
|
5607
|
-
*
|
|
5608
|
-
* }
|
|
5609
|
-
* const formattedAmount = resolveAmount(params) // Returns '1000000000000'
|
|
5985
|
+
* const tags = { chain: 'Ethereum', amount: 100, extra: undefined }
|
|
5986
|
+
* const cleaned = cleanTags(tags)
|
|
5987
|
+
* // { chain: 'Ethereum', amount: 100 }
|
|
5988
|
+
*
|
|
5989
|
+
* const empty = cleanTags(undefined)
|
|
5990
|
+
* // {}
|
|
5610
5991
|
* ```
|
|
5611
5992
|
*/
|
|
5612
|
-
function
|
|
5613
|
-
if (
|
|
5614
|
-
return
|
|
5993
|
+
function cleanTags(tags) {
|
|
5994
|
+
if (tags == null)
|
|
5995
|
+
return {};
|
|
5996
|
+
return Object.fromEntries(Object.entries(tags).filter(([, value]) => value !== undefined));
|
|
5997
|
+
}
|
|
5998
|
+
|
|
5999
|
+
/**
|
|
6000
|
+
* Check if a pattern is valid.
|
|
6001
|
+
*
|
|
6002
|
+
* @remarks
|
|
6003
|
+
* Invalid patterns:
|
|
6004
|
+
* - Empty segments (e.g., `a..b`, `.a`, `a.`)
|
|
6005
|
+
* - `**` not at end (e.g., `a.**.b`)
|
|
6006
|
+
*
|
|
6007
|
+
* @param pattern - The pattern to validate.
|
|
6008
|
+
* @returns True if valid, false otherwise.
|
|
6009
|
+
*/
|
|
6010
|
+
function isValidPattern(pattern) {
|
|
6011
|
+
// Empty pattern is valid (matches empty topic)
|
|
6012
|
+
if (pattern === '')
|
|
6013
|
+
return true;
|
|
6014
|
+
const segments = pattern.split('.');
|
|
6015
|
+
// Check for empty segments
|
|
6016
|
+
if (segments.includes(''))
|
|
6017
|
+
return false;
|
|
6018
|
+
// Check that ** only appears at the end
|
|
6019
|
+
const doubleStarIndex = segments.indexOf('**');
|
|
6020
|
+
if (doubleStarIndex !== -1 && doubleStarIndex !== segments.length - 1) {
|
|
6021
|
+
return false;
|
|
5615
6022
|
}
|
|
5616
|
-
return
|
|
6023
|
+
return true;
|
|
5617
6024
|
}
|
|
5618
6025
|
/**
|
|
5619
|
-
*
|
|
6026
|
+
* Convert a pattern to a regular expression.
|
|
6027
|
+
*
|
|
6028
|
+
* @param pattern - The pattern to convert.
|
|
6029
|
+
* @returns A RegExp that matches topics according to the pattern.
|
|
6030
|
+
*/
|
|
6031
|
+
function patternToRegex(pattern) {
|
|
6032
|
+
const segments = pattern.split('.');
|
|
6033
|
+
// Handle ** at end specially to match zero or more segments
|
|
6034
|
+
const lastSegment = segments.at(-1);
|
|
6035
|
+
if (lastSegment === '**') {
|
|
6036
|
+
// Everything before ** must match, then optionally more segments
|
|
6037
|
+
const prefix = segments
|
|
6038
|
+
.slice(0, -1)
|
|
6039
|
+
.map((seg) => {
|
|
6040
|
+
if (seg === '*')
|
|
6041
|
+
return '[^.]+';
|
|
6042
|
+
return seg.replaceAll(/[.*+?^${}()|[\]\\]/g, String.raw `\$&`);
|
|
6043
|
+
})
|
|
6044
|
+
.join(String.raw `\.`);
|
|
6045
|
+
// ** matches zero or more segments:
|
|
6046
|
+
// - If prefix is empty (**), match anything
|
|
6047
|
+
// - Otherwise, match prefix, then optionally (dot + more content)
|
|
6048
|
+
if (prefix === '') {
|
|
6049
|
+
return /^.*$/;
|
|
6050
|
+
}
|
|
6051
|
+
return new RegExp(String.raw `^${prefix}(?:\..*)?$`);
|
|
6052
|
+
}
|
|
6053
|
+
// No ** - just convert segments normally
|
|
6054
|
+
const escaped = segments
|
|
6055
|
+
.map((segment) => {
|
|
6056
|
+
if (segment === '*')
|
|
6057
|
+
return '[^.]+';
|
|
6058
|
+
return segment.replaceAll(/[.*+?^${}()|[\]\\]/g, String.raw `\$&`);
|
|
6059
|
+
})
|
|
6060
|
+
.join(String.raw `\.`);
|
|
6061
|
+
return new RegExp(String.raw `^${escaped}$`);
|
|
6062
|
+
}
|
|
6063
|
+
/**
|
|
6064
|
+
* Execute an event handler with error isolation.
|
|
5620
6065
|
*
|
|
5621
|
-
*
|
|
5622
|
-
*
|
|
5623
|
-
*
|
|
6066
|
+
* @param handler - The handler function to execute.
|
|
6067
|
+
* @param event - The event to pass to the handler.
|
|
6068
|
+
* @param logger - Optional logger for error reporting.
|
|
6069
|
+
*
|
|
6070
|
+
* @remarks
|
|
6071
|
+
* - Synchronous errors are caught and logged
|
|
6072
|
+
* - Promise rejections are caught without awaiting
|
|
6073
|
+
* - Handler errors never propagate to caller
|
|
6074
|
+
*/
|
|
6075
|
+
function executeHandler(handler, event, logger) {
|
|
6076
|
+
try {
|
|
6077
|
+
const result = handler(event);
|
|
6078
|
+
// Handle promise rejection without awaiting
|
|
6079
|
+
if (result instanceof Promise) {
|
|
6080
|
+
result.catch((err) => {
|
|
6081
|
+
logger?.error('Event handler promise rejected', {
|
|
6082
|
+
event: event.name,
|
|
6083
|
+
error: extractErrorInfo(err),
|
|
6084
|
+
});
|
|
6085
|
+
});
|
|
6086
|
+
}
|
|
6087
|
+
}
|
|
6088
|
+
catch (err) {
|
|
6089
|
+
// Isolate handler errors
|
|
6090
|
+
logger?.error('Event handler threw', {
|
|
6091
|
+
event: event.name,
|
|
6092
|
+
error: extractErrorInfo(err),
|
|
6093
|
+
});
|
|
6094
|
+
}
|
|
6095
|
+
}
|
|
6096
|
+
/**
|
|
6097
|
+
* Create an event bus.
|
|
6098
|
+
*
|
|
6099
|
+
* @param opts - Options for the event bus.
|
|
6100
|
+
* @returns An EventBus instance.
|
|
6101
|
+
*
|
|
6102
|
+
* @example
|
|
6103
|
+
* ```typescript
|
|
6104
|
+
* import { createEventBus } from '@core/runtime'
|
|
6105
|
+
*
|
|
6106
|
+
* const bus = createEventBus({ logger })
|
|
6107
|
+
*
|
|
6108
|
+
* // Subscribe to events
|
|
6109
|
+
* const unsubscribe = bus.on('tx.*', (event) => {
|
|
6110
|
+
* console.log('Transaction event:', event.name)
|
|
6111
|
+
* })
|
|
6112
|
+
*
|
|
6113
|
+
* // Emit events
|
|
6114
|
+
* bus.emit({ name: 'tx.sent', data: { txHash: '0x123' } })
|
|
6115
|
+
*
|
|
6116
|
+
* // Unsubscribe
|
|
6117
|
+
* unsubscribe()
|
|
6118
|
+
* ```
|
|
6119
|
+
*/
|
|
6120
|
+
function createEventBus(opts) {
|
|
6121
|
+
const logger = opts?.logger;
|
|
6122
|
+
const subscriptions = new Set();
|
|
6123
|
+
function createBus(baseTags) {
|
|
6124
|
+
return {
|
|
6125
|
+
emit(event) {
|
|
6126
|
+
// Invalid topic names silently don't match any subscriptions
|
|
6127
|
+
if (!isValidPattern(event.name))
|
|
6128
|
+
return;
|
|
6129
|
+
const mergedTags = Object.keys(baseTags).length > 0 || event.tags
|
|
6130
|
+
? { ...baseTags, ...cleanTags(event.tags) }
|
|
6131
|
+
: undefined;
|
|
6132
|
+
const finalEvent = mergedTags === undefined ? event : { ...event, tags: mergedTags };
|
|
6133
|
+
// Notify all matching subscribers
|
|
6134
|
+
for (const sub of subscriptions) {
|
|
6135
|
+
// Match-all subscription
|
|
6136
|
+
if (sub.pattern === null) {
|
|
6137
|
+
executeHandler(sub.handler, finalEvent, logger);
|
|
6138
|
+
continue;
|
|
6139
|
+
}
|
|
6140
|
+
// Patterned subscription: invalid pattern => regex=null => never match
|
|
6141
|
+
if (sub.regex === null)
|
|
6142
|
+
continue;
|
|
6143
|
+
if (!sub.regex.test(event.name))
|
|
6144
|
+
continue;
|
|
6145
|
+
executeHandler(sub.handler, finalEvent, logger);
|
|
6146
|
+
}
|
|
6147
|
+
},
|
|
6148
|
+
child(tags) {
|
|
6149
|
+
// Merge and clean tags, don't mutate parent
|
|
6150
|
+
const childTags = { ...baseTags, ...cleanTags(tags) };
|
|
6151
|
+
return createBus(childTags);
|
|
6152
|
+
},
|
|
6153
|
+
on(patternOrHandler, handler) {
|
|
6154
|
+
let sub;
|
|
6155
|
+
if (typeof patternOrHandler === 'function') {
|
|
6156
|
+
// on(handler) - subscribe to all
|
|
6157
|
+
sub = { pattern: null, handler: patternOrHandler, regex: null };
|
|
6158
|
+
}
|
|
6159
|
+
else {
|
|
6160
|
+
// on(pattern, handler)
|
|
6161
|
+
if (typeof handler !== 'function') {
|
|
6162
|
+
throw new TypeError(`EventBus.on("${patternOrHandler}") expected a function handler`);
|
|
6163
|
+
}
|
|
6164
|
+
const regex = isValidPattern(patternOrHandler)
|
|
6165
|
+
? patternToRegex(patternOrHandler)
|
|
6166
|
+
: null;
|
|
6167
|
+
sub = {
|
|
6168
|
+
pattern: patternOrHandler,
|
|
6169
|
+
handler,
|
|
6170
|
+
regex,
|
|
6171
|
+
};
|
|
6172
|
+
}
|
|
6173
|
+
subscriptions.add(sub);
|
|
6174
|
+
// Return unsubscribe function (idempotent)
|
|
6175
|
+
let unsubscribed = false;
|
|
6176
|
+
return () => {
|
|
6177
|
+
if (!unsubscribed) {
|
|
6178
|
+
subscriptions.delete(sub);
|
|
6179
|
+
unsubscribed = true;
|
|
6180
|
+
}
|
|
6181
|
+
};
|
|
6182
|
+
},
|
|
6183
|
+
};
|
|
6184
|
+
}
|
|
6185
|
+
return createBus({});
|
|
6186
|
+
}
|
|
6187
|
+
|
|
6188
|
+
/** No-op counter that discards all increments. */
|
|
6189
|
+
const noopCounter = {
|
|
6190
|
+
inc: () => {
|
|
6191
|
+
// Intentionally empty - discards increment
|
|
6192
|
+
},
|
|
6193
|
+
};
|
|
6194
|
+
/** No-op histogram that discards all observations. */
|
|
6195
|
+
const noopHistogram = {
|
|
6196
|
+
observe: () => {
|
|
6197
|
+
// Intentionally empty - discards observation
|
|
6198
|
+
},
|
|
6199
|
+
};
|
|
6200
|
+
/** No-op timer that returns an empty stop function. */
|
|
6201
|
+
const noopTimer = {
|
|
6202
|
+
start: () => () => {
|
|
6203
|
+
// Intentionally empty - discards timing
|
|
6204
|
+
},
|
|
6205
|
+
};
|
|
6206
|
+
/**
|
|
6207
|
+
* Create a no-op metrics instance.
|
|
6208
|
+
*
|
|
6209
|
+
* @returns A Metrics instance that discards all operations.
|
|
6210
|
+
*/
|
|
6211
|
+
function createNoopMetrics() {
|
|
6212
|
+
// Self-referential instance - child() returns same object to avoid allocations
|
|
6213
|
+
const instance = {
|
|
6214
|
+
counter: () => noopCounter,
|
|
6215
|
+
histogram: () => noopHistogram,
|
|
6216
|
+
timer: () => noopTimer,
|
|
6217
|
+
child: () => instance,
|
|
6218
|
+
};
|
|
6219
|
+
return instance;
|
|
6220
|
+
}
|
|
6221
|
+
/**
|
|
6222
|
+
* A no-op metrics implementation that discards all operations.
|
|
6223
|
+
*
|
|
6224
|
+
* @remarks
|
|
6225
|
+
* Use this when metrics collection is disabled or not configured.
|
|
6226
|
+
* All metric operations are safe to call and return immediately.
|
|
6227
|
+
* The `child()` method returns the same instance to avoid allocations.
|
|
6228
|
+
*
|
|
6229
|
+
* @example
|
|
6230
|
+
* ```typescript
|
|
6231
|
+
* import { noopMetrics } from '@core/runtime'
|
|
6232
|
+
*
|
|
6233
|
+
* // Use when metrics are disabled
|
|
6234
|
+
* const metrics = config.metricsEnabled
|
|
6235
|
+
* ? createDatadogMetrics(config)
|
|
6236
|
+
* : noopMetrics
|
|
6237
|
+
*
|
|
6238
|
+
* // All operations are safe to call
|
|
6239
|
+
* metrics.counter('requests').inc()
|
|
6240
|
+
* const stop = metrics.timer('query').start()
|
|
6241
|
+
* stop()
|
|
6242
|
+
* ```
|
|
6243
|
+
*/
|
|
6244
|
+
const noopMetrics = createNoopMetrics();
|
|
6245
|
+
|
|
6246
|
+
/**
|
|
6247
|
+
* Define a schema for runtime logger interfaces.
|
|
6248
|
+
*
|
|
6249
|
+
* @remarks
|
|
6250
|
+
* Validate that a runtime logger provides the minimal methods expected by the SDK.
|
|
6251
|
+
*
|
|
6252
|
+
* @example
|
|
6253
|
+
* ```typescript
|
|
6254
|
+
* import { loggerSchema } from '@core/runtime'
|
|
6255
|
+
*
|
|
6256
|
+
* const logger = {
|
|
6257
|
+
* debug: () => undefined,
|
|
6258
|
+
* info: () => undefined,
|
|
6259
|
+
* warn: () => undefined,
|
|
6260
|
+
* error: () => undefined,
|
|
6261
|
+
* child: () => logger,
|
|
6262
|
+
* }
|
|
6263
|
+
*
|
|
6264
|
+
* loggerSchema.parse(logger)
|
|
6265
|
+
* ```
|
|
6266
|
+
*/
|
|
6267
|
+
const loggerSchema = z.custom((value) => {
|
|
6268
|
+
if (value === null || typeof value !== 'object') {
|
|
6269
|
+
return false;
|
|
6270
|
+
}
|
|
6271
|
+
const record = value;
|
|
6272
|
+
return (typeof record['debug'] === 'function' &&
|
|
6273
|
+
typeof record['info'] === 'function' &&
|
|
6274
|
+
typeof record['warn'] === 'function' &&
|
|
6275
|
+
typeof record['error'] === 'function' &&
|
|
6276
|
+
typeof record['child'] === 'function');
|
|
6277
|
+
}, {
|
|
6278
|
+
message: 'Invalid logger',
|
|
6279
|
+
});
|
|
6280
|
+
|
|
6281
|
+
/**
|
|
6282
|
+
* Define a schema for runtime metrics interfaces.
|
|
6283
|
+
*
|
|
6284
|
+
* @remarks
|
|
6285
|
+
* Validate that a metrics implementation exposes the minimal API expected by the runtime.
|
|
6286
|
+
*
|
|
6287
|
+
* @example
|
|
6288
|
+
* ```typescript
|
|
6289
|
+
* import { metricsSchema } from '@core/runtime'
|
|
6290
|
+
*
|
|
6291
|
+
* const metrics = {
|
|
6292
|
+
* counter: () => ({ inc: () => undefined }),
|
|
6293
|
+
* histogram: () => ({ observe: () => undefined }),
|
|
6294
|
+
* timer: () => ({ start: () => () => undefined }),
|
|
6295
|
+
* child: () => metrics,
|
|
6296
|
+
* }
|
|
6297
|
+
*
|
|
6298
|
+
* metricsSchema.parse(metrics)
|
|
6299
|
+
* ```
|
|
6300
|
+
*/
|
|
6301
|
+
const metricsSchema = z.custom((value) => {
|
|
6302
|
+
if (value === null || typeof value !== 'object') {
|
|
6303
|
+
return false;
|
|
6304
|
+
}
|
|
6305
|
+
const record = value;
|
|
6306
|
+
return (typeof record['counter'] === 'function' &&
|
|
6307
|
+
typeof record['histogram'] === 'function' &&
|
|
6308
|
+
typeof record['timer'] === 'function' &&
|
|
6309
|
+
typeof record['child'] === 'function');
|
|
6310
|
+
}, {
|
|
6311
|
+
message: 'Invalid metrics',
|
|
6312
|
+
});
|
|
6313
|
+
|
|
6314
|
+
/**
|
|
6315
|
+
* Omit undefined values from an object.
|
|
6316
|
+
*
|
|
6317
|
+
* @param obj - The object to process.
|
|
6318
|
+
* @returns A new object with undefined values removed.
|
|
6319
|
+
*
|
|
6320
|
+
* @internal
|
|
6321
|
+
* @remarks
|
|
6322
|
+
* Used by both production and mock loggers to ensure consistent behavior.
|
|
6323
|
+
* This prevents undefined values from being serialized in log output,
|
|
6324
|
+
* which can cause issues with some log transports.
|
|
6325
|
+
*/
|
|
6326
|
+
function omitUndefined(obj) {
|
|
6327
|
+
const result = {};
|
|
6328
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
6329
|
+
if (value !== undefined) {
|
|
6330
|
+
result[key] = value;
|
|
6331
|
+
}
|
|
6332
|
+
}
|
|
6333
|
+
return result;
|
|
6334
|
+
}
|
|
6335
|
+
|
|
6336
|
+
/**
|
|
6337
|
+
* Default redaction paths for web3/blockchain SDKs.
|
|
6338
|
+
*
|
|
6339
|
+
* @remarks
|
|
6340
|
+
* These paths target common sensitive fields in blockchain applications.
|
|
6341
|
+
* All user fields are nested under `context`, so paths start with `context.`.
|
|
6342
|
+
* Wildcard `*` matches any key at that level.
|
|
6343
|
+
*/
|
|
6344
|
+
const DEFAULT_REDACT_PATHS = [
|
|
6345
|
+
// Generic Credentials
|
|
6346
|
+
'context.password',
|
|
6347
|
+
'context.passphrase',
|
|
6348
|
+
'context.secret',
|
|
6349
|
+
'context.token',
|
|
6350
|
+
'context.*.password',
|
|
6351
|
+
'context.*.passphrase',
|
|
6352
|
+
'context.*.secret',
|
|
6353
|
+
'context.*.token',
|
|
6354
|
+
// API Keys & Auth Tokens
|
|
6355
|
+
'context.apiKey',
|
|
6356
|
+
'context.apiSecret',
|
|
6357
|
+
'context.accessToken',
|
|
6358
|
+
'context.refreshToken',
|
|
6359
|
+
'context.jwt',
|
|
6360
|
+
'context.bearerToken',
|
|
6361
|
+
'context.sessionId',
|
|
6362
|
+
'context.authorization',
|
|
6363
|
+
'context.cookie',
|
|
6364
|
+
'context.*.apiKey',
|
|
6365
|
+
'context.*.apiSecret',
|
|
6366
|
+
'context.*.accessToken',
|
|
6367
|
+
'context.*.refreshToken',
|
|
6368
|
+
'context.*.jwt',
|
|
6369
|
+
'context.*.bearerToken',
|
|
6370
|
+
'context.*.sessionId',
|
|
6371
|
+
'context.*.authorization',
|
|
6372
|
+
'context.*.cookie',
|
|
6373
|
+
// Web3 / Crypto Keys
|
|
6374
|
+
'context.privateKey',
|
|
6375
|
+
'context.secretKey',
|
|
6376
|
+
'context.signingKey',
|
|
6377
|
+
'context.encryptionKey',
|
|
6378
|
+
'context.*.privateKey',
|
|
6379
|
+
'context.*.secretKey',
|
|
6380
|
+
'context.*.signingKey',
|
|
6381
|
+
'context.*.encryptionKey',
|
|
6382
|
+
// Web3 / Crypto Mnemonics and Seeds
|
|
6383
|
+
'context.mnemonic',
|
|
6384
|
+
'context.seed',
|
|
6385
|
+
'context.seedPhrase',
|
|
6386
|
+
'context.*.mnemonic',
|
|
6387
|
+
'context.*.seed',
|
|
6388
|
+
'context.*.seedPhrase',
|
|
6389
|
+
// OTP / Verification Codes
|
|
6390
|
+
'context.otp',
|
|
6391
|
+
'context.verificationCode',
|
|
6392
|
+
'context.*.otp',
|
|
6393
|
+
'context.*.verificationCode',
|
|
6394
|
+
// Payment Information
|
|
6395
|
+
'context.cardNumber',
|
|
6396
|
+
'context.cvv',
|
|
6397
|
+
'context.accountNumber',
|
|
6398
|
+
'context.*.cardNumber',
|
|
6399
|
+
'context.*.cvv',
|
|
6400
|
+
'context.*.accountNumber',
|
|
6401
|
+
];
|
|
6402
|
+
/**
|
|
6403
|
+
* Wrap user fields under `context` to prevent collision with pino internals.
|
|
6404
|
+
*
|
|
6405
|
+
* @param fields - User-provided log fields.
|
|
6406
|
+
* @returns Object with fields nested under `context`, or undefined if empty.
|
|
6407
|
+
*
|
|
6408
|
+
* @remarks
|
|
6409
|
+
* This function handles edge cases by returning undefined for null, undefined,
|
|
6410
|
+
* or empty objects to avoid unnecessary wrapping in log output.
|
|
6411
|
+
* Undefined values are cleaned before wrapping.
|
|
6412
|
+
*/
|
|
6413
|
+
function wrapInContext(fields) {
|
|
6414
|
+
if (!fields)
|
|
6415
|
+
return undefined;
|
|
6416
|
+
// Clean undefined values for consistency and transport compatibility
|
|
6417
|
+
const cleaned = omitUndefined(fields);
|
|
6418
|
+
// Handle edge case: all values were undefined, resulting in empty object
|
|
6419
|
+
const keys = Object.keys(cleaned);
|
|
6420
|
+
if (keys.length === 0)
|
|
6421
|
+
return undefined;
|
|
6422
|
+
return { context: cleaned };
|
|
6423
|
+
}
|
|
6424
|
+
/**
|
|
6425
|
+
* Wrap a pino instance to conform to our Logger interface.
|
|
6426
|
+
*
|
|
6427
|
+
* @param pinoInstance - The pino logger instance to wrap.
|
|
6428
|
+
* @returns A Logger instance conforming to our stable interface.
|
|
6429
|
+
*/
|
|
6430
|
+
function wrapPino(pinoInstance) {
|
|
6431
|
+
return {
|
|
6432
|
+
debug(message, fields) {
|
|
6433
|
+
const wrapped = wrapInContext(fields);
|
|
6434
|
+
if (wrapped) {
|
|
6435
|
+
pinoInstance.debug(wrapped, message);
|
|
6436
|
+
}
|
|
6437
|
+
else {
|
|
6438
|
+
pinoInstance.debug(message);
|
|
6439
|
+
}
|
|
6440
|
+
},
|
|
6441
|
+
info(message, fields) {
|
|
6442
|
+
const wrapped = wrapInContext(fields);
|
|
6443
|
+
if (wrapped) {
|
|
6444
|
+
pinoInstance.info(wrapped, message);
|
|
6445
|
+
}
|
|
6446
|
+
else {
|
|
6447
|
+
pinoInstance.info(message);
|
|
6448
|
+
}
|
|
6449
|
+
},
|
|
6450
|
+
warn(message, fields) {
|
|
6451
|
+
const wrapped = wrapInContext(fields);
|
|
6452
|
+
if (wrapped) {
|
|
6453
|
+
pinoInstance.warn(wrapped, message);
|
|
6454
|
+
}
|
|
6455
|
+
else {
|
|
6456
|
+
pinoInstance.warn(message);
|
|
6457
|
+
}
|
|
6458
|
+
},
|
|
6459
|
+
error(message, fields) {
|
|
6460
|
+
const wrapped = wrapInContext(fields);
|
|
6461
|
+
if (wrapped) {
|
|
6462
|
+
pinoInstance.error(wrapped, message);
|
|
6463
|
+
}
|
|
6464
|
+
else {
|
|
6465
|
+
pinoInstance.error(message);
|
|
6466
|
+
}
|
|
6467
|
+
},
|
|
6468
|
+
child(tags) {
|
|
6469
|
+
// Child bindings stay flat (not wrapped) - they're part of logger's base context
|
|
6470
|
+
const cleaned = omitUndefined(tags);
|
|
6471
|
+
return wrapPino(pinoInstance.child(cleaned));
|
|
6472
|
+
},
|
|
6473
|
+
};
|
|
6474
|
+
}
|
|
6475
|
+
/**
|
|
6476
|
+
* Build pino redact configuration from our simplified options.
|
|
6477
|
+
*
|
|
6478
|
+
* @param redact - The redact configuration option.
|
|
6479
|
+
* @returns Pino-compatible redact configuration or undefined.
|
|
6480
|
+
*/
|
|
6481
|
+
function buildRedactConfig(redact) {
|
|
6482
|
+
// Explicitly disabled
|
|
6483
|
+
if (redact === false) {
|
|
6484
|
+
return undefined;
|
|
6485
|
+
}
|
|
6486
|
+
// Custom paths provided
|
|
6487
|
+
if (Array.isArray(redact)) {
|
|
6488
|
+
return redact.length > 0
|
|
6489
|
+
? { paths: redact, censor: '[REDACTED]' }
|
|
6490
|
+
: undefined;
|
|
6491
|
+
}
|
|
6492
|
+
// Default: use web3 sensible defaults
|
|
6493
|
+
return {
|
|
6494
|
+
paths: [...DEFAULT_REDACT_PATHS],
|
|
6495
|
+
censor: '[REDACTED]',
|
|
6496
|
+
};
|
|
6497
|
+
}
|
|
6498
|
+
/**
|
|
6499
|
+
* Create a logger backed by pino.
|
|
6500
|
+
*
|
|
6501
|
+
* @param options - Logger options (optional).
|
|
6502
|
+
* @param stream - Destination stream (optional).
|
|
6503
|
+
* @returns A Logger instance.
|
|
6504
|
+
* @throws Error if invalid pino options are provided.
|
|
6505
|
+
*
|
|
6506
|
+
* @remarks
|
|
6507
|
+
* This is a thin wrapper around pino that exposes our stable Logger interface.
|
|
6508
|
+
* Pino handles all transport concerns: JSON, pretty printing, file, remote, browser, etc.
|
|
6509
|
+
*
|
|
6510
|
+
* **Security**: By default, sensitive web3 fields (privateKey, mnemonic, apiKey, etc.)
|
|
6511
|
+
* are automatically redacted from log output. Use `redact: false` to disable.
|
|
6512
|
+
*
|
|
6513
|
+
* @example
|
|
6514
|
+
* ```typescript
|
|
6515
|
+
* import { createLogger } from '@core/runtime'
|
|
6516
|
+
*
|
|
6517
|
+
* // Default: web3 sensitive fields are redacted
|
|
6518
|
+
* const logger = createLogger({ level: 'info' })
|
|
6519
|
+
* logger.info('Signing', { privateKey: '0x123...' })
|
|
6520
|
+
* // Output: { context: { privateKey: '[REDACTED]' }, msg: 'Signing' }
|
|
6521
|
+
*
|
|
6522
|
+
* // Disable redaction (use with caution)
|
|
6523
|
+
* const unsafeLogger = createLogger({ level: 'debug', redact: false })
|
|
6524
|
+
*
|
|
6525
|
+
* // Custom redaction paths
|
|
6526
|
+
* const customLogger = createLogger({
|
|
6527
|
+
* level: 'info',
|
|
6528
|
+
* redact: ['context.mySecret', 'context.*.credentials']
|
|
6529
|
+
* })
|
|
6530
|
+
*
|
|
6531
|
+
* // Pretty output for development
|
|
6532
|
+
* const devLogger = createLogger({
|
|
6533
|
+
* level: 'debug',
|
|
6534
|
+
* transport: { target: 'pino-pretty' }
|
|
6535
|
+
* })
|
|
6536
|
+
*
|
|
6537
|
+
* // Browser logger
|
|
6538
|
+
* const browserLogger = createLogger({
|
|
6539
|
+
* browser: { asObject: true }
|
|
6540
|
+
* })
|
|
6541
|
+
* ```
|
|
6542
|
+
*/
|
|
6543
|
+
function createLogger(options, stream) {
|
|
6544
|
+
const { redact, ...pinoOptions } = {};
|
|
6545
|
+
// Build redaction config
|
|
6546
|
+
const redactConfig = buildRedactConfig(redact);
|
|
6547
|
+
// Build final pino options, only include redact if defined
|
|
6548
|
+
const finalOptions = redactConfig
|
|
6549
|
+
? { ...pinoOptions, redact: redactConfig }
|
|
6550
|
+
: pinoOptions;
|
|
6551
|
+
const pinoInstance = pino(finalOptions);
|
|
6552
|
+
return wrapPino(pinoInstance);
|
|
6553
|
+
}
|
|
6554
|
+
|
|
6555
|
+
/**
|
|
6556
|
+
* Factory for creating Runtime instances with defaults.
|
|
6557
|
+
*
|
|
6558
|
+
* @packageDocumentation
|
|
6559
|
+
*/
|
|
6560
|
+
// ============================================================================
|
|
6561
|
+
// Validation Schema
|
|
6562
|
+
// ============================================================================
|
|
6563
|
+
/**
|
|
6564
|
+
* Schema for validating {@link RuntimeOptions}.
|
|
6565
|
+
*
|
|
6566
|
+
* @remarks
|
|
6567
|
+
* Used internally by {@link createRuntime} to validate options from JS consumers.
|
|
6568
|
+
* Exported for advanced use cases where manual validation is needed.
|
|
6569
|
+
*/
|
|
6570
|
+
const runtimeOptionsSchema = z
|
|
6571
|
+
.object({
|
|
6572
|
+
logger: loggerSchema.optional(),
|
|
6573
|
+
metrics: metricsSchema.optional(),
|
|
6574
|
+
})
|
|
6575
|
+
.passthrough();
|
|
6576
|
+
// ============================================================================
|
|
6577
|
+
// Factory
|
|
6578
|
+
// ============================================================================
|
|
6579
|
+
/**
|
|
6580
|
+
* Create a complete Runtime with sensible defaults.
|
|
6581
|
+
*
|
|
6582
|
+
* @param options - Optional configuration to override logger and metrics.
|
|
6583
|
+
* @returns A frozen, immutable Runtime with all services guaranteed present.
|
|
6584
|
+
* @throws KitError (INPUT_VALIDATION_FAILED) if options contain invalid services.
|
|
6585
|
+
*
|
|
6586
|
+
* @remarks
|
|
6587
|
+
* Creates a fully-configured runtime by merging provided options with defaults.
|
|
6588
|
+
* The returned runtime is frozen to enforce immutability.
|
|
6589
|
+
*
|
|
6590
|
+
* | Service | Default | Configurable |
|
|
6591
|
+
* |---------|---------|--------------|
|
|
6592
|
+
* | `logger` | pino logger (info level) | Yes |
|
|
6593
|
+
* | `metrics` | No-op metrics | Yes |
|
|
6594
|
+
* | `events` | Internal event bus | No |
|
|
6595
|
+
* | `clock` | `Date.now()` | No |
|
|
6596
|
+
*
|
|
6597
|
+
* **Why only logger and metrics?**
|
|
6598
|
+
*
|
|
6599
|
+
* - **Logger/Metrics**: Integration points with your infrastructure
|
|
6600
|
+
* - **Events**: Internal pub/sub mechanism - subscribe via `runtime.events.on()`
|
|
6601
|
+
* - **Clock**: Testing concern - use mock factories for tests
|
|
6602
|
+
*
|
|
6603
|
+
* @example
|
|
6604
|
+
* ```typescript
|
|
6605
|
+
* import { createRuntime, createLogger } from '@core/runtime'
|
|
6606
|
+
*
|
|
6607
|
+
* // Use all defaults
|
|
6608
|
+
* const runtime = createRuntime()
|
|
6609
|
+
*
|
|
6610
|
+
* // Custom logger
|
|
6611
|
+
* const runtime = createRuntime({
|
|
6612
|
+
* logger: createLogger({ level: 'debug' }),
|
|
6613
|
+
* })
|
|
6614
|
+
*
|
|
6615
|
+
* // Custom metrics (e.g., Prometheus)
|
|
6616
|
+
* const runtime = createRuntime({
|
|
6617
|
+
* metrics: myPrometheusMetrics,
|
|
6618
|
+
* })
|
|
6619
|
+
*
|
|
6620
|
+
* // Subscribe to events (don't replace the bus)
|
|
6621
|
+
* runtime.events.on('operation.*', (event) => {
|
|
6622
|
+
* console.log('Event:', event.name)
|
|
6623
|
+
* })
|
|
6624
|
+
* ```
|
|
6625
|
+
*/
|
|
6626
|
+
function createRuntime(options) {
|
|
6627
|
+
// Validate options for JS consumers
|
|
6628
|
+
if (options != null) {
|
|
6629
|
+
parseOrThrow(options, runtimeOptionsSchema, 'runtime options');
|
|
6630
|
+
}
|
|
6631
|
+
// Resolve logger first (events may need it)
|
|
6632
|
+
const logger = options?.logger ?? createLogger();
|
|
6633
|
+
// Internal services - not configurable
|
|
6634
|
+
const events = createEventBus({ logger });
|
|
6635
|
+
const clock = defaultClock;
|
|
6636
|
+
// Resolve metrics
|
|
6637
|
+
const metrics = options?.metrics ?? noopMetrics;
|
|
6638
|
+
return Object.freeze({ logger, events, metrics, clock });
|
|
6639
|
+
}
|
|
6640
|
+
|
|
6641
|
+
/**
|
|
6642
|
+
* Invocation context resolution - resolves the **WHO/HOW** of an operation.
|
|
6643
|
+
*
|
|
6644
|
+
* @packageDocumentation
|
|
6645
|
+
*/
|
|
6646
|
+
// ============================================================================
|
|
6647
|
+
// Validation Schemas
|
|
6648
|
+
// ============================================================================
|
|
6649
|
+
/**
|
|
6650
|
+
* Schema for validating Caller.
|
|
6651
|
+
*/
|
|
6652
|
+
const callerSchema = z.object({
|
|
6653
|
+
type: z.string(),
|
|
6654
|
+
name: z.string(),
|
|
6655
|
+
version: z.string().optional(),
|
|
6656
|
+
});
|
|
6657
|
+
/**
|
|
6658
|
+
* Schema for validating InvocationMeta input.
|
|
6659
|
+
*/
|
|
6660
|
+
const invocationMetaSchema = z
|
|
6661
|
+
.object({
|
|
6662
|
+
traceId: z.string().optional(),
|
|
6663
|
+
runtime: z.object({}).passthrough().optional(),
|
|
6664
|
+
tokens: z.object({}).passthrough().optional(),
|
|
6665
|
+
callers: z.array(callerSchema).optional(),
|
|
6666
|
+
})
|
|
6667
|
+
.strict();
|
|
6668
|
+
// ============================================================================
|
|
6669
|
+
// Invocation Context Resolution
|
|
6670
|
+
// ============================================================================
|
|
6671
|
+
/**
|
|
6672
|
+
* Resolve invocation metadata to invocation context.
|
|
6673
|
+
*
|
|
6674
|
+
* @param meta - User-provided invocation metadata (**WHO/HOW**), optional.
|
|
6675
|
+
* @param defaults - Default runtime and tokens to use if not overridden.
|
|
6676
|
+
* @returns Frozen, immutable invocation context with guaranteed values.
|
|
6677
|
+
* @throws KitError when meta contains invalid properties.
|
|
6678
|
+
*
|
|
6679
|
+
* @remarks
|
|
6680
|
+
* Resolves the **WHO** called and **HOW** to observe:
|
|
6681
|
+
* - TraceId: Uses provided value or generates new one
|
|
6682
|
+
* - Runtime: Uses meta.runtime if provided, otherwise defaults.runtime
|
|
6683
|
+
* - Tokens: Uses meta.tokens if provided, otherwise defaults.tokens
|
|
6684
|
+
* - Callers: Uses provided array or empty array
|
|
6685
|
+
*
|
|
6686
|
+
* The returned context is frozen to enforce immutability.
|
|
6687
|
+
*
|
|
6688
|
+
* @example
|
|
6689
|
+
* ```typescript
|
|
6690
|
+
* import { resolveInvocationContext, createRuntime } from '@core/runtime'
|
|
6691
|
+
* import { createTokenRegistry } from '@core/tokens'
|
|
6692
|
+
*
|
|
6693
|
+
* const defaults = {
|
|
6694
|
+
* runtime: createRuntime(),
|
|
6695
|
+
* tokens: createTokenRegistry(),
|
|
6696
|
+
* }
|
|
6697
|
+
*
|
|
6698
|
+
* // Minimal - just using defaults
|
|
6699
|
+
* const ctx = resolveInvocationContext(undefined, defaults)
|
|
6700
|
+
*
|
|
6701
|
+
* // With trace ID and caller info
|
|
6702
|
+
* const ctx = resolveInvocationContext(
|
|
6703
|
+
* {
|
|
6704
|
+
* traceId: 'abc-123',
|
|
6705
|
+
* callers: [{ type: 'kit', name: 'BridgeKit', version: '1.0.0' }],
|
|
6706
|
+
* },
|
|
6707
|
+
* defaults
|
|
6708
|
+
* )
|
|
6709
|
+
*
|
|
6710
|
+
* // With runtime override (complete replacement)
|
|
6711
|
+
* const ctx = resolveInvocationContext(
|
|
6712
|
+
* { runtime: createRuntime({ logger: myLogger }) },
|
|
6713
|
+
* defaults
|
|
6714
|
+
* )
|
|
6715
|
+
* ```
|
|
6716
|
+
*/
|
|
6717
|
+
function resolveInvocationContext(meta, defaults) {
|
|
6718
|
+
// Validate meta input if provided
|
|
6719
|
+
if (meta !== undefined) {
|
|
6720
|
+
const result = invocationMetaSchema.safeParse(meta);
|
|
6721
|
+
if (!result.success) {
|
|
6722
|
+
throw createValidationFailedError$1('invocationMeta', meta, result.error.errors.map((e) => e.message).join(', '));
|
|
6723
|
+
}
|
|
6724
|
+
}
|
|
6725
|
+
// Generate trace ID if not provided
|
|
6726
|
+
const traceId = meta?.traceId ?? createTraceId();
|
|
6727
|
+
// Use meta overrides or fall back to defaults
|
|
6728
|
+
const runtime = meta?.runtime ?? defaults.runtime;
|
|
6729
|
+
const tokens = meta?.tokens ?? defaults.tokens;
|
|
6730
|
+
const callers = meta?.callers ?? [];
|
|
6731
|
+
return Object.freeze({ traceId, runtime, tokens, callers });
|
|
6732
|
+
}
|
|
6733
|
+
|
|
6734
|
+
/**
|
|
6735
|
+
* Extend an invocation context by appending a caller to its call chain.
|
|
6736
|
+
*
|
|
6737
|
+
* @param context - The existing invocation context to extend.
|
|
6738
|
+
* @param caller - The caller to append to the call chain.
|
|
6739
|
+
* @returns A new frozen invocation context with the caller appended.
|
|
6740
|
+
*
|
|
6741
|
+
* @remarks
|
|
6742
|
+
* This function creates a new immutable context with the caller appended
|
|
6743
|
+
* to the `callers` array while preserving all other context properties
|
|
6744
|
+
* (traceId, runtime, tokens).
|
|
6745
|
+
*
|
|
6746
|
+
* The returned context is frozen to enforce immutability.
|
|
6747
|
+
*
|
|
6748
|
+
* @example
|
|
6749
|
+
* ```typescript
|
|
6750
|
+
* import { extendInvocationContext } from '@core/runtime'
|
|
6751
|
+
*
|
|
6752
|
+
* const caller = { type: 'provider', name: 'CCTPV2', version: '1.0.0' }
|
|
6753
|
+
* const extended = extendInvocationContext(existingContext, caller)
|
|
6754
|
+
* // extended.callers === [...existingContext.callers, caller]
|
|
6755
|
+
* ```
|
|
6756
|
+
*/
|
|
6757
|
+
function extendInvocationContext(context, caller) {
|
|
6758
|
+
return Object.freeze({
|
|
6759
|
+
traceId: context.traceId,
|
|
6760
|
+
runtime: context.runtime,
|
|
6761
|
+
tokens: context.tokens,
|
|
6762
|
+
callers: [...context.callers, caller],
|
|
6763
|
+
});
|
|
6764
|
+
}
|
|
6765
|
+
|
|
6766
|
+
// Clock - expose defaultClock for backward compatibility
|
|
6767
|
+
/** Clock validation schema (backward compatibility). */
|
|
6768
|
+
z.custom((val) => val !== null &&
|
|
6769
|
+
typeof val === 'object' &&
|
|
6770
|
+
'now' in val &&
|
|
6771
|
+
typeof val['now'] === 'function');
|
|
6772
|
+
/** EventBus validation schema (backward compatibility). */
|
|
6773
|
+
z.custom((val) => val !== null &&
|
|
6774
|
+
typeof val === 'object' &&
|
|
6775
|
+
'emit' in val &&
|
|
6776
|
+
typeof val['emit'] === 'function');
|
|
6777
|
+
/** Runtime validation schema (backward compatibility). */
|
|
6778
|
+
z
|
|
6779
|
+
.object({
|
|
6780
|
+
logger: z.any().optional(),
|
|
6781
|
+
events: z.any().optional(),
|
|
6782
|
+
metrics: z.any().optional(),
|
|
6783
|
+
clock: z.any().optional(),
|
|
6784
|
+
})
|
|
6785
|
+
.passthrough();
|
|
6786
|
+
|
|
6787
|
+
/**
|
|
6788
|
+
* Create a structured error for token resolution failures.
|
|
6789
|
+
*
|
|
6790
|
+
* @remarks
|
|
6791
|
+
* Returns a `KitError` with `INPUT_INVALID_TOKEN` code (1006).
|
|
6792
|
+
* The error trace contains the selector and chain context for debugging.
|
|
6793
|
+
*
|
|
6794
|
+
* @param message - Human-readable error description.
|
|
6795
|
+
* @param selector - The token selector that failed to resolve.
|
|
6796
|
+
* @param chainId - The chain being resolved for (optional).
|
|
6797
|
+
* @param cause - The underlying error, if any (optional).
|
|
6798
|
+
* @returns A KitError with INPUT type and FATAL recoverability.
|
|
6799
|
+
*
|
|
6800
|
+
* @example
|
|
6801
|
+
* ```typescript
|
|
6802
|
+
* throw createTokenResolutionError(
|
|
6803
|
+
* 'Unknown token symbol: FAKE',
|
|
6804
|
+
* 'FAKE',
|
|
6805
|
+
* 'Ethereum'
|
|
6806
|
+
* )
|
|
6807
|
+
* ```
|
|
6808
|
+
*/
|
|
6809
|
+
function createTokenResolutionError(message, selector, chainId, cause) {
|
|
6810
|
+
const trace = {
|
|
6811
|
+
selector,
|
|
6812
|
+
...(chainId === undefined ? {} : { chainId }),
|
|
6813
|
+
...({} ),
|
|
6814
|
+
};
|
|
6815
|
+
return new KitError({
|
|
6816
|
+
...InputError.INVALID_TOKEN,
|
|
6817
|
+
recoverability: 'FATAL',
|
|
6818
|
+
message,
|
|
6819
|
+
cause: { trace },
|
|
6820
|
+
});
|
|
6821
|
+
}
|
|
6822
|
+
|
|
6823
|
+
/**
|
|
6824
|
+
* USDC token definition with addresses and metadata.
|
|
6825
|
+
*
|
|
6826
|
+
* @remarks
|
|
6827
|
+
* This is the built-in USDC definition used by the TokenRegistry.
|
|
6828
|
+
* Includes all known USDC addresses across supported chains.
|
|
6829
|
+
*
|
|
6830
|
+
* Keys use the `Blockchain` enum for type safety. Both enum values
|
|
6831
|
+
* and string literals are supported:
|
|
6832
|
+
* - `Blockchain.Ethereum` or `'Ethereum'`
|
|
6833
|
+
*
|
|
6834
|
+
* @example
|
|
6835
|
+
* ```typescript
|
|
6836
|
+
* import { USDC } from '@core/tokens'
|
|
6837
|
+
* import { Blockchain } from '@core/chains'
|
|
6838
|
+
*
|
|
6839
|
+
* console.log(USDC.symbol) // 'USDC'
|
|
6840
|
+
* console.log(USDC.decimals) // 6
|
|
6841
|
+
* console.log(USDC.locators[Blockchain.Ethereum])
|
|
6842
|
+
* // '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48'
|
|
6843
|
+
* ```
|
|
6844
|
+
*/
|
|
6845
|
+
const USDC = {
|
|
6846
|
+
symbol: 'USDC',
|
|
6847
|
+
decimals: 6,
|
|
6848
|
+
locators: {
|
|
6849
|
+
// =========================================================================
|
|
6850
|
+
// Mainnets (alphabetically sorted)
|
|
6851
|
+
// =========================================================================
|
|
6852
|
+
[Blockchain.Arbitrum]: '0xaf88d065e77c8cC2239327C5EDb3A432268e5831',
|
|
6853
|
+
[Blockchain.Avalanche]: '0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E',
|
|
6854
|
+
[Blockchain.Base]: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913',
|
|
6855
|
+
[Blockchain.Celo]: '0xcebA9300f2b948710d2653dD7B07f33A8B32118C',
|
|
6856
|
+
[Blockchain.Codex]: '0xd996633a415985DBd7D6D12f4A4343E31f5037cf',
|
|
6857
|
+
[Blockchain.Ethereum]: '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48',
|
|
6858
|
+
[Blockchain.Hedera]: '0.0.456858',
|
|
6859
|
+
[Blockchain.HyperEVM]: '0xb88339CB7199b77E23DB6E890353E22632Ba630f',
|
|
6860
|
+
[Blockchain.Ink]: '0x2D270e6886d130D724215A266106e6832161EAEd',
|
|
6861
|
+
[Blockchain.Linea]: '0x176211869ca2b568f2a7d4ee941e073a821ee1ff',
|
|
6862
|
+
[Blockchain.NEAR]: '17208628f84f5d6ad33f0da3bbbeb27ffcb398eac501a31bd6ad2011e36133a1',
|
|
6863
|
+
[Blockchain.Noble]: 'uusdc',
|
|
6864
|
+
[Blockchain.Optimism]: '0x0b2c639c533813f4aa9d7837caf62653d097ff85',
|
|
6865
|
+
[Blockchain.Plume]: '0x222365EF19F7947e5484218551B56bb3965Aa7aF',
|
|
6866
|
+
[Blockchain.Polkadot_Asset_Hub]: '1337',
|
|
6867
|
+
[Blockchain.Polygon]: '0x3c499c542cef5e3811e1192ce70d8cc03d5c3359',
|
|
6868
|
+
[Blockchain.Sei]: '0xe15fC38F6D8c56aF07bbCBe3BAf5708A2Bf42392',
|
|
6869
|
+
[Blockchain.Solana]: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v',
|
|
6870
|
+
[Blockchain.Sonic]: '0x29219dd400f2Bf60E5a23d13Be72B486D4038894',
|
|
6871
|
+
[Blockchain.Stellar]: 'USDC-GA5ZSEJYB37JRC5AVCIA5MOP4RHTM335X2KGX3IHOJAPP5RE34K4KZVN',
|
|
6872
|
+
[Blockchain.Sui]: '0xdba34672e30cb065b1f93e3ab55318768fd6fef66c15942c9f7cb846e2f900e7::usdc::USDC',
|
|
6873
|
+
[Blockchain.Unichain]: '0x078D782b760474a361dDA0AF3839290b0EF57AD6',
|
|
6874
|
+
[Blockchain.World_Chain]: '0x79A02482A880bCe3F13E09da970dC34dB4cD24D1',
|
|
6875
|
+
[Blockchain.XDC]: '0xfA2958CB79b0491CC627c1557F441eF849Ca8eb1',
|
|
6876
|
+
[Blockchain.ZKSync_Era]: '0x1d17CBcF0D6D143135aE902365D2E5e2A16538D4',
|
|
6877
|
+
// =========================================================================
|
|
6878
|
+
// Testnets (alphabetically sorted)
|
|
6879
|
+
// =========================================================================
|
|
6880
|
+
[Blockchain.Arbitrum_Sepolia]: '0x75faf114eafb1BDbe2F0316DF893fd58CE46AA4d',
|
|
6881
|
+
[Blockchain.Avalanche_Fuji]: '0x5425890298aed601595a70AB815c96711a31Bc65',
|
|
6882
|
+
[Blockchain.Base_Sepolia]: '0x036CbD53842c5426634e7929541eC2318f3dCF7e',
|
|
6883
|
+
[Blockchain.Codex_Testnet]: '0x6d7f141b6819C2c9CC2f818e6ad549E7Ca090F8f',
|
|
6884
|
+
[Blockchain.Ethereum_Sepolia]: '0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238',
|
|
6885
|
+
[Blockchain.Hedera_Testnet]: '0.0.429274',
|
|
6886
|
+
[Blockchain.HyperEVM_Testnet]: '0x2B3370eE501B4a559b57D449569354196457D8Ab',
|
|
6887
|
+
[Blockchain.Ink_Testnet]: '0xFabab97dCE620294D2B0b0e46C68964e326300Ac',
|
|
6888
|
+
[Blockchain.Linea_Sepolia]: '0xfece4462d57bd51a6a552365a011b95f0e16d9b7',
|
|
6889
|
+
[Blockchain.NEAR_Testnet]: '3e2210e1184b45b64c8a434c0a7e7b23cc04ea7eb7a6c3c32520d03d4afcb8af',
|
|
6890
|
+
[Blockchain.Noble_Testnet]: 'uusdc',
|
|
6891
|
+
[Blockchain.Optimism_Sepolia]: '0x5fd84259d66Cd46123540766Be93DFE6D43130D7',
|
|
6892
|
+
[Blockchain.Plume_Testnet]: '0xcB5f30e335672893c7eb944B374c196392C19D18',
|
|
6893
|
+
[Blockchain.Polkadot_Westmint]: '31337',
|
|
6894
|
+
[Blockchain.Polygon_Amoy_Testnet]: '0x41e94eb019c0762f9bfcf9fb1e58725bfb0e7582',
|
|
6895
|
+
[Blockchain.Sei_Testnet]: '0x4fCF1784B31630811181f670Aea7A7bEF803eaED',
|
|
6896
|
+
[Blockchain.Solana_Devnet]: '4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU',
|
|
6897
|
+
[Blockchain.Sonic_Testnet]: '0x0BA304580ee7c9a980CF72e55f5Ed2E9fd30Bc51',
|
|
6898
|
+
[Blockchain.Stellar_Testnet]: 'USDC-GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5',
|
|
6899
|
+
[Blockchain.Sui_Testnet]: '0xa1ec7fc00a6f40db9693ad1415d0c193ad3906494428cf252621037bd7117e29::usdc::USDC',
|
|
6900
|
+
[Blockchain.Unichain_Sepolia]: '0x31d0220469e10c4E71834a79b1f276d740d3768F',
|
|
6901
|
+
[Blockchain.World_Chain_Sepolia]: '0x66145f38cBAC35Ca6F1Dfb4914dF98F1614aeA88',
|
|
6902
|
+
[Blockchain.XDC_Apothem]: '0xb5AB69F7bBada22B28e79C8FFAECe55eF1c771D4',
|
|
6903
|
+
[Blockchain.ZKSync_Sepolia]: '0xAe045DE5638162fa134807Cb558E15A3F5A7F853',
|
|
6904
|
+
},
|
|
6905
|
+
};
|
|
6906
|
+
|
|
6907
|
+
// Re-export for consumers
|
|
6908
|
+
/**
|
|
6909
|
+
* All default token definitions.
|
|
6910
|
+
*
|
|
6911
|
+
* @remarks
|
|
6912
|
+
* These tokens are automatically included in the TokenRegistry when created
|
|
6913
|
+
* without explicit defaults. Extensions can override these definitions.
|
|
6914
|
+
*
|
|
6915
|
+
* @example
|
|
6916
|
+
* ```typescript
|
|
6917
|
+
* import { createTokenRegistry } from '@core/tokens'
|
|
6918
|
+
*
|
|
6919
|
+
* // Registry uses these by default
|
|
6920
|
+
* const registry = createTokenRegistry()
|
|
6921
|
+
*
|
|
6922
|
+
* // Add custom tokens (built-ins are still included)
|
|
6923
|
+
* const customRegistry = createTokenRegistry({
|
|
6924
|
+
* tokens: [myCustomToken],
|
|
6925
|
+
* })
|
|
6926
|
+
* ```
|
|
6927
|
+
*/
|
|
6928
|
+
const DEFAULT_TOKENS = [USDC];
|
|
6929
|
+
|
|
6930
|
+
/**
|
|
6931
|
+
* Check if a selector is a raw token selector (object form).
|
|
6932
|
+
*
|
|
6933
|
+
* @param selector - The token selector to check.
|
|
6934
|
+
* @returns True if the selector is a raw token selector.
|
|
6935
|
+
*/
|
|
6936
|
+
function isRawSelector(selector) {
|
|
6937
|
+
return typeof selector === 'object' && 'locator' in selector;
|
|
6938
|
+
}
|
|
6939
|
+
/**
|
|
6940
|
+
* Normalize a symbol to uppercase for case-insensitive lookup.
|
|
6941
|
+
*
|
|
6942
|
+
* @param symbol - The symbol to normalize.
|
|
6943
|
+
* @returns The normalized (uppercase) symbol.
|
|
6944
|
+
*/
|
|
6945
|
+
function normalizeSymbol(symbol) {
|
|
6946
|
+
return symbol.toUpperCase();
|
|
6947
|
+
}
|
|
6948
|
+
/**
|
|
6949
|
+
* Create a token registry with built-in tokens and optional extensions.
|
|
6950
|
+
*
|
|
6951
|
+
* @remarks
|
|
6952
|
+
* The registry always includes built-in tokens (DEFAULT_TOKENS) like USDC.
|
|
6953
|
+
* Custom tokens are merged on top - use this to add new tokens or override
|
|
6954
|
+
* built-in definitions.
|
|
6955
|
+
*
|
|
6956
|
+
* @param options - Configuration options for the registry.
|
|
6957
|
+
* @returns A token registry instance.
|
|
6958
|
+
*
|
|
6959
|
+
* @example
|
|
6960
|
+
* ```typescript
|
|
6961
|
+
* import { createTokenRegistry } from '@core/tokens'
|
|
6962
|
+
*
|
|
6963
|
+
* // Create registry with built-in tokens (USDC, etc.)
|
|
6964
|
+
* const registry = createTokenRegistry()
|
|
6965
|
+
*
|
|
6966
|
+
* // Resolve USDC on Ethereum
|
|
6967
|
+
* const usdc = registry.resolve('USDC', 'Ethereum')
|
|
6968
|
+
* console.log(usdc.locator) // '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48'
|
|
6969
|
+
* console.log(usdc.decimals) // 6
|
|
6970
|
+
* ```
|
|
6971
|
+
*
|
|
6972
|
+
* @example
|
|
6973
|
+
* ```typescript
|
|
6974
|
+
* // Add custom tokens (built-ins are still included)
|
|
6975
|
+
* const myToken: TokenDefinition = {
|
|
6976
|
+
* symbol: 'MY',
|
|
6977
|
+
* decimals: 18,
|
|
6978
|
+
* locators: { Ethereum: '0x...' },
|
|
6979
|
+
* }
|
|
6980
|
+
*
|
|
6981
|
+
* const registry = createTokenRegistry({ tokens: [myToken] })
|
|
6982
|
+
* registry.resolve('USDC', 'Ethereum') // Still works!
|
|
6983
|
+
* registry.resolve('MY', 'Ethereum') // Also works
|
|
6984
|
+
* ```
|
|
6985
|
+
*
|
|
6986
|
+
* @example
|
|
6987
|
+
* ```typescript
|
|
6988
|
+
* // Override a built-in token
|
|
6989
|
+
* const customUsdc: TokenDefinition = {
|
|
6990
|
+
* symbol: 'USDC',
|
|
6991
|
+
* decimals: 6,
|
|
6992
|
+
* locators: { MyChain: '0xCustomAddress' },
|
|
6993
|
+
* }
|
|
6994
|
+
*
|
|
6995
|
+
* const registry = createTokenRegistry({ tokens: [customUsdc] })
|
|
6996
|
+
* // Now USDC resolves to customUsdc definition
|
|
6997
|
+
* ```
|
|
6998
|
+
*
|
|
6999
|
+
* @example
|
|
7000
|
+
* ```typescript
|
|
7001
|
+
* // Resolve arbitrary tokens by raw locator
|
|
7002
|
+
* const registry = createTokenRegistry()
|
|
7003
|
+
* const token = registry.resolve(
|
|
7004
|
+
* { locator: '0x1234...', decimals: 18 },
|
|
7005
|
+
* 'Ethereum'
|
|
7006
|
+
* )
|
|
7007
|
+
* ```
|
|
7008
|
+
*/
|
|
7009
|
+
function createTokenRegistry(options = {}) {
|
|
7010
|
+
const { tokens = [], requireDecimals = false } = options;
|
|
7011
|
+
// Build the token map: always start with DEFAULT_TOKENS, then add custom tokens
|
|
7012
|
+
const tokenMap = new Map();
|
|
7013
|
+
// Add built-in tokens first
|
|
7014
|
+
for (const def of DEFAULT_TOKENS) {
|
|
7015
|
+
tokenMap.set(normalizeSymbol(def.symbol), def);
|
|
7016
|
+
}
|
|
7017
|
+
// Custom tokens override built-ins
|
|
7018
|
+
for (const def of tokens) {
|
|
7019
|
+
tokenMap.set(normalizeSymbol(def.symbol), def);
|
|
7020
|
+
}
|
|
7021
|
+
/**
|
|
7022
|
+
* Resolve a symbol selector to token information.
|
|
7023
|
+
*/
|
|
7024
|
+
function resolveSymbol(symbol, chainId) {
|
|
7025
|
+
const normalizedSymbol = normalizeSymbol(symbol);
|
|
7026
|
+
const definition = tokenMap.get(normalizedSymbol);
|
|
7027
|
+
if (definition === undefined) {
|
|
7028
|
+
throw createTokenResolutionError(`Unknown token symbol: ${symbol}. Register it via createTokenRegistry({ tokens: [...] }) or use a raw selector.`, symbol, chainId);
|
|
7029
|
+
}
|
|
7030
|
+
const locator = definition.locators[chainId];
|
|
7031
|
+
if (locator === undefined || locator.trim() === '') {
|
|
7032
|
+
throw createTokenResolutionError(`Token ${symbol} has no locator for chain ${chainId}`, symbol, chainId);
|
|
7033
|
+
}
|
|
7034
|
+
return {
|
|
7035
|
+
symbol: definition.symbol,
|
|
7036
|
+
decimals: definition.decimals,
|
|
7037
|
+
locator,
|
|
7038
|
+
};
|
|
7039
|
+
}
|
|
7040
|
+
/**
|
|
7041
|
+
* Resolve a raw selector to token information.
|
|
7042
|
+
*/
|
|
7043
|
+
function resolveRaw(selector, chainId) {
|
|
7044
|
+
const { locator, decimals } = selector;
|
|
7045
|
+
// Validate locator
|
|
7046
|
+
if (!locator || typeof locator !== 'string') {
|
|
7047
|
+
throw createTokenResolutionError('Raw selector must have a valid locator string', selector, chainId);
|
|
7048
|
+
}
|
|
7049
|
+
// Decimals are always required for raw selectors
|
|
7050
|
+
if (decimals === undefined) {
|
|
7051
|
+
const message = requireDecimals
|
|
7052
|
+
? 'Decimals required for raw token selector (requireDecimals: true)'
|
|
7053
|
+
: 'Decimals required for raw token selector. Provide { locator, decimals }.';
|
|
7054
|
+
throw createTokenResolutionError(message, selector, chainId);
|
|
7055
|
+
}
|
|
7056
|
+
// Validate decimals
|
|
7057
|
+
if (typeof decimals !== 'number' ||
|
|
7058
|
+
decimals < 0 ||
|
|
7059
|
+
!Number.isInteger(decimals)) {
|
|
7060
|
+
throw createTokenResolutionError(`Invalid decimals: ${String(decimals)}. Must be a non-negative integer.`, selector, chainId);
|
|
7061
|
+
}
|
|
7062
|
+
return {
|
|
7063
|
+
decimals,
|
|
7064
|
+
locator,
|
|
7065
|
+
};
|
|
7066
|
+
}
|
|
7067
|
+
return {
|
|
7068
|
+
resolve(selector, chainId) {
|
|
7069
|
+
// Runtime validation of inputs - these checks are for JS consumers
|
|
7070
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
7071
|
+
if (selector === null || selector === undefined) {
|
|
7072
|
+
throw createTokenResolutionError('Token selector cannot be null or undefined', selector, chainId);
|
|
7073
|
+
}
|
|
7074
|
+
if (chainId === '' || typeof chainId !== 'string') {
|
|
7075
|
+
throw createTokenResolutionError('Chain ID is required for token resolution', selector, chainId);
|
|
7076
|
+
}
|
|
7077
|
+
// Dispatch based on selector type
|
|
7078
|
+
if (isRawSelector(selector)) {
|
|
7079
|
+
return resolveRaw(selector, chainId);
|
|
7080
|
+
}
|
|
7081
|
+
if (typeof selector === 'string') {
|
|
7082
|
+
return resolveSymbol(selector, chainId);
|
|
7083
|
+
}
|
|
7084
|
+
throw createTokenResolutionError(`Invalid selector type: ${typeof selector}. Expected string or object with locator.`, selector, chainId);
|
|
7085
|
+
},
|
|
7086
|
+
get(symbol) {
|
|
7087
|
+
if (!symbol || typeof symbol !== 'string') {
|
|
7088
|
+
return undefined;
|
|
7089
|
+
}
|
|
7090
|
+
return tokenMap.get(normalizeSymbol(symbol));
|
|
7091
|
+
},
|
|
7092
|
+
has(symbol) {
|
|
7093
|
+
if (!symbol || typeof symbol !== 'string') {
|
|
7094
|
+
return false;
|
|
7095
|
+
}
|
|
7096
|
+
return tokenMap.has(normalizeSymbol(symbol));
|
|
7097
|
+
},
|
|
7098
|
+
symbols() {
|
|
7099
|
+
return Array.from(tokenMap.values()).map((def) => def.symbol);
|
|
7100
|
+
},
|
|
7101
|
+
entries() {
|
|
7102
|
+
return Array.from(tokenMap.values());
|
|
7103
|
+
},
|
|
7104
|
+
};
|
|
7105
|
+
}
|
|
7106
|
+
|
|
7107
|
+
/**
|
|
7108
|
+
* Define a schema for token registry interfaces.
|
|
7109
|
+
*
|
|
7110
|
+
* @remarks
|
|
7111
|
+
* Validate that a registry exposes the `resolve()` API used by adapters.
|
|
7112
|
+
*
|
|
7113
|
+
* @example
|
|
7114
|
+
* ```typescript
|
|
7115
|
+
* import { tokenRegistrySchema } from '@core/tokens'
|
|
7116
|
+
*
|
|
7117
|
+
* const registry = {
|
|
7118
|
+
* resolve: () => ({ locator: '0x0', decimals: 6 }),
|
|
7119
|
+
* }
|
|
7120
|
+
*
|
|
7121
|
+
* tokenRegistrySchema.parse(registry)
|
|
7122
|
+
* ```
|
|
7123
|
+
*/
|
|
7124
|
+
z.custom((value) => {
|
|
7125
|
+
if (value === null || typeof value !== 'object') {
|
|
7126
|
+
return false;
|
|
7127
|
+
}
|
|
7128
|
+
const record = value;
|
|
7129
|
+
return (typeof record['resolve'] === 'function' &&
|
|
7130
|
+
typeof record['get'] === 'function' &&
|
|
7131
|
+
typeof record['has'] === 'function' &&
|
|
7132
|
+
typeof record['symbols'] === 'function' &&
|
|
7133
|
+
typeof record['entries'] === 'function');
|
|
7134
|
+
}, {
|
|
7135
|
+
message: 'Invalid token registry',
|
|
7136
|
+
});
|
|
7137
|
+
|
|
7138
|
+
/**
|
|
7139
|
+
* Type guard to check if the destination is a forwarder-only destination.
|
|
7140
|
+
*
|
|
7141
|
+
* Forwarder-only destinations have `useForwarder: true` and no adapter.
|
|
7142
|
+
* They require a `recipientAddress` to be specified.
|
|
7143
|
+
*
|
|
7144
|
+
* @param dest - The bridge destination to check
|
|
7145
|
+
* @returns True if this is a forwarder-only destination without adapter
|
|
7146
|
+
*/
|
|
7147
|
+
function isForwarderOnlyDestination(dest) {
|
|
7148
|
+
return (dest.useForwarder === true &&
|
|
7149
|
+
!('adapter' in dest) &&
|
|
7150
|
+
'recipientAddress' in dest);
|
|
7151
|
+
}
|
|
7152
|
+
/**
|
|
7153
|
+
* Resolves a chain identifier to a chain definition.
|
|
7154
|
+
*
|
|
7155
|
+
* Both AdapterContext and BridgeDestinationWithAddress have the chain property
|
|
7156
|
+
* at the top level, so we can directly access it from either type.
|
|
7157
|
+
*
|
|
7158
|
+
* @param ctx - The bridge destination containing the chain identifier
|
|
7159
|
+
* @returns The resolved chain definition
|
|
7160
|
+
* @throws If the chain definition cannot be resolved
|
|
7161
|
+
*
|
|
7162
|
+
* @example
|
|
7163
|
+
* ```typescript
|
|
7164
|
+
* import { Blockchain } from '@core/chains'
|
|
7165
|
+
*
|
|
7166
|
+
* // AdapterContext
|
|
7167
|
+
* const chain1 = resolveChainDefinition({
|
|
7168
|
+
* adapter: mockAdapter,
|
|
7169
|
+
* chain: 'Ethereum'
|
|
7170
|
+
* })
|
|
7171
|
+
*
|
|
7172
|
+
* // BridgeDestinationWithAddress
|
|
7173
|
+
* const chain2 = resolveChainDefinition({
|
|
7174
|
+
* adapter: mockAdapter,
|
|
7175
|
+
* chain: 'Base',
|
|
7176
|
+
* recipientAddress: '0x123...'
|
|
7177
|
+
* })
|
|
7178
|
+
* ```
|
|
7179
|
+
*/
|
|
7180
|
+
function resolveChainDefinition(ctx) {
|
|
7181
|
+
return resolveChainIdentifier(ctx.chain);
|
|
7182
|
+
}
|
|
7183
|
+
/**
|
|
7184
|
+
* Resolves the signer's address from a bridge destination.
|
|
7185
|
+
*
|
|
7186
|
+
* This function resolves the address that will be used for transaction signing,
|
|
7187
|
+
* ignoring any `recipientAddress` field which is handled separately.
|
|
7188
|
+
*
|
|
7189
|
+
* It handles two cases:
|
|
7190
|
+
* - Developer-controlled adapters - returns the explicit address from context
|
|
7191
|
+
* - User-controlled adapters - calls getAddress() on the adapter
|
|
7192
|
+
*
|
|
7193
|
+
* @param ctx - The bridge destination to resolve the address from
|
|
7194
|
+
* @returns The resolved signer address string
|
|
7195
|
+
*
|
|
7196
|
+
* @example
|
|
7197
|
+
* ```typescript
|
|
7198
|
+
* // Developer-controlled adapter
|
|
7199
|
+
* const addr1 = await resolveAddress({
|
|
7200
|
+
* adapter: devAdapter,
|
|
7201
|
+
* chain: 'Ethereum',
|
|
7202
|
+
* address: '0x1234567890123456789012345678901234567890'
|
|
7203
|
+
* }) // Returns: '0x1234567890123456789012345678901234567890'
|
|
7204
|
+
*
|
|
7205
|
+
* // User-controlled adapter
|
|
7206
|
+
* const addr2 = await resolveAddress({
|
|
7207
|
+
* adapter: userAdapter,
|
|
7208
|
+
* chain: 'Ethereum'
|
|
7209
|
+
* }) // Returns adapter's connected address
|
|
7210
|
+
* ```
|
|
7211
|
+
*/
|
|
7212
|
+
async function resolveAddress(ctx) {
|
|
7213
|
+
// Handle based on adapter's addressContext
|
|
7214
|
+
if (ctx.adapter.capabilities?.addressContext === 'developer-controlled') {
|
|
7215
|
+
// Developer-controlled: address must be provided explicitly
|
|
7216
|
+
if ('address' in ctx && ctx.address) {
|
|
7217
|
+
return ctx.address;
|
|
7218
|
+
}
|
|
7219
|
+
throw new Error('Address is required in context for developer-controlled adapters. ' +
|
|
7220
|
+
'Please provide: { adapter, chain, address: "0x..." }');
|
|
7221
|
+
}
|
|
7222
|
+
else {
|
|
7223
|
+
// User-controlled: address should not be provided (auto-resolved from adapter)
|
|
7224
|
+
if ('address' in ctx && ctx.address) {
|
|
7225
|
+
throw new Error('Address should not be provided for user-controlled adapters. ' +
|
|
7226
|
+
'The address is automatically resolved from the connected wallet.');
|
|
7227
|
+
}
|
|
7228
|
+
// Derive address from adapter
|
|
7229
|
+
const chain = resolveChainDefinition(ctx);
|
|
7230
|
+
return await ctx.adapter.getAddress(chain);
|
|
7231
|
+
}
|
|
7232
|
+
}
|
|
7233
|
+
/**
|
|
7234
|
+
* Resolves the amount of a transfer by formatting it according to the token's decimal places.
|
|
7235
|
+
*
|
|
7236
|
+
* This function takes the raw amount from the transfer parameters and formats it
|
|
7237
|
+
* using the appropriate decimal places for the specified token. Currently supports
|
|
7238
|
+
* USDC (6 decimals) and falls back to the raw amount for other tokens.
|
|
7239
|
+
*
|
|
7240
|
+
* @param params - The bridge parameters containing the amount, token type, and from context
|
|
7241
|
+
* @returns The formatted amount string with proper decimal places
|
|
7242
|
+
*
|
|
7243
|
+
* @example
|
|
7244
|
+
* ```typescript
|
|
7245
|
+
* import { Adapter } from '@core/adapter'
|
|
7246
|
+
*
|
|
7247
|
+
* const params = {
|
|
7248
|
+
* amount: '1000000',
|
|
7249
|
+
* token: 'USDC',
|
|
7250
|
+
* from: { adapter: mockAdapter, chain: Ethereum },
|
|
7251
|
+
* to: { adapter: mockAdapter, chain: Base }
|
|
7252
|
+
* }
|
|
7253
|
+
* const formattedAmount = resolveAmount(params) // Returns '1000000000000'
|
|
7254
|
+
* ```
|
|
7255
|
+
*/
|
|
7256
|
+
function resolveAmount(params) {
|
|
7257
|
+
if (params.token === 'USDC') {
|
|
7258
|
+
return parseUnits(params.amount, 6).toString();
|
|
7259
|
+
}
|
|
7260
|
+
return params.amount;
|
|
7261
|
+
}
|
|
7262
|
+
/**
|
|
7263
|
+
* Resolves the invocation context for a bridge operation.
|
|
7264
|
+
*
|
|
7265
|
+
* Takes optional invocation metadata and resolves it into a full
|
|
7266
|
+
* InvocationContext with BridgeKit caller information appended.
|
|
7267
|
+
*
|
|
7268
|
+
* @param invocationMeta - Optional invocation metadata for tracing and correlation
|
|
7269
|
+
* @returns An InvocationContext with traceId, runtime, tokens, and caller chain
|
|
7270
|
+
*
|
|
7271
|
+
* @example
|
|
7272
|
+
* ```typescript
|
|
7273
|
+
* // With user-provided invocation metadata
|
|
7274
|
+
* const invocation = resolveBridgeInvocation({
|
|
7275
|
+
* traceId: 'my-custom-trace-id',
|
|
7276
|
+
* callers: [{ type: 'app', name: 'MyDApp' }],
|
|
7277
|
+
* })
|
|
7278
|
+
* // invocation.traceId === 'my-custom-trace-id'
|
|
7279
|
+
* // invocation.callers === [{ type: 'app', name: 'MyDApp' }, { type: 'kit', name: 'BridgeKit' }]
|
|
7280
|
+
*
|
|
7281
|
+
* // Without invocation metadata (auto-generated traceId)
|
|
7282
|
+
* const invocation2 = resolveBridgeInvocation()
|
|
7283
|
+
* // invocation2.traceId === <auto-generated OpenTelemetry-compatible trace ID>
|
|
7284
|
+
* // invocation2.callers === [{ type: 'kit', name: 'BridgeKit' }]
|
|
7285
|
+
* ```
|
|
7286
|
+
*/
|
|
7287
|
+
function resolveBridgeInvocation(invocationMeta) {
|
|
7288
|
+
const bridgeKitCaller = {
|
|
7289
|
+
type: 'kit',
|
|
7290
|
+
name: 'BridgeKit',
|
|
7291
|
+
version: pkg.version,
|
|
7292
|
+
};
|
|
7293
|
+
// Create default runtime and tokens for invocation context resolution
|
|
7294
|
+
const defaults = {
|
|
7295
|
+
runtime: createRuntime(),
|
|
7296
|
+
tokens: createTokenRegistry(),
|
|
7297
|
+
};
|
|
7298
|
+
// Resolve invocation metadata to full context, then extend with BridgeKit caller
|
|
7299
|
+
const baseContext = resolveInvocationContext(invocationMeta, defaults);
|
|
7300
|
+
return extendInvocationContext(baseContext, bridgeKitCaller);
|
|
7301
|
+
}
|
|
7302
|
+
/**
|
|
7303
|
+
* Resolves and normalizes bridge configuration for the provider.
|
|
7304
|
+
*
|
|
7305
|
+
* This function takes the optional configuration from bridge parameters and returns
|
|
7306
|
+
* a normalized BridgeConfig with:
|
|
7307
|
+
* - Default transfer speed set to FAST if not provided
|
|
5624
7308
|
* - Max fee values converted from human-readable to smallest units (6 decimals for USDC)
|
|
5625
7309
|
* - Custom fee values converted from human-readable to smallest units (6 decimals for USDC)
|
|
5626
7310
|
*
|
|
@@ -5683,6 +7367,7 @@ function resolveConfig(params) {
|
|
|
5683
7367
|
* - Amount formatting
|
|
5684
7368
|
*
|
|
5685
7369
|
* @param params - The bridge parameters containing source/destination contexts, amount, and token
|
|
7370
|
+
* @param invocationMeta - Optional invocation metadata for tracing and correlation
|
|
5686
7371
|
* @returns Promise resolving to normalized bridge parameters for provider consumption
|
|
5687
7372
|
* @throws \{Error\} If parameters cannot be resolved (invalid chains, etc.)
|
|
5688
7373
|
*
|
|
@@ -5702,22 +7387,32 @@ function resolveConfig(params) {
|
|
|
5702
7387
|
* ```
|
|
5703
7388
|
*/
|
|
5704
7389
|
async function resolveBridgeParams(params) {
|
|
5705
|
-
|
|
5706
|
-
const
|
|
7390
|
+
// Resolve chains
|
|
7391
|
+
const fromChain = resolveChainIdentifier(params.from.chain);
|
|
7392
|
+
const toChain = resolveChainIdentifier(params.to.chain);
|
|
7393
|
+
// Check if this is a forwarder-only destination (no adapter)
|
|
7394
|
+
const isForwarderOnly = isForwarderOnlyDestination(params.to);
|
|
5707
7395
|
// Validate adapter chain support after resolution
|
|
5708
|
-
// This ensures adapters support the resolved chains before proceeding
|
|
5709
7396
|
params.from.adapter.validateChainSupport(fromChain);
|
|
5710
|
-
|
|
7397
|
+
// Only validate destination adapter if it exists
|
|
7398
|
+
if (!isForwarderOnly && 'adapter' in params.to) {
|
|
7399
|
+
params.to.adapter.validateChainSupport(toChain);
|
|
7400
|
+
}
|
|
7401
|
+
// For forwarder-only destinations, use recipientAddress directly
|
|
7402
|
+
// For other destinations, resolve address from adapter
|
|
5711
7403
|
const [fromAddress, toAddress] = await Promise.all([
|
|
5712
7404
|
resolveAddress(params.from),
|
|
5713
|
-
|
|
7405
|
+
isForwarderOnly
|
|
7406
|
+
? Promise.resolve(params.to.recipientAddress)
|
|
7407
|
+
: resolveAddress(params.to),
|
|
5714
7408
|
]);
|
|
5715
7409
|
const token = params.token ?? 'USDC';
|
|
5716
|
-
// Extract adapters - now always from explicit contexts
|
|
5717
|
-
const fromAdapter = params.from.adapter;
|
|
5718
|
-
const toAdapter = params.to.adapter;
|
|
5719
7410
|
// Extract recipientAddress from params.to if it exists
|
|
5720
7411
|
const recipientAddress = 'recipientAddress' in params.to ? params.to.recipientAddress : undefined;
|
|
7412
|
+
// Extract useForwarder from params.to if it exists
|
|
7413
|
+
const useForwarder = 'useForwarder' in params.to ? params.to.useForwarder : undefined;
|
|
7414
|
+
// Resolve invocation metadata to full InvocationContext with BridgeKit caller
|
|
7415
|
+
const resolvedInvocation = resolveBridgeInvocation(params.invocationMeta);
|
|
5721
7416
|
return {
|
|
5722
7417
|
amount: resolveAmount({
|
|
5723
7418
|
...params,
|
|
@@ -5727,16 +7422,21 @@ async function resolveBridgeParams(params) {
|
|
|
5727
7422
|
config: resolveConfig({
|
|
5728
7423
|
...params}),
|
|
5729
7424
|
source: {
|
|
5730
|
-
adapter:
|
|
7425
|
+
adapter: params.from.adapter,
|
|
5731
7426
|
chain: fromChain,
|
|
5732
7427
|
address: fromAddress,
|
|
5733
7428
|
},
|
|
5734
7429
|
destination: {
|
|
5735
|
-
adapter
|
|
7430
|
+
// Only include adapter if it exists (not forwarder-only)
|
|
7431
|
+
...(!isForwarderOnly &&
|
|
7432
|
+
'adapter' in params.to && { adapter: params.to.adapter }),
|
|
5736
7433
|
chain: toChain,
|
|
5737
7434
|
address: toAddress,
|
|
5738
7435
|
...(recipientAddress !== undefined && { recipientAddress }),
|
|
7436
|
+
...(useForwarder !== undefined && { useForwarder }),
|
|
5739
7437
|
},
|
|
7438
|
+
// Pass resolved InvocationContext as invocationMeta (superset is compatible)
|
|
7439
|
+
invocationMeta: resolvedInvocation,
|
|
5740
7440
|
};
|
|
5741
7441
|
}
|
|
5742
7442
|
|
|
@@ -5814,6 +7514,36 @@ const formatBridgeResult = (result, formatDirection) => {
|
|
|
5814
7514
|
};
|
|
5815
7515
|
};
|
|
5816
7516
|
|
|
7517
|
+
/**
|
|
7518
|
+
* BridgeKit caller component for retry and estimate operations.
|
|
7519
|
+
*/
|
|
7520
|
+
const BRIDGE_KIT_CALLER = {
|
|
7521
|
+
type: 'kit',
|
|
7522
|
+
name: 'BridgeKit',
|
|
7523
|
+
version: pkg.version,
|
|
7524
|
+
};
|
|
7525
|
+
/**
|
|
7526
|
+
* Merge BridgeKit's caller into the invocation metadata for retry operations.
|
|
7527
|
+
*
|
|
7528
|
+
* Prepends the BridgeKit caller to the callers array.
|
|
7529
|
+
*
|
|
7530
|
+
* @param invocationMeta - Optional invocation metadata provided by caller.
|
|
7531
|
+
* @returns Merged invocation metadata with BridgeKit caller prepended.
|
|
7532
|
+
*
|
|
7533
|
+
* @internal
|
|
7534
|
+
*/
|
|
7535
|
+
function mergeRetryInvocationMeta(invocationMeta) {
|
|
7536
|
+
// Prepend BridgeKit caller to existing callers array
|
|
7537
|
+
const existingCallers = invocationMeta?.callers ?? [];
|
|
7538
|
+
return invocationMeta
|
|
7539
|
+
? {
|
|
7540
|
+
...invocationMeta,
|
|
7541
|
+
callers: [BRIDGE_KIT_CALLER, ...existingCallers],
|
|
7542
|
+
}
|
|
7543
|
+
: {
|
|
7544
|
+
callers: [BRIDGE_KIT_CALLER, ...existingCallers],
|
|
7545
|
+
};
|
|
7546
|
+
}
|
|
5817
7547
|
/**
|
|
5818
7548
|
* Route cross-chain USDC bridging through Circle's Cross-Chain Transfer Protocol v2 (CCTPv2).
|
|
5819
7549
|
*
|
|
@@ -5910,7 +7640,7 @@ class BridgeKit {
|
|
|
5910
7640
|
* - CCTPv2 support for the chain pair
|
|
5911
7641
|
* - Transfer configuration options
|
|
5912
7642
|
*
|
|
5913
|
-
* @param params - The transfer parameters containing source, destination, amount, and
|
|
7643
|
+
* @param params - The transfer parameters containing source, destination, amount, token, and optional invocation metadata
|
|
5914
7644
|
* @returns Promise resolving to the transfer result with transaction details and steps
|
|
5915
7645
|
* @throws {KitError} When any parameter validation fails.
|
|
5916
7646
|
* @throws {Error} When CCTPv2 does not support the specified route.
|
|
@@ -5927,18 +7657,24 @@ class BridgeKit {
|
|
|
5927
7657
|
* privateKey: process.env.PRIVATE_KEY,
|
|
5928
7658
|
* })
|
|
5929
7659
|
*
|
|
7660
|
+
* // Basic usage
|
|
5930
7661
|
* const result = await kit.bridge({
|
|
5931
|
-
* from: {
|
|
5932
|
-
*
|
|
5933
|
-
* chain: 'Ethereum'
|
|
5934
|
-
* },
|
|
5935
|
-
* to: {
|
|
5936
|
-
* adapter,
|
|
5937
|
-
* chain: 'Base'
|
|
5938
|
-
* },
|
|
7662
|
+
* from: { adapter, chain: 'Ethereum' },
|
|
7663
|
+
* to: { adapter, chain: 'Base' },
|
|
5939
7664
|
* amount: '100.50'
|
|
5940
7665
|
* })
|
|
5941
7666
|
*
|
|
7667
|
+
* // With custom invocation metadata
|
|
7668
|
+
* const result = await kit.bridge({
|
|
7669
|
+
* from: { adapter, chain: 'Ethereum' },
|
|
7670
|
+
* to: { adapter, chain: 'Base' },
|
|
7671
|
+
* amount: '100.50',
|
|
7672
|
+
* invocationMeta: {
|
|
7673
|
+
* traceId: 'custom-trace-id',
|
|
7674
|
+
* callers: [{ type: 'app', name: 'MyDApp', version: '1.0.0' }],
|
|
7675
|
+
* },
|
|
7676
|
+
* })
|
|
7677
|
+
*
|
|
5942
7678
|
* // Handle result
|
|
5943
7679
|
* if (result.state === 'success') {
|
|
5944
7680
|
* console.log('Bridge completed!')
|
|
@@ -5984,6 +7720,8 @@ class BridgeKit {
|
|
|
5984
7720
|
* @param context - The retry context containing fresh adapter instances for both
|
|
5985
7721
|
* source and destination chains. These adapters should be properly
|
|
5986
7722
|
* configured with current network connections and signing capabilities.
|
|
7723
|
+
* @param invocationMeta - Optional invocation metadata for tracing and correlation.
|
|
7724
|
+
* If not provided, uses the traceId from the original result.
|
|
5987
7725
|
* @returns A promise that resolves to the updated bridge result after retry execution.
|
|
5988
7726
|
* The result will contain the complete step history including both original
|
|
5989
7727
|
* and retry attempts.
|
|
@@ -6015,31 +7753,35 @@ class BridgeKit {
|
|
|
6015
7753
|
* // ... other properties
|
|
6016
7754
|
* }
|
|
6017
7755
|
*
|
|
7756
|
+
* // Basic retry (uses traceId from original result)
|
|
7757
|
+
* const retryResult = await kit.retry(failedResult, {
|
|
7758
|
+
* from: sourceAdapter,
|
|
7759
|
+
* to: destAdapter
|
|
7760
|
+
* })
|
|
6018
7761
|
*
|
|
6019
|
-
*
|
|
6020
|
-
*
|
|
6021
|
-
*
|
|
6022
|
-
*
|
|
6023
|
-
*
|
|
6024
|
-
*
|
|
6025
|
-
*
|
|
6026
|
-
*
|
|
6027
|
-
*
|
|
6028
|
-
* console.error('Retry failed:', error.message)
|
|
6029
|
-
* // Handle retry failure (may require manual intervention)
|
|
6030
|
-
* }
|
|
7762
|
+
* // Retry with custom invocation metadata
|
|
7763
|
+
* const retryResult = await kit.retry(
|
|
7764
|
+
* failedResult,
|
|
7765
|
+
* { from: sourceAdapter, to: destAdapter },
|
|
7766
|
+
* {
|
|
7767
|
+
* traceId: 'custom-trace-id',
|
|
7768
|
+
* callers: [{ type: 'app', name: 'MyApp' }],
|
|
7769
|
+
* }
|
|
7770
|
+
* )
|
|
6031
7771
|
* ```
|
|
6032
7772
|
*/
|
|
6033
|
-
async retry(result, context) {
|
|
7773
|
+
async retry(result, context, invocationMeta) {
|
|
6034
7774
|
const provider = this.providers.find((p) => p.name === result.provider);
|
|
6035
7775
|
if (!provider) {
|
|
6036
7776
|
throw new Error(`Provider ${result.provider} not found`);
|
|
6037
7777
|
}
|
|
7778
|
+
// Merge BridgeKit caller into invocation metadata for retry operation
|
|
7779
|
+
const mergedMeta = mergeRetryInvocationMeta(invocationMeta);
|
|
6038
7780
|
// Format the bridge result into bigint string values for internal use
|
|
6039
7781
|
const formattedBridgeResultInternal = formatBridgeResult(result, 'to-internal');
|
|
6040
7782
|
// Execute the retry using the provider
|
|
6041
7783
|
// Format the bridge result into human-readable string values for the user
|
|
6042
|
-
return formatBridgeResult(await provider.retry(formattedBridgeResultInternal, context), 'to-human-readable');
|
|
7784
|
+
return formatBridgeResult(await provider.retry(formattedBridgeResultInternal, context, mergedMeta), 'to-human-readable');
|
|
6043
7785
|
}
|
|
6044
7786
|
/**
|
|
6045
7787
|
* Estimate the cost and fees for a cross-chain USDC bridge operation.
|
|
@@ -6047,13 +7789,15 @@ class BridgeKit {
|
|
|
6047
7789
|
* This method calculates the expected gas fees and protocol costs for bridging
|
|
6048
7790
|
* without actually executing the transaction. It performs the same validation
|
|
6049
7791
|
* as the bridge method but stops before execution.
|
|
6050
|
-
*
|
|
7792
|
+
*
|
|
7793
|
+
* @param params - The bridge parameters for cost estimation, including optional invocation metadata
|
|
6051
7794
|
* @returns Promise resolving to detailed cost breakdown including gas estimates
|
|
6052
7795
|
* @throws {KitError} When the parameters are invalid.
|
|
6053
7796
|
* @throws {UnsupportedRouteError} When the route is not supported.
|
|
6054
7797
|
*
|
|
6055
7798
|
* @example
|
|
6056
7799
|
* ```typescript
|
|
7800
|
+
* // Basic usage
|
|
6057
7801
|
* const estimate = await kit.estimate({
|
|
6058
7802
|
* from: { adapter: adapter, chain: 'Ethereum' },
|
|
6059
7803
|
* to: { adapter: adapter, chain: 'Base' },
|
|
@@ -6061,6 +7805,18 @@ class BridgeKit {
|
|
|
6061
7805
|
* token: 'USDC'
|
|
6062
7806
|
* })
|
|
6063
7807
|
* console.log('Estimated cost:', estimate.totalCost)
|
|
7808
|
+
*
|
|
7809
|
+
* // With custom invocation metadata
|
|
7810
|
+
* const estimate = await kit.estimate({
|
|
7811
|
+
* from: { adapter: adapter, chain: 'Ethereum' },
|
|
7812
|
+
* to: { adapter: adapter, chain: 'Base' },
|
|
7813
|
+
* amount: '10.50',
|
|
7814
|
+
* token: 'USDC',
|
|
7815
|
+
* invocationMeta: {
|
|
7816
|
+
* traceId: 'custom-trace-id',
|
|
7817
|
+
* callers: [{ type: 'app', name: 'MyDApp', version: '1.0.0' }],
|
|
7818
|
+
* },
|
|
7819
|
+
* })
|
|
6064
7820
|
* ```
|
|
6065
7821
|
*/
|
|
6066
7822
|
async estimate(params) {
|
|
@@ -6112,6 +7868,9 @@ class BridgeKit {
|
|
|
6112
7868
|
* // Get only EVM mainnet chains
|
|
6113
7869
|
* const evmMainnets = kit.getSupportedChains({ chainType: 'evm', isTestnet: false })
|
|
6114
7870
|
*
|
|
7871
|
+
* // Get only chains that support forwarding
|
|
7872
|
+
* const forwarderChains = kit.getSupportedChains({ forwarderSupported: true })
|
|
7873
|
+
*
|
|
6115
7874
|
* console.log('Supported chains:')
|
|
6116
7875
|
* allChains.forEach(chain => {
|
|
6117
7876
|
* console.log(`- ${chain.name} (${chain.type})`)
|
|
@@ -6145,6 +7904,18 @@ class BridgeKit {
|
|
|
6145
7904
|
if (options?.isTestnet !== undefined) {
|
|
6146
7905
|
chains = chains.filter((chain) => chain.isTestnet === options.isTestnet);
|
|
6147
7906
|
}
|
|
7907
|
+
// Apply forwarder support filter if provided
|
|
7908
|
+
if (options?.forwarderSupported !== undefined) {
|
|
7909
|
+
chains = chains.filter((chain) => {
|
|
7910
|
+
const fs = chain.cctp?.forwarderSupported;
|
|
7911
|
+
if (!fs) {
|
|
7912
|
+
return !options.forwarderSupported;
|
|
7913
|
+
}
|
|
7914
|
+
return options.forwarderSupported
|
|
7915
|
+
? fs.source || fs.destination
|
|
7916
|
+
: !fs.source && !fs.destination;
|
|
7917
|
+
});
|
|
7918
|
+
}
|
|
6148
7919
|
return chains;
|
|
6149
7920
|
}
|
|
6150
7921
|
/**
|
|
@@ -6332,5 +8103,5 @@ class BridgeKit {
|
|
|
6332
8103
|
// Auto-register this kit for user agent tracking
|
|
6333
8104
|
registerKit(`${pkg.name}/${pkg.version}`);
|
|
6334
8105
|
|
|
6335
|
-
export { BalanceError, Blockchain, BridgeChain, BridgeKit, InputError, KitError, NetworkError, OnchainError, RpcError, TransferSpeed, bridgeParamsWithChainIdentifierSchema, getErrorCode, getErrorMessage, isBalanceError, isFatalError, isInputError, isKitError, isNetworkError, isOnchainError, isRetryableError, isRpcError, resolveChainIdentifier, setExternalPrefix };
|
|
8106
|
+
export { BalanceError, Blockchain, BridgeChain, BridgeKit, InputError, KitError, NetworkError, OnchainError, RpcError, TransferSpeed, bridgeParamsWithChainIdentifierSchema, createRuntime, createTokenRegistry, createTraceId, extendInvocationContext, getErrorCode, getErrorMessage, isBalanceError, isFatalError, isInputError, isKitError, isNetworkError, isOnchainError, isRetryableError, isRpcError, resolveChainIdentifier, resolveInvocationContext, setExternalPrefix };
|
|
6336
8107
|
//# sourceMappingURL=index.mjs.map
|