From cd5813780738e9162d2423255a6d686a4c4aaf16 Mon Sep 17 00:00:00 2001 From: xam-darnold Date: Mon, 25 Oct 2021 23:25:43 -0500 Subject: [PATCH 1/3] add keep3r --- contracts/PriceOracleProxyFTM.sol | 85 ++++++++++++++++++++++++++++++- 1 file changed, 84 insertions(+), 1 deletion(-) diff --git a/contracts/PriceOracleProxyFTM.sol b/contracts/PriceOracleProxyFTM.sol index e8f8413..525c668 100644 --- a/contracts/PriceOracleProxyFTM.sol +++ b/contracts/PriceOracleProxyFTM.sol @@ -57,6 +57,16 @@ interface IStdReference { returns (ReferenceData[] memory); } +interface IKeep3rV2Oracle { + function current(address tokenIn, uint amountIn, address tokenOut) external view returns (uint amountOut, uint lastUpdatedAgo); + function quote(address tokenIn, uint amountIn, address tokenOut, uint points) external view returns (uint amountOut, uint lastUpdatedAgo); +} + +interface IXToken { + function decimals() external view returns (uint8); + function getShareValue() external view returns (uint256); +} + contract PriceOracleProxyFTM is PriceOracle, Exponential { /// @notice Admin address address public admin; @@ -67,6 +77,12 @@ contract PriceOracleProxyFTM is PriceOracle, Exponential { /// @notice Chainlink Aggregators mapping(address => AggregatorV3Interface) public aggregators; + /// @notice Keep3r Oracles + mapping(address => IKeep3rV2Oracle) public keep3rs; + + /// @notice Keep3r Oracles + mapping(address => address) public xTokenUnderlyings; + /// @notice The v1 price oracle, maintain by CREAM V1PriceOracleInterface public v1PriceOracle; @@ -80,6 +96,13 @@ contract PriceOracleProxyFTM is PriceOracle, Exponential { /// @notice Quote symbol we used for BAND reference contract string public constant QUOTE_SYMBOL = "USD"; + /// @notice WFTM address + address public constant WFTM_ADDRESS = address(0x21be370D5312f44cB42ce377BC9b8a0cEF1A4C83); + + /// @notice scWFTM address + address public constant SCWFTM_ADDRESS = address(0x5AA53f03197E08C4851CAD8C92c7922DA5857E5d); + + /** * @param admin_ The address of admin to set aggregators * @param v1PriceOracle_ The address of the v1 price oracle, which will continue to operate and hold prices for collateral assets @@ -101,6 +124,8 @@ contract PriceOracleProxyFTM is PriceOracle, Exponential { uint chainLinkPrice = getPriceFromChainlink(cTokenAddress); uint bandPrice = getPriceFromBAND(cTokenAddress); + uint keep3rPrice = getPriceFromKeep3r(cTokenAddress); + if (chainLinkPrice != 0 && bandPrice != 0) { checkPriceDiff(chainLinkPrice, bandPrice); @@ -114,6 +139,10 @@ contract PriceOracleProxyFTM is PriceOracle, Exponential { return bandPrice; } + if (keep3rPrice != 0) { + return keep3rPrice; + } + return getPriceFromV1(cTokenAddress); } @@ -144,7 +173,7 @@ contract PriceOracleProxyFTM is PriceOracle, Exponential { if (address(aggregator) != address(0)) { ( , int answer, , , ) = aggregator.latestRoundData(); - // It's fine for price to be 0. We have two price feeds. + // It's fine for price to be 0. We have three price feeds. if (answer == 0) { return 0; } @@ -171,6 +200,41 @@ contract PriceOracleProxyFTM is PriceOracle, Exponential { return 0; } + + /** + * @notice Try to get the underlying price of a cToken from Keep3r. + * @param cTokenAddress The token to get the underlying price of + * @return The price. Return 0 if the keep3r is not set. + */ + function getPriceFromKeep3r(address cTokenAddress) public view returns (uint) { + IKeep3rV2Oracle keep3r = keep3rs[cTokenAddress]; + address xTokenUnderlying = xTokenUnderlyings[cTokenAddress]; + if (address(keep3r) != address(0)) { + uint answer; + //check if collateral is a xToken + if (xTokenUnderlying != address(0)) { + IXToken xToken = IXToken(CErc20(cTokenAddress).underlying()); + (uint tempAnswer, ) = keep3r.quote(xTokenUnderlying, 1, WFTM_ADDRESS, 2); + answer = div_(mul_(tempAnswer, uint(xToken.getShareValue())), 10**uint(xToken.decimals())); + } else { + (answer, ) = keep3r.quote(CErc20(cTokenAddress).underlying(), 1, WFTM_ADDRESS, 2); + } + + + // It's fine for price to be 0. We have three price feeds. + if (answer == 0) { + return 0; + } + //multiply by wftm price in USD + uint wftmPrice = getUnderlyingPrice(CToken(SCWFTM_ADDRESS)); + + uint price = mul_(uint(answer), wftmPrice); + + return getNormalizedPrice(price, cTokenAddress); + } + return 0; + } + /** * @notice Normalize the price according to the underlying decimals. * @param price The original price @@ -198,6 +262,9 @@ contract PriceOracleProxyFTM is PriceOracle, Exponential { event MaxPriceDiffUpdated(uint maxDiff); event AggregatorUpdated(address cTokenAddress, address source); event UnderlyingSymbolUpdated(address cTokenAddress, string symbol); + event Keep3rUpdated(address cTokenAddress, address keep3r); + event xTokenUnderlyingUpdated(address cTokenAddress, address xTokenUnderlying); + function _setAdmin(address _admin) external { require(msg.sender == admin, "only the admin may set the new admin"); @@ -226,4 +293,20 @@ contract PriceOracleProxyFTM is PriceOracle, Exponential { emit UnderlyingSymbolUpdated(cTokenAddresses[i], symbols[i]); } } + + function _setKeep3rOracles(address[] calldata cTokenAddresses, address[] calldata sources) external { + require(msg.sender == admin, "only the admin may set the keeper oracles"); + for (uint i = 0; i < cTokenAddresses.length; i++) { + keep3rs[cTokenAddresses[i]] = IKeep3rV2Oracle(sources[i]); + emit Keep3rUpdated(cTokenAddresses[i], sources[i]); + } + } + + function _setXToken(address[] calldata cTokenAddresses, address[] calldata sources) external { + require(msg.sender == admin, "only the admin may set the keeper oracles"); + for (uint i = 0; i < cTokenAddresses.length; i++) { + xTokenUnderlyings[cTokenAddresses[i]] = sources[i]; + emit xTokenUnderlyingUpdated(cTokenAddresses[i], sources[i]); + } + } } \ No newline at end of file From 5656c66b8466a7100a533af5a3fc91dfc0412e4c Mon Sep 17 00:00:00 2001 From: xam-darnold Date: Fri, 29 Oct 2021 06:01:19 -0500 Subject: [PATCH 2/3] update priceOracleProxyFTM fixing xToken --- contracts/PriceOracleProxyFTM.sol | 29 ++--------------------------- 1 file changed, 2 insertions(+), 27 deletions(-) diff --git a/contracts/PriceOracleProxyFTM.sol b/contracts/PriceOracleProxyFTM.sol index 525c668..5f42fc7 100644 --- a/contracts/PriceOracleProxyFTM.sol +++ b/contracts/PriceOracleProxyFTM.sol @@ -60,11 +60,7 @@ interface IStdReference { interface IKeep3rV2Oracle { function current(address tokenIn, uint amountIn, address tokenOut) external view returns (uint amountOut, uint lastUpdatedAgo); function quote(address tokenIn, uint amountIn, address tokenOut, uint points) external view returns (uint amountOut, uint lastUpdatedAgo); -} - -interface IXToken { - function decimals() external view returns (uint8); - function getShareValue() external view returns (uint256); + function xTokenUnderlying() external view returns (address); } contract PriceOracleProxyFTM is PriceOracle, Exponential { @@ -80,9 +76,6 @@ contract PriceOracleProxyFTM is PriceOracle, Exponential { /// @notice Keep3r Oracles mapping(address => IKeep3rV2Oracle) public keep3rs; - /// @notice Keep3r Oracles - mapping(address => address) public xTokenUnderlyings; - /// @notice The v1 price oracle, maintain by CREAM V1PriceOracleInterface public v1PriceOracle; @@ -208,18 +201,9 @@ contract PriceOracleProxyFTM is PriceOracle, Exponential { */ function getPriceFromKeep3r(address cTokenAddress) public view returns (uint) { IKeep3rV2Oracle keep3r = keep3rs[cTokenAddress]; - address xTokenUnderlying = xTokenUnderlyings[cTokenAddress]; if (address(keep3r) != address(0)) { uint answer; - //check if collateral is a xToken - if (xTokenUnderlying != address(0)) { - IXToken xToken = IXToken(CErc20(cTokenAddress).underlying()); - (uint tempAnswer, ) = keep3r.quote(xTokenUnderlying, 1, WFTM_ADDRESS, 2); - answer = div_(mul_(tempAnswer, uint(xToken.getShareValue())), 10**uint(xToken.decimals())); - } else { - (answer, ) = keep3r.quote(CErc20(cTokenAddress).underlying(), 1, WFTM_ADDRESS, 2); - } - + (answer, ) = keep3r.quote(keep3r.xTokenUnderlying(), 1, WFTM_ADDRESS, 2); // It's fine for price to be 0. We have three price feeds. if (answer == 0) { @@ -263,7 +247,6 @@ contract PriceOracleProxyFTM is PriceOracle, Exponential { event AggregatorUpdated(address cTokenAddress, address source); event UnderlyingSymbolUpdated(address cTokenAddress, string symbol); event Keep3rUpdated(address cTokenAddress, address keep3r); - event xTokenUnderlyingUpdated(address cTokenAddress, address xTokenUnderlying); function _setAdmin(address _admin) external { @@ -301,12 +284,4 @@ contract PriceOracleProxyFTM is PriceOracle, Exponential { emit Keep3rUpdated(cTokenAddresses[i], sources[i]); } } - - function _setXToken(address[] calldata cTokenAddresses, address[] calldata sources) external { - require(msg.sender == admin, "only the admin may set the keeper oracles"); - for (uint i = 0; i < cTokenAddresses.length; i++) { - xTokenUnderlyings[cTokenAddresses[i]] = sources[i]; - emit xTokenUnderlyingUpdated(cTokenAddresses[i], sources[i]); - } - } } \ No newline at end of file From 910a0f0578a1e29bfe789cc1aa1a520c07465eaf Mon Sep 17 00:00:00 2001 From: xam-darnold Date: Sat, 30 Oct 2021 20:40:07 -0500 Subject: [PATCH 3/3] gas optimization on getUnderlyingPrice --- contracts/PriceOracleProxyFTM.sol | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/contracts/PriceOracleProxyFTM.sol b/contracts/PriceOracleProxyFTM.sol index 5f42fc7..da09aea 100644 --- a/contracts/PriceOracleProxyFTM.sol +++ b/contracts/PriceOracleProxyFTM.sol @@ -117,7 +117,6 @@ contract PriceOracleProxyFTM is PriceOracle, Exponential { uint chainLinkPrice = getPriceFromChainlink(cTokenAddress); uint bandPrice = getPriceFromBAND(cTokenAddress); - uint keep3rPrice = getPriceFromKeep3r(cTokenAddress); if (chainLinkPrice != 0 && bandPrice != 0) { checkPriceDiff(chainLinkPrice, bandPrice); @@ -131,6 +130,8 @@ contract PriceOracleProxyFTM is PriceOracle, Exponential { if (bandPrice != 0) { return bandPrice; } + + uint keep3rPrice = getPriceFromKeep3r(cTokenAddress); if (keep3rPrice != 0) { return keep3rPrice;