Skip to content

ReHypothecationHook: add slippage protection to addReHypothecatedLiquidity and removeReHypothecatedLiquidity #122

Description

@yanisepfl

Description

The addReHypothecatedLiquidity and removeReHypothecatedLiquidity methods accept only a shares parameter and have no slippage protection. This exposes users to sandwich attacks and unfavorable price movements.

When totalSupply() == 0, _sharesToAmounts reads the current pool price via getSlot0 to determine the deposit ratio. When totalSupply() > 0, amounts are computed proportionally from yield source balances, which also fluctuate. In both cases, the amounts a user actually transfers can differ significantly from what they expected at the time of signing the transaction.

A malicious actor can sandwich a deposit or withdrawal by manipulating the pool price or yield source state before the victim's transaction, causing the user to deposit more tokens (or receive fewer) than intended.

Insufficient Warning

The contract acknowledges slippage risk in a WARNING comment:

WARNING: Liquidity additions and removals may be affected by slippage. Users can protect against unexpected slippage in general by verifying the amount received is as expected, using a wrapper that performs these checks.

This suggests users should verify amounts via an external wrapper. However, the slippage does not occur in the yield source interaction but earlier, in _sharesToAmounts, which converts shares into amount0 and amount1:

  • When totalSupply() == 0, the conversion reads the current pool price via getSlot0. An attacker can manipulate the pool price before the victim's transaction, causing a skewed deposit ratio (e.g., far more ETH than expected relative to USDC).
  • When totalSupply() > 0, the conversion uses _getAmountInYieldSource for each currency. These balances shift whenever _afterSwap_resolveHookDelta rebalances tokens between the pool and yield sources, so a swap landing right before the victim's transaction changes the ratio.

By the time _depositToYieldSource or _withdrawFromYieldSource is called, the amounts are already determined — the user has committed to a potentially unfavorable ratio within the same atomic call. There is no external hook point between the share-to-amount conversion and the token transfer where a wrapper could intercept and revert.

Slippage protection should be enforced at the contract level with minimal overhead, not deferred to implementors.

Suggested fix

Add slippage parameters to both functions. Two possible approaches:

Option A — Amount-based (simpler)

function addReHypothecatedLiquidity(
    uint256 shares,
    uint256 amount0Max,
    uint256 amount1Max
) ...

function removeReHypothecatedLiquidity(
    uint256 shares,
    uint256 amount0Min,
    uint256 amount1Min
) ...

Check that the computed amounts are within the user's bounds before executing transfers. This is consistent with standard Uniswap V4 periphery patterns.

Option B — Price-based

function addReHypothecatedLiquidity(
    uint256 shares,
    uint160 expectedSqrtPriceX96,
    uint24 maxPriceSlippage
) ...

Check that the current pool price hasn't deviated beyond the user's tolerance before any state changes. This protects against sandwich attacks at the source.

Either approach should revert before any token transfers or share minting/burning if the slippage check fails.

Context

We built a production rehypothecation hook (Alphix) on top of this pattern and encountered this exact issue. We implemented price-based slippage protection with full test coverage including sandwich attack scenarios. Happy to contribute a PR if the team has a preferred approach.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions