-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
6 changed files
with
407 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
// SPDX-License-Identifier: MIT | ||
// Gearbox Protocol. Generalized leverage for DeFi protocols | ||
// (c) Gearbox Holdings, 2022 | ||
pragma solidity ^0.8.10; | ||
|
||
import {AggregatorV3Interface} from "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol"; | ||
import {IPriceFeedType} from "./IPriceFeedType.sol"; | ||
|
||
interface ILPPriceFeedEvents { | ||
/// @dev Emits on updating the virtual price bounds | ||
event NewLimiterParams(uint256 lowerBound, uint256 upperBound); | ||
} | ||
|
||
interface ILPPriceFeedExceptions { | ||
/// @dev Thrown on returning a value that violates the current bounds | ||
error ValueOutOfRangeException(); | ||
|
||
/// @dev Thrown on failing sanity checks when setting new bounds | ||
error IncorrectLimitsException(); | ||
} | ||
|
||
/// @title Interface for LP PriceFeeds with limiter | ||
interface ILPPriceFeed is AggregatorV3Interface, IPriceFeedType, ILPPriceFeedEvents, ILPPriceFeedExceptions { | ||
/// @dev Sets the lower and upper bounds for virtual price. | ||
/// @param _lowerBound The new lower bound | ||
/// @notice The upper bound is computed automatically | ||
function setLimiter(uint256 _lowerBound) external; | ||
|
||
/// @dev Returns the lower bound | ||
function lowerBound() external view returns (uint256); | ||
|
||
/// @dev Returns the upper bound | ||
function upperBound() external view returns (uint256); | ||
|
||
/// @dev Returns the pre-defined window between the lower and upper bounds | ||
/// @notice In bp format | ||
function delta() external view returns (uint256); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
// SPDX-License-Identifier: BUSL-1.1 | ||
// Gearbox Protocol. Generalized leverage for DeFi protocols | ||
// (c) Gearbox Holdings, 2022 | ||
pragma solidity ^0.8.10; | ||
|
||
import {AggregatorV3Interface} from "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol"; | ||
import {AggregatorV2V3Interface} from "@chainlink/contracts/src/v0.8/interfaces/AggregatorV2V3Interface.sol"; | ||
import {PERCENTAGE_FACTOR} from "@gearbox-protocol/core-v2/contracts/libraries/PercentageMath.sol"; | ||
import {PriceFeedType, IPriceFeedType} from "../interfaces/IPriceFeedType.sol"; | ||
|
||
// EXCEPTIONS | ||
import {NotImplementedException} from "../interfaces/IErrors.sol"; | ||
|
||
interface ChainlinkReadableAggregator { | ||
function aggregator() external view returns (address); | ||
|
||
function phaseAggregators(uint16 idx) external view returns (AggregatorV2V3Interface); | ||
|
||
function phaseId() external view returns (uint16); | ||
} | ||
|
||
/// @title Price feed with an upper bound on price | ||
/// @notice Used to limit prices on assets that should not rise above | ||
/// a certain level, such as stablecoins and other pegged assets | ||
contract BoundedPriceFeed is ChainlinkReadableAggregator, AggregatorV3Interface, IPriceFeedType { | ||
/// @dev Chainlink price feed for the Vault's underlying | ||
AggregatorV3Interface public immutable priceFeed; | ||
|
||
/// @dev The upper bound on Chainlink price for the asset | ||
int256 public immutable upperBound; | ||
|
||
/// @dev Decimals of the returned result. | ||
uint8 public immutable override decimals; | ||
|
||
/// @dev Price feed description | ||
string public override description; | ||
|
||
uint256 public constant override version = 1; | ||
|
||
PriceFeedType public constant override priceFeedType = PriceFeedType.BOUNDED_ORACLE; | ||
|
||
bool public constant override skipPriceCheck = false; | ||
|
||
/// @dev Constructor | ||
/// @param _priceFeed Chainlink price feed to receive results from | ||
/// @param _upperBound Initial upper bound for the Chainlink price | ||
constructor(address _priceFeed, int256 _upperBound) { | ||
priceFeed = AggregatorV3Interface(_priceFeed); | ||
description = string(abi.encodePacked(priceFeed.description(), " Bounded")); | ||
decimals = priceFeed.decimals(); | ||
upperBound = _upperBound; | ||
} | ||
|
||
/// @dev Implemented for compatibility, but reverts since Gearbox's price feeds | ||
/// do not store historical data. | ||
function getRoundData(uint80) | ||
external | ||
pure | ||
virtual | ||
override | ||
returns ( | ||
uint80, // roundId, | ||
int256, // answer, | ||
uint256, // startedAt, | ||
uint256, // updatedAt, | ||
uint80 // answeredInRound | ||
) | ||
{ | ||
revert NotImplementedException(); // F:[LPF-2] | ||
} | ||
|
||
/// @dev Returns the value if it is below the upper bound, otherwise returns the upper bound | ||
/// @param value Value to be checked and bounded | ||
function _upperBoundValue(int256 value) internal view returns (int256) { | ||
return (value > upperBound) ? upperBound : value; | ||
} | ||
|
||
/// @dev Returns the upper-bounded USD price of the token | ||
function latestRoundData() | ||
external | ||
view | ||
override | ||
returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound) | ||
{ | ||
(roundId, answer, startedAt, updatedAt, answeredInRound) = priceFeed.latestRoundData(); // F:[OYPF-4] | ||
|
||
answer = _upperBoundValue(answer); | ||
} | ||
|
||
/// @dev Returns the current phase's aggregator address | ||
function aggregator() external view returns (address) { | ||
return ChainlinkReadableAggregator(address(priceFeed)).aggregator(); | ||
} | ||
|
||
/// @dev Returns a phase aggregator by index | ||
function phaseAggregators(uint16 idx) external view returns (AggregatorV2V3Interface) { | ||
return ChainlinkReadableAggregator(address(priceFeed)).phaseAggregators(idx); | ||
} | ||
|
||
function phaseId() external view returns (uint16) { | ||
return ChainlinkReadableAggregator(address(priceFeed)).phaseId(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
// SPDX-License-Identifier: BUSL-1.1 | ||
// Gearbox Protocol. Generalized leverage for DeFi protocols | ||
// (c) Gearbox Holdings, 2022 | ||
pragma solidity ^0.8.10; | ||
|
||
import {AggregatorV3Interface} from "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol"; | ||
import {PriceFeedChecker} from "./PriceFeedChecker.sol"; | ||
import {PERCENTAGE_FACTOR} from "@gearbox-protocol/core-v2/contracts/libraries/PercentageMath.sol"; | ||
import {PriceFeedType, IPriceFeedType} from "../interfaces/IPriceFeedType.sol"; | ||
|
||
// EXCEPTIONS | ||
import {NotImplementedException} from "../interfaces/IErrors.sol"; | ||
|
||
/// @title Price feed that composes an base asset-denominated price feed with a USD one | ||
/// @notice Used for better price tracking for correlated assets (such as stETH or WBTC) or on networks where | ||
/// only feeds for the native tokens exist | ||
contract CompositePriceFeed is PriceFeedChecker, AggregatorV3Interface, IPriceFeedType { | ||
/// @dev Chainlink base asset price feed for the target asset | ||
AggregatorV3Interface public immutable targetToBasePriceFeed; | ||
|
||
/// @dev Chainlink Base asset / USD price feed | ||
AggregatorV3Interface public immutable baseToUsdPriceFeed; | ||
|
||
/// @dev Decimals of the returned result. | ||
uint8 public immutable override decimals; | ||
|
||
/// @dev 10 ^ Decimals of Target / Base price feed, to divide the product of answers | ||
int256 public immutable answerDenominator; | ||
|
||
/// @dev Price feed description | ||
string public override description; | ||
|
||
uint256 public constant override version = 1; | ||
|
||
PriceFeedType public constant override priceFeedType = PriceFeedType.COMPOSITE_ORACLE; | ||
|
||
bool public constant override skipPriceCheck = true; | ||
|
||
/// @dev Constructor | ||
/// @param _targetToBasePriceFeed Base asset price feed for target asset | ||
/// @param _baseToUsdPriceFeed USD price feed for base asset | ||
constructor(address _targetToBasePriceFeed, address _baseToUsdPriceFeed) { | ||
targetToBasePriceFeed = AggregatorV3Interface(_targetToBasePriceFeed); | ||
baseToUsdPriceFeed = AggregatorV3Interface(_baseToUsdPriceFeed); | ||
description = string(abi.encodePacked(targetToBasePriceFeed.description(), " to USD Composite")); | ||
decimals = baseToUsdPriceFeed.decimals(); | ||
answerDenominator = int256(10 ** targetToBasePriceFeed.decimals()); | ||
} | ||
|
||
/// @dev Implemented for compatibility, but reverts since Gearbox's price feeds | ||
/// do not store historical data. | ||
function getRoundData(uint80) | ||
external | ||
pure | ||
virtual | ||
override | ||
returns ( | ||
uint80, // roundId, | ||
int256, // answer, | ||
uint256, // startedAt, | ||
uint256, // updatedAt, | ||
uint80 // answeredInRound | ||
) | ||
{ | ||
revert NotImplementedException(); | ||
} | ||
|
||
/// @dev Returns the composite USD-denominated price of the asset, computed as (Target / base rate * base / USD rate) | ||
function latestRoundData() | ||
external | ||
view | ||
override | ||
returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound) | ||
{ | ||
(uint80 roundId0, int256 answer0, uint256 startedAt0, uint256 updatedAt0, uint80 answeredInRound0) = | ||
targetToBasePriceFeed.latestRoundData(); | ||
|
||
_checkAnswer(roundId0, answer0, updatedAt0, answeredInRound0); | ||
|
||
(roundId, answer, startedAt, updatedAt, answeredInRound) = baseToUsdPriceFeed.latestRoundData(); | ||
|
||
_checkAnswer(roundId, answer, updatedAt, answeredInRound); | ||
|
||
answer = (answer0 * answer) / answerDenominator; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
// SPDX-License-Identifier: BUSL-1.1 | ||
// Gearbox Protocol. Generalized leverage for DeFi protocols | ||
// (c) Gearbox Holdings, 2022 | ||
pragma solidity ^0.8.10; | ||
|
||
import {ILPPriceFeed} from "../interfaces/ILPPriceFeed.sol"; | ||
import {PriceFeedChecker} from "./PriceFeedChecker.sol"; | ||
import {ACLNonReentrantTrait} from "../core/ACLNonReentrantTrait.sol"; | ||
import {PERCENTAGE_FACTOR} from "@gearbox-protocol/core-v2/contracts/libraries/PercentageMath.sol"; | ||
|
||
// EXCEPTIONS | ||
import {NotImplementedException} from "../interfaces/IErrors.sol"; | ||
|
||
/// @title Abstract PriceFeed for an LP token | ||
/// @notice For most pools/vaults, the LP token price depends on Chainlink prices of pool assets and the pool's | ||
/// internal exchange rate. | ||
abstract contract LPPriceFeed is ILPPriceFeed, PriceFeedChecker, ACLNonReentrantTrait { | ||
/// @dev The lower bound for the contract's token-to-underlying exchange rate. | ||
/// @notice Used to protect against LP token / share price manipulation. | ||
uint256 public lowerBound; | ||
|
||
/// @dev Window size in PERCENTAGE format. Upper bound = lowerBound * (1 + delta) | ||
uint256 public immutable delta; | ||
|
||
/// @dev Decimals of the returned result. | ||
uint8 public constant override decimals = 8; | ||
|
||
/// @dev Price feed description | ||
string public override description; | ||
|
||
/// @dev Constructor | ||
/// @param addressProvider Address of address provier which is use for getting ACL | ||
/// @param _delta Pre-defined window in PERCENTAGE FORMAT which is allowed for SC value | ||
/// @param _description Price feed description | ||
constructor(address addressProvider, uint256 _delta, string memory _description) | ||
ACLNonReentrantTrait(addressProvider) | ||
{ | ||
description = _description; // F:[LPF-1] | ||
delta = _delta; // F:[LPF-1] | ||
} | ||
|
||
/// @dev Implemented for compatibility, but reverts since Gearbox's price feeds | ||
/// do not store historical data. | ||
function getRoundData(uint80) | ||
external | ||
pure | ||
virtual | ||
override | ||
returns ( | ||
uint80, // roundId, | ||
int256, // answer, | ||
uint256, // startedAt, | ||
uint256, // updatedAt, | ||
uint80 // answeredInRound | ||
) | ||
{ | ||
revert NotImplementedException(); // F:[LPF-2] | ||
} | ||
|
||
/// @dev Checks that value is in range [lowerBound; upperBound], | ||
/// Reverts if below lowerBound and returns min(value, upperBound) | ||
/// @param value Value to be checked and bounded | ||
function _checkAndUpperBoundValue(uint256 value) internal view returns (uint256) { | ||
uint256 lb = lowerBound; | ||
if (value < lb) revert ValueOutOfRangeException(); // F:[LPF-3] | ||
|
||
uint256 uBound = _upperBound(lb); | ||
|
||
return (value > uBound) ? uBound : value; | ||
} | ||
|
||
/// @dev Updates the bounds for the exchange rate value | ||
/// @param _lowerBound The new lower bound (the upper bound is computed dynamically) | ||
/// from the lower bound | ||
function setLimiter(uint256 _lowerBound) | ||
external | ||
override | ||
configuratorOnly // F:[LPF-4] | ||
{ | ||
_setLimiter(_lowerBound); // F:[LPF-4,5] | ||
} | ||
|
||
/// @dev IMPLEMENTATION: setLimiter | ||
function _setLimiter(uint256 _lowerBound) internal { | ||
if (_lowerBound == 0 || !_checkCurrentValueInBounds(_lowerBound, _upperBound(_lowerBound))) { | ||
revert IncorrectLimitsException(); | ||
} // F:[LPF-4] | ||
|
||
lowerBound = _lowerBound; // F:[LPF-5] | ||
emit NewLimiterParams(lowerBound, _upperBound(_lowerBound)); // F:[LPF-5] | ||
} | ||
|
||
function _upperBound(uint256 lb) internal view returns (uint256) { | ||
return (lb * (PERCENTAGE_FACTOR + delta)) / PERCENTAGE_FACTOR; // F:[LPF-5] | ||
} | ||
|
||
/// @dev Returns the upper bound, calculated based on the lower bound | ||
function upperBound() external view returns (uint256) { | ||
return _upperBound(lowerBound); // F:[LPF-5] | ||
} | ||
|
||
function _checkCurrentValueInBounds(uint256 _lowerBound, uint256 _upperBound) | ||
internal | ||
view | ||
virtual | ||
returns (bool); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
// SPDX-License-Identifier: BUSL-1.1 | ||
// Gearbox Protocol. Generalized leverage for DeFi protocols | ||
// (c) Gearbox Holdings, 2022 | ||
pragma solidity ^0.8.10; | ||
|
||
import {IPriceOracleV2Exceptions} from "@gearbox-protocol/core-v2/contracts/interfaces/IPriceOracle.sol"; | ||
|
||
/// @title Sanity checker for Chainlink price feed results | ||
contract PriceFeedChecker is IPriceOracleV2Exceptions { | ||
function _checkAnswer(uint80 roundID, int256 price, uint256 updatedAt, uint80 answeredInRound) internal pure { | ||
if (price <= 0) revert ZeroPriceException(); // F:[PO-5] | ||
if (answeredInRound < roundID || updatedAt == 0) { | ||
revert ChainPriceStaleException(); | ||
} // F:[PO-5] | ||
} | ||
} |
Oops, something went wrong.