Core mechanisms and economic principles that underpin automated on-chain liquidation systems.
How Margin Calls Are Enforced On-Chain
Foundational Concepts
Collateralization Ratio
The Collateralization Ratio is the key health metric for a loan, calculated as (Collateral Value / Debt Value) * 100. It determines a position's safety.
- A 150% ratio means $150 of collateral backs $100 of debt.
- Protocols set a Minimum Collateralization Ratio (e.g., 110%) as the liquidation threshold.
- This ratio dynamically changes with market prices, triggering liquidations when breached.
Liquidation Engine
The Liquidation Engine is a set of permissionless smart contracts that automatically enforces margin calls.
- It continuously monitors collateral ratios against oracle price feeds.
- When a position becomes undercollateralized, the engine auctions the collateral.
- It uses incentives like liquidation bonuses to attract liquidators who repay debt and close the position, ensuring system solvency.
Price Oracles
Price Oracles are critical infrastructure providing external market data to smart contracts. They are the source of truth for collateral and debt valuations.
- Decentralized oracles like Chainlink aggregate data from multiple exchanges.
- Manipulation-resistant designs are essential to prevent false liquidations.
- Oracle latency and staleness can create arbitrage opportunities or risks for users.
Liquidation Bonus & Incentives
The Liquidation Bonus is a discount offered to liquidators when they purchase collateral, making the role economically viable.
- A 5% bonus means a liquidator buys $100 of collateral for $95 of repaid debt.
- This incentive ensures rapid execution, protecting the protocol from bad debt.
- The bonus size is a trade-off between user cost and system security.
Health Factor
The Health Factor is a user-facing, inverse representation of risk, commonly used in protocols like Aave and Compound.
- Calculated as Collateral Value / (Debt * Liquidation Threshold).
- A Health Factor below 1.0 makes a position eligible for liquidation.
- It provides a buffer below the minimum collateral ratio, giving users a warning margin.
Liquidation Process Flow
The Liquidation Process is the step-by-sequence executed on-chain when a margin call is triggered.
-
- Oracle updates asset price, recalculating the collateral ratio.
-
- A keeper bot detects the undercollateralized position.
-
- Liquidator repays part or all of the debt in exchange for discounted collateral.
-
- Remaining collateral (if any) is returned to the original borrower.
The Liquidation Execution Flow
Process overview
Health Factor Monitoring
Continuous on-chain calculation of a position's solvency
Detailed Instructions
A position's health factor (HF) is calculated continuously, typically as (Collateral Value * Liquidation Threshold) / Borrowed Value. This is a key risk metric. When a user interacts with the protocol or an external keeper bot calls a specific function, the contract recalculates this value using the latest oracle prices.
- Sub-step 1: Fetch Prices: The contract queries the configured price oracle (e.g., Chainlink's
AggregatorV3Interface) for the collateral and debt assets. - Sub-step 2: Calculate Values: It computes the total collateral value in the protocol's base currency (e.g., USD) and the total borrowed value.
- Sub-step 3: Compare to Threshold: The resulting HF is compared against the protocol's liquidation threshold, often
1.0. IfHF < 1.0, the position is eligible for liquidation.
solidity// Simplified health factor check uint256 healthFactor = (collateralValueInEth * liquidationThreshold) / totalBorrowsInEth; require(healthFactor < 1 ether, "Health factor not below threshold");
Tip: The liquidation threshold is asset-specific and set by governance, representing the maximum loan-to-value (LTV) at which a position can be liquidated.
Liquidation Trigger and Incentive
How liquidators are called and compensated for executing
Detailed Instructions
Liquidation is a permissionless process. Any external account (a liquidator) can call the liquidate() function, providing the target user's address and the debt to be repaid. The primary incentive is a liquidation bonus (or penalty) applied to the seized collateral.
- Sub-step 1: Function Call: The liquidator calls
liquidate(address user, uint256 debtToCover, address collateralAsset). - Sub-step 2: Bonus Calculation: The contract calculates the amount of collateral to seize using the formula:
collateralSeized = (debtToCover * (1 + liquidationBonus)) / collateralPrice. A typical bonus ranges from 5% to 15%. - Sub-step 3: Incentive Verification: The liquidator's profit is the difference between the market value of the seized collateral and the debt they repaid. They must ensure this covers their gas costs and provides a margin.
solidity// Calculating collateral to seize with a 10% bonus uint256 collateralSeized = (debtToCover * 1.1e18) / collateralPrice;
Tip: Liquidators often use sophisticated bots that monitor the mempool and blockchain state to be the first to call liquidation on profitable positions, creating a competitive landscape.
Debt Repayment and Collateral Seizure
The atomic on-chain transfer of assets during liquidation
Detailed Instructions
This step executes the core financial transfer atomically within the smart contract. The liquidator repays the user's debt on their behalf and receives a proportional amount of the user's collateral, plus the bonus.
- Sub-step 1: Debt Transfer: The contract transfers the specified
debtToCoveramount (e.g., 1000 USDC) from the liquidator's balance to the protocol's treasury, reducing the target user's debt obligation. - Sub-step 2: Collateral Transfer: Simultaneously, the contract transfers the calculated
collateralSeizedamount (e.g., 0.5 ETH) from the protocol's holding contract to the liquidator's address. - Sub-step 3: State Update: The user's debt and collateral balances within the protocol's internal accounting are updated. Their health factor is recalculated and should now be above the threshold if the liquidation was sufficient.
solidity// Core transfer logic (conceptual) _debtToken.transferFrom(liquidator, address(this), debtToCover); userDebt -= debtToCover; _collateralToken.transfer(liquidator, collateralSeized); userCollateral -= collateralSeized;
Tip: This atomic execution is critical. If any part fails (e.g., insufficient allowance/balance), the entire transaction reverts, preventing partial liquidations that could leave the protocol undercollateralized.
Post-Liquidation State and Close Factor
Managing partial liquidations and finalizing the position state
Detailed Instructions
Protocols often implement a close factor to limit the amount of debt that can be liquidated in a single transaction, preventing overly punitive liquidations. The final step ensures the position is left in a stable state.
- Sub-step 1: Apply Close Factor: The maximum debt that can be repaid is calculated as
min(debtToCover, userTotalDebt * closeFactor). A common close factor is 50%, meaning only half the debt can be liquidated at once. - Sub-step 2: Health Factor Recheck: After the transfer, the contract recalculates the user's new health factor. If
HFis still below 1.0, the position remains open for further liquidation. - Sub-step 3: Emit Event: A
LiquidationCallevent is emitted with all relevant parameters (user, liquidator, debtRepaid, collateralSeized, healthFactor). This is crucial for off-chain indexing and analytics.
solidity// Applying a close factor of 50% uint256 maxClose = (userTotalDebt * 5000) / 10000; // 50% in basis points uint256 actualDebtToCover = debtToCover < maxClose ? debtToCover : maxClose;
Tip: The close factor protects users from total liquidation due to short-term volatility. It forces liquidators to perform multiple transactions, giving the user time to add collateral or repay debt.
Margin Call Mechanisms by Protocol
Comparison of liquidation trigger methods, fees, and execution parameters.
| Feature | MakerDAO | Aave V3 | Compound V3 |
|---|---|---|---|
Liquidation Trigger | User-specific Liquidation Ratio | Health Factor < 1 | Health Factor < 1 |
Liquidation Fee | 13% (includes 3% stability fee) | 5-10% (variable by asset) | 8% fixed bonus |
Liquidation Close Factor | Up to 100% of debt | Up to 50% of debt | Up to 100% of debt |
Price Oracle | Maker Oracle (medianizer) | Chainlink + Fallback Oracle | Chainlink Price Feed |
Liquidation Execution | Keeper auctions (English) | Fixed-price, immediate | Fixed-price, immediate |
Max Gas Cost Reimbursement | No | Yes, up to 50% of bonus | No |
Minimum Position Size for Liquidation | No minimum | Configurable by asset (e.g., $100 for ETH) | No minimum |
Technical Implementation Details
Core Enforcement Process
On-chain margin calls are enforced through a liquidation engine that continuously monitors user positions against predefined collateralization ratios. This is not a manual process but an automated, permissionless function triggered by price oracles.
Key Components
- Price Oracles: Provide real-time asset valuations. Protocols like Aave and Compound use decentralized oracle networks (e.g., Chainlink) to fetch asset prices. A significant price drop in collateral value is the primary trigger.
- Health Factor / Collateral Factor: A numerical representation of a position's safety. In Aave, a Health Factor below 1 makes a position eligible for liquidation. In Compound, it's the Collateral Factor.
- Liquidators: Permissionless actors or bots who can call the liquidation function. They are incentivized with a liquidation bonus, a discount on the seized collateral, paid by the underwater borrower.
Process Flow
- Oracle reports a price drop.
- The protocol's smart contract recalculates the user's health factor.
- If the factor falls below the threshold, the position is flagged.
- A liquidator repays a portion of the borrower's debt in exchange for discounted collateral.
- The borrower's debt is reduced, and the system's solvency is maintained.
How Liquidators (Keepers) Operate
Process overview
Monitor Protocol Health and Positions
Continuously scan for undercollateralized positions using on-chain data.
Detailed Instructions
Liquidators run off-chain bots that poll a protocol's smart contracts for user positions. The core task is to check if a position's health factor or collateralization ratio has fallen below the protocol's defined liquidation threshold. This is done by calling view functions on the lending or margin contract, such as getAccountLiquidity(address user) or getHealthFactor(address user). Bots typically subscribe to new block events and monitor price feeds from oracles like Chainlink to calculate real-time collateral values.
- Sub-step 1: Query the protocol's
LiquidationEngineorVaultcontract for a list of open positions. - Sub-step 2: For each position, call
getPositionDebt(address user)andgetPositionCollateral(address user)to fetch current balances. - Sub-step 3: Fetch the latest asset prices from the designated oracle (e.g.,
AggregatorV3Interface(0x5f4eC3Df9cbd43714FE2740f5E3616155c5b8419).latestAnswer()for ETH/USD). - Sub-step 4: Calculate the health factor:
(collateralValue * liquidationThreshold) / debtValue. Flag any position where the result is< 1.0.
Tip: Efficient bots use multi-call contracts (like Maker's
Multicall) to batch these read queries and reduce RPC calls and latency.
Assess Profitability and Gas Costs
Calculate the potential liquidation reward against the transaction execution cost.
Detailed Instructions
Before submitting a transaction, a keeper must ensure the operation is economically viable. The liquidation incentive (or bonus) is a percentage of the seized collateral or repaid debt, defined by the protocol (e.g., 5-15%). The keeper calculates the expected reward in the base asset (like ETH or a stablecoin) and compares it to the current network gas fee. The profit is Liquidation Reward - (Gas Used * Gas Price). On networks like Ethereum during congestion, this calculation is critical.
- Sub-step 1: Determine the exact liquidation penalty parameter from the protocol's contract (e.g., call
liquidationPenalty()). - Sub-step 2: Estimate the
gasUsedfor theliquidate()function by simulating the transaction usingeth_estimateGason a forked node or testnet. - Sub-step 3: Fetch the current
gasPricefrom a provider or use a gas estimation API. Factor in potential priority fees (e.g.,maxPriorityFeePerGas). - Sub-step 4: Compute net profit. If negative or below a minimum threshold (e.g., $20), the bot may skip the opportunity.
solidity// Example calculation snippet uint256 collateralSeized = (debtToCover * (1e18 + liquidationBonus)) / 1e18; uint256 rewardValue = (collateralSeized * oraclePrice) / 1e18; uint256 gasCostInEth = (estimatedGas * gasPrice); bool isProfitable = rewardValue > gasCostInEth;
Tip: Use flashbots bundles or private transaction pools (like Taichi Network) to avoid failed tx gas costs and front-running.
Execute the Liquidation Transaction
Call the on-chain liquidation function with precise parameters.
Detailed Instructions
The keeper's bot constructs and broadcasts a transaction to the protocol's public liquidation function. This function typically requires specifying the underwater borrower's address, the debt asset to be repaid, and the collateral asset to be seized. The caller must often repay a specific amount of debt, up to a close factor (e.g., 50% of the debt). The transaction must be signed with the keeper's private key and sent with a competitive gas price to win the MEV (Maximal Extractable Value) race against other liquidators.
- Sub-step 1: Encode the function call data. For Aave V3, this might be:
ILendingPool.liquidate(address user, address debtAsset, uint256 debtToCover, address collateralAsset). - Sub-step 2: Set the
debtToCoveramount. This is often calculated as the minimum needed to bring the health factor back above 1.0, or the maximum allowed by thecloseFactor. - Sub-step 3: Sign the transaction using a wallet library (ethers.js, web3.py) with a nonce managed to prevent conflicts.
- Sub-step 4: Broadcast the transaction via a reliable RPC endpoint or a private mempool service.
javascript// Example using ethers.js const tx = await lendingContract.liquidate( borrowerAddress, '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', // USDC ethers.utils.parseUnits('5000', 6), // Cover 5000 USDC debt '0xC02aaa39b223fe8D0A0e5C4F27eAD9083C756Cc2' // WETH as collateral , { gasLimit: 500000, maxFeePerGas: gasPrice });
Tip: To avoid reverts, ensure the keeper's wallet holds enough of the debt asset (e.g., USDC) to cover the
debtToCoveramount, as the contract will pull it.
Handle the Seized Collateral and Reward
Manage the assets received from a successful liquidation.
Detailed Instructions
Upon successful execution, the protocol transfers the specified collateral asset from the borrower's position to the liquidator. The amount seized is typically the value of the repaid debt plus the liquidation bonus. The keeper must then decide how to manage this asset. Common strategies include holding it, swapping it for a stablecoin via a DEX (like Uniswap) to realize profit, or using it as collateral in another DeFi position. The keeper's contract must also handle any leftover debt or dust from partial liquidations.
- Sub-step 1: Verify the transaction receipt for success and parse the
Transferevents to confirm the exact amount of collateral received by the keeper's address. - Sub-step 2: If the seized asset is volatile (e.g., WETH), the keeper may immediately call
swapExactTokensForTokenson a DEX router to convert it to USDC, minimizing price risk. - Sub-step 3: Account for any protocol-specific mechanics. In Compound, for example, the liquidator receives cTokens (cETH), which must be redeemed for the underlying asset.
- Sub-step 4: Update internal accounting to track profits, accounting for gas costs paid in the native token (ETH).
solidity// Example: Redeeming cTokens after a Compound liquidation ICErc20 cToken = ICErc20(0x4Ddc2D193948926D02f9B1fE9e1daa0718270ED5); // cETH uint256 redeemResult = cToken.redeem(cTokenBalanceOfKeeper); // Redeems underlying ETH require(redeemResult == 0, 'Compound redemption error');
Tip: For efficiency, advanced keepers use flash loans to fund the debt repayment, allowing them to liquidate without pre-funding capital. The entire sequence—loan, liquidation, swap, repayment—is executed atomically in one transaction.
Risks and Protocol Mitigations
Protocol Documentation and Code
Ready to Start Building?
Let's bring your Web3 vision to life.
From concept to deployment, ChainScore helps you architect, build, and scale secure blockchain solutions.