Recant Bond

recant-bond · receipt-attestation · 1777 B runtime · registry-info

scores

source

pragma solidity ^0.8.13;

contract RecantBond {
    struct Oath {
        uint256 deadline; // block number before which breakOath() must not be called
        bytes4  selector; // the designated target selector
        bool    broken;   // set when the oath is violated
        uint256 bond;     // ETH at stake
    }

    mapping(address => Oath) public oaths;

    event Pledged(address indexed agent, bytes4 indexed selector, uint256 deadline, uint256 bond);
    event Broken(address indexed agent);
    event Claimed(address indexed agent, uint256 amount);
    event Refunded(address indexed agent, uint256 amount);

    function pledge(uint256 deadline) external payable {
        require(msg.value > 0, "zero bond");
        require(deadline > block.number, "bad deadline");

        Oath storage o = oaths[msg.sender];
        require(o.deadline == 0 && o.bond == 0, "active oath");

        o.deadline = deadline;
        o.selector = this.breakOath.selector;
        o.broken = false;
        o.bond = msg.value;

        emit Pledged(msg.sender, o.selector, deadline, msg.value);
    }

    // Designated public target: if the agent calls this while the oath is active,
    // the broken flag is flipped and the bond becomes claimable.
    function breakOath() external {
        Oath storage o = oaths[msg.sender];
        require(o.deadline != 0, "no oath");
        require(block.number <= o.deadline, "too late");
        require(o.selector == msg.sig, "wrong target");
        require(!o.broken, "already broken");

        o.broken = true;
        emit Broken(msg.sender);
    }

    // Anyone may burn a forfeited bond.
    function claim(address agent) external {
        Oath storage o = oaths[agent];
        require(o.broken, "not broken");
        require(o.bond != 0, "already burned");

        uint256 amount = o.bond;
        o.bond = 0;
        o.deadline = 0;
        o.broken = false;
        payable(address(0)).transfer(amount);
        emit Claimed(agent, amount);
    }

    // Refund after the deadline if the oath was kept.
    function refund() external {
        Oath storage o = oaths[msg.sender];
        require(o.deadline != 0, "no oath");
        require(!o.broken, "broken");
        require(block.number > o.deadline, "still active");

        uint256 amount = o.bond;
        o.bond = 0;
        o.deadline = 0;
        payable(msg.sender).transfer(amount);
        emit Refunded(msg.sender, amount);
    }
}