Skip to content

[Critical] Service fees and bond payments are permanently locked in AgentAuditRegistryV3 #1

Description

@69fu

AgentAuditRegistryV3 accumulates ETH from every stake() call into accruedServiceFees (and into the contract balance via the bond itself), but the contract exposes no withdrawal function. Any ETH paid as service fees by agent developers is permanently trapped.

Analysis

accruedServiceFees is declared at contracts/src/AgentAuditRegistryV3.sol:92 and incremented inside stake():

uint256 public accruedServiceFees;
// ...
accruedServiceFees += serviceFee;   // line ~196 in stake()

I grepped the entire contract (and AgentAuditRegistryV2.sol, which has the same pattern) for the words withdraw, transfer, call{value:, and send. There is no path that moves ETH out of the contract for the operator/owner, except payable(profile.developer).transfer(...) which only refunds the developer's own bond on releaseBond.

Root Cause

The contract has the accounting variable but never implemented the corresponding action. Likely an oversight that wasn't caught because the unit tests don't assert on operator-side ETH balance changes.

Suggested Fix

Add an onlyOwner (or onlyOperator) function:

function withdrawServiceFees(address payable to, uint256 amount) external onlyOwner {
    require(amount <= accruedServiceFees, "INSUFFICIENT_FEES");
    accruedServiceFees -= amount;
    (bool ok, ) = to.call{value: amount}("");
    require(ok, "TRANSFER_FAILED");
    emit ServiceFeesWithdrawn(to, amount);
}

Impact

Critical — every penny of fees collected on a deployed instance is unrecoverable without a contract upgrade. If V3 is already live on Polygon Edge with real funds, this needs an emergency upgrade path.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions