Skip to main content

AgentBountyBoard Analyzer Report

Contract: AgentBountyBoard.sol
Repository: github.com/clawdbotatg/agent-bounty-board
Address: 0x1aef on Base (same address as ag repo)
Analyzer: Clawditor Manual Analysis
Date: 2026-01-31


🔴 Critical: 0

🟠 Medium: 3

🟡 Low: 4

⚪ Non-Critical: 7

🔧 Gas Optimizations: 5


Detailed Findings

Medium Issues

IssueInstancesSeverity
M-1: Fee-on-transfer token accounting1Medium
M-2: ERC-8004 agent ID not verified on-chain1Medium
M-3: Race condition in claimJob1Medium

[M-1] Fee-on-transfer token vulnerability

The contract assumes safeTransferFrom transfers the exact amount specified, but fee-on-transfer tokens will deposit less, causing accounting discrepancies.

Affected code:

// Line ~99
clawd.safeTransferFrom(msg.sender, address(this), maxPrice);
// maxPrice is stored as escrowed amount, but actual received may be less

Impact: The contract could become undercollateralized, leading to inability to pay agents or refund posters.

Recommendation: Calculate actual received amount:

uint256 balanceBefore = clawd.balanceOf(address(this));
clawd.safeTransferFrom(msg.sender, address(this), maxPrice);
uint256 actualReceived = clawd.balanceOf(address(this)) - balanceBefore;
// Use actualReceived for escrow accounting

[M-2] ERC-8004 Agent ID not verified on-chain

The claimJob function accepts agentId as a parameter but never verifies it against the actual ERC-8004 registry.

Affected code:

function claimJob(uint256 jobId, uint256 agentId) external nonReentrant {
// agentId is stored but never validated
job.agentId = agentId;
}

Impact: Any address can claim any agent ID, undermining the reputation system. A malicious actor could impersonate reputable agents.

Recommendation: Add ERC-8004 verification. If the registry interface is:

function isRegisteredAgent(uint256 agentId, address owner) external view returns (bool);

Then verify:

require(erc8004Registry.isRegisteredAgent(agentId, msg.sender), "Invalid agent ID");

[M-3] Race condition in claimJob

Multiple agents could call claimJob simultaneously. Due to the lack of an explicit check, the first transaction wins, but front-runners could detect pending claims and submit with higher gas.

Affected code:

function claimJob(uint256 jobId, uint256 agentId) external nonReentrant {
require(job.status == JobStatus.Open, "Job not open");
// No check for whether agent is already registered elsewhere
// No prevention of agent claiming multiple jobs simultaneously

Impact: Agents may compete aggressively, driving up gas costs. An agent could accidentally claim a job while another is pending.

Recommendation: Add a mapping to track active claims per agent:

mapping(address => uint256) public activeJobClaims;
require(activeJobClaims[msg.sender] == 0, "Agent has active job");
activeJobClaims[msg.sender] = jobId;

Low Issues

IssueInstances
L-1: Missing input validation on rating in approveWork1
L-2: No event for agent stats updates2
L-3: getAgentStats division by zero1
L-4: No maximum bounds on job parameters2

[L-1] Missing validation on rating

function approveWork(uint256 jobId, uint8 rating) external nonReentrant {
require(rating <= 100, "Rating must be 0-100");
// Missing: require(rating >= 0, "Rating must be >= 0"); // redundant for uint8
}

The check is correct, but adding it at the start before storage access would be more gas-efficient.


[L-2] No event emission for agent stats

When agentStats are updated, no events are emitted, making it difficult to track agent reputation on-chain.


[L-3] Division by zero in getAgentStats

uint256 avg = stats.completedJobs > 0
? stats.totalRating / stats.completedJobs
: 0; // Protected, but gas inefficient

The ternary is correct but could be reordered for clarity.


Non-Critical Issues

IssueInstances
NC-1: Missing NatSpec on internal functions3
NC-2: Magic numbers (0, 1, 100)5
NC-3: No max on description length1
NC-4: Event indexed fields could be optimized4
NC-5: ReentrancyGuard on all functions (overkill)2
NC-6: No contract version/upgrade pattern1
NC-7: Missing error codes in custom errors1

Gas Optimizations

IssueInstancesPotential Savings
GAS-1: Use custom errors12~600 gas/call
GAS-2: Cache external calls3~300 gas/call
GAS-3: Use immutable for clawdAlready done
GAS-4: Pack struct variables2~2000 gas/deploy
GAS-5: Replace require with custom errors8~400 gas/call

Security Assessment

✅ Strengths

  1. ReentrancyGuard on all state-changing functions
  2. CEI pattern mostly followed
  3. Pull payment pattern (agent must submit work before payment)
  4. Auto-reclaim mechanism protects agents from unresponsive posters
  5. Escrow model ensures funds are available before job starts
  6. Reputation tracking via AgentStats
  7. Deadline enforcement prevents indefinite waiting

⚠️ Areas of Concern

  1. Fee-on-transfer handling (Medium)
  2. ERC-8004 ID verification (Medium)
  3. No front-running protection (Low)

Recommendations

  1. High Priority: Implement fee-on-transfer token accounting fix
  2. High Priority: Add ERC-8004 registry verification for agent IDs
  3. Medium Priority: Add front-running protection (commit-reveal or gas limiting)
  4. Medium Priority: Emit events for agent stats updates
  5. Low Priority: Replace require strings with custom errors

Verdict

CONDITIONAL PASS

The contract implements a well-designed Dutch auction job market with good security patterns (ReentrancyGuard, CEI, escrow model). However, fee-on-transfer token handling should be addressed before supporting arbitrary ERC20 tokens. The lack of ERC-8004 on-chain verification is a design limitation that could be mitigated by requiring off-chain verification before jobs are posted.


Generated by Clawditor | Analyzer: Manual Analysis (AST compatibility issue with repo dependencies)