diff --git a/.github/workflows/code-coverage.yml b/.github/workflows/code-coverage.yml
index 6c7a77887..de29524ec 100644
--- a/.github/workflows/code-coverage.yml
+++ b/.github/workflows/code-coverage.yml
@@ -21,7 +21,7 @@ jobs:
# Report code coverage to discord
- name: Generate coverage
- run: forge coverage --report lcov --no-match-test testLoad
+ run: forge coverage --report lcov --no-match-test "testLoad|invariant"
- name: Setup LCOV
uses: hrishikesh-kadam/setup-lcov@v1
- name: Filter lcov
diff --git a/.github/workflows/forge-tests.yml b/.github/workflows/forge-tests.yml
deleted file mode 100644
index b77869bef..000000000
--- a/.github/workflows/forge-tests.yml
+++ /dev/null
@@ -1,55 +0,0 @@
-name: Forge Tests
-
-on: [push]
-
-env:
- ## Loads environment from secrets
- ETH_RPC_URL: ${{secrets.ETH_RPC_URL}}
-
-jobs:
- tests:
- runs-on: ubuntu-latest
- steps:
- - uses: actions/checkout@v2
- with:
- submodules: recursive
-
- - name: Install Foundry
- uses: foundry-rs/foundry-toolchain@v1
- with:
- version: nightly
-
- - name: Create ids
- shell: bash
- run: |
- echo "##[set-output name=dir;]$(echo ${GITHUB_REF#refs/heads/}-gas-reports)"
- echo "##[set-output name=branch;]$(echo ${GITHUB_REF#refs/heads/})"
- id: gas
-
- - name: Cache gas snasphot
- id: cache-gas
- uses: actions/cache@v3
- with:
- path: gas-reports-*
- key: ${{ steps.gas.outputs.dir }}-${{ github.run_id }}
- restore-keys: |
- ${{ steps.gas.outputs.dir }}-
-
- - name: Run tests and compare gas
- shell: bash
- run: |
- prevRuns=$(find . -name "gas-reports-*" -printf '.' | wc -m)
- if [ $prevRuns = "0" ]; then echo "No previous run"; else dir=$(ls -td -- gas-reports-* | head -n 1) && forge snapshot --no-match-test testLoad --diff $dir/.gas-snapshot;fi 2>&1 | tee gas-diff-output.txt
- mkdir -p gas-reports-${{ github.run_id }}
- forge snapshot --no-match-test testLoad --gas-report --snap gas-reports-${{ github.run_id }}/.gas-snapshot
- echo "##[set-output name=gas-diff;]$(echo $(cat gas-diff-output.txt|tail -1|sed -e 's/\x1b\[[0-9;]*m//g'))"
- id: forge-tests
-
- - name: Send to Discord
- uses: appleboy/discord-action@master
- with:
- webhook_id: ${{ secrets.DISCORD_ID }}
- webhook_token: ${{ secrets.DISCORD_TOKEN }}
- message: https://github.com/ajna-finance/contracts/commit/${{ github.sha }} ```${{ steps.forge-tests.outputs.gas-diff }} compared with last run in branch ${{ steps.gas.outputs.branch }}```
-
- timeout-minutes: 10
\ No newline at end of file
diff --git a/.github/workflows/size-check.yml b/.github/workflows/size-check.yml
index de68bb5a8..10f623fcd 100644
--- a/.github/workflows/size-check.yml
+++ b/.github/workflows/size-check.yml
@@ -9,7 +9,8 @@ jobs:
submodules: recursive
- name: Cache compiler installations
- uses: actions/cache@v2
+ id: cache-compiler
+ uses: actions/cache@v3
with:
path: |
~/.solcx
@@ -32,13 +33,13 @@ jobs:
- name: Set pip cache directory path
id: pip-cache-dir-path
run: |
- echo "::set-output name=dir::$(pip cache dir)"
+ echo "PIP_CACHE_DIR=$(pip cache dir)" >> $GITHUB_ENV
- name: Restore pip cache
- uses: actions/cache@v2
+ uses: actions/cache@v3
id: pip-cache
with:
path: |
- ${{ steps.pip-cache-dir-path.outputs.dir }}
+ ${PIP_CACHE_DIR}
key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements-dev.txt') }}
restore-keys: |
${{ runner.os }}-pip-${{ hashFiles('**/requirements-dev.txt') }}
@@ -47,11 +48,11 @@ jobs:
run: pip install -r requirements-dev.txt
- name: Run check-size script
+ id: check-size
run: |
mkdir -p size-reports-${{ github.run_id }}
./check-size.sh | tee size-reports-${{github.run_id}}/size-report
- echo "##[set-output name=size-report;]$(echo $(cat size-reports-${{github.run_id}}/size-report|tail -1))"
- id: check-size
+ echo "SIZE_REPORT=$(echo $(cat size-reports-${{github.run_id}}/size-report|tail -1))" >> $GITHUB_OUTPUT
- name: Send size report to Discord
uses: appleboy/discord-action@master
@@ -59,5 +60,5 @@ jobs:
webhook_id: ${{ secrets.DISCORD_ID }}
webhook_token: ${{ secrets.DISCORD_TOKEN }}
username: "Contract Size Reporter"
- message: Largest contract size for `${{ github.ref }}` ```${{ steps.check-size.outputs.size-report }}```
- timeout-minutes: 2
\ No newline at end of file
+ message: Largest contract size for `${{ github.ref }}` ```${{ steps.check-size.outputs.SIZE_REPORT }}```
+ timeout-minutes: 3
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
index 5e9c34d5d..1357170a3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -13,4 +13,5 @@ reports/
coverage/
*.info
report/
-keystore/
\ No newline at end of file
+keystore/
+broadcast/
\ No newline at end of file
diff --git a/Makefile b/Makefile
index 80d2ddfff..5a0c59780 100644
--- a/Makefile
+++ b/Makefile
@@ -14,13 +14,20 @@ install :; git submodule update --init --recursive
build :; forge clean && forge build
# Tests
-test :; forge test --no-match-test testLoad # --ffi # enable if you need the `ffi` cheat code on HEVM
-test-with-gas-report :; FOUNDRY_PROFILE=optimized forge test --no-match-test testLoad --gas-report # --ffi # enable if you need the `ffi` cheat code on HEVM
+test :; forge test --no-match-test "testLoad|invariant" # --ffi # enable if you need the `ffi` cheat code on HEVM
+test-with-gas-report :; FOUNDRY_PROFILE=optimized forge test --no-match-test "testLoad|invariant" --gas-report # --ffi # enable if you need the `ffi` cheat code on HEVM
test-load :; FOUNDRY_PROFILE=optimized forge test --match-test testLoad --gas-report
-coverage :; forge coverage --no-match-test testLoad
+test-invariant :; forge t --mt invariant
+coverage :; forge coverage --no-match-test "testLoad|invariant"
# Generate Gas Snapshots
snapshot :; forge clean && forge snapshot
analyze:
slither src/. ; slither src/libraries/external/.
+
+
+# Deployment
+deploy-contracts:
+ forge script ./deploy.sol \
+ --rpc-url ${ETH_RPC_URL} --sender ${DEPLOY_ADDRESS} --keystore ${DEPLOY_KEY} --broadcast -vvv
diff --git a/README.md b/README.md
index 834b02766..22eeddcb6 100644
--- a/README.md
+++ b/README.md
@@ -2,16 +2,21 @@
The Ajna protocol is a non-custodial, peer-to-peer, permissionless lending, borrowing and trading system that requires no governance or external price feeds to function. The protocol consists of pools: pairings of quote tokens provided by lenders and collateral tokens provided by borrowers. Ajna is capable of accepting fungible tokens as quote tokens and both fungible and non-fungible tokens as collateral tokens.
-## Limitations
-- The following types of tokens are incompatible with Ajna, and no countermeasures exist to explicitly prevent creating a pool with such tokens:
- - Fungible tokens whose balance rebases.
- - NFTs which charge a fee on transfer.
- - Fungible tokens with more than 18 decimals or 0 decimals.
+## Accepted tokens:
+- Fungible tokens (following the [ERC20 token standard](https://eips.ethereum.org/EIPS/eip-20)).
+- Non-fungible tokens (following the [ERC721 token standard](https://eips.ethereum.org/EIPS/eip-721))
- Special considerations have been made to support specific NFTs with nonstandard ERC721 implementations, including _CryptoPunks_ and _CryptoKitties_. This support is limited to Ethereum mainnet.
+
+### Token limitations
+- The following types of tokens are incompatible with Ajna, and no countermeasures exist to explicitly prevent creating a pool with such tokens, actors should use them at their own risk:
+ - NFT and fungible tokens which charge a fee on transfer.
+ - Fungible tokens whose balance rebases.
+ - Fungible tokens with more than 18 decimals or 0 decimals.
- Borrowers cannot draw debt from a pool in the same block as when the pool was created.
- With the exception of quantized prices, pool inputs and most accumulators are not explicitly limited. The pool will stop functioning when the bounds of a `uint256` need to be exceeded to process a request.
+
## Development
### Requirements
- `python` 3.0+
@@ -150,35 +155,38 @@ Modifications to, or notices of actions by Licensor, contemplated above or under
## Deployment
-A deployment script has been created to automate deployment of libraries and factory contracts.
-To use it, set up an environment with the following:
-- **AJNA_TOKEN** - address of the AJNA token on your target chain
-- **ETH_RPC_URL** - node pointing to the target chain
-- **DEPLOY_KEY** - path to the JSON keystore file for your deployment account
+A deployment script has been created to automate deployment of libraries, factory contracts, and manager contracts.
+
+To use it, ensure the following env variables are in your `.env` file or exported into your environment.
+| Environment Variable | Purpose |
+|----------------------|---------|
+| `AJNA_TOKEN` | address of the AJNA token on your target chain
+| `DEPLOY_ADDRESS` | address from which you wish to deploy
+| `DEPLOY_KEY` | path to the JSON keystore file for the deployment address
+| `ETHERSCAN_API_KEY` | required to verify contracts
+| `ETH_RPC_URL` | node on your target deployment network
-Ensure your deployment account is funded with some ETH for gas.
-The deployment script takes no arguments, and interactively prompts for your keystore password:
+Since contract source has not yet been made public, the `--verify` switch has been omitted. To run:
+
```
-./deploy.sh
+make deploy-contracts
```
Upon completion, contract addresses will be printed to `stdout`:
```
-Deploying to chain with AJNA token address 0xDD576260ed60AaAb798D8ECa9bdBf33D70E077F4
-Enter keystore password:
-Deploying libraries...
-Deployed Auctions to 0xDD576260ed60AaAb798D8ECa9bdBf33D70E077F4
-Deployed LenderActions to 0x4c08A2ec1f5C067DC53A5fCc36C649501F403b93
-Deployed PoolCommons to 0x8BBCA51044d00dbf16aaB8Fd6cbC5B45503B898b
-Deploying factories...
-Deployed ERC20PoolFactory to 0xED625fbf62695A13d2cADEdd954b23cc97249988
-Deployed ERC721PoolFactory to 0x775D30918A42160bC7aE56BA1660E32ff50CF6dC
-Deploying PoolInfoUtils...
-Deployed PoolInfoUtils to 0xd8A51cE16c7665111401C0Ba2ABEcE03B847b4e6
+== Logs ==
+ Deploying to chain with AJNA token address 0x34A1D3fff3958843C43aD80F30b94c510645C...
+ === Deployment addresses ===
+ ERC20 factory 0x50EEf481cae4250d252Ae577A09bF514f224C...
+ ERC721 factory 0x62c20Aa1e0272312BC100b4e23B4DC1Ed96dD...
+ PoolInfoUtils 0xDEb1E9a6Be7Baf84208BB6E10aC9F9bbE1D70...
+ PositionManager 0xD718d5A27a29FF1cD22403426084bA0d47986...
+ RewardsManager 0x4f559F30f5eB88D635FDe1548C4267DB8FaB0...
+
```
-Record the factory addresses.
+Record these addresses.
### Validation
@@ -203,4 +211,4 @@ cast send ${DAI_TOKEN} "approve(address,uint256)" ${WBTC_DAI_POOL} 50000ether \
--from ${DEPLOY_ADDRESS} --keystore ${DEPLOY_KEY}
cast send ${WBTC_DAI_POOL} "addQuoteToken(uint256,uint256)" 100ether 3232 \
--from ${DEPLOY_ADDRESS} --keystore ${DEPLOY_KEY}
-```
+```
\ No newline at end of file
diff --git a/check-code-coverage.sh b/check-code-coverage.sh
index 72fdb5be2..9bc6ba7ae 100755
--- a/check-code-coverage.sh
+++ b/check-code-coverage.sh
@@ -1,6 +1,6 @@
#!/bin/bash
-forge coverage --report lcov --no-match-test testLoad
+forge coverage --report lcov --no-match-test "testLoad|invariant"
lcov -r lcov.info "tests/*" -o lcov-filtered.info --rc lcov_branch_coverage=1
diff --git a/deploy.sh b/deploy.sh
deleted file mode 100755
index c23e79d98..000000000
--- a/deploy.sh
+++ /dev/null
@@ -1,63 +0,0 @@
-#!/bin/bash
-
-# load environment variables from .env file
-set -o allexport
-source .env
-set +o allexport
-
-echo Deploying to chain with AJNA token address ${AJNA_TOKEN:?}
-
-read -p "Enter keystore password: " -s password
-
-regex="Deployed to: ([0-9xa-fA-F]+)"
-linkage=()
-
-echo
-echo Deploying libraries...
-libraries=( Auctions LenderActions BorrowerActions PoolCommons PositionNFTSVG )
-for contract in "${libraries[@]}"
-do
- createlib="forge create --rpc-url ${ETH_RPC_URL:?} --keystore ${DEPLOY_KEY:?} --password ${password:?} \
- src/libraries/external/$contract.sol:$contract"
- output=$($createlib)
- if [[ $output =~ $regex ]]
- then
- address=${BASH_REMATCH[1]}
- printf "Deployed %20s to %s\n" ${contract:0:20} $address
- linkage+="--libraries src/libraries/external/$contract.sol:$contract:$address "
- else
- echo $contract was not deployed: $output
- exit 1
- fi
-done
-
-echo Deploying factories...
-factories=( ERC20PoolFactory ERC721PoolFactory )
-for contract in "${factories[@]}"
-do
- createfactory="forge create --rpc-url ${ETH_RPC_URL:?} --keystore ${DEPLOY_KEY:?} --password ${password:?} \
- src/$contract.sol:$contract --constructor-args ${AJNA_TOKEN:?} ${linkage}"
- output=$($createfactory)
- if [[ $output =~ $regex ]]
- then
- address=${BASH_REMATCH[1]}
- printf "Deployed %20s to %s\n" ${contract:0:20} $address
- else
- echo $contract was not deployed: $output
- exit 2
- fi
-done
-
-echo Deploying PoolInfoUtils...
-contract=PoolInfoUtils
-create="forge create --rpc-url ${ETH_RPC_URL:?} --keystore ${DEPLOY_KEY:?} --password ${password:?} \
- src/$contract.sol:$contract ${linkage}"
-output=$($create)
-if [[ $output =~ $regex ]]
-then
- address=${BASH_REMATCH[1]}
- printf "Deployed %20s to %s\n" ${contract:0:20} $address
-else
- echo $contract was not deployed: $output
- exit 1
-fi
\ No newline at end of file
diff --git a/deploy.sol b/deploy.sol
new file mode 100644
index 000000000..3b50b91f6
--- /dev/null
+++ b/deploy.sol
@@ -0,0 +1,36 @@
+// SPDX-License-Identifier: MIT
+pragma solidity 0.8.14;
+
+import { Script } from "forge-std/Script.sol";
+import "forge-std/console.sol";
+
+import { ERC20PoolFactory } from 'src/ERC20PoolFactory.sol';
+import { ERC721PoolFactory } from 'src/ERC721PoolFactory.sol';
+import { PoolInfoUtils } from 'src/PoolInfoUtils.sol';
+import { PositionManager } from 'src/PositionManager.sol';
+import { RewardsManager } from 'src/RewardsManager.sol';
+
+contract Deploy is Script {
+ address ajna;
+
+ function run() public {
+ ajna = vm.envAddress("AJNA_TOKEN");
+ console.log("Deploying to chain with AJNA token address %s", ajna);
+
+ vm.startBroadcast();
+ ERC20PoolFactory erc20factory = new ERC20PoolFactory(ajna);
+ ERC721PoolFactory erc721factory = new ERC721PoolFactory(ajna);
+ PoolInfoUtils poolInfoUtils = new PoolInfoUtils();
+
+ PositionManager positionManager = new PositionManager(erc20factory, erc721factory);
+ RewardsManager rewardsManager = new RewardsManager(ajna, positionManager);
+ vm.stopBroadcast();
+
+ console.log("=== Deployment addresses ===");
+ console.log("ERC20 factory %s", address(erc20factory));
+ console.log("ERC721 factory %s", address(erc721factory));
+ console.log("PoolInfoUtils %s", address(poolInfoUtils));
+ console.log("PositionManager %s", address(positionManager));
+ console.log("RewardsManager %s", address(rewardsManager));
+ }
+}
\ No newline at end of file
diff --git a/docs/Functions.md b/docs/Functions.md
index ac1d3b0c8..7e12e88ea 100644
--- a/docs/Functions.md
+++ b/docs/Functions.md
@@ -129,7 +129,7 @@
emit events:
- LenderActions.transferLPs():
- - TransferLPTokens
+ - TransferLPs
### kick
external libraries call:
@@ -370,7 +370,7 @@
- BorrowerActions.drawDebt()
- borrower not sender BorrowerNotSender()
- borrower debt less than pool min debt AmountLTMinDebt()
- - limit price reached LimitIndexReached()
+ - limit price reached LimitIndexExceeded()
- borrower cannot draw more debt BorrowerUnderCollateralized()
emit events:
diff --git a/docs/drawio/addCollateral.drawio b/docs/drawio/addCollateral.drawio
new file mode 100644
index 000000000..b62d8a395
--- /dev/null
+++ b/docs/drawio/addCollateral.drawio
@@ -0,0 +1 @@
+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
\ No newline at end of file
diff --git a/docs/drawio/addQuoteToken.drawio b/docs/drawio/addQuoteToken.drawio
new file mode 100644
index 000000000..0ea7fef45
--- /dev/null
+++ b/docs/drawio/addQuoteToken.drawio
@@ -0,0 +1 @@
+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
\ No newline at end of file
diff --git a/docs/drawio/bucketTake.drawio b/docs/drawio/bucketTake.drawio
new file mode 100644
index 000000000..942266631
--- /dev/null
+++ b/docs/drawio/bucketTake.drawio
@@ -0,0 +1 @@
+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
\ No newline at end of file
diff --git a/docs/drawio/components.drawio b/docs/drawio/components.drawio
new file mode 100644
index 000000000..ddebbac67
--- /dev/null
+++ b/docs/drawio/components.drawio
@@ -0,0 +1 @@
+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
\ No newline at end of file
diff --git a/docs/drawio/drawDebt.drawio b/docs/drawio/drawDebt.drawio
new file mode 100644
index 000000000..2653b64ad
--- /dev/null
+++ b/docs/drawio/drawDebt.drawio
@@ -0,0 +1 @@
+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
\ No newline at end of file
diff --git a/docs/drawio/kick.drawio b/docs/drawio/kick.drawio
new file mode 100644
index 000000000..c4758a438
--- /dev/null
+++ b/docs/drawio/kick.drawio
@@ -0,0 +1 @@
+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
\ No newline at end of file
diff --git a/docs/drawio/kickWithDeposit.drawio b/docs/drawio/kickWithDeposit.drawio
new file mode 100644
index 000000000..6c1076aed
--- /dev/null
+++ b/docs/drawio/kickWithDeposit.drawio
@@ -0,0 +1 @@
+7V1bl5s4Ev41Pqez59iHu+3Hvs70Ts+mpzuZZPdNBtnWNCAHRLc9v34lkLgKfGnAzrTzkIAQkqz66lNVqUQG+rW3/iUAq+Xv2IHuQFOc9UC/GWiaZuoG/YeVbJKS8VRJChYBcpIiNSt4Rn9DXiiqRciBYaEiwdglaFUstLHvQ5sUykAQ4LditTl2i72uwAJWCp5t4FZLvyGHLHmpqijZg18hWix51xOTP5gB+2UR4Mjn/fnYh8kTD4hmeNVwCRz8livSbwf6dYAxSa689TV02bSKGUveu6t5mg45gD7Z5YXxDMyAahszWxvPjZky1JIWXoEb8Wm4jGyCsB/yAZONmB869hW7jDz3Ac2hi+iv1K9WMEAeJDCgT1xe/JiVXb0tEYHPK2CzV98ocmjZknguvVPpJRUmAfSVIL13XbAK0SzuVaElAbSjIESv8AmGCWZYKY4I6+k6xUJclckAOrypdK6VuF0P2fzaBTPoXqVCu8YuZt3HYqOvkQC/pAhgDc3pGO+Ah1wG7D9h4AAf8GKOYpUK8gq4aOHTG5vKIv7pVeFweb3CgMB1rogL6xeI6bQFG1pFaNXYSl7hOjXhOHrLAVTgc5nD5lhgE3ClWKRNZ+igFxwgO4JFaGoOLRWU5GS7wsgncf/m1cC8KYEFB2SJF9gHbh4umQiV0xRhrQbtLFPDLIhUMw2JTKsi1VSrA5HqFYk+Us69xp53poDToABDqHK6rPZIAcYd0nWk//mfuaK+fCfj2Ys9HRpnFqjVo0NZwJIRe1WoqtaVUHXrLNXWpWpM1P7EWhm9TE8tl/C5odcLdn0DVzhEVJb8Ge0m//i8BpzAGmBpxTWgVzNQShfqeRGoV7hD6UIq1qpUrRaE+vl/998j7/N6+Kh/W8+sb//ePH0fjj+mTCvy2kXMu8pUt3ZbAvRJByvARGrdnyn9BCg95euT8OynH1PzG9V8sq9IDzPpzWkntp8qkWje+MuJ1voRYfFgmGjiJa2gKqt1PFPiOQuqJtqelX0BS+yBfJ3EbLxd03mmEKDvuGgWAPZzku5nQWZa7mdx+pjA7bzDqYQaKPoVkx2ygXvJxU/wKgcGF85ZfyFtCvmLL+zZzdA6MlbbjxnqOzKLbnaBw2qA+YzDj4HDcuDquDisBjp7xeG9f8bhkXBoiVeOgEOp7zypSBQ6C/jMbzPz6TYrLYkgq/OAYzGx6fwLErLhEwcigoswKEw2R2lxrlnDdJaDzXfeS3zzX3YzUqaaKLhZ5x/fbPJ3OYMvLqyVXoijgGtOrTFKQLCApGEeubvNJq8RBwF0AaFuRmEEMqnyVx+ZeZvjsWmRx9SyBZ4MlL+VYeMyCMAmV41bzbX9pNZh2o+ab27f+vQiGUEG1HRO3hH3qbcrHfQqKAvYdhDBmPNgSHK8lqsjee2CLGn9JXadxwBReGjXDAYUUbfMc4TOp7qWJOr0wMioqAIVJ6BMhx5ynETbmEMKMk+16vCUFUeuXlX8NxGCJA7Bcx34UAb5pAEZ1Q2ppk40rhfvhH0pHoHn8xCSQZn72oCUIhXgSfChA8JlGnaAvnPJ8kzYoFbQT0ruEPu1zWzXPUNpBSbQTL3YREK3FYaqNKSa4+aG2qI6Rd5PLdU11++I6qom44UP3+79ORUWtWxidkqYzhFU9+mn56I0x+PdZKSMVG1iFOQ2VFuhph65aXy63JSjI9sFYcjs8BwjqcdkJFMpxsLUccmY3pWRLGtLQy0xkllyVkU/dePaUr8jRqrG873IJRf/ANYRW09tmECqVsSM2MZvxyASjRp9cZDY+202uZNEVk25g/4bsl/o1ZcAwtjNpV4uNaOX7IauX6y71DAX69fOJnpj1KL51WjlAMIGEdKhIn8xiNN2Yy0NSfxk16Y/bjyk0czcIyQyKRqMqsVVJB8TEdsZhc2KrvIJNO3oC239jG8z/OEaERY0YdzDb5OwyZTyWnKfRU3YzSZ3U46ZFFyOWlnnAyiNCRqnEkUxS9EK6mt0s5Ab8n5qF/Lm+t0s5Jr+c6D95OxKvZQIpRvjTlCkTuX91DrOzfWLKJK43Wbzr6qxlluDYzWZ64XaEBczzHAAg9OzLzP07mhganWpnAcZmKZasjC1VizMabHR0vvdWZh6Vf4S+w16iI31N4aM2khsmwYj8u0AejCeoRXG1JRygW/DcESUGzgj9z4/qjRgEuuql7NV2ptVOpzqBQUwFatilaa7cgWrtKudOtFZv+t0wQJsyUQ9mRXbNA6MBJUXyUpDNUt/ayR5lFBgtiE7Sm/5lqw5OGBHtgcbcKvHIY6adA29CmKs3cyqvXczSi6E6Kd2N6O5fjcuhy5L8ayJ0YjdjOd8WGb77i3LLeavsK2Rh6+Pnw5ePX86A1NvLYKpjPSpKTcF36kQw3FpO69Dk/IoSS5drJwiuNNGbKeWM7eGc/rizHKyizUuNXHocl1paG9PvW2IGjvF1dOotRM7AwrwnYTb6N//ipXbpRMOgzi1L0iq3v5+GQ5KUe1eIusisP+U3CZjzRd+FTVZVs07enzHq9yN/FpYadjYLg5fLc6+1r6+Vjkca8pOqsp2ANo4rSBXx50yy3JQF2kY+ytJ8uaBunCG3ntPPpW29MfKjtDTO4Ne1TZ+wOD8/YuTOCg3KQUQxuMqWvo9+2zKmKqElH/6ablmPTr0yJyuTCTClTBBC4dl5aLVaheh8zGR012ZtFZ45sSOiYjvQp36JnHPKRG5iOh0UIiIWmb6vJVDKl372hOriDhDnY4U7TB3W9e1YluaMlJ0bZr+eafzXderVe61BPsOIpkisJDjaAe6MDblxY4149wAe7G17yAbhsVnwHGo18kKPbBaxbloZZYNoIdfWZMutQSz5tzELhSZax+XcltyBiaTUuKE7PNmY9mx+c6sO9mHsCreJAkoDuYxmNgSHruRdBJzSAkpIcbPKcMmO8txtIgyELBrj0Sd8dS2c2nu6FwaLeBJ+lGdqkXJMiioJxe5RLrAn9K2x867GRKx1cqIKvmovJfb1ezXH/vO6V0A2e+Mzfcdg0lXfC35/NseqTA8XeXSZjbDHu/d+2E0nyMb0fl9QD8i5CCy2SdaenD4NrdkDmkZ8BiJ8PrUQWFDATwDJ11IFRFoq+uiJvcGpN8dphCL/CzWLh6MCCbAvcK+cxvabFjO/rE/kGYM/YhgFCej75d6Xt+0i+0X6KSjtl2AvFhJNIUl0yV2h21HXhRHH8O6Hs5LwNYvcjXTi64XyUWTfIFLmxrVJUBVpx2x0FE8ubqz/Ko5OcRN2n4oTeP3AgsDTbfiP4M9EkpKTtf+Xw8Q8aKtaShmK17dvi6TMS4ZvNPmU/5b6r/bxZLitWoCJ+7Q6Z8068RgMUuJieqkIf5Yk/NTpCSli0QMqSinx6CezrlChJeKsSWLyumwlIyWWKf97Em9ePDdMErIO/QcbaWh1iJCqrSfTulK8lGSyGenDKHz9JF5q5zM0+jknhhviZj+Txn97iNgvZ2yWmcjyyriyTj0VH+FJcZmD9FqU5EPv/ZoYHP9jrhsp4jFk4hOA4/7yjzqWD7+3WPyWTziXGz8nFDTk8NbPs1tiF2ZPk5zy0G8z4GyBMt/sCj6FxZC7yWy1RjJiSP3I3dVG6Opf3cW2S+Q5N89K0DvER9jIkkjkSqAtrcC0Nvsv2tLSD/77/D02/8D
\ No newline at end of file
diff --git a/docs/drawio/moveQuoteToken.drawio b/docs/drawio/moveQuoteToken.drawio
new file mode 100644
index 000000000..5ec0cbef3
--- /dev/null
+++ b/docs/drawio/moveQuoteToken.drawio
@@ -0,0 +1 @@
+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
\ No newline at end of file
diff --git a/docs/drawio/poolContractsArchitecture.drawio b/docs/drawio/poolContractsArchitecture.drawio
new file mode 100644
index 000000000..6000897e3
--- /dev/null
+++ b/docs/drawio/poolContractsArchitecture.drawio
@@ -0,0 +1 @@
+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
\ No newline at end of file
diff --git a/docs/drawio/removeCollateral.drawio b/docs/drawio/removeCollateral.drawio
new file mode 100644
index 000000000..b1f8199cb
--- /dev/null
+++ b/docs/drawio/removeCollateral.drawio
@@ -0,0 +1 @@
+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
\ No newline at end of file
diff --git a/docs/drawio/removeQuoteToken.drawio b/docs/drawio/removeQuoteToken.drawio
new file mode 100644
index 000000000..dfe6768ca
--- /dev/null
+++ b/docs/drawio/removeQuoteToken.drawio
@@ -0,0 +1 @@
+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
\ No newline at end of file
diff --git a/docs/drawio/repayDebt.drawio b/docs/drawio/repayDebt.drawio
new file mode 100644
index 000000000..a9a221166
--- /dev/null
+++ b/docs/drawio/repayDebt.drawio
@@ -0,0 +1 @@
+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
\ No newline at end of file
diff --git a/docs/drawio/settle.drawio b/docs/drawio/settle.drawio
new file mode 100644
index 000000000..30d3e9999
--- /dev/null
+++ b/docs/drawio/settle.drawio
@@ -0,0 +1 @@
+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
\ No newline at end of file
diff --git a/docs/drawio/take.drawio b/docs/drawio/take.drawio
new file mode 100644
index 000000000..f8a6de366
--- /dev/null
+++ b/docs/drawio/take.drawio
@@ -0,0 +1 @@
+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
\ No newline at end of file
diff --git a/docs/html/addCollateral.html b/docs/html/addCollateral.html
new file mode 100644
index 000000000..2e0301cd4
--- /dev/null
+++ b/docs/html/addCollateral.html
@@ -0,0 +1,11 @@
+
+
+
+
+addCollateral
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/html/addQuoteToken.html b/docs/html/addQuoteToken.html
new file mode 100644
index 000000000..11fd15bda
--- /dev/null
+++ b/docs/html/addQuoteToken.html
@@ -0,0 +1,11 @@
+
+
+
+
+addQuoteToken
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/html/bucketTake.html b/docs/html/bucketTake.html
new file mode 100644
index 000000000..ff4d63be4
--- /dev/null
+++ b/docs/html/bucketTake.html
@@ -0,0 +1,11 @@
+
+
+
+
+bucketTake
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/html/components.html b/docs/html/components.html
new file mode 100644
index 000000000..08bd8ff7a
--- /dev/null
+++ b/docs/html/components.html
@@ -0,0 +1,11 @@
+
+
+
+
+components
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/html/drawDebt.html b/docs/html/drawDebt.html
new file mode 100644
index 000000000..1e57c7099
--- /dev/null
+++ b/docs/html/drawDebt.html
@@ -0,0 +1,11 @@
+
+
+
+
+drawDebt
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/html/kick.html b/docs/html/kick.html
new file mode 100644
index 000000000..af55df35c
--- /dev/null
+++ b/docs/html/kick.html
@@ -0,0 +1,11 @@
+
+
+
+
+kick
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/html/kickWithDeposit.html b/docs/html/kickWithDeposit.html
new file mode 100644
index 000000000..45b15fa7b
--- /dev/null
+++ b/docs/html/kickWithDeposit.html
@@ -0,0 +1,11 @@
+
+
+
+
+kickWithDeposit
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/html/moveQuoteToken.html b/docs/html/moveQuoteToken.html
new file mode 100644
index 000000000..4df0727f6
--- /dev/null
+++ b/docs/html/moveQuoteToken.html
@@ -0,0 +1,11 @@
+
+
+
+
+moveQuoteToken
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/html/poolContractsArchitecture.html b/docs/html/poolContractsArchitecture.html
new file mode 100644
index 000000000..4bd8403d6
--- /dev/null
+++ b/docs/html/poolContractsArchitecture.html
@@ -0,0 +1,11 @@
+
+
+
+
+Untitled Diagram-Page-1
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/html/removeCollateral.html b/docs/html/removeCollateral.html
new file mode 100644
index 000000000..b7622c8af
--- /dev/null
+++ b/docs/html/removeCollateral.html
@@ -0,0 +1,11 @@
+
+
+
+
+removeCollateral
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/html/removeQuoteToken.html b/docs/html/removeQuoteToken.html
new file mode 100644
index 000000000..5c04479b9
--- /dev/null
+++ b/docs/html/removeQuoteToken.html
@@ -0,0 +1,11 @@
+
+
+
+
+removeQuoteToken
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/html/repayDebt.html b/docs/html/repayDebt.html
new file mode 100644
index 000000000..02d7c2d78
--- /dev/null
+++ b/docs/html/repayDebt.html
@@ -0,0 +1,11 @@
+
+
+
+
+repayDebt
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/html/settle.html b/docs/html/settle.html
new file mode 100644
index 000000000..e8fedd42e
--- /dev/null
+++ b/docs/html/settle.html
@@ -0,0 +1,11 @@
+
+
+
+
+addCollateral
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/html/take.html b/docs/html/take.html
new file mode 100644
index 000000000..308899a7d
--- /dev/null
+++ b/docs/html/take.html
@@ -0,0 +1,11 @@
+
+
+
+
+take
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/jpeg/ajnaContractsArchitecture.jpeg b/docs/jpeg/ajnaContractsArchitecture.jpeg
new file mode 100644
index 000000000..7a4ae3937
Binary files /dev/null and b/docs/jpeg/ajnaContractsArchitecture.jpeg differ
diff --git a/docs/jpeg/poolContract.jpeg b/docs/jpeg/poolContract.jpeg
new file mode 100644
index 000000000..5612d316d
Binary files /dev/null and b/docs/jpeg/poolContract.jpeg differ
diff --git a/docs/jpeg/poolContractsArchitecture.jpeg b/docs/jpeg/poolContractsArchitecture.jpeg
new file mode 100644
index 000000000..03048a0b3
Binary files /dev/null and b/docs/jpeg/poolContractsArchitecture.jpeg differ
diff --git a/foundry.toml b/foundry.toml
index 55b2b54cc..c1b3e82d2 100644
--- a/foundry.toml
+++ b/foundry.toml
@@ -24,3 +24,9 @@ optimizer_runs = 200
[fuzz]
runs = 300
+
+[invariant]
+runs = 10 # The number of calls to make in the invariant tests
+depth = 100 # The number of times to run the invariant tests
+call_override = false # Override calls
+fail_on_revert = false # Fail the test if the contract reverts
\ No newline at end of file
diff --git a/src/ERC20Pool.sol b/src/ERC20Pool.sol
index 4f90e78c8..f139b8ce5 100644
--- a/src/ERC20Pool.sol
+++ b/src/ERC20Pool.sol
@@ -15,8 +15,7 @@ import { IERC20Taker } from './interfaces/pool/erc20/IERC20Taker.sol';
import {
IPoolLenderActions,
- IPoolLiquidationActions,
- IERC20Token
+ IPoolLiquidationActions
} from './interfaces/pool/IPool.sol';
import {
IERC3156FlashBorrower,
@@ -39,7 +38,10 @@ import {
_roundToScale,
_roundUpToScale
} from './libraries/helpers/PoolHelper.sol';
-import { _revertIfAuctionClearable } from './libraries/helpers/RevertsHelper.sol';
+import {
+ _revertIfAuctionClearable,
+ _revertOnExpiry
+} from './libraries/helpers/RevertsHelper.sol';
import { Loans } from './libraries/internal/Loans.sol';
import { Deposits } from './libraries/internal/Deposits.sol';
@@ -165,7 +167,7 @@ contract ERC20Pool is FlashloanablePool, IERC20Pool {
if (amountToBorrow_ != 0) {
// update pool balances state
- poolBalances.t0Debt += result.t0DebtChange;
+ poolBalances.t0Debt = result.t0PoolDebt;
// move borrowed amount from pool to sender
_transferQuoteToken(msg.sender, amountToBorrow_);
@@ -184,7 +186,9 @@ contract ERC20Pool is FlashloanablePool, IERC20Pool {
function repayDebt(
address borrowerAddress_,
uint256 maxQuoteTokenAmountToRepay_,
- uint256 collateralAmountToPull_
+ uint256 collateralAmountToPull_,
+ address collateralReceiver_,
+ uint256 limitIndex_
) external nonReentrant {
PoolState memory poolState = _accruePoolInterest();
@@ -200,7 +204,8 @@ contract ERC20Pool is FlashloanablePool, IERC20Pool {
poolState,
borrowerAddress_,
maxQuoteTokenAmountToRepay_,
- collateralAmountToPull_
+ collateralAmountToPull_,
+ limitIndex_
);
emit RepayDebt(borrowerAddress_, result.quoteTokenToRepay, collateralAmountToPull_, result.newLup);
@@ -212,7 +217,7 @@ contract ERC20Pool is FlashloanablePool, IERC20Pool {
if (result.quoteTokenToRepay != 0) {
// update pool balances state
- poolBalances.t0Debt -= result.t0RepaidDebt;
+ poolBalances.t0Debt = result.t0PoolDebt;
if (result.t0DebtInAuctionChange != 0) {
poolBalances.t0DebtInAuction -= result.t0DebtInAuctionChange;
}
@@ -224,52 +229,8 @@ contract ERC20Pool is FlashloanablePool, IERC20Pool {
// update pool balances state
poolBalances.pledgedCollateral = result.poolCollateral;
- // move collateral from pool to sender
- _transferCollateral(msg.sender, collateralAmountToPull_);
- }
- }
-
- /************************************/
- /*** Flashloan External Functions ***/
- /************************************/
-
- /// @inheritdoc FlashloanablePool
- function flashLoan(
- IERC3156FlashBorrower receiver_,
- address token_,
- uint256 amount_,
- bytes calldata data_
- ) external override(IERC3156FlashLender, FlashloanablePool) nonReentrant returns (bool) {
- if (token_ == _getArgAddress(QUOTE_ADDRESS)) return _flashLoanQuoteToken(receiver_, token_, amount_, data_);
-
- if (token_ == _getArgAddress(COLLATERAL_ADDRESS)) {
- _transferCollateral(address(receiver_), amount_);
-
- if (receiver_.onFlashLoan(msg.sender, token_, amount_, 0, data_) !=
- keccak256("ERC3156FlashBorrower.onFlashLoan")) revert FlashloanCallbackFailed();
-
- _transferCollateralFrom(address(receiver_), amount_);
- return true;
- }
-
- revert FlashloanUnavailableForToken();
- }
-
- /// @inheritdoc FlashloanablePool
- function flashFee(
- address token_,
- uint256
- ) external pure override(IERC3156FlashLender, FlashloanablePool) returns (uint256) {
- if (token_ == _getArgAddress(QUOTE_ADDRESS) || token_ == _getArgAddress(COLLATERAL_ADDRESS)) return 0;
- revert FlashloanUnavailableForToken();
- }
-
- /// @inheritdoc FlashloanablePool
- function maxFlashLoan(
- address token_
- ) external view override(IERC3156FlashLender, FlashloanablePool) returns (uint256 maxLoan_) {
- if (token_ == _getArgAddress(QUOTE_ADDRESS) || token_ == _getArgAddress(COLLATERAL_ADDRESS)) {
- maxLoan_ = IERC20Token(token_).balanceOf(address(this));
+ // move collateral from pool to address specified as collateral receiver
+ _transferCollateral(collateralReceiver_, collateralAmountToPull_);
}
}
@@ -286,8 +247,10 @@ contract ERC20Pool is FlashloanablePool, IERC20Pool {
*/
function addCollateral(
uint256 amountToAdd_,
- uint256 index_
+ uint256 index_,
+ uint256 expiry_
) external override nonReentrant returns (uint256 bucketLPs_) {
+ _revertOnExpiry(expiry_);
PoolState memory poolState = _accruePoolInterest();
// revert if the dust amount was not exceeded, but round on the scale amount
@@ -359,12 +322,11 @@ contract ERC20Pool is FlashloanablePool, IERC20Pool {
) external override nonReentrant {
PoolState memory poolState = _accruePoolInterest();
- uint256 assets = Maths.wmul(poolBalances.t0Debt, poolState.inflator) + _getPoolQuoteTokenBalance();
+ uint256 assets = Maths.wmul(poolBalances.t0Debt, poolState.inflator) + _getNormalizedPoolQuoteTokenBalance();
uint256 liabilities = Deposits.treeSum(deposits) + auctions.totalBondEscrowed + reserveAuction.unclaimed;
(
- ,
,
uint256 collateralSettled,
uint256 t0DebtSettled
@@ -427,18 +389,11 @@ contract ERC20Pool is FlashloanablePool, IERC20Pool {
result.quoteTokenAmount = _roundUpToScale(result.quoteTokenAmount, _getArgUint256(QUOTE_SCALE));
// update pool balances state
- uint256 t0PoolDebt = poolBalances.t0Debt;
uint256 t0DebtInAuction = poolBalances.t0DebtInAuction;
-
- if (result.t0DebtPenalty != 0) {
- t0PoolDebt += result.t0DebtPenalty;
- t0DebtInAuction += result.t0DebtPenalty;
- }
-
- t0PoolDebt -= result.t0RepayAmount;
+ t0DebtInAuction += result.t0DebtPenalty;
t0DebtInAuction -= result.t0DebtInAuctionChange;
- poolBalances.t0Debt = t0PoolDebt;
+ poolBalances.t0Debt = result.t0PoolDebt;
poolBalances.t0DebtInAuction = t0DebtInAuction;
poolBalances.pledgedCollateral -= result.collateralAmount;
@@ -457,7 +412,7 @@ contract ERC20Pool is FlashloanablePool, IERC20Pool {
);
}
- _transferQuoteTokenFrom(callee_, result.quoteTokenAmount);
+ _transferQuoteTokenFrom(msg.sender, result.quoteTokenAmount);
}
/**
@@ -488,18 +443,11 @@ contract ERC20Pool is FlashloanablePool, IERC20Pool {
);
// update pool balances state
- uint256 t0PoolDebt = poolBalances.t0Debt;
uint256 t0DebtInAuction = poolBalances.t0DebtInAuction;
-
- if (result.t0DebtPenalty != 0) {
- t0PoolDebt += result.t0DebtPenalty;
- t0DebtInAuction += result.t0DebtPenalty;
- }
-
- t0PoolDebt -= result.t0RepayAmount;
+ t0DebtInAuction += result.t0DebtPenalty;
t0DebtInAuction -= result.t0DebtInAuctionChange;
- poolBalances.t0Debt = t0PoolDebt;
+ poolBalances.t0Debt = result.t0PoolDebt;
poolBalances.t0DebtInAuction = t0DebtInAuction;
poolBalances.pledgedCollateral -= result.collateralAmount;
@@ -509,6 +457,20 @@ contract ERC20Pool is FlashloanablePool, IERC20Pool {
_updateInterestState(poolState, result.newLup);
}
+ /***************************/
+ /*** Flashloan Functions ***/
+ /***************************/
+
+ /**
+ * @inheritdoc FlashloanablePool
+ * @dev Override default implementation and allows flashloans for both quote and collateral token.
+ */
+ function _isFlashloanSupported(
+ address token_
+ ) internal virtual view override returns (bool) {
+ return token_ == _getArgAddress(QUOTE_ADDRESS) || token_ == _getArgAddress(COLLATERAL_ADDRESS);
+ }
+
/************************/
/*** Helper Functions ***/
/************************/
diff --git a/src/ERC20PoolFactory.sol b/src/ERC20PoolFactory.sol
index c6ed65b41..6098ea682 100644
--- a/src/ERC20PoolFactory.sol
+++ b/src/ERC20PoolFactory.sol
@@ -4,8 +4,8 @@ pragma solidity 0.8.14;
import { ClonesWithImmutableArgs } from '@clones/ClonesWithImmutableArgs.sol';
-import { IERC20PoolFactory } from './interfaces/pool/erc20/IERC20PoolFactory.sol';
-
+import { IERC20PoolFactory } from './interfaces/pool/erc20/IERC20PoolFactory.sol';
+import { IPoolFactory } from './interfaces/pool/IPoolFactory.sol';
import { IERC20Token, PoolType } from './interfaces/pool/IPool.sol';
import { ERC20Pool } from './ERC20Pool.sol';
@@ -49,7 +49,9 @@ contract ERC20PoolFactory is PoolDeployer, IERC20PoolFactory {
*/
function deployPool(
address collateral_, address quote_, uint256 interestRate_
- ) external canDeploy(ERC20_NON_SUBSET_HASH, collateral_, quote_, interestRate_) returns (address pool_) {
+ ) external canDeploy(collateral_, quote_, interestRate_) returns (address pool_) {
+ if (deployedPools[ERC20_NON_SUBSET_HASH][collateral_][quote_] != address(0)) revert IPoolFactory.PoolAlreadyExists();
+
uint256 quoteTokenScale = 10 ** (18 - IERC20Token(quote_).decimals());
uint256 collateralScale = 10 ** (18 - IERC20Token(collateral_).decimals());
diff --git a/src/ERC721Pool.sol b/src/ERC721Pool.sol
index b1bf36b9e..386055912 100644
--- a/src/ERC721Pool.sol
+++ b/src/ERC721Pool.sol
@@ -24,15 +24,13 @@ import {
IERC721PoolLenderActions
} from './interfaces/pool/erc721/IERC721Pool.sol';
import { IERC721Taker } from './interfaces/pool/erc721/IERC721Taker.sol';
-import {
- ICryptoPunks,
- ICryptoKitties,
- NFTTypes
-} from './interfaces/pool/erc721/IERC721NonStandard.sol';
import { FlashloanablePool } from './base/FlashloanablePool.sol';
-import { _revertIfAuctionClearable } from './libraries/helpers/RevertsHelper.sol';
+import {
+ _revertIfAuctionClearable,
+ _revertOnExpiry
+} from './libraries/helpers/RevertsHelper.sol';
import { Maths } from './libraries/internal/Maths.sol';
import { Deposits } from './libraries/internal/Deposits.sol';
@@ -64,7 +62,6 @@ contract ERC721Pool is FlashloanablePool, IERC721Pool {
// immutable args offset
uint256 internal constant SUBSET = 93;
- uint256 internal constant NFT_TYPE = 125;
/***********************/
/*** State Variables ***/
@@ -163,7 +160,7 @@ contract ERC721Pool is FlashloanablePool, IERC721Pool {
if (result.t0DebtInAuctionChange != 0) {
poolBalances.t0DebtInAuction -= result.t0DebtInAuctionChange;
}
- poolBalances.pledgedCollateral += Maths.wad(tokenIdsToPledge_.length);
+ poolBalances.pledgedCollateral = result.poolCollateral;
// move collateral from sender to pool
_transferFromSenderToPool(borrowerTokenIds[borrowerAddress_], tokenIdsToPledge_);
@@ -174,7 +171,7 @@ contract ERC721Pool is FlashloanablePool, IERC721Pool {
// move borrowed amount from pool to sender
if (amountToBorrow_ != 0) {
// update pool balances state
- poolBalances.t0Debt += result.t0DebtChange;
+ poolBalances.t0Debt = result.t0PoolDebt;
// move borrowed amount from pool to sender
_transferQuoteToken(msg.sender, amountToBorrow_);
@@ -187,14 +184,16 @@ contract ERC721Pool is FlashloanablePool, IERC721Pool {
* - decrement poolBalances.t0Debt accumulator
* - decrement poolBalances.t0DebtInAuction accumulator
* - decrement poolBalances.pledgedCollateral accumulator
- * - update borrowerTokenIds arrays
+ * - update borrowerTokenIds and bucketTokenIds arrays
* @dev emit events:
* - RepayDebt
*/
function repayDebt(
address borrowerAddress_,
uint256 maxQuoteTokenAmountToRepay_,
- uint256 noOfNFTsToPull_
+ uint256 noOfNFTsToPull_,
+ address collateralReceiver_,
+ uint256 limitIndex_
) external nonReentrant {
PoolState memory poolState = _accruePoolInterest();
@@ -206,7 +205,8 @@ contract ERC721Pool is FlashloanablePool, IERC721Pool {
poolState,
borrowerAddress_,
maxQuoteTokenAmountToRepay_,
- Maths.wad(noOfNFTsToPull_)
+ Maths.wad(noOfNFTsToPull_),
+ limitIndex_
);
emit RepayDebt(borrowerAddress_, result.quoteTokenToRepay, noOfNFTsToPull_, result.newLup);
@@ -218,9 +218,12 @@ contract ERC721Pool is FlashloanablePool, IERC721Pool {
poolState.collateral = result.poolCollateral;
_updateInterestState(poolState, result.newLup);
+ // update pool balances state
+ poolBalances.pledgedCollateral = result.poolCollateral;
+
if (result.quoteTokenToRepay != 0) {
// update pool balances state
- poolBalances.t0Debt -= result.t0RepaidDebt;
+ poolBalances.t0Debt = result.t0PoolDebt;
if (result.t0DebtInAuctionChange != 0) {
poolBalances.t0DebtInAuction -= result.t0DebtInAuctionChange;
}
@@ -229,11 +232,8 @@ contract ERC721Pool is FlashloanablePool, IERC721Pool {
_transferQuoteTokenFrom(msg.sender, result.quoteTokenToRepay);
}
if (noOfNFTsToPull_ != 0) {
- // update pool balances state
- poolBalances.pledgedCollateral = result.poolCollateral;
-
- // move collateral from pool to sender
- _transferFromPoolToAddress(msg.sender, borrowerTokenIds[msg.sender], noOfNFTsToPull_);
+ // move collateral from pool to address specified as collateral receiver
+ _transferFromPoolToAddress(collateralReceiver_, borrowerTokenIds[msg.sender], noOfNFTsToPull_);
}
}
@@ -244,14 +244,16 @@ contract ERC721Pool is FlashloanablePool, IERC721Pool {
/**
* @inheritdoc IERC721PoolLenderActions
* @dev write state:
- * - update borrowerTokenIds arrays
+ * - update bucketTokenIds arrays
* @dev emit events:
* - AddCollateralNFT
*/
function addCollateral(
uint256[] calldata tokenIdsToAdd_,
- uint256 index_
+ uint256 index_,
+ uint256 expiry_
) external override nonReentrant returns (uint256 bucketLPs_) {
+ _revertOnExpiry(expiry_);
PoolState memory poolState = _accruePoolInterest();
bucketLPs_ = LenderActions.addCollateral(
@@ -282,6 +284,8 @@ contract ERC721Pool is FlashloanablePool, IERC721Pool {
uint256 noOfNFTsToRemove_,
uint256 toIndex_
) external override nonReentrant returns (uint256 collateralMerged_, uint256 bucketLPs_) {
+ _revertIfAuctionClearable(auctions, loans);
+
PoolState memory poolState = _accruePoolInterest();
uint256 collateralAmount = Maths.wad(noOfNFTsToRemove_);
@@ -356,7 +360,8 @@ contract ERC721Pool is FlashloanablePool, IERC721Pool {
) external nonReentrant override {
PoolState memory poolState = _accruePoolInterest();
- uint256 assets = Maths.wmul(poolBalances.t0Debt, poolState.inflator) + _getPoolQuoteTokenBalance();
+ uint256 assets = Maths.wmul(poolBalances.t0Debt, poolState.inflator) + _getNormalizedPoolQuoteTokenBalance();
+
uint256 liabilities = Deposits.treeSum(deposits) + auctions.totalBondEscrowed + reserveAuction.unclaimed;
SettleParams memory params = SettleParams(
@@ -370,7 +375,6 @@ contract ERC721Pool is FlashloanablePool, IERC721Pool {
);
(
uint256 collateralRemaining,
- uint256 t0DebtRemaining,
uint256 collateralSettled,
uint256 t0DebtSettled
) = Auctions.settlePoolDebt(
@@ -381,8 +385,7 @@ contract ERC721Pool is FlashloanablePool, IERC721Pool {
params
);
- // slither-disable-next-line incorrect-equality
- if (t0DebtRemaining == 0) _rebalanceTokens(params.borrower, collateralRemaining);
+ if (collateralSettled > 0) _rebalanceTokens(params.borrower, collateralRemaining);
// update pool balances state
poolBalances.t0Debt -= t0DebtSettled;
@@ -422,24 +425,20 @@ contract ERC721Pool is FlashloanablePool, IERC721Pool {
);
// update pool balances state
- uint256 t0PoolDebt = poolBalances.t0Debt;
uint256 t0DebtInAuction = poolBalances.t0DebtInAuction;
-
- if (result.t0DebtPenalty != 0) {
- t0PoolDebt += result.t0DebtPenalty;
- t0DebtInAuction += result.t0DebtPenalty;
- }
-
- t0PoolDebt -= result.t0RepayAmount;
+ t0DebtInAuction += result.t0DebtPenalty;
t0DebtInAuction -= result.t0DebtInAuctionChange;
- poolBalances.t0Debt = t0PoolDebt;
- poolBalances.t0DebtInAuction = t0DebtInAuction;
- poolBalances.pledgedCollateral -= result.collateralAmount;
+ poolBalances.t0Debt = result.t0PoolDebt;
+ poolBalances.t0DebtInAuction = t0DebtInAuction;
+
+ // the total collateral taken from borrower pledged collateral (collateral taken plus collateral compensated if auction settled)
+ uint256 collateralSettled = result.collateralAmount + result.compensatedCollateral;
+ poolBalances.pledgedCollateral -= collateralSettled;
// update pool interest rate state
poolState.debt = result.poolDebt;
- poolState.collateral -= result.collateralAmount;
+ poolState.collateral -= collateralSettled;
_updateInterestState(poolState, result.newLup);
// transfer rounded collateral from pool to taker
@@ -449,10 +448,12 @@ contract ERC721Pool is FlashloanablePool, IERC721Pool {
result.collateralAmount / 1e18
);
+ uint256 totalQuoteTokenAmount = result.quoteTokenAmount + result.excessQuoteToken;
+
if (data_.length != 0) {
IERC721Taker(callee_).atomicSwapCallback(
tokensTaken,
- result.quoteTokenAmount / _getArgUint256(QUOTE_SCALE),
+ totalQuoteTokenAmount / _getArgUint256(QUOTE_SCALE),
data_
);
}
@@ -460,7 +461,7 @@ contract ERC721Pool is FlashloanablePool, IERC721Pool {
if (result.settledAuction) _rebalanceTokens(borrowerAddress_, result.remainingCollateral);
// transfer from taker to pool the amount of quote tokens needed to cover collateral auctioned (including excess for rounded collateral)
- _transferQuoteTokenFrom(callee_, result.quoteTokenAmount + result.excessQuoteToken);
+ _transferQuoteTokenFrom(msg.sender, totalQuoteTokenAmount);
// transfer from pool to borrower the excess of quote tokens after rounding collateral auctioned
if (result.excessQuoteToken != 0) _transferQuoteToken(borrowerAddress_, result.excessQuoteToken);
@@ -494,24 +495,20 @@ contract ERC721Pool is FlashloanablePool, IERC721Pool {
);
// update pool balances state
- uint256 t0PoolDebt = poolBalances.t0Debt;
uint256 t0DebtInAuction = poolBalances.t0DebtInAuction;
-
- if (result.t0DebtPenalty != 0) {
- t0PoolDebt += result.t0DebtPenalty;
- t0DebtInAuction += result.t0DebtPenalty;
- }
-
- t0PoolDebt -= result.t0RepayAmount;
+ t0DebtInAuction += result.t0DebtPenalty;
t0DebtInAuction -= result.t0DebtInAuctionChange;
- poolBalances.t0Debt = t0PoolDebt;
- poolBalances.t0DebtInAuction = t0DebtInAuction;
- poolBalances.pledgedCollateral -= result.collateralAmount;
+ poolBalances.t0Debt = result.t0PoolDebt;
+ poolBalances.t0DebtInAuction = t0DebtInAuction;
+
+ // the total collateral taken from borrower pledged collateral (collateral taken plus collateral compensated if auction settled)
+ uint256 collateralSettled = result.collateralAmount + result.compensatedCollateral;
+ poolBalances.pledgedCollateral -= collateralSettled;
// update pool interest rate state
poolState.debt = result.poolDebt;
- poolState.collateral -= result.collateralAmount;
+ poolState.collateral -= collateralSettled;
_updateInterestState(poolState, result.newLup);
if (result.settledAuction) _rebalanceTokens(borrowerAddress_, result.remainingCollateral);
@@ -560,22 +557,13 @@ contract ERC721Pool is FlashloanablePool, IERC721Pool {
uint256[] calldata tokenIds_
) internal {
bool subset = _getArgUint256(SUBSET) != 0;
- uint8 nftType = _getArgUint8(NFT_TYPE);
for (uint256 i = 0; i < tokenIds_.length;) {
uint256 tokenId = tokenIds_[i];
if (subset && !tokenIdsAllowed[tokenId]) revert OnlySubset();
poolTokens_.push(tokenId);
- if (nftType == uint8(NFTTypes.STANDARD_ERC721)){
- _transferNFT(msg.sender, address(this), tokenId);
- }
- else if (nftType == uint8(NFTTypes.CRYPTOKITTIES)) {
- ICryptoKitties(_getArgAddress(COLLATERAL_ADDRESS)).transferFrom(msg.sender ,address(this), tokenId);
- }
- else{
- ICryptoPunks(_getArgAddress(COLLATERAL_ADDRESS)).buyPunk(tokenId);
- }
+ _transferNFT(msg.sender, address(this), tokenId);
unchecked { ++i; }
}
@@ -598,21 +586,11 @@ contract ERC721Pool is FlashloanablePool, IERC721Pool {
uint256 noOfNFTsInPool = poolTokens_.length;
- uint8 nftType = _getArgUint8(NFT_TYPE);
-
for (uint256 i = 0; i < amountToRemove_;) {
uint256 tokenId = poolTokens_[--noOfNFTsInPool]; // start with transferring the last token added in bucket
poolTokens_.pop();
- if (nftType == uint8(NFTTypes.STANDARD_ERC721)){
- _transferNFT(address(this), toAddress_, tokenId);
- }
- else if (nftType == uint8(NFTTypes.CRYPTOKITTIES)) {
- ICryptoKitties(_getArgAddress(COLLATERAL_ADDRESS)).transfer(toAddress_, tokenId);
- }
- else {
- ICryptoPunks(_getArgAddress(COLLATERAL_ADDRESS)).transferPunk(toAddress_, tokenId);
- }
+ _transferNFT(address(this), toAddress_, tokenId);
tokensTransferred[i] = tokenId;
@@ -624,13 +602,14 @@ contract ERC721Pool is FlashloanablePool, IERC721Pool {
/**
* @dev Helper function to transfer an NFT from owner to target address (reused in code to reduce contract deployment bytecode size).
+ * @dev Since transferFrom is used instead of safeTransferFrom, calling smart contracts must be careful to check that they support any received NFTs.
* @param from_ NFT owner address.
* @param to_ New NFT owner address.
* @param tokenId_ NFT token id to be transferred.
*/
function _transferNFT(address from_, address to_, uint256 tokenId_) internal {
// slither-disable-next-line calls-loop
- IERC721Token(_getArgAddress(COLLATERAL_ADDRESS)).safeTransferFrom(from_, to_, tokenId_);
+ IERC721Token(_getArgAddress(COLLATERAL_ADDRESS)).transferFrom(from_, to_, tokenId_);
}
/************************/
diff --git a/src/ERC721PoolFactory.sol b/src/ERC721PoolFactory.sol
index 98301a5ce..ef1da0440 100644
--- a/src/ERC721PoolFactory.sol
+++ b/src/ERC721PoolFactory.sol
@@ -5,9 +5,8 @@ pragma solidity 0.8.14;
import { ClonesWithImmutableArgs } from '@clones/ClonesWithImmutableArgs.sol';
import { IERC165 } from '@openzeppelin/contracts/utils/introspection/IERC165.sol';
-import { IERC721PoolFactory } from './interfaces/pool/erc721/IERC721PoolFactory.sol';
-import { NFTTypes } from './interfaces/pool/erc721/IERC721NonStandard.sol';
-
+import { IERC721PoolFactory } from './interfaces/pool/erc721/IERC721PoolFactory.sol';
+import { IPoolFactory } from './interfaces/pool/IPoolFactory.sol';
import { IERC20Token, PoolType } from './interfaces/pool/IPool.sol';
import { ERC721Pool } from './ERC721Pool.sol';
@@ -53,29 +52,16 @@ contract ERC721PoolFactory is PoolDeployer, IERC721PoolFactory {
*/
function deployPool(
address collateral_, address quote_, uint256[] memory tokenIds_, uint256 interestRate_
- ) external canDeploy(getNFTSubsetHash(tokenIds_), collateral_, quote_, interestRate_) returns (address pool_) {
- uint256 quoteTokenScale = 10**(18 - IERC20Token(quote_).decimals());
+ ) external canDeploy(collateral_, quote_, interestRate_) returns (address pool_) {
+ bytes32 subsetHash = getNFTSubsetHash(tokenIds_);
+ if (deployedPools[subsetHash][collateral_][quote_] != address(0)) revert IPoolFactory.PoolAlreadyExists();
- NFTTypes nftType;
- // CryptoPunks NFTs
- if (collateral_ == 0xb47e3cd837dDF8e4c57F05d70Ab865de6e193BBB ) {
- nftType = NFTTypes.CRYPTOPUNKS;
- }
- // CryptoKitties and CryptoFighters NFTs
- else if (collateral_ == 0x06012c8cf97BEaD5deAe237070F9587f8E7A266d || collateral_ == 0x87d598064c736dd0C712D329aFCFAA0Ccc1921A1) {
- nftType = NFTTypes.CRYPTOKITTIES;
- }
- // All other NFTs that support the EIP721 standard
- else {
- // Here 0x80ac58cd is the ERC721 interface Id
- // Neither a standard NFT nor a non-standard supported NFT(punk, kitty or fighter)
- try IERC165(collateral_).supportsInterface(0x80ac58cd) returns (bool supportsERC721Interface) {
- if (!supportsERC721Interface) revert NFTNotSupported();
- } catch {
- revert NFTNotSupported();
- }
+ uint256 quoteTokenScale = 10**(18 - IERC20Token(quote_).decimals());
- nftType = NFTTypes.STANDARD_ERC721;
+ try IERC165(collateral_).supportsInterface(0x80ac58cd) returns (bool supportsERC721Interface) {
+ if (!supportsERC721Interface) revert NFTNotSupported();
+ } catch {
+ revert NFTNotSupported();
}
bytes memory data = abi.encodePacked(
@@ -84,8 +70,7 @@ contract ERC721PoolFactory is PoolDeployer, IERC721PoolFactory {
collateral_,
quote_,
quoteTokenScale,
- tokenIds_.length,
- nftType
+ tokenIds_.length
);
ERC721Pool pool = ERC721Pool(address(implementation).clone(data));
@@ -93,7 +78,7 @@ contract ERC721PoolFactory is PoolDeployer, IERC721PoolFactory {
pool_ = address(pool);
// Track the newly deployed pool
- deployedPools[getNFTSubsetHash(tokenIds_)][collateral_][quote_] = pool_;
+ deployedPools[subsetHash][collateral_][quote_] = pool_;
deployedPoolsList.push(pool_);
emit PoolCreated(pool_);
@@ -105,8 +90,36 @@ contract ERC721PoolFactory is PoolDeployer, IERC721PoolFactory {
/*** Pool Creation Functions ***/
/*******************************/
+ /**
+ * @notice Get the hash of the subset of NFTs that will be used to create the pool
+ * @dev If no tokenIds are provided, the default ERC721_NON_SUBSET_HASH is returned
+ * @param tokenIds_ The array of token ids that will be used to create the pool
+ * @return bytes32 The hash of the subset of NFTs that will be used to create the pool
+ */
function getNFTSubsetHash(uint256[] memory tokenIds_) public pure returns (bytes32) {
if (tokenIds_.length == 0) return ERC721_NON_SUBSET_HASH;
- else return keccak256(abi.encodePacked(tokenIds_));
+ else {
+ // check the array of token ids is sorted in ascending order
+ // revert if not sorted
+ _checkTokenIdSortOrder(tokenIds_);
+
+ // hash the sorted array of tokenIds
+ return keccak256(abi.encode(tokenIds_));
+ }
}
+
+ /**
+ * @notice Check that the array of token ids is sorted in ascending order, else revert.
+ * @dev The counters are modified in unchecked blocks due to being bounded by array length
+ * @param tokenIds_ The array of token ids to check for sorting
+ */
+ function _checkTokenIdSortOrder(uint256[] memory tokenIds_) internal pure {
+ for (uint256 i = 0; i < tokenIds_.length - 1; ) {
+ if (tokenIds_[i] >= tokenIds_[i + 1]) revert TokenIdSubsetInvalid();
+ unchecked {
+ ++i;
+ }
+ }
+ }
+
}
diff --git a/src/PoolInfoUtils.sol b/src/PoolInfoUtils.sol
index 807bf57e9..f395952d6 100644
--- a/src/PoolInfoUtils.sol
+++ b/src/PoolInfoUtils.sol
@@ -58,13 +58,13 @@ contract PoolInfoUtils {
/**
* @notice Get a bucket struct for a given index.
- * @param index_ The index of the bucket to retrieve.
- * @return price_ Bucket price (WAD)
- * @return quoteTokens_ Amount of quote token in bucket, deposit + interest (WAD)
- * @return collateral_ Unencumbered collateral in bucket (WAD).
- * @return bucketLPs_ Outstanding LP balance in bucket (WAD)
- * @return scale_ Lender interest multiplier (WAD).
- * @return exchangeRate_ The exchange rate of the bucket, in RAY units.
+ * @param index_ The index of the bucket to retrieve.
+ * @return price_ Bucket price (WAD)
+ * @return quoteTokens_ Amount of quote token in bucket, deposit + interest (WAD)
+ * @return collateral_ Unencumbered collateral in bucket (WAD).
+ * @return bucketLPs_ Outstanding LP balance in bucket (WAD)
+ * @return scale_ Lender interest multiplier (WAD).
+ * @return exchangeRate_ The exchange rate of the bucket, in WAD units.
*/
function bucketInfo(address ajnaPool_, uint256 index_)
external
@@ -83,12 +83,7 @@ contract PoolInfoUtils {
price_ = _priceAt(index_);
(bucketLPs_, collateral_, , quoteTokens_, scale_) = pool.bucketInfo(index_);
- if (bucketLPs_ == 0) {
- exchangeRate_ = Maths.RAY;
- } else {
- uint256 bucketSize = quoteTokens_ * 1e18 + price_ * collateral_; // 10^36 + // 10^36
- exchangeRate_ = bucketSize * 1e18 / bucketLPs_; // 10^27
- }
+ exchangeRate_ = Buckets.getExchangeRate(collateral_, bucketLPs_, quoteTokens_, price_);
}
/**
@@ -187,7 +182,7 @@ contract PoolInfoUtils {
(,uint256 poolDebt,) = pool.debtInfo();
uint256 poolSize = pool.depositSize();
- uint256 quoteTokenBalance = IERC20Token(pool.quoteTokenAddress()).balanceOf(ajnaPool_);
+ uint256 quoteTokenBalance = IERC20Token(pool.quoteTokenAddress()).balanceOf(ajnaPool_) * pool.quoteTokenScale();
(uint256 bondEscrowed, uint256 unclaimedReserve, uint256 auctionKickTime) = pool.reservesInfo();
@@ -352,13 +347,13 @@ contract PoolInfoUtils {
/**
* @notice Calculate the amount of quote tokens in bucket for a given amount of LP Tokens.
- * @param lpTokens_ The number of lpTokens to calculate amounts for.
+ * @param lps_ The number of LPs to calculate amounts for.
* @param index_ The price bucket index for which the value should be calculated.
* @return quoteAmount_ The exact amount of quote tokens that can be exchanged for the given LP Tokens, WAD units.
*/
function lpsToQuoteTokens(
address ajnaPool_,
- uint256 lpTokens_,
+ uint256 lps_,
uint256 index_
) external view returns (uint256 quoteAmount_) {
IPool pool = IPool(ajnaPool_);
@@ -367,7 +362,7 @@ contract PoolInfoUtils {
bucketLPs_,
bucketCollateral,
bucketDeposit,
- lpTokens_,
+ lps_,
bucketDeposit,
_priceAt(index_)
);
@@ -375,13 +370,13 @@ contract PoolInfoUtils {
/**
* @notice Calculate the amount of collateral tokens in bucket for a given amount of LP Tokens.
- * @param lpTokens_ The number of lpTokens to calculate amounts for.
+ * @param lps_ The number of LPs to calculate amounts for.
* @param index_ The price bucket index for which the value should be calculated.
* @return collateralAmount_ The exact amount of collateral tokens that can be exchanged for the given LP Tokens, WAD units.
*/
function lpsToCollateral(
address ajnaPool_,
- uint256 lpTokens_,
+ uint256 lps_,
uint256 index_
) external view returns (uint256 collateralAmount_) {
IPool pool = IPool(ajnaPool_);
@@ -390,7 +385,7 @@ contract PoolInfoUtils {
bucketCollateral,
bucketLPs_,
bucketDeposit,
- lpTokens_,
+ lps_,
_priceAt(index_)
);
}
diff --git a/src/PositionManager.sol b/src/PositionManager.sol
index f23de3393..d1caa295b 100644
--- a/src/PositionManager.sol
+++ b/src/PositionManager.sol
@@ -165,7 +165,7 @@ contract PositionManager is ERC721, PermitERC721, IPositionManager, Multicall, R
emit MemorializePosition(owner, params_.tokenId);
- // update pool lp token accounting and transfer ownership of lp tokens to PositionManager contract
+ // update pool lps accounting and transfer ownership of lps to PositionManager contract
pool.transferLPs(owner, address(this), params_.indexes);
}
@@ -180,7 +180,7 @@ contract PositionManager is ERC721, PermitERC721, IPositionManager, Multicall, R
*/
function mint(
MintParams calldata params_
- ) external override returns (uint256 tokenId_) {
+ ) external override nonReentrant returns (uint256 tokenId_) {
tokenId_ = _nextId++;
// revert if the address is not a valid Ajna pool
@@ -252,7 +252,8 @@ contract PositionManager is ERC721, PermitERC721, IPositionManager, Multicall, R
) = IPool(params_.pool).moveQuoteToken(
maxQuote,
params_.fromIndex,
- params_.toIndex
+ params_.toIndex,
+ params_.expiry
);
// update position LPs state
@@ -307,7 +308,7 @@ contract PositionManager is ERC721, PermitERC721, IPositionManager, Multicall, R
emit RedeemPosition(owner, params_.tokenId);
- // update pool lp token accounting and transfer ownership of lp tokens from PositionManager contract
+ // update pool lps accounting and transfer ownership of lps from PositionManager contract
pool.transferLPs(address(this), owner, params_.indexes);
}
@@ -350,7 +351,7 @@ contract PositionManager is ERC721, PermitERC721, IPositionManager, Multicall, R
/**********************/
/// @inheritdoc IPositionManagerDerivedState
- function getLPTokens(
+ function getLPs(
uint256 tokenId_,
uint256 index_
) external override view returns (uint256) {
diff --git a/src/RewardsManager.sol b/src/RewardsManager.sol
index 0e0ab2d38..bd406c5a6 100644
--- a/src/RewardsManager.sol
+++ b/src/RewardsManager.sol
@@ -107,11 +107,13 @@ contract RewardsManager is IRewardsManager {
uint256 tokenId_,
uint256 epochToClaim_
) external override {
- if (msg.sender != stakes[tokenId_].owner) revert NotOwnerOfDeposit();
+ StakeInfo storage stakeInfo = stakes[tokenId_];
+
+ if (msg.sender != stakeInfo.owner) revert NotOwnerOfDeposit();
if (isEpochClaimed[tokenId_][epochToClaim_]) revert AlreadyClaimed();
- _claimRewards(tokenId_, epochToClaim_);
+ _claimRewards(stakeInfo, tokenId_, epochToClaim_, true, stakeInfo.ajnaPool);
}
/**
@@ -149,8 +151,8 @@ contract RewardsManager is IRewardsManager {
BucketState storage bucketState = stakeInfo.snapshot[bucketId];
- // record the number of lp tokens in bucket at the time of staking
- bucketState.lpsAtStakeTime = positionManager.getLPTokens(
+ // record the number of lps in bucket at the time of staking
+ bucketState.lpsAtStakeTime = positionManager.getLPs(
tokenId_,
bucketId
);
@@ -173,7 +175,7 @@ contract RewardsManager is IRewardsManager {
);
// transfer rewards to sender
- IERC20(ajnaToken).safeTransfer(msg.sender, updateReward);
+ _transferAjnaRewards(updateReward);
}
/**
@@ -187,13 +189,30 @@ contract RewardsManager is IRewardsManager {
function unstake(
uint256 tokenId_
) external override {
- if (msg.sender != stakes[tokenId_].owner) revert NotOwnerOfDeposit();
+ StakeInfo storage stakeInfo = stakes[tokenId_];
- address ajnaPool = stakes[tokenId_].ajnaPool;
+ if (msg.sender != stakeInfo.owner) revert NotOwnerOfDeposit();
+
+ address ajnaPool = stakeInfo.ajnaPool;
// claim rewards, if any
- _claimRewards(tokenId_, IPool(ajnaPool).currentBurnEpoch());
+ _claimRewards(
+ stakeInfo,
+ tokenId_,
+ IPool(ajnaPool).currentBurnEpoch(),
+ false,
+ ajnaPool
+ );
+
+ // remove bucket snapshots recorded at the time of staking
+ uint256[] memory positionIndexes = positionManager.getPositionIndexes(tokenId_);
+ for (uint256 i = 0; i < positionIndexes.length; ) {
+ delete stakeInfo.snapshot[positionIndexes[i]]; // reset BucketState struct for current position
+ unchecked { ++i; }
+ }
+
+ // remove recorded stake info
delete stakes[tokenId_];
emit Unstake(msg.sender, ajnaPool, tokenId_);
@@ -214,7 +233,7 @@ contract RewardsManager is IRewardsManager {
updateReward = _updateBucketExchangeRates(pool_, indexes_);
// transfer rewards to sender
- IERC20(ajnaToken).safeTransfer(msg.sender, updateReward);
+ _transferAjnaRewards(updateReward);
}
/*******************************/
@@ -255,7 +274,19 @@ contract RewardsManager is IRewardsManager {
return (
stakes[tokenId_].owner,
stakes[tokenId_].ajnaPool,
- stakes[tokenId_].lastInteractionBurnEpoch);
+ stakes[tokenId_].lastInteractionBurnEpoch
+ );
+ }
+
+ /// @inheritdoc IRewardsManagerState
+ function getBucketStateStakeInfo(
+ uint256 tokenId_,
+ uint256 bucketId_
+ ) external view override returns (uint256, uint256) {
+ return (
+ stakes[tokenId_].snapshot[bucketId_].lpsAtStakeTime,
+ stakes[tokenId_].snapshot[bucketId_].rateAtStakeTime
+ );
}
/**************************/
@@ -291,15 +322,13 @@ contract RewardsManager is IRewardsManager {
positionIndexes
);
- uint256 nextEpoch = epoch + 1;
-
- // update epoch token claim trackers
- rewardsClaimed[nextEpoch] += nextEpochRewards;
- isEpochClaimed[tokenId_][nextEpoch] = true;
-
rewards_ += nextEpochRewards;
unchecked { ++epoch; }
+
+ // update epoch token claim trackers
+ rewardsClaimed[epoch] += nextEpochRewards;
+ isEpochClaimed[tokenId_][epoch] = true;
}
}
@@ -391,7 +420,7 @@ contract RewardsManager is IRewardsManager {
// calculate the equivalent amount of quote tokens given the stakes lp balance,
// and the exchange rate at the next and current burn events
- interestEarned_ = Maths.rayToWad(Maths.rmul(nextExchangeRate - exchangeRate_, bucketLPs));
+ interestEarned_ = Maths.wmul(nextExchangeRate - exchangeRate_, bucketLPs);
}
}
@@ -423,8 +452,9 @@ contract RewardsManager is IRewardsManager {
// calculate rewards earned
newRewards_ = Maths.wmul(
REWARD_FACTOR,
- Maths.wmul(
- Maths.wdiv(interestEarned_, totalInterestEarnedInPeriod), totalBurnedInPeriod
+ Maths.wdiv(
+ Maths.wmul(interestEarned_, totalBurnedInPeriod),
+ totalInterestEarnedInPeriod
)
);
@@ -440,47 +470,49 @@ contract RewardsManager is IRewardsManager {
/**
* @notice Claim rewards that have been accumulated by a staked NFT.
- * @param tokenId_ ID of the staked LP NFT.
- * @param epochToClaim_ The burn epoch to claim rewards for (rewards calculation starts from the last claimed epoch)
+ * @param stakeInfo_ Details of stake to claim rewards for.
+ * @param tokenId_ ID of the staked LP NFT.
+ * @param epochToClaim_ The burn epoch to claim rewards for (rewards calculation starts from the last claimed epoch)
+ * @param validateEpoch_ True if the epoch is received as a parameter and needs to be validated (lower or equal with latest epoch).
+ * @param ajnaPool_ Address of ajna pool associated with the stake.
*/
function _claimRewards(
+ StakeInfo storage stakeInfo_,
uint256 tokenId_,
- uint256 epochToClaim_
+ uint256 epochToClaim_,
+ bool validateEpoch_,
+ address ajnaPool_
) internal {
- StakeInfo storage stakeInfo = stakes[tokenId_];
- address ajnaPool = stakeInfo.ajnaPool;
+ // revert if higher epoch to claim than current burn epoch
+ if (validateEpoch_ && epochToClaim_ > IPool(ajnaPool_).currentBurnEpoch()) revert EpochNotAvailable();
// update bucket exchange rates and claim associated rewards
uint256 rewardsEarned = _updateBucketExchangeRates(
- ajnaPool,
+ ajnaPool_,
positionManager.getPositionIndexes(tokenId_)
);
rewardsEarned += _calculateAndClaimRewards(tokenId_, epochToClaim_);
uint256[] memory burnEpochsClaimed = _getBurnEpochsClaimed(
- stakeInfo.lastInteractionBurnEpoch,
+ stakeInfo_.lastInteractionBurnEpoch,
epochToClaim_
);
emit ClaimRewards(
msg.sender,
- ajnaPool,
+ ajnaPool_,
tokenId_,
burnEpochsClaimed,
rewardsEarned
);
// update last interaction burn event
- stakeInfo.lastInteractionBurnEpoch = uint96(epochToClaim_);
-
- uint256 ajnaBalance = IERC20(ajnaToken).balanceOf(address(this));
-
- if (rewardsEarned > ajnaBalance) rewardsEarned = ajnaBalance;
+ stakeInfo_.lastInteractionBurnEpoch = uint96(epochToClaim_);
// transfer rewards to sender
- IERC20(ajnaToken).safeTransfer(msg.sender, rewardsEarned);
+ _transferAjnaRewards(rewardsEarned);
}
/**
@@ -688,6 +720,20 @@ contract RewardsManager is IRewardsManager {
}
}
+ /** @notice Utility method to transfer Ajna rewards to the sender
+ * @dev This method is used to transfer rewards to the sender after a successful claim or update.
+ * @dev It is used to ensure that rewards claimers will be able to claim some portion of the remaining tokens if a claim would exceed the remaining contract balance.
+ * @param rewardsEarned_ Amount of rewards earned by the caller.
+ */
+ function _transferAjnaRewards(uint256 rewardsEarned_) internal {
+ // check that rewards earned isn't greater than remaining balance
+ // if remaining balance is greater, set to remaining balance
+ uint256 ajnaBalance = IERC20(ajnaToken).balanceOf(address(this));
+ if (rewardsEarned_ > ajnaBalance) rewardsEarned_ = ajnaBalance;
+ // transfer rewards to sender
+ IERC20(ajnaToken).safeTransfer(msg.sender, rewardsEarned_);
+ }
+
/************************/
/*** Helper Functions ***/
/************************/
diff --git a/src/base/FlashloanablePool.sol b/src/base/FlashloanablePool.sol
index 1beba296d..22e70da69 100644
--- a/src/base/FlashloanablePool.sol
+++ b/src/base/FlashloanablePool.sol
@@ -2,6 +2,9 @@
pragma solidity 0.8.14;
+import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
+import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
+
import { Pool } from './Pool.sol';
import { IERC3156FlashBorrower } from '../interfaces/pool/IERC3156FlashBorrower.sol';
@@ -12,36 +15,45 @@ import { IERC3156FlashBorrower } from '../interfaces/pool/IERC3156FlashBorrower.
* @notice Flash loans can be taking in ERC20 quote and ERC20 collateral tokens.
*/
abstract contract FlashloanablePool is Pool {
+ using SafeERC20 for IERC20;
+
/**
* @notice Called by flashloan borrowers to borrow liquidity which must be repaid in the same transaction.
* @param receiver_ Address of the contract which implements the appropriate interface to receive tokens.
* @param token_ Address of the ERC20 token caller wants to borrow.
* @param amount_ The amount of tokens to borrow.
* @param data_ User-defined calldata passed to the receiver.
- * @return True if successful.
+ * @return success_ True if flashloan was successful.
*/
function flashLoan(
IERC3156FlashBorrower receiver_,
address token_,
uint256 amount_,
bytes calldata data_
- ) external virtual override nonReentrant returns (bool) {
- if (token_ == _getArgAddress(QUOTE_ADDRESS)) return _flashLoanQuoteToken(receiver_, token_, amount_, data_);
- revert FlashloanUnavailableForToken();
- }
+ ) external virtual override nonReentrant returns (bool success_) {
+ if (!_isFlashloanSupported(token_)) revert FlashloanUnavailableForToken();
+
+ IERC20 tokenContract = IERC20(token_);
+
+ uint256 initialBalance = tokenContract.balanceOf(address(this));
+
+ tokenContract.safeTransfer(
+ address(receiver_),
+ amount_
+ );
- function _flashLoanQuoteToken(IERC3156FlashBorrower receiver_,
- address token_,
- uint256 amount_,
- bytes calldata data_
- ) internal returns (bool) {
- _transferQuoteToken(address(receiver_), amount_);
-
if (receiver_.onFlashLoan(msg.sender, token_, amount_, 0, data_) !=
keccak256("ERC3156FlashBorrower.onFlashLoan")) revert FlashloanCallbackFailed();
- _transferQuoteTokenFrom(address(receiver_), amount_);
- return true;
+ tokenContract.safeTransferFrom(
+ address(receiver_),
+ address(this),
+ amount_
+ );
+
+ if (tokenContract.balanceOf(address(this)) != initialBalance) revert FlashloanIncorrectBalance();
+
+ success_ = true;
}
/**
@@ -51,7 +63,7 @@ abstract contract FlashloanablePool is Pool {
address token_,
uint256
) external virtual view override returns (uint256) {
- if (token_ != _getArgAddress(QUOTE_ADDRESS)) revert FlashloanUnavailableForToken();
+ if (!_isFlashloanSupported(token_)) revert FlashloanUnavailableForToken();
return 0;
}
@@ -63,6 +75,18 @@ abstract contract FlashloanablePool is Pool {
function maxFlashLoan(
address token_
) external virtual view override returns (uint256 maxLoan_) {
- if (token_ == _getArgAddress(QUOTE_ADDRESS)) maxLoan_ = _getPoolQuoteTokenBalance();
+ if (_isFlashloanSupported(token_)) maxLoan_ = IERC20(token_).balanceOf(address(this));
+ }
+
+ /**
+ * @notice Returns true if pool allows flashloans for given token address, false otherwise.
+ * @dev Allows flashloans for quote token, overriden in pool implementation to allow flashloans for other tokens.
+ * @param token_ Address of the ERC20 token to be lent.
+ * @return True if token can be flashloaned, false otherwise.
+ */
+ function _isFlashloanSupported(
+ address token_
+ ) internal virtual view returns (bool) {
+ return token_ == _getArgAddress(QUOTE_ADDRESS);
}
}
\ No newline at end of file
diff --git a/src/base/PermitERC721.sol b/src/base/PermitERC721.sol
index a8f3a87af..41084f410 100644
--- a/src/base/PermitERC721.sol
+++ b/src/base/PermitERC721.sol
@@ -109,7 +109,6 @@ abstract contract PermitERC721 is ERC721, IPermit {
* @notice Called by an NFT owner to enable their NFT to be transferred by a spender address without making a seperate approve call
* @param from_ The address of the current owner of the NFT
* @param to_ The address of the new owner of the NFT
- * @param spender_ The address of the third party who will execute the transaction involving an owners NFT
* @param tokenId_ The id of the NFT being interacted with
* @param deadline_ The unix timestamp by which the permit must be called
* @param v_ Component of secp256k1 signature
@@ -117,9 +116,9 @@ abstract contract PermitERC721 is ERC721, IPermit {
* @param s_ Component of secp256k1 signature
*/
function safeTransferFromWithPermit(
- address from_, address to_, address spender_, uint256 tokenId_, uint256 deadline_, uint8 v_, bytes32 r_, bytes32 s_
+ address from_, address to_, uint256 tokenId_, uint256 deadline_, uint8 v_, bytes32 r_, bytes32 s_
) external {
- this.permit(spender_, tokenId_, deadline_, v_, r_, s_);
+ this.permit(msg.sender, tokenId_, deadline_, v_, r_, s_);
safeTransferFrom(from_, to_, tokenId_);
}
diff --git a/src/base/Pool.sol b/src/base/Pool.sol
index f51c77f5f..36a7a7e84 100644
--- a/src/base/Pool.sol
+++ b/src/base/Pool.sol
@@ -45,7 +45,8 @@ import {
} from '../libraries/helpers/PoolHelper.sol';
import {
_revertIfAuctionDebtLocked,
- _revertIfAuctionClearable
+ _revertIfAuctionClearable,
+ _revertOnExpiry
} from '../libraries/helpers/RevertsHelper.sol';
import { Buckets } from '../libraries/internal/Buckets.sol';
@@ -93,7 +94,7 @@ abstract contract Pool is Clone, ReentrancyGuard, Multicall, IPool {
bool internal isPoolInitialized;
- mapping(address => mapping(address => mapping(uint256 => uint256))) private _lpTokenAllowances; // owner address -> new owner address -> deposit index -> allowed amount
+ mapping(address => mapping(address => mapping(uint256 => uint256))) private _lpAllowances; // owner address -> new owner address -> deposit index -> allowed amount
/******************/
/*** Immutables ***/
@@ -131,8 +132,10 @@ abstract contract Pool is Clone, ReentrancyGuard, Multicall, IPool {
/// @inheritdoc IPoolLenderActions
function addQuoteToken(
uint256 quoteTokenAmountToAdd_,
- uint256 index_
+ uint256 index_,
+ uint256 expiry_
) external override nonReentrant returns (uint256 bucketLPs_) {
+ _revertOnExpiry(expiry_);
PoolState memory poolState = _accruePoolInterest();
// round to token precision
@@ -159,22 +162,24 @@ abstract contract Pool is Clone, ReentrancyGuard, Multicall, IPool {
/**
* @inheritdoc IPoolLenderActions
* @dev write state:
- * - _lpTokenAllowances mapping
+ * - _lpAllowances mapping
*/
function approveLpOwnership(
- address allowedNewOwner_,
+ address newOwner,
uint256 index_,
- uint256 lpsAmountToApprove_
+ uint256 amount_
) external nonReentrant {
- _lpTokenAllowances[msg.sender][allowedNewOwner_][index_] = lpsAmountToApprove_;
+ _lpAllowances[msg.sender][newOwner][index_] = amount_;
}
/// @inheritdoc IPoolLenderActions
function moveQuoteToken(
uint256 maxAmountToMove_,
uint256 fromIndex_,
- uint256 toIndex_
+ uint256 toIndex_,
+ uint256 expiry_
) external override nonReentrant returns (uint256 fromBucketLPs_, uint256 toBucketLPs_) {
+ _revertOnExpiry(expiry_);
PoolState memory poolState = _accruePoolInterest();
_revertIfAuctionDebtLocked(deposits, poolBalances, fromIndex_, poolState.inflator);
@@ -242,7 +247,7 @@ abstract contract Pool is Clone, ReentrancyGuard, Multicall, IPool {
) external override nonReentrant {
LenderActions.transferLPs(
buckets,
- _lpTokenAllowances,
+ _lpAllowances,
owner_,
newOwner_,
indexes_
@@ -273,11 +278,11 @@ abstract contract Pool is Clone, ReentrancyGuard, Multicall, IPool {
);
// update pool balances state
+ poolBalances.t0Debt = result.t0PoolDebt;
poolBalances.t0DebtInAuction += result.t0KickedDebt;
- poolBalances.t0Debt += result.t0KickPenalty;
// update pool interest rate state
- poolState.debt += result.kickPenalty;
+ poolState.debt = Maths.wmul(result.t0PoolDebt, poolState.inflator);
_updateInterestState(poolState, result.lup);
if(result.amountToCoverBond != 0) _transferQuoteTokenFrom(msg.sender, result.amountToCoverBond);
@@ -304,11 +309,11 @@ abstract contract Pool is Clone, ReentrancyGuard, Multicall, IPool {
);
// update pool balances state
- poolBalances.t0Debt += result.t0KickPenalty;
+ poolBalances.t0Debt = result.t0PoolDebt;
poolBalances.t0DebtInAuction += result.t0KickedDebt;
// update pool interest rate state
- poolState.debt += result.kickPenalty;
+ poolState.debt = Maths.wmul(result.t0PoolDebt, poolState.inflator);
_updateInterestState(poolState, result.lup);
// transfer from kicker to pool the difference to cover bond
@@ -320,10 +325,10 @@ abstract contract Pool is Clone, ReentrancyGuard, Multicall, IPool {
* @dev write state:
* - reset kicker's claimable accumulator
*/
- function withdrawBonds() external {
+ function withdrawBonds(address recipient_) external {
uint256 claimable = auctions.kickers[msg.sender].claimable;
auctions.kickers[msg.sender].claimable = 0;
- _transferQuoteToken(msg.sender, claimable);
+ _transferQuoteToken(recipient_, claimable);
}
/*********************************/
@@ -356,8 +361,8 @@ abstract contract Pool is Clone, ReentrancyGuard, Multicall, IPool {
reserveAuction,
StartReserveAuctionParams({
poolSize: Deposits.treeSum(deposits),
- poolDebt: poolBalances.t0Debt,
- poolBalance: _getPoolQuoteTokenBalance(),
+ t0PoolDebt: poolBalances.t0Debt,
+ poolBalance: _getNormalizedPoolQuoteTokenBalance(),
inflator: inflatorState.inflator
})
);
@@ -424,10 +429,7 @@ abstract contract Pool is Clone, ReentrancyGuard, Multicall, IPool {
* @return poolState_ Struct containing pool details.
*/
function _accruePoolInterest() internal returns (PoolState memory poolState_) {
- // retrieve t0Debt amount from poolBalances struct
- uint256 t0Debt = poolBalances.t0Debt;
-
- // initialize fields of poolState_ struct with initial values
+ poolState_.t0Debt = poolBalances.t0Debt;
poolState_.collateral = poolBalances.pledgedCollateral;
poolState_.inflator = inflatorState.inflator;
poolState_.rate = interestState.interestRate;
@@ -435,9 +437,9 @@ abstract contract Pool is Clone, ReentrancyGuard, Multicall, IPool {
poolState_.quoteDustLimit = _getArgUint256(QUOTE_SCALE);
// check if t0Debt is not equal to 0, indicating that there is debt to be tracked for the pool
- if (t0Debt != 0) {
+ if (poolState_.t0Debt != 0) {
// Calculate prior pool debt
- poolState_.debt = Maths.wmul(t0Debt, poolState_.inflator);
+ poolState_.debt = Maths.wmul(poolState_.t0Debt, poolState_.inflator);
// calculate elapsed time since inflator was last updated
uint256 elapsed = block.timestamp - inflatorState.inflatorUpdate;
@@ -455,7 +457,7 @@ abstract contract Pool is Clone, ReentrancyGuard, Multicall, IPool {
);
poolState_.inflator = newInflator;
// After debt owed to lenders has accrued, calculate current debt owed by borrowers
- poolState_.debt = Maths.wmul(t0Debt, poolState_.inflator);
+ poolState_.debt = Maths.wmul(poolState_.t0Debt, poolState_.inflator);
// update total interest earned accumulator with the newly accrued interest
reserveAuction.totalInterestEarned += newInterest;
@@ -507,8 +509,11 @@ abstract contract Pool is Clone, ReentrancyGuard, Multicall, IPool {
IERC20(_getArgAddress(QUOTE_ADDRESS)).safeTransfer(to_, amount_ / _getArgUint256(QUOTE_SCALE));
}
- function _getPoolQuoteTokenBalance() internal view returns (uint256) {
- return IERC20(_getArgAddress(QUOTE_ADDRESS)).balanceOf(address(this));
+ /**
+ * @dev returns the pool quote token balance normalized to WAD to be used for calculating pool reserves
+ */
+ function _getNormalizedPoolQuoteTokenBalance() internal view returns (uint256) {
+ return IERC20(_getArgAddress(QUOTE_ADDRESS)).balanceOf(address(this)) * _getArgUint256(QUOTE_SCALE);
}
function _lup(uint256 debt_) internal view returns (uint256) {
@@ -710,4 +715,19 @@ abstract contract Pool is Clone, ReentrancyGuard, Multicall, IPool {
reserveAuction.kicked
);
}
+
+ /// @inheritdoc IPoolState
+ function totalAuctionsInPool() external view override returns (uint256) {
+ return auctions.noOfAuctions;
+ }
+
+ /// @inheritdoc IPoolState
+ function totalT0Debt() external view override returns (uint256) {
+ return poolBalances.t0Debt;
+ }
+
+ /// @inheritdoc IPoolState
+ function totalT0DebtInAuction() external view override returns (uint256) {
+ return poolBalances.t0DebtInAuction;
+ }
}
diff --git a/src/base/PoolDeployer.sol b/src/base/PoolDeployer.sol
index ffb433a4b..2ae7459c6 100644
--- a/src/base/PoolDeployer.sol
+++ b/src/base/PoolDeployer.sol
@@ -35,10 +35,9 @@ abstract contract PoolDeployer {
* @notice Ensures that pools are deployed according to specifications.
* @dev Used by both ERC20, and ERC721 pool factory types.
*/
- modifier canDeploy(bytes32 subsetHash_, address collateral_, address quote_, uint256 interestRate_) {
+ modifier canDeploy(address collateral_, address quote_, uint256 interestRate_) {
if (collateral_ == address(0) || quote_ == address(0)) revert IPoolFactory.DeployWithZeroAddress();
- if (deployedPools[subsetHash_][collateral_][quote_] != address(0)) revert IPoolFactory.PoolAlreadyExists();
- if (MIN_RATE >= interestRate_ || interestRate_ >= MAX_RATE) revert IPoolFactory.PoolInterestRateInvalid();
+ if (MIN_RATE > interestRate_ || interestRate_ > MAX_RATE) revert IPoolFactory.PoolInterestRateInvalid();
_;
}
diff --git a/src/interfaces/pool/IPool.sol b/src/interfaces/pool/IPool.sol
index 2a203fbe9..8ced68587 100644
--- a/src/interfaces/pool/IPool.sol
+++ b/src/interfaces/pool/IPool.sol
@@ -44,7 +44,7 @@ interface IERC20Token {
}
interface IERC721Token {
- function safeTransferFrom(
+ function transferFrom(
address from,
address to,
uint256 tokenId
diff --git a/src/interfaces/pool/commons/IPoolErrors.sol b/src/interfaces/pool/commons/IPoolErrors.sol
index 7641ee9d3..a0803f5f3 100644
--- a/src/interfaces/pool/commons/IPoolErrors.sol
+++ b/src/interfaces/pool/commons/IPoolErrors.sol
@@ -75,6 +75,11 @@ interface IPoolErrors {
*/
error FlashloanCallbackFailed();
+ /**
+ * @notice Balance of pool contract before flash loan is different than the balance after flash loan.
+ */
+ error FlashloanIncorrectBalance();
+
/**
* @notice Pool cannot facilitate a flashloan for the specified token address.
*/
@@ -105,7 +110,7 @@ interface IPoolErrors {
/**
* @notice Borrower is attempting to borrow more quote token than is available before the supplied limitIndex.
*/
- error LimitIndexReached();
+ error LimitIndexExceeded();
/**
* @notice When moving quote token HTP must stay below LUP.
@@ -179,6 +184,16 @@ interface IPoolErrors {
*/
error TakeNotPastCooldown();
+ /**
+ * @notice Current block timestamp has reached or exceeded a user-provided expiration.
+ */
+ error TransactionExpired();
+
+ /**
+ * @notice Owner of the LP tokens attemps to transfer LPs to same address.
+ */
+ error TransferToSameOwner();
+
/**
* @notice The threshold price of the loan to be inserted in loans heap is zero.
*/
diff --git a/src/interfaces/pool/commons/IPoolEvents.sol b/src/interfaces/pool/commons/IPoolEvents.sol
index f8c7cbf47..49927d9b6 100644
--- a/src/interfaces/pool/commons/IPoolEvents.sol
+++ b/src/interfaces/pool/commons/IPoolEvents.sol
@@ -211,13 +211,13 @@ interface IPoolEvents {
* @param owner The original owner address of the position.
* @param newOwner The new owner address of the position.
* @param indexes Array of price bucket indexes at which LP tokens were transferred.
- * @param lpTokens Amount of LP tokens transferred.
+ * @param lps Amount of LPs transferred.
*/
- event TransferLPTokens(
+ event TransferLPs(
address owner,
address newOwner,
uint256[] indexes,
- uint256 lpTokens
+ uint256 lps
);
/**
diff --git a/src/interfaces/pool/commons/IPoolInternals.sol b/src/interfaces/pool/commons/IPoolInternals.sol
index 197e4ecb2..576f3a721 100644
--- a/src/interfaces/pool/commons/IPoolInternals.sol
+++ b/src/interfaces/pool/commons/IPoolInternals.sol
@@ -11,22 +11,22 @@ pragma solidity 0.8.14;
/*****************************/
struct BucketTakeResult {
- uint256 collateralAmount;
- uint256 t0RepayAmount;
- uint256 t0DebtPenalty;
- uint256 remainingCollateral;
- uint256 poolDebt;
- uint256 newLup;
- uint256 t0DebtInAuctionChange;
- bool settledAuction;
+ uint256 collateralAmount; // [WAD] amount of collateral taken
+ uint256 compensatedCollateral; // [WAD] amount of borrower collateral that is compensated with LPs
+ uint256 t0DebtPenalty; // [WAD] t0 penalty applied on first take
+ uint256 remainingCollateral; // [WAD] amount of borrower collateral remaining after take
+ uint256 poolDebt; // [WAD] current pool debt
+ uint256 t0PoolDebt; // [WAD] t0 pool debt
+ uint256 newLup; // [WAD] current lup
+ uint256 t0DebtInAuctionChange; // [WAD] the amount of t0 debt recovered by take action
+ bool settledAuction; // true if auction is settled by take action
}
struct KickResult {
- uint256 amountToCoverBond; // amount of bond that needs to be covered
- uint256 kickPenalty; // kick penalty
- uint256 t0KickPenalty; // t0 kick penalty
- uint256 t0KickedDebt; // new t0 debt after kick
- uint256 lup; // current lup
+ uint256 amountToCoverBond; // [WAD] amount of bond that needs to be covered
+ uint256 t0PoolDebt; // [WAD] t0 debt in pool after kick
+ uint256 t0KickedDebt; // [WAD] new t0 debt after kick
+ uint256 lup; // [WAD] current lup
}
struct SettleParams {
@@ -38,16 +38,17 @@ struct SettleParams {
}
struct TakeResult {
- uint256 collateralAmount;
- uint256 quoteTokenAmount;
- uint256 t0RepayAmount;
- uint256 t0DebtPenalty;
- uint256 excessQuoteToken;
- uint256 remainingCollateral;
- uint256 poolDebt;
- uint256 newLup;
- uint256 t0DebtInAuctionChange;
- bool settledAuction;
+ uint256 collateralAmount; // [WAD] amount of collateral taken
+ uint256 compensatedCollateral; // [WAD] amount of borrower collateral that is compensated with LPs
+ uint256 quoteTokenAmount; // [WAD] amount of quote tokens paid by taker for taken collateral
+ uint256 t0DebtPenalty; // [WAD] t0 penalty applied on first take
+ uint256 excessQuoteToken; // [WAD] (NFT only) amount of quote tokens to be paid by taker to borrower for fractional collateral
+ uint256 remainingCollateral; // [WAD] amount of borrower collateral remaining after take
+ uint256 poolDebt; // [WAD] current pool debt
+ uint256 t0PoolDebt; // [WAD] t0 pool debt
+ uint256 newLup; // [WAD] current lup
+ uint256 t0DebtInAuctionChange; // [WAD] the amount of t0 debt recovered by take action
+ bool settledAuction; // true if auction is settled by take action
}
/******************************************/
@@ -83,7 +84,7 @@ struct DrawDebtResult {
uint256 remainingCollateral; // [WAD] amount of borrower collateral after draw debt (for NFT can be diminished if auction settled)
bool settledAuction; // true if collateral pledged settles auction
uint256 t0DebtInAuctionChange; // [WAD] change of t0 pool debt in auction after pledge collateral
- uint256 t0DebtChange; // [WAD] change of total t0 pool debt after after draw debt
+ uint256 t0PoolDebt; // [WAD] amount of t0 debt in pool after draw debt
}
struct RepayDebtResult {
@@ -93,6 +94,6 @@ struct RepayDebtResult {
uint256 remainingCollateral; // [WAD] amount of borrower collateral after pull collateral
bool settledAuction; // true if repay debt settles auction
uint256 t0DebtInAuctionChange; // [WAD] change of t0 pool debt in auction after repay debt
- uint256 t0RepaidDebt; // [WAD] amount of t0 repaid debt
+ uint256 t0PoolDebt; // [WAD] amount of t0 debt in pool after repay
uint256 quoteTokenToRepay; // [WAD] quote token amount to be transferred from sender to pool
}
\ No newline at end of file
diff --git a/src/interfaces/pool/commons/IPoolLenderActions.sol b/src/interfaces/pool/commons/IPoolLenderActions.sol
index 11546147f..d29172751 100644
--- a/src/interfaces/pool/commons/IPoolLenderActions.sol
+++ b/src/interfaces/pool/commons/IPoolLenderActions.sol
@@ -10,11 +10,13 @@ interface IPoolLenderActions {
* @notice Called by lenders to add an amount of credit at a specified price bucket.
* @param amount The amount of quote token to be added by a lender.
* @param index The index of the bucket to which the quote tokens will be added.
+ * @param expiry Timestamp after which this TX will revert, preventing inclusion in a block with unfavorable price.
* @return lpbChange The amount of LP Tokens changed for the added quote tokens.
*/
function addQuoteToken(
uint256 amount,
- uint256 index
+ uint256 index,
+ uint256 expiry
) external returns (uint256 lpbChange);
/**
@@ -35,13 +37,15 @@ interface IPoolLenderActions {
* @param maxAmount The maximum amount of quote token to be moved by a lender.
* @param fromIndex The bucket index from which the quote tokens will be removed.
* @param toIndex The bucket index to which the quote tokens will be added.
+ * @param expiry Timestamp after which this TX will revert, preventing inclusion in a block with unfavorable price.
* @return lpbAmountFrom The amount of LPs moved out from bucket.
* @return lpbAmountTo The amount of LPs moved to destination bucket.
*/
function moveQuoteToken(
uint256 maxAmount,
uint256 fromIndex,
- uint256 toIndex
+ uint256 toIndex,
+ uint256 expiry
) external returns (uint256 lpbAmountFrom, uint256 lpbAmountTo);
/**
diff --git a/src/interfaces/pool/commons/IPoolLiquidationActions.sol b/src/interfaces/pool/commons/IPoolLiquidationActions.sol
index 150738f95..8f20c9047 100644
--- a/src/interfaces/pool/commons/IPoolLiquidationActions.sol
+++ b/src/interfaces/pool/commons/IPoolLiquidationActions.sol
@@ -63,6 +63,7 @@ interface IPoolLiquidationActions {
/**
* @notice Called by kickers to withdraw their auction bonds (the amount of quote tokens that are not locked in active auctions).
+ * @param recipient Address to receive claimed bonds amount.
*/
- function withdrawBonds() external;
+ function withdrawBonds(address recipient) external;
}
\ No newline at end of file
diff --git a/src/interfaces/pool/commons/IPoolReserveAuctionActions.sol b/src/interfaces/pool/commons/IPoolReserveAuctionActions.sol
index 9e28f5fbe..6ceed46dc 100644
--- a/src/interfaces/pool/commons/IPoolReserveAuctionActions.sol
+++ b/src/interfaces/pool/commons/IPoolReserveAuctionActions.sol
@@ -27,7 +27,7 @@ interface IPoolReserveAuctionActions {
struct StartReserveAuctionParams {
uint256 poolSize; // [WAD] total deposits in pool (with accrued debt)
- uint256 poolDebt; // [WAD] current t0 pool debt
+ uint256 t0PoolDebt; // [WAD] current t0 pool debt
uint256 poolBalance; // [WAD] pool quote token balance
uint256 inflator; // [WAD] pool current inflator
}
\ No newline at end of file
diff --git a/src/interfaces/pool/commons/IPoolState.sol b/src/interfaces/pool/commons/IPoolState.sol
index d8123d53a..bfda7af77 100644
--- a/src/interfaces/pool/commons/IPoolState.sol
+++ b/src/interfaces/pool/commons/IPoolState.sol
@@ -222,6 +222,26 @@ interface IPoolState {
*/
function pledgedCollateral() external view returns (uint256);
+ /**
+ * @notice Returns the total number of active auctions in pool
+ * @return totalAuctions_ number of active auctions.
+ */
+ function totalAuctionsInPool() external view returns (uint256);
+
+ /**
+ * @notice Returns the `t0Debt` state variable.
+ * @dev This value should be multiplied by inflator in order to calculate current debt of the pool.
+ * @return The total t0Debt in the system, in WAD units.
+ */
+ function totalT0Debt() external view returns (uint256);
+
+ /**
+ * @notice Returns the `t0DebtInAuction` state variable.
+ * @dev This value should be multiplied by inflator in order to calculate current debt in auction of the pool.
+ * @return The total t0DebtInAuction in the system, in WAD units.
+ */
+ function totalT0DebtInAuction() external view returns (uint256);
+
}
/*********************/
@@ -250,6 +270,7 @@ struct PoolBalancesState {
struct PoolState {
uint8 poolType; // pool type, can be ERC20 or ERC721
+ uint256 t0Debt; // [WAD] t0 debt in pool
uint256 debt; // [WAD] total debt in pool, accrued in current block
uint256 collateral; // [WAD] total collateral pledged in pool
uint256 inflator; // [WAD] current pool inflator
@@ -261,12 +282,12 @@ struct PoolState {
/*** Buckets State ***/
struct Lender {
- uint256 lps; // [RAY] Lender LP accumulator
+ uint256 lps; // [WAD] Lender LP accumulator
uint256 depositTime; // timestamp of last deposit
}
struct Bucket {
- uint256 lps; // [RAY] Bucket LP accumulator
+ uint256 lps; // [WAD] Bucket LP accumulator
uint256 collateral; // [WAD] Available collateral tokens deposited in the bucket
uint256 bankruptcyTime; // Timestamp when bucket become insolvent, 0 if healthy
mapping(address => Lender) lenders; // lender address to Lender struct mapping
diff --git a/src/interfaces/pool/erc20/IERC20PoolBorrowerActions.sol b/src/interfaces/pool/erc20/IERC20PoolBorrowerActions.sol
index dbab099da..d92a95712 100644
--- a/src/interfaces/pool/erc20/IERC20PoolBorrowerActions.sol
+++ b/src/interfaces/pool/erc20/IERC20PoolBorrowerActions.sol
@@ -29,10 +29,14 @@ interface IERC20PoolBorrowerActions {
* @param borrowerAddress_ The borrower whose loan is being interacted with.
* @param maxQuoteTokenAmountToRepay_ The amount of quote tokens to repay.
* @param collateralAmountToPull_ The amount of collateral to be puled from the pool.
+ * @param recipient_ The address to receive amount of pulled collateral.
+ * @param limitIndex_ Ensures LUP has not moved far from state when borrower pulls collateral.
*/
function repayDebt(
address borrowerAddress_,
uint256 maxQuoteTokenAmountToRepay_,
- uint256 collateralAmountToPull_
+ uint256 collateralAmountToPull_,
+ address recipient_,
+ uint256 limitIndex_
) external;
}
diff --git a/src/interfaces/pool/erc20/IERC20PoolLenderActions.sol b/src/interfaces/pool/erc20/IERC20PoolLenderActions.sol
index b43ee2652..ce0831efb 100644
--- a/src/interfaces/pool/erc20/IERC20PoolLenderActions.sol
+++ b/src/interfaces/pool/erc20/IERC20PoolLenderActions.sol
@@ -9,11 +9,14 @@ interface IERC20PoolLenderActions {
/**
* @notice Deposit claimable collateral into a specified bucket.
- * @param amount Amount of collateral to deposit.
- * @param index The bucket index to which collateral will be deposited.
+ * @param amount Amount of collateral to deposit.
+ * @param index The bucket index to which collateral will be deposited.
+ * @param expiry Timestamp after which this TX will revert, preventing inclusion in a block with unfavorable price.
+ * @return lpbChange The amount of LP Tokens changed for the added collateral.
*/
function addCollateral(
uint256 amount,
- uint256 index
+ uint256 index,
+ uint256 expiry
) external returns (uint256 lpbChange);
}
\ No newline at end of file
diff --git a/src/interfaces/pool/erc721/IERC721NonStandard.sol b/src/interfaces/pool/erc721/IERC721NonStandard.sol
deleted file mode 100644
index eb66243bf..000000000
--- a/src/interfaces/pool/erc721/IERC721NonStandard.sol
+++ /dev/null
@@ -1,20 +0,0 @@
-// SPDX-License-Identifier: MIT
-
-pragma solidity 0.8.14;
-
-interface ICryptoKitties {
- function transferFrom(address from_, address to_, uint256 tokenId_) external;
- function transfer(address to_, uint256 tokenId_) external;
- function approve(address to_, uint256 tokenId_) external;
- function ownerOf(uint256 tokenId_) external returns(address);
- function kittyIndexToApproved(uint256 tokenId_) external returns(address);
-}
-
-interface ICryptoPunks {
- function buyPunk(uint punkIndex) external;
- function transferPunk(address to, uint punkIndex) external;
- function offerPunkForSaleToAddress(uint punkIndex, uint minSalePriceInWei, address toAddress) external;
- function punkIndexToAddress(uint punkIndex) external returns(address);
-}
-
-enum NFTTypes{ STANDARD_ERC721, CRYPTOPUNKS, CRYPTOKITTIES }
\ No newline at end of file
diff --git a/src/interfaces/pool/erc721/IERC721PoolBorrowerActions.sol b/src/interfaces/pool/erc721/IERC721PoolBorrowerActions.sol
index 5f8e36c95..9206c9b5e 100644
--- a/src/interfaces/pool/erc721/IERC721PoolBorrowerActions.sol
+++ b/src/interfaces/pool/erc721/IERC721PoolBorrowerActions.sol
@@ -29,10 +29,14 @@ interface IERC721PoolBorrowerActions {
* @param borrowerAddress_ The borrower whose loan is being interacted with.
* @param maxQuoteTokenAmountToRepay_ The amount of quote tokens to repay.
* @param noOfNFTsToPull_ The integer number of NFT collateral to be puled from the pool.
+ * @param recipient_ The address to receive amount of pulled collateral.
+ * @param limitIndex_ Ensures LUP has not moved far from state when borrower pulls collateral.
*/
function repayDebt(
address borrowerAddress_,
uint256 maxQuoteTokenAmountToRepay_,
- uint256 noOfNFTsToPull_
+ uint256 noOfNFTsToPull_,
+ address recipient_,
+ uint256 limitIndex_
) external;
}
diff --git a/src/interfaces/pool/erc721/IERC721PoolFactory.sol b/src/interfaces/pool/erc721/IERC721PoolFactory.sol
index 52bfde301..30d842cbf 100644
--- a/src/interfaces/pool/erc721/IERC721PoolFactory.sol
+++ b/src/interfaces/pool/erc721/IERC721PoolFactory.sol
@@ -10,6 +10,15 @@ import { IPoolFactory } from '../IPoolFactory.sol';
*/
interface IERC721PoolFactory is IPoolFactory {
+ /**************/
+ /*** Errors ***/
+ /**************/
+
+ /**
+ * @notice User tried to deploy a pool with an array of tokenIds that weren't sorted, or contained duplicates.
+ */
+ error TokenIdSubsetInvalid();
+
/**************************/
/*** External Functions ***/
/**************************/
diff --git a/src/interfaces/pool/erc721/IERC721PoolLenderActions.sol b/src/interfaces/pool/erc721/IERC721PoolLenderActions.sol
index 1c07f06ec..cf068b951 100644
--- a/src/interfaces/pool/erc721/IERC721PoolLenderActions.sol
+++ b/src/interfaces/pool/erc721/IERC721PoolLenderActions.sol
@@ -11,10 +11,13 @@ interface IERC721PoolLenderActions {
* @notice Deposit claimable collateral into a specified bucket.
* @param tokenIds Array of collateral to deposit.
* @param index The bucket index to which collateral will be deposited.
+ * @param expiry Timestamp after which this TX will revert, preventing inclusion in a block with unfavorable price.
+ * @return lpbChange The amount of LP Tokens changed for the added collateral.
*/
function addCollateral(
uint256[] calldata tokenIds,
- uint256 index
+ uint256 index,
+ uint256 expiry
) external returns (uint256);
/**
diff --git a/src/interfaces/position/IPositionManagerDerivedState.sol b/src/interfaces/position/IPositionManagerDerivedState.sol
index a7e7614a2..a2123b174 100644
--- a/src/interfaces/position/IPositionManagerDerivedState.sol
+++ b/src/interfaces/position/IPositionManagerDerivedState.sol
@@ -8,16 +8,16 @@ pragma solidity 0.8.14;
interface IPositionManagerDerivedState {
/**
- * @notice Returns the lpTokens accrued to a given tokenId, bucket pairing.
+ * @notice Returns the LPs accrued to a given tokenId, bucket pairing.
* @dev Nested mappings aren't returned normally as part of the default getter for a mapping.
- * @param tokenId Unique ID of token.
- * @param index Index of bucket to check LP balance of.
- * @return lpTokens Balance of lpTokens in the bucket for this position.
+ * @param tokenId Unique ID of token.
+ * @param index Index of bucket to check LP balance of.
+ * @return lps Balance of lps in the bucket for this position.
*/
- function getLPTokens(
+ function getLPs(
uint256 tokenId,
uint256 index
- ) external view returns (uint256 lpTokens);
+ ) external view returns (uint256 lps);
/**
* @notice Returns an array of bucket indexes in which an NFT has liquidity.
diff --git a/src/interfaces/position/IPositionManagerOwnerActions.sol b/src/interfaces/position/IPositionManagerOwnerActions.sol
index 70b3ee84d..60e7e6110 100644
--- a/src/interfaces/position/IPositionManagerOwnerActions.sol
+++ b/src/interfaces/position/IPositionManagerOwnerActions.sol
@@ -9,7 +9,7 @@ interface IPositionManagerOwnerActions {
/**
* @notice Called by owners to burn an existing NFT.
- * @dev Requires that all lp tokens have been removed from the NFT prior to calling.
+ * @dev Requires that all lps have been removed from the NFT prior to calling.
* @param params Calldata struct supplying inputs required to update the underlying assets owed to an NFT.
*/
function burn(
@@ -29,7 +29,8 @@ interface IPositionManagerOwnerActions {
) external;
/**
- * @notice Called by owners to add quote tokens and receive a representative NFT.
+ * @notice Called by owners to mint and receive an Ajna Position NFT.
+ * @dev PositionNFTs can only be minited with an association to pools that have been deployed by the Ajna ERC20PoolFactory or ERC721PoolFactory.
* @param params Calldata struct supplying inputs required to mint a positions NFT.
* @return tokenId The tokenId of the newly minted NFT.
*/
@@ -94,6 +95,7 @@ interface IPositionManagerOwnerActions {
address pool; // The pool address associated with positions NFT
uint256 fromIndex; // The bucket index from which liquidity should be moved
uint256 toIndex; // The bucket index to which liquidity should be moved
+ uint256 expiry; // Timestamp after which this TX will revert, preventing inclusion in a block with unfavorable price
}
/**
@@ -104,4 +106,4 @@ interface IPositionManagerOwnerActions {
address pool; // The pool address associated with positions NFT
uint256[] indexes; // The array of bucket indexes to reedem positions for
}
-}
\ No newline at end of file
+}
diff --git a/src/interfaces/rewards/IRewardsManagerErrors.sol b/src/interfaces/rewards/IRewardsManagerErrors.sol
index f02a09017..f9f1bb33d 100644
--- a/src/interfaces/rewards/IRewardsManagerErrors.sol
+++ b/src/interfaces/rewards/IRewardsManagerErrors.sol
@@ -11,6 +11,11 @@ interface IRewardsManagerErrors {
*/
error AlreadyClaimed();
+ /**
+ * @notice User attempted to claim rewards for an epoch that is not yet available.
+ */
+ error EpochNotAvailable();
+
/**
* @notice User attempted to record updated exchange rates outside of the allowed period.
*/
diff --git a/src/interfaces/rewards/IRewardsManagerState.sol b/src/interfaces/rewards/IRewardsManagerState.sol
index 01ebd8a3b..0367f3cab 100644
--- a/src/interfaces/rewards/IRewardsManagerState.sol
+++ b/src/interfaces/rewards/IRewardsManagerState.sol
@@ -47,6 +47,18 @@ interface IRewardsManagerState {
uint256 tokenId
) external view returns (address, address, uint256);
+ /**
+ * @notice Retrieve information about recorded LPs and rate values for a given bucket and a given stake, at stake time.
+ * @param tokenId ID of the NFT staked in the rewards contract to retrieve information about.
+ * @param bucketId ID of the bucket to retrieve recorded information at stake time.
+ * @return [WAD] LP amount the NFT owner is entitled in current bucket at the time of staking.
+ * @return [WAD] current bucket exchange rate at the time of staking.
+ */
+ function getBucketStateStakeInfo(
+ uint256 tokenId,
+ uint256 bucketId
+ ) external view returns (uint256, uint256);
+
}
/*********************/
@@ -62,6 +74,6 @@ struct StakeInfo {
}
struct BucketState {
- uint256 lpsAtStakeTime; // [RAY] LP amount the NFT owner is entitled in current bucket at the time of staking
- uint256 rateAtStakeTime; // [RAY] current bucket exchange rate at the time of staking (RAY)
+ uint256 lpsAtStakeTime; // [WAD] LP amount the NFT owner is entitled in current bucket at the time of staking
+ uint256 rateAtStakeTime; // [WAD] current bucket exchange rate at the time of staking
}
diff --git a/src/libraries/external/Auctions.sol b/src/libraries/external/Auctions.sol
index cd7cc022b..16153864a 100644
--- a/src/libraries/external/Auctions.sol
+++ b/src/libraries/external/Auctions.sol
@@ -59,17 +59,13 @@ library Auctions {
struct BucketTakeParams {
address borrower; // borrower address to take from
- uint256 collateral; // [WAD] borrower available collateral to take
bool depositTake; // deposit or arb take, used by bucket take
uint256 index; // bucket index, used by bucket take
uint256 inflator; // [WAD] current pool inflator
- uint256 t0Debt; // [WAD] borrower t0 debt
uint256 collateralScale; // precision of collateral token based on decimals
}
struct TakeParams {
address borrower; // borrower address to take from
- uint256 collateral; // [WAD] borrower available collateral to take
- uint256 t0Debt; // [WAD] borrower t0 debt
uint256 takeCollateral; // [WAD] desired amount to take
uint256 inflator; // [WAD] current pool inflator
uint256 poolType; // pool type (ERC20 or NFT)
@@ -84,13 +80,13 @@ library Auctions {
uint256 amountToDebitFromDeposit; // [WAD] the amount of quote tokens used to kick and debited from lender deposit
uint256 bucketCollateral; // [WAD] amount of collateral in bucket
uint256 bucketDeposit; // [WAD] amount of quote tokens in bucket
- uint256 bucketLPs; // [RAY] LPs of the bucket
+ uint256 bucketLPs; // [WAD] LPs of the bucket
uint256 bucketPrice; // [WAD] bucket price
- uint256 bucketRate; // [RAY] bucket exchange rate
+ uint256 bucketRate; // [WAD] bucket exchange rate
uint256 bucketScale; // [WAD] bucket scales
uint256 bucketUnscaledDeposit; // [WAD] unscaled amount of quote tokens in bucket
- uint256 lenderLPs; // [RAY] LPs of lender in bucket
- uint256 redeemedLPs; // [RAY] LPs used by kick action
+ uint256 lenderLPs; // [WAD] LPs of lender in bucket
+ uint256 redeemedLPs; // [WAD] LPs used by kick action
}
struct SettleLocalVars {
uint256 collateralUsed; // [WAD] collateral used to settle debt
@@ -122,20 +118,6 @@ library Auctions {
uint256 unscaledDeposit; // [WAD] Unscaled bucket quantity
uint256 unscaledQuoteTokenAmount; // [WAD] The unscaled token amount that taker should pay for collateral taken.
}
- struct TakeLoanLocalVars {
- uint256 repaidDebt; // [WAD] the amount of debt repaid to th epool by take auction
- uint256 borrowerDebt; // [WAD] the amount of borrower debt
- bool inAuction; // true if loan in auction
- }
- struct TakeFromLoanLocalVars {
- uint256 borrowerDebt; // [WAD] borrower's accrued debt
- bool inAuction; // true if loan still in auction after auction is taken, false otherwise
- uint256 newLup; // [WAD] LUP after auction is taken
- uint256 repaidDebt; // [WAD] debt repaid when auction is taken
- uint256 t0DebtInAuction; // [WAD] t0 pool debt in auction
- uint256 t0DebtInAuctionChange; // [WAD] t0 change amount of debt after auction is taken
- uint256 t0PoolDebt; // [WAD] t0 pool debt
- }
/**************/
/*** Events ***/
@@ -162,6 +144,7 @@ library Auctions {
error AuctionNotClearable();
error AuctionPriceGtBucketPrice();
error BorrowerOk();
+ error CollateralRoundingNeededButNotPossible();
error InsufficientLiquidity();
error InsufficientCollateral();
error NoAuction();
@@ -192,7 +175,6 @@ library Auctions {
* - BucketBankruptcy
* @param params_ Settle params
* @return collateralRemaining_ The amount of borrower collateral left after settle.
- * @return t0DebtRemaining_ The amount of t0 debt left after settle.
* @return collateralSettled_ The amount of collateral settled.
* @return t0DebtSettled_ The amount of t0 debt settled.
*/
@@ -204,7 +186,6 @@ library Auctions {
SettleParams memory params_
) external returns (
uint256 collateralRemaining_,
- uint256 t0DebtRemaining_,
uint256 collateralSettled_,
uint256 t0DebtSettled_
) {
@@ -312,14 +293,13 @@ library Auctions {
}
}
- t0DebtRemaining_ = borrower.t0Debt;
- t0DebtSettled_ -= t0DebtRemaining_;
+ t0DebtSettled_ -= borrower.t0Debt;
emit Settle(params_.borrower, t0DebtSettled_);
if (borrower.t0Debt == 0) {
// settle auction
- borrower.collateral = _settleAuction(
+ (borrower.collateral, ) = _settleAuction(
auctions_,
buckets_,
deposits_,
@@ -379,7 +359,7 @@ library Auctions {
DepositsState storage deposits_,
mapping(uint256 => Bucket) storage buckets_,
LoansState storage loans_,
- PoolState memory poolState_,
+ PoolState calldata poolState_,
uint256 index_
) external returns (
KickResult memory kickResult_
@@ -406,7 +386,7 @@ library Auctions {
vars.bucketPrice
);
- vars.amountToDebitFromDeposit = Maths.rayToWad(Maths.rmul(vars.lenderLPs, vars.bucketRate)); // calculate amount to remove based on lender LPs in bucket
+ vars.amountToDebitFromDeposit = Maths.wmul(vars.lenderLPs, vars.bucketRate); // calculate amount to remove based on lender LPs in bucket
if (vars.amountToDebitFromDeposit > vars.bucketDeposit) vars.amountToDebitFromDeposit = vars.bucketDeposit; // cap the amount to remove at bucket deposit
@@ -444,7 +424,7 @@ library Auctions {
Deposits.unscaledRemove(deposits_, index_, vars.bucketUnscaledDeposit);
} else {
- vars.redeemedLPs = Maths.wrdivr(vars.amountToDebitFromDeposit, vars.bucketRate);
+ vars.redeemedLPs = Maths.wdiv(vars.amountToDebitFromDeposit, vars.bucketRate);
Deposits.unscaledRemove(
deposits_,
@@ -484,19 +464,20 @@ library Auctions {
if (borrower.collateral == 0) revert InsufficientCollateral(); // revert if borrower's collateral is 0
+ uint256 t0RepayAmount;
+ uint256 t0BorrowerDebt;
(
result_.collateralAmount,
- result_.t0RepayAmount,
- borrower.t0Debt,
+ t0RepayAmount,
+ t0BorrowerDebt,
result_.t0DebtPenalty
) = _takeBucket(
auctions_,
buckets_,
deposits_,
+ borrower,
BucketTakeParams({
borrower: borrowerAddress_,
- collateral: borrower.collateral,
- t0Debt: borrower.t0Debt,
inflator: poolState_.inflator,
depositTake: depositTake_,
index: index_,
@@ -505,16 +486,20 @@ library Auctions {
);
borrower.collateral -= result_.collateralAmount;
+ borrower.t0Debt = t0BorrowerDebt - t0RepayAmount;
- if (result_.t0DebtPenalty != 0) {
- poolState_.debt += Maths.wmul(result_.t0DebtPenalty, poolState_.inflator);
- }
+ // update pool debt: apply penalty if case
+ poolState_.t0Debt += result_.t0DebtPenalty;
+ poolState_.t0Debt -= t0RepayAmount;
+ poolState_.debt = Maths.wmul(poolState_.t0Debt, poolState_.inflator);
+ result_.t0PoolDebt = poolState_.t0Debt;
+ result_.poolDebt = poolState_.debt;
(
- result_.poolDebt,
result_.newLup,
- result_.t0DebtInAuctionChange,
- result_.settledAuction
+ result_.settledAuction,
+ result_.remainingCollateral,
+ result_.compensatedCollateral
) = _takeLoan(
auctions_,
buckets_,
@@ -522,9 +507,16 @@ library Auctions {
loans_,
poolState_,
borrower,
- borrowerAddress_,
- result_.t0RepayAmount
+ borrowerAddress_
);
+
+ if (result_.settledAuction) {
+ // the overall debt in auction change is the total borrower debt exiting auction
+ result_.t0DebtInAuctionChange = t0BorrowerDebt;
+ } else {
+ // the overall debt in auction change is the amount of partially repaid debt
+ result_.t0DebtInAuctionChange = t0RepayAmount;
+ }
}
/**
@@ -547,22 +539,28 @@ library Auctions {
) external returns (TakeResult memory result_) {
Borrower memory borrower = loans_.borrowers[borrowerAddress_];
- // revert if borrower's collateral is 0 or if maxCollateral to be taken is 0
- if (borrower.collateral == 0 || collateral_ == 0) revert InsufficientCollateral();
+ if (
+ (collateral_ == 0) || // revert if amount to take is 0
+ (poolState_.poolType == uint8(PoolType.ERC721) && borrower.collateral < 1e18) || // revert in case of NFT take when there isn't a full token to be taken
+ (poolState_.poolType == uint8(PoolType.ERC20) && borrower.collateral == 0) // revert in case of ERC20 take when no collateral to be taken
+ ) {
+ revert InsufficientCollateral();
+ }
+ uint256 t0RepayAmount;
+ uint256 t0BorrowerDebt;
(
result_.collateralAmount,
result_.quoteTokenAmount,
- result_.t0RepayAmount,
- borrower.t0Debt,
+ t0RepayAmount,
+ t0BorrowerDebt,
result_.t0DebtPenalty,
result_.excessQuoteToken
) = _take(
auctions_,
+ borrower,
TakeParams({
borrower: borrowerAddress_,
- collateral: borrower.collateral,
- t0Debt: borrower.t0Debt,
takeCollateral: collateral_,
inflator: poolState_.inflator,
poolType: poolState_.poolType,
@@ -571,16 +569,20 @@ library Auctions {
);
borrower.collateral -= result_.collateralAmount;
+ borrower.t0Debt = t0BorrowerDebt - t0RepayAmount;
- if (result_.t0DebtPenalty != 0) {
- poolState_.debt += Maths.wmul(result_.t0DebtPenalty, poolState_.inflator);
- }
+ // update pool debt: apply penalty if case
+ poolState_.t0Debt += result_.t0DebtPenalty;
+ poolState_.t0Debt -= t0RepayAmount;
+ poolState_.debt = Maths.wmul(poolState_.t0Debt, poolState_.inflator);
+ result_.t0PoolDebt = poolState_.t0Debt;
+ result_.poolDebt = poolState_.debt;
(
- result_.poolDebt,
result_.newLup,
- result_.t0DebtInAuctionChange,
- result_.settledAuction
+ result_.settledAuction,
+ result_.remainingCollateral,
+ result_.compensatedCollateral
) = _takeLoan(
auctions_,
buckets_,
@@ -588,9 +590,16 @@ library Auctions {
loans_,
poolState_,
borrower,
- borrowerAddress_,
- result_.t0RepayAmount
+ borrowerAddress_
);
+
+ if (result_.settledAuction) {
+ // the overall debt in auction change is the total borrower debt exiting auction
+ result_.t0DebtInAuctionChange = t0BorrowerDebt;
+ } else {
+ // the overall debt in auction change is the amount of partially repaid debt
+ result_.t0DebtInAuctionChange = t0RepayAmount;
+ }
}
/**
@@ -611,7 +620,7 @@ library Auctions {
uint256 curUnclaimedAuctionReserve = reserveAuction_.unclaimed;
uint256 claimable = _claimableReserves(
- Maths.wmul(params_.poolDebt, params_.inflator),
+ Maths.wmul(params_.t0PoolDebt, params_.inflator),
params_.poolSize,
auctions_.totalBondEscrowed,
curUnclaimedAuctionReserve,
@@ -670,10 +679,11 @@ library Auctions {
* @notice Performs auction settle based on pool type, emits settle event and removes auction from auctions queue.
* @dev emit events:
* - AuctionNFTSettle or AuctionSettle
- * @param borrowerAddress_ Address of the borrower that exits auction.
- * @param borrowerCollateral_ Borrower collateral amount before auction exit (in NFT could be fragmented as result of partial takes).
- * @param poolType_ Type of the pool (can be ERC20 or NFT).
- * @return remainingCollateral_ Collateral remaining after auction is settled (same amount for ERC20 pool, rounded collateral for NFT pool).
+ * @param borrowerAddress_ Address of the borrower that exits auction.
+ * @param borrowerCollateral_ Borrower collateral amount before auction exit (in NFT could be fragmented as result of partial takes).
+ * @param poolType_ Type of the pool (can be ERC20 or NFT).
+ * @return remainingCollateral_ Collateral remaining after auction is settled (same amount for ERC20 pool, rounded collateral for NFT pool).
+ * @return compensatedCollateral_ Amount of collateral compensated (NFT settle only), to be deducted from pool pledged collateral accumulator. 0 for ERC20 pools.
*/
function _settleAuction(
AuctionsState storage auctions_,
@@ -682,18 +692,38 @@ library Auctions {
address borrowerAddress_,
uint256 borrowerCollateral_,
uint256 poolType_
- ) internal returns (uint256 remainingCollateral_) {
+ ) internal returns (uint256 remainingCollateral_, uint256 compensatedCollateral_) {
+
if (poolType_ == uint8(PoolType.ERC721)) {
uint256 lps;
uint256 bucketIndex;
- (remainingCollateral_, lps, bucketIndex) = _settleNFTCollateral(
- auctions_,
- buckets_,
- deposits_,
- borrowerAddress_,
- borrowerCollateral_
- );
+ remainingCollateral_ = (borrowerCollateral_ / Maths.WAD) * Maths.WAD; // floor collateral of borrower
+
+ // if there's fraction of NFTs remaining then reward difference to borrower as LPs in auction price bucket
+ if (remainingCollateral_ != borrowerCollateral_) {
+
+ // calculate the amount of collateral that should be compensated with LPs
+ compensatedCollateral_ = borrowerCollateral_ - remainingCollateral_;
+
+ uint256 auctionPrice = _auctionPrice(
+ auctions_.liquidations[borrowerAddress_].kickMomp,
+ auctions_.liquidations[borrowerAddress_].neutralPrice,
+ auctions_.liquidations[borrowerAddress_].kickTime
+ );
+
+ // determine the bucket index to compensate fractional collateral
+ bucketIndex = auctionPrice > MIN_PRICE ? _indexOf(auctionPrice) : MAX_FENWICK_INDEX;
+
+ // deposit collateral in bucket and reward LPs to compensate fractional collateral
+ lps = Buckets.addCollateral(
+ buckets_[bucketIndex],
+ borrowerAddress_,
+ Deposits.valueAt(deposits_, bucketIndex),
+ compensatedCollateral_,
+ _priceAt(bucketIndex)
+ );
+ }
emit AuctionNFTSettle(borrowerAddress_, remainingCollateral_, lps, bucketIndex);
@@ -706,46 +736,6 @@ library Auctions {
_removeAuction(auctions_, borrowerAddress_);
}
- /**
- * @notice Performs NFT collateral settlement by rounding down borrower's collateral amount and by moving borrower's token ids to pool claimable array.
- * @param borrowerAddress_ Address of the borrower that exits auction.
- * @param borrowerCollateral_ Borrower collateral amount before auction exit (could be fragmented as result of partial takes).
- * @return floorCollateral_ Rounded down collateral, the number of NFT tokens borrower can pull after auction exit.
- * @return lps_ LPs given to the borrower to compensate fractional collateral (if any).
- * @return bucketIndex_ Index of the bucket with LPs to compensate.
- */
- function _settleNFTCollateral(
- AuctionsState storage auctions_,
- mapping(uint256 => Bucket) storage buckets_,
- DepositsState storage deposits_,
- address borrowerAddress_,
- uint256 borrowerCollateral_
- ) internal returns (uint256 floorCollateral_, uint256 lps_, uint256 bucketIndex_) {
- floorCollateral_ = (borrowerCollateral_ / Maths.WAD) * Maths.WAD; // floor collateral of borrower
-
- // if there's fraction of NFTs remaining then reward difference to borrower as LPs in auction price bucket
- if (floorCollateral_ != borrowerCollateral_) {
- // cover borrower's fractional amount with LPs in auction price bucket
- uint256 fractionalCollateral = borrowerCollateral_ - floorCollateral_;
-
- uint256 auctionPrice = _auctionPrice(
- auctions_.liquidations[borrowerAddress_].kickMomp,
- auctions_.liquidations[borrowerAddress_].neutralPrice,
- auctions_.liquidations[borrowerAddress_].kickTime
- );
-
- bucketIndex_ = auctionPrice > MIN_PRICE ? _indexOf(auctionPrice) : MAX_FENWICK_INDEX;
-
- lps_ = Buckets.addCollateral(
- buckets_[bucketIndex_],
- borrowerAddress_,
- Deposits.valueAt(deposits_, bucketIndex_),
- fractionalCollateral,
- _priceAt(bucketIndex_)
- );
- }
- }
-
/**
* @notice Called to start borrower liquidation and to update the auctions queue.
* @dev write state:
@@ -770,7 +760,7 @@ library Auctions {
AuctionsState storage auctions_,
DepositsState storage deposits_,
LoansState storage loans_,
- PoolState memory poolState_,
+ PoolState calldata poolState_,
address borrowerAddress_,
uint256 additionalDebt_
) internal returns (
@@ -806,10 +796,6 @@ library Auctions {
momp
);
- // when loan is kicked, penalty of three months of interest is added
- kickResult_.kickPenalty = Maths.wmul(Maths.wdiv(poolState_.rate, 4 * 1e18), borrowerDebt);
- kickResult_.t0KickPenalty = Maths.wdiv(kickResult_.kickPenalty, poolState_.inflator);
-
// record liquidation info
uint256 neutralPrice = Maths.wmul(borrower.t0Np, poolState_.inflator);
_recordAuction(
@@ -827,14 +813,20 @@ library Auctions {
// remove kicked loan from heap
Loans.remove(loans_, borrowerAddress_, loans_.indices[borrowerAddress_]);
- kickResult_.t0KickedDebt += kickResult_.t0KickPenalty;
+ // when loan is kicked, penalty of three months of interest is added
+ uint256 t0KickPenalty = Maths.wmul(kickResult_.t0KickedDebt, Maths.wdiv(poolState_.rate, 4 * 1e18));
+ uint256 kickPenalty = Maths.wmul(t0KickPenalty, poolState_.inflator);
+
+ kickResult_.t0PoolDebt = poolState_.t0Debt + t0KickPenalty;
+ kickResult_.t0KickedDebt += t0KickPenalty;
+ // update borrower debt with kicked debt penalty
borrower.t0Debt = kickResult_.t0KickedDebt;
emit Kick(
borrowerAddress_,
- borrowerDebt + kickResult_.kickPenalty,
- borrower.collateral,
+ borrowerDebt + kickPenalty,
+ borrowerCollateral,
bondSize
);
}
@@ -843,7 +835,8 @@ library Auctions {
* @notice Performs take collateral on an auction and updates bond size and kicker balance accordingly.
* @dev emit events:
* - Take
- * @param params_ Struct containing take action params details.
+ * @param borrower_ Struct containing auctioned borrower details.
+ * @param params_ Struct containing take action params details.
* @return Collateral amount taken.
* @return Quote token to be received from taker.
* @return T0 debt amount repaid.
@@ -853,22 +846,34 @@ library Auctions {
*/
function _take(
AuctionsState storage auctions_,
+ Borrower memory borrower_,
TakeParams memory params_
) internal returns (uint256, uint256, uint256, uint256, uint256, uint256) {
Liquidation storage liquidation = auctions_.liquidations[params_.borrower];
- TakeLocalVars memory vars = _prepareTake(liquidation, params_.t0Debt, params_.collateral, params_.inflator);
+ TakeLocalVars memory vars = _prepareTake(
+ liquidation,
+ borrower_.t0Debt,
+ borrower_.collateral,
+ params_.inflator
+ );
// These are placeholder max values passed to calculateTakeFlows because there is no explicit bound on the
// quote token amount in take calls (as opposed to bucketTake)
vars.unscaledDeposit = type(uint256).max;
vars.bucketScale = Maths.WAD;
+ uint256 takeableCollateral = borrower_.collateral;
+ // for NFT take make sure the take flow and bond change calculation happens for the rounded collateral that can be taken
+ if (params_.poolType == uint8(PoolType.ERC721)) {
+ takeableCollateral = (takeableCollateral / 1e18) * 1e18;
+ }
+
// In the case of take, the taker binds the collateral qty but not the quote token qty
// ugly to get take work like a bucket take -- this is the max amount of quote token from the take that could go to
// reduce the debt of the borrower -- analagous to the amount of deposit in the bucket for a bucket take
vars = _calculateTakeFlowsAndBondChange(
- Maths.min(params_.collateral, params_.takeCollateral),
+ Maths.min(takeableCollateral, params_.takeCollateral),
params_.inflator,
params_.collateralScale,
vars
@@ -888,14 +893,18 @@ library Auctions {
// slither-disable-next-line divide-before-multiply
uint256 collateralTaken = (vars.collateralAmount / 1e18) * 1e18; // solidity rounds down, so if 2.5 it will be 2.5 / 1 = 2
- if (collateralTaken != vars.collateralAmount && params_.collateral >= collateralTaken + 1e18) { // collateral taken not a round number
- collateralTaken += 1e18; // round up collateral to take
- // taker should send additional quote tokens to cover difference between collateral needed to be taken and rounded collateral, at auction price
- // borrower will get quote tokens for the difference between rounded collateral and collateral taken to cover debt
- vars.excessQuoteToken = Maths.wmul(collateralTaken - vars.collateralAmount, vars.auctionPrice);
+ if (collateralTaken != vars.collateralAmount) { // collateral taken not a round number
+ if (Maths.min(borrower_.collateral, params_.takeCollateral) >= collateralTaken + 1e18) {
+ collateralTaken += 1e18; // round up collateral to take
+ // taker should send additional quote tokens to cover difference between collateral needed to be taken and rounded collateral, at auction price
+ // borrower will get quote tokens for the difference between rounded collateral and collateral taken to cover debt
+ vars.excessQuoteToken = Maths.wmul(collateralTaken - vars.collateralAmount, vars.auctionPrice);
+ vars.collateralAmount = collateralTaken;
+ } else {
+ // shouldn't get here, but just in case revert
+ revert CollateralRoundingNeededButNotPossible();
+ }
}
-
- vars.collateralAmount = collateralTaken;
}
return (
@@ -912,7 +921,8 @@ library Auctions {
* @notice Performs bucket take collateral on an auction and rewards taker and kicker (if case).
* @dev emit events:
* - BucketTake
- * @param params_ Struct containing take action details.
+ * @param borrower_ Struct containing auctioned borrower details.
+ * @param params_ Struct containing take action details.
* @return Collateral amount taken.
* @return T0 debt amount repaid.
* @return T0 borrower debt (including penalty).
@@ -922,12 +932,18 @@ library Auctions {
AuctionsState storage auctions_,
mapping(uint256 => Bucket) storage buckets_,
DepositsState storage deposits_,
+ Borrower memory borrower_,
BucketTakeParams memory params_
) internal returns (uint256, uint256, uint256, uint256) {
Liquidation storage liquidation = auctions_.liquidations[params_.borrower];
- TakeLocalVars memory vars = _prepareTake(liquidation, params_.t0Debt, params_.collateral, params_.inflator);
+ TakeLocalVars memory vars = _prepareTake(
+ liquidation,
+ borrower_.t0Debt,
+ borrower_.collateral,
+ params_.inflator
+ );
vars.unscaledDeposit = Deposits.unscaledValueAt(deposits_, params_.index);
@@ -944,7 +960,7 @@ library Auctions {
vars.bucketScale = Deposits.scale(deposits_, params_.index);
vars = _calculateTakeFlowsAndBondChange(
- params_.collateral,
+ borrower_.collateral,
params_.inflator,
params_.collateralScale,
vars
@@ -982,13 +998,13 @@ library Auctions {
* @notice If borrower becomes recollateralized then auction is settled. Update loan's state.
* @dev reverts on:
* - borrower debt less than pool min debt AmountLTMinDebt()
- * @param borrower_ The borrower details owning loan that is taken.
- * @param borrowerAddress_ The address of the borrower.
- * @param t0RepaidDebt_ T0 debt amount repaid by the take action.
- * @return poolDebt_ Accrued debt pool after debt is repaid.
- * @return newLup_ The new LUP of pool (after debt is repaid).
- * @return t0DebtInAuctionChange_ The overall debt in auction change (remaining borrower debt if auction settled, repaid debt otherwise).
- * @return settledAuction_ True if auction is settled by the take action.
+ * @param borrower_ Struct containing pool details.
+ * @param borrower_ The borrower details owning loan that is taken.
+ * @param borrowerAddress_ The address of the borrower.
+ * @return newLup_ The new LUP of pool (after debt is repaid).
+ * @return settledAuction_ True if auction is settled by the take action. (NFT take: rebalance borrower collateral in pool if true)
+ * @return remainingCollateral_ Borrower collateral remaining after take action. (NFT take: collateral to be rebalanced in case of NFT settlement)
+ * @return compensatedCollateral_ Amount of collateral compensated, to be deducted from pool pledged collateral accumulator.
*/
function _takeLoan(
AuctionsState storage auctions_,
@@ -997,41 +1013,34 @@ library Auctions {
LoansState storage loans_,
PoolState memory poolState_,
Borrower memory borrower_,
- address borrowerAddress_,
- uint256 t0RepaidDebt_
+ address borrowerAddress_
) internal returns (
- uint256 poolDebt_,
uint256 newLup_,
- uint256 t0DebtInAuctionChange_,
- bool settledAuction_
+ bool settledAuction_,
+ uint256 remainingCollateral_,
+ uint256 compensatedCollateral_
) {
- TakeLoanLocalVars memory vars;
-
- vars.repaidDebt = Maths.wmul(t0RepaidDebt_, poolState_.inflator);
- vars.borrowerDebt = Maths.wmul(borrower_.t0Debt, poolState_.inflator);
-
- vars.borrowerDebt -= vars.repaidDebt;
- poolDebt_ = poolState_.debt - vars.repaidDebt;
+ uint256 borrowerDebt = Maths.wmul(borrower_.t0Debt, poolState_.inflator);
// check that taking from loan doesn't leave borrower debt under min debt amount
- _revertOnMinDebt(loans_, poolDebt_, vars.borrowerDebt, poolState_.quoteDustLimit);
-
- newLup_ = _lup(deposits_, poolDebt_);
+ _revertOnMinDebt(
+ loans_,
+ poolState_.debt,
+ borrowerDebt,
+ poolState_.quoteDustLimit
+ );
- vars.inAuction = true;
+ // calculate new lup with repaid debt from take
+ newLup_ = _lup(deposits_, poolState_.debt);
- if (_isCollateralized(vars.borrowerDebt, borrower_.collateral, newLup_, poolState_.poolType)) {
- // settle auction if borrower becomes re-collateralized
+ remainingCollateral_ = borrower_.collateral;
- vars.inAuction = false;
+ if (_isCollateralized(borrowerDebt, borrower_.collateral, newLup_, poolState_.poolType)) {
settledAuction_ = true;
- // the overall debt in auction change is the total borrower debt exiting auction
- t0DebtInAuctionChange_ = borrower_.t0Debt;
-
// settle auction and update borrower's collateral with value after settlement
- borrower_.collateral = _settleAuction(
+ (remainingCollateral_, compensatedCollateral_) = _settleAuction(
auctions_,
buckets_,
deposits_,
@@ -1039,12 +1048,9 @@ library Auctions {
borrower_.collateral,
poolState_.poolType
);
- } else {
- // the overall debt in auction change is the amount of partially repaid debt
- t0DebtInAuctionChange_ = t0RepaidDebt_;
- }
- borrower_.t0Debt -= t0RepaidDebt_;
+ borrower_.collateral = remainingCollateral_;
+ }
// update loan state, stamp borrower t0Np only when exiting from auction
Loans.update(
@@ -1053,11 +1059,11 @@ library Auctions {
deposits_,
borrower_,
borrowerAddress_,
- vars.borrowerDebt,
+ poolState_.debt,
poolState_.rate,
newLup_,
- vars.inAuction,
- !vars.inAuction // stamp borrower t0Np if exiting from auction
+ !settledAuction_,
+ settledAuction_ // stamp borrower t0Np if exiting from auction
);
}
@@ -1146,6 +1152,8 @@ library Auctions {
vars.t0RepayAmount = Maths.wdiv(vars.scaledQuoteTokenAmount, inflator_);
vars.unscaledQuoteTokenAmount = vars.unscaledDeposit;
+ vars.scaledQuoteTokenAmount = Maths.wmul(vars.collateralAmount, vars.auctionPrice);
+
} else if (vars.borrowerDebt <= borrowerCollateralValue) {
// borrower debt is constraining factor
vars.collateralAmount = _roundToScale(Maths.wdiv(vars.borrowerDebt, borrowerPrice), collateralScale_);
@@ -1164,7 +1172,7 @@ library Auctions {
}
if (vars.isRewarded) {
- // take is above neutralPrice, Kicker is rewarded
+ // take is below neutralPrice, Kicker is rewarded
vars.bondChange = Maths.wmul(vars.scaledQuoteTokenAmount, uint256(vars.bpf));
} else {
// take is above neutralPrice, Kicker is penalized
@@ -1332,11 +1340,12 @@ library Auctions {
) internal {
Bucket storage bucket = buckets_[bucketIndex_];
- uint256 bucketExchangeRate = Buckets.getUnscaledExchangeRate(
+ uint256 scaledDeposit = Maths.wmul(vars.unscaledDeposit, vars.bucketScale);
+
+ uint256 exchangeRate = Buckets.getExchangeRate(
bucket.collateral,
bucket.lps,
- vars.unscaledDeposit,
- vars.bucketScale,
+ scaledDeposit,
vars.bucketPrice
);
@@ -1345,10 +1354,9 @@ library Auctions {
// if arb take - taker is awarded collateral * (bucket price - auction price) worth (in quote token terms) units of LPB in the bucket
if (!depositTake_) {
- uint256 takerReward = Maths.wmul(vars.collateralAmount, vars.bucketPrice - vars.auctionPrice);
- uint256 takerRewardUnscaledQuoteToken = Maths.wdiv(takerReward, vars.bucketScale);
+ uint256 takerReward = Maths.wmul(vars.collateralAmount, vars.bucketPrice - vars.auctionPrice);
- totalLPsReward = Maths.wrdivr(takerRewardUnscaledQuoteToken, bucketExchangeRate);
+ totalLPsReward = Maths.wdiv(takerReward, exchangeRate);
Buckets.addLenderLPs(bucket, bankruptcyTime, msg.sender, totalLPsReward);
}
@@ -1357,7 +1365,7 @@ library Auctions {
// the bondholder/kicker is awarded bond change worth of LPB in the bucket
if (vars.isRewarded) {
- kickerLPsReward = Maths.wrdivr(Maths.wdiv(vars.bondChange, vars.bucketScale), bucketExchangeRate);
+ kickerLPsReward = Maths.wdiv(vars.bondChange, exchangeRate);
totalLPsReward += kickerLPsReward;
Buckets.addLenderLPs(bucket, bankruptcyTime, vars.kicker, kickerLPsReward);
diff --git a/src/libraries/external/BorrowerActions.sol b/src/libraries/external/BorrowerActions.sol
index 5fb1f70a5..04e5b020e 100644
--- a/src/libraries/external/BorrowerActions.sol
+++ b/src/libraries/external/BorrowerActions.sol
@@ -20,7 +20,10 @@ import {
_priceAt,
_isCollateralized
} from '../helpers/PoolHelper.sol';
-import { _revertOnMinDebt } from '../helpers/RevertsHelper.sol';
+import {
+ _revertIfLupDroppedBelowLimit,
+ _revertOnMinDebt
+} from '../helpers/RevertsHelper.sol';
import { Buckets } from '../internal/Buckets.sol';
import { Deposits } from '../internal/Deposits.sol';
@@ -41,16 +44,19 @@ library BorrowerActions {
/*************************/
struct DrawDebtLocalVars {
- uint256 borrowerDebt; // [WAD] borrower's accrued debt
- uint256 debtChange; // [WAD] additional debt resulted from draw debt action
- bool inAuction; // true if loan is auctioned
- uint256 lupId; // id of new LUP
- bool stampT0Np; // true if loan's t0 neutral price should be restamped (when drawing debt or pledge settles auction)
+ bool borrow; // true if borrow action
+ uint256 borrowerDebt; // [WAD] borrower's accrued debt
+ uint256 compensatedCollateral; // [WAD] amount of borrower collateral that is compensated with LPs (NFTs only)
+ uint256 t0BorrowAmount; // [WAD] t0 amount to borrow
+ uint256 t0DebtChange; // [WAD] additional t0 debt resulted from draw debt action
+ bool inAuction; // true if loan is auctioned
+ bool pledge; // true if pledge action
+ bool stampT0Np; // true if loan's t0 neutral price should be restamped (when drawing debt or pledge settles auction)
}
struct RepayDebtLocalVars {
uint256 borrowerDebt; // [WAD] borrower's accrued debt
+ uint256 compensatedCollateral; // [WAD] amount of borrower collateral that is compensated with LPs (NFTs only)
bool inAuction; // true if loan still in auction after repay, false otherwise
- uint256 newLup; // [WAD] LUP after repay debt action
bool pull; // true if pull action
bool repay; // true if repay action
bool stampT0Np; // true if loan's t0 neutral price should be restamped (when repay settles auction or pull collateral)
@@ -63,10 +69,11 @@ library BorrowerActions {
/**************/
// See `IPoolErrors` for descriptions
+ error AuctionActive();
error BorrowerNotSender();
error BorrowerUnderCollateralized();
error InsufficientCollateral();
- error LimitIndexReached();
+ error LimitIndexExceeded();
error NoDebt();
/***************************/
@@ -91,7 +98,7 @@ library BorrowerActions {
* @dev reverts on:
* - borrower not sender BorrowerNotSender()
* - borrower debt less than pool min debt AmountLTMinDebt()
- * - limit price reached LimitIndexReached()
+ * - limit price reached LimitIndexExceeded()
* - borrower cannot draw more debt BorrowerUnderCollateralized()
* @dev emit events:
* - Auctions._settleAuction:
@@ -112,21 +119,27 @@ library BorrowerActions {
) {
Borrower memory borrower = loans_.borrowers[borrowerAddress_];
- result_.poolDebt = poolState_.debt;
- result_.newLup = _lup(deposits_, result_.poolDebt);
- result_.poolCollateral = poolState_.collateral;
-
DrawDebtLocalVars memory vars;
+ vars.pledge = collateralToPledge_ != 0;
+ vars.borrow = amountToBorrow_ != 0 || limitIndex_ != 0; // enable an intentional 0 borrow loan call to update borrower's loan state
vars.borrowerDebt = Maths.wmul(borrower.t0Debt, poolState_.inflator);
+ vars.inAuction = _inAuction(auctions_, borrowerAddress_);
+
+ result_.t0PoolDebt = poolState_.t0Debt;
+ result_.poolDebt = poolState_.debt;
+ result_.poolCollateral = poolState_.collateral;
- // pledge collateral to pool
- if (collateralToPledge_ != 0) {
+ result_.remainingCollateral = borrower.collateral;
+
+ if (vars.pledge) {
// add new amount of collateral to pledge to borrower balance
borrower.collateral += collateralToPledge_;
- // load loan's auction state
- vars.inAuction = _inAuction(auctions_, borrowerAddress_);
+ result_.remainingCollateral += collateralToPledge_;
+
+ result_.newLup = _lup(deposits_, result_.poolDebt);
+
// if loan is auctioned and becomes collateralized by newly pledged collateral then settle auction
if (
vars.inAuction &&
@@ -142,7 +155,10 @@ library BorrowerActions {
result_.t0DebtInAuctionChange = borrower.t0Debt;
// settle auction and update borrower's collateral with value after settlement
- result_.remainingCollateral = Auctions._settleAuction(
+ (
+ result_.remainingCollateral,
+ vars.compensatedCollateral
+ ) = Auctions._settleAuction(
auctions_,
buckets_,
deposits_,
@@ -151,37 +167,49 @@ library BorrowerActions {
poolState_.poolType
);
- borrower.collateral = result_.remainingCollateral;
+ borrower.collateral = result_.remainingCollateral;
+ result_.poolCollateral -= vars.compensatedCollateral;
}
// add new amount of collateral to pledge to pool balance
result_.poolCollateral += collateralToPledge_;
}
- // borrow against pledged collateral
- // check both values to enable an intentional 0 borrow loan call to update borrower's loan state
- if (amountToBorrow_ != 0 || limitIndex_ != 0) {
+ if (vars.borrow) {
// only intended recipient can borrow quote
if (borrowerAddress_ != msg.sender) revert BorrowerNotSender();
- // add origination fee to the amount to borrow and add to borrower's debt
- vars.debtChange = Maths.wmul(amountToBorrow_, _feeRate(poolState_.rate) + Maths.WAD);
+ // an auctioned borrower in not allowed to draw more debt (even if collateralized at the new LUP) if auction is not settled
+ if (vars.inAuction) revert AuctionActive();
+
+ vars.t0BorrowAmount = Maths.wdiv(amountToBorrow_, poolState_.inflator);
+
+ // t0 debt change is t0 amount to borrow plus the origination fee
+ vars.t0DebtChange = Maths.wmul(vars.t0BorrowAmount, _feeRate(poolState_.rate) + Maths.WAD);
+
+ borrower.t0Debt += vars.t0DebtChange;
- vars.borrowerDebt += vars.debtChange;
+ vars.borrowerDebt = Maths.wmul(borrower.t0Debt, poolState_.inflator);
- // check that drawing debt doesn't leave borrower debt under min debt amount
- _revertOnMinDebt(loans_, result_.poolDebt, vars.borrowerDebt, poolState_.quoteDustLimit);
+ // check that drawing debt doesn't leave borrower debt under pool min debt amount
+ _revertOnMinDebt(
+ loans_,
+ result_.poolDebt,
+ vars.borrowerDebt,
+ poolState_.quoteDustLimit
+ );
// add debt change to pool's debt
- result_.poolDebt += vars.debtChange;
+ result_.t0PoolDebt += vars.t0DebtChange;
+ result_.poolDebt = Maths.wmul(result_.t0PoolDebt, poolState_.inflator);
- // determine new lup index and revert if borrow happens at a price higher than the specified limit (lower index than lup index)
- vars.lupId = _lupIndex(deposits_, result_.poolDebt);
- if (vars.lupId > limitIndex_) revert LimitIndexReached();
+ result_.newLup = _lup(deposits_, result_.poolDebt);
+
+ // revert if borrow drives LUP price under the specified price limit
+ _revertIfLupDroppedBelowLimit(result_.newLup, limitIndex_);
// calculate new lup and check borrow action won't push borrower into a state of under-collateralization
// this check also covers the scenario when loan is already auctioned
- result_.newLup = _priceAt(vars.lupId);
if (!_isCollateralized(vars.borrowerDebt, borrower.collateral, result_.newLup, poolState_.poolType)) {
revert BorrowerUnderCollateralized();
@@ -189,10 +217,11 @@ library BorrowerActions {
// stamp borrower t0Np when draw debt
vars.stampT0Np = true;
+ }
- result_.t0DebtChange = Maths.wdiv(vars.debtChange, poolState_.inflator);
-
- borrower.t0Debt += result_.t0DebtChange;
+ // calculate LUP if it wasn't calculated previously
+ if (!vars.pledge && !vars.borrow) {
+ result_.newLup = _lup(deposits_, result_.poolDebt);
}
// update loan state
@@ -202,7 +231,7 @@ library BorrowerActions {
deposits_,
borrower,
borrowerAddress_,
- vars.borrowerDebt,
+ result_.poolDebt,
poolState_.rate,
result_.newLup,
vars.inAuction,
@@ -230,6 +259,7 @@ library BorrowerActions {
* - borrower debt less than pool min debt AmountLTMinDebt()
* - borrower not sender BorrowerNotSender()
* - not enough collateral to pull InsufficientCollateral()
+ * - limit price reached LimitIndexExceeded()
* @dev emit events:
* - Auctions._settleAuction:
* - AuctionNFTSettle or AuctionSettle
@@ -242,7 +272,8 @@ library BorrowerActions {
PoolState calldata poolState_,
address borrowerAddress_,
uint256 maxQuoteTokenAmountToRepay_,
- uint256 collateralAmountToPull_
+ uint256 collateralAmountToPull_,
+ uint256 limitIndex_
) external returns (
RepayDebtResult memory result_
) {
@@ -251,35 +282,45 @@ library BorrowerActions {
RepayDebtLocalVars memory vars;
vars.repay = maxQuoteTokenAmountToRepay_ != 0;
- vars.pull = collateralAmountToPull_ != 0;
+ vars.pull = collateralAmountToPull_ != 0;
vars.borrowerDebt = Maths.wmul(borrower.t0Debt, poolState_.inflator);
+ vars.inAuction = _inAuction(auctions_, borrowerAddress_);
+ result_.t0PoolDebt = poolState_.t0Debt;
result_.poolDebt = poolState_.debt;
result_.poolCollateral = poolState_.collateral;
+ result_.remainingCollateral = borrower.collateral;
+
if (vars.repay) {
if (borrower.t0Debt == 0) revert NoDebt();
if (maxQuoteTokenAmountToRepay_ == type(uint256).max) {
- result_.t0RepaidDebt = borrower.t0Debt;
+ vars.t0RepaidDebt = borrower.t0Debt;
} else {
- result_.t0RepaidDebt = Maths.min(
+ vars.t0RepaidDebt = Maths.min(
borrower.t0Debt,
Maths.wdiv(maxQuoteTokenAmountToRepay_, poolState_.inflator)
);
}
- result_.quoteTokenToRepay = Maths.wmul(result_.t0RepaidDebt, poolState_.inflator);
+ result_.t0PoolDebt -= vars.t0RepaidDebt;
- result_.poolDebt -= result_.quoteTokenToRepay;
- vars.borrowerDebt -= result_.quoteTokenToRepay;
+ result_.poolDebt = Maths.wmul(result_.t0PoolDebt, poolState_.inflator);
+ result_.quoteTokenToRepay = Maths.wmul(vars.t0RepaidDebt, poolState_.inflator);
+ vars.borrowerDebt = Maths.wmul(borrower.t0Debt - vars.t0RepaidDebt, poolState_.inflator);
// check that paying the loan doesn't leave borrower debt under min debt amount
- _revertOnMinDebt(loans_, result_.poolDebt, vars.borrowerDebt, poolState_.quoteDustLimit);
+ _revertOnMinDebt(
+ loans_,
+ result_.poolDebt,
+ vars.borrowerDebt,
+ poolState_.quoteDustLimit
+ );
result_.newLup = _lup(deposits_, result_.poolDebt);
- vars.inAuction = _inAuction(auctions_, borrowerAddress_);
+ // if loan is auctioned and becomes collateralized by repaying debt then settle auction
if (vars.inAuction) {
if (_isCollateralized(vars.borrowerDebt, borrower.collateral, result_.newLup, poolState_.poolType)) {
// borrower becomes re-collateralized
@@ -292,7 +333,10 @@ library BorrowerActions {
result_.t0DebtInAuctionChange = borrower.t0Debt;
// settle auction and update borrower's collateral with value after settlement
- result_.remainingCollateral = Auctions._settleAuction(
+ (
+ result_.remainingCollateral,
+ vars.compensatedCollateral
+ ) = Auctions._settleAuction(
auctions_,
buckets_,
deposits_,
@@ -301,23 +345,29 @@ library BorrowerActions {
poolState_.poolType
);
- borrower.collateral = result_.remainingCollateral;
+ borrower.collateral = result_.remainingCollateral;
+ result_.poolCollateral -= vars.compensatedCollateral;
} else {
// partial repay, remove only the paid debt from pool auctions debt accumulator
- result_.t0DebtInAuctionChange = result_.t0RepaidDebt;
+ result_.t0DebtInAuctionChange = vars.t0RepaidDebt;
}
}
- borrower.t0Debt -= result_.t0RepaidDebt;
+ borrower.t0Debt -= vars.t0RepaidDebt;
}
if (vars.pull) {
// only intended recipient can pull collateral
if (borrowerAddress_ != msg.sender) revert BorrowerNotSender();
- // calculate LUP only if it wasn't calculated by repay action
+ // an auctioned borrower in not allowed to pull collateral (even if collateralized at the new LUP) if auction is not settled
+ if (vars.inAuction) revert AuctionActive();
+
+ // calculate LUP only if it wasn't calculated in repay action
if (!vars.repay) result_.newLup = _lup(deposits_, result_.poolDebt);
+ _revertIfLupDroppedBelowLimit(result_.newLup, limitIndex_);
+
uint256 encumberedCollateral = borrower.t0Debt != 0 ? Maths.wdiv(vars.borrowerDebt, result_.newLup) : 0;
if (borrower.collateral - encumberedCollateral < collateralAmountToPull_) revert InsufficientCollateral();
@@ -329,7 +379,7 @@ library BorrowerActions {
result_.poolCollateral -= collateralAmountToPull_;
}
- // calculate LUP if repay is called with 0 amount
+ // calculate LUP if it wasn't calculated previously
if (!vars.repay && !vars.pull) {
result_.newLup = _lup(deposits_, result_.poolDebt);
}
@@ -341,7 +391,7 @@ library BorrowerActions {
deposits_,
borrower,
borrowerAddress_,
- vars.borrowerDebt,
+ result_.poolDebt,
poolState_.rate,
result_.newLup,
vars.inAuction,
@@ -366,18 +416,11 @@ library BorrowerActions {
return auctions_.liquidations[borrower_].kickTime != 0;
}
- function _lupIndex(
- DepositsState storage deposits_,
- uint256 debt_
- ) internal view returns (uint256) {
- return Deposits.findIndexOfSum(deposits_, debt_);
- }
-
function _lup(
DepositsState storage deposits_,
uint256 debt_
) internal view returns (uint256) {
- return _priceAt(_lupIndex(deposits_, debt_));
+ return _priceAt(Deposits.findIndexOfSum(deposits_, debt_));
}
}
diff --git a/src/libraries/external/LenderActions.sol b/src/libraries/external/LenderActions.sol
index f38bf6ea0..91fbf4d6f 100644
--- a/src/libraries/external/LenderActions.sol
+++ b/src/libraries/external/LenderActions.sol
@@ -34,19 +34,26 @@ library LenderActions {
/*************************/
struct MoveQuoteLocalVars {
- uint256 amountToMove; // [WAD] Quote token amount to move between indexes.
- uint256 fromBucketPrice; // [WAD] Price of the bucket to move amount from.
- uint256 fromBucketLPs; // [RAY] Amount of LPs in the bucket to move amount from.
- uint256 fromBucketDepositTime; // Time of lender deposit in the bucket to move amount from.
- uint256 toBucketPrice; // [WAD] Price of the bucket to move amount to.
- uint256 toBucketBankruptcyTime; // Time the bucket to move amount to was marked as insolvent.
- uint256 ptp; // [WAD] Pool Threshold Price.
- uint256 htp; // [WAD] Highest Threshold Price.
+ uint256 amountToMove; // [WAD] Quote token amount to move between indexes.
+ uint256 fromBucketPrice; // [WAD] Price of the bucket to move amount from.
+ uint256 fromBucketCollateral; // [WAD] Total amount of collateral in from bucket.
+ uint256 fromBucketLPs; // [WAD] Total amount of LPs in from bucket.
+ uint256 fromBucketLenderLPs; // [WAD] Amount of LPs owned by lender in from bucket.
+ uint256 fromBucketDepositTime; // Time of lender deposit in the bucket to move amount from.
+ uint256 fromBucketRemainingLPs; // Amount of LPs remaining in from bucket after move.
+ uint256 fromBucketRemainingDeposit; // Amount of scaled deposit remaining in from bucket after move.
+ uint256 toBucketPrice; // [WAD] Price of the bucket to move amount to.
+ uint256 toBucketBankruptcyTime; // Time the bucket to move amount to was marked as insolvent.
+ uint256 toBucketUnscaledDeposit; // Amount of unscaled deposit in to bucket.
+ uint256 toBucketDeposit; // Amount of scaled deposit in to bucket.
+ uint256 toBucketScale; // Scale deposit of to bucket.
+ uint256 ptp; // [WAD] Pool Threshold Price.
+ uint256 htp; // [WAD] Highest Threshold Price.
}
struct RemoveDepositParams {
uint256 depositConstraint; // [WAD] Constraint on deposit in quote token.
- uint256 lpConstraint; // [RAY] Constraint in LPB terms.
- uint256 bucketLPs; // [RAY] Total LPB in the bucket.
+ uint256 lpConstraint; // [WAD] Constraint in LPB terms.
+ uint256 bucketLPs; // [WAD] Total LPB in the bucket.
uint256 bucketCollateral; // [WAD] Claimable collateral in the bucket.
uint256 price; // [WAD] Price of bucket.
uint256 index; // Bucket index.
@@ -62,7 +69,7 @@ library LenderActions {
event BucketBankruptcy(uint256 indexed index, uint256 lpForfeited);
event MoveQuoteToken(address indexed lender, uint256 indexed from, uint256 indexed to, uint256 amount, uint256 lpRedeemedFrom, uint256 lpAwardedTo, uint256 lup);
event RemoveQuoteToken(address indexed lender, uint256 indexed price, uint256 amount, uint256 lpRedeemed, uint256 lup);
- event TransferLPTokens(address owner, address newOwner, uint256[] indexes, uint256 lpTokens);
+ event TransferLPs(address owner, address newOwner, uint256[] indexes, uint256 lps);
/**************/
/*** Errors ***/
@@ -80,6 +87,7 @@ library LenderActions {
error InsufficientLiquidity();
error InsufficientCollateral();
error MoveToSamePrice();
+ error TransferToSameOwner();
/***************************/
/*** External Functions ***/
@@ -191,6 +199,7 @@ library LenderActions {
* - dust amount DustAmountNotExceeded()
* - invalid index InvalidIndex()
* @dev emit events:
+ * - BucketBankruptcy
* - MoveQuoteToken
*/
function moveQuoteToken(
@@ -218,18 +227,21 @@ library LenderActions {
Lender storage fromBucketLender = fromBucket.lenders[msg.sender];
vars.fromBucketPrice = _priceAt(params_.fromIndex);
- vars.toBucketPrice = _priceAt(params_.toIndex);
+ vars.fromBucketCollateral = fromBucket.collateral;
+ vars.fromBucketLPs = fromBucket.lps;
vars.fromBucketDepositTime = fromBucketLender.depositTime;
- if (fromBucket.bankruptcyTime < vars.fromBucketDepositTime) vars.fromBucketLPs = fromBucketLender.lps;
+ vars.toBucketPrice = _priceAt(params_.toIndex);
+
+ if (fromBucket.bankruptcyTime < vars.fromBucketDepositTime) vars.fromBucketLenderLPs = fromBucketLender.lps;
- (vars.amountToMove, fromBucketRedeemedLPs_, ) = _removeMaxDeposit(
+ (vars.amountToMove, fromBucketRedeemedLPs_, vars.fromBucketRemainingDeposit) = _removeMaxDeposit(
deposits_,
RemoveDepositParams({
depositConstraint: params_.maxAmountToMove,
- lpConstraint: vars.fromBucketLPs,
- bucketLPs: fromBucket.lps,
- bucketCollateral: fromBucket.collateral,
+ lpConstraint: vars.fromBucketLenderLPs,
+ bucketLPs: vars.fromBucketLPs,
+ bucketCollateral: vars.fromBucketCollateral,
price: vars.fromBucketPrice,
index: params_.fromIndex,
dustLimit: poolState_.quoteDustLimit
@@ -245,41 +257,53 @@ library LenderActions {
}
}
- uint256 unscaledToBucketDeposit = Deposits.unscaledValueAt(deposits_, params_.toIndex);
- uint256 toBucketScale = Deposits.scale(deposits_, params_.toIndex);
- uint256 toBucketDeposit = Maths.wmul(toBucketScale, unscaledToBucketDeposit);
- vars.toBucketPrice = _priceAt(params_.toIndex);
+ vars.toBucketUnscaledDeposit = Deposits.unscaledValueAt(deposits_, params_.toIndex);
+ vars.toBucketScale = Deposits.scale(deposits_, params_.toIndex);
+ vars.toBucketDeposit = Maths.wmul(vars.toBucketUnscaledDeposit, vars.toBucketScale);
+
toBucketLPs_ = Buckets.quoteTokensToLPs(
toBucket.collateral,
toBucket.lps,
- toBucketDeposit,
+ vars.toBucketDeposit,
vars.amountToMove,
vars.toBucketPrice
);
- Deposits.unscaledAdd(deposits_, params_.toIndex, Maths.wdiv(vars.amountToMove, toBucketScale));
+ Deposits.unscaledAdd(deposits_, params_.toIndex, Maths.wdiv(vars.amountToMove, vars.toBucketScale));
lup_ = _lup(deposits_, poolState_.debt);
vars.htp = Maths.wmul(params_.thresholdPrice, poolState_.inflator);
// check loan book's htp against new lup, revert if move drives LUP below HTP
- if (params_.fromIndex < params_.toIndex) if(vars.htp > lup_) revert LUPBelowHTP();
+ if (params_.fromIndex < params_.toIndex && vars.htp > lup_) revert LUPBelowHTP();
- // update lender LPs balance in from bucket
- fromBucketLender.lps -= fromBucketRedeemedLPs_;
+ // update lender and bucket LPs balance in from bucket
+ vars.fromBucketRemainingLPs = vars.fromBucketLPs - fromBucketRedeemedLPs_;
- // update lender LPs balance and deposit time in target bucket
- Lender storage toBucketLender = toBucket.lenders[msg.sender];
+ if (vars.fromBucketCollateral == 0 && vars.fromBucketRemainingDeposit == 0 && vars.fromBucketRemainingLPs != 0) {
+ emit BucketBankruptcy(params_.fromIndex, vars.fromBucketRemainingLPs);
+ fromBucket.lps = 0;
+ fromBucket.bankruptcyTime = block.timestamp;
+ } else {
+ // update lender and bucket LPs balance
+ fromBucketLender.lps -= fromBucketRedeemedLPs_;
+
+ fromBucket.lps = vars.fromBucketRemainingLPs;
+ }
- if (vars.toBucketBankruptcyTime >= toBucketLender.depositTime) toBucketLender.lps = toBucketLPs_;
- else toBucketLender.lps += toBucketLPs_;
+ // update lender and bucket LPs balance in target bucket
+ Lender storage toBucketLender = toBucket.lenders[msg.sender];
+ if (vars.toBucketBankruptcyTime >= toBucketLender.depositTime) {
+ toBucketLender.lps = toBucketLPs_;
+ } else {
+ toBucketLender.lps += toBucketLPs_;
+ }
// set deposit time to the greater of the lender's from bucket and the target bucket's last bankruptcy timestamp + 1 so deposit won't get invalidated
toBucketLender.depositTime = Maths.max(vars.fromBucketDepositTime, vars.toBucketBankruptcyTime + 1);
- // update buckets LPs balance
- fromBucket.lps -= fromBucketRedeemedLPs_;
- toBucket.lps += toBucketLPs_;
+ // update bucket LPs balance
+ toBucket.lps += toBucketLPs_;
emit MoveQuoteToken(
msg.sender,
@@ -320,7 +344,7 @@ library LenderActions {
RemoveDepositParams memory removeParams;
- if (bucket.bankruptcyTime < lender.depositTime) removeParams.lpConstraint = lender.lps;
+ if (bucket.bankruptcyTime < depositTime) removeParams.lpConstraint = lender.lps;
if (removeParams.lpConstraint == 0) revert NoClaim(); // revert if no LP to claim
@@ -331,8 +355,8 @@ library LenderActions {
removeParams.index = params_.index;
removeParams.dustLimit = poolState_.quoteDustLimit;
- uint256 unscaledRemaining;
- (removedAmount_, redeemedLPs_, unscaledRemaining) = _removeMaxDeposit(
+ uint256 scaledRemaining;
+ (removedAmount_, redeemedLPs_, scaledRemaining) = _removeMaxDeposit(
deposits_,
removeParams
);
@@ -351,16 +375,16 @@ library LenderActions {
// check loan book's htp against new lup
if (htp > lup_) revert LUPBelowHTP();
- // update lender and bucket LPs balances
- lender.lps -= redeemedLPs_;
-
uint256 lpsRemaining = removeParams.bucketLPs - redeemedLPs_;
- if (removeParams.bucketCollateral == 0 && unscaledRemaining == 0 && lpsRemaining != 0) {
+ if (removeParams.bucketCollateral == 0 && scaledRemaining == 0 && lpsRemaining != 0) {
emit BucketBankruptcy(params_.index, lpsRemaining);
bucket.lps = 0;
bucket.bankruptcyTime = block.timestamp;
} else {
+ // update lender and bucket LPs balances
+ lender.lps -= redeemedLPs_;
+
bucket.lps = lpsRemaining;
}
@@ -375,6 +399,8 @@ library LenderActions {
* @dev reverts on:
* - not enough collateral InsufficientCollateral()
* - insufficient LPs InsufficientLPs()
+ * @dev emit events:
+ * - BucketBankruptcy
*/
function removeCollateral(
mapping(uint256 => Bucket) storage buckets_,
@@ -388,13 +414,14 @@ library LenderActions {
if (amount_ > bucketCollateral) revert InsufficientCollateral();
- uint256 bucketPrice = _priceAt(index_);
- uint256 bucketLPs = bucket.lps;
+ uint256 bucketPrice = _priceAt(index_);
+ uint256 bucketLPs = bucket.lps;
+ uint256 bucketDeposit = Deposits.valueAt(deposits_, index_);
lpAmount_ = Buckets.collateralToLPs(
bucketCollateral,
bucketLPs,
- Deposits.valueAt(deposits_, index_),
+ bucketDeposit,
amount_,
bucketPrice
);
@@ -405,12 +432,27 @@ library LenderActions {
if (bucket.bankruptcyTime < lender.depositTime) lenderLpBalance = lender.lps;
if (lenderLpBalance == 0 || lpAmount_ > lenderLpBalance) revert InsufficientLPs();
- // update lender LPs balance
- lender.lps -= lpAmount_;
-
// update bucket LPs and collateral balance
- bucket.lps -= Maths.min(bucketLPs, lpAmount_);
- bucket.collateral -= Maths.min(bucketCollateral, amount_);
+ bucketLPs -= lpAmount_;
+
+ // If clearing out the bucket collateral, ensure it's zeroed out
+ if (bucketLPs == 0 && bucketDeposit == 0) {
+ amount_ = bucketCollateral;
+ }
+
+ bucketCollateral -= Maths.min(bucketCollateral, amount_);
+ bucket.collateral = bucketCollateral;
+
+ if (bucketCollateral == 0 && bucketDeposit == 0 && bucketLPs != 0) {
+ emit BucketBankruptcy(index_, bucketLPs);
+ bucket.lps = 0;
+ bucket.bankruptcyTime = block.timestamp;
+ } else {
+ // update lender LPs balance
+ lender.lps -= lpAmount_;
+
+ bucket.lps = bucketLPs;
+ }
}
/**
@@ -507,7 +549,7 @@ library LenderActions {
* - invalid index InvalidIndex()
* - no allowance NoAllowance()
* @dev emit events:
- * - TransferLPTokens
+ * - TransferLPs
*/
function transferLPs(
mapping(uint256 => Bucket) storage buckets_,
@@ -516,6 +558,9 @@ library LenderActions {
address newOwner_,
uint256[] calldata indexes_
) external {
+ // revert if new owner address is the same as old owner address
+ if (owner_ == newOwner_) revert TransferToSameOwner();
+
uint256 indexesLength = indexes_.length;
uint256 tokensTransferred;
@@ -539,7 +584,7 @@ library LenderActions {
delete allowances_[owner_][newOwner_][index]; // delete allowance
- // move lp tokens to the new owner address
+ // move lps to the new owner address
Lender storage newLender = bucket.lenders[newOwner_];
newLender.lps += transferAmount;
@@ -554,7 +599,7 @@ library LenderActions {
unchecked { ++i; }
}
- emit TransferLPTokens(owner_, newOwner_, indexes_, tokensTransferred);
+ emit TransferLPs(owner_, newOwner_, indexes_, tokensTransferred);
}
/**************************/
@@ -600,9 +645,13 @@ library LenderActions {
collateralAmount_ = Maths.min(maxAmount_, bucketCollateral);
// determine how much LP would be required to remove the requested amount
- uint256 collateralValue = Maths.wmul(bucketPrice, bucketCollateral);
- uint256 lpsForAllCollateral = Maths.rmul(bucketLPs, Maths.wwdivr(collateralValue, collateralValue + bucketDeposit));
- uint256 requiredLPs = Maths.rmul(lpsForAllCollateral, Maths.wwdivr(collateralAmount_, bucketCollateral));
+ uint256 requiredLPs = Buckets.collateralToLPs(
+ bucketCollateral,
+ bucketLPs,
+ bucketDeposit,
+ collateralAmount_,
+ bucketPrice
+ );
// limit withdrawal by the lender's LPB
if (requiredLPs <= lenderLpBalance) {
@@ -610,21 +659,28 @@ library LenderActions {
lpAmount_ = requiredLPs;
} else {
lpAmount_ = lenderLpBalance;
- collateralAmount_ = Maths.wmul(Maths.rrdivw(lenderLpBalance,lpsForAllCollateral), bucketCollateral);
+ collateralAmount_ = Maths.wmul(Maths.wdiv(lenderLpBalance, requiredLPs), collateralAmount_);
}
- // update lender LPs balance
- lender.lps -= lpAmount_;
-
// update bucket LPs and collateral balance
- bucketLPs -= Maths.min(bucketLPs, lpAmount_);
+ bucketLPs -= Maths.min(bucketLPs, lpAmount_);
+
+ // If clearing out the bucket collateral, ensure it's zeroed out
+ if (bucketLPs == 0 && bucketDeposit == 0) {
+ collateralAmount_ = bucketCollateral;
+ }
+
bucketCollateral -= Maths.min(bucketCollateral, collateralAmount_);
- bucket.collateral = bucketCollateral;
+ bucket.collateral = bucketCollateral;
+
if (bucketCollateral == 0 && bucketDeposit == 0 && bucketLPs != 0) {
emit BucketBankruptcy(index_, bucketLPs);
bucket.lps = 0;
bucket.bankruptcyTime = block.timestamp;
} else {
+ // update lender LPs balance
+ lender.lps -= lpAmount_;
+
bucket.lps = bucketLPs;
}
}
@@ -635,69 +691,61 @@ library LenderActions {
* @dev write state:
* - Deposits.unscaledRemove (remove amount in Fenwick tree, from index):
* - update values array state
- * @return removedAmount_ Amount of scaled deposit removed.
- * @return redeemedLPs_ Amount of bucket LPs corresponding for calculated unscaled deposit amount.
+ * @return removedAmount_ Amount of scaled deposit removed.
+ * @return redeemedLPs_ Amount of bucket LPs corresponding for calculated scaled deposit amount.
+ * @return scaledRemaining_ Amount of scaled deposit remaining.
*/
function _removeMaxDeposit(
DepositsState storage deposits_,
RemoveDepositParams memory params_
- ) internal returns (uint256 removedAmount_, uint256 redeemedLPs_, uint256 unscaledRemaining_) {
+ ) internal returns (uint256 removedAmount_, uint256 redeemedLPs_, uint256 scaledRemaining_) {
uint256 unscaledDepositAvailable = Deposits.unscaledValueAt(deposits_, params_.index);
if (unscaledDepositAvailable == 0) revert InsufficientLiquidity(); // revert if there's no liquidity available to remove
uint256 depositScale = Deposits.scale(deposits_, params_.index);
- uint256 unscaledExchangeRate = Buckets.getUnscaledExchangeRate(
+ uint256 scaledDepositAvailable = Maths.wmul(unscaledDepositAvailable, depositScale);
+
+ uint256 exchangeRate = Buckets.getExchangeRate(
params_.bucketCollateral,
params_.bucketLPs,
- unscaledDepositAvailable,
- depositScale,
+ scaledDepositAvailable,
params_.price
);
// Below is pseudocode explaining the logic behind finding the constrained amount of deposit and LPB
- // unscaledRemovedAmount is constrained by the de-scaled maxAmount(in QT), the unscaledDeposit constraint, and
- // the lender LPB exchange rate in unscaled deposit-to-LPB for the bucket:
- // unscaledRemovedAmount = min ( maxAmount_/scale, unscaledDeposit, lenderLPsBalance*unscaledExchangeRate)
- // redeemedLPs_ = min ( maxAmount_/(unscaledExchangeRate*scale), unscaledDeposit/unscaledExchangeRate, lenderLPsBalance)
+ // scaledRemovedAmount is constrained by the scaled maxAmount(in QT), the scaledDeposit constraint, and
+ // the lender LPB exchange rate in scaled deposit-to-LPB for the bucket:
+ // scaledRemovedAmount = min ( maxAmount_, scaledDeposit, lenderLPsBalance*exchangeRate)
+ // redeemedLPs_ = min ( maxAmount_/scaledExchangeRate, scaledDeposit/exchangeRate, lenderLPsBalance)
- uint256 unscaledRemovedAmount;
- uint256 unscaledLpConstraint = Maths.rmul(params_.lpConstraint, unscaledExchangeRate);
+ uint256 scaledLpConstraint = Maths.wmul(params_.lpConstraint, exchangeRate);
if (
- params_.depositConstraint < Maths.wmul(unscaledDepositAvailable, depositScale) &&
- Maths.wwdivr(params_.depositConstraint, depositScale) < unscaledLpConstraint
+ params_.depositConstraint < scaledDepositAvailable &&
+ params_.depositConstraint < scaledLpConstraint
) {
// depositConstraint is binding constraint
- unscaledRemovedAmount = Maths.wdiv(params_.depositConstraint, depositScale);
- redeemedLPs_ = Maths.wrdivr(unscaledRemovedAmount, unscaledExchangeRate);
- } else if (Maths.wadToRay(unscaledDepositAvailable) < unscaledLpConstraint) {
- // unscaledDeposit is binding constraint
- unscaledRemovedAmount = unscaledDepositAvailable;
- redeemedLPs_ = Maths.wrdivr(unscaledRemovedAmount, unscaledExchangeRate);
+ removedAmount_ = params_.depositConstraint;
+ redeemedLPs_ = Maths.wdiv(removedAmount_, exchangeRate);
+ } else if (scaledDepositAvailable < scaledLpConstraint) {
+ // scaledDeposit is binding constraint
+ removedAmount_ = scaledDepositAvailable;
+ redeemedLPs_ = Maths.wdiv(removedAmount_, exchangeRate);
} else {
// redeeming all LPs
- redeemedLPs_ = params_.lpConstraint;
- unscaledRemovedAmount = Maths.rayToWad(Maths.rmul(redeemedLPs_, unscaledExchangeRate));
+ redeemedLPs_ = params_.lpConstraint;
+ removedAmount_ = Maths.wmul(redeemedLPs_, exchangeRate);
}
// If clearing out the bucket deposit, ensure it's zeroed out
if (redeemedLPs_ == params_.bucketLPs) {
- unscaledRemovedAmount = unscaledDepositAvailable;
+ removedAmount_ = scaledDepositAvailable;
}
- // calculate the scaled amount removed from deposits
- removedAmount_ = Maths.wmul(depositScale, unscaledRemovedAmount);
- // calculate amount remaining
- unscaledRemaining_ = unscaledDepositAvailable - unscaledRemovedAmount;
- uint256 remaining = Maths.wmul(depositScale, unscaledRemaining_);
-
- // abandon dust amounts upon last withdrawal
- if (remaining < params_.dustLimit && redeemedLPs_ == params_.bucketLPs) {
- unscaledRemovedAmount = unscaledDepositAvailable;
- unscaledRemaining_ = 0;
- }
+ scaledRemaining_ = scaledDepositAvailable - removedAmount_;
+ uint256 unscaledRemovedAmount = Maths.min(unscaledDepositAvailable, Maths.wdiv(removedAmount_, depositScale));
Deposits.unscaledRemove(deposits_, params_.index, unscaledRemovedAmount); // update FenwickTree
}
diff --git a/src/libraries/helpers/PoolHelper.sol b/src/libraries/helpers/PoolHelper.sol
index c8b279a4f..dcb7a79f7 100644
--- a/src/libraries/helpers/PoolHelper.sol
+++ b/src/libraries/helpers/PoolHelper.sol
@@ -191,7 +191,7 @@ import { Maths } from '../internal/Maths.sol';
// max collateral to lps
uint256 rate = Buckets.getExchangeRate(bucketCollateral_, bucketLPs_, deposit_, bucketPrice_);
- collateralAmount_ = Maths.rwdivw(Maths.rmul(lenderLPsBalance_, rate), bucketPrice_);
+ collateralAmount_ = Maths.wdiv(Maths.wmul(lenderLPsBalance_, rate), bucketPrice_);
if (collateralAmount_ > bucketCollateral_) {
// user is owed more collateral than is available in the bucket
@@ -219,7 +219,7 @@ import { Maths } from '../internal/Maths.sol';
) pure returns (uint256 quoteTokenAmount_) {
uint256 rate = Buckets.getExchangeRate(bucketCollateral_, bucketLPs_, deposit_, bucketPrice_);
- quoteTokenAmount_ = Maths.rayToWad(Maths.rmul(lenderLPsBalance_, rate));
+ quoteTokenAmount_ = Maths.wmul(lenderLPsBalance_, rate);
if (quoteTokenAmount_ > deposit_) quoteTokenAmount_ = deposit_;
if (quoteTokenAmount_ > maxQuoteToken_) quoteTokenAmount_ = maxQuoteToken_;
diff --git a/src/libraries/helpers/RevertsHelper.sol b/src/libraries/helpers/RevertsHelper.sol
index 62e9473ec..fe2fc1bc7 100644
--- a/src/libraries/helpers/RevertsHelper.sol
+++ b/src/libraries/helpers/RevertsHelper.sol
@@ -10,7 +10,7 @@ import {
PoolBalancesState
} from '../../interfaces/pool/commons/IPoolState.sol';
-import { _minDebtAmount } from './PoolHelper.sol';
+import { _minDebtAmount, _priceAt } from './PoolHelper.sol';
import { Loans } from '../internal/Loans.sol';
import { Deposits } from '../internal/Deposits.sol';
@@ -20,7 +20,9 @@ import { Maths } from '../internal/Maths.sol';
error AuctionNotCleared();
error AmountLTMinDebt();
error DustAmountNotExceeded();
+ error LimitIndexExceeded();
error RemoveDepositLockedByAuctionDebt();
+ error TransactionExpired();
/**
* @notice Called by LPB removal functions assess whether or not LPB is locked.
@@ -58,6 +60,37 @@ import { Maths } from '../internal/Maths.sol';
}
}
+ /**
+ * @notice Check if LUP is at or above index limit provided by borrower.
+ * @notice Prevents stale transactions and certain MEV manipulations.
+ * @param newLup_ New LUP as a result of the borrower action.
+ * @param limitIndex_ Limit price index provided by user creating the TX.
+ */
+ function _revertIfLupDroppedBelowLimit(
+ uint256 newLup_,
+ uint256 limitIndex_
+ ) pure {
+ if (newLup_ < _priceAt(limitIndex_)) revert LimitIndexExceeded();
+ }
+
+ /**
+ * @notice Check if expiration provided by user has met or exceeded current block height timestamp.
+ * @notice Prevents stale transactions interacting with the pool at potentially unfavorable prices.
+ * @param expiry_ Expiration provided by user when creating the TX.
+ */
+ function _revertOnExpiry(
+ uint256 expiry_
+ ) view {
+ if (block.timestamp >= expiry_) revert TransactionExpired();
+ }
+
+ /**
+ * @notice Called when borrower debt changes, ensuring minimum debt rules are honored.
+ * @param loans_ Loans heap, used to determine loan count.
+ * @param poolDebt_ Total pool debt, used to calculate average debt.
+ * @param borrowerDebt_ New debt for the borrower, assuming the current transaction succeeds.
+ * @param quoteDust_ Smallest amount of quote token when can be transferred, determined by token scale.
+ */
function _revertOnMinDebt(
LoansState storage loans_,
uint256 poolDebt_,
@@ -65,11 +98,9 @@ import { Maths } from '../internal/Maths.sol';
uint256 quoteDust_
) view {
if (borrowerDebt_ != 0) {
+ if (borrowerDebt_ < quoteDust_) revert DustAmountNotExceeded();
uint256 loansCount = Loans.noOfLoans(loans_);
- if (loansCount >= 10) {
+ if (loansCount >= 10)
if (borrowerDebt_ < _minDebtAmount(poolDebt_, loansCount)) revert AmountLTMinDebt();
- } else {
- if (borrowerDebt_ < quoteDust_) revert DustAmountNotExceeded();
- }
}
}
diff --git a/src/libraries/internal/Buckets.sol b/src/libraries/internal/Buckets.sol
index a192d9305..8075f9307 100644
--- a/src/libraries/internal/Buckets.sol
+++ b/src/libraries/internal/Buckets.sol
@@ -107,7 +107,7 @@ library Buckets {
) internal pure returns (uint256 lps_) {
uint256 rate = getExchangeRate(bucketCollateral_, bucketLPs_, deposit_, bucketPrice_);
- lps_ = (collateral_ * bucketPrice_ * 1e18 + rate / 2) / rate;
+ lps_ = Maths.wdiv(Maths.wmul(collateral_, bucketPrice_), rate);
}
/**
@@ -126,8 +126,8 @@ library Buckets {
uint256 quoteTokens_,
uint256 bucketPrice_
) internal pure returns (uint256) {
- return Maths.rdiv(
- Maths.wadToRay(quoteTokens_),
+ return Maths.wdiv(
+ quoteTokens_,
getExchangeRate(bucketCollateral_, bucketLPs_, deposit_, bucketPrice_)
);
}
@@ -145,30 +145,7 @@ library Buckets {
uint256 bucketDeposit_,
uint256 bucketPrice_
) internal pure returns (uint256) {
- return bucketLPs_ == 0
- ? Maths.RAY
- : (bucketDeposit_ * 1e18 + bucketPrice_ * bucketCollateral_) * 1e18 / bucketLPs_;
- // 10^36 * 1e18 / 10^27 = 10^54 / 10^27 = 10^27
- }
-
- /**
- * @notice Returns the unscaled exchange rate for a given bucket.
- * @param bucketCollateral_ Amount of collateral in bucket.
- * @param bucketLPs_ Amount of LPs in bucket.
- * @param bucketUnscaledDeposit_ The amount of unscaled Fenwick tree amount in bucket.
- * @param bucketScale_ Bucket scale factor
- * @param bucketPrice_ Bucket's price.
- */
- function getUnscaledExchangeRate(
- uint256 bucketCollateral_,
- uint256 bucketLPs_,
- uint256 bucketUnscaledDeposit_,
- uint256 bucketScale_,
- uint256 bucketPrice_
- ) internal pure returns (uint256) {
- return bucketLPs_ == 0
- ? Maths.RAY
- : (bucketUnscaledDeposit_ + bucketPrice_ * bucketCollateral_ / bucketScale_ ) * 10**36 / bucketLPs_;
- // 10^18 * 1e36 / 10^27 = 10^54 / 10^27 = 10^27
+ return bucketLPs_ == 0 ? Maths.WAD :
+ Maths.wdiv(bucketDeposit_ + Maths.wmul(bucketPrice_, bucketCollateral_), bucketLPs_);
}
}
diff --git a/src/libraries/internal/Loans.sol b/src/libraries/internal/Loans.sol
index fefe20cc3..ff1bf1200 100644
--- a/src/libraries/internal/Loans.sol
+++ b/src/libraries/internal/Loans.sol
@@ -64,14 +64,14 @@ library Loans {
* - remove:
* - remove loan from loans array
* - update borrower in address => borrower mapping
- * @param loans_ Holds loan heap data.
- * @param borrower_ Borrower struct with borrower details.
- * @param borrowerAddress_ Borrower's address to update.
- * @param borrowerAccruedDebt_ Borrower's current debt.
- * @param poolRate_ Pool's current rate.
- * @param lup_ Current LUP.
- * @param inAuction_ Whether the loan is in auction or not.
- * @param t0NpUpdate_ Whether the neutral price of borrower should be updated or not.
+ * @param loans_ Holds loan heap data.
+ * @param borrower_ Borrower struct with borrower details.
+ * @param borrowerAddress_ Borrower's address to update.
+ * @param poolDebt_ Pool's current debt.
+ * @param poolRate_ Pool's current rate.
+ * @param lup_ Current LUP.
+ * @param inAuction_ Whether the loan is in auction or not.
+ * @param t0NpUpdate_ Whether the neutral price of borrower should be updated or not.
*/
function update(
LoansState storage loans_,
@@ -79,7 +79,7 @@ library Loans {
DepositsState storage deposits_,
Borrower memory borrower_,
address borrowerAddress_,
- uint256 borrowerAccruedDebt_,
+ uint256 poolDebt_,
uint256 poolRate_,
uint256 lup_,
bool inAuction_,
@@ -111,7 +111,7 @@ library Loans {
if (t0NpUpdate_) {
if (t0ThresholdPrice != 0) {
uint256 loansInPool = loans_.loans.length - 1 + auctions_.noOfAuctions;
- uint256 curMomp = _priceAt(Deposits.findIndexOfSum(deposits_, Maths.wdiv(borrowerAccruedDebt_, loansInPool * 1e18)));
+ uint256 curMomp = _priceAt(Deposits.findIndexOfSum(deposits_, Maths.wdiv(poolDebt_, loansInPool * 1e18)));
borrower_.t0Np = (1e18 + poolRate_) * curMomp * t0ThresholdPrice / lup_ / 1e18;
} else {
diff --git a/src/libraries/internal/Maths.sol b/src/libraries/internal/Maths.sol
index 75d12c68f..c01f6e059 100644
--- a/src/libraries/internal/Maths.sol
+++ b/src/libraries/internal/Maths.sol
@@ -9,7 +9,6 @@ pragma solidity 0.8.14;
library Maths {
uint256 internal constant WAD = 1e18;
- uint256 internal constant RAY = 10**27;
function wmul(uint256 x, uint256 y) internal pure returns (uint256) {
return (x * y + 1e18 / 2) / 1e18;
@@ -35,30 +34,6 @@ library Maths {
return (x * y + 10**27 / 2) / 10**27;
}
- function rdiv(uint256 x, uint256 y) internal pure returns (uint256) {
- return (x * 10**27 + y / 2) / y;
- }
-
- /** @notice Divides a WAD by a RAY and returns a RAY */
- function wrdivr(uint256 x, uint256 y) internal pure returns (uint256) {
- return (x * 1e36 + y / 2) / y;
- }
-
- /** @notice Divides a WAD by a WAD and returns a RAY */
- function wwdivr(uint256 x, uint256 y) internal pure returns (uint256) {
- return (x * 1e27 + y / 2) / y;
- }
-
- /** @notice Divides a RAY by another RAY and returns a WAD */
- function rrdivw(uint256 x, uint256 y) internal pure returns (uint256) {
- return (x * 1e18 + y / 2) / y;
- }
-
- /** @notice Divides a RAY by a WAD and returns a WAD */
- function rwdivw(uint256 x, uint256 y) internal pure returns (uint256) {
- return (x * 1e9 + y / 2) / y;
- }
-
function rpow(uint256 x, uint256 n) internal pure returns (uint256 z) {
z = n % 2 != 0 ? x : 10**27;
@@ -71,10 +46,6 @@ library Maths {
}
}
- function wadToRay(uint256 x) internal pure returns (uint256) {
- return x * 10**9;
- }
-
function rayToWad(uint256 x) internal pure returns (uint256) {
return (x + 10**9 / 2) / 10**9;
}
diff --git a/tests/INVARIANTS.md b/tests/INVARIANTS.md
new file mode 100644
index 000000000..8799e40a3
--- /dev/null
+++ b/tests/INVARIANTS.md
@@ -0,0 +1,69 @@
+# Ajna Pool Invariants
+
+## Collateral
+- #### ERC20:
+ - **CT1**: pool collateral token balance (`Collateral.balanceOf(pool)`) = sum of collateral balances across all borrowers (`Borrower.collateral`) + sum of claimable collateral across all buckets (`Bucket.collateral`)
+- #### NFT:
+ - **CT2**: number of tokens owned by the pool (`Collateral.balanceOf(pool)`) * `1e18` = sum of collateral across all borrowers (`Borrower.collateral`) + sum of claimable collateral across all buckets (`Bucket.collateral`)
+ - **CT3**: number of tokens owned by the pool (`Collateral.balanceOf(pool)` = length of borrower array token ids (`ERC721Pool.borrowerTokenIds.length`) + length of buckets array token ids (`ERC721Pool.bucketTokenIds.length`)
+ - **CT4**: number of borrower token ids (`ERC721Pool.borrowerTokenIds.length`) * `1e18` <= borrower balance (`Borrower.collateral`) Note: can be lower in case when fractional collateral that is rebalanced / moved to buckets claimable token ids
+ - **CT5**: token ids in buckets array (`ERC721Pool.bucketTokenIds`) and in borrowers array (`ERC721Pool.borrowerTokenIds`) are owned by pool contract (`Collateral.ownerOf(tokenId)`)
+ - **CT6**: in case of subset pools: token ids in buckets array (`ERC721Pool.bucketTokenIds`) and in borrowers array (`ERC721Pool.borrowerTokenIds`) should have a mapping of `True` in allowed token ids mapping (`ERC721Pool.tokenIdsAllowed`)
+
+- **CT7**: total pledged collateral in pool (`PoolBalancesState.pledgedCollateral`) = sum of collateral balances across all borrowers (`Borrower.collateral`)
+
+## Quote Token
+- **QT1**: pool quote token balance (`Quote.balanceOf(pool)`) >= liquidation bonds (`AuctionsState.totalBondEscrowed`) + pool deposit size (`Pool.depositSize()`) + reserve auction unclaimed amount (`reserveAuction.unclaimed`) - pool t0 debt (`PoolBalancesState.t0Debt`)
+- **QT2**: pool t0 debt (`PoolBalancesState.t0Debt`) = sum of t0 debt across all borrowers (`Borrower.t0Debt`)
+
+## Auctions
+- **A1**: total t0 debt auctioned (`PoolBalancesState.t0DebtInAuction`) = sum of debt across all auctioned borrowers (`Borrower.t0Debt` where borrower's `kickTime != 0`)
+- **A2**: sum of bonds locked in auctions (`Liquidation.bondSize`) = sum of locked balances across all kickers (`Kicker.locked`) = total bond escrowed accumulator (`AuctionsState.totalBondEscrowed`)
+- **A3**: number of borrowers with debt (`LoansState.borrowers.length` with `t0Debt != 0`) = number of loans (`LoansState.loans.length -1`) + number of auctioned borrowers (`AuctionsState.noOfAuctions`)
+- **A4**: number of recorded auctions (`AuctionsState.noOfAuctions`) = length of auctioned borrowers (count of borrowers in `AuctionsState.liquidations` with `kickTime != 0`)
+- **A5**: for each `Liquidation` recorded in liquidation mapping (`AuctionsState.liquidations`) the kicker address (`Liquidation.kicker`) has a locked balance (`Kicker.locked`) equal or greater than liquidation bond size (`Liquidation.bondSize`)
+- **A6**: if a `Liquidation` is not taken then the take flag (`Liquidation.alreadyTaken`) should be `False`, if already taken then the take flag should be `True`
+
+## Loans
+- **L1**: for each `Loan` in loans array (`LoansState.loans`) starting from index 1, the corresponding address (`Loan.borrower`) is not `0x`, the threshold price (`Loan.thresholdPrice`) is different than 0 and the id mapped in indices mapping (`LoansState.indices`) equals index of loan in loans array.
+- **L2**: `Loan` in loans array (`LoansState.loans`) at index 0 has the corresponding address (`Loan.borrower`) equal with `0x` address and the threshold price (`Loan.thresholdPrice`) equal with 0
+- **L3**: Loans array (`LoansState.loans`) is a max-heap with respect to t0-threshold price: the t0TP of loan at index `i` is >= the t0-threshold price of the loans at index `2*i` and `2*i+1`
+
+## Buckets
+- **B1**: sum of LPs of lenders in bucket (`Lender.lps`) = bucket LPs accumulator (`Bucket.lps`)
+- **B2**: bucket LPs accumulator (`Bucket.lps`) = 0 if no deposit / collateral in bucket
+- **B3**: if no collateral or deposit in bucket then the bucket exchange rate is `1e27`
+- **B4**: bankrupt bucket LPs accumulator = 0; lender LPs for deposits before bankruptcy time = 0
+- **B5**: when adding quote tokens: lender deposit time (`Lender.depositTime`) = timestamp of block when deposit happened (`block.timestamp`)
+
+## Interest
+- **I1**: interest rate (`InterestState.interestRate`) cannot be updated more than once in a 12 hours period of time (`InterestState.interestRateUpdate`)
+- **I2**: reserve interest (`ReserveAuctionState.totalInterestEarned`) accrues only once per block (`block.timestamp - InflatorState.inflatorUpdate != 0`) and only if there's debt in the pool (`PoolBalancesState.t0Debt != 0`)
+- **I3**: pool inflator (`InflatorState.inflator`) cannot be updated more than once per block (`block.timestamp - InflatorState.inflatorUpdate != 0`) and equals `1e18` if there's no debt in the pool (`PoolBalancesState.t0Debt != 0`)
+
+## Fenwick tree
+- **F1**: Value represented at index `i` (`Deposits.valueAt(i)`) is equal to the accumulation of scaled values incremented or decremented from index `i`
+- **F2**: For any index `i`, the prefix sum up to and including `i` is the sum of values stored in indices `j<=i`
+- **F3**: For any index `i < MAX_FENWICK_INDEX`, `findIndexOfSum(prefixSum(i)) > i`
+- **F4**: For any index `i`, there is zero deposit above `i` and below `findIndexOfSum(prefixSum(i))`: `prefixSum(findIndexOfSum(prefixSum(i))-1 == prefixSum(i)'
+
+## Exchange rate invariants ##
+- **R1**: Exchange rates are unchanged by pledging collateral
+- **R2**: Exchange rates are unchanged by removing collateral
+- **R3**: Exchange rates are unchanged by depositing quote token into a bucket
+- **R4**: Exchange rates are unchanged by withdrawing deposit (quote token) from a bucket
+- **R5**: Exchange rates are unchanged by adding collateral token into a bucket
+- **R6**: Exchange rates are unchanged by removing collateral token from a bucket
+- **R7**: Exchange rates are unchanged under depositTakes
+- **R8**: Exchange rates are unchanged under arbTakes
+
+## Reserves ##
+- **RE1**: Reserves are unchanged by pledging collateral
+- **RE2**: Reserves are unchanged by removing collateral
+- **RE3**: Reserves are unchanged by depositing quote token into a bucket
+- **RE4**: Reserves are unchanged by withdrawing deposit (quote token) from a bucket after the penalty period hes expired
+- **RE5**: Reserves are unchanged by adding collateral token into a bucket
+- **RE6**: Reserves are unchanged by removing collateral token from a bucket
+- **RE7**: Reserves increase by 7% of the loan quantity upon the first take (including depositTake or arbTake)
+- **RE8**: Reserves are unchanged under takes/depositTakes/arbTakes after the first take
+- **RE9**: Reserves increase by .25% of the debt when a loan is kicked
diff --git a/tests/README.md b/tests/README.md
index 2fc707430..7cccef612 100644
--- a/tests/README.md
+++ b/tests/README.md
@@ -12,6 +12,10 @@ make test-with-gas-report
```bash
make test-load
```
+- run invariant tests:
+```bash
+make test-invariant
+```
- generate code coverage report:
```bash
make coverage
diff --git a/tests/brownie/test_scaled_pool.py b/tests/brownie/test_scaled_pool.py
index de812d77e..c926480d1 100644
--- a/tests/brownie/test_scaled_pool.py
+++ b/tests/brownie/test_scaled_pool.py
@@ -7,13 +7,14 @@
def test_quote_deposit_move_remove_scaled(
lenders,
scaled_pool,
+ chain,
capsys,
test_utils
):
with test_utils.GasWatcher(["addQuoteToken", "moveQuoteToken", "removeQuoteToken"]):
add_txes = []
for i in range(2530, 2550):
- tx = scaled_pool.addQuoteToken(100 * 10**18, i, {"from": lenders[0]})
+ tx = scaled_pool.addQuoteToken(100 * 10**18, i, chain.time() + 30, {"from": lenders[0]})
add_txes.append(tx)
with capsys.disabled():
print("\n==================================")
@@ -24,7 +25,7 @@ def test_quote_deposit_move_remove_scaled(
move_txes = []
for i in range(2530, 2550):
- tx = scaled_pool.moveQuoteToken(100 * 10**18, i, i + 30, {"from": lenders[0]})
+ tx = scaled_pool.moveQuoteToken(100 * 10**18, i, i + 30, chain.time() + 30, {"from": lenders[0]})
move_txes.append(tx)
with capsys.disabled():
print("\n==================================")
@@ -49,14 +50,16 @@ def test_borrow_repay_scaled(
lenders,
borrowers,
scaled_pool,
+ chain,
capsys,
test_utils
):
with test_utils.GasWatcher(["addQuoteToken", "drawDebt", "repayDebt"]):
- scaled_pool.addQuoteToken(100 * 10**18, 2550, {"from": lenders[0]})
- scaled_pool.addQuoteToken(100 * 10**18, 2560, {"from": lenders[0]})
- scaled_pool.addQuoteToken(100 * 10**18, 2570, {"from": lenders[0]})
+ expiry = chain.time() + 30
+ scaled_pool.addQuoteToken(100 * 10**18, 2550, expiry, {"from": lenders[0]})
+ scaled_pool.addQuoteToken(100 * 10**18, 2560, expiry, {"from": lenders[0]})
+ scaled_pool.addQuoteToken(100 * 10**18, 2570, expiry, {"from": lenders[0]})
col_txes = []
for i in range(10):
@@ -85,11 +88,11 @@ def test_borrow_repay_scaled(
print(f"Transaction: {i} | {test_utils.get_usage(txes[i].gas_used)}")
repay_txes = []
- tx = scaled_pool.repayDebt(borrowers[0], 110 * 10**18, 0, {"from": borrowers[0]})
+ tx = scaled_pool.repayDebt(borrowers[0], 110 * 10**18, 0, borrowers[0], 7388, {"from": borrowers[0]})
repay_txes.append(tx)
- tx = scaled_pool.repayDebt(borrowers[0], 110 * 10**18, 0, {"from": borrowers[0]})
+ tx = scaled_pool.repayDebt(borrowers[0], 110 * 10**18, 0, borrowers[0], 7388, {"from": borrowers[0]})
repay_txes.append(tx)
- tx = scaled_pool.repayDebt(borrowers[0], 50 * 10**18, 0, {"from": borrowers[0]})
+ tx = scaled_pool.repayDebt(borrowers[0], 50 * 10**18, 0, borrowers[0], 7388, {"from": borrowers[0]})
repay_txes.append(tx)
with capsys.disabled():
print("\n==================================")
diff --git a/tests/brownie/test_stable_volatile.py b/tests/brownie/test_stable_volatile.py
index e121ba1d4..49f6c1762 100644
--- a/tests/brownie/test_stable_volatile.py
+++ b/tests/brownie/test_stable_volatile.py
@@ -76,7 +76,7 @@ def borrowers(ajna_protocol, scaled_pool):
def pool_helper(ajna_protocol, scaled_pool, lenders, borrowers, test_utils, chain):
pool_helper = PoolHelper(ajna_protocol, scaled_pool)
# Adds liquidity to an empty pool and draws debt up to a target utilization
- add_initial_liquidity(lenders, pool_helper)
+ add_initial_liquidity(lenders, pool_helper, chain)
draw_initial_debt(borrowers, pool_helper, test_utils, chain, target_utilization=GOAL_UTILIZATION)
global last_triggered
last_triggered = dict.fromkeys(range(0, max(NUM_LENDERS, NUM_BORROWERS)), 0)
@@ -84,7 +84,7 @@ def pool_helper(ajna_protocol, scaled_pool, lenders, borrowers, test_utils, chai
return pool_helper
-def add_initial_liquidity(lenders, pool_helper):
+def add_initial_liquidity(lenders, pool_helper, chain):
# Lenders 0-9 will be "new to the pool" upon actual testing
# TODO: determine this non-arbitrarily
deposit_amount = MIN_PARTICIPATION * 10**18
@@ -97,7 +97,7 @@ def add_initial_liquidity(lenders, pool_helper):
price_index = price_position + MAX_BUCKET
log(f" lender {i} depositing {deposit_amount/1e18} into bucket {price_index} "
f"({pool_helper.indexToPrice(price_index) / 1e18:.1f})")
- pool_helper.pool.addQuoteToken(deposit_amount, price_index, {"from": lenders[i]})
+ pool_helper.pool.addQuoteToken(deposit_amount, price_index, chain.time() + 30, {"from": lenders[i]})
def draw_initial_debt(borrowers, pool_helper, test_utils, chain, target_utilization):
@@ -249,7 +249,7 @@ def draw_and_bid(lenders, borrowers, start_from, pool_helper, chain, test_utils,
# Add or remove liquidity
if user_index < NUM_LENDERS:
if random.choice([True, False]):
- price = add_quote_token(lenders[user_index], user_index, pool_helper)
+ price = add_quote_token(lenders[user_index], user_index, pool_helper, chain)
if price:
buckets_deposited[user_index].add(price)
else:
@@ -294,7 +294,7 @@ def draw_debt(borrower, borrower_index, pool_helper, test_utils, collateralizati
tx = pledge_and_borrow(pool_helper, borrower, borrower_index, collateral_to_deposit, borrow_amount, test_utils)
-def add_quote_token(lender, lender_index, pool_helper):
+def add_quote_token(lender, lender_index, pool_helper, chain):
dai = pool_helper.quoteToken()
index_offset = ((lender_index % 6) - 2) * 2
lup_index = pool_helper.lupIndex()
@@ -307,7 +307,7 @@ def add_quote_token(lender, lender_index, pool_helper):
return None
log(f" lender {lender_index:>4} adding {quantity / 10**18:.1f} liquidity at {deposit_price / 10**18:.1f}")
- tx = pool_helper.pool.addQuoteToken(quantity, deposit_index, {"from": lender})
+ tx = pool_helper.pool.addQuoteToken(quantity, deposit_index, chain.time() + 15, {"from": lender})
return deposit_price
@@ -364,7 +364,7 @@ def repay_debt(borrower, borrower_index, pool_helper, test_utils):
f"is withdrawing {collateral_deposited/1e18:.1f} collateral")
# assert collateral_to_withdraw > 0
repay_amount = int(repay_amount * 1.01)
- tx = pool_helper.pool.repayDebt(borrower, repay_amount, collateral_to_withdraw, {"from": borrower})
+ tx = pool_helper.pool.repayDebt(borrower, repay_amount, collateral_to_withdraw, borrower, 7388, {"from": borrower})
elif debt == 0:
log(f" borrower {borrower_index:>4} has no debt to repay")
else:
diff --git a/tests/forge/ERC20Pool/ERC20DSTestPlus.sol b/tests/forge/ERC20Pool/ERC20DSTestPlus.sol
index 38badd274..0cc630f02 100644
--- a/tests/forge/ERC20Pool/ERC20DSTestPlus.sol
+++ b/tests/forge/ERC20Pool/ERC20DSTestPlus.sol
@@ -75,8 +75,9 @@ abstract contract ERC20DSTestPlus is DSTestPlus, IERC20PoolEvents {
(, uint256 bucketQuote, uint256 bucketCollateral, , ,) = _poolUtils.bucketInfo(address(_pool), bucketIndex);
(uint256 lenderLpBalance, ) = _pool.lenderInfo(bucketIndex, lender);
- // redeem LP for quote token if available
uint256 lpRedeemed;
+
+ // redeem LP for quote token if available
if(lenderLpBalance != 0 && bucketQuote != 0) {
(, lpRedeemed) = _pool.removeQuoteToken(type(uint256).max, bucketIndex);
lenderLpBalance -= lpRedeemed;
@@ -88,8 +89,6 @@ abstract contract ERC20DSTestPlus is DSTestPlus, IERC20PoolEvents {
lenderLpBalance -= lpRedeemed;
}
- // confirm the redemption amount returned by removal methods is correct
- assertEq(lenderLpBalance, 0);
// confirm the user actually has 0 LPB in the bucket
(lenderLpBalance, ) = _pool.lenderInfo(bucketIndex, lender);
assertEq(lenderLpBalance, 0);
@@ -195,7 +194,7 @@ abstract contract ERC20DSTestPlus is DSTestPlus, IERC20PoolEvents {
lendersDepositedIndex[from].add(index);
bucketsUsed.add(index);
- return ERC20Pool(address(_pool)).addCollateral(amount, index);
+ return ERC20Pool(address(_pool)).addCollateral(amount, index, type(uint256).max);
}
function _addCollateralWithoutCheckingLP(
@@ -218,7 +217,7 @@ abstract contract ERC20DSTestPlus is DSTestPlus, IERC20PoolEvents {
lendersDepositedIndex[from].add(index);
bucketsUsed.add(index);
- return ERC20Pool(address(_pool)).addCollateral(amount, index);
+ return ERC20Pool(address(_pool)).addCollateral(amount, index, type(uint256).max);
}
function _borrow(
@@ -366,7 +365,7 @@ abstract contract ERC20DSTestPlus is DSTestPlus, IERC20PoolEvents {
vm.expectEmit(true, true, false, true);
emit RepayDebt(borrower, repaid, 0, newLup);
_assertQuoteTokenTransferEvent(from, address(_pool), repaid);
- ERC20Pool(address(_pool)).repayDebt(borrower, amount, 0);
+ ERC20Pool(address(_pool)).repayDebt(borrower, amount, 0, borrower, MAX_FENWICK_INDEX);
}
function _repayDebt(
@@ -394,7 +393,23 @@ abstract contract ERC20DSTestPlus is DSTestPlus, IERC20PoolEvents {
_assertCollateralTokenTransferEvent(address(_pool), from, collateralToPull);
}
- ERC20Pool(address(_pool)).repayDebt(borrower, amountToRepay, collateralToPull);
+ ERC20Pool(address(_pool)).repayDebt(borrower, amountToRepay, collateralToPull, borrower, MAX_FENWICK_INDEX);
+ }
+
+ function _repayDebtAndPullToRecipient(
+ address from,
+ address borrower,
+ address recipient,
+ uint256 amountToRepay,
+ uint256 amountRepaid,
+ uint256 collateralToPull,
+ uint256 newLup
+ ) internal {
+ changePrank(from);
+ vm.expectEmit(true, true, false, true);
+ emit RepayDebt(borrower, amountRepaid, collateralToPull, newLup);
+ _assertCollateralTokenTransferEvent(address(_pool), recipient, collateralToPull);
+ ERC20Pool(address(_pool)).repayDebt(borrower, amountToRepay, collateralToPull, recipient, MAX_FENWICK_INDEX);
}
function _repayDebtNoLupCheck(
@@ -407,7 +422,7 @@ abstract contract ERC20DSTestPlus is DSTestPlus, IERC20PoolEvents {
_repayDebt(from, borrower, amountToRepay, amountRepaid, collateralToPull, 0);
}
- function _transferLpTokens(
+ function _transferLPs(
address operator,
address from,
address to,
@@ -416,7 +431,7 @@ abstract contract ERC20DSTestPlus is DSTestPlus, IERC20PoolEvents {
) internal {
changePrank(operator);
vm.expectEmit(true, true, true, true);
- emit TransferLPTokens(from, to, indexes, lpBalance);
+ emit TransferLPs(from, to, indexes, lpBalance);
_pool.transferLPs(from, to, indexes);
for(uint256 i = 0; i < indexes.length ;i++ ){
@@ -439,7 +454,7 @@ abstract contract ERC20DSTestPlus is DSTestPlus, IERC20PoolEvents {
) internal {
changePrank(from);
vm.expectRevert(abi.encodeWithSignature('BucketBankruptcyBlock()'));
- ERC20Pool(address(_pool)).addCollateral(amount, index);
+ ERC20Pool(address(_pool)).addCollateral(amount, index, type(uint256).max);
}
function _assertAddCollateralAtIndex0Revert(
@@ -448,7 +463,7 @@ abstract contract ERC20DSTestPlus is DSTestPlus, IERC20PoolEvents {
) internal {
changePrank(from);
vm.expectRevert(IPoolErrors.InvalidIndex.selector);
- ERC20Pool(address(_pool)).addCollateral(amount, 0);
+ ERC20Pool(address(_pool)).addCollateral(amount, 0, type(uint256).max);
}
function _assertAddCollateralDustRevert(
@@ -458,7 +473,18 @@ abstract contract ERC20DSTestPlus is DSTestPlus, IERC20PoolEvents {
) internal {
changePrank(from);
vm.expectRevert(IPoolErrors.DustAmountNotExceeded.selector);
- ERC20Pool(address(_pool)).addCollateral(amount, index);
+ ERC20Pool(address(_pool)).addCollateral(amount, index, type(uint256).max);
+ }
+
+ function _assertAddCollateralExpiredRevert(
+ address from,
+ uint256 amount,
+ uint256 index,
+ uint256 expiry
+ ) internal {
+ changePrank(from);
+ vm.expectRevert(IPoolErrors.TransactionExpired.selector);
+ ERC20Pool(address(_pool)).addCollateral(amount, index, expiry);
}
function _assertDeployWith0xAddressRevert(
@@ -497,7 +523,17 @@ abstract contract ERC20DSTestPlus is DSTestPlus, IERC20PoolEvents {
) internal {
changePrank(from);
vm.expectRevert(IPoolErrors.InsufficientCollateral.selector);
- ERC20Pool(address(_pool)).repayDebt(from, 0, amount);
+ ERC20Pool(address(_pool)).repayDebt(from, 0, amount, from, MAX_FENWICK_INDEX);
+ }
+
+ function _assertPullLimitIndexRevert(
+ address from,
+ uint256 amount,
+ uint256 indexLimit
+ ) internal {
+ changePrank(from);
+ vm.expectRevert(IPoolErrors.LimitIndexExceeded.selector);
+ ERC20Pool(address(_pool)).repayDebt(from, 0, amount, from, indexLimit);
}
function _assertRepayNoDebtRevert(
@@ -507,7 +543,7 @@ abstract contract ERC20DSTestPlus is DSTestPlus, IERC20PoolEvents {
) internal {
changePrank(from);
vm.expectRevert(IPoolErrors.NoDebt.selector);
- ERC20Pool(address(_pool)).repayDebt(borrower, amount, 0);
+ ERC20Pool(address(_pool)).repayDebt(borrower, amount, 0, borrower, MAX_FENWICK_INDEX);
}
function _assertPullBorrowerNotSenderRevert(
@@ -517,7 +553,7 @@ abstract contract ERC20DSTestPlus is DSTestPlus, IERC20PoolEvents {
) internal {
changePrank(from);
vm.expectRevert(IPoolErrors.BorrowerNotSender.selector);
- ERC20Pool(address(_pool)).repayDebt(borrower, 0, amount);
+ ERC20Pool(address(_pool)).repayDebt(borrower, 0, amount, borrower, MAX_FENWICK_INDEX);
}
function _assertRepayMinDebtRevert(
@@ -527,7 +563,7 @@ abstract contract ERC20DSTestPlus is DSTestPlus, IERC20PoolEvents {
) internal {
changePrank(from);
vm.expectRevert(IPoolErrors.AmountLTMinDebt.selector);
- ERC20Pool(address(_pool)).repayDebt(borrower, amount, 0);
+ ERC20Pool(address(_pool)).repayDebt(borrower, amount, 0, borrower, MAX_FENWICK_INDEX);
}
function _assertRemoveAllCollateralNoClaimRevert(
@@ -580,6 +616,17 @@ abstract contract ERC20DSTestPlus is DSTestPlus, IERC20PoolEvents {
_pool.transferLPs(from, to, indexes);
}
+ function _assertTransferToSameOwnerRevert(
+ address operator,
+ address from,
+ address to,
+ uint256[] memory indexes
+ ) internal {
+ changePrank(operator);
+ vm.expectRevert(IPoolErrors.TransferToSameOwner.selector);
+ _pool.transferLPs(from, to, indexes);
+ }
+
function _assertDepositLockedByAuctionDebtRevert(
address operator,
uint256 amount,
@@ -596,7 +643,7 @@ abstract contract ERC20DSTestPlus is DSTestPlus, IERC20PoolEvents {
uint256 indexLimit
) internal override {
changePrank(from);
- vm.expectRevert(IPoolErrors.BorrowerUnderCollateralized.selector);
+ vm.expectRevert(IPoolErrors.AuctionActive.selector);
ERC20Pool(address(_pool)).drawDebt(from, amount, indexLimit, 0);
}
@@ -617,7 +664,7 @@ abstract contract ERC20DSTestPlus is DSTestPlus, IERC20PoolEvents {
uint256 indexLimit
) internal override {
changePrank(from);
- vm.expectRevert(IPoolErrors.LimitIndexReached.selector);
+ vm.expectRevert(IPoolErrors.LimitIndexExceeded.selector);
ERC20Pool(address(_pool)).drawDebt(from, amount, indexLimit, 0);
}
diff --git a/tests/forge/ERC20Pool/ERC20FlashloanCollateral.t.sol b/tests/forge/ERC20Pool/ERC20FlashloanCollateral.t.sol
deleted file mode 100644
index 25d971830..000000000
--- a/tests/forge/ERC20Pool/ERC20FlashloanCollateral.t.sol
+++ /dev/null
@@ -1,141 +0,0 @@
-// SPDX-License-Identifier: UNLICENSED
-pragma solidity 0.8.14;
-
-import { ERC20HelperContract } from './ERC20DSTestPlus.sol';
-import { FlashloanBorrower, SomeDefiStrategy } from '../utils/FlashloanBorrower.sol';
-
-import 'src/libraries/helpers/PoolHelper.sol';
-import 'src/ERC20Pool.sol';
-
-import { IPoolErrors } from 'src/interfaces/pool/IPool.sol';
-
-contract ERC20PoolFlashloanTest is ERC20HelperContract {
- address internal _borrower;
- address internal _lender;
- uint internal _bucketId;
- uint internal _bucketPrice;
-
- function setUp() external {
- _lender = makeAddr("lender");
- _borrower = makeAddr("borrower");
-
- _mintQuoteAndApproveTokens(_lender, 100_000 * 1e18);
- _mintQuoteAndApproveTokens(_borrower, 5_000 * 1e18);
- _mintCollateralAndApproveTokens(_borrower, 100 * 1e18);
-
- // lender adds liquidity
- _bucketPrice = 502.433988063349232760 * 1e18;
- _bucketId = _indexOf(_bucketPrice);
- assertEq(_bucketId, 2909);
- _addInitialLiquidity(
- {
- from: _lender,
- amount: 100_000 * 1e18,
- index: _bucketId
- }
- );
-
- // borrower draws debt
- _pledgeCollateral(
- {
- from: _borrower,
- borrower: _borrower,
- amount: 100 * 1e18
- }
- );
- _borrow(
- {
- from: _borrower,
- amount: 25_000 * 1e18,
- indexLimit: _bucketId,
- newLup: _bucketPrice
- }
- );
- (uint256 poolDebt,,) = _pool.debtInfo();
- assertEq(poolDebt, 25_024.038461538461550000 * 1e18);
- }
-
- function testCollateralFlashloan() external tearDown {
- skip(1 days);
- uint256 loanAmount = 100 * 1e18;
- assertEq(_pool.maxFlashLoan(address(_collateral)), loanAmount);
-
- // Create an example defi strategy
- SomeDefiStrategy strategy = new SomeDefiStrategy(_collateral);
- deal(address(_collateral), address(strategy), 10 * 1e18);
-
- // Create a flashloan borrower contract which interacts with the strategy
- bytes memory strategyCalldata = abi.encodeWithSignature("makeMoney(uint256)", loanAmount);
- FlashloanBorrower flasher = new FlashloanBorrower(address(strategy), strategyCalldata);
-
- // Run the token approvals
- changePrank(address(flasher));
- _collateral.approve(address(_pool), loanAmount);
- _collateral.approve(address(strategy), loanAmount);
-
- // Use a flashloan to interact with the strategy
- assertEq(_collateral.balanceOf(address(flasher)), 0);
- assertTrue(!flasher.callbackInvoked());
- _pool.flashLoan(flasher, address(_collateral), loanAmount, new bytes(0));
- assertTrue(flasher.callbackInvoked());
- assertEq(_collateral.balanceOf(address(flasher)), 3.5 * 1e18);
- }
-
- function testFlashloanFee() external tearDown {
- uint256 loanAmount = 100 * 1e18;
-
- // Ensure there is no fee for quote token
- uint256 fee = _pool.flashFee(address(_quote), loanAmount);
- assertEq(fee, 0);
-
- // Ensure there is no fee for collateral
- fee = _pool.flashFee(address(_collateral), loanAmount);
- assertEq(fee, 0);
-
- // Ensure fee reverts for a random address which isn't a token
- _assertFlashloanFeeRevertsForToken(makeAddr("nobody"), loanAmount);
- }
-
- function testMaxFlashloan() external tearDown {
- assertEq(_pool.maxFlashLoan(_pool.quoteTokenAddress()), 75_000 * 1e18);
- assertEq(_pool.maxFlashLoan(_pool.collateralAddress()), 100 * 1e18);
- assertEq(_pool.maxFlashLoan(makeAddr("nobody")), 0);
- }
-
- function testCannotFlashloanMoreCollateralThanAvailable() external tearDown {
- FlashloanBorrower flasher = new FlashloanBorrower(address(0), new bytes(0));
-
- // Cannot flashloan less than pool size but more than available quote token
- _assertFlashloanTooLargeRevert(flasher, _pool.quoteTokenAddress(), 90_000 * 1e18);
-
- // Cannot flashloan more collateral than pledged
- _assertFlashloanTooLargeRevert(flasher, _pool.collateralAddress(), 150 * 1e18);
- }
-
- function testCannotFlashloanNonToken() external tearDown {
- FlashloanBorrower flasher = new FlashloanBorrower(address(0), new bytes(0));
-
- // Cannot flashloan a random address which isn't a token
- _assertFlashloanUnavailableForToken(flasher, makeAddr("nobody"), 1);
- }
-
- function testCallbackFailure() external tearDown {
- uint256 loanAmount = 100 * 1e18;
-
- // Create an example defi strategy
- SomeDefiStrategy strategy = new SomeDefiStrategy(_collateral);
-
- // Create a flashloan borrower contract which invokes a non-existant method on the strategy
- bytes memory strategyCalldata = abi.encodeWithSignature("missing()");
- FlashloanBorrower flasher = new FlashloanBorrower(address(strategy), strategyCalldata);
-
- // Run approvals
- changePrank(address(flasher));
- _quote.approve(address(_pool), loanAmount);
-
- // Make a failed attempt to interact with the strategy
- vm.expectRevert(IPoolErrors.FlashloanCallbackFailed.selector);
- _pool.flashLoan(flasher, address(_collateral), loanAmount, new bytes(0));
- assertFalse(flasher.callbackInvoked());
- }
-}
\ No newline at end of file
diff --git a/tests/forge/ERC20Pool/ERC20PoolBorrow.t.sol b/tests/forge/ERC20Pool/ERC20PoolBorrow.t.sol
index b30e6ea11..cfa46f2bc 100644
--- a/tests/forge/ERC20Pool/ERC20PoolBorrow.t.sol
+++ b/tests/forge/ERC20Pool/ERC20PoolBorrow.t.sol
@@ -33,52 +33,42 @@ contract ERC20PoolBorrowTest is ERC20HelperContract {
_mintQuoteAndApproveTokens(_lender, 200_000 * 1e18);
_mintQuoteAndApproveTokens(_lender1, 200_000 * 1e18);
- // lender deposits 10000 DAI in 5 buckets each
- _addLiquidity(
- {
- from: _lender,
- amount: 10_000 * 1e18,
- index: highest,
- lpAward: 10_000 * 1e27,
- newLup: MAX_PRICE
- }
- );
- _addLiquidity(
- {
- from: _lender,
- amount: 10_000 * 1e18,
- index: high,
- lpAward: 10_000 * 1e27,
- newLup: MAX_PRICE
- }
- );
- _addLiquidity(
- {
- from: _lender,
- amount: 10_000 * 1e18,
- index: med,
- lpAward: 10_000 * 1e27,
- newLup: MAX_PRICE
- }
- );
- _addLiquidity(
- {
- from: _lender,
- amount: 10_000 * 1e18,
- index: low,
- lpAward: 10_000 * 1e27,
- newLup: MAX_PRICE
- }
- );
- _addLiquidity(
- {
- from: _lender,
- amount: 10_000 * 1e18,
- index: lowest,
- lpAward: 10_000 * 1e27,
- newLup: MAX_PRICE
- }
- );
+ // lender deposits 10000 quote in 5 buckets each
+ _addLiquidity({
+ from: _lender,
+ amount: 10_000 * 1e18,
+ index: highest,
+ lpAward: 10_000 * 1e18,
+ newLup: MAX_PRICE
+ });
+ _addLiquidity({
+ from: _lender,
+ amount: 10_000 * 1e18,
+ index: high,
+ lpAward: 10_000 * 1e18,
+ newLup: MAX_PRICE
+ });
+ _addLiquidity({
+ from: _lender,
+ amount: 10_000 * 1e18,
+ index: med,
+ lpAward: 10_000 * 1e18,
+ newLup: MAX_PRICE
+ });
+ _addLiquidity({
+ from: _lender,
+ amount: 10_000 * 1e18,
+ index: low,
+ lpAward: 10_000 * 1e18,
+ newLup: MAX_PRICE
+ });
+ _addLiquidity({
+ from: _lender,
+ amount: 10_000 * 1e18,
+ index: lowest,
+ lpAward: 10_000 * 1e18,
+ newLup: MAX_PRICE
+ });
_assertPool(
PoolParams({
@@ -135,103 +125,81 @@ contract ERC20PoolBorrowTest is ERC20HelperContract {
assertEq(_quote.balanceOf(address(_pool)), 29_000 * 1e18);
assertEq(_quote.balanceOf(_lender), 150_000 * 1e18);
- _assertLenderLpBalance(
- {
- lender: _lender,
- index: highest,
- lpBalance: 10_000 * 1e27,
- depositTime: _startTime
- }
- );
- _assertLenderLpBalance(
- {
- lender: _lender,
- index: high,
- lpBalance: 10_000 * 1e27,
- depositTime: _startTime
- }
- );
- _assertLenderLpBalance(
- {
- lender: _lender,
- index: med,
- lpBalance: 10_000 * 1e27,
- depositTime: _startTime
- }
- );
- _assertLenderLpBalance(
- {
- lender: _lender,
- index: low,
- lpBalance: 10_000 * 1e27,
- depositTime: _startTime
- }
- );
- _assertLenderLpBalance(
- {
- lender: _lender,
- index: lowest,
- lpBalance: 10_000 * 1e27,
- depositTime: _startTime
- }
- );
+ _assertLenderLpBalance({
+ lender: _lender,
+ index: highest,
+ lpBalance: 10_000 * 1e18,
+ depositTime: _startTime
+ });
+ _assertLenderLpBalance({
+ lender: _lender,
+ index: high,
+ lpBalance: 10_000 * 1e18,
+ depositTime: _startTime
+ });
+ _assertLenderLpBalance({
+ lender: _lender,
+ index: med,
+ lpBalance: 10_000 * 1e18,
+ depositTime: _startTime
+ });
+ _assertLenderLpBalance({
+ lender: _lender,
+ index: low,
+ lpBalance: 10_000 * 1e18,
+ depositTime: _startTime
+ });
+ _assertLenderLpBalance({
+ lender: _lender,
+ index: lowest,
+ lpBalance: 10_000 * 1e18,
+ depositTime: _startTime
+ });
// check buckets
- _assertBucket(
- {
- index: highest,
- lpBalance: 10_000 * 1e27,
- collateral: 0,
- deposit: 10_000 * 1e18,
- exchangeRate: 1 * 1e27
- }
- );
- _assertBucket(
- {
- index: high,
- lpBalance: 10_000 * 1e27,
- collateral: 0,
- deposit: 10_000 * 1e18,
- exchangeRate: 1 * 1e27
- }
- );
- _assertBucket(
- {
- index: med,
- lpBalance: 10_000 * 1e27,
- collateral: 0,
- deposit: 10_000 * 1e18,
- exchangeRate: 1 * 1e27
- }
- );
- _assertBucket(
- {
- index: low,
- lpBalance: 10_000 * 1e27,
- collateral: 0,
- deposit: 10_000 * 1e18,
- exchangeRate: 1 * 1e27
- }
- );
- _assertBucket(
- {
- index: lowest,
- lpBalance: 10_000 * 1e27,
- collateral: 0,
- deposit: 10_000 * 1e18,
- exchangeRate: 1 * 1e27
- }
- );
+ _assertBucket({
+ index: highest,
+ lpBalance: 10_000 * 1e18,
+ collateral: 0,
+ deposit: 10_000 * 1e18,
+ exchangeRate: 1 * 1e18
+ });
+ _assertBucket({
+ index: high,
+ lpBalance: 10_000 * 1e18,
+ collateral: 0,
+ deposit: 10_000 * 1e18,
+ exchangeRate: 1 * 1e18
+ });
+ _assertBucket({
+ index: med,
+ lpBalance: 10_000 * 1e18,
+ collateral: 0,
+ deposit: 10_000 * 1e18,
+ exchangeRate: 1 * 1e18
+ });
+ _assertBucket({
+ index: low,
+ lpBalance: 10_000 * 1e18,
+ collateral: 0,
+ deposit: 10_000 * 1e18,
+ exchangeRate: 1 * 1e18
+ });
+ _assertBucket({
+ index: lowest,
+ lpBalance: 10_000 * 1e18,
+ collateral: 0,
+ deposit: 10_000 * 1e18,
+ exchangeRate: 1 * 1e18
+ });
- // borrow 19_000 DAI
- _borrow(
- {
- from: _borrower,
- amount: 19_000 * 1e18,
- indexLimit: 3_500,
- newLup: 2_951.419442869698640451 * 1e18
- }
- );
+ // borrow 19_000 quote
+ _borrow({
+ from: _borrower,
+ amount: 19_000 * 1e18,
+ indexLimit: 3_500,
+ newLup: 2_951.419442869698640451 * 1e18
+ });
_assertPool(
PoolParams({
@@ -250,6 +218,7 @@ contract ERC20PoolBorrowTest is ERC20HelperContract {
interestRateUpdate: _startTime
})
);
+
// check balances
assertEq(_quote.balanceOf(address(_pool)), 10_000 * 1e18);
assertEq(_quote.balanceOf(_lender), 150_000 * 1e18);
@@ -319,15 +288,13 @@ contract ERC20PoolBorrowTest is ERC20HelperContract {
assertEq(_quote.balanceOf(address(_pool)), 50_038.461538461538480000 * 1e18);
assertEq(_quote.balanceOf(_lender), 150_000 * 1e18);
- // borrow 8_000 DAI
- _borrow(
- {
- from: _borrower,
- amount: 8_000 * 1e18,
- indexLimit: 3_500,
- newLup: 3_010.892022197881557845 * 1e18
- }
- );
+ // borrow 8_000 quote
+ _borrow({
+ from: _borrower,
+ amount: 8_000 * 1e18,
+ indexLimit: 3_500,
+ newLup: 3_010.892022197881557845 * 1e18
+ });
_assertPool(
PoolParams({
@@ -350,7 +317,9 @@ contract ERC20PoolBorrowTest is ERC20HelperContract {
function testPoolBorrowerInterestAccumulation() external tearDown {
(uint256 liquidityAdded, , , , ) = _poolUtils.poolLoansInfo(address(_pool));
+
skip(10 days);
+
_drawDebt({
from: _borrower,
borrower: _borrower,
@@ -361,6 +330,7 @@ contract ERC20PoolBorrowTest is ERC20HelperContract {
});
uint256 expectedDebt = 21_051.890446235135648008 * 1e18;
+
_assertPool(
PoolParams({
htp: 420.403846153846154040 * 1e18,
@@ -378,26 +348,24 @@ contract ERC20PoolBorrowTest is ERC20HelperContract {
interestRateUpdate: _startTime + 10 days
})
);
- _assertBorrower(
- {
- borrower: _borrower,
- borrowerDebt: expectedDebt,
- borrowerCollateral: 50 * 1e18,
- borrowert0Np: 441.424038461538461742 * 1e18,
- borrowerCollateralization: 7.080141877038845214 * 1e18
- }
- );
+ _assertBorrower({
+ borrower: _borrower,
+ borrowerDebt: expectedDebt,
+ borrowerCollateral: 50 * 1e18,
+ borrowert0Np: 441.424038461538461742 * 1e18,
+ borrowerCollateralization: 7.080141877038845214 * 1e18
+ });
skip(10 days);
- _pledgeCollateral(
- {
- from: _borrower,
- borrower: _borrower,
- amount: 10 * 1e18
- }
- );
+
+ _pledgeCollateral({
+ from: _borrower,
+ borrower: _borrower,
+ amount: 10 * 1e18
+ });
expectedDebt = 21_083.636385101213387311 * 1e18;
+
_assertPool(
PoolParams({
htp: 352.454532537342231182 * 1e18,
@@ -415,18 +383,17 @@ contract ERC20PoolBorrowTest is ERC20HelperContract {
interestRateUpdate: _startTime + 20 days
})
);
- _assertBorrower(
- {
- borrower: _borrower,
- borrowerDebt: expectedDebt,
- borrowerCollateral: 60 * 1e18,
- borrowert0Np: 441.424038461538461742 * 1e18,
- borrowerCollateralization: 8.483377444958217435 * 1e18
- }
- );
+ _assertBorrower({
+ borrower: _borrower,
+ borrowerDebt: expectedDebt,
+ borrowerCollateral: 60 * 1e18,
+ borrowert0Np: 441.424038461538461742 * 1e18,
+ borrowerCollateralization: 8.483377444958217435 * 1e18
+ });
_assertLenderInterest(liquidityAdded, 55.509493137959600000 * 1e18);
skip(10 days);
+
_repayDebtNoLupCheck({
from: _borrower,
borrower: _borrower,
@@ -436,6 +403,7 @@ contract ERC20PoolBorrowTest is ERC20HelperContract {
});
expectedDebt = 21_118.612213260575680078 * 1e18;
+
_assertPool(
PoolParams({
htp: 424.349858731660857846 * 1e18,
@@ -453,28 +421,26 @@ contract ERC20PoolBorrowTest is ERC20HelperContract {
interestRateUpdate: _startTime + 30 days
})
);
- _assertBorrower(
- {
- borrower: _borrower,
- borrowerDebt: expectedDebt,
- borrowerCollateral: 50 * 1e18,
- borrowert0Np: 445.838278846153846359 * 1e18,
- borrowerCollateralization: 7.057773002983275247 * 1e18
- }
- );
+ _assertBorrower({
+ borrower: _borrower,
+ borrowerDebt: expectedDebt,
+ borrowerCollateral: 50 * 1e18,
+ borrowert0Np: 445.838278846153846359 * 1e18,
+ borrowerCollateralization: 7.057773002983275247 * 1e18
+ });
_assertLenderInterest(liquidityAdded, 86.113113158840750000 * 1e18);
skip(10 days);
- _borrowZeroAmount(
- {
- from: _borrower,
- amount: 0,
- indexLimit: 3_000,
- newLup: 2_981.007422784467321543 * 1e18
- }
- );
+
+ _borrowZeroAmount({
+ from: _borrower,
+ amount: 0,
+ indexLimit: 3_000,
+ newLup: 2_981.007422784467321543 * 1e18
+ });
expectedDebt = 21_157.152643010853304038 * 1e18;
+
_assertPool(
PoolParams({
htp: 425.900107294311861922 * 1e18,
@@ -492,15 +458,13 @@ contract ERC20PoolBorrowTest is ERC20HelperContract {
interestRateUpdate: _startTime + 40 days
})
);
- _assertBorrower(
- {
- borrower: _borrower,
- borrowerDebt: expectedDebt,
- borrowerCollateral: 50 * 1e18,
- borrowert0Np: 448.381722115384615591 * 1e18,
- borrowerCollateralization: 7.044916376706357984 * 1e18
- }
- );
+ _assertBorrower({
+ borrower: _borrower,
+ borrowerDebt: expectedDebt,
+ borrowerCollateral: 50 * 1e18,
+ borrowert0Np: 448.381722115384615591 * 1e18,
+ borrowerCollateralization: 7.044916376706357984 * 1e18
+ });
_assertLenderInterest(liquidityAdded, 119.836959946754650000 * 1e18);
skip(10 days);
@@ -509,6 +473,7 @@ contract ERC20PoolBorrowTest is ERC20HelperContract {
IERC20Pool(address(_pool)).drawDebt(_borrower, 0, 0, 0);
expectedDebt = 21_199.628356897284442294 * 1e18;
+
_assertPool(
PoolParams({
htp: 427.611922756860156608 * 1e18,
@@ -526,19 +491,19 @@ contract ERC20PoolBorrowTest is ERC20HelperContract {
interestRateUpdate: _startTime + 50 days
})
);
- _assertBorrower(
- {
- borrower: _borrower,
- borrowerDebt: expectedDebt,
- borrowerCollateral: 50 * 1e18,
- borrowert0Np: 448.381722115384615591 * 1e18,
- borrowerCollateralization: 7.030801136225104190 * 1e18
- }
- );
+ _assertBorrower({
+ borrower: _borrower,
+ borrowerDebt: expectedDebt,
+ borrowerCollateral: 50 * 1e18,
+ borrowert0Np: 448.381722115384615591 * 1e18,
+ borrowerCollateralization: 7.030801136225104190 * 1e18
+ });
_assertLenderInterest(liquidityAdded, 157.005764521268350000 * 1e18);
skip(10 days);
+
expectedDebt = 21_246.450141935843866714 * 1e18;
+
_assertPool(
PoolParams({
htp: 427.611922756860156608 * 1e18,
@@ -556,15 +521,13 @@ contract ERC20PoolBorrowTest is ERC20HelperContract {
interestRateUpdate: _startTime + 50 days
})
);
- _assertBorrower(
- {
- borrower: _borrower,
- borrowerDebt: expectedDebt,
- borrowerCollateral: 50 * 1e18,
- borrowert0Np: 448.381722115384615591 * 1e18,
- borrowerCollateralization: 7.015307034516347067 * 1e18
- }
- );
+ _assertBorrower({
+ borrower: _borrower,
+ borrowerDebt: expectedDebt,
+ borrowerCollateral: 50 * 1e18,
+ borrowert0Np: 448.381722115384615591 * 1e18,
+ borrowerCollateralization: 7.015307034516347067 * 1e18
+ });
}
/**
@@ -577,111 +540,90 @@ contract ERC20PoolBorrowTest is ERC20HelperContract {
*/
function testPoolBorrowReverts() external tearDown {
// should revert if borrower attempts to borrow with an out of bounds limitIndex
- _assertBorrowLimitIndexRevert(
- {
- from: _borrower,
- amount: 1_000 * 1e18,
- indexLimit: 1000
- }
- );
+ _assertBorrowLimitIndexRevert({
+ from: _borrower,
+ amount: 1_000 * 1e18,
+ indexLimit: 1000
+ });
// should revert if borrower tries to borrow on behalf of different address
- _assertBorrowBorrowerNotSenderRevert(
- {
- from: _borrower,
- borrower: _borrower2,
- amount: 1 * 1e18,
- indexLimit: 7000
- }
- );
+ _assertBorrowBorrowerNotSenderRevert({
+ from: _borrower,
+ borrower: _borrower2,
+ amount: 1 * 1e18,
+ indexLimit: 7000
+ });
// should revert if borrower didn't pledged any collateral
- _assertBorrowBorrowerUnderCollateralizedRevert(
- {
- from: _borrower,
- amount: 500 * 1e18,
- indexLimit: 3000
- }
- );
+ _assertBorrowBorrowerUnderCollateralizedRevert({
+ from: _borrower,
+ amount: 500 * 1e18,
+ indexLimit: 3000
+ });
// borrower 1 borrows 500 quote from the pool after adding sufficient collateral
- _pledgeCollateral(
- {
- from: _borrower,
- borrower: _borrower,
- amount: 50 * 1e18
- }
- );
- _borrow(
- {
- from: _borrower,
- amount: 500 * 1e18,
- indexLimit: 3_000,
- newLup: 3_010.892022197881557845 * 1e18
- }
- );
+ _pledgeCollateral({
+ from: _borrower,
+ borrower: _borrower,
+ amount: 50 * 1e18
+ });
+ _borrow({
+ from: _borrower,
+ amount: 500 * 1e18,
+ indexLimit: 3_000,
+ newLup: 3_010.892022197881557845 * 1e18
+ });
// borrower 2 borrows 15k quote from the pool with borrower2 becoming new queue HEAD
- _pledgeCollateral(
- {
- from: _borrower2,
- borrower: _borrower2,
- amount: 6 * 1e18
- }
- );
- _borrow(
- {
- from: _borrower2,
- amount: 15_000 * 1e18,
- indexLimit: 3_000,
- newLup: 2_995.912459898389633881 * 1e18
- }
- );
+ _pledgeCollateral({
+ from: _borrower2,
+ borrower: _borrower2,
+ amount: 6 * 1e18
+ });
+ _borrow({
+ from: _borrower2,
+ amount: 15_000 * 1e18,
+ indexLimit: 3_000,
+ newLup: 2_995.912459898389633881 * 1e18
+ });
// should revert if borrower undercollateralized
- _assertBorrowBorrowerUnderCollateralizedRevert(
- {
- from: _borrower2,
- amount: 2_976 * 1e18,
- indexLimit: 3000
- }
- );
+ _assertBorrowBorrowerUnderCollateralizedRevert({
+ from: _borrower2,
+ amount: 2_976 * 1e18,
+ indexLimit: 3000
+ });
// should be able to borrow if properly specified
- _borrow(
- {
- from: _borrower2,
- amount: 10 * 1e18,
- indexLimit: 3_000,
- newLup: 2_995.912459898389633881 * 1e18
- }
- );
+ _borrow({
+ from: _borrower2,
+ amount: 10 * 1e18,
+ indexLimit: 3_000,
+ newLup: 2_995.912459898389633881 * 1e18
+ });
}
function testMinBorrowAmountCheck() external tearDown {
// 10 borrowers draw debt
for (uint i=0; i<10; ++i) {
- _anonBorrowerDrawsDebt(100 * 1e18, 1_200 * 1e18, 7777);
+ _anonBorrowerDrawsDebt(100 * 1e18, 1_200 * 1e18, MAX_FENWICK_INDEX);
}
+
(, uint256 loansCount, , , ) = _poolUtils.poolLoansInfo(address(_pool));
assertEq(loansCount, 10);
- _pledgeCollateral(
- {
- from: _borrower,
- borrower: _borrower,
- amount: 100 * 1e18
- }
- );
+ _pledgeCollateral({
+ from: _borrower,
+ borrower: _borrower,
+ amount: 100 * 1e18
+ });
// should revert if borrower attempts to borrow more than minimum amount
- _assertBorrowMinDebtRevert(
- {
- from: _borrower,
- amount: 10 * 1e18,
- indexLimit: 7_777
- }
- );
+ _assertBorrowMinDebtRevert({
+ from: _borrower,
+ amount: 10 * 1e18,
+ indexLimit: MAX_FENWICK_INDEX
+ });
}
/**
@@ -695,38 +637,30 @@ contract ERC20PoolBorrowTest is ERC20HelperContract {
deal(address(_quote), _borrower, _quote.balanceOf(_borrower) + 10_000 * 1e18);
// should revert if borrower has no debt
- _assertRepayNoDebtRevert(
- {
- from: _borrower,
- borrower: _borrower,
- amount: 10_000 * 1e18
- }
- );
+ _assertRepayNoDebtRevert({
+ from: _borrower,
+ borrower: _borrower,
+ amount: 10_000 * 1e18
+ });
- _assertPullBorrowerNotSenderRevert(
- {
- from: _borrower,
- borrower: _borrower2,
- amount: 10_000 * 1e18
- }
- );
+ _assertPullBorrowerNotSenderRevert({
+ from: _borrower,
+ borrower: _borrower2,
+ amount: 10_000 * 1e18
+ });
// borrower 1 borrows 1000 quote from the pool
- _pledgeCollateral(
- {
- from: _borrower,
- borrower: _borrower,
- amount: 50 * 1e18
- }
- );
- _borrow(
- {
- from: _borrower,
- amount: 1_000 * 1e18,
- indexLimit: 3_000,
- newLup: 3_010.892022197881557845 * 1e18
- }
- );
+ _pledgeCollateral({
+ from: _borrower,
+ borrower: _borrower,
+ amount: 50 * 1e18
+ });
+ _borrow({
+ from: _borrower,
+ amount: 1_000 * 1e18,
+ indexLimit: 3_000,
+ newLup: 3_010.892022197881557845 * 1e18
+ });
_assertPool(
PoolParams({
@@ -747,21 +681,17 @@ contract ERC20PoolBorrowTest is ERC20HelperContract {
);
// borrower 2 borrows 5k quote from the pool and becomes new queue HEAD
- _pledgeCollateral(
- {
- from: _borrower2,
- borrower: _borrower2,
- amount: 50 * 1e18
- }
- );
- _borrow(
- {
- from: _borrower2,
- amount: 5_000 * 1e18,
- indexLimit: 3_000,
- newLup: 3_010.892022197881557845 * 1e18
- }
- );
+ _pledgeCollateral({
+ from: _borrower2,
+ borrower: _borrower2,
+ amount: 50 * 1e18
+ });
+ _borrow({
+ from: _borrower2,
+ amount: 5_000 * 1e18,
+ indexLimit: 3_000,
+ newLup: 3_010.892022197881557845 * 1e18
+ });
_assertPool(
PoolParams({
@@ -781,6 +711,14 @@ contract ERC20PoolBorrowTest is ERC20HelperContract {
})
);
+ // should revert if LUP is below the limit
+ ( , , , , , uint256 lupIndex ) = _poolUtils.poolPricesInfo(address(_pool));
+ _assertPullLimitIndexRevert({
+ from: _borrower,
+ amount: 20 * 1e18,
+ indexLimit: lupIndex - 1
+ });
+
// should be able to repay loan if properly specified
_repayDebt({
from: _borrower,
@@ -813,48 +751,43 @@ contract ERC20PoolBorrowTest is ERC20HelperContract {
function testMinRepayAmountCheck() external tearDown {
// borrower 1 borrows 1000 quote from the pool
_drawDebt({
- from: _borrower,
- borrower: _borrower,
- amountToBorrow: 1_000 * 1e18,
- limitIndex: 3_000,
+ from: _borrower,
+ borrower: _borrower,
+ amountToBorrow: 1_000 * 1e18,
+ limitIndex: 3_000,
collateralToPledge: 50 * 1e18,
- newLup: 3_010.892022197881557845 * 1e18
+ newLup: 3_010.892022197881557845 * 1e18
});
// 9 other borrowers draw debt
for (uint i=0; i<9; ++i) {
- _anonBorrowerDrawsDebt(100 * 1e18, 1_000 * 1e18, 7777);
+ _anonBorrowerDrawsDebt(100 * 1e18, 1_000 * 1e18, MAX_FENWICK_INDEX);
}
+
(, uint256 loansCount, , , ) = _poolUtils.poolLoansInfo(address(_pool));
assertEq(loansCount, 10);
// should revert if amount left after repay is less than the average debt
- _assertRepayMinDebtRevert(
- {
- from: _borrower,
- borrower: _borrower,
- amount: 950 * 1e18
- }
- );
+ _assertRepayMinDebtRevert({
+ from: _borrower,
+ borrower: _borrower,
+ amount: 950 * 1e18
+ });
}
function testRepayLoanFromDifferentActor() external tearDown {
// borrower 1 borrows 1000 quote from the pool
- _pledgeCollateral(
- {
- from: _borrower,
- borrower: _borrower,
- amount: 50 * 1e18
- }
- );
- _borrow(
- {
- from: _borrower,
- amount: 1_000 * 1e18,
- indexLimit: 3_000,
- newLup: 3_010.892022197881557845 * 1e18
- }
- );
+ _pledgeCollateral({
+ from: _borrower,
+ borrower: _borrower,
+ amount: 50 * 1e18
+ });
+ _borrow({
+ from: _borrower,
+ amount: 1_000 * 1e18,
+ indexLimit: 3_000,
+ newLup: 3_010.892022197881557845 * 1e18
+ });
_assertPool(
PoolParams({
@@ -910,25 +843,23 @@ contract ERC20PoolBorrowTest is ERC20HelperContract {
*/
function testZeroThresholdPriceLoan() external tearDown {
// borrower 1 initiates a highly overcollateralized loan with a TP of 0 that won't be inserted into the Queue
- _pledgeCollateral(
- {
- from: _borrower,
- borrower: _borrower,
- amount: 50 * 1e18
- }
- );
+ _pledgeCollateral({
+ from: _borrower,
+ borrower: _borrower,
+ amount: 50 * 1e18
+ });
+
vm.expectRevert(abi.encodeWithSignature('ZeroThresholdPrice()'));
IERC20Pool(address(_pool)).drawDebt(_borrower, 0.00000000000000001 * 1e18, 3000, 0);
-
// borrower 1 borrows 500 quote from the pool after using a non 0 TP
_drawDebt({
- from: _borrower,
- borrower: _borrower,
- amountToBorrow: 500 * 1e18,
- limitIndex: 3_000,
+ from: _borrower,
+ borrower: _borrower,
+ amountToBorrow: 500 * 1e18,
+ limitIndex: 3_000,
collateralToPledge: 50 * 1e18,
- newLup: 3_010.892022197881557845 * 1e18
+ newLup: 3_010.892022197881557845 * 1e18
});
_assertPool(
@@ -985,19 +916,19 @@ contract ERC20PoolBorrowTest is ERC20HelperContract {
})
);
- _assertBorrower(
- {
- borrower: _borrower,
- borrowerDebt: 500.480769230769231 * 1e18,
- borrowerCollateral: 50 * 1e18,
- borrowert0Np: 10.510096153846153851 * 1e18,
- borrowerCollateralization: 300.799971477982403259 * 1e18
- }
- );
+ _assertBorrower({
+ borrower: _borrower,
+ borrowerDebt: 500.480769230769231 * 1e18,
+ borrowerCollateral: 50 * 1e18,
+ borrowert0Np: 10.510096153846153851 * 1e18,
+ borrowerCollateralization: 300.799971477982403259 * 1e18
+ });
+
deal(address(_quote), _borrower, _quote.balanceOf(_borrower) + 10_000 * 1e18);
+
// should revert if borrower repays most, but not all of their debt resulting in a 0 tp loan remaining on the book
vm.expectRevert(abi.encodeWithSignature('ZeroThresholdPrice()'));
- IERC20Pool(address(_pool)).repayDebt(_borrower, 500.480769230769231000 * 1e18 - 1, 0);
+ IERC20Pool(address(_pool)).repayDebt(_borrower, 500.480769230769231000 * 1e18 - 1, 0, _borrower, MAX_FENWICK_INDEX);
// should be able to pay back all pendingDebt
_repayDebt({
@@ -1032,70 +963,66 @@ contract ERC20PoolBorrowTest is ERC20HelperContract {
// check balances before borrow
assertEq(_quote.balanceOf(_lender), 150_000 * 1e18);
- _assertLenderLpBalance(
- {
- lender: _lender,
- index: highest,
- lpBalance: 10_000 * 1e27,
- depositTime: _startTime
- }
- );
+ _assertLenderLpBalance({
+ lender: _lender,
+ index: highest,
+ lpBalance: 10_000 * 1e18,
+ depositTime: _startTime
+ });
+
assertEq(_quote.balanceOf(_borrower), 0);
assertEq(_collateral.balanceOf(_borrower), 100 * 1e18);
// pledge and borrow
- _pledgeCollateral(
- {
- from: _borrower,
- borrower: _borrower,
- amount: 100 * 1e18
- }
- );
- _borrow(
- {
- from: _borrower,
- amount: 21_000 * 1e18,
- indexLimit: 3_000,
- newLup: 2_981.007422784467321543 * 1e18
- }
- );
+ _pledgeCollateral({
+ from: _borrower,
+ borrower: _borrower,
+ amount: 100 * 1e18
+ });
+ _borrow({
+ from: _borrower,
+ amount: 21_000 * 1e18,
+ indexLimit: 3_000,
+ newLup: 2_981.007422784467321543 * 1e18
+ });
+
assertEq(_quote.balanceOf(_borrower), 21_000 * 1e18);
assertEq(_collateral.balanceOf(_borrower), 0);
- _assertPoolPrices(
- {
- htp: 210.201923076923077020 * 1e18,
- htpIndex: 3_083,
- hpb: 3_010.892022197881557845 * 1e18,
- hpbIndex: 2550,
- lup: 2_981.007422784467321543 * 1e18,
- lupIndex: 2_552
- }
- );
+ _assertPoolPrices({
+ htp: 210.201923076923077020 * 1e18,
+ htpIndex: 3_083,
+ hpb: 3_010.892022197881557845 * 1e18,
+ hpbIndex: 2550,
+ lup: 2_981.007422784467321543 * 1e18,
+ lupIndex: 2_552
+ });
+
+ _addLiquidity({
+ from: _lender,
+ amount: 10_000 * 1e18,
+ index: _indexOf(200 * 1e18),
+ lpAward: 10_000 * 1e18,
+ newLup: 2_981.007422784467321543 * 1e18
+ });
+
// penalty should not be applied on buckets with prices lower than PTP
- _addLiquidity(
- {
- from: _lender,
- amount: 10_000 * 1e18,
- index: _indexOf(200 * 1e18),
- lpAward: 10_000 * 1e27,
- newLup: 2_981.007422784467321543 * 1e18
- }
- );
+
assertEq(_quote.balanceOf(_lender), 140_000 * 1e18);
- _removeAllLiquidity(
- {
- from: _lender,
- amount: 10_000 * 1e18,
- index: _indexOf(200 * 1e18),
- newLup: 2_981.007422784467321543 * 1e18,
- lpRedeem: 10_000 * 1e27
- }
- );
+
+ _removeAllLiquidity({
+ from: _lender,
+ amount: 10_000 * 1e18,
+ index: _indexOf(200 * 1e18),
+ newLup: 2_981.007422784467321543 * 1e18,
+ lpRedeem: 10_000 * 1e18
+ });
+
assertEq(_quote.balanceOf(_lender), 150_000 * 1e18); // no tokens paid as penalty
// repay entire loan
deal(address(_quote), _borrower, _quote.balanceOf(_borrower) + 40 * 1e18);
+
_repayDebt({
from: _borrower,
borrower: _borrower,
@@ -1104,31 +1031,32 @@ contract ERC20PoolBorrowTest is ERC20HelperContract {
collateralToPull: 0,
newLup: MAX_PRICE
});
+
assertEq(_quote.balanceOf(_borrower), 19.807692307692298000 * 1e18);
assertEq(_collateral.balanceOf(_borrower), 0);
- _assertPoolPrices(
- {
- htp: 0,
- htpIndex: 7_388,
- hpb: 3_010.892022197881557845 * 1e18,
- hpbIndex: 2550,
- lup: MAX_PRICE,
- lupIndex: 0
- }
- );
+ _assertPoolPrices({
+ htp: 0,
+ htpIndex: 7_388,
+ hpb: 3_010.892022197881557845 * 1e18,
+ hpbIndex: 2550,
+ lup: MAX_PRICE,
+ lupIndex: 0
+ });
+
// lender removes everything from above PTP, penalty should be applied
uint256 snapshot = vm.snapshot();
- _removeAllLiquidity(
- {
- from: _lender,
- amount: 9_990.384615384615380000 * 1e18,
- index: highest,
- newLup: MAX_PRICE,
- lpRedeem: 10_000 * 1e27
- }
- );
+
+ _removeAllLiquidity({
+ from: _lender,
+ amount: 9_990.384615384615380000 * 1e18,
+ index: highest,
+ newLup: MAX_PRICE,
+ lpRedeem: 10_000 * 1e18
+ });
+
assertEq(_quote.balanceOf(_lender), 159_990.384615384615380000 * 1e18); // 5 tokens paid as penalty
+
vm.revertTo(snapshot);
// borrower pulls first all their collateral pledged, PTP goes to 0, penalty should be applied
@@ -1139,30 +1067,31 @@ contract ERC20PoolBorrowTest is ERC20HelperContract {
amountRepaid: 0,
collateralToPull: 100 * 1e18
});
+
assertEq(_quote.balanceOf(_borrower), 19.807692307692298000 * 1e18);
assertEq(_collateral.balanceOf(_borrower), 100 * 1e18);
- _removeAllLiquidity(
- {
- from: _lender,
- amount: 9_990.384615384615380000 * 1e18,
- index: highest,
- newLup: MAX_PRICE,
- lpRedeem: 10_000 * 1e27
- }
- );
+
+ _removeAllLiquidity({
+ from: _lender,
+ amount: 9_990.384615384615380000 * 1e18,
+ index: highest,
+ newLup: MAX_PRICE,
+ lpRedeem: 10_000 * 1e18
+ });
+
assertEq(_quote.balanceOf(_lender), 159_990.384615384615380000 * 1e18); // 5 tokens paid as penalty
// lender removes everything from price above PTP after 24 hours, penalty should not be applied
skip(1 days);
- _removeAllLiquidity(
- {
- from: _lender,
- amount: 10_000 * 1e18,
- index: med,
- newLup: MAX_PRICE,
- lpRedeem: 10_000 * 1e27
- }
- );
+
+ _removeAllLiquidity({
+ from: _lender,
+ amount: 10_000 * 1e18,
+ index: med,
+ newLup: MAX_PRICE,
+ lpRedeem: 10_000 * 1e18
+ });
+
assertEq(_quote.balanceOf(_lender), 169_990.384615384615380000 * 1e18); // no tokens paid as penalty
}
}
@@ -1192,52 +1121,42 @@ contract ERC20PoolBorrowFuzzyTest is ERC20FuzzyHelperContract {
_mintQuoteAndApproveTokens(_lender, 200_000 * 1e18);
_mintQuoteAndApproveTokens(_lender1, 200_000 * 1e18);
- // lender deposits 10000 DAI in 5 buckets each
- _addLiquidity(
- {
- from: _lender,
- amount: 10_000 * 1e18,
- index: highest,
- lpAward: 10_000 * 1e27,
- newLup: MAX_PRICE
- }
- );
- _addLiquidity(
- {
- from: _lender,
- amount: 10_000 * 1e18,
- index: high,
- lpAward: 10_000 * 1e27,
- newLup: MAX_PRICE
- }
- );
- _addLiquidity(
- {
- from: _lender,
- amount: 10_000 * 1e18,
- index: med,
- lpAward: 10_000 * 1e27,
- newLup: MAX_PRICE
- }
- );
- _addLiquidity(
- {
- from: _lender,
- amount: 10_000 * 1e18,
- index: low,
- lpAward: 10_000 * 1e27,
- newLup: MAX_PRICE
- }
- );
- _addLiquidity(
- {
- from: _lender,
- amount: 10_000 * 1e18,
- index: lowest,
- lpAward: 10_000 * 1e27,
- newLup: MAX_PRICE
- }
- );
+ // lender deposits 10000 quote tokens in 5 buckets each
+ _addLiquidity({
+ from: _lender,
+ amount: 10_000 * 1e18,
+ index: highest,
+ lpAward: 10_000 * 1e18,
+ newLup: MAX_PRICE
+ });
+ _addLiquidity({
+ from: _lender,
+ amount: 10_000 * 1e18,
+ index: high,
+ lpAward: 10_000 * 1e18,
+ newLup: MAX_PRICE
+ });
+ _addLiquidity({
+ from: _lender,
+ amount: 10_000 * 1e18,
+ index: med,
+ lpAward: 10_000 * 1e18,
+ newLup: MAX_PRICE
+ });
+ _addLiquidity({
+ from: _lender,
+ amount: 10_000 * 1e18,
+ index: low,
+ lpAward: 10_000 * 1e18,
+ newLup: MAX_PRICE
+ });
+ _addLiquidity({
+ from: _lender,
+ amount: 10_000 * 1e18,
+ index: lowest,
+ lpAward: 10_000 * 1e18,
+ newLup: MAX_PRICE
+ });
_assertPool(
PoolParams({
@@ -1267,22 +1186,23 @@ contract ERC20PoolBorrowFuzzyTest is ERC20FuzzyHelperContract {
uint256[] memory indexes = new uint256[](numIndexes);
for (uint256 i = 0; i < numIndexes; ++i) {
deal(address(_quote), _lender, mintAmount_);
+
indexes[i] = _randomIndex();
_addLiquidity({
from: _lender,
amount: mintAmount_,
index: indexes[i],
- lpAward: mintAmount_ * 1e9,
+ lpAward: mintAmount_,
newLup: _calculateLup(address(_pool), 0)
});
_assertBucket({
index: indexes[i],
- lpBalance: mintAmount_ * 1e9,
+ lpBalance: mintAmount_,
collateral: 0,
deposit: mintAmount_,
- exchangeRate: 1e27
+ exchangeRate: 1e18
});
}
@@ -1291,7 +1211,9 @@ contract ERC20PoolBorrowFuzzyTest is ERC20FuzzyHelperContract {
uint256 limitIndex = _findLowestIndexPrice(indexes);
uint256 borrowAmount = Maths.wdiv(mintAmount_, Maths.wad(3));
uint256 requiredCollateral = _requiredCollateral(Maths.wdiv(mintAmount_, Maths.wad(3)), limitIndex);
+
deal(address(_collateral), _borrower, requiredCollateral);
+
_drawDebt({
from: _borrower,
borrower: _borrower,
@@ -1305,10 +1227,10 @@ contract ERC20PoolBorrowFuzzyTest is ERC20FuzzyHelperContract {
for (uint256 i = 0; i < numIndexes; ++i) {
_assertBucket({
index: indexes[i],
- lpBalance: mintAmount_ * 1e9,
+ lpBalance: mintAmount_,
collateral: 0,
deposit: mintAmount_,
- exchangeRate: 1e27
+ exchangeRate: 1e18
});
}
@@ -1318,6 +1240,7 @@ contract ERC20PoolBorrowFuzzyTest is ERC20FuzzyHelperContract {
// check pool state
(uint256 minDebt, , uint256 poolActualUtilization, uint256 poolTargetUtilization) = _poolUtils.poolUtilizationInfo(address(_pool));
+
_assertPool(
PoolParams({
htp: Maths.wdiv(debt, requiredCollateral),
@@ -1335,6 +1258,7 @@ contract ERC20PoolBorrowFuzzyTest is ERC20FuzzyHelperContract {
interestRateUpdate: _startTime
})
);
+
assertLt(_htp(), _poolUtils.lup(address(_pool)));
assertGt(minDebt, 0);
assertEq(_poolUtils.lup(address(_pool)), _calculateLup(address(_pool), debt));
@@ -1344,7 +1268,9 @@ contract ERC20PoolBorrowFuzzyTest is ERC20FuzzyHelperContract {
// repay all debt and withdraw collateral
(debt, , ) = _poolUtils.borrowerInfo(address(_pool), address(_borrower));
+
deal(address(_quote), _borrower, debt);
+
_repayDebt({
from: _borrower,
borrower: _borrower,
@@ -1361,16 +1287,17 @@ contract ERC20PoolBorrowFuzzyTest is ERC20FuzzyHelperContract {
// check that only deposits above the htp earned interest
if (indexes[i] <= _poolUtils.priceToIndex(Maths.wdiv(debt, requiredCollateral))) {
assertGt(deposit, mintAmount_);
- assertGt(exchangeRate, 1e27);
+ assertGt(exchangeRate, 1e18);
} else {
assertEq(deposit, mintAmount_);
- assertEq(exchangeRate, 1e27);
+ assertEq(exchangeRate, 1e18);
}
- assertEq(lpAccumulator, mintAmount_ * 1e9);
+ assertEq(lpAccumulator, mintAmount_);
+
_assertBucket({
index: indexes[i],
- lpBalance: mintAmount_ * 1e9,
+ lpBalance: mintAmount_,
collateral: 0,
deposit: deposit,
exchangeRate: exchangeRate
diff --git a/tests/forge/ERC20Pool/ERC20PoolCollateral.t.sol b/tests/forge/ERC20Pool/ERC20PoolCollateral.t.sol
index 6d698e413..f88d638ad 100644
--- a/tests/forge/ERC20Pool/ERC20PoolCollateral.t.sol
+++ b/tests/forge/ERC20Pool/ERC20PoolCollateral.t.sol
@@ -29,30 +29,24 @@ contract ERC20PoolCollateralTest is ERC20HelperContract {
/**
* @notice With 1 lender and 1 borrower test pledgeCollateral, borrow, and pullCollateral.
*/
- function testAddPullCollateral() external tearDown {
+ function testPledgeAndPullCollateral() external tearDown {
// lender deposits 10000 Quote into 3 buckets
- _addInitialLiquidity(
- {
- from: _lender,
- amount: 10_000 * 1e18,
- index: 2550
- }
- );
- _addInitialLiquidity(
- {
- from: _lender,
- amount: 10_000 * 1e18,
- index: 2551
- }
- );
- _addInitialLiquidity(
- {
- from: _lender,
- amount: 10_000 * 1e18,
- index: 2552
- }
- );
+ _addInitialLiquidity({
+ from: _lender,
+ amount: 10_000 * 1e18,
+ index: 2550
+ });
+ _addInitialLiquidity({
+ from: _lender,
+ amount: 10_000 * 1e18,
+ index: 2551
+ });
+ _addInitialLiquidity({
+ from: _lender,
+ amount: 10_000 * 1e18,
+ index: 2552
+ });
_assertPool(
PoolParams({
@@ -71,24 +65,21 @@ contract ERC20PoolCollateralTest is ERC20HelperContract {
interestRateUpdate: _startTime
})
);
+
assertEq(_collateral.balanceOf(_borrower), 150 * 1e18);
// borrower pledge 100 collateral and get a 21_000 Quote loan
- _pledgeCollateral(
- {
- from: _borrower,
- borrower: _borrower,
- amount: 100 * 1e18
- }
- );
- _borrow(
- {
- from: _borrower,
- amount: 21_000 * 1e18,
- indexLimit: 3_000,
- newLup: 2_981.007422784467321543 * 1e18
- }
- );
+ _pledgeCollateral({
+ from: _borrower,
+ borrower: _borrower,
+ amount: 100 * 1e18
+ });
+ _borrow({
+ from: _borrower,
+ amount: 21_000 * 1e18,
+ indexLimit: 3_000,
+ newLup: 2_981.007422784467321543 * 1e18
+ });
_assertPool(
PoolParams({
@@ -107,15 +98,14 @@ contract ERC20PoolCollateralTest is ERC20HelperContract {
interestRateUpdate: _startTime
})
);
- _assertBorrower(
- {
- borrower: _borrower,
- borrowerDebt: 21_020.192307692307702000 * 1e18,
- borrowerCollateral: 100 * 1e18,
- borrowert0Np: 220.712019230769230871 * 1e18,
- borrowerCollateralization: 14.181637252165253251 * 1e18
- }
- );
+ _assertBorrower({
+ borrower: _borrower,
+ borrowerDebt: 21_020.192307692307702000 * 1e18,
+ borrowerCollateral: 100 * 1e18,
+ borrowert0Np: 220.712019230769230871 * 1e18,
+ borrowerCollateralization: 14.181637252165253251 * 1e18
+ });
+
assertEq(_collateral.balanceOf(_borrower), 50 * 1e18);
// pass time to allow interest to accrue
@@ -147,15 +137,14 @@ contract ERC20PoolCollateralTest is ERC20HelperContract {
interestRateUpdate: _startTime + 10 days
})
);
- _assertBorrower(
- {
- borrower: _borrower,
- borrowerDebt: 21_049.006823139002918431 * 1e18,
- borrowerCollateral: 50 * 1e18,
- borrowert0Np: 441.424038461538461742 * 1e18,
- borrowerCollateralization: 7.081111825921092812 * 1e18
- }
- );
+ _assertBorrower({
+ borrower: _borrower,
+ borrowerDebt: 21_049.006823139002918431 * 1e18,
+ borrowerCollateral: 50 * 1e18,
+ borrowert0Np: 441.424038461538461742 * 1e18,
+ borrowerCollateralization: 7.081111825921092812 * 1e18
+ });
+
assertEq(_collateral.balanceOf(_borrower), 100 * 1e18);
// remove all of the remaining claimable collateral
@@ -184,39 +173,150 @@ contract ERC20PoolCollateralTest is ERC20HelperContract {
interestRateUpdate: _startTime + 10 days
})
);
- _assertBorrower(
- {
- borrower: _borrower,
- borrowerDebt: 21_049.006823139002918431 * 1e18,
- borrowerCollateral: 7.061038044473493202 * 1e18,
- borrowert0Np: 3_140.657612229160876676 * 1e18,
- borrowerCollateralization: 1 * 1e18
- }
- );
+ _assertBorrower({
+ borrower: _borrower,
+ borrowerDebt: 21_049.006823139002918431 * 1e18,
+ borrowerCollateral: 7.061038044473493202 * 1e18,
+ borrowert0Np: 3_140.657612229160876676 * 1e18,
+ borrowerCollateralization: 1 * 1e18
+ });
+
assertEq(_collateral.balanceOf(_borrower), 142.938961955526506798 * 1e18);
}
+ /**
+ * @notice With 1 lender and 1 borrower test pledgeCollateral, borrow, pull and transfer collateral to a different recipient.
+ */
+ function testPledgeAndPullCollateralToDifferentRecipient() external tearDown {
+ // lender deposits 10000 Quote into 3 buckets
+
+ address collateralReceiver = makeAddr("receiver");
+
+ _addInitialLiquidity({
+ from: _lender,
+ amount: 10_000 * 1e18,
+ index: 2550
+ });
+ _addInitialLiquidity({
+ from: _lender,
+ amount: 10_000 * 1e18,
+ index: 2551
+ });
+ _addInitialLiquidity({
+ from: _lender,
+ amount: 10_000 * 1e18,
+ index: 2552
+ });
+
+ assertEq(_collateral.balanceOf(collateralReceiver), 0);
+ assertEq(_collateral.balanceOf(_borrower), 150 * 1e18);
+
+ // borrower pledge 100 collateral and get a 21_000 Quote loan
+ _pledgeCollateral({
+ from: _borrower,
+ borrower: _borrower,
+ amount: 100 * 1e18
+ });
+ _borrow({
+ from: _borrower,
+ amount: 21_000 * 1e18,
+ indexLimit: 3_000,
+ newLup: 2_981.007422784467321543 * 1e18
+ });
+
+ _assertPool(
+ PoolParams({
+ htp: 210.201923076923077020 * 1e18,
+ lup: 2_981.007422784467321543 * 1e18,
+ poolSize: 30_000 * 1e18,
+ pledgedCollateral: 100 * 1e18,
+ encumberedCollateral: 7.051372011699988577 * 1e18,
+ poolDebt: 21_020.192307692307702000 * 1e18,
+ actualUtilization: 0.700673076923076923 * 1e18,
+ targetUtilization: 1e18,
+ minDebtAmount: 2_102.019230769230770200 * 1e18,
+ loans: 1,
+ maxBorrower: _borrower,
+ interestRate: 0.05 * 1e18,
+ interestRateUpdate: _startTime
+ })
+ );
+ _assertBorrower({
+ borrower: _borrower,
+ borrowerDebt: 21_020.192307692307702000 * 1e18,
+ borrowerCollateral: 100 * 1e18,
+ borrowert0Np: 220.712019230769230871 * 1e18,
+ borrowerCollateralization: 14.181637252165253251 * 1e18
+ });
+
+ assertEq(_collateral.balanceOf(collateralReceiver), 0);
+ assertEq(_collateral.balanceOf(_borrower), 50 * 1e18);
+
+ // pass time to allow interest to accrue
+ skip(10 days);
+
+ // remove some of the collateral and transfer to recipient
+ _repayDebtAndPullToRecipient({
+ from: _borrower,
+ borrower: _borrower,
+ recipient: collateralReceiver,
+ amountToRepay: 0,
+ amountRepaid: 0,
+ collateralToPull: 50 * 1e18,
+ newLup: 2_981.007422784467321543 * 1e18
+ });
+
+ _assertBorrower({
+ borrower: _borrower,
+ borrowerDebt: 21_049.006823139002918431 * 1e18,
+ borrowerCollateral: 50 * 1e18,
+ borrowert0Np: 441.424038461538461742 * 1e18,
+ borrowerCollateralization: 7.081111825921092812 * 1e18
+ });
+
+ assertEq(_collateral.balanceOf(collateralReceiver), 50 * 1e18);
+ assertEq(_collateral.balanceOf(_borrower), 50 * 1e18);
+
+ // remove all of the remaining claimable collateral
+ _repayDebtAndPullToRecipient({
+ from: _borrower,
+ borrower: _borrower,
+ recipient: collateralReceiver,
+ amountToRepay: 0,
+ amountRepaid: 0,
+ collateralToPull: 50 * 1e18 - _encumberance(21_049.006823139002918431 * 1e18, _lup()),
+ newLup: 2_981.007422784467321543 * 1e18
+ });
+
+ _assertBorrower({
+ borrower: _borrower,
+ borrowerDebt: 21_049.006823139002918431 * 1e18,
+ borrowerCollateral: 7.061038044473493202 * 1e18,
+ borrowert0Np: 3_140.657612229160876676 * 1e18,
+ borrowerCollateralization: 1 * 1e18
+ });
+
+ assertEq(_collateral.balanceOf(collateralReceiver), 92.938961955526506798 * 1e18);
+ assertEq(_collateral.balanceOf(_borrower), 50 * 1e18);
+ }
+
/**
* @notice 1 borrower tests reverts in pullCollateral.
* Reverts:
* Attempts to remove more than available claimable collateral.
*/
function testPullCollateralRequireEnoughCollateral() external tearDown {
- _assertPullInsufficientCollateralRevert(
- {
- from: _borrower,
- amount: 100 * 1e18
- }
- );
+ _assertPullInsufficientCollateralRevert({
+ from: _borrower,
+ amount: 100 * 1e18
+ });
// borrower deposits 100 collateral
- _pledgeCollateral(
- {
- from: _borrower,
- borrower: _borrower,
- amount: 100 * 1e18
- }
- );
+ _pledgeCollateral({
+ from: _borrower,
+ borrower: _borrower,
+ amount: 100 * 1e18
+ });
// should be able to now remove collateral
_repayDebtNoLupCheck({
@@ -236,105 +336,90 @@ contract ERC20PoolCollateralTest is ERC20HelperContract {
_mintCollateralAndApproveTokens(_bidder, 100 * 1e18);
// should revert if adding collateral at index 0
- _assertAddCollateralAtIndex0Revert(
- {
- from: _bidder,
- amount: 4 * 1e18
- }
- );
+ _assertAddCollateralAtIndex0Revert({
+ from: _bidder,
+ amount: 4 * 1e18
+ });
// actor deposits collateral into a bucket
- _addCollateral(
- {
- from: _bidder,
- amount: 4 * 1e18,
- index: 2550,
- lpAward: 12_043.56808879152623138 * 1e27
- }
- );
+ _addCollateral({
+ from: _bidder,
+ amount: 4 * 1e18,
+ index: 2550,
+ lpAward: 12_043.56808879152623138 * 1e18
+ });
// check bucket state and bidder's LPs
- _assertBucket(
- {
- index: 2550,
- lpBalance: 12_043.56808879152623138 * 1e27,
- collateral: 4 * 1e18,
- deposit: 0,
- exchangeRate: 1 * 1e27
- }
- );
- _assertLenderLpBalance(
- {
- lender: _bidder,
- index: 2550,
- lpBalance: 12_043.56808879152623138 * 1e27,
- depositTime: _startTime
- }
- );
+ _assertBucket({
+ index: 2550,
+ lpBalance: 12_043.56808879152623138 * 1e18,
+ collateral: 4 * 1e18,
+ deposit: 0,
+ exchangeRate: 1 * 1e18
+ });
+ _assertLenderLpBalance({
+ lender: _bidder,
+ index: 2550,
+ lpBalance: 12_043.56808879152623138 * 1e18,
+ depositTime: _startTime
+ });
+
// check balances
assertEq(_collateral.balanceOf(_bidder), 96 * 1e18);
assertEq(_collateral.balanceOf(address(_pool)), 4 * 1e18);
assertEq(_quote.balanceOf(address(_pool)), 0);
// actor withdraws some of their collateral
- _removeCollateral(
- {
- from: _bidder,
- amount: 1.53 * 1e18,
- index: 2550,
- lpRedeem: 4_606.664793962758783502850000000 * 1e27
- }
- );
+ _removeCollateral({
+ from: _bidder,
+ amount: 1.53 * 1e18,
+ index: 2550,
+ lpRedeem: 4_606.664793962758783503 * 1e18
+ });
+
// check bucket state and bidder's LPs
- _assertBucket(
- {
- index: 2550,
- lpBalance: 7_436.90329482876744787715 * 1e27,
- collateral: 2.47 * 1e18,
- deposit: 0,
- exchangeRate: 1 * 1e27
- }
- );
- _assertLenderLpBalance(
- {
- lender: _bidder,
- index: 2550,
- lpBalance: 7_436.90329482876744787715 * 1e27,
- depositTime: _startTime
- }
- );
+ _assertBucket({
+ index: 2550,
+ lpBalance: 7_436.903294828767447877 * 1e18,
+ collateral: 2.47 * 1e18,
+ deposit: 0,
+ exchangeRate: 1 * 1e18
+ });
+ _assertLenderLpBalance({
+ lender: _bidder,
+ index: 2550,
+ lpBalance: 7_436.903294828767447877 * 1e18,
+ depositTime: _startTime
+ });
+
// check balances
assertEq(_collateral.balanceOf(_bidder), 97.53 * 1e18);
assertEq(_collateral.balanceOf(address(_pool)), 2.47 * 1e18);
assertEq(_quote.balanceOf(address(_pool)), 0);
// actor withdraws remainder of their _collateral
- _removeCollateral(
- {
- from: _bidder,
- amount: 2.47 * 1e18,
- index: 2550,
- lpRedeem: 7_436.90329482876744787715 * 1e27
- }
- );
+ _removeCollateral({
+ from: _bidder,
+ amount: 2.47 * 1e18,
+ index: 2550,
+ lpRedeem: 7_436.903294828767447877 * 1e18
+ });
+
// check bucket state and bidder's LPs
- _assertBucket(
- {
- index: 2550,
- lpBalance: 0,
- collateral: 0,
- deposit: 0,
- exchangeRate: 1 * 1e27
- }
- );
- _assertLenderLpBalance(
- {
- lender: _bidder,
- index: 2550,
- lpBalance: 0,
- depositTime: _startTime
- }
- );
+ _assertBucket({
+ index: 2550,
+ lpBalance: 0,
+ collateral: 0,
+ deposit: 0,
+ exchangeRate: 1 * 1e18
+ });
+ _assertLenderLpBalance({
+ lender: _bidder,
+ index: 2550,
+ lpBalance: 0,
+ depositTime: _startTime
+ });
+
// check balances
assertEq(_collateral.balanceOf(_bidder), 100 * 1e18);
assertEq(_collateral.balanceOf(address(_pool)), 0);
@@ -346,73 +431,63 @@ contract ERC20PoolCollateralTest is ERC20HelperContract {
_mintCollateralAndApproveTokens(_bidder, 1 * 1e18);
// actor deposits collateral into a bucket
- _addCollateral(
- {
- from: _bidder,
- amount: 1 * 1e18,
- index: 1530,
- lpAward: 487616.252661175041981841 * 1e27
- }
- );
+ _addCollateral({
+ from: _bidder,
+ amount: 1 * 1e18,
+ index: 1530,
+ lpAward: 487616.252661175041981841 * 1e18
+ });
+
+ _removeCollateral({
+ from: _bidder,
+ amount: 0.5 * 1e18,
+ index: 1530,
+ lpRedeem: 243_808.126330587520990921 * 1e18
+ });
- _removeCollateral(
- {
- from: _bidder,
- amount: 0.5 * 1e18,
- index: 1530,
- lpRedeem: 243_808.1263305875209909205 * 1e27
- }
- );
// check bucket state and bidder's LPs
- _assertBucket(
- {
- index: 1530,
- lpBalance: 243_808.1263305875209909205 * 1e27,
- collateral: 0.5 * 1e18,
- deposit: 0,
- exchangeRate: 1 * 1e27
- }
- );
- _assertLenderLpBalance(
- {
- lender: _bidder,
- index: 1530,
- lpBalance: 243_808.1263305875209909205 * 1e27,
- depositTime: _startTime
- }
- );
+ _assertBucket({
+ index: 1530,
+ lpBalance: 243_808.126330587520990920 * 1e18,
+ collateral: 0.5 * 1e18,
+ deposit: 0,
+ exchangeRate: 1 * 1e18
+ });
+ _assertLenderLpBalance({
+ lender: _bidder,
+ index: 1530,
+ lpBalance: 243_808.126330587520990920 * 1e18,
+ depositTime: _startTime
+ });
+
// check balances
assertEq(_collateral.balanceOf(_bidder), 0.5 * 1e18);
assertEq(_collateral.balanceOf(address(_pool)), 0.5 * 1e18);
assertEq(_quote.balanceOf(address(_pool)), 0);
// actor withdraws remainder of their _collateral
- _removeAllCollateral(
- {
- from: _bidder,
- amount: 0.5 * 1e18,
- index: 1530,
- lpRedeem: 243_808.1263305875209909205 * 1e27
- }
- );
+ _removeAllCollateral({
+ from: _bidder,
+ amount: 0.5 * 1e18,
+ index: 1530,
+ lpRedeem: 243_808.126330587520990920 * 1e18
+ });
+
// check bucket state and bidder's LPs
- _assertBucket(
- {
- index: 1530,
- lpBalance: 0,
- collateral: 0,
- deposit: 0,
- exchangeRate: 1 * 1e27
- }
- );
- _assertLenderLpBalance(
- {
- lender: _bidder,
- index: 1530,
- lpBalance: 0,
- depositTime: _startTime
- }
- );
+ _assertBucket({
+ index: 1530,
+ lpBalance: 0,
+ collateral: 0,
+ deposit: 0,
+ exchangeRate: 1 * 1e18
+ });
+ _assertLenderLpBalance({
+ lender: _bidder,
+ index: 1530,
+ lpBalance: 0,
+ depositTime: _startTime
+ });
+
// check balances
assertEq(_collateral.balanceOf(_bidder), 1 * 1e18);
assertEq(_collateral.balanceOf(address(_pool)), 0 * 1e18);
@@ -423,42 +498,44 @@ contract ERC20PoolCollateralTest is ERC20HelperContract {
uint256 testIndex = 6348;
// should revert if no collateral in the bucket
- _assertRemoveInsufficientCollateralRevert(
- {
- from: _lender,
- amount: 3.50 * 1e18,
- index: testIndex
- }
- );
+ _assertRemoveInsufficientCollateralRevert({
+ from: _lender,
+ amount: 3.50 * 1e18,
+ index: testIndex
+ });
// another actor deposits some collateral
deal(address(_collateral), _bidder, 100 * 1e18);
+
changePrank(_bidder);
_collateral.approve(address(_pool), 100 * 1e18);
- _addCollateral(
- {
- from: _bidder,
- amount: 0.65 * 1e18,
- index: testIndex,
- lpAward: 0.0000116119721720119 * 1e27
- }
- );
+
+ _addCollateral({
+ from: _bidder,
+ amount: 0.65 * 1e18,
+ index: testIndex,
+ lpAward: 0.000011611972172012 * 1e18
+ });
// should revert if actor has no LPB in the bucket
- _assertRemoveAllCollateralNoClaimRevert(
- {
- from: _lender,
- index: testIndex
- }
- );
+ _assertRemoveAllCollateralNoClaimRevert({
+ from: _lender,
+ index: testIndex
+ });
// should revert if actor does not have LP
- _assertRemoveAllCollateralNoClaimRevert(
- {
- from: _lender,
- index: testIndex
- }
- );
+ _assertRemoveAllCollateralNoClaimRevert({
+ from: _lender,
+ index: testIndex
+ });
+
+ // should revert if expiration passed
+ _assertAddCollateralExpiredRevert({
+ from: _lender,
+ amount: 0.5 * 1e18,
+ index: testIndex,
+ expiry: block.timestamp - 2 minutes
+ });
}
function testPledgeCollateralFromDifferentActor() external tearDown {
@@ -480,17 +557,16 @@ contract ERC20PoolCollateralTest is ERC20HelperContract {
interestRateUpdate: _startTime
})
);
+
assertEq(_collateral.balanceOf(_borrower), 150 * 1e18);
assertEq(_collateral.balanceOf(_borrower2), 100 * 1e18);
// borrower deposits 100 collateral
- _pledgeCollateral(
- {
- from: _borrower2,
- borrower: _borrower2,
- amount: 100 * 1e18
- }
- );
+ _pledgeCollateral({
+ from: _borrower2,
+ borrower: _borrower2,
+ amount: 100 * 1e18
+ });
// check pool state collateral accounting updated properly
_assertPool(
@@ -510,7 +586,158 @@ contract ERC20PoolCollateralTest is ERC20HelperContract {
interestRateUpdate: _startTime
})
);
+
assertEq(_collateral.balanceOf(_borrower), 150 * 1e18);
assertEq(_collateral.balanceOf(_borrower2), 0);
}
+
+ function testAddRemoveCollateralBucketExchangeRateInvariantDifferentActor() external tearDown {
+ _mintCollateralAndApproveTokens(_lender, 50000000000 * 1e18);
+
+ _addInitialLiquidity({
+ from: _bidder,
+ amount: 6879,
+ index: 2570
+ });
+
+ _assertLenderLpBalance({
+ lender: _lender,
+ index: 2570,
+ lpBalance: 0,
+ depositTime: 0
+ });
+ _assertLenderLpBalance({
+ lender: _bidder,
+ index: 2570,
+ lpBalance: 6879,
+ depositTime: _startTime
+ });
+ _assertBucket({
+ index: 2570,
+ lpBalance: 6879,
+ collateral: 0,
+ deposit: 6879,
+ exchangeRate: 1 * 1e18 // exchange rate should not change
+ });
+
+ _addCollateral({
+ from: _lender,
+ amount: 3642907759.282013932739218713 * 1e18,
+ index: 2570,
+ lpAward: 9927093687851.086595628225711617 * 1e18
+ });
+
+ _assertLenderLpBalance({
+ lender: _lender,
+ index: 2570,
+ lpBalance: 9927093687851.086595628225711617 * 1e18,
+ depositTime: _startTime
+ });
+ _assertLenderLpBalance({
+ lender: _bidder,
+ index: 2570,
+ lpBalance: 6879,
+ depositTime: _startTime
+ });
+ _assertBucket({
+ index: 2570,
+ lpBalance: 9927093687851.086595628225718496 * 1e18,
+ collateral: 3642907759.282013932739218713 * 1e18,
+ deposit: 6879,
+ exchangeRate: 1 * 1e18 // exchange rate should not change
+ });
+
+ _removeAllCollateral({
+ from: _lender,
+ amount: 3642907759.282013932739218713 * 1e18,
+ index: 2570,
+ lpRedeem: 9927093687851.086595628225711617 * 1e18
+ });
+
+ _assertLenderLpBalance({
+ lender: _lender,
+ index: 2570,
+ lpBalance: 0, // LPs should get back to same value as before add / remove collateral
+ depositTime: _startTime
+ });
+ _assertLenderLpBalance({
+ lender: _bidder,
+ index: 2570,
+ lpBalance: 6879, // LPs should get back to same value as before add / remove collateral
+ depositTime: _startTime
+ });
+ _assertBucket({
+ index: 2570,
+ lpBalance: 6879,
+ collateral: 0,
+ deposit: 6879,
+ exchangeRate: 1 * 1e18 // exchange rate should not change
+ });
+ }
+
+ function testAddRemoveCollateralBucketExchangeRateInvariantSameActor() external tearDown {
+ _mintCollateralAndApproveTokens(_lender, 50000000000 * 1e18);
+
+ _addInitialLiquidity({
+ from: _lender,
+ amount: 6879,
+ index: 2570
+ });
+
+ _assertLenderLpBalance({
+ lender: _lender,
+ index: 2570,
+ lpBalance: 6879,
+ depositTime: _startTime
+ });
+ _assertBucket({
+ index: 2570,
+ lpBalance: 6879,
+ collateral: 0,
+ deposit: 6879,
+ exchangeRate: 1 * 1e18 // exchange rate should not change
+ });
+
+ _addCollateral({
+ from: _lender,
+ amount: 3642907759.282013932739218713 * 1e18,
+ index: 2570,
+ lpAward: 9927093687851.086595628225711617 * 1e18
+ });
+
+ _assertLenderLpBalance({
+ lender: _lender,
+ index: 2570,
+ lpBalance: 9927093687851.086595628225718496 * 1e18,
+ depositTime: _startTime
+ });
+ _assertBucket({
+ index: 2570,
+ lpBalance: 9927093687851.086595628225718496 * 1e18,
+ collateral: 3642907759.282013932739218713 * 1e18,
+ deposit: 6879,
+ exchangeRate: 1 * 1e18 // exchange rate should not change
+ });
+
+ _removeAllCollateral({
+ from: _lender,
+ amount: 3642907759.282013932739218713 * 1e18,
+ index: 2570,
+ lpRedeem: 9927093687851.086595628225711617 * 1e18
+ });
+
+ _assertLenderLpBalance({
+ lender: _lender,
+ index: 2570,
+ lpBalance: 6879, // LPs should get back to same value as before add / remove collateral
+ depositTime: _startTime
+ });
+ _assertBucket({
+ index: 2570,
+ lpBalance: 6879,
+ collateral: 0,
+ deposit: 6879,
+ exchangeRate: 1 * 1e18 // exchange rate should not change
+ });
+ }
}
diff --git a/tests/forge/ERC20Pool/ERC20PoolFactory.t.sol b/tests/forge/ERC20Pool/ERC20PoolFactory.t.sol
index c7b1d9015..a9e8d58f9 100644
--- a/tests/forge/ERC20Pool/ERC20PoolFactory.t.sol
+++ b/tests/forge/ERC20Pool/ERC20PoolFactory.t.sol
@@ -3,6 +3,8 @@ pragma solidity 0.8.14;
import { ERC20HelperContract } from './ERC20DSTestPlus.sol';
+import { Token } from '../utils/Tokens.sol';
+
import { ERC20Pool } from 'src/ERC20Pool.sol';
import { ERC20PoolFactory } from 'src/ERC20PoolFactory.sol';
import { IPoolErrors } from 'src/interfaces/pool/commons/IPoolErrors.sol';
@@ -23,46 +25,38 @@ contract ERC20PoolFactoryTest is ERC20HelperContract {
function testDeployERC20PoolWithZeroAddress() external {
// should revert if trying to deploy with zero address as collateral
- _assertDeployWith0xAddressRevert(
- {
- poolFactory: address(_poolFactory),
- collateral: address(0),
- quote: address(_quote),
- interestRate: 0.05 * 10**18
- }
- );
+ _assertDeployWith0xAddressRevert({
+ poolFactory: address(_poolFactory),
+ collateral: address(0),
+ quote: address(_quote),
+ interestRate: 0.05 * 10**18
+ });
// should revert if trying to deploy with zero address as quote token
- _assertDeployWith0xAddressRevert(
- {
- poolFactory: address(_poolFactory),
- collateral: address(_collateral),
- quote: address(0),
- interestRate: 0.05 * 10**18
- }
- );
+ _assertDeployWith0xAddressRevert({
+ poolFactory: address(_poolFactory),
+ collateral: address(_collateral),
+ quote: address(0),
+ interestRate: 0.05 * 10**18
+ });
}
function testDeployERC20PoolWithInvalidRate() external {
// should revert if trying to deploy with interest rate lower than accepted
- _assertDeployWithInvalidRateRevert(
- {
- poolFactory: address(_poolFactory),
- collateral: address(_collateral),
- quote: address(_quote),
- interestRate: 10**18
- }
- );
+ _assertDeployWithInvalidRateRevert({
+ poolFactory: address(_poolFactory),
+ collateral: address(_collateral),
+ quote: address(_quote),
+ interestRate: 10**18
+ });
// should revert if trying to deploy with interest rate higher than accepted
- _assertDeployWithInvalidRateRevert(
- {
- poolFactory: address(_poolFactory),
- collateral: address(_collateral),
- quote: address(_quote),
- interestRate: 2 * 10**18
- }
- );
+ _assertDeployWithInvalidRateRevert({
+ poolFactory: address(_poolFactory),
+ collateral: address(_collateral),
+ quote: address(_quote),
+ interestRate: 2 * 10**18
+ });
// check tracking of deployed pools
assertEq(_poolFactory.getDeployedPoolsList().length, 0);
@@ -72,14 +66,12 @@ contract ERC20PoolFactoryTest is ERC20HelperContract {
address poolOne = _poolFactory.deployPool(address(_collateral), address(_quote), 0.05 * 10**18);
// should revert if trying to deploy same pool one more time
- _assertDeployMultipleTimesRevert(
- {
- poolFactory: address(_poolFactory),
- collateral: address(_collateral),
- quote: address(_quote),
- interestRate: 0.05 * 10**18
- }
- );
+ _assertDeployMultipleTimesRevert({
+ poolFactory: address(_poolFactory),
+ collateral: address(_collateral),
+ quote: address(_quote),
+ interestRate: 0.05 * 10**18
+ });
// should deploy different pool
address poolTwo = _poolFactory.deployPool(address(_collateral), address(_collateral), 0.05 * 10**18);
@@ -94,6 +86,22 @@ contract ERC20PoolFactoryTest is ERC20HelperContract {
assertEq(_poolFactory.deployedPoolsList(1), poolTwo);
}
+ function testDeployERC20PoolWithMinRate() external {
+ _poolFactory.deployPool(address(new Token("Collateral", "C1")), address(new Token("Quote", "Q1")), 0.01 * 10**18);
+
+ // check tracking of deployed pools
+ assertEq(_poolFactory.getDeployedPoolsList().length, 1);
+ assertEq(_poolFactory.getNumberOfDeployedPools(), 1);
+ }
+
+ function testDeployERC20PoolWithMaxRate() external {
+ _poolFactory.deployPool(address(new Token("Collateral", "C1")), address(new Token("Quote", "Q1")), 0.1 * 10**18);
+
+ // check tracking of deployed pools
+ assertEq(_poolFactory.getDeployedPoolsList().length, 1);
+ assertEq(_poolFactory.getNumberOfDeployedPools(), 1);
+ }
+
function testDeployERC20Pool() external {
skip(333);
diff --git a/tests/forge/ERC20Pool/ERC20PoolFlashloan.t.sol b/tests/forge/ERC20Pool/ERC20PoolFlashloan.t.sol
new file mode 100644
index 000000000..a316f48ad
--- /dev/null
+++ b/tests/forge/ERC20Pool/ERC20PoolFlashloan.t.sol
@@ -0,0 +1,266 @@
+// SPDX-License-Identifier: UNLICENSED
+pragma solidity 0.8.14;
+
+import '@openzeppelin/contracts/token/ERC20/ERC20.sol';
+
+import { ERC20HelperContract } from './ERC20DSTestPlus.sol';
+import {
+ FlashloanBorrower,
+ SomeDefiStrategy,
+ SomeDefiStrategyWithRepayment
+} from '../utils/FlashloanBorrower.sol';
+
+import 'src/libraries/helpers/PoolHelper.sol';
+import 'src/ERC20Pool.sol';
+import 'src/ERC20PoolFactory.sol';
+
+import { IPoolErrors } from 'src/interfaces/pool/IPool.sol';
+
+contract ERC20PoolFlashloanTest is ERC20HelperContract {
+ address internal _borrower;
+ address internal _lender;
+ uint internal _bucketId;
+ uint internal _bucketPrice;
+
+ function setUp() external {
+ _lender = makeAddr("lender");
+ _borrower = makeAddr("borrower");
+
+ _mintQuoteAndApproveTokens(_lender, 100_000 * 1e18);
+ _mintQuoteAndApproveTokens(_borrower, 5_000 * 1e18);
+ _mintCollateralAndApproveTokens(_borrower, 100 * 1e18);
+
+ // lender adds liquidity
+ _bucketPrice = 502.433988063349232760 * 1e18;
+ _bucketId = _indexOf(_bucketPrice);
+ assertEq(_bucketId, 2909);
+
+ _addInitialLiquidity({
+ from: _lender,
+ amount: 100_000 * 1e18,
+ index: _bucketId
+ });
+
+ // borrower draws debt
+ _pledgeCollateral({
+ from: _borrower,
+ borrower: _borrower,
+ amount: 100 * 1e18
+ });
+ _borrow({
+ from: _borrower,
+ amount: 25_000 * 1e18,
+ indexLimit: _bucketId,
+ newLup: _bucketPrice
+ });
+
+ (uint256 poolDebt,,) = _pool.debtInfo();
+ assertEq(poolDebt, 25_024.038461538461550000 * 1e18);
+ }
+
+ function testCollateralFlashloan() external tearDown {
+ skip(1 days);
+ uint256 loanAmount = 100 * 1e18;
+ assertEq(_pool.maxFlashLoan(address(_collateral)), loanAmount);
+
+ // Create an example defi strategy
+ SomeDefiStrategy strategy = new SomeDefiStrategy(_collateral);
+ deal(address(_collateral), address(strategy), 10 * 1e18);
+
+ // Create a flashloan borrower contract which interacts with the strategy
+ bytes memory strategyCalldata = abi.encodeWithSignature("makeMoney(uint256)", loanAmount);
+ FlashloanBorrower flasher = new FlashloanBorrower(address(strategy), strategyCalldata);
+
+ // Run the token approvals
+ changePrank(address(flasher));
+ _collateral.approve(address(_pool), loanAmount);
+ _collateral.approve(address(strategy), loanAmount);
+
+ // Use a flashloan to interact with the strategy
+ assertEq(_collateral.balanceOf(address(flasher)), 0);
+ assertTrue(!flasher.callbackInvoked());
+ _pool.flashLoan(flasher, address(_collateral), loanAmount, new bytes(0));
+ assertTrue(flasher.callbackInvoked());
+ assertEq(_collateral.balanceOf(address(flasher)), 3.5 * 1e18);
+ }
+
+ function testFlashloanFee() external tearDown {
+ uint256 loanAmount = 100 * 1e18;
+
+ // Ensure there is no fee for quote token
+ uint256 fee = _pool.flashFee(address(_quote), loanAmount);
+ assertEq(fee, 0);
+
+ // Ensure there is no fee for collateral
+ fee = _pool.flashFee(address(_collateral), loanAmount);
+ assertEq(fee, 0);
+
+ // Ensure fee reverts for a random address which isn't a token
+ _assertFlashloanFeeRevertsForToken(makeAddr("nobody"), loanAmount);
+ }
+
+ function testMaxFlashloan() external tearDown {
+ assertEq(_pool.maxFlashLoan(_pool.quoteTokenAddress()), 75_000 * 1e18);
+ assertEq(_pool.maxFlashLoan(_pool.collateralAddress()), 100 * 1e18);
+ assertEq(_pool.maxFlashLoan(makeAddr("nobody")), 0);
+ }
+
+ function testCannotFlashloanMoreCollateralThanAvailable() external tearDown {
+ FlashloanBorrower flasher = new FlashloanBorrower(address(0), new bytes(0));
+
+ // Cannot flashloan less than pool size but more than available quote token
+ _assertFlashloanTooLargeRevert(flasher, _pool.quoteTokenAddress(), 90_000 * 1e18);
+
+ // Cannot flashloan more collateral than pledged
+ _assertFlashloanTooLargeRevert(flasher, _pool.collateralAddress(), 150 * 1e18);
+ }
+
+ function testCannotFlashloanNonToken() external tearDown {
+ FlashloanBorrower flasher = new FlashloanBorrower(address(0), new bytes(0));
+
+ // Cannot flashloan a random address which isn't a token
+ _assertFlashloanUnavailableForToken(flasher, makeAddr("nobody"), 1);
+ }
+
+ function testCallbackFailure() external tearDown {
+ uint256 loanAmount = 100 * 1e18;
+
+ // Create an example defi strategy
+ SomeDefiStrategy strategy = new SomeDefiStrategy(_collateral);
+
+ // Create a flashloan borrower contract which invokes a non-existant method on the strategy
+ bytes memory strategyCalldata = abi.encodeWithSignature("missing()");
+ FlashloanBorrower flasher = new FlashloanBorrower(address(strategy), strategyCalldata);
+
+ // Run approvals
+ changePrank(address(flasher));
+ _quote.approve(address(_pool), loanAmount);
+
+ // Make a failed attempt to interact with the strategy
+ vm.expectRevert(IPoolErrors.FlashloanCallbackFailed.selector);
+ _pool.flashLoan(flasher, address(_collateral), loanAmount, new bytes(0));
+ assertFalse(flasher.callbackInvoked());
+ }
+
+ function testIncorrectBalanceAfterFlashloanFailure() external tearDown {
+ skip(1 days);
+ uint256 loanAmount = 100 * 1e18;
+ assertEq(_pool.maxFlashLoan(address(_collateral)), loanAmount);
+
+ // Create an example defi strategy that pays a fee to pool contract
+ SomeDefiStrategyWithRepayment strategy = new SomeDefiStrategyWithRepayment(_collateral, address(_pool));
+ deal(address(_collateral), address(strategy), 10 * 1e18);
+
+ // Create a flashloan borrower contract which interacts with the strategy
+ bytes memory strategyCalldata = abi.encodeWithSignature("makeMoney(uint256)", loanAmount);
+ FlashloanBorrower flasher = new FlashloanBorrower(address(strategy), strategyCalldata);
+
+ // Run the token approvals
+ changePrank(address(flasher));
+ _collateral.approve(address(_pool), loanAmount);
+ _collateral.approve(address(strategy), loanAmount);
+
+ // should revert as the pool balance after flashloan is different than the initial balance
+ vm.expectRevert(IPoolErrors.FlashloanIncorrectBalance.selector);
+ _pool.flashLoan(flasher, address(_collateral), loanAmount, new bytes(0));
+ }
+}
+
+contract ERC20PoolFlashloanPrecisionTest is ERC20HelperContract {
+
+ ERC20 WBTC = ERC20(0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599);
+ ERC20 USDC = ERC20(0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48);
+
+ address internal _borrower;
+ address internal _lender;
+
+ function setUp() external {
+ _pool = ERC20Pool(new ERC20PoolFactory(_ajna).deployPool(address(WBTC), address(USDC), 0.05 * 10**18));
+
+ _borrower = makeAddr("borrower");
+ _lender = makeAddr("lender");
+
+ deal(address(WBTC), _borrower, 10 * 1e8);
+
+ deal(address(USDC), _lender, 10_000 * 1e6);
+
+ vm.startPrank(_borrower);
+ WBTC.approve(address(_pool), 10 * 1e18);
+
+ changePrank(_lender);
+ USDC.approve(address(_pool), 10_000 * 1e18);
+
+ _addInitialLiquidity({
+ from: _lender,
+ amount: 10_000 * 1e18,
+ index: 2500
+ });
+
+ _pledgeCollateral({
+ from: _borrower,
+ borrower: _borrower,
+ amount: 10 * 1e18
+ });
+ }
+
+ function testWbtcFlashloan() external tearDown {
+ skip(1 days);
+ uint256 loanAmount = 10 * 1e8;
+ assertEq(_pool.maxFlashLoan(address(WBTC)), 10 * 1e8);
+
+ // Create an example defi strategy
+ SomeDefiStrategy strategy = new SomeDefiStrategy(WBTC);
+ deal(address(WBTC), address(strategy), 10 * 1e8);
+
+ // Create a flashloan borrower contract which interacts with the strategy
+ bytes memory strategyCalldata = abi.encodeWithSignature("makeMoney(uint256)", loanAmount);
+ FlashloanBorrower flasher = new FlashloanBorrower(address(strategy), strategyCalldata);
+
+ // Run the token approvals
+ changePrank(address(flasher));
+ WBTC.approve(address(_pool), loanAmount);
+ WBTC.approve(address(strategy), loanAmount);
+
+ // cannot flashloan more than available in pool (by specifying pool instead collateral precision)
+ vm.expectRevert('SafeERC20: low-level call failed');
+ _pool.flashLoan(flasher, address(WBTC), 10 * 1e18, new bytes(0));
+
+ // Use a flashloan to interact with the strategy
+ assertEq(WBTC.balanceOf(address(flasher)), 0);
+ assertTrue(!flasher.callbackInvoked());
+ _pool.flashLoan(flasher, address(WBTC), loanAmount, new bytes(0));
+ assertTrue(flasher.callbackInvoked());
+ assertEq(WBTC.balanceOf(address(flasher)), 0.35 * 1e8);
+ }
+
+ function testUsdcFlashloan() external tearDown {
+ skip(1 days);
+ uint256 loanAmount = 10_000 * 1e6;
+ assertEq(_pool.maxFlashLoan(address(USDC)), loanAmount);
+
+ // Create an example defi strategy which produces enough yield to pay the fee
+ SomeDefiStrategy strategy = new SomeDefiStrategy(USDC);
+ deal(address(USDC), address(strategy), 10_000 * 1e6);
+
+ // Create a flashloan borrower contract which interacts with the strategy
+ bytes memory strategyCalldata = abi.encodeWithSignature("makeMoney(uint256)", loanAmount);
+ FlashloanBorrower flasher = new FlashloanBorrower(address(strategy), strategyCalldata);
+
+ // Run approvals
+ changePrank(address(flasher));
+ USDC.approve(address(_pool), loanAmount);
+ USDC.approve(address(strategy), loanAmount);
+
+ // cannot flashloan more than available in pool (by specifying pool instead quote token precision)
+ vm.expectRevert('ERC20: transfer amount exceeds balance');
+ _pool.flashLoan(flasher, address(USDC), 10_000 * 1e18, new bytes(0));
+
+ // Use a flashloan to interact with the strategy
+ assertEq(USDC.balanceOf(address(flasher)), 0);
+ assertTrue(!flasher.callbackInvoked());
+ _pool.flashLoan(flasher, address(USDC), loanAmount, new bytes(0));
+ assertTrue(flasher.callbackInvoked());
+ assertEq(USDC.balanceOf(address(flasher)), 350 * 1e6);
+ }
+
+}
\ No newline at end of file
diff --git a/tests/forge/ERC20Pool/ERC20PoolGasLoadTest.t.sol b/tests/forge/ERC20Pool/ERC20PoolGasLoadTest.t.sol
index 23e301817..bd028a147 100644
--- a/tests/forge/ERC20Pool/ERC20PoolGasLoadTest.t.sol
+++ b/tests/forge/ERC20Pool/ERC20PoolGasLoadTest.t.sol
@@ -45,15 +45,13 @@ contract ERC20PoolGasLoadTest is ERC20DSTestPlus {
_mintQuoteAndApproveTokens(lender, 200_000 * 1e18);
vm.startPrank(lender);
- _pool.addQuoteToken(100_000 * 1e18, 7388 - i);
- _pool.addQuoteToken(100_000 * 1e18, 1 + i);
+ _pool.addQuoteToken(100_000 * 1e18, 7388 - i, block.timestamp + 2 minutes);
+ _pool.addQuoteToken(100_000 * 1e18, 1 + i, block.timestamp + 2 minutes);
vm.stopPrank();
_lenders.push(lender);
- unchecked {
- ++i;
- }
+ unchecked { ++i; }
}
}
@@ -69,9 +67,8 @@ contract ERC20PoolGasLoadTest is ERC20DSTestPlus {
vm.stopPrank();
_borrowers.push(borrower);
- unchecked {
- ++i;
- }
+
+ unchecked { ++i; }
}
}
@@ -105,9 +102,11 @@ contract ERC20PoolCommonActionsGasLoadTest is ERC20PoolGasLoadTest {
vm.assume(borrowerId_ <= LOANS_COUNT);
address borrower = _borrowers[borrowerId_];
+
skip(15 hours);
+
vm.prank(borrower);
- ERC20Pool(address(_pool)).repayDebt(borrower, 100 * 1e18, 0);
+ ERC20Pool(address(_pool)).repayDebt(borrower, 100 * 1e18, 0, borrower, MAX_FENWICK_INDEX);
assertEq(_noOfLoans(), LOANS_COUNT);
}
@@ -116,11 +115,14 @@ contract ERC20PoolCommonActionsGasLoadTest is ERC20PoolGasLoadTest {
assertEq(_noOfLoans(), LOANS_COUNT);
vm.assume(borrowerId_ <= LOANS_COUNT);
+
skip(15 hours);
+
address borrower = _borrowers[borrowerId_];
(uint256 debt, , ) = _poolUtils.borrowerInfo(address(_pool), borrower);
+
vm.prank(borrower);
- ERC20Pool(address(_pool)).repayDebt(borrower, debt, 0);
+ ERC20Pool(address(_pool)).repayDebt(borrower, debt, 0, borrower, MAX_FENWICK_INDEX);
assertEq(_noOfLoans(), LOANS_COUNT - 1);
}
@@ -129,18 +131,20 @@ contract ERC20PoolCommonActionsGasLoadTest is ERC20PoolGasLoadTest {
assertEq(_noOfLoans(), LOANS_COUNT);
vm.assume(borrowerId_ <= LOANS_COUNT);
+
skip(15 hours);
+
address borrower = _borrowers[borrowerId_];
+
vm.prank(borrower);
- _drawDebtNoLupCheck(
- {
- from: borrower,
- borrower: borrower,
- amountToBorrow: 1_000 * 1e18,
- limitIndex: 5000,
- collateralToPledge: 0
- }
- );
+
+ _drawDebtNoLupCheck({
+ from: borrower,
+ borrower: borrower,
+ amountToBorrow: 1_000 * 1e18,
+ limitIndex: 5000,
+ collateralToPledge: 0
+ });
assertEq(_noOfLoans(), LOANS_COUNT);
}
@@ -156,31 +160,33 @@ contract ERC20PoolCommonActionsGasLoadTest is ERC20PoolGasLoadTest {
_mintCollateralAndApproveTokens(newBorrower, 2_000 * 1e18);
vm.startPrank(newBorrower);
+
skip(15 hours);
- _drawDebtNoLupCheck(
- {
- from: newBorrower,
- borrower: newBorrower,
- amountToBorrow: 0,
- limitIndex: 0,
- collateralToPledge: 1_000 * 1e18
- }
- );
+
+ _drawDebtNoLupCheck({
+ from: newBorrower,
+ borrower: newBorrower,
+ amountToBorrow: 0,
+ limitIndex: 0,
+ collateralToPledge: 1_000 * 1e18
+ });
+
+
skip(15 hours);
- _drawDebtNoLupCheck(
- {
- from: newBorrower,
- borrower: newBorrower,
- amountToBorrow: 1_000 * 1e18,
- limitIndex: 5000,
- collateralToPledge: 0
- }
- );
+ _drawDebtNoLupCheck({
+ from: newBorrower,
+ borrower: newBorrower,
+ amountToBorrow: 1_000 * 1e18,
+ limitIndex: 5000,
+ collateralToPledge: 0
+ });
+
vm.stopPrank();
assertEq(_noOfLoans(), LOANS_COUNT + 1);
vm.revertTo(snapshot);
+
assertEq(_noOfLoans(), LOANS_COUNT);
}
@@ -193,10 +199,12 @@ contract ERC20PoolCommonActionsGasLoadTest is ERC20PoolGasLoadTest {
assertEq(_noOfLoans(), LOANS_COUNT);
address borrower = _borrowers[i];
+
vm.prank(borrower);
- ERC20Pool(address(_pool)).repayDebt(borrower, 100 * 1e18, 0);
+ ERC20Pool(address(_pool)).repayDebt(borrower, 100 * 1e18, 0, borrower, MAX_FENWICK_INDEX);
assertEq(_noOfLoans(), LOANS_COUNT);
+
vm.revertTo(snapshot);
}
@@ -213,10 +221,12 @@ contract ERC20PoolCommonActionsGasLoadTest is ERC20PoolGasLoadTest {
address borrower = _borrowers[i];
(uint256 debt, , ) = _poolUtils.borrowerInfo(address(_pool), borrower);
+
vm.prank(borrower);
- ERC20Pool(address(_pool)).repayDebt(borrower, debt, 0);
+ ERC20Pool(address(_pool)).repayDebt(borrower, debt, 0, borrower, MAX_FENWICK_INDEX);
assertEq(_noOfLoans(), LOANS_COUNT - 1);
+
vm.revertTo(snapshot);
}
@@ -228,20 +238,23 @@ contract ERC20PoolCommonActionsGasLoadTest is ERC20PoolGasLoadTest {
for (uint256 i; i < LOANS_COUNT; i++) {
uint256 snapshot = vm.snapshot();
+
skip(15 hours);
+
assertEq(_noOfLoans(), LOANS_COUNT);
address borrower = _borrowers[i];
- _drawDebtNoLupCheck(
- {
- from: borrower,
- borrower: borrower,
- amountToBorrow: 1_000 * 1e18,
- limitIndex: 5000,
- collateralToPledge: 0
- }
- );
+
+ _drawDebtNoLupCheck({
+ from: borrower,
+ borrower: borrower,
+ amountToBorrow: 1_000 * 1e18,
+ limitIndex: 5000,
+ collateralToPledge: 0
+ });
+
assertEq(_noOfLoans(), LOANS_COUNT);
+
vm.revertTo(snapshot);
}
@@ -255,14 +268,19 @@ contract ERC20PoolCommonActionsGasLoadTest is ERC20PoolGasLoadTest {
_mintQuoteAndApproveTokens(lender, 200_000 * 1e18);
vm.startPrank(lender);
+
skip(15 hours);
- _pool.addQuoteToken(10_000 * 1e18, index_);
+ _pool.addQuoteToken(10_000 * 1e18, index_, block.timestamp + 2 minutes);
+
skip(15 hours);
_pool.removeQuoteToken(5_000 * 1e18, index_);
+
skip(15 hours);
- _pool.moveQuoteToken(1_000 * 1e18, index_, index_ + 1);
+ _pool.moveQuoteToken(1_000 * 1e18, index_, index_ + 1, block.timestamp + 2 minutes);
+
skip(15 hours);
_pool.removeQuoteToken(type(uint256).max, index_);
+
vm.stopPrank();
}
@@ -272,16 +290,23 @@ contract ERC20PoolCommonActionsGasLoadTest is ERC20PoolGasLoadTest {
for (uint256 i = 1; i < LENDERS; i++) {
uint256 snapshot = vm.snapshot();
+
vm.startPrank(lender);
+
skip(15 hours);
- _pool.addQuoteToken(10_000 * 1e18, 7388 - i);
+ _pool.addQuoteToken(10_000 * 1e18, 7388 - i, block.timestamp + 2 minutes);
+
skip(15 hours);
- _pool.addQuoteToken(10_000 * 1e18, 1 + i);
+ _pool.addQuoteToken(10_000 * 1e18, 1 + i, block.timestamp + 2 minutes);
+
skip(15 hours);
_pool.removeQuoteToken(5_000 * 1e18, 7388 - i);
+
skip(15 hours);
_pool.removeQuoteToken(type(uint256).max, 1 + i);
+
vm.stopPrank();
+
vm.revertTo(snapshot);
}
}
@@ -291,14 +316,19 @@ contract ERC20PoolCommonActionsGasLoadTest is ERC20PoolGasLoadTest {
_mintQuoteAndApproveTokens(kicker, type(uint256).max); // mint enough to cover bonds
vm.warp(100_000 days);
+
vm.startPrank(kicker);
+
for (uint256 i; i < LOANS_COUNT; i ++) {
_pool.kick(_borrowers[i]);
}
+
skip(2 hours);
+
for (uint256 i; i < LOANS_COUNT - 1; i ++) {
ERC20Pool(address(_pool)).take(_borrowers[i], 100 * 1e18, kicker, new bytes(0));
}
+
vm.stopPrank();
}
@@ -307,14 +337,19 @@ contract ERC20PoolCommonActionsGasLoadTest is ERC20PoolGasLoadTest {
_mintQuoteAndApproveTokens(kicker, type(uint256).max); // mint enough to cover bonds
vm.warp(100_000 days);
+
vm.startPrank(kicker);
+
for (uint256 i; i < LOANS_COUNT; i ++) {
_pool.kick(_borrowers[LOANS_COUNT - 1 - i]);
}
+
skip(2 hours);
+
for (uint256 i; i < LOANS_COUNT - 1; i ++) {
ERC20Pool(address(_pool)).take(_borrowers[LOANS_COUNT - 1 - i], 100 * 1e18, kicker, new bytes(0));
}
+
vm.stopPrank();
}
@@ -323,15 +358,21 @@ contract ERC20PoolCommonActionsGasLoadTest is ERC20PoolGasLoadTest {
_mintQuoteAndApproveTokens(kicker, type(uint256).max); // mint enough to cover bonds
vm.startPrank(kicker);
- _pool.addQuoteToken(500_000_000_000_000 * 1e18, 3_000);
+
+ _pool.addQuoteToken(500_000_000_000_000 * 1e18, 3_000, block.timestamp + 2 minutes);
vm.warp(100_000 days);
+
_pool.kickWithDeposit(3_000); // worst case scenario, pool interest accrues
+
skip(80 hours);
+
_pool.settle(_borrowers[LOANS_COUNT - 1], 10);
+
// kick remaining loans with deposit to get average gas cost
for (uint256 i; i < LOANS_COUNT - 1; i ++) {
_pool.kickWithDeposit(3_000);
}
+
vm.stopPrank();
}
}
@@ -359,19 +400,28 @@ contract ERC20PoolGasArbTakeLoadTest is ERC20PoolGasLoadTest {
_mintQuoteAndApproveTokens(kicker, type(uint256).max); // mint enough to cover bonds
vm.warp(100_000 days);
+
vm.startPrank(kicker);
+
for (uint256 i; i < LOANS_COUNT; i ++) {
_pool.kick(_borrowers[i]);
}
+
// add quote tokens in bucket to arb
- _pool.addQuoteToken(100_000 * 1e18, 1_000);
+ _pool.addQuoteToken(100_000 * 1e18, 1_000, block.timestamp + 2 minutes);
+
vm.stopPrank();
assertEq(_noOfLoans(), 0); // assert all loans are kicked
+
skip(14 hours);
+
address taker = makeAddr("taker");
+
vm.startPrank(taker);
+
_pool.bucketTake(_borrowers[0], depositTake_, 1_000);
+
vm.stopPrank();
}
@@ -380,19 +430,28 @@ contract ERC20PoolGasArbTakeLoadTest is ERC20PoolGasLoadTest {
_mintQuoteAndApproveTokens(kicker, type(uint256).max); // mint enough to cover bonds
vm.warp(100_000 days);
+
vm.startPrank(kicker);
+
for (uint256 i; i < LOANS_COUNT; i ++) {
_pool.kick(_borrowers[LOANS_COUNT - 1 - i]);
}
+
// add quote tokens in bucket to arb
- _pool.addQuoteToken(100_000 * 1e18, 1_000);
+ _pool.addQuoteToken(100_000 * 1e18, 1_000, block.timestamp + 2 minutes);
+
vm.stopPrank();
assertEq(_noOfLoans(), 0); // assert all loans are kicked
+
skip(14 hours);
+
address taker = makeAddr("taker");
+
vm.startPrank(taker);
+
_pool.bucketTake(_borrowers[LOANS_COUNT - 1], depositTake_, 1_000);
+
vm.stopPrank();
}
@@ -410,7 +469,9 @@ contract ERC20PoolGasArbTakeLoadTest is ERC20PoolGasLoadTest {
_mintQuoteAndApproveTokens(lender, 200_000 * 1e18);
vm.startPrank(lender);
- _pool.addQuoteToken(200_000 * 1e18, 5000 - i);
+
+ _pool.addQuoteToken(200_000 * 1e18, 5000 - i, block.timestamp + 2 minutes);
+
vm.stopPrank();
_lenders.push(lender);
diff --git a/tests/forge/ERC20Pool/ERC20PoolInfoUtils.t.sol b/tests/forge/ERC20Pool/ERC20PoolInfoUtils.t.sol
index 6053f0262..3d2994639 100644
--- a/tests/forge/ERC20Pool/ERC20PoolInfoUtils.t.sol
+++ b/tests/forge/ERC20Pool/ERC20PoolInfoUtils.t.sol
@@ -34,51 +34,41 @@ contract ERC20PoolInfoUtilsTest is ERC20HelperContract {
_mintQuoteAndApproveTokens(_lender1, 200_000 * 1e18);
// lender deposits 10000 DAI in 5 buckets each
- _addLiquidity(
- {
- from: _lender,
- amount: 10_000 * 1e18,
- index: highest,
- lpAward: 10_000 * 1e27,
- newLup: MAX_PRICE
- }
- );
- _addLiquidity(
- {
- from: _lender,
- amount: 10_000 * 1e18,
- index: high,
- lpAward: 10_000 * 1e27,
- newLup: MAX_PRICE
- }
- );
- _addLiquidity(
- {
- from: _lender,
- amount: 10_000 * 1e18,
- index: med,
- lpAward: 10_000 * 1e27,
- newLup: MAX_PRICE
- }
- );
- _addLiquidity(
- {
- from: _lender,
- amount: 10_000 * 1e18,
- index: low,
- lpAward: 10_000 * 1e27,
- newLup: MAX_PRICE
- }
- );
- _addLiquidity(
- {
- from: _lender,
- amount: 10_000 * 1e18,
- index: lowest,
- lpAward: 10_000 * 1e27,
- newLup: MAX_PRICE
- }
- );
+ _addLiquidity({
+ from: _lender,
+ amount: 10_000 * 1e18,
+ index: highest,
+ lpAward: 10_000 * 1e18,
+ newLup: MAX_PRICE
+ });
+ _addLiquidity({
+ from: _lender,
+ amount: 10_000 * 1e18,
+ index: high,
+ lpAward: 10_000 * 1e18,
+ newLup: MAX_PRICE
+ });
+ _addLiquidity({
+ from: _lender,
+ amount: 10_000 * 1e18,
+ index: med,
+ lpAward: 10_000 * 1e18,
+ newLup: MAX_PRICE
+ });
+ _addLiquidity({
+ from: _lender,
+ amount: 10_000 * 1e18,
+ index: low,
+ lpAward: 10_000 * 1e18,
+ newLup: MAX_PRICE
+ });
+ _addLiquidity({
+ from: _lender,
+ amount: 10_000 * 1e18,
+ index: lowest,
+ lpAward: 10_000 * 1e18,
+ newLup: MAX_PRICE
+ });
_drawDebt({
from: _borrower,
@@ -114,12 +104,13 @@ contract ERC20PoolInfoUtilsTest is ERC20HelperContract {
uint256 scale,
uint256 exchangeRate
) = _poolUtils.bucketInfo(address(_pool), 5000);
+
assertEq(price, 0.014854015662334135 * 1e18);
assertEq(quoteTokens, 0);
assertEq(collateral, 0);
assertEq(bucketLPs, 0);
assertEq(scale, 1 * 1e18);
- assertEq(exchangeRate, 1 * 1e27);
+ assertEq(exchangeRate, 1 * 1e18);
(
price,
@@ -132,9 +123,9 @@ contract ERC20PoolInfoUtilsTest is ERC20HelperContract {
assertEq(price, 2_995.912459898389633881 * 1e18);
assertEq(quoteTokens, 10_000 * 1e18);
assertEq(collateral, 0);
- assertEq(bucketLPs, 10_000 * 1e27);
+ assertEq(bucketLPs, 10_000 * 1e18);
assertEq(scale, 1 * 1e18);
- assertEq(exchangeRate, 1 * 1e27);
+ assertEq(exchangeRate, 1 * 1e18);
}
function testPoolInfoUtilsLoansInfo() external {
@@ -161,6 +152,7 @@ contract ERC20PoolInfoUtilsTest is ERC20HelperContract {
uint256 lup,
uint256 lupIndex
) = _poolUtils.poolPricesInfo(address(_pool));
+
assertEq(hpb, 3_010.892022197881557845 * 1e18);
assertEq(hpbIndex, 2550);
assertEq(htp, 210.201923076923077020 * 1e18);
@@ -183,6 +175,7 @@ contract ERC20PoolInfoUtilsTest is ERC20HelperContract {
uint256 auctionPrice,
uint256 timeRemaining
) = _poolUtils.poolReservesInfo(address(_pool));
+
assertEq(reserves, 20.192307692307702000 * 1e18);
assertEq(claimableReserves, 0);
assertEq(claimableReservesRemaining, 0);
@@ -197,6 +190,7 @@ contract ERC20PoolInfoUtilsTest is ERC20HelperContract {
uint256 poolActualUtilization,
uint256 poolTargetUtilization
) = _poolUtils.poolUtilizationInfo(address(_pool));
+
assertEq(poolMinDebtAmount, 2_102.019230769230770200 * 1e18);
assertEq(poolCollateralization, 14.181637252165253251 * 1e18);
assertEq(poolActualUtilization, 0.420403846153846154 * 1e18);
@@ -220,46 +214,46 @@ contract ERC20PoolInfoUtilsTest is ERC20HelperContract {
assertEq(
_poolUtils.lpsToCollateral(
address(_pool),
- 100 * 1e27,
+ 100 * 1e18,
high
), 0
);
+
changePrank(_borrower2);
- ERC20Pool(address(_pool)).addCollateral(10 * 1e18, high);
+ ERC20Pool(address(_pool)).addCollateral(10 * 1e18, high, block.timestamp + 5 minutes);
assertEq(
_poolUtils.lpsToCollateral(
address(_pool),
- 5 * 1e27,
+ 5 * 1e18,
high
), 1668940620571264
);
assertEq(
_poolUtils.lpsToCollateral(
address(_pool),
- 20 * 1e27,
+ 20 * 1e18,
high
), 6675762482285055
);
-
assertEq(
_poolUtils.lpsToQuoteTokens(
address(_pool),
- 100 * 1e27,
+ 100 * 1e18,
high
), 100000000000000000000
);
assertEq(
_poolUtils.lpsToQuoteTokens(
address(_pool),
- 5 * 1e27,
+ 5 * 1e18,
high
), 5000000000000000000
);
assertEq(
_poolUtils.lpsToQuoteTokens(
address(_pool),
- 20 * 1e27,
+ 20 * 1e18,
high
), 20000000000000000000
);
diff --git a/tests/forge/ERC20Pool/ERC20PoolInterestRateAndEMAs.t.sol b/tests/forge/ERC20Pool/ERC20PoolInterestRateAndEMAs.t.sol
index 2fc2e2b44..0e4e3e4ff 100644
--- a/tests/forge/ERC20Pool/ERC20PoolInterestRateAndEMAs.t.sol
+++ b/tests/forge/ERC20Pool/ERC20PoolInterestRateAndEMAs.t.sol
@@ -14,58 +14,54 @@ contract ERC20PoolInterestRateTestAndEMAs is ERC20HelperContract {
address internal _borrower;
address internal _borrower2;
+ address internal _borrower3;
address internal _lender;
address internal _lender1;
+ address internal _lender2;
function setUp() external {
_borrower = makeAddr("borrower");
_borrower2 = makeAddr("borrower2");
+ _borrower3 = makeAddr("borrower3");
_lender = makeAddr("lender");
_lender1 = makeAddr("_lender1");
+ _lender2 = makeAddr("_lender2");
_mintCollateralAndApproveTokens(_borrower, 10_000 * 1e18);
_mintCollateralAndApproveTokens(_borrower2, 200 * 1e18);
+ _mintCollateralAndApproveTokens(_borrower3, 1_000_000_000 * 1e18);
_mintQuoteAndApproveTokens(_lender, 200_000 * 1e18);
_mintQuoteAndApproveTokens(_lender1, 200_000 * 1e18);
+ _mintQuoteAndApproveTokens(_lender2, 100_000_000_000_000_000 * 1e18);
}
function testPoolInterestRateIncreaseDecrease() external tearDown {
- _addInitialLiquidity(
- {
- from: _lender,
- amount: 10_000 * 1e18,
- index: 2550
- }
- );
- _addInitialLiquidity(
- {
- from: _lender,
- amount: 20_000 * 1e18,
- index: 2551
- }
- );
- _addInitialLiquidity(
- {
- from: _lender,
- amount: 20_000 * 1e18,
- index: 2552
- }
- );
- _addInitialLiquidity(
- {
- from: _lender,
- amount: 50_000 * 1e18,
- index: 3900
- }
- );
- _addInitialLiquidity(
- {
- from: _lender,
- amount: 10_000 * 1e18,
- index: 4200
- }
- );
+ _addInitialLiquidity({
+ from: _lender,
+ amount: 10_000 * 1e18,
+ index: 2550
+ });
+ _addInitialLiquidity({
+ from: _lender,
+ amount: 20_000 * 1e18,
+ index: 2551
+ });
+ _addInitialLiquidity({
+ from: _lender,
+ amount: 20_000 * 1e18,
+ index: 2552
+ });
+ _addInitialLiquidity({
+ from: _lender,
+ amount: 50_000 * 1e18,
+ index: 3900
+ });
+ _addInitialLiquidity({
+ from: _lender,
+ amount: 10_000 * 1e18,
+ index: 4200
+ });
skip(10 days);
@@ -91,15 +87,13 @@ contract ERC20PoolInterestRateTestAndEMAs is ERC20HelperContract {
vm.expectEmit(true, true, false, true);
emit UpdateInterestRate(0.05 * 1e18, 0.055 * 1e18);
- _drawDebtNoLupCheck(
- {
- from: _borrower,
- borrower: _borrower,
- amountToBorrow: 46_000 * 1e18,
- limitIndex: 4_300,
- collateralToPledge: 100 * 1e18
- }
- );
+ _drawDebtNoLupCheck({
+ from: _borrower,
+ borrower: _borrower,
+ amountToBorrow: 46_000 * 1e18,
+ limitIndex: 4_300,
+ collateralToPledge: 100 * 1e18
+ });
_assertPool(
PoolParams({
@@ -118,12 +112,10 @@ contract ERC20PoolInterestRateTestAndEMAs is ERC20HelperContract {
interestRateUpdate: _startTime + 10 days
})
);
- _assertEMAs(
- {
- debtEma: 4_340.881358710158802477 * 1e18,
- lupColEma: 28_103.845662221475161347 * 1e18
- }
- );
+ _assertEMAs({
+ debtEma: 4_340.881358710158802477 * 1e18,
+ lupColEma: 28_103.845662221475161347 * 1e18
+ });
skip(14 hours);
@@ -154,16 +146,15 @@ contract ERC20PoolInterestRateTestAndEMAs is ERC20HelperContract {
interestRateUpdate: _startTime + 10 days + 14 hours
})
);
- _assertEMAs(
- {
- debtEma: 8_279.448467499588505755 * 1e18,
- lupColEma: 53_558.163735316008374982 * 1e18
- }
- );
+ _assertEMAs({
+ debtEma: 8_279.448467499588505755 * 1e18,
+ lupColEma: 53_558.163735316008374982 * 1e18
+ });
vm.revertTo(snapshot);
// repay entire loan
deal(address(_quote), _borrower, _quote.balanceOf(_borrower) + 200 * 1e18);
+
_repayDebt({
from: _borrower,
borrower: _borrower,
@@ -190,40 +181,33 @@ contract ERC20PoolInterestRateTestAndEMAs is ERC20HelperContract {
interestRateUpdate: _startTime + 10 days + 14 hours
})
);
- _assertEMAs(
- {
- debtEma: 4_340.881358710158802477 * 1e18,
- lupColEma: 28_103.845662221475161347 * 1e18
- }
- );
+ _assertEMAs({
+ debtEma: 4_340.881358710158802477 * 1e18,
+ lupColEma: 28_103.845662221475161347 * 1e18
+ });
}
function testOverutilizedPoolInterestRateIncrease() external tearDown {
// lender deposits 1000
- _addInitialLiquidity(
- {
- from: _lender,
- amount: 1_000 * 1e18,
- index: 3232
- }
- );
+ _addInitialLiquidity({
+ from: _lender,
+ amount: 1_000 * 1e18,
+ index: 3232
+ });
// borrower draws 9100
- _pledgeCollateral(
- {
- from: _borrower,
- borrower: _borrower,
- amount: 1_500 * 1e18
- }
- );
- _borrow(
- {
- from: _borrower,
- amount: 995 * 1e18,
- indexLimit: 3300,
- newLup: 100.332368143282009890 * 1e18
- }
- );
+ _pledgeCollateral({
+ from: _borrower,
+ borrower: _borrower,
+ amount: 1_500 * 1e18
+ });
+ _borrow({
+ from: _borrower,
+ amount: 995 * 1e18,
+ indexLimit: 3300,
+ newLup: 100.332368143282009890 * 1e18
+ });
+
_assertPool(
PoolParams({
htp: 0.663971153846153846 * 1e18,
@@ -244,15 +228,15 @@ contract ERC20PoolInterestRateTestAndEMAs is ERC20HelperContract {
// force an interest rate update
skip(13 hours);
- _addLiquidity(
- {
- from: _lender,
- amount: 0,
- index: 3232,
- lpAward: 0,
- newLup: 100.332368143282009890 * 1e18
- }
- );
+
+ _addLiquidity({
+ from: _lender,
+ amount: 0,
+ index: 3232,
+ lpAward: 0,
+ newLup: 100.332368143282009890 * 1e18
+ });
+
_assertPool(
PoolParams({
htp: 0.664069695689831259 * 1e18,
@@ -275,30 +259,26 @@ contract ERC20PoolInterestRateTestAndEMAs is ERC20HelperContract {
function testPoolInterestRateDecrease() external tearDown {
// lender makes an initial deposit
skip(1 hours);
- _addInitialLiquidity(
- {
- from: _lender,
- amount: 10_000 * 1e18,
- index: 2873
- }
- );
+
+ _addInitialLiquidity({
+ from: _lender,
+ amount: 10_000 * 1e18,
+ index: 2873
+ });
+
// borrower draws debt
skip(2 hours);
- _pledgeCollateral(
- {
- from: _borrower,
- borrower: _borrower,
- amount: 10 * 1e18
- }
- );
- _borrow(
- {
- from: _borrower,
- amount: 5_000 * 1e18,
- indexLimit: 3000,
- newLup: 601.252968524772188572 * 1e18
- }
- );
+ _pledgeCollateral({
+ from: _borrower,
+ borrower: _borrower,
+ amount: 10 * 1e18
+ });
+ _borrow({
+ from: _borrower,
+ amount: 5_000 * 1e18,
+ indexLimit: 3000,
+ newLup: 601.252968524772188572 * 1e18
+ });
_assertPool(
PoolParams({
@@ -323,13 +303,11 @@ contract ERC20PoolInterestRateTestAndEMAs is ERC20HelperContract {
vm.expectEmit(true, true, false, true);
emit UpdateInterestRate(0.05 * 1e18, 0.045 * 1e18);
- _addLiquidityNoEventCheck(
- {
- from: _lender1,
- amount: 1_000 * 1e18,
- index: 2873
- }
- );
+ _addLiquidityNoEventCheck({
+ from: _lender1,
+ amount: 1_000 * 1e18,
+ index: 2873
+ });
_assertPool(
PoolParams({
@@ -351,25 +329,21 @@ contract ERC20PoolInterestRateTestAndEMAs is ERC20HelperContract {
}
function testMinInterestRate() external tearDown {
- _addInitialLiquidity(
- {
- from: _lender,
- amount: 10_000 * 1e18,
- index: _i1505_26
- }
- );
+ _addInitialLiquidity({
+ from: _lender,
+ amount: 10_000 * 1e18,
+ index: _i1505_26
+ });
// pledge a tiny amount of collateral and draw a tiny amount of debt
- _drawDebt(
- {
- from: _borrower,
- borrower: _borrower,
- amountToBorrow: 0.00001 * 1e18,
- limitIndex: _i1505_26,
- collateralToPledge: 0.00001 * 1e18,
- newLup: _p1505_26
- }
- );
+ _drawDebt({
+ from: _borrower,
+ borrower: _borrower,
+ amountToBorrow: 0.00001 * 1e18,
+ limitIndex: _i1505_26,
+ collateralToPledge: 0.00001 * 1e18,
+ newLup: _p1505_26
+ });
// confirm interest rate starts out at 5%
_assertPool(
@@ -394,17 +368,15 @@ contract ERC20PoolInterestRateTestAndEMAs is ERC20HelperContract {
while (i < 77) {
// trigger an interest accumulation
skip(12 hours);
- _borrowZeroAmount(
- {
- from: _borrower,
- amount: 0,
- indexLimit: _i1505_26,
- newLup: _p1505_26
- }
- );
- unchecked {
- ++i;
- }
+
+ _borrowZeroAmount({
+ from: _borrower,
+ amount: 0,
+ indexLimit: _i1505_26,
+ newLup: _p1505_26
+ });
+
+ unchecked { ++i; }
}
// show the rate bottoms out at 10 bps
@@ -428,25 +400,21 @@ contract ERC20PoolInterestRateTestAndEMAs is ERC20HelperContract {
}
function testMaxInterestRate() external tearDown {
- _addInitialLiquidity(
- {
- from: _lender,
- amount: 10_000 * 1e18,
- index: _i1505_26
- }
- );
+ _addInitialLiquidity({
+ from: _lender,
+ amount: 10_000 * 1e18,
+ index: _i1505_26
+ });
// pledge a lot of collateral, but draw a tiny amount of debt
- _drawDebt(
- {
- from: _borrower,
- borrower: _borrower,
- amountToBorrow: 0.00001 * 1e18,
- limitIndex: _i1505_26,
- collateralToPledge: 10_000 * 1e18,
- newLup: _p1505_26
- }
- );
+ _drawDebt({
+ from: _borrower,
+ borrower: _borrower,
+ amountToBorrow: 0.00001 * 1e18,
+ limitIndex: _i1505_26,
+ collateralToPledge: 10_000 * 1e18,
+ newLup: _p1505_26
+ });
// confirm interest rate starts out at 5%
_assertPool(
@@ -471,17 +439,15 @@ contract ERC20PoolInterestRateTestAndEMAs is ERC20HelperContract {
while (i < 196) {
// trigger an interest accumulation
skip(12 hours);
- _borrowZeroAmount(
- {
- from: _borrower,
- amount: 0,
- indexLimit: _i1505_26,
- newLup: _p1505_26
- }
- );
- unchecked {
- ++i;
- }
+
+ _borrowZeroAmount({
+ from: _borrower,
+ amount: 0,
+ indexLimit: _i1505_26,
+ newLup: _p1505_26
+ });
+
+ unchecked { ++i; }
}
// show the rate maxed out at 50000%
@@ -506,46 +472,36 @@ contract ERC20PoolInterestRateTestAndEMAs is ERC20HelperContract {
function testPendingInflator() external tearDown {
// add liquidity
- _addInitialLiquidity(
- {
- from: _lender,
- amount: 10_000 * 1e18,
- index: 2550
- }
- );
- _addInitialLiquidity(
- {
- from: _lender,
- amount: 10_000 * 1e18,
- index: 2552
- }
- );
- _addInitialLiquidity(
- {
- from: _lender,
- amount: 10_000 * 1e18,
- index: 4200
- }
- );
+ _addInitialLiquidity({
+ from: _lender,
+ amount: 10_000 * 1e18,
+ index: 2550
+ });
+ _addInitialLiquidity({
+ from: _lender,
+ amount: 10_000 * 1e18,
+ index: 2552
+ });
+ _addInitialLiquidity({
+ from: _lender,
+ amount: 10_000 * 1e18,
+ index: 4200
+ });
skip(3600);
// draw debt
- _pledgeCollateral(
- {
- from: _borrower,
- borrower: _borrower,
- amount: 50 * 1e18
- }
- );
- _borrow(
- {
- from: _borrower,
- amount: 15_000 * 1e18,
- indexLimit: 4_300,
- newLup: 2_981.007422784467321543 * 1e18
- }
- );
+ _pledgeCollateral({
+ from: _borrower,
+ borrower: _borrower,
+ amount: 50 * 1e18
+ });
+ _borrow({
+ from: _borrower,
+ amount: 15_000 * 1e18,
+ indexLimit: 4_300,
+ newLup: 2_981.007422784467321543 * 1e18
+ });
_assertPool(
PoolParams({
@@ -590,20 +546,16 @@ contract ERC20PoolInterestRateTestAndEMAs is ERC20HelperContract {
function testPoolEMAAndTargetUtilizationUpdate() external tearDown {
// add initial quote to the pool
- _addInitialLiquidity(
- {
- from: _lender,
- amount: 10_000 * 1e18,
- index: 3_010
- }
- );
- _addInitialLiquidity(
- {
- from: _lender,
- amount: 10_000 * 1e18,
- index: 2_995
- }
- );
+ _addInitialLiquidity({
+ from: _lender,
+ amount: 10_000 * 1e18,
+ index: 3_010
+ });
+ _addInitialLiquidity({
+ from: _lender,
+ amount: 10_000 * 1e18,
+ index: 2_995
+ });
_assertPool(
PoolParams({
@@ -622,29 +574,23 @@ contract ERC20PoolInterestRateTestAndEMAs is ERC20HelperContract {
interestRateUpdate: _startTime
})
);
- _assertEMAs(
- {
- debtEma: 0,
- lupColEma: 0
- }
- );
+ _assertEMAs({
+ debtEma: 0,
+ lupColEma: 0
+ });
// borrower 1 borrows 500 quote from the pool
- _pledgeCollateral(
- {
- from: _borrower,
- borrower: _borrower,
- amount: 50 * 1e18
- }
- );
- _borrow(
- {
- from: _borrower,
- amount: 500 * 1e18,
- indexLimit: 3_010,
- newLup: 327.188250324085203338 * 1e18
- }
- );
+ _pledgeCollateral({
+ from: _borrower,
+ borrower: _borrower,
+ amount: 50 * 1e18
+ });
+ _borrow({
+ from: _borrower,
+ amount: 500 * 1e18,
+ indexLimit: 3_010,
+ newLup: 327.188250324085203338 * 1e18
+ });
_assertPool(
PoolParams({
@@ -663,28 +609,22 @@ contract ERC20PoolInterestRateTestAndEMAs is ERC20HelperContract {
interestRateUpdate: _startTime
})
);
- _assertEMAs(
- {
- debtEma: 0,
- lupColEma: 0
- }
- );
+ _assertEMAs({
+ debtEma: 0,
+ lupColEma: 0
+ });
- _pledgeCollateral(
- {
- from: _borrower2,
- borrower: _borrower2,
- amount: 50 * 1e18
- }
- );
- _borrow(
- {
- from: _borrower2,
- amount: 500 * 1e18,
- indexLimit: 3_010,
- newLup: 327.188250324085203338 * 1e18
- }
- );
+ _pledgeCollateral({
+ from: _borrower2,
+ borrower: _borrower2,
+ amount: 50 * 1e18
+ });
+ _borrow({
+ from: _borrower2,
+ amount: 500 * 1e18,
+ indexLimit: 3_010,
+ newLup: 327.188250324085203338 * 1e18
+ });
_assertPool(
PoolParams({
@@ -703,24 +643,20 @@ contract ERC20PoolInterestRateTestAndEMAs is ERC20HelperContract {
interestRateUpdate: _startTime
})
);
- _assertEMAs(
- {
- debtEma: 0,
- lupColEma: 0
- }
- );
+ _assertEMAs({
+ debtEma: 0,
+ lupColEma: 0
+ });
skip(10 days);
// borrower 1 borrows 500 quote from the pool
- _borrow(
- {
- from: _borrower,
- amount: 10 * 1e18,
- indexLimit: 3_010,
- newLup: 327.188250324085203338 * 1e18
- }
- );
+ _borrow({
+ from: _borrower,
+ amount: 10 * 1e18,
+ indexLimit: 3_010,
+ newLup: 327.188250324085203338 * 1e18
+ });
_assertPool(
PoolParams({
@@ -739,11 +675,63 @@ contract ERC20PoolInterestRateTestAndEMAs is ERC20HelperContract {
interestRateUpdate: _startTime
})
);
- _assertEMAs(
- {
- debtEma: 95.440014344854493304 * 1e18,
- lupColEma: 954.400143448544933043 * 1e18
- }
+ _assertEMAs({
+ debtEma: 95.440014344854493304 * 1e18,
+ lupColEma: 954.400143448544933043 * 1e18
+ });
+ }
+
+ function testAccruePoolInterestHtpGtMaxPrice() external tearDown {
+ _addLiquidityNoEventCheck({
+ from: _lender2,
+ amount: 100_000_000_000_000_000 * 1e18,
+ index: 1
+ });
+
+ _drawDebtNoLupCheck({
+ from: _borrower3,
+ borrower: _borrower3,
+ amountToBorrow: 90_000_000_000_000_000 * 1e18,
+ limitIndex: 5000,
+ collateralToPledge: 90_100_000 * 1e18
+ });
+
+ skip(100 days);
+
+ assertGt(MAX_PRICE, _htp());
+
+ uint256 expectedPoolDebt = 91329091841208027.611736396814389869 * 1e18;
+
+ _assertPool(
+ PoolParams({
+ htp: 999850593.357807564705882353 * 1e18,
+ lup: 999969141.897027226245329498 * 1e18,
+ poolSize: 100_000_000_000_000_000 * 1e18,
+ pledgedCollateral: 90_100_000 * 1e18,
+ encumberedCollateral: 91_331_910.170696775095411340 * 1e18,
+ poolDebt: 91329091841208027.611736396814389869 * 1e18,
+ actualUtilization: 0,
+ targetUtilization: 1 * 1e18,
+ minDebtAmount: 9132909184120802.761173639681438987 * 1e18,
+ loans: 1,
+ maxBorrower: address(_borrower3),
+ interestRate: 0.05 * 1e18,
+ interestRateUpdate: _startTime
+ })
);
+
+ (uint256 poolDebt,,) = _pool.debtInfo();
+ assertEq(poolDebt, expectedPoolDebt);
+
+ // force accrue interest
+ _addLiquidityNoEventCheck({
+ from: _lender2,
+ amount: 0,
+ index: 1
+ });
+
+ // check that no interest earned if HTP is over the highest price bucket
+ (poolDebt,,) = _pool.debtInfo();
+ assertEq(poolDebt, expectedPoolDebt);
}
}
diff --git a/tests/forge/ERC20Pool/ERC20PoolLiquidationsArbTake.t.sol b/tests/forge/ERC20Pool/ERC20PoolLiquidationsArbTake.t.sol
index a9efb84f3..c7b3ab308 100644
--- a/tests/forge/ERC20Pool/ERC20PoolLiquidationsArbTake.t.sol
+++ b/tests/forge/ERC20Pool/ERC20PoolLiquidationsArbTake.t.sol
@@ -28,75 +28,57 @@ contract ERC20PoolLiquidationsArbTakeTest is ERC20HelperContract {
_mintCollateralAndApproveTokens(_lender1, 4 * 1e18);
// Lender adds Quote token accross 5 prices
- _addInitialLiquidity(
- {
- from: _lender,
- amount: 2_000 * 1e18,
- index: _i9_91
- }
- );
- _addInitialLiquidity(
- {
- from: _lender,
- amount: 5_000 * 1e18,
- index: _i9_81
- }
- );
- _addInitialLiquidity(
- {
- from: _lender,
- amount: 11_000 * 1e18,
- index: _i9_72
- }
- );
- _addInitialLiquidity(
- {
- from: _lender,
- amount: 25_000 * 1e18,
- index: _i9_62
- }
- );
- _addInitialLiquidity(
- {
- from: _lender,
- amount: 30_000 * 1e18,
- index: _i9_52
- }
- );
-
- // first borrower adds collateral token and borrows
- _pledgeCollateral(
- {
- from: _borrower,
- borrower: _borrower,
- amount: 2 * 1e18
- }
- );
- _borrow(
- {
- from: _borrower,
- amount: 19.25 * 1e18,
- indexLimit: _i9_91,
- newLup: 9.917184843435912074 * 1e18
- }
- );
-
- // second borrower adds collateral token and borrows
- _pledgeCollateral(
- {
- from: _borrower2,
- borrower: _borrower2,
- amount: 1_000 * 1e18
- }
- );
- _borrow(
- {
- from: _borrower2,
- amount: 7_980 * 1e18,
- indexLimit: _i9_72,
- newLup: 9.721295865031779605 * 1e18
- }
- );
+ _addInitialLiquidity({
+ from: _lender,
+ amount: 2_000 * 1e18,
+ index: _i9_91
+ });
+ _addInitialLiquidity({
+ from: _lender,
+ amount: 5_000 * 1e18,
+ index: _i9_81
+ });
+ _addInitialLiquidity({
+ from: _lender,
+ amount: 11_000 * 1e18,
+ index: _i9_72
+ });
+ _addInitialLiquidity({
+ from: _lender,
+ amount: 25_000 * 1e18,
+ index: _i9_62
+ });
+ _addInitialLiquidity({
+ from: _lender,
+ amount: 30_000 * 1e18,
+ index: _i9_52
+ });
+
+ // first borrower pledge collateral and borrows
+ _pledgeCollateral({
+ from: _borrower,
+ borrower: _borrower,
+ amount: 2 * 1e18
+ });
+ _borrow({
+ from: _borrower,
+ amount: 19.25 * 1e18,
+ indexLimit: _i9_91,
+ newLup: 9.917184843435912074 * 1e18
+ });
+
+ // second borrower adds collateral and borrows
+ _pledgeCollateral({
+ from: _borrower2,
+ borrower: _borrower2,
+ amount: 1_000 * 1e18
+ });
+ _borrow({
+ from: _borrower2,
+ amount: 7_980 * 1e18,
+ indexLimit: _i9_72,
+ newLup: 9.721295865031779605 * 1e18
+ });
/*****************************/
/*** Assert pre-kick state ***/
@@ -119,43 +101,37 @@ contract ERC20PoolLiquidationsArbTakeTest is ERC20HelperContract {
interestRateUpdate: _startTime
})
);
- _assertBorrower(
- {
- borrower: _borrower,
- borrowerDebt: 19.268509615384615394 * 1e18,
- borrowerCollateral: 2 * 1e18,
- borrowert0Np: 10.115967548076923081 * 1e18,
- borrowerCollateralization: 1.009034539679184679 * 1e18
- }
- );
- _assertBorrower(
- {
- borrower: _borrower2,
- borrowerDebt: 7_987.673076923076926760 * 1e18,
- borrowerCollateral: 1_000 * 1e18,
- borrowert0Np: 8.471136974495192174 * 1e18,
- borrowerCollateralization: 1.217037273735858713 * 1e18
- }
- );
- _assertReserveAuction(
- {
- reserves: 7.691586538461542154 * 1e18,
- claimableReserves : 0,
- claimableReservesRemaining: 0,
- auctionPrice: 0,
- timeRemaining: 0
- }
- );
+ _assertBorrower({
+ borrower: _borrower,
+ borrowerDebt: 19.268509615384615394 * 1e18,
+ borrowerCollateral: 2 * 1e18,
+ borrowert0Np: 10.115967548076923081 * 1e18,
+ borrowerCollateralization: 1.009034539679184679 * 1e18
+ });
+ _assertBorrower({
+ borrower: _borrower2,
+ borrowerDebt: 7_987.673076923076926760 * 1e18,
+ borrowerCollateral: 1_000 * 1e18,
+ borrowert0Np: 8.471136974495192174 * 1e18,
+ borrowerCollateralization: 1.217037273735858713 * 1e18
+ });
+
+ _assertReserveAuction({
+ reserves: 7.691586538461542154 * 1e18,
+ claimableReserves : 0,
+ claimableReservesRemaining: 0,
+ auctionPrice: 0,
+ timeRemaining: 0
+ });
+
assertEq(_quote.balanceOf(_lender), 47_000 * 1e18);
// should revert if there's no auction started
- _assertArbTakeNoAuctionRevert(
- {
- from: _lender,
- borrower: _borrower,
- index: _i9_91
- }
- );
+ _assertArbTakeNoAuctionRevert({
+ from: _lender,
+ borrower: _borrower,
+ index: _i9_91
+ });
// Skip to make borrower undercollateralized
skip(100 days);
@@ -176,27 +152,22 @@ contract ERC20PoolLiquidationsArbTakeTest is ERC20HelperContract {
neutralPrice: 0
})
);
-
- _assertBorrower(
- {
- borrower: _borrower,
- borrowerDebt: 19.534277977147272573 * 1e18,
- borrowerCollateral: 2 * 1e18,
- borrowert0Np: 10.115967548076923081 * 1e18,
- borrowerCollateralization: 0.995306391810796636 * 1e18
- }
- );
+ _assertBorrower({
+ borrower: _borrower,
+ borrowerDebt: 19.534277977147272573 * 1e18,
+ borrowerCollateral: 2 * 1e18,
+ borrowert0Np: 10.115967548076923081 * 1e18,
+ borrowerCollateralization: 0.995306391810796636 * 1e18
+ });
- _kick(
- {
- from: _lender,
- borrower: _borrower,
- debt: 19.778456451861613480 * 1e18,
- collateral: 2 * 1e18,
- bond: 0.195342779771472726 * 1e18,
- transferAmount: 0.195342779771472726 * 1e18
- }
- );
+ _kick({
+ from: _lender,
+ borrower: _borrower,
+ debt: 19.778456451861613480 * 1e18,
+ collateral: 2 * 1e18,
+ bond: 0.195342779771472726 * 1e18,
+ transferAmount: 0.195342779771472726 * 1e18
+ });
_assertAuction(
AuctionParams({
@@ -214,196 +185,163 @@ contract ERC20PoolLiquidationsArbTakeTest is ERC20HelperContract {
neutralPrice: 10.255495938002318100 * 1e18
})
);
-
- _assertKicker(
- {
- kicker: _lender,
- claimable: 0,
- locked: 0.195342779771472726 * 1e18
- }
- );
+ _assertKicker({
+ kicker: _lender,
+ claimable: 0,
+ locked: 0.195342779771472726 * 1e18
+ });
}
function testArbTakeCollateralRestrict() external tearDown {
skip(6.5 hours);
- _assertLenderLpBalance(
- {
- lender: _taker,
- index: _i9_91,
- lpBalance: 0,
- depositTime: 0
- }
- );
- _assertLenderLpBalance(
- {
- lender: _lender,
- index: _i9_91,
- lpBalance: 2_000 * 1e27,
- depositTime: _startTime
- }
- );
- _assertBucket(
- {
- index: _i9_91,
- lpBalance: 2_000 * 1e27,
- collateral: 0,
- deposit: 2_027.000651340490292000 * 1e18,
- exchangeRate: 1.013500325670245146000000000 * 1e27
- }
- );
- _assertBorrower(
- {
- borrower: _borrower,
- borrowerDebt: 19.779116873676490456 * 1e18,
- borrowerCollateral: 2 * 1e18,
- borrowert0Np: 10.115967548076923081 * 1e18,
- borrowerCollateralization: 0.982985835729561629 * 1e18
- }
- );
+ _assertLenderLpBalance({
+ lender: _taker,
+ index: _i9_91,
+ lpBalance: 0,
+ depositTime: 0
+ });
+ _assertLenderLpBalance({
+ lender: _lender,
+ index: _i9_91,
+ lpBalance: 2_000 * 1e18,
+ depositTime: _startTime
+ });
+ _assertBucket({
+ index: _i9_91,
+ lpBalance: 2_000 * 1e18,
+ collateral: 0,
+ deposit: 2_027.000651340490292000 * 1e18,
+ exchangeRate: 1.013500325670245146 * 1e18
+ });
+ _assertBorrower({
+ borrower: _borrower,
+ borrowerDebt: 19.779116873676490456 * 1e18,
+ borrowerCollateral: 2 * 1e18,
+ borrowert0Np: 10.115967548076923081 * 1e18,
+ borrowerCollateralization: 0.982985835729561629 * 1e18
+ });
// add liquidity to accrue interest and update reserves before arb take
- _addLiquidity(
- {
- from: _lender1,
- amount: 1 * 1e18,
- index: _i9_52,
- lpAward: 0.999996826562080000190961519 * 1e27,
- newLup: 9.721295865031779605 * 1e18
- }
- );
- _assertBucket(
- {
- index: _i9_91,
- lpBalance: 2_000 * 1e27,
- collateral: 0,
- deposit: 2_027.007083921634518000 * 1e18,
- exchangeRate: 1.013503541960817259000000000 * 1e27
- }
- );
- _assertReserveAuction(
- {
- reserves: 23.911413759224212224 * 1e18,
- claimableReserves : 0,
- claimableReservesRemaining: 0,
- auctionPrice: 0,
- timeRemaining: 0
- }
- );
+ _addLiquidity({
+ from: _lender1,
+ amount: 1 * 1e18,
+ index: _i9_52,
+ lpAward: 0.99999682656208 * 1e18,
+ newLup: 9.721295865031779605 * 1e18
+ });
+
+ _assertBucket({
+ index: _i9_91,
+ lpBalance: 2_000 * 1e18,
+ collateral: 0,
+ deposit: 2_027.007083921634518000 * 1e18,
+ exchangeRate: 1.013503541960817259 * 1e18
+ });
+ _assertReserveAuction({
+ reserves: 23.911413759224212224 * 1e18,
+ claimableReserves : 0,
+ claimableReservesRemaining: 0,
+ auctionPrice: 0,
+ timeRemaining: 0
+ });
_assertAuction(
- AuctionParams({
- borrower: _borrower,
- active: true,
- kicker: _lender,
- bondSize: 0.195342779771472726 * 1e18, // should be the same after arb take, kicker will be rewarded with LPs
- bondFactor: 0.01 * 1e18,
- kickTime: block.timestamp - 6.5 hours,
- kickMomp: 9.818751856078723036 * 1e18,
- totalBondEscrowed: 0.195342779771472726 * 1e18,
- auctionPrice: 7.251730722192532064 * 1e18,
- debtInAuction: 19.779116873676490456 * 1e18,
- thresholdPrice: 9.889558436838245228 * 1e18,
- neutralPrice: 10.255495938002318100 * 1e18
- })
- );
-
- _assertBorrower(
- {
- borrower: _borrower,
- borrowerDebt: 19.779116873676490456 * 1e18,
- borrowerCollateral: 2 * 1e18,
- borrowert0Np: 10.115967548076923081 * 1e18,
- borrowerCollateralization: 0.982985835729561629 * 1e18
- }
+ AuctionParams({
+ borrower: _borrower,
+ active: true,
+ kicker: _lender,
+ bondSize: 0.195342779771472726 * 1e18, // should be the same after arb take, kicker will be rewarded with LPs
+ bondFactor: 0.01 * 1e18,
+ kickTime: block.timestamp - 6.5 hours,
+ kickMomp: 9.818751856078723036 * 1e18,
+ totalBondEscrowed: 0.195342779771472726 * 1e18,
+ auctionPrice: 7.251730722192532064 * 1e18,
+ debtInAuction: 19.779116873676490456 * 1e18,
+ thresholdPrice: 9.889558436838245228 * 1e18,
+ neutralPrice: 10.255495938002318100 * 1e18
+ })
);
+ _assertBorrower({
+ borrower: _borrower,
+ borrowerDebt: 19.779116873676490456 * 1e18,
+ borrowerCollateral: 2 * 1e18,
+ borrowert0Np: 10.115967548076923081 * 1e18,
+ borrowerCollateralization: 0.982985835729561629 * 1e18
+ });
// Amount is restricted by the collateral in the loan
- _arbTake(
- {
- from: _taker,
- borrower: _borrower,
- kicker: _lender,
- index: _i9_91,
- collateralArbed: 2 * 1e18,
- quoteTokenAmount: 14.503461444385064128 * 1e18,
- bondChange: 0.145034614443850641 * 1e18,
- isReward: true,
- lpAwardTaker: 5.259881215780552826000000000 * 1e27,
- lpAwardKicker: 0.143102227509983165000000000 * 1e27
- }
- );
-
- _assertLenderLpBalance(
- {
- lender: _taker,
- index: _i9_91,
- lpBalance: 5.259881215780552826000000000 * 1e27,
- depositTime: _startTime + 100 days + 6.5 hours
- }
- );
- _assertLenderLpBalance(
- {
- lender: _lender,
- index: _i9_91,
- lpBalance: 2_000.143102227509983165000000000 * 1e27, // rewarded with LPs in bucket
- depositTime: _startTime + 100 days + 6.5 hours
- }
- );
- _assertBucket(
- {
- index: _i9_91,
- lpBalance: 2_005.402983443290535991000000000 * 1e27,
- collateral: 2 * 1e18,
- deposit: 2_012.648657091693304514 * 1e18,
- exchangeRate: 1.013503541960817259000463129 * 1e27
- }
- );
+ _arbTake({
+ from: _taker,
+ borrower: _borrower,
+ kicker: _lender,
+ index: _i9_91,
+ collateralArbed: 2 * 1e18,
+ quoteTokenAmount: 14.503461444385064128 * 1e18,
+ bondChange: 0.145034614443850641 * 1e18,
+ isReward: true,
+ lpAwardTaker: 5.259881215780552826 * 1e18,
+ lpAwardKicker: 0.143102227509983165 * 1e18
+ });
+
+ _assertLenderLpBalance({
+ lender: _taker,
+ index: _i9_91,
+ lpBalance: 5.259881215780552826 * 1e18,
+ depositTime: _startTime + 100 days + 6.5 hours
+ });
+ _assertLenderLpBalance({
+ lender: _lender,
+ index: _i9_91,
+ lpBalance: 2_000.143102227509983165 * 1e18, // rewarded with LPs in bucket
+ depositTime: _startTime + 100 days + 6.5 hours
+ });
+ _assertBucket({
+ index: _i9_91,
+ lpBalance: 2_005.402983443290535991 * 1e18,
+ collateral: 2 * 1e18,
+ deposit: 2_012.648657091693304514 * 1e18,
+ exchangeRate: 1.013503541960817259 * 1e18
+ });
// reserves should remain the same after arb take
- _assertReserveAuction(
- {
- reserves: 25.295951940381566551 * 1e18,
- claimableReserves : 0,
- claimableReservesRemaining: 0,
- auctionPrice: 0,
- timeRemaining: 0
- }
- );
- _assertBorrower(
- {
- borrower: _borrower,
- borrowerDebt: 6.805228224892631302 * 1e18,
- borrowerCollateral: 0,
- borrowert0Np: 10.115967548076923081 * 1e18,
- borrowerCollateralization: 0
- }
- );
+ _assertReserveAuction({
+ reserves: 25.295951940381566551 * 1e18,
+ claimableReserves : 0,
+ claimableReservesRemaining: 0,
+ auctionPrice: 0,
+ timeRemaining: 0
+ });
+ _assertBorrower({
+ borrower: _borrower,
+ borrowerDebt: 6.805228224892631302 * 1e18,
+ borrowerCollateral: 0,
+ borrowert0Np: 10.115967548076923081 * 1e18,
+ borrowerCollateralization: 0
+ });
_assertAuction(
- AuctionParams({
- borrower: _borrower,
- active: true,
- kicker: _lender,
- bondSize: 0.195342779771472726 * 1e18, // bond size remains the same, kicker was rewarded with LPs
- bondFactor: 0.01 * 1e18,
- kickTime: block.timestamp - 6.5 hours,
- kickMomp: 9.818751856078723036 * 1e18,
- totalBondEscrowed: 0.195342779771472726 * 1e18,
- auctionPrice: 7.251730722192532064 * 1e18,
- debtInAuction: 6.805228224892631302 * 1e18,
- thresholdPrice: 0,
- neutralPrice: 10.255495938002318100 * 1e18
- })
+ AuctionParams({
+ borrower: _borrower,
+ active: true,
+ kicker: _lender,
+ bondSize: 0.195342779771472726 * 1e18, // bond size remains the same, kicker was rewarded with LPs
+ bondFactor: 0.01 * 1e18,
+ kickTime: block.timestamp - 6.5 hours,
+ kickMomp: 9.818751856078723036 * 1e18,
+ totalBondEscrowed: 0.195342779771472726 * 1e18,
+ auctionPrice: 7.251730722192532064 * 1e18,
+ debtInAuction: 6.805228224892631302 * 1e18,
+ thresholdPrice: 0,
+ neutralPrice: 10.255495938002318100 * 1e18
+ })
);
// Arb take should fail on an auction without any remaining collateral to auction
- _assertArbTakeInsufficentCollateralRevert(
- {
- from: _taker,
- borrower: _borrower,
- index: _i9_91
- }
- );
+ _assertArbTakeInsufficentCollateralRevert({
+ from: _taker,
+ borrower: _borrower,
+ index: _i9_91
+ });
}
function testArbTakeDebtRestrict() external tearDown {
@@ -411,103 +349,85 @@ contract ERC20PoolLiquidationsArbTakeTest is ERC20HelperContract {
skip(5 hours);
_assertAuction(
- AuctionParams({
- borrower: _borrower,
- active: true,
- kicker: _lender,
- bondSize: 0.195342779771472726 * 1e18,
- bondFactor: 0.01 * 1e18,
- kickTime: block.timestamp - 5 hours,
- kickMomp: 9.818751856078723036 * 1e18,
- totalBondEscrowed: 0.195342779771472726 * 1e18,
- auctionPrice: 20.510991876004636192 * 1e18,
- debtInAuction: 19.778456451861613480 * 1e18,
- thresholdPrice: 9.889482233342512889 * 1e18,
- neutralPrice: 10.255495938002318100 * 1e18
- })
+ AuctionParams({
+ borrower: _borrower,
+ active: true,
+ kicker: _lender,
+ bondSize: 0.195342779771472726 * 1e18,
+ bondFactor: 0.01 * 1e18,
+ kickTime: block.timestamp - 5 hours,
+ kickMomp: 9.818751856078723036 * 1e18,
+ totalBondEscrowed: 0.195342779771472726 * 1e18,
+ auctionPrice: 20.510991876004636192 * 1e18,
+ debtInAuction: 19.778456451861613480 * 1e18,
+ thresholdPrice: 9.889482233342512889 * 1e18,
+ neutralPrice: 10.255495938002318100 * 1e18
+ })
);
- _addLiquidity(
- {
- from: _lender,
- amount: 25_000 * 1e18,
- index: _i1505_26,
- lpAward: 25_000 * 1e27,
- newLup: 1_505.263728469068226832 * 1e18
- }
- );
+ _addLiquidity({
+ from: _lender,
+ amount: 25_000 * 1e18,
+ index: _i1505_26,
+ lpAward: 25_000 * 1e18,
+ newLup: 1_505.263728469068226832 * 1e18
+ });
- _assertBorrower(
- {
- borrower: _borrower,
- borrowerDebt: 19.778964466685025779 * 1e18,
- borrowerCollateral: 2 * 1e18,
- borrowert0Np: 10.115967548076923081 * 1e18,
- borrowerCollateralization: 152.208547722958917634 * 1e18
- }
- );
+ _assertBorrower({
+ borrower: _borrower,
+ borrowerDebt: 19.778964466685025779 * 1e18,
+ borrowerCollateral: 2 * 1e18,
+ borrowert0Np: 10.115967548076923081 * 1e18,
+ borrowerCollateralization: 152.208547722958917634 * 1e18
+ });
// Amount is restricted by the debt in the loan
- _arbTake(
- {
- from: _taker,
- borrower: _borrower,
- kicker: _lender,
- index: _i1505_26,
- collateralArbed: 1.031812215971460994 * 1e18,
- quoteTokenAmount: 21.163491979352977584 * 1e18,
- bondChange: 0.195342779771472726 * 1e18,
- isReward: false,
- lpAwardTaker: 1_531.986011313779866428534379038 * 1e27,
- lpAwardKicker: 0
- }
- );
-
- _assertBorrower(
- {
- borrower: _borrower,
- borrowerDebt: 0,
- borrowerCollateral: 0.968187784028539006 * 1e18,
- borrowert0Np: 0,
- borrowerCollateralization: 1 * 1e18
- }
- );
-
- _assertLenderLpBalance(
- {
- lender: _taker,
- index: _i1505_26,
- lpBalance: 1_531.986011313779866428534379038 * 1e27,
- depositTime: block.timestamp
- }
- );
- _assertLenderLpBalance(
- {
- lender: _lender,
- index: _i1505_26,
- lpBalance: 25_000 * 1e27,
- depositTime: block.timestamp
- }
- );
- _assertBucket(
- {
- index: _i1505_26,
- lpBalance: 26_531.986011313779866428534379038 * 1e27,
- collateral: 1.031812215971460994 * 1e18,
- deposit: 24_978.836508020647022417 * 1e18,
- exchangeRate: 0.999999999999999999999729424 * 1e27
- }
- );
-
- _assertReserveAuction(
- {
- reserves: 25.482262302272484525 * 1e18,
- claimableReserves : 0,
- claimableReservesRemaining: 0,
- auctionPrice: 0,
- timeRemaining: 0
- }
- );
+ _arbTake({
+ from: _taker,
+ borrower: _borrower,
+ kicker: _lender,
+ index: _i1505_26,
+ collateralArbed: 1.031812215971460994 * 1e18,
+ quoteTokenAmount: 21.163491979352977584 * 1e18,
+ bondChange: 0.195342779771472726 * 1e18,
+ isReward: false,
+ lpAwardTaker: 1_531.986011313779866429 * 1e18,
+ lpAwardKicker: 0
+ });
+
+ _assertBorrower({
+ borrower: _borrower,
+ borrowerDebt: 0,
+ borrowerCollateral: 0.968187784028539006 * 1e18,
+ borrowert0Np: 0,
+ borrowerCollateralization: 1 * 1e18
+ });
+ _assertLenderLpBalance({
+ lender: _taker,
+ index: _i1505_26,
+ lpBalance: 1_531.986011313779866429 * 1e18,
+ depositTime: block.timestamp
+ });
+ _assertLenderLpBalance({
+ lender: _lender,
+ index: _i1505_26,
+ lpBalance: 25_000 * 1e18,
+ depositTime: block.timestamp
+ });
+ _assertBucket({
+ index: _i1505_26,
+ lpBalance: 26_531.986011313779866429 * 1e18,
+ collateral: 1.031812215971460994 * 1e18,
+ deposit: 24_978.836508020647022417 * 1e18,
+ exchangeRate: 1 * 1e18
+ });
+ _assertReserveAuction({
+ reserves: 25.482262302272484525 * 1e18,
+ claimableReserves : 0,
+ claimableReservesRemaining: 0,
+ auctionPrice: 0,
+ timeRemaining: 0
+ });
}
function testArbTakeDepositRestrict() external tearDown {
@@ -515,166 +435,127 @@ contract ERC20PoolLiquidationsArbTakeTest is ERC20HelperContract {
skip(5 hours);
_assertAuction(
- AuctionParams({
- borrower: _borrower,
- active: true,
- kicker: _lender,
- bondSize: 0.195342779771472726 * 1e18,
- bondFactor: 0.01 * 1e18,
- kickTime: block.timestamp - 5 hours,
- kickMomp: 9.818751856078723036 * 1e18,
- totalBondEscrowed: 0.195342779771472726 * 1e18,
- auctionPrice: 20.510991876004636192 * 1e18,
- debtInAuction: 19.778456451861613480 * 1e18,
- thresholdPrice: 9.889482233342512889 * 1e18,
- neutralPrice: 10.255495938002318100 * 1e18
- })
+ AuctionParams({
+ borrower: _borrower,
+ active: true,
+ kicker: _lender,
+ bondSize: 0.195342779771472726 * 1e18,
+ bondFactor: 0.01 * 1e18,
+ kickTime: block.timestamp - 5 hours,
+ kickMomp: 9.818751856078723036 * 1e18,
+ totalBondEscrowed: 0.195342779771472726 * 1e18,
+ auctionPrice: 20.510991876004636192 * 1e18,
+ debtInAuction: 19.778456451861613480 * 1e18,
+ thresholdPrice: 9.889482233342512889 * 1e18,
+ neutralPrice: 10.255495938002318100 * 1e18
+ })
);
- _addLiquidity(
- {
- from: _lender,
- amount: 15.0 * 1e18,
- index: _i1505_26,
- lpAward: 15.0 * 1e27,
- newLup: 9.721295865031779605 * 1e18
- }
- );
+ _addLiquidity({
+ from: _lender,
+ amount: 15.0 * 1e18,
+ index: _i1505_26,
+ lpAward: 15.0 * 1e18,
+ newLup: 9.721295865031779605 * 1e18
+ });
- _assertBorrower(
- {
- borrower: _borrower,
- borrowerDebt: 19.778964466685025779 * 1e18,
- borrowerCollateral: 2 * 1e18,
- borrowert0Np: 10.115967548076923081 * 1e18,
- borrowerCollateralization: 0.982993410135902682 * 1e18
- }
- );
+ _assertBorrower({
+ borrower: _borrower,
+ borrowerDebt: 19.778964466685025779 * 1e18,
+ borrowerCollateral: 2 * 1e18,
+ borrowert0Np: 10.115967548076923081 * 1e18,
+ borrowerCollateralization: 0.982993410135902682 * 1e18
+ });
// Amount is restricted by the deposit in the bucket
- _arbTake(
- {
- from: _taker,
- borrower: _borrower,
- kicker: _lender,
- index: _i1505_26,
- collateralArbed: 0.731315193857015473 * 1e18,
- quoteTokenAmount: 15.000000000000000000 * 1e18,
- bondChange: 0.15 * 1e18,
- isReward: false,
- lpAwardTaker: 1_085.822235391290531116686016658 * 1e27,
- lpAwardKicker: 0
- }
- );
+ _arbTake({
+ from: _taker,
+ borrower: _borrower,
+ kicker: _lender,
+ index: _i1505_26,
+ collateralArbed: 0.731315193857015473 * 1e18,
+ quoteTokenAmount: 14.99999999999999999 * 1e18,
+ bondChange: 0.15 * 1e18,
+ isReward: false,
+ lpAwardTaker: 1_085.822235391290531090 * 1e18,
+ lpAwardKicker: 0
+ });
_assertAuction(
- AuctionParams({
- borrower: _borrower,
- active: false,
- kicker: address(0),
- bondSize: 0,
- bondFactor: 0,
- kickTime: 0,
- kickMomp: 0,
- totalBondEscrowed: 0,
- auctionPrice: 0,
- debtInAuction: 0,
- thresholdPrice: 4.858174346779663271 * 1e18,
- neutralPrice: 0
- })
- );
-
- _assertBucket(
- {
- index: _i1505_26,
- lpBalance: 1_100.822235391290531116686016658 * 1e27,
- collateral: 0.731315193857015473 * 1e18,
- deposit: 0,
- exchangeRate: 0.999999999999999999966196099 * 1e27
- }
- );
-
- _assertBorrower(
- {
- borrower: _borrower,
- borrowerDebt: 6.163491979352977583 * 1e18,
- borrowerCollateral: 1.268684806142984527 * 1e18,
- borrowert0Np: 5.108498139847549815 * 1e18,
- borrowerCollateralization: 2.001018319047304755 * 1e18
- }
- );
-
- _assertLenderLpBalance(
- {
- lender: _taker,
- index: _i1505_26,
- lpBalance: 1_085.822235391290531116686016658 * 1e27,
- depositTime: block.timestamp
- }
- );
-
- _assertLenderLpBalance(
- {
- lender: _lender,
- index: _i1505_26,
- lpBalance: 15.0 * 1e27,
- depositTime: block.timestamp
- }
+ AuctionParams({
+ borrower: _borrower,
+ active: false,
+ kicker: address(0),
+ bondSize: 0,
+ bondFactor: 0,
+ kickTime: 0,
+ kickMomp: 0,
+ totalBondEscrowed: 0,
+ auctionPrice: 0,
+ debtInAuction: 0,
+ thresholdPrice: 4.858174346779663271 * 1e18,
+ neutralPrice: 0
+ })
);
+ _assertBucket({
+ index: _i1505_26,
+ lpBalance: 1_100.822235391290531090 * 1e18,
+ collateral: 0.731315193857015473 * 1e18,
+ deposit: 0,
+ exchangeRate: 1 * 1e18
+ });
+ _assertBorrower({
+ borrower: _borrower,
+ borrowerDebt: 6.163491979352977583 * 1e18,
+ borrowerCollateral: 1.268684806142984527 * 1e18,
+ borrowert0Np: 5.057793757429320955 * 1e18,
+ borrowerCollateralization: 2.001018319047304755 * 1e18
+ });
+ _assertLenderLpBalance({
+ lender: _taker,
+ index: _i1505_26,
+ lpBalance: 1_085.822235391290531090 * 1e18,
+ depositTime: block.timestamp
+ });
+ _assertLenderLpBalance({
+ lender: _lender,
+ index: _i1505_26,
+ lpBalance: 15.0 * 1e18,
+ depositTime: block.timestamp
+ });
}
function testArbTakeGTNeutralPrice() external tearDown {
skip(3 hours);
- _addLiquidity(
- {
- from: _lender,
- amount: 1_000 * 1e18,
- index: _i10016,
- lpAward: 1_000 * 1e27,
- newLup: 9.721295865031779605 * 1e18
- }
- );
-
- _assertLenderLpBalance(
- {
- lender: _taker,
- index: _i10016,
- lpBalance: 0,
- depositTime: 0
- }
- );
-
- _assertLenderLpBalance(
- {
- lender: _lender,
- index: _i10016,
- lpBalance: 1_000 * 1e27,
- depositTime: block.timestamp
- }
- );
-
- _assertBucket(
- {
- index: _i10016,
- lpBalance: 1_000 * 1e27,
- collateral: 0,
- deposit: 1_000 * 1e18,
- exchangeRate: 1.0 * 1e27
- }
- );
-
- _assertBorrower(
- {
- borrower: _borrower,
- borrowerDebt: 19.778761259189860403 * 1e18,
- borrowerCollateral: 2 * 1e18,
- borrowert0Np: 10.115967548076923081 * 1e18,
- borrowerCollateralization: 0.983003509435146965 * 1e18
- }
- );
-
+ _addLiquidity({
+ from: _lender,
+ amount: 1_000 * 1e18,
+ index: _i10016,
+ lpAward: 1_000 * 1e18,
+ newLup: 9.721295865031779605 * 1e18
+ });
+
+ _assertLenderLpBalance({
+ lender: _taker,
+ index: _i10016,
+ lpBalance: 0,
+ depositTime: 0
+ });
+ _assertLenderLpBalance({
+ lender: _lender,
+ index: _i10016,
+ lpBalance: 1_000 * 1e18,
+ depositTime: block.timestamp
+ });
+ _assertBucket({
+ index: _i10016,
+ lpBalance: 1_000 * 1e18,
+ collateral: 0,
+ deposit: 1_000 * 1e18,
+ exchangeRate: 1.0 * 1e18
+ });
_assertAuction(
AuctionParams({
borrower: _borrower,
@@ -691,73 +572,51 @@ contract ERC20PoolLiquidationsArbTakeTest is ERC20HelperContract {
neutralPrice: 10.255495938002318100 * 1e18
})
);
-
- _assertBorrower(
- {
- borrower: _borrower,
- borrowerDebt: 19.778761259189860403 * 1e18,
- borrowerCollateral: 2 * 1e18,
- borrowert0Np: 10.115967548076923081 * 1e18,
- borrowerCollateralization: 0.983003509435146965 * 1e18
- }
- );
-
- _arbTake(
- {
- from: _taker,
- borrower: _borrower,
- kicker: _lender,
- index: _i10016,
- collateralArbed: 0.257950403803869741 * 1e18,
- quoteTokenAmount: 21.163274547333150631 * 1e18,
- bondChange: 0.195342779771472726 * 1e18,
- isReward: false,
- lpAwardTaker: 2_562.597355112798042001349648580 * 1e27,
- lpAwardKicker: 0
- }
- );
-
- _assertLenderLpBalance(
- {
- lender: _taker,
- index: _i10016,
- lpBalance: 2_562.597355112798042001349648580 * 1e27, // arb taker was rewarded LPBs in arbed bucket
- depositTime: _startTime + 100 days + 3 hours
- }
- );
- _assertLenderLpBalance(
- {
- lender: _lender,
- index: _i10016,
- lpBalance: 1_000 * 1e27,
- depositTime: _startTime + 100 days + 3 hours
- }
- );
- _assertKicker(
- {
- kicker: _lender,
- claimable: 0,
- locked: 0 // kicker was penalized
- }
- );
- _assertBucket(
- {
- index: _i10016,
- lpBalance: 3_562.597355112798042001349648580 * 1e27, // LP balance in arbed bucket increased with LPs awarded for arb taker
- collateral: 0.257950403803869741 * 1e18, // arbed collateral added to the arbed bucket
- deposit: 978.836725452666849368 * 1e18, // quote token amount is diminished in arbed bucket
- exchangeRate: 1.000000000000000000007160522 * 1e27
- }
- );
- _assertBorrower(
- {
- borrower: _borrower,
- borrowerDebt: 0,
- borrowerCollateral: 1.742049596196130259 * 1e18,
- borrowert0Np: 0,
- borrowerCollateralization: 1 * 1e18
- }
- );
+ _assertBorrower({
+ borrower: _borrower,
+ borrowerDebt: 19.778761259189860403 * 1e18,
+ borrowerCollateral: 2 * 1e18,
+ borrowert0Np: 10.115967548076923081 * 1e18,
+ borrowerCollateralization: 0.983003509435146965 * 1e18
+ });
+
+ _arbTake({
+ from: _taker,
+ borrower: _borrower,
+ kicker: _lender,
+ index: _i10016,
+ collateralArbed: 0.257950403803869741 * 1e18,
+ quoteTokenAmount: 21.163274547333150631 * 1e18,
+ bondChange: 0.195342779771472726 * 1e18,
+ isReward: false,
+ lpAwardTaker: 2_562.597355112798042 * 1e18,
+ lpAwardKicker: 0
+ });
+
+ _assertLenderLpBalance({
+ lender: _taker,
+ index: _i10016,
+ lpBalance: 2_562.597355112798042 * 1e18, // arb taker was rewarded LPBs in arbed bucket
+ depositTime: _startTime + 100 days + 3 hours
+ });
+ _assertLenderLpBalance({
+ lender: _lender,
+ index: _i10016,
+ lpBalance: 1_000 * 1e18,
+ depositTime: _startTime + 100 days + 3 hours
+ });
+ _assertKicker({
+ kicker: _lender,
+ claimable: 0,
+ locked: 0 // kicker was penalized
+ });
+ _assertBucket({
+ index: _i10016,
+ lpBalance: 3_562.597355112798042 * 1e18, // LP balance in arbed bucket increased with LPs awarded for arb taker
+ collateral: 0.257950403803869741 * 1e18, // arbed collateral added to the arbed bucket
+ deposit: 978.836725452666849368 * 1e18, // quote token amount is diminished in arbed bucket
+ exchangeRate: 1 * 1e18
+ });
_assertAuction(
AuctionParams({
borrower: _borrower,
@@ -774,27 +633,30 @@ contract ERC20PoolLiquidationsArbTakeTest is ERC20HelperContract {
neutralPrice: 0
})
);
+ _assertBorrower({
+ borrower: _borrower,
+ borrowerDebt: 0,
+ borrowerCollateral: 1.742049596196130259 * 1e18,
+ borrowert0Np: 0,
+ borrowerCollateralization: 1 * 1e18
+ });
}
function testArbTakeReverts() external tearDown {
// should revert if borrower not auctioned
- _assertArbTakeNoAuction(
- {
- from: _lender,
- borrower: _borrower2,
- index: _i9_91
- }
- );
+ _assertArbTakeNoAuction({
+ from: _lender,
+ borrower: _borrower2,
+ index: _i9_91
+ });
// should revert if auction in grace period
- _assertArbTakeAuctionInCooldownRevert(
- {
- from: _lender,
- borrower: _borrower,
- index: _i9_91
- }
- );
+ _assertArbTakeAuctionInCooldownRevert({
+ from: _lender,
+ borrower: _borrower,
+ index: _i9_91
+ });
skip(2.5 hours);
@@ -814,49 +676,40 @@ contract ERC20PoolLiquidationsArbTakeTest is ERC20HelperContract {
neutralPrice: 10.255495938002318100 * 1e18
})
);
-
- _assertBorrower(
- {
- borrower: _borrower,
- borrowerDebt: 19.778710457642278866 * 1e18,
- borrowerCollateral: 2 * 1e18,
- borrowert0Np: 10.115967548076923081 * 1e18,
- borrowerCollateralization: 0.983006034276170567 * 1e18
- }
- );
+ _assertBorrower({
+ borrower: _borrower,
+ borrowerDebt: 19.778710457642278866 * 1e18,
+ borrowerCollateral: 2 * 1e18,
+ borrowert0Np: 10.115967548076923081 * 1e18,
+ borrowerCollateralization: 0.983006034276170567 * 1e18
+ });
// should revert if bucket deposit is 0
- _assertArbTakeAuctionInsufficientLiquidityRevert(
- {
- from: _taker,
- borrower: _borrower,
- index: _i100_33
- }
- );
+ _assertArbTakeAuctionInsufficientLiquidityRevert({
+ from: _taker,
+ borrower: _borrower,
+ index: _i100_33
+ });
// should revert if auction price is greater than the bucket price
- _assertArbTakeAuctionPriceGreaterThanBucketPriceRevert(
- {
- from: _taker,
- borrower: _borrower,
- index: _i9_91
- }
- );
+ _assertArbTakeAuctionPriceGreaterThanBucketPriceRevert({
+ from: _taker,
+ borrower: _borrower,
+ index: _i9_91
+ });
skip(4 hours);
// 10 borrowers draw debt to enable the min debt check
for (uint i=0; i<10; ++i) {
- _anonBorrowerDrawsDebt(1_000 * 1e18, 6_000 * 1e18, 7777);
+ _anonBorrowerDrawsDebt(1_000 * 1e18, 6_000 * 1e18, MAX_FENWICK_INDEX);
}
// should revert if auction leaves borrower with debt under minimum pool debt
- _assertArbTakeDebtUnderMinPoolDebtRevert(
- {
- from: _taker,
- borrower: _borrower,
- index: _i9_91
- }
- );
+ _assertArbTakeDebtUnderMinPoolDebtRevert({
+ from: _taker,
+ borrower: _borrower,
+ index: _i9_91
+ });
}
}
diff --git a/tests/forge/ERC20Pool/ERC20PoolLiquidationsDepositTake.t.sol b/tests/forge/ERC20Pool/ERC20PoolLiquidationsDepositTake.t.sol
index cdbf77375..5a1818a59 100644
--- a/tests/forge/ERC20Pool/ERC20PoolLiquidationsDepositTake.t.sol
+++ b/tests/forge/ERC20Pool/ERC20PoolLiquidationsDepositTake.t.sol
@@ -28,75 +28,57 @@ contract ERC20PoolLiquidationsDepositTakeTest is ERC20HelperContract {
_mintCollateralAndApproveTokens(_lender1, 4 * 1e18);
// Lender adds Quote token accross 5 prices
- _addInitialLiquidity(
- {
- from: _lender,
- amount: 2_000 * 1e18,
- index: _i9_91
- }
- );
- _addInitialLiquidity(
- {
- from: _lender,
- amount: 5_000 * 1e18,
- index: _i9_81
- }
- );
- _addInitialLiquidity(
- {
- from: _lender,
- amount: 11_000 * 1e18,
- index: _i9_72
- }
- );
- _addInitialLiquidity(
- {
- from: _lender,
- amount: 25_000 * 1e18,
- index: _i9_62
- }
- );
- _addInitialLiquidity(
- {
- from: _lender,
- amount: 30_000 * 1e18,
- index: _i9_52
- }
- );
+ _addInitialLiquidity({
+ from: _lender,
+ amount: 2_000 * 1e18,
+ index: _i9_91
+ });
+ _addInitialLiquidity({
+ from: _lender,
+ amount: 5_000 * 1e18,
+ index: _i9_81
+ });
+ _addInitialLiquidity({
+ from: _lender,
+ amount: 11_000 * 1e18,
+ index: _i9_72
+ });
+ _addInitialLiquidity({
+ from: _lender,
+ amount: 25_000 * 1e18,
+ index: _i9_62
+ });
+ _addInitialLiquidity({
+ from: _lender,
+ amount: 30_000 * 1e18,
+ index: _i9_52
+ });
// first borrower adds collateral token and borrows
- _pledgeCollateral(
- {
- from: _borrower,
- borrower: _borrower,
- amount: 2 * 1e18
- }
- );
- _borrow(
- {
- from: _borrower,
- amount: 19.25 * 1e18,
- indexLimit: _i9_91,
- newLup: 9.917184843435912074 * 1e18
- }
- );
+ _pledgeCollateral({
+ from: _borrower,
+ borrower: _borrower,
+ amount: 2 * 1e18
+ });
+ _borrow({
+ from: _borrower,
+ amount: 19.25 * 1e18,
+ indexLimit: _i9_91,
+ newLup: 9.917184843435912074 * 1e18
+ });
// second borrower adds collateral token and borrows
- _pledgeCollateral(
- {
- from: _borrower2,
- borrower: _borrower2,
- amount: 1_000 * 1e18
- }
- );
- _borrow(
- {
- from: _borrower2,
- amount: 7_980 * 1e18,
- indexLimit: _i9_72,
- newLup: 9.721295865031779605 * 1e18
- }
- );
+ _pledgeCollateral({
+ from: _borrower2,
+ borrower: _borrower2,
+ amount: 1_000 * 1e18
+ });
+ _borrow({
+ from: _borrower2,
+ amount: 7_980 * 1e18,
+ indexLimit: _i9_72,
+ newLup: 9.721295865031779605 * 1e18
+ });
/*****************************/
/*** Assert pre-kick state ***/
@@ -119,43 +101,36 @@ contract ERC20PoolLiquidationsDepositTakeTest is ERC20HelperContract {
interestRateUpdate: _startTime
})
);
- _assertBorrower(
- {
- borrower: _borrower,
- borrowerDebt: 19.268509615384615394 * 1e18,
- borrowerCollateral: 2 * 1e18,
- borrowert0Np: 10.115967548076923081 * 1e18,
- borrowerCollateralization: 1.009034539679184679 * 1e18
- }
- );
- _assertBorrower(
- {
- borrower: _borrower2,
- borrowerDebt: 7_987.673076923076926760 * 1e18,
- borrowerCollateral: 1_000 * 1e18,
- borrowert0Np: 8.471136974495192174 * 1e18,
- borrowerCollateralization: 1.217037273735858713 * 1e18
- }
- );
- _assertReserveAuction(
- {
- reserves: 7.691586538461542154 * 1e18,
- claimableReserves : 0,
- claimableReservesRemaining: 0,
- auctionPrice: 0,
- timeRemaining: 0
- }
- );
+ _assertBorrower({
+ borrower: _borrower,
+ borrowerDebt: 19.268509615384615394 * 1e18,
+ borrowerCollateral: 2 * 1e18,
+ borrowert0Np: 10.115967548076923081 * 1e18,
+ borrowerCollateralization: 1.009034539679184679 * 1e18
+ });
+ _assertBorrower({
+ borrower: _borrower2,
+ borrowerDebt: 7_987.673076923076926760 * 1e18,
+ borrowerCollateral: 1_000 * 1e18,
+ borrowert0Np: 8.471136974495192174 * 1e18,
+ borrowerCollateralization: 1.217037273735858713 * 1e18
+ });
+ _assertReserveAuction({
+ reserves: 7.691586538461542154 * 1e18,
+ claimableReserves : 0,
+ claimableReservesRemaining: 0,
+ auctionPrice: 0,
+ timeRemaining: 0
+ });
+
assertEq(_quote.balanceOf(_lender), 47_000 * 1e18);
// should revert if there's no auction started
- _assertDepositTakeNoAuctionRevert(
- {
- from: _lender,
- borrower: _borrower,
- index: _i9_91
- }
- );
+ _assertDepositTakeNoAuctionRevert({
+ from: _lender,
+ borrower: _borrower,
+ index: _i9_91
+ });
// Skip to make borrower undercollateralized
skip(250 days);
@@ -176,27 +151,22 @@ contract ERC20PoolLiquidationsDepositTakeTest is ERC20HelperContract {
neutralPrice: 0
})
);
-
- _assertBorrower(
- {
- borrower: _borrower,
- borrowerDebt: 19.939819504377940339 * 1e18,
- borrowerCollateral: 2 * 1e18,
- borrowert0Np: 10.115967548076923081 * 1e18,
- borrowerCollateralization: 0.975063576969429891 * 1e18
- }
- );
+ _assertBorrower({
+ borrower: _borrower,
+ borrowerDebt: 19.939819504377940339 * 1e18,
+ borrowerCollateral: 2 * 1e18,
+ borrowert0Np: 10.115967548076923081 * 1e18,
+ borrowerCollateralization: 0.975063576969429891 * 1e18
+ });
- _kick(
- {
- from: _lender,
- borrower: _borrower,
- debt: 20.189067248182664593 * 1e18,
- collateral: 2 * 1e18,
- bond: 0.199398195043779403 * 1e18,
- transferAmount: 0.199398195043779403 * 1e18
- }
- );
+ _kick({
+ from: _lender,
+ borrower: _borrower,
+ debt: 20.189067248182664593 * 1e18,
+ collateral: 2 * 1e18,
+ bond: 0.199398195043779403 * 1e18,
+ transferAmount: 0.199398195043779403 * 1e18
+ });
_assertAuction(
AuctionParams({
@@ -214,195 +184,162 @@ contract ERC20PoolLiquidationsDepositTakeTest is ERC20HelperContract {
neutralPrice: 10.468405239798418677 * 1e18
})
);
-
- _assertKicker(
- {
- kicker: _lender,
- claimable: 0,
- locked: 0.199398195043779403 * 1e18
- }
- );
+ _assertKicker({
+ kicker: _lender,
+ claimable: 0,
+ locked: 0.199398195043779403 * 1e18
+ });
}
function testDepositTakeCollateralRestrict() external tearDown {
skip(6.5 hours);
- _assertLenderLpBalance(
- {
- lender: _taker,
- index: _i9_91,
- lpBalance: 0,
- depositTime: 0
- }
- );
- _assertLenderLpBalance(
- {
- lender: _lender,
- index: _i9_91,
- lpBalance: 2_000 * 1e27,
- depositTime: _startTime
- }
- );
- _assertBucket(
- {
- index: _i9_91,
- lpBalance: 2_000 * 1e27,
- collateral: 0,
- deposit: 2_000 * 1e18,
- exchangeRate: 1 * 1e27
- }
- );
- _assertBorrower(
- {
- borrower: _borrower,
- borrowerDebt: 20.189741380689676442 * 1e18,
- borrowerCollateral: 2 * 1e18,
- borrowert0Np: 10.115967548076923081 * 1e18,
- borrowerCollateralization: 0.962993599742653326 * 1e18
- }
- );
+ _assertLenderLpBalance({
+ lender: _taker,
+ index: _i9_91,
+ lpBalance: 0,
+ depositTime: 0
+ });
+ _assertLenderLpBalance({
+ lender: _lender,
+ index: _i9_91,
+ lpBalance: 2_000 * 1e18,
+ depositTime: _startTime
+ });
+ _assertBucket({
+ index: _i9_91,
+ lpBalance: 2_000 * 1e18,
+ collateral: 0,
+ deposit: 2_000 * 1e18,
+ exchangeRate: 1 * 1e18
+ });
+ _assertBorrower({
+ borrower: _borrower,
+ borrowerDebt: 20.189741380689676442 * 1e18,
+ borrowerCollateral: 2 * 1e18,
+ borrowert0Np: 10.115967548076923081 * 1e18,
+ borrowerCollateralization: 0.962993599742653326 * 1e18
+ });
// add liquidity to accrue interest and update reserves before deposit take
- _addLiquidity(
- {
- from: _lender1,
- amount: 1 * 1e18,
- index: _i9_52,
- lpAward: 0.999996755983514720095749768 * 1e27,
- newLup: 9.721295865031779605 * 1e18
- }
- );
- _assertBucket(
- {
- index: _i9_91,
- lpBalance: 2_000 * 1e27,
- collateral: 0,
- deposit: 2_000.006488054017914000 * 1e18,
- exchangeRate: 1.000003244027008957000000000 * 1e27
- }
- );
- _assertReserveAuction(
- {
- reserves: 286.940475866492567343 * 1e18,
- claimableReserves : 245.508339417301835201 * 1e18,
- claimableReservesRemaining: 0,
- auctionPrice: 0,
- timeRemaining: 0
- }
- );
+ _addLiquidity({
+ from: _lender1,
+ amount: 1 * 1e18,
+ index: _i9_52,
+ lpAward: 0.999996755983514720 * 1e18,
+ newLup: 9.721295865031779605 * 1e18
+ });
+
+ _assertBucket({
+ index: _i9_91,
+ lpBalance: 2_000 * 1e18,
+ collateral: 0,
+ deposit: 2_000.006488054017914000 * 1e18,
+ exchangeRate: 1.000003244027008957 * 1e18
+ });
+ _assertReserveAuction({
+ reserves: 286.940475866492567343 * 1e18,
+ claimableReserves : 245.508339417301835201 * 1e18,
+ claimableReservesRemaining: 0,
+ auctionPrice: 0,
+ timeRemaining: 0
+ });
_assertAuction(
- AuctionParams({
- borrower: _borrower,
- active: true,
- kicker: _lender,
- bondSize: 0.199398195043779403 * 1e18,
- bondFactor: 0.01 * 1e18,
- kickTime: block.timestamp - 6.5 hours,
- kickMomp: 9.818751856078723036 * 1e18,
- totalBondEscrowed: 0.199398195043779403 * 1e18,
- auctionPrice: 7.402280333270247968 * 1e18,
- debtInAuction: 20.189741380689676442 * 1e18,
- thresholdPrice: 10.094870690344838221 * 1e18,
- neutralPrice: 10.468405239798418677 * 1e18
- })
- );
-
- _assertBorrower(
- {
- borrower: _borrower,
- borrowerDebt: 20.189741380689676442 * 1e18,
- borrowerCollateral: 2 * 1e18,
- borrowert0Np: 10.115967548076923081 * 1e18,
- borrowerCollateralization: 0.962993599742653326 * 1e18
- }
+ AuctionParams({
+ borrower: _borrower,
+ active: true,
+ kicker: _lender,
+ bondSize: 0.199398195043779403 * 1e18,
+ bondFactor: 0.01 * 1e18,
+ kickTime: block.timestamp - 6.5 hours,
+ kickMomp: 9.818751856078723036 * 1e18,
+ totalBondEscrowed: 0.199398195043779403 * 1e18,
+ auctionPrice: 7.402280333270247968 * 1e18,
+ debtInAuction: 20.189741380689676442 * 1e18,
+ thresholdPrice: 10.094870690344838221 * 1e18,
+ neutralPrice: 10.468405239798418677 * 1e18
+ })
);
+ _assertBorrower({
+ borrower: _borrower,
+ borrowerDebt: 20.189741380689676442 * 1e18,
+ borrowerCollateral: 2 * 1e18,
+ borrowert0Np: 10.115967548076923081 * 1e18,
+ borrowerCollateralization: 0.962993599742653326 * 1e18
+ });
// Amount is restricted by the collateral in the loan
- _depositTake(
- {
- from: _taker,
- borrower: _borrower,
- kicker: _lender,
- index: _i9_91,
- collateralArbed: 2 * 1e18,
- quoteTokenAmount: 19.834369686871824148 * 1e18,
- bondChange: 0.198343696868718241 * 1e18,
- isReward: true,
- lpAwardTaker: 0,
- lpAwardKicker: 0.198343053438495848000000000 * 1e27
- }
- );
-
- _assertLenderLpBalance(
- {
- lender: _taker,
- index: _i9_91,
- lpBalance: 0,
- depositTime: 0
- }
- );
- _assertLenderLpBalance(
- {
- lender: _lender,
- index: _i9_91,
- lpBalance: 2_000.198343053438495848000000000 * 1e27,
- depositTime: _startTime + 250 days + 6.5 hours
- }
- );
- _assertBucket(
- {
- index: _i9_91,
- lpBalance: 2_000.198343053438495848000000000 * 1e27,
- collateral: 2 * 1e18,
- deposit: 1_980.370462064014808094 * 1e18,
- exchangeRate: 1.000003244027008957000258924 * 1e27
- }
- );
+ _depositTake({
+ from: _taker,
+ borrower: _borrower,
+ kicker: _lender,
+ index: _i9_91,
+ collateralArbed: 2 * 1e18,
+ quoteTokenAmount: 19.834369686871824148 * 1e18,
+ bondChange: 0.198343696868718241 * 1e18,
+ isReward: true,
+ lpAwardTaker: 0,
+ lpAwardKicker: 0.198343053438495848 * 1e18
+ });
+
+ _assertLenderLpBalance({
+ lender: _taker,
+ index: _i9_91,
+ lpBalance: 0,
+ depositTime: 0
+ });
+ _assertLenderLpBalance({
+ lender: _lender,
+ index: _i9_91,
+ lpBalance: 2_000.198343053438495848 * 1e18,
+ depositTime: _startTime + 250 days + 6.5 hours
+ });
+ _assertBucket({
+ index: _i9_91,
+ lpBalance: 2_000.198343053438495848 * 1e18,
+ collateral: 2 * 1e18,
+ deposit: 1_980.370462064014808094 * 1e18,
+ exchangeRate: 1.000003244027008957 * 1e18
+ });
// reserves should remain the same after deposit take
- _assertReserveAuction(
- {
- reserves: 288.353757763140844694 * 1e18,
- claimableReserves : 247.012735034416886695 * 1e18,
- claimableReservesRemaining: 0,
- auctionPrice: 0,
- timeRemaining: 0
- }
- );
+ _assertReserveAuction({
+ reserves: 288.353757763140844694 * 1e18,
+ claimableReserves : 247.012735034416886695 * 1e18,
+ claimableReservesRemaining: 0,
+ auctionPrice: 0,
+ timeRemaining: 0
+ });
_assertAuction(
- AuctionParams({
- borrower: _borrower,
- active: true,
- kicker: _lender,
- bondSize: 0.199398195043779403 * 1e18,
- bondFactor: 0.01 * 1e18,
- kickTime: block.timestamp - 6.5 hours,
- kickMomp: 9.818751856078723036 * 1e18,
- totalBondEscrowed: 0.199398195043779403 * 1e18,
- auctionPrice: 7.402280333270247968 * 1e18,
- debtInAuction: 1.966997287334847886 * 1e18,
- thresholdPrice: 0,
- neutralPrice: 10.468405239798418677 * 1e18
- })
+ AuctionParams({
+ borrower: _borrower,
+ active: true,
+ kicker: _lender,
+ bondSize: 0.199398195043779403 * 1e18,
+ bondFactor: 0.01 * 1e18,
+ kickTime: block.timestamp - 6.5 hours,
+ kickMomp: 9.818751856078723036 * 1e18,
+ totalBondEscrowed: 0.199398195043779403 * 1e18,
+ auctionPrice: 7.402280333270247968 * 1e18,
+ debtInAuction: 1.966997287334847886 * 1e18,
+ thresholdPrice: 0,
+ neutralPrice: 10.468405239798418677 * 1e18
+ })
);
- _assertBorrower(
- {
- borrower: _borrower,
- borrowerDebt: 1.966997287334847886 * 1e18,
- borrowerCollateral: 0,
- borrowert0Np: 10.115967548076923081 * 1e18,
- borrowerCollateralization: 0
- }
- );
+ _assertBorrower({
+ borrower: _borrower,
+ borrowerDebt: 1.966997287334847886 * 1e18,
+ borrowerCollateral: 0,
+ borrowert0Np: 10.115967548076923081 * 1e18,
+ borrowerCollateralization: 0
+ });
// deposit take should fail on an auction without any remaining collateral to auction
- _assertDepositTakeInsufficentCollateralRevert(
- {
- from: _taker,
- borrower: _borrower,
- index: _i9_91
- }
- );
+ _assertDepositTakeInsufficentCollateralRevert({
+ from: _taker,
+ borrower: _borrower,
+ index: _i9_91
+ });
}
function testDepositTakeDebtRestrict() external tearDown {
@@ -410,113 +347,92 @@ contract ERC20PoolLiquidationsDepositTakeTest is ERC20HelperContract {
skip(5 hours);
_assertAuction(
- AuctionParams({
- borrower: _borrower,
- active: true,
- kicker: _lender,
- bondSize: 0.199398195043779403 * 1e18,
- bondFactor: 0.01 * 1e18,
- kickTime: block.timestamp - 5 hours,
- kickMomp: 9.818751856078723036 * 1e18,
- totalBondEscrowed: 0.199398195043779403 * 1e18,
- auctionPrice: 20.936810479596837344 * 1e18,
- debtInAuction: 20.189067248182664592 * 1e18,
- thresholdPrice: 10.094792904825850359 * 1e18,
- neutralPrice: 10.468405239798418677 * 1e18
- })
- );
-
- _addLiquidity(
- {
- from: _lender,
- amount: 25_000 * 1e18,
- index: _i1505_26,
- lpAward: 25_000 * 1e27,
- newLup: 1_505.263728469068226832 * 1e18
- }
- );
-
- _assertBorrower(
- {
- borrower: _borrower,
- borrowerDebt: 20.189585809651700719 * 1e18,
- borrowerCollateral: 2 * 1e18,
- borrowert0Np: 10.115967548076923081 * 1e18,
- borrowerCollateralization: 149.112888462473727465 * 1e18
- }
+ AuctionParams({
+ borrower: _borrower,
+ active: true,
+ kicker: _lender,
+ bondSize: 0.199398195043779403 * 1e18,
+ bondFactor: 0.01 * 1e18,
+ kickTime: block.timestamp - 5 hours,
+ kickMomp: 9.818751856078723036 * 1e18,
+ totalBondEscrowed: 0.199398195043779403 * 1e18,
+ auctionPrice: 20.936810479596837344 * 1e18,
+ debtInAuction: 20.189067248182664592 * 1e18,
+ thresholdPrice: 10.094792904825850359 * 1e18,
+ neutralPrice: 10.468405239798418677 * 1e18
+ })
);
- _assertBucket(
- {
- index: _i1505_26,
- lpBalance: 25_000 * 1e27,
- collateral: 0.0 * 1e18,
- deposit: 25_000 * 1e18,
- exchangeRate: 1.0 * 1e27
- }
- );
+ _addLiquidity({
+ from: _lender,
+ amount: 25_000 * 1e18,
+ index: _i1505_26,
+ lpAward: 25_000 * 1e18,
+ newLup: 1_505.263728469068226832 * 1e18
+ });
+
+ _assertBorrower({
+ borrower: _borrower,
+ borrowerDebt: 20.189585809651700719 * 1e18,
+ borrowerCollateral: 2 * 1e18,
+ borrowert0Np: 10.115967548076923081 * 1e18,
+ borrowerCollateralization: 149.112888462473727465 * 1e18
+ });
+ _assertBucket({
+ index: _i1505_26,
+ lpBalance: 25_000 * 1e18,
+ collateral: 0.0 * 1e18,
+ deposit: 25_000 * 1e18,
+ exchangeRate: 1.0 * 1e18
+ });
// Amount is restricted by the debt in the loan
- _depositTake(
- {
- from: _taker,
- borrower: _borrower,
- kicker: _lender,
- index: _i1505_26,
- collateralArbed: 0.014351542794629452 * 1e18,
- quoteTokenAmount: 21.602856816327319769 * 1e18,
- bondChange: 0.199398195043779403 * 1e18,
- isReward: false,
- lpAwardTaker: 0,
- lpAwardKicker: 0
- }
- );
-
- _assertBorrower(
- {
- borrower: _borrower,
- borrowerDebt: 0,
- borrowerCollateral: 1.985648457205370548 * 1e18,
- borrowert0Np: 0,
- borrowerCollateralization: 1 * 1e18
- }
- );
-
- _assertLenderLpBalance(
- {
- lender: _taker,
- index: _i1505_26,
- lpBalance: 0,
- depositTime: 0
- }
- );
- _assertLenderLpBalance(
- {
- lender: _lender,
- index: _i1505_26,
- lpBalance: 25_000 * 1e27,
- depositTime: block.timestamp
- }
- );
- _assertBucket(
- {
- index: _i1505_26,
- lpBalance: 25_000 * 1e27,
- collateral: 0.014351542794629452 * 1e18,
- deposit: 24_978.397143183672680231 * 1e18,
- exchangeRate: 1.000000000000000000010323898 * 1e27
- }
- );
-
- _assertReserveAuction(
- {
- reserves: 288.543944498908261870 * 1e18,
- claimableReserves : 247.213075232011850972 * 1e18,
- claimableReservesRemaining: 0,
- auctionPrice: 0,
- timeRemaining: 0
- }
- );
+ _depositTake({
+ from: _taker,
+ borrower: _borrower,
+ kicker: _lender,
+ index: _i1505_26,
+ collateralArbed: 0.014351542794629452 * 1e18,
+ quoteTokenAmount: 21.602856816327319769 * 1e18,
+ bondChange: 0.199398195043779403 * 1e18,
+ isReward: false,
+ lpAwardTaker: 0,
+ lpAwardKicker: 0
+ });
+
+ _assertBorrower({
+ borrower: _borrower,
+ borrowerDebt: 0,
+ borrowerCollateral: 1.985648457205370548 * 1e18,
+ borrowert0Np: 0,
+ borrowerCollateralization: 1 * 1e18
+ });
+ _assertLenderLpBalance({
+ lender: _taker,
+ index: _i1505_26,
+ lpBalance: 0,
+ depositTime: 0
+ });
+ _assertLenderLpBalance({
+ lender: _lender,
+ index: _i1505_26,
+ lpBalance: 25_000 * 1e18,
+ depositTime: block.timestamp
+ });
+ _assertBucket({
+ index: _i1505_26,
+ lpBalance: 25_000 * 1e18,
+ collateral: 0.014351542794629452 * 1e18,
+ deposit: 24_978.397143183672680231 * 1e18,
+ exchangeRate: 1 * 1e18
+ });
+ _assertReserveAuction({
+ reserves: 288.543944498908261870 * 1e18,
+ claimableReserves : 247.213075232011850972 * 1e18,
+ claimableReservesRemaining: 0,
+ auctionPrice: 0,
+ timeRemaining: 0
+ });
}
function testDepositTakeDepositRestrict() external tearDown {
@@ -524,166 +440,127 @@ contract ERC20PoolLiquidationsDepositTakeTest is ERC20HelperContract {
skip(5 hours);
_assertAuction(
- AuctionParams({
- borrower: _borrower,
- active: true,
- kicker: _lender,
- bondSize: 0.199398195043779403 * 1e18,
- bondFactor: 0.01 * 1e18,
- kickTime: block.timestamp - 5 hours,
- kickMomp: 9.818751856078723036 * 1e18,
- totalBondEscrowed: 0.199398195043779403 * 1e18,
- auctionPrice: 20.936810479596837344 * 1e18,
- debtInAuction: 20.189067248182664592 * 1e18,
- thresholdPrice: 10.094792904825850359 * 1e18,
- neutralPrice: 10.468405239798418677 * 1e18
- })
+ AuctionParams({
+ borrower: _borrower,
+ active: true,
+ kicker: _lender,
+ bondSize: 0.199398195043779403 * 1e18,
+ bondFactor: 0.01 * 1e18,
+ kickTime: block.timestamp - 5 hours,
+ kickMomp: 9.818751856078723036 * 1e18,
+ totalBondEscrowed: 0.199398195043779403 * 1e18,
+ auctionPrice: 20.936810479596837344 * 1e18,
+ debtInAuction: 20.189067248182664592 * 1e18,
+ thresholdPrice: 10.094792904825850359 * 1e18,
+ neutralPrice: 10.468405239798418677 * 1e18
+ })
);
- _addLiquidity(
- {
- from: _lender,
- amount: 15.0 * 1e18,
- index: _i1505_26,
- lpAward: 15.0 * 1e27,
- newLup: 9.721295865031779605 * 1e18
- }
- );
+ _addLiquidity({
+ from: _lender,
+ amount: 15.0 * 1e18,
+ index: _i1505_26,
+ lpAward: 15.0 * 1e18,
+ newLup: 9.721295865031779605 * 1e18
+ });
- _assertBorrower(
- {
- borrower: _borrower,
- borrowerDebt: 20.189585809651700719 * 1e18,
- borrowerCollateral: 2 * 1e18,
- borrowert0Np: 10.115967548076923081 * 1e18,
- borrowerCollateralization: 0.963001020098637267 * 1e18
- }
- );
+ _assertBorrower({
+ borrower: _borrower,
+ borrowerDebt: 20.189585809651700719 * 1e18,
+ borrowerCollateral: 2 * 1e18,
+ borrowert0Np: 10.115967548076923081 * 1e18,
+ borrowerCollateralization: 0.963001020098637267 * 1e18
+ });
// Amount is restricted by the deposit in the bucket in the loan
- _depositTake(
- {
- from: _taker,
- borrower: _borrower,
- kicker: _lender,
- index: _i1505_26,
- collateralArbed: 0.009965031187761219 * 1e18,
- quoteTokenAmount: 15.0 * 1e18,
- bondChange: 0.15 * 1e18,
- isReward: false,
- lpAwardTaker: 0,
- lpAwardKicker: 0
- }
- );
+ _depositTake({
+ from: _taker,
+ borrower: _borrower,
+ kicker: _lender,
+ index: _i1505_26,
+ collateralArbed: 0.009965031187761219 * 1e18,
+ quoteTokenAmount: 14.999999999999999995 * 1e18,
+ bondChange: 0.15 * 1e18,
+ isReward: false,
+ lpAwardTaker: 0,
+ lpAwardKicker: 0
+ });
_assertAuction(
- AuctionParams({
- borrower: _borrower,
- active: false,
- kicker: address(0),
- bondSize: 0,
- bondFactor: 0,
- kickTime: 0,
- kickMomp: 0,
- totalBondEscrowed: 0,
- auctionPrice: 0,
- debtInAuction: 0,
- thresholdPrice: 3.317960196583009903 * 1e18,
- neutralPrice: 0
- })
- );
-
- _assertBucket(
- {
- index: _i1505_26,
- lpBalance: 15 * 1e27,
- collateral: 0.009965031187761219 * 1e18,
- deposit: 0,
- exchangeRate: 0.999999999999999999688877723 * 1e27
- }
- );
-
- _assertBorrower(
- {
- borrower: _borrower,
- borrowerDebt: 6.602856816327319769 * 1e18,
- borrowerCollateral: 1.990034968812238781 * 1e18,
- borrowert0Np: 3.417963776167124997 * 1e18,
- borrowerCollateralization: 2.929901291475173000 * 1e18
- }
- );
-
- _assertLenderLpBalance(
- {
- lender: _taker,
- index: _i1505_26,
- lpBalance: 0,
- depositTime: 0
- }
- );
-
- _assertLenderLpBalance(
- {
- lender: _lender,
- index: _i1505_26,
- lpBalance: 15.0 * 1e27,
- depositTime: block.timestamp
- }
+ AuctionParams({
+ borrower: _borrower,
+ active: false,
+ kicker: address(0),
+ bondSize: 0,
+ bondFactor: 0,
+ kickTime: 0,
+ kickMomp: 0,
+ totalBondEscrowed: 0,
+ auctionPrice: 0,
+ debtInAuction: 0,
+ thresholdPrice: 3.317960196583009903 * 1e18,
+ neutralPrice: 0
+ })
);
+ _assertBucket({
+ index: _i1505_26,
+ lpBalance: 15 * 1e18,
+ collateral: 0.009965031187761219 * 1e18,
+ deposit: 0,
+ exchangeRate: 1 * 1e18
+ });
+ _assertBorrower({
+ borrower: _borrower,
+ borrowerDebt: 6.602856816327319769 * 1e18,
+ borrowerCollateral: 1.990034968812238781 * 1e18,
+ borrowert0Np: 3.384038787324199948 * 1e18,
+ borrowerCollateralization: 2.929901291475173000 * 1e18
+ });
+ _assertLenderLpBalance({
+ lender: _taker,
+ index: _i1505_26,
+ lpBalance: 0,
+ depositTime: 0
+ });
+ _assertLenderLpBalance({
+ lender: _lender,
+ index: _i1505_26,
+ lpBalance: 15.0 * 1e18,
+ depositTime: block.timestamp
+ });
}
function testDepositTakeGTNeutralPrice() external tearDown {
skip(3 hours);
- _addLiquidity(
- {
- from: _lender,
- amount: 1_000 * 1e18,
- index: _i10016,
- lpAward: 1_000 * 1e27,
- newLup: 9.721295865031779605 * 1e18
- }
- );
-
- _assertLenderLpBalance(
- {
- lender: _taker,
- index: _i10016,
- lpBalance: 0,
- depositTime: 0
- }
- );
-
- _assertLenderLpBalance(
- {
- lender: _lender,
- index: _i10016,
- lpBalance: 1_000 * 1e27,
- depositTime: block.timestamp
- }
- );
-
- _assertBucket(
- {
- index: _i10016,
- lpBalance: 1_000 * 1e27,
- collateral: 0,
- deposit: 1_000 * 1e18,
- exchangeRate: 1.0 * 1e27
- }
- );
-
- _assertBorrower(
- {
- borrower: _borrower,
- borrowerDebt: 20.189378383465778990 * 1e18,
- borrowerCollateral: 2 * 1e18,
- borrowert0Np: 10.115967548076923081 * 1e18,
- borrowerCollateralization: 0.963010913995558897 * 1e18
- }
- );
-
+ _addLiquidity({
+ from: _lender,
+ amount: 1_000 * 1e18,
+ index: _i10016,
+ lpAward: 1_000 * 1e18,
+ newLup: 9.721295865031779605 * 1e18
+ });
+
+ _assertLenderLpBalance({
+ lender: _taker,
+ index: _i10016,
+ lpBalance: 0,
+ depositTime: 0
+ });
+ _assertLenderLpBalance({
+ lender: _lender,
+ index: _i10016,
+ lpBalance: 1_000 * 1e18,
+ depositTime: block.timestamp
+ });
+ _assertBucket({
+ index: _i10016,
+ lpBalance: 1_000 * 1e18,
+ collateral: 0,
+ deposit: 1_000 * 1e18,
+ exchangeRate: 1.0 * 1e18
+ });
_assertAuction(
AuctionParams({
borrower: _borrower,
@@ -700,74 +577,52 @@ contract ERC20PoolLiquidationsDepositTakeTest is ERC20HelperContract {
neutralPrice: 10.468405239798418677 * 1e18
})
);
-
- _assertBorrower(
- {
- borrower: _borrower,
- borrowerDebt: 20.189378383465778990 * 1e18,
- borrowerCollateral: 2 * 1e18,
- borrowert0Np: 10.115967548076923081 * 1e18,
- borrowerCollateralization: 0.963010913995558897 * 1e18
- }
- );
-
- _depositTake(
- {
- from: _taker,
- borrower: _borrower,
- kicker: _lender,
- index: _i10016,
- collateralArbed: 0.002156704581707556 * 1e18,
- quoteTokenAmount: 21.602634870308383519 * 1e18,
- bondChange: 0.199398195043779403 * 1e18,
- isReward: false,
- lpAwardTaker: 0,
- lpAwardKicker: 0
- }
- );
+ _assertBorrower({
+ borrower: _borrower,
+ borrowerDebt: 20.189378383465778990 * 1e18,
+ borrowerCollateral: 2 * 1e18,
+ borrowert0Np: 10.115967548076923081 * 1e18,
+ borrowerCollateralization: 0.963010913995558897 * 1e18
+ });
+
+ _depositTake({
+ from: _taker,
+ borrower: _borrower,
+ kicker: _lender,
+ index: _i10016,
+ collateralArbed: 0.002156704581707556 * 1e18,
+ quoteTokenAmount: 21.602634870308383519 * 1e18,
+ bondChange: 0.199398195043779403 * 1e18,
+ isReward: false,
+ lpAwardTaker: 0,
+ lpAwardKicker: 0
+ });
// deposit taker wasn't rewarded any LPBs in arbed bucket
- _assertLenderLpBalance(
- {
- lender: _taker,
- index: _i10016,
- lpBalance: 0,
- depositTime: 0
- }
- );
- _assertLenderLpBalance(
- {
- lender: _lender,
- index: _i10016,
- lpBalance: 1_000 * 1e27,
- depositTime: _startTime + 250 days + 3 hours
- }
- );
- _assertKicker(
- {
- kicker: _lender,
- claimable: 0,
- locked: 0 // kicker was penalized
- }
- );
- _assertBucket(
- {
- index: _i10016,
- lpBalance: 1_000 * 1e27, // LP balance in arbed bucket increased with LPs awarded for deposit taker
- collateral: 0.002156704581707556 * 1e18, // arbed collateral added to the arbed bucket
- deposit: 978.397365129691616481* 1e18, // quote token amount is diminished in arbed bucket
- exchangeRate: 0.999999999999999999966005802 * 1e27
- }
- );
- _assertBorrower(
- {
- borrower: _borrower,
- borrowerDebt: 0,
- borrowerCollateral: 1.997843295418292444 * 1e18,
- borrowert0Np: 0,
- borrowerCollateralization: 1 * 1e18
- }
- );
+ _assertLenderLpBalance({
+ lender: _taker,
+ index: _i10016,
+ lpBalance: 0,
+ depositTime: 0
+ });
+ _assertLenderLpBalance({
+ lender: _lender,
+ index: _i10016,
+ lpBalance: 1_000 * 1e18,
+ depositTime: _startTime + 250 days + 3 hours
+ });
+ _assertKicker({
+ kicker: _lender,
+ claimable: 0,
+ locked: 0 // kicker was penalized
+ });
+ _assertBucket({
+ index: _i10016,
+ lpBalance: 1_000 * 1e18, // LP balance in arbed bucket increased with LPs awarded for deposit taker
+ collateral: 0.002156704581707556 * 1e18, // arbed collateral added to the arbed bucket
+ deposit: 978.397365129691616481* 1e18, // quote token amount is diminished in arbed bucket
+ exchangeRate: 1 * 1e18
+ });
_assertAuction(
AuctionParams({
borrower: _borrower,
@@ -784,18 +639,24 @@ contract ERC20PoolLiquidationsDepositTakeTest is ERC20HelperContract {
neutralPrice: 0
})
);
+ _assertBorrower({
+ borrower: _borrower,
+ borrowerDebt: 0,
+ borrowerCollateral: 1.997843295418292444 * 1e18,
+ borrowert0Np: 0,
+ borrowerCollateralization: 1 * 1e18
+ });
}
function testDepositTakeReverts() external tearDown {
// should revert if auction in grace period
- _assertDepositTakeAuctionInCooldownRevert(
- {
- from: _lender,
- borrower: _borrower,
- index: _i9_91
- }
- );
+ _assertDepositTakeAuctionInCooldownRevert({
+ from: _lender,
+ borrower: _borrower,
+ index: _i9_91
+ }
+);
skip(2.5 hours);
@@ -817,36 +678,31 @@ contract ERC20PoolLiquidationsDepositTakeTest is ERC20HelperContract {
);
// should revert if bucket deposit is 0
- _assertDepositTakeAuctionInsufficientLiquidityRevert(
- {
- from: _taker,
- borrower: _borrower,
- index: _i100_33
- }
- );
+ _assertDepositTakeAuctionInsufficientLiquidityRevert({
+ from: _taker,
+ borrower: _borrower,
+ index: _i100_33
+ });
// should revert if auction price is greater than the bucket price
- _assertDepositTakeAuctionPriceGreaterThanBucketPriceRevert(
- {
- from: _taker,
- borrower: _borrower,
- index: _i9_91
- }
- );
+ _assertDepositTakeAuctionPriceGreaterThanBucketPriceRevert({
+ from: _taker,
+ borrower: _borrower,
+ index: _i9_91
+ });
skip(4 hours);
// 10 borrowers draw debt to enable the min debt check
- for (uint i=0; i<10; ++i) {
- _anonBorrowerDrawsDebt(1_000 * 1e18, 6_000 * 1e18, 7777);
- }
+ for (uint256 i=0; i<10; ++i) {
+ _anonBorrowerDrawsDebt(1_000 * 1e18, 6_000 * 1e18, MAX_FENWICK_INDEX);
+ }
+
// should revert if auction leaves borrower with debt under minimum pool debt
- _assertDepositTakeDebtUnderMinPoolDebtRevert(
- {
- from: _taker,
- borrower: _borrower,
- index: _i9_91
- }
- );
+ _assertDepositTakeDebtUnderMinPoolDebtRevert({
+ from: _taker,
+ borrower: _borrower,
+ index: _i9_91
+ });
}
}
diff --git a/tests/forge/ERC20Pool/ERC20PoolLiquidationsKick.t.sol b/tests/forge/ERC20Pool/ERC20PoolLiquidationsKick.t.sol
index 7688a6d21..160c5591f 100644
--- a/tests/forge/ERC20Pool/ERC20PoolLiquidationsKick.t.sol
+++ b/tests/forge/ERC20Pool/ERC20PoolLiquidationsKick.t.sol
@@ -11,12 +11,14 @@ contract ERC20PoolLiquidationsKickTest is ERC20HelperContract {
address internal _borrower2;
address internal _lender;
address internal _lender1;
+ address internal _withdrawRecipient;
function setUp() external {
- _borrower = makeAddr("borrower");
- _borrower2 = makeAddr("borrower2");
- _lender = makeAddr("lender");
- _lender1 = makeAddr("lender1");
+ _borrower = makeAddr("borrower");
+ _borrower2 = makeAddr("borrower2");
+ _lender = makeAddr("lender");
+ _lender1 = makeAddr("lender1");
+ _withdrawRecipient = makeAddr("withdrawRecipient");
_mintQuoteAndApproveTokens(_lender, 120_000 * 1e18);
_mintQuoteAndApproveTokens(_lender1, 120_000 * 1e18);
@@ -26,75 +28,57 @@ contract ERC20PoolLiquidationsKickTest is ERC20HelperContract {
_mintCollateralAndApproveTokens(_lender1, 4 * 1e18);
// Lender adds Quote token accross 5 prices
- _addInitialLiquidity(
- {
- from: _lender,
- amount: 2_000 * 1e18,
- index: _i9_91
- }
- );
- _addInitialLiquidity(
- {
- from: _lender,
- amount: 5_000 * 1e18,
- index: _i9_81
- }
- );
- _addInitialLiquidity(
- {
- from: _lender,
- amount: 11_000 * 1e18,
- index: _i9_72
- }
- );
- _addInitialLiquidity(
- {
- from: _lender,
- amount: 25_000 * 1e18,
- index: _i9_62
- }
- );
- _addInitialLiquidity(
- {
- from: _lender,
- amount: 30_000 * 1e18,
- index: _i9_52
- }
- );
+ _addInitialLiquidity({
+ from: _lender,
+ amount: 2_000 * 1e18,
+ index: _i9_91
+ });
+ _addInitialLiquidity({
+ from: _lender,
+ amount: 5_000 * 1e18,
+ index: _i9_81
+ });
+ _addInitialLiquidity({
+ from: _lender,
+ amount: 11_000 * 1e18,
+ index: _i9_72
+ });
+ _addInitialLiquidity({
+ from: _lender,
+ amount: 25_000 * 1e18,
+ index: _i9_62
+ });
+ _addInitialLiquidity({
+ from: _lender,
+ amount: 30_000 * 1e18,
+ index: _i9_52
+ });
// first borrower adds collateral token and borrows
- _pledgeCollateral(
- {
- from: _borrower,
- borrower: _borrower,
- amount: 2 * 1e18
- }
- );
- _borrow(
- {
- from: _borrower,
- amount: 19.25 * 1e18,
- indexLimit: _i9_91,
- newLup: 9.917184843435912074 * 1e18
- }
- );
+ _pledgeCollateral({
+ from: _borrower,
+ borrower: _borrower,
+ amount: 2 * 1e18
+ });
+ _borrow({
+ from: _borrower,
+ amount: 19.25 * 1e18,
+ indexLimit: _i9_91,
+ newLup: 9.917184843435912074 * 1e18
+ });
// second borrower adds collateral token and borrows
- _pledgeCollateral(
- {
- from: _borrower2,
- borrower: _borrower2,
- amount: 1_000 * 1e18
- }
- );
- _borrow(
- {
- from: _borrower2,
- amount: 7_980 * 1e18,
- indexLimit: _i9_72,
- newLup: 9.721295865031779605 * 1e18
- }
- );
+ _pledgeCollateral({
+ from: _borrower2,
+ borrower: _borrower2,
+ amount: 1_000 * 1e18
+ });
+ _borrow({
+ from: _borrower2,
+ amount: 7_980 * 1e18,
+ indexLimit: _i9_72,
+ newLup: 9.721295865031779605 * 1e18
+ });
/*****************************/
/*** Assert pre-kick state ***/
@@ -117,33 +101,28 @@ contract ERC20PoolLiquidationsKickTest is ERC20HelperContract {
interestRateUpdate: _startTime
})
);
- _assertBorrower(
- {
- borrower: _borrower,
- borrowerDebt: 19.268509615384615394 * 1e18,
- borrowerCollateral: 2 * 1e18,
- borrowert0Np: 10.115967548076923081 * 1e18,
- borrowerCollateralization: 1.009034539679184679 * 1e18
- }
- );
- _assertBorrower(
- {
- borrower: _borrower2,
- borrowerDebt: 7_987.673076923076926760 * 1e18,
- borrowerCollateral: 1_000 * 1e18,
- borrowert0Np: 8.471136974495192174 * 1e18,
- borrowerCollateralization: 1.217037273735858713 * 1e18
- }
- );
- _assertReserveAuction(
- {
- reserves: 7.691586538461542154 * 1e18,
- claimableReserves : 0,
- claimableReservesRemaining: 0,
- auctionPrice: 0,
- timeRemaining: 0
- }
- );
+ _assertBorrower({
+ borrower: _borrower,
+ borrowerDebt: 19.268509615384615394 * 1e18,
+ borrowerCollateral: 2 * 1e18,
+ borrowert0Np: 10.115967548076923081 * 1e18,
+ borrowerCollateralization: 1.009034539679184679 * 1e18
+ });
+ _assertBorrower({
+ borrower: _borrower2,
+ borrowerDebt: 7_987.673076923076926760 * 1e18,
+ borrowerCollateral: 1_000 * 1e18,
+ borrowert0Np: 8.471136974495192174 * 1e18,
+ borrowerCollateralization: 1.217037273735858713 * 1e18
+ });
+ _assertReserveAuction({
+ reserves: 7.691586538461542154 * 1e18,
+ claimableReserves : 0,
+ claimableReservesRemaining: 0,
+ auctionPrice: 0,
+ timeRemaining: 0
+ });
+
assertEq(_quote.balanceOf(_lender), 47_000 * 1e18);
}
@@ -168,27 +147,22 @@ contract ERC20PoolLiquidationsKickTest is ERC20HelperContract {
neutralPrice: 0
})
);
+ _assertBorrower({
+ borrower: _borrower,
+ borrowerDebt: 19.534277977147272573 * 1e18,
+ borrowerCollateral: 2 * 1e18,
+ borrowert0Np: 10.115967548076923081 * 1e18,
+ borrowerCollateralization: 0.995306391810796636 * 1e18
+ });
- _assertBorrower(
- {
- borrower: _borrower,
- borrowerDebt: 19.534277977147272573 * 1e18,
- borrowerCollateral: 2 * 1e18,
- borrowert0Np: 10.115967548076923081 * 1e18,
- borrowerCollateralization: 0.995306391810796636 * 1e18
- }
- );
-
- _kick(
- {
- from: _lender,
- borrower: _borrower,
- debt: 19.778456451861613480 * 1e18,
- collateral: 2 * 1e18,
- bond: 0.195342779771472726 * 1e18,
- transferAmount: 0.195342779771472726 * 1e18
- }
- );
+ _kick({
+ from: _lender,
+ borrower: _borrower,
+ debt: 19.778456451861613480 * 1e18,
+ collateral: 2 * 1e18,
+ bond: 0.195342779771472726 * 1e18,
+ transferAmount: 0.195342779771472726 * 1e18
+ });
/******************************/
/*** Assert Post-kick state ***/
@@ -211,25 +185,23 @@ contract ERC20PoolLiquidationsKickTest is ERC20HelperContract {
interestRateUpdate: block.timestamp
})
);
- _assertBorrower(
- {
- borrower: _borrower,
- borrowerDebt: 19.778456451861613480 * 1e18,
- borrowerCollateral: 2 * 1e18,
- borrowert0Np: 10.115967548076923081 * 1e18,
- borrowerCollateralization: 0.983018658578564579 * 1e18
- }
- );
- _assertBorrower(
- {
- borrower: _borrower2,
- borrowerDebt: 8_097.846143253778448241 * 1e18,
- borrowerCollateral: 1_000 * 1e18,
- borrowert0Np: 8.471136974495192174 * 1e18,
- borrowerCollateralization: 1.200479200648987171 * 1e18
- }
- );
+ _assertBorrower({
+ borrower: _borrower,
+ borrowerDebt: 19.778456451861613480 * 1e18,
+ borrowerCollateral: 2 * 1e18,
+ borrowert0Np: 10.115967548076923081 * 1e18,
+ borrowerCollateralization: 0.983018658578564579 * 1e18
+ });
+ _assertBorrower({
+ borrower: _borrower2,
+ borrowerDebt: 8_097.846143253778448241 * 1e18,
+ borrowerCollateral: 1_000 * 1e18,
+ borrowert0Np: 8.471136974495192174 * 1e18,
+ borrowerCollateralization: 1.200479200648987171 * 1e18
+ });
+
assertEq(_quote.balanceOf(_lender), 46_999.804657220228527274 * 1e18);
+
_assertAuction(
AuctionParams({
borrower: _borrower,
@@ -246,55 +218,44 @@ contract ERC20PoolLiquidationsKickTest is ERC20HelperContract {
neutralPrice: 10.255495938002318100 * 1e18
})
);
- _assertKicker(
- {
- kicker: _lender,
- claimable: 0,
- locked: 0.195342779771472726 * 1e18
- }
- );
- _assertReserveAuction(
- {
- reserves: 23.872320013924039720 * 1e18,
- claimableReserves : 0,
- claimableReservesRemaining: 0,
- auctionPrice: 0,
- timeRemaining: 0
- }
- );
+ _assertKicker({
+ kicker: _lender,
+ claimable: 0,
+ locked: 0.195342779771472726 * 1e18
+ });
+ _assertReserveAuction({
+ reserves: 23.872320013924039720 * 1e18,
+ claimableReserves : 0,
+ claimableReservesRemaining: 0,
+ auctionPrice: 0,
+ timeRemaining: 0
+ });
// kick should fail if borrower properly collateralized
- _assertKickCollateralizedBorrowerRevert(
- {
- from: _lender,
- borrower: _borrower2
- }
- );
+ _assertKickCollateralizedBorrowerRevert({
+ from: _lender,
+ borrower: _borrower2
+ });
- _assertDepositLockedByAuctionDebtRevert(
- {
- operator: _lender,
- amount: 100 * 1e18,
- index: _i9_91
- }
- );
+ _assertDepositLockedByAuctionDebtRevert({
+ operator: _lender,
+ amount: 100 * 1e18,
+ index: _i9_91
+ });
- // check locked pool actions if auction kicked for more than 72 hours and auction head not cleared
skip(80 hours);
- _assertRemoveLiquidityAuctionNotClearedRevert(
- {
- from: _lender,
- amount: 1_000 * 1e18,
- index: _i9_91
- }
- );
- _assertRemoveCollateralAuctionNotClearedRevert(
- {
- from: _lender,
- amount: 10 * 1e18,
- index: _i9_91
- }
- );
+
+ // check locked pool actions if auction kicked for more than 72 hours and auction head not cleared
+ _assertRemoveLiquidityAuctionNotClearedRevert({
+ from: _lender,
+ amount: 1_000 * 1e18,
+ index: _i9_91
+ });
+ _assertRemoveCollateralAuctionNotClearedRevert({
+ from: _lender,
+ amount: 10 * 1e18,
+ index: _i9_91
+ });
}
function testKickAndSaveByRepay() external tearDown {
@@ -318,27 +279,23 @@ contract ERC20PoolLiquidationsKickTest is ERC20HelperContract {
neutralPrice: 0
})
);
+ _assertBorrower({
+ borrower: _borrower,
+ borrowerDebt: 19.534277977147272573 * 1e18,
+ borrowerCollateral: 2 * 1e18,
+ borrowert0Np: 10.115967548076923081 * 1e18,
+ borrowerCollateralization: 0.995306391810796636 * 1e18
+ });
- _assertBorrower(
- {
- borrower: _borrower,
- borrowerDebt: 19.534277977147272573 * 1e18,
- borrowerCollateral: 2 * 1e18,
- borrowert0Np: 10.115967548076923081 * 1e18,
- borrowerCollateralization: 0.995306391810796636 * 1e18
- }
- );
+ _kick({
+ from: _lender,
+ borrower: _borrower,
+ debt: 19.778456451861613480 * 1e18,
+ collateral: 2 * 1e18,
+ bond: 0.195342779771472726 * 1e18,
+ transferAmount: 0.195342779771472726 * 1e18
+ });
- _kick(
- {
- from: _lender,
- borrower: _borrower,
- debt: 19.778456451861613480 * 1e18,
- collateral: 2 * 1e18,
- bond: 0.195342779771472726 * 1e18,
- transferAmount: 0.195342779771472726 * 1e18
- }
- );
_assertAuction(
AuctionParams({
borrower: _borrower,
@@ -355,25 +312,21 @@ contract ERC20PoolLiquidationsKickTest is ERC20HelperContract {
neutralPrice: 10.255495938002318100 * 1e18
})
);
+ _assertKicker({
+ kicker: _lender,
+ claimable: 0,
+ locked: 0.195342779771472726 * 1e18
+ });
- _assertKicker(
- {
- kicker: _lender,
- claimable: 0,
- locked: 0.195342779771472726 * 1e18
- }
- );
+ _repayAndSettleAuction({
+ from: _borrower,
+ borrower: _borrower,
+ amount: 2 * 1e18,
+ repaid: 2 * 1e18,
+ collateral: 2 * 1e18,
+ newLup: 9.721295865031779605 * 1e18
+ });
- _repayAndSettleAuction(
- {
- from: _borrower,
- borrower: _borrower,
- amount: 2 * 1e18,
- repaid: 2 * 1e18,
- collateral: 2 * 1e18,
- newLup: 9.721295865031779605 * 1e18
- }
- );
_assertAuction(
AuctionParams({
borrower: _borrower,
@@ -390,40 +343,35 @@ contract ERC20PoolLiquidationsKickTest is ERC20HelperContract {
neutralPrice: 0
})
);
- _assertKicker(
- {
- kicker: _lender,
- claimable: 0.195342779771472726 * 1e18,
- locked: 0
- }
- );
+ _assertKicker({
+ kicker: _lender,
+ claimable: 0.195342779771472726 * 1e18,
+ locked: 0
+ });
// Skip to make borrower undercollateralized again
skip(750 days);
- _assertBorrower(
- {
- borrower: _borrower,
- borrowerDebt: 19.500754673204780610 * 1e18,
- borrowerCollateral: 2 * 1e18,
- borrowert0Np: 9.347497433934260033 * 1e18,
- borrowerCollateralization: 0.997017400397270737 * 1e18
- }
- );
+ _assertBorrower({
+ borrower: _borrower,
+ borrowerDebt: 19.500754673204780610 * 1e18,
+ borrowerCollateral: 2 * 1e18,
+ borrowert0Np: 9.254718877190426162 * 1e18,
+ borrowerCollateralization: 0.997017400397270737 * 1e18
+ });
// Kick method only emit Kick event and doesn't call transfer method when kicker has enough bond amount in claimable
- _kick(
- {
- from: _lender,
- borrower: _borrower,
- debt: 19.720138163278334392 * 1e18,
- collateral: 2 * 1e18,
- bond: 0.195007546732047806 * 1e18,
- transferAmount: 0
- }
- );
+ _kick({
+ from: _lender,
+ borrower: _borrower,
+ debt: 19.720138163278334392 * 1e18,
+ collateral: 2 * 1e18,
+ bond: 0.195007546732047806 * 1e18,
+ transferAmount: 0
+ });
uint256 snapshot = vm.snapshot();
+
// kicker not saved if partial debt paid only
_repayDebt({
from: _borrower,
@@ -444,12 +392,13 @@ contract ERC20PoolLiquidationsKickTest is ERC20HelperContract {
kickTime: _startTime + 850 days,
kickMomp: 9.818751856078723036 * 1e18,
totalBondEscrowed: 0.195007546732047806 * 1e18,
- auctionPrice: 332.622741621515951584 * 1e18,
+ auctionPrice: 329.321295632797165376 * 1e18,
debtInAuction: 19.720038163278334392 * 1e18,
thresholdPrice: 9.860019081639167196 * 1e18,
- neutralPrice: 10.394460675672373487 * 1e18
+ neutralPrice: 10.291290488524911418 * 1e18
})
);
+
vm.revertTo(snapshot);
// kicker saved if enough debt paid
@@ -479,18 +428,31 @@ contract ERC20PoolLiquidationsKickTest is ERC20HelperContract {
})
);
- // kicker withdraws his auction bonds
+ // kicker balance befor withdraw auction bonds
+ assertEq(_quote.balanceOf(_lender), 46_999.804657220228527274 * 1e18);
+
+ snapshot = vm.snapshot();
+
changePrank(_lender);
+
+ // kicker withdraws auction bonds and transfer to a different address
+ _pool.withdrawBonds(_withdrawRecipient);
+
+ assertEq(_quote.balanceOf(_withdrawRecipient), 0.195342779771472726 * 1e18);
assertEq(_quote.balanceOf(_lender), 46_999.804657220228527274 * 1e18);
- _pool.withdrawBonds();
+
+ vm.revertTo(snapshot);
+
+ // kicker withdraws auction bonds
+ _pool.withdrawBonds(_lender);
+
assertEq(_quote.balanceOf(_lender), 47_000 * 1e18);
- _assertKicker(
- {
- kicker: _lender,
- claimable: 0,
- locked: 0
- }
- );
+
+ _assertKicker({
+ kicker: _lender,
+ claimable: 0,
+ locked: 0
+ });
}
function testKickAndSaveByPledgeCollateral() external tearDown {
@@ -514,27 +476,23 @@ contract ERC20PoolLiquidationsKickTest is ERC20HelperContract {
neutralPrice: 0
})
);
+ _assertBorrower({
+ borrower: _borrower,
+ borrowerDebt: 19.534277977147272573 * 1e18,
+ borrowerCollateral: 2 * 1e18,
+ borrowert0Np: 10.115967548076923081 * 1e18,
+ borrowerCollateralization: 0.995306391810796636 * 1e18
+ });
- _assertBorrower(
- {
- borrower: _borrower,
- borrowerDebt: 19.534277977147272573 * 1e18,
- borrowerCollateral: 2 * 1e18,
- borrowert0Np: 10.115967548076923081 * 1e18,
- borrowerCollateralization: 0.995306391810796636 * 1e18
- }
- );
+ _kick({
+ from: _lender,
+ borrower: _borrower,
+ debt: 19.778456451861613480 * 1e18,
+ collateral: 2 * 1e18,
+ bond: 0.195342779771472726 * 1e18,
+ transferAmount: 0.195342779771472726 * 1e18
+ });
- _kick(
- {
- from: _lender,
- borrower: _borrower,
- debt: 19.778456451861613480 * 1e18,
- collateral: 2 * 1e18,
- bond: 0.195342779771472726 * 1e18,
- transferAmount: 0.195342779771472726 * 1e18
- }
- );
_assertAuction(
AuctionParams({
borrower: _borrower,
@@ -551,22 +509,19 @@ contract ERC20PoolLiquidationsKickTest is ERC20HelperContract {
neutralPrice: 10.255495938002318100 * 1e18
})
);
- _assertKicker(
- {
- kicker: _lender,
- claimable: 0,
- locked: 0.195342779771472726 * 1e18
- }
- );
+ _assertKicker({
+ kicker: _lender,
+ claimable: 0,
+ locked: 0.195342779771472726 * 1e18
+ });
+
+ _pledgeCollateralAndSettleAuction({
+ from: _borrower,
+ borrower: _borrower,
+ amount: 2 * 1e18,
+ collateral: 4 * 1e18 // collateral after auction settled = 2 new pledged + initial 2 collateral pledged
+ });
- _pledgeCollateralAndSettleAuction(
- {
- from: _borrower,
- borrower: _borrower,
- amount: 2 * 1e18,
- collateral: 4 * 1e18 // collateral after auction settled = 2 new pledged + initial 2 collateral pledged
- }
- );
_assertAuction(
AuctionParams({
borrower: _borrower,
@@ -583,26 +538,25 @@ contract ERC20PoolLiquidationsKickTest is ERC20HelperContract {
neutralPrice: 0
})
);
- _assertKicker(
- {
- kicker: _lender,
- claimable: 0.195342779771472726 * 1e18,
- locked: 0
- }
- );
+ _assertKicker({
+ kicker: _lender,
+ claimable: 0.195342779771472726 * 1e18,
+ locked: 0
+ });
// kicker withdraws his auction bonds
changePrank(_lender);
assertEq(_quote.balanceOf(_lender), 46_999.804657220228527274 * 1e18);
- _pool.withdrawBonds();
+
+ _pool.withdrawBonds(_lender);
+
assertEq(_quote.balanceOf(_lender), 47_000 * 1e18);
- _assertKicker(
- {
- kicker: _lender,
- claimable: 0,
- locked: 0
- }
- );
+
+ _assertKicker({
+ kicker: _lender,
+ claimable: 0,
+ locked: 0
+ });
}
function testKickActiveAuctionReverts() external tearDown {
@@ -626,27 +580,23 @@ contract ERC20PoolLiquidationsKickTest is ERC20HelperContract {
neutralPrice: 0
})
);
+ _assertBorrower({
+ borrower: _borrower,
+ borrowerDebt: 19.534277977147272573 * 1e18,
+ borrowerCollateral: 2 * 1e18,
+ borrowert0Np: 10.115967548076923081 * 1e18,
+ borrowerCollateralization: 0.995306391810796636 * 1e18
+ });
- _assertBorrower(
- {
- borrower: _borrower,
- borrowerDebt: 19.534277977147272573 * 1e18,
- borrowerCollateral: 2 * 1e18,
- borrowert0Np: 10.115967548076923081 * 1e18,
- borrowerCollateralization: 0.995306391810796636 * 1e18
- }
- );
+ _kick({
+ from: _lender,
+ borrower: _borrower,
+ debt: 19.778456451861613480 * 1e18,
+ collateral: 2 * 1e18,
+ bond: 0.195342779771472726 * 1e18,
+ transferAmount: 0.195342779771472726 * 1e18
+ });
- _kick(
- {
- from: _lender,
- borrower: _borrower,
- debt: 19.778456451861613480 * 1e18,
- collateral: 2 * 1e18,
- bond: 0.195342779771472726 * 1e18,
- transferAmount: 0.195342779771472726 * 1e18
- }
- );
_assertAuction(
AuctionParams({
borrower: _borrower,
@@ -665,92 +615,76 @@ contract ERC20PoolLiquidationsKickTest is ERC20HelperContract {
);
// should not allow borrower to draw more debt if auction kicked
- _assertBorrowAuctionActiveRevert(
- {
- from: _borrower,
- amount: 1 * 1e18,
- indexLimit: 7000
- }
- );
+ _assertBorrowAuctionActiveRevert({
+ from: _borrower,
+ amount: 1 * 1e18,
+ indexLimit: 7000
+ });
}
function testInterestsAccumulationWithAllLoansAuctioned() external tearDown {
// Borrower2 borrows
- _borrow(
- {
- from: _borrower2,
- amount: 1_730 * 1e18,
- indexLimit: _i9_72,
- newLup: 9.721295865031779605 * 1e18
- }
- );
+ _borrow({
+ from: _borrower2,
+ amount: 1_730 * 1e18,
+ indexLimit: _i9_72,
+ newLup: 9.721295865031779605 * 1e18
+ });
// Skip to make borrower undercollateralized
skip(100 days);
- _assertBorrower(
- {
- borrower: _borrower,
- borrowerDebt: 19.534277977147272573 * 1e18,
- borrowerCollateral: 2 * 1e18,
- borrowert0Np: 10.115967548076923081 * 1e18,
- borrowerCollateralization: 0.995306391810796636 * 1e18
- }
- );
- _assertBorrower(
- {
- borrower: _borrower2,
- borrowerDebt: 9_853.394241979221645666 * 1e18,
- borrowerCollateral: 1_000 * 1e18,
- borrowert0Np: 10.307611531622595991 * 1e18,
- borrowerCollateralization: 0.986593617011217057 * 1e18
- }
- );
- _assertLoans(
- {
- noOfLoans: 2,
- maxBorrower: _borrower2,
- maxThresholdPrice: 9.719336538461538466 * 1e18
- }
- );
+
+ _assertBorrower({
+ borrower: _borrower,
+ borrowerDebt: 19.534277977147272573 * 1e18,
+ borrowerCollateral: 2 * 1e18,
+ borrowert0Np: 10.115967548076923081 * 1e18,
+ borrowerCollateralization: 0.995306391810796636 * 1e18
+ });
+ _assertBorrower({
+ borrower: _borrower2,
+ borrowerDebt: 9_853.394241979221645666 * 1e18,
+ borrowerCollateral: 1_000 * 1e18,
+ borrowert0Np: 10.307611531622595991 * 1e18,
+ borrowerCollateralization: 0.986593617011217057 * 1e18
+ });
+ _assertLoans({
+ noOfLoans: 2,
+ maxBorrower: _borrower2,
+ maxThresholdPrice: 9.719336538461538466 * 1e18
+ });
// kick first loan
- _kick(
- {
- from: _lender,
- borrower: _borrower2,
- debt: 9_976.561670003961916237 * 1e18,
- collateral: 1_000 * 1e18,
- bond: 98.533942419792216457 * 1e18,
- transferAmount: 98.533942419792216457 * 1e18
- }
- );
- _assertLoans(
- {
- noOfLoans: 1,
- maxBorrower: _borrower,
- maxThresholdPrice: 9.767138988573636287 * 1e18
- }
- );
+ _kick({
+ from: _lender,
+ borrower: _borrower2,
+ debt: 9_976.561670003961916237 * 1e18,
+ collateral: 1_000 * 1e18,
+ bond: 98.533942419792216457 * 1e18,
+ transferAmount: 98.533942419792216457 * 1e18
+ });
+
+ _assertLoans({
+ noOfLoans: 1,
+ maxBorrower: _borrower,
+ maxThresholdPrice: 9.767138988573636287 * 1e18
+ });
// kick 2nd loan
- _kick(
- {
- from: _lender,
- borrower: _borrower,
- debt: 19.754038604390179389 * 1e18,
- collateral: 2 * 1e18,
- bond: 0.195342779771472726 * 1e18,
- transferAmount: 0.195342779771472726 * 1e18
- }
- );
- _assertLoans(
- {
- noOfLoans: 0,
- maxBorrower: address(0),
- maxThresholdPrice: 0
- }
- );
+ _kick({
+ from: _lender,
+ borrower: _borrower,
+ debt: 19.754038604390179389 * 1e18,
+ collateral: 2 * 1e18,
+ bond: 0.195342779771472726 * 1e18,
+ transferAmount: 0.195342779771472726 * 1e18
+ });
+ _assertLoans({
+ noOfLoans: 0,
+ maxBorrower: address(0),
+ maxThresholdPrice: 0
+ });
_assertPool(
PoolParams({
htp: 0,
@@ -772,15 +706,13 @@ contract ERC20PoolLiquidationsKickTest is ERC20HelperContract {
// force pool interest accumulation
skip(14 hours);
- _addLiquidity(
- {
- from: _lender1,
- amount: 1 * 1e18,
- index: _i9_91,
- lpAward: 0.943930837199358257319707317 * 1e27,
- newLup: 9.721295865031779605 * 1e18
- }
- );
+ _addLiquidity({
+ from: _lender1,
+ amount: 1 * 1e18,
+ index: _i9_91,
+ lpAward: 0.943930837199358257 * 1e18,
+ newLup: 9.721295865031779605 * 1e18
+ });
_assertPool(
PoolParams({
diff --git a/tests/forge/ERC20Pool/ERC20PoolLiquidationsKickWithDeposit.t.sol b/tests/forge/ERC20Pool/ERC20PoolLiquidationsKickWithDeposit.t.sol
index 7480b89e5..6bcd439fc 100644
--- a/tests/forge/ERC20Pool/ERC20PoolLiquidationsKickWithDeposit.t.sol
+++ b/tests/forge/ERC20Pool/ERC20PoolLiquidationsKickWithDeposit.t.sol
@@ -42,90 +42,72 @@ contract ERC20PoolLiquidationsKickWithDepositTest is ERC20HelperContract {
_mintCollateralAndApproveTokens(_borrower5, 1_000 * 1e18);
// Lender 1 adds Quote token accross 2 buckets
- _addInitialLiquidity(
- {
- from: _lender1,
- amount: 50_000 * 1e18,
- index: 2500
- }
- );
- _addInitialLiquidity(
- {
- from: _lender1,
- amount: 50_000 * 1e18,
- index: 2501
- }
- );
- _addInitialLiquidity(
- {
- from: _lender1,
- amount: 1_000 * 1e18,
- index: 2502
- }
- );
+ _addInitialLiquidity({
+ from: _lender1,
+ amount: 50_000 * 1e18,
+ index: 2500
+ });
+ _addInitialLiquidity({
+ from: _lender1,
+ amount: 50_000 * 1e18,
+ index: 2501
+ });
+ _addInitialLiquidity({
+ from: _lender1,
+ amount: 1_000 * 1e18,
+ index: 2502
+ });
// all 5 borrowers draw debt from pool
- _drawDebt(
- {
- from: _borrower1,
- borrower: _borrower1,
- amountToBorrow: 20_000 * 1e18,
- limitIndex: 5000,
- collateralToPledge: 1_000 * 1e18,
- newLup: 3_863.654368867279344664 * 1e18
- }
- );
- _drawDebt(
- {
- from: _borrower2,
- borrower: _borrower2,
- amountToBorrow: 20_000 * 1e18,
- limitIndex: 5000,
- collateralToPledge: 1_000 * 1e18,
- newLup: 3_863.654368867279344664 * 1e18
- }
- );
- _drawDebt(
- {
- from: _borrower3,
- borrower: _borrower3,
- amountToBorrow: 20_000 * 1e18,
- limitIndex: 5000,
- collateralToPledge: 1_000 * 1e18,
- newLup: 3_844.432207828138682757 * 1e18
- }
- );
- _drawDebt(
- {
- from: _borrower4,
- borrower: _borrower4,
- amountToBorrow: 20_000 * 1e18,
- limitIndex: 5000,
- collateralToPledge: 1_000 * 1e18,
- newLup: 3_844.432207828138682757 * 1e18
- }
- );
- _drawDebt(
- {
- from: _borrower5,
- borrower: _borrower5,
- amountToBorrow: 20_000 * 1e18,
- limitIndex: 5000,
- collateralToPledge: 1_000 * 1e18,
- newLup: 3_825.305679430983794766 * 1e18
- }
- );
+ _drawDebt({
+ from: _borrower1,
+ borrower: _borrower1,
+ amountToBorrow: 20_000 * 1e18,
+ limitIndex: 5000,
+ collateralToPledge: 1_000 * 1e18,
+ newLup: 3_863.654368867279344664 * 1e18
+ });
+ _drawDebt({
+ from: _borrower2,
+ borrower: _borrower2,
+ amountToBorrow: 20_000 * 1e18,
+ limitIndex: 5000,
+ collateralToPledge: 1_000 * 1e18,
+ newLup: 3_863.654368867279344664 * 1e18
+ });
+ _drawDebt({
+ from: _borrower3,
+ borrower: _borrower3,
+ amountToBorrow: 20_000 * 1e18,
+ limitIndex: 5000,
+ collateralToPledge: 1_000 * 1e18,
+ newLup: 3_844.432207828138682757 * 1e18
+ });
+ _drawDebt({
+ from: _borrower4,
+ borrower: _borrower4,
+ amountToBorrow: 20_000 * 1e18,
+ limitIndex: 5000,
+ collateralToPledge: 1_000 * 1e18,
+ newLup: 3_844.432207828138682757 * 1e18
+ });
+ _drawDebt({
+ from: _borrower5,
+ borrower: _borrower5,
+ amountToBorrow: 20_000 * 1e18,
+ limitIndex: 5000,
+ collateralToPledge: 1_000 * 1e18,
+ newLup: 3_825.305679430983794766 * 1e18
+ });
// Lender 2 adds Quote token to top bucket
- _addLiquidity(
- {
- from: _lender2,
- amount: 10_000 * 1e18,
- index: 2500,
- lpAward: 10_000 * 1e27,
- newLup: 3_844.432207828138682757 * 1e18
- }
- );
+ _addLiquidity({
+ from: _lender2,
+ amount: 10_000 * 1e18,
+ index: 2500,
+ lpAward: 10_000 * 1e18,
+ newLup: 3_844.432207828138682757 * 1e18
+ });
/*****************************/
/*** Assert pre-kick state ***/
@@ -148,6 +130,7 @@ contract ERC20PoolLiquidationsKickWithDepositTest is ERC20HelperContract {
interestRateUpdate: _startTime
})
);
+
// assert balances
assertEq(_quote.balanceOf(address(_pool)), 11_000 * 1e18);
assertEq(_quote.balanceOf(_lender1), 49_000 * 1e18);
@@ -159,13 +142,11 @@ contract ERC20PoolLiquidationsKickWithDepositTest is ERC20HelperContract {
assertEq(_quote.balanceOf(_borrower5), 20_000 * 1e18);
// assert lender cannot remove desired amount of 15000 quote tokens as LUP moves below HTP
- _assertRemoveLiquidityLupBelowHtpRevert(
- {
- from: _lender1,
- amount: 15_000 * 1e18,
- index: 2500
- }
- );
+ _assertRemoveLiquidityLupBelowHtpRevert({
+ from: _lender1,
+ amount: 15_000 * 1e18,
+ index: 2500
+ });
}
function testKickWithDepositAmountHigherThanAuctionBond() external tearDown {
@@ -176,29 +157,25 @@ contract ERC20PoolLiquidationsKickWithDepositTest is ERC20HelperContract {
*/
// assert bucket state pre kick with deposit
- _assertBucket(
- {
- index: 2500,
- lpBalance: 60_000 * 1e27,
- collateral: 0,
- deposit: 60_000 * 1e18,
- exchangeRate: 1 * 1e27
- }
- );
+ _assertBucket({
+ index: 2500,
+ lpBalance: 60_000 * 1e18,
+ collateral: 0,
+ deposit: 60_000 * 1e18,
+ exchangeRate: 1 * 1e18
+ });
- _kickWithDeposit(
- {
- from: _lender1,
- index: 2500,
- borrower: _borrower1,
- debt: 20_269.471153846153855500 * 1e18,
- collateral: 1_000 * 1e18,
- bond: 6_005.769230769230772000 * 1e18,
- removedFromDeposit: 6_005.769230769230772000 * 1e18,
- transferAmount: 0,
- lup: 3_844.432207828138682757 * 1e18
- }
- );
+ _kickWithDeposit({
+ from: _lender1,
+ index: 2500,
+ borrower: _borrower1,
+ debt: 20_269.471153846153855500 * 1e18,
+ collateral: 1_000 * 1e18,
+ bond: 6_005.769230769230772000 * 1e18,
+ removedFromDeposit: 6_005.769230769230772000 * 1e18,
+ transferAmount: 0,
+ lup: 3_844.432207828138682757 * 1e18
+ });
/******************************/
/*** Assert post-kick state ***/
@@ -221,6 +198,7 @@ contract ERC20PoolLiquidationsKickWithDepositTest is ERC20HelperContract {
interestRateUpdate: _startTime
})
);
+
// assert balances - no change, bond was covered from deposit
assertEq(_quote.balanceOf(address(_pool)), 11_000 * 1e18);
assertEq(_quote.balanceOf(_lender1), 49_000 * 1e18);
@@ -230,41 +208,34 @@ contract ERC20PoolLiquidationsKickWithDepositTest is ERC20HelperContract {
assertEq(_quote.balanceOf(_borrower3), 20_000 * 1e18);
assertEq(_quote.balanceOf(_borrower4), 20_000 * 1e18);
assertEq(_quote.balanceOf(_borrower5), 20_000 * 1e18);
- // assert lenders LPs in bucket used
- _assertLenderLpBalance(
- {
- lender: _lender1,
- index: 2500,
- lpBalance: 43_994.230769230769228 * 1e27, // reduced by amount used to cover auction bond
- depositTime: _startTime
- }
- );
- _assertLenderLpBalance(
- {
- lender: _lender2,
- index: 2500,
- lpBalance: 10_000 * 1e27,
- depositTime: _startTime
- }
- );
+
+ // assert lenders LPs in bucket used to kick
+ _assertLenderLpBalance({
+ lender: _lender1,
+ index: 2500,
+ lpBalance: 43_994.230769230769228 * 1e18, // reduced by amount used to cover auction bond
+ depositTime: _startTime
+ });
+ _assertLenderLpBalance({
+ lender: _lender2,
+ index: 2500,
+ lpBalance: 10_000 * 1e18,
+ depositTime: _startTime
+ });
// assert bucket LPs
- _assertBucket(
- {
- index: 2500,
- lpBalance: 53_994.230769230769228 * 1e27, // reduced by amount used to cover auction bond
- collateral: 0,
- deposit: 53_994.230769230769228000 * 1e18, // reduced by amount used to cover auction bond
- exchangeRate: 1 * 1e27
- }
- );
+ _assertBucket({
+ index: 2500,
+ lpBalance: 53_994.230769230769228 * 1e18, // reduced by amount used to cover auction bond
+ collateral: 0,
+ deposit: 53_994.230769230769228000 * 1e18, // reduced by amount used to cover auction bond
+ exchangeRate: 1 * 1e18
+ });
// assert lender1 as a kicker
- _assertKicker(
- {
- kicker: _lender1,
- claimable: 0,
- locked: 6_005.769230769230772000 * 1e18
- }
- );
+ _assertKicker({
+ kicker: _lender1,
+ claimable: 0,
+ locked: 6_005.769230769230772000 * 1e18
+ });
// assert kicked auction
_assertAuction(
AuctionParams({
@@ -292,26 +263,23 @@ contract ERC20PoolLiquidationsKickWithDepositTest is ERC20HelperContract {
*/
// borrower 1 draws more debt from pool, bond size will increase from 6_005.769230769230772000 in prev scenario to 8_708.365384615384619400
- _drawDebt(
- {
- from: _borrower1,
- borrower: _borrower1,
- amountToBorrow: 9_000 * 1e18,
- limitIndex: 5000,
- collateralToPledge: 0,
- newLup: 3_844.432207828138682757 * 1e18
- }
- );
+ _drawDebt({
+ from: _borrower1,
+ borrower: _borrower1,
+ amountToBorrow: 9_000 * 1e18,
+ limitIndex: 5000,
+ collateralToPledge: 0,
+ newLup: 3_844.432207828138682757 * 1e18
+ });
// Lender 3 adds collateral to top bucket
- _addCollateral(
- {
- from: _lender3,
- amount: 1 * 1e18,
- index: 2500,
- lpAward: 3_863.654368867279344664 * 1e27 // less than bond size
- }
- );
+ _addCollateral({
+ from: _lender3,
+ amount: 1 * 1e18,
+ index: 2500,
+ lpAward: 3_863.654368867279344664 * 1e18 // less than bond size
+ });
+
// assert balances
assertEq(_quote.balanceOf(address(_pool)), 2_000 * 1e18);
assertEq(_quote.balanceOf(_lender1), 49_000 * 1e18);
@@ -323,19 +291,17 @@ contract ERC20PoolLiquidationsKickWithDepositTest is ERC20HelperContract {
assertEq(_quote.balanceOf(_borrower4), 20_000 * 1e18);
assertEq(_quote.balanceOf(_borrower5), 20_000 * 1e18);
- _kickWithDeposit(
- {
- from: _lender3,
- index: 2500,
- borrower: _borrower1,
- debt: 29_390.733173076923090475 * 1e18,
- collateral: 1_000 * 1e18,
- bond: 8_708.365384615384619400 * 1e18,
- removedFromDeposit: 3_863.654368867279344664 * 1e18,
- transferAmount: 4_844.711015748105274736 * 1e18,
- lup: 99836282890
- }
- );
+ _kickWithDeposit({
+ from: _lender3,
+ index: 2500,
+ borrower: _borrower1,
+ debt: 29_390.733173076923090475 * 1e18,
+ collateral: 1_000 * 1e18,
+ bond: 8_708.365384615384619400 * 1e18,
+ removedFromDeposit: 3_863.654368867279344664 * 1e18,
+ transferAmount: 4_844.711015748105274736 * 1e18,
+ lup: 99836282890
+ });
/******************************/
/*** Assert post-kick state ***/
@@ -358,6 +324,7 @@ contract ERC20PoolLiquidationsKickWithDepositTest is ERC20HelperContract {
interestRateUpdate: _startTime
})
);
+
// assert balances
assertEq(_quote.balanceOf(address(_pool)), 6_844.711015748105274736 * 1e18); // increased with the amount sent to cover bond
assertEq(_quote.balanceOf(_lender1), 49_000 * 1e18);
@@ -368,41 +335,34 @@ contract ERC20PoolLiquidationsKickWithDepositTest is ERC20HelperContract {
assertEq(_quote.balanceOf(_borrower3), 20_000 * 1e18);
assertEq(_quote.balanceOf(_borrower4), 20_000 * 1e18);
assertEq(_quote.balanceOf(_borrower5), 20_000 * 1e18);
+
// assert lenders LPs in bucket used
- _assertLenderLpBalance(
- {
- lender: _lender1,
- index: 2500,
- lpBalance: 50_000 * 1e27,
- depositTime: _startTime
- }
- );
- _assertLenderLpBalance(
- {
- lender: _lender3,
- index: 2500,
- lpBalance: 0,
- depositTime: _startTime
- }
- );
+ _assertLenderLpBalance({
+ lender: _lender1,
+ index: 2500,
+ lpBalance: 50_000 * 1e18,
+ depositTime: _startTime
+ });
+ _assertLenderLpBalance({
+ lender: _lender3,
+ index: 2500,
+ lpBalance: 0,
+ depositTime: _startTime
+ });
// assert bucket LPs
- _assertBucket(
- {
- index: 2500,
- lpBalance: 60_000 * 1e27,
- collateral: 1 * 1e18,
- deposit: 56_136.345631132720655336 * 1e18,
- exchangeRate: 1 * 1e27
- }
- );
+ _assertBucket({
+ index: 2500,
+ lpBalance: 60_000 * 1e18,
+ collateral: 1 * 1e18,
+ deposit: 56_136.345631132720655336 * 1e18,
+ exchangeRate: 1 * 1e18
+ });
// assert lender3 as a kicker
- _assertKicker(
- {
- kicker: _lender3,
- claimable: 0,
- locked: 8_708.365384615384619400 * 1e18
- }
- );
+ _assertKicker({
+ kicker: _lender3,
+ claimable: 0,
+ locked: 8_708.365384615384619400 * 1e18
+ });
// assert kicked auction
_assertAuction(
AuctionParams({
@@ -429,26 +389,23 @@ contract ERC20PoolLiquidationsKickWithDepositTest is ERC20HelperContract {
*/
// lender 2 adds liquidity in new top bucket 2499
- _addLiquidity(
- {
- from: _lender2,
- amount: 10_000 * 1e18,
- index: 2499,
- lpAward: 10_000 * 1e27,
- newLup: 3_844.432207828138682757 * 1e18
- }
- );
+ _addLiquidity({
+ from: _lender2,
+ amount: 10_000 * 1e18,
+ index: 2499,
+ lpAward: 10_000 * 1e18,
+ newLup: 3_844.432207828138682757 * 1e18
+ });
+
// borrower draws more debt consuming entire deposit from bucket 2499
- _drawDebt(
- {
- from: _borrower1,
- borrower: _borrower1,
- amountToBorrow: 15_000 * 1e18,
- limitIndex: 5000,
- collateralToPledge: 0,
- newLup: 3_844.432207828138682757 * 1e18
- }
- );
+ _drawDebt({
+ from: _borrower1,
+ borrower: _borrower1,
+ amountToBorrow: 15_000 * 1e18,
+ limitIndex: 5000,
+ collateralToPledge: 0,
+ newLup: 3_844.432207828138682757 * 1e18
+ });
// assert balances
assertEq(_quote.balanceOf(address(_pool)), 6_000 * 1e18);
@@ -462,19 +419,17 @@ contract ERC20PoolLiquidationsKickWithDepositTest is ERC20HelperContract {
assertEq(_quote.balanceOf(_borrower5), 20_000 * 1e18);
// lender 2 kicks using all LPs from bucket 2499 (10_000) and sending additional quote tokens to cover auction bond (510.096153846153851000)
- _kickWithDeposit(
- {
- from: _lender2,
- index: 2499,
- borrower: _borrower1,
- debt: 35_471.574519230769247125 * 1e18,
- collateral: 1_000 * 1e18,
- bond: 10_510.096153846153851000 * 1e18,
- removedFromDeposit: 10_000 * 1e18,
- transferAmount: 510.096153846153851000 * 1e18,
- lup: 99836282890
- }
- );
+ _kickWithDeposit({
+ from: _lender2,
+ index: 2499,
+ borrower: _borrower1,
+ debt: 35_471.574519230769247125 * 1e18,
+ collateral: 1_000 * 1e18,
+ bond: 10_510.096153846153851000 * 1e18,
+ removedFromDeposit: 10_000 * 1e18,
+ transferAmount: 510.096153846153851000 * 1e18,
+ lup: 99836282890
+ });
/******************************/
/*** Assert post-kick state ***/
@@ -497,6 +452,7 @@ contract ERC20PoolLiquidationsKickWithDepositTest is ERC20HelperContract {
interestRateUpdate: _startTime
})
);
+
// assert balances
assertEq(_quote.balanceOf(address(_pool)), 6_510.096153846153851000 * 1e18); // increased with the amount sent to cover bond
assertEq(_quote.balanceOf(_lender1), 49_000 * 1e18);
@@ -507,33 +463,28 @@ contract ERC20PoolLiquidationsKickWithDepositTest is ERC20HelperContract {
assertEq(_quote.balanceOf(_borrower3), 20_000 * 1e18);
assertEq(_quote.balanceOf(_borrower4), 20_000 * 1e18);
assertEq(_quote.balanceOf(_borrower5), 20_000 * 1e18);
+
// assert lenders LPs in bucket used
- _assertLenderLpBalance(
- {
- lender: _lender2,
- index: 2499,
- lpBalance: 0,
- depositTime: _startTime
- }
- );
+ _assertLenderLpBalance({
+ lender: _lender2,
+ index: 2499,
+ lpBalance: 0,
+ depositTime: _startTime
+ });
// assert bucket - LPs and deposit obliterated
- _assertBucket(
- {
- index: 2499,
- lpBalance: 0,
- collateral: 0,
- deposit: 0,
- exchangeRate: 1 * 1e27
- }
- );
+ _assertBucket({
+ index: 2499,
+ lpBalance: 0,
+ collateral: 0,
+ deposit: 0,
+ exchangeRate: 1 * 1e18
+ });
// assert lender2 as a kicker
- _assertKicker(
- {
- kicker: _lender2,
- claimable: 0,
- locked: 10_510.096153846153851000 * 1e18
- }
- );
+ _assertKicker({
+ kicker: _lender2,
+ claimable: 0,
+ locked: 10_510.096153846153851000 * 1e18
+ });
// assert kicked auction
_assertAuction(
AuctionParams({
@@ -548,7 +499,7 @@ contract ERC20PoolLiquidationsKickWithDepositTest is ERC20HelperContract {
auctionPrice: 123_636.939803752939029248 * 1e18,
debtInAuction: 35_471.574519230769247125 * 1e18,
thresholdPrice: 35.471574519230769247 * 1e18,
- neutralPrice: 37.154109537259614798 * 1e18
+ neutralPrice: 36.969263221153845869 * 1e18
})
);
}
@@ -561,46 +512,39 @@ contract ERC20PoolLiquidationsKickWithDepositTest is ERC20HelperContract {
*/
// lender1 adds collateral to bucket to be entitled to higher deposit than available
- _addCollateral(
- {
- from: _lender1,
- amount: 10 * 1e18,
- index: 2500,
- lpAward: 38636.54368867279344664 * 1e27
- }
- );
+ _addCollateral({
+ from: _lender1,
+ amount: 10 * 1e18,
+ index: 2500,
+ lpAward: 38636.54368867279344664 * 1e18
+ });
+
// assert lender and bucket LP balances pre kick
- _assertLenderLpBalance(
- {
- lender: _lender1,
- index: 2500,
- lpBalance: 88_636.54368867279344664 * 1e27,
- depositTime: _startTime
- }
- );
- _assertBucket(
- {
- index: 2500,
- lpBalance: 98_636.54368867279344664 * 1e27,
- collateral: 10 * 1e18,
- deposit: 60_000 * 1e18,
- exchangeRate: 1 * 1e27
- }
- );
+ _assertLenderLpBalance({
+ lender: _lender1,
+ index: 2500,
+ lpBalance: 88_636.54368867279344664 * 1e18,
+ depositTime: _startTime
+ });
+ _assertBucket({
+ index: 2500,
+ lpBalance: 98_636.54368867279344664 * 1e18,
+ collateral: 10 * 1e18,
+ deposit: 60_000 * 1e18,
+ exchangeRate: 1 * 1e18
+ });
- _kickWithDeposit(
- {
- from: _lender1,
- index: 2500,
- borrower: _borrower1,
- debt: 20_269.471153846153855500 * 1e18,
- collateral: 1_000 * 1e18,
- bond: 6_005.769230769230772000 * 1e18,
- removedFromDeposit: 6_005.769230769230772000 * 1e18,
- transferAmount: 0,
- lup: 3_844.432207828138682757 * 1e18
- }
- );
+ _kickWithDeposit({
+ from: _lender1,
+ index: 2500,
+ borrower: _borrower1,
+ debt: 20_269.471153846153855500 * 1e18,
+ collateral: 1_000 * 1e18,
+ bond: 6_005.769230769230772000 * 1e18,
+ removedFromDeposit: 6_005.769230769230772000 * 1e18,
+ transferAmount: 0,
+ lup: 3_844.432207828138682757 * 1e18
+ });
/******************************/
/*** Assert post-kick state ***/
@@ -623,6 +567,7 @@ contract ERC20PoolLiquidationsKickWithDepositTest is ERC20HelperContract {
interestRateUpdate: _startTime
})
);
+
// assert balances - no change, bond was covered from deposit
assertEq(_quote.balanceOf(address(_pool)), 11_000 * 1e18);
assertEq(_quote.balanceOf(_lender1), 49_000 * 1e18);
@@ -632,41 +577,34 @@ contract ERC20PoolLiquidationsKickWithDepositTest is ERC20HelperContract {
assertEq(_quote.balanceOf(_borrower3), 20_000 * 1e18);
assertEq(_quote.balanceOf(_borrower4), 20_000 * 1e18);
assertEq(_quote.balanceOf(_borrower5), 20_000 * 1e18);
+
// assert lenders LPs in bucket used
- _assertLenderLpBalance(
- {
- lender: _lender1,
- index: 2500,
- lpBalance: 82_630.77445790356267464 * 1e27, // reduced by amount used to cover auction bond
- depositTime: _startTime
- }
- );
- _assertLenderLpBalance(
- {
- lender: _lender2,
- index: 2500,
- lpBalance: 10_000 * 1e27,
- depositTime: _startTime
- }
- );
+ _assertLenderLpBalance({
+ lender: _lender1,
+ index: 2500,
+ lpBalance: 82_630.77445790356267464 * 1e18, // reduced by amount used to cover auction bond
+ depositTime: _startTime
+ });
+ _assertLenderLpBalance({
+ lender: _lender2,
+ index: 2500,
+ lpBalance: 10_000 * 1e18,
+ depositTime: _startTime
+ });
// assert bucket LPs
- _assertBucket(
- {
- index: 2500,
- lpBalance: 92_630.77445790356267464 * 1e27, // reduced by amount used to cover auction bond
- collateral: 10 * 1e18,
- deposit: 53_994.230769230769228000 * 1e18, // reduced by amount used to cover auction bond
- exchangeRate: 1 * 1e27
- }
- );
+ _assertBucket({
+ index: 2500,
+ lpBalance: 92_630.77445790356267464 * 1e18, // reduced by amount used to cover auction bond
+ collateral: 10 * 1e18,
+ deposit: 53_994.230769230769228000 * 1e18, // reduced by amount used to cover auction bond
+ exchangeRate: 1 * 1e18
+ });
// assert lender1 as a kicker
- _assertKicker(
- {
- kicker: _lender1,
- claimable: 0,
- locked: 6_005.769230769230772000 * 1e18
- }
- );
+ _assertKicker({
+ kicker: _lender1,
+ claimable: 0,
+ locked: 6_005.769230769230772000 * 1e18
+ });
// assert kicked auction
_assertAuction(
AuctionParams({
@@ -708,19 +646,18 @@ contract ERC20PoolLiquidationsKickWithDepositTest is ERC20HelperContract {
assertEq(thresholdPrice, 20.019230769230769240 * 1e18);
// kick borrower 1
- _kickWithDeposit(
- {
- from: _lender1,
- index: 2500,
- borrower: _borrower1,
- debt: 20_269.471153846153855500 * 1e18,
- collateral: 1_000 * 1e18,
- bond: 6_005.769230769230772000 * 1e18,
- removedFromDeposit: 6_005.769230769230772000 * 1e18,
- transferAmount: 0,
- lup: 3_844.432207828138682757 * 1e18
- }
- );
+ _kickWithDeposit({
+ from: _lender1,
+ index: 2500,
+ borrower: _borrower1,
+ debt: 20_269.471153846153855500 * 1e18,
+ collateral: 1_000 * 1e18,
+ bond: 6_005.769230769230772000 * 1e18,
+ removedFromDeposit: 6_005.769230769230772000 * 1e18,
+ transferAmount: 0,
+ lup: 3_844.432207828138682757 * 1e18
+ });
+
(borrower, thresholdPrice) = _pool.loanInfo(1);
assertEq(borrower, _borrower5);
assertEq(thresholdPrice, 20.019230769230769240 * 1e18);
@@ -748,19 +685,18 @@ contract ERC20PoolLiquidationsKickWithDepositTest is ERC20HelperContract {
assertEq(prev, address(0));
// kick borrower 5
- _kickWithDeposit(
- {
- from: _lender1,
- index: 2500,
- borrower: _borrower5,
- debt: 20_269.471153846153855500 * 1e18,
- collateral: 1_000 * 1e18,
- bond: 6_005.769230769230772000 * 1e18,
- removedFromDeposit: 6_005.769230769230772000 * 1e18,
- transferAmount: 0,
- lup: 99836282890
- }
- );
+ _kickWithDeposit({
+ from: _lender1,
+ index: 2500,
+ borrower: _borrower5,
+ debt: 20_269.471153846153855500 * 1e18,
+ collateral: 1_000 * 1e18,
+ bond: 6_005.769230769230772000 * 1e18,
+ removedFromDeposit: 6_005.769230769230772000 * 1e18,
+ transferAmount: 0,
+ lup: 99836282890
+ });
+
(borrower, thresholdPrice) = _pool.loanInfo(1);
assertEq(borrower, _borrower4);
assertEq(thresholdPrice, 20.019230769230769240 * 1e18);
@@ -787,19 +723,18 @@ contract ERC20PoolLiquidationsKickWithDepositTest is ERC20HelperContract {
assertEq(prev, _borrower1);
// kick borrower 4
- _kickWithDeposit(
- {
- from: _lender1,
- index: 2500,
- borrower: _borrower4,
- debt: 20_269.471153846153855500 * 1e18,
- collateral: 1_000 * 1e18,
- bond: 6_005.769230769230772000 * 1e18,
- removedFromDeposit: 6_005.769230769230772000 * 1e18,
- transferAmount: 0,
- lup: 99836282890
- }
- );
+ _kickWithDeposit({
+ from: _lender1,
+ index: 2500,
+ borrower: _borrower4,
+ debt: 20_269.471153846153855500 * 1e18,
+ collateral: 1_000 * 1e18,
+ bond: 6_005.769230769230772000 * 1e18,
+ removedFromDeposit: 6_005.769230769230772000 * 1e18,
+ transferAmount: 0,
+ lup: 99836282890
+ });
+
(borrower, thresholdPrice) = _pool.loanInfo(1);
assertEq(borrower, _borrower3);
assertEq(thresholdPrice, 20.019230769230769240 * 1e18);
@@ -830,19 +765,18 @@ contract ERC20PoolLiquidationsKickWithDepositTest is ERC20HelperContract {
assertEq(prev, _borrower5);
// kick borrower 3
- _kickWithDeposit(
- {
- from: _lender1,
- index: 2500,
- borrower: _borrower3,
- debt: 20_269.471153846153855500 * 1e18,
- collateral: 1_000 * 1e18,
- bond: 6_005.769230769230772000 * 1e18,
- removedFromDeposit: 6_005.769230769230772000 * 1e18,
- transferAmount: 0,
- lup: 99836282890
- }
- );
+ _kickWithDeposit({
+ from: _lender1,
+ index: 2500,
+ borrower: _borrower3,
+ debt: 20_269.471153846153855500 * 1e18,
+ collateral: 1_000 * 1e18,
+ bond: 6_005.769230769230772000 * 1e18,
+ removedFromDeposit: 6_005.769230769230772000 * 1e18,
+ transferAmount: 0,
+ lup: 99836282890
+ });
+
(borrower, thresholdPrice) = _pool.loanInfo(1);
assertEq(borrower, _borrower2);
assertEq(thresholdPrice, 20.019230769230769240 * 1e18);
@@ -877,19 +811,18 @@ contract ERC20PoolLiquidationsKickWithDepositTest is ERC20HelperContract {
assertEq(prev, _borrower4);
// kick borrower 2
- _kickWithDeposit(
- {
- from: _lender1,
- index: 2500,
- borrower: _borrower2,
- debt: 20_269.471153846153855500 * 1e18,
- collateral: 1_000 * 1e18,
- bond: 6_005.769230769230772000 * 1e18,
- removedFromDeposit: 6_005.769230769230772000 * 1e18,
- transferAmount: 0,
- lup: 99836282890
- }
- );
+ _kickWithDeposit({
+ from: _lender1,
+ index: 2500,
+ borrower: _borrower2,
+ debt: 20_269.471153846153855500 * 1e18,
+ collateral: 1_000 * 1e18,
+ bond: 6_005.769230769230772000 * 1e18,
+ removedFromDeposit: 6_005.769230769230772000 * 1e18,
+ transferAmount: 0,
+ lup: 99836282890
+ });
+
(borrower, thresholdPrice) = _pool.loanInfo(1);
assertEq(borrower, address(0));
assertEq(thresholdPrice, 0);
@@ -948,6 +881,7 @@ contract ERC20PoolLiquidationsKickWithDepositTest is ERC20HelperContract {
// skip to make loans clearable
skip(80 hours);
+
// settle borrower 2
_assertAuction(
AuctionParams({
@@ -965,14 +899,14 @@ contract ERC20PoolLiquidationsKickWithDepositTest is ERC20HelperContract {
neutralPrice: 21.020192307692307702 * 1e18
})
);
- _settle(
- {
- from: _lender1,
- borrower: _borrower2,
- maxDepth: 1,
- settledDebt: 20_269.471153846153855500 * 1e18
- }
- );
+
+ _settle({
+ from: _lender1,
+ borrower: _borrower2,
+ maxDepth: 1,
+ settledDebt: 20_269.471153846153855500 * 1e18
+ });
+
_assertAuction(
AuctionParams({
borrower: _borrower2,
@@ -989,6 +923,7 @@ contract ERC20PoolLiquidationsKickWithDepositTest is ERC20HelperContract {
neutralPrice: 0
})
);
+
(, , , , , , head, next, prev) = _pool.auctionInfo(_borrower1);
assertEq(head, _borrower1);
assertEq(next, _borrower5);
@@ -1027,14 +962,14 @@ contract ERC20PoolLiquidationsKickWithDepositTest is ERC20HelperContract {
neutralPrice: 21.125293269230769068 * 1e18
})
);
- _settle(
- {
- from: _lender1,
- borrower: _borrower4,
- maxDepth: 5,
- settledDebt: 20_269.471153846153855500 * 1e18
- }
- );
+
+ _settle({
+ from: _lender1,
+ borrower: _borrower4,
+ maxDepth: 5,
+ settledDebt: 20_269.471153846153855500 * 1e18
+ });
+
_assertAuction(
AuctionParams({
borrower: _borrower4,
@@ -1051,6 +986,7 @@ contract ERC20PoolLiquidationsKickWithDepositTest is ERC20HelperContract {
neutralPrice: 0
})
);
+
(, , , , , , head, next, prev) = _pool.auctionInfo(_borrower1);
assertEq(head, _borrower1);
assertEq(next, _borrower5);
@@ -1089,14 +1025,14 @@ contract ERC20PoolLiquidationsKickWithDepositTest is ERC20HelperContract {
neutralPrice: 21.020192307692307702 * 1e18
})
);
- _settle(
- {
- from: _lender1,
- borrower: _borrower1,
- maxDepth: 5,
- settledDebt: 20_269.471153846153855500 * 1e18
- }
- );
+
+ _settle({
+ from: _lender1,
+ borrower: _borrower1,
+ maxDepth: 5,
+ settledDebt: 20_269.471153846153855500 * 1e18
+ });
+
_assertAuction(
AuctionParams({
borrower: _borrower1,
@@ -1113,6 +1049,7 @@ contract ERC20PoolLiquidationsKickWithDepositTest is ERC20HelperContract {
neutralPrice: 0
})
);
+
(, , , , , , head, next, prev) = _pool.auctionInfo(_borrower1);
assertEq(head, _borrower5);
assertEq(next, address(0));
@@ -1151,14 +1088,14 @@ contract ERC20PoolLiquidationsKickWithDepositTest is ERC20HelperContract {
neutralPrice: 21.230919735576922742 * 1e18
})
);
- _settle(
- {
- from: _lender1,
- borrower: _borrower5,
- maxDepth: 5,
- settledDebt: 20_269.471153846153855500 * 1e18
- }
- );
+
+ _settle({
+ from: _lender1,
+ borrower: _borrower5,
+ maxDepth: 5,
+ settledDebt: 20_269.471153846153855500 * 1e18
+ });
+
_assertAuction(
AuctionParams({
borrower: _borrower5,
@@ -1175,6 +1112,7 @@ contract ERC20PoolLiquidationsKickWithDepositTest is ERC20HelperContract {
neutralPrice: 0
})
);
+
(, , , , , , head, next, prev) = _pool.auctionInfo(_borrower1);
assertEq(head, _borrower3);
assertEq(next, address(0));
@@ -1213,14 +1151,14 @@ contract ERC20PoolLiquidationsKickWithDepositTest is ERC20HelperContract {
neutralPrice: 21.125293269230769068 * 1e18
})
);
- _settle(
- {
- from: _lender1,
- borrower: _borrower3,
- maxDepth: 5,
- settledDebt: 20_269.471153846153855500 * 1e18
- }
- );
+
+ _settle({
+ from: _lender1,
+ borrower: _borrower3,
+ maxDepth: 5,
+ settledDebt: 20_269.471153846153855500 * 1e18
+ });
+
_assertAuction(
AuctionParams({
borrower: _borrower3,
@@ -1237,6 +1175,7 @@ contract ERC20PoolLiquidationsKickWithDepositTest is ERC20HelperContract {
neutralPrice: 0
})
);
+
(, , , , , , head, next, prev) = _pool.auctionInfo(_borrower1);
assertEq(head, address(0));
assertEq(next, address(0));
@@ -1277,124 +1216,102 @@ contract ERC20PoolLiquidationsKickWithDepositTest is ERC20HelperContract {
})
);
// assert lender1 after settle
- _assertLenderLpBalance(
- {
- lender: _lender1,
- index: 2500,
- lpBalance: 19_971.15384615384614 * 1e27,
- depositTime: _startTime
- }
- );
+ _assertLenderLpBalance({
+ lender: _lender1,
+ index: 2500,
+ lpBalance: 19_971.15384615384614 * 1e18,
+ depositTime: _startTime
+ });
// assert lender1 as a kicker
- _assertKicker(
- {
- kicker: _lender1,
- claimable: 30_028.846153846153860000 * 1e18,
- locked: 0
- }
- );
+ _assertKicker({
+ kicker: _lender1,
+ claimable: 30_028.846153846153860000 * 1e18,
+ locked: 0
+ });
// assert borrowers after settle
- _assertBorrower(
- {
- borrower: _borrower1,
- borrowerDebt: 0,
- borrowerCollateral: 994.725169378126588636 * 1e18,
- borrowert0Np: 21.020192307692307702 * 1e18,
- borrowerCollateralization: 1 * 1e18
- }
- );
- _assertBorrower(
- {
- borrower: _borrower2,
- borrowerDebt: 0,
- borrowerCollateral: 994.751412316543869246 * 1e18,
- borrowert0Np: 21.020192307692307702 * 1e18,
- borrowerCollateralization: 1 * 1e18
- }
- );
- _assertBorrower(
- {
- borrower: _borrower3,
- borrowerDebt: 0,
- borrowerCollateral: 0, // last borrower settled
- borrowert0Np: 21.125293269230769068 * 1e18,
- borrowerCollateralization: 1 * 1e18
- }
- );
- _assertBorrower(
- {
- borrower: _borrower4,
- borrowerDebt: 0,
- borrowerCollateral: 994.737734630435747061 * 1e18,
- borrowert0Np: 21.125293269230769068 * 1e18,
- borrowerCollateralization: 1 * 1e18
- }
- );
- _assertBorrower(
- {
- borrower: _borrower5,
- borrowerDebt: 0,
- borrowerCollateral: 0,
- borrowert0Np: 21.230919735576922742 * 1e18,
- borrowerCollateralization: 1 * 1e18
- }
- );
+ _assertBorrower({
+ borrower: _borrower1,
+ borrowerDebt: 0,
+ borrowerCollateral: 994.725169378126588636 * 1e18,
+ borrowert0Np: 21.020192307692307702 * 1e18,
+ borrowerCollateralization: 1 * 1e18
+ });
+ _assertBorrower({
+ borrower: _borrower2,
+ borrowerDebt: 0,
+ borrowerCollateral: 994.751412316543869246 * 1e18,
+ borrowert0Np: 21.020192307692307702 * 1e18,
+ borrowerCollateralization: 1 * 1e18
+ });
+ _assertBorrower({
+ borrower: _borrower3,
+ borrowerDebt: 0,
+ borrowerCollateral: 0, // last borrower settled
+ borrowert0Np: 21.125293269230769068 * 1e18,
+ borrowerCollateralization: 1 * 1e18
+ });
+ _assertBorrower({
+ borrower: _borrower4,
+ borrowerDebt: 0,
+ borrowerCollateral: 994.737734630435747061 * 1e18,
+ borrowert0Np: 21.125293269230769068 * 1e18,
+ borrowerCollateralization: 1 * 1e18
+ });
+ _assertBorrower({
+ borrower: _borrower5,
+ borrowerDebt: 0,
+ borrowerCollateral: 0,
+ borrowert0Np: 21.230919735576922742 * 1e18,
+ borrowerCollateralization: 1 * 1e18
+ });
}
function testKickWithDepositReverts() external tearDown {
// assert lender cannot kick with a bucket without deposit
- _assertKickWithInsufficientLiquidityRevert(
- {
- from: _lender1,
- index: 2_503
- }
- );
+ _assertKickWithInsufficientLiquidityRevert({
+ from: _lender1,
+ index: 2_503
+ });
+
// assert lender cannot kick a loan if the proposed LUP doesn't render borrower uncollateralized
- _assertKickWithBadProposedLupRevert(
- {
- from: _lender2,
- index: 2_500
- }
- );
+ _assertKickWithBadProposedLupRevert({
+ from: _lender2,
+ index: 2_500
+ });
// Lender 2 adds Quote token at a much lower price the tries to kick with deposit
- _addLiquidity(
- {
- from: _lender3,
- amount: 150_000 * 1e18,
- index: 7000,
- lpAward: 150_000 * 1e27,
- newLup: 3_844.432207828138682757 * 1e18
- }
- );
- _assertKickPriceBelowProposedLupRevert(
- {
- from: _lender3,
- index: 7000
- }
- );
+ _addLiquidity({
+ from: _lender3,
+ amount: 150_000 * 1e18,
+ index: 7000,
+ lpAward: 150_000 * 1e18,
+ newLup: 3_844.432207828138682757 * 1e18
+ });
+
+ _assertKickPriceBelowProposedLupRevert({
+ from: _lender3,
+ index: 7000
+ });
// asert failure when lender has LPs but insufficient quote token balance to post remaining bond
- _addLiquidity(
- {
- from: _lender4,
- amount: 5_000 * 1e18,
- index: 2499,
- lpAward: 5_000 * 1e27,
- newLup: 3_844.432207828138682757 * 1e18
- }
- );
+ _addLiquidity({
+ from: _lender4,
+ amount: 5_000 * 1e18,
+ index: 2499,
+ lpAward: 5_000 * 1e18,
+ newLup: 3_844.432207828138682757 * 1e18
+ });
+
// borrower draws more debt consuming entire deposit from bucket 2499
- _drawDebt(
- {
- from: _borrower1,
- borrower: _borrower1,
- amountToBorrow: 15_000 * 1e18,
- limitIndex: 5000,
- collateralToPledge: 0,
- newLup: 3_825.305679430983794766 * 1e18
- }
- );
+ _drawDebt({
+ from: _borrower1,
+ borrower: _borrower1,
+ amountToBorrow: 15_000 * 1e18,
+ limitIndex: 5000,
+ collateralToPledge: 0,
+ newLup: 3_825.305679430983794766 * 1e18
+ });
+
changePrank(_lender4);
vm.expectRevert("ERC20: transfer amount exceeds balance");
_pool.kickWithDeposit(2499);
diff --git a/tests/forge/ERC20Pool/ERC20PoolLiquidationsMisc.t.sol b/tests/forge/ERC20Pool/ERC20PoolLiquidationsMisc.t.sol
index 540295041..f01816df5 100644
--- a/tests/forge/ERC20Pool/ERC20PoolLiquidationsMisc.t.sol
+++ b/tests/forge/ERC20Pool/ERC20PoolLiquidationsMisc.t.sol
@@ -27,75 +27,57 @@ contract ERC20PoolLiquidationsMiscTest is ERC20HelperContract {
_mintCollateralAndApproveTokens(_lender1, 4 * 1e18);
// Lender adds Quote token accross 5 prices
- _addInitialLiquidity(
- {
- from: _lender,
- amount: 2_000 * 1e18,
- index: _i9_91
- }
- );
- _addInitialLiquidity(
- {
- from: _lender,
- amount: 5_000 * 1e18,
- index: _i9_81
- }
- );
- _addInitialLiquidity(
- {
- from: _lender,
- amount: 11_000 * 1e18,
- index: _i9_72
- }
- );
- _addInitialLiquidity(
- {
- from: _lender,
- amount: 25_000 * 1e18,
- index: _i9_62
- }
- );
- _addInitialLiquidity(
- {
- from: _lender,
- amount: 30_000 * 1e18,
- index: _i9_52
- }
- );
+ _addInitialLiquidity({
+ from: _lender,
+ amount: 2_000 * 1e18,
+ index: _i9_91
+ });
+ _addInitialLiquidity({
+ from: _lender,
+ amount: 5_000 * 1e18,
+ index: _i9_81
+ });
+ _addInitialLiquidity({
+ from: _lender,
+ amount: 11_000 * 1e18,
+ index: _i9_72
+ });
+ _addInitialLiquidity({
+ from: _lender,
+ amount: 25_000 * 1e18,
+ index: _i9_62
+ });
+ _addInitialLiquidity({
+ from: _lender,
+ amount: 30_000 * 1e18,
+ index: _i9_52
+ });
// first borrower adds collateral token and borrows
- _pledgeCollateral(
- {
- from: _borrower,
- borrower: _borrower,
- amount: 2 * 1e18
- }
- );
- _borrow(
- {
- from: _borrower,
- amount: 19.25 * 1e18,
- indexLimit: _i9_91,
- newLup: 9.917184843435912074 * 1e18
- }
- );
+ _pledgeCollateral({
+ from: _borrower,
+ borrower: _borrower,
+ amount: 2 * 1e18
+ });
+ _borrow({
+ from: _borrower,
+ amount: 19.25 * 1e18,
+ indexLimit: _i9_91,
+ newLup: 9.917184843435912074 * 1e18
+ });
// second borrower adds collateral token and borrows
- _pledgeCollateral(
- {
- from: _borrower2,
- borrower: _borrower2,
- amount: 1_000 * 1e18
- }
- );
- _borrow(
- {
- from: _borrower2,
- amount: 7_980 * 1e18,
- indexLimit: _i9_72,
- newLup: 9.721295865031779605 * 1e18
- }
- );
+ _pledgeCollateral({
+ from: _borrower2,
+ borrower: _borrower2,
+ amount: 1_000 * 1e18
+ });
+ _borrow({
+ from: _borrower2,
+ amount: 7_980 * 1e18,
+ indexLimit: _i9_72,
+ newLup: 9.721295865031779605 * 1e18
+ });
/*****************************/
/*** Assert pre-kick state ***/
@@ -118,43 +100,36 @@ contract ERC20PoolLiquidationsMiscTest is ERC20HelperContract {
interestRateUpdate: _startTime
})
);
- _assertBorrower(
- {
- borrower: _borrower,
- borrowerDebt: 19.268509615384615394 * 1e18,
- borrowerCollateral: 2 * 1e18,
- borrowert0Np: 10.115967548076923081 * 1e18,
- borrowerCollateralization: 1.009034539679184679 * 1e18
- }
- );
- _assertBorrower(
- {
- borrower: _borrower2,
- borrowerDebt: 7_987.673076923076926760 * 1e18,
- borrowerCollateral: 1_000 * 1e18,
- borrowert0Np: 8.471136974495192174 * 1e18,
- borrowerCollateralization: 1.217037273735858713 * 1e18
- }
- );
- _assertReserveAuction(
- {
- reserves: 7.691586538461542154 * 1e18,
- claimableReserves : 0,
- claimableReservesRemaining: 0,
- auctionPrice: 0,
- timeRemaining: 0
- }
- );
+ _assertBorrower({
+ borrower: _borrower,
+ borrowerDebt: 19.268509615384615394 * 1e18,
+ borrowerCollateral: 2 * 1e18,
+ borrowert0Np: 10.115967548076923081 * 1e18,
+ borrowerCollateralization: 1.009034539679184679 * 1e18
+ });
+ _assertBorrower({
+ borrower: _borrower2,
+ borrowerDebt: 7_987.673076923076926760 * 1e18,
+ borrowerCollateral: 1_000 * 1e18,
+ borrowert0Np: 8.471136974495192174 * 1e18,
+ borrowerCollateralization: 1.217037273735858713 * 1e18
+ });
+ _assertReserveAuction({
+ reserves: 7.691586538461542154 * 1e18,
+ claimableReserves : 0,
+ claimableReservesRemaining: 0,
+ auctionPrice: 0,
+ timeRemaining: 0
+ });
+
assertEq(_quote.balanceOf(_lender), 47_000 * 1e18);
// should revert if there's no auction started
- _assertTakeNoAuctionRevert(
- {
- from: _lender,
- borrower: _borrower,
- maxCollateral: 10 * 1e18
- }
- );
+ _assertTakeNoAuctionRevert({
+ from: _lender,
+ borrower: _borrower,
+ maxCollateral: 10 * 1e18
+ });
}
@@ -163,53 +138,41 @@ contract ERC20PoolLiquidationsMiscTest is ERC20HelperContract {
skip(25 hours);
// Lender attempts to withdraw entire position
- _removeLiquidity(
- {
- from: _lender,
- amount: 2_000.00 * 1e18,
- index: _i9_91,
- newLup: 9.721295865031779605 * 1e18,
- lpRedeem: 1_999.891367962935869240000000000 * 1e27
- }
- );
-
- _removeLiquidity(
- {
- from: _lender,
- amount: 5_000 * 1e18,
- index: _i9_81,
- newLup: 9.721295865031779605 * 1e18,
- lpRedeem: 4_999.728419907339673101000000000 * 1e27
- }
- );
-
- _removeLiquidity(
- {
- from: _lender,
- amount: 2_992.8 * 1e18,
- index: _i9_72,
- newLup: 9.721295865031779605 * 1e18,
- lpRedeem: 2_992.637443019737234731000000000 * 1e27
- }
- );
+ _removeLiquidity({
+ from: _lender,
+ amount: 2_000.00 * 1e18,
+ index: _i9_91,
+ newLup: 9.721295865031779605 * 1e18,
+ lpRedeem: 1_999.891367962935869240 * 1e18
+ });
+ _removeLiquidity({
+ from: _lender,
+ amount: 5_000 * 1e18,
+ index: _i9_81,
+ newLup: 9.721295865031779605 * 1e18,
+ lpRedeem: 4_999.728419907339673101 * 1e18
+ });
+ _removeLiquidity({
+ from: _lender,
+ amount: 2_992.8 * 1e18,
+ index: _i9_72,
+ newLup: 9.721295865031779605 * 1e18,
+ lpRedeem: 2_992.637443019737234731 * 1e18
+ });
// Lender amount to withdraw is restricted by HTP
- _assertRemoveAllLiquidityLupBelowHtpRevert(
- {
- from: _lender,
- index: _i9_72
- }
- );
+ _assertRemoveAllLiquidityLupBelowHtpRevert({
+ from: _lender,
+ index: _i9_72
+ });
- _assertBucket(
- {
- index: _i9_72,
- lpBalance: 8_007.362556980262765269000000000 * 1e27,
- collateral: 0,
- deposit: 8_007.797508658144068000 * 1e18,
- exchangeRate: 1.000054318968922187999940710 * 1e27
- }
- );
+ _assertBucket({
+ index: _i9_72,
+ lpBalance: 8_007.362556980262765269 * 1e18,
+ collateral: 0,
+ deposit: 8_007.797508658144068000 * 1e18,
+ exchangeRate: 1.000054318968922188 * 1e18
+ });
skip(16 hours);
@@ -229,38 +192,30 @@ contract ERC20PoolLiquidationsMiscTest is ERC20HelperContract {
neutralPrice: 0
})
);
+ _assertBorrower({
+ borrower: _borrower,
+ borrowerDebt: 19.272843317722413898 * 1e18,
+ borrowerCollateral: 2 * 1e18,
+ borrowert0Np: 10.115967548076923081 * 1e18,
+ borrowerCollateralization: 0.998794730435100101 * 1e18
+ });
- _assertBorrower(
- {
- borrower: _borrower,
- borrowerDebt: 19.272843317722413898 * 1e18,
- borrowerCollateral: 2 * 1e18,
- borrowert0Np: 10.115967548076923081 * 1e18,
- borrowerCollateralization: 0.998794730435100101 * 1e18
- }
- );
-
- _kick(
- {
- from: _lender,
- borrower: _borrower,
- debt: 19.489662805046791054 * 1e18,
- collateral: 2 * 1e18,
- bond: 0.192728433177224139 * 1e18,
- transferAmount: 0.192728433177224139 * 1e18
- }
- );
-
- _assertBucket(
- {
- index: _i9_72,
- lpBalance: 8_007.362556980262765269000000000 * 1e27,
- collateral: 0,
- deposit: 8_008.361347558277120605 * 1e18,
- exchangeRate: 1.000124734027079076000026734 * 1e27
- }
- );
+ _kick({
+ from: _lender,
+ borrower: _borrower,
+ debt: 19.489662805046791054 * 1e18,
+ collateral: 2 * 1e18,
+ bond: 0.192728433177224139 * 1e18,
+ transferAmount: 0.192728433177224139 * 1e18
+ });
+ _assertBucket({
+ index: _i9_72,
+ lpBalance: 8_007.362556980262765269 * 1e18,
+ collateral: 0,
+ deposit: 8_008.361347558277120605 * 1e18,
+ exchangeRate: 1.000124734027079076 * 1e18
+ });
_assertAuction(
AuctionParams({
borrower: _borrower,
@@ -279,41 +234,27 @@ contract ERC20PoolLiquidationsMiscTest is ERC20HelperContract {
);
// lender cannot withdraw - deposit in buckets within liquidation debt from the top-of-book down are frozen
- _assertRemoveDepositLockedByAuctionDebtRevert(
- {
- from: _lender,
- amount: 10.0 * 1e18,
- index: _i9_72
- }
- );
+ _assertRemoveDepositLockedByAuctionDebtRevert({
+ from: _lender,
+ amount: 10.0 * 1e18,
+ index: _i9_72
+ });
// lender cannot move funds
- _assertMoveDepositLockedByAuctionDebtRevert(
- {
- from: _lender,
- amount: 10.0 * 1e18,
- fromIndex: _i9_72,
- toIndex: _i9_81
- }
- );
+ _assertMoveDepositLockedByAuctionDebtRevert({
+ from: _lender,
+ amount: 10.0 * 1e18,
+ fromIndex: _i9_72,
+ toIndex: _i9_81
+ });
// lender can add / remove liquidity in buckets that are not within liquidation debt
changePrank(_lender1);
- _pool.addQuoteToken(2_000 * 1e18, 5000);
+ _pool.addQuoteToken(2_000 * 1e18, 5000, block.timestamp + 1 minutes);
_pool.removeQuoteToken(2_000 * 1e18, 5000);
skip(3 hours);
- _assertBorrower(
- {
- borrower: _borrower,
- borrowerDebt: 19.489933125874732298 * 1e18,
- borrowerCollateral: 2 * 1e18,
- borrowert0Np: 10.115967548076923081 * 1e18,
- borrowerCollateralization: 0.987669594447545452 * 1e18
- }
- );
-
_assertAuction(
AuctionParams({
borrower: _borrower,
@@ -330,18 +271,23 @@ contract ERC20PoolLiquidationsMiscTest is ERC20HelperContract {
neutralPrice: 10.118242741804267296 * 1e18
})
);
+ _assertBorrower({
+ borrower: _borrower,
+ borrowerDebt: 19.489933125874732298 * 1e18,
+ borrowerCollateral: 2 * 1e18,
+ borrowert0Np: 10.115967548076923081 * 1e18,
+ borrowerCollateralization: 0.987669594447545452 * 1e18
+ });
- _take(
- {
- from: _lender,
- borrower: _borrower,
- maxCollateral: 2.0 * 1e18,
- bondChange: 0.192728433177224139 * 1e18,
- givenAmount: 20.854228444685963559 * 1e18,
- collateralTaken: 0.257631549479994909 * 1e18,
- isReward: false
- }
- );
+ _take({
+ from: _lender,
+ borrower: _borrower,
+ maxCollateral: 2.0 * 1e18,
+ bondChange: 0.192728433177224139 * 1e18,
+ givenAmount: 20.854228444685963559 * 1e18,
+ collateralTaken: 0.257631549479994909 * 1e18,
+ isReward: false
+ });
// Borrower is removed from auction, keeps collateral in system
_assertAuction(
@@ -360,17 +306,13 @@ contract ERC20PoolLiquidationsMiscTest is ERC20HelperContract {
neutralPrice: 0
})
);
-
- _assertBorrower(
- {
- borrower: _borrower,
- borrowerDebt: 0,
- borrowerCollateral: 1.742368450520005091 * 1e18,
- borrowert0Np: 0,
- borrowerCollateralization: 1 * 1e18
- }
- );
-
+ _assertBorrower({
+ borrower: _borrower,
+ borrowerDebt: 0,
+ borrowerCollateral: 1.742368450520005091 * 1e18,
+ borrowert0Np: 0,
+ borrowerCollateralization: 1 * 1e18
+ });
_assertPool(
PoolParams({
htp: 7.991488192808991114 * 1e18,
@@ -389,65 +331,53 @@ contract ERC20PoolLiquidationsMiscTest is ERC20HelperContract {
})
);
- _removeLiquidity(
- {
- from: _lender,
- amount: 8_008.373442262808822463 * 1e18,
- index: _i9_72,
- newLup: 9.624807173121239337 * 1e18,
- lpRedeem: 8_007.362556980262762824000000000 * 1e27
- }
- );
+ _removeLiquidity({
+ from: _lender,
+ amount: 8_008.373442262808822463 * 1e18,
+ index: _i9_72,
+ newLup: 9.624807173121239337 * 1e18,
+ lpRedeem: 8_007.362556980262762824 * 1e18
+ });
- _assertBucket(
- {
- index: _i9_72,
- lpBalance: 0.000000000000002445000000000 * 1e27,
- collateral: 0,
- deposit: 2445,
- exchangeRate: 1 * 1e27
- }
- );
+ _assertBucket({
+ index: _i9_72,
+ lpBalance: 0.000000000000002445 * 1e18,
+ collateral: 0,
+ deposit: 2445,
+ exchangeRate: 1 * 1e18
+ });
- _removeLiquidity(
- {
- from: _lender,
- amount: 25_000.037756489769875000 * 1e18,
- index: _i9_62,
- newLup: 9.529276179422528643 * 1e18,
- lpRedeem: 25_000 * 1e27
- }
- );
+ _removeLiquidity({
+ from: _lender,
+ amount: 25_000.037756489769875000 * 1e18,
+ index: _i9_62,
+ newLup: 9.529276179422528643 * 1e18,
+ lpRedeem: 25_000 * 1e18
+ });
- _assertBucket(
- {
- index: _i9_62,
- lpBalance: 0,
- collateral: 0,
- deposit: 0,
- exchangeRate: 1 * 1e27
- }
- );
+ _assertBucket({
+ index: _i9_62,
+ lpBalance: 0,
+ collateral: 0,
+ deposit: 0,
+ exchangeRate: 1 * 1e18
+ });
- _removeLiquidity(
- {
- from: _lender,
- amount: 22_000 * 1e18,
- index: _i9_52,
- newLup: 9.529276179422528643 * 1e18,
- lpRedeem: 21_999.966774339181882911000000000 * 1e27
- }
- );
+ _removeLiquidity({
+ from: _lender,
+ amount: 22_000 * 1e18,
+ index: _i9_52,
+ newLup: 9.529276179422528643 * 1e18,
+ lpRedeem: 21_999.966774339181882911 * 1e18
+ });
- _assertBucket(
- {
- index: _i9_52,
- lpBalance: 8_000.033225660818117089000000000 * 1e27,
- collateral: 0,
- deposit: 8_000.045307787723850000 * 1e18,
- exchangeRate: 1.000001510259590794999992127 * 1e27
- }
- );
+ _assertBucket({
+ index: _i9_52,
+ lpBalance: 8_000.033225660818117089 * 1e18,
+ collateral: 0,
+ deposit: 8_000.045307787723850000 * 1e18,
+ exchangeRate: 1.000001510259590795 * 1e18
+ });
_assertRemoveAllLiquidityLupBelowHtpRevert({
from: _lender,
@@ -473,16 +403,13 @@ contract ERC20PoolLiquidationsMiscTest is ERC20HelperContract {
interestRateUpdate: block.timestamp - 28 hours
})
);
-
- _assertBorrower(
- {
- borrower: _borrower2,
- borrowerDebt: 7_990.503913730158190391 * 1e18,
- borrowerCollateral: 1_000.00 * 1e18,
- borrowert0Np: 8.471136974495192174 * 1e18,
- borrowerCollateralization: 1.192575121957988603 * 1e18
- }
- );
+ _assertBorrower({
+ borrower: _borrower2,
+ borrowerDebt: 7_990.503913730158190391 * 1e18,
+ borrowerCollateral: 1_000.00 * 1e18,
+ borrowert0Np: 8.471136974495192174 * 1e18,
+ borrowerCollateralization: 1.192575121957988603 * 1e18
+ });
// draw debt is called to trigger accrual of pool interest that will push the lup back up
IERC20Pool(address(_pool)).drawDebt(_lender, 0, 0, 0);
@@ -507,27 +434,23 @@ contract ERC20PoolLiquidationsMiscTest is ERC20HelperContract {
skip(117 days);
- _assertBorrower(
- {
- borrower: _borrower2,
- borrowerDebt: 8_105.430237884635118282 * 1e18,
- borrowerCollateral: 1_000 * 1e18,
- borrowert0Np: 8.471136974495192174 * 1e18,
- borrowerCollateralization: 0.000000012317209569 * 1e18
- }
- );
+ _assertBorrower({
+ borrower: _borrower2,
+ borrowerDebt: 8_105.430237884635118282 * 1e18,
+ borrowerCollateral: 1_000 * 1e18,
+ borrowert0Np: 8.471136974495192174 * 1e18,
+ borrowerCollateralization: 0.000000012317209569 * 1e18
+ });
// kick borrower 2
changePrank(_lender);
_pool.kickWithDeposit(_i9_52);
- _assertRemoveDepositLockedByAuctionDebtRevert(
- {
- from: _lender,
- amount: 10.0 * 1e18,
- index: _i9_52
- }
- );
+ _assertRemoveDepositLockedByAuctionDebtRevert({
+ from: _lender,
+ amount: 10.0 * 1e18,
+ index: _i9_52
+ });
skip(10 hours);
@@ -547,28 +470,23 @@ contract ERC20PoolLiquidationsMiscTest is ERC20HelperContract {
neutralPrice: 8.596021534820399875 * 1e18
})
);
+ _assertBorrower({
+ borrower: _borrower2,
+ borrowerDebt: 8_196.162962286455648823 * 1e18,
+ borrowerCollateral: 1_000 * 1e18,
+ borrowert0Np: 8.471136974495192174 * 1e18,
+ borrowerCollateralization: 0.000000012180856255 * 1e18
+ });
- _assertBorrower(
- {
- borrower: _borrower2,
- borrowerDebt: 8_196.162962286455648823 * 1e18,
- borrowerCollateral: 1_000 * 1e18,
- borrowert0Np: 8.471136974495192174 * 1e18,
- borrowerCollateralization: 0.000000012180856255 * 1e18
- }
- );
-
- _take(
- {
- from: _lender,
- borrower: _borrower2,
- maxCollateral: 1_000.0 * 1e18,
- bondChange: 88.990371346118344167 * 1e18,
- givenAmount: 595.579761213908032000 * 1e18,
- collateralTaken: 1_000 * 1e18,
- isReward: true
- }
- );
+ _take({
+ from: _lender,
+ borrower: _borrower2,
+ maxCollateral: 1_000.0 * 1e18,
+ bondChange: 88.990371346118344167 * 1e18,
+ givenAmount: 595.579761213908032000 * 1e18,
+ collateralTaken: 1_000 * 1e18,
+ isReward: true
+ });
_assertPool(
PoolParams({
@@ -587,16 +505,13 @@ contract ERC20PoolLiquidationsMiscTest is ERC20HelperContract {
interestRateUpdate: block.timestamp - 10 hours
})
);
-
- _assertBorrower(
- {
- borrower: _borrower2,
- borrowerDebt: 8_263.304979778717856240 * 1e18,
- borrowerCollateral: 0,
- borrowert0Np: 8.471136974495192174 * 1e18,
- borrowerCollateralization: 0
- }
- );
+ _assertBorrower({
+ borrower: _borrower2,
+ borrowerDebt: 8_263.304979778717856240 * 1e18,
+ borrowerCollateral: 0,
+ borrowert0Np: 8.471136974495192174 * 1e18,
+ borrowerCollateralization: 0
+ });
_assertRemoveLiquidityAuctionNotClearedRevert({
from: _lender,
@@ -604,14 +519,12 @@ contract ERC20PoolLiquidationsMiscTest is ERC20HelperContract {
index: _i9_52
});
- _settle(
- {
- from: _lender,
- borrower: _borrower2,
- maxDepth: 10,
- settledDebt: 7_468.035011263740962170 * 1e18
- }
- );
+ _settle({
+ from: _lender,
+ borrower: _borrower2,
+ maxDepth: 10,
+ settledDebt: 7_468.035011263740962170 * 1e18
+ });
_assertPool(
PoolParams({
@@ -630,51 +543,40 @@ contract ERC20PoolLiquidationsMiscTest is ERC20HelperContract {
interestRateUpdate: block.timestamp - 10 hours
})
);
-
- _assertBucket(
- {
- index: _i9_91,
- lpBalance: 0,
- collateral: 0,
- deposit: 0,
- exchangeRate: 1 * 1e27
- }
- );
- _assertBucket(
- {
- index: _i9_81,
- lpBalance: 0,
- collateral: 0,
- deposit: 0,
- exchangeRate: 1 * 1e27
- }
- );
- _assertBucket(
- {
- index: _i9_72,
- lpBalance: 0,
- collateral: 0,
- deposit: 0,
- exchangeRate: 1 * 1e27
- }
- );
- _assertBucket(
- {
- index: _i9_62,
- lpBalance: 0,
- collateral: 0,
- deposit: 0,
- exchangeRate: 1 * 1e27
- }
- );
- _assertBucket(
- {
- index: _i9_52,
- lpBalance: 0 * 1e27,
- collateral: 0,
- deposit: 0,
- exchangeRate: 1 * 1e27
- }
- );
+ _assertBucket({
+ index: _i9_91,
+ lpBalance: 0,
+ collateral: 0,
+ deposit: 0,
+ exchangeRate: 1 * 1e18
+ });
+ _assertBucket({
+ index: _i9_81,
+ lpBalance: 0,
+ collateral: 0,
+ deposit: 0,
+ exchangeRate: 1 * 1e18
+ });
+ _assertBucket({
+ index: _i9_72,
+ lpBalance: 0,
+ collateral: 0,
+ deposit: 0,
+ exchangeRate: 1 * 1e18
+ });
+ _assertBucket({
+ index: _i9_62,
+ lpBalance: 0,
+ collateral: 0,
+ deposit: 0,
+ exchangeRate: 1 * 1e18
+ });
+ _assertBucket({
+ index: _i9_52,
+ lpBalance: 0,
+ collateral: 0,
+ deposit: 0,
+ exchangeRate: 1 * 1e18
+ });
}
}
diff --git a/tests/forge/ERC20Pool/ERC20PoolLiquidationsScaled.t.sol b/tests/forge/ERC20Pool/ERC20PoolLiquidationsScaled.t.sol
index 98fac1dbc..dcf7813db 100644
--- a/tests/forge/ERC20Pool/ERC20PoolLiquidationsScaled.t.sol
+++ b/tests/forge/ERC20Pool/ERC20PoolLiquidationsScaled.t.sol
@@ -73,15 +73,15 @@ contract ERC20PoolLiquidationsScaledTest is ERC20DSTestPlus {
// deposit 200k quote token across 4 buckets
uint256 lpBalance;
for (uint i=0; i<4; ++i) {
- _addInitialLiquidity(
- {
+
+ _addInitialLiquidity({
from: _lender,
amount: 50_000 * 1e18,
index: startBucketId + i
- }
- );
+ });
+
(lpBalance, ) = _pool.lenderInfo(startBucketId + i, _lender);
- assertEq(lpBalance, 50_000 * 1e27);
+ assertEq(lpBalance, 50_000 * 1e18);
}
assertEq(_pool.depositSize(), 200_000 * 1e18);
}
@@ -295,6 +295,7 @@ contract ERC20PoolLiquidationsScaledTest is ERC20DSTestPlus {
,
,
) = _pool.auctionInfo(_borrower);
+
assertEq(auctionKicker, kicker);
assertGe(auctionBondFactor, 0.01 * 1e18);
assertLe(auctionBondFactor, 0.3 * 1e18);
diff --git a/tests/forge/ERC20Pool/ERC20PoolLiquidationsSettle.t.sol b/tests/forge/ERC20Pool/ERC20PoolLiquidationsSettle.t.sol
index a37269ff1..0cea6fdc9 100644
--- a/tests/forge/ERC20Pool/ERC20PoolLiquidationsSettle.t.sol
+++ b/tests/forge/ERC20Pool/ERC20PoolLiquidationsSettle.t.sol
@@ -26,75 +26,57 @@ contract ERC20PoolLiquidationsSettleTest is ERC20HelperContract {
_mintCollateralAndApproveTokens(_lender1, 4 * 1e18);
// Lender adds Quote token accross 5 prices
- _addInitialLiquidity(
- {
- from: _lender,
- amount: 2_000 * 1e18,
- index: _i9_91
- }
- );
- _addInitialLiquidity(
- {
- from: _lender,
- amount: 5_000 * 1e18,
- index: _i9_81
- }
- );
- _addInitialLiquidity(
- {
- from: _lender,
- amount: 11_000 * 1e18,
- index: _i9_72
- }
- );
- _addInitialLiquidity(
- {
- from: _lender,
- amount: 25_000 * 1e18,
- index: _i9_62
- }
- );
- _addInitialLiquidity(
- {
- from: _lender,
- amount: 30_000 * 1e18,
- index: _i9_52
- }
- );
+ _addInitialLiquidity({
+ from: _lender,
+ amount: 2_000 * 1e18,
+ index: _i9_91
+ });
+ _addInitialLiquidity({
+ from: _lender,
+ amount: 5_000 * 1e18,
+ index: _i9_81
+ });
+ _addInitialLiquidity({
+ from: _lender,
+ amount: 11_000 * 1e18,
+ index: _i9_72
+ });
+ _addInitialLiquidity({
+ from: _lender,
+ amount: 25_000 * 1e18,
+ index: _i9_62
+ });
+ _addInitialLiquidity({
+ from: _lender,
+ amount: 30_000 * 1e18,
+ index: _i9_52
+ });
// first borrower adds collateral token and borrows
- _pledgeCollateral(
- {
- from: _borrower,
- borrower: _borrower,
- amount: 2 * 1e18
- }
- );
- _borrow(
- {
- from: _borrower,
- amount: 19.25 * 1e18,
- indexLimit: _i9_91,
- newLup: 9.917184843435912074 * 1e18
- }
- );
+ _pledgeCollateral({
+ from: _borrower,
+ borrower: _borrower,
+ amount: 2 * 1e18
+ });
+ _borrow({
+ from: _borrower,
+ amount: 19.25 * 1e18,
+ indexLimit: _i9_91,
+ newLup: 9.917184843435912074 * 1e18
+ });
// second borrower adds collateral token and borrows
- _pledgeCollateral(
- {
- from: _borrower2,
- borrower: _borrower2,
- amount: 1_000 * 1e18
- }
- );
- _borrow(
- {
- from: _borrower2,
- amount: 7_980 * 1e18,
- indexLimit: _i9_72,
- newLup: 9.721295865031779605 * 1e18
- }
- );
+ _pledgeCollateral({
+ from: _borrower2,
+ borrower: _borrower2,
+ amount: 1_000 * 1e18
+ });
+ _borrow({
+ from: _borrower2,
+ amount: 7_980 * 1e18,
+ indexLimit: _i9_72,
+ newLup: 9.721295865031779605 * 1e18
+ });
/*****************************/
/*** Assert pre-kick state ***/
@@ -117,47 +99,40 @@ contract ERC20PoolLiquidationsSettleTest is ERC20HelperContract {
interestRateUpdate: _startTime
})
);
- _assertBorrower(
- {
- borrower: _borrower,
- borrowerDebt: 19.268509615384615394 * 1e18,
- borrowerCollateral: 2 * 1e18,
- borrowert0Np: 10.115967548076923081 * 1e18,
- borrowerCollateralization: 1.009034539679184679 * 1e18
- }
- );
- _assertBorrower(
- {
- borrower: _borrower2,
- borrowerDebt: 7_987.673076923076926760 * 1e18,
- borrowerCollateral: 1_000 * 1e18,
- borrowert0Np: 8.471136974495192174 * 1e18,
- borrowerCollateralization: 1.217037273735858713 * 1e18
- }
- );
- _assertReserveAuction(
- {
- reserves: 7.691586538461542154 * 1e18,
- claimableReserves : 0,
- claimableReservesRemaining: 0,
- auctionPrice: 0,
- timeRemaining: 0
- }
- );
+ _assertBorrower({
+ borrower: _borrower,
+ borrowerDebt: 19.268509615384615394 * 1e18,
+ borrowerCollateral: 2 * 1e18,
+ borrowert0Np: 10.115967548076923081 * 1e18,
+ borrowerCollateralization: 1.009034539679184679 * 1e18
+ });
+ _assertBorrower({
+ borrower: _borrower2,
+ borrowerDebt: 7_987.673076923076926760 * 1e18,
+ borrowerCollateral: 1_000 * 1e18,
+ borrowert0Np: 8.471136974495192174 * 1e18,
+ borrowerCollateralization: 1.217037273735858713 * 1e18
+ });
+ _assertReserveAuction({
+ reserves: 7.691586538461542154 * 1e18,
+ claimableReserves : 0,
+ claimableReservesRemaining: 0,
+ auctionPrice: 0,
+ timeRemaining: 0
+ });
+
assertEq(_quote.balanceOf(_lender), 47_000 * 1e18);
}
function testSettleOnAuctionKicked72HoursAgoAndPartiallyTaken() external tearDown {
// Borrower2 borrows
- _borrow(
- {
- from: _borrower2,
- amount: 1_730 * 1e18,
- indexLimit: _i9_72,
- newLup: 9.721295865031779605 * 1e18
- }
- );
+ _borrow({
+ from: _borrower2,
+ amount: 1_730 * 1e18,
+ indexLimit: _i9_72,
+ newLup: 9.721295865031779605 * 1e18
+ });
// Skip to make borrower undercollateralized
skip(100 days);
@@ -178,27 +153,22 @@ contract ERC20PoolLiquidationsSettleTest is ERC20HelperContract {
neutralPrice: 0
})
);
-
- _assertBorrower(
- {
- borrower: _borrower2,
- borrowerDebt: 9_853.394241979221645666 * 1e18,
- borrowerCollateral: 1_000 * 1e18,
- borrowert0Np: 10.307611531622595991 * 1e18,
- borrowerCollateralization: 0.986593617011217057 * 1e18
- }
- );
-
- _kick(
- {
- from: _lender,
- borrower: _borrower2,
- debt: 9_976.561670003961916237 * 1e18,
- collateral: 1_000 * 1e18,
- bond: 98.533942419792216457 * 1e18,
- transferAmount: 98.533942419792216457 * 1e18
- }
- );
+ _assertBorrower({
+ borrower: _borrower2,
+ borrowerDebt: 9_853.394241979221645666 * 1e18,
+ borrowerCollateral: 1_000 * 1e18,
+ borrowert0Np: 10.307611531622595991 * 1e18,
+ borrowerCollateralization: 0.986593617011217057 * 1e18
+ });
+
+ _kick({
+ from: _lender,
+ borrower: _borrower2,
+ debt: 9_976.561670003961916237 * 1e18,
+ collateral: 1_000 * 1e18,
+ bond: 98.533942419792216457 * 1e18,
+ transferAmount: 98.533942419792216457 * 1e18
+ });
_assertAuction(
AuctionParams({
@@ -216,51 +186,41 @@ contract ERC20PoolLiquidationsSettleTest is ERC20HelperContract {
neutralPrice: 10.449783245217816340 * 1e18
})
);
- _assertBorrower(
- {
- borrower: _borrower2,
- borrowerDebt: 9_976.561670003961916237 * 1e18,
- borrowerCollateral: 1_000 * 1e18,
- borrowert0Np: 10.307611531622595991 * 1e18,
- borrowerCollateralization: 0.974413448899967463 * 1e18
- }
- );
- _assertBucket(
- {
- index: _i9_91,
- lpBalance: 2_000 * 1e27,
- collateral: 0,
- deposit: 2_118.781595119199960000 * 1e18,
- exchangeRate: 1.05939079755959998 * 1e27
- }
- );
- _assertBucket(
- {
- index: _i9_81,
- lpBalance: 5_000 * 1e27,
- collateral: 0,
- deposit: 5_000 * 1e18,
- exchangeRate: 1 * 1e27
- }
- );
- _assertBucket(
- {
- index: _i9_72,
- lpBalance: 11_000 * 1e27,
- collateral: 0,
- deposit: 11_000 * 1e18,
- exchangeRate: 1 * 1e27
- }
- );
- _assertBucket(
- {
- index: _i9_62,
- lpBalance: 25_000 * 1e27,
- collateral: 0,
- deposit: 25_000 * 1e18,
- exchangeRate: 1 * 1e27
- }
- );
+ _assertBorrower({
+ borrower: _borrower2,
+ borrowerDebt: 9_976.561670003961916237 * 1e18,
+ borrowerCollateral: 1_000 * 1e18,
+ borrowert0Np: 10.307611531622595991 * 1e18,
+ borrowerCollateralization: 0.974413448899967463 * 1e18
+ });
+ _assertBucket({
+ index: _i9_91,
+ lpBalance: 2_000 * 1e18,
+ collateral: 0,
+ deposit: 2_118.781595119199960000 * 1e18,
+ exchangeRate: 1.05939079755959998 * 1e18
+ });
+ _assertBucket({
+ index: _i9_81,
+ lpBalance: 5_000 * 1e18,
+ collateral: 0,
+ deposit: 5_000 * 1e18,
+ exchangeRate: 1 * 1e18
+ });
+ _assertBucket({
+ index: _i9_72,
+ lpBalance: 11_000 * 1e18,
+ collateral: 0,
+ deposit: 11_000 * 1e18,
+ exchangeRate: 1 * 1e18
+ });
+ _assertBucket({
+ index: _i9_62,
+ lpBalance: 25_000 * 1e18,
+ collateral: 0,
+ deposit: 25_000 * 1e18,
+ exchangeRate: 1 * 1e18
+ });
// skip ahead so take can be called on the loan
skip(10 hours);
@@ -281,29 +241,25 @@ contract ERC20PoolLiquidationsSettleTest is ERC20HelperContract {
neutralPrice: 10.449783245217816340 * 1e18
})
);
-
- _assertBorrower(
- {
- borrower: _borrower2,
- borrowerDebt: 9_977.074177773911990381 * 1e18,
- borrowerCollateral: 1_000 * 1e18,
- borrowert0Np: 10.307611531622595991 * 1e18,
- borrowerCollateralization: 0.974363394700228467 * 1e18
- }
- );
+ _assertBorrower({
+ borrower: _borrower2,
+ borrowerDebt: 9_977.074177773911990381 * 1e18,
+ borrowerCollateral: 1_000 * 1e18,
+ borrowert0Np: 10.307611531622595991 * 1e18,
+ borrowerCollateralization: 0.974363394700228467 * 1e18
+ });
// take partial 800 collateral
- _take(
- {
- from: _lender,
- borrower: _borrower2,
- maxCollateral: 800 * 1e18,
- bondChange: 5.224891622608908288 * 1e18,
- givenAmount: 522.489162260890828800 * 1e18,
- collateralTaken: 800 * 1e18,
- isReward: true
- }
- );
+ _take({
+ from: _lender,
+ borrower: _borrower2,
+ maxCollateral: 800 * 1e18,
+ bondChange: 5.224891622608908288 * 1e18,
+ givenAmount: 522.489162260890828800 * 1e18,
+ collateralTaken: 800 * 1e18,
+ isReward: true
+ });
+
_assertAuction(
AuctionParams({
borrower: _borrower2,
@@ -320,83 +276,66 @@ contract ERC20PoolLiquidationsSettleTest is ERC20HelperContract {
neutralPrice: 10.449783245217816340 * 1e18
})
);
- _assertBorrower(
- {
- borrower: _borrower2,
- borrowerDebt: 10_158.205099579803908908 * 1e18,
- borrowerCollateral: 200 * 1e18,
- borrowert0Np: 10.307611531622595991 * 1e18,
- borrowerCollateralization: 0.191397904841159446 * 1e18
- }
- );
- _assertBucket(
- {
- index: _i9_91,
- lpBalance: 2_000 * 1e27,
- collateral: 0,
- deposit: 2_118.911507166546112000 * 1e18,
- exchangeRate: 1.059455753583273056000 * 1e27
- }
- );
- _assertBucket(
- {
- index: _i9_81,
- lpBalance: 5_000 * 1e27,
- collateral: 0,
- deposit: 5_000.306572531226000000 * 1e18,
- exchangeRate: 1.0000613145062452 * 1e27
- }
- );
- _assertBucket(
- {
- index: _i9_72,
- lpBalance: 11_000 * 1e27,
- collateral: 0,
- deposit: 11_000 * 1e18,
- exchangeRate: 1 * 1e27
- }
- );
- _assertBucket(
- {
- index: _i9_62,
- lpBalance: 25_000 * 1e27,
- collateral: 0,
- deposit: 25_000 * 1e18,
- exchangeRate: 1 * 1e27
- }
- );
+ _assertBorrower({
+ borrower: _borrower2,
+ borrowerDebt: 10_158.205099579803908908 * 1e18,
+ borrowerCollateral: 200 * 1e18,
+ borrowert0Np: 10.307611531622595991 * 1e18,
+ borrowerCollateralization: 0.191397904841159446 * 1e18
+ });
+ _assertBucket({
+ index: _i9_91,
+ lpBalance: 2_000 * 1e18,
+ collateral: 0,
+ deposit: 2_118.911507166546112000 * 1e18,
+ exchangeRate: 1.059455753583273056 * 1e18
+ });
+ _assertBucket({
+ index: _i9_81,
+ lpBalance: 5_000 * 1e18,
+ collateral: 0,
+ deposit: 5_000.306572531226000000 * 1e18,
+ exchangeRate: 1.0000613145062452 * 1e18
+ });
+ _assertBucket({
+ index: _i9_72,
+ lpBalance: 11_000 * 1e18,
+ collateral: 0,
+ deposit: 11_000 * 1e18,
+ exchangeRate: 1 * 1e18
+ });
+ _assertBucket({
+ index: _i9_62,
+ lpBalance: 25_000 * 1e18,
+ collateral: 0,
+ deposit: 25_000 * 1e18,
+ exchangeRate: 1 * 1e18
+ });
// settle should affect first 3 buckets, reducing deposit and incrementing collateral
skip(73 hours);
- _assertBorrower(
- {
- borrower: _borrower2,
- borrowerDebt: 10_162.015140830231868753 * 1e18,
- borrowerCollateral: 200 * 1e18,
- borrowert0Np: 10.307611531622595991 * 1e18,
- borrowerCollateralization: 0.191326144082827145 * 1e18
- }
- );
-
- _assertBucket(
- {
- index: _i9_91,
- lpBalance: 2_000 * 1e27,
- collateral: 0,
- deposit: 2_118.911507166546112000 * 1e18,
- exchangeRate: 1.059455753583273056000 * 1e27
- }
- );
-
- _settle(
- {
- from: _lender,
- borrower: _borrower2,
- maxDepth: 10,
- settledDebt: 10_019.485661146575724663 * 1e18
- }
- );
+ _assertBorrower({
+ borrower: _borrower2,
+ borrowerDebt: 10_162.015140830231868753 * 1e18,
+ borrowerCollateral: 200 * 1e18,
+ borrowert0Np: 10.307611531622595991 * 1e18,
+ borrowerCollateralization: 0.191326144082827145 * 1e18
+ });
+ _assertBucket({
+ index: _i9_91,
+ lpBalance: 2_000 * 1e18,
+ collateral: 0,
+ deposit: 2_118.911507166546112000 * 1e18,
+ exchangeRate: 1.059455753583273056 * 1e18
+ });
+
+ _settle({
+ from: _lender,
+ borrower: _borrower2,
+ maxDepth: 10,
+ settledDebt: 10_019.485661146575724663 * 1e18
+ });
_assertAuction(
AuctionParams({
@@ -414,54 +353,41 @@ contract ERC20PoolLiquidationsSettleTest is ERC20HelperContract {
neutralPrice: 0
})
);
- _assertBorrower(
- {
- borrower: _borrower2,
- borrowerDebt: 0,
- borrowerCollateral: 0,
- borrowert0Np: 10.307611531622595991 * 1e18,
- borrowerCollateralization: 1 * 1e18
- }
- );
- _assertBucket(
- {
- index: _i9_91,
- lpBalance: 2_000 * 1e27,
- collateral: 200 * 1e18,
- deposit: 0,
- exchangeRate: 0.9917184843435912074 * 1e27
- }
- );
- _assertBucket(
- {
- index: _i9_81,
- lpBalance: 0,
- collateral: 0,
- deposit: 0,
- exchangeRate: 1 * 1e27
- }
- );
-
- _assertBucket(
- {
- index: _i9_72,
- lpBalance: 11_000 * 1e27,
- collateral: 0,
- deposit: 8_807.325768325035155556 * 1e18,
- exchangeRate: 0.800665978938639559596000000 * 1e27
- }
- );
-
- _assertBucket(
- {
- index: _i9_62,
- lpBalance: 25_000 * 1e27,
- collateral: 0,
- deposit: 25_000 * 1e18,
- exchangeRate: 1 * 1e27
- }
- );
-
+ _assertBorrower({
+ borrower: _borrower2,
+ borrowerDebt: 0,
+ borrowerCollateral: 0,
+ borrowert0Np: 10.307611531622595991 * 1e18,
+ borrowerCollateralization: 1 * 1e18
+ });
+ _assertBucket({
+ index: _i9_91,
+ lpBalance: 2_000 * 1e18,
+ collateral: 200 * 1e18,
+ deposit: 0,
+ exchangeRate: 0.991718484343591207 * 1e18
+ });
+ _assertBucket({
+ index: _i9_81,
+ lpBalance: 0,
+ collateral: 0,
+ deposit: 0,
+ exchangeRate: 1 * 1e18
+ });
+ _assertBucket({
+ index: _i9_72,
+ lpBalance: 11_000 * 1e18,
+ collateral: 0,
+ deposit: 8_807.325768325035155556 * 1e18,
+ exchangeRate: 0.800665978938639560 * 1e18
+ });
+ _assertBucket({
+ index: _i9_62,
+ lpBalance: 25_000 * 1e18,
+ collateral: 0,
+ deposit: 25_000 * 1e18,
+ exchangeRate: 1 * 1e18
+ });
_assertPool(
PoolParams({
htp: 9.910303333009215085 * 1e18,
@@ -483,14 +409,12 @@ contract ERC20PoolLiquidationsSettleTest is ERC20HelperContract {
function testSettleOnAuctionKicked72HoursAgo() external tearDown {
// Borrower2 borrows
- _borrow(
- {
- from: _borrower2,
- amount: 1_730 * 1e18,
- indexLimit: _i9_72,
- newLup: 9.721295865031779605 * 1e18
- }
- );
+ _borrow({
+ from: _borrower2,
+ amount: 1_730 * 1e18,
+ indexLimit: _i9_72,
+ newLup: 9.721295865031779605 * 1e18
+ });
// Skip to make borrower undercollateralized
skip(100 days);
@@ -511,27 +435,23 @@ contract ERC20PoolLiquidationsSettleTest is ERC20HelperContract {
neutralPrice: 0
})
);
+ _assertBorrower({
+ borrower: _borrower2,
+ borrowerDebt: 9_853.394241979221645666 * 1e18,
+ borrowerCollateral: 1_000 * 1e18,
+ borrowert0Np: 10.307611531622595991 * 1e18,
+ borrowerCollateralization: 0.986593617011217057 * 1e18
+ });
+
+ _kick({
+ from: _lender,
+ borrower: _borrower2,
+ debt: 9_976.561670003961916237 * 1e18,
+ collateral: 1_000 * 1e18,
+ bond: 98.533942419792216457 * 1e18,
+ transferAmount: 98.533942419792216457 * 1e18
+ });
- _assertBorrower(
- {
- borrower: _borrower2,
- borrowerDebt: 9_853.394241979221645666 * 1e18,
- borrowerCollateral: 1_000 * 1e18,
- borrowert0Np: 10.307611531622595991 * 1e18,
- borrowerCollateralization: 0.986593617011217057 * 1e18
- }
- );
-
- _kick(
- {
- from: _lender,
- borrower: _borrower2,
- debt: 9_976.561670003961916237 * 1e18,
- collateral: 1_000 * 1e18,
- bond: 98.533942419792216457 * 1e18,
- transferAmount: 98.533942419792216457 * 1e18
- }
- );
_assertAuction(
AuctionParams({
borrower: _borrower2,
@@ -548,51 +468,41 @@ contract ERC20PoolLiquidationsSettleTest is ERC20HelperContract {
neutralPrice: 10.449783245217816340 * 1e18
})
);
- _assertBorrower(
- {
- borrower: _borrower2,
- borrowerDebt: 9_976.561670003961916237 * 1e18,
- borrowerCollateral: 1_000 * 1e18,
- borrowert0Np: 10.307611531622595991 * 1e18,
- borrowerCollateralization: 0.974413448899967463 * 1e18
- }
- );
- _assertBucket(
- {
- index: _i9_91,
- lpBalance: 2_000 * 1e27,
- collateral: 0,
- deposit: 2_118.781595119199960000 * 1e18,
- exchangeRate: 1.05939079755959998 * 1e27
- }
- );
- _assertBucket(
- {
- index: _i9_81,
- lpBalance: 5_000 * 1e27,
- collateral: 0,
- deposit: 5_000 * 1e18,
- exchangeRate: 1 * 1e27
- }
- );
- _assertBucket(
- {
- index: _i9_72,
- lpBalance: 11_000 * 1e27,
- collateral: 0,
- deposit: 11_000 * 1e18,
- exchangeRate: 1 * 1e27
- }
- );
- _assertBucket(
- {
- index: _i9_62,
- lpBalance: 25_000 * 1e27,
- collateral: 0,
- deposit: 25_000 * 1e18,
- exchangeRate: 1 * 1e27
- }
- );
+ _assertBorrower({
+ borrower: _borrower2,
+ borrowerDebt: 9_976.561670003961916237 * 1e18,
+ borrowerCollateral: 1_000 * 1e18,
+ borrowert0Np: 10.307611531622595991 * 1e18,
+ borrowerCollateralization: 0.974413448899967463 * 1e18
+ });
+ _assertBucket({
+ index: _i9_91,
+ lpBalance: 2_000 * 1e18,
+ collateral: 0,
+ deposit: 2_118.781595119199960000 * 1e18,
+ exchangeRate: 1.05939079755959998 * 1e18
+ });
+ _assertBucket({
+ index: _i9_81,
+ lpBalance: 5_000 * 1e18,
+ collateral: 0,
+ deposit: 5_000 * 1e18,
+ exchangeRate: 1 * 1e18
+ });
+ _assertBucket({
+ index: _i9_72,
+ lpBalance: 11_000 * 1e18,
+ collateral: 0,
+ deposit: 11_000 * 1e18,
+ exchangeRate: 1 * 1e18
+ });
+ _assertBucket({
+ index: _i9_62,
+ lpBalance: 25_000 * 1e18,
+ collateral: 0,
+ deposit: 25_000 * 1e18,
+ exchangeRate: 1 * 1e18
+ });
// settle should work on an kicked auction if 72 hours passed from kick time
// settle should affect first 3 buckets, reducing deposit and incrementing collateral
@@ -614,25 +524,21 @@ contract ERC20PoolLiquidationsSettleTest is ERC20HelperContract {
neutralPrice: 10.449783245217816340 * 1e18
})
);
+ _assertBorrower({
+ borrower: _borrower2,
+ borrowerDebt: 9_980.303582194898667001 * 1e18,
+ borrowerCollateral: 1_000 * 1e18,
+ borrowert0Np: 10.307611531622595991 * 1e18,
+ borrowerCollateralization: 0.974048112361512224 * 1e18
+ });
+
+ _settle({
+ from: _lender,
+ borrower: _borrower2,
+ maxDepth: 10,
+ settledDebt: 9_840.828245192307696845 * 1e18
+ });
- _assertBorrower(
- {
- borrower: _borrower2,
- borrowerDebt: 9_980.303582194898667001 * 1e18,
- borrowerCollateral: 1_000 * 1e18,
- borrowert0Np: 10.307611531622595991 * 1e18,
- borrowerCollateralization: 0.974048112361512224 * 1e18
- }
- );
-
- _settle(
- {
- from: _lender,
- borrower: _borrower2,
- maxDepth: 10,
- settledDebt: 9_840.828245192307696845 * 1e18
- }
- );
_assertAuction(
AuctionParams({
borrower: _borrower2,
@@ -649,85 +555,71 @@ contract ERC20PoolLiquidationsSettleTest is ERC20HelperContract {
neutralPrice: 0
})
);
- _assertBorrower(
- {
- borrower: _borrower2,
- borrowerDebt: 0,
- borrowerCollateral: 0,
- borrowert0Np: 10.307611531622595991 * 1e18,
- borrowerCollateralization: 1 * 1e18
- }
- );
- _assertBucket(
- {
- index: _i9_91,
- lpBalance: 2_000 * 1e27,
- collateral: 213.743127712733065764 * 1e18,
- deposit: 0,
- exchangeRate: 1.059865053270651414002083680 * 1e27
- }
- );
- _assertBucket(
- {
- index: _i9_81,
- lpBalance: 5_000 * 1e27,
- collateral: 509.457659688392150697 * 1e18,
- deposit: 0,
- exchangeRate: 1.000447668331784572999225097 * 1e27
- }
- );
- _assertBucket(
- {
- index: _i9_72,
- lpBalance: 11_000 * 1e27,
- collateral: 276.799212598874783539 * 1e18,
- deposit: 8_289.734142970131967959 * 1e18,
- exchangeRate: 0.998234653077420534042741275 * 1e27
- }
- );
- _assertBucket(
- {
- index: _i9_62,
- lpBalance: 25_000 * 1e27,
- collateral: 0,
- deposit: 25_000 * 1e18,
- exchangeRate: 1 * 1e27
- }
- );
+ _assertBorrower({
+ borrower: _borrower2,
+ borrowerDebt: 0,
+ borrowerCollateral: 0,
+ borrowert0Np: 10.307611531622595991 * 1e18,
+ borrowerCollateralization: 1 * 1e18
+ });
+ _assertBucket({
+ index: _i9_91,
+ lpBalance: 2_000 * 1e18,
+ collateral: 213.743127712733065764 * 1e18,
+ deposit: 0,
+ exchangeRate: 1.059865053270651414 * 1e18
+ });
+ _assertBucket({
+ index: _i9_81,
+ lpBalance: 5_000 * 1e18,
+ collateral: 509.457659688392150697 * 1e18,
+ deposit: 0,
+ exchangeRate: 1.000447668331784573 * 1e18
+ });
+ _assertBucket({
+ index: _i9_72,
+ lpBalance: 11_000 * 1e18,
+ collateral: 276.799212598874783539 * 1e18,
+ deposit: 8_289.734142970131967959 * 1e18,
+ exchangeRate: 0.998234653077420534 * 1e18
+ });
+ _assertBucket({
+ index: _i9_62,
+ lpBalance: 25_000 * 1e18,
+ collateral: 0,
+ deposit: 25_000 * 1e18,
+ exchangeRate: 1 * 1e18
+ });
}
function testSettleAuctionReverts() external tearDown {
// Borrower2 borrows
- _borrow(
- {
- from: _borrower2,
- amount: 1_730 * 1e18,
- indexLimit: _i9_72,
- newLup: 9.721295865031779605 * 1e18
- }
- );
+ _borrow({
+ from: _borrower2,
+ amount: 1_730 * 1e18,
+ indexLimit: _i9_72,
+ newLup: 9.721295865031779605 * 1e18
+ });
// Skip to make borrower undercollateralized
skip(100 days);
// settle should revert on a borrower that is not auctioned
- _assertSettleOnNotKickedAuctionRevert(
- {
- from: _lender,
- borrower: _borrower2
- }
- );
+ _assertSettleOnNotKickedAuctionRevert({
+ from: _lender,
+ borrower: _borrower2
+ });
uint256 kickTime = _startTime + 100 days;
- _kick(
- {
- from: _lender,
- borrower: _borrower2,
- debt: 9_976.561670003961916237 * 1e18,
- collateral: 1_000 * 1e18,
- bond: 98.533942419792216457 * 1e18,
- transferAmount: 98.533942419792216457 * 1e18
- }
- );
+
+ _kick({
+ from: _lender,
+ borrower: _borrower2,
+ debt: 9_976.561670003961916237 * 1e18,
+ collateral: 1_000 * 1e18,
+ bond: 98.533942419792216457 * 1e18,
+ transferAmount: 98.533942419792216457 * 1e18
+ });
+
_assertAuction(
AuctionParams({
borrower: _borrower2,
@@ -744,23 +636,20 @@ contract ERC20PoolLiquidationsSettleTest is ERC20HelperContract {
neutralPrice: 10.449783245217816340 * 1e18
})
);
- _assertBorrower(
- {
- borrower: _borrower2,
- borrowerDebt: 9_976.561670003961916237 * 1e18,
- borrowerCollateral: 1_000 * 1e18,
- borrowert0Np: 10.307611531622595991 * 1e18,
- borrowerCollateralization: 0.974413448899967463 * 1e18
- }
- );
+ _assertBorrower({
+ borrower: _borrower2,
+ borrowerDebt: 9_976.561670003961916237 * 1e18,
+ borrowerCollateral: 1_000 * 1e18,
+ borrowert0Np: 10.307611531622595991 * 1e18,
+ borrowerCollateralization: 0.974413448899967463 * 1e18
+ });
// settle should revert on an kicked auction but 72 hours not passed (there's still debt to settle and collateral to be auctioned)
- _assertSettleOnNotClearableAuctionRevert(
- {
- from: _lender,
- borrower: _borrower2
- }
- );
+ _assertSettleOnNotClearableAuctionRevert({
+ from: _lender,
+ borrower: _borrower2
+ });
+
// skip ahead so take can be called on the loan
skip(10 hours);
@@ -780,218 +669,208 @@ contract ERC20PoolLiquidationsSettleTest is ERC20HelperContract {
neutralPrice: 10.449783245217816340 * 1e18
})
);
-
- _assertBorrower(
- {
- borrower: _borrower2,
- borrowerDebt: 9_977.074177773911990381 * 1e18,
- borrowerCollateral: 1_000 * 1e18,
- borrowert0Np: 10.307611531622595991 * 1e18,
- borrowerCollateralization: 0.974363394700228467 * 1e18
- }
- );
+ _assertBorrower({
+ borrower: _borrower2,
+ borrowerDebt: 9_977.074177773911990381 * 1e18,
+ borrowerCollateral: 1_000 * 1e18,
+ borrowert0Np: 10.307611531622595991 * 1e18,
+ borrowerCollateralization: 0.974363394700228467 * 1e18
+ });
// take entire collateral
- _take(
- {
- from: _lender,
- borrower: _borrower2,
- maxCollateral: 1_000 * 1e18,
- bondChange: 6.531114528261135360 * 1e18,
- givenAmount: 653.111452826113536000 * 1e18,
- collateralTaken: 1_000 * 1e18,
- isReward: true
- }
- );
+ _take({
+ from: _lender,
+ borrower: _borrower2,
+ maxCollateral: 1_000 * 1e18,
+ bondChange: 6.531114528261135360 * 1e18,
+ givenAmount: 653.111452826113536000 * 1e18,
+ collateralTaken: 1_000 * 1e18,
+ isReward: true
+ });
// remove quote tokens should fail since auction head is clearable
- _assertRemoveLiquidityAuctionNotClearedRevert(
- {
- from: _lender,
- amount: 1_000 * 1e18,
- index: _i9_52
- }
- );
- _assertRemoveAllLiquidityAuctionNotClearedRevert(
- {
- from: _lender,
- index: _i9_52
- }
- );
+ _assertRemoveLiquidityAuctionNotClearedRevert({
+ from: _lender,
+ amount: 1_000 * 1e18,
+ index: _i9_52
+ });
+
+ _assertRemoveAllLiquidityAuctionNotClearedRevert({
+ from: _lender,
+ index: _i9_52
+ });
+
// remove collateral should fail since auction head is clearable
- _assertRemoveCollateralAuctionNotClearedRevert(
- {
- from: _lender,
- amount: 10 * 1e18,
- index: _i9_52
- }
- );
+ _assertRemoveCollateralAuctionNotClearedRevert({
+ from: _lender,
+ amount: 10 * 1e18,
+ index: _i9_52
+ });
// remove all collateral should fail since auction head is clearable
- _assertRemoveAllCollateralAuctionNotClearedRevert(
- {
- from: _lender,
- index: _i9_52
- }
- );
+ _assertRemoveAllCollateralAuctionNotClearedRevert({
+ from: _lender,
+ 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: 94.388085261495553046979329248 * 1e27,
- newLup: 9.721295865031779605 * 1e18
- }
- );
- _assertLenderLpBalance(
- {
- lender: _lender1,
- index: _i9_91,
- lpBalance: 94.388085261495553046979329248 * 1e27,
- depositTime: _startTime + 100 days + 10 hours
- }
- );
+ _addLiquidity({
+ from: _lender1,
+ amount: 100 * 1e18,
+ index: _i9_91,
+ lpAward: 94.388085261495553047 * 1e18,
+ newLup: 9.721295865031779605 * 1e18
+ });
+
+ _assertLenderLpBalance({
+ lender: _lender1,
+ index: _i9_91,
+ lpBalance: 94.388085261495553047 * 1e18,
+ depositTime: _startTime + 100 days + 10 hours
+ });
+
// adding to a different bucket for testing move in same block with bucket bankruptcy
- _addLiquidity(
- {
- from: _lender1,
- amount: 100 * 1e18,
- index: _i9_52,
- lpAward: 100 * 1e27,
- newLup: 9.721295865031779605 * 1e18
- }
- );
+ _addLiquidity({
+ from: _lender1,
+ amount: 100 * 1e18,
+ index: _i9_52,
+ lpAward: 100 * 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(
- {
- borrower: _borrower2,
- borrowerDebt: 10_028.889031920233428707 * 1e18,
- borrowerCollateral: 0,
- borrowert0Np: 10.307611531622595991 * 1e18,
- borrowerCollateralization: 0
- }
- );
+ _assertBorrower({
+ borrower: _borrower2,
+ borrowerDebt: 10_028.889031920233428707 * 1e18,
+ borrowerCollateral: 0,
+ borrowert0Np: 10.307611531622595991 * 1e18,
+ borrowerCollateralization: 0
+ });
assertTrue(block.timestamp - kickTime < 72 hours); // assert auction was kicked less than 72 hours ago
- _settle(
- {
- from: _lender,
- borrower: _borrower2,
- maxDepth: 10,
- settledDebt: 9_891.935520844277346922 * 1e18
- }
- );
+
+ _settle({
+ from: _lender,
+ borrower: _borrower2,
+ maxDepth: 10,
+ settledDebt: 9_891.935520844277346922 * 1e18
+ });
// bucket is insolvent, balances are resetted
- _assertBucket(
- {
- index: _i9_91,
- lpBalance: 0, // bucket is bankrupt
- collateral: 0,
- deposit: 0,
- exchangeRate: 1 * 1e27
- }
- );
+ _assertBucket({
+ index: _i9_91,
+ lpBalance: 0, // bucket is bankrupt
+ collateral: 0,
+ deposit: 0,
+ exchangeRate: 1 * 1e18
+ });
// after bucket bankruptcy lenders balance is zero
- _assertLenderLpBalance(
- {
- lender: _lender,
- index: _i9_91,
- lpBalance: 0,
- depositTime: _startTime
- }
- );
- _assertLenderLpBalance(
- {
- lender: _lender1,
- index: _i9_91,
- lpBalance: 0,
- depositTime: _startTime + 100 days + 10 hours
- }
- );
+ _assertLenderLpBalance({
+ lender: _lender,
+ index: _i9_91,
+ lpBalance: 0,
+ depositTime: _startTime
+ });
+ _assertLenderLpBalance({
+ lender: _lender1,
+ index: _i9_91,
+ lpBalance: 0,
+ depositTime: _startTime + 100 days + 10 hours
+ });
+
// cannot add liquidity in same block when bucket marked insolvent
- _assertAddLiquidityBankruptcyBlockRevert(
- {
- from: _lender1,
- amount: 1_000 * 1e18,
- index: _i9_91
- }
- );
+ _assertAddLiquidityBankruptcyBlockRevert({
+ from: _lender1,
+ amount: 1_000 * 1e18,
+ index: _i9_91
+ });
+
// cannot add collateral in same block when bucket marked insolvent
- _assertAddCollateralBankruptcyBlockRevert(
- {
- from: _lender1,
- amount: 10 * 1e18,
- index: _i9_91
- }
- );
+ _assertAddCollateralBankruptcyBlockRevert({
+ from: _lender1,
+ amount: 10 * 1e18,
+ index: _i9_91
+ });
+
// cannot move LPs in same block when bucket marked insolvent
- _assertMoveLiquidityBankruptcyBlockRevert(
- {
- from: _lender1,
- amount: 10 * 1e18,
- fromIndex: _i9_52,
- toIndex: _i9_91
- }
- );
+ _assertMoveLiquidityBankruptcyBlockRevert({
+ from: _lender1,
+ amount: 10 * 1e18,
+ fromIndex: _i9_52,
+ toIndex: _i9_91
+ });
// all operations should work if not in same block
skip(1 hours);
- _pool.addQuoteToken(100 * 1e18, _i9_91);
- _pool.moveQuoteToken(10 * 1e18, _i9_52, _i9_91);
- ERC20Pool(address(_pool)).addCollateral(4 * 1e18, _i9_91);
- _assertLenderLpBalance(
- {
- lender: _lender1,
- index: _i9_91,
- lpBalance: 149.668739373743648296000000000 * 1e27,
- depositTime: _startTime + 100 days + 10 hours + 1 hours
- }
- );
+
+ _pool.addQuoteToken(100 * 1e18, _i9_91, block.timestamp + 1 minutes);
+ _pool.moveQuoteToken(10 * 1e18, _i9_52, _i9_91, block.timestamp + 1 minutes);
+ ERC20Pool(address(_pool)).addCollateral(4 * 1e18, _i9_91, block.timestamp + 1 minutes);
+
+ _assertLenderLpBalance({
+ lender: _lender1,
+ index: _i9_91,
+ lpBalance: 149.668739373743648296 * 1e18,
+ depositTime: _startTime + 100 days + 10 hours + 1 hours
+ });
// bucket is healthy again
- _assertBucket(
- {
- index: _i9_91,
- lpBalance: 149.668739373743648296000000000 * 1e27,
- collateral: 4 * 1e18,
- deposit: 110.0000000000000000000000 * 1e18,
- exchangeRate: 1.00000000000000000000000000 * 1e27
- }
- );
+ _assertBucket({
+ index: _i9_91,
+ lpBalance: 149.668739373743648296 * 1e18,
+ collateral: 4 * 1e18,
+ deposit: 110 * 1e18,
+ exchangeRate: 1 * 1e18
+ });
// when moving to a bucket that was marked insolvent, the deposit time should be the greater between from bucket deposit time and insolvency time + 1
changePrank(_lender);
- _assertLenderLpBalance(
- {
- lender: _lender,
- index: _i9_91,
- lpBalance: 0,
- depositTime: _startTime
- }
- );
- _pool.moveQuoteToken(1_000 * 1e18, _i9_52, _i9_91);
- _assertLenderLpBalance(
- {
- lender: _lender,
- index: _i9_91,
- lpBalance: 1_000.000000000000000000000000000 * 1e27,
- depositTime: _startTime + 100 days + 10 hours + 1 // _i9_91 bucket insolvency time + 1 (since deposit in _i9_52 from bucket was done before _i9_91 target bucket become insolvent)
- }
- );
- _pool.addQuoteToken(1_000 * 1e18, _i9_52);
- _pool.moveQuoteToken(1_000 * 1e18, _i9_52, _i9_91);
- _assertLenderLpBalance(
- {
- lender: _lender,
- index: _i9_91,
- lpBalance: 1_999.999999999999999999130185000 * 1e27,
- depositTime: _startTime + 100 days + 10 hours + 1 hours // time of deposit in _i9_52 from bucket (since deposit in _i9_52 from bucket was done after _i9_91 target bucket become insolvent)
- }
- );
+ _assertLenderLpBalance({
+ lender: _lender,
+ index: _i9_91,
+ lpBalance: 0,
+ depositTime: _startTime
+ });
+
+ _pool.moveQuoteToken(1_000 * 1e18, _i9_52, _i9_91, block.timestamp + 1 minutes);
+
+ _assertLenderLpBalance({
+ lender: _lender,
+ index: _i9_91,
+ lpBalance: 1_000 * 1e18,
+ depositTime: _startTime + 100 days + 10 hours + 1 // _i9_91 bucket insolvency time + 1 (since deposit in _i9_52 from bucket was done before _i9_91 target bucket become insolvent)
+ });
+
+ _pool.addQuoteToken(1_000 * 1e18, _i9_52, block.timestamp + 1 minutes);
+ _pool.moveQuoteToken(1_000 * 1e18, _i9_52, _i9_91, block.timestamp + 1 minutes);
+
+ _assertLenderLpBalance({
+ lender: _lender,
+ index: _i9_91,
+ lpBalance: 2_000 * 1e18,
+ depositTime: _startTime + 100 days + 10 hours + 1 hours // time of deposit in _i9_52 from bucket (since deposit in _i9_52 from bucket was done after _i9_91 target bucket become insolvent)
+ });
+
+ // ensure bucket bankruptcy when moving amounts from an unbalanced bucket leave bucket healthy
+ _assertBucket({
+ index: _i9_72,
+ lpBalance: 11_000 * 1e18,
+ collateral: 0 * 1e18,
+ deposit: 9_035.875749431291350856 * 1e18,
+ exchangeRate: 0.821443249948299214 * 1e18
+ });
+
+ vm.expectEmit(true, true, false, true);
+ emit BucketBankruptcy(_i9_72, 3827);
+ _pool.moveQuoteToken(10000000000 * 1e18, _i9_72, _i9_91, type(uint256).max);
+
+ _assertBucket({
+ index: _i9_72,
+ lpBalance: 0,
+ collateral: 0 * 1e18,
+ deposit: 0,
+ exchangeRate: 1 * 1e18
+ });
}
}
diff --git a/tests/forge/ERC20Pool/ERC20PoolLiquidationsTake.t.sol b/tests/forge/ERC20Pool/ERC20PoolLiquidationsTake.t.sol
index 6a539dd8d..db55f6fd8 100644
--- a/tests/forge/ERC20Pool/ERC20PoolLiquidationsTake.t.sol
+++ b/tests/forge/ERC20Pool/ERC20PoolLiquidationsTake.t.sol
@@ -26,75 +26,57 @@ contract ERC20PoolLiquidationsTakeTest is ERC20HelperContract {
_mintCollateralAndApproveTokens(_lender1, 4 * 1e18);
// Lender adds Quote token accross 5 prices
- _addInitialLiquidity(
- {
- from: _lender,
- amount: 2_000 * 1e18,
- index: _i9_91
- }
- );
- _addInitialLiquidity(
- {
- from: _lender,
- amount: 5_000 * 1e18,
- index: _i9_81
- }
- );
- _addInitialLiquidity(
- {
- from: _lender,
- amount: 11_000 * 1e18,
- index: _i9_72
- }
- );
- _addInitialLiquidity(
- {
- from: _lender,
- amount: 25_000 * 1e18,
- index: _i9_62
- }
- );
- _addInitialLiquidity(
- {
- from: _lender,
- amount: 30_000 * 1e18,
- index: _i9_52
- }
- );
+ _addInitialLiquidity({
+ from: _lender,
+ amount: 2_000 * 1e18,
+ index: _i9_91
+ });
+ _addInitialLiquidity({
+ from: _lender,
+ amount: 5_000 * 1e18,
+ index: _i9_81
+ });
+ _addInitialLiquidity({
+ from: _lender,
+ amount: 11_000 * 1e18,
+ index: _i9_72
+ });
+ _addInitialLiquidity({
+ from: _lender,
+ amount: 25_000 * 1e18,
+ index: _i9_62
+ });
+ _addInitialLiquidity({
+ from: _lender,
+ amount: 30_000 * 1e18,
+ index: _i9_52
+ });
// first borrower adds collateral token and borrows
- _pledgeCollateral(
- {
- from: _borrower,
- borrower: _borrower,
- amount: 2 * 1e18
- }
- );
- _borrow(
- {
- from: _borrower,
- amount: 19.25 * 1e18,
- indexLimit: _i9_91,
- newLup: 9.917184843435912074 * 1e18
- }
- );
+ _pledgeCollateral({
+ from: _borrower,
+ borrower: _borrower,
+ amount: 2 * 1e18
+ });
+ _borrow({
+ from: _borrower,
+ amount: 19.25 * 1e18,
+ indexLimit: _i9_91,
+ newLup: 9.917184843435912074 * 1e18
+ });
// second borrower adds collateral token and borrows
- _pledgeCollateral(
- {
- from: _borrower2,
- borrower: _borrower2,
- amount: 1_000 * 1e18
- }
- );
- _borrow(
- {
- from: _borrower2,
- amount: 7_980 * 1e18,
- indexLimit: _i9_72,
- newLup: 9.721295865031779605 * 1e18
- }
- );
+ _pledgeCollateral({
+ from: _borrower2,
+ borrower: _borrower2,
+ amount: 1_000 * 1e18
+ });
+ _borrow({
+ from: _borrower2,
+ amount: 7_980 * 1e18,
+ indexLimit: _i9_72,
+ newLup: 9.721295865031779605 * 1e18
+ });
/*****************************/
/*** Assert pre-kick state ***/
@@ -117,81 +99,68 @@ contract ERC20PoolLiquidationsTakeTest is ERC20HelperContract {
interestRateUpdate: _startTime
})
);
- _assertBorrower(
- {
- borrower: _borrower,
- borrowerDebt: 19.268509615384615394 * 1e18,
- borrowerCollateral: 2 * 1e18,
- borrowert0Np: 10.115967548076923081 * 1e18,
- borrowerCollateralization: 1.009034539679184679 * 1e18
- }
- );
- _assertBorrower(
- {
- borrower: _borrower2,
- borrowerDebt: 7_987.673076923076926760 * 1e18,
- borrowerCollateral: 1_000 * 1e18,
- borrowert0Np: 8.471136974495192174 * 1e18,
- borrowerCollateralization: 1.217037273735858713 * 1e18
- }
- );
- _assertReserveAuction(
- {
- reserves: 7.691586538461542154 * 1e18,
- claimableReserves : 0,
- claimableReservesRemaining: 0,
- auctionPrice: 0,
- timeRemaining: 0
- }
- );
+ _assertBorrower({
+ borrower: _borrower,
+ borrowerDebt: 19.268509615384615394 * 1e18,
+ borrowerCollateral: 2 * 1e18,
+ borrowert0Np: 10.115967548076923081 * 1e18,
+ borrowerCollateralization: 1.009034539679184679 * 1e18
+ });
+ _assertBorrower({
+ borrower: _borrower2,
+ borrowerDebt: 7_987.673076923076926760 * 1e18,
+ borrowerCollateral: 1_000 * 1e18,
+ borrowert0Np: 8.471136974495192174 * 1e18,
+ borrowerCollateralization: 1.217037273735858713 * 1e18
+ });
+ _assertReserveAuction({
+ reserves: 7.691586538461542154 * 1e18,
+ claimableReserves : 0,
+ claimableReservesRemaining: 0,
+ auctionPrice: 0,
+ timeRemaining: 0
+ });
+
assertEq(_quote.balanceOf(_lender), 47_000 * 1e18);
// should revert if there's no auction started
- _assertTakeNoAuctionRevert(
- {
- from: _lender,
- borrower: _borrower,
- maxCollateral: 10 * 1e18
- }
- );
+ _assertTakeNoAuctionRevert({
+ from: _lender,
+ borrower: _borrower,
+ maxCollateral: 10 * 1e18
+ });
}
function testTakeCoolDownPeriod() external tearDown {
// should revert if there's no auction started
- _assertTakeNoAuctionRevert(
- {
- from: _lender,
- borrower: _borrower,
- maxCollateral: 10 * 1e18
- }
- );
+ _assertTakeNoAuctionRevert({
+ from: _lender,
+ borrower: _borrower,
+ maxCollateral: 10 * 1e18
+ });
/********************/
/*** Kick Auction ***/
/********************/
- _borrow(
- {
- from: _borrower2,
- amount: 1_700.0 * 1e18,
- indexLimit: _p9_72,
- newLup: _p9_72
- }
- );
+ _borrow({
+ from: _borrower2,
+ amount: 1_700.0 * 1e18,
+ indexLimit: _i9_72,
+ newLup: _p9_72
+ });
skip(100 days);
- _kick(
- {
- from: _lender,
- borrower: _borrower2,
- debt: 9_945.738101507554206918 * 1e18,
- collateral: 1_000 * 1e18,
- bond: 98.229512113654856365 * 1e18,
- transferAmount: 98.229512113654856365 * 1e18
- }
- );
+ _kick({
+ from: _lender,
+ borrower: _borrower2,
+ debt: 9_945.738101507554206918 * 1e18,
+ collateral: 1_000 * 1e18,
+ bond: 98.229512113654856365 * 1e18,
+ transferAmount: 98.229512113654856365 * 1e18
+ });
/********************/
/*** Take Auction ***/
@@ -200,54 +169,43 @@ contract ERC20PoolLiquidationsTakeTest is ERC20HelperContract {
skip(30 minutes);
// should revert if still in cool down period
- _assertTakeAuctionInCooldownRevert(
- {
- from: _lender,
- borrower: _borrower2,
- maxCollateral: 10 * 1e18
- }
- );
+ _assertTakeAuctionInCooldownRevert({
+ from: _lender,
+ borrower: _borrower2,
+ maxCollateral: 10 * 1e18
+ });
}
function testTakeLoanColConstraintBpfPosNoResidual() external tearDown {
// Increase neutralPrice so it exceeds TP
- _addLiquidity(
- {
- from: _lender,
- amount: 10_000 * 1e18,
- index: _i1505_26,
- lpAward: 10_000 * 1e27,
- newLup: _p1505_26
- }
- );
-
- _pledgeCollateral(
- {
- from: _borrower,
- borrower: _borrower,
- amount: 1_000 * 1e18
- }
- );
-
- _borrow(
- {
- from: _borrower,
- amount: 9_020 * 1e18,
- indexLimit: _i9_72,
- newLup: _p9_72
- }
- );
+ _addLiquidity({
+ from: _lender,
+ amount: 10_000 * 1e18,
+ index: _i1505_26,
+ lpAward: 10_000 * 1e18,
+ newLup: _p1505_26
+ });
+
+ _pledgeCollateral({
+ from: _borrower,
+ borrower: _borrower,
+ amount: 1_000 * 1e18
+ });
+ _borrow({
+ from: _borrower,
+ amount: 9_020 * 1e18,
+ indexLimit: _i9_72,
+ newLup: _p9_72
+ });
// calling borrow stamps loan with new t0NeutralPrice
- _borrow(
- {
- from: _borrower2,
- amount: 1_700.0 * 1e18,
- indexLimit: _p9_72,
- newLup: _p9_72
- }
- );
+ _borrow({
+ from: _borrower2,
+ amount: 1_700.0 * 1e18,
+ indexLimit: _i9_72,
+ newLup: _p9_72
+ });
_assertPool(
PoolParams({
@@ -266,16 +224,13 @@ contract ERC20PoolLiquidationsTakeTest is ERC20HelperContract {
interestRateUpdate: block.timestamp
})
);
-
- _assertBorrower(
- {
- borrower: _borrower2,
- borrowerDebt: 9_689.307692307692312160* 1e18,
- borrowerCollateral: 1_000 * 1e18,
- borrowert0Np: 1_575.326150647652569911 * 1e18,
- borrowerCollateralization: 1.003301388885552947 * 1e18
- }
- );
+ _assertBorrower({
+ borrower: _borrower2,
+ borrowerDebt: 9_689.307692307692312160* 1e18,
+ borrowerCollateral: 1_000 * 1e18,
+ borrowert0Np: 1_575.326150647652569911 * 1e18,
+ borrowerCollateralization: 1.003301388885552947 * 1e18
+ });
skip(100 days);
@@ -296,8 +251,6 @@ contract ERC20PoolLiquidationsTakeTest is ERC20HelperContract {
interestRateUpdate: block.timestamp - 100 days
})
);
-
-
_assertAuction(
AuctionParams({
borrower: _borrower2,
@@ -314,55 +267,42 @@ contract ERC20PoolLiquidationsTakeTest is ERC20HelperContract {
neutralPrice: 0
})
);
-
- _assertBorrower(
- {
- borrower: _borrower2,
- borrowerDebt: 9_822.951211365485636462* 1e18,
- borrowerCollateral: 1_000 * 1e18,
- borrowert0Np: 1_575.326150647652569911 * 1e18,
- borrowerCollateralization: 0.989651241857326201 * 1e18
- }
- );
-
- _kick(
- {
- from: _lender,
- borrower: _borrower2,
- debt: 9_945.738101507554206918 * 1e18,
- collateral: 1_000 * 1e18,
- bond: 2_946.885363409645690939 * 1e18,
- transferAmount: 2_946.885363409645690939 * 1e18
- }
- );
-
- _assertKicker(
- {
- kicker: _lender,
- claimable: 0,
- locked: 2_946.885363409645690939 * 1e18
- }
- );
-
- _assertBorrower(
- {
- borrower: _borrower2,
- borrowerDebt: 9_945.738101507554206918 * 1e18,
- borrowerCollateral: 1_000 * 1e18,
- borrowert0Np: 1_575.326150647652569911 * 1e18,
- borrowerCollateralization: 0.977433325291186371 * 1e18
- }
- );
-
- _assertReserveAuction(
- {
- reserves: 176.383108065231049467 * 1e18,
- claimableReserves : 80.790723478491074900 * 1e18,
- claimableReservesRemaining: 0,
- auctionPrice: 0,
- timeRemaining: 0
- }
- );
+ _assertBorrower({
+ borrower: _borrower2,
+ borrowerDebt: 9_822.951211365485636462* 1e18,
+ borrowerCollateral: 1_000 * 1e18,
+ borrowert0Np: 1_575.326150647652569911 * 1e18,
+ borrowerCollateralization: 0.989651241857326201 * 1e18
+ });
+
+ _kick({
+ from: _lender,
+ borrower: _borrower2,
+ debt: 9_945.738101507554206918 * 1e18,
+ collateral: 1_000 * 1e18,
+ bond: 2_946.885363409645690939 * 1e18,
+ transferAmount: 2_946.885363409645690939 * 1e18
+ });
+
+ _assertKicker({
+ kicker: _lender,
+ claimable: 0,
+ locked: 2_946.885363409645690939 * 1e18
+ });
+ _assertBorrower({
+ borrower: _borrower2,
+ borrowerDebt: 9_945.738101507554206918 * 1e18,
+ borrowerCollateral: 1_000 * 1e18,
+ borrowert0Np: 1_575.326150647652569911 * 1e18,
+ borrowerCollateralization: 0.977433325291186371 * 1e18
+ });
+ _assertReserveAuction({
+ reserves: 176.383108065231049467 * 1e18,
+ claimableReserves : 80.790723478491074900 * 1e18,
+ claimableReservesRemaining: 0,
+ auctionPrice: 0,
+ timeRemaining: 0
+ });
skip(47000 seconds); // 13.05 hrs
@@ -383,7 +323,6 @@ contract ERC20PoolLiquidationsTakeTest is ERC20HelperContract {
interestRateUpdate: block.timestamp - 47000 seconds
})
);
-
_assertAuction(
AuctionParams({
borrower: _borrower2,
@@ -400,29 +339,24 @@ contract ERC20PoolLiquidationsTakeTest is ERC20HelperContract {
neutralPrice: 1_597.054445085392479852 * 1e18
})
);
-
- _assertBorrower(
- {
- borrower: _borrower2,
- borrowerDebt: 9_946.405146835980073929 * 1e18,
- borrowerCollateral: 1_000.000000000000000 * 1e18,
- borrowert0Np: 1_575.326150647652569911 * 1e18,
- borrowerCollateralization: 0.977367774740624830 * 1e18
- }
- );
+ _assertBorrower({
+ borrower: _borrower2,
+ borrowerDebt: 9_946.405146835980073929 * 1e18,
+ borrowerCollateral: 1_000.000000000000000 * 1e18,
+ borrowert0Np: 1_575.326150647652569911 * 1e18,
+ borrowerCollateralization: 0.977367774740624830 * 1e18
+ });
// BPF Positive, Loan Col constraint
- _take(
- {
- from: _lender,
- borrower: _borrower2,
- maxCollateral: 1_001 * 1e18,
- bondChange: 3_598.602058071496018124* 1e18,
- givenAmount: 12_005.655124053999200000 * 1e18,
- collateralTaken: 1000.0 * 1e18,
- isReward: true
- }
- );
+ _take({
+ from: _lender,
+ borrower: _borrower2,
+ maxCollateral: 1_001 * 1e18,
+ bondChange: 3_598.602058071496018124* 1e18,
+ givenAmount: 12_005.655124053999200000 * 1e18,
+ collateralTaken: 1000.0 * 1e18,
+ isReward: true
+ });
// Residual is not collateralized, auction is active
_assertAuction(
@@ -441,58 +375,46 @@ contract ERC20PoolLiquidationsTakeTest is ERC20HelperContract {
neutralPrice: 1_597.054445085392479852 * 1e18
})
);
-
// Bad debt remains
- _assertBorrower(
- {
- borrower: _borrower2,
- borrowerDebt: 2_235.600441131995497104 * 1e18,
- borrowerCollateral: 0,
- borrowert0Np: 1_575.326150647652569911 * 1e18,
- borrowerCollateralization: 0
- }
- );
+ _assertBorrower({
+ borrower: _borrower2,
+ borrowerDebt: 2_235.600441131995497104 * 1e18,
+ borrowerCollateral: 0,
+ borrowert0Np: 1_575.326150647652569911 * 1e18,
+ borrowerCollateralization: 0
+ });
}
function testTakeCallerColConstraintBpfPosNoResidual() external tearDown {
// Increase neutralPrice so it exceeds TP
- _addLiquidity(
- {
- from: _lender,
- amount: 10_000 * 1e18,
- index: _i1505_26,
- lpAward: 10_000 * 1e27,
- newLup: _p1505_26
- }
- );
-
- _pledgeCollateral(
- {
- from: _borrower,
- borrower: _borrower,
- amount: 1_000 * 1e18
- }
- );
-
- _borrow(
- {
- from: _borrower,
- amount: 9_020 * 1e18,
- indexLimit: _i9_72,
- newLup: _p9_72
- }
- );
+ _addLiquidity({
+ from: _lender,
+ amount: 10_000 * 1e18,
+ index: _i1505_26,
+ lpAward: 10_000 * 1e18,
+ newLup: _p1505_26
+ });
+
+ _pledgeCollateral({
+ from: _borrower,
+ borrower: _borrower,
+ amount: 1_000 * 1e18
+ });
+ _borrow({
+ from: _borrower,
+ amount: 9_020 * 1e18,
+ indexLimit: _i9_72,
+ newLup: _p9_72
+ });
// calling borrow stamps loan with new t0NeutralPrice
- _borrow(
- {
- from: _borrower2,
- amount: 1_700.0 * 1e18,
- indexLimit: _p9_72,
- newLup: _p9_72
- }
- );
+ _borrow({
+ from: _borrower2,
+ amount: 1_700.0 * 1e18,
+ indexLimit: _i9_72,
+ newLup: _p9_72
+ });
_assertPool(
PoolParams({
@@ -511,16 +433,13 @@ contract ERC20PoolLiquidationsTakeTest is ERC20HelperContract {
interestRateUpdate: block.timestamp
})
);
-
- _assertBorrower(
- {
- borrower: _borrower2,
- borrowerDebt: 9_689.307692307692312160* 1e18,
- borrowerCollateral: 1_000 * 1e18,
- borrowert0Np: 1_575.326150647652569911 * 1e18,
- borrowerCollateralization: 1.003301388885552947 * 1e18
- }
- );
+ _assertBorrower({
+ borrower: _borrower2,
+ borrowerDebt: 9_689.307692307692312160* 1e18,
+ borrowerCollateral: 1_000 * 1e18,
+ borrowert0Np: 1_575.326150647652569911 * 1e18,
+ borrowerCollateralization: 1.003301388885552947 * 1e18
+ });
skip(100 days);
@@ -541,8 +460,6 @@ contract ERC20PoolLiquidationsTakeTest is ERC20HelperContract {
interestRateUpdate: block.timestamp - 100 days
})
);
-
-
_assertAuction(
AuctionParams({
borrower: _borrower2,
@@ -559,55 +476,42 @@ contract ERC20PoolLiquidationsTakeTest is ERC20HelperContract {
neutralPrice: 0
})
);
-
- _assertBorrower(
- {
- borrower: _borrower2,
- borrowerDebt: 9_822.951211365485636462* 1e18,
- borrowerCollateral: 1_000 * 1e18,
- borrowert0Np: 1_575.326150647652569911 * 1e18,
- borrowerCollateralization: 0.989651241857326201 * 1e18
- }
- );
-
- _kick(
- {
- from: _lender,
- borrower: _borrower2,
- debt: 9_945.738101507554206918 * 1e18,
- collateral: 1_000 * 1e18,
- bond: 2_946.885363409645690939 * 1e18,
- transferAmount: 2_946.885363409645690939 * 1e18
- }
- );
-
- _assertKicker(
- {
- kicker: _lender,
- claimable: 0,
- locked: 2_946.885363409645690939 * 1e18
- }
- );
-
- _assertBorrower(
- {
- borrower: _borrower2,
- borrowerDebt: 9_945.738101507554206918 * 1e18,
- borrowerCollateral: 1_000 * 1e18,
- borrowert0Np: 1_575.326150647652569911 * 1e18,
- borrowerCollateralization: 0.977433325291186371 * 1e18
- }
- );
-
- _assertReserveAuction(
- {
- reserves: 176.383108065231049467 * 1e18,
- claimableReserves : 80.790723478491074900 * 1e18,
- claimableReservesRemaining: 0,
- auctionPrice: 0,
- timeRemaining: 0
- }
- );
+ _assertBorrower({
+ borrower: _borrower2,
+ borrowerDebt: 9_822.951211365485636462* 1e18,
+ borrowerCollateral: 1_000 * 1e18,
+ borrowert0Np: 1_575.326150647652569911 * 1e18,
+ borrowerCollateralization: 0.989651241857326201 * 1e18
+ });
+
+ _kick({
+ from: _lender,
+ borrower: _borrower2,
+ debt: 9_945.738101507554206918 * 1e18,
+ collateral: 1_000 * 1e18,
+ bond: 2_946.885363409645690939 * 1e18,
+ transferAmount: 2_946.885363409645690939 * 1e18
+ });
+
+ _assertKicker({
+ kicker: _lender,
+ claimable: 0,
+ locked: 2_946.885363409645690939 * 1e18
+ });
+ _assertBorrower({
+ borrower: _borrower2,
+ borrowerDebt: 9_945.738101507554206918 * 1e18,
+ borrowerCollateral: 1_000 * 1e18,
+ borrowert0Np: 1_575.326150647652569911 * 1e18,
+ borrowerCollateralization: 0.977433325291186371 * 1e18
+ });
+ _assertReserveAuction({
+ reserves: 176.383108065231049467 * 1e18,
+ claimableReserves : 80.790723478491074900 * 1e18,
+ claimableReservesRemaining: 0,
+ auctionPrice: 0,
+ timeRemaining: 0
+ });
skip(43000 seconds); // 11.94 hrs
@@ -628,7 +532,6 @@ contract ERC20PoolLiquidationsTakeTest is ERC20HelperContract {
interestRateUpdate: block.timestamp - 43000 seconds
})
);
-
_assertAuction(
AuctionParams({
borrower: _borrower2,
@@ -645,29 +548,24 @@ contract ERC20PoolLiquidationsTakeTest is ERC20HelperContract {
neutralPrice: 1_597.054445085392479852 * 1e18
})
);
-
- _assertBorrower(
- {
- borrower: _borrower2,
- borrowerDebt: 9_946.348375279124882460 * 1e18,
- borrowerCollateral: 1_000.000000000000000 * 1e18,
- borrowert0Np: 1_575.326150647652569911 * 1e18,
- borrowerCollateralization: 0.977373353339734632 * 1e18
- }
- );
+ _assertBorrower({
+ borrower: _borrower2,
+ borrowerDebt: 9_946.348375279124882460 * 1e18,
+ borrowerCollateral: 1_000.000000000000000 * 1e18,
+ borrowert0Np: 1_575.326150647652569911 * 1e18,
+ borrowerCollateralization: 0.977373353339734632 * 1e18
+ });
// BPF Positive, caller collateral is constraint
- _take(
- {
- from: _lender,
- borrower: _borrower2,
- maxCollateral: 10 * 1e18,
- bondChange: 77.051043093949465946 * 1e18,
- givenAmount: 259.336494770337503360 * 1e18,
- collateralTaken: 10.0 * 1e18,
- isReward: true
- }
- );
+ _take({
+ from: _lender,
+ borrower: _borrower2,
+ maxCollateral: 10 * 1e18,
+ bondChange: 77.051043093949465946 * 1e18,
+ givenAmount: 259.336494770337503360 * 1e18,
+ collateralTaken: 10.0 * 1e18,
+ isReward: true
+ });
// Residual is not collateralized, auction is active
_assertAuction(
@@ -686,57 +584,45 @@ contract ERC20PoolLiquidationsTakeTest is ERC20HelperContract {
neutralPrice: 1_597.054445085392479852 * 1e18
})
);
-
- _assertBorrower(
- {
- borrower: _borrower2,
- borrowerDebt: 10_460.307309872275586822 * 1e18,
- borrowerCollateral: 990 * 1e18,
- borrowert0Np: 1_575.326150647652569911 * 1e18,
- borrowerCollateralization: 0.920057377023560467 * 1e18
- }
- );
+ _assertBorrower({
+ borrower: _borrower2,
+ borrowerDebt: 10_460.307309872275586822 * 1e18,
+ borrowerCollateral: 990 * 1e18,
+ borrowert0Np: 1_575.326150647652569911 * 1e18,
+ borrowerCollateralization: 0.920057377023560467 * 1e18
+ });
}
function testTakeCallerColConstraintBpfPosResidual () external tearDown {
// Increase neutralPrice so it exceeds TP
- _addLiquidity(
- {
- from: _lender,
- amount: 10_000 * 1e18,
- index: _i1505_26,
- lpAward: 10_000 * 1e27,
- newLup: _p1505_26
- }
- );
-
- _pledgeCollateral(
- {
- from: _borrower,
- borrower: _borrower,
- amount: 1_000 * 1e18
- }
- );
-
- _borrow(
- {
- from: _borrower,
- amount: 9_020 * 1e18,
- indexLimit: _i9_72,
- newLup: _p9_72
- }
- );
+ _addLiquidity({
+ from: _lender,
+ amount: 10_000 * 1e18,
+ index: _i1505_26,
+ lpAward: 10_000 * 1e18,
+ newLup: _p1505_26
+ });
+
+ _pledgeCollateral({
+ from: _borrower,
+ borrower: _borrower,
+ amount: 1_000 * 1e18
+ });
+ _borrow({
+ from: _borrower,
+ amount: 9_020 * 1e18,
+ indexLimit: _i9_72,
+ newLup: _p9_72
+ });
// calling borrow stamps loan with new t0NeutralPrice
- _borrow(
- {
- from: _borrower2,
- amount: 1_700.0 * 1e18,
- indexLimit: _p9_72,
- newLup: _p9_72
- }
- );
+ _borrow({
+ from: _borrower2,
+ amount: 1_700.0 * 1e18,
+ indexLimit: _i9_72,
+ newLup: _p9_72
+ });
_assertPool(
PoolParams({
@@ -755,16 +641,13 @@ contract ERC20PoolLiquidationsTakeTest is ERC20HelperContract {
interestRateUpdate: block.timestamp
})
);
-
- _assertBorrower(
- {
- borrower: _borrower2,
- borrowerDebt: 9_689.307692307692312160* 1e18,
- borrowerCollateral: 1_000 * 1e18,
- borrowert0Np: 1_575.326150647652569911 * 1e18,
- borrowerCollateralization: 1.003301388885552947 * 1e18
- }
- );
+ _assertBorrower({
+ borrower: _borrower2,
+ borrowerDebt: 9_689.307692307692312160* 1e18,
+ borrowerCollateral: 1_000 * 1e18,
+ borrowert0Np: 1_575.326150647652569911 * 1e18,
+ borrowerCollateralization: 1.003301388885552947 * 1e18
+ });
skip(100 days);
@@ -785,8 +668,6 @@ contract ERC20PoolLiquidationsTakeTest is ERC20HelperContract {
interestRateUpdate: block.timestamp - 100 days
})
);
-
-
_assertAuction(
AuctionParams({
borrower: _borrower2,
@@ -803,55 +684,42 @@ contract ERC20PoolLiquidationsTakeTest is ERC20HelperContract {
neutralPrice: 0
})
);
-
- _assertBorrower(
- {
- borrower: _borrower2,
- borrowerDebt: 9_822.951211365485636462* 1e18,
- borrowerCollateral: 1_000 * 1e18,
- borrowert0Np: 1_575.326150647652569911 * 1e18,
- borrowerCollateralization: 0.989651241857326201 * 1e18
- }
- );
-
- _kick(
- {
- from: _lender,
- borrower: _borrower2,
- debt: 9_945.738101507554206918 * 1e18,
- collateral: 1_000 * 1e18,
- bond: 2_946.885363409645690939 * 1e18,
- transferAmount: 2_946.885363409645690939 * 1e18
- }
- );
-
- _assertKicker(
- {
- kicker: _lender,
- claimable: 0,
- locked: 2_946.885363409645690939 * 1e18
- }
- );
-
- _assertBorrower(
- {
- borrower: _borrower2,
- borrowerDebt: 9_945.738101507554206918 * 1e18,
- borrowerCollateral: 1_000 * 1e18,
- borrowert0Np: 1_575.326150647652569911 * 1e18,
- borrowerCollateralization: 0.977433325291186371 * 1e18
- }
- );
-
- _assertReserveAuction(
- {
- reserves: 176.383108065231049467 * 1e18,
- claimableReserves : 80.790723478491074900 * 1e18,
- claimableReservesRemaining: 0,
- auctionPrice: 0,
- timeRemaining: 0
- }
- );
+ _assertBorrower({
+ borrower: _borrower2,
+ borrowerDebt: 9_822.951211365485636462* 1e18,
+ borrowerCollateral: 1_000 * 1e18,
+ borrowert0Np: 1_575.326150647652569911 * 1e18,
+ borrowerCollateralization: 0.989651241857326201 * 1e18
+ });
+
+ _kick({
+ from: _lender,
+ borrower: _borrower2,
+ debt: 9_945.738101507554206918 * 1e18,
+ collateral: 1_000 * 1e18,
+ bond: 2_946.885363409645690939 * 1e18,
+ transferAmount: 2_946.885363409645690939 * 1e18
+ });
+
+ _assertKicker({
+ kicker: _lender,
+ claimable: 0,
+ locked: 2_946.885363409645690939 * 1e18
+ });
+ _assertBorrower({
+ borrower: _borrower2,
+ borrowerDebt: 9_945.738101507554206918 * 1e18,
+ borrowerCollateral: 1_000 * 1e18,
+ borrowert0Np: 1_575.326150647652569911 * 1e18,
+ borrowerCollateralization: 0.977433325291186371 * 1e18
+ });
+ _assertReserveAuction({
+ reserves: 176.383108065231049467 * 1e18,
+ claimableReserves : 80.790723478491074900 * 1e18,
+ claimableReservesRemaining: 0,
+ auctionPrice: 0,
+ timeRemaining: 0
+ });
skip(43000 seconds); // 11.94 hrs
@@ -872,7 +740,6 @@ contract ERC20PoolLiquidationsTakeTest is ERC20HelperContract {
interestRateUpdate: block.timestamp - 43000 seconds
})
);
-
_assertAuction(
AuctionParams({
borrower: _borrower2,
@@ -889,29 +756,24 @@ contract ERC20PoolLiquidationsTakeTest is ERC20HelperContract {
neutralPrice: 1_597.054445085392479852 * 1e18
})
);
-
- _assertBorrower(
- {
- borrower: _borrower2,
- borrowerDebt: 9_946.348375279124882460 * 1e18,
- borrowerCollateral: 1_000.000000000000000 * 1e18,
- borrowert0Np: 1_575.326150647652569911 * 1e18,
- borrowerCollateralization: 0.977373353339734632 * 1e18
- }
- );
+ _assertBorrower({
+ borrower: _borrower2,
+ borrowerDebt: 9_946.348375279124882460 * 1e18,
+ borrowerCollateral: 1_000.000000000000000 * 1e18,
+ borrowert0Np: 1_575.326150647652569911 * 1e18,
+ borrowerCollateralization: 0.977373353339734632 * 1e18
+ });
// BPF Positive, Caller Col constraint
- _take(
- {
- from: _lender,
- borrower: _borrower2,
- maxCollateral: 577.0 * 1e18,
- bondChange: 4_445.845186520884185062 * 1e18,
- givenAmount: 14_963.715748248473943872 * 1e18,
- collateralTaken: 577.000000000000000000 * 1e18,
- isReward: true
- }
- );
+ _take({
+ from: _lender,
+ borrower: _borrower2,
+ maxCollateral: 577.0 * 1e18,
+ bondChange: 4_445.845186520884185062 * 1e18,
+ givenAmount: 14_963.715748248473943872 * 1e18,
+ collateralTaken: 577.000000000000000000 * 1e18,
+ isReward: true
+ });
// Residual is collateralized, auction is not active
_assertAuction(
@@ -930,29 +792,24 @@ contract ERC20PoolLiquidationsTakeTest is ERC20HelperContract {
neutralPrice: 0
})
);
-
- _assertBorrower(
- {
- borrower: _borrower2,
- borrowerDebt: 124.722199821073865675 * 1e18,
- borrowerCollateral: 423.000000000000000000 * 1e18,
- borrowert0Np: 0.303909165615512483 * 1e18,
- borrowerCollateralization: 5_105.158167959369510853 * 1e18
- }
- );
+ _assertBorrower({
+ borrower: _borrower2,
+ borrowerDebt: 124.722199821073865675 * 1e18,
+ borrowerCollateral: 423.000000000000000000 * 1e18,
+ borrowert0Np: 0.303909165615512483 * 1e18,
+ borrowerCollateralization: 5_105.158167959369510853 * 1e18
+ });
}
function testTakeCallerColConstraintBpfNegResidual () external tearDown {
- _borrow(
- {
- from: _borrower2,
- amount: 1_700.0 * 1e18,
- indexLimit: _p9_72,
- newLup: _p9_72
- }
- );
+ _borrow({
+ from: _borrower2,
+ amount: 1_700.0 * 1e18,
+ indexLimit: _i9_72,
+ newLup: _p9_72
+ });
_assertPool(
PoolParams({
@@ -971,29 +828,24 @@ contract ERC20PoolLiquidationsTakeTest is ERC20HelperContract {
interestRateUpdate: block.timestamp
})
);
-
- _assertBorrower(
- {
- borrower: _borrower2,
- borrowerDebt: 9_689.307692307692312160 * 1e18,
- borrowerCollateral: 1_000 * 1e18,
- borrowert0Np: 10.275765152019230606 * 1e18,
- borrowerCollateralization: 1.003301388885552947 * 1e18
- }
- );
+ _assertBorrower({
+ borrower: _borrower2,
+ borrowerDebt: 9_689.307692307692312160 * 1e18,
+ borrowerCollateral: 1_000 * 1e18,
+ borrowert0Np: 10.275765152019230606 * 1e18,
+ borrowerCollateralization: 1.003301388885552947 * 1e18
+ });
skip(100 days);
- _kick(
- {
- from: _lender,
- borrower: _borrower2,
- debt: 9_945.738101507554206918 * 1e18,
- collateral: 1_000 * 1e18,
- bond: 98.229512113654856365 * 1e18,
- transferAmount: 98.229512113654856365 * 1e18
- }
- );
+ _kick({
+ from: _lender,
+ borrower: _borrower2,
+ debt: 9_945.738101507554206918 * 1e18,
+ collateral: 1_000 * 1e18,
+ bond: 98.229512113654856365 * 1e18,
+ transferAmount: 98.229512113654856365 * 1e18
+ });
_assertAuction(
AuctionParams({
@@ -1011,31 +863,25 @@ contract ERC20PoolLiquidationsTakeTest is ERC20HelperContract {
neutralPrice: 10.417497612122395691 * 1e18
})
);
- _assertKicker(
- {
- kicker: _lender,
- claimable: 0,
- locked: 98.229512113654856365 * 1e18
- }
- );
- _assertBorrower(
- {
- borrower: _borrower2,
- borrowerDebt: 9_945.738101507554206918 * 1e18,
- borrowerCollateral: 1_000 * 1e18,
- borrowert0Np: 10.275765152019230606 * 1e18,
- borrowerCollateralization: 0.977433325291186371 * 1e18
- }
- );
- _assertReserveAuction(
- {
- reserves: 147.625795655539437491 * 1e18,
- claimableReserves : 97.799433758115930094 * 1e18,
- claimableReservesRemaining: 0,
- auctionPrice: 0,
- timeRemaining: 0
- }
- );
+ _assertKicker({
+ kicker: _lender,
+ claimable: 0,
+ locked: 98.229512113654856365 * 1e18
+ });
+ _assertBorrower({
+ borrower: _borrower2,
+ borrowerDebt: 9_945.738101507554206918 * 1e18,
+ borrowerCollateral: 1_000 * 1e18,
+ borrowert0Np: 10.275765152019230606 * 1e18,
+ borrowerCollateralization: 0.977433325291186371 * 1e18
+ });
+ _assertReserveAuction({
+ reserves: 147.625795655539437491 * 1e18,
+ claimableReserves : 97.799433758115930094 * 1e18,
+ claimableReservesRemaining: 0,
+ auctionPrice: 0,
+ timeRemaining: 0
+ });
skip(2 hours);
@@ -1056,7 +902,6 @@ contract ERC20PoolLiquidationsTakeTest is ERC20HelperContract {
interestRateUpdate: block.timestamp - 2 hours
})
);
-
_assertAuction(
AuctionParams({
borrower: _borrower2,
@@ -1073,29 +918,24 @@ contract ERC20PoolLiquidationsTakeTest is ERC20HelperContract {
neutralPrice: 10.417497612122395691 * 1e18
})
);
-
- _assertBorrower(
- {
- borrower: _borrower2,
- borrowerDebt: 9_945.840284273233679079 * 1e18,
- borrowerCollateral: 1_000.000000000000000 * 1e18,
- borrowert0Np: 10.275765152019230606 * 1e18,
- borrowerCollateralization: 0.977423283219567398 * 1e18
- }
- );
+ _assertBorrower({
+ borrower: _borrower2,
+ borrowerDebt: 9_945.840284273233679079 * 1e18,
+ borrowerCollateral: 1_000.000000000000000 * 1e18,
+ borrowert0Np: 10.275765152019230606 * 1e18,
+ borrowerCollateralization: 0.977423283219567398 * 1e18
+ });
// BPF Negative, Caller collateral constraint
- _take(
- {
- from: _lender,
- borrower: _borrower2,
- maxCollateral: 10.0 * 1e18,
- bondChange: 16.667996179395833107 * 1e18,
- givenAmount: 1_666.799617939583310720 * 1e18,
- collateralTaken: 10.0 * 1e18,
- isReward: false
- }
- );
+ _take({
+ from: _lender,
+ borrower: _borrower2,
+ maxCollateral: 10.0 * 1e18,
+ bondChange: 16.667996179395833107 * 1e18,
+ givenAmount: 1_666.799617939583310720 * 1e18,
+ collateralTaken: 10.0 * 1e18,
+ isReward: false
+ });
_assertPool(
PoolParams({
@@ -1114,7 +954,6 @@ contract ERC20PoolLiquidationsTakeTest is ERC20HelperContract {
interestRateUpdate: block.timestamp - 2 hours
})
);
-
// Residual is collateralized, auction is not active
_assertAuction(
AuctionParams({
@@ -1132,58 +971,46 @@ contract ERC20PoolLiquidationsTakeTest is ERC20HelperContract {
neutralPrice: 0
})
);
-
- _assertBorrower(
- {
- borrower: _borrower2,
- borrowerDebt: 8_975.249486232776725894 * 1e18,
- borrowerCollateral: 990.000000000000000000 * 1e18,
- borrowert0Np: 9.438566662973887791 * 1e18,
- borrowerCollateralization: 1.072291407736791833 * 1e18
- }
- );
+ _assertBorrower({
+ borrower: _borrower2,
+ borrowerDebt: 8_975.249486232776725894 * 1e18,
+ borrowerCollateral: 990.000000000000000000 * 1e18,
+ borrowert0Np: 9.438566662973887791 * 1e18,
+ borrowerCollateralization: 1.072291407736791833 * 1e18
+ });
}
function testTakeLoanDebtConstraintBpfPosResidual() external tearDown {
// Increase neutralPrice so it exceeds TP
- _addLiquidity(
- {
- from: _lender,
- amount: 10_000 * 1e18,
- index: _i1505_26,
- lpAward: 10_000 * 1e27,
- newLup: _p1505_26
- }
- );
-
- _pledgeCollateral(
- {
- from: _borrower,
- borrower: _borrower,
- amount: 1_000 * 1e18
- }
- );
-
- _borrow(
- {
- from: _borrower,
- amount: 9_020 * 1e18,
- indexLimit: _i9_72,
- newLup: _p9_72
- }
- );
+ _addLiquidity({
+ from: _lender,
+ amount: 10_000 * 1e18,
+ index: _i1505_26,
+ lpAward: 10_000 * 1e18,
+ newLup: _p1505_26
+ });
+
+ _pledgeCollateral({
+ from: _borrower,
+ borrower: _borrower,
+ amount: 1_000 * 1e18
+ });
+ _borrow({
+ from: _borrower,
+ amount: 9_020 * 1e18,
+ indexLimit: _i9_72,
+ newLup: _p9_72
+ });
// calling borrow stamps loan with new t0NeutralPrice
- _borrow(
- {
- from: _borrower2,
- amount: 1_700.0 * 1e18,
- indexLimit: _p9_72,
- newLup: _p9_72
- }
- );
+ _borrow({
+ from: _borrower2,
+ amount: 1_700.0 * 1e18,
+ indexLimit: _i9_72,
+ newLup: _p9_72
+ });
_assertPool(
PoolParams({
@@ -1202,16 +1029,13 @@ contract ERC20PoolLiquidationsTakeTest is ERC20HelperContract {
interestRateUpdate: block.timestamp
})
);
-
- _assertBorrower(
- {
- borrower: _borrower2,
- borrowerDebt: 9_689.307692307692312160* 1e18,
- borrowerCollateral: 1_000 * 1e18,
- borrowert0Np: 1_575.326150647652569911 * 1e18,
- borrowerCollateralization: 1.003301388885552947 * 1e18
- }
- );
+ _assertBorrower({
+ borrower: _borrower2,
+ borrowerDebt: 9_689.307692307692312160* 1e18,
+ borrowerCollateral: 1_000 * 1e18,
+ borrowert0Np: 1_575.326150647652569911 * 1e18,
+ borrowerCollateralization: 1.003301388885552947 * 1e18
+ });
skip(100 days);
@@ -1232,8 +1056,6 @@ contract ERC20PoolLiquidationsTakeTest is ERC20HelperContract {
interestRateUpdate: block.timestamp - 100 days
})
);
-
-
_assertAuction(
AuctionParams({
borrower: _borrower2,
@@ -1250,55 +1072,42 @@ contract ERC20PoolLiquidationsTakeTest is ERC20HelperContract {
neutralPrice: 0
})
);
-
- _assertBorrower(
- {
- borrower: _borrower2,
- borrowerDebt: 9_822.951211365485636462* 1e18,
- borrowerCollateral: 1_000 * 1e18,
- borrowert0Np: 1_575.326150647652569911 * 1e18,
- borrowerCollateralization: 0.989651241857326201 * 1e18
- }
- );
-
- _kick(
- {
- from: _lender,
- borrower: _borrower2,
- debt: 9_945.738101507554206918 * 1e18,
- collateral: 1_000 * 1e18,
- bond: 2_946.885363409645690939 * 1e18,
- transferAmount: 2_946.885363409645690939 * 1e18
- }
- );
-
- _assertKicker(
- {
- kicker: _lender,
- claimable: 0,
- locked: 2_946.885363409645690939 * 1e18
- }
- );
-
- _assertBorrower(
- {
- borrower: _borrower2,
- borrowerDebt: 9_945.738101507554206918 * 1e18,
- borrowerCollateral: 1_000 * 1e18,
- borrowert0Np: 1_575.326150647652569911 * 1e18,
- borrowerCollateralization: 0.977433325291186371 * 1e18
- }
- );
-
- _assertReserveAuction(
- {
- reserves: 176.383108065231049467 * 1e18,
- claimableReserves : 80.790723478491074900 * 1e18,
- claimableReservesRemaining: 0,
- auctionPrice: 0,
- timeRemaining: 0
- }
- );
+ _assertBorrower({
+ borrower: _borrower2,
+ borrowerDebt: 9_822.951211365485636462* 1e18,
+ borrowerCollateral: 1_000 * 1e18,
+ borrowert0Np: 1_575.326150647652569911 * 1e18,
+ borrowerCollateralization: 0.989651241857326201 * 1e18
+ });
+
+ _kick({
+ from: _lender,
+ borrower: _borrower2,
+ debt: 9_945.738101507554206918 * 1e18,
+ collateral: 1_000 * 1e18,
+ bond: 2_946.885363409645690939 * 1e18,
+ transferAmount: 2_946.885363409645690939 * 1e18
+ });
+
+ _assertKicker({
+ kicker: _lender,
+ claimable: 0,
+ locked: 2_946.885363409645690939 * 1e18
+ });
+ _assertBorrower({
+ borrower: _borrower2,
+ borrowerDebt: 9_945.738101507554206918 * 1e18,
+ borrowerCollateral: 1_000 * 1e18,
+ borrowert0Np: 1_575.326150647652569911 * 1e18,
+ borrowerCollateralization: 0.977433325291186371 * 1e18
+ });
+ _assertReserveAuction({
+ reserves: 176.383108065231049467 * 1e18,
+ claimableReserves : 80.790723478491074900 * 1e18,
+ claimableReservesRemaining: 0,
+ auctionPrice: 0,
+ timeRemaining: 0
+ });
skip(43000 seconds); // 11.94 hrs
@@ -1319,7 +1128,6 @@ contract ERC20PoolLiquidationsTakeTest is ERC20HelperContract {
interestRateUpdate: block.timestamp - 43000 seconds
})
);
-
_assertAuction(
AuctionParams({
borrower: _borrower2,
@@ -1336,29 +1144,24 @@ contract ERC20PoolLiquidationsTakeTest is ERC20HelperContract {
neutralPrice: 1_597.054445085392479852 * 1e18
})
);
-
- _assertBorrower(
- {
- borrower: _borrower2,
- borrowerDebt: 9_946.348375279124882460 * 1e18,
- borrowerCollateral: 1_000.000000000000000 * 1e18,
- borrowert0Np: 1_575.326150647652569911 * 1e18,
- borrowerCollateralization: 0.977373353339734632 * 1e18
- }
- );
+ _assertBorrower({
+ borrower: _borrower2,
+ borrowerDebt: 9_946.348375279124882460 * 1e18,
+ borrowerCollateral: 1_000.000000000000000 * 1e18,
+ borrowert0Np: 1_575.326150647652569911 * 1e18,
+ borrowerCollateralization: 0.977373353339734632 * 1e18
+ });
// BPF Positive, Loan Debt constraint
- _take(
- {
- from: _lender,
- borrower: _borrower2,
- maxCollateral: 1_001 * 1e18,
- bondChange: 4_498.564564314381167419 * 1e18,
- givenAmount: 15_141.157325863044791651 * 1e18,
- collateralTaken: 583.842136806534270091 * 1e18,
- isReward: true
- }
- );
+ _take({
+ from: _lender,
+ borrower: _borrower2,
+ maxCollateral: 1_001 * 1e18,
+ bondChange: 4_498.564564314381167419 * 1e18,
+ givenAmount: 15_141.157325863044791651 * 1e18,
+ collateralTaken: 583.842136806534270091 * 1e18,
+ isReward: true
+ });
// Residual is collateralized, auction is not active
_assertAuction(
@@ -1377,51 +1180,45 @@ contract ERC20PoolLiquidationsTakeTest is ERC20HelperContract {
neutralPrice: 0
})
);
-
- _assertBorrower(
- {
- borrower: _borrower2,
- borrowerDebt: 0,
- borrowerCollateral: 416.157863193465729909 * 1e18,
- borrowert0Np: 0,
- borrowerCollateralization: 1 * 1e18
- }
- );
+ _assertBorrower({
+ borrower: _borrower2,
+ borrowerDebt: 0,
+ borrowerCollateral: 416.157863193465729909 * 1e18,
+ borrowert0Np: 0,
+ borrowerCollateralization: 1 * 1e18
+ });
}
function testTakeAndSettle() external tearDown {
// Borrower2 borrows
- _borrow(
- {
- from: _borrower2,
- amount: 1_730 * 1e18,
- indexLimit: _i9_72,
- newLup: 9.721295865031779605 * 1e18
- }
- );
+ _borrow({
+ from: _borrower2,
+ amount: 1_730 * 1e18,
+ indexLimit: _i9_72,
+ newLup: 9.721295865031779605 * 1e18
+ });
// Skip to make borrower undercollateralized
skip(100 days);
- _assertBorrower(
- {
- borrower: _borrower2,
- borrowerDebt: 9_853.394241979221645666 * 1e18,
- borrowerCollateral: 1_000 * 1e18,
- borrowert0Np: 10.307611531622595991 * 1e18,
- borrowerCollateralization: 0.986593617011217057 * 1e18
- }
- );
- _kick(
- {
- from: _lender,
- borrower: _borrower2,
- debt: 9_976.561670003961916237 * 1e18,
- collateral: 1_000 * 1e18,
- bond: 98.533942419792216457 * 1e18,
- transferAmount: 98.533942419792216457 * 1e18
- }
- );
+
+ _assertBorrower({
+ borrower: _borrower2,
+ borrowerDebt: 9_853.394241979221645666 * 1e18,
+ borrowerCollateral: 1_000 * 1e18,
+ borrowert0Np: 10.307611531622595991 * 1e18,
+ borrowerCollateralization: 0.986593617011217057 * 1e18
+ });
+
+ _kick({
+ from: _lender,
+ borrower: _borrower2,
+ debt: 9_976.561670003961916237 * 1e18,
+ collateral: 1_000 * 1e18,
+ bond: 98.533942419792216457 * 1e18,
+ transferAmount: 98.533942419792216457 * 1e18
+ });
+
_assertAuction(
AuctionParams({
borrower: _borrower2,
@@ -1438,33 +1235,28 @@ contract ERC20PoolLiquidationsTakeTest is ERC20HelperContract {
neutralPrice: 10.449783245217816340 * 1e18
})
);
- _assertKicker(
- {
- kicker: _lender,
- claimable: 0,
- locked: 98.533942419792216457 * 1e18
- }
- );
- _assertBorrower(
- {
- borrower: _borrower2,
- borrowerDebt: 9_976.561670003961916237 * 1e18,
- borrowerCollateral: 1_000 * 1e18,
- borrowert0Np: 10.307611531622595991 * 1e18,
- borrowerCollateralization: 0.974413448899967463 * 1e18
- }
- );
- _assertReserveAuction(
- {
- reserves: 148.064352861909228810 * 1e18,
- claimableReserves : 98.083873122003682866 * 1e18,
- claimableReservesRemaining: 0,
- auctionPrice: 0,
- timeRemaining: 0
- }
- );
+ _assertKicker({
+ kicker: _lender,
+ claimable: 0,
+ locked: 98.533942419792216457 * 1e18
+ });
+ _assertBorrower({
+ borrower: _borrower2,
+ borrowerDebt: 9_976.561670003961916237 * 1e18,
+ borrowerCollateral: 1_000 * 1e18,
+ borrowert0Np: 10.307611531622595991 * 1e18,
+ borrowerCollateralization: 0.974413448899967463 * 1e18
+ });
+ _assertReserveAuction({
+ reserves: 148.064352861909228810 * 1e18,
+ claimableReserves : 98.083873122003682866 * 1e18,
+ claimableReservesRemaining: 0,
+ auctionPrice: 0,
+ timeRemaining: 0
+ });
uint256 preTakeSnapshot = vm.snapshot();
+
skip(364 minutes);
_assertAuction(
@@ -1483,38 +1275,23 @@ contract ERC20PoolLiquidationsTakeTest is ERC20HelperContract {
neutralPrice: 10.449783245217816340 * 1e18
})
);
-
- _assertBorrower(
- {
- borrower: _borrower2,
- borrowerDebt: 9_976.872588243234769567 * 1e18,
- borrowerCollateral: 1_000 * 1e18,
- borrowert0Np: 10.307611531622595991 * 1e18,
- borrowerCollateralization: 0.974383082378677738 * 1e18
- }
- );
-
- _take(
- {
- from: _lender,
- borrower: _borrower2,
- maxCollateral: 1_000 * 1e18,
- bondChange: 99.778877943799773760 * 1e18,
- givenAmount: 9_977.887794379977376000 * 1e18,
- collateralTaken: 1000.0 * 1e18,
- isReward: true
- }
- );
-
- _assertBorrower(
- {
- borrower: _borrower2,
- borrowerDebt: 797.144752984083601436 * 1e18,
- borrowerCollateral: 0,
- borrowert0Np: 10.307611531622595991 * 1e18,
- borrowerCollateralization: 0
- }
- );
+ _assertBorrower({
+ borrower: _borrower2,
+ borrowerDebt: 9_976.872588243234769567 * 1e18,
+ borrowerCollateral: 1_000 * 1e18,
+ borrowert0Np: 10.307611531622595991 * 1e18,
+ borrowerCollateralization: 0.974383082378677738 * 1e18
+ });
+
+ _take({
+ from: _lender,
+ borrower: _borrower2,
+ maxCollateral: 1_000 * 1e18,
+ bondChange: 99.778877943799773760 * 1e18,
+ givenAmount: 9_977.887794379977376000 * 1e18,
+ collateralTaken: 1000.0 * 1e18,
+ isReward: true
+ });
_assertAuction(
AuctionParams({
@@ -1532,14 +1309,18 @@ contract ERC20PoolLiquidationsTakeTest is ERC20HelperContract {
neutralPrice: 10.449783245217816340 * 1e18
})
);
-
- _assertKicker(
- {
- kicker: _lender,
- claimable: 0,
- locked: 198.312820363591990217 * 1e18
- }
- );
+ _assertBorrower({
+ borrower: _borrower2,
+ borrowerDebt: 797.144752984083601436 * 1e18,
+ borrowerCollateral: 0,
+ borrowert0Np: 10.307611531622595991 * 1e18,
+ borrowerCollateralization: 0
+ });
+ _assertKicker({
+ kicker: _lender,
+ claimable: 0,
+ locked: 198.312820363591990217 * 1e18
+ });
vm.revertTo(preTakeSnapshot);
@@ -1562,30 +1343,26 @@ contract ERC20PoolLiquidationsTakeTest is ERC20HelperContract {
neutralPrice: 10.449783245217816340 * 1e18
})
);
-
- _assertBorrower(
- {
- borrower: _borrower2,
- borrowerDebt: 9_977.074177773911990381 * 1e18,
- borrowerCollateral: 1_000 * 1e18,
- borrowert0Np: 10.307611531622595991 * 1e18,
- borrowerCollateralization: 0.974363394700228467 * 1e18
- }
- );
+ _assertBorrower({
+ borrower: _borrower2,
+ borrowerDebt: 9_977.074177773911990381 * 1e18,
+ borrowerCollateral: 1_000 * 1e18,
+ borrowert0Np: 10.307611531622595991 * 1e18,
+ borrowerCollateralization: 0.974363394700228467 * 1e18
+ });
// partial take for 20 collateral
// Collateral amount is restrained by taker
- _take(
- {
- from: _lender,
- borrower: _borrower2,
- maxCollateral: 20 * 1e18,
- bondChange: 0.130622290565222707 * 1e18,
- givenAmount: 13.062229056522270720 * 1e18,
- collateralTaken: 20 * 1e18,
- isReward: true
- }
- );
+ _take({
+ from: _lender,
+ borrower: _borrower2,
+ maxCollateral: 20 * 1e18,
+ bondChange: 0.130622290565222707 * 1e18,
+ givenAmount: 13.062229056522270720 * 1e18,
+ collateralTaken: 20 * 1e18,
+ isReward: true
+ });
+
_assertAuction(
AuctionParams({
borrower: _borrower2,
@@ -1602,46 +1379,38 @@ contract ERC20PoolLiquidationsTakeTest is ERC20HelperContract {
neutralPrice: 10.449783245217816340 * 1e18
})
);
- _assertKicker(
- {
- kicker: _lender,
- claimable: 0,
- locked: 98.664564710357439164 * 1e18 // locked bond + reward, auction is not yet finished
- }
- );
- _assertBorrower(
- {
- borrower: _borrower2,
- borrowerDebt: 10_662.537763452128781688 * 1e18,
- borrowerCollateral: 980 * 1e18,
- borrowert0Np: 10.307611531622595991 * 1e18,
- borrowerCollateralization: 0.893489913853932440 * 1e18
- }
- );
-
+ _assertBorrower({
+ borrower: _borrower2,
+ borrowerDebt: 10_662.537763452128781688 * 1e18,
+ borrowerCollateral: 980 * 1e18,
+ borrowert0Np: 10.307611531622595991 * 1e18,
+ borrowerCollateralization: 0.893489913853932440 * 1e18
+ });
+ _assertKicker({
+ kicker: _lender,
+ claimable: 0,
+ locked: 98.664564710357439164 * 1e18 // locked bond + reward, auction is not yet finished
+ });
// reserves should increase after take action
- _assertReserveAuction(
- {
- reserves: 846.536571996419330152 * 1e18,
- claimableReserves : 793.126206771778158186 * 1e18,
- claimableReservesRemaining: 0,
- auctionPrice: 0,
- timeRemaining: 0
- }
- );
+ _assertReserveAuction({
+ reserves: 846.536571996419330152 * 1e18,
+ claimableReserves : 793.126206771778158186 * 1e18,
+ claimableReservesRemaining: 0,
+ auctionPrice: 0,
+ timeRemaining: 0
+ });
// take remaining collateral
- _take(
- {
- from: _lender,
- borrower: _borrower2,
- maxCollateral: 981 * 1e18,
- bondChange: 6.400492237695912653 * 1e18,
- givenAmount: 640.049223769591265280 * 1e18,
- collateralTaken: 980 * 1e18,
- isReward: true
- }
- );
+ _take({
+ from: _lender,
+ borrower: _borrower2,
+ maxCollateral: 981 * 1e18,
+ bondChange: 6.400492237695912653 * 1e18,
+ givenAmount: 640.049223769591265280 * 1e18,
+ collateralTaken: 980 * 1e18,
+ isReward: true
+ });
+
_assertAuction(
AuctionParams({
borrower: _borrower2,
@@ -1658,62 +1427,51 @@ contract ERC20PoolLiquidationsTakeTest is ERC20HelperContract {
neutralPrice: 10.449783245217816340 * 1e18
})
);
- _assertKicker(
- {
- kicker: _lender,
- claimable: 0,
- locked: 105.065056948053351817 * 1e18 // locked bond + reward, auction is not yet finalized
- }
- );
- _assertBorrower(
- {
- borrower: _borrower2,
- borrowerDebt: 10_028.889031920233428707 * 1e18,
- borrowerCollateral: 0,
- borrowert0Np: 10.307611531622595991 * 1e18,
- borrowerCollateralization: 0
- }
- );
+ _assertBorrower({
+ borrower: _borrower2,
+ borrowerDebt: 10_028.889031920233428707 * 1e18,
+ borrowerCollateral: 0,
+ borrowert0Np: 10.307611531622595991 * 1e18,
+ borrowerCollateralization: 0
+ });
+ _assertKicker({
+ kicker: _lender,
+ claimable: 0,
+ locked: 105.065056948053351817 * 1e18 // locked bond + reward, auction is not yet finalized
+ });
// reserves should increase after take action
- _assertReserveAuction(
- {
- reserves: 846.536571996419329799 * 1e18,
- claimableReserves : 796.294450429437634598 * 1e18,
- claimableReservesRemaining: 0,
- auctionPrice: 0,
- timeRemaining: 0
- }
- );
+ _assertReserveAuction({
+ reserves: 846.536571996419329799 * 1e18,
+ claimableReserves : 796.294450429437634598 * 1e18,
+ claimableReservesRemaining: 0,
+ auctionPrice: 0,
+ timeRemaining: 0
+ });
// should revert if there's no more collateral to be auctioned
- _assertTakeInsufficentCollateralRevert(
- {
- from: _lender,
- borrower: _borrower2,
- maxCollateral: 10 * 1e18
- }
- );
+ _assertTakeInsufficentCollateralRevert({
+ from: _lender,
+ borrower: _borrower2,
+ maxCollateral: 10 * 1e18
+ });
// full clear / debt settle
uint256 postTakeSnapshot = vm.snapshot();
- _assertBucket(
- {
- index: 3_696,
- lpBalance: 2_000 * 1e27,
- collateral: 0,
- deposit: 2_118.911507166546112000 * 1e18,
- exchangeRate: 1.059455753583273056000000000 * 1e27
- }
- );
- _settle(
- {
- from: _lender,
- borrower: _borrower2,
- maxDepth: 10,
- settledDebt: 9_891.935520844277346922 * 1e18
- }
- );
+ _assertBucket({
+ index: 3_696,
+ lpBalance: 2_000 * 1e18,
+ collateral: 0,
+ deposit: 2_118.911507166546112000 * 1e18,
+ exchangeRate: 1.059455753583273056 * 1e18
+ });
+
+ _settle({
+ from: _lender,
+ borrower: _borrower2,
+ maxDepth: 10,
+ settledDebt: 9_891.935520844277346922 * 1e18
+ });
_assertAuction(
AuctionParams({
@@ -1731,137 +1489,109 @@ contract ERC20PoolLiquidationsTakeTest is ERC20HelperContract {
neutralPrice: 0
})
);
- _assertKicker(
- {
- kicker: _lender,
- claimable: 105.065056948053351817 * 1e18,
- locked: 0
- }
- );
- _assertBorrower(
- {
- borrower: _borrower2,
- borrowerDebt: 0,
- borrowerCollateral: 0,
- borrowert0Np: 10.307611531622595991 * 1e18,
- borrowerCollateralization: 1 * 1e18
- }
- );
-
- _assertBucket(
- {
- index: _i9_91,
- lpBalance: 0, // bucket is bankrupt
- collateral: 0,
- deposit: 0,
- exchangeRate: 1 * 1e27
- }
- );
- _assertLenderLpBalance(
- {
- lender: _lender,
- index: _i9_91,
- lpBalance: 0, // bucket is bankrupt
- depositTime: _startTime
- }
- );
- _assertBucket(
- {
- index: _i9_81,
- lpBalance: 0, // bucket is bankrupt
- collateral: 0,
- deposit: 0,
- exchangeRate: 1 * 1e27
- }
- );
- _assertLenderLpBalance(
- {
- lender: _lender,
- index: _i9_81,
- lpBalance: 0, // bucket is bankrupt
- depositTime: _startTime
- }
- );
- _assertBucket(
- {
- index: _i9_72,
- lpBalance: 11_000 * 1e27,
- collateral: 0,
- deposit: 8_935.875749431291350857 * 1e18,
- exchangeRate: 0.812352340857390122805181818 * 1e27
- }
- );
- _assertLenderLpBalance(
- {
- lender: _lender,
- index: _i9_72,
- lpBalance: 11_000 * 1e27,
- depositTime: _startTime
- }
- );
- _assertBucket(
- {
- index: _i9_62,
- lpBalance: 25_000 * 1e27,
- collateral: 0,
- deposit: 25_000 * 1e18,
- exchangeRate: 1 * 1e27
- }
- );
- _assertLenderLpBalance(
- {
- lender: _lender,
- index: _i9_62,
- lpBalance: 25_000 * 1e27,
- depositTime: _startTime
- }
- );
- _assertBucket(
- {
- index: _i9_52,
- lpBalance: 30_000 * 1e27,
- collateral: 0,
- deposit: 30_000 * 1e18,
- exchangeRate: 1 * 1e27
- }
- );
- _assertLenderLpBalance(
- {
- lender: _lender,
- index: _i9_52,
- lpBalance: 30_000 * 1e27,
- depositTime: _startTime
- }
- );
+ _assertBorrower({
+ borrower: _borrower2,
+ borrowerDebt: 0,
+ borrowerCollateral: 0,
+ borrowert0Np: 10.307611531622595991 * 1e18,
+ borrowerCollateralization: 1 * 1e18
+ });
+ _assertKicker({
+ kicker: _lender,
+ claimable: 105.065056948053351817 * 1e18,
+ locked: 0
+ });
+ _assertBucket({
+ index: _i9_91,
+ lpBalance: 0, // bucket is bankrupt
+ collateral: 0,
+ deposit: 0,
+ exchangeRate: 1 * 1e18
+ });
+ _assertLenderLpBalance({
+ lender: _lender,
+ index: _i9_91,
+ lpBalance: 0, // bucket is bankrupt
+ depositTime: _startTime
+ });
+ _assertBucket({
+ index: _i9_81,
+ lpBalance: 0, // bucket is bankrupt
+ collateral: 0,
+ deposit: 0,
+ exchangeRate: 1 * 1e18
+ });
+ _assertLenderLpBalance({
+ lender: _lender,
+ index: _i9_81,
+ lpBalance: 0, // bucket is bankrupt
+ depositTime: _startTime
+ });
+ _assertBucket({
+ index: _i9_72,
+ lpBalance: 11_000 * 1e18,
+ collateral: 0,
+ deposit: 8_935.875749431291350857 * 1e18,
+ exchangeRate: 0.812352340857390123 * 1e18
+ });
+ _assertLenderLpBalance({
+ lender: _lender,
+ index: _i9_72,
+ lpBalance: 11_000 * 1e18,
+ depositTime: _startTime
+ });
+ _assertBucket({
+ index: _i9_62,
+ lpBalance: 25_000 * 1e18,
+ collateral: 0,
+ deposit: 25_000 * 1e18,
+ exchangeRate: 1 * 1e18
+ });
+ _assertLenderLpBalance({
+ lender: _lender,
+ index: _i9_62,
+ lpBalance: 25_000 * 1e18,
+ depositTime: _startTime
+ });
+ _assertBucket({
+ index: _i9_52,
+ lpBalance: 30_000 * 1e18,
+ collateral: 0,
+ deposit: 30_000 * 1e18,
+ exchangeRate: 1 * 1e18
+ });
+ _assertLenderLpBalance({
+ lender: _lender,
+ index: _i9_52,
+ lpBalance: 30_000 * 1e18,
+ depositTime: _startTime
+ });
vm.revertTo(postTakeSnapshot);
- _assertReserveAuction(
- {
- reserves: 846.536571996419329799 * 1e18,
- claimableReserves : 796.294450429437634598 * 1e18,
- claimableReservesRemaining: 0,
- auctionPrice: 0,
- timeRemaining: 0
- }
- );
+
+ _assertReserveAuction({
+ reserves: 846.536571996419329799 * 1e18,
+ claimableReserves : 796.294450429437634598 * 1e18,
+ claimableReservesRemaining: 0,
+ auctionPrice: 0,
+ timeRemaining: 0
+ });
+
// partial clears / debt settled - max buckets to use is 1, remaining will be taken from reserves
- _settle(
- {
- from: _lender,
- borrower: _borrower2,
- maxDepth: 1,
- settledDebt: 2_923.975862386543877283 * 1e18
- }
- );
- _assertReserveAuction(
- {
- reserves: 0.989870342666661239 * 1e18,
- claimableReserves : 0,
- claimableReservesRemaining: 0,
- auctionPrice: 0,
- timeRemaining: 0
- }
- );
+ _settle({
+ from: _lender,
+ borrower: _borrower2,
+ maxDepth: 1,
+ settledDebt: 2_923.975862386543877283 * 1e18
+ });
+
+ _assertReserveAuction({
+ reserves: 0.989870342666661239 * 1e18,
+ claimableReserves : 0,
+ claimableReservesRemaining: 0,
+ auctionPrice: 0,
+ timeRemaining: 0
+ });
_assertAuction(
AuctionParams({
borrower: _borrower2,
@@ -1878,31 +1608,27 @@ contract ERC20PoolLiquidationsTakeTest is ERC20HelperContract {
neutralPrice: 10.449783245217816340 * 1e18
})
);
- _assertKicker(
- {
- kicker: _lender,
- claimable: 0,
- locked: 105.065056948053351817 * 1e18 // locked bond + reward, auction is not yet finalized
- }
- );
- _assertBorrower(
- {
- borrower: _borrower2,
- borrowerDebt: 7_064.430823099934649143 * 1e18,
- borrowerCollateral: 0,
- borrowert0Np: 10.307611531622595991 * 1e18,
- borrowerCollateralization: 0
- }
- );
+ _assertBorrower({
+ borrower: _borrower2,
+ borrowerDebt: 7_064.430823099934649143 * 1e18,
+ borrowerCollateral: 0,
+ borrowert0Np: 10.307611531622595991 * 1e18,
+ borrowerCollateralization: 0
+ });
+ _assertKicker({
+ kicker: _lender,
+ claimable: 0,
+ locked: 105.065056948053351817 * 1e18 // locked bond + reward, auction is not yet finalized
+ });
+
// clear remaining debt
- _settle(
- {
- from: _lender,
- borrower: _borrower2,
- maxDepth: 5,
- settledDebt: 6_967.959658457733469639 * 1e18
- }
- );
+ _settle({
+ from: _lender,
+ borrower: _borrower2,
+ maxDepth: 5,
+ settledDebt: 6_967.959658457733469639 * 1e18
+ });
+
_assertAuction(
AuctionParams({
borrower: _borrower2,
@@ -1919,61 +1645,55 @@ contract ERC20PoolLiquidationsTakeTest is ERC20HelperContract {
neutralPrice: 0
})
);
- _assertKicker(
- {
- kicker: _lender,
- claimable: 105.065056948053351817 * 1e18,
- locked: 0
- }
- );
- _assertBorrower(
- {
- borrower: _borrower2,
- borrowerDebt: 0,
- borrowerCollateral: 0,
- borrowert0Np: 10.307611531622595991 * 1e18,
- borrowerCollateralization: 1 * 1e18
- }
- );
+ _assertBorrower({
+ borrower: _borrower2,
+ borrowerDebt: 0,
+ borrowerCollateral: 0,
+ borrowert0Np: 10.307611531622595991 * 1e18,
+ borrowerCollateralization: 1 * 1e18
+ });
+ _assertKicker({
+ kicker: _lender,
+ claimable: 105.065056948053351817 * 1e18,
+ locked: 0
+ });
// kicker withdraws his auction bonds
assertEq(_quote.balanceOf(_lender), 46_248.354604754094247543 * 1e18);
- _pool.withdrawBonds();
+
+ _pool.withdrawBonds(_lender);
+
assertEq(_quote.balanceOf(_lender), 46_353.419661702147599360 * 1e18);
- _assertKicker(
- {
- kicker: _lender,
- claimable: 0,
- locked: 0
- }
- );
+
+ _assertKicker({
+ kicker: _lender,
+ claimable: 0,
+ locked: 0
+ });
}
function testTakeReverts() external tearDown {
// Borrower2 borrows
- _borrow(
- {
- from: _borrower2,
- amount: 1_730 * 1e18,
- indexLimit: _i9_72,
- newLup: 9.721295865031779605 * 1e18
- }
- );
+ _borrow({
+ from: _borrower2,
+ amount: 1_730 * 1e18,
+ indexLimit: _i9_72,
+ newLup: 9.721295865031779605 * 1e18
+ });
// Skip to make borrower undercollateralized
skip(100 days);
- _kick(
- {
- from: _lender,
- borrower: _borrower2,
- debt: 9_976.561670003961916237 * 1e18,
- collateral: 1_000 * 1e18,
- bond: 98.533942419792216457 * 1e18,
- transferAmount: 98.533942419792216457 * 1e18
- }
- );
+ _kick({
+ from: _lender,
+ borrower: _borrower2,
+ debt: 9_976.561670003961916237 * 1e18,
+ collateral: 1_000 * 1e18,
+ bond: 98.533942419792216457 * 1e18,
+ transferAmount: 98.533942419792216457 * 1e18
+ });
+
_assertAuction(
AuctionParams({
borrower: _borrower2,
@@ -1990,31 +1710,25 @@ contract ERC20PoolLiquidationsTakeTest is ERC20HelperContract {
neutralPrice: 10.449783245217816340 * 1e18
})
);
- _assertKicker(
- {
- kicker: _lender,
- claimable: 0,
- locked: 98.533942419792216457 * 1e18
- }
- );
- _assertBorrower(
- {
- borrower: _borrower2,
- borrowerDebt: 9_976.561670003961916237 * 1e18,
- borrowerCollateral: 1_000 * 1e18,
- borrowert0Np: 10.307611531622595991 * 1e18,
- borrowerCollateralization: 0.974413448899967463 * 1e18
- }
- );
- _assertReserveAuction(
- {
- reserves: 148.064352861909228810 * 1e18,
- claimableReserves : 98.083873122003682866 * 1e18,
- claimableReservesRemaining: 0,
- auctionPrice: 0,
- timeRemaining: 0
- }
- );
+ _assertBorrower({
+ borrower: _borrower2,
+ borrowerDebt: 9_976.561670003961916237 * 1e18,
+ borrowerCollateral: 1_000 * 1e18,
+ borrowert0Np: 10.307611531622595991 * 1e18,
+ borrowerCollateralization: 0.974413448899967463 * 1e18
+ });
+ _assertKicker({
+ kicker: _lender,
+ claimable: 0,
+ locked: 98.533942419792216457 * 1e18
+ });
+ _assertReserveAuction({
+ reserves: 148.064352861909228810 * 1e18,
+ claimableReserves : 98.083873122003682866 * 1e18,
+ claimableReservesRemaining: 0,
+ auctionPrice: 0,
+ timeRemaining: 0
+ });
// Skip to make borrower undercollateralized
skip(100 days);
@@ -2035,27 +1749,22 @@ contract ERC20PoolLiquidationsTakeTest is ERC20HelperContract {
neutralPrice: 0
})
);
-
- _assertBorrower(
- {
- borrower: _borrower,
- borrowerDebt: 19.776602251620519294 * 1e18,
- borrowerCollateral: 2 * 1e18,
- borrowert0Np: 10.115967548076923081 * 1e18,
- borrowerCollateralization: 0.983110823724556080 * 1e18
- }
- );
-
- _kick(
- {
- from: _lender,
- borrower: _borrower,
- debt: 19.999089026951250136 * 1e18,
- collateral: 2 * 1e18,
- bond: 0.197766022516205193 * 1e18,
- transferAmount: 0.197766022516205193 * 1e18
- }
- );
+ _assertBorrower({
+ borrower: _borrower,
+ borrowerDebt: 19.776602251620519294 * 1e18,
+ borrowerCollateral: 2 * 1e18,
+ borrowert0Np: 10.115967548076923081 * 1e18,
+ borrowerCollateralization: 0.983110823724556080 * 1e18
+ });
+
+ _kick({
+ from: _lender,
+ borrower: _borrower,
+ debt: 19.999089026951250136 * 1e18,
+ collateral: 2 * 1e18,
+ bond: 0.197766022516205193 * 1e18,
+ transferAmount: 0.197766022516205193 * 1e18
+ });
_assertAuction(
AuctionParams({
@@ -2073,66 +1782,56 @@ contract ERC20PoolLiquidationsTakeTest is ERC20HelperContract {
neutralPrice: 10.382716182100772629 * 1e18
})
);
-
- _assertKicker(
- {
- kicker: _lender,
- claimable: 0,
- locked: 98.731708442308421650 * 1e18
- }
- );
+ _assertKicker({
+ kicker: _lender,
+ claimable: 0,
+ locked: 98.731708442308421650 * 1e18
+ });
skip(2 hours);
// 10 borrowers draw debt to enable the min debt check
for (uint i=0; i<10; ++i) {
- _anonBorrowerDrawsDebt(1_000 * 1e18, 6_000 * 1e18, 7_777);
- }
+ _anonBorrowerDrawsDebt(1_000 * 1e18, 6_000 * 1e18, MAX_FENWICK_INDEX);
+ }
+
// should revert if auction leaves borrower with debt under minimum pool debt
- _assertTakeDebtUnderMinPoolDebtRevert(
- {
- from: _lender,
- borrower: _borrower,
- maxCollateral: 0.1 * 1e18
- }
- );
+ _assertTakeDebtUnderMinPoolDebtRevert({
+ from: _lender,
+ borrower: _borrower,
+ maxCollateral: 0.1 * 1e18
+ });
}
function testTakeAuctionPriceLtNeutralPrice() external tearDown {
- _addLiquidity(
- {
- from: _lender1,
- amount: 1 * 1e18,
- index: _i9_91,
- lpAward: 1 * 1e27,
- newLup: 9.721295865031779605 * 1e18
- }
- );
+ _addLiquidity({
+ from: _lender1,
+ amount: 1 * 1e18,
+ index: _i9_91,
+ lpAward: 1 * 1e18,
+ newLup: 9.721295865031779605 * 1e18
+ });
// Borrower2 borrows
- _borrow(
- {
- from: _borrower2,
- amount: 1_730 * 1e18,
- indexLimit: _i9_72,
- newLup: 9.721295865031779605 * 1e18
- }
- );
+ _borrow({
+ from: _borrower2,
+ amount: 1_730 * 1e18,
+ indexLimit: _i9_72,
+ newLup: 9.721295865031779605 * 1e18
+ });
// Skip to make borrower undercollateralized
skip(100 days);
- _kick(
- {
- from: _lender,
- borrower: _borrower2,
- debt: 9_976.561670003961916237 * 1e18,
- collateral: 1_000 * 1e18,
- bond: 98.533942419792216457 * 1e18,
- transferAmount: 98.533942419792216457 * 1e18
- }
- );
+ _kick({
+ from: _lender,
+ borrower: _borrower2,
+ debt: 9_976.561670003961916237 * 1e18,
+ collateral: 1_000 * 1e18,
+ bond: 98.533942419792216457 * 1e18,
+ transferAmount: 98.533942419792216457 * 1e18
+ });
_assertAuction(
AuctionParams({
@@ -2150,55 +1849,142 @@ contract ERC20PoolLiquidationsTakeTest is ERC20HelperContract {
neutralPrice: 10.449783245217816340 * 1e18
})
);
- _assertKicker(
- {
- kicker: _lender,
- claimable: 0,
- locked: 98.533942419792216457 * 1e18
- }
- );
- _assertBorrower(
- {
- borrower: _borrower2,
- borrowerDebt: 9_976.561670003961916237 * 1e18,
- borrowerCollateral: 1_000 * 1e18,
- borrowert0Np: 10.307611531622595991 * 1e18,
- borrowerCollateralization: 0.974413448899967463 * 1e18
- }
- );
+ _assertBorrower({
+ borrower: _borrower2,
+ borrowerDebt: 9_976.561670003961916237 * 1e18,
+ borrowerCollateral: 1_000 * 1e18,
+ borrowert0Np: 10.307611531622595991 * 1e18,
+ borrowerCollateralization: 0.974413448899967463 * 1e18
+ });
+ _assertKicker({
+ kicker: _lender,
+ claimable: 0,
+ locked: 98.533942419792216457 * 1e18
+ });
skip(3 hours);
- _assertBucket(
- {
- index: _i9_91,
- lpBalance: 2_001 * 1e27,
- collateral: 0,
- deposit: 2_119.781255869507381179 * 1e18,
- exchangeRate: 1.059360947461023179000000000 * 1e27
- }
- );
+ _assertBucket({
+ index: _i9_91,
+ lpBalance: 2_001 * 1e18,
+ collateral: 0,
+ deposit: 2_119.781255869507381179 * 1e18,
+ exchangeRate: 1.059360947461023179 * 1e18
+ });
+
+ _take({
+ from: _lender,
+ borrower: _borrower2,
+ maxCollateral: 1_001 * 1e18,
+ bondChange: 98.533942419792216457 * 1e18,
+ givenAmount: 10_675.085498940513902727 * 1e18,
+ collateralTaken: 127.695058936100465256 * 1e18,
+ isReward: false
+ });
+
+ _assertBorrower({
+ borrower: _borrower2,
+ borrowerDebt: 0,
+ borrowerCollateral: 872.304941063899534744 * 1e18,
+ borrowert0Np: 0,
+ borrowerCollateralization: 1 * 1e18
+ });
+ }
+}
- _take(
- {
- from: _lender,
- borrower: _borrower2,
- maxCollateral: 1_001 * 1e18,
- bondChange: 98.533942419792216457 * 1e18,
- givenAmount: 10_675.085498940513902727 * 1e18,
- collateralTaken: 127.695058936100465256 * 1e18,
- isReward: false
- }
- );
+contract ERC20PoolLiquidationsTakeAndRepayAllDebtInPoolTest is ERC20HelperContract {
+
+ address internal _lender;
+ address internal _borrower;
+ address internal _kicker;
+ address internal _taker;
+
+ function setUp() external {
+ _lender = makeAddr("lender");
+ _borrower = makeAddr("borrower");
+ _kicker = makeAddr("kicker");
+ _taker = makeAddr("taker");
+
+ _mintQuoteAndApproveTokens(_lender, 1_000_000 * 1e18);
+ _mintQuoteAndApproveTokens(_borrower, 1_000_000 * 1e18);
+ _mintQuoteAndApproveTokens(_kicker, 1_000_000 * 1e18);
+ _mintQuoteAndApproveTokens(_taker, 1_000_000 * 1e18);
+
+ _mintCollateralAndApproveTokens(_borrower, 150_000 * 1e18);
+
+ _addInitialLiquidity({
+ from: _lender,
+ amount: 1_000 * 1e18,
+ index: 2690
+ });
+ _addInitialLiquidity({
+ from: _lender,
+ amount: 1_000 * 1e18,
+ index: 2700
+ });
+ }
+
+ function testTakeAuctionRepaidAmountGreaterThanPoolDebt() external tearDown {
+ _repayDebtNoLupCheck({
+ from: _borrower,
+ borrower: _borrower,
+ amountToRepay: 0,
+ amountRepaid: 0,
+ collateralToPull: 0
+ });
+
+ _drawDebtNoLupCheck({
+ from: _borrower,
+ borrower: _borrower,
+ amountToBorrow: 635.189921955815900534 * 1e18,
+ limitIndex: 7000,
+ collateralToPledge: 0.428329945169804100 * 1e18
+ });
+
+ skip(3276);
+
+ _repayDebtNoLupCheck({
+ from: _borrower,
+ borrower: _borrower,
+ amountToRepay: type(uint256).max,
+ amountRepaid: 635.803983894118939950 * 1e18,
+ collateralToPull: 0.428329945169804100 * 1e18
+ });
+
+ _drawDebtNoLupCheck({
+ from: _borrower,
+ borrower: _borrower,
+ amountToBorrow: 100 * 1e18,
+ limitIndex: 7000,
+ collateralToPledge: 0.067433366047580170 * 1e18
+ });
+
+ skip(964);
+ skip(86400 * 200);
+
+ _kick({
+ from: _kicker,
+ borrower: _borrower,
+ debt: 104.162540773774892915 * 1e18,
+ collateral: 0.067433366047580170 * 1e18,
+ bond: 1.028765834802714992 * 1e18,
+ transferAmount: 1.028765834802714992 * 1e18
+ });
+
+ skip(964);
+ skip(3600 * 3);
+
+ // the calculated repaid amount is with 1 WAD greater than the pool debt
+ // check that take works and doesn't overflow
+ _take({
+ from: _taker,
+ borrower: _borrower,
+ maxCollateral: 0.067433366047580170 * 1e18,
+ bondChange: 1.028765834802714992 * 1e18,
+ givenAmount: 111.455789568155429076 * 1e18,
+ collateralTaken: 0.010471063560951988 * 1e18,
+ isReward: false
+ });
- _assertBorrower(
- {
- borrower: _borrower2,
- borrowerDebt: 0,
- borrowerCollateral: 872.304941063899534744 * 1e18,
- borrowert0Np: 0,
- borrowerCollateralization: 1 * 1e18
- }
- );
}
}
diff --git a/tests/forge/ERC20Pool/ERC20PoolLoanHeap.t.sol b/tests/forge/ERC20Pool/ERC20PoolLoanHeap.t.sol
new file mode 100644
index 000000000..c8fbbf988
--- /dev/null
+++ b/tests/forge/ERC20Pool/ERC20PoolLoanHeap.t.sol
@@ -0,0 +1,186 @@
+// SPDX-License-Identifier: UNLICENSED
+pragma solidity 0.8.14;
+
+import { ERC20HelperContract } from './ERC20DSTestPlus.sol';
+
+import 'src/libraries/helpers/PoolHelper.sol';
+
+contract ERC20PoolLoanHeapTest is ERC20HelperContract {
+
+ address internal _borrower1;
+ address internal _borrower2;
+ address internal _borrower3;
+ address internal _borrower4;
+ address internal _borrower5;
+ address internal _borrower6;
+ address internal _lender1;
+ address internal _lender2;
+ address internal _lender3;
+ address internal _lender4;
+
+ function setUp() external {
+ _borrower1 = makeAddr("borrower1");
+ _borrower2 = makeAddr("borrower2");
+ _borrower3 = makeAddr("borrower3");
+ _borrower4 = makeAddr("borrower4");
+ _borrower5 = makeAddr("borrower5");
+ _borrower6 = makeAddr("borrower6");
+ _lender1 = makeAddr("lender1");
+ _lender2 = makeAddr("lender2");
+ _lender3 = makeAddr("lender3");
+ _lender4 = makeAddr("lender4");
+
+ _mintQuoteAndApproveTokens(_lender1, 150_000 * 1e18);
+ _mintQuoteAndApproveTokens(_lender2, 150_000 * 1e18);
+ _mintQuoteAndApproveTokens(_lender3, 150_000 * 1e18);
+ _mintQuoteAndApproveTokens(_lender4, 5_000 * 1e18);
+
+ _mintCollateralAndApproveTokens(_lender1, 1_000 * 1e18);
+ _mintCollateralAndApproveTokens(_lender3, 1_000 * 1e18);
+ _mintCollateralAndApproveTokens(_borrower1, 1_000 * 1e18);
+ _mintCollateralAndApproveTokens(_borrower2, 1_000 * 1e18);
+ _mintCollateralAndApproveTokens(_borrower3, 1_000 * 1e18);
+ _mintCollateralAndApproveTokens(_borrower4, 1_000 * 1e18);
+ _mintCollateralAndApproveTokens(_borrower5, 1_000 * 1e18);
+ _mintCollateralAndApproveTokens(_borrower6, 1_000 * 1e18);
+
+ // Lender 1 adds Quote token accross 3 buckets
+ _addInitialLiquidity({
+ from: _lender1,
+ amount: 50_000 * 1e18,
+ index: 2500
+ });
+ _addInitialLiquidity({
+ from: _lender1,
+ amount: 50_000 * 1e18,
+ index: 2501
+ });
+ _addInitialLiquidity({
+ from: _lender1,
+ amount: 1_000 * 1e18,
+ index: 2502
+ });
+ }
+
+ function testLoanHeapUpdateThresholdPrice() external {
+ // all 6 borrowers draw debt from pool
+ _drawDebt({
+ from: _borrower1,
+ borrower: _borrower1,
+ amountToBorrow: 1_000 * 1e18,
+ limitIndex: 5000,
+ collateralToPledge: 1_000 * 1e18,
+ newLup: 3_863.654368867279344664 * 1e18
+ });
+ _drawDebt({
+ from: _borrower2,
+ borrower: _borrower2,
+ amountToBorrow: 2_000 * 1e18,
+ limitIndex: 5000,
+ collateralToPledge: 1_000 * 1e18,
+ newLup: 3_863.654368867279344664 * 1e18
+ });
+ _drawDebt({
+ from: _borrower3,
+ borrower: _borrower3,
+ amountToBorrow: 3_000 * 1e18,
+ limitIndex: 5000,
+ collateralToPledge: 1_000 * 1e18,
+ newLup: 3_863.654368867279344664 * 1e18
+ });
+ _drawDebt({
+ from: _borrower4,
+ borrower: _borrower4,
+ amountToBorrow: 4_000 * 1e18,
+ limitIndex: 5000,
+ collateralToPledge: 1_000 * 1e18,
+ newLup: 3_863.654368867279344664 * 1e18
+ });
+ _drawDebt({
+ from: _borrower5,
+ borrower: _borrower5,
+ amountToBorrow: 5_000 * 1e18,
+ limitIndex: 5000,
+ collateralToPledge: 1_000 * 1e18,
+ newLup: 3_863.654368867279344664 * 1e18
+ });
+ _drawDebt({
+ from: _borrower6,
+ borrower: _borrower6,
+ amountToBorrow: 6_000 * 1e18,
+ limitIndex: 5000,
+ collateralToPledge: 1_000 * 1e18,
+ newLup: 3_863.654368867279344664 * 1e18
+ });
+
+ _assertLoans({
+ noOfLoans: 6,
+ maxBorrower: _borrower6,
+ maxThresholdPrice: 6.005769230769230772 * 1e18
+ });
+
+ // borrower 4 draws debt and becomes loan with highest threshold price in heap
+ _drawDebt({
+ from: _borrower4,
+ borrower: _borrower4,
+ amountToBorrow: 10_000 * 1e18,
+ limitIndex: 5000,
+ collateralToPledge: 0,
+ newLup: 3_863.654368867279344664 * 1e18
+ });
+
+ _assertLoans({
+ noOfLoans: 6,
+ maxBorrower: _borrower4,
+ maxThresholdPrice: 14.013461538461538468 * 1e18
+ });
+
+ // borrower 4 repays debt, borrower 6 becomes loan with highest threshold price in heap
+ _repayDebt({
+ from: _borrower4,
+ borrower: _borrower4,
+ amountToRepay: 11_000 * 1e18,
+ amountRepaid: 11_000 * 1e18,
+ collateralToPull: 0,
+ newLup: 3_863.654368867279344664 * 1e18
+ });
+
+ _assertLoans({
+ noOfLoans: 6,
+ maxBorrower: _borrower6,
+ maxThresholdPrice: 6.005769230769230772 * 1e18
+ });
+
+ // borrower 6 repays debt, borrower 5 becomes loan with highest threshold price in heap
+ _repayDebt({
+ from: _borrower6,
+ borrower: _borrower6,
+ amountToRepay: 5_000 * 1e18,
+ amountRepaid: 5_000 * 1e18,
+ collateralToPull: 0,
+ newLup: 3_863.654368867279344664 * 1e18
+ });
+
+ _assertLoans({
+ noOfLoans: 6,
+ maxBorrower: _borrower5,
+ maxThresholdPrice: 5.004807692307692310 * 1e18
+ });
+
+ // borrower 6 draws more debt and becomes loan with highest threshold price in heap
+ _drawDebt({
+ from: _borrower6,
+ borrower: _borrower6,
+ amountToBorrow: 11_000 * 1e18,
+ limitIndex: 5000,
+ collateralToPledge: 0,
+ newLup: 3_863.654368867279344664 * 1e18
+ });
+
+ _assertLoans({
+ noOfLoans: 6,
+ maxBorrower: _borrower6,
+ maxThresholdPrice: 12.016346153846153854 * 1e18
+ });
+ }
+}
\ No newline at end of file
diff --git a/tests/forge/ERC20Pool/ERC20PoolMulticall.t.sol b/tests/forge/ERC20Pool/ERC20PoolMulticall.t.sol
index f4a61c935..26d758e5b 100644
--- a/tests/forge/ERC20Pool/ERC20PoolMulticall.t.sol
+++ b/tests/forge/ERC20Pool/ERC20PoolMulticall.t.sol
@@ -31,49 +31,49 @@ contract ERC20PoolMulticallTest is ERC20HelperContract {
bytes[] memory callsToExecute = new bytes[](3);
callsToExecute[0] = abi.encodeWithSignature(
- "addQuoteToken(uint256,uint256)",
+ "addQuoteToken(uint256,uint256,uint256)",
10_000 * 1e18,
- 2550
+ 2550,
+ block.timestamp + 5 minutes
);
callsToExecute[1] = abi.encodeWithSignature(
- "addQuoteToken(uint256,uint256)",
+ "addQuoteToken(uint256,uint256,uint256)",
10_000 * 1e18,
- 2551
+ 2551,
+ block.timestamp + 5 minutes
);
callsToExecute[2] = abi.encodeWithSignature(
- "addQuoteToken(uint256,uint256)",
+ "addQuoteToken(uint256,uint256,uint256)",
10_000 * 1e18,
- 2552
+ 2552,
+ block.timestamp + 5 minutes
);
changePrank(_lender);
vm.expectEmit(true, true, false, true);
- emit AddQuoteToken(_lender, 2550, 10_000 * 1e18, 10_000 * 1e27, MAX_PRICE);
+ emit AddQuoteToken(_lender, 2550, 10_000 * 1e18, 10_000 * 1e18, MAX_PRICE);
vm.expectEmit(true, true, false, true);
emit Transfer(_lender, address(_pool), 10_000 * 1e18);
vm.expectEmit(true, true, false, true);
- emit AddQuoteToken(_lender, 2551, 10_000 * 1e18, 10_000 * 1e27, MAX_PRICE);
+ emit AddQuoteToken(_lender, 2551, 10_000 * 1e18, 10_000 * 1e18, MAX_PRICE);
vm.expectEmit(true, true, false, true);
emit Transfer(_lender, address(_pool), 10_000 * 1e18);
vm.expectEmit(true, true, false, true);
- emit AddQuoteToken(_lender, 2552, 10_000 * 1e18, 10_000 * 1e27, MAX_PRICE);
+ emit AddQuoteToken(_lender, 2552, 10_000 * 1e18, 10_000 * 1e18, MAX_PRICE);
vm.expectEmit(true, true, false, true);
emit Transfer(_lender, address(_pool), 10_000 * 1e18);
ERC20Pool(address(_pool)).multicall(callsToExecute);
-
- _assertPoolPrices(
- {
+ _assertPoolPrices({
htp: 0,
htpIndex: 7388,
hpb: 3_010.892022197881557845 * 1e18,
hpbIndex: 2550,
lup: MAX_PRICE,
lupIndex: 0
- }
- );
+ });
// check balances
assertEq(_quote.balanceOf(address(_pool)), 30_000 * 1e18);
@@ -82,59 +82,47 @@ contract ERC20PoolMulticallTest is ERC20HelperContract {
assertEq(_pool.depositSize(), 30_000 * 1e18);
// check buckets
- _assertBucket(
- {
- index: 2550,
- lpBalance: 10_000 * 1e27,
- collateral: 0,
- deposit: 10_000 * 1e18,
- exchangeRate: 1 * 1e27
- }
- );
- _assertLenderLpBalance(
- {
- lender: _lender,
- index: 2550,
- lpBalance: 10_000 * 1e27,
- depositTime: _startTime
- }
- );
-
- _assertBucket(
- {
- index: 2551,
- lpBalance: 10_000 * 1e27,
- collateral: 0,
- deposit: 10_000 * 1e18,
- exchangeRate: 1 * 1e27
- }
- );
- _assertLenderLpBalance(
- {
- lender: _lender,
- index: 2551,
- lpBalance: 10_000 * 1e27,
- depositTime: _startTime
- }
- );
-
- _assertBucket(
- {
- index: 2552,
- lpBalance: 10_000 * 1e27,
- collateral: 0,
- deposit: 10_000 * 1e18,
- exchangeRate: 1 * 1e27
- }
- );
- _assertLenderLpBalance(
- {
- lender: _lender,
- index: 2552,
- lpBalance: 10_000 * 1e27,
- depositTime: _startTime
- }
- );
+ _assertBucket({
+ index: 2550,
+ lpBalance: 10_000 * 1e18,
+ collateral: 0,
+ deposit: 10_000 * 1e18,
+ exchangeRate: 1 * 1e18
+ });
+ _assertLenderLpBalance({
+ lender: _lender,
+ index: 2550,
+ lpBalance: 10_000 * 1e18,
+ depositTime: _startTime
+ });
+
+ _assertBucket({
+ index: 2551,
+ lpBalance: 10_000 * 1e18,
+ collateral: 0,
+ deposit: 10_000 * 1e18,
+ exchangeRate: 1 * 1e18
+ });
+ _assertLenderLpBalance({
+ lender: _lender,
+ index: 2551,
+ lpBalance: 10_000 * 1e18,
+ depositTime: _startTime
+ });
+
+ _assertBucket({
+ index: 2552,
+ lpBalance: 10_000 * 1e18,
+ collateral: 0,
+ deposit: 10_000 * 1e18,
+ exchangeRate: 1 * 1e18
+ });
+ _assertLenderLpBalance({
+ lender: _lender,
+ index: 2552,
+ lpBalance: 10_000 * 1e18,
+ depositTime: _startTime
+ });
}
function testMulticallRevertString() public {
@@ -149,7 +137,7 @@ contract ERC20PoolMulticallTest is ERC20HelperContract {
);
changePrank(_lender);
- vm.expectRevert(IPoolErrors.LimitIndexReached.selector);
+ vm.expectRevert(IPoolErrors.LimitIndexExceeded.selector);
ERC20Pool(address(_pool)).multicall(callsToExecute);
}
}
diff --git a/tests/forge/ERC20Pool/ERC20PoolPrecision.t.sol b/tests/forge/ERC20Pool/ERC20PoolPrecision.t.sol
index 8eb018c38..dbc6e5346 100644
--- a/tests/forge/ERC20Pool/ERC20PoolPrecision.t.sol
+++ b/tests/forge/ERC20Pool/ERC20PoolPrecision.t.sol
@@ -17,7 +17,7 @@ contract ERC20PoolPrecisionTest is ERC20DSTestPlus {
uint256 internal constant MAX_DEPOSIT = 1e22 * 1e18;
uint256 internal constant MAX_COLLATERAL = 1e12 * 1e18;
uint256 internal constant POOL_PRECISION = 1e18;
- uint256 internal constant LP_PRECISION = 1e27;
+ uint256 internal constant LP_PRECISION = 1e18;
uint256 internal _collateralPrecision;
uint256 internal _quotePrecision;
@@ -91,125 +91,101 @@ contract ERC20PoolPrecisionTest is ERC20DSTestPlus {
uint256 start = block.timestamp;
// deposit 50_000 quote tokens into each of 3 buckets
- _addInitialLiquidity(
- {
- from: _lender,
- amount: 50_000 * POOL_PRECISION,
- index: 2549
- }
- );
- _addInitialLiquidity(
- {
- from: _lender,
- amount: 50_000 * POOL_PRECISION,
- index: 2550
- }
- );
- _addInitialLiquidity(
- {
- from: _lender,
- amount: 50_000 * POOL_PRECISION,
- index: 2551
- }
- );
+ _addInitialLiquidity({
+ from: _lender,
+ amount: 50_000 * POOL_PRECISION,
+ index: 2549
+ });
+ _addInitialLiquidity({
+ from: _lender,
+ amount: 50_000 * POOL_PRECISION,
+ index: 2550
+ });
+ _addInitialLiquidity({
+ from: _lender,
+ amount: 50_000 * POOL_PRECISION,
+ index: 2551
+ });
// check balances
assertEq(_quote.balanceOf(address(_pool)), 150_000 * _quotePrecision);
assertEq(_quote.balanceOf(_lender), 50_000 * _quotePrecision);
// check initial pool state
- _assertPoolPrices(
- {
- htp: 0,
- htpIndex: 7388,
- hpb: 3_025.946482308870940904 * 1e18,
- hpbIndex: 2549,
- lup: MAX_PRICE,
- lupIndex: 0
- }
- );
- _assertLoans(
- {
- noOfLoans: 0,
- maxBorrower: address(0),
- maxThresholdPrice: 0
- }
- );
+ _assertPoolPrices({
+ htp: 0,
+ htpIndex: 7388,
+ hpb: 3_025.946482308870940904 * 1e18,
+ hpbIndex: 2549,
+ lup: MAX_PRICE,
+ lupIndex: 0
+ });
+ _assertLoans({
+ noOfLoans: 0,
+ maxBorrower: address(0),
+ maxThresholdPrice: 0
+ });
assertEq(_pool.depositSize(), 150_000 * POOL_PRECISION);
// check bucket balance
- _assertBucket(
- {
- index: 2549,
- lpBalance: 50_000 * 1e27,
- collateral: 0,
- deposit: 50_000 * POOL_PRECISION,
- exchangeRate: 1 * LP_PRECISION
- }
- );
- _assertLenderLpBalance(
- {
- lender: _lender,
- index: 2549,
- lpBalance: 50_000 * 1e27,
- depositTime: start
- }
- );
+ _assertBucket({
+ index: 2549,
+ lpBalance: 50_000 * 1e18,
+ collateral: 0,
+ deposit: 50_000 * POOL_PRECISION,
+ exchangeRate: 1 * LP_PRECISION
+ });
+ _assertLenderLpBalance({
+ lender: _lender,
+ index: 2549,
+ lpBalance: 50_000 * 1e18,
+ depositTime: start
+ });
skip(1 days); // skip to avoid penalty
// lender removes some quote token from highest priced bucket
- _removeLiquidity(
- {
- from: _lender,
- amount: 25_000 * POOL_PRECISION,
- index: 2549,
- newLup: MAX_PRICE,
- lpRedeem: 25_000 * 1e27
- }
- );
+ _removeLiquidity({
+ from: _lender,
+ amount: 25_000 * POOL_PRECISION,
+ index: 2549,
+ newLup: MAX_PRICE,
+ lpRedeem: 25_000 * 1e18
+ });
// check balances
assertEq(_quote.balanceOf(address(_pool)), 125_000 * _quotePrecision);
assertEq(_quote.balanceOf(_lender), 75_000 * _quotePrecision);
// check pool state
- _assertPoolPrices(
- {
- htp: 0,
- htpIndex: 7388,
- hpb: 3_025.946482308870940904 * 1e18,
- hpbIndex: 2549,
- lup: MAX_PRICE,
- lupIndex: 0
- }
- );
- _assertLoans(
- {
- noOfLoans: 0,
- maxBorrower: address(0),
- maxThresholdPrice: 0
- }
- );
+ _assertPoolPrices({
+ htp: 0,
+ htpIndex: 7388,
+ hpb: 3_025.946482308870940904 * 1e18,
+ hpbIndex: 2549,
+ lup: MAX_PRICE,
+ lupIndex: 0
+ });
+ _assertLoans({
+ noOfLoans: 0,
+ maxBorrower: address(0),
+ maxThresholdPrice: 0
+ });
assertEq(_pool.depositSize(), 125_000 * POOL_PRECISION);
// check bucket balance
- _assertBucket(
- {
- index: 2549,
- lpBalance: 25_000 * 1e27,
- collateral: 0,
- deposit: 25_000 * POOL_PRECISION,
- exchangeRate: 1 * 1e27
- }
- );
- _assertLenderLpBalance(
- {
- lender: _lender,
- index: 2549,
- lpBalance: 25_000 * LP_PRECISION,
- depositTime: start
- }
- );
+ _assertBucket({
+ index: 2549,
+ lpBalance: 25_000 * 1e18,
+ collateral: 0,
+ deposit: 25_000 * POOL_PRECISION,
+ exchangeRate: 1 * 1e18
+ });
+ _assertLenderLpBalance({
+ lender: _lender,
+ index: 2549,
+ lpBalance: 25_000 * LP_PRECISION,
+ depositTime: start
+ });
}
function testAddRemoveCollateralPrecision (
@@ -266,36 +242,28 @@ contract ERC20PoolPrecisionTest is ERC20DSTestPlus {
uint256 start = block.timestamp;
- _addInitialLiquidity(
- {
- from: _lender,
- amount: 50_000 * POOL_PRECISION,
- index: 2549
- }
- );
- _addInitialLiquidity(
- {
- from: _lender,
- amount: 50_000 * POOL_PRECISION,
- index: 2550
- }
- );
- _addInitialLiquidity(
- {
- from: _lender,
- amount: 50_000 * POOL_PRECISION,
- index: 2551
- }
- );
+ _addInitialLiquidity({
+ from: _lender,
+ amount: 50_000 * POOL_PRECISION,
+ index: 2549
+ });
+ _addInitialLiquidity({
+ from: _lender,
+ amount: 50_000 * POOL_PRECISION,
+ index: 2550
+ });
+ _addInitialLiquidity({
+ from: _lender,
+ amount: 50_000 * POOL_PRECISION,
+ index: 2551
+ });
// borrowers adds collateral
- _pledgeCollateral(
- {
- from: _borrower,
- borrower: _borrower,
- amount: 50 * POOL_PRECISION
- }
- );
+ _pledgeCollateral({
+ from: _borrower,
+ borrower: _borrower,
+ amount: 50 * POOL_PRECISION
+ });
// check balances
assertEq(_collateral.balanceOf(address(_pool)), 50 * _collateralPrecision);
@@ -304,55 +272,45 @@ contract ERC20PoolPrecisionTest is ERC20DSTestPlus {
assertEq(_quote.balanceOf(_borrower), 0);
// check pool state
- _assertPoolPrices(
- {
- htp: 0,
- htpIndex: 7388,
- hpb: 3_025.946482308870940904 * 1e18,
- hpbIndex: 2549,
- lup: MAX_PRICE,
- lupIndex: 0
- }
- );
- _assertLoans(
- {
- noOfLoans: 0,
- maxBorrower: address(0),
- maxThresholdPrice: 0
- }
- );
+ _assertPoolPrices({
+ htp: 0,
+ htpIndex: 7388,
+ hpb: 3_025.946482308870940904 * 1e18,
+ hpbIndex: 2549,
+ lup: MAX_PRICE,
+ lupIndex: 0
+ });
+ _assertLoans({
+ noOfLoans: 0,
+ maxBorrower: address(0),
+ maxThresholdPrice: 0
+ });
assertEq(_pool.depositSize(), 150_000 * POOL_PRECISION);
// check bucket balance
- _assertBucket(
- {
- index: 2549,
- lpBalance: 50_000 * LP_PRECISION,
- collateral: 0,
- deposit: 50_000 * POOL_PRECISION,
- exchangeRate: 1 * LP_PRECISION
- }
- );
- _assertLenderLpBalance(
- {
- lender: _lender,
- index: 2549,
- lpBalance: 50_000 * LP_PRECISION,
- depositTime: start
- }
- );
+ _assertBucket({
+ index: 2549,
+ lpBalance: 50_000 * LP_PRECISION,
+ collateral: 0,
+ deposit: 50_000 * POOL_PRECISION,
+ exchangeRate: 1 * LP_PRECISION
+ });
+ _assertLenderLpBalance({
+ lender: _lender,
+ index: 2549,
+ lpBalance: 50_000 * LP_PRECISION,
+ depositTime: start
+ });
// borrower borrows
uint256 price = _priceAt(2549);
- _borrow(
- {
- from: _borrower,
- amount: 10_000 * POOL_PRECISION,
- indexLimit: 3_000,
- newLup: price
- }
- );
+ _borrow({
+ from: _borrower,
+ amount: 10_000 * POOL_PRECISION,
+ indexLimit: 3_000,
+ newLup: price
+ });
// check balances
assertEq(_collateral.balanceOf(address(_pool)), 50 * _collateralPrecision);
@@ -363,54 +321,47 @@ contract ERC20PoolPrecisionTest is ERC20DSTestPlus {
// check pool state
uint256 debt = 10_008.653846153846150000 * 1e18;
uint256 col = 50 * 1e18;
- _assertBorrower(
- {
- borrower: _borrower,
- borrowerDebt: debt,
- borrowerCollateral: col,
- borrowert0Np: 209.180865384615384535 * 1e18,
- borrowerCollateralization: 15.116650694597107214 * 1e18
- }
- );
- _assertPoolPrices(
- {
- htp: 200.173076923076923000 * 1e18,
- htpIndex: 3093,
- hpb: 3_025.946482308870940904 * 1e18,
- hpbIndex: 2549,
- lup: price,
- lupIndex: 2549
- }
- );
- _assertLoans(
- {
- noOfLoans: 1,
- maxBorrower: _borrower,
- maxThresholdPrice: 200.173076923076923000 * 1e18
- }
- );
+
+ _assertBorrower({
+ borrower: _borrower,
+ borrowerDebt: debt,
+ borrowerCollateral: col,
+ borrowert0Np: 209.180865384615384535 * 1e18,
+ borrowerCollateralization: 15.116650694597107214 * 1e18
+ });
+ _assertPoolPrices({
+ htp: 200.173076923076923000 * 1e18,
+ htpIndex: 3093,
+ hpb: 3_025.946482308870940904 * 1e18,
+ hpbIndex: 2549,
+ lup: price,
+ lupIndex: 2549
+ });
+ _assertLoans({
+ noOfLoans: 1,
+ maxBorrower: _borrower,
+ maxThresholdPrice: 200.173076923076923000 * 1e18
+ });
+
(uint256 poolDebt,,) = _pool.debtInfo();
+
assertEq(_pool.depositSize(), 150_000 * POOL_PRECISION);
assertEq(poolDebt, debt);
assertEq(_pool.pledgedCollateral(), col);
- _assertBucket(
- {
- index: 2549,
- lpBalance: 50_000 * LP_PRECISION,
- collateral: 0,
- deposit: 50_000 * POOL_PRECISION,
- exchangeRate: 1 * LP_PRECISION
- }
- );
- _assertLenderLpBalance(
- {
- lender: _lender,
- index: 2549,
- lpBalance: 50_000 * LP_PRECISION,
- depositTime: start
- }
- );
+ _assertBucket({
+ index: 2549,
+ lpBalance: 50_000 * LP_PRECISION,
+ collateral: 0,
+ deposit: 50_000 * POOL_PRECISION,
+ exchangeRate: 1 * LP_PRECISION
+ });
+ _assertLenderLpBalance({
+ lender: _lender,
+ index: 2549,
+ lpBalance: 50_000 * LP_PRECISION,
+ depositTime: start
+ });
// borrower repays half of loan
_repayDebt({
@@ -432,54 +383,47 @@ contract ERC20PoolPrecisionTest is ERC20DSTestPlus {
// check pool state
debt = 5_008.653846153846150000 * 1e18;
col = 50 * 1e18;
- _assertBorrower(
- {
- borrower: _borrower,
- borrowerDebt: debt,
- borrowerCollateral: col,
- borrowert0Np: 209.180865384615384535 * 1e18,
- borrowerCollateralization: 30.207183159927296805 * 1e18
- }
- );
- _assertPoolPrices(
- {
- htp: 100.173076923076923000 * 1e18,
- htpIndex: 3232,
- hpb: 3_025.946482308870940904 * 1e18,
- hpbIndex: 2549,
- lup: price,
- lupIndex: 2549
- }
- );
- _assertLoans(
- {
- noOfLoans: 1,
- maxBorrower: _borrower,
- maxThresholdPrice: 100.173076923076923000 * 1e18
- }
- );
+
+ _assertBorrower({
+ borrower: _borrower,
+ borrowerDebt: debt,
+ borrowerCollateral: col,
+ borrowert0Np: 209.180865384615384535 * 1e18,
+ borrowerCollateralization: 30.207183159927296805 * 1e18
+ });
+ _assertPoolPrices({
+ htp: 100.173076923076923000 * 1e18,
+ htpIndex: 3232,
+ hpb: 3_025.946482308870940904 * 1e18,
+ hpbIndex: 2549,
+ lup: price,
+ lupIndex: 2549
+ });
+ _assertLoans({
+ noOfLoans: 1,
+ maxBorrower: _borrower,
+ maxThresholdPrice: 100.173076923076923000 * 1e18
+ });
+
(poolDebt,,) = _pool.debtInfo();
+
assertEq(_pool.depositSize(), 150_000 * 1e18);
assertEq(poolDebt, debt);
assertEq(_pool.pledgedCollateral(), col);
- _assertBucket(
- {
- index: 2549,
- lpBalance: 50_000 * LP_PRECISION,
- collateral: 0,
- deposit: 50_000 * 1e18,
- exchangeRate: 1 * 1e27
- }
- );
- _assertLenderLpBalance(
- {
- lender: _lender,
- index: 2549,
- lpBalance: 50_000 * LP_PRECISION,
- depositTime: start
- }
- );
+ _assertBucket({
+ index: 2549,
+ lpBalance: 50_000 * LP_PRECISION,
+ collateral: 0,
+ deposit: 50_000 * 1e18,
+ exchangeRate: 1 * 1e18
+ });
+ _assertLenderLpBalance({
+ lender: _lender,
+ index: 2549,
+ lpBalance: 50_000 * LP_PRECISION,
+ depositTime: start
+ });
// remove all of the remaining claimable collateral
uint256 unencumberedCollateral = col - _encumberedCollateral(debt, _lup());
@@ -536,7 +480,7 @@ contract ERC20PoolPrecisionTest is ERC20DSTestPlus {
lpBalance: 0,
collateral: 0,
deposit: 0,
- exchangeRate: 1e27
+ exchangeRate: 1e18
});
// addQuoteToken should add scaled quote token amount validate LP
@@ -545,8 +489,9 @@ contract ERC20PoolPrecisionTest is ERC20DSTestPlus {
amount: quoteAmount,
index: bucketId
});
+
(uint256 lenderLpBalance, ) = _pool.lenderInfo(bucketId, _lender);
- assertEq(lenderLpBalance, scaledQuoteAmount * 1e9);
+ assertEq(lenderLpBalance, scaledQuoteAmount);
// deposit collateral and sanity check bidder LPs
uint256 bidderLpBalance;
@@ -614,11 +559,12 @@ contract ERC20PoolPrecisionTest is ERC20DSTestPlus {
amount: scaledQuoteAmount1,
index: bucketId
});
+
(uint256 lpBalance1, ) = _pool.lenderInfo(bucketId, _lender);
// addQuoteToken should add scaled quote token amount and LP
vm.expectEmit(true, true, false, true);
- emit AddQuoteToken(lender2, bucketId, scaledQuoteAmount2, scaledQuoteAmount2 * 1e9, MAX_PRICE);
+ emit AddQuoteToken(lender2, bucketId, scaledQuoteAmount2, scaledQuoteAmount2, MAX_PRICE);
_addLiquidityNoEventCheck(lender2, quoteAmount2, bucketId);
(uint256 lpBalance2, ) = _pool.lenderInfo(bucketId, lender2);
if (scaledQuoteAmount2 != 0) {
@@ -654,58 +600,53 @@ contract ERC20PoolPrecisionTest is ERC20DSTestPlus {
uint256 amountToMove = bound(uint256(amountToMove_), 0, _lenderDepositNormalized);
init(boundColPrecision, boundQuotePrecision);
- _addInitialLiquidity(
- {
- from: _lender,
- amount: _lenderDepositNormalized,
- index: fromBucketId
- }
- );
+ _addInitialLiquidity({
+ from: _lender,
+ amount: _lenderDepositNormalized,
+ index: fromBucketId
+ });
if (fromBucketId == toBucketId) {
- _assertMoveLiquidityToSamePriceRevert(
- {
- from: _lender,
- amount: amountToMove,
- fromIndex: fromBucketId,
- toIndex: toBucketId
- }
- );
+ _assertMoveLiquidityToSamePriceRevert({
+ from: _lender,
+ amount: amountToMove,
+ fromIndex: fromBucketId,
+ toIndex: toBucketId
+ });
+
return;
}
if (amountToMove != 0 && amountToMove < _quoteDust) {
- _assertMoveLiquidityDustRevert(
- {
- from: _lender,
- amount: amountToMove,
- fromIndex: fromBucketId,
- toIndex: toBucketId
- }
- );
+ _assertMoveLiquidityDustRevert({
+ from: _lender,
+ amount: amountToMove,
+ fromIndex: fromBucketId,
+ toIndex: toBucketId
+ });
+
return;
}
- _moveLiquidity(
- {
- from: _lender,
- amount: amountToMove,
- fromIndex: fromBucketId,
- toIndex: toBucketId,
- lpRedeemFrom: amountToMove * 1e9,
- lpAwardTo: amountToMove * 1e9,
- newLup: MAX_PRICE
- }
- );
+ _moveLiquidity({
+ from: _lender,
+ amount: amountToMove,
+ fromIndex: fromBucketId,
+ toIndex: toBucketId,
+ lpRedeemFrom: amountToMove,
+ lpAwardTo: amountToMove,
+ newLup: MAX_PRICE
+ });
// validate from and to buckets have appropriate amounts of deposit and LPs
(, uint256 deposit,, uint256 lps,,) = _poolUtils.bucketInfo(address(_pool), fromBucketId);
uint256 remaining = _lenderDepositNormalized - amountToMove;
+
assertEq(deposit, remaining);
- assertEq(lps, remaining * 1e9);
+ assertEq(lps, remaining);
(, deposit,, lps,,) = _poolUtils.bucketInfo(address(_pool), toBucketId);
assertEq(deposit, amountToMove);
- assertEq(lps, amountToMove * 1e9);
+ assertEq(lps, amountToMove);
}
function testDrawMinDebtAmount(
@@ -767,8 +708,10 @@ contract ERC20PoolPrecisionTest is ERC20DSTestPlus {
// have last borrower attempt an bad repay before tearDown
(uint256 minDebtAmount, , , ) = _poolUtils.poolUtilizationInfo(address(_pool));
assertGt(minDebtAmount, 1);
+
(uint256 debt, , ) = _poolUtils.borrowerInfo(address(_pool), borrower);
uint256 repayAmount = debt - minDebtAmount / 2;
+
_assertRepayMinDebtRevert({
from: borrower,
borrower: borrower,
@@ -843,8 +786,10 @@ contract ERC20PoolPrecisionTest is ERC20DSTestPlus {
collateralToPledge: collateralToPledge,
newLup: _priceAt(bucketId)
});
+
(uint256 currentDebt, uint256 pledgedCollateral, ) = _poolUtils.borrowerInfo(address(_pool), _borrower);
assertGt(currentDebt, debtToDraw);
+
// round the collateral amount to token precision
uint256 collateralRounded = (collateralToPledge / collateralScale) * collateralScale;
assertEq(pledgedCollateral, collateralRounded);
@@ -853,6 +798,32 @@ contract ERC20PoolPrecisionTest is ERC20DSTestPlus {
skip(1 weeks);
}
+ function testFlashLoanPrecision(
+ uint8 collateralPrecisionDecimals_,
+ uint8 quotePrecisionDecimals_
+ ) external tearDown {
+ // setup fuzzy bounds and initialize the pool
+ uint256 collateralDecimals = bound(uint256(collateralPrecisionDecimals_), 1, 18);
+ uint256 quoteDecimals = bound(uint256(quotePrecisionDecimals_), 1, 18);
+ init(collateralDecimals, quoteDecimals);
+
+ // add liquidity
+ _addInitialLiquidity({
+ from: _lender,
+ amount: 10_000 * 1e18,
+ index: 2500
+ });
+
+ _pledgeCollateral({
+ from: _borrower,
+ borrower: _borrower,
+ amount: 150 * 1e18
+ });
+
+ assertEq(_pool.maxFlashLoan(address(_collateral)), 150 * 10 ** collateralDecimals);
+ assertEq(_pool.maxFlashLoan(address(_quote)), 10_000 * 10 ** quoteDecimals);
+ }
+
/**********************/
/*** Helper Methods ***/
@@ -913,4 +884,24 @@ contract ERC20PoolPrecisionTest is ERC20DSTestPlus {
newLup: MAX_PRICE
});
}
+
+ function testMoveQuoteDustAmountRevert() external virtual tearDown {
+ init(8, 6);
+
+ _addInitialLiquidity({
+ from: _lender,
+ amount: 50_000 * 1e6,
+ index: 2550
+ });
+
+ assertEq(_quoteDust, 0.000001 * 1e18);
+
+ _assertMoveLiquidityDustRevert({
+ from: _lender,
+ amount: 0.00000001 * 1e18,
+ fromIndex: 2550,
+ toIndex: 2551
+ });
+ }
+
}
\ No newline at end of file
diff --git a/tests/forge/ERC20Pool/ERC20PoolPurchaseQuote.t.sol b/tests/forge/ERC20Pool/ERC20PoolPurchaseQuote.t.sol
index a3e83fbe5..6806ef005 100644
--- a/tests/forge/ERC20Pool/ERC20PoolPurchaseQuote.t.sol
+++ b/tests/forge/ERC20Pool/ERC20PoolPurchaseQuote.t.sol
@@ -33,93 +33,78 @@ contract ERC20PoolPurchaseQuoteTokenTest is ERC20HelperContract {
uint256 testIndex = 2550;
// lender adds initial quote to pool
- _addInitialLiquidity(
- {
- from: _lender,
- amount: 10_000 * 1e18,
- index: testIndex
- }
- );
+ _addInitialLiquidity({
+ from: _lender,
+ amount: 10_000 * 1e18,
+ index: testIndex
+ });
// bidder deposits collateral into a bucket
uint256 collateralToPurchaseWith = 4 * 1e18;
- _addCollateral(
- {
- from: _bidder,
- amount: collateralToPurchaseWith,
- index: testIndex,
- lpAward: 12_043.56808879152623138 * 1e27
- }
- );
+
+ _addCollateral({
+ from: _bidder,
+ amount: collateralToPurchaseWith,
+ index: testIndex,
+ lpAward: 12_043.56808879152623138 * 1e18
+ });
// check bucket state and LPs
- _assertBucket(
- {
- index: testIndex,
- lpBalance: 22_043.56808879152623138 * 1e27,
- collateral: collateralToPurchaseWith,
- deposit: 10_000 * 1e18,
- exchangeRate: 1 * 1e27
- }
- );
- _assertLenderLpBalance(
- {
- lender: _lender,
- index: testIndex,
- lpBalance: 10_000 * 1e27,
- depositTime: _startTime
- }
- );
- _assertLenderLpBalance(
- {
- lender: _bidder,
- index: testIndex,
- lpBalance: 12_043.56808879152623138 * 1e27,
- depositTime: _startTime
- }
- );
+ _assertBucket({
+ index: testIndex,
+ lpBalance: 22_043.56808879152623138 * 1e18,
+ collateral: collateralToPurchaseWith,
+ deposit: 10_000 * 1e18,
+ exchangeRate: 1 * 1e18
+ });
+ _assertLenderLpBalance({
+ lender: _lender,
+ index: testIndex,
+ lpBalance: 10_000 * 1e18,
+ depositTime: _startTime
+ });
+ _assertLenderLpBalance({
+ lender: _bidder,
+ index: testIndex,
+ lpBalance: 12_043.56808879152623138 * 1e18,
+ depositTime: _startTime
+ });
uint256 availableCollateral = collateralToPurchaseWith;
skip(1 days); // skip to avoid penalty
+
// bidder uses their LP to purchase all quote token in the bucket
- _removeLiquidity(
- {
- from: _bidder,
- amount: 10_000 * 1e18,
- index: testIndex,
- newLup: _lup(),
- lpRedeem: 10_000 * 1e27
- }
- );
+ _removeLiquidity({
+ from: _bidder,
+ amount: 10_000 * 1e18,
+ index: testIndex,
+ newLup: _lup(),
+ lpRedeem: 10_000 * 1e18
+ });
+
assertEq(_quote.balanceOf(_bidder), 10_000 * 1e18);
// check bucket state
- _assertBucket(
- {
- index: testIndex,
- lpBalance: 12_043.56808879152623138 * 1e27,
- collateral: collateralToPurchaseWith,
- deposit: 0,
- exchangeRate: 1 * 1e27
- }
- );
- _assertLenderLpBalance(
- {
- lender: _lender,
- index: testIndex,
- lpBalance: 10_000 * 1e27,
- depositTime: _startTime
- }
- );
- _assertLenderLpBalance(
- {
- lender: _bidder,
- index: testIndex,
- lpBalance: 2_043.56808879152623138 * 1e27,
- depositTime: _startTime
- }
- );
+ _assertBucket({
+ index: testIndex,
+ lpBalance: 12_043.56808879152623138 * 1e18,
+ collateral: collateralToPurchaseWith,
+ deposit: 0,
+ exchangeRate: 1 * 1e18
+ });
+ _assertLenderLpBalance({
+ lender: _lender,
+ index: testIndex,
+ lpBalance: 10_000 * 1e18,
+ depositTime: _startTime
+ });
+ _assertLenderLpBalance({
+ lender: _bidder,
+ index: testIndex,
+ lpBalance: 2_043.56808879152623138 * 1e18,
+ depositTime: _startTime
+ });
// check pool state and balances
assertEq(_collateral.balanceOf(_lender), 0);
@@ -128,82 +113,67 @@ contract ERC20PoolPurchaseQuoteTokenTest is ERC20HelperContract {
assertEq(_quote.balanceOf(address(_pool)), 0);
// lender exchanges their LP for collateral
- _removeAllCollateral(
- {
- from: _lender,
- amount: 3.321274866808485288 * 1e18,
- index: testIndex,
- lpRedeem: 10_000 * 1e27
- }
- );
-
- _assertBucket(
- {
- index: testIndex,
- lpBalance: 2_043.56808879152623138 * 1e27,
- collateral: 0.678725133191514712 * 1e18,
- deposit: 0,
- exchangeRate: 0.999999999999999999892795209 * 1e27
- }
- );
- _assertLenderLpBalance(
- {
- lender: _lender,
- index: testIndex,
- lpBalance: 0,
- depositTime: _startTime
- }
- );
- _assertLenderLpBalance(
- {
- lender: _bidder,
- index: testIndex,
- lpBalance: 2_043.56808879152623138 * 1e27,
- depositTime: _startTime
- }
- );
+ _removeAllCollateral({
+ from: _lender,
+ amount: 3.321274866808485288 * 1e18,
+ index: testIndex,
+ lpRedeem: 10_000 * 1e18
+ });
+
+ _assertBucket({
+ index: testIndex,
+ lpBalance: 2_043.56808879152623138 * 1e18,
+ collateral: 0.678725133191514712 * 1e18,
+ deposit: 0,
+ exchangeRate: 1 * 1e18
+ });
+ _assertLenderLpBalance({
+ lender: _lender,
+ index: testIndex,
+ lpBalance: 0,
+ depositTime: _startTime
+ });
+ _assertLenderLpBalance({
+ lender: _bidder,
+ index: testIndex,
+ lpBalance: 2_043.56808879152623138 * 1e18,
+ depositTime: _startTime
+ });
assertEq(_collateral.balanceOf(_lender), 3.321274866808485288 * 1e18);
// bidder removes their _collateral
- _removeAllCollateral(
- {
- from: _bidder,
- amount: 0.678725133191514712 * 1e18,
- index: testIndex,
- lpRedeem: 2_043.56808879152623138 * 1e27
- }
- );
+ _removeAllCollateral({
+ from: _bidder,
+ amount: 0.678725133191514712 * 1e18,
+ index: testIndex,
+ lpRedeem: 2_043.568088791526231161 * 1e18
+ });
+
// check pool balances
assertEq(_collateral.balanceOf(address(_pool)), 0);
assertEq(_quote.balanceOf(address(_pool)), 0);
// check bucket state
- _assertBucket(
- {
- index: testIndex,
- lpBalance: 0,
- collateral: 0,
- deposit: 0,
- exchangeRate: 1 * 1e27
- }
- );
- _assertLenderLpBalance(
- {
- lender: _lender,
- index: testIndex,
- lpBalance: 0,
- depositTime: _startTime
- }
- );
- _assertLenderLpBalance(
- {
- lender: _bidder,
- index: testIndex,
- lpBalance: 0,
- depositTime: _startTime
- }
- );
+ _assertBucket({
+ index: testIndex,
+ lpBalance: 0,
+ collateral: 0,
+ deposit: 0,
+ exchangeRate: 1 * 1e18
+ });
+ _assertLenderLpBalance({
+ lender: _lender,
+ index: testIndex,
+ lpBalance: 0,
+ depositTime: _startTime
+ });
+ _assertLenderLpBalance({
+ lender: _bidder,
+ index: testIndex,
+ lpBalance: 0,
+ depositTime: _startTime
+ });
}
/**
@@ -214,62 +184,48 @@ contract ERC20PoolPurchaseQuoteTokenTest is ERC20HelperContract {
// lenders add liquidity
// lender 1
- _addInitialLiquidity(
- {
- from: _lender,
- amount: 6_000 * 1e18,
- index: 2550
- }
- );
- _addInitialLiquidity(
- {
- from: _lender,
- amount: 10_000 * 1e18,
- index: 2551
- }
- );
- _addInitialLiquidity(
- {
- from: _lender,
- amount: 5_000 * 1e18,
- index: 2552
- }
- );
+ _addInitialLiquidity({
+ from: _lender,
+ amount: 6_000 * 1e18,
+ index: 2550
+ });
+ _addInitialLiquidity({
+ from: _lender,
+ amount: 10_000 * 1e18,
+ index: 2551
+ });
+ _addInitialLiquidity({
+ from: _lender,
+ amount: 5_000 * 1e18,
+ index: 2552
+ });
// lender 2
- _addInitialLiquidity(
- {
- from: _lender1,
- amount: 4_000 * 1e18,
- index: 2550
- }
- );
- _addInitialLiquidity(
- {
- from: _lender1,
- amount: 5_000 * 1e18,
- index: 2552
- }
- );
+ _addInitialLiquidity({
+ from: _lender1,
+ amount: 4_000 * 1e18,
+ index: 2550
+ });
+ _addInitialLiquidity({
+ from: _lender1,
+ amount: 5_000 * 1e18,
+ index: 2552
+ });
skip(3600);
// borrower draws debt
- _pledgeCollateral(
- {
- from: _borrower,
- borrower: _borrower,
- amount: 100 * 1e18
- }
- );
- _borrow(
- {
- from: _borrower,
- amount: 15_000 * 1e18,
- indexLimit: 3_000,
- newLup: _priceAt(2551)
- }
- );
+ _pledgeCollateral({
+ from: _borrower,
+ borrower: _borrower,
+ amount: 100 * 1e18
+ });
+ _borrow({
+ from: _borrower,
+ amount: 15_000 * 1e18,
+ indexLimit: 3_000,
+ newLup: _priceAt(2551)
+ });
skip(86400);
@@ -286,101 +242,88 @@ contract ERC20PoolPurchaseQuoteTokenTest is ERC20HelperContract {
assertEq(collateralToPurchaseWith, 3.388032491631335842 * 1e18);
// bidder purchases all quote from the highest bucket
- _addCollateral(
- {
- from: _bidder,
- amount: collateralToPurchaseWith,
- index: 2550,
- lpAward: 10_200.383861467480875668505869503 * 1e27
- }
- );
+ _addCollateral({
+ from: _bidder,
+ amount: collateralToPurchaseWith,
+ index: 2550,
+ lpAward: 10_200.383861467480875669 * 1e18
+ });
skip(25 hours); // remove liquidity after one day to avoid early withdraw penalty
- _removeAllLiquidity(
- {
- from: _bidder,
- amount: amountWithInterest,
- index: 2550,
- newLup: _priceAt(2552),
- lpRedeem: 10_000.349513872212134187863727799 * 1e27
- }
- );
+
+ _removeAllLiquidity({
+ from: _bidder,
+ amount: amountWithInterest,
+ index: 2550,
+ newLup: _priceAt(2552),
+ lpRedeem: 10_000.349513872212134207 * 1e18
+ });
// bidder withdraws unused collateral
uint256 expectedCollateral = 0.066443194797165079 * 1e18;
- _removeAllCollateral(
- {
- from: _bidder,
- amount: expectedCollateral,
- index: 2550,
- lpRedeem: 200.034347595268741480642141704 * 1e27
- }
- );
- _assertLenderLpBalance(
- {
- lender: _bidder,
- index: 2550,
- lpBalance: 0,
- depositTime: _startTime + 3600 + 86400
- }
- );
+
+ _removeAllCollateral({
+ from: _bidder,
+ amount: expectedCollateral,
+ index: 2550,
+ lpRedeem: 200.034347595268741462 * 1e18
+ });
+
+ _assertLenderLpBalance({
+ lender: _bidder,
+ index: 2550,
+ lpBalance: 0,
+ depositTime: _startTime + 3600 + 86400
+ });
skip(7200);
// lender exchanges their LP for collateral
expectedCollateral = 1.992953578100502458 * 1e18;
- _removeAllCollateral(
- {
- from: _lender,
- amount: expectedCollateral,
- index: 2550,
- lpRedeem: 6_000 * 1e27
- }
- );
-
- _assertLenderLpBalance(
- {
- lender: _lender,
- index: 2550,
- lpBalance: 0,
- depositTime: _startTime
- }
- );
+
+ _removeAllCollateral({
+ from: _lender,
+ amount: expectedCollateral,
+ index: 2550,
+ lpRedeem: 6_000 * 1e18
+ });
+
+ _assertLenderLpBalance({
+ lender: _lender,
+ index: 2550,
+ lpBalance: 0,
+ depositTime: _startTime
+ });
skip(3600);
// lender1 exchanges their LP for collateral
expectedCollateral = 1.328635718733668305 * 1e18;
- _removeAllCollateral(
- {
- from: _lender1,
- amount: expectedCollateral,
- index: 2550,
- lpRedeem: 4_000 * 1e27
- }
- );
-
- _assertLenderLpBalance(
- {
- lender: _lender1,
- index: 2550,
- lpBalance: 0,
- depositTime: _startTime
- }
- );
+
+ _removeAllCollateral({
+ from: _lender1,
+ amount: expectedCollateral,
+ index: 2550,
+ lpRedeem: 4_000 * 1e18
+ });
+
+ _assertLenderLpBalance({
+ lender: _lender1,
+ index: 2550,
+ lpBalance: 0,
+ depositTime: _startTime
+ });
// check pool balances
assertEq(_collateral.balanceOf(address(_pool)), 100 * 1e18);
// check bucket state
- _assertBucket(
- {
- index: 2550,
- lpBalance: 0,
- collateral: 0,
- deposit: 0,
- exchangeRate: 1 * 1e27
- }
- );
+ _assertBucket({
+ index: 2550,
+ lpBalance: 0,
+ collateral: 0,
+ deposit: 0,
+ exchangeRate: 1 * 1e18
+ });
}
}
diff --git a/tests/forge/ERC20Pool/ERC20PoolQuoteToken.t.sol b/tests/forge/ERC20Pool/ERC20PoolQuoteToken.t.sol
index 3ccda4596..bd072cf69 100644
--- a/tests/forge/ERC20Pool/ERC20PoolQuoteToken.t.sol
+++ b/tests/forge/ERC20Pool/ERC20PoolQuoteToken.t.sol
@@ -3,6 +3,7 @@ pragma solidity 0.8.14;
import { ERC20HelperContract } from './ERC20DSTestPlus.sol';
+import 'src/interfaces/pool/IPool.sol';
import 'src/libraries/helpers/PoolHelper.sol';
contract ERC20PoolQuoteTokenTest is ERC20HelperContract {
@@ -33,22 +34,13 @@ contract ERC20PoolQuoteTokenTest is ERC20HelperContract {
function testPoolDepositQuoteToken() external tearDown {
assertEq(_hpb(), MIN_PRICE);
- // should revert if trying to deposit at index 0
- _assertAddLiquidityAtIndex0Revert(
- {
- from: _lender,
- amount: 10_000 * 1e18
- }
- );
-
// test 10_000 deposit at price of 3_010.892022197881557845
- _addInitialLiquidity(
- {
- from: _lender,
- amount: 10_000 * 1e18,
- index: 2550
- }
- );
+ _addInitialLiquidity({
+ from: _lender,
+ amount: 10_000 * 1e18,
+ index: 2550
+ });
+
_assertPool(
PoolParams({
htp: 0,
@@ -66,35 +58,31 @@ contract ERC20PoolQuoteTokenTest is ERC20HelperContract {
interestRateUpdate: _startTime
})
);
- _assertBucket(
- {
- index: 2550,
- lpBalance: 10_000 * 1e27,
- collateral: 0,
- deposit: 10_000 * 1e18,
- exchangeRate: 1 * 1e27
- }
- );
- _assertLenderLpBalance(
- {
- lender: _lender,
- index: 2550,
- lpBalance: 10_000 * 1e27,
- depositTime: _startTime
- }
- );
+ _assertBucket({
+ index: 2550,
+ lpBalance: 10_000 * 1e18,
+ collateral: 0,
+ deposit: 10_000 * 1e18,
+ exchangeRate: 1 * 1e18
+ });
+ _assertLenderLpBalance({
+ lender: _lender,
+ index: 2550,
+ lpBalance: 10_000 * 1e18,
+ depositTime: _startTime
+ });
+
// check balances
assertEq(_quote.balanceOf(address(_pool)), 10_000 * 1e18);
assertEq(_quote.balanceOf(_lender), 190_000 * 1e18);
// test 20_000 deposit at price of 2_995.912459898389633881
- _addInitialLiquidity(
- {
- from: _lender,
- amount: 20_000 * 1e18,
- index: 2551
- }
- );
+ _addInitialLiquidity({
+ from: _lender,
+ amount: 20_000 * 1e18,
+ index: 2551
+ });
+
_assertPool(
PoolParams({
htp: 0,
@@ -112,54 +100,44 @@ contract ERC20PoolQuoteTokenTest is ERC20HelperContract {
interestRateUpdate: _startTime
})
);
-
- _assertBucket(
- {
- index: 2550,
- lpBalance: 10_000 * 1e27,
- collateral: 0,
- deposit: 10_000 * 1e18,
- exchangeRate: 1 * 1e27
- }
- );
- _assertLenderLpBalance(
- {
- lender: _lender,
- index: 2550,
- lpBalance: 10_000 * 1e27,
- depositTime: _startTime
- }
- );
- _assertBucket(
- {
- index: 2551,
- lpBalance: 20_000 * 1e27,
- collateral: 0,
- deposit: 20_000 * 1e18,
- exchangeRate: 1 * 1e27
- }
- );
- _assertLenderLpBalance(
- {
- lender: _lender,
- index: 2551,
- lpBalance: 20_000 * 1e27,
- depositTime: _startTime
- }
- );
+ _assertBucket({
+ index: 2550,
+ lpBalance: 10_000 * 1e18,
+ collateral: 0,
+ deposit: 10_000 * 1e18,
+ exchangeRate: 1 * 1e18
+ });
+ _assertLenderLpBalance({
+ lender: _lender,
+ index: 2550,
+ lpBalance: 10_000 * 1e18,
+ depositTime: _startTime
+ });
+ _assertBucket({
+ index: 2551,
+ lpBalance: 20_000 * 1e18,
+ collateral: 0,
+ deposit: 20_000 * 1e18,
+ exchangeRate: 1 * 1e18
+ });
+ _assertLenderLpBalance({
+ lender: _lender,
+ index: 2551,
+ lpBalance: 20_000 * 1e18,
+ depositTime: _startTime
+ });
// check balances
assertEq(_quote.balanceOf(address(_pool)), 30_000 * 1e18);
assertEq(_quote.balanceOf(_lender), 170_000 * 1e18);
// test 40_000 deposit at price of 3_025.946482308870940904 DAI
- _addInitialLiquidity(
- {
- from: _lender,
- amount: 40_000 * 1e18,
- index: 2549
- }
- );
+ _addInitialLiquidity({
+ from: _lender,
+ amount: 40_000 * 1e18,
+ index: 2549
+ });
+
_assertPool(
PoolParams({
htp: 0,
@@ -177,92 +155,107 @@ contract ERC20PoolQuoteTokenTest is ERC20HelperContract {
interestRateUpdate: _startTime
})
);
-
- _assertBucket(
- {
- index: 2549,
- lpBalance: 40_000 * 1e27,
- collateral: 0,
- deposit: 40_000 * 1e18,
- exchangeRate: 1 * 1e27
- }
- );
- _assertLenderLpBalance(
- {
- lender: _lender,
- index: 2549,
- lpBalance: 40_000 * 1e27,
- depositTime: _startTime
- }
- );
- _assertBucket(
- {
- index: 2550,
- lpBalance: 10_000 * 1e27,
- collateral: 0,
- deposit: 10_000 * 1e18,
- exchangeRate: 1 * 1e27
- }
- );
- _assertLenderLpBalance(
- {
- lender: _lender,
- index: 2550,
- lpBalance: 10_000 * 1e27,
- depositTime: _startTime
- }
- );
- _assertBucket(
- {
- index: 2551,
- lpBalance: 20_000 * 1e27,
- collateral: 0,
- deposit: 20_000 * 1e18,
- exchangeRate: 1 * 1e27
- }
- );
- _assertLenderLpBalance(
- {
- lender: _lender,
- index: 2551,
- lpBalance: 20_000 * 1e27,
- depositTime: _startTime
- }
- );
+ _assertBucket({
+ index: 2549,
+ lpBalance: 40_000 * 1e18,
+ collateral: 0,
+ deposit: 40_000 * 1e18,
+ exchangeRate: 1 * 1e18
+ });
+ _assertLenderLpBalance({
+ lender: _lender,
+ index: 2549,
+ lpBalance: 40_000 * 1e18,
+ depositTime: _startTime
+ });
+ _assertBucket({
+ index: 2550,
+ lpBalance: 10_000 * 1e18,
+ collateral: 0,
+ deposit: 10_000 * 1e18,
+ exchangeRate: 1 * 1e18
+ });
+ _assertLenderLpBalance({
+ lender: _lender,
+ index: 2550,
+ lpBalance: 10_000 * 1e18,
+ depositTime: _startTime
+ });
+ _assertBucket({
+ index: 2551,
+ lpBalance: 20_000 * 1e18,
+ collateral: 0,
+ deposit: 20_000 * 1e18,
+ exchangeRate: 1 * 1e18
+ });
+ _assertLenderLpBalance({
+ lender: _lender,
+ index: 2551,
+ lpBalance: 20_000 * 1e18,
+ depositTime: _startTime
+ });
// check balances
assertEq(_quote.balanceOf(address(_pool)), 70_000 * 1e18);
assertEq(_quote.balanceOf(_lender), 130_000 * 1e18);
}
- function testPoolRemoveQuoteToken() external tearDown {
- _addLiquidity(
- {
- from: _lender,
- amount: 40_000 * 1e18,
- index: 2549,
- lpAward: 40_000 * 1e27,
- newLup: MAX_PRICE
- }
- );
- _addLiquidity(
- {
- from: _lender,
- amount: 10_000 * 1e18,
- index: 2550,
- lpAward: 10_000 * 1e27,
- newLup: MAX_PRICE
- }
- );
- _addLiquidity(
- {
- from: _lender,
- amount: 20_000 * 1e18,
- index: 2551,
- lpAward: 20_000 * 1e27,
- newLup: MAX_PRICE
- }
+ function testPoolAddQuoteTokenReverts() external tearDown {
+ // should revert if trying to deposit at index 0
+ _assertAddLiquidityAtIndex0Revert({
+ from: _lender,
+ amount: 10_000 * 1e18
+ });
+
+ // should revert if passing an already-expired timestamp
+ _assertAddLiquidityExpiredRevert({
+ from: _lender,
+ amount: 100_000 * 1e18,
+ index: 3232,
+ expiry: block.timestamp - 1 minutes
+ });
+
+ // should revert if passing future timestamp but time has elapsed
+ bytes memory data = abi.encodeWithSignature(
+ "addQuoteToken(uint256,uint256,uint256)",
+ 50_000 * 1e18,
+ 3333,
+ block.timestamp + 5 minutes
);
+
+ // should succeed if time hasn't passed
+ (bool success, ) = address(_pool).call(data);
+ assertEq(success, true);
+
+ // should fail if expiration exceeded
+ skip(6 minutes);
+ vm.expectRevert(IPoolErrors.TransactionExpired.selector);
+ (success, ) = address(_pool).call(data);
+ }
+
+ function testPoolRemoveQuoteToken() external tearDown {
+ _addLiquidity({
+ from: _lender,
+ amount: 40_000 * 1e18,
+ index: 2549,
+ lpAward: 40_000 * 1e18,
+ newLup: MAX_PRICE
+ });
+ _addLiquidity({
+ from: _lender,
+ amount: 10_000 * 1e18,
+ index: 2550,
+ lpAward: 10_000 * 1e18,
+ newLup: MAX_PRICE
+ });
+ _addLiquidity( {
+ from: _lender,
+ amount: 20_000 * 1e18,
+ index: 2551,
+ lpAward: 20_000 * 1e18,
+ newLup: MAX_PRICE
+ });
+
_assertPool(
PoolParams({
htp: 0,
@@ -280,73 +273,60 @@ contract ERC20PoolQuoteTokenTest is ERC20HelperContract {
interestRateUpdate: _startTime
})
);
-
- _assertBucket(
- {
- index: 2549,
- lpBalance: 40_000 * 1e27,
- collateral: 0,
- deposit: 40_000 * 1e18,
- exchangeRate: 1 * 1e27
- }
- );
- _assertLenderLpBalance(
- {
- lender: _lender,
- index: 2549,
- lpBalance: 40_000 * 1e27,
- depositTime: _startTime
- }
- );
- _assertBucket(
- {
- index: 2550,
- lpBalance: 10_000 * 1e27,
- collateral: 0,
- deposit: 10_000 * 1e18,
- exchangeRate: 1 * 1e27
- }
- );
- _assertLenderLpBalance(
- {
- lender: _lender,
- index: 2550,
- lpBalance: 10_000 * 1e27,
- depositTime: _startTime
- }
- );
- _assertBucket(
- {
- index: 2551,
- lpBalance: 20_000 * 1e27,
- collateral: 0,
- deposit: 20_000 * 1e18,
- exchangeRate: 1 * 1e27
- }
- );
- _assertLenderLpBalance(
- {
- lender: _lender,
- index: 2551,
- lpBalance: 20_000 * 1e27,
- depositTime: _startTime
- }
- );
+ _assertBucket({
+ index: 2549,
+ lpBalance: 40_000 * 1e18,
+ collateral: 0,
+ deposit: 40_000 * 1e18,
+ exchangeRate: 1 * 1e18
+ });
+ _assertLenderLpBalance({
+ lender: _lender,
+ index: 2549,
+ lpBalance: 40_000 * 1e18,
+ depositTime: _startTime
+ });
+ _assertBucket({
+ index: 2550,
+ lpBalance: 10_000 * 1e18,
+ collateral: 0,
+ deposit: 10_000 * 1e18,
+ exchangeRate: 1 * 1e18
+ });
+ _assertLenderLpBalance({
+ lender: _lender,
+ index: 2550,
+ lpBalance: 10_000 * 1e18,
+ depositTime: _startTime
+ });
+ _assertBucket({
+ index: 2551,
+ lpBalance: 20_000 * 1e18,
+ collateral: 0,
+ deposit: 20_000 * 1e18,
+ exchangeRate: 1 * 1e18
+ });
+ _assertLenderLpBalance({
+ lender: _lender,
+ index: 2551,
+ lpBalance: 20_000 * 1e18,
+ depositTime: _startTime
+ });
// check balances
assertEq(_quote.balanceOf(address(_pool)), 70_000 * 1e18);
assertEq(_quote.balanceOf(_lender), 130_000 * 1e18);
skip(1 days); // skip to avoid penalty
- _removeLiquidity(
- {
- from: _lender,
- amount: 5_000 * 1e18,
- index: 2549,
- newLup: MAX_PRICE,
- lpRedeem: 5_000 * 1e27
- }
- );
+
+ _removeLiquidity({
+ from: _lender,
+ amount: 5_000 * 1e18,
+ index: 2549,
+ newLup: MAX_PRICE,
+ lpRedeem: 5_000 * 1e18
+ });
+
_assertPool(
PoolParams({
htp: 0,
@@ -364,72 +344,58 @@ contract ERC20PoolQuoteTokenTest is ERC20HelperContract {
interestRateUpdate: _startTime + 1 days
})
);
-
- _assertBucket(
- {
- index: 2549,
- lpBalance: 35_000 * 1e27,
- collateral: 0,
- deposit: 35_000 * 1e18,
- exchangeRate: 1 * 1e27
- }
- );
- _assertLenderLpBalance(
- {
- lender: _lender,
- index: 2549,
- lpBalance: 35_000 * 1e27,
- depositTime: _startTime
- }
- );
- _assertBucket(
- {
- index: 2550,
- lpBalance: 10_000 * 1e27,
- collateral: 0,
- deposit: 10_000 * 1e18,
- exchangeRate: 1 * 1e27
- }
- );
- _assertLenderLpBalance(
- {
- lender: _lender,
- index: 2550,
- lpBalance: 10_000 * 1e27,
- depositTime: _startTime
- }
- );
- _assertBucket(
- {
- index: 2551,
- lpBalance: 20_000 * 1e27,
- collateral: 0,
- deposit: 20_000 * 1e18,
- exchangeRate: 1 * 1e27
- }
- );
- _assertLenderLpBalance(
- {
- lender: _lender,
- index: 2551,
- lpBalance: 20_000 * 1e27,
- depositTime: _startTime
- }
- );
+ _assertBucket({
+ index: 2549,
+ lpBalance: 35_000 * 1e18,
+ collateral: 0,
+ deposit: 35_000 * 1e18,
+ exchangeRate: 1 * 1e18
+ });
+ _assertLenderLpBalance({
+ lender: _lender,
+ index: 2549,
+ lpBalance: 35_000 * 1e18,
+ depositTime: _startTime
+ });
+ _assertBucket({
+ index: 2550,
+ lpBalance: 10_000 * 1e18,
+ collateral: 0,
+ deposit: 10_000 * 1e18,
+ exchangeRate: 1 * 1e18
+ });
+ _assertLenderLpBalance({
+ lender: _lender,
+ index: 2550,
+ lpBalance: 10_000 * 1e18,
+ depositTime: _startTime
+ });
+ _assertBucket({
+ index: 2551,
+ lpBalance: 20_000 * 1e18,
+ collateral: 0,
+ deposit: 20_000 * 1e18,
+ exchangeRate: 1 * 1e18
+ });
+ _assertLenderLpBalance({
+ lender: _lender,
+ index: 2551,
+ lpBalance: 20_000 * 1e18,
+ depositTime: _startTime
+ });
// check balances
assertEq(_quote.balanceOf(address(_pool)), 65_000 * 1e18);
assertEq(_quote.balanceOf(_lender), 135_000 * 1e18);
- _removeLiquidity(
- {
- from: _lender,
- amount: 35_000 * 1e18,
- index: 2549,
- newLup: MAX_PRICE,
- lpRedeem: 35_000 * 1e27
- }
- );
+ _removeLiquidity({
+ from: _lender,
+ amount: 35_000 * 1e18,
+ index: 2549,
+ newLup: MAX_PRICE,
+ lpRedeem: 35_000 * 1e18
+ });
+
_assertPool(
PoolParams({
htp: 0,
@@ -447,58 +413,45 @@ contract ERC20PoolQuoteTokenTest is ERC20HelperContract {
interestRateUpdate: _startTime + 1 days
})
);
-
- _assertBucket(
- {
- index: 2549,
- lpBalance: 0,
- collateral: 0,
- deposit: 0,
- exchangeRate: 1 * 1e27
- }
- );
- _assertLenderLpBalance(
- {
- lender: _lender,
- index: 2549,
- lpBalance: 0,
- depositTime: _startTime
- }
- );
- _assertBucket(
- {
- index: 2550,
- lpBalance: 10_000 * 1e27,
- collateral: 0,
- deposit: 10_000 * 1e18,
- exchangeRate: 1 * 1e27
- }
- );
- _assertLenderLpBalance(
- {
- lender: _lender,
- index: 2550,
- lpBalance: 10_000 * 1e27,
- depositTime: _startTime
- }
- );
- _assertBucket(
- {
- index: 2551,
- lpBalance: 20_000 * 1e27,
- collateral: 0,
- deposit: 20_000 * 1e18,
- exchangeRate: 1 * 1e27
- }
- );
- _assertLenderLpBalance(
- {
- lender: _lender,
- index: 2551,
- lpBalance: 20_000 * 1e27,
- depositTime: _startTime
- }
- );
+ _assertBucket({
+ index: 2549,
+ lpBalance: 0,
+ collateral: 0,
+ deposit: 0,
+ exchangeRate: 1 * 1e18
+ });
+ _assertLenderLpBalance({
+ lender: _lender,
+ index: 2549,
+ lpBalance: 0,
+ depositTime: _startTime
+ });
+ _assertBucket({
+ index: 2550,
+ lpBalance: 10_000 * 1e18,
+ collateral: 0,
+ deposit: 10_000 * 1e18,
+ exchangeRate: 1 * 1e18
+ });
+ _assertLenderLpBalance({
+ lender: _lender,
+ index: 2550,
+ lpBalance: 10_000 * 1e18,
+ depositTime: _startTime
+ });
+ _assertBucket({
+ index: 2551,
+ lpBalance: 20_000 * 1e18,
+ collateral: 0,
+ deposit: 20_000 * 1e18,
+ exchangeRate: 1 * 1e18
+ });
+ _assertLenderLpBalance({
+ lender: _lender,
+ index: 2551,
+ lpBalance: 20_000 * 1e18,
+ depositTime: _startTime
+ });
// check balances
assertEq(_quote.balanceOf(address(_pool)), 30_000 * 1e18);
@@ -512,32 +465,31 @@ contract ERC20PoolQuoteTokenTest is ERC20HelperContract {
*/
function testPoolRemoveQuoteTokenNotAvailable() external tearDown {
_mintCollateralAndApproveTokens(_borrower, _collateral.balanceOf(_borrower) + 3_500_000 * 1e18);
+
// lender adds initial quote token
- _addLiquidity(
- {
- from: _lender,
- amount: 11_000 * 1e18,
- index: 4550,
- lpAward: 11_000 * 1e27,
- newLup: MAX_PRICE
- }
- );
+ _addLiquidity({
+ from: _lender,
+ amount: 11_000 * 1e18,
+ index: 4550,
+ lpAward: 11_000 * 1e18,
+ newLup: MAX_PRICE
+ });
_drawDebt({
- from: _borrower,
- borrower: _borrower,
- amountToBorrow: 10_000 * 1e18,
- limitIndex: 7000,
+ from: _borrower,
+ borrower: _borrower,
+ amountToBorrow: 10_000 * 1e18,
+ limitIndex: 7000,
collateralToPledge: 3_500_000 * 1e18,
- newLup: 0.140143083210662942 * 1e18
+ newLup: 0.140143083210662942 * 1e18
});
_assertRemoveAllLiquidityLupBelowHtpRevert(
- {
- from: _lender,
- index: 4550
- }
- );
+ {
+ from: _lender,
+ index: 4550
+ }
+ );
}
/**
@@ -549,172 +501,147 @@ contract ERC20PoolQuoteTokenTest is ERC20HelperContract {
function testPoolRemoveQuoteTokenReverts() external tearDown {
_mintCollateralAndApproveTokens(_borrower, _collateral.balanceOf(_borrower) + 3_500_000 * 1e18);
_mintCollateralAndApproveTokens(_lender, 1 * 1e18);
+
// lender adds initial quote token
- _addLiquidity(
- {
- from: _lender,
- amount: 41_000 * 1e18,
- index: 4549,
- lpAward: 41_000 * 1e27,
- newLup: MAX_PRICE
- }
- );
- _addLiquidity(
- {
- from: _lender,
- amount: 10_000 * 1e18,
- index: 4550,
- lpAward: 10_000 * 1e27,
- newLup: MAX_PRICE
- }
- );
- _addLiquidity(
- {
- from: _lender,
- amount: 20_000 * 1e18,
- index: 4551,
- lpAward: 20_000 * 1e27,
- newLup: MAX_PRICE
- }
- );
- _addLiquidity(
- {
- from: _lender,
- amount: 30_000 * 1e18,
- index: 4990,
- lpAward: 30_000 * 1e27,
- newLup: MAX_PRICE
- }
- );
+ _addLiquidity({
+ from: _lender,
+ amount: 41_000 * 1e18,
+ index: 4549,
+ lpAward: 41_000 * 1e18,
+ newLup: MAX_PRICE
+ });
+ _addLiquidity({
+ from: _lender,
+ amount: 10_000 * 1e18,
+ index: 4550,
+ lpAward: 10_000 * 1e18,
+ newLup: MAX_PRICE
+ });
+ _addLiquidity({
+ from: _lender,
+ amount: 20_000 * 1e18,
+ index: 4551,
+ lpAward: 20_000 * 1e18,
+ newLup: MAX_PRICE
+ });
+ _addLiquidity( {
+ from: _lender,
+ amount: 30_000 * 1e18,
+ index: 4990,
+ lpAward: 30_000 * 1e18,
+ newLup: MAX_PRICE
+ });
+
// add collateral in order to give lender LPs in bucket 5_000 with 0 deposit
// used to test revert on remove when bucket deposit is 0
- _addCollateral(
- {
- from: _lender,
- amount: 1 * 1e18,
- index: 5000,
- lpAward: 0.014854015662334135 * 1e27
- }
- );
- _pledgeCollateral(
- {
- from: _borrower,
- borrower: _borrower,
- amount: 3_500_000 * 1e18
- }
- );
- _borrow(
- {
- from: _borrower,
- amount: 70_000 * 1e18,
- indexLimit: 7_000,
- newLup: 0.139445853940958153 * 1e18
- }
- );
+ _addCollateral({
+ from: _lender,
+ amount: 1 * 1e18,
+ index: 5000,
+ lpAward: 0.014854015662334135 * 1e18
+ });
+
+ _pledgeCollateral({
+ from: _borrower,
+ borrower: _borrower,
+ amount: 3_500_000 * 1e18
+ });
+ _borrow({
+ from: _borrower,
+ amount: 70_000 * 1e18,
+ indexLimit: 7_000,
+ newLup: 0.139445853940958153 * 1e18
+ });
// ensure lender cannot withdraw from a bucket with no deposit
- _assertRemoveAllLiquidityNoClaimRevert(
- {
- from: _lender1,
- index: 4550
- }
- );
+ _assertRemoveAllLiquidityNoClaimRevert({
+ from: _lender1,
+ index: 4550
+ });
+
// should revert if no quote token in bucket deposit
- _assertRemoveInsufficientLiquidityRevert(
- {
- from: _lender,
- amount: 1 * 1e18,
- index: 5000
- }
- );
+ _assertRemoveInsufficientLiquidityRevert({
+ from: _lender,
+ amount: 1 * 1e18,
+ index: 5000
+ });
+
// should revert if removing quote token from higher price buckets would drive lup below htp
- _assertRemoveLiquidityLupBelowHtpRevert(
- {
- from: _lender,
- amount: 20_000 * 1e18,
- index: 4551
- }
- );
+ _assertRemoveLiquidityLupBelowHtpRevert({
+ from: _lender,
+ amount: 20_000 * 1e18,
+ index: 4551
+ });
- _addLiquidity(
- {
- from: _lender1,
- amount: 20_000 * 1e18,
- index: 4550,
- lpAward: 20_000 * 1e27,
- newLup: _priceAt(4550)
- }
- );
+ _addLiquidity({
+ from: _lender1,
+ amount: 20_000 * 1e18,
+ index: 4550,
+ lpAward: 20_000 * 1e18,
+ newLup: _priceAt(4550)
+ });
skip(1 days); // skip to avoid penalty
+
// should be able to removeQuoteToken
- _removeLiquidity(
- {
- from: _lender,
- amount: 10_000 * 1e18,
- index: 4990,
- newLup: _priceAt(4550),
- lpRedeem: 10_000 * 1e27
- }
- );
+ _removeLiquidity({
+ from: _lender,
+ amount: 10_000 * 1e18,
+ index: 4990,
+ newLup: _priceAt(4550),
+ lpRedeem: 10_000 * 1e18
+ });
}
function testPoolRemoveQuoteTokenWithCollateral() external {
// add 10 collateral into the 100 bucket, for LP worth 1000 quote tokens
_mintCollateralAndApproveTokens(_lender, 10 * 1e18);
+
uint256 i100 = _indexOf(100 * 1e18);
- _addCollateral(
- {
- from: _lender,
- amount: 10 * 1e18,
- index: i100,
- lpAward: 1003.3236814328200989 * 1e27
- }
- );
- // someone else deposits into the bucket
- _addLiquidity(
- {
- from: _lender1,
- amount: 900 * 1e18,
- index: i100,
- lpAward: 900 * 1e27,
- newLup: MAX_PRICE
- }
- );
+ _addCollateral({
+ from: _lender,
+ amount: 10 * 1e18,
+ index: i100,
+ lpAward: 1003.3236814328200989 * 1e18
+ });
+
+ // another lender deposits into the bucket
+ _addLiquidity({
+ from: _lender1,
+ amount: 900 * 1e18,
+ index: i100,
+ lpAward: 900 * 1e18,
+ newLup: MAX_PRICE
+ });
// should be able to remove a small amount of deposit
skip(1 days);
- _removeLiquidity(
- {
- from: _lender,
- amount: 100 * 1e18,
- index: i100,
- newLup: MAX_PRICE,
- lpRedeem: 100 * 1e27
- }
- );
+
+ _removeLiquidity({
+ from: _lender,
+ amount: 100 * 1e18,
+ index: i100,
+ newLup: MAX_PRICE,
+ lpRedeem: 100 * 1e18
+ });
// should be able to remove the rest
- _removeAllLiquidity(
- {
- from: _lender,
- amount: 800 * 1e18,
- index: i100,
- newLup: MAX_PRICE,
- lpRedeem: 800 * 1e27
- }
- );
+ _removeAllLiquidity({
+ from: _lender,
+ amount: 800 * 1e18,
+ index: i100,
+ newLup: MAX_PRICE,
+ lpRedeem: 800 * 1e18
+ });
- _assertBucket(
- {
- index: i100,
- lpBalance: 1_003.3236814328200989 * 1e27,
- collateral: 10 * 1e18,
- deposit: 0,
- exchangeRate: 1 * 1e27
- }
- );
+ _assertBucket({
+ index: i100,
+ lpBalance: 1_003.3236814328200989 * 1e18,
+ collateral: 10 * 1e18,
+ deposit: 0,
+ exchangeRate: 1 * 1e18
+ });
}
function testPoolRemoveQuoteTokenWithDebt() external tearDown {
@@ -723,59 +650,47 @@ contract ERC20PoolQuoteTokenTest is ERC20HelperContract {
// lender adds initial quote token
skip(1 minutes); // prevent deposit from having a zero timestamp
- _addLiquidity(
- {
- from: _lender,
- amount: 3_400 * 1e18,
- index: 1606,
- lpAward: 3_400 * 1e27,
- newLup: MAX_PRICE
- }
- );
- _addLiquidity(
- {
- from: _lender,
- amount: 3_400 * 1e18,
- index: 1663,
- lpAward: 3_400 * 1e27,
- newLup: MAX_PRICE
- }
- );
+ _addLiquidity({
+ from: _lender,
+ amount: 3_400 * 1e18,
+ index: 1606,
+ lpAward: 3_400 * 1e18,
+ newLup: MAX_PRICE
+ });
+ _addLiquidity({
+ from: _lender,
+ amount: 3_400 * 1e18,
+ index: 1663,
+ lpAward: 3_400 * 1e18,
+ newLup: MAX_PRICE
+ });
- _assertBucket(
- {
- index: 1606,
- lpBalance: 3_400 * 1e27,
- collateral: 0,
- deposit: 3_400 * 1e18,
- exchangeRate: 1 * 1e27
- }
- );
- _assertLenderLpBalance(
- {
- lender: _lender,
- index: 1606,
- lpBalance: 3_400 * 1e27,
- depositTime: _startTime + 1 minutes
- }
- );
- _assertBucket(
- {
- index: 1663,
- lpBalance: 3_400 * 1e27,
- collateral: 0,
- deposit: 3_400 * 1e18,
- exchangeRate: 1 * 1e27
- }
- );
- _assertLenderLpBalance(
- {
- lender: _lender,
- index: 1663,
- lpBalance: 3_400 * 1e27,
- depositTime: _startTime + 1 minutes
- }
- );
+ _assertBucket({
+ index: 1606,
+ lpBalance: 3_400 * 1e18,
+ collateral: 0,
+ deposit: 3_400 * 1e18,
+ exchangeRate: 1 * 1e18
+ });
+ _assertLenderLpBalance({
+ lender: _lender,
+ index: 1606,
+ lpBalance: 3_400 * 1e18,
+ depositTime: _startTime + 1 minutes
+ });
+ _assertBucket({
+ index: 1663,
+ lpBalance: 3_400 * 1e18,
+ collateral: 0,
+ deposit: 3_400 * 1e18,
+ exchangeRate: 1 * 1e18
+ });
+ _assertLenderLpBalance({
+ lender: _lender,
+ index: 1663,
+ lpBalance: 3_400 * 1e18,
+ depositTime: _startTime + 1 minutes
+ });
skip(59 minutes);
@@ -783,264 +698,218 @@ contract ERC20PoolQuoteTokenTest is ERC20HelperContract {
// borrower takes a loan of 3000 quote token
_drawDebt({
- from: _borrower,
- borrower: _borrower,
- amountToBorrow: 3_000 * 1e18,
- limitIndex: 2_000,
+ from: _borrower,
+ borrower: _borrower,
+ amountToBorrow: 3_000 * 1e18,
+ limitIndex: 2_000,
collateralToPledge: 100 * 1e18,
- newLup: 333_777.824045947762079231 * 1e18
+ newLup: 333_777.824045947762079231 * 1e18
});
skip(2 hours);
- _assertLenderLpBalance(
- {
- lender: _lender,
- index: 1663,
- lpBalance: 3_400 * 1e27,
- depositTime: _startTime + 1 minutes
- }
- );
- _assertLenderLpBalance(
- {
- lender: _lender,
- index: 1663,
- lpBalance: 3_400 * 1e27,
- depositTime: _startTime + 1 minutes
- }
- );
+ _assertLenderLpBalance({
+ lender: _lender,
+ index: 1663,
+ lpBalance: 3_400 * 1e18,
+ depositTime: _startTime + 1 minutes
+ });
+ _assertLenderLpBalance({
+ lender: _lender,
+ index: 1663,
+ lpBalance: 3_400 * 1e18,
+ depositTime: _startTime + 1 minutes
+ });
// lender makes a partial withdrawal, paying an early withdrawal penalty - current annualized interest rate divided by 52 (one week of interest)
(uint256 interestRate, ) = _pool.interestRateInfo();
uint256 penalty = Maths.WAD - Maths.wdiv(interestRate, 52 * 10**18);
assertLt(penalty, Maths.WAD);
+
uint256 expectedWithdrawal1 = Maths.wmul(1_700 * 1e18, penalty);
- _removeLiquidityWithPenalty(
- {
- from: _lender,
- amount: 1_700 * 1e18,
- amountRemoved: expectedWithdrawal1,
- index: 1606,
- newLup: _priceAt(1663),
- lpRedeem: 1_699.988795593461528952000000000 * 1e27
- }
- );
+
+ _removeLiquidityWithPenalty({
+ from: _lender,
+ amount: 1_700 * 1e18,
+ amountRemoved: expectedWithdrawal1,
+ index: 1606,
+ newLup: _priceAt(1663),
+ lpRedeem: 1_699.988795593461528952000000000 * 1e18
+ });
// lender removes all quote token, including interest, from the bucket
skip(1 days);
+
assertGt(_priceAt(1606), _htp());
+
uint256 expectedWithdrawal2 = 1_700.144243451229452671 * 1e18;
- _removeAllLiquidity(
- {
- from: _lender,
- amount: expectedWithdrawal2,
- index: 1606,
- newLup: _priceAt(1663),
- lpRedeem: 1_700.011204406538471048000000000 * 1e27
- }
- );
+
+ _removeAllLiquidity({
+ from: _lender,
+ amount: expectedWithdrawal2,
+ index: 1606,
+ newLup: _priceAt(1663),
+ lpRedeem: 1_700.011204406538471048000000000 * 1e18
+ });
+
assertEq(_quote.balanceOf(_lender), lenderBalanceBefore + expectedWithdrawal1 + expectedWithdrawal2);
- _assertBucket(
- {
- index: 1606,
- lpBalance: 0,
- collateral: 0,
- deposit: 0,
- exchangeRate: 1 * 1e27
- }
- );
- _assertLenderLpBalance(
- {
- lender: _lender,
- index: 1606,
- lpBalance: 0,
- depositTime: _startTime + 1 minutes
- }
- );
- _assertBucket(
- {
- index: 1663,
- lpBalance: 3_400 * 1e27,
- collateral: 0,
- deposit: 3_400.266076335718765800 * 1e18,
- exchangeRate: 1.000078257745799637000000000 * 1e27
- }
- );
- _assertLenderLpBalance(
- {
- lender: _lender,
- index: 1663,
- lpBalance: 3_400 * 1e27,
- depositTime: _startTime + 1 minutes
- }
- );
+ _assertBucket({
+ index: 1606,
+ lpBalance: 0,
+ collateral: 0,
+ deposit: 0,
+ exchangeRate: 1 * 1e18
+ });
+ _assertLenderLpBalance({
+ lender: _lender,
+ index: 1606,
+ lpBalance: 0,
+ depositTime: _startTime + 1 minutes
+ });
+ _assertBucket({
+ index: 1663,
+ lpBalance: 3_400 * 1e18,
+ collateral: 0,
+ deposit: 3_400.266076335718765800 * 1e18,
+ exchangeRate: 1.000078257745799637000000000 * 1e18
+ });
+ _assertLenderLpBalance({
+ lender: _lender,
+ index: 1663,
+ lpBalance: 3_400 * 1e18,
+ depositTime: _startTime + 1 minutes
+ });
}
function testPoolMoveQuoteToken() external tearDown {
- _addLiquidity(
- {
- from: _lender,
- amount: 40_000 * 1e18,
- index: 2549,
- lpAward: 40_000 * 1e27,
- newLup: MAX_PRICE
- }
- );
- _addLiquidity(
- {
- from: _lender,
- amount: 10_000 * 1e18,
- index: 2550,
- lpAward: 10_000 * 1e27,
- newLup: MAX_PRICE
- }
- );
- _addLiquidity(
- {
- from: _lender,
- amount: 20_000 * 1e18,
- index: 2551,
- lpAward: 20_000 * 1e27,
- newLup: MAX_PRICE
- }
- );
+ _addLiquidity({
+ from: _lender,
+ amount: 40_000 * 1e18,
+ index: 2549,
+ lpAward: 40_000 * 1e18,
+ newLup: MAX_PRICE
+ });
+ _addLiquidity({
+ from: _lender,
+ amount: 10_000 * 1e18,
+ index: 2550,
+ lpAward: 10_000 * 1e18,
+ newLup: MAX_PRICE
+ });
+ _addLiquidity( {
+ from: _lender,
+ amount: 20_000 * 1e18,
+ index: 2551,
+ lpAward: 20_000 * 1e18,
+ newLup: MAX_PRICE
+ });
- _assertLenderLpBalance(
- {
- lender: _lender,
- index: 2549,
- lpBalance: 40_000 * 1e27,
- depositTime: _startTime
- }
- );
- _assertLenderLpBalance(
- {
- lender: _lender,
- index: 2552,
- lpBalance: 0,
- depositTime: 0
- }
- );
+ _assertLenderLpBalance({
+ lender: _lender,
+ index: 2549,
+ lpBalance: 40_000 * 1e18,
+ depositTime: _startTime
+ });
+ _assertLenderLpBalance({
+ lender: _lender,
+ index: 2552,
+ lpBalance: 0,
+ depositTime: 0
+ });
- _moveLiquidity(
- {
- from: _lender,
- amount: 5_000 * 1e18,
- fromIndex: 2549,
- toIndex: 2552,
- lpRedeemFrom: 5_000 * 1e27,
- lpAwardTo: 5_000 * 1e27,
- newLup: MAX_PRICE
- }
- );
+ _moveLiquidity({
+ from: _lender,
+ amount: 5_000 * 1e18,
+ fromIndex: 2549,
+ toIndex: 2552,
+ lpRedeemFrom: 5_000 * 1e18,
+ lpAwardTo: 5_000 * 1e18,
+ newLup: MAX_PRICE
+ });
- _assertLenderLpBalance(
- {
- lender: _lender,
- index: 2549,
- lpBalance: 35_000 * 1e27,
- depositTime: _startTime
- }
- );
- _assertLenderLpBalance(
- {
- lender: _lender,
- index: 2552,
- lpBalance: 5_000 * 1e27,
- depositTime: _startTime
- }
- );
+ _assertLenderLpBalance({
+ lender: _lender,
+ index: 2549,
+ lpBalance: 35_000 * 1e18,
+ depositTime: _startTime
+ });
+ _assertLenderLpBalance({
+ lender: _lender,
+ index: 2552,
+ lpBalance: 5_000 * 1e18,
+ depositTime: _startTime
+ });
- _moveLiquidity(
- {
- from: _lender,
- amount: 5_000 * 1e18,
- fromIndex: 2549,
- toIndex: 2540,
- lpRedeemFrom: 5_000 * 1e27,
- lpAwardTo: 5_000 * 1e27,
- newLup: MAX_PRICE
- }
- );
+ _moveLiquidity({
+ from: _lender,
+ amount: 5_000 * 1e18,
+ fromIndex: 2549,
+ toIndex: 2540,
+ lpRedeemFrom: 5_000 * 1e18,
+ lpAwardTo: 5_000 * 1e18,
+ newLup: MAX_PRICE
+ });
- _assertLenderLpBalance(
- {
- lender: _lender,
- index: 2540,
- lpBalance: 5_000 * 1e27,
- depositTime: _startTime
- }
- );
- _assertLenderLpBalance(
- {
- lender: _lender,
- index: 2549,
- lpBalance: 30_000 * 1e27,
- depositTime: _startTime
- }
- );
- _assertLenderLpBalance(
- {
- lender: _lender,
- index: 2552,
- lpBalance: 5_000 * 1e27,
- depositTime: _startTime
- }
- );
+ _assertLenderLpBalance({
+ lender: _lender,
+ index: 2540,
+ lpBalance: 5_000 * 1e18,
+ depositTime: _startTime
+ });
+ _assertLenderLpBalance({
+ lender: _lender,
+ index: 2549,
+ lpBalance: 30_000 * 1e18,
+ depositTime: _startTime
+ });
+ _assertLenderLpBalance({
+ lender: _lender,
+ index: 2552,
+ lpBalance: 5_000 * 1e18,
+ depositTime: _startTime
+ });
- _moveLiquidity(
- {
- from: _lender,
- amount: 15_000 * 1e18,
- fromIndex: 2551,
- toIndex: 2777,
- lpRedeemFrom: 15_000 * 1e27,
- lpAwardTo: 15_000 * 1e27,
- newLup: MAX_PRICE
- }
- );
+ _moveLiquidity({
+ from: _lender,
+ amount: 15_000 * 1e18,
+ fromIndex: 2551,
+ toIndex: 2777,
+ lpRedeemFrom: 15_000 * 1e18,
+ lpAwardTo: 15_000 * 1e18,
+ newLup: MAX_PRICE
+ });
- _assertLenderLpBalance(
- {
- lender: _lender,
- index: 2540,
- lpBalance: 5_000 * 1e27,
- depositTime: _startTime
- }
- );
- _assertLenderLpBalance(
- {
- lender: _lender,
- index: 2549,
- lpBalance: 30_000 * 1e27,
- depositTime: _startTime
- }
- );
- _assertLenderLpBalance(
- {
- lender: _lender,
- index: 2551,
- lpBalance: 5_000 * 1e27,
- depositTime: _startTime
- }
- );
- _assertLenderLpBalance(
- {
- lender: _lender,
- index: 2552,
- lpBalance: 5_000 * 1e27,
- depositTime: _startTime
- }
- );
- _assertLenderLpBalance(
- {
- lender: _lender,
- index: 2777,
- lpBalance: 15_000 * 1e27,
- depositTime: _startTime
- }
- );
+ _assertLenderLpBalance({
+ lender: _lender,
+ index: 2540,
+ lpBalance: 5_000 * 1e18,
+ depositTime: _startTime
+ });
+ _assertLenderLpBalance({
+ lender: _lender,
+ index: 2549,
+ lpBalance: 30_000 * 1e18,
+ depositTime: _startTime
+ });
+ _assertLenderLpBalance({
+ lender: _lender,
+ index: 2551,
+ lpBalance: 5_000 * 1e18,
+ depositTime: _startTime
+ });
+ _assertLenderLpBalance({
+ lender: _lender,
+ index: 2552,
+ lpBalance: 5_000 * 1e18,
+ depositTime: _startTime
+ });
+ _assertLenderLpBalance({
+ lender: _lender,
+ index: 2777,
+ lpBalance: 15_000 * 1e18,
+ depositTime: _startTime
+ });
}
/**
@@ -1056,134 +925,118 @@ contract ERC20PoolQuoteTokenTest is ERC20HelperContract {
_mintCollateralAndApproveTokens(_borrower, _collateral.balanceOf(_lender1) + 1_500_000 * 1e18);
// lender adds initial quote token
- _addLiquidity(
- {
- from: _lender,
- amount: 40_000 * 1e18,
- index: 4549,
- lpAward: 40_000 * 1e27,
- newLup: MAX_PRICE
- }
- );
- _addLiquidity(
- {
- from: _lender,
- amount: 10_000 * 1e18,
- index: 4550,
- lpAward: 10_000 * 1e27,
- newLup: MAX_PRICE
- }
- );
- _addLiquidity(
- {
- from: _lender,
- amount: 20_000 * 1e18,
- index: 4551,
- lpAward: 20_000 * 1e27,
- newLup: MAX_PRICE
- }
- );
- _addLiquidity(
- {
- from: _lender,
- amount: 30_000 * 1e18,
- index: 4651,
- lpAward: 30_000 * 1e27,
- newLup: MAX_PRICE
- }
- );
+ _addLiquidity({
+ from: _lender,
+ amount: 40_000 * 1e18,
+ index: 4549,
+ lpAward: 40_000 * 1e18,
+ newLup: MAX_PRICE
+ });
+ _addLiquidity({
+ from: _lender,
+ amount: 10_000 * 1e18,
+ index: 4550,
+ lpAward: 10_000 * 1e18,
+ newLup: MAX_PRICE
+ });
+ _addLiquidity({
+ from: _lender,
+ amount: 20_000 * 1e18,
+ index: 4551,
+ lpAward: 20_000 * 1e18,
+ newLup: MAX_PRICE
+ });
+ _addLiquidity({
+ from: _lender,
+ amount: 30_000 * 1e18,
+ index: 4651,
+ lpAward: 30_000 * 1e18,
+ newLup: MAX_PRICE
+ });
// should revert if moving quote token to the existing price
- _assertMoveLiquidityToSamePriceRevert(
- {
- from: _lender,
- amount: 5_000 * 1e18,
- fromIndex: 4549,
- toIndex: 4549
- }
- );
+ _assertMoveLiquidityToSamePriceRevert({
+ from: _lender,
+ amount: 5_000 * 1e18,
+ fromIndex: 4549,
+ toIndex: 4549
+ });
// should revert if moving quote token to index 0
- _assertMoveLiquidityToIndex0Revert(
- {
- from: _lender,
- amount: 5_000 * 1e18,
- fromIndex: 4549
- }
- );
+ _assertMoveLiquidityToIndex0Revert({
+ from: _lender,
+ amount: 5_000 * 1e18,
+ fromIndex: 4549
+ });
// borrow all available quote in the higher priced original 3 buckets, as well as some of the new lowest price bucket
- _pledgeCollateral(
- {
- from: _borrower,
- borrower: _borrower,
- amount: 1_500_000 * 1e18
- }
- );
- _borrow(
- {
- from: _borrower,
- amount: 60_000.1 * 1e18,
- indexLimit: 4_651,
- newLup: 0.139445853940958153 * 1e18
- }
- );
+ _pledgeCollateral({
+ from: _borrower,
+ borrower: _borrower,
+ amount: 1_500_000 * 1e18
+ });
+ _borrow({
+ from: _borrower,
+ amount: 60_000.1 * 1e18,
+ indexLimit: 4_651,
+ newLup: 0.139445853940958153 * 1e18
+ });
// should revert if movement would drive lup below htp
- _assertMoveLiquidityLupBelowHtpRevert(
- {
- from: _lender,
- amount: 40_000 * 1e18,
- fromIndex: 4549,
- toIndex: 6000
- }
- );
+ _assertMoveLiquidityLupBelowHtpRevert({
+ from: _lender,
+ amount: 40_000 * 1e18,
+ fromIndex: 4549,
+ toIndex: 6000
+ });
+
+ // should revert if transaction expired
+ _assertMoveLiquidityExpiredRevert({
+ from: _lender,
+ amount: 30_000 * 1e18,
+ fromIndex: 4549,
+ toIndex: 4459,
+ expiry: block.timestamp - 20
+ });
// should be able to moveQuoteToken if properly specified
- _moveLiquidity(
- {
- from: _lender,
- amount: 10_000 * 1e18,
- fromIndex: 4549,
- toIndex: 4550,
- lpRedeemFrom: 10_000 * 1e27,
- lpAwardTo: 10_000 * 1e27,
- newLup: _priceAt(4551)
- }
- );
+ _moveLiquidity({
+ from: _lender,
+ amount: 10_000 * 1e18,
+ fromIndex: 4549,
+ toIndex: 4550,
+ lpRedeemFrom: 10_000 * 1e18,
+ lpAwardTo: 10_000 * 1e18,
+ newLup: _priceAt(4551)
+ });
}
function testMoveQuoteTokenWithDebt() external tearDown {
// lender makes an initial deposit
skip(1 hours);
- _addLiquidity(
- {
- from: _lender,
- amount: 10_000 * 1e18,
- index: 2873,
- lpAward: 10_000 * 1e27,
- newLup: MAX_PRICE
- }
- );
+ _addLiquidity({
+ from: _lender,
+ amount: 10_000 * 1e18,
+ index: 2873,
+ lpAward: 10_000 * 1e18,
+ newLup: MAX_PRICE
+ });
// borrower draws debt, establishing a pool threshold price
skip(2 hours);
- _pledgeCollateral(
- {
- from: _borrower,
- borrower: _borrower,
- amount: 10 * 1e18
- }
- );
- _borrow(
- {
- from: _borrower,
- amount: 5_000 * 1e18,
- indexLimit: 3_000,
- newLup: 601.252968524772188572 * 1e18
- }
- );
+
+ _pledgeCollateral({
+ from: _borrower,
+ borrower: _borrower,
+ amount: 10 * 1e18
+ });
+ _borrow({
+ from: _borrower,
+ amount: 5_000 * 1e18,
+ indexLimit: 3_000,
+ newLup: 601.252968524772188572 * 1e18
+ });
(uint256 poolDebt,,) = _pool.debtInfo();
uint256 ptp = Maths.wdiv(poolDebt, 10 * 1e18);
@@ -1191,79 +1044,165 @@ contract ERC20PoolQuoteTokenTest is ERC20HelperContract {
// lender moves some liquidity below the pool threshold price; penalty should be assessed
skip(16 hours);
- _moveLiquidityWithPenalty(
- {
- from: _lender,
- amount: 2_500 * 1e18,
- amountMoved: 2_497.596153846153845 * 1e18,
- fromIndex: 2873,
- toIndex: 2954,
- lpRedeemFrom: 2_499.899333909953254268000000000 * 1e27,
- lpAwardTo: 2_497.596153846153845 * 1e27,
- newLup: _lup()
- }
- );
+
+ _moveLiquidityWithPenalty({
+ from: _lender,
+ amount: 2_500 * 1e18,
+ amountMoved: 2_497.596153846153845 * 1e18,
+ fromIndex: 2873,
+ toIndex: 2954,
+ lpRedeemFrom: 2_499.899333909953254268000000000 * 1e18,
+ lpAwardTo: 2_497.596153846153845 * 1e18,
+ newLup: _lup()
+ });
// another lender provides liquidity to prevent LUP from moving
skip(1 hours);
- _addLiquidity(
- {
- from: _lender1,
- amount: 1_000 * 1e18,
- index: 2873,
- lpAward: 999.956320611641422442838174928 * 1e27,
- newLup: 601.252968524772188572 * 1e18
- }
- );
+ _addLiquidity({
+ from: _lender1,
+ amount: 1_000 * 1e18,
+ index: 2873,
+ lpAward: 999.956320611641422443 * 1e18,
+ newLup: 601.252968524772188572 * 1e18
+ });
// lender moves more liquidity; no penalty assessed as sufficient time has passed
skip(12 hours);
- _moveLiquidity(
- {
- from: _lender,
- amount: 2_500 * 1e18,
- fromIndex: 2873,
- toIndex: 2954,
- lpRedeemFrom: 2_499.810182702901761330952408614 * 1e27,
- lpAwardTo: 2_500 * 1e27,
- newLup: _lup()
- }
- );
+
+ _moveLiquidity({
+ from: _lender,
+ amount: 2_500 * 1e18,
+ fromIndex: 2873,
+ toIndex: 2954,
+ lpRedeemFrom: 2_499.810182702901761331 * 1e18,
+ lpAwardTo: 2_500 * 1e18,
+ newLup: _lup()
+ });
// after a week, another lender funds the pool
skip(7 days);
- _addLiquidity(
- {
- from: _lender1,
- amount: 9_000 * 1e18,
- index: 2873,
- lpAward: 8_993.373316759001213153971860794 * 1e27,
- newLup: 601.252968524772188572 * 1e18
- }
- );
+ _addLiquidity({
+ from: _lender1,
+ amount: 9_000 * 1e18,
+ index: 2873,
+ lpAward: 8_993.373316759001213155 * 1e18,
+ newLup: 601.252968524772188572 * 1e18
+ });
// lender removes all their quote, with interest
skip(1 hours);
- _removeAllLiquidity(
- {
- from: _lender,
- amount: 5_003.981613396490344248 * 1e18,
- index: 2873,
- newLup: 601.252968524772188572 * 1e18,
- lpRedeem: 5_000.290483387144984401047591386 * 1e27
- }
- );
- _removeAllLiquidity(
- {
- from: _lender,
- amount: 4_997.596153846153845 * 1e18,
- index: 2954,
- newLup: 601.252968524772188572 * 1e18,
- lpRedeem: 4_997.596153846153845 * 1e27
- }
- );
+
+ _removeAllLiquidity({
+ from: _lender,
+ amount: 5_003.981613396490344248 * 1e18,
+ index: 2873,
+ newLup: 601.252968524772188572 * 1e18,
+ lpRedeem: 5_000.290483387144984401 * 1e18
+ });
+
+ _removeAllLiquidity({
+ from: _lender,
+ amount: 4_997.596153846153845 * 1e18,
+ index: 2954,
+ newLup: 601.252968524772188572 * 1e18,
+ lpRedeem: 4_997.596153846153845 * 1e18
+ });
+
assertGt(_quote.balanceOf(_lender), 200_000 * 1e18);
}
+
+ function testAddRemoveQuoteTokenBucketExchangeRateInvariantDifferentActor() tearDown external {
+ _mintQuoteAndApproveTokens(_lender, 1000000000000000000 * 1e18);
+
+ uint256 initialLenderBalance = _quote.balanceOf(_lender);
+
+ _addCollateral({
+ from: _borrower,
+ amount: 13167,
+ index: 2570,
+ lpAward: 35880690
+ });
+
+ _assertLenderLpBalance({
+ lender: _borrower,
+ index: 2570,
+ lpBalance: 35880690,
+ depositTime: _startTime
+ });
+ _assertLenderLpBalance({
+ lender: _lender,
+ index: 2570,
+ lpBalance: 0,
+ depositTime: 0
+ });
+ _assertBucket({
+ index: 2570,
+ lpBalance: 35880690,
+ collateral: 13167,
+ deposit: 0,
+ exchangeRate: 1 * 1e18
+ });
+
+ _addLiquidity({
+ from: _lender,
+ amount: 984665640564039457.584007913129639933 * 1e18,
+ index: 2570,
+ lpAward: 984665640564039457.584007913129639933 * 1e18,
+ newLup: MAX_PRICE
+ });
+
+ _assertLenderLpBalance({
+ lender: _borrower,
+ index: 2570,
+ lpBalance: 35880690,
+ depositTime: _startTime
+ });
+ _assertLenderLpBalance({
+ lender: _lender,
+ index: 2570,
+ lpBalance: 984665640564039457.584007913129639933 * 1e18,
+ depositTime: _startTime
+ });
+ _assertBucket({
+ index: 2570,
+ lpBalance: 984665640564039457.584007913165520623 * 1e18,
+ collateral: 13167,
+ deposit: 984665640564039457.584007913129639933 * 1e18,
+ exchangeRate: 1 * 1e18 // exchange rate should not change
+ });
+
+ skip(48 hours); // to avoid penalty
+
+ _removeAllLiquidity({
+ from: _lender,
+ amount: 984665640564039457.584007913129639933 * 1e18,
+ index: 2570,
+ newLup: MAX_PRICE,
+ lpRedeem: 984665640564039457.584007913129639933 * 1e18
+ });
+
+ _assertLenderLpBalance({
+ lender: _borrower,
+ index: 2570,
+ lpBalance: 35880690,
+ depositTime: _startTime
+ });
+ _assertLenderLpBalance({
+ lender: _lender,
+ index: 2570,
+ lpBalance: 0, // LPs should get back to same value as before add / remove collateral
+ depositTime: _startTime
+ });
+ _assertBucket({
+ index: 2570,
+ lpBalance: 35880690,
+ collateral: 13167,
+ deposit: 0,
+ exchangeRate: 1 * 1e18 // exchange rate should not change
+ });
+
+ assertEq(_quote.balanceOf(_lender), initialLenderBalance);
+ }
}
diff --git a/tests/forge/ERC20Pool/ERC20PoolReserveAuction.t.sol b/tests/forge/ERC20Pool/ERC20PoolReserveAuction.t.sol
new file mode 100644
index 000000000..b0b7fcd63
--- /dev/null
+++ b/tests/forge/ERC20Pool/ERC20PoolReserveAuction.t.sol
@@ -0,0 +1,115 @@
+// SPDX-License-Identifier: UNLICENSED
+pragma solidity 0.8.14;
+
+import '@openzeppelin/contracts/token/ERC20/ERC20.sol';
+
+import { ERC20HelperContract } from './ERC20DSTestPlus.sol';
+import { FlashloanBorrower, SomeDefiStrategy } from '../utils/FlashloanBorrower.sol';
+
+import 'src/libraries/helpers/PoolHelper.sol';
+import 'src/ERC20Pool.sol';
+import 'src/ERC20PoolFactory.sol';
+
+import { IPoolErrors } from 'src/interfaces/pool/IPool.sol';
+
+contract ERC20PoolReserveAuctionTest is ERC20HelperContract {
+
+ ERC20 WBTC = ERC20(0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599);
+ ERC20 USDC = ERC20(0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48);
+ ERC20 AJNA = ERC20(_ajna);
+
+ address internal _borrower;
+ address internal _lender;
+ address internal _bidder;
+
+ function setUp() external {
+ _pool = ERC20Pool(new ERC20PoolFactory(address(AJNA)).deployPool(address(WBTC), address(USDC), 0.05 * 10**18));
+
+ _borrower = makeAddr("borrower");
+ _lender = makeAddr("lender");
+ _bidder = makeAddr("bidder");
+
+ deal(address(WBTC), _borrower, 10 * 1e8);
+ deal(address(USDC), _borrower, 100 * 1e6);
+
+ deal(address(USDC), _lender, 10_000 * 1e6);
+
+ deal(address(AJNA), _bidder, 10 * 1e18);
+
+ vm.startPrank(_borrower);
+ WBTC.approve(address(_pool), 10 * 1e18);
+ USDC.approve(address(_pool), 1_000 * 1e18);
+
+ changePrank(_bidder);
+ AJNA.approve(address(_pool), 10 * 1e18);
+
+ changePrank(_lender);
+ USDC.approve(address(_pool), 1_000 * 1e18);
+
+ _addInitialLiquidity({
+ from: _lender,
+ amount: 1_000 * 1e18,
+ index: 2500
+ });
+
+ _drawDebtNoLupCheck({
+ from: _borrower,
+ borrower: _borrower,
+ amountToBorrow: 300 * 1e18,
+ limitIndex: 7000,
+ collateralToPledge: 1 * 1e18
+ });
+ }
+
+ function testStartAndTakeUsdcReserveAuction() external {
+ // skip time to accumulate interest
+ skip(26 weeks);
+
+ // repay entire debt
+ _repayDebtNoLupCheck({
+ from: _borrower,
+ borrower: _borrower,
+ amountToRepay: 400 * 1e18,
+ amountRepaid: 307.869212479869665749 * 1e18,
+ collateralToPull: 0
+ });
+
+ assertEq(USDC.balanceOf(address(_borrower)), 92.130788 * 1e6);
+ assertEq(USDC.balanceOf(address(_pool)), 1_007.869212 * 1e6);
+
+ _assertReserveAuction({
+ reserves: 1.297969216344413 * 1e18,
+ claimableReserves : 1.297969216344413 * 1e18,
+ claimableReservesRemaining: 0,
+ auctionPrice: 0,
+ timeRemaining: 0
+ });
+
+ // kick off a new auction
+ _startClaimableReserveAuction({
+ from: _bidder,
+ remainingReserves: 1.284989524180968870 * 1e18,
+ price: 1000000000 * 1e18
+ });
+
+ skip(60 hours);
+
+ _assertReserveAuction({
+ reserves: 0.000000692163444130 * 1e18,
+ claimableReserves : 0.000000692163444130 * 1e18,
+ claimableReservesRemaining: 1.284989524180968870 * 1e18,
+ auctionPrice: 0.000000000867361737 * 1e18,
+ timeRemaining: 43200
+ });
+
+ assertEq(USDC.balanceOf(address(_pool)), 1_007.856233 * 1e6);
+ assertEq(USDC.balanceOf(address(_bidder)), 0.012979 * 1e6); // kicker reward
+ assertEq(AJNA.balanceOf(address(_bidder)), 10 * 1e18);
+
+ _pool.takeReserves(10 * 1e18);
+
+ assertEq(USDC.balanceOf(address(_pool)), 1_006.571244 * 1e6);
+ assertEq(USDC.balanceOf(address(_bidder)), 1.297968 * 1e6);
+ assertEq(AJNA.balanceOf(address(_bidder)), 9.999999998885449254 * 1e18);
+ }
+}
\ No newline at end of file
diff --git a/tests/forge/ERC20Pool/ERC20PoolTransferLPTokens.t.sol b/tests/forge/ERC20Pool/ERC20PoolTransferLPTokens.t.sol
deleted file mode 100644
index 89dca6f78..000000000
--- a/tests/forge/ERC20Pool/ERC20PoolTransferLPTokens.t.sol
+++ /dev/null
@@ -1,632 +0,0 @@
-// SPDX-License-Identifier: UNLICENSED
-pragma solidity 0.8.14;
-
-import { ERC20HelperContract } from './ERC20DSTestPlus.sol';
-
-import 'src/libraries/helpers/PoolHelper.sol';
-
-contract ERC20PoolTransferLPTokensTest is ERC20HelperContract {
-
- address internal _lender;
- address internal _lender1;
- address internal _lender2;
-
- function setUp() external {
- _lender = makeAddr("lender");
- _lender1 = makeAddr("lender1");
- _lender2 = makeAddr("lender2");
-
- _mintQuoteAndApproveTokens(_lender, 200_000 * 1e18);
- _mintQuoteAndApproveTokens(_lender1, 200_000 * 1e18);
- _mintQuoteAndApproveTokens(_lender2, 200_000 * 1e18);
- }
-
- /********************************/
- /*** Transfer LP Tokens Tests ***/
- /********************************/
-
- function testTransferLPTokensToZeroAddress() external tearDown {
- uint256[] memory indexes = new uint256[](3);
- indexes[0] = 2550;
- indexes[1] = 2551;
- indexes[2] = 2552;
-
- // should fail if allowed owner is not set
- _assertTransferNoAllowanceRevert(
- {
- operator: _lender,
- from: _lender1,
- to: _lender2,
- indexes: indexes
- }
- );
-
- // should fail if allowed owner is set to 0x
- changePrank(_lender1);
- _pool.approveLpOwnership(address(0), indexes[0], 1_000 * 1e18);
-
- _assertTransferNoAllowanceRevert(
- {
- operator: _lender,
- from: _lender1,
- to: _lender2,
- indexes: indexes
- }
- );
- }
-
- function testTransferLPTokensToUnallowedAddress() external tearDown {
- uint256[] memory indexes = new uint256[](3);
- indexes[0] = 2550;
- indexes[1] = 2551;
- indexes[2] = 2552;
-
- // should fail if allowed owner is set to lender2 address but trying to transfer to lender address
- changePrank(_lender1);
- _pool.approveLpOwnership(_lender2, indexes[0], 1_000 * 1e27);
- _pool.approveLpOwnership(_lender2, indexes[1], 1_000 * 1e27);
- _pool.approveLpOwnership(_lender2, indexes[2], 1_000 * 1e27);
-
- _assertTransferNoAllowanceRevert(
- {
- operator: _lender,
- from: _lender1,
- to: _lender2,
- indexes: indexes
- }
- );
- }
-
- function testTransferLPTokensToInvalidIndex() external tearDown {
- uint256[] memory indexes = new uint256[](3);
- indexes[0] = 9999;
- indexes[1] = 2550;
- indexes[2] = 2552;
-
- // should fail since 9999 is not a valid index
- changePrank(_lender1);
- _pool.approveLpOwnership(_lender2, indexes[0], 1_000 * 1e27);
- _pool.approveLpOwnership(_lender2, indexes[1], 1_000 * 1e27);
- _pool.approveLpOwnership(_lender2, indexes[2], 1_000 * 1e27);
-
- _assertTransferInvalidIndexRevert(
- {
- operator: _lender,
- from: _lender1,
- to: _lender2,
- indexes: indexes
- }
- );
- }
-
- function testTransferLPTokensGreaterThanBalance() external tearDown {
- uint256[] memory indexes = new uint256[](2);
- indexes[0] = 2550;
- indexes[1] = 2551;
-
- _addInitialLiquidity(
- {
- from: _lender1,
- amount: 10_000 * 1e18,
- index: indexes[0]
- }
- );
- _addInitialLiquidity(
- {
- from: _lender1,
- amount: 20_000 * 1e18,
- index: indexes[1]
- }
- );
-
- // set allowed owner to lender2 address
- _pool.approveLpOwnership(_lender2, indexes[0], 10_000 * 1e27);
- _pool.approveLpOwnership(_lender2, indexes[1], 30_000 * 1e27);
-
- _assertTransferNoAllowanceRevert(
- {
- operator: _lender2,
- from: _lender1,
- to: _lender2,
- indexes: indexes
- }
- );
- }
-
- function testTransferLPTokensForAllIndexes() external tearDown {
- uint256[] memory indexes = new uint256[](3);
- indexes[0] = 2550;
- indexes[1] = 2551;
- indexes[2] = 2552;
-
- skip(1 hours);
-
- _addInitialLiquidity(
- {
- from: _lender1,
- amount: 10_000 * 1e18,
- index: indexes[0]
- }
- );
- _addInitialLiquidity(
- {
- from: _lender1,
- amount: 20_000 * 1e18,
- index: indexes[1]
- }
- );
- _addInitialLiquidity(
- {
- from: _lender1,
- amount: 30_000 * 1e18,
- index: indexes[2]
- }
- );
-
- // check lenders lp balance
- _assertLenderLpBalance(
- {
- lender: _lender1,
- index: indexes[0],
- lpBalance: 10_000 * 1e27,
- depositTime: _startTime + 1 hours
- }
- );
- _assertLenderLpBalance(
- {
- lender: _lender2,
- index: indexes[0],
- lpBalance: 0,
- depositTime: 0
- }
- );
- _assertLenderLpBalance(
- {
- lender: _lender1,
- index: indexes[1],
- lpBalance: 20_000 * 1e27,
- depositTime: _startTime + 1 hours
- }
- );
- _assertLenderLpBalance(
- {
- lender: _lender2,
- index: indexes[1],
- lpBalance: 0,
- depositTime: 0
- }
- );
- _assertLenderLpBalance(
- {
- lender: _lender1,
- index: indexes[2],
- lpBalance: 30_000 * 1e27,
- depositTime: _startTime + 1 hours
- }
- );
- _assertLenderLpBalance(
- {
- lender: _lender2,
- index: indexes[2],
- lpBalance: 0,
- depositTime: 0
- }
- );
-
- // set allowed owner to lender2 address
- _pool.approveLpOwnership(_lender2, indexes[0], 10_000 * 1e27);
- _pool.approveLpOwnership(_lender2, indexes[1], 20_000 * 1e27);
- _pool.approveLpOwnership(_lender2, indexes[2], 30_000 * 1e27);
-
- // transfer LP tokens for all indexes
- _transferLpTokens(
- {
- operator: _lender,
- from: _lender1,
- to: _lender2,
- indexes: indexes,
- lpBalance: 60_000 * 1e27
- }
- );
-
- // check that old token ownership was removed - a new transfer should fail
- _assertTransferNoAllowanceRevert(
- {
- operator: _lender,
- from: _lender1,
- to: _lender2,
- indexes: indexes
- }
- );
-
- // check lenders lp balance
- _assertLenderLpBalance(
- {
- lender: _lender1,
- index: indexes[0],
- lpBalance: 0,
- depositTime: 0
- }
- );
- _assertLenderLpBalance(
- {
- lender: _lender2,
- index: indexes[0],
- lpBalance: 10_000 * 1e27,
- depositTime: _startTime + 1 hours
- }
- );
- _assertLenderLpBalance(
- {
- lender: _lender1,
- index: indexes[1],
- lpBalance: 0,
- depositTime: 0
- }
- );
- _assertLenderLpBalance(
- {
- lender: _lender2,
- index: indexes[1],
- lpBalance: 20_000 * 1e27,
- depositTime: _startTime + 1 hours
- }
- );
- _assertLenderLpBalance(
- {
- lender: _lender1,
- index: indexes[2],
- lpBalance: 0,
- depositTime: 0
- }
- );
- _assertLenderLpBalance(
- {
- lender: _lender2,
- index: indexes[2],
- lpBalance: 30_000 * 1e27,
- depositTime: _startTime + 1 hours
- }
- );
- }
-
- function testTransferLPTokensForTwoIndexes() external tearDown {
- uint256[] memory depositIndexes = new uint256[](3);
- depositIndexes[0] = 2550;
- depositIndexes[1] = 2551;
- depositIndexes[2] = 2552;
-
- uint256[] memory transferIndexes = new uint256[](2);
- transferIndexes[0] = 2550;
- transferIndexes[1] = 2552;
-
- _addInitialLiquidity(
- {
- from: _lender1,
- amount: 10_000 * 1e18,
- index: depositIndexes[0]
- }
- );
- _addInitialLiquidity(
- {
- from: _lender1,
- amount: 20_000 * 1e18,
- index: depositIndexes[1]
- }
- );
- _addInitialLiquidity(
- {
- from: _lender1,
- amount: 30_000 * 1e18,
- index: depositIndexes[2]
- }
- );
-
- // check lenders lp balance
- _assertLenderLpBalance(
- {
- lender: _lender1,
- index: depositIndexes[0],
- lpBalance: 10_000 * 1e27,
- depositTime: _startTime
- }
- );
- _assertLenderLpBalance(
- {
- lender: _lender2,
- index: depositIndexes[0],
- lpBalance: 0,
- depositTime: 0
- }
- );
- _assertLenderLpBalance(
- {
- lender: _lender1,
- index: depositIndexes[1],
- lpBalance: 20_000 * 1e27,
- depositTime: _startTime
- }
- );
- _assertLenderLpBalance(
- {
- lender: _lender2,
- index: depositIndexes[1],
- lpBalance: 0,
- depositTime: 0
- }
- );
- _assertLenderLpBalance(
- {
- lender: _lender1,
- index: depositIndexes[2],
- lpBalance: 30_000 * 1e27,
- depositTime: _startTime
- }
- );
- _assertLenderLpBalance(
- {
- lender: _lender2,
- index: depositIndexes[2],
- lpBalance: 0,
- depositTime: 0
- }
- );
-
- // set allowed owner to lender2 address
- _pool.approveLpOwnership(_lender2, transferIndexes[0], 10_000 * 1e27);
- _pool.approveLpOwnership(_lender2, transferIndexes[1], 30_000 * 1e27);
-
- // transfer LP tokens for 2 indexes
- _transferLpTokens(
- {
- operator: _lender,
- from: _lender1,
- to: _lender2,
- indexes: transferIndexes,
- lpBalance: 40_000 * 1e27
- }
- );
-
- // check that old token ownership was removed - transfer with same indexes should fail
- _assertTransferNoAllowanceRevert(
- {
- operator: _lender,
- from: _lender1,
- to: _lender2,
- indexes: transferIndexes
- }
- );
-
- // check lenders lp balance
- _assertLenderLpBalance(
- {
- lender: _lender1,
- index: depositIndexes[0],
- lpBalance: 0,
- depositTime: 0
- }
- );
- _assertLenderLpBalance(
- {
- lender: _lender2,
- index: depositIndexes[0],
- lpBalance: 10_000 * 1e27,
- depositTime: _startTime
- }
- );
- _assertLenderLpBalance(
- {
- lender: _lender1,
- index: depositIndexes[1],
- lpBalance: 20_000 * 1e27,
- depositTime: _startTime
- }
- );
- _assertLenderLpBalance(
- {
- lender: _lender2,
- index: depositIndexes[1],
- lpBalance: 0,
- depositTime: 0
- }
- );
- _assertLenderLpBalance(
- {
- lender: _lender1,
- index: depositIndexes[2],
- lpBalance: 0,
- depositTime: 0
- }
- );
- _assertLenderLpBalance(
- {
- lender: _lender2,
- index: depositIndexes[2],
- lpBalance: 30_000 * 1e27,
- depositTime: _startTime
- }
- );
- }
-
- function testTransferLPTokensToLenderWithLPTokens() external tearDown {
- uint256[] memory indexes = new uint256[](3);
- indexes[0] = 2550;
- indexes[1] = 2551;
- indexes[2] = 2552;
-
- skip(1 hours);
-
- _addInitialLiquidity(
- {
- from: _lender1,
- amount: 10_000 * 1e18,
- index: indexes[0]
- }
- );
- _addInitialLiquidity(
- {
- from: _lender1,
- amount: 20_000 * 1e18,
- index: indexes[1]
- }
- );
- _addInitialLiquidity(
- {
- from: _lender1,
- amount: 30_000 * 1e18,
- index: indexes[2]
- }
- );
-
- skip(1 hours);
-
- _addInitialLiquidity(
- {
- from: _lender2,
- amount: 5_000 * 1e18,
- index: indexes[0]
- }
- );
- _addInitialLiquidity(
- {
- from: _lender2,
- amount: 10_000 * 1e18,
- index: indexes[1]
- }
- );
- _addInitialLiquidity(
- {
- from: _lender2,
- amount: 15_000 * 1e18,
- index: indexes[2]
- }
- );
-
- // check lenders lp balance
- _assertLenderLpBalance(
- {
- lender: _lender1,
- index: indexes[0],
- lpBalance: 10_000 * 1e27,
- depositTime: _startTime + 1 hours
- }
- );
- _assertLenderLpBalance(
- {
- lender: _lender2,
- index: indexes[0],
- lpBalance: 5_000 * 1e27,
- depositTime: _startTime + 2 hours
- }
- );
- _assertLenderLpBalance(
- {
- lender: _lender1,
- index: indexes[1],
- lpBalance: 20_000 * 1e27,
- depositTime: _startTime + 1 hours
- }
- );
- _assertLenderLpBalance(
- {
- lender: _lender2,
- index: indexes[1],
- lpBalance: 10_000 * 1e27,
- depositTime: _startTime + 2 hours
- }
- );
- _assertLenderLpBalance(
- {
- lender: _lender1,
- index: indexes[2],
- lpBalance: 30_000 * 1e27,
- depositTime: _startTime + 1 hours
- }
- );
- _assertLenderLpBalance(
- {
- lender: _lender2,
- index: indexes[2],
- lpBalance: 15_000 * 1e27,
- depositTime: _startTime + 2 hours
- }
- );
-
- // set allowed owner to lender2 address
- changePrank(_lender1);
- _pool.approveLpOwnership(_lender2, indexes[0], 10_000 * 1e27);
- _pool.approveLpOwnership(_lender2, indexes[1], 20_000 * 1e27);
- _pool.approveLpOwnership(_lender2, indexes[2], 30_000 * 1e27);
-
- // transfer LP tokens for all indexes
- _transferLpTokens(
- {
- operator: _lender,
- from: _lender1,
- to: _lender2,
- indexes: indexes,
- lpBalance: 60_000 * 1e27
- }
- );
-
- // check that old token ownership was removed - transfer with same indexes should fail
- _assertTransferNoAllowanceRevert(
- {
- operator: _lender,
- from: _lender1,
- to: _lender2,
- indexes: indexes
- }
- );
-
- // check lenders lp balance
- _assertLenderLpBalance(
- {
- lender: _lender1,
- index: indexes[0],
- lpBalance: 0,
- depositTime: 0
- }
- );
- _assertLenderLpBalance(
- {
- lender: _lender2,
- index: indexes[0],
- lpBalance: 15_000 * 1e27,
- depositTime: _startTime + 2 hours
- }
- );
- _assertLenderLpBalance(
- {
- lender: _lender1,
- index: indexes[1],
- lpBalance: 0,
- depositTime: 0
- }
- );
- _assertLenderLpBalance(
- {
- lender: _lender2,
- index: indexes[1],
- lpBalance: 30_000 * 1e27,
- depositTime: _startTime + 2 hours
- }
- );
- _assertLenderLpBalance(
- {
- lender: _lender1,
- index: indexes[2],
- lpBalance: 0,
- depositTime: 0
- }
- );
- _assertLenderLpBalance(
- {
- lender: _lender2,
- index: indexes[2],
- lpBalance: 45_000 * 1e27,
- depositTime: _startTime + 2 hours
- }
- );
- }
-}
diff --git a/tests/forge/ERC20Pool/ERC20PoolTransferLPs.t.sol b/tests/forge/ERC20Pool/ERC20PoolTransferLPs.t.sol
new file mode 100644
index 000000000..7ac64107d
--- /dev/null
+++ b/tests/forge/ERC20Pool/ERC20PoolTransferLPs.t.sol
@@ -0,0 +1,541 @@
+// SPDX-License-Identifier: UNLICENSED
+pragma solidity 0.8.14;
+
+import { ERC20HelperContract } from './ERC20DSTestPlus.sol';
+
+import 'src/libraries/helpers/PoolHelper.sol';
+
+contract ERC20PoolTransferLPsTest is ERC20HelperContract {
+
+ address internal _lender;
+ address internal _lender1;
+ address internal _lender2;
+
+ function setUp() external {
+ _lender = makeAddr("lender");
+ _lender1 = makeAddr("lender1");
+ _lender2 = makeAddr("lender2");
+
+ _mintQuoteAndApproveTokens(_lender, 200_000 * 1e18);
+ _mintQuoteAndApproveTokens(_lender1, 200_000 * 1e18);
+ _mintQuoteAndApproveTokens(_lender2, 200_000 * 1e18);
+ }
+
+ /**************************/
+ /*** Transfer LPs Tests ***/
+ /**************************/
+
+ function testTransferLPsToZeroAddress() external tearDown {
+ uint256[] memory indexes = new uint256[](3);
+ indexes[0] = 2550;
+ indexes[1] = 2551;
+ indexes[2] = 2552;
+
+ // should fail if allowed owner is not set
+ _assertTransferNoAllowanceRevert({
+ operator: _lender,
+ from: _lender1,
+ to: _lender2,
+ indexes: indexes
+ });
+
+ // should fail if allowed owner is set to 0x
+ changePrank(_lender1);
+ _pool.approveLpOwnership(address(0), indexes[0], 1_000 * 1e18);
+
+ _assertTransferNoAllowanceRevert({
+ operator: _lender,
+ from: _lender1,
+ to: _lender2,
+ indexes: indexes
+ });
+ }
+
+ function testTransferLPsToUnallowedAddress() external tearDown {
+ uint256[] memory indexes = new uint256[](3);
+ indexes[0] = 2550;
+ indexes[1] = 2551;
+ indexes[2] = 2552;
+
+ // should fail if allowed owner is set to lender2 address but trying to transfer to lender address
+ changePrank(_lender1);
+ _pool.approveLpOwnership(_lender2, indexes[0], 1_000 * 1e18);
+ _pool.approveLpOwnership(_lender2, indexes[1], 1_000 * 1e18);
+ _pool.approveLpOwnership(_lender2, indexes[2], 1_000 * 1e18);
+
+ _assertTransferNoAllowanceRevert({
+ operator: _lender,
+ from: _lender1,
+ to: _lender2,
+ indexes: indexes
+ });
+ }
+
+ function testTransferLPsToInvalidIndex() external tearDown {
+ uint256[] memory indexes = new uint256[](3);
+ indexes[0] = 9999;
+ indexes[1] = 2550;
+ indexes[2] = 2552;
+
+ // should fail since 9999 is not a valid index
+ changePrank(_lender1);
+ _pool.approveLpOwnership(_lender2, indexes[0], 1_000 * 1e18);
+ _pool.approveLpOwnership(_lender2, indexes[1], 1_000 * 1e18);
+ _pool.approveLpOwnership(_lender2, indexes[2], 1_000 * 1e18);
+
+ _assertTransferInvalidIndexRevert({
+ operator: _lender,
+ from: _lender1,
+ to: _lender2,
+ indexes: indexes
+ });
+ }
+
+ function testTransferLPsGreaterThanBalance() external tearDown {
+ uint256[] memory indexes = new uint256[](2);
+ indexes[0] = 2550;
+ indexes[1] = 2551;
+
+ _addInitialLiquidity({
+ from: _lender1,
+ amount: 10_000 * 1e18,
+ index: indexes[0]
+ });
+ _addInitialLiquidity({
+ from: _lender1,
+ amount: 20_000 * 1e18,
+ index: indexes[1]
+ });
+
+ // set allowed owner to lender2 address
+ _pool.approveLpOwnership(_lender2, indexes[0], 10_000 * 1e18);
+ _pool.approveLpOwnership(_lender2, indexes[1], 30_000 * 1e18);
+
+ _assertTransferNoAllowanceRevert({
+ operator: _lender2,
+ from: _lender1,
+ to: _lender2,
+ indexes: indexes
+ });
+ }
+
+ function testTransferLPsToSameOwner() external {
+ uint256[] memory indexes = new uint256[](1);
+ indexes[0] = 2550;
+
+ skip(1 hours);
+
+ _addInitialLiquidity({
+ from: _lender1,
+ amount: 10_000 * 1e18,
+ index: indexes[0]
+ });
+
+ changePrank(_lender1);
+ _pool.approveLpOwnership(_lender1, indexes[0], 10_000 * 1e18);
+
+ _assertLenderLpBalance({
+ lender: _lender1,
+ index: indexes[0],
+ lpBalance: 10_000 * 1e18,
+ depositTime: _startTime + 1 hours
+ });
+
+ // should revert if trying to transfer LPs to same address
+ _assertTransferToSameOwnerRevert({
+ operator: _lender,
+ from: _lender1,
+ to: _lender1,
+ indexes: indexes
+ });
+ }
+
+ function testTransferLPsForAllIndexes() external tearDown {
+ uint256[] memory indexes = new uint256[](3);
+ indexes[0] = 2550;
+ indexes[1] = 2551;
+ indexes[2] = 2552;
+
+ skip(1 hours);
+
+ _addInitialLiquidity({
+ from: _lender1,
+ amount: 10_000 * 1e18,
+ index: indexes[0]
+ });
+ _addInitialLiquidity({
+ from: _lender1,
+ amount: 20_000 * 1e18,
+ index: indexes[1]
+ });
+ _addInitialLiquidity({
+ from: _lender1,
+ amount: 30_000 * 1e18,
+ index: indexes[2]
+ });
+
+ // check lenders lp balance
+ _assertLenderLpBalance({
+ lender: _lender1,
+ index: indexes[0],
+ lpBalance: 10_000 * 1e18,
+ depositTime: _startTime + 1 hours
+ });
+ _assertLenderLpBalance({
+ lender: _lender2,
+ index: indexes[0],
+ lpBalance: 0,
+ depositTime: 0
+ });
+ _assertLenderLpBalance({
+ lender: _lender1,
+ index: indexes[1],
+ lpBalance: 20_000 * 1e18,
+ depositTime: _startTime + 1 hours
+ });
+ _assertLenderLpBalance({
+ lender: _lender2,
+ index: indexes[1],
+ lpBalance: 0,
+ depositTime: 0
+ });
+ _assertLenderLpBalance({
+ lender: _lender1,
+ index: indexes[2],
+ lpBalance: 30_000 * 1e18,
+ depositTime: _startTime + 1 hours
+ });
+ _assertLenderLpBalance({
+ lender: _lender2,
+ index: indexes[2],
+ lpBalance: 0,
+ depositTime: 0
+ });
+
+ // set allowed owner to lender2 address
+ _pool.approveLpOwnership(_lender2, indexes[0], 10_000 * 1e18);
+ _pool.approveLpOwnership(_lender2, indexes[1], 20_000 * 1e18);
+ _pool.approveLpOwnership(_lender2, indexes[2], 30_000 * 1e18);
+
+ // transfer LP tokens for all indexes
+ _transferLPs({
+ operator: _lender,
+ from: _lender1,
+ to: _lender2,
+ indexes: indexes,
+ lpBalance: 60_000 * 1e18
+ });
+
+ // check that old token ownership was removed - a new transfer should fail
+ _assertTransferNoAllowanceRevert({
+ operator: _lender,
+ from: _lender1,
+ to: _lender2,
+ indexes: indexes
+ });
+
+ // check lenders lp balance
+ _assertLenderLpBalance({
+ lender: _lender1,
+ index: indexes[0],
+ lpBalance: 0,
+ depositTime: 0
+ });
+ _assertLenderLpBalance({
+ lender: _lender2,
+ index: indexes[0],
+ lpBalance: 10_000 * 1e18,
+ depositTime: _startTime + 1 hours
+ });
+ _assertLenderLpBalance({
+ lender: _lender1,
+ index: indexes[1],
+ lpBalance: 0,
+ depositTime: 0
+ });
+ _assertLenderLpBalance({
+ lender: _lender2,
+ index: indexes[1],
+ lpBalance: 20_000 * 1e18,
+ depositTime: _startTime + 1 hours
+ });
+ _assertLenderLpBalance({
+ lender: _lender1,
+ index: indexes[2],
+ lpBalance: 0,
+ depositTime: 0
+ });
+ _assertLenderLpBalance({
+ lender: _lender2,
+ index: indexes[2],
+ lpBalance: 30_000 * 1e18,
+ depositTime: _startTime + 1 hours
+ });
+ }
+
+ function testTransferLPsForTwoIndexes() external tearDown {
+ uint256[] memory depositIndexes = new uint256[](3);
+ depositIndexes[0] = 2550;
+ depositIndexes[1] = 2551;
+ depositIndexes[2] = 2552;
+
+ uint256[] memory transferIndexes = new uint256[](2);
+ transferIndexes[0] = 2550;
+ transferIndexes[1] = 2552;
+
+ _addInitialLiquidity({
+ from: _lender1,
+ amount: 10_000 * 1e18,
+ index: depositIndexes[0]
+ });
+ _addInitialLiquidity({
+ from: _lender1,
+ amount: 20_000 * 1e18,
+ index: depositIndexes[1]
+ });
+ _addInitialLiquidity({
+ from: _lender1,
+ amount: 30_000 * 1e18,
+ index: depositIndexes[2]
+ });
+
+ // check lenders lp balance
+ _assertLenderLpBalance({
+ lender: _lender1,
+ index: depositIndexes[0],
+ lpBalance: 10_000 * 1e18,
+ depositTime: _startTime
+ });
+ _assertLenderLpBalance({
+ lender: _lender2,
+ index: depositIndexes[0],
+ lpBalance: 0,
+ depositTime: 0
+ });
+ _assertLenderLpBalance({
+ lender: _lender1,
+ index: depositIndexes[1],
+ lpBalance: 20_000 * 1e18,
+ depositTime: _startTime
+ });
+ _assertLenderLpBalance({
+ lender: _lender2,
+ index: depositIndexes[1],
+ lpBalance: 0,
+ depositTime: 0
+ });
+ _assertLenderLpBalance({
+ lender: _lender1,
+ index: depositIndexes[2],
+ lpBalance: 30_000 * 1e18,
+ depositTime: _startTime
+ });
+ _assertLenderLpBalance({
+ lender: _lender2,
+ index: depositIndexes[2],
+ lpBalance: 0,
+ depositTime: 0
+ });
+
+ // set allowed owner to lender2 address
+ _pool.approveLpOwnership(_lender2, transferIndexes[0], 10_000 * 1e18);
+ _pool.approveLpOwnership(_lender2, transferIndexes[1], 30_000 * 1e18);
+
+ // transfer LP tokens for 2 indexes
+ _transferLPs({
+ operator: _lender,
+ from: _lender1,
+ to: _lender2,
+ indexes: transferIndexes,
+ lpBalance: 40_000 * 1e18
+ });
+
+ // check that old token ownership was removed - transfer with same indexes should fail
+ _assertTransferNoAllowanceRevert({
+ operator: _lender,
+ from: _lender1,
+ to: _lender2,
+ indexes: transferIndexes
+ });
+
+ // check lenders lp balance
+ _assertLenderLpBalance({
+ lender: _lender1,
+ index: depositIndexes[0],
+ lpBalance: 0,
+ depositTime: 0
+ });
+ _assertLenderLpBalance({
+ lender: _lender2,
+ index: depositIndexes[0],
+ lpBalance: 10_000 * 1e18,
+ depositTime: _startTime
+ });
+ _assertLenderLpBalance({
+ lender: _lender1,
+ index: depositIndexes[1],
+ lpBalance: 20_000 * 1e18,
+ depositTime: _startTime
+ });
+ _assertLenderLpBalance({
+ lender: _lender2,
+ index: depositIndexes[1],
+ lpBalance: 0,
+ depositTime: 0
+ });
+ _assertLenderLpBalance({
+ lender: _lender1,
+ index: depositIndexes[2],
+ lpBalance: 0,
+ depositTime: 0
+ });
+ _assertLenderLpBalance({
+ lender: _lender2,
+ index: depositIndexes[2],
+ lpBalance: 30_000 * 1e18,
+ depositTime: _startTime
+ });
+ }
+
+ function testTransferLPsToLenderWithLPTokens() external tearDown {
+ uint256[] memory indexes = new uint256[](3);
+ indexes[0] = 2550;
+ indexes[1] = 2551;
+ indexes[2] = 2552;
+
+ skip(1 hours);
+
+ _addInitialLiquidity({
+ from: _lender1,
+ amount: 10_000 * 1e18,
+ index: indexes[0]
+ });
+ _addInitialLiquidity({
+ from: _lender1,
+ amount: 20_000 * 1e18,
+ index: indexes[1]
+ });
+ _addInitialLiquidity({
+ from: _lender1,
+ amount: 30_000 * 1e18,
+ index: indexes[2]
+ });
+
+ skip(1 hours);
+
+ _addInitialLiquidity({
+ from: _lender2,
+ amount: 5_000 * 1e18,
+ index: indexes[0]
+ });
+ _addInitialLiquidity({
+ from: _lender2,
+ amount: 10_000 * 1e18,
+ index: indexes[1]
+ });
+ _addInitialLiquidity({
+ from: _lender2,
+ amount: 15_000 * 1e18,
+ index: indexes[2]
+ });
+
+ // check lenders lp balance
+ _assertLenderLpBalance({
+ lender: _lender1,
+ index: indexes[0],
+ lpBalance: 10_000 * 1e18,
+ depositTime: _startTime + 1 hours
+ });
+ _assertLenderLpBalance({
+ lender: _lender2,
+ index: indexes[0],
+ lpBalance: 5_000 * 1e18,
+ depositTime: _startTime + 2 hours
+ });
+ _assertLenderLpBalance({
+ lender: _lender1,
+ index: indexes[1],
+ lpBalance: 20_000 * 1e18,
+ depositTime: _startTime + 1 hours
+ });
+ _assertLenderLpBalance({
+ lender: _lender2,
+ index: indexes[1],
+ lpBalance: 10_000 * 1e18,
+ depositTime: _startTime + 2 hours
+ });
+ _assertLenderLpBalance({
+ lender: _lender1,
+ index: indexes[2],
+ lpBalance: 30_000 * 1e18,
+ depositTime: _startTime + 1 hours
+ });
+ _assertLenderLpBalance({
+ lender: _lender2,
+ index: indexes[2],
+ lpBalance: 15_000 * 1e18,
+ depositTime: _startTime + 2 hours
+ });
+
+ // set allowed owner to lender2 address
+ changePrank(_lender1);
+ _pool.approveLpOwnership(_lender2, indexes[0], 10_000 * 1e18);
+ _pool.approveLpOwnership(_lender2, indexes[1], 20_000 * 1e18);
+ _pool.approveLpOwnership(_lender2, indexes[2], 30_000 * 1e18);
+
+ // transfer LP tokens for all indexes
+ _transferLPs({
+ operator: _lender,
+ from: _lender1,
+ to: _lender2,
+ indexes: indexes,
+ lpBalance: 60_000 * 1e18
+ });
+
+ // check that old token ownership was removed - transfer with same indexes should fail
+ _assertTransferNoAllowanceRevert({
+ operator: _lender,
+ from: _lender1,
+ to: _lender2,
+ indexes: indexes
+ });
+
+ // check lenders lp balance
+ _assertLenderLpBalance({
+ lender: _lender1,
+ index: indexes[0],
+ lpBalance: 0,
+ depositTime: 0
+ });
+ _assertLenderLpBalance({
+ lender: _lender2,
+ index: indexes[0],
+ lpBalance: 15_000 * 1e18,
+ depositTime: _startTime + 2 hours
+ });
+ _assertLenderLpBalance({
+ lender: _lender1,
+ index: indexes[1],
+ lpBalance: 0,
+ depositTime: 0
+ });
+ _assertLenderLpBalance({
+ lender: _lender2,
+ index: indexes[1],
+ lpBalance: 30_000 * 1e18,
+ depositTime: _startTime + 2 hours
+ });
+ _assertLenderLpBalance({
+ lender: _lender1,
+ index: indexes[2],
+ lpBalance: 0,
+ depositTime: 0
+ });
+ _assertLenderLpBalance({
+ lender: _lender2,
+ index: indexes[2],
+ lpBalance: 45_000 * 1e18,
+ depositTime: _startTime + 2 hours
+ });
+ }
+}
diff --git a/tests/forge/ERC20Pool/ERC20SafeTransferTokens.sol b/tests/forge/ERC20Pool/ERC20SafeTransferTokens.sol
index d72edb38e..06abe35ff 100644
--- a/tests/forge/ERC20Pool/ERC20SafeTransferTokens.sol
+++ b/tests/forge/ERC20Pool/ERC20SafeTransferTokens.sol
@@ -36,7 +36,7 @@ contract ERC20SafeTransferTokens is ERC20HelperContract {
usdt.approve(address(pool), 1_000 * 1e18);
- pool.addQuoteToken(1_000 * 1e18, 2549);
+ pool.addQuoteToken(1_000 * 1e18, 2549, type(uint256).max);
changePrank(_borrower);
@@ -54,7 +54,7 @@ contract ERC20SafeTransferTokens is ERC20HelperContract {
_quote.approve(address(pool), 1_000 * 1e18);
- pool.addQuoteToken(1_000 * 1e18, 2549);
+ pool.addQuoteToken(1_000 * 1e18, 2549, type(uint256).max);
changePrank(_borrower);
diff --git a/tests/forge/ERC20Pool/invariants/BasicInvariants.t.sol b/tests/forge/ERC20Pool/invariants/BasicInvariants.t.sol
new file mode 100644
index 000000000..55062cabf
--- /dev/null
+++ b/tests/forge/ERC20Pool/invariants/BasicInvariants.t.sol
@@ -0,0 +1,248 @@
+
+// SPDX-License-Identifier: UNLICENSED
+
+pragma solidity 0.8.14;
+
+import '@std/Test.sol';
+import "@std/console.sol";
+
+import { TestBase } from './TestBase.sol';
+
+import { Maths } from 'src/libraries/internal/Maths.sol';
+
+import { LENDER_MIN_BUCKET_INDEX, LENDER_MAX_BUCKET_INDEX, BORROWER_MIN_BUCKET_INDEX, BasicPoolHandler } from './handlers/BasicPoolHandler.sol';
+
+import { IBaseHandler } from './handlers/IBaseHandler.sol';
+
+// contains invariants for the test
+contract BasicInvariants is TestBase {
+
+ /**************************************************************************************************************************************/
+ /*** Invariant Tests ***/
+ /***************************************************************************************************************************************
+ * Bucket
+ * B1: totalBucketLPs === totalLenderLps
+ * B2: bucketLps == 0 (if bucket quote and collateral is 0)
+ * B3: exchangeRate == 0 (if bucket quote and collateral is 0)
+ * Quote Token
+ * QT1: poolQtBal + poolDebt >= totalBondEscrowed + poolDepositSize
+ * QT2: pool t0 debt = sum of all borrower's t0 debt
+
+ * Collateral Token
+ * CT1: poolCtBal >= sum of all borrower's collateral + sum of all bucket's claimable collateral
+ * CT7: pool Pledged collateral = sum of all borrower's pledged collateral
+
+ * Loan
+ * L1: for each Loan in loans array (LoansState.loans) starting from index 1, the corresponding address (Loan.borrower) is not 0x, the threshold price (Loan.thresholdPrice) is different than 0
+ * L2: Loan in loans array (LoansState.loans) at index 0 has the corresponding address (Loan.borrower) equal with 0x address and the threshold price (Loan.thresholdPrice) equal with 0
+ * L3: Loans array (LoansState.loans) is a max-heap with respect to t0-threshold price: the t0TP of loan at index i is >= the t0-threshold price of the loans at index 2i and 2i+1
+
+ * Interest Rate
+ * I1: Interest rate should only update once in 12 hours
+ * I3: Inflator should only update once per block
+ ****************************************************************************************************************************************/
+
+ uint256 internal constant NUM_ACTORS = 10;
+ BasicPoolHandler internal _basicPoolHandler;
+ address internal _handler;
+
+ // bucket exchange rate tracking
+ mapping(uint256 => uint256) internal previousBucketExchangeRate;
+
+ uint256 previousInterestRateUpdate;
+
+ uint256 previousInflator;
+
+ uint256 previousInflatorUpdate;
+
+ function setUp() public override virtual{
+
+ super.setUp();
+
+ _basicPoolHandler = new BasicPoolHandler(address(_pool), address(_quote), address(_collateral), address(_poolInfo), NUM_ACTORS);
+ _handler = address(_basicPoolHandler);
+ excludeContract(address(_collateral));
+ excludeContract(address(_quote));
+ excludeContract(address(_poolFactory));
+ excludeContract(address(_pool));
+ excludeContract(address(_poolInfo));
+ excludeContract(address(_impl));
+
+ for (uint256 bucketIndex = LENDER_MIN_BUCKET_INDEX; bucketIndex <= LENDER_MAX_BUCKET_INDEX; bucketIndex++) {
+ ( , , , , ,uint256 exchangeRate) = _poolInfo.bucketInfo(address(_pool), bucketIndex);
+ previousBucketExchangeRate[bucketIndex] = exchangeRate;
+ }
+
+ (, previousInterestRateUpdate) = _pool.interestRateInfo();
+
+ // TODO: Change once this issue is resolved -> https://github.com/foundry-rs/foundry/issues/2963
+ targetSender(address(0x1234));
+ }
+
+ // checks pool lps are equal to sum of all lender lps in a bucket
+ function invariant_Lps_B1() public {
+ uint256 actorCount = IBaseHandler(_handler).getActorsCount();
+ for (uint256 bucketIndex = LENDER_MIN_BUCKET_INDEX; bucketIndex <= LENDER_MAX_BUCKET_INDEX; bucketIndex++) {
+ uint256 totalLps;
+ for (uint256 i = 0; i < actorCount; i++) {
+ address lender = IBaseHandler(_handler).actors(i);
+ (uint256 lps, ) = _pool.lenderInfo(bucketIndex, lender);
+ totalLps += lps;
+ }
+ (uint256 bucketLps, , , , ) = _pool.bucketInfo(bucketIndex);
+ assertEq(bucketLps, totalLps, "Incorrect Bucket/lender lps");
+ }
+ }
+
+ // checks bucket lps are equal to 0 if bucket quote and collateral are 0
+ // checks exchange rate is 1e27 if bucket quote and collateral are 0
+ function invariant_Buckets_B2_B3() public view {
+ for (uint256 bucketIndex = LENDER_MIN_BUCKET_INDEX; bucketIndex <= LENDER_MAX_BUCKET_INDEX; bucketIndex++) {
+ ( ,uint256 deposit, uint256 collateral, uint256 bucketLps, ,uint256 exchangeRate) = _poolInfo.bucketInfo(address(_pool), bucketIndex);
+
+ if (collateral == 0 && deposit == 0) {
+ require(bucketLps == 0, "Incorrect bucket lps");
+ require(exchangeRate == 1e18, "Incorrect exchange rate");
+ }
+ }
+ }
+
+ // checks pool quote token balance is greater than equals total deposits in pool
+ function invariant_quoteTokenBalance_QT1() public {
+ uint256 poolBalance = _quote.balanceOf(address(_pool));
+ uint256 t0debt = _pool.totalT0Debt();
+ (uint256 inflator, ) = _pool.inflatorInfo();
+ uint256 poolDebt = Maths.wmul(t0debt, inflator);
+ (uint256 totalPoolBond, uint256 unClaimed, ) = _pool.reservesInfo();
+
+ assertGe(poolBalance + poolDebt, totalPoolBond + _pool.depositSize() + unClaimed, "Incorrect pool debt");
+ }
+
+ // checks pools collateral Balance to be equal to collateral pledged
+ function invariant_collateralBalance_CT1_CT7() public {
+ uint256 actorCount = IBaseHandler(_handler).getActorsCount();
+ uint256 totalCollateralPledged;
+ for(uint256 i = 0; i < actorCount; i++) {
+ address borrower = IBaseHandler(_handler).actors(i);
+ ( , uint256 borrowerCollateral, ) = _pool.borrowerInfo(borrower);
+ totalCollateralPledged += borrowerCollateral;
+ }
+
+ assertEq(_pool.pledgedCollateral(), totalCollateralPledged, "Incorrect Collateral Pledged");
+
+ uint256 collateralBalance = _collateral.balanceOf(address(_pool));
+ uint256 bucketCollateral;
+
+ for (uint256 bucketIndex = LENDER_MIN_BUCKET_INDEX; bucketIndex <= LENDER_MAX_BUCKET_INDEX; bucketIndex++) {
+ (, , uint256 collateral , , ) = _pool.bucketInfo(bucketIndex);
+ bucketCollateral += collateral;
+ }
+
+ assertGe(collateralBalance, bucketCollateral + _pool.pledgedCollateral());
+ }
+
+ // checks pool debt is equal to sum of all borrowers debt
+ function invariant_pooldebt_QT2() public view {
+ uint256 actorCount = IBaseHandler(_handler).getActorsCount();
+ uint256 totalDebt;
+ for(uint256 i = 0; i < actorCount; i++) {
+ address borrower = IBaseHandler(_handler).actors(i);
+ (uint256 debt, , ) = _pool.borrowerInfo(borrower);
+ totalDebt += debt;
+ }
+
+ uint256 poolDebt = _pool.totalT0Debt();
+
+ require(poolDebt == totalDebt, "Incorrect pool debt");
+ }
+
+ function _invariant_exchangeRate_RE1_RE2_R3_R4_R5_R6() public {
+ for (uint256 bucketIndex = LENDER_MIN_BUCKET_INDEX; bucketIndex <= LENDER_MAX_BUCKET_INDEX; bucketIndex++) {
+ ( , , , , ,uint256 exchangeRate) = _poolInfo.bucketInfo(address(_pool), bucketIndex);
+ if (!IBaseHandler(_handler).shouldExchangeRateChange()) {
+ console.log("======================================");
+ console.log("Bucket Index -->", bucketIndex);
+ console.log("Previous exchange Rate -->", previousBucketExchangeRate[bucketIndex]);
+ console.log("Current exchange Rate -->", exchangeRate);
+ requireWithinDiff(exchangeRate, previousBucketExchangeRate[bucketIndex], 1e12, "Incorrect exchange Rate changed");
+ console.log("======================================");
+ }
+ previousBucketExchangeRate[bucketIndex] = exchangeRate;
+ }
+ }
+
+ function invariant_loan_L1_L2_L3() public view {
+ (address borrower, uint256 tp) = _pool.loanInfo(0);
+
+ // first loan in loan heap should be 0
+ require(borrower == address(0), "Incorrect borrower");
+ require(tp == 0, "Incorrect threshold price");
+
+ ( , , uint256 totalLoans) = _pool.loansInfo();
+
+ for(uint256 loanId = 1; loanId < totalLoans; loanId++) {
+ (borrower, tp) = _pool.loanInfo(loanId);
+
+ // borrower address and threshold price should not 0
+ require(borrower != address(0), "Incorrect borrower");
+ require(tp != 0, "Incorrect threshold price");
+
+ // tp of a loan at index 'i' in loan array should be greater than equals to loans at index '2i' and '2i+1'
+ (, uint256 tp1) = _pool.loanInfo(2 * loanId);
+ (, uint256 tp2) = _pool.loanInfo(2 * loanId + 1);
+
+ require(tp >= tp1, "Incorrect loan heap");
+ require(tp >= tp2, "Incorrect loan heap");
+ }
+ }
+
+ // interest should only update once in 12 hours
+ function invariant_interest_rate_I1() public {
+
+ (, uint256 currentInterestRateUpdate) = _pool.interestRateInfo();
+
+ if (currentInterestRateUpdate != previousInterestRateUpdate) {
+ require(currentInterestRateUpdate - previousInterestRateUpdate >= 12 hours, "Incorrect interest rate update");
+ }
+ previousInterestRateUpdate = currentInterestRateUpdate;
+ }
+
+ // inflator should only update once per block
+ function invariant_inflator_I3() public {
+ (uint256 currentInflator, uint256 currentInflatorUpdate) = _pool.inflatorInfo();
+ if(currentInflatorUpdate == previousInflatorUpdate) {
+ require(currentInflator == previousInflator, "Incorrect inflator update");
+ }
+ previousInflator = currentInflator;
+ previousInflatorUpdate = currentInflatorUpdate;
+ }
+
+ function invariant_call_summary() external view virtual {
+ console.log("\nCall Summary\n");
+ console.log("--Lender----------");
+ console.log("BBasicHandler.addQuoteToken ", IBaseHandler(_handler).numberOfCalls("BBasicHandler.addQuoteToken"));
+ console.log("UBBasicHandler.addQuoteToken ", IBaseHandler(_handler).numberOfCalls("UBBasicHandler.addQuoteToken"));
+ console.log("BBasicHandler.removeQuoteToken ", IBaseHandler(_handler).numberOfCalls("BBasicHandler.removeQuoteToken"));
+ console.log("UBBasicHandler.removeQuoteToken ", IBaseHandler(_handler).numberOfCalls("UBBasicHandler.removeQuoteToken"));
+ console.log("BBasicHandler.addCollateral ", IBaseHandler(_handler).numberOfCalls("BBasicHandler.addCollateral"));
+ console.log("UBBasicHandler.addCollateral ", IBaseHandler(_handler).numberOfCalls("UBBasicHandler.addCollateral"));
+ console.log("BBasicHandler.removeCollateral ", IBaseHandler(_handler).numberOfCalls("BBasicHandler.removeCollateral"));
+ console.log("UBBasicHandler.removeCollateral ", IBaseHandler(_handler).numberOfCalls("UBBasicHandler.removeCollateral"));
+ console.log("--Borrower--------");
+ console.log("BBasicHandler.drawDebt ", IBaseHandler(_handler).numberOfCalls("BBasicHandler.drawDebt"));
+ console.log("UBBasicHandler.drawDebt ", IBaseHandler(_handler).numberOfCalls("UBBasicHandler.drawDebt"));
+ console.log("BBasicHandler.repayDebt ", IBaseHandler(_handler).numberOfCalls("BBasicHandler.repayDebt"));
+ console.log("UBBasicHandler.repayDebt ", IBaseHandler(_handler).numberOfCalls("UBBasicHandler.repayDebt"));
+ console.log("------------------");
+ console.log(
+ "Sum",
+ IBaseHandler(_handler).numberOfCalls("BBasicHandler.addQuoteToken") +
+ IBaseHandler(_handler).numberOfCalls("BBasicHandler.removeQuoteToken") +
+ IBaseHandler(_handler).numberOfCalls("BBasicHandler.addCollateral") +
+ IBaseHandler(_handler).numberOfCalls("BBasicHandler.removeCollateral") +
+ IBaseHandler(_handler).numberOfCalls("BBasicHandler.drawDebt") +
+ IBaseHandler(_handler).numberOfCalls("BBasicHandler.repayDebt")
+ );
+ }
+
+}
\ No newline at end of file
diff --git a/tests/forge/ERC20Pool/invariants/InvariantTest.sol b/tests/forge/ERC20Pool/invariants/InvariantTest.sol
new file mode 100644
index 000000000..a1e7ab0e6
--- /dev/null
+++ b/tests/forge/ERC20Pool/invariants/InvariantTest.sol
@@ -0,0 +1,76 @@
+// SPDX-License-Identifier: UNLICENSED
+
+pragma solidity 0.8.14;
+
+contract InvariantTest {
+
+ struct FuzzSelector {
+ address addr;
+ bytes4[] selectors;
+ }
+
+ address[] private _excludedContracts;
+ address[] private _excludedSenders;
+ address[] private _targetedContracts;
+ address[] private _targetedSenders;
+
+ FuzzSelector[] internal _targetedSelectors;
+
+ function excludeContract(address newExcludedContract_) internal {
+ _excludedContracts.push(newExcludedContract_);
+ }
+
+ function excludeContracts() public view returns (address[] memory excludedContracts_) {
+ require(_excludedContracts.length != uint256(0), "NO_EXCLUDED_CONTRACTS");
+ excludedContracts_ = _excludedContracts;
+ }
+
+ function excludeSender(address newExcludedSender_) internal {
+ _excludedSenders.push(newExcludedSender_);
+ }
+
+ function excludeSenders() public view returns (address[] memory excludedSenders_) {
+ require(_excludedSenders.length != uint256(0), "NO_EXCLUDED_SENDERS");
+ excludedSenders_ = _excludedSenders;
+ }
+
+ function targetContract(address newTargetedContract_) internal {
+ _targetedContracts.push(newTargetedContract_);
+ }
+
+ function targetContracts() public view returns (address[] memory targetedContracts_) {
+ require(_targetedContracts.length != uint256(0), "NO_TARGETED_CONTRACTS");
+ targetedContracts_ = _targetedContracts;
+ }
+
+ function targetSelector(FuzzSelector memory newTargetedSelector_) internal {
+ _targetedSelectors.push(newTargetedSelector_);
+ }
+
+ function targetSelectors() public view returns (FuzzSelector[] memory targetedSelectors_) {
+ require(targetedSelectors_.length != uint256(0), "NO_TARGETED_SELECTORS");
+ targetedSelectors_ = _targetedSelectors;
+ }
+
+ function targetSender(address newTargetedSender_) internal {
+ _targetedSenders.push(newTargetedSender_);
+ }
+
+ function targetSenders() public view returns (address[] memory targetedSenders_) {
+ require(_targetedSenders.length != uint256(0), "NO_TARGETED_SENDERS");
+ targetedSenders_ = _targetedSenders;
+ }
+
+ /************************/
+ /*** Helper Functions ***/
+ /************************/
+
+ function getDiff(uint256 x, uint256 y) internal pure returns (uint256 diff) {
+ diff = x > y ? x - y : y - x;
+ }
+
+ function requireWithinDiff(uint256 x, uint256 y, uint256 expectedDiff, string memory err) internal pure {
+ require(getDiff(x, y) <= expectedDiff, err);
+ }
+
+}
\ No newline at end of file
diff --git a/tests/forge/ERC20Pool/invariants/LiquidationInvariant.t.sol b/tests/forge/ERC20Pool/invariants/LiquidationInvariant.t.sol
new file mode 100644
index 000000000..b455e9d8b
--- /dev/null
+++ b/tests/forge/ERC20Pool/invariants/LiquidationInvariant.t.sol
@@ -0,0 +1,152 @@
+// SPDX-License-Identifier: UNLICENSED
+
+pragma solidity 0.8.14;
+
+import '@std/Test.sol';
+import "@std/console.sol";
+
+import { TestBase } from './TestBase.sol';
+
+import { LENDER_MIN_BUCKET_INDEX, LENDER_MAX_BUCKET_INDEX, BORROWER_MIN_BUCKET_INDEX } from './handlers/BasicPoolHandler.sol';
+
+import { LiquidationPoolHandler } from './handlers/LiquidationPoolHandler.sol';
+import { BasicInvariants } from './BasicInvariants.t.sol';
+import { IBaseHandler } from './handlers/IBaseHandler.sol';
+
+contract LiquidationInvariant is BasicInvariants {
+
+ /**************************************************************************************************************************************/
+ /*** Invariant Tests ***/
+ /***************************************************************************************************************************************
+ * Auction
+ * A1: totalDebtInAuction = sum of all debt of all borrowers kicked
+ * A2: totalBondEscrowed = sum of all kicker's bond = total Bond in Auction
+ * A3: number of borrowers with debt = number of loans + number of auctioned borrowers
+ * A4: number of auctions = total borrowers kicked
+ * A5: for each auction, kicker locked bond is more than equal to auction bond
+ ****************************************************************************************************************************************/
+
+ LiquidationPoolHandler internal _liquidationPoolHandler;
+
+ function setUp() public override virtual{
+
+ super.setUp();
+
+ excludeContract(address(_basicPoolHandler));
+
+ _liquidationPoolHandler = new LiquidationPoolHandler(address(_pool), address(_quote), address(_collateral), address(_poolInfo), NUM_ACTORS);
+ _handler = address(_liquidationPoolHandler);
+ }
+
+ // checks sum of all borrower's t0debt is equals to total pool t0debtInAuction
+ function invariant_debtInAuction_A1() public view {
+ uint256 actorCount = IBaseHandler(_handler).getActorsCount();
+ uint256 totalT0debtInAuction;
+ for(uint256 i = 0; i < actorCount; i++) {
+ address borrower = IBaseHandler(_handler).actors(i);
+ (, , , uint256 kickTime, , , , , ) = _pool.auctionInfo(borrower);
+ if(kickTime != 0) {
+ (uint256 t0debt, , ) = _pool.borrowerInfo(borrower);
+ totalT0debtInAuction += t0debt;
+ }
+ }
+ require(_pool.totalT0DebtInAuction() == totalT0debtInAuction, "Incorrect debt in auction");
+ }
+
+ // checks sum of all kicker bond is equal to total pool bond
+ function invariant_bond_A2() public view {
+ uint256 actorCount = IBaseHandler(_handler).getActorsCount();
+ uint256 totalKickerBond;
+ for(uint256 i = 0; i < actorCount; i++) {
+ address kicker = IBaseHandler(_handler).actors(i);
+ (, uint256 bond) = _pool.kickerInfo(kicker);
+ totalKickerBond += bond;
+ }
+
+ uint256 totalBondInAuction;
+
+ for(uint256 i = 0; i < actorCount; i++) {
+ address borrower = IBaseHandler(_handler).actors(i);
+ (, , uint256 bondSize, , , , , , ) = _pool.auctionInfo(borrower);
+ totalBondInAuction += bondSize;
+ }
+
+ require(totalBondInAuction == totalKickerBond, "Incorrect bond");
+
+ (uint256 totalPoolBond, , ) = _pool.reservesInfo();
+
+ require(totalPoolBond == totalKickerBond, "Incorrect bond");
+ }
+
+ // checks total borrowers with debt is equals to sum of borrowers unkicked and borrowers kicked
+ // checks total auctions is equals to total borrowers kicked
+ function invariant_auctions_A3_A4() public view {
+ uint256 actorCount = IBaseHandler(_handler).getActorsCount();
+ uint256 totalBorrowersWithDebt;
+ for(uint256 i = 0; i < actorCount; i++) {
+ address borrower = IBaseHandler(_handler).actors(i);
+ (uint256 t0Debt, , ) = _pool.borrowerInfo(borrower);
+ if(t0Debt > 0) {
+ totalBorrowersWithDebt += 1;
+ }
+ }
+ ( , , uint256 loansCount) = _pool.loansInfo();
+ uint256 totalAuction = _pool.totalAuctionsInPool();
+ require(totalBorrowersWithDebt == loansCount + totalAuction, "incorrect no of borrowers in LoanState");
+
+ uint256 borrowersKicked;
+ for(uint256 i = 0; i < actorCount; i++) {
+ address borrower = IBaseHandler(_handler).actors(i);
+ (, , , uint256 kickTime, , , , , ) = _pool.auctionInfo(borrower);
+ if(kickTime != 0) {
+ borrowersKicked += 1;
+ }
+ }
+ require(borrowersKicked == totalAuction, "Incorrect borrowers in auction");
+ }
+
+ function invariant_borrowers_A5() public view {
+ uint256 actorCount = IBaseHandler(_handler).getActorsCount();
+ for(uint256 i = 0; i < actorCount; i++) {
+ address borrower = IBaseHandler(_handler).actors(i);
+ (address kicker, , uint256 bondSize, , , , , , ) = _pool.auctionInfo(borrower);
+ (, uint256 lockedAmount) = _pool.kickerInfo(kicker);
+ require(lockedAmount >= bondSize, "Incorrect bond locked");
+ }
+ }
+
+ function invariant_call_summary() external view virtual override{
+ console.log("\nCall Summary\n");
+ console.log("--Lender----------");
+ console.log("BLiquidationHandler.addQuoteToken ", IBaseHandler(_handler).numberOfCalls("BBasicHandler.addQuoteToken"));
+ console.log("UBLiquidationHandler.addQuoteToken ", IBaseHandler(_handler).numberOfCalls("UBBasicHandler.addQuoteToken"));
+ console.log("BLiquidationHandler.removeQuoteToken ", IBaseHandler(_handler).numberOfCalls("BBasicHandler.removeQuoteToken"));
+ console.log("UBLiquidationHandler.removeQuoteToken ", IBaseHandler(_handler).numberOfCalls("UBBasicHandler.removeQuoteToken"));
+ console.log("BLiquidationHandler.addCollateral ", IBaseHandler(_handler).numberOfCalls("BBasicHandler.addCollateral"));
+ console.log("UBLiquidationHandler.addCollateral ", IBaseHandler(_handler).numberOfCalls("UBBasicHandler.addCollateral"));
+ console.log("BLiquidationHandler.removeCollateral ", IBaseHandler(_handler).numberOfCalls("BBasicHandler.removeCollateral"));
+ console.log("UBLiquidationHandler.removeCollateral ", IBaseHandler(_handler).numberOfCalls("UBBasicHandler.removeCollateral"));
+ console.log("--Borrower--------");
+ console.log("BLiquidationHandler.drawDebt ", IBaseHandler(_handler).numberOfCalls("BBasicHandler.drawDebt"));
+ console.log("UBLiquidationHandler.drawDebt ", IBaseHandler(_handler).numberOfCalls("UBBasicHandler.drawDebt"));
+ console.log("BLiquidationHandler.repayDebt ", IBaseHandler(_handler).numberOfCalls("BBasicHandler.repayDebt"));
+ console.log("UBLiquidationHandler.repayDebt ", IBaseHandler(_handler).numberOfCalls("UBBasicHandler.repayDebt"));
+ console.log("BLiquidationHandler.kickAuction ", IBaseHandler(_handler).numberOfCalls("BLiquidationHandler.kickAuction"));
+ console.log("UBLiquidationHandler.kickAuction ", IBaseHandler(_handler).numberOfCalls("UBLiquidationHandler.kickAuction"));
+ console.log("BLiquidationHandler.takeAuction ", IBaseHandler(_handler).numberOfCalls("BLiquidationHandler.takeAuction"));
+ console.log("UBLiquidationHandler.takeAuction ", IBaseHandler(_handler).numberOfCalls("UBLiquidationHandler.takeAuction"));
+ console.log("------------------");
+ console.log(
+ "Sum",
+ IBaseHandler(_handler).numberOfCalls("BBasicHandler.addQuoteToken") +
+ IBaseHandler(_handler).numberOfCalls("BBasicHandler.removeQuoteToken") +
+ IBaseHandler(_handler).numberOfCalls("BBasicHandler.addCollateral") +
+ IBaseHandler(_handler).numberOfCalls("BBasicHandler.removeCollateral") +
+ IBaseHandler(_handler).numberOfCalls("BBasicHandler.drawDebt") +
+ IBaseHandler(_handler).numberOfCalls("BBasicHandler.repayDebt") +
+ IBaseHandler(_handler).numberOfCalls("BLiquidationHandler.kickAuction") +
+ IBaseHandler(_handler).numberOfCalls("BLiquidationHandler.takeAuction")
+ );
+ }
+
+}
\ No newline at end of file
diff --git a/tests/forge/ERC20Pool/invariants/ReserveInvariants.t.sol b/tests/forge/ERC20Pool/invariants/ReserveInvariants.t.sol
new file mode 100644
index 000000000..0b9095fc8
--- /dev/null
+++ b/tests/forge/ERC20Pool/invariants/ReserveInvariants.t.sol
@@ -0,0 +1,59 @@
+// SPDX-License-Identifier: UNLICENSED
+
+pragma solidity 0.8.14;
+
+import '@std/Test.sol';
+import "@std/console.sol";
+
+import { TestBase } from './TestBase.sol';
+
+import { LENDER_MIN_BUCKET_INDEX, LENDER_MAX_BUCKET_INDEX, BORROWER_MIN_BUCKET_INDEX } from './handlers/BasicPoolHandler.sol';
+
+import { ReservePoolHandler } from './handlers/ReservePoolHandler.sol';
+import { LiquidationInvariant } from './LiquidationInvariant.t.sol';
+import { IBaseHandler } from './handlers/IBaseHandler.sol';
+
+contract ReserveInvariants is LiquidationInvariant {
+
+ ReservePoolHandler internal _reservePoolHandler;
+ uint256 previousReserves;
+
+ function setUp() public override virtual {
+
+ super.setUp();
+
+ excludeContract(address(_liquidationPoolHandler));
+
+ _reservePoolHandler = new ReservePoolHandler(address(_pool), address(_quote), address(_collateral), address(_poolInfo), NUM_ACTORS);
+ _handler = address(_reservePoolHandler);
+
+ (previousReserves, , , , ) = _poolInfo.poolReservesInfo(address(_pool));
+ }
+
+ // FIXME
+ function _invariant_reserves_RE1_RE2_RE3_RE4_RE5_RE6_RE7_RE8_RE9() public {
+
+ (uint256 currentReserves, , , , ) = _poolInfo.poolReservesInfo(address(_pool));
+ console.log("Current Reserves -->", currentReserves);
+ console.log("Previous Reserves -->", previousReserves);
+ if(!IBaseHandler(_handler).shouldReserveChange()) {
+ require(currentReserves == previousReserves, "Incorrect Reserves change");
+ }
+
+ uint256 firstTakeIncreaseInReserve = IBaseHandler(_handler).firstTakeIncreaseInReserve();
+
+ console.log("firstTakeIncreaseInReserve -->", firstTakeIncreaseInReserve);
+ if(IBaseHandler(_handler).firstTake()) {
+ requireWithinDiff(currentReserves, previousReserves + firstTakeIncreaseInReserve, 1e2, "Incorrect Reserves change with first take");
+ }
+
+ uint256 loanKickIncreaseInReserve = IBaseHandler(_handler).loanKickIncreaseInReserve();
+
+ console.log("loanKickIncreaseInReserve -->", loanKickIncreaseInReserve);
+ if(loanKickIncreaseInReserve != 0) {
+ requireWithinDiff(currentReserves, previousReserves + loanKickIncreaseInReserve, 1e2, "Incorrect Reserves change with kick");
+ }
+ previousReserves = currentReserves;
+ }
+
+}
\ No newline at end of file
diff --git a/tests/forge/ERC20Pool/invariants/TestBase.sol b/tests/forge/ERC20Pool/invariants/TestBase.sol
new file mode 100644
index 000000000..1e626e676
--- /dev/null
+++ b/tests/forge/ERC20Pool/invariants/TestBase.sol
@@ -0,0 +1,38 @@
+// SPDX-License-Identifier: UNLICENSED
+
+pragma solidity 0.8.14;
+
+import '@std/Test.sol';
+import "forge-std/console.sol";
+
+import { ERC20Pool } from 'src/ERC20Pool.sol';
+import { ERC20PoolFactory } from 'src/ERC20PoolFactory.sol';
+import { Token } from '../../utils/Tokens.sol';
+import { PoolInfoUtils } from 'src/PoolInfoUtils.sol';
+import { InvariantTest } from './InvariantTest.sol';
+
+contract TestBase is InvariantTest, Test {
+
+ // Mainnet ajna address
+ address internal _ajna = 0x9a96ec9B57Fb64FbC60B423d1f4da7691Bd35079;
+
+ Token internal _quote;
+ Token internal _collateral;
+
+ ERC20Pool internal _pool;
+ ERC20Pool internal _impl;
+ PoolInfoUtils internal _poolInfo;
+ ERC20PoolFactory internal _poolFactory;
+
+ function setUp() public virtual {
+ // Tokens
+ _quote = new Token("Quote", "Q");
+ _collateral = new Token("Collateral", "C");
+
+ // Pool
+ _poolFactory = new ERC20PoolFactory(_ajna);
+ _pool = ERC20Pool(_poolFactory.deployPool(address(_collateral), address(_quote), 0.05 * 10**18));
+ _poolInfo = new PoolInfoUtils();
+ _impl = _poolFactory.implementation();
+ }
+}
\ No newline at end of file
diff --git a/tests/forge/ERC20Pool/invariants/handlers/BaseHandler.sol b/tests/forge/ERC20Pool/invariants/handlers/BaseHandler.sol
new file mode 100644
index 000000000..863ece656
--- /dev/null
+++ b/tests/forge/ERC20Pool/invariants/handlers/BaseHandler.sol
@@ -0,0 +1,153 @@
+
+// SPDX-License-Identifier: UNLICENSED
+
+pragma solidity 0.8.14;
+
+import { Strings } from '@openzeppelin/contracts/utils/Strings.sol';
+import '@std/Test.sol';
+import '@std/Vm.sol';
+import "forge-std/console.sol";
+
+import { ERC20Pool } from 'src/ERC20Pool.sol';
+import { ERC20PoolFactory } from 'src/ERC20PoolFactory.sol';
+import { Token } from '../../../utils/Tokens.sol';
+import { PoolInfoUtils } from 'src/PoolInfoUtils.sol';
+import { InvariantTest } from '../InvariantTest.sol';
+
+
+uint256 constant LENDER_MIN_BUCKET_INDEX = 2570;
+uint256 constant LENDER_MAX_BUCKET_INDEX = 2590;
+
+uint256 constant BORROWER_MIN_BUCKET_INDEX = 2600;
+uint256 constant BORROWER_MAX_BUCKET_INDEX = 2620;
+
+contract BaseHandler is InvariantTest, Test {
+
+ // Tokens
+ Token internal _quote;
+ Token internal _collateral;
+
+ // Pool
+ ERC20Pool internal _pool;
+ PoolInfoUtils internal _poolInfo;
+
+ // Modifiers
+ address internal _actor;
+ uint256 internal _lenderBucketIndex;
+ uint256 internal _limitIndex;
+ address[] public actors;
+
+ // Logging
+ mapping(bytes32 => uint256) public numberOfCalls;
+
+ // Lender tracking
+ mapping(address => uint256[]) public touchedBuckets;
+
+ // bucket exchange rate invariant check
+ bool public shouldExchangeRateChange;
+
+ bool public shouldReserveChange;
+
+ // if take is called on auction first time
+ bool public firstTake;
+
+ // mapping borrower address to first take on auction
+ mapping(address => bool) internal isFirstTakeOnAuction;
+
+ // amount of reserve increase after first take
+ uint256 public firstTakeIncreaseInReserve;
+
+ // amount of reserve increase after kicking a loan
+ uint256 public loanKickIncreaseInReserve;
+
+ constructor(address pool, address quote, address collateral, address poolInfo, uint256 numOfActors) {
+ // Tokens
+ _quote = Token(quote);
+ _collateral = Token(collateral);
+
+ // Pool
+ _pool = ERC20Pool(pool);
+ _poolInfo = PoolInfoUtils(poolInfo);
+
+ // Actors
+ actors = _buildActors(numOfActors);
+ }
+
+ /**************************************************************************************************************************************/
+ /*** Helper Functions ***/
+ /**************************************************************************************************************************************/
+
+ modifier useRandomActor(uint256 actorIndex) {
+ // pre condition
+ firstTakeIncreaseInReserve = 0;
+ loanKickIncreaseInReserve = 0;
+
+ vm.stopPrank();
+
+ address actor = actors[constrictToRange(actorIndex, 0, actors.length - 1)];
+ _actor = actor;
+ vm.startPrank(actor);
+ _;
+ vm.stopPrank();
+ }
+
+ modifier useRandomLenderBucket(uint256 bucketIndex) {
+ uint256[] storage lenderBucketIndexes = touchedBuckets[_actor];
+ 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);
+ lenderBucketIndexes.push(_lenderBucketIndex);
+ } else {
+ // if actor has touched more than three buckets, reuse one of the touched buckets
+ _lenderBucketIndex = lenderBucketIndexes[constrictToRange(bucketIndex, 0, lenderBucketIndexes.length - 1)];
+ }
+ _;
+ }
+
+ function _buildActors(uint256 noOfActors_) internal returns(address[] memory) {
+ address[] memory actorsAddress = new address[](noOfActors_);
+ for(uint i = 0; i < noOfActors_; i++) {
+ address actor = makeAddr(string(abi.encodePacked("Actor", Strings.toString(i))));
+ actorsAddress[i] = actor;
+
+ vm.startPrank(actor);
+
+ _quote.mint(actor, 1e45);
+ _quote.approve(address(_pool), 1e45);
+
+ _collateral.mint(actor, 1e45);
+ _collateral.approve(address(_pool), 1e45);
+
+ vm.stopPrank();
+ }
+ return actorsAddress;
+ }
+
+ function getActorsCount() external view returns(uint256) {
+ return actors.length;
+ }
+
+ function constrictToRange(
+ uint256 x,
+ uint256 min,
+ uint256 max
+ ) pure public returns (uint256 result) {
+ require(max >= min, "MAX_LESS_THAN_MIN");
+
+ uint256 size = max - min;
+
+ if (size == 0) return min; // Using max would be equivalent as well.
+ if (max != type(uint256).max) size++; // Make the max inclusive.
+
+ // Ensure max is inclusive in cases where x != 0 and max is at uint max.
+ if (max == type(uint256).max && x != 0) x--; // Accounted for later.
+
+ if (x < min) x += size * (((min - x) / size) + 1);
+
+ result = min + ((x - min) % size);
+
+ // Account for decrementing x to make max inclusive.
+ if (max == type(uint256).max && x != 0) result++;
+ }
+
+}
\ No newline at end of file
diff --git a/tests/forge/ERC20Pool/invariants/handlers/BasicPoolHandler.sol b/tests/forge/ERC20Pool/invariants/handlers/BasicPoolHandler.sol
new file mode 100644
index 000000000..84edad7fb
--- /dev/null
+++ b/tests/forge/ERC20Pool/invariants/handlers/BasicPoolHandler.sol
@@ -0,0 +1,359 @@
+// SPDX-License-Identifier: UNLICENSED
+pragma solidity 0.8.14;
+
+import { Strings } from '@openzeppelin/contracts/utils/Strings.sol';
+import "forge-std/console.sol";
+import '@std/Test.sol';
+import '@std/Vm.sol';
+
+import { ERC20Pool } from 'src/ERC20Pool.sol';
+import { ERC20PoolFactory } from 'src/ERC20PoolFactory.sol';
+import { Token } from '../../../utils/Tokens.sol';
+import { PoolInfoUtils, _collateralization } from 'src/PoolInfoUtils.sol';
+
+import { LENDER_MIN_BUCKET_INDEX, LENDER_MAX_BUCKET_INDEX, BORROWER_MIN_BUCKET_INDEX, BaseHandler } from './BaseHandler.sol';
+
+/**
+ * @dev this contract manages multiple lenders
+ * @dev methods in this contract are called in random order
+ * @dev randomly selects a lender contract to make a txn
+ */
+abstract contract UnboundedBasicPoolHandler is BaseHandler {
+
+ /**************************************************************************************************************************************/
+ /*** Lender Functions ***/
+ /**************************************************************************************************************************************/
+
+ function addQuoteToken(uint256 amount, uint256 bucketIndex) internal {
+ numberOfCalls['UBBasicHandler.addQuoteToken']++;
+
+ shouldExchangeRateChange = false;
+ shouldReserveChange = false;
+
+ // Pre condition
+ (uint256 lpBalanceBefore, ) = _pool.lenderInfo(bucketIndex, _actor);
+
+ _pool.addQuoteToken(amount, bucketIndex, block.timestamp + 1 minutes);
+
+ // Post condition
+ (uint256 lpBalanceAfter, ) = _pool.lenderInfo(bucketIndex, _actor);
+ require(lpBalanceAfter > lpBalanceBefore, "LP balance should increase");
+ }
+
+ function removeQuoteToken(uint256 amount, uint256 bucketIndex) internal {
+ numberOfCalls['UBBasicHandler.removeQuoteToken']++;
+
+ // Pre condition
+ (uint256 lpBalanceBefore, ) = _pool.lenderInfo(bucketIndex, _actor);
+
+ if (lpBalanceBefore == 0) {
+ amount = constrictToRange(amount, 1, 1e36);
+ addQuoteToken(amount, bucketIndex);
+ }
+
+ (lpBalanceBefore, ) = _pool.lenderInfo(bucketIndex, _actor);
+
+ try _pool.removeQuoteToken(amount, bucketIndex) {
+ // Post condition
+ (uint256 lpBalanceAfter, ) = _pool.lenderInfo(bucketIndex, _actor);
+ require(lpBalanceAfter < lpBalanceBefore, "LP balance should decrease");
+ shouldExchangeRateChange = false;
+ shouldReserveChange = false;
+ }
+ catch (bytes memory _err){
+ bytes32 err = keccak256(_err);
+ require(
+ err == keccak256(abi.encodeWithSignature("LUPBelowHTP()")) ||
+ err == keccak256(abi.encodeWithSignature("InsufficientLiquidity()")) ||
+ err == keccak256(abi.encodeWithSignature("RemoveDepositLockedByAuctionDebt()")) ||
+ err == keccak256(abi.encodeWithSignature("NoClaim()")));
+ }
+ }
+
+ function moveQuoteToken(uint256 amount, uint256 fromIndex, uint256 toIndex) internal {
+ if(fromIndex == toIndex) return;
+
+ (uint256 lpBalance, ) = _pool.lenderInfo(fromIndex, _actor);
+
+ if (lpBalance == 0) {
+ addQuoteToken(amount, fromIndex);
+ }
+
+ try _pool.moveQuoteToken(amount, fromIndex, toIndex, block.timestamp + 1 minutes) {
+ shouldExchangeRateChange = false;
+ shouldReserveChange = false;
+ }
+ catch (bytes memory _err){
+ bytes32 err = keccak256(_err);
+ require(
+ err == keccak256(abi.encodeWithSignature("LUPBelowHTP()")) ||
+ err == keccak256(abi.encodeWithSignature("InsufficientLiquidity()")) ||
+ err == keccak256(abi.encodeWithSignature("MoveToSamePrice()")) ||
+ err == keccak256(abi.encodeWithSignature("DustAmountNotExceeded()")) ||
+ err == keccak256(abi.encodeWithSignature("InvalidIndex()")) ||
+ err == keccak256(abi.encodeWithSignature("BucketBankruptcyBlock()"))
+ );
+ }
+ }
+
+ function addCollateral(uint256 amount, uint256 bucketIndex) internal {
+ numberOfCalls['UBBasicHandler.addCollateral']++;
+
+ shouldExchangeRateChange = false;
+ shouldReserveChange = false;
+
+ // Pre condition
+ (uint256 lpBalanceBefore, ) = _pool.lenderInfo(bucketIndex, _actor);
+
+ _pool.addCollateral(amount, bucketIndex, block.timestamp + 1 minutes);
+
+ // Post condition
+ (uint256 lpBalanceAfter, ) = _pool.lenderInfo(bucketIndex, _actor);
+ require(lpBalanceAfter > lpBalanceBefore, "LP balance should increase");
+ }
+
+ function removeCollateral(uint256 amount, uint256 bucketIndex) internal {
+ numberOfCalls['UBBasicHandler.removeCollateral']++;
+
+ shouldExchangeRateChange = false;
+ shouldReserveChange = false;
+
+ // Pre condition
+ (uint256 lpBalanceBefore, ) = _pool.lenderInfo(bucketIndex, _actor);
+
+ if(lpBalanceBefore == 0) {
+ addCollateral(amount, bucketIndex);
+ }
+
+ (lpBalanceBefore, ) = _pool.lenderInfo(bucketIndex, _actor);
+
+ _pool.removeCollateral(amount, bucketIndex);
+
+ // Post condition
+ (uint256 lpBalanceAfter, ) = _pool.lenderInfo(bucketIndex, _actor);
+ require(lpBalanceAfter < lpBalanceBefore, "LP balance should decrease");
+ }
+
+ /**************************/
+ /*** Borrower Functions ***/
+ /**************************/
+
+ function pledgeCollateral(uint256 amount) internal {
+ numberOfCalls['UBBasicHandler.pledgeCollateral']++;
+
+ shouldExchangeRateChange = false;
+ shouldReserveChange = false;
+
+ _pool.drawDebt(_actor, 0, 0, amount);
+ }
+
+ function pullCollateral(uint256 amount) internal {
+ numberOfCalls['UBBasicHandler.pullCollateral']++;
+
+ try _pool.repayDebt(_actor, 0, amount, _actor, 7388) {
+ shouldExchangeRateChange = false;
+ shouldReserveChange = false;
+ } catch (bytes memory _err){
+ bytes32 err = keccak256(_err);
+ require(
+ err == keccak256(abi.encodeWithSignature("InsufficientCollateral()")) ||
+ err == keccak256(abi.encodeWithSignature("AuctionActive()"))
+ );
+ }
+ }
+
+ function drawDebt(uint256 amount) internal {
+ numberOfCalls['UBBasicHandler.drawDebt']++;
+
+ // Pre Condition
+ // 1. borrower's debt should exceed minDebt
+ // 2. pool needs sufficent quote token to draw debt
+ // 3. drawDebt should not make borrower under collateralized
+
+ // 1. borrower's debt should exceed minDebt
+ (uint256 debt, uint256 collateral, ) = _poolInfo.borrowerInfo(address(_pool), _actor);
+ (uint256 minDebt, , , ) = _poolInfo.poolUtilizationInfo(address(_pool));
+ if (amount < minDebt) amount = minDebt + 1;
+
+
+ // TODO: Need to constrain amount so LUP > HTP
+
+
+ // 2. pool needs sufficent quote token to draw debt
+ uint256 poolQuoteBalance = _quote.balanceOf(address(_pool));
+
+ if (amount > poolQuoteBalance) {
+ addQuoteToken(amount * 2, LENDER_MAX_BUCKET_INDEX);
+ }
+
+ // 3. drawing of addition debt will make them under collateralized
+ uint256 lup = _poolInfo.lup(address(_pool));
+ (debt, collateral, ) = _poolInfo.borrowerInfo(address(_pool), _actor);
+
+ if (_collateralization(debt, collateral, lup) < 1) {
+ repayDebt(debt);
+ (debt, collateral, ) = _poolInfo.borrowerInfo(address(_pool), _actor);
+ require(debt == 0, "borrower has debt");
+ }
+
+ (uint256 poolDebt, , ) = _pool.debtInfo();
+
+ // find bucket to borrow quote token
+ uint256 bucket = _pool.depositIndex(amount + poolDebt) - 1;
+
+ uint256 price = _poolInfo.indexToPrice(bucket);
+
+ uint256 collateralToPledge = ((amount * 1e18 + price / 2) / price) * 101 / 100;
+
+ try _pool.drawDebt(_actor, amount, 7388, collateralToPledge) {
+ shouldExchangeRateChange = true;
+ shouldReserveChange = true;
+ }
+ catch (bytes memory _err){
+ bytes32 err = keccak256(_err);
+ require(
+ err == keccak256(abi.encodeWithSignature("BorrowerUnderCollateralized()")) ||
+ err == keccak256(abi.encodeWithSignature("AuctionActive()"))
+ );
+ }
+ }
+
+ function repayDebt(uint256 amountToRepay) internal {
+ numberOfCalls['UBBasicHandler.repayDebt']++;
+
+ // Pre condition
+ (uint256 debt, , ) = PoolInfoUtils(_poolInfo).borrowerInfo(address(_pool), _actor);
+ if (debt == 0) {
+ drawDebt(amountToRepay);
+ }
+
+ try _pool.repayDebt(_actor, amountToRepay, 0, _actor, 7388) {
+ shouldExchangeRateChange = true;
+ shouldReserveChange = true;
+ }
+ catch(bytes memory _err) {
+ bytes32 err = keccak256(_err);
+ require(
+ err == keccak256(abi.encodeWithSignature("NoDebt()")) ||
+ err == keccak256(abi.encodeWithSignature("AmountLTMinDebt()"))
+ );
+ }
+ }
+
+}
+
+
+/**
+ * @dev this contract manages multiple lenders
+ * @dev methods in this contract are called in random order
+ * @dev randomly selects a lender contract to make a txn
+ */
+contract BasicPoolHandler is UnboundedBasicPoolHandler {
+
+ constructor(address pool, address quote, address collateral, address poolInfo, uint256 numOfActors) BaseHandler(pool, quote, collateral, poolInfo, numOfActors) {}
+
+ /**************************/
+ /*** Lender Functions ***/
+ /**************************/
+
+ function addQuoteToken(uint256 actorIndex, uint256 amount, uint256 bucketIndex) public useRandomActor(actorIndex) useRandomLenderBucket(bucketIndex) {
+ numberOfCalls['BBasicHandler.addQuoteToken']++;
+
+ uint256 totalSupply = _quote.totalSupply();
+ uint256 minDeposit = totalSupply == 0 ? 1 : _quote.balanceOf(address(_actor)) / totalSupply + 1;
+ amount = constrictToRange(amount, minDeposit, 1e36);
+
+ // Action
+ super.addQuoteToken(amount, _lenderBucketIndex);
+ }
+
+ function removeQuoteToken(uint256 actorIndex, uint256 amount, uint256 bucketIndex) public useRandomActor(actorIndex) useRandomLenderBucket(bucketIndex) {
+ numberOfCalls['BBasicHandler.removeQuoteToken']++;
+
+ uint256 poolBalance = _quote.balanceOf(address(_pool));
+
+ if (poolBalance < amount) return; // (not enough quote token to withdraw / quote tokens are borrowed)
+
+ // Action
+ super.removeQuoteToken(amount, _lenderBucketIndex);
+ }
+
+ function moveQuoteToken(uint256 actorIndex, uint256 amount, uint256 fromBucketIndex, uint256 toBucketIndex) public useRandomActor(actorIndex) {
+ numberOfCalls['BBasicHandler.moveQuoteToken']++;
+
+ fromBucketIndex = constrictToRange(fromBucketIndex, LENDER_MIN_BUCKET_INDEX, LENDER_MAX_BUCKET_INDEX);
+
+ toBucketIndex = constrictToRange(toBucketIndex, LENDER_MIN_BUCKET_INDEX, LENDER_MAX_BUCKET_INDEX);
+
+ amount = constrictToRange(amount, 1, 1e36);
+
+ super.moveQuoteToken(amount, fromBucketIndex, toBucketIndex);
+ }
+
+ function addCollateral(uint256 actorIndex, uint256 amount, uint256 bucketIndex) public useRandomActor(actorIndex) useRandomLenderBucket(bucketIndex) {
+ numberOfCalls['BBasicHandler.addCollateral']++;
+
+ amount = constrictToRange(amount, 1, 1e36);
+
+ // Action
+ super.addCollateral(amount, _lenderBucketIndex);
+ }
+
+ function removeCollateral(uint256 actorIndex, uint256 amount, uint256 bucketIndex) public useRandomActor(actorIndex) useRandomLenderBucket(bucketIndex) {
+ numberOfCalls['BBasicHandler.removeCollateral']++;
+
+ (uint256 lpBalance, ) = _pool.lenderInfo(_lenderBucketIndex, _actor);
+ ( , uint256 bucketCollateral, , , ) = _pool.bucketInfo(_lenderBucketIndex);
+
+ if (lpBalance == 0 || bucketCollateral == 0) return; // no value in bucket
+
+ amount = constrictToRange(amount, 1, 1e36);
+
+ // Action
+ super.removeCollateral(amount, _lenderBucketIndex);
+ }
+
+
+ /**************************/
+ /*** Borrower Functions ***/
+ /**************************/
+
+ function pledgeCollateral(uint256 actorIndex, uint256 amountToPledge) public useRandomActor(actorIndex) {
+ numberOfCalls['BBasicHandler.pledgeCollateral']++;
+
+ amountToPledge = constrictToRange(amountToPledge, 1, 1e36);
+
+ // Action
+ super.pledgeCollateral(amountToPledge);
+ }
+
+ function pullCollateral(uint256 actorIndex, uint256 amountToPull) public useRandomActor(actorIndex) {
+ numberOfCalls['BBasicHandler.pullCollateral']++;
+
+ amountToPull = constrictToRange(amountToPull, 1, 1e36);
+
+ // Action
+ super.pullCollateral(amountToPull);
+ }
+
+ function drawDebt(uint256 actorIndex, uint256 amountToBorrow) public useRandomActor(actorIndex) {
+ numberOfCalls['BBasicHandler.drawDebt']++;
+
+ amountToBorrow = constrictToRange(amountToBorrow, 1, 1e36);
+
+ // Action
+ super.drawDebt(amountToBorrow);
+
+ // skip time to make borrower undercollateralized
+ vm.warp(block.timestamp + 200 days);
+ }
+
+ function repayDebt(uint256 actorIndex, uint256 amountToRepay) public useRandomActor(actorIndex) {
+ numberOfCalls['BBasicHandler.repayDebt']++;
+
+ amountToRepay = constrictToRange(amountToRepay, 1, 1e36);
+
+ // Action
+ super.repayDebt(amountToRepay);
+ }
+}
diff --git a/tests/forge/ERC20Pool/invariants/handlers/IBaseHandler.sol b/tests/forge/ERC20Pool/invariants/handlers/IBaseHandler.sol
new file mode 100644
index 000000000..092c21f7e
--- /dev/null
+++ b/tests/forge/ERC20Pool/invariants/handlers/IBaseHandler.sol
@@ -0,0 +1,22 @@
+// SPDX-License-Identifier: UNLICENSED
+
+pragma solidity 0.8.14;
+
+interface IBaseHandler {
+
+ function getActorsCount() external view returns(uint256);
+
+ function actors(uint256) external view returns(address);
+
+ function numberOfCalls(bytes32) external view returns(uint256);
+
+ function shouldExchangeRateChange() external view returns(bool);
+
+ function shouldReserveChange() external view returns(bool);
+
+ function firstTake() external view returns(bool);
+
+ function firstTakeIncreaseInReserve() external view returns(uint256);
+
+ function loanKickIncreaseInReserve() external view returns(uint256);
+}
\ No newline at end of file
diff --git a/tests/forge/ERC20Pool/invariants/handlers/LiquidationPoolHandler.sol b/tests/forge/ERC20Pool/invariants/handlers/LiquidationPoolHandler.sol
new file mode 100644
index 000000000..3c3bdc319
--- /dev/null
+++ b/tests/forge/ERC20Pool/invariants/handlers/LiquidationPoolHandler.sol
@@ -0,0 +1,148 @@
+
+// SPDX-License-Identifier: UNLICENSED
+pragma solidity 0.8.14;
+
+import '@std/Vm.sol';
+
+import { BasicPoolHandler } from './BasicPoolHandler.sol';
+import { LENDER_MIN_BUCKET_INDEX, LENDER_MAX_BUCKET_INDEX, BaseHandler } from './BaseHandler.sol';
+import { Maths } from 'src/libraries/internal/Maths.sol';
+
+abstract contract UnBoundedLiquidationPoolHandler is BaseHandler {
+ function kickAuction(address borrower) internal {
+ numberOfCalls['UBLiquidationHandler.kickAuction']++;
+
+ (uint256 borrowerDebt, , ) = _poolInfo.borrowerInfo(address(_pool), borrower);
+
+ try _pool.kick(borrower) {
+ shouldExchangeRateChange = true;
+ shouldReserveChange = true;
+ loanKickIncreaseInReserve = Maths.wmul(borrowerDebt, 0.25 * 1e18);
+ }
+ catch {
+ }
+ }
+
+ function takeAuction(address borrower, uint256 amount, address taker) internal {
+ numberOfCalls['UBLiquidationHandler.takeAuction']++;
+
+ (uint256 borrowerDebt, , ) = _poolInfo.borrowerInfo(address(_pool), borrower);
+
+ try _pool.take(borrower, amount, taker, bytes("")) {
+ shouldExchangeRateChange = true;
+ shouldReserveChange = true;
+
+ if(!isFirstTakeOnAuction[borrower]) {
+ firstTakeIncreaseInReserve = Maths.wmul(borrowerDebt, 0.07 * 1e18);
+ firstTake = true;
+ isFirstTakeOnAuction[borrower] = true;
+ }
+ else {
+ isFirstTakeOnAuction[borrower] = false;
+ firstTake = false;
+ }
+ }
+ catch {
+ }
+ }
+
+ function bucketTake(address borrower, bool depositTake, uint256 bucketIndex) internal {
+ numberOfCalls['UBLiquidationHandler.bucketTake']++;
+
+ (uint256 borrowerDebt, , ) = _poolInfo.borrowerInfo(address(_pool), borrower);
+
+ try _pool.bucketTake(borrower, depositTake, bucketIndex) {
+ shouldExchangeRateChange = true;
+ shouldReserveChange = true;
+
+ if(!firstTake) {
+ firstTakeIncreaseInReserve = Maths.wmul(borrowerDebt, 0.07 * 1e18);
+ firstTake = true;
+ }
+ else {
+ firstTake = false;
+ }
+ }
+ catch {
+ }
+ }
+}
+
+contract LiquidationPoolHandler is UnBoundedLiquidationPoolHandler, BasicPoolHandler {
+
+ constructor(address pool, address quote, address collateral, address poolInfo, uint256 numOfActors) BasicPoolHandler(pool, quote, collateral, poolInfo, numOfActors) {}
+
+ function _kickAuction(uint256 borrowerIndex, uint256 amount, uint256 kickerIndex) internal useRandomActor(kickerIndex) {
+ numberOfCalls['BLiquidationHandler.kickAuction']++;
+
+ shouldExchangeRateChange = true;
+
+ borrowerIndex = constrictToRange(borrowerIndex, 0, actors.length - 1);
+ address borrower = actors[borrowerIndex];
+ address kicker = _actor;
+ amount = constrictToRange(amount, 1, 1e36);
+
+ ( , , , uint256 kickTime, , , , , ) = _pool.auctionInfo(borrower);
+
+ if (kickTime == 0) {
+ (uint256 debt, , ) = _pool.borrowerInfo(borrower);
+ if (debt == 0) {
+ changePrank(borrower);
+ _actor = borrower;
+ super.drawDebt(amount);
+ }
+ changePrank(kicker);
+ _actor = kicker;
+ super.kickAuction(borrower);
+ }
+
+ // skip some time for more interest
+ vm.warp(block.timestamp + 2 hours);
+ }
+
+ function kickAuction(uint256 borrowerIndex, uint256 amount, uint256 kickerIndex) external {
+ _kickAuction(borrowerIndex, amount, kickerIndex);
+ }
+
+ function takeAuction(uint256 borrowerIndex, uint256 amount, uint256 actorIndex) external useRandomActor(actorIndex){
+ numberOfCalls['BLiquidationHandler.takeAuction']++;
+
+ amount = constrictToRange(amount, 1, 1e36);
+
+ shouldExchangeRateChange = true;
+
+ borrowerIndex = constrictToRange(borrowerIndex, 0, actors.length - 1);
+
+ address borrower = actors[borrowerIndex];
+ address taker = _actor;
+
+ ( , , , uint256 kickTime, , , , , ) = _pool.auctionInfo(borrower);
+
+ if (kickTime == 0) {
+ _kickAuction(borrowerIndex, amount * 100, actorIndex);
+ }
+ changePrank(taker);
+ super.takeAuction(borrower, amount, taker);
+ }
+
+ function bucketTake(uint256 borrowerIndex, uint256 bucketIndex, bool depositTake, uint256 takerIndex) external useRandomActor(takerIndex) {
+ numberOfCalls['BLiquidationHandler.bucketTake']++;
+
+ shouldExchangeRateChange = true;
+
+ borrowerIndex = constrictToRange(borrowerIndex, 0, actors.length - 1);
+
+ bucketIndex = constrictToRange(bucketIndex, LENDER_MIN_BUCKET_INDEX, LENDER_MAX_BUCKET_INDEX);
+
+ address borrower = actors[borrowerIndex];
+ address taker = _actor;
+
+ ( , , , uint256 kickTime, , , , , ) = _pool.auctionInfo(borrower);
+
+ if (kickTime == 0) {
+ _kickAuction(borrowerIndex, 1e24, bucketIndex);
+ }
+ changePrank(taker);
+ super.bucketTake(borrower, depositTake, bucketIndex);
+ }
+}
\ No newline at end of file
diff --git a/tests/forge/ERC20Pool/invariants/handlers/ReservePoolHandler.sol b/tests/forge/ERC20Pool/invariants/handlers/ReservePoolHandler.sol
new file mode 100644
index 000000000..1e592a2e3
--- /dev/null
+++ b/tests/forge/ERC20Pool/invariants/handlers/ReservePoolHandler.sol
@@ -0,0 +1,48 @@
+
+// SPDX-License-Identifier: UNLICENSED
+pragma solidity 0.8.14;
+
+import '@std/Vm.sol';
+
+import { LiquidationPoolHandler } from './LiquidationPoolHandler.sol';
+import { LENDER_MIN_BUCKET_INDEX, LENDER_MAX_BUCKET_INDEX, BaseHandler } from './BaseHandler.sol';
+import { Auctions } from 'src/libraries/external/Auctions.sol';
+
+abstract contract UnBoundedReservePoolHandler is BaseHandler {
+ function startClaimableReserveAuction() internal {
+ (, uint256 claimableReserves, , , ) = _poolInfo.poolReservesInfo(address(_pool));
+ if(claimableReserves == 0) return;
+ try _pool.startClaimableReserveAuction(){
+ shouldReserveChange = true;
+ } catch {
+ }
+ }
+
+ function takeReserves(uint256 amount) internal {
+ try _pool.takeReserves(amount){
+ shouldReserveChange = true;
+ } catch {
+ }
+ }
+}
+
+contract ReservePoolHandler is UnBoundedReservePoolHandler, LiquidationPoolHandler {
+
+ constructor(address pool, address quote, address collateral, address poolInfo, uint256 numOfActors) LiquidationPoolHandler(pool, quote, collateral, poolInfo, numOfActors) {}
+
+ function startClaimableReserveAuction(uint256 actorIndex) external useRandomActor(actorIndex) {
+ super.startClaimableReserveAuction();
+ }
+
+ function takeReserves(uint256 actorIndex, uint256 amount) external useRandomActor(actorIndex) {
+ (, , uint256 claimableReservesRemaining, , ) = _poolInfo.poolReservesInfo(address(_pool));
+
+ if(claimableReservesRemaining == 0) {
+ super.startClaimableReserveAuction();
+ }
+ (, , claimableReservesRemaining, , ) = _poolInfo.poolReservesInfo(address(_pool));
+
+ amount = constrictToRange(amount, 0, claimableReservesRemaining);
+ super.takeReserves(amount);
+ }
+}
\ No newline at end of file
diff --git a/tests/forge/ERC20Pool/regression/RegressionTestBasic.t.sol b/tests/forge/ERC20Pool/regression/RegressionTestBasic.t.sol
new file mode 100644
index 000000000..57e0a9819
--- /dev/null
+++ b/tests/forge/ERC20Pool/regression/RegressionTestBasic.t.sol
@@ -0,0 +1,121 @@
+// SPDX-License-Identifier: UNLICENSED
+
+pragma solidity 0.8.14;
+
+import { BasicInvariants } from "../invariants/BasicInvariants.t.sol";
+
+import '@std/console.sol';
+
+contract RegressionTestBasic is BasicInvariants {
+
+ function setUp() public override {
+ super.setUp();
+ }
+
+ function test_regression_invariantUnderflow_1() external {
+ _basicPoolHandler.addQuoteToken(14227, 5211, 3600000000000000000000);
+ // check invariants hold true
+ invariant_Lps_B1();
+ invariant_quoteTokenBalance_QT1();
+ }
+
+ function test_exchange_rate_bug_simulation() external {
+ // Action sequence
+ // 1. addQuoteToken(6879, 2570)
+ // 2. addCollateral(3642907759282013932739218713, 2570)
+ // 3. removeCollateral(296695924278944779257290397234298756, 2570)
+
+ uint256 previousExchangeRate = 1e18;
+ _basicPoolHandler.addQuoteToken(999999999844396154169639088436193915956854451, 6879, 2809);
+ ( , uint256 quote, uint256 collateral, uint256 lps, , uint256 exchangeRate) = _poolInfo.bucketInfo(address(_pool), 2570);
+ console.log("After addQuoteToken(6879, 2570)");
+ console.log("============");
+ console.log("Quote Tokens -->", quote);
+ console.log("Collateral Tokens -->", collateral);
+ console.log("Lps -->", lps);
+ console.log("Exchange Rate-->", exchangeRate);
+ console.log("============");
+ require(previousExchangeRate == exchangeRate, "Incorrect exchange rate");
+ previousExchangeRate = exchangeRate;
+ _basicPoolHandler.addCollateral(2, 36429077592820139327392187131, 202214962129783771592);
+ ( , quote, collateral, lps, , exchangeRate) = _poolInfo.bucketInfo(address(_pool), 2570);
+ console.log("After addCollateral(3642907759282013932739218713, 2570)");
+ console.log("============");
+ console.log("Quote Tokens -->", quote);
+ console.log("Collateral Tokens -->", collateral);
+ console.log("Lps -->", lps);
+ console.log("Exchange Rate-->", exchangeRate);
+ console.log("============");
+ require(previousExchangeRate == exchangeRate, "Incorrect exchange rate");
+ previousExchangeRate = exchangeRate;
+ _basicPoolHandler.removeCollateral(1, 2296695924278944779257290397234298756, 10180568736759156593834642286260647915348262280903719122483474452532722106636);
+ ( , quote, collateral, lps, , exchangeRate) = _poolInfo.bucketInfo(address(_pool), 2570);
+ console.log("After removeCollateral(296695924278944779257290397234298756, 2570)");
+ console.log("============");
+ console.log("Quote Tokens -->", quote);
+ console.log("Collateral Tokens -->", collateral);
+ console.log("Lps -->", lps);
+ console.log("Exchange Rate-->", exchangeRate);
+ console.log("============");
+ require(previousExchangeRate == exchangeRate, "Incorrect exchange rate");
+ }
+
+ function test_exchange_rate_bug2() external {
+ uint256 previousExchangeRate = 1e18;
+ _basicPoolHandler.addQuoteToken(211670885988646987334214990781526025942, 115792089237316195423570985008687907853269984665640564039457584007913129639934, 6894274025938223490357894120267612065037086600750070030707794233);
+
+ ( , uint256 quote, uint256 collateral, uint256 lps, , uint256 exchangeRate) = _poolInfo.bucketInfo(address(_pool), 2570);
+ console.log("After addQuoteToken(211670885988646987334214990781526025942, 115792089237316195423570985008687907853269984665640564039457584007913129639934, 6894274025938223490357894120267612065037086600750070030707794233)");
+ console.log("============");
+ console.log("Quote Tokens -->", quote);
+ console.log("Collateral Tokens -->", collateral);
+ console.log("Lps -->", lps);
+ console.log("Exchange Rate-->", exchangeRate);
+ console.log("============");
+ require(previousExchangeRate == exchangeRate, "Incorrect exchange rate");
+ previousExchangeRate = exchangeRate;
+ _basicPoolHandler.addCollateral(117281, 115792089237316195423570985008687907853269984665640564039457584007913129639935, 2);
+
+ ( , quote, collateral, lps, , exchangeRate) = _poolInfo.bucketInfo(address(_pool), 2570);
+ console.log("After addCollateral(117281, 115792089237316195423570985008687907853269984665640564039457584007913129639935, 2)");
+ console.log("============");
+ console.log("Quote Tokens -->", quote);
+ console.log("Collateral Tokens -->", collateral);
+ console.log("Lps -->", lps);
+ console.log("Exchange Rate-->", exchangeRate);
+ console.log("============");
+ require(previousExchangeRate == exchangeRate, "Incorrect exchange rate");
+ previousExchangeRate = exchangeRate;
+
+ _basicPoolHandler.removeCollateral(115792089237316195423570985008687907853269984665640564039457584007913129639932, 12612911637698029036253737442696522, 115792089237316195423570985008687907853269984665640564039457584007913129639933);
+
+ ( , quote, collateral, lps, , exchangeRate) = _poolInfo.bucketInfo(address(_pool), 2570);
+ console.log("After removeCollateral(115792089237316195423570985008687907853269984665640564039457584007913129639932, 12612911637698029036253737442696522, 115792089237316195423570985008687907853269984665640564039457584007913129639933)");
+ console.log("============");
+ console.log("Quote Tokens -->", quote);
+ console.log("Collateral Tokens -->", collateral);
+ console.log("Lps -->", lps);
+ console.log("Exchange Rate-->", exchangeRate);
+ console.log("============");
+ // require(previousExchangeRate == exchangeRate, "Incorrect exchange rate");
+ previousExchangeRate = exchangeRate;
+
+ _basicPoolHandler.removeCollateral(1, 1e36, 2570);
+ _basicPoolHandler.removeQuoteToken(1, 1e36, 2570);
+
+ _basicPoolHandler.removeCollateral(2, 1e36, 2570);
+ _basicPoolHandler.removeQuoteToken(2, 1e36, 2570);
+
+ ( , quote, collateral, lps, , exchangeRate) = _poolInfo.bucketInfo(address(_pool), 2570);
+ console.log("After removeCollateral(115792089237316195423570985008687907853269984665640564039457584007913129639932, 12612911637698029036253737442696522, 115792089237316195423570985008687907853269984665640564039457584007913129639933)");
+ console.log("============");
+ console.log("Quote Tokens -->", quote);
+ console.log("Collateral Tokens -->", collateral);
+ console.log("Lps -->", lps);
+ console.log("Exchange Rate-->", exchangeRate);
+ console.log("============");
+ require(previousExchangeRate == exchangeRate, "Incorrect exchange rate");
+
+ }
+
+}
\ No newline at end of file
diff --git a/tests/forge/ERC20Pool/regression/RegressionTestLiquidation.t.sol b/tests/forge/ERC20Pool/regression/RegressionTestLiquidation.t.sol
new file mode 100644
index 000000000..29c6b6076
--- /dev/null
+++ b/tests/forge/ERC20Pool/regression/RegressionTestLiquidation.t.sol
@@ -0,0 +1,70 @@
+// SPDX-License-Identifier: UNLICENSED
+
+pragma solidity 0.8.14;
+
+import { LiquidationInvariant } from "../invariants/LiquidationInvariant.t.sol";
+
+import '@std/console.sol';
+
+contract RegressionTestLiquidation is LiquidationInvariant {
+
+ function setUp() public override {
+ super.setUp();
+
+ }
+
+ function test_regression_quote_token() external {
+ _liquidationPoolHandler.addQuoteToken(115792089237316195423570985008687907853269984665640564039457584007913129639932, 3, 115792089237316195423570985008687907853269984665640564039457584007913129639932);
+
+ // check invariants hold true
+ invariant_quoteTokenBalance_QT1();
+ }
+
+ function test_arithmetic_overflow() external {
+ _liquidationPoolHandler.kickAuction(128942392769655840156268259377571235707684499808935108685525899532745, 9654010200996517229486923829624352823010316518405842367464881, 135622574118732106350824249104903);
+ _liquidationPoolHandler.addQuoteToken(3487, 871, 1654);
+
+ // check invariants hold true
+ invariant_quoteTokenBalance_QT1();
+ }
+
+ function test_bucket_take_lps_bug() public {
+ _liquidationPoolHandler.removeQuoteToken(7033457611004217223271238592369692530886316746601644, 0, 115792089237316195423570985008687907853269984665640564039457584007913129639932);
+ _liquidationPoolHandler.addQuoteToken(1, 20033186019073, 1);
+ _liquidationPoolHandler.bucketTake(0, 0, false, 2876997751);
+
+ invariant_Lps_B1();
+ }
+
+ function test_interest_rate_bug() public {
+ _liquidationPoolHandler.bucketTake(18065045387666484532028539614323078235438354477798625297386607289, 14629545458306, true, 1738460279262663206365845078188769);
+
+ invariant_interest_rate_I1();
+ }
+
+ function test_incorrect_no_of_borrowers() public {
+ _liquidationPoolHandler.moveQuoteToken(18178450611611937161732340858718395124120481640398450530303803, 0, 93537843531612826457318744802930982491, 15596313608676556633725998020226886686244513);
+ _liquidationPoolHandler.addCollateral(2208149704044082902772911545020934265, 340235628931125711729099234105522626267587665393753030264689924088, 2997844437211835697043096396926932785920355866486893005710984415271);
+ _liquidationPoolHandler.moveQuoteToken(56944009718062971164908977784993293, 737882204379007468599822110965749781465, 1488100463155679769353095066686506252, 11960033727528802202227468733333727294);
+ _liquidationPoolHandler.moveQuoteToken(47205392335275917691737183012282140599753693978176314740917, 2, 115792089237316195423570985008687907853269984665640564039457584007913129639934, 164043848691337333691028718232);
+ _liquidationPoolHandler.kickAuction(184206711567329609153924955630229148705869686378631519380021040314, 78351, 115792089237316195423570985008687907853269984665640564039457584007913129639933);
+ _liquidationPoolHandler.kickAuction(3, 199726916764352560035199423206927461876998880387108455962754538835220966553, 3);
+ _liquidationPoolHandler.removeQuoteToken(999999991828440064944955196599190431639924811, 2781559202773230142346489450532860130, 3000000005240421579956496007310960085855569344);
+ _liquidationPoolHandler.pullCollateral(48768502867710912107594904694036421700, 275047566877984818806178837359260100);
+ _liquidationPoolHandler.bucketTake(2, 115792089237316195423570985008687907853269984665640564039457584007913129639934, false, 8154570107391684241724530527782571978369827827856399749867491880);
+ _liquidationPoolHandler.removeCollateral(43733538637150108518954934566131291302796656384802361118757432084573, 1, 115792089237316195423570985008687907853269984665640564039457584007913129639934);
+ _liquidationPoolHandler.addQuoteToken(1, 2, 2);
+ _liquidationPoolHandler.repayDebt(647805461526201272, 0);
+ _liquidationPoolHandler.kickAuction(1019259585194528028904148545812353964867041444572537077023497678982801, 58796345025472936970320, 131319002678489819637546489086162345032717166507611595521);
+ _liquidationPoolHandler.moveQuoteToken(2, 2, 0, 115792089237316195423570985008687907853269984665640564039457584007913129639935);
+ _liquidationPoolHandler.moveQuoteToken(6164937621056362865643346803975636714, 4, 115792089237316195423570985008687907853269984665640564039457584007913129639934, 315548939052682258);
+ _liquidationPoolHandler.repayDebt(2987067394366841692658, 170206016570563384086766968869520628);
+ _liquidationPoolHandler.pledgeCollateral(3558446182295495994762049031, 0);
+ _liquidationPoolHandler.drawDebt(4525700839008283200312069904720925039, 3000000000753374912785563581177665475703155339);
+ _liquidationPoolHandler.kickAuction(1, 3559779948348618822016735773117619950447774, 218801416747720);
+ _liquidationPoolHandler.addQuoteToken(1469716416900282992357252011629715552, 13037214114647887147246343731476169800, 984665637618013480616943810604306792);
+ _liquidationPoolHandler.pullCollateral(438961419917818200942534689247815826455600131, 64633474453314038763068322072915580384442279897841981);
+
+ invariant_auctions_A3_A4();
+ }
+}
\ No newline at end of file
diff --git a/tests/forge/ERC20Pool/regression/RegressionTestReserves.t.sol b/tests/forge/ERC20Pool/regression/RegressionTestReserves.t.sol
new file mode 100644
index 000000000..53ed71d77
--- /dev/null
+++ b/tests/forge/ERC20Pool/regression/RegressionTestReserves.t.sol
@@ -0,0 +1,50 @@
+// SPDX-License-Identifier: UNLICENSED
+
+pragma solidity 0.8.14;
+
+import { ReserveInvariants } from "../invariants/ReserveInvariants.t.sol";
+
+import '@std/console.sol';
+
+contract RegressionTestReserve is ReserveInvariants {
+
+ function setUp() public override {
+ super.setUp();
+ }
+
+ function _test_reserve_1() external {
+ (uint256 reserve, , , , ) = _poolInfo.poolReservesInfo(address(_pool));
+ console.log("Initial Reserve -->", reserve);
+
+ _reservePoolHandler.kickAuction(3833, 15167, 15812);
+
+ (reserve, , , , ) = _poolInfo.poolReservesInfo(address(_pool));
+ console.log("Reserve after kick --->", reserve);
+ _invariant_reserves_RE1_RE2_RE3_RE4_RE5_RE6_RE7_RE8_RE9();
+
+
+ _reservePoolHandler.removeQuoteToken(3841, 5339, 3672);
+
+ (reserve, , , , ) = _poolInfo.poolReservesInfo(address(_pool));
+ console.log("Reserve after removeQuoteToken --->", reserve);
+ _invariant_reserves_RE1_RE2_RE3_RE4_RE5_RE6_RE7_RE8_RE9();
+ }
+
+ function _test_reserve_2() external {
+ (uint256 reserve, , , , ) = _poolInfo.poolReservesInfo(address(_pool));
+ console.log("Initial Reserve -->", reserve);
+
+ _reservePoolHandler.bucketTake(19730, 10740, false, 15745);
+
+ (reserve, , , , ) = _poolInfo.poolReservesInfo(address(_pool));
+ console.log("Reserve after bucketTake --->", reserve);
+ _invariant_reserves_RE1_RE2_RE3_RE4_RE5_RE6_RE7_RE8_RE9();
+
+
+ _reservePoolHandler.addCollateral(14982, 18415, 2079);
+
+ (reserve, , , , ) = _poolInfo.poolReservesInfo(address(_pool));
+ console.log("Reserve after addCollateral --->", reserve);
+ _invariant_reserves_RE1_RE2_RE3_RE4_RE5_RE6_RE7_RE8_RE9();
+ }
+}
\ No newline at end of file
diff --git a/tests/forge/ERC721Pool/ERC721DSTestPlus.sol b/tests/forge/ERC721Pool/ERC721DSTestPlus.sol
index 6d8bc97a8..964941451 100644
--- a/tests/forge/ERC721Pool/ERC721DSTestPlus.sol
+++ b/tests/forge/ERC721Pool/ERC721DSTestPlus.sol
@@ -5,8 +5,8 @@ import { ERC20 } from '@openzeppelin/contracts/token/ERC20/ERC20.sol';
import '@openzeppelin/contracts/token/ERC721/ERC721.sol';
import '@openzeppelin/contracts/utils/structs/EnumerableSet.sol';
-import { DSTestPlus } from '../utils/DSTestPlus.sol';
-import { NFTCollateralToken, Token } from '../utils/Tokens.sol';
+import { DSTestPlus } from '../utils/DSTestPlus.sol';
+import { NFTCollateralToken, Token, TokenWithNDecimals } from '../utils/Tokens.sol';
import { ERC721Pool } from 'src/ERC721Pool.sol';
import { ERC721PoolFactory } from 'src/ERC721PoolFactory.sol';
@@ -25,7 +25,7 @@ abstract contract ERC721DSTestPlus is DSTestPlus, IERC721PoolEvents {
using EnumerableSet for EnumerableSet.UintSet;
NFTCollateralToken internal _collateral;
- Token internal _quote;
+ TokenWithNDecimals internal _quote;
ERC20 internal _ajnaToken;
mapping(uint256 => uint256) NFTidToIndex;
@@ -100,13 +100,12 @@ abstract contract ERC721DSTestPlus is DSTestPlus, IERC721PoolEvents {
{
uint256 fractionOfNftRemaining = lpsAsCollateral % 1e18;
assertLt(fractionOfNftRemaining, 1e18);
-
- // 1 wei workaround due to rounding
- depositRequired = Maths.wmul(1e18 - fractionOfNftRemaining + 1, price);
+
+ depositRequired = Maths.wmul(1e18 - fractionOfNftRemaining, price);
}
deal(_pool.quoteTokenAddress(), lender, depositRequired);
Token(_pool.quoteTokenAddress()).approve(address(_pool) , depositRequired);
- _pool.addQuoteToken(depositRequired, bucketIndex);
+ _pool.addQuoteToken(depositRequired, bucketIndex, block.timestamp + 1 minutes);
(lenderLpBalance, ) = _pool.lenderInfo(bucketIndex, lender);
lpsAsCollateral = _poolUtils.lpsToCollateral(address(_pool), lenderLpBalance, bucketIndex);
}
@@ -175,7 +174,7 @@ abstract contract ERC721DSTestPlus is DSTestPlus, IERC721PoolEvents {
emit Transfer(from, address(_pool), tokenIds[i]);
}
- lps_ = ERC721Pool(address(_pool)).addCollateral(tokenIds, index);
+ lps_ = ERC721Pool(address(_pool)).addCollateral(tokenIds, index, block.timestamp + 10 minutes);
for (uint256 i = 0; i < tokenIds.length; i++) {
assertEq(_collateral.ownerOf(tokenIds[i]), address(_pool)); // token is owned by pool after add
@@ -336,7 +335,7 @@ abstract contract ERC721DSTestPlus is DSTestPlus, IERC721PoolEvents {
emit RepayDebt(borrower, amountRepaid, collateralToPull, newLup);
}
- ERC721Pool(address(_pool)).repayDebt(borrower, amountToRepay, collateralToPull);
+ ERC721Pool(address(_pool)).repayDebt(borrower, amountToRepay, collateralToPull, borrower, MAX_FENWICK_INDEX);
// post pull checks
if (collateralToPull != 0) {
@@ -352,7 +351,7 @@ abstract contract ERC721DSTestPlus is DSTestPlus, IERC721PoolEvents {
emit RepayDebt(borrower, amountRepaid, collateralToPull, newLup);
}
- ERC721Pool(address(_pool)).repayDebt(borrower, amountToRepay, collateralToPull);
+ ERC721Pool(address(_pool)).repayDebt(borrower, amountToRepay, collateralToPull, borrower, MAX_FENWICK_INDEX);
}
}
@@ -430,10 +429,42 @@ abstract contract ERC721DSTestPlus is DSTestPlus, IERC721PoolEvents {
}
}
+ function _assertCollateralInvariants() internal {
+ uint256 collateralInBuckets;
+ for(uint256 bucketIndex = 0; bucketIndex <= 7388; bucketIndex++) {
+ (, uint256 bucketCollateral, , , ) = _pool.bucketInfo(bucketIndex);
+ collateralInBuckets += bucketCollateral;
+ }
+
+ uint256 borrowersCollateral;
+ for (uint256 i = 0; i < borrowers.length(); i++) {
+ (, uint256 borrowerCollateral, ) = _poolUtils.borrowerInfo(address(_pool), borrowers.at(i));
+ borrowersCollateral += borrowerCollateral;
+ }
+
+ // pool pledged collateral accumulator should be equal with the amounts of collateral owned by borrowers
+ assertEq(borrowersCollateral, _pool.pledgedCollateral());
+
+ // collateral in buckets + collateral owned borrowers should be equal with the total number of tokens owned by the pool
+ uint256 poolBalance = _collateral.balanceOf(address(_pool));
+ assertEq(collateralInBuckets + borrowersCollateral, poolBalance * 1e18);
+ }
+
/**********************/
/*** Revert asserts ***/
/**********************/
+ function _assertAddCollateralExpiredRevert(
+ address from,
+ uint256[] memory tokenIds,
+ uint256 index,
+ uint256 expiry
+ ) internal {
+ changePrank(from);
+ vm.expectRevert(IPoolErrors.TransactionExpired.selector);
+ ERC721Pool(address(_pool)).addCollateral(tokenIds, index, expiry);
+ }
+
function _assertDeployWith0xAddressRevert(
address poolFactory,
address collateral,
@@ -504,11 +535,22 @@ abstract contract ERC721DSTestPlus is DSTestPlus, IERC721PoolEvents {
uint256 indexLimit
) internal override {
changePrank(from);
- vm.expectRevert(IPoolErrors.LimitIndexReached.selector);
+ vm.expectRevert(IPoolErrors.LimitIndexExceeded.selector);
uint256[] memory emptyArray;
ERC721Pool(address(_pool)).drawDebt(from, amount, indexLimit, emptyArray);
}
+ function _assertMergeRemoveCollateralAuctionNotClearedRevert(
+ address from,
+ uint256 toIndex,
+ uint256 noOfNFTsToRemove,
+ uint256[] memory removeCollateralAtIndex
+ ) internal {
+ changePrank(from);
+ vm.expectRevert(abi.encodeWithSignature('AuctionNotCleared()'));
+ ERC721Pool(address(_pool)).mergeOrRemoveCollateral(removeCollateralAtIndex, noOfNFTsToRemove, toIndex);
+ }
+
function _assertCannotMergeToHigherPriceRevert(
address from,
uint256 toIndex,
@@ -531,6 +573,17 @@ abstract contract ERC721DSTestPlus is DSTestPlus, IERC721PoolEvents {
ERC721Pool(address(_pool)).drawDebt(from, amount, indexLimit, emptyArray);
}
+ function _assertBorrowDustRevert(
+ address from,
+ uint256 amount,
+ uint256 indexLimit
+ ) internal {
+ changePrank(from);
+ vm.expectRevert(IPoolErrors.DustAmountNotExceeded.selector);
+ uint256[] memory emptyArray;
+ ERC721Pool(address(_pool)).drawDebt(from, amount, indexLimit, emptyArray);
+ }
+
function _assertBorrowMinDebtRevert(
address from,
uint256 amount,
@@ -548,7 +601,17 @@ abstract contract ERC721DSTestPlus is DSTestPlus, IERC721PoolEvents {
) internal {
changePrank(from);
vm.expectRevert(IPoolErrors.InsufficientCollateral.selector);
- ERC721Pool(address(_pool)).repayDebt(from, 0, amount);
+ ERC721Pool(address(_pool)).repayDebt(from, 0, amount, from, MAX_FENWICK_INDEX);
+ }
+
+ function _assertPullLimitIndexRevert(
+ address from,
+ uint256 amount,
+ uint256 indexLimit
+ ) internal {
+ changePrank(from);
+ vm.expectRevert(IPoolErrors.LimitIndexExceeded.selector);
+ ERC721Pool(address(_pool)).repayDebt(from, 0, amount, from, indexLimit);
}
function _assertRepayNoDebtRevert(
@@ -558,7 +621,7 @@ abstract contract ERC721DSTestPlus is DSTestPlus, IERC721PoolEvents {
) internal {
changePrank(from);
vm.expectRevert(IPoolErrors.NoDebt.selector);
- ERC721Pool(address(_pool)).repayDebt(borrower, amount, 0);
+ ERC721Pool(address(_pool)).repayDebt(borrower, amount, 0, borrower, MAX_FENWICK_INDEX);
}
function _assertRepayMinDebtRevert(
@@ -568,7 +631,7 @@ abstract contract ERC721DSTestPlus is DSTestPlus, IERC721PoolEvents {
) internal {
changePrank(from);
vm.expectRevert(IPoolErrors.AmountLTMinDebt.selector);
- ERC721Pool(address(_pool)).repayDebt(borrower, amount, 0);
+ ERC721Pool(address(_pool)).repayDebt(borrower, amount, 0, borrower, MAX_FENWICK_INDEX);
}
function _assertRemoveCollateralNoClaimRevert(
@@ -603,7 +666,7 @@ abstract contract ERC721HelperContract is ERC721DSTestPlus {
_collateral = new NFTCollateralToken();
vm.makePersistent(address(_collateral));
- _quote = new Token("Quote", "Q");
+ _quote = new TokenWithNDecimals("Quote", "Q", 18);
vm.makePersistent(address(_quote));
_ajnaToken = ERC20(_ajna);
vm.makePersistent(_ajna);
@@ -670,6 +733,65 @@ abstract contract ERC721HelperContract is ERC721DSTestPlus {
}
}
+abstract contract ERC721NDecimalsHelperContract is ERC721DSTestPlus {
+ using EnumerableSet for EnumerableSet.AddressSet;
+ ERC721PoolFactory internal _poolFactory;
+
+ constructor(uint8 decimals) {
+ vm.createSelectFork(vm.envString("ETH_RPC_URL"));
+
+ _collateral = new NFTCollateralToken();
+ vm.makePersistent(address(_collateral));
+ _quote = new TokenWithNDecimals("Quote", "Q", decimals);
+ vm.makePersistent(address(_quote));
+ _ajnaToken = ERC20(_ajna);
+ vm.makePersistent(_ajna);
+ _poolUtils = new PoolInfoUtils();
+ vm.makePersistent(address(_poolUtils));
+ _poolFactory = new ERC721PoolFactory(_ajna);
+ vm.makePersistent(address(_poolFactory));
+
+ _startTime = block.timestamp;
+ uint256[] memory tokenIds;
+ address contractAddress = _poolFactory.deployPool(address(_collateral), address(_quote), tokenIds, 0.05 * 10**18);
+ vm.makePersistent(contractAddress);
+ _pool = ERC721Pool(contractAddress);
+ }
+
+ function _mintAndApproveQuoteTokens(address operator_, uint256 mintAmount_) internal {
+ deal(address(_quote), operator_, mintAmount_);
+ vm.prank(operator_);
+ _quote.approve(address(_pool), type(uint256).max);
+ }
+
+ function _mintAndApproveCollateralTokens(address operator_, uint256 mintAmount_) internal {
+ _collateral.mint(operator_, mintAmount_);
+ vm.prank(operator_);
+ _collateral.setApprovalForAll(address(_pool), true);
+ }
+
+ /**
+ * @dev Creates debt for an anonymous non-player borrower not otherwise involved in the test.
+ **/
+ function _anonBorrowerDrawsDebt(uint256 loanAmount) internal {
+ // _anonBorrowerCount += 1;
+
+ address borrower = makeAddr(string(abi.encodePacked("anonBorrower", borrowers.length())));
+ vm.stopPrank();
+ _mintAndApproveCollateralTokens(borrower, 1);
+ uint256[] memory tokenIdsToAdd = new uint256[](1);
+ tokenIdsToAdd[0] = _collateral.totalSupply();
+
+ _drawDebtNoLupCheck({
+ from: borrower,
+ borrower: borrower,
+ amountToBorrow: loanAmount,
+ limitIndex: MAX_FENWICK_INDEX,
+ tokenIds: tokenIdsToAdd
+ });
+ }
+}
+
abstract contract ERC721FuzzyHelperContract is ERC721DSTestPlus {
uint256 public constant LARGEST_AMOUNT = type(uint256).max / 10**27;
@@ -678,7 +800,7 @@ abstract contract ERC721FuzzyHelperContract is ERC721DSTestPlus {
constructor() {
_collateral = new NFTCollateralToken();
- _quote = new Token("Quote", "Q");
+ _quote = new TokenWithNDecimals("Quote", "Q", 18);
_ajnaToken = ERC20(_ajna);
_poolUtils = new PoolInfoUtils();
_poolFactory = new ERC721PoolFactory(_ajna);
diff --git a/tests/forge/ERC721Pool/ERC721NonStandardTokenTransfer.sol b/tests/forge/ERC721Pool/ERC721NonStandardTokenTransfer.sol
deleted file mode 100644
index 82c16794b..000000000
--- a/tests/forge/ERC721Pool/ERC721NonStandardTokenTransfer.sol
+++ /dev/null
@@ -1,121 +0,0 @@
-// SPDX-License-Identifier: UNLICENSED
-pragma solidity 0.8.14;
-
-import { ERC721HelperContract } from './ERC721DSTestPlus.sol';
-
-import 'src/ERC721Pool.sol';
-import 'src/ERC721PoolFactory.sol';
-
-interface ICryptoFighters {
- function transferFrom(address from_, address to_, uint256 tokenId_) external;
- function transfer(address to_, uint256 tokenId_) external;
- function approve(address to_, uint256 tokenId_) external;
- function ownerOf(uint256 tokenId_) external returns(address);
- function fighterIndexToApproved(uint256 tokenId_) external returns(address);
-}
-
-contract ERC721PoolNonStandardNftTest is ERC721HelperContract {
- address internal _borrower;
- address cryptoKittiesAddress = 0x06012c8cf97BEaD5deAe237070F9587f8E7A266d;
- ICryptoKitties cryptoKittiesContract = ICryptoKitties(cryptoKittiesAddress);
- address cryptoFightersAddress = 0x87d598064c736dd0C712D329aFCFAA0Ccc1921A1;
- ICryptoFighters cryptoFightersContract = ICryptoFighters(cryptoFightersAddress);
- address cryptoPunksAddress = 0xb47e3cd837dDF8e4c57F05d70Ab865de6e193BBB;
- ICryptoPunks cryptoPunksContract = ICryptoPunks(cryptoPunksAddress);
-
- function setUp() external {
- // Borrower has Crypto Kitties/Fighters/Punks in his wallet at specified block
- vm.createSelectFork(vm.envString("ETH_RPC_URL"), 16167859);
- }
-
- function testCryptoKittiesNftTransfer() external {
- uint256[] memory tokenIds;
- _pool = ERC721Pool(new ERC721PoolFactory(_ajna).deployPool(cryptoKittiesAddress, address(_quote), tokenIds, 0.05 * 10**18));
-
- _borrower = 0xDAd45d13472568181B83A731D1e170f4c6e95a83;
- changePrank(_borrower);
-
- assertEq(cryptoKittiesContract.ownerOf(1777317), _borrower);
- // Borrower approves Nft
- cryptoKittiesContract.approve(address(_pool), 1777317);
-
- assertEq(cryptoKittiesContract.kittyIndexToApproved(1777317), address(_pool));
-
- tokenIds = new uint256[](1);
- tokenIds[0] = 1777317;
-
- // Pledge Collateral
- ERC721Pool(address(_pool)).drawDebt(_borrower, 0, 0, tokenIds);
-
- // Check Pool is owner of NFT
- assertEq(cryptoKittiesContract.ownerOf(1777317), address(_pool));
-
- // Pull collateral
- ERC721Pool(address(_pool)).repayDebt(_borrower, 0, 1);
-
- // Check Borrower is owner of NFT
- assertEq(cryptoKittiesContract.ownerOf(1777317), _borrower);
-
- }
-
- function testCryptoFightersNftTransfer() external {
- uint256[] memory tokenIds;
- _pool = ERC721Pool(new ERC721PoolFactory(_ajna).deployPool(cryptoFightersAddress, address(_quote), tokenIds, 0.05 * 10**18));
-
- _borrower = 0x4E2EAE6ABA4E61Eb16c2D7905a4747323Ca7a504;
- changePrank(_borrower);
-
- assertEq(cryptoFightersContract.ownerOf(1), _borrower);
- // Borrower approves Nft
- cryptoFightersContract.approve(address(_pool), 1);
-
- assertEq(cryptoFightersContract.fighterIndexToApproved(1), address(_pool));
-
- tokenIds = new uint256[](1);
- tokenIds[0] = 1;
-
- // Pledge Collateral
- ERC721Pool(address(_pool)).drawDebt(_borrower, 0, 0, tokenIds);
-
- // Check Pool is owner of NFT
- assertEq(cryptoFightersContract.ownerOf(1), address(_pool));
-
- // Pull collateral
- ERC721Pool(address(_pool)).repayDebt(_borrower, 0, 1);
-
- // Check Borrower is owner of NFT
- assertEq(cryptoFightersContract.ownerOf(1), _borrower);
-
- }
-
- function testCryptoPunksNftTransfer() external {
- uint256[] memory tokenIds;
- _pool = ERC721Pool(new ERC721PoolFactory(_ajna).deployPool(cryptoPunksAddress, address(_quote), tokenIds, 0.05 * 10**18));
-
-
- _borrower = 0xB88F61E6FbdA83fbfffAbE364112137480398018;
- changePrank(_borrower);
-
- // Check Borrower is owner of NFT
- assertEq(cryptoPunksContract.punkIndexToAddress(1), _borrower);
-
- // Borrower approves Nft
- cryptoPunksContract.offerPunkForSaleToAddress(1, 0, address(_pool));
-
- tokenIds = new uint256[](1);
- tokenIds[0] = 1;
-
- // Pledge Collateral
- ERC721Pool(address(_pool)).drawDebt(_borrower, 0, 0, tokenIds);
-
- // Check Pool is owner of NFT
- assertEq(cryptoPunksContract.punkIndexToAddress(1), address(_pool));
-
- // Pull collateral
- ERC721Pool(address(_pool)).repayDebt(_borrower, 0, 1);
-
- // Check Borrower is owner of NFT
- assertEq(cryptoPunksContract.punkIndexToAddress(1), _borrower);
-
- }
-}
diff --git a/tests/forge/ERC721Pool/ERC721PoolBorrow.t.sol b/tests/forge/ERC721Pool/ERC721PoolBorrow.t.sol
index 806ffc285..04ae37064 100644
--- a/tests/forge/ERC721Pool/ERC721PoolBorrow.t.sol
+++ b/tests/forge/ERC721Pool/ERC721PoolBorrow.t.sol
@@ -1,13 +1,13 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.14;
-import { ERC721HelperContract, ERC721FuzzyHelperContract } from './ERC721DSTestPlus.sol';
+import { ERC721HelperContract, ERC721FuzzyHelperContract, ERC721NDecimalsHelperContract } from './ERC721DSTestPlus.sol';
import 'src/ERC721Pool.sol';
import 'src/libraries/internal/Maths.sol';
-import { MAX_PRICE, _priceAt } from 'src/libraries/helpers/PoolHelper.sol';
+import { MAX_FENWICK_INDEX, MAX_PRICE, _priceAt } from 'src/libraries/helpers/PoolHelper.sol';
abstract contract ERC721PoolBorrowTest is ERC721HelperContract {
address internal _borrower;
@@ -59,127 +59,101 @@ contract ERC721SubsetPoolBorrowTest is ERC721PoolBorrowTest {
function testBorrowLimitReached() external tearDown {
// lender deposits 10000 Quote into 3 buckets
- _addInitialLiquidity(
- {
- from: _lender,
- amount: 10_000 * 1e18,
- index: 2550
- }
- );
- _addInitialLiquidity(
- {
- from: _lender,
- amount: 10_000 * 1e18,
- index: 2551
- }
- );
- _addInitialLiquidity(
- {
- from: _lender,
- amount: 10_000 * 1e18,
- index: 2552
- }
- );
+ _addInitialLiquidity({
+ from: _lender,
+ amount: 10_000 * 1e18,
+ index: 2550
+ });
+ _addInitialLiquidity({
+ from: _lender,
+ amount: 10_000 * 1e18,
+ index: 2551
+ });
+ _addInitialLiquidity({
+ from: _lender,
+ amount: 10_000 * 1e18,
+ index: 2552
+ });
// borrower deposits three NFTs into the subset pool
uint256[] memory tokenIdsToAdd = new uint256[](3);
tokenIdsToAdd[0] = 1;
tokenIdsToAdd[1] = 3;
tokenIdsToAdd[2] = 5;
- _pledgeCollateral(
- {
- from: _borrower,
- borrower: _borrower,
- tokenIds: tokenIdsToAdd
- }
- );
+ _pledgeCollateral({
+ from: _borrower,
+ borrower: _borrower,
+ tokenIds: tokenIdsToAdd
+ });
// should revert if insufficient quote available before limit price
- _assertBorrowLimitIndexRevert(
- {
- from: _borrower,
- amount: 21_000 * 1e18,
- indexLimit: 2551
- }
- );
+ _assertBorrowLimitIndexRevert({
+ from: _borrower,
+ amount: 21_000 * 1e18,
+ indexLimit: 2551
+ });
}
function testBorrowBorrowerUnderCollateralized() external tearDown {
// add initial quote to the pool
- _addInitialLiquidity(
- {
- from: _lender,
- amount: 1_000 * 1e18,
- index: 3575
- }
- );
+ _addInitialLiquidity({
+ from: _lender,
+ amount: 1_000 * 1e18,
+ index: 3575
+ });
// borrower pledges some collateral
uint256[] memory tokenIdsToAdd = new uint256[](2);
tokenIdsToAdd[0] = 5;
tokenIdsToAdd[1] = 3;
- _pledgeCollateral(
- {
- from: _borrower,
- borrower: _borrower,
- tokenIds: tokenIdsToAdd
- }
- );
+ _pledgeCollateral({
+ from: _borrower,
+ borrower: _borrower,
+ tokenIds: tokenIdsToAdd
+ });
// should revert if borrower did not deposit enough collateral
- _assertBorrowBorrowerUnderCollateralizedRevert(
- {
- from: _borrower,
- amount: 40 * 1e18,
- indexLimit: 4000
- }
- );
+ _assertBorrowBorrowerUnderCollateralizedRevert({
+ from: _borrower,
+ amount: 40 * 1e18,
+ indexLimit: 4000
+ });
}
function testBorrowPoolUnderCollateralized() external tearDown {
// add initial quote to the pool
- _addInitialLiquidity(
- {
- from: _lender,
- amount: 1_000 * 1e18,
- index: 3232
- }
- );
+ _addInitialLiquidity({
+ from: _lender,
+ amount: 1_000 * 1e18,
+ index: 3232
+ });
// should revert if borrow would result in pool under collateralization
- _assertBorrowBorrowerUnderCollateralizedRevert(
- {
- from: _borrower,
- amount: 500,
- indexLimit: 4000
- }
- );
+ _assertBorrowBorrowerUnderCollateralizedRevert({
+ from: _borrower,
+ amount: 500,
+ indexLimit: 4000
+ });
}
function testBorrowAndRepay() external {
// lender deposits 10000 Quote into 3 buckets
- _addInitialLiquidity(
- {
- from: _lender,
- amount: 10_000 * 1e18,
- index: 2550
- }
- );
- _addInitialLiquidity(
- {
- from: _lender,
- amount: 10_000 * 1e18,
- index: 2551
- }
- );
- _addInitialLiquidity(
- {
- from: _lender,
- amount: 10_000 * 1e18,
- index: 2552
- }
- );
+ _addInitialLiquidity({
+ from: _lender,
+ amount: 10_000 * 1e18,
+ index: 2550
+ });
+ _addInitialLiquidity({
+ from: _lender,
+ amount: 10_000 * 1e18,
+ index: 2551
+ });
+ _addInitialLiquidity({
+ from: _lender,
+ amount: 10_000 * 1e18,
+ index: 2552
+ });
// check initial token balances
assertEq(_collateral.balanceOf(_borrower), 52);
@@ -206,40 +180,33 @@ contract ERC721SubsetPoolBorrowTest is ERC721PoolBorrowTest {
interestRateUpdate: _startTime
})
);
-
// check initial bucket state
- _assertBucket(
- {
- index: 2550,
- lpBalance: 10_000 * 1e27,
- collateral: 0,
- deposit: 10_000 * 1e18,
- exchangeRate: 1 * 1e27
- }
- );
+ _assertBucket({
+ index: 2550,
+ lpBalance: 10_000 * 1e18,
+ collateral: 0,
+ deposit: 10_000 * 1e18,
+ exchangeRate: 1 * 1e18
+ });
// borrower deposits three NFTs into the subset pool
uint256[] memory tokenIdsToAdd = new uint256[](3);
tokenIdsToAdd[0] = 1;
tokenIdsToAdd[1] = 3;
tokenIdsToAdd[2] = 5;
- _pledgeCollateral(
- {
- from: _borrower,
- borrower: _borrower,
- tokenIds: tokenIdsToAdd
- }
- );
+ _pledgeCollateral({
+ from: _borrower,
+ borrower: _borrower,
+ tokenIds: tokenIdsToAdd
+ });
// borrower borrows from the pool
uint256 borrowAmount = 3_000 * 1e18;
- _borrow(
- {
- from: _borrower,
- amount: borrowAmount,
- indexLimit: 2551,
- newLup: _priceAt(2550)
- }
- );
+ _borrow({
+ from: _borrower,
+ amount: borrowAmount,
+ indexLimit: 2551,
+ newLup: _priceAt(2550)
+ });
// check token balances after borrow
assertEq(_collateral.balanceOf(_borrower), 49);
@@ -266,28 +233,22 @@ contract ERC721SubsetPoolBorrowTest is ERC721PoolBorrowTest {
interestRateUpdate: _startTime
})
);
-
// check bucket state after borrow
- _assertBucket(
- {
- index: 2550,
- lpBalance: 10_000 * 1e27,
- collateral: 0,
- deposit: 10_000 * 1e18,
- exchangeRate: 1 * 1e27
- }
- );
-
+ _assertBucket({
+ index: 2550,
+ lpBalance: 10_000 * 1e18,
+ collateral: 0,
+ deposit: 10_000 * 1e18,
+ exchangeRate: 1 * 1e18
+ });
// check borrower info after borrow
- _assertBorrower(
- {
- borrower: _borrower,
- borrowerDebt: 3_002.884615384615386000 * 1e18,
- borrowerCollateral: 3 * 1e18,
- borrowert0Np: 1_051.009615384615385100 * 1e18,
- borrowerCollateralization: 3.007999714779824033 * 1e18
- }
- );
+ _assertBorrower({
+ borrower: _borrower,
+ borrowerDebt: 3_002.884615384615386000 * 1e18,
+ borrowerCollateral: 3 * 1e18,
+ borrowert0Np: 1_051.009615384615385100 * 1e18,
+ borrowerCollateralization: 3.007999714779824033 * 1e18
+ });
// pass time to allow interest to accumulate
skip(10 days);
@@ -328,40 +289,33 @@ contract ERC721SubsetPoolBorrowTest is ERC721PoolBorrowTest {
})
);
// check bucket state after partial repay
- _assertBucket(
- {
- index: 2550,
- lpBalance: 10_000 * 1e27,
- collateral: 0,
- deposit: 10_001.17341179741568 * 1e18,
- exchangeRate: 1.000117341179741568 * 1e27
- }
- );
-
+ _assertBucket({
+ index: 2550,
+ lpBalance: 10_000 * 1e18,
+ collateral: 0,
+ deposit: 10_001.17341179741568 * 1e18,
+ exchangeRate: 1.000117341179741568 * 1e18
+ });
// check borrower info after partial repay
- _assertBorrower(
- {
- borrower: _borrower,
- borrowerDebt: 1_507.000974734143274062 * 1e18,
- borrowerCollateral: 3 * 1e18,
- borrowert0Np: 1_051.009615384615385100 * 1e18,
- borrowerCollateralization: 5.993809040625961846 * 1e18
- }
- );
+ _assertBorrower({
+ borrower: _borrower,
+ borrowerDebt: 1_507.000974734143274062 * 1e18,
+ borrowerCollateral: 3 * 1e18,
+ borrowert0Np: 1_051.009615384615385100 * 1e18,
+ borrowerCollateralization: 5.993809040625961846 * 1e18
+ });
// pass time to allow additional interest to accumulate
skip(10 days);
// find pending debt after interest accumulation
- _assertBorrower(
- {
- borrower: _borrower,
- borrowerDebt: 1_508.860066921599065131 * 1e18,
- borrowerCollateral: 3 * 1e18,
- borrowert0Np: 1_051.009615384615385100 * 1e18,
- borrowerCollateralization: 5.986423966420065589 * 1e18
- }
- );
+ _assertBorrower({
+ borrower: _borrower,
+ borrowerDebt: 1_508.860066921599065131 * 1e18,
+ borrowerCollateral: 3 * 1e18,
+ borrowert0Np: 1_051.009615384615385100 * 1e18,
+ borrowerCollateralization: 5.986423966420065589 * 1e18
+ });
// mint additional quote to allow borrower to repay their loan plus interest
deal(address(_quote), _borrower, _quote.balanceOf(_borrower) + 1_000 * 1e18);
@@ -409,33 +363,26 @@ contract ERC721SubsetPoolBorrowTest is ERC721PoolBorrowTest {
interestRateUpdate: _startTime + 20 days
})
);
- _assertEMAs(
- {
- debtEma: 142.074529848655991542 * 1e18,
- lupColEma: 851.567601449557349751 * 1e18
- }
- );
-
+ _assertEMAs({
+ debtEma: 142.074529848655991542 * 1e18,
+ lupColEma: 851.567601449557349751 * 1e18
+ });
// check bucket state after fully repay
- _assertBucket(
- {
- index: 2550,
- lpBalance: 10_000 * 1e27,
- collateral: 0,
- deposit: 10_001.70173768409813 * 1e18,
- exchangeRate: 1.000170173768409813 * 1e27
- }
- );
+ _assertBucket({
+ index: 2550,
+ lpBalance: 10_000 * 1e18,
+ collateral: 0,
+ deposit: 10_001.70173768409813 * 1e18,
+ exchangeRate: 1.000170173768409813 * 1e18
+ });
// check borrower info after fully repay
- _assertBorrower(
- {
- borrower: _borrower,
- borrowerDebt: 0,
- borrowerCollateral: 0,
- borrowert0Np: 0,
- borrowerCollateralization: 1 * 1e18
- }
- );
+ _assertBorrower({
+ borrower: _borrower,
+ borrowerDebt: 0,
+ borrowerCollateral: 0,
+ borrowert0Np: 0,
+ borrowerCollateralization: 1 * 1e18
+ });
assertEq(_collateral.balanceOf(_borrower), 52);
assertEq(_collateral.balanceOf(address(_pool)), 0);
@@ -443,86 +390,77 @@ contract ERC721SubsetPoolBorrowTest is ERC721PoolBorrowTest {
function testPoolRepayRequireChecks() external tearDown {
// add initial quote to the pool
- _addInitialLiquidity(
- {
- from: _lender,
- amount: 10_000 * 1e18,
- index: 2550
- }
- );
- _addInitialLiquidity(
- {
- from: _lender,
- amount: 10_000 * 1e18,
- index: 2551
- }
- );
+ _addInitialLiquidity({
+ from: _lender,
+ amount: 10_000 * 1e18,
+ index: 2550
+ });
+ _addInitialLiquidity({
+ from: _lender,
+ amount: 10_000 * 1e18,
+ index: 2551
+ });
deal(address(_quote), _borrower, _quote.balanceOf(_borrower) + 10_000 * 1e18);
+
// should revert if borrower has no debt
- _assertRepayNoDebtRevert(
- {
- from: _borrower,
- borrower: _borrower,
- amount: 10_000 * 1e18
- }
- );
+ _assertRepayNoDebtRevert({
+ from: _borrower,
+ borrower: _borrower,
+ amount: 10_000 * 1e18
+ });
// borrower 1 borrows 1000 quote from the pool
uint256[] memory tokenIdsToAdd = new uint256[](3);
tokenIdsToAdd[0] = 1;
tokenIdsToAdd[1] = 3;
tokenIdsToAdd[2] = 5;
- _pledgeCollateral(
- {
- from: _borrower,
- borrower: _borrower,
- tokenIds: tokenIdsToAdd
- }
- );
- _borrow(
- {
- from: _borrower,
- amount: 1_000 * 1e18,
- indexLimit: 3_000,
- newLup: 3_010.892022197881557845 * 1e18
- }
- );
+ _pledgeCollateral({
+ from: _borrower,
+ borrower: _borrower,
+ tokenIds: tokenIdsToAdd
+ });
+ _borrow({
+ from: _borrower,
+ amount: 1_000 * 1e18,
+ indexLimit: 3_000,
+ newLup: 3_010.892022197881557845 * 1e18
+ });
- _assertLoans(
- {
- noOfLoans: 1,
- maxBorrower: _borrower,
- maxThresholdPrice: 333.653846153846154 * 1e18
- }
- );
+ _assertLoans({
+ noOfLoans: 1,
+ maxBorrower: _borrower,
+ maxThresholdPrice: 333.653846153846154 * 1e18
+ });
+
+ // should revert if LUP is below the limit
+ ( , , , , , uint256 lupIndex ) = _poolUtils.poolPricesInfo(address(_pool));
+ _assertPullLimitIndexRevert({
+ from: _borrower,
+ amount: 2,
+ indexLimit: lupIndex - 1
+ });
// borrower 2 borrows 3k quote from the pool and becomes new queue HEAD
tokenIdsToAdd = new uint256[](1);
tokenIdsToAdd[0] = 53;
- _pledgeCollateral(
- {
- from: _borrower2,
- borrower: _borrower2,
- tokenIds: tokenIdsToAdd
- }
- );
- _borrow(
- {
- from: _borrower2,
- amount: 3_000 * 1e18,
- indexLimit: 3_000,
- newLup: 3_010.892022197881557845 * 1e18
- }
- );
+ _pledgeCollateral({
+ from: _borrower2,
+ borrower: _borrower2,
+ tokenIds: tokenIdsToAdd
+ });
+ _borrow({
+ from: _borrower2,
+ amount: 3_000 * 1e18,
+ indexLimit: 3_000,
+ newLup: 3_010.892022197881557845 * 1e18
+ });
- _assertLoans(
- {
- noOfLoans: 2,
- maxBorrower: _borrower2,
- maxThresholdPrice: 3_002.884615384615386 * 1e18
- }
- );
+ _assertLoans({
+ noOfLoans: 2,
+ maxBorrower: _borrower2,
+ maxThresholdPrice: 3_002.884615384615386 * 1e18
+ });
// should be able to repay loan if properly specified
_repayDebt({
@@ -537,49 +475,39 @@ contract ERC721SubsetPoolBorrowTest is ERC721PoolBorrowTest {
function testRepayLoanFromDifferentActor() external tearDown {
- _addInitialLiquidity(
- {
- from: _lender,
- amount: 10_000 * 1e18,
- index: 2550
- }
- );
- _addInitialLiquidity(
- {
- from: _lender,
- amount: 10_000 * 1e18,
- index: 2551
- }
- );
- _addInitialLiquidity(
- {
- from: _lender,
- amount: 10_000 * 1e18,
- index: 2552
- }
- );
+ _addInitialLiquidity({
+ from: _lender,
+ amount: 10_000 * 1e18,
+ index: 2550
+ });
+ _addInitialLiquidity({
+ from: _lender,
+ amount: 10_000 * 1e18,
+ index: 2551
+ });
+ _addInitialLiquidity({
+ from: _lender,
+ amount: 10_000 * 1e18,
+ index: 2552
+ });
// borrower deposits three NFTs into the subset pool
uint256[] memory tokenIdsToAdd = new uint256[](3);
tokenIdsToAdd[0] = 1;
tokenIdsToAdd[1] = 3;
tokenIdsToAdd[2] = 5;
- _pledgeCollateral(
- {
- from: _borrower,
- borrower: _borrower,
- tokenIds: tokenIdsToAdd
- }
- );
+ _pledgeCollateral({
+ from: _borrower,
+ borrower: _borrower,
+ tokenIds: tokenIdsToAdd
+ });
// borrower borrows from the pool
- _borrow(
- {
- from: _borrower,
- amount: 3_000 * 1e18,
- indexLimit: 2_551,
- newLup: 3_010.892022197881557845 * 1e18
- }
- );
+ _borrow({
+ from: _borrower,
+ amount: 3_000 * 1e18,
+ indexLimit: 2_551,
+ newLup: 3_010.892022197881557845 * 1e18
+ });
// check token balances after borrow
assertEq(_pool.pledgedCollateral(), Maths.wad(3));
@@ -616,71 +544,54 @@ contract ERC721SubsetPoolBorrowTest is ERC721PoolBorrowTest {
}
}
-contract ERC721CollectionPoolBorrowTest is ERC721PoolBorrowTest {
- uint internal _anonBorrowerCount = 0;
+contract ERC721CollectionPoolBorrowTest is ERC721NDecimalsHelperContract(18) {
+ address internal _borrower;
+ address internal _lender;
- function createPool() external override returns (ERC721Pool) {
- return _deployCollectionPool();
- }
+ function setUp() external {
+ _borrower = makeAddr("borrower");
+ _lender = makeAddr("lender");
- /**
- * @dev Creates debt for an anonymous non-player borrower not otherwise involved in the test.
- **/
- function _anonBorrowerDrawsDebt(uint256 loanAmount) internal {
- _anonBorrowerCount += 1;
- address borrower = makeAddr(string(abi.encodePacked("anonBorrower", _anonBorrowerCount)));
- vm.stopPrank();
- _mintAndApproveCollateralTokens(borrower, 1);
- uint256[] memory tokenIdsToAdd = new uint256[](1);
- tokenIdsToAdd[0] = _collateral.totalSupply();
-
- _drawDebtNoLupCheck(
- {
- from: borrower,
- borrower: borrower,
- amountToBorrow: loanAmount,
- limitIndex: 7_777,
- tokenIds: tokenIdsToAdd
- }
- );
+ _mintAndApproveQuoteTokens(_lender, 200_000 * 1e18);
+ _mintAndApproveCollateralTokens(_borrower, 52);
+
+ vm.prank(_borrower);
+ _quote.approve(address(_pool), 200_000 * 1e18);
}
function testMinBorrowAmountCheck() external tearDown {
// add initial quote to the pool
changePrank(_lender);
- _pool.addQuoteToken(20_000 * 1e18, 2550);
+ _pool.addQuoteToken(20_000 * 1e18, 2550, block.timestamp + 1 minutes);
// 10 borrowers draw debt
for (uint i=0; i<10; ++i) {
_anonBorrowerDrawsDebt(1_200 * 1e18);
}
+
(, uint256 loansCount, , , ) = _poolUtils.poolLoansInfo(address(_pool));
assertEq(loansCount, 10);
uint256[] memory tokenIdsToAdd = new uint256[](1);
tokenIdsToAdd[0] = 5;
- _pledgeCollateral(
- {
- from: _borrower,
- borrower: _borrower,
- tokenIds: tokenIdsToAdd
- }
- );
+ _pledgeCollateral({
+ from: _borrower,
+ borrower: _borrower,
+ tokenIds: tokenIdsToAdd
+ });
// should revert if borrower attempts to borrow more than minimum amount
- _assertBorrowMinDebtRevert(
- {
- from: _borrower,
- amount: 100 * 1e18,
- indexLimit: 7_777
- }
- );
+ _assertBorrowMinDebtRevert({
+ from: _borrower,
+ amount: 100 * 1e18,
+ indexLimit: MAX_FENWICK_INDEX
+ });
}
function testMinRepayAmountCheck() external tearDown {
// add initial quote to the pool
changePrank(_lender);
- _pool.addQuoteToken(20_000 * 1e18, 2550);
+ _pool.addQuoteToken(20_000 * 1e18, 2550, block.timestamp + 1 minutes);
// 9 other borrowers draw debt
for (uint i=0; i<9; ++i) {
@@ -693,31 +604,74 @@ contract ERC721CollectionPoolBorrowTest is ERC721PoolBorrowTest {
tokenIdsToAdd[1] = 3;
tokenIdsToAdd[2] = 5;
- _drawDebtNoLupCheck(
- {
- from: _borrower,
- borrower: _borrower,
- amountToBorrow: 1_000 * 1e18,
- limitIndex: 3000,
- tokenIds: tokenIdsToAdd
- }
- );
+ _drawDebtNoLupCheck({
+ from: _borrower,
+ borrower: _borrower,
+ amountToBorrow: 1_000 * 1e18,
+ limitIndex: 3000,
+ tokenIds: tokenIdsToAdd
+ });
(, uint256 loansCount, , , ) = _poolUtils.poolLoansInfo(address(_pool));
assertEq(loansCount, 10);
// should revert if amount left after repay is less than the average debt
- _assertRepayMinDebtRevert(
- {
- from: _borrower,
- borrower: _borrower,
- amount: 900 * 1e18
- }
- );
+ _assertRepayMinDebtRevert({
+ from: _borrower,
+ borrower: _borrower,
+ amount: 900 * 1e18
+ });
+ }
+}
+
+contract ERC721ScaledQuoteTokenBorrowTest is ERC721NDecimalsHelperContract(4) {
+ address internal _borrower;
+ address internal _lender;
+
+ function setUp() external {
+ _borrower = makeAddr("borrower");
+ _lender = makeAddr("lender");
+
+ _mintAndApproveQuoteTokens(_lender, 20_000 * 1e4);
+ _mintAndApproveCollateralTokens(_borrower, 5);
}
+ function testMinDebtBelowDustLimitCheck() external tearDown {
+ // add initial quote to the pool
+ changePrank(_lender);
+ _pool.addQuoteToken(20_000 * 1e18, 2550, block.timestamp + 30);
+
+ // borrower pledges a single NFT
+ uint256[] memory tokenIdsToAdd = new uint256[](1);
+ tokenIdsToAdd[0] = 5;
+ _pledgeCollateral({
+ from: _borrower,
+ borrower: _borrower,
+ tokenIds: tokenIdsToAdd
+ });
+
+ // should revert if borrower tries to draw debt below dust limit
+ _assertBorrowDustRevert({
+ from: _borrower,
+ amount: 0.00005 * 1e18,
+ indexLimit: 2550
+ });
+
+ // 10 borrowers draw debt at the dust limit
+ for (uint i=0; i<10; ++i) {
+ _anonBorrowerDrawsDebt(0.0001 * 1e18);
+ }
+
+ // should still revert if borrower tries to draw debt below dust limit
+ _assertBorrowDustRevert({
+ from: _borrower,
+ amount: 0.000075 * 1e18,
+ indexLimit: 2550
+ });
+ }
}
+
contract ERC721PoolBorrowFuzzyTest is ERC721FuzzyHelperContract {
address internal _borrower;
@@ -754,22 +708,23 @@ contract ERC721PoolBorrowFuzzyTest is ERC721FuzzyHelperContract {
uint256[] memory indexes = new uint256[](numIndexes);
for (uint256 i = 0; i < numIndexes; ++i) {
deal(address(_quote), _lender, mintAmount_);
+
indexes[i] = _randomIndexWithMinimumPrice(5000); // setting a minimum price for collateral prevents exceeding memory and gas limits
_addLiquidity({
from: _lender,
amount: mintAmount_,
index: indexes[i],
- lpAward: mintAmount_ * 1e9,
+ lpAward: mintAmount_,
newLup: _calculateLup(address(_pool), 0)
});
_assertBucket({
index: indexes[i],
- lpBalance: mintAmount_ * 1e9,
+ lpBalance: mintAmount_,
collateral: 0,
deposit: mintAmount_,
- exchangeRate: 1e27
+ exchangeRate: 1e18
});
}
@@ -792,10 +747,10 @@ contract ERC721PoolBorrowFuzzyTest is ERC721FuzzyHelperContract {
for (uint256 i = 0; i < numIndexes; ++i) {
_assertBucket({
index: indexes[i],
- lpBalance: mintAmount_ * 1e9,
+ lpBalance: mintAmount_,
collateral: 0,
deposit: mintAmount_,
- exchangeRate: 1e27
+ exchangeRate: 1e18
});
}
@@ -832,6 +787,7 @@ contract ERC721PoolBorrowFuzzyTest is ERC721FuzzyHelperContract {
// repay all debt and withdraw collateral
(debt, , ) = _poolUtils.borrowerInfo(address(_pool), address(_borrower));
deal(address(_quote), _borrower, debt);
+
_repayDebt({
from: _borrower,
borrower: _borrower,
@@ -848,16 +804,17 @@ contract ERC721PoolBorrowFuzzyTest is ERC721FuzzyHelperContract {
// check that only deposits above the htp earned interest
if (indexes[i] <= _poolUtils.priceToIndex(Maths.wdiv(debt, Maths.wad(tokenIdsToAdd.length)))) {
assertGt(deposit, mintAmount_);
- assertGt(exchangeRate, 1e27);
+ assertGt(exchangeRate, 1e18);
} else {
assertEq(deposit, mintAmount_);
- assertEq(exchangeRate, 1e27);
+ assertEq(exchangeRate, 1e18);
}
- assertEq(lpAccumulator, mintAmount_ * 1e9);
+ assertEq(lpAccumulator, mintAmount_);
+
_assertBucket({
index: indexes[i],
- lpBalance: mintAmount_ * 1e9,
+ lpBalance: mintAmount_,
collateral: 0,
deposit: deposit,
exchangeRate: exchangeRate
diff --git a/tests/forge/ERC721Pool/ERC721PoolCollateral.t.sol b/tests/forge/ERC721Pool/ERC721PoolCollateral.t.sol
index cee499c25..89c99740a 100644
--- a/tests/forge/ERC721Pool/ERC721PoolCollateral.t.sol
+++ b/tests/forge/ERC721Pool/ERC721PoolCollateral.t.sol
@@ -3,6 +3,8 @@ pragma solidity 0.8.14;
import { ERC721HelperContract } from './ERC721DSTestPlus.sol';
+import { ERC721Pool } from 'src/ERC721Pool.sol';
+
import 'src/PoolInfoUtils.sol';
import 'src/libraries/helpers/PoolHelper.sol';
@@ -13,20 +15,14 @@ contract ERC721PoolCollateralTest is ERC721HelperContract {
address internal _lender;
address internal _lender2;
- function setUp() external {
+ function setUp() virtual external {
_borrower = makeAddr("borrower");
_borrower2 = makeAddr("borrower2");
_lender = makeAddr("lender");
_lender2 = makeAddr("lender2");
- // deploy subset pool
- uint256[] memory subsetTokenIds = new uint256[](5);
- subsetTokenIds[0] = 1;
- subsetTokenIds[1] = 3;
- subsetTokenIds[2] = 5;
- subsetTokenIds[3] = 51;
- subsetTokenIds[4] = 53;
- _pool = _deploySubsetPool(subsetTokenIds);
+ // deploy collection pool
+ _pool = _deployCollectionPool();
_mintAndApproveQuoteTokens(_lender, 200_000 * 1e18);
_mintAndApproveQuoteTokens(_borrower, 100 * 1e18);
@@ -35,15 +31,11 @@ contract ERC721PoolCollateralTest is ERC721HelperContract {
_mintAndApproveCollateralTokens(_borrower2, 53);
}
- /*******************************/
- /*** ERC721 Collection Tests ***/
- /*******************************/
+ /************************************/
+ /*** ERC721 Collection Pool Tests ***/
+ /************************************/
- /***************************/
- /*** ERC721 Subset Tests ***/
- /***************************/
-
- function testPledgeCollateralSubset() external tearDown {
+ function testPledgeCollateral() external tearDown {
// check initial token balances
assertEq(_pool.pledgedCollateral(), 0);
@@ -55,14 +47,12 @@ contract ERC721PoolCollateralTest is ERC721HelperContract {
tokenIdsToAdd[1] = 3;
tokenIdsToAdd[2] = 5;
- // borrower deposits three NFTs into the subset pool
- _pledgeCollateral(
- {
- from: _borrower,
- borrower: _borrower,
- tokenIds: tokenIdsToAdd
- }
- );
+ // borrower deposits three NFTs into the pool
+ _pledgeCollateral({
+ from: _borrower,
+ borrower: _borrower,
+ tokenIds: tokenIdsToAdd
+ });
// check token balances after add
assertEq(_pool.pledgedCollateral(), Maths.wad(3));
@@ -70,22 +60,7 @@ contract ERC721PoolCollateralTest is ERC721HelperContract {
assertEq(_collateral.balanceOf(address(_pool)), 3);
}
- function testPledgeCollateralNotInSubset() external tearDown {
- uint256[] memory tokenIdsToAdd = new uint256[](3);
- tokenIdsToAdd[0] = 2;
- tokenIdsToAdd[1] = 4;
- tokenIdsToAdd[2] = 6;
-
- // should revert if borrower attempts to add tokens not in the pool subset
- _assertPledgeCollateralNotInSubsetRevert(
- {
- from: _borrower,
- tokenIds: tokenIdsToAdd
- }
- );
- }
-
- function testPledgeCollateralInSubsetFromDifferentActor() external tearDown {
+ function testPledgeCollateralFromDifferentActor() external tearDown {
// check initial token balances
assertEq(_pool.pledgedCollateral(), 0);
@@ -93,36 +68,30 @@ contract ERC721PoolCollateralTest is ERC721HelperContract {
assertEq(_collateral.balanceOf(_borrower2), 53);
assertEq(_collateral.balanceOf(address(_pool)), 0);
- _assertBorrower(
- {
- borrower: _borrower,
- borrowerDebt: 0,
- borrowerCollateral: 0,
- borrowert0Np: 0,
- borrowerCollateralization: 1 * 1e18
- }
- );
- _assertBorrower(
- {
- borrower: _borrower2,
- borrowerDebt: 0,
- borrowerCollateral: 0,
- borrowert0Np: 0,
- borrowerCollateralization: 1 * 1e18
- }
- );
+ _assertBorrower({
+ borrower: _borrower,
+ borrowerDebt: 0,
+ borrowerCollateral: 0,
+ borrowert0Np: 0,
+ borrowerCollateralization: 1 * 1e18
+ });
+ _assertBorrower({
+ borrower: _borrower2,
+ borrowerDebt: 0,
+ borrowerCollateral: 0,
+ borrowert0Np: 0,
+ borrowerCollateralization: 1 * 1e18
+ });
uint256[] memory tokenIdsToAdd = new uint256[](1);
tokenIdsToAdd[0] = 53;
- // borrower deposits three NFTs into the subset pool
- _pledgeCollateral(
- {
- from: _borrower2,
- borrower: _borrower,
- tokenIds: tokenIdsToAdd
- }
- );
+ // borrower deposits three NFTs into the pool
+ _pledgeCollateral({
+ from: _borrower2,
+ borrower: _borrower,
+ tokenIds: tokenIdsToAdd
+ });
// check token balances after add
assertEq(_pool.pledgedCollateral(), Maths.wad(1));
@@ -131,24 +100,20 @@ contract ERC721PoolCollateralTest is ERC721HelperContract {
assertEq(_collateral.balanceOf(_borrower2), 52);
assertEq(_collateral.balanceOf(address(_pool)), 1);
- _assertBorrower(
- {
- borrower: _borrower,
- borrowerDebt: 0,
- borrowerCollateral: 1 * 1e18,
- borrowert0Np: 0,
- borrowerCollateralization: 1 * 1e18
- }
- );
- _assertBorrower(
- {
- borrower: _borrower2,
- borrowerDebt: 0,
- borrowerCollateral: 0,
- borrowert0Np: 0,
- borrowerCollateralization: 1 * 1e18
- }
- );
+ _assertBorrower({
+ borrower: _borrower,
+ borrowerDebt: 0,
+ borrowerCollateral: 1 * 1e18,
+ borrowert0Np: 0,
+ borrowerCollateralization: 1 * 1e18
+ });
+ _assertBorrower({
+ borrower: _borrower2,
+ borrowerDebt: 0,
+ borrowerCollateral: 0,
+ borrowert0Np: 0,
+ borrowerCollateralization: 1 * 1e18
+ });
}
function testPullCollateral() external tearDown {
@@ -169,14 +134,12 @@ contract ERC721PoolCollateralTest is ERC721HelperContract {
tokenIdsToAdd[1] = 3;
tokenIdsToAdd[2] = 5;
- // borrower deposits three NFTs into the subset pool
- _pledgeCollateral(
- {
- from: _borrower,
- borrower: _borrower,
- tokenIds: tokenIdsToAdd
- }
- );
+ // borrower deposits three NFTs into the pool
+ _pledgeCollateral({
+ from: _borrower,
+ borrower: _borrower,
+ tokenIds: tokenIdsToAdd
+ });
// check token balances after add
assertEq(_pool.pledgedCollateral(), Maths.wad(3));
@@ -191,25 +154,21 @@ contract ERC721PoolCollateralTest is ERC721HelperContract {
assertEq(_collateral.ownerOf(5), address(_pool));
// should fail if trying to pull collateral by an address without pledged collateral
- _assertPullInsufficientCollateralRevert(
- {
- from: _lender,
- amount: 3
- }
- );
+ _assertPullInsufficientCollateralRevert({
+ from: _lender,
+ amount: 3
+ });
// borrower2 is owner of NFT
assertEq(_collateral.ownerOf(53), _borrower2);
tokenIdsToAdd = new uint256[](1);
tokenIdsToAdd[0] = 53;
- _pledgeCollateral(
- {
- from: _borrower2,
- borrower: _borrower2,
- tokenIds: tokenIdsToAdd
- }
- );
+ _pledgeCollateral({
+ from: _borrower2,
+ borrower: _borrower2,
+ tokenIds: tokenIdsToAdd
+ });
// check token balances after add
assertEq(_pool.pledgedCollateral(), Maths.wad(4));
@@ -242,14 +201,96 @@ contract ERC721PoolCollateralTest is ERC721HelperContract {
assertEq(_collateral.ownerOf(3), _borrower);
assertEq(_collateral.ownerOf(5), _borrower);
-
// should fail if borrower tries to pull more NFTs than remaining in pool
- _assertPullInsufficientCollateralRevert(
- {
- from: _borrower,
- amount: 3
- }
- );
+ _assertPullInsufficientCollateralRevert({
+ from: _borrower,
+ amount: 3
+ });
+ }
+
+ function testPullCollateralToDifferentRecipient() external tearDown {
+ address tokensReceiver = makeAddr("tokensReceiver");
+
+ // check initial token balances
+ assertEq(_pool.pledgedCollateral(), 0);
+
+ assertEq(_collateral.balanceOf(_borrower), 52);
+ assertEq(_collateral.balanceOf(_borrower2), 53);
+ assertEq(_collateral.balanceOf(tokensReceiver), 0);
+ assertEq(_collateral.balanceOf(address(_pool)), 0);
+
+ // borrower is owner of NFTs
+ assertEq(_collateral.ownerOf(1), _borrower);
+ assertEq(_collateral.ownerOf(3), _borrower);
+ assertEq(_collateral.ownerOf(5), _borrower);
+
+ uint256[] memory tokenIdsToAdd = new uint256[](3);
+ tokenIdsToAdd[0] = 1;
+ tokenIdsToAdd[1] = 3;
+ tokenIdsToAdd[2] = 5;
+
+ // borrower deposits three NFTs into the pool
+ _pledgeCollateral({
+ from: _borrower,
+ borrower: _borrower,
+ tokenIds: tokenIdsToAdd
+ });
+
+ // borrower2 deposits three NFTs into the pool
+ tokenIdsToAdd = new uint256[](1);
+ tokenIdsToAdd[0] = 53;
+ _pledgeCollateral({
+ from: _borrower2,
+ borrower: _borrower2,
+ tokenIds: tokenIdsToAdd
+ });
+
+ // check token balances after add
+ assertEq(_pool.pledgedCollateral(), Maths.wad(4));
+ assertEq(_collateral.balanceOf(address(_pool)), 4);
+
+ // pool is owner of pledged NFTs
+ assertEq(_collateral.ownerOf(1), address(_pool));
+ assertEq(_collateral.ownerOf(3), address(_pool));
+ assertEq(_collateral.ownerOf(5), address(_pool));
+ assertEq(_collateral.ownerOf(53), address(_pool));
+
+ // borrower removes some of their deposited NFTs from the pool and transfer to a different recipient
+ changePrank(_borrower);
+ ERC721Pool(address(_pool)).repayDebt(_borrower, 0, 2, tokensReceiver, MAX_FENWICK_INDEX);
+
+ // check token balances after remove
+ assertEq(_pool.pledgedCollateral(), Maths.wad(2));
+
+ assertEq(_collateral.balanceOf(_borrower), 49);
+ assertEq(_collateral.balanceOf(_borrower2), 52);
+ assertEq(_collateral.balanceOf(tokensReceiver), 2);
+ assertEq(_collateral.balanceOf(address(_pool)), 2);
+
+ // pool is owner of remaining pledged NFT
+ assertEq(_collateral.ownerOf(1), address(_pool));
+ // recipient is owner of 2 pulled NFTs
+ assertEq(_collateral.ownerOf(3), tokensReceiver);
+ assertEq(_collateral.ownerOf(5), tokensReceiver);
+
+ // borrower2 removes deposited NFT from the pool and transfer to same recipient
+ changePrank(_borrower2);
+ ERC721Pool(address(_pool)).repayDebt(_borrower2, 0, 1, tokensReceiver, MAX_FENWICK_INDEX);
+
+ // check token balances after remove
+ assertEq(_pool.pledgedCollateral(), Maths.wad(1));
+
+ assertEq(_collateral.balanceOf(_borrower), 49);
+ assertEq(_collateral.balanceOf(_borrower2), 52);
+ assertEq(_collateral.balanceOf(tokensReceiver), 3);
+ assertEq(_collateral.balanceOf(address(_pool)), 1);
+
+ // pool is owner of remaining pledged NFT
+ assertEq(_collateral.ownerOf(1), address(_pool));
+ // recipient is owner of 3 pulled NFTs
+ assertEq(_collateral.ownerOf(3), tokensReceiver);
+ assertEq(_collateral.ownerOf(5), tokensReceiver);
+ assertEq(_collateral.ownerOf(53), tokensReceiver);
}
function testPullCollateralNotInPool() external tearDown {
@@ -263,13 +304,11 @@ contract ERC721PoolCollateralTest is ERC721HelperContract {
tokenIdsToAdd[1] = 3;
tokenIdsToAdd[2] = 5;
- _pledgeCollateral(
- {
- from: _borrower,
- borrower: _borrower,
- tokenIds: tokenIdsToAdd
- }
- );
+ _pledgeCollateral({
+ from: _borrower,
+ borrower: _borrower,
+ tokenIds: tokenIdsToAdd
+ });
// pool is owner of pledged NFTs
assertEq(_collateral.ownerOf(1), address(_pool));
@@ -277,12 +316,10 @@ contract ERC721PoolCollateralTest is ERC721HelperContract {
assertEq(_collateral.ownerOf(5), address(_pool));
// should revert if borrower attempts to remove more collateral than pledged from pool
- _assertPullInsufficientCollateralRevert(
- {
- from: _borrower,
- amount: 5
- }
- );
+ _assertPullInsufficientCollateralRevert({
+ from: _borrower,
+ amount: 5
+ });
// borrower should be able to remove collateral in the pool
_repayDebtNoLupCheck({
@@ -299,27 +336,21 @@ contract ERC721PoolCollateralTest is ERC721HelperContract {
}
function testPullCollateralPartiallyEncumbered() external tearDown {
- _addInitialLiquidity(
- {
- from: _lender,
- amount: 10_000 * 1e18,
- index: 2552
- }
- );
- _addInitialLiquidity(
- {
- from: _lender,
- amount: 10_000 * 1e18,
- index: 2551
- }
- );
- _addInitialLiquidity(
- {
- from: _lender,
- amount: 10_000 * 1e18,
- index: 2550
- }
- );
+ _addInitialLiquidity({
+ from: _lender,
+ amount: 10_000 * 1e18,
+ index: 2552
+ });
+ _addInitialLiquidity({
+ from: _lender,
+ amount: 10_000 * 1e18,
+ index: 2551
+ });
+ _addInitialLiquidity({
+ from: _lender,
+ amount: 10_000 * 1e18,
+ index: 2550
+ });
// check initial token balances
assertEq(_collateral.balanceOf(_borrower), 52);
@@ -352,22 +383,18 @@ contract ERC721PoolCollateralTest is ERC721HelperContract {
tokenIdsToAdd[1] = 3;
tokenIdsToAdd[2] = 5;
- // borrower deposits three NFTs into the subset pool
- _pledgeCollateral(
- {
- from: _borrower,
- borrower: _borrower,
- tokenIds: tokenIdsToAdd
- }
- );
- _borrow(
- {
- from: _borrower,
- amount: 3_000 * 1e18,
- indexLimit: 2_551,
- newLup: _priceAt(2550)
- }
- );
+ // borrower deposits three NFTs into the pool
+ _pledgeCollateral({
+ from: _borrower,
+ borrower: _borrower,
+ tokenIds: tokenIdsToAdd
+ });
+ _borrow({
+ from: _borrower,
+ amount: 3_000 * 1e18,
+ indexLimit: 2_551,
+ newLup: _priceAt(2550)
+ });
// check token balances after borrow
assertEq(_collateral.balanceOf(_borrower), 49);
@@ -434,258 +461,212 @@ contract ERC721PoolCollateralTest is ERC721HelperContract {
function testPullCollateralOverlyEncumbered() external tearDown {
// lender deposits 10000 Quote into 3 buckets
- _addInitialLiquidity(
- {
- from: _lender,
- amount: 10_000 * 1e18,
- index: 2552
- }
- );
- _addInitialLiquidity(
- {
- from: _lender,
- amount: 10_000 * 1e18,
- index: 2551
- }
- );
- _addInitialLiquidity(
- {
- from: _lender,
- amount: 10_000 * 1e18,
- index: 2550
- }
- );
+ _addInitialLiquidity({
+ from: _lender,
+ amount: 10_000 * 1e18,
+ index: 2552
+ });
+ _addInitialLiquidity({
+ from: _lender,
+ amount: 10_000 * 1e18,
+ index: 2551
+ });
+ _addInitialLiquidity({
+ from: _lender,
+ amount: 10_000 * 1e18,
+ index: 2550
+ });
uint256[] memory tokenIdsToAdd = new uint256[](3);
tokenIdsToAdd[0] = 1;
tokenIdsToAdd[1] = 3;
tokenIdsToAdd[2] = 5;
- // borrower deposits three NFTs into the subset pool
- _pledgeCollateral(
- {
- from: _borrower,
- borrower: _borrower,
- tokenIds: tokenIdsToAdd
- }
- );
+ // borrower deposits three NFTs into the pool
+ _pledgeCollateral({
+ from: _borrower,
+ borrower: _borrower,
+ tokenIds: tokenIdsToAdd
+ });
// check collateralization after pledge
(uint256 poolDebt,,) = _pool.debtInfo();
assertEq(_encumberance(poolDebt, _lup()), 0);
// borrower borrows some quote
- _borrow(
- {
- from: _borrower,
- amount: 9_000 * 1e18,
- indexLimit: 2_551,
- newLup: _priceAt(2550)
- }
- );
+ _borrow({
+ from: _borrower,
+ amount: 9_000 * 1e18,
+ indexLimit: 2_551,
+ newLup: _priceAt(2550)
+ });
// check collateralization after borrow
(poolDebt,,) = _pool.debtInfo();
assertEq(_encumberance(poolDebt, _lup()), 2.992021560300836411 * 1e18);
// should revert if borrower attempts to pull more collateral than is unencumbered
- _assertPullInsufficientCollateralRevert(
- {
- from: _borrower,
- amount: 2
- }
- );
+ _assertPullInsufficientCollateralRevert({
+ from: _borrower,
+ amount: 2
+ });
}
function testAddRemoveCollateral() external tearDown {
// lender adds some liquidity
- _addInitialLiquidity(
- {
- from: _lender,
- amount: 10_000 * 1e18,
- index: 1692
- }
- );
- _addInitialLiquidity(
- {
- from: _lender,
- amount: 10_000 * 1e18,
- index: 1530
- }
- );
+ _addInitialLiquidity({
+ from: _lender,
+ amount: 10_000 * 1e18,
+ index: 1692
+ });
+ _addInitialLiquidity({
+ from: _lender,
+ amount: 10_000 * 1e18,
+ index: 1530
+ });
uint256[] memory tokenIds = new uint256[](2);
tokenIds[0] = 1;
tokenIds[1] = 5;
// add three tokens to a single bucket
- _addCollateral(
- {
- from: _borrower,
- tokenIds: tokenIds,
- index: 1530,
- lpAward: 975_232.505322350083963682 * 1e27
- }
- );
+ _addCollateral({
+ from: _borrower,
+ tokenIds: tokenIds,
+ index: 1530,
+ lpAward: 975_232.505322350083963682 * 1e18
+ });
// should revert if the actor does not have any LP to remove a token
- _assertRemoveCollateralInsufficientLPsRevert(
- {
- from: _borrower2,
- amount: 1,
- index: 1530
- }
- );
+ _assertRemoveCollateralInsufficientLPsRevert({
+ from: _borrower2,
+ amount: 1,
+ index: 1530
+ });
// should revert if we try to remove a token from a bucket with no collateral
- _assertRemoveInsufficientCollateralRevert(
- {
- from: _borrower,
- amount: 1,
- index: 1692
- }
- );
+ _assertRemoveInsufficientCollateralRevert({
+ from: _borrower,
+ amount: 1,
+ index: 1692
+ });
// remove one token
- _removeCollateral(
- {
- from: _borrower,
- amount: 1,
- index: 1530,
- lpRedeem: 487_616.252661175041981841 * 1e27
- }
- );
+ _removeCollateral({
+ from: _borrower,
+ amount: 1,
+ index: 1530,
+ lpRedeem: 487_616.252661175041981841 * 1e18
+ });
+
+ _assertBucket({
+ index: 1530,
+ lpBalance: 497_616.252661175041981841 * 1e18,
+ collateral: Maths.wad(1),
+ deposit: 10_000 * 1e18,
+ exchangeRate: 1 * 1e18
+ });
+ _assertLenderLpBalance({
+ lender: _borrower,
+ index: 1530,
+ lpBalance: 487_616.252661175041981841 * 1e18,
+ depositTime: _startTime
+ });
- _assertBucket(
- {
- index: 1530,
- lpBalance: 497_616.252661175041981841 * 1e27,
- collateral: Maths.wad(1),
- deposit: 10_000 * 1e18,
- exchangeRate: 1 * 1e27
- }
- );
- _assertLenderLpBalance(
- {
- lender: _borrower,
- index: 1530,
- lpBalance: 487_616.252661175041981841 * 1e27,
- depositTime: _startTime
- }
- );
// remove another token
- _removeCollateral(
- {
- from: _borrower,
- amount: 1,
- index: 1530,
- lpRedeem: 487_616.252661175041981841 * 1e27
- }
- );
+ _removeCollateral({
+ from: _borrower,
+ amount: 1,
+ index: 1530,
+ lpRedeem: 487_616.252661175041981841 * 1e18
+ });
- _assertBucket(
- {
- index: 1530,
- lpBalance: 10_000 * 1e27,
- collateral: 0,
- deposit: 10_000 * 1e18,
- exchangeRate: 1 * 1e27
- }
- );
- _assertLenderLpBalance(
- {
- lender: _borrower,
- index: 1530,
- lpBalance: 0,
- depositTime: _startTime
- }
- );
+ _assertBucket({
+ index: 1530,
+ lpBalance: 10_000 * 1e18,
+ collateral: 0,
+ deposit: 10_000 * 1e18,
+ exchangeRate: 1 * 1e18
+ });
+ _assertLenderLpBalance({
+ lender: _borrower,
+ index: 1530,
+ lpBalance: 0,
+ depositTime: _startTime
+ });
// lender removes quote token
skip(1 days); // skip to avoid penalty
- _removeAllLiquidity(
- {
- from: _lender,
- amount: 10_000 * 1e18,
- index: 1530,
- newLup: MAX_PRICE,
- lpRedeem: 10_000 * 1e27
- }
- );
- _assertBucket(
- {
- index: 1530,
- lpBalance: 0,
- collateral: 0,
- deposit: 0,
- exchangeRate: 1 * 1e27
- }
- );
+ _removeAllLiquidity({
+ from: _lender,
+ amount: 10_000 * 1e18,
+ index: 1530,
+ newLup: MAX_PRICE,
+ lpRedeem: 10_000 * 1e18
+ });
+
+ _assertBucket({
+ index: 1530,
+ lpBalance: 0,
+ collateral: 0,
+ deposit: 0,
+ exchangeRate: 1 * 1e18
+ });
}
function testMergeOrRemoveCollateral() external tearDown {
for (uint256 i = 3060; i < (3060 + 10); i++) {
- _addLiquidity(
- {
- from: _lender,
- amount: 20 * 1e18,
- index: i,
- newLup: MAX_PRICE,
- lpAward: 20 * 1e27
- }
- );
+ _addLiquidity({
+ from: _lender,
+ amount: 20 * 1e18,
+ index: i,
+ newLup: MAX_PRICE,
+ lpAward: 20 * 1e18
+ });
}
// borrower pledge collateral and draws debt
uint256[] memory tokenIdsToAdd = new uint256[](2);
tokenIdsToAdd[0] = 1;
tokenIdsToAdd[1] = 3;
- _pledgeCollateral(
- {
- from: _borrower,
- borrower: _borrower,
- tokenIds: tokenIdsToAdd
- }
- );
- _borrow(
- {
- from: _borrower,
- amount: 150 * 1e18,
- indexLimit: 8191,
- newLup: 228.476350374240318479 * 1e18
- }
- );
+ _pledgeCollateral({
+ from: _borrower,
+ borrower: _borrower,
+ tokenIds: tokenIdsToAdd
+ });
+ _borrow({
+ from: _borrower,
+ amount: 150 * 1e18,
+ indexLimit: MAX_FENWICK_INDEX,
+ newLup: 228.476350374240318479 * 1e18
+ });
// Borrower starts with possession of tokens 1 and 3
uint256[] memory borrowerTokenIds = new uint256[](2);
borrowerTokenIds[0] = 1;
borrowerTokenIds[1] = 3;
- _assertBorrower(
- {
- borrower: _borrower,
- borrowerDebt: 150.144230769230769300 * 1e18,
- borrowerCollateral: 2.0 * 1e18,
- borrowert0Np: 78.825721153846153882 * 1e18,
- borrowerCollateralization: 3.043424968161510485 * 1e18,
- tokenIds: borrowerTokenIds
- }
- );
+ _assertBorrower({
+ borrower: _borrower,
+ borrowerDebt: 150.144230769230769300 * 1e18,
+ borrowerCollateral: 2.0 * 1e18,
+ borrowert0Np: 78.825721153846153882 * 1e18,
+ borrowerCollateralization: 3.043424968161510485 * 1e18,
+ tokenIds: borrowerTokenIds
+ });
// skip to render borrower undercollateralized
skip(10000 days);
- _kick(
- {
- from: _lender,
- borrower: _borrower,
- debt: 598.174133241016922933 * 1e18,
- collateral: 2.0 * 1e18,
- bond: 5.907892673985352325 * 1e18,
- transferAmount: 5.907892673985352325 * 1e18
- }
- );
+ _kick({
+ from: _lender,
+ borrower: _borrower,
+ debt: 598.174133241016922932 * 1e18,
+ collateral: 2.0 * 1e18,
+ bond: 5.907892673985352325 * 1e18,
+ transferAmount: 5.907892673985352325 * 1e18
+ });
skip(32 hours);
@@ -724,50 +705,58 @@ contract ERC721PoolCollateralTest is ERC721HelperContract {
})
);
+ // force an interest accumulation to assert bucket with interest
+ _addLiquidity({
+ from: _lender,
+ amount: 0 * 1e18,
+ index: 7000,
+ newLup: 99836282890,
+ lpAward: 0
+ });
+ _assertBucket({
+ index: 3060,
+ lpBalance: 20 * 1e18,
+ collateral: 0.0000000000000000000 * 1e18,
+ deposit: 20.010216420146293860 * 1e18,
+ exchangeRate: 1.000510821007314693 * 1e18
+ });
+
// Before depositTake: NFTs pledged by liquidated borrower are owned by the borrower in the pool
assertEq(_collateral.ownerOf(1), address(_pool));
assertEq(_collateral.ownerOf(3), address(_pool));
// exchange collateral for lpb 3060 - 3070, going down in price
for (uint256 i = _i236_59; i < (3060 + 10); i++) {
- _depositTake(
- {
- from: _lender,
- borrower: _borrower,
- index: i
- }
- );
+ _depositTake({
+ from: _lender,
+ borrower: _borrower,
+ index: i
+ });
}
- _assertBucket(
- {
- index: 3060,
- lpBalance: 20.2 * 1e27,
- collateral: 0.085430491711717314 * 1e18,
- deposit: 0,
- exchangeRate: 1.000610882095524250072170475 * 1e27
- }
- );
+ _assertBucket({
+ index: 3060,
+ lpBalance: 20.202020202020202022 * 1e18,
+ collateral: 0.085430491711717314 * 1e18,
+ deposit: 0,
+ exchangeRate: 1.000510821007314698 * 1e18
+ });
- _assertBucket(
- {
- index: 3061,
- lpBalance: 20.2 * 1e27,
- collateral: 0.085857644170275899 * 1e18,
- deposit: 0,
- exchangeRate: 1.000610882095524239992886155 * 1e27
- }
- );
+ _assertBucket({
+ index: 3061,
+ lpBalance: 20.202020202020202019 * 1e18,
+ collateral: 0.085857644170275899 * 1e18,
+ deposit: 0,
+ exchangeRate: 1.000510821007314688 * 1e18
+ });
- _assertBucket(
- {
- index: 3070,
- lpBalance: 0,
- collateral: 0,
- deposit: 0,
- exchangeRate: 1 * 1e27
- }
- );
+ _assertBucket({
+ index: 3070,
+ lpBalance: 0,
+ collateral: 0,
+ deposit: 0,
+ exchangeRate: 1 * 1e18
+ });
_assertAuction(
AuctionParams({
@@ -795,7 +784,7 @@ contract ERC721PoolCollateralTest is ERC721HelperContract {
encumberedCollateral: 4407944209.541175956055268556 * 1e18,
poolDebt: 440.072765067090279852 * 1e18,
actualUtilization: 0,
- targetUtilization: 3_123_578_486.651416548727612650 * 1e18,
+ targetUtilization: 2_996_091_127.870826153174895975 * 1e18,
minDebtAmount: 0,
loans: 0,
maxBorrower: address(0),
@@ -808,51 +797,52 @@ contract ERC721PoolCollateralTest is ERC721HelperContract {
borrowerTokenIds = new uint256[](1);
borrowerTokenIds[0] = 1;
- _assertBorrower(
- {
- borrower: _borrower,
- borrowerDebt: 440.072765067090279852 * 1e18,
- borrowerCollateral: 1.126214674710621229 * 1e18,
- borrowert0Np: 78.825721153846153882 * 1e18,
- borrowerCollateralization: 0.000000000255496581 * 1e18,
- tokenIds: borrowerTokenIds
- }
- );
+ _assertBorrower({
+ borrower: _borrower,
+ borrowerDebt: 440.072765067090279852 * 1e18,
+ borrowerCollateral: 1.126214674710621229 * 1e18,
+ borrowert0Np: 78.825721153846153882 * 1e18,
+ borrowerCollateralization: 0.000000000255496581 * 1e18,
+ tokenIds: borrowerTokenIds
+ });
// after depositTake but before take: NFTs pledged by liquidated borrower are owned by the pool
assertEq(_collateral.ownerOf(1), address(_pool));
assertEq(_collateral.ownerOf(3), address(_pool));
- _take(
- {
- from: _lender,
- borrower: _borrower,
- maxCollateral: 2.0 * 1e18,
- bondChange: 0.000000052051493471 * 1e18,
- givenAmount: 0.000005205149347131 * 1e18,
- collateralTaken: 1.126214674710621229 * 1e18,
- isReward: true
- }
- );
+ _take({
+ from: _lender,
+ borrower: _borrower,
+ maxCollateral: 2 * 1e18,
+ bondChange: 0.000000046218092021 * 1e18,
+ givenAmount: 0.000004621809202112 * 1e18,
+ collateralTaken: 1 * 1e18,
+ isReward: true
+ });
// Borrower has < 1 collateral, no NFTs in possession
borrowerTokenIds = new uint256[](0);
- _assertBorrower(
- {
- borrower: _borrower,
- borrowerDebt: 440.072759913992426192 * 1e18,
- borrowerCollateral: 0.126214674710621229 * 1e18,
- borrowert0Np: 78.825721153846153882 * 1e18,
- borrowerCollateralization: 0.000000000028633456 * 1e18,
- tokenIds: borrowerTokenIds
- }
- );
+ _assertBorrower({
+ borrower: _borrower,
+ borrowerDebt: 440.072760491499169762 * 1e18,
+ borrowerCollateral: 0.126214674710621229 * 1e18,
+ borrowert0Np: 78.825721153846153882 * 1e18,
+ borrowerCollateralization: 0.000000000028633456 * 1e18,
+ tokenIds: borrowerTokenIds
+ });
// after take: NFT with ID 1, pledged by liquidated borrower is owned by the taker
assertEq(_collateral.ownerOf(1), address(_pool));
assertEq(_collateral.ownerOf(3), _lender);
+ // subsequent take should fail as there's less than 1 NFT remaining in the loan
+ _assertTakeInsufficentCollateralRevert({
+ from: _lender,
+ borrower: _borrower2,
+ maxCollateral: 1 * 1e18
+ });
+
// 70.16 hours
skip(4210 minutes);
@@ -861,37 +851,33 @@ contract ERC721PoolCollateralTest is ERC721HelperContract {
borrower: _borrower,
active: true,
kicker: address(_lender),
- bondSize: 5.907892726036845796 * 1e18,
+ bondSize: 5.907892720203444346 * 1e18,
bondFactor: 0.010 * 1e18,
kickTime: block.timestamp - (32 hours + 4210 minutes),
kickMomp: 0.000000099836282890 * 1e18,
- totalBondEscrowed: 5.907892726036845796 * 1e18,
+ totalBondEscrowed: 5.907892720203444346 * 1e18,
auctionPrice: 0 * 1e18,
- debtInAuction: 440.072759913992426192 * 1e18,
- thresholdPrice: 3_488.390484128255500242 * 1e18,
+ debtInAuction: 440.072760491499169762 * 1e18,
+ thresholdPrice: 3_488.390488706064472517 * 1e18,
neutralPrice: 310.164365384230997074 * 1e18
})
);
- _settle(
- {
- from: _lender,
- borrower: _borrower,
- maxDepth: 10,
- settledDebt: 111.818402566884385900 * 1e18
- }
- );
+ _settle({
+ from: _lender,
+ borrower: _borrower,
+ maxDepth: 10,
+ settledDebt: 111.818402713623487748 * 1e18
+ });
- _assertBorrower(
- {
- borrower: _borrower,
- borrowerDebt: 0,
- borrowerCollateral: 0,
- borrowert0Np: 78.825721153846153882 * 1e18,
- borrowerCollateralization: 1.0 * 1e18,
- tokenIds: borrowerTokenIds
- }
- );
+ _assertBorrower({
+ borrower: _borrower,
+ borrowerDebt: 0,
+ borrowerCollateral: 0,
+ borrowert0Np: 78.825721153846153882 * 1e18,
+ borrowerCollateralization: 1.0 * 1e18,
+ tokenIds: borrowerTokenIds
+ });
// after take: NFT, 1 pledged by liquidated borrower is owned by the taker
assertEq(_collateral.ownerOf(1), address(_pool));
@@ -914,49 +900,40 @@ contract ERC721PoolCollateralTest is ERC721HelperContract {
})
);
- _assertBucket(
- {
- index: 3060,
- lpBalance: 20.2 * 1e27,
- collateral: 0.085430491711717314 * 1e18,
- deposit: 0,
- exchangeRate: 1.000610882095524250072170475 * 1e27
- }
- );
- _assertBucket(
- {
- index: 3069,
- lpBalance: 20.2 * 1e27,
- collateral: 0.089352655062849951 * 1e18,
- deposit: 0,
- exchangeRate: 1.000610882095524241676916623 * 1e27
- }
- );
- _assertLenderLpBalance(
- {
- lender: _lender,
- index: 3069,
- lpBalance: 20.2 * 1e27,
- depositTime: _startTime + 10000 days + 32 hours
- }
- );
- _assertBucket(
- {
- index: 7388,
- lpBalance: 0.000000012600803969278909906 * 1e27, // LPs awarded to borrower for settled collateral
- collateral: 0.126214674710621229 * 1e18, // settled collateral amount
- deposit: 0,
- exchangeRate: 1.000000000000000000006624324 * 1e27
- }
- );
- _assertLenderLpBalance(
- {
- lender: _borrower,
- index: 7388,
- lpBalance: 0.000000012600803969278909906 * 1e27,
- depositTime: _startTime + 10000 days + 32 hours + 4210 minutes
- }
- );
+ _assertBucket({
+ index: 3060,
+ lpBalance: 20.202020202020202022 * 1e18,
+ collateral: 0.085430491711717314 * 1e18,
+ deposit: 0,
+ exchangeRate: 1.000510821007314698 * 1e18
+ });
+ _assertBucket({
+ index: 3069,
+ lpBalance: 20.202020202020202020 * 1e18,
+ collateral: 0.089352655062849951 * 1e18,
+ deposit: 0,
+ exchangeRate: 1.000510821007314689 * 1e18
+ });
+ _assertLenderLpBalance({
+ lender: _lender,
+ index: 3069,
+ lpBalance: 20.202020202020202020 * 1e18,
+ depositTime: _startTime + 10000 days + 32 hours
+ });
+ _assertBucket({
+ index: 7388,
+ lpBalance: 0.000000012600803969 * 1e18, // LPs awarded to borrower for settled collateral
+ collateral: 0.126214674710621229 * 1e18, // settled collateral amount
+ deposit: 0,
+ exchangeRate: 1 * 1e18
+ });
+ _assertLenderLpBalance({
+ lender: _borrower,
+ index: 7388,
+ lpBalance: 0.000000012600803969 * 1e18,
+ depositTime: _startTime + 10000 days + 32 hours + 4210 minutes
+ });
+
assertEq(_collateral.balanceOf(_lender), 1);
assertEq(_collateral.balanceOf(_borrower), 50);
assertEq(_collateral.balanceOf(address(_pool)), 1);
@@ -968,183 +945,149 @@ contract ERC721PoolCollateralTest is ERC721HelperContract {
removalIndexes[removalI] = i;
removalI++;
}
+
// Reverts because 3059 is a higher price than 3060, must merge down in price
- _assertCannotMergeToHigherPriceRevert(
- {
- from: _lender,
- toIndex: 3059,
- noOfNFTsToRemove: 1.0,
- removeCollateralAtIndex: removalIndexes
- }
- );
+ _assertCannotMergeToHigherPriceRevert({
+ from: _lender,
+ toIndex: 3059,
+ noOfNFTsToRemove: 1.0,
+ removeCollateralAtIndex: removalIndexes
+ });
- _mergeOrRemoveCollateral(
- {
- from: _lender,
- toIndex: 3069,
- noOfNFTsToRemove: 1.0,
- collateralMerged: 0.873785325289378771 * 1e18,
- removeCollateralAtIndex: removalIndexes,
- toIndexLps: 197.657763058028917103677822434 * 1e27
- }
- );
+ _mergeOrRemoveCollateral({
+ from: _lender,
+ toIndex: 3070,
+ noOfNFTsToRemove: 1.0,
+ collateralMerged: 0.873785325289378771 * 1e18,
+ removeCollateralAtIndex: removalIndexes,
+ toIndexLps: 196.674391102516337019 * 1e18
+ });
- _assertBucket(
- {
- index: 3060,
- lpBalance: 0,
- collateral: 0,
- deposit: 0,
- exchangeRate: 1.0 * 1e27
- }
- );
- _assertBucket(
- {
- index: 3061,
- lpBalance: 0,
- collateral: 0,
- deposit: 0,
- exchangeRate: 1.0 * 1e27
- }
- );
- _assertBucket(
- {
- index: 3069,
- lpBalance: 197.657763058028917103677822434 * 1e27, // new LPs amount accounting collateral merged in bucket
- collateral: 0.873785325289378771 * 1e18, // reflects collateral merged in the bucket
- deposit: 0,
- exchangeRate: 0.999999999999999999999999999 * 1e27
- }
- );
- _assertLenderLpBalance(
- {
- lender: _lender,
- index: 3069,
- lpBalance: 197.657763058028917103677822434 * 1e27,
- depositTime: _startTime + 10000 days + 32 hours + 4210 minutes
- }
- );
- _assertBucket(
- {
- index: 7388,
- lpBalance: 0.000000012600803969278909906 * 1e27, // LPs awarded to borrower for settled collateral
- collateral: 0.126214674710621229 * 1e18, // settled collateral amount
- deposit: 0,
- exchangeRate: 1.000000000000000000006624324 * 1e27
- }
- );
+ _assertBucket({
+ index: 3060,
+ lpBalance: 0,
+ collateral: 0,
+ deposit: 0,
+ exchangeRate: 1 * 1e18
+ });
+ _assertBucket({
+ index: 3061,
+ lpBalance: 0,
+ collateral: 0,
+ deposit: 0,
+ exchangeRate: 1 * 1e18
+ });
+ _assertBucket({
+ index: 3070,
+ lpBalance: 196.674391102516337019 * 1e18, // new LPs amount accounting collateral merged in bucket
+ collateral: 0.873785325289378771 * 1e18, // reflects collateral merged in the bucket
+ deposit: 0,
+ exchangeRate: 1 * 1e18
+ });
+ _assertLenderLpBalance({
+ lender: _lender,
+ index: 3070,
+ lpBalance: 196.674391102516337019 * 1e18,
+ depositTime: _startTime + 10000 days + 32 hours + 4210 minutes
+ });
+ _assertBucket({
+ index: 7388,
+ lpBalance: 0.000000012600803969 * 1e18, // LPs awarded to borrower for settled collateral
+ collateral: 0.126214674710621229 * 1e18, // settled collateral amount
+ deposit: 0,
+ exchangeRate: 1 * 1e18
+ });
assertEq(_collateral.balanceOf(_lender), 1);
assertEq(_collateral.balanceOf(_borrower), 50);
assertEq(_collateral.balanceOf(address(_pool)), 1);
// lender deposit quote tokens in bucket 7388 in order to claim and merge settled collateral and to be able to remove entire NFT
- _addLiquidity(
- {
- from: _lender,
- amount: 10 * 1e18,
- index: 7388,
- lpAward: 9.999999999999999999933756760 * 1e27, // LPs awarded to lender for depositing quote tokens in bucket 7388
- newLup: MAX_PRICE
- }
- );
- _assertLenderLpBalance(
- {
- lender: _lender,
- index: 7388,
- lpBalance: 9.999999999999999999933756760 * 1e27, // lender now owns LPs in bucket 7388 which can be used to merge bucket collateral
- depositTime: _startTime + 10000 days + (32 hours + 4210 minutes)
- }
- );
+ _addLiquidity({
+ from: _lender,
+ amount: 10 * 1e18,
+ index: 7388,
+ lpAward: 10 * 1e18, // LPs awarded to lender for depositing quote tokens in bucket 7388
+ newLup: MAX_PRICE
+ });
+
+ _assertLenderLpBalance({
+ lender: _lender,
+ index: 7388,
+ lpBalance: 10 * 1e18, // lender now owns LPs in bucket 7388 which can be used to merge bucket collateral
+ depositTime: _startTime + 10000 days + (32 hours + 4210 minutes)
+ });
// collateral is now splitted accross buckets 3069 and 7388
uint256[] memory allRemovalIndexes = new uint256[](2);
- allRemovalIndexes[0] = 3069;
+ allRemovalIndexes[0] = 3070;
allRemovalIndexes[1] = 7388;
- _mergeOrRemoveCollateral(
- {
- from: _lender,
- toIndex: 7388,
- noOfNFTsToRemove: 1,
- collateralMerged: 1 * 1e18,
- removeCollateralAtIndex: allRemovalIndexes,
- toIndexLps: 0
- }
- );
+ _mergeOrRemoveCollateral({
+ from: _lender,
+ toIndex: 7388,
+ noOfNFTsToRemove: 1,
+ collateralMerged: 1 * 1e18,
+ removeCollateralAtIndex: allRemovalIndexes,
+ toIndexLps: 0
+ });
- _assertBucket(
- {
- index: 3060,
- lpBalance: 0,
- collateral: 0,
- deposit: 0,
- exchangeRate: 1.0 * 1e27
- }
- );
- _assertBucket(
- {
- index: 3061,
- lpBalance: 0,
- collateral: 0,
- deposit: 0,
- exchangeRate: 1.0 * 1e27
- }
- );
- _assertBucket(
- {
- index: 3069,
- lpBalance: 0,
- collateral: 0,
- deposit: 0,
- exchangeRate: 1.0 * 1e27
- }
- );
- _assertBucket(
- {
- index: 7388,
- lpBalance: 10.000000000000000000212666669 * 1e27, // LPs in bucket 7388 diminished when NFT merged and removed
- collateral: 0, // no collateral remaining as it was merged and removed
- deposit: 10 * 1e18,
- exchangeRate: 0.999999999999999999978733333 * 1e27
- }
- );
- _assertLenderLpBalance(
- {
- lender: _lender,
- index: 7388,
- lpBalance: 9.999999987399196030933756763 * 1e27, // lender LPs decreased with the amount used to merge NFT
- depositTime: _startTime + 10000 days + (32 hours + 4210 minutes)
- }
- );
- _assertLenderLpBalance(
- {
- lender: _borrower,
- index: 7388,
- lpBalance: 0.000000012600803969278909906 * 1e27, // Borrower LPs remain the same in the bucket
- depositTime: _startTime + 10000 days + (32 hours + 4210 minutes)
- }
- );
+ _assertBucket({
+ index: 3060,
+ lpBalance: 0,
+ collateral: 0,
+ deposit: 0,
+ exchangeRate: 1.0 * 1e18
+ });
+ _assertBucket({
+ index: 3061,
+ lpBalance: 0,
+ collateral: 0,
+ deposit: 0,
+ exchangeRate: 1.0 * 1e18
+ });
+ _assertBucket({
+ index: 3070,
+ lpBalance: 0,
+ collateral: 0,
+ deposit: 0,
+ exchangeRate: 1.0 * 1e18
+ });
+ _assertBucket({
+ index: 7388,
+ lpBalance: 10 * 1e18, // LPs in bucket 7388 diminished when NFT merged and removed
+ collateral: 0, // no collateral remaining as it was merged and removed
+ deposit: 10 * 1e18,
+ exchangeRate: 1 * 1e18
+ });
+ _assertLenderLpBalance({
+ lender: _lender,
+ index: 7388,
+ lpBalance: 9.999999987399196031 * 1e18, // lender LPs decreased with the amount used to merge NFT
+ depositTime: _startTime + 10000 days + (32 hours + 4210 minutes)
+ });
+ _assertLenderLpBalance({
+ lender: _borrower,
+ index: 7388,
+ lpBalance: 0.000000012600803969 * 1e18, // Borrower LPs remain the same in the bucket
+ depositTime: _startTime + 10000 days + (32 hours + 4210 minutes)
+ });
- _removeAllLiquidity(
- {
- from: _lender,
- amount: 9.987201910492245717 * 1e18,
- index: 7388,
- newLup: MAX_PRICE,
- lpRedeem: 9.999999987399196030933756763 * 1e27
- }
- );
+ _removeAllLiquidity({
+ from: _lender,
+ amount: 9.987201910492245717 * 1e18,
+ index: 7388,
+ newLup: MAX_PRICE,
+ lpRedeem: 9.999999987399196031 * 1e18
+ });
- _assertBucket(
- {
- index: 7388,
- lpBalance: 0.000000012600803969278909906 * 1e27, // LPs in bucket 7388 diminished when NFT merged and removed
- collateral: 0, // no collateral remaining as it was merged and removed
- deposit: 0.000000012600803969 * 1e18,
- exchangeRate: 0.999999999977865705499427682 * 1e27
- }
- );
+ _assertBucket({
+ index: 7388,
+ lpBalance: 0.000000012600803969 * 1e18, // LPs in bucket 7388 diminished when NFT merged and removed
+ collateral: 0, // no collateral remaining as it was merged and removed
+ deposit: 0.000000012600803969 * 1e18,
+ exchangeRate: 1 * 1e18
+ });
_assertPool(
PoolParams({
@@ -1155,7 +1098,7 @@ contract ERC721PoolCollateralTest is ERC721HelperContract {
encumberedCollateral: 0,
poolDebt: 0,
actualUtilization: 0,
- targetUtilization: 3123578486.651416548727612650 * 1e18,
+ targetUtilization: 2_996_091_127.870826153174895975 * 1e18,
minDebtAmount: 0,
loans: 0,
maxBorrower: address(0),
@@ -1167,6 +1110,61 @@ contract ERC721PoolCollateralTest is ERC721HelperContract {
assertEq(_collateral.balanceOf(_lender), 2);
assertEq(_collateral.balanceOf(_borrower), 50);
assertEq(_collateral.balanceOf(address(_pool)), 0);
+ }
+}
+contract ERC721SubsetPoolCollateralTest is ERC721PoolCollateralTest {
+
+ function setUp() override external {
+ _borrower = makeAddr("borrower");
+ _borrower2 = makeAddr("borrower2");
+ _lender = makeAddr("lender");
+ _lender2 = makeAddr("lender2");
+
+ // deploy subset pool
+ uint256[] memory subsetTokenIds = new uint256[](5);
+ subsetTokenIds[0] = 1;
+ subsetTokenIds[1] = 3;
+ subsetTokenIds[2] = 5;
+ subsetTokenIds[3] = 51;
+ subsetTokenIds[4] = 53;
+ _pool = _deploySubsetPool(subsetTokenIds);
+
+ _mintAndApproveQuoteTokens(_lender, 200_000 * 1e18);
+ _mintAndApproveQuoteTokens(_borrower, 100 * 1e18);
+
+ _mintAndApproveCollateralTokens(_borrower, 52);
+ _mintAndApproveCollateralTokens(_borrower2, 53);
+ }
+
+ /********************************/
+ /*** ERC721 Subset Pool Tests ***/
+ /********************************/
+
+ function testPledgeCollateralNotInSubset() external tearDown {
+ uint256[] memory tokenIdsToAdd = new uint256[](3);
+ tokenIdsToAdd[0] = 2;
+ tokenIdsToAdd[1] = 4;
+ tokenIdsToAdd[2] = 6;
+
+ // should revert if borrower attempts to add tokens not in the pool subset
+ _assertPledgeCollateralNotInSubsetRevert({
+ from: _borrower,
+ tokenIds: tokenIdsToAdd
+ });
+ }
+
+ function testRemoveCollateralReverts() external tearDown {
+ uint256 testIndex = 6248;
+ uint256[] memory tokenIdsToAdd = new uint256[](2);
+ tokenIdsToAdd[0] = 3;
+ tokenIdsToAdd[1] = 5;
+
+ _assertAddCollateralExpiredRevert({
+ from: _lender,
+ tokenIds: tokenIdsToAdd,
+ index: testIndex,
+ expiry: block.timestamp - 15
+ });
}
}
diff --git a/tests/forge/ERC721Pool/ERC721PoolFactory.t.sol b/tests/forge/ERC721Pool/ERC721PoolFactory.t.sol
index 19539ff7e..b8b5cc45e 100644
--- a/tests/forge/ERC721Pool/ERC721PoolFactory.t.sol
+++ b/tests/forge/ERC721Pool/ERC721PoolFactory.t.sol
@@ -2,12 +2,13 @@
pragma solidity 0.8.14;
import { ERC721HelperContract } from './ERC721DSTestPlus.sol';
-import { NFTCollateralToken, Token } from '../utils/Tokens.sol';
+import { NFTCollateralToken, TokenWithNDecimals } from '../utils/Tokens.sol';
import { ERC721Pool } from 'src/ERC721Pool.sol';
import { ERC721PoolFactory } from 'src/ERC721PoolFactory.sol';
import { IPoolErrors } from 'src/interfaces/pool/commons/IPoolErrors.sol';
import { IPoolFactory } from 'src/interfaces/pool/IPoolFactory.sol';
+import { IERC721PoolFactory } from 'src/interfaces/pool/erc721/IERC721PoolFactory.sol';
contract ERC721PoolFactoryTest is ERC721HelperContract {
address internal _NFTCollectionPoolAddress;
@@ -23,7 +24,7 @@ contract ERC721PoolFactoryTest is ERC721HelperContract {
function setUp() external {
_startTime = block.timestamp;
_collateral = new NFTCollateralToken();
- _quote = new Token("Quote", "Q");
+ _quote = new TokenWithNDecimals("Quote", "Q", 18);
// deploy factory
_factory = new ERC721PoolFactory(_ajna);
@@ -89,24 +90,20 @@ contract ERC721PoolFactoryTest is ERC721HelperContract {
function testDeployERC721CollectionPoolWithZeroAddress() external {
// should revert if trying to deploy with zero address as collateral
- _assertDeployWith0xAddressRevert(
- {
- poolFactory: address(_factory),
- collateral: address(0),
- quote: address(_quote),
- interestRate: 0.05 * 10**18
- }
- );
+ _assertDeployWith0xAddressRevert({
+ poolFactory: address(_factory),
+ collateral: address(0),
+ quote: address(_quote),
+ interestRate: 0.05 * 10**18
+ });
// should revert if trying to deploy with zero address as quote token
- _assertDeployWith0xAddressRevert(
- {
- poolFactory: address(_factory),
- collateral: address(_collateral),
- quote: address(0),
- interestRate: 0.05 * 10**18
- }
- );
+ _assertDeployWith0xAddressRevert({
+ poolFactory: address(_factory),
+ collateral: address(_collateral),
+ quote: address(0),
+ interestRate: 0.05 * 10**18
+ });
// check tracking of deployed pools
assertEq(_factory.getDeployedPoolsList().length, 3);
@@ -115,52 +112,73 @@ contract ERC721PoolFactoryTest is ERC721HelperContract {
function testDeployERC721CollectionPoolWithInvalidRate() external {
// should revert if trying to deploy with interest rate lower than accepted
- _assertDeployWithInvalidRateRevert(
- {
- poolFactory: address(_factory),
- collateral: address(_quote),
- quote: address(_quote),
- interestRate: 10**18
- }
- );
+ _assertDeployWithInvalidRateRevert({
+ poolFactory: address(_factory),
+ collateral: address(_quote),
+ quote: address(_quote),
+ interestRate: 10**18
+ });
// should revert if trying to deploy with interest rate higher than accepted
- _assertDeployWithInvalidRateRevert(
- {
- poolFactory: address(_factory),
- collateral: address(_quote),
- quote: address(_quote),
- interestRate: 2 * 10**18
- }
- );
+ _assertDeployWithInvalidRateRevert({
+ poolFactory: address(_factory),
+ collateral: address(_quote),
+ quote: address(_quote),
+ interestRate: 2 * 10**18
+ });
// check tracking of deployed pools
assertEq(_factory.getDeployedPoolsList().length, 3);
assertEq(_factory.getNumberOfDeployedPools(), 3);
}
+ // FIXME: This test is exceeding block gas limit
function testDeployERC721CollectionPoolWithNonNFTAddress() external {
// should revert if trying to deploy with non NFT
- _assertDeployWithNonNFTRevert(
- {
- poolFactory: address(_factory),
- collateral: address(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2),
- quote: address(_quote),
- interestRate: 0.05 * 10**18
- }
- );
+ _assertDeployWithNonNFTRevert({
+ poolFactory: address(_factory),
+ collateral: address(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2),
+ quote: address(_quote),
+ interestRate: 0.05 * 10**18
+ });
}
function testDeployERC721PoolMultipleTimes() external {
// should revert if trying to deploy same pool one more time
- _assertDeployMultipleTimesRevert(
- {
- poolFactory: address(_factory),
- collateral: address(_collateral),
- quote: address(_quote),
- interestRate: 0.05 * 10**18
- }
+ _assertDeployMultipleTimesRevert({
+ poolFactory: address(_factory),
+ collateral: address(_collateral),
+ quote: address(_quote),
+ interestRate: 0.05 * 10**18
+ });
+ }
+
+ function testDeployERC721PoolWithMinRate() external {
+ uint256[] memory tokenIds = new uint256[](0);
+ _factory.deployPool(
+ address(new NFTCollateralToken()),
+ address(new TokenWithNDecimals("Quote", "Q1", 18)),
+ tokenIds,
+ 0.01 * 10**18
+ );
+
+ // check tracking of deployed pools
+ assertEq(_factory.getDeployedPoolsList().length, 4);
+ assertEq(_factory.getNumberOfDeployedPools(), 4);
+ }
+
+ function testDeployERC721PoolWithMaxRate() external {
+ uint256[] memory tokenIds = new uint256[](0);
+ _factory.deployPool(
+ address(new NFTCollateralToken()),
+ address(new TokenWithNDecimals("Quote", "Q1", 18)),
+ tokenIds,
+ 0.1 * 10**18
);
+
+ // check tracking of deployed pools
+ assertEq(_factory.getDeployedPoolsList().length, 4);
+ assertEq(_factory.getNumberOfDeployedPools(), 4);
}
function testDeployERC721CollectionPool() external {
@@ -188,6 +206,32 @@ contract ERC721PoolFactoryTest is ERC721HelperContract {
/*** ERC721 Collection Subset Tests ***/
/**************************************/
+ function testGetNFTSubsetHashTokenOrdering() external {
+ uint256[] memory tokenIdsOne = new uint256[](3);
+ tokenIdsOne[0] = 1;
+ tokenIdsOne[1] = 70;
+ tokenIdsOne[2] = 3;
+ uint256[] memory tokenIdsTwo = new uint256[](3);
+ tokenIdsTwo[0] = 1;
+ tokenIdsTwo[1] = 2;
+ tokenIdsTwo[2] = 3;
+ uint256[] memory tokenIdsThree = new uint256[](3);
+ tokenIdsThree[0] = 1;
+ tokenIdsThree[1] = 2;
+ tokenIdsThree[2] = 2;
+
+ // check sort order
+ vm.expectRevert(IERC721PoolFactory.TokenIdSubsetInvalid.selector);
+ _factory.getNFTSubsetHash(tokenIdsOne);
+
+ // check valid subset hash
+ assertEq(_factory.getNFTSubsetHash(tokenIdsTwo), keccak256(abi.encode(tokenIdsTwo)));
+
+ // check for duplicate tokenIds
+ vm.expectRevert(IERC721PoolFactory.TokenIdSubsetInvalid.selector);
+ _factory.getNFTSubsetHash(tokenIdsThree);
+ }
+
function testDeployERC721SubsetPoolWithZeroAddress() external {
uint256[] memory tokenIdsTestSubset = new uint256[](3);
tokenIdsTestSubset[0] = 1;
diff --git a/tests/forge/ERC721Pool/ERC721FlashloanQuote.t.sol b/tests/forge/ERC721Pool/ERC721PoolFlashloan.t.sol
similarity index 89%
rename from tests/forge/ERC721Pool/ERC721FlashloanQuote.t.sol
rename to tests/forge/ERC721Pool/ERC721PoolFlashloan.t.sol
index 0838f8b44..bb0e01d3e 100644
--- a/tests/forge/ERC721Pool/ERC721FlashloanQuote.t.sol
+++ b/tests/forge/ERC721Pool/ERC721PoolFlashloan.t.sol
@@ -31,32 +31,28 @@ contract ERC721PoolFlashloanTest is ERC721HelperContract {
_bucketPrice = 251.186576139566121965 * 1e18;
_bucketId = _indexOf(_bucketPrice);
assertEq(_bucketId, 3048);
- _addInitialLiquidity(
- {
- from: _lender,
- amount: 300 * 1e18,
- index: _bucketId
- }
- );
+
+ _addInitialLiquidity({
+ from: _lender,
+ amount: 300 * 1e18,
+ index: _bucketId
+ });
// borrower draws debt
uint256[] memory tokenIdsToAdd = new uint256[](1);
tokenIdsToAdd[0] = 1;
- _pledgeCollateral(
- {
- from: _borrower,
- borrower: _borrower,
- tokenIds: tokenIdsToAdd
- }
- );
- _borrow(
- {
- from: _borrower,
- amount: 200 * 1e18,
- indexLimit: _bucketId,
- newLup: _bucketPrice
- }
- );
+ _pledgeCollateral({
+ from: _borrower,
+ borrower: _borrower,
+ tokenIds: tokenIdsToAdd
+ });
+ _borrow({
+ from: _borrower,
+ amount: 200 * 1e18,
+ indexLimit: _bucketId,
+ newLup: _bucketPrice
+ });
+
(uint256 poolDebt,,) = _pool.debtInfo();
assertEq(poolDebt, 200.192307692307692400 * 1e18);
}
diff --git a/tests/forge/ERC721Pool/ERC721PoolInterest.t.sol b/tests/forge/ERC721Pool/ERC721PoolInterest.t.sol
index 806ebb9ee..d1a2df150 100644
--- a/tests/forge/ERC721Pool/ERC721PoolInterest.t.sol
+++ b/tests/forge/ERC721Pool/ERC721PoolInterest.t.sol
@@ -47,41 +47,32 @@ contract ERC721PoolSubsetInterestTest is ERC721PoolInterestTest {
}
function testBorrowerInterestCalculation() external tearDown {
- _addInitialLiquidity(
- {
- from: _lender,
- amount: 10_000 * 1e18,
- index: 2550
- }
- );
- _addInitialLiquidity(
- {
- from: _lender,
- amount: 10_000 * 1e18,
- index: 2551
- }
- );
- _addInitialLiquidity(
- {
- from: _lender,
- amount: 10_000 * 1e18,
- index: 2552
- }
- );
- _addInitialLiquidity(
- {
- from: _lender,
- amount: 10_000 * 1e18,
- index: 2553
- }
- );
- _addInitialLiquidity(
- {
- from: _lender,
- amount: 10_000 * 1e18,
- index: 2554
- }
- );
+ _addInitialLiquidity({
+ from: _lender,
+ amount: 10_000 * 1e18,
+ index: 2550
+ });
+ _addInitialLiquidity({
+ from: _lender,
+ amount: 10_000 * 1e18,
+ index: 2551
+ });
+ _addInitialLiquidity({
+ from: _lender,
+ amount: 10_000 * 1e18,
+ index: 2552
+ });
+ _addInitialLiquidity({
+ from: _lender,
+ amount: 10_000 * 1e18,
+ index: 2553
+ });
+ _addInitialLiquidity({
+ from: _lender,
+ amount: 10_000 * 1e18,
+ index: 2554
+ });
+
(uint256 liquidityAdded, , , , ) = _poolUtils.poolLoansInfo(address(_pool));
skip(10 days);
@@ -91,59 +82,54 @@ contract ERC721PoolSubsetInterestTest is ERC721PoolInterestTest {
tokenIdsToAdd[0] = 1;
tokenIdsToAdd[1] = 3;
tokenIdsToAdd[2] = 5;
- _pledgeCollateral(
- {
- from: _borrower,
- borrower: _borrower,
- tokenIds: tokenIdsToAdd
- }
- );
- _borrow(
- {
- from: _borrower,
- amount: 5_000 * 1e18,
- indexLimit: 2_551,
- newLup: 3_010.892022197881557845 * 1e18
- }
- );
+ _pledgeCollateral({
+ from: _borrower,
+ borrower: _borrower,
+ tokenIds: tokenIdsToAdd
+ });
+ _borrow({
+ from: _borrower,
+ amount: 5_000 * 1e18,
+ indexLimit: 2_551,
+ newLup: 3_010.892022197881557845 * 1e18
+ });
+
uint256 expectedDebt = 5_004.326923076923075000 * 1e18;
(uint256 poolDebt,,) = _pool.debtInfo();
assertEq(poolDebt, expectedDebt);
- _assertBorrower(
- {
- borrower: _borrower,
- borrowerDebt: expectedDebt,
- borrowerCollateral: 3 * 1e18,
- borrowert0Np: 1_743.173878205128204457 * 1e18,
- borrowerCollateralization: 1.804973217265326249 * 1e18
- }
- );
+
+ _assertBorrower({
+ borrower: _borrower,
+ borrowerDebt: expectedDebt,
+ borrowerCollateral: 3 * 1e18,
+ borrowert0Np: 1_743.173878205128204457 * 1e18,
+ borrowerCollateralization: 1.804973217265326249 * 1e18
+ });
+
_assertLenderInterest(liquidityAdded, 0);
// borrower pledge additional collateral after some time has passed
skip(10 days);
tokenIdsToAdd = new uint256[](1);
tokenIdsToAdd[0] = 51;
- _pledgeCollateral(
- {
- from: _borrower,
- borrower: _borrower,
- tokenIds: tokenIdsToAdd
- }
- );
+ _pledgeCollateral({
+ from: _borrower,
+ borrower: _borrower,
+ tokenIds: tokenIdsToAdd
+ });
expectedDebt = 5_010.500446015624727374 * 1e18;
(poolDebt,,) = _pool.debtInfo();
assertEq(poolDebt, expectedDebt);
- _assertBorrower(
- {
- borrower: _borrower,
- borrowerDebt: expectedDebt,
- borrowerCollateral: 4 * 1e18,
- borrowert0Np: 1_743.173878205128204457 * 1e18,
- borrowerCollateralization: 2.403665705362551645 * 1e18
- }
- );
+
+ _assertBorrower({
+ borrower: _borrower,
+ borrowerDebt: expectedDebt,
+ borrowerCollateral: 4 * 1e18,
+ borrowert0Np: 1_743.173878205128204457 * 1e18,
+ borrowerCollateralization: 2.403665705362551645 * 1e18
+ });
+
_assertLenderInterest(liquidityAdded, 5.279480966289700000 * 1e18);
// borrower pulls some of their collateral after some time has passed
@@ -160,41 +146,39 @@ contract ERC721PoolSubsetInterestTest is ERC721PoolInterestTest {
expectedDebt = 5_016.063127975675193806 * 1e18;
(poolDebt,,) = _pool.debtInfo();
assertEq(poolDebt, expectedDebt);
- _assertBorrower(
- {
- borrower: _borrower,
- borrowerDebt: expectedDebt,
- borrowerCollateral: 3 * 1e18,
- borrowert0Np: 1_735.667387820512819845 * 1e18,
- borrowerCollateralization: 1.800750077529217167 * 1e18
- }
- );
+
+ _assertBorrower({
+ borrower: _borrower,
+ borrowerDebt: expectedDebt,
+ borrowerCollateral: 3 * 1e18,
+ borrowert0Np: 1_735.667387820512819845 * 1e18,
+ borrowerCollateralization: 1.800750077529217167 * 1e18
+ });
+
_assertLenderInterest(liquidityAdded, 10.036615877540250000 * 1e18);
// borrower borrows some additional quote after some time has passed
skip(10 days);
- _borrow(
- {
- from: _borrower,
- amount: 1_000 * 1e18,
- indexLimit: 3_000,
- newLup: 3_010.892022197881557845 * 1e18
- }
- );
+ _borrow({
+ from: _borrower,
+ amount: 1_000 * 1e18,
+ indexLimit: 3_000,
+ newLup: 3_010.892022197881557845 * 1e18
+ });
expectedDebt = 6_021.775783320497493092 * 1e18;
(poolDebt,,) = _pool.debtInfo();
assertEq(poolDebt, expectedDebt);
- _assertBorrower(
- {
- borrower: _borrower,
- borrowerDebt: expectedDebt,
- borrowerCollateral: 3 * 1e18,
- borrowert0Np: 2_073.483875782488916529 * 1e18,
- borrowerCollateralization: 1.500002057800446964 * 1e18
- }
- );
+
+ _assertBorrower({
+ borrower: _borrower,
+ borrowerDebt: expectedDebt,
+ borrowerCollateral: 3 * 1e18,
+ borrowert0Np: 2_073.483875782488916529 * 1e18,
+ borrowerCollateralization: 1.500002057800446964 * 1e18
+ });
+
_assertLenderInterest(liquidityAdded, 14.322580066674600000 * 1e18);
// mint additional quote to borrower to enable repayment
@@ -202,6 +186,7 @@ contract ERC721PoolSubsetInterestTest is ERC721PoolInterestTest {
// borrower repays their loan after some additional time
skip(10 days);
+
_approveAndRepayDebt({
from: _borrower,
borrower: _borrower,
@@ -214,41 +199,33 @@ contract ERC721PoolSubsetInterestTest is ERC721PoolInterestTest {
(poolDebt,,) = _pool.debtInfo();
assertEq(poolDebt, 0);
- _assertBorrower(
- {
- borrower: _borrower,
- borrowerDebt: 0,
- borrowerCollateral: 3 * 1e18,
- borrowert0Np: 2_073.483875782488916529 * 1e18,
- borrowerCollateralization: 1 * 1e18
- }
- );
-
+ _assertBorrower({
+ borrower: _borrower,
+ borrowerDebt: 0,
+ borrowerCollateral: 3 * 1e18,
+ borrowert0Np: 2_073.483875782488916529 * 1e18,
+ borrowerCollateralization: 1 * 1e18
+ });
}
function testMultipleBorrowerInterestAccumulation() external tearDown {
// lender deposits 10000 Quote into 3 buckets
- _addInitialLiquidity(
- {
- from: _lender,
- amount: 10_000 * 1e18,
- index: 2550
- }
- );
- _addInitialLiquidity(
- {
- from: _lender,
- amount: 10_000 * 1e18,
- index: 2551
- }
- );
- _addInitialLiquidity(
- {
- from: _lender,
- amount: 10_000 * 1e18,
- index: 2552
- }
- );
+ _addInitialLiquidity({
+ from: _lender,
+ amount: 10_000 * 1e18,
+ index: 2550
+ });
+ _addInitialLiquidity({
+ from: _lender,
+ amount: 10_000 * 1e18,
+ index: 2551
+ });
+ _addInitialLiquidity({
+ from: _lender,
+ amount: 10_000 * 1e18,
+ index: 2552
+ });
+
(uint256 liquidityAdded, , , , ) = _poolUtils.poolLoansInfo(address(_pool));
skip(2 hours);
@@ -258,81 +235,73 @@ contract ERC721PoolSubsetInterestTest is ERC721PoolInterestTest {
tokenIdsToAdd[0] = 1;
tokenIdsToAdd[1] = 3;
tokenIdsToAdd[2] = 5;
- _pledgeCollateral(
- {
- from: _borrower,
- borrower: _borrower,
- tokenIds: tokenIdsToAdd
- }
- );
+ _pledgeCollateral({
+ from: _borrower,
+ borrower: _borrower,
+ tokenIds: tokenIdsToAdd
+ });
uint256 borrowAmount = 8_000 * 1e18;
- _borrow(
- {
- from: _borrower,
- amount: borrowAmount,
- indexLimit: 2_551,
- newLup: _priceAt(2550)
- }
- );
+
+ _borrow({
+ from: _borrower,
+ amount: borrowAmount,
+ indexLimit: 2_551,
+ newLup: _priceAt(2550)
+ });
+
uint256 expectedBorrower1Debt = 8_007.692307692307696000 * 1e18;
(uint256 poolDebt,,) = _pool.debtInfo();
assertEq(poolDebt, expectedBorrower1Debt);
- _assertBorrower(
- {
- borrower: _borrower,
- borrowerDebt: expectedBorrower1Debt,
- borrowerCollateral: 3 * 1e18,
- borrowert0Np: 2_802.692307692307693600 * 1e18,
- borrowerCollateralization: 1.127999893042434013 * 1e18
- }
- );
+
+ _assertBorrower({
+ borrower: _borrower,
+ borrowerDebt: expectedBorrower1Debt,
+ borrowerCollateral: 3 * 1e18,
+ borrowert0Np: 2_802.692307692307693600 * 1e18,
+ borrowerCollateralization: 1.127999893042434013 * 1e18
+ });
skip(4 hours);
// borrower 2 pledges one NFT and takes out a loan with TP around 2750
tokenIdsToAdd = new uint256[](1);
tokenIdsToAdd[0] = 53;
- _pledgeCollateral(
- {
- from: _borrower2,
- borrower: _borrower2,
- tokenIds: tokenIdsToAdd
- }
- );
+ _pledgeCollateral({
+ from: _borrower2,
+ borrower: _borrower2,
+ tokenIds: tokenIdsToAdd
+ });
borrowAmount = 2_750 * 1e18;
- _borrow(
- {
- from: _borrower2,
- amount: borrowAmount,
- indexLimit: 3_000,
- newLup: _priceAt(2551)
- }
- );
+ _borrow({
+ from: _borrower2,
+ amount: borrowAmount,
+ indexLimit: 3_000,
+ newLup: _priceAt(2551)
+ });
+
expectedBorrower1Debt = 8_007.875133804645608008 * 1e18;
uint256 expectedBorrower2Debt = 2_752.644230769230770500 * 1e18;
uint256 expectedPoolDebt = 10_760.519364573876378508 * 1e18;
(poolDebt,,) = _pool.debtInfo();
assertEq(poolDebt, expectedPoolDebt);
- _assertBorrower(
- {
- borrower: _borrower,
- borrowerDebt: expectedBorrower1Debt,
- borrowerCollateral: 3 * 1e18,
- borrowert0Np: 2_802.692307692307693600 * 1e18,
- borrowerCollateralization: 1.122362328272840838 * 1e18
- }
- );
- _assertBorrower(
- {
- borrower: _borrower2,
- borrowerDebt: expectedBorrower2Debt,
- borrowerCollateral: 1 * 1e18,
- borrowert0Np: 2_904.661507289418461411 * 1e18,
- borrowerCollateralization: 1.088376197116173336 * 1e18
- }
- );
+
+ _assertBorrower({
+ borrower: _borrower,
+ borrowerDebt: expectedBorrower1Debt,
+ borrowerCollateral: 3 * 1e18,
+ borrowert0Np: 2_802.692307692307693600 * 1e18,
+ borrowerCollateralization: 1.122362328272840838 * 1e18
+ });
+ _assertBorrower({
+ borrower: _borrower2,
+ borrowerDebt: expectedBorrower2Debt,
+ borrowerCollateral: 1 * 1e18,
+ borrowert0Np: 2_904.661507289418461411 * 1e18,
+ borrowerCollateralization: 1.088376197116173336 * 1e18
+ });
+
_assertLenderInterest(liquidityAdded, 0.158098662307440000 * 1e18);
skip(4 hours);
@@ -340,148 +309,133 @@ contract ERC721PoolSubsetInterestTest is ERC721PoolInterestTest {
// borrower 3 pledges one NFT and takes out a loan with TP around 2500
tokenIdsToAdd = new uint256[](1);
tokenIdsToAdd[0] = 73;
- _pledgeCollateral(
- {
- from: _borrower3,
- borrower: _borrower3,
- tokenIds: tokenIdsToAdd
- }
- );
+ _pledgeCollateral({
+ from: _borrower3,
+ borrower: _borrower3,
+ tokenIds: tokenIdsToAdd
+ });
+
borrowAmount = 2_500 * 1e18;
- _borrow(
- {
- from: _borrower3,
- amount: borrowAmount,
- indexLimit: 3_000,
- newLup: _priceAt(2551)
- }
- );
+ _borrow({
+ from: _borrower3,
+ amount: borrowAmount,
+ indexLimit: 3_000,
+ newLup: _priceAt(2551)
+ });
+
expectedBorrower1Debt = 8_008.057964091143327677 * 1e18;
expectedBorrower2Debt = 2_752.707077245346929053 * 1e18;
- uint256 expectedBorrower3Debt = 2_502.403846153846155000 * 1e18;
- expectedPoolDebt = 13_263.168887490336411731 * 1e18;
+ uint256 expectedBorrower3Debt = 2_502.403846153846154999 * 1e18;
+ expectedPoolDebt = 13_263.168887490336411730 * 1e18;
+
(poolDebt,,) = _pool.debtInfo();
assertEq(poolDebt, expectedPoolDebt);
- _assertBorrower(
- {
- borrower: _borrower,
- borrowerDebt: expectedBorrower1Debt,
- borrowerCollateral: 3 * 1e18,
- borrowert0Np: 2_802.692307692307693600 * 1e18,
- borrowerCollateralization: 1.122336703854666979 * 1e18
- }
- );
- _assertBorrower(
- {
- borrower: _borrower2,
- borrowerDebt: expectedBorrower2Debt,
- borrowerCollateral: 1 * 1e18,
- borrowert0Np: 2_904.661507289418461411 * 1e18,
- borrowerCollateralization: 1.088351348628209297 * 1e18
- }
- );
- _assertBorrower(
- {
- borrower: _borrower3,
- borrowerDebt: expectedBorrower3Debt,
- borrowerCollateral: 1 * 1e18,
- borrowert0Np: 2_640.541083248800813687 * 1e18,
- borrowerCollateralization: 1.197213816827790670 * 1e18
- }
- );
+
+ _assertBorrower({
+ borrower: _borrower,
+ borrowerDebt: expectedBorrower1Debt,
+ borrowerCollateral: 3 * 1e18,
+ borrowert0Np: 2_802.692307692307693600 * 1e18,
+ borrowerCollateralization: 1.122336703854666979 * 1e18
+ });
+ _assertBorrower({
+ borrower: _borrower2,
+ borrowerDebt: expectedBorrower2Debt,
+ borrowerCollateral: 1 * 1e18,
+ borrowert0Np: 2_904.661507289418461411 * 1e18,
+ borrowerCollateralization: 1.088351348628209297 * 1e18
+ });
+ _assertBorrower({
+ borrower: _borrower3,
+ borrowerDebt: expectedBorrower3Debt,
+ borrowerCollateral: 1 * 1e18,
+ borrowert0Np: 2_640.541083248800813686 * 1e18,
+ borrowerCollateralization: 1.197213816827790670 * 1e18
+ });
+
_assertLenderInterest(liquidityAdded, 0.371995968618930000 * 1e18);
skip(4 hours);
// trigger an interest accumulation
- _addLiquidity(
- {
- from: _lender,
- amount: 1 * 1e18,
- index: 2550,
- lpAward: 0.999978753161905147653029533 * 1e27,
- newLup: 2995.912459898389633881 * 1e18
- }
- );
+ _addLiquidity({
+ from: _lender,
+ amount: 1 * 1e18,
+ index: 2550,
+ lpAward: 0.999978753161905148 * 1e18,
+ newLup: 2995.912459898389633881 * 1e18
+ });
liquidityAdded += 1e18;
// check pool and borrower debt to confirm interest has accumulated
- expectedPoolDebt = 13_263.471703022178416340 * 1e18;
+ expectedPoolDebt = 13_263.471703022178416339 * 1e18;
+
(poolDebt,,) = _pool.debtInfo();
assertEq(poolDebt, expectedPoolDebt);
+
_assertLenderInterest(liquidityAdded, 0.637418685977190000 * 1e18);
expectedBorrower1Debt = 8_008.240798551896146546 * 1e18;
- _assertBorrower(
- {
- borrower: _borrower,
- borrowerDebt: expectedBorrower1Debt,
- borrowerCollateral: 3 * 1e18,
- borrowert0Np: 2_802.692307692307693600 * 1e18,
- borrowerCollateralization: 1.122311080021518821 * 1e18
- }
- );
+
+ _assertBorrower({
+ borrower: _borrower,
+ borrowerDebt: expectedBorrower1Debt,
+ borrowerCollateral: 3 * 1e18,
+ borrowert0Np: 2_802.692307692307693600 * 1e18,
+ borrowerCollateralization: 1.122311080021518821 * 1e18
+ });
+
expectedBorrower2Debt = 2_752.769925156330518052 * 1e18;
- _assertBorrower(
- {
- borrower: _borrower2,
- borrowerDebt: expectedBorrower2Debt,
- borrowerCollateral: 1 * 1e18,
- borrowert0Np: 2_904.661507289418461411 * 1e18,
- borrowerCollateralization: 1.088326500707555859 * 1e18
- }
- );
- expectedBorrower3Debt = 2_502.460979313951751742 * 1e18;
- _assertBorrower(
- {
- borrower: _borrower3,
- borrowerDebt: expectedBorrower3Debt,
- borrowerCollateral: 1 * 1e18,
- borrowert0Np: 2_640.541083248800813687 * 1e18,
- borrowerCollateralization: 1.197186483491030227 * 1e18
- }
- );
+
+ _assertBorrower({
+ borrower: _borrower2,
+ borrowerDebt: expectedBorrower2Debt,
+ borrowerCollateral: 1 * 1e18,
+ borrowert0Np: 2_904.661507289418461411 * 1e18,
+ borrowerCollateralization: 1.088326500707555859 * 1e18
+ });
+
+ expectedBorrower3Debt = 2_502.460979313951751741 * 1e18;
+
+ _assertBorrower({
+ borrower: _borrower3,
+ borrowerDebt: expectedBorrower3Debt,
+ borrowerCollateral: 1 * 1e18,
+ borrowert0Np: 2_640.541083248800813686 * 1e18,
+ borrowerCollateralization: 1.197186483491030227 * 1e18
+ });
// ensure debt from the three borrowers adds up to the pool debt
assertEq(expectedPoolDebt, expectedBorrower1Debt + expectedBorrower2Debt + expectedBorrower3Debt);
}
function testBorrowerInterestCalculationAfterRepayingAllDebtOnce() external tearDown {
- _addInitialLiquidity(
- {
- from: _lender,
- amount: 10_000 * 1e18,
- index: 2550
- }
- );
- _addInitialLiquidity(
- {
- from: _lender,
- amount: 10_000 * 1e18,
- index: 2551
- }
- );
- _addInitialLiquidity(
- {
- from: _lender,
- amount: 10_000 * 1e18,
- index: 2552
- }
- );
- _addInitialLiquidity(
- {
- from: _lender,
- amount: 10_000 * 1e18,
- index: 2553
- }
- );
- _addInitialLiquidity(
- {
- from: _lender,
- amount: 10_000 * 1e18,
- index: 2554
- }
- );
+ _addInitialLiquidity({
+ from: _lender,
+ amount: 10_000 * 1e18,
+ index: 2550
+ });
+ _addInitialLiquidity({
+ from: _lender,
+ amount: 10_000 * 1e18,
+ index: 2551
+ });
+ _addInitialLiquidity({
+ from: _lender,
+ amount: 10_000 * 1e18,
+ index: 2552
+ });
+ _addInitialLiquidity({
+ from: _lender,
+ amount: 10_000 * 1e18,
+ index: 2553
+ });
+ _addInitialLiquidity({
+ from: _lender,
+ amount: 10_000 * 1e18,
+ index: 2554
+ });
+
(uint256 liquidityAdded, , , , ) = _poolUtils.poolLoansInfo(address(_pool));
skip(10 days);
@@ -491,34 +445,29 @@ contract ERC721PoolSubsetInterestTest is ERC721PoolInterestTest {
tokenIdsToAdd[0] = 1;
tokenIdsToAdd[1] = 3;
tokenIdsToAdd[2] = 5;
- _pledgeCollateral(
- {
- from: _borrower,
- borrower: _borrower,
- tokenIds: tokenIdsToAdd
- }
- );
- _borrow(
- {
- from: _borrower,
- amount: 5_000 * 1e18,
- indexLimit: 2_551,
- newLup: 3_010.892022197881557845 * 1e18
- }
- );
+ _pledgeCollateral({
+ from: _borrower,
+ borrower: _borrower,
+ tokenIds: tokenIdsToAdd
+ });
+ _borrow({
+ from: _borrower,
+ amount: 5_000 * 1e18,
+ indexLimit: 2_551,
+ newLup: 3_010.892022197881557845 * 1e18
+ });
uint256 expectedDebt = 5_004.326923076923075000 * 1e18;
(uint256 poolDebt, , ) = _pool.debtInfo();
assertEq(poolDebt, expectedDebt);
- _assertBorrower(
- {
- borrower: _borrower,
- borrowerDebt: expectedDebt,
- borrowerCollateral: 3 * 1e18,
- borrowert0Np: 1_743.173878205128204457 * 1e18,
- borrowerCollateralization: 1.804973217265326249 * 1e18
- }
- );
+
+ _assertBorrower({
+ borrower: _borrower,
+ borrowerDebt: expectedDebt,
+ borrowerCollateral: 3 * 1e18,
+ borrowert0Np: 1_743.173878205128204457 * 1e18,
+ borrowerCollateralization: 1.804973217265326249 * 1e18
+ });
// time passes and interest accrues
skip(30 days);
@@ -526,15 +475,14 @@ contract ERC721PoolSubsetInterestTest is ERC721PoolInterestTest {
expectedDebt = 5_022.870348947539432923 * 1e18;
(poolDebt, , ) = _pool.debtInfo();
assertEq(poolDebt, expectedDebt);
- _assertBorrower(
- {
- borrower: _borrower,
- borrowerDebt: expectedDebt,
- borrowerCollateral: 3 * 1e18,
- borrowert0Np: 1_743.173878205128204457 * 1e18,
- borrowerCollateralization: 1.798309619615464420 * 1e18
- }
- );
+
+ _assertBorrower({
+ borrower: _borrower,
+ borrowerDebt: expectedDebt,
+ borrowerCollateral: 3 * 1e18,
+ borrowert0Np: 1_743.173878205128204457 * 1e18,
+ borrowerCollateralization: 1.798309619615464420 * 1e18
+ });
// mint additional quote to borrower to enable repayment
deal(address(_quote), _borrower, 20_000 * 1e18);
@@ -554,41 +502,37 @@ contract ERC721PoolSubsetInterestTest is ERC721PoolInterestTest {
(poolDebt, , ) = _pool.debtInfo();
assertEq(poolDebt, 0);
- _assertBorrower(
- {
- borrower: _borrower,
- borrowerDebt: 0,
- borrowerCollateral: 3 * 1e18,
- borrowert0Np: 1_743.173878205128204457 * 1e18,
- borrowerCollateralization: 1 * 1e18
- }
- );
+ _assertBorrower({
+ borrower: _borrower,
+ borrowerDebt: 0,
+ borrowerCollateral: 3 * 1e18,
+ borrowert0Np: 1_743.173878205128204457 * 1e18,
+ borrowerCollateralization: 1 * 1e18
+ });
+
_assertLenderInterest(liquidityAdded, 21.157033792511550000 * 1e18);
// borrower borrows again once repayed all debt
- _borrow(
- {
- from: _borrower,
- amount: 5_000 * 1e18,
- indexLimit: 2_551,
- newLup: 3_010.892022197881557845 * 1e18
- }
- );
-
- expectedDebt = 5_003.894230769230770000 * 1e18;
+ _borrow({
+ from: _borrower,
+ amount: 5_000 * 1e18,
+ indexLimit: 2_551,
+ newLup: 3_010.892022197881557845 * 1e18
+ });
+
+ expectedDebt = 5_003.894230769230769999 * 1e18;
(poolDebt, , ) = _pool.debtInfo();
assertEq(poolDebt, expectedDebt);
- _assertBorrower(
- {
- borrower: _borrower,
- borrowerDebt: expectedDebt,
- borrowerCollateral: 3 * 1e18,
- borrowert0Np: 1_726.979669209494250313 * 1e18,
- borrowerCollateralization: 1.805129295309881815 * 1e18
- }
- );
+ _assertBorrower({
+ borrower: _borrower,
+ borrowerDebt: expectedDebt,
+ borrowerCollateral: 3 * 1e18,
+ borrowert0Np: 1_726.979669209494250312 * 1e18,
+ borrowerCollateralization: 1.805129295309881815 * 1e18
+ });
+
_assertLenderInterest(liquidityAdded, 21.157033792511550000 * 1e18);
@@ -596,26 +540,24 @@ contract ERC721PoolSubsetInterestTest is ERC721PoolInterestTest {
skip(10 days);
tokenIdsToAdd = new uint256[](1);
tokenIdsToAdd[0] = 51;
- _pledgeCollateral(
- {
- from: _borrower,
- borrower: _borrower,
- tokenIds: tokenIdsToAdd
- }
- );
-
- expectedDebt = 5_009.449578476990224066 * 1e18;
+ _pledgeCollateral({
+ from: _borrower,
+ borrower: _borrower,
+ tokenIds: tokenIdsToAdd
+ });
+
+ expectedDebt = 5_009.449578476990224065 * 1e18;
(poolDebt, , ) = _pool.debtInfo();
assertEq(poolDebt, expectedDebt);
- _assertBorrower(
- {
- borrower: _borrower,
- borrowerDebt: expectedDebt,
- borrowerCollateral: 4 * 1e18,
- borrowert0Np: 1_726.979669209494250313 * 1e18,
- borrowerCollateralization: 2.404169939255701731 * 1e18
- }
- );
+
+ _assertBorrower({
+ borrower: _borrower,
+ borrowerDebt: expectedDebt,
+ borrowerCollateral: 4 * 1e18,
+ borrowert0Np: 1_726.979669209494250312 * 1e18,
+ borrowerCollateralization: 2.404169939255701731 * 1e18
+ });
+
_assertLenderInterest(liquidityAdded, 25.907847709272800000 * 1e18);
// borrower pulls some of their collateral after some time has passed
@@ -629,44 +571,42 @@ contract ERC721PoolSubsetInterestTest is ERC721PoolInterestTest {
collateralToPull: 1
});
- expectedDebt = 5_014.454664494689841710 * 1e18;
+ expectedDebt = 5_014.454664494689841709 * 1e18;
(poolDebt, , ) = _pool.debtInfo();
assertEq(poolDebt, expectedDebt);
- _assertBorrower(
- {
- borrower: _borrower,
- borrowerDebt: expectedDebt,
- borrowerCollateral: 3 * 1e18,
- borrowert0Np: 1_720.257643586910442803 * 1e18,
- borrowerCollateralization: 1.801327695821111558 * 1e18
- }
- );
+
+ _assertBorrower({
+ borrower: _borrower,
+ borrowerDebt: expectedDebt,
+ borrowerCollateral: 3 * 1e18,
+ borrowert0Np: 1_720.257643586910442802 * 1e18,
+ borrowerCollateralization: 1.801327695821111558 * 1e18
+ });
+
_assertLenderInterest(liquidityAdded, 30.188116923725550000 * 1e18);
// borrower borrows some additional quote after some time has passed
skip(10 days);
- _borrow(
- {
- from: _borrower,
- amount: 1_000 * 1e18,
- indexLimit: 3_000,
- newLup: 3_010.892022197881557845 * 1e18
- }
- );
+ _borrow({
+ from: _borrower,
+ amount: 1_000 * 1e18,
+ indexLimit: 3_000,
+ newLup: 3_010.892022197881557845 * 1e18
+ });
- expectedDebt = 6_019.594382773827921758 * 1e18;
+ expectedDebt = 6_019.594382773827921756 * 1e18;
(poolDebt, , ) = _pool.debtInfo();
assertEq(poolDebt, expectedDebt);
- _assertBorrower(
- {
- borrower: _borrower,
- borrowerDebt: expectedDebt,
- borrowerCollateral: 3 * 1e18,
- borrowert0Np: 2_055.969470907112040316 * 1e18,
- borrowerCollateralization: 1.500545633513497515 * 1e18
- }
- );
+
+ _assertBorrower({
+ borrower: _borrower,
+ borrowerDebt: expectedDebt,
+ borrowerCollateral: 3 * 1e18,
+ borrowert0Np: 2_055.969470907112040315 * 1e18,
+ borrowerCollateralization: 1.500545633513497515 * 1e18
+ });
+
_assertLenderInterest(liquidityAdded, 34.044037663170400000 * 1e18);
// mint additional quote to borrower to enable repayment
@@ -679,7 +619,7 @@ contract ERC721PoolSubsetInterestTest is ERC721PoolInterestTest {
from: _borrower,
borrower: _borrower,
amountToRepay: 7_000 * 1e18,
- amountRepaid: 6_024.465544800440916672 * 1e18,
+ amountRepaid: 6_024.465544800440916669 * 1e18,
collateralToPull: 0,
newLup: MAX_PRICE
});
@@ -687,15 +627,14 @@ contract ERC721PoolSubsetInterestTest is ERC721PoolInterestTest {
(poolDebt, , ) = _pool.debtInfo();
assertEq(poolDebt, 0);
- _assertBorrower(
- {
- borrower: _borrower,
- borrowerDebt: 0,
- borrowerCollateral: 3 * 1e18,
- borrowert0Np: 2_055.969470907112040316 * 1e18,
- borrowerCollateralization: 1 * 1e18
- }
- );
+ _assertBorrower({
+ borrower: _borrower,
+ borrowerDebt: 0,
+ borrowerCollateral: 3 * 1e18,
+ borrowert0Np: 2_055.969470907112040315 * 1e18,
+ borrowerCollateralization: 1 * 1e18
+ });
+
_assertLenderInterest(liquidityAdded, 38.215088437572850000 * 1e18);
}
}
\ No newline at end of file
diff --git a/tests/forge/ERC721Pool/ERC721PoolLiquidationsDepositTake.t.sol b/tests/forge/ERC721Pool/ERC721PoolLiquidationsDepositTake.t.sol
new file mode 100644
index 000000000..341bdb1d9
--- /dev/null
+++ b/tests/forge/ERC721Pool/ERC721PoolLiquidationsDepositTake.t.sol
@@ -0,0 +1,308 @@
+// SPDX-License-Identifier: UNLICENSED
+pragma solidity 0.8.14;
+
+import { ERC721HelperContract } from './ERC721DSTestPlus.sol';
+
+import 'src/libraries/helpers/PoolHelper.sol';
+
+contract ERC721PoolLiquidationsDepositTakeTest is ERC721HelperContract {
+
+ address internal _borrower;
+ address internal _borrower2;
+ address internal _lender;
+ address internal _lender2;
+ address internal _taker;
+
+ function setUp() external {
+ _borrower = makeAddr("borrower");
+ _borrower2 = makeAddr("borrower2");
+ _lender = makeAddr("lender");
+ _lender2 = makeAddr("lender2");
+ _taker = makeAddr("taker");
+
+ // deploy subset pool
+ uint256[] memory subsetTokenIds = new uint256[](6);
+ subsetTokenIds[0] = 1;
+ subsetTokenIds[1] = 3;
+ subsetTokenIds[2] = 5;
+ subsetTokenIds[3] = 51;
+ subsetTokenIds[4] = 53;
+ subsetTokenIds[5] = 73;
+ _pool = _deploySubsetPool(subsetTokenIds);
+
+ _mintAndApproveQuoteTokens(_lender, 120_000 * 1e18);
+ _mintAndApproveQuoteTokens(_lender2, 120_000 * 1e18);
+ _mintAndApproveQuoteTokens(_borrower, 10 * 1e18);
+
+ _mintAndApproveCollateralTokens(_borrower, 6);
+ _mintAndApproveCollateralTokens(_borrower2, 74);
+
+ // Lender adds Quote token accross 5 prices
+ _addInitialLiquidity({
+ from: _lender,
+ amount: 2_000 * 1e18,
+ index: _i9_91
+ });
+ _addInitialLiquidity({
+ from: _lender,
+ amount: 5_000 * 1e18,
+ index: _i9_81
+ });
+ _addInitialLiquidity({
+ from: _lender,
+ amount: 11_000 * 1e18,
+ index: _i9_72
+ });
+ _addInitialLiquidity({
+ from: _lender,
+ amount: 25_000 * 1e18,
+ index: _i9_62
+ });
+ _addInitialLiquidity({
+ from: _lender,
+ amount: 30_000 * 1e18,
+ index: _i9_52
+ });
+
+ // first borrower adds collateral token and borrows
+ uint256[] memory tokenIdsToAdd = new uint256[](2);
+ tokenIdsToAdd[0] = 1;
+ tokenIdsToAdd[1] = 3;
+
+ uint256 expectedNewLup = 9.917184843435912074 * 1e18;
+
+ // borrower deposits two NFTs into the subset pool and borrows
+ _pledgeCollateral({
+ from: _borrower,
+ borrower: _borrower,
+ tokenIds: tokenIdsToAdd
+ });
+ _borrow({
+ from: _borrower,
+ amount: 19.8 * 1e18,
+ indexLimit: _i9_91,
+ newLup: expectedNewLup
+ });
+
+ // second borrower deposits three NFTs into the subset pool and borrows
+ tokenIdsToAdd = new uint256[](3);
+ tokenIdsToAdd[0] = 51;
+ tokenIdsToAdd[1] = 53;
+ tokenIdsToAdd[2] = 73;
+ _pledgeCollateral({
+ from: _borrower2,
+ borrower: _borrower2,
+ tokenIds: tokenIdsToAdd
+ });
+ _borrow({
+ from: _borrower2,
+ amount: 15 * 1e18,
+ indexLimit: _i9_72,
+ newLup: expectedNewLup
+ });
+
+ /*****************************/
+ /*** Assert pre-kick state ***/
+ /*****************************/
+
+ _assertPool(
+ PoolParams({
+ htp: 9.909519230769230774 * 1e18,
+ lup: expectedNewLup,
+ poolSize: 73_000 * 1e18,
+ pledgedCollateral: 5 * 1e18,
+ encumberedCollateral: 3.512434434608473285 * 1e18,
+ poolDebt: 34.833461538461538478 * 1e18,
+ actualUtilization: 0.000477170706006322 * 1e18,
+ targetUtilization: 1 * 1e18,
+ minDebtAmount: 1.741673076923076924 * 1e18,
+ loans: 2,
+ maxBorrower: address(_borrower),
+ interestRate: 0.05 * 1e18,
+ interestRateUpdate: _startTime
+ })
+ );
+ _assertBorrower({
+ borrower: _borrower,
+ borrowerDebt: 19.819038461538461548 * 1e18,
+ borrowerCollateral: 2 * 1e18,
+ borrowert0Np: 10.404995192307692312 * 1e18,
+ borrowerCollateralization: 1.000773560501591181 * 1e18
+ });
+ _assertBorrower({
+ borrower: _borrower2,
+ borrowerDebt: 15.014423076923076930 * 1e18,
+ borrowerCollateral: 3 * 1e18,
+ borrowert0Np: 5.255048076923076925 * 1e18,
+ borrowerCollateralization: 1.981531649793150539 * 1e18
+ });
+
+ assertEq(_quote.balanceOf(_lender), 47_000 * 1e18);
+
+ // Skip to make borrower undercollateralized
+ skip(1000 days);
+
+ _kick({
+ from: _lender,
+ borrower: _borrower,
+ debt: 23.012828827714740289 * 1e18,
+ collateral: 2 * 1e18,
+ bond: 0.227287198298417188 * 1e18,
+ transferAmount: 0.227287198298417188 * 1e18
+ });
+
+ /******************************/
+ /*** Assert Post-kick state ***/
+ /******************************/
+
+ _assertPool(
+ PoolParams({
+ htp: 6.582216822103492762 * 1e18,
+ lup: 9.917184843435912074 * 1e18,
+ poolSize: 73_000 * 1e18,
+ pledgedCollateral: 5 * 1e18,
+ encumberedCollateral: 4.056751649452525709 * 1e18,
+ poolDebt: 40.231555971534224231 * 1e18,
+ actualUtilization: 0.000551117205089510 * 1e18,
+ targetUtilization: 0.811350329890505142 * 1e18,
+ minDebtAmount: 4.023155597153422423 * 1e18,
+ loans: 1,
+ maxBorrower: address(_borrower2),
+ interestRate: 0.045 * 1e18,
+ interestRateUpdate: block.timestamp
+ })
+ );
+ _assertBorrower({
+ borrower: _borrower,
+ borrowerDebt: 23.012828827714740289 * 1e18,
+ borrowerCollateral: 2 * 1e18,
+ borrowert0Np: 10.404995192307692312 * 1e18,
+ borrowerCollateralization: 0.861883162446546169 * 1e18
+ });
+ _assertBorrower({
+ borrower: _borrower2,
+ borrowerDebt: 17.218727143819483942 * 1e18,
+ borrowerCollateral: 3 * 1e18,
+ borrowert0Np: 5.255048076923076925 * 1e18,
+ borrowerCollateralization: 1.727860269914713433 * 1e18
+ });
+ }
+
+ function testDepositTakeNFTAndSettleAuction() external {
+
+ skip(5 hours);
+
+ _assertAuction(
+ AuctionParams({
+ borrower: _borrower,
+ active: true,
+ kicker: _lender,
+ bondSize: 0.227287198298417188 * 1e18,
+ bondFactor: 0.01 * 1e18,
+ kickTime: block.timestamp - 5 hours,
+ kickMomp: 9.917184843435912074 * 1e18,
+ totalBondEscrowed: 0.227287198298417188 * 1e18,
+ auctionPrice: 23.865155821333804736 * 1e18,
+ debtInAuction: 23.012828827714740289 * 1e18,
+ thresholdPrice: 11.506709959118993144 * 1e18,
+ neutralPrice: 11.932577910666902372 * 1e18
+ })
+ );
+
+ _addLiquidity({
+ from: _lender,
+ amount: 15.0 * 1e18,
+ index: _i1505_26,
+ lpAward: 15.0 * 1e18,
+ newLup: 9.917184843435912074 * 1e18
+ });
+
+ _assertBorrower({
+ borrower: _borrower,
+ borrowerDebt: 23.013419918237986289 * 1e18,
+ borrowerCollateral: 2 * 1e18,
+ borrowert0Np: 10.404995192307692312 * 1e18,
+ borrowerCollateralization: 0.861861025320848319 * 1e18
+ });
+
+ // before deposit take: NFTs pledged by auctioned borrower are owned by the pool
+ assertEq(_collateral.ownerOf(3), address(_pool));
+ assertEq(_collateral.ownerOf(1), address(_pool));
+
+ _depositTake({
+ from: _taker,
+ borrower: _borrower,
+ kicker: _lender,
+ index: _i1505_26,
+ collateralArbed: 0.009965031187761219 * 1e18,
+ quoteTokenAmount: 14.999999999999999995 * 1e18,
+ bondChange: 0.15 * 1e18,
+ isReward: false,
+ lpAwardTaker: 0,
+ lpAwardKicker: 0
+ });
+
+ _assertAuction(
+ AuctionParams({
+ borrower: _borrower,
+ active: false,
+ kicker: address(0),
+ bondSize: 0,
+ bondFactor: 0,
+ kickTime: 0,
+ kickMomp: 0,
+ totalBondEscrowed: 0,
+ auctionPrice: 0,
+ debtInAuction: 0,
+ thresholdPrice: 9.624359312514645329 * 1e18,
+ neutralPrice: 0
+ })
+ );
+ // borrower is compensated LPs for fractional collateral
+ _assertLenderLpBalance({
+ lender: _borrower,
+ index: 3519,
+ lpBalance: 23.737330323739529015 * 1e18,
+ depositTime: block.timestamp
+ });
+ _assertBucket({
+ index: _i1505_26,
+ lpBalance: 15 * 1e18,
+ collateral: 0.009965031187761219 * 1e18,
+ deposit: 0,
+ exchangeRate: 1 * 1e18
+ });
+ _assertBorrower({
+ borrower: _borrower,
+ borrowerDebt: 9.624359312514645329 * 1e18,
+ borrowerCollateral: 1 * 1e18,
+ borrowert0Np: 8.769696613728507377 * 1e18,
+ borrowerCollateralization: 1.030425457052554443 * 1e18
+ });
+ _assertLenderLpBalance({
+ lender: _taker,
+ index: _i1505_26,
+ lpBalance: 0,
+ depositTime: 0
+ });
+ _assertLenderLpBalance({
+ lender: _lender,
+ index: _i1505_26,
+ lpBalance: 15.0 * 1e18,
+ depositTime: block.timestamp
+ });
+
+ // borrower should be able to repay and pull collateral from the pool
+ _repayDebtNoLupCheck({
+ from: _borrower,
+ borrower: _borrower,
+ amountToRepay: 10 * 1e18,
+ amountRepaid: 10 * 1e18,
+ collateralToPull: 1
+ });
+
+ // after deposit take and pull: NFT taken remains in pool, the pulled one goes to borrower
+ assertEq(_collateral.ownerOf(3), address(_pool));
+ assertEq(_collateral.ownerOf(1), _borrower);
+ }
+}
diff --git a/tests/forge/ERC721Pool/ERC721PoolLiquidationsKick.t.sol b/tests/forge/ERC721Pool/ERC721PoolLiquidationsKick.t.sol
index c56487125..956703212 100644
--- a/tests/forge/ERC721Pool/ERC721PoolLiquidationsKick.t.sol
+++ b/tests/forge/ERC721Pool/ERC721PoolLiquidationsKick.t.sol
@@ -34,84 +34,65 @@ contract ERC721PoolLiquidationsKickTest is ERC721HelperContract {
_mintAndApproveCollateralTokens(_borrower2, 74);
// Lender adds Quote token accross 5 prices
- _addInitialLiquidity(
- {
- from: _lender,
- amount: 2_000 * 1e18,
- index: _i9_91
- }
- );
- _addInitialLiquidity(
- {
- from: _lender,
- amount: 5_000 * 1e18,
- index: _i9_81
- }
- );
- _addInitialLiquidity(
- {
- from: _lender,
- amount: 11_000 * 1e18,
- index: _i9_72
- }
- );
- _addInitialLiquidity(
- {
- from: _lender,
- amount: 25_000 * 1e18,
- index: _i9_62
- }
- );
- _addInitialLiquidity(
- {
- from: _lender,
- amount: 30_000 * 1e18,
- index: _i9_52
- }
- );
+ _addInitialLiquidity({
+ from: _lender,
+ amount: 2_000 * 1e18,
+ index: _i9_91
+ });
+ _addInitialLiquidity({
+ from: _lender,
+ amount: 5_000 * 1e18,
+ index: _i9_81
+ });
+ _addInitialLiquidity({
+ from: _lender,
+ amount: 11_000 * 1e18,
+ index: _i9_72
+ });
+ _addInitialLiquidity({
+ from: _lender,
+ amount: 25_000 * 1e18,
+ index: _i9_62
+ });
+ _addInitialLiquidity({
+ from: _lender,
+ amount: 30_000 * 1e18,
+ index: _i9_52
+ });
// first borrower adds collateral token and borrows
uint256[] memory tokenIdsToAdd = new uint256[](2);
tokenIdsToAdd[0] = 1;
tokenIdsToAdd[1] = 3;
-
// borrower deposits two NFTs into the subset pool and borrows
- _pledgeCollateral(
- {
- from: _borrower,
- borrower: _borrower,
- tokenIds: tokenIdsToAdd
- }
- );
- _borrow(
- {
- from: _borrower,
- amount: 19.8 * 1e18,
- indexLimit: _i9_91,
- newLup: 9.917184843435912074 * 1e18
- }
- );
+ _pledgeCollateral({
+ from: _borrower,
+ borrower: _borrower,
+ tokenIds: tokenIdsToAdd
+ });
+ _borrow({
+ from: _borrower,
+ amount: 19.8 * 1e18,
+ indexLimit: _i9_91,
+ newLup: 9.917184843435912074 * 1e18
+ });
// second borrower deposits three NFTs into the subset pool and borrows
tokenIdsToAdd = new uint256[](3);
tokenIdsToAdd[0] = 51;
tokenIdsToAdd[1] = 53;
tokenIdsToAdd[2] = 73;
- _pledgeCollateral(
- {
- from: _borrower2,
- borrower: _borrower2,
- tokenIds: tokenIdsToAdd
- }
- );
- _borrow(
- {
- from: _borrower2,
- amount: 15 * 1e18,
- indexLimit: _i9_72,
- newLup: 9.917184843435912074 * 1e18
- }
- );
+ _pledgeCollateral({
+ from: _borrower2,
+ borrower: _borrower2,
+ tokenIds: tokenIdsToAdd
+ });
+ _borrow({
+ from: _borrower2,
+ amount: 15 * 1e18,
+ indexLimit: _i9_72,
+ newLup: 9.917184843435912074 * 1e18
+ });
/*****************************/
/*** Assert pre-kick state ***/
@@ -134,24 +115,21 @@ contract ERC721PoolLiquidationsKickTest is ERC721HelperContract {
interestRateUpdate: _startTime
})
);
- _assertBorrower(
- {
- borrower: _borrower,
- borrowerDebt: 19.819038461538461548 * 1e18,
- borrowerCollateral: 2 * 1e18,
- borrowert0Np: 10.404995192307692312 * 1e18,
- borrowerCollateralization: 1.000773560501591181 * 1e18
- }
- );
- _assertBorrower(
- {
- borrower: _borrower2,
- borrowerDebt: 15.014423076923076930 * 1e18,
- borrowerCollateral: 3 * 1e18,
- borrowert0Np: 5.255048076923076925 * 1e18,
- borrowerCollateralization: 1.981531649793150539 * 1e18
- }
- );
+ _assertBorrower({
+ borrower: _borrower,
+ borrowerDebt: 19.819038461538461548 * 1e18,
+ borrowerCollateral: 2 * 1e18,
+ borrowert0Np: 10.404995192307692312 * 1e18,
+ borrowerCollateralization: 1.000773560501591181 * 1e18
+ });
+ _assertBorrower({
+ borrower: _borrower2,
+ borrowerDebt: 15.014423076923076930 * 1e18,
+ borrowerCollateral: 3 * 1e18,
+ borrowert0Np: 5.255048076923076925 * 1e18,
+ borrowerCollateralization: 1.981531649793150539 * 1e18
+ });
+
assertEq(_quote.balanceOf(_lender), 47_000 * 1e18);
}
@@ -176,27 +154,22 @@ contract ERC721PoolLiquidationsKickTest is ERC721HelperContract {
neutralPrice: 0
})
);
+ _assertBorrower({
+ borrower: _borrower,
+ borrowerDebt: 22.728719829841718804 * 1e18,
+ borrowerCollateral: 2 * 1e18,
+ borrowert0Np: 10.404995192307692312 * 1e18,
+ borrowerCollateralization: 0.872656701977127996 * 1e18
+ });
- _assertBorrower(
- {
- borrower: _borrower,
- borrowerDebt: 22.728719829841718804 * 1e18,
- borrowerCollateral: 2 * 1e18,
- borrowert0Np: 10.404995192307692312 * 1e18,
- borrowerCollateralization: 0.872656701977127996 * 1e18
- }
- );
-
- _kick(
- {
- from: _lender,
- borrower: _borrower,
- debt: 23.012828827714740289 * 1e18,
- collateral: 2 * 1e18,
- bond: 0.227287198298417188 * 1e18,
- transferAmount: 0.227287198298417188 * 1e18
- }
- );
+ _kick({
+ from: _lender,
+ borrower: _borrower,
+ debt: 23.012828827714740289 * 1e18,
+ collateral: 2 * 1e18,
+ bond: 0.227287198298417188 * 1e18,
+ transferAmount: 0.227287198298417188 * 1e18
+ });
/******************************/
/*** Assert Post-kick state ***/
@@ -219,25 +192,6 @@ contract ERC721PoolLiquidationsKickTest is ERC721HelperContract {
interestRateUpdate: block.timestamp
})
);
- _assertBorrower(
- {
- borrower: _borrower,
- borrowerDebt: 23.012828827714740289 * 1e18,
- borrowerCollateral: 2 * 1e18,
- borrowert0Np: 10.404995192307692312 * 1e18,
- borrowerCollateralization: 0.861883162446546169 * 1e18
- }
- );
- _assertBorrower(
- {
- borrower: _borrower2,
- borrowerDebt: 17.218727143819483942 * 1e18,
- borrowerCollateral: 3 * 1e18,
- borrowert0Np: 5.255048076923076925 * 1e18,
- borrowerCollateralization: 1.727860269914713433 * 1e18
- }
- );
- assertEq(_quote.balanceOf(_lender), 46_999.772712801701582812 * 1e18);
_assertAuction(
AuctionParams({
borrower: _borrower,
@@ -254,46 +208,53 @@ contract ERC721PoolLiquidationsKickTest is ERC721HelperContract {
neutralPrice: 11.932577910666902372 * 1e18
})
);
- _assertKicker(
- {
- kicker: _lender,
- claimable: 0,
- locked: 0.227287198298417188 * 1e18
- }
- );
+ _assertBorrower({
+ borrower: _borrower,
+ borrowerDebt: 23.012828827714740289 * 1e18,
+ borrowerCollateral: 2 * 1e18,
+ borrowert0Np: 10.404995192307692312 * 1e18,
+ borrowerCollateralization: 0.861883162446546169 * 1e18
+ });
+ _assertBorrower({
+ borrower: _borrower2,
+ borrowerDebt: 17.218727143819483942 * 1e18,
+ borrowerCollateral: 3 * 1e18,
+ borrowert0Np: 5.255048076923076925 * 1e18,
+ borrowerCollateralization: 1.727860269914713433 * 1e18
+ });
+ _assertKicker({
+ kicker: _lender,
+ claimable: 0,
+ locked: 0.227287198298417188 * 1e18
+ });
+
+ assertEq(_quote.balanceOf(_lender), 46_999.772712801701582812 * 1e18);
// kick should fail if borrower in liquidation
- _assertKickAuctionActiveRevert(
- {
- from: _lender,
- borrower: _borrower
- }
- );
+ _assertKickAuctionActiveRevert({
+ from: _lender,
+ borrower: _borrower
+ });
// kick should fail if borrower properly collateralized
- _assertKickCollateralizedBorrowerRevert(
- {
- from: _lender,
- borrower: _borrower2
- }
- );
+ _assertKickCollateralizedBorrowerRevert({
+ from: _lender,
+ borrower: _borrower2
+ });
// check locked pool actions if auction kicked for more than 72 hours and auction head not cleared
skip(80 hours);
- _assertRemoveLiquidityAuctionNotClearedRevert(
- {
- from: _lender,
- amount: 1_000 * 1e18,
- index: _i9_91
- }
- );
- _assertRemoveCollateralAuctionNotClearedRevert(
- {
- from: _lender,
- amount: 10 * 1e18,
- index: _i9_91
- }
- );
+ _assertRemoveLiquidityAuctionNotClearedRevert({
+ from: _lender,
+ amount: 1_000 * 1e18,
+ index: _i9_91
+ });
+
+ _assertRemoveCollateralAuctionNotClearedRevert({
+ from: _lender,
+ amount: 10 * 1e18,
+ index: _i9_91
+ });
}
function testKickSubsetPoolAndSettleByRepayAndPledge() external tearDown {
@@ -316,26 +277,23 @@ contract ERC721PoolLiquidationsKickTest is ERC721HelperContract {
neutralPrice: 0
})
);
- _assertBorrower(
- {
- borrower: _borrower,
- borrowerDebt: 22.728719829841718804 * 1e18,
- borrowerCollateral: 2 * 1e18,
- borrowert0Np: 10.404995192307692312 * 1e18,
- borrowerCollateralization: 0.872656701977127996 * 1e18
- }
- );
+ _assertBorrower({
+ borrower: _borrower,
+ borrowerDebt: 22.728719829841718804 * 1e18,
+ borrowerCollateral: 2 * 1e18,
+ borrowert0Np: 10.404995192307692312 * 1e18,
+ borrowerCollateralization: 0.872656701977127996 * 1e18
+ });
+
+ _kick({
+ from: _lender,
+ borrower: _borrower,
+ debt: 23.012828827714740289 * 1e18,
+ collateral: 2 * 1e18,
+ bond: 0.227287198298417188 * 1e18,
+ transferAmount: 0.227287198298417188 * 1e18
+ });
- _kick(
- {
- from: _lender,
- borrower: _borrower,
- debt: 23.012828827714740289 * 1e18,
- collateral: 2 * 1e18,
- bond: 0.227287198298417188 * 1e18,
- transferAmount: 0.227287198298417188 * 1e18
- }
- );
_assertAuction(
AuctionParams({
borrower: _borrower,
@@ -352,17 +310,16 @@ contract ERC721PoolLiquidationsKickTest is ERC721HelperContract {
neutralPrice: 11.932577910666902372 * 1e18
})
);
- _assertBorrower(
- {
- borrower: _borrower,
- borrowerDebt: 23.012828827714740289 * 1e18,
- borrowerCollateral: 2 * 1e18,
- borrowert0Np: 10.404995192307692312 * 1e18,
- borrowerCollateralization: 0.861883162446546169 * 1e18
- }
- );
+ _assertBorrower({
+ borrower: _borrower,
+ borrowerDebt: 23.012828827714740289 * 1e18,
+ borrowerCollateral: 2 * 1e18,
+ borrowert0Np: 10.404995192307692312 * 1e18,
+ borrowerCollateralization: 0.861883162446546169 * 1e18
+ });
uint256 snapshot = vm.snapshot();
+
// borrower repays debt in order to exit from auction
_repayDebt({
from: _borrower,
@@ -389,27 +346,24 @@ contract ERC721PoolLiquidationsKickTest is ERC721HelperContract {
neutralPrice: 0
})
);
- _assertBorrower(
- {
- borrower: _borrower,
- borrowerDebt: 0,
- borrowerCollateral: 2 * 1e18,
- borrowert0Np: 0,
- borrowerCollateralization: 1 * 1e18
- }
- );
+ _assertBorrower({
+ borrower: _borrower,
+ borrowerDebt: 0,
+ borrowerCollateral: 2 * 1e18,
+ borrowert0Np: 0,
+ borrowerCollateralization: 1 * 1e18
+ });
+
vm.revertTo(snapshot);
// borrower pledge one more NFT to exit from auction
uint256[] memory tokenIdsToAdd = new uint256[](1);
tokenIdsToAdd[0] = 5;
- _pledgeCollateral(
- {
- from: _borrower,
- borrower: _borrower,
- tokenIds: tokenIdsToAdd
- }
- );
+ _pledgeCollateral({
+ from: _borrower,
+ borrower: _borrower,
+ tokenIds: tokenIdsToAdd
+ });
_assertAuction(
AuctionParams({
@@ -427,15 +381,13 @@ contract ERC721PoolLiquidationsKickTest is ERC721HelperContract {
neutralPrice: 0
})
);
- _assertBorrower(
- {
- borrower: _borrower,
- borrowerDebt: 23.012828827714740289 * 1e18,
- borrowerCollateral: 3 * 1e18,
- borrowert0Np: 6.989927127403846156 * 1e18,
- borrowerCollateralization: 1.292824743669819254 * 1e18
- }
- );
+ _assertBorrower({
+ borrower: _borrower,
+ borrowerDebt: 23.012828827714740289 * 1e18,
+ borrowerCollateral: 3 * 1e18,
+ borrowert0Np: 6.989927127403846156 * 1e18,
+ borrowerCollateralization: 1.292824743669819254 * 1e18
+ });
}
}
\ No newline at end of file
diff --git a/tests/forge/ERC721Pool/ERC721PoolLiquidationsSettle.t.sol b/tests/forge/ERC721Pool/ERC721PoolLiquidationsSettle.t.sol
index 48f3bf1c9..18010fd62 100644
--- a/tests/forge/ERC721Pool/ERC721PoolLiquidationsSettle.t.sol
+++ b/tests/forge/ERC721Pool/ERC721PoolLiquidationsSettle.t.sol
@@ -34,36 +34,29 @@ contract ERC721PoolLiquidationsSettleTest is ERC721HelperContract {
_mintAndApproveCollateralTokens(_borrower2, 74);
// Lender adds Quote token in one bucket
- _addInitialLiquidity(
- {
- from: _lender,
- amount: 15_000 * 1e18,
- index: 2500
- }
- );
- _addInitialLiquidity(
- {
- from: _lender,
- amount: 1_000 * 1e18,
- index: 2501
- }
- );
+ _addInitialLiquidity({
+ from: _lender,
+ amount: 15_000 * 1e18,
+ index: 2500
+ });
+ _addInitialLiquidity({
+ from: _lender,
+ amount: 1_000 * 1e18,
+ index: 2501
+ });
// first borrower adds collateral token and borrows
uint256[] memory tokenIdsToAdd = new uint256[](2);
tokenIdsToAdd[0] = 1;
tokenIdsToAdd[1] = 3;
-
// borrower deposits two NFTs into the subset pool and borrows
- _drawDebtNoLupCheck(
- {
- from: _borrower,
- borrower: _borrower,
- amountToBorrow: 5_000 * 1e18,
- limitIndex: 5000,
- tokenIds: tokenIdsToAdd
- }
- );
+ _drawDebtNoLupCheck({
+ from: _borrower,
+ borrower: _borrower,
+ amountToBorrow: 5_000 * 1e18,
+ limitIndex: 5000,
+ tokenIds: tokenIdsToAdd
+ });
// second borrower deposits three NFTs into the subset pool and borrows
tokenIdsToAdd = new uint256[](3);
@@ -71,15 +64,13 @@ contract ERC721PoolLiquidationsSettleTest is ERC721HelperContract {
tokenIdsToAdd[1] = 53;
tokenIdsToAdd[2] = 73;
// borrower deposits two NFTs into the subset pool and borrows
- _drawDebtNoLupCheck(
- {
- from: _borrower2,
- borrower: _borrower2,
- amountToBorrow: 5_000 * 1e18,
- limitIndex: 5000,
- tokenIds: tokenIdsToAdd
- }
- );
+ _drawDebtNoLupCheck({
+ from: _borrower2,
+ borrower: _borrower2,
+ amountToBorrow: 5_000 * 1e18,
+ limitIndex: 5000,
+ tokenIds: tokenIdsToAdd
+ });
/*****************************/
/*** Assert pre-kick state ***/
@@ -102,24 +93,21 @@ contract ERC721PoolLiquidationsSettleTest is ERC721HelperContract {
interestRateUpdate: _startTime
})
);
- _assertBorrower(
- {
- borrower: _borrower,
- borrowerDebt: 5_004.807692307692310000 * 1e18,
- borrowerCollateral: 2 * 1e18,
- borrowert0Np: 2_627.524038461538462750 * 1e18,
- borrowerCollateralization: 1.543977154129479546 * 1e18
- }
- );
- _assertBorrower(
- {
- borrower: _borrower2,
- borrowerDebt: 5_004.807692307692310000 * 1e18,
- borrowerCollateral: 3 * 1e18,
- borrowert0Np: 1_751.682692307692308500 * 1e18,
- borrowerCollateralization: 2.315965731194219318 * 1e18
- }
- );
+ _assertBorrower({
+ borrower: _borrower,
+ borrowerDebt: 5_004.807692307692310000 * 1e18,
+ borrowerCollateral: 2 * 1e18,
+ borrowert0Np: 2_627.524038461538462750 * 1e18,
+ borrowerCollateralization: 1.543977154129479546 * 1e18
+ });
+ _assertBorrower({
+ borrower: _borrower2,
+ borrowerDebt: 5_004.807692307692310000 * 1e18,
+ borrowerCollateral: 3 * 1e18,
+ borrowert0Np: 1_751.682692307692308500 * 1e18,
+ borrowerCollateralization: 2.315965731194219318 * 1e18
+ });
+
assertEq(_quote.balanceOf(address(_pool)), 6_000 * 1e18);
assertEq(_quote.balanceOf(_lender), 104_000 * 1e18);
assertEq(_quote.balanceOf(_borrower), 5_100 * 1e18);
@@ -130,34 +118,31 @@ contract ERC721PoolLiquidationsSettleTest is ERC721HelperContract {
/*** Kick both loans ***/
/***********************/
- _kickWithDeposit(
- {
- from: _lender,
- index: 2500,
- borrower: _borrower,
- debt: 5_067.367788461538463875 * 1e18,
- collateral: 2 * 1e18,
- bond: 1_501.442307692307693000 * 1e18,
- removedFromDeposit: 1_501.442307692307693000 * 1e18,
- transferAmount: 0,
- lup: 3_863.654368867279344664 * 1e18
- }
- );
- _kickWithDeposit(
- {
- from: _lender,
- index: 2500,
- borrower: _borrower2,
- debt: 5_067.367788461538463875 * 1e18,
- collateral: 3 * 1e18,
- bond: 1_501.442307692307693000 * 1e18,
- removedFromDeposit: 1_501.442307692307693000 * 1e18,
- transferAmount: 0,
- lup: 3_863.654368867279344664 * 1e18
- }
- );
+ _kickWithDeposit({
+ from: _lender,
+ index: 2500,
+ borrower: _borrower,
+ debt: 5_067.367788461538463875 * 1e18,
+ collateral: 2 * 1e18,
+ bond: 1_501.442307692307693000 * 1e18,
+ removedFromDeposit: 1_501.442307692307693000 * 1e18,
+ transferAmount: 0,
+ lup: 3_863.654368867279344664 * 1e18
+ });
- // skip to make loans clearable
+ _kickWithDeposit({
+ from: _lender,
+ index: 2500,
+ borrower: _borrower2,
+ debt: 5_067.367788461538463875 * 1e18,
+ collateral: 3 * 1e18,
+ bond: 1_501.442307692307693000 * 1e18,
+ removedFromDeposit: 1_501.442307692307693000 * 1e18,
+ transferAmount: 0,
+ lup: 3_863.654368867279344664 * 1e18
+ });
+
+ // skip to make loans clearable
skip(80 hours);
_assertPool(
PoolParams({
@@ -176,24 +161,21 @@ contract ERC721PoolLiquidationsSettleTest is ERC721HelperContract {
interestRateUpdate: _startTime
})
);
- _assertBorrower(
- {
- borrower: _borrower,
- borrowerDebt: 5_069.682183392068152308 * 1e18,
- borrowerCollateral: 2 * 1e18,
- borrowert0Np: 2_627.524038461538462750 * 1e18,
- borrowerCollateralization: 1.524219558190194493 * 1e18
- }
- );
- _assertBorrower(
- {
- borrower: _borrower2,
- borrowerDebt: 5_069.682183392068152308 * 1e18,
- borrowerCollateral: 3 * 1e18,
- borrowert0Np: 1_751.682692307692308500 * 1e18,
- borrowerCollateralization: 2.286329337285291739 * 1e18
- }
- );
+ _assertBorrower({
+ borrower: _borrower,
+ borrowerDebt: 5_069.682183392068152308 * 1e18,
+ borrowerCollateral: 2 * 1e18,
+ borrowert0Np: 2_627.524038461538462750 * 1e18,
+ borrowerCollateralization: 1.524219558190194493 * 1e18
+ });
+ _assertBorrower({
+ borrower: _borrower2,
+ borrowerDebt: 5_069.682183392068152308 * 1e18,
+ borrowerCollateral: 3 * 1e18,
+ borrowert0Np: 1_751.682692307692308500 * 1e18,
+ borrowerCollateralization: 2.286329337285291739 * 1e18
+ });
+
assertEq(_quote.balanceOf(address(_pool)), 6_000 * 1e18);
assertEq(_quote.balanceOf(_lender), 104_000 * 1e18);
assertEq(_quote.balanceOf(_borrower), 5_100 * 1e18);
@@ -220,14 +202,22 @@ contract ERC721PoolLiquidationsSettleTest is ERC721HelperContract {
neutralPrice: 1_751.682692307692308500 * 1e18
})
);
- _settle(
- {
- from: _lender,
- borrower: _borrower2,
- maxDepth: 1,
- settledDebt: 5_067.367788461538463875 * 1e18
- }
- );
+
+ // revert if auction is not settled
+ _assertMergeRemoveCollateralAuctionNotClearedRevert({
+ from: _lender,
+ toIndex: MAX_FENWICK_INDEX,
+ noOfNFTsToRemove: 2,
+ removeCollateralAtIndex: new uint256[](0)
+ });
+
+ _settle({
+ from: _lender,
+ borrower: _borrower2,
+ maxDepth: 1,
+ settledDebt: 5_067.367788461538463875 * 1e18
+ });
+
_assertAuction(
AuctionParams({
borrower: _borrower2,
@@ -245,31 +235,21 @@ contract ERC721PoolLiquidationsSettleTest is ERC721HelperContract {
})
);
+ // revert if auction is not settled
+ _assertMergeRemoveCollateralAuctionNotClearedRevert({
+ from: _lender,
+ toIndex: MAX_FENWICK_INDEX,
+ noOfNFTsToRemove: 2,
+ removeCollateralAtIndex: new uint256[](0)
+ });
+
// settle borrower
- _settle(
- {
- from: _lender,
- borrower: _borrower,
- maxDepth: 5,
- settledDebt: 5_067.367788461538463875 * 1e18
- }
- );
- _assertAuction(
- AuctionParams({
- borrower: _borrower,
- active: false,
- kicker: address(0),
- bondSize: 0,
- bondFactor: 0,
- kickTime: 0,
- kickMomp: 0,
- totalBondEscrowed: 0,
- auctionPrice: 0,
- debtInAuction: 0,
- thresholdPrice: 0,
- neutralPrice: 0
- })
- );
+ _settle({
+ from: _lender,
+ borrower: _borrower,
+ maxDepth: 5,
+ settledDebt: 5_067.367788461538463875 * 1e18
+ });
_assertPool(
PoolParams({
@@ -288,77 +268,85 @@ contract ERC721PoolLiquidationsSettleTest is ERC721HelperContract {
interestRateUpdate: _startTime + 80 hours
})
);
- _assertBorrower(
- {
- borrower: _borrower,
- borrowerDebt: 0,
- borrowerCollateral: 0,
- borrowert0Np: 2_627.524038461538462750 * 1e18,
- borrowerCollateralization: 1 * 1e18
- }
- );
- _assertBorrower(
- {
- borrower: _borrower2,
- borrowerDebt: 0,
- borrowerCollateral: 1 * 1e18,
- borrowert0Np: 1_751.682692307692308500 * 1e18,
- borrowerCollateralization: 1 * 1e18
- }
+ _assertAuction(
+ AuctionParams({
+ borrower: _borrower,
+ active: false,
+ kicker: address(0),
+ bondSize: 0,
+ bondFactor: 0,
+ kickTime: 0,
+ kickMomp: 0,
+ totalBondEscrowed: 0,
+ auctionPrice: 0,
+ debtInAuction: 0,
+ thresholdPrice: 0,
+ neutralPrice: 0
+ })
);
+ _assertBorrower({
+ borrower: _borrower,
+ borrowerDebt: 0,
+ borrowerCollateral: 0,
+ borrowert0Np: 2_627.524038461538462750 * 1e18,
+ borrowerCollateralization: 1 * 1e18
+ });
+ _assertBorrower({
+ borrower: _borrower2,
+ borrowerDebt: 0,
+ borrowerCollateral: 1 * 1e18,
+ borrowert0Np: 1_751.682692307692308500 * 1e18,
+ borrowerCollateralization: 1 * 1e18
+ });
// assert bucket used for settle
- _assertBucket(
- {
- index: MAX_FENWICK_INDEX,
- lpBalance: 0.000000137345389190751978670 * 1e27,
- collateral: 1.375706158271934624 * 1e18,
- deposit: 0,
- exchangeRate: 0.999999999999999999994537736 * 1e27
- }
- );
+ _assertBucket({
+ index: MAX_FENWICK_INDEX,
+ lpBalance: 0.000000137345389190 * 1e18,
+ collateral: 1.375706158271934624 * 1e18,
+ deposit: 0,
+ exchangeRate: 1.000000000007280914 * 1e18
+ });
+
assertEq(_quote.balanceOf(address(_pool)), 6_000 * 1e18);
assertEq(_quote.balanceOf(_lender), 104_000 * 1e18);
assertEq(_quote.balanceOf(_borrower), 5_100 * 1e18);
assertEq(_quote.balanceOf(_borrower2), 13_000 * 1e18);
// lender adds liquidity in min bucket and merge / removes 2 NFTs
- _addLiquidity(
- {
- from: _lender,
- amount: 100 * 1e18,
- index: MAX_FENWICK_INDEX,
- lpAward: 100.000000000000000000546226400 * 1e27,
- newLup: MAX_PRICE
- }
- );
+ _addLiquidity({
+ from: _lender,
+ amount: 100 * 1e18,
+ index: MAX_FENWICK_INDEX,
+ lpAward: 99.999999999271908600 * 1e18,
+ newLup: MAX_PRICE
+ });
+
uint256[] memory removalIndexes = new uint256[](3);
removalIndexes[0] = 2500;
removalIndexes[1] = 2501;
removalIndexes[2] = MAX_FENWICK_INDEX;
- _mergeOrRemoveCollateral(
- {
- from: _lender,
- toIndex: MAX_FENWICK_INDEX,
- noOfNFTsToRemove: 2,
- collateralMerged: 2 * 1e18,
- removeCollateralAtIndex: removalIndexes,
- toIndexLps: 0
- }
- );
+ _mergeOrRemoveCollateral({
+ from: _lender,
+ toIndex: MAX_FENWICK_INDEX,
+ noOfNFTsToRemove: 2,
+ collateralMerged: 2 * 1e18,
+ removeCollateralAtIndex: removalIndexes,
+ toIndexLps: 0
+ });
+
// lender merge and claim one more NFT
removalIndexes = new uint256[](2);
removalIndexes[0] = 2500;
removalIndexes[1] = MAX_FENWICK_INDEX;
- _mergeOrRemoveCollateral(
- {
- from: _lender,
- toIndex: MAX_FENWICK_INDEX,
- noOfNFTsToRemove: 1,
- collateralMerged: 1 * 1e18,
- removeCollateralAtIndex: removalIndexes,
- toIndexLps: 0
- }
- );
+ _mergeOrRemoveCollateral({
+ from: _lender,
+ toIndex: MAX_FENWICK_INDEX,
+ noOfNFTsToRemove: 1,
+ collateralMerged: 1 * 1e18,
+ removeCollateralAtIndex: removalIndexes,
+ toIndexLps: 0
+ });
+
// lender claims one more settled NFT
_pool.removeCollateral(1, MAX_FENWICK_INDEX);
@@ -381,15 +369,13 @@ contract ERC721PoolLiquidationsSettleTest is ERC721HelperContract {
// check borrower 2 owner of 1 NFT
assertEq(_collateral.ownerOf(51), _borrower2);
- _assertBucket(
- {
- index: 2500,
- lpBalance: 1_861.033884081553472671582113012 * 1e27,
- collateral: 0,
- deposit: 1861.636634299022017158 * 1e18,
- exchangeRate: 1.000323879227898104734699503 * 1e27
- }
- );
+ _assertBucket({
+ index: 2500,
+ lpBalance: 1_861.033884081553472950 * 1e18,
+ collateral: 0,
+ deposit: 1_861.636634299022017158 * 1e18,
+ exchangeRate: 1.000323879227898105 * 1e18
+ });
}
function testKickAndSettleSubsetPoolByRepay() external tearDown {
@@ -397,16 +383,16 @@ contract ERC721PoolLiquidationsSettleTest is ERC721HelperContract {
assertEq(_collateral.ownerOf(51), address(_pool));
assertEq(_collateral.ownerOf(53), address(_pool));
assertEq(_collateral.ownerOf(73), address(_pool));
+
// borrower 2 repays debt and settles auction
- _repayDebtNoLupCheck(
- {
- from: _borrower2,
- borrower: _borrower2,
- amountToRepay: 6_000 * 1e18,
- amountRepaid: 0,
- collateralToPull: 3
- }
- );
+ _repayDebtNoLupCheck({
+ from: _borrower2,
+ borrower: _borrower2,
+ amountToRepay: 6_000 * 1e18,
+ amountRepaid: 0,
+ collateralToPull: 3
+ });
+
_assertAuction(
AuctionParams({
borrower: _borrower2,
@@ -423,34 +409,32 @@ contract ERC721PoolLiquidationsSettleTest is ERC721HelperContract {
neutralPrice: 0
})
);
- _assertBorrower(
- {
- borrower: _borrower2,
- borrowerDebt: 0,
- borrowerCollateral: 0,
- borrowert0Np: 0,
- borrowerCollateralization: 1 * 1e18
- }
- );
+ _assertBorrower({
+ borrower: _borrower2,
+ borrowerDebt: 0,
+ borrowerCollateral: 0,
+ borrowert0Np: 0,
+ borrowerCollateralization: 1 * 1e18
+ });
+
// after settle: NFTs pledged by auctioned borrower are owned by the borrower
assertEq(_collateral.ownerOf(51), address(_borrower2));
assertEq(_collateral.ownerOf(53), address(_borrower2));
assertEq(_collateral.ownerOf(73), address(_borrower2));
-
// before auction settle: NFTs pledged by auctioned borrower are owned by the pool
assertEq(_collateral.ownerOf(1), address(_pool));
assertEq(_collateral.ownerOf(3), address(_pool));
+
// borrower repays debt and settles auction
- _repayDebtNoLupCheck(
- {
- from: _borrower,
- borrower: _borrower,
- amountToRepay: 6_000 * 1e18,
- amountRepaid: 0,
- collateralToPull: 2
- }
- );
+ _repayDebtNoLupCheck({
+ from: _borrower,
+ borrower: _borrower,
+ amountToRepay: 6_000 * 1e18,
+ amountRepaid: 0,
+ collateralToPull: 2
+ });
+
_assertAuction(
AuctionParams({
borrower: _borrower,
@@ -467,15 +451,14 @@ contract ERC721PoolLiquidationsSettleTest is ERC721HelperContract {
neutralPrice: 0
})
);
- _assertBorrower(
- {
- borrower: _borrower,
- borrowerDebt: 0,
- borrowerCollateral: 0,
- borrowert0Np: 0,
- borrowerCollateralization: 1 * 1e18
- }
- );
+ _assertBorrower({
+ borrower: _borrower,
+ borrowerDebt: 0,
+ borrowerCollateral: 0,
+ borrowert0Np: 0,
+ borrowerCollateralization: 1 * 1e18
+ });
+
// after settle: NFTs pledged by auctioned borrower are owned by the borrower
assertEq(_collateral.ownerOf(1), address(_borrower));
assertEq(_collateral.ownerOf(3), address(_borrower));
diff --git a/tests/forge/ERC721Pool/ERC721PoolLiquidationsSettleAuction.t.sol b/tests/forge/ERC721Pool/ERC721PoolLiquidationsSettleAuction.t.sol
new file mode 100644
index 000000000..24f55ccc2
--- /dev/null
+++ b/tests/forge/ERC721Pool/ERC721PoolLiquidationsSettleAuction.t.sol
@@ -0,0 +1,1288 @@
+// SPDX-License-Identifier: UNLICENSED
+pragma solidity 0.8.14;
+
+import { ERC721Pool } from 'src/ERC721Pool.sol';
+
+import { ERC721HelperContract } from "./ERC721DSTestPlus.sol";
+
+import 'src/libraries/helpers/PoolHelper.sol';
+
+contract ERC721PoolLiquidationsSettleAuctionTest is ERC721HelperContract {
+
+ address internal _borrower;
+ address internal _borrower2;
+ address internal _lender;
+
+ function setUp() external {
+ _borrower = makeAddr("borrower");
+ _borrower2 = makeAddr("borrower2");
+ _lender = makeAddr("lender");
+
+ // deploy subset pool
+ uint256[] memory subsetTokenIds = new uint256[](9);
+ subsetTokenIds[0] = 1;
+ subsetTokenIds[1] = 2;
+ subsetTokenIds[2] = 3;
+ subsetTokenIds[3] = 4;
+ subsetTokenIds[4] = 5;
+ subsetTokenIds[5] = 6;
+ subsetTokenIds[6] = 51;
+ subsetTokenIds[7] = 53;
+ subsetTokenIds[8] = 73;
+ _pool = _deploySubsetPool(subsetTokenIds);
+
+ _mintAndApproveQuoteTokens(_lender, 120_000 * 1e18);
+ _mintAndApproveQuoteTokens(_borrower, 100 * 1e18);
+ _mintAndApproveQuoteTokens(_borrower2, 8_000 * 1e18);
+
+ _mintAndApproveCollateralTokens(_borrower, 6);
+ _mintAndApproveCollateralTokens(_borrower2, 74);
+
+ // Lender adds Quote token in one bucket
+ _addInitialLiquidity({
+ from: _lender,
+ amount: 8_000 * 1e18,
+ index: 2500
+ });
+ _addInitialLiquidity({
+ from: _lender,
+ amount: 2_000 * 1e18,
+ index: 2501
+ });
+ _addInitialLiquidity({
+ from: _lender,
+ amount: 2_000 * 1e18,
+ index: 2502
+ });
+ _addInitialLiquidity({
+ from: _lender,
+ amount: 1_000 * 1e18,
+ index: 2503
+ });
+
+ // first borrower adds collateral token and borrows
+ uint256[] memory tokenIdsToAdd = new uint256[](2);
+ tokenIdsToAdd[0] = 1;
+ tokenIdsToAdd[1] = 3;
+ // borrower deposits two NFTs into the subset pool and borrows
+ _drawDebtNoLupCheck({
+ from: _borrower,
+ borrower: _borrower,
+ amountToBorrow: 5_000 * 1e18,
+ limitIndex: 5000,
+ tokenIds: tokenIdsToAdd
+ });
+
+ // second borrower deposits three NFTs into the subset pool and borrows
+ tokenIdsToAdd = new uint256[](3);
+ tokenIdsToAdd[0] = 51;
+ tokenIdsToAdd[1] = 53;
+ tokenIdsToAdd[2] = 73;
+ // borrower deposits two NFTs into the subset pool and borrows
+ _drawDebtNoLupCheck({
+ from: _borrower2,
+ borrower: _borrower2,
+ amountToBorrow: 5_000 * 1e18,
+ limitIndex: 5000,
+ tokenIds: tokenIdsToAdd
+ });
+
+ // kick both loans
+ _kickWithDeposit({
+ from: _lender,
+ index: 2500,
+ borrower: _borrower,
+ debt: 5_067.367788461538463875 * 1e18,
+ collateral: 2 * 1e18,
+ bond: 1_501.442307692307693000 * 1e18,
+ removedFromDeposit: 1_501.442307692307693000 * 1e18,
+ transferAmount: 0,
+ lup: 3_825.305679430983794766 * 1e18
+ });
+ _kickWithDeposit({
+ from: _lender,
+ index: 2500,
+ borrower: _borrower2,
+ debt: 5_067.367788461538463875 * 1e18,
+ collateral: 3 * 1e18,
+ bond: 1_501.442307692307693000 * 1e18,
+ removedFromDeposit: 1_501.442307692307693000 * 1e18,
+ transferAmount: 0,
+ lup: 99836282890
+ });
+ }
+
+ function testSettlePartialDebtSubsetPool() external tearDown {
+ // 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);
+
+ // skip to make loans clearable
+ skip(80 hours);
+
+ _assertBucket({
+ index: 2500,
+ lpBalance: 4_997.115384615384614 * 1e18,
+ collateral: 0,
+ deposit: 4_997.115384615384614 * 1e18,
+ exchangeRate: 1 * 1e18
+ });
+ _assertBorrower({
+ borrower: _borrower,
+ borrowerDebt: 5_069.682183392068152308 * 1e18,
+ borrowerCollateral: 2 * 1e18,
+ borrowert0Np: 2_627.524038461538462750 * 1e18,
+ borrowerCollateralization: 0.000000000039385618 * 1e18
+ });
+
+ // first settle call settles partial borrower debt
+ _settle({
+ from: _lender,
+ borrower: _borrower,
+ maxDepth: 1,
+ settledDebt: 4_997.146788514113366576 * 1e18
+ });
+
+ // collateral in bucket used to settle auction increased with the amount used to settle debt
+ _assertBucket({
+ index: 2500,
+ lpBalance: 4_997.115384615384614 * 1e18,
+ collateral: 1.293963857643160539 * 1e18,
+ deposit: 0,
+ exchangeRate: 1.000463012547417693 * 1e18
+ });
+ // partial borrower debt is settled, borrower collateral decreased with the amount used to settle debt
+ _assertBorrower({
+ borrower: _borrower,
+ borrowerDebt: 70.253071652712624346 * 1e18,
+ borrowerCollateral: 0.706036142356839461 * 1e18,
+ borrowert0Np: 2_627.524038461538462750 * 1e18,
+ borrowerCollateralization: 0.000000001003344372 * 1e18
+ });
+
+ _assertCollateralInvariants();
+
+ // the 2 token ids are rebalanced and transferred to pool claimable tokens array after settle
+ assertEq(ERC721Pool(address(_pool)).bucketTokenIds(0), 3);
+ assertEq(ERC721Pool(address(_pool)).bucketTokenIds(1), 1);
+
+ // all NFTs are owned by the pool
+ assertEq(_collateral.ownerOf(1), address(_pool));
+ assertEq(_collateral.ownerOf(3), address(_pool));
+ assertEq(_collateral.ownerOf(51), address(_pool));
+ 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: 2500
+ });
+
+ _assertBucket({
+ index: 2500,
+ lpBalance: 24_987.859419295112503013 * 1e18,
+ collateral: 1.293963857643160539 * 1e18,
+ deposit: 20_000 * 1e18,
+ exchangeRate: 1.000463012547417693 * 1e18
+ });
+
+ _settle({
+ from: _lender,
+ borrower: _borrower,
+ maxDepth: 1,
+ settledDebt: 70.220999947425097299 * 1e18
+ });
+
+
+ _assertBucket({
+ index: 2500,
+ lpBalance: 24_987.859419295112503013 * 1e18,
+ collateral: 1.312146920864032689 * 1e18,
+ deposit: 19_929.746928347287375654 * 1e18,
+ exchangeRate: 1.000463012547417693 * 1e18
+ });
+ _assertBorrower({
+ borrower: _borrower,
+ borrowerDebt: 0,
+ borrowerCollateral: 0,
+ borrowert0Np: 2_627.524038461538462750 * 1e18,
+ borrowerCollateralization: 1 * 1e18
+ });
+
+ _assertCollateralInvariants();
+
+ _settle({
+ from: _lender,
+ borrower: _borrower2,
+ maxDepth: 1,
+ settledDebt: 5_067.367788461538463875 * 1e18
+ });
+
+ _assertBucket({
+ index: 2500,
+ lpBalance: 24_987.859419295112503013 * 1e18,
+ collateral: 2.624293841728065377 * 1e18,
+ deposit: 14_860.064744955219223346 * 1e18,
+ exchangeRate: 1.000463012547417693 * 1e18
+ });
+ _assertBucket({
+ index: 7388,
+ lpBalance: 0.000000137345389190 * 1e18,
+ collateral: 1.375706158271934623 * 1e18,
+ deposit: 0,
+ exchangeRate: 1.000000000007280914 * 1e18
+ });
+ // borrower 2 can claim 1 NFT token (id 51) after settle
+ _assertBorrower({
+ borrower: _borrower2,
+ borrowerDebt: 0,
+ borrowerCollateral: 1 * 1e18,
+ borrowert0Np: 1_769.243311298076895206 * 1e18,
+ borrowerCollateralization: 1 * 1e18
+ });
+
+ _assertCollateralInvariants();
+
+ assertEq(ERC721Pool(address(_pool)).borrowerTokenIds(_borrower2, 0), 51);
+
+ // 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);
+
+ // lender can claim 2 NFTs from bucket 2500
+ changePrank(_lender);
+ _pool.removeCollateral(2, 2500);
+
+ // lender adds liquidity in min bucket and merge / removes the other 2 NFTs
+ _addLiquidity({
+ from: _lender,
+ amount: 100 * 1e18,
+ index: MAX_FENWICK_INDEX,
+ lpAward: 99.999999999271908600 * 1e18,
+ newLup: MAX_PRICE
+ });
+
+ uint256[] memory removalIndexes = new uint256[](2);
+ removalIndexes[0] = 2500;
+ removalIndexes[1] = MAX_FENWICK_INDEX;
+ _mergeOrRemoveCollateral({
+ from: _lender,
+ toIndex: MAX_FENWICK_INDEX,
+ noOfNFTsToRemove: 2,
+ collateralMerged: 2 * 1e18,
+ removeCollateralAtIndex: removalIndexes,
+ toIndexLps: 0
+ });
+
+ // the 4 NFTs claimed from pool are owned by lender
+ assertEq(_collateral.ownerOf(1), _lender);
+ assertEq(_collateral.ownerOf(3), _lender);
+ assertEq(_collateral.ownerOf(53), _lender);
+ assertEq(_collateral.ownerOf(73), _lender);
+ assertEq(_collateral.ownerOf(51), address(_pool));
+
+ // borrower 2 can pull 1 NFT from pool
+ _repayDebtNoLupCheck({
+ from: _borrower2,
+ borrower: _borrower2,
+ amountToRepay: 0,
+ amountRepaid: 0,
+ collateralToPull: 1
+ });
+
+ assertEq(_collateral.ownerOf(1), _lender);
+ assertEq(_collateral.ownerOf(3), _lender);
+ assertEq(_collateral.ownerOf(53), _lender);
+ assertEq(_collateral.ownerOf(73), _lender);
+ // the NFT pulled from pool is owned by lender
+ assertEq(_collateral.ownerOf(51), _borrower2);
+
+ _assertBucket({
+ index: 2500,
+ lpBalance: 14_853.187532758404035619 * 1e18,
+ collateral: 0,
+ deposit: 14_860.064744955219223346 * 1e18,
+ exchangeRate: 1.000463012547417693 * 1e18
+ });
+ _assertBucket({
+ index: MAX_FENWICK_INDEX,
+ lpBalance: 99.999999999271908600 * 1e18,
+ collateral: 0,
+ deposit: 100 * 1e18,
+ exchangeRate: 1.000000000007280914 * 1e18
+ });
+ _assertBorrower({
+ borrower: _borrower,
+ borrowerDebt: 0,
+ borrowerCollateral: 0,
+ borrowert0Np: 2_627.524038461538462750 * 1e18,
+ borrowerCollateralization: 1 * 1e18
+ });
+ _assertBorrower({
+ borrower: _borrower2,
+ borrowerDebt: 0,
+ borrowerCollateral: 0,
+ borrowert0Np: 0,
+ borrowerCollateralization: 1 * 1e18
+ });
+
+ _assertCollateralInvariants();
+ }
+
+ function testDepositTakeAndSettleSubsetPool() external tearDown {
+
+ // 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);
+
+ _assertBucket({
+ index: 2500,
+ lpBalance: 4997.115384615384614 * 1e18,
+ collateral: 0,
+ deposit: 4_997.115384615384614 * 1e18,
+ exchangeRate: 1 * 1e18
+ });
+ _assertBorrower({
+ borrower: _borrower,
+ borrowerDebt: 5_067.367788461538463875 * 1e18,
+ borrowerCollateral: 2 * 1e18,
+ borrowert0Np: 2_627.524038461538462750 * 1e18,
+ borrowerCollateralization: 0.000000000039403606 * 1e18
+ });
+
+ skip(32 hours);
+
+ _depositTake({
+ from: _lender,
+ borrower: _borrower,
+ index: 2500
+ });
+
+ _assertBucket({
+ index: 2500,
+ lpBalance: 7_138.736263736263734674 * 1e18,
+ collateral: 1.848006454703595366 * 1e18,
+ deposit: 0,
+ exchangeRate: 1.000185179648802797 * 1e18
+ });
+ _assertBorrower({
+ borrower: _borrower,
+ borrowerDebt: 425.033210305552211086 * 1e18,
+ borrowerCollateral: 0.151993545296404634 * 1e18,
+ borrowert0Np: 2_627.524038461538462750 * 1e18,
+ borrowerCollateralization: 0.000000000035701847 * 1e18
+ });
+
+ _assertCollateralInvariants();
+
+ // borrower tries to repay remaining debt
+ _repayDebtNoLupCheck({
+ from: _borrower2,
+ borrower: _borrower2,
+ amountToRepay: 1000 * 1e18,
+ amountRepaid: 0,
+ collateralToPull: 0
+ });
+
+ skip(80 hours);
+
+ _settle({
+ from: _lender,
+ borrower: _borrower,
+ maxDepth: 1,
+ settledDebt: 424.955585758182281304 * 1e18
+ });
+
+ _assertBucket({
+ index: 2500,
+ lpBalance: 7_138.736263736263734674 * 1e18,
+ collateral: 1.848006454703595366 * 1e18,
+ deposit: 0,
+ exchangeRate: 1.000185179648802797 * 1e18
+ });
+ _assertBucket({
+ index: 2501,
+ lpBalance: 2_000 * 1e18,
+ collateral: 0.110613668792155518 * 1e18,
+ deposit: 1_575.963420924493188203 * 1e18,
+ exchangeRate: 1.000605085927545054 * 1e18
+ });
+ _assertBucket({
+ index: MAX_FENWICK_INDEX,
+ lpBalance: 4131213057,
+ collateral: 0.041379876504249116 * 1e18,
+ deposit: 0,
+ exchangeRate: 1 * 1e18
+ });
+ _assertBorrower({
+ borrower: _borrower,
+ borrowerDebt: 0,
+ borrowerCollateral: 0,
+ borrowert0Np: 2_627.524038461538462750 * 1e18,
+ borrowerCollateralization: 1 * 1e18
+ });
+
+ _assertCollateralInvariants();
+
+ // 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);
+
+ // lender adds liquidity in min bucket and merge / removes the other 2 NFTs
+ _addLiquidity({
+ from: _lender,
+ amount: 100 * 1e18,
+ index: MAX_FENWICK_INDEX,
+ lpAward: 100 * 1e18,
+ newLup: 3_806.274307891526195092 * 1e18
+ });
+
+ uint256[] memory removalIndexes = new uint256[](3);
+ removalIndexes[0] = 2500;
+ removalIndexes[1] = 2501;
+ removalIndexes[2] = MAX_FENWICK_INDEX;
+ _mergeOrRemoveCollateral({
+ from: _lender,
+ toIndex: MAX_FENWICK_INDEX,
+ noOfNFTsToRemove: 2,
+ collateralMerged: 2 * 1e18,
+ removeCollateralAtIndex: removalIndexes,
+ toIndexLps: 0
+ });
+
+ // the 2 NFTs claimed from pool are owned by lender
+ assertEq(_collateral.ownerOf(1), _lender);
+ assertEq(_collateral.ownerOf(3), _lender);
+
+ _assertCollateralInvariants();
+ }
+
+ function testDepositTakeAndSettleByPledgeSubsetPool() external tearDown {
+
+ // 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);
+
+ _assertBucket({
+ index: 2500,
+ lpBalance: 4_997.115384615384614000 * 1e18,
+ collateral: 0,
+ deposit: 4_997.115384615384614000 * 1e18,
+ exchangeRate: 1 * 1e18
+ });
+ _assertBorrower({
+ borrower: _borrower,
+ borrowerDebt: 5_067.367788461538463875 * 1e18,
+ borrowerCollateral: 2 * 1e18,
+ borrowert0Np: 2_627.524038461538462750 * 1e18,
+ borrowerCollateralization: 0.000000000039403606 * 1e18
+ });
+
+ skip(32 hours);
+
+ _depositTake({
+ from: _lender,
+ borrower: _borrower,
+ kicker: _lender,
+ index: 2500,
+ collateralArbed: 1.848006454703595366 * 1e18,
+ quoteTokenAmount: 7_140.058212410478208121 * 1e18,
+ bondChange: 2_142.017463723143462436 * 1e18,
+ isReward: true,
+ lpAwardTaker: 0,
+ lpAwardKicker: 2_141.620879120879120674 * 1e18
+ });
+
+ _assertBucket({
+ index: 2500,
+ lpBalance: 7_138.736263736263734674 * 1e18,
+ collateral: 1.848006454703595366 * 1e18,
+ deposit: 0,
+ exchangeRate: 1.000185179648802797 * 1e18
+ });
+ _assertBorrower({
+ borrower: _borrower,
+ borrowerDebt: 425.033210305552211086 * 1e18,
+ borrowerCollateral: 0.151993545296404634 * 1e18,
+ borrowert0Np: 2_627.524038461538462750 * 1e18,
+ borrowerCollateralization: 0.000000000035701847 * 1e18
+ });
+
+ _assertCollateralInvariants();
+
+ // borrower 2 repays entire debt and pulls collateral
+ _repayDebt({
+ from: _borrower2,
+ borrower: _borrower2,
+ amountToRepay: 6_000 * 1e18,
+ amountRepaid: 5_068.293419619520519499 * 1e18,
+ collateralToPull: 3,
+ newLup: 3_844.432207828138682757 * 1e18
+ });
+
+ // borrower exits from auction by pledging more collateral
+ uint256[] memory tokenIdsToAdd = new uint256[](3);
+ tokenIdsToAdd[0] = 2;
+ tokenIdsToAdd[1] = 4;
+ tokenIdsToAdd[2] = 5;
+ _drawDebtNoLupCheck({
+ from: _borrower,
+ borrower: _borrower,
+ amountToBorrow: 0,
+ limitIndex: 0,
+ tokenIds: tokenIdsToAdd
+ });
+
+ _assertBorrower({
+ borrower: _borrower,
+ borrowerDebt: 425.033210305552211086 * 1e18,
+ borrowerCollateral: 3 * 1e18,
+ borrowert0Np: 149.442714324960768925 * 1e18,
+ borrowerCollateralization: 27.135048141751657646 * 1e18
+ });
+ _assertAuction(
+ AuctionParams({
+ borrower: _borrower,
+ active: false,
+ kicker: address(0),
+ bondSize: 0,
+ bondFactor: 0,
+ kickTime: 0,
+ kickMomp: 0,
+ totalBondEscrowed: 0,
+ auctionPrice: 0,
+ debtInAuction: 0,
+ thresholdPrice: 141.677736768517403695 * 1e18,
+ neutralPrice: 0
+ })
+ );
+
+ _assertCollateralInvariants();
+
+ // the 3 new token ids pledged are owned by borrower
+ assertEq(ERC721Pool(address(_pool)).borrowerTokenIds(_borrower, 0), 1);
+ assertEq(ERC721Pool(address(_pool)).borrowerTokenIds(_borrower, 1), 3);
+ assertEq(ERC721Pool(address(_pool)).borrowerTokenIds(_borrower, 2), 2);
+ // tokens used to settle auction are moved to pool claimable array
+ assertEq(ERC721Pool(address(_pool)).bucketTokenIds(0), 5);
+ assertEq(ERC721Pool(address(_pool)).bucketTokenIds(1), 4);
+
+ _assertBucket({
+ index: 2500,
+ lpBalance: 7_138.736263736263734674 * 1e18,
+ collateral: 1.848006454703595366 * 1e18,
+ deposit: 0,
+ exchangeRate: 1.000185179648802797 * 1e18
+ });
+ _assertBucket({
+ index: 6113,
+ lpBalance: 0.000008766823996015 * 1e18,
+ collateral: 0.151993545296404634 * 1e18,
+ deposit: 0,
+ exchangeRate: 1 * 1e18
+ });
+
+ // lender adds liquidity in bucket 6113 and merge / removes the other 2 NFTs
+ _addLiquidity({
+ from: _lender,
+ amount: 1000 * 1e18,
+ index: 6113,
+ lpAward: 1_000 * 1e18,
+ newLup: 3_844.432207828138682757 * 1e18
+ });
+ uint256[] memory removalIndexes = new uint256[](2);
+ removalIndexes[0] = 2500;
+ removalIndexes[1] = 6113;
+ _mergeOrRemoveCollateral({
+ from: _lender,
+ toIndex: 6113,
+ noOfNFTsToRemove: 2,
+ collateralMerged: 2 * 1e18,
+ removeCollateralAtIndex: removalIndexes,
+ toIndexLps: 0
+ });
+
+ // borrower repays entire debt and pulls collateral
+ _repayDebt({
+ from: _borrower,
+ borrower: _borrower,
+ amountToRepay: 500 * 1e18,
+ amountRepaid: 425.033210305552211086 * 1e18,
+ collateralToPull: 3,
+ newLup: MAX_PRICE
+ });
+ // borrower removes tokens from auction price bucket for compensated collateral fraction
+ _removeAllLiquidity({
+ from: _borrower,
+ amount: 0.000008757551393712 * 1e18,
+ index: 6113,
+ newLup: MAX_PRICE,
+ lpRedeem: 0.000008766823996015 * 1e18
+ });
+
+ // the 3 NFTs pulled from pool are owned by borrower
+ assertEq(_collateral.ownerOf(1), _borrower);
+ assertEq(_collateral.ownerOf(2), _borrower);
+ assertEq(_collateral.ownerOf(3), _borrower);
+ // the 2 NFTs claimed from pool are owned by lender
+ assertEq(_collateral.ownerOf(5), _lender);
+ assertEq(_collateral.ownerOf(4), _lender);
+
+ _assertCollateralInvariants();
+ }
+
+ function testDepositTakeAndSettleByRepaySubsetPool() external tearDown {
+
+ // 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);
+
+ _assertBucket({
+ index: 2500,
+ lpBalance: 4997.115384615384614 * 1e18,
+ collateral: 0,
+ deposit: 4_997.115384615384614000 * 1e18,
+ exchangeRate: 1 * 1e18
+ });
+ _assertBorrower({
+ borrower: _borrower,
+ borrowerDebt: 5_067.367788461538463875 * 1e18,
+ borrowerCollateral: 2 * 1e18,
+ borrowert0Np: 2_627.524038461538462750 * 1e18,
+ borrowerCollateralization: 0.000000000039403606 * 1e18
+ });
+
+ skip(32 hours);
+
+ _depositTake({
+ from: _lender,
+ borrower: _borrower,
+ kicker: _lender,
+ index: 2500,
+ collateralArbed: 1.848006454703595366 * 1e18,
+ quoteTokenAmount: 7_140.058212410478208121 * 1e18,
+ bondChange: 2_142.017463723143462436 * 1e18,
+ isReward: true,
+ lpAwardTaker: 0,
+ lpAwardKicker: 2_141.620879120879120674 * 1e18
+ });
+
+ _assertBucket({
+ index: 2500,
+ lpBalance: 7_138.736263736263734674 * 1e18,
+ collateral: 1.848006454703595366 * 1e18,
+ deposit: 0,
+ exchangeRate: 1.000185179648802797 * 1e18
+ });
+ _assertBorrower({
+ borrower: _borrower,
+ borrowerDebt: 425.033210305552211086 * 1e18,
+ borrowerCollateral: 0.151993545296404634 * 1e18,
+ borrowert0Np: 2_627.524038461538462750 * 1e18,
+ borrowerCollateralization: 0.000000000035701847 * 1e18
+ });
+
+ _assertCollateralInvariants();
+
+ // borrower 2 repays entire debt and pulls collateral
+ _repayDebt({
+ from: _borrower2,
+ borrower: _borrower2,
+ amountToRepay: 6_000 * 1e18,
+ amountRepaid: 5_068.293419619520519499 * 1e18,
+ collateralToPull: 3,
+ newLup: 3_844.432207828138682757 * 1e18
+ });
+ // borrower exits from auction by repaying the debt
+ _repayDebt({
+ from: _borrower,
+ borrower: _borrower,
+ amountToRepay: 500 * 1e18,
+ amountRepaid: 425.033210305552211086 * 1e18,
+ collateralToPull: 0,
+ newLup: MAX_PRICE
+ });
+
+ _assertBorrower({
+ borrower: _borrower,
+ borrowerDebt: 0,
+ borrowerCollateral: 0,
+ borrowert0Np: 0,
+ borrowerCollateralization: 1 * 1e18
+ });
+ _assertAuction(
+ AuctionParams({
+ borrower: _borrower,
+ active: false,
+ kicker: address(0),
+ bondSize: 0,
+ bondFactor: 0,
+ kickTime: 0,
+ kickMomp: 0,
+ totalBondEscrowed: 0,
+ auctionPrice: 0,
+ debtInAuction: 0,
+ thresholdPrice: 0,
+ neutralPrice: 0
+ })
+ );
+
+ _assertCollateralInvariants();
+
+ // 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);
+
+ _assertBucket({
+ index: 2500,
+ lpBalance: 7_138.736263736263734674 * 1e18,
+ collateral: 1.848006454703595366 * 1e18,
+ deposit: 0,
+ exchangeRate: 1.000185179648802797 * 1e18
+ });
+ _assertBucket({
+ index: 6113,
+ lpBalance: 0.000008766823996015 * 1e18,
+ collateral: 0.151993545296404634 * 1e18,
+ deposit: 0,
+ exchangeRate: 1 * 1e18
+ });
+
+ // lender adds liquidity in bucket 6113 and merge / removes the other 2 NFTs
+ _addLiquidity({
+ from: _lender,
+ amount: 1000 * 1e18,
+ index: 6113,
+ lpAward: 1_000 * 1e18,
+ newLup: MAX_PRICE
+ });
+ uint256[] memory removalIndexes = new uint256[](2);
+ removalIndexes[0] = 2500;
+ removalIndexes[1] = 6113;
+ _mergeOrRemoveCollateral({
+ from: _lender,
+ toIndex: 6113,
+ noOfNFTsToRemove: 2,
+ collateralMerged: 2 * 1e18,
+ removeCollateralAtIndex: removalIndexes,
+ toIndexLps: 0
+ });
+
+ // the 2 NFTs claimed from pool are owned by lender
+ assertEq(_collateral.ownerOf(3), _lender);
+ assertEq(_collateral.ownerOf(1), _lender);
+
+ _assertCollateralInvariants();
+
+ // borrower removes tokens from auction price bucket for compensated collateral fraction
+ _removeAllLiquidity({
+ from: _borrower,
+ amount: 0.000008757551393712 * 1e18,
+ index: 6113,
+ newLup: MAX_PRICE,
+ lpRedeem: 0.000008766823996015 * 1e18
+ });
+
+ }
+
+ function testDepositTakeAndSettleByRegularTakeSubsetPool() external tearDown {
+
+ // 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);
+
+ _assertBucket({
+ index: 2502,
+ lpBalance: 2_000 * 1e18,
+ collateral: 0,
+ deposit: 2_000 * 1e18,
+ exchangeRate: 1 * 1e18
+ });
+ _assertBorrower({
+ borrower: _borrower,
+ borrowerDebt: 5_067.367788461538463875 * 1e18,
+ borrowerCollateral: 2 * 1e18,
+ borrowert0Np: 2_627.524038461538462750 * 1e18,
+ borrowerCollateralization: 0.000000000039403606 * 1e18
+ });
+
+ skip(4 hours);
+
+ _addLiquidity({
+ from: _lender,
+ amount: 1_000 * 1e18,
+ index: 2000,
+ lpAward: 1_000 * 1e18,
+ newLup: 3_806.274307891526195092 * 1e18
+ });
+
+ _depositTake({
+ from: _lender,
+ borrower: _borrower,
+ kicker: _lender,
+ index: 2000,
+ collateralArbed: 0.021378186081598093 * 1e18,
+ quoteTokenAmount: 999.9999999999999908 * 1e18,
+ bondChange: 299.99999999999999724 * 1e18,
+ isReward: false,
+ lpAwardTaker: 0,
+ lpAwardKicker: 0
+ });
+
+ _assertBucket({
+ index: 2000,
+ lpBalance: 1_000 * 1e18,
+ collateral: 0.021378186081598093 * 1e18,
+ deposit: 0,
+ exchangeRate: 0.999999999999999991 * 1e18
+ });
+ _assertBorrower({
+ borrower: _borrower,
+ borrowerDebt: 4_422.207326928504959735 * 1e18,
+ borrowerCollateral: 1.978621813918401907 * 1e18,
+ borrowert0Np: 2_627.524038461538462750 * 1e18,
+ borrowerCollateralization: 1.703035796058482710 * 1e18
+ });
+
+ _assertCollateralInvariants();
+
+ assertEq(_quote.balanceOf(_borrower), 5_100 * 1e18);
+ assertEq(_quote.balanceOf(address(_pool)), 4_000 * 1e18);
+
+ // borrower exits from auction by regular take
+ _take({
+ from: _lender,
+ borrower: _borrower,
+ maxCollateral: 1,
+ bondChange: 1_201.442307692307695760 * 1e18,
+ givenAmount: 4_422.207326928504959735 * 1e18,
+ collateralTaken: 0.286141493566424944 * 1e18,
+ isReward: false
+ });
+
+ assertEq(_quote.balanceOf(_borrower), 16_132.410148540612418451 * 1e18);
+ assertEq(_quote.balanceOf(address(_pool)), 8_422.207326928504959735 * 1e18);
+
+ // borrower 2 repays entire debt and pulls collateral
+ _repayDebt({
+ from: _borrower2,
+ borrower: _borrower2,
+ amountToRepay: 6_000 * 1e18,
+ amountRepaid: 5_067.483483110752298817 * 1e18,
+ collateralToPull: 3,
+ newLup: MAX_PRICE
+ });
+
+ _assertBorrower({
+ borrower: _borrower,
+ borrowerDebt: 0,
+ borrowerCollateral: 0,
+ borrowert0Np: 0,
+ borrowerCollateralization: 1 * 1e18
+ });
+ _assertAuction(
+ AuctionParams({
+ borrower: _borrower,
+ active: false,
+ kicker: address(0),
+ bondSize: 0,
+ bondFactor: 0,
+ kickTime: 0,
+ kickMomp: 0,
+ totalBondEscrowed: 0,
+ auctionPrice: 0,
+ debtInAuction: 0,
+ thresholdPrice: 0,
+ neutralPrice: 0
+ })
+ );
+
+ _assertCollateralInvariants();
+
+ // remaining token is moved to pool claimable array
+ assertEq(ERC721Pool(address(_pool)).bucketTokenIds(0), 1);
+
+ _assertBucket({
+ index: 2000,
+ lpBalance: 1_000 * 1e18,
+ collateral: 0.021378186081598093 * 1e18,
+ deposit: 0,
+ exchangeRate: 0.999999999999999991 * 1e18
+ });
+ _assertBucket({
+ index: 2222,
+ lpBalance: 15_127.888999922350308085 * 1e18,
+ collateral: 0.978621813918401907 * 1e18,
+ deposit: 0,
+ exchangeRate: 1 * 1e18
+ });
+
+ // lender adds liquidity in bucket 2222 and merge / removes remaining NFTs
+ _addLiquidity({
+ from: _lender,
+ amount: 20_000 * 1e18,
+ index: 2222,
+ lpAward: 20_000 * 1e18,
+ newLup: MAX_PRICE
+ });
+ _addLiquidity({
+ from: _lender,
+ amount: 10_000 * 1e18,
+ index: 2000,
+ lpAward: 10_000.00000000000009 * 1e18,
+ newLup: MAX_PRICE
+ });
+ uint256[] memory removalIndexes = new uint256[](2);
+ removalIndexes[0] = 2000;
+ removalIndexes[1] = 2222;
+ _mergeOrRemoveCollateral({
+ from: _lender,
+ toIndex: 2222,
+ noOfNFTsToRemove: 1,
+ collateralMerged: 1 * 1e18,
+ removeCollateralAtIndex: removalIndexes,
+ toIndexLps: 0
+ });
+
+ // the 2 NFTs (one taken, one claimed) are owned by lender
+ assertEq(_collateral.ownerOf(3), _lender);
+ assertEq(_collateral.ownerOf(1), _lender);
+
+ _assertCollateralInvariants();
+
+ // borrower removes tokens from auction price bucket for compensated collateral fraction
+ _removeAllLiquidity({
+ from: _borrower,
+ amount: 15_113.342952807040348884 * 1e18,
+ index: 2222,
+ newLup: MAX_PRICE,
+ lpRedeem: 15_127.888999922350308085 * 1e18
+ });
+ }
+
+ function testDepositTakeAndSettleByBucketTakeSubsetPool() external tearDown {
+
+ // 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);
+
+ _assertBucket({
+ index: 2502,
+ lpBalance: 2_000 * 1e18,
+ collateral: 0,
+ deposit: 2_000 * 1e18,
+ exchangeRate: 1 * 1e18
+ });
+
+ _assertBorrower({
+ borrower: _borrower,
+ borrowerDebt: 5_067.367788461538463875 * 1e18,
+ borrowerCollateral: 2 * 1e18,
+ borrowert0Np: 2_627.524038461538462750 * 1e18,
+ borrowerCollateralization: 0.000000000039403606 * 1e18
+ });
+
+ skip(32 hours);
+
+ _depositTake({
+ from: _lender,
+ borrower: _borrower,
+ kicker: _lender,
+ index: 2502,
+ collateralArbed: 0.747044074730990508 * 1e18,
+ quoteTokenAmount: 2_857.671941853722277733 * 1e18,
+ bondChange: 857.301582556116683320 * 1e18,
+ isReward: true,
+ lpAwardTaker: 0,
+ lpAwardKicker: 857.142857142857143034 * 1e18
+ });
+
+ _assertBucket({
+ index: 2502,
+ lpBalance: 2_857.142857142857143034 * 1e18,
+ collateral: 0.747044074730990508 * 1e18,
+ deposit: 0,
+ exchangeRate: 1.000185179648802797 * 1e18
+ });
+ _assertBorrower({
+ borrower: _borrower,
+ borrowerDebt: 3_422.703599695281361864 * 1e18,
+ borrowerCollateral: 1.252955925269009492 * 1e18,
+ borrowert0Np: 2_627.524038461538462750 * 1e18,
+ borrowerCollateralization: 0.000000000036547267 * 1e18
+ });
+
+ _assertCollateralInvariants();
+
+ // borrower 2 repays entire debt and pulls collateral
+ _repayDebt({
+ from: _borrower2,
+ borrower: _borrower2,
+ amountToRepay: 6_000 * 1e18,
+ amountRepaid: 5_068.293419619520519499 * 1e18,
+ collateralToPull: 3,
+ newLup: 3_863.654368867279344664 * 1e18
+ });
+
+ // borrower exits from auction by bucket take: lender adds quote token at a higher priced bucket and calls deposit take
+ _addLiquidity({
+ from: _lender,
+ amount: 10_000 * 1e18,
+ index: 2400,
+ lpAward: 10_000 * 1e18,
+ newLup: 6_362.157913642177655049 * 1e18
+ });
+
+ _depositTake({
+ from: _lender,
+ borrower: _borrower,
+ kicker: _lender,
+ index: 2400,
+ collateralArbed: 0.768540585971418892 * 1e18,
+ quoteTokenAmount: 4_889.576570993259088377 * 1e18,
+ bondChange: 1_466.872971297977726513 * 1e18,
+ isReward: true,
+ lpAwardTaker: 0,
+ lpAwardKicker: 1_466.872971297977726513 * 1e18
+ });
+
+ _assertBorrower({
+ borrower: _borrower,
+ borrowerDebt: 0,
+ borrowerCollateral: 0,
+ borrowert0Np: 0,
+ borrowerCollateralization: 1 * 1e18
+ });
+ _assertAuction(
+ AuctionParams({
+ borrower: _borrower,
+ active: false,
+ kicker: address(0),
+ bondSize: 0,
+ bondFactor: 0,
+ kickTime: 0,
+ kickMomp: 0,
+ totalBondEscrowed: 0,
+ auctionPrice: 0,
+ debtInAuction: 0,
+ thresholdPrice: 0,
+ neutralPrice: 0
+ })
+ );
+
+ _assertCollateralInvariants();
+
+ // 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);
+
+ _assertBucket({
+ index: 2400,
+ lpBalance: 11_466.872971297977726513 * 1e18,
+ collateral: 0.768540585971418892 * 1e18,
+ deposit: 6_577.296400304718638136 * 1e18,
+ exchangeRate: 1 * 1e18
+ });
+ _assertBucket({
+ index: 6113,
+ lpBalance: 0.000027940555056533 * 1e18,
+ collateral: 0.484415339297590600 * 1e18,
+ deposit: 0,
+ exchangeRate: 1 * 1e18
+ });
+
+ // lender adds liquidity in bucket 6113 and merge / removes the other 2 NFTs
+ _addLiquidity({
+ from: _lender,
+ amount: 1_000 * 1e18,
+ index: 6113,
+ lpAward: 1_000 * 1e18,
+ newLup: MAX_PRICE
+ });
+ uint256[] memory removalIndexes = new uint256[](3);
+ removalIndexes[0] = 2400;
+ removalIndexes[1] = 2502;
+ removalIndexes[2] = 6113;
+ _mergeOrRemoveCollateral({
+ from: _lender,
+ toIndex: 6113,
+ noOfNFTsToRemove: 2,
+ collateralMerged: 2 * 1e18,
+ removeCollateralAtIndex: removalIndexes,
+ toIndexLps: 0
+ });
+
+ // the 2 NFTs claimed from pool are owned by lender
+ assertEq(_collateral.ownerOf(3), _lender);
+ assertEq(_collateral.ownerOf(1), _lender);
+
+ _assertCollateralInvariants();
+
+ // borrower removes tokens from auction price bucket for compensated collateral fraction
+ _removeAllLiquidity({
+ from: _borrower,
+ amount: 0.000027911002546377 * 1e18,
+ index: 6113,
+ newLup: MAX_PRICE,
+ lpRedeem: 0.000027940555056533 * 1e18
+ });
+ }
+
+ function testDepositTakeAndSettleBySettleSubsetPool() external {
+
+ // 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);
+
+ _assertBucket({
+ index: 2502,
+ lpBalance: 2_000 * 1e18,
+ collateral: 0,
+ deposit: 2_000 * 1e18,
+ exchangeRate: 1 * 1e18
+ });
+
+ _assertBorrower({
+ borrower: _borrower,
+ borrowerDebt: 5_067.367788461538463875 * 1e18,
+ borrowerCollateral: 2 * 1e18,
+ borrowert0Np: 2_627.524038461538462750 * 1e18,
+ borrowerCollateralization: 0.000000000039403606 * 1e18
+ });
+
+ skip(32 hours);
+
+ _depositTake({
+ from: _lender,
+ borrower: _borrower,
+ kicker: _lender,
+ index: 2502,
+ collateralArbed: 0.747044074730990508 * 1e18,
+ quoteTokenAmount: 2_857.671941853722277733 * 1e18,
+ bondChange: 857.301582556116683320 * 1e18,
+ isReward: true,
+ lpAwardTaker: 0,
+ lpAwardKicker: 857.142857142857143034 * 1e18
+ });
+
+ _assertBucket({
+ index: 2502,
+ lpBalance: 2_857.142857142857143034 * 1e18,
+ collateral: 0.747044074730990508 * 1e18,
+ deposit: 0,
+ exchangeRate: 1.000185179648802797 * 1e18
+ });
+ _assertBorrower({
+ borrower: _borrower,
+ borrowerDebt: 3_422.703599695281361864 * 1e18,
+ borrowerCollateral: 1.252955925269009492 * 1e18,
+ borrowert0Np: 2_627.524038461538462750 * 1e18,
+ borrowerCollateralization: 0.000000000036547267 * 1e18
+ });
+
+ _assertCollateralInvariants();
+
+ // borrower 2 repays entire debt and pulls collateral
+ _repayDebt({
+ from: _borrower2,
+ borrower: _borrower2,
+ amountToRepay: 6_000 * 1e18,
+ amountRepaid: 5_068.293419619520519499 * 1e18,
+ collateralToPull: 3,
+ newLup: 3_863.654368867279344664 * 1e18
+ });
+
+ skip(72 hours);
+
+ // borrower exits from auction by pool debt settle
+ _settle({
+ from: _lender,
+ borrower: _borrower,
+ maxDepth: 10,
+ settledDebt: 3_422.078505440842334340 * 1e18
+ });
+
+ _assertBorrower({
+ borrower: _borrower,
+ borrowerDebt: 0,
+ borrowerCollateral: 0,
+ borrowert0Np: 2_627.524038461538462750 * 1e18,
+ borrowerCollateralization: 1 * 1e18
+ });
+ _assertAuction(
+ AuctionParams({
+ borrower: _borrower,
+ active: false,
+ kicker: address(0),
+ bondSize: 0,
+ bondFactor: 0,
+ kickTime: 0,
+ kickMomp: 0,
+ totalBondEscrowed: 0,
+ auctionPrice: 0,
+ debtInAuction: 0,
+ thresholdPrice: 0,
+ neutralPrice: 0
+ })
+ );
+
+ _assertCollateralInvariants();
+
+ // 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);
+
+ _assertBucket({
+ index: 2500,
+ lpBalance: 4_997.115384615384614000 * 1e18,
+ collateral: 0.886272650740532744 * 1e18,
+ deposit: 1_574.636172339194134488 * 1e18,
+ exchangeRate: 1.000354601931047794 * 1e18
+ });
+ _assertBucket({
+ index: 2502,
+ lpBalance: 2_857.142857142857143034 * 1e18,
+ collateral: 0.747044074730990508 * 1e18,
+ deposit: 0,
+ exchangeRate: 1.000185179648802797 * 1e18
+ });
+ _assertBucket({
+ index: 7388,
+ lpBalance: 0.000000036608295127 * 1e18,
+ collateral: 0.366683274528476748 * 1e18,
+ deposit: 0,
+ exchangeRate: 1 * 1e18
+ });
+
+ // lender adds liquidity in bucket 7388 and merge / removes the other 2 NFTs
+ _addLiquidity({
+ from: _lender,
+ amount: 1_000 * 1e18,
+ index: 7388,
+ lpAward: 1_000 * 1e18,
+ newLup: MAX_PRICE
+ });
+ uint256[] memory removalIndexes = new uint256[](3);
+ removalIndexes[0] = 2500;
+ removalIndexes[1] = 2502;
+ removalIndexes[2] = 7388;
+ _mergeOrRemoveCollateral({
+ from: _lender,
+ toIndex: 7388,
+ noOfNFTsToRemove: 2,
+ collateralMerged: 2 * 1e18,
+ removeCollateralAtIndex: removalIndexes,
+ toIndexLps: 0
+ });
+
+ // the 2 NFTs claimed from pool are owned by lender
+ assertEq(_collateral.ownerOf(3), _lender);
+ assertEq(_collateral.ownerOf(1), _lender);
+
+ _assertCollateralInvariants();
+ }
+}
diff --git a/tests/forge/ERC721Pool/ERC721PoolLiquidationsTake.t.sol b/tests/forge/ERC721Pool/ERC721PoolLiquidationsTake.t.sol
index 939607332..59531d696 100644
--- a/tests/forge/ERC721Pool/ERC721PoolLiquidationsTake.t.sol
+++ b/tests/forge/ERC721Pool/ERC721PoolLiquidationsTake.t.sol
@@ -3,6 +3,8 @@ pragma solidity 0.8.14;
import { ERC721HelperContract } from "./ERC721DSTestPlus.sol";
+import { NFTNoopTakeExample } from "../interactions/NFTTakeExample.sol";
+
import 'src/libraries/helpers/PoolHelper.sol';
contract ERC721PoolLiquidationsTakeTest is ERC721HelperContract {
@@ -10,11 +12,13 @@ contract ERC721PoolLiquidationsTakeTest is ERC721HelperContract {
address internal _borrower;
address internal _borrower2;
address internal _lender;
+ address internal _withdrawRecipient;
function setUp() external {
- _borrower = makeAddr("borrower");
- _borrower2 = makeAddr("borrower2");
- _lender = makeAddr("lender");
+ _borrower = makeAddr("borrower");
+ _borrower2 = makeAddr("borrower2");
+ _lender = makeAddr("lender");
+ _withdrawRecipient = makeAddr("withdrawRecipient");
// deploy subset pool
uint256[] memory subsetTokenIds = new uint256[](6);
@@ -34,84 +38,66 @@ contract ERC721PoolLiquidationsTakeTest is ERC721HelperContract {
_mintAndApproveCollateralTokens(_borrower2, 74);
// Lender adds Quote token accross 5 prices
- _addInitialLiquidity(
- {
- from: _lender,
- amount: 2_000 * 1e18,
- index: _i9_91
- }
- );
- _addInitialLiquidity(
- {
- from: _lender,
- amount: 5_000 * 1e18,
- index: _i9_81
- }
- );
- _addInitialLiquidity(
- {
- from: _lender,
- amount: 11_000 * 1e18,
- index: _i9_72
- }
- );
- _addInitialLiquidity(
- {
- from: _lender,
- amount: 25_000 * 1e18,
- index: _i9_62
- }
- );
- _addInitialLiquidity(
- {
- from: _lender,
- amount: 30_000 * 1e18,
- index: _i9_52
- }
- );
+ _addInitialLiquidity({
+ from: _lender,
+ amount: 2_000 * 1e18,
+ index: _i9_91
+ });
+ _addInitialLiquidity({
+ from: _lender,
+ amount: 5_000 * 1e18,
+ index: _i9_81
+ });
+ _addInitialLiquidity({
+ from: _lender,
+ amount: 11_000 * 1e18,
+ index: _i9_72
+ });
+ _addInitialLiquidity({
+ from: _lender,
+ amount: 25_000 * 1e18,
+ index: _i9_62
+ });
+ _addInitialLiquidity({
+ from: _lender,
+ amount: 30_000 * 1e18,
+ index: _i9_52
+ });
- // first borrower adds collateral token and borrows
+ // first borrower adds collateral token and borrows
uint256[] memory tokenIdsToAdd = new uint256[](2);
tokenIdsToAdd[0] = 1;
tokenIdsToAdd[1] = 3;
// borrower deposits two NFTs into the subset pool and borrows
- _pledgeCollateral(
- {
- from: _borrower,
- borrower: _borrower,
- tokenIds: tokenIdsToAdd
- }
- );
- _borrow(
- {
- from: _borrower,
- amount: 19.8 * 1e18,
- indexLimit: _i9_91,
- newLup: 9.917184843435912074 * 1e18
- }
- );
+ _pledgeCollateral({
+ from: _borrower,
+ borrower: _borrower,
+ tokenIds: tokenIdsToAdd
+ });
+ _borrow({
+ from: _borrower,
+ amount: 19.8 * 1e18,
+ indexLimit: _i9_91,
+ newLup: 9.917184843435912074 * 1e18
+ });
// second borrower deposits three NFTs into the subset pool and borrows
tokenIdsToAdd = new uint256[](3);
tokenIdsToAdd[0] = 51;
tokenIdsToAdd[1] = 53;
tokenIdsToAdd[2] = 73;
- _pledgeCollateral(
- {
- from: _borrower2,
- borrower: _borrower2,
- tokenIds: tokenIdsToAdd
- }
- );
- _borrow(
- {
- from: _borrower2,
- amount: 15 * 1e18,
- indexLimit: _i9_72,
- newLup: 9.917184843435912074 * 1e18
- }
- );
+ _pledgeCollateral({
+ from: _borrower2,
+ borrower: _borrower2,
+ tokenIds: tokenIdsToAdd
+ });
+ _borrow({
+ from: _borrower2,
+ amount: 15 * 1e18,
+ indexLimit: _i9_72,
+ newLup: 9.917184843435912074 * 1e18
+ });
/*****************************/
/*** Assert pre-kick state ***/
@@ -134,24 +120,21 @@ contract ERC721PoolLiquidationsTakeTest is ERC721HelperContract {
interestRateUpdate: _startTime
})
);
- _assertBorrower(
- {
- borrower: _borrower,
- borrowerDebt: 19.819038461538461548 * 1e18,
- borrowerCollateral: 2 * 1e18,
- borrowert0Np: 10.404995192307692312 * 1e18,
- borrowerCollateralization: 1.000773560501591181 * 1e18
- }
- );
- _assertBorrower(
- {
- borrower: _borrower2,
- borrowerDebt: 15.014423076923076930 * 1e18,
- borrowerCollateral: 3 * 1e18,
- borrowert0Np: 5.255048076923076925 * 1e18,
- borrowerCollateralization: 1.981531649793150539 * 1e18
- }
- );
+ _assertBorrower({
+ borrower: _borrower,
+ borrowerDebt: 19.819038461538461548 * 1e18,
+ borrowerCollateral: 2 * 1e18,
+ borrowert0Np: 10.404995192307692312 * 1e18,
+ borrowerCollateralization: 1.000773560501591181 * 1e18
+ });
+ _assertBorrower({
+ borrower: _borrower2,
+ borrowerDebt: 15.014423076923076930 * 1e18,
+ borrowerCollateral: 3 * 1e18,
+ borrowert0Np: 5.255048076923076925 * 1e18,
+ borrowerCollateralization: 1.981531649793150539 * 1e18
+ });
+
assertEq(_quote.balanceOf(_lender), 47_000 * 1e18);
}
@@ -177,26 +160,22 @@ contract ERC721PoolLiquidationsTakeTest is ERC721HelperContract {
})
);
- _assertBorrower(
- {
- borrower: _borrower,
- borrowerDebt: 22.728719829841718804 * 1e18,
- borrowerCollateral: 2 * 1e18,
- borrowert0Np: 10.404995192307692312 * 1e18,
- borrowerCollateralization: 0.872656701977127996 * 1e18
- }
- );
+ _assertBorrower({
+ borrower: _borrower,
+ borrowerDebt: 22.728719829841718804 * 1e18,
+ borrowerCollateral: 2 * 1e18,
+ borrowert0Np: 10.404995192307692312 * 1e18,
+ borrowerCollateralization: 0.872656701977127996 * 1e18
+ });
- _kick(
- {
- from: _lender,
- borrower: _borrower,
- debt: 23.012828827714740289 * 1e18,
- collateral: 2 * 1e18,
- bond: 0.227287198298417188 * 1e18,
- transferAmount: 0.227287198298417188 * 1e18
- }
- );
+ _kick({
+ from: _lender,
+ borrower: _borrower,
+ debt: 23.012828827714740289 * 1e18,
+ collateral: 2 * 1e18,
+ bond: 0.227287198298417188 * 1e18,
+ transferAmount: 0.227287198298417188 * 1e18
+ });
/******************************/
/*** Assert Post-kick state ***/
@@ -219,25 +198,23 @@ contract ERC721PoolLiquidationsTakeTest is ERC721HelperContract {
interestRateUpdate: block.timestamp
})
);
- _assertBorrower(
- {
- borrower: _borrower,
- borrowerDebt: 23.012828827714740289 * 1e18,
- borrowerCollateral: 2 * 1e18,
- borrowert0Np: 10.404995192307692312 * 1e18,
- borrowerCollateralization: 0.861883162446546169 * 1e18
- }
- );
- _assertBorrower(
- {
- borrower: _borrower2,
- borrowerDebt: 17.218727143819483942 * 1e18,
- borrowerCollateral: 3 * 1e18,
- borrowert0Np: 5.255048076923076925 * 1e18,
- borrowerCollateralization: 1.727860269914713433 * 1e18
- }
- );
+ _assertBorrower({
+ borrower: _borrower,
+ borrowerDebt: 23.012828827714740289 * 1e18,
+ borrowerCollateral: 2 * 1e18,
+ borrowert0Np: 10.404995192307692312 * 1e18,
+ borrowerCollateralization: 0.861883162446546169 * 1e18
+ });
+ _assertBorrower({
+ borrower: _borrower2,
+ borrowerDebt: 17.218727143819483942 * 1e18,
+ borrowerCollateral: 3 * 1e18,
+ borrowert0Np: 5.255048076923076925 * 1e18,
+ borrowerCollateralization: 1.727860269914713433 * 1e18
+ });
+
assertEq(_quote.balanceOf(_lender), 46_999.772712801701582812 * 1e18);
+
_assertAuction(
AuctionParams({
borrower: _borrower,
@@ -254,22 +231,22 @@ contract ERC721PoolLiquidationsTakeTest is ERC721HelperContract {
neutralPrice: 11.932577910666902372 * 1e18
})
);
- _assertKicker(
- {
- kicker: _lender,
- claimable: 0,
- locked: 0.227287198298417188 * 1e18
- }
- );
+ _assertKicker({
+ kicker: _lender,
+ claimable: 0,
+ locked: 0.227287198298417188 * 1e18
+ });
skip(5.5 hours);
// before take: NFTs pledged by auctioned borrower are owned by the pool
assertEq(_collateral.ownerOf(3), address(_pool));
assertEq(_collateral.ownerOf(1), address(_pool));
+
// before take: check quote token balances of taker and borrower
assertEq(_quote.balanceOf(_lender), 46_999.772712801701582812 * 1e18);
assertEq(_quote.balanceOf(_borrower), 119.8 * 1e18);
+
_assertAuction(
AuctionParams({
borrower: _borrower,
@@ -287,15 +264,13 @@ contract ERC721PoolLiquidationsTakeTest is ERC721HelperContract {
})
);
- _assertBorrower(
- {
- borrower: _borrower,
- borrowerDebt: 23.013479028125331754 * 1e18,
- borrowerCollateral: 2 * 1e18,
- borrowert0Np: 10.404995192307692312 * 1e18,
- borrowerCollateralization: 0.861858811639550854 * 1e18
- }
- );
+ _assertBorrower({
+ borrower: _borrower,
+ borrowerDebt: 23.013479028125331754 * 1e18,
+ borrowerCollateral: 2 * 1e18,
+ borrowert0Np: 10.404995192307692312 * 1e18,
+ borrowerCollateralization: 0.861858811639550854 * 1e18
+ });
uint256 snapshot = vm.snapshot();
@@ -303,17 +278,15 @@ contract ERC721PoolLiquidationsTakeTest is ERC721HelperContract {
/* Take partial collateral tokens (1) ***/
/****************************************/
- _take(
- {
- from: _lender,
- borrower: _borrower,
- maxCollateral: 1,
- bondChange: 0.168752135153387434 * 1e18,
- givenAmount: 16.875213515338743424 * 1e18,
- collateralTaken: 1.0 * 1e18,
- isReward: false
- }
- );
+ _take({
+ from: _lender,
+ borrower: _borrower,
+ maxCollateral: 1,
+ bondChange: 0.168752135153387434 * 1e18,
+ givenAmount: 16.875213515338743424 * 1e18,
+ collateralTaken: 1.0 * 1e18,
+ isReward: false
+ });
_assertPool(
PoolParams({
@@ -333,15 +306,13 @@ contract ERC721PoolLiquidationsTakeTest is ERC721HelperContract {
})
);
- _assertBorrower(
- {
- borrower: _borrower,
- borrowerDebt: 7.749209044755361552 * 1e18,
- borrowerCollateral: 1 * 1e18,
- borrowert0Np: 7.061045370627448273 * 1e18,
- borrowerCollateralization: 1.279767365438131935 * 1e18
- }
- );
+ _assertBorrower({
+ borrower: _borrower,
+ borrowerDebt: 7.749209044755361552 * 1e18,
+ borrowerCollateral: 1 * 1e18,
+ borrowert0Np: 7.061045370627448273 * 1e18,
+ borrowerCollateralization: 1.279767365438131935 * 1e18
+ });
_assertAuction(
AuctionParams({
@@ -359,18 +330,16 @@ contract ERC721PoolLiquidationsTakeTest is ERC721HelperContract {
neutralPrice: 0
})
);
-
- _assertKicker(
- {
- kicker: address(0),
- claimable: 0,
- locked: 0
- }
- );
+ _assertKicker({
+ kicker: address(0),
+ claimable: 0,
+ locked: 0
+ });
// after take: one NFT pledged by liquidated borrower is owned by the taker
assertEq(_collateral.ownerOf(3), _lender);
assertEq(_collateral.ownerOf(1), address(_pool));
+
// after take: check quote token balances of taker and borrower
assertEq(_quote.balanceOf(_lender), 46_982.897499286362839388 * 1e18);
assertEq(_quote.balanceOf(_borrower), 119.8 * 1e18); // no additional tokens as there is no rounding of collateral taken (1)
@@ -381,17 +350,15 @@ contract ERC721PoolLiquidationsTakeTest is ERC721HelperContract {
/*** Take all collateral tokens (2) ***/
/**************************************/
- _take(
- {
- from: _lender,
- borrower: _borrower,
- maxCollateral: 2,
- bondChange: 0.227287198298417188 * 1e18,
- givenAmount: 24.624422560094104976 * 1e18,
- collateralTaken: 1.459206577606363895 * 1e18, // not a rounded collateral, difference of 2 - 1.16 collateral should go to borrower in quote tokens at auction price
- isReward: false
- }
- );
+ _take({
+ from: _lender,
+ borrower: _borrower,
+ maxCollateral: 2,
+ bondChange: 0.227287198298417188 * 1e18,
+ givenAmount: 24.624422560094104976 * 1e18,
+ collateralTaken: 1.459206577606363895 * 1e18, // not a rounded collateral, difference of 2 - 1.16 collateral should go to borrower in quote tokens at auction price
+ isReward: false
+ });
_assertPool(
PoolParams({
@@ -411,15 +378,13 @@ contract ERC721PoolLiquidationsTakeTest is ERC721HelperContract {
})
);
- _assertBorrower(
- {
- borrower: _borrower,
- borrowerDebt: 0,
- borrowerCollateral: 0,
- borrowert0Np: 0,
- borrowerCollateralization: 1 * 1e18
- }
- );
+ _assertBorrower({
+ borrower: _borrower,
+ borrowerDebt: 0,
+ borrowerCollateral: 0,
+ borrowert0Np: 0,
+ borrowerCollateralization: 1 * 1e18
+ });
_assertAuction(
AuctionParams({
@@ -437,18 +402,16 @@ contract ERC721PoolLiquidationsTakeTest is ERC721HelperContract {
neutralPrice: 0
})
);
-
- _assertKicker(
- {
- kicker: address(0),
- claimable: 0,
- locked: 0 * 1e18
- }
- );
+ _assertKicker({
+ kicker: address(0),
+ claimable: 0,
+ locked: 0 * 1e18
+ });
// after take: NFTs pledged by liquidated borrower are owned by the taker
assertEq(_collateral.ownerOf(3), _lender);
assertEq(_collateral.ownerOf(1), _lender);
+
// after take: check quote token balances of taker and borrower
assertEq(_quote.balanceOf(_lender), 46_966.022285771024095971 * 1e18);
assertEq(_quote.balanceOf(_borrower), 128.926004470583381865 * 1e18); // borrower gets quote tokens from the difference of rounded collateral (2) and needed collateral (1.16) at auction price (19.8) = 16.6 additional tokens
@@ -475,27 +438,22 @@ contract ERC721PoolLiquidationsTakeTest is ERC721HelperContract {
neutralPrice: 0
})
);
+ _assertBorrower({
+ borrower: _borrower,
+ borrowerDebt: 22.728719829841718804 * 1e18,
+ borrowerCollateral: 2 * 1e18,
+ borrowert0Np: 10.404995192307692312 * 1e18,
+ borrowerCollateralization: 0.872656701977127996 * 1e18
+ });
- _assertBorrower(
- {
- borrower: _borrower,
- borrowerDebt: 22.728719829841718804 * 1e18,
- borrowerCollateral: 2 * 1e18,
- borrowert0Np: 10.404995192307692312 * 1e18,
- borrowerCollateralization: 0.872656701977127996 * 1e18
- }
- );
-
- _kick(
- {
- from: _lender,
- borrower: _borrower,
- debt: 23.012828827714740289 * 1e18,
- collateral: 2 * 1e18,
- bond: 0.227287198298417188 * 1e18,
- transferAmount: 0.227287198298417188 * 1e18
- }
- );
+ _kick({
+ from: _lender,
+ borrower: _borrower,
+ debt: 23.012828827714740289 * 1e18,
+ collateral: 2 * 1e18,
+ bond: 0.227287198298417188 * 1e18,
+ transferAmount: 0.227287198298417188 * 1e18
+ });
/******************************/
/*** Assert Post-kick state ***/
@@ -518,25 +476,23 @@ contract ERC721PoolLiquidationsTakeTest is ERC721HelperContract {
interestRateUpdate: block.timestamp
})
);
- _assertBorrower(
- {
- borrower: _borrower,
- borrowerDebt: 23.012828827714740289 * 1e18,
- borrowerCollateral: 2 * 1e18,
- borrowert0Np: 10.404995192307692312 * 1e18,
- borrowerCollateralization: 0.861883162446546169 * 1e18
- }
- );
- _assertBorrower(
- {
- borrower: _borrower2,
- borrowerDebt: 17.218727143819483942 * 1e18,
- borrowerCollateral: 3 * 1e18,
- borrowert0Np: 5.255048076923076925 * 1e18,
- borrowerCollateralization: 1.727860269914713433 * 1e18
- }
- );
+ _assertBorrower({
+ borrower: _borrower,
+ borrowerDebt: 23.012828827714740289 * 1e18,
+ borrowerCollateral: 2 * 1e18,
+ borrowert0Np: 10.404995192307692312 * 1e18,
+ borrowerCollateralization: 0.861883162446546169 * 1e18
+ });
+ _assertBorrower({
+ borrower: _borrower2,
+ borrowerDebt: 17.218727143819483942 * 1e18,
+ borrowerCollateral: 3 * 1e18,
+ borrowert0Np: 5.255048076923076925 * 1e18,
+ borrowerCollateralization: 1.727860269914713433 * 1e18
+ });
+
assertEq(_quote.balanceOf(_lender), 46_999.772712801701582812 * 1e18);
+
_assertAuction(
AuctionParams({
borrower: _borrower,
@@ -553,19 +509,18 @@ contract ERC721PoolLiquidationsTakeTest is ERC721HelperContract {
neutralPrice: 11.932577910666902372 * 1e18
})
);
- _assertKicker(
- {
- kicker: _lender,
- claimable: 0,
- locked: 0.227287198298417188 * 1e18
- }
- );
+ _assertKicker({
+ kicker: _lender,
+ claimable: 0,
+ locked: 0.227287198298417188 * 1e18
+ });
skip(10 hours);
// before take: NFTs pledged by auctioned borrower are owned by the pool
assertEq(_collateral.ownerOf(3), address(_pool));
assertEq(_collateral.ownerOf(1), address(_pool));
+
// before take: check quote token balances of taker
assertEq(_quote.balanceOf(_lender), 46_999.772712801701582812 * 1e18);
@@ -585,36 +540,32 @@ contract ERC721PoolLiquidationsTakeTest is ERC721HelperContract {
neutralPrice: 11.932577910666902372 * 1e18
})
);
-
- _assertBorrower(
- {
- borrower: _borrower,
- borrowerDebt: 23.014011023943546872 * 1e18,
- borrowerCollateral: 2 * 1e18,
- borrowert0Np: 10.404995192307692312 * 1e18,
- borrowerCollateralization: 0.861838888763733724 * 1e18
- }
- );
+ _assertBorrower({
+ borrower: _borrower,
+ borrowerDebt: 23.014011023943546872 * 1e18,
+ borrowerCollateral: 2 * 1e18,
+ borrowert0Np: 10.404995192307692312 * 1e18,
+ borrowerCollateralization: 0.861838888763733724 * 1e18
+ });
/**************************************/
/*** Take all collateral tokens (2) ***/
/**************************************/
- _take(
- {
- from: _lender,
- borrower: _borrower,
- maxCollateral: 2,
- bondChange: 0.014915722388333628 * 1e18,
- givenAmount: 1.491572238833362816 * 1e18,
- collateralTaken: 2 * 1e18,
- isReward: true
- }
- );
+ _take({
+ from: _lender,
+ borrower: _borrower,
+ maxCollateral: 2,
+ bondChange: 0.014915722388333628 * 1e18,
+ givenAmount: 1.491572238833362816 * 1e18,
+ collateralTaken: 2 * 1e18,
+ isReward: true
+ });
// after take: NFTs pledged by liquidated borrower are owned by the taker
assertEq(_collateral.ownerOf(3), _lender);
assertEq(_collateral.ownerOf(1), _lender);
+
// after take : Taker quote token used for buying collateral
assertEq(_quote.balanceOf(_lender), 46_998.281140562868219996 * 1e18);
@@ -637,16 +588,13 @@ contract ERC721PoolLiquidationsTakeTest is ERC721HelperContract {
);
// Borrower collateral is 0 and some debt is still to be paid
- _assertBorrower(
- {
- borrower: _borrower,
- borrowerDebt: 23.148335279174565965 * 1e18,
- borrowerCollateral: 0,
- borrowert0Np: 10.404995192307692312 * 1e18,
- borrowerCollateralization: 0
- }
- );
-
+ _assertBorrower({
+ borrower: _borrower,
+ borrowerDebt: 23.148335279174565965 * 1e18,
+ borrowerCollateral: 0,
+ borrowert0Np: 10.404995192307692312 * 1e18,
+ borrowerCollateralization: 0
+ });
_assertAuction(
AuctionParams({
borrower: _borrower,
@@ -663,24 +611,20 @@ contract ERC721PoolLiquidationsTakeTest is ERC721HelperContract {
neutralPrice: 11.932577910666902372 * 1e18
})
);
-
// kicker bond is locked as auction is not cleared
- _assertKicker(
- {
- kicker: _lender,
- claimable: 0,
- locked: 0.242202920686750816 * 1e18
- }
- );
+ _assertKicker({
+ kicker: _lender,
+ claimable: 0,
+ locked: 0.242202920686750816 * 1e18
+ });
- _settle(
- {
- from: _lender,
- borrower: _borrower,
- maxDepth: 10,
- settledDebt: 20.183898781290497858 * 1e18
- }
- );
+ // settle auction
+ _settle({
+ from: _lender,
+ borrower: _borrower,
+ maxDepth: 10,
+ settledDebt: 20.183898781290497858 * 1e18
+ });
_assertAuction(
AuctionParams({
@@ -698,18 +642,24 @@ contract ERC721PoolLiquidationsTakeTest is ERC721HelperContract {
neutralPrice: 0
})
);
+ _assertKicker({
+ kicker: _lender,
+ claimable: 0.242202920686750816 * 1e18,
+ locked: 0
+ });
- _assertKicker(
- {
- kicker: _lender,
- claimable: 0.242202920686750816 * 1e18,
- locked: 0
- }
- );
+ uint256 snapshot = vm.snapshot();
- // Kicker claims bond + reward
changePrank(_lender);
- _pool.withdrawBonds();
+
+ // Kicker claims bond + reward and transfer to a different address
+ _pool.withdrawBonds(_withdrawRecipient);
+ assertEq(_quote.balanceOf(_withdrawRecipient), 0.242202920686750816 * 1e18);
+
+ vm.revertTo(snapshot);
+
+ // Kicker claims bond + reward
+ _pool.withdrawBonds(_lender);
assertEq(_quote.balanceOf(_lender), 46_998.523343483554970812 * 1e18);
}
@@ -733,37 +683,30 @@ contract ERC721PoolLiquidationsTakeTest is ERC721HelperContract {
neutralPrice: 0
})
);
+ _assertBorrower({
+ borrower: _borrower,
+ borrowerDebt: 22.728719829841718804 * 1e18,
+ borrowerCollateral: 2 * 1e18,
+ borrowert0Np: 10.404995192307692312 * 1e18,
+ borrowerCollateralization: 0.872656701977127996 * 1e18
+ });
- _assertBorrower(
- {
- borrower: _borrower,
- borrowerDebt: 22.728719829841718804 * 1e18,
- borrowerCollateral: 2 * 1e18,
- borrowert0Np: 10.404995192307692312 * 1e18,
- borrowerCollateralization: 0.872656701977127996 * 1e18
- }
- );
-
- _kick(
- {
- from: _lender,
- borrower: _borrower,
- debt: 23.012828827714740289 * 1e18,
- collateral: 2 * 1e18,
- bond: 0.227287198298417188 * 1e18,
- transferAmount: 0.227287198298417188 * 1e18
- }
- );
+ _kick({
+ from: _lender,
+ borrower: _borrower,
+ debt: 23.012828827714740289 * 1e18,
+ collateral: 2 * 1e18,
+ bond: 0.227287198298417188 * 1e18,
+ transferAmount: 0.227287198298417188 * 1e18
+ });
- _assertBorrower(
- {
- borrower: _borrower,
- borrowerDebt: 23.012828827714740289 * 1e18,
- borrowerCollateral: 2 * 1e18,
- borrowert0Np: 10.404995192307692312 * 1e18,
- borrowerCollateralization: 0.861883162446546169 * 1e18
- }
- );
+ _assertBorrower({
+ borrower: _borrower,
+ borrowerDebt: 23.012828827714740289 * 1e18,
+ borrowerCollateral: 2 * 1e18,
+ borrowert0Np: 10.404995192307692312 * 1e18,
+ borrowerCollateralization: 0.861883162446546169 * 1e18
+ });
_assertAuction(
AuctionParams({
borrower: _borrower,
@@ -784,17 +727,16 @@ contract ERC721PoolLiquidationsTakeTest is ERC721HelperContract {
// skip enough time to accumulate debt and take to not settle auction
skip(50 hours);
- _take(
- {
- from: _lender,
- borrower: _borrower,
- maxCollateral: 1,
- bondChange: 0.000000000000006781 * 1e18,
- givenAmount: 0.000000000000678144 * 1e18,
- collateralTaken: 1 * 1e18,
- isReward: true
- }
- );
+ _take({
+ from: _lender,
+ borrower: _borrower,
+ maxCollateral: 1,
+ bondChange: 0.000000000000006781 * 1e18,
+ givenAmount: 0.000000000000678144 * 1e18,
+ collateralTaken: 1 * 1e18,
+ isReward: true
+ });
+
_assertAuction(
AuctionParams({
borrower: _borrower,
@@ -839,15 +781,23 @@ contract ERC721PoolLiquidationsTakeTest is ERC721HelperContract {
neutralPrice: 0
})
);
- _assertBorrower(
- {
- borrower: _borrower,
- borrowerDebt: 0,
- borrowerCollateral: 1 * 1e18,
- borrowert0Np: 0,
- borrowerCollateralization: 1 * 1e18
- }
- );
+ _assertBorrower({
+ borrower: _borrower,
+ borrowerDebt: 0,
+ borrowerCollateral: 1 * 1e18,
+ borrowert0Np: 0,
+ borrowerCollateralization: 1 * 1e18
+ });
+
+ // borrower should be able to pull collateral from the pool
+ _repayDebtNoLupCheck({
+ from: _borrower,
+ borrower: _borrower,
+ amountToRepay: 0,
+ amountRepaid: 0,
+ collateralToPull: 1
+ });
+
vm.revertTo(snapshot);
// borrower repays part of debt, but not enough to exit from auction
@@ -859,16 +809,15 @@ contract ERC721PoolLiquidationsTakeTest is ERC721HelperContract {
collateralToPull: 0,
newLup: _priceAt(3696)
});
+
// borrower pledge one more NFT to exit from auction
uint256[] memory tokenIdsToAdd = new uint256[](1);
tokenIdsToAdd[0] = 5;
- _pledgeCollateral(
- {
- from: _borrower,
- borrower: _borrower,
- tokenIds: tokenIdsToAdd
- }
- );
+ _pledgeCollateral({
+ from: _borrower,
+ borrower: _borrower,
+ tokenIds: tokenIdsToAdd
+ });
_assertAuction(
AuctionParams({
@@ -886,14 +835,51 @@ contract ERC721PoolLiquidationsTakeTest is ERC721HelperContract {
neutralPrice: 0
})
);
- _assertBorrower(
- {
- borrower: _borrower,
- borrowerDebt: 19.630052245331353428 * 1e18,
- borrowerCollateral: 2 * 1e18,
- borrowert0Np: 8.902861174861655548 * 1e18,
- borrowerCollateralization: 1.010408400292926569 * 1e18
- }
- );
+ _assertBorrower({
+ borrower: _borrower,
+ borrowerDebt: 19.630052245331353428 * 1e18,
+ borrowerCollateral: 2 * 1e18,
+ borrowert0Np: 8.902861174861655548 * 1e18,
+ borrowerCollateralization: 1.010408400292926569 * 1e18
+ });
+
+ }
+
+ function testTakeCollateralWithAtomicSwapSubsetPool() external tearDown {
+ // Skip to make borrower undercollateralized
+ skip(1000 days);
+
+ _kick({
+ from: _lender,
+ borrower: _borrower,
+ debt: 23.012828827714740289 * 1e18,
+ collateral: 2 * 1e18,
+ bond: 0.227287198298417188 * 1e18,
+ transferAmount: 0.227287198298417188 * 1e18
+ });
+
+ skip(5.5 hours);
+
+ uint256 initialBalance = 10_000 * 1e18;
+
+ // instantiate a NOOP taker contract which implements IERC721Taker
+ NFTNoopTakeExample taker = new NFTNoopTakeExample();
+ deal(address(_quote), address(taker), initialBalance);
+ changePrank(address(taker));
+ _quote.approve(address(_pool), type(uint256).max);
+
+ bytes memory data = abi.encode(address(_pool));
+ _pool.take(_borrower, 2, address(taker), data);
+
+ // check that token ids are the same as id pledged by borrower
+ assertEq(taker.tokenIdsReceived(0), 3);
+ assertEq(taker.tokenIdsReceived(1), 1);
+
+ // check that the amount of quote tokens passed to taker contract is the same as the one deducted from taker balance
+ uint256 currentBalance = _quote.balanceOf(address(taker));
+ assertEq(initialBalance - taker.quoteAmountDueReceived(), currentBalance);
+
+ // check address received is the address of current ajna pool
+ assertEq(taker.poolAddressReceived(), address(_pool));
}
}
\ No newline at end of file
diff --git a/tests/forge/ERC721Pool/ERC721PoolPurchaseQuote.t.sol b/tests/forge/ERC721Pool/ERC721PoolPurchaseQuote.t.sol
index 18a868850..a4c80af8f 100644
--- a/tests/forge/ERC721Pool/ERC721PoolPurchaseQuote.t.sol
+++ b/tests/forge/ERC721Pool/ERC721PoolPurchaseQuote.t.sol
@@ -48,15 +48,13 @@ contract ERC721PoolPurchaseQuoteTest is ERC721HelperContract {
assertEq(_priceAtTestIndex, 3_010.892022197881557845 * 1e18);
// check bucket state
- _assertBucket(
- {
- index: testIndex,
- lpBalance: 0,
- collateral: 0,
- deposit: 0,
- exchangeRate: 1 * 1e27
- }
- );
+ _assertBucket({
+ index: testIndex,
+ lpBalance: 0,
+ collateral: 0,
+ deposit: 0,
+ exchangeRate: 1 * 1e18
+ });
// check pool state
assertEq(_collateral.balanceOf(_bidder), 13);
@@ -66,59 +64,50 @@ contract ERC721PoolPurchaseQuoteTest is ERC721HelperContract {
assertEq(_quote.balanceOf(_bidder), 0);
// lender adds initial quote to pool
- _addInitialLiquidity(
- {
- from: _lender,
- amount: 10_000 * 1e18,
- index: testIndex
- }
- );
+ _addInitialLiquidity({
+ from: _lender,
+ amount: 10_000 * 1e18,
+ index: testIndex
+ });
// check bucket state
- _assertBucket(
- {
- index: testIndex,
- lpBalance: 10_000 * 1e27,
- collateral: 0,
- deposit: 10_000 * 1e18,
- exchangeRate: 1 * 1e27
- }
- );
+ _assertBucket({
+ index: testIndex,
+ lpBalance: 10_000 * 1e18,
+ collateral: 0,
+ deposit: 10_000 * 1e18,
+ exchangeRate: 1 * 1e18
+ });
// _bidder deposits collateral into a bucket
changePrank(_bidder);
+
uint256[] memory tokenIdsToAdd = new uint256[](3);
tokenIdsToAdd[0] = 65;
tokenIdsToAdd[1] = 70;
tokenIdsToAdd[2] = 73;
- uint256 lpBalanceChange = _addCollateral(
- {
- from: _bidder,
- tokenIds: tokenIdsToAdd,
- index: testIndex,
- lpAward: 9_032.676066593644673535 * 1e27
- }
- );
+ uint256 lpBalanceChange = _addCollateral({
+ from: _bidder,
+ tokenIds: tokenIdsToAdd,
+ index: testIndex,
+ lpAward: 9_032.676066593644673535 * 1e18
+ });
// check bucket state
- _assertBucket(
- {
- index: testIndex,
- lpBalance: 19_032.676066593644673535 * 1e27,
- collateral: Maths.wad(3),
- deposit: 10_000 * 1e18,
- exchangeRate: 1 * 1e27
- }
- );
- _assertLenderLpBalance(
- {
- lender: _bidder,
- index: testIndex,
- lpBalance: lpBalanceChange,
- depositTime: _startTime
- }
- );
+ _assertBucket({
+ index: testIndex,
+ lpBalance: 19_032.676066593644673535 * 1e18,
+ collateral: Maths.wad(3),
+ deposit: 10_000 * 1e18,
+ exchangeRate: 1 * 1e18
+ });
+ _assertLenderLpBalance({
+ lender: _bidder,
+ index: testIndex,
+ lpBalance: lpBalanceChange,
+ depositTime: _startTime
+ });
// check pool state
assertEq(_collateral.balanceOf(_bidder), 10);
@@ -129,76 +118,65 @@ contract ERC721PoolPurchaseQuoteTest is ERC721HelperContract {
// bidder removes quote token from bucket
skip(1 days); // skip to avoid penalty
+
uint256 qtToRemove = Maths.wmul(_priceAtTestIndex, 3 * 1e18);
- _removeAllLiquidity(
- {
- from: _bidder,
- amount: qtToRemove,
- index: testIndex,
- newLup: _lup(),
- lpRedeem: 9_032.676066593644673535 * 1e27
- }
- );
+
+ _removeAllLiquidity({
+ from: _bidder,
+ amount: qtToRemove,
+ index: testIndex,
+ newLup: _lup(),
+ lpRedeem: 9_032.676066593644673535 * 1e18
+ });
+
assertEq(_quote.balanceOf(_bidder), qtToRemove);
- _assertBucket(
- {
- index: testIndex,
- lpBalance: 10_000 * 1e27,
- collateral: Maths.wad(3),
- deposit: 967.323933406355326465 * 1e18,
- exchangeRate: 1 * 1e27
- }
- );
- _assertLenderLpBalance(
- {
- lender: _bidder,
- index: testIndex,
- lpBalance: 0,
- depositTime: _startTime
- }
- );
+ _assertBucket({
+ index: testIndex,
+ lpBalance: 10_000 * 1e18,
+ collateral: Maths.wad(3),
+ deposit: 967.323933406355326465 * 1e18,
+ exchangeRate: 1 * 1e18
+ });
+ _assertLenderLpBalance({
+ lender: _bidder,
+ index: testIndex,
+ lpBalance: 0,
+ depositTime: _startTime
+ });
// lender removes all collateral from bucket
- _removeCollateral(
- {
- from: _lender,
- amount: 3,
- index: testIndex,
- lpRedeem: 9_032.676066593644673535 * 1e27
- }
- );
-
- _assertBucket(
- {
- index: testIndex,
- lpBalance: 967.323933406355326465 * 1e27,
- collateral: 0,
- deposit: 967.323933406355326465 * 1e18,
- exchangeRate: 1 * 1e27
- }
- );
+ _removeCollateral({
+ from: _lender,
+ amount: 3,
+ index: testIndex,
+ lpRedeem: 9_032.676066593644673535 * 1e18
+ });
+
+ _assertBucket({
+ index: testIndex,
+ lpBalance: 967.323933406355326465 * 1e18,
+ collateral: 0,
+ deposit: 967.323933406355326465 * 1e18,
+ exchangeRate: 1 * 1e18
+ });
// lender removes remaining quote token to empty the bucket
- _removeAllLiquidity(
- {
- from: _lender,
- amount: 967.323933406355326465 * 1e18,
- index: testIndex,
- newLup: _lup(),
- lpRedeem: 967.323933406355326465 * 1e27
- }
- );
-
- _assertBucket(
- {
- index: testIndex,
- lpBalance: 0,
- collateral: 0,
- deposit: 0,
- exchangeRate: 1 * 1e27
- }
- );
+ _removeAllLiquidity({
+ from: _lender,
+ amount: 967.323933406355326465 * 1e18,
+ index: testIndex,
+ newLup: _lup(),
+ lpRedeem: 967.323933406355326465 * 1e18
+ });
+
+ _assertBucket({
+ index: testIndex,
+ lpBalance: 0,
+ collateral: 0,
+ deposit: 0,
+ exchangeRate: 1 * 1e18
+ });
}
/**
@@ -210,42 +188,32 @@ contract ERC721PoolPurchaseQuoteTest is ERC721HelperContract {
*/
function testSubsetPurchaseQuoteWithDebt() external tearDown {
// lenders add liquidity
- _addInitialLiquidity(
- {
- from: _lender,
- amount: 20_000 * 1e18,
- index: 2350
- }
- );
- _addInitialLiquidity(
- {
- from: _lender,
- amount: 10_000 * 1e18,
- index: 2351
- }
- );
- _addInitialLiquidity(
- {
- from: _lender,
- amount: 10_000 * 1e18,
- index: 2352
- }
- );
-
- _addInitialLiquidity(
- {
- from: _lender2,
- amount: 4_000 * 1e18,
- index: 2350
- }
- );
- _addInitialLiquidity(
- {
- from: _lender2,
- amount: 5_000 * 1e18,
- index: 2352
- }
- );
+ _addInitialLiquidity({
+ from: _lender,
+ amount: 20_000 * 1e18,
+ index: 2350
+ });
+ _addInitialLiquidity({
+ from: _lender,
+ amount: 10_000 * 1e18,
+ index: 2351
+ });
+ _addInitialLiquidity({
+ from: _lender,
+ amount: 10_000 * 1e18,
+ index: 2352
+ });
+
+ _addInitialLiquidity({
+ from: _lender2,
+ amount: 4_000 * 1e18,
+ index: 2350
+ });
+ _addInitialLiquidity({
+ from: _lender2,
+ amount: 5_000 * 1e18,
+ index: 2352
+ });
skip(3600);
@@ -255,34 +223,30 @@ contract ERC721PoolPurchaseQuoteTest is ERC721HelperContract {
tokenIdsToAdd[1] = 3;
tokenIdsToAdd[2] = 5;
tokenIdsToAdd[3] = 51;
- _pledgeCollateral(
- {
- from: _borrower,
- borrower: _borrower,
- tokenIds: tokenIdsToAdd
- }
- );
- _borrow(
- {
- from: _borrower,
- amount: 24_000 * 1e18,
- indexLimit: 2_351,
- newLup: 8_123.467933811934300919 * 1e18
- }
- );
+ _pledgeCollateral({
+ from: _borrower,
+ borrower: _borrower,
+ tokenIds: tokenIdsToAdd
+ });
+ _borrow({
+ from: _borrower,
+ amount: 24_000 * 1e18,
+ indexLimit: 2_351,
+ newLup: 8_123.467933811934300919 * 1e18
+ });
+
assertEq(_lup(), _priceAt(2351));
+
skip(86400);
// check bucket state
- _assertBucket(
- {
- index: 2350,
- lpBalance: 24_000 * 1e27,
- collateral: 0,
- deposit: 24_000 * 1e18,
- exchangeRate: 1 * 1e27
- }
- );
+ _assertBucket({
+ index: 2350,
+ lpBalance: 24_000 * 1e18,
+ collateral: 0,
+ deposit: 24_000 * 1e18,
+ exchangeRate: 1 * 1e18
+ });
// bidder purchases all quote from the highest bucket
tokenIdsToAdd = new uint256[](4);
@@ -292,102 +256,87 @@ contract ERC721PoolPurchaseQuoteTest is ERC721HelperContract {
tokenIdsToAdd[3] = 74;
uint256 amountToPurchase = 10_100 * 1e18;
assertGt(_quote.balanceOf(address(_pool)), amountToPurchase);
+
uint256 amountWithInterest = 24_002.749104114061152000 * 1e18;
- _addCollateral(
- {
- from: _bidder,
- tokenIds: tokenIdsToAdd,
- index: 2350,
- lpAward: 32_654.410675370944354984500292928 * 1e27
- }
- );
+ _addCollateral({
+ from: _bidder,
+ tokenIds: tokenIdsToAdd,
+ index: 2350,
+ lpAward: 32_654.410675370944354985 * 1e18
+ });
+
skip(25 hours); // remove liquidity after one day to avoid early withdraw penalty
- _removeAllLiquidity(
- {
- from: _bidder,
- amount: amountWithInterest,
- index: 2350,
- newLup: _priceAt(2352),
- lpRedeem: 24_000.766696558404292700773653981 * 1e27
- }
- );
+
+ _removeAllLiquidity({
+ from: _bidder,
+ amount: amountWithInterest,
+ index: 2350,
+ newLup: _priceAt(2352),
+ lpRedeem: 24_000.766696558404301151 * 1e18
+ });
assertEq(_quote.balanceOf(_bidder), amountWithInterest);
// check bucket state
- _assertBucket(
- {
- index: 2350,
- lpBalance: 32_653.643978812540062283726638947 * 1e27,
- collateral: Maths.wad(4),
- deposit: 0,
- exchangeRate: 1.000082597676179283352120528 * 1e27
- }
- );
+ _assertBucket({
+ index: 2350,
+ lpBalance: 32_653.643978812540053834 * 1e18,
+ collateral: Maths.wad(4),
+ deposit: 0,
+ exchangeRate: 1.000082597676179284 * 1e18
+ });
// bidder withdraws unused collateral
- (uint256 amount) = _removeCollateral(
- {
- from: _bidder,
- amount: 1,
- index: 2350,
- lpRedeem: 8_163.410994703135015570931665340 * 1e27
- }
- );
-
- _assertLenderLpBalance(
- {
- lender: _bidder,
- index: 2350,
- lpBalance: 490.232984109405046712794973607 * 1e27,
- depositTime: _startTime + 25 hours
- }
- );
+ (uint256 amount) = _removeCollateral({
+ from: _bidder,
+ amount: 1,
+ index: 2350,
+ lpRedeem: 8_163.410994703135010282 * 1e18
+ });
+
+ _assertLenderLpBalance({
+ lender: _bidder,
+ index: 2350,
+ lpBalance: 490.232984109405043552 * 1e18,
+ depositTime: _startTime + 25 hours
+ });
skip(7200);
changePrank(_lender);
// lender exchanges their lp for collateral
- (amount) = _removeCollateral(
- {
- from: _lender,
- amount: 1,
- index: 2350,
- lpRedeem: 8_163.410994703135015570931665340 * 1e27
- }
- );
-
- _assertLenderLpBalance(
- {
- lender: _bidder,
- index: 2350,
- lpBalance: 490.232984109405046712794973607 * 1e27,
- depositTime: _startTime + 25 hours
- }
- );
+ (amount) = _removeCollateral({
+ from: _lender,
+ amount: 1,
+ index: 2350,
+ lpRedeem: 8_163.410994703135018445 * 1e18
+ });
+
+ _assertLenderLpBalance({
+ lender: _bidder,
+ index: 2350,
+ lpBalance: 490.232984109405043552 * 1e18,
+ depositTime: _startTime + 25 hours
+ });
skip(3600);
// check bucket state
- _assertBucket(
- {
- index: 2350,
- lpBalance: 16_326.821989406270031141863308267 * 1e27,
- collateral: Maths.wad(2),
- deposit: 0,
- exchangeRate: 1.000082597676179283352120529 * 1e27
- }
- );
+ _assertBucket({
+ index: 2350,
+ lpBalance: 16_326.821989406270025107 * 1e18,
+ collateral: Maths.wad(2),
+ deposit: 0,
+ exchangeRate: 1.000082597676179284 * 1e18
+ });
// should revert if lender2 attempts to remove more collateral than lp is available for
- _assertRemoveCollateralInsufficientLPsRevert(
- {
- from: _lender2,
- amount: 1,
- index: 2350
- }
- );
+ _assertRemoveCollateralInsufficientLPsRevert({
+ from: _lender2,
+ amount: 1,
+ index: 2350
+ });
skip(3600);
}
diff --git a/tests/forge/ERC721Pool/ERC721PoolReserveAuction.t.sol b/tests/forge/ERC721Pool/ERC721PoolReserveAuction.t.sol
index 8a93dd435..ccbba4718 100644
--- a/tests/forge/ERC721Pool/ERC721PoolReserveAuction.t.sol
+++ b/tests/forge/ERC721Pool/ERC721PoolReserveAuction.t.sol
@@ -27,57 +27,51 @@ contract ERC721PoolReserveAuctionTest is ERC721HelperContract {
// lender adds liquidity and borrower draws debt
uint16 bucketId = 1663;
- _addInitialLiquidity(
- {
- from: _lender,
- amount: 200_000 * 1e18,
- index: bucketId
- }
- );
+
+ _addInitialLiquidity({
+ from: _lender,
+ amount: 200_000 * 1e18,
+ index: bucketId
+ });
// borrower draws debt
uint256[] memory tokenIdsToAdd = new uint256[](1);
tokenIdsToAdd[0] = 1;
- _pledgeCollateral(
- {
- from: _borrower,
- borrower: _borrower,
- tokenIds: tokenIdsToAdd
- }
- );
- _borrow(
- {
- from: _borrower,
- amount: 175_000 * 1e18,
- indexLimit: bucketId,
- newLup: 251_183.992399245533703810 * 1e18
- }
- );
+ _pledgeCollateral({
+ from: _borrower,
+ borrower: _borrower,
+ tokenIds: tokenIdsToAdd
+ });
+ _borrow({
+ from: _borrower,
+ amount: 175_000 * 1e18,
+ indexLimit: bucketId,
+ newLup: 251_183.992399245533703810 * 1e18
+ });
+
(uint256 poolDebt,,) = _pool.debtInfo();
assertEq(poolDebt - 175_000 * 1e18, 168.26923076923085 * 1e18);
+
skip(26 weeks);
+
(poolDebt,,) = _pool.debtInfo();
assertEq(poolDebt - 175_000 * 1e18, 4_590.373946590638353626 * 1e18); // debt matches develop
}
function testClaimableReserveNoAuction() external {
// ensure empty state is returned
- _assertReserveAuction(
- {
- reserves: 168.26923076923085 * 1e18,
- claimableReserves : 0,
- claimableReservesRemaining: 0,
- auctionPrice: 0,
- timeRemaining: 0
- }
- );
+ _assertReserveAuction({
+ reserves: 168.26923076923085 * 1e18,
+ claimableReserves : 0,
+ claimableReservesRemaining: 0,
+ auctionPrice: 0,
+ timeRemaining: 0
+ });
// ensure cannot take when no auction was started
- _assertTakeReservesNoAuctionRevert(
- {
- amount: 555 * 1e18
- }
- );
+ _assertTakeReservesNoAuctionRevert({
+ amount: 555 * 1e18
+ });
}
function testUnclaimableReserves() external {
@@ -91,16 +85,16 @@ contract ERC721PoolReserveAuctionTest is ERC721HelperContract {
newLup: 251_183.992399245533703810 * 1e18
});
- _assertReserveAuction(
- {
- reserves: 499.181304561658553626 * 1e18,
- claimableReserves : 0,
- claimableReservesRemaining: 0,
- auctionPrice: 0,
- timeRemaining: 0
- }
- );
+ _assertReserveAuction({
+ reserves: 499.181304561658553626 * 1e18,
+ claimableReserves : 0,
+ claimableReservesRemaining: 0,
+ auctionPrice: 0,
+ timeRemaining: 0
+ });
+
changePrank(_bidder);
+
_assertTakeReservesNoReservesRevert();
}
@@ -114,57 +108,70 @@ contract ERC721PoolReserveAuctionTest is ERC721HelperContract {
collateralToPull: 0,
newLup: MAX_PRICE
});
- _assertReserveAuction(
- {
- reserves: 499.181304561658553626 * 1e18,
- claimableReserves : 499.181304561658553626 * 1e18,
- claimableReservesRemaining: 0,
- auctionPrice: 0,
- timeRemaining: 0
- }
- );
+
+ _assertReserveAuction({
+ reserves: 499.181304561658553626 * 1e18,
+ claimableReserves : 499.181304561658553626 * 1e18,
+ claimableReservesRemaining: 0,
+ auctionPrice: 0,
+ timeRemaining: 0
+ });
// kick off a new auction
- _startClaimableReserveAuction(
- {
- from: _bidder,
- remainingReserves: 494.189491516041968090 * 1e18,
- price: 1_000_000_000 * 1e18
- }
- );
+ _startClaimableReserveAuction({
+ from: _bidder,
+ remainingReserves: 494.189491516041968090 * 1e18,
+ price: 1_000_000_000 * 1e18
+ });
+
_assertReserveAuctionPrice(1_000_000_000 * 1e18);
// check prices
skip(37 minutes);
_assertReserveAuctionPrice(652176034.882782126826643053 * 1e18);
+
skip(23 hours); // 23 hours 37 minutes
_assertReserveAuctionPrice(77.745441780421987394 * 1e18);
+
skip(1400); // 24 hours 0 minutes 20 seconds
_assertReserveAuctionPrice(59.604644775390625 * 1e18);
+
skip(100); // 24 hours 2 minutes
_assertReserveAuctionPrice(58.243272807255146201 * 1e18);
+
skip(58 minutes); // 25 hours
_assertReserveAuctionPrice(29.8023223876953125 * 1e18);
+
skip(5 hours); // 30 hours
_assertReserveAuctionPrice(0.931322574615478515 * 1e18);
+
skip(121 minutes); // 32 hours 1 minute
_assertReserveAuctionPrice(0.230156355619639189 * 1e18);
+
skip(7700 seconds); // 34 hours 9 minutes 20 seconds
_assertReserveAuctionPrice(0.052459681325756842 * 1e18);
+
skip(8 hours); // 42 hours 9 minutes 20 seconds
_assertReserveAuctionPrice(0.000204920630178738 * 1e18);
+
skip(6 hours); // 42 hours 9 minutes 20 seconds
_assertReserveAuctionPrice(0.000003201884846542 * 1e18);
+
skip(3100 seconds); // 43 hours
_assertReserveAuctionPrice(0.000001755953640897 * 1e18);
+
skip(5 hours); // 48 hours
_assertReserveAuctionPrice(0.000000054873551278 * 1e18);
+
skip(12 hours); // 60 hours
_assertReserveAuctionPrice(0.000000000013396863 * 1e18);
+
skip(11 hours); // 71 hours
_assertReserveAuctionPrice(0.000000000000006541 * 1e18);
+
skip(3599 seconds); // 71 hours 59 minutes 59 seconds
_assertReserveAuctionPrice(0.000000000000003308 * 1e18);
+
skip(1 seconds); // 72 hours
_assertReserveAuctionPrice(0.000000000000003270 * 1e18);
}
diff --git a/tests/forge/FenwickTree.t.sol b/tests/forge/FenwickTree.t.sol
index 30359e338..49af985aa 100644
--- a/tests/forge/FenwickTree.t.sol
+++ b/tests/forge/FenwickTree.t.sol
@@ -109,11 +109,11 @@ contract FenwickTreeTest is DSTestPlus {
function testFenwickFuzzyScalingPrefix(
uint256 insertions_,
uint256 totalAmount_,
+ uint256 seed_,
uint256 scaleIndex_,
uint256 factor_
- ) external {
-
- _tree.fuzzyFill(insertions_, totalAmount_, false);
+ ) external {
+ _tree.fuzzyFill(insertions_, totalAmount_, seed_, false);
uint256 scaleIndex = bound(scaleIndex_, 2, MAX_INDEX);
uint256 subIndex = randomInRange(1, scaleIndex - 1);
@@ -158,11 +158,11 @@ contract FenwickTreeTest is DSTestPlus {
function testLoadFenwickFuzzyScalingFind(
uint256 insertions_,
uint256 totalAmount_,
+ uint256 seed_,
uint256 scaleIndex_,
uint256 factor_
- ) external {
-
- _tree.fuzzyFill(insertions_, totalAmount_, false);
+ ) external {
+ _tree.fuzzyFill(insertions_, totalAmount_, seed_, false);
uint256 scaleIndex = bound(scaleIndex_, 2, 7388);
uint256 subIndex = randomInRange(0, scaleIndex - 1);
@@ -190,10 +190,10 @@ contract FenwickTreeTest is DSTestPlus {
*/
function testLoadFenwickFuzzyRemoval(
uint256 insertions_,
- uint256 totalAmount_
- ) external {
-
- _tree.fuzzyFill(insertions_, totalAmount_, true);
+ uint256 totalAmount_,
+ uint256 seed_
+ ) external {
+ _tree.fuzzyFill(insertions_, totalAmount_, seed_, true);
// get Index randombly
uint256 removalIndex = _tree.getIByInsertIndex(randomInRange(0, _tree.numInserts() - 1));
@@ -213,7 +213,6 @@ contract FenwickTreeTest is DSTestPlus {
assertEq(preRemovalTreeSum - removalAmount, _tree.treeSum());
assertEq(preRemovalParentIndexSum - removalAmount, postRemovalParentIndexSum);
}
-
}
contract FenwickTreeGasLoadTest is DSTestPlus {
diff --git a/tests/forge/Heap.t.sol b/tests/forge/Heap.t.sol
index d537d0d70..b2f93ee85 100644
--- a/tests/forge/Heap.t.sol
+++ b/tests/forge/Heap.t.sol
@@ -231,10 +231,10 @@ contract HeapTest is DSTestPlus {
assertEq(_loans.getTotalTps(), 7);
}
- function testLoadHeapFuzzy(uint256 inserts_) public {
+ function testLoadHeapFuzzy(uint256 inserts_, uint256 seed_) public {
// test adding different TPs
- _loans.fuzzyFill(inserts_, true);
+ _loans.fuzzyFill(inserts_, seed_, true);
// test adding different TPs
address removeAddress = _loans.getIdByInsertIndex(randomInRange(1, _loans.numInserts() - 1, true));
diff --git a/tests/forge/MathTest.t.sol b/tests/forge/MathTest.t.sol
index e59e894d9..51291ba55 100644
--- a/tests/forge/MathTest.t.sol
+++ b/tests/forge/MathTest.t.sol
@@ -21,7 +21,6 @@ contract MathTest is DSTestPlus {
function testZeroStaysZero() external {
assertEq(Maths.rayToWad(0), 0);
- assertEq(Maths.wadToRay(0), 0);
}
function testMultiplication() external {
@@ -31,31 +30,6 @@ contract MathTest is DSTestPlus {
assertEq(debt * inflator, 10_213.6546200311111065616975993 * 1e45);
}
- function testDivision() external {
- uint256 debt = 11_000.143012091382543917 * 1e18;
- uint256 price = 1_001.6501589292607751220 * 1e18;
-
- assertEq(Maths.wdiv(debt, price), 10.98202093218880245 * 1e18);
- assertEq(debt * 1e18 / price, 10.98202093218880245 * 1e18);
- assertEq(Maths.wwdivr(debt, price), 10.982020932188802450191601163 * 1e27);
-
- uint256 exchangeRate = 1.09232010 * 1e27;
- assertEq(Maths.rdiv(Maths.wadToRay(debt), exchangeRate), Maths.wrdivr(debt, exchangeRate));
-
- uint256 lpBalance = 36_900.58124 * 1e27;
- uint256 lpRedemption = Maths.rdiv(lpBalance, exchangeRate);
- assertEq(Maths.rayToWad(lpRedemption), Maths.rrdivw(lpBalance, exchangeRate));
- assertEq(Maths.rayToWad(Maths.rdiv(lpRedemption, Maths.wadToRay(price))), Maths.rwdivw(lpRedemption, price));
-
- uint256 claimableCollateral1 = Maths.rwdivw(Maths.rdiv(lpBalance, exchangeRate), price); // rounds
- uint256 claimableCollateral2 = lpBalance * 1e36 / exchangeRate / price; // truncates
- assertEq(claimableCollateral1, 33.726184963566645999 * 1e18);
- assertEq(claimableCollateral2, 33.726184963566645998 * 1e18);
-
- assertEq(Maths.wdiv(1 * 1e18, 60 * 1e18), 0.016666666666666667 * 1e18);
- assertEq(Maths.rdiv(1 * 1e27, 3 * 1e27), 0.333333333333333333333333333 * 1e27);
- }
-
function testScaleConversions() external {
assertEq(Maths.wad(153), 153 * 1e18);
}
diff --git a/tests/forge/PositionManager.t.sol b/tests/forge/PositionManager.t.sol
index 5e691fc0f..5545bf3bf 100644
--- a/tests/forge/PositionManager.t.sol
+++ b/tests/forge/PositionManager.t.sol
@@ -27,6 +27,7 @@ abstract contract PositionManagerERC20PoolHelperContract is ERC20HelperContract
vm.prank(operator_);
_quote.approve(address(_pool), type(uint256).max);
+
vm.prank(operator_);
_quote.approve(address(_positionManager), type(uint256).max);
}
@@ -42,7 +43,7 @@ abstract contract PositionManagerERC20PoolHelperContract is ERC20HelperContract
}
function _getPermitSig(
- address receiver_,
+ address spender_,
uint256 tokenId_,
uint256 deadline_,
uint256 ownerPrivateKey_
@@ -56,7 +57,7 @@ abstract contract PositionManagerERC20PoolHelperContract is ERC20HelperContract
keccak256(
abi.encode(
_positionManager.PERMIT_TYPEHASH(),
- receiver_,
+ spender_,
tokenId_,
0,
deadline_
@@ -90,11 +91,11 @@ contract PositionManagerERC20PoolTest is PositionManagerERC20PoolHelperContract
require(tokenId != 0, "tokenId nonce not incremented");
// check position info
- address owner = _positionManager.ownerOf(tokenId);
- uint256 lpTokens = _positionManager.getLPTokens(tokenId, mintPrice);
+ address owner = _positionManager.ownerOf(tokenId);
+ uint256 lps = _positionManager.getLPs(tokenId, mintPrice);
assertEq(owner, testAddress);
- assertEq(lpTokens, 0);
+ assertEq(lps, 0);
// deploy a new factory to simulate creating a pool outside of expected factories
ERC20PoolFactory invalidFactory = new ERC20PoolFactory(_ajna);
@@ -107,10 +108,10 @@ contract PositionManagerERC20PoolTest is PositionManagerERC20PoolHelperContract
/**
* @notice Tests attachment of a created position to an already existing NFT.
- * LP tokens are checked to verify ownership of position.
+ * LPs are checked to verify ownership of position.
* Reverts:
- * Attempts to memorialize when lp tokens aren't allowed to be transfered.
- * Attempts to set position owner when not owner of the LP tokens.
+ * Attempts to memorialize when lps aren't allowed to be transfered.
+ * Attempts to set position owner when not owner of the LPs.
*/
function testMemorializePositions() external {
address testAddress = makeAddr("testAddress");
@@ -124,27 +125,21 @@ contract PositionManagerERC20PoolTest is PositionManagerERC20PoolHelperContract
indexes[1] = 2551;
indexes[2] = 2552;
- _addInitialLiquidity(
- {
- from: testAddress,
- amount: 3_000 * 1e18,
- index: indexes[0]
- }
- );
- _addInitialLiquidity(
- {
- from: testAddress,
- amount: 3_000 * 1e18,
- index: indexes[1]
- }
- );
- _addInitialLiquidity(
- {
- from: testAddress,
- amount: 3_000 * 1e18,
- index: indexes[2]
- }
- );
+ _addInitialLiquidity({
+ from: testAddress,
+ amount: 3_000 * 1e18,
+ index: indexes[0]
+ });
+ _addInitialLiquidity({
+ from: testAddress,
+ amount: 3_000 * 1e18,
+ index: indexes[1]
+ });
+ _addInitialLiquidity({
+ from: testAddress,
+ amount: 3_000 * 1e18,
+ index: indexes[2]
+ });
// mint an NFT to later memorialize existing positions into
uint256 tokenId = _mintNFT(testAddress, testAddress, address(_pool));
@@ -162,24 +157,24 @@ contract PositionManagerERC20PoolTest is PositionManagerERC20PoolHelperContract
_positionManager.memorializePositions(memorializeParams);
// allow position manager to take ownership of the position
- _pool.approveLpOwnership(address(_positionManager), indexes[0], 3_000 * 1e27);
- _pool.approveLpOwnership(address(_positionManager), indexes[1], 3_000 * 1e27);
- _pool.approveLpOwnership(address(_positionManager), indexes[2], 3_000 * 1e27);
+ _pool.approveLpOwnership(address(_positionManager), indexes[0], 3_000 * 1e18);
+ _pool.approveLpOwnership(address(_positionManager), indexes[1], 3_000 * 1e18);
+ _pool.approveLpOwnership(address(_positionManager), indexes[2], 3_000 * 1e18);
// memorialize quote tokens into minted NFT
vm.expectEmit(true, true, true, true);
emit MemorializePosition(testAddress, tokenId);
vm.expectEmit(true, true, true, true);
- emit TransferLPTokens(testAddress, address(_positionManager), indexes, 9_000 * 1e27);
+ emit TransferLPs(testAddress, address(_positionManager), indexes, 9_000 * 1e18);
_positionManager.memorializePositions(memorializeParams);
// check memorialization success
- uint256 positionAtPriceOneLPTokens = _positionManager.getLPTokens(tokenId, indexes[0]);
- assertGt(positionAtPriceOneLPTokens, 0);
+ uint256 positionAtPriceOneLPs = _positionManager.getLPs(tokenId, indexes[0]);
+ assertGt(positionAtPriceOneLPs, 0);
- // check lp tokens at non added to price
- uint256 positionAtWrongPriceLPTokens = _positionManager.getLPTokens(tokenId, 4000000 * 1e18);
- assertEq(positionAtWrongPriceLPTokens, 0);
+ // check lps at non added to price
+ uint256 positionAtWrongPriceLPs = _positionManager.getLPs(tokenId, 4000000 * 1e18);
+ assertEq(positionAtWrongPriceLPs, 0);
assertTrue(_positionManager.isIndexInPosition(tokenId, 2550));
assertTrue(_positionManager.isIndexInPosition(tokenId, 2551));
@@ -198,85 +193,67 @@ contract PositionManagerERC20PoolTest is PositionManagerERC20PoolHelperContract
indexes[1] = 2551;
indexes[2] = 2552;
- _addInitialLiquidity(
- {
- from: testAddress,
- amount: 3_000 * 1e18,
- index: indexes[0]
- }
- );
- _addInitialLiquidity(
- {
- from: testAddress,
- amount: 3_000 * 1e18,
- index: indexes[1]
- }
- );
- _addInitialLiquidity(
- {
- from: testAddress,
- amount: 3_000 * 1e18,
- index: indexes[2]
- }
- );
+ _addInitialLiquidity({
+ from: testAddress,
+ amount: 3_000 * 1e18,
+ index: indexes[0]
+ });
+ _addInitialLiquidity({
+ from: testAddress,
+ amount: 3_000 * 1e18,
+ index: indexes[1]
+ });
+ _addInitialLiquidity({
+ from: testAddress,
+ amount: 3_000 * 1e18,
+ index: indexes[2]
+ });
// mint an NFT to later memorialize existing positions into
uint256 tokenId = _mintNFT(testAddress, testAddress, address(_pool));
// check LPs
- _assertLenderLpBalance(
- {
- lender: testAddress,
- index: indexes[0],
- lpBalance: 3_000 * 1e27,
- depositTime: _startTime
- }
- );
- _assertLenderLpBalance(
- {
- lender: address(_positionManager),
- index: indexes[0],
- lpBalance: 0,
- depositTime: 0
- }
- );
- _assertLenderLpBalance(
- {
- lender: testAddress,
- index: indexes[1],
- lpBalance: 3_000 * 1e27,
- depositTime: _startTime
- }
- );
- _assertLenderLpBalance(
- {
- lender: address(_positionManager),
- index: indexes[1],
- lpBalance: 0,
- depositTime: 0
- }
- );
- _assertLenderLpBalance(
- {
- lender: testAddress,
- index: indexes[2],
- lpBalance: 3_000 * 1e27,
- depositTime: _startTime
- }
- );
- _assertLenderLpBalance(
- {
- lender: address(_positionManager),
- index: indexes[2],
- lpBalance: 0,
- depositTime: 0
- }
- );
+ _assertLenderLpBalance({
+ lender: testAddress,
+ index: indexes[0],
+ lpBalance: 3_000 * 1e18,
+ depositTime: _startTime
+ });
+ _assertLenderLpBalance({
+ lender: address(_positionManager),
+ index: indexes[0],
+ lpBalance: 0,
+ depositTime: 0
+ });
+ _assertLenderLpBalance({
+ lender: testAddress,
+ index: indexes[1],
+ lpBalance: 3_000 * 1e18,
+ depositTime: _startTime
+ });
+ _assertLenderLpBalance({
+ lender: address(_positionManager),
+ index: indexes[1],
+ lpBalance: 0,
+ depositTime: 0
+ });
+ _assertLenderLpBalance({
+ lender: testAddress,
+ index: indexes[2],
+ lpBalance: 3_000 * 1e18,
+ depositTime: _startTime
+ });
+ _assertLenderLpBalance({
+ lender: address(_positionManager),
+ index: indexes[2],
+ lpBalance: 0,
+ depositTime: 0
+ });
// check position manager state
- assertEq(_positionManager.getLPTokens(tokenId, indexes[0]), 0);
- assertEq(_positionManager.getLPTokens(tokenId, indexes[1]), 0);
- assertEq(_positionManager.getLPTokens(tokenId, indexes[2]), 0);
+ assertEq(_positionManager.getLPs(tokenId, indexes[0]), 0);
+ assertEq(_positionManager.getLPs(tokenId, indexes[1]), 0);
+ assertEq(_positionManager.getLPs(tokenId, indexes[2]), 0);
assertFalse(_positionManager.isIndexInPosition(tokenId, indexes[0]));
assertFalse(_positionManager.isIndexInPosition(tokenId, indexes[1]));
assertFalse(_positionManager.isIndexInPosition(tokenId, indexes[2]));
@@ -286,221 +263,179 @@ contract PositionManagerERC20PoolTest is PositionManagerERC20PoolHelperContract
tokenId, indexes
);
// allow position manager to take ownership of the position
- _pool.approveLpOwnership(address(_positionManager), indexes[0], 3_000 * 1e27);
- _pool.approveLpOwnership(address(_positionManager), indexes[1], 3_000 * 1e27);
- _pool.approveLpOwnership(address(_positionManager), indexes[2], 3_000 * 1e27);
+ _pool.approveLpOwnership(address(_positionManager), indexes[0], 3_000 * 1e18);
+ _pool.approveLpOwnership(address(_positionManager), indexes[1], 3_000 * 1e18);
+ _pool.approveLpOwnership(address(_positionManager), indexes[2], 3_000 * 1e18);
// memorialize quote tokens into minted NFT
vm.expectEmit(true, true, true, true);
emit MemorializePosition(testAddress, tokenId);
vm.expectEmit(true, true, true, true);
- emit TransferLPTokens(testAddress, address(_positionManager), indexes, 9_000 * 1e27);
+ emit TransferLPs(testAddress, address(_positionManager), indexes, 9_000 * 1e18);
_positionManager.memorializePositions(memorializeParams);
- _assertLenderLpBalance(
- {
- lender: testAddress,
- index: indexes[0],
- lpBalance: 0,
- depositTime: 0
- }
- );
- _assertLenderLpBalance(
- {
- lender: address(_positionManager),
- index: indexes[0],
- lpBalance: 3_000 * 1e27,
- depositTime: _startTime
- }
- );
- _assertLenderLpBalance(
- {
- lender: testAddress,
- index: indexes[1],
- lpBalance: 0,
- depositTime: 0
- }
- );
- _assertLenderLpBalance(
- {
- lender: address(_positionManager),
- index: indexes[1],
- lpBalance: 3_000 * 1e27,
- depositTime: _startTime
- }
- );
- _assertLenderLpBalance(
- {
- lender: testAddress,
- index: indexes[2],
- lpBalance: 0,
- depositTime: 0
- }
- );
- _assertLenderLpBalance(
- {
- lender: address(_positionManager),
- index: indexes[2],
- lpBalance: 3_000 * 1e27,
- depositTime: _startTime
- }
- );
+ _assertLenderLpBalance({
+ lender: testAddress,
+ index: indexes[0],
+ lpBalance: 0,
+ depositTime: 0
+ });
+ _assertLenderLpBalance({
+ lender: address(_positionManager),
+ index: indexes[0],
+ lpBalance: 3_000 * 1e18,
+ depositTime: _startTime
+ });
+ _assertLenderLpBalance({
+ lender: testAddress,
+ index: indexes[1],
+ lpBalance: 0,
+ depositTime: 0
+ });
+ _assertLenderLpBalance({
+ lender: address(_positionManager),
+ index: indexes[1],
+ lpBalance: 3_000 * 1e18,
+ depositTime: _startTime
+ });
+ _assertLenderLpBalance({
+ lender: testAddress,
+ index: indexes[2],
+ lpBalance: 0,
+ depositTime: 0
+ });
+ _assertLenderLpBalance({
+ lender: address(_positionManager),
+ index: indexes[2],
+ lpBalance: 3_000 * 1e18,
+ depositTime: _startTime
+ });
// check position manager state
- assertEq(_positionManager.getLPTokens(tokenId, indexes[0]), 3_000 * 1e27);
- assertEq(_positionManager.getLPTokens(tokenId, indexes[1]), 3_000 * 1e27);
- assertEq(_positionManager.getLPTokens(tokenId, indexes[2]), 3_000 * 1e27);
+ assertEq(_positionManager.getLPs(tokenId, indexes[0]), 3_000 * 1e18);
+ assertEq(_positionManager.getLPs(tokenId, indexes[1]), 3_000 * 1e18);
+ assertEq(_positionManager.getLPs(tokenId, indexes[2]), 3_000 * 1e18);
assertTrue(_positionManager.isIndexInPosition(tokenId, indexes[0]));
assertTrue(_positionManager.isIndexInPosition(tokenId, indexes[1]));
assertTrue(_positionManager.isIndexInPosition(tokenId, indexes[2]));
// add more liquidity
- _addInitialLiquidity(
- {
- from: testAddress,
- amount: 1_000 * 1e18,
- index: indexes[0]
- }
- );
- _addInitialLiquidity(
- {
- from: testAddress,
- amount: 2_000 * 1e18,
- index: indexes[1]
- }
- );
- _addInitialLiquidity(
- {
- from: testAddress,
- amount: 3_000 * 1e18,
- index: indexes[2]
- }
- );
+ _addInitialLiquidity({
+ from: testAddress,
+ amount: 1_000 * 1e18,
+ index: indexes[0]
+ });
+ _addInitialLiquidity({
+ from: testAddress,
+ amount: 2_000 * 1e18,
+ index: indexes[1]
+ });
+ _addInitialLiquidity({
+ from: testAddress,
+ amount: 3_000 * 1e18,
+ index: indexes[2]
+ });
// check LP balance
- _assertLenderLpBalance(
- {
- lender: testAddress,
- index: indexes[0],
- lpBalance: 1_000 * 1e27,
- depositTime: _startTime
- }
- );
- _assertLenderLpBalance(
- {
- lender: address(_positionManager),
- index: indexes[0],
- lpBalance: 3_000 * 1e27,
- depositTime: _startTime
- }
- );
- _assertLenderLpBalance(
- {
- lender: testAddress,
- index: indexes[1],
- lpBalance: 2_000 * 1e27,
- depositTime: _startTime
- }
- );
- _assertLenderLpBalance(
- {
- lender: address(_positionManager),
- index: indexes[1],
- lpBalance: 3_000 * 1e27,
- depositTime: _startTime
- }
- );
- _assertLenderLpBalance(
- {
- lender: testAddress,
- index: indexes[2],
- lpBalance: 3_000 * 1e27,
- depositTime: _startTime
- }
- );
- _assertLenderLpBalance(
- {
- lender: address(_positionManager),
- index: indexes[2],
- lpBalance: 3_000 * 1e27,
- depositTime: _startTime
- }
- );
+ _assertLenderLpBalance({
+ lender: testAddress,
+ index: indexes[0],
+ lpBalance: 1_000 * 1e18,
+ depositTime: _startTime
+ });
+ _assertLenderLpBalance({
+ lender: address(_positionManager),
+ index: indexes[0],
+ lpBalance: 3_000 * 1e18,
+ depositTime: _startTime
+ });
+ _assertLenderLpBalance({
+ lender: testAddress,
+ index: indexes[1],
+ lpBalance: 2_000 * 1e18,
+ depositTime: _startTime
+ });
+ _assertLenderLpBalance({
+ lender: address(_positionManager),
+ index: indexes[1],
+ lpBalance: 3_000 * 1e18,
+ depositTime: _startTime
+ });
+ _assertLenderLpBalance({
+ lender: testAddress,
+ index: indexes[2],
+ lpBalance: 3_000 * 1e18,
+ depositTime: _startTime
+ });
+ _assertLenderLpBalance({
+ lender: address(_positionManager),
+ index: indexes[2],
+ lpBalance: 3_000 * 1e18,
+ depositTime: _startTime
+ });
// check position manager state
- assertEq(_positionManager.getLPTokens(tokenId, indexes[0]), 3_000 * 1e27);
- assertEq(_positionManager.getLPTokens(tokenId, indexes[1]), 3_000 * 1e27);
- assertEq(_positionManager.getLPTokens(tokenId, indexes[2]), 3_000 * 1e27);
+ assertEq(_positionManager.getLPs(tokenId, indexes[0]), 3_000 * 1e18);
+ assertEq(_positionManager.getLPs(tokenId, indexes[1]), 3_000 * 1e18);
+ assertEq(_positionManager.getLPs(tokenId, indexes[2]), 3_000 * 1e18);
assertTrue(_positionManager.isIndexInPosition(tokenId, indexes[0]));
assertTrue(_positionManager.isIndexInPosition(tokenId, indexes[1]));
assertTrue(_positionManager.isIndexInPosition(tokenId, indexes[2]));
// allow position manager to take ownership of the new LPs
- _pool.approveLpOwnership(address(_positionManager), indexes[0], 1_000 * 1e27);
- _pool.approveLpOwnership(address(_positionManager), indexes[1], 2_000 * 1e27);
- _pool.approveLpOwnership(address(_positionManager), indexes[2], 3_000 * 1e27);
+ _pool.approveLpOwnership(address(_positionManager), indexes[0], 1_000 * 1e18);
+ _pool.approveLpOwnership(address(_positionManager), indexes[1], 2_000 * 1e18);
+ _pool.approveLpOwnership(address(_positionManager), indexes[2], 3_000 * 1e18);
// rememorialize quote tokens into minted NFT
vm.expectEmit(true, true, true, true);
emit MemorializePosition(testAddress, tokenId);
vm.expectEmit(true, true, true, true);
- emit TransferLPTokens(testAddress, address(_positionManager), indexes, 6_000 * 1e27);
+ emit TransferLPs(testAddress, address(_positionManager), indexes, 6_000 * 1e18);
_positionManager.memorializePositions(memorializeParams);
// check LP balance
- _assertLenderLpBalance(
- {
- lender: testAddress,
- index: indexes[0],
- lpBalance: 0,
- depositTime: 0
- }
- );
- _assertLenderLpBalance(
- {
- lender: address(_positionManager),
- index: indexes[0],
- lpBalance: 4_000 * 1e27,
- depositTime: _startTime
- }
- );
- _assertLenderLpBalance(
- {
- lender: testAddress,
- index: indexes[1],
- lpBalance: 0,
- depositTime: 0
- }
- );
- _assertLenderLpBalance(
- {
- lender: address(_positionManager),
- index: indexes[1],
- lpBalance: 5_000 * 1e27,
- depositTime: _startTime
- }
- );
- _assertLenderLpBalance(
- {
- lender: testAddress,
- index: indexes[2],
- lpBalance: 0,
- depositTime: 0
- }
- );
- _assertLenderLpBalance(
- {
- lender: address(_positionManager),
- index: indexes[2],
- lpBalance: 6_000 * 1e27,
- depositTime: _startTime
- }
- );
+ _assertLenderLpBalance({
+ lender: testAddress,
+ index: indexes[0],
+ lpBalance: 0,
+ depositTime: 0
+ });
+ _assertLenderLpBalance({
+ lender: address(_positionManager),
+ index: indexes[0],
+ lpBalance: 4_000 * 1e18,
+ depositTime: _startTime
+ });
+ _assertLenderLpBalance({
+ lender: testAddress,
+ index: indexes[1],
+ lpBalance: 0,
+ depositTime: 0
+ });
+ _assertLenderLpBalance({
+ lender: address(_positionManager),
+ index: indexes[1],
+ lpBalance: 5_000 * 1e18,
+ depositTime: _startTime
+ });
+ _assertLenderLpBalance({
+ lender: testAddress,
+ index: indexes[2],
+ lpBalance: 0,
+ depositTime: 0
+ });
+ _assertLenderLpBalance({
+ lender: address(_positionManager),
+ index: indexes[2],
+ lpBalance: 6_000 * 1e18,
+ depositTime: _startTime
+ });
// check position manager state
- assertEq(_positionManager.getLPTokens(tokenId, indexes[0]), 4_000 * 1e27);
- assertEq(_positionManager.getLPTokens(tokenId, indexes[1]), 5_000 * 1e27);
- assertEq(_positionManager.getLPTokens(tokenId, indexes[2]), 6_000 * 1e27);
+ assertEq(_positionManager.getLPs(tokenId, indexes[0]), 4_000 * 1e18);
+ assertEq(_positionManager.getLPs(tokenId, indexes[1]), 5_000 * 1e18);
+ assertEq(_positionManager.getLPs(tokenId, indexes[2]), 6_000 * 1e18);
assertTrue(_positionManager.isIndexInPosition(tokenId, indexes[0]));
assertTrue(_positionManager.isIndexInPosition(tokenId, indexes[1]));
assertTrue(_positionManager.isIndexInPosition(tokenId, indexes[2]));
@@ -525,151 +460,117 @@ contract PositionManagerERC20PoolTest is PositionManagerERC20PoolHelperContract
indexes[2] = 2552;
indexes[3] = 2553;
- _addInitialLiquidity(
- {
- from: testLender1,
- amount: 3_000 * 1e18,
- index: indexes[0]
- }
- );
- _addInitialLiquidity(
- {
- from: testLender1,
- amount: 3_000 * 1e18,
- index: indexes[1]
- }
- );
- _addInitialLiquidity(
- {
- from: testLender1,
- amount: 3_000 * 1e18,
- index: indexes[2]
- }
- );
-
- _addInitialLiquidity(
- {
- from: testLender2,
- amount: 3_000 * 1e18,
- index: indexes[0]
- }
- );
- _addInitialLiquidity(
- {
- from: testLender2,
- amount: 3_000 * 1e18,
- index: indexes[3]
- }
- );
+ _addInitialLiquidity({
+ from: testLender1,
+ amount: 3_000 * 1e18,
+ index: indexes[0]
+ });
+ _addInitialLiquidity({
+ from: testLender1,
+ amount: 3_000 * 1e18,
+ index: indexes[1]
+ });
+ _addInitialLiquidity({
+ from: testLender1,
+ amount: 3_000 * 1e18,
+ index: indexes[2]
+ });
+
+ _addInitialLiquidity({
+ from: testLender2,
+ amount: 3_000 * 1e18,
+ index: indexes[0]
+ });
+ _addInitialLiquidity({
+ from: testLender2,
+ amount: 3_000 * 1e18,
+ index: indexes[3]
+ });
// mint NFTs to later memorialize existing positions into
uint256 tokenId1 = _mintNFT(testLender1, testLender1, address(_pool));
uint256 tokenId2 = _mintNFT(testLender2, testLender2, address(_pool));
// check LPs
- _assertLenderLpBalance(
- {
- lender: testLender1,
- index: indexes[0],
- lpBalance: 3_000 * 1e27,
- depositTime: _startTime
- }
- );
- _assertLenderLpBalance(
- {
- lender: testLender2,
- index: indexes[0],
- lpBalance: 3_000 * 1e27,
- depositTime: _startTime
- }
- );
- _assertLenderLpBalance(
- {
- lender: address(_positionManager),
- index: indexes[0],
- lpBalance: 0,
- depositTime: 0
- }
- );
- _assertLenderLpBalance(
- {
- lender: testLender1,
- index: indexes[1],
- lpBalance: 3_000 * 1e27,
- depositTime: _startTime
- }
- );
- _assertLenderLpBalance(
- {
- lender: testLender2,
- index: indexes[1],
- lpBalance: 0,
- depositTime: 0
- }
- );
- _assertLenderLpBalance(
- {
- lender: address(_positionManager),
- index: indexes[1],
- lpBalance: 0,
- depositTime: 0
- }
- );
- _assertLenderLpBalance(
- {
- lender: testLender1,
- index: indexes[2],
- lpBalance: 3_000 * 1e27,
- depositTime: _startTime
- }
- );
- _assertLenderLpBalance(
- {
- lender: testLender2,
- index: indexes[2],
- lpBalance: 0,
- depositTime: 0
- }
- );
- _assertLenderLpBalance(
- {
- lender: address(_positionManager),
- index: indexes[2],
- lpBalance: 0,
- depositTime: 0
- }
- );
- _assertLenderLpBalance(
- {
- lender: testLender1,
- index: indexes[3],
- lpBalance: 0,
- depositTime: 0
- }
- );
- _assertLenderLpBalance(
- {
- lender: testLender2,
- index: indexes[3],
- lpBalance: 3_000 * 1e27,
- depositTime: _startTime
- }
- );
- _assertLenderLpBalance(
- {
- lender: address(_positionManager),
- index: indexes[3],
- lpBalance: 0,
- depositTime: 0
- }
- );
-
- assertEq(_positionManager.getLPTokens(indexes[0], tokenId1), 0);
- assertEq(_positionManager.getLPTokens(indexes[1], tokenId1), 0);
- assertEq(_positionManager.getLPTokens(indexes[2], tokenId1), 0);
-
- assertEq(_positionManager.getLPTokens(indexes[0], tokenId2), 0);
- assertEq(_positionManager.getLPTokens(indexes[3], tokenId2), 0);
+ _assertLenderLpBalance({
+ lender: testLender1,
+ index: indexes[0],
+ lpBalance: 3_000 * 1e18,
+ depositTime: _startTime
+ });
+ _assertLenderLpBalance({
+ lender: testLender2,
+ index: indexes[0],
+ lpBalance: 3_000 * 1e18,
+ depositTime: _startTime
+ });
+ _assertLenderLpBalance({
+ lender: address(_positionManager),
+ index: indexes[0],
+ lpBalance: 0,
+ depositTime: 0
+ });
+ _assertLenderLpBalance({
+ lender: testLender1,
+ index: indexes[1],
+ lpBalance: 3_000 * 1e18,
+ depositTime: _startTime
+ });
+ _assertLenderLpBalance({
+ lender: testLender2,
+ index: indexes[1],
+ lpBalance: 0,
+ depositTime: 0
+ });
+ _assertLenderLpBalance({
+ lender: address(_positionManager),
+ index: indexes[1],
+ lpBalance: 0,
+ depositTime: 0
+ });
+ _assertLenderLpBalance({
+ lender: testLender1,
+ index: indexes[2],
+ lpBalance: 3_000 * 1e18,
+ depositTime: _startTime
+ });
+ _assertLenderLpBalance({
+ lender: testLender2,
+ index: indexes[2],
+ lpBalance: 0,
+ depositTime: 0
+ });
+ _assertLenderLpBalance({
+ lender: address(_positionManager),
+ index: indexes[2],
+ lpBalance: 0,
+ depositTime: 0
+ });
+ _assertLenderLpBalance({
+ lender: testLender1,
+ index: indexes[3],
+ lpBalance: 0,
+ depositTime: 0
+ });
+ _assertLenderLpBalance({
+ lender: testLender2,
+ index: indexes[3],
+ lpBalance: 3_000 * 1e18,
+ depositTime: _startTime
+ });
+ _assertLenderLpBalance({
+ lender: address(_positionManager),
+ index: indexes[3],
+ lpBalance: 0,
+ depositTime: 0
+ });
+
+ assertEq(_positionManager.getLPs(indexes[0], tokenId1), 0);
+ assertEq(_positionManager.getLPs(indexes[1], tokenId1), 0);
+ assertEq(_positionManager.getLPs(indexes[2], tokenId1), 0);
+
+ assertEq(_positionManager.getLPs(indexes[0], tokenId2), 0);
+ assertEq(_positionManager.getLPs(indexes[3], tokenId2), 0);
(uint256 poolSize, , , , ) = _poolUtils.poolLoansInfo(address(_pool));
assertEq(poolSize, 15_000 * 1e18);
@@ -686,94 +587,78 @@ contract PositionManagerERC20PoolTest is PositionManagerERC20PoolHelperContract
// allow position manager to take ownership of lender 1's position
changePrank(testLender1);
- _pool.approveLpOwnership(address(_positionManager), indexes[0], 3_000 * 1e27);
- _pool.approveLpOwnership(address(_positionManager), indexes[1], 3_000 * 1e27);
- _pool.approveLpOwnership(address(_positionManager), indexes[2], 3_000 * 1e27);
+ _pool.approveLpOwnership(address(_positionManager), indexes[0], 3_000 * 1e18);
+ _pool.approveLpOwnership(address(_positionManager), indexes[1], 3_000 * 1e18);
+ _pool.approveLpOwnership(address(_positionManager), indexes[2], 3_000 * 1e18);
// memorialize lender 1 quote tokens into minted NFT
vm.expectEmit(true, true, true, true);
emit MemorializePosition(testLender1, tokenId1);
vm.expectEmit(true, true, true, true);
- emit TransferLPTokens(testLender1, address(_positionManager), lender1Indexes, 9_000 * 1e27);
+ emit TransferLPs(testLender1, address(_positionManager), lender1Indexes, 9_000 * 1e18);
_positionManager.memorializePositions(memorializeParams);
// check lender, position manager, and pool state
- _assertLenderLpBalance(
- {
- lender: testLender1,
- index: indexes[0],
- lpBalance: 0,
- depositTime: 0
- }
- );
- _assertLenderLpBalance(
- {
- lender: address(_positionManager),
- index: indexes[0],
- lpBalance: 3_000 * 1e27,
- depositTime: _startTime
- }
- );
- _assertLenderLpBalance(
- {
- lender: testLender1,
- index: indexes[1],
- lpBalance: 0,
- depositTime: 0
- }
- );
- _assertLenderLpBalance(
- {
- lender: address(_positionManager),
- index: indexes[1],
- lpBalance: 3_000 * 1e27,
- depositTime: _startTime
- }
- );
- _assertLenderLpBalance(
- {
- lender: testLender1,
- index: indexes[2],
- lpBalance: 0,
- depositTime: 0
- }
- );
- _assertLenderLpBalance(
- {
- lender: address(_positionManager),
- index: indexes[2],
- lpBalance: 3_000 * 1e27,
- depositTime: _startTime
- }
- );
- _assertLenderLpBalance(
- {
- lender: testLender1,
- index: indexes[3],
- lpBalance: 0,
- depositTime: 0
- }
- );
- _assertLenderLpBalance(
- {
- lender: address(_positionManager),
- index: indexes[3],
- lpBalance: 0,
- depositTime: 0
- }
- );
-
- assertEq(_positionManager.getLPTokens(tokenId1, indexes[0]), 3_000 * 1e27);
- assertEq(_positionManager.getLPTokens(tokenId1, indexes[1]), 3_000 * 1e27);
- assertEq(_positionManager.getLPTokens(tokenId1, indexes[2]), 3_000 * 1e27);
+ _assertLenderLpBalance({
+ lender: testLender1,
+ index: indexes[0],
+ lpBalance: 0,
+ depositTime: 0
+ });
+ _assertLenderLpBalance({
+ lender: address(_positionManager),
+ index: indexes[0],
+ lpBalance: 3_000 * 1e18,
+ depositTime: _startTime
+ });
+ _assertLenderLpBalance({
+ lender: testLender1,
+ index: indexes[1],
+ lpBalance: 0,
+ depositTime: 0
+ });
+ _assertLenderLpBalance({
+ lender: address(_positionManager),
+ index: indexes[1],
+ lpBalance: 3_000 * 1e18,
+ depositTime: _startTime
+ });
+ _assertLenderLpBalance({
+ lender: testLender1,
+ index: indexes[2],
+ lpBalance: 0,
+ depositTime: 0
+ });
+ _assertLenderLpBalance({
+ lender: address(_positionManager),
+ index: indexes[2],
+ lpBalance: 3_000 * 1e18,
+ depositTime: _startTime
+ });
+ _assertLenderLpBalance({
+ lender: testLender1,
+ index: indexes[3],
+ lpBalance: 0,
+ depositTime: 0
+ });
+ _assertLenderLpBalance({
+ lender: address(_positionManager),
+ index: indexes[3],
+ lpBalance: 0,
+ depositTime: 0
+ });
+
+ assertEq(_positionManager.getLPs(tokenId1, indexes[0]), 3_000 * 1e18);
+ assertEq(_positionManager.getLPs(tokenId1, indexes[1]), 3_000 * 1e18);
+ assertEq(_positionManager.getLPs(tokenId1, indexes[2]), 3_000 * 1e18);
(poolSize, , , , ) = _poolUtils.poolLoansInfo(address(_pool));
assertEq(poolSize, 15_000 * 1e18);
// allow position manager to take ownership of lender 2's position
changePrank(testLender2);
- _pool.approveLpOwnership(address(_positionManager), indexes[0], 3_000 * 1e27);
- _pool.approveLpOwnership(address(_positionManager), indexes[3], 3_000 * 1e27);
+ _pool.approveLpOwnership(address(_positionManager), indexes[0], 3_000 * 1e18);
+ _pool.approveLpOwnership(address(_positionManager), indexes[3], 3_000 * 1e18);
// memorialize lender 2 quote tokens into minted NFT
uint256[] memory newIndexes = new uint256[](2);
@@ -787,81 +672,65 @@ contract PositionManagerERC20PoolTest is PositionManagerERC20PoolHelperContract
vm.expectEmit(true, true, true, true);
emit MemorializePosition(testLender2, tokenId2);
vm.expectEmit(true, true, true, true);
- emit TransferLPTokens(testLender2, address(_positionManager), newIndexes, 6_000 * 1e27);
+ emit TransferLPs(testLender2, address(_positionManager), newIndexes, 6_000 * 1e18);
_positionManager.memorializePositions(memorializeParams);
// // check lender, position manager, and pool state
- _assertLenderLpBalance(
- {
- lender: testLender2,
- index: indexes[0],
- lpBalance: 0,
- depositTime: 0
- }
- );
- _assertLenderLpBalance(
- {
- lender: address(_positionManager),
- index: indexes[0],
- lpBalance: 6_000 * 1e27,
- depositTime: _startTime
- }
- );
- _assertLenderLpBalance(
- {
- lender: testLender2,
- index: indexes[1],
- lpBalance: 0,
- depositTime: 0
- }
- );
- _assertLenderLpBalance(
- {
- lender: address(_positionManager),
- index: indexes[1],
- lpBalance: 3_000 * 1e27,
- depositTime: _startTime
- }
- );
- _assertLenderLpBalance(
- {
- lender: testLender2,
- index: indexes[2],
- lpBalance: 0,
- depositTime: 0
- }
- );
- _assertLenderLpBalance(
- {
- lender: address(_positionManager),
- index: indexes[2],
- lpBalance: 3_000 * 1e27,
- depositTime: _startTime
- }
- );
- _assertLenderLpBalance(
- {
- lender: testLender2,
- index: indexes[3],
- lpBalance: 0,
- depositTime: 0
- }
- );
- _assertLenderLpBalance(
- {
- lender: address(_positionManager),
- index: indexes[3],
- lpBalance: 3_000 * 1e27,
- depositTime: _startTime
- }
- );
-
- assertEq(_positionManager.getLPTokens(tokenId1, indexes[0]), 3_000 * 1e27);
- assertEq(_positionManager.getLPTokens(tokenId1, indexes[1]), 3_000 * 1e27);
- assertEq(_positionManager.getLPTokens(tokenId1, indexes[2]), 3_000 * 1e27);
-
- assertEq(_positionManager.getLPTokens(tokenId2, indexes[0]), 3_000 * 1e27);
- assertEq(_positionManager.getLPTokens(tokenId2, indexes[3]), 3_000 * 1e27);
+ _assertLenderLpBalance({
+ lender: testLender2,
+ index: indexes[0],
+ lpBalance: 0,
+ depositTime: 0
+ });
+ _assertLenderLpBalance({
+ lender: address(_positionManager),
+ index: indexes[0],
+ lpBalance: 6_000 * 1e18,
+ depositTime: _startTime
+ });
+ _assertLenderLpBalance({
+ lender: testLender2,
+ index: indexes[1],
+ lpBalance: 0,
+ depositTime: 0
+ });
+ _assertLenderLpBalance({
+ lender: address(_positionManager),
+ index: indexes[1],
+ lpBalance: 3_000 * 1e18,
+ depositTime: _startTime
+ });
+ _assertLenderLpBalance({
+ lender: testLender2,
+ index: indexes[2],
+ lpBalance: 0,
+ depositTime: 0
+ });
+ _assertLenderLpBalance({
+ lender: address(_positionManager),
+ index: indexes[2],
+ lpBalance: 3_000 * 1e18,
+ depositTime: _startTime
+ });
+ _assertLenderLpBalance({
+ lender: testLender2,
+ index: indexes[3],
+ lpBalance: 0,
+ depositTime: 0
+ });
+ _assertLenderLpBalance({
+ lender: address(_positionManager),
+ index: indexes[3],
+ lpBalance: 3_000 * 1e18,
+ depositTime: _startTime
+ });
+
+ assertEq(_positionManager.getLPs(tokenId1, indexes[0]), 3_000 * 1e18);
+ assertEq(_positionManager.getLPs(tokenId1, indexes[1]), 3_000 * 1e18);
+ assertEq(_positionManager.getLPs(tokenId1, indexes[2]), 3_000 * 1e18);
+
+ assertEq(_positionManager.getLPs(tokenId2, indexes[0]), 3_000 * 1e18);
+ assertEq(_positionManager.getLPs(tokenId2, indexes[3]), 3_000 * 1e18);
(poolSize, , , , ) = _poolUtils.poolLoansInfo(address(_pool));
assertEq(poolSize, 15_000 * 1e18);
@@ -905,86 +774,73 @@ contract PositionManagerERC20PoolTest is PositionManagerERC20PoolHelperContract
// add initial liquidity
uint256 mintAmount = 50_000 * 1e18;
_mintQuoteAndApproveManagerTokens(testMinter, mintAmount);
- _addInitialLiquidity(
- {
- from: testMinter,
- amount: 15_000 * 1e18,
- index: testIndexPrice
- }
- );
+
+ _addInitialLiquidity({
+ from: testMinter,
+ amount: 15_000 * 1e18,
+ index: testIndexPrice
+ });
uint256 tokenId = _mintNFT(testMinter, testMinter, address(_pool));
// check owner
assertEq(_positionManager.ownerOf(tokenId), testMinter);
// check LPs
- _assertLenderLpBalance(
- {
- lender: testMinter,
- index: testIndexPrice,
- lpBalance: 15_000 * 1e27,
- depositTime: _startTime
- }
- );
- _assertLenderLpBalance(
- {
- lender: testReceiver,
- index: testIndexPrice,
- lpBalance: 0,
- depositTime: 0
- }
- );
- _assertLenderLpBalance(
- {
- lender: address(_positionManager),
- index: testIndexPrice,
- lpBalance: 0,
- depositTime: 0
- }
- );
+ _assertLenderLpBalance({
+ lender: testMinter,
+ index: testIndexPrice,
+ lpBalance: 15_000 * 1e18,
+ depositTime: _startTime
+ });
+ _assertLenderLpBalance({
+ lender: testReceiver,
+ index: testIndexPrice,
+ lpBalance: 0,
+ depositTime: 0
+ });
+ _assertLenderLpBalance({
+ lender: address(_positionManager),
+ index: testIndexPrice,
+ lpBalance: 0,
+ depositTime: 0
+ });
// check position manager state
- assertEq(_positionManager.getLPTokens(tokenId, testIndexPrice), 0);
+ assertEq(_positionManager.getLPs(tokenId, testIndexPrice), 0);
assertFalse(_positionManager.isIndexInPosition(tokenId, testIndexPrice));
// memorialize positions
uint256[] memory indexes = new uint256[](1);
indexes[0] = testIndexPrice;
// allow position manager to take ownership of the position of testMinter
- _pool.approveLpOwnership(address(_positionManager), indexes[0], 15_000 * 1e27);
+ _pool.approveLpOwnership(address(_positionManager), indexes[0], 15_000 * 1e18);
// memorialize positions of testMinter
IPositionManagerOwnerActions.MemorializePositionsParams memory memorializeParams = IPositionManagerOwnerActions.MemorializePositionsParams(
tokenId, indexes
);
_positionManager.memorializePositions(memorializeParams);
- _assertLenderLpBalance(
- {
- lender: testMinter,
- index: testIndexPrice,
- lpBalance: 0,
- depositTime: 0
- }
- );
- _assertLenderLpBalance(
- {
- lender: testReceiver,
- index: testIndexPrice,
- lpBalance: 0,
- depositTime: 0
- }
- );
- _assertLenderLpBalance(
- {
- lender: address(_positionManager),
- index: testIndexPrice,
- lpBalance: 15_000 * 1e27,
- depositTime: _startTime
- }
- );
+ _assertLenderLpBalance({
+ lender: testMinter,
+ index: testIndexPrice,
+ lpBalance: 0,
+ depositTime: 0
+ });
+ _assertLenderLpBalance({
+ lender: testReceiver,
+ index: testIndexPrice,
+ lpBalance: 0,
+ depositTime: 0
+ });
+ _assertLenderLpBalance({
+ lender: address(_positionManager),
+ index: testIndexPrice,
+ lpBalance: 15_000 * 1e18,
+ depositTime: _startTime
+ });
// check position manager state
- assertEq(_positionManager.getLPTokens(tokenId, testIndexPrice), 15_000 * 1e27);
+ assertEq(_positionManager.getLPs(tokenId, testIndexPrice), 15_000 * 1e18);
assertTrue(_positionManager.isIndexInPosition(tokenId, testIndexPrice));
// approve and transfer NFT to different address
@@ -1008,33 +864,27 @@ contract PositionManagerERC20PoolTest is PositionManagerERC20PoolHelperContract
_positionManager.reedemPositions(reedemParams);
// check pool state
- _assertLenderLpBalance(
- {
- lender: testMinter,
- index: testIndexPrice,
- lpBalance: 0,
- depositTime: 0
- }
- );
- _assertLenderLpBalance(
- {
- lender: testReceiver,
- index: testIndexPrice,
- lpBalance: 15_000 * 1e27,
- depositTime: _startTime
- }
- );
- _assertLenderLpBalance(
- {
- lender: address(_positionManager),
- index: testIndexPrice,
- lpBalance: 0,
- depositTime: 0
- }
- );
+ _assertLenderLpBalance({
+ lender: testMinter,
+ index: testIndexPrice,
+ lpBalance: 0,
+ depositTime: 0
+ });
+ _assertLenderLpBalance({
+ lender: testReceiver,
+ index: testIndexPrice,
+ lpBalance: 15_000 * 1e18,
+ depositTime: _startTime
+ });
+ _assertLenderLpBalance({
+ lender: address(_positionManager),
+ index: testIndexPrice,
+ lpBalance: 0,
+ depositTime: 0
+ });
// check position manager state
- assertEq(_positionManager.getLPTokens(tokenId, testIndexPrice), 0);
+ assertEq(_positionManager.getLPs(tokenId, testIndexPrice), 0);
assertFalse(_positionManager.isIndexInPosition(tokenId, testIndexPrice));
}
@@ -1053,53 +903,46 @@ contract PositionManagerERC20PoolTest is PositionManagerERC20PoolHelperContract
// add initial liquidity
uint256 mintAmount = 50_000 * 1e18;
_mintQuoteAndApproveManagerTokens(testMinter, mintAmount);
- _addInitialLiquidity(
- {
- from: testMinter,
- amount: 15_000 * 1e18,
- index: testIndexPrice
- }
- );
+
+ _addInitialLiquidity({
+ from: testMinter,
+ amount: 15_000 * 1e18,
+ index: testIndexPrice
+ });
uint256 tokenId = _mintNFT(testMinter, testMinter, address(_pool));
// check owner
assertEq(_positionManager.ownerOf(tokenId), testMinter);
// check LPs
- _assertLenderLpBalance(
- {
- lender: testMinter,
- index: testIndexPrice,
- lpBalance: 15_000 * 1e27,
- depositTime: _startTime
- }
- );
- _assertLenderLpBalance(
- {
- lender: testReceiver,
- index: testIndexPrice,
- lpBalance: 0,
- depositTime: 0
- }
- );
- _assertLenderLpBalance(
- {
- lender: address(_positionManager),
- index: testIndexPrice,
- lpBalance: 0,
- depositTime: 0
- }
- );
+ _assertLenderLpBalance({
+ lender: testMinter,
+ index: testIndexPrice,
+ lpBalance: 15_000 * 1e18,
+ depositTime: _startTime
+ });
+ _assertLenderLpBalance({
+ lender: testReceiver,
+ index: testIndexPrice,
+ lpBalance: 0,
+ depositTime: 0
+ });
+ _assertLenderLpBalance({
+ lender: address(_positionManager),
+ index: testIndexPrice,
+ lpBalance: 0,
+ depositTime: 0
+ });
// check position manager state
- assertEq(_positionManager.getLPTokens(tokenId, testIndexPrice), 0);
+ assertEq(_positionManager.getLPs(tokenId, testIndexPrice), 0);
assertFalse(_positionManager.isIndexInPosition(tokenId, testIndexPrice));
// memorialize positions
uint256[] memory indexes = new uint256[](1);
indexes[0] = testIndexPrice;
// allow position manager to take ownership of the position of testMinter
- _pool.approveLpOwnership(address(_positionManager), indexes[0], 15_000 * 1e27);
+ _pool.approveLpOwnership(address(_positionManager), indexes[0], 15_000 * 1e18);
// memorialize positions of testMinter
IPositionManagerOwnerActions.MemorializePositionsParams memory memorializeParams = IPositionManagerOwnerActions.MemorializePositionsParams(
tokenId, indexes
@@ -1107,35 +950,31 @@ contract PositionManagerERC20PoolTest is PositionManagerERC20PoolHelperContract
_positionManager.memorializePositions(memorializeParams);
// check pool state
- _assertLenderLpBalance(
- {
- lender: testMinter,
- index: testIndexPrice,
- lpBalance: 0,
- depositTime: 0
- }
- );
- _assertLenderLpBalance(
- {
- lender: testReceiver,
- index: testIndexPrice,
- lpBalance: 0,
- depositTime: 0
- }
- );
- _assertLenderLpBalance(
- {
- lender: address(_positionManager),
- index: testIndexPrice,
- lpBalance: 15_000 * 1e27,
- depositTime: _startTime
- }
- );
+ _assertLenderLpBalance({
+ lender: testMinter,
+ index: testIndexPrice,
+ lpBalance: 0,
+ depositTime: 0
+ });
+ _assertLenderLpBalance({
+ lender: testReceiver,
+ index: testIndexPrice,
+ lpBalance: 0,
+ depositTime: 0
+ });
+ _assertLenderLpBalance({
+ lender: address(_positionManager),
+ index: testIndexPrice,
+ lpBalance: 15_000 * 1e18,
+ depositTime: _startTime
+ });
// check position manager state
- assertEq(_positionManager.getLPTokens(tokenId, testIndexPrice), 15_000 * 1e27);
+ assertEq(_positionManager.getLPs(tokenId, testIndexPrice), 15_000 * 1e18);
assertTrue(_positionManager.isIndexInPosition(tokenId, testIndexPrice));
+ address testSpender = makeAddr("testSpender");
+
// approve and transfer NFT by permit to different address
{
uint256 deadline = block.timestamp + 1 days;
@@ -1148,7 +987,7 @@ contract PositionManagerERC20PoolTest is PositionManagerERC20PoolHelperContract
keccak256(
abi.encode(
_positionManager.PERMIT_TYPEHASH(),
- testReceiver,
+ testSpender,
tokenId,
0,
deadline
@@ -1157,7 +996,8 @@ contract PositionManagerERC20PoolTest is PositionManagerERC20PoolHelperContract
)
)
);
- _positionManager.safeTransferFromWithPermit(testMinter, testReceiver, testReceiver, tokenId, deadline, v, r, s );
+ changePrank(testSpender);
+ _positionManager.safeTransferFromWithPermit(testMinter, testReceiver, tokenId, deadline, v, r, s );
}
// check owner
@@ -1177,37 +1017,33 @@ contract PositionManagerERC20PoolTest is PositionManagerERC20PoolHelperContract
_positionManager.reedemPositions(reedemParams);
// check pool state
- _assertLenderLpBalance(
- {
- lender: testMinter,
- index: testIndexPrice,
- lpBalance: 0,
- depositTime: 0
- }
- );
- _assertLenderLpBalance(
- {
- lender: testReceiver,
- index: testIndexPrice,
- lpBalance: 15_000 * 1e27,
- depositTime: _startTime
- }
- );
- _assertLenderLpBalance(
- {
- lender: address(_positionManager),
- index: testIndexPrice,
- lpBalance: 0,
- depositTime: 0
- }
- );
+ _assertLenderLpBalance({
+ lender: testMinter,
+ index: testIndexPrice,
+ lpBalance: 0,
+ depositTime: 0
+ });
+ _assertLenderLpBalance({
+ lender: testReceiver,
+ index: testIndexPrice,
+ lpBalance: 15_000 * 1e18,
+ depositTime: _startTime
+ });
+ _assertLenderLpBalance({
+ lender: address(_positionManager),
+ index: testIndexPrice,
+ lpBalance: 0,
+ depositTime: 0
+ });
// check position manager state
- assertEq(_positionManager.getLPTokens(tokenId, testIndexPrice), 0);
+ assertEq(_positionManager.getLPs(tokenId, testIndexPrice), 0);
assertFalse(_positionManager.isIndexInPosition(tokenId, testIndexPrice));
}
function testPermitByContract() external {
+ address testSpender = makeAddr("spender");
+
// deploy recipient contract
(address nonMintingContractOwner, uint256 nonMintingContractPrivateKey) = makeAddrAndKey("nonMintingContract");
ContractNFTRecipient recipientContract = new ContractNFTRecipient(nonMintingContractOwner);
@@ -1217,50 +1053,58 @@ contract PositionManagerERC20PoolTest is PositionManagerERC20PoolHelperContract
ContractNFTRecipient ownerContract = new ContractNFTRecipient(testContractOwner);
uint256 tokenId = _mintNFT(address(ownerContract), address(ownerContract), address(_pool));
+ changePrank(testSpender);
+
// check contract owned nft can't be signed by non owner
uint256 deadline = block.timestamp + 1 days;
- (uint8 v, bytes32 r, bytes32 s) = _getPermitSig(address(recipientContract), tokenId, deadline, nonMintingContractPrivateKey);
+ (uint8 v, bytes32 r, bytes32 s) = _getPermitSig(testSpender, tokenId, deadline, nonMintingContractPrivateKey);
vm.expectRevert("ajna/nft-unauthorized");
- _positionManager.safeTransferFromWithPermit(address(ownerContract), address(recipientContract), address(recipientContract), tokenId, deadline, v, r, s );
+ _positionManager.safeTransferFromWithPermit(address(ownerContract), address(recipientContract), tokenId, deadline, v, r, s );
// check owner can permit their contract to transfer the NFT
deadline = block.timestamp + 1 days;
- (v, r, s) = _getPermitSig(address(recipientContract), tokenId, deadline, ownerPrivateKey);
- _positionManager.safeTransferFromWithPermit(address(ownerContract), address(recipientContract), address(recipientContract), tokenId, deadline, v, r, s );
+ (v, r, s) = _getPermitSig(testSpender, tokenId, deadline, ownerPrivateKey);
+ _positionManager.safeTransferFromWithPermit(address(ownerContract), address(recipientContract), tokenId, deadline, v, r, s );
}
function testPermitReverts() external {
// generate addresses and set test params
(address testMinter, uint256 minterPrivateKey) = makeAddrAndKey("testMinter");
(address testReceiver, uint256 receiverPrivateKey) = makeAddrAndKey("testReceiver");
+ address testSpender = makeAddr("spender");
vm.prank(testMinter);
uint256 tokenId = _mintNFT(testMinter, testMinter, address(_pool));
assertEq(_positionManager.ownerOf(tokenId), testMinter);
+ changePrank(testSpender);
+
// check can't use a deadline in the past
uint256 deadline = block.timestamp - 1 days;
- (uint8 v, bytes32 r, bytes32 s) = _getPermitSig(testReceiver, tokenId, deadline, minterPrivateKey);
+ (uint8 v, bytes32 r, bytes32 s) = _getPermitSig(testSpender, tokenId, deadline, minterPrivateKey);
vm.expectRevert("ajna/nft-permit-expired");
- _positionManager.safeTransferFromWithPermit(testMinter, testReceiver, testReceiver, tokenId, deadline, v, r, s );
+ _positionManager.safeTransferFromWithPermit(testMinter, testReceiver, tokenId, deadline, v, r, s );
// check can't self approve
+ changePrank(testMinter);
deadline = block.timestamp + 1 days;
- (v, r, s) = _getPermitSig(testMinter, tokenId, deadline, minterPrivateKey);
+ (v, r, s) = _getPermitSig(testSpender, tokenId, deadline, minterPrivateKey);
vm.expectRevert("ERC721Permit: approval to current owner");
- _positionManager.safeTransferFromWithPermit(testMinter, testMinter, testMinter, tokenId, deadline, v, r, s );
+ _positionManager.safeTransferFromWithPermit(testMinter, testMinter, tokenId, deadline, v, r, s );
+
+ changePrank(testSpender);
// check signer is authorized to permit
deadline = block.timestamp + 1 days;
- (v, r, s) = _getPermitSig(testReceiver, tokenId, deadline, receiverPrivateKey);
+ (v, r, s) = _getPermitSig(testSpender, tokenId, deadline, receiverPrivateKey);
vm.expectRevert("ajna/nft-unauthorized");
- _positionManager.safeTransferFromWithPermit(testMinter, testReceiver, testReceiver, tokenId, deadline, v, r, s );
+ _positionManager.safeTransferFromWithPermit(testMinter, testReceiver, tokenId, deadline, v, r, s );
// check signature is valid
deadline = block.timestamp + 1 days;
- (v, r, s) = _getPermitSig(testReceiver, tokenId, deadline, minterPrivateKey);
+ (v, r, s) = _getPermitSig(testSpender, tokenId, deadline, minterPrivateKey);
vm.expectRevert("ajna/nft-invalid-signature");
- _positionManager.safeTransferFromWithPermit(testMinter, testReceiver, testReceiver, tokenId, deadline, 0, r, s );
+ _positionManager.safeTransferFromWithPermit(testMinter, testReceiver, tokenId, deadline, 0, r, s );
}
/**
@@ -1299,13 +1143,12 @@ contract PositionManagerERC20PoolTest is PositionManagerERC20PoolHelperContract
// add initial liquidity
uint256 mintAmount = 50_000 * 1e18;
_mintQuoteAndApproveManagerTokens(testMinter, mintAmount);
- _addInitialLiquidity(
- {
- from: testMinter,
- amount: 15_000 * 1e18,
- index: testIndexPrice
- }
- );
+
+ _addInitialLiquidity({
+ from: testMinter,
+ amount: 15_000 * 1e18,
+ index: testIndexPrice
+ });
uint256 tokenId = _mintNFT(testMinter, testMinter, address(_pool));
@@ -1316,7 +1159,7 @@ contract PositionManagerERC20PoolTest is PositionManagerERC20PoolHelperContract
uint256[] memory indexes = new uint256[](1);
indexes[0] = testIndexPrice;
// allow position manager to take ownership of the position of testMinter
- _pool.approveLpOwnership(address(_positionManager), indexes[0], 15_000 * 1e27);
+ _pool.approveLpOwnership(address(_positionManager), indexes[0], 15_000 * 1e18);
// memorialize positions of testMinter
IPositionManagerOwnerActions.MemorializePositionsParams memory memorializeParams = IPositionManagerOwnerActions.MemorializePositionsParams(
tokenId, indexes
@@ -1353,20 +1196,18 @@ contract PositionManagerERC20PoolTest is PositionManagerERC20PoolHelperContract
address notOwner = makeAddr("notOwner");
_mintQuoteAndApproveManagerTokens(testAddress, 10_000 * 1e18);
- _addInitialLiquidity(
- {
- from: testAddress,
- amount: 10_000 * 1e18,
- index: 2550
- }
- );
+ _addInitialLiquidity({
+ from: testAddress,
+ amount: 10_000 * 1e18,
+ index: 2550
+ });
// mint position NFT
uint256 tokenId = _mintNFT(testAddress, testAddress, address(_pool));
// construct move liquidity params
IPositionManagerOwnerActions.MoveLiquidityParams memory moveLiquidityParams = IPositionManagerOwnerActions.MoveLiquidityParams(
- tokenId, address(_pool), 2550, 2551
+ tokenId, address(_pool), 2550, 2551, block.timestamp + 30
);
// move liquidity should fail because is not performed by owner
@@ -1386,20 +1227,16 @@ contract PositionManagerERC20PoolTest is PositionManagerERC20PoolHelperContract
_mintQuoteAndApproveManagerTokens(testAddress2, 10_000 * 1e18);
_mintCollateralAndApproveTokens(testAddress3, 10_000 * 1e18);
- _addInitialLiquidity(
- {
- from: testAddress1,
- amount: 2_500 * 1e18,
- index: mintIndex
- }
- );
- _addInitialLiquidity(
- {
- from: testAddress2,
- amount: 5_500 * 1e18,
- index: mintIndex
- }
- );
+ _addInitialLiquidity({
+ from: testAddress1,
+ amount: 2_500 * 1e18,
+ index: mintIndex
+ });
+ _addInitialLiquidity({
+ from: testAddress2,
+ amount: 5_500 * 1e18,
+ index: mintIndex
+ });
uint256 tokenId1 = _mintNFT(testAddress1, testAddress1, address(_pool));
uint256 tokenId2 = _mintNFT(testAddress2, testAddress2, address(_pool));
@@ -1407,60 +1244,48 @@ contract PositionManagerERC20PoolTest is PositionManagerERC20PoolHelperContract
assertEq(_positionManager.ownerOf(tokenId2), testAddress2);
// check pool state
- _assertLenderLpBalance(
- {
- lender: testAddress1,
- index: mintIndex,
- lpBalance: 2_500 * 1e27,
- depositTime: _startTime
- }
- );
- _assertLenderLpBalance(
- {
- lender: testAddress2,
- index: mintIndex,
- lpBalance: 5_500 * 1e27,
- depositTime: _startTime
- }
- );
- _assertLenderLpBalance(
- {
- lender: address(_positionManager),
- index: mintIndex,
- lpBalance: 0,
- depositTime: 0
- }
- );
- _assertLenderLpBalance(
- {
- lender: testAddress1,
- index: moveIndex,
- lpBalance: 0,
- depositTime: 0
- }
- );
- _assertLenderLpBalance(
- {
- lender: testAddress2,
- index: moveIndex,
- lpBalance: 0 * 1e27,
- depositTime: 0
- }
- );
- _assertLenderLpBalance(
- {
- lender: address(_positionManager),
- index: moveIndex,
- lpBalance: 0,
- depositTime: 0
- }
- );
+ _assertLenderLpBalance({
+ lender: testAddress1,
+ index: mintIndex,
+ lpBalance: 2_500 * 1e18,
+ depositTime: _startTime
+ });
+ _assertLenderLpBalance({
+ lender: testAddress2,
+ index: mintIndex,
+ lpBalance: 5_500 * 1e18,
+ depositTime: _startTime
+ });
+ _assertLenderLpBalance({
+ lender: address(_positionManager),
+ index: mintIndex,
+ lpBalance: 0,
+ depositTime: 0
+ });
+ _assertLenderLpBalance({
+ lender: testAddress1,
+ index: moveIndex,
+ lpBalance: 0,
+ depositTime: 0
+ });
+ _assertLenderLpBalance({
+ lender: testAddress2,
+ index: moveIndex,
+ lpBalance: 0 * 1e18,
+ depositTime: 0
+ });
+ _assertLenderLpBalance({
+ lender: address(_positionManager),
+ index: moveIndex,
+ lpBalance: 0,
+ depositTime: 0
+ });
// check position manager state
- assertEq(_positionManager.getLPTokens(tokenId1, mintIndex), 0);
- assertEq(_positionManager.getLPTokens(tokenId1, moveIndex), 0);
- assertEq(_positionManager.getLPTokens(tokenId2, mintIndex), 0);
- assertEq(_positionManager.getLPTokens(tokenId2, moveIndex), 0);
+ assertEq(_positionManager.getLPs(tokenId1, mintIndex), 0);
+ assertEq(_positionManager.getLPs(tokenId1, moveIndex), 0);
+ assertEq(_positionManager.getLPs(tokenId2, mintIndex), 0);
+ assertEq(_positionManager.getLPs(tokenId2, moveIndex), 0);
assertFalse(_positionManager.isIndexInPosition(tokenId1, mintIndex));
assertFalse(_positionManager.isIndexInPosition(tokenId1, moveIndex));
assertFalse(_positionManager.isIndexInPosition(tokenId2, mintIndex));
@@ -1468,7 +1293,7 @@ contract PositionManagerERC20PoolTest is PositionManagerERC20PoolHelperContract
// allow position manager to take ownership of the position of testAddress1
changePrank(testAddress1);
- _pool.approveLpOwnership(address(_positionManager), mintIndex, 2_500 * 1e27);
+ _pool.approveLpOwnership(address(_positionManager), mintIndex, 2_500 * 1e18);
// memorialize positions of testAddress1
uint256[] memory indexes = new uint256[](1);
@@ -1480,60 +1305,48 @@ contract PositionManagerERC20PoolTest is PositionManagerERC20PoolHelperContract
_positionManager.memorializePositions(memorializeParams);
// check pool state
- _assertLenderLpBalance(
- {
- lender: testAddress1,
- index: mintIndex,
- lpBalance: 0,
- depositTime: 0
- }
- );
- _assertLenderLpBalance(
- {
- lender: testAddress2,
- index: mintIndex,
- lpBalance: 5_500 * 1e27,
- depositTime: _startTime
- }
- );
- _assertLenderLpBalance(
- {
- lender: address(_positionManager),
- index: mintIndex,
- lpBalance: 2_500 * 1e27,
- depositTime: _startTime
- }
- );
- _assertLenderLpBalance(
- {
- lender: testAddress1,
- index: moveIndex,
- lpBalance: 0,
- depositTime: 0
- }
- );
- _assertLenderLpBalance(
- {
- lender: testAddress2,
- index: moveIndex,
- lpBalance: 0,
- depositTime: 0
- }
- );
- _assertLenderLpBalance(
- {
- lender: address(_positionManager),
- index: moveIndex,
- lpBalance: 0,
- depositTime: 0
- }
- );
+ _assertLenderLpBalance({
+ lender: testAddress1,
+ index: mintIndex,
+ lpBalance: 0,
+ depositTime: 0
+ });
+ _assertLenderLpBalance({
+ lender: testAddress2,
+ index: mintIndex,
+ lpBalance: 5_500 * 1e18,
+ depositTime: _startTime
+ });
+ _assertLenderLpBalance({
+ lender: address(_positionManager),
+ index: mintIndex,
+ lpBalance: 2_500 * 1e18,
+ depositTime: _startTime
+ });
+ _assertLenderLpBalance({
+ lender: testAddress1,
+ index: moveIndex,
+ lpBalance: 0,
+ depositTime: 0
+ });
+ _assertLenderLpBalance({
+ lender: testAddress2,
+ index: moveIndex,
+ lpBalance: 0,
+ depositTime: 0
+ });
+ _assertLenderLpBalance({
+ lender: address(_positionManager),
+ index: moveIndex,
+ lpBalance: 0,
+ depositTime: 0
+ });
// check position manager state
- assertEq(_positionManager.getLPTokens(tokenId1, mintIndex), 2_500 * 1e27);
- assertEq(_positionManager.getLPTokens(tokenId1, moveIndex), 0);
- assertEq(_positionManager.getLPTokens(tokenId2, mintIndex), 0);
- assertEq(_positionManager.getLPTokens(tokenId2, moveIndex), 0);
+ assertEq(_positionManager.getLPs(tokenId1, mintIndex), 2_500 * 1e18);
+ assertEq(_positionManager.getLPs(tokenId1, moveIndex), 0);
+ assertEq(_positionManager.getLPs(tokenId2, mintIndex), 0);
+ assertEq(_positionManager.getLPs(tokenId2, moveIndex), 0);
assertTrue(_positionManager.isIndexInPosition(tokenId1, mintIndex));
assertFalse(_positionManager.isIndexInPosition(tokenId1, moveIndex));
assertFalse(_positionManager.isIndexInPosition(tokenId2, mintIndex));
@@ -1541,7 +1354,7 @@ contract PositionManagerERC20PoolTest is PositionManagerERC20PoolHelperContract
// construct move liquidity params
IPositionManagerOwnerActions.MoveLiquidityParams memory moveLiquidityParams = IPositionManagerOwnerActions.MoveLiquidityParams(
- tokenId1, address(_pool), mintIndex, moveIndex
+ tokenId1, address(_pool), mintIndex, moveIndex, block.timestamp + 30
);
// move liquidity called by testAddress1 owner
@@ -1551,60 +1364,48 @@ contract PositionManagerERC20PoolTest is PositionManagerERC20PoolHelperContract
_positionManager.moveLiquidity(moveLiquidityParams);
// check pool state
- _assertLenderLpBalance(
- {
- lender: testAddress1,
- index: mintIndex,
- lpBalance: 0,
- depositTime: 0
- }
- );
- _assertLenderLpBalance(
- {
- lender: testAddress2,
- index: mintIndex,
- lpBalance: 5_500 * 1e27,
- depositTime: _startTime
- }
- );
- _assertLenderLpBalance(
- {
- lender: address(_positionManager),
- index: mintIndex,
- lpBalance: 0,
- depositTime: _startTime
- }
- );
- _assertLenderLpBalance(
- {
- lender: testAddress1,
- index: moveIndex,
- lpBalance: 0,
- depositTime: 0
- }
- );
- _assertLenderLpBalance(
- {
- lender: testAddress2,
- index: moveIndex,
- lpBalance: 0,
- depositTime: 0
- }
- );
- _assertLenderLpBalance(
- {
- lender: address(_positionManager),
- index: moveIndex,
- lpBalance: 2_500 * 1e27,
- depositTime: _startTime
- }
- );
+ _assertLenderLpBalance({
+ lender: testAddress1,
+ index: mintIndex,
+ lpBalance: 0,
+ depositTime: 0
+ });
+ _assertLenderLpBalance({
+ lender: testAddress2,
+ index: mintIndex,
+ lpBalance: 5_500 * 1e18,
+ depositTime: _startTime
+ });
+ _assertLenderLpBalance({
+ lender: address(_positionManager),
+ index: mintIndex,
+ lpBalance: 0,
+ depositTime: _startTime
+ });
+ _assertLenderLpBalance({
+ lender: testAddress1,
+ index: moveIndex,
+ lpBalance: 0,
+ depositTime: 0
+ });
+ _assertLenderLpBalance({
+ lender: testAddress2,
+ index: moveIndex,
+ lpBalance: 0,
+ depositTime: 0
+ });
+ _assertLenderLpBalance({
+ lender: address(_positionManager),
+ index: moveIndex,
+ lpBalance: 2_500 * 1e18,
+ depositTime: _startTime
+ });
// check position manager state
- assertEq(_positionManager.getLPTokens(tokenId1, mintIndex), 0);
- assertEq(_positionManager.getLPTokens(tokenId1, moveIndex), 2_500 * 1e27);
- assertEq(_positionManager.getLPTokens(tokenId2, mintIndex), 0);
- assertEq(_positionManager.getLPTokens(tokenId2, moveIndex), 0);
+ assertEq(_positionManager.getLPs(tokenId1, mintIndex), 0);
+ assertEq(_positionManager.getLPs(tokenId1, moveIndex), 2_500 * 1e18);
+ assertEq(_positionManager.getLPs(tokenId2, mintIndex), 0);
+ assertEq(_positionManager.getLPs(tokenId2, moveIndex), 0);
assertFalse(_positionManager.isIndexInPosition(tokenId1, mintIndex));
assertTrue(_positionManager.isIndexInPosition(tokenId1, moveIndex));
assertFalse(_positionManager.isIndexInPosition(tokenId2, mintIndex));
@@ -1612,7 +1413,7 @@ contract PositionManagerERC20PoolTest is PositionManagerERC20PoolHelperContract
// allow position manager to take ownership of the position of testAddress2
changePrank(testAddress2);
- _pool.approveLpOwnership(address(_positionManager), mintIndex, 5_500 * 1e27);
+ _pool.approveLpOwnership(address(_positionManager), mintIndex, 5_500 * 1e18);
// memorialize positions of testAddress2
memorializeParams = IPositionManagerOwnerActions.MemorializePositionsParams(
@@ -1622,60 +1423,48 @@ contract PositionManagerERC20PoolTest is PositionManagerERC20PoolHelperContract
_positionManager.memorializePositions(memorializeParams);
// check pool state
- _assertLenderLpBalance(
- {
- lender: testAddress1,
- index: mintIndex,
- lpBalance: 0,
- depositTime: 0
- }
- );
- _assertLenderLpBalance(
- {
- lender: testAddress2,
- index: mintIndex,
- lpBalance: 0,
- depositTime: 0
- }
- );
- _assertLenderLpBalance(
- {
- lender: address(_positionManager),
- index: mintIndex,
- lpBalance: 5_500 * 1e27,
- depositTime: _startTime
- }
- );
- _assertLenderLpBalance(
- {
- lender: testAddress1,
- index: moveIndex,
- lpBalance: 0,
- depositTime: 0
- }
- );
- _assertLenderLpBalance(
- {
- lender: testAddress2,
- index: moveIndex,
- lpBalance: 0,
- depositTime: 0
- }
- );
- _assertLenderLpBalance(
- {
- lender: address(_positionManager),
- index: moveIndex,
- lpBalance: 2_500 * 1e27,
- depositTime: _startTime
- }
- );
+ _assertLenderLpBalance({
+ lender: testAddress1,
+ index: mintIndex,
+ lpBalance: 0,
+ depositTime: 0
+ });
+ _assertLenderLpBalance({
+ lender: testAddress2,
+ index: mintIndex,
+ lpBalance: 0,
+ depositTime: 0
+ });
+ _assertLenderLpBalance({
+ lender: address(_positionManager),
+ index: mintIndex,
+ lpBalance: 5_500 * 1e18,
+ depositTime: _startTime
+ });
+ _assertLenderLpBalance({
+ lender: testAddress1,
+ index: moveIndex,
+ lpBalance: 0,
+ depositTime: 0
+ });
+ _assertLenderLpBalance({
+ lender: testAddress2,
+ index: moveIndex,
+ lpBalance: 0,
+ depositTime: 0
+ });
+ _assertLenderLpBalance({
+ lender: address(_positionManager),
+ index: moveIndex,
+ lpBalance: 2_500 * 1e18,
+ depositTime: _startTime
+ });
// check position manager state
- assertEq(_positionManager.getLPTokens(tokenId1, mintIndex), 0);
- assertEq(_positionManager.getLPTokens(tokenId1, moveIndex), 2_500 * 1e27);
- assertEq(_positionManager.getLPTokens(tokenId2, mintIndex), 5_500 * 1e27);
- assertEq(_positionManager.getLPTokens(tokenId2, moveIndex), 0);
+ assertEq(_positionManager.getLPs(tokenId1, mintIndex), 0);
+ assertEq(_positionManager.getLPs(tokenId1, moveIndex), 2_500 * 1e18);
+ assertEq(_positionManager.getLPs(tokenId2, mintIndex), 5_500 * 1e18);
+ assertEq(_positionManager.getLPs(tokenId2, moveIndex), 0);
assertFalse(_positionManager.isIndexInPosition(tokenId1, mintIndex));
assertTrue(_positionManager.isIndexInPosition(tokenId1, moveIndex));
assertTrue(_positionManager.isIndexInPosition(tokenId2, mintIndex));
@@ -1683,17 +1472,15 @@ contract PositionManagerERC20PoolTest is PositionManagerERC20PoolHelperContract
// construct move liquidity params
moveLiquidityParams = IPositionManagerOwnerActions.MoveLiquidityParams(
- tokenId2, address(_pool), mintIndex, moveIndex
+ tokenId2, address(_pool), mintIndex, moveIndex, block.timestamp + 30
);
- _addCollateral(
- {
- from: testAddress3,
- amount: 10_000 * 1e18,
- index: mintIndex,
- lpAward: 30_108_920.22197881557845 * 1e27
- }
- );
+ _addCollateral({
+ from: testAddress3,
+ amount: 10_000 * 1e18,
+ index: mintIndex,
+ lpAward: 30_108_920.22197881557845 * 1e18
+ });
// move liquidity called by testAddress2 owner
vm.expectEmit(true, true, true, true);
@@ -1702,60 +1489,48 @@ contract PositionManagerERC20PoolTest is PositionManagerERC20PoolHelperContract
_positionManager.moveLiquidity(moveLiquidityParams);
// check pool state
- _assertLenderLpBalance(
- {
- lender: testAddress1,
- index: mintIndex,
- lpBalance: 0,
- depositTime: 0
- }
- );
- _assertLenderLpBalance(
- {
- lender: testAddress2,
- index: mintIndex,
- lpBalance: 0,
- depositTime: 0
- }
- );
- _assertLenderLpBalance(
- {
- lender: address(_positionManager),
- index: mintIndex,
- lpBalance: 0 * 1e27,
- depositTime: _startTime
- }
- );
- _assertLenderLpBalance(
- {
- lender: testAddress1,
- index: moveIndex,
- lpBalance: 0,
- depositTime: 0
- }
- );
- _assertLenderLpBalance(
- {
- lender: testAddress2,
- index: moveIndex,
- lpBalance: 0,
- depositTime: 0
- }
- );
- _assertLenderLpBalance(
- {
- lender: address(_positionManager),
- index: moveIndex,
- lpBalance: 8_000 * 1e27,
- depositTime: _startTime
- }
- );
+ _assertLenderLpBalance({
+ lender: testAddress1,
+ index: mintIndex,
+ lpBalance: 0,
+ depositTime: 0
+ });
+ _assertLenderLpBalance({
+ lender: testAddress2,
+ index: mintIndex,
+ lpBalance: 0,
+ depositTime: 0
+ });
+ _assertLenderLpBalance({
+ lender: address(_positionManager),
+ index: mintIndex,
+ lpBalance: 0 * 1e18,
+ depositTime: _startTime
+ });
+ _assertLenderLpBalance({
+ lender: testAddress1,
+ index: moveIndex,
+ lpBalance: 0,
+ depositTime: 0
+ });
+ _assertLenderLpBalance({
+ lender: testAddress2,
+ index: moveIndex,
+ lpBalance: 0,
+ depositTime: 0
+ });
+ _assertLenderLpBalance({
+ lender: address(_positionManager),
+ index: moveIndex,
+ lpBalance: 8_000 * 1e18,
+ depositTime: _startTime
+ });
// check position manager state
- assertEq(_positionManager.getLPTokens(tokenId1, mintIndex), 0);
- assertEq(_positionManager.getLPTokens(tokenId1, moveIndex), 2_500 * 1e27);
- assertEq(_positionManager.getLPTokens(tokenId2, mintIndex), 0);
- assertEq(_positionManager.getLPTokens(tokenId2, moveIndex), 5_500 * 1e27);
+ assertEq(_positionManager.getLPs(tokenId1, mintIndex), 0);
+ assertEq(_positionManager.getLPs(tokenId1, moveIndex), 2_500 * 1e18);
+ assertEq(_positionManager.getLPs(tokenId2, mintIndex), 0);
+ assertEq(_positionManager.getLPs(tokenId2, moveIndex), 5_500 * 1e18);
assertFalse(_positionManager.isIndexInPosition(tokenId1, mintIndex));
assertTrue(_positionManager.isIndexInPosition(tokenId1, moveIndex));
assertFalse(_positionManager.isIndexInPosition(tokenId2, mintIndex));
@@ -1763,7 +1538,7 @@ contract PositionManagerERC20PoolTest is PositionManagerERC20PoolHelperContract
// check can't move liquidity from position with no liquidity
moveLiquidityParams = IPositionManagerOwnerActions.MoveLiquidityParams(
- tokenId2, address(_pool), 1000, 2000
+ tokenId2, address(_pool), 1000, 2000, block.timestamp + 30
);
changePrank(address(testAddress2));
vm.expectRevert(IPositionManagerErrors.RemoveLiquidityFailed.selector);
@@ -1778,45 +1553,40 @@ contract PositionManagerERC20PoolTest is PositionManagerERC20PoolHelperContract
// add initial liquidity
uint256 mintAmount = 50_000 * 1e18;
_mintQuoteAndApproveManagerTokens(testMinter, mintAmount);
- _addInitialLiquidity(
- {
- from: testMinter,
- amount: 15_000 * 1e18,
- index: testIndexPrice
- }
- );
+
+ _addInitialLiquidity({
+ from: testMinter,
+ amount: 15_000 * 1e18,
+ index: testIndexPrice
+ });
uint256 tokenId = _mintNFT(testMinter, testMinter, address(_pool));
// check owner
assertEq(_positionManager.ownerOf(tokenId), testMinter);
// check pool state
- _assertLenderLpBalance(
- {
- lender: testMinter,
- index: testIndexPrice,
- lpBalance: 15_000 * 1e27,
- depositTime: _startTime
- }
- );
- _assertLenderLpBalance(
- {
- lender: address(_positionManager),
- index: testIndexPrice,
- lpBalance: 0,
- depositTime: 0
- }
- );
+ _assertLenderLpBalance({
+ lender: testMinter,
+ index: testIndexPrice,
+ lpBalance: 15_000 * 1e18,
+ depositTime: _startTime
+ });
+ _assertLenderLpBalance({
+ lender: address(_positionManager),
+ index: testIndexPrice,
+ lpBalance: 0,
+ depositTime: 0
+ });
// check position manager state
- assertEq(_positionManager.getLPTokens(tokenId, testIndexPrice), 0);
+ assertEq(_positionManager.getLPs(tokenId, testIndexPrice), 0);
assertFalse(_positionManager.isIndexInPosition(tokenId, testIndexPrice));
// memorialize positions
uint256[] memory indexes = new uint256[](1);
indexes[0] = testIndexPrice;
// allow position manager to take ownership of the position of testMinter
- _pool.approveLpOwnership(address(_positionManager), indexes[0], 15_000 * 1e27);
+ _pool.approveLpOwnership(address(_positionManager), indexes[0], 15_000 * 1e18);
// memorialize positions of testMinter
IPositionManagerOwnerActions.MemorializePositionsParams memory memorializeParams = IPositionManagerOwnerActions.MemorializePositionsParams(
tokenId, indexes
@@ -1824,25 +1594,21 @@ contract PositionManagerERC20PoolTest is PositionManagerERC20PoolHelperContract
_positionManager.memorializePositions(memorializeParams);
// check pool state
- _assertLenderLpBalance(
- {
- lender: testMinter,
- index: testIndexPrice,
- lpBalance: 0,
- depositTime: 0
- }
- );
- _assertLenderLpBalance(
- {
- lender: address(_positionManager),
- index: testIndexPrice,
- lpBalance: 15_000 * 1e27,
- depositTime: _startTime
- }
- );
+ _assertLenderLpBalance({
+ lender: testMinter,
+ index: testIndexPrice,
+ lpBalance: 0,
+ depositTime: 0
+ });
+ _assertLenderLpBalance({
+ lender: address(_positionManager),
+ index: testIndexPrice,
+ lpBalance: 15_000 * 1e18,
+ depositTime: _startTime
+ });
// check position manager state
- assertEq(_positionManager.getLPTokens(tokenId, testIndexPrice), 15_000 * 1e27);
+ assertEq(_positionManager.getLPs(tokenId, testIndexPrice), 15_000 * 1e18);
assertTrue(_positionManager.isIndexInPosition(tokenId, testIndexPrice));
// redeem positions of testMinter
@@ -1862,25 +1628,21 @@ contract PositionManagerERC20PoolTest is PositionManagerERC20PoolHelperContract
_positionManager.reedemPositions(reedemParams);
// check pool state
- _assertLenderLpBalance(
- {
- lender: testMinter,
- index: testIndexPrice,
- lpBalance: 15_000 * 1e27,
- depositTime: _startTime
- }
- );
- _assertLenderLpBalance(
- {
- lender: address(_positionManager),
- index: testIndexPrice,
- lpBalance: 0,
- depositTime: 0
- }
- );
+ _assertLenderLpBalance({
+ lender: testMinter,
+ index: testIndexPrice,
+ lpBalance: 15_000 * 1e18,
+ depositTime: _startTime
+ });
+ _assertLenderLpBalance({
+ lender: address(_positionManager),
+ index: testIndexPrice,
+ lpBalance: 0,
+ depositTime: 0
+ });
// check position manager state
- assertEq(_positionManager.getLPTokens(tokenId, testIndexPrice), 0);
+ assertEq(_positionManager.getLPs(tokenId, testIndexPrice), 0);
assertFalse(_positionManager.isIndexInPosition(tokenId, testIndexPrice));
// should fail if trying to redeem one more time
@@ -1916,92 +1678,75 @@ contract PositionManagerERC20PoolTest is PositionManagerERC20PoolHelperContract
uint256 mintAmount = 50_000 * 1e18;
_mintQuoteAndApproveManagerTokens(testMinter, mintAmount);
_mintQuoteAndApproveManagerTokens(testReceiver, mintAmount);
- _addInitialLiquidity(
- {
- from: testReceiver,
- amount: 25_000 * 1e18,
- index: testIndexPrice
- }
- );
- _addInitialLiquidity(
- {
- from: testReceiver,
- amount: 15_000 * 1e18,
- index: 2551
- }
- );
- _addInitialLiquidity(
- {
- from: testMinter,
- amount: 15_000 * 1e18,
- index: testIndexPrice
- }
- );
+ _addInitialLiquidity({
+ from: testReceiver,
+ amount: 25_000 * 1e18,
+ index: testIndexPrice
+ });
+ _addInitialLiquidity({
+ from: testReceiver,
+ amount: 15_000 * 1e18,
+ index: 2551
+ });
+
+ _addInitialLiquidity({
+ from: testMinter,
+ amount: 15_000 * 1e18,
+ index: testIndexPrice
+ });
uint256 tokenId = _mintNFT(testMinter, testMinter, address(_pool));
// check owner
assertEq(_positionManager.ownerOf(tokenId), testMinter);
// check pool state
- _assertLenderLpBalance(
- {
- lender: testMinter,
- index: testIndexPrice,
- lpBalance: 15_000 * 1e27,
- depositTime: _startTime
- }
- );
- _assertLenderLpBalance(
- {
- lender: testReceiver,
- index: testIndexPrice,
- lpBalance: 25_000 * 1e27,
- depositTime: _startTime
- }
- );
- _assertLenderLpBalance(
- {
- lender: address(_positionManager),
- index: testIndexPrice,
- lpBalance: 0,
- depositTime: 0
- }
- );
- _assertLenderLpBalance(
- {
- lender: testMinter,
- index: 2551,
- lpBalance: 0,
- depositTime: 0
- }
- );
- _assertLenderLpBalance(
- {
- lender: testReceiver,
- index: 2551,
- lpBalance: 15_000 * 1e27,
- depositTime: _startTime
- }
- );
- _assertLenderLpBalance(
- {
- lender: address(_positionManager),
- index: 2551,
- lpBalance: 0,
- depositTime: 0
- }
- );
+ _assertLenderLpBalance({
+ lender: testMinter,
+ index: testIndexPrice,
+ lpBalance: 15_000 * 1e18,
+ depositTime: _startTime
+ });
+ _assertLenderLpBalance({
+ lender: testReceiver,
+ index: testIndexPrice,
+ lpBalance: 25_000 * 1e18,
+ depositTime: _startTime
+ });
+ _assertLenderLpBalance({
+ lender: address(_positionManager),
+ index: testIndexPrice,
+ lpBalance: 0,
+ depositTime: 0
+ });
+ _assertLenderLpBalance({
+ lender: testMinter,
+ index: 2551,
+ lpBalance: 0,
+ depositTime: 0
+ });
+ _assertLenderLpBalance({
+ lender: testReceiver,
+ index: 2551,
+ lpBalance: 15_000 * 1e18,
+ depositTime: _startTime
+ });
+ _assertLenderLpBalance({
+ lender: address(_positionManager),
+ index: 2551,
+ lpBalance: 0,
+ depositTime: 0
+ });
// check position manager state
- assertEq(_positionManager.getLPTokens(tokenId, testIndexPrice), 0);
+ assertEq(_positionManager.getLPs(tokenId, testIndexPrice), 0);
assertFalse(_positionManager.isIndexInPosition(tokenId, testIndexPrice));
// memorialize positions
uint256[] memory indexes = new uint256[](1);
indexes[0] = testIndexPrice;
// allow position manager to take ownership of the position of testMinter
- _pool.approveLpOwnership(address(_positionManager), indexes[0], 15_000 * 1e27);
+ _pool.approveLpOwnership(address(_positionManager), indexes[0], 15_000 * 1e18);
// memorialize positions of testMinter
IPositionManagerOwnerActions.MemorializePositionsParams memory memorializeParams = IPositionManagerOwnerActions.MemorializePositionsParams(
tokenId, indexes
@@ -2009,57 +1754,45 @@ contract PositionManagerERC20PoolTest is PositionManagerERC20PoolHelperContract
_positionManager.memorializePositions(memorializeParams);
// check pool state
- _assertLenderLpBalance(
- {
- lender: testMinter,
- index: testIndexPrice,
- lpBalance: 0,
- depositTime: 0
- }
- );
- _assertLenderLpBalance(
- {
- lender: testReceiver,
- index: testIndexPrice,
- lpBalance: 25_000 * 1e27,
- depositTime: _startTime
- }
- );
- _assertLenderLpBalance(
- {
- lender: address(_positionManager),
- index: testIndexPrice,
- lpBalance: 15_000 * 1e27,
- depositTime: _startTime
- }
- );
- _assertLenderLpBalance(
- {
- lender: testMinter,
- index: 2551,
- lpBalance: 0,
- depositTime: 0
- }
- );
- _assertLenderLpBalance(
- {
- lender: testReceiver,
- index: 2551,
- lpBalance: 15_000 * 1e27,
- depositTime: _startTime
- }
- );
- _assertLenderLpBalance(
- {
- lender: address(_positionManager),
- index: 2551,
- lpBalance: 0,
- depositTime: 0
- }
- );
+ _assertLenderLpBalance({
+ lender: testMinter,
+ index: testIndexPrice,
+ lpBalance: 0,
+ depositTime: 0
+ });
+ _assertLenderLpBalance({
+ lender: testReceiver,
+ index: testIndexPrice,
+ lpBalance: 25_000 * 1e18,
+ depositTime: _startTime
+ });
+ _assertLenderLpBalance({
+ lender: address(_positionManager),
+ index: testIndexPrice,
+ lpBalance: 15_000 * 1e18,
+ depositTime: _startTime
+ });
+ _assertLenderLpBalance({
+ lender: testMinter,
+ index: 2551,
+ lpBalance: 0,
+ depositTime: 0
+ });
+ _assertLenderLpBalance({
+ lender: testReceiver,
+ index: 2551,
+ lpBalance: 15_000 * 1e18,
+ depositTime: _startTime
+ });
+ _assertLenderLpBalance({
+ lender: address(_positionManager),
+ index: 2551,
+ lpBalance: 0,
+ depositTime: 0
+ });
// check position manager state
- assertEq(_positionManager.getLPTokens(tokenId, testIndexPrice), 15_000 * 1e27);
+ assertEq(_positionManager.getLPs(tokenId, testIndexPrice), 15_000 * 1e18);
assertTrue(_positionManager.isIndexInPosition(tokenId, testIndexPrice));
// approve and transfer NFT to different address
@@ -2089,62 +1822,50 @@ contract PositionManagerERC20PoolTest is PositionManagerERC20PoolHelperContract
vm.expectEmit(true, true, true, true);
emit RedeemPosition(testReceiver, tokenId);
vm.expectEmit(true, true, true, true);
- emit TransferLPTokens(address(_positionManager), testReceiver, indexes, 15_000 * 1e27);
+ emit TransferLPs(address(_positionManager), testReceiver, indexes, 15_000 * 1e18);
changePrank(testReceiver);
_positionManager.reedemPositions(reedemParams);
// check pool state
- _assertLenderLpBalance(
- {
- lender: testMinter,
- index: testIndexPrice,
- lpBalance: 0,
- depositTime: 0
- }
- );
- _assertLenderLpBalance(
- {
- lender: testReceiver,
- index: testIndexPrice,
- lpBalance: 40_000 * 1e27,
- depositTime: _startTime
- }
- );
- _assertLenderLpBalance(
- {
- lender: address(_positionManager),
- index: testIndexPrice,
- lpBalance: 0,
- depositTime: 0
- }
- );
- _assertLenderLpBalance(
- {
- lender: testMinter,
- index: 2551,
- lpBalance: 0,
- depositTime: 0
- }
- );
- _assertLenderLpBalance(
- {
- lender: testReceiver,
- index: 2551,
- lpBalance: 15_000 * 1e27,
- depositTime: _startTime
- }
- );
- _assertLenderLpBalance(
- {
- lender: address(_positionManager),
- index: 2551,
- lpBalance: 0,
- depositTime: 0
- }
- );
+ _assertLenderLpBalance({
+ lender: testMinter,
+ index: testIndexPrice,
+ lpBalance: 0,
+ depositTime: 0
+ });
+ _assertLenderLpBalance({
+ lender: testReceiver,
+ index: testIndexPrice,
+ lpBalance: 40_000 * 1e18,
+ depositTime: _startTime
+ });
+ _assertLenderLpBalance({
+ lender: address(_positionManager),
+ index: testIndexPrice,
+ lpBalance: 0,
+ depositTime: 0
+ });
+ _assertLenderLpBalance({
+ lender: testMinter,
+ index: 2551,
+ lpBalance: 0,
+ depositTime: 0
+ });
+ _assertLenderLpBalance({
+ lender: testReceiver,
+ index: 2551,
+ lpBalance: 15_000 * 1e18,
+ depositTime: _startTime
+ });
+ _assertLenderLpBalance({
+ lender: address(_positionManager),
+ index: 2551,
+ lpBalance: 0,
+ depositTime: 0
+ });
// check position manager state
- assertEq(_positionManager.getLPTokens(tokenId, testIndexPrice), 0);
+ assertEq(_positionManager.getLPs(tokenId, testIndexPrice), 0);
assertFalse(_positionManager.isIndexInPosition(tokenId, testIndexPrice));
}
@@ -2159,39 +1880,32 @@ contract PositionManagerERC20PoolTest is PositionManagerERC20PoolHelperContract
uint256[] memory indexes = new uint256[](1);
indexes[0] = 2550;
- _addInitialLiquidity(
- {
- from: lender,
- amount: 10_000 * 1e18,
- index: 2550
- }
- );
- _assertLenderLpBalance(
- {
- lender: lender,
- index: 2550,
- lpBalance: 10_000 * 1e27,
- depositTime: _startTime
- }
- );
- _assertLenderLpBalance(
- {
- lender: minter,
- index: 2550,
- lpBalance: 0,
- depositTime: 0
- }
- );
- _assertLenderLpBalance(
- {
- lender: address(_positionManager),
- index: 2550,
- lpBalance: 0,
- depositTime: 0
- }
- );
+ _addInitialLiquidity({
+ from: lender,
+ amount: 10_000 * 1e18,
+ index: 2550
+ });
+ _assertLenderLpBalance({
+ lender: lender,
+ index: 2550,
+ lpBalance: 10_000 * 1e18,
+ depositTime: _startTime
+ });
+ _assertLenderLpBalance({
+ lender: minter,
+ index: 2550,
+ lpBalance: 0,
+ depositTime: 0
+ });
+ _assertLenderLpBalance({
+ lender: address(_positionManager),
+ index: 2550,
+ lpBalance: 0,
+ depositTime: 0
+ });
+
// allow position manager to take ownership of the position
- _pool.approveLpOwnership(address(_positionManager), indexes[0], 10_000 * 1e27);
+ _pool.approveLpOwnership(address(_positionManager), indexes[0], 10_000 * 1e18);
// 3rd party minter mints NFT and memorialize lender positions
uint256 tokenId = _mintNFT(minter, lender, address(_pool));
@@ -2200,34 +1914,29 @@ contract PositionManagerERC20PoolTest is PositionManagerERC20PoolHelperContract
tokenId, indexes
);
_positionManager.memorializePositions(memorializeParams);
- _assertLenderLpBalance(
- {
- lender: lender,
- index: 2550,
- lpBalance: 0,
- depositTime: 0
- }
- );
- _assertLenderLpBalance(
- {
- lender: minter,
- index: 2550,
- lpBalance: 0,
- depositTime: 0
- }
- );
- _assertLenderLpBalance(
- {
- lender: address(_positionManager),
- index: 2550,
- lpBalance: 10_000 * 1e27,
- depositTime: _startTime
- }
- );
+
+ _assertLenderLpBalance({
+ lender: lender,
+ index: 2550,
+ lpBalance: 0,
+ depositTime: 0
+ });
+ _assertLenderLpBalance({
+ lender: minter,
+ index: 2550,
+ lpBalance: 0,
+ depositTime: 0
+ });
+ _assertLenderLpBalance({
+ lender: address(_positionManager),
+ index: 2550,
+ lpBalance: 10_000 * 1e18,
+ depositTime: _startTime
+ });
// minter cannot move liquidity on behalf of lender (is not approved)
IPositionManagerOwnerActions.MoveLiquidityParams memory moveLiquidityParams = IPositionManagerOwnerActions.MoveLiquidityParams(
- tokenId, address(_pool), 2550, 2551
+ tokenId, address(_pool), 2550, 2551, block.timestamp + 30
);
vm.expectRevert(IPositionManagerErrors.NoAuth.selector);
_positionManager.moveLiquidity(moveLiquidityParams);
@@ -2253,30 +1962,25 @@ contract PositionManagerERC20PoolTest is PositionManagerERC20PoolHelperContract
changePrank(minter);
// minter can move liquidity on behalf of lender
_positionManager.moveLiquidity(moveLiquidityParams);
- _assertLenderLpBalance(
- {
- lender: lender,
- index: 2551,
- lpBalance: 0,
- depositTime: 0
- }
- );
- _assertLenderLpBalance(
- {
- lender: minter,
- index: 2551,
- lpBalance: 0,
- depositTime: 0
- }
- );
- _assertLenderLpBalance(
- {
- lender: address(_positionManager),
- index: 2551,
- lpBalance: 10_000 * 1e27,
- depositTime: _startTime
- }
- );
+
+ _assertLenderLpBalance({
+ lender: lender,
+ index: 2551,
+ lpBalance: 0,
+ depositTime: 0
+ });
+ _assertLenderLpBalance({
+ lender: minter,
+ index: 2551,
+ lpBalance: 0,
+ depositTime: 0
+ });
+ _assertLenderLpBalance({
+ lender: address(_positionManager),
+ index: 2551,
+ lpBalance: 10_000 * 1e18,
+ depositTime: _startTime
+ });
// minter can redeem liquidity on behalf of lender
indexes[0] = 2551;
@@ -2284,30 +1988,25 @@ contract PositionManagerERC20PoolTest is PositionManagerERC20PoolHelperContract
tokenId, address(_pool), indexes
);
_positionManager.reedemPositions(reedemParams);
- _assertLenderLpBalance(
- {
- lender: lender,
- index: 2551,
- lpBalance: 10_000 * 1e27,
- depositTime: _startTime
- }
- );
- _assertLenderLpBalance(
- {
- lender: minter,
- index: 2551,
- lpBalance: 0,
- depositTime: 0
- }
- );
- _assertLenderLpBalance(
- {
- lender: address(_positionManager),
- index: 2551,
- lpBalance: 0,
- depositTime: 0
- }
- );
+
+ _assertLenderLpBalance({
+ lender: lender,
+ index: 2551,
+ lpBalance: 10_000 * 1e18,
+ depositTime: _startTime
+ });
+ _assertLenderLpBalance({
+ lender: minter,
+ index: 2551,
+ lpBalance: 0,
+ depositTime: 0
+ });
+ _assertLenderLpBalance({
+ lender: address(_positionManager),
+ index: 2551,
+ lpBalance: 0,
+ depositTime: 0
+ });
// minter can burn NFT on behalf of lender
_positionManager.burn(burnParams);
@@ -2326,31 +2025,26 @@ contract PositionManagerERC20PoolTest is PositionManagerERC20PoolHelperContract
uint256[] memory indexes = new uint256[](1);
indexes[0] = 2550;
- _addInitialLiquidity(
- {
- from: lender,
- amount: 10_000 * 1e18,
- index: 2550
- }
- );
- _assertLenderLpBalance(
- {
- lender: lender,
- index: 2550,
- lpBalance: 10_000 * 1e27,
- depositTime: _startTime
- }
- );
- _assertLenderLpBalance(
- {
- lender: minter,
- index: 2550,
- lpBalance: 0,
- depositTime: 0
- }
- );
+ _addInitialLiquidity({
+ from: lender,
+ amount: 10_000 * 1e18,
+ index: 2550
+ });
+ _assertLenderLpBalance({
+ lender: lender,
+ index: 2550,
+ lpBalance: 10_000 * 1e18,
+ depositTime: _startTime
+ });
+ _assertLenderLpBalance({
+ lender: minter,
+ index: 2550,
+ lpBalance: 0,
+ depositTime: 0
+ });
+
// allow position manager to take ownership of the position
- _pool.approveLpOwnership(address(_positionManager), indexes[0], 10_000 * 1e27);
+ _pool.approveLpOwnership(address(_positionManager), indexes[0], 10_000 * 1e18);
// 3rd party minter mints NFT and memorialize lender positions
changePrank(minter);
@@ -2372,22 +2066,19 @@ contract PositionManagerERC20PoolTest is PositionManagerERC20PoolHelperContract
tokenId, address(_pool), indexes
);
_positionManager.reedemPositions(reedemParams);
- _assertLenderLpBalance(
- {
- lender: lender,
- index: 2550,
- lpBalance: 0,
- depositTime: 0
- }
- );
- _assertLenderLpBalance(
- {
- lender: minter,
- index: 2550,
- lpBalance: 10_000 * 1e27,
- depositTime: _startTime
- }
- );
+
+ _assertLenderLpBalance({
+ lender: lender,
+ index: 2550,
+ lpBalance: 0,
+ depositTime: 0
+ });
+ _assertLenderLpBalance({
+ lender: minter,
+ index: 2550,
+ lpBalance: 10_000 * 1e18,
+ depositTime: _startTime
+ });
}
function testMayInteractReverts() external {
@@ -2434,13 +2125,11 @@ contract PositionManagerERC20PoolTest is PositionManagerERC20PoolHelperContract
uint256[] memory indexes = new uint256[](1);
indexes[0] = 2550;
- _addInitialLiquidity(
- {
- from: testAddress,
- amount: 3_000 * 1e18,
- index: indexes[0]
- }
- );
+ _addInitialLiquidity({
+ from: testAddress,
+ amount: 3_000 * 1e18,
+ index: indexes[0]
+ });
// mint NFT
uint256 tokenId = _mintNFT(testAddress, testAddress, address(_pool));
@@ -2454,7 +2143,7 @@ contract PositionManagerERC20PoolTest is PositionManagerERC20PoolHelperContract
assertEq(tokenName(quoteTokenAddress), "Quote");
// allow position manager to take ownership of the position
- _pool.approveLpOwnership(address(_positionManager), indexes[0], 3_000 * 1e27);
+ _pool.approveLpOwnership(address(_positionManager), indexes[0], 3_000 * 1e18);
// memorialize position
IPositionManagerOwnerActions.MemorializePositionsParams memory memorializeParams = IPositionManagerOwnerActions.MemorializePositionsParams(
@@ -2514,85 +2203,67 @@ contract PositionManagerERC721PoolTest is PositionManagerERC721PoolHelperContrac
indexes[1] = 2551;
indexes[2] = 2552;
- _addInitialLiquidity(
- {
- from: testAddress1,
- amount: 3_000 * 1e18,
- index: indexes[0]
- }
- );
- _addInitialLiquidity(
- {
- from: testAddress1,
- amount: 3_000 * 1e18,
- index: indexes[1]
- }
- );
- _addInitialLiquidity(
- {
- from: testAddress1,
- amount: 3_000 * 1e18,
- index: indexes[2]
- }
- );
+ _addInitialLiquidity({
+ from: testAddress1,
+ amount: 3_000 * 1e18,
+ index: indexes[0]
+ });
+ _addInitialLiquidity({
+ from: testAddress1,
+ amount: 3_000 * 1e18,
+ index: indexes[1]
+ });
+ _addInitialLiquidity({
+ from: testAddress1,
+ amount: 3_000 * 1e18,
+ index: indexes[2]
+ });
// mint an NFT to later memorialize existing positions into
uint256 tokenId = _mintNFT(testAddress1, testAddress1, address(_pool), keccak256("ERC721_NON_SUBSET_HASH"));
// check LPs
- _assertLenderLpBalance(
- {
- lender: testAddress1,
- index: indexes[0],
- lpBalance: 3_000 * 1e27,
- depositTime: currentTime
- }
- );
- _assertLenderLpBalance(
- {
- lender: address(_positionManager),
- index: indexes[0],
- lpBalance: 0,
- depositTime: 0
- }
- );
- _assertLenderLpBalance(
- {
- lender: testAddress1,
- index: indexes[1],
- lpBalance: 3_000 * 1e27,
- depositTime: currentTime
- }
- );
- _assertLenderLpBalance(
- {
- lender: address(_positionManager),
- index: indexes[1],
- lpBalance: 0,
- depositTime: 0
- }
- );
- _assertLenderLpBalance(
- {
- lender: testAddress1,
- index: indexes[2],
- lpBalance: 3_000 * 1e27,
- depositTime: currentTime
- }
- );
- _assertLenderLpBalance(
- {
- lender: address(_positionManager),
- index: indexes[2],
- lpBalance: 0,
- depositTime: 0
- }
- );
+ _assertLenderLpBalance({
+ lender: testAddress1,
+ index: indexes[0],
+ lpBalance: 3_000 * 1e18,
+ depositTime: currentTime
+ });
+ _assertLenderLpBalance({
+ lender: address(_positionManager),
+ index: indexes[0],
+ lpBalance: 0,
+ depositTime: 0
+ });
+ _assertLenderLpBalance({
+ lender: testAddress1,
+ index: indexes[1],
+ lpBalance: 3_000 * 1e18,
+ depositTime: currentTime
+ });
+ _assertLenderLpBalance({
+ lender: address(_positionManager),
+ index: indexes[1],
+ lpBalance: 0,
+ depositTime: 0
+ });
+ _assertLenderLpBalance({
+ lender: testAddress1,
+ index: indexes[2],
+ lpBalance: 3_000 * 1e18,
+ depositTime: currentTime
+ });
+ _assertLenderLpBalance({
+ lender: address(_positionManager),
+ index: indexes[2],
+ lpBalance: 0,
+ depositTime: 0
+ });
// check position manager state
- assertEq(_positionManager.getLPTokens(tokenId, indexes[0]), 0);
- assertEq(_positionManager.getLPTokens(tokenId, indexes[1]), 0);
- assertEq(_positionManager.getLPTokens(tokenId, indexes[2]), 0);
+ assertEq(_positionManager.getLPs(tokenId, indexes[0]), 0);
+ assertEq(_positionManager.getLPs(tokenId, indexes[1]), 0);
+ assertEq(_positionManager.getLPs(tokenId, indexes[2]), 0);
assertFalse(_positionManager.isIndexInPosition(tokenId, indexes[0]));
assertFalse(_positionManager.isIndexInPosition(tokenId, indexes[1]));
assertFalse(_positionManager.isIndexInPosition(tokenId, indexes[2]));
@@ -2602,228 +2273,186 @@ contract PositionManagerERC721PoolTest is PositionManagerERC721PoolHelperContrac
tokenId, indexes
);
// allow position manager to take ownership of the position
- _pool.approveLpOwnership(address(_positionManager), indexes[0], 3_000 * 1e27);
- _pool.approveLpOwnership(address(_positionManager), indexes[1], 3_000 * 1e27);
- _pool.approveLpOwnership(address(_positionManager), indexes[2], 3_000 * 1e27);
+ _pool.approveLpOwnership(address(_positionManager), indexes[0], 3_000 * 1e18);
+ _pool.approveLpOwnership(address(_positionManager), indexes[1], 3_000 * 1e18);
+ _pool.approveLpOwnership(address(_positionManager), indexes[2], 3_000 * 1e18);
// memorialize quote tokens into minted NFT
vm.expectEmit(true, true, true, true);
emit MemorializePosition(testAddress1, tokenId);
vm.expectEmit(true, true, true, true);
- emit TransferLPTokens(testAddress1, address(_positionManager), indexes, 9_000 * 1e27);
+ emit TransferLPs(testAddress1, address(_positionManager), indexes, 9_000 * 1e18);
_positionManager.memorializePositions(memorializeParams);
- _assertLenderLpBalance(
- {
- lender: testAddress1,
- index: indexes[0],
- lpBalance: 0,
- depositTime: 0
- }
- );
- _assertLenderLpBalance(
- {
- lender: address(_positionManager),
- index: indexes[0],
- lpBalance: 3_000 * 1e27,
- depositTime: currentTime
- }
- );
- _assertLenderLpBalance(
- {
- lender: testAddress1,
- index: indexes[1],
- lpBalance: 0,
- depositTime: 0
- }
- );
- _assertLenderLpBalance(
- {
- lender: address(_positionManager),
- index: indexes[1],
- lpBalance: 3_000 * 1e27,
- depositTime: currentTime
- }
- );
- _assertLenderLpBalance(
- {
- lender: testAddress1,
- index: indexes[2],
- lpBalance: 0,
- depositTime: 0
- }
- );
- _assertLenderLpBalance(
- {
- lender: address(_positionManager),
- index: indexes[2],
- lpBalance: 3_000 * 1e27,
- depositTime: currentTime
- }
- );
+ _assertLenderLpBalance({
+ lender: testAddress1,
+ index: indexes[0],
+ lpBalance: 0,
+ depositTime: 0
+ });
+ _assertLenderLpBalance({
+ lender: address(_positionManager),
+ index: indexes[0],
+ lpBalance: 3_000 * 1e18,
+ depositTime: currentTime
+ });
+ _assertLenderLpBalance({
+ lender: testAddress1,
+ index: indexes[1],
+ lpBalance: 0,
+ depositTime: 0
+ });
+ _assertLenderLpBalance({
+ lender: address(_positionManager),
+ index: indexes[1],
+ lpBalance: 3_000 * 1e18,
+ depositTime: currentTime
+ });
+ _assertLenderLpBalance({
+ lender: testAddress1,
+ index: indexes[2],
+ lpBalance: 0,
+ depositTime: 0
+ });
+ _assertLenderLpBalance({
+ lender: address(_positionManager),
+ index: indexes[2],
+ lpBalance: 3_000 * 1e18,
+ depositTime: currentTime
+ });
// check position manager state
- assertEq(_positionManager.getLPTokens(tokenId, indexes[0]), 3_000 * 1e27);
- assertEq(_positionManager.getLPTokens(tokenId, indexes[1]), 3_000 * 1e27);
- assertEq(_positionManager.getLPTokens(tokenId, indexes[2]), 3_000 * 1e27);
+ assertEq(_positionManager.getLPs(tokenId, indexes[0]), 3_000 * 1e18);
+ assertEq(_positionManager.getLPs(tokenId, indexes[1]), 3_000 * 1e18);
+ assertEq(_positionManager.getLPs(tokenId, indexes[2]), 3_000 * 1e18);
assertTrue(_positionManager.isIndexInPosition(tokenId, indexes[0]));
assertTrue(_positionManager.isIndexInPosition(tokenId, indexes[1]));
assertTrue(_positionManager.isIndexInPosition(tokenId, indexes[2]));
// add more liquidity
- _addInitialLiquidity(
- {
- from: testAddress1,
- amount: 1_000 * 1e18,
- index: indexes[0]
- }
- );
- _addInitialLiquidity(
- {
- from: testAddress1,
- amount: 2_000 * 1e18,
- index: indexes[1]
- }
- );
- _addInitialLiquidity(
- {
- from: testAddress1,
- amount: 3_000 * 1e18,
- index: indexes[2]
- }
- );
+ _addInitialLiquidity({
+ from: testAddress1,
+ amount: 1_000 * 1e18,
+ index: indexes[0]
+ });
+ _addInitialLiquidity({
+ from: testAddress1,
+ amount: 2_000 * 1e18,
+ index: indexes[1]
+ });
+ _addInitialLiquidity({
+ from: testAddress1,
+ amount: 3_000 * 1e18,
+ index: indexes[2]
+ });
// check LP balance
- _assertLenderLpBalance(
- {
- lender: testAddress1,
- index: indexes[0],
- lpBalance: 1_000 * 1e27,
- depositTime: currentTime
- }
- );
- _assertLenderLpBalance(
- {
- lender: address(_positionManager),
- index: indexes[0],
- lpBalance: 3_000 * 1e27,
- depositTime: currentTime
- }
- );
- _assertLenderLpBalance(
- {
- lender: testAddress1,
- index: indexes[1],
- lpBalance: 2_000 * 1e27,
- depositTime: currentTime
- }
- );
- _assertLenderLpBalance(
- {
- lender: address(_positionManager),
- index: indexes[1],
- lpBalance: 3_000 * 1e27,
- depositTime: currentTime
- }
- );
- _assertLenderLpBalance(
- {
- lender: testAddress1,
- index: indexes[2],
- lpBalance: 3_000 * 1e27,
- depositTime: currentTime
- }
- );
- _assertLenderLpBalance(
- {
- lender: address(_positionManager),
- index: indexes[2],
- lpBalance: 3_000 * 1e27,
- depositTime: currentTime
- }
- );
+ _assertLenderLpBalance({
+ lender: testAddress1,
+ index: indexes[0],
+ lpBalance: 1_000 * 1e18,
+ depositTime: currentTime
+ });
+ _assertLenderLpBalance({
+ lender: address(_positionManager),
+ index: indexes[0],
+ lpBalance: 3_000 * 1e18,
+ depositTime: currentTime
+ });
+ _assertLenderLpBalance({
+ lender: testAddress1,
+ index: indexes[1],
+ lpBalance: 2_000 * 1e18,
+ depositTime: currentTime
+ });
+ _assertLenderLpBalance({
+ lender: address(_positionManager),
+ index: indexes[1],
+ lpBalance: 3_000 * 1e18,
+ depositTime: currentTime
+ });
+ _assertLenderLpBalance({
+ lender: testAddress1,
+ index: indexes[2],
+ lpBalance: 3_000 * 1e18,
+ depositTime: currentTime
+ });
+ _assertLenderLpBalance({
+ lender: address(_positionManager),
+ index: indexes[2],
+ lpBalance: 3_000 * 1e18,
+ depositTime: currentTime
+ });
// check position manager state
- assertEq(_positionManager.getLPTokens(tokenId, indexes[0]), 3_000 * 1e27);
- assertEq(_positionManager.getLPTokens(tokenId, indexes[1]), 3_000 * 1e27);
- assertEq(_positionManager.getLPTokens(tokenId, indexes[2]), 3_000 * 1e27);
+ assertEq(_positionManager.getLPs(tokenId, indexes[0]), 3_000 * 1e18);
+ assertEq(_positionManager.getLPs(tokenId, indexes[1]), 3_000 * 1e18);
+ assertEq(_positionManager.getLPs(tokenId, indexes[2]), 3_000 * 1e18);
assertTrue(_positionManager.isIndexInPosition(tokenId, indexes[0]));
assertTrue(_positionManager.isIndexInPosition(tokenId, indexes[1]));
assertTrue(_positionManager.isIndexInPosition(tokenId, indexes[2]));
// allow position manager to take ownership of the new LPs
- _pool.approveLpOwnership(address(_positionManager), indexes[0], 1_000 * 1e27);
- _pool.approveLpOwnership(address(_positionManager), indexes[1], 2_000 * 1e27);
- _pool.approveLpOwnership(address(_positionManager), indexes[2], 3_000 * 1e27);
+ _pool.approveLpOwnership(address(_positionManager), indexes[0], 1_000 * 1e18);
+ _pool.approveLpOwnership(address(_positionManager), indexes[1], 2_000 * 1e18);
+ _pool.approveLpOwnership(address(_positionManager), indexes[2], 3_000 * 1e18);
// rememorialize quote tokens into minted NFT
vm.expectEmit(true, true, true, true);
emit MemorializePosition(testAddress1, tokenId);
vm.expectEmit(true, true, true, true);
- emit TransferLPTokens(testAddress1, address(_positionManager), indexes, 6_000 * 1e27);
+ emit TransferLPs(testAddress1, address(_positionManager), indexes, 6_000 * 1e18);
_positionManager.memorializePositions(memorializeParams);
// check LP balance
- _assertLenderLpBalance(
- {
- lender: testAddress1,
- index: indexes[0],
- lpBalance: 0,
- depositTime: 0
- }
- );
- _assertLenderLpBalance(
- {
- lender: address(_positionManager),
- index: indexes[0],
- lpBalance: 4_000 * 1e27,
- depositTime: currentTime
- }
- );
- _assertLenderLpBalance(
- {
- lender: testAddress1,
- index: indexes[1],
- lpBalance: 0,
- depositTime: 0
- }
- );
- _assertLenderLpBalance(
- {
- lender: address(_positionManager),
- index: indexes[1],
- lpBalance: 5_000 * 1e27,
- depositTime: currentTime
- }
- );
- _assertLenderLpBalance(
- {
- lender: testAddress1,
- index: indexes[2],
- lpBalance: 0,
- depositTime: 0
- }
- );
- _assertLenderLpBalance(
- {
- lender: address(_positionManager),
- index: indexes[2],
- lpBalance: 6_000 * 1e27,
- depositTime: currentTime
- }
- );
+ _assertLenderLpBalance({
+ lender: testAddress1,
+ index: indexes[0],
+ lpBalance: 0,
+ depositTime: 0
+ });
+ _assertLenderLpBalance({
+ lender: address(_positionManager),
+ index: indexes[0],
+ lpBalance: 4_000 * 1e18,
+ depositTime: currentTime
+ });
+ _assertLenderLpBalance({
+ lender: testAddress1,
+ index: indexes[1],
+ lpBalance: 0,
+ depositTime: 0
+ });
+ _assertLenderLpBalance({
+ lender: address(_positionManager),
+ index: indexes[1],
+ lpBalance: 5_000 * 1e18,
+ depositTime: currentTime
+ });
+ _assertLenderLpBalance({
+ lender: testAddress1,
+ index: indexes[2],
+ lpBalance: 0,
+ depositTime: 0
+ });
+ _assertLenderLpBalance({
+ lender: address(_positionManager),
+ index: indexes[2],
+ lpBalance: 6_000 * 1e18,
+ depositTime: currentTime
+ });
// check position manager state
- assertEq(_positionManager.getLPTokens(tokenId, indexes[0]), 4_000 * 1e27);
- assertEq(_positionManager.getLPTokens(tokenId, indexes[1]), 5_000 * 1e27);
- assertEq(_positionManager.getLPTokens(tokenId, indexes[2]), 6_000 * 1e27);
+ assertEq(_positionManager.getLPs(tokenId, indexes[0]), 4_000 * 1e18);
+ assertEq(_positionManager.getLPs(tokenId, indexes[1]), 5_000 * 1e18);
+ assertEq(_positionManager.getLPs(tokenId, indexes[2]), 6_000 * 1e18);
assertTrue(_positionManager.isIndexInPosition(tokenId, indexes[0]));
assertTrue(_positionManager.isIndexInPosition(tokenId, indexes[1]));
assertTrue(_positionManager.isIndexInPosition(tokenId, indexes[2]));
// construct move liquidity params
IPositionManagerOwnerActions.MoveLiquidityParams memory moveLiquidityParams = IPositionManagerOwnerActions.MoveLiquidityParams(
- tokenId, address(_pool), indexes[0], indexes[1]
+ tokenId, address(_pool), indexes[0], indexes[1], block.timestamp + 30
);
// move liquidity called by testAddress1
@@ -2833,59 +2462,47 @@ contract PositionManagerERC721PoolTest is PositionManagerERC721PoolHelperContrac
_positionManager.moveLiquidity(moveLiquidityParams);
// check LP balance
- _assertLenderLpBalance(
- {
- lender: testAddress1,
- index: indexes[0],
- lpBalance: 0,
- depositTime: 0
- }
- );
- _assertLenderLpBalance(
- {
- lender: address(_positionManager),
- index: indexes[0],
- lpBalance: 0,
- depositTime: currentTime
- }
- );
- _assertLenderLpBalance(
- {
- lender: testAddress1,
- index: indexes[1],
- lpBalance: 0,
- depositTime: 0
- }
- );
- _assertLenderLpBalance(
- {
- lender: address(_positionManager),
- index: indexes[1],
- lpBalance: 9_000 * 1e27,
- depositTime: currentTime
- }
- );
- _assertLenderLpBalance(
- {
- lender: testAddress1,
- index: indexes[2],
- lpBalance: 0,
- depositTime: 0
- }
- );
- _assertLenderLpBalance(
- {
- lender: address(_positionManager),
- index: indexes[2],
- lpBalance: 6_000 * 1e27,
- depositTime: currentTime
- }
- );
+ _assertLenderLpBalance({
+ lender: testAddress1,
+ index: indexes[0],
+ lpBalance: 0,
+ depositTime: 0
+ });
+ _assertLenderLpBalance({
+ lender: address(_positionManager),
+ index: indexes[0],
+ lpBalance: 0,
+ depositTime: currentTime
+ });
+ _assertLenderLpBalance({
+ lender: testAddress1,
+ index: indexes[1],
+ lpBalance: 0,
+ depositTime: 0
+ });
+ _assertLenderLpBalance({
+ lender: address(_positionManager),
+ index: indexes[1],
+ lpBalance: 9_000 * 1e18,
+ depositTime: currentTime
+ });
+ _assertLenderLpBalance({
+ lender: testAddress1,
+ index: indexes[2],
+ lpBalance: 0,
+ depositTime: 0
+ });
+ _assertLenderLpBalance({
+ lender: address(_positionManager),
+ index: indexes[2],
+ lpBalance: 6_000 * 1e18,
+ depositTime: currentTime
+ });
// check position manager state
- assertEq(_positionManager.getLPTokens(tokenId, indexes[0]), 0);
- assertEq(_positionManager.getLPTokens(tokenId, indexes[1]), 9_000 * 1e27);
- assertEq(_positionManager.getLPTokens(tokenId, indexes[2]), 6_000 * 1e27);
+ assertEq(_positionManager.getLPs(tokenId, indexes[0]), 0);
+ assertEq(_positionManager.getLPs(tokenId, indexes[1]), 9_000 * 1e18);
+ assertEq(_positionManager.getLPs(tokenId, indexes[2]), 6_000 * 1e18);
assertFalse(_positionManager.isIndexInPosition(tokenId, indexes[0]));
assertTrue(_positionManager.isIndexInPosition(tokenId, indexes[1]));
assertTrue(_positionManager.isIndexInPosition(tokenId, indexes[2]));
@@ -2928,83 +2545,65 @@ contract PositionManagerERC721PoolTest is PositionManagerERC721PoolHelperContrac
_positionManager.reedemPositions(reedemParams);
// check pool state
- _assertLenderLpBalance(
- {
- lender: testAddress1,
- index: indexes[0],
- lpBalance: 0,
- depositTime: 0
- }
- );
- _assertLenderLpBalance(
- {
- lender: testAddress2,
- index: indexes[0],
- lpBalance: 0,
- depositTime: 0
- }
- );
- _assertLenderLpBalance(
- {
- lender: address(_positionManager),
- index: indexes[0],
- lpBalance: 0,
- depositTime: currentTime
- }
- );
- _assertLenderLpBalance(
- {
- lender: testAddress1,
- index: indexes[0],
- lpBalance: 0,
- depositTime: 0
- }
- );
- _assertLenderLpBalance(
- {
- lender: testAddress2,
- index: indexes[1],
- lpBalance: 9_000 * 1e27,
- depositTime: currentTime
- }
- );
- _assertLenderLpBalance(
- {
- lender: address(_positionManager),
- index: indexes[1],
- lpBalance: 0,
- depositTime: 0
- }
- );
- _assertLenderLpBalance(
- {
- lender: testAddress1,
- index: indexes[0],
- lpBalance: 0,
- depositTime: 0
- }
- );
- _assertLenderLpBalance(
- {
- lender: testAddress2,
- index: indexes[2],
- lpBalance: 6_000 * 1e27,
- depositTime: currentTime
- }
- );
- _assertLenderLpBalance(
- {
- lender: address(_positionManager),
- index: indexes[2],
- lpBalance: 0,
- depositTime: 0
- }
- );
+ _assertLenderLpBalance({
+ lender: testAddress1,
+ index: indexes[0],
+ lpBalance: 0,
+ depositTime: 0
+ });
+ _assertLenderLpBalance({
+ lender: testAddress2,
+ index: indexes[0],
+ lpBalance: 0,
+ depositTime: 0
+ });
+ _assertLenderLpBalance({
+ lender: address(_positionManager),
+ index: indexes[0],
+ lpBalance: 0,
+ depositTime: currentTime
+ });
+ _assertLenderLpBalance({
+ lender: testAddress1,
+ index: indexes[0],
+ lpBalance: 0,
+ depositTime: 0
+ });
+ _assertLenderLpBalance({
+ lender: testAddress2,
+ index: indexes[1],
+ lpBalance: 9_000 * 1e18,
+ depositTime: currentTime
+ });
+ _assertLenderLpBalance({
+ lender: address(_positionManager),
+ index: indexes[1],
+ lpBalance: 0,
+ depositTime: 0
+ });
+ _assertLenderLpBalance({
+ lender: testAddress1,
+ index: indexes[0],
+ lpBalance: 0,
+ depositTime: 0
+ });
+ _assertLenderLpBalance({
+ lender: testAddress2,
+ index: indexes[2],
+ lpBalance: 6_000 * 1e18,
+ depositTime: currentTime
+ });
+ _assertLenderLpBalance({
+ lender: address(_positionManager),
+ index: indexes[2],
+ lpBalance: 0,
+ depositTime: 0
+ });
// check position manager state
- assertEq(_positionManager.getLPTokens(tokenId, indexes[0]), 0);
- assertEq(_positionManager.getLPTokens(tokenId, indexes[1]), 0);
- assertEq(_positionManager.getLPTokens(tokenId, indexes[2]), 0);
+ assertEq(_positionManager.getLPs(tokenId, indexes[0]), 0);
+ assertEq(_positionManager.getLPs(tokenId, indexes[1]), 0);
+ assertEq(_positionManager.getLPs(tokenId, indexes[2]), 0);
assertFalse(_positionManager.isIndexInPosition(tokenId, indexes[0]));
assertFalse(_positionManager.isIndexInPosition(tokenId, indexes[1]));
assertFalse(_positionManager.isIndexInPosition(tokenId, indexes[2]));
diff --git a/tests/forge/RewardsManager.t.sol b/tests/forge/RewardsManager.t.sol
index cbfe9578a..2bad9d0b7 100644
--- a/tests/forge/RewardsManager.t.sol
+++ b/tests/forge/RewardsManager.t.sol
@@ -160,8 +160,15 @@ contract RewardsManagerTest is DSTestPlus {
_rewardsManager.unstake(tokenId);
assertEq(_positionManager.ownerOf(tokenId), minter);
- // check token was transferred to rewards contract
+ // check token was transferred from rewards contract to minter
assertEq(_positionManager.ownerOf(tokenId), address(minter));
+
+ // invariant: all bucket snapshots are removed for the token id that was unstaken
+ for(uint256 bucketIndex = 0; bucketIndex <= 7388; bucketIndex++) {
+ (uint256 lps, uint256 rate) = _rewardsManager.getBucketStateStakeInfo(tokenId, bucketIndex);
+ assertEq(lps, 0);
+ assertEq(rate, 0);
+ }
}
function _triggerReserveAuctionsNoTake(TriggerReserveAuctionParams memory params_) internal {
@@ -179,7 +186,7 @@ contract RewardsManagerTest is DSTestPlus {
// borrower repays some of their debt, providing reserves to be claimed
// don't pull any collateral, as such functionality is unrelated to reserve auctions
- params_.pool.repayDebt(borrower, Maths.wdiv(params_.borrowAmount, Maths.wad(2)), 0);
+ params_.pool.repayDebt(borrower, Maths.wdiv(params_.borrowAmount, Maths.wad(2)), 0, borrower, MAX_FENWICK_INDEX);
// start reserve auction
changePrank(_bidder);
@@ -240,7 +247,7 @@ contract RewardsManagerTest is DSTestPlus {
tokenId_ = _positionManager.mint(mintParams);
for (uint256 i = 0; i < params_.indexes.length; i++) {
- params_.pool.addQuoteToken(params_.mintAmount, params_.indexes[i]);
+ params_.pool.addQuoteToken(params_.mintAmount, params_.indexes[i], type(uint256).max);
(uint256 lpBalance, ) = params_.pool.lenderInfo(params_.indexes[i], params_.minter);
params_.pool.approveLpOwnership(address(_positionManager), params_.indexes[i], lpBalance);
}
@@ -279,7 +286,7 @@ contract RewardsManagerTest is DSTestPlus {
// borrower repays some of their debt, providing reserves to be claimed
// don't pull any collateral, as such functionality is unrelated to reserve auctions
- params_.pool.repayDebt(borrower, params_.borrowAmount, 0);
+ params_.pool.repayDebt(borrower, params_.borrowAmount, 0, borrower, MAX_FENWICK_INDEX);
// start reserve auction
changePrank(_bidder);
@@ -408,7 +415,7 @@ contract RewardsManagerTest is DSTestPlus {
// check rewards earned
uint256 rewardsEarned = _rewardsManager.calculateRewards(tokenIdOne, currentBurnEpoch);
- assertEq(rewardsEarned, 40.214136545950568150 * 1e18);
+ assertEq(rewardsEarned, 40.214136545950568100 * 1e18);
// claim rewards accrued since deposit
changePrank(_minterOne);
@@ -504,7 +511,7 @@ contract RewardsManagerTest is DSTestPlus {
pool: address(_poolOne),
tokenId: tokenIdOne,
claimedArray: _epochsClaimedArray(2, 0),
- reward: 80.793427892333608620 * 1e18,
+ reward: 80.793427892333608615 * 1e18,
updateRatesReward: 3.689026486034825940 * 1e18
});
}
@@ -623,7 +630,7 @@ contract RewardsManagerTest is DSTestPlus {
// borrower1 repays their loan
(uint256 debt, , ) = _poolOne.borrowerInfo(borrower1);
- _poolOne.repayDebt(borrower1, debt, 0);
+ _poolOne.repayDebt(borrower1, debt, 0, borrower1, MAX_FENWICK_INDEX);
/*****************************/
/*** First Reserve Auction ***/
@@ -683,7 +690,7 @@ contract RewardsManagerTest is DSTestPlus {
// borrower1 repays their loan again
changePrank(borrower1);
(debt, , ) = _poolOne.borrowerInfo(borrower1);
- _poolOne.repayDebt(borrower1, debt, 0);
+ _poolOne.repayDebt(borrower1, debt, 0, borrower1, MAX_FENWICK_INDEX);
// recorder updates the change in exchange rates in the second index
_updateExchangeRates({
@@ -704,7 +711,7 @@ contract RewardsManagerTest is DSTestPlus {
pool: address(_poolOne),
tokenId: tokenIdOne,
claimedArray: _epochsClaimedArray(1, 0),
- reward: 0.298393183929769729 * 1e18,
+ reward: 0.298393183929769450 * 1e18,
updateRatesReward: 0
});
}
@@ -759,7 +766,7 @@ contract RewardsManagerTest is DSTestPlus {
assertEq(_ajnaToken.balanceOf(_updater), 18.914328218434904846 * 1e18);
uint256 rewardsEarned = _rewardsManager.calculateRewards(tokenIdOne, _poolOne.currentBurnEpoch());
- assertEq(rewardsEarned, 189.143282184349085719 * 1e18);
+ assertEq(rewardsEarned, 189.143282184349085696 * 1e18);
assertLt(rewardsEarned, Maths.wmul(totalTokensBurned, 0.800000000000000000 * 1e18));
/******************************/
@@ -784,7 +791,7 @@ contract RewardsManagerTest is DSTestPlus {
// check available rewards
rewardsEarned = _rewardsManager.calculateRewards(tokenIdOne, _poolOne.currentBurnEpoch());
- assertEq(rewardsEarned, 354.209322508542220912 * 1e18);
+ assertEq(rewardsEarned, 354.209322508542220207 * 1e18);
assertLt(rewardsEarned, Maths.wmul(totalTokensBurned, 0.800000000000000000 * 1e18));
/*****************************/
@@ -801,7 +808,7 @@ contract RewardsManagerTest is DSTestPlus {
// skip updating exchange rates and check available rewards
uint256 rewardsEarnedNoUpdate = _rewardsManager.calculateRewards(tokenIdOne, _poolOne.currentBurnEpoch());
- assertEq(rewardsEarnedNoUpdate, 354.209322508542220912 * 1e18);
+ assertEq(rewardsEarnedNoUpdate, 354.209322508542220207 * 1e18);
assertLt(rewardsEarned, Maths.wmul(totalTokensBurned, 0.800000000000000000 * 1e18));
// snapshot calling update exchange rate
@@ -817,7 +824,7 @@ contract RewardsManagerTest is DSTestPlus {
// check available rewards
rewardsEarned = _rewardsManager.calculateRewards(tokenIdOne, _poolOne.currentBurnEpoch());
- assertEq(rewardsEarned, 489.772410159936903182 * 1e18);
+ assertEq(rewardsEarned, 489.772410159936902605 * 1e18);
assertGt(rewardsEarned, rewardsEarnedNoUpdate);
assertLt(rewardsEarned, Maths.wmul(totalTokensBurned, 0.800000000000000000 * 1e18));
@@ -838,7 +845,7 @@ contract RewardsManagerTest is DSTestPlus {
// check rewards earned
rewardsEarned = _rewardsManager.calculateRewards(tokenIdOne, _poolOne.currentBurnEpoch());
- assertEq(rewardsEarned, 354.209322508542220912 * 1e18);
+ assertEq(rewardsEarned, 354.209322508542220207 * 1e18);
// call update exchange rate
changePrank(_updater2);
@@ -850,7 +857,7 @@ contract RewardsManagerTest is DSTestPlus {
// check rewards earned won't increase since previous update was missed
rewardsEarned = _rewardsManager.calculateRewards(tokenIdOne, _poolOne.currentBurnEpoch());
- assertEq(rewardsEarned, 354.209322508542220912 * 1e18);
+ assertEq(rewardsEarned, 354.209322508542220207 * 1e18);
/*****************************/
/*** Fifth Reserve Auction ***/
@@ -873,7 +880,7 @@ contract RewardsManagerTest is DSTestPlus {
assertEq(_ajnaToken.balanceOf(_updater2), 10.978967849507124864 * 1e18);
rewardsEarned = _rewardsManager.calculateRewards(tokenIdOne, _poolOne.currentBurnEpoch());
- assertEq(rewardsEarned, 463.999001003613678404 * 1e18);
+ assertEq(rewardsEarned, 463.999001003613677587 * 1e18);
// claim all rewards accrued since deposit
changePrank(_minterOne);
@@ -904,11 +911,11 @@ contract RewardsManagerTest is DSTestPlus {
});
uint256 tokenIdTwo = _mintAndMemorializePositionNFT(mintMemorializeParams);
// bucket exchange rates are not changed at the time minter two stakes
- assertEq(_poolOne.bucketExchangeRate(2550), 1e27);
- assertEq(_poolOne.bucketExchangeRate(2551), 1e27);
- assertEq(_poolOne.bucketExchangeRate(2552), 1e27);
- assertEq(_poolOne.bucketExchangeRate(2553), 1e27);
- assertEq(_poolOne.bucketExchangeRate(2555), 1e27);
+ assertEq(_poolOne.bucketExchangeRate(2550), 1e18);
+ assertEq(_poolOne.bucketExchangeRate(2551), 1e18);
+ assertEq(_poolOne.bucketExchangeRate(2552), 1e18);
+ assertEq(_poolOne.bucketExchangeRate(2553), 1e18);
+ assertEq(_poolOne.bucketExchangeRate(2555), 1e18);
_stakeToken(address(_poolOne), _minterTwo, tokenIdTwo);
// borrower borrows and change the exchange rates of buckets
@@ -928,11 +935,11 @@ contract RewardsManagerTest is DSTestPlus {
});
uint256 tokenIdThree = _mintAndMemorializePositionNFT(mintMemorializeParams);
// bucket exchange rates are higher at the time minter three stakes
- assertEq(_poolOne.bucketExchangeRate(2550), 1.000000116565164638999999999 * 1e27);
- assertEq(_poolOne.bucketExchangeRate(2551), 1.000000116565164638999999999 * 1e27);
- assertEq(_poolOne.bucketExchangeRate(2552), 1.000000116565164638999999999 * 1e27);
- assertEq(_poolOne.bucketExchangeRate(2553), 1.000000116565164638999999999 * 1e27);
- assertEq(_poolOne.bucketExchangeRate(2555), 1.000000116565164638999999999 * 1e27);
+ assertEq(_poolOne.bucketExchangeRate(2550), 1.000000116565164639 * 1e18);
+ assertEq(_poolOne.bucketExchangeRate(2551), 1.000000116565164639 * 1e18);
+ assertEq(_poolOne.bucketExchangeRate(2552), 1.000000116565164639 * 1e18);
+ assertEq(_poolOne.bucketExchangeRate(2553), 1.000000116565164639 * 1e18);
+ assertEq(_poolOne.bucketExchangeRate(2555), 1.000000116565164639 * 1e18);
_stakeToken(address(_poolOne), _minterThree, tokenIdThree);
skip(1 days);
@@ -951,30 +958,26 @@ contract RewardsManagerTest is DSTestPlus {
pool: address(_poolOne),
tokenId: tokenIdTwo,
claimedArray: _epochsClaimedArray(1, 0),
- reward: 20.035397317001861785 * 1e18,
+ reward: 20.035397317001861795 * 1e18,
updateRatesReward: 0
});
uint256 minterTwoBalance = _ajnaToken.balanceOf(_minterTwo);
- assertEq(minterTwoBalance, 20.035397317001861785 * 1e18);
+ assertEq(minterTwoBalance, 20.035397317001861795 * 1e18);
_unstakeToken({
minter: _minterThree,
pool: address(_poolOne),
tokenId: tokenIdThree,
claimedArray: _epochsClaimedArray(1, 0),
- reward: 16.692493739675876000 * 1e18,
+ reward: 16.692493739675875940 * 1e18,
updateRatesReward: 0
});
uint256 minterThreeBalance = _ajnaToken.balanceOf(_minterThree);
- assertEq(minterThreeBalance, 16.692493739675876000 * 1e18);
+ assertEq(minterThreeBalance, 16.692493739675875940 * 1e18);
assertGt(minterTwoBalance, minterThreeBalance);
}
- function testMultiPeriodRewardsMultiClaim() external {
-
- }
-
// Calling updateExchangeRates not needed since deposits will update the exchange rate themselves
function testClaimRewardsMultipleDepositsSameBucketsMultipleAuctions() external {
skip(10);
@@ -1180,7 +1183,7 @@ contract RewardsManagerTest is DSTestPlus {
updater: _updater,
pool: address(_poolOne),
depositIndexes: depositIndexes,
- reward: 11.241216009399483348 * 1e18
+ reward: 11.241216009399483350 * 1e18
});
_triggerReserveAuctions(TriggerReserveAuctionParams({
@@ -1209,24 +1212,24 @@ contract RewardsManagerTest is DSTestPlus {
pool: address(_poolOne),
epoch: 1,
timestamp: block.timestamp - (52 weeks + 72 hours),
- interest: 6447445050021308895,
- burned: 81574747191341355205
+ interest: 6.447445050021308895 * 1e18,
+ burned: 81.574747191341355205 * 1e18
});
_assertBurn({
pool: address(_poolOne),
epoch: 2,
timestamp: block.timestamp - (26 weeks + 48 hours),
- burned: 306399067379332449973,
- interest: 23974564976746846096
+ burned: 306.399067379332450033 * 1e18,
+ interest: 23.974564976746846096 * 1e18
});
_assertBurn({
pool: address(_poolOne),
epoch: 3,
timestamp: block.timestamp - 24 hours,
- burned: 699814215483322160364,
- interest: 55764974712671474765
+ burned: 699.814215483322160424 * 1e18,
+ interest: 55.764974712671474765 * 1e18
});
// both stakers claim rewards
@@ -1235,7 +1238,7 @@ contract RewardsManagerTest is DSTestPlus {
pool: address(_poolOne),
tokenId: tokenIdOne,
claimedArray: _epochsClaimedArray(3, 0),
- reward: 58.317851290276861885 * 1e18,
+ reward: 58.317851290276861945 * 1e18,
updateRatesReward: 0
});
@@ -1244,7 +1247,7 @@ contract RewardsManagerTest is DSTestPlus {
pool: address(_poolOne),
tokenId: tokenIdTwo,
claimedArray: _epochsClaimedArray(3, 0),
- reward: 291.589256451384309685 * 1e18,
+ reward: 291.589256451384309690 * 1e18,
updateRatesReward: 0
});
}
@@ -1328,14 +1331,41 @@ contract RewardsManagerTest is DSTestPlus {
assertGt(_ajnaToken.balanceOf(_updater), 0);
// check owner can withdraw the NFT and rewards will be automatically claimed
+
+ uint256 snapshot = vm.snapshot();
+
+ // claimed rewards amount is greater than available tokens in rewards manager contract
+
+ // burn rewards manager tokens and leave only 5 tokens available
+ changePrank(address(_rewardsManager));
+ IERC20Token(address(_ajnaToken)).burn(99_999_990.978586345404952410 * 1e18);
+
+ uint256 managerBalance = _ajnaToken.balanceOf(address(_rewardsManager));
+ assertEq(managerBalance, 5 * 1e18);
+
changePrank(_minterOne);
vm.expectEmit(true, true, true, true);
- emit ClaimRewards(_minterOne, address(_poolOne), tokenIdOne, _epochsClaimedArray(1, 0), 40.214136545950568150 * 1e18);
+ emit ClaimRewards(_minterOne, address(_poolOne), tokenIdOne, _epochsClaimedArray(1, 0), 40.214136545950568100 * 1e18);
+ vm.expectEmit(true, true, true, true);
+ emit Unstake(_minterOne, address(_poolOne), tokenIdOne);
+ _rewardsManager.unstake(tokenIdOne);
+
+ // minter one receives only the amount of 5 ajna tokens available in manager balance instead calculated rewards of 40.214136545950568150
+ assertEq(_ajnaToken.balanceOf(_minterOne), managerBalance);
+ // all 5 tokens available in manager balance were used to reward minter one
+ assertEq(_ajnaToken.balanceOf(address(_rewardsManager)), 0);
+
+ vm.revertTo(snapshot);
+
+ // test when enough tokens in rewards manager contracts
+ changePrank(_minterOne);
+ vm.expectEmit(true, true, true, true);
+ emit ClaimRewards(_minterOne, address(_poolOne), tokenIdOne, _epochsClaimedArray(1, 0), 40.214136545950568100 * 1e18);
vm.expectEmit(true, true, true, true);
emit Unstake(_minterOne, address(_poolOne), tokenIdOne);
_rewardsManager.unstake(tokenIdOne);
assertEq(_positionManager.ownerOf(tokenIdOne), _minterOne);
- assertEq(_ajnaToken.balanceOf(_minterOne), 40.214136545950568150 * 1e18);
+ assertEq(_ajnaToken.balanceOf(_minterOne), 40.214136545950568100 * 1e18);
assertLt(_ajnaToken.balanceOf(_minterOne), tokensToBurn);
uint256 currentBurnEpoch = _poolOne.currentBurnEpoch();
@@ -1376,6 +1406,7 @@ contract RewardsManagerTest is DSTestPlus {
mintAmount: 1000 * 1e18,
pool: _poolTwo
});
+
uint256 tokenIdTwo = _mintAndMemorializePositionNFT(mintMemorializeParams);
// minterOne deposits their NFT into the rewards contract
@@ -1391,6 +1422,7 @@ contract RewardsManagerTest is DSTestPlus {
limitIndex: 3,
pool: _poolOne
});
+
uint256 tokensToBurn = _triggerReserveAuctions(triggerReserveAuctionParams);
uint256 currentBurnEpochPoolOne = _poolOne.currentBurnEpoch();
@@ -1418,9 +1450,9 @@ contract RewardsManagerTest is DSTestPlus {
changePrank(_minterOne);
assertEq(_ajnaToken.balanceOf(_minterOne), 4.021413654595047590 * 1e18);
vm.expectEmit(true, true, true, true);
- emit ClaimRewards(_minterOne, address(_poolOne), tokenIdOne, _epochsClaimedArray(1, 0), 40.214136545950568150 * 1e18);
+ emit ClaimRewards(_minterOne, address(_poolOne), tokenIdOne, _epochsClaimedArray(1, 0), 40.214136545950568100 * 1e18);
_rewardsManager.claimRewards(tokenIdOne, currentBurnEpochPoolOne);
- assertEq(_ajnaToken.balanceOf(_minterOne), 44.235550200545615740 * 1e18);
+ assertEq(_ajnaToken.balanceOf(_minterOne), 44.235550200545615690 * 1e18);
assertLt(_ajnaToken.balanceOf(_minterOne), tokensToBurn);
}
@@ -1596,4 +1628,34 @@ contract RewardsManagerTest is DSTestPlus {
}
}
+ function testClaimRewardsFreezeUnclaimedYield() external {
+ skip(10);
+
+ uint256[] memory depositIndexes = new uint256[](5);
+ depositIndexes[0] = 9;
+ depositIndexes[1] = 1;
+ depositIndexes[2] = 2;
+ depositIndexes[3] = 3;
+ depositIndexes[4] = 4;
+ MintAndMemorializeParams memory mintMemorializeParams = MintAndMemorializeParams({
+ indexes: depositIndexes,
+ minter: _minterOne,
+ mintAmount: 1000 * 1e18,
+ pool: _poolOne
+ });
+
+ uint256 tokenIdOne = _mintAndMemorializePositionNFT(mintMemorializeParams);
+ _stakeToken(address(_poolOne), _minterOne, tokenIdOne);
+
+ uint256 currentBurnEpoch = _poolOne.currentBurnEpoch();
+
+ changePrank(_minterOne);
+ // should revert if the epoch to claim is not available yet
+ vm.expectRevert(IRewardsManagerErrors.EpochNotAvailable.selector);
+ _rewardsManager.claimRewards(tokenIdOne, currentBurnEpoch + 10);
+
+ // user should be able to claim rewards for current epoch
+ _rewardsManager.claimRewards(tokenIdOne, currentBurnEpoch);
+ }
+
}
diff --git a/tests/forge/interactions/BalancerUniswapExample.sol b/tests/forge/interactions/BalancerUniswapExample.sol
index 5b83d185c..d3b7c6eec 100644
--- a/tests/forge/interactions/BalancerUniswapExample.sol
+++ b/tests/forge/interactions/BalancerUniswapExample.sol
@@ -134,9 +134,9 @@ contract BalancerUniswapPurchaser {
// approve ajna pool to transfer flash loaned collateral
collateral.approve(decoded.ajnaPool, loanAmount);
// purchase USDC with 1 WETH from ajna
- uint256 lps = IAjnaPool(decoded.ajnaPool).addCollateral(loanAmount, decoded.bucketIndex);
+ uint256 lps = IAjnaPool(decoded.ajnaPool).addCollateral(loanAmount, decoded.bucketIndex, block.timestamp + 5 minutes);
(uint256 quoteAmount, ) = IAjnaPool(decoded.ajnaPool).removeQuoteToken(type(uint256).max, decoded.bucketIndex);
- assert(lps == 83008350.10362729922336157 * 1e27); // LPS in bucket
+ assert(lps == 83008350.10362729922336157 * 1e18); // LPS in bucket
assert(quoteAmount == 4995.19230769230769 * 1e18); // Purchased quote amount
assert(quote.balanceOf(address(this)) == 4995.192307 * 1e6); // USDC balance after Ajna purchase
assert(collateral.balanceOf(address(this)) == 0); // WETH balance after Ajna purchase
diff --git a/tests/forge/interactions/ERC20TakeWithExternalLiquidity.t.sol b/tests/forge/interactions/ERC20TakeWithExternalLiquidity.t.sol
index f16a77e10..330352a96 100644
--- a/tests/forge/interactions/ERC20TakeWithExternalLiquidity.t.sol
+++ b/tests/forge/interactions/ERC20TakeWithExternalLiquidity.t.sol
@@ -54,11 +54,11 @@ contract ERC20TakeWithExternalLiquidityTest is Test {
// add liquidity to the Ajna pool
vm.startPrank(_lender);
usdc.approve(address(_ajnaPool), type(uint256).max);
- _ajnaPool.addQuoteToken(2_000 * 1e18, 3696);
- _ajnaPool.addQuoteToken(5_000 * 1e18, 3698);
- _ajnaPool.addQuoteToken(11_000 * 1e18, 3700);
- _ajnaPool.addQuoteToken(25_000 * 1e18, 3702);
- _ajnaPool.addQuoteToken(30_000 * 1e18, 3704);
+ _ajnaPool.addQuoteToken(2_000 * 1e18, 3696, type(uint256).max);
+ _ajnaPool.addQuoteToken(5_000 * 1e18, 3698, type(uint256).max);
+ _ajnaPool.addQuoteToken(11_000 * 1e18, 3700, type(uint256).max);
+ _ajnaPool.addQuoteToken(25_000 * 1e18, 3702, type(uint256).max);
+ _ajnaPool.addQuoteToken(30_000 * 1e18, 3704, type(uint256).max);
vm.stopPrank();
// borrower draws debt
@@ -132,4 +132,27 @@ contract ERC20TakeWithExternalLiquidityTest is Test {
// confirm we earned some quote token
assertGt(usdc.balanceOf(address(taker)), 0);
}
+
+ function testTakeCalleeDiffersFromSender() external {
+
+ // _lender is msg.sender, QT & CT balances pre take
+ assertEq(usdc.balanceOf(_lender), 119_999.999999926999804658 * 1e18);
+ assertEq(weth.balanceOf(_lender), 0);
+
+ // callee, _lender1 QT & CT balances pre take
+ assertEq(usdc.balanceOf(_lender1), 120_000.0 * 1e18);
+ assertEq(weth.balanceOf(_lender1), 4.0 * 1e18);
+
+ // lender calls take, passing _lender1 as the callee
+ changePrank(_lender);
+ _ajnaPool.take(_borrower, 1_001 * 1e18, _lender1, new bytes(0));
+
+ // _lender is has QT deducted from balance
+ assertEq(usdc.balanceOf(_lender), 119_999.999999926985301196 * 1e18);
+ assertEq(weth.balanceOf(_lender), 0);
+
+ // callee, _lender1 receives CT from take
+ assertEq(usdc.balanceOf(_lender1), 120_000.0 * 1e18);
+ assertEq(weth.balanceOf(_lender1), 6.0 * 1e18);
+ }
}
diff --git a/tests/forge/interactions/ERC721TakeWithExternalLiquidity.sol b/tests/forge/interactions/ERC721TakeWithExternalLiquidity.sol
index 8e3327282..927b22a58 100644
--- a/tests/forge/interactions/ERC721TakeWithExternalLiquidity.sol
+++ b/tests/forge/interactions/ERC721TakeWithExternalLiquidity.sol
@@ -36,13 +36,11 @@ contract ERC721TakeWithExternalLiquidityTest is ERC721HelperContract {
_quote.approve(address(_pool), type(uint256).max);
// lender deposits 50_000 Quote into the pool
- _addInitialLiquidity(
- {
- from: _lender,
- amount: 50_000 * 1e18,
- index: _i1004_98
- }
- );
+ _addInitialLiquidity({
+ from: _lender,
+ amount: 50_000 * 1e18,
+ index: _i1004_98
+ });
uint256[] memory tokenIdsToAdd = new uint256[](2);
tokenIdsToAdd[0] = 1;
@@ -90,4 +88,35 @@ contract ERC721TakeWithExternalLiquidityTest is ERC721HelperContract {
// confirm we earned some quote token
assertEq(_quote.balanceOf(address(taker)), 970.423096682230524352 * 1e18);
}
+
+ function testTakeNFTCalleeDiffersFromSender() external {
+ // instantiate and fund a hypothetical NFT marketplace
+ NFTMarketPlace marketPlace = new NFTMarketPlace(_quote);
+ deal(address(_quote), address(marketPlace), 25_000 * 1e18);
+
+ // instantiate a taker contract which implements IERC721Taker and uses this marketplace
+ NFTTakeExample taker = new NFTTakeExample(address(marketPlace));
+ changePrank(address(taker));
+ assertEq(_quote.balanceOf(address(taker)), 0);
+ _quote.approve(address(_pool), type(uint256).max);
+ _collateral.setApprovalForAll(address(marketPlace), true);
+
+ // _lender is msg.sender, QT & CT balances pre take
+ assertEq(_quote.balanceOf(_lender), 49_979.825641778370686641 * 1e18);
+ assertEq(_quote.balanceOf(address(taker)), 0);
+
+ // call take using taker contract
+ changePrank(_lender);
+ bytes memory data = abi.encode(address(_pool));
+ vm.expectEmit(true, true, false, true);
+ uint256 quoteTokenPaid = 529.576903317769475648 * 1e18;
+ uint256 collateralPurchased = 2 * 1e18;
+ uint256 bondChange = 5.295769033177694756 * 1e18;
+ emit Take(_borrower, quoteTokenPaid, collateralPurchased, bondChange, true);
+ _pool.take(_borrower, 2, address(taker), data);
+
+ // _lender is msg.sender, QT & CT balances post take
+ assertEq(_quote.balanceOf(_lender), 49_450.248738460601210993 * 1e18);
+ assertEq(_quote.balanceOf(address(taker)), 1_500.0 * 1e18); // QT is increased as NFTTakeExample contract sells the NFT
+ }
}
diff --git a/tests/forge/interactions/Interfaces.sol b/tests/forge/interactions/Interfaces.sol
index e00c7b003..3ed9cd8a6 100644
--- a/tests/forge/interactions/Interfaces.sol
+++ b/tests/forge/interactions/Interfaces.sol
@@ -13,7 +13,8 @@ interface IAjnaPool {
function addCollateral(
uint256 amount,
- uint256 index
+ uint256 index,
+ uint256 expiry
) external returns (uint256 lpbChange);
function removeQuoteToken(
diff --git a/tests/forge/interactions/NFTTakeExample.sol b/tests/forge/interactions/NFTTakeExample.sol
index 9a1e91687..78d730e26 100644
--- a/tests/forge/interactions/NFTTakeExample.sol
+++ b/tests/forge/interactions/NFTTakeExample.sol
@@ -58,6 +58,30 @@ contract NFTMarketPlace is INFTMarketPlace {
currency.transfer(msg.sender, collectionOffer);
}
+ /** @notice Implementing this method allows contracts to receive ERC721 tokens
+ * @dev https://forum.openzeppelin.com/t/erc721holder-ierc721receiver-and-onerc721received/11828
+ */
+ function onERC721Received(address, address, uint256, bytes memory) external pure returns (bytes4) {
+ return this.onERC721Received.selector;
+ }
+}
+
+contract NFTNoopTakeExample is IERC721Taker {
+ uint256[] public tokenIdsReceived;
+ uint256 public quoteAmountDueReceived;
+ address public poolAddressReceived;
+
+ function atomicSwapCallback(
+ uint256[] memory tokenIds,
+ uint256 quoteAmountDue,
+ bytes calldata data
+ ) external {
+ // NOOP, records inputs passed from pool to be checked in tests
+ tokenIdsReceived = tokenIds;
+ quoteAmountDueReceived = quoteAmountDue;
+ poolAddressReceived = abi.decode(data, (address));
+ }
+
/** @notice Implementing this method allows contracts to receive ERC721 tokens
* @dev https://forum.openzeppelin.com/t/erc721holder-ierc721receiver-and-onerc721received/11828
*/
diff --git a/tests/forge/interactions/PurchaseQuoteWithExternalLiquidity.t.sol b/tests/forge/interactions/PurchaseQuoteWithExternalLiquidity.t.sol
index 481be68c8..e9dfd6cfe 100644
--- a/tests/forge/interactions/PurchaseQuoteWithExternalLiquidity.t.sol
+++ b/tests/forge/interactions/PurchaseQuoteWithExternalLiquidity.t.sol
@@ -28,7 +28,7 @@ contract PurchaseQuoteWithExternalLiquidityTest is Test {
deal(USDC, _lender, 120_000 * 1e6);
vm.startPrank(_lender);
usdc.approve(address(_ajnaPool), type(uint256).max);
- _ajnaPool.addQuoteToken(5_000 * 1e18, 500);
+ _ajnaPool.addQuoteToken(5_000 * 1e18, 500, type(uint256).max);
vm.stopPrank();
}
diff --git a/tests/forge/utils/DSTestPlus.sol b/tests/forge/utils/DSTestPlus.sol
index dcc00ab2e..ef1f25d0c 100644
--- a/tests/forge/utils/DSTestPlus.sol
+++ b/tests/forge/utils/DSTestPlus.sol
@@ -22,7 +22,7 @@ abstract contract DSTestPlus is Test, IPoolEvents {
using EnumerableSet for EnumerableSet.UintSet;
// nonce for generating random addresses
- uint16 internal _nonce = 0;
+ uint256 internal _nonce = 0;
// mainnet address of AJNA token, because tests are forked
address internal _ajna = 0x9a96ec9B57Fb64FbC60B423d1f4da7691Bd35079;
@@ -104,7 +104,7 @@ abstract contract DSTestPlus is Test, IPoolEvents {
uint256 index
) internal {
uint256 quoteTokenScale = IPool(address(_pool)).quoteTokenScale();
- uint256 lpAmount = (amount / quoteTokenScale) * quoteTokenScale * 1e9;
+ uint256 lpAmount = (amount / quoteTokenScale) * quoteTokenScale;
_addLiquidity(from, amount, index, lpAmount, MAX_PRICE);
}
@@ -115,7 +115,7 @@ abstract contract DSTestPlus is Test, IPoolEvents {
uint256 index
) internal {
changePrank(from);
- _pool.addQuoteToken(amount, index);
+ _pool.addQuoteToken(amount, index, type(uint256).max);
// Add for tearDown
lenders.add(from);
@@ -136,7 +136,7 @@ abstract contract DSTestPlus is Test, IPoolEvents {
vm.expectEmit(true, true, false, true);
emit AddQuoteToken(from, index, (amount / quoteTokenScale) * quoteTokenScale, lpAward, newLup);
_assertQuoteTokenTransferEvent(from, address(_pool), amount);
- _pool.addQuoteToken(amount, index);
+ _pool.addQuoteToken(amount, index, type(uint256).max);
// Add for tearDown
lenders.add(from);
@@ -246,7 +246,7 @@ abstract contract DSTestPlus is Test, IPoolEvents {
vm.expectEmit(true, true, false, true);
emit Kick(borrower, debt, collateral, bond);
vm.expectEmit(true, true, false, true);
- emit RemoveQuoteToken(from, index, removedFromDeposit, removedFromDeposit * 1e9, lup);
+ emit RemoveQuoteToken(from, index, removedFromDeposit, removedFromDeposit, lup);
if(transferAmount != 0) _assertQuoteTokenTransferEvent(from, address(_pool), transferAmount);
_pool.kickWithDeposit(index);
}
@@ -276,7 +276,7 @@ abstract contract DSTestPlus is Test, IPoolEvents {
changePrank(from);
vm.expectEmit(true, true, true, true);
emit MoveQuoteToken(from, fromIndex, toIndex, amountMoved, lpRedeemFrom, lpAwardTo, newLup);
- (uint256 lpbFrom, uint256 lpbTo) = _pool.moveQuoteToken(amount, fromIndex, toIndex);
+ (uint256 lpbFrom, uint256 lpbTo) = _pool.moveQuoteToken(amount, fromIndex, toIndex, type(uint256).max);
assertEq(lpbFrom, lpRedeemFrom);
assertEq(lpbTo, lpAwardTo);
@@ -711,7 +711,7 @@ abstract contract DSTestPlus is Test, IPoolEvents {
) internal {
changePrank(from);
vm.expectRevert(abi.encodeWithSignature('BucketBankruptcyBlock()'));
- _pool.addQuoteToken(amount, index);
+ _pool.addQuoteToken(amount, index, type(uint256).max);
}
function _assertAddLiquidityAtIndex0Revert(
@@ -720,7 +720,7 @@ abstract contract DSTestPlus is Test, IPoolEvents {
) internal {
changePrank(from);
vm.expectRevert(IPoolErrors.InvalidIndex.selector);
- _pool.addQuoteToken(amount, 0);
+ _pool.addQuoteToken(amount, 0, type(uint256).max);
}
function _assertAddLiquidityDustRevert(
@@ -730,7 +730,18 @@ abstract contract DSTestPlus is Test, IPoolEvents {
) internal {
changePrank(from);
vm.expectRevert(IPoolErrors.DustAmountNotExceeded.selector);
- _pool.addQuoteToken(amount, index);
+ _pool.addQuoteToken(amount, index, type(uint256).max);
+ }
+
+ function _assertAddLiquidityExpiredRevert(
+ address from,
+ uint256 amount,
+ uint256 index,
+ uint256 expiry
+ ) internal {
+ changePrank(from);
+ vm.expectRevert(IPoolErrors.TransactionExpired.selector);
+ _pool.addQuoteToken(amount, index, expiry);
}
function _assertArbTakeNoAuction(
@@ -1100,7 +1111,7 @@ abstract contract DSTestPlus is Test, IPoolEvents {
) internal {
changePrank(from);
vm.expectRevert(abi.encodeWithSignature('BucketBankruptcyBlock()'));
- _pool.moveQuoteToken(amount, fromIndex, toIndex);
+ _pool.moveQuoteToken(amount, fromIndex, toIndex, type(uint256).max);
}
function _assertMoveLiquidityLupBelowHtpRevert(
@@ -1111,7 +1122,19 @@ abstract contract DSTestPlus is Test, IPoolEvents {
) internal {
changePrank(from);
vm.expectRevert(IPoolErrors.LUPBelowHTP.selector);
- _pool.moveQuoteToken(amount, fromIndex, toIndex);
+ _pool.moveQuoteToken(amount, fromIndex, toIndex, type(uint256).max);
+ }
+
+ function _assertMoveLiquidityExpiredRevert(
+ address from,
+ uint256 amount,
+ uint256 fromIndex,
+ uint256 toIndex,
+ uint256 expiry
+ ) internal {
+ changePrank(from);
+ vm.expectRevert(IPoolErrors.TransactionExpired.selector);
+ _pool.moveQuoteToken(amount, fromIndex, toIndex, expiry);
}
function _assertMoveLiquidityDustRevert(
@@ -1122,7 +1145,7 @@ abstract contract DSTestPlus is Test, IPoolEvents {
) internal {
changePrank(from);
vm.expectRevert(IPoolErrors.DustAmountNotExceeded.selector);
- _pool.moveQuoteToken(amount, fromIndex, toIndex);
+ _pool.moveQuoteToken(amount, fromIndex, toIndex, type(uint256).max);
}
function _assertMoveLiquidityToSamePriceRevert(
@@ -1133,7 +1156,7 @@ abstract contract DSTestPlus is Test, IPoolEvents {
) internal {
changePrank(from);
vm.expectRevert(IPoolErrors.MoveToSamePrice.selector);
- _pool.moveQuoteToken(amount, fromIndex, toIndex);
+ _pool.moveQuoteToken(amount, fromIndex, toIndex, type(uint256).max);
}
function _assertMoveLiquidityToIndex0Revert(
@@ -1143,7 +1166,7 @@ abstract contract DSTestPlus is Test, IPoolEvents {
) internal {
changePrank(from);
vm.expectRevert(IPoolErrors.InvalidIndex.selector);
- _pool.moveQuoteToken(amount, fromIndex, 0);
+ _pool.moveQuoteToken(amount, fromIndex, 0, type(uint256).max);
}
function _assertMoveDepositLockedByAuctionDebtRevert(
@@ -1154,7 +1177,7 @@ abstract contract DSTestPlus is Test, IPoolEvents {
) internal {
changePrank(from);
vm.expectRevert(IPoolErrors.RemoveDepositLockedByAuctionDebt.selector);
- _pool.moveQuoteToken(amount, fromIndex, toIndex);
+ _pool.moveQuoteToken(amount, fromIndex, toIndex, type(uint256).max);
}
function _assertTakeAuctionInCooldownRevert(
@@ -1253,6 +1276,14 @@ abstract contract DSTestPlus is Test, IPoolEvents {
lup_ = _priceAt(lupIndex);
}
+ function setRandomSeed(uint256 seed) public {
+ _nonce = seed;
+ }
+
+ function getNextNonce() public returns (uint256) {
+ return _nonce == type(uint256).max ? 0 : ++_nonce;
+ }
+
function randomInRange(uint256 min, uint256 max) public returns (uint256) {
return randomInRange(min, max, false);
}
@@ -1260,23 +1291,19 @@ abstract contract DSTestPlus is Test, IPoolEvents {
function randomInRange(uint256 min, uint256 max, bool nonZero) public returns (uint256) {
if (max == 0 && nonZero) return 1;
else if (max == min) return max;
- uint256 rand = uint(keccak256(abi.encodePacked(block.timestamp, msg.sender, _nonce))) % (max - min + 1) + min;
- _nonce++;
- return rand;
+ return uint(keccak256(abi.encodePacked(msg.sender, getNextNonce()))) % (max - min + 1) + min;
}
// returns a random index between 1 and 7388
function _randomIndex() internal returns (uint256 index_) {
// calculate a random index between 1 and 7388
- index_ = 1 + uint256(keccak256(abi.encodePacked(block.number, block.difficulty))) % 7387;
- vm.roll(block.number + 1); // advance block to ensure that the index price is different
+ index_ = 1 + uint256(keccak256(abi.encodePacked(msg.sender, getNextNonce()))) % 7387;
}
// returns a random index between 1 and a given maximum
// used for testing in NFT pools where higher indexes (and lower prices) would require so many NFTs that gas and memory limits would be exceeded
function _randomIndexWithMinimumPrice(uint256 minimumPrice_) internal returns (uint256 index_) {
- index_ = 1 + uint256(keccak256(abi.encodePacked(block.number, block.difficulty))) % minimumPrice_;
- vm.roll(block.number + 1); // advance block to ensure that the index price is different
+ index_ = 1 + uint256(keccak256(abi.encodePacked(msg.sender, getNextNonce()))) % minimumPrice_;
}
// find the bucket index in array corresponding to the highest bucket price
diff --git a/tests/forge/utils/FenwickTreeInstance.sol b/tests/forge/utils/FenwickTreeInstance.sol
index 7845d6116..784d74fea 100644
--- a/tests/forge/utils/FenwickTreeInstance.sol
+++ b/tests/forge/utils/FenwickTreeInstance.sol
@@ -68,42 +68,83 @@ contract FenwickTreeInstance is DSTestPlus {
* @notice fills fenwick tree with fuzzed values and tests additions.
*/
function fuzzyFill(
- uint256 insertions_,
- uint256 amount_,
- bool trackInserts)
- external {
-
+ uint256 insertions_, // number of insertions to perform
+ uint256 amount_, // total amount to insert
+ uint256 seed_, // seed for psuedorandom number generator
+ bool trackInserts
+ ) external {
uint256 i;
uint256 amount;
+ uint256 cumulativeAmount;
// Calculate total insertions
- uint256 insertsDec = bound(insertions_, 1000, 2000);
+ insertions_ = bound(insertions_, 1000, 2000);
// Calculate total amount to insert
- uint256 totalAmount = bound(amount_, 1 * 1e18, 9_000_000_000_000_000 * 1e18);
- uint256 totalAmountDec = totalAmount;
+ amount_ = bound(amount_, 1 * 1e18, 9_000_000_000_000_000 * 1e18);
+ // Initialize and print seed for randomness
+ setRandomSeed(bound(seed_, 0, type(uint256).max - 1));
- while (totalAmountDec > 0 && insertsDec > 0) {
+ while (amount_ > 0 && insertions_ > 0) {
// Insert at random index
i = randomInRange(1, MAX_FENWICK_INDEX);
// If last iteration, insert remaining
- amount = insertsDec == 1 ? totalAmountDec : (totalAmountDec % insertsDec) * randomInRange(1_000, 1 * 1e10, true);
+ amount = insertions_ == 1 ? amount_ : (amount_ % insertions_) * randomInRange(1_000, 1 * 1e10, true);
// Update values
add(i, amount);
- totalAmountDec -= amount;
- insertsDec -= 1;
+ amount_ -= amount;
+ insertions_ -= 1;
+ cumulativeAmount += amount;
// Verify tree sum
- assertEq(deposits.treeSum(), totalAmount - totalAmountDec);
+ assertEq(deposits.treeSum(), cumulativeAmount);
if (trackInserts) inserts.push(i);
}
- assertEq(deposits.treeSum(), totalAmount);
+ assertEq(deposits.treeSum(), cumulativeAmount);
}
-}
+ /**
+ * @notice fills fenwick tree with deterministic values and tests additions.
+ */
+ function nonFuzzyFill(
+ uint256 insertions_, // number of insertions to perform
+ uint256 amount_, // total amount to insert
+ uint256 seed_, // seed for psuedorandom number generator
+ bool trackInserts
+ ) external {
+ uint256 i;
+ uint256 amount;
+ uint256 cumulativeAmount;
+
+ // Initialize and print seed for randomness
+ setRandomSeed(seed_);
+
+ while (amount_ > 0 && insertions_ > 0) {
+
+ // Insert at random index
+ i = randomInRange(1, MAX_FENWICK_INDEX);
+
+ // If last iteration, insert remaining
+ amount = insertions_ == 1 ? amount_ : (amount_ % insertions_) * randomInRange(1_000, 1 * 1e10, true);
+
+ // Update values
+ add(i, amount);
+ amount_ -= amount;
+ insertions_ -= 1;
+ cumulativeAmount += amount;
+
+ // Verify tree sum
+ assertEq(deposits.treeSum(), cumulativeAmount);
+
+ if (trackInserts) inserts.push(i);
+ }
+
+ assertEq(deposits.treeSum(), cumulativeAmount);
+ }
+}
diff --git a/tests/forge/utils/FlashloanBorrower.sol b/tests/forge/utils/FlashloanBorrower.sol
index 55e701d8f..a23db27eb 100644
--- a/tests/forge/utils/FlashloanBorrower.sol
+++ b/tests/forge/utils/FlashloanBorrower.sol
@@ -1,8 +1,11 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.14;
+import '@openzeppelin/contracts/token/ERC20/ERC20.sol';
+
import { Token } from '../utils/Tokens.sol';
+import 'src/ERC20Pool.sol';
import 'src/interfaces/pool/IERC3156FlashBorrower.sol';
import 'src/libraries/internal/Maths.sol';
@@ -32,10 +35,30 @@ contract FlashloanBorrower is IERC3156FlashBorrower {
// Example of some defi strategy which produces a fixed return
contract SomeDefiStrategy {
- Token public token;
+ ERC20 public token;
+
+ constructor(ERC20 token_) {
+ token = token_;
+ }
+
+ function makeMoney(uint256 amount_) external {
+ // step 1: take deposit from caller
+ token.transferFrom(msg.sender, address(this), amount_);
+ // step 2: earn 3.5% reward
+ uint256 reward = Maths.wmul(0.035 * 1e18, amount_);
+ // step 3: profit
+ token.transfer(msg.sender, amount_ + reward);
+ }
+}
- constructor(Token token_) {
+// Example of some defi strategy which repays to pool
+contract SomeDefiStrategyWithRepayment {
+ ERC20 public token;
+ address public pool;
+
+ constructor(ERC20 token_, address pool_) {
token = token_;
+ pool = pool_;
}
function makeMoney(uint256 amount_) external {
@@ -45,5 +68,8 @@ contract SomeDefiStrategy {
uint256 reward = Maths.wmul(0.035 * 1e18, amount_);
// step 3: profit
token.transfer(msg.sender, amount_ + reward);
+
+ // repay amount to pool
+ token.transfer(pool, 1 * 1e18);
}
}
\ No newline at end of file
diff --git a/tests/forge/utils/HeapInstance.sol b/tests/forge/utils/HeapInstance.sol
index 779961ef9..3cc509799 100644
--- a/tests/forge/utils/HeapInstance.sol
+++ b/tests/forge/utils/HeapInstance.sol
@@ -60,10 +60,10 @@ contract HeapInstance is DSTestPlus {
* @notice fills Heap with fuzzed values and tests additions.
*/
function fuzzyFill(
- uint256 inserts_,
- bool trackInserts_)
- external {
-
+ uint256 inserts_, // number of insertions to perform
+ uint256 seed_, // seed for psuedorandom number generator
+ bool trackInserts_
+ ) external {
uint256 tp;
address borrower;
@@ -71,6 +71,9 @@ contract HeapInstance is DSTestPlus {
uint256 totalInserts = bound(inserts_, 1000, 2000);
uint256 insertsDec = totalInserts;
+ // Initialize and print seed for randomness
+ setRandomSeed(bound(seed_, 0, type(uint256).max - 1));
+
while (insertsDec > 0) {
// build address and TP
@@ -91,4 +94,3 @@ contract HeapInstance is DSTestPlus {
assertEq(_heap.loans.length - 1, totalInserts);
}
}
-