Skip to content

Liquidation Mechanism: Phases 1 & 2 (core + DEX)#41

Merged
kgrgpg merged 61 commits intomainfrom
feature/liquidation-mechanism
Sep 30, 2025
Merged

Liquidation Mechanism: Phases 1 & 2 (core + DEX)#41
kgrgpg merged 61 commits intomainfrom
feature/liquidation-mechanism

Conversation

@kgrgpg
Copy link
Contributor

@kgrgpg kgrgpg commented Aug 27, 2025

Summary

This PR introduces the liquidation system to Tidal Protocol and now includes both Phase 1 (core repay‑for‑seize) and Phase 2 (DEX‑based liquidation). In plain terms: when a position becomes unsafe, a keeper can repay part of the borrower’s debt and take some collateral so the position is brought back to a safe level.

What’s new here compared to the main branch

  • Core liquidation engine that tells you exactly how much to repay and how much collateral to seize to restore the health factor to ~1.05 (the target safety level).
  • A transaction to execute liquidations safely: it only uses the required repay amount, ignores any extra you try to send, and enforces min/max constraints.
  • Optional DEX liquidation path with guardrails so keepers can swap collateral to repay debt:
    • Oracle vs DEX price‑deviation guard
    • Slippage checks
    • Allow‑list of permitted swapper types
  • Governance controls to configure the DEX guardrails and allowed swappers.
  • Public scripts to quote liquidations and to read liquidation/DEX settings.
  • A design document explaining the mechanism.

Safety guardrails and behavior

  • No liquidation is allowed if the position is already healthy.
  • Overpayment isn’t honored; only the quoted required amount is consumed.
  • Slippage and price‑deviation checks prevent bad DEX fills.
  • All DEX settings are governed and can be tightened or disabled.

Tests and status

  • Comprehensive tests for Phase 1 and DEX paths (healthy cases, insolvency improvement, overpay behavior, slippage/guards).
  • All Cadence tests pass locally via ./run_tests.sh.

Note on PRs

kgrgpg added 8 commits August 20, 2025 12:36
…oteLiquidation, liquidateRepayForSeize; add scripts and tests
- Update cadence/contracts/TidalProtocol.cdc
- Update cadence/tests/liquidation_phase1_test.cdc
- Update cadence/transactions/tidal-protocol/pool-management/liquidate_repay_for_seize.cdc
- Update LIQUIDATION_MECHANISM_DESIGN.md
Copy link
Collaborator

@nialexsan nialexsan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've realized that I need these functions in the user initialized tide closure to capture loss


/// Liquidation quote output
access(all) struct LiquidationQuote {
access(all) let requiredRepay: UFix64
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we need a repay type, or is it safe to assume that the type is always MOET?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@nialexsan Not always MOET — the repay token is whatever the position’s debt token is. We pass the debt type into both quote and execution, so the quote doesn’t need to repeat it (it would be redundant).

…n- Add allowedSwapperTypes + dexOracleDeviationBps with getters/governance setter\n- Implement liquidateViaDex with oracle dev guard + slippage minRepay\n- Add get_dex_liquidation_config script\n- Add set_dex_liquidation_config governance tx\n- Add liquidate_via_dex management tx
… + separate DEX test\n- Pool.liquidateViaDex takes Swapper, adds oracle/slippage/hops guards\n- Add Pool.internalSeize/internalRepay (resource)\n- Add MockDexSwapper + liquidate_via_mock_dex tx\n- Move DEX test into liquidation_phase2_dex_test.cdc; remove from phase1
Tests: add snapshot-based safe reset and handle zero-quote insolvency/rounding edges. Docs: clarify repay-all redemption, partial-to-above-one, and insolvency scenarios. Contract: quoteLiquidation rounds up and avoids HF-worsening keeper steps with zero-quote/full-seize fallback.
…allback in quoteLiquidation

Tests: add insolvency_redemption_test.cdc; update repay_and_close_position.cdc to repay then withdraw.\nContract: discrete bounded-search fallback (~16 steps) in quoteLiquidation when direct solve would worsen HF; preserves determinism and caps.
@kgrgpg kgrgpg changed the title Liquidation Mechanism: Phase 1 implementation and tests Liquidation Mechanism: Phases 1 & 2 (core + DEX) Sep 3, 2025
…on struct sinks/sources for CLI compat"

This reverts commit 664e165.
…s and insolvency test; keep repay flow using withdraw path
…ToConsumer(); repay via sink.depositCapacity to avoid EParticipant
…rage path; all tests pass locally with flow test
newProtocolFee = protocolFeeBps!
}
emit LiquidationParamsUpdated(poolUUID: self.uuid)
emit LiquidationParamsUpdatedV2(poolUUID: self.uuid, targetHF: newTarget, warmupSec: newWarmup, protocolFeeBps: newProtocolFee)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should consolidate these to a single event

Comment on lines 29 to 36
access(all) event LiquidationParamsUpdated(poolUUID: UInt64)
// Backward-compatible v2 event including updated params for observability
access(all) event LiquidationParamsUpdatedV2(
poolUUID: UInt64,
targetHF: UInt128,
warmupSec: UInt64,
protocolFeeBps: UInt16
)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same suggestion as elsewhere - we should consolidate these events.

Comment on lines 1348 to 1351
access(self) fun internalRepayInferred(pid: UInt64, from: @{FungibleToken.Vault}): UFix64 {
let inferredType = from.getType()
return self.internalRepay(pid: pid, debtType: inferredType, from: <-from)
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would think based on the assertions in internalRepay we can just update internalRepay to this function signature to avoid this extra passthrough method.

Comment on lines 2189 to 2209
access(EGovernance) fun addSupportedTokenWithLiquidationBonus(
tokenType: Type,
collateralFactor: UFix64,
borrowFactor: UFix64,
interestCurve: {InterestCurve},
depositRate: UFix64,
depositCapacityCap: UFix64,
liquidationBonus: UFix64?
) {
self.addSupportedToken(
tokenType: tokenType,
collateralFactor: collateralFactor,
borrowFactor: borrowFactor,
interestCurve: interestCurve,
depositRate: depositRate,
depositCapacityCap: depositCapacityCap
)
if liquidationBonus != nil {
self.setTokenLiquidationBonus(tokenType: tokenType, bonus: liquidationBonus!)
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can be consolidated with addSupportedToken

Copy link
Contributor

@sisyphusSmiling sisyphusSmiling left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changes in this PR were quite hard to follow without conversation in or author resolution of the previous comments. I understand the desire to use AI tooling to address changes, but I don't think choice of tooling should drive PR practice. In future PRs, it would be helpful as a reviewer to use inline comments to track discussion and progress on the topics?

I left comments under outstanding items I think should be addressed before merging.

@kgrgpg
Copy link
Contributor Author

kgrgpg commented Sep 29, 2025

@sisyphusSmiling Thanks for the review. I will address the new items before merging.

I understand the inconvenience faced by the reviewer if inline comments are not used. Indeed, using Github CLI via AI is not able to tap into the inline comments and code referencing. I have been actively looking for this soultion. In any case, I will ensure better reviewer experience in the future.

…uidationParamsUpdated with params; simplify internalRepay to infer type and remove passthrough; remove addSupportedTokenWithLiquidationBonus in favor of addSupportedToken + setTokenLiquidationBonus; update design doc event signature.
@kgrgpg
Copy link
Contributor Author

kgrgpg commented Sep 30, 2025

Addressed the outstanding review items:

  • Consolidated liquidation events: replaced the pair (LiquidationParamsUpdated + LiquidationParamsUpdatedV2) with a single event LiquidationParamsUpdated(poolUUID, targetHF, warmupSec, protocolFeeBps). Updated emit sites and LIQUIDATION_MECHANISM_DESIGN.md accordingly. Note for consumers: update any off-chain listeners to the new signature.
  • Simplified repay path: removed internalRepayInferred; internalRepay(pid, from) now infers the type from the passed-in vault. Updated DEX liquidation call site.
  • Consolidated token support helpers: removed addSupportedTokenWithLiquidationBonus. Keep addSupportedToken (defaults to 5% liquidation bonus). For custom per-token bonuses, call setTokenLiquidationBonus after adding the token.

Search shows no scripts/txs referencing the removed event or helper names. All Cadence tests pass locally via ./run_tests.sh.

Pushed to the PR head branch feature/liquidation-mechanism in commit 42c7c4a.

@kgrgpg kgrgpg merged commit 93229e7 into main Sep 30, 2025
2 checks passed
@kgrgpg kgrgpg deleted the feature/liquidation-mechanism branch September 30, 2025 09:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants