Track a live crypto price using Wormhole Queries. Server calls the Wormhole Query Proxy, verifies a Guardian-signed response, decodes a real price, and the UI shows:
- Price (scaled & formatted)
- Last price update (absolute + relative)
- Query block (anchor block for the call)
live-crypto-prices/
├─ public/
├─ src/
│ ├─ app/
│ │ ├─ api/
│ │ │ └─ queries/
│ │ │ └─ route.ts
│ │ ├─ favicon.ico
│ │ ├─ globals.css
│ │ ├─ layout.tsx
│ │ └─ page.tsx
│ ├─ components/
│ │ └─ PriceWidget.tsx
│ └─ lib/
│ ├─ queries/
│ │ ├─ buildRequest.tsx
│ │ ├─ client.ts
│ │ └─ decode.ts
│ ├─ config.ts
│ └─ types.ts
├─ .env.example
├─ .env.local
├─ .gitignore
├─ eslint.config.mjs
├─ next-env.d.ts
├─ next.config.ts
├─ package-lock.json
├─ package.json
├─ postcss.config.mjs
├─ README.md
└─ tsconfig.json
This app reads a live price from the Witnet Price Router by calling latestPrice(bytes4). It utilizes Wormhole Queries to execute the call, anchored to a specific block via the Query Proxy, and returns a Guardian-signed result. We decode the tuple, scale the value, and display the price, the feed’s own “last update” timestamp, and the anchor block, providing you with an integrity-attested result that’s also suitable for on-chain verification.
-
Clone this repo and install dependencies:
git clone https://github.com/martin0995/live-crypto-prices.git cd live-crypto-prices npm install -
Create a
.env.localfile in the root with the following vars:# Wormhole Query Proxy (use testnet by default per docs) QUERY_URL=https://testnet.query.wormhole.com/v1/query # or https://query.wormhole.com/v1/query QUERIES_API_KEY=INSERT_API_KEY # Chain & RPC (HTTP) WORMHOLE_CHAIN_ID=10003 # Arbitrum Sepolia (Wormhole chain id) RPC_URL=https://arbitrum-sepolia.drpc.org # Arbitrum Sepolia RPC # Witnet Price Router on Arbitrum Sepolia (publicly documented) CALL_TO=0x1111AbA2164AcdC6D291b08DfB374280035E1111 # Feed ID4 for ETH/USD (6 decimals) on Witnet Router FEED_ID4=0x3d15f701 FEED_DECIMALS=6 FEED_HEARTBEAT_SEC=86400
-
Start the dev server:
npm run dev
- Server encodes
latestPrice(bytes4)with yourFEED_ID4and fetches the latest block fromRPC_URL. - Builds an
EthCallQueryRequest(anchored to that block) targetingCALL_TO. - POSTs the serialized request to the Query Proxy with header
X-API-Key. - Parses the Guardian-signed response, decodes the tuple (value, timestamp, drTxHash, status), and scales value by
FEED_DECIMALS. - Returns
{ price, updatedAt, blockNumber, … }to the UI. - UI displays the price, “last price update” (absolute + relative), and the anchor block. Polls every ~30s with no overlapping requests.
- “Last price update” is the oracle’s own timestamp (not “now”). Prices change when the feed updates (deviation/heartbeat), not on every block.
- Stale badge appears when
now − updatedAt > FEED_HEARTBEAT_SEC. - The feed-specific status code is not interpreted in the UI (kept vendor-agnostic).