Skip to content

Commit 3127a39

Browse files
committed
Adds PriceCapStableApi3ReaderProxyV1
1 parent cd4deb6 commit 3127a39

File tree

2 files changed

+161
-0
lines changed

2 files changed

+161
-0
lines changed
Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity ^0.8.27;
3+
4+
import "@api3/contracts/interfaces/IApi3ReaderProxy.sol";
5+
import "./interfaces/IPriceCapStableApi3ReaderProxyV1.sol";
6+
7+
/**
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.
11+
* 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.
14+
*/
15+
contract PriceCapStableApi3ReaderProxyV1 is IPriceCapStableApi3ReaderProxyV1 {
16+
/// @notice IApi3ReaderProxy contract address
17+
address public immutable override proxy;
18+
19+
/// @notice The maximum price (inclusive) that this proxy will report.
20+
int224 public immutable override priceCap;
21+
22+
/// @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_) {
26+
if (proxy_ == address(0)) {
27+
revert ZeroProxyAddress();
28+
}
29+
if (priceCap_ <= 0) {
30+
revert PriceCapMustBePositive();
31+
}
32+
proxy = proxy_;
33+
priceCap = priceCap_;
34+
}
35+
36+
/// @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
42+
/// @return timestamp Timestamp from the underlying proxy
43+
function read()
44+
public
45+
view
46+
override
47+
returns (int224 value, uint32 timestamp)
48+
{
49+
(int224 baseValue, uint32 baseTimestamp) = IApi3ReaderProxy(proxy)
50+
.read();
51+
52+
value = baseValue > priceCap ? priceCap : baseValue;
53+
timestamp = baseTimestamp;
54+
}
55+
56+
/// @dev AggregatorV2V3Interface users are already responsible with
57+
/// validating the values that they receive (e.g., revert if the spot price
58+
/// of an asset is negative). Therefore, this contract omits validation.
59+
function latestAnswer() external view override returns (int256 value) {
60+
(value, ) = read();
61+
}
62+
63+
/// @dev A Chainlink feed contract returns the block timestamp at which the
64+
/// feed was last updated. On the other hand, an Api3 feed timestamp
65+
/// denotes the point in time at which the first-party oracles signed the
66+
/// data used to do the last update. We find this to be a reasonable
67+
/// approximation, considering that usually the timestamp is only used to
68+
/// check if the last update is stale.
69+
function latestTimestamp()
70+
external
71+
view
72+
override
73+
returns (uint256 timestamp)
74+
{
75+
(, timestamp) = read();
76+
}
77+
78+
/// @dev Api3 feeds are updated asynchronously and not in rounds
79+
function latestRound() external pure override returns (uint256) {
80+
revert FunctionIsNotSupported();
81+
}
82+
83+
/// @dev Functions that use the round ID as an argument are not supported
84+
function getAnswer(uint256) external pure override returns (int256) {
85+
revert FunctionIsNotSupported();
86+
}
87+
88+
/// @dev Functions that use the round ID as an argument are not supported
89+
function getTimestamp(uint256) external pure override returns (uint256) {
90+
revert FunctionIsNotSupported();
91+
}
92+
93+
/// @dev Api3 feeds always use 18 decimals
94+
function decimals() external pure override returns (uint8) {
95+
return 18;
96+
}
97+
98+
/// @dev Underlying proxy dApp ID and dAPI name act as the description, and
99+
/// this is left empty to save gas on contract deployment
100+
function description() external pure override returns (string memory) {
101+
return "";
102+
}
103+
104+
/// @dev A unique version is chosen to easily check if an unverified
105+
/// contract that acts as a Chainlink feed is a PriceCapStableApi3ReaderProxyV1
106+
function version() external pure override returns (uint256) {
107+
return 4918;
108+
}
109+
110+
/// @dev Functions that use the round ID as an argument are not supported
111+
function getRoundData(
112+
uint80
113+
)
114+
external
115+
pure
116+
override
117+
returns (uint80, int256, uint256, uint256, uint80)
118+
{
119+
revert FunctionIsNotSupported();
120+
}
121+
122+
/// @dev Rounds IDs are returned as `0` as invalid values.
123+
/// Similar to `latestAnswer()`, we leave the validation of the returned
124+
/// value to the caller.
125+
function latestRoundData()
126+
external
127+
view
128+
override
129+
returns (
130+
uint80 roundId,
131+
int256 answer,
132+
uint256 startedAt,
133+
uint256 updatedAt,
134+
uint80 answeredInRound
135+
)
136+
{
137+
roundId = answeredInRound = 0;
138+
(answer, startedAt) = read();
139+
updatedAt = startedAt;
140+
}
141+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity ^0.8.4;
3+
4+
import "@api3/contracts/interfaces/IApi3ReaderProxy.sol";
5+
import "../vendor/@chainlink/contracts@1.2.0/src/v0.8/shared/interfaces/AggregatorV2V3Interface.sol";
6+
7+
interface IPriceCapStableApi3ReaderProxyV1 is
8+
IApi3ReaderProxy,
9+
AggregatorV2V3Interface
10+
{
11+
error ZeroProxyAddress();
12+
13+
error PriceCapMustBePositive();
14+
15+
error FunctionIsNotSupported();
16+
17+
function proxy() external view returns (address proxy);
18+
19+
function priceCap() external view returns (int224 priceCap);
20+
}

0 commit comments

Comments
 (0)