Two-Party Handshake Receipt

two-party-handshake-receipt · receipt-attestation · 2386 B runtime · funds-movement

scores

source

pragma solidity ^0.8.13;

contract TwoPartyHandshake {
    struct Receipt {
        address sender;
        address recipient;
        uint256 deadline;
        uint256 senderDeposit;
        bool completed;
    }

    mapping(bytes32 => Receipt) public receipts;

    event Initiated(bytes32 indexed receiptHash, address indexed sender, address indexed recipient, uint256 deadline);
    event Echoed(bytes32 indexed receiptHash, address indexed sender, address indexed recipient);
    event Withdrawn(bytes32 indexed receiptHash, address indexed sender);

    function initiate(
        bytes32 receiptHash,
        address recipient,
        uint256 timeoutBlocks
    ) external payable returns (uint256 deadline) {
        require(msg.value > 0, 'deposit required');
        require(recipient != address(0), 'recipient required');
        require(timeoutBlocks > 0, 'timeout required');
        require(receipts[receiptHash].sender == address(0), 'receipt exists');

        deadline = block.number + timeoutBlocks;
        receipts[receiptHash] = Receipt({
            sender: msg.sender,
            recipient: recipient,
            deadline: deadline,
            senderDeposit: msg.value,
            completed: false
        });

        emit Initiated(receiptHash, msg.sender, recipient, deadline);
    }

    function echo(bytes32 receiptHash) external payable {
        Receipt storage r = receipts[receiptHash];
        require(r.sender != address(0), 'receipt missing');
        require(!r.completed, 'receipt completed');
        require(msg.sender == r.recipient, 'not recipient');
        require(block.number <= r.deadline, 'timeout passed');
        require(msg.value == r.senderDeposit, 'deposit mismatch');

        r.completed = true;

        address sender = r.sender;
        address recipient = r.recipient;
        uint256 amount = msg.value;

        (bool s, ) = payable(sender).call{value: amount}('');
        require(s, 'sender refund failed');
        (bool t, ) = payable(recipient).call{value: amount}('');
        require(t, 'recipient refund failed');

        emit Echoed(receiptHash, sender, recipient);
    }

    function withdraw(bytes32 receiptHash) external {
        Receipt storage r = receipts[receiptHash];
        require(r.sender != address(0), 'receipt missing');
        require(!r.completed, 'receipt completed');
        require(msg.sender == r.sender, 'not sender');
        require(block.number > r.deadline, 'timeout pending');

        uint256 amount = r.senderDeposit;
        address sender = r.sender;
        delete receipts[receiptHash];

        (bool s, ) = payable(sender).call{value: amount}('');
        require(s, 'refund failed');

        emit Withdrawn(receiptHash, sender);
    }
}