22pragma solidity ^ 0.8.27 ;
33
44import "@api3/contracts/interfaces/IApi3ReaderProxy.sol " ;
5- import "./interfaces/IPriceCapStableApi3ReaderProxyV1 .sol " ;
5+ import "./interfaces/IPriceCappedApi3ReaderProxyV1 .sol " ;
66
77/**
8- * @title An immutable proxy contract that provides price capping mechanism.
9- * It reads the price from the underlying API3 proxy and if this price exceeds a
10- * predefined `priceCap`, this contract will report the `priceCap` instead.
8+ * @title An immutable proxy contract that provides a price bounding mechanism.
9+ * It reads the price from the underlying Api3 proxy and if this price falls
10+ * outside a predefined `lowerBound` and `upperBound`, this contract will report
11+ * the respective bound instead.
1112 * This is primarily intended for assets (e.g., stablecoins) where a protocol
12- * wants to limit the maximum price it ingests for risk management purposes.
13- * @dev The `priceCap` is immutable and set during deployment.
13+ * wants to limit the price range it ingests for risk management purposes.
14+ * @dev `lowerBound` and `upperBound` are immutable and set during deployment.
15+ * To set only an upper bound, `lowerBound_` can be set to 0.
16+ * To set only a lower bound, `upperBound_` can be set to `type(int224).max`.
17+ * If `lowerBound_` is 0 and `upperBound_` is `type(int224).max`, no effective
18+ * capping occurs, though negative prices from the underlying proxy would be
19+ * floored at 0.
1420 */
15- contract PriceCapStableApi3ReaderProxyV1 is IPriceCapStableApi3ReaderProxyV1 {
21+ contract PriceCappedApi3ReaderProxyV1 is IPriceCappedApi3ReaderProxyV1 {
1622 /// @notice IApi3ReaderProxy contract address
1723 address public immutable override proxy;
1824
25+ /// @notice The minimum price (inclusive) that this proxy will report.
26+ int224 public immutable override lowerBound;
27+
1928 /// @notice The maximum price (inclusive) that this proxy will report.
20- int224 public immutable override priceCap ;
29+ int224 public immutable override upperBound ;
2130
2231 /// @param proxy_ IApi3ReaderProxy contract address
23- /// @param priceCap_ The maximum price value this proxy will report. Must be
24- /// a positive value.
25- constructor (address proxy_ , int224 priceCap_ ) {
32+ /// @param lowerBound_ The minimum price (inclusive) this proxy will report
33+ /// @param upperBound_ The maximum price (inclusive) this proxy will report
34+ constructor (address proxy_ , int224 lowerBound_ , int224 upperBound_ ) {
2635 if (proxy_ == address (0 )) {
2736 revert ZeroProxyAddress ();
2837 }
29- if (priceCap_ <= 0 ) {
30- revert PriceCapMustBePositive ();
38+ if (lowerBound_ < 0 ) {
39+ revert LowerBoundMustBeNonNegative ();
40+ }
41+ if (upperBound_ <= lowerBound_) {
42+ revert UpperBoundMustBeGreaterThanLowerBound ();
3143 }
3244 proxy = proxy_;
33- priceCap = priceCap_;
45+ lowerBound = lowerBound_;
46+ upperBound = upperBound_;
3447 }
3548
3649 /// @notice Reads the latest value and timestamp from the underlying
37- /// `IApi3ReaderProxy` and applies the price cap.
38- /// @dev If the `baseValue` from the underlying proxy is greater than
39- /// `priceCap`, then `priceCap` is returned as the `value`. Otherwise, the
40- /// `baseValue` is returned. The timestamp is passed through unmodified.
41- /// @return value Value of the underlying proxy, potentially capped
50+ /// `IApi3ReaderProxy` and applies the price bounds.
51+ /// @dev If the `baseValue` from the underlying proxy is less than
52+ /// `lowerBound`, then `lowerBound` is returned as the `value`. If
53+ /// `baseValue` is greater than `upperBound`, then `upperBound` is returned.
54+ /// Otherwise, the `baseValue` is returned. The timestamp is passed through
55+ /// unmodified.
56+ /// @return value Value of the underlying proxy, potentially bounded
4257 /// @return timestamp Timestamp from the underlying proxy
4358 function read ()
4459 public
@@ -49,8 +64,24 @@ contract PriceCapStableApi3ReaderProxyV1 is IPriceCapStableApi3ReaderProxyV1 {
4964 (int224 baseValue , uint32 baseTimestamp ) = IApi3ReaderProxy (proxy)
5065 .read ();
5166
52- value = baseValue > priceCap ? priceCap : baseValue;
5367 timestamp = baseTimestamp;
68+
69+ if (baseValue < lowerBound) {
70+ value = lowerBound;
71+ } else if (baseValue > upperBound) {
72+ value = upperBound;
73+ } else {
74+ value = baseValue;
75+ }
76+ }
77+
78+ /// @notice Checks if the current price from the underlying proxy would be
79+ /// capped or floored by the bounds.
80+ /// @return True if the base value is less than `lowerBound` or greater
81+ /// than `upperBound`, false otherwise.
82+ function isCapped () external view returns (bool ) {
83+ (int224 baseValue , ) = IApi3ReaderProxy (proxy).read ();
84+ return baseValue < lowerBound || baseValue > upperBound;
5485 }
5586
5687 /// @dev AggregatorV2V3Interface users are already responsible with
@@ -102,7 +133,7 @@ contract PriceCapStableApi3ReaderProxyV1 is IPriceCapStableApi3ReaderProxyV1 {
102133 }
103134
104135 /// @dev A unique version is chosen to easily check if an unverified
105- /// contract that acts as a Chainlink feed is a PriceCapStableApi3ReaderProxyV1
136+ /// contract that acts as a Chainlink feed is a PriceCappedApi3ReaderProxyV1
106137 function version () external pure override returns (uint256 ) {
107138 return 4918 ;
108139 }
0 commit comments