diff --git a/foundry.toml b/foundry.toml index beec336..6e27de2 100644 --- a/foundry.toml +++ b/foundry.toml @@ -24,8 +24,8 @@ ignored_warnings_from = [ [rpc_endpoints] sepolia = "${BASE_SEPOLIA_RPC_URL}" -# [etherscan] -# sepolia = { key = "${BASE_SCAN_API_KEY}" } +[etherscan] +sepolia = { key = "${BASE_SCAN_API_KEY}" } [fmt] bracket_spacing = true diff --git a/script/ComputeSafeRecoveryCalldata.s.sol b/script/ComputeSafeRecoveryCalldata.s.sol new file mode 100644 index 0000000..44e5a60 --- /dev/null +++ b/script/ComputeSafeRecoveryCalldata.s.sol @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.25; + +import { Script } from "forge-std/Script.sol"; +import { console } from "forge-std/console.sol"; + +contract ComputeSafeRecoveryCalldataScript is Script { + function run() public { + address oldOwner = vm.envAddress("OLD_OWNER"); + address newOwner = vm.envAddress("NEW_OWNER"); + address previousOwnerInLinkedList = address(1); + + bytes memory recoveryCalldata = abi.encodeWithSignature( + "swapOwner(address,address,address)", previousOwnerInLinkedList, oldOwner, newOwner + ); + + console.log("recoveryCalldata", vm.toString(recoveryCalldata)); + } +} diff --git a/script/DeploySafeNativeRecovery.s.sol b/script/DeploySafeNativeRecovery.s.sol new file mode 100644 index 0000000..b037b74 --- /dev/null +++ b/script/DeploySafeNativeRecovery.s.sol @@ -0,0 +1,51 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.25; + +import { Script } from "forge-std/Script.sol"; +import { console } from "forge-std/console.sol"; +import { Verifier } from "ether-email-auth/packages/contracts/src/utils/Verifier.sol"; +import { ECDSAOwnedDKIMRegistry } from + "ether-email-auth/packages/contracts/src/utils/ECDSAOwnedDKIMRegistry.sol"; +import { EmailAuth } from "ether-email-auth/packages/contracts/src/EmailAuth.sol"; +import { SafeRecoverySubjectHandler } from "src/handlers/SafeRecoverySubjectHandler.sol"; +import { SafeEmailRecoveryModule } from "src/modules/SafeEmailRecoveryModule.sol"; + +contract DeploySafeNativeRecovery_Script is Script { + function run() public { + vm.startBroadcast(vm.envUint("PRIVATE_KEY")); + address verifier = vm.envOr("VERIFIER", address(0)); + address dkimRegistry = vm.envOr("DKIM_REGISTRY", address(0)); + address dkimRegistrySigner = vm.envOr("SIGNER", address(0)); + address emailAuthImpl = vm.envOr("EMAIL_AUTH_IMPL", address(0)); + address subjectHandler = vm.envOr("SUBJECT_HANDLER", address(0)); + + if (verifier == address(0)) { + verifier = address(new Verifier()); + console.log("Deployed Verifier at", verifier); + } + + if (dkimRegistry == address(0)) { + require(dkimRegistrySigner != address(0), "DKIM_REGISTRY_SIGNER is required"); + dkimRegistry = address(new ECDSAOwnedDKIMRegistry(dkimRegistrySigner)); + console.log("Deployed DKIM Registry at", dkimRegistry); + } + + if (emailAuthImpl == address(0)) { + emailAuthImpl = address(new EmailAuth()); + console.log("Deployed Email Auth at", emailAuthImpl); + } + + if (subjectHandler == address(0)) { + subjectHandler = address(new SafeRecoverySubjectHandler()); + console.log("Deployed Subject Handler at", subjectHandler); + } + + address module = address( + new SafeEmailRecoveryModule(verifier, dkimRegistry, emailAuthImpl, subjectHandler) + ); + + console.log("Deployed Email Recovery Module at ", vm.toString(module)); + + vm.stopBroadcast(); + } +} diff --git a/src/EmailRecoveryManager.sol b/src/EmailRecoveryManager.sol index 439fa8e..372978c 100644 --- a/src/EmailRecoveryManager.sol +++ b/src/EmailRecoveryManager.sol @@ -201,7 +201,7 @@ abstract contract EmailRecoveryManager is uint256 delay, uint256 expiry ) - internal + public { address account = msg.sender; diff --git a/src/interfaces/ISafe.sol b/src/interfaces/ISafe.sol index e8d0adb..e47642c 100644 --- a/src/interfaces/ISafe.sol +++ b/src/interfaces/ISafe.sol @@ -7,4 +7,21 @@ interface ISafe { function getOwners() external view returns (address[] memory); function setFallbackHandler(address handler) external; function setGuard(address guard) external; + function execTransactionFromModule( + address to, + uint256 value, + bytes memory data, + uint8 operation + ) + external + returns (bool success); + function execTransactionFromModuleReturnData( + address to, + uint256 value, + bytes memory data, + uint8 operation + ) + external + returns (bool success, bytes memory returnData); + function isModuleEnabled(address module) external view returns (bool); } diff --git a/src/modules/SafeEmailRecoveryModule.sol b/src/modules/SafeEmailRecoveryModule.sol new file mode 100644 index 0000000..c4dfe9d --- /dev/null +++ b/src/modules/SafeEmailRecoveryModule.sol @@ -0,0 +1,74 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.25; + +import { ISafe } from "../interfaces/ISafe.sol"; +import { Enum } from "@safe-global/safe-contracts/contracts/common/Enum.sol"; +import { EmailRecoveryManager } from "../EmailRecoveryManager.sol"; + +/** + * A safe module that recovers a safe owner via ZK Email + */ +contract SafeEmailRecoveryModule is EmailRecoveryManager { + bytes4 public constant selector = bytes4(keccak256(bytes("swapOwner(address,address,address)"))); + + event RecoveryExecuted(address indexed account); + + error InvalidAccount(address account); + error InvalidSelector(bytes4 selector); + error RecoveryFailed(address account); + + constructor( + address verifier, + address dkimRegistry, + address emailAuthImpl, + address subjectHandler + ) + EmailRecoveryManager(verifier, dkimRegistry, emailAuthImpl, subjectHandler) + { } + + /** + * Check if a recovery request can be initiated based on guardian acceptance + * @param account The smart account to check + * @return true if the recovery request can be started, false otherwise + */ + function canStartRecoveryRequest(address account) external view returns (bool) { + GuardianConfig memory guardianConfig = getGuardianConfig(account); + + return guardianConfig.acceptedWeight >= guardianConfig.threshold; + } + + /** + * @notice Executes recovery on a Safe account. Called from the recovery manager + * @param account The account to execute recovery for + * @param recoveryData The recovery data that should be executed on the Safe + * being recovered. recoveryData = abi.encode(safeAccount, recoveryFunctionCalldata) + */ + function recover(address account, bytes calldata recoveryData) internal override { + (address encodedAccount, bytes memory recoveryCalldata) = + abi.decode(recoveryData, (address, bytes)); + + if (encodedAccount == address(0) || encodedAccount != account) { + revert InvalidAccount(encodedAccount); + } + + bytes4 calldataSelector; + assembly { + calldataSelector := mload(add(recoveryCalldata, 32)) + } + if (calldataSelector != selector) { + revert InvalidSelector(calldataSelector); + } + + bool success = ISafe(account).execTransactionFromModule({ + to: account, + value: 0, + data: recoveryCalldata, + operation: uint8(Enum.Operation.Call) + }); + if (!success) { + revert RecoveryFailed(account); + } + + emit RecoveryExecuted(account); + } +} diff --git a/test/integration/OwnableValidatorRecovery/EmailRecoveryModule/EmailRecoveryModule.t.sol b/test/integration/OwnableValidatorRecovery/EmailRecoveryModule/EmailRecoveryModule.t.sol index 16705d1..c63804b 100644 --- a/test/integration/OwnableValidatorRecovery/EmailRecoveryModule/EmailRecoveryModule.t.sol +++ b/test/integration/OwnableValidatorRecovery/EmailRecoveryModule/EmailRecoveryModule.t.sol @@ -67,6 +67,7 @@ contract OwnableValidatorRecovery_EmailRecoveryModule_Integration_Test is assertEq(recoveryRequest.executeAfter, 0); assertEq(recoveryRequest.executeBefore, 0); assertEq(recoveryRequest.currentWeight, 1); + assertEq(recoveryRequest.recoveryDataHash, recoveryDataHash1); // handle recovery request for guardian 2 uint256 executeAfter = block.timestamp + delay; @@ -76,6 +77,7 @@ contract OwnableValidatorRecovery_EmailRecoveryModule_Integration_Test is assertEq(recoveryRequest.executeAfter, executeAfter); assertEq(recoveryRequest.executeBefore, executeBefore); assertEq(recoveryRequest.currentWeight, 3); + assertEq(recoveryRequest.recoveryDataHash, recoveryDataHash1); // Time travel so that the recovery delay has passed vm.warp(block.timestamp + delay); @@ -89,6 +91,7 @@ contract OwnableValidatorRecovery_EmailRecoveryModule_Integration_Test is assertEq(recoveryRequest.executeAfter, 0); assertEq(recoveryRequest.executeBefore, 0); assertEq(recoveryRequest.currentWeight, 0); + assertEq(recoveryRequest.recoveryDataHash, bytes32(0)); assertEq(updatedOwner, newOwner1); } diff --git a/test/integration/OwnableValidatorRecovery/UniversalEmailRecoveryModule/UniversalEmailRecoveryModule.t.sol b/test/integration/OwnableValidatorRecovery/UniversalEmailRecoveryModule/UniversalEmailRecoveryModule.t.sol index f5eb74c..6fa9462 100644 --- a/test/integration/OwnableValidatorRecovery/UniversalEmailRecoveryModule/UniversalEmailRecoveryModule.t.sol +++ b/test/integration/OwnableValidatorRecovery/UniversalEmailRecoveryModule/UniversalEmailRecoveryModule.t.sol @@ -66,6 +66,7 @@ contract OwnableValidatorRecovery_UniversalEmailRecoveryModule_Integration_Test assertEq(recoveryRequest.executeAfter, 0); assertEq(recoveryRequest.executeBefore, 0); assertEq(recoveryRequest.currentWeight, 1); + assertEq(recoveryRequest.recoveryDataHash, recoveryDataHash1); // handle recovery request for guardian 2 uint256 executeAfter = block.timestamp + delay; @@ -75,6 +76,7 @@ contract OwnableValidatorRecovery_UniversalEmailRecoveryModule_Integration_Test assertEq(recoveryRequest.executeAfter, executeAfter); assertEq(recoveryRequest.executeBefore, executeBefore); assertEq(recoveryRequest.currentWeight, 3); + assertEq(recoveryRequest.recoveryDataHash, recoveryDataHash1); // Time travel so that the recovery delay has passed vm.warp(block.timestamp + delay); @@ -88,6 +90,7 @@ contract OwnableValidatorRecovery_UniversalEmailRecoveryModule_Integration_Test assertEq(recoveryRequest.executeAfter, 0); assertEq(recoveryRequest.executeBefore, 0); assertEq(recoveryRequest.currentWeight, 0); + assertEq(recoveryRequest.recoveryDataHash, bytes32(0)); assertEq(updatedOwner, newOwner1); } diff --git a/test/integration/SafeRecovery/SafeNativeIntegrationBase.t.sol b/test/integration/SafeRecovery/SafeNativeIntegrationBase.t.sol new file mode 100644 index 0000000..cefabaa --- /dev/null +++ b/test/integration/SafeRecovery/SafeNativeIntegrationBase.t.sol @@ -0,0 +1,225 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.25; + +import { console2 } from "forge-std/console2.sol"; + +import { ModuleKitHelpers } from "modulekit/ModuleKit.sol"; +import { MODULE_TYPE_EXECUTOR } from "modulekit/external/ERC7579.sol"; +import { EmailAuthMsg, EmailProof } from "ether-email-auth/packages/contracts/src/EmailAuth.sol"; +import { SubjectUtils } from "ether-email-auth/packages/contracts/src/libraries/SubjectUtils.sol"; +import { Strings } from "@openzeppelin/contracts/utils/Strings.sol"; + +import { Safe } from "@safe-global/safe-contracts/contracts/Safe.sol"; +import { SafeProxy } from "@safe-global/safe-contracts/contracts/proxies/SafeProxy.sol"; +import { SafeEmailRecoveryModule } from "src/modules/SafeEmailRecoveryModule.sol"; +import { SafeRecoverySubjectHandler } from "src/handlers/SafeRecoverySubjectHandler.sol"; +import { IntegrationBase } from "../IntegrationBase.t.sol"; + +abstract contract SafeNativeIntegrationBase is IntegrationBase { + using ModuleKitHelpers for *; + using Strings for uint256; + using Strings for address; + + SafeEmailRecoveryModule emailRecoveryModule; + address emailRecoveryModuleAddress; + Safe public safeSingleton; + Safe public safe; + address public safeAddress; + address public owner; + bytes isInstalledContext; + bytes4 functionSelector; + uint256 nullifierCount; + address subjectHandler; + + /** + * Helper function to return if current account type is safe or not + */ + function isAccountTypeSafe() public returns (bool) { + string memory currentAccountType = vm.envOr("ACCOUNT_TYPE", string("")); + if (Strings.equal(currentAccountType, "SAFE")) { + return true; + } else { + return false; + } + } + + function skipIfNotSafeAccountType() public { + if (isAccountTypeSafe()) { + vm.skip(false); + } else { + vm.skip(true); + } + } + + function setUp() public virtual override { + if (!isAccountTypeSafe()) { + return; + } + super.setUp(); + + subjectHandler = address(new SafeRecoverySubjectHandler()); + emailRecoveryModule = new SafeEmailRecoveryModule( + address(verifier), + address(dkimRegistry), + address(emailAuthImpl), + address(subjectHandler) + ); + emailRecoveryModuleAddress = address(emailRecoveryModule); + + safeSingleton = new Safe(); + SafeProxy safeProxy = new SafeProxy(address(safeSingleton)); + safe = Safe(payable(address(safeProxy))); + safeAddress = address(safe); + + isInstalledContext = bytes("0"); + functionSelector = bytes4(keccak256(bytes("swapOwner(address,address,address)"))); + + // Compute guardian addresses + guardians1 = new address[](3); + guardians1[0] = emailRecoveryModule.computeEmailAuthAddress(safeAddress, accountSalt1); + guardians1[1] = emailRecoveryModule.computeEmailAuthAddress(safeAddress, accountSalt2); + guardians1[2] = emailRecoveryModule.computeEmailAuthAddress(safeAddress, accountSalt3); + + address[] memory owners = new address[](1); + owner = owner1; + owners[0] = owner; + + safe.setup( + owners, 1, address(0), bytes("0"), address(0), address(0), 0, payable(address(0)) + ); + + vm.startPrank(safeAddress); + safe.enableModule(address(emailRecoveryModule)); + vm.stopPrank(); + } + + function generateMockEmailProof( + string memory subject, + bytes32 nullifier, + bytes32 accountSalt + ) + public + view + returns (EmailProof memory) + { + EmailProof memory emailProof; + emailProof.domainName = "gmail.com"; + emailProof.publicKeyHash = bytes32( + vm.parseUint( + "6632353713085157925504008443078919716322386156160602218536961028046468237192" + ) + ); + emailProof.timestamp = block.timestamp; + emailProof.maskedSubject = subject; + emailProof.emailNullifier = nullifier; + emailProof.accountSalt = accountSalt; + emailProof.isCodeExist = true; + emailProof.proof = bytes("0"); + + return emailProof; + } + + function getAccountSaltForGuardian(address guardian) public returns (bytes32) { + if (guardian == guardians1[0]) { + return accountSalt1; + } + if (guardian == guardians1[1]) { + return accountSalt2; + } + if (guardian == guardians1[2]) { + return accountSalt3; + } + + revert("Invalid guardian address"); + } + + function generateNewNullifier() public returns (bytes32) { + return keccak256(abi.encode(nullifierCount++)); + } + + function acceptGuardian(address account, address guardian) public { + EmailAuthMsg memory emailAuthMsg = getAcceptanceEmailAuthMessage(account, guardian); + emailRecoveryModule.handleAcceptance(emailAuthMsg, templateIdx); + } + + function getAcceptanceEmailAuthMessage( + address account, + address guardian + ) + public + returns (EmailAuthMsg memory) + { + string memory accountString = SubjectUtils.addressToChecksumHexString(account); + string memory subject = string.concat("Accept guardian request for ", accountString); + bytes32 nullifier = generateNewNullifier(); + bytes32 accountSalt = getAccountSaltForGuardian(guardian); + + EmailProof memory emailProof = generateMockEmailProof(subject, nullifier, accountSalt); + + bytes[] memory subjectParamsForAcceptance = new bytes[](1); + subjectParamsForAcceptance[0] = abi.encode(account); + return EmailAuthMsg({ + templateId: emailRecoveryModule.computeAcceptanceTemplateId(templateIdx), + subjectParams: subjectParamsForAcceptance, + skipedSubjectPrefix: 0, + proof: emailProof + }); + } + + function handleRecovery( + address account, + address oldOwner, + address newOwner, + address guardian + ) + public + { + EmailAuthMsg memory emailAuthMsg = + getRecoveryEmailAuthMessage(account, oldOwner, newOwner, guardian); + emailRecoveryModule.handleRecovery(emailAuthMsg, templateIdx); + } + + function getRecoveryEmailAuthMessage( + address account, + address oldOwner, + address newOwner, + address guardian + ) + public + returns (EmailAuthMsg memory) + { + string memory accountString = SubjectUtils.addressToChecksumHexString(account); + string memory oldOwnerString = SubjectUtils.addressToChecksumHexString(oldOwner); + string memory newOwnerString = SubjectUtils.addressToChecksumHexString(newOwner); + string memory recoveryModuleString = + SubjectUtils.addressToChecksumHexString(address(emailRecoveryModule)); + + string memory subject = string.concat( + "Recover account ", + accountString, + " from old owner ", + oldOwnerString, + " to new owner ", + newOwnerString, + " using recovery module ", + recoveryModuleString + ); + bytes32 nullifier = generateNewNullifier(); + bytes32 accountSalt = getAccountSaltForGuardian(guardian); + + EmailProof memory emailProof = generateMockEmailProof(subject, nullifier, accountSalt); + + bytes[] memory subjectParamsForRecovery = new bytes[](4); + subjectParamsForRecovery[0] = abi.encode(account); + subjectParamsForRecovery[1] = abi.encode(oldOwner); + subjectParamsForRecovery[2] = abi.encode(newOwner); + subjectParamsForRecovery[3] = abi.encode(emailRecoveryModuleAddress); + + return EmailAuthMsg({ + templateId: emailRecoveryModule.computeRecoveryTemplateId(templateIdx), + subjectParams: subjectParamsForRecovery, + skipedSubjectPrefix: 0, + proof: emailProof + }); + } +} diff --git a/test/integration/SafeRecovery/SafeRecoveryNativeModule.t.sol b/test/integration/SafeRecovery/SafeRecoveryNativeModule.t.sol new file mode 100644 index 0000000..6eae5a2 --- /dev/null +++ b/test/integration/SafeRecovery/SafeRecoveryNativeModule.t.sol @@ -0,0 +1,101 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.25; + +import { console2 } from "forge-std/console2.sol"; +import { ModuleKitHelpers, ModuleKitUserOp } from "modulekit/ModuleKit.sol"; +import { MODULE_TYPE_EXECUTOR } from "erc7579/interfaces/IERC7579Module.sol"; +import { IERC7579Account } from "erc7579/interfaces/IERC7579Account.sol"; +import { Safe } from "@safe-global/safe-contracts/contracts/Safe.sol"; +import { SafeProxy } from "@safe-global/safe-contracts/contracts/proxies/SafeProxy.sol"; +import { Strings } from "@openzeppelin/contracts/utils/Strings.sol"; +import { EmailAuthMsg } from "ether-email-auth/packages/contracts/src/EmailAuth.sol"; +import { SafeEmailRecoveryModule } from "src/modules/SafeEmailRecoveryModule.sol"; +import { IEmailRecoveryManager } from "src/interfaces/IEmailRecoveryManager.sol"; +import { GuardianStorage, GuardianStatus } from "src/libraries/EnumerableGuardianMap.sol"; +import { SafeNativeIntegrationBase } from "./SafeNativeIntegrationBase.t.sol"; + +contract SafeRecoveryNativeModule_Integration_Test is SafeNativeIntegrationBase { + function setUp() public override { + super.setUp(); + } + + function testIntegration_AccountRecovery() public { + skipIfNotSafeAccountType(); + + address newOwner = owner2; + // Configure recovery + vm.startPrank(safeAddress); + emailRecoveryModule.configureRecovery(guardians1, guardianWeights, threshold, delay, expiry); + vm.stopPrank(); + + bytes memory recoveryCalldata = abi.encodeWithSignature( + "swapOwner(address,address,address)", address(1), owner, newOwner + ); + bytes memory recoveryData = abi.encode(safeAddress, recoveryCalldata); + bytes32 recoveryDataHash = keccak256(recoveryData); + + bytes[] memory subjectParamsForRecovery = new bytes[](4); + subjectParamsForRecovery[0] = abi.encode(safeAddress); + subjectParamsForRecovery[1] = abi.encode(owner); + subjectParamsForRecovery[2] = abi.encode(newOwner); + subjectParamsForRecovery[3] = abi.encode(emailRecoveryModuleAddress); + + // Accept guardian + EmailAuthMsg memory emailAuthMsg = getAcceptanceEmailAuthMessage(safeAddress, guardians1[0]); + emailRecoveryModule.handleAcceptance(emailAuthMsg, templateIdx); + GuardianStorage memory guardianStorage1 = + emailRecoveryModule.getGuardian(safeAddress, guardians1[0]); + assertEq(uint256(guardianStorage1.status), uint256(GuardianStatus.ACCEPTED)); + assertEq(guardianStorage1.weight, uint256(1)); + + // Accept guardian + emailAuthMsg = getAcceptanceEmailAuthMessage(safeAddress, guardians1[1]); + emailRecoveryModule.handleAcceptance(emailAuthMsg, templateIdx); + GuardianStorage memory guardianStorage2 = + emailRecoveryModule.getGuardian(safeAddress, guardians1[1]); + assertEq(uint256(guardianStorage2.status), uint256(GuardianStatus.ACCEPTED)); + assertEq(guardianStorage2.weight, uint256(2)); + + // Time travel so that EmailAuth timestamp is valid + vm.warp(12 seconds); + + // handle recovery request for guardian 1 + emailAuthMsg = getRecoveryEmailAuthMessage(safeAddress, owner, newOwner, guardians1[0]); + emailRecoveryModule.handleRecovery(emailAuthMsg, templateIdx); + IEmailRecoveryManager.RecoveryRequest memory recoveryRequest = + emailRecoveryModule.getRecoveryRequest(safeAddress); + assertEq(recoveryRequest.executeAfter, 0); + assertEq(recoveryRequest.executeBefore, 0); + assertEq(recoveryRequest.currentWeight, 1); + assertEq(recoveryRequest.recoveryDataHash, recoveryDataHash); + + // handle recovery request for guardian 2 + uint256 executeAfter = block.timestamp + delay; + uint256 executeBefore = block.timestamp + expiry; + emailAuthMsg = getRecoveryEmailAuthMessage(safeAddress, owner, newOwner, guardians1[1]); + emailRecoveryModule.handleRecovery(emailAuthMsg, templateIdx); + recoveryRequest = emailRecoveryModule.getRecoveryRequest(safeAddress); + assertEq(recoveryRequest.executeAfter, executeAfter); + assertEq(recoveryRequest.executeBefore, executeBefore); + assertEq(recoveryRequest.currentWeight, 3); + assertEq(recoveryRequest.recoveryDataHash, recoveryDataHash); + + vm.warp(block.timestamp + delay); + + // Complete recovery + emailRecoveryModule.completeRecovery(safeAddress, recoveryData); + + recoveryRequest = emailRecoveryModule.getRecoveryRequest(safeAddress); + assertEq(recoveryRequest.executeAfter, 0); + assertEq(recoveryRequest.executeBefore, 0); + assertEq(recoveryRequest.currentWeight, 0); + assertEq(recoveryRequest.recoveryDataHash, bytes32(0)); + + vm.prank(safeAddress); + bool isOwner = Safe(payable(safeAddress)).isOwner(newOwner); + assertTrue(isOwner); + + bool oldOwnerIsOwner = Safe(payable(safeAddress)).isOwner(owner); + assertFalse(oldOwnerIsOwner); + } +} diff --git a/test/unit/EmailRecoveryManager/configureRecovery.t.sol b/test/unit/EmailRecoveryManager/configureRecovery.t.sol index 0b16dc1..da8eb1e 100644 --- a/test/unit/EmailRecoveryManager/configureRecovery.t.sol +++ b/test/unit/EmailRecoveryManager/configureRecovery.t.sol @@ -24,17 +24,13 @@ contract EmailRecoveryManager_configureRecovery_Test is UnitBase { vm.expectRevert(IEmailRecoveryManager.SetupAlreadyCalled.selector); vm.startPrank(accountAddress); - emailRecoveryModule.exposed_configureRecovery( - guardians, guardianWeights, threshold, delay, expiry - ); + emailRecoveryModule.configureRecovery(guardians, guardianWeights, threshold, delay, expiry); } function test_ConfigureRecovery_RevertWhen_ConfigureRecoveryCalledTwice() public { vm.startPrank(accountAddress); vm.expectRevert(IEmailRecoveryManager.SetupAlreadyCalled.selector); - emailRecoveryModule.exposed_configureRecovery( - guardians, guardianWeights, threshold, delay, expiry - ); + emailRecoveryModule.configureRecovery(guardians, guardianWeights, threshold, delay, expiry); } function test_ConfigureRecovery_Succeeds() public { @@ -49,9 +45,7 @@ contract EmailRecoveryManager_configureRecovery_Test is UnitBase { emit IEmailRecoveryManager.RecoveryConfigured( instance.account, guardians.length, totalWeight, threshold ); - emailRecoveryModule.exposed_configureRecovery( - guardians, guardianWeights, threshold, delay, expiry - ); + emailRecoveryModule.configureRecovery(guardians, guardianWeights, threshold, delay, expiry); IEmailRecoveryManager.RecoveryConfig memory recoveryConfig = emailRecoveryModule.getRecoveryConfig(accountAddress); @@ -87,7 +81,7 @@ contract EmailRecoveryManager_configureRecovery_Test is UnitBase { guardianWeights.length ) ); - emailRecoveryModule.exposed_configureRecovery( + emailRecoveryModule.configureRecovery( zeroGuardians, guardianWeights, threshold, delay, expiry ); } @@ -105,7 +99,7 @@ contract EmailRecoveryManager_configureRecovery_Test is UnitBase { zeroGuardianWeights.length ) ); - emailRecoveryModule.exposed_configureRecovery( + emailRecoveryModule.configureRecovery( guardians, zeroGuardianWeights, threshold, delay, expiry ); } @@ -117,7 +111,7 @@ contract EmailRecoveryManager_configureRecovery_Test is UnitBase { uint256 zeroThreshold = 0; vm.expectRevert(IGuardianManager.ThresholdCannotBeZero.selector); - emailRecoveryModule.exposed_configureRecovery( + emailRecoveryModule.configureRecovery( guardians, guardianWeights, zeroThreshold, delay, expiry ); } @@ -135,7 +129,7 @@ contract EmailRecoveryManager_configureRecovery_Test is UnitBase { IGuardianManager.ThresholdExceedsTotalWeight.selector, threshold, 0 ) ); - emailRecoveryModule.exposed_configureRecovery( + emailRecoveryModule.configureRecovery( zeroGuardians, zeroGuardianWeights, threshold, delay, expiry ); } diff --git a/test/unit/UniversalEmailRecoveryModuleHarness.sol b/test/unit/UniversalEmailRecoveryModuleHarness.sol index c2694f8..0bd4ea4 100644 --- a/test/unit/UniversalEmailRecoveryModuleHarness.sol +++ b/test/unit/UniversalEmailRecoveryModuleHarness.sol @@ -18,18 +18,6 @@ contract UniversalEmailRecoveryModuleHarness is UniversalEmailRecoveryModule { UniversalEmailRecoveryModule(verifier, dkimRegistry, emailAuthImpl, subjectHandler) { } - function exposed_configureRecovery( - address[] memory guardians, - uint256[] memory weights, - uint256 threshold, - uint256 delay, - uint256 expiry - ) - external - { - configureRecovery(guardians, weights, threshold, delay, expiry); - } - function exposed_acceptGuardian( address guardian, uint256 templateIdx, diff --git a/test/unit/modules/UniversalEmailRecoveryModule/onUninstall.t.sol b/test/unit/modules/UniversalEmailRecoveryModule/onUninstall.t.sol index b9611f3..ad8b762 100644 --- a/test/unit/modules/UniversalEmailRecoveryModule/onUninstall.t.sol +++ b/test/unit/modules/UniversalEmailRecoveryModule/onUninstall.t.sol @@ -15,13 +15,6 @@ contract UniversalEmailRecoveryModule_onUninstall_Test is UnitBase { super.setUp(); } - // TODO: Assess - function test_OnUninstall_RevertWhen_InvalidValidatorsLength() public { - vm.prank(accountAddress); - instance.uninstallModule(MODULE_TYPE_EXECUTOR, recoveryModuleAddress, ""); - vm.stopPrank(); - } - function test_OnUninstall_Succeeds() public { vm.prank(accountAddress); instance.uninstallModule(MODULE_TYPE_EXECUTOR, recoveryModuleAddress, "");