diff --git a/contracts/arbitrum/connectors/aave/v3-import/events.sol b/contracts/arbitrum/connectors/aave/v3-import/events.sol new file mode 100644 index 00000000..08013251 --- /dev/null +++ b/contracts/arbitrum/connectors/aave/v3-import/events.sol @@ -0,0 +1,14 @@ +pragma solidity ^0.7.0; +pragma experimental ABIEncoderV2; + +contract Events { + event LogAaveV3Import( + address indexed user, + address[] ctokens, + string[] supplyIds, + string[] borrowIds, + uint256[] flashLoanFees, + uint256[] supplyAmts, + uint256[] borrowAmts + ); +} diff --git a/contracts/arbitrum/connectors/aave/v3-import/helpers.sol b/contracts/arbitrum/connectors/aave/v3-import/helpers.sol new file mode 100644 index 00000000..41afc79e --- /dev/null +++ b/contracts/arbitrum/connectors/aave/v3-import/helpers.sol @@ -0,0 +1,286 @@ +pragma solidity ^0.7.0; + +import { DSMath } from "../../../common/math.sol"; +import { Basic } from "../../../common/basic.sol"; +import { TokenInterface, AccountInterface } from "../../../common/interfaces.sol"; +import { AaveInterface, AavePoolProviderInterface, AaveDataProviderInterface } from "./interface.sol"; +import "./events.sol"; +import "./interface.sol"; + +abstract contract Helper is DSMath, Basic { + /** + * @dev Aave referal code + */ + uint16 internal constant referalCode = 3228; + + /** + * @dev Aave Lending Pool Provider + */ + AavePoolProviderInterface internal constant aaveProvider = + AavePoolProviderInterface(0xa97684ead0e402dC232d5A977953DF7ECBaB3CDb); + + /** + * @dev Aave Protocol Data Provider + */ + AaveDataProviderInterface internal constant aaveData = + AaveDataProviderInterface(0x69FA688f1Dc47d4B5d8029D5a35FB7a548310654); + + function getIsColl(address token, address user) + internal + view + returns (bool isCol) + { + (, , , , , , , , isCol) = aaveData.getUserReserveData(token, user); + } + + struct ImportData { + address[] _supplyTokens; + address[] _borrowTokens; + ATokenInterface[] aTokens; + uint256[] supplyAmts; + uint256[] variableBorrowAmts; + uint256[] variableBorrowAmtsWithFee; + uint256[] stableBorrowAmts; + uint256[] stableBorrowAmtsWithFee; + uint256[] totalBorrowAmts; + uint256[] totalBorrowAmtsWithFee; + bool convertStable; + } + + struct ImportInputData { + address[] supplyTokens; + address[] borrowTokens; + bool convertStable; + uint256[] flashLoanFees; + } +} + +contract AaveHelpers is Helper { + function getBorrowAmount(address _token, address userAccount) + internal + view + returns (uint256 stableBorrow, uint256 variableBorrow) + { + ( + , + address stableDebtTokenAddress, + address variableDebtTokenAddress + ) = aaveData.getReserveTokensAddresses(_token); + + stableBorrow = ATokenInterface(stableDebtTokenAddress).balanceOf( + userAccount + ); + variableBorrow = ATokenInterface(variableDebtTokenAddress).balanceOf( + userAccount + ); + } + + function getBorrowAmounts( + address userAccount, + AaveInterface aave, + ImportInputData memory inputData, + ImportData memory data + ) internal returns (ImportData memory) { + if (inputData.borrowTokens.length > 0) { + data._borrowTokens = new address[](inputData.borrowTokens.length); + data.variableBorrowAmts = new uint256[]( + inputData.borrowTokens.length + ); + data.variableBorrowAmtsWithFee = new uint256[]( + inputData.borrowTokens.length + ); + data.stableBorrowAmts = new uint256[]( + inputData.borrowTokens.length + ); + data.stableBorrowAmtsWithFee = new uint256[]( + inputData.borrowTokens.length + ); + data.totalBorrowAmts = new uint256[](inputData.borrowTokens.length); + data.totalBorrowAmtsWithFee = new uint256[]( + inputData.borrowTokens.length + ); + for (uint256 i = 0; i < inputData.borrowTokens.length; i++) { + for (uint256 j = i; j < inputData.borrowTokens.length; j++) { + if (j != i) { + require( + inputData.borrowTokens[i] != + inputData.borrowTokens[j], + "token-repeated" + ); + } + } + } + for (uint256 i = 0; i < inputData.borrowTokens.length; i++) { + address _token = inputData.borrowTokens[i] == ethAddr + ? wethAddr + : inputData.borrowTokens[i]; + data._borrowTokens[i] = _token; + + ( + data.stableBorrowAmts[i], + data.variableBorrowAmts[i] + ) = getBorrowAmount(_token, userAccount); + + if (data.variableBorrowAmts[i] != 0) { + data.variableBorrowAmtsWithFee[i] = add( + data.variableBorrowAmts[i], + inputData.flashLoanFees[i] + ); + data.stableBorrowAmtsWithFee[i] = data.stableBorrowAmts[i]; + } else { + data.stableBorrowAmtsWithFee[i] = add( + data.stableBorrowAmts[i], + inputData.flashLoanFees[i] + ); + } + + data.totalBorrowAmts[i] = add( + data.stableBorrowAmts[i], + data.variableBorrowAmts[i] + ); + data.totalBorrowAmtsWithFee[i] = add( + data.stableBorrowAmtsWithFee[i], + data.variableBorrowAmtsWithFee[i] + ); + + if (data.totalBorrowAmts[i] > 0) { + uint256 _amt = data.totalBorrowAmts[i]; + TokenInterface(_token).approve(address(aave), _amt); + } + } + } + return data; + } + + function getSupplyAmounts( + address userAccount, + ImportInputData memory inputData, + ImportData memory data + ) internal view returns (ImportData memory) { + data.supplyAmts = new uint256[](inputData.supplyTokens.length); + data._supplyTokens = new address[](inputData.supplyTokens.length); + data.aTokens = new ATokenInterface[](inputData.supplyTokens.length); + + for (uint256 i = 0; i < inputData.supplyTokens.length; i++) { + for (uint256 j = i; j < inputData.supplyTokens.length; j++) { + if (j != i) { + require( + inputData.supplyTokens[i] != inputData.supplyTokens[j], + "token-repeated" + ); + } + } + } + for (uint256 i = 0; i < inputData.supplyTokens.length; i++) { + address _token = inputData.supplyTokens[i] == ethAddr + ? wethAddr + : inputData.supplyTokens[i]; + (address _aToken, , ) = aaveData.getReserveTokensAddresses(_token); + data._supplyTokens[i] = _token; + data.aTokens[i] = ATokenInterface(_aToken); + data.supplyAmts[i] = data.aTokens[i].balanceOf(userAccount); + } + + return data; + } + + function _paybackBehalfOne( + AaveInterface aave, + address token, + uint256 amt, + uint256 rateMode, + address user + ) private { + aave.repay(token, amt, rateMode, user); + } + + function _PaybackStable( + uint256 _length, + AaveInterface aave, + address[] memory tokens, + uint256[] memory amts, + address user + ) internal { + for (uint256 i = 0; i < _length; i++) { + if (amts[i] > 0) { + _paybackBehalfOne(aave, tokens[i], amts[i], 1, user); + } + } + } + + function _PaybackVariable( + uint256 _length, + AaveInterface aave, + address[] memory tokens, + uint256[] memory amts, + address user + ) internal { + for (uint256 i = 0; i < _length; i++) { + if (amts[i] > 0) { + _paybackBehalfOne(aave, tokens[i], amts[i], 2, user); + } + } + } + + function _TransferAtokens( + uint256 _length, + AaveInterface aave, + ATokenInterface[] memory atokenContracts, + uint256[] memory amts, + address[] memory tokens, + address userAccount + ) internal { + for (uint256 i = 0; i < _length; i++) { + if (amts[i] > 0) { + uint256 _amt = amts[i]; + require( + atokenContracts[i].transferFrom( + userAccount, + address(this), + _amt + ), + "allowance?" + ); + + if (!getIsColl(tokens[i], address(this))) { + aave.setUserUseReserveAsCollateral(tokens[i], true); + } + } + } + } + + function _BorrowVariable( + uint256 _length, + AaveInterface aave, + address[] memory tokens, + uint256[] memory amts + ) internal { + for (uint256 i = 0; i < _length; i++) { + if (amts[i] > 0) { + _borrowOne(aave, tokens[i], amts[i], 2); + } + } + } + + function _BorrowStable( + uint256 _length, + AaveInterface aave, + address[] memory tokens, + uint256[] memory amts + ) internal { + for (uint256 i = 0; i < _length; i++) { + if (amts[i] > 0) { + _borrowOne(aave, tokens[i], amts[i], 1); + } + } + } + + function _borrowOne( + AaveInterface aave, + address token, + uint256 amt, + uint256 rateMode + ) private { + aave.borrow(token, amt, rateMode, referalCode, address(this)); + } +} diff --git a/contracts/arbitrum/connectors/aave/v3-import/interface.sol b/contracts/arbitrum/connectors/aave/v3-import/interface.sol new file mode 100644 index 00000000..2085007b --- /dev/null +++ b/contracts/arbitrum/connectors/aave/v3-import/interface.sol @@ -0,0 +1,94 @@ +pragma solidity ^0.7.0; + +interface AaveInterface { + function supply( + address asset, + uint256 amount, + address onBehalfOf, + uint16 referralCode + ) external; + + function withdraw( + address _asset, + uint256 _amount, + address _to + ) external; + + function borrow( + address _asset, + uint256 _amount, + uint256 _interestRateMode, + uint16 _referralCode, + address _onBehalfOf + ) external; + + function repay( + address _asset, + uint256 _amount, + uint256 _rateMode, + address _onBehalfOf + ) external; + + function setUserUseReserveAsCollateral( + address _asset, + bool _useAsCollateral + ) external; + + function swapBorrowRateMode(address _asset, uint256 _rateMode) external; +} + +interface ATokenInterface { + function scaledBalanceOf(address _user) external view returns (uint256); + + function isTransferAllowed(address _user, uint256 _amount) + external + view + returns (bool); + + function balanceOf(address _user) external view returns (uint256); + + function transferFrom( + address, + address, + uint256 + ) external returns (bool); + + function allowance(address, address) external returns (uint256); +} + +interface AavePoolProviderInterface { + function getPool() external view returns (address); +} + +interface AaveDataProviderInterface { + 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 + ); +} + +interface AaveAddressProviderRegistryInterface { + function getAddressesProvidersList() + external + view + returns (address[] memory); +} diff --git a/contracts/arbitrum/connectors/aave/v3-import/main.sol b/contracts/arbitrum/connectors/aave/v3-import/main.sol new file mode 100644 index 00000000..55227773 --- /dev/null +++ b/contracts/arbitrum/connectors/aave/v3-import/main.sol @@ -0,0 +1,110 @@ +pragma solidity ^0.7.0; +pragma experimental ABIEncoderV2; +/** + * @title Aave v3 import connector . + * @dev Import EOA's aave V3 position to DSA's aave v3 position + */ +import { TokenInterface, AccountInterface } from "../../../common/interfaces.sol"; +import { AaveInterface, ATokenInterface } from "./interface.sol"; +import "./helpers.sol"; +import "./events.sol"; + +contract AaveV3ImportResolver is AaveHelpers { + function _importAave(address userAccount, ImportInputData memory inputData) + internal + returns (string memory _eventName, bytes memory _eventParam) + { + require( + AccountInterface(address(this)).isAuth(userAccount), + "user-account-not-auth" + ); + + require(inputData.supplyTokens.length > 0, "0-length-not-allowed"); + + ImportData memory data; + + AaveInterface aave = AaveInterface(aaveProvider.getPool()); + + data = getBorrowAmounts(userAccount, aave, inputData, data); + data = getSupplyAmounts(userAccount, inputData, data); + + // payback borrowed amount; + _PaybackStable( + data._borrowTokens.length, + aave, + data._borrowTokens, + data.stableBorrowAmts, + userAccount + ); + _PaybackVariable( + data._borrowTokens.length, + aave, + data._borrowTokens, + data.variableBorrowAmts, + userAccount + ); + + // transfer atokens to this address; + _TransferAtokens( + data._supplyTokens.length, + aave, + data.aTokens, + data.supplyAmts, + data._supplyTokens, + userAccount + ); + + // borrow assets after migrating position + if (data.convertStable) { + _BorrowVariable( + data._borrowTokens.length, + aave, + data._borrowTokens, + data.totalBorrowAmtsWithFee + ); + } else { + _BorrowStable( + data._borrowTokens.length, + aave, + data._borrowTokens, + data.stableBorrowAmtsWithFee + ); + _BorrowVariable( + data._borrowTokens.length, + aave, + data._borrowTokens, + data.variableBorrowAmtsWithFee + ); + } + + _eventName = "LogAaveV3Import(address,bool,address[],address[],uint256[],uint256[],uint256[],uint256[])"; + _eventParam = abi.encode( + userAccount, + inputData.convertStable, + inputData.supplyTokens, + inputData.borrowTokens, + inputData.flashLoanFees, + data.supplyAmts, + data.stableBorrowAmts, + data.variableBorrowAmts + ); + } + + /** + * @dev Import aave V3 position . + * @notice Import EOA's aave V3 position to DSA's aave v3 position + * @param userAccount The address of the EOA from which aave position will be imported + * @param inputData The struct containing all the neccessary input data + */ + function importAave(address userAccount, ImportInputData memory inputData) + external + payable + returns (string memory _eventName, bytes memory _eventParam) + { + (_eventName, _eventParam) = _importAave(userAccount, inputData); + } +} + +contract ConnectV2AaveV3ImportArbitrum is AaveV3ImportResolver { + string public constant name = "Aave-v3-import-v1"; +} diff --git a/contracts/avalanche/connectors/aave/v3-import/events.sol b/contracts/avalanche/connectors/aave/v3-import/events.sol new file mode 100644 index 00000000..08013251 --- /dev/null +++ b/contracts/avalanche/connectors/aave/v3-import/events.sol @@ -0,0 +1,14 @@ +pragma solidity ^0.7.0; +pragma experimental ABIEncoderV2; + +contract Events { + event LogAaveV3Import( + address indexed user, + address[] ctokens, + string[] supplyIds, + string[] borrowIds, + uint256[] flashLoanFees, + uint256[] supplyAmts, + uint256[] borrowAmts + ); +} diff --git a/contracts/avalanche/connectors/aave/v3-import/helpers.sol b/contracts/avalanche/connectors/aave/v3-import/helpers.sol new file mode 100644 index 00000000..357de5f4 --- /dev/null +++ b/contracts/avalanche/connectors/aave/v3-import/helpers.sol @@ -0,0 +1,288 @@ +pragma solidity ^0.7.0; +import { DSMath } from "../../../common/math.sol"; +import { Basic } from "../../../common/basic.sol"; +import { TokenInterface, AccountInterface } from "../../../common/interfaces.sol"; +import { AaveInterface, AavePoolProviderInterface, AaveDataProviderInterface } from "./interface.sol"; +import "./events.sol"; +import "./interface.sol"; + +abstract contract Helper is DSMath, Basic { + /** + * @dev Aave referal code + */ + uint16 internal constant referalCode = 3228; + + /** + * @dev Aave Pool Provider + */ + AavePoolProviderInterface internal constant aaveProvider = + AavePoolProviderInterface(0xa97684ead0e402dC232d5A977953DF7ECBaB3CDb); + + /** + * @dev Aave Protocol Data Provider + */ + AaveDataProviderInterface internal constant aaveData = + AaveDataProviderInterface(0x69FA688f1Dc47d4B5d8029D5a35FB7a548310654); + + function getIsColl(address token, address user) + internal + view + returns (bool isCol) + { + (, , , , , , , , isCol) = aaveData.getUserReserveData(token, user); + } + + struct ImportData { + address[] _supplyTokens; + address[] _borrowTokens; + ATokenInterface[] aTokens; + uint256[] supplyAmts; + uint256[] variableBorrowAmts; + uint256[] variableBorrowAmtsWithFee; + uint256[] stableBorrowAmts; + uint256[] stableBorrowAmtsWithFee; + uint256[] totalBorrowAmts; + uint256[] totalBorrowAmtsWithFee; + bool convertStable; + } + + struct ImportInputData { + address[] supplyTokens; + address[] borrowTokens; + bool convertStable; + uint256[] flashLoanFees; + } +} + +contract AaveHelpers is Helper { + function getBorrowAmount(address _token, address userAccount) + internal + view + returns (uint256 stableBorrow, uint256 variableBorrow) + { + ( + , + address stableDebtTokenAddress, + address variableDebtTokenAddress + ) = aaveData.getReserveTokensAddresses(_token); + + stableBorrow = ATokenInterface(stableDebtTokenAddress).balanceOf( + userAccount + ); + variableBorrow = ATokenInterface(variableDebtTokenAddress).balanceOf( + userAccount + ); + } + + function getBorrowAmounts( + address userAccount, + AaveInterface aave, + ImportInputData memory inputData, + ImportData memory data + ) internal returns (ImportData memory) { + if (inputData.borrowTokens.length > 0) { + data._borrowTokens = new address[](inputData.borrowTokens.length); + data.variableBorrowAmts = new uint256[]( + inputData.borrowTokens.length + ); + data.variableBorrowAmtsWithFee = new uint256[]( + inputData.borrowTokens.length + ); + data.stableBorrowAmts = new uint256[]( + inputData.borrowTokens.length + ); + data.stableBorrowAmtsWithFee = new uint256[]( + inputData.borrowTokens.length + ); + data.totalBorrowAmts = new uint256[](inputData.borrowTokens.length); + data.totalBorrowAmtsWithFee = new uint256[]( + inputData.borrowTokens.length + ); + for (uint256 i = 0; i < inputData.borrowTokens.length; i++) { + for (uint256 j = i; j < inputData.borrowTokens.length; j++) { + if (j != i) { + require( + inputData.borrowTokens[i] != + inputData.borrowTokens[j], + "token-repeated" + ); + } + } + } + for (uint256 i = 0; i < inputData.borrowTokens.length; i++) { + address _token = inputData.borrowTokens[i] == avaxAddr + ? wavaxAddr + : inputData.borrowTokens[i]; + data._borrowTokens[i] = _token; + + ( + data.stableBorrowAmts[i], + data.variableBorrowAmts[i] + ) = getBorrowAmount(_token, userAccount); + + if (data.variableBorrowAmts[i] != 0) { + data.variableBorrowAmtsWithFee[i] = add( + data.variableBorrowAmts[i], + inputData.flashLoanFees[i] + ); + data.stableBorrowAmtsWithFee[i] = data.stableBorrowAmts[i]; + } else { + data.stableBorrowAmtsWithFee[i] = add( + data.stableBorrowAmts[i], + inputData.flashLoanFees[i] + ); + data.variableBorrowAmtsWithFee[i] = data.variableBorrowAmts[ + i + ]; + } + + data.totalBorrowAmts[i] = add( + data.stableBorrowAmts[i], + data.variableBorrowAmts[i] + ); + data.totalBorrowAmtsWithFee[i] = add( + data.stableBorrowAmtsWithFee[i], + data.variableBorrowAmtsWithFee[i] + ); + + if (data.totalBorrowAmts[i] > 0) { + uint256 _amt = data.totalBorrowAmts[i]; + TokenInterface(_token).approve(address(aave), _amt); + } + } + } + return data; + } + + function getSupplyAmounts( + address userAccount, + ImportInputData memory inputData, + ImportData memory data + ) internal view returns (ImportData memory) { + data.supplyAmts = new uint256[](inputData.supplyTokens.length); + data._supplyTokens = new address[](inputData.supplyTokens.length); + data.aTokens = new ATokenInterface[](inputData.supplyTokens.length); + + for (uint256 i = 0; i < inputData.supplyTokens.length; i++) { + for (uint256 j = i; j < inputData.supplyTokens.length; j++) { + if (j != i) { + require( + inputData.supplyTokens[i] != inputData.supplyTokens[j], + "token-repeated" + ); + } + } + } + for (uint256 i = 0; i < inputData.supplyTokens.length; i++) { + address _token = inputData.supplyTokens[i] == avaxAddr + ? wavaxAddr + : inputData.supplyTokens[i]; + (address _aToken, , ) = aaveData.getReserveTokensAddresses(_token); + data._supplyTokens[i] = _token; + data.aTokens[i] = ATokenInterface(_aToken); + data.supplyAmts[i] = data.aTokens[i].balanceOf(userAccount); + } + + return data; + } + + function _paybackBehalfOne( + AaveInterface aave, + address token, + uint256 amt, + uint256 rateMode, + address user + ) private { + aave.repay(token, amt, rateMode, user); + } + + function _PaybackStable( + uint256 _length, + AaveInterface aave, + address[] memory tokens, + uint256[] memory amts, + address user + ) internal { + for (uint256 i = 0; i < _length; i++) { + if (amts[i] > 0) { + _paybackBehalfOne(aave, tokens[i], amts[i], 1, user); + } + } + } + + function _PaybackVariable( + uint256 _length, + AaveInterface aave, + address[] memory tokens, + uint256[] memory amts, + address user + ) internal { + for (uint256 i = 0; i < _length; i++) { + if (amts[i] > 0) { + _paybackBehalfOne(aave, tokens[i], amts[i], 2, user); + } + } + } + + function _TransferAtokens( + uint256 _length, + AaveInterface aave, + ATokenInterface[] memory atokenContracts, + uint256[] memory amts, + address[] memory tokens, + address userAccount + ) internal { + for (uint256 i = 0; i < _length; i++) { + if (amts[i] > 0) { + uint256 _amt = amts[i]; + require( + atokenContracts[i].transferFrom( + userAccount, + address(this), + _amt + ), + "allowance?" + ); + + if (!getIsColl(tokens[i], address(this))) { + aave.setUserUseReserveAsCollateral(tokens[i], true); + } + } + } + } + + function _BorrowVariable( + uint256 _length, + AaveInterface aave, + address[] memory tokens, + uint256[] memory amts + ) internal { + for (uint256 i = 0; i < _length; i++) { + if (amts[i] > 0) { + _borrowOne(aave, tokens[i], amts[i], 2); + } + } + } + + function _BorrowStable( + uint256 _length, + AaveInterface aave, + address[] memory tokens, + uint256[] memory amts + ) internal { + for (uint256 i = 0; i < _length; i++) { + if (amts[i] > 0) { + _borrowOne(aave, tokens[i], amts[i], 1); + } + } + } + + function _borrowOne( + AaveInterface aave, + address token, + uint256 amt, + uint256 rateMode + ) private { + aave.borrow(token, amt, rateMode, referalCode, address(this)); + } +} diff --git a/contracts/avalanche/connectors/aave/v3-import/interface.sol b/contracts/avalanche/connectors/aave/v3-import/interface.sol new file mode 100644 index 00000000..4cce4a80 --- /dev/null +++ b/contracts/avalanche/connectors/aave/v3-import/interface.sol @@ -0,0 +1,94 @@ +pragma solidity ^0.7.0; + +interface AaveInterface { + function supply( + address _asset, + uint256 _amount, + address _onBehalfOf, + uint16 _referralCode + ) external; + + function withdraw( + address _asset, + uint256 _amount, + address _to + ) external; + + function borrow( + address _asset, + uint256 _amount, + uint256 _interestRateMode, + uint16 _referralCode, + address _onBehalfOf + ) external; + + function repay( + address _asset, + uint256 _amount, + uint256 _rateMode, + address _onBehalfOf + ) external; + + function setUserUseReserveAsCollateral( + address _asset, + bool _useAsCollateral + ) external; + + function swapBorrowRateMode(address _asset, uint256 _rateMode) external; +} + +interface ATokenInterface { + function scaledBalanceOf(address _user) external view returns (uint256); + + function isTransferAllowed(address _user, uint256 _amount) + external + view + returns (bool); + + function balanceOf(address _user) external view returns (uint256); + + function transferFrom( + address, + address, + uint256 + ) external returns (bool); + + function allowance(address, address) external returns (uint256); +} + +interface AavePoolProviderInterface { + function getPool() external view returns (address); +} + +interface AaveDataProviderInterface { + 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 + ); +} + +interface AaveAddressProviderRegistryInterface { + function getAddressesProvidersList() + external + view + returns (address[] memory); +} diff --git a/contracts/avalanche/connectors/aave/v3-import/main.sol b/contracts/avalanche/connectors/aave/v3-import/main.sol new file mode 100644 index 00000000..8d183d87 --- /dev/null +++ b/contracts/avalanche/connectors/aave/v3-import/main.sol @@ -0,0 +1,110 @@ +pragma solidity ^0.7.0; +pragma experimental ABIEncoderV2; +/** + * @title Aave v3 import connector . + * @dev Import EOA's aave V3 position to DSA's aave v3 position + */ +import { TokenInterface, AccountInterface } from "../../../common/interfaces.sol"; +import { AaveInterface, ATokenInterface } from "./interface.sol"; +import "./helpers.sol"; +import "./events.sol"; + +contract AaveV3ImportResolver is AaveHelpers { + function _importAave(address userAccount, ImportInputData memory inputData) + internal + returns (string memory _eventName, bytes memory _eventParam) + { + require( + AccountInterface(address(this)).isAuth(userAccount), + "user-account-not-auth" + ); + + require(inputData.supplyTokens.length > 0, "0-length-not-allowed"); + + ImportData memory data; + + AaveInterface aave = AaveInterface(aaveProvider.getPool()); + + data = getBorrowAmounts(userAccount, aave, inputData, data); + data = getSupplyAmounts(userAccount, inputData, data); + + // payback borrowed amount; + _PaybackStable( + data._borrowTokens.length, + aave, + data._borrowTokens, + data.stableBorrowAmts, + userAccount + ); + _PaybackVariable( + data._borrowTokens.length, + aave, + data._borrowTokens, + data.variableBorrowAmts, + userAccount + ); + + // transfer atokens to this address; + _TransferAtokens( + data._supplyTokens.length, + aave, + data.aTokens, + data.supplyAmts, + data._supplyTokens, + userAccount + ); + + // borrow assets after migrating position + if (data.convertStable) { + _BorrowVariable( + data._borrowTokens.length, + aave, + data._borrowTokens, + data.totalBorrowAmtsWithFee + ); + } else { + _BorrowStable( + data._borrowTokens.length, + aave, + data._borrowTokens, + data.stableBorrowAmtsWithFee + ); + _BorrowVariable( + data._borrowTokens.length, + aave, + data._borrowTokens, + data.variableBorrowAmtsWithFee + ); + } + + _eventName = "LogAaveV3Import(address,bool,address[],address[],uint256[],uint256[],uint256[],uint256[])"; + _eventParam = abi.encode( + userAccount, + inputData.convertStable, + inputData.supplyTokens, + inputData.borrowTokens, + inputData.flashLoanFees, + data.supplyAmts, + data.stableBorrowAmts, + data.variableBorrowAmts + ); + } + + /** + * @dev Import aave V3 position . + * @notice Import EOA's aave V3 position to DSA's aave v3 position + * @param userAccount The address of the EOA from which aave position will be imported + * @param inputData The struct containing all the neccessary input data + */ + function importAave(address userAccount, ImportInputData memory inputData) + external + payable + returns (string memory _eventName, bytes memory _eventParam) + { + (_eventName, _eventParam) = _importAave(userAccount, inputData); + } +} + +contract ConnectV2AaveV3ImportAvalanche is AaveV3ImportResolver { + string public constant name = "Aave-v3-import-v1"; +} diff --git a/contracts/fantom/connectors/aave/v3-import/events.sol b/contracts/fantom/connectors/aave/v3-import/events.sol new file mode 100644 index 00000000..08013251 --- /dev/null +++ b/contracts/fantom/connectors/aave/v3-import/events.sol @@ -0,0 +1,14 @@ +pragma solidity ^0.7.0; +pragma experimental ABIEncoderV2; + +contract Events { + event LogAaveV3Import( + address indexed user, + address[] ctokens, + string[] supplyIds, + string[] borrowIds, + uint256[] flashLoanFees, + uint256[] supplyAmts, + uint256[] borrowAmts + ); +} diff --git a/contracts/fantom/connectors/aave/v3-import/helpers.sol b/contracts/fantom/connectors/aave/v3-import/helpers.sol new file mode 100644 index 00000000..3928644f --- /dev/null +++ b/contracts/fantom/connectors/aave/v3-import/helpers.sol @@ -0,0 +1,286 @@ +pragma solidity ^0.7.0; + +import { DSMath } from "../../../common/math.sol"; +import { Basic } from "../../../common/basic.sol"; +import { TokenInterface, AccountInterface } from "../../../common/interfaces.sol"; +import { AaveInterface, AavePoolProviderInterface, AaveDataProviderInterface } from "./interface.sol"; +import "./events.sol"; +import "./interface.sol"; + +abstract contract Helper is DSMath, Basic { + /** + * @dev Aave referal code + */ + uint16 internal constant referalCode = 3228; + + /** + * @dev Aave Lending Pool Provider + */ + AavePoolProviderInterface internal constant aaveProvider = + AavePoolProviderInterface(0xa97684ead0e402dC232d5A977953DF7ECBaB3CDb); + + /** + * @dev Aave Protocol Data Provider + */ + AaveDataProviderInterface internal constant aaveData = + AaveDataProviderInterface(0x69FA688f1Dc47d4B5d8029D5a35FB7a548310654); + + function getIsColl(address token, address user) + internal + view + returns (bool isCol) + { + (, , , , , , , , isCol) = aaveData.getUserReserveData(token, user); + } + + struct ImportData { + address[] _supplyTokens; + address[] _borrowTokens; + ATokenInterface[] aTokens; + uint256[] supplyAmts; + uint256[] variableBorrowAmts; + uint256[] variableBorrowAmtsWithFee; + uint256[] stableBorrowAmts; + uint256[] stableBorrowAmtsWithFee; + uint256[] totalBorrowAmts; + uint256[] totalBorrowAmtsWithFee; + bool convertStable; + } + + struct ImportInputData { + address[] supplyTokens; + address[] borrowTokens; + bool convertStable; + uint256[] flashLoanFees; + } +} + +contract AaveHelpers is Helper { + function getBorrowAmount(address _token, address userAccount) + internal + view + returns (uint256 stableBorrow, uint256 variableBorrow) + { + ( + , + address stableDebtTokenAddress, + address variableDebtTokenAddress + ) = aaveData.getReserveTokensAddresses(_token); + + stableBorrow = ATokenInterface(stableDebtTokenAddress).balanceOf( + userAccount + ); + variableBorrow = ATokenInterface(variableDebtTokenAddress).balanceOf( + userAccount + ); + } + + function getBorrowAmounts( + address userAccount, + AaveInterface aave, + ImportInputData memory inputData, + ImportData memory data + ) internal returns (ImportData memory) { + if (inputData.borrowTokens.length > 0) { + data._borrowTokens = new address[](inputData.borrowTokens.length); + data.variableBorrowAmts = new uint256[]( + inputData.borrowTokens.length + ); + data.variableBorrowAmtsWithFee = new uint256[]( + inputData.borrowTokens.length + ); + data.stableBorrowAmts = new uint256[]( + inputData.borrowTokens.length + ); + data.stableBorrowAmtsWithFee = new uint256[]( + inputData.borrowTokens.length + ); + data.totalBorrowAmts = new uint256[](inputData.borrowTokens.length); + data.totalBorrowAmtsWithFee = new uint256[]( + inputData.borrowTokens.length + ); + for (uint256 i = 0; i < inputData.borrowTokens.length; i++) { + for (uint256 j = i; j < inputData.borrowTokens.length; j++) { + if (j != i) { + require( + inputData.borrowTokens[i] != + inputData.borrowTokens[j], + "token-repeated" + ); + } + } + } + for (uint256 i = 0; i < inputData.borrowTokens.length; i++) { + address _token = inputData.borrowTokens[i] == ftmAddr + ? wftmAddr + : inputData.borrowTokens[i]; + data._borrowTokens[i] = _token; + + ( + data.stableBorrowAmts[i], + data.variableBorrowAmts[i] + ) = getBorrowAmount(_token, userAccount); + + if (data.variableBorrowAmts[i] != 0) { + data.variableBorrowAmtsWithFee[i] = add( + data.variableBorrowAmts[i], + inputData.flashLoanFees[i] + ); + data.stableBorrowAmtsWithFee[i] = data.stableBorrowAmts[i]; + } else { + data.stableBorrowAmtsWithFee[i] = add( + data.stableBorrowAmts[i], + inputData.flashLoanFees[i] + ); + } + + data.totalBorrowAmts[i] = add( + data.stableBorrowAmts[i], + data.variableBorrowAmts[i] + ); + data.totalBorrowAmtsWithFee[i] = add( + data.stableBorrowAmtsWithFee[i], + data.variableBorrowAmtsWithFee[i] + ); + + if (data.totalBorrowAmts[i] > 0) { + uint256 _amt = data.totalBorrowAmts[i]; + TokenInterface(_token).approve(address(aave), _amt); + } + } + } + return data; + } + + function getSupplyAmounts( + address userAccount, + ImportInputData memory inputData, + ImportData memory data + ) internal view returns (ImportData memory) { + data.supplyAmts = new uint256[](inputData.supplyTokens.length); + data._supplyTokens = new address[](inputData.supplyTokens.length); + data.aTokens = new ATokenInterface[](inputData.supplyTokens.length); + + for (uint256 i = 0; i < inputData.supplyTokens.length; i++) { + for (uint256 j = i; j < inputData.supplyTokens.length; j++) { + if (j != i) { + require( + inputData.supplyTokens[i] != inputData.supplyTokens[j], + "token-repeated" + ); + } + } + } + for (uint256 i = 0; i < inputData.supplyTokens.length; i++) { + address _token = inputData.supplyTokens[i] == ftmAddr + ? wftmAddr + : inputData.supplyTokens[i]; + (address _aToken, , ) = aaveData.getReserveTokensAddresses(_token); + data._supplyTokens[i] = _token; + data.aTokens[i] = ATokenInterface(_aToken); + data.supplyAmts[i] = data.aTokens[i].balanceOf(userAccount); + } + + return data; + } + + function _paybackBehalfOne( + AaveInterface aave, + address token, + uint256 amt, + uint256 rateMode, + address user + ) private { + aave.repay(token, amt, rateMode, user); + } + + function _PaybackStable( + uint256 _length, + AaveInterface aave, + address[] memory tokens, + uint256[] memory amts, + address user + ) internal { + for (uint256 i = 0; i < _length; i++) { + if (amts[i] > 0) { + _paybackBehalfOne(aave, tokens[i], amts[i], 1, user); + } + } + } + + function _PaybackVariable( + uint256 _length, + AaveInterface aave, + address[] memory tokens, + uint256[] memory amts, + address user + ) internal { + for (uint256 i = 0; i < _length; i++) { + if (amts[i] > 0) { + _paybackBehalfOne(aave, tokens[i], amts[i], 2, user); + } + } + } + + function _TransferAtokens( + uint256 _length, + AaveInterface aave, + ATokenInterface[] memory atokenContracts, + uint256[] memory amts, + address[] memory tokens, + address userAccount + ) internal { + for (uint256 i = 0; i < _length; i++) { + if (amts[i] > 0) { + uint256 _amt = amts[i]; + require( + atokenContracts[i].transferFrom( + userAccount, + address(this), + _amt + ), + "allowance?" + ); + + if (!getIsColl(tokens[i], address(this))) { + aave.setUserUseReserveAsCollateral(tokens[i], true); + } + } + } + } + + function _BorrowVariable( + uint256 _length, + AaveInterface aave, + address[] memory tokens, + uint256[] memory amts + ) internal { + for (uint256 i = 0; i < _length; i++) { + if (amts[i] > 0) { + _borrowOne(aave, tokens[i], amts[i], 2); + } + } + } + + function _BorrowStable( + uint256 _length, + AaveInterface aave, + address[] memory tokens, + uint256[] memory amts + ) internal { + for (uint256 i = 0; i < _length; i++) { + if (amts[i] > 0) { + _borrowOne(aave, tokens[i], amts[i], 1); + } + } + } + + function _borrowOne( + AaveInterface aave, + address token, + uint256 amt, + uint256 rateMode + ) private { + aave.borrow(token, amt, rateMode, referalCode, address(this)); + } +} diff --git a/contracts/fantom/connectors/aave/v3-import/interface.sol b/contracts/fantom/connectors/aave/v3-import/interface.sol new file mode 100644 index 00000000..2085007b --- /dev/null +++ b/contracts/fantom/connectors/aave/v3-import/interface.sol @@ -0,0 +1,94 @@ +pragma solidity ^0.7.0; + +interface AaveInterface { + function supply( + address asset, + uint256 amount, + address onBehalfOf, + uint16 referralCode + ) external; + + function withdraw( + address _asset, + uint256 _amount, + address _to + ) external; + + function borrow( + address _asset, + uint256 _amount, + uint256 _interestRateMode, + uint16 _referralCode, + address _onBehalfOf + ) external; + + function repay( + address _asset, + uint256 _amount, + uint256 _rateMode, + address _onBehalfOf + ) external; + + function setUserUseReserveAsCollateral( + address _asset, + bool _useAsCollateral + ) external; + + function swapBorrowRateMode(address _asset, uint256 _rateMode) external; +} + +interface ATokenInterface { + function scaledBalanceOf(address _user) external view returns (uint256); + + function isTransferAllowed(address _user, uint256 _amount) + external + view + returns (bool); + + function balanceOf(address _user) external view returns (uint256); + + function transferFrom( + address, + address, + uint256 + ) external returns (bool); + + function allowance(address, address) external returns (uint256); +} + +interface AavePoolProviderInterface { + function getPool() external view returns (address); +} + +interface AaveDataProviderInterface { + 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 + ); +} + +interface AaveAddressProviderRegistryInterface { + function getAddressesProvidersList() + external + view + returns (address[] memory); +} diff --git a/contracts/fantom/connectors/aave/v3-import/main.sol b/contracts/fantom/connectors/aave/v3-import/main.sol new file mode 100644 index 00000000..112845b6 --- /dev/null +++ b/contracts/fantom/connectors/aave/v3-import/main.sol @@ -0,0 +1,111 @@ +pragma solidity ^0.7.0; +pragma experimental ABIEncoderV2; +/** + * @title Aave v3 import connector . + * @dev Import EOA's aave V3 position to DSA's aave v3 position + */ + +import { TokenInterface, AccountInterface } from "../../../common/interfaces.sol"; +import { AaveInterface, ATokenInterface } from "./interface.sol"; +import "./helpers.sol"; +import "./events.sol"; + +contract AaveV3ImportResolver is AaveHelpers { + function _importAave(address userAccount, ImportInputData memory inputData) + internal + returns (string memory _eventName, bytes memory _eventParam) + { + require( + AccountInterface(address(this)).isAuth(userAccount), + "user-account-not-auth" + ); + + require(inputData.supplyTokens.length > 0, "0-length-not-allowed"); + + ImportData memory data; + + AaveInterface aave = AaveInterface(aaveProvider.getPool()); + + data = getBorrowAmounts(userAccount, aave, inputData, data); + data = getSupplyAmounts(userAccount, inputData, data); + + // payback borrowed amount; + _PaybackStable( + data._borrowTokens.length, + aave, + data._borrowTokens, + data.stableBorrowAmts, + userAccount + ); + _PaybackVariable( + data._borrowTokens.length, + aave, + data._borrowTokens, + data.variableBorrowAmts, + userAccount + ); + + // transfer atokens to this address; + _TransferAtokens( + data._supplyTokens.length, + aave, + data.aTokens, + data.supplyAmts, + data._supplyTokens, + userAccount + ); + + // borrow assets after migrating position + if (data.convertStable) { + _BorrowVariable( + data._borrowTokens.length, + aave, + data._borrowTokens, + data.totalBorrowAmtsWithFee + ); + } else { + _BorrowStable( + data._borrowTokens.length, + aave, + data._borrowTokens, + data.stableBorrowAmtsWithFee + ); + _BorrowVariable( + data._borrowTokens.length, + aave, + data._borrowTokens, + data.variableBorrowAmtsWithFee + ); + } + + _eventName = "LogAaveV3Import(address,bool,address[],address[],uint256[],uint256[],uint256[],uint256[])"; + _eventParam = abi.encode( + userAccount, + inputData.convertStable, + inputData.supplyTokens, + inputData.borrowTokens, + inputData.flashLoanFees, + data.supplyAmts, + data.stableBorrowAmts, + data.variableBorrowAmts + ); + } + + /** + * @dev Import aave V3 position . + * @notice Import EOA's aave V3 position to DSA's aave v3 position + * @param userAccount The address of the EOA from which aave position will be imported + * @param inputData The struct containing all the neccessary input data + */ + function importAave(address userAccount, ImportInputData memory inputData) + external + payable + returns (string memory _eventName, bytes memory _eventParam) + { + (_eventName, _eventParam) = _importAave(userAccount, inputData); + } +} + +contract ConnectV2AaveV3ImportFantom is AaveV3ImportResolver { + string public constant name = "Aave-v3-import-v1"; +} diff --git a/contracts/mainnet/connectors/aave/v3-import/events.sol b/contracts/mainnet/connectors/aave/v3-import/events.sol new file mode 100644 index 00000000..08013251 --- /dev/null +++ b/contracts/mainnet/connectors/aave/v3-import/events.sol @@ -0,0 +1,14 @@ +pragma solidity ^0.7.0; +pragma experimental ABIEncoderV2; + +contract Events { + event LogAaveV3Import( + address indexed user, + address[] ctokens, + string[] supplyIds, + string[] borrowIds, + uint256[] flashLoanFees, + uint256[] supplyAmts, + uint256[] borrowAmts + ); +} diff --git a/contracts/mainnet/connectors/aave/v3-import/helpers.sol b/contracts/mainnet/connectors/aave/v3-import/helpers.sol new file mode 100644 index 00000000..591394b5 --- /dev/null +++ b/contracts/mainnet/connectors/aave/v3-import/helpers.sol @@ -0,0 +1,286 @@ +pragma solidity ^0.7.0; + +import { DSMath } from "../../../common/math.sol"; +import { Basic } from "../../../common/basic.sol"; +import { TokenInterface, AccountInterface } from "../../../common/interfaces.sol"; +import { AaveInterface, AavePoolProviderInterface, AaveDataProviderInterface } from "./interface.sol"; +import "./events.sol"; +import "./interface.sol"; + +abstract contract Helper is DSMath, Basic { + /** + * @dev Aave referal code + */ + uint16 internal constant referalCode = 3228; + + /** + * @dev Aave Lending Pool Provider + */ + AavePoolProviderInterface internal constant aaveProvider = + AavePoolProviderInterface(0xB53C1a33016B2DC2fF3653530bfF1848a515c8c5); + + /** + * @dev Aave Protocol Data Provider + */ + AaveDataProviderInterface internal constant aaveData = + AaveDataProviderInterface(0x057835Ad21a177dbdd3090bB1CAE03EaCF78Fc6d); + + function getIsColl(address token, address user) + internal + view + returns (bool isCol) + { + (, , , , , , , , isCol) = aaveData.getUserReserveData(token, user); + } + + struct ImportData { + address[] _supplyTokens; + address[] _borrowTokens; + ATokenInterface[] aTokens; + uint256[] supplyAmts; + uint256[] variableBorrowAmts; + uint256[] variableBorrowAmtsWithFee; + uint256[] stableBorrowAmts; + uint256[] stableBorrowAmtsWithFee; + uint256[] totalBorrowAmts; + uint256[] totalBorrowAmtsWithFee; + bool convertStable; + } + + struct ImportInputData { + address[] supplyTokens; + address[] borrowTokens; + bool convertStable; + uint256[] flashLoanFees; + } +} + +contract AaveHelpers is Helper { + function getBorrowAmount(address _token, address userAccount) + internal + view + returns (uint256 stableBorrow, uint256 variableBorrow) + { + ( + , + address stableDebtTokenAddress, + address variableDebtTokenAddress + ) = aaveData.getReserveTokensAddresses(_token); + + stableBorrow = ATokenInterface(stableDebtTokenAddress).balanceOf( + userAccount + ); + variableBorrow = ATokenInterface(variableDebtTokenAddress).balanceOf( + userAccount + ); + } + + function getBorrowAmounts( + address userAccount, + AaveInterface aave, + ImportInputData memory inputData, + ImportData memory data + ) internal returns (ImportData memory) { + if (inputData.borrowTokens.length > 0) { + data._borrowTokens = new address[](inputData.borrowTokens.length); + data.variableBorrowAmts = new uint256[]( + inputData.borrowTokens.length + ); + data.variableBorrowAmtsWithFee = new uint256[]( + inputData.borrowTokens.length + ); + data.stableBorrowAmts = new uint256[]( + inputData.borrowTokens.length + ); + data.stableBorrowAmtsWithFee = new uint256[]( + inputData.borrowTokens.length + ); + data.totalBorrowAmts = new uint256[](inputData.borrowTokens.length); + data.totalBorrowAmtsWithFee = new uint256[]( + inputData.borrowTokens.length + ); + for (uint256 i = 0; i < inputData.borrowTokens.length; i++) { + for (uint256 j = i; j < inputData.borrowTokens.length; j++) { + if (j != i) { + require( + inputData.borrowTokens[i] != + inputData.borrowTokens[j], + "token-repeated" + ); + } + } + } + for (uint256 i = 0; i < inputData.borrowTokens.length; i++) { + address _token = inputData.borrowTokens[i] == ethAddr + ? wethAddr + : inputData.borrowTokens[i]; + data._borrowTokens[i] = _token; + + ( + data.stableBorrowAmts[i], + data.variableBorrowAmts[i] + ) = getBorrowAmount(_token, userAccount); + + if (data.variableBorrowAmts[i] != 0) { + data.variableBorrowAmtsWithFee[i] = add( + data.variableBorrowAmts[i], + inputData.flashLoanFees[i] + ); + data.stableBorrowAmtsWithFee[i] = data.stableBorrowAmts[i]; + } else { + data.stableBorrowAmtsWithFee[i] = add( + data.stableBorrowAmts[i], + inputData.flashLoanFees[i] + ); + } + + data.totalBorrowAmts[i] = add( + data.stableBorrowAmts[i], + data.variableBorrowAmts[i] + ); + data.totalBorrowAmtsWithFee[i] = add( + data.stableBorrowAmtsWithFee[i], + data.variableBorrowAmtsWithFee[i] + ); + + if (data.totalBorrowAmts[i] > 0) { + uint256 _amt = data.totalBorrowAmts[i]; + TokenInterface(_token).approve(address(aave), _amt); + } + } + } + return data; + } + + function getSupplyAmounts( + address userAccount, + ImportInputData memory inputData, + ImportData memory data + ) internal view returns (ImportData memory) { + data.supplyAmts = new uint256[](inputData.supplyTokens.length); + data._supplyTokens = new address[](inputData.supplyTokens.length); + data.aTokens = new ATokenInterface[](inputData.supplyTokens.length); + + for (uint256 i = 0; i < inputData.supplyTokens.length; i++) { + for (uint256 j = i; j < inputData.supplyTokens.length; j++) { + if (j != i) { + require( + inputData.supplyTokens[i] != inputData.supplyTokens[j], + "token-repeated" + ); + } + } + } + for (uint256 i = 0; i < inputData.supplyTokens.length; i++) { + address _token = inputData.supplyTokens[i] == ethAddr + ? wethAddr + : inputData.supplyTokens[i]; + (address _aToken, , ) = aaveData.getReserveTokensAddresses(_token); + data._supplyTokens[i] = _token; + data.aTokens[i] = ATokenInterface(_aToken); + data.supplyAmts[i] = data.aTokens[i].balanceOf(userAccount); + } + + return data; + } + + function _paybackBehalfOne( + AaveInterface aave, + address token, + uint256 amt, + uint256 rateMode, + address user + ) private { + aave.repay(token, amt, rateMode, user); + } + + function _PaybackStable( + uint256 _length, + AaveInterface aave, + address[] memory tokens, + uint256[] memory amts, + address user + ) internal { + for (uint256 i = 0; i < _length; i++) { + if (amts[i] > 0) { + _paybackBehalfOne(aave, tokens[i], amts[i], 1, user); + } + } + } + + function _PaybackVariable( + uint256 _length, + AaveInterface aave, + address[] memory tokens, + uint256[] memory amts, + address user + ) internal { + for (uint256 i = 0; i < _length; i++) { + if (amts[i] > 0) { + _paybackBehalfOne(aave, tokens[i], amts[i], 2, user); + } + } + } + + function _TransferAtokens( + uint256 _length, + AaveInterface aave, + ATokenInterface[] memory atokenContracts, + uint256[] memory amts, + address[] memory tokens, + address userAccount + ) internal { + for (uint256 i = 0; i < _length; i++) { + if (amts[i] > 0) { + uint256 _amt = amts[i]; + require( + atokenContracts[i].transferFrom( + userAccount, + address(this), + _amt + ), + "allowance?" + ); + + if (!getIsColl(tokens[i], address(this))) { + aave.setUserUseReserveAsCollateral(tokens[i], true); + } + } + } + } + + function _BorrowVariable( + uint256 _length, + AaveInterface aave, + address[] memory tokens, + uint256[] memory amts + ) internal { + for (uint256 i = 0; i < _length; i++) { + if (amts[i] > 0) { + _borrowOne(aave, tokens[i], amts[i], 2); + } + } + } + + function _BorrowStable( + uint256 _length, + AaveInterface aave, + address[] memory tokens, + uint256[] memory amts + ) internal { + for (uint256 i = 0; i < _length; i++) { + if (amts[i] > 0) { + _borrowOne(aave, tokens[i], amts[i], 1); + } + } + } + + function _borrowOne( + AaveInterface aave, + address token, + uint256 amt, + uint256 rateMode + ) private { + aave.borrow(token, amt, rateMode, referalCode, address(this)); + } +} diff --git a/contracts/mainnet/connectors/aave/v3-import/interface.sol b/contracts/mainnet/connectors/aave/v3-import/interface.sol new file mode 100644 index 00000000..2085007b --- /dev/null +++ b/contracts/mainnet/connectors/aave/v3-import/interface.sol @@ -0,0 +1,94 @@ +pragma solidity ^0.7.0; + +interface AaveInterface { + function supply( + address asset, + uint256 amount, + address onBehalfOf, + uint16 referralCode + ) external; + + function withdraw( + address _asset, + uint256 _amount, + address _to + ) external; + + function borrow( + address _asset, + uint256 _amount, + uint256 _interestRateMode, + uint16 _referralCode, + address _onBehalfOf + ) external; + + function repay( + address _asset, + uint256 _amount, + uint256 _rateMode, + address _onBehalfOf + ) external; + + function setUserUseReserveAsCollateral( + address _asset, + bool _useAsCollateral + ) external; + + function swapBorrowRateMode(address _asset, uint256 _rateMode) external; +} + +interface ATokenInterface { + function scaledBalanceOf(address _user) external view returns (uint256); + + function isTransferAllowed(address _user, uint256 _amount) + external + view + returns (bool); + + function balanceOf(address _user) external view returns (uint256); + + function transferFrom( + address, + address, + uint256 + ) external returns (bool); + + function allowance(address, address) external returns (uint256); +} + +interface AavePoolProviderInterface { + function getPool() external view returns (address); +} + +interface AaveDataProviderInterface { + 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 + ); +} + +interface AaveAddressProviderRegistryInterface { + function getAddressesProvidersList() + external + view + returns (address[] memory); +} diff --git a/contracts/mainnet/connectors/aave/v3-import/main.sol b/contracts/mainnet/connectors/aave/v3-import/main.sol new file mode 100644 index 00000000..244238e9 --- /dev/null +++ b/contracts/mainnet/connectors/aave/v3-import/main.sol @@ -0,0 +1,110 @@ +pragma solidity ^0.7.0; +pragma experimental ABIEncoderV2; +/** + * @title Aave v3 import connector . + * @dev Import EOA's aave V3 position to DSA's aave v3 position + */ +import { TokenInterface, AccountInterface } from "../../../common/interfaces.sol"; +import { AaveInterface, ATokenInterface } from "./interface.sol"; +import "./helpers.sol"; +import "./events.sol"; + +contract AaveV3ImportResolver is AaveHelpers { + function _importAave(address userAccount, ImportInputData memory inputData) + internal + returns (string memory _eventName, bytes memory _eventParam) + { + require( + AccountInterface(address(this)).isAuth(userAccount), + "user-account-not-auth" + ); + + require(inputData.supplyTokens.length > 0, "0-length-not-allowed"); + + ImportData memory data; + + AaveInterface aave = AaveInterface(aaveProvider.getPool()); + + data = getBorrowAmounts(userAccount, aave, inputData, data); + data = getSupplyAmounts(userAccount, inputData, data); + + // payback borrowed amount; + _PaybackStable( + data._borrowTokens.length, + aave, + data._borrowTokens, + data.stableBorrowAmts, + userAccount + ); + _PaybackVariable( + data._borrowTokens.length, + aave, + data._borrowTokens, + data.variableBorrowAmts, + userAccount + ); + + // transfer atokens to this address; + _TransferAtokens( + data._supplyTokens.length, + aave, + data.aTokens, + data.supplyAmts, + data._supplyTokens, + userAccount + ); + + // borrow assets after migrating position + if (data.convertStable) { + _BorrowVariable( + data._borrowTokens.length, + aave, + data._borrowTokens, + data.totalBorrowAmtsWithFee + ); + } else { + _BorrowStable( + data._borrowTokens.length, + aave, + data._borrowTokens, + data.stableBorrowAmtsWithFee + ); + _BorrowVariable( + data._borrowTokens.length, + aave, + data._borrowTokens, + data.variableBorrowAmtsWithFee + ); + } + + _eventName = "LogAaveV3Import(address,bool,address[],address[],uint256[],uint256[],uint256[],uint256[])"; + _eventParam = abi.encode( + userAccount, + inputData.convertStable, + inputData.supplyTokens, + inputData.borrowTokens, + inputData.flashLoanFees, + data.supplyAmts, + data.stableBorrowAmts, + data.variableBorrowAmts + ); + } + + /** + * @dev Import aave V3 position . + * @notice Import EOA's aave V3 position to DSA's aave v3 position + * @param userAccount The address of the EOA from which aave position will be imported + * @param inputData The struct containing all the neccessary input data + */ + function importAave(address userAccount, ImportInputData memory inputData) + external + payable + returns (string memory _eventName, bytes memory _eventParam) + { + (_eventName, _eventParam) = _importAave(userAccount, inputData); + } +} + +contract ConnectV2AaveV3Import is AaveV3ImportResolver { + string public constant name = "Aave-v3-import-v1"; +} diff --git a/contracts/optimism/connectors/aave/v3-import/events.sol b/contracts/optimism/connectors/aave/v3-import/events.sol new file mode 100644 index 00000000..08013251 --- /dev/null +++ b/contracts/optimism/connectors/aave/v3-import/events.sol @@ -0,0 +1,14 @@ +pragma solidity ^0.7.0; +pragma experimental ABIEncoderV2; + +contract Events { + event LogAaveV3Import( + address indexed user, + address[] ctokens, + string[] supplyIds, + string[] borrowIds, + uint256[] flashLoanFees, + uint256[] supplyAmts, + uint256[] borrowAmts + ); +} diff --git a/contracts/optimism/connectors/aave/v3-import/helpers.sol b/contracts/optimism/connectors/aave/v3-import/helpers.sol new file mode 100644 index 00000000..41afc79e --- /dev/null +++ b/contracts/optimism/connectors/aave/v3-import/helpers.sol @@ -0,0 +1,286 @@ +pragma solidity ^0.7.0; + +import { DSMath } from "../../../common/math.sol"; +import { Basic } from "../../../common/basic.sol"; +import { TokenInterface, AccountInterface } from "../../../common/interfaces.sol"; +import { AaveInterface, AavePoolProviderInterface, AaveDataProviderInterface } from "./interface.sol"; +import "./events.sol"; +import "./interface.sol"; + +abstract contract Helper is DSMath, Basic { + /** + * @dev Aave referal code + */ + uint16 internal constant referalCode = 3228; + + /** + * @dev Aave Lending Pool Provider + */ + AavePoolProviderInterface internal constant aaveProvider = + AavePoolProviderInterface(0xa97684ead0e402dC232d5A977953DF7ECBaB3CDb); + + /** + * @dev Aave Protocol Data Provider + */ + AaveDataProviderInterface internal constant aaveData = + AaveDataProviderInterface(0x69FA688f1Dc47d4B5d8029D5a35FB7a548310654); + + function getIsColl(address token, address user) + internal + view + returns (bool isCol) + { + (, , , , , , , , isCol) = aaveData.getUserReserveData(token, user); + } + + struct ImportData { + address[] _supplyTokens; + address[] _borrowTokens; + ATokenInterface[] aTokens; + uint256[] supplyAmts; + uint256[] variableBorrowAmts; + uint256[] variableBorrowAmtsWithFee; + uint256[] stableBorrowAmts; + uint256[] stableBorrowAmtsWithFee; + uint256[] totalBorrowAmts; + uint256[] totalBorrowAmtsWithFee; + bool convertStable; + } + + struct ImportInputData { + address[] supplyTokens; + address[] borrowTokens; + bool convertStable; + uint256[] flashLoanFees; + } +} + +contract AaveHelpers is Helper { + function getBorrowAmount(address _token, address userAccount) + internal + view + returns (uint256 stableBorrow, uint256 variableBorrow) + { + ( + , + address stableDebtTokenAddress, + address variableDebtTokenAddress + ) = aaveData.getReserveTokensAddresses(_token); + + stableBorrow = ATokenInterface(stableDebtTokenAddress).balanceOf( + userAccount + ); + variableBorrow = ATokenInterface(variableDebtTokenAddress).balanceOf( + userAccount + ); + } + + function getBorrowAmounts( + address userAccount, + AaveInterface aave, + ImportInputData memory inputData, + ImportData memory data + ) internal returns (ImportData memory) { + if (inputData.borrowTokens.length > 0) { + data._borrowTokens = new address[](inputData.borrowTokens.length); + data.variableBorrowAmts = new uint256[]( + inputData.borrowTokens.length + ); + data.variableBorrowAmtsWithFee = new uint256[]( + inputData.borrowTokens.length + ); + data.stableBorrowAmts = new uint256[]( + inputData.borrowTokens.length + ); + data.stableBorrowAmtsWithFee = new uint256[]( + inputData.borrowTokens.length + ); + data.totalBorrowAmts = new uint256[](inputData.borrowTokens.length); + data.totalBorrowAmtsWithFee = new uint256[]( + inputData.borrowTokens.length + ); + for (uint256 i = 0; i < inputData.borrowTokens.length; i++) { + for (uint256 j = i; j < inputData.borrowTokens.length; j++) { + if (j != i) { + require( + inputData.borrowTokens[i] != + inputData.borrowTokens[j], + "token-repeated" + ); + } + } + } + for (uint256 i = 0; i < inputData.borrowTokens.length; i++) { + address _token = inputData.borrowTokens[i] == ethAddr + ? wethAddr + : inputData.borrowTokens[i]; + data._borrowTokens[i] = _token; + + ( + data.stableBorrowAmts[i], + data.variableBorrowAmts[i] + ) = getBorrowAmount(_token, userAccount); + + if (data.variableBorrowAmts[i] != 0) { + data.variableBorrowAmtsWithFee[i] = add( + data.variableBorrowAmts[i], + inputData.flashLoanFees[i] + ); + data.stableBorrowAmtsWithFee[i] = data.stableBorrowAmts[i]; + } else { + data.stableBorrowAmtsWithFee[i] = add( + data.stableBorrowAmts[i], + inputData.flashLoanFees[i] + ); + } + + data.totalBorrowAmts[i] = add( + data.stableBorrowAmts[i], + data.variableBorrowAmts[i] + ); + data.totalBorrowAmtsWithFee[i] = add( + data.stableBorrowAmtsWithFee[i], + data.variableBorrowAmtsWithFee[i] + ); + + if (data.totalBorrowAmts[i] > 0) { + uint256 _amt = data.totalBorrowAmts[i]; + TokenInterface(_token).approve(address(aave), _amt); + } + } + } + return data; + } + + function getSupplyAmounts( + address userAccount, + ImportInputData memory inputData, + ImportData memory data + ) internal view returns (ImportData memory) { + data.supplyAmts = new uint256[](inputData.supplyTokens.length); + data._supplyTokens = new address[](inputData.supplyTokens.length); + data.aTokens = new ATokenInterface[](inputData.supplyTokens.length); + + for (uint256 i = 0; i < inputData.supplyTokens.length; i++) { + for (uint256 j = i; j < inputData.supplyTokens.length; j++) { + if (j != i) { + require( + inputData.supplyTokens[i] != inputData.supplyTokens[j], + "token-repeated" + ); + } + } + } + for (uint256 i = 0; i < inputData.supplyTokens.length; i++) { + address _token = inputData.supplyTokens[i] == ethAddr + ? wethAddr + : inputData.supplyTokens[i]; + (address _aToken, , ) = aaveData.getReserveTokensAddresses(_token); + data._supplyTokens[i] = _token; + data.aTokens[i] = ATokenInterface(_aToken); + data.supplyAmts[i] = data.aTokens[i].balanceOf(userAccount); + } + + return data; + } + + function _paybackBehalfOne( + AaveInterface aave, + address token, + uint256 amt, + uint256 rateMode, + address user + ) private { + aave.repay(token, amt, rateMode, user); + } + + function _PaybackStable( + uint256 _length, + AaveInterface aave, + address[] memory tokens, + uint256[] memory amts, + address user + ) internal { + for (uint256 i = 0; i < _length; i++) { + if (amts[i] > 0) { + _paybackBehalfOne(aave, tokens[i], amts[i], 1, user); + } + } + } + + function _PaybackVariable( + uint256 _length, + AaveInterface aave, + address[] memory tokens, + uint256[] memory amts, + address user + ) internal { + for (uint256 i = 0; i < _length; i++) { + if (amts[i] > 0) { + _paybackBehalfOne(aave, tokens[i], amts[i], 2, user); + } + } + } + + function _TransferAtokens( + uint256 _length, + AaveInterface aave, + ATokenInterface[] memory atokenContracts, + uint256[] memory amts, + address[] memory tokens, + address userAccount + ) internal { + for (uint256 i = 0; i < _length; i++) { + if (amts[i] > 0) { + uint256 _amt = amts[i]; + require( + atokenContracts[i].transferFrom( + userAccount, + address(this), + _amt + ), + "allowance?" + ); + + if (!getIsColl(tokens[i], address(this))) { + aave.setUserUseReserveAsCollateral(tokens[i], true); + } + } + } + } + + function _BorrowVariable( + uint256 _length, + AaveInterface aave, + address[] memory tokens, + uint256[] memory amts + ) internal { + for (uint256 i = 0; i < _length; i++) { + if (amts[i] > 0) { + _borrowOne(aave, tokens[i], amts[i], 2); + } + } + } + + function _BorrowStable( + uint256 _length, + AaveInterface aave, + address[] memory tokens, + uint256[] memory amts + ) internal { + for (uint256 i = 0; i < _length; i++) { + if (amts[i] > 0) { + _borrowOne(aave, tokens[i], amts[i], 1); + } + } + } + + function _borrowOne( + AaveInterface aave, + address token, + uint256 amt, + uint256 rateMode + ) private { + aave.borrow(token, amt, rateMode, referalCode, address(this)); + } +} diff --git a/contracts/optimism/connectors/aave/v3-import/interface.sol b/contracts/optimism/connectors/aave/v3-import/interface.sol new file mode 100644 index 00000000..2085007b --- /dev/null +++ b/contracts/optimism/connectors/aave/v3-import/interface.sol @@ -0,0 +1,94 @@ +pragma solidity ^0.7.0; + +interface AaveInterface { + function supply( + address asset, + uint256 amount, + address onBehalfOf, + uint16 referralCode + ) external; + + function withdraw( + address _asset, + uint256 _amount, + address _to + ) external; + + function borrow( + address _asset, + uint256 _amount, + uint256 _interestRateMode, + uint16 _referralCode, + address _onBehalfOf + ) external; + + function repay( + address _asset, + uint256 _amount, + uint256 _rateMode, + address _onBehalfOf + ) external; + + function setUserUseReserveAsCollateral( + address _asset, + bool _useAsCollateral + ) external; + + function swapBorrowRateMode(address _asset, uint256 _rateMode) external; +} + +interface ATokenInterface { + function scaledBalanceOf(address _user) external view returns (uint256); + + function isTransferAllowed(address _user, uint256 _amount) + external + view + returns (bool); + + function balanceOf(address _user) external view returns (uint256); + + function transferFrom( + address, + address, + uint256 + ) external returns (bool); + + function allowance(address, address) external returns (uint256); +} + +interface AavePoolProviderInterface { + function getPool() external view returns (address); +} + +interface AaveDataProviderInterface { + 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 + ); +} + +interface AaveAddressProviderRegistryInterface { + function getAddressesProvidersList() + external + view + returns (address[] memory); +} diff --git a/contracts/optimism/connectors/aave/v3-import/main.sol b/contracts/optimism/connectors/aave/v3-import/main.sol new file mode 100644 index 00000000..052fdcc6 --- /dev/null +++ b/contracts/optimism/connectors/aave/v3-import/main.sol @@ -0,0 +1,110 @@ +pragma solidity ^0.7.0; +pragma experimental ABIEncoderV2; +/** + * @title Aave v3 import connector . + * @dev Import EOA's aave V3 position to DSA's aave v3 position + */ +import { TokenInterface, AccountInterface } from "../../../common/interfaces.sol"; +import { AaveInterface, ATokenInterface } from "./interface.sol"; +import "./helpers.sol"; +import "./events.sol"; + +contract AaveV3ImportResolver is AaveHelpers { + function _importAave(address userAccount, ImportInputData memory inputData) + internal + returns (string memory _eventName, bytes memory _eventParam) + { + require( + AccountInterface(address(this)).isAuth(userAccount), + "user-account-not-auth" + ); + + require(inputData.supplyTokens.length > 0, "0-length-not-allowed"); + + ImportData memory data; + + AaveInterface aave = AaveInterface(aaveProvider.getPool()); + + data = getBorrowAmounts(userAccount, aave, inputData, data); + data = getSupplyAmounts(userAccount, inputData, data); + + // payback borrowed amount; + _PaybackStable( + data._borrowTokens.length, + aave, + data._borrowTokens, + data.stableBorrowAmts, + userAccount + ); + _PaybackVariable( + data._borrowTokens.length, + aave, + data._borrowTokens, + data.variableBorrowAmts, + userAccount + ); + + // transfer atokens to this address; + _TransferAtokens( + data._supplyTokens.length, + aave, + data.aTokens, + data.supplyAmts, + data._supplyTokens, + userAccount + ); + + // borrow assets after migrating position + if (data.convertStable) { + _BorrowVariable( + data._borrowTokens.length, + aave, + data._borrowTokens, + data.totalBorrowAmtsWithFee + ); + } else { + _BorrowStable( + data._borrowTokens.length, + aave, + data._borrowTokens, + data.stableBorrowAmtsWithFee + ); + _BorrowVariable( + data._borrowTokens.length, + aave, + data._borrowTokens, + data.variableBorrowAmtsWithFee + ); + } + + _eventName = "LogAaveV3Import(address,bool,address[],address[],uint256[],uint256[],uint256[],uint256[])"; + _eventParam = abi.encode( + userAccount, + inputData.convertStable, + inputData.supplyTokens, + inputData.borrowTokens, + inputData.flashLoanFees, + data.supplyAmts, + data.stableBorrowAmts, + data.variableBorrowAmts + ); + } + + /** + * @dev Import aave V3 position . + * @notice Import EOA's aave V3 position to DSA's aave v3 position + * @param userAccount The address of the EOA from which aave position will be imported + * @param inputData The struct containing all the neccessary input data + */ + function importAave(address userAccount, ImportInputData memory inputData) + external + payable + returns (string memory _eventName, bytes memory _eventParam) + { + (_eventName, _eventParam) = _importAave(userAccount, inputData); + } +} + +contract ConnectV2AaveV3ImportOptimism is AaveV3ImportResolver { + string public constant name = "Aave-v3-import-v1"; +} diff --git a/contracts/polygon/connectors/aave/v3-import/events.sol b/contracts/polygon/connectors/aave/v3-import/events.sol new file mode 100644 index 00000000..08013251 --- /dev/null +++ b/contracts/polygon/connectors/aave/v3-import/events.sol @@ -0,0 +1,14 @@ +pragma solidity ^0.7.0; +pragma experimental ABIEncoderV2; + +contract Events { + event LogAaveV3Import( + address indexed user, + address[] ctokens, + string[] supplyIds, + string[] borrowIds, + uint256[] flashLoanFees, + uint256[] supplyAmts, + uint256[] borrowAmts + ); +} diff --git a/contracts/polygon/connectors/aave/v3-import/helpers.sol b/contracts/polygon/connectors/aave/v3-import/helpers.sol new file mode 100644 index 00000000..eb054b6f --- /dev/null +++ b/contracts/polygon/connectors/aave/v3-import/helpers.sol @@ -0,0 +1,286 @@ +pragma solidity ^0.7.0; + +import { DSMath } from "../../../common/math.sol"; +import { Basic } from "../../../common/basic.sol"; +import { TokenInterface, AccountInterface } from "../../../common/interfaces.sol"; +import { AaveInterface, AavePoolProviderInterface, AaveDataProviderInterface } from "./interface.sol"; +import "./events.sol"; +import "./interface.sol"; + +abstract contract Helper is DSMath, Basic { + /** + * @dev Aave referal code + */ + uint16 internal constant referalCode = 3228; + + /** + * @dev Aave Lending Pool Provider + */ + AavePoolProviderInterface internal constant aaveProvider = + AavePoolProviderInterface(0xa97684ead0e402dC232d5A977953DF7ECBaB3CDb); + + /** + * @dev Aave Protocol Data Provider + */ + AaveDataProviderInterface internal constant aaveData = + AaveDataProviderInterface(0x69FA688f1Dc47d4B5d8029D5a35FB7a548310654); + + function getIsColl(address token, address user) + internal + view + returns (bool isCol) + { + (, , , , , , , , isCol) = aaveData.getUserReserveData(token, user); + } + + struct ImportData { + address[] _supplyTokens; + address[] _borrowTokens; + ATokenInterface[] aTokens; + uint256[] supplyAmts; + uint256[] variableBorrowAmts; + uint256[] variableBorrowAmtsWithFee; + uint256[] stableBorrowAmts; + uint256[] stableBorrowAmtsWithFee; + uint256[] totalBorrowAmts; + uint256[] totalBorrowAmtsWithFee; + bool convertStable; + } + + struct ImportInputData { + address[] supplyTokens; + address[] borrowTokens; + bool convertStable; + uint256[] flashLoanFees; + } +} + +contract AaveHelpers is Helper { + function getBorrowAmount(address _token, address userAccount) + internal + view + returns (uint256 stableBorrow, uint256 variableBorrow) + { + ( + , + address stableDebtTokenAddress, + address variableDebtTokenAddress + ) = aaveData.getReserveTokensAddresses(_token); + + stableBorrow = ATokenInterface(stableDebtTokenAddress).balanceOf( + userAccount + ); + variableBorrow = ATokenInterface(variableDebtTokenAddress).balanceOf( + userAccount + ); + } + + function getBorrowAmounts( + address userAccount, + AaveInterface aave, + ImportInputData memory inputData, + ImportData memory data + ) internal returns (ImportData memory) { + if (inputData.borrowTokens.length > 0) { + data._borrowTokens = new address[](inputData.borrowTokens.length); + data.variableBorrowAmts = new uint256[]( + inputData.borrowTokens.length + ); + data.variableBorrowAmtsWithFee = new uint256[]( + inputData.borrowTokens.length + ); + data.stableBorrowAmts = new uint256[]( + inputData.borrowTokens.length + ); + data.stableBorrowAmtsWithFee = new uint256[]( + inputData.borrowTokens.length + ); + data.totalBorrowAmts = new uint256[](inputData.borrowTokens.length); + data.totalBorrowAmtsWithFee = new uint256[]( + inputData.borrowTokens.length + ); + for (uint256 i = 0; i < inputData.borrowTokens.length; i++) { + for (uint256 j = i; j < inputData.borrowTokens.length; j++) { + if (j != i) { + require( + inputData.borrowTokens[i] != + inputData.borrowTokens[j], + "token-repeated" + ); + } + } + } + for (uint256 i = 0; i < inputData.borrowTokens.length; i++) { + address _token = inputData.borrowTokens[i] == maticAddr + ? wmaticAddr + : inputData.borrowTokens[i]; + data._borrowTokens[i] = _token; + + ( + data.stableBorrowAmts[i], + data.variableBorrowAmts[i] + ) = getBorrowAmount(_token, userAccount); + + if (data.variableBorrowAmts[i] != 0) { + data.variableBorrowAmtsWithFee[i] = add( + data.variableBorrowAmts[i], + inputData.flashLoanFees[i] + ); + data.stableBorrowAmtsWithFee[i] = data.stableBorrowAmts[i]; + } else { + data.stableBorrowAmtsWithFee[i] = add( + data.stableBorrowAmts[i], + inputData.flashLoanFees[i] + ); + } + + data.totalBorrowAmts[i] = add( + data.stableBorrowAmts[i], + data.variableBorrowAmts[i] + ); + data.totalBorrowAmtsWithFee[i] = add( + data.stableBorrowAmtsWithFee[i], + data.variableBorrowAmtsWithFee[i] + ); + + if (data.totalBorrowAmts[i] > 0) { + uint256 _amt = data.totalBorrowAmts[i]; + TokenInterface(_token).approve(address(aave), _amt); + } + } + } + return data; + } + + function getSupplyAmounts( + address userAccount, + ImportInputData memory inputData, + ImportData memory data + ) internal view returns (ImportData memory) { + data.supplyAmts = new uint256[](inputData.supplyTokens.length); + data._supplyTokens = new address[](inputData.supplyTokens.length); + data.aTokens = new ATokenInterface[](inputData.supplyTokens.length); + + for (uint256 i = 0; i < inputData.supplyTokens.length; i++) { + for (uint256 j = i; j < inputData.supplyTokens.length; j++) { + if (j != i) { + require( + inputData.supplyTokens[i] != inputData.supplyTokens[j], + "token-repeated" + ); + } + } + } + for (uint256 i = 0; i < inputData.supplyTokens.length; i++) { + address _token = inputData.supplyTokens[i] == maticAddr + ? wmaticAddr + : inputData.supplyTokens[i]; + (address _aToken, , ) = aaveData.getReserveTokensAddresses(_token); + data._supplyTokens[i] = _token; + data.aTokens[i] = ATokenInterface(_aToken); + data.supplyAmts[i] = data.aTokens[i].balanceOf(userAccount); + } + + return data; + } + + function _paybackBehalfOne( + AaveInterface aave, + address token, + uint256 amt, + uint256 rateMode, + address user + ) private { + aave.repay(token, amt, rateMode, user); + } + + function _PaybackStable( + uint256 _length, + AaveInterface aave, + address[] memory tokens, + uint256[] memory amts, + address user + ) internal { + for (uint256 i = 0; i < _length; i++) { + if (amts[i] > 0) { + _paybackBehalfOne(aave, tokens[i], amts[i], 1, user); + } + } + } + + function _PaybackVariable( + uint256 _length, + AaveInterface aave, + address[] memory tokens, + uint256[] memory amts, + address user + ) internal { + for (uint256 i = 0; i < _length; i++) { + if (amts[i] > 0) { + _paybackBehalfOne(aave, tokens[i], amts[i], 2, user); + } + } + } + + function _TransferAtokens( + uint256 _length, + AaveInterface aave, + ATokenInterface[] memory atokenContracts, + uint256[] memory amts, + address[] memory tokens, + address userAccount + ) internal { + for (uint256 i = 0; i < _length; i++) { + if (amts[i] > 0) { + uint256 _amt = amts[i]; + require( + atokenContracts[i].transferFrom( + userAccount, + address(this), + _amt + ), + "allowance?" + ); + + if (!getIsColl(tokens[i], address(this))) { + aave.setUserUseReserveAsCollateral(tokens[i], true); + } + } + } + } + + function _BorrowVariable( + uint256 _length, + AaveInterface aave, + address[] memory tokens, + uint256[] memory amts + ) internal { + for (uint256 i = 0; i < _length; i++) { + if (amts[i] > 0) { + _borrowOne(aave, tokens[i], amts[i], 2); + } + } + } + + function _BorrowStable( + uint256 _length, + AaveInterface aave, + address[] memory tokens, + uint256[] memory amts + ) internal { + for (uint256 i = 0; i < _length; i++) { + if (amts[i] > 0) { + _borrowOne(aave, tokens[i], amts[i], 1); + } + } + } + + function _borrowOne( + AaveInterface aave, + address token, + uint256 amt, + uint256 rateMode + ) private { + aave.borrow(token, amt, rateMode, referalCode, address(this)); + } +} diff --git a/contracts/polygon/connectors/aave/v3-import/interface.sol b/contracts/polygon/connectors/aave/v3-import/interface.sol new file mode 100644 index 00000000..2085007b --- /dev/null +++ b/contracts/polygon/connectors/aave/v3-import/interface.sol @@ -0,0 +1,94 @@ +pragma solidity ^0.7.0; + +interface AaveInterface { + function supply( + address asset, + uint256 amount, + address onBehalfOf, + uint16 referralCode + ) external; + + function withdraw( + address _asset, + uint256 _amount, + address _to + ) external; + + function borrow( + address _asset, + uint256 _amount, + uint256 _interestRateMode, + uint16 _referralCode, + address _onBehalfOf + ) external; + + function repay( + address _asset, + uint256 _amount, + uint256 _rateMode, + address _onBehalfOf + ) external; + + function setUserUseReserveAsCollateral( + address _asset, + bool _useAsCollateral + ) external; + + function swapBorrowRateMode(address _asset, uint256 _rateMode) external; +} + +interface ATokenInterface { + function scaledBalanceOf(address _user) external view returns (uint256); + + function isTransferAllowed(address _user, uint256 _amount) + external + view + returns (bool); + + function balanceOf(address _user) external view returns (uint256); + + function transferFrom( + address, + address, + uint256 + ) external returns (bool); + + function allowance(address, address) external returns (uint256); +} + +interface AavePoolProviderInterface { + function getPool() external view returns (address); +} + +interface AaveDataProviderInterface { + 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 + ); +} + +interface AaveAddressProviderRegistryInterface { + function getAddressesProvidersList() + external + view + returns (address[] memory); +} diff --git a/contracts/polygon/connectors/aave/v3-import/main.sol b/contracts/polygon/connectors/aave/v3-import/main.sol new file mode 100644 index 00000000..a43102c5 --- /dev/null +++ b/contracts/polygon/connectors/aave/v3-import/main.sol @@ -0,0 +1,111 @@ +pragma solidity ^0.7.0; +pragma experimental ABIEncoderV2; +/** + * @title Aave v3 import connector . + * @dev Import EOA's aave V3 position to DSA's aave v3 position + */ + +import { TokenInterface, AccountInterface } from "../../../common/interfaces.sol"; +import { AaveInterface, ATokenInterface } from "./interface.sol"; +import "./helpers.sol"; +import "./events.sol"; + +contract AaveV3ImportResolver is AaveHelpers { + function _importAave(address userAccount, ImportInputData memory inputData) + internal + returns (string memory _eventName, bytes memory _eventParam) + { + require( + AccountInterface(address(this)).isAuth(userAccount), + "user-account-not-auth" + ); + + require(inputData.supplyTokens.length > 0, "0-length-not-allowed"); + + ImportData memory data; + + AaveInterface aave = AaveInterface(aaveProvider.getPool()); + + data = getBorrowAmounts(userAccount, aave, inputData, data); + data = getSupplyAmounts(userAccount, inputData, data); + + // payback borrowed amount; + _PaybackStable( + data._borrowTokens.length, + aave, + data._borrowTokens, + data.stableBorrowAmts, + userAccount + ); + _PaybackVariable( + data._borrowTokens.length, + aave, + data._borrowTokens, + data.variableBorrowAmts, + userAccount + ); + + // transfer atokens to this address; + _TransferAtokens( + data._supplyTokens.length, + aave, + data.aTokens, + data.supplyAmts, + data._supplyTokens, + userAccount + ); + + // borrow assets after migrating position + if (data.convertStable) { + _BorrowVariable( + data._borrowTokens.length, + aave, + data._borrowTokens, + data.totalBorrowAmtsWithFee + ); + } else { + _BorrowStable( + data._borrowTokens.length, + aave, + data._borrowTokens, + data.stableBorrowAmtsWithFee + ); + _BorrowVariable( + data._borrowTokens.length, + aave, + data._borrowTokens, + data.variableBorrowAmtsWithFee + ); + } + + _eventName = "LogAaveV3Import(address,bool,address[],address[],uint256[],uint256[],uint256[],uint256[])"; + _eventParam = abi.encode( + userAccount, + inputData.convertStable, + inputData.supplyTokens, + inputData.borrowTokens, + inputData.flashLoanFees, + data.supplyAmts, + data.stableBorrowAmts, + data.variableBorrowAmts + ); + } + + /** + * @dev Import aave V3 position . + * @notice Import EOA's aave V3 position to DSA's aave v3 position + * @param userAccount The address of the EOA from which aave position will be imported + * @param inputData The struct containing all the neccessary input data + */ + function importAave(address userAccount, ImportInputData memory inputData) + external + payable + returns (string memory _eventName, bytes memory _eventParam) + { + (_eventName, _eventParam) = _importAave(userAccount, inputData); + } +} + +contract ConnectV2AaveV3ImportPolygon is AaveV3ImportResolver { + string public constant name = "Aave-v3-import-v1"; +} diff --git a/hardhat.config.ts b/hardhat.config.ts index 5edfbdf3..9b7e4eb6 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -37,9 +37,9 @@ const PRIVATE_KEY = process.env.PRIVATE_KEY; const ETHERSCAN_API = process.env.ETHERSCAN_API_KEY; const POLYGONSCAN_API = process.env.POLYGON_API_KEY; const ARBISCAN_API = process.env.ARBISCAN_API_KEY; -const OPTIMISM_API = process.env.OPTIMISM_API_KEY; const SNOWTRACE_API = process.env.SNOWTRACE_API_KEY; const FANTOMSCAN_API = process.env.FANTOM_API_KEY; +const OPTIMISM_API = process.env.OPTIMISM_API_KEY; const mnemonic = process.env.MNEMONIC ?? "test test test test test test test test test test test junk"; const networkGasPriceConfig: Record = { @@ -71,6 +71,7 @@ function getScanApiKey(networkType: string) { else if (networkType === "polygon") return POLYGONSCAN_API; else if (networkType === "arbitrum") return ARBISCAN_API; else if (networkType === "fantom") return FANTOMSCAN_API; + else if (networkType === "fantom") return FANTOMSCAN_API; else if (networkType === "optimism") return OPTIMISM_API; else return ETHERSCAN_API; } diff --git a/package.json b/package.json index 84f57114..0deaf533 100644 --- a/package.json +++ b/package.json @@ -35,7 +35,7 @@ "chalk": "^5.0.0", "dotenv": "^10.0.0", "hardhat-docgen": "^1.2.0", - "inquirer": "^8.2.0", + "inquirer": "^8.2.0", "minimist": "^1.2.5", "solc": "^0.8.10", "typechain": "^6.0.5"