From 9940630eac79b083e050eff3e06fbebb9c69a8ce Mon Sep 17 00:00:00 2001 From: Przemyslaw Rzad Date: Fri, 19 Aug 2022 12:28:15 +0200 Subject: [PATCH] =?UTF-8?q?=F0=9F=A6=B7=20Automatically=20inject=20call=20?= =?UTF-8?q?history=20into=20hardhat=20provider=20(#776)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .changeset/modern-tools-drop.md | 7 ++ waffle-chai/.mocharc.js | 3 +- waffle-chai/src/index.ts | 1 + waffle-chai/src/inject-call-history.ts | 78 +++++++++++++++++++++++ waffle-chai/test/index.ts | 7 -- waffle-chai/test/test-setup.ts | 6 ++ waffle-hardhat/.mocharc.js | 1 + waffle-hardhat/src/inject-call-history.ts | 62 ------------------ waffle-hardhat/test/test-setup.ts | 7 +- waffle-optimism/.mocharc.js | 3 +- waffle-optimism/test/test-setup.ts | 6 ++ 11 files changed, 109 insertions(+), 72 deletions(-) create mode 100644 .changeset/modern-tools-drop.md create mode 100644 waffle-chai/src/inject-call-history.ts create mode 100644 waffle-chai/test/test-setup.ts delete mode 100644 waffle-hardhat/src/inject-call-history.ts create mode 100644 waffle-optimism/test/test-setup.ts diff --git a/.changeset/modern-tools-drop.md b/.changeset/modern-tools-drop.md new file mode 100644 index 000000000..3dd909e0f --- /dev/null +++ b/.changeset/modern-tools-drop.md @@ -0,0 +1,7 @@ +--- +"@ethereum-waffle/chai": patch +"@ethereum-waffle/hardhat": patch +"@ethereum-waffle/optimism": patch +--- + +🦷 (Experimental) Automatically inject call history into hardhat provider diff --git a/waffle-chai/.mocharc.js b/waffle-chai/.mocharc.js index 9091d10d5..2ca91d7b5 100644 --- a/waffle-chai/.mocharc.js +++ b/waffle-chai/.mocharc.js @@ -2,5 +2,6 @@ process.env.NODE_ENV = 'test' module.exports = { require: 'ts-node/register/transpile-only', timeout: 50000, - spec: 'test/**/*.{js,ts}' + spec: 'test/**/*.{js,ts}', + file: 'test/test-setup.ts' } diff --git a/waffle-chai/src/index.ts b/waffle-chai/src/index.ts index 864e074b3..60f2432b4 100644 --- a/waffle-chai/src/index.ts +++ b/waffle-chai/src/index.ts @@ -15,6 +15,7 @@ import {supportChangeTokenBalance} from './matchers/changeTokenBalance'; import {supportChangeTokenBalances} from './matchers/changeTokenBalances'; import {supportCalledOnContract} from './matchers/calledOnContract/calledOnContract'; import {supportCalledOnContractWith} from './matchers/calledOnContract/calledOnContractWith'; +import './inject-call-history'; export function waffleChai(chai: Chai.ChaiStatic, utils: Chai.ChaiUtils) { supportBigNumber(chai.Assertion, utils); diff --git a/waffle-chai/src/inject-call-history.ts b/waffle-chai/src/inject-call-history.ts new file mode 100644 index 000000000..f1d6283cc --- /dev/null +++ b/waffle-chai/src/inject-call-history.ts @@ -0,0 +1,78 @@ +import type {RecordedCall} from '@ethereum-waffle/provider'; +import {utils} from 'ethers'; + +/** + * Injects call history into hardhat provider, + * making it possible to use matchers like calledOnContract + */ + +class CallHistory { + recordedCalls: RecordedCall[] = []; + + addUniqueCall(call: RecordedCall) { + if (!this.recordedCalls.find(c => c.address === call.address && c.data === call.data)) { + this.recordedCalls.push(call); + } + } + + clearAll() { + this.recordedCalls = []; + } +} + +function toRecordedCall(message: any): RecordedCall { + return { + address: message.to?.buf ? utils.getAddress(utils.hexlify(message.to.buf)) : undefined, + data: message.data ? utils.hexlify(message.data) : '0x' + }; +} + +const inject = () => { + let waffle: any; + try { + waffle = require('hardhat')?.waffle; + } catch { return; } + if (!waffle || !waffle.provider) return; + const callHistory = new CallHistory(); + (waffle.provider as any).clearCallHistory = () => { + callHistory.clearAll(); + }; + + let beforeMessageListener: (message: any) => void | undefined; + const init = waffle.provider?._hardhatNetwork?.provider?._wrapped?._wrapped?._wrapped?._init; + if (!init) return; + waffle.provider._hardhatNetwork.provider._wrapped._wrapped._wrapped._init = async function () { + await init.apply(this); + if (typeof beforeMessageListener === 'function') { + // has to be here because of weird behaviour of init function, which requires us to re-register the handler. + waffle.provider + ?._hardhatNetwork + ?.provider + ?._wrapped + ?._wrapped + ?._wrapped + ?._node + ?._vmTracer + ?._vm + ?.off?.('beforeMessage', beforeMessageListener); + } + beforeMessageListener = (message: any) => { + callHistory.addUniqueCall(toRecordedCall(message)); + }; + waffle.provider.callHistory = callHistory.recordedCalls; + waffle.provider + ?._hardhatNetwork.provider + ?._wrapped._wrapped + ?._wrapped + ?._node + ?._vmTracer + ?._vm + ?.on?.('beforeMessage', beforeMessageListener); + }; +}; + +let injected = false; +if (!injected && !!process.env.WAFFLE_EXPERIMENTAL_HARDHAT_CALL_HISTORY) { + injected = true; + inject(); +} diff --git a/waffle-chai/test/index.ts b/waffle-chai/test/index.ts index 69e8fe141..b9d6dcafb 100644 --- a/waffle-chai/test/index.ts +++ b/waffle-chai/test/index.ts @@ -1,10 +1,3 @@ -import chai from 'chai'; -import chaiAsPromised from 'chai-as-promised'; -import {waffleChai} from '../src'; - -chai.use(chaiAsPromised); -chai.use(waffleChai); - export {calledOnContractTest} from './matchers/calledOnContract/calledOnContractTest'; export {calledOnContractValidatorsTest} from './matchers/calledOnContract/calledOnContractValidatorsTest'; export {calledOnContractWithTest} from './matchers/calledOnContract/calledOnContractWithTest'; diff --git a/waffle-chai/test/test-setup.ts b/waffle-chai/test/test-setup.ts new file mode 100644 index 000000000..b73a3b41c --- /dev/null +++ b/waffle-chai/test/test-setup.ts @@ -0,0 +1,6 @@ +import chai from 'chai'; +import chaiAsPromised from 'chai-as-promised'; +import {waffleChai} from '../src'; + +chai.use(chaiAsPromised); +chai.use(waffleChai); diff --git a/waffle-hardhat/.mocharc.js b/waffle-hardhat/.mocharc.js index 5e519f10e..607dc937f 100644 --- a/waffle-hardhat/.mocharc.js +++ b/waffle-hardhat/.mocharc.js @@ -1,4 +1,5 @@ process.env.NODE_ENV = 'test' +process.env.WAFFLE_EXPERIMENTAL_HARDHAT_CALL_HISTORY = true module.exports = { require: 'ts-node/register/transpile-only', timeout: 50000, diff --git a/waffle-hardhat/src/inject-call-history.ts b/waffle-hardhat/src/inject-call-history.ts deleted file mode 100644 index 1140ddb76..000000000 --- a/waffle-hardhat/src/inject-call-history.ts +++ /dev/null @@ -1,62 +0,0 @@ -import {waffle} from 'hardhat'; -import type {RecordedCall} from 'ethereum-waffle'; -import {utils} from 'ethers'; - -class CallHistory { - recordedCalls: RecordedCall[] = []; - - addUniqueCall(call: RecordedCall) { - if (!this.recordedCalls.find(c => c.address === call.address && c.data === call.data)) { - this.recordedCalls.push(call); - } - } - - clearAll() { - this.recordedCalls = []; - } -} - -function toRecordedCall(message: any): RecordedCall { - return { - address: message.to?.buf ? utils.getAddress(utils.hexlify(message.to.buf)) : undefined, - data: message.data ? utils.hexlify(message.data) : '0x' - }; -} - -const callHistory = new CallHistory(); -(waffle.provider as any).clearCallHistory = () => { - callHistory.clearAll(); -}; - -let beforeMessageListener: (message: any) => void | undefined; - -const init = (waffle.provider as any)._hardhatNetwork.provider._wrapped._wrapped._wrapped._init; -(waffle.provider as any)._hardhatNetwork.provider._wrapped._wrapped._wrapped._init = async function () { - await init.apply(this); - if (typeof beforeMessageListener === 'function') { - // hast to be here because of weird behaviour of init function - (waffle.provider as any) - ._hardhatNetwork - .provider - ._wrapped - ._wrapped - ._wrapped - ._node - ._vmTracer - ._vm - .off('beforeMessage', beforeMessageListener); - } - beforeMessageListener = (message: any) => { - callHistory.addUniqueCall(toRecordedCall(message)); - }; - const provider: any = waffle.provider; - provider.callHistory = callHistory.recordedCalls; - (waffle.provider as any) - ._hardhatNetwork.provider - ._wrapped._wrapped - ._wrapped - ._node - ._vmTracer - ._vm - .on('beforeMessage', beforeMessageListener); -}; diff --git a/waffle-hardhat/test/test-setup.ts b/waffle-hardhat/test/test-setup.ts index 2409ce5b4..64e44c900 100644 --- a/waffle-hardhat/test/test-setup.ts +++ b/waffle-hardhat/test/test-setup.ts @@ -1 +1,6 @@ -import '../src/inject-call-history'; +import chai from 'chai'; +import chaiAsPromised from 'chai-as-promised'; +import {solidity} from 'ethereum-waffle'; + +chai.use(chaiAsPromised); +chai.use(solidity); diff --git a/waffle-optimism/.mocharc.js b/waffle-optimism/.mocharc.js index 580d9d980..5e519f10e 100644 --- a/waffle-optimism/.mocharc.js +++ b/waffle-optimism/.mocharc.js @@ -2,5 +2,6 @@ process.env.NODE_ENV = 'test' module.exports = { require: 'ts-node/register/transpile-only', timeout: 50000, - spec: 'test/**/*.test.{js,ts}' + spec: 'test/**/*.test.{js,ts}', + file: 'test/test-setup.ts' } diff --git a/waffle-optimism/test/test-setup.ts b/waffle-optimism/test/test-setup.ts new file mode 100644 index 000000000..64e44c900 --- /dev/null +++ b/waffle-optimism/test/test-setup.ts @@ -0,0 +1,6 @@ +import chai from 'chai'; +import chaiAsPromised from 'chai-as-promised'; +import {solidity} from 'ethereum-waffle'; + +chai.use(chaiAsPromised); +chai.use(solidity);