diff --git a/certora/confs/AccrueInterest.conf b/certora/confs/AccrueInterest.conf index 8f8c26d72..3ae9ec84b 100644 --- a/certora/confs/AccrueInterest.conf +++ b/certora/confs/AccrueInterest.conf @@ -1,12 +1,12 @@ { "files": [ - "certora/harness/MorphoHarness.sol" + "certora/harness/MorphoHarness.sol", ], "verify": "MorphoHarness:certora/specs/AccrueInterest.spec", "prover_args": [ "-depth 3", "-smt_hashingScheme plaininjectivity", - "-mediumTimeout 30" + "-mediumTimeout 30", ], "rule_sanity": "basic", "server": "production", diff --git a/certora/confs/AssetsAccounting.conf b/certora/confs/AssetsAccounting.conf index 6dcd57dc6..e8a011349 100644 --- a/certora/confs/AssetsAccounting.conf +++ b/certora/confs/AssetsAccounting.conf @@ -1,6 +1,6 @@ { "files": [ - "certora/harness/MorphoHarness.sol" + "certora/harness/MorphoHarness.sol", ], "verify": "MorphoHarness:certora/specs/AssetsAccounting.spec", "rule_sanity": "basic", diff --git a/certora/confs/ConsistentState.conf b/certora/confs/ConsistentState.conf index fdfa6e17a..45c0c672f 100644 --- a/certora/confs/ConsistentState.conf +++ b/certora/confs/ConsistentState.conf @@ -1,6 +1,6 @@ { "files": [ - "certora/harness/MorphoHarness.sol" + "certora/harness/MorphoHarness.sol", ], "verify": "MorphoHarness:certora/specs/ConsistentState.spec", "rule_sanity": "basic", diff --git a/certora/confs/ExactMath.conf b/certora/confs/ExactMath.conf index 947e98bb4..35d755e27 100644 --- a/certora/confs/ExactMath.conf +++ b/certora/confs/ExactMath.conf @@ -1,12 +1,13 @@ { "files": [ - "certora/harness/MorphoHarness.sol" + "certora/harness/MorphoHarness.sol", ], "verify": "MorphoHarness:certora/specs/ExactMath.spec", "rule_sanity": "basic", "prover_args": [ + "-depth 3", "-smt_hashingScheme plaininjectivity", - "-mediumTimeout 30" + "-mediumTimeout 30", ], "server": "production", "msg": "Morpho Blue Exact Math" diff --git a/certora/confs/Health.conf b/certora/confs/Health.conf index f361bf43d..9238fde6b 100644 --- a/certora/confs/Health.conf +++ b/certora/confs/Health.conf @@ -6,7 +6,7 @@ "verify": "MorphoHarness:certora/specs/Health.spec", "rule_sanity": "basic", "prover_args": [ - "-smt_hashingScheme plaininjectivity" + "-smt_hashingScheme plaininjectivity", ], "server": "production", "msg": "Morpho Blue Health" diff --git a/certora/confs/LibSummary.conf b/certora/confs/LibSummary.conf index f931810eb..6f944ed00 100644 --- a/certora/confs/LibSummary.conf +++ b/certora/confs/LibSummary.conf @@ -1,6 +1,6 @@ { "files": [ - "certora/harness/MorphoHarness.sol" + "certora/harness/MorphoHarness.sol", ], "verify": "MorphoHarness:certora/specs/LibSummary.spec", "rule_sanity": "basic", diff --git a/certora/confs/Liveness.conf b/certora/confs/Liveness.conf index 211a0897a..b3788c685 100644 --- a/certora/confs/Liveness.conf +++ b/certora/confs/Liveness.conf @@ -1,6 +1,6 @@ { "files": [ - "certora/harness/MorphoInternalAccess.sol" + "certora/harness/MorphoInternalAccess.sol", ], "verify": "MorphoInternalAccess:certora/specs/Liveness.spec", "rule_sanity": "basic", diff --git a/certora/confs/RatioMath.conf b/certora/confs/RatioMath.conf index 9062f1b14..a0ac6df8b 100644 --- a/certora/confs/RatioMath.conf +++ b/certora/confs/RatioMath.conf @@ -1,13 +1,13 @@ { "files": [ - "certora/harness/MorphoHarness.sol" + "certora/harness/MorphoHarness.sol", ], "verify": "MorphoHarness:certora/specs/RatioMath.spec", "rule_sanity": "basic", "prover_args": [ "-smt_hashingScheme plaininjectivity", "-mediumTimeout 30", - "-timeout 3600" + "-timeout 3600", ], "server": "production", "msg": "Morpho Blue Ratio Math" diff --git a/certora/confs/Reentrancy.conf b/certora/confs/Reentrancy.conf index 84019dceb..db93b6a23 100644 --- a/certora/confs/Reentrancy.conf +++ b/certora/confs/Reentrancy.conf @@ -1,11 +1,11 @@ { "files": [ - "certora/harness/MorphoHarness.sol" + "certora/harness/MorphoHarness.sol", ], "verify": "MorphoHarness:certora/specs/Reentrancy.spec", "rule_sanity": "basic", "prover_args": [ - "-enableStorageSplitting false" + "-enableStorageSplitting false", ], "server": "production", "msg": "Morpho Blue Reentrancy" diff --git a/certora/confs/Reverts.conf b/certora/confs/Reverts.conf index 6992b6b4b..a31c697d4 100644 --- a/certora/confs/Reverts.conf +++ b/certora/confs/Reverts.conf @@ -1,6 +1,6 @@ { "files": [ - "certora/harness/MorphoHarness.sol" + "certora/harness/MorphoHarness.sol", ], "verify": "MorphoHarness:certora/specs/Reverts.spec", "rule_sanity": "basic", diff --git a/certora/confs/Transfer.conf b/certora/confs/Transfer.conf index 6425f56a5..bedba566c 100644 --- a/certora/confs/Transfer.conf +++ b/certora/confs/Transfer.conf @@ -3,7 +3,7 @@ "certora/harness/TransferHarness.sol", "certora/dispatch/ERC20Standard.sol", "certora/dispatch/ERC20USDT.sol", - "certora/dispatch/ERC20NoRevert.sol" + "certora/dispatch/ERC20NoRevert.sol", ], "verify": "TransferHarness:certora/specs/Transfer.spec", "rule_sanity": "basic", diff --git a/certora/specs/Health.spec b/certora/specs/Health.spec index 00221d026..45c535187 100644 --- a/certora/specs/Health.spec +++ b/certora/specs/Health.spec @@ -7,6 +7,7 @@ methods { function borrowShares(MorphoHarness.Id, address) external returns uint256 envfree; function collateral(MorphoHarness.Id, address) external returns uint256 envfree; function isAuthorized(address, address user) external returns bool envfree; + function lastUpdate(MorphoHarness.Id) external returns uint256 envfree; function libId(MorphoHarness.MarketParams) external returns MorphoHarness.Id envfree; function isHealthy(MorphoHarness.MarketParams, address user) external returns bool envfree; @@ -21,12 +22,12 @@ persistent ghost uint256 lastPrice; persistent ghost bool priceChanged; function mockPrice() returns uint256 { - uint256 somePrice; - if (somePrice != lastPrice) { + uint256 updatedPrice; + if (updatedPrice != lastPrice) { priceChanged = true; - lastPrice = somePrice; + lastPrice = updatedPrice; } - return somePrice; + return updatedPrice; } function summaryMulDivUp(uint256 x, uint256 y, uint256 d) returns uint256 { @@ -106,3 +107,28 @@ filtered { f -> !f.isView } // This invariant ensures that bad debt realization cannot be bypassed. invariant alwaysCollateralized(MorphoHarness.Id id, address borrower) borrowShares(id, borrower) != 0 => collateral(id, borrower) != 0; + +// Checks that passing a seized amount input to liquidate leads to repaid shares S and repaid amount A such that liquidating instead with shares S also repays the amount A. +rule liquidateEquivalentInputDebtAndInputCollateral(env e, MorphoHarness.MarketParams marketParams, address borrower, uint256 seizedAssets, bytes data) { + MorphoHarness.Id id = libId(marketParams); + + // Assume no interest accrual to ease the verification. + require lastUpdate(id) == e.block.timestamp; + + storage init = lastStorage; + uint256 sharesBefore = borrowShares(id, borrower); + + uint256 repaidAssets1; + _, repaidAssets1 = liquidate(e, marketParams, borrower, seizedAssets, 0, data); + require !priceChanged; + // Omit the bad debt realization case. + require collateral(id, borrower) != 0; + uint256 sharesAfter = borrowShares(id, borrower); + uint256 repaidShares1 = assert_uint256(sharesBefore - sharesAfter); + + uint256 repaidAssets2; + _, repaidAssets2 = liquidate(e, marketParams, borrower, 0, repaidShares1, data) at init; + require !priceChanged; + + assert repaidAssets1 == repaidAssets2; +}