@rhinestone/shared-configs 1.7.17 → 1.9.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 +235 -0
- package/dist/scripts/__tests__/validation.test.js +83 -0
- package/dist/scripts/generate.js +10 -2
- package/dist/scripts/validation.d.ts +46 -0
- package/dist/scripts/validation.d.ts.map +1 -1
- package/dist/scripts/validation.js +170 -0
- package/dist/src/chains.d.ts +5 -1
- package/dist/src/chains.d.ts.map +1 -1
- package/dist/src/chains.js +235 -0
- package/dist/src/generated/networks.d.ts +1281 -1281
- package/dist/src/generated/networks.d.ts.map +1 -1
- 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 +36 -1
- package/dist/src/types.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -10,6 +10,7 @@ function validChain(overrides = {}) {
|
|
|
10
10
|
name: "Ethereum",
|
|
11
11
|
vmType: "evm",
|
|
12
12
|
network: "mainnet",
|
|
13
|
+
stack: "vanilla",
|
|
13
14
|
nativeToken,
|
|
14
15
|
wrappedNativeToken: { ...nativeToken, symbol: "WETH" },
|
|
15
16
|
tokens: [{ ...nativeToken, balanceSlot: null, approvalSlot: null }],
|
|
@@ -213,3 +214,85 @@ function errorsFor(chain) {
|
|
|
213
214
|
(0, bun_test_1.expect)(Object.keys(chains)).toHaveLength(Object.keys(index_1.chainRegistry).length);
|
|
214
215
|
});
|
|
215
216
|
});
|
|
217
|
+
(0, bun_test_1.describe)("classifyChainStacks", () => {
|
|
218
|
+
const nets = (mainnets, testnets = []) => ({ mainnets, testnets });
|
|
219
|
+
// The common case: nothing is authored anywhere. viem already knows what Base
|
|
220
|
+
// is, so the value is read off its definition and cannot drift from it.
|
|
221
|
+
(0, bun_test_1.it)("derives the stack from viem's own chain definition", () => {
|
|
222
|
+
const { chains, issues } = (0, validation_1.classifyChainStacks)({ "8453": validChain({ name: "Base" }), "42161": validChain({ name: "Arbitrum One" }) }, nets([
|
|
223
|
+
{ id: 8453, viemChain: "base" },
|
|
224
|
+
{ id: 42161, viemChain: "arbitrum" },
|
|
225
|
+
]));
|
|
226
|
+
(0, bun_test_1.expect)(chains["8453"].stack).toBe("op-stack");
|
|
227
|
+
// viem's arbitrum carries no formatters — Orbit is genuinely vanilla here.
|
|
228
|
+
(0, bun_test_1.expect)(chains["42161"].stack).toBe("vanilla");
|
|
229
|
+
(0, bun_test_1.expect)(issues).toEqual([]);
|
|
230
|
+
});
|
|
231
|
+
// The whole reason the field exists, and the reason it is not defaulted: this
|
|
232
|
+
// is the shape of a chain added faster than viem ships it, and guessing
|
|
233
|
+
// "vanilla" for an OP-stack L2 would misprice its gas with no error anywhere.
|
|
234
|
+
(0, bun_test_1.it)("refuses to guess for an EVM chain viem does not ship", () => {
|
|
235
|
+
const { chains, issues } = (0, validation_1.classifyChainStacks)({ "1234": validChain({ name: "Newchain" }) }, nets([{ id: 1234 }]));
|
|
236
|
+
(0, bun_test_1.expect)(chains["1234"].stack).toBeUndefined();
|
|
237
|
+
(0, bun_test_1.expect)(issues).toHaveLength(1);
|
|
238
|
+
(0, bun_test_1.expect)(issues[0].severity).toBe("error");
|
|
239
|
+
(0, bun_test_1.expect)(issues[0].message).toContain("author `stack`");
|
|
240
|
+
});
|
|
241
|
+
(0, bun_test_1.it)("takes the authored stack for a chain viem does not ship", () => {
|
|
242
|
+
const { chains, issues } = (0, validation_1.classifyChainStacks)({ "1234": validChain({ name: "Newchain" }) }, nets([{ id: 1234, stack: "op-stack" }]));
|
|
243
|
+
(0, bun_test_1.expect)(chains["1234"].stack).toBe("op-stack");
|
|
244
|
+
(0, bun_test_1.expect)(issues).toEqual([]);
|
|
245
|
+
});
|
|
246
|
+
(0, bun_test_1.it)("rejects an authored value outside the union", () => {
|
|
247
|
+
const { chains, issues } = (0, validation_1.classifyChainStacks)({ "1234": validChain({ name: "Newchain" }) }, nets([{ id: 1234, stack: "orbit" }]));
|
|
248
|
+
(0, bun_test_1.expect)(chains["1234"].stack).toBeUndefined();
|
|
249
|
+
(0, bun_test_1.expect)(issues[0]?.message).toContain('"orbit" is not one of');
|
|
250
|
+
});
|
|
251
|
+
// Authoring may agree with viem, never override it: for a chain viem ships,
|
|
252
|
+
// viem's definition is what the consumer actually uses.
|
|
253
|
+
(0, bun_test_1.it)("rejects an authored stack that contradicts viem", () => {
|
|
254
|
+
const { issues } = (0, validation_1.classifyChainStacks)({ "8453": validChain({ name: "Base" }) }, nets([{ id: 8453, viemChain: "base", stack: "vanilla" }]));
|
|
255
|
+
(0, bun_test_1.expect)(issues[0]?.severity).toBe("error");
|
|
256
|
+
(0, bun_test_1.expect)(issues[0]?.message).toContain("contradicts viem");
|
|
257
|
+
});
|
|
258
|
+
(0, bun_test_1.it)("errors when viemChain names an export viem does not have", () => {
|
|
259
|
+
const { issues } = (0, validation_1.classifyChainStacks)({ "1234": validChain({ name: "Nope" }) }, nets([{ id: 1234, viemChain: "definitelyNotAChain" }]));
|
|
260
|
+
(0, bun_test_1.expect)(issues[0]?.message).toContain("no export");
|
|
261
|
+
});
|
|
262
|
+
// Non-EVM and virtual chains are never built as viem chains, so they must not
|
|
263
|
+
// be forced to declare a stack — that would be authoring noise on 3 of 24
|
|
264
|
+
// entries for a value nothing reads.
|
|
265
|
+
(0, bun_test_1.it)("stamps vanilla for non-EVM and virtual chains without authoring", () => {
|
|
266
|
+
const { chains, issues } = (0, validation_1.classifyChainStacks)({
|
|
267
|
+
"792703809": validChain({ name: "Solana", vmType: "svm" }),
|
|
268
|
+
"1337": validChain({ name: "HyperCore", virtual: true }),
|
|
269
|
+
}, nets([{ id: 792703809 }, { id: 1337 }]));
|
|
270
|
+
(0, bun_test_1.expect)(chains["792703809"].stack).toBe("vanilla");
|
|
271
|
+
(0, bun_test_1.expect)(chains["1337"].stack).toBe("vanilla");
|
|
272
|
+
(0, bun_test_1.expect)(issues).toEqual([]);
|
|
273
|
+
});
|
|
274
|
+
// Same invariant classifyChainNetworks holds for `network`: the field is
|
|
275
|
+
// present if and only if it was derived here, so the structural check in
|
|
276
|
+
// validateChainConfig stays an independent second gate rather than re-reading
|
|
277
|
+
// whatever the jsonnet happened to emit.
|
|
278
|
+
(0, bun_test_1.it)("ignores a stack carried in on the input, including when it then fails", () => {
|
|
279
|
+
const { chains } = (0, validation_1.classifyChainStacks)({ "8453": validChain({ name: "Base", stack: "zksync" }) }, nets([{ id: 8453, viemChain: "base" }]));
|
|
280
|
+
(0, bun_test_1.expect)(chains["8453"].stack).toBe("op-stack");
|
|
281
|
+
const { chains: failed } = (0, validation_1.classifyChainStacks)({ "1234": validChain({ name: "Newchain", stack: "op-stack" }) }, nets([{ id: 1234 }]));
|
|
282
|
+
(0, bun_test_1.expect)(failed["1234"].stack).toBeUndefined();
|
|
283
|
+
});
|
|
284
|
+
(0, bun_test_1.it)("stamps every chain in the real registry", () => {
|
|
285
|
+
const { chains, issues } = (0, validation_1.classifyChainStacks)(index_1.chainRegistry,
|
|
286
|
+
// The real network entries are not exported, so re-derive from the
|
|
287
|
+
// registry: every EVM chain in it is one viem ships today, which is the
|
|
288
|
+
// property that keeps the field authored nowhere.
|
|
289
|
+
nets(Object.keys(index_1.chainRegistry).map((id) => ({
|
|
290
|
+
id: Number(id),
|
|
291
|
+
stack: "vanilla",
|
|
292
|
+
}))));
|
|
293
|
+
(0, bun_test_1.expect)(issues.filter((i) => i.severity === "error")).toEqual([]);
|
|
294
|
+
for (const entry of Object.values(chains)) {
|
|
295
|
+
(0, bun_test_1.expect)(entry.stack).toBeDefined();
|
|
296
|
+
}
|
|
297
|
+
});
|
|
298
|
+
});
|
package/dist/scripts/generate.js
CHANGED
|
@@ -88,7 +88,7 @@ function renderChains(chainRegistry, mainnets, testnets) {
|
|
|
88
88
|
}
|
|
89
89
|
return [
|
|
90
90
|
"// Auto-generated by scripts/generate.ts. Do not edit manually.",
|
|
91
|
-
'import type { ChainNetwork, PegGroup, ProviderName, SettlementLayer, SupportedChain, SwapQuoter, SwapQuoterConfig, VmType } from "./types";',
|
|
91
|
+
'import type { ChainNetwork, ChainStack, PegGroup, ProviderName, SettlementLayer, SupportedChain, SwapQuoter, SwapQuoterConfig, VmType } from "./types";',
|
|
92
92
|
"",
|
|
93
93
|
"interface Token {",
|
|
94
94
|
" /** EVM: 0x-prefixed hex. SVM: base58 SPL mint. TVM: base58 (T-prefixed). */",
|
|
@@ -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
|
"",
|
|
@@ -114,6 +116,8 @@ function renderChains(chainRegistry, mainnets, testnets) {
|
|
|
114
116
|
" vmType: VmType;",
|
|
115
117
|
" /** Whether this chain is a mainnet or a testnet. */",
|
|
116
118
|
" network: ChainNetwork;",
|
|
119
|
+
" /** Execution stack, for consumers that must synthesise a viem `Chain`. */",
|
|
120
|
+
" stack: ChainStack;",
|
|
117
121
|
" /** CAIP-2 chain identifier; populated for non-eip155 chains only. */",
|
|
118
122
|
" caip2?: string;",
|
|
119
123
|
" /** True for virtual chains (e.g. HyperCore) that settle on another chain. */",
|
|
@@ -360,7 +364,11 @@ async function generate() {
|
|
|
360
364
|
// registry that reaches configs/chains.json — and from there the published
|
|
361
365
|
// facts artifact and the read-client's bundled fallback — carries the
|
|
362
366
|
// classification rather than leaving it implicit in the network maps.
|
|
363
|
-
const { chains:
|
|
367
|
+
const { chains: networkClassified, issues: networkIssues } = (0, validation_1.classifyChainNetworks)(output.chains, output.mainnets, output.testnets);
|
|
368
|
+
// Then the execution stack, for consumers that have to build a viem `Chain`
|
|
369
|
+
// themselves because the chain postdates their pinned viem.
|
|
370
|
+
const { chains: classifiedChains, issues: stackIssues } = (0, validation_1.classifyChainStacks)(networkClassified, output.networks);
|
|
371
|
+
const classificationIssues = [...networkIssues, ...stackIssues];
|
|
364
372
|
await (0, promises_1.writeFile)(node_path_1.default.join(configsDir, "chains.json"), `${JSON.stringify(classifiedChains, null, 2)}\n`);
|
|
365
373
|
await (0, promises_1.writeFile)(node_path_1.default.join(configsDir, "mainnets.json"), `${JSON.stringify(output.mainnets, null, 2)}\n`);
|
|
366
374
|
await (0, promises_1.writeFile)(node_path_1.default.join(configsDir, "testnets.json"), `${JSON.stringify(output.testnets, null, 2)}\n`);
|
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
export type SettlementLayer = 'ACROSS' | 'ECO' | 'RELAY' | 'OFT' | 'NEAR' | 'RHINO' | 'CCTP';
|
|
2
2
|
export type ChainNetwork = 'mainnet' | 'testnet';
|
|
3
|
+
/**
|
|
4
|
+
* Which execution stack a chain runs, for consumers that must build a viem
|
|
5
|
+
* `Chain` object themselves.
|
|
6
|
+
*
|
|
7
|
+
* The value set is deliberately "the stacks viem ships a `chainConfig` for",
|
|
8
|
+
* not a taxonomy of L2 architectures: the only thing a consumer does with this
|
|
9
|
+
* is pick the matching `chainConfig` for its formatters/serializers. Arbitrum
|
|
10
|
+
* Orbit is absent for that reason — viem's own `arbitrum` carries no
|
|
11
|
+
* formatters, fees or serializers, so an Orbit chain is correctly served as
|
|
12
|
+
* `vanilla` and an `orbit` value would promise behaviour viem does not have.
|
|
13
|
+
*/
|
|
14
|
+
export type ChainStack = 'op-stack' | 'zksync' | 'celo' | 'vanilla';
|
|
3
15
|
export interface ChainData {
|
|
4
16
|
name: string;
|
|
5
17
|
settlementLayers: SettlementLayer[];
|
|
@@ -10,6 +22,8 @@ export interface ChainData {
|
|
|
10
22
|
/** Whether this chain is a mainnet or a testnet. Stamped by
|
|
11
23
|
* `classifyChainNetworks`, not authored in the jsonnet. */
|
|
12
24
|
network?: ChainNetwork;
|
|
25
|
+
/** Execution stack. Stamped by `classifyChainStacks`. */
|
|
26
|
+
stack?: ChainStack;
|
|
13
27
|
/** Virtual chains (e.g. HyperCore) settle on another chain and have no
|
|
14
28
|
* direct settlement layers of their own — excluded from reachability checks. */
|
|
15
29
|
virtual?: boolean;
|
|
@@ -51,6 +65,38 @@ export declare function classifyChainNetworks(chains: ChainRegistry, mainnets: R
|
|
|
51
65
|
chains: ChainRegistry;
|
|
52
66
|
issues: ValidationIssue[];
|
|
53
67
|
};
|
|
68
|
+
/** The subset of a generated network entry that stack classification needs. */
|
|
69
|
+
export interface StackNetworkEntry {
|
|
70
|
+
id: number;
|
|
71
|
+
/** Absent for non-EVM and virtual chains, and for any chain viem does not ship. */
|
|
72
|
+
viemChain?: string;
|
|
73
|
+
/** Authored in config/shared-configs.jsonnet. Only meaningful — and only
|
|
74
|
+
* permitted to decide the outcome — when there is no `viemChain`. */
|
|
75
|
+
stack?: string;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Stamps `stack` onto every registry entry.
|
|
79
|
+
*
|
|
80
|
+
* Derived from viem wherever viem defines the chain, which is every EVM chain
|
|
81
|
+
* today, so the common case is authored nowhere and cannot drift. The jsonnet's
|
|
82
|
+
* `stack` is the escape hatch for the case that motivates the field at all: a
|
|
83
|
+
* chain viem has never heard of, which a consumer has to synthesise with
|
|
84
|
+
* `defineChain` and therefore needs told which `chainConfig` to apply.
|
|
85
|
+
*
|
|
86
|
+
* For such a chain the field is **required, not defaulted**. Defaulting to
|
|
87
|
+
* `vanilla` would make the single most likely new chain — an OP-stack L2 —
|
|
88
|
+
* synthesise without OP-stack formatters, so its L1 fee fields would be missing
|
|
89
|
+
* and its gas silently mispriced. Today's behaviour for an unknown chain is a
|
|
90
|
+
* loud skip; a silent misprice would be strictly worse, so the decision is
|
|
91
|
+
* forced here, where a human is adding the chain anyway.
|
|
92
|
+
*/
|
|
93
|
+
export declare function classifyChainStacks(chains: ChainRegistry, networks: {
|
|
94
|
+
mainnets: StackNetworkEntry[];
|
|
95
|
+
testnets: StackNetworkEntry[];
|
|
96
|
+
}): {
|
|
97
|
+
chains: ChainRegistry;
|
|
98
|
+
issues: ValidationIssue[];
|
|
99
|
+
};
|
|
54
100
|
/**
|
|
55
101
|
* Validates a chain registry for structural and configuration problems.
|
|
56
102
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../../scripts/validation.ts"],"names":[],"mappings":"
|
|
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;AAgKD,MAAM,MAAM,aAAa,GAAG,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;AAEtD,MAAM,MAAM,kBAAkB,GAAG,OAAO,GAAG,SAAS,CAAC;AAErD,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,kBAAkB,CAAC;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,eAAe,EAAE,CAAC;IAC1B,SAAS,EAAE,OAAO,CAAC;CACpB;AAiBD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,aAAa,EACrB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACjC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAChC;IAAE,MAAM,EAAE,aAAa,CAAC;IAAC,MAAM,EAAE,eAAe,EAAE,CAAA;CAAE,CAwEtD;AAED,+EAA+E;AAC/E,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,mFAAmF;IACnF,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;0EACsE;IACtE,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AA6CD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,aAAa,EACrB,QAAQ,EAAE;IACR,QAAQ,EAAE,iBAAiB,EAAE,CAAC;IAC9B,QAAQ,EAAE,iBAAiB,EAAE,CAAC;CAC/B,GACA;IAAE,MAAM,EAAE,aAAa,CAAC;IAAC,MAAM,EAAE,eAAe,EAAE,CAAA;CAAE,CAyFtD;AAED;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,aAAa,GAAG,gBAAgB,CAiE3E;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,gBAAgB,GAAG,MAAM,CAWvE"}
|
|
@@ -1,8 +1,46 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
2
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
36
|
exports.classifyChainNetworks = classifyChainNetworks;
|
|
37
|
+
exports.classifyChainStacks = classifyChainStacks;
|
|
4
38
|
exports.validateChainConfig = validateChainConfig;
|
|
5
39
|
exports.formatValidationIssues = formatValidationIssues;
|
|
40
|
+
const celo_1 = require("viem/celo");
|
|
41
|
+
const viemChains = __importStar(require("viem/chains"));
|
|
42
|
+
const op_stack_1 = require("viem/op-stack");
|
|
43
|
+
const zksync_1 = require("viem/zksync");
|
|
6
44
|
// Runtime mirrors of the string unions in src/types.ts. Keeping these in sync
|
|
7
45
|
// is a build-time concern only: an unrecognised value here means the jsonnet
|
|
8
46
|
// introduced a layer/quoter/vm the TS unions don't know about yet, which SHOULD
|
|
@@ -29,6 +67,7 @@ const SWAP_QUOTERS = new Set([
|
|
|
29
67
|
const VM_TYPES = new Set(['evm', 'svm', 'tvm']);
|
|
30
68
|
const PEG_GROUPS = new Set(['USD', 'ETH']);
|
|
31
69
|
const NETWORKS = new Set(['mainnet', 'testnet']);
|
|
70
|
+
const STACKS = new Set(['op-stack', 'zksync', 'celo', 'vanilla']);
|
|
32
71
|
// Registry keys are chain ids consumers index by directly, so they must be
|
|
33
72
|
// canonical positive integers — no leading zeros, which would let "01" and "1"
|
|
34
73
|
// both address chain 1.
|
|
@@ -60,6 +99,14 @@ function structuralProblems(chain) {
|
|
|
60
99
|
if (!NETWORKS.has(chain.network)) {
|
|
61
100
|
problems.push(`network ${JSON.stringify(chain.network)} is not "mainnet" or "testnet"`);
|
|
62
101
|
}
|
|
102
|
+
// Same deal as `network`: stamped by classifyChainStacks, not authored, and
|
|
103
|
+
// checked here anyway because this is the gate the published artifact passes
|
|
104
|
+
// through. A consumer synthesising a chain object reads this to pick viem's
|
|
105
|
+
// matching chainConfig, so an entry reaching the CDN without it would be
|
|
106
|
+
// built with vanilla formatters — silently mispriced rather than absent.
|
|
107
|
+
if (!STACKS.has(chain.stack)) {
|
|
108
|
+
problems.push(`stack ${JSON.stringify(chain.stack)} is not one of ${[...STACKS].join(', ')}`);
|
|
109
|
+
}
|
|
63
110
|
if (!isAbsentOr(chain.caip2, isNonEmptyString))
|
|
64
111
|
problems.push('caip2 is not a string');
|
|
65
112
|
if (!isAbsentOr(chain.virtual, (v) => typeof v === 'boolean')) {
|
|
@@ -241,6 +288,129 @@ function classifyChainNetworks(chains, mainnets, testnets) {
|
|
|
241
288
|
}
|
|
242
289
|
return { chains: classified, issues };
|
|
243
290
|
}
|
|
291
|
+
const isChainStack = (v) => typeof v === 'string' && STACKS.has(v);
|
|
292
|
+
const STACK_FORMATTERS = [
|
|
293
|
+
['op-stack', op_stack_1.chainConfig.formatters],
|
|
294
|
+
['zksync', zksync_1.chainConfig.formatters],
|
|
295
|
+
['celo', celo_1.chainConfig.formatters],
|
|
296
|
+
];
|
|
297
|
+
/**
|
|
298
|
+
* Reads the stack straight off viem's own chain definition.
|
|
299
|
+
*
|
|
300
|
+
* viem builds a stack-specific chain by spreading its `chainConfig`, so the
|
|
301
|
+
* `formatters` object is shared by reference with the config it came from —
|
|
302
|
+
* comparing identity is exact, and needs no list of chain names to maintain.
|
|
303
|
+
*/
|
|
304
|
+
function stackFromViem(viemChainName) {
|
|
305
|
+
const chain = viemChains[viemChainName];
|
|
306
|
+
if (!isObject(chain)) {
|
|
307
|
+
return { problem: `viem/chains has no export "${viemChainName}"` };
|
|
308
|
+
}
|
|
309
|
+
const formatters = chain.formatters;
|
|
310
|
+
if (!formatters)
|
|
311
|
+
return { stack: 'vanilla' };
|
|
312
|
+
for (const [stack, reference] of STACK_FORMATTERS) {
|
|
313
|
+
if (formatters === reference)
|
|
314
|
+
return { stack };
|
|
315
|
+
}
|
|
316
|
+
// viem gave this chain custom formatters from a config this generator does not
|
|
317
|
+
// know about — a new stack in a viem upgrade. Failing here is the point: the
|
|
318
|
+
// alternative is publishing it as `vanilla`, which reads as "no special
|
|
319
|
+
// handling needed" when the opposite was just established.
|
|
320
|
+
return {
|
|
321
|
+
problem: `viem's "${viemChainName}" carries formatters from a chainConfig this generator ` +
|
|
322
|
+
'does not recognise — add it to STACK_FORMATTERS and to the ChainStack union',
|
|
323
|
+
};
|
|
324
|
+
}
|
|
325
|
+
/**
|
|
326
|
+
* Stamps `stack` onto every registry entry.
|
|
327
|
+
*
|
|
328
|
+
* Derived from viem wherever viem defines the chain, which is every EVM chain
|
|
329
|
+
* today, so the common case is authored nowhere and cannot drift. The jsonnet's
|
|
330
|
+
* `stack` is the escape hatch for the case that motivates the field at all: a
|
|
331
|
+
* chain viem has never heard of, which a consumer has to synthesise with
|
|
332
|
+
* `defineChain` and therefore needs told which `chainConfig` to apply.
|
|
333
|
+
*
|
|
334
|
+
* For such a chain the field is **required, not defaulted**. Defaulting to
|
|
335
|
+
* `vanilla` would make the single most likely new chain — an OP-stack L2 —
|
|
336
|
+
* synthesise without OP-stack formatters, so its L1 fee fields would be missing
|
|
337
|
+
* and its gas silently mispriced. Today's behaviour for an unknown chain is a
|
|
338
|
+
* loud skip; a silent misprice would be strictly worse, so the decision is
|
|
339
|
+
* forced here, where a human is adding the chain anyway.
|
|
340
|
+
*/
|
|
341
|
+
function classifyChainStacks(chains, networks) {
|
|
342
|
+
const issues = [];
|
|
343
|
+
const stamped = {};
|
|
344
|
+
const entriesById = new Map();
|
|
345
|
+
for (const entry of [...networks.mainnets, ...networks.testnets]) {
|
|
346
|
+
entriesById.set(String(entry.id), entry);
|
|
347
|
+
}
|
|
348
|
+
for (const [chainId, chain] of Object.entries(chains)) {
|
|
349
|
+
const chainName = isNonEmptyString(chain?.name)
|
|
350
|
+
? chain.name
|
|
351
|
+
: '<unnamed>';
|
|
352
|
+
// Same rule as `network`: present if and only if derived here, including on
|
|
353
|
+
// the failure paths, so the structural check stays an independent gate.
|
|
354
|
+
const base = { ...chain };
|
|
355
|
+
delete base.stack;
|
|
356
|
+
const entry = entriesById.get(chainId);
|
|
357
|
+
const fail = (message) => {
|
|
358
|
+
issues.push({ severity: 'error', chainId, chainName, message });
|
|
359
|
+
stamped[chainId] = withSortedKeys(base);
|
|
360
|
+
};
|
|
361
|
+
let authored;
|
|
362
|
+
if (entry?.stack !== undefined) {
|
|
363
|
+
if (!isChainStack(entry.stack)) {
|
|
364
|
+
fail(`stack ${JSON.stringify(entry.stack)} is not one of ${[...STACKS].join(', ')}`);
|
|
365
|
+
continue;
|
|
366
|
+
}
|
|
367
|
+
authored = entry.stack;
|
|
368
|
+
}
|
|
369
|
+
if (entry?.viemChain) {
|
|
370
|
+
const { stack, problem } = stackFromViem(entry.viemChain);
|
|
371
|
+
if (problem || !stack) {
|
|
372
|
+
fail(problem ?? 'stack could not be derived from viem');
|
|
373
|
+
continue;
|
|
374
|
+
}
|
|
375
|
+
// Authoring is allowed to agree, never to override: viem's definition is
|
|
376
|
+
// what the consumer actually uses for a chain viem ships.
|
|
377
|
+
if (authored !== undefined && authored !== stack) {
|
|
378
|
+
fail(`stack ${JSON.stringify(authored)} contradicts viem's own "${entry.viemChain}", ` +
|
|
379
|
+
`which is ${JSON.stringify(stack)} — drop the override or fix it`);
|
|
380
|
+
continue;
|
|
381
|
+
}
|
|
382
|
+
stamped[chainId] = withSortedKeys({ ...base, stack });
|
|
383
|
+
continue;
|
|
384
|
+
}
|
|
385
|
+
const isEvm = chain.vmType === undefined || chain.vmType === 'evm';
|
|
386
|
+
const isVirtual = chain.virtual === true;
|
|
387
|
+
if (isEvm && !isVirtual) {
|
|
388
|
+
if (authored === undefined) {
|
|
389
|
+
fail('is an EVM chain with no viem chain entry, so consumers must synthesise it — ' +
|
|
390
|
+
`author \`stack\` for it in config/shared-configs.jsonnet (one of ${[...STACKS].join(', ')}). ` +
|
|
391
|
+
'It is not defaulted: publishing an OP-stack chain as "vanilla" would drop its ' +
|
|
392
|
+
'L1 fee fields and misprice gas with no error anywhere');
|
|
393
|
+
continue;
|
|
394
|
+
}
|
|
395
|
+
stamped[chainId] = withSortedKeys({ ...base, stack: authored });
|
|
396
|
+
continue;
|
|
397
|
+
}
|
|
398
|
+
// Non-EVM (Solana, Tron) and virtual (HyperCore) chains are never built as
|
|
399
|
+
// viem chains, so the value is inert. Stamped anyway to keep the field
|
|
400
|
+
// required for every entry rather than optional-in-practice.
|
|
401
|
+
if (authored !== undefined) {
|
|
402
|
+
issues.push({
|
|
403
|
+
severity: 'warning',
|
|
404
|
+
chainId,
|
|
405
|
+
chainName,
|
|
406
|
+
message: `has an authored stack ${JSON.stringify(authored)} but is non-EVM or virtual, ` +
|
|
407
|
+
'so it is never synthesised as a viem chain — the value is ignored',
|
|
408
|
+
});
|
|
409
|
+
}
|
|
410
|
+
stamped[chainId] = withSortedKeys({ ...base, stack: 'vanilla' });
|
|
411
|
+
}
|
|
412
|
+
return { chains: stamped, issues };
|
|
413
|
+
}
|
|
244
414
|
/**
|
|
245
415
|
* Validates a chain registry for structural and configuration problems.
|
|
246
416
|
*
|
package/dist/src/chains.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ChainNetwork, PegGroup, ProviderName, SettlementLayer, SupportedChain, SwapQuoter, SwapQuoterConfig, VmType } from "./types";
|
|
1
|
+
import type { ChainNetwork, ChainStack, PegGroup, ProviderName, SettlementLayer, SupportedChain, SwapQuoter, SwapQuoterConfig, VmType } from "./types";
|
|
2
2
|
interface Token {
|
|
3
3
|
/** EVM: 0x-prefixed hex. SVM: base58 SPL mint. TVM: base58 (T-prefixed). */
|
|
4
4
|
address: string;
|
|
@@ -8,6 +8,8 @@ interface Token {
|
|
|
8
8
|
pegGroup?: PegGroup;
|
|
9
9
|
balanceSlot: number | null;
|
|
10
10
|
approvalSlot: number | null;
|
|
11
|
+
/** Bridge layers that can carry this token on this chain, when known. */
|
|
12
|
+
settlementLayers?: SettlementLayer[];
|
|
11
13
|
unpriced?: boolean;
|
|
12
14
|
}
|
|
13
15
|
interface NativeToken {
|
|
@@ -21,6 +23,8 @@ interface Chain {
|
|
|
21
23
|
vmType: VmType;
|
|
22
24
|
/** Whether this chain is a mainnet or a testnet. */
|
|
23
25
|
network: ChainNetwork;
|
|
26
|
+
/** Execution stack, for consumers that must synthesise a viem `Chain`. */
|
|
27
|
+
stack: ChainStack;
|
|
24
28
|
/** CAIP-2 chain identifier; populated for non-eip155 chains only. */
|
|
25
29
|
caip2?: string;
|
|
26
30
|
/** True for virtual chains (e.g. HyperCore) that settle on another chain. */
|
package/dist/src/chains.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chains.d.ts","sourceRoot":"","sources":["../../src/chains.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,QAAQ,EAAE,YAAY,EAAE,eAAe,EAAE,cAAc,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"chains.d.ts","sourceRoot":"","sources":["../../src/chains.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,UAAU,EAAE,QAAQ,EAAE,YAAY,EAAE,eAAe,EAAE,cAAc,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAEvJ,UAAU,KAAK;IACb,4EAA4E;IAC5E,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,yEAAyE;IACzE,gBAAgB,CAAC,EAAE,eAAe,EAAE,CAAC;IACrC,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,UAAU,WAAW;IACnB,oFAAoF;IACpF,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,UAAU,KAAK;IACb,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,oDAAoD;IACpD,OAAO,EAAE,YAAY,CAAC;IACtB,0EAA0E;IAC1E,KAAK,EAAE,UAAU,CAAC;IAClB,qEAAqE;IACrE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,6EAA6E;IAC7E,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,WAAW,EAAE,WAAW,CAAC;IACzB,kBAAkB,EAAE,WAAW,CAAC;IAChC,MAAM,EAAE,KAAK,EAAE,CAAC;IAChB,gBAAgB,EAAE,eAAe,EAAE,CAAC;IACpC,SAAS,EAAE,YAAY,EAAE,CAAC;IAC1B,WAAW,EAAE,UAAU,EAAE,CAAC;IAC1B,gBAAgB,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,UAAU,EAAE,gBAAgB,CAAC,CAAC,CAAC;CAClE;AAED,QAAA,MAAM,MAAM,EAAE,MAAM,CAAC,cAAc,EAAE,KAAK,CA2iDzC,CAAC;AAEF,OAAO,EAAE,MAAM,EAAE,CAAC;AAClB,YAAY,EAAE,KAAK,EAAE,KAAK,EAAE,WAAW,EAAE,CAAC"}
|