Description:
Proxy contract enabling upgradeable smart contract patterns. Delegates calls to an implementation contract.
Blockchain: Ethereum
Source Code: View Code On The Blockchain
Solidity Source Code:
{{
"language": "Solidity",
"sources": {
"@api3/contracts/interfaces/IApi3ReaderProxy.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/// @title Interface of the proxy contract that is used to read a specific API3
/// data feed
/// @notice While reading API3 data feeds, users are strongly recommended to
/// use this interface to interact with data feed-specific proxy contracts,
/// rather than accessing the underlying contracts directly
interface IApi3ReaderProxy {
/// @notice Returns the current value and timestamp of the API3 data feed
/// associated with the proxy contract
/// @dev The user is responsible for validating the returned data. For
/// example, if `value` is the spot price of an asset, it would be
/// reasonable to reject values that are not positive.
/// `timestamp` does not necessarily refer to a timestamp of the chain that
/// the read proxy is deployed on. Considering that it may refer to an
/// off-chain time (such as the system time of the data sources, or the
/// timestamp of another chain), the user should not expect it to be
/// strictly bounded by `block.timestamp`.
/// Considering that the read proxy contract may be upgradeable, the user
/// should not assume any hard guarantees about the behavior in general.
/// For example, even though it may sound reasonable to expect `timestamp`
/// to never decrease over time and the current implementation of the proxy
/// contract guarantees it, technically, an upgrade can cause `timestamp`
/// to decrease. Therefore, the user should be able to handle any change in
/// behavior, which may include reverting gracefully.
/// @return value Data feed value
/// @return timestamp Data feed timestamp
function read() external view returns (int224 value, uint32 timestamp);
}
"
},
"contracts/interfaces/IProductApi3ReaderProxyV1.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
import "@api3/contracts/interfaces/IApi3ReaderProxy.sol";
import "../vendor/@chainlink/contracts@1.2.0/src/v0.8/shared/interfaces/AggregatorV2V3Interface.sol";
interface IProductApi3ReaderProxyV1 is
IApi3ReaderProxy,
AggregatorV2V3Interface
{
error ZeroProxyAddress();
error SameProxyAddress();
error ZeroDenominator();
error FunctionIsNotSupported();
function proxy1() external view returns (address);
function proxy2() external view returns (address);
}
"
},
"contracts/ProductApi3ReaderProxyV1.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity 0.8.27;
import "@api3/contracts/interfaces/IApi3ReaderProxy.sol";
import "./interfaces/IProductApi3ReaderProxyV1.sol";
/// @title An immutable proxy contract that is used to read a composition of two
/// IApi3ReaderProxy data feeds by multiplying their values
/// @dev This contract implements the AggregatorV2V3Interface to be compatible
/// with Chainlink aggregators. This allows the contract to be used as a drop-in
/// replacement for Chainlink aggregators in existing dApps.
/// Refer to https://github.com/api3dao/migrate-from-chainlink-to-api3 for more
/// information about the Chainlink interface implementation.
contract ProductApi3ReaderProxyV1 is IProductApi3ReaderProxyV1 {
/// @notice First IApi3ReaderProxy contract address
address public immutable override proxy1;
/// @notice Second IApi3ReaderProxy contract address
address public immutable override proxy2;
/// @param proxy1_ First IApi3ReaderProxy contract address
/// @param proxy2_ Second IApi3ReaderProxy contract address
constructor(address proxy1_, address proxy2_) {
if (proxy1_ == address(0) || proxy2_ == address(0)) {
revert ZeroProxyAddress();
}
if (proxy1_ == proxy2_) {
revert SameProxyAddress();
}
proxy1 = proxy1_;
proxy2 = proxy2_;
}
/// @notice Returns the current value and timestamp of the rate composition
/// between two IApi3ReaderProxy proxies by multiplying their values
/// @dev Calculates product as `(int256(value1) * int256(value2)) / 1e18`.
/// The initial multiplication `int256(value1) * int256(value2)` may revert
/// on `int256` overflow. The final `int256` result of the full expression
/// is then cast to `int224`. This cast is unchecked for gas optimization
/// and may silently truncate if the result exceeds `int224` limits.
/// The returned timestamp is `block.timestamp`, reflecting the on-chain
/// computation time of the product. Underlying feed timestamps are not
/// aggregated due to complexity and potential for misinterpretation.
/// @return value Value of the product of the two proxies
/// @return timestamp Timestamp of the current block
function read()
public
view
override
returns (int224 value, uint32 timestamp)
{
(int224 value1, ) = IApi3ReaderProxy(proxy1).read();
(int224 value2, ) = IApi3ReaderProxy(proxy2).read();
value = int224((int256(value1) * int256(value2)) / 1e18);
timestamp = uint32(block.timestamp);
}
/// @dev AggregatorV2V3Interface users are already responsible with
/// validating the values that they receive (e.g., revert if the spot price
/// of an asset is negative). Therefore, this contract omits validation.
function latestAnswer() external view override returns (int256 value) {
(value, ) = read();
}
/// @dev A Chainlink feed contract returns the block timestamp at which the
/// feed was last updated. On the other hand, an Api3 feed timestamp
/// denotes the point in time at which the first-party oracles signed the
/// data used to do the last update. We find this to be a reasonable
/// approximation, considering that usually the timestamp is only used to
/// check if the last update is stale.
function latestTimestamp()
external
view
override
returns (uint256 timestamp)
{
(, timestamp) = read();
}
/// @dev Api3 feeds are updated asynchronously and not in rounds
function latestRound() external pure override returns (uint256) {
revert FunctionIsNotSupported();
}
/// @dev Functions that use the round ID as an argument are not supported
function getAnswer(uint256) external pure override returns (int256) {
revert FunctionIsNotSupported();
}
/// @dev Functions that use the round ID as an argument are not supported
function getTimestamp(uint256) external pure override returns (uint256) {
revert FunctionIsNotSupported();
}
/// @dev Api3 feeds always use 18 decimals
function decimals() external pure override returns (uint8) {
return 18;
}
/// @dev Underlying proxies dApp ID and dAPI name act as the description, and
/// this is left empty to save gas on contract deployment
function description() external pure override returns (string memory) {
return "";
}
/// @dev A unique version is chosen to easily check if an unverified
/// contract that acts as a Chainlink feed is a ProductApi3ReaderProxyV1
function version() external pure override returns (uint256) {
return 4914;
}
/// @dev Functions that use the round ID as an argument are not supported
function getRoundData(
uint80
)
external
pure
override
returns (uint80, int256, uint256, uint256, uint80)
{
revert FunctionIsNotSupported();
}
/// @dev Rounds IDs are returned as `0` as invalid values.
/// Similar to `latestAnswer()`, we leave the validation of the returned
/// value to the caller.
function latestRoundData()
external
view
override
returns (
uint80 roundId,
int256 answer,
uint256 startedAt,
uint256 updatedAt,
uint80 answeredInRound
)
{
roundId = answeredInRound = 0;
(answer, startedAt) = read();
updatedAt = startedAt;
}
}
"
},
"contracts/vendor/@chainlink/contracts@1.2.0/src/v0.8/shared/interfaces/AggregatorInterface.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// solhint-disable-next-line interface-starts-with-i
interface AggregatorInterface {
function latestAnswer() external view returns (int256);
function latestTimestamp() external view returns (uint256);
function latestRound() external view returns (uint256);
function getAnswer(uint256 roundId) external view returns (int256);
function getTimestamp(uint256 roundId) external view returns (uint256);
event AnswerUpdated(int256 indexed current, uint256 indexed roundId, uint256 updatedAt);
event NewRound(uint256 indexed roundId, address indexed startedBy, uint256 startedAt);
}
"
},
"contracts/vendor/@chainlink/contracts@1.2.0/src/v0.8/shared/interfaces/AggregatorV2V3Interface.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {AggregatorInterface} from "./AggregatorInterface.sol";
import {AggregatorV3Interface} from "./AggregatorV3Interface.sol";
// solhint-disable-next-line interface-starts-with-i
interface AggregatorV2V3Interface is AggregatorInterface, AggregatorV3Interface {}
"
},
"contracts/vendor/@chainlink/contracts@1.2.0/src/v0.8/shared/interfaces/AggregatorV3Interface.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// solhint-disable-next-line interface-starts-with-i
interface AggregatorV3Interface {
function decimals() external view returns (uint8);
function description() external view returns (string memory);
function version() external view returns (uint256);
function getRoundData(
uint80 _roundId
) external view returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound);
function latestRoundData()
external
view
returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound);
}
"
}
},
"settings": {
"optimizer": {
"enabled": true,
"runs": 1000
},
"evmVersion": "paris",
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"metadata": {
"useLiteralContent": true
}
}
}}
Submitted on: 2025-11-03 17:18:01
Comments
Log in to comment.
No comments yet.