Waffle

Library for writing and testing smart contracts.

README

Waffle


CI undefined Discord

The most advanced framework for testing smart contracts.

Sweeter, simpler and faster.

Links



Philosophy


Simpler: Minimalistic, few dependencies.
Sweeter: Nice syntax, easy to extend.
Faster: Strong focus on the speed of test execution.

Features:


Sweet set of chai matchers, e.g.:
expect(...).to.be.revertedWith('Error message')
expect(...).to.emit(contract, 'EventName').withArgs(...))

Importing contracts from npm modules working out of the box, e.g.:
import "openzeppelin-solidity/contracts/token/ERC20/ERC20.sol";

Fixtures that help write fast and maintainable test suites, e.g.:
const {token} = await loadFixture(standardTokenWithBalance);

Customizable compilation options with native solc, dockerized solc and any version of solc-js loaded remotely at compiled time
Mocking smart contracts, e.g.:
const mockToken = await deployMockContract(wallet, IERC20.abi);

Support for promise-based configuration, e.g.:
use native solc binary for fast compilation in CI environment
use solc-js based on contract versions detected (async)

Support for TypeScript
Type-safe contract deployment and interactions with TypeChain

Documentation


Documentation is available here .

Installation:


To get started install ethereum-wafflewith yarn:

  1. ``` null
  2. yarn add --dev ethereum-waffle

  3. ```

Or if you prefer using npm:

  1. ``` null
  2. npm install --save-dev ethereum-waffle

  3. ```

Step by step guide


Add external dependency:


To add an external library install it using npm:

  1. ``` shell
  2. npm install @openzeppelin/contracts -D
  3. ```

or with yarn:

  1. ``` shell
  2. yarn add @openzeppelin/contracts -D
  3. ```

Note


Find this example in examples/basicand use it.

Example contract


Below is an example contract written in Solidity. Place it in contracts/BasicToken.solfile of your project:

  1. ``` solidity
  2. pragma solidity ^0.6.0;

  3. import "@openzeppelin/contracts/token/ERC20/ERC20.sol";

  4. // Example class - a mock class using delivering from ERC20
  5. contract BasicToken is ERC20 {
  6.     constructor(uint256 initialBalance) ERC20("Basic", "BSC") public {
  7.         _mint(msg.sender, initialBalance);
  8.     }
  9. }

  10. ```

Example test


Below is an example test written for the contract above compiled with Waffle. Place it under test/BasicToken.test.tsfile in your project directory:

  1. ``` ts
  2. import {expect, use} from 'chai';
  3. import {Contract} from 'ethers';
  4. import {deployContract, MockProvider, solidity} from 'ethereum-waffle';
  5. import BasicToken from '../build/BasicToken.json';

  6. use(solidity);

  7. describe('BasicToken', () => {
  8.   const [wallet, walletTo] = new MockProvider().getWallets();
  9.   let token: Contract;

  10.   beforeEach(async () => {
  11.     token = await deployContract(wallet, BasicToken, [1000]);
  12.   });

  13.   it('Assigns initial balance', async () => {
  14.     expect(await token.balanceOf(wallet.address)).to.equal(1000);
  15.   });

  16.   it('Transfer adds amount to destination account', async () => {
  17.     await token.transfer(walletTo.address, 7);
  18.     expect(await token.balanceOf(walletTo.address)).to.equal(7);
  19.   });

  20.   it('Transfer emits event', async () => {
  21.     await expect(token.transfer(walletTo.address, 7))
  22.       .to.emit(token, 'Transfer')
  23.       .withArgs(wallet.address, walletTo.address, 7);
  24.   });

  25.   it('Can not transfer above the amount', async () => {
  26.     await expect(token.transfer(walletTo.address, 1007)).to.be.reverted;
  27.   });

  28.   it('Can not transfer from empty account', async () => {
  29.     const tokenFromOtherWallet = token.connect(walletTo);
  30.     await expect(tokenFromOtherWallet.transfer(wallet.address, 1))
  31.       .to.be.reverted;
  32.   });

  33.   it('Calls totalSupply on BasicToken contract', async () => {
  34.     await token.totalSupply();
  35.     expect('totalSupply').to.be.calledOnContract(token);
  36.   });

  37.   it('Calls balanceOf with sender address on BasicToken contract', async () => {
  38.     await token.balanceOf(wallet.address);
  39.     expect('balanceOf').to.be.calledOnContractWith(token, [wallet.address]);
  40.   });
  41. });
  42. ```

Note: You will also need to install the following dependencies to run the example above:

  1. ``` shell
  2. yarn add mocha -D
  3. yarn add chai -D
  4. ```

Or with npm:

  1. ``` null
  2. npm i chai -D
  3. npm i mocha -D

  4. ```

Compiling


To compile your smart contracts run:

  1. ``` shell
  2. npx waffle
  3. ```

To compile using a custom configuration file run:

  1. ``` shell
  2. npx waffle config.json
  3. ```

Example configuration file looks like this (all fields optional):

  1. ``` json
  2. {
  3.   "sourceDirectory": "./custom_contracts",
  4.   "outputDirectory": "./custom_build",
  5.   "nodeModulesDirectory": "./custom_node_modules"
  6. }
  7. ```

To enable generation of typechain artifacts:

  1. ``` json
  2. {
  3.   "typechainEnabled": true
  4. }
  5. ```

Flattener


To flat your smart contracts run:

  1. ``` shell
  2. npx waffle flatten
  3. ```

In configuration file you can add optional field with path to flatten files:

  1. ``` json
  2. {
  3.   "flattenOutputDirectory": "./custom_flatten"
  4. }
  5. ```

Running tests


To run the tests run the following command:

  1. ``` shell
  2. npx mocha
  3. ```

Adding an npm script


For convenience, you can add the following to your package.json:

  1. ``` null
  2. {
  3.   ...,
  4.   "scripts": {
  5.     "test": "waffle && mocha"
  6.   }
  7. }

  8. ```

Now you can build and test your contracts with one command:

  1. ``` shell
  2. npm test
  3. ```

Documentation


For detailed feature walkthrough checkout documentation .

Contributing


Contributions are always welcome, no matter how large or small. Before contributing, please read the code of conduct and contribution policy .

Before you issue pull request:

Make sure all tests and linters pass. Make sure you have test coverage for any new features.

Running tests


Note: To make end-to-end test pass, you need to:

have Docker installed, up and running
have Ethereum stable docker image pulled, if not run docker pull ethereum/solc:stable
have native solidity 0.5.* installed

To run tests type:

  1. ``` shell
  2. yarn test
  3. ```

To run linter type:

  1. ``` shell
  2. yarn lint
  3. ```

Building documentation


Install Sphinx to build documentation:

  1. ``` shell
  2. cd docs
  3. make html
  4. ```

Before building documentation for the first time you may have to install required python packages:

  1. ``` shell
  2. pip3 install -r docs/requirements.txt
  3. ```

Roadmap


See #155

License


Waffle is released under the MIT License .