One-Claim ledger
What the One-Claim Ledger is
One-Claim is EDMA’s global uniqueness ledger. It makes sure the same real-world evidence is monetized exactly once—across all routes and both marketplaces. When PoV says “this gate can pass,” One-Claim answers “has anyone already used these facts?” If yes, the action is refused. If no, we reserve the claim and finalize it atomically with the EMT (Trade) or settlement (Tokens). That is how we prevent double sales, re-wrapped credits, and paperwork reruns—at chain speed.
Rule that never moves: One-Claim is not up for vote or per-deal negotiation. It is a safety property of the rail.
The idea in one line
Build the claim key from the facts → reserve it → finalize it exactly once. Everything else is ergonomics.
How a claim key is built
We compute a claim_id from the smallest set of fields that uniquely identify the real event, plus the PoV hash that binds the dossier. The format is stable and public:
claim_id = keccak256(
chain_id,
lane, // TRADE or TOKENS
schema_id, // which PoV schema/checklist
claim_key_fields, // minimal identity for the event/unit
pov_hash // the canonical evidence commitment
)
Typical claim_key_fields (illustrative, not exhaustive):
Trade — On-Board & sealed: {BL_number, seal_number, hash(container_ids_sorted)}
Trade — Customs cleared: {customs_entry_number, country_code, importer_tax_id}
Trade — Arrival & DC QA: {ASN_number, DC_id, delivery_window}
Tokens — Energy 1 MWh: {device_id, start_ts, end_ts, quantity_Wh}
Tokens — Carbon 1 t: {program/registry, project_id, vintage, unit_serial}
We never rely on filenames or free-text. If any input changes, the key—and the PoV hash—change.
Reserve → finalize
One-Claim runs inside the PoV Gate so race conditions can’t leak value.
PoV PASS (tentative): The checklist matches; all counted attestations reference the same PoV hash.
RESERVE: The contract tries reserve(claim_id).
If the id is free, it is marked reserved by this tx for the next few EVM steps.
If it is already reserved/finalized, the call reverts with ONE_CLAIM_TAKEN.
FINALIZE: In the same transaction, we mint the EMT (Trade) or settle/retire (Tokens), flip Locked→Unlocked EDSD, post the fee line, and burn 50%. Then we mark the claim finalized.
Reservation never lives across blocks; there is no “hold.” It is atomic with the EMT/settlement or it doesn’t happen.
What happens on revocation, split, and replacement
Revocation after pass: The original claim remains in history; dependent future slices freeze. A replacement claim may be filed with a new PoV hash and a small link record replaces: old_claim_id. When the new gate passes, it finalizes; downstream releases resume. We never erase the old entry.
Split shipments: Each sub-lot gets its own claim_id (same BL/ASN family, different container subset). One-Claim prevents reusing the same container list across sub-lots.
Merges: Multiple sub-lot claim_ids can be referenced by a single downstream gate; the gate lists the children; One-Claim does not need a new id to merge—only to consume children exactly once.
Cancellations: A claim that never reached FINALIZE has no effect; a finalized claim that cannot deliver resolves using the MPA’s variance rules (partial pay / replacement / cancel & refund). One-Claim records the outcome; the id remains non-reusable.
Cross-market guarantees
A shipment’s On-Board claim cannot be reused to mint a token in Tokens.
A token serial that settled/retired in Tokens cannot be wrapped into a Trade milestone or relisted elsewhere on EDMA.
If you mirror to an external registry, we create a mirror record that maps external serials to the same claim lineage; One-Claim prevents re-wrapping and resale on re-import.
Short version: the same physical event cannot produce two independent cashflows on EDMA.
What operators feel
Before you pass a gate, the UI shows whether a proposed claim is free or taken, using the key preview and PoV hash.
When you submit, PoV and One-Claim run together; if the id is free, the EMT mints and the slice pays; if not, you see a clean ONE_CLAIM_TAKEN error.
If you split, the UI generates child claim_ids for each container subset.
If you replace, the submission references replaces: old_claim_id; downstream slices unfreeze when the replacement finalizes.
If a buyer blocks during the review window, that’s not a One-Claim event; it’s a gate pause (see B5).
API & webhooks
API
POST /v1/oneclaim/check — preview a claim_id for a given schema + key; returns FREE | FINALIZED | CONFLICT.
GET /v1/oneclaim/{claim_id} — status + lineage (replaces/replaced_by).
Gate helper endpoints (/v1/trade/proof/..., /v1/tokens/settle) call One-Claim internally; you rarely call it directly.
Webhooks
oneclaim.finalized — a new claim finalized (stage/listing, claim_id, pov_hash).
oneclaim.conflict — a duplicate attempt was blocked.
oneclaim.replaced — a replacement finalized; lists old/new ids; any frozen slices resume.
Security properties
Atomicity: reserve and finalize occur in the same transaction as EMT/settlement; there’s no cross-block race.
Minimal keys: only the identity fields + PoV hash are used; if any input changes, the id changes.
Global scope: a claim is unique across all EDMA routes.
Non-erasable history: revocations and replacements append state; we never “delete and retry.”
Determinism: claim_id is a pure function of inputs; anyone can re-compute it from the proof page.
Governance knobs
Governance may add schemas, adjust which fields compose the key for a lane, or refine freshness windows that a claim considers valid.
Governance cannot:
allow a slice to release without a unique claim,
override an existing finalized claim,
bypass PoV/EMT, must-fund before shipping, or the 50% burn.
Edge cases to remember
Same BL, different seals. Different claim_ids. If the MPA insists on a single seal, PoV fails rather than One-Claim.
Same serial, different PoV hash. Still a conflict in Tokens—serial is part of the key; a new dossier cannot sell the same unit twice.
External registry re-numbering. Use the mirror record; One-Claim prevents re-entry as “new.”
Plain recap
Last updated