@@ -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
135206struct ChainwebConfig {
136207 uint256 numberOfChains;
137208 uint256 chainIdOffset;
138209 uint256 chainwebChainIdOffset;
139210 string externalHostUrl;
211+ ExplorerConfig explorer;
212+ SolidityConfig solidity ;
140213}
141214
142215contract 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
191381contract 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