Pre-Liquidation
Step-by-step guide to deploying pre-liquidation contracts, authorizing borrowers, and building monitoring bots with Solidity and TypeScript examples.
1
Parameter
Value
Meaning
2
import {PreLiquidationFactory} from "lotus/pre-liquidation/PreLiquidationFactory.sol";
import {IPreLiquidation} from "lotus/pre-liquidation/interfaces/IPreLiquidation.sol";
PreLiquidationParams memory params = PreLiquidationParams({
preLltv: 0.85e18,
preLCF1: 0.05e18,
preLCF2: 0.20e18,
preLIF1: 1.01e18,
preLIF2: 1.05e18,
preLiquidationOracle: oracleAddress
});
bytes32 salt = keccak256("my-pre-liquidation-v1");
IPreLiquidation preLiq = factory.createPreLiquidation(
marketId,
trancheIndex,
params,
salt
);address predictedAddress = factory.computePreLiquidationAddress(
marketId,
trancheIndex,
params,
salt
);3
// Borrower calls this on the Lotus core contract
lotus.setAuthorization(address(preLiq), true);// Off-chain: borrower signs an EIP-712 typed message
Authorization memory auth = Authorization({
authorizer: borrowerAddress,
authorized: address(preLiq),
isAuthorized: true,
nonce: lotus.nonce(borrowerAddress),
deadline: block.timestamp + 1 hours
});
// Borrower signs the typed data (e.g. via wallet)
(uint8 v, bytes32 r, bytes32 s) = vm.sign(borrowerKey, typedDataHash);
// Anyone can submit the signed authorization
lotus.setAuthorizationWithSig(auth, Signature({v: v, r: r, s: s}));4
import { ethers } from "ethers";
const provider = new ethers.JsonRpcProvider(RPC_URL);
const signer = new ethers.Wallet(PRIVATE_KEY, provider);
const preLiq = new ethers.Contract(preLiqAddress, preLiqAbi, signer);
const oracle = new ethers.Contract(oracleAddress, oracleAbi, provider);
const lotus = new ethers.Contract(lotusAddress, lotusAbi, provider);
const ORACLE_PRICE_SCALE = 10n ** 36n;
const WAD = 10n ** 18n;
async function checkAndPreLiquidate(borrower: string) {
// 1. Get position and tranche state
const position = await lotus.getPosition(marketId, borrower);
const tranches = await lotus.getMarketTranches(marketId);
const tranche = tranches[trancheIndex];
const collateral = position.collateral[trancheIndex];
const borrowShares = position.borrowShares[trancheIndex];
if (collateral === 0n || borrowShares === 0n) return;
// 2. Compute LTV using the pre-liquidation oracle
const price = await oracle.price();
const borrowed = borrowShares * tranche.trancheBorrowAssets / tranche.trancheBorrowShares;
const collateralValue = collateral * price / ORACLE_PRICE_SCALE;
const ltv = borrowed * WAD / collateralValue;
const preLltv = await preLiq.PRE_LLTV();
const lltv = await preLiq.LLTV();
// 3. Check if in pre-liquidation zone
if (ltv <= preLltv || ltv >= lltv) return;
// 4. Compute close factor via interpolation
const preLCF1 = await preLiq.PRE_LCF_1();
const preLCF2 = await preLiq.PRE_LCF_2();
const quotient = (ltv - preLltv) * WAD / (lltv - preLltv);
const closeFactor = quotient * (preLCF2 - preLCF1) / WAD + preLCF1;
// 5. Calculate repay amount
const maxRepayShares = borrowShares * closeFactor / WAD;
// 6. Execute pre-liquidation
const tx = await preLiq.preLiquidate(borrower, 0, maxRepayShares, "0x");
const receipt = await tx.wait();
console.log(`Pre-liquidated ${borrower}: tx ${receipt.hash}`);
}contract FlashPreLiquidator is IPreLiquidationCallback {
function execute(address preLiq, address borrower, uint256 repaidShares) external {
// Trigger pre-liquidation with callback data
IPreLiquidation(preLiq).preLiquidate(borrower, 0, repaidShares, abi.encode(msg.sender));
}
function onPreLiquidate(uint256 repaidAssets, bytes calldata data) external {
// At this point, collateral has been sent to this contract.
// Swap collateral → loan token to cover repaidAssets.
// The pre-liquidation contract will pull repaidAssets of loan token from us.
}
}5
function test_preLiquidation() public {
// Setup: create market, deposit collateral, borrow
vm.prank(borrower);
lotus.supplyCollateral(marketParams, trancheIndex, 10e18, borrower, "");
vm.prank(borrower);
lotus.borrow(marketParams, trancheIndex, 8000e6, 0, borrower, borrower, "");
// Borrower authorizes pre-liquidation contract
vm.prank(borrower);
lotus.setAuthorization(address(preLiq), true);
// Price drops → borrower enters pre-liquidation zone
oracle.setPrice(newLowerPrice);
// Liquidator executes pre-liquidation
uint256 repaidShares = 100e6; // repay a portion of borrow shares
vm.prank(liquidator);
(uint256 seized, uint256 repaid) = preLiq.preLiquidate(borrower, 0, repaidShares, "");
// Verify partial liquidation occurred
assertGt(seized, 0, "collateral should be seized");
assertGt(repaid, 0, "debt should be repaid");
}See Also
Last updated

