@rhinestone/shared-configs 1.4.132 → 1.4.133

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.
@@ -0,0 +1,36 @@
1
+ {
2
+ "1": {
3
+ "name": "Ethereum",
4
+ "settlementLayers": ["ACROSS", "ECO", "RELAY"],
5
+ "tokens": [
6
+ { "symbol": "USDC", "address": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", "decimals": 6 },
7
+ { "symbol": "WETH", "address": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", "decimals": 18 }
8
+ ],
9
+ "nativeToken": { "address": "0x0000000000000000000000000000000000000000", "decimals": 18, "symbol": "ETH" },
10
+ "wrappedNativeToken": { "address": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", "decimals": 18, "symbol": "WETH" },
11
+ "swapQuoters": [],
12
+ "swapQuoterConfig": {}
13
+ },
14
+ "100": {
15
+ "name": "Gnosis",
16
+ "settlementLayers": ["RELAY"],
17
+ "tokens": [
18
+ { "symbol": "USDC", "address": "0xDDAfbb505ad214D7b80b1f830fcCc89B60fb7A83", "decimals": 6 }
19
+ ],
20
+ "nativeToken": { "address": "0x0000000000000000000000000000000000000000", "decimals": 18, "symbol": "xDAI" },
21
+ "wrappedNativeToken": { "address": "0xe91D153E0b41518A2Ce8Dd3D7944Fa863463a97d", "decimals": 18, "symbol": "WXDAI" },
22
+ "swapQuoters": [],
23
+ "swapQuoterConfig": {}
24
+ },
25
+ "10": {
26
+ "name": "OP Mainnet",
27
+ "settlementLayers": ["ACROSS", "RELAY"],
28
+ "tokens": [
29
+ { "symbol": "USDC", "address": "0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85", "decimals": 6 }
30
+ ],
31
+ "nativeToken": { "address": "0x0000000000000000000000000000000000000000", "decimals": 18, "symbol": "ETH" },
32
+ "wrappedNativeToken": { "address": "0x4200000000000000000000000000000000000006", "decimals": 18, "symbol": "WETH" },
33
+ "swapQuoters": [],
34
+ "swapQuoterConfig": {}
35
+ }
36
+ }
@@ -0,0 +1,47 @@
1
+ {
2
+ "1": {
3
+ "name": "Ethereum",
4
+ "settlementLayers": ["ACROSS", "ECO", "RELAY"],
5
+ "tokens": [
6
+ { "symbol": "USDC", "address": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", "decimals": 6 },
7
+ { "symbol": "WETH", "address": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", "decimals": 18 }
8
+ ],
9
+ "nativeToken": { "address": "0x0000000000000000000000000000000000000000", "decimals": 18, "symbol": "ETH" },
10
+ "wrappedNativeToken": { "address": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", "decimals": 18, "symbol": "WETH" },
11
+ "swapQuoters": [],
12
+ "swapQuoterConfig": {}
13
+ },
14
+ "100": {
15
+ "name": "Gnosis",
16
+ "settlementLayers": ["RELAY"],
17
+ "tokens": [
18
+ { "symbol": "USDC", "address": "0xDDAfbb505ad214D7b80b1f830fcCc89B60fb7A83", "decimals": 6 }
19
+ ],
20
+ "nativeToken": { "address": "0x0000000000000000000000000000000000000000", "decimals": 18, "symbol": "xDAI" },
21
+ "wrappedNativeToken": { "address": "0xe91D153E0b41518A2Ce8Dd3D7944Fa863463a97d", "decimals": 18, "symbol": "WXDAI" },
22
+ "swapQuoters": [],
23
+ "swapQuoterConfig": {}
24
+ },
25
+ "10": {
26
+ "name": "OP Mainnet",
27
+ "settlementLayers": ["ACROSS", "RELAY"],
28
+ "tokens": [
29
+ { "symbol": "USDC", "address": "0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85", "decimals": 6 }
30
+ ],
31
+ "nativeToken": { "address": "0x0000000000000000000000000000000000000000", "decimals": 18, "symbol": "ETH" },
32
+ "wrappedNativeToken": { "address": "0x4200000000000000000000000000000000000006", "decimals": 18, "symbol": "WETH" },
33
+ "swapQuoters": [],
34
+ "swapQuoterConfig": {}
35
+ },
36
+ "42161": {
37
+ "name": "Arbitrum One",
38
+ "settlementLayers": ["ACROSS", "ECO", "RELAY"],
39
+ "tokens": [
40
+ { "symbol": "USDC", "address": "0xaf88d065e77c8cC2239327C5EDb3A432268e5831", "decimals": 6 }
41
+ ],
42
+ "nativeToken": { "address": "0x0000000000000000000000000000000000000000", "decimals": 18, "symbol": "ETH" },
43
+ "wrappedNativeToken": { "address": "0x82aF49447D8a07e3bd95BD0d56f35241523fBab1", "decimals": 18, "symbol": "WETH" },
44
+ "swapQuoters": [],
45
+ "swapQuoterConfig": {}
46
+ }
47
+ }
@@ -0,0 +1,36 @@
1
+ {
2
+ "1": {
3
+ "name": "Ethereum",
4
+ "settlementLayers": ["ACROSS", "ECO", "RELAY"],
5
+ "tokens": [
6
+ { "symbol": "USDC", "address": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", "decimals": 6 },
7
+ { "symbol": "WETH", "address": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", "decimals": 18 }
8
+ ],
9
+ "nativeToken": { "address": "0x0000000000000000000000000000000000000000", "decimals": 18, "symbol": "ETH" },
10
+ "wrappedNativeToken": { "address": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", "decimals": 18, "symbol": "WETH" },
11
+ "swapQuoters": [],
12
+ "swapQuoterConfig": {}
13
+ },
14
+ "100": {
15
+ "name": "Gnosis",
16
+ "settlementLayers": [],
17
+ "tokens": [
18
+ { "symbol": "USDC", "address": "0xDDAfbb505ad214D7b80b1f830fcCc89B60fb7A83", "decimals": 6 }
19
+ ],
20
+ "nativeToken": { "address": "0x0000000000000000000000000000000000000000", "decimals": 18, "symbol": "xDAI" },
21
+ "wrappedNativeToken": { "address": "0xe91D153E0b41518A2Ce8Dd3D7944Fa863463a97d", "decimals": 18, "symbol": "WXDAI" },
22
+ "swapQuoters": [],
23
+ "swapQuoterConfig": {}
24
+ },
25
+ "10": {
26
+ "name": "OP Mainnet",
27
+ "settlementLayers": ["ACROSS", "RELAY"],
28
+ "tokens": [
29
+ { "symbol": "USDC", "address": "0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85", "decimals": 6 }
30
+ ],
31
+ "nativeToken": { "address": "0x0000000000000000000000000000000000000000", "decimals": 18, "symbol": "ETH" },
32
+ "wrappedNativeToken": { "address": "0x4200000000000000000000000000000000000006", "decimals": 18, "symbol": "WETH" },
33
+ "swapQuoters": [],
34
+ "swapQuoterConfig": {}
35
+ }
36
+ }
@@ -0,0 +1,36 @@
1
+ {
2
+ "1": {
3
+ "name": "Ethereum",
4
+ "settlementLayers": ["ACROSS", "ECO", "RELAY"],
5
+ "tokens": [
6
+ { "symbol": "USDC", "address": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", "decimals": 6 },
7
+ { "symbol": "WETH", "address": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", "decimals": 18 }
8
+ ],
9
+ "nativeToken": { "address": "0x0000000000000000000000000000000000000000", "decimals": 18, "symbol": "ETH" },
10
+ "wrappedNativeToken": { "address": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", "decimals": 18, "symbol": "WETH" },
11
+ "swapQuoters": [],
12
+ "swapQuoterConfig": {}
13
+ },
14
+ "100": {
15
+ "name": "Gnosis",
16
+ "settlementLayers": ["RELAY"],
17
+ "tokens": [
18
+ { "symbol": "USDC", "address": "0xDDAfbb505ad214D7b80b1f830fcCc89B60fb7A83", "decimals": 6 }
19
+ ],
20
+ "nativeToken": { "address": "0x0000000000000000000000000000000000000000", "decimals": 18, "symbol": "xDAI" },
21
+ "wrappedNativeToken": { "address": "0xe91D153E0b41518A2Ce8Dd3D7944Fa863463a97d", "decimals": 18, "symbol": "WXDAI" },
22
+ "swapQuoters": [],
23
+ "swapQuoterConfig": {}
24
+ },
25
+ "10": {
26
+ "name": "OP Mainnet",
27
+ "settlementLayers": ["ACROSS"],
28
+ "tokens": [
29
+ { "symbol": "USDC", "address": "0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85", "decimals": 6 }
30
+ ],
31
+ "nativeToken": { "address": "0x0000000000000000000000000000000000000000", "decimals": 18, "symbol": "ETH" },
32
+ "wrappedNativeToken": { "address": "0x4200000000000000000000000000000000000006", "decimals": 18, "symbol": "WETH" },
33
+ "swapQuoters": [],
34
+ "swapQuoterConfig": {}
35
+ }
36
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=impact-analysis.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"impact-analysis.test.d.ts","sourceRoot":"","sources":["../../../scripts/__tests__/impact-analysis.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,91 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const bun_test_1 = require("bun:test");
7
+ const impact_analysis_1 = require("../impact-analysis");
8
+ const chains_base_json_1 = __importDefault(require("./fixtures/chains-base.json"));
9
+ const chains_pr_remove_layer_json_1 = __importDefault(require("./fixtures/chains-pr-remove-layer.json"));
10
+ const chains_pr_add_chain_json_1 = __importDefault(require("./fixtures/chains-pr-add-chain.json"));
11
+ const chains_pr_orphan_json_1 = __importDefault(require("./fixtures/chains-pr-orphan.json"));
12
+ (0, bun_test_1.describe)("analyzeImpact", () => {
13
+ (0, bun_test_1.it)("detects no changes when configs are identical", () => {
14
+ const analysis = (0, impact_analysis_1.analyzeImpact)(chains_base_json_1.default, chains_base_json_1.default);
15
+ (0, bun_test_1.expect)(analysis.hasChanges).toBe(false);
16
+ (0, bun_test_1.expect)(analysis.chainChanges).toHaveLength(0);
17
+ });
18
+ (0, bun_test_1.it)("detects added chain", () => {
19
+ const analysis = (0, impact_analysis_1.analyzeImpact)(chains_base_json_1.default, chains_pr_add_chain_json_1.default);
20
+ (0, bun_test_1.expect)(analysis.hasChanges).toBe(true);
21
+ const added = analysis.chainChanges.filter((c) => c.type === "added");
22
+ (0, bun_test_1.expect)(added).toHaveLength(1);
23
+ (0, bun_test_1.expect)(added[0].chainName).toBe("Arbitrum One");
24
+ (0, bun_test_1.expect)(added[0].layersAfter).toEqual(["ACROSS", "ECO", "RELAY"]);
25
+ });
26
+ (0, bun_test_1.it)("detects removed settlement layer", () => {
27
+ const analysis = (0, impact_analysis_1.analyzeImpact)(chains_base_json_1.default, chains_pr_remove_layer_json_1.default);
28
+ (0, bun_test_1.expect)(analysis.hasChanges).toBe(true);
29
+ const modified = analysis.chainChanges.filter((c) => c.type === "modified");
30
+ (0, bun_test_1.expect)(modified).toHaveLength(1);
31
+ (0, bun_test_1.expect)(modified[0].chainName).toBe("OP Mainnet");
32
+ (0, bun_test_1.expect)(modified[0].layersRemoved).toContain("RELAY");
33
+ (0, bun_test_1.expect)(modified[0].layersAfter).toEqual(["ACROSS"]);
34
+ });
35
+ (0, bun_test_1.it)("detects removed chain (orphan = chain with zero layers effectively removed)", () => {
36
+ // Compare base with a config where Gnosis has no layers
37
+ const analysis = (0, impact_analysis_1.analyzeImpact)(chains_base_json_1.default, chains_pr_orphan_json_1.default);
38
+ (0, bun_test_1.expect)(analysis.hasChanges).toBe(true);
39
+ const modified = analysis.chainChanges.filter((c) => c.type === "modified");
40
+ (0, bun_test_1.expect)(modified).toHaveLength(1);
41
+ (0, bun_test_1.expect)(modified[0].chainName).toBe("Gnosis");
42
+ (0, bun_test_1.expect)(modified[0].layersRemoved).toContain("RELAY");
43
+ (0, bun_test_1.expect)(modified[0].layersAfter).toEqual([]);
44
+ });
45
+ (0, bun_test_1.it)("computes layer summaries correctly", () => {
46
+ const analysis = (0, impact_analysis_1.analyzeImpact)(chains_base_json_1.default, chains_pr_remove_layer_json_1.default);
47
+ const relaySummary = analysis.layerSummaries.find((s) => s.layer === "RELAY");
48
+ (0, bun_test_1.expect)(relaySummary).toBeDefined();
49
+ (0, bun_test_1.expect)(relaySummary.chainsBefore).toBe(3); // Ethereum, Gnosis, OP Mainnet
50
+ (0, bun_test_1.expect)(relaySummary.chainsAfter).toBe(2); // Ethereum, Gnosis
51
+ (0, bun_test_1.expect)(relaySummary.chainsRemoved).toContain("OP Mainnet");
52
+ });
53
+ (0, bun_test_1.it)("identifies fragile chains in PR config", () => {
54
+ const analysis = (0, impact_analysis_1.analyzeImpact)(chains_base_json_1.default, chains_pr_remove_layer_json_1.default);
55
+ // Gnosis is still single-layer, and now OP Mainnet is also single-layer
56
+ (0, bun_test_1.expect)(analysis.fragileChains.length).toBeGreaterThanOrEqual(2);
57
+ const gnosis = analysis.fragileChains.find((c) => c.chainName === "Gnosis");
58
+ (0, bun_test_1.expect)(gnosis).toBeDefined();
59
+ (0, bun_test_1.expect)(gnosis.singleLayer).toBe("RELAY");
60
+ const op = analysis.fragileChains.find((c) => c.chainName === "OP Mainnet");
61
+ (0, bun_test_1.expect)(op).toBeDefined();
62
+ (0, bun_test_1.expect)(op.singleLayer).toBe("ACROSS");
63
+ });
64
+ });
65
+ (0, bun_test_1.describe)("renderMarkdown", () => {
66
+ (0, bun_test_1.it)("renders 'no changes' message when nothing changed", () => {
67
+ const analysis = (0, impact_analysis_1.analyzeImpact)(chains_base_json_1.default, chains_base_json_1.default);
68
+ const md = (0, impact_analysis_1.renderMarkdown)(analysis);
69
+ (0, bun_test_1.expect)(md).toContain("No config changes detected");
70
+ });
71
+ (0, bun_test_1.it)("renders chain changes table for modifications", () => {
72
+ const analysis = (0, impact_analysis_1.analyzeImpact)(chains_base_json_1.default, chains_pr_remove_layer_json_1.default);
73
+ const md = (0, impact_analysis_1.renderMarkdown)(analysis);
74
+ (0, bun_test_1.expect)(md).toContain("Chain Changes");
75
+ (0, bun_test_1.expect)(md).toContain("OP Mainnet");
76
+ (0, bun_test_1.expect)(md).toContain("Settlement Layer Impact");
77
+ });
78
+ (0, bun_test_1.it)("renders fragile chains section", () => {
79
+ const analysis = (0, impact_analysis_1.analyzeImpact)(chains_base_json_1.default, chains_pr_remove_layer_json_1.default);
80
+ const md = (0, impact_analysis_1.renderMarkdown)(analysis);
81
+ (0, bun_test_1.expect)(md).toContain("Fragile Chains");
82
+ (0, bun_test_1.expect)(md).toContain("Gnosis");
83
+ (0, bun_test_1.expect)(md).toContain("RELAY only");
84
+ });
85
+ (0, bun_test_1.it)("renders added chain", () => {
86
+ const analysis = (0, impact_analysis_1.analyzeImpact)(chains_base_json_1.default, chains_pr_add_chain_json_1.default);
87
+ const md = (0, impact_analysis_1.renderMarkdown)(analysis);
88
+ (0, bun_test_1.expect)(md).toContain("Arbitrum One");
89
+ (0, bun_test_1.expect)(md).toContain("added");
90
+ });
91
+ });
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=validation.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validation.test.d.ts","sourceRoot":"","sources":["../../../scripts/__tests__/validation.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,93 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const bun_test_1 = require("bun:test");
4
+ const validation_1 = require("../validation");
5
+ (0, bun_test_1.describe)("validateChainConfig", () => {
6
+ (0, bun_test_1.it)("returns no issues for chains with multiple settlement layers", () => {
7
+ const chains = {
8
+ "1": { name: "Ethereum", settlementLayers: ["ACROSS", "ECO", "RELAY"], tokens: [] },
9
+ };
10
+ const result = (0, validation_1.validateChainConfig)(chains);
11
+ (0, bun_test_1.expect)(result.issues).toHaveLength(0);
12
+ (0, bun_test_1.expect)(result.hasErrors).toBe(false);
13
+ });
14
+ (0, bun_test_1.it)("returns error for chain with zero settlement layers", () => {
15
+ const chains = {
16
+ "100": { name: "Gnosis", settlementLayers: [], tokens: [] },
17
+ };
18
+ const result = (0, validation_1.validateChainConfig)(chains);
19
+ (0, bun_test_1.expect)(result.issues).toHaveLength(1);
20
+ (0, bun_test_1.expect)(result.issues[0].severity).toBe("error");
21
+ (0, bun_test_1.expect)(result.issues[0].chainId).toBe("100");
22
+ (0, bun_test_1.expect)(result.issues[0].message).toContain("no settlement layers");
23
+ (0, bun_test_1.expect)(result.hasErrors).toBe(true);
24
+ });
25
+ (0, bun_test_1.it)("returns warning for chain with single settlement layer", () => {
26
+ const chains = {
27
+ "100": { name: "Gnosis", settlementLayers: ["RELAY"], tokens: [] },
28
+ };
29
+ const result = (0, validation_1.validateChainConfig)(chains);
30
+ (0, bun_test_1.expect)(result.issues).toHaveLength(1);
31
+ (0, bun_test_1.expect)(result.issues[0].severity).toBe("warning");
32
+ (0, bun_test_1.expect)(result.issues[0].message).toContain("RELAY");
33
+ (0, bun_test_1.expect)(result.hasErrors).toBe(false);
34
+ });
35
+ (0, bun_test_1.it)("handles mixed chains correctly", () => {
36
+ const chains = {
37
+ "1": { name: "Ethereum", settlementLayers: ["ACROSS", "ECO", "RELAY"], tokens: [] },
38
+ "100": { name: "Gnosis", settlementLayers: ["RELAY"], tokens: [] },
39
+ "999": { name: "Broken", settlementLayers: [], tokens: [] },
40
+ };
41
+ const result = (0, validation_1.validateChainConfig)(chains);
42
+ (0, bun_test_1.expect)(result.issues).toHaveLength(2);
43
+ (0, bun_test_1.expect)(result.hasErrors).toBe(true);
44
+ const errors = result.issues.filter((i) => i.severity === "error");
45
+ const warnings = result.issues.filter((i) => i.severity === "warning");
46
+ (0, bun_test_1.expect)(errors).toHaveLength(1);
47
+ (0, bun_test_1.expect)(warnings).toHaveLength(1);
48
+ (0, bun_test_1.expect)(errors[0].chainId).toBe("999");
49
+ (0, bun_test_1.expect)(warnings[0].chainId).toBe("100");
50
+ });
51
+ (0, bun_test_1.it)("returns no issues for empty chain registry", () => {
52
+ const result = (0, validation_1.validateChainConfig)({});
53
+ (0, bun_test_1.expect)(result.issues).toHaveLength(0);
54
+ (0, bun_test_1.expect)(result.hasErrors).toBe(false);
55
+ });
56
+ });
57
+ (0, bun_test_1.describe)("formatValidationIssues", () => {
58
+ (0, bun_test_1.it)("returns empty string for no issues", () => {
59
+ (0, bun_test_1.expect)((0, validation_1.formatValidationIssues)({ issues: [], hasErrors: false })).toBe("");
60
+ });
61
+ (0, bun_test_1.it)("formats errors with cross mark", () => {
62
+ const result = {
63
+ issues: [
64
+ {
65
+ severity: "error",
66
+ chainId: "100",
67
+ chainName: "Gnosis",
68
+ message: "no settlement layers",
69
+ },
70
+ ],
71
+ hasErrors: true,
72
+ };
73
+ const output = (0, validation_1.formatValidationIssues)(result);
74
+ (0, bun_test_1.expect)(output).toContain("\u2717");
75
+ (0, bun_test_1.expect)(output).toContain("Gnosis (100)");
76
+ });
77
+ (0, bun_test_1.it)("formats warnings with warning sign", () => {
78
+ const result = {
79
+ issues: [
80
+ {
81
+ severity: "warning",
82
+ chainId: "100",
83
+ chainName: "Gnosis",
84
+ message: "single settlement layer [RELAY]",
85
+ },
86
+ ],
87
+ hasErrors: false,
88
+ };
89
+ const output = (0, validation_1.formatValidationIssues)(result);
90
+ (0, bun_test_1.expect)(output).toContain("\u26A0");
91
+ (0, bun_test_1.expect)(output).toContain("Gnosis (100)");
92
+ });
93
+ });
@@ -8,6 +8,7 @@ const node_fs_1 = require("node:fs");
8
8
  const promises_1 = require("node:fs/promises");
9
9
  const node_os_1 = __importDefault(require("node:os"));
10
10
  const node_path_1 = __importDefault(require("node:path"));
11
+ const validation_1 = require("./validation");
11
12
  function renderNetworkEnum(name, entries) {
12
13
  const lines = entries.map((entry) => ` ${entry.enumKey} = ${entry.viemChain}.id,`);
13
14
  return `enum ${name} {\n${lines.join("\n")}\n}\n`;
@@ -338,6 +339,17 @@ async function generate() {
338
339
  await (0, promises_1.writeFile)(node_path_1.default.join(configsDir, "testnets.json"), `${JSON.stringify(output.testnets, null, 2)}\n`);
339
340
  await (0, promises_1.writeFile)(node_path_1.default.join(configsDir, "providers.json"), `${JSON.stringify(output.providers, null, 2)}\n`);
340
341
  await (0, promises_1.writeFile)(node_path_1.default.join(configsDir, "oft.json"), `${JSON.stringify(output.oft, null, 2)}\n`);
342
+ // Validate chain configuration
343
+ const validationResult = (0, validation_1.validateChainConfig)(output.chains);
344
+ const validationOutput = (0, validation_1.formatValidationIssues)(validationResult);
345
+ if (validationOutput) {
346
+ console.log('\nChain config validation:');
347
+ console.log(validationOutput);
348
+ console.log('');
349
+ }
350
+ if (validationResult.hasErrors) {
351
+ throw new Error('Chain configuration has errors — see above');
352
+ }
341
353
  await (0, promises_1.writeFile)(node_path_1.default.join(sharedConfigsRoot, "src", "chains.ts"), `${renderChains(output.chains, output.mainnets, output.testnets)}\n`);
342
354
  await (0, promises_1.writeFile)(node_path_1.default.join(generatedDir, "networks.ts"), `${renderNetworks(output.networks)}\n`);
343
355
  const prodAddrConfig = runOrchestratorFile(yeetRoot, "prod", output.chainNames);
@@ -0,0 +1,31 @@
1
+ import type { ChainRegistry } from "./validation";
2
+ export interface ChainChange {
3
+ chainId: string;
4
+ chainName: string;
5
+ type: 'added' | 'removed' | 'modified';
6
+ layersBefore: string[];
7
+ layersAfter: string[];
8
+ layersAdded: string[];
9
+ layersRemoved: string[];
10
+ }
11
+ export interface LayerSummary {
12
+ layer: string;
13
+ chainsBefore: number;
14
+ chainsAfter: number;
15
+ chainsRemoved: string[];
16
+ chainsAdded: string[];
17
+ }
18
+ export interface FragileChain {
19
+ chainId: string;
20
+ chainName: string;
21
+ singleLayer: string;
22
+ }
23
+ export interface ImpactAnalysis {
24
+ chainChanges: ChainChange[];
25
+ layerSummaries: LayerSummary[];
26
+ fragileChains: FragileChain[];
27
+ hasChanges: boolean;
28
+ }
29
+ export declare function analyzeImpact(base: ChainRegistry, pr: ChainRegistry): ImpactAnalysis;
30
+ export declare function renderMarkdown(analysis: ImpactAnalysis): string;
31
+ //# sourceMappingURL=impact-analysis.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"impact-analysis.d.ts","sourceRoot":"","sources":["../../scripts/impact-analysis.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAElD,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,OAAO,GAAG,SAAS,GAAG,UAAU,CAAC;IACvC,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,aAAa,EAAE,MAAM,EAAE,CAAC;CACzB;AAED,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,WAAW,EAAE,MAAM,EAAE,CAAC;CACvB;AAED,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,cAAc;IAC7B,YAAY,EAAE,WAAW,EAAE,CAAC;IAC5B,cAAc,EAAE,YAAY,EAAE,CAAC;IAC/B,aAAa,EAAE,YAAY,EAAE,CAAC;IAC9B,UAAU,EAAE,OAAO,CAAC;CACrB;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,aAAa,EAAE,EAAE,EAAE,aAAa,GAAG,cAAc,CAiHpF;AAED,wBAAgB,cAAc,CAAC,QAAQ,EAAE,cAAc,GAAG,MAAM,CAyF/D"}
@@ -0,0 +1,189 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.analyzeImpact = analyzeImpact;
4
+ exports.renderMarkdown = renderMarkdown;
5
+ function analyzeImpact(base, pr) {
6
+ const chainChanges = [];
7
+ const allChainIds = new Set([...Object.keys(base), ...Object.keys(pr)]);
8
+ for (const chainId of allChainIds) {
9
+ const baseChain = base[chainId];
10
+ const prChain = pr[chainId];
11
+ if (!baseChain && prChain) {
12
+ chainChanges.push({
13
+ chainId,
14
+ chainName: prChain.name,
15
+ type: 'added',
16
+ layersBefore: [],
17
+ layersAfter: [...prChain.settlementLayers],
18
+ layersAdded: [...prChain.settlementLayers],
19
+ layersRemoved: [],
20
+ });
21
+ }
22
+ else if (baseChain && !prChain) {
23
+ chainChanges.push({
24
+ chainId,
25
+ chainName: baseChain.name,
26
+ type: 'removed',
27
+ layersBefore: [...baseChain.settlementLayers],
28
+ layersAfter: [],
29
+ layersAdded: [],
30
+ layersRemoved: [...baseChain.settlementLayers],
31
+ });
32
+ }
33
+ else if (baseChain && prChain) {
34
+ const baseLayers = [...baseChain.settlementLayers].sort();
35
+ const prLayers = [...prChain.settlementLayers].sort();
36
+ if (baseLayers.join(',') !== prLayers.join(',')) {
37
+ const layersAdded = prChain.settlementLayers.filter((l) => !baseChain.settlementLayers.includes(l));
38
+ const layersRemoved = baseChain.settlementLayers.filter((l) => !prChain.settlementLayers.includes(l));
39
+ chainChanges.push({
40
+ chainId,
41
+ chainName: prChain.name,
42
+ type: 'modified',
43
+ layersBefore: [...baseChain.settlementLayers],
44
+ layersAfter: [...prChain.settlementLayers],
45
+ layersAdded,
46
+ layersRemoved,
47
+ });
48
+ }
49
+ }
50
+ }
51
+ // Compute per-layer summaries
52
+ const allLayers = new Set();
53
+ for (const chain of Object.values(base)) {
54
+ for (const layer of chain.settlementLayers) {
55
+ allLayers.add(layer);
56
+ }
57
+ }
58
+ for (const chain of Object.values(pr)) {
59
+ for (const layer of chain.settlementLayers) {
60
+ allLayers.add(layer);
61
+ }
62
+ }
63
+ const layerSummaries = [];
64
+ for (const layer of [...allLayers].sort()) {
65
+ const baseChainsWithLayer = [];
66
+ for (const chain of Object.values(base)) {
67
+ if (chain.settlementLayers.includes(layer)) {
68
+ baseChainsWithLayer.push(chain.name);
69
+ }
70
+ }
71
+ const prChainsWithLayer = [];
72
+ for (const chain of Object.values(pr)) {
73
+ if (chain.settlementLayers.includes(layer)) {
74
+ prChainsWithLayer.push(chain.name);
75
+ }
76
+ }
77
+ const chainsAdded = prChainsWithLayer.filter((name) => !baseChainsWithLayer.includes(name));
78
+ const chainsRemoved = baseChainsWithLayer.filter((name) => !prChainsWithLayer.includes(name));
79
+ layerSummaries.push({
80
+ layer,
81
+ chainsBefore: baseChainsWithLayer.length,
82
+ chainsAfter: prChainsWithLayer.length,
83
+ chainsAdded,
84
+ chainsRemoved,
85
+ });
86
+ }
87
+ // Identify fragile chains in PR config
88
+ const fragileChains = [];
89
+ for (const [chainId, chain] of Object.entries(pr)) {
90
+ if (chain.settlementLayers.length === 1) {
91
+ fragileChains.push({
92
+ chainId,
93
+ chainName: chain.name,
94
+ singleLayer: chain.settlementLayers[0],
95
+ });
96
+ }
97
+ }
98
+ return {
99
+ chainChanges,
100
+ layerSummaries,
101
+ fragileChains,
102
+ hasChanges: chainChanges.length > 0,
103
+ };
104
+ }
105
+ function renderMarkdown(analysis) {
106
+ if (!analysis.hasChanges) {
107
+ return '## Config Impact Analysis\n\nNo config changes detected.';
108
+ }
109
+ const sections = ['## Config Impact Analysis'];
110
+ // Chain Changes table
111
+ if (analysis.chainChanges.length > 0) {
112
+ const lines = [
113
+ '',
114
+ '### Chain Changes',
115
+ '',
116
+ '| Chain | Type | Settlement Layers | Change |',
117
+ '|-------|------|-------------------|--------|',
118
+ ];
119
+ for (const change of analysis.chainChanges) {
120
+ const icon = change.type === 'added' ? '\u{1F7E2}' : change.type === 'removed' ? '\u{1F534}' : '\u{1F7E1}';
121
+ const typeLabel = `${icon} ${change.type}`;
122
+ let layers;
123
+ let changeDetail;
124
+ if (change.type === 'added') {
125
+ layers = change.layersAfter.join(', ');
126
+ changeDetail = `Added with ${change.layersAfter.join(', ')}`;
127
+ }
128
+ else if (change.type === 'removed') {
129
+ layers = `~~${change.layersBefore.join(', ')}~~`;
130
+ changeDetail = `Removed (was ${change.layersBefore.join(', ')})`;
131
+ }
132
+ else {
133
+ layers = `${change.layersBefore.join(', ')} \u2192 ${change.layersAfter.join(', ')}`;
134
+ const parts = [];
135
+ if (change.layersAdded.length > 0) {
136
+ parts.push(`+${change.layersAdded.join(', ')}`);
137
+ }
138
+ if (change.layersRemoved.length > 0) {
139
+ parts.push(`-${change.layersRemoved.join(', ')}`);
140
+ }
141
+ changeDetail = parts.join(', ');
142
+ }
143
+ lines.push(`| ${change.chainName} (${change.chainId}) | ${typeLabel} | ${layers} | ${changeDetail} |`);
144
+ }
145
+ sections.push(lines.join('\n'));
146
+ }
147
+ // Settlement Layer Impact table
148
+ const layersWithChanges = analysis.layerSummaries.filter((s) => s.chainsAdded.length > 0 || s.chainsRemoved.length > 0);
149
+ if (layersWithChanges.length > 0) {
150
+ const lines = [
151
+ '',
152
+ '### Settlement Layer Impact',
153
+ '',
154
+ '| Layer | Chains Before | Chains After | Added | Removed |',
155
+ '|-------|---------------|--------------|-------|---------|',
156
+ ];
157
+ for (const summary of layersWithChanges) {
158
+ const added = summary.chainsAdded.length > 0 ? summary.chainsAdded.join(', ') : '-';
159
+ const removed = summary.chainsRemoved.length > 0 ? summary.chainsRemoved.join(', ') : '-';
160
+ lines.push(`| ${summary.layer} | ${summary.chainsBefore} | ${summary.chainsAfter} | ${added} | ${removed} |`);
161
+ }
162
+ sections.push(lines.join('\n'));
163
+ }
164
+ // Fragile Chains section
165
+ if (analysis.fragileChains.length > 0) {
166
+ const lines = [
167
+ '',
168
+ '### Fragile Chains',
169
+ '',
170
+ 'These chains have only a single settlement layer:',
171
+ '',
172
+ ];
173
+ for (const chain of analysis.fragileChains) {
174
+ lines.push(`- **${chain.chainName} (${chain.chainId})**: ${chain.singleLayer} only`);
175
+ }
176
+ sections.push(lines.join('\n'));
177
+ }
178
+ return sections.join('\n');
179
+ }
180
+ // CLI entry point — runs when invoked directly via `bun run scripts/impact-analysis.ts`
181
+ const cliArgs = process.argv.slice(2);
182
+ if (cliArgs.length >= 2) {
183
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
184
+ const fs = require("fs");
185
+ const base = JSON.parse(fs.readFileSync(cliArgs[0], 'utf8'));
186
+ const pr = JSON.parse(fs.readFileSync(cliArgs[1], 'utf8'));
187
+ const analysis = analyzeImpact(base, pr);
188
+ console.log(renderMarkdown(analysis));
189
+ }
@@ -0,0 +1,38 @@
1
+ export type SettlementLayer = 'ACROSS' | 'ECO' | 'RELAY' | 'OFT';
2
+ export interface ChainData {
3
+ name: string;
4
+ settlementLayers: SettlementLayer[];
5
+ tokens: Array<{
6
+ symbol: string;
7
+ [key: string]: unknown;
8
+ }>;
9
+ [key: string]: unknown;
10
+ }
11
+ export type ChainRegistry = Record<string, ChainData>;
12
+ export type ValidationSeverity = 'error' | 'warning';
13
+ export interface ValidationIssue {
14
+ severity: ValidationSeverity;
15
+ chainId: string;
16
+ chainName: string;
17
+ message: string;
18
+ }
19
+ export interface ValidationResult {
20
+ issues: ValidationIssue[];
21
+ hasErrors: boolean;
22
+ }
23
+ /**
24
+ * Validates a chain registry for configuration problems.
25
+ *
26
+ * Checks performed:
27
+ * - Orphaned chains: chains with no settlement layers (error)
28
+ * - Single-layer fragility: chains with exactly one settlement layer (warning)
29
+ */
30
+ export declare function validateChainConfig(chains: ChainRegistry): ValidationResult;
31
+ /**
32
+ * Formats validation issues for console output.
33
+ *
34
+ * Errors are prefixed with a cross mark, warnings with a warning sign.
35
+ * Returns an empty string when there are no issues.
36
+ */
37
+ export declare function formatValidationIssues(result: ValidationResult): string;
38
+ //# sourceMappingURL=validation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../../scripts/validation.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,eAAe,GAAG,QAAQ,GAAG,KAAK,GAAG,OAAO,GAAG,KAAK,CAAC;AAEjE,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,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,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;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,aAAa,GAAG,gBAAgB,CA4B3E;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,gBAAgB,GAAG,MAAM,CAWvE"}
@@ -0,0 +1,55 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.validateChainConfig = validateChainConfig;
4
+ exports.formatValidationIssues = formatValidationIssues;
5
+ /**
6
+ * Validates a chain registry for configuration problems.
7
+ *
8
+ * Checks performed:
9
+ * - Orphaned chains: chains with no settlement layers (error)
10
+ * - Single-layer fragility: chains with exactly one settlement layer (warning)
11
+ */
12
+ function validateChainConfig(chains) {
13
+ const issues = [];
14
+ for (const [chainId, chain] of Object.entries(chains)) {
15
+ const layers = chain.settlementLayers;
16
+ if (layers.length === 0) {
17
+ issues.push({
18
+ severity: 'error',
19
+ chainId,
20
+ chainName: chain.name,
21
+ message: 'no settlement layers \u2014 chain is unreachable',
22
+ });
23
+ }
24
+ else if (layers.length === 1) {
25
+ const layer = layers[0];
26
+ issues.push({
27
+ severity: 'warning',
28
+ chainId,
29
+ chainName: chain.name,
30
+ message: `single settlement layer [${layer}] \u2014 disabling ${layer} removes this chain entirely`,
31
+ });
32
+ }
33
+ }
34
+ return {
35
+ issues,
36
+ hasErrors: issues.some((issue) => issue.severity === 'error'),
37
+ };
38
+ }
39
+ /**
40
+ * Formats validation issues for console output.
41
+ *
42
+ * Errors are prefixed with a cross mark, warnings with a warning sign.
43
+ * Returns an empty string when there are no issues.
44
+ */
45
+ function formatValidationIssues(result) {
46
+ if (result.issues.length === 0) {
47
+ return '';
48
+ }
49
+ return result.issues
50
+ .map((issue) => {
51
+ const prefix = issue.severity === 'error' ? '\u2717' : '\u26A0';
52
+ return `${prefix} ${issue.chainName} (${issue.chainId}): ${issue.message}`;
53
+ })
54
+ .join('\n');
55
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rhinestone/shared-configs",
3
- "version": "1.4.132",
3
+ "version": "1.4.133",
4
4
  "description": "Reusable configuration files for Rhinestone services",
5
5
  "author": {
6
6
  "name": "Rhinestone",
@@ -23,6 +23,7 @@
23
23
  "generate:abis:extract": "bun run scripts/generate-abis.ts --extract",
24
24
  "sync:abis": "bun run scripts/sync-abis.ts",
25
25
  "compile": "tsc",
26
+ "test": "bun test",
26
27
  "build": "bun run clean && bun run generate && bun run sync:abis --generate && bun run compile",
27
28
  "changeset:release": "bun run build && changeset publish"
28
29
  },