@@ -7,19 +7,21 @@ import {console2} from "forge-std/console2.sol";
77import {Test} from "forge-std/test.sol " ;
88import {Script, console} from "forge-std/Script.sol " ;
99
10+ interface IChainwebChainId {
11+ function getChainId () external view returns (uint32 );
12+ }
13+
1014contract Chainweb is CommonBase {
1115 uint24 private _chains;
1216 uint256 private _chainIdOffset;
1317 uint24 private _chainwebChainIdOffset;
1418 uint256 [] private _chainForks;
1519 string private _hostUrl;
1620
17- constructor (
18- uint24 chainNumbers ,
19- uint256 chainIdOffset ,
20- uint24 chainwebChainIdOffset ,
21- string memory hostUrl
22- ) {
21+ // @notice Precompile that provides the chainweb-chain-id
22+ address public constant CHAIN_ID_PRECOMPILE = address (0x9b02c3e2dF42533e0FD166798B5A616f59DBd2cc );
23+
24+ constructor (uint24 chainNumbers , uint256 chainIdOffset , uint24 chainwebChainIdOffset , string memory hostUrl ) {
2325 _chains = chainNumbers;
2426 _chainIdOffset = chainIdOffset;
2527 _chainwebChainIdOffset = chainwebChainIdOffset;
@@ -37,12 +39,13 @@ contract Chainweb is CommonBase {
3739 return path;
3840 }
3941
40- function rpcUrl (string memory chainId ) private returns (string memory ) {
42+ function rpcUrl (string memory chainId , string memory chainwebChainId ) private returns (string memory ) {
4143 // TODO - support forking from a custom RPC URL if provided
42- string [] memory cmds = new string [](3 );
44+ string [] memory cmds = new string [](4 );
4345 cmds[0 ] = getNodePath ();
4446 cmds[1 ] = "start " ;
4547 cmds[2 ] = chainId;
48+ cmds[3 ] = chainwebChainId;
4649 bytes memory output = vm.ffi (cmds);
4750 string memory url = string (output);
4851 return url;
@@ -52,40 +55,53 @@ contract Chainweb is CommonBase {
5255 console.log ("Setting main RPC node for " , _chains, "chains " );
5356 for (uint256 i = 0 ; i < _chains; i++ ) {
5457 if (bytes (_hostUrl).length > 0 ) {
55- string memory url = string (
56- abi.encodePacked (
57- _hostUrl,
58- "/chain/ " ,
59- vm.toString (i + _chainwebChainIdOffset),
60- "/evm/rpc "
61- )
62- );
58+ string memory url =
59+ string (abi.encodePacked (_hostUrl, "/chain/ " , vm.toString (i + _chainwebChainIdOffset), "/evm/rpc " ));
6360 console.log ("Using custom RPC URL: " , url);
6461 _chainForks.push (vm.createFork (url));
6562 } else {
66- string memory url = rpcUrl (vm.toString (i + _chainIdOffset));
63+ string memory url = rpcUrl (vm.toString (i + _chainIdOffset), vm. toString (i + _chainwebChainIdOffset) );
6764 console.log ("Forking " , url);
6865 _chainForks.push (vm.createFork (url));
6966 }
7067 }
7168 }
7269
70+ // set the contract in the same address as devnet
71+ function deployChainWebChainIdContract (uint256 chainId ) public {
72+ // get runtime bytecode
73+ switchChain (chainId);
74+
75+ bytes memory bytecode = hex "5f545f526004601cf3 " ;
76+
77+ // place code at the target address
78+ vm.etch (CHAIN_ID_PRECOMPILE, bytecode);
79+
80+ // set storage slot 0 = desired chainId (e.g., 1337)
81+ vm.store (CHAIN_ID_PRECOMPILE, bytes32 (uint256 (0 )), bytes32 (chainId));
82+ }
83+
84+ function getActiveChainId () public view returns (uint256 ) {
85+ (bool ok , bytes memory data ) = CHAIN_ID_PRECOMPILE.staticcall ("" );
86+ require (ok, "call failed " );
87+
88+ uint32 chainId = uint32 (bytes4 (data));
89+ return chainId;
90+ }
91+
7392 function setupChainsForTest () public {
7493 console.log ("Setting main RPC node for " , _chains, "chains " );
75- string memory url = rpcUrl (vm.toString (_chainIdOffset));
94+ string memory url = rpcUrl (vm.toString (_chainIdOffset), vm. toString (_chainwebChainIdOffset) );
7695 console.log ("Forking " , url);
7796 for (uint24 i = 0 ; i < _chains; i++ ) {
78- _chainForks.push (vm.createFork (url));
97+ uint256 forkId = vm.createFork (url);
98+ _chainForks.push (forkId);
99+ deployChainWebChainIdContract (i + _chainwebChainIdOffset);
79100 }
80101 }
81102
82103 function switchChain (uint256 chainId ) public {
83- console.log (
84- "Switching to chain: " ,
85- chainId,
86- "_chainIdOffset " ,
87- _chainwebChainIdOffset
88- );
104+ console.log ("Switching to chain: " , chainId, "_chainIdOffset " , _chainwebChainIdOffset);
89105 require (chainId >= _chainwebChainIdOffset, "Invalid chain ID " );
90106 uint24 chainIndex = uint24 (chainId - _chainwebChainIdOffset);
91107 require (chainIndex < _chains, "Invalid chain ID " );
@@ -112,15 +128,23 @@ struct ChainwebConfig {
112128}
113129
114130contract ChainwebConfigReader is CommonBase {
115- function readOptionalJsonUint (string memory json , string memory key , uint256 defaultValue ) internal pure returns (uint256 ) {
131+ function readOptionalJsonUint (string memory json , string memory key , uint256 defaultValue )
132+ internal
133+ pure
134+ returns (uint256 )
135+ {
116136 try vm.parseJsonUint (json, key) returns (uint256 value ) {
117137 return value;
118138 } catch {
119139 return defaultValue;
120140 }
121141 }
122142
123- function readOptionalJsonString (string memory json , string memory key , string memory defaultValue ) internal pure returns (string memory ) {
143+ function readOptionalJsonString (string memory json , string memory key , string memory defaultValue )
144+ internal
145+ pure
146+ returns (string memory )
147+ {
124148 try vm.parseJsonString (json, key) returns (string memory value ) {
125149 return value;
126150 } catch {
@@ -138,8 +162,10 @@ contract ChainwebConfigReader is CommonBase {
138162 // Parse all fields from config
139163 uint256 numberOfChains = readOptionalJsonUint (json, string (abi.encodePacked (envPath, ".numberOfChains " )), 1 );
140164 uint256 chainIdOffset = readOptionalJsonUint (json, string (abi.encodePacked (envPath, ".chainIdOffset " )), 31337 );
141- uint256 chainwebChainIdOffset = readOptionalJsonUint (json, string (abi.encodePacked (envPath, ".chainwebChainIdOffset " )), 0 );
142- string memory extenalHostUrl = readOptionalJsonString (json, string (abi.encodePacked (envPath, ".extenalHostUrl " )), "" );
165+ uint256 chainwebChainIdOffset =
166+ readOptionalJsonUint (json, string (abi.encodePacked (envPath, ".chainwebChainIdOffset " )), 0 );
167+ string memory extenalHostUrl =
168+ readOptionalJsonString (json, string (abi.encodePacked (envPath, ".extenalHostUrl " )), "" );
143169
144170 return ChainwebConfig ({
145171 numberOfChains: numberOfChains,
@@ -154,12 +180,7 @@ contract ChainwebTest is Test {
154180 Chainweb public chainweb;
155181
156182 constructor (uint24 chainNumbers , uint24 chainwebChainIdOffset ) {
157- chainweb = new Chainweb (
158- chainNumbers,
159- block .chainid ,
160- chainwebChainIdOffset,
161- ""
162- );
183+ chainweb = new Chainweb (chainNumbers, block .chainid , chainwebChainIdOffset, "" );
163184 chainweb.setupChainsForTest ();
164185 }
165186}
@@ -169,24 +190,16 @@ contract ChainwebScript is Script {
169190 ChainwebConfigReader private configReader;
170191
171192 constructor () {
172- string memory environment = vm.envExists ("CHAINWEB " )
173- ? vm.envString ("CHAINWEB " )
174- : "anvil " ;
193+ string memory environment = vm.envExists ("CHAINWEB " ) ? vm.envString ("CHAINWEB " ) : "anvil " ;
175194
176195 configReader = new ChainwebConfigReader ();
177196 ChainwebConfig memory config = configReader.readChainwebConfig (environment);
178197
179- string memory nodeUrl = vm.envExists ("CHAINWEB_HOST " )
180- ? vm.envString ("CHAINWEB_HOST " )
181- : config.extenalHostUrl;
198+ string memory nodeUrl = vm.envExists ("CHAINWEB_HOST " ) ? vm.envString ("CHAINWEB_HOST " ) : config.extenalHostUrl;
182199 console.log ("Using node URL: " , nodeUrl);
183200
184- chainweb = new Chainweb (
185- uint24 (config.numberOfChains),
186- block .chainid ,
187- uint24 (config.chainwebChainIdOffset),
188- nodeUrl
189- );
201+ chainweb =
202+ new Chainweb (uint24 (config.numberOfChains), block .chainid , uint24 (config.chainwebChainIdOffset), nodeUrl);
190203 chainweb.setupChainsForScript ();
191204 }
192205}
0 commit comments