Interacting With The Vault Using Hooks
The Balancer Router is typically the interface Externally Owned Accounts (EOAs) use to interact with the V3 Vault. While the Router uses Permit2 for token permissions, Hooks—being separate smart contracts—cannot sign these permissions. Instead, Hooks interact directly with the Vault. This section covers some common scenarios and usage patterns for Hooks.
Making A Swap
It is possible for a Hook to make a swap by following the following steps:
- Send the tokens you are swapping to the Vault:
token.transfer(_vault, amount)
- Inform the Vault you have sent it tokens. This will update the Vaults transient accounting with the correct balances:
_vault.settle(token, amount)
- Perform the swap:
(amountCalculated, amountIn, amountOut) = _vault.swap(
VaultSwapParams({
kind: kind,
pool: pool,
tokenIn: tokenIn,
tokenOut: tokenOut,
amountGivenRaw: amount,
limitRaw: limit,
userData: userData
})
);
AddingLiquidity - Donating To Pool LPs
Hooks can add/remove liquidity to pools. The following snippet shows how the DONATION
kind can be used to add collected fees to a pool. This effectively donates the fees to the pool LPs and can be seen in action in the ExitFeeHookExample.
_vault.addLiquidity(
AddLiquidityParams({
pool: pool,
to: msg.sender, // It would mint BPTs to router, but it's a donation so no BPT is minted
maxAmountsIn: accruedFees, // Donate all accrued fees back to the pool (i.e. to the LPs)
minBptAmountOut: 0, // Donation does not return BPTs, any number above 0 will revert
kind: AddLiquidityKind.DONATION,
userData: bytes("") // User data is not used by donation, so we can set it to an empty string
})
);
Collecting Fees
The Vault sendTo
function can be used to collect fees to a hook.
_vault.sendTo(feeToken, address(this), hookFee);
where address(this)
would be the hook itself.
This is used in the FeeTakingHookExample and the LotteryHookExample to collect a fee after swapping.