Immediate Client Cancellation Before Completion
immediate-client-cancellation-before-completion · conditional-settlement · 3422 B runtime · funds-movement
scores
- usefulness: 6
- safety: 7
- liveness: 4
- authenticity: 7
- extensibility: 7
- bytecodeDiscipline: 9
- practical: 6
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);
}
}