11const { ethers, predeploy } = require ( 'hardhat' ) ;
22const { expect } = require ( 'chai' ) ;
3+ const { setCode } = require ( '@nomicfoundation/hardhat-network-helpers' ) ;
34const { impersonate } = require ( '../../helpers/account' ) ;
45const { selector } = require ( '../../helpers/methods' ) ;
56const { zip } = require ( '../../helpers/iterate' ) ;
@@ -181,7 +182,7 @@ function shouldBehaveLikeAccountERC7579({ withHooks = false } = {}) {
181182 withHooks &&
182183 describe ( 'with hook' , function ( ) {
183184 beforeEach ( async function ( ) {
184- await this . mockFromEntrypoint . $_installModule ( MODULE_TYPE_HOOK , this . modules [ MODULE_TYPE_HOOK ] , '0x' ) ;
185+ await this . mock . $_installModule ( MODULE_TYPE_HOOK , this . modules [ MODULE_TYPE_HOOK ] , '0x' ) ;
185186 } ) ;
186187
187188 it ( 'should call the hook of the installed module when performing an module install' , async function ( ) {
@@ -267,7 +268,7 @@ function shouldBehaveLikeAccountERC7579({ withHooks = false } = {}) {
267268 const anotherInstance = await ethers . deployContract ( '$ERC7579ModuleMock' , [ MODULE_TYPE_FALLBACK ] ) ;
268269 const initData = '0x12345678abcdef' ;
269270
270- await this . mockFromEntrypoint . $_installModule ( MODULE_TYPE_FALLBACK , instance , initData ) ;
271+ await this . mock . $_installModule ( MODULE_TYPE_FALLBACK , instance , initData ) ;
271272 await expect ( this . mockFromEntrypoint . uninstallModule ( MODULE_TYPE_FALLBACK , anotherInstance , initData ) )
272273 . to . be . revertedWithCustomError ( this . mock , 'ERC7579UninstalledModule' )
273274 . withArgs ( MODULE_TYPE_FALLBACK , anotherInstance ) ;
@@ -296,7 +297,8 @@ function shouldBehaveLikeAccountERC7579({ withHooks = false } = {}) {
296297 withHooks &&
297298 describe ( 'with hook' , function ( ) {
298299 beforeEach ( async function ( ) {
299- await this . mockFromEntrypoint . $_installModule ( MODULE_TYPE_HOOK , this . modules [ MODULE_TYPE_HOOK ] , '0x' ) ;
300+ await this . mock . $_installModule ( MODULE_TYPE_EXECUTOR , this . modules [ MODULE_TYPE_EXECUTOR ] , '0x' ) ;
301+ await this . mock . $_installModule ( MODULE_TYPE_HOOK , this . modules [ MODULE_TYPE_HOOK ] , '0x' ) ;
300302 } ) ;
301303
302304 it ( 'should call the hook of the installed module when performing a module uninstall' , async function ( ) {
@@ -309,13 +311,114 @@ function shouldBehaveLikeAccountERC7579({ withHooks = false } = {}) {
309311 initData ,
310312 ] ) ;
311313
312- await this . mock . $_installModule ( MODULE_TYPE_EXECUTOR , instance , initData ) ;
313314 await expect ( this . mockFromEntrypoint . uninstallModule ( MODULE_TYPE_EXECUTOR , instance , initData ) )
314315 . to . emit ( this . modules [ MODULE_TYPE_HOOK ] , 'PreCheck' )
315316 . withArgs ( predeploy . entrypoint . v09 , 0n , precheckData )
316317 . to . emit ( this . modules [ MODULE_TYPE_HOOK ] , 'PostCheck' )
317318 . withArgs ( precheckData ) ;
318319 } ) ;
320+
321+ it ( 'hook revert during the pre-check prevents uninstalling a non-hook module' , async function ( ) {
322+ const instance = this . modules [ MODULE_TYPE_EXECUTOR ] ;
323+ const initData = ethers . hexlify ( ethers . randomBytes ( 256 ) ) ;
324+
325+ // Set the hook to revert on preCheck
326+ await this . modules [ MODULE_TYPE_HOOK ] . revertOnPreCheck ( true ) ;
327+
328+ await expect (
329+ this . mockFromEntrypoint . uninstallModule ( MODULE_TYPE_EXECUTOR , instance , initData ) ,
330+ ) . to . be . revertedWith ( 'preCheck reverts' ) ;
331+ } ) ;
332+
333+ it ( 'hook revert during the post-check prevents uninstalling a non-hook module' , async function ( ) {
334+ const instance = this . modules [ MODULE_TYPE_EXECUTOR ] ;
335+ const initData = ethers . hexlify ( ethers . randomBytes ( 256 ) ) ;
336+
337+ // Set the hook to revert on postCheck
338+ await this . modules [ MODULE_TYPE_HOOK ] . revertOnPostCheck ( true ) ;
339+
340+ await expect (
341+ this . mockFromEntrypoint . uninstallModule ( MODULE_TYPE_EXECUTOR , instance , initData ) ,
342+ ) . to . be . revertedWith ( 'postCheck reverts' ) ;
343+ } ) ;
344+
345+ it ( 'can uninstall a hook module that reverts during its pre-check' , async function ( ) {
346+ const instance = this . modules [ MODULE_TYPE_HOOK ] ;
347+ const initData = ethers . hexlify ( ethers . randomBytes ( 256 ) ) ;
348+
349+ // Set the hook to revert on preCheck
350+ await instance . revertOnPreCheck ( true ) ;
351+
352+ // Should uninstall
353+ await expect ( this . mockFromEntrypoint . uninstallModule ( MODULE_TYPE_HOOK , instance , initData ) )
354+ . to . emit ( this . mock , 'ModuleUninstalled' )
355+ . withArgs ( MODULE_TYPE_HOOK , instance )
356+ . to . not . emit ( instance , 'PreCheck' )
357+ . to . not . emit ( instance , 'PostCheck' ) ;
358+
359+ await expect ( this . mock . isModuleInstalled ( MODULE_TYPE_HOOK , instance , initData ) ) . to . eventually . equal ( false ) ;
360+ } ) ;
361+
362+ it ( 'can uninstall a hook module that reverts during its post-check' , async function ( ) {
363+ const instance = this . modules [ MODULE_TYPE_HOOK ] ;
364+ const initData = ethers . hexlify ( ethers . randomBytes ( 256 ) ) ;
365+
366+ // Set the hook to revert on postCheck
367+ await instance . revertOnPostCheck ( true ) ;
368+
369+ // Should uninstall
370+ await expect ( this . mockFromEntrypoint . uninstallModule ( MODULE_TYPE_HOOK , instance , initData ) )
371+ . to . emit ( this . mock , 'ModuleUninstalled' )
372+ . withArgs ( MODULE_TYPE_HOOK , instance )
373+ . to . emit ( instance , 'PreCheck' )
374+ . withArgs (
375+ predeploy . entrypoint . v09 ,
376+ 0n ,
377+ this . mock . interface . encodeFunctionData ( 'uninstallModule' , [
378+ MODULE_TYPE_HOOK ,
379+ instance . target ,
380+ initData ,
381+ ] ) ,
382+ )
383+ . to . not . emit ( instance , 'PostCheck' ) ;
384+
385+ await expect ( this . mock . isModuleInstalled ( MODULE_TYPE_HOOK , instance , initData ) ) . to . eventually . equal ( false ) ;
386+ } ) ;
387+
388+ it ( 'can uninstall a hook module that reverts during both pre-check and post-check' , async function ( ) {
389+ const instance = this . modules [ MODULE_TYPE_HOOK ] ;
390+ const initData = ethers . hexlify ( ethers . randomBytes ( 256 ) ) ;
391+
392+ // Set the hook to revert on preCheck and postCheck
393+ await instance . revertOnPreCheck ( true ) ;
394+ await instance . revertOnPostCheck ( true ) ;
395+
396+ // Should uninstall
397+ await expect ( this . mockFromEntrypoint . uninstallModule ( MODULE_TYPE_HOOK , instance , initData ) )
398+ . to . emit ( this . mock , 'ModuleUninstalled' )
399+ . withArgs ( MODULE_TYPE_HOOK , instance )
400+ . to . not . emit ( instance , 'PreCheck' )
401+ . to . not . emit ( instance , 'PostCheck' ) ;
402+
403+ await expect ( this . mock . isModuleInstalled ( MODULE_TYPE_HOOK , instance , initData ) ) . to . eventually . equal ( false ) ;
404+ } ) ;
405+
406+ it ( 'can uninstall a hook module that has no code (removed delegation)' , async function ( ) {
407+ const instance = this . modules [ MODULE_TYPE_HOOK ] ;
408+ const initData = ethers . hexlify ( ethers . randomBytes ( 256 ) ) ;
409+
410+ // Delete the code of the module to simulate a removed delegation
411+ await setCode ( instance . target , '0x' ) ;
412+
413+ // Should uninstall
414+ await expect ( this . mockFromEntrypoint . uninstallModule ( MODULE_TYPE_HOOK , instance , initData ) )
415+ . to . emit ( this . mock , 'ModuleUninstalled' )
416+ . withArgs ( MODULE_TYPE_HOOK , instance )
417+ . to . not . emit ( instance , 'PreCheck' )
418+ . to . not . emit ( instance , 'PostCheck' ) ;
419+
420+ await expect ( this . mock . isModuleInstalled ( MODULE_TYPE_HOOK , instance , initData ) ) . to . eventually . equal ( false ) ;
421+ } ) ;
319422 } ) ;
320423 } ) ;
321424
@@ -515,7 +618,7 @@ function shouldBehaveLikeAccountERC7579({ withHooks = false } = {}) {
515618 withHooks &&
516619 describe ( 'with hook' , function ( ) {
517620 beforeEach ( async function ( ) {
518- await this . mockFromEntrypoint . $_installModule ( MODULE_TYPE_HOOK , this . modules [ MODULE_TYPE_HOOK ] , '0x' ) ;
621+ await this . mock . $_installModule ( MODULE_TYPE_HOOK , this . modules [ MODULE_TYPE_HOOK ] , '0x' ) ;
519622 } ) ;
520623
521624 it ( `should call the hook of the installed module when executing ${ execFn } ` , async function ( ) {
@@ -596,7 +699,7 @@ function shouldBehaveLikeAccountERC7579({ withHooks = false } = {}) {
596699 withHooks &&
597700 describe ( 'with hook' , function ( ) {
598701 beforeEach ( async function ( ) {
599- await this . mockFromEntrypoint . $_installModule ( MODULE_TYPE_HOOK , this . modules [ MODULE_TYPE_HOOK ] , '0x' ) ;
702+ await this . mock . $_installModule ( MODULE_TYPE_HOOK , this . modules [ MODULE_TYPE_HOOK ] , '0x' ) ;
600703 } ) ;
601704
602705 it ( 'should call the hook of the installed module when performing a callback' , async function ( ) {
0 commit comments