Skip to content

[SECURITY] 2 High + 1 Medium — RevenueBondFactory.redeem non-pro-rata + AgentCredit.liquidate no incentive (Base mainnet) #3

Description

@Leite007

[SECURITY] 2 High + 1 Medium — RevenueBondFactory.redeem non-pro-rata escrow & AgentCredit.liquidate no incentive (Base mainnet)

Summary (TL;DR)

I found three vulnerabilities in the Arcis Protocol smart contracts deployed on Base mainnet (chain 8453). Two are High severity (loss of user funds / broken lending-liquidation mechanism) and one is Medium (fee-share over-minting). All were validated against a pinned commit, reproduced with executable Foundry PoCs, and confirmed on the live Base fork.

Affected contracts (live)

Contract Address (Base) Issue
RevenueBondFactory 0xEb65d8BB08e0Ea4A6Bb9162D53d1B444f99681BA High — non-pro-rata escrow redemption
AgentCredit 0xdF31800E620F728297340d66Acf5a306f07ce7a1 High — no incentive to liquidate
ArcisVault (raUSDC) 0x00325D9Da832B38179Ed2f0dabd4062D93E325A7 Medium — fee-share over-mint

Repo / commit: Arcis-Protocol/core @ 256825bab5dbfeec7d20a878a33047a6d8c1c4db (Solidity 0.8.24).


Finding #1 — HIGH: RevenueBondFactory.redeem distributes escrow NON-pro-rata (co-investor loss, front-runnable)

File/lines: src/core/RevenueBondFactory.sol redeem() — underfunded branch, lines 227-231.

In the underfunded branch, the entire escrow balance is paid to whichever holder calls redeem first, with no reservation for other holders. Subsequent redeemers receive zero.

Scenario: Two holders (A, B) each invested 100K. Escrow = 100K (underfunded). Fair pro-rata: 50K each. Instead, A redeems first and receives the full 100K; B receives 0. B loses 100% of what it was owed. This is deterministic and front-runnable.

Impact: User-fund loss exploitable by call ordering. Severity: High.


Finding #2 — HIGH: AgentCredit.liquidate has NO economic incentive — liquidation mechanism broken

File/lines: src/core/AgentCredit.sol liquidate() (lines 217-243). The constant LIQUIDATION_BONUS_BPS = 500 (line 29) is declared but never used in the liquidation logic.

liquidate requires healthFactor < 1.0 (collateralValue < owedAmount), yet the liquidator receives only the collateral shares — which are always worth LESS than the debt it pays. No rational actor will execute a liquidation at a guaranteed loss.

Impact: The liquidation mechanism is inoperative. Insolvent positions are never closed, permanently tying up lenders' liquidity and exposing the pool to growing, irreversible bad debt. Severity: High (incentive failure in the core lending function).


Finding #3 — MEDIUM: ArcisVault.harvest over-mints fee shares to the feeRecipient

File/lines: src/core/ArcisVault.sol harvest() (lines 489-522). Fee shares are computed using totalAssets() BEFORE deployedBalance is updated (line 520), resulting in a smaller denominator and more fee shares minted than correct. Same pattern in ArcisAgentVault. The over-mint per event is small (fraction of a basis point) but accumulates as compounding fee in favor of the feeRecipient.

Impact: ERC-4626 yield-inclusion violation; deterministic over-mint favoring feeRecipient. Severity: Medium.


Reproducibility

All three findings have executable Foundry PoCs that pass and reproduce the exact scenarios described above. PoC source code and detailed logs are available privately upon request — I am withholding them from this public issue per responsible disclosure practice.

forge build
forge test --match-contract ArcisFindings -vv   # both High PoCs pass

False positives explicitly ruled out

  • Reentrancy: all target functions use nonReentrant + CEI pattern.
    • ERC-4626 donation/inflation: totalAssets() is internal accounting; external donation not counted.
      • withdraw underflow with early-withdrawal fee: floor rounding keeps amount < reserveBalance.
        • Interest-rate divide-before-multiply: rates capped at 50% APR, no usable overflow.

Disclosure protocol

  1. Responsible disclosure to the Arcis team (this report + private PoCs on request).
    1. 90-day window before any public disclosure (standard whitehat practice).
    1. No formal bug bounty program found; disclosing directly to the team.
      Please reach out so I can share the full PoC suite privately.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions