X1 Token Price Oracle βeta

Live
Connecting…

A decentralised, multi-feeder token price oracle deployed on X1.

X1 Oracle

What is X1 Oracle

A decentralised, multi-feeder token price oracle deployed on X1 • a high-performance, Solana Virtual Machine fork.
Prices are written on-chain as PriceFeed PDAs by multiple independent feeder nodes and are readable by any dApp or program at zero cost. The oracle is governed by an on-chain Registry that controls which feeder wallets may push prices and which token mints are tracked.

The X1 Oracle is currently in βeta on mainnet.

Get Your Token Listed

How to apply (requests not open yet)

To have your token tracked by the X1 Price Oracle, reach out to 𝕏 @ludolphus with the following information:

  • Token name — the ticker symbol (e.g. XNT, PEPE)
  • Mint address — the token's on-chain mint public key
Listing fee
25 XNT
due after approval
Contact @ludolphus on 𝕏

Token Prices

TokenLatest PriceXNT PriceFeedersAge
Loading…

Registered Feeders

Loading feeders…

Integration Guide

Program & Registry

KeyAddress
Program ID
Registry PDA

Account Layout Rust struct

Each price entry is stored in a PDA derived from the token mint and the feeder's public key. Multiple feeders produce independent entries for the same token.

FieldTypeDescription
mintPubkeyToken mint address
price_usdu64Price × 10⁹ (9 decimals)
timestampu64Unix time of last update
feederPubkeyFeeder that wrote this entry
token_nameStringHuman-readable name
seeds = ["price_feed", mint, feeder]

Read price in a dApp JavaScript

Derive the PDA for a known mint + feeder pair, fetch the account, and decode the 9-decimal fixed-point price. No special SDK required — plain @solana/web3.js is enough.

// Derive the PDA
const [pda] = PublicKey.findProgramAddressSync(
  [
    new TextEncoder().encode("price_feed"),
    mint.toBytes(),
    feeder.toBytes(),
  ],
  new PublicKey("PROGRAM_ID")
);

// Fetch and decode
const info = await connection.getAccountInfo(pda);
const d    = new DataView(info.data.buffer);
const price = d.getBigUint64(40, true); // offset 40
const ts    = d.getBigUint64(48, true); // offset 48

// Convert to float: divide by 1_000_000_000
const priceUsd = Number(price) / 1e9;

Get all feeders for a token JavaScript

Use getProgramAccounts filtered by the 8-byte PriceFeed discriminator to retrieve every entry on-chain, then filter client-side by mint.

// Anchor discriminator = SHA-256("account:PriceFeed")[0..8]
const disc = new Uint8Array(
  (await crypto.subtle.digest(
    "SHA-256",
    new TextEncoder().encode("account:PriceFeed")
  ))
).slice(0, 8);

const accounts = await connection
  .getProgramAccounts(PROGRAM_ID, {
    filters: [{ dataSize: 125 }]
  });

// Pick the freshest price across all feeders
const best = accounts
  .map(({ account }) => decode(account.data))
  .filter(f => f.mint === targetMint.toBase58())
  .sort((a, b) => Number(b.timestamp - a.timestamp))[0];

List approved tokens JavaScript

Fetch all ApprovedToken PDAs to discover which mints are tracked by the oracle. Each PDA is seeded from ["approved_token", mint] and is exactly 117 bytes.

// ApprovedToken::SPACE = 117 bytes
const accounts = await connection
  .getProgramAccounts(PROGRAM_ID, {
    filters: [{ dataSize: 117 }]
  });

accounts.forEach(({ account }) => {
  const d    = new Uint8Array(account.data);
  const view = new DataView(d.buffer);
  let off = 8; // skip discriminator
  const mint    = new PublicKey(d.slice(off, off+32)).toBase58(); off += 32;
  const nameLen = view.getUint32(off, true);                      off += 4;
  const name    = new TextDecoder().decode(d.slice(off, off+nameLen));
  console.log(`${name}: ${mint}`);
});
seeds = ["approved_token", mint]

Invoke get_price JavaScript

Call the get_price instruction to have the program validate freshness, feeder registration, and token approval on-chain. It emits a PriceQueried event with the verified price data. Useful when you need a guaranteed on-chain validation rather than a client-side check.

const disc = createHash("sha256")
  .update("global:get_price").digest().slice(0, 8);

const [priceFeed] = PublicKey.findProgramAddressSync(
  [Buffer.from("price_feed"), mint.toBytes(), feeder.toBytes()],
  PROGRAM_ID
);
const [registry] = PublicKey.findProgramAddressSync(
  [Buffer.from("registry")], PROGRAM_ID
);
const [approvedToken] = PublicKey.findProgramAddressSync(
  [Buffer.from("approved_token"), mint.toBytes()], PROGRAM_ID
);

const ix = new TransactionInstruction({
  programId: PROGRAM_ID,
  keys: [
    { pubkey: priceFeed,     isSigner: false, isWritable: false },
    { pubkey: registry,      isSigner: false, isWritable: false },
    { pubkey: approvedToken, isSigner: false, isWritable: false },
  ],
  data: Buffer.from(disc),
});

// Simulate (read-only — no SOL spent)
const result = await connection.simulateTransaction(tx);

// Parse PriceQueried event from logs
// Event disc = sha256("event:PriceQueried")[0..8]
// Layout: mint(32), feeder(32), price_usd(u64), timestamp(u64),
//         token_name(4+N), age_secs(u64)

Get median price JavaScript

Call get_median_price to compute the statistical median across all active feeders. The program filters out stale feeds (> 90 s) and feeds from deregistered feeders, then emits a MedianPriceQueried event.

// 1. Fetch all PriceFeed PDAs for the mint
const allFeeds = await connection.getProgramAccounts(PROGRAM_ID, {
  filters: [{ dataSize: 125 }],
});
const remaining = allFeeds
  .filter(({ account }) =>
    new PublicKey(account.data.slice(8, 40)).toBase58() === mint.toBase58()
  )
  .map(({ pubkey }) =>
    ({ pubkey, isSigner: false, isWritable: false })
  );

// 2. Build instruction — registry + approvedToken + remaining feeds
const disc = createHash("sha256")
  .update("global:get_median_price").digest().slice(0, 8);

const ix = new TransactionInstruction({
  programId: PROGRAM_ID,
  keys: [
    { pubkey: registry,      isSigner: false, isWritable: false },
    { pubkey: approvedToken, isSigner: false, isWritable: false },
    ...remaining,
  ],
  data: Buffer.from(disc),
});

// 3. Simulate, then parse MedianPriceQueried event from logs
// Event disc = sha256("event:MedianPriceQueried")[0..8]
// Layout: mint(32), token_name(4+N), median_price_usd(u64),
//         feeder_count(u8), timestamp(u64)

CPI from another program Anchor / Rust

Pass the PriceFeed PDA as an account to your program and deserialize it directly. No CPI call needed — just borrow the account data and check staleness.

// In your Anchor program:
#[account(
  seeds = [
    b"price_feed",
    mint.key().as_ref(),
    feeder.key().as_ref(),
  ],
  bump,
  seeds::program = ORACLE_PROGRAM_ID
)]
pub price_feed: Account<'info, PriceFeed>,

// Then in your instruction handler:
let feed = &ctx.accounts.price_feed;
let age  = clock.unix_timestamp as u64
             - feed.timestamp;
require!(age <= 90, ErrorCode::StalePrice);
let price = feed.price_usd; // u64, 9 decimals

PriceFeed account byte offsets Reference

Use these offsets when decoding raw account data with DataView in JavaScript or via AccountInfo in Rust. All integers are little-endian. Account size: 125 bytes.

OffsetSizeFieldDecode as
08 BDiscriminatorSkip (Anchor header)
832 Bmintnew PublicKey(data.slice(8, 40))
408 Bprice_usdDataView.getBigUint64(40, true) ÷ 10⁹
488 BtimestampDataView.getBigUint64(48, true) (Unix s)
5632 Bfeedernew PublicKey(data.slice(56, 88))
884 + N Btoken_nameu32 length prefix + UTF-8 bytes

FeederRecord account Reference

Each registered feeder has a FeederRecord PDA (seeds: ["feeder", feeder_pubkey], size: 41 bytes). Its existence on-chain proves the feeder is authorized. The update_price instruction validates this PDA rather than scanning the registry Vec. To check if a feeder is authorized, test whether the account exists:

const [feederRecord] = PublicKey.findProgramAddressSync(
  [Buffer.from("feeder"), feederPubkey.toBytes()],
  PROGRAM_ID
);
const authorized = (await connection.getAccountInfo(feederRecord)) !== null;
OffsetSizeFieldDecode as
08 BDiscriminatorSkip (Anchor header)
832 Bfeedernew PublicKey(data.slice(8, 40))
401 Bbumpdata[40]

ApprovedToken account byte offsets Reference

Each approved token mint has one PDA derived from ["approved_token", mint]. Use dataSize: 117 to filter these accounts. Note: offsets after name are variable because name length varies.

OffsetSizeFieldDecode as
08 BDiscriminatorSkip (Anchor header)
832 Bmintnew PublicKey(data.slice(8, 40))
404 + N Bnameu32 length prefix + UTF-8 (max 32 bytes)
40+4+N32 Badded_bynew PublicKey(data.slice(off, off+32))
72+N8 Badded_atDataView.getBigInt64(off, true) (Unix s)

Per-Feeder Data

#FeederPrice (USD)TimestampAgeStatus
Address copied to clipboard