Track: Carbon Markets & Emissions Transparency

Carbon registries are good at one guarantee: a retired credit stays retired, once. What happens next is not their job. After retirement, companies cite those credits in sustainability reports, product labels and marketing pages, and nothing compares those claims with each other. Two companies can each cite the same retired tonne, and each claim, read alone, looks perfectly fine.

OnceOnly is a proof of concept for checking exactly that. It reads the serial numbers behind a climate claim, turns them into the exact units they name, compares them with every other registered claim, and records the result on a smart contract that refuses to register the same units twice.

The insight: a claim is an interval

A Verra or Gold Standard serial number is not an opaque ID. It names an issuance batch and an inclusive range of unit numbers inside it. So "has anyone else claimed these credits?" becomes an exact question about overlapping intervals. A document hash or a string comparison can't answer it, because two different serials can share units.

There is a trap: unit numbers restart in every batch. When we audited Berkeley's public registry files on our own machine and ignored the batch, 11,631 adjacent pairs of Verra retirements looked like overlaps. Every one was between different batches. These are collisions of the wrong key, not duplicate claims and not fraud. So every range in OnceOnly is keyed by registry and batch.

What OnceOnly does

  1. Reads the evidence. A deterministic parser extracts serial numbers from the claim's documents. The browser fingerprints each file with SHA-256 before upload.
  2. Reconciles with a registry reference. Each range is checked against retirement and issuance records: fully retired, partly covered, issued but not retired, or not found. The public repository ships an invented reference (see the last section); real registry data is opt-in.
  3. Compares with every other claim. Ranges are checked against all active claims in the same batch. The Ruler draws the registry record, both claims and the exact shared units, and each finding says what it establishes and what it doesn't.
  4. Anchors on a smart contract. ClaimLedger keeps each batch's active ranges as a sorted list of disjoint intervals. Registration checks the new range's two neighbours and reverts with RangeOverlap(namespace, existingClaim, overlapStart, overlapEnd) if any unit is taken.
  5. Lets anyone verify. A claim's status and evidence fingerprint can be read straight from the contract, with no account. Anyone can open a challenge; a reviewer can uphold it (releasing the units) or dismiss it.

A walkthrough (synthetic data, reproducible)

A broker statement for Northwind Foods cites units 9,000,000,001–9,000,010,000 of synthetic batch VCS:9900001. Every check passes, and the claim is anchored as ledger claim #1.

A second statement, for Helios Apparel, cites 9,000,005,442–9,000,012,441: a different serial number, so a hash comparison sees nothing in common. OnceOnly blocks it: 4,559 units (9,000,005,442–9,000,010,000) already back Northwind's claim.

Then we send Helios's registration straight to the contract anyway, past the interface and the rules engine. ClaimLedger refuses it with RangeOverlap(…, 1, 9000005442, 9000010000), and the failed transaction is mined and recorded in the claim's audit trail. The rule lives in the contract, not in the UI.

All companies, documents and registry records in this example are invented and labelled SYNTHETIC. make reset, an API restart, make seed and make demo-check reproduce the exact state.

How it is built

Layer What it does
Smart contract (Solidity 0.8.24, Hardhat) ClaimLedger: per-batch sorted interval lists with an off-chain hint, so a wrong hint can only revert and never admits an overlap. Stores unit ranges, a salted claimant commitment, the evidence and report hashes, and minimal claim metadata (a hash of the claim reference, the registrar's address, the registration time and the status); never documents or names. Roles for registrar and reviewer; challenge, uphold, dismiss and withdraw
Backend (Python, FastAPI, SQLite, web3.py) Serial parser, registry reconciliation, a rules engine with named findings (DUP-001 for double use, plus registry, evidence and beneficiary checks), an evidence store keyed by SHA-256, and the chain adapter
Frontend (React, TypeScript, Vite, Tailwind) Intake, the Ruler, claim detail with provenance and audit trail, public verification and an evidence page
On chain vs off chain Documents, names and checks stay off chain. On chain: ranges, hashes, a salted commitment, the registrar's address, time and status, the ordering and the refusal

Optional AI-assisted extraction (Anthropic Claude, cross-checked against the parser) is implemented but disabled in the demo, and its accuracy has not been measured.

Evaluation: what we measured and what it means

What Result
270 synthetic claim pairs (9 case types), OnceOnly engine and contract precision 1.000, recall 1.000; the two agree on every pair
Same pairs, a baseline that matches exact serial strings or document hashes recall 0.333: it missed all 120 overlaps that weren't word-for-word copies
Same pairs, an interval check that ignores the batch precision 0.857: 30 false alarms on "same unit numbers, different batch" pairs
Gas to register one range into a batch holding 10 → 200 claims (local Hardhat chain) flat at 301,452 gas; the earlier linear-scan design grew to 832,608
Deterministic parser, 60 synthetic snippets in 8 layouts 65% of documents exact; block precision 0.979, recall 0.687

How to read this honestly. The pairs are invented, and their labels use the same interval definition as the engine, so the perfect score is expected by construction. It shows the implementation matches its specification and that simpler methods miss real patterns. It is not a measure of real-world accuracy or of how often double claims happen. The parser fails on table layouts and prose ranges, and OCR damage to a batch id can make it read the wrong batch; we found that in this evaluation and haven't fixed it yet. Everything here is reproducible with make eval and make gas.

Why it matters for the climate

If two public claims rely on the same retired tonne, its climate benefit is counted twice, and buyers, auditors and the public are misled about how much was actually offset. We don't know how often that happens: we found no documented public case, and a synthetic evaluation cannot tell. OnceOnly is a way to find out. It turns a question that today needs manual cross-checking of PDFs into an exact, auditable check. Any real-world impact would depend on adoption and on how common overlaps turn out to be.

Who it is for

  • Assurance providers and auditors checking corporate climate claims, who today see one client's claims at a time.
  • Corporate sustainability teams that want to show the units behind a claim are not also behind someone else's.
  • Registry and market researchers studying how retired credits are used after retirement.
  • Investigative journalists and watchdogs examining claims, with findings that state what they do and don't establish.

These are intended users; there are no pilots or partnerships yet.

Scaling and adoption

  • No registry change needed. OnceOnly sits downstream of retirement and needs only the serials companies already disclose.
  • Registry adapters. The parser and reference handle current and legacy Verra and Gold Standard formats. Other registries need a parser and an importer.
  • Data ingestion. make data builds the full Berkeley VROD index (493,518 retirement and issuance records in v2026-08) on the user's machine. Production would need live registry feeds.
  • Ledger. Registration cost stays flat as a batch fills. A real deployment would move to a public L2 or a permissioned consortium chain, with registrar keys per assurer, access controls and key custody.
  • Trust model. A shared contract gives competitors, auditors and registries one neutral, first-come ordering that no single operator can quietly edit. That is the reason for a blockchain here rather than a central database.

What is real, and what this prototype cannot do

  • Real: the code, the contract and its transactions (on a local development chain), and the measurements above.
  • Synthetic: every registry record, company, document and claim in the public demo. The repository contains no third-party registry data. Real data is opt-in: make data downloads the Berkeley Carbon Trading Project's Voluntary Registry Offsets Database (CC BY 4.0) to your own machine.
  • Status: a proof of concept on a local Hardhat chain (chain id 31337). There is no public deployment and no authentication; one registrar key is held by the backend.
  • What an overlap means: a signal to investigate, not proof of fraud. OnceOnly cannot tell which of two claims is right, whether a document is true, or anything about credit quality, additionality or permanence. It only prevents overlaps among claims registered on its own ledger.

Registries make sure a tonne is retired once. OnceOnly is our attempt to make sure it is claimed once, and to show, unit by unit, when it might not be.

Built With

Share this project:

Updates

Submission history