Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add receiver #191

Merged
merged 9 commits into from
Jul 31, 2023
12 changes: 6 additions & 6 deletions src/Blue.sol
MathisGD marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ contract Blue is IFlashLender {
market.borrowableAsset.safeTransferFrom(msg.sender, address(this), amount);
}

function withdraw(Market memory market, uint256 amount, address onBehalf) external {
function withdraw(Market memory market, uint256 amount, address onBehalf, address receiver) external {
makcandrov marked this conversation as resolved.
Show resolved Hide resolved
Id id = market.id();
require(lastUpdate[id] != 0, Errors.MARKET_NOT_CREATED);
require(amount != 0, Errors.ZERO_AMOUNT);
Expand All @@ -145,12 +145,12 @@ contract Blue is IFlashLender {

require(totalBorrow[id] <= totalSupply[id], Errors.INSUFFICIENT_LIQUIDITY);

market.borrowableAsset.safeTransfer(msg.sender, amount);
market.borrowableAsset.safeTransfer(receiver, amount);
}

// Borrow management.

function borrow(Market memory market, uint256 amount, address onBehalf) external {
function borrow(Market memory market, uint256 amount, address onBehalf, address receiver) external {
Id id = market.id();
require(lastUpdate[id] != 0, Errors.MARKET_NOT_CREATED);
require(amount != 0, Errors.ZERO_AMOUNT);
Expand All @@ -167,7 +167,7 @@ contract Blue is IFlashLender {
require(_isHealthy(market, id, onBehalf), Errors.INSUFFICIENT_COLLATERAL);
require(totalBorrow[id] <= totalSupply[id], Errors.INSUFFICIENT_LIQUIDITY);

market.borrowableAsset.safeTransfer(msg.sender, amount);
market.borrowableAsset.safeTransfer(receiver, amount);
}

function repay(Market memory market, uint256 amount, address onBehalf, bytes calldata data) external {
Expand Down Expand Up @@ -207,7 +207,7 @@ contract Blue is IFlashLender {
market.collateralAsset.safeTransferFrom(msg.sender, address(this), amount);
}

function withdrawCollateral(Market memory market, uint256 amount, address onBehalf) external {
function withdrawCollateral(Market memory market, uint256 amount, address onBehalf, address receiver) external {
Id id = market.id();
require(lastUpdate[id] != 0, Errors.MARKET_NOT_CREATED);
require(amount != 0, Errors.ZERO_AMOUNT);
Expand All @@ -219,7 +219,7 @@ contract Blue is IFlashLender {

require(_isHealthy(market, id, onBehalf), Errors.INSUFFICIENT_COLLATERAL);

market.collateralAsset.safeTransfer(msg.sender, amount);
market.collateralAsset.safeTransfer(receiver, amount);
}

// Liquidation.
Expand Down
120 changes: 82 additions & 38 deletions test/forge/Blue.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -271,7 +271,7 @@ contract BlueTest is
blue.supply(market, amountLent, address(this), hex"");

vm.prank(BORROWER);
blue.borrow(market, amountBorrowed, BORROWER);
blue.borrow(market, amountBorrowed, BORROWER, BORROWER);

uint256 totalSupplyBefore = blue.totalSupply(id);
uint256 totalSupplySharesBefore = blue.totalSupplyShares(id);
Expand All @@ -281,7 +281,7 @@ contract BlueTest is

collateralAsset.setBalance(address(this), 1);
blue.supplyCollateral(market, 1, address(this), hex"");
blue.withdrawCollateral(market, 1, address(this));
blue.withdrawCollateral(market, 1, address(this), address(this));

uint256 totalSupplyAfter = blue.totalSupply(id);
vm.assume(totalSupplyAfter > totalSupplyBefore);
Expand Down Expand Up @@ -321,26 +321,37 @@ contract BlueTest is
borrowableAsset.setBalance(address(this), amountLent);
blue.supply(market, amountLent, address(this), hex"");

if (amountBorrowed == 0) {
blue.borrow(market, amountBorrowed, address(this));
return;
}

if (amountBorrowed > amountLent) {
vm.prank(BORROWER);
vm.expectRevert(bytes(Errors.INSUFFICIENT_LIQUIDITY));
blue.borrow(market, amountBorrowed, BORROWER);
blue.borrow(market, amountBorrowed, BORROWER, BORROWER);
return;
}

vm.prank(BORROWER);
blue.borrow(market, amountBorrowed, BORROWER);
blue.borrow(market, amountBorrowed, BORROWER, BORROWER);
MerlinEgalite marked this conversation as resolved.
Show resolved Hide resolved

assertEq(blue.borrowShare(id, BORROWER), amountBorrowed * SharesMath.VIRTUAL_SHARES, "borrow share");
assertEq(borrowableAsset.balanceOf(BORROWER), amountBorrowed, "BORROWER balance");
assertEq(borrowableAsset.balanceOf(address(blue)), amountLent - amountBorrowed, "blue balance");
}

function testBorrowReceiver(uint256 amountLent, uint256 amountBorrowed, address receiver) public {
MathisGD marked this conversation as resolved.
Show resolved Hide resolved
vm.assume(receiver != address(blue));
amountLent = bound(amountLent, 1, 2 ** 64);
amountBorrowed = bound(amountBorrowed, 1, amountLent);

borrowableAsset.setBalance(address(this), amountLent);
blue.supply(market, amountLent, address(this), hex"");

vm.prank(BORROWER);
blue.borrow(market, amountBorrowed, BORROWER, receiver);

assertEq(blue.borrowShare(id, BORROWER), amountBorrowed * SharesMath.VIRTUAL_SHARES, "borrow share");
assertEq(borrowableAsset.balanceOf(receiver), amountBorrowed, "receiver balance");
assertEq(borrowableAsset.balanceOf(address(blue)), amountLent - amountBorrowed, "blue balance");
}

function testWithdraw(uint256 amountLent, uint256 amountWithdrawn, uint256 amountBorrowed) public {
amountLent = bound(amountLent, 1, 2 ** 64);
amountWithdrawn = bound(amountWithdrawn, 1, 2 ** 64);
Expand All @@ -351,19 +362,19 @@ contract BlueTest is
blue.supply(market, amountLent, address(this), hex"");

vm.prank(BORROWER);
blue.borrow(market, amountBorrowed, BORROWER);
blue.borrow(market, amountBorrowed, BORROWER, BORROWER);

if (amountWithdrawn > amountLent - amountBorrowed) {
if (amountWithdrawn > amountLent) {
vm.expectRevert();
} else {
vm.expectRevert(bytes(Errors.INSUFFICIENT_LIQUIDITY));
}
blue.withdraw(market, amountWithdrawn, address(this));
blue.withdraw(market, amountWithdrawn, address(this), address(this));
return;
}

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

assertApproxEqAbs(
blue.supplyShare(id, address(this)),
Expand All @@ -377,6 +388,25 @@ contract BlueTest is
);
}

function testWithdrawReceiver(uint256 amountLent, uint256 amountWithdrawn, address receiver) public {
Rubilmax marked this conversation as resolved.
Show resolved Hide resolved
vm.assume(receiver != address(blue));
amountLent = bound(amountLent, 1, 2 ** 64);
amountWithdrawn = bound(amountWithdrawn, 1, amountLent);

borrowableAsset.setBalance(address(this), amountLent);
blue.supply(market, amountLent, address(this), hex"");
blue.withdraw(market, amountWithdrawn, address(this), receiver);

assertApproxEqAbs(
blue.supplyShare(id, address(this)),
(amountLent - amountWithdrawn) * SharesMath.VIRTUAL_SHARES,
100,
"supply share"
);
assertEq(borrowableAsset.balanceOf(receiver), amountWithdrawn, "receiver balance");
assertEq(borrowableAsset.balanceOf(address(blue)), amountLent - amountWithdrawn, "blue balance");
}

function testCollateralRequirements(
uint256 amountCollateral,
uint256 amountBorrowed,
Expand All @@ -403,11 +433,11 @@ contract BlueTest is
uint256 borrowValue = amountBorrowed.mulWadUp(priceBorrowable);
if (borrowValue == 0 || (collateralValue > 0 && borrowValue <= collateralValue.mulWadDown(LLTV))) {
vm.prank(BORROWER);
blue.borrow(market, amountBorrowed, BORROWER);
blue.borrow(market, amountBorrowed, BORROWER, BORROWER);
} else {
vm.prank(BORROWER);
vm.expectRevert(bytes(Errors.INSUFFICIENT_COLLATERAL));
blue.borrow(market, amountBorrowed, BORROWER);
blue.borrow(market, amountBorrowed, BORROWER, BORROWER);
}
}

Expand All @@ -420,7 +450,7 @@ contract BlueTest is
blue.supply(market, amountLent, address(this), hex"");

vm.startPrank(BORROWER);
blue.borrow(market, amountBorrowed, BORROWER);
blue.borrow(market, amountBorrowed, BORROWER, BORROWER);
blue.repay(market, amountRepaid, BORROWER, hex"");
vm.stopPrank();

Expand All @@ -447,7 +477,7 @@ contract BlueTest is
blue.supply(market, amountLent, address(this), hex"");

vm.prank(onBehalf);
blue.borrow(market, amountBorrowed, onBehalf);
blue.borrow(market, amountBorrowed, onBehalf, onBehalf);

blue.repay(market, amountRepaid, onBehalf, hex"");

Expand Down Expand Up @@ -482,17 +512,31 @@ contract BlueTest is

if (amountWithdrawn > amountDeposited) {
vm.expectRevert(stdError.arithmeticError);
blue.withdrawCollateral(market, amountWithdrawn, address(this));
blue.withdrawCollateral(market, amountWithdrawn, address(this), address(this));
return;
}

blue.withdrawCollateral(market, amountWithdrawn, address(this));
blue.withdrawCollateral(market, amountWithdrawn, address(this), address(this));

assertEq(blue.collateral(id, address(this)), amountDeposited - amountWithdrawn, "this collateral");
assertEq(collateralAsset.balanceOf(address(this)), amountWithdrawn, "this balance");
assertEq(collateralAsset.balanceOf(address(blue)), amountDeposited - amountWithdrawn, "blue balance");
}

function testWithdrawCollateral(uint256 amountDeposited, uint256 amountWithdrawn, address receiver) public {
vm.assume(receiver != address(blue));
amountDeposited = bound(amountDeposited, 1, 2 ** 64);
amountWithdrawn = bound(amountWithdrawn, 1, amountDeposited);

collateralAsset.setBalance(address(this), amountDeposited);
blue.supplyCollateral(market, amountDeposited, address(this), hex"");
blue.withdrawCollateral(market, amountWithdrawn, address(this), receiver);

assertEq(blue.collateral(id, address(this)), amountDeposited - amountWithdrawn, "this collateral");
assertEq(collateralAsset.balanceOf(receiver), amountWithdrawn, "receiver balance");
assertEq(collateralAsset.balanceOf(address(blue)), amountDeposited - amountWithdrawn, "blue balance");
}

function testLiquidate(uint256 amountLent) public {
borrowableOracle.setPrice(1e18);
amountLent = bound(amountLent, 1000, 2 ** 64);
Expand All @@ -514,7 +558,7 @@ contract BlueTest is
// Borrow
vm.startPrank(BORROWER);
blue.supplyCollateral(market, amountCollateral, BORROWER, hex"");
blue.borrow(market, amountBorrowed, BORROWER);
blue.borrow(market, amountBorrowed, BORROWER, BORROWER);
vm.stopPrank();

// Price change
Expand Down Expand Up @@ -558,7 +602,7 @@ contract BlueTest is
// Borrow
vm.startPrank(BORROWER);
blue.supplyCollateral(market, amountCollateral, BORROWER, hex"");
blue.borrow(market, amountBorrowed, BORROWER);
blue.borrow(market, amountBorrowed, BORROWER, BORROWER);
vm.stopPrank();

// Price change
Expand Down Expand Up @@ -613,10 +657,10 @@ contract BlueTest is
blue.supply(marketFuzz, 1, address(this), hex"");

vm.expectRevert("unknown market");
blue.withdraw(marketFuzz, 1, address(this));
blue.withdraw(marketFuzz, 1, address(this), address(this));

vm.expectRevert("unknown market");
blue.borrow(marketFuzz, 1, address(this));
blue.borrow(marketFuzz, 1, address(this), address(this));

vm.expectRevert("unknown market");
blue.repay(marketFuzz, 1, address(this), hex"");
Expand All @@ -625,7 +669,7 @@ contract BlueTest is
blue.supplyCollateral(marketFuzz, 1, address(this), hex"");

vm.expectRevert("unknown market");
blue.withdrawCollateral(marketFuzz, 1, address(this));
blue.withdrawCollateral(marketFuzz, 1, address(this), address(this));

vm.expectRevert("unknown market");
blue.liquidate(marketFuzz, address(0), 1, hex"");
Expand All @@ -636,10 +680,10 @@ contract BlueTest is
blue.supply(market, 0, address(this), hex"");

vm.expectRevert("zero amount");
blue.withdraw(market, 0, address(this));
blue.withdraw(market, 0, address(this), address(this));

vm.expectRevert("zero amount");
blue.borrow(market, 0, address(this));
blue.borrow(market, 0, address(this), address(this));

vm.expectRevert("zero amount");
blue.repay(market, 0, address(this), hex"");
Expand All @@ -648,7 +692,7 @@ contract BlueTest is
blue.supplyCollateral(market, 0, address(this), hex"");

vm.expectRevert("zero amount");
blue.withdrawCollateral(market, 0, address(this));
blue.withdrawCollateral(market, 0, address(this), address(this));

vm.expectRevert("zero amount");
blue.liquidate(market, address(0), 0, hex"");
Expand All @@ -658,13 +702,13 @@ contract BlueTest is
amount = bound(amount, 1, type(uint256).max / SharesMath.VIRTUAL_SHARES);

vm.expectRevert(stdError.arithmeticError);
blue.withdraw(market, amount, address(this));
blue.withdraw(market, amount, address(this), address(this));

vm.expectRevert(stdError.arithmeticError);
blue.repay(market, amount, address(this), hex"");

vm.expectRevert(stdError.arithmeticError);
blue.withdrawCollateral(market, amount, address(this));
blue.withdrawCollateral(market, amount, address(this), address(this));
}

function testSetApproval(address manager, bool isAllowed) public {
Expand All @@ -678,11 +722,11 @@ contract BlueTest is
vm.startPrank(attacker);

vm.expectRevert("not approved");
blue.withdraw(market, 1, address(this));
blue.withdraw(market, 1, address(this), address(this));
vm.expectRevert("not approved");
blue.withdrawCollateral(market, 1, address(this));
blue.withdrawCollateral(market, 1, address(this), address(this));
vm.expectRevert("not approved");
blue.borrow(market, 1, address(this));
blue.borrow(market, 1, address(this), address(this));

vm.stopPrank();
}
Expand All @@ -698,9 +742,9 @@ contract BlueTest is

vm.startPrank(manager);

blue.withdraw(market, 1 ether, address(this));
blue.withdrawCollateral(market, 1 ether, address(this));
blue.borrow(market, 1 ether, address(this));
blue.withdraw(market, 1 ether, address(this), address(this));
blue.withdrawCollateral(market, 1 ether, address(this), address(this));
blue.borrow(market, 1 ether, address(this), address(this));

vm.stopPrank();
}
Expand Down Expand Up @@ -758,7 +802,7 @@ contract BlueTest is
amount = bound(amount, 1, 2 ** 64);
borrowableAsset.setBalance(address(this), amount);
blue.supply(market, amount, address(this), hex"");
blue.borrow(market, amount, address(this));
blue.borrow(market, amount, address(this), address(this));

borrowableAsset.approve(address(blue), 0);

Expand All @@ -774,7 +818,7 @@ contract BlueTest is
collateralAsset.setBalance(address(this), amount);
blue.supply(market, amount, address(this), hex"");
blue.supplyCollateral(market, amount, address(this), hex"");
blue.borrow(market, amount.mulWadDown(LLTV), address(this));
blue.borrow(market, amount.mulWadDown(LLTV), address(this), address(this));

borrowableOracle.setPrice(1.01e18);

Expand Down Expand Up @@ -823,7 +867,7 @@ contract BlueTest is
uint256 toBorrow = abi.decode(data, (uint256));
collateralAsset.setBalance(address(this), amount);
borrowableAsset.setBalance(address(this), toBorrow);
blue.borrow(market, toBorrow, address(this));
blue.borrow(market, toBorrow, address(this), address(this));
}
}

Expand All @@ -835,7 +879,7 @@ contract BlueTest is
borrowableAsset.approve(address(blue), amount);
} else if (selector == this.testFlashActions.selector) {
uint256 toWithdraw = abi.decode(data, (uint256));
blue.withdrawCollateral(market, toWithdraw, address(this));
blue.withdrawCollateral(market, toWithdraw, address(this), address(this));
}
}

Expand Down
Loading