Simple Dead Man Switch Recovery Signer

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;
    }
}
4 Likes