Prevent Null Content Hash in Receipts

prevent-null-content-hash-in-receipts · receipt-attestation · 1417 B runtime · registry-info

scores

source

pragma solidity ^0.8.13;

contract GracePeriodReceipt {
    enum Status { Pending, Disputed, Finalized }

    struct Receipt {
        address sender;
        address recipient;
        bytes32 contentHash;
        uint256 deadline;
        Status status;
    }

    error InvalidRecipient();
    error InvalidContentHash();
    error Nonexistent();
    error NotRecipient();
    error NotPending();
    error TooLate();
    error NotExpired();

    uint256 public nextId;
    mapping(uint256 => Receipt) public receipts;

    event Submission(uint256 indexed id, address indexed sender, address indexed recipient, bytes32 contentHash, uint256 deadline);
    event Disputed(uint256 indexed id, address indexed recipient);
    event Finalized(uint256 indexed id, address indexed finalizer);

    function submit(address recipient, bytes32 contentHash) external returns (uint256 id) {
        if (recipient == address(0)) revert InvalidRecipient();
        if (contentHash == bytes32(0)) revert InvalidContentHash();
        id = nextId++;
        uint256 deadline = block.timestamp + 24 hours;
        receipts[id] = Receipt({
            sender: msg.sender,
            recipient: recipient,
            contentHash: contentHash,
            deadline: deadline,
            status: Status.Pending
        });
        emit Submission(id, msg.sender, recipient, contentHash, deadline);
    }

    function dispute(uint256 id) external {
        Receipt storage r = receipts[id];
        if (r.sender == address(0)) revert Nonexistent();
        if (msg.sender != r.recipient) revert NotRecipient();
        if (r.status != Status.Pending) revert NotPending();
        if (block.timestamp > r.deadline) revert TooLate();
        r.status = Status.Disputed;
        emit Disputed(id, msg.sender);
    }

    function finalize(uint256 id) external {
        Receipt storage r = receipts[id];
        if (r.sender == address(0)) revert Nonexistent();
        if (r.status != Status.Pending) revert NotPending();
        if (block.timestamp <= r.deadline) revert NotExpired();
        r.status = Status.Finalized;
        emit Finalized(id, msg.sender);
    }
}