Skip to content

Commit

Permalink
feat: amount as input and max
Browse files Browse the repository at this point in the history
  • Loading branch information
MathisGD committed Aug 8, 2023
1 parent 757adb5 commit 1f8df02
Show file tree
Hide file tree
Showing 4 changed files with 36 additions and 60 deletions.
24 changes: 18 additions & 6 deletions src/Blue.sol
Original file line number Diff line number Diff line change
Expand Up @@ -159,17 +159,23 @@ contract Blue is IBlue {
IERC20(market.borrowableAsset).safeTransferFrom(msg.sender, address(this), amount);
}

function withdraw(Market memory market, uint256 shares, address onBehalf, address receiver) external {
function withdraw(Market memory market, uint256 amount, address onBehalf, address receiver) external {
Id id = market.id();
require(lastUpdate[id] != 0, Errors.MARKET_NOT_CREATED);
require(shares != 0, Errors.ZERO_SHARES);
require(amount != 0, Errors.ZERO_AMOUNT);
// No need to verify that onBehalf != address(0) thanks to the authorization check.
require(receiver != address(0), Errors.ZERO_ADDRESS);
require(_isSenderAuthorized(onBehalf), Errors.UNAUTHORIZED);

_accrueInterests(market, id);

uint256 amount = shares.toAssetsDown(totalSupply[id], totalSupplyShares[id]);
uint256 shares;
if (amount == type(uint256).max) {
shares = supplyShares[id][onBehalf];
amount = shares.toAssetsDown(totalSupply[id], totalSupplyShares[id]);
} else {
shares = amount.toSharesUp(totalSupply[id], totalSupplyShares[id]);
}

supplyShares[id][onBehalf] -= shares;
totalSupplyShares[id] -= shares;
Expand Down Expand Up @@ -208,15 +214,21 @@ contract Blue is IBlue {
IERC20(market.borrowableAsset).safeTransfer(receiver, amount);
}

function repay(Market memory market, uint256 shares, address onBehalf, bytes calldata data) external {
function repay(Market memory market, uint256 amount, address onBehalf, bytes calldata data) external {
Id id = market.id();
require(lastUpdate[id] != 0, Errors.MARKET_NOT_CREATED);
require(shares != 0, Errors.ZERO_SHARES);
require(amount != 0, Errors.ZERO_AMOUNT);
require(onBehalf != address(0), Errors.ZERO_ADDRESS);

_accrueInterests(market, id);

uint256 amount = shares.toAssetsUp(totalBorrow[id], totalBorrowShares[id]);
uint256 shares;
if (amount == type(uint256).max) {
shares = borrowShares[id][onBehalf];
amount = shares.toAssetsUp(totalBorrow[id], totalBorrowShares[id]);
} else {
shares = amount.toSharesDown(totalBorrow[id], totalBorrowShares[id]);
}

borrowShares[id][onBehalf] -= shares;
totalBorrowShares[id] -= shares;
Expand Down
2 changes: 0 additions & 2 deletions src/libraries/Errors.sol
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,6 @@ library Errors {

string internal constant ZERO_AMOUNT = "zero amount";

string internal constant ZERO_SHARES = "zero shares";

string internal constant ZERO_ADDRESS = "zero address";

string internal constant UNAUTHORIZED = "unauthorized";
Expand Down
63 changes: 16 additions & 47 deletions test/forge/Blue.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -365,34 +365,34 @@ contract BlueTest is
borrowableAsset.setBalance(address(blue), blue.totalSupply(id));
}

function testWithdrawShares(uint256 amountLent, uint256 sharesWithdrawn, uint256 amountBorrowed, address receiver)
function testWithdraw(uint256 amountLent, uint256 amountWithdrawn, uint256 amountBorrowed, address receiver)
public
{
vm.assume(receiver != BORROWER);
vm.assume(receiver != address(0));
vm.assume(receiver != address(blue));
vm.assume(receiver != address(this));
sharesWithdrawn = bound(sharesWithdrawn, 1, 2 ** 64);
amountWithdrawn = bound(amountWithdrawn, 1, 2 ** 64);

_testWithdrawCommon(amountLent);
amountBorrowed = bound(amountBorrowed, 1, blue.totalSupply(id));
blue.borrow(market, amountBorrowed, BORROWER, BORROWER);

uint256 totalSupplyBefore = blue.totalSupply(id);
uint256 supplySharesBefore = blue.supplyShares(id, address(this));
uint256 amountWithdrawn = sharesWithdrawn.toAssetsDown(blue.totalSupply(id), blue.totalSupplyShares(id));
uint256 sharesWithdrawn = amountWithdrawn.toSharesUp(blue.totalSupply(id), blue.totalSupplyShares(id));

if (sharesWithdrawn > blue.supplyShares(id, address(this))) {
vm.expectRevert(stdError.arithmeticError);
blue.withdraw(market, sharesWithdrawn, address(this), receiver);
blue.withdraw(market, amountWithdrawn, address(this), receiver);
return;
} else if (amountWithdrawn > totalSupplyBefore - amountBorrowed) {
vm.expectRevert(bytes(Errors.INSUFFICIENT_LIQUIDITY));
blue.withdraw(market, sharesWithdrawn, address(this), receiver);
blue.withdraw(market, amountWithdrawn, address(this), receiver);
return;
}

blue.withdraw(market, sharesWithdrawn, address(this), receiver);
blue.withdraw(market, amountWithdrawn, address(this), receiver);

assertEq(blue.supplyShares(id, address(this)), supplySharesBefore - sharesWithdrawn, "supply share");
assertEq(borrowableAsset.balanceOf(receiver), amountWithdrawn, "receiver balance");
Expand All @@ -403,28 +403,13 @@ contract BlueTest is
);
}

function testWithdrawAmount(uint256 amountLent, uint256 exactAmountWithdrawn) public {
_testWithdrawCommon(amountLent);

uint256 totalSupplyBefore = blue.totalSupply(id);
uint256 supplySharesBefore = blue.supplyShares(id, address(this));
exactAmountWithdrawn = bound(
exactAmountWithdrawn, 1, supplySharesBefore.toAssetsDown(blue.totalSupply(id), blue.totalSupplyShares(id))
);
uint256 sharesWithdrawn = blue.withdrawAmount(market, exactAmountWithdrawn, address(this), address(this));

assertEq(blue.supplyShares(id, address(this)), supplySharesBefore - sharesWithdrawn, "supply share");
assertEq(borrowableAsset.balanceOf(address(this)), exactAmountWithdrawn, "this balance");
assertEq(borrowableAsset.balanceOf(address(blue)), totalSupplyBefore - exactAmountWithdrawn, "blue balance");
}

function testWithdrawAll(uint256 amountLent) public {
_testWithdrawCommon(amountLent);

uint256 totalSupplyBefore = blue.totalSupply(id);
uint256 amountWithdrawn =
blue.supplyShares(id, address(this)).toAssetsDown(blue.totalSupply(id), blue.totalSupplyShares(id));
blue.withdraw(market, blue.supplyShares(id, address(this)), address(this), address(this));
blue.withdraw(market, type(uint256).max, address(this), address(this));

assertEq(blue.supplyShares(id, address(this)), 0, "supply share");
assertEq(borrowableAsset.balanceOf(address(this)), amountWithdrawn, "this balance");
Expand All @@ -445,44 +430,31 @@ contract BlueTest is
);
}

function testRepayShares(uint256 amountBorrowed, uint256 sharesRepaid, address onBehalf) public {
function testRepay(uint256 amountBorrowed, uint256 amountRepaid, address onBehalf) public {
vm.assume(onBehalf != address(0));
vm.assume(onBehalf != address(blue));
_testRepayCommon(amountBorrowed, onBehalf);

uint256 thisBalanceBefore = borrowableAsset.balanceOf(address(this));
uint256 borrowSharesBefore = blue.borrowShares(id, onBehalf);
sharesRepaid = bound(sharesRepaid, 1, borrowSharesBefore);
amountRepaid =
bound(amountRepaid, 1, borrowSharesBefore.toAssetsDown(blue.totalBorrow(id), blue.totalBorrowShares(id)));

uint256 amountRepaid = sharesRepaid.toAssetsUp(blue.totalBorrow(id), blue.totalBorrowShares(id));
blue.repay(market, sharesRepaid, onBehalf, hex"");
uint256 sharesRepaid = amountRepaid.toSharesDown(blue.totalBorrow(id), blue.totalBorrowShares(id));
blue.repay(market, amountRepaid, onBehalf, hex"");

assertEq(blue.borrowShares(id, onBehalf), borrowSharesBefore - sharesRepaid, "borrow share");
assertEq(borrowableAsset.balanceOf(address(this)), thisBalanceBefore - amountRepaid, "this balance");
assertEq(borrowableAsset.balanceOf(address(blue)), amountRepaid, "blue balance");
}

function testRepayAmount(uint256 amountBorrowed, uint256 exactAmountRepaid) public {
_testRepayCommon(amountBorrowed, address(this));

uint256 thisBalanceBefore = borrowableAsset.balanceOf(address(this));
uint256 borrowSharesBefore = blue.borrowShares(id, address(this));
exactAmountRepaid =
bound(exactAmountRepaid, 1, borrowSharesBefore.toAssetsUp(blue.totalBorrow(id), blue.totalBorrowShares(id)));
uint256 sharesRepaid = blue.repayAmount(market, exactAmountRepaid, address(this), hex"");

assertEq(blue.borrowShares(id, address(this)), borrowSharesBefore - sharesRepaid, "borrow share");
assertEq(borrowableAsset.balanceOf(address(this)), thisBalanceBefore - exactAmountRepaid, "this balance");
assertEq(borrowableAsset.balanceOf(address(blue)), exactAmountRepaid, "blue balance");
}

function testRepayAll(uint256 amountBorrowed) public {
_testRepayCommon(amountBorrowed, address(this));

uint256 amountRepaid =
blue.borrowShares(id, address(this)).toAssetsUp(blue.totalBorrow(id), blue.totalBorrowShares(id));
borrowableAsset.setBalance(address(this), amountRepaid);
blue.repay(market, blue.borrowShares(id, address(this)), address(this), hex"");
blue.repay(market, type(uint256).max, address(this), hex"");

assertEq(blue.borrowShares(id, address(this)), 0, "borrow share");
assertEq(borrowableAsset.balanceOf(address(this)), 0, "this balance");
Expand Down Expand Up @@ -717,13 +689,13 @@ contract BlueTest is
vm.expectRevert(bytes(Errors.ZERO_AMOUNT));
blue.supply(market, 0, address(this), hex"");

vm.expectRevert(bytes(Errors.ZERO_SHARES));
vm.expectRevert(bytes(Errors.ZERO_AMOUNT));
blue.withdraw(market, 0, address(this), address(this));

vm.expectRevert(bytes(Errors.ZERO_AMOUNT));
blue.borrow(market, 0, address(this), address(this));

vm.expectRevert(bytes(Errors.ZERO_SHARES));
vm.expectRevert(bytes(Errors.ZERO_AMOUNT));
blue.repay(market, 0, address(this), hex"");

vm.expectRevert(bytes(Errors.ZERO_AMOUNT));
Expand Down Expand Up @@ -931,10 +903,7 @@ contract BlueTest is
assertGt(blue.borrowShares(market.id(), address(this)), 0, "no borrow");

blue.repay(
market,
blue.borrowShares(id, address(this)),
address(this),
abi.encode(this.testFlashActions.selector, abi.encode(amount))
market, type(uint256).max, address(this), abi.encode(this.testFlashActions.selector, abi.encode(amount))
);
assertEq(blue.collateral(market.id(), address(this)), 0, "no withdraw collateral");
}
Expand Down
7 changes: 2 additions & 5 deletions test/hardhat/Blue.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -132,16 +132,13 @@ describe("Blue", () => {
let amount = BigNumber.WAD.mul(1 + Math.floor(random() * 100));

if (random() < 2 / 3) {
const totalSupply = await blue.totalSupply(id);
const totalSupplyShares = await blue.totalSupplyShares(id);
Promise.all([
blue.connect(user).supply(market, amount, user.address, []),
blue.connect(user).withdraw(market, amount.mul(totalSupplyShares.add(BigNumber.WAD)).div(totalSupply.add(1)).div(2), user.address, user.address),
blue.connect(user).withdraw(market, amount.div(2), user.address, user.address),
]);
} else {
const totalSupply = await blue.totalSupply(id);
const totalBorrow = await blue.totalBorrow(id);
const totalBorrowShares = await blue.totalBorrowShares(id);
const liquidity = BigNumber.from(totalSupply).sub(BigNumber.from(totalBorrow));

amount = BigNumber.min(amount, BigNumber.from(liquidity).div(2));
Expand All @@ -150,7 +147,7 @@ describe("Blue", () => {
Promise.all([
blue.connect(user).supplyCollateral(market, amount, user.address, []),
blue.connect(user).borrow(market, amount.div(2), user.address, user.address),
blue.connect(user).repay(market, amount.mul(totalBorrowShares.add(BigNumber.WAD)).div(totalBorrow.add(1)).div(4), user.address, []),
blue.connect(user).repay(market, amount.div(4), user.address, []),
blue.connect(user).withdrawCollateral(market, amount.div(8), user.address, user.address),
]);
}
Expand Down

0 comments on commit 1f8df02

Please sign in to comment.