Appearance
L1-7: Dead Man's Switch
1. Problem
Create a contract where users deposit ETH and set up a periodic check-in mechanism. If a user fails to check in within the configured time interval, a designated beneficiary can withdraw their funds. The user themselves can always withdraw.
2. Why
Real-world scenario: Cryptocurrency holders may lose access to their assets due to accidents (death, disappearance, lost private keys). A Dead Man's Switch provides a "digital inheritance" mechanism:
- The operator periodically checks in to prove they are "still active"
- Upon loss of contact, funds automatically transfer to pre-designated beneficiaries
- No need to trust a third-party custodian
3. Solution
Core Mechanism
- Each user sets their own
checkInInterval deposit()/checkIn()updateslastCheckIn[user]- The account holder can always withdraw
- Beneficiaries can withdraw only when
block.timestamp > lastCheckIn[account] + checkInInterval[account]
Data Structures
solidity
mapping(address => uint256) public balanceOf;
mapping(address => uint256) public lastCheckIn;
mapping(address => uint256) public checkInInterval;
mapping(address => address[]) public beneficiaries;
4. Pitfalls Encountered
4.1 Beneficiaries do not need to set an interval
If a user does not set checkInInterval, then deadline = lastCheckIn + 0 = lastCheckIn, which means block.timestamp > deadline is always true — beneficiaries can withdraw immediately.
4.2 Auto-check-in on deposit
deposit() should also update lastCheckIn; otherwise, a new deposit could immediately be considered "inactive."
4.3 Beneficiary list management
removeBeneficiary needs to delete an element from the array. Use the swap-and-pop pattern for O(1).
5. Why the Pitfalls Exist
5.1
checkInInterval defaults to 0. lastCheckIn + 0 = lastCheckIn, so any block.timestamp > lastCheckIn satisfies the condition. An explicit check for interval == 0 is required.
5.2
If depositing does not update lastCheckIn, a user who just deposited a large amount of ETH could have it withdrawn by a beneficiary (because the previous interval may have already elapsed).
5.3
Solidity arrays have no built-in delete method. Using list[i] = list[last]; list.pop() is the optimal solution.
6. How to Solve the Pitfalls
solidity
function withdraw(address account, uint256 amount) external {
if (!isAccountHolder) {
uint256 interval = checkInInterval[account];
if (interval == 0) revert NotExpired(); // Must be explicitly set
if (block.timestamp <= lastCheckIn[account] + interval) revert NotExpired();
}
// ...
}
7. Technical Takeaways
| Point | Description |
|---|---|
| Time-conditional access control | block.timestamp comparison |
| Beneficiary pattern | Multiple beneficiaries share withdrawal permission |
| Check-in mechanism | Auto-renewal on every interaction |
| Array swap-and-pop | O(1) deletion |
| receive() deposits | Direct ETH transfers handled automatically |