From 899b78c8dded6e28f2b7102c5e8e0013e3b16c6f Mon Sep 17 00:00:00 2001 From: Islarky Overlord Date: Wed, 16 Jun 2021 06:40:47 +0800 Subject: [PATCH] added aave_v2 and compound to be standardized --- contracts/protocols/mainnet/common/math.sol | 34 +++ .../mainnet/resolvers/aave/v2/helpers.sol | 247 ++++++++++++++++++ .../mainnet/resolvers/aave/v2/interface.sol | 98 +++++++ .../mainnet/resolvers/aave/v2/main.sol | 36 +++ .../mainnet/resolvers/compound/helpers.sol | 61 +++++ .../mainnet/resolvers/compound/interface.sol | 50 ++++ .../mainnet/resolvers/compound/main.sol | 90 +++++++ 7 files changed, 616 insertions(+) create mode 100644 contracts/protocols/mainnet/common/math.sol create mode 100644 contracts/protocols/mainnet/resolvers/aave/v2/helpers.sol create mode 100644 contracts/protocols/mainnet/resolvers/aave/v2/interface.sol create mode 100644 contracts/protocols/mainnet/resolvers/aave/v2/main.sol create mode 100644 contracts/protocols/mainnet/resolvers/compound/helpers.sol create mode 100644 contracts/protocols/mainnet/resolvers/compound/interface.sol create mode 100644 contracts/protocols/mainnet/resolvers/compound/main.sol diff --git a/contracts/protocols/mainnet/common/math.sol b/contracts/protocols/mainnet/common/math.sol new file mode 100644 index 0000000..2a61361 --- /dev/null +++ b/contracts/protocols/mainnet/common/math.sol @@ -0,0 +1,34 @@ +pragma solidity ^0.6.0; + +contract DSMath { + uint constant WAD = 10 ** 18; + uint constant RAY = 10 ** 27; + + function add(uint256 x, uint256 y) internal pure returns (uint256 z) { + require((z = x + y) >= x, "math-not-safe"); + } + + function mul(uint256 x, uint256 y) internal pure returns (uint256 z) { + require(y == 0 || (z = x * y) / y == x, "math-not-safe"); + } + + function rmul(uint x, uint y) internal pure returns (uint z) { + z = add(mul(x, y), RAY / 2) / RAY; + } + + function wmul(uint256 x, uint256 y) internal pure returns (uint256 z) { + z = add(mul(x, y), WAD / 2) / WAD; + } + + function rdiv(uint x, uint y) internal pure returns (uint z) { + z = add(mul(x, RAY), y / 2) / y; + } + + function wdiv(uint256 x, uint256 y) internal pure returns (uint256 z) { + z = add(mul(x, WAD), y / 2) / y; + } + + function sub(uint256 x, uint256 y) internal pure returns (uint256 z) { + require((z = x - y) <= x, "ds-math-sub-underflow"); + } +} diff --git a/contracts/protocols/mainnet/resolvers/aave/v2/helpers.sol b/contracts/protocols/mainnet/resolvers/aave/v2/helpers.sol new file mode 100644 index 0000000..7f1984e --- /dev/null +++ b/contracts/protocols/mainnet/resolvers/aave/v2/helpers.sol @@ -0,0 +1,247 @@ +pragma solidity ^0.6.0; + +import {DSMath} from "../../../common/math.sol"; +import { + TokenInterface, + AaveProtocolDataProvider, + AaveAddressProvider, + AaveLendingPool, + AavePriceOracle, + ChainLinkInterface, + AaveIncentivesInterface +} from "./interface.sol"; + +abstract contract Helpers is DSMath { + /** + * @dev Return ethereum address + */ + function getEthAddr() internal pure returns (address) { + return 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE; // ETH Address + } + + /** + * @dev Return Weth address + */ + function getWethAddr() internal pure returns (address) { + return 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2; // Mainnet WETH Address + // return 0xd0A1E359811322d97991E03f863a0C30C2cF029C; // Kovan WETH Address + } + + /** + * @dev get Aave Provider Address + */ + function getAaveAddressProvider() internal pure returns (address) { + return 0xB53C1a33016B2DC2fF3653530bfF1848a515c8c5; // Mainnet + // return 0x652B2937Efd0B5beA1c8d54293FC1289672AFC6b; // Kovan + } + + /** + * @dev get Aave Protocol Data Provider + */ + function getAaveProtocolDataProvider() internal pure returns (address) { + return 0x057835Ad21a177dbdd3090bB1CAE03EaCF78Fc6d; // Mainnet + // return 0x744C1aaA95232EeF8A9994C4E0b3a89659D9AB79; // Kovan + } + + /** + * @dev get Chainlink ETH price feed Address + */ + function getChainlinkEthFeed() internal pure returns (address) { + return 0x5f4eC3Df9cbd43714FE2740f5E3616155c5b8419; //mainnet + // return 0x9326BFA02ADD2366b30bacB125260Af641031331; //kovan + } + + /** + * @dev Aave Incentives address + */ + function getAaveIncentivesAddress() internal pure returns (address) { + return 0xd784927Ff2f95ba542BfC824c8a8a98F3495f6b5; // polygon mainnet + } + + struct AaveUserTokenData { + uint256 tokenPriceInEth; + uint256 tokenPriceInUsd; + uint256 supplyBalance; + uint256 stableBorrowBalance; + uint256 variableBorrowBalance; + uint256 supplyRate; + uint256 stableBorrowRate; + uint256 userStableBorrowRate; + uint256 variableBorrowRate; + bool isCollateral; + AaveTokenData aaveTokenData; + } + + struct AaveUserData { + uint256 totalCollateralETH; + uint256 totalBorrowsETH; + uint256 availableBorrowsETH; + uint256 currentLiquidationThreshold; + uint256 ltv; + uint256 healthFactor; + uint256 ethPriceInUsd; + uint256 pendingRewards; + } + + struct AaveTokenData { + uint256 ltv; + uint256 threshold; + uint256 reserveFactor; + bool usageAsCollEnabled; + bool borrowEnabled; + bool stableBorrowEnabled; + bool isActive; + bool isFrozen; + uint256 totalSupply; + uint256 availableLiquidity; + uint256 totalStableDebt; + uint256 totalVariableDebt; + uint256 collateralEmission; + uint256 debtEmission; + } + + struct TokenPrice { + uint256 priceInEth; + uint256 priceInUsd; + } + + function getTokensPrices( + AaveAddressProvider aaveAddressProvider, + address[] memory tokens + ) + internal + view + returns (TokenPrice[] memory tokenPrices, uint256 ethPrice) + { + uint256[] memory _tokenPrices = + AavePriceOracle(aaveAddressProvider.getPriceOracle()) + .getAssetsPrices(tokens); + ethPrice = uint256( + ChainLinkInterface(getChainlinkEthFeed()).latestAnswer() + ); + tokenPrices = new TokenPrice[](_tokenPrices.length); + for (uint256 i = 0; i < _tokenPrices.length; i++) { + tokenPrices[i] = TokenPrice( + _tokenPrices[i], + wmul(_tokenPrices[i], uint256(ethPrice) * 10**10) + ); + } + } + + function collateralData(AaveProtocolDataProvider aaveData, address token) + internal + view + returns (AaveTokenData memory aaveTokenData) + { + ( + , + aaveTokenData.ltv, + aaveTokenData.threshold, + , + aaveTokenData.reserveFactor, + aaveTokenData.usageAsCollEnabled, + aaveTokenData.borrowEnabled, + aaveTokenData.stableBorrowEnabled, + aaveTokenData.isActive, + aaveTokenData.isFrozen + ) = aaveData.getReserveConfigurationData(token); + + (address aToken, , address debtToken) = + aaveData.getReserveTokensAddresses(token); + + AaveIncentivesInterface.AssetData memory _data; + AaveIncentivesInterface incentives = + AaveIncentivesInterface(getAaveIncentivesAddress()); + + _data = incentives.assets(aToken); + aaveTokenData.collateralEmission = _data.emissionPerSecond; + _data = incentives.assets(debtToken); + aaveTokenData.debtEmission = _data.emissionPerSecond; + aaveTokenData.totalSupply = TokenInterface(aToken).totalSupply(); + } + + function getTokenData( + AaveProtocolDataProvider aaveData, + address user, + address token, + uint256 tokenPriceInEth, + uint256 tokenPriceInUsd + ) internal view returns (AaveUserTokenData memory tokenData) { + AaveTokenData memory aaveTokenData = collateralData(aaveData, token); + + ( + tokenData.supplyBalance, + tokenData.stableBorrowBalance, + tokenData.variableBorrowBalance, + , + , + tokenData.userStableBorrowRate, + , + , + tokenData.isCollateral + ) = aaveData.getUserReserveData(token, user); + + ( + aaveTokenData.availableLiquidity, + aaveTokenData.totalStableDebt, + aaveTokenData.totalVariableDebt, + tokenData.supplyRate, + tokenData.variableBorrowRate, + tokenData.stableBorrowRate, + , + , + , + + ) = aaveData.getReserveData(token); + + tokenData.tokenPriceInEth = tokenPriceInEth; + tokenData.tokenPriceInUsd = tokenPriceInUsd; + tokenData.aaveTokenData = aaveTokenData; + } + + function getPendingRewards(address[] memory _tokens, address user) + internal + view + returns (uint256 rewards) + { + uint256 arrLength = 2 * _tokens.length; + address[] memory _atokens = new address[](arrLength); + AaveProtocolDataProvider aaveData = + AaveProtocolDataProvider(getAaveProtocolDataProvider()); + for (uint256 i = 0; i < _tokens.length; i++) { + (_atokens[2 * i], , _atokens[2 * i + 1]) = aaveData + .getReserveTokensAddresses(_tokens[i]); + } + rewards = AaveIncentivesInterface(getAaveIncentivesAddress()) + .getRewardsBalance(_atokens, user); + } + + function getUserData( + AaveLendingPool aave, + address user, + uint256 ethPriceInUsd, + address[] memory tokens + ) internal view returns (AaveUserData memory userData) { + ( + uint256 totalCollateralETH, + uint256 totalDebtETH, + uint256 availableBorrowsETH, + uint256 currentLiquidationThreshold, + uint256 ltv, + uint256 healthFactor + ) = aave.getUserAccountData(user); + + uint256 pendingRewards = getPendingRewards(tokens, user); + + userData = AaveUserData( + totalCollateralETH, + totalDebtETH, + availableBorrowsETH, + currentLiquidationThreshold, + ltv, + healthFactor, + ethPriceInUsd, + pendingRewards + ); + } +} diff --git a/contracts/protocols/mainnet/resolvers/aave/v2/interface.sol b/contracts/protocols/mainnet/resolvers/aave/v2/interface.sol new file mode 100644 index 0000000..1335adf --- /dev/null +++ b/contracts/protocols/mainnet/resolvers/aave/v2/interface.sol @@ -0,0 +1,98 @@ +pragma solidity ^0.6.0; +pragma experimental ABIEncoderV2; + + +interface AaveProtocolDataProvider { + 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 getReserveConfigurationData(address asset) external view returns ( + uint256 decimals, + uint256 ltv, + uint256 liquidationThreshold, + uint256 liquidationBonus, + uint256 reserveFactor, + bool usageAsCollateralEnabled, + bool borrowingEnabled, + bool stableBorrowRateEnabled, + bool isActive, + bool isFrozen + ); + + function getReserveData(address asset) external view returns ( + uint256 availableLiquidity, + uint256 totalStableDebt, + uint256 totalVariableDebt, + uint256 liquidityRate, + uint256 variableBorrowRate, + uint256 stableBorrowRate, + uint256 averageStableBorrowRate, + uint256 liquidityIndex, + uint256 variableBorrowIndex, + uint40 lastUpdateTimestamp + ); + + function getReserveTokensAddresses(address asset) external view returns ( + address aTokenAddress, + address stableDebtTokenAddress, + address variableDebtTokenAddress + ); +} + +interface AaveLendingPool { + function getUserAccountData(address user) external view returns ( + uint256 totalCollateralETH, + uint256 totalDebtETH, + uint256 availableBorrowsETH, + uint256 currentLiquidationThreshold, + uint256 ltv, + uint256 healthFactor + ); +} + +interface TokenInterface { + function totalSupply() external view returns (uint); +} + +interface AaveAddressProvider { + function getLendingPool() external view returns (address); + function getPriceOracle() external view returns (address); +} + +interface AavePriceOracle { + function getAssetPrice(address _asset) external view returns(uint256); + function getAssetsPrices(address[] calldata _assets) external view returns(uint256[] memory); + function getSourceOfAsset(address _asset) external view returns(uint256); + function getFallbackOracle() external view returns(uint256); +} + +interface AaveIncentivesInterface { + struct AssetData { + uint128 emissionPerSecond; + uint128 lastUpdateTimestamp; + uint256 index; + } + + function getRewardsBalance( + address[] calldata assets, + address user + ) external view returns (uint256); + + function assets( + address asset + ) external view returns (AssetData memory); +} + +interface ChainLinkInterface { + function latestAnswer() external view returns (int256); + function decimals() external view returns (uint256); +} \ No newline at end of file diff --git a/contracts/protocols/mainnet/resolvers/aave/v2/main.sol b/contracts/protocols/mainnet/resolvers/aave/v2/main.sol new file mode 100644 index 0000000..ef3e425 --- /dev/null +++ b/contracts/protocols/mainnet/resolvers/aave/v2/main.sol @@ -0,0 +1,36 @@ +pragma solidity ^0.6.0; +pragma experimental ABIEncoderV2; + +import { Helpers } from "./helpers.sol"; +import { AaveAddressProvider, AaveProtocolDataProvider, AaveLendingPool } from "./interface.sol"; + +abstract contract Resolver is Helpers { + function getPosition(address user, address[] memory tokens) public view returns(AaveUserTokenData[] memory, AaveUserData memory) { + AaveAddressProvider addrProvider = AaveAddressProvider(getAaveAddressProvider()); + uint length = tokens.length; + address[] memory _tokens = new address[](length); + + for (uint i = 0; i < length; i++) { + _tokens[i] = tokens[i] == getEthAddr() ? getWethAddr() : tokens[i]; + } + + AaveUserTokenData[] memory tokensData = new AaveUserTokenData[](length); + (TokenPrice[] memory tokenPrices, uint ethPrice) = getTokensPrices(addrProvider, _tokens); + + for (uint i = 0; i < length; i++) { + tokensData[i] = getTokenData( + AaveProtocolDataProvider(getAaveProtocolDataProvider()), + user, + _tokens[i], + tokenPrices[i].priceInEth, + tokenPrices[i].priceInUsd + ); + } + + return (tokensData, getUserData(AaveLendingPool(addrProvider.getLendingPool()), user, ethPrice, _tokens)); + } +} + +contract InstaAaveV2Resolver is Resolver { + string public constant name = "AaveV2-Resolver-v1.6"; +} diff --git a/contracts/protocols/mainnet/resolvers/compound/helpers.sol b/contracts/protocols/mainnet/resolvers/compound/helpers.sol new file mode 100644 index 0000000..a7f77f1 --- /dev/null +++ b/contracts/protocols/mainnet/resolvers/compound/helpers.sol @@ -0,0 +1,61 @@ +pragma solidity ^0.6.0; + +import {DSMath} from "../../common/math.sol"; +import {ComptrollerLensInterface, TokenInterface} from "./interface.sol"; + +abstract contract Helpers is DSMath { + /** + * @dev get Compound Comptroller + */ + function getComptroller() public pure returns (ComptrollerLensInterface) { + return + ComptrollerLensInterface( + 0x3d9819210A31b4961b30EF54bE2aeD79B9c9Cd3B + ); + } + + /** + * @dev get Compound Open Feed Oracle Address + */ + function getOracleAddress() public view returns (address) { + return getComptroller().oracle(); + } + + /** + * @dev get Comp Read Address + */ + function getCompReadAddress() public pure returns (address) { + return 0xd513d22422a3062Bd342Ae374b4b9c20E0a9a074; + } + + /** + * @dev get ETH Address + */ + function getCETHAddress() public pure returns (address) { + return 0x4Ddc2D193948926D02f9B1fE9e1daa0718270ED5; + } + + /** + * @dev get Comp Token Address + */ + function getCompToken() public pure returns (TokenInterface) { + return TokenInterface(0xc00e94Cb662C3520282E6f5717214004A7f26888); + } + + struct CompData { + uint256 tokenPriceInEth; + uint256 tokenPriceInUsd; + uint256 exchangeRateStored; + uint256 balanceOfUser; + uint256 borrowBalanceStoredUser; + uint256 totalBorrows; + uint256 totalSupplied; + uint256 borrowCap; + uint256 supplyRatePerBlock; + uint256 borrowRatePerBlock; + uint256 collateralFactor; + uint256 compSpeed; + bool isComped; + bool isBorrowPaused; + } +} diff --git a/contracts/protocols/mainnet/resolvers/compound/interface.sol b/contracts/protocols/mainnet/resolvers/compound/interface.sol new file mode 100644 index 0000000..61e46ac --- /dev/null +++ b/contracts/protocols/mainnet/resolvers/compound/interface.sol @@ -0,0 +1,50 @@ +pragma solidity ^0.6.0; +pragma experimental ABIEncoderV2; + +interface CTokenInterface { + function exchangeRateStored() external view returns (uint); + function borrowRatePerBlock() external view returns (uint); + function supplyRatePerBlock() external view returns (uint); + function borrowBalanceStored(address) external view returns (uint); + function totalBorrows() external view returns (uint); + + function underlying() external view returns (address); + function balanceOf(address) external view returns (uint); + function getCash() external view returns (uint); +} + +interface TokenInterface { + function decimals() external view returns (uint); + function balanceOf(address) external view returns (uint); +} + + +interface OrcaleComp { + function getUnderlyingPrice(address) external view returns (uint); +} + +interface ComptrollerLensInterface { + function markets(address) external view returns (bool, uint, bool); + function getAccountLiquidity(address) external view returns (uint, uint, uint); + function claimComp(address) external; + function compAccrued(address) external view returns (uint); + function borrowCaps(address) external view returns (uint); + function borrowGuardianPaused(address) external view returns (bool); + function oracle() external view returns (address); + function compSpeeds(address) external view returns (uint); +} + +interface CompReadInterface { + struct CompBalanceMetadataExt { + uint balance; + uint votes; + address delegate; + uint allocated; + } + + function getCompBalanceMetadataExt( + TokenInterface comp, + ComptrollerLensInterface comptroller, + address account + ) external returns (CompBalanceMetadataExt memory); +} \ No newline at end of file diff --git a/contracts/protocols/mainnet/resolvers/compound/main.sol b/contracts/protocols/mainnet/resolvers/compound/main.sol new file mode 100644 index 0000000..70d8192 --- /dev/null +++ b/contracts/protocols/mainnet/resolvers/compound/main.sol @@ -0,0 +1,90 @@ +pragma solidity ^0.6.0; +pragma experimental ABIEncoderV2; + +/** + * @title Compound. + * @dev Lending & Borrowing. + */ + +import {Helpers} from "./helpers.sol"; +import { + TokenInterface, + ComptrollerLensInterface, + CompReadInterface, + OrcaleComp, + CTokenInterface +} from "./interface.sol"; + +abstract contract Resolver is Helpers { + function getPriceInEth(CTokenInterface cToken) + public + view + returns (uint256 priceInETH, uint256 priceInUSD) + { + uint256 decimals = + getCETHAddress() == address(cToken) + ? 18 + : TokenInterface(cToken.underlying()).decimals(); + uint256 price = + OrcaleComp(getOracleAddress()).getUnderlyingPrice(address(cToken)); + uint256 ethPrice = + OrcaleComp(getOracleAddress()).getUnderlyingPrice(getCETHAddress()); + priceInUSD = price / 10**(18 - decimals); + priceInETH = wdiv(priceInUSD, ethPrice); + } + + function getCompoundData(address owner, address[] memory cAddress) + public + view + returns (CompData[] memory) + { + CompData[] memory tokensData = new CompData[](cAddress.length); + ComptrollerLensInterface troller = getComptroller(); + for (uint256 i = 0; i < cAddress.length; i++) { + CTokenInterface cToken = CTokenInterface(cAddress[i]); + (uint256 priceInETH, uint256 priceInUSD) = getPriceInEth(cToken); + (, uint256 collateralFactor, bool isComped) = + troller.markets(address(cToken)); + uint256 _totalBorrowed = cToken.totalBorrows(); + tokensData[i] = CompData( + priceInETH, + priceInUSD, + cToken.exchangeRateStored(), + cToken.balanceOf(owner), + cToken.borrowBalanceStored(owner), + _totalBorrowed, + add(_totalBorrowed, cToken.getCash()), + troller.borrowCaps(cAddress[i]), + cToken.supplyRatePerBlock(), + cToken.borrowRatePerBlock(), + collateralFactor, + troller.compSpeeds(cAddress[i]), + isComped, + troller.borrowGuardianPaused(cAddress[i]) + ); + } + + return tokensData; + } + + function getPosition(address owner, address[] memory cAddress) + public + returns ( + CompData[] memory, + CompReadInterface.CompBalanceMetadataExt memory + ) + { + return ( + getCompoundData(owner, cAddress), + CompReadInterface(getCompReadAddress()).getCompBalanceMetadataExt( + getCompToken(), + getComptroller(), + owner + ) + ); + } +} + +contract InstaCompoundResolver is Resolver { + string public constant name = "Compound-Resolver-v1.4"; +}