I'm attempting to fork Safemoon (or really NotSafeMoon), and use it as a vehicle to learn smart contract development. (I've got a substantial amount of what you might call "Web 2.0" dev experience).

So say I have something like so in my constructor:

constructor () {
        _rOwned[_msgSender()] = _rTotal;
        IUniswapV2Router02 _uniswapV2Router = IUniswapV2Router02(0x10ED43C718714eb63d5aA57B78B54704E256024E);       // binance PANCAKE V2
        uniswapV2Pair = IUniswapV2Factory(_uniswapV2Router.factory()).createPair(address(this), _uniswapV2Router.WETH());

When I run my tests with npx hardhat test I get the following failure:

Compilation finished successfully

  TestToken contract
      1) "before each" hook for "Has the right name"

  0 passing (807ms)
  1 failing

  1) TestToken contract
       "before each" hook for "Has the right name":
     Error: Transaction reverted: function call to a non-contract account

Now, this does make perfect sense, after all I am attempting to call the Pancakeswap v2 router contract. How do I get around this limitation? Is there a way to inject the contract address for the router as an environment variable perhaps? Is there a mock constructor for the UniswapRouter I can be using? Generally, how is this sort of thing done in a way that remains testable (and how is it therefore tested) with smart contract development?


It sounds like you're facing one of two problems - either you're using a local dev chain and haven't deployed a Uniswap to it, or you're using a testing fork of a chain, and haven't told your test suite where to lookf for it. You probably have code in your tests for deploying the notSafeMoon contracts using something like const tokenArtifacts = await ethers.getContractFactory("Token");, or something reasonably like that. You will need to do something similar for any other contract you wish to interact with. There's a difference between whether you're testing with a local dev chain or working with a mainnet fork here, so we'll split the answer to cover both:

(note: even though the OP working with Pancake Swap which is on BSC, since Pancake Swap is a Uniswap v2 fork, and since most testing fork environments use the Ethereum mainnet to fork from as of this writing, we'll talk about Uniswap v2 and Ethereum mainnet forks, though the same can be extrapolated to Pancake Swap and forking BSC for a testing environment (provided one can find a service for forking BSC))

local dev chain (default)

You will need to deploy a Uniswap v2 Router, though this probably isn't all: we'd assume there are more contracts that the Router interacts with in the Uniswap suite, you'd need to deploy all of them. A quick search turns up a repo dedicated to this here, though this is not an endorsement. Looking at the getContractFactory function, you'll need to have the contract source code in the contracts folder in Hardhat, and then to call getContractFactory on the name of the contract (not the name of the .sol, incidentally - the actual name inside the contract), followed by deploy, as outlined in the Hardhat docs.

mainnet forks

(If you're interested in learning more about forked testing environments, here are the Hardhat docs.)

If you are using an environment which is forked off of mainnet, you don't need to deploy the contracts since they have already been deployed for you. This does till mean that you'll also need the ABI and address of a Uniswap v2 Router. Uniswap actually has the ABIs of their contracts in their docs, though as a suggestion for the future, Etherscan is usually your best friend here. Most major projects verify their contracts on Etherscan, meaning the full source code and ABI is freely available on their page. If you wanted to find the ABI of a Uni v2 Router on Etherscan, for example, you could just start typing in "Uniswap v2", and scroll down the options: they've got a contract tagged "Uniswap V2: Router".

(Projects often list their deployed addresses in their docs, you can also use that to find ABIs for projects that don't include theirs in their docs.)

Once you have the ABI, you can either copy-paste it in the tests or in a separate file with an import, and then you can use it to create a new contract:

const router = new ethers.Contract(ADDRESS, ABI)

(There is an optional third argument for passing in a provider or a specific signer, Ethers docs.)

Note that since you're only interacting with the Router, you should only need to build the contract for the router - the rest will be done using on-chain state.

Hope that's enough to get you unblocked!

  • 1
    Thank you so much! Super clear and comprehensive :) (I was working on a local dev chain, but am now going to try out both approaches :))
    – Abraham P
    Jul 25 at 10:31

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.