defi-wonderland / smock

The Solidity mocking library
MIT License
319 stars 40 forks source link

incompatiability with waffle deployed contract #85

Open alxiong opened 2 years ago

alxiong commented 2 years ago

Describe the bug It seems that

const counterFactory = await ethers.getContractFactory('Counter');
const counter = await counterFactory.deploy(1);

// result in different deployed artifacts than:

const counter = (await waffle.deployContract(alice, CounterABI, [1])) as Counter;

Reproduction steps Please see https://github.com/alxiong/smock/commit/ddbb962d5fe5755d5033a3590c75d2a04a17e445 for a demo to reproduce.

For quick view:

contracts:

pragma solidity ^0.8.4;

import 'hardhat/console.sol';
import '@openzeppelin/contracts/token/ERC20/IERC20.sol';
import '@openzeppelin/contracts/token/ERC20/ERC20.sol';

contract Counter {
  address public deployer;
  uint256 public count;

  IERC20 private _dai;

  constructor(uint256 _startAt) {
    count = _startAt;
    deployer = msg.sender;
  }

  function add(uint256 _amount) external {
    count += _amount;
  }

  function setDaiAddress(address _daiAddress) external {
    _dai = IERC20(_daiAddress);
  }

  function payDaiToAdd(uint256 _amount) external {
    count += _amount;
    console.log('from contract: calling transferFrom');
    _dai.transferFrom(msg.sender, address(this), _amount);
    console.log('from contract: finish transferFrom');
  }
}

contract Dai is ERC20('Dai Stablecoin', 'DAI') {}

Tests:

import { waffle, ethers } from 'hardhat';
import CounterABI from '../../../artifacts/test/contracts/mock/Counter.sol/Counter.json';

describe('waffle deploy v.s. ethers deploy', () => {
  let counter: Counter;
  let daiFactory: MockContractFactory<Dai__factory>;
  let daiMock: MockContract<Dai>;
  let [alice] = waffle.provider.getWallets();

  before(async () => {
    daiFactory = await smock.mock('Dai');
  })

  describe('deploy using waffle', () => {
    beforeEach(async () => {
      counter = (await waffle.deployContract(alice, CounterABI, [1])) as Counter;
      daiMock = await daiFactory.deploy();

      await counter.setDaiAddress(daiMock.address);
    })

    it('should have correct call count', async () => {
      const amount = 5;
      expect(daiMock.transferFrom).to.have.callCount(0);

      await daiMock.setVariable('_balances', {
        [alice.address]: amount,
      });
      await daiMock.setVariable('_allowances', {
        [alice.address]: {
          [counter.address]: amount,
        },
      });

      await expect(counter.payDaiToAdd(amount)).to.not.be.reverted;
      // FIXME: the callCount is 12 !! Whaaaat?
      expect(daiMock.transferFrom).to.have.callCount(1);
    })
  })

  describe('deploy using ethers', () => {
    beforeEach(async () => {
      const counterFactory = await ethers.getContractFactory('Counter');
      counter = await counterFactory.deploy(1);
      daiMock = await daiFactory.deploy();

      await counter.setDaiAddress(daiMock.address);
    })
    it('should have correct call count', async () => {
      const amount = 5;
      expect(daiMock.transferFrom).to.have.callCount(0);

      await daiMock.setVariable('_balances', {
        [alice.address]: amount,
      });
      await daiMock.setVariable('_allowances', {
        [alice.address]: {
          [counter.address]: amount,
        },
      });

      await expect(counter.payDaiToAdd(amount)).to.not.be.reverted;
      // NOTE: correctly counted
      expect(daiMock.transferFrom).to.have.callCount(1);
    })
  })
})

std.err Output


  waffle deploy v.s. ethers deploy
    deploy using waffle
from contract: calling transferFrom
from contract: finish transferFrom
      1) should have correct call count
    deploy using ethers
from contract: calling transferFrom
from contract: finish transferFrom
      ✔ should have correct call count
...

  198 passing (7s)
  3 pending
  1 failing

  1) waffle deploy v.s. ethers deploy
       deploy using waffle
         should have correct call count:
     AssertionError: expected transferFrom to have been called exactly once, but it was called 12 times
marcelomorgado commented 2 years ago

Facing the same counting bug here