Smock: The powerful mocking tool for Hardhat

Features of smock and how to use them with examples

We’ve covered mocking contracts before, but now there’s an additional great tool available: smock. It simplifies the mocking process greatly and also gives you more testing power. You’ll be able to change the return values for functions as well as changing internal contract storage directly!

How cool is that?

Unit Test Cat

How to set up ?

Requirements: Currently only Hardhat with Waffle is supported. You can follow the setup here. If you use Truffle, you can take a look at mock-contract or wait for future support (see bottom).

The smock tool is available as plugin. You can install it via Npm using:

$ npm install @eth-optimism/smock --save-dev

Now you just have to add the storage plugin inside hardhat.config.js:


That's all, let's go ahead and use it.


The first functionality of changing the return values is available via smockit. With this tool you can mock function calls:

  • - asserting call count and call data
  • - returning mocked data
  • - reverting with data

Will the code in the function be executed?

  • No, there will be no code of the mocked function executed.

Can I mock an internal function call?

  • No, that's currently not supported. Only external function calls can be mocked. But this might change in the future (see bottom).

smockit is available via import:

const { smockit } = require("@eth-optimism/smock");


The second functionality of changing internal storage is available via smoddit. With this tool you can directly change the contract storage:

  • modify primitive types directly
  • change data in mappings
  • modify stored structs

smoddit is available via import:

const { smoddit } = require("@eth-optimism/smock");

Let's see an example


Imagine we have an ERC-20 contract with a mintUpTo function. This function will mint whatever amount is required to mint to get the passed address to the requested balance amount.

This function as you can see is onlyOwner. Now imagine a complex system where the onlyOwner is really just another contract who's allowed to call this function. Let's see an example second contract for this...

pragma solidity ^0.7.0;

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/access/Ownable.sol";

contract MyERC20 is ERC20, Ownable {
    constructor() ERC20("MyERC20", "MYE") {}

    function mintUpTo(
        address to, uint256 amount
    ) external onlyOwner returns (uint256) {
        uint256 currentBalance = balanceOf(to);

        if (currentBalance >= amount) return 0;

        uint256 mintBalance = amount - currentBalance;
        _mint(to, mintBalance);

        return mintBalance;
pragma solidity ^0.7.0;

import "hardhat/console.sol";
import "./MyERC20.sol";

contract MyOtherContract {
  MyERC20 private myERC20;

  constructor(MyERC20 myERC20_) {
      myERC20 = myERC20_;

  function myOtherFunction(
      address to, uint256 amount
  ) external returns (bool) {
    // do stuff

    uint256 mintAmount = myERC20.mintUpTo(to, amount);
    console.log("The minted amount was", mintAmount);

    // do more stuff


In this contract we call the mintUpTo function. Remember this will only work if the contract is actually the owner. This is  the intended behavior we want in a mainnet setup. But wouldn't it be useful if we could avoid this just for our unit-tests?

Figuring out the system state in where MyOtherContract is actually the owner of myERC20 might require some complex initialization steps. Let's avoid this by mocking the mintUpTo function... 

Mocking mintUpTo via smockit

We can first create the smocked contract using:


This will create a copy of the contract with its own address. The address is actually just an empty account without any code. So don't call any actual functions here, only mocked ones.

So to mock our function we use:


We now pass the mocked contract address to our MyOtherContract. And as you can see, we can call myOtherFunction even though it's not the owner of myERC20.

On top we can actually check and verify the calls to mintUpTo. So in our unit test we can assert that the mintUpTo does indeed happen with the expected call parameters available via:

const { expect } = require("chai");
const { smoddit, smockit } = require("@eth-optimism/smock");
const { BigNumber } = require("ethers");

describe("My ERC20 and other contract", function () {
	let myERC20;

	beforeEach(async () => {
		const MyERC20 = await ethers.getContractFactory("MyERC20");
		myERC20 = await MyERC20.deploy();
		await myERC20.deployed();

	it("call mint up in my ERC20", async function () {
		const MyMockContract = await smockit(myERC20);
		const MyOtherContract = await ethers.getContractFactory(
		const myOtherContract = await MyOtherContract.deploy(

		const mockedMintAmount = 30;

		const to = "0xdAC17F958D2ee523a2206206994597C13D831ec7";
		const amount = 100;

		await myOtherContract.myOtherFunction(to, amount);

it("call mint up in my ERC20", async function () {
  const MyModifiableERC20Factory = await smoddit("MyERC20");
  const MyModifiableERC20 = await MyModifiableERC20Factory.deploy();

  const MyOtherContract = await ethers.getContractFactory(
  const myOtherContract = await MyOtherContract.deploy(

  await MyModifiableERC20.transferOwnership(myOtherContract.address);

  const to = "0xdAC17F958D2ee523a2206206994597C13D831ec7";
  const transferAmount = 100;
  const mockedBalance = BigNumber.from("10");

    _balances: {
      [to]: mockedBalance,

  expect((await MyModifiableERC20.balanceOf(to)).toString()).to.equal(
  await myOtherContract.myOtherFunction(to, transferAmount);
  expect((await MyModifiableERC20.balanceOf(to)).toString()).to.equal(

Changing token balance via smoddit

We can also modify the internal storage of a contract via smoddit. Create a contract factory  and use it to deploy the contract:

const Factory = await smoddit("MyERC20");
const Contract = await Factory.deploy()

The resulting contract can be modified directly. In our case we can for example change the ERC-20 balance of the contract:

    _balances: {
      [to]: mockedBalance,

An Optimistic Smock Future

I've reached out to Kelvin Fichter, the author of Smock and part of the Optimism team. As of right now you need Waffle and Buidler to use the tool. But in the next version it's planned to add support for Truffle.

And what I'm particularly excited about, we might get support for mocking internal functions. Instead of creating an empty account for the mocked address, the contract would have actual code behind the address. This would greatly improve the functionality. Stay tuned for further updates!

Markus Waas

Solidity Developer

More great blog posts from Markus Waas

  • Uniswap

    Using the new Uniswap v2 as oracle in your contracts

    How does the Uniswap v2 oracle function and how to integrate with it

    We've covered Uniswap previously here . But let's go through the basics first again. What is UniSwap? If you're not familiar with Uniswap yet, it's a fully decentralized protocol for automated liquidity provision on Ethereum. An easier-to-understand description would be that it's a decentralized...

  • 721 Insurance

    How to build and use ERC-721 tokens in 2021

    An intro for devs to the uniquely identifying token standard and its future

    The ERC-721 standard has been around for a while now. Originally made popular by blockchain games, it's more and more used for other applications like Defi. But what exactly is it? A non-fungible token (NFT) is a uniquely identifying token. The word non-fungible implies you cannot just replace...

  • Set Protocol

    Trustless token management with Set Protocol

    How to integrate token sets in your contracts

    With Set Protocol you can create baskets of tokens that give users different levels of exposure to underlying assets (currently only ERC-20 tokens). Set Protocol and their TokenSet functionality is the perfect example for making use of the new paradigm of Defi and composability. You can let...