Instead of trying to import contracts that are incompatible with your test contract's version and then attempting to deploy those contracts using the new
keyword, you can instead use the StdCheats#deployCode() function. If you want to deploy contract/s from an external library this way you'll have to first import those contract/s into one of your project files so that foundry compiles and generates the artifacts for these contracts in the output directory(i.e. the ./out
folder by default). For example you can add these imports in a ./test/mocks/Uniswap.sol
file and after compiling should observe the artifacts for these contracts in the ./out
folder:
// SPDX-License-Identifier: UNLICENSED
pragma solidity >=0.7.0;
import { UniswapV3Factory } from "@uniswap/v3-core/contracts/UniswapV3Factory.sol";
import { NonfungiblePositionManager } from "@uniswap/v3-periphery/contracts/NonfungiblePositionManager.sol";
Now in your test file you can deploy these contracts as follows:
contract UniswapTest is StdCheats {
// Uniswap V3 contracts
IUniswapV3Factory internal uniswapV3Factory;
INonfungiblePositionManager internal nonfungiblePositionManager;
function setUp() public virtual {
IUniswapV3Factory uniswapV3Factory = IUniswapV3Factory(deployCode("UniswapV3Factory.sol"));
INonfungiblePositionManager = INonfungiblePositionManager(deployCode("NonfungiblePositionManager.sol", abi.encode(address(uniswapV3Factory), wethAddress)));
}
function test_Uniswap() external {
// ... test uniswap
}
}
However you may not want to deploy bytecode generated by foundry(given the configured compiler & optimizer settings) but instead deploy a specific bytecode for a contract for a reason such as the following:
In the context of Uniswap the v3-periphery
repo has a PoolAddress
library with a POOL_INIT_CODE_HASH
constant which is the keccak256 hash of the UniswapV3Pool
contract creation bytecode defined in v3-core
, and the PoolAddress
library is a dependency of many periphery contracts including NonfungiblePositionManager
, so in order to have the periphery contracts function in your local foundry environment you'll want to deploy the exact UniswapV3Factory
bytecode(which contains the UniswapV3Pool
creation bytecode within it). The factory bytecode can be generated by running hardhat compile
on the Uniswap/v3-core
repo and should be visible in the ./artifacts/contracts/UniswapV3Factory.sol/UniswapV3Factory.json
file. You can then copy and paste the bytecode
in to a Precompiles
contract defined as follows:
// SPDX-License-Identifier: UNLICENSED
pragma solidity >=0.7.0;
import { IUniswapV3Factory } from "@uniswap/v3-core/contracts/interfaces/IUniswapV3Factory.sol";
contract Precompiles {
bytes private constant UNISWAP_V3_FACTORY_0_7_BYTE_CODE =
hex"60a0604052348..."; // bytecode is too long to show in this message
// taken from https://ethereum.stackexchange.com/q/132029/86303
function deployCode(bytes memory bytecode) internal returns (address payable addr) {
assembly {
addr := create(0, add(bytecode, 0x20), mload(bytecode))
if iszero(extcodesize(addr)) {
returndatacopy(0, 0, returndatasize())
revert(0, returndatasize())
}
}
}
function deployUniswapV3Factory() public returns (IUniswapV3Factory uniswapV3Factory) {
bytes memory creationBytecode = UNISWAP_V3_FACTORY_0_7_BYTE_CODE;
uniswapV3Factory = IUniswapV3Factory(deployCode(creationBytecode));
}
}
You can then deploy the UniswapV3Factory
(with the exact bytecode as Uniswap's V3 Factory deployments) in your test contract as follows:
contract UniswapTest is StdCheats {
// Uniswap V3 contracts
IUniswapV3Factory internal uniswapV3Factory;
INonfungiblePositionManager internal nonfungiblePositionManager;
Precompiles internal precompiles;
function setUp() public virtual {
precompiles = new Precompiles();
uniswapV3Factory = precompiles.deployUniswapV3Factory();
nonfungiblePositionManager = INonfungiblePositionManager(deployCode("NonfungiblePositionManager.sol", abi.encode(address(uniswapV3Factory), wethAddress, address(nonfungibleTokenPositionDescriptor))));
swapRouter = ISwapRouter(deployCode("SwapRouter.sol", abi.encode(address(uniswapV3Factory), wethAddress)));
}
function test_Uniswap() external {
// test uniswap...
}
}
NB:
- You may have noticed that I've ignored deploying the
NonfungibleTokenPositionDescriptor
contract which the NonfungiblePositionManager
contract requires in it's constructor parameters. This is for simplicity of the example(since it's a minor dependency), but also the NonfungibleTokenPositionDescriptor
contract has to be linked to the NFTDescriptor
library and there's currently no way to programmatically(i.e. using solidity) link a library to a contract using StdCheats#deployCode()
in foundry.
- If you're trying to import an interface in your test contract that has an incompatible version you can simply copy paste the interface file into your project and change the pragma version directive and use this interface instead.