Last-Minute Deposit Could Dilute Accrued Yield in an ERC-4626 Vault
Plether recorded earned senior yield in a separate unpaidSeniorYield variable, but priced new ERC 4626 shares using only seniorPrincipal.
TL;DR
Plether recorded earned senior yield in a separate unpaidSeniorYield variable, but priced new ERC-4626 shares using only seniorPrincipal. A depositor could enter immediately before pool revenue paid the accrued yield, receive shares at an understated price, and capture part of the return earned by earlier LPs. Plether fixed the issue by removing the deferred-yield liability and checkpointing a junior-funded senior coupon directly into principal before deposits are priced.
The focus is pending-yield dilution in ERC-4626 vaults: why reconcile() did not protect existing LPs, how to calculate the loss, and which accounting invariants developers and auditors should test. The same codebase also had a related zero-amount ERC-4626 withdrawal issue. Our article on DeFi vault curation and allocator risk covers the wider vault risk model.
Between April 14 and May 4, 2026, SC Audit Studio ran a pre-audit consultation on Plether Perpetuals, a forex-indexed perps protocol designed for Arbitrum. The protocol used a USDC-denominated liquidity pool. The Plether pre-audit report contains two High and seven Medium severity findings. SC-H1 was a High-severity accounting flaw: a new senior LP could deposit immediately before accrued yield was paid and capture part of the return earned by earlier depositors.
The Protocol Context
Plether's liquidity lived in a contract called HousePool, which split LP capital across senior and junior tranches using a pair of ERC-4626-compatible TrancheVault contracts. The junior tranche absorbed first losses in exchange for residual upside, while the senior tranche received last-loss protection and targeted a fixed return. The reviewed code is preserved through the history of Plether's public plether-core repository.
ERC-4626 standardizes tokenized vaults: users provide an underlying asset and receive shares representing a proportional claim on the vault. In the simplest implementation, a deposit receives shares according to:
01shares minted = deposit assets * total share supply / total managed assetsThe exact implementation may add offsets, fees, or rounding rules, but the economic requirement remains the same. If the asset value in the denominator is understated, a new depositor receives too many shares and dilutes existing holders. If it is overstated, the new depositor receives too few.
In the reviewed implementation, the senior return moved through two accounting stages. First, elapsed yield accrued into a separate liability:
01uint256 public seniorPrincipal;02uint256 public juniorPrincipal;03uint256 public unpaidSeniorYield;Later, when the pool recognized real revenue, the waterfall paid as much of unpaidSeniorYield as the revenue allowed and added the payment to seniorPrincipal:
01uint256 seniorPayout = nextState.unpaidSeniorYield;02if (seniorPayout > remaining) {03 seniorPayout = remaining;04}05 06nextState.seniorPrincipal += seniorPayout;07nextState.unpaidSeniorYield -= seniorPayout;08remaining -= seniorPayout;The senior vault priced shares against seniorPrincipal. It excluded the yield already recorded in unpaidSeniorYield.
unpaidSeniorYield tracked a future payment, so LPs could not withdraw it immediately. Payment depended on the pool recognizing enough real revenue. Once that revenue arrived, the unpaid amount had priority in the waterfall. The claim belonged to the senior LPs who owned shares during the accrual period. New LPs could share in it even though their entry price ignored it.
What We Noticed
An ERC-4626 deposit mints shares at the vault's current exchange rate. That rate must include value already assigned to existing shareholders. An omission lets a new depositor acquire part of that value for free.
The mechanics resemble the better-known ERC-4626 inflation or donation attack, with the price error reversed. In the conventional attack, someone changes the assets-to-shares exchange rate so a victim receives too few shares. OpenZeppelin's ERC-4626 security guide explains that pattern and its virtual-asset defense. In SC-H1, protocol accounting omitted a senior receivable and the attacker received too many shares. Virtual shares alone would not correct a missing economic claim.
Plether accrued the fixed senior return with logic equivalent to:
01function accrueSeniorYield(02 uint256 seniorPrincipal,03 uint256 seniorRateBps,04 uint256 elapsed05) internal pure returns (uint256) {06 return07 (seniorPrincipal * seniorRateBps * elapsed)08 / (10_000 * 365 days);09}Accrual updated unpaidSeniorYield and left seniorPrincipal unchanged. The share price could not see the yield until enough revenue arrived and distributeRevenue() converted the unpaid amount into principal.
This created a gap between economic ownership and ERC-4626 accounting:
01Economic senior claim = seniorPrincipal + unpaidSeniorYield02Value used for senior share pricing = seniorPrincipalAnyone depositing during that gap received too many shares.
Why Reconciliation Before Deposit Was Not Enough
The vault called POOL.reconcile() before completing ordinary deposits. The ordering looked safe because the old state was processed before the new LP received shares. The flaw was inside the reconciliation result.
Without enough revenue, reconciliation calculated elapsed senior yield and added it to unpaidSeniorYield. seniorPrincipal, the share-price input, stayed unchanged. The subsequent deposit still minted against principal alone.
Only a later reconciliation with distributable revenue converted the debt into principal. By then, the attacker already owned newly minted shares. The protocol correctly remembered how much yield was due, but no longer remembered which share supply had earned it.
A pre-mint checkpoint protects existing holders only when it does one of the following:
- move earned value into the assets used by the conversion functions;
- snapshot the eligible share supply and distribute the reward only to those shares; or
- make new depositors pay for the pending claim through a higher entry price.
The reviewed implementation did none of those three things.
Tracing the Dilution Path
Assume Alice is the only senior LP:
- Alice owns 100,000 shares backed by 100,000 USDC of
seniorPrincipal. - Over time, 8,000 USDC of senior yield accrues into
unpaidSeniorYield. - Alice has an economic claim of 108,000 USDC. The vault still prices her 100,000 shares against 100,000 USDC of principal.
An attacker watches for revenue that will pay the accrued yield and deposits 100,000 USDC just before the payment. Because the 8,000 USDC receivable is missing from the share price, the attacker receives approximately 100,000 shares.
The senior state immediately before payment is now:
01seniorPrincipal: 200,000 USDC02total shares: 200,00003unpaidSeniorYield: 8,000 USDCWhen revenue arrives, the waterfall moves the 8,000 USDC into seniorPrincipal. The vault now has 208,000 USDC of principal backing 200,000 shares. Alice and the attacker each own half, so each position is worth approximately 104,000 USDC.
Alice earned the entire 8,000 USDC before the attacker joined, but receives only 4,000 USDC of it. The attacker captures the other 4,000 USDC merely by entering before payment.
The correct pre-deposit share price would have treated Alice's shares as a claim on 108,000 USDC. At that price, a 100,000 USDC deposit would mint about 92,592 shares instead of 100,000. After the 8,000 USDC payment, Alice would still own the full value she had earned before the attacker entered.
The example assumes enough revenue arrives to pay the full 8,000 USDC. A partial payment produces a smaller immediate extraction. Each future payment is still shared across the expanded supply. An attacker could size the deposit according to the expected payment instead of doubling the tranche.
More generally, if existing shares have principal value P, pending yield is Y, and the attacker deposits D at the understated price, the attacker's approximate share of the old yield is:
01captured yield = Y * D / (P + D)Ignoring rounding and entry or exit fees, the formula shows the main relationship. A deposit equal to existing principal captures about half of the pending payment. A deposit nine times larger captures about 90%. The attacker must temporarily supply capital and does not need to manipulate an oracle or create the yield.
The Attack
The attack works as follows:
Step 1: Existing senior LPs provide liquidity and hold senior vault shares.
Step 2: Time passes. Fixed senior yield accrues into unpaidSeniorYield, but the amount is not incorporated into seniorPrincipal or the senior vault's share price.
Step 3: The attacker observes an upcoming transaction that will bring revenue into the pool and trigger reconciliation. "Front-running" here means controlling transaction order around that public state change; it does not mean changing Plether's market price or breaking its trading-order mechanism.
Step 4: The attacker front-runs that transaction with a senior deposit. The vault mints shares using the understated pre-payment value.
Step 5: Revenue is reconciled. distributeRevenue() pays the old unpaidSeniorYield by increasing seniorPrincipal.
Step 6: The payment increases the value of every senior share, including the attacker's newly minted shares. Yield earned before the attacker joined is redistributed to them.
After satisfying the vault's withdrawal restrictions, the attacker can exit with their deposit plus the captured portion of the earlier LPs' yield. The larger the deposit relative to the existing senior tranche, the greater the dilution.
The cooldown increases the attacker's capital lock-up and market exposure. It cannot restore the original ownership after the old yield has increased principal across the enlarged share supply.
Why This Matters in Plether's Context
Plether presented the senior tranche as the more stable side of its LP waterfall. Its fixed return was compensation for providing long-lived capital, but the accounting did not preserve that return for the LPs who were exposed during the accrual period.
unpaidSeniorYield represented a senior claim with priority over future revenue. Deposits were priced without that claim, so an attacker could acquire part of it without bearing the time or risk that created it. Existing LPs lost yield and the attacker received an unearned gain.
The timing was visible on-chain. A searcher could see the transaction that realized or reconciled revenue and place a deposit before it. Profitability depended on the pending yield, expected payment, gas, cooldown, fees, and the risk of holding the senior position. Those costs might make a specific attempt unattractive, while the dilution bug would remain.
The Fix
The report recommended reworking the vault accounting system. Plether responded with a rewrite that removed the deferred unpaidSeniorYield liability and changed the senior return into a junior-funded coupon.
Under the revised model, elapsed coupon is transferred directly from existing junior principal to senior principal and is capped by the junior capital available:
01function paySeniorCoupon(02 WaterfallState memory state,03 uint256 seniorRateBps,04 uint256 elapsed05) internal pure returns (06 WaterfallState memory nextState,07 uint256 couponPaid08) {09 nextState = state;10 uint256 couponDue = calculateSeniorCoupon(11 state.seniorPrincipal,12 seniorRateBps,13 elapsed14 );15 16 if (couponDue == 0 || state.juniorPrincipal == 0) {17 return (nextState, 0);18 }19 20 couponPaid = couponDue < state.juniorPrincipal21 ? couponDue22 : state.juniorPrincipal;23 24 nextState.juniorPrincipal -= couponPaid;25 nextState.seniorPrincipal += couponPaid;26}Plether's implementation restores an impaired senior high-water mark before raising it. Funded coupon value enters seniorPrincipal at the checkpoint and reaches the senior share price before a later depositor can mint shares. Unfunded coupon shortfalls expire, leaving no deferred claim for future entrants to capture.
Plether also added minimum-size checks before deposit, mint, withdrawal, and redeem flows so dust operations could not cheaply force repeated coupon checkpoints.
The rewrite was delivered in Plether core PR #23. Since it changed the protocol's senior economics, the report called for a second audit.
A claim-inclusive exchange rate or a reward index tied to the share supply at each checkpoint could also prevent dilution. Both approaches would need consistent handling across totalAssets(), previews, deposits, mints, withdrawals, redemptions, impairment, and partial payments. Plether removed the deferred claim and assigned the funding source directly. Under the new economics, junior capital funds the coupon, payment is limited by available junior principal, and shortfalls expire.
Under the revised model, seniorRateBps defines a target coupon. Payment depends on available junior principal. Any missing portion expires instead of becoming future debt.
Takeaway
Vault share pricing must reflect every claim assigned to existing shareholders. Deferred rewards, pending fees, earned yield, and receivables can create the same vulnerability when new shares are minted before those values enter the exchange rate.
For every ERC-4626 vault with delayed reward settlement, ask three questions:
- When is yield economically earned?
- When does that yield enter
totalAssets()or the value used to price shares? - Can anyone deposit between those two events?
If the answers reveal a gap, new entrants may be able to buy old yield at no cost and dilute the users who actually earned it.
Auditors should test these state transitions:
- deposit immediately before and after a reward checkpoint;
- partial payment of a pending reward;
- deposit while the pool has no distributable cash;
- mint and deposit parity at the same block;
- withdrawal while rewards are accrued but unpaid;
- full tranche impairment followed by recapitalization; and
- repeated dust operations around checkpoint boundaries.
The invariant is more useful than any single test: a newly minted share must not receive value earned before that share existed unless the depositor paid for that value at entry.
About Us
At SC Audit Studio, we specialize in protocol security assessments. Our team has worked with companies including Aave, 1inch, and Li.Fi across EVM and non-EVM environments.
Contact SC Audit Studio for protocol security assessments.