File: src/facets/CoreFacet.sol:406-459
updateTask allows increasing task.reward without depositing additional USDC. If the relay server does not validate newReward against available balance, a requester can inflate their task's reward and drain other tasks' escrow at acceptance time.
updateTask(taskA, 210 USDC) — no deposit requiredif (newReward != 0 && newReward != task.reward) {
refund = newReward < task.reward ? task.reward - newReward : 0;
task.reward = newReward; // ← increases without pulling USDC
}
Mitigation: Pull newReward - task.reward from requester on increase, or track per-task escrow explicitly.
File: src/facets/EvaluatorFacet.sol:78-135
evaluate() pays the evaluator's fee + returns their stake BEFORE the appeal window opens. If the verdict is overturned on appeal, the evaluator keeps their payment. The stake is returned immediately — zero skin in the game during the appeal period.
// Payment happens BEFORE appeal window opens
if (evalFee + stakeReturn > 0) {
s.usdcToken.transfer(evalCfg.evaluator, evalFee + stakeReturn);
}
task.status = ITMPCore.TaskStatus.Appealing; // set AFTER payment
Recommendation: Hold evaluator fee + stake in escrow until appeal window closes. Slash on overturned verdict.
File: src/hooks/RewardVault.sol:97-101
emergencyWithdraw ignores totalReserved accounting, allowing the owner to drain ALL tokens including amounts reserved for active task rewards.
function emergencyWithdraw(address to) external onlyOwner {
uint256 balance = token.balanceOf(address(this));
token.safeTransfer(to, balance); // ← bypasses totalReserved
}
File: src/facets/CoreFacet.sol:517-546
_refundAuctionClaimed pays the winning bidder on expiry without checking if task.deliverable was ever set. A bidder can win an auction then wait for expiry to collect payment without delivering.
File: src/facets/CoreFacet.sol:225-277
Workers can submit unlimited deliverables, bloating taskSubmissionHashes storage. Compare: auctions cap bids at 500 (MAX_BIDS_PER_TASK).