diff --git a/protocol/contracts/libraries/Oracle/LibUsdOracle.sol b/protocol/contracts/libraries/Oracle/LibUsdOracle.sol index 67570a6016..1e05a576e7 100644 --- a/protocol/contracts/libraries/Oracle/LibUsdOracle.sol +++ b/protocol/contracts/libraries/Oracle/LibUsdOracle.sol @@ -123,7 +123,16 @@ library LibUsdOracle { 0, lookback ); - return tokenPrice.mul(chainlinkTokenPrice).div(CHAINLINK_DENOMINATOR); + + // if token decimals != 0, Beanstalk is attempting to query the USD/TOKEN price, and + // thus the price needs to be inverted. + if (tokenDecimals != 0) { + tokenPrice = (10 ** (6 + tokenDecimals)) / tokenPrice; + return (tokenPrice * chainlinkTokenPrice) / (10 ** tokenDecimals); + } else { + // return the TOKEN/USD price. + return (tokenPrice * chainlinkTokenPrice) / CHAINLINK_DENOMINATOR; + } } // If the oracle implementation address is not set, use the current contract. diff --git a/protocol/test/foundry/silo/Oracle.t.sol b/protocol/test/foundry/silo/Oracle.t.sol index 97196e0fc5..9acccea3e9 100644 --- a/protocol/test/foundry/silo/Oracle.t.sol +++ b/protocol/test/foundry/silo/Oracle.t.sol @@ -32,29 +32,7 @@ contract OracleTest is TestHelper { uint256 price = OracleFacet(address(bs)).getUsdTokenPrice(WBTC); assertEq(price, 0.00002e8, "price using encode type 0x01"); - // change encode type to 0x02: - vm.prank(address(bs)); - bs.updateOracleImplementationForToken( - WBTC, - IMockFBeanstalk.Implementation( - WBTC_USDC_03_POOL, - bytes4(0), - bytes1(0x02), - abi.encode(LibChainlinkOracle.FOUR_HOUR_TIMEOUT) - ) - ); - - // also uniswap relies on having a chainlink oracle for the dollar-denominated token, in this case USDC - vm.prank(address(bs)); - bs.updateOracleImplementationForToken( - C.USDC, - IMockFBeanstalk.Implementation( - USDC_USD_CHAINLINK_PRICE_AGGREGATOR, - bytes4(0), - bytes1(0x01), - abi.encode(LibChainlinkOracle.FOUR_DAY_TIMEOUT) - ) - ); + setupUniswapWBTCOracleImplementation(); price = OracleFacet(address(bs)).getTokenUsdPrice(WBTC); // 1 USDC will get ~500 satoshis of BTC at $50k @@ -66,6 +44,28 @@ contract OracleTest is TestHelper { assertApproxEqRel(price, 50000e6, 0.001e18, "price using encode type 0x02"); } + function test_uniswap_external() public { + setupUniswapWBTCOracleImplementation(); + + // exercise TokenUsd price and UsdToken price + uint256 tokenUsdPriceFromExternal = OracleFacet(BEANSTALK).getTokenUsdPriceFromExternal( + WBTC, + 0 + ); + assertApproxEqRel( + tokenUsdPriceFromExternal, + 50000e6, + 0.001e18, + "tokenUsdPriceFromExternal" + ); + + uint256 usdTokenPriceFromExternal = OracleFacet(BEANSTALK).getUsdTokenPriceFromExternal( + WBTC, + 0 + ); + assertEq(usdTokenPriceFromExternal, 0.00002e6, "usdTokenPriceFromExternal"); + } + /** * @notice verifies functionality with LSDChainlinkOracle.sol. */ @@ -282,4 +282,29 @@ contract OracleTest is TestHelper { uint256 priceWBTC = OracleFacet(address(bs)).getUsdTokenPrice(WBTC); assertEq(priceWBTC, 0.00002e8); // adjusted to 8 decimals } + + function setupUniswapWBTCOracleImplementation() public { + vm.prank(address(bs)); + bs.updateOracleImplementationForToken( + WBTC, + IMockFBeanstalk.Implementation( + WBTC_USDC_03_POOL, + bytes4(0), + bytes1(0x02), + abi.encode(LibChainlinkOracle.FOUR_HOUR_TIMEOUT) + ) + ); + + // also uniswap relies on having a chainlink oracle for the dollar-denominated token, in this case USDC + vm.prank(address(bs)); + bs.updateOracleImplementationForToken( + C.USDC, + IMockFBeanstalk.Implementation( + USDC_USD_CHAINLINK_PRICE_AGGREGATOR, + bytes4(0), + bytes1(0x01), + abi.encode(LibChainlinkOracle.FOUR_DAY_TIMEOUT) + ) + ); + } }