Perennial V2 Update #3 — disclosed vulnerability reports and payouts
Every publicly disclosed and closed bug bounty report we hold for Perennial V2 Update #3, with our own summary of each finding and a link to the original disclosure.
- Reports indexed
- 20
- Total paid
- $0
- Critical
- 0
- Largest payout
- $0
This page collects the 20 closed, publicly disclosed Perennial V2 Update #3 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 Logic error, Access control, 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
- Logic error findings disclosed against Perennial V2 Update #3
- Access control findings disclosed against Perennial V2 Update #3
Curated highlights
The largest disclosed payouts in this group, with our own summary of each. Every report links back to the original disclosure.
Perennial V2 Update #3: Anyone can cancel other accounts `nonces` and `groups`, leading to griefing their `Intents`.
Perennial V2's verifier layer (VerifierBase and its AccountVerifier, OrderVerifier and Verifier derivatives) fails to bind the entity that signs an EIP-712 message to the account whose state it mutates. Signature validity is checked only against common.signer while nonce and group cancellation are applied against the distinct common.account field, with no check that the signer is authorized for that account. Consequently any caller able to produce a valid signature for any signer address (including their own) can consume nonces and groups belonging to arbitrary victims. This lets outsiders permanently invalidate pending Intents, cancel market makers' standing orders, depress competitors' fill rates, and shatter the limit-order Intents system, which is why the protocol team confirmed and fixed the flaw in PRs to both the perennial-v2 and root packages.
Perennial V2 Update #3: Market coordinator can liquidate all users in the market
Perennial V2's market coordinator can raise margin, maintenance and minimum-USD parameters without any upside ceiling, bounded only on the downside to prevent zero values. A malicious coordinator can inflate these ratios to extreme levels, instantly rendering all maker and taker positions unhealthy, then liquidate every position while percentage-based fees accumulate to the coordinator's own maker position. Combined with the coordinator's fee-setting power, this lets the coordinator extract at least 1% of all users' notional and wipe out the entire market. No timelock exists to warn users in advance of the parameter change, so the abuse is carried out without user recourse.
Perennial V2 Update #3: _ineligible() redemptionEligible is miscalculated
Perennial V2's Vault._ineligible() understated the redemptionEligible figure because the eligibility formula subtracted the full global.deposit even though that accumulator already contains the depositAssets being processed in the same update() call, and those assets are not yet part of totalCollateral. The double-counting makes ineligible assets appear smaller than they are, which drives the vault to allocate collateral toward opening too many positions and eventually breaks users' ability to claimAssets. The walkthrough shows redemptionEligible returning 90 where 100 is correct, and the protocol team acknowledged and fixed the bug in a follow-up PR that passes the pending deposit into the calculation.
Perennial V2 Update #3: Market coordinator can steal all market collateral by abusing very low value of `scale`
A Perennial V2 market coordinator can steal all market collateral by exploiting weak validation of the fee-scale risk parameter. The proportional and adiabatic fee formulas multiply by order size divided by scale without an upper bound, so setting scale near zero (while either lowering makerLimit or inflating efficiencyLimit to pass the only guard) charges settlement fees far exceeding 100%. The coordinator opens a large settled maker position and unsettled taker position, changes risk parameters, then settles: the taker is driven into huge bad debt that is booked as maker profit and withdrawn immediately. In vault integrations this can be repeated across rebalances to drain nearly all vault funds.
Perennial V2 Update #3: Market coordinator can set proportional and adiabatic fees much higher than limited by protocol due to fixed point truncation
Perennial v2's risk parameter handling validates the ratio between makerLimit and the taker/maker fee scale before those values are truncated to integers for storage. Because validation operates on the untruncated values while storage keeps only integer magnitudes, a market coordinator can pick values that pass the protocol-enforced minimum ratio check yet, once truncated, collapse below that ratio. For high-priced tokens like WBTC, e.g. a makerLimit of 3.9 and scale of 1.95 validates as a valid 50% ratio but is stored as 3 and 1 (33%), letting the coordinator charge proportional and adiabatic fees up to roughly 1.5x higher than the protocol intends at the cost of users. The protocol team confirmed and fixed the issue in a linked pull request.
Perennial V2 Update #3: Corrupted storage after upgrade in the `MarketFactory` contract.
Perennial's upgradeable MarketFactory contract shipped an upgrade that inserted a new `extensions` mapping between already-occupied storage slots. Because Solidity maps declared variables to fixed slot offsets, this shifted the address of every subsequent field, so after the upgrade `operators`, `_markets`, and `_referralFees` would read and write from the wrong slots, silently corrupting operator grants, market registrations, and referral fees. The flaw requires no attacker action — it is triggered simply by performing the upgrade, after which the factory behaves unpredictably depending on how many slots were inserted. The protocol remedied it by moving the new fields to the end of the storage layout so existing values remain aligned.
All reports in this group
- Perennial V2 Update #3: The `RiskParameter.liquidationFee` variable is not treated and validated as a percentage value, leading to breaking protocol invariants.Logic error$0
- Perennial V2 Update #3: `MultiInvoker`, `Manager` and `Account` unexpected reverts in certain conditions due to AAVE reverting on deposits and withdrawals with 0 amountLogic error$0
- Perennial V2 Update #3: when ReserveBase undercollateralized , Manager.orders will not be able to executeLogic error$0
- Perennial V2 Update #3: Market coordinator can set `staleAfter` to a huge value allowing anyone to steal all market collateral when there are no transactions for some timeLogic error$0
- Perennial V2 Update #3: Maliciously specifying a very large intent.price will result in a large gain at settlement, stealing fundsLogic error$0
- Perennial V2 Update #3: `MultiInvoker` and `Manager` orders execution can be DOS in key moments if AAVE/Compound utilization is at 100%Logic error$0
- Perennial V2 Update #3: TriggerOrder.notionalValue() Using the wrong latestPositionLocal to calculate the value causes the user to overpay feesLogic error$0
- Perennial V2 Update #3: `Controller`'s core function of Rebalance will not rebalance when rebalance is needed in some cases, breaking core functionalityLogic error$0
- Perennial V2 Update #3: Emptyset reserve strategies may revert when aave/compound supply limit is reached or pool owner pause/froze the poolLogic error$0
- Perennial V2 Update #3: Market coordinator can steal all market collateral by changing adiabatic feesLogic error$0
- Perennial V2 Update #3: Keepers can lose compensation feeLogic error$0
- Perennial V2 Update #3: The `Market.migrate()` function has no effect and does not migrate `PositionStorageGlobal` to the new storage layout, breaking the migration assumption.Logic error$0
- Perennial V2 Update #3: Lack of access control in the `MarketFactory.updateExtension()` function.Access control$0
- Perennial V2 Update #3: settle() asyncFee is left in the KeepFactory and is not transfer to the keeper.Logic error$0