@rhinestone/shared-configs 1.9.0 → 1.10.1
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/dist/configs/chains.json +141 -0
- package/dist/scripts/__tests__/validation.test.js +170 -0
- package/dist/scripts/generate.js +28 -5
- package/dist/scripts/validation.d.ts +57 -0
- package/dist/scripts/validation.d.ts.map +1 -1
- package/dist/scripts/validation.js +213 -2
- package/dist/src/__tests__/chainFactsClient.test.js +12 -2
- package/dist/src/__tests__/chainVirtualization.test.js +9 -3
- package/dist/src/chainVirtualization.d.ts +10 -8
- package/dist/src/chainVirtualization.d.ts.map +1 -1
- package/dist/src/chainVirtualization.js +11 -12
- package/dist/src/chains.d.ts +14 -3
- package/dist/src/chains.d.ts.map +1 -1
- package/dist/src/chains.js +141 -0
- package/dist/src/generated/packageVersion.d.ts +1 -1
- package/dist/src/generated/packageVersion.d.ts.map +1 -1
- package/dist/src/generated/packageVersion.js +1 -1
- package/dist/src/index.d.ts +2 -2
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/types.d.ts +38 -6
- package/dist/src/types.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/configs/chains.json
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"1": {
|
|
3
|
+
"caip2": "eip155:1",
|
|
4
|
+
"explorer": {
|
|
5
|
+
"url": "https://etherscan.io",
|
|
6
|
+
"addressPath": "/address/",
|
|
7
|
+
"txPath": "/tx/"
|
|
8
|
+
},
|
|
3
9
|
"name": "Ethereum",
|
|
4
10
|
"nativeToken": {
|
|
5
11
|
"address": "0x0000000000000000000000000000000000000000",
|
|
@@ -84,6 +90,12 @@
|
|
|
84
90
|
}
|
|
85
91
|
},
|
|
86
92
|
"10": {
|
|
93
|
+
"caip2": "eip155:10",
|
|
94
|
+
"explorer": {
|
|
95
|
+
"url": "https://optimistic.etherscan.io",
|
|
96
|
+
"addressPath": "/address/",
|
|
97
|
+
"txPath": "/tx/"
|
|
98
|
+
},
|
|
87
99
|
"name": "OP Mainnet",
|
|
88
100
|
"nativeToken": {
|
|
89
101
|
"address": "0x0000000000000000000000000000000000000000",
|
|
@@ -179,6 +191,12 @@
|
|
|
179
191
|
}
|
|
180
192
|
},
|
|
181
193
|
"56": {
|
|
194
|
+
"caip2": "eip155:56",
|
|
195
|
+
"explorer": {
|
|
196
|
+
"url": "https://bscscan.com",
|
|
197
|
+
"addressPath": "/address/",
|
|
198
|
+
"txPath": "/tx/"
|
|
199
|
+
},
|
|
182
200
|
"name": "BNB Smart Chain",
|
|
183
201
|
"nativeToken": {
|
|
184
202
|
"address": "0x0000000000000000000000000000000000000000",
|
|
@@ -247,6 +265,12 @@
|
|
|
247
265
|
}
|
|
248
266
|
},
|
|
249
267
|
"100": {
|
|
268
|
+
"caip2": "eip155:100",
|
|
269
|
+
"explorer": {
|
|
270
|
+
"url": "https://gnosisscan.io",
|
|
271
|
+
"addressPath": "/address/",
|
|
272
|
+
"txPath": "/tx/"
|
|
273
|
+
},
|
|
250
274
|
"name": "Gnosis",
|
|
251
275
|
"nativeToken": {
|
|
252
276
|
"address": "0x0000000000000000000000000000000000000000",
|
|
@@ -282,6 +306,12 @@
|
|
|
282
306
|
}
|
|
283
307
|
},
|
|
284
308
|
"130": {
|
|
309
|
+
"caip2": "eip155:130",
|
|
310
|
+
"explorer": {
|
|
311
|
+
"url": "https://uniscan.xyz",
|
|
312
|
+
"addressPath": "/address/",
|
|
313
|
+
"txPath": "/tx/"
|
|
314
|
+
},
|
|
285
315
|
"name": "Unichain",
|
|
286
316
|
"nativeToken": {
|
|
287
317
|
"address": "0x0000000000000000000000000000000000000000",
|
|
@@ -365,6 +395,12 @@
|
|
|
365
395
|
}
|
|
366
396
|
},
|
|
367
397
|
"137": {
|
|
398
|
+
"caip2": "eip155:137",
|
|
399
|
+
"explorer": {
|
|
400
|
+
"url": "https://polygonscan.com",
|
|
401
|
+
"addressPath": "/address/",
|
|
402
|
+
"txPath": "/tx/"
|
|
403
|
+
},
|
|
368
404
|
"name": "Polygon",
|
|
369
405
|
"nativeToken": {
|
|
370
406
|
"address": "0x0000000000000000000000000000000000000000",
|
|
@@ -436,6 +472,12 @@
|
|
|
436
472
|
}
|
|
437
473
|
},
|
|
438
474
|
"143": {
|
|
475
|
+
"caip2": "eip155:143",
|
|
476
|
+
"explorer": {
|
|
477
|
+
"url": "https://monadscan.com",
|
|
478
|
+
"addressPath": "/address/",
|
|
479
|
+
"txPath": "/tx/"
|
|
480
|
+
},
|
|
439
481
|
"name": "Monad",
|
|
440
482
|
"nativeToken": {
|
|
441
483
|
"address": "0x0000000000000000000000000000000000000000",
|
|
@@ -505,6 +547,12 @@
|
|
|
505
547
|
}
|
|
506
548
|
},
|
|
507
549
|
"146": {
|
|
550
|
+
"caip2": "eip155:146",
|
|
551
|
+
"explorer": {
|
|
552
|
+
"url": "https://sonicscan.org",
|
|
553
|
+
"addressPath": "/address/",
|
|
554
|
+
"txPath": "/tx/"
|
|
555
|
+
},
|
|
508
556
|
"name": "Sonic",
|
|
509
557
|
"nativeToken": {
|
|
510
558
|
"address": "0x0000000000000000000000000000000000000000",
|
|
@@ -546,6 +594,12 @@
|
|
|
546
594
|
}
|
|
547
595
|
},
|
|
548
596
|
"999": {
|
|
597
|
+
"caip2": "eip155:999",
|
|
598
|
+
"explorer": {
|
|
599
|
+
"url": "https://hyperevmscan.io",
|
|
600
|
+
"addressPath": "/address/",
|
|
601
|
+
"txPath": "/tx/"
|
|
602
|
+
},
|
|
549
603
|
"name": "HyperEVM",
|
|
550
604
|
"nativeToken": {
|
|
551
605
|
"address": "0x0000000000000000000000000000000000000000",
|
|
@@ -599,6 +653,11 @@
|
|
|
599
653
|
},
|
|
600
654
|
"1337": {
|
|
601
655
|
"caip2": "hypercore:mainnet",
|
|
656
|
+
"explorer": {
|
|
657
|
+
"addressPath": "/explorer/address/",
|
|
658
|
+
"txPath": "/explorer/tx/",
|
|
659
|
+
"url": "https://app.hyperliquid.xyz"
|
|
660
|
+
},
|
|
602
661
|
"name": "HyperCore",
|
|
603
662
|
"nativeToken": {
|
|
604
663
|
"address": "0x0000000000000000000000000000000000000000",
|
|
@@ -628,6 +687,12 @@
|
|
|
628
687
|
}
|
|
629
688
|
},
|
|
630
689
|
"1868": {
|
|
690
|
+
"caip2": "eip155:1868",
|
|
691
|
+
"explorer": {
|
|
692
|
+
"url": "https://soneium.blockscout.com",
|
|
693
|
+
"addressPath": "/address/",
|
|
694
|
+
"txPath": "/tx/"
|
|
695
|
+
},
|
|
631
696
|
"name": "Soneium Mainnet",
|
|
632
697
|
"nativeToken": {
|
|
633
698
|
"address": "0x0000000000000000000000000000000000000000",
|
|
@@ -696,6 +761,12 @@
|
|
|
696
761
|
}
|
|
697
762
|
},
|
|
698
763
|
"4663": {
|
|
764
|
+
"caip2": "eip155:4663",
|
|
765
|
+
"explorer": {
|
|
766
|
+
"url": "https://robinhoodchain.blockscout.com",
|
|
767
|
+
"addressPath": "/address/",
|
|
768
|
+
"txPath": "/tx/"
|
|
769
|
+
},
|
|
699
770
|
"name": "Robinhood Chain",
|
|
700
771
|
"nativeToken": {
|
|
701
772
|
"address": "0x0000000000000000000000000000000000000000",
|
|
@@ -753,6 +824,12 @@
|
|
|
753
824
|
}
|
|
754
825
|
},
|
|
755
826
|
"8453": {
|
|
827
|
+
"caip2": "eip155:8453",
|
|
828
|
+
"explorer": {
|
|
829
|
+
"url": "https://basescan.org",
|
|
830
|
+
"addressPath": "/address/",
|
|
831
|
+
"txPath": "/tx/"
|
|
832
|
+
},
|
|
756
833
|
"name": "Base",
|
|
757
834
|
"nativeToken": {
|
|
758
835
|
"address": "0x0000000000000000000000000000000000000000",
|
|
@@ -822,6 +899,12 @@
|
|
|
822
899
|
}
|
|
823
900
|
},
|
|
824
901
|
"9745": {
|
|
902
|
+
"caip2": "eip155:9745",
|
|
903
|
+
"explorer": {
|
|
904
|
+
"url": "https://plasmascan.to",
|
|
905
|
+
"addressPath": "/address/",
|
|
906
|
+
"txPath": "/tx/"
|
|
907
|
+
},
|
|
825
908
|
"name": "Plasma",
|
|
826
909
|
"nativeToken": {
|
|
827
910
|
"address": "0x0000000000000000000000000000000000000000",
|
|
@@ -865,6 +948,12 @@
|
|
|
865
948
|
}
|
|
866
949
|
},
|
|
867
950
|
"9746": {
|
|
951
|
+
"caip2": "eip155:9746",
|
|
952
|
+
"explorer": {
|
|
953
|
+
"url": "https://testnet.plasmascan.to",
|
|
954
|
+
"addressPath": "/address/",
|
|
955
|
+
"txPath": "/tx/"
|
|
956
|
+
},
|
|
868
957
|
"name": "Plasma Testnet",
|
|
869
958
|
"nativeToken": {
|
|
870
959
|
"address": "0x0000000000000000000000000000000000000000",
|
|
@@ -910,6 +999,12 @@
|
|
|
910
999
|
}
|
|
911
1000
|
},
|
|
912
1001
|
"42161": {
|
|
1002
|
+
"caip2": "eip155:42161",
|
|
1003
|
+
"explorer": {
|
|
1004
|
+
"url": "https://arbiscan.io",
|
|
1005
|
+
"addressPath": "/address/",
|
|
1006
|
+
"txPath": "/tx/"
|
|
1007
|
+
},
|
|
913
1008
|
"name": "Arbitrum One",
|
|
914
1009
|
"nativeToken": {
|
|
915
1010
|
"address": "0x0000000000000000000000000000000000000000",
|
|
@@ -994,6 +1089,12 @@
|
|
|
994
1089
|
}
|
|
995
1090
|
},
|
|
996
1091
|
"43114": {
|
|
1092
|
+
"caip2": "eip155:43114",
|
|
1093
|
+
"explorer": {
|
|
1094
|
+
"url": "https://snowtrace.io",
|
|
1095
|
+
"addressPath": "/address/",
|
|
1096
|
+
"txPath": "/tx/"
|
|
1097
|
+
},
|
|
997
1098
|
"name": "Avalanche",
|
|
998
1099
|
"nativeToken": {
|
|
999
1100
|
"address": "0x0000000000000000000000000000000000000000",
|
|
@@ -1071,6 +1172,12 @@
|
|
|
1071
1172
|
}
|
|
1072
1173
|
},
|
|
1073
1174
|
"84532": {
|
|
1175
|
+
"caip2": "eip155:84532",
|
|
1176
|
+
"explorer": {
|
|
1177
|
+
"url": "https://sepolia.basescan.org",
|
|
1178
|
+
"addressPath": "/address/",
|
|
1179
|
+
"txPath": "/tx/"
|
|
1180
|
+
},
|
|
1074
1181
|
"name": "Base Sepolia",
|
|
1075
1182
|
"nativeToken": {
|
|
1076
1183
|
"address": "0x0000000000000000000000000000000000000000",
|
|
@@ -1146,6 +1253,12 @@
|
|
|
1146
1253
|
}
|
|
1147
1254
|
},
|
|
1148
1255
|
"421614": {
|
|
1256
|
+
"caip2": "eip155:421614",
|
|
1257
|
+
"explorer": {
|
|
1258
|
+
"url": "https://sepolia.arbiscan.io",
|
|
1259
|
+
"addressPath": "/address/",
|
|
1260
|
+
"txPath": "/tx/"
|
|
1261
|
+
},
|
|
1149
1262
|
"name": "Arbitrum Sepolia",
|
|
1150
1263
|
"nativeToken": {
|
|
1151
1264
|
"address": "0x0000000000000000000000000000000000000000",
|
|
@@ -1207,6 +1320,12 @@
|
|
|
1207
1320
|
}
|
|
1208
1321
|
},
|
|
1209
1322
|
"747474": {
|
|
1323
|
+
"caip2": "eip155:747474",
|
|
1324
|
+
"explorer": {
|
|
1325
|
+
"url": "https://katanascan.com",
|
|
1326
|
+
"addressPath": "/address/",
|
|
1327
|
+
"txPath": "/tx/"
|
|
1328
|
+
},
|
|
1210
1329
|
"name": "Katana",
|
|
1211
1330
|
"nativeToken": {
|
|
1212
1331
|
"address": "0x0000000000000000000000000000000000000000",
|
|
@@ -1265,6 +1384,12 @@
|
|
|
1265
1384
|
}
|
|
1266
1385
|
},
|
|
1267
1386
|
"11155111": {
|
|
1387
|
+
"caip2": "eip155:11155111",
|
|
1388
|
+
"explorer": {
|
|
1389
|
+
"url": "https://sepolia.etherscan.io",
|
|
1390
|
+
"addressPath": "/address/",
|
|
1391
|
+
"txPath": "/tx/"
|
|
1392
|
+
},
|
|
1268
1393
|
"name": "Sepolia",
|
|
1269
1394
|
"nativeToken": {
|
|
1270
1395
|
"address": "0x0000000000000000000000000000000000000000",
|
|
@@ -1326,6 +1451,12 @@
|
|
|
1326
1451
|
}
|
|
1327
1452
|
},
|
|
1328
1453
|
"11155420": {
|
|
1454
|
+
"caip2": "eip155:11155420",
|
|
1455
|
+
"explorer": {
|
|
1456
|
+
"url": "https://optimism-sepolia.blockscout.com",
|
|
1457
|
+
"addressPath": "/address/",
|
|
1458
|
+
"txPath": "/tx/"
|
|
1459
|
+
},
|
|
1329
1460
|
"name": "OP Sepolia",
|
|
1330
1461
|
"nativeToken": {
|
|
1331
1462
|
"address": "0x0000000000000000000000000000000000000000",
|
|
@@ -1388,6 +1519,11 @@
|
|
|
1388
1519
|
},
|
|
1389
1520
|
"728126428": {
|
|
1390
1521
|
"caip2": "tron:mainnet",
|
|
1522
|
+
"explorer": {
|
|
1523
|
+
"addressPath": "/#/address/",
|
|
1524
|
+
"txPath": "/#/transaction/",
|
|
1525
|
+
"url": "https://tronscan.org"
|
|
1526
|
+
},
|
|
1391
1527
|
"name": "Tron",
|
|
1392
1528
|
"nativeToken": {
|
|
1393
1529
|
"address": "0x0000000000000000000000000000000000000000",
|
|
@@ -1429,6 +1565,11 @@
|
|
|
1429
1565
|
},
|
|
1430
1566
|
"792703809": {
|
|
1431
1567
|
"caip2": "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp",
|
|
1568
|
+
"explorer": {
|
|
1569
|
+
"addressPath": "/address/",
|
|
1570
|
+
"txPath": "/tx/",
|
|
1571
|
+
"url": "https://solscan.io"
|
|
1572
|
+
},
|
|
1432
1573
|
"name": "Solana",
|
|
1433
1574
|
"nativeToken": {
|
|
1434
1575
|
"address": "11111111111111111111111111111111",
|
|
@@ -11,6 +11,12 @@ function validChain(overrides = {}) {
|
|
|
11
11
|
vmType: "evm",
|
|
12
12
|
network: "mainnet",
|
|
13
13
|
stack: "vanilla",
|
|
14
|
+
caip2: "eip155:1",
|
|
15
|
+
explorer: {
|
|
16
|
+
url: "https://etherscan.io",
|
|
17
|
+
addressPath: "/address/",
|
|
18
|
+
txPath: "/tx/",
|
|
19
|
+
},
|
|
14
20
|
nativeToken,
|
|
15
21
|
wrappedNativeToken: { ...nativeToken, symbol: "WETH" },
|
|
16
22
|
tokens: [{ ...nativeToken, balanceSlot: null, approvalSlot: null }],
|
|
@@ -53,9 +59,26 @@ function errorsFor(chain) {
|
|
|
53
59
|
["a non-boolean virtual flag", { virtual: "true" }],
|
|
54
60
|
["a missing network", { network: undefined }],
|
|
55
61
|
["an unknown network", { network: "devnet" }],
|
|
62
|
+
// `explorer` is required (nullable) in the public ChainEntry, so the build
|
|
63
|
+
// gate has to reject what the type says cannot happen.
|
|
64
|
+
["a missing explorer", { explorer: undefined }],
|
|
65
|
+
["an explorer that is not an object", { explorer: "https://etherscan.io" }],
|
|
66
|
+
["an explorer missing txPath", {
|
|
67
|
+
explorer: { url: "https://etherscan.io", addressPath: "/address/" },
|
|
68
|
+
}],
|
|
69
|
+
// Links are built by concatenation, so the slash convention is load-bearing.
|
|
70
|
+
["an explorer path with no leading slash", {
|
|
71
|
+
explorer: { url: "https://etherscan.io", addressPath: "address/", txPath: "/tx/" },
|
|
72
|
+
}],
|
|
73
|
+
["an explorer url with a trailing slash", {
|
|
74
|
+
explorer: { url: "https://etherscan.io/", addressPath: "/address/", txPath: "/tx/" },
|
|
75
|
+
}],
|
|
56
76
|
])("rejects %s", (_label, overrides) => {
|
|
57
77
|
(0, bun_test_1.expect)(errorsFor(validChain(overrides)).length).toBeGreaterThan(0);
|
|
58
78
|
});
|
|
79
|
+
(0, bun_test_1.it)("accepts a null explorer, the answer for a chain that has none", () => {
|
|
80
|
+
(0, bun_test_1.expect)(errorsFor(validChain({ explorer: null }))).toEqual([]);
|
|
81
|
+
});
|
|
59
82
|
(0, bun_test_1.it)("names the offending value so the build failure is actionable", () => {
|
|
60
83
|
const [error] = errorsFor(validChain({ settlementLayers: ["TELEPORT"] }));
|
|
61
84
|
(0, bun_test_1.expect)(error.message).toContain("TELEPORT");
|
|
@@ -214,6 +237,153 @@ function errorsFor(chain) {
|
|
|
214
237
|
(0, bun_test_1.expect)(Object.keys(chains)).toHaveLength(Object.keys(index_1.chainRegistry).length);
|
|
215
238
|
});
|
|
216
239
|
});
|
|
240
|
+
(0, bun_test_1.describe)("classifyChainCaip2", () => {
|
|
241
|
+
(0, bun_test_1.it)("derives eip155:<chainId> for a chain that declares none", () => {
|
|
242
|
+
const { chains, issues } = (0, validation_1.classifyChainCaip2)({
|
|
243
|
+
"8453": validChain({ name: "Base", caip2: undefined }),
|
|
244
|
+
});
|
|
245
|
+
(0, bun_test_1.expect)(chains["8453"].caip2).toBe("eip155:8453");
|
|
246
|
+
(0, bun_test_1.expect)(issues).toEqual([]);
|
|
247
|
+
});
|
|
248
|
+
(0, bun_test_1.it)("keeps a declared id for a chain whose key is not an eip155 one", () => {
|
|
249
|
+
const { chains, issues } = (0, validation_1.classifyChainCaip2)({
|
|
250
|
+
"792703809": validChain({ name: "Solana", vmType: "svm", caip2: "solana:abc" }),
|
|
251
|
+
});
|
|
252
|
+
(0, bun_test_1.expect)(chains["792703809"].caip2).toBe("solana:abc");
|
|
253
|
+
(0, bun_test_1.expect)(issues).toEqual([]);
|
|
254
|
+
});
|
|
255
|
+
// The whole reason the field is authored rather than derived: HyperCore's
|
|
256
|
+
// registry key is 1337, but `eip155:1337` is the Hardhat local chain id.
|
|
257
|
+
(0, bun_test_1.it)("keeps a virtual EVM chain's declared id instead of deriving from its key", () => {
|
|
258
|
+
const { chains, issues } = (0, validation_1.classifyChainCaip2)({
|
|
259
|
+
"1337": validChain({
|
|
260
|
+
name: "HyperCore",
|
|
261
|
+
virtual: true,
|
|
262
|
+
caip2: "hypercore:mainnet",
|
|
263
|
+
}),
|
|
264
|
+
});
|
|
265
|
+
(0, bun_test_1.expect)(chains["1337"].caip2).toBe("hypercore:mainnet");
|
|
266
|
+
(0, bun_test_1.expect)(issues).toEqual([]);
|
|
267
|
+
});
|
|
268
|
+
// Deriving here would hand the entry an `eip155:` id that is confidently
|
|
269
|
+
// wrong — HyperCore's `eip155:1337` failure, reintroduced by the mechanism
|
|
270
|
+
// meant to prevent it.
|
|
271
|
+
bun_test_1.it.each([
|
|
272
|
+
["a non-EVM chain", { vmType: "svm" }],
|
|
273
|
+
["a virtual chain", { virtual: true }],
|
|
274
|
+
])("errors when %s declares no caip2, rather than deriving one", (_label, overrides) => {
|
|
275
|
+
const { issues } = (0, validation_1.classifyChainCaip2)({
|
|
276
|
+
"424242": validChain({ ...overrides, caip2: undefined }),
|
|
277
|
+
});
|
|
278
|
+
(0, bun_test_1.expect)(issues).toHaveLength(1);
|
|
279
|
+
(0, bun_test_1.expect)(issues[0].severity).toBe("error");
|
|
280
|
+
(0, bun_test_1.expect)(issues[0].message).toContain("must author its caip2");
|
|
281
|
+
});
|
|
282
|
+
(0, bun_test_1.it)("errors when a real EVM chain declares an id that isn't its own", () => {
|
|
283
|
+
const { issues } = (0, validation_1.classifyChainCaip2)({
|
|
284
|
+
"8453": validChain({ name: "Base", caip2: "eip155:84532" }),
|
|
285
|
+
});
|
|
286
|
+
(0, bun_test_1.expect)(issues).toHaveLength(1);
|
|
287
|
+
(0, bun_test_1.expect)(issues[0].severity).toBe("error");
|
|
288
|
+
(0, bun_test_1.expect)(issues[0].message).toContain("eip155:8453");
|
|
289
|
+
});
|
|
290
|
+
(0, bun_test_1.it)("errors on a malformed declaration rather than falling back to the derived id", () => {
|
|
291
|
+
const { chains, issues } = (0, validation_1.classifyChainCaip2)({
|
|
292
|
+
"1337": validChain({ name: "HyperCore", virtual: true, caip2: "hypercore" }),
|
|
293
|
+
});
|
|
294
|
+
// Left as authored, so the structural check rejects it too — silently
|
|
295
|
+
// publishing eip155:1337 here is the exact failure being guarded against.
|
|
296
|
+
(0, bun_test_1.expect)(chains["1337"].caip2).toBe("hypercore");
|
|
297
|
+
(0, bun_test_1.expect)(issues.some((i) => i.message.includes("malformed"))).toBe(true);
|
|
298
|
+
});
|
|
299
|
+
// chainIdFromCaip2 is a reverse lookup over this field, so a collision makes
|
|
300
|
+
// it silently return whichever entry was iterated first.
|
|
301
|
+
(0, bun_test_1.it)("errors when two chains resolve to the same id", () => {
|
|
302
|
+
const { issues } = (0, validation_1.classifyChainCaip2)({
|
|
303
|
+
"1": validChain(),
|
|
304
|
+
"1337": validChain({ name: "Impostor", virtual: true, caip2: "eip155:1" }),
|
|
305
|
+
});
|
|
306
|
+
(0, bun_test_1.expect)(issues.some((i) => i.message.includes("already uses"))).toBe(true);
|
|
307
|
+
});
|
|
308
|
+
(0, bun_test_1.it)("leaves the real generated registry issue-free", () => {
|
|
309
|
+
const { issues } = (0, validation_1.classifyChainCaip2)(index_1.chainRegistry);
|
|
310
|
+
(0, bun_test_1.expect)(issues).toEqual([]);
|
|
311
|
+
});
|
|
312
|
+
});
|
|
313
|
+
(0, bun_test_1.describe)("classifyChainExplorers", () => {
|
|
314
|
+
const nets = (mainnets, testnets = []) => ({ mainnets, testnets });
|
|
315
|
+
// The classifier's input is pre-stamp, i.e. whatever the jsonnet authored —
|
|
316
|
+
// which for most chains is nothing. `validChain` carries an explorer so the
|
|
317
|
+
// structural checks pass, so strip it to exercise the derivation path.
|
|
318
|
+
const unauthored = (overrides = {}) => validChain({ ...overrides, explorer: undefined });
|
|
319
|
+
(0, bun_test_1.it)("derives the explorer from viem's own chain definition", () => {
|
|
320
|
+
const { chains, issues } = (0, validation_1.classifyChainExplorers)({ "8453": unauthored({ name: "Base" }) }, nets([{ id: 8453, viemChain: "base" }]));
|
|
321
|
+
(0, bun_test_1.expect)(chains["8453"].explorer).toEqual({
|
|
322
|
+
url: "https://basescan.org",
|
|
323
|
+
addressPath: "/address/",
|
|
324
|
+
txPath: "/tx/",
|
|
325
|
+
});
|
|
326
|
+
(0, bun_test_1.expect)(issues).toEqual([]);
|
|
327
|
+
});
|
|
328
|
+
// The regression this exists for: viem's chain 1337 is `localhost`, and
|
|
329
|
+
// HyperCore's registry key is 1337. Resolving by id would hand HyperCore
|
|
330
|
+
// Localhost's metadata — the `eip155:1337` mistake from the other direction.
|
|
331
|
+
(0, bun_test_1.it)("resolves by declared viemChain name, never by chain id", () => {
|
|
332
|
+
const { chains } = (0, validation_1.classifyChainExplorers)({ "1337": unauthored({ name: "HyperCore", virtual: true }) },
|
|
333
|
+
// No viemChain declared, exactly as the registry has it.
|
|
334
|
+
nets([{ id: 1337 }]));
|
|
335
|
+
(0, bun_test_1.expect)(chains["1337"].explorer).toBeNull();
|
|
336
|
+
});
|
|
337
|
+
(0, bun_test_1.it)("lets an authored explorer win, for paths viem cannot express", () => {
|
|
338
|
+
const tronscan = {
|
|
339
|
+
url: "https://tronscan.org",
|
|
340
|
+
addressPath: "/#/address/",
|
|
341
|
+
txPath: "/#/transaction/",
|
|
342
|
+
};
|
|
343
|
+
const { chains, issues } = (0, validation_1.classifyChainExplorers)({
|
|
344
|
+
"728126428": validChain({
|
|
345
|
+
name: "Tron",
|
|
346
|
+
vmType: "tvm",
|
|
347
|
+
explorer: tronscan,
|
|
348
|
+
}),
|
|
349
|
+
}, nets([{ id: 728126428 }]));
|
|
350
|
+
(0, bun_test_1.expect)(chains["728126428"].explorer).toEqual(tronscan);
|
|
351
|
+
(0, bun_test_1.expect)(issues).toEqual([]);
|
|
352
|
+
});
|
|
353
|
+
(0, bun_test_1.it)("errors on a malformed authored explorer", () => {
|
|
354
|
+
const { issues } = (0, validation_1.classifyChainExplorers)({ "1": validChain({ explorer: { url: "https://etherscan.io" } }) }, nets([{ id: 1, viemChain: "mainnet" }]));
|
|
355
|
+
(0, bun_test_1.expect)(issues).toHaveLength(1);
|
|
356
|
+
(0, bun_test_1.expect)(issues[0].severity).toBe("error");
|
|
357
|
+
});
|
|
358
|
+
// A missing explorer costs a hyperlink, not a wrong answer, so it warns
|
|
359
|
+
// rather than failing the build.
|
|
360
|
+
(0, bun_test_1.it)("warns rather than errors when nothing can supply one", () => {
|
|
361
|
+
const { chains, issues } = (0, validation_1.classifyChainExplorers)({ "424242": unauthored({ name: "Newchain" }) }, nets([{ id: 424242 }]));
|
|
362
|
+
(0, bun_test_1.expect)(chains["424242"].explorer).toBeNull();
|
|
363
|
+
(0, bun_test_1.expect)(issues).toHaveLength(1);
|
|
364
|
+
(0, bun_test_1.expect)(issues[0].severity).toBe("warning");
|
|
365
|
+
});
|
|
366
|
+
// Asserted against the shipped registry rather than by re-running the
|
|
367
|
+
// classifier, so this covers what actually reaches the artifact.
|
|
368
|
+
(0, bun_test_1.it)("leaves every shipped chain with a well-formed explorer", () => {
|
|
369
|
+
// HyperCore was the last null. Its explorer is authored (viem ships no
|
|
370
|
+
// HyperCore chain) and must stay Hyperliquid's own — a HyperCore txHash is
|
|
371
|
+
// a Core L1 hash that a HyperEVM scanner cannot resolve.
|
|
372
|
+
const withoutExplorer = Object.entries(index_1.chainRegistry)
|
|
373
|
+
.filter(([, e]) => e.explorer == null)
|
|
374
|
+
.map(([id]) => id);
|
|
375
|
+
(0, bun_test_1.expect)(withoutExplorer).toEqual([]);
|
|
376
|
+
(0, bun_test_1.expect)(index_1.chainRegistry["1337"].explorer.url).toBe("https://app.hyperliquid.xyz");
|
|
377
|
+
for (const [id, entry] of Object.entries(index_1.chainRegistry)) {
|
|
378
|
+
const explorer = entry.explorer;
|
|
379
|
+
if (explorer === null)
|
|
380
|
+
continue;
|
|
381
|
+
(0, bun_test_1.expect)(explorer.url, id).toMatch(/^https:\/\/[^/]+$/);
|
|
382
|
+
(0, bun_test_1.expect)(explorer.addressPath, id).toMatch(/^\/.*\/$/);
|
|
383
|
+
(0, bun_test_1.expect)(explorer.txPath, id).toMatch(/^\/.*\/$/);
|
|
384
|
+
}
|
|
385
|
+
});
|
|
386
|
+
});
|
|
217
387
|
(0, bun_test_1.describe)("classifyChainStacks", () => {
|
|
218
388
|
const nets = (mainnets, testnets = []) => ({ mainnets, testnets });
|
|
219
389
|
// The common case: nothing is authored anywhere. viem already knows what Base
|
package/dist/scripts/generate.js
CHANGED
|
@@ -111,6 +111,15 @@ function renderChains(chainRegistry, mainnets, testnets) {
|
|
|
111
111
|
" decimals: number;",
|
|
112
112
|
"}",
|
|
113
113
|
"",
|
|
114
|
+
"interface Explorer {",
|
|
115
|
+
" /** Origin, no trailing slash. */",
|
|
116
|
+
" url: string;",
|
|
117
|
+
" /** Path to an address page, with both slashes (`/address/`). */",
|
|
118
|
+
" addressPath: string;",
|
|
119
|
+
" /** Path to a transaction page, with both slashes (`/tx/`). */",
|
|
120
|
+
" txPath: string;",
|
|
121
|
+
"}",
|
|
122
|
+
"",
|
|
114
123
|
"interface Chain {",
|
|
115
124
|
" name: string;",
|
|
116
125
|
" vmType: VmType;",
|
|
@@ -118,8 +127,11 @@ function renderChains(chainRegistry, mainnets, testnets) {
|
|
|
118
127
|
" network: ChainNetwork;",
|
|
119
128
|
" /** Execution stack, for consumers that must synthesise a viem `Chain`. */",
|
|
120
129
|
" stack: ChainStack;",
|
|
121
|
-
" /** CAIP-2 chain identifier
|
|
122
|
-
"
|
|
130
|
+
" /** CAIP-2 chain identifier. Present on every chain: authored for the",
|
|
131
|
+
" * non-eip155 ones, derived as `eip155:<chainId>` for the rest. */",
|
|
132
|
+
" caip2: string;",
|
|
133
|
+
" /** Block explorer, or null for a chain that has none (render plain text). */",
|
|
134
|
+
" explorer: Explorer | null;",
|
|
123
135
|
" /** True for virtual chains (e.g. HyperCore) that settle on another chain. */",
|
|
124
136
|
" virtual?: boolean;",
|
|
125
137
|
" nativeToken: NativeToken;",
|
|
@@ -134,7 +146,7 @@ function renderChains(chainRegistry, mainnets, testnets) {
|
|
|
134
146
|
`const chains: Record<SupportedChain, Chain> = ${JSON.stringify(chains, null, 2)};`,
|
|
135
147
|
"",
|
|
136
148
|
"export { chains };",
|
|
137
|
-
"export type { Chain, Token, NativeToken };",
|
|
149
|
+
"export type { Chain, Token, NativeToken, Explorer };",
|
|
138
150
|
].join("\n");
|
|
139
151
|
}
|
|
140
152
|
function runJsonnet(yeetRoot) {
|
|
@@ -367,8 +379,19 @@ async function generate() {
|
|
|
367
379
|
const { chains: networkClassified, issues: networkIssues } = (0, validation_1.classifyChainNetworks)(output.chains, output.mainnets, output.testnets);
|
|
368
380
|
// Then the execution stack, for consumers that have to build a viem `Chain`
|
|
369
381
|
// themselves because the chain postdates their pinned viem.
|
|
370
|
-
const { chains:
|
|
371
|
-
|
|
382
|
+
const { chains: stackClassified, issues: stackIssues } = (0, validation_1.classifyChainStacks)(networkClassified, output.networks);
|
|
383
|
+
// Then the canonical CAIP-2 id, so consumers key a chain by the id the wire
|
|
384
|
+
// uses instead of each deriving `eip155:<id>` for themselves.
|
|
385
|
+
const { chains: caip2Classified, issues: caip2Issues } = (0, validation_1.classifyChainCaip2)(stackClassified);
|
|
386
|
+
// Then the block explorer, so a consumer rendering a link for a chain added
|
|
387
|
+
// after its release does not need a hand-maintained table of its own.
|
|
388
|
+
const { chains: classifiedChains, issues: explorerIssues } = (0, validation_1.classifyChainExplorers)(caip2Classified, output.networks);
|
|
389
|
+
const classificationIssues = [
|
|
390
|
+
...networkIssues,
|
|
391
|
+
...stackIssues,
|
|
392
|
+
...caip2Issues,
|
|
393
|
+
...explorerIssues,
|
|
394
|
+
];
|
|
372
395
|
await (0, promises_1.writeFile)(node_path_1.default.join(configsDir, "chains.json"), `${JSON.stringify(classifiedChains, null, 2)}\n`);
|
|
373
396
|
await (0, promises_1.writeFile)(node_path_1.default.join(configsDir, "mainnets.json"), `${JSON.stringify(output.mainnets, null, 2)}\n`);
|
|
374
397
|
await (0, promises_1.writeFile)(node_path_1.default.join(configsDir, "testnets.json"), `${JSON.stringify(output.testnets, null, 2)}\n`);
|
|
@@ -65,6 +65,63 @@ export declare function classifyChainNetworks(chains: ChainRegistry, mainnets: R
|
|
|
65
65
|
chains: ChainRegistry;
|
|
66
66
|
issues: ValidationIssue[];
|
|
67
67
|
};
|
|
68
|
+
/**
|
|
69
|
+
* Stamps every chain with its canonical CAIP-2 id, deriving `eip155:<chainId>`
|
|
70
|
+
* for the chains that do not declare one.
|
|
71
|
+
*
|
|
72
|
+
* Why this exists: `caip2` was authored only for the chains whose id is not an
|
|
73
|
+
* EIP-155 one (Solana, Tron, HyperCore), so every consumer that wanted a CAIP-2
|
|
74
|
+
* id had to carry the same `entry.caip2 ?? \`eip155:${id}\`` fallback — the
|
|
75
|
+
* orchestrator, deposit-service, the SDK and the indexers each had their own.
|
|
76
|
+
* A fallback repeated in five places is a rule with five chances to drift, and
|
|
77
|
+
* the one place it is genuinely load-bearing (HyperCore, whose `eip155:1337`
|
|
78
|
+
* spelling is wrong) is exactly where getting it wrong is silent.
|
|
79
|
+
*
|
|
80
|
+
* Derived rather than authored per chain for the same reason `network` is: an
|
|
81
|
+
* EVM chain's CAIP-2 id is a restatement of its chain id, so authoring it would
|
|
82
|
+
* only create a second copy that could disagree with the first.
|
|
83
|
+
*
|
|
84
|
+
* Uniqueness is checked because `chainIdFromCaip2` is a reverse lookup over this
|
|
85
|
+
* field — two chains sharing an id makes that lookup silently return whichever
|
|
86
|
+
* came first.
|
|
87
|
+
*/
|
|
88
|
+
export declare function classifyChainCaip2(chains: ChainRegistry): {
|
|
89
|
+
chains: ChainRegistry;
|
|
90
|
+
issues: ValidationIssue[];
|
|
91
|
+
};
|
|
92
|
+
/**
|
|
93
|
+
* Where a chain's transactions and addresses can be looked at by a human.
|
|
94
|
+
* `url` has no trailing slash; the paths carry their own leading and trailing
|
|
95
|
+
* ones, so a link is `${url}${addressPath}${value}`.
|
|
96
|
+
*/
|
|
97
|
+
export interface ChainExplorer {
|
|
98
|
+
url: string;
|
|
99
|
+
addressPath: string;
|
|
100
|
+
txPath: string;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Stamps every chain with its block explorer, derived from the chain's viem
|
|
104
|
+
* definition where it has one and authored in the jsonnet where it does not.
|
|
105
|
+
*
|
|
106
|
+
* Resolved by the entry's declared `viemChain` **name**, never by chain id. A
|
|
107
|
+
* lookup by id is actively wrong here: viem's chain 1337 is `localhost`, so
|
|
108
|
+
* HyperCore — whose registry key is 1337 — would silently inherit Localhost's
|
|
109
|
+
* metadata. That is the same mistake as addressing HyperCore by `eip155:1337`,
|
|
110
|
+
* arrived at from the other direction.
|
|
111
|
+
*
|
|
112
|
+
* A missing explorer is not an error. It resolves to `null`, which consumers
|
|
113
|
+
* render as plain text instead of a link; the cost of not knowing one is a
|
|
114
|
+
* missing hyperlink, not a wrong answer. It is still worth a warning, because
|
|
115
|
+
* the usual cause is a chain added without one rather than a chain that
|
|
116
|
+
* genuinely has none.
|
|
117
|
+
*/
|
|
118
|
+
export declare function classifyChainExplorers(chains: ChainRegistry, networks: {
|
|
119
|
+
mainnets: StackNetworkEntry[];
|
|
120
|
+
testnets: StackNetworkEntry[];
|
|
121
|
+
}): {
|
|
122
|
+
chains: ChainRegistry;
|
|
123
|
+
issues: ValidationIssue[];
|
|
124
|
+
};
|
|
68
125
|
/** The subset of a generated network entry that stack classification needs. */
|
|
69
126
|
export interface StackNetworkEntry {
|
|
70
127
|
id: number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../../scripts/validation.ts"],"names":[],"mappings":"AAKA,MAAM,MAAM,eAAe,GACvB,QAAQ,GACR,KAAK,GACL,OAAO,GACP,KAAK,GACL,MAAM,GACN,OAAO,GACP,MAAM,CAAC;AAEX,MAAM,MAAM,YAAY,GAAG,SAAS,GAAG,SAAS,CAAC;AAEjD;;;;;;;;;;GAUG;AACH,MAAM,MAAM,UAAU,GAAG,UAAU,GAAG,QAAQ,GAAG,MAAM,GAAG,SAAS,CAAC;AAEpE,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,gBAAgB,EAAE,eAAe,EAAE,CAAC;IACpC,MAAM,EAAE,KAAK,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;KAAE,CAAC,CAAC;IAC1D;+DAC2D;IAC3D,OAAO,CAAC,EAAE,YAAY,CAAC;IACvB,yDAAyD;IACzD,KAAK,CAAC,EAAE,UAAU,CAAC;IACnB;oFACgF;IAChF,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;
|
|
1
|
+
{"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../../scripts/validation.ts"],"names":[],"mappings":"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"}
|