Skip to content

Commit da92375

Browse files
pawelpolak2yivlad
andauthored
🗽 Add address parameter to the mock contract (#815)
* 🗽 Add address parameter to the mock contract Co-authored-by: yivlad <jacenko.vlad@gmail.com>
1 parent b54c6b9 commit da92375

File tree

11 files changed

+274
-162
lines changed

11 files changed

+274
-162
lines changed

‎.changeset/blue-numbers-hammer.md

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
"@ethereum-waffle/hardhat": patch
3+
"@ethereum-waffle/mock-contract": patch
4+
---
5+
6+
Add mock contract deployment at a specified address

‎docs/source/mock-contract.rst

+9
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,15 @@ Create an instance of a mock contract providing the :code:`ABI` of the smart con
1616
1717
const mockContract = await deployMockContract(wallet, contractAbi);
1818
19+
You can also choose the deployment address of the mock contract with the options argument:
20+
21+
.. code-block:: ts
22+
23+
const mockContract = await deployMockContract(wallet, contractAbi, {
24+
address: deploymentAddress,
25+
overrride: false // optional, specifies if the contract should be overwritten
26+
})
27+
1928
The mock contract can now be integrated into other contracts by using the :code:`address` attribute.
2029
Return values for mocked functions can be set using:
2130

‎pnpm-lock.yaml

+4
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎waffle-hardhat/package.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,15 @@
2020
},
2121
"devDependencies": {
2222
"@ethereum-waffle/chai": "workspace:*",
23+
"@ethereum-waffle/mock-contract": "workspace:^*",
2324
"@ethereum-waffle/provider": "workspace:*",
2425
"@nomiclabs/hardhat-ethers": "2.1.0",
25-
"hardhat-waffle-dev": "2.0.3-dev.c5b5c29",
2626
"@types/node": "^17.0.41",
2727
"eslint": "^7.14.0",
2828
"ethereum-waffle": "workspace:*",
2929
"ethers": "5.6.2",
3030
"hardhat": "2.10.1",
31+
"hardhat-waffle-dev": "2.0.3-dev.c5b5c29",
3132
"mocha": "^8.2.1"
3233
}
3334
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import {waffle} from 'hardhat';
2+
import {MockProvider} from 'ethereum-waffle';
3+
import {mockContractDirectTest} from '@ethereum-waffle/mock-contract/test/directTest';
4+
import {mockContractProxiedTest} from '@ethereum-waffle/mock-contract/test/proxiedTest';
5+
6+
describe('INTEGRATION: Mock Contract', () => {
7+
const provider = waffle.provider as MockProvider;
8+
9+
before(async () => {
10+
await provider.send('hardhat_reset', []);
11+
});
12+
13+
mockContractDirectTest(provider);
14+
mockContractProxiedTest(provider);
15+
});

‎waffle-mock-contract/package.json

+10-10
Original file line numberDiff line numberDiff line change
@@ -32,32 +32,32 @@
3232
"module": "dist/esm/src/index.ts",
3333
"types": "dist/esm/src/index.d.ts",
3434
"scripts": {
35-
"test": "export NODE_ENV=test && yarn test:build && mocha",
35+
"test": "export NODE_ENV=test && mocha",
3636
"lint": "eslint '{src,test}/**/*.ts'",
3737
"lint:fix": "eslint --fix '{src,test}/**/*.ts'",
38-
"build": "rimraf ./dist && yarn build:sol && yarn build:esm && yarn build:cjs",
38+
"build": "rimraf ./dist && yarn build:sol && yarn build:esm && yarn build:cjs && ts-node ./test/helpers/buildTestContracts.ts",
3939
"build:sol": "ts-node compile.ts",
4040
"build:esm": "tsc -p tsconfig.build.json --outDir dist/esm --module ES6",
4141
"build:cjs": "tsc -p tsconfig.build.json --outDir dist/cjs",
42-
"test:build": "ts-node ./test/helpers/buildTestContracts.ts",
4342
"clean": "rimraf ./dist ./test/example/build"
4443
},
4544
"engines": {
4645
"node": ">=10.0"
4746
},
4847
"devDependencies": {
49-
"ethers": "5.6.2",
50-
"@ethersproject/abi": "^5.6.1",
5148
"@ethereum-waffle/chai": "workspace:*",
5249
"@ethereum-waffle/compiler": "workspace:*",
53-
"solc": "0.8.15",
5450
"@ethereum-waffle/provider": "workspace:*",
55-
"typechain": "^8.0.0",
51+
"@ethersproject/abi": "^5.6.1",
52+
"@ethersproject/providers": "5.6.2",
53+
"eslint": "^7.14.0",
54+
"ethers": "5.6.2",
5655
"mocha": "^8.2.1",
5756
"rimraf": "^3.0.2",
58-
"typescript": "^4.6.2",
59-
"eslint": "^7.14.0",
60-
"ts-node": "^9.0.0"
57+
"solc": "0.8.15",
58+
"ts-node": "^9.0.0",
59+
"typechain": "^8.0.0",
60+
"typescript": "^4.6.2"
6161
},
6262
"peerDependencies": {
6363
"ethers": "*"

‎waffle-mock-contract/src/index.ts

+33-3
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,17 @@ import {Contract, ContractFactory, Signer, utils} from 'ethers';
22
import type {JsonFragment} from '@ethersproject/abi';
33

44
import DoppelgangerContract from './Doppelganger.json';
5+
import type {JsonRpcProvider} from '@ethersproject/providers';
56

67
type ABI = string | Array<utils.Fragment | JsonFragment | string>
78

89
export type Stub = ReturnType<typeof stub>;
910

11+
type DeployOptions = {
12+
address: string;
13+
override?: boolean;
14+
}
15+
1016
export interface MockContract extends Contract {
1117
mock: {
1218
[key: string]: Stub;
@@ -15,7 +21,31 @@ export interface MockContract extends Contract {
1521
staticcall (contract: Contract, functionName: string, ...params: any[]): Promise<any>;
1622
}
1723

18-
async function deploy(signer: Signer) {
24+
async function deploy(signer: Signer, options?: DeployOptions) {
25+
if (options) {
26+
const {address, override} = options;
27+
const provider = signer.provider as JsonRpcProvider;
28+
if (!override && await provider.getCode(address) !== '0x') {
29+
throw new Error(
30+
`${address} already contains a contract. ` +
31+
'If you want to override it, set the override parameter.');
32+
}
33+
if ((provider as any)._hardhatNetwork) {
34+
if (await provider.send('hardhat_setCode', [
35+
address,
36+
'0x' + DoppelgangerContract.evm.deployedBytecode.object
37+
])) {
38+
return new Contract(address, DoppelgangerContract.abi, signer);
39+
} else throw new Error(`Couldn't deploy at ${address}`);
40+
} else {
41+
if (await provider.send('evm_setAccountCode', [
42+
address,
43+
'0x' + DoppelgangerContract.evm.deployedBytecode.object
44+
])) {
45+
return new Contract(address, DoppelgangerContract.abi, signer);
46+
} else throw new Error(`Couldn't deploy at ${address}`);
47+
}
48+
}
1949
const factory = new ContractFactory(DoppelgangerContract.abi, DoppelgangerContract.bytecode, signer);
2050
return factory.deploy();
2151
}
@@ -53,8 +83,8 @@ function createMock(abi: ABI, mockContractInstance: Contract) {
5383
return mockedAbi;
5484
}
5585

56-
export async function deployMockContract(signer: Signer, abi: ABI): Promise<MockContract> {
57-
const mockContractInstance = await deploy(signer);
86+
export async function deployMockContract(signer: Signer, abi: ABI, options?: DeployOptions): Promise<MockContract> {
87+
const mockContractInstance = await deploy(signer, options);
5888

5989
const mock = createMock(abi, mockContractInstance);
6090
const mockedContract = new Contract(mockContractInstance.address, abi, signer) as MockContract;
+2-100
Original file line numberDiff line numberDiff line change
@@ -1,102 +1,4 @@
1-
import {use, expect} from 'chai';
2-
import chaiAsPromised from 'chai-as-promised';
31
import {MockProvider} from '@ethereum-waffle/provider';
4-
import {waffleChai} from '@ethereum-waffle/chai';
5-
import {ContractFactory} from 'ethers';
2+
import {mockContractDirectTest} from './directTest';
63

7-
import {deployMockContract} from '../src';
8-
import Counter from './helpers/interfaces/Counter.json';
9-
10-
use(chaiAsPromised);
11-
use(waffleChai);
12-
13-
describe('Mock Contract - Integration (called directly)', () => {
14-
const [wallet] = new MockProvider().getWallets();
15-
16-
it('throws readable error if mock was not set up for a method', async () => {
17-
const mockCounter = await deployMockContract(wallet, Counter.abi);
18-
19-
await expect(mockCounter.read()).to.be.revertedWith('Mock on the method is not initialized');
20-
});
21-
22-
it('mocking returned values', async () => {
23-
const mockCounter = await deployMockContract(wallet, Counter.abi);
24-
await mockCounter.mock.read.returns(45291);
25-
26-
expect(await mockCounter.read()).to.equal(45291);
27-
});
28-
29-
it('mocking revert', async () => {
30-
const mockCounter = await deployMockContract(wallet, Counter.abi);
31-
await mockCounter.mock.read.reverts();
32-
33-
await expect(mockCounter.read()).to.be.revertedWith('Mock revert');
34-
});
35-
36-
it('mock with call arguments', async () => {
37-
const mockCounter = await deployMockContract(wallet, Counter.abi);
38-
await mockCounter.mock.add.returns(1);
39-
await mockCounter.mock.add.withArgs(1).returns(2);
40-
await mockCounter.mock.add.withArgs(2).reverts();
41-
42-
expect(await mockCounter.add(0)).to.equal(1);
43-
expect(await mockCounter.add(1)).to.equal(2);
44-
await expect(mockCounter.add(2)).to.be.revertedWith('Mock revert');
45-
expect(await mockCounter.add(3)).to.equal(1);
46-
});
47-
48-
it('should be able to call to another contract', async () => {
49-
const counterFactory = new ContractFactory(Counter.abi, Counter.bytecode, wallet);
50-
const counter = await counterFactory.deploy();
51-
const mockCounter = await deployMockContract(wallet, Counter.abi);
52-
53-
expect(await mockCounter.staticcall(counter, 'read()')).to.equal('0');
54-
expect(await mockCounter.staticcall(counter, 'read')).to.equal('0');
55-
});
56-
57-
it('should be able to call another contract with a parameter', async () => {
58-
const counterFactory = new ContractFactory(Counter.abi, Counter.bytecode, wallet);
59-
const counter = await counterFactory.deploy();
60-
const mockCounter = await deployMockContract(wallet, Counter.abi);
61-
62-
expect(await mockCounter.staticcall(counter, 'add', 1)).to.equal('1');
63-
});
64-
65-
it('should be able to call another contract with many parameters', async () => {
66-
const counterFactory = new ContractFactory(Counter.abi, Counter.bytecode, wallet);
67-
const counter = await counterFactory.deploy();
68-
const mockCounter = await deployMockContract(wallet, Counter.abi);
69-
70-
expect(await mockCounter.staticcall(counter, 'addThree', 1, 2, 3)).to.equal('6');
71-
});
72-
73-
it('should be able to execute another contract', async () => {
74-
const counterFactory = new ContractFactory(Counter.abi, Counter.bytecode, wallet);
75-
const counter = await counterFactory.deploy();
76-
const mockCounter = await deployMockContract(wallet, Counter.abi);
77-
78-
await mockCounter.call(counter, 'increment()');
79-
expect(await counter.read()).to.equal('1');
80-
81-
await mockCounter.call(counter, 'increment');
82-
expect(await counter.read()).to.equal('2');
83-
});
84-
85-
it('should be able to execute another contract with a parameter', async () => {
86-
const counterFactory = new ContractFactory(Counter.abi, Counter.bytecode, wallet);
87-
const counter = await counterFactory.deploy();
88-
const mockCounter = await deployMockContract(wallet, Counter.abi);
89-
90-
await mockCounter.call(counter, 'increaseBy', 2);
91-
expect(await counter.read()).to.equal('2');
92-
});
93-
94-
it('should be able to execute another contract with many parameters', async () => {
95-
const counterFactory = new ContractFactory(Counter.abi, Counter.bytecode, wallet);
96-
const counter = await counterFactory.deploy();
97-
const mockCounter = await deployMockContract(wallet, Counter.abi);
98-
99-
await mockCounter.call(counter, 'increaseByThreeValues', 1, 2, 3);
100-
expect(await counter.read()).to.equal('6');
101-
});
102-
});
4+
mockContractDirectTest(new MockProvider());

0 commit comments

Comments
 (0)