DeadmanSwitch Technical Documentation
Overview
DeadmanSwitch is a Solidity smart contract module designed for account recovery in cases of user inactivity. It implements a safety mechanism allowing a designated nominee to recover access to an account after a specified period of inactivity.
License
AGPL-3.0-only
Solidity Version
^0.8.25
Dependencies
- modulekit/Modules.sol:
ERC7579ValidatorBase,ERC7579HookBase - modulekit/ModuleKit.sol:
PackedUserOperation - solady/utils/SignatureCheckerLib.sol
- solady/utils/ECDSA.sol
Key Components
Constants & Storage
DeadmanSwitchStorage Struct
struct DeadmanSwitchStorage {
uint48 lastAccess; // Timestamp of the last account activity
uint48 timeout; // Duration after which nominee can take control
address nominee; // Address authorized to recover the account
}
State Variables
mapping(address account => DeadmanSwitchStorage) public config: Maps accounts to their DeadmanSwitch configuration
Events
ModuleInitialized(address indexed account, address nominee, uint48 timeout)ModuleUninitialized(address indexed account)NomineeSet(address indexed account, address nominee)TimeoutSet(address indexed account, uint48 timeout)
Errors
UnsupportedOperation(): Thrown when attempting unsupported operationsAlreadyInitialized(address): Thrown when trying to initialize an already initialized accountNotInitialized(address): Thrown when performing operations on an uninitialized account
Core Functionality
Module Management
onInstall
function onInstall(bytes calldata data) external
Initializes the DeadmanSwitch for an account.
- Data format:
abi.encodePacked(nominee, timeout) - Nominee: first 20 bytes (address)
- Timeout: next 6 bytes (uint48)
- Emits
ModuleInitializedevent
onUninstall
function onUninstall(bytes calldata) external
Removes the DeadmanSwitch configuration.
- Clears all stored data
- Emits
ModuleUninitializedevent
isInitialized
function isInitialized(address smartAccount) public view returns (bool)
Checks if an account has initialized the module.
- Returns
trueif nominee is set (non-zero address)
Configuration Functions
setNominee
function setNominee(address nominee) external
Updates the nominee address.
- Requires initialized module
- Emits
NomineeSetevent
setTimeout
function setTimeout(uint48 timeout) external
Updates the timeout duration.
- Requires initialized module
- Emits
TimeoutSetevent
Hook Functions
_preCheck
function _preCheck(
address account,
address,
uint256,
bytes calldata
) internal override returns (bytes memory hookData)
Updates last access time before execution.
- Only updates if module is initialized
- Returns empty string
_postCheck
function _postCheck(address, bytes calldata) internal override
Post-execution hook (currently unused).
Validation Functions
validateUserOp
function validateUserOp(
PackedUserOperation calldata userOp,
bytes32 userOpHash
) external override returns (ValidationData)
Validates user operations for account recovery.
- Checks nominee's signature
- Verifies timeout period
- Returns packed validation data:
- Signature validity
- Valid after timestamp (timeout)
- Valid until (max uint48)
isValidSignatureWithSender
function isValidSignatureWithSender(
address,
bytes32,
bytes calldata
) external pure override returns (bytes4)
ERC-1271 signature validation (unsupported).
- Always reverts with
UnsupportedOperation
Metadata Functions
isModuleType
function isModuleType(uint256 typeID) external pure returns (bool)
Identifies supported module types.
- Returns true for
TYPE_HOOKorTYPE_VALIDATOR
name
function name() external pure virtual returns (string memory)
Returns module name: "DeadmanSwitch"
version
function version() external pure virtual returns (string memory)
Returns module version: "1.0.0"
Technical Specifications
-
Timing Mechanism
- Uses
uint48for timestamps and timeouts - Updates
lastAccesson every valid operation - Recovery window opens after
lastAccess + timeout
- Uses
-
Security Features
- Nominee signature required for recovery
- ERC-1271 signatures explicitly unsupported
- Module can be uninstalled by the account owner
-
Gas Optimization
- Uses
uint48for timestamps to save storage - Implements view functions where possible
- Uses
Integration Guide
To integrate this module:
- Deploy the DeadmanSwitch contract
- Install the module on the target account with:
bytes memory initData = abi.encodePacked(nomineeAddress, timeoutInSeconds);
// Call installation method with initData - Ensure the account's validation flow includes this module
Example Usage
// Installation
bytes memory initData = abi.encodePacked(
address(0x1234...), // nominee
uint48(7 days) // timeout
);
account.installModule(deadmanSwitch, initData);
// Updating nominee
deadmanSwitch.setNominee(newNomineeAddress);
// Updating timeout
deadmanSwitch.setTimeout(uint48(14 days));
Security Considerations
-
Recovery Window
- Nominee can only recover after timeout
- Each account interaction resets the timer
-
Access Control
- Only account owner can change nominee and timeout
- Only nominee can execute recovery
-
Limitations
- No emergency override
- Timeout period is fixed once set
Best Practices
- Choose an appropriate timeout period
- Select a trusted nominee
- Regularly verify nominee and timeout settings
- Consider backup recovery mechanisms