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/fractional pool #922

Merged
merged 7 commits into from
Apr 6, 2024
Merged
Show file tree
Hide file tree
Changes from 3 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
1 change: 1 addition & 0 deletions cspell.json
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@
"twap",
"typechain",
"TYPEHASH",
"Twocrypto",
"Ubiqui",
"UbiquiStick",
"Unassigns",
Expand Down
44 changes: 44 additions & 0 deletions packages/contracts/src/dollar/facets/UbiquityPoolFacet.sol
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@ contract UbiquityPoolFacet is IUbiquityPool, Modifiers {
return LibUbiquityPool.collateralInformation(collateralAddress);
}

/// @inheritdoc IUbiquityPool
function collateralRatio() external view returns (uint256) {
return LibUbiquityPool.collateralRatio();
}

/// @inheritdoc IUbiquityPool
function collateralUsdBalance()
external
Expand All @@ -41,6 +46,15 @@ contract UbiquityPoolFacet is IUbiquityPool, Modifiers {
return LibUbiquityPool.collateralUsdBalance();
}

/// @inheritdoc IUbiquityPool
function ethUsdPriceFeedInformation()
gitcoindev marked this conversation as resolved.
Show resolved Hide resolved
external
view
returns (address, uint256)
{
return LibUbiquityPool.ethUsdPriceFeedInformation();
}

/// @inheritdoc IUbiquityPool
function freeCollateralBalance(
uint256 collateralIndex
Expand Down Expand Up @@ -81,6 +95,11 @@ contract UbiquityPoolFacet is IUbiquityPool, Modifiers {
);
}

/// @inheritdoc IUbiquityPool
function governanceEthPoolAddress() external view returns (address) {
return LibUbiquityPool.governanceEthPoolAddress();
}

//====================
// Public functions
//====================
Expand Down Expand Up @@ -180,6 +199,22 @@ contract UbiquityPoolFacet is IUbiquityPool, Modifiers {
);
}

/// @inheritdoc IUbiquityPool
function setCollateralRatio(uint256 newCollateralRatio) external onlyAdmin {
LibUbiquityPool.setCollateralRatio(newCollateralRatio);
}

/// @inheritdoc IUbiquityPool
function setEthUsdChainLinkPriceFeed(
address newPriceFeedAddress,
uint256 newStalenessThreshold
) external onlyAdmin {
LibUbiquityPool.setEthUsdChainLinkPriceFeed(
newPriceFeedAddress,
newStalenessThreshold
);
}

/// @inheritdoc IUbiquityPool
function setFees(
uint256 collateralIndex,
Expand All @@ -189,6 +224,15 @@ contract UbiquityPoolFacet is IUbiquityPool, Modifiers {
LibUbiquityPool.setFees(collateralIndex, newMintFee, newRedeemFee);
}

/// @inheritdoc IUbiquityPool
function setGovernanceEthPoolAddress(
address newGovernanceEthPoolAddress
) external onlyAdmin {
LibUbiquityPool.setGovernanceEthPoolAddress(
newGovernanceEthPoolAddress
);
}

/// @inheritdoc IUbiquityPool
function setPoolCeiling(
uint256 collateralIndex,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;

import {ICurveStableSwapMetaNG} from "./ICurveStableSwapMetaNG.sol";

/**
* @notice Curve's CurveTwocryptoOptimized interface
*
* @dev Differences between Curve's crypto and stable swap meta pools (and how Ubiquity organization uses them):
* 1. They contain different tokens:
* a) Curve's stable swap metapool containts Dollar/3CRVLP pair
* b) Curve's crypto pool contains Governance/ETH pair
* 2. They use different bonding curve shapes:
* a) Curve's stable swap metapool is more straight (because underlying tokens are pegged to USD)
* b) Curve's crypto pool resembles Uniswap's bonding curve (because underlying tokens are not USD pegged)
*
* @dev Basically `ICurveTwocryptoOptimized` has the same interface as `ICurveStableSwapMetaNG`
* but we distinguish them in the code for clarity.
*/
interface ICurveTwocryptoOptimized is ICurveStableSwapMetaNG {}
68 changes: 65 additions & 3 deletions packages/contracts/src/dollar/interfaces/IUbiquityPool.sol
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,12 @@ interface IUbiquityPool {
view
returns (LibUbiquityPool.CollateralInformation memory returnData);

/**
* @notice Returns current collateral ratio
* @return Collateral ratio
*/
function collateralRatio() external view returns (uint256);

/**
* @notice Returns USD value of all collateral tokens held in the pool, in E18
* @return balanceTally USD value of all collateral tokens
Expand All @@ -41,6 +47,15 @@ interface IUbiquityPool {
view
returns (uint256 balanceTally);

/**
* @notice Returns chainlink price feed information for ETH/USD pair
* @return Price feed address and staleness threshold in seconds
*/
function ethUsdPriceFeedInformation()
external
view
returns (address, uint256);

/**
* @notice Returns free collateral balance (i.e. that can be borrowed by AMO minters)
* @param collateralIndex collateral token index
Expand Down Expand Up @@ -78,6 +93,12 @@ interface IUbiquityPool {
uint256 collateralIndex
) external view returns (uint256);

/**
* @notice Returns pool address for Governance/ETH pair
* @return Pool address
*/
function governanceEthPoolAddress() external view returns (address);

//====================
// Public functions
//====================
Expand Down Expand Up @@ -128,6 +149,12 @@ interface IUbiquityPool {
uint256 collateralIndex
) external returns (uint256 collateralAmount);

/**
* @notice Updates collateral token price in USD from ChainLink price feed
* @param collateralIndex Collateral token index
*/
function updateChainLinkCollateralPrice(uint256 collateralIndex) external;

//=========================
// AMO minters functions
//=========================
Expand Down Expand Up @@ -181,10 +208,30 @@ interface IUbiquityPool {
) external;

/**
* @notice Updates collateral token price in USD from ChainLink price feed
* @param collateralIndex Collateral token index
* @notice Sets collateral ratio
* @dev How much collateral/governance tokens user should provide/get to mint/redeem Dollar tokens, 1e6 precision
*
* @dev Example (1_000_000 = 100%):
* - Mint: user provides 1 collateral token to get 1 Dollar
* - Redeem: user gets 1 collateral token for 1 Dollar
*
* @dev Example (900_000 = 90%):
* - Mint: user provides 0.9 collateral token and 0.1 Governance token to get 1 Dollar
* - Redeem: user gets 0.9 collateral token and 0.1 Governance token for 1 Dollar
*
* @param newCollateralRatio New collateral ratio
*/
function updateChainLinkCollateralPrice(uint256 collateralIndex) external;
function setCollateralRatio(uint256 newCollateralRatio) external;

/**
* @notice Sets chainlink params for ETH/USD price feed
* @param newPriceFeedAddress New chainlink price feed address for ETH/USD pair
* @param newStalenessThreshold New threshold in seconds when chainlink's ETH/USD price feed answer should be considered stale
*/
function setEthUsdChainLinkPriceFeed(
address newPriceFeedAddress,
uint256 newStalenessThreshold
) external;

/**
* @notice Sets mint and redeem fees, 1_000_000 = 100%
Expand All @@ -198,6 +245,21 @@ interface IUbiquityPool {
uint256 newRedeemFee
) external;

/**
* @notice Sets a new pool address for Governance/ETH pair
*
* @dev Based on Curve's CurveTwocryptoOptimized contract. Used for fetching Governance token USD price.
* How it works:
* 1. Fetch Governance/ETH price from CurveTwocryptoOptimized's built-in oracle
* 2. Fetch ETH/USD price from chainlink feed
* 3. Calculate Governance token price in USD
*
* @param newGovernanceEthPoolAddress New pool address for Governance/ETH pair
*/
function setGovernanceEthPoolAddress(
address newGovernanceEthPoolAddress
) external;

/**
* @notice Sets max amount of collateral for a particular collateral token
* @param collateralIndex Collateral token index
Expand Down
114 changes: 114 additions & 0 deletions packages/contracts/src/dollar/libraries/LibUbiquityPool.sol
Original file line number Diff line number Diff line change
Expand Up @@ -48,45 +48,56 @@
uint256[] collateralPriceFeedStalenessThresholds;
// collateral index -> collateral price
uint256[] collateralPrices;
// how much collateral/governance tokens user should provide/get to mint/redeem Dollar tokens, 1e6 precision
uint256 collateralRatio;

Check failure on line 52 in packages/contracts/src/dollar/libraries/LibUbiquityPool.sol

View workflow job for this annotation

GitHub Actions / check_storage_layout (src/dollar/libraries/LibUbiquityPool.sol:LibUbiquityPool)

Variable diff

variable "collateralSymbols" of type "ArrayTypeName" was replaced by variable "collateralRatio" of type "uint256" (storage slot 0x0000000000000000000000000000000000000000000000000000000000000006, byte #0)
// array collateral symbols
string[] collateralSymbols;

Check failure on line 54 in packages/contracts/src/dollar/libraries/LibUbiquityPool.sol

View workflow job for this annotation

GitHub Actions / check_storage_layout (src/dollar/libraries/LibUbiquityPool.sol:LibUbiquityPool)

Variable diff

variable "isCollateralEnabled" of type "Mapping" was replaced by variable "collateralSymbols" of type "ArrayTypeName" (storage slot 0x0000000000000000000000000000000000000000000000000000000000000007, byte #0)
// collateral address -> is it enabled
mapping(address collateralAddress => bool isEnabled) isCollateralEnabled;

Check failure on line 56 in packages/contracts/src/dollar/libraries/LibUbiquityPool.sol

View workflow job for this annotation

GitHub Actions / check_storage_layout (src/dollar/libraries/LibUbiquityPool.sol:LibUbiquityPool)

Variable diff

variable "missingDecimals" of type "ArrayTypeName" was replaced by variable "isCollateralEnabled" of type "Mapping" (storage slot 0x0000000000000000000000000000000000000000000000000000000000000008, byte #0)
// Number of decimals needed to get to E18. collateral index -> missing decimals
uint256[] missingDecimals;

Check warning on line 58 in packages/contracts/src/dollar/libraries/LibUbiquityPool.sol

View workflow job for this annotation

GitHub Actions / check_storage_layout (src/dollar/libraries/LibUbiquityPool.sol:LibUbiquityPool)

Label diff

variable "poolCeilings" was renamed to "missingDecimals". Is it intentional? (storage slot 0x0000000000000000000000000000000000000000000000000000000000000009, byte #0)
// Total across all collaterals. Accounts for missing_decimals
uint256[] poolCeilings;

Check failure on line 60 in packages/contracts/src/dollar/libraries/LibUbiquityPool.sol

View workflow job for this annotation

GitHub Actions / check_storage_layout (src/dollar/libraries/LibUbiquityPool.sol:LibUbiquityPool)

Variable diff

variable "lastRedeemedBlock" of type "Mapping" was replaced by variable "poolCeilings" of type "ArrayTypeName" (storage slot 0x000000000000000000000000000000000000000000000000000000000000000a, byte #0)
//====================
// Redeem related
//====================
// user -> block number (collateral independent)
mapping(address => uint256) lastRedeemedBlock;

Check failure on line 65 in packages/contracts/src/dollar/libraries/LibUbiquityPool.sol

View workflow job for this annotation

GitHub Actions / check_storage_layout (src/dollar/libraries/LibUbiquityPool.sol:LibUbiquityPool)

Variable diff

variable "mintPriceThreshold" of type "uint256" was replaced by variable "lastRedeemedBlock" of type "Mapping" (storage slot 0x000000000000000000000000000000000000000000000000000000000000000b, byte #0)
// 1010000 = $1.01
uint256 mintPriceThreshold;

Check warning on line 67 in packages/contracts/src/dollar/libraries/LibUbiquityPool.sol

View workflow job for this annotation

GitHub Actions / check_storage_layout (src/dollar/libraries/LibUbiquityPool.sol:LibUbiquityPool)

Label diff

variable "redeemPriceThreshold" was renamed to "mintPriceThreshold". Is it intentional? (storage slot 0x000000000000000000000000000000000000000000000000000000000000000c, byte #0)
// 990000 = $0.99
uint256 redeemPriceThreshold;

Check failure on line 69 in packages/contracts/src/dollar/libraries/LibUbiquityPool.sol

View workflow job for this annotation

GitHub Actions / check_storage_layout (src/dollar/libraries/LibUbiquityPool.sol:LibUbiquityPool)

Variable diff

variable "redeemCollateralBalances" of type "Mapping" was replaced by variable "redeemPriceThreshold" of type "uint256" (storage slot 0x000000000000000000000000000000000000000000000000000000000000000d, byte #0)
// address -> collateral index -> balance
mapping(address user => mapping(uint256 collateralIndex => uint256 amount)) redeemCollateralBalances;

Check failure on line 71 in packages/contracts/src/dollar/libraries/LibUbiquityPool.sol

View workflow job for this annotation

GitHub Actions / check_storage_layout (src/dollar/libraries/LibUbiquityPool.sol:LibUbiquityPool)

Variable diff

variable "redemptionDelayBlocks" of type "uint256" was replaced by variable "redeemCollateralBalances" of type "Mapping" (storage slot 0x000000000000000000000000000000000000000000000000000000000000000e, byte #0)
// number of blocks to wait before being able to collectRedemption()
uint256 redemptionDelayBlocks;

Check failure on line 73 in packages/contracts/src/dollar/libraries/LibUbiquityPool.sol

View workflow job for this annotation

GitHub Actions / check_storage_layout (src/dollar/libraries/LibUbiquityPool.sol:LibUbiquityPool)

Variable diff

variable "unclaimedPoolCollateral" of type "ArrayTypeName" was replaced by variable "redemptionDelayBlocks" of type "uint256" (storage slot 0x000000000000000000000000000000000000000000000000000000000000000f, byte #0)
// collateral index -> balance
uint256[] unclaimedPoolCollateral;

Check warning on line 75 in packages/contracts/src/dollar/libraries/LibUbiquityPool.sol

View workflow job for this annotation

GitHub Actions / check_storage_layout (src/dollar/libraries/LibUbiquityPool.sol:LibUbiquityPool)

Label diff

variable "mintingFee" was renamed to "unclaimedPoolCollateral". Is it intentional? (storage slot 0x0000000000000000000000000000000000000000000000000000000000000010, byte #0)
//================
// Fees related
//================
// minting fee of a particular collateral index, 1_000_000 = 100%
uint256[] mintingFee;

Check warning on line 80 in packages/contracts/src/dollar/libraries/LibUbiquityPool.sol

View workflow job for this annotation

GitHub Actions / check_storage_layout (src/dollar/libraries/LibUbiquityPool.sol:LibUbiquityPool)

Label diff

variable "redemptionFee" was renamed to "mintingFee". Is it intentional? (storage slot 0x0000000000000000000000000000000000000000000000000000000000000011, byte #0)
// redemption fee of a particular collateral index, 1_000_000 = 100%
uint256[] redemptionFee;

Check warning on line 82 in packages/contracts/src/dollar/libraries/LibUbiquityPool.sol

View workflow job for this annotation

GitHub Actions / check_storage_layout (src/dollar/libraries/LibUbiquityPool.sol:LibUbiquityPool)

Label diff

variable "isBorrowPaused" was renamed to "redemptionFee". Is it intentional? (storage slot 0x0000000000000000000000000000000000000000000000000000000000000012, byte #0)
//=================
// Pause related
//=================
// whether borrowing collateral by AMO minters is paused for a particular collateral index
bool[] isBorrowPaused;

Check warning on line 87 in packages/contracts/src/dollar/libraries/LibUbiquityPool.sol

View workflow job for this annotation

GitHub Actions / check_storage_layout (src/dollar/libraries/LibUbiquityPool.sol:LibUbiquityPool)

Label diff

variable "isMintPaused" was renamed to "isBorrowPaused". Is it intentional? (storage slot 0x0000000000000000000000000000000000000000000000000000000000000013, byte #0)
// whether minting is paused for a particular collateral index
bool[] isMintPaused;

Check warning on line 89 in packages/contracts/src/dollar/libraries/LibUbiquityPool.sol

View workflow job for this annotation

GitHub Actions / check_storage_layout (src/dollar/libraries/LibUbiquityPool.sol:LibUbiquityPool)

Label diff

variable "isRedeemPaused" was renamed to "isMintPaused". Is it intentional? (storage slot 0x0000000000000000000000000000000000000000000000000000000000000014, byte #0)
// whether redeeming is paused for a particular collateral index
bool[] isRedeemPaused;
//====================================
// Governance token pricing related
//====================================
// chainlink price feed for ETH/USD pair
address ethUsdPriceFeedAddress;
// threshold in seconds when chainlink's ETH/USD price feed answer should be considered stale
uint256 ethUsdPriceFeedStalenessThreshold;
rndquu marked this conversation as resolved.
Show resolved Hide resolved
// Curve's CurveTwocryptoOptimized contract for Governance/ETH pair
address governanceEthPoolAddress;
}

/// @notice Struct used for detailed collateral information
Expand Down Expand Up @@ -138,14 +149,23 @@
);
/// @notice Emitted on setting a collateral price
event CollateralPriceSet(uint256 collateralIndex, uint256 newPrice);
/// @notice Emitted on setting a collateral ratio
event CollateralRatioSet(uint256 newCollateralRatio);
/// @notice Emitted on enabling/disabling a particular collateral token
event CollateralToggled(uint256 collateralIndex, bool newState);
/// @notice Emitted on setting chainlink's price feed for ETH/USD pair
event EthUsdPriceFeedSet(
address newPriceFeedAddress,
uint256 newStalenessThreshold
);
/// @notice Emitted when fees are updated
event FeesSet(
uint256 collateralIndex,
uint256 newMintFee,
uint256 newRedeemFee
);
/// @notice Emitted on setting a pool for Governance/ETH pair
event GovernanceEthPoolSet(address newGovernanceEthPoolAddress);
/// @notice Emitted on toggling pause for mint/redeem/borrow
event MintRedeemBorrowToggled(uint256 collateralIndex, uint8 toggleIndex);
/// @notice Emitted when new pool ceiling (i.e. max amount of collateral) is set
Expand Down Expand Up @@ -258,6 +278,15 @@
);
}

/**
* @notice Returns current collateral ratio
* @return Collateral ratio
*/
function collateralRatio() internal view returns (uint256) {
UbiquityPoolStorage storage poolStorage = ubiquityPoolStorage();
return poolStorage.collateralRatio;
}

/**
* @notice Returns USD value of all collateral tokens held in the pool, in E18
* @return balanceTally USD value of all collateral tokens
Expand All @@ -278,6 +307,22 @@
}
}

/**
* @notice Returns chainlink price feed information for ETH/USD pair
* @return Price feed address and staleness threshold in seconds
*/
function ethUsdPriceFeedInformation()
internal
view
returns (address, uint256)
{
UbiquityPoolStorage storage poolStorage = ubiquityPoolStorage();
return (
poolStorage.ethUsdPriceFeedAddress,
poolStorage.ethUsdPriceFeedStalenessThreshold
);
}

/**
* @notice Returns free collateral balance (i.e. that can be borrowed by AMO minters)
* @param collateralIndex collateral token index
Expand Down Expand Up @@ -347,6 +392,15 @@
poolStorage.redeemCollateralBalances[userAddress][collateralIndex];
}

/**
* @notice Returns pool address for Governance/ETH pair
* @return Pool address
*/
function governanceEthPoolAddress() internal view returns (address) {
UbiquityPoolStorage storage poolStorage = ubiquityPoolStorage();
return poolStorage.governanceEthPoolAddress;
}

//====================
// Public functions
//====================
Expand Down Expand Up @@ -778,6 +832,45 @@
);
}

/**
* @notice Sets collateral ratio
* @dev How much collateral/governance tokens user should provide/get to mint/redeem Dollar tokens, 1e6 precision
*
* @dev Example (1_000_000 = 100%):
* - Mint: user provides 1 collateral token to get 1 Dollar
* - Redeem: user gets 1 collateral token for 1 Dollar
*
* @dev Example (900_000 = 90%):
* - Mint: user provides 0.9 collateral token and 0.1 Governance token to get 1 Dollar
* - Redeem: user gets 0.9 collateral token and 0.1 Governance token for 1 Dollar
*
* @param newCollateralRatio New collateral ratio
*/
function setCollateralRatio(uint256 newCollateralRatio) internal {
Copy link

@0xadrii 0xadrii Apr 19, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[LOW] - Collateral ratio should be capped

As seen in Frax's code, collateral ratio will always be capped at 100%. For Ubiquity, if a higher value is wrongly set and the dynamic CR is implemented, then it could cause some issues. It is recommended to add a maximum value for newCollateralRatio of 1_000_000.

It would also be good to cap the fees in the setFees function to the max acceptable values, as well as the redemption delay blocks in setRedemptionDelayBlocks.

UbiquityPoolStorage storage poolStorage = ubiquityPoolStorage();

poolStorage.collateralRatio = newCollateralRatio;

emit CollateralRatioSet(newCollateralRatio);
}

/**
* @notice Sets chainlink params for ETH/USD price feed
* @param newPriceFeedAddress New chainlink price feed address for ETH/USD pair
* @param newStalenessThreshold New threshold in seconds when chainlink's ETH/USD price feed answer should be considered stale
*/
function setEthUsdChainLinkPriceFeed(
address newPriceFeedAddress,
uint256 newStalenessThreshold
) internal {
UbiquityPoolStorage storage poolStorage = ubiquityPoolStorage();

poolStorage.ethUsdPriceFeedAddress = newPriceFeedAddress;
poolStorage.ethUsdPriceFeedStalenessThreshold = newStalenessThreshold;

emit EthUsdPriceFeedSet(newPriceFeedAddress, newStalenessThreshold);
}

/**
* @notice Sets mint and redeem fees, 1_000_000 = 100%
* @param collateralIndex Collateral token index
Expand All @@ -797,6 +890,27 @@
emit FeesSet(collateralIndex, newMintFee, newRedeemFee);
}

/**
* @notice Sets a new pool address for Governance/ETH pair
*
* @dev Based on Curve's CurveTwocryptoOptimized contract. Used for fetching Governance token USD price.
* How it works:
* 1. Fetch Governance/ETH price from CurveTwocryptoOptimized's built-in oracle
* 2. Fetch ETH/USD price from chainlink feed
* 3. Calculate Governance token price in USD
*
* @param newGovernanceEthPoolAddress New pool address for Governance/ETH pair
*/
function setGovernanceEthPoolAddress(
address newGovernanceEthPoolAddress
) internal {
UbiquityPoolStorage storage poolStorage = ubiquityPoolStorage();

poolStorage.governanceEthPoolAddress = newGovernanceEthPoolAddress;

emit GovernanceEthPoolSet(newGovernanceEthPoolAddress);
}

/**
* @notice Sets max amount of collateral for a particular collateral token
* @param collateralIndex Collateral token index
Expand Down
Loading
Loading