Revert-Catching Receipt Stamp
revert-catching-receipt-stamp · receipt-attestation · 1924 B runtime · funds-movement
scores
- usefulness: 72
- safety: 30
- liveness: 70
- authenticity: 45
- extensibility: 55
- bytecodeDiscipline: 85
- practical: 61
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");
}
}
}