Uniswap Fork Testing Hardhat

Hello

I am trying to run the uniswap fork on hardhat for learning purpose but getting following error:

   Should add liquidity:
 Error: Transaction reverted: function call to a non-contract account
  at UniswapV2Router02.getReserves (contracts/libraries/UniswapV2Library.sol:33)
  at UniswapV2Router02._addLiquidity (contracts/UniswapV2Router02.sol:46)
  at UniswapV2Router02.addLiquidityETH (contracts/UniswapV2Router02.sol:86)
  at processTicksAndRejections (internal/process/task_queues.js:93:5)
  at runNextTicks (internal/process/task_queues.js:62:3)
  at listOnTimeout (internal/timers.js:523:9)
  at processTimers (internal/timers.js:497:7)
  at HardhatNode._mineBlockWithPendingTxs (node_modules/hardhat/src/internal/hardhat-network/provider/node.ts:1588:23)
  at HardhatNode.mineBlock (node_modules/hardhat/src/internal/hardhat-network/provider/node.ts:442:16)
  at EthModule._sendTransactionAndReturnHash (node_modules/hardhat/src/internal/hardhat-network/provider/modules/eth.ts:1500:18)

The UniswapV2Pair is deployed. What I am missing?

Try to provide more context so someone can help. Are you following a guide? What commands did you run? What function did you try to call?

Hi, I'm having the same problem...this is an example test to reproduce the case:

const { ethers } = require('hardhat');
const {
    ether,
    expectRevert,
    expectEvent,
    time
} = require('@openzeppelin/test-helpers')
const { MaxUint256 } = require('ethers/constants')

const overrides = {
    gasLimit: 9999999
}

let deployer, alice, bob, carl
let tokenA, tokenB, myWETH, factory, router

describe('UniswapV2 stack', function () {
    beforeEach(async () => {
        [deployer, alice, bob, carl] = await ethers.getSigners()
        const UniswapV2Factory = await ethers.getContractFactory('UniswapV2Factory')
        const UniswapV2Router02 = await ethers.getContractFactory('UniswapV2Router02')
        const WETH = await ethers.getContractFactory('WETH')
        const ERC20 = await ethers.getContractFactory('ERC20Mock')

        tokenA = await ERC20.deploy()
        tokenB = await ERC20.deploy()
        await tokenA.initialize("TokenA", "TKA")
        await tokenB.initialize("TokenB", "TKB")
        await tokenA.mint(alice.address, ethers.BigNumber.from('1000000'))
        await tokenB.mint(alice.address, ethers.BigNumber.from('1000000'))
        await tokenA.mint(bob.address, ethers.BigNumber.from('1000000'))
        await tokenB.mint(bob.address, ethers.BigNumber.from('1000000'))

        myWETH = await WETH.deploy()
        factory = await UniswapV2Factory.deploy(deployer.address)
        router = await UniswapV2Router02.deploy(factory.address, myWETH.address, overrides)

        await factory.createPair(tokenA.address, tokenB.address)
        
    })

    it('addLiquidity', async () => {
        await tokenA.approve(router.address, MaxUint256)
        await tokenB.approve(router.address, MaxUint256)

        await router.addLiquidity(
            tokenA.address,
            tokenB.address,
            ethers.BigNumber.from('100'),
            ethers.BigNumber.from('100'),
            0,
            0,
            deployer.address,
            MaxUint256,
            overrides
        )
    })
})

Any help is appreciated :wink:

@Isa_O @gperezalba
I had similar issues and after doing some debugging I found that there is an issue with
UniswapV2Library function. For some reason function pairFor of this library returns different pair address.

Change this function to this and it will work:

import '@uniswap/v2-core/contracts/interfaces/IUniswapV2Factory.sol';
    function pairFor(address factory, address tokenA, address tokenB) internal view returns (address pair) {
        pair = IUniswapV2Factory(factory).getPair(tokenA,tokenB);
    }
4 Likes

YESSSSS THANK YOU

I just read the uniswap docs explciitly say to just deploy their bytecode but for some reason i chose to ignore that. Your solution helped me immensely, thank you so much

1 Like

Thank you so much for the solution. It worked very well.

I have forked uniswap contracts (router + factory) and during the test I got the exactly same issue some of you got. It seems like the pairFor function doesn't return the correct pair address for 2 given tokens.

// UniswapV2Library.sol

library UniswapV2Library {
  ...
    function pairFor(address factory, address tokenA, address tokenB) internal pure returns (address pair) {
        (address token0, address token1) = sortTokens(tokenA, tokenB);
        pair = address(uint(keccak256(abi.encodePacked(
                hex'ff',
                factory,
                keccak256(abi.encodePacked(token0, token1)),
                hex'96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f' // init code hash
            ))));
    }
  ...
}

The purpose of this function is to get the contract address for 2 tokens without calling any contract (pure function). The problem is the hex value init code hash this hash is just a keccak256() of your compiled contract UniswapV2Pair.sol

How to solve your problem ?

Very easy. Just find your bytecode from compiled contract, hash it and replace on the contract.

For Hardhat users:

  1. Find IUniswapV2Pair.json on the artifacts folder
  2. Find the bytecode on the file
  3. Copy it on Keccak 256 Online (Don't forget to change the input type to "hex" and delete the "0x" at the start)
  4. Hash it and copy the hash in the pairFor function

Sorry if it's bad written it's the first time I help online

2 Likes

Awesome! Thanks for your help!