Price oracle
Heads up
Testnet only. The feed verifies RedStone's signed price packages with RedStone's consumer code, which is licensed BUSL-1.1 (non-production use). A production price source will be announced before mainnet. Do not secure real value with this feed.
GullyChain has no push oracle yet. Parent-chain feeds (Chainlink, Pyth, API3 on Arbitrum Sepolia) cannot be read from an L3 contract in the same transaction, so the testnet runs RedStone in pull mode: the transaction that needs a price carries RedStone's signed packages in its calldata, and the contract verifies them on chain.
#Contracts
| Contract | Address |
|---|---|
| GullyPriceFeed v1.0.0 (ETH, BTC, USDC) | 0x4c64c79ee59a83842a22c14712D66bfef48eb301 |
| ETH / USD adapter (AggregatorV3-style) | 0x732b8a31E19827B6c81db7a433fd120e302862c8 |
| BTC / USD adapter | 0xE7Df3ec99314C0716510DAcd5B61a089Cf95130c |
| USDC / USD adapter | 0x7cEbF1d241165302b9a0bc2CFD24020ACB7AE44e |
- Prices have 8 decimals. Packages must come from RedStone's primary production data service, signed by at least 3 signers, and be at most 60 seconds old.
- Feed ids are
bytes32("ETH"),bytes32("BTC")andbytes32("USDC").
#Reading a price
Stored price. updatePrices() verifies the packages in its calldata and stores them. Anyone can call it. latestPrice(feedId) returns the last stored (value, timestamp, round), and each adapter exposes it as latestRoundData() for code written against Chainlink's AggregatorV3Interface.
Note
Nobody updates the stored price on a schedule. It moves only when a transaction calls updatePrices(), so always check updatedAt (or use priceIfFresh) before you rely on it.
Fresh price in your transaction. livePrice(feedId) verifies the packages appended to the current call and returns the price without storing it. priceIfFresh(feedId, maxAge) returns the stored price, or reverts StalePrice if it is older than maxAge seconds.
From a browser or script, wrap the contract with RedStone's EVM connector so each call carries the packages:
// @redstone-finance/evm-connector 0.9.0 (1.0.0 does not install from npm)
import { WrapperBuilder } from "@redstone-finance/evm-connector";
const wrapped = WrapperBuilder.wrap(priceFeed).usingDataService({
dataServiceId: "redstone-primary-prod",
uniqueSignersCount: 3,
dataPackagesIds: ["ETH", "BTC", "USDC"],
});
await wrapped.updatePrices(); // store fresh prices on chain
const [value] = await wrapped.livePrice(ETH); // or read without storingA contract that calls livePrice must pass the calldata suffix through, so it is simplest to have the user's transaction call updatePrices() first and then read priceIfFresh.
#USD prices for display
Gully web apps show USD values with the same-origin endpoint /price/eth on gullychain.xyz:
curl -s https://gullychain.xyz/price/eth
# {"symbol":"ETH","usd":2344.08,"source":"coingecko","ts":1793146177}It is the mainnet ETH price, cached, for display only. Test ETH has no value. See RPC and indexer API.
#Real-world assets
The TEST-TBILL demo asset has its own mock NAV oracle (AggregatorV3-style, 8 decimals) that accrues a fixed 4.5% a year. See Real-world assets.
#Before mainnet
Consumers code against the AggregatorV3 adapters, so the source behind them can change without touching DeepCore or a lending market. The plan for mainnet: a licensed RedStone feed or a push feed from Chainlink or API3, a staleness bound, and a second source to compare against.