From 53318e375cbca68bf919dc55268b1893bfe7b65a Mon Sep 17 00:00:00 2001 From: grandizzy Date: Thu, 7 Dec 2023 11:53:19 +0200 Subject: [PATCH 1/5] Take 1 - invariant code cleanup --- Makefile | 4 +- .../handlers/PanicExitERC20PoolHandler.sol | 8 +- .../handlers/PanicExitERC721PoolHandler.sol | 8 +- .../base/handlers/BasicPoolHandler.sol | 96 +++++--- .../base/handlers/LiquidationPoolHandler.sol | 132 +++++++---- .../base/handlers/ReservePoolHandler.sol | 5 +- .../base/handlers/unbounded/BaseHandler.sol | 113 ++++++++- .../unbounded/UnboundedBasicPoolHandler.sol | 76 +++--- .../UnboundedLiquidationPoolHandler.sol | 219 +++++++++++------- .../unbounded/UnboundedReservePoolHandler.sol | 2 - 10 files changed, 467 insertions(+), 196 deletions(-) diff --git a/Makefile b/Makefile index 8efb142aa..faad73388 100644 --- a/Makefile +++ b/Makefile @@ -52,8 +52,8 @@ test-swap-load-erc20 :; FOUNDRY_INVARIANT_SHRINK_SEQUENCE=false RUST # Regression Tests test-regression-all : test-regression-erc20 test-regression-erc721 test-regression-prototech -test-regression-erc20 :; forge t --mt test_regression --mc ERC20 --nmc "RealWorldRegression|Prototech" -test-regression-erc721 :; forge t --mt test_regression --mc ERC721 --nmc "RealWorldRegression|Prototech" +test-regression-erc20 :; forge t --mt test_regression --mc ERC20 --nmc "RealWorldRegression|Prototech|Position" +test-regression-erc721 :; forge t --mt test_regression --mc ERC721 --nmc "RealWorldRegression|Prototech|Position" test-regression-rewards :; forge t --mt test_regression --mc Rewards --nmc "RealWorldRegression|Prototech" test-regression-position :; forge t --mt test_regression --mc Position --nmc "RealWorldRegression|Prototech" test-regression-prototech :; forge t --mt test_regression --mc Prototech diff --git a/tests/forge/invariants/ERC20Pool/handlers/PanicExitERC20PoolHandler.sol b/tests/forge/invariants/ERC20Pool/handlers/PanicExitERC20PoolHandler.sol index a6932f66d..06990fdf1 100644 --- a/tests/forge/invariants/ERC20Pool/handlers/PanicExitERC20PoolHandler.sol +++ b/tests/forge/invariants/ERC20Pool/handlers/PanicExitERC20PoolHandler.sol @@ -151,14 +151,12 @@ contract PanicExitERC20PoolHandler is UnboundedLiquidationPoolHandler, Unbounded ) external useTimestamps skipTime(skippedTime_) writeLogs { numberOfCalls['BPanicExitPoolHandler.withdrawBonds']++; - kickerIndex_ = constrictToRange(kickerIndex_, 0, LENDERS - 1); - address kicker = _lenders[kickerIndex_]; - - (uint256 kickerClaimable, ) = _pool.kickerInfo(kicker); + kickerIndex_ = constrictToRange(kickerIndex_, 0, LENDERS - 1); + address kicker = _lenders[kickerIndex_]; _actor = kicker; changePrank(_actor); - _withdrawBonds(kicker, kickerClaimable); + _withdrawBonds(kicker, _getKickerInfo(kicker).claimableBond); } function settleHeadAuction( diff --git a/tests/forge/invariants/ERC721Pool/handlers/PanicExitERC721PoolHandler.sol b/tests/forge/invariants/ERC721Pool/handlers/PanicExitERC721PoolHandler.sol index 8d16ad0cb..927e29e87 100644 --- a/tests/forge/invariants/ERC721Pool/handlers/PanicExitERC721PoolHandler.sol +++ b/tests/forge/invariants/ERC721Pool/handlers/PanicExitERC721PoolHandler.sol @@ -149,14 +149,12 @@ contract PanicExitERC721PoolHandler is UnboundedLiquidationPoolHandler, Unbounde ) external useTimestamps skipTime(skippedTime_) writeLogs { numberOfCalls['BPanicExitPoolHandler.withdrawBonds']++; - kickerIndex_ = constrictToRange(kickerIndex_, 0, LENDERS - 1); - address kicker = _lenders[kickerIndex_]; - - (uint256 kickerClaimable, ) = _pool.kickerInfo(kicker); + kickerIndex_ = constrictToRange(kickerIndex_, 0, LENDERS - 1); + address kicker = _lenders[kickerIndex_]; _actor = kicker; changePrank(_actor); - _withdrawBonds(kicker, kickerClaimable); + _withdrawBonds(kicker, _getKickerInfo(kicker).claimableBond); } function settleHeadAuction( diff --git a/tests/forge/invariants/base/handlers/BasicPoolHandler.sol b/tests/forge/invariants/base/handlers/BasicPoolHandler.sol index 82c7db254..2fea74e0d 100644 --- a/tests/forge/invariants/base/handlers/BasicPoolHandler.sol +++ b/tests/forge/invariants/base/handlers/BasicPoolHandler.sol @@ -29,7 +29,10 @@ abstract contract BasicPoolHandler is UnboundedBasicPoolHandler { uint256 boundedAmount = _preAddQuoteToken(amountToAdd_); // Action phase - _addQuoteToken(boundedAmount, _lenderBucketIndex); + _addQuoteToken( + boundedAmount, + _lenderBucketIndex + ); } function removeQuoteToken( @@ -44,7 +47,10 @@ abstract contract BasicPoolHandler is UnboundedBasicPoolHandler { uint256 boundedAmount = _preRemoveQuoteToken(amountToRemove_); // Action phase - _removeQuoteToken(boundedAmount, _lenderBucketIndex); + _removeQuoteToken( + boundedAmount, + _lenderBucketIndex + ); } function moveQuoteToken( @@ -64,7 +70,11 @@ abstract contract BasicPoolHandler is UnboundedBasicPoolHandler { ) = _preMoveQuoteToken(amountToMove_, fromIndex_, toIndex_); // Action phase - _moveQuoteToken(boundedAmount, boundedFromIndex, boundedToIndex); + _moveQuoteToken( + boundedAmount, + boundedFromIndex, + boundedToIndex + ); } function transferLps( @@ -77,11 +87,22 @@ abstract contract BasicPoolHandler is UnboundedBasicPoolHandler { numberOfCalls['BBasicHandler.transferLps']++; // Prepare test phase - (address receiver, uint256 boundedLps) = _preTransferLps(toActorIndex_, lpsToTransfer_); + ( + address receiver, + uint256 boundedLps + ) = _preTransferLps(toActorIndex_, lpsToTransfer_); // Action phase - _increaseLPAllowance(receiver, _lenderBucketIndex, boundedLps); - _transferLps(_actor, receiver, _lenderBucketIndex); + _increaseLPAllowance( + receiver, + _lenderBucketIndex, + boundedLps + ); + _transferLps( + _actor, + receiver, + _lenderBucketIndex + ); } function stampLoan( @@ -89,6 +110,7 @@ abstract contract BasicPoolHandler is UnboundedBasicPoolHandler { uint256 skippedTime_ ) external useRandomActor(actorIndex_) useTimestamps skipTime(skippedTime_) { numberOfCalls['BBasicHandler.stampLoan']++; + _stampLoan(); } @@ -99,18 +121,25 @@ abstract contract BasicPoolHandler is UnboundedBasicPoolHandler { function _preAddQuoteToken( uint256 amountToAdd_ ) internal view returns (uint256 boundedAmount_) { - boundedAmount_ = constrictToRange(amountToAdd_, Maths.max(_pool.quoteTokenScale(), MIN_QUOTE_AMOUNT), MAX_QUOTE_AMOUNT); + boundedAmount_ = constrictToRange( + amountToAdd_, Maths.max(_pool.quoteTokenScale(), MIN_QUOTE_AMOUNT), MAX_QUOTE_AMOUNT + ); } function _preRemoveQuoteToken( uint256 amountToRemove_ ) internal returns (uint256 boundedAmount_) { - boundedAmount_ = constrictToRange(amountToRemove_, MIN_QUOTE_AMOUNT, MAX_QUOTE_AMOUNT); + boundedAmount_ = constrictToRange( + amountToRemove_, MIN_QUOTE_AMOUNT, MAX_QUOTE_AMOUNT + ); // ensure actor has quote tokens to remove - (uint256 lpBalanceBefore, ) = _pool.lenderInfo(_lenderBucketIndex, _actor); - if (lpBalanceBefore == 0) { - _addQuoteToken(boundedAmount_, _lenderBucketIndex); + LenderInfo memory lenderInfo = _getLenderInfo(_lenderBucketIndex, _actor); + if (lenderInfo.lpBalance == 0) { + _addQuoteToken( + boundedAmount_, + _lenderBucketIndex + ); } } @@ -119,17 +148,27 @@ abstract contract BasicPoolHandler is UnboundedBasicPoolHandler { uint256 fromIndex_, uint256 toIndex_ ) internal returns (uint256 boundedFromIndex_, uint256 boundedToIndex_, uint256 boundedAmount_) { - boundedFromIndex_ = constrictToRange(fromIndex_, LENDER_MIN_BUCKET_INDEX, LENDER_MAX_BUCKET_INDEX); - boundedToIndex_ = constrictToRange(toIndex_, LENDER_MIN_BUCKET_INDEX, LENDER_MAX_BUCKET_INDEX); - boundedAmount_ = constrictToRange(amountToMove_, MIN_QUOTE_AMOUNT, MAX_QUOTE_AMOUNT); - - // ensure actor has LP to move - (uint256 lpBalance, ) = _pool.lenderInfo(boundedFromIndex_, _actor); - if (lpBalance == 0) _addQuoteToken(boundedAmount_, boundedToIndex_); + boundedFromIndex_ = constrictToRange( + fromIndex_, LENDER_MIN_BUCKET_INDEX, LENDER_MAX_BUCKET_INDEX + ); + boundedToIndex_ = constrictToRange( + toIndex_, LENDER_MIN_BUCKET_INDEX, LENDER_MAX_BUCKET_INDEX + ); + boundedAmount_ = constrictToRange( + amountToMove_, MIN_QUOTE_AMOUNT, MAX_QUOTE_AMOUNT + ); + + LenderInfo memory lenderInfo = _getLenderInfo(boundedFromIndex_, _actor); + if (lenderInfo.lpBalance == 0) { + _addQuoteToken( + boundedAmount_, + boundedToIndex_ + ); + } + lenderInfo = _getLenderInfo(boundedFromIndex_, _actor); - (uint256 lps, ) = _pool.lenderInfo(boundedFromIndex_, _actor); // restrict amount to move by available deposit inside bucket - uint256 availableDeposit = _poolInfo.lpToQuoteTokens(address(_pool), lps, boundedFromIndex_); + uint256 availableDeposit = _lpToQuoteTokens(lenderInfo.lpBalance, boundedFromIndex_); boundedAmount_ = Maths.min(boundedAmount_, availableDeposit); } @@ -138,14 +177,17 @@ abstract contract BasicPoolHandler is UnboundedBasicPoolHandler { uint256 lpsToTransfer_ ) internal returns (address receiver_, uint256 boundedLps_) { // ensure actor has LP to transfer - (uint256 senderLpBalance, ) = _pool.lenderInfo(_lenderBucketIndex, _actor); - if (senderLpBalance == 0) _addQuoteToken(1e24, _lenderBucketIndex); - - (senderLpBalance, ) = _pool.lenderInfo(_lenderBucketIndex, _actor); - - boundedLps_ = constrictToRange(lpsToTransfer_, 0, senderLpBalance); + LenderInfo memory senderInfo = _getLenderInfo(_lenderBucketIndex, _actor); + if (senderInfo.lpBalance == 0) { + _addQuoteToken( + 1e24, + _lenderBucketIndex + ); + } + senderInfo = _getLenderInfo(_lenderBucketIndex, _actor); - receiver_ = actors[constrictToRange(toActorIndex_, 0, actors.length - 1)]; + boundedLps_ = constrictToRange(lpsToTransfer_, 0, senderInfo.lpBalance); + receiver_ = actors[constrictToRange(toActorIndex_, 0, actors.length - 1)]; } function _preDrawDebt( diff --git a/tests/forge/invariants/base/handlers/LiquidationPoolHandler.sol b/tests/forge/invariants/base/handlers/LiquidationPoolHandler.sol index a57130a80..5c4960c1b 100644 --- a/tests/forge/invariants/base/handlers/LiquidationPoolHandler.sol +++ b/tests/forge/invariants/base/handlers/LiquidationPoolHandler.sol @@ -20,7 +20,12 @@ abstract contract LiquidationPoolHandler is UnboundedLiquidationPoolHandler, Bas uint256 skippedTime_ ) external useTimestamps skipTime(skippedTime_) writeLogs { numberOfCalls['BLiquidationHandler.kickAuction']++; - _kickAuction(borrowerIndex_, amount_, kickerIndex_); + + _kickAuction( + borrowerIndex_, + amount_, + kickerIndex_ + ); } function lenderKickAuction( @@ -29,6 +34,7 @@ abstract contract LiquidationPoolHandler is UnboundedLiquidationPoolHandler, Bas uint256 skippedTime_ ) external useRandomActor(kickerIndex_) useRandomLenderBucket(bucketIndex_) useTimestamps skipTime(skippedTime_) writeLogs { numberOfCalls['BLiquidationHandler.lenderKickAuction']++; + _lenderKickAuction(_lenderBucketIndex); } @@ -38,7 +44,11 @@ abstract contract LiquidationPoolHandler is UnboundedLiquidationPoolHandler, Bas uint256 skippedTime_ ) external useRandomActor(kickerIndex_) useTimestamps skipTime(skippedTime_) writeLogs { numberOfCalls['BLiquidationHandler.withdrawBonds']++; - _withdrawBonds(_actor, maxAmount_); + + _withdrawBonds( + _actor, + maxAmount_ + ); } /****************************/ @@ -53,29 +63,38 @@ abstract contract LiquidationPoolHandler is UnboundedLiquidationPoolHandler, Bas ) external useRandomActor(takerIndex_) useTimestamps skipTime(skippedTime_) writeLogs { numberOfCalls['BLiquidationHandler.takeAuction']++; - address borrower; // try to take from head auction if any - (, , , , , , , address headAuction, , ) = _pool.auctionInfo(address(0)); - if (headAuction != address(0)) { - (, uint256 auctionedCollateral, ) = _poolInfo.borrowerInfo(address(_pool), headAuction); - borrower = headAuction; - amount_ = auctionedCollateral / 2; + AuctionInfo memory auctionInfo = _getAuctionInfo(address(0)); - (, , , uint256 kickTime, , , , , , ) = _pool.auctionInfo(borrower); + address borrower; + if (auctionInfo.head != address(0)) { + borrower = auctionInfo.head; + + BorrowerInfo memory borrowerInfo = _getBorrowerInfo(borrower); + amount_ = borrowerInfo.collateral / 2; + + auctionInfo = _getAuctionInfo(borrower); // TODO: eliminate this unnecessary skip, perhaps advance by single block instead - if (block.timestamp - kickTime < 1 hours) { + if (block.timestamp - auctionInfo.kickTime < 1 hours) { vm.warp(block.timestamp + 61 minutes); } - } else { address taker = _actor; // no head auction, prepare take action - (amount_, borrower) = _preTake(amount_, borrowerIndex_, takerIndex_); + ( + amount_, + borrower + ) = _preTake(amount_, borrowerIndex_, takerIndex_); + _actor = taker; changePrank(taker); } - _takeAuction(borrower, amount_, _actor); + _takeAuction( + borrower, + amount_, + _actor + ); } function bucketTake( @@ -87,20 +106,22 @@ abstract contract LiquidationPoolHandler is UnboundedLiquidationPoolHandler, Bas ) external useRandomActor(takerIndex_) useTimestamps skipTime(skippedTime_) writeLogs { numberOfCalls['BLiquidationHandler.bucketTake']++; - bucketIndex_ = constrictToRange(bucketIndex_, LENDER_MIN_BUCKET_INDEX, LENDER_MAX_BUCKET_INDEX); + bucketIndex_ = constrictToRange( + bucketIndex_, LENDER_MIN_BUCKET_INDEX, LENDER_MAX_BUCKET_INDEX + ); - address borrower; // try to take from head auction if any - (, , , , , , , address headAuction, , ) = _pool.auctionInfo(address(0)); - if (headAuction != address(0)) { - borrower = headAuction; + AuctionInfo memory auctionInfo = _getAuctionInfo(address(0)); + + address borrower; + if (auctionInfo.head != address(0)) { + borrower = auctionInfo.head; - (, , , uint256 kickTime, , , , , , ) = _pool.auctionInfo(borrower); + auctionInfo = _getAuctionInfo(borrower); // skip to make auction takeable - if (block.timestamp - kickTime < 1 hours) { + if (block.timestamp - auctionInfo.kickTime < 1 hours) { vm.warp(block.timestamp + 61 minutes); } - } else { address taker = _actor; // no head auction, prepare take action @@ -109,7 +130,12 @@ abstract contract LiquidationPoolHandler is UnboundedLiquidationPoolHandler, Bas changePrank(taker); } - _bucketTake(_actor, borrower, depositTake_, bucketIndex_); + _bucketTake( + _actor, + borrower, + depositTake_, + bucketIndex_ + ); } /******************************/ @@ -124,11 +150,12 @@ abstract contract LiquidationPoolHandler is UnboundedLiquidationPoolHandler, Bas ) external useRandomActor(actorIndex_) useTimestamps skipTime(skippedTime_) writeLogs { numberOfCalls['BLiquidationHandler.settleAuction']++; - address borrower; // try to settle head auction if any - (, , , , , , , address headAuction, , ) = _pool.auctionInfo(address(0)); - if (headAuction != address(0)) { - borrower = headAuction; + AuctionInfo memory auctionInfo = _getAuctionInfo(address(0)); + + address borrower; + if (auctionInfo.head != address(0)) { + borrower = auctionInfo.head; } else { address settler = _actor; // no head auction, prepare take action @@ -137,7 +164,10 @@ abstract contract LiquidationPoolHandler is UnboundedLiquidationPoolHandler, Bas changePrank(settler); } - _settleAuction(borrower, LENDER_MAX_BUCKET_INDEX - LENDER_MIN_BUCKET_INDEX); + _settleAuction( + borrower, + LENDER_MAX_BUCKET_INDEX - LENDER_MIN_BUCKET_INDEX + ); } @@ -146,48 +176,65 @@ abstract contract LiquidationPoolHandler is UnboundedLiquidationPoolHandler, Bas /*******************************/ function _preKick(uint256 borrowerIndex_, uint256 amount_) internal returns(address borrower_, bool borrowerKicked_) { - borrowerIndex_ = constrictToRange(borrowerIndex_, 0, actors.length - 1); - borrower_ = actors[borrowerIndex_]; - amount_ = constrictToRange(amount_, MIN_QUOTE_AMOUNT, MAX_QUOTE_AMOUNT); + amount_ = constrictToRange( + amount_, MIN_QUOTE_AMOUNT, MAX_QUOTE_AMOUNT + ); + borrowerIndex_ = constrictToRange( + borrowerIndex_, 0, actors.length - 1 + ); - ( , , , uint256 kickTime, , , , , , ) = _pool.auctionInfo(borrower_); + borrower_ = actors[borrowerIndex_]; - borrowerKicked_ = kickTime != 0; + AuctionInfo memory auctionInfo = _getAuctionInfo(borrower_); + borrowerKicked_ = auctionInfo.kickTime != 0; if (!borrowerKicked_) { // if borrower not kicked then check if it is undercollateralized / kickable - uint256 lup = _poolInfo.lup(address(_pool)); - (uint256 debt, uint256 collateral, ) = _poolInfo.borrowerInfo(address(_pool), borrower_); + BorrowerInfo memory borrowerInfo = _getBorrowerInfo(borrower_); - if (_isCollateralized(debt, collateral, lup, _pool.poolType())) { + if (_isBorrowerCollateralized(borrowerInfo)) { changePrank(borrower_); _actor = borrower_; uint256 drawDebtAmount = _preDrawDebt(amount_); _drawDebt(drawDebtAmount); // skip to make borrower undercollateralized - (debt, , ) = _poolInfo.borrowerInfo(address(_pool), borrower_); - if (debt != 0) vm.warp(block.timestamp + _getKickSkipTime()); + borrowerInfo = _getBorrowerInfo(borrower_); + if (borrowerInfo.debt != 0) vm.warp(block.timestamp + _getKickSkipTime()); } } } function _preTake(uint256 amount_, uint256 borrowerIndex_, uint256 kickerIndex_) internal returns(uint256 boundedAmount_, address borrower_){ boundedAmount_ = _constrictTakeAmount(amount_); - borrower_ = _kickAuction(borrowerIndex_, boundedAmount_ * 100, kickerIndex_); + + borrower_ = _kickAuction( + borrowerIndex_, + boundedAmount_ * 100, + kickerIndex_ + ); // skip time to make auction takeable vm.warp(block.timestamp + 61 minutes); } function _preBucketTake(uint256 borrowerIndex_, uint256 kickerIndex_) internal returns(address borrower_) { - borrower_ = _kickAuction(borrowerIndex_, 1e24, kickerIndex_); + borrower_ = _kickAuction( + borrowerIndex_, + 1e24, + kickerIndex_ + ); + // skip time to make auction takeable vm.warp(block.timestamp + 61 minutes); } function _preSettleAuction(uint256 borrowerIndex_, uint256 kickerIndex_) internal returns(address borrower_) { - borrower_ = _kickAuction(borrowerIndex_, 1e24, kickerIndex_); + borrower_ = _kickAuction( + borrowerIndex_, + 1e24, + kickerIndex_ + ); // skip time to make auction clearable vm.warp(block.timestamp + 73 hours); @@ -206,7 +253,10 @@ abstract contract LiquidationPoolHandler is UnboundedLiquidationPoolHandler, Bas // Prepare test phase address kicker = _actor; bool borrowerKicked; - (borrower_, borrowerKicked)= _preKick(borrowerIndex_, amount_); + ( + borrower_, + borrowerKicked + )= _preKick(borrowerIndex_, amount_); // Action phase _actor = kicker; diff --git a/tests/forge/invariants/base/handlers/ReservePoolHandler.sol b/tests/forge/invariants/base/handlers/ReservePoolHandler.sol index 7535a5cf4..d2d13f5d0 100644 --- a/tests/forge/invariants/base/handlers/ReservePoolHandler.sol +++ b/tests/forge/invariants/base/handlers/ReservePoolHandler.sol @@ -42,7 +42,10 @@ abstract contract ReservePoolHandler is UnboundedReservePoolHandler, Liquidation // take reserve auction if remaining claimable reserves (, , uint256 claimableReservesRemaining, , ) = _poolInfo.poolReservesInfo(address(_pool)); if (claimableReservesRemaining != 0) { - uint256 boundedAmount = constrictToRange(amountToTake_, claimableReservesRemaining / 2, claimableReservesRemaining); + uint256 boundedAmount = constrictToRange( + amountToTake_, claimableReservesRemaining / 2, claimableReservesRemaining + ); + _takeReserves(boundedAmount); } } diff --git a/tests/forge/invariants/base/handlers/unbounded/BaseHandler.sol b/tests/forge/invariants/base/handlers/unbounded/BaseHandler.sol index 565e433ec..157542e49 100644 --- a/tests/forge/invariants/base/handlers/unbounded/BaseHandler.sol +++ b/tests/forge/invariants/base/handlers/unbounded/BaseHandler.sol @@ -13,7 +13,8 @@ import { MAX_FENWICK_INDEX, MAX_PRICE, MIN_PRICE, - _indexOf + _indexOf, + _isCollateralized } from 'src/libraries/helpers/PoolHelper.sol'; import { Maths } from 'src/libraries/internal/Maths.sol'; @@ -28,6 +29,42 @@ abstract contract BaseHandler is Test { using EnumerableSet for EnumerableSet.UintSet; + struct BorrowerInfo { + uint256 debt; + uint256 collateral; + uint256 t0Np; + } + + struct BucketInfo { + uint256 lpBalance; + uint256 collateral; + uint256 deposit; + uint256 scale; + uint256 bankruptcyTime; + } + + struct AuctionInfo { + address kicker; + uint256 bondFactor; + uint256 bondSize; + uint256 kickTime; + uint256 referencePrice; + uint256 neutralPrice; + uint256 thresholdPrice; + address head; + } + + struct KickerInfo { + uint256 claimableBond; + uint256 lockedBond; + uint256 totalBond; + } + + struct LenderInfo { + uint256 lpBalance; + uint256 depositTime; + } + // Tokens TokenWithNDecimals internal _quote; BurnableToken internal _ajna; @@ -259,6 +296,75 @@ abstract contract BaseHandler is Test { /*** Pool Helper Functions ***/ /*****************************/ + function _getAuctionInfo(address borrower_) internal view returns (AuctionInfo memory auctionInfo_) { + ( + auctionInfo_.kicker, + auctionInfo_.bondFactor, + auctionInfo_.bondSize, + auctionInfo_.kickTime, + auctionInfo_.referencePrice, + auctionInfo_.neutralPrice, + auctionInfo_.thresholdPrice, + auctionInfo_.head, + , + ) = _pool.auctionInfo(borrower_); + } + + function _getBorrowerInfo(address borrower_) internal view returns (BorrowerInfo memory borrowerInfo_) { + ( + borrowerInfo_.debt, + borrowerInfo_.collateral, + borrowerInfo_.t0Np + ) = _poolInfo.borrowerInfo(address(_pool), borrower_); + } + + function _getBucketInfo(uint256 index_) internal view returns (BucketInfo memory bucketInfo_) { + ( + bucketInfo_.lpBalance, + bucketInfo_.collateral, + bucketInfo_.bankruptcyTime, + bucketInfo_.deposit, + bucketInfo_.scale + ) = _pool.bucketInfo(index_); + } + + function _getLenderInfo( + uint256 index_, + address lender_ + ) internal view returns (LenderInfo memory lenderInfo_) { + ( + lenderInfo_.lpBalance, + lenderInfo_.depositTime + ) = _pool.lenderInfo(index_, lender_); + } + + function _getKickerInfo(address kicker_) internal view returns (KickerInfo memory kickerInfo_) { + ( + kickerInfo_.claimableBond, + kickerInfo_.lockedBond + ) = _pool.kickerInfo(kicker_); + kickerInfo_.totalBond = kickerInfo_.claimableBond + kickerInfo_.lockedBond; + } + + function _getLup() internal view returns (uint256) { + return _poolInfo.lup(address(_pool)); + } + + function _getPoolQuoteBalance() internal view returns (uint256) { + return _quote.balanceOf(address(_pool)) * _pool.quoteTokenScale(); + } + + function _isBorrowerCollateralized(BorrowerInfo memory borrowerInfo_) internal view returns (bool) { + return _isCollateralized(borrowerInfo_.debt, borrowerInfo_.collateral, _getLup(), _pool.poolType()); + } + + function _lpToQuoteTokens( + uint256 lp_, + uint256 index_ + ) internal view returns (uint256) { + return _poolInfo.lpToQuoteTokens(address(_pool), lp_, index_); + } + function _getKickSkipTime() internal returns (uint256) { return vm.envOr("SKIP_TIME_TO_KICK", uint256(200 days)); } @@ -436,11 +542,6 @@ abstract contract BaseHandler is Test { /*** Auctions Helper Functions ***/ /*********************************/ - function _getKickerBond(address kicker_) internal view returns (uint256 bond_) { - (uint256 claimableBond, uint256 lockedBond) = _pool.kickerInfo(kicker_); - bond_ = claimableBond + lockedBond; - } - function _recordSettleBucket( address borrower_, uint256 borrowerCollateralBefore_, diff --git a/tests/forge/invariants/base/handlers/unbounded/UnboundedBasicPoolHandler.sol b/tests/forge/invariants/base/handlers/unbounded/UnboundedBasicPoolHandler.sol index 7eda8bf9d..3b9a00164 100644 --- a/tests/forge/invariants/base/handlers/unbounded/UnboundedBasicPoolHandler.sol +++ b/tests/forge/invariants/base/handlers/unbounded/UnboundedBasicPoolHandler.sol @@ -27,14 +27,16 @@ abstract contract UnboundedBasicPoolHandler is BaseHandler { ) internal updateLocalStateAndPoolInterest { numberOfCalls['UBBasicHandler.addQuoteToken']++; - (uint256 lpBalanceBeforeAction, ) = _pool.lenderInfo(bucketIndex_, _actor); - // ensure actor always has amount of quote to add _ensureQuoteAmount(_actor, amount_); - try _pool.addQuoteToken(amount_, bucketIndex_, block.timestamp + 1 minutes - ) returns (uint256, uint256 addedAmount_) { + LenderInfo memory lenderInfoBeforeAdd = _getLenderInfo(bucketIndex_, _actor); + try _pool.addQuoteToken( + amount_, + bucketIndex_, + block.timestamp + 1 minutes + ) returns (uint256, uint256 addedAmount_) { // amount is rounded in pool to token scale amount_ = _roundToScale(amount_, _pool.quoteTokenScale()); @@ -43,15 +45,18 @@ abstract contract UnboundedBasicPoolHandler is BaseHandler { // **B5**: when adding quote tokens: lender deposit time = timestamp of block when deposit happened lenderDepositTime[_actor][bucketIndex_] = block.timestamp; + // **R3**: Exchange rates are unchanged by depositing quote token into a bucket exchangeRateShouldNotChange[bucketIndex_] = true; _fenwickAdd(addedAmount_, bucketIndex_); + LenderInfo memory lenderInfoAfterAdd = _getLenderInfo(bucketIndex_, _actor); // Post action condition - (uint256 lpBalanceAfterAction, ) = _pool.lenderInfo(bucketIndex_, _actor); - require(lpBalanceAfterAction > lpBalanceBeforeAction, "LP balance should increase"); - + require( + lenderInfoAfterAdd.lpBalance > lenderInfoBeforeAdd.lpBalance, + "LP balance should increase" + ); } catch (bytes memory err) { _ensurePoolError(err); } @@ -63,12 +68,15 @@ abstract contract UnboundedBasicPoolHandler is BaseHandler { ) internal updateLocalStateAndPoolInterest { numberOfCalls['UBBasicHandler.removeQuoteToken']++; - (uint256 lpBalanceBeforeAction, ) = _pool.lenderInfo(bucketIndex_, _actor); + // record fenwick tree state before action + fenwickDeposits[bucketIndex_] = _getBucketInfo(bucketIndex_).deposit; - ( , , , uint256 deposit, ) = _pool.bucketInfo(bucketIndex_); - fenwickDeposits[bucketIndex_] = deposit; + LenderInfo memory lenderInfoBeforeRemove = _getLenderInfo(bucketIndex_, _actor); - try _pool.removeQuoteToken(amount_, bucketIndex_) returns (uint256 removedAmount_, uint256) { + try _pool.removeQuoteToken( + amount_, + bucketIndex_ + ) returns (uint256 removedAmount_, uint256) { // **R4**: Exchange rates are unchanged by withdrawing deposit (quote token) from a bucket exchangeRateShouldNotChange[bucketIndex_] = true; @@ -77,10 +85,12 @@ abstract contract UnboundedBasicPoolHandler is BaseHandler { // rounding in favour of pool goes to reserves increaseInReserves += removedAmount_ - _roundToScale(removedAmount_, _pool.quoteTokenScale()); + LenderInfo memory lenderInfoAfterRemove = _getLenderInfo(bucketIndex_, _actor); // Post action condition - (uint256 lpBalanceAfterAction, ) = _pool.lenderInfo(bucketIndex_, _actor); - require(lpBalanceAfterAction < lpBalanceBeforeAction, "LP balance should decrease"); - + require( + lenderInfoAfterRemove.lpBalance < lenderInfoBeforeRemove.lpBalance, + "LP balance should decrease" + ); } catch (bytes memory err) { _ensurePoolError(err); } @@ -93,8 +103,8 @@ abstract contract UnboundedBasicPoolHandler is BaseHandler { ) internal updateLocalStateAndPoolInterest { numberOfCalls['UBBasicHandler.moveQuoteToken']++; - ( , , , uint256 fromDeposit, ) = _pool.bucketInfo(fromIndex_); - fenwickDeposits[fromIndex_] = fromDeposit; + // record fenwick tree state before action + fenwickDeposits[fromIndex_] = _getBucketInfo(fromIndex_).deposit; try _pool.moveQuoteToken( amount_, @@ -102,12 +112,12 @@ abstract contract UnboundedBasicPoolHandler is BaseHandler { toIndex_, block.timestamp + 1 minutes ) returns (uint256, uint256, uint256 movedAmount_) { - - (, uint256 fromBucketDepositTime) = _pool.lenderInfo(fromIndex_, _actor); - (, uint256 toBucketDepositTime) = _pool.lenderInfo(toIndex_, _actor); - // **B5**: when moving quote tokens: lender deposit time = timestamp of block when move happened - lenderDepositTime[_actor][toIndex_] = Maths.max(fromBucketDepositTime, toBucketDepositTime); + lenderDepositTime[_actor][toIndex_] = Maths.max( + _getLenderInfo(fromIndex_, _actor).depositTime, + _getLenderInfo(toIndex_, _actor).depositTime + ); + // **RE3**: Reserves increase only when moving quote tokens into a lower-priced bucket // movedAmount_ can be greater than amount_ in case when bucket gets empty by moveQuoteToken if (amount_ > movedAmount_) increaseInReserves += amount_ - movedAmount_; @@ -136,7 +146,12 @@ abstract contract UnboundedBasicPoolHandler is BaseHandler { buckets[0] = bucketIndex_; uint256[] memory amounts = new uint256[](1); amounts[0] = amount_; - _pool.increaseLPAllowance(receiver_, buckets, amounts); + + _pool.increaseLPAllowance( + receiver_, + buckets, + amounts + ); } function _transferLps( @@ -151,14 +166,16 @@ abstract contract UnboundedBasicPoolHandler is BaseHandler { changePrank(receiver_); - try _pool.transferLP(sender_, receiver_, buckets) { - - (, uint256 senderDepositTime) = _pool.lenderInfo(bucketIndex_, sender_); - (, uint256 receiverDepositTime) = _pool.lenderInfo(bucketIndex_, receiver_); - + try _pool.transferLP( + sender_, + receiver_, + buckets + ) { // **B6**: when receiving transferred LP : receiver deposit time (`Lender.depositTime`) = max of sender and receiver deposit time - lenderDepositTime[receiver_][bucketIndex_] = Maths.max(senderDepositTime, receiverDepositTime); - + lenderDepositTime[receiver_][bucketIndex_] = Maths.max( + _getLenderInfo(bucketIndex_, sender_).depositTime, + _getLenderInfo(bucketIndex_, receiver_).depositTime + ); } catch (bytes memory err) { _ensurePoolError(err); } @@ -166,6 +183,7 @@ abstract contract UnboundedBasicPoolHandler is BaseHandler { function _stampLoan() internal updateLocalStateAndPoolInterest { numberOfCalls['UBBasicHandler.stampLoan']++; + try _pool.stampLoan() { } catch (bytes memory err) { _ensurePoolError(err); diff --git a/tests/forge/invariants/base/handlers/unbounded/UnboundedLiquidationPoolHandler.sol b/tests/forge/invariants/base/handlers/unbounded/UnboundedLiquidationPoolHandler.sol index 2b061fa8a..e81ac843f 100644 --- a/tests/forge/invariants/base/handlers/unbounded/UnboundedLiquidationPoolHandler.sol +++ b/tests/forge/invariants/base/handlers/unbounded/UnboundedLiquidationPoolHandler.sol @@ -37,21 +37,22 @@ abstract contract UnboundedLiquidationPoolHandler is BaseHandler { ) internal updateLocalStateAndPoolInterest { numberOfCalls['UBLiquidationHandler.kickAuction']++; - (uint256 borrowerDebt, , ) = _poolInfo.borrowerInfo(address(_pool), borrower_); + BorrowerInfo memory borrowerInfo = _getBorrowerInfo(borrower_); + KickerInfo memory kickerInfoBeforeKick = _getKickerInfo(_actor); // ensure actor always has the amount to pay for bond - _ensureQuoteAmount(_actor, borrowerDebt); + _ensureQuoteAmount(_actor, borrowerInfo.debt); - uint256 kickerBondBefore = _getKickerBond(_actor); - - try _pool.kick(borrower_, 7388) { + try _pool.kick( + borrower_, + 7388 + ) { numberOfActions['kick']++; - uint256 kickerBondAfter = _getKickerBond(_actor); + KickerInfo memory kickerInfoAfterKick = _getKickerInfo(_actor); // **A7**: totalBondEscrowed should increase when auctioned kicked with the difference needed to cover the bond - increaseInBonds += kickerBondAfter - kickerBondBefore; - + increaseInBonds += kickerInfoAfterKick.totalBond - kickerInfoBeforeKick.totalBond; } catch (bytes memory err) { _ensurePoolError(err); } @@ -63,29 +64,31 @@ abstract contract UnboundedLiquidationPoolHandler is BaseHandler { numberOfCalls['UBLiquidationHandler.lenderKickAuction']++; (address maxBorrower, , ) = _pool.loansInfo(); - (uint256 borrowerDebt, , ) = _poolInfo.borrowerInfo(address(_pool), maxBorrower); - ( , , , uint256 depositBeforeAction, ) = _pool.bucketInfo(bucketIndex_); - fenwickDeposits[bucketIndex_] = depositBeforeAction; + BorrowerInfo memory borrowerInfo = _getBorrowerInfo(maxBorrower); + if (borrowerInfo.debt == 0) return; - uint256 kickerBondBefore = _getKickerBond(_actor); + BucketInfo memory bucketInfoBeforeKick = _getBucketInfo(bucketIndex_); + KickerInfo memory kickerInfoBeforeKick = _getKickerInfo(_actor); - if (borrowerDebt == 0) return; + // record fenwick tree state before action + fenwickDeposits[bucketIndex_] = bucketInfoBeforeKick.deposit; // ensure actor always has the amount to add for kick - _ensureQuoteAmount(_actor, borrowerDebt); + _ensureQuoteAmount(_actor, borrowerInfo.debt); - try _pool.lenderKick(bucketIndex_, 7388) { + try _pool.lenderKick( + bucketIndex_, + 7388 + ) { numberOfActions['lenderKick']++; - ( , , , uint256 depositAfterAction, ) = _pool.bucketInfo(bucketIndex_); - - uint256 kickerBondAfter = _getKickerBond(_actor); + BucketInfo memory bucketInfoAfterKick = _getBucketInfo(bucketIndex_); + KickerInfo memory kickerInfoAfterKick = _getKickerInfo(_actor); // **A7**: totalBondEscrowed should increase when auctioned kicked with the difference needed to cover the bond - increaseInBonds += kickerBondAfter - kickerBondBefore; - - _fenwickRemove(depositBeforeAction - depositAfterAction, bucketIndex_); + increaseInBonds += kickerInfoAfterKick.totalBond - kickerInfoBeforeKick.totalBond; + _fenwickRemove(bucketInfoBeforeKick.deposit - bucketInfoAfterKick.deposit, bucketIndex_); } catch (bytes memory err) { _ensurePoolError(err); } @@ -97,22 +100,22 @@ abstract contract UnboundedLiquidationPoolHandler is BaseHandler { ) internal updateLocalStateAndPoolInterest { numberOfCalls['UBLiquidationHandler.withdrawBonds']++; - uint256 balanceBeforeWithdraw = _quote.balanceOf(address(_pool)) * _pool.quoteTokenScale(); - (uint256 claimableBondBeforeWithdraw, ) = _pool.kickerInfo(_actor); + KickerInfo memory kickerInfoBeforeWithdraw = _getKickerInfo(_actor); + uint256 poolBalanceBeforeWithdraw = _getPoolQuoteBalance(); try _pool.withdrawBonds(kicker_, maxAmount_) { - uint256 balanceAfterWithdraw = _quote.balanceOf(address(_pool)) * _pool.quoteTokenScale(); - (uint256 claimableBondAfterWithdraw, ) = _pool.kickerInfo(_actor); + KickerInfo memory kickerInfoAfterWithdraw = _getKickerInfo(_actor); + uint256 poolBalanceAfterWithdraw = _getPoolQuoteBalance(); // **A7** Claimable bonds should be available for withdrawal from pool at any time (bonds are guaranteed by the protocol). require( - claimableBondAfterWithdraw < claimableBondBeforeWithdraw, + kickerInfoAfterWithdraw.claimableBond < kickerInfoBeforeWithdraw.claimableBond, "A7: claimable bond not available to withdraw" ); // **A7**: totalBondEscrowed should decrease only when kicker bonds withdrawned - decreaseInBonds += balanceBeforeWithdraw - balanceAfterWithdraw; + decreaseInBonds += poolBalanceBeforeWithdraw - poolBalanceAfterWithdraw; } catch (bytes memory err) { _ensurePoolError(err); @@ -130,48 +133,54 @@ abstract contract UnboundedLiquidationPoolHandler is BaseHandler { ) internal updateLocalStateAndPoolInterest { numberOfCalls['UBLiquidationHandler.takeAuction']++; - (address kicker, , , , , , , , , ) = _pool.auctionInfo(borrower_); + AuctionInfo memory auctionInfo = _getAuctionInfo(borrower_); + BorrowerInfo memory borrowerInfoBeforeTake = _getBorrowerInfo(borrower_); + KickerInfo memory kickerInfoBeforeTake = _getKickerInfo(auctionInfo.kicker); - ( - uint256 borrowerDebtBeforeTake, - uint256 borrowerCollateralBeforeTake, - ) = _poolInfo.borrowerInfo(address(_pool), borrower_); - uint256 totalBondBeforeTake = _getKickerBond(kicker); - uint256 totalBalanceBeforeTake = _quote.balanceOf(address(_pool)) * _pool.quoteTokenScale(); + uint256 totalBalanceBeforeTake = _getPoolQuoteBalance(); (uint256 kickTimeBefore, , , , uint256 auctionPrice, , , , ) = _poolInfo.auctionStatus(address(_pool), borrower_); // ensure actor always has the amount to take collateral _ensureQuoteAmount(taker_, 1e45); - try _pool.take(borrower_, amount_, taker_, bytes("")) { + try _pool.take( + borrower_, + amount_, + taker_, + bytes("") + ) { numberOfActions['take']++; - (uint256 borrowerDebtAfterTake, uint256 borrowerCollateralAfterTake, ) = _poolInfo.borrowerInfo(address(_pool), borrower_); - uint256 totalBondAfterTake = _getKickerBond(kicker); - uint256 totalBalanceAfterTake = _quote.balanceOf(address(_pool)) * _pool.quoteTokenScale(); + BorrowerInfo memory borrowerInfoAfterTake = _getBorrowerInfo(borrower_); + KickerInfo memory kickerInfoAfterTake = _getKickerInfo(auctionInfo.kicker); + + uint256 totalBalanceAfterTake = _getPoolQuoteBalance(); // **RE7**: Reserves decrease with debt covered by take. - decreaseInReserves += borrowerDebtBeforeTake - borrowerDebtAfterTake; + decreaseInReserves += borrowerInfoBeforeTake.debt - borrowerInfoAfterTake.debt; // **A8**: kicker reward <= Borrower penalty - borrowerPenalty = Maths.wmul(borrowerCollateralBeforeTake - borrowerCollateralAfterTake, auctionPrice) - (borrowerDebtBeforeTake - borrowerDebtAfterTake); + borrowerPenalty = Maths.wmul( + borrowerInfoBeforeTake.collateral - borrowerInfoAfterTake.collateral, + auctionPrice + ) - (borrowerInfoBeforeTake.debt - borrowerInfoAfterTake.debt); - if (totalBondBeforeTake > totalBondAfterTake) { + if (kickerInfoBeforeTake.totalBond > kickerInfoAfterTake.totalBond) { // **RE7**: Reserves increase by bond penalty on take. - increaseInReserves += totalBondBeforeTake - totalBondAfterTake; + increaseInReserves += kickerInfoBeforeTake.totalBond - kickerInfoAfterTake.totalBond; // **A7**: Total Bond decrease by bond penalty on take. - decreaseInBonds += totalBondBeforeTake - totalBondAfterTake; + decreaseInBonds += kickerInfoBeforeTake.totalBond - kickerInfoAfterTake.totalBond; } else { // **RE7**: Reserves decrease by bond reward on take. - decreaseInReserves += totalBondAfterTake - totalBondBeforeTake; + decreaseInReserves += kickerInfoAfterTake.totalBond - kickerInfoBeforeTake.totalBond; // **A7**: Total Bond increase by bond penalty on take. - increaseInBonds += totalBondAfterTake - totalBondBeforeTake; + increaseInBonds += kickerInfoAfterTake.totalBond - kickerInfoBeforeTake.totalBond; // **A8**: kicker reward <= Borrower penalty - kickerReward += totalBondAfterTake - totalBondBeforeTake; + kickerReward += kickerInfoAfterTake.totalBond - kickerInfoBeforeTake.totalBond; } // **RE7**: Reserves increase with the quote token paid by taker. @@ -180,12 +189,11 @@ abstract contract UnboundedLiquidationPoolHandler is BaseHandler { if (_pool.poolType() == 1) { _recordSettleBucket( borrower_, - borrowerCollateralBeforeTake, + borrowerInfoBeforeTake.collateral, kickTimeBefore, auctionPrice ); } - } catch (bytes memory err) { _ensurePoolError(err); } @@ -201,21 +209,46 @@ abstract contract UnboundedLiquidationPoolHandler is BaseHandler { (address kicker, , , , , , , , , ) = _pool.auctionInfo(borrower_); ( , , , , uint256 auctionPrice, , , , ) = _poolInfo.auctionStatus(address(_pool), borrower_); - uint256 auctionBucketIndex = auctionPrice < MIN_PRICE ? 7388 : (auctionPrice > MAX_PRICE ? 0 : _indexOf(auctionPrice)); + uint256 auctionBucketIndex = auctionPrice < MIN_PRICE ? 7388 : (auctionPrice > MAX_PRICE ? 0 : _indexOf(auctionPrice)); - LocalBucketTakeVars memory beforeBucketTakeVars = getBucketTakeInfo(bucketIndex_, kicker, _actor, auctionBucketIndex, borrower_); + LocalBucketTakeVars memory beforeBucketTakeVars = _getBucketTakeInfo( + bucketIndex_, + kicker, + _actor, + auctionBucketIndex, + borrower_ + ); - try _pool.bucketTake(borrower_, depositTake_, bucketIndex_) { + try _pool.bucketTake( + borrower_, + depositTake_, + bucketIndex_ + ) { numberOfActions['bucketTake']++; - LocalBucketTakeVars memory afterBucketTakeVars = getBucketTakeInfo(bucketIndex_, kicker, _actor, auctionBucketIndex, borrower_); + LocalBucketTakeVars memory afterBucketTakeVars = _getBucketTakeInfo( + bucketIndex_, + kicker, + _actor, + auctionBucketIndex, + borrower_ + ); // **B7**: when awarded bucket take LP : taker deposit time = timestamp of block when award happened - if (afterBucketTakeVars.takerLps > beforeBucketTakeVars.takerLps) lenderDepositTime[taker_][bucketIndex_] = block.timestamp; + if (afterBucketTakeVars.takerLps > beforeBucketTakeVars.takerLps) { + lenderDepositTime[taker_][bucketIndex_] = block.timestamp; + } // for ERC-721 pools; adjust borrower collateral by compensated collateral awarded to the bucket at the auction price - if (_pool.poolType() == 1 && beforeBucketTakeVars.borrowerLps != afterBucketTakeVars.borrowerLps) { - if (bucketIndex_ == auctionBucketIndex) revert("Cannot distinguish bucketTake collateral from compensated bucket collateral"); + if ( + _pool.poolType() == 1 + && + beforeBucketTakeVars.borrowerLps != afterBucketTakeVars.borrowerLps + ) { + if (bucketIndex_ == auctionBucketIndex) { + revert("Cannot distinguish bucketTake collateral from compensated bucket collateral"); + } + afterBucketTakeVars.borrowerCollateral += afterBucketTakeVars.compensatedBucketCollateral - beforeBucketTakeVars.compensatedBucketCollateral; } @@ -225,23 +258,32 @@ abstract contract UnboundedLiquidationPoolHandler is BaseHandler { // when kicker and taker are same, kicker Reward = total Reward (lps) - taker Reward (Collateral Price * difference of bucket used and auction price) if (!depositTake_ && kicker == _actor) { - uint256 totalReward = rewardedLpToQuoteToken(afterBucketTakeVars.kickerLps - beforeBucketTakeVars.kickerLps, bucketIndex_); - uint256 takerReward = Maths.floorWmul(beforeBucketTakeVars.borrowerCollateral - afterBucketTakeVars.borrowerCollateral, _priceAt(bucketIndex_) - auctionPrice); + uint256 totalReward = _rewardedLpToQuoteToken( + afterBucketTakeVars.kickerLps - beforeBucketTakeVars.kickerLps, + bucketIndex_ + ); + uint256 takerReward = Maths.floorWmul( + beforeBucketTakeVars.borrowerCollateral - afterBucketTakeVars.borrowerCollateral, + _priceAt(bucketIndex_) - auctionPrice + ); // **A8**: kicker reward <= Borrower penalty kickerReward = totalReward - takerReward; } else { // **A8**: kicker reward <= Borrower penalty - kickerReward = rewardedLpToQuoteToken(afterBucketTakeVars.kickerLps - beforeBucketTakeVars.kickerLps, bucketIndex_); + kickerReward = _rewardedLpToQuoteToken( + afterBucketTakeVars.kickerLps - beforeBucketTakeVars.kickerLps, + bucketIndex_ + ); } } // **A8**: kicker reward <= Borrower penalty - if (depositTake_) { - borrowerPenalty = Maths.wmul(beforeBucketTakeVars.borrowerCollateral - afterBucketTakeVars.borrowerCollateral, _priceAt(bucketIndex_)) - (beforeBucketTakeVars.borrowerDebt - afterBucketTakeVars.borrowerDebt); - } else { - borrowerPenalty = Maths.wmul(beforeBucketTakeVars.borrowerCollateral - afterBucketTakeVars.borrowerCollateral, auctionPrice) - (beforeBucketTakeVars.borrowerDebt - afterBucketTakeVars.borrowerDebt); - } + uint256 price = depositTake_ ? _priceAt(bucketIndex_) : auctionPrice; + borrowerPenalty = Maths.wmul( + beforeBucketTakeVars.borrowerCollateral - afterBucketTakeVars.borrowerCollateral, + price + ) - (beforeBucketTakeVars.borrowerDebt - afterBucketTakeVars.borrowerDebt); // reserves are increased by take penalty of borrower (Deposit used from bucket - Borrower debt reduced) increaseInReserves += borrowerPenalty; @@ -267,9 +309,13 @@ abstract contract UnboundedLiquidationPoolHandler is BaseHandler { exchangeRateShouldNotChange[bucketIndex_] = true; // **CT2**: Keep track of bucketIndex when borrower is removed from auction to check collateral added into that bucket - (, , , uint256 kickTime, , , , , , ) = _pool.auctionInfo(borrower_); - if (kickTime == 0 && _pool.poolType() == 1) { + if ( + _getAuctionInfo(borrower_).kickTime == 0 + && + _pool.poolType() == 1 + ) { buckets.add(auctionBucketIndex); + if (beforeBucketTakeVars.borrowerLps < afterBucketTakeVars.borrowerLps) { lenderDepositTime[borrower_][auctionBucketIndex] = block.timestamp; } @@ -277,7 +323,6 @@ abstract contract UnboundedLiquidationPoolHandler is BaseHandler { // assign value to fenwick tree to mitigate rounding error that could be created in a _fenwickRemove call fenwickDeposits[bucketIndex_] = afterBucketTakeVars.deposit; - } catch (bytes memory err) { _ensurePoolError(err); } @@ -299,7 +344,10 @@ abstract contract UnboundedLiquidationPoolHandler is BaseHandler { (uint256 reservesBeforeAction, , , , )= _poolInfo.poolReservesInfo(address(_pool)); (uint256 inflator, ) = _pool.inflatorInfo(); - try _pool.settle(borrower_, maxDepth_) { + try _pool.settle( + borrower_, + maxDepth_ + ) { numberOfActions['settle']++; // settle borrower debt with exchanging borrower collateral with quote tokens starting from hpb @@ -381,8 +429,13 @@ abstract contract UnboundedLiquidationPoolHandler is BaseHandler { } } // **CT2**: Keep track of bucketIndex when borrower is removed from auction to check collateral added into that bucket - (, , , uint256 kickTime, , , , , , ) = _pool.auctionInfo(borrower_); - if (kickTime == 0 && collateral % 1e18 != 0 && _pool.poolType() == 1) { + if ( + _getAuctionInfo(borrower_).kickTime == 0 + && + collateral % 1e18 != 0 + && + _pool.poolType() == 1 + ) { buckets.add(7388); lenderDepositTime[borrower_][7388] = block.timestamp; } @@ -391,19 +444,29 @@ abstract contract UnboundedLiquidationPoolHandler is BaseHandler { } } - function getBucketTakeInfo(uint256 bucketIndex_, address kicker_, address taker_, uint256 auctionBucketIndex_, address borrower_) internal view returns(LocalBucketTakeVars memory bucketTakeVars) { - (bucketTakeVars.kickerLps, ) = _pool.lenderInfo(bucketIndex_, kicker_); - (bucketTakeVars.takerLps, ) = _pool.lenderInfo(bucketIndex_, taker_); - ( , , , bucketTakeVars.deposit, ) = _pool.bucketInfo(bucketIndex_); - bucketTakeVars.kickerBond = _getKickerBond(kicker_); - (bucketTakeVars.borrowerLps, ) = _pool.lenderInfo(auctionBucketIndex_, borrower_); - (bucketTakeVars.borrowerDebt, bucketTakeVars.borrowerCollateral, ) = _poolInfo.borrowerInfo(address(_pool), borrower_); - ( , bucketTakeVars.compensatedBucketCollateral, , , ) = _pool.bucketInfo(auctionBucketIndex_); + function _getBucketTakeInfo( + uint256 bucketIndex_, + address kicker_, + address taker_, + uint256 auctionBucketIndex_, + address borrower_ + ) internal view returns(LocalBucketTakeVars memory bucketTakeVars) { + bucketTakeVars.kickerLps = _getLenderInfo(bucketIndex_, kicker_).lpBalance; + bucketTakeVars.takerLps = _getLenderInfo(bucketIndex_, taker_).lpBalance; + bucketTakeVars.borrowerLps = _getLenderInfo(auctionBucketIndex_, borrower_).lpBalance; + + bucketTakeVars.deposit = _getBucketInfo(bucketIndex_).deposit; + bucketTakeVars.compensatedBucketCollateral = _getBucketInfo(auctionBucketIndex_).collateral; + bucketTakeVars.kickerBond = _getKickerInfo(kicker_).totalBond; + + BorrowerInfo memory borrowerInfo = _getBorrowerInfo(borrower_); + bucketTakeVars.borrowerDebt = borrowerInfo.debt; + bucketTakeVars.borrowerCollateral = borrowerInfo.collateral; } // Helper function to calculate quote tokens from lps in a bucket irrespective of deposit available. // LP rewarded -> quote token rounded up (as LP rewarded are calculated as rewarded quote token -> LP rounded down) - function rewardedLpToQuoteToken(uint256 lps_, uint256 bucketIndex_) internal view returns(uint256 quoteTokens_) { + function _rewardedLpToQuoteToken(uint256 lps_, uint256 bucketIndex_) internal view returns(uint256 quoteTokens_) { (uint256 bucketLP, uint256 bucketCollateral , , uint256 bucketDeposit, ) = _pool.bucketInfo(bucketIndex_); quoteTokens_ = Buckets.lpToQuoteTokens( diff --git a/tests/forge/invariants/base/handlers/unbounded/UnboundedReservePoolHandler.sol b/tests/forge/invariants/base/handlers/unbounded/UnboundedReservePoolHandler.sol index c6493c439..4a058a990 100644 --- a/tests/forge/invariants/base/handlers/unbounded/UnboundedReservePoolHandler.sol +++ b/tests/forge/invariants/base/handlers/unbounded/UnboundedReservePoolHandler.sol @@ -21,7 +21,6 @@ abstract contract UnboundedReservePoolHandler is BaseHandler { if (claimableReserves == 0) return; try _pool.kickReserveAuction() { - // **RE11**: Reserves increase by claimableReserves by kickReserveAuction decreaseInReserves += claimableReserves; } catch (bytes memory err) { @@ -44,7 +43,6 @@ abstract contract UnboundedReservePoolHandler is BaseHandler { (, uint256 claimableReservesBeforeAction, ,) = _pool.reservesInfo(); try _pool.takeReserves(amount_) { - (, uint256 claimableReservesAfterAction, ,) = _pool.reservesInfo(); // reserves are guaranteed by the protocol) require( From e2adcfb901c120833550d23a9d3f85bb2559821e Mon Sep 17 00:00:00 2001 From: grandizzy Date: Thu, 7 Dec 2023 12:37:57 +0200 Subject: [PATCH 2/5] Take 2: reserves and other helpers --- .../base/handlers/unbounded/BaseHandler.sol | 18 ++++++++++++++++++ .../UnboundedLiquidationPoolHandler.sol | 7 ++++--- .../unbounded/UnboundedReservePoolHandler.sol | 13 +++++++------ 3 files changed, 29 insertions(+), 9 deletions(-) diff --git a/tests/forge/invariants/base/handlers/unbounded/BaseHandler.sol b/tests/forge/invariants/base/handlers/unbounded/BaseHandler.sol index 157542e49..fa8513cb8 100644 --- a/tests/forge/invariants/base/handlers/unbounded/BaseHandler.sol +++ b/tests/forge/invariants/base/handlers/unbounded/BaseHandler.sol @@ -65,6 +65,14 @@ abstract contract BaseHandler is Test { uint256 depositTime; } + struct ReservesInfo { + uint256 reserves; + uint256 claimableReserves; + uint256 claimableReservesRemaining; + uint256 auctionPrice; + uint256 timeRemaining; + } + // Tokens TokenWithNDecimals internal _quote; BurnableToken internal _ajna; @@ -346,6 +354,16 @@ abstract contract BaseHandler is Test { kickerInfo_.totalBond = kickerInfo_.claimableBond + kickerInfo_.lockedBond; } + function _getReservesInfo() internal view returns (ReservesInfo memory reservesInfo_) { + ( + reservesInfo_.reserves, + reservesInfo_.claimableReserves, + reservesInfo_.claimableReservesRemaining, + reservesInfo_.auctionPrice, + reservesInfo_.timeRemaining + ) = _poolInfo.poolReservesInfo(address(_pool)); + } + function _getLup() internal view returns (uint256) { return _poolInfo.lup(address(_pool)); } diff --git a/tests/forge/invariants/base/handlers/unbounded/UnboundedLiquidationPoolHandler.sol b/tests/forge/invariants/base/handlers/unbounded/UnboundedLiquidationPoolHandler.sol index e81ac843f..ed21ec361 100644 --- a/tests/forge/invariants/base/handlers/unbounded/UnboundedLiquidationPoolHandler.sol +++ b/tests/forge/invariants/base/handlers/unbounded/UnboundedLiquidationPoolHandler.sol @@ -207,7 +207,7 @@ abstract contract UnboundedLiquidationPoolHandler is BaseHandler { ) internal updateLocalStateAndPoolInterest { numberOfCalls['UBLiquidationHandler.bucketTake']++; - (address kicker, , , , , , , , , ) = _pool.auctionInfo(borrower_); + address kicker = _getAuctionInfo(borrower_).kicker; ( , , , , uint256 auctionPrice, , , , ) = _poolInfo.auctionStatus(address(_pool), borrower_); uint256 auctionBucketIndex = auctionPrice < MIN_PRICE ? 7388 : (auctionPrice > MAX_PRICE ? 0 : _indexOf(auctionPrice)); @@ -279,10 +279,10 @@ abstract contract UnboundedLiquidationPoolHandler is BaseHandler { } // **A8**: kicker reward <= Borrower penalty - uint256 price = depositTake_ ? _priceAt(bucketIndex_) : auctionPrice; + uint256 rewardPrice = depositTake_ ? _priceAt(bucketIndex_) : auctionPrice; borrowerPenalty = Maths.wmul( beforeBucketTakeVars.borrowerCollateral - afterBucketTakeVars.borrowerCollateral, - price + rewardPrice ) - (beforeBucketTakeVars.borrowerDebt - afterBucketTakeVars.borrowerDebt); // reserves are increased by take penalty of borrower (Deposit used from bucket - Borrower debt reduced) @@ -304,6 +304,7 @@ abstract contract UnboundedLiquidationPoolHandler is BaseHandler { // **A7**: Total Bond increase by bond penalty on take. increaseInBonds += afterBucketTakeVars.kickerBond - beforeBucketTakeVars.kickerBond; } + // **R7**: Exchange rates are unchanged under depositTakes // **R8**: Exchange rates are unchanged under arbTakes exchangeRateShouldNotChange[bucketIndex_] = true; diff --git a/tests/forge/invariants/base/handlers/unbounded/UnboundedReservePoolHandler.sol b/tests/forge/invariants/base/handlers/unbounded/UnboundedReservePoolHandler.sol index 4a058a990..758fc33fa 100644 --- a/tests/forge/invariants/base/handlers/unbounded/UnboundedReservePoolHandler.sol +++ b/tests/forge/invariants/base/handlers/unbounded/UnboundedReservePoolHandler.sol @@ -17,7 +17,7 @@ abstract contract UnboundedReservePoolHandler is BaseHandler { function _kickReserveAuction() internal updateLocalStateAndPoolInterest { numberOfCalls['UBReserveHandler.kickReserveAuction']++; - (, uint256 claimableReserves, , , ) = _poolInfo.poolReservesInfo(address(_pool)); + uint256 claimableReserves = _getReservesInfo().claimableReserves; if (claimableReserves == 0) return; try _pool.kickReserveAuction() { @@ -40,16 +40,17 @@ abstract contract UnboundedReservePoolHandler is BaseHandler { // ensure actor always has the amount to take reserves _ensureAjnaAmount(_actor, 1e45); - (, uint256 claimableReservesBeforeAction, ,) = _pool.reservesInfo(); + uint256 claimableReservesBeforeTake = _getReservesInfo().claimableReservesRemaining; - try _pool.takeReserves(amount_) { - (, uint256 claimableReservesAfterAction, ,) = _pool.reservesInfo(); + try _pool.takeReserves( + amount_ + ) { + uint256 claimableReservesAfterTake = _getReservesInfo().claimableReservesRemaining; // reserves are guaranteed by the protocol) require( - claimableReservesAfterAction < claimableReservesBeforeAction, + claimableReservesAfterTake < claimableReservesBeforeTake, "QT1: claimable reserve not avaialble to take" ); - } catch (bytes memory err) { _ensurePoolError(err); } From 451b82d9a8feb7cecfb3f55ce93406f066a79ecb Mon Sep 17 00:00:00 2001 From: grandizzy Date: Thu, 7 Dec 2023 13:03:46 +0200 Subject: [PATCH 3/5] Take 3 - more changes --- .../handlers/BasicERC20PoolHandler.sol | 44 ++++++----- .../handlers/BasicERC721PoolHandler.sol | 74 +++++++++++-------- .../unbounded/BaseERC721PoolHandler.sol | 10 ++- .../UnboundedBasicERC721PoolHandler.sol | 16 ++-- .../base/handlers/unbounded/BaseHandler.sol | 18 ++++- .../UnboundedLiquidationPoolHandler.sol | 74 ++++++++++--------- 6 files changed, 142 insertions(+), 94 deletions(-) diff --git a/tests/forge/invariants/ERC20Pool/handlers/BasicERC20PoolHandler.sol b/tests/forge/invariants/ERC20Pool/handlers/BasicERC20PoolHandler.sol index 51c90a089..5a0c5f0ee 100644 --- a/tests/forge/invariants/ERC20Pool/handlers/BasicERC20PoolHandler.sol +++ b/tests/forge/invariants/ERC20Pool/handlers/BasicERC20PoolHandler.sol @@ -74,8 +74,7 @@ contract BasicERC20PoolHandler is UnboundedBasicERC20PoolHandler, BasicPoolHandl numberOfCalls['BBasicHandler.pledgeCollateral']++; // borrower cannot make any action when in auction - (uint256 kickTime,,,,,,,,) = _poolInfo.auctionStatus(address(_pool), _actor); - if (kickTime != 0) return; + if (_getAuctionInfo(_actor).kickTime != 0) return; // Prepare test phase uint256 boundedAmount = _prePledgeCollateral(amountToPledge_); @@ -92,8 +91,7 @@ contract BasicERC20PoolHandler is UnboundedBasicERC20PoolHandler, BasicPoolHandl numberOfCalls['BBasicHandler.pullCollateral']++; // borrower cannot make any action when in auction - (uint256 kickTime,,,,,,,,) = _poolInfo.auctionStatus(address(_pool), _actor); - if (kickTime != 0) return; + if (_getAuctionInfo(_actor).kickTime != 0) return; // Prepare test phase uint256 boundedAmount = _prePullCollateral(amountToPull_); @@ -110,8 +108,7 @@ contract BasicERC20PoolHandler is UnboundedBasicERC20PoolHandler, BasicPoolHandl numberOfCalls['BBasicHandler.drawDebt']++; // borrower cannot make any action when in auction - (uint256 kickTime,,,,,,,,) = _poolInfo.auctionStatus(address(_pool), _actor); - if (kickTime != 0) return; + if (_getAuctionInfo(_actor).kickTime != 0) return; // Prepare test phase uint256 boundedAmount = _preDrawDebt(amountToBorrow_); @@ -128,8 +125,7 @@ contract BasicERC20PoolHandler is UnboundedBasicERC20PoolHandler, BasicPoolHandl numberOfCalls['BBasicHandler.repayDebt']++; // borrower cannot make any action when in auction - (uint256 kickTime,,,,,,,,) = _poolInfo.auctionStatus(address(_pool), _actor); - if (kickTime != 0) return; + if (_getAuctionInfo(_actor).kickTime != 0) return; // Prepare test phase uint256 boundedAmount = _preRepayDebt(amountToRepay_); @@ -145,35 +141,46 @@ contract BasicERC20PoolHandler is UnboundedBasicERC20PoolHandler, BasicPoolHandl function _preAddCollateral( uint256 amountToAdd_ ) internal view returns (uint256 boundedAmount_) { - boundedAmount_ = constrictToRange(amountToAdd_, MIN_COLLATERAL_AMOUNT, MAX_COLLATERAL_AMOUNT); + boundedAmount_ = constrictToRange( + amountToAdd_, MIN_COLLATERAL_AMOUNT, MAX_COLLATERAL_AMOUNT + ); } function _preRemoveCollateral( uint256 amountToRemove_ ) internal returns (uint256 boundedAmount_) { - boundedAmount_ = constrictToRange(amountToRemove_, MIN_COLLATERAL_AMOUNT, MAX_COLLATERAL_AMOUNT); + boundedAmount_ = constrictToRange( + amountToRemove_, MIN_COLLATERAL_AMOUNT, MAX_COLLATERAL_AMOUNT + ); // ensure actor has collateral to remove - (uint256 lpBalanceBefore, ) = _pool.lenderInfo(_lenderBucketIndex, _actor); - if (lpBalanceBefore == 0) _addCollateral(boundedAmount_, _lenderBucketIndex); + if (_getLenderInfo(_lenderBucketIndex, _actor).lpBalance == 0) { + _addCollateral(boundedAmount_, _lenderBucketIndex); + } } function _prePledgeCollateral( uint256 amountToPledge_ ) internal view returns (uint256 boundedAmount_) { - boundedAmount_ = constrictToRange(amountToPledge_, _erc20Pool.collateralScale(), MAX_COLLATERAL_AMOUNT); + boundedAmount_ = constrictToRange( + amountToPledge_, _erc20Pool.collateralScale(), MAX_COLLATERAL_AMOUNT + ); } function _prePullCollateral( uint256 amountToPull_ ) internal view returns (uint256 boundedAmount_) { - boundedAmount_ = constrictToRange(amountToPull_, MIN_COLLATERAL_AMOUNT, MAX_COLLATERAL_AMOUNT); + boundedAmount_ = constrictToRange( + amountToPull_, MIN_COLLATERAL_AMOUNT, MAX_COLLATERAL_AMOUNT + ); } function _preDrawDebt( uint256 amountToBorrow_ ) internal override returns (uint256 boundedAmount_) { - boundedAmount_ = constrictToRange(amountToBorrow_, MIN_DEBT_AMOUNT, MAX_DEBT_AMOUNT); + boundedAmount_ = constrictToRange( + amountToBorrow_, MIN_DEBT_AMOUNT, MAX_DEBT_AMOUNT + ); // borrower cannot make any action when in auction (uint256 kickTime, uint256 collateral,,,,,,,) = _poolInfo.auctionStatus(address(_pool), _actor); @@ -218,11 +225,12 @@ contract BasicERC20PoolHandler is UnboundedBasicERC20PoolHandler, BasicPoolHandl function _preRepayDebt( uint256 amountToRepay_ ) internal returns (uint256 boundedAmount_) { - boundedAmount_ = constrictToRange(amountToRepay_, Maths.max(_pool.quoteTokenScale(), MIN_QUOTE_AMOUNT), MAX_QUOTE_AMOUNT); + boundedAmount_ = constrictToRange( + amountToRepay_, Maths.max(_pool.quoteTokenScale(), MIN_QUOTE_AMOUNT), MAX_QUOTE_AMOUNT + ); // ensure actor has debt to repay - (uint256 debt, , ) = PoolInfoUtils(_poolInfo).borrowerInfo(address(_pool), _actor); - if (debt == 0) { + if (_getBorrowerInfo(_actor).debt == 0) { boundedAmount_ = _preDrawDebt(boundedAmount_); _drawDebt(boundedAmount_); } diff --git a/tests/forge/invariants/ERC721Pool/handlers/BasicERC721PoolHandler.sol b/tests/forge/invariants/ERC721Pool/handlers/BasicERC721PoolHandler.sol index 74c352e53..6afd52d6d 100644 --- a/tests/forge/invariants/ERC721Pool/handlers/BasicERC721PoolHandler.sol +++ b/tests/forge/invariants/ERC721Pool/handlers/BasicERC721PoolHandler.sol @@ -88,8 +88,7 @@ contract BasicERC721PoolHandler is UnboundedBasicERC721PoolHandler, BasicPoolHan numberOfCalls['BBasicHandler.pledgeCollateral']++; // borrower cannot make any action when in auction - (uint256 kickTime,,,,,,,,) = _poolInfo.auctionStatus(address(_pool), _actor); - if (kickTime != 0) return; + if (_getAuctionInfo(_actor).kickTime != 0) return; // Prepare test phase uint256 boundedAmount = _prePledgeCollateral(amountToPledge_); @@ -106,8 +105,7 @@ contract BasicERC721PoolHandler is UnboundedBasicERC721PoolHandler, BasicPoolHan numberOfCalls['BBasicHandler.pullCollateral']++; // borrower cannot make any action when in auction - (uint256 kickTime,,,,,,,,) = _poolInfo.auctionStatus(address(_pool), _actor); - if (kickTime != 0) return; + if (_getAuctionInfo(_actor).kickTime != 0) return; // Prepare test phase uint256 boundedAmount = _prePullCollateral(amountToPull_); @@ -124,8 +122,7 @@ contract BasicERC721PoolHandler is UnboundedBasicERC721PoolHandler, BasicPoolHan numberOfCalls['BBasicHandler.drawDebt']++; // borrower cannot make any action when in auction - (uint256 kickTime,,,,,,,,) = _poolInfo.auctionStatus(address(_pool), _actor); - if (kickTime != 0) return; + if (_getAuctionInfo(_actor).kickTime != 0) return; // Prepare test phase uint256 boundedAmount = _preDrawDebt(amountToBorrow_); @@ -142,8 +139,7 @@ contract BasicERC721PoolHandler is UnboundedBasicERC721PoolHandler, BasicPoolHan numberOfCalls['BBasicHandler.repayDebt']++; // borrower cannot make any action when in auction - (uint256 kickTime,,,,,,,,) = _poolInfo.auctionStatus(address(_pool), _actor); - if (kickTime != 0) return; + if (_getAuctionInfo(_actor).kickTime != 0) return; // Prepare test phase uint256 boundedAmount = _preRepayDebt(amountToRepay_); @@ -159,17 +155,22 @@ contract BasicERC721PoolHandler is UnboundedBasicERC721PoolHandler, BasicPoolHan function _preAddCollateral( uint256 amountToAdd_ ) internal view returns (uint256 boundedAmount_) { - boundedAmount_ = constrictToRange(amountToAdd_, MIN_COLLATERAL_AMOUNT, MAX_COLLATERAL_AMOUNT); + boundedAmount_ = constrictToRange( + amountToAdd_, MIN_COLLATERAL_AMOUNT, MAX_COLLATERAL_AMOUNT + ); } function _preRemoveCollateral( uint256 amountToRemove_ ) internal returns (uint256 boundedAmount_) { - boundedAmount_ = constrictToRange(amountToRemove_, MIN_COLLATERAL_AMOUNT, MAX_COLLATERAL_AMOUNT); + boundedAmount_ = constrictToRange( + amountToRemove_, MIN_COLLATERAL_AMOUNT, MAX_COLLATERAL_AMOUNT + ); // ensure actor has collateral to remove - (uint256 lpBalanceBefore, ) = _pool.lenderInfo(_lenderBucketIndex, _actor); - if (lpBalanceBefore == 0) _addCollateral(boundedAmount_, _lenderBucketIndex); + if (_getLenderInfo(_lenderBucketIndex, _actor).lpBalance == 0) { + _addCollateral(boundedAmount_, _lenderBucketIndex); + } } function _preMergeCollateral() internal returns(uint256 NFTAmount_, uint256[] memory bucketIndexes_) { @@ -179,12 +180,16 @@ contract BasicERC721PoolHandler is UnboundedBasicERC721PoolHandler, BasicPoolHan uint256 bucketIndex = bucketIndexes_[i]; // Add Quote token in each bucket such that user has enough lps in each bucket to merge collateral - uint256 price = _poolInfo.indexToPrice(bucketIndex); - _addQuoteToken(price, bucketIndex); + _addQuoteToken( + _poolInfo.indexToPrice(bucketIndex), + bucketIndex + ); + + uint256 collateralAmount = _poolInfo.lpToCollateral( + address(_erc721Pool), _getLenderInfo(bucketIndex, _actor).lpBalance, bucketIndex + ); - (uint256 lenderLps, ) = _erc721Pool.lenderInfo(bucketIndex, _actor); - uint256 collateralAmount =_poolInfo.lpToCollateral(address(_erc721Pool), lenderLps, bucketIndex); - NFTAmount_ += collateralAmount; + NFTAmount_ += collateralAmount; } // Round collateral amount @@ -194,23 +199,28 @@ contract BasicERC721PoolHandler is UnboundedBasicERC721PoolHandler, BasicPoolHan function _prePledgeCollateral( uint256 amountToPledge_ ) internal view returns (uint256 boundedAmount_) { - boundedAmount_ = constrictToRange(amountToPledge_, MIN_COLLATERAL_AMOUNT, MAX_COLLATERAL_AMOUNT); + boundedAmount_ = constrictToRange( + amountToPledge_, MIN_COLLATERAL_AMOUNT, MAX_COLLATERAL_AMOUNT + ); } function _prePullCollateral( uint256 amountToPull_ ) internal view returns (uint256 boundedAmount_) { - boundedAmount_ = constrictToRange(amountToPull_, 0, MAX_COLLATERAL_AMOUNT); + boundedAmount_ = constrictToRange( + amountToPull_, 0, MAX_COLLATERAL_AMOUNT + ); } function _preDrawDebt( uint256 amountToBorrow_ ) internal override returns (uint256 boundedAmount_) { - boundedAmount_ = constrictToRange(amountToBorrow_, MIN_DEBT_AMOUNT, MAX_DEBT_AMOUNT); + boundedAmount_ = constrictToRange( + amountToBorrow_, MIN_DEBT_AMOUNT, MAX_DEBT_AMOUNT + ); // borrower cannot make any action when in auction - (uint256 kickTime, uint256 collateral, uint256 debt,,,,,,) = _poolInfo.auctionStatus(address(_pool), _actor); - if (kickTime != 0) return boundedAmount_; + if (_getAuctionInfo(_actor).kickTime != 0) return boundedAmount_; // Pre Condition // 1. borrower's debt should exceed minDebt @@ -218,7 +228,6 @@ contract BasicERC721PoolHandler is UnboundedBasicERC721PoolHandler, BasicPoolHan // 3. drawDebt should not make borrower under collateralized // 1. borrower's debt should exceed minDebt - (debt, collateral, ) = _poolInfo.borrowerInfo(address(_pool), _actor); (uint256 minDebt, , , ) = _poolInfo.poolUtilizationInfo(address(_pool)); if (boundedAmount_ < minDebt && minDebt < MAX_DEBT_AMOUNT) boundedAmount_ = minDebt + 1; @@ -235,27 +244,30 @@ contract BasicERC721PoolHandler is UnboundedBasicERC721PoolHandler, BasicPoolHan (uint256 currentPoolDebt, , , ) = _pool.debtInfo(); uint256 nextPoolDebt = currentPoolDebt + boundedAmount_; uint256 newLup = _priceAt(_pool.depositIndex(nextPoolDebt)); - (debt, collateral, ) = _poolInfo.borrowerInfo(address(_pool), _actor); + BorrowerInfo memory borrowerInfo = _getBorrowerInfo(_actor); // repay debt if borrower becomes undercollateralized with new debt at new lup - if (!_isCollateralized(debt + boundedAmount_, collateral, newLup, _pool.poolType())) { + if (!_isCollateralized( + borrowerInfo.debt + boundedAmount_, borrowerInfo.collateral, newLup, _pool.poolType()) + ) { _repayDebt(type(uint256).max); - (debt, collateral, ) = _poolInfo.borrowerInfo(address(_pool), _actor); - _pullCollateral(collateral); + borrowerInfo = _getBorrowerInfo(_actor); + _pullCollateral(borrowerInfo.collateral); - require(debt == 0, "borrower has debt"); + require(borrowerInfo.debt == 0, "borrower has debt"); } } function _preRepayDebt( uint256 amountToRepay_ ) internal returns (uint256 boundedAmount_) { - boundedAmount_ = constrictToRange(amountToRepay_, Maths.max(_pool.quoteTokenScale(), MIN_QUOTE_AMOUNT), MAX_QUOTE_AMOUNT); + boundedAmount_ = constrictToRange( + amountToRepay_, Maths.max(_pool.quoteTokenScale(), MIN_QUOTE_AMOUNT), MAX_QUOTE_AMOUNT + ); // ensure actor has debt to repay - (uint256 debt, , ) = PoolInfoUtils(_poolInfo).borrowerInfo(address(_pool), _actor); - if (debt == 0) { + if (_getBorrowerInfo(_actor).debt == 0) { boundedAmount_ = _preDrawDebt(boundedAmount_); _drawDebt(boundedAmount_); } diff --git a/tests/forge/invariants/ERC721Pool/handlers/unbounded/BaseERC721PoolHandler.sol b/tests/forge/invariants/ERC721Pool/handlers/unbounded/BaseERC721PoolHandler.sol index 70b249508..daaa1b099 100644 --- a/tests/forge/invariants/ERC721Pool/handlers/unbounded/BaseERC721PoolHandler.sol +++ b/tests/forge/invariants/ERC721Pool/handlers/unbounded/BaseERC721PoolHandler.sol @@ -103,15 +103,19 @@ abstract contract BaseERC721PoolHandler is BaseHandler { , , , ) = _poolInfo.auctionStatus(address(_erc721Pool), borrower_); - try _erc721Pool.repayDebt(borrower_, amount_, 0, borrower_, 7388) { - + try _erc721Pool.repayDebt( + borrower_, + amount_, + 0, + borrower_, + 7388 + ) { _recordSettleBucket( borrower_, borrowerCollateralBefore, kickTimeBefore, auctionPrice ); - } catch (bytes memory err) { _ensurePoolError(err); } diff --git a/tests/forge/invariants/ERC721Pool/handlers/unbounded/UnboundedBasicERC721PoolHandler.sol b/tests/forge/invariants/ERC721Pool/handlers/unbounded/UnboundedBasicERC721PoolHandler.sol index 80649c99f..b1fe50320 100644 --- a/tests/forge/invariants/ERC721Pool/handlers/unbounded/UnboundedBasicERC721PoolHandler.sol +++ b/tests/forge/invariants/ERC721Pool/handlers/unbounded/UnboundedBasicERC721PoolHandler.sol @@ -112,7 +112,7 @@ abstract contract UnboundedBasicERC721PoolHandler is UnboundedBasicPoolHandler, ) internal updateLocalStateAndPoolInterest { numberOfCalls['UBBasicHandler.pledgeCollateral']++; - (, uint256 borrowerCollateralBefore, ) = _pool.borrowerInfo(_actor); + uint256 borrowerCollateralBefore = _getBorrowerInfo(_actor).collateral; (uint256 kickTimeBefore, , , , uint256 auctionPrice, , , , ) =_poolInfo.auctionStatus(address(_erc721Pool), _actor); // **R1**: Exchange rates are unchanged by pledging collateral @@ -134,7 +134,6 @@ abstract contract UnboundedBasicERC721PoolHandler is UnboundedBasicPoolHandler, kickTimeBefore, auctionPrice ); - } catch (bytes memory err) { _ensurePoolError(err); } @@ -175,7 +174,10 @@ abstract contract UnboundedBasicERC721PoolHandler is UnboundedBasicPoolHandler, if (bucket > LENDER_MAX_BUCKET_INDEX) return; // calculates collateral required to borrow quote tokens, added 1 for roundup such that 0.8 NFT will become 1 - uint256 collateralToPledge = Maths.wdiv(Maths.wmul(COLLATERALIZATION_FACTOR, amount_), price) / 1e18 + 1; + uint256 collateralToPledge = Maths.wdiv( + Maths.wmul(COLLATERALIZATION_FACTOR, amount_), + price + ) / 1e18 + 1; _ensureCollateralAmount(_actor, collateralToPledge); uint256[] memory tokenIds = new uint256[](collateralToPledge); @@ -185,7 +187,7 @@ abstract contract UnboundedBasicERC721PoolHandler is UnboundedBasicPoolHandler, (uint256 interestRate, ) = _erc721Pool.interestRateInfo(); - (, uint256 borrowerCollateralBefore, ) = _pool.borrowerInfo(_actor); + uint256 borrowerCollateralBefore = _getBorrowerInfo(_actor).collateral; (uint256 kickTimeBefore, , , , uint256 auctionPrice, , , , ) =_poolInfo.auctionStatus(address(_erc721Pool), _actor); try _erc721Pool.drawDebt(_actor, amount_, 7388, tokenIds) { @@ -214,17 +216,17 @@ abstract contract UnboundedBasicERC721PoolHandler is UnboundedBasicPoolHandler, ) internal updateLocalStateAndPoolInterest { numberOfCalls['UBBasicHandler.repayDebt']++; - (uint256 borrowerDebt, uint256 borrowerCollateralBefore, ) = _poolInfo.borrowerInfo(address(_pool), _actor); + BorrowerInfo memory borrowerInfoBeforeAction = _getBorrowerInfo(_actor); (uint256 kickTimeBefore, , , , uint256 auctionPrice, , , , ) =_poolInfo.auctionStatus(address(_erc721Pool), _actor); // ensure actor always has amount of quote to repay - _ensureQuoteAmount(_actor, borrowerDebt + 10 * 1e18); + _ensureQuoteAmount(_actor, borrowerInfoBeforeAction.debt + 10 * 1e18); try _erc721Pool.repayDebt(_actor, amountToRepay_, 0, _actor, 7388) { _recordSettleBucket( _actor, - borrowerCollateralBefore, + borrowerInfoBeforeAction.collateral, kickTimeBefore, auctionPrice ); diff --git a/tests/forge/invariants/base/handlers/unbounded/BaseHandler.sol b/tests/forge/invariants/base/handlers/unbounded/BaseHandler.sol index fa8513cb8..fcdffce5c 100644 --- a/tests/forge/invariants/base/handlers/unbounded/BaseHandler.sol +++ b/tests/forge/invariants/base/handlers/unbounded/BaseHandler.sol @@ -30,8 +30,10 @@ abstract contract BaseHandler is Test { using EnumerableSet for EnumerableSet.UintSet; struct BorrowerInfo { + uint256 t0Debt; uint256 debt; uint256 collateral; + uint256 npTpRatio; uint256 t0Np; } @@ -324,6 +326,12 @@ abstract contract BaseHandler is Test { borrowerInfo_.collateral, borrowerInfo_.t0Np ) = _poolInfo.borrowerInfo(address(_pool), borrower_); + + ( + borrowerInfo_.t0Debt, + , + borrowerInfo_.npTpRatio + ) = _pool.borrowerInfo(borrower_); } function _getBucketInfo(uint256 index_) internal view returns (BucketInfo memory bucketInfo_) { @@ -566,10 +574,16 @@ abstract contract BaseHandler is Test { uint256 kickTimeBefore_, uint256 auctionPrice_ ) internal { - (uint256 kickTimeAfter, , , , , , , , ) = _poolInfo.auctionStatus(address(_pool), borrower_); + uint256 kickTimeAfter = _getAuctionInfo(borrower_).kickTime; // **CT2**: Keep track of bucketIndex when borrower is removed from auction to check collateral added into that bucket - if (kickTimeBefore_ != 0 && kickTimeAfter == 0 && borrowerCollateralBefore_ % 1e18 != 0) { + if ( + kickTimeBefore_ != 0 + && + kickTimeAfter == 0 + && + borrowerCollateralBefore_ % 1e18 != 0 + ) { if (auctionPrice_ < MIN_PRICE) { buckets.add(7388); lenderDepositTime[borrower_][7388] = block.timestamp; diff --git a/tests/forge/invariants/base/handlers/unbounded/UnboundedLiquidationPoolHandler.sol b/tests/forge/invariants/base/handlers/unbounded/UnboundedLiquidationPoolHandler.sol index ed21ec361..3c0be2695 100644 --- a/tests/forge/invariants/base/handlers/unbounded/UnboundedLiquidationPoolHandler.sol +++ b/tests/forge/invariants/base/handlers/unbounded/UnboundedLiquidationPoolHandler.sol @@ -103,8 +103,10 @@ abstract contract UnboundedLiquidationPoolHandler is BaseHandler { KickerInfo memory kickerInfoBeforeWithdraw = _getKickerInfo(_actor); uint256 poolBalanceBeforeWithdraw = _getPoolQuoteBalance(); - try _pool.withdrawBonds(kicker_, maxAmount_) { - + try _pool.withdrawBonds( + kicker_, + maxAmount_ + ) { KickerInfo memory kickerInfoAfterWithdraw = _getKickerInfo(_actor); uint256 poolBalanceAfterWithdraw = _getPoolQuoteBalance(); @@ -116,7 +118,6 @@ abstract contract UnboundedLiquidationPoolHandler is BaseHandler { // **A7**: totalBondEscrowed should decrease only when kicker bonds withdrawned decreaseInBonds += poolBalanceBeforeWithdraw - poolBalanceAfterWithdraw; - } catch (bytes memory err) { _ensurePoolError(err); } @@ -338,11 +339,10 @@ abstract contract UnboundedLiquidationPoolHandler is BaseHandler { uint256 maxDepth_ ) internal updateLocalStateAndPoolInterest { numberOfCalls['UBLiquidationHandler.settleAuction']++; - ( - uint256 borrowerT0Debt, - uint256 collateral, - ) = _pool.borrowerInfo(borrower_); - (uint256 reservesBeforeAction, , , , )= _poolInfo.poolReservesInfo(address(_pool)); + + BorrowerInfo memory borrowerInfo = _getBorrowerInfo(borrower_); + + uint256 reservesBeforeAction = _getReservesInfo().reserves; (uint256 inflator, ) = _pool.inflatorInfo(); try _pool.settle( @@ -352,14 +352,14 @@ abstract contract UnboundedLiquidationPoolHandler is BaseHandler { numberOfActions['settle']++; // settle borrower debt with exchanging borrower collateral with quote tokens starting from hpb - while (maxDepth_ != 0 && borrowerT0Debt != 0 && collateral != 0) { + while (maxDepth_ != 0 && borrowerInfo.t0Debt != 0 && borrowerInfo.collateral != 0) { uint256 bucketIndex = fenwickIndexForSum(1); - uint256 maxSettleableDebt = Maths.floorWmul(collateral, _priceAt(bucketIndex)); + uint256 maxSettleableDebt = Maths.floorWmul(borrowerInfo.collateral, _priceAt(bucketIndex)); uint256 fenwickDeposit = fenwickDeposits[bucketIndex]; - uint256 borrowerDebt = Maths.wmul(borrowerT0Debt, inflator); + uint256 borrowerDebt = Maths.wmul(borrowerInfo.t0Debt, inflator); if (fenwickDeposit == 0 && maxSettleableDebt != 0) { - collateral = 0; + borrowerInfo.collateral = 0; // Deposits in the tree is zero, insert entire collateral into lowest bucket 7388 // **B5**: when settle with collateral: record min bucket where collateral added buckets.add(7388); @@ -369,23 +369,26 @@ abstract contract UnboundedLiquidationPoolHandler is BaseHandler { // enough deposit in bucket and collateral avail to settle entire debt if (fenwickDeposit >= borrowerDebt && maxSettleableDebt >= borrowerDebt) { fenwickDeposits[bucketIndex] -= borrowerDebt; - collateral -= Maths.ceilWdiv(borrowerDebt, _priceAt(bucketIndex)); - borrowerT0Debt = 0; + + borrowerInfo.collateral -= Maths.ceilWdiv(borrowerDebt, _priceAt(bucketIndex)); + borrowerInfo.t0Debt = 0; } // enough collateral, therefore not enough deposit to settle entire debt, we settle only deposit amount else if (maxSettleableDebt >= fenwickDeposit) { fenwickDeposits[bucketIndex] = 0; - collateral -= Maths.ceilWdiv(fenwickDeposit, _priceAt(bucketIndex)); - borrowerT0Debt -= Maths.floorWdiv(fenwickDeposit, inflator); + + borrowerInfo.collateral -= Maths.ceilWdiv(fenwickDeposit, _priceAt(bucketIndex)); + borrowerInfo.t0Debt -= Maths.floorWdiv(fenwickDeposit, inflator); } // exchange all collateral with deposit else { fenwickDeposits[bucketIndex] -= maxSettleableDebt; - collateral = 0; - borrowerT0Debt -= Maths.floorWdiv(maxSettleableDebt, inflator); + + borrowerInfo.collateral = 0; + borrowerInfo.t0Debt -= Maths.floorWdiv(maxSettleableDebt, inflator); } } else { - collateral = 0; + borrowerInfo.collateral = 0; // **B5**: when settle with collateral: record min bucket where collateral added. // Lender doesn't get any LP when settle bad debt. buckets.add(7388); @@ -396,9 +399,10 @@ abstract contract UnboundedLiquidationPoolHandler is BaseHandler { } // if collateral becomes 0 and still debt is left, settle debt by reserves and hpb making buckets bankrupt - if (borrowerT0Debt != 0 && collateral == 0) { + if (borrowerInfo.t0Debt != 0 && borrowerInfo.collateral == 0) { + + uint256 reservesAfterAction = _getReservesInfo().reserves; - (uint256 reservesAfterAction, , , , )= _poolInfo.poolReservesInfo(address(_pool)); if (reservesBeforeAction > reservesAfterAction) { // **RE12**: Reserves decrease by amount of reserve used to settle a auction decreaseInReserves = reservesBeforeAction - reservesAfterAction; @@ -406,23 +410,28 @@ abstract contract UnboundedLiquidationPoolHandler is BaseHandler { // Reserves might increase upto 2 WAD due to rounding issue increaseInReserves = reservesAfterAction - reservesBeforeAction; } - borrowerT0Debt -= Maths.min(Maths.wdiv(decreaseInReserves, inflator), borrowerT0Debt); + borrowerInfo.t0Debt -= Maths.min( + Maths.wdiv(decreaseInReserves, inflator), + borrowerInfo.t0Debt + ); - while (maxDepth_ != 0 && borrowerT0Debt != 0) { + while (maxDepth_ != 0 && borrowerInfo.t0Debt != 0) { uint256 bucketIndex = fenwickIndexForSum(1); uint256 fenwickDeposit = fenwickDeposits[bucketIndex]; - uint256 borrowerDebt = Maths.wmul(borrowerT0Debt, inflator); + uint256 borrowerDebt = Maths.wmul(borrowerInfo.t0Debt, inflator); if (bucketIndex != MAX_FENWICK_INDEX) { // debt is greater than bucket deposit if (borrowerDebt > fenwickDeposit) { fenwickDeposits[bucketIndex] = 0; - borrowerT0Debt -= Maths.floorWdiv(fenwickDeposit, inflator); + + borrowerInfo.t0Debt -= Maths.floorWdiv(fenwickDeposit, inflator); } // bucket deposit is greater than debt else { fenwickDeposits[bucketIndex] -= borrowerDebt; - borrowerT0Debt = 0; + + borrowerInfo.t0Debt = 0; } } @@ -433,7 +442,7 @@ abstract contract UnboundedLiquidationPoolHandler is BaseHandler { if ( _getAuctionInfo(borrower_).kickTime == 0 && - collateral % 1e18 != 0 + borrowerInfo.collateral % 1e18 != 0 && _pool.poolType() == 1 ) { @@ -468,12 +477,11 @@ abstract contract UnboundedLiquidationPoolHandler is BaseHandler { // Helper function to calculate quote tokens from lps in a bucket irrespective of deposit available. // LP rewarded -> quote token rounded up (as LP rewarded are calculated as rewarded quote token -> LP rounded down) function _rewardedLpToQuoteToken(uint256 lps_, uint256 bucketIndex_) internal view returns(uint256 quoteTokens_) { - (uint256 bucketLP, uint256 bucketCollateral , , uint256 bucketDeposit, ) = _pool.bucketInfo(bucketIndex_); - - quoteTokens_ = Buckets.lpToQuoteTokens( - bucketCollateral, - bucketLP, - bucketDeposit, + BucketInfo memory bucketInfo = _getBucketInfo(bucketIndex_); + quoteTokens_ = Buckets.lpToQuoteTokens( + bucketInfo.collateral, + bucketInfo.lpBalance, + bucketInfo.deposit, lps_, _priceAt(bucketIndex_), Math.Rounding.Up From 881fea6442a42a26e72324aed5fc96a7d79b466d Mon Sep 17 00:00:00 2001 From: prateek105 Date: Thu, 7 Dec 2023 18:53:55 +0530 Subject: [PATCH 4/5] Fix failing unit tests --- tests/forge/unit/ERC20Pool/ERC20PoolFactory.t.sol | 2 +- tests/forge/unit/Positions/PositionManager.t.sol | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/forge/unit/ERC20Pool/ERC20PoolFactory.t.sol b/tests/forge/unit/ERC20Pool/ERC20PoolFactory.t.sol index ab19a4bb2..5526c6de8 100644 --- a/tests/forge/unit/ERC20Pool/ERC20PoolFactory.t.sol +++ b/tests/forge/unit/ERC20Pool/ERC20PoolFactory.t.sol @@ -12,7 +12,7 @@ import { IPoolErrors } from 'src/interfaces/pool/commons/IPoolErrors.sol'; import { IPoolFactory } from 'src/interfaces/pool/IPoolFactory.sol'; contract ERC20PoolFactoryTest is ERC20HelperContract { - address immutable poolAddress = 0x3Cd0C8d97cD0291E178560E5675dD27AeD0A90d1; + address immutable poolAddress = 0x9Fd7552Da37D2D296CB4a84d507c35Dcc926220a; function setUp() external { // deploy new pool factory for factory tests diff --git a/tests/forge/unit/Positions/PositionManager.t.sol b/tests/forge/unit/Positions/PositionManager.t.sol index 373ba1279..ac3d0b35c 100644 --- a/tests/forge/unit/Positions/PositionManager.t.sol +++ b/tests/forge/unit/Positions/PositionManager.t.sol @@ -1338,7 +1338,7 @@ contract PositionManagerERC20PoolTest is PositionManagerERC20PoolHelperContract _positionManager.nonces(5); // check domain separator matches expectations for the test chain - assertEq(_positionManager.DOMAIN_SEPARATOR(), 0x3d157903719b756bd03176f9e889ef6b5c39980675541c11c005ea6c4e1b6c07); + assertEq(_positionManager.DOMAIN_SEPARATOR(), 0x255893ac72554d931c70a8c246ff1216c7122c81a4d4f7f2a2eb5377f2481f12); } function testPermitReverts() external { From 396bcf6585f79ce09fc56904e060a2cb3d55dae6 Mon Sep 17 00:00:00 2001 From: grandizzy Date: Thu, 7 Dec 2023 17:19:14 +0200 Subject: [PATCH 5/5] Take 4 --- .../base/handlers/ReservePoolHandler.sol | 8 ++-- .../base/handlers/unbounded/BaseHandler.sol | 42 ++++++++++++------- 2 files changed, 31 insertions(+), 19 deletions(-) diff --git a/tests/forge/invariants/base/handlers/ReservePoolHandler.sol b/tests/forge/invariants/base/handlers/ReservePoolHandler.sol index d2d13f5d0..1e65a882e 100644 --- a/tests/forge/invariants/base/handlers/ReservePoolHandler.sol +++ b/tests/forge/invariants/base/handlers/ReservePoolHandler.sol @@ -19,8 +19,7 @@ abstract contract ReservePoolHandler is UnboundedReservePoolHandler, Liquidation numberOfCalls['BReserveHandler.kickReserveAuction']++; // take all reserves if available - (, , uint256 claimableReservesRemaining, , ) = _poolInfo.poolReservesInfo(address(_pool)); - _takeReserves(claimableReservesRemaining); + _takeReserves(_getReservesInfo().claimableReservesRemaining); // Action phase _kickReserveAuction(); @@ -34,13 +33,12 @@ abstract contract ReservePoolHandler is UnboundedReservePoolHandler, Liquidation numberOfCalls['BReserveHandler.takeReserves']++; // kick reserve auction if claimable reserves available - (, uint256 claimableReserves, , , ) = _poolInfo.poolReservesInfo(address(_pool)); - if (claimableReserves != 0) { + if (_getReservesInfo().claimableReserves != 0) { _kickReserveAuction(); } // take reserve auction if remaining claimable reserves - (, , uint256 claimableReservesRemaining, , ) = _poolInfo.poolReservesInfo(address(_pool)); + uint256 claimableReservesRemaining = _getReservesInfo().claimableReservesRemaining; if (claimableReservesRemaining != 0) { uint256 boundedAmount = constrictToRange( amountToTake_, claimableReservesRemaining / 2, claimableReservesRemaining diff --git a/tests/forge/invariants/base/handlers/unbounded/BaseHandler.sol b/tests/forge/invariants/base/handlers/unbounded/BaseHandler.sol index fcdffce5c..8b9d046fc 100644 --- a/tests/forge/invariants/base/handlers/unbounded/BaseHandler.sol +++ b/tests/forge/invariants/base/handlers/unbounded/BaseHandler.sol @@ -182,11 +182,11 @@ abstract contract BaseHandler is Test { address currentActor = _actor; // clear head auction if more than 72 hours passed - (, , , , , , , address headAuction, , ) = _pool.auctionInfo(address(0)); + address headAuction = _getAuctionInfo(address(0)).head; if (headAuction != address(0)) { - (, , , uint256 kickTime, , , , , , ) = _pool.auctionInfo(headAuction); + uint256 kickTime = _getAuctionInfo(headAuction).kickTime; if (block.timestamp - kickTime > 72 hours) { - (uint256 auctionedDebt, , ) = _poolInfo.borrowerInfo(address(_pool), headAuction); + uint256 auctionedDebt = _getBorrowerInfo(headAuction).debt; try vm.startPrank(headAuction) { } catch { @@ -213,7 +213,7 @@ abstract contract BaseHandler is Test { (address borrower, , ) = _pool.loansInfo(); if (borrower != address(0)) { - (uint256 debt, , ) = _poolInfo.borrowerInfo(address(_pool), borrower); + uint256 debt = _getBorrowerInfo(borrower).debt; try vm.startPrank(borrower) { } catch { @@ -270,7 +270,9 @@ abstract contract BaseHandler is Test { if (lenderBucketIndexes.length < 3) { // if actor has touched less than three buckets, add a new bucket - _lenderBucketIndex = constrictToRange(bucketIndex_, LENDER_MIN_BUCKET_INDEX, LENDER_MAX_BUCKET_INDEX); + _lenderBucketIndex = constrictToRange( + bucketIndex_, LENDER_MIN_BUCKET_INDEX, LENDER_MAX_BUCKET_INDEX + ); lenderBucketIndexes.push(_lenderBucketIndex); } else { @@ -290,7 +292,15 @@ abstract contract BaseHandler is Test { if (logToFile == true) { if (numberOfCalls["Write logs"]++ == 0) vm.writeFile(path, ""); - printInNextLine(string(abi.encodePacked("================= Handler Call : ", Strings.toString(numberOfCalls["Write logs"]), " =================="))); + printInNextLine( + string( + abi.encodePacked( + "================= Handler Call : ", + Strings.toString(numberOfCalls["Write logs"]), + " ==================" + ) + ) + ); } if (logVerbosity > 0) { @@ -479,7 +489,7 @@ abstract contract BaseHandler is Test { increaseInReserves = 0; decreaseInReserves = 0; // record reserves before each action - (previousReserves, , , , ) = _poolInfo.poolReservesInfo(address(_pool)); + previousReserves = _getReservesInfo().reserves; // reset penalties before each action borrowerPenalty = 0; @@ -559,8 +569,7 @@ abstract contract BaseHandler is Test { // update local fenwick to pool fenwick before each action function _updateLocalFenwick() internal { for (uint256 bucketIndex = LENDER_MIN_BUCKET_INDEX; bucketIndex <= LENDER_MAX_BUCKET_INDEX; bucketIndex++) { - (, , , uint256 deposits, ) = _pool.bucketInfo(bucketIndex); - fenwickDeposits[bucketIndex] = deposits; + fenwickDeposits[bucketIndex] = _getBucketInfo(bucketIndex).deposit; } } @@ -678,7 +687,11 @@ abstract contract BaseHandler is Test { uint256 bucketIndex = buckets.at(j); (uint256 lenderLps, ) = _pool.lenderInfo(bucketIndex, actors[i]); if (lenderLps != 0) { - data = string(abi.encodePacked("Lps at ", Strings.toString(bucketIndex), " = ", Strings.toString(lenderLps))); + data = string( + abi.encodePacked( + "Lps at ", Strings.toString(bucketIndex), " = ", Strings.toString(lenderLps) + ) + ); printLine(data); } } @@ -691,10 +704,11 @@ abstract contract BaseHandler is Test { for (uint256 i = 0; i < actors.length; i++) { printLine(""); printLog("Actor ", i + 1); - (uint256 debt, uint256 pledgedCollateral, ) = _poolInfo.borrowerInfo(address(_pool), actors[i]); - if (debt != 0 || pledgedCollateral != 0) { - printLog("Debt = ", debt); - printLog("Pledged collateral = ", pledgedCollateral); + + BorrowerInfo memory borrowerInfo = _getBorrowerInfo(actors[i]); + if (borrowerInfo.debt != 0 || borrowerInfo.collateral != 0) { + printLog("Debt = ", borrowerInfo.debt); + printLog("Pledged collateral = ", borrowerInfo.collateral); } } printInNextLine("=======================");