diff --git a/docs/Functions.md b/docs/Functions.md index 49445287b..6a1ee5799 100644 --- a/docs/Functions.md +++ b/docs/Functions.md @@ -25,6 +25,8 @@ - pool inflator and inflatorUpdate state reverts on: + - block timestamp greater than expiry TransactionExpired() + - head auction not cleared AuctionNotCleared() - LenderActions.addQuoteToken(): - invalid bucket index InvalidIndex() - same block when bucket becomes insolvent BucketBankruptcyBlock() diff --git a/src/base/Pool.sol b/src/base/Pool.sol index fba318b41..51310167d 100644 --- a/src/base/Pool.sol +++ b/src/base/Pool.sol @@ -149,6 +149,9 @@ abstract contract Pool is Clone, ReentrancyGuard, Multicall, IPool { uint256 expiry_ ) external override nonReentrant returns (uint256 bucketLP_) { _revertAfterExpiry(expiry_); + + _revertIfAuctionClearable(auctions, loans); + PoolState memory poolState = _accruePoolInterest(); // round to token precision diff --git a/tests/forge/unit/ERC20Pool/ERC20PoolLiquidationsLenderKick.t.sol b/tests/forge/unit/ERC20Pool/ERC20PoolLiquidationsLenderKick.t.sol index d8c7b1d14..fc2b976c7 100644 --- a/tests/forge/unit/ERC20Pool/ERC20PoolLiquidationsLenderKick.t.sol +++ b/tests/forge/unit/ERC20Pool/ERC20PoolLiquidationsLenderKick.t.sol @@ -1152,15 +1152,7 @@ contract ERC20PoolLiquidationsLenderKickAuctionTest is ERC20HelperContract { }) ); - // add more liquidity to settle auction - _mintQuoteAndApproveTokens(_lender1, 40_000 * 1e18); - _addLiquidity({ - from: _lender1, - amount: 40_000 * 1e18, - index: 2500, - lpAward: 39_985.826748556412134387 * 1e18, - newLup: 3_863.654368867279344664 * 1e18 - }); + _settle({ from: _lender1, @@ -1212,8 +1204,8 @@ contract ERC20PoolLiquidationsLenderKickAuctionTest is ERC20HelperContract { PoolParams({ htp: 0, lup: MAX_PRICE, - poolSize: 49_645.701045977641587831 * 1e18, - pledgedCollateral: 4_973.729764048432419297 * 1e18, + poolSize: 9_645.701045977641587831 * 1e18, + pledgedCollateral: 4_973.703521110015138686 * 1e18, encumberedCollateral: 0, poolDebt: 0, actualUtilization: 0, @@ -1229,8 +1221,8 @@ contract ERC20PoolLiquidationsLenderKickAuctionTest is ERC20HelperContract { _assertLenderLpBalance({ lender: _lender1, index: 2500, - lpBalance: 89_985.826748556412134387 * 1e18, - depositTime: _startTime + 80 hours + lpBalance: 50_000.000000000000000000 * 1e18, + depositTime: _startTime }); // assert lender1 as a kicker _assertKicker({ @@ -1256,7 +1248,7 @@ contract ERC20PoolLiquidationsLenderKickAuctionTest is ERC20HelperContract { _assertBorrower({ borrower: _borrower3, borrowerDebt: 0, - borrowerCollateral: 994.751412316543869246 * 1e18, + borrowerCollateral: 994.725169378126588635 * 1e18, borrowert0Np: 21.125293269230769068 * 1e18, borrowerCollateralization: 1 * 1e18 }); diff --git a/tests/forge/unit/ERC20Pool/ERC20PoolLiquidationsSettle.t.sol b/tests/forge/unit/ERC20Pool/ERC20PoolLiquidationsSettle.t.sol index bbb509694..265adb912 100644 --- a/tests/forge/unit/ERC20Pool/ERC20PoolLiquidationsSettle.t.sol +++ b/tests/forge/unit/ERC20Pool/ERC20PoolLiquidationsSettle.t.sol @@ -680,6 +680,23 @@ contract ERC20PoolLiquidationsSettleTest is ERC20HelperContract { borrowerCollateralization: 0.974363394700228467 * 1e18 }); + _addLiquidityWithPenalty({ + from: _lender1, + amount: 100 * 1e18, + amountAdded: 99.987671232876712300 * 1e18, + index: _i9_52, + lpAward: 99.987671232876712300 * 1e18, + newLup: 9.721295865031779605 * 1e18 + }); + + _addLiquidity({ + from: _lender1, + amount: 100 * 1e18, + index: _i9_91, + lpAward: 99.367201799558744044 * 1e18, + newLup: 9.721295865031779605 * 1e18 + }); + // take entire collateral _take({ from: _lender, @@ -716,15 +733,6 @@ contract ERC20PoolLiquidationsSettleTest is ERC20HelperContract { index: _i9_52 }); - // add liquidity in same block should be possible as debt was not yet settled / bucket is not yet insolvent - _addLiquidity({ - from: _lender1, - amount: 100 * 1e18, - index: _i9_91, - lpAward: 99.367201799558744044 * 1e18, - newLup: 9.721295865031779605 * 1e18 - }); - _assertLenderLpBalance({ lender: _lender1, index: _i9_91, @@ -732,16 +740,6 @@ contract ERC20PoolLiquidationsSettleTest is ERC20HelperContract { depositTime: _startTime + 100 days + 10 hours }); - // adding to a different bucket for testing move in same block with bucket bankruptcy - _addLiquidityWithPenalty({ - from: _lender1, - amount: 100 * 1e18, - amountAdded: 99.987671232876712300 * 1e18, - index: _i9_52, - lpAward: 99.987671232876712300 * 1e18, - newLup: 9.721295865031779605 * 1e18 - }); - // settle to make buckets insolvent // settle should work because there is still debt to settle but no collateral left to auction (even if 72 hours didn't pass from kick) _assertBorrower({ @@ -751,7 +749,7 @@ contract ERC20PoolLiquidationsSettleTest is ERC20HelperContract { borrowert0Np: 10.307611531622595991 * 1e18, borrowerCollateralization: 0 }); - + assertTrue(block.timestamp - kickTime < 72 hours); // assert auction was kicked less than 72 hours ago // LP forfeited when forgive bad debt should be reflected in BucketBankruptcy event @@ -889,7 +887,7 @@ contract ERC20PoolLiquidationsSettleTest is ERC20HelperContract { }); } - function testSettleZeroExchangeRateResidualBankruptcy() external { + function testSettleZeroExchangeRateResidualBankruptcy() external tearDown { // Borrower2 borrows _borrow({ from: _borrower2, @@ -968,6 +966,15 @@ contract ERC20PoolLiquidationsSettleTest is ERC20HelperContract { borrowerCollateralization: 0.974363394700228467 * 1e18 }); + // add liquidity in same block should be possible as debt was not yet settled / bucket is not yet insolvent + _addLiquidity({ + from: _lender1, + amount: 100 * 1e18, + index: _i9_91, + lpAward: 99.367201799558744044 * 1e18, + newLup: 9.721295865031779605 * 1e18 + }); + // take entire collateral _take({ from: _lender, @@ -979,15 +986,6 @@ contract ERC20PoolLiquidationsSettleTest is ERC20HelperContract { isReward: true }); - // add liquidity in same block should be possible as debt was not yet settled / bucket is not yet insolvent - _addLiquidity({ - from: _lender1, - amount: 100 * 1e18, - index: _i9_91, - lpAward: 99.367201799558744044 * 1e18, - newLup: 9.721295865031779605 * 1e18 - }); - _assertLenderLpBalance({ lender: _lender1, index: _i9_91, @@ -995,34 +993,9 @@ contract ERC20PoolLiquidationsSettleTest is ERC20HelperContract { depositTime: _startTime + 100 days + 10 hours }); - // adding to a different bucket for testing move in same block with bucket bankruptcy - _addLiquidityWithPenalty({ - from: _lender1, - amount: 100 * 1e18, - amountAdded: 99.987671232876712300 * 1e18, - index: _i9_52, - lpAward: 99.987671232876712300 * 1e18, - newLup: 9.721295865031779605 * 1e18 - }); - - (uint256 reserves, , , ,) = _poolUtils.poolReservesInfo(address(_pool)); - - // Add 10 more quote token than would be enough to cover debt, with reserves - _addLiquidity({ - from: _lender1, - amount: 2_884.311069344372084707 * 1e18 + 10 - reserves, - index: _i9_81, - lpAward: 2_020.307252493359351053 * 1e18, - newLup: 9.721295865031779605 * 1e18 - }); - - uint256 bucket1Deposit = 2_112.736560735960384000 * 1e18; - uint256 bucket2Deposit = 7_065.014537346601772213 * 1e18; - uint256 debtToSettle = 10_028.889031920233428709 * 1e18; - _assertBorrower({ borrower: _borrower2, - borrowerDebt: debtToSettle, + borrowerDebt: 10_028.889031920233428709 * 1e18, borrowerCollateral: 0, borrowert0Np: 10.307611531622595991 * 1e18, borrowerCollateralization: 0 @@ -1032,23 +1005,13 @@ contract ERC20PoolLiquidationsSettleTest is ERC20HelperContract { index: _i9_91, lpBalance: 2_099.367201799558744044 * 1e18, collateral: 0, - deposit: bucket1Deposit, - exchangeRate: 1.006368280367980193 * 1e18 - }); - - _assertBucket({ - index: _i9_81, - lpBalance: 7_020.307252493359351053 * 1e18, - collateral: 0, - deposit: bucket2Deposit, + deposit: 2_112.736560735960384000 * 1e18, exchangeRate: 1.006368280367980193 * 1e18 }); // LP forfeited when forgive bad debt should be reflected in BucketBankruptcy event vm.expectEmit(true, true, false, true); emit BucketBankruptcy(_i9_91, 2_099.367201799558744044 * 1e18); - vm.expectEmit(true, true, false, true); - emit BucketBankruptcy(_i9_81, 7_020.307252493359351053 * 1e18); _settle({ from: _lender, borrower: _borrower2, @@ -1065,25 +1028,6 @@ contract ERC20PoolLiquidationsSettleTest is ERC20HelperContract { exchangeRate: 1 * 1e18 }); - skip(1 hours); - // bucket is insolvent, balances are resetted - _assertBucketAssets({ - index: _i9_81, - lpBalance: 0, // bucket is bankrupt - collateral: 0, - deposit: 0, - exchangeRate: 1 * 1e18 - }); - - // add to bankrupt bucket with deposit to enable tearDown - _addLiquidity({ - from: _lender1, - amount: 1 * 1e18, - index: _i9_81, - lpAward: 1 * 1e18, - newLup: 9.721295865031779605 * 1e18 - }); - } } diff --git a/tests/forge/unit/ERC20Pool/ERC20PoolReserveAuction.t.sol b/tests/forge/unit/ERC20Pool/ERC20PoolReserveAuction.t.sol index bd4e9e6e6..c7d28bf7e 100644 --- a/tests/forge/unit/ERC20Pool/ERC20PoolReserveAuction.t.sol +++ b/tests/forge/unit/ERC20Pool/ERC20PoolReserveAuction.t.sol @@ -178,34 +178,52 @@ contract ERC20PoolReserveAuctionNoFundsTest is ERC20HelperContract { changePrank(_actor2); pool.updateInterest(); - pool.take(_actor3, 506252187686489913395361995, _actor2, new bytes(0)); + vm.expectRevert(IPoolErrors.NoReserves.selector); + pool.kickReserveAuction(); + // pool balance remains the same assertEq(_quote.balanceOf(address(pool)), 1017); - vm.warp(block.timestamp + 86400); - changePrank(_actor3); pool.updateInterest(); // not enough balance to start new auction vm.expectRevert(IPoolErrors.NoReserves.selector); pool.kickReserveAuction(); + + // repay debt to have enough balance to kick new reserves auction + ERC20Pool(address(_pool)).repayDebt(_actor3, type(uint256).max, 0, _actor3, MAX_FENWICK_INDEX); + + uint256 initialPoolBalance = 103934; + uint256 initialAvailableAmount = 102917; + + assertEq(_quote.balanceOf(address(pool)), initialPoolBalance); + assertEq(_availableQuoteToken(), initialAvailableAmount); - // add tokens to have enough balance to kick new reserves auction - pool.addQuoteToken(100, 2572, block.timestamp + 1); pool.kickReserveAuction(); - // pool balance diminished by reward given to reserves kicker - assertEq(_quote.balanceOf(address(pool)), 1116); - assertEq(_availableQuoteToken(), 0); + + uint256 kickerReward = 12; + uint256 claimableTokens = 1229; + + ( , , uint256 claimable, , ) = _poolUtils.poolReservesInfo(address(_pool)); + assertEq(claimable, claimableTokens); + + // pool balance diminished by reward given to reserves kicker (12) + assertEq(_quote.balanceOf(address(pool)), initialPoolBalance - kickerReward); + // available quote token (available to remove / draw debt from) diminished by kicker reward + claimable tokens + assertEq(_availableQuoteToken(), initialAvailableAmount - (kickerReward + claimableTokens)); + skip(24 hours); // mint and approve ajna tokens for taker deal(address(_ajna), _actor3, 1e45); ERC20(address(_ajna)).approve(address(_pool), type(uint256).max); - pool.takeReserves(787); + pool.takeReserves(claimableTokens); - assertEq(_quote.balanceOf(address(pool)), 1017); - assertEq(_availableQuoteToken(), 0); + // quote token balance diminished by quote token taken from reserve auction + assertEq(_quote.balanceOf(address(pool)), initialPoolBalance - kickerReward - claimableTokens); + // available quote token (available to remove / draw debt from) is not modified + assertEq(_availableQuoteToken(), initialAvailableAmount - (kickerReward + claimableTokens)); } } diff --git a/tests/forge/unit/ERC721Pool/ERC721PoolLiquidationsSettleAuction.t.sol b/tests/forge/unit/ERC721Pool/ERC721PoolLiquidationsSettleAuction.t.sol index f262f1a17..599e903c4 100644 --- a/tests/forge/unit/ERC721Pool/ERC721PoolLiquidationsSettleAuction.t.sol +++ b/tests/forge/unit/ERC721Pool/ERC721PoolLiquidationsSettleAuction.t.sol @@ -112,10 +112,38 @@ contract ERC721PoolLiquidationsSettleAuctionTest is ERC721HelperContract { } function testSettlePartialDebtSubsetPool() external tearDown { + _assertBucket({ + index: 2500, + lpBalance: 8_000 * 1e18, + collateral: 0, + deposit: 13_734.486139425783008000 * 1e18, + exchangeRate: 1.716810767428222876 * 1e18 + }); + // the 2 token ids are owned by borrower before settle assertEq(ERC721Pool(address(_pool)).borrowerTokenIds(_borrower, 0), 1); assertEq(ERC721Pool(address(_pool)).borrowerTokenIds(_borrower, 1), 3); + _addLiquidityNoEventCheck({ + from: _lender, + amount: 5_000 * 1e18, + index: 2499 + }); + + // adding more liquidity to settle all auctions + _addLiquidityNoEventCheck({ + from: _lender, + amount: 20_000 * 1e18, + index: 2500 + }); + + // lender adds liquidity in min bucket to and merge / remove the other NFTs + _addLiquidityNoEventCheck({ + from: _lender, + amount: 100 * 1e18, + index: MAX_FENWICK_INDEX + }); + // skip to make loans clearable skip(80 hours); @@ -124,13 +152,7 @@ contract ERC721PoolLiquidationsSettleAuctionTest is ERC721HelperContract { borrowerDebt: 10_195.576288428866513838 * 1e18, borrowerCollateral: 2 * 1e18, borrowert0Np: 2_627.524038461538462750 * 1e18, - borrowerCollateralization: 0.750385377189985519 * 1e18 - }); - - _addLiquidityNoEventCheck({ - from: _lender, - amount: 5_000 * 1e18, - index: 2499 + borrowerCollateralization: 0.757907990596315111 * 1e18 }); // first settle call settles partial borrower debt @@ -138,24 +160,24 @@ contract ERC721PoolLiquidationsSettleAuctionTest is ERC721HelperContract { from: _lender, borrower: _borrower, maxDepth: 1, - settledDebt: 2_485.081590832967738263 * 1e18 + settledDebt: 2_485.576684127234225966 * 1e18 }); // collateral in bucket used to settle auction increased with the amount used to settle debt _assertBucket({ index: 2499, lpBalance: 5_000 * 1e18, - collateral: 1.287673250019003865 * 1e18, + collateral: 1.287929788232333535 * 1e18, deposit: 0, - exchangeRate: 1.000000000000000001 * 1e18 + exchangeRate: 1.000199226172731231 * 1e18 }); // partial borrower debt is settled, borrower collateral decreased with the amount used to settle debt _assertBorrower({ borrower: _borrower, - borrowerDebt: 5_195.576288428866513840 * 1e18, - borrowerCollateral: 0.712326749980996135 * 1e18, + borrowerDebt: 5_194.580157565210365777 * 1e18, + borrowerCollateral: 0.712070211767666465 * 1e18, borrowert0Np: 2_627.524038461538462750 * 1e18, - borrowerCollateralization: 0.527081453163032823 * 1e18 + borrowerCollateralization: 0.529627631336027971 * 1e18 }); _assertCollateralInvariants(); @@ -173,18 +195,11 @@ contract ERC721PoolLiquidationsSettleAuctionTest is ERC721HelperContract { assertEq(_collateral.ownerOf(53), address(_pool)); assertEq(_collateral.ownerOf(73), address(_pool)); - // adding more liquidity to settle all auctions - _addLiquidityNoEventCheck({ - from: _lender, - amount: 20_000 * 1e18, - index: 2000 - }); - _settle({ from: _lender, borrower: _borrower, maxDepth: 1, - settledDebt: 2_582.286197628570725612 * 1e18 + settledDebt: 2_258.399659496838436164 * 1e18 }); // no token id left in borrower token ids array @@ -195,18 +210,33 @@ contract ERC721PoolLiquidationsSettleAuctionTest is ERC721HelperContract { assertEq(ERC721Pool(address(_pool)).bucketTokenIds(1), 1); _assertBucket({ - index: 2000, - lpBalance: 20_000 * 1e18, - collateral: 0.111071996695171075 * 1e18, - deposit: 14_804.423711571133486162 * 1e18, - exchangeRate: 1.000000000000000002 * 1e18 + index: 2500, + lpBalance: 19_649.507551702938715450 * 1e18, + collateral: 0.712070211767666465 * 1e18, + deposit: 30_990.013747352002213011 * 1e18, + exchangeRate: 1.717152801066721367 * 1e18 + }); + _assertBorrower({ + borrower: _borrower, + borrowerDebt: 650.665648223383086331 * 1e18, + borrowerCollateral: 0, + borrowert0Np: 2_627.524038461538462750 * 1e18, + borrowerCollateralization: 0 + }); + + _settle({ + from: _lender, + borrower: _borrower, + maxDepth: 5, + settledDebt: 323.391444837465801745 * 1e18 }); + _assertBorrower({ borrower: _borrower, borrowerDebt: 0, borrowerCollateral: 0, borrowert0Np: 2_627.524038461538462750 * 1e18, - borrowerCollateralization: 1 * 1e18 + borrowerCollateralization: 1.0 * 1e18 }); _assertCollateralInvariants(); @@ -219,60 +249,53 @@ contract ERC721PoolLiquidationsSettleAuctionTest is ERC721HelperContract { }); _assertBucket({ - index: 2000, - lpBalance: 20_000 * 1e18, - collateral: 0.329304013831793309 * 1e18, - deposit: 4_596.260291921984421072 * 1e18, - exchangeRate: 1.000000000000000003 * 1e18 + index: 2500, + lpBalance: 19_649.507551702938715450 * 1e18, + collateral: 3.354170784195916811 * 1e18, + deposit: 20_131.184679479470055267 * 1e18, + exchangeRate: 1.684039215572972305 * 1e18 }); _assertBucket({ index: 2499, lpBalance: 5_000 * 1e18, - collateral: 1.287673250019003865 * 1e18, + collateral: 1.287929788232333535 * 1e18, deposit: 0, - exchangeRate: 1.000000000000000001 * 1e18 + exchangeRate: 1.000199226172731231 * 1e18 }); _assertBucket({ index: 7388, - lpBalance: 0.000000138075849129 * 1e18, - collateral: 1.383022736149202826 * 1e18, - deposit: 0, - exchangeRate: 1.000000000003575156 * 1e18 + lpBalance: 99.984931542573546395 * 1e18, + collateral: 0.357899427571749654 * 1e18, + deposit: 100.004851122084218862 * 1e18, + exchangeRate: 1.000199226172731231 * 1e18 }); - // borrower 2 can claim 2 NFT tokens (id 51 and 53) after settle + _assertBorrower({ borrower: _borrower2, borrowerDebt: 0, - borrowerCollateral: 2 * 1e18, + borrowerCollateral: 0, borrowert0Np: 1_769.243311298076895206 * 1e18, borrowerCollateralization: 1 * 1e18 }); _assertCollateralInvariants(); - assertEq(ERC721Pool(address(_pool)).borrowerTokenIds(_borrower2, 0), 51); - assertEq(ERC721Pool(address(_pool)).borrowerTokenIds(_borrower2, 1), 53); - // tokens used to settle auction are moved to pool claimable array assertEq(ERC721Pool(address(_pool)).bucketTokenIds(0), 3); assertEq(ERC721Pool(address(_pool)).bucketTokenIds(1), 1); assertEq(ERC721Pool(address(_pool)).bucketTokenIds(2), 73); + assertEq(ERC721Pool(address(_pool)).bucketTokenIds(3), 53); + assertEq(ERC721Pool(address(_pool)).bucketTokenIds(4), 51); // lender can claim 1 NFTs from bucket 2499 changePrank(_lender); _pool.removeCollateral(1, 2499); - // lender adds liquidity in min bucket and merge / removes the other NFTs - _addLiquidityNoEventCheck({ - from: _lender, - amount: 100 * 1e18, - index: MAX_FENWICK_INDEX - }); - uint256[] memory removalIndexes = new uint256[](3); - removalIndexes[0] = 2000; - removalIndexes[1] = 2499; + removalIndexes[0] = 2499; + removalIndexes[1] = 2500; removalIndexes[2] = MAX_FENWICK_INDEX; + _mergeOrRemoveCollateral({ from: _lender, toIndex: MAX_FENWICK_INDEX, @@ -283,34 +306,18 @@ contract ERC721PoolLiquidationsSettleAuctionTest is ERC721HelperContract { }); // the 3 NFTs claimed from pool are owned by lender - assertEq(_collateral.ownerOf(1), _lender); - assertEq(_collateral.ownerOf(3), _lender); - assertEq(_collateral.ownerOf(73), _lender); - assertEq(_collateral.ownerOf(51), address(_pool)); - assertEq(_collateral.ownerOf(53), address(_pool)); - - // borrower 2 can pull 2 NFTs from pool - _repayDebtNoLupCheck({ - from: _borrower2, - borrower: _borrower2, - amountToRepay: 0, - amountRepaid: 0, - collateralToPull: 2 - }); - - assertEq(_collateral.ownerOf(1), _lender); - assertEq(_collateral.ownerOf(3), _lender); + assertEq(_collateral.ownerOf(1), address(_pool)); + assertEq(_collateral.ownerOf(3), address(_pool)); assertEq(_collateral.ownerOf(73), _lender); - // the NFTs pulled from pool are owned by borrower - assertEq(_collateral.ownerOf(51), _borrower2); - assertEq(_collateral.ownerOf(53), _borrower2); + assertEq(_collateral.ownerOf(51), _lender); + assertEq(_collateral.ownerOf(53), _lender); _assertBucket({ - index: 2000, - lpBalance: 4_596.260291921984408543 * 1e18, - collateral: 0, - deposit: 4_596.260291921984421072 * 1e18, - exchangeRate: 1.000000000000000003 * 1e18 + index: 2500, + lpBalance: 15_721.542280862700379804 * 1e18, + collateral: 1.642100572428250346 * 1e18, + deposit: 20_131.184679479470055267 * 1e18, + exchangeRate: 1.684039215572972305 * 1e18 }); _assertBucket({ index: 2499, @@ -321,10 +328,10 @@ contract ERC721PoolLiquidationsSettleAuctionTest is ERC721HelperContract { }); _assertBucket({ index: MAX_FENWICK_INDEX, - lpBalance: 99.999999999642484483 * 1e18, - collateral: 0, - deposit: 100 * 1e18, - exchangeRate: 1.000000000003575156 * 1e18 + lpBalance: 99.984931542573546395 * 1e18, + collateral: 0.357899427571749654 * 1e18, + deposit: 100.004851122084218862 * 1e18, + exchangeRate: 1.000199226172731231 * 1e18 }); _assertBorrower({ borrower: _borrower, @@ -337,10 +344,23 @@ contract ERC721PoolLiquidationsSettleAuctionTest is ERC721HelperContract { borrower: _borrower2, borrowerDebt: 0, borrowerCollateral: 0, - borrowert0Np: 0, + borrowert0Np: 1_769.243311298076895206 * 1e18, borrowerCollateralization: 1 * 1e18 }); + uint256[] memory removalIndexes2 = new uint256[](2); + removalIndexes2[0] = 2500; + removalIndexes2[1] = MAX_FENWICK_INDEX; + + _mergeOrRemoveCollateral({ + from: _lender, + toIndex: MAX_FENWICK_INDEX, + noOfNFTsToRemove: 2, + collateralMerged: 2 * 1e18, + removeCollateralAtIndex: removalIndexes2, + toIndexLps: 0 + }); + _assertCollateralInvariants(); }