Start a liquidation
Anyone can callliquidate() on an active loan when the debt, priced in the collateral token, exceeds liquidationThreshold * supplyAmount / 1e7. Calling wagers a stake of stake * supplyAmount / 10000 of the supply token on the liquidation succeeding, and spawns the openOracle game whose settled price decides the outcome.
You fund both sides of the initial price report and become the game’s initial reporter at your chosen priceRatio. From that point the position is decided by the oracle game: if your report survives undisputed for settlementTime, it settles at your price; if someone disputes, the game escalates per the openOracle rules. The liquidate() transaction submits the initial report for you. For everything after that, disputes and settlement, see the oracle contract reference (testnet).
Only the final settled price determines the liquidation outcome, not the price at the moment you call. The market can move and disputes track it while the game runs, so a position that is underwater now can settle healthy by the time the game ends. Weigh the probability that the game finishes in a state that pays you out, as a function of the current market price, the collateral equity buffer, and the oracle parameters: your stake is wagered on the settled price, and the equity buffer share (liquidatorFraction / 1e7) is your compensation for that risk as well as the expected losses to your oracle report being disputed after normal market movements.
After verifying the position is underwater at the real market price, check the arrangement state allows the call: active is true, finished, cancelled, and inLiquidation are all false, gracePeriod is zero, and the current time is before start + term. Your own reported price must already imply the position is liquidatable, or the call reverts. While the game runs, the borrower cannot repay or top up collateral.
Review Supported tokens before liquidating: openLend’s and openOracle’s accounting is only compatible with the token classes listed there.
uint256
Unique identifier of the lending arrangement to liquidate.
uint256
Borrow token per supply token in 1e18 fixed-point. Sets the initial reported price: the report’s borrow token side is
initialLiquidity * priceRatio / 1e18. Report the market price; a mispriced report is a profitable swap for anyone else.uint128
Cap on the initial liquidity you are willing to fund (
initialLiquidity * supplyAmount / 100 of the supply token). Reverts if a collateral top-up between transaction broadcast and inclusion grows the requirement past this.bytes32
Required hash pinning the loan’s configuration, in the stricter liquidate variant. See Calculating the liquidate param hash for how to calculate it.
uint256
Reverts if
1e18 * debt / supplyAmount is below this floor. Protects against a repayment or top-up landing first and leaving the position healthier than the one you priced.uint96
Native ETH paid as the openOracle settler reward, sized at your discretion. It pays whoever settles the oracle game, which releases your report liquidity.
address
Address recorded as liquidator. Funds are still pulled from
msg.sender. Stake recovery, the equity buffer share, and the report liquidity all go to this address. Make sure you control the address passed here.uint64
Reverts if the loan’s configured
finalizerReward differs. You escrow exactly the configured amount, and this guards against the configuration changing between transaction broadcast and inclusion.TimingBoundaries
Optional openOracle timing boundaries binding the transaction to the block number and timestamp range you built it for. Validation is skipped entirely when its
blockTimestamp is zero. See timing boundaries in the oracle contract reference (testnet).msg.value alongside the rewards; ERC-20 sides are pulled from msg.sender. Any excess msg.value is refunded to the recorded liquidator.
Resolution and payouts
The liquidation resolves throughfinalize() once the oracle game can no longer be disputed; see Finalize and autoSettle. Debt is measured at finalize time, so it includes interest accrued up to resolution.
If the liquidation succeeds with an equity buffer, you recover your stake plus the buffer share set by the loan’s liquidatorFraction, and the lender receives the rest. If the settled price implies the debt exceeds the collateral entirely, the lender receives all collateral and you recover only your stake.
If the liquidation fails, your stake is forfeited into the loan’s collateral. A liquidation fails in two ways: the settled price implies the debt, converted to supply-token terms at that price, does not exceed liquidationThreshold * supplyAmount / 1e7; or the loan sets a nonzero maxBaseFee and the block base fee at finalize time exceeds maxBaseFee * currentAmount1 / originalAmount1, which restores the loan even at an underwater settled price. See Price oracle for the base fee gate.
Settling the oracle game itself is a separate, permissionless step on the oracle: settle() releases the final reporter’s two-sided report liquidity to their oracle internal balance and pays the settler reward. If your report went undisputed, that returns your initial liquidity. If it was disputed, you swapped one side at your quoted price and must withdraw your resulting balances from the oracle with withdraw().
You also receive a quarter of any oracle game fees collected during the liquidation. See Fee receiver.
If a payout to you fails, for example because the token blacklists the recipient, the amount is credited to a per-recipient escrow. See Recover held tokens.
Native ETH payouts that cannot be delivered directly, for example to a contract that rejects plain ETH transfers, arrive wrapped as WETH instead. For a liquidator this applies to stake recovery and the buffer share on native ETH collateral, excess msg.value refunds, and the finalizer reward. See Receiving WETH.
Calculating the liquidate param hash
liquidate() requires a param hash that pins the loan’s configuration: if the loan’s terms change between transaction broadcast and inclusion, the hash no longer matches and the call reverts instead of liquidating a position you didn’t price.
The hash is keccak256(abi.encode(arrangement)) over the loan’s full LendingArrangement, with two groups of fields zeroed. The first group is the volatile fields: amortization state that legitimately drifts, and liquidation lifecycle state that a pending settle can mutate. Those are instead guarded live at execution by the other transaction parameters, such as worstRatio, maxInitialLiquidity, and expectedFinalizerReward. The second group is the refinance staging surface: refiParams, curveOpen, gasCompensation, and interestRateParams. Zeroing these means a borrower staging or cancelling a refinance cannot invalidate your pending liquidate transaction.
Read the arrangement from lendingArrangements(lendingId) on openLend, or getLending(lendingId) on the data provider, then compute: