Skip to content

Commit

Permalink
feat(callbacks&MM): adding callback + improving MM
Browse files Browse the repository at this point in the history
  • Loading branch information
tomrpl committed Nov 15, 2023
1 parent 356b663 commit a66985e
Show file tree
Hide file tree
Showing 11 changed files with 254 additions and 38 deletions.
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,6 @@
[submodule "lib/metamorpho"]
path = lib/metamorpho
url = https://github.com/morpho-org/metamorpho
[submodule "lib/solmate"]
path = lib/solmate
url = https://github.com/transmissions11/solmate
1 change: 1 addition & 0 deletions foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,6 @@ script = "/dev/null"
[profile.test]
via-ir = false

fuzz.max_test_rejects = 100000

# See more config options https://github.com/foundry-rs/foundry/tree/master/config
1 change: 1 addition & 0 deletions lib/solmate
Submodule solmate added at e0e9ff
5 changes: 2 additions & 3 deletions remappings.txt
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
@forge-std/=lib/morpho-blue/lib/forge-std/src/

@morpho-blue/=lib/morpho-blue/src/
@morpho-blue-test/=lib/morpho-blue/test/
@morpho-blue-test/=lib/morpho-blue/test/forge/

@openzeppelin4/=lib/openzeppelin-contracts/contracts/
@openzeppelin/=lib/metamorpho/lib/openzeppelin-contracts/contracts/

@snippets/=src/

@solmate/=lib/morpho-blue/lib/solmate/src/
solmate/=lib/morpho-blue/lib/permit2/lib/solmate/
@solmate/=lib/solmate/src/

@metamorpho/=lib/metamorpho/src/
@metamorpho-test/=lib/metamorpho/test/forge/
24 changes: 6 additions & 18 deletions src/blue/CallbackSnippets.sol → src/blue/CallbacksSnippets.sol
Original file line number Diff line number Diff line change
Expand Up @@ -85,27 +85,17 @@ contract CallbacksSnippets is IMorphoSupplyCollateralCallback, IMorphoRepayCallb
}
}

// function onMorphoFlashLoan(uint256 amount, bytes memory data) external {
// require(msg.sender == address(morpho));
// (bytes4 selector, bytes memory _data) = abi.decode(data, (bytes4, bytes));
// if (selector == this.flashLoan.selector) {
// assertEq(loanToken.balanceOf(address(this)), amount);
// loanToken.approve(address(morpho), amount);
// }
// }

// leverage function
function leverageMe(
uint256 leverageFactor,
uint256 loanLeverageFactor,
uint256 initialCollateral,
uint256 collateralInitAmount,
SwapMock _swapMock,
MarketParams calldata marketParams
) public {
_setSwapMock(_swapMock);

uint256 collateralAssets = initialCollateral * leverageFactor;
uint256 loanAmount = initialCollateral * loanLeverageFactor;
uint256 collateralAssets = collateralInitAmount * leverageFactor;
uint256 loanAmount = collateralInitAmount * loanLeverageFactor;

_approveMaxTo(address(marketParams.collateralToken), address(this));

Expand Down Expand Up @@ -139,19 +129,17 @@ contract CallbacksSnippets is IMorphoSupplyCollateralCallback, IMorphoRepayCallb
);
}

// deleverage function
function deLeverageMe(
uint256 leverageFactor,
uint256 loanLeverageFactor,
uint256 initialCollateral,
uint256 collateralInitAmount,
SwapMock _swapMock,
MarketParams calldata marketParams
) public returns (uint256 amountRepayed) {
// might be redundant
_setSwapMock(_swapMock);

uint256 collateralAssets = initialCollateral * leverageFactor;
uint256 loanAmount = initialCollateral * loanLeverageFactor;
uint256 collateralAssets = collateralInitAmount * leverageFactor;
uint256 loanAmount = collateralInitAmount * loanLeverageFactor;

_approveMaxTo(address(marketParams.collateralToken), address(this));

Expand Down
2 changes: 1 addition & 1 deletion src/blue/mocks/SwapMock.sol
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// SPDX-License-Identifier: GPL-2.0-or-later
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity ^0.8.0;

import {ORACLE_PRICE_SCALE} from "@morpho-blue/libraries/ConstantsLib.sol";
Expand Down
24 changes: 14 additions & 10 deletions src/metamorpho/MetamorphoSnippets.sol
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import {Math} from "@openzeppelin/utils/math/Math.sol";
import {ERC20} from "@openzeppelin/token/ERC20/ERC20.sol";

contract MetamorphoSnippets {
uint256 constant FEE = 0.2 ether; // 20%
IMorpho public immutable morpho;
IMetaMorpho public immutable vault;

Expand Down Expand Up @@ -69,7 +68,6 @@ contract MetamorphoSnippets {
cap = vault.config(id).cap;
}

// TO TEST
function supplyAPRMarket(MarketParams memory marketParams, Market memory market)
public
view
Expand All @@ -86,10 +84,6 @@ contract MetamorphoSnippets {
supplyRate = borrowRate.wMulDown(1 ether - market.fee).wMulDown(utilization);
}

// TODO: edit comment
// a amount at 6%, B amount at 3 %:
// (a*6%) + (B*3%) / (a+b+ IDLE)

function supplyAPRVault() public view returns (uint256 avgSupplyRate) {
uint256 ratio;
uint256 queueLength = vault.withdrawQueueLength();
Expand Down Expand Up @@ -121,16 +115,26 @@ contract MetamorphoSnippets {
shares = vault.deposit(assets, onBehalf);
}

function withdrawFromVault(uint256 assets, address onBehalf) public returns (uint256 redeemed) {
// withdraw from the vault a nb of asset
function withdrawFromVaultAmount(uint256 assets, address onBehalf) public returns (uint256 redeemed) {
address receiver = onBehalf;
redeemed = vault.withdraw(assets, receiver, onBehalf);
}

// maxWithdraw from the vault
function withdrawFromVaultAll(address onBehalf) public returns (uint256 redeemed) {
address receiver = onBehalf;
uint256 assets = vault.maxWithdraw(address(this));
redeemed = vault.withdraw(assets, receiver, onBehalf);
}

function redeemAllFromVault(address receiver) public returns (uint256 redeemed) {
// maxRedeem from the vault
function redeemAllFromVault(address onBehalf) public returns (uint256 redeemed) {
address receiver = onBehalf;
uint256 maxToRedeem = vault.maxRedeem(address(this));
redeemed = vault.redeem(maxToRedeem, receiver, address(this));
redeemed = vault.redeem(maxToRedeem, receiver, onBehalf);
}

// TODO:
// // Reallocation example
// Reallocation example
}
2 changes: 1 addition & 1 deletion test/forge/blue/TestBlueSnippets.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {MathLib} from "@morpho-blue/libraries/MathLib.sol";
import {SharesMathLib} from "@morpho-blue/libraries/SharesMathLib.sol";

// we need to import everything in there
import "@morpho-blue-test/forge/BaseTest.sol";
import "@morpho-blue-test/BaseTest.sol";

contract TestIntegrationSnippets is BaseTest {
using MathLib for uint256;
Expand Down
181 changes: 181 additions & 0 deletions test/forge/blue/TestCallbacksSnippets.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity ^0.8.0;

import "@morpho-blue-test/BaseTest.sol";
import {SwapMock} from "@snippets/blue/mocks/SwapMock.sol";
import {CallbacksSnippets} from "@snippets/blue/CallbacksSnippets.sol";

contract CallbacksIntegrationTest is BaseTest {
using MathLib for uint256;
using MorphoLib for IMorpho;
using MorphoBalancesLib for IMorpho;
using MarketParamsLib for MarketParams;
using SharesMathLib for uint256;

SwapMock internal swapMock;

CallbacksSnippets public snippets;

function setUp() public virtual override {
super.setUp();
swapMock = new SwapMock(address(collateralToken), address(loanToken), address(oracle));
snippets = new CallbacksSnippets(address(morpho)); // todos add the addres of WETH, lido, wsteth
}

function testLeverageMe(uint256 collateralInitAmount) public {
// INITIALISATION

uint256 leverageFactor = 4; // nb to set
uint256 loanLeverageFactor = 3; // max here would be 3.2 = 0.8 * leverageFactor

collateralInitAmount = bound(collateralInitAmount, MIN_TEST_AMOUNT, MAX_TEST_AMOUNT / leverageFactor);

collateralToken.setBalance(address(snippets), collateralInitAmount);

uint256 collateralAmount = collateralInitAmount * leverageFactor;

oracle.setPrice(ORACLE_PRICE_SCALE);

// supplying enough liquidity in the market
vm.startPrank(SUPPLIER);
loanToken.setBalance(address(SUPPLIER), collateralAmount);
morpho.supply(marketParams, collateralAmount, 0, address(SUPPLIER), hex"");
vm.stopPrank();

// approve the swap contract
vm.startPrank(address(snippets));
loanToken.approve(address(morpho), type(uint256).max);
loanToken.approve(address(swapMock), type(uint256).max);
collateralToken.approve(address(morpho), type(uint256).max);
collateralToken.approve(address(swapMock), type(uint256).max);
vm.stopPrank();

uint256 loanAmount = collateralInitAmount * loanLeverageFactor;

snippets.leverageMe(leverageFactor, loanLeverageFactor, collateralInitAmount, swapMock, marketParams);

assertGt(morpho.borrowShares(marketParams.id(), address(snippets)), 0, "no borrow");
assertEq(morpho.collateral(marketParams.id(), address(snippets)), collateralAmount, "no collateral");
assertEq(morpho.expectedBorrowAssets(marketParams, address(snippets)), loanAmount, "no collateral");
}

function testDeLeverageMe(uint256 collateralInitAmount) public {
/// same as testLeverageMe

uint256 leverageFactor = 4; // nb to set
uint256 loanLeverageFactor = 3; // max here would be 3.2 = 0.8 * leverageFactor

collateralInitAmount = bound(collateralInitAmount, MIN_TEST_AMOUNT, MAX_TEST_AMOUNT / leverageFactor);

collateralToken.setBalance(address(snippets), collateralInitAmount);

uint256 collateralAmount = collateralInitAmount * leverageFactor;

oracle.setPrice(ORACLE_PRICE_SCALE);

vm.startPrank(SUPPLIER);
loanToken.setBalance(address(SUPPLIER), collateralAmount);
morpho.supply(marketParams, collateralAmount, 0, address(SUPPLIER), hex"");
vm.stopPrank();

vm.startPrank(address(snippets));
loanToken.approve(address(morpho), type(uint256).max);
loanToken.approve(address(swapMock), type(uint256).max);
collateralToken.approve(address(morpho), type(uint256).max);
collateralToken.approve(address(swapMock), type(uint256).max);
vm.stopPrank();

uint256 loanAmount = collateralInitAmount * loanLeverageFactor;

snippets.leverageMe(leverageFactor, loanLeverageFactor, collateralInitAmount, swapMock, marketParams);

assertGt(morpho.borrowShares(marketParams.id(), address(snippets)), 0, "no borrow");
assertEq(morpho.collateral(marketParams.id(), address(snippets)), collateralAmount, "no collateral");
assertEq(morpho.expectedBorrowAssets(marketParams, address(snippets)), loanAmount, "no collateral");

/// end of testLeverageMe

uint256 amountRepayed =
snippets.deLeverageMe(leverageFactor, loanLeverageFactor, collateralInitAmount, swapMock, marketParams);

assertEq(morpho.borrowShares(marketParams.id(), address(snippets)), 0, "no borrow");
assertEq(amountRepayed, loanAmount, "no repaid");
}

struct LiquidateTestParams {
uint256 amountCollateral;
uint256 amountSupplied;
uint256 amountBorrowed;
uint256 priceCollateral;
uint256 lltv;
}

// TODOS: implement the following function
// function testLiquidateWithoutCollateral(LiquidateTestParams memory params, uint256 amountSeized) public {
// _setLltv(_boundTestLltv(params.lltv));
// (params.amountCollateral, params.amountBorrowed, params.priceCollateral) =
// _boundUnhealthyPosition(params.amountCollateral, params.amountBorrowed, params.priceCollateral);

// vm.assume(params.amountCollateral > 1);

// params.amountSupplied =
// bound(params.amountSupplied, params.amountBorrowed, params.amountBorrowed + MAX_TEST_AMOUNT);
// _supply(params.amountSupplied);

// collateralToken.setBalance(BORROWER, params.amountCollateral);

// oracle.setPrice(type(uint256).max / params.amountCollateral);

// vm.startPrank(BORROWER);
// morpho.supplyCollateral(marketParams, params.amountCollateral, BORROWER, hex"");
// morpho.borrow(marketParams, params.amountBorrowed, 0, BORROWER, BORROWER);
// vm.stopPrank();

// oracle.setPrice(params.priceCollateral);

// // uint256 borrowShares = morpho.borrowShares(id, BORROWER);
// uint256 liquidationIncentiveFactor = _liquidationIncentiveFactor(marketParams.lltv);
// uint256 maxSeized = params.amountBorrowed.wMulDown(liquidationIncentiveFactor).mulDivDown(
// ORACLE_PRICE_SCALE, params.priceCollateral
// );
// vm.assume(maxSeized != 0);

// amountSeized = bound(amountSeized, 1, Math.min(maxSeized, params.amountCollateral - 1));

// uint256 expectedRepaid =
// amountSeized.mulDivUp(params.priceCollateral, ORACLE_PRICE_SCALE).wDivUp(liquidationIncentiveFactor);
// // uint256 expectedRepaidShares =
// // expectedRepaid.toSharesDown(morpho.totalBorrowAssets(id), morpho.totalBorrowShares(id));

// vm.startPrank(address(snippets));
// loanToken.approve(address(morpho), type(uint256).max);
// loanToken.approve(address(swapMock), type(uint256).max);
// collateralToken.approve(address(morpho), type(uint256).max);
// collateralToken.approve(address(swapMock), type(uint256).max);
// loanToken.approve(address(snippets), type(uint256).max);
// collateralToken.approve(address(snippets), type(uint256).max);
// loanToken.setBalance(address(snippets), params.amountBorrowed);

// // vm.prank(LIQUIDATOR);

// (uint256 returnSeized, uint256 returnRepaid) =
// snippets.liquidateWithoutCollat(BORROWER, params.amountBorrowed, amountSeized, swapMock, marketParams);
// // morpho.liquidate(marketParams, BORROWER, amountSeized, 0, hex"");
// // uint256 expectedCollateral = params.amountCollateral - amountSeized;
// // uint256 expectedBorrowed = params.amountBorrowed - expectedRepaid;
// // uint256 expectedBorrowShares = borrowShares - expectedRepaidShares;

// assertEq(returnSeized, amountSeized, "returned seized amount");
// assertEq(returnRepaid, expectedRepaid, "returned asset amount");
// // assertEq(morpho.borrowShares(id, BORROWER), expectedBorrowShares, "borrow shares");
// // assertEq(morpho.totalBorrowAssets(id), expectedBorrowed, "total borrow");
// // assertEq(morpho.totalBorrowShares(id), expectedBorrowShares, "total borrow shares");
// // assertEq(morpho.collateral(id, BORROWER), expectedCollateral, "collateral");
// // assertEq(loanToken.balanceOf(BORROWER), params.amountBorrowed, "borrower balance");
// // assertEq(loanToken.balanceOf(LIQUIDATOR), expectedBorrowed, "liquidator balance");
// // assertEq(loanToken.balanceOf(address(morpho)), params.amountSupplied - expectedBorrowed, "morpho
// balance");
// // assertEq(collateralToken.balanceOf(address(morpho)), expectedCollateral, "morpho collateral balance");
// // assertEq(collateralToken.balanceOf(LIQUIDATOR), amountSeized, "liquidator collateral balance");
// }
}
32 changes: 32 additions & 0 deletions test/forge/blue/mocks/TestSwapMock.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity ^0.8.0;

import "@morpho-blue-test/BaseTest.sol";
import {SwapMock} from "@snippets/blue/mocks/SwapMock.sol";

contract TestIntegrationSnippets is BaseTest {
SwapMock internal swapMock;

function setUp() public virtual override {
super.setUp();
swapMock = new SwapMock(address(collateralToken), address(loanToken), address(oracle));
}

function testSwapCollatToLoan(uint256 amount) public {
amount = bound(amount, MIN_TEST_AMOUNT, MAX_TEST_AMOUNT);
collateralToken.setBalance(address(this), amount);
collateralToken.approve(address(swapMock), type(uint256).max);

uint256 swappedAssets = swapMock.swapCollatToLoan(amount);
assertEq(swappedAssets, amount, " error in swap");
}

function testSwapLoanToCollat(uint256 amount) public {
amount = bound(amount, MIN_TEST_AMOUNT, MAX_TEST_AMOUNT);
loanToken.setBalance(address(this), amount);
loanToken.approve(address(swapMock), type(uint256).max);

uint256 swappedAssets = swapMock.swapLoanToCollat(amount);
assertEq(swappedAssets, amount, " error in swap");
}
}
Loading

0 comments on commit a66985e

Please sign in to comment.