Fenix Finance — disclosed vulnerability reports and payouts
Every publicly disclosed and closed bug bounty report we hold for Fenix Finance, with our own summary of each finding and a link to the original disclosure.
- Reports indexed
- 9
- Total paid
- $0
- Critical
- 1
- Largest payout
- $0
This page collects the 9 closed, publicly disclosed Fenix Finance reports indexed on CoinBuggie. Nothing here is active or unpatched — every entry was published by the programme or the researcher after remediation.
The findings concentrate in Reentrancy, Integer overflow / underflow, Logic error, across EVM-Solidity. Reading a single protocol end to end is the fastest way to see which assumptions its codebase repeatedly gets wrong, which is usually a better predictor of where the next finding lives than the category alone.
Use the vulnerability class links below to compare these findings against the same bug class in other protocols.
What reviewers look for
- Reentrancy findings disclosed against Fenix Finance
- Integer overflow / underflow findings disclosed against Fenix Finance
- Logic error findings disclosed against Fenix Finance
Curated highlights
The largest disclosed payouts in this group, with our own summary of each. Every report links back to the original disclosure.
Fenix Finance: `dettachFromManagedNFT` might revert and temporarily prevent users from detaching in certain situations
Fenix Finance is a Curve-style voting-escrow protocol where users can attach their veNFT to a ManagedNFT strategy that votes on gauges on their behalf. When a user calls dettachFromManagedNFT, the function ends by invoking _poke, which recasts the previously voted gauge set through _vote. If any gauge in that set was killed mid-epoch, _vote reverts with GaugeAlreadyKilled, aborting the entire detach transaction even though the detach itself is legitimate. The consequence is that a user's veNFT becomes temporarily undetachable until the ManagedNFT independently recasts its votes to exclude the dead gauge, a self-inflicted availability condition with no fund loss.
Fenix Finance: `boostedValue` should be added to `permanentTotalSupply` for permanently locked tokens
In Fenix Finance's VotingEscrowUpgradeableV2, the `_processLockChange` function adds a boosted FNX boost value to a user's individual locked amount but omits the same boost from the global `permanentTotalSupply` accumulator when the lock is permanently locked. Since `unlockPermanent` subtracts the full locked balance from that ledger, earlier permanent unlockers can drain the ledger below what later unlockers are owed, causing an underflow revert that blocks the last permanent-lock holder from unlocking their veNFT. The sponsor confirmed the bug as a missed regression after refactoring, and the Code4rena judge settled on Medium severity after a High claim about incorrect per-user voting power was retracted, since per-veNFT voting power is computed from a separate history mapping. The fix adds the boosted value into the permanent supply accumulator so the ledger matches the amounts later subtracted.
Fenix Finance: Potential incorrect index update in revived gauge under specific conditions
Fenix Finance's Voter contract records reward distribution per gauge via an index compared against a global index, computing the claimable delta as (global - gauge) scaled by vote weight. The reviveGauge function re-activates a killed gauge but never resets its index to the current global index. In the normal flow distributeAll refreshes every gauge each epoch and hides the flaw, but when the gauge set is large enough to force per-gauge manual distribution, a killed gauge can be revived while still carrying a stale index and then over-claim rewards for the period it was dead, shrinking the share available to correctly synced gauges. Fenix acknowledged the report as technically valid under these specific conditions, matching the adjudicated medium severity.
Fenix Finance: If rewards are not distributed to some gauges in an epoch, it can lead to incorrect rewards distribution in the next epoch
Fenix Finance's VoterUpgradeableV2 tracks a single global reward index that advances on every minter distribution, but each gauge's claimable amount is priced against the current epoch's weightsPerEpoch snapshot multiplied by the index delta since the gauge last claimed. When a gauge is skipped for an entire epoch and its vote weight changes the following week, its deferred rewards get valued at the new weights, so claimed totals can exceed the FNX actually held by the contract. The included two-pool PoC shows the contract attempting to pay 190 FNX when only 150 is available, causing distribution reverts and unfair allocations. The sponsor acknowledged the design and pointed to distributeAll() as the primary mitigation, but the judge upheld the finding at Medium because the flaw existed in the audited code and skipping a gauge for a full epoch, while unusual, was not an impossible precondition.
Fenix Finance: The `VoterUpgradeableV2.createV3Gauge` function incorrectly uses `v2GaugeFactory` instead of `v3GaugeFactory`
Fenix Finance's VoterUpgradeableV2.createV3Gauge deploys gauges for V3 pools through the wrong factory: the call hardcodes IGaugeFactory(v2GaugeFactory) instead of v3GaugeFactory. Because GaugeFactoryUpgradeable.createGauge initializes each new gauge proxy with the calling factory's defaultBlastGovernor and merklGaugeMiddleman, V3-pool gauges end up managed under the V2 factory's configuration. The sponsor confirmed the finding as valid but stated the V2/V3 implementations are currently identical, so there is no immediate economic impact and only future risk if the implementations diverge; the judge accordingly downgraded it from high to Medium severity. The fix is a one-line change to reference v3GaugeFactory.
Fenix Finance: `killGauge()` will lead to wrong calculation of emission
Fenix Finance's VoterUpgradeableV2 gauge logic has an emission-accounting flaw that manifests when a governance-authorized killGauge() call removes a pool. The function subtracts the killed pool's weight from totalWeightsPerEpoch but leaves the corresponding per-pool entry in weightsPerEpoch intact. When notifyRewardAmount() later divides the epoch emission by the now-smaller total weight, the index is inflated, while distribute() still credits gauges using their unreduced vote weights. In the report's worked example, killing one of three equal-weight pools raises each surviving gauge's credit from 26.5e18 to 40e18, so two surviving gauges together draw the entire 80e18 emission budget — over-promising rewards beyond what the epoch can fund.