Skip to content

Commit f50a63a

Browse files
authored
Fix slow compilation with via-ir (#217)
* refactor: change stdChains to a mapping * ci: add a test contract to compile Test in CI * fix: no more stack too deep (once foundry-rs/foundry#3659 is merged) * ci: more granular build results * fix: add ABIEncoderV2 pragma to compilation test * refactor: cleanup code, more compilation tests * build: add view modifiers to silence compiler warnings * ci/refactor: more via-ir benchmarks, now in test dir * chore: udpate chain names * chore: fix test name * ci: reorder checks, add 0.6.12
1 parent 51be71e commit f50a63a

File tree

10 files changed

+138
-102
lines changed

10 files changed

+138
-102
lines changed

.github/workflows/ci.yml

Lines changed: 37 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,43 @@ jobs:
2121
- name: Install dependencies
2222
run: forge install
2323

24-
- name: Check backward compatibility
25-
run: |
26-
forge build --skip test --use solc:0.8.0
27-
forge build --skip test --use solc:0.7.6
28-
forge build --skip test --use solc:0.7.0
29-
forge build --skip test --use solc:0.6.2
24+
# Backwards compatibility checks.
25+
- name: Check compatibility with 0.8.0
26+
if: always()
27+
run: forge build --skip test --use solc:0.8.0
28+
29+
- name: Check compatibility with 0.7.6
30+
if: always()
31+
run: forge build --skip test --use solc:0.7.6
32+
33+
- name: Check compatibility with 0.7.0
34+
if: always()
35+
run: forge build --skip test --use solc:0.7.0
36+
37+
- name: Check compatibility with 0.6.12
38+
if: always()
39+
run: forge build --skip test --use solc:0.6.12
40+
41+
- name: Check compatibility with 0.6.2
42+
if: always()
43+
run: forge build --skip test --use solc:0.6.2
44+
45+
# via-ir compilation time checks.
46+
- name: Measure compilation time of Test with 0.8.17 --via-ir
47+
if: always()
48+
run: forge build --skip test --contracts test/compilation/CompilationTest.sol --use solc:0.8.17 --via-ir
49+
50+
- name: Measure compilation time of TestBase with 0.8.17 --via-ir
51+
if: always()
52+
run: forge build --skip test --contracts test/compilation/CompilationTestBase.sol --use solc:0.8.17 --via-ir
53+
54+
- name: Measure compilation time of Script with 0.8.17 --via-ir
55+
if: always()
56+
run: forge build --skip test --contracts test/compilation/CompilationScript.sol --use solc:0.8.17 --via-ir
57+
58+
- name: Measure compilation time of ScriptBase with 0.8.17 --via-ir
59+
if: always()
60+
run: forge build --skip test --contracts test/compilation/CompilationScriptBase.sol --use solc:0.8.17 --via-ir
3061

3162
test:
3263
runs-on: ubuntu-latest

foundry.toml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,10 @@
22
fs_permissions = [{ access = "read-write", path = "./"}]
33

44
[rpc_endpoints]
5-
# We intentionally use both dashes and underscores in the key names to ensure both are supported.
65
# The RPC URLs below match the StdChains URLs but append a trailing slash for testing.
76
mainnet = "https://api.mycryptoapi.com/eth/"
87
optimism_goerli = "https://goerli.optimism.io/"
9-
arbitrum-one-goerli = "https://goerli-rollup.arbitrum.io/rpc/"
8+
arbitrum_one_goerli = "https://goerli-rollup.arbitrum.io/rpc/"
109

1110
[fmt]
1211
# These are all the `forge fmt` defaults.

src/StdCheats.sol

Lines changed: 36 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -26,27 +26,8 @@ abstract contract StdCheatsSafe {
2626
string rpcUrl;
2727
}
2828

29-
struct Chains {
30-
Chain Anvil;
31-
Chain Hardhat;
32-
Chain Mainnet;
33-
Chain Goerli;
34-
Chain Sepolia;
35-
Chain Optimism;
36-
Chain OptimismGoerli;
37-
Chain ArbitrumOne;
38-
Chain ArbitrumOneGoerli;
39-
Chain ArbitrumNova;
40-
Chain Polygon;
41-
Chain PolygonMumbai;
42-
Chain Avalanche;
43-
Chain AvalancheFuji;
44-
Chain BnbSmartChain;
45-
Chain BnbSmartChainTestnet;
46-
Chain GnosisChain;
47-
}
48-
49-
Chains stdChains;
29+
// Maps from a chain's name (matching what's in the `foundry.toml` file) to chain data.
30+
mapping(string => Chain) internal stdChains;
5031

5132
// Data structures to parse Transaction objects from the broadcast artifact
5233
// that conform to EIP1559. The Raw structs is what is parsed from the JSON
@@ -227,63 +208,33 @@ abstract contract StdCheatsSafe {
227208

228209
function _constructor() private returns (uint256) {
229210
// Initialize `stdChains` with the defaults.
230-
stdChains = Chains({
231-
Anvil: Chain("Anvil", 31337, "http://127.0.0.1:8545"),
232-
Hardhat: Chain("Hardhat", 31337, "http://127.0.0.1:8545"),
233-
Mainnet: Chain("Mainnet", 1, "https://api.mycryptoapi.com/eth"),
234-
Goerli: Chain("Goerli", 5, "https://goerli.infura.io/v3/84842078b09946638c03157f83405213"), // Default Infura key from ethers.js: https://github.com/ethers-io/ethers.js/blob/c80fcddf50a9023486e9f9acb1848aba4c19f7b6/packages/providers/src.ts/infura-provider.ts
235-
Sepolia: Chain("Sepolia", 11155111, "https://rpc.sepolia.dev"),
236-
Optimism: Chain("Optimism", 10, "https://mainnet.optimism.io"),
237-
OptimismGoerli: Chain("OptimismGoerli", 420, "https://goerli.optimism.io"),
238-
ArbitrumOne: Chain("ArbitrumOne", 42161, "https://arb1.arbitrum.io/rpc"),
239-
ArbitrumOneGoerli: Chain("ArbitrumOneGoerli", 421613, "https://goerli-rollup.arbitrum.io/rpc"),
240-
ArbitrumNova: Chain("ArbitrumNova", 42170, "https://nova.arbitrum.io/rpc"),
241-
Polygon: Chain("Polygon", 137, "https://polygon-rpc.com"),
242-
PolygonMumbai: Chain("PolygonMumbai", 80001, "https://rpc-mumbai.matic.today"),
243-
Avalanche: Chain("Avalanche", 43114, "https://api.avax.network/ext/bc/C/rpc"),
244-
AvalancheFuji: Chain("AvalancheFuji", 43113, "https://api.avax-test.network/ext/bc/C/rpc"),
245-
BnbSmartChain: Chain("BnbSmartChain", 56, "https://bsc-dataseed1.binance.org"),
246-
BnbSmartChainTestnet: Chain("BnbSmartChainTestnet", 97, "https://data-seed-prebsc-1-s1.binance.org:8545"),
247-
GnosisChain: Chain("GnosisChain", 100, "https://rpc.gnosischain.com")
248-
});
211+
stdChains["anvil"] = Chain("Anvil", 31337, "http://127.0.0.1:8545");
212+
stdChains["hardhat"] = Chain("Hardhat", 31337, "http://127.0.0.1:8545");
213+
stdChains["mainnet"] = Chain("Mainnet", 1, "https://api.mycryptoapi.com/eth");
214+
stdChains["goerli"] = Chain("Goerli", 5, "https://goerli.infura.io/v3/6770454bc6ea42c58aac12978531b93f");
215+
stdChains["sepolia"] = Chain("Sepolia", 11155111, "https://rpc.sepolia.dev");
216+
stdChains["optimism"] = Chain("Optimism", 10, "https://mainnet.optimism.io");
217+
stdChains["optimism_goerli"] = Chain("Optimism Goerli", 420, "https://goerli.optimism.io");
218+
stdChains["arbitrum_one"] = Chain("Arbitrum One", 42161, "https://arb1.arbitrum.io/rpc");
219+
stdChains["arbitrum_one_goerli"] = Chain("Arbitrum One Goerli", 421613, "https://goerli-rollup.arbitrum.io/rpc");
220+
stdChains["arbitrum_nova"] = Chain("Arbitrum Nova", 42170, "https://nova.arbitrum.io/rpc");
221+
stdChains["polygon"] = Chain("Polygon", 137, "https://polygon-rpc.com");
222+
stdChains["polygon_mumbai"] = Chain("Polygon Mumbai", 80001, "https://rpc-mumbai.matic.today");
223+
stdChains["avalanche"] = Chain("Avalanche", 43114, "https://api.avax.network/ext/bc/C/rpc");
224+
stdChains["avalanche_fuji"] = Chain("Avalanche Fuji", 43113, "https://api.avax-test.network/ext/bc/C/rpc");
225+
stdChains["bnb_smart_chain"] = Chain("BNB Smart Chain", 56, "https://bsc-dataseed1.binance.org");
226+
stdChains["bnb_smart_chain_testnet"] = Chain("BNB Smart Chain Testnet", 97, "https://data-seed-prebsc-1-s1.binance.org:8545");// forgefmt: disable-line
227+
stdChains["gnosis_chain"] = Chain("Gnosis Chain", 100, "https://rpc.gnosischain.com");
249228

250229
// Loop over RPC URLs in the config file to replace the default RPC URLs
251-
(string[2][] memory rpcs) = vm.rpcUrls();
230+
Vm.Rpc[] memory rpcs = vm.rpcUrlStructs();
252231
for (uint256 i = 0; i < rpcs.length; i++) {
253-
(string memory name, string memory rpcUrl) = (rpcs[i][0], rpcs[i][1]);
254-
// forgefmt: disable-start
255-
if (isEqual(name, "anvil")) stdChains.Anvil.rpcUrl = rpcUrl;
256-
else if (isEqual(name, "hardhat")) stdChains.Hardhat.rpcUrl = rpcUrl;
257-
else if (isEqual(name, "mainnet")) stdChains.Mainnet.rpcUrl = rpcUrl;
258-
else if (isEqual(name, "goerli")) stdChains.Goerli.rpcUrl = rpcUrl;
259-
else if (isEqual(name, "sepolia")) stdChains.Sepolia.rpcUrl = rpcUrl;
260-
else if (isEqual(name, "optimism")) stdChains.Optimism.rpcUrl = rpcUrl;
261-
else if (isEqual(name, "optimism_goerli", "optimism-goerli")) stdChains.OptimismGoerli.rpcUrl = rpcUrl;
262-
else if (isEqual(name, "arbitrum_one", "arbitrum-one")) stdChains.ArbitrumOne.rpcUrl = rpcUrl;
263-
else if (isEqual(name, "arbitrum_one_goerli", "arbitrum-one-goerli")) stdChains.ArbitrumOneGoerli.rpcUrl = rpcUrl;
264-
else if (isEqual(name, "arbitrum_nova", "arbitrum-nova")) stdChains.ArbitrumNova.rpcUrl = rpcUrl;
265-
else if (isEqual(name, "polygon")) stdChains.Polygon.rpcUrl = rpcUrl;
266-
else if (isEqual(name, "polygon_mumbai", "polygon-mumbai")) stdChains.PolygonMumbai.rpcUrl = rpcUrl;
267-
else if (isEqual(name, "avalanche")) stdChains.Avalanche.rpcUrl = rpcUrl;
268-
else if (isEqual(name, "avalanche_fuji", "avalanche-fuji")) stdChains.AvalancheFuji.rpcUrl = rpcUrl;
269-
else if (isEqual(name, "bnb_smart_chain", "bnb-smart-chain")) stdChains.BnbSmartChain.rpcUrl = rpcUrl;
270-
else if (isEqual(name, "bnb_smart_chain_testnet", "bnb-smart-chain-testnet")) stdChains.BnbSmartChainTestnet.rpcUrl = rpcUrl;
271-
else if (isEqual(name, "gnosis_chain", "gnosis-chain")) stdChains.GnosisChain.rpcUrl = rpcUrl;
272-
// forgefmt: disable-end
232+
stdChains[rpcs[i].name].rpcUrl = rpcs[i].url;
273233
}
274234
return 0;
275235
}
276236

277-
function isEqual(string memory a, string memory b) private pure returns (bool) {
278-
return keccak256(abi.encode(a)) == keccak256(abi.encode(b));
279-
}
280-
281-
function isEqual(string memory a, string memory b, string memory c) private pure returns (bool) {
282-
return
283-
keccak256(abi.encode(a)) == keccak256(abi.encode(b)) || keccak256(abi.encode(a)) == keccak256(abi.encode(c));
284-
}
285-
286-
function assumeNoPrecompiles(address addr) internal virtual {
237+
function assumeNoPrecompiles(address addr) internal view virtual {
287238
// Assembly required since `block.chainid` was introduced in 0.8.0.
288239
uint256 chainId;
289240
assembly {
@@ -292,21 +243,21 @@ abstract contract StdCheatsSafe {
292243
assumeNoPrecompiles(addr, chainId);
293244
}
294245

295-
function assumeNoPrecompiles(address addr, uint256 chainId) internal virtual {
246+
function assumeNoPrecompiles(address addr, uint256 chainId) internal view virtual {
296247
// Note: For some chains like Optimism these are technically predeploys (i.e. bytecode placed at a specific
297248
// address), but the same rationale for excluding them applies so we include those too.
298249

299250
// These should be present on all EVM-compatible chains.
300251
vm.assume(addr < address(0x1) || addr > address(0x9));
301252

302253
// forgefmt: disable-start
303-
if (chainId == stdChains.Optimism.chainId || chainId == stdChains.OptimismGoerli.chainId) {
254+
if (chainId == stdChains["optimism"].chainId || chainId == stdChains["optimism_goerli"].chainId) {
304255
// https://github.com/ethereum-optimism/optimism/blob/eaa371a0184b56b7ca6d9eb9cb0a2b78b2ccd864/op-bindings/predeploys/addresses.go#L6-L21
305256
vm.assume(addr < address(0x4200000000000000000000000000000000000000) || addr > address(0x4200000000000000000000000000000000000800));
306-
} else if (chainId == stdChains.ArbitrumOne.chainId || chainId == stdChains.ArbitrumOneGoerli.chainId) {
257+
} else if (chainId == stdChains["arbitrum_one"].chainId || chainId == stdChains["arbitrum_one_goerli"].chainId) {
307258
// https://developer.arbitrum.io/useful-addresses#arbitrum-precompiles-l2-same-on-all-arb-chains
308259
vm.assume(addr < address(0x0000000000000000000000000000000000000064) || addr > address(0x0000000000000000000000000000000000000068));
309-
} else if (chainId == stdChains.Avalanche.chainId || chainId == stdChains.AvalancheFuji.chainId) {
260+
} else if (chainId == stdChains["avalanche"].chainId || chainId == stdChains["avalanche_fuji"].chainId) {
310261
// https://github.com/ava-labs/subnet-evm/blob/47c03fd007ecaa6de2c52ea081596e0a88401f58/precompile/params.go#L18-L59
311262
vm.assume(addr < address(0x0100000000000000000000000000000000000000) || addr > address(0x01000000000000000000000000000000000000ff));
312263
vm.assume(addr < address(0x0200000000000000000000000000000000000000) || addr > address(0x02000000000000000000000000000000000000FF));
@@ -315,7 +266,12 @@ abstract contract StdCheatsSafe {
315266
// forgefmt: disable-end
316267
}
317268

318-
function readEIP1559ScriptArtifact(string memory path) internal virtual returns (EIP1559ScriptArtifact memory) {
269+
function readEIP1559ScriptArtifact(string memory path)
270+
internal
271+
view
272+
virtual
273+
returns (EIP1559ScriptArtifact memory)
274+
{
319275
string memory data = vm.readFile(path);
320276
bytes memory parsedData = vm.parseJson(data);
321277
RawEIP1559ScriptArtifact memory rawArtifact = abi.decode(parsedData, (RawEIP1559ScriptArtifact));
@@ -367,14 +323,14 @@ abstract contract StdCheatsSafe {
367323
return txDetail;
368324
}
369325

370-
function readTx1559s(string memory path) internal virtual returns (Tx1559[] memory) {
326+
function readTx1559s(string memory path) internal view virtual returns (Tx1559[] memory) {
371327
string memory deployData = vm.readFile(path);
372328
bytes memory parsedDeployData = vm.parseJson(deployData, ".transactions");
373329
RawTx1559[] memory rawTxs = abi.decode(parsedDeployData, (RawTx1559[]));
374330
return rawToConvertedEIPTx1559s(rawTxs);
375331
}
376332

377-
function readTx1559(string memory path, uint256 index) internal virtual returns (Tx1559 memory) {
333+
function readTx1559(string memory path, uint256 index) internal view virtual returns (Tx1559 memory) {
378334
string memory deployData = vm.readFile(path);
379335
string memory key = string(abi.encodePacked(".transactions[", vm.toString(index), "]"));
380336
bytes memory parsedDeployData = vm.parseJson(deployData, key);
@@ -383,14 +339,14 @@ abstract contract StdCheatsSafe {
383339
}
384340

385341
// Analogous to readTransactions, but for receipts.
386-
function readReceipts(string memory path) internal virtual returns (Receipt[] memory) {
342+
function readReceipts(string memory path) internal view virtual returns (Receipt[] memory) {
387343
string memory deployData = vm.readFile(path);
388344
bytes memory parsedDeployData = vm.parseJson(deployData, ".receipts");
389345
RawReceipt[] memory rawReceipts = abi.decode(parsedDeployData, (RawReceipt[]));
390346
return rawToConvertedReceipts(rawReceipts);
391347
}
392348

393-
function readReceipt(string memory path, uint256 index) internal virtual returns (Receipt memory) {
349+
function readReceipt(string memory path, uint256 index) internal view virtual returns (Receipt memory) {
394350
string memory deployData = vm.readFile(path);
395351
string memory key = string(abi.encodePacked(".receipts[", vm.toString(index), "]"));
396352
bytes memory parsedDeployData = vm.parseJson(deployData, key);

src/Vm.sol

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,11 @@ interface VmSafe {
1616
address emitter;
1717
}
1818

19+
struct Rpc {
20+
string name;
21+
string url;
22+
}
23+
1924
// Loads a storage slot from an address (who, slot)
2025
function load(address, bytes32) external view returns (bytes32);
2126
// Signs data, (privateKey, digest) => (v, r, s)
@@ -191,6 +196,8 @@ interface VmSafe {
191196
function rpcUrl(string calldata) external view returns (string memory);
192197
// Returns all rpc urls and their aliases `[alias, url][]`
193198
function rpcUrls() external view returns (string[2][] memory);
199+
// Returns all rpc urls and their aliases as structs.
200+
function rpcUrlStructs() external view returns (Rpc[] memory);
194201
// If the condition is false, discard this run's fuzz inputs and generate new ones.
195202
function assume(bool) external pure;
196203
}

test/StdCheats.t.sol

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -186,15 +186,15 @@ contract StdCheatsTest is Test {
186186
assertEq(txDetail.value, 0);
187187
}
188188

189-
function testReadEIP1559Transaction() public {
189+
function testReadEIP1559Transaction() public view {
190190
string memory root = vm.projectRoot();
191191
string memory path = string.concat(root, "/test/fixtures/broadcast.log.json");
192192
uint256 index = 0;
193193
Tx1559 memory transaction = readTx1559(path, index);
194194
transaction;
195195
}
196196

197-
function testReadEIP1559Transactions() public {
197+
function testReadEIP1559Transactions() public view {
198198
string memory root = vm.projectRoot();
199199
string memory path = string.concat(root, "/test/fixtures/broadcast.log.json");
200200
Tx1559[] memory transactions = readTx1559s(path);
@@ -212,7 +212,7 @@ contract StdCheatsTest is Test {
212212
);
213213
}
214214

215-
function testReadReceipts() public {
215+
function testReadReceipts() public view {
216216
string memory root = vm.projectRoot();
217217
string memory path = string.concat(root, "/test/fixtures/broadcast.log.json");
218218
Receipt[] memory receipts = readReceipts(path);
@@ -229,14 +229,14 @@ contract StdCheatsTest is Test {
229229

230230
function testChainRpcInitialization() public {
231231
// RPCs specified in `foundry.toml` should be updated.
232-
assertEq(stdChains.Mainnet.rpcUrl, "https://api.mycryptoapi.com/eth/");
233-
assertEq(stdChains.OptimismGoerli.rpcUrl, "https://goerli.optimism.io/");
234-
assertEq(stdChains.ArbitrumOneGoerli.rpcUrl, "https://goerli-rollup.arbitrum.io/rpc/");
232+
assertEq(stdChains["mainnet"].rpcUrl, "https://api.mycryptoapi.com/eth/");
233+
assertEq(stdChains["optimism_goerli"].rpcUrl, "https://goerli.optimism.io/");
234+
assertEq(stdChains["arbitrum_one_goerli"].rpcUrl, "https://goerli-rollup.arbitrum.io/rpc/");
235235

236236
// Other RPCs should remain unchanged.
237-
assertEq(stdChains.Anvil.rpcUrl, "http://127.0.0.1:8545");
238-
assertEq(stdChains.Hardhat.rpcUrl, "http://127.0.0.1:8545");
239-
assertEq(stdChains.Sepolia.rpcUrl, "https://rpc.sepolia.dev");
237+
assertEq(stdChains["anvil"].rpcUrl, "http://127.0.0.1:8545");
238+
assertEq(stdChains["hardhat"].rpcUrl, "http://127.0.0.1:8545");
239+
assertEq(stdChains["sepolia"].rpcUrl, "https://rpc.sepolia.dev");
240240
}
241241

242242
// Ensure we can connect to the default RPC URL for each chain.
@@ -247,6 +247,14 @@ contract StdCheatsTest is Test {
247247
vm.createSelectFork(rpcUrl);
248248
}
249249
}
250+
251+
function testAssumeNoPrecompiles(address addr) external {
252+
assumeNoPrecompiles(addr, stdChains["optimism_goerli"].chainId);
253+
assertTrue(
254+
addr < address(1) || (addr > address(9) && addr < address(0x4200000000000000000000000000000000000000))
255+
|| addr > address(0x4200000000000000000000000000000000000800)
256+
);
257+
}
250258
}
251259

252260
contract Bar {

test/StdUtils.t.sol

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -88,9 +88,4 @@ contract StdUtilsTest is Test {
8888
address create2Address = computeCreate2Address(salt, initcodeHash, deployer);
8989
assertEq(create2Address, 0xB147a5d25748fda14b463EB04B111027C290f4d3);
9090
}
91-
92-
function testAssumeNoPrecompilesL1(address addr) external {
93-
assumeNoPrecompiles(addr, stdChains.Mainnet.chainId);
94-
assertTrue(addr < address(1) || addr > address(9));
95-
}
9691
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity >=0.6.2 <0.9.0;
3+
4+
pragma experimental ABIEncoderV2;
5+
6+
import "../../src/Script.sol";
7+
8+
// The purpose of this contract is to benchmark compilation time to avoid accidentally introducing
9+
// a change that results in very long compilation times with via-ir. See https://github.com/foundry-rs/forge-std/issues/207
10+
contract CompilationScript is Script {}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity >=0.6.2 <0.9.0;
3+
4+
pragma experimental ABIEncoderV2;
5+
6+
import "../../src/Script.sol";
7+
8+
// The purpose of this contract is to benchmark compilation time to avoid accidentally introducing
9+
// a change that results in very long compilation times with via-ir. See https://github.com/foundry-rs/forge-std/issues/207
10+
contract CompilationScriptBase is ScriptBase {}

test/compilation/CompilationTest.sol

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity >=0.6.2 <0.9.0;
3+
4+
pragma experimental ABIEncoderV2;
5+
6+
import "../../src/Test.sol";
7+
8+
// The purpose of this contract is to benchmark compilation time to avoid accidentally introducing
9+
// a change that results in very long compilation times with via-ir. See https://github.com/foundry-rs/forge-std/issues/207
10+
contract CompilationTest is Test {}

0 commit comments

Comments
 (0)