ddd oracle · solana mainnet

Build with DDD_

The DDD ratio lives on-chain. Read stablecoin dominance directly from a Solana program account — no API keys, no middlemen, no rate limits.

Program XJjnewyPHcfb2ogMN1uAZGyt25XbKN2DWnm1GfAwddd
GitHub package @snowplow1337/ddd-oracle-sdk github packages ↗
AI agents SDK / Node Python Raw RPC Account layout Embed widget
ddd-oracle · live · solana mainnet
fetching summary account via JSON-RPC
Quick Start

install

Node.js / GitHub Packages

The @snowplow1337/ddd-oracle-sdk package handles account deserialization. Peer dependency on @solana/web3.js.

$ npm install @snowplow1337/ddd-oracle-sdk @solana/web3.js
javascript · node
// Read the DDD ratio from Solana mainnet
import('@snowplow1337/ddd-oracle-sdk').then(async ({ readDDD }) => {
  const { Connection } = await import('@solana/web3.js');
  const conn = new Connection('https://api.mainnet-beta.solana.com');

  const s = await readDDD(conn);

  console.log('DDD     = ', s.dddPercent.toFixed(4) + '%');
  console.log('Stables = $' + (Number(s.totalStablesUsd) / 1e9).toFixed(2) + 'B');
});
ESM only. The SDK ships as ES modules. Run with node --input-type=module or use dynamic import() as shown above.
agent http api

JSON for tools & bots

Versioned read-only endpoints backed by the same on-chain accounts as the SDK. Big integers are returned as strings. Send If-None-Match with the previous ETag to receive 304 Not Modified when the oracle sequence is unchanged.

Production: set SOLANA_RPC_URL in Netlify to your RPC provider. Optional ?debug=1 adds non-sensitive metadata.

zero dependencies

Python (stdlib only)

No packages required — uses only Python's standard library to query the Solana JSON-RPC and decode the binary account layout directly.

python 3 · stdlib only
import urllib.request, json, base64, struct
from datetime import datetime, timezone

# Base58 encoder (no deps)
ALPH = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
def b58(b):
    n = int.from_bytes(b, 'big'); s = ''
    while n: n, r = divmod(n, 58); s = ALPH[r] + s
    return '1' * (len(b) - len(b.lstrip(b'\x00'))) + s

# Fetch all 4 oracle accounts in one RPC call
req = urllib.request.Request(
    'https://api.mainnet-beta.solana.com',
    data=json.dumps({
        "jsonrpc": "2.0", "id": 1, "method": "getMultipleAccounts",
        "params": [[
            "8DAncbbsEkmsCCakHpNK5zf49XLcWQrryTBYdjgcLWxA",  # summary
            "97g36T1PV3anxCwxV6ue5MmF2A9HQHMsLqevckrkqjzK", # chains
            "GznG6pxbLPrqeYDd5aFXJcXK5SQ4wXifBtqjep34DNe",  # issuers
            "7FRjdiN489qMU2R8G2RumBPxRjanY9683gnXttKP4Fhv",  # tokens
        ], {"encoding": "base64"}],
    }).encode(),
    headers={'Content-Type': 'application/json'},
)
r = json.loads(urllib.request.urlopen(req).read())['result']['value']
s, c, ib, t = (base64.b64decode(a['data'][0]) for a in r)
ts_utc = lambda u: datetime.fromtimestamp(u, tz=timezone.utc).isoformat()

# Summary account layout (offset 73): ddd, stables, m2, ts, slot, seq
ddd, stb, m2, ts, slot, seq = struct.unpack_from('<QQQqQQ', s, 73)

print(f"DDD ratio : {ddd/1e4:.4f}%")
print(f"Stables   : ${stb/1e9:,.4f}B")
print(f"US M2     : ${m2/1e12:,.4f}T")
print(f"Updated   : {ts_utc(ts)}  (slot {slot:,})")
print(f"Sequence  : {seq}")
print(f"Admin     : {b58(s[8:40])}")

# Book accounts: chains, issuers, tokens
def read_book(name, d):
    cnt = d[25]
    print(f"\n--- {name}  (count={cnt}) ---")
    for k in range(cnt):
        o = 26 + k*29
        nm = d[o+1:o+1+d[o]].decode('utf-8', errors='replace')
        sup, pct = struct.unpack_from('<QI', d, o+17)
        print(f"  {k+1:<3} {nm:<16} ${sup/1e9:>10,.2f}B  {pct/1e6:.4f}%")

read_book('CHAINS',  c)
read_book('ISSUERS', ib)
read_book('TOKENS',  t)
On-Chain

accounts

Program & Account Addresses

The oracle writes to four accounts. Read any of them directly via getAccountInfo or getMultipleAccounts.

Account Role Address
program Oracle program ID XJjnewyPHcfb2ogMN1uAZGyt25XbKN2DWnm1GfAwddd
summary DDD ratio, totals, metadata 8DAncbbsEkmsCCakHpNK5zf49XLcWQrryTBYdjgcLWxA
chains Supply breakdown by chain 97g36T1PV3anxCwxV6ue5MmF2A9HQHMsLqevckrkqjzK
issuers Supply breakdown by issuer GznG6pxbLPrqeYDd5aFXJcXK5SQ4wXifBtqjep34DNe
tokens Supply breakdown by token 7FRjdiN489qMU2R8G2RumBPxRjanY9683gnXttKP4Fhv
Permissionless reads. These accounts are publicly readable on Solana mainnet. No wallet, no transaction, no fees — a single RPC getAccountInfo call is all you need.
raw rpc

Direct JSON-RPC Call

No SDK? Fetch the summary account directly with a standard Solana JSON-RPC call. Works with any HTTP client in any language.

bash · curl
$ curl -sS https://api.mainnet-beta.solana.com \
  -X POST \
  -H 'Content-Type: application/json' \
  -d '{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "getAccountInfo",
    "params": [
      "8DAncbbsEkmsCCakHpNK5zf49XLcWQrryTBYdjgcLWxA",
      { "encoding": "base64", "commitment": "confirmed" }
    ]
  }'

A successful response is not meant to be human-readable at a glance. The RPC returns JSON; inside it, result.value.data[0] is a base64-encoded binary account (the oracle’s on-chain layout). Solana does not turn that into “DDD = 1.39%” for you — you decode base64, then read little-endian fields starting at byte 73 (see the layout table below). The giant string that looks like gibberish is normal.

If a python3 -c "…" example printed nothing: double quotes make bash/zsh expand $ inside Python f-strings before Python runs. Use the version below with single-quoted -c '…' so stdin from curl still reaches Python (do not use curl | python3 <<'PY' — that replaces stdin and breaks the pipe).

bash · same curl, piped into python (readable numbers)
$ curl -sS 'https://api.mainnet-beta.solana.com' -X POST \
  -H 'Content-Type: application/json' \
  -d '{"jsonrpc":"2.0","id":1,"method":"getAccountInfo","params":["8DAncbbsEkmsCCakHpNK5zf49XLcWQrryTBYdjgcLWxA",{"encoding":"base64","commitment":"confirmed"}]}' \
| python3 -c '
import sys, json, base64, struct
from datetime import datetime, timezone
j = json.load(sys.stdin)
b64 = j["result"]["value"]["data"][0]
raw = base64.b64decode(b64)
ddd, stb, m2, ts, slot, seq = struct.unpack_from("<QQQqQQ", raw, 73)
print("DDD %     ", round(ddd / 1e4, 4))
print("Stables   ", "${:.2f}B".format(stb / 1e9))
print("US M2     ", "${:.2f}T".format(m2 / 1e12))
print("Updated   ", datetime.fromtimestamp(ts, tz=timezone.utc).strftime("%Y-%m-%d %H:%M UTC"))
print("Slot      ", slot)
print("Sequence  ", seq)
'
Dedicated RPC (QuickNode, etc.) — Replace the URL with yours. Include "commitment": "confirmed" in the params object; many setups need it. If nothing comes back: try the URL without a trailing slash; check QuickNode for an IP allowlist (your home IP must be allowed, or the call is rejected); run curl -sS (shown above) or add -v to see HTTP errors. On Windows, run the same command in Git Bash or WSL — PowerShell often breaks multiline JSON in single quotes.
Reference

binary layout

Summary Account Layout

The summary account is a packed binary struct. All integers are little-endian. The DDD ratio and supply figures start at byte offset 73.

Field Offset Type Description
discriminator 0 [u8; 8] Anchor account discriminator
admin 8 [u8; 32] Admin pubkey (base58 encoded)
authority 40 [u8; 32] Update authority pubkey
ddd_ratio 73 u64 LE DDD ratio × 10⁶. Divide by 10,000 for percentage.
e.g. 13900 → 1.3900%
total_stables_usd 81 u64 LE Total stablecoin supply in USD (raw cents × 10⁰).
e.g. 319000000000 → $319B
m2_usd 89 u64 LE US M2 money supply in USD.
e.g. 21700000000000 → $21.7T
data_timestamp 97 i64 LE Unix timestamp (UTC) of last data update
slot 105 u64 LE Solana slot of last write
sequence 113 u64 LE Monotonically increasing write counter. Use to detect stale reads.
chain_count 121 u8 Number of chain entries in the chains book
issuer_count 122 u8 Number of issuer entries in the issuers book
token_count 123 u8 Number of token entries in the tokens book
sdk return value

readDDD() Return Fields

The GitHub Packages SDK returns a parsed object. All bigint fields are raw on-chain values.

Field JS Type Description
dddPercent number DDD ratio as a percentage (e.g. 1.3900). Ready to display.
totalStablesUsd bigint Total stablecoin supply in USD (raw). Divide by 1e9 for billions.
m2Usd bigint US M2 in USD (raw). Divide by 1e12 for trillions.
dataTimestamp number Unix timestamp of last oracle update (UTC seconds).
slot bigint Solana slot when the data was last written.
sequence bigint Write sequence counter. Compare across reads to detect stale data.
good to know

Usage Notes

agent workflows

Using DDD with AI Agents

Humans often choose stablecoins based on preference, habit, brand trust or chain loyalty. Agents should use live data. DDD Oracle gives agents machine readable stablecoin signals, including digital dollar dominance, issuer concentration and chain distribution.

Treasury agents

Monitor issuer concentration before recommending stablecoin allocation.

Risk monitoring agents

Alert when TCD, HHI or top issuer share rises too high.

Payment planning agents

Use chain distribution now, and future velocity data later, to help choose where stablecoins should move.

Research agents

Read stablecoin data directly instead of scraping dashboards.

DDD is the decision layer. Jupiter and LI.FI are execution layers. DDD helps agents decide which stablecoin and chain make sense. Execution providers handle swaps, bridges, quotes and transactions.

DDD decides. Execution providers execute.

Website embed

iframe

Embed the live stablecoin tracker

Add the live stablecoin dominance ratio to any page. Real-time data from the DDD API on each load, no API key, free to use.

Preview

Embed code

<iframe src="https://digitaldollardominance.com/widget" width="380" height="100" frameborder="0"></iframe>
Live data
Pulls from the DDD API on every load. Always current.
No auth required
No API key, no rate limits, no tracking.
Lightweight
Single iframe, no dependencies. Under 2KB.
Dark & light themes
Works on dark dashboards and light sites. Add ?theme=light to the widget URL for a light background.

Questions? @dddtracker

Stay stable

Get the DDD Weekly.

One email a week. What moved the ratio, which chains grew, and which issuers gained ground.