Revert-Catching Receipt Stamp

revert-catching-receipt-stamp · receipt-attestation · 1924 B runtime · funds-movement

scores

source

pragma solidity ^0.8.13;

contract ReceiptStamp {
    address public owner;
    uint256 public immutable failureFee;
    bool private locked;

    mapping(address => bool) public registeredPredicates;
    mapping(address => mapping(address => mapping(bytes32 => bytes32))) public receipts;

    event ReceiptStamped(
        address indexed stamper,
        address indexed predicate,
        bytes32 indexed taskId,
        bytes32 receiptHash,
        bool success
    );

    modifier onlyOwner() {
        require(msg.sender == owner, "not owner");
        _;
    }

    modifier noReentrant() {
        require(!locked, "reentrant");
        locked = true;
        _;
        locked = false;
    }

    constructor(uint256 _failureFee) {
        owner = msg.sender;
        failureFee = _failureFee;
    }

    function registerPredicate(address predicate) external onlyOwner {
        require(predicate.code.length > 0, "not contract");
        registeredPredicates[predicate] = true;
    }

    function unregisterPredicate(address predicate) external onlyOwner {
        registeredPredicates[predicate] = false;
    }

    function stamp(
        address predicate,
        bytes calldata data,
        bytes32 taskId
    ) external payable noReentrant returns (bytes32 receiptHash) {
        require(registeredPredicates[predicate], "unregistered");
        require(receipts[msg.sender][predicate][taskId] == bytes32(0), "exists");
        require(msg.value == failureFee, "fee");

        (bool success, ) = predicate.call(data);

        receiptHash = keccak256(abi.encodePacked(msg.sender, predicate, taskId, success, keccak256(data)));
        receipts[msg.sender][predicate][taskId] = receiptHash;

        emit ReceiptStamped(msg.sender, predicate, taskId, receiptHash, success);

        if (success) {
            (bool sent, ) = msg.sender.call{value: msg.value}("");
            require(sent, "refund failed");
        }
    }
}