The previous page treated state as all-or-nothing: one range, entirely mutable. Real covenants need finer control.
- Some fields should be permanently fixed — a token’s decimals, an owner set at genesis.
- Some values are too big to carry in the script at all, or would make every transition pay for bytes it does not touch.
KCC1 answers these with two independent techniques. They are often used together, but they solve different problems.
Template views
A template view selects a contiguous subset of the encoded state as its variable range. Encoded state outside that range is treated as part of the template.
state.start <= view.start
view.start + view.len <= state.start + state.len
view.prefix = R[0 : view.start]
view.encoded_state = R[view.start : view.start + view.len]
view.suffix = R[view.start + view.len :]
view.template = (view.prefix, view.suffix) Two constraints keep views coherent:
- The range must be non-empty.
- It must consist of one or more complete consecutive encoded fields — you cannot cut a field in half or start mid-push.
The view’s template hash is TemplateHash(view.prefix, view.suffix), computed exactly as before. The canonical template from the previous page is simply the view that selects the complete encoded state.
The same program, cut three ways
KCC1’s conformance vector is a ten-byte program with three state fields:
template.prefix = 51
byte[2] a = 02aabb
bool b = 0101
byte[2] c = 02ccdd
template.suffix = 75
R = 5102aabb010102ccdd75
state.start = 1
state.len = 8 Three declared views over those identical bytes:
Views may overlap and may be nested. Here the first two overlap on the complete encoding of b, and the third is nested inside both.
Different views of the same program generally have different template hashes, because each hash commits to every byte outside its own cut. That is the entire mechanism:
Under a given view, everything outside the selected range is part of the template hash. A continuation authenticated against that hash therefore cannot change those bytes — including state fields outside the view.
So under the b view, fields a and c are frozen. A transition using that view can flip b and nothing else. Changing bytes outside a view’s range requires an independently authorized template.
When to reach for a view
A view is the right tool when a contract has fields with genuinely different lifetimes — a mutable counter alongside an immutable configuration, for example. Rather than re-validating the immutable fields on every transition, you pick a view that excludes them and let the template hash do the work.
Virtual elements
A virtual element is a value represented in the program’s bytecode by a commitment rather than its full encoding. The complete value is supplied as an opening when it is needed.
It has four parts:
| Part | Lives in |
|---|---|
the commitment c | encoded state |
| a canonical encoding of the complete value | (definition) |
| an opening | invocation data — witness, not state |
a verification rule binding the opening to c | the program |
A protocol defining a virtual element must specify all five of: the value type and canonical encoding; the commitment scheme and byte length; the opening encoding and where it appears among the invocation arguments; the verification rule; and how a changed value produces the next commitment.
Two rules are non-negotiable:
- A covenant must verify an opening before using the complete value.
- When a transition changes the value, the continuation’s state must contain a commitment to the new value’s canonical encoding.
The hash-committed scheme
KCC1 defines one concrete realization:
payload = Packed(value)
commitment = Hash(payload) The state field is byte[32] commitment; the opening is payload. The covenant checks Hash(payload) = commitment, decodes, and for a changed value computes Hash(Packed(next_value)).
Packed concatenates the fixed payloads from the Value ABI in declaration order, without script push opcodes. That is the key difference from encoded state, which includes them. It is valid only when the layout has a statically known packed width, including after recursive record lowering.
That last caption points at the real trade-off. For a nine-byte record a commitment is worse. Virtual elements pay off when the value is large, or when most transitions never need to look inside it — a transition that does not touch the value never has to carry it.
Combining the two
Template views and virtual elements are independent, and KCC1 is explicit that they compose:
- A template view selects which encoded state fields may change while preserving a template hash.
- A virtual element commits to a value that is not stored in the program at all.
Select a view that contains the virtual element’s commitment field, and a continuation using that view can update the commitment while every other program and state byte stays immutable.
This is exactly the shape KCC20 uses. Its extended_state_digest is a commitment to token-specific state that the base transfer treats as opaque and must preserve unchanged — while token-specific entrypoints, working under a different view, may update it.
Next
Leaders & Delegators — how multi-input transitions divide validation work.