From 506dde5e492c6f5b29c49386eeec46c487ad4bd6 Mon Sep 17 00:00:00 2001 From: ant <0xdevant@gmail.com> Date: Fri, 20 Sep 2024 15:10:32 +0800 Subject: [PATCH] v4 guides: hook liquidity (#767) --- .../v4/guides/04-hooks/liquidity.mdx | 205 ++++++++++++++++++ 1 file changed, 205 insertions(+) create mode 100644 docs/contracts/v4/guides/04-hooks/liquidity.mdx diff --git a/docs/contracts/v4/guides/04-hooks/liquidity.mdx b/docs/contracts/v4/guides/04-hooks/liquidity.mdx new file mode 100644 index 000000000..00a527343 --- /dev/null +++ b/docs/contracts/v4/guides/04-hooks/liquidity.mdx @@ -0,0 +1,205 @@ +--- +title: Liquidity Hooks +--- + +This guide will walk through on an example of adding and removing liquidity. There are four hook functions available to customize and extend these behavior: + +- `beforeAddLiquidity` +- `afterAddLiquidity` +- `beforeRemoveLiquidity` +- `afterRemoveLiquidity` + +As the names suggest `beforeAddLiquidity`/`afterAddLiquidity` are functions called before or after liquidity is added to a pool. +Similarly `beforeRemoveLiquidity`/`afterRemoveLiquidity` are functions called before or after liquidity is removed from a pool. + +This guide will go through the parameters and examples specifically for `beforeAddLiquidity` and `beforeRemoveLiquidity`. + +Note: The liquidity examples are not production ready code, and are implemented in a simplistic manner for the purpose of learning. + +## Set Up the Contract + +Declare the solidity version used to compile the contract, since transient storage is used the solidity version will be `>=0.8.24`. + +```solidity +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.24; +``` + +Import the relevant dependencies from `v4-core` and `v4-periphery`: + +```solidity +import {BaseHook} from "v4-periphery/src/base/hooks/BaseHook.sol"; + +import {Hooks} from "v4-core/src/libraries/Hooks.sol"; +import {IPoolManager} from "v4-core/src/interfaces/IPoolManager.sol"; +import {PoolKey} from "v4-core/src/types/PoolKey.sol"; +import {PoolId, PoolIdLibrary} from "v4-core/src/types/PoolId.sol"; +import {BalanceDelta} from "v4-core/src/types/BalanceDelta.sol"; +import {BeforeSwapDelta, BeforeSwapDeltaLibrary} from "v4-core/src/types/BeforeSwapDelta.sol"; +``` + +Create a contract called LiquidityHook, use `PoolIdLibrary` to attach functions of computing ID of a pool to `PoolKey`. Declare two mappings to act as counters when calling `beforeAddLiquidity` and `beforeRemoveLiquidity`. + +```solidity +contract LiquidityHook is BaseHook { + using PoolIdLibrary for PoolKey; + + // NOTE: --------------------------------------------------------- + // state variables should typically be unique to a pool + // a single hook contract should be able to service multiple pools + // --------------------------------------------------------------- + + mapping(PoolId => uint256 count) public beforeAddLiquidityCount; + mapping(PoolId => uint256 count) public beforeRemoveLiquidityCount; + + constructor(IPoolManager _poolManager) BaseHook(_poolManager) {} +``` + +Override `getHookPermissions` from `BaseHook.sol` to return a struct of permissions to signal which hook functions are to be implemented. +It will also be used at deployment to validate the address correctly represents the expected permissions. + +```solidity +function getHookPermissions() public pure override returns (Hooks.Permissions memory) { + return Hooks.Permissions({ + beforeInitialize: false, + afterInitialize: false, + beforeAddLiquidity: true, + afterAddLiquidity: false, + beforeRemoveLiquidity: true, + afterRemoveLiquidity: false, + beforeSwap: false, + afterSwap: false, + beforeDonate: false, + afterDonate: false, + beforeAddLiquidityReturnDelta: false, + afterSwapReturnDelta: false, + afterAddLiquidityReturnDelta: false, + afterRemoveLiquidityReturnDelta: false + }); +} +``` + +## beforeAddLiquidity + +Here the example shows that every time __before__ liquidity is added to a pool, `beforeAddLiquidityCount` for that pool will be incremented by one. + +```solidity +function beforeAddLiquidity( + address, + PoolKey calldata key, + IPoolManager.ModifyLiquidityParams calldata, + bytes calldata +) external override returns (bytes4) { + beforeAddLiquidityCount[key.toId()]++; + return BaseHook.beforeAddLiquidity.selector; +} +``` + +### `beforeAddLiquidity` Parameters + +When triggering the `beforeAddLiquidity` hook function, there are some parameters we can make use of to customize or extend the behavior of `modifyLiquidity`. These parameters are described in `beforeAddLiquidity` from [`IHooks.sol`](https://github.com/Uniswap/v4-core/blob/main/src/interfaces/IHooks.sol#L47). + +A brief overview of the parameters: +- `sender` The initial `msg.sender` for the add liquidity call +- `key` The key for the pool +- `params` The parameters for adding liquidity i.e. `ModifyLiquidityParams` from [`IPoolManager.sol`](https://github.com/Uniswap/v4-core/blob/main/src/interfaces/IPoolManager.sol#L125C12-L125C33) +- `hookData` Arbitrary data handed into the `PoolManager` by the liquidity provider to be be passed on to the hook + +## beforeRemoveLiquidity + +Similiar as above, every time __before__ liquidity is removed from a pool, `beforeRemoveLiquidityCount` for that pool will be incremented by one. + +```solidity +function beforeRemoveLiquidity( + address, + PoolKey calldata key, + IPoolManager.ModifyLiquidityParams calldata, + bytes calldata +) external override returns (bytes4) { + beforeRemoveLiquidityCount[key.toId()]++; + return BaseHook.beforeRemoveLiquidity.selector; +} +``` + +### `beforeRemoveLiquidity` Parameters + +When triggering the `beforeRemoveLiquidity` hook function, there are some parameters we can make use of to customize or extend the behavior of `modifyLiquidity`. These parameters are described in `beforeRemoveLiquidity` from [`IHooks.sol`](https://github.com/Uniswap/v4-core/blob/main/src/interfaces/IHooks.sol#L78). + +A brief overview of the parameters: +- `sender` The initial msg.sender for the remove liquidity call +- `key` The key for the pool +- `params` The parameters for removing liquidity i.e. `ModifyLiquidityParams` from [`IPoolManager.sol`](https://github.com/Uniswap/v4-core/blob/main/src/interfaces/IPoolManager.sol#L125C12-L125C33) +- `hookData` Arbitrary data handed into the `PoolManager` by the liquidity provider to be be passed on to the hook + +## A Complete Liquidity Hook Contract + +```solidity +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.24; + +import {BaseHook} from "v4-periphery/src/base/hooks/BaseHook.sol"; + +import {Hooks} from "v4-core/src/libraries/Hooks.sol"; +import {IPoolManager} from "v4-core/src/interfaces/IPoolManager.sol"; +import {PoolKey} from "v4-core/src/types/PoolKey.sol"; +import {PoolId, PoolIdLibrary} from "v4-core/src/types/PoolId.sol"; +import {BalanceDelta} from "v4-core/src/types/BalanceDelta.sol"; +import {BeforeSwapDelta, BeforeSwapDeltaLibrary} from "v4-core/src/types/BeforeSwapDelta.sol"; + +contract LiquidityHook is BaseHook { + using PoolIdLibrary for PoolKey; + + // NOTE: --------------------------------------------------------- + // state variables should typically be unique to a pool + // a single hook contract should be able to service multiple pools + // --------------------------------------------------------------- + + mapping(PoolId => uint256 count) public beforeAddLiquidityCount; + mapping(PoolId => uint256 count) public beforeRemoveLiquidityCount; + + constructor(IPoolManager _poolManager) BaseHook(_poolManager) {} + + function getHookPermissions() public pure override returns (Hooks.Permissions memory) { + return Hooks.Permissions({ + beforeInitialize: false, + afterInitialize: false, + beforeAddLiquidity: true, + afterAddLiquidity: false, + beforeRemoveLiquidity: true, + afterRemoveLiquidity: false, + beforeSwap: false, + afterSwap: false, + beforeDonate: false, + afterDonate: false, + beforeAddLiquidityReturnDelta: false, + afterSwapReturnDelta: false, + afterAddLiquidityReturnDelta: false, + afterRemoveLiquidityReturnDelta: false + }); + } + + // ----------------------------------------------- + // NOTE: see IHooks.sol for function documentation + // ----------------------------------------------- + + function beforeAddLiquidity( + address, + PoolKey calldata key, + IPoolManager.ModifyLiquidityParams calldata, + bytes calldata + ) external override returns (bytes4) { + beforeAddLiquidityCount[key.toId()]++; + return BaseHook.beforeAddLiquidity.selector; + } + + function beforeRemoveLiquidity( + address, + PoolKey calldata key, + IPoolManager.ModifyLiquidityParams calldata, + bytes calldata + ) external override returns (bytes4) { + beforeRemoveLiquidityCount[key.toId()]++; + return BaseHook.beforeRemoveLiquidity.selector; + } +} +``` \ No newline at end of file