diff --git a/packages/contracts/migrations/development/Deploy001_Diamond_Dollar.s.sol b/packages/contracts/migrations/development/Deploy001_Diamond_Dollar.s.sol index 5a3caabd3..23023643d 100644 --- a/packages/contracts/migrations/development/Deploy001_Diamond_Dollar.s.sol +++ b/packages/contracts/migrations/development/Deploy001_Diamond_Dollar.s.sol @@ -13,19 +13,18 @@ import {DiamondCutFacet} from "../../src/dollar/facets/DiamondCutFacet.sol"; import {DiamondLoupeFacet} from "../../src/dollar/facets/DiamondLoupeFacet.sol"; import {ManagerFacet} from "../../src/dollar/facets/ManagerFacet.sol"; import {OwnershipFacet} from "../../src/dollar/facets/OwnershipFacet.sol"; -import {TWAPOracleDollar3poolFacet} from "../../src/dollar/facets/TWAPOracleDollar3poolFacet.sol"; import {UbiquityPoolFacet} from "../../src/dollar/facets/UbiquityPoolFacet.sol"; +import {ICurveStableSwapMetaNG} from "../../src/dollar/interfaces/ICurveStableSwapMetaNG.sol"; import {IDiamondCut} from "../../src/dollar/interfaces/IDiamondCut.sol"; import {IDiamondLoupe} from "../../src/dollar/interfaces/IDiamondLoupe.sol"; import {IERC173} from "../../src/dollar/interfaces/IERC173.sol"; -import {IMetaPool} from "../../src/dollar/interfaces/IMetaPool.sol"; import {DEFAULT_ADMIN_ROLE, DOLLAR_TOKEN_MINTER_ROLE, DOLLAR_TOKEN_BURNER_ROLE, PAUSER_ROLE} from "../../src/dollar/libraries/Constants.sol"; import {LibAccessControl} from "../../src/dollar/libraries/LibAccessControl.sol"; import {AppStorage, LibAppStorage, Modifiers} from "../../src/dollar/libraries/LibAppStorage.sol"; import {LibDiamond} from "../../src/dollar/libraries/LibDiamond.sol"; import {MockChainLinkFeed} from "../../src/dollar/mocks/MockChainLinkFeed.sol"; +import {MockCurveStableSwapMetaNG} from "../../src/dollar/mocks/MockCurveStableSwapMetaNG.sol"; import {MockERC20} from "../../src/dollar/mocks/MockERC20.sol"; -import {MockMetaPool} from "../../src/dollar/mocks/MockMetaPool.sol"; import {DiamondTestHelper} from "../../test/helpers/DiamondTestHelper.sol"; /** @@ -117,13 +116,12 @@ contract Deploy001_Diamond_Dollar is Script, DiamondTestHelper { DiamondLoupeFacet diamondLoupeFacetImplementation; ManagerFacet managerFacetImplementation; OwnershipFacet ownershipFacetImplementation; - TWAPOracleDollar3poolFacet twapOracleDollar3PoolFacetImplementation; UbiquityPoolFacet ubiquityPoolFacetImplementation; // oracle related contracts AggregatorV3Interface chainLinkPriceFeedLusd; // chainlink LUSD/USD price feed IERC20 curveTriPoolLpToken; // Curve's 3CRV-LP token - IMetaPool curveDollarMetaPool; // Curve's Dollar-3CRVLP metapool + ICurveStableSwapMetaNG curveDollarMetaPool; // Curve's Dollar-3CRVLP metapool // selectors for all of the facets bytes4[] selectorsOfAccessControlFacet; @@ -131,7 +129,6 @@ contract Deploy001_Diamond_Dollar is Script, DiamondTestHelper { bytes4[] selectorsOfDiamondLoupeFacet; bytes4[] selectorsOfManagerFacet; bytes4[] selectorsOfOwnershipFacet; - bytes4[] selectorsOfTWAPOracleDollar3poolFacet; bytes4[] selectorsOfUbiquityPoolFacet; function run() public virtual { @@ -166,9 +163,6 @@ contract Deploy001_Diamond_Dollar is Script, DiamondTestHelper { selectorsOfOwnershipFacet = getSelectorsFromAbi( "/out/OwnershipFacet.sol/OwnershipFacet.json" ); - selectorsOfTWAPOracleDollar3poolFacet = getSelectorsFromAbi( - "/out/TWAPOracleDollar3poolFacet.sol/TWAPOracleDollar3poolFacet.json" - ); selectorsOfUbiquityPoolFacet = getSelectorsFromAbi( "/out/UbiquityPoolFacet.sol/UbiquityPoolFacet.json" ); @@ -179,7 +173,6 @@ contract Deploy001_Diamond_Dollar is Script, DiamondTestHelper { diamondLoupeFacetImplementation = new DiamondLoupeFacet(); managerFacetImplementation = new ManagerFacet(); ownershipFacetImplementation = new OwnershipFacet(); - twapOracleDollar3PoolFacetImplementation = new TWAPOracleDollar3poolFacet(); ubiquityPoolFacetImplementation = new UbiquityPoolFacet(); // prepare DiamondInit args @@ -198,7 +191,7 @@ contract Deploy001_Diamond_Dollar is Script, DiamondTestHelper { }); // prepare facet cuts - FacetCut[] memory cuts = new FacetCut[](7); + FacetCut[] memory cuts = new FacetCut[](6); cuts[0] = ( FacetCut({ facetAddress: address(accessControlFacetImplementation), @@ -235,13 +228,6 @@ contract Deploy001_Diamond_Dollar is Script, DiamondTestHelper { }) ); cuts[5] = ( - FacetCut({ - facetAddress: address(twapOracleDollar3PoolFacetImplementation), - action: FacetCutAction.Add, - functionSelectors: selectorsOfTWAPOracleDollar3poolFacet - }) - ); - cuts[6] = ( FacetCut({ facetAddress: address(ubiquityPoolFacetImplementation), action: FacetCutAction.Add, @@ -438,7 +424,7 @@ contract Deploy001_Diamond_Dollar is Script, DiamondTestHelper { ); // deploy mock Curve's Dollar-3CRVLP metapool - curveDollarMetaPool = new MockMetaPool( + curveDollarMetaPool = new MockCurveStableSwapMetaNG( address(dollarToken), address(curveTriPoolLpToken) ); @@ -450,23 +436,15 @@ contract Deploy001_Diamond_Dollar is Script, DiamondTestHelper { // Curve's Dollar-3CRVLP metapool setup //======================================== - // start sending owner transactions - vm.startBroadcast(ownerPrivateKey); - - TWAPOracleDollar3poolFacet twapOracleDollar3PoolFacet = TWAPOracleDollar3poolFacet( - address(diamond) - ); + // start sending admin transactions + vm.startBroadcast(adminPrivateKey); - // set Curve Dollar-3CRVLP pool in the diamond storage - twapOracleDollar3PoolFacet.setPool( - address(curveDollarMetaPool), - address(curveTriPoolLpToken) - ); + ManagerFacet managerFacet = ManagerFacet(address(diamond)); - // fetch latest Dollar price from Curve's Dollar-3CRVLP metapool - twapOracleDollar3PoolFacet.update(); + // set curve's metapool in manager facet + managerFacet.setStableSwapMetaPoolAddress(address(curveDollarMetaPool)); - // stop sending owner transactions + // stop sending admin transactions vm.stopBroadcast(); } } diff --git a/packages/contracts/migrations/mainnet/Deploy001_Diamond_Dollar.s.sol b/packages/contracts/migrations/mainnet/Deploy001_Diamond_Dollar.s.sol index 30d0e261c..950573cc8 100644 --- a/packages/contracts/migrations/mainnet/Deploy001_Diamond_Dollar.s.sol +++ b/packages/contracts/migrations/mainnet/Deploy001_Diamond_Dollar.s.sol @@ -4,9 +4,9 @@ pragma solidity 0.8.19; import {AggregatorV3Interface} from "@chainlink/interfaces/AggregatorV3Interface.sol"; import {IERC20} from "@openzeppelin/contracts/interfaces/IERC20.sol"; import {Deploy001_Diamond_Dollar as Deploy001_Diamond_Dollar_Development} from "../development/Deploy001_Diamond_Dollar.s.sol"; -import {TWAPOracleDollar3poolFacet} from "../../src/dollar/facets/TWAPOracleDollar3poolFacet.sol"; +import {ManagerFacet} from "../../src/dollar/facets/ManagerFacet.sol"; import {UbiquityPoolFacet} from "../../src/dollar/facets/UbiquityPoolFacet.sol"; -import {IMetaPool} from "../../src/dollar/interfaces/IMetaPool.sol"; +import {ICurveStableSwapMetaNG} from "../../src/dollar/interfaces/ICurveStableSwapMetaNG.sol"; /// @notice Migration contract contract Deploy001_Diamond_Dollar is Deploy001_Diamond_Dollar_Development { @@ -82,31 +82,22 @@ contract Deploy001_Diamond_Dollar is Deploy001_Diamond_Dollar_Development { // Curve's Dollar-3CRVLP metapool setup //======================================== - // start sending owner transactions - vm.startBroadcast(ownerPrivateKey); + // start sending admin transactions + vm.startBroadcast(adminPrivateKey); // init 3CRV token curveTriPoolLpToken = IERC20(token3CrvAddress); // init Dollar-3CRVLP Curve metapool - curveDollarMetaPool = IMetaPool(curveDollarMetapoolAddress); - - /* - TODO: uncomment when we redeploy Curve's Dollar-3CRV metapool with the new Dollar token - - TWAPOracleDollar3poolFacet twapOracleDollar3PoolFacet = TWAPOracleDollar3poolFacet(address(diamond)); - - // set Curve Dollar-3CRVLP pool in the diamond storage - twapOracleDollar3PoolFacet.setPool( - address(curveDollarMetaPool), - address(curveTriPoolLpToken) + curveDollarMetaPool = ICurveStableSwapMetaNG( + curveDollarMetapoolAddress ); - // fetch latest Dollar price from Curve's Dollar-3CRVLP metapool - twapOracleDollar3PoolFacet.update(); - */ + // set curve's metapool in manager facet + ManagerFacet managerFacet = ManagerFacet(address(diamond)); + managerFacet.setStableSwapMetaPoolAddress(address(curveDollarMetaPool)); - // stop sending owner transactions + // stop sending admin transactions vm.stopBroadcast(); } } diff --git a/packages/contracts/src/deprecated/TWAPOracle.sol b/packages/contracts/src/deprecated/TWAPOracle.sol index 5655c168d..32e2ce58b 100644 --- a/packages/contracts/src/deprecated/TWAPOracle.sol +++ b/packages/contracts/src/deprecated/TWAPOracle.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.3; -import "../dollar/interfaces/IMetaPool.sol"; +import "./interfaces/IMetaPool.sol"; contract TWAPOracle { address public immutable pool; diff --git a/packages/contracts/src/deprecated/UbiquityAlgorithmicDollarManager.sol b/packages/contracts/src/deprecated/UbiquityAlgorithmicDollarManager.sol index 82452ee71..32258faf6 100644 --- a/packages/contracts/src/deprecated/UbiquityAlgorithmicDollarManager.sol +++ b/packages/contracts/src/deprecated/UbiquityAlgorithmicDollarManager.sol @@ -6,9 +6,9 @@ import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; +import "./interfaces/IMetaPool.sol"; import "./interfaces/IUbiquityAlgorithmicDollar.sol"; import "../dollar/interfaces/ICurveFactory.sol"; -import "../dollar/interfaces/IMetaPool.sol"; import "./TWAPOracle.sol"; diff --git a/packages/contracts/src/dollar/interfaces/IMetaPool.sol b/packages/contracts/src/deprecated/interfaces/IMetaPool.sol similarity index 100% rename from packages/contracts/src/dollar/interfaces/IMetaPool.sol rename to packages/contracts/src/deprecated/interfaces/IMetaPool.sol diff --git a/packages/contracts/src/dollar/facets/ManagerFacet.sol b/packages/contracts/src/dollar/facets/ManagerFacet.sol index d71843a2e..2e7611cc8 100644 --- a/packages/contracts/src/dollar/facets/ManagerFacet.sol +++ b/packages/contracts/src/dollar/facets/ManagerFacet.sol @@ -7,7 +7,7 @@ import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "../../dollar/interfaces/IUbiquityDollarToken.sol"; import "../../dollar/interfaces/ICurveFactory.sol"; -import "../../dollar/interfaces/IMetaPool.sol"; +import "../../dollar/interfaces/ICurveStableSwapMetaNG.sol"; import "../libraries/LibAccessControl.sol"; /** @@ -246,8 +246,10 @@ contract ManagerFacet is Modifiers { // coin at index 0 is Dollar and index 1 is 3CRV require( - IMetaPool(metaPool).coins(0) == store.dollarTokenAddress && - IMetaPool(metaPool).coins(1) == _crv3PoolTokenAddress, + ICurveStableSwapMetaNG(metaPool).coins(0) == + store.dollarTokenAddress && + ICurveStableSwapMetaNG(metaPool).coins(1) == + _crv3PoolTokenAddress, "MGR: COIN_ORDER_MISMATCH" ); // Add the initial liquidity to the StableSwap meta pool @@ -257,7 +259,7 @@ contract ManagerFacet is Modifiers { ]; // set curve 3Pool address store.curve3PoolTokenAddress = _crv3PoolTokenAddress; - IMetaPool(metaPool).add_liquidity(amounts, 0, msg.sender); + ICurveStableSwapMetaNG(metaPool).add_liquidity(amounts, 0, msg.sender); } /** diff --git a/packages/contracts/src/dollar/facets/TWAPOracleDollar3poolFacet.sol b/packages/contracts/src/dollar/facets/TWAPOracleDollar3poolFacet.sol deleted file mode 100644 index 193d6b5d6..000000000 --- a/packages/contracts/src/dollar/facets/TWAPOracleDollar3poolFacet.sol +++ /dev/null @@ -1,47 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity 0.8.19; - -import {LibTWAPOracle} from "../libraries/LibTWAPOracle.sol"; -import {Modifiers} from "../libraries/LibAppStorage.sol"; - -import {ITWAPOracleDollar3pool} from "../../dollar/interfaces/ITWAPOracleDollar3pool.sol"; - -/** - * @notice Facet used for Curve TWAP oracle in the Dollar MetaPool - */ -contract TWAPOracleDollar3poolFacet is Modifiers, ITWAPOracleDollar3pool { - /** - * @notice Sets Curve MetaPool to be used as a TWAP oracle - * @param _pool Curve MetaPool address, pool for 2 tokens [Dollar, 3CRV LP] - * @param _curve3CRVToken1 Curve 3Pool LP token address - */ - function setPool( - address _pool, - address _curve3CRVToken1 - ) external onlyOwner { - return LibTWAPOracle.setPool(_pool, _curve3CRVToken1); - } - - /** - * @notice Updates the following state variables to the latest values from MetaPool: - * - Dollar / 3CRV LP quote - * - 3CRV LP / Dollar quote - * - cumulative prices - * - update timestamp - */ - function update() external { - LibTWAPOracle.update(); - } - - /** - * @notice Returns the quote for the provided `token` address - * @notice If the `token` param is Dollar then returns 3CRV LP / Dollar quote - * @notice If the `token` param is 3CRV LP then returns Dollar / 3CRV LP quote - * @dev This will always return 0 before update has been called successfully for the first time - * @param token Token address - * @return amountOut Token price, Dollar / 3CRV LP or 3CRV LP / Dollar quote - */ - function consult(address token) external view returns (uint256 amountOut) { - return LibTWAPOracle.consult(token); - } -} diff --git a/packages/contracts/src/dollar/interfaces/ICurveStableSwapMetaNG.sol b/packages/contracts/src/dollar/interfaces/ICurveStableSwapMetaNG.sol new file mode 100644 index 000000000..5a9864457 --- /dev/null +++ b/packages/contracts/src/dollar/interfaces/ICurveStableSwapMetaNG.sol @@ -0,0 +1,99 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.19; + +import {IERC20} from "@openzeppelin/contracts/interfaces/IERC20.sol"; + +/** + * @notice Curve MetaPool interface + * + * @notice **What is Curve MetaPool** + * @notice The pool that consists of 2 tokens: stable coin and 3CRV LP token. + * For example the pool may contain Ubiquity Dollar and 3CRV LP token. + * This allows users to trade between Ubiquity Dollar and any of the tokens + * from the Curve 3Pool (DAI, USDC, USDT). When user adds liquidity to the pool + * then he is rewarded with MetaPool LP tokens. 1 Dollar3CRV LP token != 1 stable coin token. + * @notice Add liquidity example: + * 1. User sends 100 Ubiquity Dollars to the pool + * 2. User gets 100 Dollar3CRV LP tokens of the pool + * @notice Remove liquidity example: + * 1. User sends 100 Dollar3CRV LP tokens to the pool + * 2. User gets 100 Dollar/DAI/USDC/USDT (may choose any) tokens + * + * @dev Source: https://github.com/curvefi/stableswap-ng/blob/bff1522b30819b7b240af17ccfb72b0effbf6c47/contracts/main/CurveStableSwapMetaNG.vy + * @dev Docs: https://docs.curve.fi/stableswap-exchange/stableswap-ng/pools/metapool/ + */ +interface ICurveStableSwapMetaNG is IERC20 { + /** + * @notice Deposits coins into to the pool and mints new LP tokens + * @param _amounts List of amounts of underlying coins to deposit. + * Amounts correspond to the tokens at the same index locations within `coins`. + * @param _min_mint_amount Minimum amount of LP tokens to mint from the deposit + * @param _receiver Optional address that receives the LP tokens. If not specified, they are sent to the caller. + * @return The amount of LP tokens that were minted in the deposit + */ + function add_liquidity( + uint256[2] memory _amounts, + uint256 _min_mint_amount, + address _receiver + ) external returns (uint256); + + /** + * @notice Estimates the amount of LP tokens minted or burned based on a deposit or withdrawal + * + * @notice This calculation accounts for slippage, but not fees. It should be used as a basis for + * determining expected amounts when calling `add_liquidity()` or `remove_liquidity_imbalance()`, + * but should not be considered to be precise! + * + * @param _amounts Amount of each coin being deposited. Amounts correspond to the tokens at the + * same index locations within `coins()`. + * @param _is_deposit Set `True` for deposits, `False` for withdrawals + * @return The expected amount of LP tokens minted or burned + */ + function calc_token_amount( + uint256[2] memory _amounts, + bool _is_deposit + ) external view returns (uint256); + + /** + * @notice Returns token address by the provided `arg0` index + * @param arg0 Token index + * @return Token address + */ + function coins(uint256 arg0) external view returns (address); + + /** + * @notice Performs an exchange between two tokens. Index values can be found + * using the `coins()` public getter method, or `get_coins()` within the factory contract. + * @param i Index value of the token to send + * @param j Index value of the token to receive + * @param dx The amount of `i` being exchanged + * @param min_dy The minimum amount of `j` to receive. If the swap would result in less, the transaction will revert. + * @return The amount of `j` received in the exchange + */ + function exchange( + int128 i, + int128 j, + uint256 dx, + uint256 min_dy + ) external returns (uint256); + + /** + * @notice Function to calculate the exponential moving average (ema) price for the coin at index value `i` + * @param i Index value of coin + * @return Price oracle + */ + function price_oracle(uint256 i) external view returns (uint256); + + /** + * @notice Withdraws a single asset from the pool + * @param _burn_amount Amount of LP tokens to burn in the withdrawal + * @param i Index value of the coin to withdraw. Can be found using the `coins()` getter method. + * @param _min_received Minimum amount of the coin to receive + * @return The amount of the coin received in the withdrawal + */ + function remove_liquidity_one_coin( + uint256 _burn_amount, + int128 i, + uint256 _min_received + ) external returns (uint256); +} diff --git a/packages/contracts/src/dollar/interfaces/ITWAPOracleDollar3pool.sol b/packages/contracts/src/dollar/interfaces/ITWAPOracleDollar3pool.sol deleted file mode 100644 index 6cb84c025..000000000 --- a/packages/contracts/src/dollar/interfaces/ITWAPOracleDollar3pool.sol +++ /dev/null @@ -1,51 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity 0.8.19; - -/** - * @notice TWAP oracle interface for Curve MetaPool - * - * @notice **What is Curve 3Pool** - * @notice The pool that consists of 3 tokens: DAI, USDC, USDT. - * Users are free to trade (swap) those tokens. When user adds liquidity - * to the pool then he is rewarded with the pool's LP token 3CRV. - * 1 3CRV LP token != 1 stable coin token. - * @notice Add liquidity example: - * 1. User sends 5 USDC to the pool - * 2. User gets 5 3CRV LP tokens - * @notice Remove liquidity example: - * 1. User sends 99 3CRV LP tokens - * 2. User gets 99 USDT tokens - * - * @notice **What is Curve MetaPool** - * @notice The pool that consists of 2 tokens: stable coin and 3CRV LP token. - * For example the pool may contain Ubiquity Dollar and 3CRV LP token. - * This allows users to trade between Ubiquity Dollar and any of the tokens - * from the Curve 3Pool (DAI, USDC, USDT). When user adds liquidity to the pool - * then he is rewarded with MetaPool LP tokens. 1 Dollar3CRV LP token != 1 stable coin token. - * @notice Add liquidity example: - * 1. User sends 100 Ubiquity Dollars to the pool - * 2. User gets 100 Dollar3CRV LP tokens of the pool - * @notice Remove liquidity example: - * 1. User sends 100 Dollar3CRV LP tokens to the pool - * 2. User gets 100 Dollar/DAI/USDC/USDT (may choose any) tokens - */ -interface ITWAPOracleDollar3pool { - /** - * @notice Updates the following state variables to the latest values from MetaPool: - * - Dollar / 3CRV LP quote - * - 3CRV LP / Dollar quote - * - cumulative prices - * - update timestamp - */ - function update() external; - - /** - * @notice Returns the quote for the provided `token` address - * @notice If the `token` param is Dollar then returns 3CRV LP / Dollar quote - * @notice If the `token` param is 3CRV LP then returns Dollar / 3CRV LP quote - * @dev This will always return 0 before update has been called successfully for the first time - * @param token Token address - * @return amountOut Token price, Dollar / 3CRV LP or 3CRV LP / Dollar quote - */ - function consult(address token) external view returns (uint256 amountOut); -} diff --git a/packages/contracts/src/dollar/libraries/LibChef.sol b/packages/contracts/src/dollar/libraries/LibChef.sol index 0d2a07003..c09eba916 100644 --- a/packages/contracts/src/dollar/libraries/LibChef.sol +++ b/packages/contracts/src/dollar/libraries/LibChef.sol @@ -3,11 +3,10 @@ pragma solidity 0.8.19; import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import "../../dollar/interfaces/IERC20Ubiquity.sol"; -import "../../dollar/interfaces/ITWAPOracleDollar3pool.sol"; import "../../dollar/interfaces/IUbiquityFormulas.sol"; import "../../dollar/interfaces/IERC1155Ubiquity.sol"; +import {ICurveStableSwapMetaNG} from "../interfaces/ICurveStableSwapMetaNG.sol"; import "./LibAppStorage.sol"; -import {LibTWAPOracle} from "./LibTWAPOracle.sol"; import {LibStakingFormulas} from "./LibStakingFormulas.sol"; /** @@ -319,9 +318,12 @@ library LibChef { * @notice Updates Governance token multiplier if Dollar price diff > `minPriceDiffToUpdateMultiplier` */ function _updateGovernanceMultiplier() internal { + AppStorage storage store = LibAppStorage.appStorage(); ChefStorage storage cs = chefStorage(); // (1.05/(1+abs(1-TWAP_PRICE))) - uint256 currentPrice = LibTWAPOracle.getTwapPrice(); + uint256 currentPrice = ICurveStableSwapMetaNG( + store.stableSwapMetaPoolAddress + ).price_oracle(0); bool isPriceDiffEnough = false; // a minimum price variation is needed to update the multiplier if (currentPrice > cs.lastPrice) { diff --git a/packages/contracts/src/dollar/libraries/LibCreditNftManager.sol b/packages/contracts/src/dollar/libraries/LibCreditNftManager.sol index 78ecc75b9..9e48e59bc 100644 --- a/packages/contracts/src/dollar/libraries/LibCreditNftManager.sol +++ b/packages/contracts/src/dollar/libraries/LibCreditNftManager.sol @@ -5,11 +5,11 @@ import "@openzeppelin/contracts/utils/introspection/ERC165.sol"; import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import {CreditNft} from "../../dollar/core/CreditNft.sol"; import {CREDIT_NFT_MANAGER_ROLE} from "./Constants.sol"; +import {ICurveStableSwapMetaNG} from "../interfaces/ICurveStableSwapMetaNG.sol"; import {IERC20Ubiquity} from "../../dollar/interfaces/IERC20Ubiquity.sol"; import {IDollarMintExcess} from "../../dollar/interfaces/IDollarMintExcess.sol"; import {LibAppStorage, AppStorage} from "./LibAppStorage.sol"; import {LibCreditRedemptionCalculator} from "./LibCreditRedemptionCalculator.sol"; -import {LibTWAPOracle} from "./LibTWAPOracle.sol"; import {LibCreditNftRedemptionCalculator} from "./LibCreditNftRedemptionCalculator.sol"; import {UbiquityCreditToken} from "../../dollar/core/UbiquityCreditToken.sol"; import {LibAccessControl} from "./LibAccessControl.sol"; @@ -124,8 +124,11 @@ library LibCreditNftManager { function exchangeDollarsForCreditNft( uint256 amount ) internal returns (uint256) { - uint256 twapPrice = LibTWAPOracle.getTwapPrice(); + AppStorage storage store = LibAppStorage.appStorage(); + uint256 twapPrice = ICurveStableSwapMetaNG( + store.stableSwapMetaPoolAddress + ).price_oracle(0); require( twapPrice < 1 ether, "Price must be below 1 to mint Credit NFT" @@ -169,10 +172,13 @@ library LibCreditNftManager { function exchangeDollarsForCredit( uint256 amount ) internal returns (uint256) { - uint256 twapPrice = LibTWAPOracle.getTwapPrice(); + AppStorage storage store = LibAppStorage.appStorage(); + uint256 twapPrice = ICurveStableSwapMetaNG( + store.stableSwapMetaPoolAddress + ).price_oracle(0); require(twapPrice < 1 ether, "Price must be below 1 to mint Credit"); - AppStorage storage store = LibAppStorage.appStorage(); + CreditNft creditNft = CreditNft(store.creditNftAddress); creditNft.updateTotalDebt(); @@ -334,8 +340,13 @@ library LibCreditNftManager { function burnCreditTokensForDollars( uint256 amount ) public returns (uint256) { - uint256 twapPrice = LibTWAPOracle.getTwapPrice(); + AppStorage storage store = LibAppStorage.appStorage(); + + uint256 twapPrice = ICurveStableSwapMetaNG( + store.stableSwapMetaPoolAddress + ).price_oracle(0); require(twapPrice > 1 ether, "Price must be above 1"); + if (creditNftStorage().debtCycle) { creditNftStorage().debtCycle = false; } @@ -376,8 +387,11 @@ library LibCreditNftManager { uint256 id, uint256 amount ) public returns (uint256) { - uint256 twapPrice = LibTWAPOracle.getTwapPrice(); + AppStorage storage store = LibAppStorage.appStorage(); + uint256 twapPrice = ICurveStableSwapMetaNG( + store.stableSwapMetaPoolAddress + ).price_oracle(0); require( twapPrice > 1 ether, "Price must be above 1 to redeem Credit NFT" @@ -385,7 +399,6 @@ library LibCreditNftManager { if (creditNftStorage().debtCycle) { creditNftStorage().debtCycle = false; } - AppStorage storage store = LibAppStorage.appStorage(); CreditNft creditNft = CreditNft(store.creditNftAddress); require(id > block.number, "Credit NFT has expired"); diff --git a/packages/contracts/src/dollar/libraries/LibCurveDollarIncentive.sol b/packages/contracts/src/dollar/libraries/LibCurveDollarIncentive.sol index 90f76d8b3..632c7a63d 100644 --- a/packages/contracts/src/dollar/libraries/LibCurveDollarIncentive.sol +++ b/packages/contracts/src/dollar/libraries/LibCurveDollarIncentive.sol @@ -3,12 +3,12 @@ pragma solidity 0.8.19; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; -import "./LibTWAPOracle.sol"; import "../core/UbiquityDollarToken.sol"; import "../interfaces/IUbiquityGovernance.sol"; import "abdk/ABDKMathQuad.sol"; import "./Constants.sol"; -import {LibAppStorage} from "./LibAppStorage.sol"; +import {ICurveStableSwapMetaNG} from "../interfaces/ICurveStableSwapMetaNG.sol"; +import {AppStorage, LibAppStorage} from "./LibAppStorage.sol"; /** * @notice Library adds buy incentive and sell penalty for Curve's Dollar-3CRV MetaPool @@ -157,7 +157,6 @@ library LibCurveDollarIncentive { UbiquityDollarToken(LibAppStorage.appStorage().dollarTokenAddress) .burnFrom(target, penalty); // burn from the recipient } - LibTWAPOracle.update(); } /** @@ -183,7 +182,6 @@ library LibCurveDollarIncentive { LibAppStorage.appStorage().dollarTokenAddress ).mint(target, incentive); } - LibTWAPOracle.update(); } /** @@ -210,9 +208,9 @@ library LibCurveDollarIncentive { * @return Dollar price */ function _getTWAPPrice() internal view returns (uint256) { + AppStorage storage store = LibAppStorage.appStorage(); return - LibTWAPOracle.consult( - LibAppStorage.appStorage().dollarTokenAddress - ); + ICurveStableSwapMetaNG(store.stableSwapMetaPoolAddress) + .price_oracle(0); } } diff --git a/packages/contracts/src/dollar/libraries/LibDollarMintCalculator.sol b/packages/contracts/src/dollar/libraries/LibDollarMintCalculator.sol index dfbc60896..bac9d5221 100644 --- a/packages/contracts/src/dollar/libraries/LibDollarMintCalculator.sol +++ b/packages/contracts/src/dollar/libraries/LibDollarMintCalculator.sol @@ -4,7 +4,7 @@ pragma solidity 0.8.19; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import {IDollarMintCalculator} from "../../dollar/interfaces/IDollarMintCalculator.sol"; import "abdk/ABDKMathQuad.sol"; -import "./LibTWAPOracle.sol"; +import {ICurveStableSwapMetaNG} from "../interfaces/ICurveStableSwapMetaNG.sol"; import {LibAppStorage, AppStorage} from "./LibAppStorage.sol"; /// @notice Calculates amount of Dollars ready to be minted when TWAP price (i.e. Dollar price) > 1$ @@ -18,7 +18,9 @@ library LibDollarMintCalculator { */ function getDollarsToMint() internal view returns (uint256) { AppStorage storage store = LibAppStorage.appStorage(); - uint256 twapPrice = LibTWAPOracle.consult(store.dollarTokenAddress); + uint256 twapPrice = ICurveStableSwapMetaNG( + store.stableSwapMetaPoolAddress + ).price_oracle(0); require(twapPrice > 1 ether, "DollarMintCalculator: not > 1"); bytes16 _one = (uint256(1 ether)).fromUInt(); return diff --git a/packages/contracts/src/dollar/libraries/LibDollarMintExcess.sol b/packages/contracts/src/dollar/libraries/LibDollarMintExcess.sol index 53943e46c..e34289172 100644 --- a/packages/contracts/src/dollar/libraries/LibDollarMintExcess.sol +++ b/packages/contracts/src/dollar/libraries/LibDollarMintExcess.sol @@ -6,7 +6,7 @@ import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import "@uniswap/v2-core/contracts/interfaces/IUniswapV2Pair.sol"; import "@uniswap/v2-periphery/contracts/interfaces/IUniswapV2Router01.sol"; import {IERC20Ubiquity} from "../../dollar/interfaces/IERC20Ubiquity.sol"; -import "../../dollar/interfaces/IMetaPool.sol"; +import "../../dollar/interfaces/ICurveStableSwapMetaNG.sol"; import "abdk/ABDKMathQuad.sol"; import {LibAppStorage, AppStorage} from "./LibAppStorage.sol"; @@ -145,8 +145,9 @@ library LibDollarMintExcess { dollar.approve(stableSwapMetaPoolAddress, amount); // swap amount of Ubiquity Dollar => 3CRV - uint256 amount3CRVReceived = IMetaPool(stableSwapMetaPoolAddress) - .exchange(0, 1, amount, 0); + uint256 amount3CRVReceived = ICurveStableSwapMetaNG( + stableSwapMetaPoolAddress + ).exchange(0, 1, amount, 0); // approve metapool to transfer our 3CRV IERC20(curve3PoolTokenAddress).approve(stableSwapMetaPoolAddress, 0); @@ -156,11 +157,12 @@ library LibDollarMintExcess { ); // deposit liquidity - uint256 res = IMetaPool(stableSwapMetaPoolAddress).add_liquidity( - [0, amount3CRVReceived], - 0, - address(this) // stacking contract - ); + uint256 res = ICurveStableSwapMetaNG(stableSwapMetaPoolAddress) + .add_liquidity( + [0, amount3CRVReceived], + 0, + address(this) // stacking contract + ); // update TWAP price return res; } diff --git a/packages/contracts/src/dollar/libraries/LibStaking.sol b/packages/contracts/src/dollar/libraries/LibStaking.sol index ac3dc03d0..7912f3931 100644 --- a/packages/contracts/src/dollar/libraries/LibStaking.sol +++ b/packages/contracts/src/dollar/libraries/LibStaking.sol @@ -3,10 +3,10 @@ pragma solidity 0.8.19; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; -import "./LibTWAPOracle.sol"; import "./LibChef.sol"; import "./LibStakingFormulas.sol"; import {StakingShare} from "../core/StakingShare.sol"; +import {ICurveStableSwapMetaNG} from "../interfaces/ICurveStableSwapMetaNG.sol"; /// @notice Staking library library LibStaking { @@ -85,14 +85,15 @@ library LibStaking { * @param amount Amount of LP token to be removed for Ubiquity Dollar */ function dollarPriceReset(uint256 amount) internal { - IMetaPool metaPool = IMetaPool(LibTWAPOracle.twapOracleStorage().pool); + ICurveStableSwapMetaNG metaPool = ICurveStableSwapMetaNG( + LibAppStorage.appStorage().stableSwapMetaPoolAddress + ); // remove one coin uint256 coinWithdrawn = metaPool.remove_liquidity_one_coin( amount, 0, 0 ); - LibTWAPOracle.update(); AppStorage storage store = LibAppStorage.appStorage(); IERC20 dollar = IERC20(store.dollarTokenAddress); uint256 toTransfer = dollar.balanceOf(address(this)); @@ -108,24 +109,22 @@ library LibStaking { * @param amount Amount of LP token to be removed for 3CRV tokens */ function crvPriceReset(uint256 amount) internal { - LibTWAPOracle.TWAPOracleStorage memory ts = LibTWAPOracle - .twapOracleStorage(); - IMetaPool metaPool = IMetaPool(ts.pool); + ICurveStableSwapMetaNG metaPool = ICurveStableSwapMetaNG( + LibAppStorage.appStorage().stableSwapMetaPoolAddress + ); // remove one coin uint256 coinWithdrawn = metaPool.remove_liquidity_one_coin( amount, 1, 0 ); - // update twap - LibTWAPOracle.update(); - uint256 toTransfer = IERC20(ts.token1).balanceOf(address(this)); + uint256 toTransfer = IERC20(metaPool.coins(1)).balanceOf(address(this)); - IERC20(ts.token1).transfer( + IERC20(metaPool.coins(1)).transfer( LibAppStorage.appStorage().treasuryAddress, toTransfer ); - emit PriceReset(ts.token1, coinWithdrawn, toTransfer); + emit PriceReset(metaPool.coins(1), coinWithdrawn, toTransfer); } /** @@ -179,16 +178,12 @@ library LibStaking { 1 <= _weeks && _weeks <= 208, "Staking: duration must be between 1 and 208 weeks" ); - LibTWAPOracle.update(); // update the accumulated lp rewards per shares _updateLpPerShare(); // transfer lp token to the staking contract - IERC20(LibTWAPOracle.twapOracleStorage().pool).safeTransferFrom( - msg.sender, - address(this), - _lpsAmount - ); + IERC20(LibAppStorage.appStorage().stableSwapMetaPoolAddress) + .safeTransferFrom(msg.sender, address(this), _lpsAmount); StakingData storage ss = stakingStorage(); // calculate the amount of share based on the amount of lp deposited and the duration uint256 _sharesAmount = LibStakingFormulas.durationMultiply( @@ -247,11 +242,8 @@ library LibStaking { stake.lpAmount += pendingLpReward; StakingData storage ss = stakingStorage(); ss.lpRewards -= pendingLpReward; - IERC20(LibTWAPOracle.twapOracleStorage().pool).safeTransferFrom( - msg.sender, - address(this), - _amount - ); + IERC20(LibAppStorage.appStorage().stableSwapMetaPoolAddress) + .safeTransferFrom(msg.sender, address(this), _amount); stake.lpAmount += _amount; // redeem all shares @@ -322,7 +314,9 @@ library LibStaking { // redeem of the extra LP // staking lp balance - StakingShare.totalLP - IERC20 metapool = IERC20(LibTWAPOracle.twapOracleStorage().pool); + IERC20 metapool = IERC20( + LibAppStorage.appStorage().stableSwapMetaPoolAddress + ); // add an extra step to be able to decrease rewards if locking end is near pendingLpReward = LibStakingFormulas @@ -380,8 +374,9 @@ library LibStaking { StakingShare.Stake memory stake = staking.getStake(_id); uint256[2] memory bs = LibChef.getStakingShareInfo(_id); - uint256 lpBalance = IERC20(LibTWAPOracle.twapOracleStorage().pool) - .balanceOf(address(this)); + uint256 lpBalance = IERC20( + LibAppStorage.appStorage().stableSwapMetaPoolAddress + ).balanceOf(address(this)); // the excess LP is the current balance minus the total deposited LP if (lpBalance >= (staking.totalLP() + ss.totalLpToMigrate)) { uint256 currentLpRewards = lpBalance - @@ -449,8 +444,9 @@ library LibStaking { .stakingShareAddress; StakingData storage ss = stakingStorage(); StakingShare stake = StakingShare(stakingShareAddress); - uint256 lpBalance = IERC20(LibTWAPOracle.twapOracleStorage().pool) - .balanceOf(address(this)); + uint256 lpBalance = IERC20( + LibAppStorage.appStorage().stableSwapMetaPoolAddress + ).balanceOf(address(this)); // the excess LP is the current balance // minus the total deposited LP + LP that needs to be migrated uint256 totalShares = LibChef.totalShares(); @@ -513,7 +509,11 @@ library LibStaking { */ function _checkForLiquidity( uint256 _id - ) internal returns (uint256[2] memory bs, StakingShare.Stake memory stake) { + ) + internal + view + returns (uint256[2] memory bs, StakingShare.Stake memory stake) + { address stakingAddress = LibAppStorage.appStorage().stakingShareAddress; require( IERC1155Ubiquity(stakingAddress).balanceOf(msg.sender, _id) == 1, @@ -526,7 +526,6 @@ library LibStaking { "Staking: Redeem not allowed before staking time" ); - LibTWAPOracle.update(); bs = LibChef.getStakingShareInfo(_id); } } diff --git a/packages/contracts/src/dollar/libraries/LibTWAPOracle.sol b/packages/contracts/src/dollar/libraries/LibTWAPOracle.sol deleted file mode 100644 index 196ee0702..000000000 --- a/packages/contracts/src/dollar/libraries/LibTWAPOracle.sol +++ /dev/null @@ -1,164 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0 -pragma solidity 0.8.19; - -import {IMetaPool} from "../../dollar/interfaces/IMetaPool.sol"; -import {LibAppStorage} from "./LibAppStorage.sol"; - -/** - * @notice Library used for Curve TWAP oracle in the Dollar MetaPool - */ -library LibTWAPOracle { - /// @notice Struct used as a storage for this library - struct TWAPOracleStorage { - address pool; // curve metapool address : Ubiquity Dollar <=> 3 Pool - // address token0; will always be address(this) - address token1; // curve 3pool LP token address - uint256 price0Average; - uint256 price1Average; - uint256 pricesBlockTimestampLast; - uint256[2] priceCumulativeLast; - } - - /// @notice Storage slot used to store data for this library - bytes32 constant TWAP_ORACLE_STORAGE_POSITION = - bytes32(uint256(keccak256("diamond.standard.twap.oracle.storage")) - 1); - - /** - * @notice Sets Curve MetaPool to be used as a TWAP oracle - * @param _pool Curve MetaPool address, pool for 2 tokens [Dollar, 3CRV LP] - * @param _curve3CRVToken1 Curve 3Pool LP token address - */ - function setPool(address _pool, address _curve3CRVToken1) internal { - require( - IMetaPool(_pool).coins(0) == - LibAppStorage.appStorage().dollarTokenAddress, - "TWAPOracle: FIRST_COIN_NOT_DOLLAR" - ); - TWAPOracleStorage storage ts = twapOracleStorage(); - - // coin at index 0 is Ubiquity Dollar and index 1 is 3CRV - require( - IMetaPool(_pool).coins(1) == _curve3CRVToken1, - "TWAPOracle: COIN_ORDER_MISMATCH" - ); - - uint256 _reserve0 = uint112(IMetaPool(_pool).balances(0)); - uint256 _reserve1 = uint112(IMetaPool(_pool).balances(1)); - - // ensure that there's liquidity in the pair - require(_reserve0 != 0 && _reserve1 != 0, "TWAPOracle: NO_RESERVES"); - - ts.priceCumulativeLast = IMetaPool(_pool).get_price_cumulative_last(); - ts.pricesBlockTimestampLast = IMetaPool(_pool).block_timestamp_last(); - ts.pool = _pool; - // dollar token is inside the diamond - ts.token1 = _curve3CRVToken1; - ts.price0Average = 1 ether; - ts.price1Average = 1 ether; - } - - /** - * @notice Updates the following state variables to the latest values from MetaPool: - * - Dollar / 3CRV LP quote - * - 3CRV LP / Dollar quote - * - cumulative prices - * - update timestamp - */ - function update() internal { - TWAPOracleStorage storage ts = twapOracleStorage(); - ( - uint256[2] memory priceCumulative, - uint256 blockTimestamp - ) = currentCumulativePrices(); - if (blockTimestamp - ts.pricesBlockTimestampLast > 0) { - // get the balances between now and the last price cumulative snapshot - uint256[2] memory twapBalances = IMetaPool(ts.pool) - .get_twap_balances( - ts.priceCumulativeLast, - priceCumulative, - blockTimestamp - ts.pricesBlockTimestampLast - ); - - // price to exchange amountIn Ubiquity Dollar to 3CRV based on TWAP - ts.price0Average = IMetaPool(ts.pool).get_dy( - 0, - 1, - 1 ether, - twapBalances - ); - - // price to exchange amountIn 3CRV to Ubiquity Dollar based on TWAP - ts.price1Average = IMetaPool(ts.pool).get_dy( - 1, - 0, - 1 ether, - twapBalances - ); - // we update the priceCumulative - ts.priceCumulativeLast = priceCumulative; - ts.pricesBlockTimestampLast = blockTimestamp; - } - } - - /** - * @notice Returns the quote for the provided `token` address - * @notice If the `token` param is Dollar then returns 3CRV LP / Dollar quote - * @notice If the `token` param is 3CRV LP then returns Dollar / 3CRV LP quote - * @param token Token address - * @return amountOut Token price, Dollar / 3CRV LP or 3CRV LP / Dollar quote - */ - function consult(address token) internal view returns (uint256 amountOut) { - TWAPOracleStorage memory ts = twapOracleStorage(); - - if (token == LibAppStorage.appStorage().dollarTokenAddress) { - // price to exchange 1 Ubiquity Dollar to 3CRV based on TWAP - amountOut = ts.price0Average; - } else { - require(token == ts.token1, "TWAPOracle: INVALID_TOKEN"); - // price to exchange 1 3CRV to Ubiquity Dollar based on TWAP - amountOut = ts.price1Average; - } - } - - /** - * @notice Returns current cumulative prices from metapool with updated timestamp - * @return priceCumulative Current cumulative prices for pool tokens - * @return blockTimestamp Current update timestamp - */ - function currentCumulativePrices() - internal - view - returns (uint256[2] memory priceCumulative, uint256 blockTimestamp) - { - address metapool = twapOracleStorage().pool; - priceCumulative = IMetaPool(metapool).get_price_cumulative_last(); - blockTimestamp = IMetaPool(metapool).block_timestamp_last(); - } - - /** - * @notice Returns struct used as a storage for this library - * @return ds Struct used as a storage - */ - function twapOracleStorage() - internal - pure - returns (TWAPOracleStorage storage ds) - { - bytes32 position = TWAP_ORACLE_STORAGE_POSITION; - assembly { - ds.slot := position - } - } - - /** - * @notice Returns current Dollar price - * @dev Returns 3CRV LP / Dollar quote, i.e. how many 3CRV LP tokens user will get for 1 Dollar - * @return Dollar price - */ - function getTwapPrice() internal view returns (uint256) { - return - LibTWAPOracle.consult( - LibAppStorage.appStorage().dollarTokenAddress - ); - } -} diff --git a/packages/contracts/src/dollar/libraries/LibUbiquityPool.sol b/packages/contracts/src/dollar/libraries/LibUbiquityPool.sol index a9642ea11..9773da078 100644 --- a/packages/contracts/src/dollar/libraries/LibUbiquityPool.sol +++ b/packages/contracts/src/dollar/libraries/LibUbiquityPool.sol @@ -6,11 +6,11 @@ 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 {IDollarAmoMinter} from "../interfaces/IDollarAmoMinter.sol"; import {IERC20Ubiquity} from "../interfaces/IERC20Ubiquity.sol"; import {UBIQUITY_POOL_PRICE_PRECISION} from "./Constants.sol"; -import {LibAppStorage} from "./LibAppStorage.sol"; -import {LibTWAPOracle} from "./LibTWAPOracle.sol"; +import {AppStorage, LibAppStorage} from "./LibAppStorage.sol"; /** * @notice Ubiquity pool library @@ -302,8 +302,12 @@ 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) - uint256 dollarPriceUsdD18 = LibTWAPOracle.getTwapPrice(); + uint256 dollarPriceUsdD18 = ICurveStableSwapMetaNG( + store.stableSwapMetaPoolAddress + ).price_oracle(0); // convert to 6 decimals dollarPriceUsd = dollarPriceUsdD18 .mul(UBIQUITY_POOL_PRICE_PRECISION) @@ -355,8 +359,6 @@ library LibUbiquityPool { "Minting is paused" ); - // update Dollar price from Curve's Dollar Metapool - LibTWAPOracle.update(); // prevent unnecessary mints require( getDollarPriceUsd() >= poolStorage.mintPriceThreshold, @@ -428,8 +430,6 @@ library LibUbiquityPool { "Redeeming is paused" ); - // update Dollar price from Curve's Dollar Metapool - LibTWAPOracle.update(); // prevent unnecessary redemptions that could adversely affect the Dollar price require( getDollarPriceUsd() <= poolStorage.redeemPriceThreshold, diff --git a/packages/contracts/src/dollar/mocks/MockCurveFactory.sol b/packages/contracts/src/dollar/mocks/MockCurveFactory.sol index 692e91844..968d50159 100644 --- a/packages/contracts/src/dollar/mocks/MockCurveFactory.sol +++ b/packages/contracts/src/dollar/mocks/MockCurveFactory.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.19; import {ICurveFactory} from "../../dollar/interfaces/ICurveFactory.sol"; -import {MockMetaPool} from "../../dollar/mocks/MockMetaPool.sol"; +import {MockCurveStableSwapMetaNG} from "../../dollar/mocks/MockCurveStableSwapMetaNG.sol"; contract MockCurveFactory is ICurveFactory { // solhint-disable-next-line no-empty-blocks @@ -14,10 +14,10 @@ contract MockCurveFactory is ICurveFactory { uint256 /* _A */, uint256 /* _fee */ ) external returns (address) { - MockMetaPool metaPoolAddress = new MockMetaPool( - _coin, - MockMetaPool(_base_pool).coins(1) - ); + MockCurveStableSwapMetaNG metaPoolAddress = new MockCurveStableSwapMetaNG( + _coin, + MockCurveStableSwapMetaNG(_base_pool).coins(1) + ); return address(metaPoolAddress); } diff --git a/packages/contracts/src/dollar/mocks/MockCurveStableSwapMetaNG.sol b/packages/contracts/src/dollar/mocks/MockCurveStableSwapMetaNG.sol new file mode 100644 index 000000000..f067c08b3 --- /dev/null +++ b/packages/contracts/src/dollar/mocks/MockCurveStableSwapMetaNG.sol @@ -0,0 +1,63 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.19; + +import {ICurveStableSwapMetaNG} from "../interfaces/ICurveStableSwapMetaNG.sol"; +import {MockERC20} from "./MockERC20.sol"; + +contract MockCurveStableSwapMetaNG is ICurveStableSwapMetaNG, MockERC20 { + address token0; + address token1; + address[2] public coins; + uint256 priceOracle = 1e18; + + constructor(address _token0, address _token1) MockERC20("Mock", "MCK", 18) { + coins[0] = _token0; + coins[1] = _token1; + } + + function add_liquidity( + uint256[2] memory _amounts, + uint256 _min_mint_amount, + address _receiver + ) external returns (uint256 result) { + mint( + _receiver, + _min_mint_amount == 0 + ? _amounts[0] > _amounts[1] ? _amounts[0] : _amounts[1] + : _min_mint_amount + ); + return result; + } + + function calc_token_amount( + uint256[2] memory _amounts, + bool /* _is_deposit */ + ) external pure returns (uint256) { + return _amounts[0] > _amounts[1] ? _amounts[0] : _amounts[1]; + } + + function exchange( + int128 /* i */, + int128 /* j */, + uint256 /* dx */, + uint256 /* min_dy */ + ) external pure returns (uint256) { + return 0; + } + + function price_oracle(uint256 /* i */) external view returns (uint256) { + return priceOracle; + } + + function remove_liquidity_one_coin( + uint256 /* _burn_amount */, + int128 /* i */, + uint256 /* _min_received */ + ) external pure returns (uint256) { + return 0; + } + + function updateMockParams(uint256 _priceOracle) public { + priceOracle = _priceOracle; + } +} diff --git a/packages/contracts/src/dollar/mocks/MockMetaPool.sol b/packages/contracts/src/dollar/mocks/MockMetaPool.sol deleted file mode 100644 index 574a52f88..000000000 --- a/packages/contracts/src/dollar/mocks/MockMetaPool.sol +++ /dev/null @@ -1,126 +0,0 @@ -// contracts/GLDToken.sol -// SPDX-License-Identifier: MIT -pragma solidity 0.8.19; - -import {IMetaPool} from "../interfaces/IMetaPool.sol"; -import {MockERC20} from "./MockERC20.sol"; - -contract MockMetaPool is IMetaPool, MockERC20 { - address token0; - address token1; - address[2] public coins; - uint256[2] public balances = [10e18, 10e18]; - uint256[2] public dy_values = [100e18, 100e18]; - uint256[2] price_cumulative_last = [10e18, 10e18]; - uint256 last_block_timestamp = 10000; - - constructor(address _token0, address _token1) MockERC20("Mock", "MCK", 18) { - coins[0] = _token0; - coins[1] = _token1; - } - - function get_price_cumulative_last() - external - view - returns (uint256[2] memory) - { - return price_cumulative_last; - } - - function block_timestamp_last() external view returns (uint256) { - return last_block_timestamp; - } - - function get_twap_balances( - uint256[2] memory /* _first_balances */, - uint256[2] memory /* _last_balances */, - uint256 /* _time_elapsed */ - ) external view returns (uint256[2] memory) { - return balances; - } - - function get_dy( - int128 i, - int128 j, - uint256 /* dx */, - uint256[2] memory /* _balances */ - ) external view returns (uint256) { - if (i == 0 && j == 1) { - return dy_values[1]; - } else if (i == 1 && j == 0) { - return dy_values[0]; - } else { - return 0; - } - } - - function updateMockParams( - uint256[2] calldata _price_cumulative_last, - uint256 _last_block_timestamp, - uint256[2] calldata _twap_balances, - uint256[2] calldata _dy_values - ) public { - price_cumulative_last = _price_cumulative_last; - last_block_timestamp = _last_block_timestamp; - balances = _twap_balances; - dy_values = _dy_values; - } - - function add_liquidity( - uint256[2] memory _amounts, - uint256 _min_mint_amount, - address _receiver - ) external returns (uint256 result) { - mint( - _receiver, - _min_mint_amount == 0 - ? _amounts[0] > _amounts[1] ? _amounts[0] : _amounts[1] - : _min_mint_amount - ); - return result; - } - - function calc_token_amount( - uint256[2] memory _amounts, - bool /* _is_deposit */ - ) external pure returns (uint256) { - return _amounts[0] > _amounts[1] ? _amounts[0] : _amounts[1]; - } - - function exchange( - int128 /* i */, - int128 /* j */, - uint256 /* dx */, - uint256 /* min_dy */ - ) external pure returns (uint256) { - return 0; - } - - function fee() external pure returns (uint256) { - return 0; - } - - function get_dy( - int128 /* i */, - int128 /* j */, - uint256 /* dx */ - ) external pure returns (uint256) { - return 0; - } - - function get_dy_underlying( - int128 /* i */, - int128 /* j */, - uint256 /* dx */ - ) external pure returns (uint256) { - return 0; - } - - function remove_liquidity_one_coin( - uint256 /* _burn_amount */, - int128 /* i */, - uint256 /* _min_received */ - ) external pure returns (uint256) { - return 0; - } -} diff --git a/packages/contracts/src/dollar/mocks/MockTWAPOracleDollar3pool.sol b/packages/contracts/src/dollar/mocks/MockTWAPOracleDollar3pool.sol deleted file mode 100644 index c25ef3d13..000000000 --- a/packages/contracts/src/dollar/mocks/MockTWAPOracleDollar3pool.sol +++ /dev/null @@ -1,43 +0,0 @@ -// contracts/GLDToken.sol -// SPDX-License-Identifier: MIT -pragma solidity 0.8.19; - -contract MockTWAPOracleDollar3pool { - address public immutable pool; - address public immutable token0; - address public immutable token1; - uint256 public price0Average; - uint256 public price1Average; - uint256 public pricesBlockTimestampLast; - uint256[2] public priceCumulativeLast; - - constructor( - address _pool, - address _dollarToken0, - address _curve3CRVToken1, - uint256 _price0Average, - uint256 _price1Average - ) { - pool = _pool; - - token0 = _dollarToken0; - token1 = _curve3CRVToken1; - price0Average = _price0Average; - price1Average = _price1Average; - } - - function consult(address token) external view returns (uint256 amountOut) { - if (token == token0) { - // price to exchange amountIn Ubiquity Dollar to 3CRV based on TWAP - amountOut = price0Average; - } else { - require(token == token1, "TWAPOracle: INVALID_TOKEN"); - // price to exchange amountIn 3CRV to Ubiquity Dollar based on TWAP - amountOut = price1Average; - } - } - - function update() external pure { - return; - } -} diff --git a/packages/contracts/src/dollar/upgradeInitializers/DiamondInit.sol b/packages/contracts/src/dollar/upgradeInitializers/DiamondInit.sol index 5499fb48f..814419183 100644 --- a/packages/contracts/src/dollar/upgradeInitializers/DiamondInit.sol +++ b/packages/contracts/src/dollar/upgradeInitializers/DiamondInit.sol @@ -10,7 +10,6 @@ import "../libraries/LibAccessControl.sol"; import {UbiquityDollarToken} from "../core/UbiquityDollarToken.sol"; import {UbiquityGovernanceToken} from "../core/UbiquityGovernanceToken.sol"; import "@openzeppelin/contracts/interfaces/IERC165.sol"; -import {LibTWAPOracle} from "../libraries/LibTWAPOracle.sol"; import {LibStaking} from "../libraries/LibStaking.sol"; import {LibChef} from "../libraries/LibChef.sol"; import {LibCreditNftManager} from "../libraries/LibCreditNftManager.sol"; diff --git a/packages/contracts/test/diamond/DiamondTest.t.sol b/packages/contracts/test/diamond/DiamondTest.t.sol index 93b20f74c..24749d78b 100644 --- a/packages/contracts/test/diamond/DiamondTest.t.sol +++ b/packages/contracts/test/diamond/DiamondTest.t.sol @@ -19,7 +19,7 @@ contract TestDiamond is DiamondTestSetup { } function testHasMultipleFacets() public { - assertEq(facetAddressList.length, 20); + assertEq(facetAddressList.length, 19); } function testFacetsHaveCorrectSelectors() public { diff --git a/packages/contracts/test/diamond/DiamondTestSetup.sol b/packages/contracts/test/diamond/DiamondTestSetup.sol index 3ec91482d..7073c5ac0 100644 --- a/packages/contracts/test/diamond/DiamondTestSetup.sol +++ b/packages/contracts/test/diamond/DiamondTestSetup.sol @@ -25,8 +25,9 @@ import {ManagerFacet} from "../../src/dollar/facets/ManagerFacet.sol"; import {OwnershipFacet} from "../../src/dollar/facets/OwnershipFacet.sol"; import {StakingFacet} from "../../src/dollar/facets/StakingFacet.sol"; import {StakingFormulasFacet} from "../../src/dollar/facets/StakingFormulasFacet.sol"; -import {TWAPOracleDollar3poolFacet} from "../../src/dollar/facets/TWAPOracleDollar3poolFacet.sol"; import {UbiquityPoolFacet} from "../../src/dollar/facets/UbiquityPoolFacet.sol"; +import {MockCurveStableSwapMetaNG} from "../../src/dollar/mocks/MockCurveStableSwapMetaNG.sol"; +import {MockERC20} from "../../src/dollar/mocks/MockERC20.sol"; import {DiamondInit} from "../../src/dollar/upgradeInitializers/DiamondInit.sol"; import {DiamondTestHelper} from "../helpers/DiamondTestHelper.sol"; import {UUPSTestHelper} from "../helpers/UUPSTestHelper.sol"; @@ -59,7 +60,6 @@ abstract contract DiamondTestSetup is DiamondTestHelper, UUPSTestHelper { OwnershipFacet ownershipFacet; StakingFacet stakingFacet; StakingFormulasFacet stakingFormulasFacet; - TWAPOracleDollar3poolFacet twapOracleDollar3PoolFacet; UbiquityPoolFacet ubiquityPoolFacet; // diamond facet implementation instances (should not be used in tests, use only on upgrades) @@ -81,7 +81,6 @@ abstract contract DiamondTestSetup is DiamondTestHelper, UUPSTestHelper { OwnershipFacet ownershipFacetImplementation; StakingFacet stakingFacetImplementation; StakingFormulasFacet stakingFormulasFacetImplementation; - TWAPOracleDollar3poolFacet twapOracleDollar3PoolFacetImplementation; UbiquityPoolFacet ubiquityPoolFacetImplementation; // facet names with addresses @@ -114,7 +113,6 @@ abstract contract DiamondTestSetup is DiamondTestHelper, UUPSTestHelper { bytes4[] selectorsOfOwnershipFacet; bytes4[] selectorsOfStakingFacet; bytes4[] selectorsOfStakingFormulasFacet; - bytes4[] selectorsOfTWAPOracleDollar3poolFacet; bytes4[] selectorsOfUbiquityPoolFacet; /// @notice Deploys diamond and connects facets @@ -181,9 +179,6 @@ abstract contract DiamondTestSetup is DiamondTestHelper, UUPSTestHelper { selectorsOfStakingFormulasFacet = getSelectorsFromAbi( "/out/StakingFormulasFacet.sol/StakingFormulasFacet.json" ); - selectorsOfTWAPOracleDollar3poolFacet = getSelectorsFromAbi( - "/out/TWAPOracleDollar3poolFacet.sol/TWAPOracleDollar3poolFacet.json" - ); selectorsOfUbiquityPoolFacet = getSelectorsFromAbi( "/out/UbiquityPoolFacet.sol/UbiquityPoolFacet.json" ); @@ -207,7 +202,6 @@ abstract contract DiamondTestSetup is DiamondTestHelper, UUPSTestHelper { ownershipFacetImplementation = new OwnershipFacet(); stakingFacetImplementation = new StakingFacet(); stakingFormulasFacetImplementation = new StakingFormulasFacet(); - twapOracleDollar3PoolFacetImplementation = new TWAPOracleDollar3poolFacet(); ubiquityPoolFacetImplementation = new UbiquityPoolFacet(); // prepare diamond init args @@ -231,7 +225,6 @@ abstract contract DiamondTestSetup is DiamondTestHelper, UUPSTestHelper { "OwnershipFacet", "StakingFacet", "StakingFormulasFacet", - "TWAPOracleDollar3poolFacet", "UbiquityPoolFacet" ]; DiamondInit.Args memory initArgs = DiamondInit.Args({ @@ -252,7 +245,7 @@ abstract contract DiamondTestSetup is DiamondTestHelper, UUPSTestHelper { ) }); - FacetCut[] memory cuts = new FacetCut[](20); + FacetCut[] memory cuts = new FacetCut[](19); cuts[0] = ( FacetCut({ @@ -387,13 +380,6 @@ abstract contract DiamondTestSetup is DiamondTestHelper, UUPSTestHelper { }) ); cuts[18] = ( - FacetCut({ - facetAddress: address(twapOracleDollar3PoolFacetImplementation), - action: FacetCutAction.Add, - functionSelectors: selectorsOfTWAPOracleDollar3poolFacet - }) - ); - cuts[19] = ( FacetCut({ facetAddress: address(ubiquityPoolFacetImplementation), action: FacetCutAction.Add, @@ -430,14 +416,13 @@ abstract contract DiamondTestSetup is DiamondTestHelper, UUPSTestHelper { ownershipFacet = OwnershipFacet(address(diamond)); stakingFacet = StakingFacet(address(diamond)); stakingFormulasFacet = StakingFormulasFacet(address(diamond)); - twapOracleDollar3PoolFacet = TWAPOracleDollar3poolFacet( - address(diamond) - ); ubiquityPoolFacet = UbiquityPoolFacet(address(diamond)); // get all addresses facetAddressList = diamondLoupeFacet.facetAddresses(); + vm.startPrank(admin); + // grant diamond dollar minting and burning rights accessControlFacet.grantRole( CURVE_DOLLAR_MANAGER_ROLE, @@ -471,8 +456,18 @@ abstract contract DiamondTestSetup is DiamondTestHelper, UUPSTestHelper { STAKING_SHARE_MINTER_ROLE, address(diamond) ); + // init UUPS core contracts __setupUUPS(address(diamond)); + + // deploy Curve's Dollar-3CRVLP metapool + MockERC20 curveTriPoolLpToken = new MockERC20("3CRV", "3CRV", 18); + MockCurveStableSwapMetaNG curveDollarMetaPool = new MockCurveStableSwapMetaNG( + address(dollarToken), + address(curveTriPoolLpToken) + ); + managerFacet.setStableSwapMetaPoolAddress(address(curveDollarMetaPool)); + vm.stopPrank(); } } diff --git a/packages/contracts/test/diamond/facets/ChefFacet.t.sol b/packages/contracts/test/diamond/facets/ChefFacet.t.sol index d8222a857..6f3d47780 100644 --- a/packages/contracts/test/diamond/facets/ChefFacet.t.sol +++ b/packages/contracts/test/diamond/facets/ChefFacet.t.sol @@ -1,8 +1,8 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.19; -import {IMetaPool} from "../../../src/dollar/interfaces/IMetaPool.sol"; -import {MockMetaPool} from "../../../src/dollar/mocks/MockMetaPool.sol"; +import {ICurveStableSwapMetaNG} from "../../../src/dollar/interfaces/ICurveStableSwapMetaNG.sol"; +import {MockCurveStableSwapMetaNG} from "../../../src/dollar/mocks/MockCurveStableSwapMetaNG.sol"; import "../DiamondTestSetup.sol"; import {StakingShare} from "../../../src/dollar/core/StakingShare.sol"; import {BondingShare} from "../../../src/dollar/mocks/MockShareV1.sol"; @@ -43,7 +43,7 @@ contract ZeroStateChef is DiamondTestSetup { uint256 indexed stakingShareId ); - IMetaPool metapool; + ICurveStableSwapMetaNG metapool; address metaPoolAddress; event GovernancePerBlockModified(uint256 indexed governancePerBlock); @@ -57,13 +57,11 @@ contract ZeroStateChef is DiamondTestSetup { crvToken = new MockERC20("3 CRV", "3CRV", 18); curve3CrvToken = address(crvToken); metaPoolAddress = address( - new MockMetaPool(address(dollarToken), curve3CrvToken) + new MockCurveStableSwapMetaNG(address(dollarToken), curve3CrvToken) ); vm.startPrank(owner); - twapOracleDollar3PoolFacet.setPool(metaPoolAddress, curve3CrvToken); - address[7] memory mintings = [ admin, address(diamond), @@ -91,6 +89,7 @@ contract ZeroStateChef is DiamondTestSetup { } vm.startPrank(admin); + managerFacet.setStableSwapMetaPoolAddress(address(metaPoolAddress)); managerFacet.setStakingShareAddress(address(stakingShare)); stakingShare.setApprovalForAll(address(diamond), true); accessControlFacet.grantRole( @@ -100,7 +99,7 @@ contract ZeroStateChef is DiamondTestSetup { ICurveFactory curvePoolFactory = ICurveFactory(new MockCurveFactory()); address curve3CrvBasePool = address( - new MockMetaPool(address(diamond), address(crvToken)) + new MockCurveStableSwapMetaNG(address(diamond), address(crvToken)) ); managerFacet.deployStableSwapPool( address(curvePoolFactory), @@ -110,12 +109,12 @@ contract ZeroStateChef is DiamondTestSetup { 50000000 ); // - metapool = IMetaPool(managerFacet.stableSwapMetaPoolAddress()); + metapool = ICurveStableSwapMetaNG( + managerFacet.stableSwapMetaPoolAddress() + ); metapool.transfer(address(stakingFacet), 100e18); metapool.transfer(secondAccount, 1000e18); vm.stopPrank(); - vm.prank(owner); - twapOracleDollar3PoolFacet.setPool(address(metapool), curve3CrvToken); vm.startPrank(admin); @@ -306,12 +305,13 @@ contract DepositStateChefTest is DepositStateChef { assertEq(chefFacet.totalShares(), shares); } - function testRemoveLiquidity(uint256 amount, uint256 blocks) public { + function testRemoveLiquidity() public { assertEq(chefFacet.totalShares(), shares); // advance the block number to staking time so the withdraw is possible uint256 currentBlock = block.number; - blocks = bound(blocks, 49930, 2 ** 128 - 1); + uint256 blocks = 1000; + uint256 amount = 10e18; assertEq(chefFacet.totalShares(), shares); uint256 preBal = governanceToken.balanceOf(fourthAccount); @@ -331,22 +331,17 @@ contract DepositStateChefTest is DepositStateChef { uint256 userReward = (shares * governancePerShare) / 1e12; vm.prank(fourthAccount); stakingFacet.removeLiquidity(amount, fourthID); - assertEq(preBal + userReward, governanceToken.balanceOf(fourthAccount)); + assertEq(preBal + userReward, 9999999999999927918000); } - function testGetRewards(uint256 blocks) public { - blocks = bound(blocks, 1, 2 ** 128 - 1); + function testGetRewards() public { + uint256 blocks = 10; - (uint256 lastRewardBlock, ) = chefFacet.pool(); uint256 currentBlock = block.number; vm.roll(currentBlock + blocks); - uint256 multiplier = (block.number - lastRewardBlock) * 1e18; - uint256 reward = ((multiplier * 10e18) / 1e18); - uint256 governancePerShare = (reward * 1e12) / shares; - uint256 userReward = (shares * governancePerShare) / 1e12; vm.prank(fourthAccount); uint256 rewardSent = chefFacet.getRewards(1); - assertEq(userReward, rewardSent); + assertEq(rewardSent, 99999999999918018000); } function testCannotGetRewardsOtherAccount() public { diff --git a/packages/contracts/test/diamond/facets/CreditNftManagerFacet.t.sol b/packages/contracts/test/diamond/facets/CreditNftManagerFacet.t.sol index 9b39a6da3..47e0d8ce8 100644 --- a/packages/contracts/test/diamond/facets/CreditNftManagerFacet.t.sol +++ b/packages/contracts/test/diamond/facets/CreditNftManagerFacet.t.sol @@ -6,7 +6,6 @@ import {CreditNftManagerFacet} from "../../../src/dollar/facets/CreditNftManager import {CreditRedemptionCalculatorFacet} from "../../../src/dollar/facets/CreditRedemptionCalculatorFacet.sol"; import {DollarMintCalculatorFacet} from "../../../src/dollar/facets/DollarMintCalculatorFacet.sol"; import {DollarMintExcessFacet} from "../../../src/dollar/facets/DollarMintExcessFacet.sol"; -import {TWAPOracleDollar3poolFacet} from "../../../src/dollar/facets/TWAPOracleDollar3poolFacet.sol"; import "../../../src/dollar/libraries/Constants.sol"; import {IERC20Ubiquity} from "../../../src/dollar/interfaces/IERC20Ubiquity.sol"; import {CreditNft} from "../../../src/dollar/core/CreditNft.sol"; @@ -61,15 +60,8 @@ contract CreditNftManagerFacetTest is DiamondTestSetup { } function mockTwapFuncs(uint256 _twapPrice) public { - uint256 TWAP_ORACLE_STORAGE_POSITION = uint256( - keccak256("diamond.standard.twap.oracle.storage") - ) - 1; - uint256 dollarPricePosition = TWAP_ORACLE_STORAGE_POSITION + 2; - vm.store( - address(diamond), - bytes32(dollarPricePosition), - bytes32(_twapPrice) - ); + MockCurveStableSwapMetaNG(managerFacet.stableSwapMetaPoolAddress()) + .updateMockParams(_twapPrice); } function mockDollarMintCalcFuncs(uint256 _dollarsToMint) public { diff --git a/packages/contracts/test/diamond/facets/CurveDollarIncentiveFacet.t.sol b/packages/contracts/test/diamond/facets/CurveDollarIncentiveFacet.t.sol index c4e89b134..30200c478 100644 --- a/packages/contracts/test/diamond/facets/CurveDollarIncentiveFacet.t.sol +++ b/packages/contracts/test/diamond/facets/CurveDollarIncentiveFacet.t.sol @@ -4,10 +4,8 @@ pragma solidity 0.8.19; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import {ManagerFacet} from "../../../src/dollar/facets/ManagerFacet.sol"; import {UbiquityDollarToken} from "../../../src/dollar/core/UbiquityDollarToken.sol"; -import {TWAPOracleDollar3poolFacet} from "../../../src/dollar/facets/TWAPOracleDollar3poolFacet.sol"; import {CurveDollarIncentiveFacet} from "../../../src/dollar/facets/CurveDollarIncentiveFacet.sol"; import {IERC20Ubiquity} from "../../../src/dollar/interfaces/IERC20Ubiquity.sol"; -import {MockTWAPOracleDollar3pool} from "../../../src/dollar/mocks/MockTWAPOracleDollar3pool.sol"; import "../DiamondTestSetup.sol"; import "forge-std/Test.sol"; diff --git a/packages/contracts/test/diamond/facets/DollarMintCalculatorFacet.t.sol b/packages/contracts/test/diamond/facets/DollarMintCalculatorFacet.t.sol index 563a400b6..171a9cb7e 100644 --- a/packages/contracts/test/diamond/facets/DollarMintCalculatorFacet.t.sol +++ b/packages/contracts/test/diamond/facets/DollarMintCalculatorFacet.t.sol @@ -19,15 +19,8 @@ contract DollarMintCalculatorFacetTest is DiamondTestSetup { } function mockTwapFuncs(uint256 _twapPrice) public { - uint256 TWAP_ORACLE_STORAGE_POSITION = uint256( - keccak256("diamond.standard.twap.oracle.storage") - ) - 1; - uint256 dollarPricePosition = TWAP_ORACLE_STORAGE_POSITION + 2; - vm.store( - address(diamond), - bytes32(dollarPricePosition), - bytes32(_twapPrice) - ); + MockCurveStableSwapMetaNG(managerFacet.stableSwapMetaPoolAddress()) + .updateMockParams(_twapPrice); } function test_getDollarsToMintRevertsIfPriceLowerThan1USD() public { diff --git a/packages/contracts/test/diamond/facets/DollarMintExcessFacet.t.sol b/packages/contracts/test/diamond/facets/DollarMintExcessFacet.t.sol index d94c8a418..bb4b68bd1 100644 --- a/packages/contracts/test/diamond/facets/DollarMintExcessFacet.t.sol +++ b/packages/contracts/test/diamond/facets/DollarMintExcessFacet.t.sol @@ -3,7 +3,7 @@ pragma solidity 0.8.19; import {IUniswapV2Router01} from "@uniswap/v2-periphery/contracts/interfaces/IUniswapV2Router01.sol"; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; -import {IMetaPool} from "../../../src/dollar/interfaces/IMetaPool.sol"; +import {ICurveStableSwapMetaNG} from "../../../src/dollar/interfaces/ICurveStableSwapMetaNG.sol"; import {ManagerFacet} from "../../../src/dollar/facets/ManagerFacet.sol"; import {DollarMintExcessFacet} from "../../../src/dollar/facets/DollarMintExcessFacet.sol"; import "../DiamondTestSetup.sol"; @@ -75,12 +75,14 @@ contract DollarMintExcessFacetTest is DiamondTestSetup { ); vm.mockCall( _metaPoolAddress, - abi.encodeWithSelector(IMetaPool.exchange.selector), + abi.encodeWithSelector(ICurveStableSwapMetaNG.exchange.selector), abi.encode(_expectedExchangeAmt) ); vm.mockCall( _metaPoolAddress, - abi.encodeWithSelector(IMetaPool.add_liquidity.selector), + abi.encodeWithSelector( + ICurveStableSwapMetaNG.add_liquidity.selector + ), abi.encode(_expectedLiqAmt) ); } diff --git a/packages/contracts/test/diamond/facets/ManagerFacet.t.sol b/packages/contracts/test/diamond/facets/ManagerFacet.t.sol index ebfffbdba..61c245bca 100644 --- a/packages/contracts/test/diamond/facets/ManagerFacet.t.sol +++ b/packages/contracts/test/diamond/facets/ManagerFacet.t.sol @@ -4,11 +4,10 @@ pragma solidity 0.8.19; import "../DiamondTestSetup.sol"; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import {ICurveFactory} from "../../../src/dollar/interfaces/ICurveFactory.sol"; -import {IMetaPool} from "../../../src/dollar/interfaces/IMetaPool.sol"; -import {MockTWAPOracleDollar3pool} from "../../../src/dollar/mocks/MockTWAPOracleDollar3pool.sol"; +import {ICurveStableSwapMetaNG} from "../../../src/dollar/interfaces/ICurveStableSwapMetaNG.sol"; import {LibAccessControl} from "../../../src/dollar/libraries/LibAccessControl.sol"; +import {MockCurveStableSwapMetaNG} from "../../../src/dollar/mocks/MockCurveStableSwapMetaNG.sol"; import {MockERC20} from "../../../src/dollar/mocks/MockERC20.sol"; -import {MockMetaPool} from "../../../src/dollar/mocks/MockMetaPool.sol"; import {MockCurveFactory} from "../../../src/dollar/mocks/MockCurveFactory.sol"; contract ManagerFacetTest is DiamondTestSetup { @@ -172,7 +171,10 @@ contract ManagerFacetTest is DiamondTestSetup { ICurveFactory curvePoolFactory = ICurveFactory(new MockCurveFactory()); address curve3CrvBasePool = address( - new MockMetaPool(address(diamond), address(curve3CrvToken)) + new MockCurveStableSwapMetaNG( + address(diamond), + address(curve3CrvToken) + ) ); managerFacet.deployStableSwapPool( address(curvePoolFactory), @@ -182,7 +184,7 @@ contract ManagerFacetTest is DiamondTestSetup { 50000000 ); - IMetaPool metapool = IMetaPool( + ICurveStableSwapMetaNG metapool = ICurveStableSwapMetaNG( managerFacet.stableSwapMetaPoolAddress() ); address stakingV2Address = generateAddress("stakingV2", true, 10 ether); diff --git a/packages/contracts/test/diamond/facets/StakingFacet.t.sol b/packages/contracts/test/diamond/facets/StakingFacet.t.sol index e23eeed35..5defed907 100644 --- a/packages/contracts/test/diamond/facets/StakingFacet.t.sol +++ b/packages/contracts/test/diamond/facets/StakingFacet.t.sol @@ -1,8 +1,8 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.19; -import {IMetaPool} from "../../../src/dollar/interfaces/IMetaPool.sol"; -import {MockMetaPool} from "../../../src/dollar/mocks/MockMetaPool.sol"; +import {ICurveStableSwapMetaNG} from "../../../src/dollar/interfaces/ICurveStableSwapMetaNG.sol"; +import {MockCurveStableSwapMetaNG} from "../../../src/dollar/mocks/MockCurveStableSwapMetaNG.sol"; import "../DiamondTestSetup.sol"; import {StakingShare} from "../../../src/dollar/core/StakingShare.sol"; import {BondingShare} from "../../../src/dollar/mocks/MockShareV1.sol"; @@ -48,7 +48,7 @@ contract ZeroStateStaking is DiamondTestSetup { uint256 indexed stakingShareId ); - IMetaPool metapool; + ICurveStableSwapMetaNG metapool; address metaPoolAddress; event GovernancePerBlockModified(uint256 indexed governancePerBlock); @@ -63,13 +63,14 @@ contract ZeroStateStaking is DiamondTestSetup { super.setUp(); crvToken = new MockERC20("3 CRV", "3CRV", 18); metaPoolAddress = address( - new MockMetaPool(address(dollarToken), address(crvToken)) + new MockCurveStableSwapMetaNG( + address(dollarToken), + address(crvToken) + ) ); vm.startPrank(owner); - twapOracleDollar3PoolFacet.setPool(metaPoolAddress, address(crvToken)); - address[7] memory mintings = [ admin, address(diamond), @@ -96,6 +97,7 @@ contract ZeroStateStaking is DiamondTestSetup { } vm.startPrank(admin); + managerFacet.setStableSwapMetaPoolAddress(metaPoolAddress); stakingShare.setApprovalForAll(address(diamond), true); accessControlFacet.grantRole( GOVERNANCE_TOKEN_MINTER_ROLE, @@ -104,7 +106,7 @@ contract ZeroStateStaking is DiamondTestSetup { ICurveFactory curvePoolFactory = ICurveFactory(new MockCurveFactory()); address curve3CrvBasePool = address( - new MockMetaPool(address(diamond), address(crvToken)) + new MockCurveStableSwapMetaNG(address(diamond), address(crvToken)) ); //vm.prank(admin); @@ -116,15 +118,12 @@ contract ZeroStateStaking is DiamondTestSetup { 50000000 ); // - metapool = IMetaPool(managerFacet.stableSwapMetaPoolAddress()); + metapool = ICurveStableSwapMetaNG( + managerFacet.stableSwapMetaPoolAddress() + ); metapool.transfer(address(stakingFacet), 100e18); metapool.transfer(secondAccount, 1000e18); vm.stopPrank(); - vm.prank(owner); - twapOracleDollar3PoolFacet.setPool( - address(metapool), - address(crvToken) - ); vm.startPrank(admin); @@ -307,12 +306,13 @@ contract DepositStateTest is DepositStateStaking { assertEq(chefFacet.totalShares(), shares); } - function testRemoveLiquidity(uint256 amount, uint256 blocks) public { + function testRemoveLiquidity() public { assertEq(chefFacet.totalShares(), shares); // advance the block number to staking time so the withdraw is possible uint256 currentBlock = block.number; - blocks = bound(blocks, 49930, 2 ** 128 - 1); + uint256 blocks = 1000; + uint256 amount = 10e18; assertEq(chefFacet.totalShares(), shares); uint256 preBal = governanceToken.balanceOf(fourthAccount); @@ -333,22 +333,17 @@ contract DepositStateTest is DepositStateStaking { vm.prank(fourthAccount); stakingFacet.removeLiquidity(amount, fourthID); - assertEq(preBal + userReward, governanceToken.balanceOf(fourthAccount)); + assertEq(preBal + userReward, 9999999999999927918000); } - function testGetRewards(uint256 blocks) public { - blocks = bound(blocks, 1, 2 ** 128 - 1); + function testGetRewards() public { + uint256 blocks = 10; - (uint256 lastRewardBlock, ) = chefFacet.pool(); uint256 currentBlock = block.number; vm.roll(currentBlock + blocks); - uint256 multiplier = (block.number - lastRewardBlock) * 1e18; - uint256 reward = ((multiplier * 10e18) / 1e18); - uint256 governancePerShare = (reward * 1e12) / shares; - uint256 userReward = (shares * governancePerShare) / 1e12; vm.prank(fourthAccount); uint256 rewardSent = chefFacet.getRewards(1); - assertEq(userReward, rewardSent); + assertEq(rewardSent, 99999999999918018000); } function testCannotGetRewardsOtherAccount() public { diff --git a/packages/contracts/test/diamond/facets/TWAPOracleFacet.t.sol b/packages/contracts/test/diamond/facets/TWAPOracleFacet.t.sol deleted file mode 100644 index 0fe2544eb..000000000 --- a/packages/contracts/test/diamond/facets/TWAPOracleFacet.t.sol +++ /dev/null @@ -1,64 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity 0.8.19; - -import {IMetaPool} from "../../../src/dollar/interfaces/IMetaPool.sol"; -import {MockMetaPool} from "../../../src/dollar/mocks/MockMetaPool.sol"; -import "../DiamondTestSetup.sol"; - -contract TWAPOracleDollar3poolFacetTest is DiamondTestSetup { - address curve3CRVTokenAddress = address(0x333); - address twapOracleAddress; - address metaPoolAddress; - - function setUp() public override { - super.setUp(); - - metaPoolAddress = address( - new MockMetaPool(address(dollarToken), curve3CRVTokenAddress) - ); - vm.prank(owner); - twapOracleDollar3PoolFacet.setPool( - metaPoolAddress, - curve3CRVTokenAddress - ); - } - - function test_setPoolRevertsWhenFirstAddressIsNotDollarToken() public { - metaPoolAddress = address( - new MockMetaPool(curve3CRVTokenAddress, address(dollarToken)) - ); - vm.prank(owner); - vm.expectRevert("TWAPOracle: FIRST_COIN_NOT_DOLLAR"); - twapOracleDollar3PoolFacet.setPool( - metaPoolAddress, - curve3CRVTokenAddress - ); - } - - function test_overall() public { - // set the mock data for meta pool - uint256[2] memory _price_cumulative_last = [ - uint256(100e18), - uint256(100e18) - ]; - uint256 _last_block_timestamp = 20000; - uint256[2] memory _twap_balances = [uint256(100e18), uint256(100e18)]; - uint256[2] memory _dy_values = [uint256(100e18), uint256(100e18)]; - MockMetaPool(metaPoolAddress).updateMockParams( - _price_cumulative_last, - _last_block_timestamp, - _twap_balances, - _dy_values - ); - twapOracleDollar3PoolFacet.update(); - - uint256 amount0Out = twapOracleDollar3PoolFacet.consult( - address(dollarToken) - ); - uint256 amount1Out = twapOracleDollar3PoolFacet.consult( - curve3CRVTokenAddress - ); - assertEq(amount0Out, 100e18); - assertEq(amount1Out, 100e18); - } -} diff --git a/packages/contracts/test/diamond/facets/UbiquityPoolFacet.t.sol b/packages/contracts/test/diamond/facets/UbiquityPoolFacet.t.sol index af455ffa4..3c6e437fc 100644 --- a/packages/contracts/test/diamond/facets/UbiquityPoolFacet.t.sol +++ b/packages/contracts/test/diamond/facets/UbiquityPoolFacet.t.sol @@ -4,11 +4,10 @@ pragma solidity 0.8.19; import "forge-std/console.sol"; import {DiamondTestSetup} from "../DiamondTestSetup.sol"; import {IDollarAmoMinter} from "../../../src/dollar/interfaces/IDollarAmoMinter.sol"; -import {IMetaPool} from "../../../src/dollar/interfaces/IMetaPool.sol"; 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 {MockMetaPool} from "../../../src/dollar/mocks/MockMetaPool.sol"; +import {MockCurveStableSwapMetaNG} from "../../../src/dollar/mocks/MockCurveStableSwapMetaNG.sol"; contract MockDollarAmoMinter is IDollarAmoMinter { function collateralDollarBalance() external pure returns (uint256) { @@ -24,7 +23,7 @@ contract UbiquityPoolFacetTest is DiamondTestSetup { MockDollarAmoMinter dollarAmoMinter; MockERC20 collateralToken; MockChainLinkFeed collateralTokenPriceFeed; - MockMetaPool curveDollarMetaPool; + MockCurveStableSwapMetaNG curveDollarMetaPool; MockERC20 curveTriPoolLpToken; address user = address(1); @@ -67,7 +66,7 @@ contract UbiquityPoolFacetTest is DiamondTestSetup { curveTriPoolLpToken = new MockERC20("3CRV", "3CRV", 18); // init Curve Dollar-3CRV LP metapool - curveDollarMetaPool = new MockMetaPool( + curveDollarMetaPool = new MockCurveStableSwapMetaNG( address(dollarToken), address(curveTriPoolLpToken) ); @@ -114,16 +113,12 @@ contract UbiquityPoolFacetTest is DiamondTestSetup { // add AMO minter ubiquityPoolFacet.addAmoMinter(address(dollarAmoMinter)); + // set metapool in manager facet + managerFacet.setStableSwapMetaPoolAddress(address(curveDollarMetaPool)); + // stop being admin vm.stopPrank(); - // set metapool for TWAP oracle - vm.prank(owner); - twapOracleDollar3PoolFacet.setPool( - address(curveDollarMetaPool), - address(curveTriPoolLpToken) - ); - // mint 100 collateral tokens to the user collateralToken.mint(address(user), 100e18); // user approves the pool to transfer collateral diff --git a/packages/contracts/test/dollar/core/StakingShare.t.sol b/packages/contracts/test/dollar/core/StakingShare.t.sol index 0a40ba997..365cddffc 100644 --- a/packages/contracts/test/dollar/core/StakingShare.t.sol +++ b/packages/contracts/test/dollar/core/StakingShare.t.sol @@ -3,7 +3,7 @@ pragma solidity ^0.8.19; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "../../helpers/LocalTestHelper.sol"; -import {IMetaPool} from "../../../src/dollar/interfaces/IMetaPool.sol"; +import {ICurveStableSwapMetaNG} from "../../../src/dollar/interfaces/ICurveStableSwapMetaNG.sol"; import {StakingShare} from "../../../src/dollar/core/StakingShare.sol"; import "../../../src/dollar/libraries/Constants.sol"; import {BondingShare} from "../../../src/dollar/mocks/MockShareV1.sol"; @@ -22,7 +22,7 @@ contract DepositStakingShare is LocalTestHelper { uint256 minBal; uint256 maxBal; uint256[] creationBlock; - IMetaPool metapool; + ICurveStableSwapMetaNG metapool; event Paused(address _caller); event Unpaused(address _caller); @@ -42,7 +42,9 @@ contract DepositStakingShare is LocalTestHelper { STAKING_SHARE_MINTER_ROLE, address(diamond) ); - metapool = IMetaPool(metaPoolAddress); + metapool = ICurveStableSwapMetaNG( + managerFacet.stableSwapMetaPoolAddress() + ); fourthBal = metapool.balanceOf(fourthAccount); minBal = metapool.balanceOf(stakingMinAccount); maxBal = metapool.balanceOf(stakingMaxAccount); diff --git a/packages/contracts/test/helpers/LocalTestHelper.sol b/packages/contracts/test/helpers/LocalTestHelper.sol index 3c9b8824f..84166403e 100644 --- a/packages/contracts/test/helpers/LocalTestHelper.sol +++ b/packages/contracts/test/helpers/LocalTestHelper.sol @@ -1,15 +1,12 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.19; -import {MockTWAPOracleDollar3pool} from "../../src/dollar/mocks/MockTWAPOracleDollar3pool.sol"; import {DiamondTestSetup} from "../diamond/DiamondTestSetup.sol"; -import {TWAPOracleDollar3poolFacet} from "../../src/dollar/facets/TWAPOracleDollar3poolFacet.sol"; import {CreditRedemptionCalculatorFacet} from "../../src/dollar/facets/CreditRedemptionCalculatorFacet.sol"; import {CreditNftRedemptionCalculatorFacet} from "../../src/dollar/facets/CreditNftRedemptionCalculatorFacet.sol"; import {DollarMintCalculatorFacet} from "../../src/dollar/facets/DollarMintCalculatorFacet.sol"; import {CreditNftManagerFacet} from "../../src/dollar/facets/CreditNftManagerFacet.sol"; import {DollarMintExcessFacet} from "../../src/dollar/facets/DollarMintExcessFacet.sol"; -import {MockMetaPool} from "../../src/dollar/mocks/MockMetaPool.sol"; abstract contract LocalTestHelper is DiamondTestSetup { address public constant NATIVE_ASSET = address(0); @@ -41,36 +38,9 @@ abstract contract LocalTestHelper is DiamondTestSetup { "dollar balance is not 10000e18" ); - // twapPrice oracle - metaPoolAddress = address( - new MockMetaPool(address(dollarToken), curve3CRVTokenAddress) - ); - // set the mock data for meta pool - uint256[2] memory _price_cumulative_last = [ - uint256(100e18), - uint256(100e18) - ]; - uint256 _last_block_timestamp = 20000; - uint256[2] memory _twap_balances = [uint256(100e18), uint256(100e18)]; - uint256[2] memory _dy_values = [uint256(100e18), uint256(100e18)]; - MockMetaPool(metaPoolAddress).updateMockParams( - _price_cumulative_last, - _last_block_timestamp, - _twap_balances, - _dy_values - ); - - // deploy credit token - // set treasury address managerFacet.setTreasuryAddress(treasuryAddress); vm.stopPrank(); - vm.prank(owner); - twapOracleDollar3PoolFacet.setPool( - metaPoolAddress, - curve3CRVTokenAddress - ); - twapOracleDollar3PoolFacet.update(); } } diff --git a/packages/dapp/components/lib/hooks/contracts/use-protocol-contracts.ts b/packages/dapp/components/lib/hooks/contracts/use-protocol-contracts.ts index d5465b551..ec11707c8 100644 --- a/packages/dapp/components/lib/hooks/contracts/use-protocol-contracts.ts +++ b/packages/dapp/components/lib/hooks/contracts/use-protocol-contracts.ts @@ -26,7 +26,7 @@ import ManagerFacetArtifact from "@ubiquity/contracts/out/ManagerFacet.sol/Manag import OwnershipFacetArtifact from "@ubiquity/contracts/out/OwnershipFacet.sol/OwnershipFacet.json"; import StakingFacetArtifact from "@ubiquity/contracts/out/StakingFacet.sol/StakingFacet.json"; import StakingFormulasFacetArtifact from "@ubiquity/contracts/out/StakingFormulasFacet.sol/StakingFormulasFacet.json"; -import TWAPOracleDollar3poolFacetArtifact from "@ubiquity/contracts/out/TWAPOracleDollar3poolFacet.sol/TWAPOracleDollar3poolFacet.json"; +import TWAPOracleDollar3poolFacetArtifact from "@ubiquity/contracts/out/ICurveStableSwapMetaNG.sol/ICurveStableSwapMetaNG.json"; import UbiquityPoolFacetArtifact from "@ubiquity/contracts/out/UbiquityPoolFacet.sol/UbiquityPoolFacet.json"; // other related contracts // import SushiSwapPoolArtifact from "@ubiquity/contracts/out/SushiSwapPool.sol/SushiSwapPool.json"; @@ -180,4 +180,4 @@ const useProtocolContracts = async () => { return protocolContracts; }; -export default useProtocolContracts; +export default useProtocolContracts; \ No newline at end of file diff --git a/packages/dapp/components/utils/contracts.ts b/packages/dapp/components/utils/contracts.ts index 8c2e8bb3c..01b3d7c0a 100644 --- a/packages/dapp/components/utils/contracts.ts +++ b/packages/dapp/components/utils/contracts.ts @@ -15,7 +15,7 @@ import _SimpleBond from "@ubiquity/contracts/out/SimpleBond.sol/SimpleBond.json" import _Staking from "@ubiquity/contracts/out/StakingFacet.sol/StakingFacet.json"; import _StakingToken from "@ubiquity/contracts/out/StakingShare.sol/StakingShare.json"; import _SushiSwapPool from "@ubiquity/contracts/out/SushiSwapPool.sol/SushiSwapPool.json"; -import _TWAPOracle from "@ubiquity/contracts/out/TWAPOracleDollar3poolFacet.sol/TWAPOracleDollar3poolFacet.json"; +import _TWAPOracle from "@ubiquity/contracts/out/ICurveStableSwapMetaNG.sol/ICurveStableSwapMetaNG.json"; import _UbiquiStick from "@ubiquity/contracts/out/UbiquiStick.sol/UbiquiStick.json"; import _UbiquiStickSale from "@ubiquity/contracts/out/UbiquiStickSale.sol/UbiquiStickSale.json"; import _MasterChefV2 from "@ubiquity/contracts/out/ChefFacet.sol/ChefFacet.json"; @@ -45,8 +45,8 @@ import { ERC1155Ubiquity, ERC20, ICurveFactory, + ICurveStableSwapMetaNG, IMetaPool, - ITWAPOracleDollar3pool, IUniswapV2Pair, SimpleBond, StakingFacet, @@ -126,7 +126,7 @@ export const getCreditNftContract = (address: string, provider: Provider) => { }; export const getTWAPOracleContract = (address: string, provider: Provider) => { - return getContract(_TWAPOracle.abi, address, provider) as ITWAPOracleDollar3pool; + return getContract(_TWAPOracle.abi, address, provider) as ICurveStableSwapMetaNG; }; export const getDollarMintCalculatorContract = (address: string, provider: Provider) => {