|
1 | 1 | // SPDX-License-Identifier: BUSL-1.1 |
2 | 2 | // Gearbox Protocol. Generalized leverage for DeFi protocols |
3 | 3 | // (c) Gearbox Foundation, 2023. |
4 | | -pragma solidity ^0.8.10; |
| 4 | +pragma solidity ^0.8.17; |
5 | 5 |
|
| 6 | +import {AbstractPriceFeed} from "./AbstractPriceFeed.sol"; |
| 7 | +import {PriceFeedType} from "../interfaces/IPriceFeed.sol"; |
6 | 8 | import {AggregatorV3Interface} from "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol"; |
7 | 9 | import {AggregatorV2V3Interface} from "@chainlink/contracts/src/v0.8/interfaces/AggregatorV2V3Interface.sol"; |
8 | | -import {PERCENTAGE_FACTOR} from "@gearbox-protocol/core-v2/contracts/libraries/Constants.sol"; |
9 | | -import {PriceFeedType, IPriceFeedType} from "../interfaces/IPriceFeedType.sol"; |
10 | | - |
11 | | -// EXCEPTIONS |
12 | | -import {NotImplementedException} from "@gearbox-protocol/core-v3/contracts/interfaces/IExceptions.sol"; |
13 | 10 |
|
14 | 11 | interface ChainlinkReadableAggregator { |
15 | 12 | function aggregator() external view returns (address); |
16 | | - |
17 | 13 | function phaseAggregators(uint16 idx) external view returns (AggregatorV2V3Interface); |
18 | | - |
19 | 14 | function phaseId() external view returns (uint16); |
20 | 15 | } |
21 | 16 |
|
22 | | -/// @title Price feed with an upper bound on price |
23 | | -/// @notice Used to limit prices on assets that should not rise above |
24 | | -/// a certain level, such as stablecoins and other pegged assets |
25 | | -contract BoundedPriceFeed is ChainlinkReadableAggregator, AggregatorV3Interface, IPriceFeedType { |
26 | | - /// @dev Chainlink price feed for the Vault's underlying |
27 | | - AggregatorV3Interface public immutable priceFeed; |
28 | | - |
29 | | - /// @dev The upper bound on Chainlink price for the asset |
30 | | - int256 public immutable upperBound; |
31 | | - |
32 | | - /// @dev Decimals of the returned result. |
33 | | - uint8 public immutable override decimals; |
34 | | - |
35 | | - /// @dev Price feed description |
36 | | - string public override description; |
37 | | - |
38 | | - uint256 public constant override version = 1; |
39 | | - |
| 17 | +/// @title Bounded price feed |
| 18 | +/// @notice Can be used to provide upper-bounded answers for assets that are |
| 19 | +/// expected to have the price in a certain range, e.g. stablecoins |
| 20 | +contract BoundedPriceFeed is AbstractPriceFeed, ChainlinkReadableAggregator { |
| 21 | + /// @notice Contract version |
| 22 | + uint256 public constant override version = 3_00; |
40 | 23 | PriceFeedType public constant override priceFeedType = PriceFeedType.BOUNDED_ORACLE; |
41 | 24 |
|
42 | | - bool public constant override skipPriceCheck = false; |
| 25 | + /// @notice Underlying price feed |
| 26 | + address public immutable priceFeed; |
| 27 | + uint32 public immutable stalenessPeriod; |
| 28 | + bool public immutable skipCheck; |
43 | 29 |
|
44 | | - /// @dev Constructor |
45 | | - /// @param _priceFeed Chainlink price feed to receive results from |
46 | | - /// @param _upperBound Initial upper bound for the Chainlink price |
47 | | - constructor(address _priceFeed, int256 _upperBound) { |
48 | | - priceFeed = AggregatorV3Interface(_priceFeed); |
49 | | - description = string(abi.encodePacked(priceFeed.description(), " Bounded")); |
50 | | - decimals = priceFeed.decimals(); |
51 | | - upperBound = _upperBound; |
52 | | - } |
| 30 | + /// @notice Upper bound for underlying price feed answers |
| 31 | + int256 public immutable upperBound; |
53 | 32 |
|
54 | | - /// @dev Implemented for compatibility, but reverts since Gearbox's price feeds |
55 | | - /// do not store historical data. |
56 | | - function getRoundData(uint80) |
57 | | - external |
58 | | - pure |
59 | | - virtual |
60 | | - override |
61 | | - returns ( |
62 | | - uint80, // roundId, |
63 | | - int256, // answer, |
64 | | - uint256, // startedAt, |
65 | | - uint256, // updatedAt, |
66 | | - uint80 // answeredInRound |
67 | | - ) |
68 | | - { |
69 | | - revert NotImplementedException(); // F:[LPF-2] |
| 33 | + /// @notice Constructor |
| 34 | + /// @param _priceFeed Underlying price feed |
| 35 | + /// @param _stalenessPeriod Underlying price feed staleness period, must be non-zero unless it performs own checks |
| 36 | + /// @param _upperBound Upper bound for underlying price feed answers |
| 37 | + constructor(address _priceFeed, uint32 _stalenessPeriod, int256 _upperBound) { |
| 38 | + priceFeed = _priceFeed; |
| 39 | + stalenessPeriod = _stalenessPeriod; |
| 40 | + skipCheck = _validatePriceFeed(priceFeed, stalenessPeriod); |
| 41 | + upperBound = _upperBound; |
70 | 42 | } |
71 | 43 |
|
72 | | - /// @dev Returns the value if it is below the upper bound, otherwise returns the upper bound |
73 | | - /// @param value Value to be checked and bounded |
74 | | - function _upperBoundValue(int256 value) internal view returns (int256) { |
75 | | - return (value > upperBound) ? upperBound : value; |
| 44 | + /// @notice Price feed description |
| 45 | + function description() external view override returns (string memory) { |
| 46 | + return string(abi.encodePacked("Bounded ", AggregatorV3Interface(priceFeed).description(), " price feed")); |
76 | 47 | } |
77 | 48 |
|
78 | | - /// @dev Returns the upper-bounded USD price of the token |
| 49 | + /// @notice Returns the upper-bounded USD price of the token |
79 | 50 | function latestRoundData() |
80 | 51 | external |
81 | 52 | view |
82 | 53 | override |
83 | | - returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound) |
| 54 | + returns (uint80, int256 answer, uint256, uint256 updatedAt, uint80) |
84 | 55 | { |
85 | | - (roundId, answer, startedAt, updatedAt, answeredInRound) = priceFeed.latestRoundData(); // F:[OYPF-4] |
| 56 | + (answer, updatedAt) = _getValidatedPrice(priceFeed, stalenessPeriod, skipCheck); |
| 57 | + return (0, _upperBoundValue(answer), 0, updatedAt, 0); |
| 58 | + } |
86 | 59 |
|
87 | | - answer = _upperBoundValue(answer); |
| 60 | + /// @dev Upper-bounds given value |
| 61 | + function _upperBoundValue(int256 value) internal view returns (int256) { |
| 62 | + return (value > upperBound) ? upperBound : value; |
88 | 63 | } |
89 | 64 |
|
90 | | - /// @dev Returns the current phase's aggregator address |
91 | 65 | function aggregator() external view returns (address) { |
92 | 66 | return ChainlinkReadableAggregator(address(priceFeed)).aggregator(); |
93 | 67 | } |
94 | 68 |
|
95 | | - /// @dev Returns a phase aggregator by index |
96 | 69 | function phaseAggregators(uint16 idx) external view returns (AggregatorV2V3Interface) { |
97 | 70 | return ChainlinkReadableAggregator(address(priceFeed)).phaseAggregators(idx); |
98 | 71 | } |
|
0 commit comments