From 4210e3d1085e7f27515daa4e63257d38233a9bd4 Mon Sep 17 00:00:00 2001
From: rndquu <rndquu@gmail.com>
Date: Thu, 21 Dec 2023 13:28:45 +0300
Subject: [PATCH] refactor: refactor UbiquityPool

---
 packages/contracts/package.json               |   1 -
 .../src/dollar/facets/UbiquityPoolFacet.sol   |  25 +-
 .../src/dollar/interfaces/IUbiquityPool.sol   |  20 +-
 .../src/dollar/libraries/LibUbiquityPool.sol  | 150 ++++++-----
 .../src/dollar/mocks/MockChainLinkFeed.sol    |  31 +--
 .../diamond/facets/UbiquityPoolFacet.t.sol    | 238 +++++++++---------
 yarn.lock                                     | 126 +---------
 7 files changed, 244 insertions(+), 347 deletions(-)

diff --git a/packages/contracts/package.json b/packages/contracts/package.json
index 04224756f..28baf9cc0 100644
--- a/packages/contracts/package.json
+++ b/packages/contracts/package.json
@@ -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",
diff --git a/packages/contracts/src/dollar/facets/UbiquityPoolFacet.sol b/packages/contracts/src/dollar/facets/UbiquityPoolFacet.sol
index dff34bce9..52c6486e2 100644
--- a/packages/contracts/src/dollar/facets/UbiquityPoolFacet.sol
+++ b/packages/contracts/src/dollar/facets/UbiquityPoolFacet.sol
@@ -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
@@ -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
         );
     }
 
diff --git a/packages/contracts/src/dollar/interfaces/IUbiquityPool.sol b/packages/contracts/src/dollar/interfaces/IUbiquityPool.sol
index 7d3b9a769..5e91ddc9c 100644
--- a/packages/contracts/src/dollar/interfaces/IUbiquityPool.sol
+++ b/packages/contracts/src/dollar/interfaces/IUbiquityPool.sol
@@ -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;
 
@@ -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;
 
     /**
diff --git a/packages/contracts/src/dollar/libraries/LibUbiquityPool.sol b/packages/contracts/src/dollar/libraries/LibUbiquityPool.sol
index efc5b05b0..2f2a90a35 100644
--- a/packages/contracts/src/dollar/libraries/LibUbiquityPool.sol
+++ b/packages/contracts/src/dollar/libraries/LibUbiquityPool.sol
@@ -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";
@@ -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
@@ -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;
@@ -83,8 +87,6 @@ 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
@@ -92,6 +94,8 @@ library LibUbiquityPool {
         uint256 index;
         string symbol;
         address collateralAddress;
+        address collateralPriceFeedAddress;
+        uint256 collateralPriceFeedStalenessThreshold;
         bool isEnabled;
         uint256 missingDecimals;
         uint256 price;
@@ -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
@@ -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],
@@ -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,
@@ -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
@@ -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,
@@ -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
@@ -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
     //=========================
@@ -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();
@@ -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);
     }
 
     /**
@@ -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));
     }
 
     /**
diff --git a/packages/contracts/src/dollar/mocks/MockChainLinkFeed.sol b/packages/contracts/src/dollar/mocks/MockChainLinkFeed.sol
index 3e3d87d3b..872b7ab87 100644
--- a/packages/contracts/src/dollar/mocks/MockChainLinkFeed.sol
+++ b/packages/contracts/src/dollar/mocks/MockChainLinkFeed.sol
@@ -10,16 +10,15 @@ contract MockChainLinkFeed is AggregatorV3Interface {
     uint256 updatedAt;
     uint80 answeredInRound;
 
+    uint8 public decimals;
+
     constructor() {
         roundId = 0;
         answer = 0;
         startedAt = block.timestamp;
         updatedAt = block.timestamp;
         answeredInRound = 0;
-    }
-
-    function decimals() external pure override returns (uint8) {
-        return 18;
+        decimals = 8;
     }
 
     function description() external pure override returns (string memory) {
@@ -38,13 +37,7 @@ contract MockChainLinkFeed is AggregatorV3Interface {
         override
         returns (uint80, int256, uint256, uint256, uint80)
     {
-        return (
-            roundId,
-            answer,
-            block.timestamp,
-            block.timestamp,
-            answeredInRound
-        );
+        return (roundId, answer, startedAt, updatedAt, answeredInRound);
     }
 
     function latestRoundData()
@@ -53,22 +46,24 @@ contract MockChainLinkFeed is AggregatorV3Interface {
         override
         returns (uint80, int256, uint256, uint256, uint80)
     {
-        return (
-            roundId,
-            answer,
-            block.timestamp,
-            block.timestamp,
-            answeredInRound
-        );
+        return (roundId, answer, startedAt, updatedAt, answeredInRound);
+    }
+
+    function updateDecimals(uint8 _newDecimals) public {
+        decimals = _newDecimals;
     }
 
     function updateMockParams(
         uint80 _roundId,
         int256 _answer,
+        uint256 _startedAt,
+        uint256 _updatedAt,
         uint80 _answeredInRound
     ) public {
         roundId = _roundId;
         answer = _answer;
+        startedAt = _startedAt;
+        updatedAt = _updatedAt;
         answeredInRound = _answeredInRound;
     }
 }
diff --git a/packages/contracts/test/diamond/facets/UbiquityPoolFacet.t.sol b/packages/contracts/test/diamond/facets/UbiquityPoolFacet.t.sol
index a86efbb82..de8946d67 100644
--- a/packages/contracts/test/diamond/facets/UbiquityPoolFacet.t.sol
+++ b/packages/contracts/test/diamond/facets/UbiquityPoolFacet.t.sol
@@ -6,8 +6,8 @@ import {DiamondTestSetup} from "../DiamondTestSetup.sol";
 import {IDollarAmoMinter} from "../../../src/dollar/interfaces/IDollarAmoMinter.sol";
 import {IMetaPool} from "../../../src/dollar/interfaces/IMetaPool.sol";
 import {LibUbiquityPool} from "../../../src/dollar/libraries/LibUbiquityPool.sol";
-import {MockERC20} from "../../../src/dollar/mocks/MockERC20.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";
 
 contract MockDollarAmoMinter is IDollarAmoMinter {
@@ -26,11 +26,17 @@ contract UbiquityPoolFacetTest is DiamondTestSetup {
     MockChainLinkFeed collateralTokenPriceFeed;
     MockMetaPool curveDollarMetaPool;
     MockERC20 curveTriPoolLpToken;
+
     address user = address(1);
 
     // Events
     event AmoMinterAdded(address amoMinterAddress);
     event AmoMinterRemoved(address amoMinterAddress);
+    event CollateralPriceFeedSet(
+        uint256 collateralIndex,
+        address priceFeedAddress,
+        uint256 stalenessThreshold
+    );
     event CollateralPriceSet(uint256 collateralIndex, uint256 newPrice);
     event CollateralToggled(uint256 collateralIndex, bool newState);
     event FeesSet(
@@ -70,18 +76,24 @@ contract UbiquityPoolFacetTest is DiamondTestSetup {
         uint256 poolCeiling = 50_000e18; // max 50_000 of collateral tokens is allowed
         ubiquityPoolFacet.addCollateralToken(
             address(collateralToken),
+            address(collateralTokenPriceFeed),
             poolCeiling
         );
 
-        ubiquityPoolFacet.setCollateralChainLinkPriceFeedAddress(
-            address(collateralToken),
-            address(collateralTokenPriceFeed)
+        // set collateral price feed mock params
+        collateralTokenPriceFeed.updateMockParams(
+            1, // round id
+            100_000_000, // answer, 100_000_000 = $1.00 (chainlink 8 decimals answer is converted to 6 decimals pool price)
+            block.timestamp, // started at
+            block.timestamp, // updated at
+            1 // answered in round
         );
 
-        MockChainLinkFeed(collateralTokenPriceFeed).updateMockParams(
-            1,
-            1_000_000,
-            2
+        // set price feed for collateral token
+        ubiquityPoolFacet.setCollateralChainLinkPriceFeed(
+            address(collateralToken), // collateral token address
+            address(collateralTokenPriceFeed), // price feed address
+            1 days // price feed staleness threshold in seconds
         );
 
         // enable collateral at index 0
@@ -174,6 +186,11 @@ contract UbiquityPoolFacetTest is DiamondTestSetup {
         assertEq(info.index, 0);
         assertEq(info.symbol, "CLT");
         assertEq(info.collateralAddress, address(collateralToken));
+        assertEq(
+            info.collateralPriceFeedAddress,
+            address(collateralTokenPriceFeed)
+        );
+        assertEq(info.collateralPriceFeedStalenessThreshold, 1 days);
         assertEq(info.isEnabled, true);
         assertEq(info.missingDecimals, 0);
         assertEq(info.price, 1_000_000);
@@ -517,6 +534,68 @@ contract UbiquityPoolFacetTest is DiamondTestSetup {
         assertEq(collateralToken.balanceOf(user), 97.02e18);
     }
 
+    function testUpdateChainLinkCollateralPrice_ShouldRevert_IfChainlinkAnswerIsInvalid()
+        public
+    {
+        // set invalid answer from chainlink
+        collateralTokenPriceFeed.updateMockParams(
+            1, // round id
+            0, // invalid answer
+            block.timestamp, // started at
+            block.timestamp, // updated at
+            1 // answered in round
+        );
+
+        vm.expectRevert("Invalid price");
+        ubiquityPoolFacet.updateChainLinkCollateralPrice(0);
+    }
+
+    function testUpdateChainLinkCollateralPrice_ShouldRevert_IfChainlinkAnswerIsStale()
+        public
+    {
+        // set stale answer from chainlink
+        collateralTokenPriceFeed.updateMockParams(
+            1, // round id
+            100_000_000, // answer, 100_000_000 = $1.00
+            block.timestamp, // started at
+            block.timestamp, // updated at
+            1 // answered in round
+        );
+
+        // wait 1 day
+        vm.warp(block.timestamp + 1 days);
+
+        vm.expectRevert("Stale data");
+        ubiquityPoolFacet.updateChainLinkCollateralPrice(0);
+    }
+
+    function testUpdateChainLinkCollateralPrice_ShouldUpdateCollateralPrice()
+        public
+    {
+        // before
+        LibUbiquityPool.CollateralInformation memory info = ubiquityPoolFacet
+            .collateralInformation(address(collateralToken));
+        assertEq(info.price, 1_000_000);
+
+        // set answer from chainlink
+        collateralTokenPriceFeed.updateMockParams(
+            1, // round id
+            99_000_000, // answer, 99_000_000 = $0.99
+            block.timestamp, // started at
+            block.timestamp, // updated at
+            1 // answered in round
+        );
+
+        // update collateral price
+        ubiquityPoolFacet.updateChainLinkCollateralPrice(0);
+
+        // after
+        info = ubiquityPoolFacet.collateralInformation(
+            address(collateralToken)
+        );
+        assertEq(info.price, 990_000);
+    }
+
     //=========================
     // AMO minters functions
     //=========================
@@ -597,6 +676,11 @@ contract UbiquityPoolFacetTest is DiamondTestSetup {
         assertEq(info.index, 0);
         assertEq(info.symbol, "CLT");
         assertEq(info.collateralAddress, address(collateralToken));
+        assertEq(
+            info.collateralPriceFeedAddress,
+            address(collateralTokenPriceFeed)
+        );
+        assertEq(info.collateralPriceFeedStalenessThreshold, 1 days);
         assertEq(info.isEnabled, true);
         assertEq(info.missingDecimals, 0);
         assertEq(info.price, 1_000_000);
@@ -618,22 +702,39 @@ contract UbiquityPoolFacetTest is DiamondTestSetup {
         vm.stopPrank();
     }
 
-    function testSetCollateralPrice_ShouldSetCollateralPriceInUsd() public {
+    function testSetCollateralChainLinkPriceFeed_ShouldSetPriceFeed() public {
         vm.startPrank(admin);
 
         LibUbiquityPool.CollateralInformation memory info = ubiquityPoolFacet
             .collateralInformation(address(collateralToken));
-        assertEq(info.price, 1_000_000);
+        assertEq(
+            info.collateralPriceFeedAddress,
+            address(collateralTokenPriceFeed)
+        );
+        assertEq(info.collateralPriceFeedStalenessThreshold, 1 days);
 
-        uint256 newCollateralPrice = 1_100_000;
+        address newPriceFeedAddress = address(1);
+        uint256 newStalenessThreshold = 2 days;
         vm.expectEmit(address(ubiquityPoolFacet));
-        emit CollateralPriceSet(0, newCollateralPrice);
-        ubiquityPoolFacet.setCollateralPrice(0, newCollateralPrice);
+        emit CollateralPriceFeedSet(
+            0,
+            newPriceFeedAddress,
+            newStalenessThreshold
+        );
+        ubiquityPoolFacet.setCollateralChainLinkPriceFeed(
+            address(collateralToken),
+            newPriceFeedAddress,
+            newStalenessThreshold
+        );
 
         info = ubiquityPoolFacet.collateralInformation(
             address(collateralToken)
         );
-        assertEq(info.price, newCollateralPrice);
+        assertEq(info.collateralPriceFeedAddress, newPriceFeedAddress);
+        assertEq(
+            info.collateralPriceFeedStalenessThreshold,
+            newStalenessThreshold
+        );
 
         vm.stopPrank();
     }
@@ -777,113 +878,4 @@ contract UbiquityPoolFacetTest is DiamondTestSetup {
 
         vm.stopPrank();
     }
-
-    function testCollateralTwap() public {
-        vm.startPrank(admin);
-
-        // use mocked meta pool for collateral twap
-        MockMetaPool collateralCurveMetaPool;
-
-        collateralCurveMetaPool = new MockMetaPool(
-            address(collateralToken),
-            address(curveTriPoolLpToken)
-        );
-
-        uint256[2] memory price_cumulative_last = [
-            uint256(1e18),
-            uint256(1e18)
-        ];
-        uint256 last_block_timestamp = 100000;
-        uint256[2] memory twap_balances = [uint256(1e18), uint256(1e18)];
-        uint256[2] memory dy_values = [uint256(1e18), uint256(1e18)];
-
-        collateralCurveMetaPool.updateMockParams(
-            price_cumulative_last,
-            last_block_timestamp,
-            twap_balances,
-            dy_values
-        );
-
-        uint256[2] memory current_twap = collateralCurveMetaPool
-            .get_twap_balances(twap_balances, twap_balances, 1);
-
-        console.log(current_twap[0]);
-        console.log(current_twap[1]);
-
-        vm.stopPrank();
-    }
-
-    function testCollateralPriceFeed_ShouldUpdatePrice() public {
-        vm.startPrank(admin);
-
-        LibUbiquityPool.CollateralInformation memory info = ubiquityPoolFacet
-            .collateralInformation(address(collateralToken));
-        assertEq(info.price, 1_000_000);
-
-        int256 mockedPriceFeedPrice = 1_000_123;
-
-        MockChainLinkFeed(collateralTokenPriceFeed).updateMockParams(
-            1,
-            mockedPriceFeedPrice,
-            3
-        );
-
-        ubiquityPoolFacet.updateChainLinkCollateralPrice(0);
-
-        info = ubiquityPoolFacet.collateralInformation(
-            address(collateralToken)
-        );
-        assertEq(info.price, uint256(mockedPriceFeedPrice));
-
-        vm.stopPrank();
-    }
-
-    function testCollateralPriceFeed_ShouldWorkWithMultipleCollateralTokens()
-        public
-    {
-        vm.startPrank(admin);
-
-        LibUbiquityPool.CollateralInformation memory info = ubiquityPoolFacet
-            .collateralInformation(address(collateralToken));
-        assertEq(info.price, 1_000_000);
-
-        // init collateral token
-        MockERC20 collateralToken2 = new MockERC20("COLLATERAL2", "CLT2", 18);
-
-        // init collateral price feed and set price to 0.999999
-        MockChainLinkFeed collateralTokenPriceFeed2 = new MockChainLinkFeed();
-        MockChainLinkFeed(collateralTokenPriceFeed2).updateMockParams(
-            1,
-            999_999,
-            2
-        );
-        // add second collateral token to the pool
-        uint256 poolCeiling = 5_000e18;
-        ubiquityPoolFacet.addCollateralToken(
-            address(collateralToken2),
-            poolCeiling
-        );
-
-        // enable collateral at index 1
-        ubiquityPoolFacet.toggleCollateral(1);
-
-        ubiquityPoolFacet.setCollateralChainLinkPriceFeedAddress(
-            address(collateralToken2),
-            address(collateralTokenPriceFeed2)
-        );
-
-        ubiquityPoolFacet.updateChainLinkCollateralPrice(1);
-
-        info = ubiquityPoolFacet.collateralInformation(
-            address(collateralToken)
-        );
-        assertEq(info.price, 1_000_000);
-
-        info = ubiquityPoolFacet.collateralInformation(
-            address(collateralToken2)
-        );
-        assertEq(info.price, 999_999);
-
-        vm.stopPrank();
-    }
 }
diff --git a/yarn.lock b/yarn.lock
index 90707cc95..a82fb4672 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -270,18 +270,6 @@ __metadata:
   languageName: node
   linkType: hard
 
-"@chainlink/contracts@npm:^0.8.0":
-  version: 0.8.0
-  resolution: "@chainlink/contracts@npm:0.8.0"
-  dependencies:
-    "@eth-optimism/contracts": ^0.5.21
-    "@openzeppelin/contracts": ~4.3.3
-    "@openzeppelin/contracts-upgradeable-4.7.3": "npm:@openzeppelin/contracts-upgradeable@v4.7.3"
-    "@openzeppelin/contracts-v0.7": "npm:@openzeppelin/contracts@v3.4.2"
-  checksum: 4165aa7fa5d28c2ebef4da72e117f78889da292cff03d736d5738e2a3842d5fd162718106c2c04e9acb41298813edc99a27c456217a00bfdd4b0dfb758802cd0
-  languageName: node
-  linkType: hard
-
 "@coinbase/wallet-sdk@npm:^3.5.4":
   version: 3.6.5
   resolution: "@coinbase/wallet-sdk@npm:3.6.5"
@@ -1313,43 +1301,6 @@ __metadata:
   languageName: node
   linkType: hard
 
-"@eth-optimism/contracts@npm:^0.5.21":
-  version: 0.5.40
-  resolution: "@eth-optimism/contracts@npm:0.5.40"
-  dependencies:
-    "@eth-optimism/core-utils": 0.12.0
-    "@ethersproject/abstract-provider": ^5.7.0
-    "@ethersproject/abstract-signer": ^5.7.0
-  peerDependencies:
-    ethers: ^5
-  checksum: 11dde466c90b886efe8b5fd123fa5893187a4ff84839213d417f90ae4e45bf00b2f62d56e4ebe23ba5dd7ec33beed22c4c41e206add35fce0db073fe2dbae6ba
-  languageName: node
-  linkType: hard
-
-"@eth-optimism/core-utils@npm:0.12.0":
-  version: 0.12.0
-  resolution: "@eth-optimism/core-utils@npm:0.12.0"
-  dependencies:
-    "@ethersproject/abi": ^5.7.0
-    "@ethersproject/abstract-provider": ^5.7.0
-    "@ethersproject/address": ^5.7.0
-    "@ethersproject/bignumber": ^5.7.0
-    "@ethersproject/bytes": ^5.7.0
-    "@ethersproject/constants": ^5.7.0
-    "@ethersproject/contracts": ^5.7.0
-    "@ethersproject/hash": ^5.7.0
-    "@ethersproject/keccak256": ^5.7.0
-    "@ethersproject/properties": ^5.7.0
-    "@ethersproject/providers": ^5.7.0
-    "@ethersproject/rlp": ^5.7.0
-    "@ethersproject/transactions": ^5.7.0
-    "@ethersproject/web": ^5.7.0
-    bufio: ^1.0.7
-    chai: ^4.3.4
-  checksum: 1c820107c44bdbb46becb1b00fd0dabb44f3ac8f54e6da7872a5a134411fad26f53b193225da55e79d6a8d7f0d01cc16a123db5d41ebaf02ca78360249a4b52a
-  languageName: node
-  linkType: hard
-
 "@ethersproject/abi@npm:5, @ethersproject/abi@npm:5.7.0, @ethersproject/abi@npm:^5.0.0, @ethersproject/abi@npm:^5.0.12, @ethersproject/abi@npm:^5.1.2, @ethersproject/abi@npm:^5.5.0, @ethersproject/abi@npm:^5.7.0":
   version: 5.7.0
   resolution: "@ethersproject/abi@npm:5.7.0"
@@ -1456,7 +1407,7 @@ __metadata:
   languageName: node
   linkType: hard
 
-"@ethersproject/contracts@npm:5.7.0, @ethersproject/contracts@npm:^5.7.0":
+"@ethersproject/contracts@npm:5.7.0":
   version: 5.7.0
   resolution: "@ethersproject/contracts@npm:5.7.0"
   dependencies:
@@ -1577,7 +1528,7 @@ __metadata:
   languageName: node
   linkType: hard
 
-"@ethersproject/providers@npm:5.7.2, @ethersproject/providers@npm:^5, @ethersproject/providers@npm:^5.6.8, @ethersproject/providers@npm:^5.7.0":
+"@ethersproject/providers@npm:5.7.2, @ethersproject/providers@npm:^5, @ethersproject/providers@npm:^5.6.8":
   version: 5.7.2
   resolution: "@ethersproject/providers@npm:5.7.2"
   dependencies:
@@ -2711,20 +2662,6 @@ __metadata:
   languageName: node
   linkType: hard
 
-"@openzeppelin/contracts-upgradeable-4.7.3@npm:@openzeppelin/contracts-upgradeable@v4.7.3":
-  version: 4.7.3
-  resolution: "@openzeppelin/contracts-upgradeable@npm:4.7.3"
-  checksum: c9ffb40cb847a975d440204fc6a811f43af960050242f707332b984d29bd16dc242ffa0935de61867aeb9e0357fadedb16b09b276deda5e9775582face831021
-  languageName: node
-  linkType: hard
-
-"@openzeppelin/contracts-v0.7@npm:@openzeppelin/contracts@v3.4.2":
-  version: 3.4.2
-  resolution: "@openzeppelin/contracts@npm:3.4.2"
-  checksum: 0c90f029fe50a49643588e4c8670dae3bbf31795133a6ddce9bdcbc258486332700bb732287baabf7bf807f39182fe8ea2ffa19aa5caf359b1b9c0f083280748
-  languageName: node
-  linkType: hard
-
 "@openzeppelin/contracts@npm:3.4.1-solc-0.7-2":
   version: 3.4.1-solc-0.7-2
   resolution: "@openzeppelin/contracts@npm:3.4.1-solc-0.7-2"
@@ -2739,13 +2676,6 @@ __metadata:
   languageName: node
   linkType: hard
 
-"@openzeppelin/contracts@npm:~4.3.3":
-  version: 4.3.3
-  resolution: "@openzeppelin/contracts@npm:4.3.3"
-  checksum: 73eb23e7acc8531931076d11251629bdc8579c99ef921a3facbd8abf7b860bc214c97a4bd56cd67db7c9d1db837765132640f24987aac8097a13960bb41ca6d1
-  languageName: node
-  linkType: hard
-
 "@pedrouid/environment@npm:^1.0.1":
   version: 1.0.1
   resolution: "@pedrouid/environment@npm:1.0.1"
@@ -3819,7 +3749,6 @@ __metadata:
   version: 0.0.0-use.local
   resolution: "@ubiquity/contracts@workspace:packages/contracts"
   dependencies:
-    "@chainlink/contracts": ^0.8.0
     "@types/command-line-args": 5.2.0
     "@types/node": ^18.11.18
     "@types/react-transition-group": ^4
@@ -6244,13 +6173,6 @@ __metadata:
   languageName: node
   linkType: hard
 
-"bufio@npm:^1.0.7":
-  version: 1.2.1
-  resolution: "bufio@npm:1.2.1"
-  checksum: b6e1216f4a5877617a3580b83807d8b96c794c015bc2d5eb9e70e152dc79fe923517472bd96df3d5b8feb59a0e25e2aa3cd8a70b8f90905b92d86f2e5719ed68
-  languageName: node
-  linkType: hard
-
 "bunyan-blackhole@npm:^1.1.1":
   version: 1.1.1
   resolution: "bunyan-blackhole@npm:1.1.1"
@@ -6397,21 +6319,6 @@ __metadata:
   languageName: node
   linkType: hard
 
-"chai@npm:^4.3.4":
-  version: 4.3.10
-  resolution: "chai@npm:4.3.10"
-  dependencies:
-    assertion-error: ^1.1.0
-    check-error: ^1.0.3
-    deep-eql: ^4.1.3
-    get-func-name: ^2.0.2
-    loupe: ^2.3.6
-    pathval: ^1.1.1
-    type-detect: ^4.0.8
-  checksum: 536668c60a0d985a0fbd94418028e388d243a925d7c5e858c7443e334753511614a3b6a124bac9ca077dfc4c37acc367d62f8c294960f440749536dc181dfc6d
-  languageName: node
-  linkType: hard
-
 "chai@npm:^4.3.7":
   version: 4.3.7
   resolution: "chai@npm:4.3.7"
@@ -6462,15 +6369,6 @@ __metadata:
   languageName: node
   linkType: hard
 
-"check-error@npm:^1.0.3":
-  version: 1.0.3
-  resolution: "check-error@npm:1.0.3"
-  dependencies:
-    get-func-name: ^2.0.2
-  checksum: e2131025cf059b21080f4813e55b3c480419256914601750b0fee3bd9b2b8315b531e551ef12560419b8b6d92a3636511322752b1ce905703239e7cc451b6399
-  languageName: node
-  linkType: hard
-
 "chokidar@npm:3.5.3, chokidar@npm:^3.4.0, chokidar@npm:^3.5.2, chokidar@npm:^3.5.3":
   version: 3.5.3
   resolution: "chokidar@npm:3.5.3"
@@ -7344,7 +7242,7 @@ __metadata:
   languageName: node
   linkType: hard
 
-"deep-eql@npm:^4.1.2, deep-eql@npm:^4.1.3":
+"deep-eql@npm:^4.1.2":
   version: 4.1.3
   resolution: "deep-eql@npm:4.1.3"
   dependencies:
@@ -8816,13 +8714,6 @@ __metadata:
   languageName: node
   linkType: hard
 
-"get-func-name@npm:^2.0.1, get-func-name@npm:^2.0.2":
-  version: 2.0.2
-  resolution: "get-func-name@npm:2.0.2"
-  checksum: 3f62f4c23647de9d46e6f76d2b3eafe58933a9b3830c60669e4180d6c601ce1b4aa310ba8366143f55e52b139f992087a9f0647274e8745621fa2af7e0acf13b
-  languageName: node
-  linkType: hard
-
 "get-intrinsic@npm:^1.0.2, get-intrinsic@npm:^1.1.1, get-intrinsic@npm:^1.1.3, get-intrinsic@npm:^1.2.0":
   version: 1.2.0
   resolution: "get-intrinsic@npm:1.2.0"
@@ -10632,15 +10523,6 @@ __metadata:
   languageName: node
   linkType: hard
 
-"loupe@npm:^2.3.6":
-  version: 2.3.7
-  resolution: "loupe@npm:2.3.7"
-  dependencies:
-    get-func-name: ^2.0.1
-  checksum: 96c058ec7167598e238bb7fb9def2f9339215e97d6685d9c1e3e4bdb33d14600e11fe7a812cf0c003dfb73ca2df374f146280b2287cae9e8d989e9d7a69a203b
-  languageName: node
-  linkType: hard
-
 "lru-cache@npm:^4.0.1":
   version: 4.1.5
   resolution: "lru-cache@npm:4.1.5"
@@ -14482,7 +14364,7 @@ __metadata:
   languageName: node
   linkType: hard
 
-"type-detect@npm:^4.0.0, type-detect@npm:^4.0.5, type-detect@npm:^4.0.8":
+"type-detect@npm:^4.0.0, type-detect@npm:^4.0.5":
   version: 4.0.8
   resolution: "type-detect@npm:4.0.8"
   checksum: 62b5628bff67c0eb0b66afa371bd73e230399a8d2ad30d852716efcc4656a7516904570cd8631a49a3ce57c10225adf5d0cbdcb47f6b0255fe6557c453925a15