diff --git a/contracts/mainnet/connectors/refinance-notional/events.sol b/contracts/mainnet/connectors/refinance-notional/events.sol index 0c6a1aab..38981e7e 100644 --- a/contracts/mainnet/connectors/refinance-notional/events.sol +++ b/contracts/mainnet/connectors/refinance-notional/events.sol @@ -5,6 +5,8 @@ import { TokenInterface } from "./interface.sol"; contract Events { event LogRefinance( + uint256 indexed source, + uint256 indexed target, uint256 collateralFee, uint256 debtFee, address[] tokens, diff --git a/contracts/mainnet/connectors/refinance-notional/helpers.sol b/contracts/mainnet/connectors/refinance-notional/helpers.sol index eb86e238..2b44a63c 100644 --- a/contracts/mainnet/connectors/refinance-notional/helpers.sol +++ b/contracts/mainnet/connectors/refinance-notional/helpers.sol @@ -4,7 +4,7 @@ pragma experimental ABIEncoderV2; // import { Helpers } from "./helpers.sol"; import { Basic } from "../../common/basic.sol"; -import { Token, NotionalInterface, BalanceAction, BalanceActionWithTrades, DepositActionType, AaveV2LendingPoolProviderInterface, AaveV2DataProviderInterface, AaveV2Interface } from "./interface.sol"; +import { Token, NotionalInterface, BalanceAction, BalanceActionWithTrades, DepositActionType, AaveV2LendingPoolProviderInterface, AaveV2DataProviderInterface, AaveV2Interface, AaveV3PoolProviderInterface, AaveV3Interface, AaveV3DataProviderInterface } from "./interface.sol"; import { TokenInterface } from "../../common/interfaces.sol"; import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/SafeERC20.sol"; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; @@ -12,6 +12,13 @@ import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; contract Helpers is Basic { using SafeERC20 for IERC20; + enum Protocol { + AaveV2, + AaveV3, + Compound, + Notional + } + uint256 internal constant LEND_TRADE = 0; uint256 internal constant BORROW_TRADE = 1; uint256 internal constant INTERNAL_TOKEN_PRECISION = 1e8; @@ -21,7 +28,7 @@ contract Helpers is Basic { address payable constant feeCollector = 0xb1DC62EC38E6E3857a887210C38418E4A17Da5B2; - AaveV2DataProviderInterface internal constant aaveData = + AaveV2DataProviderInterface internal constant aaveV2Data = AaveV2DataProviderInterface(0x057835Ad21a177dbdd3090bB1CAE03EaCF78Fc6d); NotionalInterface internal constant notional = @@ -34,6 +41,17 @@ contract Helpers is Basic { AaveV2LendingPoolProviderInterface( 0xB53C1a33016B2DC2fF3653530bfF1848a515c8c5 ); + /** + * @dev Aave V3 Pool Provider + */ + AaveV3PoolProviderInterface internal constant getAaveV3Provider = + AaveV3PoolProviderInterface(0xA55125A90d75a95EC00130E8E8C197dB5641Eb19); //rinkeby address + + /** + * @dev Aave V3 Pool Data Provider + */ + AaveV3DataProviderInterface internal constant aaveV3Data = + AaveV3DataProviderInterface(0x256bBbeDbA70a1240a1EB64210abB1b063267408); //rinkeby address /** * @dev get Referral Code @@ -41,6 +59,9 @@ contract Helpers is Basic { uint16 internal constant getReferralCode = 3228; struct NotionalBorrowData { + // refinance to Notional from source + Protocol source; + // length of tokens uint256 length; // debt fee uint256 fee; @@ -73,6 +94,17 @@ contract Helpers is Basic { ); } + // withdraw balance of Aave v3 + function getWithdrawBalanceV3( + AaveV3DataProviderInterface aaveData, + address token + ) internal view returns (uint256 bal) { + (bal, , , , , , , , ) = aaveData.getUserReserveData( + token, + address(this) + ); + } + function getAssetOrUnderlyingToken(uint16 currencyId, bool underlying) internal view @@ -109,12 +141,29 @@ contract Helpers is Basic { returns (uint256 bal) { if (rateMode == 1) { - (, bal, , , , , , , ) = aaveData.getUserReserveData( + (, bal, , , , , , , ) = aaveV2Data.getUserReserveData( token, address(this) ); } else { - (, , bal, , , , , , ) = aaveData.getUserReserveData( + (, , bal, , , , , , ) = aaveV2Data.getUserReserveData( + token, + address(this) + ); + } + } + + function getAaveV3PaybackAmt(uint256 rateMode, address token) + internal + returns (uint256 bal) + { + if (rateMode == 1) { + (, bal, , , , , , , ) = aaveV3Data.getUserReserveData( + token, + address(this) + ); + } else { + (, , bal, , , , , , ) = aaveV3Data.getUserReserveData( token, address(this) ); diff --git a/contracts/mainnet/connectors/refinance-notional/helpers/aaveV2.sol b/contracts/mainnet/connectors/refinance-notional/helpers/aaveV2.sol index 7e74d5f0..8a59f557 100644 --- a/contracts/mainnet/connectors/refinance-notional/helpers/aaveV2.sol +++ b/contracts/mainnet/connectors/refinance-notional/helpers/aaveV2.sol @@ -7,7 +7,7 @@ import { Helpers } from "../helpers.sol"; import { AaveV2DataProviderInterface, AaveV2LendingPoolProviderInterface, AaveV2Interface } from "../interface.sol"; import { TokenInterface } from "../../../common/interfaces.sol"; -contract AaveHelpers is Helpers { +contract AaveV2Helpers is Helpers { // payback token to Aave V2, amts sent already checked for MAX function _aaveV2PaybackOne( AaveV2Interface aave, diff --git a/contracts/mainnet/connectors/refinance-notional/helpers/aaveV3.sol b/contracts/mainnet/connectors/refinance-notional/helpers/aaveV3.sol new file mode 100644 index 00000000..ef967c35 --- /dev/null +++ b/contracts/mainnet/connectors/refinance-notional/helpers/aaveV3.sol @@ -0,0 +1,72 @@ +//SPDX-License-Identifier: MIT +pragma solidity ^0.7.6; +pragma experimental ABIEncoderV2; + +import { Helpers } from "../helpers.sol"; + +import { AaveV3DataProviderInterface, AaveV3PoolProviderInterface, AaveV3Interface } from "../interface.sol"; +import { TokenInterface } from "../../../common/interfaces.sol"; + +contract AaveV3Helpers is Helpers { + // payback token to Aave V2, amts sent already checked for MAX + function _aaveV3PaybackOne( + AaveV3Interface aave, + TokenInterface token, + uint256 amt, + uint256 rateMode + ) internal { + if (amt > 0) { + bool isEth = address(token) == wethAddr; + if (isEth) convertEthToWeth(isEth, token, amt); + approve(token, address(aave), amt); + aave.repay(address(token), amt, rateMode, address(this)); + } + } + + function _aaveV3Payback( + uint256 length, + AaveV3Interface aave, + TokenInterface[] memory tokens, + uint256[] memory amts, + uint256[] memory rateModes + ) internal { + for (uint256 i = 0; i < length; i++) { + _aaveV3PaybackOne(aave, tokens[i], amts[i], rateModes[i]); + } + } + + // withdraw aToken from aaveV2 + function _aaveV3WithdrawOne( + AaveV3Interface aave, + AaveV3DataProviderInterface aaveData, + TokenInterface token, + uint256 amt + ) internal returns (uint256 _amt) { + if (amt > 0) { + bool isEth = address(token) == wethAddr; + aave.withdraw(address(token), amt, address(this)); + _amt = amt == uint256(-1) + ? getWithdrawBalanceV3(aaveData, address(token)) + : amt; + convertWethToEth(isEth, token, _amt); + } + } + + function _aaveV3Withdraw( + AaveV3Interface aave, + AaveV3DataProviderInterface aaveData, + uint256 length, + TokenInterface[] memory tokens, + uint256[] memory amts + ) internal returns (uint256[] memory) { + uint256[] memory finalAmts = new uint256[](length); + for (uint256 i = 0; i < length; i++) { + finalAmts[i] = _aaveV3WithdrawOne( + aave, + aaveData, + tokens[i], + amts[i] + ); + } + } +} diff --git a/contracts/mainnet/connectors/refinance-notional/helpers/notional.sol b/contracts/mainnet/connectors/refinance-notional/helpers/notional.sol index d4ac0233..10c0bbd6 100644 --- a/contracts/mainnet/connectors/refinance-notional/helpers/notional.sol +++ b/contracts/mainnet/connectors/refinance-notional/helpers/notional.sol @@ -52,7 +52,16 @@ contract NotionalHelpers is Helpers { //calculating payback amounts for Aave v2 if (_amt == uint256(-1)) { - _amt = getAaveV2PaybackAmt(data.rateModes[i], data.tokens[i]); + if (data.source == Protocol.AaveV2) + _amt = getAaveV2PaybackAmt( + data.rateModes[i], + data.tokens[i] + ); + else if(data.source == Protocol.AaveV3) + _amt = getAaveV3PaybackAmt( + data.rateModes[i], + data.tokens[i] + ); } finalAmts[i] = _amt; diff --git a/contracts/mainnet/connectors/refinance-notional/interface.sol b/contracts/mainnet/connectors/refinance-notional/interface.sol index 65b5e83f..74ab352c 100644 --- a/contracts/mainnet/connectors/refinance-notional/interface.sol +++ b/contracts/mainnet/connectors/refinance-notional/interface.sol @@ -169,4 +169,81 @@ interface AaveV2Interface { interface AaveV2LendingPoolProviderInterface { function getLendingPool() external view returns (address); +} + +// Aave V3 interfaces +interface AaveV3Interface { + function supply( + address asset, + uint256 amount, + address onBehalfOf, + uint16 referralCode + ) external; + + function withdraw( + address asset, + uint256 amount, + address to + ) external returns (uint256); + + function borrow( + address asset, + uint256 amount, + uint256 interestRateMode, + uint16 referralCode, + address onBehalfOf + ) external; + + function repay( + address asset, + uint256 amount, + uint256 interestRateMode, + address onBehalfOf + ) external returns (uint256); + + function repayWithATokens( + address asset, + uint256 amount, + uint256 interestRateMode + ) external returns (uint256); + + function setUserUseReserveAsCollateral(address asset, bool useAsCollateral) + external; + + function setUserEMode(uint8 categoryId) external; +} + +interface AaveV3PoolProviderInterface { + function getPool() external view returns (address); +} + +interface AaveV3DataProviderInterface { + function getReserveTokensAddresses(address _asset) + external + view + returns ( + address aTokenAddress, + address stableDebtTokenAddress, + address variableDebtTokenAddress + ); + + function getUserReserveData(address _asset, address _user) + external + view + returns ( + uint256 currentATokenBalance, + uint256 currentStableDebt, + uint256 currentVariableDebt, + uint256 principalStableDebt, + uint256 scaledVariableDebt, + uint256 stableBorrowRate, + uint256 liquidityRate, + uint40 stableRateLastUpdated, + bool usageAsCollateralEnabled + ); + + function getReserveEModeCategory(address asset) + external + view + returns (uint256); } \ No newline at end of file diff --git a/contracts/mainnet/connectors/refinance-notional/main.sol b/contracts/mainnet/connectors/refinance-notional/main.sol index d267fb59..13b36d1e 100644 --- a/contracts/mainnet/connectors/refinance-notional/main.sol +++ b/contracts/mainnet/connectors/refinance-notional/main.sol @@ -3,16 +3,19 @@ pragma solidity ^0.7.6; pragma experimental ABIEncoderV2; /** - * @title Refinance Notional, Aave v2. - * @dev Refinancing. + * @title Refinance. + * @dev Refinancing among Notional, Aave v2, Aave v3. */ -import { AaveV2Interface, AaveV2DataProviderInterface, AaveV2LendingPoolProviderInterface } from "./interface.sol"; +import { AaveV2Interface, AaveV2DataProviderInterface, AaveV2LendingPoolProviderInterface, AaveV3Interface, AaveV3DataProviderInterface, AaveV3PoolProviderInterface } from "./interface.sol"; import { TokenInterface } from "../../common/interfaces.sol"; -import { AaveHelpers } from "./helpers/aaveV2.sol"; +import { AaveV2Helpers } from "./helpers/aaveV2.sol"; +import { AaveV3Helpers } from "./helpers/aaveV3.sol"; import { NotionalHelpers } from "./helpers/notional.sol"; -contract RefinanceResolver is AaveHelpers, NotionalHelpers { +contract RefinanceResolver is AaveV2Helpers, AaveV3Helpers, NotionalHelpers { struct RefinanceData { + Protocol source; + Protocol target; uint256 collateralFee; uint256 debtFee; uint256[] currencyIDs; @@ -27,8 +30,10 @@ contract RefinanceResolver is AaveHelpers, NotionalHelpers { } struct RefinanceInternalData { - AaveV2Interface aave; - AaveV2DataProviderInterface aaveData; + AaveV2Interface aaveV2; + AaveV2DataProviderInterface aaveV2Data; + AaveV3Interface aaveV3; + AaveV3DataProviderInterface aaveV3Data; uint256[] depositAmts; uint256[] paybackAmts; address[] tokens; @@ -53,11 +58,15 @@ contract RefinanceResolver is AaveHelpers, NotionalHelpers { require(data.mintNTokens.length == length, "length-mismatch"); RefinanceInternalData memory refinanceInternalData; - refinanceInternalData.aave = AaveV2Interface( + refinanceInternalData.aaveV2 = AaveV2Interface( AaveV2LendingPoolProviderInterface(getAaveV2Provider) .getLendingPool() ); - refinanceInternalData.aaveData = aaveData; + refinanceInternalData.aaveV2Data = aaveV2Data; + refinanceInternalData.aaveV3 = AaveV3Interface( + AaveV3PoolProviderInterface(getAaveV3Provider).getPool() + ); + refinanceInternalData.aaveV3Data = aaveV3Data; refinanceInternalData.depositAmts; refinanceInternalData.paybackAmts; @@ -69,51 +78,108 @@ contract RefinanceResolver is AaveHelpers, NotionalHelpers { ); // Aave v2 to Notional - NotionalBorrowData memory _notionalBorrowData; + if ( + data.source == Protocol.AaveV2 && data.target == Protocol.Notional + ) { + NotionalBorrowData memory _notionalBorrowData; - _notionalBorrowData.length = length; - _notionalBorrowData.fee = data.debtFee; - _notionalBorrowData.tokens = refinanceInternalData.tokens; - _notionalBorrowData.redeemToUnderlying = data.redeemBorrowToUnderlying; - _notionalBorrowData.amts = data.borrowAmts; - _notionalBorrowData.rateModes = data.paybackRateModes; - _notionalBorrowData.fCashAmount = data.borrowfCashAmts; - _notionalBorrowData.maxBorrowRate = data.maxBorrowingRates; - _notionalBorrowData.currencyIDs = data.currencyIDs; - _notionalBorrowData.marketIndex = data.borrowMarketIndices; + _notionalBorrowData.length = length; + _notionalBorrowData.source = data.source; + _notionalBorrowData.fee = data.debtFee; + _notionalBorrowData.tokens = refinanceInternalData.tokens; + _notionalBorrowData.redeemToUnderlying = data + .redeemBorrowToUnderlying; + _notionalBorrowData.amts = data.borrowAmts; + _notionalBorrowData.rateModes = data.paybackRateModes; + _notionalBorrowData.fCashAmount = data.borrowfCashAmts; + _notionalBorrowData.maxBorrowRate = data.maxBorrowingRates; + _notionalBorrowData.currencyIDs = data.currencyIDs; + _notionalBorrowData.marketIndex = data.borrowMarketIndices; - //borrow on Notional - refinanceInternalData.paybackAmts = _notionalBorrow( - _notionalBorrowData - ); - //payback debt on Aave-v2 - _aaveV2Payback( - length, - refinanceInternalData.aave, - refinanceInternalData.tokenInterfaces, - refinanceInternalData.paybackAmts, - data.paybackRateModes - ); - //withdraw aTokens from Aave-v2 - refinanceInternalData.depositAmts = _aaveV2Withdraw( - refinanceInternalData.aave, - refinanceInternalData.aaveData, - length, - refinanceInternalData.tokenInterfaces, - data.withdrawAmts - ); - //deposit on Notional - _notionalDeposit( - length, - data.collateralFee, - data.currencyIDs, - refinanceInternalData.depositAmts, - refinanceInternalData.tokens, - data.mintNTokens - ); + //borrow on Notional + refinanceInternalData.paybackAmts = _notionalBorrow( + _notionalBorrowData + ); + //payback debt on Aave-v2 + _aaveV2Payback( + length, + refinanceInternalData.aaveV2, + refinanceInternalData.tokenInterfaces, + refinanceInternalData.paybackAmts, + data.paybackRateModes + ); + //withdraw aTokens from Aave-v2 + refinanceInternalData.depositAmts = _aaveV2Withdraw( + refinanceInternalData.aaveV2, + refinanceInternalData.aaveV2Data, + length, + refinanceInternalData.tokenInterfaces, + data.withdrawAmts + ); + //deposit on Notional + _notionalDeposit( + length, + data.collateralFee, + data.currencyIDs, + refinanceInternalData.depositAmts, + refinanceInternalData.tokens, + data.mintNTokens + ); + } + // Aave v3 to Notional + else if ( + data.source == Protocol.AaveV3 && data.target == Protocol.Notional + ) { + NotionalBorrowData memory _notionalBorrowData; + + _notionalBorrowData.length = length; + _notionalBorrowData.source = data.source; + _notionalBorrowData.fee = data.debtFee; + _notionalBorrowData.tokens = refinanceInternalData.tokens; + _notionalBorrowData.redeemToUnderlying = data + .redeemBorrowToUnderlying; + _notionalBorrowData.amts = data.borrowAmts; + _notionalBorrowData.rateModes = data.paybackRateModes; + _notionalBorrowData.fCashAmount = data.borrowfCashAmts; + _notionalBorrowData.maxBorrowRate = data.maxBorrowingRates; + _notionalBorrowData.currencyIDs = data.currencyIDs; + _notionalBorrowData.marketIndex = data.borrowMarketIndices; + + //borrow on Notional + refinanceInternalData.paybackAmts = _notionalBorrow( + _notionalBorrowData + ); + //payback debt on Aave-v3 + _aaveV3Payback( + length, + refinanceInternalData.aaveV3, + refinanceInternalData.tokenInterfaces, + refinanceInternalData.paybackAmts, + data.paybackRateModes + ); + //withdraw aTokens from Aave-v3 + refinanceInternalData.depositAmts = _aaveV3Withdraw( + refinanceInternalData.aaveV3, + refinanceInternalData.aaveV3Data, + length, + refinanceInternalData.tokenInterfaces, + data.withdrawAmts + ); + //deposit on Notional + _notionalDeposit( + length, + data.collateralFee, + data.currencyIDs, + refinanceInternalData.depositAmts, + refinanceInternalData.tokens, + data.mintNTokens + ); + } _eventName = "LogRefinance(uint,uint,address[],uint[],uint[],uint[],uint[])"; _eventParam = abi.encode( + uint256(data.source), + uint256(data.target), data.collateralFee, data.debtFee, refinanceInternalData.tokens,