SC Audit//AUDIT · Campaign 5 FiRM AuditCommand center

Autonomous SC audit pipeline

Campaign 5 report · 2026-05-14
Inverse Finance FiRM Protocol
Sherlock bug bounty · max bounty 100,000 DOLA
3
High severity
10
Medium severity
12
Low severity
10
Informational
~$9K
Expected value
8
Agents deployed

API usage and resource consumption

Agent 3 · ALE accounting
Tool uses 38
Tokens 61,106
Duration 4m 28s
Findings 10 (1M, 9 Low/Info)
Agent 6 · Staking derivative feeds
Tool uses 39
Tokens 45,424
Duration 4m 10s
Findings 10 (3M, 4L, 3I)
Agent 7 · DOLA price feeds
Tool uses 38
Tokens 73,325
Duration 3m 59s
Findings 9 (2M, 4L, 3I)
Agent 8 · Base feed infrastructure
Tool uses 25
Tokens 54,364
Duration 3m 20s
Findings 10 (1H, 4M, 5L)
Agent 9 · Market.sol post-C4
Tool uses 30
Tokens 73,535
Duration 3m 59s
Findings 12 (1M, 4L, 7I)
Agent 10 · DBR token mechanics
Tool uses 8
Tokens 33,281
Duration 3m 18s
Findings 10 (2H, 1M, 4L, 3I)
Explore · prior audit search
Tool uses 38
Tokens 52,203
Duration 1m 4s
Purpose Deduplication
Research · bounty scope
Tool uses 40
Tokens 41,723
Duration 5m 18s
Purpose Sherlock/Immunefi intel
Metric Value
Total agents8 (6 audit + 2 research)
Total tool uses (across agents)256
Total tokens (agents only)434,961
Main thread (orchestration)~115,000 est.
Total session tokens~550,000
Contracts analyzed19
Lines of code~4,100 LoC
Tokens per LoC~134
Estimated API cost~$5-8
Wall clock time~30 minutes (parallel)

All findings with ratings (1 to 10)

Rating equals novelty times exploitability times impact times submittability.

High severity

HIGH H-01 DBR accrueDueTokens() pre-initialization griefing 9/10
File src/DBR.sol:285-295 · agent DBR token mechanics The bug accrueDueTokens() is public and permissionless. When called for a user with no debt, it sets lastUpdated[user] = block.timestamp. But the conditional if(accrued > 0 || lastUpdated[user] == 0) means if lastUpdated was already set and debt == 0, the timestamp never gets refreshed. Attack
1. Attacker calls accrueDueTokens(victim) at T1     -> lastUpdated[victim] = T1
2. One year passes. Victim borrows 1000 DOLA at T2
3. onBorrow() -> accrueDueTokens(): debt=0, lastUpdated!=0
   -> accrued=0, condition FALSE, lastUpdated stays T1!
4. debts[victim] += 1000e18
5. balanceOf(victim) now computes: (T2+1 - T1) * 1000e18 / 365 days
   = ~1000 DBR consumed INSTANTLY (should be ~0.00003 DBR)
6. Victim immediately in deficit -> force replenished at penalty
Impact An attacker can grief any future borrower. The attack costs only gas. The attacker can also profit as the force replenisher, since that role earns a DOLA reward from the Market contract. Fix Always update lastUpdated no matter the accrual amount.
Novel vs C4 YES (C4 found a different DBR issue) Exploitable YES (permissionless) Direct fund loss YES
HIGH H-02 Convex feeds bypass all ChainlinkBasePriceFeed protections 7/10
File src/feeds/ConvexCurvePriceFeed.sol:36-50, ConvexFraxSharePriceFeed.sol:33-47 · agent base feed infrastructure

Legacy feeds call raw Chainlink feeds directly, with no heartbeat check, no isPriceOutOfBounds, no fallback, and no staleness signal. During a circuit breaker event, say a CRV crash past minAnswer, collateral can be overvalued 10x.

Novel UNCERTAIN (Nomoi may have reviewed) Exploitable YES (circuit breaker scenario) Precedent LUNA crash, CRV depeg
HIGH H-03 DbrDistributor modifier runs the body before checks 4/10
File src/DbrDistributor.sol:66-73 · agent DBR token mechanics

The onlyINVEscrow modifier places _ before validation. It's safe right now, since a revert rolls back, but the pattern is fragile. Code comments call this an intentional CEI violation for reentrancy protection. Likely out of scope, an acknowledged design choice.

Novel NO (acknowledged in code) Currently exploitable NO Submission do not submit

Medium severity

MED M-05 WbtcPriceFeed division by zero before bounds check 8/10
File src/feeds/WbtcPriceFeed.sol:70-71

Division by wbtcBtcPrice happens before the bounds check. A feed returning 0 triggers a division-by-zero panic, the fallback becomes unreachable, and the entire wBTC market freezes. Post-audit contract, and novel.

MED M-06 WbtcPriceFeed BTC/USD missing bounds check 7/10
File src/feeds/WbtcPriceFeed.sol:70-73

The BTC/USD Chainlink feed never gets checked for circuit breaker bounds. Only wBTC/BTC is checked. If BTC/USD hits its circuit breaker cap, the capped price passes through and never triggers the fallback. Post-audit, and novel.

MED M-07 WbtcPriceFeed fallback has zero validation 7/10
File src/feeds/WbtcPriceFeed.sol:123-131

The fallback uses Curve tricrypto plus Chainlink USDT/ETH plus ETH/USD with zero staleness, bounds, or negative checks. It returns updatedAt from the primary path, not the fallback. Post-audit, and novel.

MED M-01 Fallback feed not validated for staleness or bounds 6/10
File src/feeds/ChainlinkBasePriceFeed.sol:77-93

When the primary feed is stale or out of bounds, the fallback price gets accepted with no staleness or bounds validation. Both feeds failing at once means an undetected bad price.

MED M-03 Convex ratio floor creates an artificial price floor 6/10
File ConvexCurvePriceFeed.sol:42-46

A hardcoded 50% floor sits on the CvxCRV/CRV ratio. If CvxCRV depegs to 20%, the feed still reports 50%, overvaluing by 2.5x. Comments call it temporary, but it stays in production.

MED M-04 Fallback returns negative or zero price unvalidated 5/10
File ChainlinkBasePriceFeed.sol:78-86

The fallback price never gets checked for positive or zero. Oracle.sol mitigates it with require(price > 0), but direct feed consumers like PessimisticFeed and CurveLPPessimisticFeed bypass that check.

MED M-08 DolaPriceFeed both feeds stale scenario 5/10
File DolaPriceFeed.sol:103-131

When pyUSD/USD and FRAX/USD go stale at the same time, a stale price gets returned with updatedAt = 0. Liquidations then proceed on stale data.

MED M-02 Stale price returned instead of a revert 4/10
File ChainlinkBasePriceFeed.sol:87-90, Oracle.sol:158-162 C4 duplicate risk

Staleness is not checked in Oracle.sol. BorrowController only protects borrows. C4 flagged the same systemic issue in October 2022.

MED M-10 Missing staleness in liquidation, withdrawal, force replenish 4/10
File Market.sol:658, 630, 469 C4 duplicate risk

Same systemic staleness gap. Not submittable as novel.

MED M-09 DolaFixedPriceFeed holds $1 during a DOLA depeg 3/10
File DolaFixedPriceFeed.sol:13

An acknowledged design choice. The contract carries a "don't use for external integrations" comment. A DOLA depeg is a protocol-level systemic risk.

Low severity

ID Finding File Rating
L-01 Double-pessimistic dampening (feed plus oracle) PessimisticFeed.sol 3
L-02 Bounds check uses primary aggregator after fallback ChainlinkBasePriceFeed.sol 4
L-03 BorrowController staleness only protects borrows BorrowController.sol 3
L-04 Mutable storage for critical addresses (ConvexCurve) ConvexCurvePriceFeed.sol 2
L-05 StYEthPriceFeed missing heartbeat staleness check StYEthPriceFeed.sol 4
L-06 StYEthPriceFeed fallback returns zero, market freeze StYEthPriceFeed.sol 3
L-07 InvPriceFeed sole Curve EMA (historically exploited) InvPriceFeed.sol 4
L-08 WbtcPriceFeed no heartbeat enforcement anywhere WbtcPriceFeed.sol 5
L-09 DBR transfer totalSupply desynchronization DBR.sol 4
L-10 Market rounding mismatch (dust bad debt) Market.sol 2
L-11 ALE dust token accumulation via balanceOf pattern ALE.sol 2
L-12 DolaPriceFeed stale pyUSD used when lower DolaPriceFeed.sol 3

Submission strategy

Tier 1 · submit immediately
FindingTargetRewardConfidence
H-01 DBR accrueDueTokens griefing Critical / High $5K - $15K 70%
M-05+06+07 WbtcPriceFeed bundle Medium $2K 60%
Tier 2 · submit if tier 1 accepted
FindingTargetRewardConfidence
H-02 Convex feeds bypass protections High / Medium $2K - $5K 30%
M-01 Fallback not validated Medium $2K 40%
M-03 Convex ratio floor Medium $2K 30%
Do not submit
FindingReason
M-02, M-10 (staleness gaps)C4 duplicate, same systemic issue flagged Oct 2022
H-03 (modifier pattern)Acknowledged design choice in code comments
M-09 (DolaFixed $1)Design choice, acknowledged
All low findingsBelow bounty threshold, not novel enough
~$9,000
Total expected value
API cost ~$5-8 · ROI ~1,125x to 1,800x

Prior audit coverage and kill criteria

AuditDateScopeOverlap
Code4renaOct 2022 FiRM core (8 contracts, 901 LOC) M-02, M-10, L-09
NomoiMay 2023 cvxCRV market launch H-02 M-03 maybe
yAuditJan 2024 sDOLA None (different scope)
Sherlock ContestNov 2025 Junior Tranches None (different scope)
Sherlock AuditOct 2025 Junior Tranches None (different scope)
Kill criteria do not kill Five prior audits exist, but they cover different scopes and times. The post-audit additions, price feeds, ALE, and ChainlinkBasePriceFeed, were never audited. Key novel findings are confirmed.

Pipeline performance (cumulative)

#TargetResultEVStatus
1K2 Lending (C4 $135K)1 Medium$586 Awaiting
2Renegade ZK Dark Pool (C4)1 High$20,000 Awaiting
3GMTrade0 (correct kill)$0 Done
4CapyFi (Immunefi)0 (prior audit kill)$0 Killed
5Inverse FiRM (Sherlock)2-5 submittable$9,000 Ready
Total pipeline EV$29,586

Scope coverage (19 contracts, ~4,100 LoC)

ContractLoCAgentCoverage
Oracle.sol167MultipleFull
Market.sol688Agent 9Full
DBR.sol393Agent 10Full
DbrDistributor.sol128Agent 10Full
ALE.sol~750Agent 3Full
BorrowController.sol172MultiplePartial
ChainlinkBasePriceFeed.sol162Agent 8Full
PessimisticFeed.sol88Agent 8Full
ConvexCurvePriceFeed.sol84Agent 8Full
ConvexFraxSharePriceFeed.sol81Agent 8Full
CurveLPPessimisticFeed.sol109MultipleFull
DolaFixedPriceFeed.sol56Agent 7Full
DolaPriceFeed.sol193Agent 7Full
USDeBeforeMaturityFeed.sol74Agent 7Full
FeedSwitch.sol131Agent 7Full
StYEthPriceFeed.sol214Agent 6Full
WstETHPriceFeed.sol200Agent 6Full
WbtcPriceFeed.sol132Agent 6Full
InvPriceFeed.sol186Agent 6Full