Skip to content

forge: Deploying and testing contracts compiled by older Solidity versions #326

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Jeiwan opened this issue Dec 29, 2021 · 15 comments
Closed
Labels
T-question Type: question

Comments

@Jeiwan
Copy link

Jeiwan commented Dec 29, 2021

When compiling a test contract that imports a contract that uses an older Solidity version, it fails with:

Error:
0:
ParserError: Source file requires different compiler version (current compiler is 0.8.10+commit.fc410830.Darwin.appleclang) - note that nightly builds are considered to be strictly less than the released version
--> /Users/jeiwan/.tmp/foundrysolversions/lib/v2-core/contracts/UniswapV2Pair.sol:1:1:
|
1 | pragma solidity =0.5.16;
|

Steps to reproduce:

  1. mkdir foundrysolversions && cd $_
  2. forge init
  3. forge install Uniswap/v2-core
  4. Edit src/test/Contract.t.sol and add import "v2-core/UniswapV2Pair.sol";
  5. forge test

Is there a workaround? Is it possible to compile contracts with different compilers and then reuse binary code?

@gakonst
Copy link
Member

gakonst commented Dec 29, 2021

I don't think this is possible in general? You should not be able to import & use a file that is pragma solidity =0.5.16 in a non -0.5.16 file.

@mattsse
Copy link
Member

mattsse commented Dec 30, 2021

pragma solidity =0.5.16; pins to 0.5.16 and 0.5.16 only, meaning it won't compile with any other version.

this leaves two possible solutions:

  • use the exact version yourself (or a range that includes it like >=0.5.16)
  • upgrade the contract's pragma to a newer version

There is an unrecommended third possible workaround:
technically, it's not necessarily required to have a version pragma in a solidity file, but it's highly! recommended and the defacto standard. depending on the build tool you don't need a version pragma in your imports.

@gakonst
Copy link
Member

gakonst commented Dec 30, 2021

Yep, matches my understanding above. Closing then as it's not a Foundry issue and isn't something support-able in any framework afaict.

@gakonst gakonst closed this as completed Dec 30, 2021
@Jeiwan
Copy link
Author

Jeiwan commented Dec 31, 2021

This is doable in Hardhat, although with a hack. I think it's also possible to deploy binary code compiled by older Solidity compiler as a contract using inline assembly or some other magic. Probably this can be implemented as another cheat code (the VM contract).

This feature can be needed when testint a contract that interacts with third-party contracts deployed on mainnet. For example, a contract that uses flash loans of Uniswap. Upgrading outdated contracts can cause bugs or result in different behavior. Also, since Forge dependencies are git submodule, one would have to fork original repo and add it as a dependency instead of the original one, to be able to save changes.

Mocking is a solution but mocking is generally considered a bad practice because mocks are not exact copies and can differ in behavior. For simple cases like flash loans mocks are ok, but for more complex cases they can be problematic: imagine you used a slightly incorrect mock and then deployed to mainnet where the contract you depend on works differently. At this point though mocks seems like the simplest solution.

@gakonst
Copy link
Member

gakonst commented Dec 31, 2021

What is the way it's doable in hardhat? Maybe we can streamline that hack?

@Jeiwan
Copy link
Author

Jeiwan commented Dec 31, 2021

First step is to compile a dependency contract separately from other contracts. In Hardhat, I use an empty contract like:

pragma solidity =0.5.16;

import "@uniswap/v2-core/contracts/UniswapV2Factory.sol";

contract DependencyUniswapV2 {}

Then, in tests, it can be easily used via:

const Factory = await ethers.getContractFactory("UniswapV2Factory");

getContractFactory is a feature of Hardhat, it loads contracts from artifacts by contract name and it can instantiate a contract from bytecode and ABI:
https://github.com/nomiclabs/hardhat/blob/767f68dee84d6a47cd40153a8ab8552f41a95d5e/packages/hardhat-ethers/src/internal/helpers.ts#L60
I wish we could have something like that in Forge because writing tests in Solidity is so much better than in JS. It also feels like making Solidity compiler import contracts from bytecode is not a big problem, but it might be easier to hack it in Forge first.

@gakonst gakonst reopened this Dec 31, 2021
@jparklev
Copy link
Contributor

jparklev commented Dec 31, 2021

Here's what i'm doing for this atm lmao

Screen Shot 2021-12-31 at 2 44 59 PM

@gakonst
Copy link
Member

gakonst commented Dec 31, 2021

Honestly that's not a bad workaround, maybe something we should add on the stdlib?

2 qs:

  1. What does _compileSpace() do?
  2. How do you get its interface for further usage in the tests?

@jparklev
Copy link
Contributor

jparklev commented Dec 31, 2021

What does _compileSpace() do?

This is in a monorepo, so it's making sure the other pkg has been compiled first (does something like cd ../space-pkg; forge build)

How do you get its interface for further usage in the tests?

Just defining a new interface for it maker-style in this case

maybe something we should add on the stdlib

Yea! Guess it depends if there are any gotchas with appending calldata to bin bytecodes compiled with different versions? I don't know of anything like that, but I would defer to others who are more familiar

@Jeiwan
Copy link
Author

Jeiwan commented Jan 2, 2022

I've ended up using the same solution. Learned about it from this blog post:
https://www.libevm.com/2021/12/18/yulp-smart-contract-dev/

@Jeiwan Jeiwan changed the title forge: "Source file requires different compiler version" when compiling contracts with different Solidity versions forge: Deploying and testing contracts compiled by older Solidity versions Jan 2, 2022
@brockelmore
Copy link
Member

May i suggest to reduce external dependencies like jq, that we make it a cheatcode for loading deplyoment (and runtime) bytecode from a compiled contract? This would also make the etch cheatcode more useful i imagine

@gakonst
Copy link
Member

gakonst commented Jan 12, 2022

Supportive @brockelmore

@ncitron
Copy link
Contributor

ncitron commented Jan 13, 2022

If nobody else has, I'd like to give this one a shot. I've been wanting this feature for a while now in dapptools/foundry.

@onbjerg
Copy link
Collaborator

onbjerg commented Jan 27, 2022

Is this solved by #440?

@Jeiwan
Copy link
Author

Jeiwan commented Jan 31, 2022

@onbjerg Yes! Thanks.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
T-question Type: question
Projects
None yet
Development

No branches or pull requests

8 participants
@Jeiwan @onbjerg @ncitron @gakonst @mattsse @jparklev @brockelmore and others