Skip to content
Testnet only: a reference implementation, not a production service.Tokens and bank transfers here are simulated and have no value.

Verify the transparency report

The supply and reserve figures are published as signed JSON. Anyone can check them without trusting this site: the issuer signs each report with a key that is published in stellar.toml, and the signature covers the report's exact bytes. Change one byte and the signature stops verifying.

This page walks through the check. It is the same code the test suite runs in CI against a simulated chain, so the steps stay honest.

Why it is signed

A number on a web page proves nothing on its own. A signature does: it ties the figure to a key the issuer publishes, and it lets you detect any later edit. The report key is separate from the Stellar account keys, so signing a report never touches a key that can move funds. The key is published in the SEP-1 stellar.toml, the same document a wallet reads to learn the asset's home domain.

1. Fetch the report

GET /v1/public/supply/{code} and GET /v1/public/reserves/{code} need no authentication (see the API reference). The response body is the report; the detached JWS is in the x-jws-signature response header. Keep the body byte-for-byte: the signature is over those bytes.

# Save the body and the response headers exactly as sent.
curl -sS -D report.headers -o report.json \
  https://issuer.minimumviable.partners/v1/public/reserves/USDX

# The detached JWS is the value of x-jws-signature.
grep -i '^x-jws-signature:' report.headers

The JWS is compact and detached: header..signature, with an empty middle segment where the payload would be. The payload is the report body you already saved.

2. Get the signing key from stellar.toml

Fetch the toml from the issuer's /.well-known/stellar.toml. Its REPORT_SIGNING_KEY line carries the Ed25519 public key as base64url of the raw 32 bytes.

curl -sS https://issuer.minimumviable.partners/.well-known/stellar.toml -o stellar.toml
grep '^REPORT_SIGNING_KEY' stellar.toml

Fetching the key from the same origin only helps if you trust the origin, so the point is the key is published and stable: you can pin it, compare it across fetches, or read it from a mirror.

3. Verify the detached JWS

Reattach the saved body to the detached JWS and check the signature with the published key. In your project install the jose library and import its verification helpers from "jose"; in this repository the function below imports the same helpers from a re-export, so the page, the test and the guide share one source. The function returns the parsed report only after the signature verifies.

// DX-07 (CONTRACT §13): verify the signed supply or reserve report without trusting the site.
//
// Fetch three public things — the report body, its detached JWS from the `x-jws-signature`
// header, and the issuer's stellar.toml — then check the JWS against `REPORT_SIGNING_KEY` from
// the toml. The signature covers the exact report bytes, so parse only after it verifies.
//
// In your own project, install `jose` and import from "jose". Here the same names are
// re-exported from the repo's core package so this guide, the page and the test share one source.
import { flattenedVerify, importJWK } from "../../../../core/src/jose.ts";

export interface VerifiedReport {
  object: string;
  asset: string;
  supply?: { total: string; customer_held: string; buffer: string; frozen: string };
  reserves?: { currency: string; total: string };
  [key: string]: unknown;
}

/** The value of `REPORT_SIGNING_KEY` in a SEP-1 stellar.toml, or undefined when it is absent. */
export function reportSigningKey(tomlText: string): string | undefined {
  const line = /^\s*REPORT_SIGNING_KEY\s*=\s*"([^"]+)"\s*$/m.exec(tomlText);
  return line?.[1];
}

/**
 * Verify the detached EdDSA JWS over `report` with the key published in `tomlText`, and return
 * the parsed report. Throws when the toml has no key, the JWS is malformed, or the signature does
 * not match — which is exactly what a tampered body produces.
 */
export async function verifyReport(report: string, jws: string, tomlText: string): Promise<VerifiedReport> {
  const publicKey = reportSigningKey(tomlText);
  if (!publicKey) throw new Error("stellar.toml does not publish REPORT_SIGNING_KEY");
  const [protectedHeader, , signature] = jws.split(".");
  if (!protectedHeader || !signature) throw new Error("the report is not a detached JWS");
  const { payload } = await flattenedVerify(
    // The signer's detached JWS is `header..signature`; jose needs the base64url payload it left out.
    { protected: protectedHeader, signature, payload: Buffer.from(report, "utf8").toString("base64url") },
    await importJWK({ kty: "OKP", crv: "Ed25519", x: publicKey }, "EdDSA"),
  );
  return JSON.parse(new TextDecoder().decode(payload)) as VerifiedReport;
}

A report whose body was changed after signing fails here with a signature verification error, and so does a toml that carries a key from another key pair. That failure is the whole point: it is what "tamper-evident" means.

4. Compare supply with the chain

The signature proves the report is the issuer's; the last step is to compare its numbers with the chain itself. The report's supply.total is the token amount the issuer believes is on chain. Look up the asset on Horizon or Stellar Expert using the asset_issuer the report names, and read the asset's total amount. The two should agree: the report's supply is the on-chain supply of the token.

Supply is computed from the watcher's replay of mints and burns, and each signed snapshot pins a ledger sequence. When the two disagree, the snapshot's ledger_sequence tells you which ledger the issuer was looking at, so you can check the same point in chain history.

The reserve side is off-chain by nature: reserves.sources lists each reserve source, its amount, how much counts toward headroom, and its freshness. A source older than its max_age_seconds counts as zero, so a stale figure can only lower the reserves you can verify.

Edit on GitHub