Skip to content

Commit 95345d4

Browse files
authored
Merge pull request #29 from api3dao/24-proxy-dappid
Adds `dappId` to combinator contracts
2 parents 6d9396f + b05aa33 commit 95345d4

27 files changed

+476
-441
lines changed

README.md

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -111,9 +111,10 @@ The `NETWORK` variable should be set to a chain name as defined by `@api3/contra
111111
- **`NormalizedApi3ReaderProxyV1`**:
112112
- `NETWORK`: Target network name.
113113
- `FEED`: Address of the external data feed (e.g., a Chainlink `AggregatorV2V3Interface` compatible feed).
114+
- `DAPP_ID`: The dApp ID to associate with this proxy.
114115
- Example:
115116
```bash
116-
NETWORK=polygon FEED=0xExternalFeedAddress pnpm deploy:NormalizedApi3ReaderProxyV1
117+
NETWORK=polygon FEED=0xExternalFeedAddress DAPP_ID=YourDappId pnpm deploy:NormalizedApi3ReaderProxyV1
117118
```
118119

119120
- **`ProductApi3ReaderProxyV1`**:
@@ -183,13 +184,13 @@ Imagine your dApp requires a USD/ETH price feed with 8 decimal places, but the a
183184
1. **Deploy `InverseApi3ReaderProxyV1`**:
184185
- Input `PROXY`: Address of the ETH/USD `IApi3ReaderProxy` dAPI.
185186
- Output: An `IApi3ReaderProxy` contract. This deployed instance of `InverseApi3ReaderProxyV1` reads USD/ETH.
186-
- Example command: `NETWORK=your_network PROXY=0xAddressOfEthUsdDapi pnpm deploy:InverseApi3ReaderProxyV1`
187+
- Example command: `NETWORK=base PROXY=0xAddressOfEthUsdDapi pnpm deploy:InverseApi3ReaderProxyV1`
187188
188189
2. **Deploy `ScaledApi3FeedProxyV1`**:
189190
- Input `PROXY`: Address of the `InverseApi3ReaderProxyV1` instance deployed in step 1.
190191
- Input `DECIMALS`: `8`.
191192
- Output: An `AggregatorV2V3Interface` contract. This deployed instance of `ScaledApi3FeedProxyV1` reads USD/ETH scaled to 8 decimals.
192-
- Example command: `NETWORK=your_network PROXY=0xAddressOfDeployedInverseApi3ReaderProxyV1FromStep1 DECIMALS=8 pnpm deploy:ScaledApi3FeedProxyV1`
193+
- Example command: `NETWORK=base PROXY=0xAddressOfDeployedInverseApi3ReaderProxyV1FromStep1 DECIMALS=8 pnpm deploy:ScaledApi3FeedProxyV1`
193194
_Note: Replace `0xAddressOfDeployedInverseApi3ReaderProxyV1FromStep1` with the actual address obtained from the deployment artifact of step 1._
194195
195196
This pipeline successfully provides the dApp with the required USD/ETH feed at the desired precision and interface.
@@ -229,16 +230,17 @@ To derive the desired uStETH/USD feed and make it compatible with the Api3 ecosy
229230
1. **Deploy `NormalizedApi3ReaderProxyV1`**:
230231
- This step adapts the external uStETH/ETH feed, which implements the `AggregatorV2V3Interface`, to the `IApi3ReaderProxy` interface. A key function of `NormalizedApi3ReaderProxyV1` is to read the `decimals()` from the external feed and automatically scale its value to the 18 decimal places expected by the `IApi3ReaderProxy` interface. For instance, if the uStETH/ETH feed returns its value with a different precision (e.g., 8 or 36 decimals), this proxy will normalize it.
231232
- Input `FEED`: Address of the external uStETH/ETH `AggregatorV2V3Interface` feed.
233+
- Input `DAPP_ID`: The dApp ID to associate with this proxy.
232234
- Output: An `IApi3ReaderProxy` contract. This deployed instance of `NormalizedApi3ReaderProxyV1` reads uStETH/ETH, with its value normalized to 18 decimals.
233-
- Example command: `NETWORK=your_network FEED=0xAddressOfExternal_uStETH_ETH_Feed pnpm deploy:NormalizedApi3ReaderProxyV1`
235+
- Example command: `NETWORK=base FEED=0xAddressOfExternal_uStETH_ETH_Feed DAPP_ID=YourDappId pnpm deploy:NormalizedApi3ReaderProxyV1`
234236
235237
2. **Deploy `ProductApi3ReaderProxyV1` to calculate uStETH/USD**:
236238
- This step multiplies the normalized uStETH/ETH rate by the ETH/USD rate from the Api3 dAPI.
237239
- Input `PROXY1`: Address of the `NormalizedApi3ReaderProxyV1` instance deployed in step 1.
238240
- Input `PROXY2`: Address of the existing ETH/USD `IApi3ReaderProxy` dAPI.
239241
- Output: An `IApi3ReaderProxy` contract. This deployed instance of `ProductApi3ReaderProxyV1` reads uStETH/USD.
240242
- Calculation: `(uStETH/ETH) * (ETH/USD) = uStETH/USD`.
241-
- Example command: `NETWORK=your_network PROXY1=0xAddressOfDeployedNormalizedApi3ReaderProxyV1FromStep1 PROXY2=0xAddressOfApi3EthUsdDapi pnpm deploy:ProductApi3ReaderProxyV1`
243+
- Example command: `NETWORK=base PROXY1=0xAddressOfDeployedNormalizedApi3ReaderProxyV1FromStep1 PROXY2=0xAddressOfApi3EthUsdDapi pnpm deploy:ProductApi3ReaderProxyV1`
242244
_(Note: Replace `0xAddressOfDeployedNormalizedApi3ReaderProxyV1FromStep1` with the actual address obtained from the deployment artifact of step 1)._
243245
244246
This scenario highlights how `NormalizedApi3ReaderProxyV1` serves as a crucial bridge, enabling dApps to integrate valuable data from external sources (that may not meet Api3 dAPI listing criteria or are simply outside the current offerings) and combine it with trusted Api3 dAPIs using the standard set of combinator tools.

contracts/InverseApi3ReaderProxyV1.sol

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,34 @@
11
// SPDX-License-Identifier: MIT
22
pragma solidity 0.8.27;
33

4-
import "@api3/contracts/interfaces/IApi3ReaderProxy.sol";
4+
import "./interfaces/IApi3ReaderProxyWithDappId.sol";
55
import "./interfaces/IInverseApi3ReaderProxyV1.sol";
66

77
/// @title An immutable proxy contract that inverts the value returned by an
8-
/// IApi3ReaderProxy data feed
8+
/// IApi3ReaderProxyWithDappId data feed
99
/// @dev This contract implements the AggregatorV2V3Interface to be compatible
1010
/// with Chainlink aggregators. This allows the contract to be used as a drop-in
1111
/// replacement for Chainlink aggregators in existing dApps.
1212
/// Refer to https://github.com/api3dao/migrate-from-chainlink-to-api3 for more
1313
/// information about the Chainlink interface implementation.
1414
contract InverseApi3ReaderProxyV1 is IInverseApi3ReaderProxyV1 {
15-
/// @notice IApi3ReaderProxy contract address
15+
/// @notice IApi3ReaderProxyWithDappId contract address
1616
address public immutable override proxy;
1717

18-
/// @param proxy_ IApi3ReaderProxy contract address
18+
/// @notice dApp ID of the proxy
19+
uint256 public immutable override dappId;
20+
21+
/// @param proxy_ IApi3ReaderProxyWithDappId contract address
1922
constructor(address proxy_) {
2023
if (proxy_ == address(0)) {
2124
revert ZeroProxyAddress();
2225
}
2326
proxy = proxy_;
27+
dappId = IApi3ReaderProxyWithDappId(proxy_).dappId();
2428
}
2529

26-
/// @notice Returns the inverted value of the underlying IApi3ReaderProxy
30+
/// @notice Returns the inverted value of the underlying
31+
/// IApi3ReaderProxyWithDappId
2732
/// @dev Calculates `int224(1e36) / baseValue`. The operation will revert if
2833
/// `baseValue` is zero. If `baseValue` is non-zero but its absolute value is
2934
/// greater than `1e36`, the result of the integer division will be `0`.
@@ -35,8 +40,9 @@ contract InverseApi3ReaderProxyV1 is IInverseApi3ReaderProxyV1 {
3540
override
3641
returns (int224 value, uint32 timestamp)
3742
{
38-
(int224 baseValue, uint32 baseTimestamp) = IApi3ReaderProxy(proxy)
39-
.read();
43+
(int224 baseValue, uint32 baseTimestamp) = IApi3ReaderProxyWithDappId(
44+
proxy
45+
).read();
4046

4147
if (baseValue == 0) {
4248
revert DivisionByZero();
@@ -95,7 +101,7 @@ contract InverseApi3ReaderProxyV1 is IInverseApi3ReaderProxyV1 {
95101
}
96102

97103
/// @dev A unique version is chosen to easily check if an unverified
98-
/// contract that acts as a Chainlink feed is a InverseApi3ReaderProxyV1
104+
/// contract that acts as a Chainlink feed is an InverseApi3ReaderProxyV1
99105
function version() external pure override returns (uint256) {
100106
return 4915;
101107
}

contracts/NormalizedApi3ReaderProxyV1.sol

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ contract NormalizedApi3ReaderProxyV1 is INormalizedApi3ReaderProxyV1 {
1515
/// @notice Chainlink AggregatorV2V3Interface contract address
1616
address public immutable override feed;
1717

18+
/// @notice dApp ID of the proxy
19+
uint256 public immutable override dappId;
20+
1821
/// @notice Pre-calculated factor for scaling the feed's value to 18
1922
/// decimals.
2023
int256 public immutable scalingFactor;
@@ -24,7 +27,8 @@ contract NormalizedApi3ReaderProxyV1 is INormalizedApi3ReaderProxyV1 {
2427
bool public immutable isUpscaling;
2528

2629
/// @param feed_ The address of the Chainlink AggregatorV2V3Interface feed
27-
constructor(address feed_) {
30+
/// @param dappId_ dApp ID of the proxy
31+
constructor(address feed_, uint256 dappId_) {
2832
if (feed_ == address(0)) {
2933
revert ZeroProxyAddress();
3034
}
@@ -36,6 +40,7 @@ contract NormalizedApi3ReaderProxyV1 is INormalizedApi3ReaderProxyV1 {
3640
revert NoNormalizationNeeded();
3741
}
3842
feed = feed_;
43+
dappId = dappId_;
3944
uint8 delta = feedDecimals_ > 18
4045
? feedDecimals_ - 18
4146
: 18 - feedDecimals_;

contracts/PriceCappedApi3ReaderProxyV1.sol

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// SPDX-License-Identifier: MIT
22
pragma solidity 0.8.27;
33

4-
import "@api3/contracts/interfaces/IApi3ReaderProxy.sol";
4+
import "./interfaces/IApi3ReaderProxyWithDappId.sol";
55
import "./interfaces/IPriceCappedApi3ReaderProxyV1.sol";
66

77
/**
@@ -21,16 +21,19 @@ import "./interfaces/IPriceCappedApi3ReaderProxyV1.sol";
2121
* floored at 0 if `lowerBound_` is 0.
2222
*/
2323
contract PriceCappedApi3ReaderProxyV1 is IPriceCappedApi3ReaderProxyV1 {
24-
/// @notice IApi3ReaderProxy contract address
24+
/// @notice IApi3ReaderProxyWithDappId contract address
2525
address public immutable override proxy;
2626

27+
/// @notice dApp ID of the proxy
28+
uint256 public immutable override dappId;
29+
2730
/// @notice The minimum price (inclusive) that this proxy will report.
2831
int224 public immutable override lowerBound;
2932

3033
/// @notice The maximum price (inclusive) that this proxy will report.
3134
int224 public immutable override upperBound;
3235

33-
/// @param proxy_ IApi3ReaderProxy contract address
36+
/// @param proxy_ IApi3ReaderProxyWithDappId contract address
3437
/// @param lowerBound_ The minimum price (inclusive) this proxy will report
3538
/// @param upperBound_ The maximum price (inclusive) this proxy will report
3639
constructor(address proxy_, int224 lowerBound_, int224 upperBound_) {
@@ -44,12 +47,13 @@ contract PriceCappedApi3ReaderProxyV1 is IPriceCappedApi3ReaderProxyV1 {
4447
revert UpperBoundMustBeGreaterOrEqualToLowerBound();
4548
}
4649
proxy = proxy_;
50+
dappId = IApi3ReaderProxyWithDappId(proxy_).dappId();
4751
lowerBound = lowerBound_;
4852
upperBound = upperBound_;
4953
}
5054

5155
/// @notice Reads the current value and timestamp from the underlying
52-
/// `IApi3ReaderProxy` and applies the price bounds.
56+
/// `IApi3ReaderProxyWithDappId` and applies the price bounds.
5357
/// @dev If the `baseValue` from the underlying proxy is less than
5458
/// `lowerBound`, then `lowerBound` is returned as the `value`. If
5559
/// `baseValue` is greater than `upperBound`, then `upperBound` is returned.
@@ -63,8 +67,9 @@ contract PriceCappedApi3ReaderProxyV1 is IPriceCappedApi3ReaderProxyV1 {
6367
override
6468
returns (int224 value, uint32 timestamp)
6569
{
66-
(int224 baseValue, uint32 baseTimestamp) = IApi3ReaderProxy(proxy)
67-
.read();
70+
(int224 baseValue, uint32 baseTimestamp) = IApi3ReaderProxyWithDappId(
71+
proxy
72+
).read();
6873

6974
timestamp = baseTimestamp;
7075

@@ -82,7 +87,7 @@ contract PriceCappedApi3ReaderProxyV1 is IPriceCappedApi3ReaderProxyV1 {
8287
/// @return True if the base value is less than `lowerBound` or greater
8388
/// than `upperBound`, false otherwise.
8489
function isCapped() external view returns (bool) {
85-
(int224 baseValue, ) = IApi3ReaderProxy(proxy).read();
90+
(int224 baseValue, ) = IApi3ReaderProxyWithDappId(proxy).read();
8691
return baseValue < lowerBound || baseValue > upperBound;
8792
}
8893

contracts/ProductApi3ReaderProxyV1.sol

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,48 @@
11
// SPDX-License-Identifier: MIT
22
pragma solidity 0.8.27;
33

4-
import "@api3/contracts/interfaces/IApi3ReaderProxy.sol";
4+
import "./interfaces/IApi3ReaderProxyWithDappId.sol";
55
import "./interfaces/IProductApi3ReaderProxyV1.sol";
66

77
/// @title An immutable proxy contract that is used to read a composition of two
8-
/// IApi3ReaderProxy data feeds by multiplying their values
8+
/// IApi3ReaderProxyWithDappId data feeds by multiplying their values
99
/// @dev This contract implements the AggregatorV2V3Interface to be compatible
1010
/// with Chainlink aggregators. This allows the contract to be used as a drop-in
1111
/// replacement for Chainlink aggregators in existing dApps.
1212
/// Refer to https://github.com/api3dao/migrate-from-chainlink-to-api3 for more
1313
/// information about the Chainlink interface implementation.
1414
contract ProductApi3ReaderProxyV1 is IProductApi3ReaderProxyV1 {
15-
/// @notice First IApi3ReaderProxy contract address
15+
/// @notice First IApi3ReaderProxyWithDappId contract address
1616
address public immutable override proxy1;
1717

18-
/// @notice Second IApi3ReaderProxy contract address
18+
/// @notice Second IApi3ReaderProxyWithDappId contract address
1919
address public immutable override proxy2;
2020

21-
/// @param proxy1_ First IApi3ReaderProxy contract address
22-
/// @param proxy2_ Second IApi3ReaderProxy contract address
21+
/// @notice The dApp ID of the two proxies
22+
uint256 public immutable override dappId;
23+
24+
/// @param proxy1_ First IApi3ReaderProxyWithDappId contract address
25+
/// @param proxy2_ Second IApi3ReaderProxyWithDappId contract address
2326
constructor(address proxy1_, address proxy2_) {
2427
if (proxy1_ == address(0) || proxy2_ == address(0)) {
2528
revert ZeroProxyAddress();
2629
}
2730
if (proxy1_ == proxy2_) {
2831
revert SameProxyAddress();
2932
}
33+
uint256 dappId1 = IApi3ReaderProxyWithDappId(proxy1_).dappId();
34+
uint256 dappId2 = IApi3ReaderProxyWithDappId(proxy2_).dappId();
35+
if (dappId1 != dappId2) {
36+
revert DappIdMismatch();
37+
}
3038
proxy1 = proxy1_;
3139
proxy2 = proxy2_;
40+
dappId = dappId1;
3241
}
3342

3443
/// @notice Returns the current value and timestamp of the rate composition
35-
/// between two IApi3ReaderProxy proxies by multiplying their values
44+
/// between two IApi3ReaderProxyWithDappId proxies by multiplying their
45+
/// values
3646
/// @dev Calculates product as `(int256(value1) * int256(value2)) / 1e18`.
3747
/// The initial multiplication `int256(value1) * int256(value2)` may revert
3848
/// on `int256` overflow. The final `int256` result of the full expression
@@ -49,8 +59,8 @@ contract ProductApi3ReaderProxyV1 is IProductApi3ReaderProxyV1 {
4959
override
5060
returns (int224 value, uint32 timestamp)
5161
{
52-
(int224 value1, ) = IApi3ReaderProxy(proxy1).read();
53-
(int224 value2, ) = IApi3ReaderProxy(proxy2).read();
62+
(int224 value1, ) = IApi3ReaderProxyWithDappId(proxy1).read();
63+
(int224 value2, ) = IApi3ReaderProxyWithDappId(proxy2).read();
5464

5565
value = int224((int256(value1) * int256(value2)) / 1e18);
5666
timestamp = uint32(block.timestamp);

contracts/adapters/ScaledApi3FeedProxyV1.sol

Lines changed: 23 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,42 @@
11
// SPDX-License-Identifier: MIT
22
pragma solidity 0.8.27;
33

4-
import "@api3/contracts/interfaces/IApi3ReaderProxy.sol";
4+
import "../interfaces/IApi3ReaderProxyWithDappId.sol";
55
import "./interfaces/IScaledApi3FeedProxyV1.sol";
66

77
/// @title An immutable Chainlink AggregatorV2V3Interface feed contract that
8-
/// scales the value of an IApi3ReaderProxy data feed to a target number of
9-
/// decimals
8+
/// scales the value of an IApi3ReaderProxyWithDappId data feed to a target
9+
/// number of decimals
1010
/// @dev This contract reads an `int224` value (assumed to be 18 decimals)
11-
/// from the underlying `IApi3ReaderProxy` and scales it to `targetDecimals`.
11+
/// from the underlying `IApi3ReaderProxyWithDappId` and scales it to
12+
///`targetDecimals`.
1213
/// The scaling arithmetic uses `int256` for intermediate results, allowing the
1314
/// scaled value to exceed `int224` limits if upscaling significantly; it will
1415
/// revert on `int256` overflow.
1516
/// When downscaling, integer division (`proxyValue / scalingFactor`) is used,
1617
/// which truncates and may lead to precision loss. Integrators must carefully
1718
/// consider this potential precision loss for their specific use case.
1819
contract ScaledApi3FeedProxyV1 is IScaledApi3FeedProxyV1 {
19-
/// @notice IApi3ReaderProxy contract address
20+
/// @notice IApi3ReaderProxyWithDappId contract address
2021
address public immutable override proxy;
2122

22-
/// @dev Target decimals for the scaled value.
23-
uint8 private immutable targetDecimals;
23+
/// @notice dApp ID of the proxy
24+
uint256 public immutable override dappId;
2425

2526
/// @notice Pre-calculated factor for scaling the proxy's 18-decimal value
2627
/// to `targetDecimals`.
27-
int256 public immutable scalingFactor;
28+
int256 public immutable override scalingFactor;
2829

2930
/// @notice True if upscaling (multiply by `scalingFactor`), false if
3031
/// downscaling (divide by `scalingFactor`), to scale to `targetDecimals`.
31-
bool public immutable isUpscaling;
32+
bool public immutable override isUpscaling;
33+
34+
/// @dev Target decimals for the scaled value.
35+
uint8 private immutable targetDecimals;
3236

33-
/// @param proxy_ IApi3ReaderProxy contract address
34-
/// @param targetDecimals_ Decimals used to scale the IApi3ReaderProxy value
37+
/// @param proxy_ IApi3ReaderProxyWithDappId contract address
38+
/// @param targetDecimals_ Decimals to scale the IApi3ReaderProxyWithDappId
39+
/// value
3540
constructor(address proxy_, uint8 targetDecimals_) {
3641
if (proxy_ == address(0)) {
3742
revert ZeroProxyAddress();
@@ -43,6 +48,7 @@ contract ScaledApi3FeedProxyV1 is IScaledApi3FeedProxyV1 {
4348
revert NoScalingNeeded();
4449
}
4550
proxy = proxy_;
51+
dappId = IApi3ReaderProxyWithDappId(proxy_).dappId();
4652
targetDecimals = targetDecimals_;
4753
uint8 delta = targetDecimals_ > 18
4854
? targetDecimals_ - 18
@@ -88,7 +94,7 @@ contract ScaledApi3FeedProxyV1 is IScaledApi3FeedProxyV1 {
8894
revert FunctionIsNotSupported();
8995
}
9096

91-
/// @dev Decimals used to scale the IApi3ReaderProxy value
97+
/// @dev Decimals used to scale the IApi3ReaderProxyWithDappId value
9298
function decimals() external view override returns (uint8) {
9399
return targetDecimals;
94100
}
@@ -137,17 +143,18 @@ contract ScaledApi3FeedProxyV1 is IScaledApi3FeedProxyV1 {
137143
updatedAt = startedAt;
138144
}
139145

140-
/// @notice Reads a value from the underlying `IApi3ReaderProxy` and
141-
/// scales it to `targetDecimals`.
146+
/// @notice Reads a value from the underlying `IApi3ReaderProxyWithDappId`
147+
/// and scales it to `targetDecimals`.
142148
/// @dev Reads from the underlying proxy and applies scaling to
143149
/// `targetDecimals`. Upscaling uses multiplication; downscaling uses integer
144150
/// division (which truncates). All scaling arithmetic is performed using
145151
/// `int256`.
146152
/// @return value The scaled signed fixed-point value with `targetDecimals`.
147153
/// @return timestamp The timestamp from the underlying proxy.
148154
function _read() internal view returns (int256 value, uint32 timestamp) {
149-
(int224 proxyValue, uint32 proxyTimestamp) = IApi3ReaderProxy(proxy)
150-
.read();
155+
(int224 proxyValue, uint32 proxyTimestamp) = IApi3ReaderProxyWithDappId(
156+
proxy
157+
).read();
151158

152159
value = isUpscaling
153160
? proxyValue * scalingFactor

contracts/adapters/interfaces/IScaledApi3FeedProxyV1.sol

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,6 @@ interface IScaledApi3FeedProxyV1 is AggregatorV2V3Interface {
1717
function scalingFactor() external view returns (int256);
1818

1919
function isUpscaling() external view returns (bool);
20+
21+
function dappId() external view returns (uint256);
2022
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity 0.8.27;
3+
4+
import "@api3/contracts/interfaces/IApi3ReaderProxy.sol";
5+
6+
interface IApi3ReaderProxyWithDappId is IApi3ReaderProxy {
7+
function dappId() external view returns (uint256);
8+
}

0 commit comments

Comments
 (0)