Skip to content

Commit 3c4337d

Browse files
authored
Merge pull request #15 from Gearbox-protocol/update-reserve-feed-in-update-bounds
feat: allow updating reserve feed in `updateBounds`
2 parents d534716 + 727d8e8 commit 3c4337d

18 files changed

+214
-294
lines changed

contracts/interfaces/ILPPriceFeed.sol

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
// (c) Gearbox Foundation, 2023.
44
pragma solidity ^0.8.17;
55

6-
import {IPriceFeed} from "./IPriceFeed.sol";
6+
import {IPriceFeed} from "@gearbox-protocol/core-v2/contracts/interfaces/IPriceFeed.sol";
77

88
interface ILPPriceFeedEvents {
99
/// @notice Emitted when new LP token exchange rate bounds are set
@@ -15,14 +15,13 @@ interface ILPPriceFeedEvents {
1515

1616
/// @title LP price feed interface
1717
interface ILPPriceFeed is IPriceFeed, ILPPriceFeedEvents {
18-
function addressProvider() external view returns (address);
18+
function priceOracle() external view returns (address);
1919

2020
function lpToken() external view returns (address);
2121
function lpContract() external view returns (address);
2222

2323
function lowerBound() external view returns (uint256);
2424
function upperBound() external view returns (uint256);
25-
function delta() external view returns (uint256);
2625
function updateBoundsAllowed() external view returns (bool);
2726

2827
function getAggregatePrice() external view returns (int256 answer, uint256 updatedAt);
@@ -35,5 +34,5 @@ interface ILPPriceFeed is IPriceFeed, ILPPriceFeedEvents {
3534

3635
function setUpdateBoundsAllowed(bool allowed) external;
3736
function setLimiter(uint256 newLowerBound) external;
38-
function updateBounds() external;
37+
function updateBounds(bytes calldata updateData) external;
3938
}

contracts/interfaces/IPriceFeed.sol

Lines changed: 0 additions & 13 deletions
This file was deleted.

contracts/oracles/AbstractPriceFeed.sol

Lines changed: 0 additions & 40 deletions
This file was deleted.

contracts/oracles/LPPriceFeed.sol

Lines changed: 45 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,12 @@
44
pragma solidity ^0.8.17;
55

66
import {ILPPriceFeed} from "../interfaces/ILPPriceFeed.sol";
7-
import {AbstractPriceFeed} from "./AbstractPriceFeed.sol";
87
import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
98
import {PERCENTAGE_FACTOR} from "@gearbox-protocol/core-v2/contracts/libraries/Constants.sol";
109
import {ACLNonReentrantTrait} from "@gearbox-protocol/core-v3/contracts/traits/ACLNonReentrantTrait.sol";
10+
import {PriceFeedValidationTrait} from "@gearbox-protocol/core-v3/contracts/traits/PriceFeedValidationTrait.sol";
1111
import {IPriceOracleV3} from "@gearbox-protocol/core-v3/contracts/interfaces/IPriceOracleV3.sol";
12+
import {IUpdatablePriceFeed} from "@gearbox-protocol/core-v2/contracts/interfaces/IPriceFeed.sol";
1213
import {
1314
IAddressProviderV3, AP_PRICE_ORACLE
1415
} from "@gearbox-protocol/core-v3/contracts/interfaces/IAddressProviderV3.sol";
@@ -19,14 +20,26 @@ import {
1920
IncorrectLimitsException
2021
} from "@gearbox-protocol/core-v3/contracts/interfaces/IExceptions.sol";
2122

23+
/// @dev Window size in bps, used to compute upper bound given lower bound
24+
uint256 constant WINDOW_SIZE = 200;
25+
26+
/// @dev Buffer size in bps, used to compute new lower bound given current exchange rate
27+
uint256 constant BUFFER_SIZE = 20;
28+
2229
/// @title LP price feed
2330
/// @notice Abstract contract for LP token price feeds.
2431
/// It is assumed that the price of an LP token is the product of its exchange rate and some aggregate function
2532
/// of underlying tokens prices. This contract simplifies creation of such price feeds and provides standard
2633
/// validation of the LP token exchange rate that protects against price manipulation.
27-
abstract contract LPPriceFeed is ILPPriceFeed, AbstractPriceFeed, ACLNonReentrantTrait {
28-
/// @notice Address provider contract
29-
address public immutable override addressProvider;
34+
abstract contract LPPriceFeed is ILPPriceFeed, ACLNonReentrantTrait, PriceFeedValidationTrait {
35+
/// @notice Answer precision (always 8 decimals for USD price feeds)
36+
uint8 public constant override decimals = 8;
37+
38+
/// @notice Indicates that price oracle can skip checks for this price feed's answers
39+
bool public constant override skipPriceCheck = true;
40+
41+
/// @notice Price oracle contract
42+
address public immutable override priceOracle;
3043

3144
/// @notice LP token for which the prices are computed
3245
address public immutable override lpToken;
@@ -37,9 +50,6 @@ abstract contract LPPriceFeed is ILPPriceFeed, AbstractPriceFeed, ACLNonReentran
3750
/// @notice Lower bound for the LP token exchange rate
3851
uint256 public override lowerBound;
3952

40-
/// @notice Window size in bps, used to compute upper bound given lower bound
41-
uint256 public constant override delta = 2_00;
42-
4353
/// @notice Whether permissionless bounds update is allowed
4454
bool public override updateBoundsAllowed;
4555

@@ -52,7 +62,7 @@ abstract contract LPPriceFeed is ILPPriceFeed, AbstractPriceFeed, ACLNonReentran
5262
nonZeroAddress(_lpToken)
5363
nonZeroAddress(_lpContract)
5464
{
55-
addressProvider = _addressProvider;
65+
priceOracle = IAddressProviderV3(_addressProvider).getAddressOrRevert(AP_PRICE_ORACLE, 3_00);
5666
lpToken = _lpToken;
5767
lpContract = _lpContract;
5868
}
@@ -73,7 +83,7 @@ abstract contract LPPriceFeed is ILPPriceFeed, AbstractPriceFeed, ACLNonReentran
7383
uint256 lb = lowerBound;
7484
if (exchangeRate < lb) revert ValueOutOfRangeException();
7585

76-
uint256 ub = _upperBound(lb);
86+
uint256 ub = _calcUpperBound(lb);
7787
if (exchangeRate > ub) exchangeRate = ub;
7888

7989
(answer, updatedAt) = getAggregatePrice();
@@ -83,7 +93,7 @@ abstract contract LPPriceFeed is ILPPriceFeed, AbstractPriceFeed, ACLNonReentran
8393

8494
/// @notice Upper bound for the LP token exchange rate
8595
function upperBound() external view returns (uint256) {
86-
return _upperBound(lowerBound);
96+
return _calcUpperBound(lowerBound);
8797
}
8898

8999
/// @notice Returns aggregate price of underlying tokens
@@ -98,11 +108,6 @@ abstract contract LPPriceFeed is ILPPriceFeed, AbstractPriceFeed, ACLNonReentran
98108
/// @dev Must be implemented by derived price feeds
99109
function getScale() public view virtual override returns (uint256 scale);
100110

101-
/// @dev Computes upper bound as `lowerBound * (1 + delta)`
102-
function _upperBound(uint256 lb) internal pure returns (uint256) {
103-
return (lb * (PERCENTAGE_FACTOR + delta)) / PERCENTAGE_FACTOR;
104-
}
105-
106111
// ------------- //
107112
// CONFIGURATION //
108113
// ------------- //
@@ -116,39 +121,51 @@ abstract contract LPPriceFeed is ILPPriceFeed, AbstractPriceFeed, ACLNonReentran
116121

117122
/// @notice Sets new lower and upper bounds for the LP token exchange rate
118123
/// @param newLowerBound New lower bound value
119-
/// @dev New upper bound value is computed as `newLowerBound * (1 + delta)`
120124
function setLimiter(uint256 newLowerBound) external override controllerOnly {
121125
_setLimiter(newLowerBound, getLPExchangeRate());
122126
}
123127

124128
/// @notice Permissionlessly updates LP token's exchange rate bounds using answer from the reserve price feed.
125-
/// The lower bound is set to the induced reserve exchange rate (with small downside buffer).
126-
function updateBounds() external override {
129+
/// Lower bound is set to the induced reserve exchange rate (with small buffer for downside movement).
130+
/// @param updateData Data to update the reserve price feed with before querying its answer if it is updatable
131+
function updateBounds(bytes calldata updateData) external override {
127132
if (!updateBoundsAllowed) return;
128133

129-
address priceOracle = IAddressProviderV3(addressProvider).getAddressOrRevert(AP_PRICE_ORACLE, 3_00);
130-
address reserveFeed = IPriceOracleV3(priceOracle).priceFeedsRaw(lpToken, true);
134+
address reserveFeed = IPriceOracleV3(priceOracle).priceFeedsRaw({token: lpToken, reserve: true});
135+
try IUpdatablePriceFeed(reserveFeed).updatable() returns (bool updatable) {
136+
if (updatable) IUpdatablePriceFeed(reserveFeed).updatePrice(updateData);
137+
} catch {}
131138

132-
(, int256 reserveAnswer,,,) = ILPPriceFeed(reserveFeed).latestRoundData();
139+
uint256 reserveAnswer = IPriceOracleV3(priceOracle).getPriceRaw({token: lpToken, reserve: true});
133140
(int256 price,) = getAggregatePrice();
134-
uint256 reserveExchangeRate = uint256(reserveAnswer * int256(getScale()) / price);
141+
uint256 reserveExchangeRate = uint256(reserveAnswer * getScale() / uint256(price));
135142

136-
_setLimiter(reserveExchangeRate * 998 / 1000, getLPExchangeRate());
143+
_setLimiter(_calcLowerBound(reserveExchangeRate), getLPExchangeRate());
137144
}
138145

139-
/// @dev Sets lower bound to the current LP token exhcange rate (with small downside buffer)
140-
/// @dev Derived price feeds MUST call this in the constructor after initializing all the
141-
/// state variables needed for exchange rate calculation
146+
/// @dev Sets lower bound to the current LP token exhcange rate (with small buffer for downside movement)
147+
/// @dev Derived price feeds MUST call this in the constructor after initializing all the state variables
148+
/// needed for exchange rate calculation
142149
function _initLimiter() internal {
143150
uint256 exchangeRate = getLPExchangeRate();
144-
_setLimiter(exchangeRate * 998 / 1000, exchangeRate);
151+
_setLimiter(_calcLowerBound(exchangeRate), exchangeRate);
145152
}
146153

147154
/// @dev `setLimiter` implementation: sets new bounds, ensures that current value is within them, emits event
148155
function _setLimiter(uint256 lower, uint256 current) internal {
149-
uint256 upper = _upperBound(lower);
156+
uint256 upper = _calcUpperBound(lower);
150157
if (lower == 0 || current < lower || current > upper) revert IncorrectLimitsException();
151158
lowerBound = lower;
152159
emit SetBounds(lower, upper);
153160
}
161+
162+
/// @dev Computes upper bound as `_lowerBound * (1 + WINDOW_SIZE)`
163+
function _calcUpperBound(uint256 _lowerBound) internal pure returns (uint256) {
164+
return _lowerBound * (PERCENTAGE_FACTOR + WINDOW_SIZE) / PERCENTAGE_FACTOR;
165+
}
166+
167+
/// @dev Computes lower bound as `exchangeRate * (1 - BUFFER_SIZE)`
168+
function _calcLowerBound(uint256 exchangeRate) internal pure returns (uint256) {
169+
return exchangeRate * (PERCENTAGE_FACTOR - BUFFER_SIZE) / PERCENTAGE_FACTOR;
170+
}
154171
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// SPDX-License-Identifier: BUSL-1.1
2+
// Gearbox Protocol. Generalized leverage for DeFi protocols
3+
// (c) Gearbox Foundation, 2023.
4+
pragma solidity ^0.8.17;
5+
6+
struct PriceFeedParams {
7+
address priceFeed;
8+
uint32 stalenessPeriod;
9+
}

contracts/oracles/SingleAssetLPPriceFeed.sol

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
pragma solidity ^0.8.17;
55

66
import {LPPriceFeed} from "./LPPriceFeed.sol";
7-
import {PriceFeedParams} from "./AbstractPriceFeed.sol";
7+
import {PriceFeedParams} from "./PriceFeedParams.sol";
88
import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
99

1010
/// @title Single-asset LP price feed

contracts/oracles/balancer/BPTStablePriceFeed.sol

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
pragma solidity ^0.8.17;
55

66
import {LPPriceFeed} from "../LPPriceFeed.sol";
7-
import {PriceFeedParams} from "../AbstractPriceFeed.sol";
7+
import {PriceFeedParams} from "../PriceFeedParams.sol";
88
import {WAD} from "@gearbox-protocol/core-v2/contracts/libraries/Constants.sol";
99
import {PriceFeedType} from "@gearbox-protocol/sdk/contracts/PriceFeedType.sol";
1010
import {IBalancerStablePool} from "../../interfaces/balancer/IBalancerStablePool.sol";

contracts/oracles/balancer/BPTWeightedPriceFeed.sol

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import {WAD} from "@gearbox-protocol/core-v2/contracts/libraries/Constants.sol";
1010
import {PriceFeedType} from "@gearbox-protocol/sdk/contracts/PriceFeedType.sol";
1111

1212
import {LPPriceFeed} from "../LPPriceFeed.sol";
13-
import {PriceFeedParams} from "../AbstractPriceFeed.sol";
13+
import {PriceFeedParams} from "../PriceFeedParams.sol";
1414
import {FixedPoint} from "../../libraries/FixedPoint.sol";
1515

1616
import {IBalancerV2VaultGetters} from "../../interfaces/balancer/IBalancerV2Vault.sol";

contracts/oracles/curve/CurveCryptoLPPriceFeed.sol

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
pragma solidity ^0.8.17;
55

66
import {LPPriceFeed} from "../LPPriceFeed.sol";
7-
import {PriceFeedParams} from "../AbstractPriceFeed.sol";
7+
import {PriceFeedParams} from "../PriceFeedParams.sol";
88
import {FixedPoint} from "../../libraries/FixedPoint.sol";
99
import {ICurvePool} from "../../interfaces/curve/ICurvePool.sol";
1010
import {PriceFeedType} from "@gearbox-protocol/sdk/contracts/PriceFeedType.sol";

contracts/oracles/curve/CurveStableLPPriceFeed.sol

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
pragma solidity ^0.8.17;
55

66
import {LPPriceFeed} from "../LPPriceFeed.sol";
7-
import {PriceFeedParams} from "../AbstractPriceFeed.sol";
7+
import {PriceFeedParams} from "../PriceFeedParams.sol";
88
import {ICurvePool} from "../../interfaces/curve/ICurvePool.sol";
99
import {PriceFeedType} from "@gearbox-protocol/sdk/contracts/PriceFeedType.sol";
1010
import {WAD} from "@gearbox-protocol/core-v2/contracts/libraries/Constants.sol";

0 commit comments

Comments
 (0)