Skip to content

Commit

Permalink
feat: using base module contract to reduce LoC of reused code
Browse files Browse the repository at this point in the history
feat: fixing tests
  • Loading branch information
zeroknots committed Jul 1, 2024
1 parent c0b45cd commit 82e2e50
Show file tree
Hide file tree
Showing 10 changed files with 186 additions and 161 deletions.
100 changes: 37 additions & 63 deletions src/modules/EmailRecoveryModule.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { IERC7579Account } from "erc7579/interfaces/IERC7579Account.sol";
import { IModule } from "erc7579/interfaces/IERC7579Module.sol";
import { IEmailRecoveryModule } from "../interfaces/IEmailRecoveryModule.sol";
import { IEmailRecoveryManager } from "../interfaces/IEmailRecoveryManager.sol";
import { RecoveryModuleBase } from "./RecoveryModuleBase.sol";

/**
* @title EmailRecoveryModule
Expand All @@ -15,19 +16,17 @@ import { IEmailRecoveryManager } from "../interfaces/IEmailRecoveryManager.sol";
* executed on a validator, while the trusted recovery manager defines what a valid
* recovery request is
*/
contract EmailRecoveryModule is ERC7579ExecutorBase, IEmailRecoveryModule {
contract EmailRecoveryModule is RecoveryModuleBase {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CONSTANTS & STORAGE */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

/**
* Trusted email recovery manager contract that handles recovery requests
*/
address public immutable emailRecoveryManager;
address public immutable VALIDATOR_MODULE;

address public immutable validator;

bytes4 public immutable selector;
bytes4 public immutable RECOVERY_SELECTOR;

/**
* Account address to authorized validator
Expand All @@ -36,20 +35,21 @@ contract EmailRecoveryModule is ERC7579ExecutorBase, IEmailRecoveryModule {

event RecoveryExecuted();

error InvalidSelector(bytes4 selector);
error InvalidOnInstallData();
error InvalidValidator(address validator);
error NotTrustedRecoveryManager();
error RecoveryNotAuthorizedForAccount();

constructor(address _emailRecoveryManager, address _validator, bytes4 _selector) {
if (_selector == IModule.onUninstall.selector || _selector == IModule.onInstall.selector) {
revert InvalidSelector(_selector);
}

emailRecoveryManager = _emailRecoveryManager;
validator = _validator;
selector = _selector;
constructor(
address _emailRecoveryManager,
address _validator,
bytes4 _selector
)
RecoveryModuleBase(_emailRecoveryManager)
{
_requireSafeSelectors(_selector);

VALIDATOR_MODULE = _validator;
RECOVERY_SELECTOR = _selector;
}

/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
Expand All @@ -74,21 +74,20 @@ contract EmailRecoveryModule is ERC7579ExecutorBase, IEmailRecoveryModule {
uint256 expiry
) = abi.decode(data, (bytes, address[], uint256[], uint256, uint256, uint256));

if (
!IERC7579Account(msg.sender).isModuleInstalled(
TYPE_VALIDATOR, validator, isInstalledContext
)
) {
revert InvalidValidator(validator);
}
authorized[msg.sender] = true;

_execute({
to: emailRecoveryManager,
value: 0,
data: abi.encodeCall(
IEmailRecoveryManager.configureRecovery, (guardians, weights, threshold, delay, expiry)
)
_requireModuleInstalled({
account: msg.sender,
module: VALIDATOR_MODULE,
context: isInstalledContext
});

_configureRecoveryManager({
guardians: guardians,
weights: weights,
threshold: threshold,
delay: delay,
expiry: expiry
});
}

Expand All @@ -98,17 +97,7 @@ contract EmailRecoveryModule is ERC7579ExecutorBase, IEmailRecoveryModule {
*/
function onUninstall(bytes calldata /* data */ ) external {
authorized[msg.sender] = false;
IEmailRecoveryManager(emailRecoveryManager).deInitRecoveryFromModule(msg.sender);
}

/**
* Check if the module is initialized
* @param smartAccount The smart account to check
* @return true if the module is initialized, false otherwise
*/
function isInitialized(address smartAccount) external view returns (bool) {
return IEmailRecoveryManager(emailRecoveryManager).getGuardianConfig(smartAccount).threshold
!= 0;
EMAIL_RECOVERY_MANAGER.deInitRecoveryFromModule(msg.sender);
}

function isAuthorizedToRecover(address smartAccount) external view returns (bool) {
Expand All @@ -125,33 +114,27 @@ contract EmailRecoveryModule is ERC7579ExecutorBase, IEmailRecoveryModule {
* @param recoveryCalldata The recovery calldata that should be executed on the validator
* being recovered
*/
function recover(address account, bytes calldata recoveryCalldata) external {
if (msg.sender != emailRecoveryManager) {
revert NotTrustedRecoveryManager();
}

function recover(
address account,
bytes calldata recoveryCalldata
)
external
onlyRecoveryManager
{
if (!authorized[account]) {
revert RecoveryNotAuthorizedForAccount();
}

bytes4 calldataSelector = bytes4(recoveryCalldata[:4]);
if (calldataSelector != selector) {
if (calldataSelector != RECOVERY_SELECTOR) {
revert InvalidSelector(calldataSelector);
}

_execute({ account: account, to: validator, value: 0, data: recoveryCalldata });
_execute({ account: account, to: VALIDATOR_MODULE, value: 0, data: recoveryCalldata });

emit RecoveryExecuted();
}

/**
* @notice Returns the address of the trusted recovery manager.
* @return address The address of the email recovery manager.
*/
function getTrustedRecoveryManager() external view returns (address) {
return emailRecoveryManager;
}

/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* METADATA */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
Expand All @@ -171,13 +154,4 @@ contract EmailRecoveryModule is ERC7579ExecutorBase, IEmailRecoveryModule {
function version() external pure returns (string memory) {
return "0.0.1";
}

/**
* Returns the type of the module
* @param typeID type of the module
* @return true if the type is a module type, false otherwise
*/
function isModuleType(uint256 typeID) external pure returns (bool) {
return typeID == TYPE_EXECUTOR;
}
}
97 changes: 97 additions & 0 deletions src/modules/RecoveryModuleBase.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
import { IEmailRecoveryManager } from "../interfaces/IEmailRecoveryManager.sol";
import { ERC7579ExecutorBase } from "@rhinestone/modulekit/src/Modules.sol";
import { IEmailRecoveryModule } from "../interfaces/IEmailRecoveryModule.sol";
import { IModule } from "erc7579/interfaces/IERC7579Module.sol";
import { IERC7579Account } from "erc7579/interfaces/IERC7579Account.sol";

abstract contract RecoveryModuleBase is IEmailRecoveryModule, ERC7579ExecutorBase {
IEmailRecoveryManager internal immutable EMAIL_RECOVERY_MANAGER;

error UnauthorizedOnlyEmailRecoveryManager();

error InvalidSelector(bytes4 selector);
error InvalidValidator(address validator);

constructor(address _emailRecoveryManager) {
EMAIL_RECOVERY_MANAGER = IEmailRecoveryManager(_emailRecoveryManager);
}

/**
* Check if the module is initialized
* @param smartAccount The smart account to check
* @return true if the module is initialized, false otherwise
*/
function isInitialized(address smartAccount) external view returns (bool) {
return EMAIL_RECOVERY_MANAGER.getGuardianConfig(smartAccount).threshold != 0;
}

/**
* @notice Returns the address of the trusted recovery manager.
* @return address The address of the email recovery manager.
*/
function getTrustedRecoveryManager() external view returns (address) {
return address(EMAIL_RECOVERY_MANAGER);
}

/**
* Returns the type of the module
* @param typeID type of the module
* @return true if the type is a module type, false otherwise
*/
function isModuleType(uint256 typeID) external pure returns (bool) {
return typeID == TYPE_EXECUTOR;
}

function _requireSafeSelectors(bytes4 selector) internal pure {
if (selector == IModule.onUninstall.selector || selector == IModule.onInstall.selector) {
revert InvalidSelector(selector);
}
}

/**
* @notice Modifier to check whether the selector is safe. Reverts if the selector is for
* "onInstall" or "onUninstall"
*/
modifier withoutUnsafeSelector(bytes4 recoverySelector) {
_requireSafeSelectors(recoverySelector);
_;
}

modifier onlyRecoveryManager() {
if (msg.sender != address(EMAIL_RECOVERY_MANAGER)) {
revert UnauthorizedOnlyEmailRecoveryManager();
}
_;
}

function _requireModuleInstalled(
address account,
address module,
bytes memory context
)
internal
view
{
if (!IERC7579Account(account).isModuleInstalled(TYPE_VALIDATOR, module, context)) {
revert InvalidValidator(module);
}
}

function _configureRecoveryManager(
address[] memory guardians,
uint256[] memory weights,
uint256 threshold,
uint256 delay,
uint256 expiry
)
internal
{
_execute({
to: address(EMAIL_RECOVERY_MANAGER),
value: 0,
data: abi.encodeCall(
IEmailRecoveryManager.configureRecovery, (guardians, weights, threshold, delay, expiry)
)
});
}
}
Loading

0 comments on commit 82e2e50

Please sign in to comment.