@rhinestone/shared-configs 1.8.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 +348 -0
- package/dist/scripts/__tests__/validation.test.js +166 -0
- package/dist/scripts/generate.js +30 -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 +16 -3
- package/dist/src/chains.d.ts.map +1 -1
- package/dist/src/chains.js +348 -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 +49 -6
- package/dist/src/types.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -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
|
@@ -99,6 +99,8 @@ function renderChains(chainRegistry, mainnets, testnets) {
|
|
|
99
99
|
" pegGroup?: PegGroup;",
|
|
100
100
|
" balanceSlot: number | null;",
|
|
101
101
|
" approvalSlot: number | null;",
|
|
102
|
+
" /** Bridge layers that can carry this token on this chain, when known. */",
|
|
103
|
+
" settlementLayers?: SettlementLayer[];",
|
|
102
104
|
" unpriced?: boolean;",
|
|
103
105
|
"}",
|
|
104
106
|
"",
|
|
@@ -109,6 +111,15 @@ function renderChains(chainRegistry, mainnets, testnets) {
|
|
|
109
111
|
" decimals: number;",
|
|
110
112
|
"}",
|
|
111
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
|
+
"",
|
|
112
123
|
"interface Chain {",
|
|
113
124
|
" name: string;",
|
|
114
125
|
" vmType: VmType;",
|
|
@@ -116,8 +127,11 @@ function renderChains(chainRegistry, mainnets, testnets) {
|
|
|
116
127
|
" network: ChainNetwork;",
|
|
117
128
|
" /** Execution stack, for consumers that must synthesise a viem `Chain`. */",
|
|
118
129
|
" stack: ChainStack;",
|
|
119
|
-
" /** CAIP-2 chain identifier
|
|
120
|
-
"
|
|
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;",
|
|
121
135
|
" /** True for virtual chains (e.g. HyperCore) that settle on another chain. */",
|
|
122
136
|
" virtual?: boolean;",
|
|
123
137
|
" nativeToken: NativeToken;",
|
|
@@ -132,7 +146,7 @@ function renderChains(chainRegistry, mainnets, testnets) {
|
|
|
132
146
|
`const chains: Record<SupportedChain, Chain> = ${JSON.stringify(chains, null, 2)};`,
|
|
133
147
|
"",
|
|
134
148
|
"export { chains };",
|
|
135
|
-
"export type { Chain, Token, NativeToken };",
|
|
149
|
+
"export type { Chain, Token, NativeToken, Explorer };",
|
|
136
150
|
].join("\n");
|
|
137
151
|
}
|
|
138
152
|
function runJsonnet(yeetRoot) {
|
|
@@ -365,8 +379,19 @@ async function generate() {
|
|
|
365
379
|
const { chains: networkClassified, issues: networkIssues } = (0, validation_1.classifyChainNetworks)(output.chains, output.mainnets, output.testnets);
|
|
366
380
|
// Then the execution stack, for consumers that have to build a viem `Chain`
|
|
367
381
|
// themselves because the chain postdates their pinned viem.
|
|
368
|
-
const { chains:
|
|
369
|
-
|
|
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
|
+
];
|
|
370
395
|
await (0, promises_1.writeFile)(node_path_1.default.join(configsDir, "chains.json"), `${JSON.stringify(classifiedChains, null, 2)}\n`);
|
|
371
396
|
await (0, promises_1.writeFile)(node_path_1.default.join(configsDir, "mainnets.json"), `${JSON.stringify(output.mainnets, null, 2)}\n`);
|
|
372
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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"}
|
|
@@ -34,6 +34,8 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
36
|
exports.classifyChainNetworks = classifyChainNetworks;
|
|
37
|
+
exports.classifyChainCaip2 = classifyChainCaip2;
|
|
38
|
+
exports.classifyChainExplorers = classifyChainExplorers;
|
|
37
39
|
exports.classifyChainStacks = classifyChainStacks;
|
|
38
40
|
exports.validateChainConfig = validateChainConfig;
|
|
39
41
|
exports.formatValidationIssues = formatValidationIssues;
|
|
@@ -72,6 +74,18 @@ const STACKS = new Set(['op-stack', 'zksync', 'celo', 'vanilla']);
|
|
|
72
74
|
// canonical positive integers — no leading zeros, which would let "01" and "1"
|
|
73
75
|
// both address chain 1.
|
|
74
76
|
const CHAIN_ID_KEY = /^[1-9][0-9]*$/;
|
|
77
|
+
// CAIP-2 (https://chainagnostic.org/CAIPs/caip-2): `<namespace>:<reference>`.
|
|
78
|
+
//
|
|
79
|
+
// Deliberately shape-only — the namespace set is not ours to close, and a chain
|
|
80
|
+
// arriving under a namespace this build has never heard of is a new chain, not
|
|
81
|
+
// a malformed one.
|
|
82
|
+
//
|
|
83
|
+
// Deliberately looser than the spec on namespace length too. CAIP-2 caps it at
|
|
84
|
+
// 8 characters; `hypercore` is 9, and that spelling is canonical for us across
|
|
85
|
+
// the orchestrator, the SDK and deposit-service. Tightening this to the spec
|
|
86
|
+
// would fail the build on our own established id, so the bound is the org's,
|
|
87
|
+
// not the spec's.
|
|
88
|
+
const CAIP2 = /^[-a-z0-9]{3,16}:[-_a-zA-Z0-9]{1,32}$/;
|
|
75
89
|
const isObject = (v) => typeof v === 'object' && v !== null && !Array.isArray(v);
|
|
76
90
|
const isNonEmptyString = (v) => typeof v === 'string' && v.length > 0;
|
|
77
91
|
const isNonNegativeInt = (v) => typeof v === 'number' && Number.isInteger(v) && v >= 0;
|
|
@@ -107,8 +121,26 @@ function structuralProblems(chain) {
|
|
|
107
121
|
if (!STACKS.has(chain.stack)) {
|
|
108
122
|
problems.push(`stack ${JSON.stringify(chain.stack)} is not one of ${[...STACKS].join(', ')}`);
|
|
109
123
|
}
|
|
110
|
-
|
|
111
|
-
|
|
124
|
+
// Same deal as `network` and `stack` for EVM chains, where it is derived
|
|
125
|
+
// rather than authored — but unlike those two it is authored in the jsonnet
|
|
126
|
+
// for every chain whose id is not an EIP-155 one. Required either way: it is
|
|
127
|
+
// the id consumers key a chain by on the wire, and until it was always
|
|
128
|
+
// present each of them re-derived `eip155:<id>` for itself.
|
|
129
|
+
if (!CAIP2.test(chain.caip2)) {
|
|
130
|
+
problems.push(`caip2 ${JSON.stringify(chain.caip2)} is not a "<namespace>:<reference>" identifier`);
|
|
131
|
+
}
|
|
132
|
+
// Nullable, but the key has to be present: `null` is the answer for a chain
|
|
133
|
+
// with no explorer, and absent is not an answer at all. Stamped by
|
|
134
|
+
// classifyChainExplorers rather than authored for most chains, and checked
|
|
135
|
+
// here for the same reason as `network` and `stack` — this is the gate the
|
|
136
|
+
// published artifact passes through, and `ChainEntry` types consumers as
|
|
137
|
+
// though the key is always there.
|
|
138
|
+
if (!('explorer' in chain)) {
|
|
139
|
+
problems.push('explorer is missing (use null for a chain with no explorer)');
|
|
140
|
+
}
|
|
141
|
+
else if (chain.explorer !== null && !isChainExplorer(chain.explorer)) {
|
|
142
|
+
problems.push(`explorer ${JSON.stringify(chain.explorer)} is not null or { url (no trailing slash), addressPath, txPath (both slash-wrapped) }`);
|
|
143
|
+
}
|
|
112
144
|
if (!isAbsentOr(chain.virtual, (v) => typeof v === 'boolean')) {
|
|
113
145
|
problems.push('virtual is not a boolean');
|
|
114
146
|
}
|
|
@@ -288,6 +320,185 @@ function classifyChainNetworks(chains, mainnets, testnets) {
|
|
|
288
320
|
}
|
|
289
321
|
return { chains: classified, issues };
|
|
290
322
|
}
|
|
323
|
+
/**
|
|
324
|
+
* Stamps every chain with its canonical CAIP-2 id, deriving `eip155:<chainId>`
|
|
325
|
+
* for the chains that do not declare one.
|
|
326
|
+
*
|
|
327
|
+
* Why this exists: `caip2` was authored only for the chains whose id is not an
|
|
328
|
+
* EIP-155 one (Solana, Tron, HyperCore), so every consumer that wanted a CAIP-2
|
|
329
|
+
* id had to carry the same `entry.caip2 ?? \`eip155:${id}\`` fallback — the
|
|
330
|
+
* orchestrator, deposit-service, the SDK and the indexers each had their own.
|
|
331
|
+
* A fallback repeated in five places is a rule with five chances to drift, and
|
|
332
|
+
* the one place it is genuinely load-bearing (HyperCore, whose `eip155:1337`
|
|
333
|
+
* spelling is wrong) is exactly where getting it wrong is silent.
|
|
334
|
+
*
|
|
335
|
+
* Derived rather than authored per chain for the same reason `network` is: an
|
|
336
|
+
* EVM chain's CAIP-2 id is a restatement of its chain id, so authoring it would
|
|
337
|
+
* only create a second copy that could disagree with the first.
|
|
338
|
+
*
|
|
339
|
+
* Uniqueness is checked because `chainIdFromCaip2` is a reverse lookup over this
|
|
340
|
+
* field — two chains sharing an id makes that lookup silently return whichever
|
|
341
|
+
* came first.
|
|
342
|
+
*/
|
|
343
|
+
function classifyChainCaip2(chains) {
|
|
344
|
+
const issues = [];
|
|
345
|
+
const classified = {};
|
|
346
|
+
const seen = new Map();
|
|
347
|
+
for (const [chainId, chain] of Object.entries(chains)) {
|
|
348
|
+
const chainName = isNonEmptyString(chain?.name)
|
|
349
|
+
? chain.name
|
|
350
|
+
: '<unnamed>';
|
|
351
|
+
const declared = chain?.caip2;
|
|
352
|
+
// A malformed declaration is left exactly as authored rather than replaced
|
|
353
|
+
// with the derived form: quietly publishing `eip155:1337` for HyperCore is
|
|
354
|
+
// the precise mistake this field exists to prevent, so the structural check
|
|
355
|
+
// reports the real value instead.
|
|
356
|
+
if (declared !== undefined && !(typeof declared === 'string' && CAIP2.test(declared))) {
|
|
357
|
+
issues.push({
|
|
358
|
+
severity: 'error',
|
|
359
|
+
chainId,
|
|
360
|
+
chainName,
|
|
361
|
+
message: `declares a malformed caip2 ${JSON.stringify(declared)} — expected "<namespace>:<reference>"`,
|
|
362
|
+
});
|
|
363
|
+
classified[chainId] = withSortedKeys({ ...chain });
|
|
364
|
+
continue;
|
|
365
|
+
}
|
|
366
|
+
// A real EVM chain — and only a real EVM chain — has an id that is
|
|
367
|
+
// definitionally `eip155:<chainId>`. Everything else is addressed by an id
|
|
368
|
+
// the registry key cannot express, so its caip2 has to be authored.
|
|
369
|
+
const derivable = chain.vmType === 'evm' && chain.virtual !== true;
|
|
370
|
+
const derived = `eip155:${chainId}`;
|
|
371
|
+
// Deriving for a non-EVM or virtual entry would hand it an `eip155:` id that
|
|
372
|
+
// is confidently wrong — the same failure as HyperCore's `eip155:1337`,
|
|
373
|
+
// reintroduced by the very thing meant to prevent it. So the next svm/tvm or
|
|
374
|
+
// virtual chain added without an authored caip2 fails the build here rather
|
|
375
|
+
// than reaching the artifact under a wire id nothing uses.
|
|
376
|
+
if (declared === undefined && !derivable) {
|
|
377
|
+
issues.push({
|
|
378
|
+
severity: 'error',
|
|
379
|
+
chainId,
|
|
380
|
+
chainName,
|
|
381
|
+
message: `is ${chain.virtual === true ? 'a virtual chain' : `vmType ${JSON.stringify(chain.vmType)}`} and must author its caip2 — only a non-virtual EVM chain can derive ${JSON.stringify(derived)} from its registry key`,
|
|
382
|
+
});
|
|
383
|
+
classified[chainId] = withSortedKeys({ ...chain });
|
|
384
|
+
continue;
|
|
385
|
+
}
|
|
386
|
+
const caip2 = declared === undefined ? derived : declared;
|
|
387
|
+
// Conversely, a declaration that disagrees with the derivable form is a typo
|
|
388
|
+
// rather than an override.
|
|
389
|
+
if (declared !== undefined && derivable && declared !== derived) {
|
|
390
|
+
issues.push({
|
|
391
|
+
severity: 'error',
|
|
392
|
+
chainId,
|
|
393
|
+
chainName,
|
|
394
|
+
message: `declares caip2 ${JSON.stringify(declared)} but is a non-virtual EVM chain, whose canonical id is ${JSON.stringify(derived)}`,
|
|
395
|
+
});
|
|
396
|
+
}
|
|
397
|
+
const collision = seen.get(caip2);
|
|
398
|
+
if (collision !== undefined) {
|
|
399
|
+
issues.push({
|
|
400
|
+
severity: 'error',
|
|
401
|
+
chainId,
|
|
402
|
+
chainName,
|
|
403
|
+
message: `resolves to caip2 ${JSON.stringify(caip2)}, which chain ${collision} already uses — a reverse lookup cannot tell them apart`,
|
|
404
|
+
});
|
|
405
|
+
}
|
|
406
|
+
else {
|
|
407
|
+
seen.set(caip2, chainId);
|
|
408
|
+
}
|
|
409
|
+
classified[chainId] = withSortedKeys({ ...chain, caip2 });
|
|
410
|
+
}
|
|
411
|
+
return { chains: classified, issues };
|
|
412
|
+
}
|
|
413
|
+
// Near-universal across EVM explorers (Etherscan, Blockscout and the -scan
|
|
414
|
+
// forks all use these). Tronscan is the exception, and authors its own.
|
|
415
|
+
const DEFAULT_ADDRESS_PATH = '/address/';
|
|
416
|
+
const DEFAULT_TX_PATH = '/tx/';
|
|
417
|
+
// The slash convention is enforced, not assumed: a link is built by plain
|
|
418
|
+
// concatenation, so a path missing its leading slash or a url carrying a
|
|
419
|
+
// trailing one produces a subtly wrong URL rather than an obviously broken one.
|
|
420
|
+
function isChainExplorer(value) {
|
|
421
|
+
const hasSlashes = (v) => typeof v === 'string' && /^\/.*\/$/.test(v);
|
|
422
|
+
return (isObject(value) &&
|
|
423
|
+
typeof value.url === 'string' &&
|
|
424
|
+
value.url.length > 0 &&
|
|
425
|
+
!value.url.endsWith('/') &&
|
|
426
|
+
hasSlashes(value.addressPath) &&
|
|
427
|
+
hasSlashes(value.txPath));
|
|
428
|
+
}
|
|
429
|
+
/**
|
|
430
|
+
* Stamps every chain with its block explorer, derived from the chain's viem
|
|
431
|
+
* definition where it has one and authored in the jsonnet where it does not.
|
|
432
|
+
*
|
|
433
|
+
* Resolved by the entry's declared `viemChain` **name**, never by chain id. A
|
|
434
|
+
* lookup by id is actively wrong here: viem's chain 1337 is `localhost`, so
|
|
435
|
+
* HyperCore — whose registry key is 1337 — would silently inherit Localhost's
|
|
436
|
+
* metadata. That is the same mistake as addressing HyperCore by `eip155:1337`,
|
|
437
|
+
* arrived at from the other direction.
|
|
438
|
+
*
|
|
439
|
+
* A missing explorer is not an error. It resolves to `null`, which consumers
|
|
440
|
+
* render as plain text instead of a link; the cost of not knowing one is a
|
|
441
|
+
* missing hyperlink, not a wrong answer. It is still worth a warning, because
|
|
442
|
+
* the usual cause is a chain added without one rather than a chain that
|
|
443
|
+
* genuinely has none.
|
|
444
|
+
*/
|
|
445
|
+
function classifyChainExplorers(chains, networks) {
|
|
446
|
+
const issues = [];
|
|
447
|
+
const stamped = {};
|
|
448
|
+
const entriesById = new Map();
|
|
449
|
+
for (const entry of [...networks.mainnets, ...networks.testnets]) {
|
|
450
|
+
entriesById.set(String(entry.id), entry);
|
|
451
|
+
}
|
|
452
|
+
for (const [chainId, chain] of Object.entries(chains)) {
|
|
453
|
+
const chainName = isNonEmptyString(chain?.name)
|
|
454
|
+
? chain.name
|
|
455
|
+
: '<unnamed>';
|
|
456
|
+
const authored = chain?.explorer;
|
|
457
|
+
// Authoring wins outright, unlike `stack` where viem's definition is what
|
|
458
|
+
// the consumer actually uses. An explorer is presentation: the jsonnet is
|
|
459
|
+
// the only place a non-default path (Tronscan's `/#/address/`) or a
|
|
460
|
+
// preferred explorer among several can be expressed.
|
|
461
|
+
if (authored !== undefined) {
|
|
462
|
+
if (!isChainExplorer(authored)) {
|
|
463
|
+
issues.push({
|
|
464
|
+
severity: 'error',
|
|
465
|
+
chainId,
|
|
466
|
+
chainName,
|
|
467
|
+
message: `declares a malformed explorer ${JSON.stringify(authored)} — expected { url, addressPath, txPath }`,
|
|
468
|
+
});
|
|
469
|
+
stamped[chainId] = withSortedKeys({ ...chain });
|
|
470
|
+
continue;
|
|
471
|
+
}
|
|
472
|
+
stamped[chainId] = withSortedKeys({ ...chain, explorer: authored });
|
|
473
|
+
continue;
|
|
474
|
+
}
|
|
475
|
+
const viemChainName = entriesById.get(chainId)?.viemChain;
|
|
476
|
+
const url = viemChainName
|
|
477
|
+
? viemChains[viemChainName]?.blockExplorers?.default?.url
|
|
478
|
+
: undefined;
|
|
479
|
+
if (typeof url !== 'string' || url.length === 0) {
|
|
480
|
+
issues.push({
|
|
481
|
+
severity: 'warning',
|
|
482
|
+
chainId,
|
|
483
|
+
chainName,
|
|
484
|
+
message: viemChainName
|
|
485
|
+
? `viem chain "${viemChainName}" ships no block explorer — links will render as plain text until one is authored`
|
|
486
|
+
: 'has no viemChain to derive a block explorer from and authors none — links will render as plain text',
|
|
487
|
+
});
|
|
488
|
+
stamped[chainId] = withSortedKeys({ ...chain, explorer: null });
|
|
489
|
+
continue;
|
|
490
|
+
}
|
|
491
|
+
stamped[chainId] = withSortedKeys({
|
|
492
|
+
...chain,
|
|
493
|
+
explorer: {
|
|
494
|
+
url: url.replace(/\/+$/, ''),
|
|
495
|
+
addressPath: DEFAULT_ADDRESS_PATH,
|
|
496
|
+
txPath: DEFAULT_TX_PATH,
|
|
497
|
+
},
|
|
498
|
+
});
|
|
499
|
+
}
|
|
500
|
+
return { chains: stamped, issues };
|
|
501
|
+
}
|
|
291
502
|
const isChainStack = (v) => typeof v === 'string' && STACKS.has(v);
|
|
292
503
|
const STACK_FORMATTERS = [
|
|
293
504
|
['op-stack', op_stack_1.chainConfig.formatters],
|
|
@@ -49,9 +49,19 @@ function bumpedVersion() {
|
|
|
49
49
|
const [major, minor, patch] = packageVersion_1.PACKAGE_VERSION.split("-")[0].split(".");
|
|
50
50
|
return `${major}.${minor}.${Number(patch) + 1}`;
|
|
51
51
|
}
|
|
52
|
+
// Must produce a genuinely older version for every PACKAGE_VERSION, including
|
|
53
|
+
// the `x.y.0` releases where decrementing the patch has nowhere to go — clamping
|
|
54
|
+
// to 0 there returned PACKAGE_VERSION itself, which the client accepts as the
|
|
55
|
+
// steady state, so the staleness tests asserted the opposite of what they read.
|
|
52
56
|
function olderVersion() {
|
|
53
|
-
const [major, minor, patch] = packageVersion_1.PACKAGE_VERSION.split("-")[0]
|
|
54
|
-
|
|
57
|
+
const [major, minor, patch] = packageVersion_1.PACKAGE_VERSION.split("-")[0]
|
|
58
|
+
.split(".")
|
|
59
|
+
.map(Number);
|
|
60
|
+
if (patch > 0)
|
|
61
|
+
return `${major}.${minor}.${patch - 1}`;
|
|
62
|
+
if (minor > 0)
|
|
63
|
+
return `${major}.${minor - 1}.0`;
|
|
64
|
+
return `${major - 1}.0.0`;
|
|
55
65
|
}
|
|
56
66
|
function withEthereum(overrides) {
|
|
57
67
|
return {
|
|
@@ -30,10 +30,16 @@ const TRON_CAIP2 = "tron:mainnet";
|
|
|
30
30
|
(0, bun_test_1.expect)((0, index_1.chainIdFromCaip2)(SOLANA_CAIP2)).toBe(index_1.MainnetNetwork.SOLANA);
|
|
31
31
|
(0, bun_test_1.expect)((0, index_1.chainIdFromCaip2)(TRON_CAIP2)).toBe(index_1.MainnetNetwork.TRON);
|
|
32
32
|
});
|
|
33
|
-
(0, bun_test_1.it)("
|
|
34
|
-
|
|
35
|
-
(0, bun_test_1.expect)((0, index_1.chainIdFromCaip2)("eip155:
|
|
33
|
+
(0, bun_test_1.it)("resolves eip155 caip2 strings, now that every entry carries one", () => {
|
|
34
|
+
(0, bun_test_1.expect)((0, index_1.chainIdFromCaip2)("eip155:1")).toBe(index_1.MainnetNetwork.ETHEREUM);
|
|
35
|
+
(0, bun_test_1.expect)((0, index_1.chainIdFromCaip2)("eip155:999")).toBe(index_1.MainnetNetwork.HYPEREVM);
|
|
36
|
+
});
|
|
37
|
+
(0, bun_test_1.it)("returns undefined for ids no chain uses", () => {
|
|
38
|
+
// 1337 is HyperCore's *registry key*, but its caip2 is `hypercore:mainnet`
|
|
39
|
+
// — so the eip155 spelling resolves to nothing, which is the point of
|
|
40
|
+
// reading the field instead of deriving it.
|
|
36
41
|
(0, bun_test_1.expect)((0, index_1.chainIdFromCaip2)("eip155:1337")).toBeUndefined();
|
|
42
|
+
(0, bun_test_1.expect)((0, index_1.chainIdFromCaip2)("eip155:999999999")).toBeUndefined();
|
|
37
43
|
(0, bun_test_1.expect)((0, index_1.chainIdFromCaip2)("hypercore:testnet")).toBeUndefined();
|
|
38
44
|
});
|
|
39
45
|
});
|
|
@@ -9,17 +9,19 @@ export declare function isNonEvmChainId(chainId: number): boolean;
|
|
|
9
9
|
*/
|
|
10
10
|
export declare function getNonEvmChainIds(): number[];
|
|
11
11
|
/**
|
|
12
|
-
* Canonical CAIP-2 string for a chain id
|
|
13
|
-
*
|
|
14
|
-
* `eip155:<chainId>` form
|
|
15
|
-
*
|
|
12
|
+
* Canonical CAIP-2 string for a chain id, read straight off the registry —
|
|
13
|
+
* every entry carries one (see `classifyChainCaip2`). Falls back to the
|
|
14
|
+
* `eip155:<chainId>` form for an id the registry does not know, so callers
|
|
15
|
+
* don't need to special-case an absent entry.
|
|
16
16
|
*/
|
|
17
17
|
export declare function getCaip2(chainId: number): string;
|
|
18
18
|
/**
|
|
19
|
-
* Reverse lookup —
|
|
20
|
-
* `
|
|
21
|
-
*
|
|
22
|
-
*
|
|
19
|
+
* Reverse lookup — the numeric chain id addressed by a CAIP-2 string, or
|
|
20
|
+
* `undefined` if no registry entry uses it.
|
|
21
|
+
*
|
|
22
|
+
* Covers every chain, EVM included: `caip2` is present on all entries, and the
|
|
23
|
+
* generator rejects two chains resolving to the same one, so this is
|
|
24
|
+
* unambiguous.
|
|
23
25
|
*/
|
|
24
26
|
export declare function chainIdFromCaip2(caip2: string): number | undefined;
|
|
25
27
|
//# sourceMappingURL=chainVirtualization.d.ts.map
|