Oracles (Stork)

Stork is the primary oracle integration for Horizen Chain. It is a pull oracle that delivers price data and other off-chain data feeds at sub-second latency, designed for use cases like perpetuals, lending protocols, and any application that requires fast, verifiable market data.
Unlike push oracles (which maintain feeds on-chain at all times), Stork operates on a consumer-driven model: feeds are not posted to the chain continuously. Instead, your application fetches the latest signed data off-chain and pushes it on-chain exactly when needed. This makes it highly cost-efficient: you only pay for the data updates your protocol actually uses.
How Stork Works on Horizen

Contract Addresses
| Network | Stork Contract Address |
|---|---|
| Mainnet Horizen | 0xacC0a0cF13571d30B4b8637996F5D6D774d4fd62 |
| Testnet Horizen | 0xacC0a0cF13571d30B4b8637996F5D6D774d4fd62 |
Both mainnet and testnet share the same Stork contract address on Horizen. Source: docs.stork.network/resources/contract-addresses/evm.
Integration Steps
Step 1: Fetch Data via the Stork REST API
Before pushing anything on-chain, fetch the latest signed price data from Stork's off-chain API. Each response contains a signed payload ready to be submitted directly to the on-chain contract.
API endpoint:
GET https://rest.jp.stork-oracle.network/v1/prices/latest?assets=<ASSET_ID>
Full REST API reference: docs.stork.network/api-reference/rest-api
Available asset IDs (e.g. BTCUSD, ETHUSD) are listed in the Stork Asset ID Registry.
Step 2: Push Data On-Chain
Once you have the signed payload, submit it to the Stork contract on Horizen using updateTemporalNumericValuesV1. This verifies the aggregator signature and stores the price on-chain.
interface IStork {
function updateTemporalNumericValuesV1(
StorkStructs.TemporalNumericValueInput[] calldata updateData
) external payable;
function getUpdateFeeV1(
StorkStructs.TemporalNumericValueInput[] calldata updateData
) external view returns (uint feeAmount);
}
Important: Always call getUpdateFeeV1 first to determine the required fee, then pass that value as msg.value when calling updateTemporalNumericValuesV1. Submitting without sufficient fee will revert with InsufficientFee.
// Get required fee
uint fee = stork.getUpdateFeeV1(updateData);
// Push signed data on-chain
stork.updateTemporalNumericValuesV1{value: fee}(updateData);
Tip: If your protocol uses multiple price feeds, batch them in a single
updateTemporalNumericValuesV1call. This saves gas and ensures all prices are updated atomically in the same block.
Step 3: Read Data On-Chain
Once a feed is updated on-chain, your smart contract can read it using getTemporalNumericValueV1. This function includes an automatic staleness check — it reverts with StaleValue if the stored price is older than the chain's configured freshness threshold.
interface IStork {
function getTemporalNumericValueV1(
bytes32 id
) external view returns (StorkStructs.TemporalNumericValue memory value);
}
struct TemporalNumericValue {
// Nanosecond-precision Unix timestamp of the price update
uint64 timestampNs;
// Price scaled to 18 decimal places
int192 quantizedValue;
}
Example — reading ETH/USD price in a Solidity contract:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
interface IStork {
struct TemporalNumericValue {
uint64 timestampNs;
int192 quantizedValue;
}
function getTemporalNumericValueV1(
bytes32 id
) external view returns (TemporalNumericValue memory value);
}
contract PriceConsumer {
IStork public immutable stork;
// ETHUSD feed ID (verify from Stork Asset Registry)
bytes32 public constant ETH_USD_ID =
0x59102b37de83bdda9f38ac8254e596f0d9ac61d2035c07936675e87342817160;
// Stork contract on Horizen mainnet: 0xacC0a0cF13571d30B4b8637996F5D6D774d4fd62
constructor(address _stork) {
stork = IStork(_stork);
}
function getEthPrice() external view returns (int192) {
IStork.TemporalNumericValue memory val =
stork.getTemporalNumericValueV1(ETH_USD_ID);
return val.quantizedValue; // 18 decimal places
}
}
For view functions where you want to implement custom staleness logic, use getTemporalNumericValueUnsafeV1 instead — it returns the stored value without reverting on staleness, allowing you to implement your own freshness checks.
Reference Links
| Resource | URL |
|---|---|
| Stork Documentation | https://docs.stork.network |
| Asset ID Registry | https://docs.stork.network/resources/asset-id-registry |
| REST API Reference | https://docs.stork.network/api-reference/rest-api |
| EVM Contract API | https://docs.stork.network/api-reference/contract-apis/evm |
| EVM SDK Example | https://github.com/Stork-Oracle/stork-external/tree/main/chains/evm/examples/stork |