PATINA docs

Mint walkthrough

Two transactions with at least 144 blocks between them. The wait is what makes your claim impossible to snipe.

What you will know after this page
  • How to compute the commitment, SHA256("PTNA/commit" || claimant_xonly || salt), and build the 68 byte commit leaf around it.
  • Which byte of the SEED payload names the carrier output, and what a wrong value costs you.
  • The value the carrier must hold: at least 100000 sats for a founding Seal, at least 10000 sats once the window has closed.
  • How to recompute the artifact id yourself and check the indexer against it.
  • The reason code behind each way a reveal fails, and why your bitcoin survives every one of them.
Every hex value on this page

Computed by docs/tools/vectors.mjs from the example key and salt shown below. Your values will differ because your salt and key differ. Rerun the script to check the arithmetic.

Before you start

  1. Finish wallet setup, including one full rehearsal on regtest.
  2. Check the window is open. Ask your indexer:
curl -s https://indexer.example.org/patina/window

A response with "state": "OPEN" and a positive blocks_remaining means founding commits are still being accepted. Field by field detail is in the endpoint reference, and the cohort rules that state reports on are laid out on the public page for the Firstlight Seals.

Step one, the commit

  1. Generate a claimant key and a salt

    The claimant key is a 32 byte x-only public key that you control. The salt is 16 random bytes from a cryptographically secure generator. Never reuse a salt.

    claimant_xonly  f13dc99dc544736fca031f924b0ae1c54ff4a331f5c3b2951c6d4d5e1878866a
    salt            2fe862993a92197e084e4070cc8aa1c3

    No private key exists for that example point. Do not use it.

  2. Compute the commitment

    Single SHA-256 over the ASCII tag, the key, then the salt. No separators.

    commitment = SHA256("PTNA/commit" || claimant_xonly(32) || salt(16))
               = ac2e3090ef3265ff4fc99463cec7e2b8d9227ac29f3ac0f5fc8e24dbaa2afdd6
  3. Build the commit leaf

    A tapscript that requires your signature, pushes the commitment, and drops it without executing a conditional.

    <claimant_xonly(32)> OP_CHECKSIG PUSH32(commitment) OP_DROP
    
    68 bytes:
    20f13dc99dc544736fca031f924b0ae1c54ff4a331f5c3b2951c6d4d5e1878866aac20ac2e3090ef3265ff4fc99463cec7e2b8d9227ac29f3ac0f5fc8e24dbaa2afdd675

    Do not rebuild a pending commit with a different leaf. Historical 70 byte conditional leaves remain parseable and must reveal with their original script.

  4. Fund the commit output

    Build the P2TR output that commits to that leaf, then pay it. Fund it with enough to cover the reveal fee plus the carrier value you intend, or plan to add a second input at reveal time.

    Record the exact outpoint. You need txid:vout at reveal time.

  5. Broadcast and wait for confirmation

    The height at which the commit output confirms is the height that decides founding status. Note it.

Using the planner instead

POST /patina/plan/commit
Content-Type: application/json

{
  "network": "signet",
  "claimant_xonly": "f13dc99dc544736fca031f924b0ae1c54ff4a331f5c3b2951c6d4d5e1878866a",
  "salt": "2fe862993a92197e084e4070cc8aa1c3",
  "fee_rate_sat_vb": 12
}
On this example

The baseline freezes that the planner returns an unsigned plan and a human review summary, holds no keys and broadcasts nothing you have not signed. The exact request field names belong to the app backend, not to the protocol. Treat the block above as the shape of the information the planner needs, and check the backend for the current field names.

The wait

The reveal is invalid unless reveal_height - commit_output_height >= 144. That is about 24 hours at ten minutes a block, and it can be longer if blocks are slow.

Do not reveal early

A reveal published one block too early does not fail politely. It confirms on chain, spends your commit output, produces the reason code SEED_COMMIT_TOO_YOUNG, and creates nothing. You keep your coins and lose the fees and the commit. Count the blocks, and add a safety margin of a few blocks.

While you wait, verify your commit output height with your own node or your indexer, and confirm the window was still open at that height.

Step two, the reveal

  1. Spend the commit output by the script path

    The reveal transaction must have exactly one input whose revealed leaf commits to this salt and claimant key, with your signature satisfying the OP_CHECKSIG. Two leaves qualifying for the same salt fail the same way as none. The indexer recomputes SHA256("PTNA/commit" || claimant_xonly || salt) from the leaf and the SEED payload, and the result must equal the commitment inside the leaf.

  2. Create the carrier output

    Pick which output is the carrier and note its index. It must not be an OP_RETURN, and its value must be at least 100000 sats for a founding Seal, or 10000 sats after the window closes.

    This output is the artifact. Its confirmation height is where depth starts counting.

  3. Add the SEED marker

    One OP_RETURN output, one minimal push, 24 bytes of data.

    OP_RETURN PUSH(24) [ "PTNA" | 0x01 | 0x01 | salt(16) | flags(1) | carrier_vout(1) ]
    
    payload       18 bytes  2fe862993a92197e084e4070cc8aa1c30001
    push data     24 bytes  50544e4101012fe862993a92197e084e4070cc8aa1c30001
    scriptPubKey  26 bytes  6a1850544e4101012fe862993a92197e084e4070cc8aa1c30001

    The trailing 00 01 is flags = 0x00 then carrier_vout = 1. Get that last byte wrong and you point the artifact at the wrong output. Full grammar on Marker grammar.

  4. Check the transaction before signing

    • Exactly one OP_RETURN output whose payload starts with PTNA. Two of them void the marker.
    • The carrier_vout byte matches the index of the output you funded.
    • That output holds at least the minimum for the cohort you are claiming.
    • The salt in the payload is byte for byte the salt in your commitment.
    • The commit input is at least 144 blocks old at the height you expect to confirm at.
  5. Broadcast

    Once it confirms, the artifact exists. Compute the id yourself if you want to check the indexer:

    artifact_id = SHA256("PTNA/artifact" || reveal_txid_wire(32) || carrier_vout_le(4))

    reveal_txid_wire is the internal byte order, which is the reverse of the txid a block explorer shows you. Worked example on Identity and derivations.

Confirm it landed

Ask any indexer for the id you just computed. Four fields decide whether the mint did what you meant.

curl -s https://indexer.example.org/patina/artifacts/d3b8d3013c23dd3df76882034df80935ba55aa75772007f0b142b63adce5eff0

Check status is ALIVE, founding is what you expected, carrier is your outpoint, and endowment_sats matches the value you funded. If the artifact is missing, look for your reveal in GET /patina/invalid-events, which tells you the reason code.

Six confirmations is where the indexer treats the record as final. Before that, a reorg can undo it.

None of these checks require trusting an indexer. The public page on verifying it yourself reads the same facts off your own node.

What can go wrong, and what it looks like

Every row below is a transaction that confirmed on Bitcoin and created no artifact. The middle column is what the indexer reports for it in GET /patina/invalid-events, so this is the table to scan when your reveal is in a block but the artifact is not there.

Common mint failures and their reason codes.
MistakeReason codeResult
Revealed before 144 blocks passedSEED_COMMIT_TOO_YOUNGCommit spent, nothing created
Salt in the payload differs from the salt in the commitmentSEED_COMMITMENT_MISMATCHNothing created
No input revealed a qualifying commit leafSEED_NO_COMMIT_INPUTNothing created
carrier_vout points past the last outputSEED_CARRIER_OUT_OF_RANGENothing created
carrier_vout points at the OP_RETURNSEED_CARRIER_IS_OPRETURNNothing created
Carrier funded below the minimumSEED_CARRIER_BELOW_MINNothing created
Two OP_RETURN outputs start with PTNAVOID_DUPLICATE_MARKERMarker void, nothing created
Push is not minimal, or extra opcodes presentSEED_BAD_GRAMMARNothing created

In every one of these cases your bitcoin stays yours. You lose fees and the commit, not the coins. Full list on Reason codes, and repair paths on When something goes wrong.