22pragma solidity ^ 0.8.27 ;
33
44import "@api3/contracts/interfaces/IApi3ReaderProxy.sol " ;
5- import "../ProxyUtils.sol " ;
65import "./interfaces/IScaledApi3FeedProxyV1.sol " ;
76
87/// @title An immutable Chainlink AggregatorV2V3Interface feed contract that
@@ -11,13 +10,20 @@ import "./interfaces/IScaledApi3FeedProxyV1.sol";
1110/// @dev This contract assumes the source proxy always returns values with
1211/// 18 decimals (as all IApi3ReaderProxy-compatible proxies do)
1312contract ScaledApi3FeedProxyV1 is IScaledApi3FeedProxyV1 {
14- using ProxyUtils for int256 ;
15-
1613 /// @notice IApi3ReaderProxy contract address
1714 address public immutable override proxy;
1815
16+ /// @dev Target decimals for the scaled value.
1917 uint8 private immutable targetDecimals;
2018
19+ /// @notice Pre-calculated factor for scaling the proxy's 18-decimal value
20+ /// to `targetDecimals`.
21+ int256 public immutable scalingFactor;
22+
23+ /// @notice True if upscaling (multiply by `scalingFactor`), false if
24+ /// downscaling (divide by `scalingFactor`), to scale to `targetDecimals`.
25+ bool public immutable isUpscaling;
26+
2127 /// @param proxy_ IApi3ReaderProxy contract address
2228 /// @param targetDecimals_ Decimals used to scale the IApi3ReaderProxy value
2329 constructor (address proxy_ , uint8 targetDecimals_ ) {
@@ -27,9 +33,16 @@ contract ScaledApi3FeedProxyV1 is IScaledApi3FeedProxyV1 {
2733 if (targetDecimals_ == 0 || targetDecimals_ > 36 ) {
2834 revert InvalidDecimals ();
2935 }
30-
36+ if (targetDecimals_ == 18 ) {
37+ revert NoScalingNeeded ();
38+ }
3139 proxy = proxy_;
3240 targetDecimals = targetDecimals_;
41+ uint8 delta = targetDecimals_ > 18
42+ ? targetDecimals_ - 18
43+ : 18 - targetDecimals_;
44+ scalingFactor = int256 (10 ** uint256 (delta));
45+ isUpscaling = targetDecimals_ > 18 ;
3346 }
3447
3548 /// @dev AggregatorV2V3Interface users are already responsible with
@@ -118,19 +131,26 @@ contract ScaledApi3FeedProxyV1 is IScaledApi3FeedProxyV1 {
118131 updatedAt = startedAt;
119132 }
120133
121- /// @notice Reads from the IApi3ReaderProxy value and scales it to target
122- /// decimals
123- /// @dev Casting the scaled value to int224 might cause an overflow but this
124- /// is preferable to checking the value for overflows in every read due to
125- /// gas overhead
126- function _read ()
127- internal
128- view
129- returns (int256 scaledValue , uint32 timestamp )
130- {
131- (int256 value , uint32 proxyTimestamp ) = IApi3ReaderProxy (proxy).read ();
132-
133- scaledValue = int224 (value.scaleValue (18 , targetDecimals));
134+ /// @notice Reads a value from the underlying `IApi3ReaderProxy` and
135+ /// scales it to `targetDecimals`.
136+ /// @dev Reads an `int224` value (assumed to be 18 decimals) from the
137+ /// underlying `IApi3ReaderProxy`. This value is then scaled to
138+ /// `targetDecimals` using pre-calculated factors. The scaling arithmetic
139+ /// (e.g., `proxyValue * scalingFactor`) involves an `int224` (`proxyValue`)
140+ /// and an `int256` (`scalingFactor`). `proxyValue` is implicitly promoted
141+ /// to `int256` for this operation, resulting in an `int256` value.
142+ /// This allows the scaled result to exceed the `int224` range, provided
143+ /// it fits within `int256`. Arithmetic operations will revert on `int256`
144+ /// overflow. The function returns the scaled value as an `int256`.
145+ /// @return value The scaled signed fixed-point value with `targetDecimals`.
146+ /// @return timestamp The timestamp from the underlying proxy.
147+ function _read () internal view returns (int256 value , uint32 timestamp ) {
148+ (int224 proxyValue , uint32 proxyTimestamp ) = IApi3ReaderProxy (proxy)
149+ .read ();
150+
151+ value = isUpscaling
152+ ? proxyValue * scalingFactor
153+ : proxyValue / scalingFactor;
134154 timestamp = proxyTimestamp;
135155 }
136156}
0 commit comments