@rhinestone/shared-configs 1.7.17 → 1.8.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.
@@ -16,6 +16,7 @@
16
16
  "RHINO",
17
17
  "CCTP"
18
18
  ],
19
+ "stack": "vanilla",
19
20
  "swapQuoterConfig": {},
20
21
  "swapQuoters": [
21
22
  "1inch",
@@ -83,6 +84,7 @@
83
84
  "RHINO",
84
85
  "CCTP"
85
86
  ],
87
+ "stack": "op-stack",
86
88
  "swapQuoterConfig": {},
87
89
  "swapQuoters": [
88
90
  "1inch",
@@ -156,6 +158,7 @@
156
158
  "RHINO",
157
159
  "CCTP"
158
160
  ],
161
+ "stack": "vanilla",
159
162
  "swapQuoters": [
160
163
  "1inch",
161
164
  "0x",
@@ -209,6 +212,7 @@
209
212
  "NEAR",
210
213
  "RHINO"
211
214
  ],
215
+ "stack": "vanilla",
212
216
  "swapQuoters": [
213
217
  "1inch",
214
218
  "velora"
@@ -246,6 +250,7 @@
246
250
  "RHINO",
247
251
  "CCTP"
248
252
  ],
253
+ "stack": "op-stack",
249
254
  "swapQuoterConfig": {},
250
255
  "swapQuoters": [
251
256
  "1inch",
@@ -313,6 +318,7 @@
313
318
  "RHINO",
314
319
  "CCTP"
315
320
  ],
321
+ "stack": "vanilla",
316
322
  "swapQuoterConfig": {},
317
323
  "swapQuoters": [
318
324
  "1inch",
@@ -368,6 +374,7 @@
368
374
  "RELAY",
369
375
  "CCTP"
370
376
  ],
377
+ "stack": "vanilla",
371
378
  "swapQuoters": [
372
379
  "0x"
373
380
  ],
@@ -425,6 +432,7 @@
425
432
  "RHINO",
426
433
  "CCTP"
427
434
  ],
435
+ "stack": "vanilla",
428
436
  "swapQuoterConfig": {},
429
437
  "swapQuoters": [
430
438
  "1inch",
@@ -462,6 +470,7 @@
462
470
  "RELAY",
463
471
  "CCTP"
464
472
  ],
473
+ "stack": "vanilla",
465
474
  "swapQuoters": [
466
475
  "0x"
467
476
  ],
@@ -501,6 +510,7 @@
501
510
  },
502
511
  "network": "mainnet",
503
512
  "settlementLayers": [],
513
+ "stack": "vanilla",
504
514
  "swapQuoters": [],
505
515
  "tokens": [
506
516
  {
@@ -532,6 +542,7 @@
532
542
  "ACROSS",
533
543
  "RELAY"
534
544
  ],
545
+ "stack": "op-stack",
535
546
  "swapQuoters": [],
536
547
  "tokens": [
537
548
  {
@@ -587,6 +598,7 @@
587
598
  "ACROSS",
588
599
  "RELAY"
589
600
  ],
601
+ "stack": "vanilla",
590
602
  "swapQuoters": [
591
603
  "1inch",
592
604
  "0x"
@@ -641,6 +653,7 @@
641
653
  "RHINO",
642
654
  "CCTP"
643
655
  ],
656
+ "stack": "op-stack",
644
657
  "swapQuoterConfig": {},
645
658
  "swapQuoters": [
646
659
  "1inch",
@@ -696,6 +709,7 @@
696
709
  "OFT",
697
710
  "RHINO"
698
711
  ],
712
+ "stack": "vanilla",
699
713
  "swapQuoterConfig": {},
700
714
  "swapQuoters": [
701
715
  "0x",
@@ -730,6 +744,7 @@
730
744
  "settlementLayers": [
731
745
  "ECO"
732
746
  ],
747
+ "stack": "vanilla",
733
748
  "swapQuoters": [],
734
749
  "tokens": [
735
750
  {
@@ -774,6 +789,7 @@
774
789
  "RHINO",
775
790
  "CCTP"
776
791
  ],
792
+ "stack": "vanilla",
777
793
  "swapQuoterConfig": {},
778
794
  "swapQuoters": [
779
795
  "1inch",
@@ -839,6 +855,7 @@
839
855
  "RHINO",
840
856
  "CCTP"
841
857
  ],
858
+ "stack": "vanilla",
842
859
  "swapQuoters": [
843
860
  "1inch",
844
861
  "0x",
@@ -907,6 +924,7 @@
907
924
  "ECO",
908
925
  "CCTP"
909
926
  ],
927
+ "stack": "op-stack",
910
928
  "swapQuoters": [],
911
929
  "tokens": [
912
930
  {
@@ -965,6 +983,7 @@
965
983
  "ECO",
966
984
  "CCTP"
967
985
  ],
986
+ "stack": "vanilla",
968
987
  "swapQuoters": [],
969
988
  "tokens": [
970
989
  {
@@ -1012,6 +1031,7 @@
1012
1031
  "RELAY",
1013
1032
  "RHINO"
1014
1033
  ],
1034
+ "stack": "vanilla",
1015
1035
  "swapQuoters": [],
1016
1036
  "tokens": [
1017
1037
  {
@@ -1070,6 +1090,7 @@
1070
1090
  "ECO",
1071
1091
  "CCTP"
1072
1092
  ],
1093
+ "stack": "vanilla",
1073
1094
  "swapQuoters": [],
1074
1095
  "tokens": [
1075
1096
  {
@@ -1118,6 +1139,7 @@
1118
1139
  "ECO",
1119
1140
  "CCTP"
1120
1141
  ],
1142
+ "stack": "op-stack",
1121
1143
  "swapQuoters": [],
1122
1144
  "tokens": [
1123
1145
  {
@@ -1168,6 +1190,7 @@
1168
1190
  "NEAR",
1169
1191
  "RHINO"
1170
1192
  ],
1193
+ "stack": "vanilla",
1171
1194
  "swapQuoters": [],
1172
1195
  "tokens": [
1173
1196
  {
@@ -1209,6 +1232,7 @@
1209
1232
  "RHINO",
1210
1233
  "CCTP"
1211
1234
  ],
1235
+ "stack": "vanilla",
1212
1236
  "swapQuoters": [],
1213
1237
  "tokens": [
1214
1238
  {
@@ -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
+ });
@@ -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). */",
@@ -114,6 +114,8 @@ function renderChains(chainRegistry, mainnets, testnets) {
114
114
  " vmType: VmType;",
115
115
  " /** Whether this chain is a mainnet or a testnet. */",
116
116
  " network: ChainNetwork;",
117
+ " /** Execution stack, for consumers that must synthesise a viem `Chain`. */",
118
+ " stack: ChainStack;",
117
119
  " /** CAIP-2 chain identifier; populated for non-eip155 chains only. */",
118
120
  " caip2?: string;",
119
121
  " /** True for virtual chains (e.g. HyperCore) that settle on another chain. */",
@@ -360,7 +362,11 @@ async function generate() {
360
362
  // registry that reaches configs/chains.json — and from there the published
361
363
  // facts artifact and the read-client's bundled fallback — carries the
362
364
  // classification rather than leaving it implicit in the network maps.
363
- const { chains: classifiedChains, issues: classificationIssues } = (0, validation_1.classifyChainNetworks)(output.chains, output.mainnets, output.testnets);
365
+ const { chains: networkClassified, issues: networkIssues } = (0, validation_1.classifyChainNetworks)(output.chains, output.mainnets, output.testnets);
366
+ // Then the execution stack, for consumers that have to build a viem `Chain`
367
+ // themselves because the chain postdates their pinned viem.
368
+ const { chains: classifiedChains, issues: stackIssues } = (0, validation_1.classifyChainStacks)(networkClassified, output.networks);
369
+ const classificationIssues = [...networkIssues, ...stackIssues];
364
370
  await (0, promises_1.writeFile)(node_path_1.default.join(configsDir, "chains.json"), `${JSON.stringify(classifiedChains, null, 2)}\n`);
365
371
  await (0, promises_1.writeFile)(node_path_1.default.join(configsDir, "mainnets.json"), `${JSON.stringify(output.mainnets, null, 2)}\n`);
366
372
  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":"AAAA,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,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;oFACgF;IAChF,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAqJD,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;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,aAAa,GAAG,gBAAgB,CAiE3E;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,gBAAgB,GAAG,MAAM,CAWvE"}
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
  *
@@ -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;
@@ -21,6 +21,8 @@ interface Chain {
21
21
  vmType: VmType;
22
22
  /** Whether this chain is a mainnet or a testnet. */
23
23
  network: ChainNetwork;
24
+ /** Execution stack, for consumers that must synthesise a viem `Chain`. */
25
+ stack: ChainStack;
24
26
  /** CAIP-2 chain identifier; populated for non-eip155 chains only. */
25
27
  caip2?: string;
26
28
  /** True for virtual chains (e.g. HyperCore) that settle on another chain. */
@@ -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;AAE3I,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,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,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,CAg0CzC,CAAC;AAEF,OAAO,EAAE,MAAM,EAAE,CAAC;AAClB,YAAY,EAAE,KAAK,EAAE,KAAK,EAAE,WAAW,EAAE,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,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,CAw1CzC,CAAC;AAEF,OAAO,EAAE,MAAM,EAAE,CAAC;AAClB,YAAY,EAAE,KAAK,EAAE,KAAK,EAAE,WAAW,EAAE,CAAC"}