diff --git a/packages/contracts/src/dollar/facets/UbiquityPoolFacet.sol b/packages/contracts/src/dollar/facets/UbiquityPoolFacet.sol index 6186dc1b0..0c1e95e4e 100644 --- a/packages/contracts/src/dollar/facets/UbiquityPoolFacet.sol +++ b/packages/contracts/src/dollar/facets/UbiquityPoolFacet.sol @@ -46,6 +46,15 @@ contract UbiquityPoolFacet is IUbiquityPool, Modifiers { return LibUbiquityPool.collateralUsdBalance(); } + /// @inheritdoc IUbiquityPool + function ethUsdPriceFeedInformation() + external + view + returns (address, uint256) + { + return LibUbiquityPool.ethUsdPriceFeedInformation(); + } + /// @inheritdoc IUbiquityPool function freeCollateralBalance( uint256 collateralIndex @@ -195,6 +204,17 @@ contract UbiquityPoolFacet is IUbiquityPool, Modifiers { LibUbiquityPool.setCollateralRatio(newCollateralRatio); } + /// @inheritdoc IUbiquityPool + function setEthUsdChainLinkPriceFeed( + address newPriceFeedAddress, + uint256 newStalenessThreshold + ) external onlyAdmin { + LibUbiquityPool.setEthUsdChainLinkPriceFeed( + newPriceFeedAddress, + newStalenessThreshold + ); + } + /// @inheritdoc IUbiquityPool function setFees( uint256 collateralIndex, diff --git a/packages/contracts/src/dollar/interfaces/IUbiquityPool.sol b/packages/contracts/src/dollar/interfaces/IUbiquityPool.sol index 74ccd6043..cd3c0a762 100644 --- a/packages/contracts/src/dollar/interfaces/IUbiquityPool.sol +++ b/packages/contracts/src/dollar/interfaces/IUbiquityPool.sol @@ -47,6 +47,15 @@ interface IUbiquityPool { view returns (uint256 balanceTally); + /** + * @notice Returns chainlink price feed information for ETH/USD pair + * @return Price feed address and staleness threshold in seconds + */ + function ethUsdPriceFeedInformation() + external + view + returns (address, uint256); + /** * @notice Returns free collateral balance (i.e. that can be borrowed by AMO minters) * @param collateralIndex collateral token index @@ -214,6 +223,16 @@ interface IUbiquityPool { */ function setCollateralRatio(uint256 newCollateralRatio) external; + /** + * @notice Sets chainlink params for ETH/USD price feed + * @param newPriceFeedAddress New chainlink price feed address for ETH/USD pair + * @param newStalenessThreshold New threshold in seconds when chainlink's ETH/USD price feed answer should be considered stale + */ + function setEthUsdChainLinkPriceFeed( + address newPriceFeedAddress, + uint256 newStalenessThreshold + ) external; + /** * @notice Sets mint and redeem fees, 1_000_000 = 100% * @param collateralIndex Collateral token index diff --git a/packages/contracts/src/dollar/libraries/LibUbiquityPool.sol b/packages/contracts/src/dollar/libraries/LibUbiquityPool.sol index 2d387b88e..d67dc6fb6 100644 --- a/packages/contracts/src/dollar/libraries/LibUbiquityPool.sol +++ b/packages/contracts/src/dollar/libraries/LibUbiquityPool.sol @@ -92,6 +92,10 @@ library LibUbiquityPool { //==================================== // Governance token pricing related //==================================== + // chainlink price feed for ETH/USD pair + address ethUsdPriceFeedAddress; + // threshold in seconds when chainlink's ETH/USD price feed answer should be considered stale + uint256 ethUsdPriceFeedStalenessThreshold; // Curve's CurveTwocryptoOptimized contract for Governance/ETH pair address governanceEthPoolAddress; } @@ -149,6 +153,11 @@ library LibUbiquityPool { event CollateralRatioSet(uint256 newCollateralRatio); /// @notice Emitted on enabling/disabling a particular collateral token event CollateralToggled(uint256 collateralIndex, bool newState); + /// @notice Emitted on setting chainlink's price feed for ETH/USD pair + event EthUsdPriceFeedSet( + address newPriceFeedAddress, + uint256 newStalenessThreshold + ); /// @notice Emitted when fees are updated event FeesSet( uint256 collateralIndex, @@ -298,6 +307,22 @@ library LibUbiquityPool { } } + /** + * @notice Returns chainlink price feed information for ETH/USD pair + * @return Price feed address and staleness threshold in seconds + */ + function ethUsdPriceFeedInformation() + internal + view + returns (address, uint256) + { + UbiquityPoolStorage storage poolStorage = ubiquityPoolStorage(); + return ( + poolStorage.ethUsdPriceFeedAddress, + poolStorage.ethUsdPriceFeedStalenessThreshold + ); + } + /** * @notice Returns free collateral balance (i.e. that can be borrowed by AMO minters) * @param collateralIndex collateral token index @@ -829,6 +854,23 @@ library LibUbiquityPool { emit CollateralRatioSet(newCollateralRatio); } + /** + * @notice Sets chainlink params for ETH/USD price feed + * @param newPriceFeedAddress New chainlink price feed address for ETH/USD pair + * @param newStalenessThreshold New threshold in seconds when chainlink's ETH/USD price feed answer should be considered stale + */ + function setEthUsdChainLinkPriceFeed( + address newPriceFeedAddress, + uint256 newStalenessThreshold + ) internal { + UbiquityPoolStorage storage poolStorage = ubiquityPoolStorage(); + + poolStorage.ethUsdPriceFeedAddress = newPriceFeedAddress; + poolStorage.ethUsdPriceFeedStalenessThreshold = newStalenessThreshold; + + emit EthUsdPriceFeedSet(newPriceFeedAddress, newStalenessThreshold); + } + /** * @notice Sets mint and redeem fees, 1_000_000 = 100% * @param collateralIndex Collateral token index diff --git a/packages/contracts/test/diamond/facets/UbiquityPoolFacet.t.sol b/packages/contracts/test/diamond/facets/UbiquityPoolFacet.t.sol index 3369e9066..efa3c8ecb 100644 --- a/packages/contracts/test/diamond/facets/UbiquityPoolFacet.t.sol +++ b/packages/contracts/test/diamond/facets/UbiquityPoolFacet.t.sol @@ -27,6 +27,7 @@ contract UbiquityPoolFacetTest is DiamondTestSetup { MockCurveStableSwapMetaNG curveDollarMetaPool; MockCurveTwocryptoOptimized curveGovernanceEthPool; MockERC20 curveTriPoolLpToken; + MockChainLinkFeed ethUsdPriceFeed; MockERC20 wethToken; address user = address(1); @@ -42,6 +43,10 @@ contract UbiquityPoolFacetTest is DiamondTestSetup { event CollateralPriceSet(uint256 collateralIndex, uint256 newPrice); event CollateralRatioSet(uint256 newCollateralRatio); event CollateralToggled(uint256 collateralIndex, bool newState); + event EthUsdPriceFeedSet( + address newPriceFeedAddress, + uint256 newStalenessThreshold + ); event FeesSet( uint256 collateralIndex, uint256 newMintFee, @@ -67,6 +72,9 @@ contract UbiquityPoolFacetTest is DiamondTestSetup { // init collateral price feed collateralTokenPriceFeed = new MockChainLinkFeed(); + // init ETH/USD price feed + ethUsdPriceFeed = new MockChainLinkFeed(); + // init WETH token wethToken = new MockERC20("WETH", "WETH", 18); @@ -102,6 +110,15 @@ contract UbiquityPoolFacetTest is DiamondTestSetup { 1 // answered in round ); + // set ETH/USD price feed mock params + ethUsdPriceFeed.updateMockParams( + 1, // round id + 3000_00000000, // answer, 3000_00000000 = $3000 (8 decimals) + block.timestamp, // started at + block.timestamp, // updated at + 1 // answered in round + ); + // set price feed for collateral token ubiquityPoolFacet.setCollateralChainLinkPriceFeed( address(collateralToken), // collateral token address @@ -109,6 +126,12 @@ contract UbiquityPoolFacetTest is DiamondTestSetup { 1 days // price feed staleness threshold in seconds ); + // set price feed for ETH/USD pair + ubiquityPoolFacet.setEthUsdChainLinkPriceFeed( + address(ethUsdPriceFeed), // price feed address + 1 days // price feed staleness threshold in seconds + ); + // enable collateral at index 0 ubiquityPoolFacet.toggleCollateral(0); // set mint and redeem fees @@ -244,6 +267,15 @@ contract UbiquityPoolFacetTest is DiamondTestSetup { assertEq(balanceTally, 100e18); } + function testEthUsdPriceFeedInformation_ShouldReturnEthUsdPriceFeedInformation() + public + { + (address priceFeed, uint256 stalenessThreshold) = ubiquityPoolFacet + .ethUsdPriceFeedInformation(); + assertEq(priceFeed, address(ethUsdPriceFeed)); + assertEq(stalenessThreshold, 1 days); + } + function testFreeCollateralBalance_ShouldReturnCollateralAmountAvailableForBorrowingByAmoMinters() public { @@ -932,6 +964,37 @@ contract UbiquityPoolFacetTest is DiamondTestSetup { vm.stopPrank(); } + function testSetEthUsdChainLinkPriceFeed_ShouldSetEthUsdChainLinkPriceFeed() + public + { + vm.startPrank(admin); + + ( + address oldPriceFeedAddress, + uint256 oldStalenessThreshold + ) = ubiquityPoolFacet.ethUsdPriceFeedInformation(); + assertEq(oldPriceFeedAddress, address(ethUsdPriceFeed)); + assertEq(oldStalenessThreshold, 1 days); + + address newPriceFeedAddress = address(1); + uint256 newStalenessThreshold = 2 days; + vm.expectEmit(address(ubiquityPoolFacet)); + emit EthUsdPriceFeedSet(newPriceFeedAddress, newStalenessThreshold); + ubiquityPoolFacet.setEthUsdChainLinkPriceFeed( + newPriceFeedAddress, + newStalenessThreshold + ); + + ( + address updatedPriceFeedAddress, + uint256 updatedStalenessThreshold + ) = ubiquityPoolFacet.ethUsdPriceFeedInformation(); + assertEq(updatedPriceFeedAddress, newPriceFeedAddress); + assertEq(updatedStalenessThreshold, newStalenessThreshold); + + vm.stopPrank(); + } + function testSetFees_ShouldSetMintAndRedeemFees() public { vm.startPrank(admin);