
Code4rena · S-277 · Mar 2025
// Lack of Slippage Protection in Core EIP-4626 Vault Functions Enables Sandwich Attacks
EIP-4626 explicitly states in its Security Considerations that vaults intended for direct EOA interaction must include slippage protection (minShares for deposits, maxAssets for withdrawals). SiloVault omits these parameters entirely, leaving exchange-rate risk with the user at every interaction.
// No minShares → user receives fewer shares than expected
function deposit(uint256 _assets, address _receiver)
public override returns (uint256 shares) { ... }
// No maxShares → user pays more assets than expected
function mint(uint256 _shares, address _receiver)
public virtual override returns (uint256 assets) { ... }
// No maxShares → user burns more shares than expected
function withdraw(uint256 _assets, address _receiver, address _owner)
public virtual override returns (uint256 shares) { ... }
// No minAssets → user receives fewer assets than expected
function redeem(uint256 _shares, address _receiver, address _owner)
public virtual override returns (uint256 assets) { ... }
// EIP-4626 Security Considerations (violated):
// "implementors intend to support EOA account access directly,
// they should consider adding [...] means to accommodate
// slippage loss or unexpected deposit/withdrawal limits"▸ INCORRECT ASSUMPTIONS
▸ AFFECTED CONTRACTS
Alice submits a large deposit() transaction to SiloVault. No minShares parameter — she will accept any share amount.
↓Attacker detects the pending tx and frontruns it by manipulating an underlying market (e.g. Aave or Uniswap) that affects SiloVault's previewDeposit() rate.
↓Alice's deposit executes at the manipulated rate — she receives fewer shares than the fair-value amount. With no minShares check, the transaction succeeds silently.
↓Attacker backruns to restore the original rate, capturing the spread from Alice's slippage as profit.
Users receive fewer shares on deposit (or fewer assets on redeem) than fair value. Losses are unbounded and scale with vault TVL and attacker capital.
All four core vault functions are sandwichable in the same block. Any EOA transaction is an open target with no deadline or slippage floor.
The vault violates the EIP's own Security Considerations, creating false trust assumptions for integrators who rely on the standard for safety properties.
Add minimum-output parameters to all four core functions and enforce deadline validation per EIP-4626 Security Considerations.
function deposit(uint256 _assets, address _receiver)
public override returns (uint256 shares) {
shares = previewDeposit(_assets); // no slippage bound
_deposit(msg.sender, _receiver, _assets, shares);
}function deposit(
uint256 _assets,
address _receiver,
uint256 _minSharesOut, // slippage floor
uint256 _deadline // MEV protection
) public override returns (uint256 shares) {
require(block.timestamp <= _deadline, "EXPIRED");
shares = previewDeposit(_assets);
require(shares >= _minSharesOut, "SLIPPAGE");
_deposit(msg.sender, _receiver, _assets, shares);
}▸ SECURITY CONSIDERATIONS
My EIP-4626 audit checklist always starts with the Security Considerations section of the EIP itself — not the interface. The spec is unusually explicit about slippage for EOA interactions. If a vault targets direct EOA use and lacks minShares/maxAssets, that's a gap regardless of how clean the accounting logic is.
SiloVault had perfectly correct EIP-4626 accounting — the math, the rounding, the share calculation were all fine. The issue was at the user-interaction layer, not the core logic. These bugs are easy to miss precisely because the implementation looks correct when you're focused on the accounting invariants.
The interesting attack surface is the combination of a shared underlying market and the vault's exposure to rate changes. The sandwich doesn't manipulate the vault directly — it temporarily shifts external rates that previewDeposit() uses for share calculation. The vault is a passive victim.
This reinforced treating EIPs as security checklists, not just interface contracts. The Security Considerations section of an EIP encodes the lessons of past exploits. Skipping it to focus only on the function signatures misses the most actionable guidance in the entire document.