Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
91 changes: 91 additions & 0 deletions config-chainlink-example.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
# =================================================================
# │ Chainlink Data Streams Transmitter Example │
# =================================================================
#
# Copy this file to 'config.yml' and fill in your specific details.
# This file configures which data feeds to monitor and which on-chain
# contracts to send the data to.

# -----------------------------------------------------------------
# ⚠️ OFF-CHAIN VERIFICATION CONFIG (NO LINK NEEDED) ⚠️
# -----------------------------------------------------------------
# • The transmitter verifies the report off-chain, then calls
# `updateReport(uint16 reportVersion, bytes verifiedReportData)`.
# • Your DataStreamsFeed contract MUST grant the REPORT_VERIFIER role
# to the transmitter's EOA (or keystore account) so the call succeeds.
# • The contract itself does NOT need any LINK balance because fees are
# paid by the off-chain verifier, not on-chain.
# -----------------------------------------------------------------

# A list of all unique off-chain Data Streams to subscribe to.
# Find more feed IDs in the Chainlink documentation.
feeds:
- name: 'ETH/USD'
feedId: '0x000359843a543ee2fe414dc14c7e7920ef10f4372990b79d6361cdc0dd1ba782'

# --- Default Global Settings ---

# The default chainId to use for transactions if not specified in a target.
chainId: 43113 # Default to Avalanche Fuji

# The maximum gas limit you are willing to spend on a transaction.
gasCap: '250000'

# Cron expression defining the data update frequency.
# This example runs every 30 seconds.
interval: '*/30 * * * * *'

# The minimum price change percentage to trigger an on-chain update.
# This example is set to 0.1%
priceDeltaPercentage: 0.001

# --- Chain & Verifier Definitions ---

# A list of all supported blockchain networks with their RPC URLs.
chains:
- id: 43113
name: 'Avalanche Fuji Testnet'
currencyName: 'Fuji AVAX'
currencySymbol: 'AVAX'
currencyDecimals: 18
rpc: 'https://api.avax-test.network/ext/bc/C/rpc' # <-- TODO: Replace with your own reliable RPC URL
testnet: true
- id: 421614
name: 'Arbitrum Sepolia'
currencyName: 'Arbitrum Sepolia Ether'
currencySymbol: 'ETH'
currencyDecimals: 18
rpc: 'https://sepolia-rollup.arbitrum.io/rpc' # <-- TODO: Replace with your own reliable RPC URL
testnet: true

# The addresses of the official Chainlink Verifier contracts on each network.
verifierAddresses:
- chainId: 43113
address: '0x2bf612C65f5a4d388E687948bb2CF842FFb8aBB3'
- chainId: 421614
address: '0x2ff010DEbC1297f19579B4246cad07bd24F2488A'

# --- On-Chain Target Configurations ---

# This section defines which smart contracts to call for each feed on each chain.
# You can have multiple targets for the same feed.
targetChains:
- chainId: 43113 # Target is on Avalanche Fuji
targetContracts:
# This configuration sends ETH/USD data to a contract on Fuji
- feedId: '0x000359843a543ee2fe414dc14c7e7920ef10f4372990b79d6361cdc0dd1ba782'
# TODO: Replace with the address of your deployed contract on Fuji
address: '0xYourDataStreamsFeedContractOnFuji' # <-- CHANGED
# The name of the function to call on your smart contract
functionName: 'updateReport'
# The arguments the Transmitter should prepare and send to the function
functionArgs: ['reportVersion', 'verifiedReport']
# The ABI for the target function, required to encode the transaction
abi:
- name: 'updateReport'
type: 'function'
stateMutability: 'nonpayable'
inputs:
- { "internalType": "uint16", "name": "reportVersion", "type": "uint16" }
- { "internalType": "bytes", "name": "verifiedReportData", "type": "bytes" }
outputs: []
95 changes: 95 additions & 0 deletions config-chainlink-verify-example.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
# =================================================================
# │ Chainlink Data Streams Transmitter Example │
# =================================================================
#
# Copy this file to 'config.yml' and fill in your specific details.
# This file configures which data feeds to monitor and which on-chain
# contracts to send the data to.

# -----------------------------------------------------------------
# ✅ ON-CHAIN VERIFICATION CONFIG (LINK-FUNDED FEED) ✅
# -----------------------------------------------------------------
# • The transmitter sends the raw report and fee-token parameter to
# `verifyAndUpdateReport(bytes unverifiedReportData, bytes parameterPayload)`.
# • No REPORT_VERIFIER role assignment is required; the function is public.
# • The DataStreamsFeed contract MUST hold enough LINK (fee token) so the
# VerifierProxy can deduct the verification fee from the contract.
# Native gas for storage is, as always, paid by the caller.
# -----------------------------------------------------------------

# A list of all unique off-chain Data Streams to subscribe to.
# Find more feed IDs in the Chainlink documentation.
feeds:
- name: 'ETH/USD'
feedId: '0x000359843a543ee2fe414dc14c7e7920ef10f4372990b79d6361cdc0dd1ba782'

# --- Default Global Settings ---

# The default chainId to use for transactions if not specified in a target.
chainId: 43113 # Default to Avalanche Fuji

# The maximum gas limit you are willing to spend on a transaction.
gasCap: '250000'

# Cron expression defining the data update frequency.
# This example runs every 30 seconds.
interval: '*/30 * * * * *'

# The minimum price change percentage to trigger an on-chain update.
# This example is set to 0.1%
priceDeltaPercentage: 0.001

# --- Chain & Verifier Definitions ---

# A list of all supported blockchain networks with their RPC URLs.
chains:
- id: 43113
name: 'Avalanche Fuji Testnet'
currencyName: 'Fuji AVAX'
currencySymbol: 'AVAX'
currencyDecimals: 18
rpc: 'https://api.avax-test.network/ext/bc/C/rpc' # <-- TODO: Replace with your own reliable RPC URL
testnet: true
- id: 421614
name: 'Arbitrum Sepolia'
currencyName: 'Arbitrum Sepolia Ether'
currencySymbol: 'ETH'
currencyDecimals: 18
rpc: 'https://sepolia-rollup.arbitrum.io/rpc' # <-- TODO: Replace with your own reliable RPC URL
testnet: true

# The addresses of the official Chainlink Verifier contracts on each network.
verifierAddresses:
- chainId: 43113
address: '0x2bf612C65f5a4d388E687948bb2CF842FFb8aBB3'
- chainId: 421614
address: '0x2ff010DEbC1297f19579B4246cad07bd24F2488A'

# --- On-Chain Target Configurations ---

# This section defines which smart contracts to call for each feed on each chain.
# You can have multiple targets for the same feed.
targetChains:
- chainId: 43113 # Target is on Avalanche Fuji
targetContracts:
# This configuration sends ETH/USD data to a contract on Fuji
- feedId: '0x000359843a543ee2fe414dc14c7e7920ef10f4372990b79d6361cdc0dd1ba782'
# TODO: Replace with the address of your deployed contract on Fuji
address: '0xYourDataStreamsFeedContractOnFuji' # <-- CHANGED
# The name of the function to call on your smart contract
functionName: 'verifyAndUpdateReport'
# The arguments the Transmitter should prepare and send to the function
# • rawReport = the unverified payload from the Data Streams websocket
# • parameterPayload = abi.encode(address feeToken) – produced automatically by the transmitter
functionArgs: ['rawReport', 'parameterPayload']
# The ABI for the target function, required to encode the transaction
abi:
- name: 'verifyAndUpdateReport'
type: 'function'
stateMutability: 'nonpayable'
inputs:
- { "internalType": "bytes", "name": "unverifiedReportData", "type": "bytes" }
- { "internalType": "bytes", "name": "parameterPayload", "type": "bytes" }
outputs: []
# Off-chain verification must run so keep skipVerify false (default)
skipVerify: false
18 changes: 16 additions & 2 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,27 @@ services:
- DATASTREAMS_CLIENT_SECRET=${DATASTREAMS_CLIENT_SECRET}
volumes:
- ./logs:/transmitter/logs
- ./config.yml:/transmitter/config.yml
depends_on:
- transmitter-redis

transmitter-redis:
image: redis:latest
command: redis-server --save 60 1 --appendonly yes --requirepass ${REDIS_PASSWORD}
image: redis/redis-stack:latest
container_name: transmitter-redis
ports:
- '6379:6379'
volumes:
- ./data:/data

command: >
sh -c "
if [ -n \"$REDIS_PASSWORD\" ]; then
exec redis-stack-server --requirepass \"$REDIS_PASSWORD\" --save 60 1 --appendonly yes
else
exec redis-stack-server --save 60 1 --appendonly yes
fi
"

networks:
default:
driver: bridge
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
"cron": "^3.5.0",
"cron-parser": "^5.0.3",
"cross-env": "^7.0.3",
"dotenv": "^16.5.0",
"express": "^4.19.2",
"ioredis": "^5.5.0",
"ioredis-mock": "^8.9.0",
Expand Down
6 changes: 6 additions & 0 deletions server/services/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -382,6 +382,8 @@ export async function verifyReport(report: StreamReport) {
verifiedReportData
);
const verifiedReport: ReportV3 = {
reportVersion,
verifiedReport: verifiedReportData as Hex,
feedId,
validFromTimestamp,
observationsTimestamp,
Expand All @@ -392,6 +394,7 @@ export async function verifyReport(report: StreamReport) {
bid,
ask,
rawReport: report.rawReport,
parameterPayload: feeTokenAddressEncoded as Hex,
};
logger.info('✅ Report verified', { verifiedReport });
return verifiedReport;
Expand Down Expand Up @@ -420,6 +423,8 @@ export async function verifyReport(report: StreamReport) {
verifiedReportData
);
const verifiedReport: ReportV4 = {
reportVersion,
verifiedReport: verifiedReportData as Hex,
feedId,
validFromTimestamp,
observationsTimestamp,
Expand All @@ -429,6 +434,7 @@ export async function verifyReport(report: StreamReport) {
price,
marketStatus,
rawReport: report.rawReport,
parameterPayload: feeTokenAddressEncoded as Hex,
};
logger.info('✅ Report verified', { verifiedReport });
return verifiedReport;
Expand Down
14 changes: 14 additions & 0 deletions server/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ export type StreamReport = Report & {
};

export type ReportV3 = {
reportVersion: number;
verifiedReport: Hex;
feedId: Hex;
validFromTimestamp: number;
observationsTimestamp: number;
Expand All @@ -22,9 +24,16 @@ export type ReportV3 = {
bid: bigint;
ask: bigint;
rawReport: Hex;
/**
* Encoded parameter payload (e.g. LINK token address) that must be passed
* as the second argument to `verifyAndUpdateReport`.
*/
parameterPayload: Hex;
};

export type ReportV4 = {
reportVersion: number;
verifiedReport: Hex;
feedId: Hex;
validFromTimestamp: number;
observationsTimestamp: number;
Expand All @@ -34,6 +43,11 @@ export type ReportV4 = {
price: bigint;
marketStatus: number;
rawReport: Hex;
/**
* Encoded parameter payload (e.g. LINK token address) that must be passed
* as the second argument to `verifyAndUpdateReport`.
*/
parameterPayload: Hex;
};

export type Feed = { name: string; feedId: string };
Expand Down