npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

ethereum-doppelganger

v0.0.7

Published

Smart contract mocking library

Readme

Doppelganger

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.

Install

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.

Usage

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>)

Example

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';

chai.use(chaiAsPromised);

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()).to.eventually.be.fulfilled.and.equal(false);
    });

    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()).to.eventually.be.fulfilled.and.equal(false);
    });

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