Skip to content

Commit 579ac4b

Browse files
committed
Renames the contract and adds lower/upper bound for capping
1 parent 3127a39 commit 579ac4b

File tree

2 files changed

+61
-24
lines changed

2 files changed

+61
-24
lines changed

contracts/PriceCapStableApi3ReaderProxyV1.sol renamed to contracts/PriceCappedApi3ReaderProxyV1.sol

Lines changed: 52 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2,43 +2,58 @@
22
pragma solidity ^0.8.27;
33

44
import "@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
}

contracts/interfaces/IPriceCapStableApi3ReaderProxyV1.sol renamed to contracts/interfaces/IPriceCappedApi3ReaderProxyV1.sol

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,23 @@ pragma solidity ^0.8.4;
44
import "@api3/contracts/interfaces/IApi3ReaderProxy.sol";
55
import "../vendor/@chainlink/[email protected]/src/v0.8/shared/interfaces/AggregatorV2V3Interface.sol";
66

7-
interface IPriceCapStableApi3ReaderProxyV1 is
7+
interface IPriceCappedApi3ReaderProxyV1 is
88
IApi3ReaderProxy,
99
AggregatorV2V3Interface
1010
{
1111
error ZeroProxyAddress();
1212

13-
error PriceCapMustBePositive();
13+
error LowerBoundMustBeNonNegative();
14+
15+
error UpperBoundMustBeGreaterThanLowerBound();
1416

1517
error FunctionIsNotSupported();
1618

1719
function proxy() external view returns (address proxy);
1820

19-
function priceCap() external view returns (int224 priceCap);
21+
function lowerBound() external view returns (int224 lowerBound);
22+
23+
function upperBound() external view returns (int224 upperBound);
24+
25+
function isCapped() external view returns (bool);
2026
}

0 commit comments

Comments
 (0)