Immediate Client Cancellation Before Completion

immediate-client-cancellation-before-completion · conditional-settlement · 3422 B runtime · funds-movement

scores

source

pragma solidity ^0.8.13;

contract ArbitratedTaskEscrow {
    enum State { Created, Completed, Disputed, Resolved }

    struct Task {
        address payable client;
        address payable worker;
        address payable judge;
        uint256 amount;
        uint256 deadline;
        bytes32 taskHash;
        bytes32 submittedHash;
        uint256 challengeEnd;
        State state;
    }

    uint256 public nextTaskId;
    mapping(uint256 => Task) public tasks;

    event TaskCreated(uint256 indexed taskId, address indexed client, address indexed worker, bytes32 resultHash, uint256 deadline);
    event TaskCompleted(uint256 indexed taskId, bytes32 resultHash);
    event TaskDisputed(uint256 indexed taskId);
    event TaskResolved(uint256 indexed taskId, bool payWorker);
    event TaskCancelled(uint256 indexed taskId);

    function createTask(address payable worker, address payable judge, bytes32 resultHash, uint256 deadline) external payable returns (uint256 taskId) {
        require(msg.value > 0, "Amt=0");
        require(worker != address(0) && judge != address(0), "Addr=0");
        require(deadline > block.timestamp, "Past deadline");
        taskId = ++nextTaskId;
        tasks[taskId] = Task({
            client: payable(msg.sender),
            worker: worker,
            judge: judge,
            amount: msg.value,
            deadline: deadline,
            taskHash: resultHash,
            submittedHash: 0,
            challengeEnd: 0,
            state: State.Created
        });
        emit TaskCreated(taskId, msg.sender, worker, resultHash, deadline);
    }

    function complete(uint256 taskId, bytes32 resultHash) external {
        Task storage t = tasks[taskId];
        require(t.state == State.Created, "Not Created");
        require(msg.sender == t.worker, "Not Worker");
        require(block.timestamp <= t.deadline, "Expired");
        t.submittedHash = resultHash;
        t.state = State.Completed;
        t.challengeEnd = block.timestamp + 86400;
        emit TaskCompleted(taskId, resultHash);
    }

    function dispute(uint256 taskId) external {
        Task storage t = tasks[taskId];
        require(t.state == State.Completed, "Not Completed");
        require(msg.sender == t.client, "Not Client");
        require(block.timestamp <= t.challengeEnd, "Challenge ended");
        t.state = State.Disputed;
        emit TaskDisputed(taskId);
    }

    function resolve(uint256 taskId, bool payWorker) external {
        Task storage t = tasks[taskId];
        require(t.state == State.Disputed, "Not Disputed");
        require(msg.sender == t.judge, "Not Judge");
        t.state = State.Resolved;
        uint256 amount = t.amount;
        t.amount = 0;
        address payable payee = payWorker ? t.worker : t.client;
        (bool ok, ) = payee.call{value: amount}("");
        require(ok, "Send fail");
        emit TaskResolved(taskId, payWorker);
    }

    function claim(uint256 taskId) external {
        Task storage t = tasks[taskId];
        require(t.state == State.Completed, "Not Completed");
        require(block.timestamp > t.challengeEnd, "Window still open");
        t.state = State.Resolved;
        uint256 amount = t.amount;
        t.amount = 0;
        (bool ok, ) = t.worker.call{value: amount}("");
        require(ok, "Send fail");
        emit TaskResolved(taskId, true);
    }

    function cancel(uint256 taskId) external {
        Task storage t = tasks[taskId];
        require(t.state == State.Created, "Not Created");
        require(msg.sender == t.client, "Not Client");
        t.state = State.Resolved;
        uint256 amount = t.amount;
        t.amount = 0;
        (bool ok, ) = t.client.call{value: amount}("");
        require(ok, "Send fail");
        emit TaskCancelled(taskId);
    }
}