diff --git a/contracts/arbitrum/connectors/aave/v3/events.sol b/contracts/arbitrum/connectors/aave/v3/events.sol new file mode 100644 index 00000000..9f60f22b --- /dev/null +++ b/contracts/arbitrum/connectors/aave/v3/events.sol @@ -0,0 +1,33 @@ +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); +} diff --git a/contracts/arbitrum/connectors/aave/v3/helpers.sol b/contracts/arbitrum/connectors/aave/v3/helpers.sol new file mode 100644 index 00000000..1fff06e8 --- /dev/null +++ b/contracts/arbitrum/connectors/aave/v3/helpers.sol @@ -0,0 +1,66 @@ +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(0xa97684ead0e402dC232d5A977953DF7ECBaB3CDb); // Arbitrum address - PoolAddressesProvider + + /** + * @dev Aave Pool Data Provider + */ + AaveDataProviderInterface internal constant aaveData = + AaveDataProviderInterface(0x69FA688f1Dc47d4B5d8029D5a35FB7a548310654); //Arbitrum address - PoolDataProvider + + /** + * @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 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) + ); + } +} diff --git a/contracts/arbitrum/connectors/aave/v3/interface.sol b/contracts/arbitrum/connectors/aave/v3/interface.sol new file mode 100644 index 00000000..8ae14716 --- /dev/null +++ b/contracts/arbitrum/connectors/aave/v3/interface.sol @@ -0,0 +1,91 @@ +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); +} diff --git a/contracts/arbitrum/connectors/aave/v3/main.sol b/contracts/arbitrum/connectors/aave/v3/main.sol new file mode 100644 index 00000000..1004d0ec --- /dev/null +++ b/contracts/arbitrum/connectors/aave/v3/main.sol @@ -0,0 +1,296 @@ +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 } from "./interface.sol"; + +abstract contract AaveResolver is Events, Helpers { + /** + * @dev Deposit ETH/ERC20_Token. + * @notice Deposit a token to Aave v2 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 Withdraw ETH/ERC20_Token. + * @notice Withdraw deposited token from Aave v2 + * @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 v2 + * @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 borrow 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 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 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++) { + address token = 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 Desired borrow 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()); + + uint256 currentRateMode = rateMode == 1 ? 2 : 1; + + if (getPaybackBalance(token, currentRateMode) > 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 + returns (string memory _eventName, bytes memory _eventParam) + { + AaveInterface aave = AaveInterface(aaveProvider.getPool()); + + aave.setUserEMode(categoryId); + + _eventName = "LogSetUserEMode(uint8)"; + _eventParam = abi.encode(categoryId); + } +} + +contract ConnectV2AaveV3Arbitrum is AaveResolver { + string public constant name = "AaveV3-v1.0"; +} diff --git a/contracts/avalanche/connectors/aave/v3/events.sol b/contracts/avalanche/connectors/aave/v3/events.sol new file mode 100644 index 00000000..9f60f22b --- /dev/null +++ b/contracts/avalanche/connectors/aave/v3/events.sol @@ -0,0 +1,33 @@ +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); +} diff --git a/contracts/avalanche/connectors/aave/v3/helpers.sol b/contracts/avalanche/connectors/aave/v3/helpers.sol new file mode 100644 index 00000000..6c2f581e --- /dev/null +++ b/contracts/avalanche/connectors/aave/v3/helpers.sol @@ -0,0 +1,66 @@ +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(0xa97684ead0e402dC232d5A977953DF7ECBaB3CDb); // Avalanche address - PoolAddressesProvider + + /** + * @dev Aave Pool Data Provider + */ + AaveDataProviderInterface internal constant aaveData = + AaveDataProviderInterface(0x69FA688f1Dc47d4B5d8029D5a35FB7a548310654); // Avalanche address - PoolDataProvider + + /** + * @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 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) + ); + } +} diff --git a/contracts/avalanche/connectors/aave/v3/interface.sol b/contracts/avalanche/connectors/aave/v3/interface.sol new file mode 100644 index 00000000..8ae14716 --- /dev/null +++ b/contracts/avalanche/connectors/aave/v3/interface.sol @@ -0,0 +1,91 @@ +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); +} diff --git a/contracts/avalanche/connectors/aave/v3/main.sol b/contracts/avalanche/connectors/aave/v3/main.sol new file mode 100644 index 00000000..aa9611fb --- /dev/null +++ b/contracts/avalanche/connectors/aave/v3/main.sol @@ -0,0 +1,296 @@ +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 } from "./interface.sol"; + +abstract contract AaveResolver is Events, Helpers { + /** + * @dev Deposit avax/ERC20_Token. + * @notice Deposit a token to Aave v3 for lending / collaterization. + * @param token The address of the token to deposit.(For avax: 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 isAVAX = token == avaxAddr; + address _token = isAVAX ? wavaxAddr : token; + + TokenInterface tokenContract = TokenInterface(_token); + + if (isAVAX) { + _amt = _amt == uint256(-1) ? address(this).balance : _amt; + convertAvaxToWavax(isAVAX, 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 Withdraw avax/ERC20_Token. + * @notice Withdraw deposited token from Aave v3 + * @param token The address of the token to withdraw.(For avax: 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 isAVAX = token == avaxAddr; + address _token = isAVAX ? wavaxAddr : 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); + + convertWavaxToAvax(isAVAX, tokenContract, _amt); + + setUint(setId, _amt); + + _eventName = "LogWithdraw(address,uint256,uint256,uint256)"; + _eventParam = abi.encode(token, _amt, getId, setId); + } + + /** + * @dev Borrow avax/ERC20_Token. + * @notice Borrow a token using Aave v3 + * @param token The address of the token to borrow.(For avax: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) + * @param amt The amount of the token to borrow. + * @param rateMode The type of borrow 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 isAVAX = token == avaxAddr; + address _token = isAVAX ? wavaxAddr : token; + + aave.borrow(_token, _amt, rateMode, referralCode, address(this)); + convertWavaxToAvax(isAVAX, TokenInterface(_token), _amt); + + setUint(setId, _amt); + + _eventName = "LogBorrow(address,uint256,uint256,uint256,uint256)"; + _eventParam = abi.encode(token, _amt, rateMode, getId, setId); + } + + /** + * @dev Payback borrowed avax/ERC20_Token. + * @notice Payback debt owed. + * @param token The address of the token to payback.(For avax: 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 isAVAX = token == avaxAddr; + address _token = isAVAX ? wavaxAddr : token; + + TokenInterface tokenContract = TokenInterface(_token); + + _amt = _amt == uint256(-1) ? getPaybackBalance(_token, rateMode) : _amt; + + if (isAVAX) convertAvaxToWavax(isAVAX, 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 avax/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 avax: 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 isAVAX = token == avaxAddr; + address _token = isAVAX ? wavaxAddr : token; + + TokenInterface tokenContract = TokenInterface(_token); + + _amt = _amt == uint256(-1) ? getPaybackBalance(_token, rateMode) : _amt; + + if (isAVAX) convertAvaxToWavax(isAVAX, 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 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++) { + address token = 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 avax: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) + * @param rateMode Desired borrow 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()); + + uint256 currentRateMode = rateMode == 1 ? 2 : 1; + + if (getPaybackBalance(token, currentRateMode) > 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 + returns (string memory _eventName, bytes memory _eventParam) + { + AaveInterface aave = AaveInterface(aaveProvider.getPool()); + + aave.setUserEMode(categoryId); + + _eventName = "LogSetUserEMode(uint8)"; + _eventParam = abi.encode(categoryId); + } +} + +contract ConnectV2AaveV3Avalanche is AaveResolver { + string public constant name = "AaveV3-v1.0"; +} diff --git a/contracts/fantom/common/basic.sol b/contracts/fantom/common/basic.sol index 007e3e1c..ba834cfd 100644 --- a/contracts/fantom/common/basic.sol +++ b/contracts/fantom/common/basic.sol @@ -5,50 +5,87 @@ import { Stores } from "./stores.sol"; import { DSMath } from "./math.sol"; abstract contract Basic is DSMath, Stores { + function convert18ToDec(uint256 _dec, uint256 _amt) + internal + pure + returns (uint256 amt) + { + amt = (_amt / 10**(18 - _dec)); + } - function convert18ToDec(uint _dec, uint256 _amt) internal pure returns (uint256 amt) { - amt = (_amt / 10 ** (18 - _dec)); - } + function convertTo18(uint256 _dec, uint256 _amt) + internal + pure + returns (uint256 amt) + { + amt = mul(_amt, 10**(18 - _dec)); + } - function convertTo18(uint _dec, uint256 _amt) internal pure returns (uint256 amt) { - amt = mul(_amt, 10 ** (18 - _dec)); - } + function getTokenBal(TokenInterface token) + internal + view + returns (uint256 _amt) + { + _amt = address(token) == ftmAddr + ? address(this).balance + : token.balanceOf(address(this)); + } - function getTokenBal(TokenInterface token) internal view returns(uint _amt) { - _amt = address(token) == ftmAddr ? address(this).balance : token.balanceOf(address(this)); - } + function getTokensDec(TokenInterface buyAddr, TokenInterface sellAddr) + internal + view + returns (uint256 buyDec, uint256 sellDec) + { + buyDec = address(buyAddr) == ftmAddr ? 18 : buyAddr.decimals(); + sellDec = address(sellAddr) == ftmAddr ? 18 : sellAddr.decimals(); + } - function getTokensDec(TokenInterface buyAddr, TokenInterface sellAddr) internal view returns(uint buyDec, uint sellDec) { - buyDec = address(buyAddr) == ftmAddr ? 18 : buyAddr.decimals(); - sellDec = address(sellAddr) == ftmAddr ? 18 : sellAddr.decimals(); - } + function encodeEvent(string memory eventName, bytes memory eventParam) + internal + pure + returns (bytes memory) + { + return abi.encode(eventName, eventParam); + } - function encodeEvent(string memory eventName, bytes memory eventParam) internal pure returns (bytes memory) { - return abi.encode(eventName, eventParam); - } + function approve( + TokenInterface token, + address spender, + uint256 amount + ) internal { + try token.approve(spender, amount) {} catch { + token.approve(spender, 0); + token.approve(spender, amount); + } + } - function approve(TokenInterface token, address spender, uint256 amount) internal { - try token.approve(spender, amount) { + function changeftmAddress(address buy, address sell) + internal + pure + returns (TokenInterface _buy, TokenInterface _sell) + { + _buy = buy == ftmAddr ? TokenInterface(wftmAddr) : TokenInterface(buy); + _sell = sell == ftmAddr + ? TokenInterface(wftmAddr) + : TokenInterface(sell); + } - } catch { - token.approve(spender, 0); - token.approve(spender, amount); - } - } + function convertFtmToWftm( + bool isFtm, + TokenInterface token, + uint256 amount + ) internal { + if (isFtm) token.deposit{ value: amount }(); + } - function changeftmAddress(address buy, address sell) internal pure returns(TokenInterface _buy, TokenInterface _sell){ - _buy = buy == ftmAddr ? TokenInterface(wftmAddr) : TokenInterface(buy); - _sell = sell == ftmAddr ? TokenInterface(wftmAddr) : TokenInterface(sell); - } - - function convertEthToWeth(bool isEth, TokenInterface token, uint amount) internal { - if(isEth) token.deposit{value: amount}(); - } - - function convertWethToEth(bool isEth, TokenInterface token, uint amount) internal { - if(isEth) { - approve(token, address(token), amount); - token.withdraw(amount); - } - } -} \ No newline at end of file + function convertWftmToFtm( + bool isFtm, + TokenInterface token, + uint256 amount + ) internal { + if (isFtm) { + approve(token, address(token), amount); + token.withdraw(amount); + } + } +} diff --git a/contracts/fantom/common/interfaces.sol b/contracts/fantom/common/interfaces.sol index ea9c362d..ff92c278 100644 --- a/contracts/fantom/common/interfaces.sol +++ b/contracts/fantom/common/interfaces.sol @@ -1,22 +1,35 @@ pragma solidity ^0.7.0; interface TokenInterface { - function approve(address, uint256) external; - function transfer(address, uint) external; - function transferFrom(address, address, uint) external; - function deposit() external payable; - function withdraw(uint) external; - function balanceOf(address) external view returns (uint); - function decimals() external view returns (uint); + function approve(address, uint256) external; + + function transfer(address, uint256) external; + + function transferFrom( + address, + address, + uint256 + ) external; + + function deposit() external payable; + + function withdraw(uint256) external; + + function balanceOf(address) external view returns (uint256); + + function decimals() external view returns (uint256); } interface MemoryInterface { - function getUint(uint id) external returns (uint num); - function setUint(uint id, uint val) external; + function getUint(uint256 id) external returns (uint256 num); + + function setUint(uint256 id, uint256 val) external; } interface AccountInterface { - function enable(address) external; - function disable(address) external; - function isAuth(address) external view returns (bool); + function enable(address) external; + + function disable(address) external; + + function isAuth(address) external view returns (bool); } diff --git a/contracts/fantom/common/math.sol b/contracts/fantom/common/math.sol index f6e2e6cd..81f0bd38 100644 --- a/contracts/fantom/common/math.sol +++ b/contracts/fantom/common/math.sol @@ -3,48 +3,52 @@ pragma solidity ^0.7.0; import { SafeMath } from "@openzeppelin/contracts/math/SafeMath.sol"; contract DSMath { - uint constant WAD = 10 ** 18; - uint constant RAY = 10 ** 27; + uint256 constant WAD = 10**18; + uint256 constant RAY = 10**27; - function add(uint x, uint y) internal pure returns (uint z) { - z = SafeMath.add(x, y); - } + function add(uint256 x, uint256 y) internal pure returns (uint256 z) { + z = SafeMath.add(x, y); + } - function sub(uint x, uint y) internal virtual pure returns (uint z) { - z = SafeMath.sub(x, y); - } + function sub(uint256 x, uint256 y) + internal + pure + virtual + returns (uint256 z) + { + z = SafeMath.sub(x, y); + } - function mul(uint x, uint y) internal pure returns (uint z) { - z = SafeMath.mul(x, y); - } + function mul(uint256 x, uint256 y) internal pure returns (uint256 z) { + z = SafeMath.mul(x, y); + } - function div(uint x, uint y) internal pure returns (uint z) { - z = SafeMath.div(x, y); - } + function div(uint256 x, uint256 y) internal pure returns (uint256 z) { + z = SafeMath.div(x, y); + } - function wmul(uint x, uint y) internal pure returns (uint z) { - z = SafeMath.add(SafeMath.mul(x, y), WAD / 2) / WAD; - } + function wmul(uint256 x, uint256 y) internal pure returns (uint256 z) { + z = SafeMath.add(SafeMath.mul(x, y), WAD / 2) / WAD; + } - function wdiv(uint x, uint y) internal pure returns (uint z) { - z = SafeMath.add(SafeMath.mul(x, WAD), y / 2) / y; - } + function wdiv(uint256 x, uint256 y) internal pure returns (uint256 z) { + z = SafeMath.add(SafeMath.mul(x, WAD), y / 2) / y; + } - function rdiv(uint x, uint y) internal pure returns (uint z) { - z = SafeMath.add(SafeMath.mul(x, RAY), y / 2) / y; - } + function rdiv(uint256 x, uint256 y) internal pure returns (uint256 z) { + z = SafeMath.add(SafeMath.mul(x, RAY), y / 2) / y; + } - function rmul(uint x, uint y) internal pure returns (uint z) { - z = SafeMath.add(SafeMath.mul(x, y), RAY / 2) / RAY; - } + function rmul(uint256 x, uint256 y) internal pure returns (uint256 z) { + z = SafeMath.add(SafeMath.mul(x, y), RAY / 2) / RAY; + } - function toInt(uint x) internal pure returns (int y) { - y = int(x); - require(y >= 0, "int-overflow"); - } - - function toRad(uint wad) internal pure returns (uint rad) { - rad = mul(wad, 10 ** 27); - } + function toInt(uint256 x) internal pure returns (int256 y) { + y = int256(x); + require(y >= 0, "int-overflow"); + } + function toRad(uint256 wad) internal pure returns (uint256 rad) { + rad = mul(wad, 10**27); + } } diff --git a/contracts/fantom/common/stores.sol b/contracts/fantom/common/stores.sol index ab93ef75..0b261c7f 100644 --- a/contracts/fantom/common/stores.sol +++ b/contracts/fantom/common/stores.sol @@ -2,36 +2,39 @@ pragma solidity ^0.7.0; import { MemoryInterface } from "./interfaces.sol"; - abstract contract Stores { + /** + * @dev Return FTM address + */ + address internal constant ftmAddr = + 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE; - /** - * @dev Return FTM address - */ - address constant internal ftmAddr = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE; + /** + * @dev Return Wrapped FTM address + */ + address internal constant wftmAddr = + 0x21be370D5312f44cB42ce377BC9b8a0cEF1A4C83; - /** - * @dev Return Wrapped FTM address - */ - address constant internal wftmAddr = 0x21be370D5312f44cB42ce377BC9b8a0cEF1A4C83; + /** + * @dev Return memory variable address + */ + MemoryInterface internal constant instaMemory = + MemoryInterface(0x56439117379A53bE3CC2C55217251e2481B7a1C8); - /** - * @dev Return memory variable address - */ - MemoryInterface constant internal instaMemory = MemoryInterface(0x56439117379A53bE3CC2C55217251e2481B7a1C8); - - /** - * @dev Get Uint value from InstaMemory Contract. - */ - function getUint(uint getId, uint val) internal returns (uint returnVal) { - returnVal = getId == 0 ? val : instaMemory.getUint(getId); - } - - /** - * @dev Set Uint value in InstaMemory Contract. - */ - function setUint(uint setId, uint val) virtual internal { - if (setId != 0) instaMemory.setUint(setId, val); - } + /** + * @dev Get Uint value from InstaMemory Contract. + */ + function getUint(uint256 getId, uint256 val) + internal + returns (uint256 returnVal) + { + returnVal = getId == 0 ? val : instaMemory.getUint(getId); + } + /** + * @dev Set Uint value in InstaMemory Contract. + */ + function setUint(uint256 setId, uint256 val) internal virtual { + if (setId != 0) instaMemory.setUint(setId, val); + } } diff --git a/contracts/fantom/connectors/aave/v3/events.sol b/contracts/fantom/connectors/aave/v3/events.sol new file mode 100644 index 00000000..9f60f22b --- /dev/null +++ b/contracts/fantom/connectors/aave/v3/events.sol @@ -0,0 +1,33 @@ +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); +} diff --git a/contracts/fantom/connectors/aave/v3/helpers.sol b/contracts/fantom/connectors/aave/v3/helpers.sol new file mode 100644 index 00000000..8b970670 --- /dev/null +++ b/contracts/fantom/connectors/aave/v3/helpers.sol @@ -0,0 +1,66 @@ +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(0xa97684ead0e402dC232d5A977953DF7ECBaB3CDb); // fantom address - PoolAddressesProvider + + /** + * @dev Aave Pool Data Provider + */ + AaveDataProviderInterface internal constant aaveData = + AaveDataProviderInterface(0x69FA688f1Dc47d4B5d8029D5a35FB7a548310654); // fantom address - PoolDataProvider + + /** + * @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 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) + ); + } +} diff --git a/contracts/fantom/connectors/aave/v3/interface.sol b/contracts/fantom/connectors/aave/v3/interface.sol new file mode 100644 index 00000000..8ae14716 --- /dev/null +++ b/contracts/fantom/connectors/aave/v3/interface.sol @@ -0,0 +1,91 @@ +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); +} diff --git a/contracts/fantom/connectors/aave/v3/main.sol b/contracts/fantom/connectors/aave/v3/main.sol new file mode 100644 index 00000000..66506cff --- /dev/null +++ b/contracts/fantom/connectors/aave/v3/main.sol @@ -0,0 +1,296 @@ +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 } from "./interface.sol"; + +abstract contract AaveResolver is Events, Helpers { + /** + * @dev Deposit Ftm/ERC20_Token. + * @notice Deposit a token to Aave v3 for lending / collaterization. + * @param token The address of the token to deposit.(For ftm: 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 isFTM = token == ftmAddr; + address _token = isFTM ? wftmAddr : token; + + TokenInterface tokenContract = TokenInterface(_token); + + if (isFTM) { + _amt = _amt == uint256(-1) ? address(this).balance : _amt; + convertFtmToWftm(isFTM, 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 Withdraw ftm/ERC20_Token. + * @notice Withdraw deposited token from Aave v3 + * @param token The address of the token to withdraw.(For ftm: 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 isFTM = token == ftmAddr; + address _token = isFTM ? wftmAddr : 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); + + convertWftmToFtm(isFTM, tokenContract, _amt); + + setUint(setId, _amt); + + _eventName = "LogWithdraw(address,uint256,uint256,uint256)"; + _eventParam = abi.encode(token, _amt, getId, setId); + } + + /** + * @dev Borrow ftm/ERC20_Token. + * @notice Borrow a token using Aave v3 + * @param token The address of the token to borrow.(For ftm: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) + * @param amt The amount of the token to borrow. + * @param rateMode The type of borrow 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 isFTM = token == ftmAddr; + address _token = isFTM ? wftmAddr : token; + + aave.borrow(_token, _amt, rateMode, referralCode, address(this)); + convertWftmToFtm(isFTM, TokenInterface(_token), _amt); + + setUint(setId, _amt); + + _eventName = "LogBorrow(address,uint256,uint256,uint256,uint256)"; + _eventParam = abi.encode(token, _amt, rateMode, getId, setId); + } + + /** + * @dev Payback borrowed ftm/ERC20_Token. + * @notice Payback debt owed. + * @param token The address of the token to payback.(For ftm: 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 isFTM = token == ftmAddr; + address _token = isFTM ? wftmAddr : token; + + TokenInterface tokenContract = TokenInterface(_token); + + _amt = _amt == uint256(-1) ? getPaybackBalance(_token, rateMode) : _amt; + + if (isFTM) convertFtmToWftm(isFTM, 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 ftm/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 ftm: 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 isFTM = token == ftmAddr; + address _token = isFTM ? wftmAddr : token; + + TokenInterface tokenContract = TokenInterface(_token); + + _amt = _amt == uint256(-1) ? getPaybackBalance(_token, rateMode) : _amt; + + if (isFTM) convertFtmToWftm(isFTM, 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 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++) { + address token = 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 ftm: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) + * @param rateMode Desired borrow 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()); + + uint256 currentRateMode = rateMode == 1 ? 2 : 1; + + if (getPaybackBalance(token, currentRateMode) > 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 + returns (string memory _eventName, bytes memory _eventParam) + { + AaveInterface aave = AaveInterface(aaveProvider.getPool()); + + aave.setUserEMode(categoryId); + + _eventName = "LogSetUserEMode(uint8)"; + _eventParam = abi.encode(categoryId); + } +} + +contract ConnectV2AaveV3Fantom is AaveResolver { + string public constant name = "AaveV3-v1"; +} diff --git a/contracts/mainnet/connectors/aave/v3/events.sol b/contracts/mainnet/connectors/aave/v3/events.sol new file mode 100644 index 00000000..9f60f22b --- /dev/null +++ b/contracts/mainnet/connectors/aave/v3/events.sol @@ -0,0 +1,33 @@ +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); +} diff --git a/contracts/mainnet/connectors/aave/v3/helpers.sol b/contracts/mainnet/connectors/aave/v3/helpers.sol new file mode 100644 index 00000000..13431d11 --- /dev/null +++ b/contracts/mainnet/connectors/aave/v3/helpers.sol @@ -0,0 +1,66 @@ +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(0xA55125A90d75a95EC00130E8E8C197dB5641Eb19); // rinkeby address + + /** + * @dev Aave Pool Data Provider + */ + AaveDataProviderInterface internal constant aaveData = + AaveDataProviderInterface(0x256bBbeDbA70a1240a1EB64210abB1b063267408); // rinkeby address + + /** + * @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 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) + ); + } +} diff --git a/contracts/mainnet/connectors/aave/v3/interface.sol b/contracts/mainnet/connectors/aave/v3/interface.sol new file mode 100644 index 00000000..8ae14716 --- /dev/null +++ b/contracts/mainnet/connectors/aave/v3/interface.sol @@ -0,0 +1,91 @@ +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); +} diff --git a/contracts/mainnet/connectors/aave/v3/main.sol b/contracts/mainnet/connectors/aave/v3/main.sol new file mode 100644 index 00000000..1c7b0a8b --- /dev/null +++ b/contracts/mainnet/connectors/aave/v3/main.sol @@ -0,0 +1,296 @@ +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 } 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 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 borrow 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 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 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++) { + address token = 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 Desired borrow 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()); + + uint256 currentRateMode = rateMode == 1 ? 2 : 1; + + if (getPaybackBalance(token, currentRateMode) > 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 + returns (string memory _eventName, bytes memory _eventParam) + { + AaveInterface aave = AaveInterface(aaveProvider.getPool()); + + aave.setUserEMode(categoryId); + + _eventName = "LogSetUserEMode(uint8)"; + _eventParam = abi.encode(categoryId); + } +} + +contract ConnectV2AaveV3 is AaveResolver { + string public constant name = "AaveV3-v1.0"; +} diff --git a/contracts/optimism/connectors/aave/v3/events.sol b/contracts/optimism/connectors/aave/v3/events.sol new file mode 100644 index 00000000..9f60f22b --- /dev/null +++ b/contracts/optimism/connectors/aave/v3/events.sol @@ -0,0 +1,33 @@ +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); +} diff --git a/contracts/optimism/connectors/aave/v3/helpers.sol b/contracts/optimism/connectors/aave/v3/helpers.sol new file mode 100644 index 00000000..f0fd9188 --- /dev/null +++ b/contracts/optimism/connectors/aave/v3/helpers.sol @@ -0,0 +1,66 @@ +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(0xa97684ead0e402dC232d5A977953DF7ECBaB3CDb); // Optimism address - PoolAddressesProvider + + /** + * @dev Aave Pool Data Provider + */ + AaveDataProviderInterface internal constant aaveData = + AaveDataProviderInterface(0x69FA688f1Dc47d4B5d8029D5a35FB7a548310654); // Optimism address - PoolDataProvider + + /** + * @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 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) + ); + } +} diff --git a/contracts/optimism/connectors/aave/v3/interface.sol b/contracts/optimism/connectors/aave/v3/interface.sol new file mode 100644 index 00000000..8ae14716 --- /dev/null +++ b/contracts/optimism/connectors/aave/v3/interface.sol @@ -0,0 +1,91 @@ +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); +} diff --git a/contracts/optimism/connectors/aave/v3/main.sol b/contracts/optimism/connectors/aave/v3/main.sol new file mode 100644 index 00000000..62be7a41 --- /dev/null +++ b/contracts/optimism/connectors/aave/v3/main.sol @@ -0,0 +1,296 @@ +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 } 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 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 borrow 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 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 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++) { + address token = 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 Desired borrow 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()); + + uint256 currentRateMode = rateMode == 1 ? 2 : 1; + + if (getPaybackBalance(token, currentRateMode) > 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 + returns (string memory _eventName, bytes memory _eventParam) + { + AaveInterface aave = AaveInterface(aaveProvider.getPool()); + + aave.setUserEMode(categoryId); + + _eventName = "LogSetUserEMode(uint8)"; + _eventParam = abi.encode(categoryId); + } +} + +contract ConnectV2AaveV3Optimism is AaveResolver { + string public constant name = "AaveV3-v1.0"; +} diff --git a/contracts/polygon/connectors/aave/v3/events.sol b/contracts/polygon/connectors/aave/v3/events.sol new file mode 100644 index 00000000..9f60f22b --- /dev/null +++ b/contracts/polygon/connectors/aave/v3/events.sol @@ -0,0 +1,33 @@ +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); +} diff --git a/contracts/polygon/connectors/aave/v3/helpers.sol b/contracts/polygon/connectors/aave/v3/helpers.sol new file mode 100644 index 00000000..28447ee0 --- /dev/null +++ b/contracts/polygon/connectors/aave/v3/helpers.sol @@ -0,0 +1,66 @@ +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(0xa97684ead0e402dC232d5A977953DF7ECBaB3CDb); // Polygon address - PoolAddressesProvider + + /** + * @dev Aave Pool Data Provider + */ + AaveDataProviderInterface internal constant aaveData = + AaveDataProviderInterface(0x69FA688f1Dc47d4B5d8029D5a35FB7a548310654); // Polygon address - PoolDataProvider + + /** + * @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 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) + ); + } +} diff --git a/contracts/polygon/connectors/aave/v3/interface.sol b/contracts/polygon/connectors/aave/v3/interface.sol new file mode 100644 index 00000000..8ae14716 --- /dev/null +++ b/contracts/polygon/connectors/aave/v3/interface.sol @@ -0,0 +1,91 @@ +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); +} diff --git a/contracts/polygon/connectors/aave/v3/main.sol b/contracts/polygon/connectors/aave/v3/main.sol new file mode 100644 index 00000000..c7782667 --- /dev/null +++ b/contracts/polygon/connectors/aave/v3/main.sol @@ -0,0 +1,296 @@ +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 } from "./interface.sol"; + +abstract contract AaveResolver is Events, Helpers { + /** + * @dev Deposit Matic/ERC20_Token. + * @notice Deposit a token to Aave v3 for lending / collaterization. + * @param token The address of the token to deposit.(For Matic: 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 isMatic = token == maticAddr; + address _token = isMatic ? wmaticAddr : token; + + TokenInterface tokenContract = TokenInterface(_token); + + if (isMatic) { + _amt = _amt == uint256(-1) ? address(this).balance : _amt; + convertMaticToWmatic(isMatic, 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 Withdraw matic/ERC20_Token. + * @notice Withdraw deposited token from Aave v3 + * @param token The address of the token to withdraw.(For matic: 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 isMatic = token == maticAddr; + address _token = isMatic ? wmaticAddr : 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); + + convertWmaticToMatic(isMatic, tokenContract, _amt); + + setUint(setId, _amt); + + _eventName = "LogWithdraw(address,uint256,uint256,uint256)"; + _eventParam = abi.encode(token, _amt, getId, setId); + } + + /** + * @dev Borrow matic/ERC20_Token. + * @notice Borrow a token using Aave v3 + * @param token The address of the token to borrow.(For matic: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) + * @param amt The amount of the token to borrow. + * @param rateMode The type of borrow 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 isMatic = token == maticAddr; + address _token = isMatic ? wmaticAddr : token; + + aave.borrow(_token, _amt, rateMode, referralCode, address(this)); + convertWmaticToMatic(isMatic, TokenInterface(_token), _amt); + + setUint(setId, _amt); + + _eventName = "LogBorrow(address,uint256,uint256,uint256,uint256)"; + _eventParam = abi.encode(token, _amt, rateMode, getId, setId); + } + + /** + * @dev Payback borrowed matic/ERC20_Token. + * @notice Payback debt owed. + * @param token The address of the token to payback.(For matic: 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 isMatic = token == maticAddr; + address _token = isMatic ? wmaticAddr : token; + + TokenInterface tokenContract = TokenInterface(_token); + + _amt = _amt == uint256(-1) ? getPaybackBalance(_token, rateMode) : _amt; + + if (isMatic) convertMaticToWmatic(isMatic, 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 matic/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 matic: 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 isMatic = token == maticAddr; + address _token = isMatic ? wmaticAddr : token; + + TokenInterface tokenContract = TokenInterface(_token); + + _amt = _amt == uint256(-1) ? getPaybackBalance(_token, rateMode) : _amt; + + if (isMatic) convertMaticToWmatic(isMatic, 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 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++) { + address token = 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 matic: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) + * @param rateMode Desired borrow 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()); + + uint256 currentRateMode = rateMode == 1 ? 2 : 1; + + if (getPaybackBalance(token, currentRateMode) > 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 + returns (string memory _eventName, bytes memory _eventParam) + { + AaveInterface aave = AaveInterface(aaveProvider.getPool()); + + aave.setUserEMode(categoryId); + + _eventName = "LogSetUserEMode(uint8)"; + _eventParam = abi.encode(categoryId); + } +} + +contract ConnectV2AaveV3Polygon is AaveResolver { + string public constant name = "AaveV3-v1.0"; +} diff --git a/hardhat.config.ts b/hardhat.config.ts index 88862543..5edfbdf3 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -25,7 +25,7 @@ const chainIds = { polygon: 137, arbitrum: 42161, optimism: 10, - fantom:250 + fantom: 250 }; const alchemyApiKey = process.env.ALCHEMY_API_KEY; @@ -40,36 +40,29 @@ 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 mnemonic = - process.env.MNEMONIC ?? - "test test test test test test test test test test test junk"; +const mnemonic = process.env.MNEMONIC ?? "test test test test test test test test test test test junk"; const networkGasPriceConfig: Record = { - "mainnet": "160", - "polygon": "50", - "avalanche": "50", - "arbitrum": "2" -} + mainnet: "160", + polygon: "50", + avalanche: "50", + arbitrum: "2" +}; function createConfig(network: string) { return { url: getNetworkUrl(network), - accounts: !!PRIVATE_KEY ? [`0x${PRIVATE_KEY}`] : { mnemonic }, - gasPrice: 1000000, // 0.0001 GWEI + accounts: !!PRIVATE_KEY ? [`0x${PRIVATE_KEY}`] : { mnemonic } + // gasPrice: 1000000, // 0.0001 GWEI }; } function getNetworkUrl(networkType: string) { - if (networkType === "avalanche") - return "https://api.avax.network/ext/bc/C/rpc"; - else if (networkType === "polygon") - return `https://polygon-mainnet.g.alchemy.com/v2/${alchemyApiKey}`; - else if (networkType === "arbitrum") - return `https://arb-mainnet.g.alchemy.com/v2/${alchemyApiKey}`; - else if (networkType === "optimism") - return `https://opt-mainnet.g.alchemy.com/v2/${alchemyApiKey}`; - else if(networkType === "fantom") - return `https://rpc.ftm.tools/` + if (networkType === "avalanche") return "https://api.avax.network/ext/bc/C/rpc"; + else if (networkType === "polygon") return `https://polygon-mainnet.g.alchemy.com/v2/${alchemyApiKey}`; + else if (networkType === "arbitrum") return `https://arb-mainnet.g.alchemy.com/v2/${alchemyApiKey}`; + else if (networkType === "optimism") return `https://opt-mainnet.g.alchemy.com/v2/${alchemyApiKey}`; + else if (networkType === "fantom") return `https://rpc.ftm.tools/`; else return `https://eth-mainnet.alchemyapi.io/v2/${alchemyApiKey}`; } @@ -77,7 +70,7 @@ function getScanApiKey(networkType: string) { if (networkType === "avalanche") return SNOWTRACE_API; 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; } @@ -93,54 +86,54 @@ const config: HardhatUserConfig = { settings: { optimizer: { enabled: true, - runs: 200, - }, - }, + runs: 200 + } + } }, { - version: "0.6.0", + version: "0.6.0" }, { - version: "0.6.2", + version: "0.6.2" }, { - version: "0.6.5", - }, - ], + version: "0.6.5" + } + ] }, networks: { hardhat: { accounts: { - mnemonic, + mnemonic }, chainId: chainIds.hardhat, forking: { - url: String(getNetworkUrl(String(process.env.networkType))), - }, + url: String(getNetworkUrl(String(process.env.networkType))) + } }, mainnet: createConfig("mainnet"), polygon: createConfig("polygon"), avalanche: createConfig("avalanche"), arbitrum: createConfig("arbitrum"), optimism: createConfig("optimism"), - fantom: createConfig("fantom"), + fantom: createConfig("fantom") }, paths: { artifacts: "./artifacts", cache: "./cache", sources: "./contracts", - tests: "./test", + tests: "./test" }, etherscan: { - apiKey: getScanApiKey(String(process.env.networkType)), + apiKey: getScanApiKey(String(process.env.networkType)) }, typechain: { outDir: "typechain", - target: "ethers-v5", + target: "ethers-v5" }, mocha: { - timeout: 10000 * 1000, // 10,000 seconds - }, + timeout: 10000 * 1000 // 10,000 seconds + } // tenderly: { // project: process.env.TENDERLY_PROJECT, // username: process.env.TENDERLY_USERNAME,