Skip to content

Commit

Permalink
🏄 Migration and fixes after introducing new Ganache (#683)
Browse files Browse the repository at this point in the history
  • Loading branch information
rzadp committed Apr 5, 2022
1 parent 033af00 commit e7299db
Show file tree
Hide file tree
Showing 26 changed files with 279 additions and 151 deletions.
52 changes: 52 additions & 0 deletions docs/source/migration-guides.rst
Original file line number Diff line number Diff line change
Expand Up @@ -383,3 +383,55 @@ For example:
await provider.send('evm_mine', [timestamp])
}
Tests relying on setting gasPrice
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Since `London hardfork <https://eips.ethereum.org/EIPS/eip-1559>`_, :code:`baseFeePerGas` is replacing :code:`gasPrice`.
If your tests are relying on setting :code:`gasPrice` with :code:`changeBalance` matcher, you will have to update them.

**Before**

.. code-block:: javascript
await expect(() =>
sender.sendTransaction({
to: receiver.address,
gasPrice: 0,
value: 200
})
).to.changeBalance(sender, -200);
**After**

.. code-block:: javascript
const TX_GAS = 21000;
const BASE_FEE_PER_GAS = 875000000
const gasFees = BASE_FEE_PER_GAS * TX_GAS;
await expect(() =>
sender.sendTransaction({
to: receiver.address,
gasPrice: BASE_FEE_PER_GAS,
value: 200
})
).to.changeBalance(sender, -(gasFees + 200));
Currently there is no way to set :code:`gasPrice` to :code:`0` in :code:`Ganache`.
Instead of (deprecated) matcher :code:`changeBalance`, new matcher :code:`changeEtherBalance` can be used instead - which handles transaction fee calculation automatically.

Custom wallet mnemonic
~~~~~~~~~~~~~~~~~~~~~~

Ganache has a build-in set of Wallets with positive Ether balance.
In the new Ganache, you should not override the wallet config, otherwise you might end up with Wallets with no Ether balance.

.. code-block:: javascript
const provider = new MockProvider({
ganacheOptions: {
// Remove this, if exists:
wallet: {
mnemonic: 'horn horn horn horn horn horn horn horn horn horn horn horn'
}
}
})
2 changes: 1 addition & 1 deletion waffle-chai/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@ethereum-waffle/chai",
"description": "A sweet set of chai matchers for your blockchain testing needs.",
"version": "4.0.0-alpha.8",
"version": "4.0.0-alpha.9",
"author": "Marek Kirejczyk <account@ethworks.io> (http://ethworks.io)",
"repository": "git@github.com:EthWorks/Waffle.git",
"private": false,
Expand Down
5 changes: 5 additions & 0 deletions waffle-chai/test/matchers/MockProviderCases.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
/**
* This import only works when inside the monorepo.
* It is OK because we do not publish the `test` directory on NPM.
*/
export {describeMockProviderCases} from '@ethereum-waffle/provider/test/MockProviderCases';
Original file line number Diff line number Diff line change
Expand Up @@ -2,39 +2,39 @@ import {AssertionError, expect} from 'chai';
import {MockProvider} from '@ethereum-waffle/provider';
import {ContractFactory} from 'ethers';
import {CALLS_ABI, CALLS_BYTECODE} from '../../contracts/Calls';
import {describeMockProviderCases} from '../MockProviderCases';

async function setup() {
const provider = new MockProvider();
async function setup(provider: MockProvider) {
const [deployer] = provider.getWallets();

const factory = new ContractFactory(CALLS_ABI, CALLS_BYTECODE, deployer);
return {contract: await factory.deploy()};
}

describe('INTEGRATION: calledOnContract', () => {
describeMockProviderCases('INTEGRATION: calledOnContract', (provider) => {
it('checks that contract function was called', async () => {
const {contract} = await setup();
const {contract} = await setup(provider);
await contract.callWithoutParameter();

expect('callWithoutParameter').to.be.calledOnContract(contract);
});

it('throws assertion error when contract function was not called', async () => {
const {contract} = await setup();
const {contract} = await setup(provider);

expect(
() => expect('callWithoutParameter').to.be.calledOnContract(contract)
).to.throw(AssertionError, 'Expected contract function to be called');
});

it('checks that contract function was not called', async () => {
const {contract} = await setup();
const {contract} = await setup(provider);

expect('callWithoutParameter').not.to.be.calledOnContract(contract);
});

it('throws assertion error when contract function was called', async () => {
const {contract} = await setup();
const {contract} = await setup(provider);
await contract.callWithoutParameter();

expect(
Expand All @@ -45,8 +45,8 @@ describe('INTEGRATION: calledOnContract', () => {
it(
'checks that contract function was called on provided contract and not called on another deploy of this contract',
async () => {
const {contract} = await setup();
const {contract: secondDeployContract} = await setup();
const {contract} = await setup(provider);
const {contract: secondDeployContract} = await setup(provider);
await contract.callWithoutParameter();

expect('callWithoutParameter').to.be.calledOnContract(contract);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,16 @@ import {MockProvider} from '@ethereum-waffle/provider';
import {constants, Contract, ContractFactory, getDefaultProvider} from 'ethers';
import {CALLS_ABI, CALLS_BYTECODE} from '../../contracts/Calls';
import {validateMockProvider} from '../../../src/matchers/calledOnContract/calledOnContractValidators';
import {describeMockProviderCases} from '../MockProviderCases';

async function setup() {
const provider = new MockProvider();
async function setup(provider: MockProvider) {
const [deployer] = provider.getWallets();

const factory = new ContractFactory(CALLS_ABI, CALLS_BYTECODE, deployer);
return {contract: await factory.deploy()};
}

describe('INTEGRATION: ethCalledValidators', () => {
describeMockProviderCases('INTEGRATION: ethCalledValidators', (provider) => {
it('throws type error when the argument is not a contract', async () => {
expect(
() => expect('calledFunction').to.be.calledOnContract('invalidContract')
Expand All @@ -32,15 +32,15 @@ describe('INTEGRATION: ethCalledValidators', () => {
});

it('throws type error when the provided function is not a string', async () => {
const {contract} = await setup();
const {contract} = await setup(provider);

expect(
() => expect(12).to.be.calledOnContract(contract)
).to.throw(TypeError, 'function name must be a string');
});

it('throws type error when the provided function is not in the contract', async () => {
const {contract} = await setup();
const {contract} = await setup(provider);

expect(
() => expect('notExistingFunction').to.be.calledOnContract(contract)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,58 +2,58 @@ import {MockProvider} from '@ethereum-waffle/provider';
import {ContractFactory} from 'ethers';
import {CALLS_ABI, CALLS_BYTECODE} from '../../contracts/Calls';
import {AssertionError, expect} from 'chai';
import {describeMockProviderCases} from '../MockProviderCases';

async function setup() {
const provider = new MockProvider();
async function setup(provider: MockProvider) {
const [deployer] = provider.getWallets();

const factory = new ContractFactory(CALLS_ABI, CALLS_BYTECODE, deployer);
return {contract: await factory.deploy()};
}

describe('INTEGRATION: calledOnContractWith', () => {
describeMockProviderCases('INTEGRATION: calledOnContractWith', (provider) => {
it('checks that contract function with provided parameter was called', async () => {
const {contract} = await setup();
const {contract} = await setup(provider);

await contract.callWithParameter(1);

expect('callWithParameter').to.be.calledOnContractWith(contract, [1]);
});

it('checks that contract function with provided multiple parameters was called', async () => {
const {contract} = await setup();
const {contract} = await setup(provider);

await contract.callWithParameters(2, 3);

expect('callWithParameters').to.be.calledOnContractWith(contract, [2, 3]);
});

it('throws assertion error when contract function with parameter was not called', async () => {
const {contract} = await setup();
const {contract} = await setup(provider);

expect(
() => expect('callWithParameter').to.be.calledOnContractWith(contract, [1])
).to.throw(AssertionError, 'Expected contract function with parameters to be called');
});

it('checks that contract function with parameter was not called', async () => {
const {contract} = await setup();
const {contract} = await setup(provider);

await contract.callWithParameter(2);

expect('callWithParameter').not.to.be.calledOnContractWith(contract, [1]);
});

it('checks that contract function with parameters was not called', async () => {
const {contract} = await setup();
const {contract} = await setup(provider);

await contract.callWithParameters(1, 2);

expect('callWithParameters').not.to.be.calledOnContractWith(contract, [1, 3]);
});

it('throws assertion error when contract function with parameter was called', async () => {
const {contract} = await setup();
const {contract} = await setup(provider);
await contract.callWithParameter(2);

expect(
Expand All @@ -64,8 +64,8 @@ describe('INTEGRATION: calledOnContractWith', () => {
it(
'checks that contract function was called on provided contract and not called on another deploy of this contract',
async () => {
const {contract} = await setup();
const {contract: secondDeployContract} = await setup();
const {contract} = await setup(provider);
const {contract: secondDeployContract} = await setup(provider);
await contract.callWithParameter(2);

expect('callWithParameter').to.be.calledOnContractWith(contract, [2]);
Expand All @@ -76,7 +76,7 @@ describe('INTEGRATION: calledOnContractWith', () => {
it(
'checks that contract function which was called twice with different args, lets possibility to find desirable call',
async () => {
const {contract} = await setup();
const {contract} = await setup(provider);

await contract.callWithParameters(2, 3);
await contract.callWithParameters(4, 5);
Expand Down
40 changes: 16 additions & 24 deletions waffle-chai/test/matchers/changeBalance.test.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import {expect, AssertionError} from 'chai';
import {MockProvider} from '@ethereum-waffle/provider';
import {BigNumber, Contract} from 'ethers';
import {BASE_FEE_PER_GAS, TX_GAS} from './constants';
import {describeMockProviderCases} from './MockProviderCases';

describe('INTEGRATION: changeBalance matcher', () => {
const provider = new MockProvider();
describeMockProviderCases('INTEGRATION: changeBalance matcher', (provider) => {
const [sender, receiver] = provider.getWallets();
const contract = new Contract(receiver.address, [], provider);

Expand All @@ -13,10 +13,9 @@ describe('INTEGRATION: changeBalance matcher', () => {
await expect(() =>
sender.sendTransaction({
to: receiver.address,
gasPrice: 0,
value: 200
})
).to.changeBalance(sender, '-200');
).to.changeBalance(receiver, '200');
});

it('Should pass when expected balance change is passed as int and is equal to an actual', async () => {
Expand Down Expand Up @@ -47,27 +46,27 @@ describe('INTEGRATION: changeBalance matcher', () => {
});

it('Should take into account transaction fee', async () => {
const gasFees = BASE_FEE_PER_GAS * TX_GAS;
await expect(() =>
sender.sendTransaction({
to: receiver.address,
gasPrice: 1,
gasPrice: BASE_FEE_PER_GAS,
value: 200
})
).to.changeBalance(sender, -21200);
).to.changeBalance(sender, -(gasFees + 200));
});

it('Should throw when expected balance change value was different from an actual', async () => {
await expect(
expect(() =>
sender.sendTransaction({
to: receiver.address,
gasPrice: 0,
value: 200
})
).to.changeBalance(sender, '-500')
).to.changeBalance(receiver, '500')
).to.be.eventually.rejectedWith(
AssertionError,
`Expected "${sender.address}" to change balance by -500 wei, but it has changed by -200 wei`
`Expected "${receiver.address}" to change balance by 500 wei, but it has changed by 200 wei`
);
});

Expand All @@ -76,13 +75,12 @@ describe('INTEGRATION: changeBalance matcher', () => {
expect(() =>
sender.sendTransaction({
to: receiver.address,
gasPrice: 0,
value: 200
})
).to.not.changeBalance(sender, '-200')
).to.not.changeBalance(receiver, '200')
).to.be.eventually.rejectedWith(
AssertionError,
`Expected "${sender.address}" to not change balance by -200 wei`
`Expected "${receiver.address}" to not change balance by 200 wei`
);
});
});
Expand All @@ -104,16 +102,14 @@ describe('INTEGRATION: changeBalance matcher', () => {
it('Should pass when expected balance change is passed as string and is equal to an actual', async () => {
await expect(await sender.sendTransaction({
to: receiver.address,
gasPrice: 0,
value: 200
})
).to.changeBalance(sender, '-200');
).to.changeBalance(receiver, '200');
});

it('Should pass when expected balance change is passed as int and is equal to an actual', async () => {
await expect(await sender.sendTransaction({
to: receiver.address,
gasPrice: 0,
value: 200
})
).to.changeBalance(receiver, 200);
Expand All @@ -122,7 +118,6 @@ describe('INTEGRATION: changeBalance matcher', () => {
it('Should pass when expected balance change is passed as BN and is equal to an actual', async () => {
await expect(await sender.sendTransaction({
to: receiver.address,
gasPrice: 0,
value: 200
})
).to.changeBalance(receiver, BigNumber.from(200));
Expand All @@ -140,27 +135,25 @@ describe('INTEGRATION: changeBalance matcher', () => {
await expect(
expect(await sender.sendTransaction({
to: receiver.address,
gasPrice: 0,
value: 200
})
).to.changeBalance(sender, '-500')
).to.changeBalance(receiver, '500')
).to.be.eventually.rejectedWith(
AssertionError,
`Expected "${sender.address}" to change balance by -500 wei, but it has changed by -200 wei`
`Expected "${receiver.address}" to change balance by 500 wei, but it has changed by 200 wei`
);
});

it('Should throw in negative case when expected balance change value was equal to an actual', async () => {
await expect(
expect(await sender.sendTransaction({
to: receiver.address,
gasPrice: 0,
value: 200
})
).to.not.changeBalance(sender, '-200')
).to.not.changeBalance(receiver, '200')
).to.be.eventually.rejectedWith(
AssertionError,
`Expected "${sender.address}" to not change balance by -200 wei`
`Expected "${receiver.address}" to not change balance by 200 wei`
);
});
});
Expand All @@ -169,7 +162,6 @@ describe('INTEGRATION: changeBalance matcher', () => {
it('Should pass when expected balance change is passed as int and is equal to an actual', async () => {
await expect(await sender.sendTransaction({
to: contract.address,
gasPrice: 0,
value: 200
})
).to.changeBalance(contract, 200);
Expand Down

0 comments on commit e7299db

Please sign in to comment.