@rhinestone/shared-configs 1.9.0 → 1.10.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/dist/configs/chains.json +137 -0
- package/dist/scripts/__tests__/validation.test.js +166 -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 +137 -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,7 @@
|
|
|
599
653
|
},
|
|
600
654
|
"1337": {
|
|
601
655
|
"caip2": "hypercore:mainnet",
|
|
656
|
+
"explorer": null,
|
|
602
657
|
"name": "HyperCore",
|
|
603
658
|
"nativeToken": {
|
|
604
659
|
"address": "0x0000000000000000000000000000000000000000",
|
|
@@ -628,6 +683,12 @@
|
|
|
628
683
|
}
|
|
629
684
|
},
|
|
630
685
|
"1868": {
|
|
686
|
+
"caip2": "eip155:1868",
|
|
687
|
+
"explorer": {
|
|
688
|
+
"url": "https://soneium.blockscout.com",
|
|
689
|
+
"addressPath": "/address/",
|
|
690
|
+
"txPath": "/tx/"
|
|
691
|
+
},
|
|
631
692
|
"name": "Soneium Mainnet",
|
|
632
693
|
"nativeToken": {
|
|
633
694
|
"address": "0x0000000000000000000000000000000000000000",
|
|
@@ -696,6 +757,12 @@
|
|
|
696
757
|
}
|
|
697
758
|
},
|
|
698
759
|
"4663": {
|
|
760
|
+
"caip2": "eip155:4663",
|
|
761
|
+
"explorer": {
|
|
762
|
+
"url": "https://robinhoodchain.blockscout.com",
|
|
763
|
+
"addressPath": "/address/",
|
|
764
|
+
"txPath": "/tx/"
|
|
765
|
+
},
|
|
699
766
|
"name": "Robinhood Chain",
|
|
700
767
|
"nativeToken": {
|
|
701
768
|
"address": "0x0000000000000000000000000000000000000000",
|
|
@@ -753,6 +820,12 @@
|
|
|
753
820
|
}
|
|
754
821
|
},
|
|
755
822
|
"8453": {
|
|
823
|
+
"caip2": "eip155:8453",
|
|
824
|
+
"explorer": {
|
|
825
|
+
"url": "https://basescan.org",
|
|
826
|
+
"addressPath": "/address/",
|
|
827
|
+
"txPath": "/tx/"
|
|
828
|
+
},
|
|
756
829
|
"name": "Base",
|
|
757
830
|
"nativeToken": {
|
|
758
831
|
"address": "0x0000000000000000000000000000000000000000",
|
|
@@ -822,6 +895,12 @@
|
|
|
822
895
|
}
|
|
823
896
|
},
|
|
824
897
|
"9745": {
|
|
898
|
+
"caip2": "eip155:9745",
|
|
899
|
+
"explorer": {
|
|
900
|
+
"url": "https://plasmascan.to",
|
|
901
|
+
"addressPath": "/address/",
|
|
902
|
+
"txPath": "/tx/"
|
|
903
|
+
},
|
|
825
904
|
"name": "Plasma",
|
|
826
905
|
"nativeToken": {
|
|
827
906
|
"address": "0x0000000000000000000000000000000000000000",
|
|
@@ -865,6 +944,12 @@
|
|
|
865
944
|
}
|
|
866
945
|
},
|
|
867
946
|
"9746": {
|
|
947
|
+
"caip2": "eip155:9746",
|
|
948
|
+
"explorer": {
|
|
949
|
+
"url": "https://testnet.plasmascan.to",
|
|
950
|
+
"addressPath": "/address/",
|
|
951
|
+
"txPath": "/tx/"
|
|
952
|
+
},
|
|
868
953
|
"name": "Plasma Testnet",
|
|
869
954
|
"nativeToken": {
|
|
870
955
|
"address": "0x0000000000000000000000000000000000000000",
|
|
@@ -910,6 +995,12 @@
|
|
|
910
995
|
}
|
|
911
996
|
},
|
|
912
997
|
"42161": {
|
|
998
|
+
"caip2": "eip155:42161",
|
|
999
|
+
"explorer": {
|
|
1000
|
+
"url": "https://arbiscan.io",
|
|
1001
|
+
"addressPath": "/address/",
|
|
1002
|
+
"txPath": "/tx/"
|
|
1003
|
+
},
|
|
913
1004
|
"name": "Arbitrum One",
|
|
914
1005
|
"nativeToken": {
|
|
915
1006
|
"address": "0x0000000000000000000000000000000000000000",
|
|
@@ -994,6 +1085,12 @@
|
|
|
994
1085
|
}
|
|
995
1086
|
},
|
|
996
1087
|
"43114": {
|
|
1088
|
+
"caip2": "eip155:43114",
|
|
1089
|
+
"explorer": {
|
|
1090
|
+
"url": "https://snowtrace.io",
|
|
1091
|
+
"addressPath": "/address/",
|
|
1092
|
+
"txPath": "/tx/"
|
|
1093
|
+
},
|
|
997
1094
|
"name": "Avalanche",
|
|
998
1095
|
"nativeToken": {
|
|
999
1096
|
"address": "0x0000000000000000000000000000000000000000",
|
|
@@ -1071,6 +1168,12 @@
|
|
|
1071
1168
|
}
|
|
1072
1169
|
},
|
|
1073
1170
|
"84532": {
|
|
1171
|
+
"caip2": "eip155:84532",
|
|
1172
|
+
"explorer": {
|
|
1173
|
+
"url": "https://sepolia.basescan.org",
|
|
1174
|
+
"addressPath": "/address/",
|
|
1175
|
+
"txPath": "/tx/"
|
|
1176
|
+
},
|
|
1074
1177
|
"name": "Base Sepolia",
|
|
1075
1178
|
"nativeToken": {
|
|
1076
1179
|
"address": "0x0000000000000000000000000000000000000000",
|
|
@@ -1146,6 +1249,12 @@
|
|
|
1146
1249
|
}
|
|
1147
1250
|
},
|
|
1148
1251
|
"421614": {
|
|
1252
|
+
"caip2": "eip155:421614",
|
|
1253
|
+
"explorer": {
|
|
1254
|
+
"url": "https://sepolia.arbiscan.io",
|
|
1255
|
+
"addressPath": "/address/",
|
|
1256
|
+
"txPath": "/tx/"
|
|
1257
|
+
},
|
|
1149
1258
|
"name": "Arbitrum Sepolia",
|
|
1150
1259
|
"nativeToken": {
|
|
1151
1260
|
"address": "0x0000000000000000000000000000000000000000",
|
|
@@ -1207,6 +1316,12 @@
|
|
|
1207
1316
|
}
|
|
1208
1317
|
},
|
|
1209
1318
|
"747474": {
|
|
1319
|
+
"caip2": "eip155:747474",
|
|
1320
|
+
"explorer": {
|
|
1321
|
+
"url": "https://katanascan.com",
|
|
1322
|
+
"addressPath": "/address/",
|
|
1323
|
+
"txPath": "/tx/"
|
|
1324
|
+
},
|
|
1210
1325
|
"name": "Katana",
|
|
1211
1326
|
"nativeToken": {
|
|
1212
1327
|
"address": "0x0000000000000000000000000000000000000000",
|
|
@@ -1265,6 +1380,12 @@
|
|
|
1265
1380
|
}
|
|
1266
1381
|
},
|
|
1267
1382
|
"11155111": {
|
|
1383
|
+
"caip2": "eip155:11155111",
|
|
1384
|
+
"explorer": {
|
|
1385
|
+
"url": "https://sepolia.etherscan.io",
|
|
1386
|
+
"addressPath": "/address/",
|
|
1387
|
+
"txPath": "/tx/"
|
|
1388
|
+
},
|
|
1268
1389
|
"name": "Sepolia",
|
|
1269
1390
|
"nativeToken": {
|
|
1270
1391
|
"address": "0x0000000000000000000000000000000000000000",
|
|
@@ -1326,6 +1447,12 @@
|
|
|
1326
1447
|
}
|
|
1327
1448
|
},
|
|
1328
1449
|
"11155420": {
|
|
1450
|
+
"caip2": "eip155:11155420",
|
|
1451
|
+
"explorer": {
|
|
1452
|
+
"url": "https://optimism-sepolia.blockscout.com",
|
|
1453
|
+
"addressPath": "/address/",
|
|
1454
|
+
"txPath": "/tx/"
|
|
1455
|
+
},
|
|
1329
1456
|
"name": "OP Sepolia",
|
|
1330
1457
|
"nativeToken": {
|
|
1331
1458
|
"address": "0x0000000000000000000000000000000000000000",
|
|
@@ -1388,6 +1515,11 @@
|
|
|
1388
1515
|
},
|
|
1389
1516
|
"728126428": {
|
|
1390
1517
|
"caip2": "tron:mainnet",
|
|
1518
|
+
"explorer": {
|
|
1519
|
+
"addressPath": "/#/address/",
|
|
1520
|
+
"txPath": "/#/transaction/",
|
|
1521
|
+
"url": "https://tronscan.org"
|
|
1522
|
+
},
|
|
1391
1523
|
"name": "Tron",
|
|
1392
1524
|
"nativeToken": {
|
|
1393
1525
|
"address": "0x0000000000000000000000000000000000000000",
|
|
@@ -1429,6 +1561,11 @@
|
|
|
1429
1561
|
},
|
|
1430
1562
|
"792703809": {
|
|
1431
1563
|
"caip2": "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp",
|
|
1564
|
+
"explorer": {
|
|
1565
|
+
"addressPath": "/address/",
|
|
1566
|
+
"txPath": "/tx/",
|
|
1567
|
+
"url": "https://solscan.io"
|
|
1568
|
+
},
|
|
1432
1569
|
"name": "Solana",
|
|
1433
1570
|
"nativeToken": {
|
|
1434
1571
|
"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,149 @@ 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 the shipped registry with an explorer on every chain but HyperCore", () => {
|
|
369
|
+
const withoutExplorer = Object.entries(index_1.chainRegistry)
|
|
370
|
+
.filter(([, e]) => e.explorer == null)
|
|
371
|
+
.map(([id]) => id);
|
|
372
|
+
(0, bun_test_1.expect)(withoutExplorer).toEqual(["1337"]);
|
|
373
|
+
for (const [id, entry] of Object.entries(index_1.chainRegistry)) {
|
|
374
|
+
const explorer = entry.explorer;
|
|
375
|
+
if (explorer === null)
|
|
376
|
+
continue;
|
|
377
|
+
(0, bun_test_1.expect)(explorer.url, id).toMatch(/^https:\/\/[^/]+$/);
|
|
378
|
+
(0, bun_test_1.expect)(explorer.addressPath, id).toMatch(/^\/.*\/$/);
|
|
379
|
+
(0, bun_test_1.expect)(explorer.txPath, id).toMatch(/^\/.*\/$/);
|
|
380
|
+
}
|
|
381
|
+
});
|
|
382
|
+
});
|
|
217
383
|
(0, bun_test_1.describe)("classifyChainStacks", () => {
|
|
218
384
|
const nets = (mainnets, testnets = []) => ({ mainnets, testnets });
|
|
219
385
|
// 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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"}
|