Predicate-Locked Escrow with Timeout Refund

predicate-locked-escrow-with-timeout-refund · conditional-settlement · 1605 B runtime · funds-movement

scores

source

pragma solidity ^0.8.13;

contract PredicateEscrow {
    struct Escrow {
        address payable opener;
        uint256 createdAt;
        uint256 timeout;
        uint256 amount;
    }

    mapping(bytes32 => Escrow) private escrows;

    function open(bytes32 id, uint256 timeout) external payable {
        require(escrows[id].opener == address(0), "exists");
        escrows[id] = Escrow(payable(msg.sender), block.timestamp, timeout, msg.value);
    }

    function claim(bytes32 id, address predicate, bytes calldata data) external {
        Escrow storage esc = escrows[id];
        require(esc.opener != address(0), "not found");
        require(block.timestamp <= esc.createdAt + esc.timeout, "expired");

        (bool ok, bytes memory ret) = predicate.staticcall(data);
        require(ok && ret.length >= 32 && abi.decode(ret, (bool)), "predicate false");

        uint256 amount = esc.amount;
        delete escrows[id];
        (bool sent,) = msg.sender.call{value: amount}("");
        require(sent, "transfer failed");
    }

    function refund(bytes32 id) external {
        Escrow storage esc = escrows[id];
        require(esc.opener == msg.sender, "not opener");
        require(block.timestamp > esc.createdAt + esc.timeout, "too early");

        uint256 amount = esc.amount;
        delete escrows[id];
        (bool sent,) = msg.sender.call{value: amount}("");
        require(sent, "transfer failed");
    }
}