Made a “dead simple” (pun intended) dead man switch signer that let’s you easily configure your safe to fallback from being N-of-M to {N-1}-of-M with a delay.
To use it you just deploy the contract giving it the address of your safe + the intended delay and then add the contract as a signer to your multisig. Practically it works by letting any member on the multisig double-sign with a delay. If your member signing threshold is more than 2 I could imagine deploying several of these for the same multisig so that the threshold can “decay” if needed e.g. a 4-of-7 that becomes a 3/7 after 15 days, 2/7 after 30 days and 1/7 after 60 days.
To prevent misuse you’ll need to actively monitor it so that you can remove the DeadManSigner as a signer from your multisig in time if it was triggered maliciously.
Using this for my personal setup and thought I’d share!
// SPDX-License-Identifier: MIT
pragma solidity =0.8.36;
interface ISafe {
function isOwner(address owner) external view returns (bool);
function approveHash(bytes32 hashToApprove) external;
}
contract DeadManSigner {
ISafe internal immutable safe;
uint256 internal immutable delay;
bytes32 public safeTxHash;
uint256 public validAfter;
error InvalidConfig();
error AlreadyTriggered();
error NotSafeOwner();
error ManNotDead();
constructor(ISafe safe_, uint256 delay_) {
require(address(safe_) != address(0) && delay_ != 0, InvalidConfig());
safe = safe_;
delay = delay_;
}
function params() public view returns (ISafe, uint256) {
return (safe, delay);
}
function queue(bytes32 hash) external {
require(validAfter == 0 && safeTxHash == bytes32(0), AlreadyTriggered());
require(safe.isOwner(msg.sender), NotSafeOwner());
safeTxHash = hash;
validAfter = block.timestamp + delay;
}
function pushSignature() external {
require(validAfter != 0 && block.timestamp >= validAfter, ManNotDead());
safe.approveHash(safeTxHash);
safeTxHash = bytes32(0);
validAfter = 0;
}
}