Skip to content

Commit

Permalink
Sherlock-110: revert add settleable (#909)
Browse files Browse the repository at this point in the history
* Revert addQuoteToken when there is a settleable auction, plus some work on the baselines

* Fixed testSettlePartialDebtSubsetPool, some questions

* Fix tests: testSettleZeroExchangeRateResidualBankruptcy, testReserveAuctionNoFunds

* Update docs, remove comment

* Update test to show bucket exchange rate before settles

---------

Co-authored-by: mwc <matt@ajna.finance>
Co-authored-by: grandizzy <grandizzy.the.egg@gmail.com>
  • Loading branch information
3 people committed Jun 28, 2023
1 parent 61470bf commit 5ba6d87
Show file tree
Hide file tree
Showing 6 changed files with 172 additions and 193 deletions.
2 changes: 2 additions & 0 deletions docs/Functions.md
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
3 changes: 3 additions & 0 deletions src/base/Pool.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
20 changes: 6 additions & 14 deletions tests/forge/unit/ERC20Pool/ERC20PoolLiquidationsLenderKick.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand All @@ -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({
Expand All @@ -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
});
Expand Down
116 changes: 30 additions & 86 deletions tests/forge/unit/ERC20Pool/ERC20PoolLiquidationsSettle.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -716,32 +733,13 @@ 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,
lpBalance: 99.367201799558744044 * 1e18,
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({
Expand All @@ -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
Expand Down Expand Up @@ -889,7 +887,7 @@ contract ERC20PoolLiquidationsSettleTest is ERC20HelperContract {
});
}

function testSettleZeroExchangeRateResidualBankruptcy() external {
function testSettleZeroExchangeRateResidualBankruptcy() external tearDown {
// Borrower2 borrows
_borrow({
from: _borrower2,
Expand Down Expand Up @@ -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,
Expand All @@ -979,50 +986,16 @@ 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,
lpBalance: 99.367201799558744044 * 1e18,
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
Expand All @@ -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,
Expand All @@ -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
});

}
}

Expand Down
40 changes: 29 additions & 11 deletions tests/forge/unit/ERC20Pool/ERC20PoolReserveAuction.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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));
}

}
Loading

0 comments on commit 5ba6d87

Please sign in to comment.