Has there been any discussion of adding `getUtxosByCovenantId`?

A `getUtxosByCovenantId` RPC method that returns all live UTXOs carrying a given `covenantId`. This would let covenant token projects discover their on-chain state without maintaining a separate indexer. look up all holders, all listed tokens, total circulating supply for any covenant token without a separate backend ; a wallet that knows its covenant ID can find all its UTXOs in one call instead of scanning addresses; watch a covenant’s on-chain activity without subscribing to every block

5 Likes

A fundamental question for the Core team: who is Kaspa being built for?

First of all, I fully support the proposal to add getUtxosByCovenantId. However, I believe this discussion raises a much more fundamental question.

When covenants and Covenant IDs were introduced, I assumed they were intended to enable applications that interact directly with the blockchain through a standard RPC node. In other words, a developer should only need to write a covenant and a client application, while users could interact with it through any Kaspa RPC node—without any intermediary services.

Today, that isn’t possible.

Even when running a full node, developers still need to build and maintain their own indexer or rely on someone else’s.

To me, this is not primarily about convenience—it’s about architecture.

If an additional server is required between the user and the blockchain, then the user is no longer interacting directly with the blockchain, but with the developer’s infrastructure. That dependency also significantly increases the cost of building and operating applications. Instead of just a covenant and client code, projects now require servers, DevOps, monitoring, redundancy, maintenance, and everything else that comes with operating backend infrastructure.

This inevitably raises the barrier to entry and makes many smaller projects economically impractical.

So my question to the Core team is simple:

What is the intended architecture for applications built on Kaspa?

Should a standard RPC node expose everything necessary to interact with covenants, allowing users to communicate directly with the blockchain?

Or is the expectation that any serious application will always require its own indexing layer as a fundamental part of its architecture?

To me, getUtxosByCovenantId is not just another RPC method. It’s a reflection of a much broader design philosophy.

Is Kaspa intended to become a platform where applications can exist with little or no backend infrastructure, communicating directly with the blockchain? Or is external indexing considered a permanent and necessary layer for building on Kaspa?

I think the answer to that question will have a much greater impact on the future of the ecosystem than any single RPC method ever could.

2 Likes

Hello and welcome. This is a good and relevant question.
As far as I know, this is a RPC that is planned to be develop.

I would argue though that your concerns are exaggerated. You are correct to note that there is a need for a per-app indexer, this does not mean though that the d-app FE must trust it blindly. A frontend of a dapp could easily validate the truthfulness of the information provided by the indexer.

1 Like

I think there’s a point to be made about the simplicity of development and clarity of codebases and increased participation overall, if every app developer didn’t need to run his own node and build his own custom indexer. Wallets could also better support those txs without chasing every new app developer, better ecosystem integration in general.

Cause it’s true that users can always verify stuff by themselves, but the perception of decentralization that you get when you make an app without a database is still on another level imo.

2 Likes

I agree that it should be clearer and simpler to develop and compose applications.
There are many layers to this stack- covenant standards, script compilers, indexers, covenant RPCs, there are efforts to solve them all.

No one thinks Toccata is enough on its own, there is still much work to do to make development approachable.

1 Like

+1 on getUtxosByCovenantId. We need it for the same reason — querying covenant state without an external indexer. Without it, covenant state queries require an external indexer scanning chain history block by block. This is table stakes for any covenant that tracks mutable state (registries, token balances, membership lists).

Three additional covenant primitives unlock everything built on top of it:

1. Multi-signature verification — checkMultiSig()

Currently checkSig handles one signature. Any pattern requiring N-of-M attestation (quorum-based verification, multisig wallets, DAO votes) needs multiple signatures verified in one spend.

pub fn check_multi_sig(

&self,

pubkeys: &\[PublicKey\],

signatures: &\[Signature\],

message: &\[u8\],

threshold: usize,

) → bool;

SilverScript:

function verify_quorum(State s, pubkey[5] ops, sig[5] sigs) : (State) {

require(checkMultiSig(ops, sigs, s.payload, 3));

}

2. Block height / timestamp — ctx.block_height() and ctx.timestamp()

Covenants can’t enforce recency without knowing what block they’re executing in. Stale data, expired timeouts, attestation windows — all require temporal context.

impl CovenantContext {

pub fn block_height(&self) -> u64;

pub fn block_timestamp(&self) -> u64;  // milliseconds

}

SilverScript:

function release(State s, int attested_block) : (State) {

require(ctx.block_height() - attested_block <= 10);

}

3. Covenant event emission

Covenants that mutate state (registries, membership changes) need to notify off-chain services. Currently the only way to detect changes is polling blocks.

pub struct CovenantEvent {

pub covenant_address: Address,

pub entrypoint: String,

pub data: HashMap<String, Vec<u8>>,

}

4. Cross-covenant state reads

One covenant reading another’s state removes the off-chain bridge trust assumption. Without it, covenant composition requires a trusted relayer.

These five (UTXO-by-covenant lookup + the four above) are the complete set of covenant primitives needed for trustless verification, quorum-based security, and covenant composability on Kaspa. Happy to contribute reference implementations if there’s a branch to PR against.

Proposal: thin covenant-indexed UTXO lookup, backed by direct point-reads into the consensus virtual UTXO set

With covenant_id now a native field on UtxoEntry post-Toccata, there’s currently no way to enumerate all active UTXOs for a given covenant without running a custom external indexer that scans blocks and matches script patterns (see e.g. Covex, which already does exactly this via RPC WebSocket polling). Same gap applies more generally: there’s no way to fetch a UtxoEntry by raw (transaction_id, output_index) at all — getUtxosByAddresses only resolves standard address templates, and non-standard/covenant scriptPublicKeys aren’t addressable.

Proposed shape: a thin satellite index (analogous to UtxoIndex), keyed covenant_id → daa_score → transaction_id → output_index, populated from the same virtual-state diff stream. It stores only outpoint references, not amount/scriptPublicKey — no duplication of consensus data. Two new RPC methods: getUtxosByCovenantId(covenant_id, cursor) and a more general getUtxosByOutpoints(outpoints[]), both hard-capped at ~300 items per call, both resolving actual UTXO data via sequential calls to the existing UtxoSetStoreReader::get(outpoint) — no batch/MultiGet added to the consensus store itself, just repeated single-item reads, one request = one worker, with a configurable concurrency limit (semaphore) so load on the live UTXO set stays bounded and low-priority relative to consensus-critical paths.

Main advantages: (1) fully deterministic, bounded per-call cost via the fixed page size + cursor — no request can create unpredictable load; (2) works for any UTXO regardless of address-representability, closing a real gap, not just a covenant-specific one; (3) no duplicated/stale data — single source of truth is the live virtual UTXO set; (4) removes the practical requirement for covenant-based applications to depend on a trusted third-party indexer just to read their own on-chain state.

Question for maintainers: is this architecturally acceptable — i.e., is calling into UtxoSetStoreReader::get() from the RPC layer (which already depends on kaspa-consensusmanager) considered a reasonable use of that path, or is there a principled objection to RPC-triggered reads against the live consensus store this way? Would something along these lines have a realistic chance of being accepted, or is there a preferred alternative approach already in mind?