Base

This document is better viewed on the docs page.

Base contracts for building secure and modular Uniswap hooks, providing core functionality and common patterns for hook development.

  • BaseCustomAccounting: Base hook implementation for custom accounting, including support for swaps and liquidity management.

  • BaseCustomCurve: Base hook implementation for custom curves.

  • BaseHook: Base implementation for hooks.

  • BaseAsyncSwap: Base hook implementation for asynchronous swaps.

Hooks

BaseCustomAccounting

import "uniswap-hooks/src/base/BaseCustomAccounting.sol";

Base implementation for custom accounting and hook-owned liquidity.

To enable hook-owned liquidity, tokens must be deposited via the hook to allow control and flexibility over the liquidity. The implementation inheriting this hook must implement the respective functions to calculate the liquidity modification parameters and the amount of liquidity shares to mint or burn.

Additionally, the implementer must consider that the hook is the sole owner of the liquidity and manage fees over liquidity shares accordingly.

This base hook is designed to work with a single pool key. If you want to use the same custom accounting hook for multiple pools, you must have multiple storage instances of this contract and initialize them via the PoolManager with their respective pool keys.
This is experimental software and is provided on an "as is" and "as available" basis. We do not give any warranties and will not be liable for any losses incurred through any use of this code base.

Available since v0.1.0

Modifiers

ensure(uint256 deadline) modifier

Ensure the deadline of a liquidity modification request is not expired.

constructor(contract IPoolManager _poolManager) internal

Set the pool PoolManager address.

addLiquidity(struct BaseCustomAccounting.AddLiquidityParams params) → BalanceDelta delta external

To cover all possible scenarios, msg.sender should have already given the hook an allowance of at least amount0Desired/amount1Desired on token0/token1. Always adds assets at the ideal ratio, according to the price when the transaction is executed.

The amount0Min and amount1Min parameters are relative to the principal delta, which excludes fees accrued from the liquidity modification delta.

removeLiquidity(struct BaseCustomAccounting.RemoveLiquidityParams params) → BalanceDelta delta external

_modifyLiquidity(bytes params) → BalanceDelta callerDelta, BalanceDelta feesAccrued internal

Calls the PoolManager to unlock and call back the hook’s unlockCallback function.

unlockCallback(bytes rawData) → bytes returnData external

Callback from the PoolManager when liquidity is modified, either adding or removing.

_handleAccruedFees(struct BaseCustomAccounting.CallbackData data, BalanceDelta callerDelta, BalanceDelta feesAccrued) internal

Handle any fees accrued in a liquidity position. By default, this function transfers the tokens to the owner of the liquidity position. However, this function can be overriden to take fees accrued in the position, or any other desired logic.

_beforeInitialize(address, struct PoolKey key, uint160) → bytes4 internal

Initialize the hook’s pool key. The stored key should act immutably so that it can safely be used across the hook’s functions.

_beforeAddLiquidity(address, struct PoolKey, struct IPoolManager.ModifyLiquidityParams, bytes) → bytes4 internal

Revert when liquidity is attempted to be added via the PoolManager.

_beforeRemoveLiquidity(address, struct PoolKey, struct IPoolManager.ModifyLiquidityParams, bytes) → bytes4 internal

Revert when liquidity is attempted to be removed via the PoolManager.

_getAddLiquidity(uint160 sqrtPriceX96, struct BaseCustomAccounting.AddLiquidityParams params) → bytes modify, uint256 shares internal

Get the liquidity modification to apply for a given liquidity addition, and the amount of liquidity shares would be minted to the sender.

_getRemoveLiquidity(struct BaseCustomAccounting.RemoveLiquidityParams params) → bytes modify, uint256 shares internal

Get the liquidity modification to apply for a given liquidity removal, and the amount of liquidity shares would be burned from the sender.

_mint(struct BaseCustomAccounting.AddLiquidityParams params, BalanceDelta callerDelta, BalanceDelta feesAccrued, uint256 shares) internal

Mint liquidity shares to the sender.

_burn(struct BaseCustomAccounting.RemoveLiquidityParams params, BalanceDelta callerDelta, BalanceDelta feesAccrued, uint256 shares) internal

Burn liquidity shares from the sender.

getHookPermissions() → struct Hooks.Permissions permissions public

Set the hook permissions, specifically beforeInitialize, beforeAddLiquidity and beforeRemoveLiquidity.

poolKey() → struct PoolKey public

ExpiredPastDeadline() error

A liquidity modification order was attempted to be executed after the deadline.

PoolNotInitialized() error

Pool was not initialized.

TooMuchSlippage() error

Principal delta of liquidity modification resulted in too much slippage.

LiquidityOnlyViaHook() error

Liquidity was attempted to be added or removed via the PoolManager instead of the hook.

InvalidNativeValue() error

Native currency was not sent with the correct amount.

AlreadyInitialized() error

Hook was already initialized.

BaseCustomCurve

import "uniswap-hooks/src/base/BaseCustomCurve.sol";

Base implementation for custom curves, inheriting from BaseCustomAccounting.

This hook allows to implement a custom curve (or any logic) for swaps, which overrides the default v3-like concentrated liquidity implementation of the PoolManager. During a swap, the hook calls the _getUnspecifiedAmount function to get the amount of tokens to be sent to the receiver. The return delta created from this calculation is then consumed and applied by the PoolManager.

This hook by default does not include fee or salt mechanisms, which can be implemented by inheriting contracts if needed.
This is experimental software and is provided on an "as is" and "as available" basis. We do not give any warranties and will not be liable for any losses incurred through any use of this code base.

Available since v0.1.0

constructor(contract IPoolManager _poolManager) internal

Set the pool PoolManager address.

_getAddLiquidity(uint160, struct BaseCustomAccounting.AddLiquidityParams params) → bytes, uint256 internal

Defines how the liquidity modification data is encoded and returned for an add liquidity request.

_getRemoveLiquidity(struct BaseCustomAccounting.RemoveLiquidityParams params) → bytes, uint256 internal

Defines how the liquidity modification data is encoded and returned for a remove liquidity request.

_beforeSwap(address sender, struct PoolKey key, struct IPoolManager.SwapParams params, bytes) → bytes4, BeforeSwapDelta, uint24 internal

Overides the default swap logic of the PoolManager and calls the _getUnspecifiedAmount to get the amount of tokens to be sent to the receiver.

In order to take and settle tokens from the pool, the hook must hold the liquidity added via the addLiquidity function.

_modifyLiquidity(bytes params) → BalanceDelta callerDelta, BalanceDelta feesAccrued internal

Overides the custom accounting logic to support the custom curve integer amounts.

unlockCallback(bytes rawData) → bytes returnData external

Decodes the callback data and applies the liquidity modifications, overriding the custom accounting logic to mint and burn ERC-6909 claim tokens which are used in swaps.

_getUnspecifiedAmount(struct IPoolManager.SwapParams params) → uint256 unspecifiedAmount internal

Calculate the amount of the unspecified currency to be taken or settled from the swapper, depending on the swap direction and the fee amount to be paid to LPs.

_getSwapFeeAmount(struct IPoolManager.SwapParams params, uint256 unspecifiedAmount) → uint256 swapFeeAmount internal

Calculate the amount of fees to be paid to LPs in a swap.

_getAmountOut(struct BaseCustomAccounting.RemoveLiquidityParams params) → uint256 amount0, uint256 amount1, uint256 shares internal

Calculate the amount of tokens to use and liquidity shares to burn for a remove liquidity request.

_getAmountIn(struct BaseCustomAccounting.AddLiquidityParams params) → uint256 amount0, uint256 amount1, uint256 shares internal

Calculate the amount of tokens to use and liquidity shares to mint for an add liquidity request.

getHookPermissions() → struct Hooks.Permissions permissions public

Set the hook permissions, specifically beforeInitialize, beforeAddLiquidity, beforeRemoveLiquidity, beforeSwap, and beforeSwapReturnDelta

BaseHook

import "uniswap-hooks/src/base/BaseHook.sol";

Base hook implementation.

This contract defines all hook entry points, as well as security and permission helpers. Based on the Uniswap v4 periphery implementation.

Hook entry points must be overiden and implemented by the inheriting hook to be used. Their respective flags must be set to true in the getHookPermissions function as well.
This is experimental software and is provided on an "as is" and "as available" basis. We do not give any warranties and will not be liable for any losses incurred through any use of this code base.

Available since v0.1.0

onlyPoolManager() modifier

onlySelf() modifier

Restrict the function to only be callable by the hook itself.

onlyValidPools(contract IHooks hooks) modifier

Restrict the function to only be called for a valid pool.

constructor(contract IPoolManager _poolManager) internal

Set the pool manager and check that the hook address matches the expected permissions and flags.

getHookPermissions() → struct Hooks.Permissions permissions public

Get the hook permissions to signal which hook functions are to be implemented.

Used at deployment to validate the address correctly represents the expected permissions.

validateHookAddress(contract BaseHook hook) internal

Validate the hook address against the expected permissions.

beforeInitialize(address sender, struct PoolKey key, uint160 sqrtPriceX96) → bytes4 external

_beforeInitialize(address, struct PoolKey, uint160) → bytes4 internal

Hook implementation for beforeInitialize, to be overriden by the inheriting hook. The flag must be set to true in the getHookPermissions function.

afterInitialize(address sender, struct PoolKey key, uint160 sqrtPriceX96, int24 tick) → bytes4 external

_afterInitialize(address, struct PoolKey, uint160, int24) → bytes4 internal

Hook implementation for afterInitialize, to be overriden by the inheriting hook. The flag must be set to true in the getHookPermissions function.

beforeAddLiquidity(address sender, struct PoolKey key, struct IPoolManager.ModifyLiquidityParams params, bytes hookData) → bytes4 external

_beforeAddLiquidity(address, struct PoolKey, struct IPoolManager.ModifyLiquidityParams, bytes) → bytes4 internal

Hook implementation for beforeAddLiquidity, to be overriden by the inheriting hook. The flag must be set to true in the getHookPermissions function.

beforeRemoveLiquidity(address sender, struct PoolKey key, struct IPoolManager.ModifyLiquidityParams params, bytes hookData) → bytes4 external

_beforeRemoveLiquidity(address, struct PoolKey, struct IPoolManager.ModifyLiquidityParams, bytes) → bytes4 internal

Hook implementation for beforeRemoveLiquidity, to be overriden by the inheriting hook. The flag must be set to true in the getHookPermissions function.

afterAddLiquidity(address sender, struct PoolKey key, struct IPoolManager.ModifyLiquidityParams params, BalanceDelta delta0, BalanceDelta delta1, bytes hookData) → bytes4, BalanceDelta external

_afterAddLiquidity(address, struct PoolKey, struct IPoolManager.ModifyLiquidityParams, BalanceDelta, BalanceDelta, bytes) → bytes4, BalanceDelta internal

Hook implementation for afterAddLiquidity, to be overriden by the inheriting hook. The flag must be set to true in the getHookPermissions function.

afterRemoveLiquidity(address sender, struct PoolKey key, struct IPoolManager.ModifyLiquidityParams params, BalanceDelta delta0, BalanceDelta delta1, bytes hookData) → bytes4, BalanceDelta external

_afterRemoveLiquidity(address, struct PoolKey, struct IPoolManager.ModifyLiquidityParams, BalanceDelta, BalanceDelta, bytes) → bytes4, BalanceDelta internal

Hook implementation for afterRemoveLiquidity, to be overriden by the inheriting hook. The flag must be set to true in the getHookPermissions function.

beforeSwap(address sender, struct PoolKey key, struct IPoolManager.SwapParams params, bytes hookData) → bytes4, BeforeSwapDelta, uint24 external

_beforeSwap(address, struct PoolKey, struct IPoolManager.SwapParams, bytes) → bytes4, BeforeSwapDelta, uint24 internal

Hook implementation for beforeSwap, to be overriden by the inheriting hook. The flag must be set to true in the getHookPermissions function.

afterSwap(address sender, struct PoolKey key, struct IPoolManager.SwapParams params, BalanceDelta delta, bytes hookData) → bytes4, int128 external

_afterSwap(address, struct PoolKey, struct IPoolManager.SwapParams, BalanceDelta, bytes) → bytes4, int128 internal

Hook implementation for afterSwap, to be overriden by the inheriting hook. The flag must be set to true in the getHookPermissions function.

beforeDonate(address sender, struct PoolKey key, uint256 amount0, uint256 amount1, bytes hookData) → bytes4 external

_beforeDonate(address, struct PoolKey, uint256, uint256, bytes) → bytes4 internal

Hook implementation for beforeDonate, to be overriden by the inheriting hook. The flag must be set to true in the getHookPermissions function.

afterDonate(address sender, struct PoolKey key, uint256 amount0, uint256 amount1, bytes hookData) → bytes4 external

_afterDonate(address, struct PoolKey, uint256, uint256, bytes) → bytes4 internal

Hook implementation for afterDonate, to be overriden by the inheriting hook. The flag must be set to true in the getHookPermissions function.

poolManager() → contract IPoolManager public

NotSelf() error

The hook is not the caller.

InvalidPool() error

The pool is not authorized to use this hook.

HookNotImplemented() error

The hook function is not implemented.

NotPoolManager() error

BaseAsyncSwap

import "uniswap-hooks/src/base/BaseAsyncSwap.sol";

Base implementation for async swaps, which skip the v3-like swap implementation of the PoolManager by taking the full swap input amount and returning a delta that nets out the specified amount to 0.

This base hook allows developers to implement arbitrary logic to handle swaps, including use-cases like asynchronous swaps and custom swap-ordering. However, given this flexibility, developers should ensure that any logic implemented interacts safely with the PoolManager and works correctly.

In order to handle async swaps, the hook mints ERC-6909 claim tokens for the specified currency and amount. Inheriting contracts are free to handle these claim tokens as necessary, which can be redeemed for the underlying currency by using the settle function from the CurrencySettler library.

If the hook is used for multiple pools, the ERC-6909 tokens must be separated and managed independently for each pool in order to prevent draining of ERC-6909 tokens from one pool to another.
The hook only supports async exact-input swaps. Exact-output swaps will be processed normally by the PoolManager.
This is experimental software and is provided on an "as is" and "as available" basis. We do not give any warranties and will not be liable for any losses incurred through any use of this code base.

Available since v0.1.0

constructor(contract IPoolManager _poolManager) internal

Set the PoolManager address.

_beforeSwap(address sender, struct PoolKey key, struct IPoolManager.SwapParams params, bytes) → bytes4, BeforeSwapDelta, uint24 internal

Skip the v3-like swap implementation of the PoolManager by returning a delta that nets out the specified amount to 0 to enable asynchronous swaps.

_calculateSwapFee(struct PoolKey key, uint256 specifiedAmount) → uint256 feeAmount internal

Calculate the fee amount for the swap.

getHookPermissions() → struct Hooks.Permissions permissions public

Set the hook permissions, specifically beforeSwap and beforeSwapReturnDelta.