├── .github └── workflows │ └── test.yml ├── .gitignore ├── .gitmodules ├── README.md ├── foundry.toml ├── src-0_6_x └── Contract6.sol └── src ├── Contract.s.sol ├── Contract.sol └── Contract.t.sol /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: test 2 | 3 | on: workflow_dispatch 4 | 5 | env: 6 | FOUNDRY_PROFILE: ci 7 | 8 | jobs: 9 | check: 10 | strategy: 11 | fail-fast: true 12 | 13 | name: Foundry project 14 | runs-on: ubuntu-latest 15 | steps: 16 | - uses: actions/checkout@v3 17 | with: 18 | submodules: recursive 19 | 20 | - name: Install Foundry 21 | uses: foundry-rs/foundry-toolchain@v1 22 | with: 23 | version: nightly 24 | 25 | - name: Run Forge build 26 | run: | 27 | forge --version 28 | forge build --sizes 29 | id: build 30 | 31 | - name: Run Forge tests 32 | run: | 33 | forge test -vvv 34 | id: test 35 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiler files 2 | cache/ 3 | out/ 4 | 5 | # Ignores development broadcast logs 6 | !/broadcast 7 | /broadcast/* 8 | /broadcast/*/31337/ 9 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "lib/forge-std"] 2 | path = lib/forge-std 3 | url = https://github.com/foundry-rs/forge-std 4 | [submodule "lib-0_6_x/mip21-toolkit"] 5 | path = lib-0_6_x/mip21-toolkit 6 | url = https://github.com/clio-finance/mip21-toolkit 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Using `forge` with multiple `solc` versions 2 | 3 | This is a proof of concept to enable multi-version Solidity projects with [Foundry](https://book.getfoundry.sh/). 4 | 5 | This example uses Solidity `0.6.12` and `0.8.14`, but in theory it could work for any two or more versions. 6 | 7 | ## Rationale 8 | 9 | - Use Foundry built-in [profiles](https://book.getfoundry.sh/reference/config#profiles) for different Solidity versions. 10 | Use the `default` profile for the "main" version (`0.8.14` in this case). 11 | - Have separate `src` directories for each version. 12 | - Have separate `lib` directories for each version, but allow the non-default versions to pull compatible dependencies 13 | from the default one. 14 | - Test files and Solidity scripts go into the `src` directories and not in separate `test` and `script` ones. 15 | 16 | ## Usage 17 | 18 | Compile the non-default versions first: 19 | ``` 20 | FOUNDRY_PROFILE=0_6_x forge build 21 | ``` 22 | 23 | Compile the default version last: 24 | ``` 25 | forge build 26 | ``` 27 | 28 | --- 29 | 30 | **⚠️ WARNING:** `forge` will overwrite the compiled files in `out/` for whichever contracts that were compiled with 31 | `FOUNDRY_PROFILE=0_6_x` with the compiled files using the default profile. In other words, any contracts that were 32 | accessible with the `0.6.x` compiler will be overwritten by the default profile using Solidity `0.8.x` if the commands 33 | are run in the order above. Keep that in mind if you are using this approach to deploy contracts, you might end up 34 | submitting the bytecode generated by a different compiler version than what you intended. 35 | 36 | --- 37 | 38 | To reference contracts from compiled with versions, use the [`getCode()` cheat code][get-code]. 39 | 40 | [get-code]: https://book.getfoundry.sh/cheatcodes/get-code 41 | 42 | ```solidity 43 | pragma solidity ^0.8.14; 44 | 45 | import "forge-std/Test.sol"; 46 | 47 | contract MyTest is Test { 48 | Contract myContract = new Contract(); 49 | 50 | function testDeployContractWithAnotherVersion() public { 51 | address anotherAddress = deployCode("Contract6.sol:Contract6"); 52 | 53 | // Different compiler versions will produce different bytecodes 54 | assertTrue(keccak256(address(myContract).code) != keccak256(anotherAddress.code)); // [PASS] 55 | } 56 | } 57 | ``` 58 | -------------------------------------------------------------------------------- /foundry.toml: -------------------------------------------------------------------------------- 1 | [profile.default] 2 | src = 'src' 3 | out = 'out' 4 | libs = ['lib'] 5 | optimizer = false 6 | solc_version = '0.8.14' 7 | 8 | [profile.0_6_x] 9 | # `src` must be different fot it to work. 10 | # We also recommend putting all Solidity test files and scripts inside `src*/`. 11 | src = 'src-0_6_x' 12 | out = 'out' 13 | # The order matters! When using `forge install` with FOUNDRY_PROFILE=0_6_x, 14 | # it will use the first directory as the installation path. 15 | # If the library is compatible with all versions, 16 | # you can install it with the default profile. 17 | libs = ['lib-0_6_x', 'lib'] 18 | optimizer = false 19 | solc_version = '0.6.12' 20 | 21 | # See more config options https://github.com/foundry-rs/foundry/tree/master/config 22 | -------------------------------------------------------------------------------- /src-0_6_x/Contract6.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity ^0.6.12; 3 | 4 | import "forge-std/console2.sol"; 5 | 6 | contract Contract6 { 7 | constructor() public { 8 | console2.log("This is a Solidity 0.6.x contract!"); 9 | } 10 | } 11 | 12 | -------------------------------------------------------------------------------- /src/Contract.s.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity ^0.8.14; 3 | 4 | import "forge-std/Script.sol"; 5 | 6 | contract ContractScript is Script { 7 | function setUp() public {} 8 | 9 | function run() public { 10 | vm.broadcast(); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/Contract.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity ^0.8.14; 3 | 4 | import "forge-std/console2.sol"; 5 | 6 | contract Contract { 7 | constructor() { 8 | console2.log("This is a Solidity 0.8.x contract!"); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/Contract.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity ^0.8.14; 3 | 4 | import "forge-std/Test.sol"; 5 | import { Contract } from "./Contract.sol"; 6 | 7 | contract MyTest is Test { 8 | Contract myContract = new Contract(); 9 | 10 | function testDeployContractWithAnotherVersion() public { 11 | address anotherAddress = deployCode("Contract6.sol:Contract6"); 12 | 13 | // Different compiler versions will produce different bytecodes 14 | assertTrue(keccak256(address(myContract).code) != keccak256(anotherAddress.code)); // [PASS] 15 | } 16 | } 17 | --------------------------------------------------------------------------------