# Interfaces

An interface is a contract-level declaration that a contract conforms to a known, versioned standard. Declaring one buys you three things: the compiler and the node both *enforce* the standard's shape, wallets and infrastructure can integrate your contract without reading its code, and conforming contracts gain protocol features (partitioned state, compact operations) that ordinary contracts cannot use.

> Part of the SQRL documentation, section "Protocol".
> Rendered page: https://docs.sqrl.tech/protocol/contracts/interfaces

Interface IDs are versioned by suffix (`_v1`). A changed requirement is always a new ID, never a mutation of an existing one, so conformance can never silently drift under you.

## Syntax

Interfaces and function classifications are declared with annotations:

```scl
@interface(utxo_token_v1)
contract Token {
    @partition_scoped
    fn transfer(amounts: List<u64>) {
        // amounts[i] moves to the outpoint created by output i
        // of the anchoring transaction
        ...
    }

    fn mint(amount: Int, dest_vout: Int) {
        ...
    }

    @global
    fn burn(amount: Int) {
        ...
    }
}
```

The annotation set:

* **`@interface(<id>)`**: declares conformance, placed on the contract. The ID must be a known interface; an unknown ID, a duplicate declaration, or a bare `@interface` with no argument fails the build with a named reason.
* **`@partition_scoped`**: marks a function as touching only a bounded partition of state, derivable from its anchoring Bitcoin transaction. See [Global and partitioned state](/protocol/contracts/state-partitioning).
* **`@global`**: the explicit spelling of the default. An undeclared function is global.
* **`@compact_only`**: contract-level; declares that the contract is called exclusively through compact operations (below).
* **`@writeable_by(fn_a, fn_b)`**: field-level; restricts which functions may write a field. An empty writer list is rejected at compile time rather than silently locking the field.

## `utxo_token_v1`

The first standard interface is the UTXO-bound token. Token ownership is bound to Bitcoin outpoints rather than address-keyed balance maps, so tokens inherit Bitcoin's double-spend safety and compose naturally with Lightning channels and HTLCs, which are outpoint-level constructs.

Required functions:

| Function | Signature | Classification |
| --- | --- | --- |
| `transfer` | `(amounts: List<u64>)` | `@partition_scoped` required |
| `mint` | `(amount: Int, dest_vout: Int)` | global allowed (issuance may touch total supply) |
| `burn` | `(amount: Int)` | global allowed |

Note the addressing model: recipients are **vout indexes**, not addresses. The anchoring transaction's own outputs do the addressing, so a transfer names "output 1 of this transaction" and the tokens bind to whatever outpoint that output becomes. Ownership of the outpoint is ownership of the tokens.

`transfer` must be `@partition_scoped`; the compiler rejects a `utxo_token_v1` contract whose transfer is global, because the partitioned-stall guarantees of the interface depend on it.

## How declarations are treated

Enforcement happens twice, deliberately:

1. **At compile time**, `sclc` validates the declarations against the interface spec: every required function present, with exactly the required ABI signature and the required classification. Violations fail the build with a named reason. This is ergonomics: you find out immediately.
2. **At registration**, the node re-runs the same checks against the emitted ABI and deploy metadata. This is consensus: a hand-crafted artifact cannot claim conformance it does not have.

The compiler emits the declarations as deploy metadata alongside the bytecode and ABI. Only non-default entries are recorded; an empty object means full defaults:

```json
{
  "interfaces": ["utxo_token_v1"],
  "functions": { "transfer": "partition_scoped" }
}
```

This metadata travels in the `metadata` field of [`POST /deploy_contract`](/protocol/nodes/api-reference).

## Compact operations

For interface-defined operations, conforming contracts can skip the call envelope entirely. A compact operation carries the selector and arguments (43 bytes or less) directly inside the anchoring transaction's `OP_RETURN`, in place of the payload hash. A `utxo_token_v1` transfer is a handful of bytes: selector and per-output amounts, with the transaction's outputs doing the addressing.

The consequences are significant:

* **No envelope, nothing to withhold.** The data rides where the hash would have been, so there is zero stall surface: nobody can freeze state by withholding a payload that is already on-chain.
* **Authorization is the Bitcoin spend itself.** The spend of the token-bound input authorizes the operation; replay is structurally impossible.
* **Final at Bitcoin depth.** Compact operations are unvoidable; there is no void window to wait out.

The trade-off is visibility: arguments (amounts) become chain-visible. Compact operations are therefore an opt-in per contract, not a replacement for hash-only envelope calls. A contract deployed `@compact_only` accepts nothing else, and as a result can never stall.

## Why declare an interface

* **Enforced shape, twice.** Conformance is checked by the compiler and re-checked by the node. Integrators rely on the standard, not on your discipline.
* **Legibility.** Wallets, indexers, explorers, and Lightning infrastructure can enumerate balances and drive any conforming token through the same entry points, with no contract-specific knowledge.
* **Partitioned stalls.** Only interface contracts with UTXO-bound state qualify for partition-scoped classification, which is what keeps one user's withheld data from freezing anyone else. See [Global and partitioned state](/protocol/contracts/state-partitioning).
* **Compact operations.** The cheapest, stall-proof, replay-proof call path is exclusive to interface-defined operations.
