Free 30-min Web3 Consultation
Book Consultation
Smart Contract Security Audits
View Audit Services
Custom DeFi Protocol Development
Explore DeFi
Full-Stack Web3 dApp Development
View App Services
Free 30-min Web3 Consultation
Book Consultation
Smart Contract Security Audits
View Audit Services
Custom DeFi Protocol Development
Explore DeFi
Full-Stack Web3 dApp Development
View App Services
Free 30-min Web3 Consultation
Book Consultation
Smart Contract Security Audits
View Audit Services
Custom DeFi Protocol Development
Explore DeFi
Full-Stack Web3 dApp Development
View App Services
Free 30-min Web3 Consultation
Book Consultation
Smart Contract Security Audits
View Audit Services
Custom DeFi Protocol Development
Explore DeFi
Full-Stack Web3 dApp Development
View App Services
LABS
Guides

How to Design a Liquidation Engine for Leveraged Positions

This guide details the architecture for automatically liquidating undercollateralized positions in derivatives protocols. It covers calculating health factors, designing Dutch auctions or fixed-price liquidations, and incentivizing liquidators. The guide explains managing partial liquidations and protecting against oracle manipulation.
Chainscore © 2026
introduction
ARCHITECTURE

Introduction to Liquidation Engine Design

A liquidation engine is the critical risk management system for any protocol offering leveraged positions, such as lending markets or perpetual futures. This guide explains the core components and design patterns for building a secure and efficient engine.

A liquidation engine is an automated system that closes undercollateralized positions to protect a protocol from bad debt. Its primary function is to monitor the health factor or margin ratio of user positions. When this metric falls below a predefined threshold (e.g., a health factor of 1.0), the position is flagged for liquidation. The engine must be permissionless, allowing any third-party liquidator to trigger the process, and profitable, offering an incentive (a liquidation bonus) to ensure positions are cleared promptly.

The core design involves three key components: a price oracle for accurate asset valuation, a health check module that continuously evaluates positions, and a liquidation execution logic that defines how assets are seized and sold. For example, in a Compound-like lending market, the health factor is calculated as (Collateral Value * Collateral Factor) / Borrowed Value. If a user's borrowed ETH value rises too high against their posted USDC collateral, their health factor drops, triggering a liquidation event where a portion of their collateral is auctioned to repay the debt.

Designing the execution logic requires careful trade-offs. A common method is a fixed discount liquidation, where the liquidator purchases the collateral at a set discount (e.g., 5%) to the oracle price. More complex systems use Dutch auctions or batch auctions to improve price discovery and minimize market impact. The engine must also handle edge cases like liquidation cascades during market crashes and maximizing liquidatable debt while respecting gas limits and slippage constraints on decentralized exchanges.

Security is paramount. The engine's most critical dependency is its price oracle. Using a decentralized oracle like Chainlink or a time-weighted average price (TWAP) from a major DEX helps prevent price manipulation attacks. The logic must also be resistant to sandwich attacks and front-running; one mitigation is to calculate the liquidation incentive based on a slightly stale price from the previous block. All calculations should use fixed-point arithmetic and be thoroughly tested for integer overflow/underflow.

Implementing a basic engine in Solidity involves a keeper network or off-chain watcher to scan for unhealthy positions. When one is found, the watcher calls a function like liquidate(address user, address debtAsset, uint256 debtToCover). This function would verify the health factor, calculate the collateral to seize, transfer it to the liquidator, and use it to repay the user's debt, sending any leftover collateral (the "bonus") back to the liquidator. The AAVE V3 documentation provides a real-world reference for these mechanics.

Finally, parameter tuning is an ongoing process. The liquidation threshold, close factor (percentage of debt that can be liquidated in one transaction), and liquidation bonus must be calibrated based on asset volatility and market liquidity. Setting the bonus too low disincentivizes liquidators, risking protocol insolvency; setting it too high can unnecessarily punish borrowers. Effective engines are gas-efficient, minimize bad debt, and maintain protocol solvency even during extreme market volatility.

prerequisites
PREREQUISITES AND CORE CONCEPTS

How to Design a Liquidation Engine for Leveraged Positions

A liquidation engine is the critical risk management component of any lending or perpetual futures protocol. This guide covers the foundational concepts required to design a secure and efficient system.

A liquidation engine automatically closes an undercollateralized position to protect the protocol from bad debt. The core mechanism compares a user's Health Factor (HF) or Margin Ratio against a predefined Liquidation Threshold. When HF < 1.0 (or the equivalent), the position becomes eligible for liquidation. This is not a penalty but a necessary risk control; without it, a single default could threaten the solvency of the entire lending pool. The engine's design directly impacts user safety, capital efficiency, and market stability.

The primary inputs for calculating health are the collateral value and the debt value, both denominated in a common unit like USD. These values are sourced from price oracles, making oracle security paramount. A common formula is Health Factor = (Collateral Value * Collateral Factor) / Debt Value. The Collateral Factor (or Loan-to-Value ratio) is a risk parameter set by governance, determining how much debt can be borrowed against each asset. For example, a $100 ETH deposit with a 0.8 collateral factor allows up to $80 in stablecoin debt.

Liquidations are triggered by keepers—bots or users incentivized by a liquidation bonus. This bonus, a discount on the seized collateral (e.g., 5-10%), compensates keepers for their gas costs and execution risk. The engine must define clear rules: the liquidation close factor (what percentage of the debt to repay in one transaction) and the liquidation penalty (an additional fee added to the debt, paid to the protocol's treasury or insurers). These parameters balance between swiftly resolving insolvency and avoiding excessive, destabilizing liquidations.

Design choices include the auction model versus fixed-price liquidation. Fixed-price (e.g., Aave, Compound) is simpler: keepers repay debt to receive collateral at a fixed discount. Auction-based models (e.g., MakerDAO) can potentially achieve better prices by allowing market bidding, but are more complex and slower. The engine must also handle partial liquidations to improve a position's health factor just above the threshold, rather than closing it entirely, which is less disruptive for users.

Critical edge cases must be engineered. Oracle staleness or manipulation can cause false liquidations; using a time-weighted average price (TWAP) from a decentralized oracle like Chainlink mitigates this. Gas wars among keepers can lead to network congestion and failed transactions; implementing a Dutch auction or a randomized keeper selection can help. The system must also be pausable in an emergency and have clear, transparent logic to avoid governance attacks on its parameters.

health-factor-calculation
CORE MECHANIC

Step 1: Calculating the Health Factor

The health factor is the primary metric that determines the safety of a leveraged position and triggers liquidations. This step explains its calculation and critical thresholds.

A health factor (HF) is a numerical representation of a position's solvency, calculated as the ratio of the position's collateral value to its borrowed value, adjusted for asset-specific risk parameters. In simple terms: HF = (Total Collateral Value * Collateral Factor) / Total Borrowed Value. A health factor above 1.0 indicates the position is considered safe, as the value of the collateral exceeds the debt. A value at or below 1.0 signals the position is undercollateralized and eligible for liquidation. Protocols like Aave and Compound popularized this model, which is now standard for overcollateralized lending.

The calculation is not a simple division of market prices. Each asset is assigned a liquidation threshold (or collateral factor), representing the maximum percentage of its value that can be borrowed against. For example, if ETH has a liquidation threshold of 80% and a user deposits 1 ETH worth $3,000, the maximum they can borrow against it is $2,400. The health factor formula incorporates these thresholds per asset: HF = ÎŁ(Collateral_i * Price_i * LT_i) / ÎŁ(Debt_j * Price_j). This risk-adjusted sum ensures stablecoins and blue-chip assets provide more borrowing power than volatile altcoins.

In practice, you must fetch real-time prices from a decentralized oracle like Chainlink. The core calculation in Solidity involves fetching the user's balances for all supplied and borrowed assets, their respective prices, and their liquidation thresholds from the protocol's configuration. Here is a simplified conceptual snippet:

solidity
function calculateHealthFactor(address user) public view returns (uint256) {
    (uint256 totalCollateralInETH, uint256 totalDebtInETH) = getUserAccountData(user);
    // getUserAccountData internally sums: (collateral * price * threshold) and (debt * price)
    if (totalDebtInETH == 0) return type(uint256).max; // No debt, infinitely safe
    return (totalCollateralInETH * 1e18) / totalDebtInETH; // Returns a scaled value (e.g., 1.5e18 for HF=1.5)
}

The actual on-chain function aggregates values across all the user's positions.

The liquidation threshold is distinct from the liquidation close factor. The threshold (e.g., 80%) determines when a position becomes unhealthy. The close factor (e.g., 50%) determines how much of the undercollateralized debt a liquidator can repay in a single transaction. This limits market impact. A position with an HF of 0.95 may only have a portion of its debt liquidated to bring the HF back above 1.0, rather than the entire position at once.

Monitoring the health factor requires constant price updates. A 10% drop in collateral value or a rise in borrowed asset value can push the HF below 1.0. Therefore, the liquidation engine must listen for oracle price updates and have a low-latency method to identify positions that have crossed the threshold. In designing your engine, you must decide whether to calculate HF on-chain for each check (gas-intensive) or maintain an off-chain index of at-risk positions that are verified on-chain only when a liquidation is attempted.

To summarize, accurately calculating the health factor is the foundation of any liquidation system. It requires: fetching real-time prices from a secure oracle, applying asset-specific risk parameters, and continuously monitoring positions. The next step involves defining the liquidation logic that activates when HF <= 1.

ENGINE ARCHITECTURE

Liquidation Mechanism Comparison

Comparison of core design approaches for handling undercollateralized positions.

MechanismDutch AuctionFixed-Rate AuctionDirect Pool SaleKeeper Network

Primary Use Case

NFTs, illiquid assets

Liquid ERC-20 tokens

Stablecoin/ETH pairs

Generalized DeFi

Liquidation Speed

Minutes to hours

< 30 seconds

< 5 seconds

Varies by keeper

Price Discovery

Gradual price drop

Fixed discount to oracle

Current pool price

Keeper bid competition

Gas Efficiency for Liquidator

High (multiple tx)

Low (single tx)

Low (single tx)

Medium (bid tx + execution)

Oracle Dependency

Low (initial price only)

High (for discount calc)

High (for pool routing)

High (for trigger & bid)

Capital Efficiency

Low (slow capital unlock)

High (fast capital recycle)

High (instant recycle)

Medium (depends on bid depth)

Implementation Complexity

Medium

Low

Low

High

Example Protocol

Blur Lending

Aave V3, Compound

MakerDAO (PSM)

Synthetix (SIP-148)

implementing-dutch-auction
LIQUIDATION MECHANISM

Step 2: Implementing a Dutch Auction

A Dutch auction is a time-based price discovery mechanism where the price of an asset starts high and decreases over time until a buyer accepts it. This design is ideal for liquidations as it efficiently finds a market-clearing price for potentially illiquid collateral.

In a Dutch auction liquidation, the system does not attempt to sell the entire collateral position at a fixed discount. Instead, it lists the collateral for sale at a price that starts above the market value and linearly decreases over a predefined auction duration (e.g., 6 hours). This creates a predictable price path: current_price = start_price - ((start_price - end_price) * elapsed_time / total_duration). The first participant to call a settle function when the current price is acceptable wins the auction, pays the protocol, and receives the collateral. This mechanism is gas-efficient and minimizes the need for complex oracle updates during the sale.

The key parameters to define are the start price, end price, and duration. The start price is typically set slightly above the oracle price to give the system a buffer and attract early bidders seeking a small discount. The end price is the minimum acceptable price, often calculated as debt * liquidation_penalty / collateral_amount. A common practice is to set the end price to ensure the debt and a penalty (e.g., 10-15%) are covered. The duration must be long enough for market participants to react but short enough to limit the protocol's price exposure; durations between 2 and 24 hours are common in protocols like MakerDAO and Euler Finance.

Smart contract implementation requires tracking each auction's state. A typical Auction struct includes fields for collateralAmount, debtToCover, startPrice, endPrice, startTime, and duration. The core logic is in the settleAuction(uint auctionId) function. It first checks the auction is active and not expired, then calculates the current price based on the elapsed time. It verifies the caller has sent sufficient payment (often in the protocol's stablecoin), transfers the collateral to the caller, repays the user's debt, and sends any surplus back to the original position owner. Failed auctions that reach the end price without a taker must have a fallback, such as transferring the collateral to a treasury for manual sale.

Security considerations are paramount. The auction must be resistant to manipulation of the price feed used to set the start price. Using a time-weighted average price (TWAP) oracle from a major DEX like Uniswap V3 mitigates flash loan attacks. The contract must also guard against reentrancy during the settlement transfer and ensure proper access control so only the liquidation engine can start new auctions. Furthermore, the system should include a circuit breaker to pause auctions during extreme network congestion or oracle failure to prevent unfair settlements.

From a UX and economic perspective, a well-tuned Dutch auction aligns incentives. Liquidators are incentivized to monitor and bid, competing for profit opportunities, which helps ensure liquidations occur. The decreasing price guarantees the protocol will recover the debt if there is any market demand above the minimum price. This design is more capital-efficient than fixed-discount models, often resulting in better recovery rates for the protocol and, in cases of surplus, for the liquidated user—a feature known as collateral refunding.

incentivizing-liquidators
ECONOMIC DESIGN

Step 3: Designing Liquidator Incentives

A robust liquidation engine requires carefully calibrated incentives to ensure liquidators actively monitor and execute positions, preventing systemic undercollateralization.

The core incentive for a liquidator is the liquidation reward, typically a percentage of the liquidated collateral or debt. This reward must be high enough to cover the liquidator's operational costs—gas fees, slippage, and risk—while remaining low enough to not excessively penalize the borrower. In protocols like Aave and Compound, this reward is often a fixed percentage (e.g., 5-10%). More advanced systems implement a dynamic incentive model that scales with risk, offering higher rewards for positions that are deeper underwater or in less liquid markets to ensure timely action.

To execute a liquidation, a liquidator must repay part or all of the borrower's outstanding debt. In return, they receive the equivalent value of the borrower's collateral, plus the bonus reward. This is often facilitated by a liquidation contract or a dedicated function in the main protocol. For example, a typical flow on an EVM chain involves calling a function like liquidate(address borrower, uint256 repayAmount, address collateralAsset). The contract then calculates the seized collateral using the current oracle price and the defined liquidation penalty, transferring it to the liquidator.

A critical design consideration is liquidation fairness and resistance to MEV. Without safeguards, a profitable liquidation opportunity can be front-run by bots, leading to centralized control and wasted gas in bidding wars. Solutions include implementing Dutch auctions for the liquidation bonus (as seen in MakerDAO) or using a fixed discount with a randomized delay for execution eligibility. These mechanisms help distribute opportunities more evenly among participants and reduce the extractable value from pure latency races.

The incentive structure must also account for partial versus full liquidation. Allowing liquidators to repay only the minimum amount to restore a position's health (e.g., back to the liquidation threshold) can be more capital-efficient and less disruptive than closing the entire position. This requires precise logic to calculate the repayAmount needed to achieve a target health factor, ensuring the system remains overcollateralized post-liquidation.

Finally, parameters like the reward percentage, minimum profit threshold, and eligible collateral/debt pairs must be governable. This allows the protocol to adapt to changing market conditions, such as increased network gas prices or volatility in specific assets. A well-designed incentive system is not static; it is a key lever for protocol maintainers to ensure the stability of the lending pool and the safety of user funds under all market conditions.

partial-liquidations
ENGINE DESIGN

Step 4: Managing Partial Liquidations

Implementing a system that can liquidate only the necessary portion of a position to restore health, minimizing user impact and maximizing capital efficiency.

A partial liquidation engine is a critical risk management tool that liquidates the minimum collateral required to bring a leveraged position back above its liquidation threshold. This is superior to a full liquidation because it preserves the user's remaining position and reduces market impact. The core calculation determines the liquidation amount (L) needed to repay enough debt so the position's health factor returns to a safe level, typically just above 1.0. For example, if a position with $10,000 in ETH collateral and $8,000 in USDC debt falls to a health factor of 0.95, the engine calculates the exact USDC amount to liquidate to achieve a health factor of 1.05.

The engine must interact with an on-chain oracle (like Chainlink or a custom TWAP) to fetch the current fair market price of the collateral asset. Using this price, it calculates the current Loan-to-Value (LTV) ratio: LTV = (Debt Value) / (Collateral Value * Collateral Factor). The required debt to liquidate is derived from the formula targeting a new, higher collateralization ratio. A simplified version is: L = (Debt - (Target_Health_Factor * Collateral * Price)) / (1 - (Target_Health_Factor / Liquidation_Penalty)). This ensures the bad debt is covered, including a liquidation penalty (e.g., 5-10%) that incentivizes liquidators.

From a smart contract perspective, the liquidation function must safely handle the asset transfer and debt adjustment. A robust implementation includes checks for re-entrancy, validates oracle freshness, and uses a CEI pattern (Checks-Effects-Interactions). The contract should transfer the calculated collateral amount to the liquidator, burn the corresponding debt from the user, and apply the penalty. It's crucial to use safeTransfer for ERC20 tokens and verify the transaction doesn't push the position into immediate re-liquidation due to slippage or price updates.

To attract liquidators, the system must offer a liquidation incentive. This is typically the liquidation penalty itself, distributed as a discount on the seized collateral. For instance, with a 5% penalty, a liquidator repaying $1000 of debt receives $1050 worth of collateral. The engine must calculate this bonus accurately and ensure the protocol's solvency is not compromised. Mechanisms like liquidation caps (max percentage of collateral per tx) and gradual time-based increases in the liquidatable amount can prevent market manipulation and flash loan attacks.

Finally, the engine should emit detailed events for off-chain monitoring and include a fallback to full liquidation if the position is too small or the partial math fails. Testing is paramount: use forked mainnet simulations with tools like Foundry to test edge cases under volatile prices. A well-designed partial liquidation engine, as seen in protocols like Aave and Compound, reduces user attrition and creates a more stable, efficient lending market.

oracle-protection
LIQUIDATION ENGINE DESIGN

Step 5: Protecting Against Oracle Manipulation

A robust liquidation engine must be resilient to price feed manipulation. This guide covers design patterns using multiple oracles, time-weighted averages, and circuit breakers to secure leveraged positions.

Oracle manipulation is a primary attack vector against lending and perpetual protocols, where an attacker artificially inflates or deflates an asset's price to trigger or prevent liquidations unfairly. A single-point-of-failure oracle, like a direct Chainlink price feed, can be vulnerable to short-term price spikes on a single DEX. To mitigate this, your liquidation engine should implement a multi-oracle fallback system. Common patterns include using a primary oracle (e.g., Chainlink) with a secondary decentralized oracle (e.g., Pyth Network or an on-chain TWAP from Uniswap v3) and a circuit breaker that halts liquidations if the price deviation between sources exceeds a predefined threshold (e.g., 5%).

Implementing a Time-Weighted Average Price (TWAP) is a critical defense against flash loan attacks. Instead of using the instantaneous spot price, your smart contract should query a TWAP over a significant period (e.g., 30 minutes to 2 hours). This smooths out temporary price anomalies. On Ethereum, you can calculate a TWAP using the cumulative price stored in Uniswap v3 pools. The core logic involves sampling the cumulative price at the start and end of an interval and dividing by the elapsed time. This makes it economically prohibitive for an attacker to manipulate the price for the entire duration.

Your liquidation logic must include explicit safety checks before executing any liquidation. First, re-fetch the most recent validated price from your oracle system. Second, verify the account's health factor is still below the liquidation threshold using this fresh price. Third, implement a liquidation cap, limiting the percentage of a position that can be liquidated in a single transaction (e.g., 50%) to prevent total account wipeouts from a single bad price. These checks should be performed in a single transaction to avoid front-running.

For code implementation, your liquidate function should structure its calls. It first calls an internal _getSecurePrice(asset) function that enforces your oracle logic. A simplified version might revert if the primary and secondary oracle prices diverge too much, defaulting to the more conservative (lower for collateral) value. Then, it calculates the health factor. Only if the check passes does it proceed to calculate the liquidation amount, apply the cap, and transfer the discounted collateral to the liquidator. Always use the OpenZeppelin ReentrancyGuard to prevent reentrancy attacks during these complex state changes.

Finally, establish monitoring and emergency procedures. Even with robust on-chain logic, you need off-chain monitoring for oracle staleness (if a feed hasn't updated in X minutes) and significant deviations between your price sources. Prepare a pause mechanism, upgradeable via a timelock or multi-sig, to temporarily disable liquidations if a vulnerability is suspected. This layered approach—using multiple data sources, time-averaging, in-transaction checks, and operational safeguards—creates a liquidation engine resilient to manipulation.

LIQUIDATION ENGINE DESIGN

Frequently Asked Questions

Common technical questions and solutions for developers building on-chain liquidation systems for leveraged positions.

A liquidation engine's core function is to protect protocol solvency by automatically closing undercollateralized positions before they become insolvent. When a user's health factor (or collateralization ratio) falls below a predefined threshold (e.g., 1.0), the engine allows liquidators to repay a portion of the debt in exchange for the user's collateral at a discount. This process:

  • Prevents bad debt from accumulating on the protocol's balance sheet.
  • Maintains system liquidity by ensuring borrowed assets are backed.
  • Incentivizes third-party keepers via liquidation bonuses to perform this critical, capital-intensive work.

Protocols like Aave and Compound use this model, where the health factor is calculated as (Collateral Value * Liquidation Threshold) / Borrowed Value.

conclusion
CRITICAL IMPLEMENTATION NOTES

Conclusion and Security Considerations

Building a robust liquidation engine requires careful attention to final system integration and proactive security design. This section covers essential checks and common pitfalls.

A successful liquidation engine is not an isolated module; it must be seamlessly integrated into the broader protocol architecture. Ensure your Liquidate function is permissionless and callable by any external actor or keeper network to guarantee liveness. The contract must correctly handle the transfer of collateral and debt tokens, updating user positions and the protocol's global state in a single atomic transaction to prevent partial state corruption. Thorough integration testing with the protocol's core lending logic is non-negotiable.

Security is paramount. The primary risk is incorrect health factor calculation, which can lead to unfair liquidations or, worse, undercollateralized positions. Rigorously audit the math for edge cases: positions at exactly the liquidationThreshold, positions with multiple collateral types, and the impact of price oracle staleness or manipulation. Implement circuit breakers that can pause liquidations during extreme market volatility or if oracle prices deviate significantly from trusted secondary sources.

Economic security relies on proper incentive alignment. The liquidationBonus must be sufficient to cover the liquidator's gas costs and provide profit, especially on L2s or during network congestion, but not so large it encourages predatory behavior. Consider implementing a dynamic bonus that scales with the size of the position or network gas prices. Furthermore, design the system to withstand liquidation cascades, where one large liquidation affects collateral prices and triggers further liquidations, potentially destabilizing the protocol.

Operational considerations are often overlooked. Maintain clear, real-time subgraph indexing or an API for keepers to efficiently query underwater positions. Use event emissions liberally in your contract to log key data (e.g., LiquidationExecuted(user, collateralSeized, debtRepaid, liquidator) ) for off-chain monitoring and analytics. Plan for upgradeability and parameter tuning—factors like liquidationThreshold, closeFactor (the maximum percentage of a debt that can be repaid in one liquidation), and bonus rates may need adjustment post-deployment.

Finally, learn from existing implementations. Study the approaches of major protocols like Aave, Compound, and MakerDAO. Aave V3 uses a healthFactor and allows partial liquidations. Compound's Comptroller model centralizes risk logic. MakerDAO's system for vaults involves auctions. Each has faced and adapted to challenges, providing valuable blueprints for robustness and resilience in your own design.