// SPDX-License-Identifier: MIT pragma solidity ^0.8.13; contract PulseEscrow { bytes32 public claimHash; address public payer; uint64 public lastPing; address public executor; uint64 public timeout; event Opened(address indexed payer, address indexed executor, bytes32 claimHash, uint64 timeout); event Ping(address indexed executor, uint64 lastPing); event Claimed(address indexed claimant, uint256 amount); event Refunded(address indexed payer, uint256 amount); function open(bytes32 _claimHash, address _executor, uint64 _timeout) external payable { require(payer == address(0), "already opened"); require(_claimHash != bytes32(0), "zero hash"); require(_timeout > 0, "zero timeout"); require(msg.value > 0, "zero value"); claimHash = _claimHash; payer = msg.sender; executor = _executor; timeout = _timeout; lastPing = uint64(block.timestamp); emit Opened(msg.sender, _executor, _claimHash, _timeout); } function ping() external { require(msg.sender == executor, "not executor"); require(claimHash != bytes32(0), "closed"); lastPing = uint64(block.timestamp); emit Ping(msg.sender, lastPing); } function claim(bytes32 preimage) external { require(claimHash != bytes32(0), "closed"); require(keccak256(abi.encodePacked(preimage)) == claimHash, "bad preimage"); claimHash = bytes32(0); uint256 amount = address(this).balance; (bool ok, ) = payable(msg.sender).call{value: amount}(""); require(ok, "send failed"); emit Claimed(msg.sender, amount); } function refund() external { require(msg.sender == payer, "not payer"); require(block.timestamp >= uint256(lastPing) + uint256(timeout), "too early"); require(claimHash != bytes32(0), "closed"); claimHash = bytes32(0); uint256 amount = address(this).balance; (bool ok, ) = payable(msg.sender).call{value: amount}(""); require(ok, "send failed"); emit Refunded(msg.sender, amount); } }