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

Fix invariants in CI #706

Merged
merged 13 commits into from
Nov 25, 2024
4 changes: 1 addition & 3 deletions test/forge/InvariantTest.sol
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@ contract InvariantTest is BaseTest {

_targetSenders();

_weightSelector(this.mine.selector, 100);

targetContract(address(this));
targetSelector(FuzzSelector({addr: address(this), selectors: selectors}));
}
Expand Down Expand Up @@ -58,7 +56,7 @@ contract InvariantTest is BaseTest {
/* HANDLERS */

function mine(uint256 blocks) external {
blocks = bound(blocks, 1, 50_400);
blocks = bound(blocks, 1, 7 days / BLOCK_TIME);

_forward(blocks);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,13 @@ pragma solidity ^0.8.0;

import "../InvariantTest.sol";

contract MorphoInvariantTest is InvariantTest {
contract BaseMorphoInvariantTest is InvariantTest {
using MathLib for uint256;
using SharesMathLib for uint256;
using MorphoLib for IMorpho;
using MorphoBalancesLib for IMorpho;
using MarketParamsLib for MarketParams;

uint256 internal immutable MIN_PRICE = ORACLE_PRICE_SCALE / 10;
uint256 internal immutable MAX_PRICE = ORACLE_PRICE_SCALE * 10;

address internal immutable USER;

MarketParams[] internal allMarketParams;
Expand All @@ -22,19 +19,6 @@ contract MorphoInvariantTest is InvariantTest {
}

function setUp() public virtual override {
_weightSelector(this.setPrice.selector, 10);
_weightSelector(this.setFeeNoRevert.selector, 5);
_weightSelector(this.supplyAssetsOnBehalfNoRevert.selector, 100);
_weightSelector(this.supplySharesOnBehalfNoRevert.selector, 100);
_weightSelector(this.withdrawAssetsOnBehalfNoRevert.selector, 50);
_weightSelector(this.borrowAssetsOnBehalfNoRevert.selector, 75);
_weightSelector(this.repayAssetsOnBehalfNoRevert.selector, 35);
_weightSelector(this.repaySharesOnBehalfNoRevert.selector, 35);
_weightSelector(this.supplyCollateralOnBehalfNoRevert.selector, 100);
_weightSelector(this.withdrawCollateralOnBehalfNoRevert.selector, 50);
_weightSelector(this.liquidateSeizedAssetsNoRevert.selector, 5);
_weightSelector(this.liquidateRepaidSharesNoRevert.selector, 5);

super.setUp();

allMarketParams.push(marketParams);
Expand Down Expand Up @@ -191,12 +175,6 @@ contract MorphoInvariantTest is InvariantTest {

/* HANDLERS */

function setPrice(uint256 price) external {
price = bound(price, MIN_PRICE, MAX_PRICE);

oracle.setPrice(price);
}

function setFeeNoRevert(uint256 marketSeed, uint256 newFee) external {
MarketParams memory _marketParams = _randomMarket(marketSeed);
Id _id = _marketParams.id();
Expand Down Expand Up @@ -339,79 +317,4 @@ contract MorphoInvariantTest is InvariantTest {

_liquidateRepaidShares(_marketParams, borrower, repaidShares);
}

/* INVARIANTS */

function invariantSupplyShares() public {
address[] memory users = targetSenders();

for (uint256 i; i < allMarketParams.length; ++i) {
MarketParams memory _marketParams = allMarketParams[i];
Id _id = _marketParams.id();

uint256 sumSupplyShares = morpho.supplyShares(_id, FEE_RECIPIENT);
for (uint256 j; j < users.length; ++j) {
sumSupplyShares += morpho.supplyShares(_id, users[j]);
}

assertEq(sumSupplyShares, morpho.totalSupplyShares(_id), vm.toString(_marketParams.lltv));
}
}

function invariantBorrowShares() public {
address[] memory users = targetSenders();

for (uint256 i; i < allMarketParams.length; ++i) {
MarketParams memory _marketParams = allMarketParams[i];
Id _id = _marketParams.id();

uint256 sumBorrowShares;
for (uint256 j; j < users.length; ++j) {
sumBorrowShares += morpho.borrowShares(_id, users[j]);
}

assertEq(sumBorrowShares, morpho.totalBorrowShares(_id), vm.toString(_marketParams.lltv));
}
}

function invariantTotalSupplyGeTotalBorrow() public {
for (uint256 i; i < allMarketParams.length; ++i) {
MarketParams memory _marketParams = allMarketParams[i];
Id _id = _marketParams.id();

assertGe(morpho.totalSupplyAssets(_id), morpho.totalBorrowAssets(_id));
}
}

function invariantMorphoBalance() public {
for (uint256 i; i < allMarketParams.length; ++i) {
MarketParams memory _marketParams = allMarketParams[i];
Id _id = _marketParams.id();

assertGe(
loanToken.balanceOf(address(morpho)) + morpho.totalBorrowAssets(_id), morpho.totalSupplyAssets(_id)
);
}
}

function invariantBadDebt() public {
address[] memory users = targetSenders();

for (uint256 i; i < allMarketParams.length; ++i) {
MarketParams memory _marketParams = allMarketParams[i];
Id _id = _marketParams.id();

for (uint256 j; j < users.length; ++j) {
address user = users[j];

if (morpho.collateral(_id, user) == 0) {
assertEq(
morpho.borrowShares(_id, user),
0,
string.concat(vm.toString(_marketParams.lltv), ":", vm.toString(user))
);
}
}
}
}
}
116 changes: 116 additions & 0 deletions test/forge/invariant/MorphoDynamicInvariantTest.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;

import "./BaseMorphoInvariantTest.sol";

contract MorphoDynamicInvariantTest is BaseMorphoInvariantTest {
using MathLib for uint256;
using SharesMathLib for uint256;
using MorphoLib for IMorpho;
using MorphoBalancesLib for IMorpho;
using MarketParamsLib for MarketParams;

uint256 internal immutable MIN_PRICE = ORACLE_PRICE_SCALE / 10;
uint256 internal immutable MAX_PRICE = ORACLE_PRICE_SCALE * 10;

function setUp() public virtual override {
_weightSelector(this.supplyAssetsOnBehalfNoRevert.selector, 15);
_weightSelector(this.supplySharesOnBehalfNoRevert.selector, 5);
_weightSelector(this.withdrawAssetsOnBehalfNoRevert.selector, 10);
_weightSelector(this.borrowAssetsOnBehalfNoRevert.selector, 15);
_weightSelector(this.repayAssetsOnBehalfNoRevert.selector, 10);
_weightSelector(this.repaySharesOnBehalfNoRevert.selector, 10);
_weightSelector(this.supplyCollateralOnBehalfNoRevert.selector, 15);
_weightSelector(this.withdrawCollateralOnBehalfNoRevert.selector, 10);
_weightSelector(this.liquidateSeizedAssetsNoRevert.selector, 2);
_weightSelector(this.liquidateRepaidSharesNoRevert.selector, 2);
_weightSelector(this.setFeeNoRevert.selector, 1);
_weightSelector(this.setPrice.selector, 5);
_weightSelector(this.mine.selector, 100);
QGarchery marked this conversation as resolved.
Show resolved Hide resolved
QGarchery marked this conversation as resolved.
Show resolved Hide resolved

super.setUp();
}

/* HANDLERS */

function setPrice(uint256 price) external {
price = bound(price, MIN_PRICE, MAX_PRICE);

oracle.setPrice(price);
}

/* INVARIANTS */

function invariantSupplyShares() public {
address[] memory users = targetSenders();

for (uint256 i; i < allMarketParams.length; ++i) {
MarketParams memory _marketParams = allMarketParams[i];
Id _id = _marketParams.id();

uint256 sumSupplyShares = morpho.supplyShares(_id, FEE_RECIPIENT);
for (uint256 j; j < users.length; ++j) {
sumSupplyShares += morpho.supplyShares(_id, users[j]);
}

assertEq(sumSupplyShares, morpho.totalSupplyShares(_id), vm.toString(_marketParams.lltv));
}
}

function invariantBorrowShares() public {
address[] memory users = targetSenders();

for (uint256 i; i < allMarketParams.length; ++i) {
MarketParams memory _marketParams = allMarketParams[i];
Id _id = _marketParams.id();

uint256 sumBorrowShares;
for (uint256 j; j < users.length; ++j) {
sumBorrowShares += morpho.borrowShares(_id, users[j]);
}

assertEq(sumBorrowShares, morpho.totalBorrowShares(_id), vm.toString(_marketParams.lltv));
}
}

function invariantTotalSupplyGeTotalBorrow() public {
for (uint256 i; i < allMarketParams.length; ++i) {
MarketParams memory _marketParams = allMarketParams[i];
Id _id = _marketParams.id();

assertGe(morpho.totalSupplyAssets(_id), morpho.totalBorrowAssets(_id));
}
}

function invariantMorphoBalance() public {
for (uint256 i; i < allMarketParams.length; ++i) {
MarketParams memory _marketParams = allMarketParams[i];
Id _id = _marketParams.id();

assertGe(
loanToken.balanceOf(address(morpho)) + morpho.totalBorrowAssets(_id), morpho.totalSupplyAssets(_id)
);
}
}

function invariantBadDebt() public {
address[] memory users = targetSenders();

for (uint256 i; i < allMarketParams.length; ++i) {
MarketParams memory _marketParams = allMarketParams[i];
Id _id = _marketParams.id();

for (uint256 j; j < users.length; ++j) {
address user = users[j];

if (morpho.collateral(_id, user) == 0) {
assertEq(
morpho.borrowShares(_id, user),
0,
string.concat(vm.toString(_marketParams.lltv), ":", vm.toString(user))
);
}
}
}
}
}
40 changes: 40 additions & 0 deletions test/forge/invariant/MorphoStaticInvariantTest.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;

import "./BaseMorphoInvariantTest.sol";

contract MorphoStaticInvariantTest is BaseMorphoInvariantTest {
using MathLib for uint256;
using SharesMathLib for uint256;
using MorphoLib for IMorpho;
using MorphoBalancesLib for IMorpho;
using MarketParamsLib for MarketParams;

function setUp() public virtual override {
_weightSelector(this.supplyAssetsOnBehalfNoRevert.selector, 12);
_weightSelector(this.supplySharesOnBehalfNoRevert.selector, 5);
_weightSelector(this.withdrawAssetsOnBehalfNoRevert.selector, 12);
_weightSelector(this.borrowAssetsOnBehalfNoRevert.selector, 17);
_weightSelector(this.repayAssetsOnBehalfNoRevert.selector, 12);
_weightSelector(this.repaySharesOnBehalfNoRevert.selector, 10);
_weightSelector(this.supplyCollateralOnBehalfNoRevert.selector, 15);
_weightSelector(this.withdrawCollateralOnBehalfNoRevert.selector, 10);
_weightSelector(this.setFeeNoRevert.selector, 2);

super.setUp();
}

/* INVARIANTS */

function invariantHealthy() public {
address[] memory users = targetSenders();

for (uint256 i; i < allMarketParams.length; ++i) {
MarketParams memory _marketParams = allMarketParams[i];

for (uint256 j; j < users.length; ++j) {
assertTrue(_isHealthy(_marketParams, users[j]));
}
}
}
}
Loading