Swap Mechanics
Learn how ERC4626WrapperHook performs the wrap and unwrap paths in beforeSwap and how it accounts for them with a BeforeSwapDelta.
All conversion happens in _beforeSwap. The pool's own swap math is never consulted; the returned BeforeSwapDelta fully specifies the token movement.
Direction Detection
isExactInput = params.amountSpecified < 0
wrapping = (wrapZeroForOne == params.zeroForOne)A wrapping swap runs _deposit; otherwise it runs _withdraw. Exact output is rejected before either runs when the subclass does not support it (see Exact Input and Exact Output).
Wrap Path (Deposit)
function _deposit(uint256 underlyingAmount)
internal
returns (uint256 actualUnderlyingAmount, uint256 wrappedAmount);- Take the input.
_take(underlyingCurrency, this, underlyingAmount)pulls the underlying out of thePoolManagerinto the hook. The received amount is measured by balance delta (actualUnderlyingAmount), so a rebasing underlying that delivers slightly less is handled correctly. - Deposit to the vault, minting to the pool. After
poolManager.sync(wrapperCurrency),vault.deposit(actualUnderlyingAmount, address(poolManager))mints shares directly to thePoolManager, avoiding an extra transfer. The minted amount is measured by balance delta (wrappedAmount). - Settle the shares.
poolManager.settle()registers the minted shares as paid into the pool. The settled amount must equalwrappedAmount, or the call reverts withÂSettlementMismatch.
The hook holds a standing unlimited allowance of the underlying to the vault, granted in the constructor, so vault.deposit can pull the underlying from the hook.
Unwrap Path (Withdraw)
function _withdraw(uint256 wrappedAmount)
internal
returns (uint256 actualWrappedAmount, uint256 underlyingAmount);- Take the shares.
_take(wrapperCurrency, this, wrappedAmount)pulls the shares out of thePoolManagerinto the hook. Shares do not rebase, soactualWrappedAmount ==Â wrappedAmount. - Source the underlying.
_redeemToUnderlying(wrappedAmount)redeems the shares into the hook and returns the underlying received, measured by balance delta. In the base hook this is a plainvault.redeem(wrappedAmount, this, this). This is the single overridable seam a subclass uses to change how the underlying is sourced (see Extending the Hook). - Settle the underlying. After
poolManager.sync(underlyingCurrency), the hook transfers the received underlying to thePoolManagerand callspoolManager.settle(). The settled amount is measured by balance delta and must equal what thePoolManagerreceived, or the call reverts withSettlementMismatch. Sending the full received balance and lettingsettlemeasure it tolerates a rebasing underlying that rounds the transfer down.
BeforeSwapDelta Accounting
beforeSwap returns toBeforeSwapDelta(specified, unspecified):
- The specified delta offsets the swap's
amountSpecifiedexactly, so the pool's core swap math nets to zero and the hook fully owns the trade. - The unspecified delta is the output the hook produced: the shares minted (wrap) or the underlying redeemed (unwrap).
For an exact-input swap the specified side is the input the caller provided and the unspecified side is the conversion output. The PoolManager resolves these deltas against the caller when the unlock closes: the caller settles the input and takes the output.