Interfaces
Data Streams require several interfaces in order to retrieve and verify reports.
- Automation interfaces:
- Data Streams interfaces:
- IVerifierProxy
- IReportHandler
In the current code example for using Data Streams with Automation, these interfaces are specified in the example itself. Imports for these interfaces will be available in the future.
// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;
import {Common} from "@chainlink/contracts/src/v0.8/llo-feeds/libraries/Common.sol";
import {StreamsLookupCompatibleInterface} from "@chainlink/contracts/src/v0.8/automation/interfaces/StreamsLookupCompatibleInterface.sol";
import {ILogAutomation, Log} from "@chainlink/contracts/src/v0.8/automation/interfaces/ILogAutomation.sol";
import {IRewardManager} from "@chainlink/contracts/src/v0.8/llo-feeds/interfaces/IRewardManager.sol";
import {IVerifierFeeManager} from "@chainlink/contracts/src/v0.8/llo-feeds/interfaces/IVerifierFeeManager.sol";
import {IERC20} from "@chainlink/contracts/src/v0.8/vendor/openzeppelin-solidity/v4.8.3/contracts/interfaces/IERC20.sol";
/**
* THIS IS AN EXAMPLE CONTRACT THAT USES UN-AUDITED CODE FOR DEMONSTRATION PURPOSES.
* DO NOT USE THIS CODE IN PRODUCTION.
*/
// Custom interfaces for IVerifierProxy and IFeeManager
interface IVerifierProxy {
function verify(
bytes calldata payload,
bytes calldata parameterPayload
) external payable returns (bytes memory verifierResponse);
function s_feeManager() external view returns (IVerifierFeeManager);
}
interface IFeeManager {
function getFeeAndReward(
address subscriber,
bytes memory unverifiedReport,
address quoteAddress
) external returns (Common.Asset memory, Common.Asset memory, uint256);
function i_linkAddress() external view returns (address);
function i_nativeAddress() external view returns (address);
function i_rewardManager() external view returns (address);
}
contract StreamsUpkeep is ILogAutomation, StreamsLookupCompatibleInterface {
struct BasicReport {
bytes32 feedId; // The feed ID the report has data for
uint32 validFromTimestamp; // Earliest timestamp for which price is applicable
uint32 observationsTimestamp; // Latest timestamp for which price is applicable
uint192 nativeFee; // Base cost to validate a transaction using the report, denominated in the chain’s native token (WETH/ETH)
uint192 linkFee; // Base cost to validate a transaction using the report, denominated in LINK
uint32 expiresAt; // Latest timestamp where the report can be verified onchain
int192 price; // DON consensus median price, carried to 8 decimal places
}
struct PremiumReport {
bytes32 feedId; // The feed ID the report has data for
uint32 validFromTimestamp; // Earliest timestamp for which price is applicable
uint32 observationsTimestamp; // Latest timestamp for which price is applicable
uint192 nativeFee; // Base cost to validate a transaction using the report, denominated in the chain’s native token (WETH/ETH)
uint192 linkFee; // Base cost to validate a transaction using the report, denominated in LINK
uint32 expiresAt; // Latest timestamp where the report can be verified onchain
int192 price; // DON consensus median price, carried to 8 decimal places
int192 bid; // Simulated price impact of a buy order up to the X% depth of liquidity utilisation
int192 ask; // Simulated price impact of a sell order up to the X% depth of liquidity utilisation
}
struct Quote {
address quoteAddress;
}
event PriceUpdate(int192 indexed price);
IVerifierProxy public verifier;
address public FEE_ADDRESS;
string public constant DATASTREAMS_FEEDLABEL = "feedIDs";
string public constant DATASTREAMS_QUERYLABEL = "timestamp";
int192 public last_retrieved_price;
// This example reads the ID for the basic ETH/USD price report on Arbitrum Sepolia.
// Find a complete list of IDs at https://docs.chain.link/data-streams/stream-ids
string[] public feedIds = [
"0x00027bbaff688c906a3e20a34fe951715d1018d262a5b66e38eda027a674cd1b"
];
constructor(address _verifier) {
verifier = IVerifierProxy(_verifier);
}
// This function uses revert to convey call information.
// See https://eips.ethereum.org/EIPS/eip-3668#rationale for details.
function checkLog(
Log calldata log,
bytes memory
) external returns (bool upkeepNeeded, bytes memory performData) {
revert StreamsLookup(
DATASTREAMS_FEEDLABEL,
feedIds,
DATASTREAMS_QUERYLABEL,
log.timestamp,
""
);
}
/**
* @notice this is a new, optional function in streams lookup. It is meant to surface streams lookup errors.
* @return upkeepNeeded boolean to indicate whether the keeper should call performUpkeep or not.
* @return performData bytes that the keeper should call performUpkeep with, if
* upkeep is needed. If you would like to encode data to decode later, try `abi.encode`.
*/
function checkErrorHandler(
uint256 /*errCode*/,
bytes memory /*extraData*/
) external pure returns (bool upkeepNeeded, bytes memory performData) {
return (true, "0");
// Hardcoded to always perform upkeep.
// Read the StreamsLookup error handler guide for more information.
// https://docs.chain.link/chainlink-automation/guides/streams-lookup-error-handler
}
// The Data Streams report bytes is passed here.
// extraData is context data from feed lookup process.
// Your contract may include logic to further process this data.
// This method is intended only to be simulated offchain by Automation.
// The data returned will then be passed by Automation into performUpkeep
function checkCallback(
bytes[] calldata values,
bytes calldata extraData
) external pure returns (bool, bytes memory) {
return (true, abi.encode(values, extraData));
}
// function will be performed onchain
function performUpkeep(bytes calldata performData) external {
// Decode the performData bytes passed in by CL Automation.
// This contains the data returned by your implementation in checkCallback().
(bytes[] memory signedReports, bytes memory extraData) = abi.decode(
performData,
(bytes[], bytes)
);
bytes memory unverifiedReport = signedReports[0];
(, /* bytes32[3] reportContextData */ bytes memory reportData) = abi
.decode(unverifiedReport, (bytes32[3], bytes));
// Report verification fees
IFeeManager feeManager = IFeeManager(address(verifier.s_feeManager()));
IRewardManager rewardManager = IRewardManager(
address(feeManager.i_rewardManager())
);
address feeTokenAddress = feeManager.i_linkAddress();
(Common.Asset memory fee, , ) = feeManager.getFeeAndReward(
address(this),
reportData,
feeTokenAddress
);
// Approve rewardManager to spend this contract's balance in fees
IERC20(feeTokenAddress).approve(address(rewardManager), fee.amount);
// Verify the report
bytes memory verifiedReportData = verifier.verify(
unverifiedReport,
abi.encode(feeTokenAddress)
);
// Decode verified report data into BasicReport struct
BasicReport memory verifiedReport = abi.decode(
verifiedReportData,
(BasicReport)
);
// Log price from report
emit PriceUpdate(verifiedReport.price);
// Store the price from the report
last_retrieved_price = verifiedReport.price;
}
fallback() external payable {}
}