@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.cjs
CHANGED
|
@@ -23,8 +23,13 @@ require('@ethersproject/bytes');
|
|
|
23
23
|
require('@ethersproject/address');
|
|
24
24
|
require('bs58');
|
|
25
25
|
var units = require('@ethersproject/units');
|
|
26
|
+
var pino = require('pino');
|
|
26
27
|
var providerCctpV2 = require('@circle-fin/provider-cctp-v2');
|
|
27
28
|
|
|
29
|
+
function _interopDefault (e) { return e && e.__esModule ? e.default : e; }
|
|
30
|
+
|
|
31
|
+
var pino__default = /*#__PURE__*/_interopDefault(pino);
|
|
32
|
+
|
|
28
33
|
/**
|
|
29
34
|
* Detect the runtime environment and return a shortened identifier.
|
|
30
35
|
*
|
|
@@ -655,6 +660,18 @@ const NetworkError = {
|
|
|
655
660
|
name: 'NETWORK_TIMEOUT',
|
|
656
661
|
type: 'NETWORK',
|
|
657
662
|
},
|
|
663
|
+
/** Circle relayer failed to process the forwarding/mint transaction */
|
|
664
|
+
RELAYER_FORWARD_FAILED: {
|
|
665
|
+
code: 3003,
|
|
666
|
+
name: 'NETWORK_RELAYER_FORWARD_FAILED',
|
|
667
|
+
type: 'NETWORK',
|
|
668
|
+
},
|
|
669
|
+
/** Relayer mint is pending - waiting for confirmation */
|
|
670
|
+
RELAYER_PENDING: {
|
|
671
|
+
code: 3004,
|
|
672
|
+
name: 'NETWORK_RELAYER_PENDING',
|
|
673
|
+
type: 'NETWORK',
|
|
674
|
+
},
|
|
658
675
|
};
|
|
659
676
|
|
|
660
677
|
/**
|
|
@@ -1215,6 +1232,10 @@ const Aptos = defineChain({
|
|
|
1215
1232
|
confirmations: 1,
|
|
1216
1233
|
},
|
|
1217
1234
|
},
|
|
1235
|
+
forwarderSupported: {
|
|
1236
|
+
source: false,
|
|
1237
|
+
destination: false,
|
|
1238
|
+
},
|
|
1218
1239
|
},
|
|
1219
1240
|
});
|
|
1220
1241
|
|
|
@@ -1248,6 +1269,10 @@ const AptosTestnet = defineChain({
|
|
|
1248
1269
|
confirmations: 1,
|
|
1249
1270
|
},
|
|
1250
1271
|
},
|
|
1272
|
+
forwarderSupported: {
|
|
1273
|
+
source: false,
|
|
1274
|
+
destination: false,
|
|
1275
|
+
},
|
|
1251
1276
|
},
|
|
1252
1277
|
});
|
|
1253
1278
|
|
|
@@ -1307,6 +1332,10 @@ const ArcTestnet = defineChain({
|
|
|
1307
1332
|
fastConfirmations: 1,
|
|
1308
1333
|
},
|
|
1309
1334
|
},
|
|
1335
|
+
forwarderSupported: {
|
|
1336
|
+
source: true,
|
|
1337
|
+
destination: true,
|
|
1338
|
+
},
|
|
1310
1339
|
},
|
|
1311
1340
|
kitContracts: {
|
|
1312
1341
|
bridge: BRIDGE_CONTRACT_EVM_TESTNET,
|
|
@@ -1351,6 +1380,10 @@ const Arbitrum = defineChain({
|
|
|
1351
1380
|
fastConfirmations: 1,
|
|
1352
1381
|
},
|
|
1353
1382
|
},
|
|
1383
|
+
forwarderSupported: {
|
|
1384
|
+
source: true,
|
|
1385
|
+
destination: true,
|
|
1386
|
+
},
|
|
1354
1387
|
},
|
|
1355
1388
|
kitContracts: {
|
|
1356
1389
|
bridge: BRIDGE_CONTRACT_EVM_MAINNET,
|
|
@@ -1395,6 +1428,10 @@ const ArbitrumSepolia = defineChain({
|
|
|
1395
1428
|
fastConfirmations: 1,
|
|
1396
1429
|
},
|
|
1397
1430
|
},
|
|
1431
|
+
forwarderSupported: {
|
|
1432
|
+
source: true,
|
|
1433
|
+
destination: true,
|
|
1434
|
+
},
|
|
1398
1435
|
},
|
|
1399
1436
|
kitContracts: {
|
|
1400
1437
|
bridge: BRIDGE_CONTRACT_EVM_TESTNET,
|
|
@@ -1439,6 +1476,10 @@ const Avalanche = defineChain({
|
|
|
1439
1476
|
fastConfirmations: 1,
|
|
1440
1477
|
},
|
|
1441
1478
|
},
|
|
1479
|
+
forwarderSupported: {
|
|
1480
|
+
source: true,
|
|
1481
|
+
destination: true,
|
|
1482
|
+
},
|
|
1442
1483
|
},
|
|
1443
1484
|
kitContracts: {
|
|
1444
1485
|
bridge: BRIDGE_CONTRACT_EVM_MAINNET,
|
|
@@ -1482,6 +1523,10 @@ const AvalancheFuji = defineChain({
|
|
|
1482
1523
|
fastConfirmations: 1,
|
|
1483
1524
|
},
|
|
1484
1525
|
},
|
|
1526
|
+
forwarderSupported: {
|
|
1527
|
+
source: true,
|
|
1528
|
+
destination: true,
|
|
1529
|
+
},
|
|
1485
1530
|
},
|
|
1486
1531
|
rpcEndpoints: ['https://api.avax-test.network/ext/bc/C/rpc'],
|
|
1487
1532
|
kitContracts: {
|
|
@@ -1527,6 +1572,10 @@ const Base = defineChain({
|
|
|
1527
1572
|
fastConfirmations: 1,
|
|
1528
1573
|
},
|
|
1529
1574
|
},
|
|
1575
|
+
forwarderSupported: {
|
|
1576
|
+
source: true,
|
|
1577
|
+
destination: true,
|
|
1578
|
+
},
|
|
1530
1579
|
},
|
|
1531
1580
|
kitContracts: {
|
|
1532
1581
|
bridge: BRIDGE_CONTRACT_EVM_MAINNET,
|
|
@@ -1571,6 +1620,10 @@ const BaseSepolia = defineChain({
|
|
|
1571
1620
|
fastConfirmations: 1,
|
|
1572
1621
|
},
|
|
1573
1622
|
},
|
|
1623
|
+
forwarderSupported: {
|
|
1624
|
+
source: true,
|
|
1625
|
+
destination: true,
|
|
1626
|
+
},
|
|
1574
1627
|
},
|
|
1575
1628
|
kitContracts: {
|
|
1576
1629
|
bridge: BRIDGE_CONTRACT_EVM_TESTNET,
|
|
@@ -1657,6 +1710,10 @@ const Codex = defineChain({
|
|
|
1657
1710
|
fastConfirmations: 1,
|
|
1658
1711
|
},
|
|
1659
1712
|
},
|
|
1713
|
+
forwarderSupported: {
|
|
1714
|
+
source: true,
|
|
1715
|
+
destination: false,
|
|
1716
|
+
},
|
|
1660
1717
|
},
|
|
1661
1718
|
kitContracts: {
|
|
1662
1719
|
bridge: BRIDGE_CONTRACT_EVM_MAINNET,
|
|
@@ -1695,6 +1752,10 @@ const CodexTestnet = defineChain({
|
|
|
1695
1752
|
fastConfirmations: 1,
|
|
1696
1753
|
},
|
|
1697
1754
|
},
|
|
1755
|
+
forwarderSupported: {
|
|
1756
|
+
source: true,
|
|
1757
|
+
destination: false,
|
|
1758
|
+
},
|
|
1698
1759
|
},
|
|
1699
1760
|
kitContracts: {
|
|
1700
1761
|
bridge: BRIDGE_CONTRACT_EVM_TESTNET,
|
|
@@ -1739,6 +1800,10 @@ const Ethereum = defineChain({
|
|
|
1739
1800
|
fastConfirmations: 2,
|
|
1740
1801
|
},
|
|
1741
1802
|
},
|
|
1803
|
+
forwarderSupported: {
|
|
1804
|
+
source: true,
|
|
1805
|
+
destination: true,
|
|
1806
|
+
},
|
|
1742
1807
|
},
|
|
1743
1808
|
kitContracts: {
|
|
1744
1809
|
bridge: BRIDGE_CONTRACT_EVM_MAINNET,
|
|
@@ -1783,6 +1848,10 @@ const EthereumSepolia = defineChain({
|
|
|
1783
1848
|
fastConfirmations: 2,
|
|
1784
1849
|
},
|
|
1785
1850
|
},
|
|
1851
|
+
forwarderSupported: {
|
|
1852
|
+
source: true,
|
|
1853
|
+
destination: true,
|
|
1854
|
+
},
|
|
1786
1855
|
},
|
|
1787
1856
|
kitContracts: {
|
|
1788
1857
|
bridge: BRIDGE_CONTRACT_EVM_TESTNET,
|
|
@@ -1869,6 +1938,10 @@ const HyperEVM = defineChain({
|
|
|
1869
1938
|
fastConfirmations: 1,
|
|
1870
1939
|
},
|
|
1871
1940
|
},
|
|
1941
|
+
forwarderSupported: {
|
|
1942
|
+
source: true,
|
|
1943
|
+
destination: true,
|
|
1944
|
+
},
|
|
1872
1945
|
},
|
|
1873
1946
|
kitContracts: {
|
|
1874
1947
|
bridge: BRIDGE_CONTRACT_EVM_MAINNET,
|
|
@@ -1908,6 +1981,10 @@ const HyperEVMTestnet = defineChain({
|
|
|
1908
1981
|
fastConfirmations: 1,
|
|
1909
1982
|
},
|
|
1910
1983
|
},
|
|
1984
|
+
forwarderSupported: {
|
|
1985
|
+
source: true,
|
|
1986
|
+
destination: true,
|
|
1987
|
+
},
|
|
1911
1988
|
},
|
|
1912
1989
|
kitContracts: {
|
|
1913
1990
|
bridge: BRIDGE_CONTRACT_EVM_TESTNET,
|
|
@@ -1951,6 +2028,10 @@ const Ink = defineChain({
|
|
|
1951
2028
|
fastConfirmations: 1,
|
|
1952
2029
|
},
|
|
1953
2030
|
},
|
|
2031
|
+
forwarderSupported: {
|
|
2032
|
+
source: true,
|
|
2033
|
+
destination: true,
|
|
2034
|
+
},
|
|
1954
2035
|
},
|
|
1955
2036
|
kitContracts: {
|
|
1956
2037
|
bridge: BRIDGE_CONTRACT_EVM_MAINNET,
|
|
@@ -1993,6 +2074,10 @@ const InkTestnet = defineChain({
|
|
|
1993
2074
|
fastConfirmations: 1,
|
|
1994
2075
|
},
|
|
1995
2076
|
},
|
|
2077
|
+
forwarderSupported: {
|
|
2078
|
+
source: true,
|
|
2079
|
+
destination: true,
|
|
2080
|
+
},
|
|
1996
2081
|
},
|
|
1997
2082
|
kitContracts: {
|
|
1998
2083
|
bridge: BRIDGE_CONTRACT_EVM_TESTNET,
|
|
@@ -2031,6 +2116,10 @@ const Linea = defineChain({
|
|
|
2031
2116
|
fastConfirmations: 1,
|
|
2032
2117
|
},
|
|
2033
2118
|
},
|
|
2119
|
+
forwarderSupported: {
|
|
2120
|
+
source: true,
|
|
2121
|
+
destination: true,
|
|
2122
|
+
},
|
|
2034
2123
|
},
|
|
2035
2124
|
kitContracts: {
|
|
2036
2125
|
bridge: BRIDGE_CONTRACT_EVM_MAINNET,
|
|
@@ -2069,6 +2158,10 @@ const LineaSepolia = defineChain({
|
|
|
2069
2158
|
fastConfirmations: 1,
|
|
2070
2159
|
},
|
|
2071
2160
|
},
|
|
2161
|
+
forwarderSupported: {
|
|
2162
|
+
source: true,
|
|
2163
|
+
destination: true,
|
|
2164
|
+
},
|
|
2072
2165
|
},
|
|
2073
2166
|
kitContracts: {
|
|
2074
2167
|
bridge: BRIDGE_CONTRACT_EVM_TESTNET,
|
|
@@ -2109,6 +2202,10 @@ const Monad = defineChain({
|
|
|
2109
2202
|
fastConfirmations: 1,
|
|
2110
2203
|
},
|
|
2111
2204
|
},
|
|
2205
|
+
forwarderSupported: {
|
|
2206
|
+
source: true,
|
|
2207
|
+
destination: true,
|
|
2208
|
+
},
|
|
2112
2209
|
},
|
|
2113
2210
|
kitContracts: {
|
|
2114
2211
|
bridge: BRIDGE_CONTRACT_EVM_MAINNET,
|
|
@@ -2149,6 +2246,10 @@ const MonadTestnet = defineChain({
|
|
|
2149
2246
|
fastConfirmations: 1,
|
|
2150
2247
|
},
|
|
2151
2248
|
},
|
|
2249
|
+
forwarderSupported: {
|
|
2250
|
+
source: true,
|
|
2251
|
+
destination: true,
|
|
2252
|
+
},
|
|
2152
2253
|
},
|
|
2153
2254
|
kitContracts: {
|
|
2154
2255
|
bridge: BRIDGE_CONTRACT_EVM_TESTNET,
|
|
@@ -2230,6 +2331,10 @@ const Noble = defineChain({
|
|
|
2230
2331
|
confirmations: 1,
|
|
2231
2332
|
},
|
|
2232
2333
|
},
|
|
2334
|
+
forwarderSupported: {
|
|
2335
|
+
source: false,
|
|
2336
|
+
destination: false,
|
|
2337
|
+
},
|
|
2233
2338
|
},
|
|
2234
2339
|
});
|
|
2235
2340
|
|
|
@@ -2262,6 +2367,10 @@ const NobleTestnet = defineChain({
|
|
|
2262
2367
|
confirmations: 1,
|
|
2263
2368
|
},
|
|
2264
2369
|
},
|
|
2370
|
+
forwarderSupported: {
|
|
2371
|
+
source: false,
|
|
2372
|
+
destination: false,
|
|
2373
|
+
},
|
|
2265
2374
|
},
|
|
2266
2375
|
});
|
|
2267
2376
|
|
|
@@ -2303,6 +2412,10 @@ const Optimism = defineChain({
|
|
|
2303
2412
|
fastConfirmations: 1,
|
|
2304
2413
|
},
|
|
2305
2414
|
},
|
|
2415
|
+
forwarderSupported: {
|
|
2416
|
+
source: true,
|
|
2417
|
+
destination: true,
|
|
2418
|
+
},
|
|
2306
2419
|
},
|
|
2307
2420
|
kitContracts: {
|
|
2308
2421
|
bridge: BRIDGE_CONTRACT_EVM_MAINNET,
|
|
@@ -2347,6 +2460,10 @@ const OptimismSepolia = defineChain({
|
|
|
2347
2460
|
fastConfirmations: 1,
|
|
2348
2461
|
},
|
|
2349
2462
|
},
|
|
2463
|
+
forwarderSupported: {
|
|
2464
|
+
source: true,
|
|
2465
|
+
destination: true,
|
|
2466
|
+
},
|
|
2350
2467
|
},
|
|
2351
2468
|
kitContracts: {
|
|
2352
2469
|
bridge: BRIDGE_CONTRACT_EVM_TESTNET,
|
|
@@ -2387,6 +2504,10 @@ const Plume = defineChain({
|
|
|
2387
2504
|
fastConfirmations: 1,
|
|
2388
2505
|
},
|
|
2389
2506
|
},
|
|
2507
|
+
forwarderSupported: {
|
|
2508
|
+
source: true,
|
|
2509
|
+
destination: false,
|
|
2510
|
+
},
|
|
2390
2511
|
},
|
|
2391
2512
|
kitContracts: {
|
|
2392
2513
|
bridge: BRIDGE_CONTRACT_EVM_MAINNET,
|
|
@@ -2426,6 +2547,10 @@ const PlumeTestnet = defineChain({
|
|
|
2426
2547
|
fastConfirmations: 1,
|
|
2427
2548
|
},
|
|
2428
2549
|
},
|
|
2550
|
+
forwarderSupported: {
|
|
2551
|
+
source: true,
|
|
2552
|
+
destination: false,
|
|
2553
|
+
},
|
|
2429
2554
|
},
|
|
2430
2555
|
kitContracts: {
|
|
2431
2556
|
bridge: BRIDGE_CONTRACT_EVM_TESTNET,
|
|
@@ -2516,6 +2641,10 @@ const Polygon = defineChain({
|
|
|
2516
2641
|
fastConfirmations: 13,
|
|
2517
2642
|
},
|
|
2518
2643
|
},
|
|
2644
|
+
forwarderSupported: {
|
|
2645
|
+
source: true,
|
|
2646
|
+
destination: true,
|
|
2647
|
+
},
|
|
2519
2648
|
},
|
|
2520
2649
|
kitContracts: {
|
|
2521
2650
|
bridge: BRIDGE_CONTRACT_EVM_MAINNET,
|
|
@@ -2560,6 +2689,10 @@ const PolygonAmoy = defineChain({
|
|
|
2560
2689
|
fastConfirmations: 13,
|
|
2561
2690
|
},
|
|
2562
2691
|
},
|
|
2692
|
+
forwarderSupported: {
|
|
2693
|
+
source: true,
|
|
2694
|
+
destination: true,
|
|
2695
|
+
},
|
|
2563
2696
|
},
|
|
2564
2697
|
kitContracts: {
|
|
2565
2698
|
bridge: BRIDGE_CONTRACT_EVM_TESTNET,
|
|
@@ -2600,6 +2733,10 @@ const Sei = defineChain({
|
|
|
2600
2733
|
fastConfirmations: 1,
|
|
2601
2734
|
},
|
|
2602
2735
|
},
|
|
2736
|
+
forwarderSupported: {
|
|
2737
|
+
source: true,
|
|
2738
|
+
destination: true,
|
|
2739
|
+
},
|
|
2603
2740
|
},
|
|
2604
2741
|
kitContracts: {
|
|
2605
2742
|
bridge: BRIDGE_CONTRACT_EVM_MAINNET,
|
|
@@ -2639,6 +2776,10 @@ const SeiTestnet = defineChain({
|
|
|
2639
2776
|
fastConfirmations: 1,
|
|
2640
2777
|
},
|
|
2641
2778
|
},
|
|
2779
|
+
forwarderSupported: {
|
|
2780
|
+
source: true,
|
|
2781
|
+
destination: true,
|
|
2782
|
+
},
|
|
2642
2783
|
},
|
|
2643
2784
|
kitContracts: {
|
|
2644
2785
|
bridge: BRIDGE_CONTRACT_EVM_TESTNET,
|
|
@@ -2677,6 +2818,10 @@ const Sonic = defineChain({
|
|
|
2677
2818
|
fastConfirmations: 1,
|
|
2678
2819
|
},
|
|
2679
2820
|
},
|
|
2821
|
+
forwarderSupported: {
|
|
2822
|
+
source: true,
|
|
2823
|
+
destination: true,
|
|
2824
|
+
},
|
|
2680
2825
|
},
|
|
2681
2826
|
kitContracts: {
|
|
2682
2827
|
bridge: BRIDGE_CONTRACT_EVM_MAINNET,
|
|
@@ -2715,6 +2860,10 @@ const SonicTestnet = defineChain({
|
|
|
2715
2860
|
fastConfirmations: 1,
|
|
2716
2861
|
},
|
|
2717
2862
|
},
|
|
2863
|
+
forwarderSupported: {
|
|
2864
|
+
source: true,
|
|
2865
|
+
destination: true,
|
|
2866
|
+
},
|
|
2718
2867
|
},
|
|
2719
2868
|
kitContracts: {
|
|
2720
2869
|
bridge: BRIDGE_CONTRACT_EVM_TESTNET,
|
|
@@ -2758,6 +2907,10 @@ const Solana = defineChain({
|
|
|
2758
2907
|
fastConfirmations: 3,
|
|
2759
2908
|
},
|
|
2760
2909
|
},
|
|
2910
|
+
forwarderSupported: {
|
|
2911
|
+
source: true,
|
|
2912
|
+
destination: false,
|
|
2913
|
+
},
|
|
2761
2914
|
},
|
|
2762
2915
|
kitContracts: {
|
|
2763
2916
|
bridge: 'DFaauJEjmiHkPs1JG89A4p95hDWi9m9SAEERY1LQJiC3',
|
|
@@ -2800,6 +2953,10 @@ const SolanaDevnet = defineChain({
|
|
|
2800
2953
|
fastConfirmations: 3,
|
|
2801
2954
|
},
|
|
2802
2955
|
},
|
|
2956
|
+
forwarderSupported: {
|
|
2957
|
+
source: true,
|
|
2958
|
+
destination: false,
|
|
2959
|
+
},
|
|
2803
2960
|
},
|
|
2804
2961
|
kitContracts: {
|
|
2805
2962
|
bridge: 'DFaauJEjmiHkPs1JG89A4p95hDWi9m9SAEERY1LQJiC3',
|
|
@@ -2883,6 +3040,10 @@ const Sui = defineChain({
|
|
|
2883
3040
|
confirmations: 1,
|
|
2884
3041
|
},
|
|
2885
3042
|
},
|
|
3043
|
+
forwarderSupported: {
|
|
3044
|
+
source: false,
|
|
3045
|
+
destination: false,
|
|
3046
|
+
},
|
|
2886
3047
|
},
|
|
2887
3048
|
});
|
|
2888
3049
|
|
|
@@ -2916,6 +3077,10 @@ const SuiTestnet = defineChain({
|
|
|
2916
3077
|
confirmations: 1,
|
|
2917
3078
|
},
|
|
2918
3079
|
},
|
|
3080
|
+
forwarderSupported: {
|
|
3081
|
+
source: false,
|
|
3082
|
+
destination: false,
|
|
3083
|
+
},
|
|
2919
3084
|
},
|
|
2920
3085
|
});
|
|
2921
3086
|
|
|
@@ -2937,7 +3102,7 @@ const Unichain = defineChain({
|
|
|
2937
3102
|
chainId: 130,
|
|
2938
3103
|
isTestnet: false,
|
|
2939
3104
|
explorerUrl: 'https://unichain.blockscout.com/tx/{hash}',
|
|
2940
|
-
rpcEndpoints: ['https://
|
|
3105
|
+
rpcEndpoints: ['https://mainnet.unichain.org'],
|
|
2941
3106
|
eurcAddress: null,
|
|
2942
3107
|
usdcAddress: '0x078D782b760474a361dDA0AF3839290b0EF57AD6',
|
|
2943
3108
|
cctp: {
|
|
@@ -2957,6 +3122,10 @@ const Unichain = defineChain({
|
|
|
2957
3122
|
fastConfirmations: 1,
|
|
2958
3123
|
},
|
|
2959
3124
|
},
|
|
3125
|
+
forwarderSupported: {
|
|
3126
|
+
source: true,
|
|
3127
|
+
destination: true,
|
|
3128
|
+
},
|
|
2960
3129
|
},
|
|
2961
3130
|
kitContracts: {
|
|
2962
3131
|
bridge: BRIDGE_CONTRACT_EVM_MAINNET,
|
|
@@ -3001,6 +3170,10 @@ const UnichainSepolia = defineChain({
|
|
|
3001
3170
|
fastConfirmations: 1,
|
|
3002
3171
|
},
|
|
3003
3172
|
},
|
|
3173
|
+
forwarderSupported: {
|
|
3174
|
+
source: true,
|
|
3175
|
+
destination: true,
|
|
3176
|
+
},
|
|
3004
3177
|
},
|
|
3005
3178
|
kitContracts: {
|
|
3006
3179
|
bridge: BRIDGE_CONTRACT_EVM_TESTNET,
|
|
@@ -3027,7 +3200,7 @@ const WorldChain = defineChain({
|
|
|
3027
3200
|
explorerUrl: 'https://worldscan.org/tx/{hash}',
|
|
3028
3201
|
rpcEndpoints: ['https://worldchain-mainnet.g.alchemy.com/public'],
|
|
3029
3202
|
eurcAddress: null,
|
|
3030
|
-
usdcAddress: '
|
|
3203
|
+
usdcAddress: '0x79A02482A880bCE3F13e09Da970dC34db4CD24d1',
|
|
3031
3204
|
cctp: {
|
|
3032
3205
|
domain: 14,
|
|
3033
3206
|
contracts: {
|
|
@@ -3039,6 +3212,10 @@ const WorldChain = defineChain({
|
|
|
3039
3212
|
fastConfirmations: 1,
|
|
3040
3213
|
},
|
|
3041
3214
|
},
|
|
3215
|
+
forwarderSupported: {
|
|
3216
|
+
source: true,
|
|
3217
|
+
destination: true,
|
|
3218
|
+
},
|
|
3042
3219
|
},
|
|
3043
3220
|
kitContracts: {
|
|
3044
3221
|
bridge: BRIDGE_CONTRACT_EVM_MAINNET,
|
|
@@ -3080,6 +3257,10 @@ const WorldChainSepolia = defineChain({
|
|
|
3080
3257
|
fastConfirmations: 1,
|
|
3081
3258
|
},
|
|
3082
3259
|
},
|
|
3260
|
+
forwarderSupported: {
|
|
3261
|
+
source: true,
|
|
3262
|
+
destination: true,
|
|
3263
|
+
},
|
|
3083
3264
|
},
|
|
3084
3265
|
kitContracts: {
|
|
3085
3266
|
bridge: BRIDGE_CONTRACT_EVM_TESTNET,
|
|
@@ -3106,7 +3287,7 @@ const XDC = defineChain({
|
|
|
3106
3287
|
chainId: 50,
|
|
3107
3288
|
isTestnet: false,
|
|
3108
3289
|
explorerUrl: 'https://xdcscan.io/tx/{hash}',
|
|
3109
|
-
rpcEndpoints: ['https://erpc.xinfin.network'],
|
|
3290
|
+
rpcEndpoints: ['https://erpc.xdcrpc.com', 'https://erpc.xinfin.network'],
|
|
3110
3291
|
eurcAddress: null,
|
|
3111
3292
|
usdcAddress: '0xfA2958CB79b0491CC627c1557F441eF849Ca8eb1',
|
|
3112
3293
|
cctp: {
|
|
@@ -3120,6 +3301,10 @@ const XDC = defineChain({
|
|
|
3120
3301
|
fastConfirmations: 3,
|
|
3121
3302
|
},
|
|
3122
3303
|
},
|
|
3304
|
+
forwarderSupported: {
|
|
3305
|
+
source: true,
|
|
3306
|
+
destination: false,
|
|
3307
|
+
},
|
|
3123
3308
|
},
|
|
3124
3309
|
kitContracts: {
|
|
3125
3310
|
bridge: BRIDGE_CONTRACT_EVM_MAINNET,
|
|
@@ -3158,6 +3343,10 @@ const XDCApothem = defineChain({
|
|
|
3158
3343
|
fastConfirmations: 1,
|
|
3159
3344
|
},
|
|
3160
3345
|
},
|
|
3346
|
+
forwarderSupported: {
|
|
3347
|
+
source: true,
|
|
3348
|
+
destination: false,
|
|
3349
|
+
},
|
|
3161
3350
|
},
|
|
3162
3351
|
kitContracts: {
|
|
3163
3352
|
bridge: BRIDGE_CONTRACT_EVM_TESTNET,
|
|
@@ -3394,7 +3583,7 @@ const nonEvmChainDefinitionSchema = baseChainDefinitionSchema
|
|
|
3394
3583
|
* })
|
|
3395
3584
|
* ```
|
|
3396
3585
|
*/
|
|
3397
|
-
const chainDefinitionSchema$
|
|
3586
|
+
const chainDefinitionSchema$2 = zod.z.discriminatedUnion('type', [
|
|
3398
3587
|
evmChainDefinitionSchema,
|
|
3399
3588
|
nonEvmChainDefinitionSchema,
|
|
3400
3589
|
]);
|
|
@@ -3419,7 +3608,7 @@ zod.z.union([
|
|
|
3419
3608
|
.string()
|
|
3420
3609
|
.refine((val) => val in exports.Blockchain, 'Must be a valid Blockchain enum value as string'),
|
|
3421
3610
|
zod.z.nativeEnum(exports.Blockchain),
|
|
3422
|
-
chainDefinitionSchema$
|
|
3611
|
+
chainDefinitionSchema$2,
|
|
3423
3612
|
]);
|
|
3424
3613
|
/**
|
|
3425
3614
|
* Zod schema for validating bridge chain identifiers.
|
|
@@ -3455,7 +3644,7 @@ const bridgeChainIdentifierSchema = zod.z.union([
|
|
|
3455
3644
|
zod.z.string().refine((val) => val in exports.BridgeChain, (val) => ({
|
|
3456
3645
|
message: `Chain "${val}" is not supported for bridging. Only chains in the BridgeChain enum support CCTPv2 bridging.`,
|
|
3457
3646
|
})),
|
|
3458
|
-
chainDefinitionSchema$
|
|
3647
|
+
chainDefinitionSchema$2.refine((chainDef) => chainDef.chain in exports.BridgeChain, (chainDef) => ({
|
|
3459
3648
|
message: `Chain "${chainDef.name}" (${chainDef.chain}) is not supported for bridging. Only chains in the BridgeChain enum support CCTPv2 bridging.`,
|
|
3460
3649
|
})),
|
|
3461
3650
|
]);
|
|
@@ -3942,6 +4131,62 @@ function getErrorMessage(error) {
|
|
|
3942
4131
|
function getErrorCode(error) {
|
|
3943
4132
|
return isKitError(error) ? error.code : null;
|
|
3944
4133
|
}
|
|
4134
|
+
/**
|
|
4135
|
+
* Extract structured error information for logging and events.
|
|
4136
|
+
*
|
|
4137
|
+
* @remarks
|
|
4138
|
+
* Safely extracts error information from any thrown value, handling:
|
|
4139
|
+
* - KitError objects (message preserved - safe to log)
|
|
4140
|
+
* - Standard Error objects (message redacted for security)
|
|
4141
|
+
* - Plain objects with name/message/code (message redacted)
|
|
4142
|
+
* - Primitive values (logged as-is since they contain no structured data)
|
|
4143
|
+
*
|
|
4144
|
+
* For security, only KitError messages are included. Other error messages are
|
|
4145
|
+
* redacted to prevent logging sensitive data like tokens or PII.
|
|
4146
|
+
*
|
|
4147
|
+
* @param error - The error to extract info from.
|
|
4148
|
+
* @returns Structured error information.
|
|
4149
|
+
*
|
|
4150
|
+
* @example
|
|
4151
|
+
* ```typescript
|
|
4152
|
+
* import { extractErrorInfo } from '@core/errors'
|
|
4153
|
+
*
|
|
4154
|
+
* // Standard Error - message is redacted for security
|
|
4155
|
+
* const info1 = extractErrorInfo(new Error('Something went wrong'))
|
|
4156
|
+
* // { name: 'Error', message: 'An error occurred. See error name and code for details.' }
|
|
4157
|
+
*
|
|
4158
|
+
* // KitError - message is preserved (safe type)
|
|
4159
|
+
* const error = createNetworkConnectionError('Ethereum')
|
|
4160
|
+
* const info2 = extractErrorInfo(error)
|
|
4161
|
+
* // { name: 'NETWORK_CONNECTION_FAILED', message: 'Network connection failed for Ethereum', code: 3001 }
|
|
4162
|
+
*
|
|
4163
|
+
* // Primitive value - logged as-is
|
|
4164
|
+
* const info3 = extractErrorInfo('string error')
|
|
4165
|
+
* // { name: 'UnknownError', message: 'string error' }
|
|
4166
|
+
* ```
|
|
4167
|
+
*/
|
|
4168
|
+
function extractErrorInfo(error) {
|
|
4169
|
+
if (error === null || typeof error !== 'object') {
|
|
4170
|
+
return {
|
|
4171
|
+
name: 'UnknownError',
|
|
4172
|
+
message: String(error),
|
|
4173
|
+
};
|
|
4174
|
+
}
|
|
4175
|
+
const err = error;
|
|
4176
|
+
// Only preserve messages for KitError instances (safe type)
|
|
4177
|
+
// For other errors, redact message for security
|
|
4178
|
+
const errorMessage = isKitError(error)
|
|
4179
|
+
? getErrorMessage(error)
|
|
4180
|
+
: 'An error occurred. See error name and code for details.';
|
|
4181
|
+
const info = {
|
|
4182
|
+
name: err.name ?? 'UnknownError',
|
|
4183
|
+
message: errorMessage,
|
|
4184
|
+
};
|
|
4185
|
+
if (typeof err.code === 'number') {
|
|
4186
|
+
info.code = err.code;
|
|
4187
|
+
}
|
|
4188
|
+
return info;
|
|
4189
|
+
}
|
|
3945
4190
|
|
|
3946
4191
|
/**
|
|
3947
4192
|
* Validates if an address format is correct for the specified chain.
|
|
@@ -4486,7 +4731,7 @@ function validateWithStateTracking(value, schema, context, validatorName) {
|
|
|
4486
4731
|
* Zod schema for validating chain definition objects used in buildExplorerUrl.
|
|
4487
4732
|
* This schema ensures the chain definition has the required properties for URL generation.
|
|
4488
4733
|
*/
|
|
4489
|
-
const chainDefinitionSchema = zod.z.object({
|
|
4734
|
+
const chainDefinitionSchema$1 = zod.z.object({
|
|
4490
4735
|
name: zod.z
|
|
4491
4736
|
.string({
|
|
4492
4737
|
required_error: 'Chain name is required',
|
|
@@ -4510,15 +4755,14 @@ const transactionHashSchema = zod.z
|
|
|
4510
4755
|
required_error: 'Transaction hash is required',
|
|
4511
4756
|
invalid_type_error: 'Transaction hash must be a string',
|
|
4512
4757
|
})
|
|
4513
|
-
.
|
|
4514
|
-
.
|
|
4515
|
-
.refine((hash) => hash.length > 0, 'Transaction hash must not be empty or whitespace-only');
|
|
4758
|
+
.transform((hash) => hash.trim())
|
|
4759
|
+
.refine((hash) => hash.length > 0, 'Transaction hash cannot be empty');
|
|
4516
4760
|
/**
|
|
4517
4761
|
* Zod schema for validating buildExplorerUrl function parameters.
|
|
4518
4762
|
* This schema validates both the chain definition and transaction hash together.
|
|
4519
4763
|
*/
|
|
4520
4764
|
zod.z.object({
|
|
4521
|
-
chainDef: chainDefinitionSchema,
|
|
4765
|
+
chainDef: chainDefinitionSchema$1,
|
|
4522
4766
|
txHash: transactionHashSchema,
|
|
4523
4767
|
});
|
|
4524
4768
|
/**
|
|
@@ -4529,6 +4773,69 @@ zod.z
|
|
|
4529
4773
|
.string()
|
|
4530
4774
|
.url('Generated explorer URL is invalid');
|
|
4531
4775
|
|
|
4776
|
+
/**
|
|
4777
|
+
* Parses and validates data against a Zod schema, returning the transformed result.
|
|
4778
|
+
*
|
|
4779
|
+
* Unlike `validate` and `validateOrThrow` which use type assertions (`asserts value is T`),
|
|
4780
|
+
* this function **returns the parsed data** from Zod. This is important when your schema
|
|
4781
|
+
* includes transformations like defaults, coercion, or custom transforms.
|
|
4782
|
+
*
|
|
4783
|
+
* @typeParam T - The expected output type (caller must specify).
|
|
4784
|
+
* @param value - The value to parse and validate.
|
|
4785
|
+
* @param schema - The Zod schema to validate against.
|
|
4786
|
+
* @param context - Context string to include in error messages (e.g., 'user input', 'config').
|
|
4787
|
+
* @returns The parsed and transformed data of type T.
|
|
4788
|
+
* @throws KitError with INPUT_VALIDATION_FAILED code (1098) if validation fails.
|
|
4789
|
+
*
|
|
4790
|
+
* @remarks
|
|
4791
|
+
* This function uses `ZodTypeAny` for the schema parameter to avoid TypeScript's
|
|
4792
|
+
* "excessively deep type instantiation" error in generic contexts. The caller
|
|
4793
|
+
* must provide the expected type `T` explicitly.
|
|
4794
|
+
*
|
|
4795
|
+
* Use this instead of `validate`/`validateOrThrow` when:
|
|
4796
|
+
* - Your schema has `.default()` values
|
|
4797
|
+
* - Your schema uses `.coerce` (e.g., `z.coerce.number()`)
|
|
4798
|
+
* - Your schema has `.transform()` functions
|
|
4799
|
+
* - You need the actual parsed output, not just type narrowing
|
|
4800
|
+
*
|
|
4801
|
+
* @example
|
|
4802
|
+
* ```typescript
|
|
4803
|
+
* import { parseOrThrow } from '@core/utils'
|
|
4804
|
+
* import { z } from 'zod'
|
|
4805
|
+
*
|
|
4806
|
+
* const userSchema = z.object({
|
|
4807
|
+
* name: z.string().default('Anonymous'),
|
|
4808
|
+
* age: z.coerce.number(), // Transforms "25" → 25
|
|
4809
|
+
* })
|
|
4810
|
+
*
|
|
4811
|
+
* type User = z.infer<typeof userSchema>
|
|
4812
|
+
*
|
|
4813
|
+
* // Explicit type parameter
|
|
4814
|
+
* const user = parseOrThrow<User>({ age: '25' }, userSchema, 'user data')
|
|
4815
|
+
* console.log(user) // { name: 'Anonymous', age: 25 }
|
|
4816
|
+
* ```
|
|
4817
|
+
*
|
|
4818
|
+
* @example
|
|
4819
|
+
* ```typescript
|
|
4820
|
+
* // Usage in generic adapter functions (no deep type instantiation)
|
|
4821
|
+
* function validateInput<TInput>(
|
|
4822
|
+
* input: unknown,
|
|
4823
|
+
* schema: z.ZodTypeAny,
|
|
4824
|
+
* name: string,
|
|
4825
|
+
* ): TInput {
|
|
4826
|
+
* return parseOrThrow<TInput>(input, schema, `${name} input`)
|
|
4827
|
+
* }
|
|
4828
|
+
* ```
|
|
4829
|
+
*/
|
|
4830
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-parameters -- Intentional: T is caller-provided to avoid deep type instantiation from schema inference
|
|
4831
|
+
function parseOrThrow(value, schema, context) {
|
|
4832
|
+
const result = schema.safeParse(value);
|
|
4833
|
+
if (!result.success) {
|
|
4834
|
+
throw createValidationErrorFromZod(result.error, context);
|
|
4835
|
+
}
|
|
4836
|
+
return result.data;
|
|
4837
|
+
}
|
|
4838
|
+
|
|
4532
4839
|
/**
|
|
4533
4840
|
* A type-safe event emitter for managing action-based event subscriptions.
|
|
4534
4841
|
*
|
|
@@ -4803,7 +5110,7 @@ const parseAmount = (params) => {
|
|
|
4803
5110
|
};
|
|
4804
5111
|
|
|
4805
5112
|
var name = "@circle-fin/bridge-kit";
|
|
4806
|
-
var version = "1.
|
|
5113
|
+
var version = "1.6.0";
|
|
4807
5114
|
var pkg = {
|
|
4808
5115
|
name: name,
|
|
4809
5116
|
version: version};
|
|
@@ -5229,7 +5536,7 @@ const createDecimalStringValidator = (options) => (schema) => {
|
|
|
5229
5536
|
* console.log(result.success) // true
|
|
5230
5537
|
* ```
|
|
5231
5538
|
*/
|
|
5232
|
-
zod.z.object({
|
|
5539
|
+
const chainDefinitionSchema = zod.z.object({
|
|
5233
5540
|
name: zod.z.string().min(1, 'Chain name is required'),
|
|
5234
5541
|
type: zod.z.string().min(1, 'Chain type is required'),
|
|
5235
5542
|
});
|
|
@@ -5276,6 +5583,53 @@ const walletContextSchema = zod.z.object({
|
|
|
5276
5583
|
isTestnet: zod.z.boolean(),
|
|
5277
5584
|
}),
|
|
5278
5585
|
});
|
|
5586
|
+
/**
|
|
5587
|
+
* Schema for validating destination wallet contexts.
|
|
5588
|
+
* Extends walletContextSchema with optional useForwarder flag.
|
|
5589
|
+
*
|
|
5590
|
+
* @throws KitError if validation fails
|
|
5591
|
+
*/
|
|
5592
|
+
const destinationContextSchema = walletContextSchema.extend({
|
|
5593
|
+
useForwarder: zod.z.boolean().optional(),
|
|
5594
|
+
recipientAddress: zod.z.string().optional(),
|
|
5595
|
+
});
|
|
5596
|
+
/**
|
|
5597
|
+
* Schema for validating forwarder-only destination contexts.
|
|
5598
|
+
* Used when useForwarder is true and no adapter is provided.
|
|
5599
|
+
* Requires chain definition and recipientAddress.
|
|
5600
|
+
*
|
|
5601
|
+
* Validates that recipientAddress format is correct for the destination chain
|
|
5602
|
+
* (EVM hex address or Solana base58 address).
|
|
5603
|
+
*
|
|
5604
|
+
* @throws KitError if validation fails
|
|
5605
|
+
*/
|
|
5606
|
+
const forwarderOnlyDestinationSchema = zod.z
|
|
5607
|
+
.object({
|
|
5608
|
+
chain: chainDefinitionSchema.extend({
|
|
5609
|
+
isTestnet: zod.z.boolean(),
|
|
5610
|
+
}),
|
|
5611
|
+
recipientAddress: zod.z
|
|
5612
|
+
.string()
|
|
5613
|
+
.min(1, 'Recipient address is required for forwarder-only destination'),
|
|
5614
|
+
useForwarder: zod.z.literal(true),
|
|
5615
|
+
})
|
|
5616
|
+
.superRefine((data, ctx) => {
|
|
5617
|
+
// Pass chain name as string - isValidAddressForChain will use name-based matching
|
|
5618
|
+
if (!isValidAddressForChain(data.recipientAddress, data.chain.name)) {
|
|
5619
|
+
ctx.addIssue({
|
|
5620
|
+
code: zod.z.ZodIssueCode.custom,
|
|
5621
|
+
message: `Invalid recipient address format for chain ${data.chain.name}`,
|
|
5622
|
+
path: ['recipientAddress'],
|
|
5623
|
+
});
|
|
5624
|
+
}
|
|
5625
|
+
});
|
|
5626
|
+
/**
|
|
5627
|
+
* Schema for validating any destination - either with adapter or forwarder-only.
|
|
5628
|
+
*/
|
|
5629
|
+
const bridgeDestinationSchema$1 = zod.z.union([
|
|
5630
|
+
destinationContextSchema,
|
|
5631
|
+
forwarderOnlyDestinationSchema,
|
|
5632
|
+
]);
|
|
5279
5633
|
/**
|
|
5280
5634
|
* Schema for validating a custom fee configuration.
|
|
5281
5635
|
* Validates the simplified CustomFee interface which includes:
|
|
@@ -5373,7 +5727,7 @@ zod.z.object({
|
|
|
5373
5727
|
maxDecimals: 6,
|
|
5374
5728
|
})(zod.z.string())),
|
|
5375
5729
|
source: walletContextSchema,
|
|
5376
|
-
destination:
|
|
5730
|
+
destination: bridgeDestinationSchema$1,
|
|
5377
5731
|
token: zod.z.literal('USDC'),
|
|
5378
5732
|
config: zod.z.object({
|
|
5379
5733
|
transferSpeed: zod.z.nativeEnum(exports.TransferSpeed).optional(),
|
|
@@ -5390,6 +5744,28 @@ zod.z.object({
|
|
|
5390
5744
|
}),
|
|
5391
5745
|
});
|
|
5392
5746
|
|
|
5747
|
+
/**
|
|
5748
|
+
* Creates a Zod superRefine validator for recipient address format validation.
|
|
5749
|
+
* Validates that the address format matches the expected format for the chain type.
|
|
5750
|
+
*
|
|
5751
|
+
* @returns A superRefine function that validates recipientAddress against chain type
|
|
5752
|
+
*/
|
|
5753
|
+
function createRecipientAddressValidator() {
|
|
5754
|
+
return (data, ctx) => {
|
|
5755
|
+
const chain = data.chain;
|
|
5756
|
+
if (chain === null) {
|
|
5757
|
+
return;
|
|
5758
|
+
}
|
|
5759
|
+
if (!isValidAddressForChain(data.recipientAddress, chain)) {
|
|
5760
|
+
const chainInfo = extractChainInfo(chain);
|
|
5761
|
+
ctx.addIssue({
|
|
5762
|
+
code: zod.z.ZodIssueCode.custom,
|
|
5763
|
+
path: ['recipientAddress'],
|
|
5764
|
+
message: `Invalid address format for ${String(chainInfo.name)}. Expected ${chainInfo.expectedAddressFormat}, but received: ${data.recipientAddress}`,
|
|
5765
|
+
});
|
|
5766
|
+
}
|
|
5767
|
+
};
|
|
5768
|
+
}
|
|
5393
5769
|
/**
|
|
5394
5770
|
* Schema for validating AdapterContext for bridge operations.
|
|
5395
5771
|
* Must always contain both adapter and chain explicitly.
|
|
@@ -5409,32 +5785,52 @@ const adapterContextSchema = zod.z.object({
|
|
|
5409
5785
|
const bridgeDestinationWithAddressSchema = adapterContextSchema
|
|
5410
5786
|
.extend({
|
|
5411
5787
|
recipientAddress: zod.z.string().min(1, 'Recipient address is required'),
|
|
5788
|
+
useForwarder: zod.z.boolean().optional(),
|
|
5412
5789
|
})
|
|
5413
|
-
.superRefine((
|
|
5414
|
-
|
|
5415
|
-
|
|
5416
|
-
|
|
5417
|
-
|
|
5418
|
-
|
|
5419
|
-
|
|
5420
|
-
ctx.addIssue({
|
|
5421
|
-
code: zod.z.ZodIssueCode.custom,
|
|
5422
|
-
path: ['recipientAddress'],
|
|
5423
|
-
message: `Invalid address format for ${String(chainInfo.name)}. Expected ${chainInfo.expectedAddressFormat}, but received: ${data.recipientAddress}`,
|
|
5424
|
-
});
|
|
5425
|
-
}
|
|
5790
|
+
.superRefine(createRecipientAddressValidator());
|
|
5791
|
+
/**
|
|
5792
|
+
* Schema for validating AdapterContext with optional useForwarder.
|
|
5793
|
+
* Extends adapterContextSchema with the useForwarder flag.
|
|
5794
|
+
*/
|
|
5795
|
+
const adapterContextWithForwarderSchema = adapterContextSchema.extend({
|
|
5796
|
+
useForwarder: zod.z.boolean().optional(),
|
|
5426
5797
|
});
|
|
5798
|
+
/**
|
|
5799
|
+
* Schema for validating ForwarderDestination objects.
|
|
5800
|
+
* Used when useForwarder is true and no adapter is provided.
|
|
5801
|
+
* Requires chain, recipientAddress, and useForwarder: true.
|
|
5802
|
+
*
|
|
5803
|
+
* When using this destination type:
|
|
5804
|
+
* - The mint step completes when the IRIS API confirms forwardState === 'CONFIRMED'
|
|
5805
|
+
* - No on-chain transaction confirmation is performed (no adapter available)
|
|
5806
|
+
* - The mint step's data field will be undefined (no transaction receipt)
|
|
5807
|
+
*/
|
|
5808
|
+
const forwarderDestinationSchema = zod.z
|
|
5809
|
+
.object({
|
|
5810
|
+
chain: bridgeChainIdentifierSchema,
|
|
5811
|
+
recipientAddress: zod.z.string().min(1, 'Recipient address is required'),
|
|
5812
|
+
useForwarder: zod.z.literal(true),
|
|
5813
|
+
})
|
|
5814
|
+
.strict()
|
|
5815
|
+
.superRefine(createRecipientAddressValidator());
|
|
5427
5816
|
/**
|
|
5428
5817
|
* Schema for validating BridgeDestination union type.
|
|
5429
|
-
*
|
|
5818
|
+
* Supports three destination configurations:
|
|
5819
|
+
* - BridgeDestinationWithAddress (adapter with explicit recipient)
|
|
5820
|
+
* - ForwarderDestination (no adapter, requires useForwarder: true and recipientAddress)
|
|
5821
|
+
* - AdapterContext with optional useForwarder (adapter for default recipient)
|
|
5822
|
+
*
|
|
5823
|
+
* When using ForwarderDestination (no adapter):
|
|
5824
|
+
* - The mint step completes when the IRIS API confirms forwardState === 'CONFIRMED'
|
|
5825
|
+
* - No on-chain transaction confirmation is performed
|
|
5430
5826
|
*
|
|
5431
|
-
* The order matters: we check the more specific
|
|
5432
|
-
* This ensures that objects with
|
|
5433
|
-
* than silently treated as AdapterContext with the field ignored.
|
|
5827
|
+
* The order matters: we check the more specific schemas first.
|
|
5828
|
+
* This ensures that objects with specific fields are matched correctly.
|
|
5434
5829
|
*/
|
|
5435
5830
|
const bridgeDestinationSchema = zod.z.union([
|
|
5436
5831
|
bridgeDestinationWithAddressSchema,
|
|
5437
|
-
|
|
5832
|
+
forwarderDestinationSchema,
|
|
5833
|
+
adapterContextWithForwarderSchema.strict(),
|
|
5438
5834
|
]);
|
|
5439
5835
|
/**
|
|
5440
5836
|
* Schema for validating bridge parameters with chain identifiers.
|
|
@@ -5508,119 +5904,1411 @@ const bridgeParamsWithChainIdentifierSchema = zod.z.object({
|
|
|
5508
5904
|
});
|
|
5509
5905
|
|
|
5510
5906
|
/**
|
|
5511
|
-
*
|
|
5512
|
-
*
|
|
5513
|
-
* Both AdapterContext and BridgeDestinationWithAddress have the chain property
|
|
5514
|
-
* at the top level, so we can directly access it from either type.
|
|
5907
|
+
* Default clock implementation using `Date.now()`.
|
|
5515
5908
|
*
|
|
5516
|
-
* @
|
|
5517
|
-
*
|
|
5518
|
-
*
|
|
5909
|
+
* @remarks
|
|
5910
|
+
* Use this in production code. For testing, inject a mock clock
|
|
5911
|
+
* that returns controlled timestamps.
|
|
5519
5912
|
*
|
|
5520
5913
|
* @example
|
|
5521
5914
|
* ```typescript
|
|
5522
|
-
* import {
|
|
5523
|
-
*
|
|
5524
|
-
* // AdapterContext
|
|
5525
|
-
* const chain1 = resolveChainDefinition({
|
|
5526
|
-
* adapter: mockAdapter,
|
|
5527
|
-
* chain: 'Ethereum'
|
|
5528
|
-
* })
|
|
5915
|
+
* import { defaultClock } from '@core/runtime'
|
|
5529
5916
|
*
|
|
5530
|
-
*
|
|
5531
|
-
*
|
|
5532
|
-
*
|
|
5533
|
-
* chain: 'Base',
|
|
5534
|
-
* recipientAddress: '0x123...'
|
|
5535
|
-
* })
|
|
5917
|
+
* const start = defaultClock.now()
|
|
5918
|
+
* // ... do work ...
|
|
5919
|
+
* const elapsed = defaultClock.since(start)
|
|
5536
5920
|
* ```
|
|
5537
5921
|
*/
|
|
5538
|
-
|
|
5539
|
-
|
|
5540
|
-
|
|
5922
|
+
const defaultClock = {
|
|
5923
|
+
now: () => Date.now(),
|
|
5924
|
+
since: (start) => Date.now() - start,
|
|
5925
|
+
};
|
|
5926
|
+
|
|
5541
5927
|
/**
|
|
5542
|
-
*
|
|
5928
|
+
* ID generation utilities for distributed tracing.
|
|
5543
5929
|
*
|
|
5544
|
-
*
|
|
5545
|
-
*
|
|
5930
|
+
* @remarks
|
|
5931
|
+
* | Function | Format | Use Case |
|
|
5932
|
+
* |----------|--------|----------|
|
|
5933
|
+
* | {@link createTraceId} | 32-char hex | Standard traceId (OpenTelemetry) |
|
|
5934
|
+
* | {@link createShortOpId} | 8-char hex | Standard opId |
|
|
5935
|
+
* | {@link createOpId} | `op-{ts}-{rand}` | Timestamped operation IDs |
|
|
5936
|
+
* | {@link createUuidV4} | RFC 4122 UUID | External system compatibility |
|
|
5937
|
+
* | {@link createId} | `{prefix}-{ts}-{rand}` | Custom prefixed IDs |
|
|
5546
5938
|
*
|
|
5547
|
-
*
|
|
5548
|
-
|
|
5549
|
-
|
|
5939
|
+
* @packageDocumentation
|
|
5940
|
+
*/
|
|
5941
|
+
// ============================================================================
|
|
5942
|
+
// Crypto Utilities (Internal)
|
|
5943
|
+
// ============================================================================
|
|
5944
|
+
/** @internal */
|
|
5945
|
+
function hasGetRandomValues() {
|
|
5946
|
+
return (typeof crypto !== 'undefined' &&
|
|
5947
|
+
typeof crypto.getRandomValues === 'function');
|
|
5948
|
+
}
|
|
5949
|
+
/**
|
|
5950
|
+
* Create a W3C/OpenTelemetry-compatible trace ID.
|
|
5550
5951
|
*
|
|
5551
|
-
* @
|
|
5552
|
-
*
|
|
5952
|
+
* @returns 32-character lowercase hex string (128-bit).
|
|
5953
|
+
*
|
|
5954
|
+
* @remarks
|
|
5955
|
+
* **Standard function for generating `traceId` values.** Compatible with
|
|
5956
|
+
* OpenTelemetry, Jaeger, Zipkin, and AWS X-Ray.
|
|
5553
5957
|
*
|
|
5554
5958
|
* @example
|
|
5555
5959
|
* ```typescript
|
|
5556
|
-
* //
|
|
5557
|
-
* const addr1 = await resolveAddress({
|
|
5558
|
-
* adapter: devAdapter,
|
|
5559
|
-
* chain: 'Ethereum',
|
|
5560
|
-
* address: '0x1234567890123456789012345678901234567890'
|
|
5561
|
-
* }) // Returns: '0x1234567890123456789012345678901234567890'
|
|
5562
|
-
*
|
|
5563
|
-
* // User-controlled adapter
|
|
5564
|
-
* const addr2 = await resolveAddress({
|
|
5565
|
-
* adapter: userAdapter,
|
|
5566
|
-
* chain: 'Ethereum'
|
|
5567
|
-
* }) // Returns adapter's connected address
|
|
5960
|
+
* const traceId = createTraceId() // "a1b2c3d4e5f6789012345678abcdef00"
|
|
5568
5961
|
* ```
|
|
5569
5962
|
*/
|
|
5570
|
-
|
|
5571
|
-
|
|
5572
|
-
if (
|
|
5573
|
-
|
|
5574
|
-
if ('address' in ctx && ctx.address) {
|
|
5575
|
-
return ctx.address;
|
|
5576
|
-
}
|
|
5577
|
-
throw new Error('Address is required in context for developer-controlled adapters. ' +
|
|
5578
|
-
'Please provide: { adapter, chain, address: "0x..." }');
|
|
5963
|
+
function createTraceId() {
|
|
5964
|
+
const bytes = new Uint8Array(16);
|
|
5965
|
+
if (hasGetRandomValues()) {
|
|
5966
|
+
crypto.getRandomValues(bytes);
|
|
5579
5967
|
}
|
|
5580
5968
|
else {
|
|
5581
|
-
|
|
5582
|
-
|
|
5583
|
-
throw new Error('Address should not be provided for user-controlled adapters. ' +
|
|
5584
|
-
'The address is automatically resolved from the connected wallet.');
|
|
5969
|
+
for (let i = 0; i < 16; i++) {
|
|
5970
|
+
bytes[i] = Math.floor(Math.random() * 256); // NOSONAR:
|
|
5585
5971
|
}
|
|
5586
|
-
// Derive address from adapter
|
|
5587
|
-
const chain = resolveChainDefinition(ctx);
|
|
5588
|
-
return await ctx.adapter.getAddress(chain);
|
|
5589
5972
|
}
|
|
5973
|
+
return Array.from(bytes, (b) => b.toString(16).padStart(2, '0')).join('');
|
|
5590
5974
|
}
|
|
5975
|
+
|
|
5591
5976
|
/**
|
|
5592
|
-
*
|
|
5977
|
+
* Clean tags by removing keys with undefined values.
|
|
5593
5978
|
*
|
|
5594
|
-
*
|
|
5595
|
-
*
|
|
5596
|
-
* USDC (6 decimals) and falls back to the raw amount for other tokens.
|
|
5979
|
+
* @param tags - Tags object, may contain undefined values. Accepts `undefined` or `null`.
|
|
5980
|
+
* @returns A new object with undefined values removed, or empty object if input is nullish.
|
|
5597
5981
|
*
|
|
5598
|
-
* @
|
|
5599
|
-
*
|
|
5982
|
+
* @remarks
|
|
5983
|
+
* This helper ensures tags are safe for serialization and logging.
|
|
5984
|
+
* Similar to the internal `cleanUndefined` in the logger module, but
|
|
5985
|
+
* typed specifically for {@link Tags}.
|
|
5600
5986
|
*
|
|
5601
5987
|
* @example
|
|
5602
5988
|
* ```typescript
|
|
5603
|
-
* import {
|
|
5989
|
+
* import { cleanTags } from '@core/runtime'
|
|
5604
5990
|
*
|
|
5605
|
-
* const
|
|
5606
|
-
*
|
|
5607
|
-
*
|
|
5608
|
-
*
|
|
5609
|
-
*
|
|
5610
|
-
* }
|
|
5611
|
-
* const formattedAmount = resolveAmount(params) // Returns '1000000000000'
|
|
5991
|
+
* const tags = { chain: 'Ethereum', amount: 100, extra: undefined }
|
|
5992
|
+
* const cleaned = cleanTags(tags)
|
|
5993
|
+
* // { chain: 'Ethereum', amount: 100 }
|
|
5994
|
+
*
|
|
5995
|
+
* const empty = cleanTags(undefined)
|
|
5996
|
+
* // {}
|
|
5612
5997
|
* ```
|
|
5613
5998
|
*/
|
|
5614
|
-
function
|
|
5615
|
-
if (
|
|
5616
|
-
return
|
|
5999
|
+
function cleanTags(tags) {
|
|
6000
|
+
if (tags == null)
|
|
6001
|
+
return {};
|
|
6002
|
+
return Object.fromEntries(Object.entries(tags).filter(([, value]) => value !== undefined));
|
|
6003
|
+
}
|
|
6004
|
+
|
|
6005
|
+
/**
|
|
6006
|
+
* Check if a pattern is valid.
|
|
6007
|
+
*
|
|
6008
|
+
* @remarks
|
|
6009
|
+
* Invalid patterns:
|
|
6010
|
+
* - Empty segments (e.g., `a..b`, `.a`, `a.`)
|
|
6011
|
+
* - `**` not at end (e.g., `a.**.b`)
|
|
6012
|
+
*
|
|
6013
|
+
* @param pattern - The pattern to validate.
|
|
6014
|
+
* @returns True if valid, false otherwise.
|
|
6015
|
+
*/
|
|
6016
|
+
function isValidPattern(pattern) {
|
|
6017
|
+
// Empty pattern is valid (matches empty topic)
|
|
6018
|
+
if (pattern === '')
|
|
6019
|
+
return true;
|
|
6020
|
+
const segments = pattern.split('.');
|
|
6021
|
+
// Check for empty segments
|
|
6022
|
+
if (segments.includes(''))
|
|
6023
|
+
return false;
|
|
6024
|
+
// Check that ** only appears at the end
|
|
6025
|
+
const doubleStarIndex = segments.indexOf('**');
|
|
6026
|
+
if (doubleStarIndex !== -1 && doubleStarIndex !== segments.length - 1) {
|
|
6027
|
+
return false;
|
|
5617
6028
|
}
|
|
5618
|
-
return
|
|
6029
|
+
return true;
|
|
5619
6030
|
}
|
|
5620
6031
|
/**
|
|
5621
|
-
*
|
|
6032
|
+
* Convert a pattern to a regular expression.
|
|
6033
|
+
*
|
|
6034
|
+
* @param pattern - The pattern to convert.
|
|
6035
|
+
* @returns A RegExp that matches topics according to the pattern.
|
|
6036
|
+
*/
|
|
6037
|
+
function patternToRegex(pattern) {
|
|
6038
|
+
const segments = pattern.split('.');
|
|
6039
|
+
// Handle ** at end specially to match zero or more segments
|
|
6040
|
+
const lastSegment = segments.at(-1);
|
|
6041
|
+
if (lastSegment === '**') {
|
|
6042
|
+
// Everything before ** must match, then optionally more segments
|
|
6043
|
+
const prefix = segments
|
|
6044
|
+
.slice(0, -1)
|
|
6045
|
+
.map((seg) => {
|
|
6046
|
+
if (seg === '*')
|
|
6047
|
+
return '[^.]+';
|
|
6048
|
+
return seg.replaceAll(/[.*+?^${}()|[\]\\]/g, String.raw `\$&`);
|
|
6049
|
+
})
|
|
6050
|
+
.join(String.raw `\.`);
|
|
6051
|
+
// ** matches zero or more segments:
|
|
6052
|
+
// - If prefix is empty (**), match anything
|
|
6053
|
+
// - Otherwise, match prefix, then optionally (dot + more content)
|
|
6054
|
+
if (prefix === '') {
|
|
6055
|
+
return /^.*$/;
|
|
6056
|
+
}
|
|
6057
|
+
return new RegExp(String.raw `^${prefix}(?:\..*)?$`);
|
|
6058
|
+
}
|
|
6059
|
+
// No ** - just convert segments normally
|
|
6060
|
+
const escaped = segments
|
|
6061
|
+
.map((segment) => {
|
|
6062
|
+
if (segment === '*')
|
|
6063
|
+
return '[^.]+';
|
|
6064
|
+
return segment.replaceAll(/[.*+?^${}()|[\]\\]/g, String.raw `\$&`);
|
|
6065
|
+
})
|
|
6066
|
+
.join(String.raw `\.`);
|
|
6067
|
+
return new RegExp(String.raw `^${escaped}$`);
|
|
6068
|
+
}
|
|
6069
|
+
/**
|
|
6070
|
+
* Execute an event handler with error isolation.
|
|
5622
6071
|
*
|
|
5623
|
-
*
|
|
6072
|
+
* @param handler - The handler function to execute.
|
|
6073
|
+
* @param event - The event to pass to the handler.
|
|
6074
|
+
* @param logger - Optional logger for error reporting.
|
|
6075
|
+
*
|
|
6076
|
+
* @remarks
|
|
6077
|
+
* - Synchronous errors are caught and logged
|
|
6078
|
+
* - Promise rejections are caught without awaiting
|
|
6079
|
+
* - Handler errors never propagate to caller
|
|
6080
|
+
*/
|
|
6081
|
+
function executeHandler(handler, event, logger) {
|
|
6082
|
+
try {
|
|
6083
|
+
const result = handler(event);
|
|
6084
|
+
// Handle promise rejection without awaiting
|
|
6085
|
+
if (result instanceof Promise) {
|
|
6086
|
+
result.catch((err) => {
|
|
6087
|
+
logger?.error('Event handler promise rejected', {
|
|
6088
|
+
event: event.name,
|
|
6089
|
+
error: extractErrorInfo(err),
|
|
6090
|
+
});
|
|
6091
|
+
});
|
|
6092
|
+
}
|
|
6093
|
+
}
|
|
6094
|
+
catch (err) {
|
|
6095
|
+
// Isolate handler errors
|
|
6096
|
+
logger?.error('Event handler threw', {
|
|
6097
|
+
event: event.name,
|
|
6098
|
+
error: extractErrorInfo(err),
|
|
6099
|
+
});
|
|
6100
|
+
}
|
|
6101
|
+
}
|
|
6102
|
+
/**
|
|
6103
|
+
* Create an event bus.
|
|
6104
|
+
*
|
|
6105
|
+
* @param opts - Options for the event bus.
|
|
6106
|
+
* @returns An EventBus instance.
|
|
6107
|
+
*
|
|
6108
|
+
* @example
|
|
6109
|
+
* ```typescript
|
|
6110
|
+
* import { createEventBus } from '@core/runtime'
|
|
6111
|
+
*
|
|
6112
|
+
* const bus = createEventBus({ logger })
|
|
6113
|
+
*
|
|
6114
|
+
* // Subscribe to events
|
|
6115
|
+
* const unsubscribe = bus.on('tx.*', (event) => {
|
|
6116
|
+
* console.log('Transaction event:', event.name)
|
|
6117
|
+
* })
|
|
6118
|
+
*
|
|
6119
|
+
* // Emit events
|
|
6120
|
+
* bus.emit({ name: 'tx.sent', data: { txHash: '0x123' } })
|
|
6121
|
+
*
|
|
6122
|
+
* // Unsubscribe
|
|
6123
|
+
* unsubscribe()
|
|
6124
|
+
* ```
|
|
6125
|
+
*/
|
|
6126
|
+
function createEventBus(opts) {
|
|
6127
|
+
const logger = opts?.logger;
|
|
6128
|
+
const subscriptions = new Set();
|
|
6129
|
+
function createBus(baseTags) {
|
|
6130
|
+
return {
|
|
6131
|
+
emit(event) {
|
|
6132
|
+
// Invalid topic names silently don't match any subscriptions
|
|
6133
|
+
if (!isValidPattern(event.name))
|
|
6134
|
+
return;
|
|
6135
|
+
const mergedTags = Object.keys(baseTags).length > 0 || event.tags
|
|
6136
|
+
? { ...baseTags, ...cleanTags(event.tags) }
|
|
6137
|
+
: undefined;
|
|
6138
|
+
const finalEvent = mergedTags === undefined ? event : { ...event, tags: mergedTags };
|
|
6139
|
+
// Notify all matching subscribers
|
|
6140
|
+
for (const sub of subscriptions) {
|
|
6141
|
+
// Match-all subscription
|
|
6142
|
+
if (sub.pattern === null) {
|
|
6143
|
+
executeHandler(sub.handler, finalEvent, logger);
|
|
6144
|
+
continue;
|
|
6145
|
+
}
|
|
6146
|
+
// Patterned subscription: invalid pattern => regex=null => never match
|
|
6147
|
+
if (sub.regex === null)
|
|
6148
|
+
continue;
|
|
6149
|
+
if (!sub.regex.test(event.name))
|
|
6150
|
+
continue;
|
|
6151
|
+
executeHandler(sub.handler, finalEvent, logger);
|
|
6152
|
+
}
|
|
6153
|
+
},
|
|
6154
|
+
child(tags) {
|
|
6155
|
+
// Merge and clean tags, don't mutate parent
|
|
6156
|
+
const childTags = { ...baseTags, ...cleanTags(tags) };
|
|
6157
|
+
return createBus(childTags);
|
|
6158
|
+
},
|
|
6159
|
+
on(patternOrHandler, handler) {
|
|
6160
|
+
let sub;
|
|
6161
|
+
if (typeof patternOrHandler === 'function') {
|
|
6162
|
+
// on(handler) - subscribe to all
|
|
6163
|
+
sub = { pattern: null, handler: patternOrHandler, regex: null };
|
|
6164
|
+
}
|
|
6165
|
+
else {
|
|
6166
|
+
// on(pattern, handler)
|
|
6167
|
+
if (typeof handler !== 'function') {
|
|
6168
|
+
throw new TypeError(`EventBus.on("${patternOrHandler}") expected a function handler`);
|
|
6169
|
+
}
|
|
6170
|
+
const regex = isValidPattern(patternOrHandler)
|
|
6171
|
+
? patternToRegex(patternOrHandler)
|
|
6172
|
+
: null;
|
|
6173
|
+
sub = {
|
|
6174
|
+
pattern: patternOrHandler,
|
|
6175
|
+
handler,
|
|
6176
|
+
regex,
|
|
6177
|
+
};
|
|
6178
|
+
}
|
|
6179
|
+
subscriptions.add(sub);
|
|
6180
|
+
// Return unsubscribe function (idempotent)
|
|
6181
|
+
let unsubscribed = false;
|
|
6182
|
+
return () => {
|
|
6183
|
+
if (!unsubscribed) {
|
|
6184
|
+
subscriptions.delete(sub);
|
|
6185
|
+
unsubscribed = true;
|
|
6186
|
+
}
|
|
6187
|
+
};
|
|
6188
|
+
},
|
|
6189
|
+
};
|
|
6190
|
+
}
|
|
6191
|
+
return createBus({});
|
|
6192
|
+
}
|
|
6193
|
+
|
|
6194
|
+
/** No-op counter that discards all increments. */
|
|
6195
|
+
const noopCounter = {
|
|
6196
|
+
inc: () => {
|
|
6197
|
+
// Intentionally empty - discards increment
|
|
6198
|
+
},
|
|
6199
|
+
};
|
|
6200
|
+
/** No-op histogram that discards all observations. */
|
|
6201
|
+
const noopHistogram = {
|
|
6202
|
+
observe: () => {
|
|
6203
|
+
// Intentionally empty - discards observation
|
|
6204
|
+
},
|
|
6205
|
+
};
|
|
6206
|
+
/** No-op timer that returns an empty stop function. */
|
|
6207
|
+
const noopTimer = {
|
|
6208
|
+
start: () => () => {
|
|
6209
|
+
// Intentionally empty - discards timing
|
|
6210
|
+
},
|
|
6211
|
+
};
|
|
6212
|
+
/**
|
|
6213
|
+
* Create a no-op metrics instance.
|
|
6214
|
+
*
|
|
6215
|
+
* @returns A Metrics instance that discards all operations.
|
|
6216
|
+
*/
|
|
6217
|
+
function createNoopMetrics() {
|
|
6218
|
+
// Self-referential instance - child() returns same object to avoid allocations
|
|
6219
|
+
const instance = {
|
|
6220
|
+
counter: () => noopCounter,
|
|
6221
|
+
histogram: () => noopHistogram,
|
|
6222
|
+
timer: () => noopTimer,
|
|
6223
|
+
child: () => instance,
|
|
6224
|
+
};
|
|
6225
|
+
return instance;
|
|
6226
|
+
}
|
|
6227
|
+
/**
|
|
6228
|
+
* A no-op metrics implementation that discards all operations.
|
|
6229
|
+
*
|
|
6230
|
+
* @remarks
|
|
6231
|
+
* Use this when metrics collection is disabled or not configured.
|
|
6232
|
+
* All metric operations are safe to call and return immediately.
|
|
6233
|
+
* The `child()` method returns the same instance to avoid allocations.
|
|
6234
|
+
*
|
|
6235
|
+
* @example
|
|
6236
|
+
* ```typescript
|
|
6237
|
+
* import { noopMetrics } from '@core/runtime'
|
|
6238
|
+
*
|
|
6239
|
+
* // Use when metrics are disabled
|
|
6240
|
+
* const metrics = config.metricsEnabled
|
|
6241
|
+
* ? createDatadogMetrics(config)
|
|
6242
|
+
* : noopMetrics
|
|
6243
|
+
*
|
|
6244
|
+
* // All operations are safe to call
|
|
6245
|
+
* metrics.counter('requests').inc()
|
|
6246
|
+
* const stop = metrics.timer('query').start()
|
|
6247
|
+
* stop()
|
|
6248
|
+
* ```
|
|
6249
|
+
*/
|
|
6250
|
+
const noopMetrics = createNoopMetrics();
|
|
6251
|
+
|
|
6252
|
+
/**
|
|
6253
|
+
* Define a schema for runtime logger interfaces.
|
|
6254
|
+
*
|
|
6255
|
+
* @remarks
|
|
6256
|
+
* Validate that a runtime logger provides the minimal methods expected by the SDK.
|
|
6257
|
+
*
|
|
6258
|
+
* @example
|
|
6259
|
+
* ```typescript
|
|
6260
|
+
* import { loggerSchema } from '@core/runtime'
|
|
6261
|
+
*
|
|
6262
|
+
* const logger = {
|
|
6263
|
+
* debug: () => undefined,
|
|
6264
|
+
* info: () => undefined,
|
|
6265
|
+
* warn: () => undefined,
|
|
6266
|
+
* error: () => undefined,
|
|
6267
|
+
* child: () => logger,
|
|
6268
|
+
* }
|
|
6269
|
+
*
|
|
6270
|
+
* loggerSchema.parse(logger)
|
|
6271
|
+
* ```
|
|
6272
|
+
*/
|
|
6273
|
+
const loggerSchema = zod.z.custom((value) => {
|
|
6274
|
+
if (value === null || typeof value !== 'object') {
|
|
6275
|
+
return false;
|
|
6276
|
+
}
|
|
6277
|
+
const record = value;
|
|
6278
|
+
return (typeof record['debug'] === 'function' &&
|
|
6279
|
+
typeof record['info'] === 'function' &&
|
|
6280
|
+
typeof record['warn'] === 'function' &&
|
|
6281
|
+
typeof record['error'] === 'function' &&
|
|
6282
|
+
typeof record['child'] === 'function');
|
|
6283
|
+
}, {
|
|
6284
|
+
message: 'Invalid logger',
|
|
6285
|
+
});
|
|
6286
|
+
|
|
6287
|
+
/**
|
|
6288
|
+
* Define a schema for runtime metrics interfaces.
|
|
6289
|
+
*
|
|
6290
|
+
* @remarks
|
|
6291
|
+
* Validate that a metrics implementation exposes the minimal API expected by the runtime.
|
|
6292
|
+
*
|
|
6293
|
+
* @example
|
|
6294
|
+
* ```typescript
|
|
6295
|
+
* import { metricsSchema } from '@core/runtime'
|
|
6296
|
+
*
|
|
6297
|
+
* const metrics = {
|
|
6298
|
+
* counter: () => ({ inc: () => undefined }),
|
|
6299
|
+
* histogram: () => ({ observe: () => undefined }),
|
|
6300
|
+
* timer: () => ({ start: () => () => undefined }),
|
|
6301
|
+
* child: () => metrics,
|
|
6302
|
+
* }
|
|
6303
|
+
*
|
|
6304
|
+
* metricsSchema.parse(metrics)
|
|
6305
|
+
* ```
|
|
6306
|
+
*/
|
|
6307
|
+
const metricsSchema = zod.z.custom((value) => {
|
|
6308
|
+
if (value === null || typeof value !== 'object') {
|
|
6309
|
+
return false;
|
|
6310
|
+
}
|
|
6311
|
+
const record = value;
|
|
6312
|
+
return (typeof record['counter'] === 'function' &&
|
|
6313
|
+
typeof record['histogram'] === 'function' &&
|
|
6314
|
+
typeof record['timer'] === 'function' &&
|
|
6315
|
+
typeof record['child'] === 'function');
|
|
6316
|
+
}, {
|
|
6317
|
+
message: 'Invalid metrics',
|
|
6318
|
+
});
|
|
6319
|
+
|
|
6320
|
+
/**
|
|
6321
|
+
* Omit undefined values from an object.
|
|
6322
|
+
*
|
|
6323
|
+
* @param obj - The object to process.
|
|
6324
|
+
* @returns A new object with undefined values removed.
|
|
6325
|
+
*
|
|
6326
|
+
* @internal
|
|
6327
|
+
* @remarks
|
|
6328
|
+
* Used by both production and mock loggers to ensure consistent behavior.
|
|
6329
|
+
* This prevents undefined values from being serialized in log output,
|
|
6330
|
+
* which can cause issues with some log transports.
|
|
6331
|
+
*/
|
|
6332
|
+
function omitUndefined(obj) {
|
|
6333
|
+
const result = {};
|
|
6334
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
6335
|
+
if (value !== undefined) {
|
|
6336
|
+
result[key] = value;
|
|
6337
|
+
}
|
|
6338
|
+
}
|
|
6339
|
+
return result;
|
|
6340
|
+
}
|
|
6341
|
+
|
|
6342
|
+
/**
|
|
6343
|
+
* Default redaction paths for web3/blockchain SDKs.
|
|
6344
|
+
*
|
|
6345
|
+
* @remarks
|
|
6346
|
+
* These paths target common sensitive fields in blockchain applications.
|
|
6347
|
+
* All user fields are nested under `context`, so paths start with `context.`.
|
|
6348
|
+
* Wildcard `*` matches any key at that level.
|
|
6349
|
+
*/
|
|
6350
|
+
const DEFAULT_REDACT_PATHS = [
|
|
6351
|
+
// Generic Credentials
|
|
6352
|
+
'context.password',
|
|
6353
|
+
'context.passphrase',
|
|
6354
|
+
'context.secret',
|
|
6355
|
+
'context.token',
|
|
6356
|
+
'context.*.password',
|
|
6357
|
+
'context.*.passphrase',
|
|
6358
|
+
'context.*.secret',
|
|
6359
|
+
'context.*.token',
|
|
6360
|
+
// API Keys & Auth Tokens
|
|
6361
|
+
'context.apiKey',
|
|
6362
|
+
'context.apiSecret',
|
|
6363
|
+
'context.accessToken',
|
|
6364
|
+
'context.refreshToken',
|
|
6365
|
+
'context.jwt',
|
|
6366
|
+
'context.bearerToken',
|
|
6367
|
+
'context.sessionId',
|
|
6368
|
+
'context.authorization',
|
|
6369
|
+
'context.cookie',
|
|
6370
|
+
'context.*.apiKey',
|
|
6371
|
+
'context.*.apiSecret',
|
|
6372
|
+
'context.*.accessToken',
|
|
6373
|
+
'context.*.refreshToken',
|
|
6374
|
+
'context.*.jwt',
|
|
6375
|
+
'context.*.bearerToken',
|
|
6376
|
+
'context.*.sessionId',
|
|
6377
|
+
'context.*.authorization',
|
|
6378
|
+
'context.*.cookie',
|
|
6379
|
+
// Web3 / Crypto Keys
|
|
6380
|
+
'context.privateKey',
|
|
6381
|
+
'context.secretKey',
|
|
6382
|
+
'context.signingKey',
|
|
6383
|
+
'context.encryptionKey',
|
|
6384
|
+
'context.*.privateKey',
|
|
6385
|
+
'context.*.secretKey',
|
|
6386
|
+
'context.*.signingKey',
|
|
6387
|
+
'context.*.encryptionKey',
|
|
6388
|
+
// Web3 / Crypto Mnemonics and Seeds
|
|
6389
|
+
'context.mnemonic',
|
|
6390
|
+
'context.seed',
|
|
6391
|
+
'context.seedPhrase',
|
|
6392
|
+
'context.*.mnemonic',
|
|
6393
|
+
'context.*.seed',
|
|
6394
|
+
'context.*.seedPhrase',
|
|
6395
|
+
// OTP / Verification Codes
|
|
6396
|
+
'context.otp',
|
|
6397
|
+
'context.verificationCode',
|
|
6398
|
+
'context.*.otp',
|
|
6399
|
+
'context.*.verificationCode',
|
|
6400
|
+
// Payment Information
|
|
6401
|
+
'context.cardNumber',
|
|
6402
|
+
'context.cvv',
|
|
6403
|
+
'context.accountNumber',
|
|
6404
|
+
'context.*.cardNumber',
|
|
6405
|
+
'context.*.cvv',
|
|
6406
|
+
'context.*.accountNumber',
|
|
6407
|
+
];
|
|
6408
|
+
/**
|
|
6409
|
+
* Wrap user fields under `context` to prevent collision with pino internals.
|
|
6410
|
+
*
|
|
6411
|
+
* @param fields - User-provided log fields.
|
|
6412
|
+
* @returns Object with fields nested under `context`, or undefined if empty.
|
|
6413
|
+
*
|
|
6414
|
+
* @remarks
|
|
6415
|
+
* This function handles edge cases by returning undefined for null, undefined,
|
|
6416
|
+
* or empty objects to avoid unnecessary wrapping in log output.
|
|
6417
|
+
* Undefined values are cleaned before wrapping.
|
|
6418
|
+
*/
|
|
6419
|
+
function wrapInContext(fields) {
|
|
6420
|
+
if (!fields)
|
|
6421
|
+
return undefined;
|
|
6422
|
+
// Clean undefined values for consistency and transport compatibility
|
|
6423
|
+
const cleaned = omitUndefined(fields);
|
|
6424
|
+
// Handle edge case: all values were undefined, resulting in empty object
|
|
6425
|
+
const keys = Object.keys(cleaned);
|
|
6426
|
+
if (keys.length === 0)
|
|
6427
|
+
return undefined;
|
|
6428
|
+
return { context: cleaned };
|
|
6429
|
+
}
|
|
6430
|
+
/**
|
|
6431
|
+
* Wrap a pino instance to conform to our Logger interface.
|
|
6432
|
+
*
|
|
6433
|
+
* @param pinoInstance - The pino logger instance to wrap.
|
|
6434
|
+
* @returns A Logger instance conforming to our stable interface.
|
|
6435
|
+
*/
|
|
6436
|
+
function wrapPino(pinoInstance) {
|
|
6437
|
+
return {
|
|
6438
|
+
debug(message, fields) {
|
|
6439
|
+
const wrapped = wrapInContext(fields);
|
|
6440
|
+
if (wrapped) {
|
|
6441
|
+
pinoInstance.debug(wrapped, message);
|
|
6442
|
+
}
|
|
6443
|
+
else {
|
|
6444
|
+
pinoInstance.debug(message);
|
|
6445
|
+
}
|
|
6446
|
+
},
|
|
6447
|
+
info(message, fields) {
|
|
6448
|
+
const wrapped = wrapInContext(fields);
|
|
6449
|
+
if (wrapped) {
|
|
6450
|
+
pinoInstance.info(wrapped, message);
|
|
6451
|
+
}
|
|
6452
|
+
else {
|
|
6453
|
+
pinoInstance.info(message);
|
|
6454
|
+
}
|
|
6455
|
+
},
|
|
6456
|
+
warn(message, fields) {
|
|
6457
|
+
const wrapped = wrapInContext(fields);
|
|
6458
|
+
if (wrapped) {
|
|
6459
|
+
pinoInstance.warn(wrapped, message);
|
|
6460
|
+
}
|
|
6461
|
+
else {
|
|
6462
|
+
pinoInstance.warn(message);
|
|
6463
|
+
}
|
|
6464
|
+
},
|
|
6465
|
+
error(message, fields) {
|
|
6466
|
+
const wrapped = wrapInContext(fields);
|
|
6467
|
+
if (wrapped) {
|
|
6468
|
+
pinoInstance.error(wrapped, message);
|
|
6469
|
+
}
|
|
6470
|
+
else {
|
|
6471
|
+
pinoInstance.error(message);
|
|
6472
|
+
}
|
|
6473
|
+
},
|
|
6474
|
+
child(tags) {
|
|
6475
|
+
// Child bindings stay flat (not wrapped) - they're part of logger's base context
|
|
6476
|
+
const cleaned = omitUndefined(tags);
|
|
6477
|
+
return wrapPino(pinoInstance.child(cleaned));
|
|
6478
|
+
},
|
|
6479
|
+
};
|
|
6480
|
+
}
|
|
6481
|
+
/**
|
|
6482
|
+
* Build pino redact configuration from our simplified options.
|
|
6483
|
+
*
|
|
6484
|
+
* @param redact - The redact configuration option.
|
|
6485
|
+
* @returns Pino-compatible redact configuration or undefined.
|
|
6486
|
+
*/
|
|
6487
|
+
function buildRedactConfig(redact) {
|
|
6488
|
+
// Explicitly disabled
|
|
6489
|
+
if (redact === false) {
|
|
6490
|
+
return undefined;
|
|
6491
|
+
}
|
|
6492
|
+
// Custom paths provided
|
|
6493
|
+
if (Array.isArray(redact)) {
|
|
6494
|
+
return redact.length > 0
|
|
6495
|
+
? { paths: redact, censor: '[REDACTED]' }
|
|
6496
|
+
: undefined;
|
|
6497
|
+
}
|
|
6498
|
+
// Default: use web3 sensible defaults
|
|
6499
|
+
return {
|
|
6500
|
+
paths: [...DEFAULT_REDACT_PATHS],
|
|
6501
|
+
censor: '[REDACTED]',
|
|
6502
|
+
};
|
|
6503
|
+
}
|
|
6504
|
+
/**
|
|
6505
|
+
* Create a logger backed by pino.
|
|
6506
|
+
*
|
|
6507
|
+
* @param options - Logger options (optional).
|
|
6508
|
+
* @param stream - Destination stream (optional).
|
|
6509
|
+
* @returns A Logger instance.
|
|
6510
|
+
* @throws Error if invalid pino options are provided.
|
|
6511
|
+
*
|
|
6512
|
+
* @remarks
|
|
6513
|
+
* This is a thin wrapper around pino that exposes our stable Logger interface.
|
|
6514
|
+
* Pino handles all transport concerns: JSON, pretty printing, file, remote, browser, etc.
|
|
6515
|
+
*
|
|
6516
|
+
* **Security**: By default, sensitive web3 fields (privateKey, mnemonic, apiKey, etc.)
|
|
6517
|
+
* are automatically redacted from log output. Use `redact: false` to disable.
|
|
6518
|
+
*
|
|
6519
|
+
* @example
|
|
6520
|
+
* ```typescript
|
|
6521
|
+
* import { createLogger } from '@core/runtime'
|
|
6522
|
+
*
|
|
6523
|
+
* // Default: web3 sensitive fields are redacted
|
|
6524
|
+
* const logger = createLogger({ level: 'info' })
|
|
6525
|
+
* logger.info('Signing', { privateKey: '0x123...' })
|
|
6526
|
+
* // Output: { context: { privateKey: '[REDACTED]' }, msg: 'Signing' }
|
|
6527
|
+
*
|
|
6528
|
+
* // Disable redaction (use with caution)
|
|
6529
|
+
* const unsafeLogger = createLogger({ level: 'debug', redact: false })
|
|
6530
|
+
*
|
|
6531
|
+
* // Custom redaction paths
|
|
6532
|
+
* const customLogger = createLogger({
|
|
6533
|
+
* level: 'info',
|
|
6534
|
+
* redact: ['context.mySecret', 'context.*.credentials']
|
|
6535
|
+
* })
|
|
6536
|
+
*
|
|
6537
|
+
* // Pretty output for development
|
|
6538
|
+
* const devLogger = createLogger({
|
|
6539
|
+
* level: 'debug',
|
|
6540
|
+
* transport: { target: 'pino-pretty' }
|
|
6541
|
+
* })
|
|
6542
|
+
*
|
|
6543
|
+
* // Browser logger
|
|
6544
|
+
* const browserLogger = createLogger({
|
|
6545
|
+
* browser: { asObject: true }
|
|
6546
|
+
* })
|
|
6547
|
+
* ```
|
|
6548
|
+
*/
|
|
6549
|
+
function createLogger(options, stream) {
|
|
6550
|
+
const { redact, ...pinoOptions } = {};
|
|
6551
|
+
// Build redaction config
|
|
6552
|
+
const redactConfig = buildRedactConfig(redact);
|
|
6553
|
+
// Build final pino options, only include redact if defined
|
|
6554
|
+
const finalOptions = redactConfig
|
|
6555
|
+
? { ...pinoOptions, redact: redactConfig }
|
|
6556
|
+
: pinoOptions;
|
|
6557
|
+
const pinoInstance = pino__default(finalOptions);
|
|
6558
|
+
return wrapPino(pinoInstance);
|
|
6559
|
+
}
|
|
6560
|
+
|
|
6561
|
+
/**
|
|
6562
|
+
* Factory for creating Runtime instances with defaults.
|
|
6563
|
+
*
|
|
6564
|
+
* @packageDocumentation
|
|
6565
|
+
*/
|
|
6566
|
+
// ============================================================================
|
|
6567
|
+
// Validation Schema
|
|
6568
|
+
// ============================================================================
|
|
6569
|
+
/**
|
|
6570
|
+
* Schema for validating {@link RuntimeOptions}.
|
|
6571
|
+
*
|
|
6572
|
+
* @remarks
|
|
6573
|
+
* Used internally by {@link createRuntime} to validate options from JS consumers.
|
|
6574
|
+
* Exported for advanced use cases where manual validation is needed.
|
|
6575
|
+
*/
|
|
6576
|
+
const runtimeOptionsSchema = zod.z
|
|
6577
|
+
.object({
|
|
6578
|
+
logger: loggerSchema.optional(),
|
|
6579
|
+
metrics: metricsSchema.optional(),
|
|
6580
|
+
})
|
|
6581
|
+
.passthrough();
|
|
6582
|
+
// ============================================================================
|
|
6583
|
+
// Factory
|
|
6584
|
+
// ============================================================================
|
|
6585
|
+
/**
|
|
6586
|
+
* Create a complete Runtime with sensible defaults.
|
|
6587
|
+
*
|
|
6588
|
+
* @param options - Optional configuration to override logger and metrics.
|
|
6589
|
+
* @returns A frozen, immutable Runtime with all services guaranteed present.
|
|
6590
|
+
* @throws KitError (INPUT_VALIDATION_FAILED) if options contain invalid services.
|
|
6591
|
+
*
|
|
6592
|
+
* @remarks
|
|
6593
|
+
* Creates a fully-configured runtime by merging provided options with defaults.
|
|
6594
|
+
* The returned runtime is frozen to enforce immutability.
|
|
6595
|
+
*
|
|
6596
|
+
* | Service | Default | Configurable |
|
|
6597
|
+
* |---------|---------|--------------|
|
|
6598
|
+
* | `logger` | pino logger (info level) | Yes |
|
|
6599
|
+
* | `metrics` | No-op metrics | Yes |
|
|
6600
|
+
* | `events` | Internal event bus | No |
|
|
6601
|
+
* | `clock` | `Date.now()` | No |
|
|
6602
|
+
*
|
|
6603
|
+
* **Why only logger and metrics?**
|
|
6604
|
+
*
|
|
6605
|
+
* - **Logger/Metrics**: Integration points with your infrastructure
|
|
6606
|
+
* - **Events**: Internal pub/sub mechanism - subscribe via `runtime.events.on()`
|
|
6607
|
+
* - **Clock**: Testing concern - use mock factories for tests
|
|
6608
|
+
*
|
|
6609
|
+
* @example
|
|
6610
|
+
* ```typescript
|
|
6611
|
+
* import { createRuntime, createLogger } from '@core/runtime'
|
|
6612
|
+
*
|
|
6613
|
+
* // Use all defaults
|
|
6614
|
+
* const runtime = createRuntime()
|
|
6615
|
+
*
|
|
6616
|
+
* // Custom logger
|
|
6617
|
+
* const runtime = createRuntime({
|
|
6618
|
+
* logger: createLogger({ level: 'debug' }),
|
|
6619
|
+
* })
|
|
6620
|
+
*
|
|
6621
|
+
* // Custom metrics (e.g., Prometheus)
|
|
6622
|
+
* const runtime = createRuntime({
|
|
6623
|
+
* metrics: myPrometheusMetrics,
|
|
6624
|
+
* })
|
|
6625
|
+
*
|
|
6626
|
+
* // Subscribe to events (don't replace the bus)
|
|
6627
|
+
* runtime.events.on('operation.*', (event) => {
|
|
6628
|
+
* console.log('Event:', event.name)
|
|
6629
|
+
* })
|
|
6630
|
+
* ```
|
|
6631
|
+
*/
|
|
6632
|
+
function createRuntime(options) {
|
|
6633
|
+
// Validate options for JS consumers
|
|
6634
|
+
if (options != null) {
|
|
6635
|
+
parseOrThrow(options, runtimeOptionsSchema, 'runtime options');
|
|
6636
|
+
}
|
|
6637
|
+
// Resolve logger first (events may need it)
|
|
6638
|
+
const logger = options?.logger ?? createLogger();
|
|
6639
|
+
// Internal services - not configurable
|
|
6640
|
+
const events = createEventBus({ logger });
|
|
6641
|
+
const clock = defaultClock;
|
|
6642
|
+
// Resolve metrics
|
|
6643
|
+
const metrics = options?.metrics ?? noopMetrics;
|
|
6644
|
+
return Object.freeze({ logger, events, metrics, clock });
|
|
6645
|
+
}
|
|
6646
|
+
|
|
6647
|
+
/**
|
|
6648
|
+
* Invocation context resolution - resolves the **WHO/HOW** of an operation.
|
|
6649
|
+
*
|
|
6650
|
+
* @packageDocumentation
|
|
6651
|
+
*/
|
|
6652
|
+
// ============================================================================
|
|
6653
|
+
// Validation Schemas
|
|
6654
|
+
// ============================================================================
|
|
6655
|
+
/**
|
|
6656
|
+
* Schema for validating Caller.
|
|
6657
|
+
*/
|
|
6658
|
+
const callerSchema = zod.z.object({
|
|
6659
|
+
type: zod.z.string(),
|
|
6660
|
+
name: zod.z.string(),
|
|
6661
|
+
version: zod.z.string().optional(),
|
|
6662
|
+
});
|
|
6663
|
+
/**
|
|
6664
|
+
* Schema for validating InvocationMeta input.
|
|
6665
|
+
*/
|
|
6666
|
+
const invocationMetaSchema = zod.z
|
|
6667
|
+
.object({
|
|
6668
|
+
traceId: zod.z.string().optional(),
|
|
6669
|
+
runtime: zod.z.object({}).passthrough().optional(),
|
|
6670
|
+
tokens: zod.z.object({}).passthrough().optional(),
|
|
6671
|
+
callers: zod.z.array(callerSchema).optional(),
|
|
6672
|
+
})
|
|
6673
|
+
.strict();
|
|
6674
|
+
// ============================================================================
|
|
6675
|
+
// Invocation Context Resolution
|
|
6676
|
+
// ============================================================================
|
|
6677
|
+
/**
|
|
6678
|
+
* Resolve invocation metadata to invocation context.
|
|
6679
|
+
*
|
|
6680
|
+
* @param meta - User-provided invocation metadata (**WHO/HOW**), optional.
|
|
6681
|
+
* @param defaults - Default runtime and tokens to use if not overridden.
|
|
6682
|
+
* @returns Frozen, immutable invocation context with guaranteed values.
|
|
6683
|
+
* @throws KitError when meta contains invalid properties.
|
|
6684
|
+
*
|
|
6685
|
+
* @remarks
|
|
6686
|
+
* Resolves the **WHO** called and **HOW** to observe:
|
|
6687
|
+
* - TraceId: Uses provided value or generates new one
|
|
6688
|
+
* - Runtime: Uses meta.runtime if provided, otherwise defaults.runtime
|
|
6689
|
+
* - Tokens: Uses meta.tokens if provided, otherwise defaults.tokens
|
|
6690
|
+
* - Callers: Uses provided array or empty array
|
|
6691
|
+
*
|
|
6692
|
+
* The returned context is frozen to enforce immutability.
|
|
6693
|
+
*
|
|
6694
|
+
* @example
|
|
6695
|
+
* ```typescript
|
|
6696
|
+
* import { resolveInvocationContext, createRuntime } from '@core/runtime'
|
|
6697
|
+
* import { createTokenRegistry } from '@core/tokens'
|
|
6698
|
+
*
|
|
6699
|
+
* const defaults = {
|
|
6700
|
+
* runtime: createRuntime(),
|
|
6701
|
+
* tokens: createTokenRegistry(),
|
|
6702
|
+
* }
|
|
6703
|
+
*
|
|
6704
|
+
* // Minimal - just using defaults
|
|
6705
|
+
* const ctx = resolveInvocationContext(undefined, defaults)
|
|
6706
|
+
*
|
|
6707
|
+
* // With trace ID and caller info
|
|
6708
|
+
* const ctx = resolveInvocationContext(
|
|
6709
|
+
* {
|
|
6710
|
+
* traceId: 'abc-123',
|
|
6711
|
+
* callers: [{ type: 'kit', name: 'BridgeKit', version: '1.0.0' }],
|
|
6712
|
+
* },
|
|
6713
|
+
* defaults
|
|
6714
|
+
* )
|
|
6715
|
+
*
|
|
6716
|
+
* // With runtime override (complete replacement)
|
|
6717
|
+
* const ctx = resolveInvocationContext(
|
|
6718
|
+
* { runtime: createRuntime({ logger: myLogger }) },
|
|
6719
|
+
* defaults
|
|
6720
|
+
* )
|
|
6721
|
+
* ```
|
|
6722
|
+
*/
|
|
6723
|
+
function resolveInvocationContext(meta, defaults) {
|
|
6724
|
+
// Validate meta input if provided
|
|
6725
|
+
if (meta !== undefined) {
|
|
6726
|
+
const result = invocationMetaSchema.safeParse(meta);
|
|
6727
|
+
if (!result.success) {
|
|
6728
|
+
throw createValidationFailedError$1('invocationMeta', meta, result.error.errors.map((e) => e.message).join(', '));
|
|
6729
|
+
}
|
|
6730
|
+
}
|
|
6731
|
+
// Generate trace ID if not provided
|
|
6732
|
+
const traceId = meta?.traceId ?? createTraceId();
|
|
6733
|
+
// Use meta overrides or fall back to defaults
|
|
6734
|
+
const runtime = meta?.runtime ?? defaults.runtime;
|
|
6735
|
+
const tokens = meta?.tokens ?? defaults.tokens;
|
|
6736
|
+
const callers = meta?.callers ?? [];
|
|
6737
|
+
return Object.freeze({ traceId, runtime, tokens, callers });
|
|
6738
|
+
}
|
|
6739
|
+
|
|
6740
|
+
/**
|
|
6741
|
+
* Extend an invocation context by appending a caller to its call chain.
|
|
6742
|
+
*
|
|
6743
|
+
* @param context - The existing invocation context to extend.
|
|
6744
|
+
* @param caller - The caller to append to the call chain.
|
|
6745
|
+
* @returns A new frozen invocation context with the caller appended.
|
|
6746
|
+
*
|
|
6747
|
+
* @remarks
|
|
6748
|
+
* This function creates a new immutable context with the caller appended
|
|
6749
|
+
* to the `callers` array while preserving all other context properties
|
|
6750
|
+
* (traceId, runtime, tokens).
|
|
6751
|
+
*
|
|
6752
|
+
* The returned context is frozen to enforce immutability.
|
|
6753
|
+
*
|
|
6754
|
+
* @example
|
|
6755
|
+
* ```typescript
|
|
6756
|
+
* import { extendInvocationContext } from '@core/runtime'
|
|
6757
|
+
*
|
|
6758
|
+
* const caller = { type: 'provider', name: 'CCTPV2', version: '1.0.0' }
|
|
6759
|
+
* const extended = extendInvocationContext(existingContext, caller)
|
|
6760
|
+
* // extended.callers === [...existingContext.callers, caller]
|
|
6761
|
+
* ```
|
|
6762
|
+
*/
|
|
6763
|
+
function extendInvocationContext(context, caller) {
|
|
6764
|
+
return Object.freeze({
|
|
6765
|
+
traceId: context.traceId,
|
|
6766
|
+
runtime: context.runtime,
|
|
6767
|
+
tokens: context.tokens,
|
|
6768
|
+
callers: [...context.callers, caller],
|
|
6769
|
+
});
|
|
6770
|
+
}
|
|
6771
|
+
|
|
6772
|
+
// Clock - expose defaultClock for backward compatibility
|
|
6773
|
+
/** Clock validation schema (backward compatibility). */
|
|
6774
|
+
zod.z.custom((val) => val !== null &&
|
|
6775
|
+
typeof val === 'object' &&
|
|
6776
|
+
'now' in val &&
|
|
6777
|
+
typeof val['now'] === 'function');
|
|
6778
|
+
/** EventBus validation schema (backward compatibility). */
|
|
6779
|
+
zod.z.custom((val) => val !== null &&
|
|
6780
|
+
typeof val === 'object' &&
|
|
6781
|
+
'emit' in val &&
|
|
6782
|
+
typeof val['emit'] === 'function');
|
|
6783
|
+
/** Runtime validation schema (backward compatibility). */
|
|
6784
|
+
zod.z
|
|
6785
|
+
.object({
|
|
6786
|
+
logger: zod.z.any().optional(),
|
|
6787
|
+
events: zod.z.any().optional(),
|
|
6788
|
+
metrics: zod.z.any().optional(),
|
|
6789
|
+
clock: zod.z.any().optional(),
|
|
6790
|
+
})
|
|
6791
|
+
.passthrough();
|
|
6792
|
+
|
|
6793
|
+
/**
|
|
6794
|
+
* Create a structured error for token resolution failures.
|
|
6795
|
+
*
|
|
6796
|
+
* @remarks
|
|
6797
|
+
* Returns a `KitError` with `INPUT_INVALID_TOKEN` code (1006).
|
|
6798
|
+
* The error trace contains the selector and chain context for debugging.
|
|
6799
|
+
*
|
|
6800
|
+
* @param message - Human-readable error description.
|
|
6801
|
+
* @param selector - The token selector that failed to resolve.
|
|
6802
|
+
* @param chainId - The chain being resolved for (optional).
|
|
6803
|
+
* @param cause - The underlying error, if any (optional).
|
|
6804
|
+
* @returns A KitError with INPUT type and FATAL recoverability.
|
|
6805
|
+
*
|
|
6806
|
+
* @example
|
|
6807
|
+
* ```typescript
|
|
6808
|
+
* throw createTokenResolutionError(
|
|
6809
|
+
* 'Unknown token symbol: FAKE',
|
|
6810
|
+
* 'FAKE',
|
|
6811
|
+
* 'Ethereum'
|
|
6812
|
+
* )
|
|
6813
|
+
* ```
|
|
6814
|
+
*/
|
|
6815
|
+
function createTokenResolutionError(message, selector, chainId, cause) {
|
|
6816
|
+
const trace = {
|
|
6817
|
+
selector,
|
|
6818
|
+
...(chainId === undefined ? {} : { chainId }),
|
|
6819
|
+
...({} ),
|
|
6820
|
+
};
|
|
6821
|
+
return new KitError({
|
|
6822
|
+
...InputError.INVALID_TOKEN,
|
|
6823
|
+
recoverability: 'FATAL',
|
|
6824
|
+
message,
|
|
6825
|
+
cause: { trace },
|
|
6826
|
+
});
|
|
6827
|
+
}
|
|
6828
|
+
|
|
6829
|
+
/**
|
|
6830
|
+
* USDC token definition with addresses and metadata.
|
|
6831
|
+
*
|
|
6832
|
+
* @remarks
|
|
6833
|
+
* This is the built-in USDC definition used by the TokenRegistry.
|
|
6834
|
+
* Includes all known USDC addresses across supported chains.
|
|
6835
|
+
*
|
|
6836
|
+
* Keys use the `Blockchain` enum for type safety. Both enum values
|
|
6837
|
+
* and string literals are supported:
|
|
6838
|
+
* - `Blockchain.Ethereum` or `'Ethereum'`
|
|
6839
|
+
*
|
|
6840
|
+
* @example
|
|
6841
|
+
* ```typescript
|
|
6842
|
+
* import { USDC } from '@core/tokens'
|
|
6843
|
+
* import { Blockchain } from '@core/chains'
|
|
6844
|
+
*
|
|
6845
|
+
* console.log(USDC.symbol) // 'USDC'
|
|
6846
|
+
* console.log(USDC.decimals) // 6
|
|
6847
|
+
* console.log(USDC.locators[Blockchain.Ethereum])
|
|
6848
|
+
* // '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48'
|
|
6849
|
+
* ```
|
|
6850
|
+
*/
|
|
6851
|
+
const USDC = {
|
|
6852
|
+
symbol: 'USDC',
|
|
6853
|
+
decimals: 6,
|
|
6854
|
+
locators: {
|
|
6855
|
+
// =========================================================================
|
|
6856
|
+
// Mainnets (alphabetically sorted)
|
|
6857
|
+
// =========================================================================
|
|
6858
|
+
[exports.Blockchain.Arbitrum]: '0xaf88d065e77c8cC2239327C5EDb3A432268e5831',
|
|
6859
|
+
[exports.Blockchain.Avalanche]: '0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E',
|
|
6860
|
+
[exports.Blockchain.Base]: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913',
|
|
6861
|
+
[exports.Blockchain.Celo]: '0xcebA9300f2b948710d2653dD7B07f33A8B32118C',
|
|
6862
|
+
[exports.Blockchain.Codex]: '0xd996633a415985DBd7D6D12f4A4343E31f5037cf',
|
|
6863
|
+
[exports.Blockchain.Ethereum]: '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48',
|
|
6864
|
+
[exports.Blockchain.Hedera]: '0.0.456858',
|
|
6865
|
+
[exports.Blockchain.HyperEVM]: '0xb88339CB7199b77E23DB6E890353E22632Ba630f',
|
|
6866
|
+
[exports.Blockchain.Ink]: '0x2D270e6886d130D724215A266106e6832161EAEd',
|
|
6867
|
+
[exports.Blockchain.Linea]: '0x176211869ca2b568f2a7d4ee941e073a821ee1ff',
|
|
6868
|
+
[exports.Blockchain.NEAR]: '17208628f84f5d6ad33f0da3bbbeb27ffcb398eac501a31bd6ad2011e36133a1',
|
|
6869
|
+
[exports.Blockchain.Noble]: 'uusdc',
|
|
6870
|
+
[exports.Blockchain.Optimism]: '0x0b2c639c533813f4aa9d7837caf62653d097ff85',
|
|
6871
|
+
[exports.Blockchain.Plume]: '0x222365EF19F7947e5484218551B56bb3965Aa7aF',
|
|
6872
|
+
[exports.Blockchain.Polkadot_Asset_Hub]: '1337',
|
|
6873
|
+
[exports.Blockchain.Polygon]: '0x3c499c542cef5e3811e1192ce70d8cc03d5c3359',
|
|
6874
|
+
[exports.Blockchain.Sei]: '0xe15fC38F6D8c56aF07bbCBe3BAf5708A2Bf42392',
|
|
6875
|
+
[exports.Blockchain.Solana]: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v',
|
|
6876
|
+
[exports.Blockchain.Sonic]: '0x29219dd400f2Bf60E5a23d13Be72B486D4038894',
|
|
6877
|
+
[exports.Blockchain.Stellar]: 'USDC-GA5ZSEJYB37JRC5AVCIA5MOP4RHTM335X2KGX3IHOJAPP5RE34K4KZVN',
|
|
6878
|
+
[exports.Blockchain.Sui]: '0xdba34672e30cb065b1f93e3ab55318768fd6fef66c15942c9f7cb846e2f900e7::usdc::USDC',
|
|
6879
|
+
[exports.Blockchain.Unichain]: '0x078D782b760474a361dDA0AF3839290b0EF57AD6',
|
|
6880
|
+
[exports.Blockchain.World_Chain]: '0x79A02482A880bCe3F13E09da970dC34dB4cD24D1',
|
|
6881
|
+
[exports.Blockchain.XDC]: '0xfA2958CB79b0491CC627c1557F441eF849Ca8eb1',
|
|
6882
|
+
[exports.Blockchain.ZKSync_Era]: '0x1d17CBcF0D6D143135aE902365D2E5e2A16538D4',
|
|
6883
|
+
// =========================================================================
|
|
6884
|
+
// Testnets (alphabetically sorted)
|
|
6885
|
+
// =========================================================================
|
|
6886
|
+
[exports.Blockchain.Arbitrum_Sepolia]: '0x75faf114eafb1BDbe2F0316DF893fd58CE46AA4d',
|
|
6887
|
+
[exports.Blockchain.Avalanche_Fuji]: '0x5425890298aed601595a70AB815c96711a31Bc65',
|
|
6888
|
+
[exports.Blockchain.Base_Sepolia]: '0x036CbD53842c5426634e7929541eC2318f3dCF7e',
|
|
6889
|
+
[exports.Blockchain.Codex_Testnet]: '0x6d7f141b6819C2c9CC2f818e6ad549E7Ca090F8f',
|
|
6890
|
+
[exports.Blockchain.Ethereum_Sepolia]: '0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238',
|
|
6891
|
+
[exports.Blockchain.Hedera_Testnet]: '0.0.429274',
|
|
6892
|
+
[exports.Blockchain.HyperEVM_Testnet]: '0x2B3370eE501B4a559b57D449569354196457D8Ab',
|
|
6893
|
+
[exports.Blockchain.Ink_Testnet]: '0xFabab97dCE620294D2B0b0e46C68964e326300Ac',
|
|
6894
|
+
[exports.Blockchain.Linea_Sepolia]: '0xfece4462d57bd51a6a552365a011b95f0e16d9b7',
|
|
6895
|
+
[exports.Blockchain.NEAR_Testnet]: '3e2210e1184b45b64c8a434c0a7e7b23cc04ea7eb7a6c3c32520d03d4afcb8af',
|
|
6896
|
+
[exports.Blockchain.Noble_Testnet]: 'uusdc',
|
|
6897
|
+
[exports.Blockchain.Optimism_Sepolia]: '0x5fd84259d66Cd46123540766Be93DFE6D43130D7',
|
|
6898
|
+
[exports.Blockchain.Plume_Testnet]: '0xcB5f30e335672893c7eb944B374c196392C19D18',
|
|
6899
|
+
[exports.Blockchain.Polkadot_Westmint]: '31337',
|
|
6900
|
+
[exports.Blockchain.Polygon_Amoy_Testnet]: '0x41e94eb019c0762f9bfcf9fb1e58725bfb0e7582',
|
|
6901
|
+
[exports.Blockchain.Sei_Testnet]: '0x4fCF1784B31630811181f670Aea7A7bEF803eaED',
|
|
6902
|
+
[exports.Blockchain.Solana_Devnet]: '4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU',
|
|
6903
|
+
[exports.Blockchain.Sonic_Testnet]: '0x0BA304580ee7c9a980CF72e55f5Ed2E9fd30Bc51',
|
|
6904
|
+
[exports.Blockchain.Stellar_Testnet]: 'USDC-GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5',
|
|
6905
|
+
[exports.Blockchain.Sui_Testnet]: '0xa1ec7fc00a6f40db9693ad1415d0c193ad3906494428cf252621037bd7117e29::usdc::USDC',
|
|
6906
|
+
[exports.Blockchain.Unichain_Sepolia]: '0x31d0220469e10c4E71834a79b1f276d740d3768F',
|
|
6907
|
+
[exports.Blockchain.World_Chain_Sepolia]: '0x66145f38cBAC35Ca6F1Dfb4914dF98F1614aeA88',
|
|
6908
|
+
[exports.Blockchain.XDC_Apothem]: '0xb5AB69F7bBada22B28e79C8FFAECe55eF1c771D4',
|
|
6909
|
+
[exports.Blockchain.ZKSync_Sepolia]: '0xAe045DE5638162fa134807Cb558E15A3F5A7F853',
|
|
6910
|
+
},
|
|
6911
|
+
};
|
|
6912
|
+
|
|
6913
|
+
// Re-export for consumers
|
|
6914
|
+
/**
|
|
6915
|
+
* All default token definitions.
|
|
6916
|
+
*
|
|
6917
|
+
* @remarks
|
|
6918
|
+
* These tokens are automatically included in the TokenRegistry when created
|
|
6919
|
+
* without explicit defaults. Extensions can override these definitions.
|
|
6920
|
+
*
|
|
6921
|
+
* @example
|
|
6922
|
+
* ```typescript
|
|
6923
|
+
* import { createTokenRegistry } from '@core/tokens'
|
|
6924
|
+
*
|
|
6925
|
+
* // Registry uses these by default
|
|
6926
|
+
* const registry = createTokenRegistry()
|
|
6927
|
+
*
|
|
6928
|
+
* // Add custom tokens (built-ins are still included)
|
|
6929
|
+
* const customRegistry = createTokenRegistry({
|
|
6930
|
+
* tokens: [myCustomToken],
|
|
6931
|
+
* })
|
|
6932
|
+
* ```
|
|
6933
|
+
*/
|
|
6934
|
+
const DEFAULT_TOKENS = [USDC];
|
|
6935
|
+
|
|
6936
|
+
/**
|
|
6937
|
+
* Check if a selector is a raw token selector (object form).
|
|
6938
|
+
*
|
|
6939
|
+
* @param selector - The token selector to check.
|
|
6940
|
+
* @returns True if the selector is a raw token selector.
|
|
6941
|
+
*/
|
|
6942
|
+
function isRawSelector(selector) {
|
|
6943
|
+
return typeof selector === 'object' && 'locator' in selector;
|
|
6944
|
+
}
|
|
6945
|
+
/**
|
|
6946
|
+
* Normalize a symbol to uppercase for case-insensitive lookup.
|
|
6947
|
+
*
|
|
6948
|
+
* @param symbol - The symbol to normalize.
|
|
6949
|
+
* @returns The normalized (uppercase) symbol.
|
|
6950
|
+
*/
|
|
6951
|
+
function normalizeSymbol(symbol) {
|
|
6952
|
+
return symbol.toUpperCase();
|
|
6953
|
+
}
|
|
6954
|
+
/**
|
|
6955
|
+
* Create a token registry with built-in tokens and optional extensions.
|
|
6956
|
+
*
|
|
6957
|
+
* @remarks
|
|
6958
|
+
* The registry always includes built-in tokens (DEFAULT_TOKENS) like USDC.
|
|
6959
|
+
* Custom tokens are merged on top - use this to add new tokens or override
|
|
6960
|
+
* built-in definitions.
|
|
6961
|
+
*
|
|
6962
|
+
* @param options - Configuration options for the registry.
|
|
6963
|
+
* @returns A token registry instance.
|
|
6964
|
+
*
|
|
6965
|
+
* @example
|
|
6966
|
+
* ```typescript
|
|
6967
|
+
* import { createTokenRegistry } from '@core/tokens'
|
|
6968
|
+
*
|
|
6969
|
+
* // Create registry with built-in tokens (USDC, etc.)
|
|
6970
|
+
* const registry = createTokenRegistry()
|
|
6971
|
+
*
|
|
6972
|
+
* // Resolve USDC on Ethereum
|
|
6973
|
+
* const usdc = registry.resolve('USDC', 'Ethereum')
|
|
6974
|
+
* console.log(usdc.locator) // '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48'
|
|
6975
|
+
* console.log(usdc.decimals) // 6
|
|
6976
|
+
* ```
|
|
6977
|
+
*
|
|
6978
|
+
* @example
|
|
6979
|
+
* ```typescript
|
|
6980
|
+
* // Add custom tokens (built-ins are still included)
|
|
6981
|
+
* const myToken: TokenDefinition = {
|
|
6982
|
+
* symbol: 'MY',
|
|
6983
|
+
* decimals: 18,
|
|
6984
|
+
* locators: { Ethereum: '0x...' },
|
|
6985
|
+
* }
|
|
6986
|
+
*
|
|
6987
|
+
* const registry = createTokenRegistry({ tokens: [myToken] })
|
|
6988
|
+
* registry.resolve('USDC', 'Ethereum') // Still works!
|
|
6989
|
+
* registry.resolve('MY', 'Ethereum') // Also works
|
|
6990
|
+
* ```
|
|
6991
|
+
*
|
|
6992
|
+
* @example
|
|
6993
|
+
* ```typescript
|
|
6994
|
+
* // Override a built-in token
|
|
6995
|
+
* const customUsdc: TokenDefinition = {
|
|
6996
|
+
* symbol: 'USDC',
|
|
6997
|
+
* decimals: 6,
|
|
6998
|
+
* locators: { MyChain: '0xCustomAddress' },
|
|
6999
|
+
* }
|
|
7000
|
+
*
|
|
7001
|
+
* const registry = createTokenRegistry({ tokens: [customUsdc] })
|
|
7002
|
+
* // Now USDC resolves to customUsdc definition
|
|
7003
|
+
* ```
|
|
7004
|
+
*
|
|
7005
|
+
* @example
|
|
7006
|
+
* ```typescript
|
|
7007
|
+
* // Resolve arbitrary tokens by raw locator
|
|
7008
|
+
* const registry = createTokenRegistry()
|
|
7009
|
+
* const token = registry.resolve(
|
|
7010
|
+
* { locator: '0x1234...', decimals: 18 },
|
|
7011
|
+
* 'Ethereum'
|
|
7012
|
+
* )
|
|
7013
|
+
* ```
|
|
7014
|
+
*/
|
|
7015
|
+
function createTokenRegistry(options = {}) {
|
|
7016
|
+
const { tokens = [], requireDecimals = false } = options;
|
|
7017
|
+
// Build the token map: always start with DEFAULT_TOKENS, then add custom tokens
|
|
7018
|
+
const tokenMap = new Map();
|
|
7019
|
+
// Add built-in tokens first
|
|
7020
|
+
for (const def of DEFAULT_TOKENS) {
|
|
7021
|
+
tokenMap.set(normalizeSymbol(def.symbol), def);
|
|
7022
|
+
}
|
|
7023
|
+
// Custom tokens override built-ins
|
|
7024
|
+
for (const def of tokens) {
|
|
7025
|
+
tokenMap.set(normalizeSymbol(def.symbol), def);
|
|
7026
|
+
}
|
|
7027
|
+
/**
|
|
7028
|
+
* Resolve a symbol selector to token information.
|
|
7029
|
+
*/
|
|
7030
|
+
function resolveSymbol(symbol, chainId) {
|
|
7031
|
+
const normalizedSymbol = normalizeSymbol(symbol);
|
|
7032
|
+
const definition = tokenMap.get(normalizedSymbol);
|
|
7033
|
+
if (definition === undefined) {
|
|
7034
|
+
throw createTokenResolutionError(`Unknown token symbol: ${symbol}. Register it via createTokenRegistry({ tokens: [...] }) or use a raw selector.`, symbol, chainId);
|
|
7035
|
+
}
|
|
7036
|
+
const locator = definition.locators[chainId];
|
|
7037
|
+
if (locator === undefined || locator.trim() === '') {
|
|
7038
|
+
throw createTokenResolutionError(`Token ${symbol} has no locator for chain ${chainId}`, symbol, chainId);
|
|
7039
|
+
}
|
|
7040
|
+
return {
|
|
7041
|
+
symbol: definition.symbol,
|
|
7042
|
+
decimals: definition.decimals,
|
|
7043
|
+
locator,
|
|
7044
|
+
};
|
|
7045
|
+
}
|
|
7046
|
+
/**
|
|
7047
|
+
* Resolve a raw selector to token information.
|
|
7048
|
+
*/
|
|
7049
|
+
function resolveRaw(selector, chainId) {
|
|
7050
|
+
const { locator, decimals } = selector;
|
|
7051
|
+
// Validate locator
|
|
7052
|
+
if (!locator || typeof locator !== 'string') {
|
|
7053
|
+
throw createTokenResolutionError('Raw selector must have a valid locator string', selector, chainId);
|
|
7054
|
+
}
|
|
7055
|
+
// Decimals are always required for raw selectors
|
|
7056
|
+
if (decimals === undefined) {
|
|
7057
|
+
const message = requireDecimals
|
|
7058
|
+
? 'Decimals required for raw token selector (requireDecimals: true)'
|
|
7059
|
+
: 'Decimals required for raw token selector. Provide { locator, decimals }.';
|
|
7060
|
+
throw createTokenResolutionError(message, selector, chainId);
|
|
7061
|
+
}
|
|
7062
|
+
// Validate decimals
|
|
7063
|
+
if (typeof decimals !== 'number' ||
|
|
7064
|
+
decimals < 0 ||
|
|
7065
|
+
!Number.isInteger(decimals)) {
|
|
7066
|
+
throw createTokenResolutionError(`Invalid decimals: ${String(decimals)}. Must be a non-negative integer.`, selector, chainId);
|
|
7067
|
+
}
|
|
7068
|
+
return {
|
|
7069
|
+
decimals,
|
|
7070
|
+
locator,
|
|
7071
|
+
};
|
|
7072
|
+
}
|
|
7073
|
+
return {
|
|
7074
|
+
resolve(selector, chainId) {
|
|
7075
|
+
// Runtime validation of inputs - these checks are for JS consumers
|
|
7076
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
7077
|
+
if (selector === null || selector === undefined) {
|
|
7078
|
+
throw createTokenResolutionError('Token selector cannot be null or undefined', selector, chainId);
|
|
7079
|
+
}
|
|
7080
|
+
if (chainId === '' || typeof chainId !== 'string') {
|
|
7081
|
+
throw createTokenResolutionError('Chain ID is required for token resolution', selector, chainId);
|
|
7082
|
+
}
|
|
7083
|
+
// Dispatch based on selector type
|
|
7084
|
+
if (isRawSelector(selector)) {
|
|
7085
|
+
return resolveRaw(selector, chainId);
|
|
7086
|
+
}
|
|
7087
|
+
if (typeof selector === 'string') {
|
|
7088
|
+
return resolveSymbol(selector, chainId);
|
|
7089
|
+
}
|
|
7090
|
+
throw createTokenResolutionError(`Invalid selector type: ${typeof selector}. Expected string or object with locator.`, selector, chainId);
|
|
7091
|
+
},
|
|
7092
|
+
get(symbol) {
|
|
7093
|
+
if (!symbol || typeof symbol !== 'string') {
|
|
7094
|
+
return undefined;
|
|
7095
|
+
}
|
|
7096
|
+
return tokenMap.get(normalizeSymbol(symbol));
|
|
7097
|
+
},
|
|
7098
|
+
has(symbol) {
|
|
7099
|
+
if (!symbol || typeof symbol !== 'string') {
|
|
7100
|
+
return false;
|
|
7101
|
+
}
|
|
7102
|
+
return tokenMap.has(normalizeSymbol(symbol));
|
|
7103
|
+
},
|
|
7104
|
+
symbols() {
|
|
7105
|
+
return Array.from(tokenMap.values()).map((def) => def.symbol);
|
|
7106
|
+
},
|
|
7107
|
+
entries() {
|
|
7108
|
+
return Array.from(tokenMap.values());
|
|
7109
|
+
},
|
|
7110
|
+
};
|
|
7111
|
+
}
|
|
7112
|
+
|
|
7113
|
+
/**
|
|
7114
|
+
* Define a schema for token registry interfaces.
|
|
7115
|
+
*
|
|
7116
|
+
* @remarks
|
|
7117
|
+
* Validate that a registry exposes the `resolve()` API used by adapters.
|
|
7118
|
+
*
|
|
7119
|
+
* @example
|
|
7120
|
+
* ```typescript
|
|
7121
|
+
* import { tokenRegistrySchema } from '@core/tokens'
|
|
7122
|
+
*
|
|
7123
|
+
* const registry = {
|
|
7124
|
+
* resolve: () => ({ locator: '0x0', decimals: 6 }),
|
|
7125
|
+
* }
|
|
7126
|
+
*
|
|
7127
|
+
* tokenRegistrySchema.parse(registry)
|
|
7128
|
+
* ```
|
|
7129
|
+
*/
|
|
7130
|
+
zod.z.custom((value) => {
|
|
7131
|
+
if (value === null || typeof value !== 'object') {
|
|
7132
|
+
return false;
|
|
7133
|
+
}
|
|
7134
|
+
const record = value;
|
|
7135
|
+
return (typeof record['resolve'] === 'function' &&
|
|
7136
|
+
typeof record['get'] === 'function' &&
|
|
7137
|
+
typeof record['has'] === 'function' &&
|
|
7138
|
+
typeof record['symbols'] === 'function' &&
|
|
7139
|
+
typeof record['entries'] === 'function');
|
|
7140
|
+
}, {
|
|
7141
|
+
message: 'Invalid token registry',
|
|
7142
|
+
});
|
|
7143
|
+
|
|
7144
|
+
/**
|
|
7145
|
+
* Type guard to check if the destination is a forwarder-only destination.
|
|
7146
|
+
*
|
|
7147
|
+
* Forwarder-only destinations have `useForwarder: true` and no adapter.
|
|
7148
|
+
* They require a `recipientAddress` to be specified.
|
|
7149
|
+
*
|
|
7150
|
+
* @param dest - The bridge destination to check
|
|
7151
|
+
* @returns True if this is a forwarder-only destination without adapter
|
|
7152
|
+
*/
|
|
7153
|
+
function isForwarderOnlyDestination(dest) {
|
|
7154
|
+
return (dest.useForwarder === true &&
|
|
7155
|
+
!('adapter' in dest) &&
|
|
7156
|
+
'recipientAddress' in dest);
|
|
7157
|
+
}
|
|
7158
|
+
/**
|
|
7159
|
+
* Resolves a chain identifier to a chain definition.
|
|
7160
|
+
*
|
|
7161
|
+
* Both AdapterContext and BridgeDestinationWithAddress have the chain property
|
|
7162
|
+
* at the top level, so we can directly access it from either type.
|
|
7163
|
+
*
|
|
7164
|
+
* @param ctx - The bridge destination containing the chain identifier
|
|
7165
|
+
* @returns The resolved chain definition
|
|
7166
|
+
* @throws If the chain definition cannot be resolved
|
|
7167
|
+
*
|
|
7168
|
+
* @example
|
|
7169
|
+
* ```typescript
|
|
7170
|
+
* import { Blockchain } from '@core/chains'
|
|
7171
|
+
*
|
|
7172
|
+
* // AdapterContext
|
|
7173
|
+
* const chain1 = resolveChainDefinition({
|
|
7174
|
+
* adapter: mockAdapter,
|
|
7175
|
+
* chain: 'Ethereum'
|
|
7176
|
+
* })
|
|
7177
|
+
*
|
|
7178
|
+
* // BridgeDestinationWithAddress
|
|
7179
|
+
* const chain2 = resolveChainDefinition({
|
|
7180
|
+
* adapter: mockAdapter,
|
|
7181
|
+
* chain: 'Base',
|
|
7182
|
+
* recipientAddress: '0x123...'
|
|
7183
|
+
* })
|
|
7184
|
+
* ```
|
|
7185
|
+
*/
|
|
7186
|
+
function resolveChainDefinition(ctx) {
|
|
7187
|
+
return resolveChainIdentifier(ctx.chain);
|
|
7188
|
+
}
|
|
7189
|
+
/**
|
|
7190
|
+
* Resolves the signer's address from a bridge destination.
|
|
7191
|
+
*
|
|
7192
|
+
* This function resolves the address that will be used for transaction signing,
|
|
7193
|
+
* ignoring any `recipientAddress` field which is handled separately.
|
|
7194
|
+
*
|
|
7195
|
+
* It handles two cases:
|
|
7196
|
+
* - Developer-controlled adapters - returns the explicit address from context
|
|
7197
|
+
* - User-controlled adapters - calls getAddress() on the adapter
|
|
7198
|
+
*
|
|
7199
|
+
* @param ctx - The bridge destination to resolve the address from
|
|
7200
|
+
* @returns The resolved signer address string
|
|
7201
|
+
*
|
|
7202
|
+
* @example
|
|
7203
|
+
* ```typescript
|
|
7204
|
+
* // Developer-controlled adapter
|
|
7205
|
+
* const addr1 = await resolveAddress({
|
|
7206
|
+
* adapter: devAdapter,
|
|
7207
|
+
* chain: 'Ethereum',
|
|
7208
|
+
* address: '0x1234567890123456789012345678901234567890'
|
|
7209
|
+
* }) // Returns: '0x1234567890123456789012345678901234567890'
|
|
7210
|
+
*
|
|
7211
|
+
* // User-controlled adapter
|
|
7212
|
+
* const addr2 = await resolveAddress({
|
|
7213
|
+
* adapter: userAdapter,
|
|
7214
|
+
* chain: 'Ethereum'
|
|
7215
|
+
* }) // Returns adapter's connected address
|
|
7216
|
+
* ```
|
|
7217
|
+
*/
|
|
7218
|
+
async function resolveAddress(ctx) {
|
|
7219
|
+
// Handle based on adapter's addressContext
|
|
7220
|
+
if (ctx.adapter.capabilities?.addressContext === 'developer-controlled') {
|
|
7221
|
+
// Developer-controlled: address must be provided explicitly
|
|
7222
|
+
if ('address' in ctx && ctx.address) {
|
|
7223
|
+
return ctx.address;
|
|
7224
|
+
}
|
|
7225
|
+
throw new Error('Address is required in context for developer-controlled adapters. ' +
|
|
7226
|
+
'Please provide: { adapter, chain, address: "0x..." }');
|
|
7227
|
+
}
|
|
7228
|
+
else {
|
|
7229
|
+
// User-controlled: address should not be provided (auto-resolved from adapter)
|
|
7230
|
+
if ('address' in ctx && ctx.address) {
|
|
7231
|
+
throw new Error('Address should not be provided for user-controlled adapters. ' +
|
|
7232
|
+
'The address is automatically resolved from the connected wallet.');
|
|
7233
|
+
}
|
|
7234
|
+
// Derive address from adapter
|
|
7235
|
+
const chain = resolveChainDefinition(ctx);
|
|
7236
|
+
return await ctx.adapter.getAddress(chain);
|
|
7237
|
+
}
|
|
7238
|
+
}
|
|
7239
|
+
/**
|
|
7240
|
+
* Resolves the amount of a transfer by formatting it according to the token's decimal places.
|
|
7241
|
+
*
|
|
7242
|
+
* This function takes the raw amount from the transfer parameters and formats it
|
|
7243
|
+
* using the appropriate decimal places for the specified token. Currently supports
|
|
7244
|
+
* USDC (6 decimals) and falls back to the raw amount for other tokens.
|
|
7245
|
+
*
|
|
7246
|
+
* @param params - The bridge parameters containing the amount, token type, and from context
|
|
7247
|
+
* @returns The formatted amount string with proper decimal places
|
|
7248
|
+
*
|
|
7249
|
+
* @example
|
|
7250
|
+
* ```typescript
|
|
7251
|
+
* import { Adapter } from '@core/adapter'
|
|
7252
|
+
*
|
|
7253
|
+
* const params = {
|
|
7254
|
+
* amount: '1000000',
|
|
7255
|
+
* token: 'USDC',
|
|
7256
|
+
* from: { adapter: mockAdapter, chain: Ethereum },
|
|
7257
|
+
* to: { adapter: mockAdapter, chain: Base }
|
|
7258
|
+
* }
|
|
7259
|
+
* const formattedAmount = resolveAmount(params) // Returns '1000000000000'
|
|
7260
|
+
* ```
|
|
7261
|
+
*/
|
|
7262
|
+
function resolveAmount(params) {
|
|
7263
|
+
if (params.token === 'USDC') {
|
|
7264
|
+
return parseUnits(params.amount, 6).toString();
|
|
7265
|
+
}
|
|
7266
|
+
return params.amount;
|
|
7267
|
+
}
|
|
7268
|
+
/**
|
|
7269
|
+
* Resolves the invocation context for a bridge operation.
|
|
7270
|
+
*
|
|
7271
|
+
* Takes optional invocation metadata and resolves it into a full
|
|
7272
|
+
* InvocationContext with BridgeKit caller information appended.
|
|
7273
|
+
*
|
|
7274
|
+
* @param invocationMeta - Optional invocation metadata for tracing and correlation
|
|
7275
|
+
* @returns An InvocationContext with traceId, runtime, tokens, and caller chain
|
|
7276
|
+
*
|
|
7277
|
+
* @example
|
|
7278
|
+
* ```typescript
|
|
7279
|
+
* // With user-provided invocation metadata
|
|
7280
|
+
* const invocation = resolveBridgeInvocation({
|
|
7281
|
+
* traceId: 'my-custom-trace-id',
|
|
7282
|
+
* callers: [{ type: 'app', name: 'MyDApp' }],
|
|
7283
|
+
* })
|
|
7284
|
+
* // invocation.traceId === 'my-custom-trace-id'
|
|
7285
|
+
* // invocation.callers === [{ type: 'app', name: 'MyDApp' }, { type: 'kit', name: 'BridgeKit' }]
|
|
7286
|
+
*
|
|
7287
|
+
* // Without invocation metadata (auto-generated traceId)
|
|
7288
|
+
* const invocation2 = resolveBridgeInvocation()
|
|
7289
|
+
* // invocation2.traceId === <auto-generated OpenTelemetry-compatible trace ID>
|
|
7290
|
+
* // invocation2.callers === [{ type: 'kit', name: 'BridgeKit' }]
|
|
7291
|
+
* ```
|
|
7292
|
+
*/
|
|
7293
|
+
function resolveBridgeInvocation(invocationMeta) {
|
|
7294
|
+
const bridgeKitCaller = {
|
|
7295
|
+
type: 'kit',
|
|
7296
|
+
name: 'BridgeKit',
|
|
7297
|
+
version: pkg.version,
|
|
7298
|
+
};
|
|
7299
|
+
// Create default runtime and tokens for invocation context resolution
|
|
7300
|
+
const defaults = {
|
|
7301
|
+
runtime: createRuntime(),
|
|
7302
|
+
tokens: createTokenRegistry(),
|
|
7303
|
+
};
|
|
7304
|
+
// Resolve invocation metadata to full context, then extend with BridgeKit caller
|
|
7305
|
+
const baseContext = resolveInvocationContext(invocationMeta, defaults);
|
|
7306
|
+
return extendInvocationContext(baseContext, bridgeKitCaller);
|
|
7307
|
+
}
|
|
7308
|
+
/**
|
|
7309
|
+
* Resolves and normalizes bridge configuration for the provider.
|
|
7310
|
+
*
|
|
7311
|
+
* This function takes the optional configuration from bridge parameters and returns
|
|
5624
7312
|
* a normalized BridgeConfig with:
|
|
5625
7313
|
* - Default transfer speed set to FAST if not provided
|
|
5626
7314
|
* - Max fee values converted from human-readable to smallest units (6 decimals for USDC)
|
|
@@ -5685,6 +7373,7 @@ function resolveConfig(params) {
|
|
|
5685
7373
|
* - Amount formatting
|
|
5686
7374
|
*
|
|
5687
7375
|
* @param params - The bridge parameters containing source/destination contexts, amount, and token
|
|
7376
|
+
* @param invocationMeta - Optional invocation metadata for tracing and correlation
|
|
5688
7377
|
* @returns Promise resolving to normalized bridge parameters for provider consumption
|
|
5689
7378
|
* @throws \{Error\} If parameters cannot be resolved (invalid chains, etc.)
|
|
5690
7379
|
*
|
|
@@ -5704,22 +7393,32 @@ function resolveConfig(params) {
|
|
|
5704
7393
|
* ```
|
|
5705
7394
|
*/
|
|
5706
7395
|
async function resolveBridgeParams(params) {
|
|
5707
|
-
|
|
5708
|
-
const
|
|
7396
|
+
// Resolve chains
|
|
7397
|
+
const fromChain = resolveChainIdentifier(params.from.chain);
|
|
7398
|
+
const toChain = resolveChainIdentifier(params.to.chain);
|
|
7399
|
+
// Check if this is a forwarder-only destination (no adapter)
|
|
7400
|
+
const isForwarderOnly = isForwarderOnlyDestination(params.to);
|
|
5709
7401
|
// Validate adapter chain support after resolution
|
|
5710
|
-
// This ensures adapters support the resolved chains before proceeding
|
|
5711
7402
|
params.from.adapter.validateChainSupport(fromChain);
|
|
5712
|
-
|
|
7403
|
+
// Only validate destination adapter if it exists
|
|
7404
|
+
if (!isForwarderOnly && 'adapter' in params.to) {
|
|
7405
|
+
params.to.adapter.validateChainSupport(toChain);
|
|
7406
|
+
}
|
|
7407
|
+
// For forwarder-only destinations, use recipientAddress directly
|
|
7408
|
+
// For other destinations, resolve address from adapter
|
|
5713
7409
|
const [fromAddress, toAddress] = await Promise.all([
|
|
5714
7410
|
resolveAddress(params.from),
|
|
5715
|
-
|
|
7411
|
+
isForwarderOnly
|
|
7412
|
+
? Promise.resolve(params.to.recipientAddress)
|
|
7413
|
+
: resolveAddress(params.to),
|
|
5716
7414
|
]);
|
|
5717
7415
|
const token = params.token ?? 'USDC';
|
|
5718
|
-
// Extract adapters - now always from explicit contexts
|
|
5719
|
-
const fromAdapter = params.from.adapter;
|
|
5720
|
-
const toAdapter = params.to.adapter;
|
|
5721
7416
|
// Extract recipientAddress from params.to if it exists
|
|
5722
7417
|
const recipientAddress = 'recipientAddress' in params.to ? params.to.recipientAddress : undefined;
|
|
7418
|
+
// Extract useForwarder from params.to if it exists
|
|
7419
|
+
const useForwarder = 'useForwarder' in params.to ? params.to.useForwarder : undefined;
|
|
7420
|
+
// Resolve invocation metadata to full InvocationContext with BridgeKit caller
|
|
7421
|
+
const resolvedInvocation = resolveBridgeInvocation(params.invocationMeta);
|
|
5723
7422
|
return {
|
|
5724
7423
|
amount: resolveAmount({
|
|
5725
7424
|
...params,
|
|
@@ -5729,16 +7428,21 @@ async function resolveBridgeParams(params) {
|
|
|
5729
7428
|
config: resolveConfig({
|
|
5730
7429
|
...params}),
|
|
5731
7430
|
source: {
|
|
5732
|
-
adapter:
|
|
7431
|
+
adapter: params.from.adapter,
|
|
5733
7432
|
chain: fromChain,
|
|
5734
7433
|
address: fromAddress,
|
|
5735
7434
|
},
|
|
5736
7435
|
destination: {
|
|
5737
|
-
adapter
|
|
7436
|
+
// Only include adapter if it exists (not forwarder-only)
|
|
7437
|
+
...(!isForwarderOnly &&
|
|
7438
|
+
'adapter' in params.to && { adapter: params.to.adapter }),
|
|
5738
7439
|
chain: toChain,
|
|
5739
7440
|
address: toAddress,
|
|
5740
7441
|
...(recipientAddress !== undefined && { recipientAddress }),
|
|
7442
|
+
...(useForwarder !== undefined && { useForwarder }),
|
|
5741
7443
|
},
|
|
7444
|
+
// Pass resolved InvocationContext as invocationMeta (superset is compatible)
|
|
7445
|
+
invocationMeta: resolvedInvocation,
|
|
5742
7446
|
};
|
|
5743
7447
|
}
|
|
5744
7448
|
|
|
@@ -5816,6 +7520,36 @@ const formatBridgeResult = (result, formatDirection) => {
|
|
|
5816
7520
|
};
|
|
5817
7521
|
};
|
|
5818
7522
|
|
|
7523
|
+
/**
|
|
7524
|
+
* BridgeKit caller component for retry and estimate operations.
|
|
7525
|
+
*/
|
|
7526
|
+
const BRIDGE_KIT_CALLER = {
|
|
7527
|
+
type: 'kit',
|
|
7528
|
+
name: 'BridgeKit',
|
|
7529
|
+
version: pkg.version,
|
|
7530
|
+
};
|
|
7531
|
+
/**
|
|
7532
|
+
* Merge BridgeKit's caller into the invocation metadata for retry operations.
|
|
7533
|
+
*
|
|
7534
|
+
* Prepends the BridgeKit caller to the callers array.
|
|
7535
|
+
*
|
|
7536
|
+
* @param invocationMeta - Optional invocation metadata provided by caller.
|
|
7537
|
+
* @returns Merged invocation metadata with BridgeKit caller prepended.
|
|
7538
|
+
*
|
|
7539
|
+
* @internal
|
|
7540
|
+
*/
|
|
7541
|
+
function mergeRetryInvocationMeta(invocationMeta) {
|
|
7542
|
+
// Prepend BridgeKit caller to existing callers array
|
|
7543
|
+
const existingCallers = invocationMeta?.callers ?? [];
|
|
7544
|
+
return invocationMeta
|
|
7545
|
+
? {
|
|
7546
|
+
...invocationMeta,
|
|
7547
|
+
callers: [BRIDGE_KIT_CALLER, ...existingCallers],
|
|
7548
|
+
}
|
|
7549
|
+
: {
|
|
7550
|
+
callers: [BRIDGE_KIT_CALLER, ...existingCallers],
|
|
7551
|
+
};
|
|
7552
|
+
}
|
|
5819
7553
|
/**
|
|
5820
7554
|
* Route cross-chain USDC bridging through Circle's Cross-Chain Transfer Protocol v2 (CCTPv2).
|
|
5821
7555
|
*
|
|
@@ -5912,7 +7646,7 @@ class BridgeKit {
|
|
|
5912
7646
|
* - CCTPv2 support for the chain pair
|
|
5913
7647
|
* - Transfer configuration options
|
|
5914
7648
|
*
|
|
5915
|
-
* @param params - The transfer parameters containing source, destination, amount, and
|
|
7649
|
+
* @param params - The transfer parameters containing source, destination, amount, token, and optional invocation metadata
|
|
5916
7650
|
* @returns Promise resolving to the transfer result with transaction details and steps
|
|
5917
7651
|
* @throws {KitError} When any parameter validation fails.
|
|
5918
7652
|
* @throws {Error} When CCTPv2 does not support the specified route.
|
|
@@ -5929,18 +7663,24 @@ class BridgeKit {
|
|
|
5929
7663
|
* privateKey: process.env.PRIVATE_KEY,
|
|
5930
7664
|
* })
|
|
5931
7665
|
*
|
|
7666
|
+
* // Basic usage
|
|
5932
7667
|
* const result = await kit.bridge({
|
|
5933
|
-
* from: {
|
|
5934
|
-
*
|
|
5935
|
-
* chain: 'Ethereum'
|
|
5936
|
-
* },
|
|
5937
|
-
* to: {
|
|
5938
|
-
* adapter,
|
|
5939
|
-
* chain: 'Base'
|
|
5940
|
-
* },
|
|
7668
|
+
* from: { adapter, chain: 'Ethereum' },
|
|
7669
|
+
* to: { adapter, chain: 'Base' },
|
|
5941
7670
|
* amount: '100.50'
|
|
5942
7671
|
* })
|
|
5943
7672
|
*
|
|
7673
|
+
* // With custom invocation metadata
|
|
7674
|
+
* const result = await kit.bridge({
|
|
7675
|
+
* from: { adapter, chain: 'Ethereum' },
|
|
7676
|
+
* to: { adapter, chain: 'Base' },
|
|
7677
|
+
* amount: '100.50',
|
|
7678
|
+
* invocationMeta: {
|
|
7679
|
+
* traceId: 'custom-trace-id',
|
|
7680
|
+
* callers: [{ type: 'app', name: 'MyDApp', version: '1.0.0' }],
|
|
7681
|
+
* },
|
|
7682
|
+
* })
|
|
7683
|
+
*
|
|
5944
7684
|
* // Handle result
|
|
5945
7685
|
* if (result.state === 'success') {
|
|
5946
7686
|
* console.log('Bridge completed!')
|
|
@@ -5986,6 +7726,8 @@ class BridgeKit {
|
|
|
5986
7726
|
* @param context - The retry context containing fresh adapter instances for both
|
|
5987
7727
|
* source and destination chains. These adapters should be properly
|
|
5988
7728
|
* configured with current network connections and signing capabilities.
|
|
7729
|
+
* @param invocationMeta - Optional invocation metadata for tracing and correlation.
|
|
7730
|
+
* If not provided, uses the traceId from the original result.
|
|
5989
7731
|
* @returns A promise that resolves to the updated bridge result after retry execution.
|
|
5990
7732
|
* The result will contain the complete step history including both original
|
|
5991
7733
|
* and retry attempts.
|
|
@@ -6017,31 +7759,35 @@ class BridgeKit {
|
|
|
6017
7759
|
* // ... other properties
|
|
6018
7760
|
* }
|
|
6019
7761
|
*
|
|
7762
|
+
* // Basic retry (uses traceId from original result)
|
|
7763
|
+
* const retryResult = await kit.retry(failedResult, {
|
|
7764
|
+
* from: sourceAdapter,
|
|
7765
|
+
* to: destAdapter
|
|
7766
|
+
* })
|
|
6020
7767
|
*
|
|
6021
|
-
*
|
|
6022
|
-
*
|
|
6023
|
-
*
|
|
6024
|
-
*
|
|
6025
|
-
*
|
|
6026
|
-
*
|
|
6027
|
-
*
|
|
6028
|
-
*
|
|
6029
|
-
*
|
|
6030
|
-
* console.error('Retry failed:', error.message)
|
|
6031
|
-
* // Handle retry failure (may require manual intervention)
|
|
6032
|
-
* }
|
|
7768
|
+
* // Retry with custom invocation metadata
|
|
7769
|
+
* const retryResult = await kit.retry(
|
|
7770
|
+
* failedResult,
|
|
7771
|
+
* { from: sourceAdapter, to: destAdapter },
|
|
7772
|
+
* {
|
|
7773
|
+
* traceId: 'custom-trace-id',
|
|
7774
|
+
* callers: [{ type: 'app', name: 'MyApp' }],
|
|
7775
|
+
* }
|
|
7776
|
+
* )
|
|
6033
7777
|
* ```
|
|
6034
7778
|
*/
|
|
6035
|
-
async retry(result, context) {
|
|
7779
|
+
async retry(result, context, invocationMeta) {
|
|
6036
7780
|
const provider = this.providers.find((p) => p.name === result.provider);
|
|
6037
7781
|
if (!provider) {
|
|
6038
7782
|
throw new Error(`Provider ${result.provider} not found`);
|
|
6039
7783
|
}
|
|
7784
|
+
// Merge BridgeKit caller into invocation metadata for retry operation
|
|
7785
|
+
const mergedMeta = mergeRetryInvocationMeta(invocationMeta);
|
|
6040
7786
|
// Format the bridge result into bigint string values for internal use
|
|
6041
7787
|
const formattedBridgeResultInternal = formatBridgeResult(result, 'to-internal');
|
|
6042
7788
|
// Execute the retry using the provider
|
|
6043
7789
|
// Format the bridge result into human-readable string values for the user
|
|
6044
|
-
return formatBridgeResult(await provider.retry(formattedBridgeResultInternal, context), 'to-human-readable');
|
|
7790
|
+
return formatBridgeResult(await provider.retry(formattedBridgeResultInternal, context, mergedMeta), 'to-human-readable');
|
|
6045
7791
|
}
|
|
6046
7792
|
/**
|
|
6047
7793
|
* Estimate the cost and fees for a cross-chain USDC bridge operation.
|
|
@@ -6049,13 +7795,15 @@ class BridgeKit {
|
|
|
6049
7795
|
* This method calculates the expected gas fees and protocol costs for bridging
|
|
6050
7796
|
* without actually executing the transaction. It performs the same validation
|
|
6051
7797
|
* as the bridge method but stops before execution.
|
|
6052
|
-
*
|
|
7798
|
+
*
|
|
7799
|
+
* @param params - The bridge parameters for cost estimation, including optional invocation metadata
|
|
6053
7800
|
* @returns Promise resolving to detailed cost breakdown including gas estimates
|
|
6054
7801
|
* @throws {KitError} When the parameters are invalid.
|
|
6055
7802
|
* @throws {UnsupportedRouteError} When the route is not supported.
|
|
6056
7803
|
*
|
|
6057
7804
|
* @example
|
|
6058
7805
|
* ```typescript
|
|
7806
|
+
* // Basic usage
|
|
6059
7807
|
* const estimate = await kit.estimate({
|
|
6060
7808
|
* from: { adapter: adapter, chain: 'Ethereum' },
|
|
6061
7809
|
* to: { adapter: adapter, chain: 'Base' },
|
|
@@ -6063,6 +7811,18 @@ class BridgeKit {
|
|
|
6063
7811
|
* token: 'USDC'
|
|
6064
7812
|
* })
|
|
6065
7813
|
* console.log('Estimated cost:', estimate.totalCost)
|
|
7814
|
+
*
|
|
7815
|
+
* // With custom invocation metadata
|
|
7816
|
+
* const estimate = await kit.estimate({
|
|
7817
|
+
* from: { adapter: adapter, chain: 'Ethereum' },
|
|
7818
|
+
* to: { adapter: adapter, chain: 'Base' },
|
|
7819
|
+
* amount: '10.50',
|
|
7820
|
+
* token: 'USDC',
|
|
7821
|
+
* invocationMeta: {
|
|
7822
|
+
* traceId: 'custom-trace-id',
|
|
7823
|
+
* callers: [{ type: 'app', name: 'MyDApp', version: '1.0.0' }],
|
|
7824
|
+
* },
|
|
7825
|
+
* })
|
|
6066
7826
|
* ```
|
|
6067
7827
|
*/
|
|
6068
7828
|
async estimate(params) {
|
|
@@ -6114,6 +7874,9 @@ class BridgeKit {
|
|
|
6114
7874
|
* // Get only EVM mainnet chains
|
|
6115
7875
|
* const evmMainnets = kit.getSupportedChains({ chainType: 'evm', isTestnet: false })
|
|
6116
7876
|
*
|
|
7877
|
+
* // Get only chains that support forwarding
|
|
7878
|
+
* const forwarderChains = kit.getSupportedChains({ forwarderSupported: true })
|
|
7879
|
+
*
|
|
6117
7880
|
* console.log('Supported chains:')
|
|
6118
7881
|
* allChains.forEach(chain => {
|
|
6119
7882
|
* console.log(`- ${chain.name} (${chain.type})`)
|
|
@@ -6147,6 +7910,18 @@ class BridgeKit {
|
|
|
6147
7910
|
if (options?.isTestnet !== undefined) {
|
|
6148
7911
|
chains = chains.filter((chain) => chain.isTestnet === options.isTestnet);
|
|
6149
7912
|
}
|
|
7913
|
+
// Apply forwarder support filter if provided
|
|
7914
|
+
if (options?.forwarderSupported !== undefined) {
|
|
7915
|
+
chains = chains.filter((chain) => {
|
|
7916
|
+
const fs = chain.cctp?.forwarderSupported;
|
|
7917
|
+
if (!fs) {
|
|
7918
|
+
return !options.forwarderSupported;
|
|
7919
|
+
}
|
|
7920
|
+
return options.forwarderSupported
|
|
7921
|
+
? fs.source || fs.destination
|
|
7922
|
+
: !fs.source && !fs.destination;
|
|
7923
|
+
});
|
|
7924
|
+
}
|
|
6150
7925
|
return chains;
|
|
6151
7926
|
}
|
|
6152
7927
|
/**
|
|
@@ -6342,6 +8117,10 @@ exports.NetworkError = NetworkError;
|
|
|
6342
8117
|
exports.OnchainError = OnchainError;
|
|
6343
8118
|
exports.RpcError = RpcError;
|
|
6344
8119
|
exports.bridgeParamsWithChainIdentifierSchema = bridgeParamsWithChainIdentifierSchema;
|
|
8120
|
+
exports.createRuntime = createRuntime;
|
|
8121
|
+
exports.createTokenRegistry = createTokenRegistry;
|
|
8122
|
+
exports.createTraceId = createTraceId;
|
|
8123
|
+
exports.extendInvocationContext = extendInvocationContext;
|
|
6345
8124
|
exports.getErrorCode = getErrorCode;
|
|
6346
8125
|
exports.getErrorMessage = getErrorMessage;
|
|
6347
8126
|
exports.isBalanceError = isBalanceError;
|
|
@@ -6353,5 +8132,6 @@ exports.isOnchainError = isOnchainError;
|
|
|
6353
8132
|
exports.isRetryableError = isRetryableError;
|
|
6354
8133
|
exports.isRpcError = isRpcError;
|
|
6355
8134
|
exports.resolveChainIdentifier = resolveChainIdentifier;
|
|
8135
|
+
exports.resolveInvocationContext = resolveInvocationContext;
|
|
6356
8136
|
exports.setExternalPrefix = setExternalPrefix;
|
|
6357
8137
|
//# sourceMappingURL=index.cjs.map
|