123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563 |
- const { ethers, predeploy } = require('hardhat');
- const { expect } = require('chai');
- const { impersonate } = require('../../helpers/account');
- const { selector } = require('../../helpers/methods');
- const { zip } = require('../../helpers/iterate');
- const {
- encodeMode,
- encodeBatch,
- encodeSingle,
- encodeDelegate,
- MODULE_TYPE_VALIDATOR,
- MODULE_TYPE_EXECUTOR,
- MODULE_TYPE_FALLBACK,
- MODULE_TYPE_HOOK,
- CALL_TYPE_CALL,
- CALL_TYPE_BATCH,
- CALL_TYPE_DELEGATE,
- EXEC_TYPE_DEFAULT,
- EXEC_TYPE_TRY,
- } = require('../../helpers/erc7579');
- const CALL_TYPE_INVALID = '0x42';
- const EXEC_TYPE_INVALID = '0x17';
- const MODULE_TYPE_INVALID = 999n;
- const coder = ethers.AbiCoder.defaultAbiCoder();
- function shouldBehaveLikeAccountERC7579({ withHooks = false } = {}) {
- describe('AccountERC7579', function () {
- beforeEach(async function () {
- await this.mock.deploy();
- await this.other.sendTransaction({ to: this.mock.target, value: ethers.parseEther('1') });
- this.modules = {};
- this.modules[MODULE_TYPE_VALIDATOR] = await ethers.deployContract('$ERC7579ModuleMock', [MODULE_TYPE_VALIDATOR]);
- this.modules[MODULE_TYPE_EXECUTOR] = await ethers.deployContract('$ERC7579ModuleMock', [MODULE_TYPE_EXECUTOR]);
- this.modules[MODULE_TYPE_FALLBACK] = await ethers.deployContract('$ERC7579ModuleMock', [MODULE_TYPE_FALLBACK]);
- this.modules[MODULE_TYPE_HOOK] = await ethers.deployContract('$ERC7579HookMock');
- this.mockFromEntrypoint = this.mock.connect(await impersonate(predeploy.entrypoint.v08.target));
- this.mockFromExecutor = this.mock.connect(await impersonate(this.modules[MODULE_TYPE_EXECUTOR].target));
- });
- describe('accountId', function () {
- it('should return the account ID', async function () {
- await expect(this.mock.accountId()).to.eventually.equal(
- withHooks
- ? '@openzeppelin/community-contracts.AccountERC7579Hooked.v0.0.0'
- : '@openzeppelin/community-contracts.AccountERC7579.v0.0.0',
- );
- });
- });
- describe('supportsExecutionMode', function () {
- for (const [callType, execType] of zip(
- [CALL_TYPE_CALL, CALL_TYPE_BATCH, CALL_TYPE_DELEGATE, CALL_TYPE_INVALID],
- [EXEC_TYPE_DEFAULT, EXEC_TYPE_TRY, EXEC_TYPE_INVALID],
- )) {
- const result = callType != CALL_TYPE_INVALID && execType != EXEC_TYPE_INVALID;
- it(`${
- result ? 'does not support' : 'supports'
- } CALL_TYPE=${callType} and EXEC_TYPE=${execType} execution mode`, async function () {
- await expect(this.mock.supportsExecutionMode(encodeMode({ callType, execType }))).to.eventually.equal(result);
- });
- }
- });
- describe('supportsModule', function () {
- it('supports MODULE_TYPE_VALIDATOR module type', async function () {
- await expect(this.mock.supportsModule(MODULE_TYPE_VALIDATOR)).to.eventually.equal(true);
- });
- it('supports MODULE_TYPE_EXECUTOR module type', async function () {
- await expect(this.mock.supportsModule(MODULE_TYPE_EXECUTOR)).to.eventually.equal(true);
- });
- it('supports MODULE_TYPE_FALLBACK module type', async function () {
- await expect(this.mock.supportsModule(MODULE_TYPE_FALLBACK)).to.eventually.equal(true);
- });
- it(
- withHooks ? 'supports MODULE_TYPE_HOOK module type' : 'does not support MODULE_TYPE_HOOK module type',
- async function () {
- await expect(this.mock.supportsModule(MODULE_TYPE_HOOK)).to.eventually.equal(withHooks);
- },
- );
- it('does not support invalid module type', async function () {
- await expect(this.mock.supportsModule(MODULE_TYPE_INVALID)).to.eventually.equal(false);
- });
- });
- describe('module installation', function () {
- it('should revert if the caller is not the canonical entrypoint or the account itself', async function () {
- await expect(this.mock.connect(this.other).installModule(MODULE_TYPE_VALIDATOR, this.mock, '0x'))
- .to.be.revertedWithCustomError(this.mock, 'AccountUnauthorized')
- .withArgs(this.other);
- });
- it('should revert if the module type is not supported', async function () {
- await expect(this.mockFromEntrypoint.installModule(MODULE_TYPE_INVALID, this.mock, '0x'))
- .to.be.revertedWithCustomError(this.mock, 'ERC7579UnsupportedModuleType')
- .withArgs(MODULE_TYPE_INVALID);
- });
- it('should revert if the module is not the provided type', async function () {
- const instance = this.modules[MODULE_TYPE_EXECUTOR];
- await expect(this.mockFromEntrypoint.installModule(MODULE_TYPE_VALIDATOR, instance, '0x'))
- .to.be.revertedWithCustomError(this.mock, 'ERC7579MismatchedModuleTypeId')
- .withArgs(MODULE_TYPE_VALIDATOR, instance);
- });
- for (const moduleTypeId of [
- MODULE_TYPE_VALIDATOR,
- MODULE_TYPE_EXECUTOR,
- MODULE_TYPE_FALLBACK,
- withHooks && MODULE_TYPE_HOOK,
- ].filter(Boolean)) {
- const prefix = moduleTypeId == MODULE_TYPE_FALLBACK ? '0x12345678' : '0x';
- const initData = ethers.hexlify(ethers.randomBytes(256));
- const fullData = ethers.concat([prefix, initData]);
- it(`should install a module of type ${moduleTypeId}`, async function () {
- const instance = this.modules[moduleTypeId];
- await expect(this.mock.isModuleInstalled(moduleTypeId, instance, fullData)).to.eventually.equal(false);
- await expect(this.mockFromEntrypoint.installModule(moduleTypeId, instance, fullData))
- .to.emit(this.mock, 'ModuleInstalled')
- .withArgs(moduleTypeId, instance)
- .to.emit(instance, 'ModuleInstalledReceived')
- .withArgs(this.mock, initData); // After decoding MODULE_TYPE_FALLBACK, it should remove the fnSig
- await expect(this.mock.isModuleInstalled(moduleTypeId, instance, fullData)).to.eventually.equal(true);
- });
- it(`does not allow to install a module of ${moduleTypeId} id twice`, async function () {
- const instance = this.modules[moduleTypeId];
- await this.mockFromEntrypoint.installModule(moduleTypeId, instance, fullData);
- await expect(this.mock.isModuleInstalled(moduleTypeId, instance, fullData)).to.eventually.equal(true);
- await expect(this.mockFromEntrypoint.installModule(moduleTypeId, instance, fullData))
- .to.be.revertedWithCustomError(
- this.mock,
- moduleTypeId == MODULE_TYPE_HOOK ? 'ERC7579HookModuleAlreadyPresent' : 'ERC7579AlreadyInstalledModule',
- )
- .withArgs(...[moduleTypeId != MODULE_TYPE_HOOK && moduleTypeId, instance].filter(Boolean));
- });
- }
- withHooks &&
- describe('with hook', function () {
- beforeEach(async function () {
- await this.mockFromEntrypoint.$_installModule(MODULE_TYPE_HOOK, this.modules[MODULE_TYPE_HOOK], '0x');
- });
- it('should call the hook of the installed module when performing an module install', async function () {
- const instance = this.modules[MODULE_TYPE_EXECUTOR];
- const initData = ethers.hexlify(ethers.randomBytes(256));
- const precheckData = this.mock.interface.encodeFunctionData('installModule', [
- MODULE_TYPE_EXECUTOR,
- instance.target,
- initData,
- ]);
- await expect(this.mockFromEntrypoint.installModule(MODULE_TYPE_EXECUTOR, instance, initData))
- .to.emit(this.modules[MODULE_TYPE_HOOK], 'PreCheck')
- .withArgs(predeploy.entrypoint.v08, 0n, precheckData)
- .to.emit(this.modules[MODULE_TYPE_HOOK], 'PostCheck')
- .withArgs(precheckData);
- });
- });
- });
- describe('module uninstallation', function () {
- it('should revert if the caller is not the canonical entrypoint or the account itself', async function () {
- await expect(this.mock.connect(this.other).uninstallModule(MODULE_TYPE_VALIDATOR, this.mock, '0x'))
- .to.be.revertedWithCustomError(this.mock, 'AccountUnauthorized')
- .withArgs(this.other);
- });
- it('should revert if the module type is not supported', async function () {
- await expect(this.mockFromEntrypoint.uninstallModule(MODULE_TYPE_INVALID, this.mock, '0x'))
- .to.be.revertedWithCustomError(this.mock, 'ERC7579UnsupportedModuleType')
- .withArgs(MODULE_TYPE_INVALID);
- });
- for (const moduleTypeId of [
- MODULE_TYPE_VALIDATOR,
- MODULE_TYPE_EXECUTOR,
- MODULE_TYPE_FALLBACK,
- withHooks && MODULE_TYPE_HOOK,
- ].filter(Boolean)) {
- const prefix = moduleTypeId == MODULE_TYPE_FALLBACK ? '0x12345678' : '0x';
- const initData = ethers.hexlify(ethers.randomBytes(256));
- const fullData = ethers.concat([prefix, initData]);
- it(`should uninstall a module of type ${moduleTypeId}`, async function () {
- const instance = this.modules[moduleTypeId];
- await this.mock.$_installModule(moduleTypeId, instance, fullData);
- await expect(this.mock.isModuleInstalled(moduleTypeId, instance, fullData)).to.eventually.equal(true);
- await expect(this.mockFromEntrypoint.uninstallModule(moduleTypeId, instance, fullData))
- .to.emit(this.mock, 'ModuleUninstalled')
- .withArgs(moduleTypeId, instance)
- .to.emit(instance, 'ModuleUninstalledReceived')
- .withArgs(this.mock, initData); // After decoding MODULE_TYPE_FALLBACK, it should remove the fnSig
- await expect(this.mock.isModuleInstalled(moduleTypeId, instance, fullData)).to.eventually.equal(false);
- });
- it(`should revert uninstalling a module of type ${moduleTypeId} if it was not installed`, async function () {
- const instance = this.modules[moduleTypeId];
- await expect(this.mockFromEntrypoint.uninstallModule(moduleTypeId, instance, fullData))
- .to.be.revertedWithCustomError(this.mock, 'ERC7579UninstalledModule')
- .withArgs(moduleTypeId, instance);
- });
- }
- it('should revert uninstalling a module of type MODULE_TYPE_FALLBACK if a different module was installed for the provided selector', async function () {
- const instance = this.modules[MODULE_TYPE_FALLBACK];
- const anotherInstance = await ethers.deployContract('$ERC7579ModuleMock', [MODULE_TYPE_FALLBACK]);
- const initData = '0x12345678abcdef';
- await this.mockFromEntrypoint.$_installModule(MODULE_TYPE_FALLBACK, instance, initData);
- await expect(this.mockFromEntrypoint.uninstallModule(MODULE_TYPE_FALLBACK, anotherInstance, initData))
- .to.be.revertedWithCustomError(this.mock, 'ERC7579UninstalledModule')
- .withArgs(MODULE_TYPE_FALLBACK, anotherInstance);
- });
- withHooks &&
- describe('with hook', function () {
- beforeEach(async function () {
- await this.mockFromEntrypoint.$_installModule(MODULE_TYPE_HOOK, this.modules[MODULE_TYPE_HOOK], '0x');
- });
- it('should call the hook of the installed module when performing a module uninstall', async function () {
- const instance = this.modules[MODULE_TYPE_EXECUTOR];
- const initData = ethers.hexlify(ethers.randomBytes(256));
- const precheckData = this.mock.interface.encodeFunctionData('uninstallModule', [
- MODULE_TYPE_EXECUTOR,
- instance.target,
- initData,
- ]);
- await this.mock.$_installModule(MODULE_TYPE_EXECUTOR, instance, initData);
- await expect(this.mockFromEntrypoint.uninstallModule(MODULE_TYPE_EXECUTOR, instance, initData))
- .to.emit(this.modules[MODULE_TYPE_HOOK], 'PreCheck')
- .withArgs(predeploy.entrypoint.v08, 0n, precheckData)
- .to.emit(this.modules[MODULE_TYPE_HOOK], 'PostCheck')
- .withArgs(precheckData);
- });
- });
- });
- describe('execution', function () {
- beforeEach(async function () {
- await this.mock.$_installModule(MODULE_TYPE_EXECUTOR, this.modules[MODULE_TYPE_EXECUTOR], '0x');
- });
- for (const [execFn, mock] of [
- ['execute', 'mockFromEntrypoint'],
- ['executeFromExecutor', 'mockFromExecutor'],
- ]) {
- describe(`executing with ${execFn}`, function () {
- it('should revert if the call type is not supported', async function () {
- await expect(
- this[mock][execFn](encodeMode({ callType: CALL_TYPE_INVALID }), encodeSingle(this.other, 0, '0x')),
- )
- .to.be.revertedWithCustomError(this.mock, 'ERC7579UnsupportedCallType')
- .withArgs(ethers.solidityPacked(['bytes1'], [CALL_TYPE_INVALID]));
- });
- it('should revert if the caller is not authorized / installed', async function () {
- const error = execFn == 'execute' ? 'AccountUnauthorized' : 'ERC7579UninstalledModule';
- const args = execFn == 'execute' ? [this.other] : [MODULE_TYPE_EXECUTOR, this.other];
- await expect(
- this[mock]
- .connect(this.other)
- [execFn](encodeMode({ callType: CALL_TYPE_CALL }), encodeSingle(this.other, 0, '0x')),
- )
- .to.be.revertedWithCustomError(this.mock, error)
- .withArgs(...args);
- });
- describe('single execution', function () {
- it('calls the target with value and args', async function () {
- const value = 0x432;
- const data = encodeSingle(
- this.target,
- value,
- this.target.interface.encodeFunctionData('mockFunctionWithArgs', [42, '0x1234']),
- );
- const tx = this[mock][execFn](encodeMode({ callType: CALL_TYPE_CALL }), data);
- await expect(tx).to.emit(this.target, 'MockFunctionCalledWithArgs').withArgs(42, '0x1234');
- await expect(tx).to.changeEtherBalances([this.mock, this.target], [-value, value]);
- });
- it('reverts when target reverts in default ExecType', async function () {
- const value = 0x012;
- const data = encodeSingle(
- this.target,
- value,
- this.target.interface.encodeFunctionData('mockFunctionRevertsReason'),
- );
- await expect(this[mock][execFn](encodeMode({ callType: CALL_TYPE_CALL }), data)).to.be.revertedWith(
- 'CallReceiverMock: reverting',
- );
- });
- it('emits ERC7579TryExecuteFail event when target reverts in try ExecType', async function () {
- const value = 0x012;
- const data = encodeSingle(
- this.target,
- value,
- this.target.interface.encodeFunctionData('mockFunctionRevertsReason'),
- );
- await expect(this[mock][execFn](encodeMode({ callType: CALL_TYPE_CALL, execType: EXEC_TYPE_TRY }), data))
- .to.emit(this.mock, 'ERC7579TryExecuteFail')
- .withArgs(
- CALL_TYPE_CALL,
- ethers.solidityPacked(
- ['bytes4', 'bytes'],
- [selector('Error(string)'), coder.encode(['string'], ['CallReceiverMock: reverting'])],
- ),
- );
- });
- });
- describe('batch execution', function () {
- it('calls the targets with value and args', async function () {
- const value1 = 0x012;
- const value2 = 0x234;
- const data = encodeBatch(
- [this.target, value1, this.target.interface.encodeFunctionData('mockFunctionWithArgs', [42, '0x1234'])],
- [
- this.anotherTarget,
- value2,
- this.anotherTarget.interface.encodeFunctionData('mockFunctionWithArgs', [42, '0x1234']),
- ],
- );
- const tx = this[mock][execFn](encodeMode({ callType: CALL_TYPE_BATCH }), data);
- await expect(tx)
- .to.emit(this.target, 'MockFunctionCalledWithArgs')
- .to.emit(this.anotherTarget, 'MockFunctionCalledWithArgs');
- await expect(tx).to.changeEtherBalances(
- [this.mock, this.target, this.anotherTarget],
- [-value1 - value2, value1, value2],
- );
- });
- it('reverts when any target reverts in default ExecType', async function () {
- const value1 = 0x012;
- const value2 = 0x234;
- const data = encodeBatch(
- [this.target, value1, this.target.interface.encodeFunctionData('mockFunction')],
- [
- this.anotherTarget,
- value2,
- this.anotherTarget.interface.encodeFunctionData('mockFunctionRevertsReason'),
- ],
- );
- await expect(this[mock][execFn](encodeMode({ callType: CALL_TYPE_BATCH }), data)).to.be.revertedWith(
- 'CallReceiverMock: reverting',
- );
- });
- it('emits ERC7579TryExecuteFail event when any target reverts in try ExecType', async function () {
- const value1 = 0x012;
- const value2 = 0x234;
- const data = encodeBatch(
- [this.target, value1, this.target.interface.encodeFunctionData('mockFunction')],
- [
- this.anotherTarget,
- value2,
- this.anotherTarget.interface.encodeFunctionData('mockFunctionRevertsReason'),
- ],
- );
- const tx = this[mock][execFn](encodeMode({ callType: CALL_TYPE_BATCH, execType: EXEC_TYPE_TRY }), data);
- await expect(tx)
- .to.emit(this.mock, 'ERC7579TryExecuteFail')
- .withArgs(
- CALL_TYPE_BATCH,
- ethers.solidityPacked(
- ['bytes4', 'bytes'],
- [selector('Error(string)'), coder.encode(['string'], ['CallReceiverMock: reverting'])],
- ),
- );
- await expect(tx).to.changeEtherBalances(
- [this.mock, this.target, this.anotherTarget],
- [-value1, value1, 0],
- );
- });
- });
- describe('delegate call execution', function () {
- it('delegate calls the target', async function () {
- const slot = ethers.hexlify(ethers.randomBytes(32));
- const value = ethers.hexlify(ethers.randomBytes(32));
- const data = encodeDelegate(
- this.target,
- this.target.interface.encodeFunctionData('mockFunctionWritesStorage', [slot, value]),
- );
- await expect(ethers.provider.getStorage(this.mock.target, slot)).to.eventually.equal(ethers.ZeroHash);
- await this[mock][execFn](encodeMode({ callType: CALL_TYPE_DELEGATE }), data);
- await expect(ethers.provider.getStorage(this.mock.target, slot)).to.eventually.equal(value);
- });
- it('reverts when target reverts in default ExecType', async function () {
- const data = encodeDelegate(
- this.target,
- this.target.interface.encodeFunctionData('mockFunctionRevertsReason'),
- );
- await expect(this[mock][execFn](encodeMode({ callType: CALL_TYPE_DELEGATE }), data)).to.be.revertedWith(
- 'CallReceiverMock: reverting',
- );
- });
- it('emits ERC7579TryExecuteFail event when target reverts in try ExecType', async function () {
- const data = encodeDelegate(
- this.target,
- this.target.interface.encodeFunctionData('mockFunctionRevertsReason'),
- );
- await expect(
- this[mock][execFn](encodeMode({ callType: CALL_TYPE_DELEGATE, execType: EXEC_TYPE_TRY }), data),
- )
- .to.emit(this.mock, 'ERC7579TryExecuteFail')
- .withArgs(
- CALL_TYPE_CALL,
- ethers.solidityPacked(
- ['bytes4', 'bytes'],
- [selector('Error(string)'), coder.encode(['string'], ['CallReceiverMock: reverting'])],
- ),
- );
- });
- });
- withHooks &&
- describe('with hook', function () {
- beforeEach(async function () {
- await this.mockFromEntrypoint.$_installModule(MODULE_TYPE_HOOK, this.modules[MODULE_TYPE_HOOK], '0x');
- });
- it(`should call the hook of the installed module when executing ${execFn}`, async function () {
- const caller = execFn === 'execute' ? predeploy.entrypoint.v08 : this.modules[MODULE_TYPE_EXECUTOR];
- const value = 17;
- const data = this.target.interface.encodeFunctionData('mockFunctionWithArgs', [42, '0x1234']);
- const mode = encodeMode({ callType: CALL_TYPE_CALL });
- const call = encodeSingle(this.target, value, data);
- const precheckData = this[mock].interface.encodeFunctionData(execFn, [mode, call]);
- const tx = this[mock][execFn](mode, call, { value });
- await expect(tx)
- .to.emit(this.modules[MODULE_TYPE_HOOK], 'PreCheck')
- .withArgs(caller, value, precheckData)
- .to.emit(this.modules[MODULE_TYPE_HOOK], 'PostCheck')
- .withArgs(precheckData);
- await expect(tx).to.changeEtherBalances([caller, this.mock, this.target], [-value, 0n, value]);
- });
- });
- });
- }
- });
- describe('fallback', function () {
- beforeEach(async function () {
- this.fallbackHandler = await ethers.deployContract('$ERC7579FallbackHandlerMock');
- });
- it('reverts if there is no fallback module installed', async function () {
- const { selector } = this.fallbackHandler.callPayable.getFragment();
- await expect(this.fallbackHandler.attach(this.mock).callPayable())
- .to.be.revertedWithCustomError(this.mock, 'ERC7579MissingFallbackHandler')
- .withArgs(selector);
- });
- describe('with a fallback module installed', function () {
- beforeEach(async function () {
- await Promise.all(
- [
- this.fallbackHandler.callPayable.getFragment().selector,
- this.fallbackHandler.callView.getFragment().selector,
- this.fallbackHandler.callRevert.getFragment().selector,
- ].map(selector =>
- this.mock.$_installModule(
- MODULE_TYPE_FALLBACK,
- this.fallbackHandler,
- coder.encode(['bytes4', 'bytes'], [selector, '0x']),
- ),
- ),
- );
- });
- it('forwards the call to the fallback handler', async function () {
- const calldata = this.fallbackHandler.interface.encodeFunctionData('callPayable');
- const value = 17n;
- await expect(this.fallbackHandler.attach(this.mock).connect(this.other).callPayable({ value }))
- .to.emit(this.fallbackHandler, 'ERC7579FallbackHandlerMockCalled')
- .withArgs(this.mock, this.other, value, calldata);
- });
- it('returns answer from the fallback handler', async function () {
- await expect(this.fallbackHandler.attach(this.mock).connect(this.other).callView()).to.eventually.deep.equal([
- this.mock.target,
- this.other.address,
- ]);
- });
- it('bubble up reverts from the fallback handler', async function () {
- await expect(
- this.fallbackHandler.attach(this.mock).connect(this.other).callRevert(),
- ).to.be.revertedWithCustomError(this.fallbackHandler, 'ERC7579FallbackHandlerMockRevert');
- });
- withHooks &&
- describe('with hook', function () {
- beforeEach(async function () {
- await this.mockFromEntrypoint.$_installModule(MODULE_TYPE_HOOK, this.modules[MODULE_TYPE_HOOK], '0x');
- });
- it('should call the hook of the installed module when performing a callback', async function () {
- const precheckData = this.fallbackHandler.interface.encodeFunctionData('callPayable');
- const value = 17n;
- // call with interface: decode returned data
- await expect(this.fallbackHandler.attach(this.mock).connect(this.other).callPayable({ value }))
- .to.emit(this.modules[MODULE_TYPE_HOOK], 'PreCheck')
- .withArgs(this.other, value, precheckData)
- .to.emit(this.modules[MODULE_TYPE_HOOK], 'PostCheck')
- .withArgs(precheckData);
- });
- });
- });
- });
- });
- }
- module.exports = {
- shouldBehaveLikeAccountERC7579,
- };
|