diff --git a/test/account/DirectCallsFromModule.t.sol b/test/account/DirectCallsFromModule.t.sol index 668ef0c2..c8100307 100644 --- a/test/account/DirectCallsFromModule.t.sol +++ b/test/account/DirectCallsFromModule.t.sol @@ -180,8 +180,4 @@ contract DirectCallsFromModuleTest is AccountTestBase { emit ValidationUninstalled(module, entityId, true); account1.uninstallValidation(_moduleEntity, "", new bytes[](1)); } - - function _buildDirectCallDisallowedError(bytes4 selector) internal pure returns (bytes memory) { - return abi.encodeWithSelector(ReferenceModularAccount.ValidationFunctionMissing.selector, selector); - } } diff --git a/test/account/SemiModularAccount.t.sol b/test/account/SemiModularAccount.t.sol new file mode 100644 index 00000000..894caefe --- /dev/null +++ b/test/account/SemiModularAccount.t.sol @@ -0,0 +1,107 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.19; + +import {AccountTestBase} from "../utils/AccountTestBase.sol"; + +import {ReferenceModularAccount} from "src/account/ReferenceModularAccount.sol"; +import {SemiModularAccount} from "src/account/SemiModularAccount.sol"; +import {ValidationConfig} from "src/helpers/ValidationConfigLib.sol"; + +import {LibClone} from "solady/utils/LibClone.sol"; + +contract SemiModularAccountTest is AccountTestBase { + SemiModularAccount internal _sma; + + address internal _other; + address internal _other2; // Used to ensure slots are not warmed + + function setUp() public { + // This is separate from the equivalence testing framework (with the env boolean variable "SMA_TEST") with + // the goal of testing specific SMA functionality, rather than equivalence. This is also why we deploy a + // new account. + SemiModularAccount impl = new SemiModularAccount(entryPoint); + _other = address(0x4546b); + _other2 = address(0x4546ab); + + bytes32 salt = bytes32(0); + bytes memory immutables = abi.encodePacked(address(owner1)); + (bool alreadyDeployed, address instance) = + LibClone.createDeterministicERC1967(address(impl), immutables, salt); + + assertFalse(alreadyDeployed); + + _sma = SemiModularAccount(payable(instance)); + } + + /* -------------------------------------------------------------------------- */ + /* Negatives */ + /* -------------------------------------------------------------------------- */ + + function test_Fail_InitializeDisabled() external { + ValidationConfig config; + bytes4[] memory selectors; + bytes memory installData; + bytes[] memory hooks; + + vm.expectRevert(SemiModularAccount.InitializerDisabled.selector); + _sma.initializeWithValidation(config, selectors, installData, hooks); + } + + function test_Fail_AccessControl_Functions() external { + vm.expectRevert(_buildDirectCallDisallowedError(SemiModularAccount.setFallbackSignerDisabled.selector)); + _sma.setFallbackSignerDisabled(true); + + vm.expectRevert(_buildDirectCallDisallowedError(SemiModularAccount.updateFallbackSigner.selector)); + _sma.updateFallbackSigner(address(0)); + } + + function test_Fail_ExecuteWithAuthorization_DisabledFallbackSigner() external { + vm.prank(address(entryPoint)); + _sma.setFallbackSignerDisabled(true); + + vm.expectRevert(SemiModularAccount.FallbackSignerDisabled.selector); + vm.prank(owner1); + _executeWithFallbackSigner(); + } + + function test_Fail_ExecuteWithAuthorization_BytecodeOverriden() external { + vm.prank(address(entryPoint)); + _sma.updateFallbackSigner(_other); + + vm.expectRevert(SemiModularAccount.FallbackSignerMismatch.selector); + vm.prank(owner1); + _executeWithFallbackSigner(); + } + + /* -------------------------------------------------------------------------- */ + /* Positives */ + /* -------------------------------------------------------------------------- */ + + function test_Pass_GetFallbackSigner_Bytecode() external { + assertEq(_sma.getFallbackSigner(), owner1); + } + + function test_Pass_GetFallbackSigner_Storage() external { + vm.prank(address(entryPoint)); + _sma.updateFallbackSigner(_other); + + assertEq(_sma.getFallbackSigner(), _other); + } + + function test_Pass_ExecuteWithAuthorization_FallbackSigner() external { + vm.prank(owner1); + _executeWithFallbackSigner(); + } + + /* -------------------------------------------------------------------------- */ + /* Internals */ + /* -------------------------------------------------------------------------- */ + + function _executeWithFallbackSigner() internal { + // _signerValidation is already the ModuleEntity for fallback validation + _sma.executeWithAuthorization( + abi.encodeCall(account1.execute, (_other2, 0, "")), + _encodeSignature(_signerValidation, GLOBAL_VALIDATION, "") + ); + } +} diff --git a/test/utils/AccountTestBase.sol b/test/utils/AccountTestBase.sol index f4e37462..7528af2a 100644 --- a/test/utils/AccountTestBase.sol +++ b/test/utils/AccountTestBase.sol @@ -277,4 +277,8 @@ abstract contract AccountTestBase is OptimizedTest { { return abi.encodePacked(uint32(validationData.length + 1), index, validationData); } + + function _buildDirectCallDisallowedError(bytes4 selector) internal pure returns (bytes memory) { + return abi.encodeWithSelector(ReferenceModularAccount.ValidationFunctionMissing.selector, selector); + } }