Skip to content
This repository has been archived by the owner. It is now read-only.
Switch branches/tags

Latest commit


Git stats


Failed to load latest commit information.
Latest commit message
Commit time

Depreciation notice

This npm is deprecated and not supported anymore. For smart contract mocking functionality use @ethereum-waffle/mock-contract.


doppelgänger /ˈdɒp(ə)lˌɡaŋə,ˈdɒp(ə)lˌɡɛŋə/ - an apparition or double of a living person

Build Status

Library for mocking smart contract dependencies during unit testing.


To start using with npm, type:

npm i -D ethereum-doppelganger

or with Yarn:

yarn add --dev ethereum-doppelganger

Doppelganger is currently developed to work with ethers-js exclusively. Support for other framework will be added in a future version.


Create a instance of Doppelganger providing the ABI/interface of the smart contract you want to mock:

import Doppelganger from 'ethereum-doppelganger';


const doppelganger = new Doppelganger(abi);

Deploy a instance of the Doppelganger smart contract:

await doppelganger.deploy(wallet);

Doppelganger can now be passed into other contracts by using the address attribute.

Return values for mocked functions can be set using:

await doppelganger.<nameOfMethod>.returns(<value>)


Below example illustrates how Doppelganger can be used to test the very simple AmIRichAlready contract.

pragma solidity ^0.4.24;

import "openzeppelin-solidity/contracts/token/ERC20/IERC20.sol";

contract AmIRichAlready {
    IERC20 private tokenContract;
    address private wallet;
    uint private constant RICHNESS = 1000000 * 10 ** 18;

    constructor (IERC20 _tokenContract) public {
        tokenContract = _tokenContract;
        wallet = msg.sender;

    function check() public view returns(bool) {
        uint balance = tokenContract.balanceOf(wallet);
        return balance > RICHNESS;

We are mostly interested in the tokenContract.balanceOf call. Doppelganger will be used to mock exactly this call with values that are significant for the return of the check() method.

import chai, {expect} from 'chai';
import chaiAsPromised from 'chai-as-promised';
import ethers from 'ethers';
import {createMockProvider, deployContract, getWallets} from 'ethereum-waffle';
import Doppelganger from 'ethereum-doppelganger';

import IERC20 from '../../build/IERC20';
import AmIRichAlready from '../../build/AmIRichAlready';


describe('Am I Rich Already?', () => {
  let provider; // connector to the ethereum network - in our case a Ganache instance 
  let user; // the account issuing interactions on the network
  let contract; // an instance of the AmIRichAlready contract
  let doppelganger; // an instance of doppelganger for the ERC20 token we want to observe

  before(async () => {
    provider = createMockProvider(); 
    [user] = await getWallets(provider);

  beforeEach(async () => {
    doppelganger = new Doppelganger(IERC20.interface); // say doppelganger what it should pretend to be
    await doppelganger.deploy(user); // deploy the doppelganger to the chain
    contract = await deployContract(user, AmIRichAlready, [doppelganger.address]); // deploy the contract under test to the chain

  describe('check method', () => {
    it('returns false if the wallet has less then 1000000 DAI', async () => {
      await doppelganger.balanceOf.returns(ethers.utils.parseEther('999999')); // configure doppelganger to return 999999 when balanceOf is called
      await expect(contract.check());

    it('returns false if the wallet has exactly 1000000 DAI', async () => {
      await doppelganger.balanceOf.returns(ethers.utils.parseEther('1000000')); // subsequent calls override the previous config
      await expect(contract.check());

    it('returns true if the wallet has more then 1000000 DAI', async () => {
      await doppelganger.balanceOf.returns(ethers.utils.parseEther('1000001'));
      await expect(contract.check());


Smart contract mocking tool




No releases published


No packages published