Skip to content

Commit 1679c87

Browse files
docs: add Conflux-maintained Pyth-compatible oracle; flag Pyth sunset (#985)
* docs: add Conflux-maintained Pyth-compatible oracle; flag Pyth sunset Pyth Network is shutting down Conflux eSpace support on July 31, 2026. Adds a new oracle page documenting the Conflux-maintained drop-in replacement (conflux-fans/oracle-contracts): verified mainnet/testnet proxy addresses, supported price feed IDs (same IDs as Pyth for major assets), migration steps from Pyth's pull model, a consumer contract example, and trust/limitation notes (1h cadence, role-based updaters, UUPS upgradeability). Adds a sunset warning to the existing Pyth tutorial linking to the migration page. Contract addresses verified on-chain: proxies and implementations have code, and the mainnet oracle serves a current CFX/USD price. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * docs: correct oracle read semantics after on-chain verification Verified every read function against the deployed mainnet and testnet contracts. Corrections: - EMA prices are not available. Mainnet reverts with "EmaPrice not supported"; testnet does not revert and returns the spot price, so EMA-reading code passes on testnet and fails on mainnet. - getPrice(id) is getPriceNoOlderThan(id, getValidTimePeriod()), and that window (3600s) equals the publish cadence, so passing 3600 to getPriceNoOlderThan reproduces the same behaviour. - getValidTimePeriod() constrains only the deprecated getPrice / getEmaPrice, not getPriceNoOlderThan. - getPriceUnsafe applies no staleness check; publishTime is the only indication of age. - Reads revert with PriceFeedNotFound() / StalePrice() rather than returning sentinel values. - updatePriceFeeds / updatePriceFeedsIfNecessary / getUpdateFee are absent from the bytecode, so leftover calls revert. - USDT0 parity is a redemption property of the LayerZero OFT design, not a guarantee that the two trade identically. Also drops suitability and staleness-bound recommendations in favour of stating the mechanism, leaving those judgements to integrators. Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
1 parent a6528b5 commit 1679c87

2 files changed

Lines changed: 142 additions & 0 deletions

File tree

docs/espace/tutorials/oracle/Pyth/priceFeed.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,10 @@ tags: [Pyth, Oracles, Tutorial]
3636

3737
# Retrieve Price
3838

39+
:::danger Pyth Network sunset — July 31, 2026
40+
Pyth Network is **shutting down Conflux eSpace support on July 31, 2026**. After that date, the price feeds used in this tutorial will stop updating. A Conflux-maintained, Pyth-compatible replacement oracle is available — see [Conflux Price Oracle (Pyth-Compatible)](../conflux-oracle.md) for contract addresses and migration steps.
41+
:::
42+
3943
This tutorial will guide you through building a project on Conflux eSpace using Hardhat and retrieving the CFX price through the Pyth oracle.
4044

4145
## Prerequisites
Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
---
2+
sidebar_position: 2
3+
title: Conflux Price Oracle (Pyth-Compatible)
4+
description: Migrate from Pyth Network to the Conflux-maintained price oracle before the July 31, 2026 sunset
5+
keywords:
6+
- Conflux eSpace
7+
- Oracle
8+
- Price Feed
9+
- Pyth Network
10+
- Pyth sunset
11+
- Migration
12+
- Smart Contracts
13+
- Solidity
14+
- Price Feed ID
15+
- CFX Price
16+
- Mainnet
17+
- Testnet
18+
- Contract Addresses
19+
displayed_sidebar: eSpaceSidebar
20+
tags: [Oracles, Pyth, Migration]
21+
---
22+
23+
# Conflux Price Oracle (Pyth-Compatible)
24+
25+
:::danger Pyth Network sunset — July 31, 2026
26+
Pyth Network has announced it is **shutting down Conflux eSpace support on July 31, 2026**. Contracts that read prices from the Pyth contract on eSpace will stop receiving updates after that date.
27+
28+
A **Conflux-maintained, Pyth-compatible price oracle** is deployed as a drop-in read-side replacement. Existing integrators can migrate by pointing at a new contract address — the read functions and price feed IDs are unchanged for the major assets.
29+
:::
30+
31+
The replacement oracle is an on-chain price feed service maintained for the Conflux community, with source code at [conflux-fans/oracle-contracts](https://github.com/conflux-fans/oracle-contracts). Its spot price read API is compatible with the [Pyth SDK Solidity interface](https://github.com/pyth-network/pyth-sdk-solidity) (`getPriceUnsafe`, `getPriceNoOlderThan`), returning the same `PythStructs.Price` type. The EMA price functions are **not supported** — see [EMA prices are not available](#ema-prices-are-not-available).
32+
33+
## Contract addresses
34+
35+
| Network | Address |
36+
| --- | --- |
37+
| Conflux eSpace Mainnet (chain id 1030) | `0x5286BD91e2C79fE066926a15193C7e531bBF6750` |
38+
| Conflux eSpace Testnet (chain id 71) | `0x838c40B3904FAfBc21b670c97b0dFeE7D8D0a016` |
39+
40+
These are the proxy addresses — the oracle uses a UUPS upgradeable proxy, so the address stays stable across logic upgrades. Always integrate against the proxy.
41+
42+
## Supported price feeds
43+
44+
All feeds use `expo = -8` (consistent with Pyth crypto feeds): real price = `price × 10⁻⁸`.
45+
46+
| Asset | Price Feed ID (bytes32) | Update Frequency |
47+
| --- | --- | --- |
48+
| BTC/USD | `0xe62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b43` | 1h |
49+
| ETH/USD | `0xff61491a931112ddf1bd8147cd1b641375f79f5825126d665480874634fd0ace` | 1h |
50+
| USDT/USD | `0x2b89b9dc8fdf9f34709a5b106b472f0f39bb6ca9ce04b0fd7f2e971688e2e53b` | 1h |
51+
| USDC/USD | `0xeaa020c61cc479712813461ce153894a96a6c00b21ed0cfc2798d1f9a9e9c94a` | 1h |
52+
| CFX/USD | `0x8879170230c9603342f3837cf9a8e76c61791198fb1271bb2552c9af7b33c933` | 1h |
53+
| BNB/USD | `0x2f95862b045670cd22bee3114c39763a4a08beeb663b145d283c31d7d1101c4f` | 1h |
54+
| AxCNH/USD | `0x6412f0e5469e5ab64fccf0eea916ae6db2bcd56568daaaf583b3054d465e8e2d` | 1h |
55+
56+
Notes:
57+
58+
- The BTC, ETH, USDT, USDC, CFX, and BNB feed IDs are **the same IDs Pyth uses**, so consumers of those feeds do not need to change their stored feed IDs.
59+
- **There is no separate USDT0 feed.** USDT0 is the [LayerZero OFT form of USDT](https://docs.usdt0.to/), backed 1:1 by USDT locked on Ethereum and operated by Everdawn Labs under licence from Tether, so its price depends on that bridging layer in addition to USDT itself. Consumers that want a USDT0 price from this oracle read the USDT feed.
60+
- AxCNH/USD is a new feed, with its ID derived from `keccak256("ConfluxOracle.AxCNH/USD")`.
61+
62+
## EMA prices are not available
63+
64+
The oracle does not provide EMA (exponentially-weighted moving average) prices. Use the spot price functions — `getPriceUnsafe` and `getPriceNoOlderThan` — for all reads.
65+
66+
:::warning Mainnet and testnet behave differently
67+
On **mainnet**, `getEmaPriceUnsafe`, `getEmaPriceNoOlderThan`, and the deprecated `getEmaPrice` all revert with `EmaPrice not supported`, for every feed.
68+
69+
On **testnet**, these calls currently do *not* revert — they return the spot price. Do not treat that as EMA data, and do not rely on it: code that reads EMA prices will appear to work on testnet and then revert on mainnet.
70+
:::
71+
72+
If you are migrating a contract that reads `getEmaPrice*` from Pyth, switch those call sites to the spot price functions before pointing at this oracle.
73+
74+
## Migrating from Pyth
75+
76+
The oracle is push-based: authorized updaters publish prices on a roughly hourly schedule. This changes the integration in two ways compared to Pyth's pull model:
77+
78+
1. **Point your consumer contract at the new oracle address** (table above) instead of the Pyth contract address.
79+
2. **Remove the Hermes/update flow.** `updatePriceFeeds(bytes[])`, `updatePriceFeedsIfNecessary(...)`, and `getUpdateFee(bytes[])` are **not implemented on this contract at all** — there is no update fee and nothing to submit. Leftover calls to them will not silently no-op; they hit the fallback and revert. Delete any code that fetched update data from Hermes and attached a fee.
80+
81+
Reads take a staleness bound: `getPriceNoOlderThan` reverts with `StalePrice()` when the stored price is older than the bound you pass. Since prices are published about once per hour, a stored price is continuously ageing towards that interval between updates.
82+
83+
```solidity
84+
// SPDX-License-Identifier: MIT
85+
pragma solidity ^0.8.19;
86+
87+
import {IPyth} from "@pythnetwork/pyth-sdk-solidity/IPyth.sol";
88+
import {PythStructs} from "@pythnetwork/pyth-sdk-solidity/PythStructs.sol";
89+
90+
contract CfxPriceConsumer {
91+
IPyth public immutable oracle;
92+
/// @notice Staleness bound in seconds, chosen by the integrator.
93+
uint public immutable maxPriceAge;
94+
bytes32 public constant CFX_USD =
95+
0x8879170230c9603342f3837cf9a8e76c61791198fb1271bb2552c9af7b33c933;
96+
97+
constructor(address oracleAddress, uint maxPriceAge_) {
98+
oracle = IPyth(oracleAddress);
99+
maxPriceAge = maxPriceAge_;
100+
}
101+
102+
/// @notice Returns the CFX/USD price with expo -8 (price × 10⁻⁸ = USD).
103+
function getCfxPrice() external view returns (PythStructs.Price memory) {
104+
// Reverts with StalePrice() if the stored price is older than maxPriceAge.
105+
return oracle.getPriceNoOlderThan(CFX_USD, maxPriceAge);
106+
}
107+
}
108+
```
109+
110+
:::caution Do not use the deprecated `getPrice(bytes32)`
111+
`getPrice(id)` is exactly `getPriceNoOlderThan(id, getValidTimePeriod())` — it is the same code path, with the contract's own validity window supplied as the bound. That window is currently **3600 seconds**, the same ~1 hour cadence on which prices are published, so any delay in an update pushes the stored price past it and the call reverts with `StalePrice()` (`0x19abf40e`).
112+
113+
Because it is the same code path, passing `3600` to `getPriceNoOlderThan` reproduces the identical behaviour. What makes `getPriceNoOlderThan` usable is choosing a bound with headroom over the publish interval, not the function itself.
114+
:::
115+
116+
You can verify the oracle is live from the command line:
117+
118+
```bash
119+
cast call 0x5286BD91e2C79fE066926a15193C7e531bBF6750 \
120+
"getPriceUnsafe(bytes32)((int64,uint64,int32,uint256))" \
121+
0x8879170230c9603342f3837cf9a8e76c61791198fb1271bb2552c9af7b33c933 \
122+
--rpc-url https://evm.confluxrpc.com
123+
```
124+
125+
## Trust and limitations
126+
127+
Understand the differences from Pyth before relying on the oracle in production:
128+
129+
- **Update cadence is ~1 hour.** Prices can therefore be up to roughly an hour behind spot at any given moment.
130+
- **Push-based with role-based access control.** Prices are published by accounts holding `UPDATER_ROLE`, and the contract is upgradeable by its admin — a different trust model from Pyth's decentralized publisher network. Review the [contract source](https://github.com/conflux-fans/oracle-contracts) and its role holders as part of your own due diligence.
131+
- **Reads revert rather than returning sentinel values.** An unknown feed ID reverts with `PriceFeedNotFound()` (`0x14aebe68`); a price older than the bound passed to `getPriceNoOlderThan` reverts with `StalePrice()` (`0x19abf40e`). Neither returns a zero or a flag, so a failed read propagates as a transaction failure unless the caller explicitly catches it.
132+
- **`getPriceUnsafe` performs no staleness check.** It returns the stored price at any age, and reverts only when a feed has never been published. If the updater stops — key rotation, infrastructure outage, funding lapse — consumers keep reading the last published price indefinitely, with no revert and no on-chain signal. The returned `publishTime` is the only indication of age. `getPriceNoOlderThan` applies an age bound you supply and reverts with `StalePrice()` when exceeded.
133+
- **`getValidTimePeriod()` constrains only the deprecated `getPrice` / `getEmaPrice`.** For `getPriceNoOlderThan`, the bound you pass is the only value consulted — the contract's validity window plays no part.
134+
135+
## Further reading
136+
137+
- [conflux-fans/oracle-contracts](https://github.com/conflux-fans/oracle-contracts) — source, deployment scripts, and integration examples
138+
- [Pyth tutorial](./Pyth/priceFeed.md) — the original Pyth integration tutorial (applicable until the sunset date)

0 commit comments

Comments
 (0)