▌ TICKER BCH / USD · chipnet · docs
docsapigithub
Docs · BCH/USD ticker
chipnet

Decentralized BCH/USD ticker.

A 7-notary federation co-witnesses prices from 13 operator-diverse exchanges; any publisher relays them into a covenant on Bitcoin Cash that commits the per-cycle median on chain. No admin keys; the covenant is the only on-chain rule.

§01Overview

Ticker is a price feed that lives entirely on Bitcoin Cash chipnet. Every cycle (~60 s when funded) it emits a new on-chain commitment containing the median BCH/USD price witnessed during that cycle, signed by at least 7 distinct publishers. Consumers read the price either by spending a Ticker NFT in a tx (atomic on-chain) or by polling the JSON API.

There is no operator and no upgrade path. The Oracle covenant address is permanent for the life of the deploy; behavioural changes require minting a new covenant at a new address. The notary federation is the only off-chain trust assumption, and the design constrains what it can do.

The on-chain code, in full, is four CashScript covenants totalling ~1.7 KB of compiled bytecode: TLSNotaryGateway, Oracle, VerifiedAttestation, and Ticker. There are no proxies, no admin paths, no off-chain governance signers.

§02Architecture

Five roles, all replaceable:

RoleWhat it isHow many
NotaryStateless HTTP daemon holding one Schnorr key from the federation OR-list. Fetches the price from its assigned CEX over TLS and signs (serverName, sourceId, price, ts, cycleSeq).7 (federated, independent operators)
PublisherDaemon that requests notary signatures, mints VerifiedAttestation NFTs via the Gateway, then races to broadcast Oracle.update consuming peer VAs. No special role; anyone with chipnet funds can run one.13 (one per source; permissionless)
TLSNotaryGatewayCovenant that verifies a notary sig + binds the price to the pinned CN/SAN hash, then mints a VerifiedAttestation NFT. The mint is the only path through which fresh VAs enter the chain.1 covenant (M=7 OR-list, 13 source CN hashes)
VerifiedAttestation (VA)51-byte NFT carrying a single notary-witnessed price observation. Short-lived: consumed by the next Oracle.update or burned.~12-13 per cycle, in mempool
OracleSelf-perpetuating covenant. Reads ≥7 distinct VAs as inputs, position-checks the median, emits a new 19-byte state commitment + 2 Mutable Ticker NFTs.1 covenant (singleton UTXO)
TickerMutable NFT containing the latest commitment (17 bytes: version, seq, lastTs, price). Anyone-can-spend; self-replicates via the Ticker covenant. Consumers spend a Ticker to read the price atomically.4 minted per cycle, persist until consumed

Cycle flow

  1. Publisher polls the Oracle UTXO to learn the current prevTs and cycleSeq.
  2. Publisher requests a signature from one of the 7 notaries (round-robin) for its assigned source.
  3. Publisher builds + broadcasts a TLSNotaryGateway.mint tx. Gateway verifies the notary sig, the CN/SAN hash, the publisher sig, and that no extra NFTs are minted. Output: one immutable VerifiedAttestation NFT.
  4. Publisher waits up to ~25 s for peer VAs from other publishers to appear in mempool.
  5. Once ≥7 distinct-pubkeyHash VAs are seen for this cycleSeq, publisher builds Oracle.update consuming them all plus the Oracle UTXO, with the median price as the new commitment.
  6. First publisher to broadcast wins the cycle. Losing publishers' txs fail with txn-mempool-conflict and the cycle advances; they retry on the new tip.

Chain-state, not consensus

There is no central coordinator. Each publisher acts on what it sees in the chain + mempool. The Oracle UTXO is the single source of truth for cycle state; the rest is replication and racing.

§03Trust model

The trust root is the 7-key notary federation. Each notary independently fetches the price from its pinned CEX over TLS and signs (serverName, sourceId, price, ts, cycleSeq) with its Schnorr key. The Oracle covenant trusts these signatures and nothing else off-chain.

Publishers are interchangeable relays. They cannot fake notary sigs. They cannot alter the price. They cannot skip the quorum check. The only adversarial power a publisher has is to broadcast a different median if they assemble a different subset of VAs, but the covenant enforces position-checked median across N ≥ 7 distinct publishers, so a single publisher cannot bias the result.

Sources are operator-diverse. 13 endpoints: 7 USD-quoted spot markets (Kraken, Coinbase, Gemini, Binance.US, Bitstamp, Crypto.com, Bitfinex, EXMO, Independent Reserve — 4 US, 5 non-US), 2 USDC (OKX, KuCoin), 2 USDT (Bybit, HTX). No two share an operator family; no quote-currency dominates. A position-checked median absorbs ≤6 outliers without bias — both stablecoin clusters could depeg the same direction simultaneously and the median still lands in the 7-USD cluster.

The covenant is the only on-chain rule. It enforces:

  • ≥ 7 distinct publishers attest per cycle (the quorum ratchets up with active committee at ~50%, decays 10%/cycle if participation drops)
  • Notary-attested time is strictly forward, with a stride budget of [30 s, 7200 s] per cycle
  • Median is position-checked across the sorted VA prices, not computed off-chain and trusted
  • Distinct pubkeyHash160 across all VA inputs is the load-bearing anti-Sybil check
  • Gateway-minted VAs only — no synthetic VA inputs slip through
  • The 2 Mutable Ticker NFTs minted per cycle carry the same commitment shape; consumer covenants can pin the Oracle category to refuse impostor Tickers

No admin keys. No upgrade path. The Oracle UTXO chain is self-perpetuating. The only way to update behaviour is to deploy a new covenant at a new address — which observers will see as a fresh address with no history.

Federation collusion model

What a colluding majority (≥ 4 of 7) can do:

  • Fast-forward time within the 7200-second-per-cycle stride cap
  • Sign attestations for arbitrary prices the publishers will then propagate

What a colluding majority cannot do:

  • Rewind the cycleSeq counter — strictly monotonic in the covenant
  • Skip the ≥ 7 distinct-publisher quorum check — enforced in script
  • Hide a cycle from observers — every state transition is an on-chain tx
  • Run the chain faster than ~1 cycle/minute — notary throughput cap + publisher stride floor

With 7 keys held by independent operators, the bribery floor for 4-of-7 collusion is comparable to Wormhole-class bridges (~$1M+). Adding an 8th notary requires deploying a new Gateway covenant with the extended OR-list; the new address coexists alongside the old.

§04Reading the price

Two paths: on-chain atomic (recommended for dApps; spend a Ticker NFT as a tx input, read its commit) or JSON API (recommended for off-chain UIs; just GET /api/v1/price).

On-chain: spend a Ticker

Each cycle mints 2 Ticker NFTs at a fixed address (see § 07 Live contracts). They are mutable (capability 0x01), anyone-can-spend, and self-replicate via the Ticker covenant. A consumer's tx that spends a Ticker becomes a chain-descendant of the Oracle.update that produced it — BCH consensus refuses to confirm the consumer's tx in any chain that excludes the Oracle.update. That is co-finality, atomic by construction.

Minimal tx shape:

  • input[0] — your consumer contract
  • input[1] — Ticker NFT from the pinned Oracle category
  • output[0] — your business logic (payment, mint, settle…)
  • output[1] — identical Ticker re-emit (Ticker covenant enforces this, mutable cap, same commit)
  • output[2] — optional change

Read price and lastTs from tx.inputs[1].nftCommitment. The 17-byte layout is:

0x80 | seq(4) | lastTs(4) | medianPrice(8)

All multi-byte fields are little-endian. medianPrice / 10^8 = USD. Pin the Oracle category as a constructor arg (LE-reversed; see § 07) so an attacker cannot feed your contract a Ticker from a different oracle.

A working reference covenant — PriceGatedRelease (locks BCH, releases on price ≥ strike) — lives at contracts/v11/examples/ in the source tree, ~286 bytes compiled. Drop your own freshness gate on lastTs; avoid tx.locktime, that introduces a chain-MTP lag the v11 design was built to escape.

Off-chain: JSON API

$ curl -s https://usd.ticker.cash/api/v1/price

{
  "medianUsd": 344.94,
  "scale": "usd-e8",
  "scaledValue": "34494000000",
  "lastLocktime": 1779818724,
  "seq": 11,
  "activeCount": 7,
  "network": "chipnet",
  "stub": false
}

CORS is open; auth is none. stub: true means the API can't currently read the Oracle UTXO (e.g., Fulcrum unreachable) and the median is the last known cached value — don't trust prices when stub is true.

§05Operating a node

There are two kinds of node, both stateless and small. The unified binary scripts/ticker-node.ts wraps either or both.

Notary

A stateless HTTP daemon that holds one Schnorr key from the federation's 7-key OR-list. Exposes POST /sign; on each request fetches the current price from its assigned CEX, signs (serverName, sourceId, price, ts, cycleSeq), returns the signature. The federation's seven keys live independently — separate hosts, separate operators. Adding an 8th notary requires deploying a new Gateway covenant with the extended OR-list.

$ ticker-node --notary --slot 0   # listens on 127.0.0.1:8081

Publisher

A daemon that polls the Oracle UTXO, requests notary signatures for its assigned source, mints a VerifiedAttestation NFT via the Gateway, waits ~25 s for peer VAs from other publishers, then builds and broadcasts the Oracle.update tx consuming all VAs. First publisher to broadcast wins the cycle; losers retry on the new tip.

A publisher needs ~12 k sats per cycle (VA mint + Oracle.update share of fee + Ticker dust). No registration, no allowlist. Run as many publishers as you can fund — they compete to land the cycle, but they all contribute their attestation toward the median.

$ ticker-node --publisher --slot 0 \
  --notary-url https://notary-0.ticker.cash \
  --notary-url https://notary-1.ticker.cash \
  --notary-url https://notary-2.ticker.cash \
  --notary-url https://notary-3.ticker.cash \
  --notary-url https://notary-4.ticker.cash \
  --notary-url https://notary-5.ticker.cash \
  --notary-url https://notary-6.ticker.cash

Bundled deploy

Federation operators typically bundle both daemons for their slot (no HTTP roundtrip for own attestations); community operators run publisher-only.

$ ticker-node --notary --publisher --slot 0
Source code. Lives on github.com/toorik2/ticker.cash. The sanitised public mirror is in progress; until it lands, the chipnet deployment is the source of truth.

§06Parameters

Covenant constants. Values are baked into the bytecode at deploy time; changing any of them requires a new deploy at a new address.

ParameterValueNotes
Federation size (M)7OR-list of independent Schnorr keys. ⌈M/2⌉ = 4 keys required for majority bribery.
Source count (N)137 USD-quoted + 2 USDC + 2 USDT, operator-diverse. CN/SAN hash pinned in Gateway.
Quorum floor (Tfloor)7Minimum distinct publishers per cycle. Below this the Oracle covenant rejects.
Quorum ratchet~50% of recent activeThreshold = max(Tfloor, ⌈0.5 × oldActive⌉). Active count decays 10%/cycle when participation drops.
Cycle stride (min/max)30 s / 7200 sNotary-attested newTs must advance by ≥30 s and ≤7200 s from prevTs. Typical cycle ~60 s when funded.
Oracle commit length19 B0x60 | seq(4) | lastTs(4) | medianUsd(8) | activeCount(2)
Ticker commit length17 B0x80 | seq(4) | lastTs(4) | medianPrice(8)
VA commit length51 B0x70 | sourceId(2) | pkh(20) | expiry(4) | reserved(8) | price(8) | ts(4) | cycleSeq(4)
Tickers minted per cycle (K)2One Mutable Ticker NFT at outputs[1..3). Capacity per cycle ≈ K × (cycle_time / RTT).
Price scaleUSD × 108medianPrice / 1e8 = USD. Same scale as sats-per-BCH for symmetry.
NetworkchipnetBitcoin Cash testnet. Mainnet deploy follows after stability period.

§07Live contracts

Current chipnet deployment. Live addresses and the most recent commitment are on the home page sidebar.

  • TLSNotaryGateway — verifies notary sigs + mints VerifiedAttestations. Constructor pins 7 notary pubkeys + 13 source CN hashes.
  • Oracle — singleton covenant; consumes ≥ 7 VAs per cycle, emits new state commitment + 2 Ticker NFTs.
  • VerifiedAttestation — anyone-can-spend NFT carrying a single notary observation. Single-use; consumed by the next Oracle.update.
  • Ticker — mutable, self-replicating NFT carrying the latest 17-byte commitment. Consumers spend a Ticker to read the price atomically.

Source for all four covenants is at contracts/v11/ in the repo. Compiled artifacts are at contracts/v11/artifacts/.

§08JSON API

GET /api/v1/price — no auth, CORS open, returns the latest known Oracle state.

{
  "medianUsd": 344.94,         // float USD (medianPrice / 1e8)
  "scale": "usd-e8",           // u64 LE scale; multiply by 1e-8 for USD
  "scaledValue": "34494000000",// raw u64 from on-chain commit
  "lastLocktime": 1779818724,  // notary-attested ts of last cycle (unix seconds)
  "seq": 11,                   // monotonic cycle counter from Oracle commit
  "activeCount": 7,            // distinct publishers in last cycle (also the ratchet input)
  "network": "chipnet",        // chipnet | mainnet
  "stub": false                // true = backend couldn't read chain, value is cached; don't trust
}

Useful for status pages, alerting, and dashboards. For dApp consumption, prefer the on-chain Ticker path described in § 04.

usd.ticker.cash · docs · chipnet
github