From b79ef39c34d402c37df46ca3f65c492d341a289a Mon Sep 17 00:00:00 2001 From: MerlinEgalite Date: Mon, 10 Jul 2023 19:52:25 +0200 Subject: [PATCH 01/18] feat: add manager approval --- src/Blue.sol | 33 ++++++++++++++++++++++++--------- test/forge/Blue.t.sol | 42 +++++++++++++++++++++--------------------- 2 files changed, 45 insertions(+), 30 deletions(-) diff --git a/src/Blue.sol b/src/Blue.sol index 2a0c956e3..09dcb82ea 100644 --- a/src/Blue.sol +++ b/src/Blue.sol @@ -58,6 +58,8 @@ contract Blue { mapping(IIrm => bool) public isIrmEnabled; // Enabled LLTVs. mapping(uint256 => bool) public isLltvEnabled; + // User's managers. + mapping(address => mapping(address => bool)) public isManagedBy; // Constructor. @@ -121,15 +123,16 @@ contract Blue { market.borrowableAsset.safeTransferFrom(msg.sender, address(this), amount); } - function withdraw(Market calldata market, uint256 amount) external { + function withdraw(Market calldata market, uint256 amount, address onBehalf) external { Id id = market.toId(); require(lastUpdate[id] != 0, "unknown market"); require(amount != 0, "zero amount"); + require(isSenderManagerOf(onBehalf), "not approved"); accrueInterests(market, id); uint256 shares = amount.wMul(totalSupplyShares[id]).wDiv(totalSupply[id]); - supplyShare[id][msg.sender] -= shares; + supplyShare[id][onBehalf] -= shares; totalSupplyShares[id] -= shares; totalSupply[id] -= amount; @@ -141,25 +144,26 @@ contract Blue { // Borrow management. - function borrow(Market calldata market, uint256 amount) external { + function borrow(Market calldata market, uint256 amount, address onBehalf) external { Id id = market.toId(); require(lastUpdate[id] != 0, "unknown market"); require(amount != 0, "zero amount"); + require(isSenderManagerOf(onBehalf), "not approved"); accrueInterests(market, id); if (totalBorrow[id] == 0) { - borrowShare[id][msg.sender] = WAD; + borrowShare[id][onBehalf] = WAD; totalBorrowShares[id] = WAD; } else { uint256 shares = amount.wMul(totalBorrowShares[id]).wDiv(totalBorrow[id]); - borrowShare[id][msg.sender] += shares; + borrowShare[id][onBehalf] += shares; totalBorrowShares[id] += shares; } totalBorrow[id] += amount; - require(isHealthy(market, id, msg.sender), "not enough collateral"); + require(isHealthy(market, id, onBehalf), "not enough collateral"); require(totalBorrow[id] <= totalSupply[id], "not enough liquidity"); market.borrowableAsset.safeTransfer(msg.sender, amount); @@ -196,16 +200,17 @@ contract Blue { market.collateralAsset.safeTransferFrom(msg.sender, address(this), amount); } - function withdrawCollateral(Market calldata market, uint256 amount) external { + function withdrawCollateral(Market calldata market, uint256 amount, address onBehalf) external { Id id = market.toId(); require(lastUpdate[id] != 0, "unknown market"); require(amount != 0, "zero amount"); + require(isSenderManagerOf(onBehalf), "not approved"); accrueInterests(market, id); - collateral[id][msg.sender] -= amount; + collateral[id][onBehalf] -= amount; - require(isHealthy(market, id, msg.sender), "not enough collateral"); + require(isHealthy(market, id, onBehalf), "not enough collateral"); market.collateralAsset.safeTransfer(msg.sender, amount); } @@ -244,6 +249,16 @@ contract Blue { market.borrowableAsset.safeTransferFrom(msg.sender, address(this), repaid); } + // Position management. + + function approveManager(address manager, bool isAllowed) external { + isManagedBy[msg.sender][manager] = isAllowed; + } + + function isSenderManagerOf(address user) internal view returns (bool) { + return msg.sender == user || isManagedBy[user][msg.sender]; + } + // Interests management. function accrueInterests(Market calldata market, Id id) private { diff --git a/test/forge/Blue.t.sol b/test/forge/Blue.t.sol index a8638c29d..4efcba8c3 100644 --- a/test/forge/Blue.t.sol +++ b/test/forge/Blue.t.sol @@ -218,19 +218,19 @@ contract BlueTest is Test { blue.supply(market, amountLent); if (amountBorrowed == 0) { - blue.borrow(market, amountBorrowed); + blue.borrow(market, amountBorrowed, address(this)); return; } if (amountBorrowed > amountLent) { vm.prank(BORROWER); vm.expectRevert("not enough liquidity"); - blue.borrow(market, amountBorrowed); + blue.borrow(market, amountBorrowed, BORROWER); return; } vm.prank(BORROWER); - blue.borrow(market, amountBorrowed); + blue.borrow(market, amountBorrowed, BORROWER); assertEq(blue.borrowShare(id, BORROWER), 1e18, "borrow share"); assertEq(borrowableAsset.balanceOf(BORROWER), amountBorrowed, "BORROWER balance"); @@ -247,7 +247,7 @@ contract BlueTest is Test { blue.supply(market, amountLent); vm.prank(BORROWER); - blue.borrow(market, amountBorrowed); + blue.borrow(market, amountBorrowed, BORROWER); if (amountWithdrawn > amountLent - amountBorrowed) { if (amountWithdrawn > amountLent) { @@ -255,11 +255,11 @@ contract BlueTest is Test { } else { vm.expectRevert("not enough liquidity"); } - blue.withdraw(market, amountWithdrawn); + blue.withdraw(market, amountWithdrawn, address(this)); return; } - blue.withdraw(market, amountWithdrawn); + blue.withdraw(market, amountWithdrawn, address(this)); assertApproxEqAbs( blue.supplyShare(id, address(this)), (amountLent - amountWithdrawn) * 1e18 / amountLent, 1e3, "supply share" @@ -296,11 +296,11 @@ contract BlueTest is Test { uint256 borrowValue = amountBorrowed.wMul(priceBorrowable); if (borrowValue == 0 || (collateralValue > 0 && borrowValue <= collateralValue.wMul(LLTV))) { vm.prank(BORROWER); - blue.borrow(market, amountBorrowed); + blue.borrow(market, amountBorrowed, BORROWER); } else { vm.prank(BORROWER); vm.expectRevert("not enough collateral"); - blue.borrow(market, amountBorrowed); + blue.borrow(market, amountBorrowed, BORROWER); } } @@ -313,7 +313,7 @@ contract BlueTest is Test { blue.supply(market, amountLent); vm.startPrank(BORROWER); - blue.borrow(market, amountBorrowed); + blue.borrow(market, amountBorrowed, BORROWER); blue.repay(market, amountRepaid); vm.stopPrank(); @@ -344,11 +344,11 @@ contract BlueTest is Test { if (amountWithdrawn > amountDeposited) { vm.expectRevert(stdError.arithmeticError); - blue.withdrawCollateral(market, amountWithdrawn); + blue.withdrawCollateral(market, amountWithdrawn, address(this)); return; } - blue.withdrawCollateral(market, amountWithdrawn); + blue.withdrawCollateral(market, amountWithdrawn, address(this)); assertEq(blue.collateral(id, address(this)), amountDeposited - amountWithdrawn, "this collateral"); assertEq(collateralAsset.balanceOf(address(this)), amountWithdrawn, "this balance"); @@ -375,7 +375,7 @@ contract BlueTest is Test { // Borrow vm.startPrank(BORROWER); blue.supplyCollateral(market, amountCollateral); - blue.borrow(market, amountBorrowed); + blue.borrow(market, amountBorrowed, BORROWER); vm.stopPrank(); // Price change @@ -417,7 +417,7 @@ contract BlueTest is Test { // Borrow vm.startPrank(BORROWER); blue.supplyCollateral(market, amountCollateral); - blue.borrow(market, amountBorrowed); + blue.borrow(market, amountBorrowed, BORROWER); vm.stopPrank(); // Price change @@ -466,10 +466,10 @@ contract BlueTest is Test { blue.supply(marketFuzz, 1); vm.expectRevert("unknown market"); - blue.withdraw(marketFuzz, 1); + blue.withdraw(marketFuzz, 1, address(this)); vm.expectRevert("unknown market"); - blue.borrow(marketFuzz, 1); + blue.borrow(marketFuzz, 1, address(this)); vm.expectRevert("unknown market"); blue.repay(marketFuzz, 1); @@ -478,7 +478,7 @@ contract BlueTest is Test { blue.supplyCollateral(marketFuzz, 1); vm.expectRevert("unknown market"); - blue.withdrawCollateral(marketFuzz, 1); + blue.withdrawCollateral(marketFuzz, 1, address(this)); vm.expectRevert("unknown market"); blue.liquidate(marketFuzz, address(0), 1); @@ -489,10 +489,10 @@ contract BlueTest is Test { blue.supply(market, 0); vm.expectRevert("zero amount"); - blue.withdraw(market, 0); + blue.withdraw(market, 0, address(this)); vm.expectRevert("zero amount"); - blue.borrow(market, 0); + blue.borrow(market, 0, address(this)); vm.expectRevert("zero amount"); blue.repay(market, 0); @@ -501,7 +501,7 @@ contract BlueTest is Test { blue.supplyCollateral(market, 0); vm.expectRevert("zero amount"); - blue.withdrawCollateral(market, 0); + blue.withdrawCollateral(market, 0, address(this)); vm.expectRevert("zero amount"); blue.liquidate(market, address(0), 0); @@ -511,13 +511,13 @@ contract BlueTest is Test { vm.assume(amount > 0); vm.expectRevert(stdError.divisionError); - blue.withdraw(market, amount); + blue.withdraw(market, amount, address(this)); vm.expectRevert(stdError.divisionError); blue.repay(market, amount); vm.expectRevert(stdError.arithmeticError); - blue.withdrawCollateral(market, amount); + blue.withdrawCollateral(market, amount, address(this)); } } From c839397f470287a26584fb0214963cf2af3b8029 Mon Sep 17 00:00:00 2001 From: MerlinEgalite Date: Tue, 11 Jul 2023 10:24:02 +0200 Subject: [PATCH 02/18] refactor: renaming --- src/Blue.sol | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/Blue.sol b/src/Blue.sol index 09dcb82ea..8e04d686d 100644 --- a/src/Blue.sol +++ b/src/Blue.sol @@ -59,7 +59,7 @@ contract Blue { // Enabled LLTVs. mapping(uint256 => bool) public isLltvEnabled; // User's managers. - mapping(address => mapping(address => bool)) public isManagedBy; + mapping(address => mapping(address => bool)) public approval; // Constructor. @@ -127,7 +127,7 @@ contract Blue { Id id = market.toId(); require(lastUpdate[id] != 0, "unknown market"); require(amount != 0, "zero amount"); - require(isSenderManagerOf(onBehalf), "not approved"); + require(isSenderApprovedFor(onBehalf), "not approved"); accrueInterests(market, id); @@ -148,7 +148,7 @@ contract Blue { Id id = market.toId(); require(lastUpdate[id] != 0, "unknown market"); require(amount != 0, "zero amount"); - require(isSenderManagerOf(onBehalf), "not approved"); + require(isSenderApprovedFor(onBehalf), "not approved"); accrueInterests(market, id); @@ -204,7 +204,7 @@ contract Blue { Id id = market.toId(); require(lastUpdate[id] != 0, "unknown market"); require(amount != 0, "zero amount"); - require(isSenderManagerOf(onBehalf), "not approved"); + require(isSenderApprovedFor(onBehalf), "not approved"); accrueInterests(market, id); @@ -251,12 +251,12 @@ contract Blue { // Position management. - function approveManager(address manager, bool isAllowed) external { - isManagedBy[msg.sender][manager] = isAllowed; + function setApproval(address manager, bool isAllowed) external { + approval[msg.sender][manager] = isAllowed; } - function isSenderManagerOf(address user) internal view returns (bool) { - return msg.sender == user || isManagedBy[user][msg.sender]; + function isSenderApprovedFor(address user) internal view returns (bool) { + return msg.sender == user || approval[user][msg.sender]; } // Interests management. From 263fbafc7610ba8f3e56c63700c4419a498ecde9 Mon Sep 17 00:00:00 2001 From: MerlinEgalite Date: Tue, 11 Jul 2023 10:37:17 +0200 Subject: [PATCH 03/18] feat: add onbehalf for supply and repay --- src/Blue.sol | 20 ++++++++++---------- test/forge/Blue.t.sol | 44 +++++++++++++++++++++---------------------- 2 files changed, 32 insertions(+), 32 deletions(-) diff --git a/src/Blue.sol b/src/Blue.sol index 8e04d686d..9176df044 100644 --- a/src/Blue.sol +++ b/src/Blue.sol @@ -102,7 +102,7 @@ contract Blue { // Supply management. - function supply(Market calldata market, uint256 amount) external { + function supply(Market calldata market, uint256 amount, address onBehalf) external { Id id = market.toId(); require(lastUpdate[id] != 0, "unknown market"); require(amount != 0, "zero amount"); @@ -110,17 +110,17 @@ contract Blue { accrueInterests(market, id); if (totalSupply[id] == 0) { - supplyShare[id][msg.sender] = WAD; + supplyShare[id][onBehalf] = WAD; totalSupplyShares[id] = WAD; } else { uint256 shares = amount.wMul(totalSupplyShares[id]).wDiv(totalSupply[id]); - supplyShare[id][msg.sender] += shares; + supplyShare[id][onBehalf] += shares; totalSupplyShares[id] += shares; } totalSupply[id] += amount; - market.borrowableAsset.safeTransferFrom(msg.sender, address(this), amount); + market.borrowableAsset.safeTransferFrom(onBehalf, address(this), amount); } function withdraw(Market calldata market, uint256 amount, address onBehalf) external { @@ -169,7 +169,7 @@ contract Blue { market.borrowableAsset.safeTransfer(msg.sender, amount); } - function repay(Market calldata market, uint256 amount) external { + function repay(Market calldata market, uint256 amount, address onBehalf) external { Id id = market.toId(); require(lastUpdate[id] != 0, "unknown market"); require(amount != 0, "zero amount"); @@ -177,27 +177,27 @@ contract Blue { accrueInterests(market, id); uint256 shares = amount.wMul(totalBorrowShares[id]).wDiv(totalBorrow[id]); - borrowShare[id][msg.sender] -= shares; + borrowShare[id][onBehalf] -= shares; totalBorrowShares[id] -= shares; totalBorrow[id] -= amount; - market.borrowableAsset.safeTransferFrom(msg.sender, address(this), amount); + market.borrowableAsset.safeTransferFrom(onBehalf, address(this), amount); } // Collateral management. /// @dev Don't accrue interests because it's not required and it saves gas. - function supplyCollateral(Market calldata market, uint256 amount) external { + function supplyCollateral(Market calldata market, uint256 amount, address onBehalf) external { Id id = market.toId(); require(lastUpdate[id] != 0, "unknown market"); require(amount != 0, "zero amount"); // Don't accrue interests because it's not required and it saves gas. - collateral[id][msg.sender] += amount; + collateral[id][onBehalf] += amount; - market.collateralAsset.safeTransferFrom(msg.sender, address(this), amount); + market.collateralAsset.safeTransferFrom(onBehalf, address(this), amount); } function withdrawCollateral(Market calldata market, uint256 amount, address onBehalf) external { diff --git a/test/forge/Blue.t.sol b/test/forge/Blue.t.sol index 4efcba8c3..1279d57fb 100644 --- a/test/forge/Blue.t.sol +++ b/test/forge/Blue.t.sol @@ -203,7 +203,7 @@ contract BlueTest is Test { amount = bound(amount, 1, 2 ** 64); borrowableAsset.setBalance(address(this), amount); - blue.supply(market, amount); + blue.supply(market, amount, address(this)); assertEq(blue.supplyShare(id, address(this)), 1e18, "supply share"); assertEq(borrowableAsset.balanceOf(address(this)), 0, "lender balance"); @@ -215,7 +215,7 @@ contract BlueTest is Test { amountBorrowed = bound(amountBorrowed, 1, 2 ** 64); borrowableAsset.setBalance(address(this), amountLent); - blue.supply(market, amountLent); + blue.supply(market, amountLent, address(this)); if (amountBorrowed == 0) { blue.borrow(market, amountBorrowed, address(this)); @@ -244,7 +244,7 @@ contract BlueTest is Test { vm.assume(amountLent >= amountBorrowed); borrowableAsset.setBalance(address(this), amountLent); - blue.supply(market, amountLent); + blue.supply(market, amountLent, address(this)); vm.prank(BORROWER); blue.borrow(market, amountBorrowed, BORROWER); @@ -287,10 +287,10 @@ contract BlueTest is Test { borrowableAsset.setBalance(address(this), amountBorrowed); collateralAsset.setBalance(BORROWER, amountCollateral); - blue.supply(market, amountBorrowed); + blue.supply(market, amountBorrowed, address(this)); vm.prank(BORROWER); - blue.supplyCollateral(market, amountCollateral); + blue.supplyCollateral(market, amountCollateral, BORROWER); uint256 collateralValue = amountCollateral.wMul(priceCollateral); uint256 borrowValue = amountBorrowed.wMul(priceBorrowable); @@ -310,11 +310,11 @@ contract BlueTest is Test { amountRepaid = bound(amountRepaid, 1, amountBorrowed); borrowableAsset.setBalance(address(this), amountLent); - blue.supply(market, amountLent); + blue.supply(market, amountLent, address(this)); vm.startPrank(BORROWER); blue.borrow(market, amountBorrowed, BORROWER); - blue.repay(market, amountRepaid); + blue.repay(market, amountRepaid, BORROWER); vm.stopPrank(); assertApproxEqAbs( @@ -328,7 +328,7 @@ contract BlueTest is Test { amount = bound(amount, 1, 2 ** 64); collateralAsset.setBalance(address(this), amount); - blue.supplyCollateral(market, amount); + blue.supplyCollateral(market, amount, address(this)); assertEq(blue.collateral(id, address(this)), amount, "collateral"); assertEq(collateralAsset.balanceOf(address(this)), 0, "this balance"); @@ -340,7 +340,7 @@ contract BlueTest is Test { amountWithdrawn = bound(amountWithdrawn, 1, 2 ** 64); collateralAsset.setBalance(address(this), amountDeposited); - blue.supplyCollateral(market, amountDeposited); + blue.supplyCollateral(market, amountDeposited, address(this)); if (amountWithdrawn > amountDeposited) { vm.expectRevert(stdError.arithmeticError); @@ -370,11 +370,11 @@ contract BlueTest is Test { borrowableAsset.setBalance(LIQUIDATOR, amountBorrowed); // Supply - blue.supply(market, amountLent); + blue.supply(market, amountLent, address(this)); // Borrow vm.startPrank(BORROWER); - blue.supplyCollateral(market, amountCollateral); + blue.supplyCollateral(market, amountCollateral, BORROWER); blue.borrow(market, amountBorrowed, BORROWER); vm.stopPrank(); @@ -412,11 +412,11 @@ contract BlueTest is Test { borrowableAsset.setBalance(LIQUIDATOR, amountBorrowed); // Supply - blue.supply(market, amountLent); + blue.supply(market, amountLent, address(this)); // Borrow vm.startPrank(BORROWER); - blue.supplyCollateral(market, amountCollateral); + blue.supplyCollateral(market, amountCollateral, BORROWER); blue.borrow(market, amountBorrowed, BORROWER); vm.stopPrank(); @@ -447,11 +447,11 @@ contract BlueTest is Test { secondAmount = bound(secondAmount, 1, 2 ** 64); borrowableAsset.setBalance(address(this), firstAmount); - blue.supply(market, firstAmount); + blue.supply(market, firstAmount, address(this)); borrowableAsset.setBalance(BORROWER, secondAmount); vm.prank(BORROWER); - blue.supply(market, secondAmount); + blue.supply(market, secondAmount, BORROWER); assertApproxEqAbs(supplyBalance(address(this)), firstAmount, 100, "same balance first user"); assertEq(blue.supplyShare(id, address(this)), 1e18, "expected shares first user"); @@ -463,7 +463,7 @@ contract BlueTest is Test { vm.assume(neq(marketFuzz, market)); vm.expectRevert("unknown market"); - blue.supply(marketFuzz, 1); + blue.supply(marketFuzz, 1, address(this)); vm.expectRevert("unknown market"); blue.withdraw(marketFuzz, 1, address(this)); @@ -472,10 +472,10 @@ contract BlueTest is Test { blue.borrow(marketFuzz, 1, address(this)); vm.expectRevert("unknown market"); - blue.repay(marketFuzz, 1); + blue.repay(marketFuzz, 1, address(this)); vm.expectRevert("unknown market"); - blue.supplyCollateral(marketFuzz, 1); + blue.supplyCollateral(marketFuzz, 1, address(this)); vm.expectRevert("unknown market"); blue.withdrawCollateral(marketFuzz, 1, address(this)); @@ -486,7 +486,7 @@ contract BlueTest is Test { function testAmountZero() public { vm.expectRevert("zero amount"); - blue.supply(market, 0); + blue.supply(market, 0, address(this)); vm.expectRevert("zero amount"); blue.withdraw(market, 0, address(this)); @@ -495,10 +495,10 @@ contract BlueTest is Test { blue.borrow(market, 0, address(this)); vm.expectRevert("zero amount"); - blue.repay(market, 0); + blue.repay(market, 0, address(this)); vm.expectRevert("zero amount"); - blue.supplyCollateral(market, 0); + blue.supplyCollateral(market, 0, address(this)); vm.expectRevert("zero amount"); blue.withdrawCollateral(market, 0, address(this)); @@ -514,7 +514,7 @@ contract BlueTest is Test { blue.withdraw(market, amount, address(this)); vm.expectRevert(stdError.divisionError); - blue.repay(market, amount); + blue.repay(market, amount, address(this)); vm.expectRevert(stdError.arithmeticError); blue.withdrawCollateral(market, amount, address(this)); From 16d7dfbb5aff8912f11dd375c2f5fa8fa423a0a7 Mon Sep 17 00:00:00 2001 From: MerlinEgalite Date: Tue, 11 Jul 2023 11:56:56 +0200 Subject: [PATCH 04/18] fix: sender --- src/Blue.sol | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Blue.sol b/src/Blue.sol index 9176df044..0e79ac732 100644 --- a/src/Blue.sol +++ b/src/Blue.sol @@ -120,7 +120,7 @@ contract Blue { totalSupply[id] += amount; - market.borrowableAsset.safeTransferFrom(onBehalf, address(this), amount); + market.borrowableAsset.safeTransferFrom(msg.sender, address(this), amount); } function withdraw(Market calldata market, uint256 amount, address onBehalf) external { @@ -182,7 +182,7 @@ contract Blue { totalBorrow[id] -= amount; - market.borrowableAsset.safeTransferFrom(onBehalf, address(this), amount); + market.borrowableAsset.safeTransferFrom(msg.sender, address(this), amount); } // Collateral management. @@ -197,7 +197,7 @@ contract Blue { collateral[id][onBehalf] += amount; - market.collateralAsset.safeTransferFrom(onBehalf, address(this), amount); + market.collateralAsset.safeTransferFrom(msg.sender, address(this), amount); } function withdrawCollateral(Market calldata market, uint256 amount, address onBehalf) external { From 209d3eaa9aab1849888555c495a9d3854afc15ef Mon Sep 17 00:00:00 2001 From: MerlinEgalite Date: Tue, 11 Jul 2023 15:29:27 +0200 Subject: [PATCH 05/18] test: add simple tests --- test/forge/Blue.t.sol | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/test/forge/Blue.t.sol b/test/forge/Blue.t.sol index 4efcba8c3..e247d3103 100644 --- a/test/forge/Blue.t.sol +++ b/test/forge/Blue.t.sol @@ -519,6 +519,42 @@ contract BlueTest is Test { vm.expectRevert(stdError.arithmeticError); blue.withdrawCollateral(market, amount, address(this)); } + + function testSetApproval(address manager, bool isAllowed) public { + blue.setApproval(manager, isAllowed); + assertEq(blue.approval(address(this), manager), isAllowed); + } + + function testNotApproved(address attacker) public { + vm.startPrank(attacker); + + vm.expectRevert("not approved"); + blue.withdraw(market, 1, address(this)); + vm.expectRevert("not approved"); + blue.withdrawCollateral(market, 1, address(this)); + vm.expectRevert("not approved"); + blue.borrow(market, 1, address(this)); + + vm.stopPrank(); + } + + function testApproved(address manager) public { + borrowableAsset.setBalance(address(this), 100 ether); + collateralAsset.setBalance(address(this), 100 ether); + + blue.supply(market, 100 ether); + blue.supplyCollateral(market, 100 ether); + + blue.setApproval(manager, true); + + vm.startPrank(manager); + + blue.withdraw(market, 1 ether, address(this)); + blue.withdrawCollateral(market, 1 ether, address(this)); + blue.borrow(market, 1 ether, address(this)); + + vm.stopPrank(); + } } function neq(Market memory a, Market memory b) pure returns (bool) { From a00d5ba735cb1e7379fce5412aff8fd72fca4976 Mon Sep 17 00:00:00 2001 From: MerlinEgalite Date: Tue, 11 Jul 2023 15:42:25 +0200 Subject: [PATCH 06/18] test: fix test --- test/forge/Blue.t.sol | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/forge/Blue.t.sol b/test/forge/Blue.t.sol index e247d3103..32f6ad43c 100644 --- a/test/forge/Blue.t.sol +++ b/test/forge/Blue.t.sol @@ -526,6 +526,8 @@ contract BlueTest is Test { } function testNotApproved(address attacker) public { + vm.assume(attacker != address(this)); + vm.startPrank(attacker); vm.expectRevert("not approved"); From d576984ba56aca2119e852f062e11bbec33f9223 Mon Sep 17 00:00:00 2001 From: MerlinEgalite Date: Tue, 11 Jul 2023 19:08:26 +0200 Subject: [PATCH 07/18] test: fix hardhat test --- test/hardhat/Blue.spec.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/hardhat/Blue.spec.ts b/test/hardhat/Blue.spec.ts index 455a8ec48..3631119b4 100644 --- a/test/hardhat/Blue.spec.ts +++ b/test/hardhat/Blue.spec.ts @@ -114,7 +114,7 @@ describe("Blue", () => { if (supplyOnly) { if (amount > BigNumber.from(0)) { await blue.connect(user).supply(market, amount); - await blue.connect(user).withdraw(market, amount.div(2)); + await blue.connect(user).withdraw(market, amount.div(2), user.address); } } else { const totalSupply = await blue.totalSupply(id); @@ -124,9 +124,9 @@ describe("Blue", () => { if (amount > BigNumber.from(0)) { await blue.connect(user).supplyCollateral(market, amount); - await blue.connect(user).borrow(market, amount.div(2)); + await blue.connect(user).borrow(market, amount.div(2), user.address); await blue.connect(user).repay(market, amount.div(4)); - await blue.connect(user).withdrawCollateral(market, amount.div(8)); + await blue.connect(user).withdrawCollateral(market, amount.div(8), user.address); } } } @@ -168,7 +168,7 @@ describe("Blue", () => { await blue.connect(user).supply(market, amount); await blue.connect(user).supplyCollateral(market, amount); - await blue.connect(user).borrow(market, borrowedAmount); + await blue.connect(user).borrow(market, borrowedAmount, user.address); } await borrowableOracle.connect(admin).setPrice(BigNumber.WAD.mul(1000)); From 21dad012140cee44e22e3aeb5265b0f3d19627f5 Mon Sep 17 00:00:00 2001 From: MerlinEgalite Date: Wed, 12 Jul 2023 18:54:11 +0200 Subject: [PATCH 08/18] test: add simple tests --- test/forge/Blue.t.sol | 47 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/test/forge/Blue.t.sol b/test/forge/Blue.t.sol index 1279d57fb..cc535bef5 100644 --- a/test/forge/Blue.t.sol +++ b/test/forge/Blue.t.sol @@ -210,6 +210,18 @@ contract BlueTest is Test { assertEq(borrowableAsset.balanceOf(address(blue)), amount, "blue balance"); } + function testSupplyOnBehalf(uint256 amount, address onBehalf) public { + vm.assume(onBehalf != address(this)); + amount = bound(amount, 1, 2 ** 64); + + borrowableAsset.setBalance(address(this), amount); + blue.supply(market, amount, onBehalf); + + assertEq(blue.supplyShare(id, onBehalf), 1e18, "supply share"); + assertEq(borrowableAsset.balanceOf(onBehalf), 0, "lender balance"); + assertEq(borrowableAsset.balanceOf(address(blue)), amount, "blue balance"); + } + function testBorrow(uint256 amountLent, uint256 amountBorrowed) public { amountLent = bound(amountLent, 1, 2 ** 64); amountBorrowed = bound(amountBorrowed, 1, 2 ** 64); @@ -324,6 +336,29 @@ contract BlueTest is Test { assertEq(borrowableAsset.balanceOf(address(blue)), amountLent - amountBorrowed + amountRepaid, "blue balance"); } + function testRepayOnBehalf(uint256 amountLent, uint256 amountBorrowed, uint256 amountRepaid, address onBehalf) + public + { + vm.assume(onBehalf != address(this)); + amountLent = bound(amountLent, 1, 2 ** 64); + amountBorrowed = bound(amountBorrowed, 1, amountLent); + amountRepaid = bound(amountRepaid, 1, amountBorrowed); + + borrowableAsset.setBalance(address(this), amountLent + amountRepaid); + blue.supply(market, amountLent, address(this)); + + vm.prank(onBehalf); + blue.borrow(market, amountBorrowed, onBehalf); + + blue.repay(market, amountRepaid, onBehalf); + + assertApproxEqAbs( + blue.borrowShare(id, onBehalf), (amountBorrowed - amountRepaid) * 1e18 / amountBorrowed, 1e3, "borrow share" + ); + assertEq(borrowableAsset.balanceOf(onBehalf), amountBorrowed, "onBehalf balance"); + assertEq(borrowableAsset.balanceOf(address(blue)), amountLent - amountBorrowed + amountRepaid, "blue balance"); + } + function testSupplyCollateral(uint256 amount) public { amount = bound(amount, 1, 2 ** 64); @@ -335,6 +370,18 @@ contract BlueTest is Test { assertEq(collateralAsset.balanceOf(address(blue)), amount, "blue balance"); } + function testSupplyCollateral(uint256 amount, address onBehalf) public { + vm.assume(onBehalf != address(this)); + amount = bound(amount, 1, 2 ** 64); + + collateralAsset.setBalance(address(this), amount); + blue.supplyCollateral(market, amount, onBehalf); + + assertEq(blue.collateral(id, onBehalf), amount, "collateral"); + assertEq(collateralAsset.balanceOf(onBehalf), 0, "this balance"); + assertEq(collateralAsset.balanceOf(address(blue)), amount, "blue balance"); + } + function testWithdrawCollateral(uint256 amountDeposited, uint256 amountWithdrawn) public { amountDeposited = bound(amountDeposited, 1, 2 ** 64); amountWithdrawn = bound(amountWithdrawn, 1, 2 ** 64); From 8484d1e0810c6fee3da53556f61b76454cad8e59 Mon Sep 17 00:00:00 2001 From: MerlinEgalite Date: Thu, 13 Jul 2023 14:40:13 +0200 Subject: [PATCH 09/18] test: remove useless tests --- test/forge/Blue.t.sol | 28 ++-------------------------- 1 file changed, 2 insertions(+), 26 deletions(-) diff --git a/test/forge/Blue.t.sol b/test/forge/Blue.t.sol index cc535bef5..8869c2417 100644 --- a/test/forge/Blue.t.sol +++ b/test/forge/Blue.t.sol @@ -199,19 +199,7 @@ contract BlueTest is Test { blue.createMarket(marketFuzz); } - function testSupply(uint256 amount) public { - amount = bound(amount, 1, 2 ** 64); - - borrowableAsset.setBalance(address(this), amount); - blue.supply(market, amount, address(this)); - - assertEq(blue.supplyShare(id, address(this)), 1e18, "supply share"); - assertEq(borrowableAsset.balanceOf(address(this)), 0, "lender balance"); - assertEq(borrowableAsset.balanceOf(address(blue)), amount, "blue balance"); - } - function testSupplyOnBehalf(uint256 amount, address onBehalf) public { - vm.assume(onBehalf != address(this)); amount = bound(amount, 1, 2 ** 64); borrowableAsset.setBalance(address(this), amount); @@ -359,26 +347,14 @@ contract BlueTest is Test { assertEq(borrowableAsset.balanceOf(address(blue)), amountLent - amountBorrowed + amountRepaid, "blue balance"); } - function testSupplyCollateral(uint256 amount) public { - amount = bound(amount, 1, 2 ** 64); - - collateralAsset.setBalance(address(this), amount); - blue.supplyCollateral(market, amount, address(this)); - - assertEq(blue.collateral(id, address(this)), amount, "collateral"); - assertEq(collateralAsset.balanceOf(address(this)), 0, "this balance"); - assertEq(collateralAsset.balanceOf(address(blue)), amount, "blue balance"); - } - - function testSupplyCollateral(uint256 amount, address onBehalf) public { - vm.assume(onBehalf != address(this)); + function testSupplyCollateralOnBehalf(uint256 amount, address onBehalf) public { amount = bound(amount, 1, 2 ** 64); collateralAsset.setBalance(address(this), amount); blue.supplyCollateral(market, amount, onBehalf); assertEq(blue.collateral(id, onBehalf), amount, "collateral"); - assertEq(collateralAsset.balanceOf(onBehalf), 0, "this balance"); + assertEq(collateralAsset.balanceOf(onBehalf), 0, "onBehalf balance"); assertEq(collateralAsset.balanceOf(address(blue)), amount, "blue balance"); } From 8f5ed6a6821638d32782c4ce67c56b0a65322e5d Mon Sep 17 00:00:00 2001 From: MerlinEgalite Date: Thu, 13 Jul 2023 15:32:46 +0200 Subject: [PATCH 10/18] Merge branch 'feat/approval' of github.com:morpho-labs/blue into feat/onbehalf --- test/forge/Blue.t.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/forge/Blue.t.sol b/test/forge/Blue.t.sol index be63bed5f..b06cee431 100644 --- a/test/forge/Blue.t.sol +++ b/test/forge/Blue.t.sol @@ -567,8 +567,8 @@ contract BlueTest is Test { borrowableAsset.setBalance(address(this), 100 ether); collateralAsset.setBalance(address(this), 100 ether); - blue.supply(market, 100 ether); - blue.supplyCollateral(market, 100 ether); + blue.supply(market, 100 ether, address(this)); + blue.supplyCollateral(market, 100 ether, address(this)); blue.setApproval(manager, true); From 418aece75b1e65b21e3986987322ac577811ed98 Mon Sep 17 00:00:00 2001 From: MerlinEgalite Date: Thu, 13 Jul 2023 16:04:49 +0200 Subject: [PATCH 11/18] test: fix tests --- test/forge/Blue.t.sol | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/forge/Blue.t.sol b/test/forge/Blue.t.sol index b06cee431..5456f9ea7 100644 --- a/test/forge/Blue.t.sol +++ b/test/forge/Blue.t.sol @@ -200,6 +200,7 @@ contract BlueTest is Test { } function testSupplyOnBehalf(uint256 amount, address onBehalf) public { + vm.assume(onBehalf != address(blue)); amount = bound(amount, 1, 2 ** 64); borrowableAsset.setBalance(address(this), amount); @@ -327,6 +328,7 @@ contract BlueTest is Test { function testRepayOnBehalf(uint256 amountLent, uint256 amountBorrowed, uint256 amountRepaid, address onBehalf) public { + vm.assume(onBehalf != address(blue)); vm.assume(onBehalf != address(this)); amountLent = bound(amountLent, 1, 2 ** 64); amountBorrowed = bound(amountBorrowed, 1, amountLent); @@ -348,6 +350,7 @@ contract BlueTest is Test { } function testSupplyCollateralOnBehalf(uint256 amount, address onBehalf) public { + vm.assume(onBehalf != address(blue)); amount = bound(amount, 1, 2 ** 64); collateralAsset.setBalance(address(this), amount); From 66af2a3ff4afe5efc35b52b48b6675811e127025 Mon Sep 17 00:00:00 2001 From: MerlinEgalite Date: Thu, 13 Jul 2023 21:09:33 +0200 Subject: [PATCH 12/18] test: apply naming suggestions --- src/Blue.sol | 14 +++++++------- test/forge/Blue.t.sol | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/Blue.sol b/src/Blue.sol index 8e04d686d..f5b04498b 100644 --- a/src/Blue.sol +++ b/src/Blue.sol @@ -59,7 +59,7 @@ contract Blue { // Enabled LLTVs. mapping(uint256 => bool) public isLltvEnabled; // User's managers. - mapping(address => mapping(address => bool)) public approval; + mapping(address => mapping(address => bool)) public isApproved; // Constructor. @@ -127,7 +127,7 @@ contract Blue { Id id = market.toId(); require(lastUpdate[id] != 0, "unknown market"); require(amount != 0, "zero amount"); - require(isSenderApprovedFor(onBehalf), "not approved"); + require(isSenderOrIsApproved(onBehalf), "not approved"); accrueInterests(market, id); @@ -148,7 +148,7 @@ contract Blue { Id id = market.toId(); require(lastUpdate[id] != 0, "unknown market"); require(amount != 0, "zero amount"); - require(isSenderApprovedFor(onBehalf), "not approved"); + require(isSenderOrIsApproved(onBehalf), "not approved"); accrueInterests(market, id); @@ -204,7 +204,7 @@ contract Blue { Id id = market.toId(); require(lastUpdate[id] != 0, "unknown market"); require(amount != 0, "zero amount"); - require(isSenderApprovedFor(onBehalf), "not approved"); + require(isSenderOrIsApproved(onBehalf), "not approved"); accrueInterests(market, id); @@ -252,11 +252,11 @@ contract Blue { // Position management. function setApproval(address manager, bool isAllowed) external { - approval[msg.sender][manager] = isAllowed; + isApproved[msg.sender][manager] = isAllowed; } - function isSenderApprovedFor(address user) internal view returns (bool) { - return msg.sender == user || approval[user][msg.sender]; + function isSenderOrIsApproved(address user) internal view returns (bool) { + return msg.sender == user || isApproved[user][msg.sender]; } // Interests management. diff --git a/test/forge/Blue.t.sol b/test/forge/Blue.t.sol index 32f6ad43c..e5f662974 100644 --- a/test/forge/Blue.t.sol +++ b/test/forge/Blue.t.sol @@ -522,7 +522,7 @@ contract BlueTest is Test { function testSetApproval(address manager, bool isAllowed) public { blue.setApproval(manager, isAllowed); - assertEq(blue.approval(address(this), manager), isAllowed); + assertEq(blue.isApproved(address(this), manager), isAllowed); } function testNotApproved(address attacker) public { From 568ea3fc948825c419c1d43a50cf0cf5080e5223 Mon Sep 17 00:00:00 2001 From: MerlinEgalite Date: Fri, 14 Jul 2023 08:55:07 +0200 Subject: [PATCH 13/18] test: fix hardhat --- test/hardhat/Blue.spec.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/test/hardhat/Blue.spec.ts b/test/hardhat/Blue.spec.ts index 3631119b4..ad0a109af 100644 --- a/test/hardhat/Blue.spec.ts +++ b/test/hardhat/Blue.spec.ts @@ -113,7 +113,7 @@ describe("Blue", () => { let supplyOnly: boolean = random() < 2 / 3; if (supplyOnly) { if (amount > BigNumber.from(0)) { - await blue.connect(user).supply(market, amount); + await blue.connect(user).supply(market, amount, user.address); await blue.connect(user).withdraw(market, amount.div(2), user.address); } } else { @@ -123,9 +123,9 @@ describe("Blue", () => { amount = BigNumber.min(amount, BigNumber.from(liq).div(2)); if (amount > BigNumber.from(0)) { - await blue.connect(user).supplyCollateral(market, amount); + await blue.connect(user).supplyCollateral(market, amount, user.address); await blue.connect(user).borrow(market, amount.div(2), user.address); - await blue.connect(user).repay(market, amount.div(4)); + await blue.connect(user).repay(market, amount.div(4), user.address); await blue.connect(user).withdrawCollateral(market, amount.div(8), user.address); } } @@ -165,8 +165,8 @@ describe("Blue", () => { await collateral.setBalance(user.address, initBalance); await collateral.connect(user).approve(blue.address, constants.MaxUint256); - await blue.connect(user).supply(market, amount); - await blue.connect(user).supplyCollateral(market, amount); + await blue.connect(user).supply(market, amount, user.address); + await blue.connect(user).supplyCollateral(market, amount, user.address); await blue.connect(user).borrow(market, borrowedAmount, user.address); } From e619ae38fbb88c529bf43e87c5344ba1ed1cb973 Mon Sep 17 00:00:00 2001 From: MerlinEgalite Date: Sun, 16 Jul 2023 16:42:49 +0200 Subject: [PATCH 14/18] fix: compilation --- src/Blue.sol | 23 +++-------------------- src/interfaces/IIrm.sol | 2 +- src/libraries/MarketIdLib.sol | 13 +++++++++++++ src/libraries/Types.sol | 16 ++++++++++++++++ src/mocks/IrmMock.sol | 9 +++++++-- 5 files changed, 40 insertions(+), 23 deletions(-) create mode 100644 src/libraries/MarketIdLib.sol create mode 100644 src/libraries/Types.sol diff --git a/src/Blue.sol b/src/Blue.sol index 3e58b2946..2c7be56a1 100644 --- a/src/Blue.sol +++ b/src/Blue.sol @@ -3,35 +3,18 @@ pragma solidity 0.8.20; import {IIrm} from "src/interfaces/IIrm.sol"; import {IERC20} from "src/interfaces/IERC20.sol"; -import {IOracle} from "src/interfaces/IOracle.sol"; +import {Market} from "src/libraries/Types.sol"; import {MathLib} from "src/libraries/MathLib.sol"; +import {Id, MarketIdLib} from "src/libraries/MarketIdLib.sol"; import {SafeTransferLib} from "src/libraries/SafeTransferLib.sol"; uint256 constant WAD = 1e18; uint256 constant ALPHA = 0.5e18; -// Market id. -type Id is bytes32; - -// Market. -struct Market { - IERC20 borrowableAsset; - IERC20 collateralAsset; - IOracle borrowableOracle; - IOracle collateralOracle; - IIrm irm; - uint256 lltv; -} - -using {toId} for Market; - -function toId(Market calldata market) pure returns (Id) { - return Id.wrap(keccak256(abi.encode(market))); -} - contract Blue { using MathLib for uint256; + using MarketIdLib for Market; using SafeTransferLib for IERC20; // Storage. diff --git a/src/interfaces/IIrm.sol b/src/interfaces/IIrm.sol index 791725d83..9736da203 100644 --- a/src/interfaces/IIrm.sol +++ b/src/interfaces/IIrm.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity >=0.5.0; -import {Market} from "src/Blue.sol"; +import {Market} from "src/libraries/Types.sol"; interface IIrm { function borrowRate(Market calldata market) external returns (uint256); diff --git a/src/libraries/MarketIdLib.sol b/src/libraries/MarketIdLib.sol new file mode 100644 index 000000000..edc74addc --- /dev/null +++ b/src/libraries/MarketIdLib.sol @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.0; + +import {Market} from "src/libraries/Types.sol"; + +// Market id. +type Id is bytes32; + +library MarketIdLib { + function toId(Market calldata market) internal pure returns (Id) { + return Id.wrap(keccak256(abi.encode(market))); + } +} diff --git a/src/libraries/Types.sol b/src/libraries/Types.sol new file mode 100644 index 000000000..9c7483db4 --- /dev/null +++ b/src/libraries/Types.sol @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.0; + +import {IIrm} from "src/interfaces/IIrm.sol"; +import {IERC20} from "src/interfaces/IERC20.sol"; +import {IOracle} from "src/interfaces/IOracle.sol"; + +// Market. +struct Market { + IERC20 borrowableAsset; + IERC20 collateralAsset; + IOracle borrowableOracle; + IOracle collateralOracle; + IIrm irm; + uint256 lltv; +} diff --git a/src/mocks/IrmMock.sol b/src/mocks/IrmMock.sol index ae28dbdcd..cfbcd44b8 100644 --- a/src/mocks/IrmMock.sol +++ b/src/mocks/IrmMock.sol @@ -1,12 +1,17 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity 0.8.20; +import {IIrm} from "src/interfaces/IIrm.sol"; + +import {Market} from "src/libraries/Types.sol"; import {MathLib} from "src/libraries/MathLib.sol"; +import {Id, MarketIdLib} from "src/libraries/MarketIdLib.sol"; -import "src/Blue.sol"; +import {Blue} from "src/Blue.sol"; contract IrmMock is IIrm { using MathLib for uint256; + using MarketIdLib for Market; Blue public immutable blue; @@ -15,7 +20,7 @@ contract IrmMock is IIrm { } function borrowRate(Market calldata market) external view returns (uint256) { - Id id = Id.wrap(keccak256(abi.encode(market))); + Id id = market.toId(); uint256 utilization = blue.totalBorrow(id).wDiv(blue.totalSupply(id)); // Divide by the number of seconds in a year. From a6bf6af35e2d7f0e9a1f8e6eb034008c3268c438 Mon Sep 17 00:00:00 2001 From: MerlinEgalite Date: Sun, 16 Jul 2023 17:50:39 +0200 Subject: [PATCH 15/18] docs: remove useless comment --- src/libraries/MarketIdLib.sol | 1 - src/libraries/Types.sol | 1 - 2 files changed, 2 deletions(-) diff --git a/src/libraries/MarketIdLib.sol b/src/libraries/MarketIdLib.sol index edc74addc..40ae53a84 100644 --- a/src/libraries/MarketIdLib.sol +++ b/src/libraries/MarketIdLib.sol @@ -3,7 +3,6 @@ pragma solidity ^0.8.0; import {Market} from "src/libraries/Types.sol"; -// Market id. type Id is bytes32; library MarketIdLib { diff --git a/src/libraries/Types.sol b/src/libraries/Types.sol index 9c7483db4..70943679d 100644 --- a/src/libraries/Types.sol +++ b/src/libraries/Types.sol @@ -5,7 +5,6 @@ import {IIrm} from "src/interfaces/IIrm.sol"; import {IERC20} from "src/interfaces/IERC20.sol"; import {IOracle} from "src/interfaces/IOracle.sol"; -// Market. struct Market { IERC20 borrowableAsset; IERC20 collateralAsset; From d2f3e6e0a0eb087054ca08ca966ca298f3ba173f Mon Sep 17 00:00:00 2001 From: MerlinEgalite Date: Mon, 17 Jul 2023 18:25:23 +0200 Subject: [PATCH 16/18] refactor: implement suggestions --- src/Blue.sol | 5 ++--- src/interfaces/IIrm.sol | 2 +- src/libraries/MarketIdLib.sol | 12 ------------ src/libraries/{Types.sol => MarketLib.sol} | 8 ++++++++ src/mocks/IrmMock.sol | 5 ++--- 5 files changed, 13 insertions(+), 19 deletions(-) delete mode 100644 src/libraries/MarketIdLib.sol rename src/libraries/{Types.sol => MarketLib.sol} (68%) diff --git a/src/Blue.sol b/src/Blue.sol index 2c7be56a1..df594120e 100644 --- a/src/Blue.sol +++ b/src/Blue.sol @@ -4,9 +4,8 @@ pragma solidity 0.8.20; import {IIrm} from "src/interfaces/IIrm.sol"; import {IERC20} from "src/interfaces/IERC20.sol"; -import {Market} from "src/libraries/Types.sol"; import {MathLib} from "src/libraries/MathLib.sol"; -import {Id, MarketIdLib} from "src/libraries/MarketIdLib.sol"; +import {Id, Market, MarketLib} from "src/libraries/MarketLib.sol"; import {SafeTransferLib} from "src/libraries/SafeTransferLib.sol"; uint256 constant WAD = 1e18; @@ -14,7 +13,7 @@ uint256 constant ALPHA = 0.5e18; contract Blue { using MathLib for uint256; - using MarketIdLib for Market; + using MarketLib for Market; using SafeTransferLib for IERC20; // Storage. diff --git a/src/interfaces/IIrm.sol b/src/interfaces/IIrm.sol index 9736da203..128fcba55 100644 --- a/src/interfaces/IIrm.sol +++ b/src/interfaces/IIrm.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity >=0.5.0; -import {Market} from "src/libraries/Types.sol"; +import {Market} from "src/libraries/MarketLib.sol"; interface IIrm { function borrowRate(Market calldata market) external returns (uint256); diff --git a/src/libraries/MarketIdLib.sol b/src/libraries/MarketIdLib.sol deleted file mode 100644 index 40ae53a84..000000000 --- a/src/libraries/MarketIdLib.sol +++ /dev/null @@ -1,12 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.0; - -import {Market} from "src/libraries/Types.sol"; - -type Id is bytes32; - -library MarketIdLib { - function toId(Market calldata market) internal pure returns (Id) { - return Id.wrap(keccak256(abi.encode(market))); - } -} diff --git a/src/libraries/Types.sol b/src/libraries/MarketLib.sol similarity index 68% rename from src/libraries/Types.sol rename to src/libraries/MarketLib.sol index 70943679d..2286c5ad6 100644 --- a/src/libraries/Types.sol +++ b/src/libraries/MarketLib.sol @@ -5,6 +5,8 @@ import {IIrm} from "src/interfaces/IIrm.sol"; import {IERC20} from "src/interfaces/IERC20.sol"; import {IOracle} from "src/interfaces/IOracle.sol"; +type Id is bytes32; + struct Market { IERC20 borrowableAsset; IERC20 collateralAsset; @@ -13,3 +15,9 @@ struct Market { IIrm irm; uint256 lltv; } + +library MarketLib { + function toId(Market calldata market) internal pure returns (Id) { + return Id.wrap(keccak256(abi.encode(market))); + } +} diff --git a/src/mocks/IrmMock.sol b/src/mocks/IrmMock.sol index cfbcd44b8..3571409cb 100644 --- a/src/mocks/IrmMock.sol +++ b/src/mocks/IrmMock.sol @@ -3,15 +3,14 @@ pragma solidity 0.8.20; import {IIrm} from "src/interfaces/IIrm.sol"; -import {Market} from "src/libraries/Types.sol"; import {MathLib} from "src/libraries/MathLib.sol"; -import {Id, MarketIdLib} from "src/libraries/MarketIdLib.sol"; +import {Id, Market, MarketLib} from "src/libraries/MarketLib.sol"; import {Blue} from "src/Blue.sol"; contract IrmMock is IIrm { using MathLib for uint256; - using MarketIdLib for Market; + using MarketLib for Market; Blue public immutable blue; From ba9ef7fd92b68356f5a7916aaa8a7bbbd34613ce Mon Sep 17 00:00:00 2001 From: MathisGD Date: Tue, 18 Jul 2023 10:02:19 +0200 Subject: [PATCH 17/18] test: fix fee tests --- test/forge/Blue.t.sol | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/forge/Blue.t.sol b/test/forge/Blue.t.sol index 6d90a0bee..5e622ee4a 100644 --- a/test/forge/Blue.t.sol +++ b/test/forge/Blue.t.sol @@ -252,18 +252,18 @@ contract BlueTest is Test { vm.stopPrank(); borrowableAsset.setBalance(address(this), amountLent); - blue.supply(market, amountLent); + blue.supply(market, amountLent, address(this)); vm.prank(BORROWER); - blue.borrow(market, amountBorrowed); + blue.borrow(market, amountBorrowed, BORROWER); uint256 totalSupplyBefore = blue.totalSupply(id); // Trigger an accrue. vm.warp(block.timestamp + timeElapsed); collateralAsset.setBalance(address(this), 1); - blue.supplyCollateral(market, 1); - blue.withdrawCollateral(market, 1); + blue.supplyCollateral(market, 1, address(this)); + blue.withdrawCollateral(market, 1, address(this)); uint256 totalSupplyAfter = blue.totalSupply(id); vm.assume(totalSupplyAfter > totalSupplyBefore); From 4ae15c2db4333c03fac118610cc5d4c4801d86fd Mon Sep 17 00:00:00 2001 From: MerlinEgalite Date: Tue, 18 Jul 2023 14:53:49 +0200 Subject: [PATCH 18/18] refactor: toId -> id --- src/Blue.sol | 16 ++++++++-------- src/libraries/MarketLib.sol | 2 +- src/mocks/IrmMock.sol | 2 +- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/Blue.sol b/src/Blue.sol index df594120e..1f8f15fbc 100644 --- a/src/Blue.sol +++ b/src/Blue.sol @@ -72,7 +72,7 @@ contract Blue { // Markets management. function createMarket(Market calldata market) external { - Id id = market.toId(); + Id id = market.id(); require(isIrmEnabled[market.irm], "IRM not enabled"); require(isLltvEnabled[market.lltv], "LLTV not enabled"); require(lastUpdate[id] == 0, "market already exists"); @@ -83,7 +83,7 @@ contract Blue { // Supply management. function supply(Market calldata market, uint256 amount) external { - Id id = market.toId(); + Id id = market.id(); require(lastUpdate[id] != 0, "unknown market"); require(amount != 0, "zero amount"); @@ -104,7 +104,7 @@ contract Blue { } function withdraw(Market calldata market, uint256 amount) external { - Id id = market.toId(); + Id id = market.id(); require(lastUpdate[id] != 0, "unknown market"); require(amount != 0, "zero amount"); @@ -124,7 +124,7 @@ contract Blue { // Borrow management. function borrow(Market calldata market, uint256 amount) external { - Id id = market.toId(); + Id id = market.id(); require(lastUpdate[id] != 0, "unknown market"); require(amount != 0, "zero amount"); @@ -148,7 +148,7 @@ contract Blue { } function repay(Market calldata market, uint256 amount) external { - Id id = market.toId(); + Id id = market.id(); require(lastUpdate[id] != 0, "unknown market"); require(amount != 0, "zero amount"); @@ -167,7 +167,7 @@ contract Blue { /// @dev Don't accrue interests because it's not required and it saves gas. function supplyCollateral(Market calldata market, uint256 amount) external { - Id id = market.toId(); + Id id = market.id(); require(lastUpdate[id] != 0, "unknown market"); require(amount != 0, "zero amount"); @@ -179,7 +179,7 @@ contract Blue { } function withdrawCollateral(Market calldata market, uint256 amount) external { - Id id = market.toId(); + Id id = market.id(); require(lastUpdate[id] != 0, "unknown market"); require(amount != 0, "zero amount"); @@ -195,7 +195,7 @@ contract Blue { // Liquidation. function liquidate(Market calldata market, address borrower, uint256 seized) external { - Id id = market.toId(); + Id id = market.id(); require(lastUpdate[id] != 0, "unknown market"); require(seized != 0, "zero amount"); diff --git a/src/libraries/MarketLib.sol b/src/libraries/MarketLib.sol index 2286c5ad6..4ca6f47a5 100644 --- a/src/libraries/MarketLib.sol +++ b/src/libraries/MarketLib.sol @@ -17,7 +17,7 @@ struct Market { } library MarketLib { - function toId(Market calldata market) internal pure returns (Id) { + function id(Market calldata market) internal pure returns (Id) { return Id.wrap(keccak256(abi.encode(market))); } } diff --git a/src/mocks/IrmMock.sol b/src/mocks/IrmMock.sol index 3571409cb..894582042 100644 --- a/src/mocks/IrmMock.sol +++ b/src/mocks/IrmMock.sol @@ -19,7 +19,7 @@ contract IrmMock is IIrm { } function borrowRate(Market calldata market) external view returns (uint256) { - Id id = market.toId(); + Id id = market.id(); uint256 utilization = blue.totalBorrow(id).wDiv(blue.totalSupply(id)); // Divide by the number of seconds in a year.