Skip to main content

Research: Liquidation Mechanism Design

Author: Research Date: 2026-06-22 Status: Complete

Overview

Liquidations are the safety mechanism that keeps PrivateLend solvent. When a user’s BTC collateral drops in value and their loan becomes undercollateralized, a liquidation must occur quickly to protect the USDC lenders. The challenge for Writz: how do you liquidate a private position? The collateral amount and debt are hidden by ZK proofs. Liquidators can’t see who is undercollateralized. This document designs the full liquidation system, including the novel ZK-private liquidation mechanism.

Standard DeFi Liquidation (Public Protocols)

In Aave, Compound, and Blend, liquidation works as follows:
  1. Any position is publicly visible: collateral amount, debt, health factor
  2. When health factor < 1.0, anyone can call liquidate(position_id)
  3. The liquidator pays some or all of the debt in USDC
  4. The liquidator receives the BTC collateral at a discount (liquidation bonus)
  5. The position is closed or partially reduced
Why this is easy in public DeFi: Every bot can scan every position continuously. When a position becomes liquidatable, multiple liquidators compete (race condition), ensuring rapid liquidation. Why this breaks for Writz: Position data is hidden. Liquidators cannot scan positions.

The Private Liquidation Problem

Writz stores positions as ZK commitments:
On-chain, only the commitment hash is stored — not the collateral or debt amounts. A liquidation bot cannot determine which positions are undercollateralized by reading the chain.

Solutions

Option A: Privileged Keeper (Operator)

A trusted Writz operator holds the decryption keys for all positions and monitors them privately. When a position becomes liquidatable, the keeper generates a ZK proof and initiates liquidation. Pros: Simplest to implement. Users trust the protocol operator. Cons: Centralization. If the keeper is offline, positions go unliquidated. If the keeper is malicious, they can selectively trigger or delay liquidations. Verdict: Acceptable for Phase 1 (alongside a timelock-based safety mechanism), but must be decentralized by Phase 2.

Option B: User Self-Reporting

When a position becomes liquidatable, the protocol emits no signal. Users must periodically submit ZK proofs proving their position is healthy. If a user fails to prove health within a window, the position is assumed liquidatable. Pros: Fully decentralized. No trusted party needed. Cons: Terrible UX. Users must monitor and act regularly or lose their collateral. Not viable for mainstream adoption. Verdict: Not recommended. A keeper (or any party with position knowledge) generates a ZK proof that a specific position is below the liquidation threshold — without revealing the actual collateral amount or debt. ZK proof statement:
The verifier (Soroban contract) checks the ZK proof using the current oracle price. If valid, it triggers the liquidation of the commitment — releasing the BTC co-sign to the liquidator and burning the USDC debt — without ever revealing the collateral amount or who the position belongs to. Pros: Decentralized and privacy-preserving. Any party can liquidate if they can prove undercollateralization. Cons: The prover must know the position’s preimage. This requires the keeper (or the user) to share the position details. In practice, the Writz keeper tracks all positions in encrypted off-chain storage.

Phase 1: Keeper + Emergency Timelock

Emergency fallback mechanism — the two lending contracts differ here, by design:
  1. commitment-tree::liquidate — Writz’s ZK liquidation path — needs no fallback at all. It is fully permissionless from the start: it requires only keeper.require_auth() (the caller authorizing their own USDC payment) plus a valid Groth16 undercollateralization proof, with no keeper-address check to fall back from. “Anyone who can submit a valid ZK proof” already describes its normal operating mode, not an emergency-only path.
  2. private-lend::liquidate — the plaintext (non-ZK) lending contract — restricts liquidation to a designated keeper by default, with a time-based fallback: Config.keeper_stale_after_secs (default 86,400 = 24h) and ProtocolState.last_keeper_heartbeat. If the designated keeper hasn’t successfully liquidated or called keeper_heartbeat within that window, liquidate opens to any caller who can satisfy the (caller-independent) undercollateralization check. See contracts/contracts/private-lend/src/lib.rs.

Phase 2: Decentralized Keeper Network

Multiple keeper nodes compete to submit liquidation proofs. The first valid proof wins. Keepers are incentivized by the liquidation bonus. A stake/bond mechanism ensures keepers don’t collude to delay liquidations.

Liquidation Parameters

Collateralization ratios

Example liquidation scenario

Partial liquidations

For large positions, full liquidation in one transaction may not be practical. Writz supports partial liquidations — a liquidator pays some of the debt and receives proportional collateral. Minimum liquidation amount: 10% of outstanding debt per liquidation call. This prevents dust liquidations that waste gas without meaningful risk reduction.

Liquidation and the ZK Circuit

The liquidation Circom circuit must prove:
Key circuit challenge: Division and comparison (ratio < threshold) are expensive in ZK circuits because they require range proofs. This will be one of the heavier operations in Writz’s circuit design. Benchmark this specifically in Phase 1.

Liquidation UX for Users

Users should be notified well before liquidation:
  • 150% → 140%: Warning notification (email, in-app)
  • 140% → 130%: Urgent notification with one-click repay button
  • 130% → 120%: Critical alert — liquidation imminent
  • < 120%: Keeper initiates liquidation
The Writz frontend should show users their health factor in real-time using their locally-stored position secret (the frontend knows the position details even though the chain doesn’t).
Last updated: 2026-06-22 Sources: What is Health Factor in DeFi — Otomato · ZK Lending on Cardano — Catalyst · Aave Liquidation Mechanism