| 1 | // SPDX-License-Identifier: MIT |
| 2 | pragma solidity >=0.7.0 <0.9.0; |
| 3 | |
| 4 | import {Test} from "../src/Test.sol"; |
| 5 | |
| 6 | contract StdChainsMock is Test { |
| 7 | function exposed_getChain(string memory chainAlias) public returns (Chain memory) { |
| 8 | return getChain(chainAlias); |
| 9 | } |
| 10 | |
| 11 | function exposed_getChain(uint256 chainId) public returns (Chain memory) { |
| 12 | return getChain(chainId); |
| 13 | } |
| 14 | |
| 15 | function exposed_setChain(string memory chainAlias, ChainData memory chainData) public { |
| 16 | setChain(chainAlias, chainData); |
| 17 | } |
| 18 | |
| 19 | function exposed_setFallbackToDefaultRpcUrls(bool useDefault) public { |
| 20 | setFallbackToDefaultRpcUrls(useDefault); |
| 21 | } |
| 22 | } |
| 23 | |
| 24 | contract StdChainsTest is Test { |
| 25 | function test_ChainRpcInitialization() public { |
| 26 | // RPCs specified in `foundry.toml` should be updated. |
| 27 | assertEq(getChain(1).rpcUrl, "https://eth.merkle.io"); |
| 28 | assertEq(getChain("optimism_sepolia").rpcUrl, "https://sepolia.optimism.io/"); |
| 29 | assertEq(getChain("arbitrum_one_sepolia").rpcUrl, "https://sepolia-rollup.arbitrum.io/rpc/"); |
| 30 | |
| 31 | // Environment variables should be the next fallback |
| 32 | assertEq(getChain("arbitrum_nova").rpcUrl, "https://nova.arbitrum.io/rpc"); |
| 33 | vm.setEnv("ARBITRUM_NOVA_RPC_URL", "myoverride"); |
| 34 | assertEq(getChain("arbitrum_nova").rpcUrl, "myoverride"); |
| 35 | vm.setEnv("ARBITRUM_NOVA_RPC_URL", "https://nova.arbitrum.io/rpc"); |
| 36 | |
| 37 | // Cannot override RPCs defined in `foundry.toml` |
| 38 | vm.setEnv("MAINNET_RPC_URL", "myoverride2"); |
| 39 | assertEq(getChain("mainnet").rpcUrl, "https://eth.merkle.io"); |
| 40 | |
| 41 | // Other RPCs should remain unchanged. |
| 42 | assertEq(getChain(31337).rpcUrl, "http://127.0.0.1:8545"); |
| 43 | assertEq(getChain("sepolia").rpcUrl, "https://sepolia.infura.io/v3/b9794ad1ddf84dfb8c34d6bb5dca2001"); |
| 44 | } |
| 45 | |
| 46 | // Named with a leading underscore to clarify this is not intended to be run as a normal test, |
| 47 | // and is intended to be used in the below `test_Rpcs` test. |
| 48 | function _testRpc(string memory rpcAlias) internal { |
| 49 | string memory rpcUrl = getChain(rpcAlias).rpcUrl; |
| 50 | vm.createSelectFork(rpcUrl); |
| 51 | } |
| 52 | |
| 53 | // Ensure we can connect to the default RPC URL for each chain. |
| 54 | // Currently commented out since this is slow and public RPCs are flaky, often resulting in failing CI. |
| 55 | // function test_Rpcs() public { |
| 56 | // _testRpc("mainnet"); |
| 57 | // _testRpc("sepolia"); |
| 58 | // _testRpc("holesky"); |
| 59 | // _testRpc("optimism"); |
| 60 | // _testRpc("optimism_sepolia"); |
| 61 | // _testRpc("arbitrum_one"); |
| 62 | // _testRpc("arbitrum_one_sepolia"); |
| 63 | // _testRpc("arbitrum_nova"); |
| 64 | // _testRpc("polygon"); |
| 65 | // _testRpc("polygon_amoy"); |
| 66 | // _testRpc("avalanche"); |
| 67 | // _testRpc("avalanche_fuji"); |
| 68 | // _testRpc("bnb_smart_chain"); |
| 69 | // _testRpc("bnb_smart_chain_testnet"); |
| 70 | // _testRpc("gnosis_chain"); |
| 71 | // _testRpc("moonbeam"); |
| 72 | // _testRpc("moonriver"); |
| 73 | // _testRpc("moonbase"); |
| 74 | // _testRpc("base_sepolia"); |
| 75 | // _testRpc("base"); |
| 76 | // _testRpc("blast_sepolia"); |
| 77 | // _testRpc("blast"); |
| 78 | // _testRpc("fantom_opera"); |
| 79 | // _testRpc("fantom_opera_testnet"); |
| 80 | // _testRpc("fraxtal"); |
| 81 | // _testRpc("fraxtal_testnet"); |
| 82 | // _testRpc("berachain_bartio_testnet"); |
| 83 | // _testRpc("flare"); |
| 84 | // _testRpc("flare_coston2"); |
| 85 | // } |
| 86 | |
| 87 | function test_RevertIf_ChainNotFound() public { |
| 88 | // We deploy a mock to properly test the revert. |
| 89 | StdChainsMock stdChainsMock = new StdChainsMock(); |
| 90 | |
| 91 | vm.expectRevert("StdChains getChain(string): Chain with alias \"does_not_exist\" not found."); |
| 92 | stdChainsMock.exposed_getChain("does_not_exist"); |
| 93 | } |
| 94 | |
| 95 | function test_RevertIf_SetChain_ChainIdExist_FirstTest() public { |
| 96 | // We deploy a mock to properly test the revert. |
| 97 | StdChainsMock stdChainsMock = new StdChainsMock(); |
| 98 | |
| 99 | vm.expectRevert("StdChains setChain(string,ChainData): Chain ID 31337 already used by \"anvil\"."); |
| 100 | stdChainsMock.exposed_setChain("anvil2", ChainData("Anvil", 31337, "URL")); |
| 101 | } |
| 102 | |
| 103 | function test_RevertIf_ChainBubbleUp() public { |
| 104 | // We deploy a mock to properly test the revert. |
| 105 | StdChainsMock stdChainsMock = new StdChainsMock(); |
| 106 | |
| 107 | stdChainsMock.exposed_setChain("needs_undefined_env_var", ChainData("", 123456789, "")); |
| 108 | // Forge environment variable error. |
| 109 | vm.expectRevert(); |
| 110 | stdChainsMock.exposed_getChain("needs_undefined_env_var"); |
| 111 | } |
| 112 | |
| 113 | function test_RevertIf_SetChain_ChainIdExists_SecondTest() public { |
| 114 | // We deploy a mock to properly test the revert. |
| 115 | StdChainsMock stdChainsMock = new StdChainsMock(); |
| 116 | |
| 117 | stdChainsMock.exposed_setChain("custom_chain", ChainData("Custom Chain", 123456789, "https://custom.chain/")); |
| 118 | |
| 119 | vm.expectRevert('StdChains setChain(string,ChainData): Chain ID 123456789 already used by "custom_chain".'); |
| 120 | |
| 121 | stdChainsMock.exposed_setChain("another_custom_chain", ChainData("", 123456789, "")); |
| 122 | } |
| 123 | |
| 124 | function test_SetChain() public { |
| 125 | setChain("custom_chain", ChainData("Custom Chain", 123456789, "https://custom.chain/")); |
| 126 | Chain memory customChain = getChain("custom_chain"); |
| 127 | assertEq(customChain.name, "Custom Chain"); |
| 128 | assertEq(customChain.chainId, 123456789); |
| 129 | assertEq(customChain.chainAlias, "custom_chain"); |
| 130 | assertEq(customChain.rpcUrl, "https://custom.chain/"); |
| 131 | Chain memory chainById = getChain(123456789); |
| 132 | assertEq(chainById.name, customChain.name); |
| 133 | assertEq(chainById.chainId, customChain.chainId); |
| 134 | assertEq(chainById.chainAlias, customChain.chainAlias); |
| 135 | assertEq(chainById.rpcUrl, customChain.rpcUrl); |
| 136 | customChain.name = "Another Custom Chain"; |
| 137 | customChain.chainId = 987654321; |
| 138 | setChain("another_custom_chain", customChain); |
| 139 | Chain memory anotherCustomChain = getChain("another_custom_chain"); |
| 140 | assertEq(anotherCustomChain.name, "Another Custom Chain"); |
| 141 | assertEq(anotherCustomChain.chainId, 987654321); |
| 142 | assertEq(anotherCustomChain.chainAlias, "another_custom_chain"); |
| 143 | assertEq(anotherCustomChain.rpcUrl, "https://custom.chain/"); |
| 144 | // Verify the first chain data was not overwritten |
| 145 | chainById = getChain(123456789); |
| 146 | assertEq(chainById.name, "Custom Chain"); |
| 147 | assertEq(chainById.chainId, 123456789); |
| 148 | } |
| 149 | |
| 150 | function test_RevertIf_SetEmptyAlias() public { |
| 151 | // We deploy a mock to properly test the revert. |
| 152 | StdChainsMock stdChainsMock = new StdChainsMock(); |
| 153 | |
| 154 | vm.expectRevert("StdChains setChain(string,ChainData): Chain alias cannot be the empty string."); |
| 155 | stdChainsMock.exposed_setChain("", ChainData("", 123456789, "")); |
| 156 | } |
| 157 | |
| 158 | function test_RevertIf_SetNoChainId0() public { |
| 159 | // We deploy a mock to properly test the revert. |
| 160 | StdChainsMock stdChainsMock = new StdChainsMock(); |
| 161 | |
| 162 | vm.expectRevert("StdChains setChain(string,ChainData): Chain ID cannot be 0."); |
| 163 | stdChainsMock.exposed_setChain("alias", ChainData("", 0, "")); |
| 164 | } |
| 165 | |
| 166 | function test_RevertIf_GetNoChainId0() public { |
| 167 | // We deploy a mock to properly test the revert. |
| 168 | StdChainsMock stdChainsMock = new StdChainsMock(); |
| 169 | |
| 170 | vm.expectRevert("StdChains getChain(uint256): Chain ID cannot be 0."); |
| 171 | stdChainsMock.exposed_getChain(0); |
| 172 | } |
| 173 | |
| 174 | function test_RevertIf_GetNoEmptyAlias() public { |
| 175 | // We deploy a mock to properly test the revert. |
| 176 | StdChainsMock stdChainsMock = new StdChainsMock(); |
| 177 | |
| 178 | vm.expectRevert("StdChains getChain(string): Chain alias cannot be the empty string."); |
| 179 | stdChainsMock.exposed_getChain(""); |
| 180 | } |
| 181 | |
| 182 | function test_RevertIf_ChainIdNotFound() public { |
| 183 | // We deploy a mock to properly test the revert. |
| 184 | StdChainsMock stdChainsMock = new StdChainsMock(); |
| 185 | |
| 186 | vm.expectRevert("StdChains getChain(string): Chain with alias \"no_such_alias\" not found."); |
| 187 | stdChainsMock.exposed_getChain("no_such_alias"); |
| 188 | } |
| 189 | |
| 190 | function test_RevertIf_ChainAliasNotFound() public { |
| 191 | // We deploy a mock to properly test the revert. |
| 192 | StdChainsMock stdChainsMock = new StdChainsMock(); |
| 193 | |
| 194 | vm.expectRevert("StdChains getChain(uint256): Chain with ID 321 not found."); |
| 195 | |
| 196 | stdChainsMock.exposed_getChain(321); |
| 197 | } |
| 198 | |
| 199 | function test_SetChain_ExistingOne() public { |
| 200 | // We deploy a mock to properly test the revert. |
| 201 | StdChainsMock stdChainsMock = new StdChainsMock(); |
| 202 | |
| 203 | setChain("custom_chain", ChainData("Custom Chain", 123456789, "https://custom.chain/")); |
| 204 | assertEq(getChain(123456789).chainId, 123456789); |
| 205 | |
| 206 | setChain("custom_chain", ChainData("Modified Chain", 9999999999999999999, "https://modified.chain/")); |
| 207 | vm.expectRevert("StdChains getChain(uint256): Chain with ID 123456789 not found."); |
| 208 | stdChainsMock.exposed_getChain(123456789); |
| 209 | |
| 210 | Chain memory modifiedChain = getChain(9999999999999999999); |
| 211 | assertEq(modifiedChain.name, "Modified Chain"); |
| 212 | assertEq(modifiedChain.chainId, 9999999999999999999); |
| 213 | assertEq(modifiedChain.rpcUrl, "https://modified.chain/"); |
| 214 | } |
| 215 | |
| 216 | function test_RevertIf_DontUseDefaultRpcUrl() public { |
| 217 | // We deploy a mock to properly test the revert. |
| 218 | StdChainsMock stdChainsMock = new StdChainsMock(); |
| 219 | |
| 220 | // Should error if default RPCs flag is set to false. |
| 221 | stdChainsMock.exposed_setFallbackToDefaultRpcUrls(false); |
| 222 | vm.expectRevert(); |
| 223 | stdChainsMock.exposed_getChain(31337); |
| 224 | vm.expectRevert(); |
| 225 | stdChainsMock.exposed_getChain("sepolia"); |
| 226 | } |
| 227 | } |