Skip to main content
Charli3 is the class most apps will use. Construct it with a network name and call methods on it. No config file, no init step.
import { Charli3 } from "charli3-js";

const c3 = new Charli3({ network: "preprod" });

Constructor

new Charli3(config: Charli3Config)
FieldTypeRequiredNotes
network"preprod" | "mainnet"yesPicks the preset (feeds, nodes, addresses, reference script).
kupoUrlstringnoOverride the baked-in Kupo endpoint with your own indexer if you prefer.

Methods

getOdvReference

c3.getOdvReference(pair: string): Promise<OracleReference>
The main read. Returns the current on-chain price for one pair, decoded and ready to use.
const { pair, price, outRef } = await c3.getOdvReference("ADA/USD");

price.value;        // number, USD per ADA (for example 0.324812)
price.rawValue;     // bigint, in base units (1e6 precision)
price.createdAt;    // Date, when the datum was posted
price.expiresAt;    // Date, when it becomes stale
price.isExpired;    // boolean
outRef.txHash;      // string, the oracle UTXO tx hash
outRef.outputIndex; // number
If the price is stale, call submitRound2 to post a fresh one.

submitRound2

c3.submitRound2(
  lucid: LucidEvolution,
  pair: string,
  opts?: SubmitRound2Options,
): Promise<SubmitRound2Result>
Build, sign, and submit a Round 2 tx for one pair. This is how you refresh a stale price.
import { Lucid, Blockfrost } from "@lucid-evolution/lucid";
import { Charli3 } from "charli3-js";

const lucid = await Lucid(
  new Blockfrost("https://cardano-preprod.blockfrost.io/api/v0", PROJECT_ID),
  "Preprod",
);
lucid.selectWallet.fromSeed(SEED_PHRASE);

const c3 = new Charli3({ network: "preprod" });
const { txHash, build } = await c3.submitRound2(lucid, "ADA/USD");
await lucid.awaitTx(txHash);
Result fields:
FieldTypeNotes
txHashstring?Submitted tx hash. Undefined if the build produced no tx (error path).
build.medianValuebigintThe IQR-consensus median of the feeds used.
build.validityMs{start, end}The validity window written into the new datum.
build.rewardDistribution[]Which nodes’ feeds were aggregated and their reward share.
What the wallet pays for: the tx fee (about 1.5 tADA) plus the min-UTXO on the outputs. The SDK does not take a cut.

getAllPrices

c3.getAllPrices(): Promise<PriceData[]>
Reads every feed listed in the preset in parallel. Handy for a dashboard row of other pairs.
const prices = await c3.getAllPrices();
for (const p of prices) {
  console.log(p.pair, p.value, p.isExpired);
}
Feeds that fail to read (no UTXO found, bad datum) are still returned with rawValue: 0n and isExpired: true. That way you can render them without wrapping every entry in try/catch.

collectFeeds

c3.collectFeeds(
  pair: string,
  opts?: CollectFeedsOptions,
): Promise<AggregateFeedResult>
The Round 1 part of the refresh path, on its own. Fetches every oracle node’s latest signed message, checks the signature, and returns the set. Most apps do not need this. It is here if you want to build something custom on top.
const feeds = await c3.collectFeeds("ADA/USD");
feeds.entries.forEach((e) => {
  console.log(e.node.rootUrl, e.value, e.signatureValid);
});

listFeeds / listOdvFeeds

c3.listFeeds(): FeedPreset[]
c3.listOdvFeeds(): OdvFeedConfig[]
Return the list of pairs available in the current preset. Use them to render a picker or dropdown.

Types

OracleReference

interface OracleReference {
  pair: string;
  price: PriceData;
  outRef: OutRef;
}

PriceData

interface PriceData {
  pair: string;
  value: number;       // human-readable price (for example 0.324812)
  rawValue: bigint;    // base-unit price (1e6 precision)
  createdAt: Date;
  expiresAt: Date;
  isExpired: boolean;
}

OutRef

interface OutRef {
  txHash: string;
  outputIndex: number;
}
Build a Cardanoscan link with:
const url = `https://preprod.cardanoscan.io/transaction/${outRef.txHash}`;

Lower-level exports

charli3-js also exports the internals for anyone who needs to customise the Round 2 build:
  • OracleReader - raw datum reader
  • OracleNodeClient - HTTP client for oracle nodes
  • buildOdvTx, selectOracleUtxos, buildVkeyWitnessSetHex - Round 2 tx construction
  • buildAggregateMessage, medianBigInt - aggregate message builder + consensus helpers
  • consensusNodes, calculateRewardDistribution, calculateMinFeeAmount, IQR_APPLICABILITY_THRESHOLD - IQR internals
  • parseOracleSettings, parseAggState, buildAggStateDatumCbor - datum parsers and builders
  • verifyEd25519, verifyFeedSignature - signature verification helpers
  • PRESETS, PREPROD, MAINNET, getPreset - network presets
See src/index.ts in the repo for the full export list.

Errors you might see

Either the feed has never been posted (rare) or Kupo is slow. Retry in a few seconds, or pass your own kupoUrl.
Some oracle nodes were offline, so the SDK could not collect enough feeds. Usually temporary, the nodes recover in a few minutes.
The validator rejected the tx. Usual causes: stale validity window, a missing signature, or a wallet too low on tADA. The error message names which check failed.