-
Notifications
You must be signed in to change notification settings - Fork 53
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
d131ed0
commit f566d0b
Showing
3 changed files
with
296 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,182 @@ | ||
. | ||
└── transferOwnership(address newOwner) external onlyOwner/ | ||
├── when msg.sender not owner/ | ||
│ └── revert with NOT_OWNER | ||
└── when msg.sender is owner/ | ||
└── should set owner to newOwner | ||
. | ||
└── enableIrm(IIrm irm) external onlyOwner/ | ||
├── when msg.sender not owner/ | ||
│ └── revert with NOT_OWNER | ||
└── when msg.sender is owner/ | ||
└── should set isIrmEnabled[irm] to true | ||
. | ||
└── enableLltv(uint256 lltv) external onlyOwner/ | ||
├── when msg.sender not owner/ | ||
│ └── revert with NOT_OWNER | ||
└── when msg.sender is owner/ | ||
├── when lltv >= WAD/ | ||
│ └── revert with LLTV_TOO_HIGH | ||
└── when lltv < WAD/ | ||
└── should set isLltvEnabled[lltv] to true | ||
. | ||
└── setFee(Market memory market, uint256 newFee) external onlyOwner/ | ||
├── when msg.sender not owner/ | ||
│ └── revert with NOT_OWNER | ||
└── when msg.sender is owner/ | ||
├── when market is not created/ | ||
│ └── revert with MARKET_NOT_CREATED | ||
└── when market is created/ | ||
├── when newFee > MAX_FEE/ | ||
│ └── revert with MAX_FEE_EXCEEDED | ||
└── when newFee <= MAX_FEE/ | ||
└── should set fee[id] to newFee | ||
. | ||
└── setFeeRecipient(address recipient) external onlyOwner/ | ||
├── when msg.sender not owner/ | ||
│ └── revert with NOT_OWNER | ||
└── when msg.sender is owner/ | ||
└── should set feeRecipient to recipient | ||
. | ||
└── createMarket(Market memory market) external/ | ||
├── when irm is not enabled/ | ||
│ └── revert with IRM_NOT_ENABLED | ||
└── when irm is enabled/ | ||
├── when lltv is not enabled/ | ||
│ └── revert with LLTV_NOT_ENABLED | ||
└── when lltv is enabled/ | ||
├── when market is already created/ | ||
│ └── revert with MARKET_CREATED | ||
└── when market is not already created/ | ||
└── it should create market | ||
|
||
. | ||
└── supply(Market calldata market, uint256 amount, address onBehalf) external/ | ||
├── when market is not created/ | ||
│ └── revert with MARKET_NOT_CREATED | ||
└── when the amount to supply is zero/ | ||
└── revert with ZERO_AMOUNT | ||
└── when market is created/ | ||
├── when the amount to supply is zero/ | ||
│ └── revert with ZERO_AMOUNT | ||
└── when the amount to supply is not zero/ | ||
├── it should add amount.toSharesDown(totalSupply[id], totalSupplyShares[id]) to supplyShare[id][onBehalf] | ||
├── it should add amount.toSharesDown(totalSupply[id], totalSupplyShares[id]) to totalSupplyShares[id] | ||
├── it should add amount to totalSupply[id] | ||
└── it should make the ERC-20 transfer (checks on blue and supplier balances) | ||
. | ||
└── withdraw(Market memory market, uint256 amount, address onBehalf) external/ | ||
├── when market is not created/ | ||
│ └── revert with MARKET_NOT_CREATED | ||
└── when market is created/ | ||
├── when the amount to withdraw is zero/ | ||
│ └── revert with ZERO_AMOUNT | ||
└── when the amount to withdraw is not zero/ | ||
├── when not sender and not approved/ | ||
│ └── revert with MANAGER_NOT_APPROVED | ||
└── when sender or approved/ | ||
├── when totalBorrow > totalSupply/ | ||
│ └── revert with INSUFFICIENT_LIQUIDITY | ||
└── when totalBorrow <= totalSupply/ | ||
├── it should remove amount.toSharesUp(totalSupply[id], totalSupplyShares[id]) to supplyShare[id][onBehalf] | ||
├── it should remove amount.toSharesUp(totalSupply[id], totalSupplyShares[id]) to totalSupplyShares[id] | ||
├── it should remouve amount from totalSupply[id] | ||
└── it should make the ERC-20 transfer (checks on blue and withdrawer balances) | ||
. | ||
└── borrow(Market memory market, uint256 amount, address onBehalf) external/ | ||
├── when market is not created/ | ||
│ └── revert with MARKET_NOT_CREATED | ||
└── when market is created/ | ||
├── when the amount to borrow is zero/ | ||
│ └── revert with ZERO_AMOUNT | ||
└── when the amount to borrow is not zero/ | ||
├── when not sender and not approved/ | ||
│ └── revert with MANAGER_NOT_APPROVED | ||
└── when sender or approved/ | ||
├── when position not healthy/ | ||
│ └── revert with INSUFFICIENT_COLLATERAL | ||
└── when position healthy/ | ||
├── when totalBorrow > totalSupply/ | ||
│ └── revert with INSUFFICIENT_LIQUIDITY | ||
└── when totalBorrow <= totalSupply/ | ||
├── it should add amount.toSharesUp(totalBorrow[id], totalBorrowShares[id]) to borrowShare[id][onBehalf] | ||
├── it should add amount.toSharesUp(totalBorrow[id], totalBorrowShares[id]) to totalBorrowShares[id] | ||
├── it should add amount to totalBorrow[id] | ||
└── it should make the ERC-20 transfer (checks on blue and borrower balances) | ||
. | ||
└── repay(Market memory market, uint256 amount, address onBehalf) external/ | ||
├── when market is not created/ | ||
│ └── revert with MARKET_NOT_CREATED | ||
└── when market is created/ | ||
├── when the amount to repay is zero/ | ||
│ └── revert with ZERO_AMOUNT | ||
└── when the amount to repay is not zero/ | ||
├── it should remove amount.toSharesDown(totalBorrow[id], totalBorrowShares[id]) from borrowShare[id][onBehalf] | ||
├── it should remove amount.toSharesDown(totalBorrow[id], totalBorrowShares[id]) from totalBorrowShares[id], | ||
├── it should remove amount from totalBorrow[id] | ||
└── it should make the ERC-20 transfer (checks on blue and repayer balances) | ||
. | ||
└── supplyCollateral(Market memory market, uint256 amount, address onBehalf) external/ | ||
├── when market is not created/ | ||
│ └── revert with MARKET_NOT_CREATED | ||
└── when market is created/ | ||
├── when the amount to supply is zero/ | ||
│ └── revert with ZERO_AMOUNT | ||
└── when the amount to supply is not zero/ | ||
├── it should add amount to collateral[id][onBehalf] | ||
└── it should make the ERC-20 transfer (checks on blue and supplier balances) | ||
. | ||
└── withdrawCollateral(Market memory market, uint256 amount, address onBehalf) external/ | ||
├── when market is not created/ | ||
│ └── revert with MARKET_NOT_CREATED | ||
└── when market is created/ | ||
├── when the amount to withdraw is zero/ | ||
│ └── revert with ZERO_AMOUNT | ||
└── when the amount to withdraw is not zero/ | ||
├── when not sender and not approved/ | ||
│ └── revert with MANAGER_NOT_APPROVED | ||
└── when sender or approved/ | ||
├── when position not healthy/ | ||
│ └── revert with INSUFFICIENT_COLLATERAL | ||
└── when position healthy/ | ||
├── it should remove amount from collateral[id][onBehalf] | ||
└── it should make the ERC-20 transfer (checks on blue and withdrawer balances) | ||
. | ||
└── liquidate(Market memory market, address borrower, address onBehalf) external/ | ||
├── when market is not created/ | ||
│ └── revert with MARKET_NOT_CREATED | ||
└── when market is created/ | ||
├── when the amount to seized is zero/ | ||
│ └── revert with ZERO_AMOUNT | ||
└── when the amount to seized is not zero/ | ||
├── when position is healthy/ | ||
│ └── revert with HEALTHY_POSITION | ||
└── when the position not healthy/ | ||
├── it should compute repaid = seized.mulWadUp(collateralPrice).divWadUp(incentive).divWadUp(borrowablePrice); | ||
├── it should remove repaid.toSharesDown(totalBorrow[id], totalBorrowShares[id]) from borrowShare[id][borrower] | ||
├── it should remove repaid.toSharesDown(totalBorrow[id], totalBorrowShares[id]) from totalBorrowShares[id] | ||
├── it should remove repaid from totalBorrow[id] | ||
├── it should remove seized from collateral[id][borrower] | ||
├── it should make the ERC-20 transfers (checks on blue and liquidator balances) | ||
└── if after the liquidation the borrower's collateral is 0/ | ||
└── it should realise bad debt/ | ||
├── it should compute badDebt = borrowShare[id][borrower].toAssetsUp(totalBorrow[id], totalBorrowShares[id]) | ||
├── it should remove bad debt from totalSupply[id] | ||
├── it should remove bad debt from totalBorrow[id] | ||
├── it should remove borrowShare[id][borrower] from totalBorrowShares[id] | ||
└── it should set borrowShare[id][borrower] to 0 | ||
. | ||
└── setApproval(address manager, bool isAllowed) external/ | ||
└── should set isApproved[msg.sender][manager] to isAllowed | ||
. | ||
└── _accrueInterests(Market memory market, Id id) internal/ | ||
├── when marketTotalBorrow is 0/ | ||
│ └── it should just set lastUpdate to block.timestamp | ||
└── when marketTotalBorrow is not 0/ | ||
├── when lastUpdate = block.timestamp/ | ||
│ └── it should just set lastUpdate to block.timestamp | ||
└── when lastUpdate doesn't equal block.timestamp/ | ||
├── it should add accruedInterests to totalBorrow | ||
├── it should add accruedInterests to totalSupply | ||
└── when fee[id] != 0/ | ||
├── it should add accruedInterests.mulWadDown(fee[id]) to feeAmount | ||
├── it should add feeAmount.mulDivDown(totalSupplyShares[id], totalSupply[id] - feeAmount) to supplyShare[id][feeRecipient] | ||
└── it should add feeAmount.mulDivDown(totalSupplyShares[id], totalSupply[id] - feeAmount) to totalSupplyShares[id] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
// SPDX-License-Identifier: UNLICENSED | ||
pragma solidity 0.8.20; | ||
|
||
import "forge-std/Test.sol"; | ||
import "forge-std/console.sol"; | ||
|
||
import "src/Blue.sol"; | ||
import {ERC20Mock as ERC20} from "src/mocks/ERC20Mock.sol"; | ||
import {OracleMock as Oracle} from "src/mocks/OracleMock.sol"; | ||
import {IrmMock as Irm} from "src/mocks/IrmMock.sol"; | ||
|
||
contract BlueBaseTest is Test { | ||
using FixedPointMathLib for uint256; | ||
|
||
address private constant BORROWER = address(1234); | ||
address private constant LIQUIDATOR = address(5678); | ||
uint256 private constant LLTV = 0.8 ether; | ||
address private constant OWNER = address(0xdead); | ||
|
||
Blue private blue; | ||
ERC20 private borrowableAsset; | ||
ERC20 private collateralAsset; | ||
Oracle private borrowableOracle; | ||
Oracle private collateralOracle; | ||
Irm private irm; | ||
Market public market; | ||
Id public id; | ||
|
||
function setUp() public { | ||
// Create Blue. | ||
blue = new Blue(OWNER); | ||
|
||
// List a market. | ||
borrowableAsset = new ERC20("borrowable", "B", 18); | ||
collateralAsset = new ERC20("collateral", "C", 18); | ||
borrowableOracle = new Oracle(); | ||
collateralOracle = new Oracle(); | ||
|
||
irm = new Irm(blue); | ||
market = Market( | ||
IERC20(address(borrowableAsset)), | ||
IERC20(address(collateralAsset)), | ||
borrowableOracle, | ||
collateralOracle, | ||
irm, | ||
LLTV | ||
); | ||
id = Id.wrap(keccak256(abi.encode(market))); | ||
|
||
vm.startPrank(OWNER); | ||
blue.enableIrm(irm); | ||
blue.enableLltv(LLTV); | ||
blue.createMarket(market); | ||
vm.stopPrank(); | ||
|
||
// We set the price of the borrowable asset to zero so that borrowers | ||
// don't need to deposit any collateral. | ||
borrowableOracle.setPrice(0); | ||
collateralOracle.setPrice(1e18); | ||
|
||
borrowableAsset.approve(address(blue), type(uint256).max); | ||
collateralAsset.approve(address(blue), type(uint256).max); | ||
vm.startPrank(BORROWER); | ||
borrowableAsset.approve(address(blue), type(uint256).max); | ||
collateralAsset.approve(address(blue), type(uint256).max); | ||
vm.stopPrank(); | ||
vm.startPrank(LIQUIDATOR); | ||
borrowableAsset.approve(address(blue), type(uint256).max); | ||
collateralAsset.approve(address(blue), type(uint256).max); | ||
vm.stopPrank(); | ||
} | ||
|
||
function netWorth(address user) internal view returns (uint256) { | ||
uint256 collateralAssetValue = collateralAsset.balanceOf(user).mulWadDown(collateralOracle.price()); | ||
uint256 borrowableAssetValue = borrowableAsset.balanceOf(user).mulWadDown(borrowableOracle.price()); | ||
return collateralAssetValue + borrowableAssetValue; | ||
} | ||
|
||
function supplyBalance(address user) internal view returns (uint256) { | ||
uint256 supplyShares = blue.supplyShare(id, user); | ||
if (supplyShares == 0) return 0; | ||
|
||
uint256 totalShares = blue.totalSupplyShares(id); | ||
uint256 totalSupply = blue.totalSupply(id); | ||
return supplyShares.divWadDown(totalShares).mulWadDown(totalSupply); | ||
} | ||
|
||
function borrowBalance(address user) internal view returns (uint256) { | ||
uint256 borrowerShares = blue.borrowShare(id, user); | ||
if (borrowerShares == 0) return 0; | ||
|
||
uint256 totalShares = blue.totalBorrowShares(id); | ||
uint256 totalBorrow = blue.totalBorrow(id); | ||
return borrowerShares.divWadUp(totalShares).mulWadUp(totalBorrow); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
// SPDX-License-Identifier: UNLICENSED | ||
pragma solidity 0.8.20; | ||
|
||
import "test/forge/BlueBase.t.sol"; | ||
|
||
contract BlueBaseTest is BlueBaseTest { | ||
function testSupplyUnknownMarket(Market memory marketFuzz) public { | ||
vm.assume(neq(marketFuzz, market)); | ||
|
||
vm.expectRevert("unknown market"); | ||
blue.supply(marketFuzz, 1, address(this)); | ||
} | ||
|
||
function testSupplyUnknownMarket() public { | ||
vm.expectRevert("zero amount"); | ||
blue.supply(market, 0, address(this)); | ||
} | ||
|
||
function testSupply(uint256 amount) {} | ||
|
||
function testSupplyOnBehalf(uint256 amount, address onBehalf) {} | ||
} |