> ## Documentation Index
> Fetch the complete documentation index at: https://charli3-js-bc690dc5.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Charli3 class

> The single entry point for reading and refreshing on-chain prices.

`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.

```ts theme={null}
import { Charli3 } from "charli3-js";

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

## Constructor

```ts theme={null}
new Charli3(config: Charli3Config)
```

| Field     | Type                     | Required | Notes                                                                    |
| --------- | ------------------------ | -------- | ------------------------------------------------------------------------ |
| `network` | `"preprod" \| "mainnet"` | yes      | Picks the preset (feeds, nodes, addresses, reference script).            |
| `kupoUrl` | `string`                 | no       | Override the baked-in Kupo endpoint with your own indexer if you prefer. |

## Methods

### getOdvReference

```ts theme={null}
c3.getOdvReference(pair: string): Promise<OracleReference>
```

The main read. Returns the current on-chain price for one pair, decoded and ready to use.

```ts theme={null}
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

```ts theme={null}
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.

```ts theme={null}
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:**

| Field                      | Type           | Notes                                                                  |
| -------------------------- | -------------- | ---------------------------------------------------------------------- |
| `txHash`                   | `string?`      | Submitted tx hash. Undefined if the build produced no tx (error path). |
| `build.medianValue`        | `bigint`       | The 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

```ts theme={null}
c3.getAllPrices(): Promise<PriceData[]>
```

Reads every feed listed in the preset in parallel. Handy for a dashboard row of other pairs.

```ts theme={null}
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

```ts theme={null}
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.

```ts theme={null}
const feeds = await c3.collectFeeds("ADA/USD");
feeds.entries.forEach((e) => {
  console.log(e.node.rootUrl, e.value, e.signatureValid);
});
```

### listFeeds / listOdvFeeds

```ts theme={null}
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

```ts theme={null}
interface OracleReference {
  pair: string;
  price: PriceData;
  outRef: OutRef;
}
```

### PriceData

```ts theme={null}
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

```ts theme={null}
interface OutRef {
  txHash: string;
  outputIndex: number;
}
```

Build a Cardanoscan link with:

```ts theme={null}
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

<AccordionGroup>
  <Accordion title="No UTXO found at ODV address">
    Either the feed has never been posted (rare) or Kupo is slow. Retry in a few seconds, or pass your own `kupoUrl`.
  </Accordion>

  <Accordion title="Aggregate build failed: not enough feeds">
    Some oracle nodes were offline, so the SDK could not collect enough feeds. Usually temporary, the nodes recover in a few minutes.
  </Accordion>

  <Accordion title="Submit failed with on-chain error">
    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.
  </Accordion>
</AccordionGroup>
