Skip to content

Commit

Permalink
feat: minor contracts cleanup (#47)
Browse files Browse the repository at this point in the history
In this PR:
* remove obsolete analytics methods in `BoundedPriceFeed`
* `BoundedPriceFeed`'s constructor now reverts if `bound` is below zero
* remove pyth dependency in favor of local interfaces
* add some missing getters here and there
* proper price feed descriptions
* minor nits
  • Loading branch information
lekhovitsky authored Jul 10, 2024
1 parent 66ba1f5 commit 69f53b8
Show file tree
Hide file tree
Showing 28 changed files with 258 additions and 269 deletions.
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,6 @@
[submodule "lib/@openzeppelin"]
path = lib/@openzeppelin
url = https://github.com/OpenZeppelin/openzeppelin-contracts
[submodule "lib/@solady"]
path = lib/@solady
url = https://github.com/Vectorized/solady
18 changes: 0 additions & 18 deletions contracts/interfaces/chainlink/AggregatorInterface.sol

This file was deleted.

7 changes: 0 additions & 7 deletions contracts/interfaces/chainlink/AggregatorV2V3Interface.sol

This file was deleted.

20 changes: 0 additions & 20 deletions contracts/interfaces/chainlink/AggregatorV3Interface.sol

This file was deleted.

2 changes: 2 additions & 0 deletions contracts/interfaces/mellow/IMellowVault.sol
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
// SPDX-License-Identifier: MIT
// Gearbox Protocol. Generalized leverage for DeFi protocols
// (c) Gearbox Foundation, 2024.
pragma solidity ^0.8.23;

interface IMellowVault {
Expand Down
13 changes: 13 additions & 0 deletions contracts/interfaces/pyth/IPyth.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// SPDX-License-Identifier: MIT
// Gearbox Protocol. Generalized leverage for DeFi protocols
// (c) Gearbox Foundation, 2024.
pragma solidity ^0.8.23;

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

interface IPyth {
function updatePriceFeeds(bytes[] calldata updateData) external payable;
function getPriceUnsafe(bytes32 id) external view returns (PythStructs.Price memory price);
function latestPriceInfoPublishTime(bytes32 priceFeedId) external view returns (uint64);
function getUpdateFee(bytes[] calldata updateData) external view returns (uint256 feeAmount);
}
13 changes: 13 additions & 0 deletions contracts/interfaces/pyth/PythStructs.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// SPDX-License-Identifier: MIT
// Gearbox Protocol. Generalized leverage for DeFi protocols
// (c) Gearbox Foundation, 2024.
pragma solidity ^0.8.23;

contract PythStructs {
struct Price {
int64 price;
uint64 conf;
int32 expo;
uint256 publishTime;
}
}
41 changes: 14 additions & 27 deletions contracts/oracles/BoundedPriceFeed.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,19 @@
// (c) Gearbox Foundation, 2024.
pragma solidity ^0.8.23;

import {AggregatorV2V3Interface} from "../interfaces/chainlink/AggregatorV2V3Interface.sol";

import {LibString} from "@solady/utils/LibString.sol";
import {IncorrectParameterException} from "@gearbox-protocol/core-v3/contracts/interfaces/IExceptions.sol";
import {IPriceFeed} from "@gearbox-protocol/core-v3/contracts/interfaces/base/IPriceFeed.sol";
import {SanityCheckTrait} from "@gearbox-protocol/core-v3/contracts/traits/SanityCheckTrait.sol";
import {PriceFeedValidationTrait} from "@gearbox-protocol/core-v3/contracts/traits/PriceFeedValidationTrait.sol";

interface ChainlinkReadableAggregator {
function aggregator() external view returns (address);
function phaseAggregators(uint16 idx) external view returns (AggregatorV2V3Interface);
function phaseId() external view returns (uint16);
}

/// @title Bounded price feed
/// @notice Can be used to provide upper-bounded answers for assets that are
/// expected to have the price in a certain range, e.g. stablecoins
contract BoundedPriceFeed is IPriceFeed, ChainlinkReadableAggregator, SanityCheckTrait, PriceFeedValidationTrait {
contract BoundedPriceFeed is IPriceFeed, SanityCheckTrait, PriceFeedValidationTrait {
using LibString for string;
using LibString for bytes32;

uint256 public constant override version = 3_10;
bytes32 public constant override contractType = "PF_BOUNDED_ORACLE";

Expand All @@ -33,22 +30,28 @@ contract BoundedPriceFeed is IPriceFeed, ChainlinkReadableAggregator, SanityChec
/// @notice Upper bound for underlying price feed answers
int256 public immutable upperBound;

/// @dev Price feed description ticker
bytes32 internal _descriptionTicker;

/// @notice Constructor
/// @param _priceFeed Underlying price feed
/// @param _stalenessPeriod Underlying price feed staleness period, must be non-zero unless it performs own checks
/// @param _upperBound Upper bound for underlying price feed answers
constructor(address _priceFeed, uint32 _stalenessPeriod, int256 _upperBound)
/// @param descriptionTicker Ticker to use in price feed description
constructor(address _priceFeed, uint32 _stalenessPeriod, int256 _upperBound, string memory descriptionTicker)
nonZeroAddress(_priceFeed) // U:[BPF-1]
{
if (_upperBound <= 0) revert IncorrectParameterException(); // U:[BPF-1]
priceFeed = _priceFeed; // U:[BPF-1]
stalenessPeriod = _stalenessPeriod; // U:[BPF-1]
skipCheck = _validatePriceFeed(priceFeed, stalenessPeriod); // U:[BPF-1]
upperBound = _upperBound; // U:[BPF-1]
_descriptionTicker = descriptionTicker.toSmallString();
}

/// @notice Price feed description
function description() external view override returns (string memory) {
return string(abi.encodePacked(IPriceFeed(priceFeed).description(), " bounded price feed")); // U:[BPF-2]
return string.concat(_descriptionTicker.fromSmallString(), " bounded price feed"); // U:[BPF-2]
}

/// @notice Returns the upper-bounded USD price of the token
Expand All @@ -61,20 +64,4 @@ contract BoundedPriceFeed is IPriceFeed, ChainlinkReadableAggregator, SanityChec
function _upperBoundValue(int256 value) internal view returns (int256) {
return (value > upperBound) ? upperBound : value;
}

// --------- //
// ANALYTICS //
// --------- //

function aggregator() external view override returns (address) {
return ChainlinkReadableAggregator(priceFeed).aggregator();
}

function phaseAggregators(uint16 idx) external view override returns (AggregatorV2V3Interface) {
return ChainlinkReadableAggregator(priceFeed).phaseAggregators(idx);
}

function phaseId() external view override returns (uint16) {
return ChainlinkReadableAggregator(priceFeed).phaseId();
}
}
25 changes: 15 additions & 10 deletions contracts/oracles/CompositePriceFeed.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// (c) Gearbox Foundation, 2024.
pragma solidity ^0.8.23;

import {LibString} from "@solady/utils/LibString.sol";
import {PriceFeedParams} from "./PriceFeedParams.sol";
import {IPriceFeed} from "@gearbox-protocol/core-v3/contracts/interfaces/base/IPriceFeed.sol";
import {PriceFeedValidationTrait} from "@gearbox-protocol/core-v3/contracts/traits/PriceFeedValidationTrait.sol";
Expand All @@ -11,6 +12,9 @@ import {SanityCheckTrait} from "@gearbox-protocol/core-v3/contracts/traits/Sanit
/// @title Composite price feed
/// @notice Computes target asset USD price as product of target/base price times base/USD price
contract CompositePriceFeed is IPriceFeed, PriceFeedValidationTrait, SanityCheckTrait {
using LibString for string;
using LibString for bytes32;

uint256 public constant override version = 3_10;
bytes32 public constant override contractType = "PF_COMPOSITE_ORACLE";

Expand All @@ -20,6 +24,7 @@ contract CompositePriceFeed is IPriceFeed, PriceFeedValidationTrait, SanityCheck
/// @notice Price feed that returns target asset price denominated in base asset
address public immutable priceFeed0;
uint32 public immutable stalenessPeriod0;
bool public immutable skipCheck0;

/// @notice Price feed that returns base price denominated in USD
address public immutable priceFeed1;
Expand All @@ -29,10 +34,14 @@ contract CompositePriceFeed is IPriceFeed, PriceFeedValidationTrait, SanityCheck
/// @notice Scale of answers in target/base price feed
int256 public immutable targetFeedScale;

/// @dev Price feed description ticker
bytes32 internal _descriptionTicker;

/// @notice Constructor
/// @param priceFeeds Array with two price feeds, where the first one returns target asset price
/// denominated in base asset, and the second one returns base price denominated in USD
constructor(PriceFeedParams[2] memory priceFeeds)
/// @param descriptionTicker Ticker to use in price feed description
constructor(PriceFeedParams[2] memory priceFeeds, string memory descriptionTicker)
nonZeroAddress(priceFeeds[0].priceFeed) // U:[CPF-1]
nonZeroAddress(priceFeeds[1].priceFeed) // U:[CPF-1]
{
Expand All @@ -44,24 +53,20 @@ contract CompositePriceFeed is IPriceFeed, PriceFeedValidationTrait, SanityCheck

targetFeedScale = int256(10 ** IPriceFeed(priceFeed0).decimals()); // U:[CPF-1]
// target/base price feed validation is omitted because it will fail if feed has other than 8 decimals
skipCheck0 = false; // U:[CPF-1]
skipCheck1 = _validatePriceFeed(priceFeed1, stalenessPeriod1); // U:[CPF-1]

_descriptionTicker = descriptionTicker.toSmallString();
}

/// @notice Price feed description
function description() external view override returns (string memory) {
return string(
abi.encodePacked(
IPriceFeed(priceFeed0).description(),
" * ",
IPriceFeed(priceFeed1).description(),
" composite price feed"
)
); // U:[CPF-2]
return string.concat(_descriptionTicker.fromSmallString(), " composite price feed"); // U:[CPF-2]
}

/// @notice Returns the USD price of the target asset, computed as target/base price times base/USD price
function latestRoundData() external view override returns (uint80, int256 answer, uint256, uint256, uint80) {
answer = _getValidatedPrice(priceFeed0, stalenessPeriod0, false); // U:[CPF-3]
answer = _getValidatedPrice(priceFeed0, stalenessPeriod0, skipCheck0); // U:[CPF-3]
int256 answer2 = _getValidatedPrice(priceFeed1, stalenessPeriod1, skipCheck1); // U:[CPF-3]
answer = (answer * answer2) / targetFeedScale; // U:[CPF-3]
return (0, answer, 0, 0, 0);
Expand Down
2 changes: 1 addition & 1 deletion contracts/oracles/LPPriceFeed.sol
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ abstract contract LPPriceFeed is ILPPriceFeed, ControlledTrait, SanityCheckTrait

/// @notice Price feed description
function description() external view override returns (string memory) {
return string(abi.encodePacked(ERC20(lpToken).symbol(), " / USD price feed")); // U:[LPPF-2]
return string.concat(ERC20(lpToken).symbol(), " / USD LP price feed"); // U:[LPPF-2]
}

/// @notice Returns USD price of the LP token with 8 decimals
Expand Down
18 changes: 4 additions & 14 deletions contracts/oracles/balancer/BPTWeightedPriceFeed.sol
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ contract BPTWeightedPriceFeed is LPPriceFeed {

uint256 weightedPrice = FixedPoint.ONE;
uint256 currentBase = FixedPoint.ONE;
for (uint256 i = 0; i < numAssets;) {
for (uint256 i = 0; i < numAssets; ++i) {
(address priceFeed, uint32 stalenessPeriod, bool skipCheck) = _getPriceFeedParams(i);
answer = _getValidatedPrice(priceFeed, stalenessPeriod, skipCheck);
answer = answer * int256(WAD_OVER_USD_FEED_SCALE);
Expand All @@ -186,10 +186,6 @@ contract BPTWeightedPriceFeed is LPPriceFeed {
weightedPrice = weightedPrice.mulDown(currentBase.powDown(weights[i]));
currentBase = FixedPoint.ONE;
}

unchecked {
++i;
}
}

answer = int256(weightedPrice / (numAssets * WAD_OVER_USD_FEED_SCALE)); // U:[BAL-W-2]
Expand All @@ -212,16 +208,12 @@ contract BPTWeightedPriceFeed is LPPriceFeed {

k = FixedPoint.ONE;
uint256 currentBase = FixedPoint.ONE;
for (uint256 i = 0; i < len;) {
for (uint256 i = 0; i < len; ++i) {
currentBase = currentBase.mulDown(balances[i]);
if (i == len - 1 || weights[i] != weights[i + 1]) {
k = k.mulDown(currentBase.powDown(weights[i]));
currentBase = FixedPoint.ONE;
}

unchecked {
++i;
}
}
}

Expand Down Expand Up @@ -295,10 +287,8 @@ contract BPTWeightedPriceFeed is LPPriceFeed {
function _sort(uint256[] memory data) internal pure returns (uint256[] memory indices) {
uint256 len = data.length;
indices = new uint256[](len);
unchecked {
for (uint256 i; i < len; ++i) {
indices[i] = i;
}
for (uint256 i; i < len; ++i) {
indices[i] = i;
}
_quickSort(data, indices, 0, len - 1);
}
Expand Down
2 changes: 1 addition & 1 deletion contracts/oracles/curve/CurveCryptoLPPriceFeed.sol
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ contract CurveCryptoLPPriceFeed is LPPriceFeed {
using FixedPoint for uint256;

uint256 public constant override version = 3_10;
bytes32 public constant contractType = "PF_CURVE_CRYPTO_ORACLE";
bytes32 public constant contractType = "PF_CURVE_CRYPTO_LP_ORACLE";

uint16 public immutable nCoins;

Expand Down
6 changes: 1 addition & 5 deletions contracts/oracles/curve/CurveStableLPPriceFeed.sol
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import {WAD} from "@gearbox-protocol/core-v3/contracts/libraries/Constants.sol";
/// @dev Older pools may be decoupled from their LP token, so constructor accepts both token and pool
contract CurveStableLPPriceFeed is LPPriceFeed {
uint256 public constant override version = 3_10;
bytes32 public immutable override contractType;
bytes32 public constant override contractType = "PF_CURVE_STABLE_LP_ORACLE";

uint16 public immutable nCoins;

Expand Down Expand Up @@ -55,10 +55,6 @@ contract CurveStableLPPriceFeed is LPPriceFeed {
skipCheck2 = nCoins > 2 ? _validatePriceFeed(priceFeed2, stalenessPeriod2) : false;
skipCheck3 = nCoins > 3 ? _validatePriceFeed(priceFeed3, stalenessPeriod3) : false;

contractType = nCoins == 2
? bytes32("PF_CURVE_2LP_ORACLE")
: (nCoins == 3 ? bytes32("PF_CURVE_3LP_ORACLE") : bytes32("PF_CURVE_4LP_ORACLE"));

_setLimiter(lowerBound); // U:[CRV-S-1]
}

Expand Down
1 change: 0 additions & 1 deletion contracts/oracles/mellow/MellowLRTPriceFeed.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
pragma solidity ^0.8.23;

import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import {IStateSerializer} from "../../interfaces/IStateSerializer.sol";
import {IMellowVault} from "../../interfaces/mellow/IMellowVault.sol";
import {SingleAssetLPPriceFeed} from "../SingleAssetLPPriceFeed.sol";
import {WAD} from "@gearbox-protocol/core-v3/contracts/libraries/Constants.sol";
Expand Down
Loading

0 comments on commit 69f53b8

Please sign in to comment.