Skip to content

Commit

Permalink
Merge pull request #855 from rndquu/feat/twap-migration
Browse files Browse the repository at this point in the history
feat: add oracle migrations
  • Loading branch information
rndquu authored Dec 26, 2023
2 parents d0bebb1 + 24dbf9f commit aed79e7
Show file tree
Hide file tree
Showing 7 changed files with 326 additions and 47 deletions.
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();
}
}
101 changes: 100 additions & 1 deletion packages/contracts/migrations/mainnet/Deploy001_Diamond_Dollar.s.sol
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

0 comments on commit aed79e7

Please sign in to comment.