Hooks
Step-by-step guide to implementing, testing, and deploying custom Lotus hooks with Solidity and TypeScript examples, including a MaxSupplyHook example.
1
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {BaseLotusHook} from "lotus/hooks/base/BaseLotusHook.sol";
import {ILotusHooks} from "lotus/hooks/interfaces/ILotusHooks.sol";
import {ILotus} from "lotus/interfaces/ILotus.sol";
import {Id} from "lotus/interfaces/Types.sol";
contract MaxSupplyHook is BaseLotusHook {
ILotus public immutable lotus;
address public owner;
// Maximum supply (in loan token assets) per market per tranche
mapping(Id => mapping(uint256 => uint256)) public maxSupply;
error NotOwner();
error SupplyCapExceeded(uint256 currentSupply, uint256 cap);
modifier onlyOwner() {
if (msg.sender != owner) revert NotOwner();
_;
}
constructor(address _owner, ILotus _lotus) {
owner = _owner;
lotus = _lotus;
}
function setMaxSupply(Id id, uint256 trancheIndex, uint256 cap) external onlyOwner {
maxSupply[id][trancheIndex] = cap;
}
function afterSupply(
ILotusHooks.SupplyParams calldata params
) external view override returns (bytes4) {
uint256 cap = maxSupply[params.id][params.trancheIndex];
if (cap == 0) return ILotusHooks.afterSupply.selector; // unconfigured
// Read current tranche supply from Lotus
ILotus.Tranche[] memory tranches = lotus.getMarketTranches(params.id);
uint256 currentSupply = tranches[params.trancheIndex].trancheSupplyAssets;
if (currentSupply > cap) {
revert SupplyCapExceeded(currentSupply, cap);
}
return ILotusHooks.afterSupply.selector;
}
}2
import {Hooks} from "lotus/hooks/libraries/Hooks.sol";
// Single hook point
uint256 permissions = Hooks.AFTER_SUPPLY_FLAG; // = 1
// Multiple hook points — combine with bitwise OR
uint256 permissions = Hooks.AFTER_SUPPLY_FLAG | Hooks.AFTER_BORROW_FLAG; // = 33
// Deploy
MaxSupplyHook hook = new MaxSupplyHook(admin, lotus);
// Configure caps
hook.setMaxSupply(marketId, 0, 1_000_000e6); // 1M USDC cap on tranche 0
// Register on the market (admin only)
uint256 permissions = Hooks.AFTER_SUPPLY_FLAG;
lotus.setMarketHook(marketParams, address(hook), permissions);import { ethers } from "ethers";
const AFTER_SUPPLY_FLAG = 1n << 0n;
// Deploy hook (assume artifact is available)
const HookFactory = new ethers.ContractFactory(abi, bytecode, signer);
const hook = await HookFactory.deploy(adminAddress, lotusAddress);
await hook.waitForDeployment();
// Register on market
const lotus = new ethers.Contract(lotusAddress, lotusAbi, adminSigner);
await lotus.setMarketHook(marketParams, await hook.getAddress(), AFTER_SUPPLY_FLAG);4
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "forge-std/Test.sol";
import {MaxSupplyHook} from "../src/MaxSupplyHook.sol";
import {Hooks} from "lotus/hooks/libraries/Hooks.sol";
contract MaxSupplyHookTest is Test {
// Assume lotus, market, and tokens are set up in setUp()
function test_supplyUnderCap() public {
// Set cap to 1M
hook.setMaxSupply(marketId, 0, 1_000_000e6);
// Register hook
vm.prank(admin);
lotus.setMarketHook(marketParams, address(hook), Hooks.AFTER_SUPPLY_FLAG);
// Supply 500K — should succeed
vm.prank(lender);
lotus.supply(marketParams, 0, 500_000e6, 0, lender, "", "");
}
function test_supplyOverCap_reverts() public {
hook.setMaxSupply(marketId, 0, 1_000_000e6);
vm.prank(admin);
lotus.setMarketHook(marketParams, address(hook), Hooks.AFTER_SUPPLY_FLAG);
// Supply 500K first
vm.prank(lender);
lotus.supply(marketParams, 0, 500_000e6, 0, lender, "", "");
// Supply 600K more — should revert (total 1.1M > 1M cap)
vm.prank(lender);
vm.expectRevert();
lotus.supply(marketParams, 0, 600_000e6, 0, lender, "", "");
}
}See Also
Last updated

