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

Entrypoints & Dispatch

How a covenant program exposes several callable branches — KCC1 function signatures and dispatch tags, the invocation layout, and how a real compiled program routes to the selected branch.

A covenant rarely has one thing to do. A vault might allow deposit, withdraw, and recover; a token needs a leader path and a delegate path. All of them live in one program R, because R is what the P2SH output committed to.

So the program needs a way to be told which branch to run, and the caller needs a way to say it. That is dispatch.

Entrypoints

An entrypoint is a named, externally invocable branch of a covenant program with an ordered list of argument types. The Program ABI identifies it by two things, both of which are part of its identity:

  • its case-sensitive name
  • its ordered argument types

An invocation is the entrypoint arguments, the optional dispatch tag, and the covenant program, all supplied in the spending input’s signature script.

Dispatch tags

KCC1 derives a four-byte tag from the entrypoint’s name and signature:

FunctionSignature = UTF8("{name}({comma-separated canonical type names})")
dispatch_tag      = Hash(FunctionSignature)[0:4]

Hash is BLAKE3 with 32-byte output, and the tag is its first four bytes. The rules on the preimage are strict:

  • Types are comma-separated inside parentheses.
  • No whitespace, no length field, no terminator.
  • A zero-argument entrypoint ends with ().

The type names must be the canonical spellings from The Value ABI. byte[] is not bytes; byte[32] is not byte[ 32 ].

FunctionSignature = UTF8("step(int,byte[4],bool,byte)")
dispatch_tag      = 2c49ed65

Some more, computed the same way:

FunctionSignaturedispatch_tag
step()4cc16552
reset()d1830c7b
step(int,byte[4],bool,byte)2c49ed65
transfer(State[],sig[],byte[])283386b0

Note the first two: same program, different names, completely unrelated tags. And note the third — step() and step(int,byte[4],bool,byte) share a name but are different entrypoints with different tags. The signature is the identity, not the name.

Uniqueness is the producer’s job

All dispatch tags in a multi-entrypoint program must be distinct, and a program producer must reject a program whose entrypoints collide. This covers both cases:

  • two different signatures whose BLAKE3 prefixes happen to collide, and
  • two entrypoints that produce the identical preimage (same name, same types).

Four bytes is short enough that this is a real check, not a formality. It is enforced at build time because at spend time a colliding tag is simply ambiguous — the script would run whichever branch it tested first.

The invocation layout

From the envelope page, the signature script must be push-only and shaped:

PushArguments(arguments)
[OP_DATA_4 dispatch_tag]
PushMinimal(R)

The tag element follows one rule with no exceptions:

Entrypoints in the programDispatch tag
exactly 1must be omitted
2 or moremust be present, as OP_DATA_4 ‖ 4 tag bytes

Omitting it for a single-entrypoint program is not an optimization you may skip — it is required. There is nothing to select, so the tag would be dead weight that the program would have to drop.

arguments 0111 whatever the entrypoint takes
OP_DATA_4 04
dispatch_tag 2c49ed65 selects the branch
push opcode + R 4c92 then 146 program bytes
Schematic layout of a signature script, using the step(int,byte[4],bool,byte) tag. Order is fixed: arguments, then tag, then the program — and R is always the final push.

Because PushMinimal(R) is last, it is the first thing the P2SH hash check consumes — which leaves the arguments and the tag sitting on the stack exactly where the program expects them.

What this looks like compiled

KCC1 says what the caller supplies; it does not dictate how the program branches internally. Here is how the SilverScript compiler does it, from a real compile of a two-entrypoint counter:

R — two-entrypoint counter (146 bytes) 146 bytes
OpToAltStack 6b @0 · stash the selector
encoded state 08070000000000000002aabb @1 · count = 7, tag = 0xaabb
OpFromAltStack 6c @13 · retrieve it
test branch 0 76009c6375 @14 · Dup, 0, NumEqual, If, Drop
branch 0 body — step 010852795193 … +48 … 00c388757551 @19 · 60 bytes
OpElse 67 @79
test branch 1 76519c6375 @80 · Dup, 1, NumEqual, If, Drop
branch 1 body — reset 01080058cd7e … +45 … 00c388757551 @85 · 57 bytes
tail 676a6868 @142 · Else, Return, EndIf, EndIf
A linear if/else chain. The unmatched fall-through is OpReturn, so an invalid selector fails rather than silently doing nothing.
Executes
(after the P2SH hash check)
selector left by the signature script
1
OpToAltStack
move the selector out of the way
empty
state pushes
count, tag — pushed unconditionally
07…aabb
OpFromAltStack
selector is back on top
07…aabb1
OpDup OP_0 OpNumEqual OpIf
is it branch 0? no
07…aabb1
OpDup OP_1 OpNumEqual OpIf OpDrop
is it branch 1? yes — run reset
07…aabb
Why OpToAltStack is there: the state pushes would otherwise bury the selector.

Two design points are worth extracting, because they generalize beyond this compiler:

The state pushes run on every path. They are not inside any branch. That is what lets the state occupy one contiguous, predictable byte range regardless of which entrypoint is invoked — the property the templates page depends on.

Dispatch bytecode may live in the template prefix. KCC1 explicitly permits this: a producer may place dispatch-preservation bytecode before or after the state range, provided the declared range and stack behaviour stay correct. Here OpToAltStack is that bytecode, and it is why the state starts at offset 1 instead of 0.

Compare with the single-entrypoint build of the same contract, where there is no selector to preserve and the state starts at offset 0:

R — the same contract with one entrypoint (58 bytes) 58 bytes
encoded state 08070000000000000002aabb @0 · starts at offset 0
entrypoint body 010852795193 … +34 … 00c388757551 @12 · 46 bytes
No dispatch chain, no alt-stack juggling, no tag in the signature script.

Current compiler divergence

The SilverScript compiler pushes a numeric branch index (0, 1, 2 …) as the selector, not a KCC1 four-byte dispatch tag. In the artifact above, branch 0 is step and branch 1 is reset, selected with OP_0 and OP_1.

KCC1 specifies OP_DATA_4 followed by Hash(FunctionSignature)[0:4].

Both are still Draft, and the two schemes trade off differently:

Numeric selectorKCC1 dispatch tag
Size1 byte5 bytes
Stable across editsNo — reordering entrypoints changes the indexYes — derived from name and types
Meaningful without the ABINoSomewhat, given a signature guess

If you are writing tooling today, read the selector convention from the compiled artifact rather than assuming either scheme.

Next

State, Templates & Continuations — how that contiguous state range is defined, hashed, and rewritten to build a successor.

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