A path towards Universal Covenant

Goal and motivations

The goal of this thread is to start engaging builders, wallet software and more generally the ecosystem to think practically on covenant usability.

Currently, the most rational choice is either shipping your own covenant-specific compatible wallet software or to push current wallet to adopt your covenant-specific SDK. I believe there are more universal paths that should be considered.

I will try to assume minimal prior knowledge and provide optional deeper accordions where I think it deserves special attention for the most curious readers.

Wallet implementation challenges

TLDR; As a builder using covenant or SilverScript (or any other abstraction layer above), I currently need to tell my users (or my wallet partners) to implement my covenant comprehension so they can (i) track covenant state across executions and (ii) build compatible transactions / transition executions.

Let’s walk through this simplified example so we understand better what’s going on under the hood (do not use this example in production, it has flaws):

contract ThresholdVault(
    pubkey initAgent, /* agent public key */
    pubkey initOwner, /* human public key */
    int initThreshold, /* threshold Kas value agent is allowed to spend in autonomy */
) {
    pubkey agent = initAgent;
    pubkey owner = initOwner;
    int threshold = initThreshold;

    /* agent can invoke this in autonomy if spent Kas value is below threshold */
    entrypoint function auto(sig agentSig) {
        require(tx.outputs.length == 2);
        require(tx.outputs[0].value <= threshold);
        require(checkSig(agentSig, agent));

        validateOutputState(1, {
            agent: agent,
            owner: owner,
            threshold: threshold,
        });
    }

    /* human and agent can together decide to spend more than threshold if
       they both sign */
    entrypoint function manual(sig agentSig, sig ownerSig) {
        require(checkSig(ownerSig, owner));
        require(checkSig(agentSig, agent));
    }
}

It compiles to a redeem_script and hence has a dedicated P2SH address: address = P2SH(BLAKE2B(redeem_script)).

First time you hear about P2SH? read 1. and 2. here now

note: deploy a covenant

When I will send the first UTXO to this address, we effectively say I will ‘deploy’ the covenant, and this UTXO will be part of the UTXO set like any others, only difference is the Script Public Key (SPK) that is a P2SH instead of a ‘standard’ or classic P2PK address.

The redeem_script can be summarized as:

template_prefix + encoded_state + template_suffix

Which means that any state changes will produce a new P2SH address. If we take a look at our Vault, since the state never changes, the address will remain stable across execution.

note: system should rely on CovenantID to fetch related P2SH

If we imagine a more complex script, usually it will have state changes (e.g. a token balance). For these covenants, you most certainly want to bind it to a stable identity, for this you’ll make sure the script enforces CovenantID continuation.

Here are two ways of doing it with our example above:

  1. programmatically
    /* there is exactly one covenant continuation */
    require(OpAuthOutputCount(this.activeInputIndex) == 1);
    /* the first (and only because of previous check) output index continuing the covenant lineage */
    int next_idx = OpAuthOutputIdx(this.activeInputIndex, 0);
    
    validateOutputState(next_idx, {
        agent: agent,
        owner: owner,
        threshold: threshold,
    });
    
  2. with annotations
    /* here, the annotation does the same for us automatically */
    #[covenant(binding = auth, from = 1, to = 1, mode = transition)]
    function auto(State prev_state, sig agentSig) : (State) {
    
        /* [...] */
    
        return({
            agent: prev_state.agent,
            owner: prev_state.owner,
            threshold: prev_state.threshold,
        });
    }
    

A wallet later will use this stable identity to identify P2SH(s) tied to this CovenantID(s), in this example case there is only one Vault instance (/covenant) living at a time, so only one P2SH address at a time for this CovenantID.

Unless a wallet specifically knows this Vault code:

  • and that wallet user somehow hints he has deployed his own Vault covenant (with provided parameters), the wallet is unable to read the current state, or know it’s even deployed.
  • wallet doesn’t know how to construct valid transition execution (invoke auto or manual function)

So what now?

Conceptually, what existing or not yet existing network actor should have the responsibility (note: multiple actors can be involved) to:

  • track covenant states, and expose them
  • verify source code (vs. what’s deployed)
  • understand how to build a “covenant-valid” transaction

I also want you to think by yourself and express more questions / challenges, share your views. I already have a biased opinion on these topics that I’ll share later.

4 Likes

Cardano solved similar interface challenges with several independent standards rather than one universal one — that could be an interesting reference point here.

Examples:

• CIP-57 (Contract Blueprint) for machine-readable contract metadata

• CIP-31 (Reference Inputs) for read-only contract state access

• CIP-30 / CIP-95 for wallet–dApp interaction

One lesson from Cardano is that separating concerns (metadata, state discovery, transaction construction) helped avoid a monolithic specification.

Question: Does KIP-10 introspection already provide enough for generic wallet state discovery, or might Kaspa eventually benefit from something analogous to reference inputs?

Kaspa has the opportunity to sequence these pieces more deliberately than Cardano did.

5 Likes

Just a thought but can AI be useful as a sort of middleware/translator between human and machine, satisfying both machine and human needs ? A human needs a buttom, so show the human a button and the machine doesnt care so no button. Lots of caveats but perhaps we need to rethink what a wallet is and does :slight_smile:

If the redeem has been crafted by SilverScript, by getting the source code, you know where the state is located (state_layout := start, length), and you also know the state fields which tell you how to read/cast the bytes.
If this is a manually crafted bytecode, you’d need similar hints by the creator (provided you trust the hints).

1 Like

I won’t stand behind a solution that relies on non-deterministic systems to take actions on behalf of users.

1 Like

Agreed, nothing that can’t be trusted should ever move a users money. But what if the AI doesnt hold a key, never signs, and never creates the thing the user approves. It only suggests what to do, picking from a fixed list of allowed actions. Everything after that is fixed and predictable: The user approves the real thing, and the covenant blocks anything outside its rules no matter what.
Not proposing claude, chatgpt, but rather open source which runs in your own environment

1 Like

Great summery my friend.
What is easy to agree on-
We need a general purpose contract verifier- reveal a p2sh utxo into the full contract (logic and state).

From there the progression would be a general purpose state decoder, which follow state transitions for contract covenant.

Lastly , the tricky part, the question of general purpose interaction surface. How can we escape the need to develop per-app-specific wallets? What can we create to allow ease of communication between covenants who has different state layout and entrypoint interfaces?

So the question is, can we create a utxo version of an “interface”?

This brings to mind the topic of a kcc20 protocol.
In my proposal, I offered a reader (which solves state tracking ) and a writer (which solve the interaction part).
This is arguably the closest thing to an “interface” that a utxo covenant to can offer. And the question is whether or not this can be generalized.
Intuitively, I think that the only possibility for general purpose interaction is to define per protocol interface, what do you think?

And ofc waiting to hear what you had in mind.

Thank you for the high quality post, it’s great having you here :slight_smile:

1 Like

from my limited understanding, I think it would be nice to have a type of node that keeps track of all covenants (scripts, current state, etc), with nodes being able to talk to each other in case someone doesn’t know how to decode a specific UTXO. Then wallets could query these nodes through APIs, with questions like “do I own any KCC tokens? what are the relevant UTXOs”? These API queries should be payable in KAS, so that the tracking work gets compensated.

2 Likes

We’ve been working on exactly these problems for the last few weeks developing a covenant-based fungible token on TN10. Non-custodial web wallet, all client-side tx building, live and working. Wanted to share our findings…

Same conclusion as OfficeForge: the app is the actor right now, and has to be. We ship the wallet inside the app. Our JS stack hand-rolls the entire write path; sighash computation, schnorr signing, ABI-correct sigscript assembly, tx construction, JSON wRPC submit. That’s four separate modules that exist only because there’s no standard way to say “here’s how you build a valid spend for this covenant.” It works, but it’s entirely tailor-made. Nobody else’s wallet can touch our token without reimplementing all of it.

The state-tracking problem: Our token has per-UTXO state (owner, amount, forSale flag, price). Every unique combination of state produces a different redeem script, which means a different P2SH address.

Our workaround: a lightweight server-side registry. Wallets register output parcels on every tx confirm and query the registry on refresh. New wallet restored from seed phrase discovers its holdings automatically via the registry , no out-of-band descriptor exchange needed. We also built an offline descriptor codec (compact binary encoding of parcel state, base58check wrapped) as a fallback for peer-to-peer transfer without the registry. Both work, but both are entirely token-specific plumbing that every covenant builder would have to reinvent.

To the three-layer breakdown in the thread:

1. Contract verifier = needed. Right now if you don’t have the source, a P2SH address is opaque. We verify by reconstruction on the client (derive the expected P2SH from known params + compiled script, refuse to act if it doesn’t match what’s on chain). That pattern is solid but requires the client to already have the source.

2. Re: State decoder / CovenantID tracking. We’ve drafted (not yet filed) a feature request for a getUtxosByCovenantId RPC on rusty-kaspa. Right now the only query surface is getUtxosByAddresses, which means we have to pre-derive every possible P2SH address or maintain our own registry. CovenantID already survives state transitions at the protocol level so the missing piece is an RPC that lets you query by it. That alone would eliminate our registry as a hard dependency and let any explorer or wallet follow token state generically.

3. General-purpose interaction surface? A fungible token and an escrow have fundamentally different state layouts and entrypoints. Trying to unify them into one abstract interaction layer would produce something too generic to be useful. But standardizing the pattern, ie… every protocol type publishes a reader spec (how to find and decode state) and a writer spec (how to build valid transitions) while letting the content vary per protocol, that’s tractable. For KCC20 specifically, the reader/writer split maps directly to what we built: reader = address derivation + UTXO query + state decode, writer = sigscript assembly + tx construction + signing.

In our specific case: the address-change-on-state-transition issue isn’t just a wallet problem, it’s a UX wall. When a token gets listed for sale, the UTXO moves to a new P2SH address (because forSale flipped from 0 to 1). To a user listing tokens for sale and watching an explorer, their funds “disappeared” to an unknown address. CovenantID indexing would fix the explorer story too, not just wallets. Running on testnet10 for now : bitphoque.org GitHub - kyle4nia/BitPhoque: A covenant-based fungible token on Kaspa. Native Layer 1. · GitHub

1 Like

The KCC20 v1.1 review draft has been expanded to incorporate the latest open-ICC interoperability work and the KCC20-Regulated architecture being developed in Compliance Wallet. The update adds a versioned common-state and opaque-extension model, defines policy registries, mirrors, and distributed Policy Proof Packets, and removes the need for one central policy server—or one globally consumed registry UTXO—to authorize every transfer. It also strengthens descriptor, Reader, Writer, regulated-control, and conformance requirements while leaving the final non-membership, sharding, and proof algorithms open for Testnet validation and community review.

KCC20_Specification_v1_1_Open_ICC_Regulated_Tracked_Changes.docx