From ebae5bb47fce035c2f8e1f670f0b71511c73c664 Mon Sep 17 00:00:00 2001 From: rndquu Date: Mon, 22 Apr 2024 10:59:50 +0300 Subject: [PATCH 1/5] feat: add ICurveStableSwapNG --- .../src/dollar/interfaces/ICurveStableSwapNG.sol | 9 +++++++++ .../src/dollar/libraries/LibUbiquityPool.sol | 4 ++-- .../src/dollar/mocks/MockCurveStableSwapNG.sol | 15 +++++++++++++++ 3 files changed, 26 insertions(+), 2 deletions(-) create mode 100644 packages/contracts/src/dollar/interfaces/ICurveStableSwapNG.sol create mode 100644 packages/contracts/src/dollar/mocks/MockCurveStableSwapNG.sol diff --git a/packages/contracts/src/dollar/interfaces/ICurveStableSwapNG.sol b/packages/contracts/src/dollar/interfaces/ICurveStableSwapNG.sol new file mode 100644 index 000000000..e6c548478 --- /dev/null +++ b/packages/contracts/src/dollar/interfaces/ICurveStableSwapNG.sol @@ -0,0 +1,9 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.19; + +import {ICurveStableSwapMetaNG} from "./ICurveStableSwapMetaNG.sol"; + +/** + * @notice Curve's interface for plain pool which contains only USD pegged assets + */ +interface ICurveStableSwapNG is ICurveStableSwapMetaNG {} diff --git a/packages/contracts/src/dollar/libraries/LibUbiquityPool.sol b/packages/contracts/src/dollar/libraries/LibUbiquityPool.sol index 695fc75cc..f67eaa870 100644 --- a/packages/contracts/src/dollar/libraries/LibUbiquityPool.sol +++ b/packages/contracts/src/dollar/libraries/LibUbiquityPool.sol @@ -6,7 +6,7 @@ import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import {SafeMath} from "@openzeppelin/contracts/utils/math/SafeMath.sol"; -import {ICurveStableSwapMetaNG} from "../interfaces/ICurveStableSwapMetaNG.sol"; +import {ICurveStableSwapNG} from "../interfaces/ICurveStableSwapNG.sol"; import {ICurveTwocryptoOptimized} from "../interfaces/ICurveTwocryptoOptimized.sol"; import {IDollarAmoMinter} from "../interfaces/IDollarAmoMinter.sol"; import {IERC20Ubiquity} from "../interfaces/IERC20Ubiquity.sol"; @@ -373,7 +373,7 @@ library LibUbiquityPool { // load storage shared across all libraries AppStorage storage store = LibAppStorage.appStorage(); // get Dollar price from Curve Metapool (18 decimals) - uint256 dollarPriceUsdD18 = ICurveStableSwapMetaNG( + uint256 dollarPriceUsdD18 = ICurveStableSwapNG( store.stableSwapMetaPoolAddress ).price_oracle(0); // convert to 6 decimals diff --git a/packages/contracts/src/dollar/mocks/MockCurveStableSwapNG.sol b/packages/contracts/src/dollar/mocks/MockCurveStableSwapNG.sol new file mode 100644 index 000000000..d95f996b9 --- /dev/null +++ b/packages/contracts/src/dollar/mocks/MockCurveStableSwapNG.sol @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.19; + +import {ICurveStableSwapNG} from "../interfaces/ICurveStableSwapNG.sol"; +import {MockCurveStableSwapMetaNG} from "./MockCurveStableSwapMetaNG.sol"; + +contract MockCurveStableSwapNG is + ICurveStableSwapNG, + MockCurveStableSwapMetaNG +{ + constructor( + address _token0, + address _token1 + ) MockCurveStableSwapMetaNG(_token0, _token1) {} +} From 2fdab8cb18dbbb683175250aa0593b7739e1d75f Mon Sep 17 00:00:00 2001 From: rndquu Date: Mon, 22 Apr 2024 13:23:52 +0300 Subject: [PATCH 2/5] feat: add plain pool setter --- .../src/dollar/facets/ManagerFacet.sol | 19 +++++++++++++++++++ .../src/dollar/libraries/LibAppStorage.sol | 1 + .../test/diamond/facets/ManagerFacet.t.sol | 8 ++++++++ 3 files changed, 28 insertions(+) diff --git a/packages/contracts/src/dollar/facets/ManagerFacet.sol b/packages/contracts/src/dollar/facets/ManagerFacet.sol index 2e7611cc8..62f1e23b4 100644 --- a/packages/contracts/src/dollar/facets/ManagerFacet.sol +++ b/packages/contracts/src/dollar/facets/ManagerFacet.sol @@ -144,6 +144,17 @@ contract ManagerFacet is Modifiers { store.stableSwapMetaPoolAddress = _stableSwapMetaPoolAddress; } + /** + * @notice Sets Curve's Dollar-Stablecoin plain pool address + * @dev `_stableSwapPlainPoolAddress` is used to fetch Dollar price in USD + * @param _stableSwapPlainPoolAddress Curve's Dollar-Stablecoin plain pool address + */ + function setStableSwapPlainPoolAddress( + address _stableSwapPlainPoolAddress + ) external onlyAdmin { + store.stableSwapPlainPoolAddress = _stableSwapPlainPoolAddress; + } + /** * @notice Sets staking contract address * @dev Staking contract participants deposit Curve LP tokens @@ -385,6 +396,14 @@ contract ManagerFacet is Modifiers { return store.stableSwapMetaPoolAddress; } + /** + * @notice Returns Curve's plain pool address for Dollar-Stablecoin pair + * @return Curve's plain pool address for Dollar-Stablecoin pair + */ + function stableSwapPlainPoolAddress() external view returns (address) { + return store.stableSwapPlainPoolAddress; + } + /** * @notice Returns staking address * @return Staking address diff --git a/packages/contracts/src/dollar/libraries/LibAppStorage.sol b/packages/contracts/src/dollar/libraries/LibAppStorage.sol index 6bd153c67..e7b94dd4d 100644 --- a/packages/contracts/src/dollar/libraries/LibAppStorage.sol +++ b/packages/contracts/src/dollar/libraries/LibAppStorage.sol @@ -17,6 +17,7 @@ struct AppStorage { address stakingShareAddress; address stakingContractAddress; address stableSwapMetaPoolAddress; + address stableSwapPlainPoolAddress; address curve3PoolTokenAddress; // 3CRV address treasuryAddress; address governanceTokenAddress; diff --git a/packages/contracts/test/diamond/facets/ManagerFacet.t.sol b/packages/contracts/test/diamond/facets/ManagerFacet.t.sol index 61c245bca..2e7909c97 100644 --- a/packages/contracts/test/diamond/facets/ManagerFacet.t.sol +++ b/packages/contracts/test/diamond/facets/ManagerFacet.t.sol @@ -85,6 +85,14 @@ contract ManagerFacetTest is DiamondTestSetup { assertEq(managerFacet.stableSwapMetaPoolAddress(), contract1); } + function testSetStableSwapPlainPoolAddress_ShouldSucceed() + public + prankAs(admin) + { + managerFacet.setStableSwapPlainPoolAddress(contract1); + assertEq(managerFacet.stableSwapPlainPoolAddress(), contract1); + } + function testSetStakingContractAddress_ShouldSucceed() public prankAs(admin) From 76267f7dadb5b26b481ebc4e8ebd20f33be272ea Mon Sep 17 00:00:00 2001 From: rndquu Date: Mon, 22 Apr 2024 14:53:19 +0300 Subject: [PATCH 3/5] feat: add stable/ETH setter --- .../src/dollar/facets/UbiquityPoolFacet.sol | 20 ++++++ .../src/dollar/interfaces/IUbiquityPool.sol | 20 ++++++ .../src/dollar/libraries/LibUbiquityPool.sol | 47 ++++++++++++++ .../diamond/facets/UbiquityPoolFacet.t.sol | 63 +++++++++++++++++++ 4 files changed, 150 insertions(+) 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); From 81f760bdbcf8c9b467ba55014f78ae86c5ca68de Mon Sep 17 00:00:00 2001 From: rndquu Date: Mon, 22 Apr 2024 17:47:02 +0300 Subject: [PATCH 4/5] feat: fetch Dollar price --- .../src/dollar/libraries/LibUbiquityPool.sol | 57 ++++++- .../diamond/facets/UbiquityPoolFacet.t.sol | 148 ++++++++++++++---- 2 files changed, 168 insertions(+), 37 deletions(-) diff --git a/packages/contracts/src/dollar/libraries/LibUbiquityPool.sol b/packages/contracts/src/dollar/libraries/LibUbiquityPool.sol index 0f1b196a1..263716368 100644 --- a/packages/contracts/src/dollar/libraries/LibUbiquityPool.sol +++ b/packages/contracts/src/dollar/libraries/LibUbiquityPool.sol @@ -374,7 +374,12 @@ library LibUbiquityPool { } /** - * @notice Returns Ubiquity Dollar token USD price (1e6 precision) from Curve Metapool (Ubiquity Dollar, Curve Tri-Pool LP) + * @notice Returns Ubiquity Dollar token USD price (1e6 precision) from Curve plain pool (Stable coin, Ubiquity Dollar) + * How it works: + * 1. Fetch Stable/ETH quote from chainlink + * 2. Fetch ETH/USD quote from chainlink + * 3. Fetch Dollar/Stable quote from Curve's plain pool + * 4. Calculate Dollar token price in USD * @return dollarPriceUsd USD price of Ubiquity Dollar */ function getDollarPriceUsd() @@ -382,15 +387,59 @@ library LibUbiquityPool { view returns (uint256 dollarPriceUsd) { - // load storage shared across all libraries AppStorage storage store = LibAppStorage.appStorage(); - // get Dollar price from Curve Metapool (18 decimals) + UbiquityPoolStorage storage poolStorage = ubiquityPoolStorage(); + + // fetch Stable/ETH quote from chainlink (18 decimals) + AggregatorV3Interface stableEthPriceFeed = AggregatorV3Interface( + poolStorage.stableEthPriceFeedAddress + ); + ( + , + int256 stableEthAnswer, + , + uint256 stableEthUpdatedAt, + + ) = stableEthPriceFeed.latestRoundData(); + uint256 stableEthPriceFeedDecimals = stableEthPriceFeed.decimals(); + // validate Stable/ETH chainlink response + require(stableEthAnswer > 0, "Invalid Stable/ETH price"); + require( + block.timestamp - stableEthUpdatedAt < + poolStorage.stableEthPriceFeedStalenessThreshold, + "Stale Stable/ETH data" + ); + + // fetch ETH/USD quote from chainlink (8 decimals) + AggregatorV3Interface ethUsdPriceFeed = AggregatorV3Interface( + poolStorage.ethUsdPriceFeedAddress + ); + (, int256 ethUsdAnswer, , uint256 ethUsdUpdatedAt, ) = ethUsdPriceFeed + .latestRoundData(); + uint256 ethUsdPriceFeedDecimals = ethUsdPriceFeed.decimals(); + // validate ETH/USD chainlink response + require(ethUsdAnswer > 0, "Invalid ETH/USD price"); + require( + block.timestamp - ethUsdUpdatedAt < + poolStorage.ethUsdPriceFeedStalenessThreshold, + "Stale ETH/USD data" + ); + + // calculate Stable/USD price + uint256 stablePriceUsdD18 = uint256(stableEthAnswer) + .mul(uint256(ethUsdAnswer)) + .div(10 ** ethUsdPriceFeedDecimals); + + // fetch Dollar/Stable quote from Curve's plain pool (18 decimals) uint256 dollarPriceUsdD18 = ICurveStableSwapNG( - store.stableSwapMetaPoolAddress + store.stableSwapPlainPoolAddress ).price_oracle(0); + // convert to 6 decimals dollarPriceUsd = dollarPriceUsdD18 .mul(UBIQUITY_POOL_PRICE_PRECISION) + .mul(stablePriceUsdD18) + .div(10 ** stableEthPriceFeedDecimals) .div(1e18); } diff --git a/packages/contracts/test/diamond/facets/UbiquityPoolFacet.t.sol b/packages/contracts/test/diamond/facets/UbiquityPoolFacet.t.sol index c9e7d7bb5..002822d74 100644 --- a/packages/contracts/test/diamond/facets/UbiquityPoolFacet.t.sol +++ b/packages/contracts/test/diamond/facets/UbiquityPoolFacet.t.sol @@ -7,7 +7,7 @@ import {IDollarAmoMinter} from "../../../src/dollar/interfaces/IDollarAmoMinter. import {LibUbiquityPool} from "../../../src/dollar/libraries/LibUbiquityPool.sol"; import {MockChainLinkFeed} from "../../../src/dollar/mocks/MockChainLinkFeed.sol"; import {MockERC20} from "../../../src/dollar/mocks/MockERC20.sol"; -import {MockCurveStableSwapMetaNG} from "../../../src/dollar/mocks/MockCurveStableSwapMetaNG.sol"; +import {MockCurveStableSwapNG} from "../../../src/dollar/mocks/MockCurveStableSwapNG.sol"; import {MockCurveTwocryptoOptimized} from "../../../src/dollar/mocks/MockCurveTwocryptoOptimized.sol"; contract MockDollarAmoMinter is IDollarAmoMinter { @@ -24,9 +24,9 @@ contract UbiquityPoolFacetTest is DiamondTestSetup { MockDollarAmoMinter dollarAmoMinter; MockERC20 collateralToken; MockChainLinkFeed collateralTokenPriceFeed; - MockCurveStableSwapMetaNG curveDollarMetaPool; + MockCurveStableSwapNG curveDollarPlainPool; MockCurveTwocryptoOptimized curveGovernanceEthPool; - MockERC20 curveTriPoolLpToken; + MockERC20 stableToken; MockChainLinkFeed ethUsdPriceFeed; MockChainLinkFeed stableEthPriceFeed; MockERC20 wethToken; @@ -86,13 +86,13 @@ contract UbiquityPoolFacetTest is DiamondTestSetup { // init WETH token wethToken = new MockERC20("WETH", "WETH", 18); - // init Curve 3CRV-LP token - curveTriPoolLpToken = new MockERC20("3CRV", "3CRV", 18); + // init stable USD pegged token + stableToken = new MockERC20("STABLE", "STABLE", 18); - // init Curve Dollar-3CRV LP metapool - curveDollarMetaPool = new MockCurveStableSwapMetaNG( - address(dollarToken), - address(curveTriPoolLpToken) + // init Curve Stable-Dollar plain pool + curveDollarPlainPool = new MockCurveStableSwapNG( + address(stableToken), + address(dollarToken) ); // init Curve Governance-WETH crypto pool @@ -121,7 +121,7 @@ contract UbiquityPoolFacetTest is DiamondTestSetup { // set ETH/USD price feed mock params ethUsdPriceFeed.updateMockParams( 1, // round id - 3000_00000000, // answer, 3000_00000000 = $3000 (8 decimals) + 2000_00000000, // answer, 2000_00000000 = $2000 (8 decimals) block.timestamp, // started at block.timestamp, // updated at 1 // answered in round @@ -130,14 +130,15 @@ contract UbiquityPoolFacetTest is DiamondTestSetup { // set stable/ETH price feed mock params stableEthPriceFeed.updateMockParams( 1, // round id - 330000000000000, // answer, 330000000000000 = 0.00033 ETH (18 decimals) + 500000000000000, // answer, 500000000000000 = 0.0005 ETH (18 decimals) block.timestamp, // started at block.timestamp, // updated at 1 // answered in round ); + stableEthPriceFeed.updateDecimals(18); - // set ETH/Governance price to 30k in Curve pool mock - curveGovernanceEthPool.updateMockParams(30_000e18); + // set ETH/Governance price to 20k in Curve pool mock + curveGovernanceEthPool.updateMockParams(20_000e18); // set price feed for collateral token ubiquityPoolFacet.setCollateralChainLinkPriceFeed( @@ -182,8 +183,10 @@ contract UbiquityPoolFacetTest is DiamondTestSetup { // add AMO minter ubiquityPoolFacet.addAmoMinter(address(dollarAmoMinter)); - // set metapool in manager facet - managerFacet.setStableSwapMetaPoolAddress(address(curveDollarMetaPool)); + // set Curve plain pool in manager facet + managerFacet.setStableSwapPlainPoolAddress( + address(curveDollarPlainPool) + ); // stop being admin vm.stopPrank(); @@ -348,6 +351,85 @@ contract UbiquityPoolFacetTest is DiamondTestSetup { assertEq(amount, 100e18); } + function testGetDollarPriceUsd_ShouldRevertOnInvalidStableEthChainlinkAnswer() + public + { + // set invalid answer from chainlink + stableEthPriceFeed.updateMockParams( + 1, // round id + 0, // invalid answer + block.timestamp, // started at + block.timestamp, // updated at + 1 // answered in round + ); + + vm.expectRevert("Invalid Stable/ETH price"); + ubiquityPoolFacet.getDollarPriceUsd(); + } + + function testGetDollarPriceUsd_ShouldRevertStableEthChainlinkAnswerIsStale() + public + { + // set stale answer from chainlink + stableEthPriceFeed.updateMockParams( + 1, // round id + 100_000_000, // answer, 100_000_000 = $1.00 + block.timestamp, // started at + block.timestamp, // updated at + 1 // answered in round + ); + + // wait 1 day + vm.warp(block.timestamp + 1 days); + + vm.expectRevert("Stale Stable/ETH data"); + ubiquityPoolFacet.getDollarPriceUsd(); + } + + function testGetDollarPriceUsd_ShouldRevertOnInvalidEthUsdChainlinkAnswer() + public + { + // set invalid answer from chainlink + ethUsdPriceFeed.updateMockParams( + 1, // round id + 0, // invalid answer + block.timestamp, // started at + block.timestamp, // updated at + 1 // answered in round + ); + + vm.expectRevert("Invalid ETH/USD price"); + ubiquityPoolFacet.getDollarPriceUsd(); + } + + function testGetDollarPriceUsd_ShouldRevertEthUsdChainlinkAnswerIsStale() + public + { + // set Stable/ETH price to be up to date (1 day forward for ease of debugging) + stableEthPriceFeed.updateMockParams( + 1, // round id + 1, // answer + block.timestamp + 1 days, // started at + block.timestamp + 1 days, // updated at + 1 // answered in round + ); + + // set stale answer from chainlink + ethUsdPriceFeed.updateMockParams( + 1, // round id + 1, // answer + block.timestamp, // started at + block.timestamp, // updated at + 1 // answered in round + ); + + // wait 1 day + vm.warp(block.timestamp + 1 days); + + vm.expectRevert("Stale ETH/USD data"); + ubiquityPoolFacet.getDollarPriceUsd(); + } + function testGetDollarPriceUsd_ShouldReturnDollarPriceInUsd() public { uint256 dollarPriceUsd = ubiquityPoolFacet.getDollarPriceUsd(); assertEq(dollarPriceUsd, 1_000_000); @@ -392,9 +474,9 @@ contract UbiquityPoolFacetTest is DiamondTestSetup { public { uint256 governancePriceUsd = ubiquityPoolFacet.getGovernancePriceUsd(); - // 1 ETH = $3000, 1 ETH = 30_000 Governance tokens - // Governance token USD price = (1 / 30000) * 3000 = 0.1 - assertEq(governancePriceUsd, 99999); // ~$0.09 + // 1 ETH = $2000, 1 ETH = 20_000 Governance tokens + // Governance token USD price = (1 / 20000) * 2000 = 0.1 + assertEq(governancePriceUsd, 100000); // $0.1 } function testGetRedeemCollateralBalance_ShouldReturnRedeemCollateralBalance() @@ -466,7 +548,7 @@ contract UbiquityPoolFacetTest is DiamondTestSetup { assertEq( ubiquityPoolFacet.getRedeemGovernanceBalance(user), - 970209702097020970209 + 970200000000000000000 ); } @@ -710,7 +792,7 @@ contract UbiquityPoolFacetTest is DiamondTestSetup { assertEq(totalDollarMint, 99e18); assertEq(collateralNeeded, 0); - assertEq(governanceNeeded, 1000010000100001000010); // ~1000.01 = 100 Dollar * $0.1 Governance from oracle + assertEq(governanceNeeded, 1000000000000000000000); // 1000 = 100 Dollar * $0.1 Governance from oracle // balances after assertEq(collateralToken.balanceOf(address(ubiquityPoolFacet)), 0); @@ -750,7 +832,7 @@ contract UbiquityPoolFacetTest is DiamondTestSetup { assertEq(totalDollarMint, 99e18); assertEq(collateralNeeded, 95e18); - assertEq(governanceNeeded, 50000500005000050000); // ~50 Governance tokens = $5 USD / $0.1 Governance from oracle + assertEq(governanceNeeded, 50000000000000000000); // 50 Governance tokens = $5 USD / $0.1 Governance from oracle // balances after assertEq(collateralToken.balanceOf(address(ubiquityPoolFacet)), 95e18); @@ -935,7 +1017,7 @@ contract UbiquityPoolFacetTest is DiamondTestSetup { // balances before assertEq(dollarToken.balanceOf(user), 99e18); - assertEq(governanceToken.balanceOf(user), 999989999899998999990); + assertEq(governanceToken.balanceOf(user), 1000000000000000000000); assertEq(governanceToken.balanceOf(address(ubiquityPoolFacet)), 0); assertEq(ubiquityPoolFacet.getRedeemCollateralBalance(user, 0), 0); assertEq(ubiquityPoolFacet.getRedeemGovernanceBalance(user), 0); @@ -950,15 +1032,15 @@ contract UbiquityPoolFacetTest is DiamondTestSetup { // balances after assertEq(dollarToken.balanceOf(user), 0); - assertEq(governanceToken.balanceOf(user), 999989999899998999990); + assertEq(governanceToken.balanceOf(user), 1000000000000000000000); assertEq( governanceToken.balanceOf(address(ubiquityPoolFacet)), - 970209702097020970209 + 970200000000000000000 ); assertEq(ubiquityPoolFacet.getRedeemCollateralBalance(user, 0), 0); assertEq( ubiquityPoolFacet.getRedeemGovernanceBalance(user), - 970209702097020970209 + 970200000000000000000 ); } @@ -988,7 +1070,7 @@ contract UbiquityPoolFacetTest is DiamondTestSetup { // balances before assertEq(dollarToken.balanceOf(user), 99e18); - assertEq(governanceToken.balanceOf(user), 1949999499994999950000); // ~1950 + assertEq(governanceToken.balanceOf(user), 1950000000000000000000); // 1950 assertEq(governanceToken.balanceOf(address(ubiquityPoolFacet)), 0); assertEq(ubiquityPoolFacet.getRedeemCollateralBalance(user, 0), 0); assertEq(ubiquityPoolFacet.getRedeemGovernanceBalance(user), 0); @@ -1003,10 +1085,10 @@ contract UbiquityPoolFacetTest is DiamondTestSetup { // balances after assertEq(dollarToken.balanceOf(user), 0); - assertEq(governanceToken.balanceOf(user), 1949999499994999950000); // ~1950 + assertEq(governanceToken.balanceOf(user), 1950000000000000000000); // 1950 assertEq( governanceToken.balanceOf(address(ubiquityPoolFacet)), - 48510485104851048510 + 48510000000000000000 ); // ~48.5 assertEq( ubiquityPoolFacet.getRedeemCollateralBalance(user, 0), @@ -1014,7 +1096,7 @@ contract UbiquityPoolFacetTest is DiamondTestSetup { ); // ~92 assertEq( ubiquityPoolFacet.getRedeemGovernanceBalance(user), - 48510485104851048510 + 48510000000000000000 ); // ~48.5 } @@ -1075,14 +1157,14 @@ contract UbiquityPoolFacetTest is DiamondTestSetup { assertEq(collateralToken.balanceOf(user), 5e18); assertEq( governanceToken.balanceOf(address(ubiquityPoolFacet)), - 48510485104851048510 + 48510000000000000000 ); // ~48 - assertEq(governanceToken.balanceOf(user), 1949999499994999950000); // ~1950 + assertEq(governanceToken.balanceOf(user), 1950000000000000000000); // ~1950 vm.prank(user); (uint256 governanceAmount, uint256 collateralAmount) = ubiquityPoolFacet .collectRedemption(0); - assertEq(governanceAmount, 48510485104851048510); // ~48 + assertEq(governanceAmount, 48510000000000000000); // ~48 assertEq(collateralAmount, 92169000000000000000); // ~92 = $95 - 2% redemption fee // balances after @@ -1092,7 +1174,7 @@ contract UbiquityPoolFacetTest is DiamondTestSetup { ); // redemption fee left in the pool assertEq(collateralToken.balanceOf(user), 97.169 ether); assertEq(governanceToken.balanceOf(address(ubiquityPoolFacet)), 0); - assertEq(governanceToken.balanceOf(user), 1998509985099850998510); // ~1998 + assertEq(governanceToken.balanceOf(user), 1998510000000000000000); // ~1998 } function testCollectRedemption_ShouldRevert_IfCollateralDisabled() public { From beb449881626cd8555326772356544c516da188a Mon Sep 17 00:00:00 2001 From: rndquu Date: Wed, 24 Apr 2024 10:38:00 +0300 Subject: [PATCH 5/5] feat: add Stable/USD setter --- .../src/dollar/facets/UbiquityPoolFacet.sol | 8 +- .../src/dollar/interfaces/IUbiquityPool.sol | 13 +-- .../src/dollar/libraries/LibUbiquityPool.sol | 90 +++++++----------- .../diamond/facets/UbiquityPoolFacet.t.sol | 95 +++++-------------- 4 files changed, 71 insertions(+), 135 deletions(-) diff --git a/packages/contracts/src/dollar/facets/UbiquityPoolFacet.sol b/packages/contracts/src/dollar/facets/UbiquityPoolFacet.sol index 1359b99c4..b80320bc1 100644 --- a/packages/contracts/src/dollar/facets/UbiquityPoolFacet.sol +++ b/packages/contracts/src/dollar/facets/UbiquityPoolFacet.sol @@ -117,12 +117,12 @@ contract UbiquityPoolFacet is IUbiquityPool, Modifiers { } /// @inheritdoc IUbiquityPool - function stableEthPriceFeedInformation() + function stableUsdPriceFeedInformation() external view returns (address, uint256) { - return LibUbiquityPool.stableEthPriceFeedInformation(); + return LibUbiquityPool.stableUsdPriceFeedInformation(); } //==================== @@ -303,11 +303,11 @@ contract UbiquityPoolFacet is IUbiquityPool, Modifiers { } /// @inheritdoc IUbiquityPool - function setStableEthChainLinkPriceFeed( + function setStableUsdChainLinkPriceFeed( address newPriceFeedAddress, uint256 newStalenessThreshold ) external onlyAdmin { - LibUbiquityPool.setStableEthChainLinkPriceFeed( + LibUbiquityPool.setStableUsdChainLinkPriceFeed( newPriceFeedAddress, newStalenessThreshold ); diff --git a/packages/contracts/src/dollar/interfaces/IUbiquityPool.sol b/packages/contracts/src/dollar/interfaces/IUbiquityPool.sol index 23538b7f5..909ddc236 100644 --- a/packages/contracts/src/dollar/interfaces/IUbiquityPool.sol +++ b/packages/contracts/src/dollar/interfaces/IUbiquityPool.sol @@ -122,11 +122,11 @@ interface IUbiquityPool { function governanceEthPoolAddress() external view returns (address); /** - * @notice Returns chainlink price feed information for stable/ETH pair + * @notice Returns chainlink price feed information for stable/USD 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() + function stableUsdPriceFeedInformation() external view returns (address, uint256); @@ -339,11 +339,12 @@ interface IUbiquityPool { ) 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 + * @notice Sets chainlink params for stable/USD price feed + * @dev Here stable coin refers to the 1st coin in the Curve's stable/Dollar plain pool + * @param newPriceFeedAddress New chainlink price feed address for stable/USD pair + * @param newStalenessThreshold New threshold in seconds when chainlink's stable/USD price feed answer should be considered stale */ - function setStableEthChainLinkPriceFeed( + function setStableUsdChainLinkPriceFeed( address newPriceFeedAddress, uint256 newStalenessThreshold ) external; diff --git a/packages/contracts/src/dollar/libraries/LibUbiquityPool.sol b/packages/contracts/src/dollar/libraries/LibUbiquityPool.sol index 263716368..b3f8a5e51 100644 --- a/packages/contracts/src/dollar/libraries/LibUbiquityPool.sol +++ b/packages/contracts/src/dollar/libraries/LibUbiquityPool.sol @@ -106,10 +106,10 @@ library LibUbiquityPool { //================================ // 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; + // chainlink price feed for stable/USD pair + address stableUsdPriceFeedAddress; + // threshold in seconds when chainlink's stable/USD price feed answer should be considered stale + uint256 stableUsdPriceFeedStalenessThreshold; } /// @notice Struct used for detailed collateral information @@ -189,8 +189,8 @@ 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( + /// @notice Emitted on setting chainlink's price feed for stable/USD pair + event StableUsdPriceFeedSet( address newPriceFeedAddress, uint256 newStalenessThreshold ); @@ -376,10 +376,9 @@ library LibUbiquityPool { /** * @notice Returns Ubiquity Dollar token USD price (1e6 precision) from Curve plain pool (Stable coin, Ubiquity Dollar) * How it works: - * 1. Fetch Stable/ETH quote from chainlink - * 2. Fetch ETH/USD quote from chainlink - * 3. Fetch Dollar/Stable quote from Curve's plain pool - * 4. Calculate Dollar token price in USD + * 1. Fetch Stable/USD quote from chainlink + * 2. Fetch Dollar/Stable quote from Curve's plain pool + * 3. Calculate Dollar token price in USD * @return dollarPriceUsd USD price of Ubiquity Dollar */ function getDollarPriceUsd() @@ -390,46 +389,26 @@ library LibUbiquityPool { AppStorage storage store = LibAppStorage.appStorage(); UbiquityPoolStorage storage poolStorage = ubiquityPoolStorage(); - // fetch Stable/ETH quote from chainlink (18 decimals) - AggregatorV3Interface stableEthPriceFeed = AggregatorV3Interface( - poolStorage.stableEthPriceFeedAddress + // fetch Stable/USD quote from chainlink (8 decimals) + AggregatorV3Interface stableUsdPriceFeed = AggregatorV3Interface( + poolStorage.stableUsdPriceFeedAddress ); ( , - int256 stableEthAnswer, + int256 stableUsdAnswer, , - uint256 stableEthUpdatedAt, + uint256 stableUsdUpdatedAt, - ) = stableEthPriceFeed.latestRoundData(); - uint256 stableEthPriceFeedDecimals = stableEthPriceFeed.decimals(); - // validate Stable/ETH chainlink response - require(stableEthAnswer > 0, "Invalid Stable/ETH price"); + ) = stableUsdPriceFeed.latestRoundData(); + uint256 stableUsdPriceFeedDecimals = stableUsdPriceFeed.decimals(); + // validate Stable/USD chainlink response + require(stableUsdAnswer > 0, "Invalid Stable/USD price"); require( - block.timestamp - stableEthUpdatedAt < - poolStorage.stableEthPriceFeedStalenessThreshold, - "Stale Stable/ETH data" + block.timestamp - stableUsdUpdatedAt < + poolStorage.stableUsdPriceFeedStalenessThreshold, + "Stale Stable/USD data" ); - // fetch ETH/USD quote from chainlink (8 decimals) - AggregatorV3Interface ethUsdPriceFeed = AggregatorV3Interface( - poolStorage.ethUsdPriceFeedAddress - ); - (, int256 ethUsdAnswer, , uint256 ethUsdUpdatedAt, ) = ethUsdPriceFeed - .latestRoundData(); - uint256 ethUsdPriceFeedDecimals = ethUsdPriceFeed.decimals(); - // validate ETH/USD chainlink response - require(ethUsdAnswer > 0, "Invalid ETH/USD price"); - require( - block.timestamp - ethUsdUpdatedAt < - poolStorage.ethUsdPriceFeedStalenessThreshold, - "Stale ETH/USD data" - ); - - // calculate Stable/USD price - uint256 stablePriceUsdD18 = uint256(stableEthAnswer) - .mul(uint256(ethUsdAnswer)) - .div(10 ** ethUsdPriceFeedDecimals); - // fetch Dollar/Stable quote from Curve's plain pool (18 decimals) uint256 dollarPriceUsdD18 = ICurveStableSwapNG( store.stableSwapPlainPoolAddress @@ -438,8 +417,8 @@ library LibUbiquityPool { // convert to 6 decimals dollarPriceUsd = dollarPriceUsdD18 .mul(UBIQUITY_POOL_PRICE_PRECISION) - .mul(stablePriceUsdD18) - .div(10 ** stableEthPriceFeedDecimals) + .mul(uint256(stableUsdAnswer)) + .div(10 ** stableUsdPriceFeedDecimals) .div(1e18); } @@ -529,19 +508,19 @@ library LibUbiquityPool { } /** - * @notice Returns chainlink price feed information for stable/ETH pair + * @notice Returns chainlink price feed information for stable/USD 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() + function stableUsdPriceFeedInformation() internal view returns (address, uint256) { UbiquityPoolStorage storage poolStorage = ubiquityPoolStorage(); return ( - poolStorage.stableEthPriceFeedAddress, - poolStorage.stableEthPriceFeedStalenessThreshold + poolStorage.stableUsdPriceFeedAddress, + poolStorage.stableUsdPriceFeedStalenessThreshold ); } @@ -1203,21 +1182,22 @@ library LibUbiquityPool { } /** - * @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 + * @notice Sets chainlink params for stable/USD price feed + * @dev Here stable coin refers to the 1st coin in the Curve's stable/Dollar plain pool + * @param newPriceFeedAddress New chainlink price feed address for stable/USD pair + * @param newStalenessThreshold New threshold in seconds when chainlink's stable/USD price feed answer should be considered stale */ - function setStableEthChainLinkPriceFeed( + function setStableUsdChainLinkPriceFeed( address newPriceFeedAddress, uint256 newStalenessThreshold ) internal { UbiquityPoolStorage storage poolStorage = ubiquityPoolStorage(); - poolStorage.stableEthPriceFeedAddress = newPriceFeedAddress; + poolStorage.stableUsdPriceFeedAddress = newPriceFeedAddress; poolStorage - .stableEthPriceFeedStalenessThreshold = newStalenessThreshold; + .stableUsdPriceFeedStalenessThreshold = newStalenessThreshold; - emit StableEthPriceFeedSet(newPriceFeedAddress, newStalenessThreshold); + emit StableUsdPriceFeedSet(newPriceFeedAddress, newStalenessThreshold); } /** diff --git a/packages/contracts/test/diamond/facets/UbiquityPoolFacet.t.sol b/packages/contracts/test/diamond/facets/UbiquityPoolFacet.t.sol index 002822d74..afc0b2723 100644 --- a/packages/contracts/test/diamond/facets/UbiquityPoolFacet.t.sol +++ b/packages/contracts/test/diamond/facets/UbiquityPoolFacet.t.sol @@ -28,7 +28,7 @@ contract UbiquityPoolFacetTest is DiamondTestSetup { MockCurveTwocryptoOptimized curveGovernanceEthPool; MockERC20 stableToken; MockChainLinkFeed ethUsdPriceFeed; - MockChainLinkFeed stableEthPriceFeed; + MockChainLinkFeed stableUsdPriceFeed; MockERC20 wethToken; address user = address(1); @@ -61,7 +61,7 @@ contract UbiquityPoolFacetTest is DiamondTestSetup { uint256 newRedeemPriceThreshold ); event RedemptionDelayBlocksSet(uint256 redemptionDelayBlocks); - event StableEthPriceFeedSet( + event StableUsdPriceFeedSet( address newPriceFeedAddress, uint256 newStalenessThreshold ); @@ -80,8 +80,8 @@ contract UbiquityPoolFacetTest is DiamondTestSetup { // init ETH/USD price feed ethUsdPriceFeed = new MockChainLinkFeed(); - // init stable/ETH price feed - stableEthPriceFeed = new MockChainLinkFeed(); + // init stable/USD price feed + stableUsdPriceFeed = new MockChainLinkFeed(); // init WETH token wethToken = new MockERC20("WETH", "WETH", 18); @@ -127,15 +127,14 @@ contract UbiquityPoolFacetTest is DiamondTestSetup { 1 // answered in round ); - // set stable/ETH price feed mock params - stableEthPriceFeed.updateMockParams( + // set stable/USD price feed mock params + stableUsdPriceFeed.updateMockParams( 1, // round id - 500000000000000, // answer, 500000000000000 = 0.0005 ETH (18 decimals) + 100_000_000, // answer, 100_000_000 = $1.00 (8 decimals) block.timestamp, // started at block.timestamp, // updated at 1 // answered in round ); - stableEthPriceFeed.updateDecimals(18); // set ETH/Governance price to 20k in Curve pool mock curveGovernanceEthPool.updateMockParams(20_000e18); @@ -153,9 +152,9 @@ 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 + // set price feed for stable/USD pair + ubiquityPoolFacet.setStableUsdChainLinkPriceFeed( + address(stableUsdPriceFeed), // price feed address 1 days // price feed staleness threshold in seconds ); @@ -351,11 +350,11 @@ contract UbiquityPoolFacetTest is DiamondTestSetup { assertEq(amount, 100e18); } - function testGetDollarPriceUsd_ShouldRevertOnInvalidStableEthChainlinkAnswer() + function testGetDollarPriceUsd_ShouldRevertOnInvalidStableUsdChainlinkAnswer() public { // set invalid answer from chainlink - stableEthPriceFeed.updateMockParams( + stableUsdPriceFeed.updateMockParams( 1, // round id 0, // invalid answer block.timestamp, // started at @@ -363,15 +362,15 @@ contract UbiquityPoolFacetTest is DiamondTestSetup { 1 // answered in round ); - vm.expectRevert("Invalid Stable/ETH price"); + vm.expectRevert("Invalid Stable/USD price"); ubiquityPoolFacet.getDollarPriceUsd(); } - function testGetDollarPriceUsd_ShouldRevertStableEthChainlinkAnswerIsStale() + function testGetDollarPriceUsd_ShouldRevertIfStableUsdChainlinkAnswerIsStale() public { // set stale answer from chainlink - stableEthPriceFeed.updateMockParams( + stableUsdPriceFeed.updateMockParams( 1, // round id 100_000_000, // answer, 100_000_000 = $1.00 block.timestamp, // started at @@ -382,51 +381,7 @@ contract UbiquityPoolFacetTest is DiamondTestSetup { // wait 1 day vm.warp(block.timestamp + 1 days); - vm.expectRevert("Stale Stable/ETH data"); - ubiquityPoolFacet.getDollarPriceUsd(); - } - - function testGetDollarPriceUsd_ShouldRevertOnInvalidEthUsdChainlinkAnswer() - public - { - // set invalid answer from chainlink - ethUsdPriceFeed.updateMockParams( - 1, // round id - 0, // invalid answer - block.timestamp, // started at - block.timestamp, // updated at - 1 // answered in round - ); - - vm.expectRevert("Invalid ETH/USD price"); - ubiquityPoolFacet.getDollarPriceUsd(); - } - - function testGetDollarPriceUsd_ShouldRevertEthUsdChainlinkAnswerIsStale() - public - { - // set Stable/ETH price to be up to date (1 day forward for ease of debugging) - stableEthPriceFeed.updateMockParams( - 1, // round id - 1, // answer - block.timestamp + 1 days, // started at - block.timestamp + 1 days, // updated at - 1 // answered in round - ); - - // set stale answer from chainlink - ethUsdPriceFeed.updateMockParams( - 1, // round id - 1, // answer - block.timestamp, // started at - block.timestamp, // updated at - 1 // answered in round - ); - - // wait 1 day - vm.warp(block.timestamp + 1 days); - - vm.expectRevert("Stale ETH/USD data"); + vm.expectRevert("Stale Stable/USD data"); ubiquityPoolFacet.getDollarPriceUsd(); } @@ -560,12 +515,12 @@ contract UbiquityPoolFacetTest is DiamondTestSetup { assertEq(governanceEthPoolAddress, address(curveGovernanceEthPool)); } - function testStableEthPriceFeedInformation_ShouldReturnStableEthPriceFeedInformation() + function testStableUsdPriceFeedInformation_ShouldReturnStableUsdPriceFeedInformation() public { (address priceFeed, uint256 stalenessThreshold) = ubiquityPoolFacet - .stableEthPriceFeedInformation(); - assertEq(priceFeed, address(stableEthPriceFeed)); + .stableUsdPriceFeedInformation(); + assertEq(priceFeed, address(stableUsdPriceFeed)); assertEq(stalenessThreshold, 1 days); } @@ -1623,7 +1578,7 @@ contract UbiquityPoolFacetTest is DiamondTestSetup { vm.stopPrank(); } - function testSetStableEthChainLinkPriceFeed_ShouldSetStableEthChainLinkPriceFeed() + function testSetStableUsdChainLinkPriceFeed_ShouldSetStableUsdChainLinkPriceFeed() public { vm.startPrank(admin); @@ -1631,15 +1586,15 @@ contract UbiquityPoolFacetTest is DiamondTestSetup { ( address oldPriceFeedAddress, uint256 oldStalenessThreshold - ) = ubiquityPoolFacet.stableEthPriceFeedInformation(); - assertEq(oldPriceFeedAddress, address(stableEthPriceFeed)); + ) = ubiquityPoolFacet.stableUsdPriceFeedInformation(); + assertEq(oldPriceFeedAddress, address(stableUsdPriceFeed)); assertEq(oldStalenessThreshold, 1 days); address newPriceFeedAddress = address(1); uint256 newStalenessThreshold = 2 days; vm.expectEmit(address(ubiquityPoolFacet)); - emit StableEthPriceFeedSet(newPriceFeedAddress, newStalenessThreshold); - ubiquityPoolFacet.setStableEthChainLinkPriceFeed( + emit StableUsdPriceFeedSet(newPriceFeedAddress, newStalenessThreshold); + ubiquityPoolFacet.setStableUsdChainLinkPriceFeed( newPriceFeedAddress, newStalenessThreshold ); @@ -1647,7 +1602,7 @@ contract UbiquityPoolFacetTest is DiamondTestSetup { ( address updatedPriceFeedAddress, uint256 updatedStalenessThreshold - ) = ubiquityPoolFacet.stableEthPriceFeedInformation(); + ) = ubiquityPoolFacet.stableUsdPriceFeedInformation(); assertEq(updatedPriceFeedAddress, newPriceFeedAddress); assertEq(updatedStalenessThreshold, newStalenessThreshold);