morpho-org/morpho-blue

Contango integration feedback

Closed this issue ยท 4 comments

Based on our efforts to integrate morpho blue, there are a few improvement points that were detected that would benefit us and other protocols integrating with you:

1. Expose a way to convert from shares to assets and vice versa when repaying debt

Even though we can go look at what you do in the code, import your lib and call the same functions, it feels very leaky having to dive this deep and import internal calculation libs when it could be a couple view endpoints.

e.g. previewRepayWithAssets(assets) => shares and previewRepayWithShares(shares) => assets

2. Allow flash loan to return data

Allow flashLoan() to return data so data can be passed back without using storage

PR #570

3. Ability to to do a "flash borrow"

Introduce a callback on borrow() before checking for collateralisation ratio, therefore allowing for a transaction to borrow in advance, come up with collateral and then deposit.

In our particular case, we'd save a flash loan operation and its involved gas costs.

e.g. Going long 1 ETHUSDC using 500 USDC as collateral when 1 ETH = 1000 USDC

Without flash borrow

  1. Flash loan 500 USDC
  2. Swap 500 collateral + 500 flash loan = 1000 USDC for 1 ETH
  3. Provide 1 ETH as collateral
  4. Borrow 500 USDC
  5. Repay flash loan

With flash borrow

  1. Initiate flash borrow 500 USDC
  2. Swap 500 collateral + 500 flash loan = 1000 USDC for 1 ETH
  3. Provide 1 ETH as collateral
  4. Flash borrow finalises without issue since there's 1 ETH collateral for 500 USDC debt

Thanks for the suggestions ! Here are some thoughts about them :

  1. I think it depends on the use case. So for each use cases it's only a couple of functions but in the end it adds up. For example, you could also want the same fonctions but for each other end point, and then you could want to duplicate those to have the cases with/without interest accrued, etc. The design choice for Blue was to make the code as simple as possible to allow for an easiest integration by looking at the code. For the use cases you are mentioning, it requires calling one conversion fonction, with the result of the functions for the proper balances (all of those functions are available in the libs, including for getting up-to-date balances)
  2. Interesting suggestion, we can it discuss in the PR. It would be nice to have an example of use case
  3. For the use case you mention you could call supplyCollateral and borrow in the callback, which would allow you to do the transfer of collateral at the end. Do you see other use cases that this misses ?

Thanks for the suggestions ! Here are some thoughts about them :

1. I think it depends on the use case. So for each use cases it's only a couple of functions but in the end it adds up. For example, you could also want the same fonctions but for each other end point, and then you could want to duplicate those to have the cases with/without interest accrued, etc. The design choice for Blue was to make the code as simple as possible to allow for an easiest integration by looking at the code. For the use cases you are mentioning, it requires calling one conversion fonction, with the result of the functions for the proper balances (all of those functions are available in the libs, including for getting up-to-date balances)

2. Interesting suggestion, we can it discuss in the PR. It would be nice to have an example pf use case.

3. For the use case you mention you could call supplyCollateral and borrow in the callback, which would allow you to do the transfer of collateral at the end. Do you see other use cases that this misses ?

The problem on 3 is that we need to swap first to find out exactly how much collateral we have. This is due to externalising the swap calculation so that we can benefit from swap aggregator (we just receive the swapBytes to execte on the smart contract size) and very few aggregators supporting you to specify how much you want as the output of the swap

Ok very clear, I understand how that would make it easier. In the end you still need to check for slippage though, so I assume that you at least have a ballpark of the collateral that you are getting.

Additionally, adding a callback to borrow would not be free and security implications would need to be considered. In particular, in the current state, just moving the health check to the end of the function also moves the oracle call after the callback. Right now the argument of the safety of callbacks is that they happen in a valid state, after everything except transfers which is ok because we don't use balances in the code (so it can only revert on transfers). That argument would not hold anymore

Agree with @QGarchery's POV on 1. and 3.

Closing this one and we let's discuss 2. directly in #570