-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathscript.ts
More file actions
156 lines (129 loc) · 4.72 KB
/
Copy pathscript.ts
File metadata and controls
156 lines (129 loc) · 4.72 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
import { writeFile } from 'node:fs/promises';
import { encodeAbiParameters, encodeFunctionData, Hex, hexToBytes, keccak256, namehash, numberToHex, stringToBytes, toHex } from 'viem';
import L2ResolverAbi from './abis/L2Resolver.json';
type ChainItem = {
name: string;
chainId: number;
subDomain: string;
shortName: string;
};
type BatchOutput = {
chainIds: number[];
forwardCalldata: Hex;
reverseCalldata: Hex;
};
const SOURCE_URL: string = "https://chainid.network/chains_mini.json";
const BATCH_SIZE: number = 10;
const L2_SUFFIX: string = '.l2.eth';
const CHAIN_IDENTIFIER_EIP7930_KEY: string = 'chain.id.eip7930';
const REVERSE_LOOKUP_NODE: Hex = keccak256(stringToBytes('reverse.chain.id.eip7930')) as Hex;
/**
* Processes a batch of chain items and returns the chainIds, forwardCalldata, and reverseCalldata for setRecords.
* @param batch - The batch of chain items to process.
* @returns The chainIds, forwardCalldata, and reverseCalldata.
*/
const processBatch = async (
batch: ChainItem[]
): Promise<[number[], Hex, Hex]> => {
const forwardNodes: Hex[] = [];
const forwardKeys: string[] = [];
const forwardValues: Hex[] = [];
const reverseNodes: Hex[] = [];
const reverseKeys: string[] = [];
const reverseValues: Hex[] = [];
const chainIds: number[] = [];
for (const item of batch) {
const subDomain = item.subDomain || item.shortName || item.name; // TODO: Review this
const domain = `${subDomain}${L2_SUFFIX}`;
chainIds.push(item.chainId);
const nodeNamehash = namehash(domain);
const chainIdBytes = encodeEIP7930ChainIdBytes(item.chainId);
forwardNodes.push(nodeNamehash);
forwardKeys.push(CHAIN_IDENTIFIER_EIP7930_KEY);
forwardValues.push(chainIdBytes);
const identifierHash = keccak256(chainIdBytes);
const reverseKey = `${CHAIN_IDENTIFIER_EIP7930_KEY}${identifierHash}`;
const encodedDomain = encodeAbiParameters([{ type: 'string' }], [domain]);
reverseNodes.push(REVERSE_LOOKUP_NODE);
reverseKeys.push(reverseKey);
reverseValues.push(encodedDomain);
}
const forwardCalldata = encodeFunctionData({
abi: L2ResolverAbi,
functionName: 'setRecords',
args: [forwardNodes, forwardKeys, forwardValues],
});
const reverseCalldata = encodeFunctionData({
abi: L2ResolverAbi,
functionName: 'setRecords',
args: [reverseNodes, reverseKeys, reverseValues],
});
return [chainIds, forwardCalldata, reverseCalldata];
};
const fetchChains = async (): Promise<ChainItem[]> => {
const response = await fetch(SOURCE_URL);
if (!response.ok) {
throw new Error(`Failed to fetch chains: ${response.status} ${response.statusText}`);
}
return (await response.json()) as ChainItem[];
};
const filteredChainItem = (item: ChainItem): ChainItem => {
return {
name: item.name,
chainId: item.chainId,
subDomain: item.subDomain || '',
shortName: item.shortName,
};
};
const chunkArray = <ChainItem>(items: ChainItem[], chunkSize: number): ChainItem[][] => {
const chunks: ChainItem[][] = [];
for (let i = 0; i < items.length; i += chunkSize) {
chunks.push(items.slice(i, i + chunkSize));
}
return chunks;
};
/**
* Encodes the chainId to the bytes format required by EIP-7930.
* Encodes those with:
* "parent": { "type": "L2", "chain": "eip155-1", ... }
* @param chainId - The chainId to encode.
* @returns The encoded bytes.
*/
const encodeEIP7930ChainIdBytes = (chainId: number): Hex => {
const version = Uint8Array.of(0x00, 0x01);
const chainType = Uint8Array.of(0x00, 0x00);
const chainRef = hexToBytes(numberToHex(chainId));
const chainRefLength = Uint8Array.of(chainRef.length);
const addressLength = Uint8Array.of(0x00);
const bytes = new Uint8Array(version.length + chainType.length + chainRefLength.length + chainRef.length + addressLength.length);
bytes.set(version, 0);
bytes.set(chainType, 2);
bytes.set(chainRefLength, 4);
bytes.set(chainRef, 5);
bytes.set(addressLength, 5 + chainRef.length);
return toHex(bytes);
}
/**
* Main function to fetch chains, filter them, and process them in batches.
* Writes the outputs to batches.json.
*/
const main = async (): Promise<void> => {
const chains: ChainItem[] = await fetchChains();
const records: ChainItem[] = chains.map(filteredChainItem);
const batches: ChainItem[][] = chunkArray(records, BATCH_SIZE);
const outputs: BatchOutput[] = [];
for (const batch of batches) {
const [chainIds, forwardCalldata, reverseCalldata] = await processBatch(batch);
outputs.push({ chainIds, forwardCalldata, reverseCalldata });
}
await writeFile(
'batches.json',
JSON.stringify(outputs, null, 2),
'utf8'
);
};
main().catch((error) => {
console.error(error);
process.exitCode = 1;
});
export { encodeEIP7930ChainIdBytes, processBatch };