Skip to content

npow/ordisat

Repository files navigation

ordisat

CI License: MIT

Query any Bitcoin address and get its BTC balance, Rune tokens, and Ordinals inscriptions in a single API call.

The problem

Bitcoin's ecosystem has fragmented into at least four data layers — mainnet UTXOs (mempool.space), Ordinals inscriptions (Ordiscan), Runes fungible tokens (BestInSlot), and L2 activity — each with its own specialized explorer and incompatible API schema. A DeFi protocol building on Runes can't answer "what is the Runes token distribution for ticker X?" without combining three separate APIs that disagree on data formats. No existing tool maintains the semantic link between a Runes token, the UTXO it lives on, the inscription that may also be on that UTXO, and the mempool status of the transaction that last touched it.

Quick start

# If running on chainwatch, tunnel API locally first:
# ssh -N -L 3300:127.0.0.1:3300 root@chainwatch

# Health
curl http://127.0.0.1:3300/health

# Unified address lookup (real non-empty sample)
curl "http://127.0.0.1:3300/v1/addresses/bc1p9fh9y93vemenx7dx6ugv588yv7z8qxun6t05tttxd0z7ujf20f5qa5ntx3?include=utxo_breakdown"

# Rune analytics + oracle view
curl "http://127.0.0.1:3300/v1/runes/880005:828"
curl "http://127.0.0.1:3300/v1/runes/880005:828/stacks-oracle"

Usage

Unified address lookup

Get a wallet's complete Bitcoin position — BTC balance, all Rune token balances (with the UTXOs they live on), and all Ordinals inscriptions — in one request:

GET /v1/addresses/{bitcoin_address}?include=utxos,inscriptions,runes,mempool
{
  "address": "bc1p...",
  "summary": {
    "btc_balance_sats": 8200000,
    "utxo_count": 12,
    "inscription_count": 4,
    "runes_held": 3
  },
  "runes_balances": [
    {
      "rune_id": "840000:3",
      "ticker": "UNCOMMON•GOODS",
      "amount": "1000",
      "utxo_txid": "abc123...",
      "utxo_vout": 0
    }
  ]
}

Runes token analytics

Holder concentration, gini coefficient, mint progress, and 24h transfer volume for any Rune:

GET /v1/runes/840000:3
{
  "rune_id": "840000:3",
  "ticker": "UNCOMMON•GOODS",
  "supply": { "minted": "284000000", "circulating": "283818000", "mint_progress_pct": 83.2 },
  "holders": { "total": 182000, "top_10_pct": 0.28, "gini_coefficient": 0.71 },
  "transfers_24h": 4821
}

Stacks oracle — Rune holder data for Clarity contracts

Returns holder distribution in a format Stacks DeFi contracts can consume directly:

GET /v1/runes/{rune_id}/stacks-oracle

WebSocket streaming is out of scope for the grant MVP.

How it works

Ordisat runs a Bitcoin full node and processes every block through three parallel indexers:

  • UTXO indexer — tracks the full UTXO set as inputs are spent and outputs are created
  • Runes indexer — decodes OP_RETURN Runestones (LEB128 encoding), tracks UTXO-level balances and transfer history; Runes balances are UTXO-attached, not account-attached
  • Ordinals indexer — tracks inscription genesis, transfers, and sat rarity from taproot witness data

All state lands in PostgreSQL. The REST API queries the DB directly with no live Bitcoin RPC calls in the hot path — address queries hit a single indexed rune_balances row.

See PRD.md for the full architecture, data model, and API specification.

Self-hosting

Ordisat ships as a Docker Compose stack now:

cp .env.example .env
# edit .env and set strong BITCOIN_RPC_PASS / POSTGRES_PASSWORD
docker compose up -d --build

Services:

  • bitcoind (Bitcoin Core)
  • postgres (state store)
  • indexer (checkpoint sync + runes processing)
  • api (loopback by default at 127.0.0.1:3300)

For endpoint and cross-check results, see VALIDATION.md.

Development

See MEMORY.md for the implementation handoff guide — build order, architecture decisions, and the minimum build needed to unlock grant funding.

License

MIT

About

Query Bitcoin addresses, Runes tokens, and Ordinals inscriptions in one API call

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages