// SPDX-License-Identifier: MIT pragma solidity 0.8.30; /// @notice One fixed-price native-ETH agreement. No admin, upgrade, or platform fee. /// The agreed arbiter is trusted to allocate the deposit after a dispute. /// A missing arbiter can delay disputed funds indefinitely; both parties may settle together. contract PostPactEscrow { enum Phase { Funded, Submitted, Disputed, Settled } address public payer; address public payee; address public arbiter; bytes32 public termsHash; uint256 public amount; uint256 public deadline; uint256 public liveDuration; Phase public phase; bytes32 public deliveryHash; uint256 public releaseAfter; uint256 public claimAfter; uint256 public payerCredit; uint256 public payeeCredit; uint256 public payeeAward; uint256 public proposalNonce; address public proposer; uint256 public proposedPayeeAmount; bool private entered; uint256 public constant REVIEW_PERIOD = 3 days; event Funded(bytes32 indexed termsHash, address indexed payer, address indexed payee, uint256 amount); event Delivered(bytes32 indexed deliveryHash, uint256 releaseAfter, uint256 claimAfter); event Disputed(address indexed by); event SettlementProposed(uint256 indexed nonce, address indexed by, uint256 payeeAmount); event Settled(uint256 payeeAmount, uint256 payerAmount); event Withdrawn(address indexed account, address indexed recipient, uint256 value); constructor(address creator, address resolver, bytes32 digest, uint256 due, uint256 duration) payable { require(msg.value > 0 && msg.value <= 100 ether, "Invalid deposit"); require(creator != address(0) && creator != msg.sender, "Invalid creator"); require(resolver != address(0) && resolver != msg.sender && resolver != creator, "Invalid arbiter"); require(digest != bytes32(0) && due > block.timestamp && due <= block.timestamp + 365 days, "Invalid terms"); require(duration >= 1 days && duration <= 90 days, "Invalid duration"); payer = msg.sender; payee = creator; arbiter = resolver; termsHash = digest; amount = msg.value; deadline = due; liveDuration = duration; emit Funded(digest, payer, creator, msg.value); } modifier participant() { require(msg.sender == payer || msg.sender == payee, "Not a participant"); _; } function submit(bytes32 digest) external { require(msg.sender == payee && phase == Phase.Funded, "Cannot submit"); require(block.timestamp <= deadline && digest != bytes32(0), "Delivery expired"); deliveryHash = digest; releaseAfter = block.timestamp + liveDuration; claimAfter = releaseAfter + REVIEW_PERIOD; phase = Phase.Submitted; emit Delivered(digest, releaseAfter, claimAfter); } function approve() external { require(msg.sender == payer && phase == Phase.Submitted && block.timestamp >= releaseAfter, "Cannot approve yet"); settle(amount); } function claim() external { require(msg.sender == payee && phase == Phase.Submitted && block.timestamp >= claimAfter, "Review still open"); settle(amount); } function refund() external { require(msg.sender == payer && phase == Phase.Funded && block.timestamp > deadline, "Cannot refund"); settle(0); } function dispute() external participant { require(phase == Phase.Funded || (phase == Phase.Submitted && block.timestamp < claimAfter), "Cannot dispute"); phase = Phase.Disputed; emit Disputed(msg.sender); } function resolve(uint256 creatorAmount) external { require(msg.sender == arbiter && phase == Phase.Disputed, "Not the arbiter"); settle(creatorAmount); } function proposeSettlement(uint256 creatorAmount) external participant { require(phase != Phase.Settled && creatorAmount <= amount, "Invalid settlement"); proposer = msg.sender; proposedPayeeAmount = creatorAmount; proposalNonce++; emit SettlementProposed(proposalNonce, msg.sender, creatorAmount); } function acceptSettlement(uint256 nonce, uint256 creatorAmount) external participant { require(phase != Phase.Settled && proposer != address(0) && proposer != msg.sender, "Cannot accept"); require(nonce == proposalNonce && creatorAmount == proposedPayeeAmount, "Proposal changed"); settle(creatorAmount); } function settle(uint256 creatorAmount) private { require(creatorAmount <= amount && phase != Phase.Settled, "Invalid settlement"); phase = Phase.Settled; payeeAward = creatorAmount; payeeCredit = creatorAmount; payerCredit = amount - creatorAmount; emit Settled(creatorAmount, amount - creatorAmount); } function withdraw(address payable recipient) external participant { require(!entered && recipient != address(0), "Invalid withdrawal"); entered = true; uint256 value; if (msg.sender == payer) { value = payerCredit; payerCredit = 0; } else { value = payeeCredit; payeeCredit = 0; } require(value > 0, "Nothing to withdraw"); (bool ok,) = recipient.call{value:value}(""); require(ok, "Transfer failed"); entered = false; emit Withdrawn(msg.sender, recipient, value); } }