Hooks

API reference for the Lotus hooks system covering ILotusHooks interface, permission flags, AllowlistHook, and BorrowHealthGuardHook parameters.

The hooks system consists of the ILotusHooks interface (which hook contracts implement), the Hooks.sol library (which the core protocol uses to manage and invoke hooks), and two production implementations: AllowlistHook and BorrowHealthGuardHook.

ILotusHooks Interface

All hooks are invoked via staticcall. Each method must return its own function selector (bytes4) on success. Any revert causes the parent operation to revert.

triangle-exclamation

afterSupply

function afterSupply(SupplyParams calldata params) external view returns (bytes4);

Called after supply state changes, before token transfer.

SupplyParams

| Field | Type | Description | |||***| | id | Id | Market identifier | | trancheIndex | uint256 | Tranche being supplied to | | sender | address | Address that initiated the supply call | | onBehalf | address | Address credited with supply shares | | assets | uint256 | Amount of loan tokens supplied | | shares | uint256 | Amount of supply shares minted |


afterBorrow

function afterBorrow(BorrowParams calldata params) external view returns (bytes4);

Called after borrow state changes and health checks, before token transfer.

BorrowParams

| Field | Type | Description | |||***| | id | Id | Market identifier | | trancheIndex | uint256 | Tranche being borrowed from | | sender | address | Address that initiated the borrow call | | onBehalf | address | Address whose position is debited | | receiver | address | Address receiving the borrowed tokens | | assets | uint256 | Amount of loan tokens borrowed | | shares | uint256 | Amount of borrow shares created |


afterSupplyCollateral

Called after collateral supply state changes, before token transfer.

SupplyCollateralParams

| Field | Type | Description | |||***| | id | Id | Market identifier | | trancheIndex | uint256 | Tranche receiving collateral | | sender | address | Address that initiated the collateral supply | | onBehalf | address | Address credited with the collateral | | assets | uint256 | Amount of collateral tokens deposited |


afterWithdraw

Called after withdrawal state changes, before token transfer.

WithdrawParams

| Field | Type | Description | |||***| | id | Id | Market identifier | | trancheIndex | uint256 | Tranche being withdrawn from | | sender | address | Address that initiated the withdrawal | | onBehalf | address | Address whose supply shares are burned | | receiver | address | Address receiving the withdrawn tokens | | assets | uint256 | Amount of loan tokens withdrawn | | shares | uint256 | Amount of supply shares burned | | shouldAccrueInterest | bool | Whether interest was accrued before withdrawal |


afterWithdrawCollateral

Called after collateral withdrawal state changes, before token transfer.

WithdrawCollateralParams

| Field | Type | Description | |||***| | id | Id | Market identifier | | trancheIndex | uint256 | Tranche from which collateral is withdrawn | | sender | address | Address that initiated the collateral withdrawal | | onBehalf | address | Address whose collateral is being withdrawn | | receiver | address | Address receiving the collateral tokens | | assets | uint256 | Amount of collateral tokens withdrawn |


Hooks Library

The Hooks.sol library provides permission flags, configuration validation, and the internal call mechanism used by Lotus core.

Permission Flags

| Flag | Value | Triggers On | |||***| | AFTER_SUPPLY_FLAG | 1 << 0 (1) | supply() | | AFTER_BORROW_FLAG | 1 << 1 (2) | borrow() | | AFTER_SUPPLY_COLLATERAL_FLAG | 1 << 2 (4) | supplyCollateral() | | AFTER_WITHDRAW_FLAG | 1 << 3 (8) | withdraw() | | AFTER_WITHDRAW_COLLATERAL_FLAG | 1 << 4 (16) | withdrawCollateral() |

Combine flags with bitwise OR. For example, a hook that fires on both supply and borrow uses AFTER_SUPPLY_FLAG | AFTER_BORROW_FLAG (3).

HookConfig

Stored per-market. The hook address is the deployed hook contract; permissions is the bitmask of active flags.

validateHookConfig

Validates a hook configuration. If hook is the zero address, returns an empty config (hooks disabled). If hook is non-zero but contains no code, reverts with InvalidHook(hook).

hasPermission

Returns true if the given flag is set in the permissions bitmask.

Errors

| Error | When | ||| | HookCallFailed(address hook, bytes4 selector) | The hook's staticcall reverted | | InvalidHookResponse(address hook, bytes4 expected, bytes4 received) | The hook returned an unexpected selector | | InvalidHook(address hook) | The hook address has no deployed code |


AllowlistHook

Restricts market operations to whitelisted addresses. Checks both the transaction sender and the on-behalf address on all five hook points.

Storage

constructor

| Parameter | Type | Description | Required | ||||| | _owner | address | Initial owner of the hook | Yes |

Reverts: ZeroAddress() if _owner is the zero address.

setOwner

| Parameter | Type | Description | Required | ||||| | newOwner | address | New owner address | Yes |

Reverts: NotOwner() if caller is not the current owner. ZeroAddress() if newOwner is the zero address.

setSenderAllowed

| Parameter | Type | Description | Required | ||||| | id | Id | Market identifier | Yes | | trancheIndex | uint256 | Tranche index | Yes | | who | address | Address to allow or disallow as sender | Yes | | allowed | bool | Whether the address is allowed | Yes |

Reverts: NotOwner() if caller is not the current owner.

setOnBehalfAllowed

| Parameter | Type | Description | Required | ||||| | id | Id | Market identifier | Yes | | trancheIndex | uint256 | Tranche index | Yes | | who | address | Address to allow or disallow as on-behalf target | Yes | | allowed | bool | Whether the address is allowed | Yes |

Reverts: NotOwner() if caller is not the current owner.

Hook Behavior

All five hook methods (afterSupply, afterBorrow, afterSupplyCollateral, afterWithdraw, afterWithdrawCollateral) perform the same check:

  1. Verify isSenderAllowed[id][trancheIndex][sender] is true. Reverts with SenderNotAllowed(sender) otherwise.

  2. Verify isOnBehalfAllowed[id][trancheIndex][onBehalf] is true. Reverts with OnBehalfNotAllowed(onBehalf) otherwise.

Errors

| Error | When | ||| | SenderNotAllowed(address sender) | Sender is not on the allowlist for this market/tranche | | OnBehalfNotAllowed(address onBehalf) | On-behalf address is not on the allowlist for this market/tranche | | NotOwner() | Caller is not the hook owner | | ZeroAddress() | Zero address provided where non-zero is required |


BorrowHealthGuardHook

Enforces a stricter health requirement on borrowers by applying a multiplier (factor) to the protocol's LLTV. Only hooks into afterBorrow; all other hook points use the default pass-through from BaseLotusHook.

Storage

The factor is in WAD format (1e18 = 100%). A factor of 0.9e18 means the borrower can only borrow up to 90% of the protocol maximum.

constructor

| Parameter | Type | Description | Required | ||||| | _owner | address | Initial owner of the hook | Yes | | _lotus | ILotus | Address of the Lotus core contract | Yes |

Reverts: ZeroAddress() if either parameter is the zero address.

setOwner

| Parameter | Type | Description | Required | ||||| | newOwner | address | New owner address | Yes |

Reverts: NotOwner() if caller is not the current owner. ZeroAddress() if newOwner is the zero address.

setFactor

| Parameter | Type | Description | Required | ||||| | id | Id | Market identifier | Yes | | trancheIndex | uint256 | Tranche index | Yes | | newFactor | uint256 | Health factor multiplier in WAD (must be ≤ 1e18) | Yes |

Reverts: NotOwner() if caller is not the current owner. InvalidFactor(newFactor) if newFactor > 1e18.

A factor of 0 means the guard is not configured for that tranche (no check performed). Setting a factor effectively tightens the borrowing limit: strictMaxBorrow = protocolMaxBorrow * factor / WAD.

afterBorrow Behavior

  1. Load the factor for the market and tranche. If zero, return immediately (unconfigured).

  2. Fetch market parameters, tranche state, and borrower position from Lotus.

  3. Convert the borrower's borrow shares to assets: borrowed = borrowShares.toAssetsUp(trancheBorrowAssets, trancheBorrowShares).

  4. Compute the protocol maximum borrow: protocolMaxBorrow = collateral * oraclePrice / ORACLE_PRICE_SCALE * LLTV / WAD.

  5. Apply the factor: strictMaxBorrow = protocolMaxBorrow * factor / WAD.

  6. If borrowed > strictMaxBorrow, revert with HealthFactorTooLow(borrowed, strictMaxBorrow).

Errors

| Error | When | ||| | HealthFactorTooLow(uint256 borrowed, uint256 maxAllowed) | Borrow exceeds the guard-adjusted maximum | | InvalidFactor(uint256 factor) | Factor exceeds 1e18 | | NotOwner() | Caller is not the hook owner | | ZeroAddress() | Zero address provided in constructor |


See Also

  • Learn → Hooks for conceptual explanation

  • Build → Hooks for implementation guide

  • Reference → Admin for setMarketHook documentation

Last updated