From 14f003cc0888e32e3e6f47e27c7cb678456fcc1c Mon Sep 17 00:00:00 2001 From: grandizzy <38490174+grandizzy@users.noreply.github.com> Date: Tue, 23 Aug 2022 21:41:55 +0300 Subject: [PATCH] Slither findings (#206) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Function ScaledPool._threshold_price(uint256,uint256,uint256) (src/base/ScaledPool.sol#388-391) is not in mixedCase Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#conformance-to-solidity-naming-conventions * Fix slither reentrancy warnings Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#reentrancy-vulnerabilities-3 * Change rate calculation, perform multiply before divide: ScaledPool._pendingInterestFactor(uint256) (src/base/ScaledPool.sol#386-389) performs a multiplication on the result of a division: -rate = (interestRate / SECONDS_PER_YEAR) * elapsed_ (src/base/ScaledPool.sol#387) Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#divide-before-multiply Fix tests * Fix Variable ScaledPool.RATE_DECREASE_COEFFICIENT (src/base/ScaledPool.sol#29) is too similar to ScaledPool.RATE_INCREASE_COEFFICIENT (src/base/ScaledPool.sol#28) Variable IERC20Pool.borrowerInfo(address).pendingDebt_ (src/erc20/interfaces/IERC20Pool.sol#189) is too similar to ERC20Pool.borrowerInfo(address).pending_debt (src/erc20/ERC20Pool.sol#286) Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#variable-names-are-too-similar IERC20Pool.borrowers(address).inflatorSnapshot (src/erc20/interfaces/IERC20Pool.sol#73) shadows: - IScaledPool.inflatorSnapshot() (src/base/interfaces/IScaledPool.sol#94) (function) Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#local-variable-shadowing * Remove slither timestamp detector as we're not using it for randomness but storing and using for calculating interest rates See https://github.com/crytic/slither/wiki/Detector-Documentation#block-timestamp * Fix Reentrancy in ERC20PoolFactory.deployPool(address,address,uint256) (src/erc20/ERC20PoolFactory.sol#25-36): External calls: - pool.initialize(interestRate_) (src/erc20/ERC20PoolFactory.sol#31) State variables written after the call(s): - deployedPools[ERC20_NON_SUBSET_HASH][collateral_][quote_] = pool_ (src/erc20/ERC20PoolFactory.sol#34) Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#reentrancy-vulnerabilities-1 FenwickTree Fix FenwickTree._findSum(uint256).ss (src/base/FenwickTree.sol#135) is a local variable never initialized FenwickTree._add(uint256,uint256).ii (src/base/FenwickTree.sol#65) is a local variable never initialized FenwickTree._prefixSum(uint256).ii (src/base/FenwickTree.sol#108) is a local variable never initialized FenwickTree._mult(uint256,uint256).sum (src/base/FenwickTree.sol#38) is a local variable never initialized FenwickTree._remove(uint256,uint256).ii (src/base/FenwickTree.sol#87) is a local variable never initialized Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#uninitialized-local-variables FenwickTree improvements - minimize calculations of indexes, LSB before commit ╭──────────────────────────────────────────────────────────┬─────────────────┬───────┬────────┬────────┬─────────╮ │ src/_test/FenwickTree.t.sol:FenwickTreeInstance contract ┆ ┆ ┆ ┆ ┆ │ ╞══════════════════════════════════════════════════════════╪═════════════════╪═══════╪════════╪════════╪═════════╡ │ Deployment Cost ┆ Deployment Size ┆ ┆ ┆ ┆ │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ 516149 ┆ 2610 ┆ ┆ ┆ ┆ │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ Function Name ┆ min ┆ avg ┆ median ┆ max ┆ # calls │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ add ┆ 13051 ┆ 97343 ┆ 37808 ┆ 300708 ┆ 4 │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ findSum ┆ 14183 ┆ 21813 ┆ 14714 ┆ 68961 ┆ 8 │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ mult ┆ 58722 ┆ 69520 ┆ 69520 ┆ 80318 ┆ 2 │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ prefixSum ┆ 1592 ┆ 11216 ┆ 11934 ┆ 15783 ┆ 8 │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ remove ┆ 13018 ┆ 13018 ┆ 13018 ┆ 13018 ┆ 1 │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ treeSum ┆ 341 ┆ 341 ┆ 341 ┆ 341 ┆ 1 │ ╰──────────────────────────────────────────────────────────┴─────────────────┴───────┴────────┴────────┴─────────╯ after commit ╭──────────────────────────────────────────────────────────┬─────────────────┬───────┬────────┬────────┬─────────╮ │ src/_test/FenwickTree.t.sol:FenwickTreeInstance contract ┆ ┆ ┆ ┆ ┆ │ ╞══════════════════════════════════════════════════════════╪═════════════════╪═══════╪════════╪════════╪═════════╡ │ Deployment Cost ┆ Deployment Size ┆ ┆ ┆ ┆ │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ 576206 ┆ 2910 ┆ ┆ ┆ ┆ │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ Function Name ┆ min ┆ avg ┆ median ┆ max ┆ # calls │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ add ┆ 12579 ┆ 96771 ┆ 37202 ┆ 300102 ┆ 4 │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ findSum ┆ 13434 ┆ 18162 ┆ 13965 ┆ 68212 ┆ 16 │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ mult ┆ 56841 ┆ 67577 ┆ 67577 ┆ 78314 ┆ 2 │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ prefixSum ┆ 1427 ┆ 10366 ┆ 10997 ┆ 14730 ┆ 8 │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ remove ┆ 12613 ┆ 12613 ┆ 12613 ┆ 12613 ┆ 1 │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ │ treeSum ┆ 341 ┆ 341 ┆ 341 ┆ 341 ┆ 2 │ ╰──────────────────────────────────────────────────────────┴─────────────────┴───────┴────────┴────────┴─────────╯ * Fix Variable ScaledPool._poolInitializations (src/base/ScaledPool.sol#69) is not in mixedCase Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#conformance-to-solidity-naming-conventions * Disable inline as we're not using strict equality to determine if an account has enough Ether or tokens FenwickTree._mult(uint256,uint256) (src/base/FenwickTree.sol#33-61) uses a dangerous strict equality: - (lsbJ < _lsb(i_)) || (i_ == 0 && j <= SIZE) (src/base/FenwickTree.sol#53) Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#dangerous-strict-equalities --- slither.config.json | 2 +- .../ERC20Pool/ERC20ScaledPoolBorrow.t.sol | 66 +++++++++--------- .../ERC20Pool/ERC20ScaledPoolCollateral.t.sol | 26 +++---- .../ERC20ScaledPoolInterestRate.t.sol | 8 +-- .../ERC20Pool/ERC20ScaledPoolPrecision.t.sol | 32 ++++----- .../ERC20ScaledPoolPurchaseQuote.t.sol | 37 +++++----- .../ERC20Pool/ERC20ScaledPoolQuoteToken.t.sol | 18 ++--- src/_test/FenwickTree.t.sol | 17 +++++ src/base/FenwickTree.sol | 67 +++++++++++-------- src/base/ScaledPool.sol | 18 ++--- src/erc20/ERC20Pool.sol | 28 ++++---- src/erc20/ERC20PoolFactory.sol | 4 +- src/erc20/interfaces/IERC20Pool.sol | 10 +-- src/erc721/ERC721Pool.sol | 4 +- 14 files changed, 183 insertions(+), 154 deletions(-) diff --git a/slither.config.json b/slither.config.json index fa31b6b..7d2ae14 100644 --- a/slither.config.json +++ b/slither.config.json @@ -8,5 +8,5 @@ "@prb-math=lib/prb-math/", "@base64-sol=lib/base64/" ], - "detectors_to_exclude": "solc-version" + "detectors_to_exclude": "solc-version,timestamp" } \ No newline at end of file diff --git a/src/_test/ERC20Pool/ERC20ScaledPoolBorrow.t.sol b/src/_test/ERC20Pool/ERC20ScaledPoolBorrow.t.sol index ceeaf1f..f9478cd 100644 --- a/src/_test/ERC20Pool/ERC20ScaledPoolBorrow.t.sol +++ b/src/_test/ERC20Pool/ERC20ScaledPoolBorrow.t.sol @@ -89,9 +89,9 @@ contract ERC20ScaledBorrowTest is DSTestPlus { // get a 21_000 DAI loan vm.expectEmit(true, true, false, true); - emit Transfer(address(_pool), address(_borrower), 21_000 * 1e18); - vm.expectEmit(true, true, false, true); emit Borrow(address(_borrower), 2_981.007422784467321543 * 1e18, 21_000 * 1e18); + vm.expectEmit(true, true, false, true); + emit Transfer(address(_pool), address(_borrower), 21_000 * 1e18); _pool.borrow(21_000 * 1e18, 3000, address(0), address(0)); assertEq(_pool.htp(), 210.201923076923077020 * 1e18); @@ -134,9 +134,9 @@ contract ERC20ScaledBorrowTest is DSTestPlus { // borrow 19_000 DAI vm.expectEmit(true, true, false, true); - emit Transfer(address(_pool), address(_borrower), 19_000 * 1e18); - vm.expectEmit(true, true, false, true); emit Borrow(address(_borrower), 2_966.176540084047110076 * 1e18, 19_000 * 1e18); + vm.expectEmit(true, true, false, true); + emit Transfer(address(_pool), address(_borrower), 19_000 * 1e18); _pool.borrow(19_000 * 1e18, 3500, address(0), address(0)); assertEq(_pool.htp(), 400.384615384615384800 * 1e18); @@ -153,9 +153,9 @@ contract ERC20ScaledBorrowTest is DSTestPlus { // repay partial vm.expectEmit(true, true, false, true); - emit Transfer(address(_borrower), address(_pool), 10_000 * 1e18); - vm.expectEmit(true, true, false, true); emit Repay(address(_borrower), 2_966.176540084047110076 * 1e18, 10_000 * 1e18); + vm.expectEmit(true, true, false, true); + emit Transfer(address(_borrower), address(_pool), 10_000 * 1e18); _pool.repay(10_000 * 1e18, address(0), address(0)); assertEq(_pool.htp(), 300.384615384615384800 * 1e18); @@ -172,9 +172,9 @@ contract ERC20ScaledBorrowTest is DSTestPlus { // repay entire loan deal(address(_quote), _borrower, _quote.balanceOf(_borrower) + 40 * 1e18); vm.expectEmit(true, true, false, true); - emit Transfer(address(_borrower), address(_pool), 30_038.461538461538480000 * 1e18); - vm.expectEmit(true, true, false, true); emit Repay(address(_borrower), BucketMath.MAX_PRICE, 30_038.461538461538480000 * 1e18); + vm.expectEmit(true, true, false, true); + emit Transfer(address(_borrower), address(_pool), 30_038.461538461538480000 * 1e18); _pool.repay(30_040 * 1e18, address(0), address(0)); assertEq(_pool.htp(), 0); @@ -213,53 +213,53 @@ contract ERC20ScaledBorrowTest is DSTestPlus { assertEq(_pool.borrowerDebt(), 21_020.192307692307702000 * 1e18); (uint256 debt, uint256 pendingDebt, uint256 col, uint256 inflator) = _pool.borrowerInfo(address(_borrower)); assertEq(debt, 21_020.192307692307702000 * 1e18); - assertEq(pendingDebt, 21_051.890446233188505554 * 1e18); + assertEq(pendingDebt, 21_051.890446235135648008 * 1e18); assertEq(col, 50 * 1e18); assertEq(inflator, 1 * 1e18); skip(864000); _pool.pledgeCollateral(10 * 1e18, address(0), address(0)); - assertEq(_pool.borrowerDebt(), 21_083.636385097313216749 * 1e18); + assertEq(_pool.borrowerDebt(), 21_083.636385101213387311 * 1e18); (debt, pendingDebt, col, inflator) = _pool.borrowerInfo(address(_borrower)); - assertEq(debt, 21_083.636385097313216749 * 1e18); - assertEq(pendingDebt, 21_083.636385097313216749 * 1e18); + assertEq(debt, 21_083.636385101213387311 * 1e18); + assertEq(pendingDebt, 21_083.636385101213387311 * 1e18); assertEq(col, 60 * 1e18); - assertEq(inflator, 1.003018244385032969 * 1e18); + assertEq(inflator, 1.003018244385218513 * 1e18); skip(864000); _pool.pullCollateral(10 * 1e18, address(0), address(0)); - assertEq(_pool.borrowerDebt(), 21_118.612213256345042351 * 1e18); + assertEq(_pool.borrowerDebt(), 21_118.612213260575675180 * 1e18); (debt, pendingDebt, col, inflator) = _pool.borrowerInfo(address(_borrower)); - assertEq(debt, 21_118.612213256345042351 * 1e18); - assertEq(pendingDebt, 21_118.612213256345042351 * 1e18); + assertEq(debt, 21_118.612213260575675180 * 1e18); + assertEq(pendingDebt, 21_118.612213260575675180 * 1e18); assertEq(col, 50 * 1e18); - assertEq(inflator, 1.004682160092703849 * 1e18); + assertEq(inflator, 1.004682160092905114 * 1e18); skip(864000); _pool.borrow(0, 3000, address(0), address(0)); - assertEq(_pool.borrowerDebt(), 21_157.152642997118010824 * 1e18); + assertEq(_pool.borrowerDebt(), 21_157.152643010853298669 * 1e18); (debt, pendingDebt, col, inflator) = _pool.borrowerInfo(address(_borrower)); - assertEq(debt, 21_157.152642997118010824 * 1e18); - assertEq(pendingDebt, 21_157.152642997118010824 * 1e18); + assertEq(debt, 21_157.152643010853298669 * 1e18); + assertEq(pendingDebt, 21_157.152643010853298669 * 1e18); assertEq(col, 50 * 1e18); - assertEq(inflator, 1.006515655675266581 * 1e18); + assertEq(inflator, 1.006515655675920014 * 1e18); skip(864000); _pool.repay(0, address(0), address(0)); - assertEq(_pool.borrowerDebt(), 21_199.628356880380570924 * 1e18); + assertEq(_pool.borrowerDebt(), 21_199.628356897284446170 * 1e18); (debt, pendingDebt, col, inflator) = _pool.borrowerInfo(address(_borrower)); - assertEq(debt, 21_199.628356880380570924 * 1e18); - assertEq(pendingDebt, 21_199.628356880380570924 * 1e18); + assertEq(debt, 21_199.628356897284446170 * 1e18); + assertEq(pendingDebt, 21_199.628356897284446170 * 1e18); assertEq(col, 50 * 1e18); - assertEq(inflator, 1.008536365726892447 * 1e18); + assertEq(inflator, 1.008536365727696620 * 1e18); skip(864000); - assertEq(_pool.borrowerDebt(), 21_199.628356880380570924 * 1e18); + assertEq(_pool.borrowerDebt(), 21_199.628356897284446170 * 1e18); (debt, pendingDebt, col, inflator) = _pool.borrowerInfo(address(_borrower)); - assertEq(debt, 21_199.628356880380570924 * 1e18); - assertEq(pendingDebt, 21_246.450141911768550258 * 1e18); + assertEq(debt, 21_199.628356897284446170 * 1e18); + assertEq(pendingDebt, 21_246.450141935843879765 * 1e18); assertEq(col, 50 * 1e18); - assertEq(inflator, 1.008536365726892447 * 1e18); + assertEq(inflator, 1.008536365727696620 * 1e18); } /** @@ -308,9 +308,9 @@ contract ERC20ScaledBorrowTest is DSTestPlus { // should be able to borrow if properly specified vm.expectEmit(true, true, false, true); - emit Transfer(address(_pool), address(_borrower2), 10 * 1e18); - vm.expectEmit(true, true, false, true); emit Borrow(address(_borrower2), 2_995.912459898389633881 * 1e18, 10 * 1e18); + vm.expectEmit(true, true, false, true); + emit Transfer(address(_pool), address(_borrower2), 10 * 1e18); _pool.borrow(10 * 1e18, 3000, address(0), address(0)); } @@ -357,9 +357,9 @@ contract ERC20ScaledBorrowTest is DSTestPlus { // should be able to repay loan if properly specified vm.expectEmit(true, true, false, true); - emit Transfer(address(_borrower), address(_pool), 0.0001 * 1e18); - vm.expectEmit(true, true, false, true); emit Repay(address(_borrower), _pool.lup(), 0.0001 * 1e18); + vm.expectEmit(true, true, false, true); + emit Transfer(address(_borrower), address(_pool), 0.0001 * 1e18); _pool.repay(0.0001 * 1e18, address(_borrower2), address(_borrower2)); } diff --git a/src/_test/ERC20Pool/ERC20ScaledPoolCollateral.t.sol b/src/_test/ERC20Pool/ERC20ScaledPoolCollateral.t.sol index afd99f0..22c9cc7 100644 --- a/src/_test/ERC20Pool/ERC20ScaledPoolCollateral.t.sol +++ b/src/_test/ERC20Pool/ERC20ScaledPoolCollateral.t.sol @@ -91,9 +91,9 @@ contract ERC20ScaledCollateralTest is DSTestPlus { // get a 21_000 Quote loan vm.expectEmit(true, true, false, true); - emit Transfer(address(_pool), address(_borrower), 21_000 * 1e18); - vm.expectEmit(true, true, false, true); emit Borrow(address(_borrower), 2_981.007422784467321543 * 1e18, 21_000 * 1e18); + vm.expectEmit(true, true, false, true); + emit Transfer(address(_pool), address(_borrower), 21_000 * 1e18); _pool.borrow(21_000 * 1e18, 3000, address(0), address(0)); // check pool state @@ -147,15 +147,15 @@ contract ERC20ScaledCollateralTest is DSTestPlus { _pool.pullCollateral(unencumberedCollateral, address(0), address(0)); // check pool state - assertEq(_pool.htp(), 2_989.185764499773229142 * 1e18); + assertEq(_pool.htp(), 2_989.185764500745498129 * 1e18); assertEq(_pool.lup(), 2_981.007422784467321543 * 1e18); - assertEq(_pool.poolSize(), 30_025.933063898944800000 * 1e18); - assertEq(_pool.borrowerDebt(), 21_049.006823135579696033 * 1e18); + assertEq(_pool.poolSize(), 30_025.933063902025680000 * 1e18); + assertEq(_pool.borrowerDebt(), 21_049.006823139002918431 * 1e18); assertEq(_pool.lenderDebt(), 21_000 * 1e18); assertEq(_pool.pledgedCollateral(), _pool.encumberedCollateral(_pool.borrowerDebt(), _pool.lup())); - assertEq(_pool.encumberedCollateral(_pool.borrowerDebt(), _pool.lup()), 7.061038044472344858 * 1e18); + assertEq(_pool.encumberedCollateral(_pool.borrowerDebt(), _pool.lup()), 7.061038044473493202 * 1e18); assertEq(_pool.encumberedCollateral(_pool.lenderDebt(), _pool.lup()), 7.044598359431304627 * 1e18); // check borrower state @@ -166,7 +166,7 @@ contract ERC20ScaledCollateralTest is DSTestPlus { _pool.encumberedCollateral(_pool.borrowerDebt(), _pool.lup()), _pool.encumberedCollateral(borrowerDebt, _pool.lup()) ); - assertEq(_collateral.balanceOf(address(_borrower)), 142.938961955527655142 * 1e18); + assertEq(_collateral.balanceOf(address(_borrower)), 142.938961955526506798 * 1e18); assertEq(_pool.borrowerCollateralization(borrowerDebt, borrowerCollateral, _pool.lup()), _pool.poolCollateralization()); } @@ -210,9 +210,9 @@ contract ERC20ScaledCollateralTest is DSTestPlus { // actor deposits collateral into a bucket uint256 collateralToDeposit = 4 * 1e18; vm.expectEmit(true, true, false, true); - emit Transfer(address(_bidder), address(_pool), collateralToDeposit); - vm.expectEmit(true, true, false, true); emit AddCollateral(address(_bidder), priceAtTestIndex, collateralToDeposit); + vm.expectEmit(true, true, false, true); + emit Transfer(address(_bidder), address(_pool), collateralToDeposit); _pool.addCollateral(collateralToDeposit, testIndex); // check bucket state @@ -229,18 +229,18 @@ contract ERC20ScaledCollateralTest is DSTestPlus { // actor withdraws some of their collateral uint256 collateralToWithdraw = 1.53 * 1e18; vm.expectEmit(true, true, true, true); - emit Transfer(address(_pool), address(_bidder), collateralToWithdraw); - vm.expectEmit(true, true, true, true); emit RemoveCollateral(address(_bidder), priceAtTestIndex, collateralToWithdraw); + vm.expectEmit(true, true, true, true); + emit Transfer(address(_pool), address(_bidder), collateralToWithdraw); uint256 lpRedeemed = _pool.removeCollateral(collateralToWithdraw, testIndex); assertEq(lpRedeemed, 4_606.664793962758783502850000000 * 1e27); // actor withdraws remainder of their _collateral collateralToWithdraw = 2.47 * 1e18; vm.expectEmit(true, true, true, true); - emit Transfer(address(_pool), address(_bidder), collateralToWithdraw); - vm.expectEmit(true, true, true, true); emit RemoveCollateral(address(_bidder), priceAtTestIndex, collateralToWithdraw); + vm.expectEmit(true, true, true, true); + emit Transfer(address(_pool), address(_bidder), collateralToWithdraw); uint256 collateralRemoved; (collateralRemoved, lpRedeemed) = _pool.removeAllCollateral(testIndex); assertEq(collateralRemoved, collateralToWithdraw); diff --git a/src/_test/ERC20Pool/ERC20ScaledPoolInterestRate.t.sol b/src/_test/ERC20Pool/ERC20ScaledPoolInterestRate.t.sol index c04e373..de5094b 100644 --- a/src/_test/ERC20Pool/ERC20ScaledPoolInterestRate.t.sol +++ b/src/_test/ERC20Pool/ERC20ScaledPoolInterestRate.t.sol @@ -95,7 +95,7 @@ contract ERC20ScaledInterestRateTest is DSTestPlus { assertEq(_pool.htp(), 0); assertEq(_pool.lup(), BucketMath.MAX_PRICE); - assertEq(_pool.poolSize(), 110_162.490615980593600000 * 1e18); + assertEq(_pool.poolSize(), 110_162.490615984432250000 * 1e18); assertEq(_pool.borrowerDebt(), 0); assertEq(_pool.lenderDebt(), 0); @@ -103,7 +103,7 @@ contract ERC20ScaledInterestRateTest is DSTestPlus { assertEq(debt, 0); assertEq(pendingDebt, 0); assertEq(col, 100 * 1e18); - assertEq(inflator, 1.001507985182860621 * 1e18); + assertEq(inflator, 1.001507985182953253 * 1e18); assertEq(_pool.interestRate(), 0.055 * 1e18); // FIXME here it should decrease assertEq(_pool.interestRateUpdate(), 864000); @@ -122,12 +122,12 @@ contract ERC20ScaledInterestRateTest is DSTestPlus { _pool.pledgeCollateral(50 * 1e18, address(0), address(0)); _pool.borrow(15_000 * 1e18, 4300, address(0), address(0)); assertEq(_pool.inflatorSnapshot(), 1.0 * 1e18); - assertEq(_pool.pendingInflator(), 1.000005707778845707 * 1e18); + assertEq(_pool.pendingInflator(), 1.000005707778846384 * 1e18); vm.warp(block.timestamp+3600); // ensure pendingInflator increases as time passes assertEq(_pool.inflatorSnapshot(), 1.0 * 1e18); - assertEq(_pool.pendingInflator(), 1.000011415590270154 * 1e18); + assertEq(_pool.pendingInflator(), 1.000011415590271509 * 1e18); } // TODO: add test related to pool utilization changes diff --git a/src/_test/ERC20Pool/ERC20ScaledPoolPrecision.t.sol b/src/_test/ERC20Pool/ERC20ScaledPoolPrecision.t.sol index e20fc5a..24088ba 100644 --- a/src/_test/ERC20Pool/ERC20ScaledPoolPrecision.t.sol +++ b/src/_test/ERC20Pool/ERC20ScaledPoolPrecision.t.sol @@ -78,9 +78,9 @@ contract ERC20ScaledPoolPrecisionTest is DSTestPlus { _pool.addQuoteToken(50_000 * _quotePoolPrecision, 2549); _pool.addQuoteToken(50_000 * _quotePoolPrecision, 2550); vm.expectEmit(true, true, false, true); - emit Transfer(address(_lender), address(_pool), 50_000 * _quotePrecision); - vm.expectEmit(true, true, false, true); emit AddQuoteToken(address(_lender), _pool.indexToPrice(2551), 50_000 * _quotePoolPrecision, BucketMath.MAX_PRICE); + vm.expectEmit(true, true, false, true); + emit Transfer(address(_lender), address(_pool), 50_000 * _quotePrecision); _pool.addQuoteToken(50_000 * _quotePoolPrecision, 2551); // check balances @@ -102,9 +102,9 @@ contract ERC20ScaledPoolPrecisionTest is DSTestPlus { // lender removes some quote token from highest priced bucket vm.expectEmit(true, true, false, true); - emit Transfer(address(_pool), address(_lender), 25_000 * _quotePrecision); - vm.expectEmit(true, true, false, true); emit RemoveQuoteToken(address(_lender), _pool.indexToPrice(2549), 25_000 * _quotePoolPrecision, BucketMath.MAX_PRICE); + vm.expectEmit(true, true, false, true); + emit Transfer(address(_pool), address(_lender), 25_000 * _quotePrecision); _pool.removeQuoteToken(25_000 * _quotePoolPrecision, 2549); // check balances @@ -142,9 +142,9 @@ contract ERC20ScaledPoolPrecisionTest is DSTestPlus { // borrowers adds collateral changePrank(_borrower); vm.expectEmit(true, true, false, true); - emit Transfer(address(_borrower), address(_pool), 50 * _collateralPrecision); - vm.expectEmit(true, true, false, true); emit PledgeCollateral(address(_borrower), 50 * _collateralPoolPrecision); + vm.expectEmit(true, true, false, true); + emit Transfer(address(_borrower), address(_pool), 50 * _collateralPrecision); _pool.pledgeCollateral(50 * _collateralPoolPrecision, address(0), address(0)); // check balances @@ -164,9 +164,9 @@ contract ERC20ScaledPoolPrecisionTest is DSTestPlus { // borrower borrows vm.expectEmit(true, true, false, true); - emit Transfer(address(_pool), address(_borrower), 10_000 * _quotePrecision); - vm.expectEmit(true, true, false, true); emit Borrow(address(_borrower), _pool.indexToPrice(2549), 10_000 * _quotePoolPrecision); + vm.expectEmit(true, true, false, true); + emit Transfer(address(_pool), address(_borrower), 10_000 * _quotePrecision); _pool.borrow(10_000 * _quotePoolPrecision, 3000, address(0), address(0)); // check balances @@ -191,9 +191,9 @@ contract ERC20ScaledPoolPrecisionTest is DSTestPlus { // borrower repays half of loan vm.expectEmit(true, true, false, true); - emit Transfer(address(_borrower), address(_pool), 5_000 * _quotePrecision); - vm.expectEmit(true, true, false, true); emit Repay(address(_borrower), _pool.indexToPrice(2549), 5_000 * _quotePoolPrecision); + vm.expectEmit(true, true, false, true); + emit Transfer(address(_borrower), address(_pool), 5_000 * _quotePrecision); _pool.repay(5_000 * _quotePoolPrecision, address(0), address(0)); // check balances @@ -218,9 +218,9 @@ contract ERC20ScaledPoolPrecisionTest is DSTestPlus { // remove all of the remaining unencumbered collateral uint256 unencumberedCollateral = col - _pool.encumberedCollateral(debt, _pool.lup()); vm.expectEmit(true, true, false, true); - emit Transfer(address(_pool), address(_borrower), unencumberedCollateral / _pool.collateralScale()); - vm.expectEmit(true, true, false, true); emit PullCollateral(address(_borrower), unencumberedCollateral); + vm.expectEmit(true, true, false, true); + emit Transfer(address(_pool), address(_borrower), unencumberedCollateral / _pool.collateralScale()); _pool.pullCollateral(unencumberedCollateral, address(0), address(0)); // FIXME: check balances @@ -260,11 +260,11 @@ contract ERC20ScaledPoolPrecisionTest is DSTestPlus { uint256 collateralRequired = Maths.wdiv(quoteToPurchase, _pool.indexToPrice(2549)); uint256 adjustedCollateralReq = collateralRequired / _pool.collateralScale(); vm.expectEmit(true, true, false, true); + emit Purchase(address(_bidder), _pool.indexToPrice(2549), quoteToPurchase, collateralRequired); + vm.expectEmit(true, true, false, true); emit Transfer(address(_bidder), address(_pool), adjustedCollateralReq); vm.expectEmit(true, true, false, true); emit Transfer(address(_pool), address(_bidder), 500 * _quotePrecision); - vm.expectEmit(true, true, false, true); - emit Purchase(address(_bidder), _pool.indexToPrice(2549), quoteToPurchase, collateralRequired); // _bidder.purchaseQuote(_pool, quoteToPurchase, 2549); // check bucket state @@ -291,9 +291,9 @@ contract ERC20ScaledPoolPrecisionTest is DSTestPlus { changePrank(_lender); uint256 lpRedemption = Maths.wrdivr(Maths.wmul(availableCollateral, _pool.indexToPrice(2549)), _pool.exchangeRate(2549)); vm.expectEmit(true, true, true, true); - emit Transfer(address(_pool), address(_lender), adjustedCollateralReq); - vm.expectEmit(true, true, true, true); emit ClaimCollateral(address(_lender), _pool.indexToPrice(2549), availableCollateral, lpRedemption); + vm.expectEmit(true, true, true, true); + emit Transfer(address(_pool), address(_lender), adjustedCollateralReq); // _lender.claimCollateral(_pool, availableCollateral, 2549); // check bucket state diff --git a/src/_test/ERC20Pool/ERC20ScaledPoolPurchaseQuote.t.sol b/src/_test/ERC20Pool/ERC20ScaledPoolPurchaseQuote.t.sol index 605b074..354c27b 100644 --- a/src/_test/ERC20Pool/ERC20ScaledPoolPurchaseQuote.t.sol +++ b/src/_test/ERC20Pool/ERC20ScaledPoolPurchaseQuote.t.sol @@ -73,9 +73,9 @@ contract ERC20ScaledPurchaseQuoteTokenTest is DSTestPlus { changePrank(_bidder); uint256 collateralToPurchaseWith = 4 * 1e18; vm.expectEmit(true, true, false, true); - emit Transfer(address(_bidder), address(_pool), collateralToPurchaseWith); - vm.expectEmit(true, true, false, true); emit AddCollateral(address(_bidder), priceAtTestIndex, collateralToPurchaseWith); + vm.expectEmit(true, true, false, true); + emit Transfer(address(_bidder), address(_pool), collateralToPurchaseWith); _pool.addCollateral(collateralToPurchaseWith, testIndex); // check bucket state @@ -86,9 +86,9 @@ contract ERC20ScaledPurchaseQuoteTokenTest is DSTestPlus { // bidder uses their LP to purchase all quote token in the bucket vm.expectEmit(true, true, false, true); - emit Transfer(address(_pool), address(_bidder), 10_000 * 1e18); - vm.expectEmit(true, true, false, true); emit RemoveQuoteToken(address(_bidder), priceAtTestIndex, 10_000 * 1e18, _pool.lup()); + vm.expectEmit(true, true, false, true); + emit Transfer(address(_pool), address(_bidder), 10_000 * 1e18); _pool.removeQuoteToken(10_000 * 1e18, testIndex); assertEq(_quote.balanceOf(address(_bidder)), 10_000 * 1e18); @@ -109,9 +109,9 @@ contract ERC20ScaledPurchaseQuoteTokenTest is DSTestPlus { changePrank(_lender); uint256 lpValueInCollateral = 3.321274866808485288 * 1e18; vm.expectEmit(true, true, true, true); - emit Transfer(address(_pool), address(_lender), lpValueInCollateral); - vm.expectEmit(true, true, true, true); emit RemoveCollateral(address(_lender), priceAtTestIndex, lpValueInCollateral); + vm.expectEmit(true, true, true, true); + emit Transfer(address(_pool), address(_lender), lpValueInCollateral); _pool.removeAllCollateral(testIndex); assertEq(_collateral.balanceOf(address(_lender)), lpValueInCollateral); assertEq(_pool.lpBalance(testIndex, address(_lender)), 0); @@ -119,9 +119,9 @@ contract ERC20ScaledPurchaseQuoteTokenTest is DSTestPlus { // bidder removes their _collateral changePrank(_bidder); vm.expectEmit(true, true, true, true); - emit Transfer(address(_pool), address(_bidder), 0.678725133191514712 * 1e18); - vm.expectEmit(true, true, true, true); emit RemoveCollateral(address(_bidder), priceAtTestIndex, 0.678725133191514712 * 1e18); + vm.expectEmit(true, true, true, true); + emit Transfer(address(_pool), address(_bidder), 0.678725133191514712 * 1e18); _pool.removeAllCollateral(testIndex); assertEq(_pool.lpBalance(testIndex, address(_bidder)), 0); @@ -169,43 +169,46 @@ contract ERC20ScaledPurchaseQuoteTokenTest is DSTestPlus { changePrank(_bidder); uint256 amountToPurchase = 10_100 * 1e18; assertGt(_quote.balanceOf(address(_pool)), amountToPurchase); - uint256 amountWithInterest = 10_000.642786573656600000 * 1e18; + uint256 amountWithInterest = 10_000.642786573732910000 * 1e18; // adding extra collateral to account for interest accumulation uint256 collateralToPurchaseWith = Maths.wmul(Maths.wdiv(amountToPurchase, p2550), 1.01 * 1e18); assertEq(collateralToPurchaseWith, 3.388032491631335842 * 1e18); _pool.addCollateral(collateralToPurchaseWith, 2550); vm.expectEmit(true, true, false, true); - emit Transfer(address(_pool), address(_bidder), amountWithInterest); - vm.expectEmit(true, true, false, true); emit RemoveQuoteToken(address(_bidder), p2550, amountWithInterest, _pool.indexToPrice(2552)); + vm.expectEmit(true, true, false, true); + emit Transfer(address(_pool), address(_bidder), amountWithInterest); _pool.removeAllQuoteToken(2550); assertEq(_quote.balanceOf(address(_bidder)), amountWithInterest); // bidder withdraws unused collateral uint256 collateralRemoved = 0; - uint256 expectedCollateral = 0.066544137733669793 * 1e18; + uint256 expectedCollateral = 0.066544137733644449 * 1e18; vm.expectEmit(true, true, true, true); emit RemoveCollateral(address(_bidder), p2550, expectedCollateral); - _pool.removeAllCollateral(2550); + (uint256 amount, ) = _pool.removeAllCollateral(2550); + assertEq(amount, expectedCollateral); collateralRemoved += expectedCollateral; assertEq(_pool.lpBalance(2550, address(_bidder)), 0); skip(7200); // lender exchanges their LP for collateral changePrank(_lender); - expectedCollateral = 1.992893012338599629 * 1e18; + expectedCollateral = 1.992893012338614836 * 1e18; vm.expectEmit(true, true, true, true); emit RemoveCollateral(address(_lender), p2550, expectedCollateral); - _pool.removeAllCollateral(2550); + (amount, ) = _pool.removeAllCollateral(2550); + assertEq(amount, expectedCollateral); collateralRemoved += expectedCollateral; assertEq(_pool.lpBalance(2550, address(_lender)), 0); skip(3600); // lender1 exchanges their LP for collateral changePrank(_lender1); - expectedCollateral = 1.328595341559066420 * 1e18; + expectedCollateral = 1.328595341559076557 * 1e18; vm.expectEmit(true, true, true, true); emit RemoveCollateral(address(_lender1), p2550, expectedCollateral); - _pool.removeAllCollateral(2550); + (amount, ) = _pool.removeAllCollateral(2550); + assertEq(amount, expectedCollateral); collateralRemoved += expectedCollateral; assertEq(_pool.lpBalance(2550, address(_lender1)), 0); assertEq(collateralRemoved, collateralToPurchaseWith); diff --git a/src/_test/ERC20Pool/ERC20ScaledPoolQuoteToken.t.sol b/src/_test/ERC20Pool/ERC20ScaledPoolQuoteToken.t.sol index 9875db7..ca03855 100644 --- a/src/_test/ERC20Pool/ERC20ScaledPoolQuoteToken.t.sol +++ b/src/_test/ERC20Pool/ERC20ScaledPoolQuoteToken.t.sol @@ -63,9 +63,9 @@ contract ERC20ScaledQuoteTokenTest is DSTestPlus { // test 10_000 DAI deposit at price of 1 MKR = 3_010.892022197881557845 DAI changePrank(_lender); vm.expectEmit(true, true, false, true); - emit Transfer(address(_lender), address(_pool), 10_000 * 1e18); - vm.expectEmit(true, true, false, true); emit AddQuoteToken(address(_lender), _p3010, 10_000 * 1e18, BucketMath.MAX_PRICE); + vm.expectEmit(true, true, false, true); + emit Transfer(address(_lender), address(_pool), 10_000 * 1e18); _pool.addQuoteToken(10_000 * 1e18, 2550); assertEq(_pool.htp(), 0); @@ -86,9 +86,9 @@ contract ERC20ScaledQuoteTokenTest is DSTestPlus { // test 20_000 DAI deposit at price of 1 MKR = 2_995.912459898389633881 DAI vm.expectEmit(true, true, false, true); - emit Transfer(address(_lender), address(_pool), 20_000 * 1e18); - vm.expectEmit(true, true, false, true); emit AddQuoteToken(address(_lender), 2_995.912459898389633881 * 1e18, 20_000 * 1e18, BucketMath.MAX_PRICE); + vm.expectEmit(true, true, false, true); + emit Transfer(address(_lender), address(_pool), 20_000 * 1e18); _pool.addQuoteToken(20_000 * 1e18, 2551); assertEq(_pool.htp(), 0); @@ -108,9 +108,9 @@ contract ERC20ScaledQuoteTokenTest is DSTestPlus { // test 40_000 DAI deposit at price of 1 MKR = 3_025.946482308870940904 DAI vm.expectEmit(true, true, false, true); - emit Transfer(address(_lender), address(_pool), 40_000 * 1e18); - vm.expectEmit(true, true, false, true); emit AddQuoteToken(address(_lender), 3_025.946482308870940904 * 1e18, 40_000 * 1e18, BucketMath.MAX_PRICE); + vm.expectEmit(true, true, false, true); + emit Transfer(address(_lender), address(_pool), 40_000 * 1e18); _pool.addQuoteToken(40_000 * 1e18, 2549); assertEq(_pool.htp(), 0); @@ -243,17 +243,17 @@ contract ERC20ScaledQuoteTokenTest is DSTestPlus { vm.expectEmit(true, true, false, true); emit RemoveQuoteToken(address(_lender), _pool.indexToPrice(1606), expectedWithdrawal, _pool.indexToPrice(1663)); uint lpRedeemed = _pool.removeQuoteToken(1_700 * 1e18, 1606); - assertEq(lpRedeemed, 1_699.988430646833722457777450974 * 1e27); + assertEq(lpRedeemed, 1_699.988430646832348876473462074 * 1e27); // lender removes all quote token, including interest, from the bucket assertGt(_pool.indexToPrice(1606), _pool.htp()); - expectedWithdrawal = 1_700.023138863804135800 * 1e18; + expectedWithdrawal = 1_700.023138863806883000 * 1e18; vm.expectEmit(true, true, false, true); emit RemoveQuoteToken(address(_lender), _pool.indexToPrice(1606), expectedWithdrawal, _pool.indexToPrice(1663)); uint256 removed; (removed, lpRedeemed) = _pool.removeAllQuoteToken(1606); assertEq(removed, expectedWithdrawal); - assertEq(lpRedeemed, 1_700.011569353166277542222549026 * 1e27); + assertEq(lpRedeemed, 1_700.011569353167651123526537926 * 1e27); assertEq(_quote.balanceOf(address(_lender)), lenderBalanceBefore + 1_700 * 1e18 + expectedWithdrawal); assertEq(_pool.lpBalance(1606, address(_lender)), 0); diff --git a/src/_test/FenwickTree.t.sol b/src/_test/FenwickTree.t.sol index f98feda..4d59d55 100644 --- a/src/_test/FenwickTree.t.sol +++ b/src/_test/FenwickTree.t.sol @@ -12,6 +12,10 @@ contract FenwickTreeInstance is FenwickTree { _add(i_, x_); } + function remove(uint256 i_, uint256 x_) public { + _remove(i_, x_); + } + function mult(uint256 i_, uint256 f_) public { _mult(i_, f_); } @@ -94,6 +98,19 @@ contract FenwickTreeTest is DSTestPlus { assertEq(tree.findSum(500 * 1e18), 9); assertEq(tree.findSum(900 * 1e18), 11); assertEq(tree.findSum(1_000 * 1e18), 8191); + + tree.remove(11, 300 * 1e18); + + assertEq(tree.treeSum(), 612 * 1e18); + + assertEq(tree.findSum(10 * 1e18), 5); + assertEq(tree.findSum(100 * 1e18), 5); + assertEq(tree.findSum(200 * 1e18), 9); + assertEq(tree.findSum(350 * 1e18), 9); + assertEq(tree.findSum(400 * 1e18), 9); + assertEq(tree.findSum(500 * 1e18), 9); + assertEq(tree.findSum(900 * 1e18), 8191); + assertEq(tree.findSum(1_000 * 1e18), 8191); } function testFenwickFirstBorrow() external { diff --git a/src/base/FenwickTree.sol b/src/base/FenwickTree.sol index 8b352e1..8a8b69e 100644 --- a/src/base/FenwickTree.sol +++ b/src/base/FenwickTree.sol @@ -35,7 +35,7 @@ abstract contract FenwickTree { require(f_ != 0, "FW:M:FACTOR_ZERO"); i_ += 1; - uint256 sum; + uint256 sum = 0; uint256 j; uint256 df = f_ - Maths.WAD; @@ -46,13 +46,17 @@ abstract contract FenwickTree { scaledI = scaling[i_]; sum = scaledI != 0 ? sum + Maths.wmul(Maths.wmul(df, values[i_]), scaledI) : sum + Maths.wmul(df, values[i_]); scaling[i_] = scaledI != 0 ? Maths.wmul(f_, scaledI) : f_; - j = i_ + _lsb(i_); - i_ -= _lsb(i_); - while ((_lsb(j) < _lsb(i_)) || (i_ == 0 && j <= SIZE)) { + uint256 lsbI = _lsb(i_); + j = i_ + lsbI; + i_ -= lsbI; + uint256 lsbJ = _lsb(j); + //slither-disable-next-line incorrect-equality + while ((lsbJ < _lsb(i_)) || (i_ == 0 && j <= SIZE)) { values[j] += sum; scaledJ = scaling[j]; if (scaledJ != 0) sum = Maths.wmul(sum, scaledJ); - j += _lsb(j); + j += lsbJ; + lsbJ = _lsb(j); } } } @@ -61,9 +65,10 @@ abstract contract FenwickTree { require(i_ < SIZE, "FW:A:INVALID_INDEX"); i_ += 1; - uint256 j = 8192; // 1 << 13 - uint256 ii; - uint256 sc = Maths.WAD; + uint256 j = 8192; // 1 << 13 + uint256 ii = 0; + uint256 sc = Maths.WAD; + uint256 index = 8192; uint256 scaled; @@ -71,11 +76,12 @@ abstract contract FenwickTree { if (((i_ - 1) & j) != 0) { ii += j; } else { - scaled = scaling[ii + j]; + scaled = scaling[index]; if (scaled != 0) sc = Maths.wmul(sc, scaled); - values[ii + j] += Maths.wdiv(x_, sc); + values[index] += Maths.wdiv(x_, sc); } j = j >> 1; + index = ii + j; } } @@ -84,8 +90,9 @@ abstract contract FenwickTree { i_ += 1; uint256 j = 8192; // 1 << 13 - uint256 ii; + uint256 ii = 0; uint256 sc = Maths.WAD; + uint256 index = 8192; uint256 scaled; @@ -93,11 +100,12 @@ abstract contract FenwickTree { if (((i_ - 1) & j) != 0) { ii += j; } else { - scaled = scaling[ii + j]; + scaled = scaling[index]; if (scaled != 0) sc = Maths.wmul(sc, scaled); - values[ii + j] -= Maths.wdiv(x_, sc); + values[index] -= Maths.wdiv(x_, sc); } j = j >> 1; + index = ii + j; } } @@ -106,19 +114,21 @@ abstract contract FenwickTree { uint256 sc = Maths.WAD; uint256 j = 8192; // 1 << 13 uint256 ii; + uint256 index = 8192; uint256 scaled; - while (j > 0 && ii + j <= SIZE) { - scaled = scaling[ii + j]; + while (j > 0 && index <= SIZE) { + scaled = scaling[index]; if (i_ & j != 0) { - s_ = scaled != 0 ? s_ + Maths.wmul(Maths.wmul(sc, scaled), values[ii + j]) : s_ + Maths.wmul(sc, values[ii + j]); + s_ = scaled != 0 ? s_ + Maths.wmul(Maths.wmul(sc, scaled), values[index]) : s_ + Maths.wmul(sc, values[index]); } else { if (scaled != 0) sc = Maths.wmul(sc, scaled); } ii = ii + (i_ & j); j = j >> 1; + index = ii + j; } } @@ -131,17 +141,18 @@ abstract contract FenwickTree { // TODO: rename this to findIndexOfSum // TODO: should this revert if failed to find a value past a given index instead of SIZE? function _findSum(uint256 x_) internal view returns (uint256 m_) { - uint256 i = 4096; // 1 << (_numBits - 1) = 1 << (13 - 1) = 4096 - uint256 ss; - uint256 sc = Maths.WAD; + uint256 i = 4096; // 1 << (_numBits - 1) = 1 << (13 - 1) = 4096 + uint256 ss = 0; + uint256 sc = Maths.WAD; + uint256 index = 4096; uint256 scaledM; uint256 scaledMInc; uint256 ssCond; while (i > 0) { - scaledMInc = scaling[m_ + i]; - ssCond = scaledMInc != 0 ? ss + Maths.wmul(Maths.wmul(sc, scaledMInc), values[m_ + i]) : ss + Maths.wmul(sc, values[m_ + i]); + scaledMInc = scaling[index]; + ssCond = scaledMInc != 0 ? ss + Maths.wmul(Maths.wmul(sc, scaledMInc), values[index]) : ss + Maths.wmul(sc, values[index]); if (ssCond < x_) { m_ += i; scaledM = scaling[m_]; @@ -150,18 +161,16 @@ abstract contract FenwickTree { if (scaledMInc != 0) sc = Maths.wmul(sc, scaledMInc); } i = i >> 1; + index = m_ + i; } } // Least significant bit - function _lsb(uint256 i_) internal pure returns (uint256) { - if (i_ == 0) return 0; - // "i & (-i)" - return - i_ & - ((i_ ^ - 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) + - 1); + function _lsb(uint256 i_) internal pure returns (uint256 lsb_) { + if (i_ != 0) { + // "i & (-i)" + lsb_ = i_ & ((i_ ^ 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) + 1); + } } function _treeSum() internal view returns (uint256) { diff --git a/src/base/ScaledPool.sol b/src/base/ScaledPool.sol index 2dce17e..c83a646 100644 --- a/src/base/ScaledPool.sol +++ b/src/base/ScaledPool.sol @@ -25,8 +25,8 @@ abstract contract ScaledPool is Clone, FenwickTree, Queue, IScaledPool { uint256 public constant SECONDS_PER_YEAR = 3_600 * 24 * 365; uint256 public constant SECONDS_PER_HALFDAY = 43_200; - uint256 public constant RATE_INCREASE_COEFFICIENT = 1.1 * 10**18; - uint256 public constant RATE_DECREASE_COEFFICIENT = 0.9 * 10**18; + uint256 public constant INCREASE_COEFFICIENT = 1.1 * 10**18; + uint256 public constant DECREASE_COEFFICIENT = 0.9 * 10**18; // lambda used for the EMAs calculated as exp(-1/7 * ln2) uint256 public constant LAMBDA_EMA = 0.905723664263906671 * 10**18; uint256 public constant EMA_RATE_FACTOR = 10**18 - LAMBDA_EMA; @@ -66,7 +66,7 @@ abstract contract ScaledPool is Clone, FenwickTree, Queue, IScaledPool { /** @dev Used for tracking LP token ownership address for transferLPTokens access control */ mapping(address => address) public override lpTokenOwnership; - uint256 internal _poolInitializations = 0; + uint256 internal poolInitializations = 0; /*********************************/ /*** Lender External Functions ***/ @@ -90,8 +90,8 @@ abstract contract ScaledPool is Clone, FenwickTree, Queue, IScaledPool { _updateInterestRate(curDebt, newLup); // move quote token amount from lender to pool - quoteToken().safeTransferFrom(msg.sender, address(this), amount_ / quoteTokenScale); emit AddQuoteToken(msg.sender, _indexToPrice(index_), amount_, newLup); + quoteToken().safeTransferFrom(msg.sender, address(this), amount_ / quoteTokenScale); } function approveNewPositionOwner(address allowedNewOwner_) external { @@ -266,8 +266,8 @@ abstract contract ScaledPool is Clone, FenwickTree, Queue, IScaledPool { _updateInterestRate(borrowerDebt, newLup); // move quote token amount from pool to lender - quoteToken().safeTransfer(msg.sender, amount / quoteTokenScale); emit RemoveQuoteToken(msg.sender, _indexToPrice(index_), amount, newLup); + quoteToken().safeTransfer(msg.sender, amount / quoteTokenScale); } function _updateInterestRate(uint256 curDebt_, uint256 lup_) internal { @@ -291,9 +291,9 @@ abstract contract ScaledPool is Clone, FenwickTree, Queue, IScaledPool { int256 increaseFactor = ((targetUtilization + actualUtilization - 10**18) ** 2) / 10**18; if (decreaseFactor < increaseFactor - 10**18) { - interestRate = Maths.wmul(interestRate, RATE_INCREASE_COEFFICIENT); + interestRate = Maths.wmul(interestRate, INCREASE_COEFFICIENT); } else if (decreaseFactor > 10**18 - increaseFactor) { - interestRate = Maths.wmul(interestRate, RATE_DECREASE_COEFFICIENT); + interestRate = Maths.wmul(interestRate, DECREASE_COEFFICIENT); } interestRateUpdate = block.timestamp; @@ -384,7 +384,7 @@ abstract contract ScaledPool is Clone, FenwickTree, Queue, IScaledPool { } function _pendingInterestFactor(uint256 elapsed_) internal view returns (uint256) { - uint256 rate = (interestRate / SECONDS_PER_YEAR) * elapsed_; + uint256 rate = (interestRate * elapsed_) / SECONDS_PER_YEAR; return PRBMathUD60x18.exp(rate); } @@ -393,7 +393,7 @@ abstract contract ScaledPool is Clone, FenwickTree, Queue, IScaledPool { return Maths.wmul(inflatorSnapshot, _pendingInterestFactor(elapsed)); } - function _threshold_price(uint256 debt_, uint256 collateral_, uint256 inflator_) internal pure returns (uint256) { + function _thresholdPrice(uint256 debt_, uint256 collateral_, uint256 inflator_) internal pure returns (uint256) { if (collateral_ != 0) return Maths.wdiv(Maths.wmul(inflator_, debt_), collateral_); return 0; } diff --git a/src/erc20/ERC20Pool.sol b/src/erc20/ERC20Pool.sol index 4eddce3..494e939 100644 --- a/src/erc20/ERC20Pool.sol +++ b/src/erc20/ERC20Pool.sol @@ -28,7 +28,7 @@ contract ERC20Pool is IERC20Pool, ScaledPool { /****************************/ function initialize(uint256 rate_) external { - require(_poolInitializations == 0, "P:INITIALIZED"); + require(poolInitializations == 0, "P:INITIALIZED"); collateralScale = 10**(18 - collateral().decimals()); quoteTokenScale = 10**(18 - quoteToken().decimals()); @@ -40,7 +40,7 @@ contract ERC20Pool is IERC20Pool, ScaledPool { minFee = 0.0005 * 10**18; // increment initializations count to ensure these values can't be updated - _poolInitializations += 1; + poolInitializations += 1; } /***********************************/ @@ -56,7 +56,7 @@ contract ERC20Pool is IERC20Pool, ScaledPool { borrower.collateral += amount_; // update loan queue - uint256 thresholdPrice = _threshold_price(borrower.debt, borrower.collateral, borrower.inflatorSnapshot); + uint256 thresholdPrice = _thresholdPrice(borrower.debt, borrower.collateral, borrower.inflatorSnapshot); if (borrower.debt != 0) _updateLoanQueue(msg.sender, thresholdPrice, oldPrev_, newPrev_); borrowers[msg.sender] = borrower; @@ -66,8 +66,8 @@ contract ERC20Pool is IERC20Pool, ScaledPool { _updateInterestRate(curDebt, _lup()); // move collateral from sender to pool - collateral().safeTransferFrom(msg.sender, address(this), amount_ / collateralScale); emit PledgeCollateral(msg.sender, amount_); + collateral().safeTransferFrom(msg.sender, address(this), amount_ / collateralScale); } function borrow(uint256 amount_, uint256 limitIndex_, address oldPrev_, address newPrev_) external override { @@ -101,15 +101,15 @@ contract ERC20Pool is IERC20Pool, ScaledPool { lenderDebt += amount_; // update loan queue - uint256 thresholdPrice = _threshold_price(borrower.debt, borrower.collateral, borrower.inflatorSnapshot); + uint256 thresholdPrice = _thresholdPrice(borrower.debt, borrower.collateral, borrower.inflatorSnapshot); _updateLoanQueue(msg.sender, thresholdPrice, oldPrev_, newPrev_); borrowers[msg.sender] = borrower; _updateInterestRate(curDebt, newLup); // move borrowed amount from pool to sender - quoteToken().safeTransfer(msg.sender, amount_ / quoteTokenScale); emit Borrow(msg.sender, newLup, amount_); + quoteToken().safeTransfer(msg.sender, amount_ / quoteTokenScale); } function pullCollateral(uint256 amount_, address oldPrev_, address newPrev_) external override { @@ -124,7 +124,7 @@ contract ERC20Pool is IERC20Pool, ScaledPool { borrower.collateral -= amount_; // update loan queue - uint256 thresholdPrice = _threshold_price(borrower.debt, borrower.collateral, borrower.inflatorSnapshot); + uint256 thresholdPrice = _thresholdPrice(borrower.debt, borrower.collateral, borrower.inflatorSnapshot); if (borrower.debt != 0) _updateLoanQueue(msg.sender, thresholdPrice, oldPrev_, newPrev_); // update pool state @@ -132,8 +132,8 @@ contract ERC20Pool is IERC20Pool, ScaledPool { _updateInterestRate(curDebt, curLup); // move collateral from pool to sender - collateral().safeTransfer(msg.sender, amount_ / collateralScale); emit PullCollateral(msg.sender, amount_); + collateral().safeTransfer(msg.sender, amount_ / collateralScale); } function repay(uint256 maxAmount_, address oldPrev_, address newPrev_) external override { @@ -162,7 +162,7 @@ contract ERC20Pool is IERC20Pool, ScaledPool { _removeLoanQueue(msg.sender, oldPrev_); } else { if (borrowersCount != 0) require(borrower.debt > _poolMinDebtAmount(curDebt), "R:B:AMT_LT_AVG_DEBT"); - uint256 thresholdPrice = _threshold_price(borrower.debt, borrower.collateral, borrower.inflatorSnapshot); + uint256 thresholdPrice = _thresholdPrice(borrower.debt, borrower.collateral, borrower.inflatorSnapshot); _updateLoanQueue(msg.sender, thresholdPrice, oldPrev_, newPrev_); } borrowers[msg.sender] = borrower; @@ -180,8 +180,8 @@ contract ERC20Pool is IERC20Pool, ScaledPool { _updateInterestRate(curDebt, newLup); // move amount to repay from sender to pool - quoteToken().safeTransferFrom(msg.sender, address(this), amount / quoteTokenScale); emit Repay(msg.sender, newLup, amount); + quoteToken().safeTransferFrom(msg.sender, address(this), amount / quoteTokenScale); } /*********************************/ @@ -210,8 +210,8 @@ contract ERC20Pool is IERC20Pool, ScaledPool { _updateInterestRate(borrowerDebt, _lup()); // move required collateral from sender to pool - collateral().safeTransferFrom(msg.sender, address(this), amount_ / collateralScale); emit AddCollateral(msg.sender, _indexToPrice(index_), amount_); + collateral().safeTransferFrom(msg.sender, address(this), amount_ / collateralScale); } function removeAllCollateral(uint256 index_) external override returns (uint256 amount_, uint256 lpAmount_) { @@ -274,8 +274,8 @@ contract ERC20Pool is IERC20Pool, ScaledPool { _updateInterestRate(borrowerDebt, _lup()); // move collateral from pool to lender - collateral().safeTransfer(msg.sender, amount_ / collateralScale); emit RemoveCollateral(msg.sender, price_, amount_); + collateral().safeTransfer(msg.sender, amount_ / collateralScale); } /**********************/ @@ -283,11 +283,11 @@ contract ERC20Pool is IERC20Pool, ScaledPool { /**********************/ function borrowerInfo(address borrower_) external view override returns (uint256, uint256, uint256, uint256) { - uint256 pending_debt = Maths.wmul(borrowers[borrower_].debt, Maths.wdiv(_pendingInflator(), inflatorSnapshot)); + uint256 pendingDebt = Maths.wmul(borrowers[borrower_].debt, Maths.wdiv(_pendingInflator(), inflatorSnapshot)); return ( borrowers[borrower_].debt, // accrued debt (WAD) - pending_debt, // current debt, accrued and pending accrual (WAD) + pendingDebt, // current debt, accrued and pending accrual (WAD) borrowers[borrower_].collateral, // deposited collateral including encumbered (WAD) borrowers[borrower_].inflatorSnapshot // used to calculate pending interest (WAD) ); diff --git a/src/erc20/ERC20PoolFactory.sol b/src/erc20/ERC20PoolFactory.sol index c9dff62..5618401 100644 --- a/src/erc20/ERC20PoolFactory.sol +++ b/src/erc20/ERC20PoolFactory.sol @@ -28,10 +28,10 @@ contract ERC20PoolFactory is IPoolFactory, PoolDeployer { bytes memory data = abi.encodePacked(collateral_, quote_); ERC20Pool pool = ERC20Pool(address(implementation).clone(data)); - pool.initialize(interestRate_); pool_ = address(pool); - deployedPools[ERC20_NON_SUBSET_HASH][collateral_][quote_] = pool_; emit PoolCreated(pool_); + + pool.initialize(interestRate_); } } diff --git a/src/erc20/interfaces/IERC20Pool.sol b/src/erc20/interfaces/IERC20Pool.sol index bf5b5b8..7d1cc80 100644 --- a/src/erc20/interfaces/IERC20Pool.sol +++ b/src/erc20/interfaces/IERC20Pool.sol @@ -65,12 +65,12 @@ interface IERC20Pool is IScaledPool { /** * @notice Mapping of borrower addresses to {Borrower} structs. * @dev NOTE: Cannot use appended underscore syntax for return params since struct is used. - * @param borrower_ Address of the borrower. - * @return debt Amount of debt that the borrower has, in quote token. - * @return collateral Amount of collateral that the borrower has deposited, in collateral token. - * @return inflatorSnapshot Snapshot of inflator value used to track interest on loans. + * @param borrower_ Address of the borrower. + * @return debt Amount of debt that the borrower has, in quote token. + * @return collateral Amount of collateral that the borrower has deposited, in collateral token. + * @return inflator Snapshot of inflator value used to track interest on loans. */ - function borrowers(address borrower_) external view returns (uint256 debt, uint256 collateral, uint256 inflatorSnapshot); + function borrowers(address borrower_) external view returns (uint256 debt, uint256 collateral, uint256 inflator); /** * @notice Returns the `collateralScale` state variable. diff --git a/src/erc721/ERC721Pool.sol b/src/erc721/ERC721Pool.sol index 301a864..fd3a783 100644 --- a/src/erc721/ERC721Pool.sol +++ b/src/erc721/ERC721Pool.sol @@ -46,7 +46,7 @@ contract ERC721Pool is IERC721Pool, ScaledPool { /****************************/ function initialize(uint256 rate_) external { - require(_poolInitializations == 0, "P:INITIALIZED"); + require(poolInitializations == 0, "P:INITIALIZED"); quoteTokenScale = 10**(18 - quoteToken().decimals()); @@ -58,7 +58,7 @@ contract ERC721Pool is IERC721Pool, ScaledPool { minFee = 0.0005 * 10**18; // increment initializations count to ensure these values can't be updated - _poolInitializations += 1; + poolInitializations += 1; } function initializeSubset(uint256[] memory tokenIds_, uint256 rate_) external override {