← Onchain Graveyard Phase 0 evidence · English

Onchain Graveyard · Pre-submission evidence

Phase 0 Feasibility Gate

Before committing to a Catalyst proposal, we set ourselves a hard gate: prove that an in-house, CIP-113-conformant non-transferable certificate actually works on a Cardano ledger — minting, refusing transfer, rising in tier for the same owner, and refusing a second certificate on a plot that already has one. This is what the gate produced.

GO
Gates passed
13 of 13
Test bench
Hydra Head, offline mode
Ledger era
Conway · Plutus V3
Mint transaction
4,773 B · ~0.398 ADA
Min-ADA per certificate
1.534 – 2.384 ADA
Independent audit
not yet — see §8

§1 Why this gate exists

Onchain Graveyard sells one thing: a permanent, personal memorial that cannot be resold. That single product promise is also the hardest technical constraint in the proposal, and it is worth stating plainly why.

On Cardano's eUTxO ledger, a minting policy alone cannot make a token non-transferable. A minting policy runs when tokens are minted or burned — it does not run when an owner moves a native token that is already sitting in an ordinary wallet address. Any design that says "mint it straight to the buyer's wallet and it will be soulbound" is mistaken, and we corrected exactly that mistake in our own earlier draft.

Non-transferability therefore requires the token to live at a spend validator that forces a continuing output on every spend. And because we refuse to be custodial, that validator must be shared custody: the owner holds the spending key, the operator holds no key at all over the owner's certificate. That is the core of what CIP-113 describes, and it is the part we had to prove we can build and run before asking anyone to fund it.

An earlier, custodial design of ours failed its own internal audit on precisely this point. So the gate below is not a formality — a negative result would have meant not submitting.

§2 What we built

Two Aiken validators, roughly 350 lines together, written for this gate and open in the repository at onchain/validators/.

The two validators and the property each one carries
Validator Shape What it enforces
memorial multi-purpose: one script hash serves both mint and spend The certificate is minted into its own validator, so the policy id and the holding address are the same hash. Every later spend must return a continuing output at that same address with an identical value and an unchanged owner. There is no operator parameter and no branch that reads an operator signature. There is no burn branch.
plot_ticket multi-purpose: mint + spend, one hash One beacon token per plot, seeded by the operator. Minting a certificate requires burning that plot's beacon in the same transaction, so a plot can be engraved exactly once, ever — structurally, not by a uniqueness check in a database. Unused tickets are reclaimable, so the min-ADA the operator fronts is working capital, not a sunk cost.

The four properties under test

  1. Non-transferable. The token cannot leave the validator, and the owner recorded in the datum cannot change — so it can be neither moved to a buyer's wallet nor sold in place.
  2. Shared custody, not custodial. Every state change requires the owner's signature. The operator cannot mint on someone's behalf, cannot upgrade someone's memorial, and cannot seize or move one.
  3. One certificate per plot, permanently. Minting authority comes from consuming a one-shot ticket, and there is no burn path to free the plot again.
  4. Tier rise only, one step, paid. A memorial may rise one tier at a time, keeping the same owner and the same inscription, and the price difference must be paid to the treasury in the same transaction — the economy is enforced by the ledger, not by our backend.

§3 How it was tested

The bench is a Hydra Head running in offline mode. A Hydra Head is isomorphic with Cardano layer 1: the same Conway ledger rules, the same Plutus V3 cost model, the same min-UTxO arithmetic. What differs is that transactions settle in milliseconds and no node, faucet or block wait is involved. Every "accepted" or "rejected" below is therefore a verdict of the real ledger rules, applied to a real, signed transaction.

Each gate is one transaction. The valid case and its attack variant are produced by the same builder with one parameter changed, so a passing gate cannot be an artefact of two differently written transactions.

Two verification layers back each other up: the Aiken unit tests (23 on these two validators, 54 across the package) name which condition in the validator fires, while the Head gives the ledger's verdict on a fully signed transaction built through the same SDK path the production service will use.

What the bench cannot do. An offline Head charges no fees, never rolls back, and is not signed by a real CIP-30 wallet. Fees in §6 are therefore computed from measured size and execution units using layer-1 protocol parameters, not observed. This gate is a necessary condition, not a substitute for the Preview/preprod run and the independent audit listed in §8.

§4 Gate results

Thirteen signed transactions. Two had to settle; eleven had to be refused. All thirteen behaved as required.

"Blocked at" distinguishes a ledger verdict from a transaction that could not even be balanced — we report which, rather than counting both as the same evidence.
ID Scenario Required Blocked at Result
Minting a certificate
G1 Owner signs, ticket is consumed, base price paid, token lands in the validator must settle ✓ settled
G5 Mint the token straight to the owner's ordinary wallet must be refused ledger ✓ refused
G6 Mint while underpaying the base price by 1 lovelace must be refused ledger ✓ refused
G7 A third party mints a memorial in someone else's name must be refused ledger ✓ refused
Non-transferability
G2a Move the token out to a buyer's wallet must be refused ledger ✓ refused
G2b Keep the token in the validator but rewrite the owner in the datum must be refused ledger ✓ refused
G2c Drain ADA out of the certificate's UTxO must be refused ledger ✓ refused
G2d A third party upgrades someone else's memorial must be refused ledger ✓ refused
Same-owner tier rise
G3 Earth → Stone, same owner, exact price difference paid must settle ✓ settled
G2e Rise a tier while underpaying the difference must be refused ledger ✓ refused
G2f Jump two tiers in one transaction must be refused ledger ✓ refused
One certificate per plot
G4a Mint a second certificate on an occupied plot, without a ticket must be refused ledger ✓ refused
G4b Mint a second certificate by burning the already-consumed ticket must be refused unbalanceable ✓ refused

§5 How the numbers were measured

A Head does not report how much a script actually consumed, so we did not estimate it. For each script slot we lowered that one redeemer's execution budget — leaving the others untouched — and binary-searched down to the exact point where the ledger starts refusing the transaction. That threshold is the real consumption. Min-ADA was found the same way, by walking the certificate's UTxO down until the ledger's minimum-value rule rejects it.

Roughly 150 transactions go into those searches. A failing submission changes no state, so the same target can be probed repeatedly; only a successful probe consumes a plot and needs a fresh one.

§6 Measurements

Percentages are of the layer-1 per-transaction caps (16,384 B; 16.5 M memory; 10 G steps). Fees are computed from these measurements with layer-1 parameters — see §9.
Transaction Size Memory Steps Fee on L1
Engrave — burn ticket, mint certificate, pay treasury 4,773 B · 29.1% 394,529 · 2.4% 140,624,999 · 1.4% ~0.398 ADA
Tier rise — one step, same owner 3,245 B · 19.8% 257,812 · 1.6% 102,539,062 · 1.0% ~0.320 ADA
Execution units per script inside the engrave transaction
Script slot Memory Steps
Spend the plot ticket 128,906 49,072,265
Mint the certificate 229,491 80,932,617
Burn the plot ticket beacon 36,132 10,620,117
Locked ADA, measured against the ledger's minimum-value rule. The certificate's datum carries the inscription, so its minimum scales with how much text a player writes.
UTxO Lovelace ADA
Certificate — longest allowed inscription (24 B name, 12 B years, 6 × 32 B lines) 2,384,032 2.384
Certificate — typical inscription (two lines) 1,534,423 1.534
Plot ticket — fronted by the operator, reclaimable 1,142,577 1.143

What a player actually pays at the cheapest tier is the service price (1 ADA) plus the locked min-ADA plus the network fee: ≈ 2.93 ADA for a typical inscription and ≈ 3.78 ADA for the longest one. All three components must be shown on the payment screen before signing.

What the operator fronts to open the pilot's 1,500 plots is ≈ 1,714 ADA of reclaimable working capital — our own money, not a Catalyst budget line.

§7 What the measurements changed

The point of measuring is to be corrected. Three planning assumptions in our own proposal did not survive contact with the ledger, and we would rather publish that than carry it into a budget.

Assumption We had written Measured Consequence
Min-ADA locked per certificate ~1.5 ADA 1.534 – 2.384 ADA Inscription length is now a pricing decision, not only a UX one; the payment screen must quote the real locked amount for the text actually written.
Real cost at the cheapest tier ~2.7 ADA 2.93 – 3.78 ADA Friction at the signing step is higher than modelled; the draft-to-certificate conversion target has to be measured against this number, not the old one.
Working capital for 1,500 plot tickets ~1,740 ADA ~1,714 ADA Cheaper than planned; the reclaimable capital line goes down.

One optimisation is already visible in the numbers: both transactions currently carry the full validator inline. Publishing the two validators as reference scripts should bring the engrave transaction down to roughly 1–1.5 KB, trading size for the reference-script fee. That is month-1 work, deliberately not done in this proof.

§8 What this does not prove

§9 Reproduce it

The harness builds the validators, starts a throwaway Head in Docker, runs all thirteen gates, performs the measurement searches and writes both a report and its raw data — including the full ledger error for every refused transaction. Deterministic keys mean a rerun produces the same addresses and script hashes.

# from the repository root
pnpm --filter @hydra-graveyard/engine phase0            # gates + measurements (~2 min)
pnpm --filter @hydra-graveyard/engine phase0 -- --keep  # leave the Head running to inspect
cd onchain && aiken check                               # 54 on-chain unit tests
Script hashes are parameterised by the operator and treasury keys; the values below come from the harness's deterministic development keys, not from production keys.
Item Value
Certificate policy / holding script 6740f9591225a45d2f6292cadce833aceb9af0cb5f7cc777207dcf53
Plot ticket policy / holding script 598a60a1c20e9e29847e097871966fef67a2211247ffefed58306dd1
Aiken v1.1.21, Plutus V3, stdlib v2.1.0
hydra-node 2.3.0 (arm64 image), offline mode, protocol version 11
Layer-1 fee parameters used txFeePerByte 44, txFeeFixed 155381, priceMemory 0.0577, priceSteps 0.0000721, utxoCostPerByte 4310