Silverscript is Kaspa’s high-level smart contract language. It compiles directly to native Kaspa Script – no virtual machine, no intermediate representation. Designed for writing covenant spending conditions on L1 UTXOs.
Repository: github.com/kaspanet/silverscript Status: Experimental, TN12 only (as of March 2026) Language: Rust (90.8%), WASM support planned License: ISC (permissive)
Architecture Context
Silverscript occupies the local-state layer of Kaspa’s programmability stack:
| Silverscript | vProgs | |
|---|---|---|
| State model | Local (UTXO) | Shared (accounts) |
| Execution | On-chain (L1 script validation) | Off-chain (ZK proof verification) |
| Scope | Covenant spending conditions | Sovereign state machines |
| Composability | Transaction-level (inputs/outputs) | Cross-program atomic (Phase 2) |
| Target | Native Kaspa Script | ZK circuits |
Together they form the two-layer programmability model: Silverscript manages UTXO-level plumbing; vProgs manage application-level logic. A mature dApp will often use both.
Contract Structure
Every Silverscript program defines a single contract with constructor parameters and entrypoint functions:
pragma silverscript ^0.1.0;
contract ContractName(param1Type param1, param2Type param2) {
state {
// Optional state fields
}
entrypoint function functionName(argType arg) {
// Function body
}
}
Components
| Component | Required | Description |
|---|---|---|
pragma |
Yes | Compiler version constraint |
contract |
Yes | Contract declaration with constructor parameters |
state |
No | Persistent state carried across UTXO transitions |
entrypoint |
Yes (at least one) | Functions callable when spending the UTXO |
Constructor parameters are baked into the script at deployment time. They become part of the script hash and cannot be changed after deployment.
Data Types
Scalar Types
| Type | Size | Description |
|---|---|---|
int |
64-bit signed | Integer arithmetic (8-byte support via KIP-10) |
bool |
1 bit | Boolean (true / false) |
string |
Variable | UTF-8 string data |
byte |
1 byte | Single byte |
bytes |
Variable | Arbitrary byte sequence |
bytes20 |
20 bytes | Fixed-size byte array |
bytes32 |
32 bytes | Fixed-size byte array (hashes, IDs) |
Cryptographic Types
| Type | Size | Description |
|---|---|---|
pubkey |
32 bytes | Schnorr public key |
sig |
65 bytes | Schnorr signature |
datasig |
64 bytes | Data signature (non-transaction) |
Collection Types
| Type | Description |
|---|---|
type[] |
Dynamic-length array |
type[N] |
Fixed-size array (N is a compile-time constant) |
Unit Literals
Value units:
1 kas = 100000000 sompis
1 grains = 100 sompis
1 litras = 1 sompi
Time units:
1 weeks = 604800 seconds
1 days = 86400 seconds
1 hours = 3600 seconds
1 minutes = 60 seconds
1 seconds = 1 second
Usage:
require(tx.outputs[0].value >= 10 kas);
require(this.age >= 7 days);
Operators
Arithmetic
| Operator | Description |
|---|---|
+ |
Addition |
- |
Subtraction |
* |
Multiplication |
/ |
Integer division |
% |
Modulo |
Comparison
| Operator | Description |
|---|---|
== |
Equal |
!= |
Not equal |
< |
Less than |
> |
Greater than |
<= |
Less than or equal |
>= |
Greater than or equal |
Logical
| Operator | Description |
|---|---|
&& |
Logical AND |
\|\| |
Logical OR |
! |
Logical NOT |
Bitwise
| Operator | Description |
|---|---|
& |
Bitwise AND |
\| |
Bitwise OR |
^ |
Bitwise XOR |
Transaction Introspection API
Contracts can inspect the spending transaction via built-in properties. These map to KIP-10 transaction introspection opcodes.
Transaction-Level
| Expression | Returns | Opcode |
|---|---|---|
tx.inputs.length |
Number of inputs | OpTxInputCount |
tx.outputs.length |
Number of outputs | OpTxOutputCount |
Input Properties
| Expression | Returns | Opcode |
|---|---|---|
tx.inputs[i].value |
Input value in sompis | OpTxInputAmount |
tx.inputs[i].scriptPubKey |
Input script public key | OpTxInputSpk |
Output Properties
| Expression | Returns | Opcode |
|---|---|---|
tx.outputs[i].value |
Output value in sompis | OpTxOutputAmount |
tx.outputs[i].scriptPubKey |
Output script public key | OpTxOutputSpk |
Active Input Context
| Expression | Returns | Description |
|---|---|---|
this.activeInputIndex |
Current input index | Which input is being validated |
this.activeScriptPubKey |
Current script | The script being executed |
this.age |
UTXO age in seconds | Time since the UTXO was created |
Cryptographic Primitives
| Function | Description |
|---|---|
blake2b(data) |
BLAKE2b hash (native to Kaspa) |
sha256(data) |
SHA-256 hash |
checkSig(signature, publicKey) |
Schnorr signature verification |
Usage:
// Verify a signature
require(checkSig(ownerSig, owner));
// Hash data
bytes32 hash = blake2b(someData);
// Compare hashes
require(blake2b(preimage) == expectedHash);
Control Flow
Conditionals
if (condition) {
// true branch
} else {
// false branch
}
Assertions
require(condition);
require fails the script (and therefore the transaction) if the condition is false. This is the primary mechanism for enforcing spending rules.
Loops
for (i, start, end, MAX_ITERATIONS) {
// loop body
}
i– loop variablestart– initial value (inclusive)end– end value (exclusive), can be a runtime valueMAX_ITERATIONS– compile-time upper bound (the compiler unrolls to this count)
Example:
// Sum all output values (up to 10 outputs)
int total = 0;
for (i, 0, tx.outputs.length, 10) {
total = total + tx.outputs[i].value;
}
The MAX_ITERATIONS parameter is required because Kaspa Script does not support true loops – the compiler unrolls the loop at compile time.
Covenant Declaration System
The most powerful Silverscript feature is the covenant declaration macro system. It generates correct covenant entrypoints automatically, providing security-by-construction.
Declaration Patterns
1:1 Transition (Singleton)
Single input, single output. The function computes the new state.
#[covenant.singleton(mode = transition)]
entrypoint function update(State prev_state, int delta) : (State new_state) {
return State { counter: prev_state.counter + delta };
}
The compiler generates:
- Prior state reconstruction from transaction context
- Output state validation via
validateOutputState() - Covenant ID and lineage checks (KIP-20)
1:1 Verification (Singleton)
Single input, single output. The developer validates the proposed output.
#[covenant.singleton(mode = verification)]
entrypoint function update(State prev_state, int newValue) {
require(newValue > prev_state.value);
require(tx.outputs[0].value >= tx.inputs[0].value);
}
1:N Verification
Single input, multiple outputs. Validates a split or distribution.
#[covenant(from = 1, to = N)]
entrypoint function split(
State prev_state,
State[] new_states,
int[] amounts
) {
int total = 0;
for (i, 0, amounts.length, 50) {
total = total + amounts[i];
require(amounts[i] > 0);
}
require(total == tx.inputs[this.activeInputIndex].value);
}
Default binding: Auth binding (OpAuth*) – per-input authorization.
N:M Verification
Multiple inputs, multiple outputs. Validates complex multi-party flows.
#[covenant(from = N, to = M)]
entrypoint function swap(
State[] prev_states,
State[] new_states,
sig[] approvals
) {
// Validate the swap logic
// All parties must approve
for (i, 0, approvals.length, 10) {
require(checkSig(approvals[i], prev_states[i].owner));
}
}
Required binding: Cov binding (OpCov*) – shared covenant context across inputs.
N:M Transition
Multiple inputs, multiple outputs. The function computes new states.
#[covenant(from = N, to = M, mode = transition)]
entrypoint function merge(
State[] prev_states
) : (State[] new_states) {
// Compute merged state
// ...
}
Pattern Summary
| Pattern | Inputs | Outputs | Binding | Use Case |
|---|---|---|---|---|
| 1:1 transition | 1 | 1 | Auth | Counter, vault update |
| 1:1 verification | 1 | 1 | Auth | Conditional update |
| 1:N verification | 1 | N | Auth | Token distribution, fan-out |
| N:M verification | N | M | Cov | Swaps, merges, batch ops |
| N:M transition | N | M | Cov | Complex multi-party flows |
Binding Modes
Auth Binding (OpAuth*)
- Per-input authorization
- Default for 1:1 and 1:N patterns
- Each input is authorized independently
- Uses
OpAuthOutputCount,OpAuthOutputIndex, etc.
Cov Binding (OpCov*)
- Shared covenant context across multiple inputs
- Required for N:M patterns
- Covenant ID derived from
OpInputCovenantId - Uses
OpCovOutputCount,OpCovOutputIndex, etc.
Verification vs. Transition Mode
Verification Mode
The developer checks that proposed outputs satisfy the contract’s rules:
#[covenant.singleton(mode = verification)]
entrypoint function update(State prev_state, int newVal) {
// Developer validates the output
require(newVal > 0);
require(tx.outputs[0].value >= 1 kas);
}
Use when: validation logic is simpler than computation, or when outputs are constructed externally.
Transition Mode
The developer computes what the outputs should be; the compiler generates validation:
#[covenant.singleton(mode = transition)]
entrypoint function bump(State prev_state) : (State new_state) {
// Developer computes new state
return State { counter: prev_state.counter + 1 };
}
Use when: the new state is deterministically computable from the inputs and action.
State Management
Declaring State
state {
int counter;
pubkey lastSigner;
bool isLocked;
}
State is serialized and carried in the UTXO’s script public key. The compiler handles:
- Serialization and deserialization
- State reconstruction from prior UTXO via
readInputState() - State validation in outputs via
validateOutputState()
Accessing Prior State
In entrypoint functions with covenant declarations, the prior state is passed as the first parameter:
entrypoint function update(State prev_state, int delta) {
// prev_state is automatically reconstructed from the spending UTXO
}
State Transitions
In transition mode, return the new state. The compiler validates that the output UTXO carries this exact state:
return State { counter: prev_state.counter + delta };
In verification mode, manually check output properties:
require(tx.outputs[0].value >= minValue);
Compiler Security Guarantees
The Silverscript compiler provides security-by-construction by generating:
- Prior state reconstruction – correct deserialization from transaction context
- Output cardinality enforcement – the declared pattern (1:1, 1:N, N:M) is enforced
- State validation –
validateOutputState()ensures outputs carry correct state - Covenant ID checks – KIP-20 lineage is verified (outputs must share the covenant ID)
- Binding mode enforcement – Auth vs. Cov opcodes are selected correctly
This eliminates entire classes of bugs that manual script writing would introduce:
- No reentrancy (UTXO consumed exactly once)
- No storage collisions (state is per-UTXO)
- No unbounded gas (scripts are bounded and deterministic)
- No lineage forgery (Covenant IDs are protocol-enforced)
Compilation
CLI
# Compile to Kaspa Script
silverscript compile contract.ss --output contract.script
# Compile with debug symbols
silverscript compile contract.ss --debug
# Check syntax without compilation
silverscript check contract.ss
Output
The compiler produces native Kaspa Script that runs directly on L1. No VM, no interpreter – the script is validated by Kaspa’s consensus-level script engine.
Complete Example: Mecenas (Recurring Payment)
pragma silverscript ^0.1.0;
// Mecenas: recurring payment contract
// Allows a patron to pay a beneficiary a fixed amount per period
contract Mecenas(
pubkey patron,
pubkey beneficiary,
int paymentAmount,
int period
) {
state {
int lastPaymentTime;
}
// Beneficiary claims a payment after the period elapses
#[covenant.singleton(mode = transition)]
entrypoint function claim(
State prev_state,
sig beneficiarySig
) : (State new_state) {
require(checkSig(beneficiarySig, beneficiary));
require(this.age >= period);
// Ensure sufficient funds remain for the payment
int inputValue = tx.inputs[this.activeInputIndex].value;
require(inputValue >= paymentAmount);
// Output must retain remaining funds minus payment
int remaining = inputValue - paymentAmount;
require(tx.outputs[0].value == remaining);
return State {
lastPaymentTime: 0
};
}
// Patron can reclaim all funds at any time
entrypoint function reclaim(sig patronSig) {
require(checkSig(patronSig, patron));
}
}
Related Resources
- Build a Vault with Silverscript – Step-by-step tutorial
- Create a Native Asset – Token issuance with covenants
- Inline ZK Covenant – Combining Silverscript with ZK proofs
- API Reference – RPC endpoints for transaction submission
- Silverscript Repository: github.com/kaspanet/silverscript