diff --git a/packages/contracts/src/dollar/facets/UbiquityPoolFacet.sol b/packages/contracts/src/dollar/facets/UbiquityPoolFacet.sol index 6f9b7b058..1359b99c4 100644 --- a/packages/contracts/src/dollar/facets/UbiquityPoolFacet.sol +++ b/packages/contracts/src/dollar/facets/UbiquityPoolFacet.sol @@ -116,6 +116,15 @@ contract UbiquityPoolFacet is IUbiquityPool, Modifiers { return LibUbiquityPool.governanceEthPoolAddress(); } + /// @inheritdoc IUbiquityPool + function stableEthPriceFeedInformation() + external + view + returns (address, uint256) + { + return LibUbiquityPool.stableEthPriceFeedInformation(); + } + //==================== // Public functions //==================== @@ -293,6 +302,17 @@ contract UbiquityPoolFacet is IUbiquityPool, Modifiers { LibUbiquityPool.setRedemptionDelayBlocks(newRedemptionDelayBlocks); } + /// @inheritdoc IUbiquityPool + function setStableEthChainLinkPriceFeed( + address newPriceFeedAddress, + uint256 newStalenessThreshold + ) external onlyAdmin { + LibUbiquityPool.setStableEthChainLinkPriceFeed( + newPriceFeedAddress, + newStalenessThreshold + ); + } + /// @inheritdoc IUbiquityPool function toggleCollateral(uint256 collateralIndex) external onlyAdmin { LibUbiquityPool.toggleCollateral(collateralIndex); diff --git a/packages/contracts/src/dollar/interfaces/IUbiquityPool.sol b/packages/contracts/src/dollar/interfaces/IUbiquityPool.sol index aa41b7e29..23538b7f5 100644 --- a/packages/contracts/src/dollar/interfaces/IUbiquityPool.sol +++ b/packages/contracts/src/dollar/interfaces/IUbiquityPool.sol @@ -121,6 +121,16 @@ interface IUbiquityPool { */ function governanceEthPoolAddress() external view returns (address); + /** + * @notice Returns chainlink price feed information for stable/ETH pair + * @dev Here stable coin refers to the 1st coin in the Curve's stable/Dollar plain pool + * @return Price feed address and staleness threshold in seconds + */ + function stableEthPriceFeedInformation() + external + view + returns (address, uint256); + //==================== // Public functions //==================== @@ -328,6 +338,16 @@ interface IUbiquityPool { uint256 newRedemptionDelayBlocks ) external; + /** + * @notice Sets chainlink params for stable/ETH price feed + * @param newPriceFeedAddress New chainlink price feed address for stable/ETH pair + * @param newStalenessThreshold New threshold in seconds when chainlink's stable/ETH price feed answer should be considered stale + */ + function setStableEthChainLinkPriceFeed( + address newPriceFeedAddress, + uint256 newStalenessThreshold + ) external; + /** * @notice Toggles (i.e. enables/disables) a particular collateral token * @param collateralIndex Collateral token index diff --git a/packages/contracts/src/dollar/libraries/LibUbiquityPool.sol b/packages/contracts/src/dollar/libraries/LibUbiquityPool.sol index f67eaa870..0f1b196a1 100644 --- a/packages/contracts/src/dollar/libraries/LibUbiquityPool.sol +++ b/packages/contracts/src/dollar/libraries/LibUbiquityPool.sol @@ -103,6 +103,13 @@ library LibUbiquityPool { uint256 ethUsdPriceFeedStalenessThreshold; // Curve's CurveTwocryptoOptimized contract for Governance/ETH pair address governanceEthPoolAddress; + //================================ + // Dollar token pricing related + //================================ + // chainlink price feed for stable/ETH pair + address stableEthPriceFeedAddress; + // threshold in seconds when chainlink's stable/ETH price feed answer should be considered stale + uint256 stableEthPriceFeedStalenessThreshold; } /// @notice Struct used for detailed collateral information @@ -182,6 +189,11 @@ library LibUbiquityPool { ); /// @notice Emitted when a new redemption delay in blocks is set event RedemptionDelayBlocksSet(uint256 redemptionDelayBlocks); + /// @notice Emitted on setting chainlink's price feed for stable/ETH pair + event StableEthPriceFeedSet( + address newPriceFeedAddress, + uint256 newStalenessThreshold + ); //===================== // Modifiers @@ -467,6 +479,23 @@ library LibUbiquityPool { return poolStorage.governanceEthPoolAddress; } + /** + * @notice Returns chainlink price feed information for stable/ETH pair + * @dev Here stable coin refers to the 1st coin in the Curve's stable/Dollar plain pool + * @return Price feed address and staleness threshold in seconds + */ + function stableEthPriceFeedInformation() + internal + view + returns (address, uint256) + { + UbiquityPoolStorage storage poolStorage = ubiquityPoolStorage(); + return ( + poolStorage.stableEthPriceFeedAddress, + poolStorage.stableEthPriceFeedStalenessThreshold + ); + } + //==================== // Public functions //==================== @@ -1124,6 +1153,24 @@ library LibUbiquityPool { emit RedemptionDelayBlocksSet(newRedemptionDelayBlocks); } + /** + * @notice Sets chainlink params for stable/ETH price feed + * @param newPriceFeedAddress New chainlink price feed address for stable/ETH pair + * @param newStalenessThreshold New threshold in seconds when chainlink's stable/ETH price feed answer should be considered stale + */ + function setStableEthChainLinkPriceFeed( + address newPriceFeedAddress, + uint256 newStalenessThreshold + ) internal { + UbiquityPoolStorage storage poolStorage = ubiquityPoolStorage(); + + poolStorage.stableEthPriceFeedAddress = newPriceFeedAddress; + poolStorage + .stableEthPriceFeedStalenessThreshold = newStalenessThreshold; + + emit StableEthPriceFeedSet(newPriceFeedAddress, newStalenessThreshold); + } + /** * @notice Toggles (i.e. enables/disables) a particular collateral token * @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 de0cf1807..c9e7d7bb5 100644 --- a/packages/contracts/test/diamond/facets/UbiquityPoolFacet.t.sol +++ b/packages/contracts/test/diamond/facets/UbiquityPoolFacet.t.sol @@ -28,6 +28,7 @@ contract UbiquityPoolFacetTest is DiamondTestSetup { MockCurveTwocryptoOptimized curveGovernanceEthPool; MockERC20 curveTriPoolLpToken; MockChainLinkFeed ethUsdPriceFeed; + MockChainLinkFeed stableEthPriceFeed; MockERC20 wethToken; address user = address(1); @@ -60,6 +61,10 @@ contract UbiquityPoolFacetTest is DiamondTestSetup { uint256 newRedeemPriceThreshold ); event RedemptionDelayBlocksSet(uint256 redemptionDelayBlocks); + event StableEthPriceFeedSet( + address newPriceFeedAddress, + uint256 newStalenessThreshold + ); function setUp() public override { super.setUp(); @@ -75,6 +80,9 @@ contract UbiquityPoolFacetTest is DiamondTestSetup { // init ETH/USD price feed ethUsdPriceFeed = new MockChainLinkFeed(); + // init stable/ETH price feed + stableEthPriceFeed = new MockChainLinkFeed(); + // init WETH token wethToken = new MockERC20("WETH", "WETH", 18); @@ -119,6 +127,15 @@ contract UbiquityPoolFacetTest is DiamondTestSetup { 1 // answered in round ); + // set stable/ETH price feed mock params + stableEthPriceFeed.updateMockParams( + 1, // round id + 330000000000000, // answer, 330000000000000 = 0.00033 ETH (18 decimals) + block.timestamp, // started at + block.timestamp, // updated at + 1 // answered in round + ); + // set ETH/Governance price to 30k in Curve pool mock curveGovernanceEthPool.updateMockParams(30_000e18); @@ -135,6 +152,12 @@ contract UbiquityPoolFacetTest is DiamondTestSetup { 1 days // price feed staleness threshold in seconds ); + // set price feed for stable/ETH pair + ubiquityPoolFacet.setStableEthChainLinkPriceFeed( + address(stableEthPriceFeed), // price feed address + 1 days // price feed staleness threshold in seconds + ); + // enable collateral at index 0 ubiquityPoolFacet.toggleCollateral(0); // set mint and redeem fees @@ -455,6 +478,15 @@ contract UbiquityPoolFacetTest is DiamondTestSetup { assertEq(governanceEthPoolAddress, address(curveGovernanceEthPool)); } + function testStableEthPriceFeedInformation_ShouldReturnStableEthPriceFeedInformation() + public + { + (address priceFeed, uint256 stalenessThreshold) = ubiquityPoolFacet + .stableEthPriceFeedInformation(); + assertEq(priceFeed, address(stableEthPriceFeed)); + assertEq(stalenessThreshold, 1 days); + } + //==================== // Public functions //==================== @@ -1509,6 +1541,37 @@ contract UbiquityPoolFacetTest is DiamondTestSetup { vm.stopPrank(); } + function testSetStableEthChainLinkPriceFeed_ShouldSetStableEthChainLinkPriceFeed() + public + { + vm.startPrank(admin); + + ( + address oldPriceFeedAddress, + uint256 oldStalenessThreshold + ) = ubiquityPoolFacet.stableEthPriceFeedInformation(); + assertEq(oldPriceFeedAddress, address(stableEthPriceFeed)); + assertEq(oldStalenessThreshold, 1 days); + + address newPriceFeedAddress = address(1); + uint256 newStalenessThreshold = 2 days; + vm.expectEmit(address(ubiquityPoolFacet)); + emit StableEthPriceFeedSet(newPriceFeedAddress, newStalenessThreshold); + ubiquityPoolFacet.setStableEthChainLinkPriceFeed( + newPriceFeedAddress, + newStalenessThreshold + ); + + ( + address updatedPriceFeedAddress, + uint256 updatedStalenessThreshold + ) = ubiquityPoolFacet.stableEthPriceFeedInformation(); + assertEq(updatedPriceFeedAddress, newPriceFeedAddress); + assertEq(updatedStalenessThreshold, newStalenessThreshold); + + vm.stopPrank(); + } + function testToggleCollateral_ShouldToggleCollateral() public { vm.startPrank(admin);