mirror of
https://github.com/arkorty/B.Tech-Project-III.git
synced 2026-04-19 12:41:48 +00:00
init
This commit is contained in:
12
dmtp/server/contracts/MockERC20.sol
Normal file
12
dmtp/server/contracts/MockERC20.sol
Normal file
@@ -0,0 +1,12 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8.20;
|
||||
|
||||
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
|
||||
|
||||
contract MockERC20 is ERC20 {
|
||||
constructor(string memory name, string memory symbol) ERC20(name, symbol) {}
|
||||
|
||||
function mint(address to, uint256 amount) external {
|
||||
_mint(to, amount);
|
||||
}
|
||||
}
|
||||
288
dmtp/server/contracts/TaskEscrow.sol
Normal file
288
dmtp/server/contracts/TaskEscrow.sol
Normal file
@@ -0,0 +1,288 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8.20;
|
||||
|
||||
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
|
||||
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
|
||||
import "@openzeppelin/contracts/access/Ownable.sol";
|
||||
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
|
||||
import "@openzeppelin/contracts/utils/Pausable.sol";
|
||||
|
||||
/**
|
||||
* @title TaskEscrow
|
||||
* @dev Escrow smart contract for AI-powered micro-task marketplace on CELO Sepolia Testnet.
|
||||
* @notice This contract securely holds cUSD payments for tasks, releasing to workers upon AI-verification approval.
|
||||
*/
|
||||
contract TaskEscrow is Ownable, ReentrancyGuard, Pausable {
|
||||
using SafeERC20 for IERC20;
|
||||
|
||||
// ============ Constants & Parameters ============
|
||||
IERC20 public immutable cUSD;
|
||||
|
||||
uint256 public constant PLATFORM_FEE_BPS = 500; // 5%
|
||||
uint256 public constant BPS_DENOMINATOR = 10000;
|
||||
|
||||
uint256 public platformFeesAccumulated;
|
||||
uint256 public taskCounter;
|
||||
|
||||
// ============ Enums ============
|
||||
enum TaskStatus {
|
||||
Open,
|
||||
InProgress,
|
||||
Completed,
|
||||
Cancelled,
|
||||
Expired
|
||||
}
|
||||
|
||||
// ============ Struct ============
|
||||
struct Task {
|
||||
uint256 taskId;
|
||||
address requester;
|
||||
address worker;
|
||||
uint256 paymentAmount;
|
||||
TaskStatus status;
|
||||
uint256 createdAt;
|
||||
uint256 expiresAt;
|
||||
}
|
||||
|
||||
// ============ Mappings ============
|
||||
mapping(uint256 => Task) public tasks;
|
||||
mapping(uint256 => bool) public taskExists;
|
||||
|
||||
// ============ Events ============
|
||||
event TaskCreated(
|
||||
uint256 indexed taskId,
|
||||
address indexed requester,
|
||||
uint256 payment,
|
||||
uint256 expiresAt
|
||||
);
|
||||
event WorkerAssigned(uint256 indexed taskId, address indexed worker);
|
||||
event PaymentReleased(
|
||||
uint256 indexed taskId,
|
||||
address indexed worker,
|
||||
uint256 workerAmount,
|
||||
uint256 platformFee
|
||||
);
|
||||
event TaskCancelled(
|
||||
uint256 indexed taskId,
|
||||
address indexed requester,
|
||||
uint256 refunded
|
||||
);
|
||||
event TaskExpired(
|
||||
uint256 indexed taskId,
|
||||
address indexed requester,
|
||||
uint256 refunded
|
||||
);
|
||||
event PlatformFeesWithdrawn(address indexed owner, uint256 amount);
|
||||
event DebugLog(
|
||||
string action,
|
||||
address sender,
|
||||
uint256 taskId,
|
||||
uint256 timestamp
|
||||
);
|
||||
|
||||
// ============ Modifiers ============
|
||||
modifier validTask(uint256 _taskId) {
|
||||
require(taskExists[_taskId], "Invalid task ID");
|
||||
_;
|
||||
}
|
||||
|
||||
modifier onlyRequester(uint256 _taskId) {
|
||||
require(tasks[_taskId].requester == msg.sender, "Not requester");
|
||||
_;
|
||||
}
|
||||
|
||||
modifier taskIsOpen(uint256 _taskId) {
|
||||
require(tasks[_taskId].status == TaskStatus.Open, "Not open");
|
||||
_;
|
||||
}
|
||||
|
||||
modifier taskInProgress(uint256 _taskId) {
|
||||
require(
|
||||
tasks[_taskId].status == TaskStatus.InProgress,
|
||||
"Not in progress"
|
||||
);
|
||||
_;
|
||||
}
|
||||
|
||||
// ============ Constructor ============
|
||||
/**
|
||||
* @param _cUSDAddress Valid cUSD contract address on Celo Sepolia.
|
||||
*/
|
||||
constructor(address _cUSDAddress) Ownable(msg.sender) {
|
||||
require(_cUSDAddress != address(0), "Invalid cUSD address");
|
||||
cUSD = IERC20(_cUSDAddress);
|
||||
}
|
||||
|
||||
// ============ Core Logic ============
|
||||
|
||||
function createTask(
|
||||
uint256 _paymentAmount,
|
||||
uint256 _durationInDays
|
||||
) external whenNotPaused nonReentrant returns (uint256) {
|
||||
require(_paymentAmount > 0, "Invalid amount");
|
||||
require(
|
||||
_durationInDays > 0 && _durationInDays <= 90,
|
||||
"Invalid duration"
|
||||
);
|
||||
|
||||
taskCounter++;
|
||||
uint256 newTaskId = taskCounter;
|
||||
uint256 expiry = block.timestamp + (_durationInDays * 1 days);
|
||||
|
||||
cUSD.safeTransferFrom(msg.sender, address(this), _paymentAmount);
|
||||
|
||||
tasks[newTaskId] = Task({
|
||||
taskId: newTaskId,
|
||||
requester: msg.sender,
|
||||
worker: address(0),
|
||||
paymentAmount: _paymentAmount,
|
||||
status: TaskStatus.Open,
|
||||
createdAt: block.timestamp,
|
||||
expiresAt: expiry
|
||||
});
|
||||
|
||||
taskExists[newTaskId] = true;
|
||||
|
||||
emit TaskCreated(newTaskId, msg.sender, _paymentAmount, expiry);
|
||||
emit DebugLog("createTask", msg.sender, newTaskId, block.timestamp);
|
||||
|
||||
return newTaskId;
|
||||
}
|
||||
|
||||
function assignWorker(
|
||||
uint256 _taskId,
|
||||
address _worker
|
||||
) external validTask(_taskId) taskIsOpen(_taskId) whenNotPaused {
|
||||
Task storage task = tasks[_taskId];
|
||||
require(
|
||||
msg.sender == task.requester || msg.sender == owner(),
|
||||
"Not authorized"
|
||||
);
|
||||
require(_worker != address(0), "Invalid worker");
|
||||
require(_worker != task.requester, "Requester cannot be worker");
|
||||
|
||||
task.worker = _worker;
|
||||
task.status = TaskStatus.InProgress;
|
||||
|
||||
emit WorkerAssigned(_taskId, _worker);
|
||||
emit DebugLog("assignWorker", msg.sender, _taskId, block.timestamp);
|
||||
}
|
||||
|
||||
function approveSubmission(
|
||||
uint256 _taskId
|
||||
)
|
||||
external
|
||||
onlyOwner
|
||||
validTask(_taskId)
|
||||
taskInProgress(_taskId)
|
||||
nonReentrant
|
||||
{
|
||||
Task storage task = tasks[_taskId];
|
||||
uint256 total = task.paymentAmount;
|
||||
uint256 platformFee = (total * PLATFORM_FEE_BPS) / BPS_DENOMINATOR;
|
||||
uint256 workerShare = total - platformFee;
|
||||
|
||||
task.status = TaskStatus.Completed;
|
||||
platformFeesAccumulated += platformFee;
|
||||
cUSD.safeTransfer(task.worker, workerShare);
|
||||
|
||||
emit PaymentReleased(_taskId, task.worker, workerShare, platformFee);
|
||||
emit DebugLog(
|
||||
"approveSubmission",
|
||||
msg.sender,
|
||||
_taskId,
|
||||
block.timestamp
|
||||
);
|
||||
}
|
||||
|
||||
function rejectSubmission(
|
||||
uint256 _taskId
|
||||
)
|
||||
external
|
||||
onlyOwner
|
||||
validTask(_taskId)
|
||||
taskInProgress(_taskId)
|
||||
nonReentrant
|
||||
{
|
||||
Task storage task = tasks[_taskId];
|
||||
task.status = TaskStatus.Cancelled;
|
||||
|
||||
cUSD.safeTransfer(task.requester, task.paymentAmount);
|
||||
|
||||
emit TaskCancelled(_taskId, task.requester, task.paymentAmount);
|
||||
emit DebugLog("rejectSubmission", msg.sender, _taskId, block.timestamp);
|
||||
}
|
||||
|
||||
function cancelTask(
|
||||
uint256 _taskId
|
||||
)
|
||||
external
|
||||
validTask(_taskId)
|
||||
taskIsOpen(_taskId)
|
||||
onlyRequester(_taskId)
|
||||
nonReentrant
|
||||
{
|
||||
Task storage task = tasks[_taskId];
|
||||
task.status = TaskStatus.Cancelled;
|
||||
|
||||
cUSD.safeTransfer(task.requester, task.paymentAmount);
|
||||
|
||||
emit TaskCancelled(_taskId, task.requester, task.paymentAmount);
|
||||
emit DebugLog("cancelTask", msg.sender, _taskId, block.timestamp);
|
||||
}
|
||||
|
||||
function claimExpiredTask(
|
||||
uint256 _taskId
|
||||
) external validTask(_taskId) nonReentrant {
|
||||
Task storage task = tasks[_taskId];
|
||||
require(block.timestamp >= task.expiresAt, "Not expired");
|
||||
require(
|
||||
task.status == TaskStatus.Open ||
|
||||
task.status == TaskStatus.InProgress,
|
||||
"Already finalized"
|
||||
);
|
||||
require(msg.sender == task.requester, "Only requester");
|
||||
|
||||
task.status = TaskStatus.Expired;
|
||||
cUSD.safeTransfer(task.requester, task.paymentAmount);
|
||||
|
||||
emit TaskExpired(_taskId, task.requester, task.paymentAmount);
|
||||
emit DebugLog("claimExpiredTask", msg.sender, _taskId, block.timestamp);
|
||||
}
|
||||
|
||||
function withdrawPlatformFees() external onlyOwner nonReentrant {
|
||||
uint256 amount = platformFeesAccumulated;
|
||||
require(amount > 0, "No fees");
|
||||
platformFeesAccumulated = 0;
|
||||
cUSD.safeTransfer(owner(), amount);
|
||||
|
||||
emit PlatformFeesWithdrawn(owner(), amount);
|
||||
emit DebugLog("withdrawPlatformFees", msg.sender, 0, block.timestamp);
|
||||
}
|
||||
|
||||
// ============ Views ============
|
||||
function getTask(
|
||||
uint256 _taskId
|
||||
) external view validTask(_taskId) returns (Task memory) {
|
||||
return tasks[_taskId];
|
||||
}
|
||||
|
||||
function isTaskExpired(
|
||||
uint256 _taskId
|
||||
) external view validTask(_taskId) returns (bool) {
|
||||
return block.timestamp >= tasks[_taskId].expiresAt;
|
||||
}
|
||||
|
||||
function getContractBalance() external view returns (uint256) {
|
||||
return cUSD.balanceOf(address(this));
|
||||
}
|
||||
|
||||
// ============ Admin Controls ============
|
||||
function pause() external onlyOwner {
|
||||
_pause();
|
||||
}
|
||||
|
||||
function unpause() external onlyOwner {
|
||||
_unpause();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user