From 2eddcfb55b36b43d0000788d070925b81d328b5a Mon Sep 17 00:00:00 2001 From: rndquu <119500907+rndquu@users.noreply.github.com> Date: Thu, 11 Apr 2024 18:29:28 +0300 Subject: [PATCH] feat: fractional mainnet migration (#924) * refactor: use a single migration * feat: add fractional migration * fix: set Governance token address --- README.md | 14 + cspell.json | 4 +- packages/contracts/.env.example | 14 + ...Deploy001_Diamond_Dollar_Governance.s.sol} | 188 +++++++++++-- .../development/Deploy002_Governance.s.sol | 42 --- .../migrations/development/deploy.sh | 7 +- .../mainnet/Deploy001_Diamond_Dollar.s.sol | 143 ---------- .../Deploy001_Diamond_Dollar_Governance.s.sol | 266 ++++++++++++++++++ .../mainnet/Deploy002_Governance.s.sol | 27 -- .../contracts/migrations/mainnet/deploy.sh | 7 +- 10 files changed, 471 insertions(+), 241 deletions(-) rename packages/contracts/migrations/development/{Deploy001_Diamond_Dollar.s.sol => Deploy001_Diamond_Dollar_Governance.s.sol} (75%) delete mode 100644 packages/contracts/migrations/development/Deploy002_Governance.s.sol delete mode 100644 packages/contracts/migrations/mainnet/Deploy001_Diamond_Dollar.s.sol create mode 100644 packages/contracts/migrations/mainnet/Deploy001_Diamond_Dollar_Governance.s.sol delete mode 100644 packages/contracts/migrations/mainnet/Deploy002_Governance.s.sol diff --git a/README.md b/README.md index 4f436f29f..fb53a4aaa 100644 --- a/README.md +++ b/README.md @@ -96,6 +96,20 @@ COLLATERAL_TOKEN_ADDRESS="0x5f98805A4E8be255a32880FDeC7F6728C6568bA0" # - testnet/anvil: deploys LUSD/USD chainlink price feed from scratch COLLATERAL_TOKEN_CHAINLINK_PRICE_FEED_ADDRESS="0x3D7aE7E594f2f2091Ad8798313450130d0Aba3a0" +# Curve's Governance/WETH pool address. +# Used to fetch Governance/ETH price from built-in oracle. +# By default set to already deployed (production ready) Governance/WETH pool address. +# - mainnet: uses already deployed Governance/ETH pool address +# - testnet/anvil: deploys Governance/WETH pool from scratch +CURVE_GOVERNANCE_WETH_POOL_ADDRESS="0xaCDc85AFCD8B83Eb171AFFCbe29FaD204F6ae45C" + +# Chainlink price feed address for ETH/USD pair. +# Used to calculate Governance token price in USD. +# By default set to ETH/USD price feed deployed on ethereum mainnet. +# - mainnet: uses already deployed ETH/USD chainlink price feed +# - testnet/anvil: deploys ETH/USD chainlink price feed from scratch +ETH_USD_CHAINLINK_PRICE_FEED_ADDRESS="0x5f4eC3Df9cbd43714FE2740f5E3616155c5b8419" + # Dollar amount in wei minted initially to provide liquidity to the Dollar-3CRV metapool # By default set to 25k Dollar tokens INITIAL_DOLLAR_MINT_AMOUNT_WEI="25000000000000000000000" diff --git a/cspell.json b/cspell.json index 326d1e1ac..40f83956d 100644 --- a/cspell.json +++ b/cspell.json @@ -11,8 +11,8 @@ "**/cspell.json", "**/node_modules/**", "packages/contracts/src/deprecated/*", - "packages/contracts/migrations/development/Deploy002_Governance.s.sol", - "packages/contracts/migrations/mainnet/Deploy002_Governance.s.sol" + "packages/contracts/migrations/development/Deploy001_Diamond_Dollar_Governance.s.sol", + "packages/contracts/migrations/mainnet/Deploy001_Diamond_Dollar_Governance.s.sol" ], "readonly": true, "useGitignore": true, diff --git a/packages/contracts/.env.example b/packages/contracts/.env.example index fdc2fe9e1..8c76dc042 100644 --- a/packages/contracts/.env.example +++ b/packages/contracts/.env.example @@ -15,6 +15,20 @@ COLLATERAL_TOKEN_ADDRESS="0x5f98805A4E8be255a32880FDeC7F6728C6568bA0" # - testnet/anvil: deploys LUSD/USD chainlink price feed from scratch COLLATERAL_TOKEN_CHAINLINK_PRICE_FEED_ADDRESS="0x3D7aE7E594f2f2091Ad8798313450130d0Aba3a0" +# Curve's Governance/WETH pool address. +# Used to fetch Governance/ETH price from built-in oracle. +# By default set to Governance/WETH pool address deployed on ethereum mainnet. +# - mainnet: uses already deployed (production ready) Governance/ETH pool address +# - testnet/anvil: deploys Governance/WETH pool from scratch +CURVE_GOVERNANCE_WETH_POOL_ADDRESS="0xaCDc85AFCD8B83Eb171AFFCbe29FaD204F6ae45C" + +# Chainlink price feed address for ETH/USD pair. +# Used to calculate Governance token price in USD. +# By default set to ETH/USD price feed deployed on ethereum mainnet. +# - mainnet: uses already deployed ETH/USD chainlink price feed +# - testnet/anvil: deploys ETH/USD chainlink price feed from scratch +ETH_USD_CHAINLINK_PRICE_FEED_ADDRESS="0x5f4eC3Df9cbd43714FE2740f5E3616155c5b8419" + # Dollar amount in wei minted initially to provide liquidity to the Dollar-3CRV metapool # By default set to 25k Dollar tokens INITIAL_DOLLAR_MINT_AMOUNT_WEI="25000000000000000000000" diff --git a/packages/contracts/migrations/development/Deploy001_Diamond_Dollar.s.sol b/packages/contracts/migrations/development/Deploy001_Diamond_Dollar_Governance.s.sol similarity index 75% rename from packages/contracts/migrations/development/Deploy001_Diamond_Dollar.s.sol rename to packages/contracts/migrations/development/Deploy001_Diamond_Dollar_Governance.s.sol index d8c274d60..d43bd448d 100644 --- a/packages/contracts/migrations/development/Deploy001_Diamond_Dollar.s.sol +++ b/packages/contracts/migrations/development/Deploy001_Diamond_Dollar_Governance.s.sol @@ -6,6 +6,8 @@ import {IERC165} from "@openzeppelin/contracts/interfaces/IERC165.sol"; import {IERC20} from "@openzeppelin/contracts/interfaces/IERC20.sol"; import {ERC1967Proxy} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; import {Script} from "forge-std/Script.sol"; +import {UbiquityAlgorithmicDollarManager} from "../../src/deprecated/UbiquityAlgorithmicDollarManager.sol"; +import {UbiquityGovernance} from "../../src/deprecated/UbiquityGovernance.sol"; import {Diamond, DiamondArgs} from "../../src/dollar/Diamond.sol"; import {UbiquityDollarToken} from "../../src/dollar/core/UbiquityDollarToken.sol"; import {AccessControlFacet} from "../../src/dollar/facets/AccessControlFacet.sol"; @@ -15,15 +17,17 @@ import {ManagerFacet} from "../../src/dollar/facets/ManagerFacet.sol"; import {OwnershipFacet} from "../../src/dollar/facets/OwnershipFacet.sol"; import {UbiquityPoolFacet} from "../../src/dollar/facets/UbiquityPoolFacet.sol"; import {ICurveStableSwapMetaNG} from "../../src/dollar/interfaces/ICurveStableSwapMetaNG.sol"; +import {ICurveTwocryptoOptimized} from "../../src/dollar/interfaces/ICurveTwocryptoOptimized.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 {DEFAULT_ADMIN_ROLE, DOLLAR_TOKEN_MINTER_ROLE, DOLLAR_TOKEN_BURNER_ROLE, PAUSER_ROLE} from "../../src/dollar/libraries/Constants.sol"; +import {DEFAULT_ADMIN_ROLE, DOLLAR_TOKEN_MINTER_ROLE, DOLLAR_TOKEN_BURNER_ROLE, GOVERNANCE_TOKEN_MINTER_ROLE, GOVERNANCE_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 {MockCurveTwocryptoOptimized} from "../../src/dollar/mocks/MockCurveTwocryptoOptimized.sol"; import {MockERC20} from "../../src/dollar/mocks/MockERC20.sol"; import {DiamondTestHelper} from "../../test/helpers/DiamondTestHelper.sol"; @@ -93,12 +97,16 @@ contract DiamondInit is Modifiers { * - StakingFacet (staking is not a part of the initial deployment) * - StakingFormulasFacet (staking is not a part of the initial deployment) */ -contract Deploy001_Diamond_Dollar is Script, DiamondTestHelper { +contract Deploy001_Diamond_Dollar_Governance is Script, DiamondTestHelper { // env variables uint256 adminPrivateKey; uint256 ownerPrivateKey; uint256 initialDollarMintAmountWei; + // owner and admin addresses derived from private keys store in `.env` file + address adminAddress; + address ownerAddress; + // threshold in seconds when price feed response should be considered stale uint256 CHAINLINK_PRICE_FEED_THRESHOLD; @@ -119,13 +127,19 @@ contract Deploy001_Diamond_Dollar is Script, DiamondTestHelper { UbiquityPoolFacet ubiquityPoolFacetImplementation; // oracle related contracts + AggregatorV3Interface chainLinkPriceFeedEth; // chainlink ETH/USD price feed AggregatorV3Interface chainLinkPriceFeedLusd; // chainlink LUSD/USD price feed IERC20 curveTriPoolLpToken; // Curve's 3CRV-LP token ICurveStableSwapMetaNG curveDollarMetaPool; // Curve's Dollar-3CRVLP metapool + ICurveTwocryptoOptimized curveGovernanceEthPool; // Curve's Governance-WETH crypto pool // collateral ERC20 token used in UbiquityPoolFacet IERC20 collateralToken; + // Governance token related contracts + UbiquityAlgorithmicDollarManager ubiquityAlgorithmicDollarManager; + UbiquityGovernance ubiquityGovernance; + // selectors for all of the facets bytes4[] selectorsOfAccessControlFacet; bytes4[] selectorsOfDiamondCutFacet; @@ -142,8 +156,8 @@ contract Deploy001_Diamond_Dollar is Script, DiamondTestHelper { "INITIAL_DOLLAR_MINT_AMOUNT_WEI" ); - address adminAddress = vm.addr(adminPrivateKey); - address ownerAddress = vm.addr(ownerPrivateKey); + adminAddress = vm.addr(adminPrivateKey); + ownerAddress = vm.addr(ownerPrivateKey); //================== // Before scripts @@ -307,6 +321,8 @@ contract Deploy001_Diamond_Dollar is Script, DiamondTestHelper { ubiquityPoolFacet.setRedemptionDelayBlocks(2); // set mint price threshold to $1.01 and redeem price to $0.99 ubiquityPoolFacet.setPriceThresholds(1010000, 990000); + // set collateral ratio to 95% + ubiquityPoolFacet.setCollateralRatio(950_000); // stop sending admin transactions vm.stopBroadcast(); @@ -388,21 +404,37 @@ contract Deploy001_Diamond_Dollar is Script, DiamondTestHelper { /** * @notice Runs after the main `run()` method * - * @dev Initializes oracle related contracts - * @dev Ubiquity protocol supports 2 oracles: + * @dev Initializes: + * - oracle related contracts + * - Governance token related contracts + * + * @dev Ubiquity protocol supports 4 oracles: * 1. Curve's Dollar-3CRVLP metapool to fetch Dollar prices * 2. Chainlink's price feed (used in UbiquityPool) to fetch collateral token prices in USD + * 3. Chainlink's price feed (used in UbiquityPool) to fetch ETH/USD price + * 4. Curve's Governance-WETH crypto pool to fetch Governance/ETH price * * There are 2 migrations (deployment scripts): - * 1. Development (for usage in testnet and local anvil instance forked from mainnet) + * 1. Development (for usage in testnet and local anvil instance) * 2. Mainnet (for production usage in mainnet) * * Development migration deploys (for ease of debugging) mocks of: - * - Chainlink price feed contract + * - Chainlink collateral price feed contract + * - Chainlink ETH/USD price feed contract * - 3CRVLP ERC20 token + * - WETH token * - Curve's Dollar-3CRVLP metapool contract + * - Curve's Governance-WETH crypto pool contract */ function afterRun() public virtual { + ManagerFacet managerFacet = ManagerFacet(address(diamond)); + UbiquityPoolFacet ubiquityPoolFacet = UbiquityPoolFacet( + address(diamond) + ); + + // set threshold to 10 years (3650 days) for ease of debugging + CHAINLINK_PRICE_FEED_THRESHOLD = 3650 days; + //======================================== // Chainlink LUSD/USD price feed deploy //======================================== @@ -423,9 +455,6 @@ contract Deploy001_Diamond_Dollar is Script, DiamondTestHelper { // start sending admin transactions vm.startBroadcast(adminPrivateKey); - // set threshold to 10 years (3650 days) for ease of debugging - CHAINLINK_PRICE_FEED_THRESHOLD = 3650 days; - // set params for LUSD/USD chainlink price feed mock MockChainLinkFeed(address(chainLinkPriceFeedLusd)).updateMockParams( 1, // round id @@ -435,10 +464,6 @@ contract Deploy001_Diamond_Dollar is Script, DiamondTestHelper { 1 // answered in round ); - UbiquityPoolFacet ubiquityPoolFacet = UbiquityPoolFacet( - address(diamond) - ); - // set price feed address and threshold in seconds ubiquityPoolFacet.setCollateralChainLinkPriceFeed( address(collateralToken), // collateral token address @@ -482,12 +507,141 @@ contract Deploy001_Diamond_Dollar is Script, DiamondTestHelper { // start sending admin transactions vm.startBroadcast(adminPrivateKey); - ManagerFacet managerFacet = ManagerFacet(address(diamond)); - // set curve's metapool in manager facet managerFacet.setStableSwapMetaPoolAddress(address(curveDollarMetaPool)); // stop sending admin transactions vm.stopBroadcast(); + + //=========================================== + // Deploy UbiquityAlgorithmicDollarManager + //=========================================== + + // start sending owner transactions + vm.startBroadcast(ownerPrivateKey); + + ubiquityAlgorithmicDollarManager = new UbiquityAlgorithmicDollarManager( + ownerAddress + ); + + // stop sending owner transactions + vm.stopBroadcast(); + + //============================= + // Deploy UbiquityGovernance + //============================= + + // start sending owner transactions + vm.startBroadcast(ownerPrivateKey); + + ubiquityGovernance = new UbiquityGovernance( + address(ubiquityAlgorithmicDollarManager) + ); + + // stop sending owner transactions + vm.stopBroadcast(); + + //================================== + // UbiquityGovernance token setup + //================================== + + // start sending admin transactions + vm.startBroadcast(adminPrivateKey); + + AccessControlFacet accessControlFacet = AccessControlFacet( + address(diamond) + ); + + // grant diamond Governance token minting and burning rights + accessControlFacet.grantRole( + GOVERNANCE_TOKEN_MINTER_ROLE, + address(diamond) + ); + accessControlFacet.grantRole( + GOVERNANCE_TOKEN_BURNER_ROLE, + address(diamond) + ); + + // set Governance token address in manager facet + managerFacet.setGovernanceTokenAddress(address(ubiquityGovernance)); + + // stop sending admin transactions + vm.stopBroadcast(); + + //======================================= + // Chainlink ETH/USD price feed deploy + //======================================= + + // start sending owner transactions + vm.startBroadcast(ownerPrivateKey); + + // deploy ETH/USD chainlink mock price feed + chainLinkPriceFeedEth = new MockChainLinkFeed(); + + // stop sending owner transactions + vm.stopBroadcast(); + + //====================================== + // Chainlink ETH/USD price feed setup + //====================================== + + // start sending admin transactions + vm.startBroadcast(adminPrivateKey); + + // set ETH/USD price feed mock params + MockChainLinkFeed(address(chainLinkPriceFeedEth)).updateMockParams( + 1, // round id + 3000_00000000, // answer, 3000_00000000 = $3000 (8 decimals) + block.timestamp, // started at + block.timestamp, // updated at + 1 // answered in round + ); + + // set price feed for ETH/USD pair + ubiquityPoolFacet.setEthUsdChainLinkPriceFeed( + address(chainLinkPriceFeedEth), // price feed address + CHAINLINK_PRICE_FEED_THRESHOLD // price feed staleness threshold in seconds + ); + + // stop sending admin transactions + vm.stopBroadcast(); + + //============================================== + // Curve's Governance-WETH crypto pool deploy + //============================================== + + // start sending owner transactions + vm.startBroadcast(ownerPrivateKey); + + // init mock WETH token + IERC20 wethToken = new MockERC20("WETH", "WETH", 18); + + // init Curve Governance-WETH crypto pool + curveGovernanceEthPool = new MockCurveTwocryptoOptimized( + address(ubiquityGovernance), + address(wethToken) + ); + + // stop sending owner transactions + vm.stopBroadcast(); + + //============================================= + // Curve's Governance-WETH crypto pool setup + //============================================= + + // start sending admin transactions + vm.startBroadcast(adminPrivateKey); + + // set ETH/Governance price to 30k in Curve pool mock + MockCurveTwocryptoOptimized(address(curveGovernanceEthPool)) + .updateMockParams(30_000e18); + + // set Governance-ETH pool + ubiquityPoolFacet.setGovernanceEthPoolAddress( + address(curveGovernanceEthPool) + ); + + // stop sending admin transactions + vm.stopBroadcast(); } } diff --git a/packages/contracts/migrations/development/Deploy002_Governance.s.sol b/packages/contracts/migrations/development/Deploy002_Governance.s.sol deleted file mode 100644 index 37f048b7c..000000000 --- a/packages/contracts/migrations/development/Deploy002_Governance.s.sol +++ /dev/null @@ -1,42 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity 0.8.19; - -import {Script} from "forge-std/Script.sol"; -import {UbiquityAlgorithmicDollarManager} from "../../src/deprecated/UbiquityAlgorithmicDollarManager.sol"; -import {UbiquityGovernance} from "../../src/deprecated/UbiquityGovernance.sol"; - -/// @notice Migration contract -contract Deploy002_Governance is Script { - // contracts - UbiquityAlgorithmicDollarManager ubiquityAlgorithmicDollarManager; - UbiquityGovernance ubiquityGovernance; - - function run() public virtual { - // read env variables - uint256 ownerPrivateKey = vm.envUint("OWNER_PRIVATE_KEY"); - - address ownerAddress = vm.addr(ownerPrivateKey); - - // start sending owner transactions - vm.startBroadcast(ownerPrivateKey); - - //=========================================== - // Deploy UbiquityAlgorithmicDollarManager - //=========================================== - - ubiquityAlgorithmicDollarManager = new UbiquityAlgorithmicDollarManager( - ownerAddress - ); - - //=========================================== - // Deploy UbiquityGovernance - //=========================================== - - ubiquityGovernance = new UbiquityGovernance( - address(ubiquityAlgorithmicDollarManager) - ); - - // stop sending owner transactions - vm.stopBroadcast(); - } -} diff --git a/packages/contracts/migrations/development/deploy.sh b/packages/contracts/migrations/development/deploy.sh index aa1275c6b..fb67b349a 100755 --- a/packages/contracts/migrations/development/deploy.sh +++ b/packages/contracts/migrations/development/deploy.sh @@ -3,8 +3,5 @@ # load env variables source .env -# Deploy001_Diamond_Dollar (deploys Diamond and Dollar contracts) -forge script migrations/development/Deploy001_Diamond_Dollar.s.sol:Deploy001_Diamond_Dollar --rpc-url $RPC_URL --broadcast -vvvv - -# Deploy002_Governance (deploys Governance token) -forge script migrations/development/Deploy002_Governance.s.sol:Deploy002_Governance --rpc-url $RPC_URL --broadcast -vvvv +# Deploy001_Diamond_Dollar_Governance (deploys Diamond, Dollar and Governance related contracts) +forge script migrations/development/Deploy001_Diamond_Dollar_Governance.s.sol:Deploy001_Diamond_Dollar_Governance --rpc-url $RPC_URL --broadcast -vvvv diff --git a/packages/contracts/migrations/mainnet/Deploy001_Diamond_Dollar.s.sol b/packages/contracts/migrations/mainnet/Deploy001_Diamond_Dollar.s.sol deleted file mode 100644 index 9f00a53d9..000000000 --- a/packages/contracts/migrations/mainnet/Deploy001_Diamond_Dollar.s.sol +++ /dev/null @@ -1,143 +0,0 @@ -// SPDX-License-Identifier: MIT -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 {ManagerFacet} from "../../src/dollar/facets/ManagerFacet.sol"; -import {UbiquityPoolFacet} from "../../src/dollar/facets/UbiquityPoolFacet.sol"; -import {ICurveStableSwapFactoryNG} from "../../src/dollar/interfaces/ICurveStableSwapFactoryNG.sol"; -import {ICurveStableSwapMetaNG} from "../../src/dollar/interfaces/ICurveStableSwapMetaNG.sol"; - -/// @notice Migration contract -contract Deploy001_Diamond_Dollar is Deploy001_Diamond_Dollar_Development { - function run() public override { - // Run migration for testnet because "Deploy001_Diamond_Dollar" migration - // is identical both for testnet/development and mainnet - super.run(); - } - - /** - * @notice Runs before the main `run()` method - * - * @dev Initializes collateral token - * @dev Collateral token is different for mainnet and development: - * - mainnet: uses LUSD address from `COLLATERAL_TOKEN_ADDRESS` env variables - * - development: deploys mocked ERC20 token from scratch - */ - function beforeRun() public override { - // read env variables - address collateralTokenAddress = vm.envAddress( - "COLLATERAL_TOKEN_ADDRESS" - ); - - //================================= - // Collateral ERC20 token setup - //================================= - - // use existing LUSD contract for mainnet - collateralToken = IERC20(collateralTokenAddress); - } - - /** - * @notice Runs after the main `run()` method - * - * @dev Initializes oracle related contracts - * - * @dev We override `afterRun()` from `Deploy001_Diamond_Dollar_Development` because - * we need to use already deployed contracts while `Deploy001_Diamond_Dollar_Development` - * deploys all oracle related contracts from scratch for ease of debugging. - * - * @dev Ubiquity protocol supports 2 oracles: - * 1. Curve's Dollar-3CRVLP metapool to fetch Dollar prices - * 2. Chainlink's price feed (used in UbiquityPool) to fetch collateral token prices in USD - * - * There are 2 migrations (deployment scripts): - * 1. Development (for usage in testnet and local anvil instance forked from mainnet) - * 2. Mainnet (for production usage in mainnet) - * - * Mainnet (i.e. production) migration uses already deployed contracts for: - * - Chainlink price feed contract - * - 3CRVLP ERC20 token - */ - function afterRun() public override { - // read env variables - address chainlinkPriceFeedAddress = vm.envAddress( - "COLLATERAL_TOKEN_CHAINLINK_PRICE_FEED_ADDRESS" - ); - - //======================================= - // Chainlink LUSD/USD price feed setup - //======================================= - - // start sending admin transactions - vm.startBroadcast(adminPrivateKey); - - // set threshold to 1 day - CHAINLINK_PRICE_FEED_THRESHOLD = 1 days; - - // init LUSD/USD chainlink price feed - chainLinkPriceFeedLusd = AggregatorV3Interface( - chainlinkPriceFeedAddress - ); - - UbiquityPoolFacet ubiquityPoolFacet = UbiquityPoolFacet( - address(diamond) - ); - - // set price feed - ubiquityPoolFacet.setCollateralChainLinkPriceFeed( - address(collateralToken), // collateral token address - address(chainLinkPriceFeedLusd), // price feed address - CHAINLINK_PRICE_FEED_THRESHOLD // price feed staleness threshold in seconds - ); - - // fetch latest prices from chainlink for collateral with index 0 - ubiquityPoolFacet.updateChainLinkCollateralPrice(0); - - // stop sending admin transactions - vm.stopBroadcast(); - - //========================================= - // Curve's Dollar-3CRVLP metapool deploy - //========================================= - - // start sending owner transactions - vm.startBroadcast(ownerPrivateKey); - - // deploy Curve Dollar-3CRV metapool - address curveDollarMetaPoolAddress = ICurveStableSwapFactoryNG( - 0x6A8cbed756804B16E05E741eDaBd5cB544AE21bf - ).deploy_metapool( - 0xbEbc44782C7dB0a1A60Cb6fe97d0b483032FF1C7, // Curve 3pool (DAI-USDT-USDC) address - "Dollar/3CRV", // pool name - "Dollar3CRV", // LP token symbol - address(dollarToken), // main token - 100, // amplification coefficient - 40000000, // trade fee, 0.04% - 20000000000, // off-peg fee multiplier - 2597, // moving average time value, 2597 = 1800 seconds - 0, // metapool implementation index - 0, // asset type - "", // method id for oracle asset type (not applicable for Dollar) - address(0) // token oracle address (not applicable for Dollar) - ); - - // stop sending owner transactions - vm.stopBroadcast(); - - //======================================== - // Curve's Dollar-3CRVLP metapool setup - //======================================== - - // start sending admin transactions - vm.startBroadcast(adminPrivateKey); - - // set curve's metapool in manager facet - ManagerFacet managerFacet = ManagerFacet(address(diamond)); - managerFacet.setStableSwapMetaPoolAddress(curveDollarMetaPoolAddress); - - // stop sending admin transactions - vm.stopBroadcast(); - } -} diff --git a/packages/contracts/migrations/mainnet/Deploy001_Diamond_Dollar_Governance.s.sol b/packages/contracts/migrations/mainnet/Deploy001_Diamond_Dollar_Governance.s.sol new file mode 100644 index 000000000..1a15e0816 --- /dev/null +++ b/packages/contracts/migrations/mainnet/Deploy001_Diamond_Dollar_Governance.s.sol @@ -0,0 +1,266 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.19; + +import {AggregatorV3Interface} from "@chainlink/interfaces/AggregatorV3Interface.sol"; +import {IERC20} from "@openzeppelin/contracts/interfaces/IERC20.sol"; +import {Deploy001_Diamond_Dollar_Governance as Deploy001_Diamond_Dollar_Governance_Development} from "../development/Deploy001_Diamond_Dollar_Governance.s.sol"; +import {UbiquityAlgorithmicDollarManager} from "../../src/deprecated/UbiquityAlgorithmicDollarManager.sol"; +import {UbiquityGovernance} from "../../src/deprecated/UbiquityGovernance.sol"; +import {ManagerFacet} from "../../src/dollar/facets/ManagerFacet.sol"; +import {UbiquityPoolFacet} from "../../src/dollar/facets/UbiquityPoolFacet.sol"; +import {ICurveStableSwapFactoryNG} from "../../src/dollar/interfaces/ICurveStableSwapFactoryNG.sol"; +import {ICurveStableSwapMetaNG} from "../../src/dollar/interfaces/ICurveStableSwapMetaNG.sol"; +import {ICurveTwocryptoOptimized} from "../../src/dollar/interfaces/ICurveTwocryptoOptimized.sol"; + +/// @notice Migration contract +contract Deploy001_Diamond_Dollar_Governance is + Deploy001_Diamond_Dollar_Governance_Development +{ + function run() public override { + // Run migration for testnet because "Deploy001_Diamond_Dollar_Governance" migration + // is identical both for testnet/development and mainnet + super.run(); + } + + /** + * @notice Runs before the main `run()` method + * + * @dev Initializes collateral token + * @dev Collateral token is different for mainnet and development: + * - mainnet: uses LUSD address from `COLLATERAL_TOKEN_ADDRESS` env variables + * - development: deploys mocked ERC20 token from scratch + */ + function beforeRun() public override { + // read env variables + address collateralTokenAddress = vm.envAddress( + "COLLATERAL_TOKEN_ADDRESS" + ); + + //================================= + // Collateral ERC20 token setup + //================================= + + // use existing LUSD contract for mainnet + collateralToken = IERC20(collateralTokenAddress); + } + + /** + * @notice Runs after the main `run()` method + * + * @dev Initializes: + * - oracle related contracts + * - Governance token related contracts + * + * @dev We override `afterRun()` from `Deploy001_Diamond_Dollar_Governance_Development` because + * we need to use already deployed contracts while `Deploy001_Diamond_Dollar_Governance_Development` + * deploys all oracle and Governance token related contracts from scratch for ease of debugging. + * + * @dev Ubiquity protocol supports 4 oracles: + * 1. Curve's Dollar-3CRVLP metapool to fetch Dollar prices + * 2. Chainlink's price feed (used in UbiquityPool) to fetch collateral token prices in USD + * 3. Chainlink's price feed (used in UbiquityPool) to fetch ETH/USD price + * 4. Curve's Governance-WETH crypto pool to fetch Governance/ETH price + * + * There are 2 migrations (deployment scripts): + * 1. Development (for usage in testnet and local anvil instance) + * 2. Mainnet (for production usage in mainnet) + * + * Mainnet (i.e. production) migration uses already deployed contracts for: + * - Chainlink collateral price feed contract + * - UbiquityAlgorithmicDollarManager contract + * - UbiquityGovernance token contract + * - Chainlink ETH/USD price feed + * - Curve's Governance-WETH crypto pool + */ + function afterRun() public override { + // read env variables + address chainlinkPriceFeedAddressEth = vm.envAddress( + "ETH_USD_CHAINLINK_PRICE_FEED_ADDRESS" + ); + address chainlinkPriceFeedAddressLusd = vm.envAddress( + "COLLATERAL_TOKEN_CHAINLINK_PRICE_FEED_ADDRESS" + ); + address curveGovernanceEthPoolAddress = vm.envAddress( + "CURVE_GOVERNANCE_WETH_POOL_ADDRESS" + ); + + // set threshold to 1 hour (default value for ETH/USD and LUSD/USD price feeds) + CHAINLINK_PRICE_FEED_THRESHOLD = 1 hours; + + ManagerFacet managerFacet = ManagerFacet(address(diamond)); + UbiquityPoolFacet ubiquityPoolFacet = UbiquityPoolFacet( + address(diamond) + ); + + //======================================= + // Chainlink LUSD/USD price feed setup + //======================================= + + // start sending admin transactions + vm.startBroadcast(adminPrivateKey); + + // init LUSD/USD chainlink price feed + chainLinkPriceFeedLusd = AggregatorV3Interface( + chainlinkPriceFeedAddressLusd + ); + + // set price feed + ubiquityPoolFacet.setCollateralChainLinkPriceFeed( + address(collateralToken), // collateral token address + address(chainLinkPriceFeedLusd), // price feed address + CHAINLINK_PRICE_FEED_THRESHOLD // price feed staleness threshold in seconds + ); + + // fetch latest prices from chainlink for collateral with index 0 + ubiquityPoolFacet.updateChainLinkCollateralPrice(0); + + // stop sending admin transactions + vm.stopBroadcast(); + + //========================================= + // Curve's Dollar-3CRVLP metapool deploy + //========================================= + + // start sending owner transactions + vm.startBroadcast(ownerPrivateKey); + + // deploy Curve Dollar-3CRV metapool + address curveDollarMetaPoolAddress = ICurveStableSwapFactoryNG( + 0x6A8cbed756804B16E05E741eDaBd5cB544AE21bf + ).deploy_metapool( + 0xbEbc44782C7dB0a1A60Cb6fe97d0b483032FF1C7, // Curve 3pool (DAI-USDT-USDC) address + "Dollar/3CRV", // pool name + "Dollar3CRV", // LP token symbol + address(dollarToken), // main token + 100, // amplification coefficient + 40000000, // trade fee, 0.04% + 20000000000, // off-peg fee multiplier + 2597, // moving average time value, 2597 = 1800 seconds + 0, // metapool implementation index + 0, // asset type + "", // method id for oracle asset type (not applicable for Dollar) + address(0) // token oracle address (not applicable for Dollar) + ); + + // stop sending owner transactions + vm.stopBroadcast(); + + //======================================== + // Curve's Dollar-3CRVLP metapool setup + //======================================== + + // start sending admin transactions + vm.startBroadcast(adminPrivateKey); + + // set curve's metapool in manager facet + managerFacet.setStableSwapMetaPoolAddress(curveDollarMetaPoolAddress); + + // stop sending admin transactions + vm.stopBroadcast(); + + //========================================== + // UbiquityAlgorithmicDollarManager setup + //========================================== + + // using already deployed (on mainnet) UbiquityAlgorithmicDollarManager + ubiquityAlgorithmicDollarManager = UbiquityAlgorithmicDollarManager( + 0x4DA97a8b831C345dBe6d16FF7432DF2b7b776d98 + ); + + //============================ + // UbiquityGovernance setup + //============================ + + // NOTICE: If owner address is `ubq.eth` (i.e. ubiquity deployer) it means that we want to perform + // a real deployment to mainnet so we start sending transactions via `startBroadcast()`. Otherwise + // we're in the forked mainnet anvil instance and the owner is not `ubq.eth` so we can't add "UBQ_MINTER_ROLE" + // and "UBQ_BURNER_ROLE" roles to the diamond contract (because only `ubq.eth` address has this permission). + // Also we can't use "vm.prank()" since it doesn't update the storage but only simulates a call. That is why + // if you're testing on an anvil instance forked from mainnet make sure to add "UBQ_MINTER_ROLE" and "UBQ_BURNER_ROLE" + // roles to the diamond contract manually. Take this command for inspiration: + // ``` + // DIAMOND_ADDRESS=0x9Bb65b12162a51413272d10399282E730822Df44; \ + // UBQ_ETH_ADDRESS=0xefC0e701A824943b469a694aC564Aa1efF7Ab7dd; \ + // UBIQUITY_ALGORITHMIC_DOLLAR_MANAGER=0x4DA97a8b831C345dBe6d16FF7432DF2b7b776d98; \ + // cast rpc anvil_impersonateAccount $UBQ_ETH_ADDRESS; \ + // cast send --unlocked --from $UBQ_ETH_ADDRESS $UBIQUITY_ALGORITHMIC_DOLLAR_MANAGER "grantRole(bytes32,address)" $(cast keccak "UBQ_BURNER_ROLE") $DIAMOND_ADDRESS --rpc-url http://localhost:8545; \ + // cast send --unlocked --from $UBQ_ETH_ADDRESS $UBIQUITY_ALGORITHMIC_DOLLAR_MANAGER "grantRole(bytes32,address)" $(cast keccak "UBQ_MINTER_ROLE") $DIAMOND_ADDRESS --rpc-url http://localhost:8545; \ + // cast rpc anvil_stopImpersonatingAccount $UBQ_ETH_ADDRESS; + // ``` + address ubiquityDeployerAddress = 0xefC0e701A824943b469a694aC564Aa1efF7Ab7dd; + + if (ownerAddress == ubiquityDeployerAddress) { + // Start sending owner transactions + vm.startBroadcast(ownerPrivateKey); + + // Owner (i.e. `ubq.eth` who is admin for UbiquityAlgorithmicDollarManager) grants diamond + // Governance token mint and burn rights + ubiquityAlgorithmicDollarManager.grantRole( + keccak256("UBQ_MINTER_ROLE"), + address(diamond) + ); + ubiquityAlgorithmicDollarManager.grantRole( + keccak256("UBQ_BURNER_ROLE"), + address(diamond) + ); + + // stop sending owner transactions + vm.stopBroadcast(); + } + + // using already deployed (on mainnet) Governance token + ubiquityGovernance = UbiquityGovernance( + 0x4e38D89362f7e5db0096CE44ebD021c3962aA9a0 + ); + + // start sending admin transactions + vm.startBroadcast(adminPrivateKey); + + // admin sets Governance token address in manager facet + managerFacet.setGovernanceTokenAddress(address(ubiquityGovernance)); + + // stop sending admin transactions + vm.stopBroadcast(); + + //====================================== + // Chainlink ETH/USD price feed setup + //====================================== + + // start sending admin transactions + vm.startBroadcast(adminPrivateKey); + + // init ETH/USD chainlink price feed + chainLinkPriceFeedEth = AggregatorV3Interface( + chainlinkPriceFeedAddressEth + ); + + // set price feed for ETH/USD pair + ubiquityPoolFacet.setEthUsdChainLinkPriceFeed( + address(chainLinkPriceFeedEth), // price feed address + CHAINLINK_PRICE_FEED_THRESHOLD // price feed staleness threshold in seconds + ); + + // stop sending admin transactions + vm.stopBroadcast(); + + //============================================= + // Curve's Governance-WETH crypto pool setup + //============================================= + + // start sending admin transactions + vm.startBroadcast(adminPrivateKey); + + // init Curve Governance-WETH crypto pool + curveGovernanceEthPool = ICurveTwocryptoOptimized( + curveGovernanceEthPoolAddress + ); + + // set Governance-ETH pool + ubiquityPoolFacet.setGovernanceEthPoolAddress( + address(curveGovernanceEthPool) + ); + + // stop sending admin transactions + vm.stopBroadcast(); + } +} diff --git a/packages/contracts/migrations/mainnet/Deploy002_Governance.s.sol b/packages/contracts/migrations/mainnet/Deploy002_Governance.s.sol deleted file mode 100644 index ee96fc85c..000000000 --- a/packages/contracts/migrations/mainnet/Deploy002_Governance.s.sol +++ /dev/null @@ -1,27 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity 0.8.19; - -import {Script} from "forge-std/Script.sol"; -import {UbiquityAlgorithmicDollarManager} from "../../src/deprecated/UbiquityAlgorithmicDollarManager.sol"; -import {UbiquityGovernance} from "../../src/deprecated/UbiquityGovernance.sol"; - -/// @notice Migration contract -/// @dev This migration is used only for reference that we use already deployed -/// UbiquityAlgorithmicDollarManager and UbiquityGovernance contracts -contract Deploy002_Governance is Script { - // contracts - UbiquityAlgorithmicDollarManager ubiquityAlgorithmicDollarManager; - UbiquityGovernance ubiquityGovernance; - - function run() public virtual { - // using already deployed (on mainnet) UbiquityAlgorithmicDollarManager - ubiquityAlgorithmicDollarManager = UbiquityAlgorithmicDollarManager( - 0x4DA97a8b831C345dBe6d16FF7432DF2b7b776d98 - ); - - // using already deployed (on mainnet) Governance token - ubiquityGovernance = UbiquityGovernance( - 0x4e38D89362f7e5db0096CE44ebD021c3962aA9a0 - ); - } -} diff --git a/packages/contracts/migrations/mainnet/deploy.sh b/packages/contracts/migrations/mainnet/deploy.sh index 5938e18e6..9f0920521 100755 --- a/packages/contracts/migrations/mainnet/deploy.sh +++ b/packages/contracts/migrations/mainnet/deploy.sh @@ -3,8 +3,5 @@ # load env variables source .env -# Deploy001_Diamond_Dollar (deploys Diamond and Dollar contracts) -forge script migrations/mainnet/Deploy001_Diamond_Dollar.s.sol:Deploy001_Diamond_Dollar --rpc-url $RPC_URL --broadcast -vvvv - -# Deploy002_Governance (use already deployed Governance token) -forge script migrations/mainnet/Deploy002_Governance.s.sol:Deploy002_Governance --rpc-url $RPC_URL --broadcast -vvvv +# Deploy001_Diamond_Dollar_Governance (deploys Diamond, Dollar and Governance related contracts) +forge script migrations/mainnet/Deploy001_Diamond_Dollar_Governance.s.sol:Deploy001_Diamond_Dollar_Governance --rpc-url $RPC_URL --broadcast -vvvv