// SPDX-License-Identifier: MIT // Generated by the OSS Singularity contract lab (roadmap stage 06 first slice). // SYNTHETIC EXAMPLE — Synthetic fair settlement example. Placeholder addresses, a digest // over the public synthetic artifact and future deadlines; no funds, no // deployment, no wallet. This contract records settlement states only: it is // not an escrow, never receives or holds anything, and is deliberately // not payable. The lab explains every requested signature; it never requests // one. Nothing signs or deploys merely because this file exists. pragma solidity ^0.8.24; /// @title FairSettlement — a readable fair-settlement state machine. /// @notice Mirrors the accepted settlement design note: the agreement comes /// before the work and before any funds move. The happy path is /// funded → delivered → accepted → released; either party may open a /// dispute (delivered → disputed → resolved → released or refunded), /// and cancellation plus every timeout end in a DEFINED outcome. The /// real budget sits with a SEPARATELY AUTHORIZED holder — an escrow /// instance or named human authority — that reads these records and /// executes transfers in its own audited system. This contract never /// holds, receives, or moves value of any kind. The Commons never /// holds funds and never gains custody. contract FairSettlement { /// The settlement states, named exactly as the design note names them. enum State { none, // agreement recorded, funding not yet declared funded, // the budget sits with the separately authorized holder delivered, // a delivery receipt links the digest to the work accepted, // the coordinator accepted the newest revision (release CONDITION) disputed, // one party named the dispute path resolved, // the holder recorded the dispute resolution released, // terminal: settled to the contributor refunded // terminal: settled back to the coordinator } // ---- Roles from the agreement (synthetic placeholders) ---- address public immutable contributor = 0x0000000000000000000000000000000000000001; address public immutable coordinator = 0x0000000000000000000000000000000000000002; // Placeholder for the separately authorized holder of the real budget. // This contract records what the holder declares; custody itself is a // separate, audited system with its own threat model. address public immutable holder = 0x0000000000000000000000000000000000000003; // ---- Delivery reference: sha256 over the delivered bytes ---- // The manifest digest of the synthetic artifact this example stands for. bytes32 public constant DECLARED_DIGEST = 0x74aebc353c86c6269f80cc8509528ab19b3a1612ac3291bab40f32701454f69b; // ---- Named deadlines: every waiting state has one ---- uint256 public constant REVIEW_DEADLINE = 1830000000; // delivery not reviewed → dispute path, never silent release uint256 public constant DISPUTE_DEADLINE = 1832592000; // dispute unresolved → the agreement's fallback uint256 public constant OUTER_DEADLINE = 1835184000; // from it (inclusive) the refund path has EXCLUSIVE priority // ---- The fallback outcome the agreement fixed for a deadlocked dispute ---- bool public constant DISPUTE_FALLBACK_RELEASES = false; // refund // ---- Settlement state: one path at a time, terminal states are final ---- State public state = State.none; uint16 public currentRevision; // last delivered revision; 0 = none yet uint16 public acceptedRevision; // 0 = nothing accepted yet bool public resolutionReleases; // the holder's recorded dispute decision event FundingRecorded(address indexed who); event DeliveryRecorded(uint16 indexed revision, bytes32 digest, address indexed who); event Accepted(uint16 indexed revision, address indexed who); event DisputeOpened(string reason, address indexed who); event DisputeResolved(bool releases, address indexed who); event Released(address indexed who); event Refunded(string reason, address indexed who); error NotContributor(); error NotCoordinator(); error NotHolder(); error NotParty(address sender); error WrongState(State current); error DuplicateRevision(uint16 revision); error NotNewestRevision(uint16 newest); error DeadlinePassed(uint256 now_, uint256 deadline); error DisputeWindowStillOpen(uint256 now_, uint256 deadline); error OuterDeadlineNotPassed(uint256 now_, uint256 deadline); error OuterDeadlinePassed(uint256 now_, uint256 deadline); modifier onlyContributor() { if (msg.sender != contributor) revert NotContributor(); _; } modifier onlyCoordinator() { if (msg.sender != coordinator) revert NotCoordinator(); _; } modifier onlyHolder() { if (msg.sender != holder) revert NotHolder(); _; } modifier onlyParty() { if (msg.sender != contributor && msg.sender != coordinator) revert NotParty(msg.sender); _; } /// @notice The holder records that the agreed budget sits with the /// separately authorized holder. This declares funding; it never /// receives anything. /// @dev Signature (): no arguments. Reverts for anyone but the holder /// or when funding was already declared. function recordFunding() external onlyHolder { if (state != State.none) revert WrongState(state); state = State.funded; emit FundingRecorded(msg.sender); } /// @notice The contributor records the next immutable delivery revision, /// referencing the declared digest — the same delivery shape as /// the coordination protocol. /// @dev Signature (uint16): the new revision number, one above the /// current. Reverts for anyone but the contributor, for a repeat /// or skip, or when the settlement is not waiting for delivery. function recordDelivery(uint16 revision) external onlyContributor { if (state != State.funded) revert WrongState(state); if (revision != currentRevision + 1) revert DuplicateRevision(revision); currentRevision = revision; state = State.delivered; emit DeliveryRecorded(revision, DECLARED_DIGEST, msg.sender); } /// @notice The coordinator accepts exactly the newest revision before the /// review deadline. Acceptance is the release CONDITION, never the /// release itself. After the deadline the dispute path is the only /// way forward — never a silent release. /// @dev Signature (uint16): the revision being accepted. Reverts for /// non-coordinators, stale revisions, a passed review deadline, or /// when the settlement is not in the delivered state. function accept(uint16 revision) external onlyCoordinator { if (state != State.delivered) revert WrongState(state); if (revision != currentRevision) revert NotNewestRevision(currentRevision); if (block.timestamp > REVIEW_DEADLINE) revert DeadlinePassed(block.timestamp, REVIEW_DEADLINE); acceptedRevision = revision; state = State.accepted; emit Accepted(revision, msg.sender); } /// @notice The holder executes the release its custody rules require once /// acceptance has satisfied the release condition — before the /// outer deadline. From the deadline instant itself on (the /// moment the refund path opens), no new release can succeed: /// refund has priority. The transfer itself happens in the /// holder's separate, audited system; this record only marks /// the outcome. /// @dev Signature (): no arguments. Reverts for anyone but the holder, /// when the settlement has not been accepted, or from the outer /// deadline instant on (OuterDeadlinePassed — the refund window /// and the release window are disjoint). function release() external onlyHolder { if (state != State.accepted) revert WrongState(state); if (block.timestamp >= OUTER_DEADLINE) revert OuterDeadlinePassed(block.timestamp, OUTER_DEADLINE); state = State.released; emit Released(msg.sender); } /// @notice Either party opens the dispute path named in the agreement, /// stating why. The holder then applies the agreed rules; the /// service records outcomes only. /// @dev Signature (string calldata): the reason the dispute was opened. /// Reverts for anyone but the two parties or when nothing is /// awaiting review. function openDispute(string calldata reason) external onlyParty { if (state != State.delivered) revert WrongState(state); state = State.disputed; emit DisputeOpened(reason, msg.sender); } /// @notice The holder records the resolution decided under the agreed /// dispute rules, inside the dispute window. releases=true means /// the work settles as delivered; false means refund. /// @dev Signature (bool): which way the resolution goes. Reverts for /// anyone but the holder, outside the disputed state, or after the /// dispute deadline. function resolveDispute(bool releases) external onlyHolder { if (state != State.disputed) revert WrongState(state); if (block.timestamp > DISPUTE_DEADLINE) revert DeadlinePassed(block.timestamp, DISPUTE_DEADLINE); resolutionReleases = releases; state = State.resolved; emit DisputeResolved(releases, msg.sender); } /// @notice The holder executes the recorded resolution: released or /// refunded, exactly as resolved. A releasing execution is /// refused from the outer deadline instant on — the refund path /// has priority from then on; a refunding execution still /// reaches its (already refund) outcome. The transfer itself /// stays in the holder's custody system. /// @dev Signature (): no arguments. Reverts for anyone but the holder, /// when no resolution has been recorded, or when the resolution /// releases from the outer deadline instant on. function executeResolution() external onlyHolder { if (state != State.resolved) revert WrongState(state); if (resolutionReleases && block.timestamp >= OUTER_DEADLINE) revert OuterDeadlinePassed(block.timestamp, OUTER_DEADLINE); if (resolutionReleases) { state = State.released; emit Released(msg.sender); } else { state = State.refunded; emit Refunded("dispute resolution", msg.sender); } } // ---- Timeouts: every waiting state names a deadline and a default ---- // A timeout produces a DEFINED outcome; it never mints authority. The two // paths below are therefore deliberately permissionless: they can only // ever reach the outcome the agreement already fixed. From the outer // deadline instant (inclusive) the refund path has EXCLUSIVE priority: // every release-minting transition is refused exactly when the refund // becomes available, so a release can never race the refund for the // same state. /// @notice Past the dispute window the agreed fallback replaces a /// silent holder: the settlement takes the outcome fixed in the /// agreement (refund) without needing anyone's cooperation — /// unless that outcome is a release and the outer deadline has /// been reached: the refund priority outranks even the agreed /// fallback. /// @dev Signature (): no arguments. Reverts while the settlement is not /// disputed, while the dispute window is still open, or when the /// fallback releases from the outer deadline instant on. function applyDisputeFallback() external { if (state != State.disputed) revert WrongState(state); if (block.timestamp <= DISPUTE_DEADLINE) revert DisputeWindowStillOpen(block.timestamp, DISPUTE_DEADLINE); if (DISPUTE_FALLBACK_RELEASES && block.timestamp >= OUTER_DEADLINE) revert OuterDeadlinePassed(block.timestamp, OUTER_DEADLINE); if (DISPUTE_FALLBACK_RELEASES) { state = State.released; emit Released(msg.sender); } else { state = State.refunded; emit Refunded("dispute fallback", msg.sender); } } /// @notice From the outer deadline instant on (inclusive), funds /// unreleased take the refund path from every non-terminal /// state: the budget flows back rather than sitting withheld /// indefinitely. From that moment on this is the only outcome — /// every release path is closed. /// @dev Signature (): no arguments. Reverts before the outer deadline /// and on terminal states (nothing left to refund). function refundAfterOuterDeadline() external { if (state == State.none || state == State.released || state == State.refunded) revert WrongState(state); if (block.timestamp < OUTER_DEADLINE) revert OuterDeadlineNotPassed(block.timestamp, OUTER_DEADLINE); state = State.refunded; emit Refunded("outer deadline", msg.sender); } /// @notice Cancellation before delivery: the agreement named cancellation, /// and the holder records the budget flowing back unused. /// @dev Signature (): no arguments. Reverts for anyone but the holder /// or when delivery has already begun. function refundOnCancellation() external onlyHolder { if (state != State.funded) revert WrongState(state); state = State.refunded; emit Refunded("cancelled before delivery", msg.sender); } }