Skip to content

Commit

Permalink
feat: add plain pool migration
Browse files Browse the repository at this point in the history
  • Loading branch information
rndquu committed Apr 25, 2024
1 parent db2a5d0 commit 92549be
Show file tree
Hide file tree
Showing 8 changed files with 152 additions and 53 deletions.
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,13 @@ COLLATERAL_TOKEN_ADDRESS="0x5f98805A4E8be255a32880FDeC7F6728C6568bA0"

# Collateral token price feed address from chainlink.
# By default set to LUSD/USD price feed deployed on ethereum mainnet.
# This price feed is used in 2 cases:
# 1) To calculate collateral price in USD
# 2) To calculate Dollar price in USD
# Since collateral token (LUSD) is the same one used in Curve's plain pool (LUSD-Dollar)
# we share the same price feed in:
# 1) `LibUbiquityPool.setCollateralChainLinkPriceFeed()` (to calculate collateral price in USD)
# 2) `LibUbiquityPool.setStableUsdChainLinkPriceFeed()` (to calculate Dollar price in USD)
# - 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"
Expand Down
7 changes: 7 additions & 0 deletions packages/contracts/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,13 @@ COLLATERAL_TOKEN_ADDRESS="0x5f98805A4E8be255a32880FDeC7F6728C6568bA0"

# Collateral token price feed address from chainlink.
# By default set to LUSD/USD price feed deployed on ethereum mainnet.
# This price feed is used in 2 cases:
# 1) To calculate collateral price in USD
# 2) To calculate Dollar price in USD
# Since collateral token (LUSD) is the same one used in Curve's plain pool (LUSD-Dollar)
# we share the same price feed in:
# 1) `LibUbiquityPool.setCollateralChainLinkPriceFeed()` (to calculate collateral price in USD)
# 2) `LibUbiquityPool.setStableUsdChainLinkPriceFeed()` (to calculate Dollar price in USD)
# - 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"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ 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 {UbiquityPoolFacet} from "../../src/dollar/facets/UbiquityPoolFacet.sol";
import {ICurveStableSwapMetaNG} from "../../src/dollar/interfaces/ICurveStableSwapMetaNG.sol";
import {ICurveStableSwapNG} from "../../src/dollar/interfaces/ICurveStableSwapNG.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";
Expand All @@ -26,7 +26,7 @@ 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 {MockCurveStableSwapNG} from "../../src/dollar/mocks/MockCurveStableSwapNG.sol";
import {MockCurveTwocryptoOptimized} from "../../src/dollar/mocks/MockCurveTwocryptoOptimized.sol";
import {MockERC20} from "../../src/dollar/mocks/MockERC20.sol";
import {DiamondTestHelper} from "../../test/helpers/DiamondTestHelper.sol";
Expand Down Expand Up @@ -129,8 +129,7 @@ contract Deploy001_Diamond_Dollar_Governance is Script, DiamondTestHelper {
// 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
ICurveStableSwapNG curveStableDollarPlainPool; // Curve's LUSD-Dollar plain pool
ICurveTwocryptoOptimized curveGovernanceEthPool; // Curve's Governance-WETH crypto pool

// collateral ERC20 token used in UbiquityPoolFacet
Expand Down Expand Up @@ -408,22 +407,22 @@ contract Deploy001_Diamond_Dollar_Governance is Script, DiamondTestHelper {
* - 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
* @dev Ubiquity protocol supports 5 oracles:
* 1. Curve's LUSD-Dollar plain pool to fetch Dollar prices
* 2. Chainlink's price feed (used in UbiquityPool) to fetch LUSD/USD price (for getting Dollar price in USD)
* 3. Chainlink's price feed (used in UbiquityPool) to fetch collateral token prices in USD (for getting collateral price in USD)
* 4. Chainlink's price feed (used in UbiquityPool) to fetch ETH/USD price
* 5. 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)
*
* Development migration deploys (for ease of debugging) mocks of:
* - Chainlink collateral price feed contract
* - Chainlink ETH/USD price feed contract
* - 3CRVLP ERC20 token
* - Chainlink LUSD/USD price feed contract (for getting Dollar and collateral prices in USD)
* - WETH token
* - Curve's Dollar-3CRVLP metapool contract
* - Curve's LUSD-Dollar plain pool contract
* - Curve's Governance-WETH crypto pool contract
*/
function afterRun() public virtual {
Expand Down Expand Up @@ -464,7 +463,7 @@ contract Deploy001_Diamond_Dollar_Governance is Script, DiamondTestHelper {
1 // answered in round
);

// set price feed address and threshold in seconds
// set collateral price feed address and threshold in seconds
ubiquityPoolFacet.setCollateralChainLinkPriceFeed(
address(collateralToken), // collateral token address
address(chainLinkPriceFeedLusd), // price feed address
Expand All @@ -474,41 +473,45 @@ contract Deploy001_Diamond_Dollar_Governance is Script, DiamondTestHelper {
// fetch latest prices from chainlink for collateral with index 0
ubiquityPoolFacet.updateChainLinkCollateralPrice(0);

// set Stable/USD price feed address and threshold in seconds
ubiquityPoolFacet.setStableUsdChainLinkPriceFeed(
address(chainLinkPriceFeedLusd), // price feed address
CHAINLINK_PRICE_FEED_THRESHOLD // price feed staleness threshold in seconds
);

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

//=========================================
// Curve's Dollar-3CRVLP metapool deploy
// Curve's LUSD-Dollar plain pool 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 MockCurveStableSwapMetaNG(
address(dollarToken),
address(curveTriPoolLpToken)
// Deploy mock Curve's LUSD-Dollar plain pool.
// Since we're using LUSD both as collateral and Dollar token pair
// in Curve's plain pool we don't deploy another mock of the "stable" coin
// paired to Dollar and simply use collateral token (i.e. LUSD).
curveStableDollarPlainPool = new MockCurveStableSwapNG(
address(collateralToken),
address(dollarToken)
);

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

//========================================
// Curve's Dollar-3CRVLP metapool setup
// Curve's LUSD-Dollar plain pool setup
//========================================

// start sending admin transactions
vm.startBroadcast(adminPrivateKey);

// set curve's metapool in manager facet
managerFacet.setStableSwapMetaPoolAddress(address(curveDollarMetaPool));
// set curve's plain pool in manager facet
managerFacet.setStableSwapPlainPoolAddress(
address(curveStableDollarPlainPool)
);

// stop sending admin transactions
vm.stopBroadcast();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,18 +55,20 @@ contract Deploy001_Diamond_Dollar_Governance is
* 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
* @dev Ubiquity protocol supports 5 oracles:
* 1. Curve's LUSD-Dollar plain pool to fetch Dollar prices
* 2. Chainlink's price feed (used in UbiquityPool) to fetch LUSD/USD price (for getting Dollar price in USD)
* 3. Chainlink's price feed (used in UbiquityPool) to fetch collateral token prices in USD (for getting collateral price in USD)
* 4. Chainlink's price feed (used in UbiquityPool) to fetch ETH/USD price
* 5. 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
* - Chainlink Stable/USD price feed contract (here "Stable" refers to the LUSD token from Curve's LUSD-Dollar plain pool)
* - UbiquityAlgorithmicDollarManager contract
* - UbiquityGovernance token contract
* - Chainlink ETH/USD price feed
Expand Down Expand Up @@ -104,7 +106,7 @@ contract Deploy001_Diamond_Dollar_Governance is
chainlinkPriceFeedAddressLusd
);

// set price feed
// set collateral price feed
ubiquityPoolFacet.setCollateralChainLinkPriceFeed(
address(collateralToken), // collateral token address
address(chainLinkPriceFeedLusd), // price feed address
Expand All @@ -114,46 +116,68 @@ contract Deploy001_Diamond_Dollar_Governance is
// fetch latest prices from chainlink for collateral with index 0
ubiquityPoolFacet.updateChainLinkCollateralPrice(0);

// set Stable/Dollar price feed
ubiquityPoolFacet.setStableUsdChainLinkPriceFeed(
address(chainLinkPriceFeedLusd), // price feed address
CHAINLINK_PRICE_FEED_THRESHOLD // price feed staleness threshold in seconds
);

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

//=========================================
// Curve's Dollar-3CRVLP metapool deploy
// Curve's LUSD-Dollar plain pool deploy
//=========================================

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

// deploy Curve Dollar-3CRV metapool
address curveDollarMetaPoolAddress = ICurveStableSwapFactoryNG(
// prepare parameters
address[] memory plainPoolCoins = new address[](2);
plainPoolCoins[0] = address(collateralToken);
plainPoolCoins[1] = address(dollarToken);

uint8[] memory plainPoolAssetTypes = new uint8[](2);
plainPoolAssetTypes[0] = 0;
plainPoolAssetTypes[1] = 0;

bytes4[] memory plainPoolMethodIds = new bytes4[](2);
plainPoolMethodIds[0] = bytes4("");
plainPoolMethodIds[1] = bytes4("");

address[] memory plainPoolTokenOracleAddresses = new address[](2);
plainPoolTokenOracleAddresses[0] = address(0);
plainPoolTokenOracleAddresses[1] = address(0);

// deploy Curve LUSD-Dollar plain pool
address curveDollarPlainPoolAddress = ICurveStableSwapFactoryNG(
0x6A8cbed756804B16E05E741eDaBd5cB544AE21bf
).deploy_metapool(
0xbEbc44782C7dB0a1A60Cb6fe97d0b483032FF1C7, // Curve 3pool (DAI-USDT-USDC) address
"Dollar/3CRV", // pool name
"Dollar3CRV", // LP token symbol
address(dollarToken), // main token
).deploy_plain_pool(
"LUSD/Dollar", // pool name
"LUSDDollar", // LP token symbol
plainPoolCoins, // coins used in the pool
100, // amplification coefficient
40000000, // trade fee, 0.04%
4000000, // 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)
0, // plain pool implementation index
plainPoolAssetTypes, // asset types
plainPoolMethodIds, // method ids for oracle asset type (not applicable for Dollar)
plainPoolTokenOracleAddresses // token oracle addresses (not applicable for Dollar)
);

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

//========================================
// Curve's Dollar-3CRVLP metapool setup
// Curve's LUSD-Dollar plain pool setup
//========================================

// start sending admin transactions
vm.startBroadcast(adminPrivateKey);

// set curve's metapool in manager facet
managerFacet.setStableSwapMetaPoolAddress(curveDollarMetaPoolAddress);
// set curve's plain pool in manager facet
managerFacet.setStableSwapPlainPoolAddress(curveDollarPlainPoolAddress);

// stop sending admin transactions
vm.stopBroadcast();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ interface ICurveStableSwapFactoryNG {
* @param _A Amplification coefficient. If set to 0 then bonding curve acts like Uniswap. Any >0 value
* makes the bonding curve to swap at 1:1 constant price, the more `_A` the longer the constant price period.
* Curve recommends set it to 100 for crypto collateralizard stablecoins. This parameter can be updated later.
* @param _fee Trade fee, given as an integer with 1e10 precision, ex: 40000000 = 0.04% fee
* @param _fee Trade fee, given as an integer with 1e10 precision, ex: 4000000 = 0.04% fee
* @param _offpeg_fee_multiplier Off-peg multiplier. Curve recommends set it to `20000000000`. This parameter can be updated
* later. More info: https://docs.curve.fi/stableswap-exchange/stableswap-ng/pools/overview/#dynamic-fees
* @param _ma_exp_time MA time; set as time_in_seconds / ln(2), ex: 866 = 600 seconds, 2597 = 1800 seconds.
Expand Down Expand Up @@ -54,4 +54,47 @@ interface ICurveStableSwapFactoryNG {
bytes4 _method_id,
address _oracle
) external returns (address);

/**
* @notice Deploys a new plain pool
* @param _name Name of the new plain pool, ex: "LUSD/Dollar"
* @param _symbol Symbol for the new pool's LP token, ex: "LUSDDollar"
* @param _coins Array of addresses of the coins being used in the pool
* @param _A Amplification coefficient. If set to 0 then bonding curve acts like Uniswap. Any >0 value
* makes the bonding curve to swap at 1:1 constant price, the more `_A` the longer the constant price period.
* Curve recommends set it to 100 for crypto collateralizard stablecoins. This parameter can be updated later.
* @param _fee Trade fee, given as an integer with 1e10 precision, ex: 4000000 = 0.04% fee
* @param _offpeg_fee_multiplier Off-peg multiplier. Curve recommends set it to `20000000000`. This parameter can be updated
* later. More info: https://docs.curve.fi/stableswap-exchange/stableswap-ng/pools/overview/#dynamic-fees
* @param _ma_exp_time MA time; set as time_in_seconds / ln(2), ex: 866 = 600 seconds, 2597 = 1800 seconds.
* This parameter can be updated later.
* @param _implementation_idx Index of the plain pool implementation to use. Can be retrieved
* via `ICurveStableSwapFactoryNG.pool_implementations()`. There is only 1 plain pool implementation right now
* so use index `0`.
* @param _asset_types Asset types of the pool tokens as an integer. Available asset type indexes:
* - 0: Standard ERC20 token with no additional features
* - 1: Oracle - token with rate oracle (e.g. wstETH)
* - 2: Rebasing - token with rebase (e.g. stETH)
* - 3: ERC4626 - token with convertToAssets method (e.g. sDAI)
* Both Dollar and LUSD are standard ERC20 tokens so we should use asset types with index `0`.
* @param _method_ids Array of first four bytes of the Keccak-256 hash of the function signatures of
* the oracle addresses that give rate oracles. This is applied only to asset type `1` (Oracle).
* For Dollar token deployment set empty.
* @param _oracles Array of rate oracle addresses. This is applied only to asset type `1` (Oracle).
* For Dollar token deployment set empty address.
* @return Deployed plain pool address
*/
function deploy_plain_pool(
string memory _name,
string memory _symbol,
address[] memory _coins,
uint256 _A,
uint256 _fee,
uint256 _offpeg_fee_multiplier,
uint256 _ma_exp_time,
uint256 _implementation_idx,
uint8[] memory _asset_types,
bytes4[] memory _method_ids,
address[] memory _oracles
) external returns (address);
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,10 @@ import {ICurveStableSwapMetaNG} from "./ICurveStableSwapMetaNG.sol";
/**
* @notice Curve's interface for plain pool which contains only USD pegged assets
*/
interface ICurveStableSwapNG is ICurveStableSwapMetaNG {}
interface ICurveStableSwapNG is ICurveStableSwapMetaNG {
function add_liquidity(
uint256[] memory _amounts,
uint256 _min_mint_amount,
address _receiver
) external returns (uint256);
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ contract MockCurveStableSwapMetaNG is ICurveStableSwapMetaNG, MockERC20 {
uint256[2] memory _amounts,
uint256 _min_mint_amount,
address _receiver
) external returns (uint256 result) {
) public returns (uint256 result) {
mint(
_receiver,
_min_mint_amount == 0
Expand Down
9 changes: 9 additions & 0 deletions packages/contracts/src/dollar/mocks/MockCurveStableSwapNG.sol
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,13 @@ contract MockCurveStableSwapNG is
address _token0,
address _token1
) MockCurveStableSwapMetaNG(_token0, _token1) {}

function add_liquidity(
uint256[] memory _amounts,
uint256 _min_mint_amount,
address _receiver
) external returns (uint256 result) {
uint256[2] memory amounts = [_amounts[0], _amounts[1]];
return add_liquidity(amounts, _min_mint_amount, _receiver);
}
}

0 comments on commit 92549be

Please sign in to comment.