Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add oracle migrations #855

Merged
merged 4 commits into from
Dec 26, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 20 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,9 +88,22 @@ ADMIN_PRIVATE_KEY="0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b7
# By default set to LUSD address in ethereum mainnet.
# - mainnet/anvil(forked from mainnet): 0x5f98805A4E8be255a32880FDeC7F6728C6568bA0 (LUSD)
# - testnet: 0x3e622317f8C93f7328350cF0B56d9eD4C620C5d6 (DAI)
# NOTICE: LUSD token is not deployed to sepolia testnet so we test DAI token instead which is deployed to testnet
# NOTICE: LUSD token is not deployed to sepolia testnet so we use DAI instead which is deployed to testnet
COLLATERAL_TOKEN_ADDRESS="0x5f98805A4E8be255a32880FDeC7F6728C6568bA0"

# Collateral token price feed address from chainlink.
# By default set to LUSD/USD price feed deployed on ethereum mainnet.
# - mainnet: uses already deployed LUSD/USD chainlink price feed
# - testnet/anvil: deploys LUSD/USD chainlink price feed from scratch
COLLATERAL_TOKEN_CHAINLINK_PRICE_FEED_ADDRESS="0x3D7aE7E594f2f2091Ad8798313450130d0Aba3a0"

# Curve metapool address (Dollar-3CRVLP).
# By default set to the old Dollar-3CRV metapool which is about to be redeployed when
# new Dollar token is deployed.
# - mainnet: uses old Dollar-3CRVLP address
# - testnet/anvil: deploys metapool from scratch
CURVE_DOLLAR_METAPOOL_ADDRESS="0x20955CB69Ae1515962177D164dfC9522feef567E"

# Owner private key (grants access to updating Diamond facets and setting TWAP oracle address).
# By default set to the private key from the 0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266 address
# which is the 1st address derived from test mnemonic "test test test test test test test test test test test junk".
Expand All @@ -101,6 +114,12 @@ OWNER_PRIVATE_KEY="0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f
# - testnet: https://ethereum-sepolia.publicnode.com
# - mainnet: https://eth.ubq.fi/v1/mainnet
RPC_URL="http://127.0.0.1:8545"

# 3CRV LP token address (which you get if you deposit to Curve's TriPool (DAI/USDC/USDT)).
# By default set to 3CRV LP token address from mainnet.
# - mainet: uses values set in TOKEN_3CRV_ADDRESS
# - testnet/anvil: deploys 3CRV LP token from scratch
TOKEN_3CRV_ADDRESS="0x6c3F90f043a72FA612cbac8115EE7e52BDe6E490"
```

We provide an `.env.example` file pre-set with recommend testing environment variables but you are free to modify or experiment with different values on your local branch.
Expand Down
1 change: 1 addition & 0 deletions cspell.json
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@
"connectkit",
"Consts",
"Cpath",
"CRVLP",
"crytic",
"Csvg",
"delegatecall",
Expand Down
19 changes: 19 additions & 0 deletions packages/contracts/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,19 @@ ADMIN_PRIVATE_KEY="0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b7
# NOTICE: LUSD token is not deployed to sepolia testnet so we use DAI instead which is deployed to testnet
COLLATERAL_TOKEN_ADDRESS="0x5f98805A4E8be255a32880FDeC7F6728C6568bA0"

# Collateral token price feed address from chainlink.
# By default set to LUSD/USD price feed deployed on ethereum mainnet.
# - mainnet: uses already deployed LUSD/USD chainlink price feed
# - testnet/anvil: deploys LUSD/USD chainlink price feed from scratch
COLLATERAL_TOKEN_CHAINLINK_PRICE_FEED_ADDRESS="0x3D7aE7E594f2f2091Ad8798313450130d0Aba3a0"

# Curve metapool address (Dollar-3CRVLP).
# By default set to the old Dollar-3CRV metapool which is about to be redeployed when
# new Dollar token is deployed.
# - mainnet: uses old Dollar-3CRVLP address
# - testnet/anvil: deploys metapool from scratch
CURVE_DOLLAR_METAPOOL_ADDRESS="0x20955CB69Ae1515962177D164dfC9522feef567E"

# Owner private key (grants access to updating Diamond facets and setting TWAP oracle address).
# By default set to the private key from the 0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266 address
# which is the 1st address derived from test mnemonic "test test test test test test test test test test test junk".
Expand All @@ -20,3 +33,9 @@ OWNER_PRIVATE_KEY="0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f
# - testnet: https://ethereum-sepolia.publicnode.com
# - mainnet: https://eth.ubq.fi/v1/mainnet
RPC_URL="http://127.0.0.1:8545"

# 3CRV LP token address (which you get if you deposit to Curve's TriPool (DAI/USDC/USDT)).
# By default set to 3CRV LP token address from mainnet.
# - mainet: uses value set in TOKEN_3CRV_ADDRESS
# - testnet/anvil: deploys 3CRV LP token from scratch
TOKEN_3CRV_ADDRESS="0x6c3F90f043a72FA612cbac8115EE7e52BDe6E490"
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;

import {AggregatorV3Interface} from "@chainlink/interfaces/AggregatorV3Interface.sol";
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 {Diamond, DiamondArgs} from "../../src/dollar/Diamond.sol";
Expand All @@ -16,10 +18,14 @@ import {UbiquityPoolFacet} from "../../src/dollar/facets/UbiquityPoolFacet.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 {MockERC20} from "../../src/dollar/mocks/MockERC20.sol";
import {MockMetaPool} from "../../src/dollar/mocks/MockMetaPool.sol";
import {DiamondTestHelper} from "../../test/helpers/DiamondTestHelper.sol";

/**
Expand Down Expand Up @@ -89,6 +95,14 @@ contract DiamondInit is Modifiers {
* - StakingFormulasFacet (staking is not a part of the initial deployment)
*/
contract Deploy001_Diamond_Dollar is Script, DiamondTestHelper {
// env variables
uint256 adminPrivateKey;
uint256 ownerPrivateKey;
address collateralTokenAddress;

// threshold in seconds when price feed response should be considered stale
uint256 CHAINLINK_PRICE_FEED_THRESHOLD;

// Dollar related contracts
UbiquityDollarToken public dollarToken;
ERC1967Proxy public proxyDollarToken;
Expand All @@ -106,6 +120,11 @@ contract Deploy001_Diamond_Dollar is Script, DiamondTestHelper {
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

// selectors for all of the facets
bytes4[] selectorsOfAccessControlFacet;
bytes4[] selectorsOfDiamondCutFacet;
Expand All @@ -117,11 +136,9 @@ contract Deploy001_Diamond_Dollar is Script, DiamondTestHelper {

function run() public virtual {
// read env variables
uint256 adminPrivateKey = vm.envUint("ADMIN_PRIVATE_KEY");
uint256 ownerPrivateKey = vm.envUint("OWNER_PRIVATE_KEY");
address collateralTokenAddress = vm.envAddress(
"COLLATERAL_TOKEN_ADDRESS"
);
adminPrivateKey = vm.envUint("ADMIN_PRIVATE_KEY");
ownerPrivateKey = vm.envUint("OWNER_PRIVATE_KEY");
collateralTokenAddress = vm.envAddress("COLLATERAL_TOKEN_ADDRESS");

address adminAddress = vm.addr(adminPrivateKey);
address ownerAddress = vm.addr(ownerPrivateKey);
Expand Down Expand Up @@ -276,8 +293,9 @@ contract Deploy001_Diamond_Dollar is Script, DiamondTestHelper {
// add collateral token (users can mint/redeem Dollars in exchange for collateral)
uint256 poolCeiling = 10_000e18; // max 10_000 of collateral tokens is allowed
ubiquityPoolFacet.addCollateralToken(
collateralTokenAddress,
poolCeiling
collateralTokenAddress, // collateral token address
address(chainLinkPriceFeedLusd), // chainlink LUSD/USD price feed address
poolCeiling // pool ceiling amount
);
// enable collateral at index 0
ubiquityPoolFacet.toggleCollateral(0);
Expand All @@ -295,6 +313,12 @@ contract Deploy001_Diamond_Dollar is Script, DiamondTestHelper {
// stop sending admin transactions
vm.stopBroadcast();

//================================================================================
// Oracles (Curve Dollar-3CRVLP metapool + LUSD/USD chainlink price feed) setup
//================================================================================

initOracles();

//==================
// Dollar deploy
//==================
Expand Down Expand Up @@ -332,4 +356,117 @@ contract Deploy001_Diamond_Dollar is Script, DiamondTestHelper {
// stop sending admin transactions
vm.stopBroadcast();
}

/**
* @notice Initializes oracle related contracts
*
* @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)
*
* Development migration deploys (for ease of debugging) mocks of:
* - Chainlink price feed contract
* - 3CRVLP ERC20 token
* - Curve's Dollar-3CRVLP metapool contract
*/
function initOracles() public virtual {
//========================================
// Chainlink LUSD/USD price feed deploy
//========================================

// start sending owner transactions
vm.startBroadcast(ownerPrivateKey);

// deploy LUSD/USD chainlink mock price feed
chainLinkPriceFeedLusd = new MockChainLinkFeed();

// stop sending owner transactions
vm.stopBroadcast();

//=======================================
// Chainlink LUSD/USD price feed setup
//=======================================

// 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
100_000_000, // answer, 100_000_000 = $1.00 (chainlink 8 decimals answer is converted to 6 decimals used in UbiquityPool)
block.timestamp, // started at
block.timestamp, // updated at
1 // answered in round
);

UbiquityPoolFacet ubiquityPoolFacet = UbiquityPoolFacet(
address(diamond)
);

// set price feed address and threshold in seconds
ubiquityPoolFacet.setCollateralChainLinkPriceFeed(
collateralTokenAddress, // 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 mock 3CRV-LP token
curveTriPoolLpToken = new MockERC20(
"Curve.fi DAI/USDC/USDT",
"3Crv",
18
);

// deploy mock Curve's Dollar-3CRVLP metapool
curveDollarMetaPool = new MockMetaPool(
address(dollarToken),
address(curveTriPoolLpToken)
);

// stop sending owner transactions
vm.stopBroadcast();

//========================================
// Curve's Dollar-3CRVLP metapool setup
//========================================

// start sending owner transactions
vm.startBroadcast(ownerPrivateKey);

TWAPOracleDollar3poolFacet twapOracleDollar3PoolFacet = TWAPOracleDollar3poolFacet(
address(diamond)
);

// set Curve Dollar-3CRVLP pool in the diamond storage
twapOracleDollar3PoolFacet.setPool(
address(curveDollarMetaPool),
address(curveTriPoolLpToken)
);

// fetch latest Dollar price from Curve's Dollar-3CRVLP metapool
twapOracleDollar3PoolFacet.update();

// stop sending owner transactions
vm.stopBroadcast();
}
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,112 @@
// 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 {TWAPOracleDollar3poolFacet} from "../../src/dollar/facets/TWAPOracleDollar3poolFacet.sol";
import {UbiquityPoolFacet} from "../../src/dollar/facets/UbiquityPoolFacet.sol";
import {IMetaPool} from "../../src/dollar/interfaces/IMetaPool.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 development and mainnet
// is identical both for testnet/development and mainnet
super.run();
}

/**
* @notice Initializes oracle related contracts
*
* @dev We override `initOracles()` 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
* - Curve's Dollar-3CRVLP metapool contract
*/
function initOracles() public override {
// read env variables
address chainlinkPriceFeedAddress = vm.envAddress(
"COLLATERAL_TOKEN_CHAINLINK_PRICE_FEED_ADDRESS"
);
address token3CrvAddress = vm.envAddress("TOKEN_3CRV_ADDRESS");
address curveDollarMetapoolAddress = vm.envAddress(
"CURVE_DOLLAR_METAPOOL_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(
collateralTokenAddress, // 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 setup
//========================================

// start sending owner transactions
vm.startBroadcast(ownerPrivateKey);

// 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)
);

// fetch latest Dollar price from Curve's Dollar-3CRVLP metapool
twapOracleDollar3PoolFacet.update();
*/

// stop sending owner transactions
vm.stopBroadcast();
}
}
Loading
Loading