Kaspalytics Learn is a work in progress.
Updated: Aug 2, 2026

Covenant IDs & Bindings

KIP-20 Covenant IDs — the consensus-tracked identity that gives a covenant lineage, how genesis derives an unforgeable ID, how continuations inherit it, and the two output scopes a script can query.

Reconstructing a successor’s address proves its contents are right. It does not prove the UTXO you are spending is the real one.

Anyone can deploy a byte-identical copy of your token covenant and fund it. It will have the same program, the same address, and behave identically. Pure script cannot tell your lineage from a counterfeit, because there is nothing in the bytes that distinguishes them.

KIP-20 fixes this in consensus. Everything on this page is a network rule, not a convention — it holds for every covenant regardless of how its bytes are laid out.

The two fields

A Covenant ID is an optional 32-byte value stored on a UTXO. When present, the UTXO is a member of that covenant’s lineage.

A covenant binding is an optional field on a transaction output carrying two things:

  • authorizing_input — the index of the input that authorizes this output’s creation
  • covenant_id — the covenant the output belongs to

Covenant bindings require transaction version ≥ 1. A covenant UTXO comes into existence one of two ways:

What happens
GenesisA new Covenant ID is created. The authorizing input does not share the output’s Covenant ID. The ID is a BLAKE2b hash over the authorizing outpoint and the full set of authorized outputs, so it is unique and unforgeable — the creator must compute the same value consensus does, or the transaction is rejected
ContinuationThe output inherits the Covenant ID of the UTXO spent by its authorizing input. Consensus checks the IDs match

Genesis

Genesis is where a Covenant ID is minted, and it is the step that has to be unforgeable — otherwise anyone could claim an existing lineage.

The transaction creator must compute the same ID that consensus will, store it in the output’s covenant binding, and submit. If consensus derives a different value, the transaction is rejected. The authorizing_input may be any valid input index.

covenant_id = BLAKE2b("CovenantID",
    outpoint.tx_id
    || le_u32(outpoint.index)
    || le_u64(len(auth_outputs))
    || for each output:
        le_u32(output_index)
        || le_u64(output.value)
        || le_u16(spk.version)
        || le_u64(len(spk.script))
        || spk.script
)
Inputs
Input 0 — funding UTXO authorizing input
an ordinary P2PKH coin
its outpoint seeds the ID
genesis
Outputs
Output 0 — new instance ID X created
binding → { authorizing_input: 0, covenant_id: X }
consensus re-derives X and compares

The authorizing input does not carry covenant ID X — that is what makes this a genesis rather than a continuation.

Minting a lineage. The ID is a function of the transaction itself, so it cannot be chosen.

Uniqueness comes from the outpoint: it names a specific UTXO, which can only ever be spent once. Two genesis events therefore cannot collide, and no one can retroactively manufacture an ID matching a lineage they do not control.

Note that the preimage commits to the full set of authorized outputs, not just the one being created — their indexes, values, and complete script public keys. A genesis transaction cannot be rearranged after the fact without changing the ID it produces.

Continuation

A continuation output’s binding ties it back to an input from the same lineage. The output inherits its Covenant ID from the UTXO spent by its authorizing input, and consensus validates that the two match.

authorizing_input : index of the input from the same lineage
covenant_id       : the inherited Covenant ID
Inputs
Input 0 — covenant instance ID X
UTXO carrying covenant ID X
its script validates the transition
continuation
Outputs
Output 0 — successor ID X inherited
binding → { authorizing_input: 0, covenant_id: X }
consensus checks the ID matches input 0
Output 1 — payout no ID
no covenant binding
leaves the lineage

Consensus only checks that the inherited ID matches. Whether this successor is a legitimate next state is the script's job.

Carrying a lineage forward. No new ID is derived — it is copied and verified.

What this buys a script

The payoff: consensus enforces the lineage, so scripts do not have to. A script that reads a non-empty Covenant ID off its own input knows, for free, that the chain of custody back to genesis is intact. It never has to walk history or verify a parent.

So the two halves divide cleanly, and this division runs through the whole covenant model:

  • The script proves the successor’s contents are correct.
  • Consensus proves the successor’s lineage is correct.

Querying the structure from script

Three opcodes read the binding fields directly:

OpcodePushes
OpInputCovenantIdThe Covenant ID of an input’s UTXO, or an empty vector if it has none
OpOutputCovenantIdThe Covenant ID on an output’s binding, or 32 zero bytes if it has no binding
OpOutputAuthorizingInputThe authorizing_input of an output’s binding, or -1 if it has no binding

Beyond those, consensus exposes covenant structure through two different scopes, and choosing the wrong one is a security bug rather than a style choice:

ScopeWhat it coversOpcodes
Authorized-outputOutputs whose binding names this specific input as authorizing_inputOpAuthOutputCount, OpAuthOutputIdx
Shared Covenant IDAll inputs and outputs carrying a given Covenant ID, across the whole transactionOpCovInputCount, OpCovInputIdx, OpCovOutputCount, OpCovOutputIdx

The shared context is ordered by transaction input index. When to use which — and what goes wrong when a covenant picks the local scope for a rule that is really about the whole group — is covered in Leaders & Delegators.

Next

That completes the consensus layer. Everything from here is convention: how programs agree to lay their bytes out so that tooling written by one author can read a covenant written by another.

The P2SH Covenant Envelope — how a covenant program is committed to, revealed, and executed.

References

Kaspalytics strives to provide accurate data - our highest level of effort is given to data validation and maintenance. However, we cannot guarantee 100% accuracy. Data is subject to inaccuracies and change.

Contact | © 2026 Kaspalytics