Skip to content

Commit

Permalink
Merge pull request #10 from rndquu/feat/collateral-twap
Browse files Browse the repository at this point in the history
refactor: refactor UbiquityPool
  • Loading branch information
rndquu authored Dec 21, 2023
2 parents 390f9da + 4210e3d commit f08df5c
Show file tree
Hide file tree
Showing 7 changed files with 244 additions and 347 deletions.
1 change: 0 additions & 1 deletion packages/contracts/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
"url": "https://github.com/ubiquity/ubiquity-dollar.git"
},
"dependencies": {
"@chainlink/contracts": "^0.8.0",
"@types/command-line-args": "5.2.0",
"command-line-args": "5.2.1",
"dotenv": "^16.0.3",
Expand Down
25 changes: 12 additions & 13 deletions packages/contracts/src/dollar/facets/UbiquityPoolFacet.sol
Original file line number Diff line number Diff line change
Expand Up @@ -136,9 +136,14 @@ contract UbiquityPoolFacet is IUbiquityPool, Modifiers {
/// @inheritdoc IUbiquityPool
function addCollateralToken(
address collateralAddress,
address chainLinkPriceFeedAddress,
uint256 poolCeiling
) external onlyAdmin {
LibUbiquityPool.addCollateralToken(collateralAddress, poolCeiling);
LibUbiquityPool.addCollateralToken(
collateralAddress,
chainLinkPriceFeedAddress,
poolCeiling
);
}

/// @inheritdoc IUbiquityPool
Expand All @@ -147,21 +152,15 @@ contract UbiquityPoolFacet is IUbiquityPool, Modifiers {
}

/// @inheritdoc IUbiquityPool
function setCollateralPrice(
uint256 collateralIndex,
uint256 newPrice
) external onlyAdmin {
LibUbiquityPool.setCollateralPrice(collateralIndex, newPrice);
}

/// @inheritdoc IUbiquityPool
function setCollateralChainLinkPriceFeedAddress(
function setCollateralChainLinkPriceFeed(
address collateralAddress,
address chainLinkPriceFeedAddress
address chainLinkPriceFeedAddress,
uint256 stalenessThreshold
) external onlyAdmin {
LibUbiquityPool.setCollateralChainLinkPriceFeedAddress(
LibUbiquityPool.setCollateralChainLinkPriceFeed(
collateralAddress,
chainLinkPriceFeedAddress
chainLinkPriceFeedAddress,
stalenessThreshold
);
}

Expand Down
20 changes: 7 additions & 13 deletions packages/contracts/src/dollar/interfaces/IUbiquityPool.sol
Original file line number Diff line number Diff line change
Expand Up @@ -142,10 +142,12 @@ interface IUbiquityPool {
/**
* @notice Adds a new collateral token
* @param collateralAddress Collateral token address
* @param chainLinkPriceFeedAddress Chainlink's price feed address
* @param poolCeiling Max amount of available tokens for collateral
*/
function addCollateralToken(
address collateralAddress,
address chainLinkPriceFeedAddress,
uint256 poolCeiling
) external;

Expand All @@ -156,23 +158,15 @@ interface IUbiquityPool {
function removeAmoMinter(address amoMinterAddress) external;

/**
* @notice Sets collateral token price in USD
* @param collateralIndex Collateral token index
* @param newPrice New USD price (precision 1e6)
*/
function setCollateralPrice(
uint256 collateralIndex,
uint256 newPrice
) external;

/**
* @notice Sets collateral ChainLink price feed address
* @notice Sets collateral ChainLink price feed params
* @param collateralAddress Collateral token address
* @param chainLinkPriceFeedAddress ChainLink price feed address
* @param stalenessThreshold Threshold in seconds when chainlink answer should be considered stale
*/
function setCollateralChainLinkPriceFeedAddress(
function setCollateralChainLinkPriceFeed(
address collateralAddress,
address chainLinkPriceFeedAddress
address chainLinkPriceFeedAddress,
uint256 stalenessThreshold
) external;

/**
Expand Down
150 changes: 93 additions & 57 deletions packages/contracts/src/dollar/libraries/LibUbiquityPool.sol
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity 0.8.19;

import {AggregatorV3Interface} from "@chainlink/interfaces/AggregatorV3Interface.sol";
import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
Expand All @@ -10,7 +11,6 @@ import {IERC20Ubiquity} from "../interfaces/IERC20Ubiquity.sol";
import {UBIQUITY_POOL_PRICE_PRECISION} from "./Constants.sol";
import {LibAppStorage} from "./LibAppStorage.sol";
import {LibTWAPOracle} from "./LibTWAPOracle.sol";
import {AggregatorV3Interface} from "@chainlink/interfaces/AggregatorV3Interface.sol";

/**
* @notice Ubiquity pool library
Expand Down Expand Up @@ -42,7 +42,11 @@ library LibUbiquityPool {
address[] collateralAddresses;
// collateral address -> collateral index
mapping(address collateralAddress => uint256 collateralIndex) collateralIndex;
// Stores price of the collateral
// collateral index -> chainlink price feed addresses
address[] collateralPriceFeedAddresses;
// collateral index -> threshold in seconds when chainlink answer should be considered stale
uint256[] collateralPriceFeedStalenessThresholds;
// collateral index -> collateral price
uint256[] collateralPrices;
// array collateral symbols
string[] collateralSymbols;
Expand Down Expand Up @@ -83,15 +87,15 @@ library LibUbiquityPool {
bool[] isMintPaused;
// whether redeeming is paused for a particular collateral index
bool[] isRedeemPaused;
// Collateral price feed contract address
address[] collateralPriceFeedAddresses;
}

/// @notice Struct used for detailed collateral information
struct CollateralInformation {
uint256 index;
string symbol;
address collateralAddress;
address collateralPriceFeedAddress;
uint256 collateralPriceFeedStalenessThreshold;
bool isEnabled;
uint256 missingDecimals;
uint256 price;
Expand Down Expand Up @@ -126,13 +130,14 @@ library LibUbiquityPool {
event AmoMinterAdded(address amoMinterAddress);
/// @notice Emitted when AMO minter is removed
event AmoMinterRemoved(address amoMinterAddress);
/// @notice Emitted on setting a collateral price
event CollateralPriceSet(uint256 collateralIndex, uint256 newPrice);
/// @notice Emitted on setting a collateral price feed address
/// @notice Emitted on setting a chainlink's collateral price feed params
event CollateralPriceFeedSet(
uint256 collateralIndex,
address priceFeedAddress
address priceFeedAddress,
uint256 stalenessThreshold
);
/// @notice Emitted on setting a collateral price
event CollateralPriceSet(uint256 collateralIndex, uint256 newPrice);
/// @notice Emitted on enabling/disabling a particular collateral token
event CollateralToggled(uint256 collateralIndex, bool newState);
/// @notice Emitted when fees are updated
Expand Down Expand Up @@ -221,6 +226,8 @@ library LibUbiquityPool {
index,
poolStorage.collateralSymbols[index],
collateralAddress,
poolStorage.collateralPriceFeedAddresses[index],
poolStorage.collateralPriceFeedStalenessThresholds[index],
poolStorage.isCollateralEnabled[collateralAddress],
poolStorage.missingDecimals[index],
poolStorage.collateralPrices[index],
Expand Down Expand Up @@ -333,6 +340,8 @@ library LibUbiquityPool {
"Minting is paused"
);

// update Dollar price from Curve's Dollar Metapool
LibTWAPOracle.update();
// prevent unnecessary mints
require(
getDollarPriceUsd() >= poolStorage.mintPriceThreshold,
Expand All @@ -341,7 +350,8 @@ library LibUbiquityPool {

// update collateral price
updateChainLinkCollateralPrice(collateralIndex);
// get amount of collateral for incoming Dollars

// get amount of collateral for minting Dollars
collateralNeeded = getDollarInCollateral(collateralIndex, dollarAmount);

// subtract the minting fee
Expand Down Expand Up @@ -402,6 +412,8 @@ library LibUbiquityPool {
"Redeeming is paused"
);

// update Dollar price from Curve's Dollar Metapool
LibTWAPOracle.update();
// prevent unnecessary redemptions that could adversely affect the Dollar price
require(
getDollarPriceUsd() <= poolStorage.redeemPriceThreshold,
Expand All @@ -416,9 +428,10 @@ library LibUbiquityPool {
)
.div(UBIQUITY_POOL_PRICE_PRECISION);

//update collateral price
// update collateral price
updateChainLinkCollateralPrice(collateralIndex);

// get collateral output for incoming Dollars
collateralOut = getDollarInCollateral(collateralIndex, dollarAfterFee);

// checks
Expand Down Expand Up @@ -503,6 +516,51 @@ library LibUbiquityPool {
}
}

/**
* @notice Updates collateral token price in USD from ChainLink price feed
* @param collateralIndex Collateral token index
*/
function updateChainLinkCollateralPrice(uint256 collateralIndex) internal {
UbiquityPoolStorage storage poolStorage = ubiquityPoolStorage();

AggregatorV3Interface priceFeed = AggregatorV3Interface(
poolStorage.collateralPriceFeedAddresses[collateralIndex]
);

// fetch latest price
(
,
// roundId
int256 answer, // startedAt
,
uint256 updatedAt,

) = // answeredInRound
priceFeed.latestRoundData();

// fetch number of decimals in chainlink feed
uint256 priceFeedDecimals = priceFeed.decimals();

// validation
require(answer > 0, "Invalid price");
require(
block.timestamp - updatedAt <
poolStorage.collateralPriceFeedStalenessThresholds[
collateralIndex
],
"Stale data"
);

// convert chainlink price to 6 decimals
uint256 price = uint256(answer).mul(UBIQUITY_POOL_PRICE_PRECISION).div(
10 ** priceFeedDecimals
);

poolStorage.collateralPrices[collateralIndex] = price;

emit CollateralPriceSet(collateralIndex, price);
}

//=========================
// AMO minters functions
//=========================
Expand Down Expand Up @@ -565,10 +623,12 @@ library LibUbiquityPool {
/**
* @notice Adds a new collateral token
* @param collateralAddress Collateral token address
* @param chainLinkPriceFeedAddress Chainlink's price feed address
* @param poolCeiling Max amount of available tokens for collateral
*/
function addCollateralToken(
address collateralAddress,
address chainLinkPriceFeedAddress,
uint256 poolCeiling
) internal {
UbiquityPoolStorage storage poolStorage = ubiquityPoolStorage();
Expand Down Expand Up @@ -607,11 +667,16 @@ library LibUbiquityPool {
poolStorage.isRedeemPaused.push(false);
poolStorage.isBorrowPaused.push(false);

// pool ceiling
// set pool ceiling
poolStorage.poolCeilings.push(poolCeiling);

// price feed
poolStorage.collateralPriceFeedAddresses.push(address(0));
// set price feed address
poolStorage.collateralPriceFeedAddresses.push(
chainLinkPriceFeedAddress
);

// set price feed staleness threshold in seconds
poolStorage.collateralPriceFeedStalenessThresholds.push(1 days);
}

/**
Expand All @@ -627,66 +692,37 @@ library LibUbiquityPool {
}

/**
* @notice Sets exact collateral token price in USD
* @param collateralIndex Collateral token index
* @param newPrice New USD price (precision 1e6)
*/
function setCollateralPrice(
uint256 collateralIndex,
uint256 newPrice
) internal {
UbiquityPoolStorage storage poolStorage = ubiquityPoolStorage();

poolStorage.collateralPrices[collateralIndex] = newPrice;

emit CollateralPriceSet(collateralIndex, newPrice);
}

/**
* @notice Sets collateral ChainLink price feed address
* @notice Sets collateral ChainLink price feed params
* @param collateralAddress Collateral token address
* @param chainLinkPriceFeedAddress ChainLink price feed address
* @param stalenessThreshold Threshold in seconds when chainlink answer should be considered stale
*/
function setCollateralChainLinkPriceFeedAddress(
function setCollateralChainLinkPriceFeed(
address collateralAddress,
address chainLinkPriceFeedAddress
address chainLinkPriceFeedAddress,
uint256 stalenessThreshold
) internal {
UbiquityPoolStorage storage poolStorage = ubiquityPoolStorage();

uint256 collateralIndex = poolStorage.collateralIndex[
collateralAddress
];

// set price feed address
poolStorage.collateralPriceFeedAddresses[
collateralIndex
] = chainLinkPriceFeedAddress;

emit CollateralPriceFeedSet(collateralIndex, chainLinkPriceFeedAddress);
}

/**
* @notice Updates collateral token price in USD from ChainLink price feed
* @param collateralIndex Collateral token index
*/
function updateChainLinkCollateralPrice(uint256 collateralIndex) internal {
UbiquityPoolStorage storage poolStorage = ubiquityPoolStorage();
// set staleness threshold in seconds when chainlink answer should be considered stale
poolStorage.collateralPriceFeedStalenessThresholds[
collateralIndex
] = stalenessThreshold;

AggregatorV3Interface priceFeed = AggregatorV3Interface(
poolStorage.collateralPriceFeedAddresses[collateralIndex]
);
(
uint80 roundID,
int256 price,
,
uint256 updatedAt,
uint80 answeredInRound
) = priceFeed.latestRoundData();
require(
price >= 0 && updatedAt != 0 && answeredInRound >= roundID,
"Invalid ChainLink price"
emit CollateralPriceFeedSet(
collateralIndex,
chainLinkPriceFeedAddress,
stalenessThreshold
);

poolStorage.collateralPrices[collateralIndex] = uint256(price);
emit CollateralPriceSet(collateralIndex, uint256(price));
}

/**
Expand Down
Loading

0 comments on commit f08df5c

Please sign in to comment.