Skip to content

Commit

Permalink
feat(extension): Add support for alternative Flashloan providers to m…
Browse files Browse the repository at this point in the history
…igration extension (#167)

* Add morpho flashloan implementation

* Balancer flashloan option

* Adjust subsidy amounts

* Rename old migrate method to migrateAave

* Rename to MigrationExtension

* Reorder imports

* Add morpho and balancer address as constructor args
  • Loading branch information
ckoopmann authored Mar 8, 2024
1 parent f1d2baf commit f2907ac
Show file tree
Hide file tree
Showing 7 changed files with 312 additions and 32 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ import { SafeCast } from "@openzeppelin/contracts/utils/SafeCast.sol";
import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
import { SafeMath } from "@openzeppelin/contracts/math/SafeMath.sol";

import { IBalancerVault } from "../interfaces/IBalancerVault.sol";
import { IMorpho } from "../interfaces/IMorpho.sol";
import { FlashLoanSimpleReceiverBase } from "../lib/FlashLoanSimpleReceiverBase.sol";
import { IPoolAddressesProvider } from "../interfaces/IPoolAddressesProvider.sol";

Expand All @@ -39,7 +41,7 @@ import { ITradeModule } from "../interfaces/ITradeModule.sol";
import { PreciseUnitMath } from "../lib/PreciseUnitMath.sol";

/**
* @title AaveMigrationExtension
* @title MigrationExtension
* @author Index Coop
* @notice This extension facilitates the migration of a SetToken's position from an unwrapped collateral
* asset to another SetToken that consists solely of Aave's wrapped collateral asset. The migration is
Expand All @@ -49,7 +51,7 @@ import { PreciseUnitMath } from "../lib/PreciseUnitMath.sol";
* redeeming any excess wrapped SetToken. This process is specifically designed to efficiently migrate
* the SetToken's collateral using only the TradeModule on the SetToken.
*/
contract AaveMigrationExtension is BaseExtension, FlashLoanSimpleReceiverBase, IERC721Receiver {
contract MigrationExtension is BaseExtension, FlashLoanSimpleReceiverBase, IERC721Receiver {
using PreciseUnitMath for uint256;
using SafeCast for int256;
using SafeERC20 for IERC20;
Expand Down Expand Up @@ -81,13 +83,15 @@ contract AaveMigrationExtension is BaseExtension, FlashLoanSimpleReceiverBase, I
ITradeModule public immutable tradeModule;
IDebtIssuanceModule public immutable issuanceModule;
INonfungiblePositionManager public immutable nonfungiblePositionManager;
IMorpho public immutable morpho;
IBalancerVault public immutable balancer;

uint256[] public tokenIds; // UniV3 LP Token IDs

/* ============ Constructor ============ */

/**
* @notice Initializes the AaveMigrationExtension with immutable migration variables.
* @notice Initializes the MigrationExtension with immutable migration variables.
* @param _manager BaseManager contract for managing the SetToken's operations and permissions.
* @param _underlyingToken Address of the underlying token to be migrated.
* @param _aaveToken Address of Aave's wrapped collateral asset.
Expand All @@ -105,7 +109,9 @@ contract AaveMigrationExtension is BaseExtension, FlashLoanSimpleReceiverBase, I
ITradeModule _tradeModule,
IDebtIssuanceModule _issuanceModule,
INonfungiblePositionManager _nonfungiblePositionManager,
IPoolAddressesProvider _addressProvider
IPoolAddressesProvider _addressProvider,
IMorpho _morpho,
IBalancerVault _balancer
)
public
BaseExtension(_manager)
Expand All @@ -119,6 +125,8 @@ contract AaveMigrationExtension is BaseExtension, FlashLoanSimpleReceiverBase, I
tradeModule = _tradeModule;
issuanceModule = _issuanceModule;
nonfungiblePositionManager = _nonfungiblePositionManager;
morpho = _morpho;
balancer = _balancer;
}

/* ========== External Functions ========== */
Expand Down Expand Up @@ -258,12 +266,13 @@ contract AaveMigrationExtension is BaseExtension, FlashLoanSimpleReceiverBase, I
/**
* @notice OPERATOR ONLY: Migrates a SetToken's position from an unwrapped collateral asset to another SetToken
* that consists solely of Aave's wrapped collateral asset
* using Aave Flashloan
* @param _decodedParams The decoded migration parameters.
* @param _underlyingLoanAmount The amount of unwrapped collateral asset to be borrowed via flash loan.
* @param _maxSubsidy The maximum amount of unwrapped collateral asset to be transferred to the Extension as a subsidy.
* @return underlyingOutputAmount The amount of unwrapped collateral asset returned to the operator.
*/
function migrate(
function migrateAave(
DecodedParams memory _decodedParams,
uint256 _underlyingLoanAmount,
uint256 _maxSubsidy
Expand Down Expand Up @@ -293,6 +302,109 @@ contract AaveMigrationExtension is BaseExtension, FlashLoanSimpleReceiverBase, I
underlyingOutputAmount = _returnExcessUnderlying();
}

/**
* @notice OPERATOR ONLY: Migrates a SetToken's position from an unwrapped collateral asset to another SetToken
* that consists solely of Aave's wrapped collateral asset
* using Balancer Flashloan
* @param _decodedParams The decoded migration parameters.
* @param _underlyingLoanAmount The amount of unwrapped collateral asset to be borrowed via flash loan.
* @param _maxSubsidy The maximum amount of unwrapped collateral asset to be transferred to the Extension as a subsidy.
* @return underlyingOutputAmount The amount of unwrapped collateral asset returned to the operator.
*/
function migrateBalancer(
DecodedParams memory _decodedParams,
uint256 _underlyingLoanAmount,
uint256 _maxSubsidy
)
external
onlyOperator
returns (uint256 underlyingOutputAmount)
{
// Subsidize the migration
if (_maxSubsidy > 0) {
underlyingToken.transferFrom(msg.sender, address(this), _maxSubsidy);
}

// Encode migration parameters for flash loan callback
bytes memory params = abi.encode(_decodedParams);
address[] memory tokens = new address[](1);
tokens[0] = address(underlyingToken);
uint256[] memory amounts = new uint256[](1);
amounts[0] = _underlyingLoanAmount;

// Request flash loan for the underlying token
balancer.flashLoan(address(this), tokens, amounts, params);

// Return remaining underlying token to the operator
underlyingOutputAmount = _returnExcessUnderlying();
}

/**
* @dev Callback function for Balancer flashloan
*/
function receiveFlashLoan(
IERC20[] memory tokens,
uint256[] memory amounts,
uint256[] memory feeAmounts,
bytes memory params
) external {
require(msg.sender == address(balancer));
// Decode parameters and migrate
DecodedParams memory decodedParams = abi.decode(params, (DecodedParams));
_migrate(decodedParams);

underlyingToken.transfer(address(balancer), amounts[0] + feeAmounts[0]);
}


/**
* @notice OPERATOR ONLY: Migrates a SetToken's position from an unwrapped collateral asset to another SetToken
* that consists solely of Aave's wrapped collateral asset
* using Morpho Flashloan
* @param _decodedParams The decoded migration parameters.
* @param _underlyingLoanAmount The amount of unwrapped collateral asset to be borrowed via flash loan.
* @param _maxSubsidy The maximum amount of unwrapped collateral asset to be transferred to the Extension as a subsidy.
* @return underlyingOutputAmount The amount of unwrapped collateral asset returned to the operator.
*/
function migrateMorpho(
DecodedParams memory _decodedParams,
uint256 _underlyingLoanAmount,
uint256 _maxSubsidy
)
external
onlyOperator
returns (uint256 underlyingOutputAmount)
{
// Subsidize the migration
if (_maxSubsidy > 0) {
underlyingToken.transferFrom(msg.sender, address(this), _maxSubsidy);
}

// Encode migration parameters for flash loan callback
bytes memory params = abi.encode(_decodedParams);

// Request flash loan for the underlying token
morpho.flashLoan(address(underlyingToken), _underlyingLoanAmount, params);

// Return remaining underlying token to the operator
underlyingOutputAmount = _returnExcessUnderlying();
}

/**
* @dev Callback function for Morpho Flashloan
*/
function onMorphoFlashLoan(uint256 assets, bytes calldata params) external
{
require(msg.sender == address(morpho), "MigrationExtension: invalid flashloan sender");

// Decode parameters and migrate
DecodedParams memory decodedParams = abi.decode(params, (DecodedParams));
_migrate(decodedParams);

underlyingToken.approve(address(morpho), assets);
}


/**
* @dev Callback function for Aave V3 flash loan, executed post-loan. It decodes the provided parameters, conducts the migration, and repays the flash loan.
* @param amount The amount borrowed.
Expand Down
4 changes: 4 additions & 0 deletions contracts/interfaces/IBalancerVault.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
pragma solidity 0.6.10;
interface IBalancerVault {
function flashLoan(address recipient, address[] memory tokens, uint256[] memory amounts, bytes memory userData) external;
}
151 changes: 151 additions & 0 deletions contracts/interfaces/IMorpho.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
// SPDX-License-bytes32entifier: UNLICENSED
pragma solidity 0.6.10;
pragma experimental ABIEncoderV2;

interface IMorpho {

struct Authorization {
address authorizer;
address authorized;
bool isAuthorized;
uint256 nonce;
uint256 deadline;
}

struct MarketParams {
address loanToken;
address collateralToken;
address oracle;
address irm;
uint256 lltv;
}

struct Signature {
uint8 v;
bytes32 r;
bytes32 s;
}

event AccrueInterest(bytes32 indexed id, uint256 prevBorrowRate, uint256 interest, uint256 feeShares);
event Borrow(
bytes32 indexed id,
address caller,
address indexed onBehalf,
address indexed receiver,
uint256 assets,
uint256 shares
);
event CreateMarket(bytes32 indexed id, MarketParams marketParams);
event EnableIrm(address indexed irm);
event EnableLltv(uint256 lltv);
event FlashLoan(address indexed caller, address indexed token, uint256 assets);
event IncrementNonce(address indexed caller, address indexed authorizer, uint256 usedNonce);
event Liquidate(
bytes32 indexed id,
address indexed caller,
address indexed borrower,
uint256 repaidAssets,
uint256 repaidShares,
uint256 seizedAssets,
uint256 badDebtAssets,
uint256 badDebtShares
);
event Repay(bytes32 indexed id, address indexed caller, address indexed onBehalf, uint256 assets, uint256 shares);
event SetAuthorization(
address indexed caller, address indexed authorizer, address indexed authorized, bool newIsAuthorized
);
event SetFee(bytes32 indexed id, uint256 newFee);
event SetFeeRecipient(address indexed newFeeRecipient);
event SetOwner(address indexed newOwner);
event Supply(bytes32 indexed id, address indexed caller, address indexed onBehalf, uint256 assets, uint256 shares);
event SupplyCollateral(bytes32 indexed id, address indexed caller, address indexed onBehalf, uint256 assets);
event Withdraw(
bytes32 indexed id,
address caller,
address indexed onBehalf,
address indexed receiver,
uint256 assets,
uint256 shares
);
event WithdrawCollateral(
bytes32 indexed id, address caller, address indexed onBehalf, address indexed receiver, uint256 assets
);


function DOMAIN_SEPARATOR() external view returns (bytes32);
function accrueInterest(MarketParams memory marketParams) external;
function borrow(
MarketParams memory marketParams,
uint256 assets,
uint256 shares,
address onBehalf,
address receiver
) external returns (uint256, uint256);
function createMarket(MarketParams memory marketParams) external;
function enableIrm(address irm) external;
function enableLltv(uint256 lltv) external;
function extSloads(bytes32[] memory slots) external view returns (bytes32[] memory res);
function feeRecipient() external view returns (address);
function flashLoan(address token, uint256 assets, bytes memory data) external;
function idToMarketParams(bytes32)
external
view
returns (address loanToken, address collateralToken, address oracle, address irm, uint256 lltv);
function isAuthorized(address, address) external view returns (bool);
function isIrmEnabled(address) external view returns (bool);
function isLltvEnabled(uint256) external view returns (bool);
function liquidate(
MarketParams memory marketParams,
address borrower,
uint256 seizedAssets,
uint256 repaidShares,
bytes memory data
) external returns (uint256, uint256);
function market(bytes32)
external
view
returns (
uint128 totalSupplyAssets,
uint128 totalSupplyShares,
uint128 totalBorrowAssets,
uint128 totalBorrowShares,
uint128 lastUpdate,
uint128 fee
);
function nonce(address) external view returns (uint256);
function owner() external view returns (address);
function position(bytes32, address)
external
view
returns (uint256 supplyShares, uint128 borrowShares, uint128 collateral);
function repay(
MarketParams memory marketParams,
uint256 assets,
uint256 shares,
address onBehalf,
bytes memory data
) external returns (uint256, uint256);
function setAuthorization(address authorized, bool newIsAuthorized) external;
function setAuthorizationWithSig(Authorization memory authorization, Signature memory signature) external;
function setFee(MarketParams memory marketParams, uint256 newFee) external;
function setFeeRecipient(address newFeeRecipient) external;
function setOwner(address newOwner) external;
function supply(
MarketParams memory marketParams,
uint256 assets,
uint256 shares,
address onBehalf,
bytes memory data
) external returns (uint256, uint256);
function supplyCollateral(MarketParams memory marketParams, uint256 assets, address onBehalf, bytes memory data)
external;
function withdraw(
MarketParams memory marketParams,
uint256 assets,
uint256 shares,
address onBehalf,
address receiver
) external returns (uint256, uint256);
function withdrawCollateral(MarketParams memory marketParams, uint256 assets, address onBehalf, address receiver)
external;
}
Loading

0 comments on commit f2907ac

Please sign in to comment.