@rhinestone/shared-configs 1.14.0 → 1.16.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 +0 -81
- package/dist/configs/contracts.dev.json +181 -0
- package/dist/configs/contracts.json +181 -0
- package/dist/configs/mainnets.json +0 -6
- package/dist/configs/oft.json +0 -3
- package/dist/configs/providers.json +0 -3
- package/dist/scripts/__tests__/hoistUniform.test.d.ts +1 -0
- package/dist/scripts/__tests__/hoistUniform.test.js +84 -0
- package/dist/scripts/generate.js +5 -2
- package/dist/scripts/hoistUniform.d.ts +30 -0
- package/dist/scripts/hoistUniform.js +62 -0
- package/dist/src/__tests__/chainFactsClient.test.js +46 -0
- package/dist/src/chainFactsClient.d.ts +26 -2
- package/dist/src/chainFactsClient.js +43 -1
- package/dist/src/chains.js +0 -85
- package/dist/src/generated/contracts.dev.js +34 -270
- package/dist/src/generated/contracts.js +34 -270
- package/dist/src/generated/networks.d.ts +718 -1362
- package/dist/src/generated/networks.js +1 -3
- package/dist/src/generated/packageVersion.d.ts +1 -1
- package/dist/src/generated/packageVersion.js +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
export type RawContracts = Record<string, Record<string, string>>;
|
|
2
|
+
/**
|
|
3
|
+
* Hoist every address that is identical on all chains carrying it into the `*`
|
|
4
|
+
* wildcard, leaving each chain with only what genuinely differs there.
|
|
5
|
+
*
|
|
6
|
+
* Consumers resolve a chain as `{ ...wildcard, ...chainSpecific }`, so this
|
|
7
|
+
* changes nothing for a chain that already had the key. What it changes is the
|
|
8
|
+
* chain the map has never heard of: chains now arrive at runtime in the facts
|
|
9
|
+
* artifact while this map is baked into the consumer's image, so a new chain
|
|
10
|
+
* falls back to the bare wildcard and every address missing from it resolves to
|
|
11
|
+
* `undefined`. That is not theoretical — it is why the first quote to Ink 500'd
|
|
12
|
+
* (RHI-5703): `permit2` was absent from the wildcard, so a `spender: undefined`
|
|
13
|
+
* reached viem, which threw inside a `catch` that then discarded the chain.
|
|
14
|
+
*
|
|
15
|
+
* Hoisted only with evidence: at least two chains carrying the key, and exactly
|
|
16
|
+
* one distinct address between them. One chain proves nothing about uniformity,
|
|
17
|
+
* and a key whose value varies is genuinely per-chain (`spokepool` has 16
|
|
18
|
+
* distinct addresses across 18 chains, `oftProxy` 7 across 7).
|
|
19
|
+
*
|
|
20
|
+
* Keys that split by *network* rather than by chain are deliberately left
|
|
21
|
+
* per-chain — the eco family is one mainnet address and one testnet address.
|
|
22
|
+
* A single wildcard value would hand a new testnet the mainnet portal, and a
|
|
23
|
+
* confidently wrong address is worse than a missing one.
|
|
24
|
+
*
|
|
25
|
+
* Note this widens resolution: a chain that never had `acrossHandler` now
|
|
26
|
+
* resolves one. Capability is gated by `settlementLayers` in the registry, not
|
|
27
|
+
* by the presence of a key here, so that is safe — but it does mean this file
|
|
28
|
+
* stops being a record of what is deployed where.
|
|
29
|
+
*/
|
|
30
|
+
export declare const hoistUniformAddresses: (contracts: RawContracts) => RawContracts;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.hoistUniformAddresses = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Hoist every address that is identical on all chains carrying it into the `*`
|
|
6
|
+
* wildcard, leaving each chain with only what genuinely differs there.
|
|
7
|
+
*
|
|
8
|
+
* Consumers resolve a chain as `{ ...wildcard, ...chainSpecific }`, so this
|
|
9
|
+
* changes nothing for a chain that already had the key. What it changes is the
|
|
10
|
+
* chain the map has never heard of: chains now arrive at runtime in the facts
|
|
11
|
+
* artifact while this map is baked into the consumer's image, so a new chain
|
|
12
|
+
* falls back to the bare wildcard and every address missing from it resolves to
|
|
13
|
+
* `undefined`. That is not theoretical — it is why the first quote to Ink 500'd
|
|
14
|
+
* (RHI-5703): `permit2` was absent from the wildcard, so a `spender: undefined`
|
|
15
|
+
* reached viem, which threw inside a `catch` that then discarded the chain.
|
|
16
|
+
*
|
|
17
|
+
* Hoisted only with evidence: at least two chains carrying the key, and exactly
|
|
18
|
+
* one distinct address between them. One chain proves nothing about uniformity,
|
|
19
|
+
* and a key whose value varies is genuinely per-chain (`spokepool` has 16
|
|
20
|
+
* distinct addresses across 18 chains, `oftProxy` 7 across 7).
|
|
21
|
+
*
|
|
22
|
+
* Keys that split by *network* rather than by chain are deliberately left
|
|
23
|
+
* per-chain — the eco family is one mainnet address and one testnet address.
|
|
24
|
+
* A single wildcard value would hand a new testnet the mainnet portal, and a
|
|
25
|
+
* confidently wrong address is worse than a missing one.
|
|
26
|
+
*
|
|
27
|
+
* Note this widens resolution: a chain that never had `acrossHandler` now
|
|
28
|
+
* resolves one. Capability is gated by `settlementLayers` in the registry, not
|
|
29
|
+
* by the presence of a key here, so that is safe — but it does mean this file
|
|
30
|
+
* stops being a record of what is deployed where.
|
|
31
|
+
*/
|
|
32
|
+
const hoistUniformAddresses = (contracts) => {
|
|
33
|
+
const perChain = Object.entries(contracts).filter(([id]) => id !== "*");
|
|
34
|
+
const addressesByKey = new Map();
|
|
35
|
+
for (const [, entries] of perChain) {
|
|
36
|
+
for (const [key, address] of Object.entries(entries)) {
|
|
37
|
+
const seen = addressesByKey.get(key) ?? new Set();
|
|
38
|
+
seen.add(address.toLowerCase());
|
|
39
|
+
addressesByKey.set(key, seen);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
const chainsCarrying = (key) => perChain.filter(([, entries]) => key in entries).length;
|
|
43
|
+
const uniform = [...addressesByKey.entries()]
|
|
44
|
+
.filter(([key, addresses]) => addresses.size === 1 && chainsCarrying(key) > 1)
|
|
45
|
+
.map(([key]) => key);
|
|
46
|
+
if (uniform.length === 0) {
|
|
47
|
+
return contracts;
|
|
48
|
+
}
|
|
49
|
+
const wildcard = { ...(contracts["*"] ?? {}) };
|
|
50
|
+
for (const key of uniform) {
|
|
51
|
+
const source = perChain.find(([, entries]) => key in entries);
|
|
52
|
+
if (source) {
|
|
53
|
+
wildcard[key] = source[1][key];
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
const hoisted = { "*": wildcard };
|
|
57
|
+
for (const [chainId, entries] of perChain) {
|
|
58
|
+
hoisted[chainId] = Object.fromEntries(Object.entries(entries).filter(([key, address]) => !(key in wildcard) || wildcard[key] !== address));
|
|
59
|
+
}
|
|
60
|
+
return hoisted;
|
|
61
|
+
};
|
|
62
|
+
exports.hoistUniformAddresses = hoistUniformAddresses;
|
|
@@ -467,4 +467,50 @@ function clientWith(extra = {}) {
|
|
|
467
467
|
(0, bun_test_1.expect)(client.getSnapshot().source).toBe("bundled");
|
|
468
468
|
});
|
|
469
469
|
});
|
|
470
|
+
(0, bun_test_1.describe)("contracts", () => {
|
|
471
|
+
const CONTRACTS = {
|
|
472
|
+
prod: { "*": { permit2: "0xPP" }, "1": { spokepool: "0x11" } },
|
|
473
|
+
dev: { "*": { permit2: "0xPP" }, "1": { spokepool: "0xDD" } },
|
|
474
|
+
};
|
|
475
|
+
(0, bun_test_1.it)("installs contracts carried by the artifact", async () => {
|
|
476
|
+
serveSigned({ ...payload(), contracts: CONTRACTS });
|
|
477
|
+
const client = clientWith();
|
|
478
|
+
(0, bun_test_1.expect)(await client.refresh()).toBe(true);
|
|
479
|
+
(0, bun_test_1.expect)(client.getContracts("prod")["1"].spokepool).toBe("0x11");
|
|
480
|
+
(0, bun_test_1.expect)(client.getContracts("dev")["1"].spokepool).toBe("0xDD");
|
|
481
|
+
});
|
|
482
|
+
// Every artifact published before contracts existed is this shape, and the
|
|
483
|
+
// consumer has to stay exactly where it was rather than lose its addresses.
|
|
484
|
+
(0, bun_test_1.it)("keeps the bundled contracts when the artifact omits them", async () => {
|
|
485
|
+
serveSigned(payload());
|
|
486
|
+
const client = clientWith();
|
|
487
|
+
(0, bun_test_1.expect)(await client.refresh()).toBe(true);
|
|
488
|
+
(0, bun_test_1.expect)(client.getSnapshot().source).toBe("remote");
|
|
489
|
+
(0, bun_test_1.expect)(client.getContracts("prod")).toEqual(clientWith().getSnapshot().contracts.prod);
|
|
490
|
+
});
|
|
491
|
+
// A bad contracts block must not cost the consumer a chain update it would
|
|
492
|
+
// otherwise have taken — chains install, contracts fall back, error reported.
|
|
493
|
+
bun_test_1.it.each([
|
|
494
|
+
["a missing half", { prod: CONTRACTS.prod }],
|
|
495
|
+
["a non-object entry", { prod: { "1": "nope" }, dev: CONTRACTS.dev }],
|
|
496
|
+
["a non-string address", { prod: { "1": { permit2: 5 } }, dev: CONTRACTS.dev }],
|
|
497
|
+
["an unusable chain key", { prod: { "01": { permit2: "0xPP" } }, dev: CONTRACTS.dev }],
|
|
498
|
+
])("rejects %s without failing the read", async (_label, contracts) => {
|
|
499
|
+
const phases = [];
|
|
500
|
+
serveSigned({ ...payload(), contracts });
|
|
501
|
+
const client = clientWith({
|
|
502
|
+
onError: (_e, phase) => phases.push(phase),
|
|
503
|
+
});
|
|
504
|
+
(0, bun_test_1.expect)(await client.refresh()).toBe(true);
|
|
505
|
+
(0, bun_test_1.expect)(client.getSnapshot().source).toBe("remote");
|
|
506
|
+
(0, bun_test_1.expect)(phases).toContain("parse");
|
|
507
|
+
(0, bun_test_1.expect)(client.getContracts("prod")).toEqual(clientWith().getSnapshot().contracts.prod);
|
|
508
|
+
});
|
|
509
|
+
(0, bun_test_1.it)("accepts the wildcard key alongside chain ids", async () => {
|
|
510
|
+
serveSigned({ ...payload(), contracts: CONTRACTS });
|
|
511
|
+
const client = clientWith();
|
|
512
|
+
(0, bun_test_1.expect)(await client.refresh()).toBe(true);
|
|
513
|
+
(0, bun_test_1.expect)(client.getContracts("prod")["*"].permit2).toBe("0xPP");
|
|
514
|
+
});
|
|
515
|
+
});
|
|
470
516
|
});
|
|
@@ -1,13 +1,35 @@
|
|
|
1
|
-
import type { ChainRegistry } from "./types";
|
|
1
|
+
import type { ChainRegistry, ContractAddresses } from "./types";
|
|
2
|
+
/**
|
|
3
|
+
* Both contract maps, because the artifact is one global object while consumers
|
|
4
|
+
* pick by their own environment — the orchestrator reads the dev map on dev and
|
|
5
|
+
* the prod map everywhere else. Publishing only one would make the artifact
|
|
6
|
+
* environment-specific, which the single-URL design rules out.
|
|
7
|
+
*/
|
|
8
|
+
interface ContractsByEnv {
|
|
9
|
+
prod: ContractAddresses;
|
|
10
|
+
dev: ContractAddresses;
|
|
11
|
+
}
|
|
2
12
|
/** Wire format of the published facts artifact. */
|
|
3
13
|
interface ChainFactsPayload {
|
|
4
14
|
version: string;
|
|
5
15
|
chains: ChainRegistry;
|
|
16
|
+
/**
|
|
17
|
+
* Optional: artifacts published before contracts were carried do not have it,
|
|
18
|
+
* and those stay valid. A payload without contracts keeps the bundled maps,
|
|
19
|
+
* which is the same position a consumer was in before this field existed.
|
|
20
|
+
*/
|
|
21
|
+
contracts?: ContractsByEnv;
|
|
6
22
|
}
|
|
7
23
|
type ChainFactsSource = "remote" | "bundled";
|
|
8
24
|
type ChainFactsErrorPhase = "config" | "fetch" | "verify" | "parse" | "stale";
|
|
9
25
|
interface ChainFactsSnapshot {
|
|
10
26
|
chains: ChainRegistry;
|
|
27
|
+
/**
|
|
28
|
+
* Per-chain contract addresses, keyed by environment. Falls back to the
|
|
29
|
+
* bundled maps when a remote artifact omits them, so this is always populated
|
|
30
|
+
* and a consumer never has to decide what to do about a missing half.
|
|
31
|
+
*/
|
|
32
|
+
contracts: ContractsByEnv;
|
|
11
33
|
source: ChainFactsSource;
|
|
12
34
|
/** null for "bundled", which ships with the package and has no version stamp of its own. */
|
|
13
35
|
version: string | null;
|
|
@@ -43,6 +65,8 @@ interface ChainFactsClient {
|
|
|
43
65
|
/** The active facts: remote once a refresh has succeeded, otherwise bundled. */
|
|
44
66
|
getSnapshot(): ChainFactsSnapshot;
|
|
45
67
|
getChainRegistry(): ChainRegistry;
|
|
68
|
+
/** Contract addresses for one environment, from the active snapshot. */
|
|
69
|
+
getContracts(env: keyof ContractsByEnv): ContractAddresses;
|
|
46
70
|
}
|
|
47
71
|
/**
|
|
48
72
|
* Reads chain facts remote-first, falling back to the registry bundled with
|
|
@@ -53,4 +77,4 @@ interface ChainFactsClient {
|
|
|
53
77
|
*/
|
|
54
78
|
declare function createChainFactsClient(options?: ChainFactsClientOptions): ChainFactsClient;
|
|
55
79
|
export { createChainFactsClient };
|
|
56
|
-
export type { ChainFactsClient, ChainFactsClientOptions, ChainFactsErrorPhase, ChainFactsPayload, ChainFactsSnapshot, ChainFactsSource, };
|
|
80
|
+
export type { ChainFactsClient, ChainFactsClientOptions, ChainFactsErrorPhase, ChainFactsPayload, ChainFactsSnapshot, ChainFactsSource, ContractsByEnv, };
|
|
@@ -5,12 +5,19 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.createChainFactsClient = createChainFactsClient;
|
|
7
7
|
const chains_json_1 = __importDefault(require("../configs/chains.json"));
|
|
8
|
+
const contracts_json_1 = __importDefault(require("../configs/contracts.json"));
|
|
9
|
+
const contracts_dev_json_1 = __importDefault(require("../configs/contracts.dev.json"));
|
|
8
10
|
const chainFactsKey_1 = require("./generated/chainFactsKey");
|
|
9
11
|
const packageVersion_1 = require("./generated/packageVersion");
|
|
10
12
|
const bundledChainRegistry = chains_json_1.default;
|
|
13
|
+
const bundledContracts = {
|
|
14
|
+
prod: contracts_json_1.default,
|
|
15
|
+
dev: contracts_dev_json_1.default,
|
|
16
|
+
};
|
|
11
17
|
const DEFAULT_FETCH_TIMEOUT_MS = 5_000;
|
|
12
18
|
const BUNDLED_SNAPSHOT = {
|
|
13
19
|
chains: bundledChainRegistry,
|
|
20
|
+
contracts: bundledContracts,
|
|
14
21
|
source: "bundled",
|
|
15
22
|
version: null,
|
|
16
23
|
};
|
|
@@ -44,6 +51,25 @@ function isChainRegistry(value) {
|
|
|
44
51
|
return (entries.length > 0 &&
|
|
45
52
|
entries.every(([id, entry]) => CHAIN_ID_KEY.test(id) && looksLikeChainEntry(entry)));
|
|
46
53
|
}
|
|
54
|
+
// Structural only, matching isChainRegistry's reasoning: an unknown contract
|
|
55
|
+
// key at read time just means an older consumer is reading a newer artifact.
|
|
56
|
+
// Keys are chain ids or the "*" wildcard consumers merge underneath a chain.
|
|
57
|
+
function isContractAddresses(value) {
|
|
58
|
+
if (!isObject(value))
|
|
59
|
+
return false;
|
|
60
|
+
return Object.entries(value).every(([id, entry]) => (id === "*" || CHAIN_ID_KEY.test(id)) &&
|
|
61
|
+
isObject(entry) &&
|
|
62
|
+
Object.values(entry).every((address) => typeof address === "string"));
|
|
63
|
+
}
|
|
64
|
+
// Both halves must be present and usable, or the field is rejected whole. A
|
|
65
|
+
// half-valid contracts block would silently leave one environment on bundled
|
|
66
|
+
// data while the other moved forward, which is the kind of split nobody would
|
|
67
|
+
// think to look for.
|
|
68
|
+
function isContractsByEnv(value) {
|
|
69
|
+
return (isObject(value) &&
|
|
70
|
+
isContractAddresses(value.prod) &&
|
|
71
|
+
isContractAddresses(value.dev));
|
|
72
|
+
}
|
|
47
73
|
// Both sides are release versions produced by the same pipeline, so a numeric
|
|
48
74
|
// x.y.z comparison is enough; a pre-release suffix (-alpha.N) is ignored, since
|
|
49
75
|
// pre-release tags never publish to the facts CDN.
|
|
@@ -254,7 +280,19 @@ function createChainFactsClient(options = {}) {
|
|
|
254
280
|
report(new Error(`chain facts ${body.version} is older than this package's bundled registry (${packageVersion_1.PACKAGE_VERSION}) — treating as stale and keeping bundled data`), "stale");
|
|
255
281
|
return null;
|
|
256
282
|
}
|
|
257
|
-
|
|
283
|
+
// Contracts are rejected on their own rather than failing the whole read:
|
|
284
|
+
// a malformed contracts block should not cost the consumer a chain update
|
|
285
|
+
// it would otherwise have taken. The absence is reported so it is visible.
|
|
286
|
+
let contracts;
|
|
287
|
+
if (body.contracts !== undefined) {
|
|
288
|
+
if (isContractsByEnv(body.contracts)) {
|
|
289
|
+
contracts = body.contracts;
|
|
290
|
+
}
|
|
291
|
+
else {
|
|
292
|
+
report(new Error("chain facts payload has an unusable contracts block — keeping bundled contracts"), "parse");
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
return { version: body.version, chains, contracts };
|
|
258
296
|
}
|
|
259
297
|
async function fetchAndInstall() {
|
|
260
298
|
const payload = await fetchRemote();
|
|
@@ -262,6 +300,9 @@ function createChainFactsClient(options = {}) {
|
|
|
262
300
|
return false;
|
|
263
301
|
snapshot = {
|
|
264
302
|
chains: payload.chains,
|
|
303
|
+
// An artifact that carries no contracts leaves the bundled maps in place,
|
|
304
|
+
// which is where every consumer was before the field existed.
|
|
305
|
+
contracts: payload.contracts ?? bundledContracts,
|
|
265
306
|
source: "remote",
|
|
266
307
|
version: payload.version,
|
|
267
308
|
};
|
|
@@ -291,5 +332,6 @@ function createChainFactsClient(options = {}) {
|
|
|
291
332
|
},
|
|
292
333
|
getSnapshot: () => snapshot,
|
|
293
334
|
getChainRegistry: () => snapshot.chains,
|
|
335
|
+
getContracts: (env) => snapshot.contracts[env],
|
|
294
336
|
};
|
|
295
337
|
}
|
package/dist/src/chains.js
CHANGED
|
@@ -1281,91 +1281,6 @@ const chains = {
|
|
|
1281
1281
|
"Alchemy"
|
|
1282
1282
|
]
|
|
1283
1283
|
},
|
|
1284
|
-
"57073": {
|
|
1285
|
-
"caip2": "eip155:57073",
|
|
1286
|
-
"explorer": {
|
|
1287
|
-
"url": "https://explorer.inkonchain.com",
|
|
1288
|
-
"addressPath": "/address/",
|
|
1289
|
-
"txPath": "/tx/"
|
|
1290
|
-
},
|
|
1291
|
-
"iconSlug": "ink",
|
|
1292
|
-
"name": "Ink",
|
|
1293
|
-
"nativeToken": {
|
|
1294
|
-
"address": "0x0000000000000000000000000000000000000000",
|
|
1295
|
-
"decimals": 18,
|
|
1296
|
-
"symbol": "ETH"
|
|
1297
|
-
},
|
|
1298
|
-
"network": "mainnet",
|
|
1299
|
-
"publicRpcUrl": "https://rpc-gel.inkonchain.com",
|
|
1300
|
-
"settlementLayers": [
|
|
1301
|
-
"ACROSS",
|
|
1302
|
-
"ECO",
|
|
1303
|
-
"RELAY",
|
|
1304
|
-
"OFT"
|
|
1305
|
-
],
|
|
1306
|
-
"stack": "op-stack",
|
|
1307
|
-
"swapQuoters": [
|
|
1308
|
-
"0x"
|
|
1309
|
-
],
|
|
1310
|
-
"tokens": [
|
|
1311
|
-
{
|
|
1312
|
-
"address": "0x0000000000000000000000000000000000000000",
|
|
1313
|
-
"approvalSlot": null,
|
|
1314
|
-
"balanceSlot": null,
|
|
1315
|
-
"decimals": 18,
|
|
1316
|
-
"pegGroup": "ETH",
|
|
1317
|
-
"settlementLayers": [
|
|
1318
|
-
"ACROSS"
|
|
1319
|
-
],
|
|
1320
|
-
"symbol": "ETH"
|
|
1321
|
-
},
|
|
1322
|
-
{
|
|
1323
|
-
"address": "0x4200000000000000000000000000000000000006",
|
|
1324
|
-
"approvalSlot": 1,
|
|
1325
|
-
"balanceSlot": 0,
|
|
1326
|
-
"decimals": 18,
|
|
1327
|
-
"pegGroup": "ETH",
|
|
1328
|
-
"priceSymbol": "ETH",
|
|
1329
|
-
"settlementLayers": [
|
|
1330
|
-
"ACROSS"
|
|
1331
|
-
],
|
|
1332
|
-
"symbol": "WETH"
|
|
1333
|
-
},
|
|
1334
|
-
{
|
|
1335
|
-
"address": "0x2d270e6886d130d724215a266106e6832161eaed",
|
|
1336
|
-
"approvalSlot": 10,
|
|
1337
|
-
"balanceSlot": 9,
|
|
1338
|
-
"decimals": 6,
|
|
1339
|
-
"pegGroup": "USD",
|
|
1340
|
-
"settlementLayers": [
|
|
1341
|
-
"ACROSS"
|
|
1342
|
-
],
|
|
1343
|
-
"symbol": "USDC"
|
|
1344
|
-
},
|
|
1345
|
-
{
|
|
1346
|
-
"address": "0x0200c29006150606b650577bbe7b6248f58470c1",
|
|
1347
|
-
"approvalSlot": 52,
|
|
1348
|
-
"balanceSlot": 51,
|
|
1349
|
-
"decimals": 6,
|
|
1350
|
-
"pegGroup": "USD",
|
|
1351
|
-
"priceSymbol": "USDT",
|
|
1352
|
-
"settlementLayers": [
|
|
1353
|
-
"ACROSS"
|
|
1354
|
-
],
|
|
1355
|
-
"symbol": "USDT0"
|
|
1356
|
-
}
|
|
1357
|
-
],
|
|
1358
|
-
"vmType": "evm",
|
|
1359
|
-
"wrappedNativeToken": {
|
|
1360
|
-
"address": "0x4200000000000000000000000000000000000006",
|
|
1361
|
-
"decimals": 18,
|
|
1362
|
-
"symbol": "WETH"
|
|
1363
|
-
},
|
|
1364
|
-
"providers": [
|
|
1365
|
-
"DRPC",
|
|
1366
|
-
"Alchemy"
|
|
1367
|
-
]
|
|
1368
|
-
},
|
|
1369
1284
|
"84532": {
|
|
1370
1285
|
"caip2": "eip155:84532",
|
|
1371
1286
|
"explorer": {
|