Skip to content

[Certora] liquidation liveness#834

Open
lilCertora wants to merge 84 commits into
mainfrom
certora/liquidationLiveness
Open

[Certora] liquidation liveness#834
lilCertora wants to merge 84 commits into
mainfrom
certora/liquidationLiveness

Conversation

@lilCertora

Copy link
Copy Markdown
Collaborator

No description provided.

@lilCertora lilCertora self-assigned this May 17, 2026
@lilCertora
lilCertora marked this pull request as ready for review June 7, 2026 17:52

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: cd6315e477

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread certora/specs/LiquidateLiveness.spec Outdated
Comment thread certora/confs/LiquidateLiveness.conf

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: bc11ba8549

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread certora/specs/LiquidateLiveness.spec
@lilCertora
lilCertora requested a review from jhoenicke June 9, 2026 11:40

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 729f46d510

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread certora/specs/LiquidateLiveness.spec

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b926c65cce

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread certora/specs/LiquidateLiveness.spec Outdated
Comment on lines +168 to +170
mathint recovery0 = ghostMulDivUp(ghostMulDivUp(collateral(id, borrower, 0), ghostPrice(market.collateralParams[0].oracle), ORACLE_PRICE_SCALE()), WAD(), market.collateralParams[0].maxLif);
mathint recovery1 = ghostMulDivUp(ghostMulDivUp(collateral(id, borrower, 1), ghostPrice(market.collateralParams[1].oracle), ORACLE_PRICE_SCALE()), WAD(), market.collateralParams[1].maxLif);
mathint recovery2 = ghostMulDivUp(ghostMulDivUp(collateral(id, borrower, 2), ghostPrice(market.collateralParams[2].oracle), ORACLE_PRICE_SCALE()), WAD(), market.collateralParams[2].maxLif);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Tighten recovery mulDivUp before computing debtAfter

When a collateral price is below ORACLE_PRICE_SCALE, the current ghostMulDivUp axioms can model mulDivUp(collateral, price, ORACLE_PRICE_SCALE) as any value up to the full collateral amount, far above the real ceil result. Separate from the earlier RCF over-approximation thread, this value feeds the bad-debt recovery here, so debtAfterBadDebt can equal _debt even when the real liquidate would realize bad debt; then the badDebtCanBeLiquidated implication is skipped instead of checking the promised debt drop. Add the mulDivUpTightBound/upper-bound axiom before using this recovery reconstruction.

Useful? React with 👍 / 👎.

@jhoenicke

Copy link
Copy Markdown
Collaborator

I replace some require by assert here: #1017. There are still two requires left, which should in principle also be provable.

Otherwise, that looks good. I think all cases are covered except one minor special case: You can liquidate more than maxRepaid if only dust ist left. I don't think that is important enough.

}

function summaryMulDivDown(uint256 x, uint256 y, uint256 d) returns uint256 {
if (d == 0) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

An assumption we are making here is that there is no overflow, I think it's worth mentioning

uint256 lltv = market.collateralParams[i].lltv;
uint256 maxLif = market.collateralParams[i].maxLif;

require lltv > 0 && lltv <= WAD(), "lltv in (0, WAD] for a created market (in CreatedMarkets.spec)";

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

it does not prove that lltv > 0 right ?
What breaks if we don't have this assumption ?


require lltv > 0 && lltv <= WAD(), "lltv in (0, WAD] for a created market (in CreatedMarkets.spec)";
require maxLif >= WAD(), "maxLif >= WAD (maxLifIsAtLeastWad)";
require lltv < WAD() => lltv * maxLif <= WAD() * (WAD() - 1), "lltv < WAD => lltv*maxLif <= WAD*(WAD-1) (lifTimesLltvStrictBound)";

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

we don't have the lifTimesLltvStrictBound rule in main anymore, we should add it back (feasible because we kept the invariant liquidationCursor < WAD)

threeCollatSetup(e, market, id, borrower);
require collateralIndex == 0 || collateralIndex == 1 || collateralIndex == 2, "seized index in {0,1,2} (<= loop_iter)";
require summaryGetBit(collateralBitmap(id, borrower), collateralIndex), "the seized collateral is active";
require getPrice(collateralIndex, market.collateralParams) > 0, "the seized collateral is priced (LIVENESS)";

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

this should be added to the list of assumptions at the top

uint256 maxLifJ = getMaxLif(collateralIndex, market.collateralParams);
uint256 priceJ = getPrice(collateralIndex, market.collateralParams);
uint128 collatJ = collateral(id, borrower, collateralIndex);
assert maxLifJ * lltvJ <= WAD() * (WAD() - 1), "maxLif*lltv <= WAD*(WAD-1) (lifTimesLltvStrictBound) => WAD*WAD - maxLif*lltv >= WAD >= 1";

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

same, lifTimesLltvStrictBound doesn't exist anymore, although here it's an assert. This is a bit suspicious because you need to know that the liquidation cursor is < WAD

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