DeFi Security AllianceRequest an audit
Menu

EVM patterns

ERC4626 Vault Security: Rounding, Donations and Safe Integration

Protecting deposits requires an enforced execution bound as well as sound share accounting. ERC4626 vault security also extends to the oracle routes that turn shares into collateral: a rounding defense does not establish that an integrated lending market is safe.

Transparent vault holding large blue cubes beside a sieve dividing a cube into small units, illustrating ERC4626 vault security and share rounding.

Key facts

Original experiment
400000 constructed scenarios across 4 conversion models; 100000 cases each
Offset 0 result
90100 zero-share deposits despite 0 cases of positive attacker profit
Higher offsets
Offsets 3 and 6 produced no zero-share deposits within the tested grid
Measurement boundary
Integer model with one user per case; no EVM deployment or live vault tested
Reference implementation
OpenZeppelin Contracts v5.4.0 conversion equations, retrieved September 9, 2026

Identify the accounting and execution boundaries

A vault can satisfy an interface check while exposing a depositor to an unacceptable exchange rate. ERC4626 vault security concerns the accounting that converts assets into shares and the conditions under which those shares can be acquired or redeemed. Reviewing the function names does not establish either property. Read the implementation behind them, including any overrides inherited by the deployed contract.

Start with the meaning of totalAssets(). The standard describes managed assets. OpenZeppelin Contracts v5.4.0, the fixed reference used for our experiment, returns the underlying token balance held by the vault. Under that implementation, an unsolicited token transfer changes the conversion numerator without minting corresponding shares. A strategy vault may calculate managed assets differently. Its debt accounting, valuation updates or loss recognition then become part of the review.

Asset units
Amounts in the smallest indivisible unit of the underlying token. A deposit amount in contract arithmetic is not automatically a whole token.
Share units
Amounts in the vault token's own smallest unit. Additional share precision changes the integer representation used in conversion.
Conversion estimate
convertToShares and convertToAssets describe an idealized conversion. They exclude fees and cannot substitute for an execution limit.
Execution result
The assets actually received or shares actually minted or burned, under the state in which the transaction executes.

Keep the depositor's outcome separate from the attacker's outcome. Losing a deposit to rounding is a user failure even if the party that manipulated the price loses more. Conversely, a vault that preserves reasonable deposit rounding can still expose a lending market that values its shares through a manipulable exchange rate. These are different acceptance questions and need different evidence.

The review boundary should identify the underlying asset and the vault implementation address before any test result is attached to a deployment. Record the router that users are expected to use. A guard implemented only in that router does not describe direct calls to the vault, and a frontend estimate does not enforce the signed transaction's eventual result.

Original experiment across 400000 deposit scenarios

We enumerated a bounded integer model on . Its purpose was to compare zero-share deposits with positive attacker profit under the same inputs. These are constructed scenarios, not observations of deployed vaults. The comparison includes a naive conversion baseline and virtual-adjusted equations reproduced from the pinned OpenZeppelin source.

Method

Population
Every combination of donation amounts from 1 through 1000 asset units and user deposits from 1 through 100 asset units, for each of 4 models.
Sequence
The attacker deposits 1 asset unit into an empty vault, directly donates assets, waits for the modeled user deposit and redeems all attacker shares.
Denominator
100000 cases per model and 400000 overall. No case was excluded or returned an unknown result.
Models
Naive conversion without virtual balances, followed by virtual balances with decimal offsets 0, 3 and 6.
Accounting assumptions
Exact token transfers, no yield or fees, no minimum-share rejection and one user deposit per case. Attacker profit is the redemption payout minus the initial deposit and donation.
Evidence
The script seo/research/erc4626-vault-security-2026-09-09/rounding-survey.py, output rounding-survey.json and complete compressed case ledger rounding-cases.csv.gz are retained in that repository directory. They are not published download links.

For the virtual-adjusted model, let A be actual assets and S actual share supply before a deposit. The share result is the integer floor of assets * (S + 10**offset) / (A + 1). Redemption uses the inverse adjusted fraction and also rounds down. The baseline omits both virtual terms and initially mints one share unit for the attacker's one asset unit.

Results

Complete grid results measured on ; counts refer to constructed cases
Conversion modelCasesZero user sharesPositive attacker profit
Naive baseline1000009505098669
Virtual balances, offset 0100000901000
Virtual balances, offset 310000000
Virtual balances, offset 610000000

The offset-zero row separates the two outcomes sharply. No attacker redemption produced a positive profit in that row, yet zero-share deposits remained possible. Raising precision eliminated zero-share results within this grid. Neither result establishes how often a real user encounters the condition, because the grid is deliberately uniform over small integer amounts.

Boundary example from the grid: initial deposit 1, donation 1000 and user deposit 100, all in asset units
ModelUser share unitsAttacker payout in asset unitsAttacker profit in asset units
Naive baseline01101100
Virtual offset 00551-450
Virtual offset 3199501-500
Virtual offset 6199600501-500

Share counts across offsets use different precision, so compare them as integer conversion results, not as directly comparable amounts of wealth. In the offset-zero example, the user's deposit still receives no ownership claim. An enforced positive minimum-share requirement would reject that execution even though the modeled attacker loses money.

Limits

  • The script executes arithmetic, not Solidity or an EVM deployment. It does not exercise token transfers, reentrancy or inherited overrides.
  • Only one user follows each donation. Repeated deposits and alternative attacker sequences require their own model; no conclusion about them follows from these totals.
  • Gas and financing costs are omitted. Positive profit means positive asset balance change in the model before those costs.
  • Inputs outside the stated grid, strategy losses and share-oracle integrations were not tested. The selected offsets are comparison settings, not universal recommendations.

Rounding and slippage checks for each operation

Choose the execution bound from the quantity the user fixes. A deposit fixes assets spent, so its protection concerns shares received. A mint fixes shares received, so the corresponding protection concerns assets spent. Applying a minimum-output check to every method without considering its direction can leave the actual exposure unbounded.

Operation-level review matrix for synchronous vaults; rounding describes the pinned fee-free reference
OperationFixed inputPreview roundingExecution bound
depositAsset amountShares downMinimum shares received
mintShare amountAssets upMaximum assets spent
withdrawAsset amountShares upMaximum shares burned
redeemShare amountAssets downMinimum assets received

ERC-5143's slippage-protected overloads express these bounds as additional arguments. Check whether the specific vault or wrapper implements equivalent enforcement. The base interface alone does not supply those extra arguments. A wallet should make the user's intended bound part of the transaction path that will execute, with the correct receiver and asset denomination.

A preview is not a reservation. Reading a favorable result before signing leaves time for state to change before execution. Even a fresh preview inside a wrapper answers the current-state conversion question; comparing the result only against that fresh preview can accept a price already manipulated before the wrapper starts. The user's bound needs an independently chosen tolerance or reference, not automatic acceptance of whatever the vault currently reports.

Rejecting zero shares is only a minimum sanity check. A result that mints a positive share amount can still be far below the user's acceptable exchange. Derive the permitted result from the quoted amount and the user's tolerance, with a documented rounding rule for that bound itself. Test the exact boundary: an amount just outside the permitted result should fail, while the amount at the boundary should follow the specified policy. Otherwise, a nominal slippage field can conceal an off-by-one error or a default that effectively disables protection.

Test limits separately. A nonzero previewDeposit result does not establish that a receiver can deposit, because preview functions intentionally ignore limits checked through the corresponding maximum functions. Also verify the actual transaction outcome. The transaction simulation security guide explains why a preflight result must retain its state context and cannot guarantee a later execution.

Fees make the distinction more consequential. Conversion functions exclude fees, while operation previews include the relevant deposit or withdrawal fee. If an integration calculates its minimum from a fee-free estimate, a legitimate fee may cause an unexpected revert. If it silently relaxes the bound after that revert, it may accept an exchange the user never authorized. Present a new quote for renewed approval when the economic terms change.

Choose defenses for the state that can change

A useful defense review starts with the state variable the proposed control constrains. Virtual balances change conversion arithmetic. Internal accounting can prevent an unsolicited transfer from changing recognized managed assets. A transaction bound constrains the user's realized exchange. Each control has a different scope, so evidence for one cannot close the others.

Defense selection by mechanism and remaining review requirement
ControlMechanism addressedRemaining requirement
Virtual balances and share precisionEmpty-vault conversion and donation economicsTest the actual offset with minimum supported deposits
Internal managed-asset accountingUnsolicited transfers changing the recognized balanceReconcile legitimate gains and losses
Minimum output or maximum inputAn execution outside the user's toleranceEnforce the bound on every supported user route
Funded initializationLow initial supply and cheap conversion movementReview ownership and residual supply after withdrawals
Collateral valuation controlsShare-rate changes entering borrowing powerTrace every oracle route and extraction path

For minimum-deposit policy, begin with the asset units the application actually supports. Include the smallest accepted amount, values around a rounding boundary and the state after nearly all shares have been redeemed. A test suite that starts every case with a large seeded balance may never reach the condition the initialization policy is supposed to control. Record why the seed remains in place and who can remove it.

Internal accounting introduces its own reconciliation job. If unsolicited transfers are excluded, the system must still define how earned yield or realized losses enter the recognized balance. Rebasing assets and transfers that deliver less than the requested amount can break assumptions borrowed from an exact-transfer model. Either demonstrate their supported behavior or reject that asset type explicitly. Merely using a familiar token transfer helper does not supply the accounting policy.

Do not treat a larger offset as a substitute for checking integration precision. A downstream system may assume that vault shares use the same decimals as the underlying asset. Review displays and amount normalization as well as transaction encoding. A correct on-chain ratio can still be misrepresented by a consumer that rescales it incorrectly.

For customized vaults, inspect the transfer and share-accounting order around callbacks. The pinned reference transfers assets before minting shares and burns shares before transferring assets out. Overrides that call strategies or other tokens create additional intermediate states. Add those states to the protocol invariant specification with the balances and permissions that must remain valid during a callback.

Trace shares into collateral valuation

Share valuation creates an exposure beyond the depositor's entry price. A lending market may use a vault conversion to translate posted shares into collateral value. If that conversion moves after an unsolicited transfer, the next question is whether borrowing power moves with it. A deposit-level rounding defense does not answer that question.

Euler's documented donation scenario is explicitly hypothetical and conditional. Its own vault shares use internal cash accounting that breaks the direct-donation assumption described there. External share vaults and their oracle routes still require inspection. Preserve that distinction when reviewing an integration rather than generalizing a mechanism to every vault implementing the same interface.

Where a donated balance can become borrowing capacity A direct transfer reaches actual assets. Managed accounting decides whether it changes the conversion. The oracle route determines whether that conversion reaches collateral valuation, which is constrained by market limits. Actual assetsDirect transfer Managed assetsAccounting policy Share conversionOracle selection Borrowing powerCaps and liquidity Each connection needs evidence from the integrated contracts.
Follow the valuation path before treating an exchange-rate change as available credit.
  1. Identify which incoming asset movements change the vault's recognized managed balance. Distinguish ordinary deposits from transfers that mint no shares.
  2. Read the conversion used by the selected oracle, including wrappers and normalization. Do not infer its behavior from the oracle's display name.
  3. Follow that value into collateral recognition and the market's borrowing constraints, then establish whether available liquidity permits extraction.
  4. Include the donor's costs and retained share ownership when evaluating the full position. A displayed price increase alone does not establish profit.

The same map helps identify an accounting interruption. If assets temporarily leave a vault through a flash-lending path, determine whether the conversion sees that temporary balance and whether another consumer can read it before repayment. Euler's exchange-rate research also describes rounding remainders as a source of accumulated balance changes. Those paths require stateful tests; our single-donation experiment does not measure them.

Build the release evidence packet

Attach test results to the deployed behavior the integrator will use. A passing arithmetic model is useful evidence about its equations. Closing an implementation review requires the asset behavior and the execution routes too. Keep a failed test input intact so that a fix can be rerun against the same state.

  1. Freeze the implementation and dependency versions. List conversion overrides and every place that changes recognized assets without minting or burning shares.
  2. Run boundary deposits around empty and nearly empty states. Check both the user's minted shares and the attacker's complete asset balance change.
  3. Exercise each supported router and direct-call path with an unacceptable result. Confirm that the user-selected bound causes a revert and that a successful path reaches the intended receiver.
  4. Test recovery after losses or disabled withdrawals, retaining the maximum-function responses and actual transaction results. A preview amount alone does not establish redeemability.
  5. Document external consumers of the share price. Keep the collateral integration review separate from the vault's deposit-rounding results.

Use the failed case to distinguish an accounting correction from a rejected transaction. Preserve the asset balance before the donation, the recognized balance immediately before deposit and the share balance after execution. If the fix changes totalAssets(), explain why the new recognized balance reflects the intended accounting policy. If the fix adds a bound, show that the same input now reverts without leaving a partial deposit. Those outcomes close different findings.

The evidence packet should also distinguish an expected revert from a test that never reached its target. An exhausted allowance or insufficient account balance can stop the transaction before the slippage check executes. Arrange the preconditions so that the unacceptable conversion is the reason for rejection, then verify a neighboring acceptable transaction succeeds. Retain both receipts or local execution traces with their input amounts. A bare assertion that a call reverted cannot identify which guard protected it.

For an external engagement, use the security review firm directory to define the needed scope before selecting a reviewer. The Pharos Production profile provides a starting point for that firm's service coverage. Request explicit evidence for the vault implementation and its connected contracts rather than treating any company profile as evidence that a particular release has passed review.

Finally, identify whether the integration is synchronous at all. Under ERC-7540 asynchronous vault semantics, the preview methods for an asynchronous direction must revert. An adapter that treats every such revert as a broken ERC-4626 implementation can reject intended behavior or construct an invalid fallback quote. Review the request and claim lifecycle before reusing the synchronous matrix.

Frequently asked questions

Can a share price denominated in another asset be compared directly with this experiment?

No. The model uses one underlying asset and integer units of that asset. A price denominated in another currency adds a conversion path whose decimals and update assumptions need independent review.

Does an audit of the underlying strategy automatically cover a new vault wrapper?

No. Check whether the reviewed scope includes the wrapper and the exact integration version. New conversion overrides or authorization paths need their own evidence even when the strategy dependency already has a report.

Should a multisig batch use the vault caller as the final receiver?

Only if that is the intended ownership destination. Review the receiver and owner fields across the batch so that acquired shares and redeemed assets reach the intended account. An authorized transaction can still name the wrong recipient.

Comments

0

    Leave a comment

    Share a question or observation about this article.

    10 to 3,000 characters.