// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "../utils/SelfMulticall.sol";
import "./RoleDeriver.sol";
import "./interfaces/IAccessControlRegistryAdminned.sol";
import "./interfaces/IAccessControlRegistry.sol";
/// @title Contract to be inherited by contracts whose adminship functionality
/// will be implemented using AccessControlRegistry
contract AccessControlRegistryAdminned is
SelfMulticall,
RoleDeriver,
IAccessControlRegistryAdminned
{
/// @notice AccessControlRegistry contract address
address public immutable override accessControlRegistry;
/// @notice Admin role description
string public override adminRoleDescription;
bytes32 internal immutable adminRoleDescriptionHash;
/// @dev Contracts deployed with the same admin role descriptions will have
/// the same roles, meaning that granting an account a role will authorize
/// it in multiple contracts. Unless you want your deployed contract to
/// share the role configuration of another contract, use a unique admin
/// role description.
/// @param _accessControlRegistry AccessControlRegistry contract address
/// @param _adminRoleDescription Admin role description
constructor(
address _accessControlRegistry,
string memory _adminRoleDescription
) {
require(_accessControlRegistry != address(0), "ACR address zero");
require(
bytes(_adminRoleDescription).length > 0,
"Admin role description empty"
);
accessControlRegistry = _accessControlRegistry;
adminRoleDescription = _adminRoleDescription;
adminRoleDescriptionHash = keccak256(
abi.encodePacked(_adminRoleDescription)
);
}
/// @notice Derives the admin role for the specific manager address
/// @param manager Manager address
/// @return adminRole Admin role
function _deriveAdminRole(
address manager
) internal view returns (bytes32 adminRole) {
adminRole = _deriveRole(
_deriveRootRole(manager),
adminRoleDescriptionHash
);
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "./AccessControlRegistryAdminned.sol";
import "./interfaces/IAccessControlRegistryAdminnedWithManager.sol";
/// @title Contract to be inherited by contracts with manager whose adminship
/// functionality will be implemented using AccessControlRegistry
/// @notice The manager address here is expected to belong to an
/// AccessControlRegistry user that is a multisig/DAO
contract AccessControlRegistryAdminnedWithManager is
AccessControlRegistryAdminned,
IAccessControlRegistryAdminnedWithManager
{
/// @notice Address of the manager that manages the related
/// AccessControlRegistry roles
/// @dev The mutability of the manager role can be implemented by
/// designating an OwnableCallForwarder contract as the manager. The
/// ownership of this contract can then be transferred, effectively
/// transferring managership.
address public immutable override manager;
/// @notice Admin role
/// @dev Since `manager` is immutable, so is `adminRole`
bytes32 public immutable override adminRole;
/// @param _accessControlRegistry AccessControlRegistry contract address
/// @param _adminRoleDescription Admin role description
/// @param _manager Manager address
constructor(
address _accessControlRegistry,
string memory _adminRoleDescription,
address _manager
)
AccessControlRegistryAdminned(
_accessControlRegistry,
_adminRoleDescription
)
{
require(_manager != address(0), "Manager address zero");
manager = _manager;
adminRole = _deriveAdminRole(_manager);
}
}
// SPDX-License-Identifier: MIT
pragma solidity 0.8.17;
import "./OevDapiServer.sol";
import "./BeaconUpdatesWithSignedData.sol";
import "./interfaces/IApi3ServerV1.sol";
/// @title First version of the contract that API3 uses to serve data feeds
/// @notice Api3ServerV1 serves data feeds in the form of Beacons, Beacon sets,
/// dAPIs, with optional OEV support for all of these.
/// The base Beacons are only updateable using signed data, and the Beacon sets
/// are updateable based on the Beacons, optionally using PSP. OEV proxy
/// Beacons and Beacon sets are updateable using OEV-signed data.
/// Api3ServerV1 does not support Beacons to be updated using RRP or PSP.
contract Api3ServerV1 is
OevDapiServer,
BeaconUpdatesWithSignedData,
IApi3ServerV1
{
/// @param _accessControlRegistry AccessControlRegistry contract address
/// @param _adminRoleDescription Admin role description
/// @param _manager Manager address
constructor(
address _accessControlRegistry,
string memory _adminRoleDescription,
address _manager
) OevDapiServer(_accessControlRegistry, _adminRoleDescription, _manager) {}
/// @notice Reads the data feed with ID
/// @param dataFeedId Data feed ID
/// @return value Data feed value
/// @return timestamp Data feed timestamp
function readDataFeedWithId(
bytes32 dataFeedId
) external view override returns (int224 value, uint32 timestamp) {
return _readDataFeedWithId(dataFeedId);
}
/// @notice Reads the data feed with dAPI name hash
/// @param dapiNameHash dAPI name hash
/// @return value Data feed value
/// @return timestamp Data feed timestamp
function readDataFeedWithDapiNameHash(
bytes32 dapiNameHash
) external view override returns (int224 value, uint32 timestamp) {
return _readDataFeedWithDapiNameHash(dapiNameHash);
}
/// @notice Reads the data feed as the OEV proxy with ID
/// @param dataFeedId Data feed ID
/// @return value Data feed value
/// @return timestamp Data feed timestamp
function readDataFeedWithIdAsOevProxy(
bytes32 dataFeedId
) external view override returns (int224 value, uint32 timestamp) {
return _readDataFeedWithIdAsOevProxy(dataFeedId);
}
/// @notice Reads the data feed as the OEV proxy with dAPI name hash
/// @param dapiNameHash dAPI name hash
/// @return value Data feed value
/// @return timestamp Data feed timestamp
function readDataFeedWithDapiNameHashAsOevProxy(
bytes32 dapiNameHash
) external view override returns (int224 value, uint32 timestamp) {
return _readDataFeedWithDapiNameHashAsOevProxy(dapiNameHash);
}
function dataFeeds(
bytes32 dataFeedId
) external view override returns (int224 value, uint32 timestamp) {
DataFeed storage dataFeed = _dataFeeds[dataFeedId];
(value, timestamp) = (dataFeed.value, dataFeed.timestamp);
}
function oevProxyToIdToDataFeed(
address proxy,
bytes32 dataFeedId
) external view override returns (int224 value, uint32 timestamp) {
DataFeed storage dataFeed = _oevProxyToIdToDataFeed[proxy][dataFeedId];
(value, timestamp) = (dataFeed.value, dataFeed.timestamp);
}
}
// SPDX-License-Identifier: MIT
pragma solidity 0.8.17;
import "./DataFeedServer.sol";
import "./interfaces/IBeaconUpdatesWithSignedData.sol";
import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
/// @title Contract that updates Beacons using signed data
contract BeaconUpdatesWithSignedData is
DataFeedServer,
IBeaconUpdatesWithSignedData
{
using ECDSA for bytes32;
/// @notice Updates a Beacon using data signed by the Airnode
/// @dev The signed data here is intentionally very general for practical
/// reasons. It is less demanding on the signer to have data signed once
/// and use that everywhere.
/// @param airnode Airnode address
/// @param templateId Template ID
/// @param timestamp Signature timestamp
/// @param data Update data (an `int256` encoded in contract ABI)
/// @param signature Template ID, timestamp and the update data signed by
/// the Airnode
/// @return beaconId Updated Beacon ID
function updateBeaconWithSignedData(
address airnode,
bytes32 templateId,
uint256 timestamp,
bytes calldata data,
bytes calldata signature
) external override returns (bytes32 beaconId) {
require(
(
keccak256(abi.encodePacked(templateId, timestamp, data))
.toEthSignedMessageHash()
).recover(signature) == airnode,
"Signature mismatch"
);
beaconId = deriveBeaconId(airnode, templateId);
int224 updatedValue = processBeaconUpdate(beaconId, timestamp, data);
emit UpdatedBeaconWithSignedData(
beaconId,
updatedValue,
uint32(timestamp)
);
}
}
// SPDX-License-Identifier: MIT
pragma solidity 0.8.17;
import "../access-control-registry/AccessControlRegistryAdminnedWithManager.sol";
import "./DataFeedServer.sol";
import "./interfaces/IDapiServer.sol";
/// @title Contract that serves dAPIs mapped to Beacons and Beacon sets
/// @notice Beacons and Beacon sets are addressed by immutable IDs. Although
/// this is trust-minimized, it requires users to manage the ID of the data
/// feed they are using. For when the user does not want to do this, dAPIs can
/// be used as an abstraction layer. By using a dAPI, the user delegates this
/// responsibility to dAPI management. It is important for dAPI management to
/// be restricted by consensus rules (by using a multisig or a DAO) and similar
/// trustless security mechanisms.
contract DapiServer is
AccessControlRegistryAdminnedWithManager,
DataFeedServer,
IDapiServer
{
/// @notice dAPI name setter role description
string public constant override DAPI_NAME_SETTER_ROLE_DESCRIPTION =
"dAPI name setter";
/// @notice dAPI name setter role
bytes32 public immutable override dapiNameSetterRole;
/// @notice dAPI name hash mapped to the data feed ID
mapping(bytes32 => bytes32) public override dapiNameHashToDataFeedId;
/// @param _accessControlRegistry AccessControlRegistry contract address
/// @param _adminRoleDescription Admin role description
/// @param _manager Manager address
constructor(
address _accessControlRegistry,
string memory _adminRoleDescription,
address _manager
)
AccessControlRegistryAdminnedWithManager(
_accessControlRegistry,
_adminRoleDescription,
_manager
)
{
dapiNameSetterRole = _deriveRole(
_deriveAdminRole(manager),
DAPI_NAME_SETTER_ROLE_DESCRIPTION
);
}
/// @notice Sets the data feed ID the dAPI name points to
/// @dev While a data feed ID refers to a specific Beacon or Beacon set,
/// dAPI names provide a more abstract interface for convenience. This
/// means a dAPI name that was pointing to a Beacon can be pointed to a
/// Beacon set, then another Beacon set, etc.
/// @param dapiName Human-readable dAPI name
/// @param dataFeedId Data feed ID the dAPI name will point to
function setDapiName(
bytes32 dapiName,
bytes32 dataFeedId
) external override {
require(dapiName != bytes32(0), "dAPI name zero");
require(
msg.sender == manager ||
IAccessControlRegistry(accessControlRegistry).hasRole(
dapiNameSetterRole,
msg.sender
),
"Sender cannot set dAPI name"
);
dapiNameHashToDataFeedId[
keccak256(abi.encodePacked(dapiName))
] = dataFeedId;
emit SetDapiName(dataFeedId, dapiName, msg.sender);
}
/// @notice Returns the data feed ID the dAPI name is set to
/// @param dapiName dAPI name
/// @return Data feed ID
function dapiNameToDataFeedId(
bytes32 dapiName
) external view override returns (bytes32) {
return dapiNameHashToDataFeedId[keccak256(abi.encodePacked(dapiName))];
}
/// @notice Reads the data feed with dAPI name hash
/// @param dapiNameHash dAPI name hash
/// @return value Data feed value
/// @return timestamp Data feed timestamp
function _readDataFeedWithDapiNameHash(
bytes32 dapiNameHash
) internal view returns (int224 value, uint32 timestamp) {
bytes32 dataFeedId = dapiNameHashToDataFeedId[dapiNameHash];
require(dataFeedId != bytes32(0), "dAPI name not set");
DataFeed storage dataFeed = _dataFeeds[dataFeedId];
(value, timestamp) = (dataFeed.value, dataFeed.timestamp);
require(timestamp > 0, "Data feed not initialized");
}
}
// SPDX-License-Identifier: MIT
pragma solidity 0.8.17;
import "../utils/ExtendedSelfMulticall.sol";
import "./aggregation/Median.sol";
import "./interfaces/IDataFeedServer.sol";
import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
/// @title Contract that serves Beacons and Beacon sets
/// @notice A Beacon is a live data feed addressed by an ID, which is derived
/// from an Airnode address and a template ID. This is suitable where the more
/// recent data point is always more favorable, e.g., in the context of an
/// asset price data feed. Beacons can also be seen as one-Airnode data feeds
/// that can be used individually or combined to build Beacon sets.
contract DataFeedServer is ExtendedSelfMulticall, Median, IDataFeedServer {
using ECDSA for bytes32;
// Airnodes serve their fulfillment data along with timestamps. This
// contract casts the reported data to `int224` and the timestamp to
// `uint32`, which works until year 2106.
struct DataFeed {
int224 value;
uint32 timestamp;
}
/// @notice Data feed with ID
mapping(bytes32 => DataFeed) internal _dataFeeds;
/// @dev Reverts if the timestamp is from more than 1 hour in the future
modifier onlyValidTimestamp(uint256 timestamp) virtual {
unchecked {
require(
timestamp < block.timestamp + 1 hours,
"Timestamp not valid"
);
}
_;
}
/// @notice Updates the Beacon set using the current values of its Beacons
/// @dev As an oddity, this function still works if some of the IDs in
/// `beaconIds` belong to Beacon sets rather than Beacons. This can be used
/// to implement hierarchical Beacon sets.
/// @param beaconIds Beacon IDs
/// @return beaconSetId Beacon set ID
function updateBeaconSetWithBeacons(
bytes32[] memory beaconIds
) public override returns (bytes32 beaconSetId) {
(int224 updatedValue, uint32 updatedTimestamp) = aggregateBeacons(
beaconIds
);
beaconSetId = deriveBeaconSetId(beaconIds);
DataFeed storage beaconSet = _dataFeeds[beaconSetId];
if (beaconSet.timestamp == updatedTimestamp) {
require(
beaconSet.value != updatedValue,
"Does not update Beacon set"
);
}
_dataFeeds[beaconSetId] = DataFeed({
value: updatedValue,
timestamp: updatedTimestamp
});
emit UpdatedBeaconSetWithBeacons(
beaconSetId,
updatedValue,
updatedTimestamp
);
}
/// @notice Reads the data feed with ID
/// @param dataFeedId Data feed ID
/// @return value Data feed value
/// @return timestamp Data feed timestamp
function _readDataFeedWithId(
bytes32 dataFeedId
) internal view returns (int224 value, uint32 timestamp) {
DataFeed storage dataFeed = _dataFeeds[dataFeedId];
(value, timestamp) = (dataFeed.value, dataFeed.timestamp);
require(timestamp >