Skip to content
This repository was archived by the owner on Jan 9, 2026. It is now read-only.

Commit 73959cc

Browse files
committed
Add explorer and solidity config to plugin config file,
Add functions to read the new config Add test cases
1 parent 423243e commit 73959cc

File tree

3 files changed

+337
-44
lines changed

3 files changed

+337
-44
lines changed

chainweb.config.json

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,22 @@
88
"numberOfChains": 5,
99
"chainwebChainIdOffset": 20,
1010
"chainIdOffset": 1789,
11-
"externalHostUrl": "http://localhost:1848/chainweb/0.0/evm-development"
11+
"externalHostUrl": "http://localhost:1848/chainweb/0.0/evm-development",
12+
"explorer": {
13+
"verifier": "blockscout",
14+
"apiURLTemplate": "http://chain-{cid}.evm.kadena.internal:8000/api/",
15+
"apiVersion": "v2"
16+
}
17+
},
18+
"testnet": {
19+
"numberOfChains": 5,
20+
"chainwebChainIdOffset": 20,
21+
"chainIdOffset": 5920,
22+
"externalHostUrl": "https://evm-testnet.chainweb.com/chainweb/0.0/evm-testnet",
23+
"explorer": {
24+
"verifier": "blockscout",
25+
"apiURLTemplate": "https://chain-{cid}.evm-testnet-blockscout.chainweb.com/api/",
26+
"apiVersion": "v2"
27+
}
1228
}
1329
}

src/Chainweb.sol

Lines changed: 239 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,15 @@ contract Chainweb is CommonBase {
1818
string private _hostUrl;
1919

2020
// @notice Precompile that provides the chainweb-chain-id
21-
address public constant CHAIN_ID_PRECOMPILE = address(0x9b02c3e2dF42533e0FD166798B5A616f59DBd2cc);
21+
address public constant CHAIN_ID_PRECOMPILE =
22+
address(0x9b02c3e2dF42533e0FD166798B5A616f59DBd2cc);
2223

23-
constructor(uint24 numberOfChains, uint256 chainIdOffset, uint24 chainwebChainIdOffset, string memory hostUrl) {
24+
constructor(
25+
uint24 numberOfChains,
26+
uint256 chainIdOffset,
27+
uint24 chainwebChainIdOffset,
28+
string memory hostUrl
29+
) {
2430
_numberOfChains = numberOfChains;
2531
_chainIdOffset = chainIdOffset;
2632
_chainwebChainIdOffset = chainwebChainIdOffset;
@@ -39,7 +45,10 @@ contract Chainweb is CommonBase {
3945
return path;
4046
}
4147

42-
function rpcUrl(string memory chainId, string memory chainwebChainId) private returns (string memory) {
48+
function rpcUrl(
49+
string memory chainId,
50+
string memory chainwebChainId
51+
) private returns (string memory) {
4352
// TODO - support forking from a custom RPC URL if provided
4453
string[] memory cmds = new string[](4);
4554
cmds[0] = getNodePath();
@@ -63,12 +72,21 @@ contract Chainweb is CommonBase {
6372
console.log("Setting main RPC node for", _numberOfChains, "chains");
6473
for (uint256 i = 0; i < _numberOfChains; i++) {
6574
if (bytes(_hostUrl).length > 0) {
66-
string memory url =
67-
string(abi.encodePacked(_hostUrl, "/chain/", vm.toString(i + _chainwebChainIdOffset), "/evm/rpc"));
75+
string memory url = string(
76+
abi.encodePacked(
77+
_hostUrl,
78+
"/chain/",
79+
vm.toString(i + _chainwebChainIdOffset),
80+
"/evm/rpc"
81+
)
82+
);
6883
console.log("Using custom RPC URL:", url);
6984
_chainForks.push(vm.createFork(url));
7085
} else {
71-
string memory url = rpcUrl(vm.toString(i + _chainIdOffset), vm.toString(i + _chainwebChainIdOffset));
86+
string memory url = rpcUrl(
87+
vm.toString(i + _chainIdOffset),
88+
vm.toString(i + _chainwebChainIdOffset)
89+
);
7290
console.log("Forking", url);
7391
_chainForks.push(vm.createFork(url));
7492
}
@@ -103,7 +121,10 @@ contract Chainweb is CommonBase {
103121
return;
104122
}
105123
console.log("Setting main RPC node for", _numberOfChains, "chains");
106-
string memory url = rpcUrl(vm.toString(_chainIdOffset), vm.toString(_chainwebChainIdOffset));
124+
string memory url = rpcUrl(
125+
vm.toString(_chainIdOffset),
126+
vm.toString(_chainwebChainIdOffset)
127+
);
107128
console.log("Forking", url);
108129
for (uint24 i = 0; i < _numberOfChains; i++) {
109130
uint256 forkId = vm.createFork(url);
@@ -113,7 +134,12 @@ contract Chainweb is CommonBase {
113134
}
114135

115136
function switchChain(uint256 chainId) public {
116-
console.log("Switching to chain:", chainId, "_chainIdOffset", _chainwebChainIdOffset);
137+
console.log(
138+
"Switching to chain:",
139+
chainId,
140+
"_chainIdOffset",
141+
_chainwebChainIdOffset
142+
);
117143
require(chainId >= _chainwebChainIdOffset, "Invalid chain ID");
118144
uint24 chainIndex = uint24(chainId - _chainwebChainIdOffset);
119145
require(chainIndex < _numberOfChains, "Invalid chain ID");
@@ -130,69 +156,238 @@ contract Chainweb is CommonBase {
130156
}
131157
return chainIds;
132158
}
159+
160+
function verifyContract(
161+
uint256 chainId,
162+
address contractAddress,
163+
string memory contractPath
164+
) public returns (bytes memory) {
165+
ChainwebConfigReader configReader = new ChainwebConfigReader();
166+
string memory environment = vm.envExists("CHAINWEB")
167+
? vm.envString("CHAINWEB")
168+
: "anvil";
169+
ChainwebConfig memory config = configReader.readChainwebConfig(
170+
environment
171+
);
172+
173+
string[] memory cmds = new string[](8);
174+
cmds[0] = "./script/verify-multichain.sh";
175+
cmds[1] = vm.toString(chainId);
176+
cmds[2] = config.explorer.verifier;
177+
cmds[3] = config.explorer.apiURLTemplate;
178+
cmds[4] = config.explorer.apiVersion;
179+
cmds[5] = config.solidity.version;
180+
cmds[6] = vm.toString(config.solidity.optimizer.runs);
181+
cmds[7] = vm.toString(contractAddress);
182+
cmds[8] = contractPath;
183+
184+
bytes memory output = vm.ffi(cmds);
185+
return output;
186+
}
187+
}
188+
189+
struct OptimizerConfig {
190+
bool enabled;
191+
uint256 runs;
192+
}
193+
194+
struct SolidityConfig {
195+
string version;
196+
OptimizerConfig optimizer;
197+
string evmVersion;
198+
}
199+
200+
struct ExplorerConfig {
201+
string verifier;
202+
string apiURLTemplate;
203+
string apiVersion;
133204
}
134205

135206
struct ChainwebConfig {
136207
uint256 numberOfChains;
137208
uint256 chainIdOffset;
138209
uint256 chainwebChainIdOffset;
139210
string externalHostUrl;
211+
ExplorerConfig explorer;
212+
SolidityConfig solidity;
140213
}
141214

142215
contract ChainwebConfigReader is CommonBase {
143-
function readOptionalJsonUint(string memory json, string memory key, uint256 defaultValue)
144-
internal
145-
pure
146-
returns (uint256)
147-
{
216+
function readOptionalJsonUint(
217+
string memory json,
218+
string memory key,
219+
uint256 defaultValue
220+
) internal pure returns (uint256) {
148221
try vm.parseJsonUint(json, key) returns (uint256 value) {
149222
return value;
150223
} catch {
151224
return defaultValue;
152225
}
153226
}
154227

155-
function readOptionalJsonString(string memory json, string memory key, string memory defaultValue)
156-
internal
157-
pure
158-
returns (string memory)
159-
{
228+
function readOptionalJsonString(
229+
string memory json,
230+
string memory key,
231+
string memory defaultValue
232+
) internal pure returns (string memory) {
160233
try vm.parseJsonString(json, key) returns (string memory value) {
161234
return value;
162235
} catch {
163236
return defaultValue;
164237
}
165238
}
166239

167-
function readChainwebConfig(string memory environment) public view returns (ChainwebConfig memory) {
240+
function readExplorerConfig(
241+
string memory environment
242+
) public view returns (ExplorerConfig memory) {
168243
string memory root = vm.projectRoot();
169-
string memory path = string(abi.encodePacked(root, "/chainweb.config.json"));
244+
string memory path = string(
245+
abi.encodePacked(root, "/chainweb.config.json")
246+
);
247+
string memory json = vm.readFile(path);
248+
249+
string memory explorerPath = string(
250+
abi.encodePacked(".", environment, ".explorer")
251+
);
252+
return
253+
ExplorerConfig({
254+
verifier: readOptionalJsonString(
255+
json,
256+
string(abi.encodePacked(explorerPath, ".verifier")),
257+
""
258+
),
259+
apiURLTemplate: readOptionalJsonString(
260+
json,
261+
string(abi.encodePacked(explorerPath, ".apiURLTemplate")),
262+
""
263+
),
264+
apiVersion: readOptionalJsonString(
265+
json,
266+
string(abi.encodePacked(explorerPath, ".apiVersion")),
267+
""
268+
)
269+
});
270+
}
271+
272+
function readSolidityConfig() public view returns (SolidityConfig memory) {
273+
string memory root = vm.projectRoot();
274+
string memory path = string(
275+
abi.encodePacked(root, "/chainweb.config.json")
276+
);
277+
string memory json = vm.readFile(path);
278+
279+
string memory solidityPath = ".solidity";
280+
string memory solidityVersion = readOptionalJsonString(
281+
json,
282+
string(abi.encodePacked(solidityPath, ".version")),
283+
""
284+
);
285+
string memory evmVersion = readOptionalJsonString(
286+
json,
287+
string(abi.encodePacked(solidityPath, ".settings.evmVersion")),
288+
""
289+
);
290+
291+
bool optimizerEnabled = false;
292+
uint256 optimizerRuns = 200;
293+
// Try/catch for optimizer.enabled
294+
try
295+
vm.parseJsonBool(
296+
json,
297+
string(
298+
abi.encodePacked(
299+
solidityPath,
300+
".settings.optimizer.enabled"
301+
)
302+
)
303+
)
304+
returns (bool val) {
305+
optimizerEnabled = val;
306+
} catch {}
307+
// Try/catch for optimizer.runs
308+
try
309+
vm.parseJsonUint(
310+
json,
311+
string(
312+
abi.encodePacked(solidityPath, ".settings.optimizer.runs")
313+
)
314+
)
315+
returns (uint256 val) {
316+
optimizerRuns = val;
317+
} catch {}
318+
OptimizerConfig memory optimizer = OptimizerConfig({
319+
enabled: optimizerEnabled,
320+
runs: optimizerRuns
321+
});
322+
return
323+
SolidityConfig({
324+
version: solidityVersion,
325+
optimizer: optimizer,
326+
evmVersion: evmVersion
327+
});
328+
}
329+
330+
function readChainwebConfig(
331+
string memory environment
332+
) public view returns (ChainwebConfig memory) {
333+
string memory root = vm.projectRoot();
334+
string memory path = string(
335+
abi.encodePacked(root, "/chainweb.config.json")
336+
);
170337
string memory json = vm.readFile(path);
171338

172339
string memory envPath = string(abi.encodePacked(".", environment));
173340

174341
// Parse all fields from config
175-
uint256 numberOfChains = readOptionalJsonUint(json, string(abi.encodePacked(envPath, ".numberOfChains")), 1);
176-
uint256 chainIdOffset = readOptionalJsonUint(json, string(abi.encodePacked(envPath, ".chainIdOffset")), 31337);
177-
uint256 chainwebChainIdOffset =
178-
readOptionalJsonUint(json, string(abi.encodePacked(envPath, ".chainwebChainIdOffset")), 0);
179-
string memory externalHostUrl =
180-
readOptionalJsonString(json, string(abi.encodePacked(envPath, ".externalHostUrl")), "");
181-
182-
return ChainwebConfig({
183-
numberOfChains: numberOfChains,
184-
chainIdOffset: chainIdOffset,
185-
chainwebChainIdOffset: chainwebChainIdOffset,
186-
externalHostUrl: externalHostUrl
187-
});
342+
uint256 numberOfChains = readOptionalJsonUint(
343+
json,
344+
string(abi.encodePacked(envPath, ".numberOfChains")),
345+
1
346+
);
347+
uint256 chainIdOffset = readOptionalJsonUint(
348+
json,
349+
string(abi.encodePacked(envPath, ".chainIdOffset")),
350+
31337
351+
);
352+
uint256 chainwebChainIdOffset = readOptionalJsonUint(
353+
json,
354+
string(abi.encodePacked(envPath, ".chainwebChainIdOffset")),
355+
0
356+
);
357+
string memory externalHostUrl = readOptionalJsonString(
358+
json,
359+
string(abi.encodePacked(envPath, ".externalHostUrl")),
360+
""
361+
);
362+
363+
// Parse explorer config
364+
ExplorerConfig memory explorer = readExplorerConfig(environment);
365+
366+
// Parse solidity config
367+
SolidityConfig memory solidity = readSolidityConfig();
368+
369+
return
370+
ChainwebConfig({
371+
numberOfChains: numberOfChains,
372+
chainIdOffset: chainIdOffset,
373+
chainwebChainIdOffset: chainwebChainIdOffset,
374+
externalHostUrl: externalHostUrl,
375+
explorer: explorer,
376+
solidity: solidity
377+
});
188378
}
189379
}
190380

191381
contract ChainwebTest is Test {
192382
Chainweb public chainweb;
193383

194384
constructor(uint24 numberOfChains, uint24 chainwebChainIdOffset) {
195-
chainweb = new Chainweb(numberOfChains, block.chainid, chainwebChainIdOffset, "");
385+
chainweb = new Chainweb(
386+
numberOfChains,
387+
block.chainid,
388+
chainwebChainIdOffset,
389+
""
390+
);
196391
chainweb.setupChainsForTest();
197392
}
198393
}
@@ -202,13 +397,20 @@ contract ChainwebScript is Script {
202397
ChainwebConfigReader private configReader;
203398

204399
constructor() {
205-
string memory environment = vm.envExists("CHAINWEB") ? vm.envString("CHAINWEB") : "anvil";
400+
string memory environment = vm.envExists("CHAINWEB")
401+
? vm.envString("CHAINWEB")
402+
: "anvil";
206403

207404
configReader = new ChainwebConfigReader();
208-
ChainwebConfig memory config = configReader.readChainwebConfig(environment);
405+
ChainwebConfig memory config = configReader.readChainwebConfig(
406+
environment
407+
);
209408

210409
chainweb = new Chainweb(
211-
uint24(config.numberOfChains), block.chainid, uint24(config.chainwebChainIdOffset), config.externalHostUrl
410+
uint24(config.numberOfChains),
411+
block.chainid,
412+
uint24(config.chainwebChainIdOffset),
413+
config.externalHostUrl
212414
);
213415
chainweb.setupChainsForScript();
214416
}

0 commit comments

Comments
 (0)