diff --git a/contracts/base/connectors/aave/aave-v3-rewards/events.sol b/contracts/base/connectors/aave/aave-v3-rewards/events.sol new file mode 100644 index 00000000..683f3049 --- /dev/null +++ b/contracts/base/connectors/aave/aave-v3-rewards/events.sol @@ -0,0 +1,13 @@ +//SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; + +contract Events { + event LogClaimed( + address[] assets, + uint256 amt, + uint256 getId, + uint256 setId + ); + + event LogAllClaimed(address[] assets, address[] rewards, uint256[] amts); +} diff --git a/contracts/base/connectors/aave/aave-v3-rewards/helpers.sol b/contracts/base/connectors/aave/aave-v3-rewards/helpers.sol new file mode 100644 index 00000000..db50b3bf --- /dev/null +++ b/contracts/base/connectors/aave/aave-v3-rewards/helpers.sol @@ -0,0 +1,14 @@ +//SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; + +import { DSMath } from "../../../common/math.sol"; +import { Basic } from "../../../common/basic.sol"; +import { AaveIncentivesInterface } from "./interface.sol"; + +abstract contract Helpers is DSMath, Basic { + /** + * @dev Aave v3 Incentives + */ + AaveIncentivesInterface internal constant incentives = + AaveIncentivesInterface(0xf9cc4F0D883F1a1eb2c253bdb46c254Ca51E1F44); +} diff --git a/contracts/base/connectors/aave/aave-v3-rewards/interface.sol b/contracts/base/connectors/aave/aave-v3-rewards/interface.sol new file mode 100644 index 00000000..f2ab4704 --- /dev/null +++ b/contracts/base/connectors/aave/aave-v3-rewards/interface.sol @@ -0,0 +1,15 @@ +//SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; + +interface AaveIncentivesInterface { + function claimRewards( + address[] calldata assets, + uint256 amount, + address to, + address reward + ) external returns (uint256); + + function claimAllRewards(address[] calldata assets, address to) + external + returns (address[] memory rewardsList, uint256[] memory claimedAmounts); +} diff --git a/contracts/base/connectors/aave/aave-v3-rewards/main.sol b/contracts/base/connectors/aave/aave-v3-rewards/main.sol new file mode 100644 index 00000000..5a6263b5 --- /dev/null +++ b/contracts/base/connectors/aave/aave-v3-rewards/main.sol @@ -0,0 +1,78 @@ +//SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; + +/** + * @title Aave v3 Rewards. + * @dev Claim Aave v3 rewards. + */ + +import { TokenInterface } from "../../../common/interfaces.sol"; +import { Stores } from "../../../common/stores.sol"; +import { Helpers } from "./helpers.sol"; +import { Events } from "./events.sol"; + +abstract contract IncentivesResolver is Helpers, Events { + /** + * @dev Claim Pending Rewards. + * @notice Claim Pending Rewards from Aave v3 incentives contract. + * @param assets The list of assets supplied and borrowed. + * @param amt The amount of reward to claim. (uint(-1) for max) + * @param reward The address of reward token to claim. + * @param getId ID to retrieve amt. + * @param setId ID stores the amount of rewards claimed. + */ + function claim( + address[] calldata assets, + uint256 amt, + address reward, + uint256 getId, + uint256 setId + ) + external + payable + returns (string memory _eventName, bytes memory _eventParam) + { + uint256 _amt = getUint(getId, amt); + + require(assets.length > 0, "invalid-assets"); + + _amt = incentives.claimRewards(assets, _amt, address(this), reward); + + TokenInterface weth = TokenInterface(wethAddr); + uint256 wethAmount = weth.balanceOf(address(this)); + convertWethToEth(wethAmount > 0, weth, wethAmount); + + setUint(setId, _amt); + + _eventName = "LogClaimed(address[],uint256,uint256,uint256)"; + _eventParam = abi.encode(assets, _amt, getId, setId); + } + + /** + * @dev Claim All Pending Rewards. + * @notice Claim All Pending Rewards from Aave v3 incentives contract. + * @param assets The list of assets supplied and borrowed. + */ + function claimAll(address[] calldata assets) + external + payable + returns (string memory _eventName, bytes memory _eventParam) + { + require(assets.length > 0, "invalid-assets"); + uint256[] memory _amts = new uint256[](assets.length); + address[] memory _rewards = new address[](assets.length); + + (_rewards, _amts) = incentives.claimAllRewards(assets, address(this)); + + TokenInterface weth = TokenInterface(wethAddr); + uint256 wethAmount = weth.balanceOf(address(this)); + convertWethToEth(wethAmount > 0, weth, wethAmount); + + _eventName = "LogAllClaimed(address[],address[],uint256[])"; + _eventParam = abi.encode(assets, _rewards, _amts); + } +} + +contract ConnectV3AaveIncentivesBase is IncentivesResolver { + string public constant name = "Aave-V3-Incentives-v1"; +} diff --git a/contracts/base/connectors/aave/v3-import-permit/events.sol b/contracts/base/connectors/aave/v3-import-permit/events.sol new file mode 100644 index 00000000..75d3c8bb --- /dev/null +++ b/contracts/base/connectors/aave/v3-import-permit/events.sol @@ -0,0 +1,25 @@ +//SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; +pragma experimental ABIEncoderV2; + +contract Events { + event LogAaveV3ImportWithPermit( + address indexed user, + address[] atokens, + string[] supplyIds, + string[] borrowIds, + uint256[] flashLoanFees, + uint256[] supplyAmts, + uint256[] borrowAmts + ); + event LogAaveV3ImportWithPermitAndCollateral( + address indexed user, + address[] atokens, + string[] supplyIds, + string[] borrowIds, + uint256[] flashLoanFees, + uint256[] supplyAmts, + uint256[] borrowAmts, + bool[] enableCollateral + ); +} diff --git a/contracts/base/connectors/aave/v3-import-permit/helpers.sol b/contracts/base/connectors/aave/v3-import-permit/helpers.sol new file mode 100644 index 00000000..c6203bc1 --- /dev/null +++ b/contracts/base/connectors/aave/v3-import-permit/helpers.sol @@ -0,0 +1,344 @@ +//SPDX-License-Identifier: MIT +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(0xe20fCBdBfFC4Dd138cE8b2E6FBb6CB49777ad64D); + + /** + * @dev Aave Protocol Data Provider + */ + AaveDataProviderInterface internal constant aaveData = + AaveDataProviderInterface(0x2d8A3C5677189723C4cB8873CfC9C8976FDF38Ac); + + 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; + } + + struct SignedPermits { + uint8[] v; + bytes32[] r; + bytes32[] s; + uint256[] expiry; + } +} + +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 _PermitATokens( + address userAccount, + ATokenInterface[] memory aTokenContracts, + address[] memory tokens, + uint8[] memory v, + bytes32[] memory r, + bytes32[] memory s, + uint256[] memory expiry + ) internal { + for (uint256 i = 0; i < tokens.length; i++) { + aTokenContracts[i].permit( + userAccount, + address(this), + uint256(-1), + expiry[i], + v[i], + r[i], + s[i] + ); + } + } + + 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 _TransferAtokensWithCollateral( + uint256 _length, + AaveInterface aave, + ATokenInterface[] memory atokenContracts, + uint256[] memory amts, + address[] memory tokens, + bool[] memory colEnable, + 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], colEnable[i]); + } + } + } + } + + 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/base/connectors/aave/v3-import-permit/interface.sol b/contracts/base/connectors/aave/v3-import-permit/interface.sol new file mode 100644 index 00000000..f7ceed9e --- /dev/null +++ b/contracts/base/connectors/aave/v3-import-permit/interface.sol @@ -0,0 +1,117 @@ +//SPDX-License-Identifier: MIT +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; + + function getUserAccountData(address user) + external + view + returns ( + uint256 totalCollateralBase, + uint256 totalDebtBase, + uint256 availableBorrowsBase, + uint256 currentLiquidationThreshold, + uint256 ltv, + uint256 healthFactor + ); +} + +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 permit( + address owner, + address spender, + uint256 value, + uint256 deadline, + uint8 v, + bytes32 r, + bytes32 s + ) external; + + 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/base/connectors/aave/v3-import-permit/main.sol b/contracts/base/connectors/aave/v3-import-permit/main.sol new file mode 100644 index 00000000..417b8f4a --- /dev/null +++ b/contracts/base/connectors/aave/v3-import-permit/main.sol @@ -0,0 +1,258 @@ +//SPDX-License-Identifier: MIT +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 AaveV3ImportPermitResolver is AaveHelpers { + function _importAave( + address userAccount, + ImportInputData memory inputData, + SignedPermits memory permitData + ) 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 + ); + + //permit this address to transfer aTokens + _PermitATokens( + userAccount, + data.aTokens, + data._supplyTokens, + permitData.v, + permitData.r, + permitData.s, + permitData.expiry + ); + + // 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 = "LogAaveV3ImportWithPermit(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 + ); + } + + function _importAaveWithCollateral( + address userAccount, + ImportInputData memory inputData, + SignedPermits memory permitData, + bool[] memory enableCollateral + ) 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"); + require( + enableCollateral.length == inputData.supplyTokens.length, + "supplytokens-enableCol-len-not-same" + ); + + 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 + ); + + //permit this address to transfer aTokens + _PermitATokens( + userAccount, + data.aTokens, + data._supplyTokens, + permitData.v, + permitData.r, + permitData.s, + permitData.expiry + ); + + // transfer atokens to this address; + _TransferAtokensWithCollateral( + data._supplyTokens.length, + aave, + data.aTokens, + data.supplyAmts, + data._supplyTokens, + enableCollateral, + 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 = "LogAaveV3ImportWithPermitAndCollateral(address,bool,address[],address[],uint256[],uint256[],uint256[],uint256[],bool[])"; + _eventParam = abi.encode( + userAccount, + inputData.convertStable, + inputData.supplyTokens, + inputData.borrowTokens, + inputData.flashLoanFees, + data.supplyAmts, + data.stableBorrowAmts, + data.variableBorrowAmts, + enableCollateral + ); + } + + /** + * @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 + * @param permitData The struct containing signed permit data like v,r,s,expiry + */ + function importAave( + address userAccount, + ImportInputData memory inputData, + SignedPermits memory permitData + ) + external + payable + returns (string memory _eventName, bytes memory _eventParam) + { + (_eventName, _eventParam) = _importAave( + userAccount, + inputData, + permitData + ); + } + + /** + * @dev Import aave V3 position (with collateral). + * @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 + * @param permitData The struct containing signed permit data like v,r,s,expiry + * @param enableCollateral The boolean array to enable selected collaterals in the imported position + */ + function importAaveWithCollateral( + address userAccount, + ImportInputData memory inputData, + SignedPermits memory permitData, + bool[] memory enableCollateral + ) + external + payable + returns (string memory _eventName, bytes memory _eventParam) + { + (_eventName, _eventParam) = _importAaveWithCollateral( + userAccount, + inputData, + permitData, + enableCollateral + ); + } +} + +contract ConnectV2AaveV3ImportPermitBase is AaveV3ImportPermitResolver { + string public constant name = "Aave-v3-import-permit-v1"; +} diff --git a/contracts/base/connectors/aave/v3-import/events.sol b/contracts/base/connectors/aave/v3-import/events.sol new file mode 100644 index 00000000..c9aaeebb --- /dev/null +++ b/contracts/base/connectors/aave/v3-import/events.sol @@ -0,0 +1,25 @@ +//SPDX-License-Identifier: MIT +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 + ); + event LogAaveV3ImportWithCollateral( + address indexed user, + address[] atokens, + string[] supplyIds, + string[] borrowIds, + uint256[] flashLoanFees, + uint256[] supplyAmts, + uint256[] borrowAmts, + bool[] enableCollateral + ); +} diff --git a/contracts/base/connectors/aave/v3-import/helpers.sol b/contracts/base/connectors/aave/v3-import/helpers.sol new file mode 100644 index 00000000..e9d3600f --- /dev/null +++ b/contracts/base/connectors/aave/v3-import/helpers.sol @@ -0,0 +1,315 @@ +//SPDX-License-Identifier: MIT +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(0xe20fCBdBfFC4Dd138cE8b2E6FBb6CB49777ad64D); + + /** + * @dev Aave Protocol Data Provider + */ + AaveDataProviderInterface internal constant aaveData = + AaveDataProviderInterface(0x2d8A3C5677189723C4cB8873CfC9C8976FDF38Ac); + + 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 _TransferAtokensWithCollateral( + uint256 _length, + AaveInterface aave, + ATokenInterface[] memory atokenContracts, + uint256[] memory amts, + address[] memory tokens, + bool[] memory colEnable, + 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], colEnable[i]); + } + } + } + } + + 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/base/connectors/aave/v3-import/interface.sol b/contracts/base/connectors/aave/v3-import/interface.sol new file mode 100644 index 00000000..384e7369 --- /dev/null +++ b/contracts/base/connectors/aave/v3-import/interface.sol @@ -0,0 +1,95 @@ +//SPDX-License-Identifier: MIT +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/base/connectors/aave/v3-import/main.sol b/contracts/base/connectors/aave/v3-import/main.sol new file mode 100644 index 00000000..66fea0e3 --- /dev/null +++ b/contracts/base/connectors/aave/v3-import/main.sol @@ -0,0 +1,209 @@ +//SPDX-License-Identifier: MIT +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 + ); + } + + function _importAaveWithCollateral(address userAccount, ImportInputData memory inputData, bool[] memory enableCollateral) + 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"); + require(enableCollateral.length == inputData.supplyTokens.length, "lengths-not-same"); + + 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; + _TransferAtokensWithCollateral( + data._supplyTokens.length, + aave, + data.aTokens, + data.supplyAmts, + data._supplyTokens, + enableCollateral, + 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 = "LogAaveV3ImportWithCollateral(address,bool,address[],address[],uint256[],uint256[],uint256[],uint256[],bool[])"; + _eventParam = abi.encode( + userAccount, + inputData.convertStable, + inputData.supplyTokens, + inputData.borrowTokens, + inputData.flashLoanFees, + data.supplyAmts, + data.stableBorrowAmts, + data.variableBorrowAmts, + enableCollateral + ); + } + + /** + * @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); + } + + /** + * @dev Import aave V3 position (with collateral). + * @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 + * @param enableCollateral The boolean array to enable selected collaterals in the imported position + */ + function importAaveWithCollateral(address userAccount, ImportInputData memory inputData, bool[] memory enableCollateral) + external + payable + returns (string memory _eventName, bytes memory _eventParam) + { + (_eventName, _eventParam) = _importAaveWithCollateral(userAccount, inputData, enableCollateral); + } +} + +contract ConnectV2AaveV3ImportBase is AaveV3ImportResolver { + string public constant name = "Aave-v3-import-v1.1"; +} diff --git a/contracts/base/connectors/aave/v3/events.sol b/contracts/base/connectors/aave/v3/events.sol new file mode 100644 index 00000000..e60aaa12 --- /dev/null +++ b/contracts/base/connectors/aave/v3/events.sol @@ -0,0 +1,64 @@ +//SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; + +contract Events { + event LogDeposit( + address indexed token, + uint256 tokenAmt, + uint256 getId, + uint256 setId + ); + event LogWithdraw( + address indexed token, + uint256 tokenAmt, + uint256 getId, + uint256 setId + ); + event LogBorrow( + address indexed token, + uint256 tokenAmt, + uint256 indexed rateMode, + uint256 getId, + uint256 setId + ); + event LogPayback( + address indexed token, + uint256 tokenAmt, + uint256 indexed rateMode, + uint256 getId, + uint256 setId + ); + event LogEnableCollateral(address[] tokens); + event LogSwapRateMode(address indexed token, uint256 rateMode); + event LogSetUserEMode(uint8 categoryId); + event LogDelegateBorrow( + address token, + uint256 amount, + uint256 rateMode, + address delegateTo, + uint256 getId, + uint256 setId + ); + event LogDepositWithoutCollateral( + address token, + uint256 amt, + uint256 getId, + uint256 setId + ); + event LogBorrowOnBehalfOf( + address token, + uint256 amt, + uint256 rateMode, + address onBehalfOf, + uint256 getId, + uint256 setId + ); + event LogPaybackOnBehalfOf( + address token, + uint256 amt, + uint256 rateMode, + address onBehalfOf, + uint256 getId, + uint256 setId + ); +} diff --git a/contracts/base/connectors/aave/v3/helpers.sol b/contracts/base/connectors/aave/v3/helpers.sol new file mode 100644 index 00000000..68e9a472 --- /dev/null +++ b/contracts/base/connectors/aave/v3/helpers.sol @@ -0,0 +1,99 @@ +//SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; + +import { DSMath } from "../../../common/math.sol"; +import { Basic } from "../../../common/basic.sol"; +import { AavePoolProviderInterface, AaveDataProviderInterface } from "./interface.sol"; + +abstract contract Helpers is DSMath, Basic { + /** + * @dev Aave Pool Provider + */ + AavePoolProviderInterface internal constant aaveProvider = + AavePoolProviderInterface(0xe20fCBdBfFC4Dd138cE8b2E6FBb6CB49777ad64D); + + /** + * @dev Aave Pool Data Provider + */ + AaveDataProviderInterface internal constant aaveData = + AaveDataProviderInterface(0x2d8A3C5677189723C4cB8873CfC9C8976FDF38Ac); + + /** + * @dev Aave Referral Code + */ + uint16 internal constant referralCode = 3228; + + /** + * @dev Checks if collateral is enabled for an asset + * @param token token address of the asset.(For ETH: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) + */ + + function getIsColl(address token) internal view returns (bool isCol) { + (, , , , , , , , isCol) = aaveData.getUserReserveData( + token, + address(this) + ); + } + + /** + * @dev Get total debt balance & fee for an asset + * @param token token address of the debt.(For ETH: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) + * @param rateMode Borrow rate mode (Stable = 1, Variable = 2) + */ + function getPaybackBalance(address token, uint256 rateMode) + internal + view + returns (uint256) + { + (, uint256 stableDebt, uint256 variableDebt, , , , , , ) = aaveData + .getUserReserveData(token, address(this)); + return rateMode == 1 ? stableDebt : variableDebt; + } + + /** + * @dev Get OnBehalfOf user's total debt balance & fee for an asset + * @param token token address of the debt.(For ETH: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) + * @param rateMode Borrow rate mode (Stable = 1, Variable = 2) + */ + function getOnBehalfOfPaybackBalance(address token, uint256 rateMode, address onBehalfOf) + internal + view + returns (uint256) + { + (, uint256 stableDebt, uint256 variableDebt, , , , , , ) = aaveData + .getUserReserveData(token, onBehalfOf); + return rateMode == 1 ? stableDebt : variableDebt; + } + + /** + * @dev Get total collateral balance for an asset + * @param token token address of the collateral.(For ETH: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) + */ + function getCollateralBalance(address token) + internal + view + returns (uint256 bal) + { + (bal, , , , , , , , ) = aaveData.getUserReserveData( + token, + address(this) + ); + } + + /** + * @dev Get debt token address for an asset + * @param token token address of the asset + * @param rateMode Debt type: stable-1, variable-2 + */ + function getDTokenAddr(address token, uint256 rateMode) + internal + view + returns(address dToken) + { + if (rateMode == 1) { + (, dToken, ) = aaveData.getReserveTokensAddresses(token); + } else { + (, , dToken) = aaveData.getReserveTokensAddresses(token); + } + } +} diff --git a/contracts/base/connectors/aave/v3/interface.sol b/contracts/base/connectors/aave/v3/interface.sol new file mode 100644 index 00000000..6de08261 --- /dev/null +++ b/contracts/base/connectors/aave/v3/interface.sol @@ -0,0 +1,96 @@ +//SPDX-License-Identifier: MIT +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 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 swapBorrowRateMode(address asset, uint256 interestRateMode) + external; + + function setUserEMode(uint8 categoryId) external; +} + +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 + ); + + function getReserveEModeCategory(address asset) + external + view + returns (uint256); +} + +interface AaveAddressProviderRegistryInterface { + function getAddressesProvidersList() + external + view + returns (address[] memory); +} + +interface ATokenInterface { + function balanceOf(address _user) external view returns (uint256); +} + +interface DTokenInterface { + function approveDelegation(address delegatee, uint256 amount) external; +} diff --git a/contracts/base/connectors/aave/v3/main.sol b/contracts/base/connectors/aave/v3/main.sol new file mode 100644 index 00000000..7c3aa530 --- /dev/null +++ b/contracts/base/connectors/aave/v3/main.sol @@ -0,0 +1,494 @@ +//SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; + +/** + * @title Aave v3. + * @dev Lending & Borrowing. + */ + +import { TokenInterface } from "../../../common/interfaces.sol"; +import { Stores } from "../../../common/stores.sol"; +import { Helpers } from "./helpers.sol"; +import { Events } from "./events.sol"; +import { AaveInterface, DTokenInterface } from "./interface.sol"; + +abstract contract AaveResolver is Events, Helpers { + /** + * @dev Deposit ETH/ERC20_Token. + * @notice Deposit a token to Aave v3 for lending / collaterization. + * @param token The address of the token to deposit.(For ETH: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) + * @param amt The amount of the token to deposit. (For max: `uint256(-1)`) + * @param getId ID to retrieve amt. + * @param setId ID stores the amount of tokens deposited. + */ + function deposit( + address token, + uint256 amt, + uint256 getId, + uint256 setId + ) + external + payable + returns (string memory _eventName, bytes memory _eventParam) + { + uint256 _amt = getUint(getId, amt); + + AaveInterface aave = AaveInterface(aaveProvider.getPool()); + + bool isEth = token == ethAddr; + address _token = isEth ? wethAddr : token; + + TokenInterface tokenContract = TokenInterface(_token); + + if (isEth) { + _amt = _amt == uint256(-1) ? address(this).balance : _amt; + convertEthToWeth(isEth, tokenContract, _amt); + } else { + _amt = _amt == uint256(-1) + ? tokenContract.balanceOf(address(this)) + : _amt; + } + + approve(tokenContract, address(aave), _amt); + + aave.supply(_token, _amt, address(this), referralCode); + + if (!getIsColl(_token)) { + aave.setUserUseReserveAsCollateral(_token, true); + } + + setUint(setId, _amt); + + _eventName = "LogDeposit(address,uint256,uint256,uint256)"; + _eventParam = abi.encode(token, _amt, getId, setId); + } + + /** + * @dev Deposit ETH/ERC20_Token without collateral + * @notice Deposit a token to Aave v3 without enabling it as collateral. + * @param token The address of the token to deposit.(For ETH: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) + * @param amt The amount of the token to deposit. (For max: `uint256(-1)`) + * @param getId ID to retrieve amt. + * @param setId ID stores the amount of tokens deposited. + */ + function depositWithoutCollateral( + address token, + uint256 amt, + uint256 getId, + uint256 setId + ) + external + payable + returns (string memory _eventName, bytes memory _eventParam) + { + uint256 _amt = getUint(getId, amt); + + AaveInterface aave = AaveInterface(aaveProvider.getPool()); + + bool isEth = token == ethAddr; + address _token = isEth ? wethAddr : token; + + TokenInterface tokenContract = TokenInterface(_token); + + if (isEth) { + _amt = _amt == uint256(-1) ? address(this).balance : _amt; + convertEthToWeth(isEth, tokenContract, _amt); + } else { + _amt = _amt == uint256(-1) + ? tokenContract.balanceOf(address(this)) + : _amt; + } + + approve(tokenContract, address(aave), _amt); + + aave.supply(_token, _amt, address(this), referralCode); + + if (getCollateralBalance(_token) > 0 && getIsColl(_token)) { + aave.setUserUseReserveAsCollateral(_token, false); + } + + setUint(setId, _amt); + + _eventName = "LogDepositWithoutCollateral(address,uint256,uint256,uint256)"; + _eventParam = abi.encode(token, _amt, getId, setId); + } + + /** + * @dev Withdraw ETH/ERC20_Token. + * @notice Withdraw deposited token from Aave v3 + * @param token The address of the token to withdraw.(For ETH: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) + * @param amt The amount of the token to withdraw. (For max: `uint256(-1)`) + * @param getId ID to retrieve amt. + * @param setId ID stores the amount of tokens withdrawn. + */ + function withdraw( + address token, + uint256 amt, + uint256 getId, + uint256 setId + ) + external + payable + returns (string memory _eventName, bytes memory _eventParam) + { + uint256 _amt = getUint(getId, amt); + + AaveInterface aave = AaveInterface(aaveProvider.getPool()); + bool isEth = token == ethAddr; + address _token = isEth ? wethAddr : token; + + TokenInterface tokenContract = TokenInterface(_token); + + uint256 initialBal = tokenContract.balanceOf(address(this)); + aave.withdraw(_token, _amt, address(this)); + uint256 finalBal = tokenContract.balanceOf(address(this)); + + _amt = sub(finalBal, initialBal); + + convertWethToEth(isEth, tokenContract, _amt); + + setUint(setId, _amt); + + _eventName = "LogWithdraw(address,uint256,uint256,uint256)"; + _eventParam = abi.encode(token, _amt, getId, setId); + } + + /** + * @dev Borrow ETH/ERC20_Token. + * @notice Borrow a token using Aave v3 + * @param token The address of the token to borrow.(For ETH: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) + * @param amt The amount of the token to borrow. + * @param rateMode The type of debt. (For Stable: 1, Variable: 2) + * @param getId ID to retrieve amt. + * @param setId ID stores the amount of tokens borrowed. + */ + function borrow( + address token, + uint256 amt, + uint256 rateMode, + uint256 getId, + uint256 setId + ) + external + payable + returns (string memory _eventName, bytes memory _eventParam) + { + uint256 _amt = getUint(getId, amt); + + AaveInterface aave = AaveInterface(aaveProvider.getPool()); + + bool isEth = token == ethAddr; + address _token = isEth ? wethAddr : token; + + aave.borrow(_token, _amt, rateMode, referralCode, address(this)); + convertWethToEth(isEth, TokenInterface(_token), _amt); + + setUint(setId, _amt); + + _eventName = "LogBorrow(address,uint256,uint256,uint256,uint256)"; + _eventParam = abi.encode(token, _amt, rateMode, getId, setId); + } + + /** + * @dev Borrow ETH/ERC20_Token on behalf of a user. + * @notice Borrow a token using Aave v3 on behalf of a user + * @param token The address of the token to borrow.(For ETH: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) + * @param amt The amount of the token to borrow. + * @param rateMode The type of debt. (For Stable: 1, Variable: 2) + * @param onBehalfOf The user who will incur the debt + * @param getId ID to retrieve amt. + * @param setId ID stores the amount of tokens borrowed. + */ + function borrowOnBehalfOf( + address token, + uint256 amt, + uint256 rateMode, + address onBehalfOf, + uint256 getId, + uint256 setId + ) + external + payable + returns (string memory _eventName, bytes memory _eventParam) + { + uint256 _amt = getUint(getId, amt); + + AaveInterface aave = AaveInterface(aaveProvider.getPool()); + + bool isEth = token == ethAddr; + address _token = isEth ? wethAddr : token; + + aave.borrow(_token, _amt, rateMode, referralCode, onBehalfOf); + convertWethToEth(isEth, TokenInterface(_token), _amt); + + setUint(setId, _amt); + + _eventName = "LogBorrowOnBehalfOf(address,uint256,uint256,address,uint256,uint256)"; + _eventParam = abi.encode( + token, + _amt, + rateMode, + onBehalfOf, + getId, + setId + ); + } + + /** + * @dev Payback borrowed ETH/ERC20_Token. + * @notice Payback debt owed. + * @param token The address of the token to payback.(For ETH: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) + * @param amt The amount of the token to payback. (For max: `uint256(-1)`) + * @param rateMode The type of debt paying back. (For Stable: 1, Variable: 2) + * @param getId ID to retrieve amt. + * @param setId ID stores the amount of tokens paid back. + */ + function payback( + address token, + uint256 amt, + uint256 rateMode, + uint256 getId, + uint256 setId + ) + external + payable + returns (string memory _eventName, bytes memory _eventParam) + { + uint256 _amt = getUint(getId, amt); + + AaveInterface aave = AaveInterface(aaveProvider.getPool()); + + bool isEth = token == ethAddr; + address _token = isEth ? wethAddr : token; + + TokenInterface tokenContract = TokenInterface(_token); + + _amt = _amt == uint256(-1) ? getPaybackBalance(_token, rateMode) : _amt; + + if (isEth) convertEthToWeth(isEth, tokenContract, _amt); + + approve(tokenContract, address(aave), _amt); + + aave.repay(_token, _amt, rateMode, address(this)); + + setUint(setId, _amt); + + _eventName = "LogPayback(address,uint256,uint256,uint256,uint256)"; + _eventParam = abi.encode(token, _amt, rateMode, getId, setId); + } + + /** + * @dev Payback borrowed ETH/ERC20_Token using aTokens. + * @notice Repays a borrowed `amount` on a specific reserve using the reserve aTokens, burning the equivalent debt tokens. + * @param token The address of the token to payback.(For ETH: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) + * @param amt The amount of the token to payback. (For max: `uint256(-1)`) + * @param rateMode The type of debt paying back. (For Stable: 1, Variable: 2) + * @param getId ID to retrieve amt. + * @param setId ID stores the amount of tokens paid back. + */ + function paybackWithATokens( + address token, + uint256 amt, + uint256 rateMode, + uint256 getId, + uint256 setId + ) + external + payable + returns (string memory _eventName, bytes memory _eventParam) + { + uint256 _amt = getUint(getId, amt); + + AaveInterface aave = AaveInterface(aaveProvider.getPool()); + + bool isEth = token == ethAddr; + address _token = isEth ? wethAddr : token; + + TokenInterface tokenContract = TokenInterface(_token); + + _amt = _amt == uint256(-1) ? getPaybackBalance(_token, rateMode) : _amt; + + if (isEth) convertEthToWeth(isEth, tokenContract, _amt); + + approve(tokenContract, address(aave), _amt); + + aave.repayWithATokens(_token, _amt, rateMode); + + setUint(setId, _amt); + + _eventName = "LogPayback(address,uint256,uint256,uint256,uint256)"; + _eventParam = abi.encode(token, _amt, rateMode, getId, setId); + } + + /** + * @dev Payback borrowed ETH/ERC20_Token on behalf of a user. + * @notice Payback debt owed on behalf os a user. + * @param token The address of the token to payback.(For ETH: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) + * @param amt The amount of the token to payback. (For max: `uint256(-1)`) + * @param rateMode The type of debt paying back. (For Stable: 1, Variable: 2) + * @param onBehalfOf Address of user who's debt to repay. + * @param getId ID to retrieve amt. + * @param setId ID stores the amount of tokens paid back. + */ + function paybackOnBehalfOf( + address token, + uint256 amt, + uint256 rateMode, + address onBehalfOf, + uint256 getId, + uint256 setId + ) + external + payable + returns (string memory _eventName, bytes memory _eventParam) + { + uint256 _amt = getUint(getId, amt); + + AaveInterface aave = AaveInterface(aaveProvider.getPool()); + + bool isEth = token == ethAddr; + address _token = isEth ? wethAddr : token; + + TokenInterface tokenContract = TokenInterface(_token); + + _amt = _amt == uint256(-1) + ? getOnBehalfOfPaybackBalance(_token, rateMode, onBehalfOf) + : _amt; + + if (isEth) convertEthToWeth(isEth, tokenContract, _amt); + + approve(tokenContract, address(aave), _amt); + + aave.repay(_token, _amt, rateMode, onBehalfOf); + + setUint(setId, _amt); + + _eventName = "LogPaybackOnBehalfOf(address,uint256,uint256,address,uint256,uint256)"; + _eventParam = abi.encode( + token, + _amt, + rateMode, + onBehalfOf, + getId, + setId + ); + } + + /** + * @dev Enable collateral + * @notice Enable an array of tokens as collateral + * @param tokens Array of tokens to enable collateral + */ + function enableCollateral(address[] calldata tokens) + external + payable + returns (string memory _eventName, bytes memory _eventParam) + { + uint256 _length = tokens.length; + require(_length > 0, "0-tokens-not-allowed"); + + AaveInterface aave = AaveInterface(aaveProvider.getPool()); + + for (uint256 i = 0; i < _length; i++) { + bool isEth = tokens[i] == ethAddr; + address _token = isEth ? wethAddr : tokens[i]; + + if (getCollateralBalance(_token) > 0 && !getIsColl(_token)) { + aave.setUserUseReserveAsCollateral(_token, true); + } + } + + _eventName = "LogEnableCollateral(address[])"; + _eventParam = abi.encode(tokens); + } + + /** + * @dev Swap borrow rate mode + * @notice Swaps user borrow rate mode between variable and stable + * @param token The address of the token to swap borrow rate.(For ETH: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) + * @param rateMode Current rate mode. (Stable = 1, Variable = 2) + */ + function swapBorrowRateMode(address token, uint256 rateMode) + external + payable + returns (string memory _eventName, bytes memory _eventParam) + { + AaveInterface aave = AaveInterface(aaveProvider.getPool()); + + bool isEth = token == ethAddr; + address _token = isEth ? wethAddr : token; + + if (getPaybackBalance(_token, rateMode) > 0) { + aave.swapBorrowRateMode(_token, rateMode); + } + + _eventName = "LogSwapRateMode(address,uint256)"; + _eventParam = abi.encode(token, rateMode); + } + + /** + * @dev Set user e-mode + * @notice Updates the user's e-mode category + * @param categoryId The category Id of the e-mode user want to set + */ + function setUserEMode(uint8 categoryId) + external + payable + returns (string memory _eventName, bytes memory _eventParam) + { + AaveInterface aave = AaveInterface(aaveProvider.getPool()); + + aave.setUserEMode(categoryId); + + _eventName = "LogSetUserEMode(uint8)"; + _eventParam = abi.encode(categoryId); + } + + /** + * @dev Approve Delegation + * @notice Gives approval to delegate debt tokens + * @param token The address of token + * @param amount The amount + * @param rateMode The type of debt + * @param delegateTo The address to whom the user is delegating + * @param getId ID to retrieve amt. + * @param setId ID stores the amount of tokens delegated. + */ + function delegateBorrow( + address token, + uint256 amount, + uint256 rateMode, + address delegateTo, + uint256 getId, + uint256 setId + ) + external + payable + returns (string memory _eventName, bytes memory _eventParam) + { + require(rateMode == 1 || rateMode == 2, "Invalid debt type"); + uint256 _amt = getUint(getId, amount); + + bool isEth = token == ethAddr; + address _token = isEth ? wethAddr : token; + + address _dToken = getDTokenAddr(_token, rateMode); + DTokenInterface(_dToken).approveDelegation(delegateTo, _amt); + + setUint(setId, _amt); + + _eventName = "LogDelegateBorrow(address,uint256,uint256,address,uint256,uint256)"; + _eventParam = abi.encode( + token, + _amt, + rateMode, + delegateTo, + getId, + setId + ); + } +} + +contract ConnectV2AaveV3Base is AaveResolver { + string public constant name = "AaveV3-v1.0"; +}