Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Certora] Complete liveness #467

Merged
merged 1 commit into from
Sep 7, 2023
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
121 changes: 118 additions & 3 deletions certora/specs/Liveness.spec
Original file line number Diff line number Diff line change
Expand Up @@ -50,11 +50,11 @@ definition isCreated(MorphoInternalAccess.Id id) returns bool =
lastUpdate(id) != 0;

// Check that tokens and shares are properly accounted following a supply.
rule supplyMovesTokensAndIncreasesShares(env e, MorphoInternalAccess.MarketParams marketParams, uint256 assets, uint256 shares, address onBehalf, bytes data) {
rule supplyChangesTokensAndShares(env e, MorphoInternalAccess.MarketParams marketParams, uint256 assets, uint256 shares, address onBehalf, bytes data) {
MorphoInternalAccess.Id id = libId(marketParams);

// Safe require that Morpho is not the sender.
require e.msg.sender != currentContract;
// Require that Morpho is not the sender.
require currentContract != e.msg.sender;
// Ensure that no interest is accumulated.
require lastUpdate(id) == e.block.timestamp;

Expand All @@ -74,6 +74,121 @@ rule supplyMovesTokensAndIncreasesShares(env e, MorphoInternalAccess.MarketParam
assert balanceAfter == balanceBefore + suppliedAssets;
}

// Check that tokens and shares are properly accounted following a withdraw.
rule withdrawChangesTokensAndShares(env e, MorphoInternalAccess.MarketParams marketParams, uint256 assets, uint256 shares, address onBehalf, address receiver) {
MorphoInternalAccess.Id id = libId(marketParams);

// Require that Morpho is not the receiver.
require currentContract != receiver;
// Ensure that no interest is accumulated.
require lastUpdate(id) == e.block.timestamp;

mathint sharesBefore = supplyShares(id, onBehalf);
mathint balanceBefore = myBalances[marketParams.borrowableToken];

uint256 withdrawnAssets;
uint256 withdrawnShares;
withdrawnAssets, withdrawnShares = withdraw(e, marketParams, assets, shares, onBehalf, receiver);

mathint sharesAfter = supplyShares(id, onBehalf);
mathint balanceAfter = myBalances[marketParams.borrowableToken];

assert assets != 0 => withdrawnAssets == assets;
assert assets == 0 => withdrawnShares == shares;
assert sharesAfter == sharesBefore - withdrawnShares;
assert balanceAfter == balanceBefore - withdrawnAssets;
}

// Check that tokens and shares are properly accounted following a borrow.
rule borrowChangesTokensAndShares(env e, MorphoInternalAccess.MarketParams marketParams, uint256 assets, uint256 shares, address onBehalf, address receiver) {
MorphoInternalAccess.Id id = libId(marketParams);

// Require that Morpho is not the receiver.
require currentContract != receiver;
// Ensure that no interest is accumulated.
require lastUpdate(id) == e.block.timestamp;

mathint sharesBefore = borrowShares(id, onBehalf);
mathint balanceBefore = myBalances[marketParams.borrowableToken];

uint256 borrowedAssets;
uint256 borrowedShares;
borrowedAssets, borrowedShares = borrow(e, marketParams, assets, shares, onBehalf, receiver);

mathint sharesAfter = borrowShares(id, onBehalf);
mathint balanceAfter = myBalances[marketParams.borrowableToken];

assert assets != 0 => borrowedAssets == assets;
assert assets == 0 => borrowedShares == shares;
assert sharesAfter == sharesBefore + borrowedShares;
assert balanceAfter == balanceBefore - borrowedAssets;
}

// Check that tokens and shares are properly accounted following a repay.
rule repayChangesTokensAndShares(env e, MorphoInternalAccess.MarketParams marketParams, uint256 assets, uint256 shares, address onBehalf, bytes data) {
MorphoInternalAccess.Id id = libId(marketParams);

// Require that Morpho is not the sender.
require currentContract != e.msg.sender;
// Ensure that no interest is accumulated.
require lastUpdate(id) == e.block.timestamp;

mathint sharesBefore = borrowShares(id, onBehalf);
mathint balanceBefore = myBalances[marketParams.borrowableToken];

uint256 repaidAssets;
uint256 repaidShares;
repaidAssets, repaidShares = repay(e, marketParams, assets, shares, onBehalf, data);

mathint sharesAfter = borrowShares(id, onBehalf);
mathint balanceAfter = myBalances[marketParams.borrowableToken];

assert assets != 0 => repaidAssets == assets;
assert assets == 0 => repaidShares == shares;
assert sharesAfter == sharesBefore - repaidShares;
assert balanceAfter == balanceBefore + repaidAssets;
}

// Check that tokens and balances are properly accounted following a supplyCollateral.
rule supplyCollateralChangesTokensAndBalance(env e, MorphoInternalAccess.MarketParams marketParams, uint256 assets, address onBehalf, bytes data) {
MorphoInternalAccess.Id id = libId(marketParams);

// Require that Morpho is not the sender.
require currentContract != e.msg.sender;

mathint collateralBefore = collateral(id, onBehalf);
mathint balanceBefore = myBalances[marketParams.collateralToken];

supplyCollateral(e, marketParams, assets, onBehalf, data);

mathint collateralAfter = collateral(id, onBehalf);
mathint balanceAfter = myBalances[marketParams.collateralToken];

assert collateralAfter == collateralBefore + assets;
assert balanceAfter == balanceBefore + assets;
}

// Check that tokens and balances are properly accounted following a withdrawCollateral.
rule withdrawCollateralChangesTokensAndBalance(env e, MorphoInternalAccess.MarketParams marketParams, uint256 assets, address onBehalf, address receiver) {
MorphoInternalAccess.Id id = libId(marketParams);

// Require that Morpho is not the receiver.
require currentContract != receiver;
// Ensure that no interest is accumulated.
require lastUpdate(id) == e.block.timestamp;

mathint collateralBefore = collateral(id, onBehalf);
mathint balanceBefore = myBalances[marketParams.collateralToken];

withdrawCollateral(e, marketParams, assets, onBehalf, receiver);

mathint collateralAfter = collateral(id, onBehalf);
mathint balanceAfter = myBalances[marketParams.collateralToken];

assert collateralAfter == collateralBefore - assets;
assert balanceAfter == balanceBefore - assets;
}

// This rule is commented out for the moment because of a bug in CVL where market IDs are not consistent accross a run.
// Check that one can always repay the debt in full.
// rule canRepayAll(env e, MorphoInternalAccess.MarketParams marketParams, uint256 shares, bytes data) {
Expand Down