@rhinestone/shared-configs 1.16.0 → 1.17.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.
@@ -15,6 +15,22 @@
15
15
  },
16
16
  "network": "mainnet",
17
17
  "publicRpcUrl": "https://eth.merkle.io",
18
+ "settlementConfig": {
19
+ "cctp": {
20
+ "domain": 0
21
+ },
22
+ "near": {
23
+ "prefix": "eth"
24
+ },
25
+ "oft": {
26
+ "adapter": "0x6c96de32cea08842dcc4058c14d3aaad7fa41dee",
27
+ "layerZeroEid": 30101
28
+ },
29
+ "rhino": {
30
+ "bridgeContract": "0xbca3039a18c0d2f2f84ba8a028c67290bc045afa",
31
+ "chainName": "ETHEREUM"
32
+ }
33
+ },
18
34
  "settlementLayers": [
19
35
  "ACROSS",
20
36
  "ECO",
@@ -79,7 +95,8 @@
79
95
  "priceSymbol": "USDT",
80
96
  "settlementLayers": [
81
97
  "ACROSS",
82
- "ECO"
98
+ "ECO",
99
+ "OFT"
83
100
  ],
84
101
  "symbol": "USDT"
85
102
  }
@@ -107,6 +124,22 @@
107
124
  },
108
125
  "network": "mainnet",
109
126
  "publicRpcUrl": "https://mainnet.optimism.io",
127
+ "settlementConfig": {
128
+ "cctp": {
129
+ "domain": 2
130
+ },
131
+ "near": {
132
+ "prefix": "op"
133
+ },
134
+ "oft": {
135
+ "adapter": "0xF03b4d9AC1D5d1E7c4cEf54C2A313b9fe051A0aD",
136
+ "layerZeroEid": 30111
137
+ },
138
+ "rhino": {
139
+ "bridgeContract": "0x0bca65bf4b4c8803d2f0b49353ed57caaf3d66dc",
140
+ "chainName": "OPTIMISM"
141
+ }
142
+ },
110
143
  "settlementLayers": [
111
144
  "ACROSS",
112
145
  "ECO",
@@ -170,7 +203,8 @@
170
203
  "priceSymbol": "USDT",
171
204
  "settlementLayers": [
172
205
  "ACROSS",
173
- "ECO"
206
+ "ECO",
207
+ "OFT"
174
208
  ],
175
209
  "symbol": "USDT0"
176
210
  },
@@ -210,10 +244,23 @@
210
244
  },
211
245
  "network": "mainnet",
212
246
  "publicRpcUrl": "https://56.rpc.thirdweb.com",
247
+ "settlementConfig": {
248
+ "cctp": {
249
+ "domain": 17
250
+ },
251
+ "near": {
252
+ "prefix": "bsc"
253
+ },
254
+ "rhino": {
255
+ "bridgeContract": "0xb80a582fa430645a043bb4f6135321ee01005fef",
256
+ "chainName": "BINANCE"
257
+ }
258
+ },
213
259
  "settlementLayers": [
214
260
  "ACROSS",
215
261
  "ECO",
216
262
  "RELAY",
263
+ "NEAR",
217
264
  "RHINO",
218
265
  "CCTP"
219
266
  ],
@@ -286,6 +333,15 @@
286
333
  },
287
334
  "network": "mainnet",
288
335
  "publicRpcUrl": "https://rpc.gnosischain.com",
336
+ "settlementConfig": {
337
+ "near": {
338
+ "prefix": "gnosis"
339
+ },
340
+ "rhino": {
341
+ "bridgeContract": "0x5e023c31e1d3dcd08a1b3e8c96f6ef8aa8fcacd1",
342
+ "chainName": "GNOSIS"
343
+ }
344
+ },
289
345
  "settlementLayers": [
290
346
  "RELAY",
291
347
  "NEAR",
@@ -329,6 +385,19 @@
329
385
  },
330
386
  "network": "mainnet",
331
387
  "publicRpcUrl": "https://mainnet.unichain.org",
388
+ "settlementConfig": {
389
+ "cctp": {
390
+ "domain": 10
391
+ },
392
+ "oft": {
393
+ "adapter": "0xc07bE8994D035631c36fb4a89C918CeFB2f03EC3",
394
+ "layerZeroEid": 30320
395
+ },
396
+ "rhino": {
397
+ "bridgeContract": "0xb0d7040b93fe5b099d3ce02ea86c4a1b695732d0",
398
+ "chainName": "UNICHAIN"
399
+ }
400
+ },
332
401
  "settlementLayers": [
333
402
  "ACROSS",
334
403
  "ECO",
@@ -393,7 +462,8 @@
393
462
  "priceSymbol": "USDT",
394
463
  "settlementLayers": [
395
464
  "ACROSS",
396
- "ECO"
465
+ "ECO",
466
+ "OFT"
397
467
  ],
398
468
  "symbol": "USDT0"
399
469
  }
@@ -421,6 +491,22 @@
421
491
  },
422
492
  "network": "mainnet",
423
493
  "publicRpcUrl": "https://polygon.drpc.org",
494
+ "settlementConfig": {
495
+ "cctp": {
496
+ "domain": 7
497
+ },
498
+ "near": {
499
+ "prefix": "pol"
500
+ },
501
+ "oft": {
502
+ "adapter": "0x6BA10300f0DC58B7a1e4c0e41f5daBb7D7829e13",
503
+ "layerZeroEid": 30109
504
+ },
505
+ "rhino": {
506
+ "bridgeContract": "0xBA4EEE20F434bC3908A0B18DA496348657133A7E",
507
+ "chainName": "MATIC_POS"
508
+ }
509
+ },
424
510
  "settlementLayers": [
425
511
  "ACROSS",
426
512
  "ECO",
@@ -470,9 +556,11 @@
470
556
  "decimals": 6,
471
557
  "pegGroup": "USD",
472
558
  "priceSymbol": "USDT",
559
+ "rhinoSymbol": "USDT",
473
560
  "settlementLayers": [
474
561
  "ACROSS",
475
- "ECO"
562
+ "ECO",
563
+ "OFT"
476
564
  ],
477
565
  "symbol": "USDT0"
478
566
  }
@@ -500,6 +588,11 @@
500
588
  },
501
589
  "network": "mainnet",
502
590
  "publicRpcUrl": "https://rpc.monad.xyz",
591
+ "settlementConfig": {
592
+ "cctp": {
593
+ "domain": 15
594
+ }
595
+ },
503
596
  "settlementLayers": [
504
597
  "ACROSS",
505
598
  "RELAY",
@@ -577,6 +670,15 @@
577
670
  },
578
671
  "network": "mainnet",
579
672
  "publicRpcUrl": "https://rpc.soniclabs.com",
673
+ "settlementConfig": {
674
+ "cctp": {
675
+ "domain": 13
676
+ },
677
+ "rhino": {
678
+ "bridgeContract": "0x5e023c31e1d3dcd08a1b3e8c96f6ef8aa8fcacd1",
679
+ "chainName": "SONIC"
680
+ }
681
+ },
580
682
  "settlementLayers": [
581
683
  "ECO",
582
684
  "RELAY",
@@ -626,10 +728,20 @@
626
728
  },
627
729
  "network": "mainnet",
628
730
  "publicRpcUrl": "https://rpc.hyperliquid.xyz/evm",
731
+ "settlementConfig": {
732
+ "cctp": {
733
+ "domain": 19
734
+ },
735
+ "rhino": {
736
+ "bridgeContract": "0x5e023c31e1d3dcd08a1b3e8c96f6ef8aa8fcacd1",
737
+ "chainName": "HYPEREVM"
738
+ }
739
+ },
629
740
  "settlementLayers": [
630
741
  "ACROSS",
631
742
  "ECO",
632
743
  "RELAY",
744
+ "RHINO",
633
745
  "CCTP"
634
746
  ],
635
747
  "stack": "vanilla",
@@ -656,6 +768,7 @@
656
768
  "decimals": 6,
657
769
  "pegGroup": "USD",
658
770
  "priceSymbol": "USDT",
771
+ "rhinoSymbol": "USDT",
659
772
  "settlementLayers": [
660
773
  "ACROSS",
661
774
  "ECO"
@@ -867,6 +980,18 @@
867
980
  },
868
981
  "network": "mainnet",
869
982
  "publicRpcUrl": "https://mainnet.base.org",
983
+ "settlementConfig": {
984
+ "cctp": {
985
+ "domain": 6
986
+ },
987
+ "near": {
988
+ "prefix": "base"
989
+ },
990
+ "rhino": {
991
+ "bridgeContract": "0x2f59e9086ec8130e21bd052065a9e6b2497bb102",
992
+ "chainName": "BASE"
993
+ }
994
+ },
870
995
  "settlementLayers": [
871
996
  "ACROSS",
872
997
  "ECO",
@@ -944,6 +1069,16 @@
944
1069
  },
945
1070
  "network": "mainnet",
946
1071
  "publicRpcUrl": "https://rpc.plasma.to",
1072
+ "settlementConfig": {
1073
+ "oft": {
1074
+ "adapter": "0x02ca37966753bDdDf11216B73B16C1dE756A7CF9",
1075
+ "layerZeroEid": 30383
1076
+ },
1077
+ "rhino": {
1078
+ "bridgeContract": "0x5e023c31e1d3dcd08a1b3e8c96f6ef8aa8fcacd1",
1079
+ "chainName": "PLASMA"
1080
+ }
1081
+ },
947
1082
  "settlementLayers": [
948
1083
  "ACROSS",
949
1084
  "ECO",
@@ -965,9 +1100,11 @@
965
1100
  "decimals": 6,
966
1101
  "pegGroup": "USD",
967
1102
  "priceSymbol": "USDT",
1103
+ "rhinoSymbol": "USDT",
968
1104
  "settlementLayers": [
969
1105
  "ACROSS",
970
- "ECO"
1106
+ "ECO",
1107
+ "OFT"
971
1108
  ],
972
1109
  "symbol": "USDT0"
973
1110
  }
@@ -1048,6 +1185,22 @@
1048
1185
  },
1049
1186
  "network": "mainnet",
1050
1187
  "publicRpcUrl": "https://arb1.arbitrum.io/rpc",
1188
+ "settlementConfig": {
1189
+ "cctp": {
1190
+ "domain": 3
1191
+ },
1192
+ "near": {
1193
+ "prefix": "arb"
1194
+ },
1195
+ "oft": {
1196
+ "adapter": "0x14E4A1B13bf7F943c8ff7C51fb60FA964A298D92",
1197
+ "layerZeroEid": 30110
1198
+ },
1199
+ "rhino": {
1200
+ "bridgeContract": "0x10417734001162Ea139e8b044DFe28DbB8B28ad0",
1201
+ "chainName": "ARBITRUM"
1202
+ }
1203
+ },
1051
1204
  "settlementLayers": [
1052
1205
  "ACROSS",
1053
1206
  "ECO",
@@ -1110,9 +1263,11 @@
1110
1263
  "decimals": 6,
1111
1264
  "pegGroup": "USD",
1112
1265
  "priceSymbol": "USDT",
1266
+ "rhinoSymbol": "USDT",
1113
1267
  "settlementLayers": [
1114
1268
  "ACROSS",
1115
- "ECO"
1269
+ "ECO",
1270
+ "OFT"
1116
1271
  ],
1117
1272
  "symbol": "USDT0"
1118
1273
  }
@@ -1140,6 +1295,18 @@
1140
1295
  },
1141
1296
  "network": "mainnet",
1142
1297
  "publicRpcUrl": "https://api.avax.network/ext/bc/C/rpc",
1298
+ "settlementConfig": {
1299
+ "cctp": {
1300
+ "domain": 1
1301
+ },
1302
+ "near": {
1303
+ "prefix": "avax"
1304
+ },
1305
+ "rhino": {
1306
+ "bridgeContract": "0x5e023c31e1d3dcd08a1b3e8c96f6ef8aa8fcacd1",
1307
+ "chainName": "AVALANCHE"
1308
+ }
1309
+ },
1143
1310
  "settlementLayers": [
1144
1311
  "ACROSS",
1145
1312
  "RELAY",
@@ -1225,6 +1392,11 @@
1225
1392
  },
1226
1393
  "network": "testnet",
1227
1394
  "publicRpcUrl": "https://sepolia.base.org",
1395
+ "settlementConfig": {
1396
+ "cctp": {
1397
+ "domain": 6
1398
+ }
1399
+ },
1228
1400
  "settlementLayers": [
1229
1401
  "ACROSS",
1230
1402
  "ECO",
@@ -1308,6 +1480,11 @@
1308
1480
  },
1309
1481
  "network": "testnet",
1310
1482
  "publicRpcUrl": "https://sepolia-rollup.arbitrum.io/rpc",
1483
+ "settlementConfig": {
1484
+ "cctp": {
1485
+ "domain": 3
1486
+ }
1487
+ },
1311
1488
  "settlementLayers": [
1312
1489
  "ACROSS",
1313
1490
  "ECO",
@@ -1377,6 +1554,12 @@
1377
1554
  },
1378
1555
  "network": "mainnet",
1379
1556
  "publicRpcUrl": "https://rpc.katana.network",
1557
+ "settlementConfig": {
1558
+ "rhino": {
1559
+ "bridgeContract": "0x04317f0e4795b1e1bab333234153fa10aaac79e9",
1560
+ "chainName": "KATANA"
1561
+ }
1562
+ },
1380
1563
  "settlementLayers": [
1381
1564
  "RELAY",
1382
1565
  "RHINO"
@@ -1523,6 +1706,11 @@
1523
1706
  },
1524
1707
  "network": "testnet",
1525
1708
  "publicRpcUrl": "https://11155111.rpc.thirdweb.com",
1709
+ "settlementConfig": {
1710
+ "cctp": {
1711
+ "domain": 0
1712
+ }
1713
+ },
1526
1714
  "settlementLayers": [
1527
1715
  "ACROSS",
1528
1716
  "ECO",
@@ -1602,6 +1790,11 @@
1602
1790
  },
1603
1791
  "network": "testnet",
1604
1792
  "publicRpcUrl": "https://sepolia.optimism.io",
1793
+ "settlementConfig": {
1794
+ "cctp": {
1795
+ "domain": 2
1796
+ }
1797
+ },
1605
1798
  "settlementLayers": [
1606
1799
  "ACROSS",
1607
1800
  "ECO",
@@ -1670,6 +1863,18 @@
1670
1863
  "symbol": "TRX"
1671
1864
  },
1672
1865
  "network": "mainnet",
1866
+ "settlementConfig": {
1867
+ "near": {
1868
+ "prefix": "tron"
1869
+ },
1870
+ "oft": {
1871
+ "adapter": "TFG4wBaDQ8sHWWP1ACeSGnoNR6RRzevLPt",
1872
+ "layerZeroEid": 30420
1873
+ },
1874
+ "rhino": {
1875
+ "chainName": "TRON"
1876
+ }
1877
+ },
1673
1878
  "settlementLayers": [
1674
1879
  "RELAY",
1675
1880
  "OFT",
@@ -1684,6 +1889,7 @@
1684
1889
  "approvalSlot": null,
1685
1890
  "balanceSlot": null,
1686
1891
  "decimals": 6,
1892
+ "nearAssetId": "nep141:tron.omft.near",
1687
1893
  "symbol": "TRX"
1688
1894
  },
1689
1895
  {
@@ -1691,7 +1897,11 @@
1691
1897
  "approvalSlot": null,
1692
1898
  "balanceSlot": null,
1693
1899
  "decimals": 6,
1900
+ "nearAssetId": "nep141:tron-d28a265909efecdcee7c5028585214ea0b96f015.omft.near",
1694
1901
  "pegGroup": "USD",
1902
+ "settlementLayers": [
1903
+ "OFT"
1904
+ ],
1695
1905
  "symbol": "USDT"
1696
1906
  }
1697
1907
  ],
@@ -1717,6 +1927,21 @@
1717
1927
  "symbol": "SOL"
1718
1928
  },
1719
1929
  "network": "mainnet",
1930
+ "settlementConfig": {
1931
+ "cctp": {
1932
+ "domain": 5
1933
+ },
1934
+ "near": {
1935
+ "prefix": "sol"
1936
+ },
1937
+ "oft": {
1938
+ "adapter": "HyXJcgYpURfDhgzuyRL7zxP4FhLg7LZQMeDrR4MXZcMN",
1939
+ "layerZeroEid": 30168
1940
+ },
1941
+ "rhino": {
1942
+ "chainName": "SOLANA"
1943
+ }
1944
+ },
1720
1945
  "settlementLayers": [
1721
1946
  "RELAY",
1722
1947
  "OFT",
@@ -1732,6 +1957,7 @@
1732
1957
  "approvalSlot": null,
1733
1958
  "balanceSlot": null,
1734
1959
  "decimals": 9,
1960
+ "nearAssetId": "nep141:sol.omft.near",
1735
1961
  "symbol": "SOL"
1736
1962
  },
1737
1963
  {
@@ -1739,6 +1965,7 @@
1739
1965
  "approvalSlot": null,
1740
1966
  "balanceSlot": null,
1741
1967
  "decimals": 9,
1968
+ "nearAssetId": "nep141:sol.omft.near",
1742
1969
  "priceSymbol": "SOL",
1743
1970
  "symbol": "WSOL"
1744
1971
  },
@@ -1747,6 +1974,7 @@
1747
1974
  "approvalSlot": null,
1748
1975
  "balanceSlot": null,
1749
1976
  "decimals": 6,
1977
+ "nearAssetId": "nep141:sol-5ce3bf3a31af18be40ba30f721101b4341690186.omft.near",
1750
1978
  "pegGroup": "USD",
1751
1979
  "symbol": "USDC"
1752
1980
  },
@@ -1755,8 +1983,12 @@
1755
1983
  "approvalSlot": null,
1756
1984
  "balanceSlot": null,
1757
1985
  "decimals": 6,
1986
+ "nearAssetId": "nep141:sol-c800a4bd850783ccb82c2b2c7e84175443606352.omft.near",
1758
1987
  "pegGroup": "USD",
1759
1988
  "priceSymbol": "USDT",
1989
+ "settlementLayers": [
1990
+ "OFT"
1991
+ ],
1760
1992
  "symbol": "USDT"
1761
1993
  }
1762
1994
  ],
@@ -1,8 +1,12 @@
1
1
  "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
2
5
  Object.defineProperty(exports, "__esModule", { value: true });
3
6
  const bun_test_1 = require("bun:test");
4
7
  const validation_1 = require("../validation");
5
8
  const index_1 = require("../../src/index");
9
+ const oft_json_1 = __importDefault(require("../../configs/oft.json"));
6
10
  const nativeToken = { address: "0x0", symbol: "ETH", decimals: 18 };
7
11
  /** A structurally complete entry, so semantic tests don't trip structural checks. */
8
12
  function validChain(overrides = {}) {
@@ -147,6 +151,119 @@ function errorsFor(chain) {
147
151
  (0, bun_test_1.expect)(result.hasErrors).toBe(false);
148
152
  });
149
153
  });
154
+ // These replace the "must match <map> in the orchestrator" comments the jsonnet
155
+ // used to carry over four flat chain-id allowlists. A layer advertised without
156
+ // the identifiers that address it is a route offered and then not taken, so the
157
+ // build is the only place left to catch it.
158
+ (0, bun_test_1.describe)("validateChainConfig — settlementConfig", () => {
159
+ const withLayer = (layer, config) => validChain({
160
+ settlementLayers: ["RELAY", layer],
161
+ settlementConfig: config,
162
+ });
163
+ bun_test_1.it.each([
164
+ ["RHINO", "rhino", { chainName: "ETHEREUM" }],
165
+ ["CCTP", "cctp", { domain: 0 }],
166
+ ["NEAR", "near", { prefix: "eth" }],
167
+ ["OFT", "oft", { adapter: "0xadapter", layerZeroEid: 30101 }],
168
+ ])("accepts %s with its payload", (layer, key, payload) => {
169
+ (0, bun_test_1.expect)(errorsFor(withLayer(layer, { [key]: payload }))).toEqual([]);
170
+ });
171
+ bun_test_1.it.each([
172
+ ["RHINO", "rhino"],
173
+ ["CCTP", "cctp"],
174
+ ["NEAR", "near"],
175
+ ["OFT", "oft"],
176
+ ])("rejects %s declared with no payload", (layer, key) => {
177
+ const errors = errorsFor(withLayer(layer, {}));
178
+ (0, bun_test_1.expect)(errors).toHaveLength(1);
179
+ (0, bun_test_1.expect)(errors[0].message).toContain(`settlementConfig.${key} is missing`);
180
+ });
181
+ bun_test_1.it.each([
182
+ ["RHINO", { rhino: {} }, "rhino.chainName"],
183
+ ["CCTP", { cctp: { domain: "0" } }, "cctp.domain"],
184
+ ["NEAR", { near: { prefix: "" } }, "near.prefix"],
185
+ ["OFT", { oft: { adapter: "0xa" } }, "oft.layerZeroEid"],
186
+ ])("rejects %s whose payload is incomplete", (layer, config, field) => {
187
+ const errors = errorsFor(withLayer(layer, config));
188
+ (0, bun_test_1.expect)(errors.some((e) => e.message.includes(field))).toBe(true);
189
+ });
190
+ // Domain 0 is Ethereum, so a truthiness check would drop a real route.
191
+ (0, bun_test_1.it)("accepts CCTP domain 0", () => {
192
+ (0, bun_test_1.expect)(errorsFor(withLayer("CCTP", { cctp: { domain: 0 } }))).toEqual([]);
193
+ });
194
+ // Absent for a destination-only chain, which nothing originates from.
195
+ (0, bun_test_1.it)("accepts a rhino entry with no bridgeContract", () => {
196
+ (0, bun_test_1.expect)(errorsFor(withLayer("RHINO", { rhino: { chainName: "SOLANA" } }))).toEqual([]);
197
+ });
198
+ (0, bun_test_1.it)("rejects a payload for a layer that is not enabled", () => {
199
+ const errors = errorsFor(validChain({ settlementConfig: { cctp: { domain: 6 } } }));
200
+ (0, bun_test_1.expect)(errors).toHaveLength(1);
201
+ (0, bun_test_1.expect)(errors[0].message).toContain("CCTP is not in settlementLayers");
202
+ });
203
+ (0, bun_test_1.it)("rejects an unknown layer key", () => {
204
+ const errors = errorsFor(validChain({ settlementConfig: { hop: {} } }));
205
+ (0, bun_test_1.expect)(errors).toHaveLength(1);
206
+ (0, bun_test_1.expect)(errors[0].message).toContain("unknown layer");
207
+ });
208
+ // 1Click's non-EVM ids are NEAR-internal; deriving one produces a string it
209
+ // rejects, so a missing id throws mid-plan rather than degrading.
210
+ (0, bun_test_1.it)("rejects a non-EVM NEAR chain whose token has no nearAssetId", () => {
211
+ const errors = errorsFor(validChain({
212
+ vmType: "svm",
213
+ settlementLayers: ["RELAY", "NEAR"],
214
+ settlementConfig: { near: { prefix: "sol" } },
215
+ tokens: [{ ...nativeToken, balanceSlot: null, approvalSlot: null }],
216
+ }));
217
+ (0, bun_test_1.expect)(errors).toHaveLength(1);
218
+ (0, bun_test_1.expect)(errors[0].message).toContain("nearAssetId");
219
+ });
220
+ (0, bun_test_1.it)("does not require nearAssetId on an EVM NEAR chain", () => {
221
+ (0, bun_test_1.expect)(errorsFor(validChain({
222
+ settlementLayers: ["RELAY", "NEAR"],
223
+ settlementConfig: { near: { prefix: "eth" } },
224
+ }))).toEqual([]);
225
+ });
226
+ });
227
+ // The chain block states OFT membership and reads its addresses back out of
228
+ // config/oft.jsonnet, so the one way the two can disagree is an adapter added
229
+ // there whose chain block was never given an `oft` entry — invisible in a diff
230
+ // of either file alone.
231
+ (0, bun_test_1.describe)("validateOftCoverage", () => {
232
+ (0, bun_test_1.it)("accepts the real generated registry", () => {
233
+ (0, bun_test_1.expect)((0, validation_1.validateOftCoverage)(index_1.chainRegistry, oft_json_1.default)).toEqual([]);
234
+ });
235
+ (0, bun_test_1.it)("reports a chain with an adapter but no OFT layer", () => {
236
+ const issues = (0, validation_1.validateOftCoverage)({ "1": validChain() }, {
237
+ USDT0: { adapters: { "1": "0xadapter" } },
238
+ });
239
+ (0, bun_test_1.expect)(issues).toHaveLength(1);
240
+ (0, bun_test_1.expect)(issues[0].message).toContain("declares no OFT layer");
241
+ });
242
+ (0, bun_test_1.it)("reports an adapter the chain entry disagrees with", () => {
243
+ const chain = validChain({
244
+ settlementLayers: ["RELAY", "OFT"],
245
+ settlementConfig: { oft: { adapter: "0xstale", layerZeroEid: 30101 } },
246
+ });
247
+ const issues = (0, validation_1.validateOftCoverage)({ "1": chain }, {
248
+ USDT0: { adapters: { "1": "0xadapter" } },
249
+ });
250
+ (0, bun_test_1.expect)(issues).toHaveLength(1);
251
+ (0, bun_test_1.expect)(issues[0].message).toContain("does not match");
252
+ });
253
+ // The registry is authored in checksummed hex; the chain block is not.
254
+ (0, bun_test_1.it)("compares adapters case-insensitively", () => {
255
+ const chain = validChain({
256
+ settlementLayers: ["RELAY", "OFT"],
257
+ settlementConfig: { oft: { adapter: "0xABC", layerZeroEid: 30101 } },
258
+ });
259
+ (0, bun_test_1.expect)((0, validation_1.validateOftCoverage)({ "1": chain }, {
260
+ USDT0: { adapters: { "1": "0xabc" } },
261
+ })).toEqual([]);
262
+ });
263
+ (0, bun_test_1.it)("ignores an OFT chain that is not in the registry", () => {
264
+ (0, bun_test_1.expect)((0, validation_1.validateOftCoverage)({}, { USDT0: { adapters: { "1": "0xa" } } })).toEqual([]);
265
+ });
266
+ });
150
267
  (0, bun_test_1.describe)("formatValidationIssues", () => {
151
268
  (0, bun_test_1.it)("returns empty string for no issues", () => {
152
269
  (0, bun_test_1.expect)((0, validation_1.formatValidationIssues)({ issues: [], hasErrors: false })).toBe("");
@@ -89,7 +89,7 @@ function renderChains(chainRegistry, mainnets, testnets) {
89
89
  }
90
90
  return [
91
91
  "// Auto-generated by scripts/generate.ts. Do not edit manually.",
92
- 'import type { ChainNetwork, ChainStack, PegGroup, ProviderName, SettlementLayer, SupportedChain, SwapQuoter, SwapQuoterConfig, VmType } from "./types";',
92
+ 'import type { ChainNetwork, ChainStack, PegGroup, ProviderName, SettlementConfig, SettlementLayer, SupportedChain, SwapQuoter, SwapQuoterConfig, VmType } from "./types";',
93
93
  "",
94
94
  "interface Token {",
95
95
  " /** EVM: 0x-prefixed hex. SVM: base58 SPL mint. TVM: base58 (T-prefixed). */",
@@ -102,6 +102,11 @@ function renderChains(chainRegistry, mainnets, testnets) {
102
102
  " approvalSlot: number | null;",
103
103
  " /** Bridge layers that can carry this token on this chain, when known. */",
104
104
  " settlementLayers?: SettlementLayer[];",
105
+ " /** Rhino's own symbol for this token, where it differs from ours. */",
106
+ " rhinoSymbol?: string;",
107
+ " /** NEAR 1Click asset id, for tokens whose id 1Click does not derive from",
108
+ " * the on-chain address (non-EVM chains). */",
109
+ " nearAssetId?: string;",
105
110
  " unpriced?: boolean;",
106
111
  "}",
107
112
  "",
@@ -150,6 +155,10 @@ function renderChains(chainRegistry, mainnets, testnets) {
150
155
  " wrappedNativeToken: NativeToken;",
151
156
  " tokens: Token[];",
152
157
  " settlementLayers: SettlementLayer[];",
158
+ " /** Per-vendor addressing for the enabled layers that need it (Rhino chain",
159
+ " * name, CCTP domain, NEAR prefix, OFT adapter). Third-party addresses —",
160
+ " * never the contracts block, which is read as Rhinestone-deployed. */",
161
+ " settlementConfig?: SettlementConfig;",
153
162
  " providers: ProviderName[];",
154
163
  " swapQuoters: SwapQuoter[];",
155
164
  " swapQuoterConfig?: Partial<Record<SwapQuoter, SwapQuoterConfig>>;",
@@ -412,6 +421,10 @@ async function generate() {
412
421
  // to insist on. A chain added without one would otherwise reach production as
413
422
  // a blank tile, which nothing anywhere reports.
414
423
  const iconSlugIssues = (0, validation_1.validateChainIconSlugs)(classifiedChains);
424
+ // The one check that needs both files: a chain block declares OFT membership
425
+ // and reads its addresses back out of the OFT registry, so only an adapter
426
+ // added there without the matching chain entry can drift.
427
+ const oftCoverageIssues = (0, validation_1.validateOftCoverage)(classifiedChains, output.oft);
415
428
  const classificationIssues = [
416
429
  ...networkIssues,
417
430
  ...stackIssues,
@@ -419,6 +432,7 @@ async function generate() {
419
432
  ...explorerIssues,
420
433
  ...publicRpcUrlIssues,
421
434
  ...iconSlugIssues,
435
+ ...oftCoverageIssues,
422
436
  ];
423
437
  await (0, promises_1.writeFile)(node_path_1.default.join(configsDir, "chains.json"), `${JSON.stringify(classifiedChains, null, 2)}\n`);
424
438
  await (0, promises_1.writeFile)(node_path_1.default.join(configsDir, "mainnets.json"), `${JSON.stringify(output.mainnets, null, 2)}\n`);
@@ -215,6 +215,16 @@ export declare function classifyChainStacks(chains: ChainRegistry, networks: {
215
215
  * - Single-layer fragility: chains with exactly one settlement layer (warning)
216
216
  */
217
217
  export declare function validateChainConfig(chains: ChainRegistry): ValidationResult;
218
+ /**
219
+ * Insists that every chain the OFT registry deploys on declares OFT.
220
+ *
221
+ * The chain block states the membership and reads the adapter and endpoint id
222
+ * back out of config/oft.jsonnet, so the two can only disagree one way: a
223
+ * deployment added to the OFT registry whose chain block was never given an
224
+ * `oft` entry. That chain silently stops advertising OFT, which is invisible in
225
+ * a diff of either file on its own — this is the check that sees both.
226
+ */
227
+ export declare function validateOftCoverage(chains: ChainRegistry, oft: Record<string, unknown>): ValidationIssue[];
218
228
  /**
219
229
  * Formats validation issues for console output.
220
230
  *