From 55e6868b18934be3d87cf0a030e8d57e4485ab1e Mon Sep 17 00:00:00 2001 From: devin distefano Date: Thu, 23 Jul 2026 10:23:56 -0500 Subject: [PATCH 1/2] remove verifier fee from samples and warn in schema --- .../DataStreams/ClientReportsVerifier.sol | 119 +---- public/samples/DataStreams/StreamsUpkeep.sol | 66 +-- .../DataStreams/StreamsUpkeepRegistrar.sol | 49 +- .../StreamsUpkeepWithErrorHandler.sol | 64 +-- src/content/data-streams/llms-full.txt | 447 ++++++------------ .../data-streams-api/onchain-verification.mdx | 54 +-- .../reference/data-streams-api/ts-sdk.mdx | 2 + .../reference/report-schema-v4.mdx | 4 +- .../evm-onchain-report-verification.mdx | 44 +- .../stellar-onchain-report-verification.mdx | 2 + .../data-streams/common/gettingStarted.mdx | 1 - .../common/gettingStartedHardhat.mdx | 9 +- .../feeds/components/reportSchemaData.ts | 8 +- 13 files changed, 228 insertions(+), 641 deletions(-) diff --git a/public/samples/DataStreams/ClientReportsVerifier.sol b/public/samples/DataStreams/ClientReportsVerifier.sol index a854c6ecd59..ae08efcfd2f 100644 --- a/public/samples/DataStreams/ClientReportsVerifier.sol +++ b/public/samples/DataStreams/ClientReportsVerifier.sol @@ -1,8 +1,6 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.20; -import {Common} from "@chainlink/contracts/src/v0.8/llo-feeds/libraries/Common.sol"; -import {IVerifierFeeManager} from "@chainlink/contracts/src/v0.8/llo-feeds/v0.3.0/interfaces/IVerifierFeeManager.sol"; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; @@ -12,35 +10,26 @@ using SafeERC20 for IERC20; * THIS IS AN EXAMPLE CONTRACT THAT USES UN-AUDITED CODE FOR DEMONSTRATION PURPOSES. * DO NOT USE THIS CODE IN PRODUCTION. * - * This contract verifies Chainlink Data Streams reports onchain and pays - * the verification fee in LINK (when required). It exposes two verification - * functions: + * This contract verifies Chainlink Data Streams reports onchain. It exposes + * two verification functions: * * - `verifyReport()` – verifies a single report payload. * - `verifyBulkReports()` – verifies multiple payloads (across any feed IDs) * in a single transaction using `verifyBulk()`. * - * - If `VerifierProxy.s_feeManager()` returns a non-zero address, the network - * expects you to interact with that FeeManager for every verification call: - * quote fees, approve the RewardManager, then call `verify()` / `verifyBulk()`. - * - * - If `s_feeManager()` returns the zero address, no FeeManager contract has - * been deployed on that chain. In that case there is nothing to quote or pay - * onchain, so the contract skips the fee logic entirely. - * - * The `if (address(feeManager) != address(0))` check below chooses the - * correct path automatically, making the same bytecode usable on any chain. + * Data Streams uses subscription-based billing. Verification calls do not + * require this contract to hold LINK or approve a FeeManager. */ // ──────────────────────────────────────────────────────────────────────────── -// Interfaces +// Interface // ──────────────────────────────────────────────────────────────────────────── interface IVerifierProxy { /** - * @notice Route a report to the correct verifier and (optionally) bill fees. + * @notice Route a report to the correct verifier. * @param payload Full report payload (header + signed report). - * @param parameterPayload ABI-encoded fee metadata. + * @param parameterPayload Empty bytes for Data Streams subscription billing. */ function verify( bytes calldata payload, @@ -51,33 +40,13 @@ interface IVerifierProxy { * @notice Route multiple reports to the correct verifier in a single call. * @param payloads Array of full report payloads. Each entry may reference * a different feed ID. Order is preserved in the output. - * @param parameterPayload ABI-encoded fee token address shared across all reports - * (or empty bytes if no FeeManager is deployed). + * @param parameterPayload Empty bytes for Data Streams subscription billing. * @return verifiedReports Verified report bytes in the same order as the input. */ function verifyBulk( bytes[] calldata payloads, bytes calldata parameterPayload ) external payable returns (bytes[] memory verifiedReports); - - function s_feeManager() external view returns (IVerifierFeeManager); -} - -interface IFeeManager { - /** - * @return fee, reward, totalDiscount - */ - 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); } // ──────────────────────────────────────────────────────────────────────────── @@ -86,7 +55,7 @@ interface IFeeManager { /** * @dev This contract implements functionality to verify Data Streams reports from - * the Data Streams API, with payment in LINK tokens. + * the Data Streams API. */ contract ClientReportsVerifier { // ----------------- Errors ----------------- @@ -164,13 +133,8 @@ contract ClientReportsVerifier { * 1. Decode the unverified report to get `reportData`. * 2. Read the first two bytes → schema version (`0x0003` or `0x0008`). * - Revert if the version is unsupported. - * 3. Fee handling: - * - Query `s_feeManager()` on the proxy. - * – Non-zero → quote the fee, approve the RewardManager, - * ABI-encode the fee token address for `verify()`. - * – Zero → no FeeManager; skip quoting/approval and pass `""`. - * 4. Call `VerifierProxy.verify()`. - * 5. Decode the verified report into the correct struct and emit the price. + * 3. Call `VerifierProxy.verify()` with an empty parameter payload. + * 4. Decode the verified report into the correct struct and emit the price. * * @param unverifiedReport Full payload returned by Streams Direct. * @custom:reverts InvalidReportVersion when schema ≠ v3/v8. @@ -186,27 +150,10 @@ contract ClientReportsVerifier { revert InvalidReportVersion(reportVersion); } - // ─── 3. Fee handling ── - IFeeManager feeManager = IFeeManager(address(i_verifierProxy.s_feeManager())); - - bytes memory parameterPayload; - if (address(feeManager) != address(0)) { - // FeeManager exists — always quote & approve - address feeToken = feeManager.i_linkAddress(); - - (Common.Asset memory fee,,) = feeManager.getFeeAndReward(address(this), reportData, feeToken); + // ─── 3. Verify through the proxy ── + bytes memory verified = i_verifierProxy.verify(unverifiedReport, bytes("")); - IERC20(feeToken).approve(feeManager.i_rewardManager(), fee.amount); - parameterPayload = abi.encode(feeToken); - } else { - // No FeeManager deployed on this chain - parameterPayload = bytes(""); - } - - // ─── 4. Verify through the proxy ── - bytes memory verified = i_verifierProxy.verify(unverifiedReport, parameterPayload); - - // ─── 5. Decode & store price ── + // ─── 4. Decode & store price ── if (reportVersion == 3) { int192 price = abi.decode(verified, (ReportV3)).price; lastDecodedPrice = price; @@ -224,13 +171,9 @@ contract ClientReportsVerifier { * @dev Steps: * 1. Decode each payload to extract `reportData` and the schema version. * - Revert if any version is unsupported. - * 2. Fee handling (when FeeManager is deployed): - * - Quote the fee for each report individually via `getFeeAndReward`. - * - Accumulate the total fee across all reports. - * - Approve the RewardManager once for the combined total. - * - ABI-encode the fee token address for `verifyBulk()`. - * 3. Call `VerifierProxy.verifyBulk()` once with all payloads. - * 4. Decode each verified report, store prices in `lastDecodedPrices`, + * 2. Call `VerifierProxy.verifyBulk()` once with all payloads and an empty + * parameter payload. + * 3. Decode each verified report, store prices in `lastDecodedPrices`, * and emit a `DecodedPrice` event per report. * * @param unverifiedReports Array of full payloads from Streams Direct. @@ -259,32 +202,10 @@ contract ClientReportsVerifier { reportVersions[i] = reportVersion; } - // ─── 2. Fee handling ── - IFeeManager feeManager = IFeeManager(address(i_verifierProxy.s_feeManager())); - - bytes memory parameterPayload; - if (address(feeManager) != address(0)) { - address feeToken = feeManager.i_linkAddress(); - uint256 totalFee = 0; - - // Quote per-report fees and accumulate - for (uint256 i = 0; i < reportDataArray.length; i++) { - (Common.Asset memory fee,,) = feeManager.getFeeAndReward(address(this), reportDataArray[i], feeToken); - totalFee += fee.amount; - } - - // Single approval covers the combined cost of all reports - IERC20(feeToken).approve(feeManager.i_rewardManager(), totalFee); - parameterPayload = abi.encode(feeToken); - } else { - // No FeeManager deployed on this chain - parameterPayload = bytes(""); - } - - // ─── 3. Verify all reports in one proxy call ── - bytes[] memory verifiedReports = i_verifierProxy.verifyBulk(unverifiedReports, parameterPayload); + // ─── 2. Verify all reports in one proxy call ── + bytes[] memory verifiedReports = i_verifierProxy.verifyBulk(unverifiedReports, bytes("")); - // ─── 4. Decode verified reports, store prices, emit events ── + // ─── 3. Decode verified reports, store prices, emit events ── int192[] memory prices = new int192[](verifiedReports.length); for (uint256 i = 0; i < verifiedReports.length; i++) { diff --git a/public/samples/DataStreams/StreamsUpkeep.sol b/public/samples/DataStreams/StreamsUpkeep.sol index e8ca23d4f39..ebc65555343 100644 --- a/public/samples/DataStreams/StreamsUpkeep.sol +++ b/public/samples/DataStreams/StreamsUpkeep.sol @@ -5,61 +5,26 @@ import {ILogAutomation, Log} from "@chainlink/contracts/src/v0.8/automation/inte import { StreamsLookupCompatibleInterface } from "@chainlink/contracts/src/v0.8/automation/interfaces/StreamsLookupCompatibleInterface.sol"; -import {Common} from "@chainlink/contracts/src/v0.8/llo-feeds/libraries/Common.sol"; - -import {IRewardManager} from "@chainlink/contracts/src/v0.8/llo-feeds/v0.3.0/interfaces/IRewardManager.sol"; -import {IVerifierFeeManager} from "@chainlink/contracts/src/v0.8/llo-feeds/v0.3.0/interfaces/IVerifierFeeManager.sol"; -import {IERC20} from "@openzeppelin/contracts/token/ERC20/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 +// Custom interface for IVerifierProxy interface IVerifierProxy { /** * @notice Verifies that the data encoded has been signed. - * correctly by routing to the correct verifier, and bills the user if applicable. + * correctly by routing to the correct verifier. * @param payload The encoded data to be verified, including the signed * report. - * @param parameterPayload Fee metadata for billing. For the current implementation this is just the abi-encoded fee - * token ERC-20 address. + * @param parameterPayload Empty bytes for Data Streams subscription billing. * @return verifierResponse The encoded report from the verifier. */ function verify( bytes calldata payload, bytes calldata parameterPayload ) external payable returns (bytes memory verifierResponse); - - function s_feeManager() external view returns (IVerifierFeeManager); -} - -interface IFeeManager { - /** - * @notice Calculates the fee and reward associated with verifying a report, including discounts for subscribers. - * This function assesses the fee and reward for report verification, applying a discount for recognized subscriber - * addresses. - * @param subscriber The address attempting to verify the report. A discount is applied if this address - * is recognized as a subscriber. - * @param unverifiedReport The report data awaiting verification. The content of this report is used to - * determine the base fee and reward, before considering subscriber discounts. - * @param quoteAddress The payment token address used for quoting fees and rewards. - * @return fee The fee assessed for verifying the report, with subscriber discounts applied where applicable. - * @return reward The reward allocated to the caller for successfully verifying the report. - * @return totalDiscount The total discount amount deducted from the fee for subscribers - */ - 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 { @@ -77,9 +42,8 @@ contract StreamsUpkeep is ILogAutomation, StreamsLookupCompatibleInterface { bytes32 feedId; // The stream 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 (e.g., WETH/ETH). - uint192 linkFee; // Base cost to validate a transaction using the report, denominated in LINK. + uint192 nativeFee; // Legacy onchain verification fee field. + uint192 linkFee; // Legacy onchain verification fee field. Not used for subscription billing. uint32 expiresAt; // Latest timestamp where the report can be verified onchain. int192 price; // DON consensus median price (8 or 18 decimals). int192 bid; // Simulated price impact of a buy order up to the X% depth of liquidity utilisation (8 or 18 decimals). @@ -100,9 +64,8 @@ contract StreamsUpkeep is ILogAutomation, StreamsLookupCompatibleInterface { bytes32 feedId; // The stream 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 (e.g., WETH/ETH). - uint192 linkFee; // Base cost to validate a transaction using the report, denominated in LINK. + uint192 nativeFee; // Legacy onchain verification fee field. + uint192 linkFee; // Legacy onchain verification fee field. Not used for subscription billing. uint32 expiresAt; // Latest timestamp where the report can be verified onchain. int192 price; // DON consensus median benchmark price (8 or 18 decimals). uint32 marketStatus; // The DON's consensus on whether the market is currently open. @@ -114,7 +77,6 @@ contract StreamsUpkeep is ILogAutomation, StreamsLookupCompatibleInterface { IVerifierProxy public verifier; - address public FEE_ADDRESS; string public constant DATASTREAMS_FEEDLABEL = "feedIDs"; string public constant DATASTREAMS_QUERYLABEL = "timestamp"; int192 public lastDecodedPrice; @@ -186,18 +148,8 @@ contract StreamsUpkeep is ILogAutomation, StreamsLookupCompatibleInterface { revert InvalidReportVersion(uint8(reportVersion)); } - // 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)); + // Verify the report. Data Streams uses subscription billing, so no fee metadata is required. + bytes memory verifiedReportData = verifier.verify(unverifiedReport, bytes("")); // Decode verified report data into the appropriate Report struct based on reportVersion if (reportVersion == 3) { diff --git a/public/samples/DataStreams/StreamsUpkeepRegistrar.sol b/public/samples/DataStreams/StreamsUpkeepRegistrar.sol index 905b8109394..05a271c2d2f 100644 --- a/public/samples/DataStreams/StreamsUpkeepRegistrar.sol +++ b/public/samples/DataStreams/StreamsUpkeepRegistrar.sol @@ -5,13 +5,7 @@ import {ILogAutomation, Log} from "@chainlink/contracts/src/v0.8/automation/inte import { StreamsLookupCompatibleInterface } from "@chainlink/contracts/src/v0.8/automation/interfaces/StreamsLookupCompatibleInterface.sol"; -import {Common} from "@chainlink/contracts/src/v0.8/llo-feeds/libraries/Common.sol"; - -import {IRewardManager} from "@chainlink/contracts/src/v0.8/llo-feeds/v0.3.0/interfaces/IRewardManager.sol"; -import {IVerifierFeeManager} from "@chainlink/contracts/src/v0.8/llo-feeds/v0.3.0/interfaces/IVerifierFeeManager.sol"; - import {LinkTokenInterface} from "@chainlink/contracts/src/v0.8/shared/interfaces/LinkTokenInterface.sol"; -import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; /** * THIS IS AN EXAMPLE CONTRACT THAT USES UN-AUDITED CODE FOR DEMONSTRATION PURPOSES. @@ -58,28 +52,12 @@ interface AutomationRegistrarInterface { ) external returns (uint256); } -// Custom interfaces for Data Streams: IVerifierProxy and IFeeManager +// Custom interface for Data Streams: IVerifierProxy 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 StreamsUpkeepRegistrar is ILogAutomation, StreamsLookupCompatibleInterface { @@ -99,9 +77,8 @@ contract StreamsUpkeepRegistrar is ILogAutomation, StreamsLookupCompatibleInterf 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 (e.g., WETH/ETH). - uint192 linkFee; // Base cost to validate a transaction using the report, denominated in LINK. + uint192 nativeFee; // Legacy onchain verification fee field. + uint192 linkFee; // Legacy onchain verification fee field. Not used for subscription billing. uint32 expiresAt; // Latest timestamp where the report can be verified onchain. int192 price; // DON consensus median price (8 or 18 decimals). int192 bid; // Simulated price impact of a buy order up to the X% depth of liquidity utilisation (8 or 18 decimals). @@ -122,9 +99,8 @@ contract StreamsUpkeepRegistrar is ILogAutomation, StreamsLookupCompatibleInterf 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 (e.g., WETH/ETH). - uint192 linkFee; // Base cost to validate a transaction using the report, denominated in LINK. + uint192 nativeFee; // Legacy onchain verification fee field. + uint192 linkFee; // Legacy onchain verification fee field. Not used for subscription billing. uint32 expiresAt; // Latest timestamp where the report can be verified onchain. int192 price; // DON consensus median benchmark price (8 or 18 decimals). uint32 marketStatus; // The DON's consensus on whether the market is currently open. @@ -138,7 +114,6 @@ contract StreamsUpkeepRegistrar is ILogAutomation, StreamsLookupCompatibleInterf IVerifierProxy public verifier; - address public FEE_ADDRESS; string public constant DATASTREAMS_FEEDLABEL = "feedIDs"; string public constant DATASTREAMS_QUERYLABEL = "timestamp"; int192 public lastDecodedPrice; @@ -238,18 +213,8 @@ contract StreamsUpkeepRegistrar is ILogAutomation, StreamsLookupCompatibleInterf revert InvalidReportVersion(uint8(reportVersion)); } - // 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)); + // Verify the report. Data Streams uses subscription billing, so no fee metadata is required. + bytes memory verifiedReportData = verifier.verify(unverifiedReport, bytes("")); // Decode verified report data into the appropriate Report struct based on reportVersion if (reportVersion == 3) { diff --git a/public/samples/DataStreams/StreamsUpkeepWithErrorHandler.sol b/public/samples/DataStreams/StreamsUpkeepWithErrorHandler.sol index 825ce30c3cc..bf94dd66e1c 100644 --- a/public/samples/DataStreams/StreamsUpkeepWithErrorHandler.sol +++ b/public/samples/DataStreams/StreamsUpkeepWithErrorHandler.sol @@ -6,11 +6,6 @@ import { StreamsLookupCompatibleInterface } from "@chainlink/contracts/src/v0.8/automation/interfaces/StreamsLookupCompatibleInterface.sol"; -import {Common} from "@chainlink/contracts/src/v0.8/llo-feeds/libraries/Common.sol"; -import {IRewardManager} from "@chainlink/contracts/src/v0.8/llo-feeds/v0.3.0/interfaces/IRewardManager.sol"; -import {IVerifierFeeManager} from "@chainlink/contracts/src/v0.8/llo-feeds/v0.3.0/interfaces/IVerifierFeeManager.sol"; -import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; - /** * THIS IS AN EXAMPLE CONTRACT THAT USES UN-AUDITED CODE. * DO NOT USE THIS CODE IN PRODUCTION. @@ -20,49 +15,19 @@ import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; // INTERFACES // ===================== -interface IFeeManager { - /** - * @notice Calculates the fee and reward associated with verifying a report, including discounts for subscribers. - * This function assesses the fee and reward for report verification, applying a discount for recognized subscriber - * addresses. - * @param subscriber The address attempting to verify the report. A discount is applied if this address - * is recognized as a subscriber. - * @param unverifiedReport The report data awaiting verification. The content of this report is used to - * determine the base fee and reward, before considering subscriber discounts. - * @param quoteAddress The payment token address used for quoting fees and rewards. - * @return fee The fee assessed for verifying the report, with subscriber discounts applied where applicable. - * @return reward The reward allocated to the caller for successfully verifying the report. - * @return totalDiscount The total discount amount deducted from the fee for subscribers. - */ - 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); -} - interface IVerifierProxy { /** * @notice Verifies that the data encoded has been signed. - * correctly by routing to the correct verifier, and bills the user if applicable. + * correctly by routing to the correct verifier. * @param payload The encoded data to be verified, including the signed * report. - * @param parameterPayload Fee metadata for billing. In the current implementation, - * this consists of the abi-encoded address of the ERC-20 token used for fees. + * @param parameterPayload Empty bytes for Data Streams subscription billing. * @return verifierResponse The encoded report from the verifier. */ function verify( bytes calldata payload, bytes calldata parameterPayload ) external payable returns (bytes memory verifierResponse); - - function s_feeManager() external view returns (IVerifierFeeManager); } // ========================== @@ -82,9 +47,8 @@ contract StreamsUpkeepWithErrorHandler is ILogAutomation, StreamsLookupCompatibl 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 (e.g., WETH/ETH). - uint192 linkFee; // Base cost to validate a transaction using the report, denominated in LINK. + uint192 nativeFee; // Legacy onchain verification fee field. + uint192 linkFee; // Legacy onchain verification fee field. Not used for subscription billing. uint32 expiresAt; // Latest timestamp where the report can be verified onchain. int192 price; // DON consensus median price (8 or 18 decimals). int192 bid; // Simulated price impact of a buy order up to the X% depth of liquidity utilisation (8 or 18 decimals). @@ -104,9 +68,8 @@ contract StreamsUpkeepWithErrorHandler is ILogAutomation, StreamsLookupCompatibl 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 (e.g., WETH/ETH). - uint192 linkFee; // Base cost to validate a transaction using the report, denominated in LINK. + uint192 nativeFee; // Legacy onchain verification fee field. + uint192 linkFee; // Legacy onchain verification fee field. Not used for subscription billing. uint32 expiresAt; // Latest timestamp where the report can be verified onchain. int192 price; // DON consensus median benchmark price (8 or 18 decimals). uint32 marketStatus; // The DON's consensus on whether the market is currently open. @@ -121,7 +84,6 @@ contract StreamsUpkeepWithErrorHandler is ILogAutomation, StreamsLookupCompatibl IVerifierProxy public verifier; - address public FEE_ADDRESS; string public constant STRING_DATASTREAMS_FEEDLABEL = "feedIDs"; string public constant STRING_DATASTREAMS_QUERYLABEL = "timestamp"; uint256 public s_error; @@ -221,18 +183,8 @@ contract StreamsUpkeepWithErrorHandler is ILogAutomation, StreamsLookupCompatibl revert InvalidReportVersion(uint8(reportVersion)); } - // 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)); + // Verify the report. Data Streams uses subscription billing, so no fee metadata is required. + bytes memory verifiedReportData = verifier.verify(unverifiedReport, bytes("")); // Decode verified report data into the appropriate Report struct based on reportVersion if (reportVersion == 3) { diff --git a/src/content/data-streams/llms-full.txt b/src/content/data-streams/llms-full.txt index 24de670fa17..902d7b6b627 100644 --- a/src/content/data-streams/llms-full.txt +++ b/src/content/data-streams/llms-full.txt @@ -4553,7 +4553,7 @@ The `IVerifierProxy` interface exposes two verification functions: ## IVerifierProxy interface -`IVerifierProxy` is the single onchain entry point for report verification. Your contract calls it directly — you never interact with the underlying Verifier contract. The proxy routes each call to the correct Verifier, handles fee collection through the `FeeManager` (when one is deployed), and returns the verified report data. +`IVerifierProxy` is the single onchain entry point for report verification. Your contract calls it directly — you never interact with the underlying Verifier contract. The proxy routes each call to the correct Verifier and returns the verified report data. You will need the deployed `VerifierProxy` address for the network you are targeting. Find it on the [Stream Addresses](/data-streams/crypto-streams) page. @@ -4563,30 +4563,30 @@ interface IVerifierProxy { // Decode the return value into the appropriate report struct (v3, v8, etc.). function verify( bytes calldata payload, // Full report payload from the Streams API - bytes calldata parameterPayload // abi.encode(feeTokenAddress), or "" if no FeeManager + bytes calldata parameterPayload // pass "" for Data Streams subscription billing ) external payable returns (bytes memory verifierResponse); // Verify multiple reports in one transaction. Accepts different feed IDs in the same call. // Returns verified report bytes in the same order as the input array. function verifyBulk( bytes[] calldata payloads, // Array of report payloads — may span different feed IDs - bytes calldata parameterPayload // abi.encode(feeTokenAddress), or "" if no FeeManager + bytes calldata parameterPayload // pass "" for Data Streams subscription billing ) external payable returns (bytes[] memory verifiedReports); - // Returns the FeeManager address, or the zero address if no FeeManager is deployed on this chain. - // Use this to determine whether fee quoting and approval steps are required before verifying. + // Legacy FeeManager accessor. Data Streams verification does not require + // per-verification fee quoting or approvals. function s_feeManager() external view returns (IVerifierFeeManager); } ``` ### `verify()` -Verifies a single report payload and optionally collects the verification fee. Pass the raw payload bytes returned by the Streams API directly as `payload` — no transformation is needed. +Verifies a single report payload. Pass the raw payload bytes returned by the Streams API directly as `payload` — no transformation is needed. -| Parameter | Type | Description | -| ------------------ | ---------------- | ------------------------------------------------------------------------------------------------------------------- | -| `payload` | `bytes calldata` | The full report payload returned by the Streams API (header + signed report body). | -| `parameterPayload` | `bytes calldata` | ABI-encoded fee token address (e.g. `abi.encode(linkTokenAddress)`), or empty bytes `""` when no FeeManager exists. | +| Parameter | Type | Description | +| ------------------ | ---------------- | ---------------------------------------------------------------------------------- | +| `payload` | `bytes calldata` | The full report payload returned by the Streams API (header + signed report body). | +| `parameterPayload` | `bytes calldata` | Empty bytes `""` for Data Streams subscription billing. | #### Returns @@ -4596,21 +4596,16 @@ Verifies a single report payload and optionally collects the verification fee. P #### Fee handling for `verify()` -Before calling `verify()`, check whether a `FeeManager` is deployed on the target network by calling `s_feeManager()` on the proxy. - -- **If `s_feeManager()` returns a non-zero address**: quote the fee using `getFeeAndReward`, approve the `RewardManager` to spend that LINK amount, then pass `abi.encode(feeTokenAddress)` as `parameterPayload`. -- **If `s_feeManager()` returns the zero address**: no fee is required. Pass empty bytes `""` as `parameterPayload`. - -Networks with a `FeeManager` currently deployed include: Arbitrum, Avalanche, Base, Blast, Bob, Ink, Linea, OP, Scroll, Soneium, and ZKSync. +Data Streams uses subscription-based billing. Your contract does not need to quote or approve a per-verification fee before calling `verify()`, and no supported EVM chain requires LINK funding for this step. Pass empty bytes `""` as `parameterPayload`. ### `verifyBulk()` Verifies multiple report payloads in a single transaction. Reports may reference different feed IDs, enabling atomic multi-feed updates. Pass the raw payload bytes from the Streams API directly — no transformation is needed. -| Parameter | Type | Description | -| ------------------ | ------------------ | --------------------------------------------------------------------------------------------------------------------------------------------- | -| `payloads` | `bytes[] calldata` | Array of full report payloads from the Streams API. Each entry may correspond to a different feed ID. Order is preserved in the output. | -| `parameterPayload` | `bytes calldata` | ABI-encoded fee token address shared across all reports (e.g. `abi.encode(linkTokenAddress)`), or empty bytes `""` when no FeeManager exists. | +| Parameter | Type | Description | +| ------------------ | ------------------ | --------------------------------------------------------------------------------------------------------------------------------------- | +| `payloads` | `bytes[] calldata` | Array of full report payloads from the Streams API. Each entry may correspond to a different feed ID. Order is preserved in the output. | +| `parameterPayload` | `bytes calldata` | Empty bytes `""` for Data Streams subscription billing. | #### Returns @@ -4620,14 +4615,7 @@ Verifies multiple report payloads in a single transaction. Reports may reference #### Fee handling for `verifyBulk()` -The same `FeeManager` check applies. When a `FeeManager` is deployed, the total fee equals the sum of individual fees across all reports in the batch. Your contract must: - -1. Decode each payload and call `getFeeAndReward` once per report to quote its fee. -2. Sum the individual fees to get the total amount required. -3. Approve the `RewardManager` contract to spend the total LINK amount in a single `approve()` call. -4. Pass the ABI-encoded fee token address as `parameterPayload`. - -When no `FeeManager` is deployed, pass empty bytes as `parameterPayload` and skip all fee logic. +Data Streams uses subscription-based billing. Your contract does not need to quote, sum, or approve per-report verification fees before calling `verifyBulk()`. Pass empty bytes `""` as `parameterPayload`. ## When to use `verifyBulk()` @@ -4645,20 +4633,18 @@ Use `verifyBulk()` when your protocol needs price data from multiple feeds in th - **What you save**: one base transaction cost (21,000 gas) per additional report beyond the first. - **What you do not save**: the cryptographic verification cost per report. -- **Fee cost**: verification fees (when applicable) are the same per report regardless of whether you use `verify()` or `verifyBulk()`. +- **Fee cost**: Data Streams does not charge a per-report onchain verification fee. ## Contract example -The contract below is an **example you deploy yourself**. It demonstrates the full verification pattern for both `verifyReport()` (single report) and `verifyBulkReports()` (multiple reports), including fee handling. Use it as a reference when writing your own contract. - -Your contract must have sufficient LINK tokens to pay for verification fees on networks where fees are required. Learn how to [fund your contract with LINK tokens](/resources/fund-your-contract). +The contract below is an **example you deploy yourself**. It demonstrates the full verification pattern for both `verifyReport()` (single report) and `verifyBulkReports()` (multiple reports). Use it as a reference when writing your own contract. ### `verifyReport()` — single report - **Input**: one `bytes` payload from the Streams API — pass it directly. -- **Fee**: checks for a `FeeManager`, quotes the fee if one exists, and approves that exact amount before calling `verify()`. +- **Fee**: passes empty bytes for `parameterPayload`; no per-verification LINK fee is required. - **Output**: stores the decoded price in `lastDecodedPrice` and emits `DecodedPrice(int192 price)`. See the [Verify report data onchain (EVM)](/data-streams/tutorials/evm-onchain-report-verification) tutorial for a step-by-step walkthrough using this function. @@ -4666,7 +4652,7 @@ See the [Verify report data onchain (EVM)](/data-streams/tutorials/evm-onchain-r ### `verifyBulkReports()` — multiple reports - **Input**: `bytes[]` array of payloads from the Streams API — each may reference a different feed ID. -- **Fee**: calls `getFeeAndReward` once per report, sums the results, and issues a single `approve()` for the combined total before calling `verifyBulk()`. +- **Fee**: passes empty bytes for `parameterPayload`; no per-verification LINK fee is required. - **Output**: stores the decoded prices in `lastDecodedPrices` (an `int192[]` array in the same order as the inputs) and emits `DecodedPrice(int192 price)` once per report. --- @@ -5179,6 +5165,8 @@ interface BaseFields { } ``` +`nativeFee` and `linkFee` are legacy onchain verification fee fields in the report schema. Data Streams uses subscription-based billing, so these fields are not used to charge per-verification fees. + ### Schema-Specific Fields - **V2/V3/V4**: `price: bigint` - Standard price data @@ -5633,8 +5621,8 @@ Chainlink Tokenized Asset Data Streams adhere to the report schema outlined belo | `feedId` | `bytes32` | Unique identifier for the Data Streams feed | | `validFromTimestamp` | `uint32` | Earliest timestamp when the price is valid (seconds) | | `observationsTimestamp` | `uint32` | Latest timestamp when the price is valid (seconds) | -| `nativeFee` | `uint192` | Cost to verify report onchain (native token) | -| `linkFee` | `uint192` | Cost to verify report onchain (LINK) | +| `nativeFee` | `uint192` | Legacy onchain verification fee field | +| `linkFee` | `uint192` | Legacy onchain verification fee field; not used for subscription billing | | `expiresAt` | `uint32` | Expiration date of the report (seconds) | | `lastUpdateTimestamp` | `uint64` | Timestamp of the last valid price update (nanoseconds) | | `price` | `int192` | Last traded price from the real-world equity market | @@ -5674,22 +5662,22 @@ Chainlink Data Streams that use the RWA Advanced (v11) schema adhere to the stru ## Schema Fields -| Field | Type | Description | -| ----------------------- | --------- | ----------------------------------------------------------------- | -| `feedId` | `bytes32` | Unique identifier for the Data Streams feed | -| `validFromTimestamp` | `uint32` | Earliest timestamp when the price is valid (seconds) | -| `observationsTimestamp` | `uint32` | Latest timestamp when the price is valid (seconds) | -| `nativeFee` | `uint192` | Cost to verify report onchain (native token) | -| `linkFee` | `uint192` | Cost to verify report onchain (LINK) | -| `expiresAt` | `uint32` | Expiration date of the report (seconds) | -| `mid` | `int192` | DON consensus mid-price | -| `lastSeenTimestampNs` | `uint64` | Timestamp of the last update for the mid price only (nanoseconds) | -| `bid` | `int192` | Median bid price | -| `bidVolume` | `int192` | Volume at bid price | -| `ask` | `int192` | Median ask price | -| `askVolume` | `int192` | Volume at ask price | -| `lastTradedPrice` | `int192` | Last traded price | -| `marketStatus` | `uint32` | Market status. Mapping varies by feed; see schema docs. | +| Field | Type | Description | +| ----------------------- | --------- | ------------------------------------------------------------------------ | +| `feedId` | `bytes32` | Unique identifier for the Data Streams feed | +| `validFromTimestamp` | `uint32` | Earliest timestamp when the price is valid (seconds) | +| `observationsTimestamp` | `uint32` | Latest timestamp when the price is valid (seconds) | +| `nativeFee` | `uint192` | Legacy onchain verification fee field | +| `linkFee` | `uint192` | Legacy onchain verification fee field; not used for subscription billing | +| `expiresAt` | `uint32` | Expiration date of the report (seconds) | +| `mid` | `int192` | DON consensus mid-price | +| `lastSeenTimestampNs` | `uint64` | Timestamp of the last update for the mid price only (nanoseconds) | +| `bid` | `int192` | Median bid price | +| `bidVolume` | `int192` | Volume at bid price | +| `ask` | `int192` | Median ask price | +| `askVolume` | `int192` | Volume at ask price | +| `lastTradedPrice` | `int192` | Last traded price | +| `marketStatus` | `uint32` | Market status. Mapping varies by feed; see schema docs. | ### Market Status Values @@ -5767,17 +5755,17 @@ DEX State Price streams adhere to the report schema outlined below. ## Schema Fields -| Field | Type | Description | -| ----------------------- | --------- | ---------------------------------------------------- | -| `feedId` | `bytes32` | Unique identifier for the Data Streams feed | -| `validFromTimestamp` | `uint32` | Earliest timestamp when the price is valid (seconds) | -| `observationsTimestamp` | `uint32` | Latest timestamp when the price is valid (seconds) | -| `nativeFee` | `uint192` | Cost to verify report onchain (native token) | -| `linkFee` | `uint192` | Cost to verify report onchain (LINK) | -| `expiresAt` | `uint32` | Expiration date of the report (seconds) | -| `price` | `int192` | DON consensus median DEX state price | -| `bid` | `int192` | N/A — equals price | -| `ask` | `int192` | N/A — equals price | +| Field | Type | Description | +| ----------------------- | --------- | ------------------------------------------------------------------------ | +| `feedId` | `bytes32` | Unique identifier for the Data Streams feed | +| `validFromTimestamp` | `uint32` | Earliest timestamp when the price is valid (seconds) | +| `observationsTimestamp` | `uint32` | Latest timestamp when the price is valid (seconds) | +| `nativeFee` | `uint192` | Legacy onchain verification fee field | +| `linkFee` | `uint192` | Legacy onchain verification fee field; not used for subscription billing | +| `expiresAt` | `uint32` | Expiration date of the report (seconds) | +| `price` | `int192` | DON consensus median DEX state price | +| `bid` | `int192` | N/A — equals price | +| `ask` | `int192` | N/A — equals price | **Notes**: @@ -5795,17 +5783,17 @@ Cryptocurrency streams adhere to the report schema outlined below. ## Schema Fields -| Field | Type | Description | -| ----------------------- | --------- | ---------------------------------------------------- | -| `feedId` | `bytes32` | Unique identifier for the Data Streams feed | -| `validFromTimestamp` | `uint32` | Earliest timestamp when the price is valid (seconds) | -| `observationsTimestamp` | `uint32` | Latest timestamp when the price is valid (seconds) | -| `nativeFee` | `uint192` | Cost to verify report onchain (native token) | -| `linkFee` | `uint192` | Cost to verify report onchain (LINK) | -| `expiresAt` | `uint32` | Expiration date of the report (seconds) | -| `price` | `int192` | DON consensus median price | -| `bid` | `int192` | Simulated buy impact price at X% liquidity depth | -| `ask` | `int192` | Simulated sell impact price at X% liquidity depth | +| Field | Type | Description | +| ----------------------- | --------- | ------------------------------------------------------------------------ | +| `feedId` | `bytes32` | Unique identifier for the Data Streams feed | +| `validFromTimestamp` | `uint32` | Earliest timestamp when the price is valid (seconds) | +| `observationsTimestamp` | `uint32` | Latest timestamp when the price is valid (seconds) | +| `nativeFee` | `uint192` | Legacy onchain verification fee field | +| `linkFee` | `uint192` | Legacy onchain verification fee field; not used for subscription billing | +| `expiresAt` | `uint32` | Expiration date of the report (seconds) | +| `price` | `int192` | DON consensus median price | +| `bid` | `int192` | Simulated buy impact price at X% liquidity depth | +| `ask` | `int192` | Simulated sell impact price at X% liquidity depth | **Note**: Future Cryptocurrency streams may use different report schemas. @@ -5830,8 +5818,8 @@ Real World Asset (RWA) streams adhere to the report schema outlined below. | `feedId` | `bytes32` | The unique identifier for the stream | | `validFromTimestamp` | `uint32` | The earliest timestamp during which the price is valid (seconds) | | `observationsTimestamp` | `uint32` | The latest timestamp during which the price is valid (seconds) | -| `nativeFee` | `uint192` | The cost to verify this report onchain when paying with the blockchain's native token | -| `linkFee` | `uint192` | The cost to verify this report onchain when paying with LINK | +| `nativeFee` | `uint192` | Legacy onchain verification fee field | +| `linkFee` | `uint192` | Legacy onchain verification fee field; not used for subscription billing | | `expiresAt` | `uint32` | The expiration date of this report (seconds) | | `price` | `int192` | The DON's consensus median price | | `marketStatus` | `uint8` | The DON's consensus on whether the market is currently open. Possible values: `0` (`Unknown`), `1` (`Closed`), `2` (`Open`). | @@ -5852,15 +5840,15 @@ Exchange Rate streams adhere to the report schema outlined below. ## Schema Fields -| Field | Type | Description | -| ----------------------- | --------- | ---------------------------------------------------- | -| `feedId` | `bytes32` | Unique identifier for the Data Streams feed | -| `validFromTimestamp` | `uint32` | Earliest timestamp when the price is valid (seconds) | -| `observationsTimestamp` | `uint32` | Latest timestamp when the price is valid (seconds) | -| `nativeFee` | `uint192` | Cost to verify report onchain (native token) | -| `linkFee` | `uint192` | Cost to verify report onchain (LINK) | -| `expiresAt` | `uint32` | Expiration date of the report (seconds) | -| `exchangeRate` | `int192` | DON's consensus median exchange rate | +| Field | Type | Description | +| ----------------------- | --------- | ------------------------------------------------------------------------ | +| `feedId` | `bytes32` | Unique identifier for the Data Streams feed | +| `validFromTimestamp` | `uint32` | Earliest timestamp when the price is valid (seconds) | +| `observationsTimestamp` | `uint32` | Latest timestamp when the price is valid (seconds) | +| `nativeFee` | `uint192` | Legacy onchain verification fee field | +| `linkFee` | `uint192` | Legacy onchain verification fee field; not used for subscription billing | +| `expiresAt` | `uint32` | Expiration date of the report (seconds) | +| `exchangeRate` | `int192` | DON's consensus median exchange rate | **Notes**: @@ -5877,17 +5865,17 @@ RWA streams adhere to the report schema outlined below. ### Schema Fields -| Field | Type | Description | -| ----------------------- | --------- | ------------------------------------------------------ | -| `feedId` | `bytes32` | Unique identifier for the Data Streams feed | -| `validFromTimestamp` | `uint32` | Earliest timestamp when the price is valid (seconds) | -| `observationsTimestamp` | `uint32` | Latest timestamp when the price is valid (seconds) | -| `nativeFee` | `uint192` | Cost to verify report onchain (native token) | -| `linkFee` | `uint192` | Cost to verify report onchain (LINK) | -| `expiresAt` | `uint32` | Expiration date of the report (seconds) | -| `lastUpdateTimestamp` | `uint64` | Timestamp of the last valid price update (nanoseconds) | -| `midPrice` | `int192` | DON's consensus median price | -| `marketStatus` | `uint32` | Market status. See schema docs for value mappings. | +| Field | Type | Description | +| ----------------------- | --------- | ------------------------------------------------------------------------ | +| `feedId` | `bytes32` | Unique identifier for the Data Streams feed | +| `validFromTimestamp` | `uint32` | Earliest timestamp when the price is valid (seconds) | +| `observationsTimestamp` | `uint32` | Latest timestamp when the price is valid (seconds) | +| `nativeFee` | `uint192` | Legacy onchain verification fee field | +| `linkFee` | `uint192` | Legacy onchain verification fee field; not used for subscription billing | +| `expiresAt` | `uint32` | Expiration date of the report (seconds) | +| `lastUpdateTimestamp` | `uint64` | Timestamp of the last valid price update (nanoseconds) | +| `midPrice` | `int192` | DON's consensus median price | +| `marketStatus` | `uint32` | Market status. See schema docs for value mappings. | ### Market Status Values @@ -5928,8 +5916,8 @@ Chainlink SmartData streams adhere to the report schema outlined below. | `feedId` | `bytes32` | Unique identifier for the Data Streams feed | | `validFromTimestamp` | `uint32` | Earliest timestamp when the price is valid (seconds) | | `observationsTimestamp` | `uint32` | Latest timestamp when the price is valid (seconds) | -| `nativeFee` | `uint192` | Cost to verify report onchain (native token) | -| `linkFee` | `uint192` | Cost to verify report onchain (LINK) | +| `nativeFee` | `uint192` | Legacy onchain verification fee field | +| `linkFee` | `uint192` | Legacy onchain verification fee field; not used for subscription billing | | `expiresAt` | `uint32` | Expiration date of the report (seconds) | | `navPerShare` | `int192` | DON consensus NAV Per Share value as reported by the Fund Manager | | `navDate` | `uint64` | Timestamp for the NAV Report publication date (milliseconds) | @@ -6240,22 +6228,22 @@ Developers are responsible for choosing the appropriate feed and ensuring that t APAC Equities data is delivered using the [RWA Advanced (v11) schema](/data-streams/reference/report-schema-v11). -| Field | Type | Description | -| ----------------------- | --------- | ------------------------------------------------------- | -| `feedId` | `bytes32` | Unique identifier for the Data Streams feed | -| `validFromTimestamp` | `uint32` | Earliest timestamp when the price is valid (seconds) | -| `observationsTimestamp` | `uint32` | Latest timestamp when the price is valid (seconds) | -| `nativeFee` | `uint192` | Cost to verify report onchain (native token) | -| `linkFee` | `uint192` | Cost to verify report onchain (LINK) | -| `expiresAt` | `uint32` | Expiration date of the report (seconds) | -| `mid` | `int192` | DON consensus mid-price | -| `lastSeenTimestampNs` | `uint64` | Timestamp of the last report update (nanoseconds) | -| `bid` | `int192` | Mid-price minus half the spread (mid − spread/2) | -| `bidVolume` | `int192` | Not populated for APAC equities; reported as 0 | -| `ask` | `int192` | Mid-price plus half the spread (mid + spread/2) | -| `askVolume` | `int192` | Not populated for APAC equities; reported as 0 | -| `lastTradedPrice` | `int192` | Not populated for APAC equities; reported as 0 | -| `marketStatus` | `uint32` | Market status. Mapping varies by feed; see schema docs. | +| Field | Type | Description | +| ----------------------- | --------- | ------------------------------------------------------------------------ | +| `feedId` | `bytes32` | Unique identifier for the Data Streams feed | +| `validFromTimestamp` | `uint32` | Earliest timestamp when the price is valid (seconds) | +| `observationsTimestamp` | `uint32` | Latest timestamp when the price is valid (seconds) | +| `nativeFee` | `uint192` | Legacy onchain verification fee field | +| `linkFee` | `uint192` | Legacy onchain verification fee field; not used for subscription billing | +| `expiresAt` | `uint32` | Expiration date of the report (seconds) | +| `mid` | `int192` | DON consensus mid-price | +| `lastSeenTimestampNs` | `uint64` | Timestamp of the last report update (nanoseconds) | +| `bid` | `int192` | Mid-price minus half the spread (mid − spread/2) | +| `bidVolume` | `int192` | Not populated for APAC equities; reported as 0 | +| `ask` | `int192` | Mid-price plus half the spread (mid + spread/2) | +| `askVolume` | `int192` | Not populated for APAC equities; reported as 0 | +| `lastTradedPrice` | `int192` | Not populated for APAC equities; reported as 0 | +| `marketStatus` | `uint32` | Market status. Mapping varies by feed; see schema docs. | For `marketStatus` value mappings, see [Standard-hours feeds (v11)](/data-streams/reference/report-schema-v11#standard-hours-feeds). For exchange schedules, see [Market Hours (APAC Equities)](/data-streams/market-hours#apac-equities). @@ -6919,61 +6907,26 @@ import {ILogAutomation, Log} from "@chainlink/contracts/src/v0.8/automation/inte import { StreamsLookupCompatibleInterface } from "@chainlink/contracts/src/v0.8/automation/interfaces/StreamsLookupCompatibleInterface.sol"; -import {Common} from "@chainlink/contracts/src/v0.8/llo-feeds/libraries/Common.sol"; - -import {IRewardManager} from "@chainlink/contracts/src/v0.8/llo-feeds/v0.3.0/interfaces/IRewardManager.sol"; -import {IVerifierFeeManager} from "@chainlink/contracts/src/v0.8/llo-feeds/v0.3.0/interfaces/IVerifierFeeManager.sol"; -import {IERC20} from "@openzeppelin/contracts/token/ERC20/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 +// Custom interface for IVerifierProxy interface IVerifierProxy { /** * @notice Verifies that the data encoded has been signed. - * correctly by routing to the correct verifier, and bills the user if applicable. + * correctly by routing to the correct verifier. * @param payload The encoded data to be verified, including the signed * report. - * @param parameterPayload Fee metadata for billing. For the current implementation this is just the abi-encoded fee - * token ERC-20 address. + * @param parameterPayload Empty bytes for Data Streams subscription billing. * @return verifierResponse The encoded report from the verifier. */ function verify( bytes calldata payload, bytes calldata parameterPayload ) external payable returns (bytes memory verifierResponse); - - function s_feeManager() external view returns (IVerifierFeeManager); -} - -interface IFeeManager { - /** - * @notice Calculates the fee and reward associated with verifying a report, including discounts for subscribers. - * This function assesses the fee and reward for report verification, applying a discount for recognized subscriber - * addresses. - * @param subscriber The address attempting to verify the report. A discount is applied if this address - * is recognized as a subscriber. - * @param unverifiedReport The report data awaiting verification. The content of this report is used to - * determine the base fee and reward, before considering subscriber discounts. - * @param quoteAddress The payment token address used for quoting fees and rewards. - * @return fee The fee assessed for verifying the report, with subscriber discounts applied where applicable. - * @return reward The reward allocated to the caller for successfully verifying the report. - * @return totalDiscount The total discount amount deducted from the fee for subscribers - */ - 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 { @@ -6991,9 +6944,8 @@ contract StreamsUpkeep is ILogAutomation, StreamsLookupCompatibleInterface { bytes32 feedId; // The stream 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 (e.g., WETH/ETH). - uint192 linkFee; // Base cost to validate a transaction using the report, denominated in LINK. + uint192 nativeFee; // Legacy onchain verification fee field. + uint192 linkFee; // Legacy onchain verification fee field. Not used for subscription billing. uint32 expiresAt; // Latest timestamp where the report can be verified onchain. int192 price; // DON consensus median price (8 or 18 decimals). int192 bid; // Simulated price impact of a buy order up to the X% depth of liquidity utilisation (8 or 18 decimals). @@ -7014,9 +6966,8 @@ contract StreamsUpkeep is ILogAutomation, StreamsLookupCompatibleInterface { bytes32 feedId; // The stream 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 (e.g., WETH/ETH). - uint192 linkFee; // Base cost to validate a transaction using the report, denominated in LINK. + uint192 nativeFee; // Legacy onchain verification fee field. + uint192 linkFee; // Legacy onchain verification fee field. Not used for subscription billing. uint32 expiresAt; // Latest timestamp where the report can be verified onchain. int192 price; // DON consensus median benchmark price (8 or 18 decimals). uint32 marketStatus; // The DON's consensus on whether the market is currently open. @@ -7028,7 +6979,6 @@ contract StreamsUpkeep is ILogAutomation, StreamsLookupCompatibleInterface { IVerifierProxy public verifier; - address public FEE_ADDRESS; string public constant DATASTREAMS_FEEDLABEL = "feedIDs"; string public constant DATASTREAMS_QUERYLABEL = "timestamp"; int192 public lastDecodedPrice; @@ -7100,18 +7050,8 @@ contract StreamsUpkeep is ILogAutomation, StreamsLookupCompatibleInterface { revert InvalidReportVersion(uint8(reportVersion)); } - // 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)); + // Verify the report. Data Streams uses subscription billing, so no fee metadata is required. + bytes memory verifiedReportData = verifier.verify(unverifiedReport, bytes("")); // Decode verified report data into the appropriate Report struct based on reportVersion if (reportVersion == 3) { @@ -7275,7 +7215,7 @@ Source: https://docs.chain.link/data-streams/tutorials/evm-onchain-report-verifi -In this tutorial, you will deploy a verifier contract to *Arbitrum Sepolia*, fund it with LINK, and use it to verify a Data Streams report onchain. You will also learn how to verify multiple reports in a single transaction using `verifyBulkReports()`. +In this tutorial, you will deploy a verifier contract to *Arbitrum Sepolia* and use it to verify a Data Streams report onchain. You will also learn how to verify multiple reports in a single transaction using `verifyBulkReports()`. ## Before you begin @@ -7291,7 +7231,7 @@ Refer to the following tutorials: ## Requirements -- Testnet ETH and LINK on *Arbitrum Sepolia*. Both are available at [faucets.chain.link](https://faucets.chain.link/arbitrum-sepolia). +- Testnet ETH on *Arbitrum Sepolia*. You can get it from [faucets.chain.link](https://faucets.chain.link/arbitrum-sepolia). - A wallet private key for signing transactions. If you use MetaMask, follow the [MetaMask guide](https://support.metamask.io/managing-my-wallet/secret-recovery-phrase-and-private-keys/how-to-export-an-accounts-private-key/) to export your private key. Never share your private key or commit it to version control. - [Foundry](https://book.getfoundry.sh/) installed. If you don't have it yet, run: @@ -7347,8 +7287,6 @@ Refer to the following tutorials: // SPDX-License-Identifier: MIT pragma solidity ^0.8.20; - import {Common} from "@chainlink/contracts/src/v0.8/llo-feeds/libraries/Common.sol"; - import {IVerifierFeeManager} from "@chainlink/contracts/src/v0.8/llo-feeds/v0.3.0/interfaces/IVerifierFeeManager.sol"; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; @@ -7358,35 +7296,26 @@ Refer to the following tutorials: * THIS IS AN EXAMPLE CONTRACT THAT USES UN-AUDITED CODE FOR DEMONSTRATION PURPOSES. * DO NOT USE THIS CODE IN PRODUCTION. * - * This contract verifies Chainlink Data Streams reports onchain and pays - * the verification fee in LINK (when required). It exposes two verification - * functions: + * This contract verifies Chainlink Data Streams reports onchain. It exposes + * two verification functions: * * - `verifyReport()` – verifies a single report payload. * - `verifyBulkReports()` – verifies multiple payloads (across any feed IDs) * in a single transaction using `verifyBulk()`. * - * - If `VerifierProxy.s_feeManager()` returns a non-zero address, the network - * expects you to interact with that FeeManager for every verification call: - * quote fees, approve the RewardManager, then call `verify()` / `verifyBulk()`. - * - * - If `s_feeManager()` returns the zero address, no FeeManager contract has - * been deployed on that chain. In that case there is nothing to quote or pay - * onchain, so the contract skips the fee logic entirely. - * - * The `if (address(feeManager) != address(0))` check below chooses the - * correct path automatically, making the same bytecode usable on any chain. + * Data Streams uses subscription-based billing. Verification calls do not + * require this contract to hold LINK or approve a FeeManager. */ // ──────────────────────────────────────────────────────────────────────────── - // Interfaces + // Interface // ──────────────────────────────────────────────────────────────────────────── interface IVerifierProxy { /** - * @notice Route a report to the correct verifier and (optionally) bill fees. + * @notice Route a report to the correct verifier. * @param payload Full report payload (header + signed report). - * @param parameterPayload ABI-encoded fee metadata. + * @param parameterPayload Empty bytes for Data Streams subscription billing. */ function verify( bytes calldata payload, @@ -7397,33 +7326,13 @@ Refer to the following tutorials: * @notice Route multiple reports to the correct verifier in a single call. * @param payloads Array of full report payloads. Each entry may reference * a different feed ID. Order is preserved in the output. - * @param parameterPayload ABI-encoded fee token address shared across all reports - * (or empty bytes if no FeeManager is deployed). + * @param parameterPayload Empty bytes for Data Streams subscription billing. * @return verifiedReports Verified report bytes in the same order as the input. */ function verifyBulk( bytes[] calldata payloads, bytes calldata parameterPayload ) external payable returns (bytes[] memory verifiedReports); - - function s_feeManager() external view returns (IVerifierFeeManager); - } - - interface IFeeManager { - /** - * @return fee, reward, totalDiscount - */ - 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); } // ──────────────────────────────────────────────────────────────────────────── @@ -7432,7 +7341,7 @@ Refer to the following tutorials: /** * @dev This contract implements functionality to verify Data Streams reports from - * the Data Streams API, with payment in LINK tokens. + * the Data Streams API. */ contract ClientReportsVerifier { // ----------------- Errors ----------------- @@ -7510,13 +7419,8 @@ Refer to the following tutorials: * 1. Decode the unverified report to get `reportData`. * 2. Read the first two bytes → schema version (`0x0003` or `0x0008`). * - Revert if the version is unsupported. - * 3. Fee handling: - * - Query `s_feeManager()` on the proxy. - * – Non-zero → quote the fee, approve the RewardManager, - * ABI-encode the fee token address for `verify()`. - * – Zero → no FeeManager; skip quoting/approval and pass `""`. - * 4. Call `VerifierProxy.verify()`. - * 5. Decode the verified report into the correct struct and emit the price. + * 3. Call `VerifierProxy.verify()` with an empty parameter payload. + * 4. Decode the verified report into the correct struct and emit the price. * * @param unverifiedReport Full payload returned by Streams Direct. * @custom:reverts InvalidReportVersion when schema ≠ v3/v8. @@ -7532,27 +7436,10 @@ Refer to the following tutorials: revert InvalidReportVersion(reportVersion); } - // ─── 3. Fee handling ── - IFeeManager feeManager = IFeeManager(address(i_verifierProxy.s_feeManager())); - - bytes memory parameterPayload; - if (address(feeManager) != address(0)) { - // FeeManager exists — always quote & approve - address feeToken = feeManager.i_linkAddress(); - - (Common.Asset memory fee,,) = feeManager.getFeeAndReward(address(this), reportData, feeToken); - - IERC20(feeToken).approve(feeManager.i_rewardManager(), fee.amount); - parameterPayload = abi.encode(feeToken); - } else { - // No FeeManager deployed on this chain - parameterPayload = bytes(""); - } - - // ─── 4. Verify through the proxy ── - bytes memory verified = i_verifierProxy.verify(unverifiedReport, parameterPayload); + // ─── 3. Verify through the proxy ── + bytes memory verified = i_verifierProxy.verify(unverifiedReport, bytes("")); - // ─── 5. Decode & store price ── + // ─── 4. Decode & store price ── if (reportVersion == 3) { int192 price = abi.decode(verified, (ReportV3)).price; lastDecodedPrice = price; @@ -7570,13 +7457,9 @@ Refer to the following tutorials: * @dev Steps: * 1. Decode each payload to extract `reportData` and the schema version. * - Revert if any version is unsupported. - * 2. Fee handling (when FeeManager is deployed): - * - Quote the fee for each report individually via `getFeeAndReward`. - * - Accumulate the total fee across all reports. - * - Approve the RewardManager once for the combined total. - * - ABI-encode the fee token address for `verifyBulk()`. - * 3. Call `VerifierProxy.verifyBulk()` once with all payloads. - * 4. Decode each verified report, store prices in `lastDecodedPrices`, + * 2. Call `VerifierProxy.verifyBulk()` once with all payloads and an empty + * parameter payload. + * 3. Decode each verified report, store prices in `lastDecodedPrices`, * and emit a `DecodedPrice` event per report. * * @param unverifiedReports Array of full payloads from Streams Direct. @@ -7605,32 +7488,10 @@ Refer to the following tutorials: reportVersions[i] = reportVersion; } - // ─── 2. Fee handling ── - IFeeManager feeManager = IFeeManager(address(i_verifierProxy.s_feeManager())); + // ─── 2. Verify all reports in one proxy call ── + bytes[] memory verifiedReports = i_verifierProxy.verifyBulk(unverifiedReports, bytes("")); - bytes memory parameterPayload; - if (address(feeManager) != address(0)) { - address feeToken = feeManager.i_linkAddress(); - uint256 totalFee = 0; - - // Quote per-report fees and accumulate - for (uint256 i = 0; i < reportDataArray.length; i++) { - (Common.Asset memory fee,,) = feeManager.getFeeAndReward(address(this), reportDataArray[i], feeToken); - totalFee += fee.amount; - } - - // Single approval covers the combined cost of all reports - IERC20(feeToken).approve(feeManager.i_rewardManager(), totalFee); - parameterPayload = abi.encode(feeToken); - } else { - // No FeeManager deployed on this chain - parameterPayload = bytes(""); - } - - // ─── 3. Verify all reports in one proxy call ── - bytes[] memory verifiedReports = i_verifierProxy.verifyBulk(unverifiedReports, parameterPayload); - - // ─── 4. Decode verified reports, store prices, emit events ── + // ─── 3. Decode verified reports, store prices, emit events ── int192[] memory prices = new int192[](verifiedReports.length); for (uint256 i = 0; i < verifiedReports.length; i++) { @@ -7704,7 +7565,7 @@ Refer to the following tutorials: ## Deploy the verifier contract -Deploy `ClientReportsVerifier` to *Arbitrum Sepolia*, passing the Chainlink-deployed `VerifierProxy` address as the constructor argument. The `VerifierProxy` is a Chainlink-deployed contract that routes your verification calls to the correct Verifier contract and handles fee collection. You are not deploying the proxy yourself — you only point your contract at it. The VerifierProxy address for Arbitrum Sepolia is 0x2ff010DEbC1297f19579B4246cad07bd24F2488A. You can find addresses for other networks on the [Stream Addresses](/data-streams/crypto-streams) page. +Deploy `ClientReportsVerifier` to *Arbitrum Sepolia*, passing the Chainlink-deployed `VerifierProxy` address as the constructor argument. The `VerifierProxy` is a Chainlink-deployed contract that routes your verification calls to the correct Verifier contract. You are not deploying the proxy yourself — you only point your contract at it. The VerifierProxy address for Arbitrum Sepolia is 0x2ff010DEbC1297f19579B4246cad07bd24F2488A. You can find addresses for other networks on the [Stream Addresses](/data-streams/crypto-streams) page. ```bash forge create src/ClientReportsVerifier.sol:ClientReportsVerifier \ @@ -7732,31 +7593,6 @@ Save the `Deployed to` address — you'll need it for the remaining steps. Expor export CONTRACT_ADDRESS=0xYourContractAddress ``` -## Fund the verifier contract - -This contract pays verification fees in LINK tokens on networks where a `FeeManager` is deployed. Arbitrum Sepolia requires fees, so the contract needs LINK before it can verify reports. - -The LINK token contract address on Arbitrum Sepolia is 0xb1D4538B4571d411F07960EF2838Ce337FE1E80E. Transfer 1 testnet LINK to your deployed contract: - -```bash -cast send 0xb1D4538B4571d411F07960EF2838Ce337FE1E80E \ - "transfer(address,uint256)" \ - $CONTRACT_ADDRESS 1000000000000000000 \ - --rpc-url $RPC_URL \ - --account dataStreamsWallet -``` - -`1000000000000000000` is 1 LINK expressed in 18 decimal places. Confirm the transfer was successful by checking the contract's LINK balance: - -```bash -cast call 0xb1D4538B4571d411F07960EF2838Ce337FE1E80E \ - "balanceOf(address)(uint256)" \ - $CONTRACT_ADDRESS \ - --rpc-url $RPC_URL -``` - -You should see a non-zero value returned. - ## Verify a single report You need a signed report payload to verify. This is the `fullReport` value from a report you fetched using the Streams API — see the [Fetch and decode reports](/data-streams/tutorials/go-sdk-fetch) tutorial for an example payload. @@ -7776,11 +7612,9 @@ When you call `verifyReport()`, the contract does the following: Unsupported versions revert with `InvalidReportVersion`. -2. **Handle fees**: The contract calls `s_feeManager()` on the proxy to check whether a `FeeManager` is deployed on the current network. On networks with a FeeManager (Arbitrum, Avalanche, Base, Blast, Bob, Ink, Linea, OP, Scroll, Soneium, ZKSync), it quotes the fee via `getFeeAndReward`, approves the `RewardManager` to spend that LINK amount, and encodes the fee token address into `parameterPayload`. On networks without a FeeManager (zero address returned), it passes empty bytes and skips fee logic entirely. This check runs automatically, so the same contract works on any supported network without changes. +2. **Verify through the proxy**: The contract calls `VerifierProxy.verify()` with an empty `parameterPayload`. Data Streams uses subscription-based billing, so the contract does not need LINK funding for per-verification fees. -3. **Verify through the proxy**: The contract calls `VerifierProxy.verify()`, which checks the DON's signatures and returns the verified report body. - -4. **Decode and store the price**: The verified bytes are decoded into the appropriate struct (`ReportV3` or `ReportV8`). The price is stored in `lastDecodedPrice` and emitted as a `DecodedPrice` event. +3. **Decode and store the price**: The verified bytes are decoded into the appropriate struct (`ReportV3` or `ReportV8`). The price is stored in `lastDecodedPrice` and emitted as a `DecodedPrice` event. Store the payload in your shell: @@ -7843,10 +7677,9 @@ This is the ETH/USD price with 18 decimal places: **\~1,926.62 USD**. Each strea The function follows the same four stages as `verifyReport()`, extended to handle an array of payloads: -1. All payloads are decoded and their versions validated in a loop before any fee or verification logic runs. -2. `getFeeAndReward` is called once per report, and the individual fees are summed into a single `totalFee`. A single `approve()` covers the combined cost of the entire batch. -3. All payloads are passed to `VerifierProxy.verifyBulk()` in one call. -4. The verified reports are decoded and stored in `lastDecodedPrices[]`, with a `DecodedPrice` event emitted per report. +1. All payloads are decoded and their versions are validated in a loop before verification runs. +2. All payloads are passed to `VerifierProxy.verifyBulk()` in one call with an empty `parameterPayload`. +3. The verified reports are decoded and stored in `lastDecodedPrices[]`, with a `DecodedPrice` event emitted per report. Payloads in the array may reference different feed IDs — there is no requirement that they all be for the same stream. @@ -10998,6 +10831,8 @@ Expected output: The first line shows the `verified` event emitted by the Verifier contract, confirming the report was accepted. The quoted hex string that follows is the `report_data` returned by your `consume_price_data` function — a single concatenated hex string containing all decoded feed fields. +`nativeFee` and `linkFee` are legacy onchain verification fee fields in the report schema. Data Streams uses subscription-based billing, so these fields are not used to charge per-verification fees. + You can view the submitted transaction on [Stellar Expert](https://stellar.expert/explorer/testnet/tx/0485114249acb1ce1c5c1926fea02c143aeaa60b3791cfee85c49df8daf7f39b) as a reference example. ## Network addresses diff --git a/src/content/data-streams/reference/data-streams-api/onchain-verification.mdx b/src/content/data-streams/reference/data-streams-api/onchain-verification.mdx index e8d90ffdc60..f1f76a44833 100644 --- a/src/content/data-streams/reference/data-streams-api/onchain-verification.mdx +++ b/src/content/data-streams/reference/data-streams-api/onchain-verification.mdx @@ -48,7 +48,7 @@ The `IVerifierProxy` interface exposes two verification functions: ## IVerifierProxy interface -`IVerifierProxy` is the single onchain entry point for report verification. Your contract calls it directly — you never interact with the underlying Verifier contract. The proxy routes each call to the correct Verifier, handles fee collection through the `FeeManager` (when one is deployed), and returns the verified report data. +`IVerifierProxy` is the single onchain entry point for report verification. Your contract calls it directly — you never interact with the underlying Verifier contract. The proxy routes each call to the correct Verifier and returns the verified report data. You will need the deployed `VerifierProxy` address for the network you are targeting. Find it on the [Stream Addresses](/data-streams/crypto-streams) page. @@ -58,30 +58,30 @@ interface IVerifierProxy { // Decode the return value into the appropriate report struct (v3, v8, etc.). function verify( bytes calldata payload, // Full report payload from the Streams API - bytes calldata parameterPayload // abi.encode(feeTokenAddress), or "" if no FeeManager + bytes calldata parameterPayload // pass "" for Data Streams subscription billing ) external payable returns (bytes memory verifierResponse); // Verify multiple reports in one transaction. Accepts different feed IDs in the same call. // Returns verified report bytes in the same order as the input array. function verifyBulk( bytes[] calldata payloads, // Array of report payloads — may span different feed IDs - bytes calldata parameterPayload // abi.encode(feeTokenAddress), or "" if no FeeManager + bytes calldata parameterPayload // pass "" for Data Streams subscription billing ) external payable returns (bytes[] memory verifiedReports); - // Returns the FeeManager address, or the zero address if no FeeManager is deployed on this chain. - // Use this to determine whether fee quoting and approval steps are required before verifying. + // Legacy FeeManager accessor. Data Streams verification does not require + // per-verification fee quoting or approvals. function s_feeManager() external view returns (IVerifierFeeManager); } ``` ### `verify()` -Verifies a single report payload and optionally collects the verification fee. Pass the raw payload bytes returned by the Streams API directly as `payload` — no transformation is needed. +Verifies a single report payload. Pass the raw payload bytes returned by the Streams API directly as `payload` — no transformation is needed. -| Parameter | Type | Description | -| ------------------ | ---------------- | ------------------------------------------------------------------------------------------------------------------- | -| `payload` | `bytes calldata` | The full report payload returned by the Streams API (header + signed report body). | -| `parameterPayload` | `bytes calldata` | ABI-encoded fee token address (e.g. `abi.encode(linkTokenAddress)`), or empty bytes `""` when no FeeManager exists. | +| Parameter | Type | Description | +| ------------------ | ---------------- | ---------------------------------------------------------------------------------- | +| `payload` | `bytes calldata` | The full report payload returned by the Streams API (header + signed report body). | +| `parameterPayload` | `bytes calldata` | Empty bytes `""` for Data Streams subscription billing. | #### Returns @@ -91,21 +91,16 @@ Verifies a single report payload and optionally collects the verification fee. P #### Fee handling for `verify()` -Before calling `verify()`, check whether a `FeeManager` is deployed on the target network by calling `s_feeManager()` on the proxy. - -- **If `s_feeManager()` returns a non-zero address**: quote the fee using `getFeeAndReward`, approve the `RewardManager` to spend that LINK amount, then pass `abi.encode(feeTokenAddress)` as `parameterPayload`. -- **If `s_feeManager()` returns the zero address**: no fee is required. Pass empty bytes `""` as `parameterPayload`. - -Networks with a `FeeManager` currently deployed include: Arbitrum, Avalanche, Base, Blast, Bob, Ink, Linea, OP, Scroll, Soneium, and ZKSync. +Data Streams uses subscription-based billing. Your contract does not need to quote or approve a per-verification fee before calling `verify()`, and no supported EVM chain requires LINK funding for this step. Pass empty bytes `""` as `parameterPayload`. ### `verifyBulk()` Verifies multiple report payloads in a single transaction. Reports may reference different feed IDs, enabling atomic multi-feed updates. Pass the raw payload bytes from the Streams API directly — no transformation is needed. -| Parameter | Type | Description | -| ------------------ | ------------------ | --------------------------------------------------------------------------------------------------------------------------------------------- | -| `payloads` | `bytes[] calldata` | Array of full report payloads from the Streams API. Each entry may correspond to a different feed ID. Order is preserved in the output. | -| `parameterPayload` | `bytes calldata` | ABI-encoded fee token address shared across all reports (e.g. `abi.encode(linkTokenAddress)`), or empty bytes `""` when no FeeManager exists. | +| Parameter | Type | Description | +| ------------------ | ------------------ | --------------------------------------------------------------------------------------------------------------------------------------- | +| `payloads` | `bytes[] calldata` | Array of full report payloads from the Streams API. Each entry may correspond to a different feed ID. Order is preserved in the output. | +| `parameterPayload` | `bytes calldata` | Empty bytes `""` for Data Streams subscription billing. | #### Returns @@ -115,14 +110,7 @@ Verifies multiple report payloads in a single transaction. Reports may reference #### Fee handling for `verifyBulk()` -The same `FeeManager` check applies. When a `FeeManager` is deployed, the total fee equals the sum of individual fees across all reports in the batch. Your contract must: - -1. Decode each payload and call `getFeeAndReward` once per report to quote its fee. -1. Sum the individual fees to get the total amount required. -1. Approve the `RewardManager` contract to spend the total LINK amount in a single `approve()` call. -1. Pass the ABI-encoded fee token address as `parameterPayload`. - -When no `FeeManager` is deployed, pass empty bytes as `parameterPayload` and skip all fee logic. +Data Streams uses subscription-based billing. Your contract does not need to quote, sum, or approve per-report verification fees before calling `verifyBulk()`. Pass empty bytes `""` as `parameterPayload`. ## When to use `verifyBulk()` @@ -140,13 +128,11 @@ Use `verifyBulk()` when your protocol needs price data from multiple feeds in th - **What you save**: one base transaction cost (21,000 gas) per additional report beyond the first. - **What you do not save**: the cryptographic verification cost per report. -- **Fee cost**: verification fees (when applicable) are the same per report regardless of whether you use `verify()` or `verifyBulk()`. +- **Fee cost**: Data Streams does not charge a per-report onchain verification fee. ## Contract example -The contract below is an **example you deploy yourself**. It demonstrates the full verification pattern for both `verifyReport()` (single report) and `verifyBulkReports()` (multiple reports), including fee handling. Use it as a reference when writing your own contract. - -Your contract must have sufficient LINK tokens to pay for verification fees on networks where fees are required. Learn how to [fund your contract with LINK tokens](/resources/fund-your-contract). +The contract below is an **example you deploy yourself**. It demonstrates the full verification pattern for both `verifyReport()` (single report) and `verifyBulkReports()` (multiple reports). Use it as a reference when writing your own contract. @@ -155,7 +141,7 @@ Your contract must have sufficient LINK tokens to pay for verification fees on n ### `verifyReport()` — single report - **Input**: one `bytes` payload from the Streams API — pass it directly. -- **Fee**: checks for a `FeeManager`, quotes the fee if one exists, and approves that exact amount before calling `verify()`. +- **Fee**: passes empty bytes for `parameterPayload`; no per-verification LINK fee is required. - **Output**: stores the decoded price in `lastDecodedPrice` and emits `DecodedPrice(int192 price)`. See the [Verify report data onchain (EVM)](/data-streams/tutorials/evm-onchain-report-verification) tutorial for a step-by-step walkthrough using this function. @@ -163,5 +149,5 @@ See the [Verify report data onchain (EVM)](/data-streams/tutorials/evm-onchain-r ### `verifyBulkReports()` — multiple reports - **Input**: `bytes[]` array of payloads from the Streams API — each may reference a different feed ID. -- **Fee**: calls `getFeeAndReward` once per report, sums the results, and issues a single `approve()` for the combined total before calling `verifyBulk()`. +- **Fee**: passes empty bytes for `parameterPayload`; no per-verification LINK fee is required. - **Output**: stores the decoded prices in `lastDecodedPrices` (an `int192[]` array in the same order as the inputs) and emits `DecodedPrice(int192 price)` once per report. diff --git a/src/content/data-streams/reference/data-streams-api/ts-sdk.mdx b/src/content/data-streams/reference/data-streams-api/ts-sdk.mdx index ac7eb303801..f5ef883f2db 100644 --- a/src/content/data-streams/reference/data-streams-api/ts-sdk.mdx +++ b/src/content/data-streams/reference/data-streams-api/ts-sdk.mdx @@ -237,6 +237,8 @@ interface BaseFields { } ``` +`nativeFee` and `linkFee` are legacy onchain verification fee fields in the report schema. Data Streams uses subscription-based billing, so these fields are not used to charge per-verification fees. + ### Schema-Specific Fields - **V2/V3/V4**: `price: bigint` - Standard price data diff --git a/src/content/data-streams/reference/report-schema-v4.mdx b/src/content/data-streams/reference/report-schema-v4.mdx index bc7da864703..0d0a5b83dcb 100644 --- a/src/content/data-streams/reference/report-schema-v4.mdx +++ b/src/content/data-streams/reference/report-schema-v4.mdx @@ -30,8 +30,8 @@ Real World Asset (RWA) streams adhere to the report schema outlined below. | `feedId` | `bytes32` | The unique identifier for the stream | | `validFromTimestamp` | `uint32` | The earliest timestamp during which the price is valid (seconds) | | `observationsTimestamp` | `uint32` | The latest timestamp during which the price is valid (seconds) | -| `nativeFee` | `uint192` | The cost to verify this report onchain when paying with the blockchain's native token | -| `linkFee` | `uint192` | The cost to verify this report onchain when paying with LINK | +| `nativeFee` | `uint192` | Legacy onchain verification fee field | +| `linkFee` | `uint192` | Legacy onchain verification fee field; not used for subscription billing | | `expiresAt` | `uint32` | The expiration date of this report (seconds) | | `price` | `int192` | The DON's consensus median price | | `marketStatus` | `uint8` | The DON's consensus on whether the market is currently open. Possible values: `0` (`Unknown`), `1` (`Closed`), `2` (`Open`). | diff --git a/src/content/data-streams/tutorials/evm-onchain-report-verification.mdx b/src/content/data-streams/tutorials/evm-onchain-report-verification.mdx index ae46f529f3a..85d7bc02199 100644 --- a/src/content/data-streams/tutorials/evm-onchain-report-verification.mdx +++ b/src/content/data-streams/tutorials/evm-onchain-report-verification.mdx @@ -50,7 +50,7 @@ import DataStreams from "@features/data-streams/common/DataStreams.astro" ]} /> -In this tutorial, you will deploy a verifier contract to _Arbitrum Sepolia_, fund it with LINK, and use it to verify a Data Streams report onchain. You will also learn how to verify multiple reports in a single transaction using `verifyBulkReports()`. +In this tutorial, you will deploy a verifier contract to _Arbitrum Sepolia_ and use it to verify a Data Streams report onchain. You will also learn how to verify multiple reports in a single transaction using `verifyBulkReports()`. @@ -68,7 +68,7 @@ Refer to the following tutorials: ## Requirements -- Testnet ETH and LINK on _Arbitrum Sepolia_. Both are available at [faucets.chain.link](https://faucets.chain.link/arbitrum-sepolia). +- Testnet ETH on _Arbitrum Sepolia_. You can get it from [faucets.chain.link](https://faucets.chain.link/arbitrum-sepolia). - A wallet private key for signing transactions. If you use MetaMask, follow the [MetaMask guide](https://support.metamask.io/managing-my-wallet/secret-recovery-phrase-and-private-keys/how-to-export-an-accounts-private-key/) to export your private key. Never share your private key or commit it to version control. - [Foundry](https://book.getfoundry.sh/) installed. If you don't have it yet, run: @@ -163,7 +163,7 @@ Refer to the following tutorials: ## Deploy the verifier contract -Deploy `ClientReportsVerifier` to _Arbitrum Sepolia_, passing the Chainlink-deployed `VerifierProxy` address as the constructor argument. The `VerifierProxy` is a Chainlink-deployed contract that routes your verification calls to the correct Verifier contract and handles fee collection. You are not deploying the proxy yourself — you only point your contract at it. The VerifierProxy address for Arbitrum Sepolia is . You can find addresses for other networks on the [Stream Addresses](/data-streams/crypto-streams) page. +Deploy `ClientReportsVerifier` to _Arbitrum Sepolia_, passing the Chainlink-deployed `VerifierProxy` address as the constructor argument. The `VerifierProxy` is a Chainlink-deployed contract that routes your verification calls to the correct Verifier contract. You are not deploying the proxy yourself — you only point your contract at it. The VerifierProxy address for Arbitrum Sepolia is . You can find addresses for other networks on the [Stream Addresses](/data-streams/crypto-streams) page. ```bash forge create src/ClientReportsVerifier.sol:ClientReportsVerifier \ @@ -191,31 +191,6 @@ Save the `Deployed to` address — you'll need it for the remaining steps. Expor export CONTRACT_ADDRESS=0xYourContractAddress ``` -## Fund the verifier contract - -This contract pays verification fees in LINK tokens on networks where a `FeeManager` is deployed. Arbitrum Sepolia requires fees, so the contract needs LINK before it can verify reports. - -The LINK token contract address on Arbitrum Sepolia is . Transfer 1 testnet LINK to your deployed contract: - -```bash -cast send 0xb1D4538B4571d411F07960EF2838Ce337FE1E80E \ - "transfer(address,uint256)" \ - $CONTRACT_ADDRESS 1000000000000000000 \ - --rpc-url $RPC_URL \ - --account dataStreamsWallet -``` - -`1000000000000000000` is 1 LINK expressed in 18 decimal places. Confirm the transfer was successful by checking the contract's LINK balance: - -```bash -cast call 0xb1D4538B4571d411F07960EF2838Ce337FE1E80E \ - "balanceOf(address)(uint256)" \ - $CONTRACT_ADDRESS \ - --rpc-url $RPC_URL -``` - -You should see a non-zero value returned. - ## Verify a single report You need a signed report payload to verify. This is the `fullReport` value from a report you fetched using the Streams API — see the [Fetch and decode reports](/data-streams/tutorials/go-sdk-fetch) tutorial for an example payload. @@ -235,11 +210,9 @@ When you call `verifyReport()`, the contract does the following: Unsupported versions revert with `InvalidReportVersion`. -2. **Handle fees**: The contract calls `s_feeManager()` on the proxy to check whether a `FeeManager` is deployed on the current network. On networks with a FeeManager (Arbitrum, Avalanche, Base, Blast, Bob, Ink, Linea, OP, Scroll, Soneium, ZKSync), it quotes the fee via `getFeeAndReward`, approves the `RewardManager` to spend that LINK amount, and encodes the fee token address into `parameterPayload`. On networks without a FeeManager (zero address returned), it passes empty bytes and skips fee logic entirely. This check runs automatically, so the same contract works on any supported network without changes. - -3. **Verify through the proxy**: The contract calls `VerifierProxy.verify()`, which checks the DON's signatures and returns the verified report body. +2. **Verify through the proxy**: The contract calls `VerifierProxy.verify()` with an empty `parameterPayload`. Data Streams uses subscription-based billing, so the contract does not need LINK funding for per-verification fees. -4. **Decode and store the price**: The verified bytes are decoded into the appropriate struct (`ReportV3` or `ReportV8`). The price is stored in `lastDecodedPrice` and emitted as a `DecodedPrice` event. +3. **Decode and store the price**: The verified bytes are decoded into the appropriate struct (`ReportV3` or `ReportV8`). The price is stored in `lastDecodedPrice` and emitted as a `DecodedPrice` event. Store the payload in your shell: @@ -302,10 +275,9 @@ This is the ETH/USD price with 18 decimal places: **~1,926.62 USD**. Each stream The function follows the same four stages as `verifyReport()`, extended to handle an array of payloads: -1. All payloads are decoded and their versions validated in a loop before any fee or verification logic runs. -2. `getFeeAndReward` is called once per report, and the individual fees are summed into a single `totalFee`. A single `approve()` covers the combined cost of the entire batch. -3. All payloads are passed to `VerifierProxy.verifyBulk()` in one call. -4. The verified reports are decoded and stored in `lastDecodedPrices[]`, with a `DecodedPrice` event emitted per report. +1. All payloads are decoded and their versions are validated in a loop before verification runs. +2. All payloads are passed to `VerifierProxy.verifyBulk()` in one call with an empty `parameterPayload`. +3. The verified reports are decoded and stored in `lastDecodedPrices[]`, with a `DecodedPrice` event emitted per report. Payloads in the array may reference different feed IDs — there is no requirement that they all be for the same stream. diff --git a/src/content/data-streams/tutorials/stellar-onchain-report-verification.mdx b/src/content/data-streams/tutorials/stellar-onchain-report-verification.mdx index 9baa2cbf26d..aaf33f5d77d 100644 --- a/src/content/data-streams/tutorials/stellar-onchain-report-verification.mdx +++ b/src/content/data-streams/tutorials/stellar-onchain-report-verification.mdx @@ -428,6 +428,8 @@ Expected output: The first line shows the `verified` event emitted by the Verifier contract, confirming the report was accepted. The quoted hex string that follows is the `report_data` returned by your `consume_price_data` function — a single concatenated hex string containing all decoded feed fields. +`nativeFee` and `linkFee` are legacy onchain verification fee fields in the report schema. Data Streams uses subscription-based billing, so these fields are not used to charge per-verification fees. + You can view the submitted transaction on [Stellar Expert](https://stellar.expert/explorer/testnet/tx/0485114249acb1ce1c5c1926fea02c143aeaa60b3791cfee85c49df8daf7f39b) as a reference example. ## Network addresses diff --git a/src/features/data-streams/common/gettingStarted.mdx b/src/features/data-streams/common/gettingStarted.mdx index 600e03cace0..ccc1f1a587c 100644 --- a/src/features/data-streams/common/gettingStarted.mdx +++ b/src/features/data-streams/common/gettingStarted.mdx @@ -204,7 +204,6 @@ The code example uses `revert` with `StreamsLookup` to convey call information a When you deploy the contract, you define the verifier proxy address. You can find this address on the [Stream Addresses](/data-streams/crypto-streams) page. The `IVerifierProxy` interface provides the following functions: -- The `s_feeManager` function to estimate the verification fees. - The `verify` function to verify the report onchain. ### Emitting a log, retrieving, and verifying the report diff --git a/src/features/data-streams/common/gettingStartedHardhat.mdx b/src/features/data-streams/common/gettingStartedHardhat.mdx index 19e3edc8a46..a9849706677 100644 --- a/src/features/data-streams/common/gettingStartedHardhat.mdx +++ b/src/features/data-streams/common/gettingStartedHardhat.mdx @@ -168,20 +168,17 @@ The code example uses `revert` with `StreamsLookup` to convey call information a When you deploy the contract, you define: 1. The verifier proxy address that you can find on the [Data Stream Addresses](/data-streams/crypto-streams) page. The `IVerifierProxy` interface provides the following functions: - - The `s_feeManager` function to estimate the verification fees. - The `verify` function to verify the report onchain. 2. The LINK token address. This address is used to register and fund your upkeep. You can find the LINK token address on the [Chainlink Token Addresses](/resources/link-token-contracts) page. 3. The registrar's contract address. This address is used to register your upkeep. You can find the registrar contract addresses on the [Chainlink Automation Supported Networks](/chainlink-automation/overview/supported-networks) page. -### Funding the upkeep contract +### Funding the upkeep -In this example, you must fund the `StreamsUpkeepRegistrar` contract with testnet LINK tokens to pay the onchain report verification fees. You can use the [`transfer-link`](https://github.com/smartcontractkit/smart-contract-examples/blob/main/data-streams/getting-started/hardhat/tasks/transfer-link.js) task to transfer LINK tokens to the `StreamsUpkeepRegistrar` contract you deployed. +Data Streams uses subscription-based billing, so you do not need to fund the `StreamsUpkeepRegistrar` contract with LINK to pay onchain report verification fees. -The `transfer-link` Hardhat task sets up the necessary parameters for the LINK token transfer and submits the transfer request to the LINK token contract using the `transfer` function. - -**Note:** Funding the `StreamsUpkeepRegistrar` contract is distinct from funding your Chainlink Automation upkeep to pay the fees to perform the upkeep. +You still need to fund your Chainlink Automation upkeep so Automation can perform the upkeep. ### Registering the upkeep diff --git a/src/features/feeds/components/reportSchemaData.ts b/src/features/feeds/components/reportSchemaData.ts index d3e5594ec59..5ca43af7e9a 100644 --- a/src/features/feeds/components/reportSchemaData.ts +++ b/src/features/feeds/components/reportSchemaData.ts @@ -27,8 +27,12 @@ const COMMON_FIELDS: SchemaField[] = [ type: "uint32", description: "Latest timestamp when the price is valid (seconds)", }, - { field: "nativeFee", type: "uint192", description: "Cost to verify report onchain (native token)" }, - { field: "linkFee", type: "uint192", description: "Cost to verify report onchain (LINK)" }, + { field: "nativeFee", type: "uint192", description: "Legacy onchain verification fee field" }, + { + field: "linkFee", + type: "uint192", + description: "Legacy onchain verification fee field; not used for subscription billing", + }, { field: "expiresAt", type: "uint32", description: "Expiration date of the report (seconds)" }, ] From b48c06120bbab04264ae18fe64d927c617b5675b Mon Sep 17 00:00:00 2001 From: devin distefano Date: Thu, 23 Jul 2026 11:39:33 -0500 Subject: [PATCH 2/2] some clarifications --- .../DataStreams/ClientReportsVerifier.sol | 7 ++-- src/content/data-streams/llms-full.txt | 35 ++++++++++--------- .../data-streams-api/onchain-verification.mdx | 24 ++++++------- .../evm-onchain-report-verification.mdx | 4 +-- 4 files changed, 36 insertions(+), 34 deletions(-) diff --git a/public/samples/DataStreams/ClientReportsVerifier.sol b/public/samples/DataStreams/ClientReportsVerifier.sol index ae08efcfd2f..b07a2c1d27e 100644 --- a/public/samples/DataStreams/ClientReportsVerifier.sol +++ b/public/samples/DataStreams/ClientReportsVerifier.sol @@ -29,7 +29,7 @@ interface IVerifierProxy { /** * @notice Route a report to the correct verifier. * @param payload Full report payload (header + signed report). - * @param parameterPayload Empty bytes for Data Streams subscription billing. + * @param parameterPayload Empty fee metadata for Data Streams subscription billing. */ function verify( bytes calldata payload, @@ -40,7 +40,7 @@ interface IVerifierProxy { * @notice Route multiple reports to the correct verifier in a single call. * @param payloads Array of full report payloads. Each entry may reference * a different feed ID. Order is preserved in the output. - * @param parameterPayload Empty bytes for Data Streams subscription billing. + * @param parameterPayload Empty fee metadata for Data Streams subscription billing. * @return verifiedReports Verified report bytes in the same order as the input. */ function verifyBulk( @@ -133,7 +133,8 @@ contract ClientReportsVerifier { * 1. Decode the unverified report to get `reportData`. * 2. Read the first two bytes → schema version (`0x0003` or `0x0008`). * - Revert if the version is unsupported. - * 3. Call `VerifierProxy.verify()` with an empty parameter payload. + * 3. Call `VerifierProxy.verify()` with empty fee metadata. Data Streams + * uses subscription billing, so no fee token address is required. * 4. Decode the verified report into the correct struct and emit the price. * * @param unverifiedReport Full payload returned by Streams Direct. diff --git a/src/content/data-streams/llms-full.txt b/src/content/data-streams/llms-full.txt index 902d7b6b627..f37d839b79d 100644 --- a/src/content/data-streams/llms-full.txt +++ b/src/content/data-streams/llms-full.txt @@ -4563,14 +4563,14 @@ interface IVerifierProxy { // Decode the return value into the appropriate report struct (v3, v8, etc.). function verify( bytes calldata payload, // Full report payload from the Streams API - bytes calldata parameterPayload // pass "" for Data Streams subscription billing + bytes calldata parameterPayload // pass empty fee metadata for Data Streams subscription billing ) external payable returns (bytes memory verifierResponse); // Verify multiple reports in one transaction. Accepts different feed IDs in the same call. // Returns verified report bytes in the same order as the input array. function verifyBulk( bytes[] calldata payloads, // Array of report payloads — may span different feed IDs - bytes calldata parameterPayload // pass "" for Data Streams subscription billing + bytes calldata parameterPayload // pass empty fee metadata for Data Streams subscription billing ) external payable returns (bytes[] memory verifiedReports); // Legacy FeeManager accessor. Data Streams verification does not require @@ -4583,10 +4583,10 @@ interface IVerifierProxy { Verifies a single report payload. Pass the raw payload bytes returned by the Streams API directly as `payload` — no transformation is needed. -| Parameter | Type | Description | -| ------------------ | ---------------- | ---------------------------------------------------------------------------------- | -| `payload` | `bytes calldata` | The full report payload returned by the Streams API (header + signed report body). | -| `parameterPayload` | `bytes calldata` | Empty bytes `""` for Data Streams subscription billing. | +| Parameter | Type | Description | +| ------------------ | ---------------- | ----------------------------------------------------------------------------------------------------------------------------------------- | +| `payload` | `bytes calldata` | The full report payload returned by the Streams API (header + signed report body). | +| `parameterPayload` | `bytes calldata` | Fee metadata parameter retained for legacy fee-manager integrations. Pass empty bytes `""` for current Data Streams subscription billing. | #### Returns @@ -4596,16 +4596,16 @@ Verifies a single report payload. Pass the raw payload bytes returned by the Str #### Fee handling for `verify()` -Data Streams uses subscription-based billing. Your contract does not need to quote or approve a per-verification fee before calling `verify()`, and no supported EVM chain requires LINK funding for this step. Pass empty bytes `""` as `parameterPayload`. +Data Streams uses subscription-based billing. Your contract does not need to quote or approve a per-verification fee before calling `verify()`, and no supported EVM chain requires LINK funding for this step. Pass empty bytes `""` as `parameterPayload` because no fee token metadata is required. ### `verifyBulk()` Verifies multiple report payloads in a single transaction. Reports may reference different feed IDs, enabling atomic multi-feed updates. Pass the raw payload bytes from the Streams API directly — no transformation is needed. -| Parameter | Type | Description | -| ------------------ | ------------------ | --------------------------------------------------------------------------------------------------------------------------------------- | -| `payloads` | `bytes[] calldata` | Array of full report payloads from the Streams API. Each entry may correspond to a different feed ID. Order is preserved in the output. | -| `parameterPayload` | `bytes calldata` | Empty bytes `""` for Data Streams subscription billing. | +| Parameter | Type | Description | +| ------------------ | ------------------ | ----------------------------------------------------------------------------------------------------------------------------------------- | +| `payloads` | `bytes[] calldata` | Array of full report payloads from the Streams API. Each entry may correspond to a different feed ID. Order is preserved in the output. | +| `parameterPayload` | `bytes calldata` | Fee metadata parameter retained for legacy fee-manager integrations. Pass empty bytes `""` for current Data Streams subscription billing. | #### Returns @@ -4615,7 +4615,7 @@ Verifies multiple report payloads in a single transaction. Reports may reference #### Fee handling for `verifyBulk()` -Data Streams uses subscription-based billing. Your contract does not need to quote, sum, or approve per-report verification fees before calling `verifyBulk()`. Pass empty bytes `""` as `parameterPayload`. +Data Streams uses subscription-based billing. Your contract does not need to quote, sum, or approve per-report verification fees before calling `verifyBulk()`. Pass empty bytes `""` as `parameterPayload` because no fee token metadata is required. ## When to use `verifyBulk()` @@ -7315,7 +7315,7 @@ Refer to the following tutorials: /** * @notice Route a report to the correct verifier. * @param payload Full report payload (header + signed report). - * @param parameterPayload Empty bytes for Data Streams subscription billing. + * @param parameterPayload Empty fee metadata for Data Streams subscription billing. */ function verify( bytes calldata payload, @@ -7326,7 +7326,7 @@ Refer to the following tutorials: * @notice Route multiple reports to the correct verifier in a single call. * @param payloads Array of full report payloads. Each entry may reference * a different feed ID. Order is preserved in the output. - * @param parameterPayload Empty bytes for Data Streams subscription billing. + * @param parameterPayload Empty fee metadata for Data Streams subscription billing. * @return verifiedReports Verified report bytes in the same order as the input. */ function verifyBulk( @@ -7419,7 +7419,8 @@ Refer to the following tutorials: * 1. Decode the unverified report to get `reportData`. * 2. Read the first two bytes → schema version (`0x0003` or `0x0008`). * - Revert if the version is unsupported. - * 3. Call `VerifierProxy.verify()` with an empty parameter payload. + * 3. Call `VerifierProxy.verify()` with empty fee metadata. Data Streams + * uses subscription billing, so no fee token address is required. * 4. Decode the verified report into the correct struct and emit the price. * * @param unverifiedReport Full payload returned by Streams Direct. @@ -7612,7 +7613,7 @@ When you call `verifyReport()`, the contract does the following: Unsupported versions revert with `InvalidReportVersion`. -2. **Verify through the proxy**: The contract calls `VerifierProxy.verify()` with an empty `parameterPayload`. Data Streams uses subscription-based billing, so the contract does not need LINK funding for per-verification fees. +2. **Verify through the proxy**: The contract calls `VerifierProxy.verify()` with empty fee metadata in `parameterPayload`. The proxy ABI still includes this parameter for legacy fee-manager integrations, but current Data Streams billing is subscription-based, so no fee token address is required and the contract does not need LINK funding for per-verification fees. 3. **Decode and store the price**: The verified bytes are decoded into the appropriate struct (`ReportV3` or `ReportV8`). The price is stored in `lastDecodedPrice` and emitted as a `DecodedPrice` event. @@ -7678,7 +7679,7 @@ This is the ETH/USD price with 18 decimal places: **\~1,926.62 USD**. Each strea The function follows the same four stages as `verifyReport()`, extended to handle an array of payloads: 1. All payloads are decoded and their versions are validated in a loop before verification runs. -2. All payloads are passed to `VerifierProxy.verifyBulk()` in one call with an empty `parameterPayload`. +2. All payloads are passed to `VerifierProxy.verifyBulk()` in one call with empty fee metadata in `parameterPayload`. 3. The verified reports are decoded and stored in `lastDecodedPrices[]`, with a `DecodedPrice` event emitted per report. Payloads in the array may reference different feed IDs — there is no requirement that they all be for the same stream. diff --git a/src/content/data-streams/reference/data-streams-api/onchain-verification.mdx b/src/content/data-streams/reference/data-streams-api/onchain-verification.mdx index f1f76a44833..9c9759e6c49 100644 --- a/src/content/data-streams/reference/data-streams-api/onchain-verification.mdx +++ b/src/content/data-streams/reference/data-streams-api/onchain-verification.mdx @@ -58,14 +58,14 @@ interface IVerifierProxy { // Decode the return value into the appropriate report struct (v3, v8, etc.). function verify( bytes calldata payload, // Full report payload from the Streams API - bytes calldata parameterPayload // pass "" for Data Streams subscription billing + bytes calldata parameterPayload // pass empty fee metadata for Data Streams subscription billing ) external payable returns (bytes memory verifierResponse); // Verify multiple reports in one transaction. Accepts different feed IDs in the same call. // Returns verified report bytes in the same order as the input array. function verifyBulk( bytes[] calldata payloads, // Array of report payloads — may span different feed IDs - bytes calldata parameterPayload // pass "" for Data Streams subscription billing + bytes calldata parameterPayload // pass empty fee metadata for Data Streams subscription billing ) external payable returns (bytes[] memory verifiedReports); // Legacy FeeManager accessor. Data Streams verification does not require @@ -78,10 +78,10 @@ interface IVerifierProxy { Verifies a single report payload. Pass the raw payload bytes returned by the Streams API directly as `payload` — no transformation is needed. -| Parameter | Type | Description | -| ------------------ | ---------------- | ---------------------------------------------------------------------------------- | -| `payload` | `bytes calldata` | The full report payload returned by the Streams API (header + signed report body). | -| `parameterPayload` | `bytes calldata` | Empty bytes `""` for Data Streams subscription billing. | +| Parameter | Type | Description | +| ------------------ | ---------------- | ----------------------------------------------------------------------------------------------------------------------------------------- | +| `payload` | `bytes calldata` | The full report payload returned by the Streams API (header + signed report body). | +| `parameterPayload` | `bytes calldata` | Fee metadata parameter retained for legacy fee-manager integrations. Pass empty bytes `""` for current Data Streams subscription billing. | #### Returns @@ -91,16 +91,16 @@ Verifies a single report payload. Pass the raw payload bytes returned by the Str #### Fee handling for `verify()` -Data Streams uses subscription-based billing. Your contract does not need to quote or approve a per-verification fee before calling `verify()`, and no supported EVM chain requires LINK funding for this step. Pass empty bytes `""` as `parameterPayload`. +Data Streams uses subscription-based billing. Your contract does not need to quote or approve a per-verification fee before calling `verify()`, and no supported EVM chain requires LINK funding for this step. Pass empty bytes `""` as `parameterPayload` because no fee token metadata is required. ### `verifyBulk()` Verifies multiple report payloads in a single transaction. Reports may reference different feed IDs, enabling atomic multi-feed updates. Pass the raw payload bytes from the Streams API directly — no transformation is needed. -| Parameter | Type | Description | -| ------------------ | ------------------ | --------------------------------------------------------------------------------------------------------------------------------------- | -| `payloads` | `bytes[] calldata` | Array of full report payloads from the Streams API. Each entry may correspond to a different feed ID. Order is preserved in the output. | -| `parameterPayload` | `bytes calldata` | Empty bytes `""` for Data Streams subscription billing. | +| Parameter | Type | Description | +| ------------------ | ------------------ | ----------------------------------------------------------------------------------------------------------------------------------------- | +| `payloads` | `bytes[] calldata` | Array of full report payloads from the Streams API. Each entry may correspond to a different feed ID. Order is preserved in the output. | +| `parameterPayload` | `bytes calldata` | Fee metadata parameter retained for legacy fee-manager integrations. Pass empty bytes `""` for current Data Streams subscription billing. | #### Returns @@ -110,7 +110,7 @@ Verifies multiple report payloads in a single transaction. Reports may reference #### Fee handling for `verifyBulk()` -Data Streams uses subscription-based billing. Your contract does not need to quote, sum, or approve per-report verification fees before calling `verifyBulk()`. Pass empty bytes `""` as `parameterPayload`. +Data Streams uses subscription-based billing. Your contract does not need to quote, sum, or approve per-report verification fees before calling `verifyBulk()`. Pass empty bytes `""` as `parameterPayload` because no fee token metadata is required. ## When to use `verifyBulk()` diff --git a/src/content/data-streams/tutorials/evm-onchain-report-verification.mdx b/src/content/data-streams/tutorials/evm-onchain-report-verification.mdx index 85d7bc02199..d4e659a5230 100644 --- a/src/content/data-streams/tutorials/evm-onchain-report-verification.mdx +++ b/src/content/data-streams/tutorials/evm-onchain-report-verification.mdx @@ -210,7 +210,7 @@ When you call `verifyReport()`, the contract does the following: Unsupported versions revert with `InvalidReportVersion`. -2. **Verify through the proxy**: The contract calls `VerifierProxy.verify()` with an empty `parameterPayload`. Data Streams uses subscription-based billing, so the contract does not need LINK funding for per-verification fees. +2. **Verify through the proxy**: The contract calls `VerifierProxy.verify()` with empty fee metadata in `parameterPayload`. The proxy ABI still includes this parameter for legacy fee-manager integrations, but current Data Streams billing is subscription-based, so no fee token address is required and the contract does not need LINK funding for per-verification fees. 3. **Decode and store the price**: The verified bytes are decoded into the appropriate struct (`ReportV3` or `ReportV8`). The price is stored in `lastDecodedPrice` and emitted as a `DecodedPrice` event. @@ -276,7 +276,7 @@ This is the ETH/USD price with 18 decimal places: **~1,926.62 USD**. Each stream The function follows the same four stages as `verifyReport()`, extended to handle an array of payloads: 1. All payloads are decoded and their versions are validated in a loop before verification runs. -2. All payloads are passed to `VerifierProxy.verifyBulk()` in one call with an empty `parameterPayload`. +2. All payloads are passed to `VerifierProxy.verifyBulk()` in one call with empty fee metadata in `parameterPayload`. 3. The verified reports are decoded and stored in `lastDecodedPrices[]`, with a `DecodedPrice` event emitted per report. Payloads in the array may reference different feed IDs — there is no requirement that they all be for the same stream.