Skip to content

Commit b6c6243

Browse files
authored
Update Data Streams examples (smartcontractkit#1631)
* Update Data Streams examples * Edits * Edits * Edits * Comments and eip-3688 info * reportContextData * Add storage variable for retrieved price * Edits and comments * Edits * Update stream IDs and verifier addresses
1 parent 3295db7 commit b6c6243

File tree

3 files changed

+102
-126
lines changed

3 files changed

+102
-126
lines changed

public/samples/DataStreams/StreamsUpkeep.sol

+85-97
Original file line numberDiff line numberDiff line change
@@ -1,54 +1,43 @@
11
// SPDX-License-Identifier: MIT
2-
pragma solidity ^0.8.19;
2+
pragma solidity ^0.8.16;
33

4-
interface StreamsLookupCompatibleInterface {
5-
error StreamsLookup(
6-
string feedParamKey,
7-
string[] feeds,
8-
string timeParamKey,
9-
uint256 time,
10-
bytes extraData
11-
);
4+
import {Common} from "@chainlink/contracts/src/v0.8/libraries/Common.sol";
5+
import {StreamsLookupCompatibleInterface} from "@chainlink/contracts/src/v0.8/automation/interfaces/StreamsLookupCompatibleInterface.sol";
6+
import {ILogAutomation, Log} from "@chainlink/contracts/src/v0.8/automation/interfaces/ILogAutomation.sol";
7+
import {IRewardManager} from "@chainlink/contracts/src/v0.8/llo-feeds/interfaces/IRewardManager.sol";
8+
import {IVerifierFeeManager} from "@chainlink/contracts/src/v0.8/llo-feeds/interfaces/IVerifierFeeManager.sol";
9+
import {IERC20} from "@chainlink/contracts/src/v0.8/vendor/openzeppelin-solidity/v4.8.0/contracts/interfaces/IERC20.sol";
1210

13-
function checkCallback(
14-
bytes[] memory values,
15-
bytes memory extraData
16-
) external view returns (bool upkeepNeeded, bytes memory performData);
17-
}
18-
19-
interface ILogAutomation {
20-
function checkLog(
21-
Log calldata log,
22-
bytes memory checkData
23-
) external returns (bool upkeepNeeded, bytes memory performData);
24-
25-
function performUpkeep(bytes calldata performData) external;
26-
}
27-
28-
struct Log {
29-
uint256 index;
30-
uint256 timestamp;
31-
bytes32 txHash;
32-
uint256 blockNumber;
33-
bytes32 blockHash;
34-
address source;
35-
bytes32[] topics;
36-
bytes data;
37-
}
11+
/**
12+
* THIS IS AN EXAMPLE CONTRACT THAT USES UN-AUDITED CODE FOR DEMONSTRATION PURPOSES.
13+
* DO NOT USE THIS CODE IN PRODUCTION.
14+
*/
3815

16+
// Custom interfaces for IVerifierProxy and IFeeManager
3917
interface IVerifierProxy {
4018
function verify(
41-
bytes memory signedReport
19+
bytes calldata payload,
20+
bytes calldata parameterPayload
4221
) external payable returns (bytes memory verifierResponse);
22+
23+
function s_feeManager() external view returns (IVerifierFeeManager);
4324
}
4425

45-
interface IReportHandler {
46-
function handleReport(bytes calldata report) external;
26+
interface IFeeManager {
27+
function getFeeAndReward(
28+
address subscriber,
29+
bytes memory unverifiedReport,
30+
address quoteAddress
31+
) external returns (Common.Asset memory, Common.Asset memory, uint256);
32+
33+
function i_linkAddress() external view returns (address);
34+
35+
function i_nativeAddress() external view returns (address);
36+
37+
function i_rewardManager() external view returns (address);
4738
}
4839

4940
contract StreamsUpkeep is ILogAutomation, StreamsLookupCompatibleInterface {
50-
IVerifierProxy public verifier;
51-
5241
struct BasicReport {
5342
bytes32 feedId; // The feed ID the report has data for
5443
uint32 validFromTimestamp; // Earliest timestamp for which price is applicable
@@ -75,100 +64,99 @@ contract StreamsUpkeep is ILogAutomation, StreamsLookupCompatibleInterface {
7564
address quoteAddress;
7665
}
7766

78-
event ReportVerified(BasicReport indexed report);
79-
event PriceUpdate(int192 price);
67+
event PriceUpdate(int192 indexed price);
68+
69+
IVerifierProxy public verifier;
70+
71+
address public FEE_ADDRESS;
72+
string public constant DATASTREAMS_FEEDLABEL = "feedIDs";
73+
string public constant DATASTREAMS_QUERYLABEL = "timestamp";
74+
int192 public last_retrieved_price;
8075

81-
address public immutable FEE_ADDRESS;
82-
string public constant STRING_DATASTREAMS_FEEDLABEL = "feedIDs";
83-
string public constant STRING_DATASTREAMS_QUERYLABEL = "timestamp";
84-
string[] public feedsHex = [
85-
"0x00023496426b520583ae20a66d80484e0fc18544866a5b0bfee15ec771963274"
76+
// This example reads the ID for the basic ETH/USD price report on Arbitrum Sepolia.
77+
// Find a complete list of IDs at https://docs.chain.link/data-streams/stream-ids
78+
string[] public feedIds = [
79+
"0x00027bbaff688c906a3e20a34fe951715d1018d262a5b66e38eda027a674cd1b"
8680
];
8781

88-
constructor(address _feeAddress, address _verifier) {
89-
verifier = IVerifierProxy(_verifier); //0x2ff010DEbC1297f19579B4246cad07bd24F2488A
90-
FEE_ADDRESS = _feeAddress; // 0xe39Ab88f8A4777030A534146A9Ca3B52bd5D43A3 (WETH)
82+
constructor(address _verifier) {
83+
verifier = IVerifierProxy(_verifier);
9184
}
9285

86+
// This function uses revert to convey call information.
87+
// See https://eips.ethereum.org/EIPS/eip-3668#rationale for details.
9388
function checkLog(
9489
Log calldata log,
9590
bytes memory
96-
) external view returns (bool upkeepNeeded, bytes memory performData) {
91+
) external returns (bool upkeepNeeded, bytes memory performData) {
9792
revert StreamsLookup(
98-
STRING_DATASTREAMS_FEEDLABEL,
99-
feedsHex,
100-
STRING_DATASTREAMS_QUERYLABEL,
93+
DATASTREAMS_FEEDLABEL,
94+
feedIds,
95+
DATASTREAMS_QUERYLABEL,
10196
log.timestamp,
10297
""
10398
);
10499
}
105100

101+
// The Data Streams report bytes is passed here.
102+
// extraData is context data from feed lookup process.
103+
// Your contract may include logic to further process this data.
104+
// This method is intended only to be simulated off-chain by Automation.
105+
// The data returned will then be passed by Automation into performUpkeep
106106
function checkCallback(
107107
bytes[] calldata values,
108108
bytes calldata extraData
109109
) external pure returns (bool, bytes memory) {
110110
return (true, abi.encode(values, extraData));
111111
}
112112

113-
function performUpkeep(bytes calldata performData) external override {
113+
// function will be performed on-chain
114+
function performUpkeep(bytes calldata performData) external {
115+
// Decode the performData bytes passed in by CL Automation.
116+
// This contains the data returned by your implementation in checkCallback().
114117
(bytes[] memory signedReports, bytes memory extraData) = abi.decode(
115118
performData,
116119
(bytes[], bytes)
117120
);
118121

119-
bytes memory report = signedReports[0];
122+
bytes memory unverifiedReport = signedReports[0];
120123

121-
bytes memory bundledReport = bundleReport(report);
124+
(, /* bytes32[3] reportContextData */ bytes memory reportData) = abi
125+
.decode(unverifiedReport, (bytes32[3], bytes));
122126

123-
BasicReport memory unverifiedReport = _getReportData(report);
127+
// Report verification fees
128+
IFeeManager feeManager = IFeeManager(address(verifier.s_feeManager()));
129+
IRewardManager rewardManager = IRewardManager(
130+
address(feeManager.i_rewardManager())
131+
);
124132

125-
bytes memory verifiedReportData = verifier.verify{
126-
value: unverifiedReport.nativeFee
127-
}(bundledReport);
128-
BasicReport memory verifiedReport = abi.decode(
129-
verifiedReportData,
130-
(BasicReport)
133+
address feeTokenAddress = feeManager.i_linkAddress();
134+
(Common.Asset memory fee, , ) = feeManager.getFeeAndReward(
135+
address(this),
136+
reportData,
137+
feeTokenAddress
131138
);
132139

133-
emit PriceUpdate(verifiedReport.price);
134-
}
140+
// Approve rewardManager to spend this contract's balance in fees
141+
IERC20(feeTokenAddress).approve(address(rewardManager), fee.amount);
135142

136-
function bundleReport(
137-
bytes memory report
138-
) internal view returns (bytes memory) {
139-
Quote memory quote;
140-
quote.quoteAddress = FEE_ADDRESS;
141-
(
142-
bytes32[3] memory reportContext,
143-
bytes memory reportData,
144-
bytes32[] memory rs,
145-
bytes32[] memory ss,
146-
bytes32 raw
147-
) = abi.decode(
148-
report,
149-
(bytes32[3], bytes, bytes32[], bytes32[], bytes32)
150-
);
151-
bytes memory bundledReport = abi.encode(
152-
reportContext,
153-
reportData,
154-
rs,
155-
ss,
156-
raw,
157-
abi.encode(quote)
143+
// Verify the report
144+
bytes memory verifiedReportData = verifier.verify(
145+
unverifiedReport,
146+
abi.encode(feeTokenAddress)
158147
);
159-
return bundledReport;
160-
}
161148

162-
function _getReportData(
163-
bytes memory signedReport
164-
) internal pure returns (BasicReport memory) {
165-
(, bytes memory reportData, , , ) = abi.decode(
166-
signedReport,
167-
(bytes32[3], bytes, bytes32[], bytes32[], bytes32)
149+
// Decode verified report data into BasicReport struct
150+
BasicReport memory verifiedReport = abi.decode(
151+
verifiedReportData,
152+
(BasicReport)
168153
);
169154

170-
BasicReport memory report = abi.decode(reportData, (BasicReport));
171-
return report;
155+
// Log price from report
156+
emit PriceUpdate(verifiedReport.price);
157+
158+
// Store the price from the report
159+
last_retrieved_price = verifiedReport.price;
172160
}
173161

174162
fallback() external payable {}

src/features/data-streams/common/gettingStarted.mdx

+15-21
Original file line numberDiff line numberDiff line change
@@ -26,22 +26,24 @@ Read data from a Data Stream and validate the answer on-chain. This example uses
2626

2727
## Deploy the Chainlink Automation upkeep
2828

29-
Deploy an upkeep that is enabled to retrieve data from Data Streams. For this example, you will read from the ETH/USD stream with ID `0x00023496426b520583ae20a66d80484e0fc18544866a5b0bfee15ec771963274` on Arbitrum Sepolia. For a complete list of available assets, IDs, and verifier proxy addresses, see the [Stream Identifiers](/data-streams/stream-ids) page.
29+
Deploy an upkeep that is enabled to retrieve data from Data Streams. For this example, you will read from the ETH/USD stream with ID `0x00027bbaff688c906a3e20a34fe951715d1018d262a5b66e38eda027a674cd1b` on Arbitrum Sepolia. For a complete list of available assets, IDs, and verifier proxy addresses, see the [Stream Identifiers](/data-streams/stream-ids) page.
3030

3131
1. Select the Arbitrum Sepolia network in MetaMask.
3232

3333
1. Open the example upkeep in Remix.
3434

3535
<CodeSample src="samples/DataStreams/StreamsUpkeep.sol" showButtonOnly />
3636

37-
1. Compile the contract.
37+
1. In the **Solidity Compiler** tab, select the `0.8.16` Solidity compiler and the `StreamsUpkeep` contract.
38+
39+
1. Compile the contract. You can ignore the warning messages for this example.
3840

3941
1. In the **Deploy & Run** tab, select **Injected Provider** as your Environment. For this example, you should be using Arbitrum Sepolia.
4042

4143
1. Deploy the contract with the following constructor variables:
4244

4345
- **FeeAddress (WETH)**: `0xe39Ab88f8A4777030A534146A9Ca3B52bd5D43A3`
44-
- **VerifierAddress**: `0xea9B98Be000FBEA7f6e88D08ebe70EbaAD10224c`
46+
- **VerifierAddress**: `0x2ff010DEbC1297f19579B4246cad07bd24F2488A`
4547

4648
1. Record the contract address.
4749

@@ -87,34 +89,26 @@ Now you can use your emitter contract to emit a log and initiate the upkeep, whi
8789

8890
1. Go to [sepolia.arbiscan.io](https://sepolia.arbiscan.io/).
8991

90-
1. Use the search to find your emitter contract using the address you saved earlier.
91-
92-
1. Click the **Contract** tab to view the contract.
93-
94-
1. Click the **Write Contract** button to find the `emitLog` function.
92+
1. Under the **Deployed Contracts** list, click the **emitLog** button to run the function and emit a log. MetaMask prompts you to accept the transaction.
9593

96-
1. Click **Connect to Web3** to connect your wallet to Arbiscan. This allows you to run write functions in Arbiscan. MetaMask will prompt you to accept the connection.
94+
After the transaction is complete, the log is emitted and the upkeep is triggered. You can find the upkeep transaction hash at [Chainlink Automation UI](https://automation.chain.link/arbitrum-sepolia). Check to make sure the transaction is successful.
9795

98-
1. Click the `emitLog` function to expand the function details.
96+
## View the retrieved price
9997

100-
1. Click the **Write** button to execute the function that will emit the log. MetaMask prompts you to accept the transaction.
98+
The retrieved price is stored as a variable in the contract and is also emitted in the logs.
10199

102-
After the transaction is complete, the log is emitted and the upkeep is triggered. You can see the retrieved data on your upkeep.
100+
1. In Remix, go to the **Deploy & Run** tab.
103101

104-
## View the upkeep
102+
1. Under the **Deployed Contracts**, find the deployed upkeep contract and view the variables.
105103

106-
View the upkeep and check the on-chain verification.
104+
1. Click the `last_retrieved_price` variable to view the retrieved price.
107105

108-
1. Go to the [Chainlink Automation UI](https://automation.chain.link/arbitrum-sepolia) for Arbitrum Sepolia.
109-
110-
1. On your list of upkeeps, click the upkeep that you crated for this guide. The upkeep details open with a list of upkeeps that were performed. You should see your log triggered upkeep on this list.
111-
112-
1. Click the transaction hash for the upkeep to view the transaction in Arbiscan.
113-
114-
1. On the **Logs** tab for your transaction, you will see that the `performUpkeep` function emitted the price from the verified report.
106+
Alternatively, you can view the price emitted in the logs for your upkeep transaction. You can find the upkeep transaction hash at [Chainlink Automation UI](https://automation.chain.link/arbitrum-sepolia) and view the logs in the explorer.
115107

116108
## Examine the code
117109

118110
The example code that you deployed has all of the interfaces and functions required to work with Chainlink Automation as an upkeep contract. It follows a similar flow to the trading flow in the [Architecture](/data-streams#example-trading-flow) documentation, but uses a basic log emitter to simulate the client contract that would initiate a `StreamsLookup`. After the contract receives and verifies the report, `performUpkeep` emits a `PriceUpdate` log message with the price. You could modify this to use the data in a way that works for your specific use case and application.
119111

112+
The code example uses `revert` with `StreamsLookup` to convey call information about what streams to retrieve. See the [eip-3668 rationale](https://eips.ethereum.org/EIPS/eip-3668#rationale) for more information on the use of `revert` in this way.
113+
120114
<CodeSample src="samples/DataStreams/StreamsUpkeep.sol" />

src/features/feeds/components/Tables.tsx

+2-8
Original file line numberDiff line numberDiff line change
@@ -353,13 +353,7 @@ const StreamsTr = ({ network, proxy, showExtraDetails }) => (
353353
<td style="width:80%;">
354354
<div className={tableStyles.assetAddress}>
355355
<span class="label">ID:</span>
356-
<a
357-
style="font-size: 0.75em;"
358-
class={tableStyles.addressLink}
359-
href={network.explorerUrl.replace("%s", proxy.feedId)}
360-
>
361-
{proxy.feedId}
362-
</a>
356+
{proxy.feedId}
363357
<button
364358
class={clsx(tableStyles.copyBtn, "copy-iconbutton")}
365359
style={{ height: "16px", width: "16px" }}
@@ -373,7 +367,7 @@ const StreamsTr = ({ network, proxy, showExtraDetails }) => (
373367
<a
374368
style="font-size: 0.75em;"
375369
class={tableStyles.addressLink}
376-
href={network.explorerUrl.replace("%s", proxy.proxyAddress)}
370+
href={network.explorerUrl.replace("%s", "0xea9B98Be000FBEA7f6e88D08ebe70EbaAD10224c")}
377371
>
378372
0x2ff010DEbC1297f19579B4246cad07bd24F2488A
379373
</a>

0 commit comments

Comments
 (0)