> For the complete documentation index, see [llms.txt](https://canopy-network.gitbook.io/docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://canopy-network.gitbook.io/docs/build/plugin-api-reference.md).

# Plugin API Reference

This document is a complete reference for the Canopy Application Blockchain Interface (ABI) — the contract your plugin implements and the types it works with.

A Canopy Template defines the application-specific behavior of a chain. It declares the transactions an application supports, validates those transactions, and determines how they change state.

Canopy provides the node, consensus, networking, transaction processing, and persistent state machine. A Template supplies the application logic that runs within that environment.

“Canopy Template” is the product term used throughout these docs. The repository uses `plugin` in source paths, configuration, and build commands to describe each language-specific implementation.

### Source of truth

This page is a detailed guide to the runtime model, with Go used for concrete examples. For generated types, protobuf definitions, exact signatures, and implementation updates, refer to the maintained source:

* [Canopy Template implementations](https://github.com/canopy-network/canopy/tree/main/plugin)
* [Go Template tutorial](https://github.com/canopy-network/canopy/blob/main/plugin/go/TUTORIAL.md)
* [Go Template protocol definitions](https://github.com/canopy-network/canopy/blob/main/plugin/go/proto/plugin.proto)
* [Go Template runtime](https://github.com/canopy-network/canopy/blob/main/plugin/go/contract/plugin.go)
* [Go Template contract](https://github.com/canopy-network/canopy/blob/main/plugin/go/contract/contract.go)
* [Go Template errors](https://github.com/canopy-network/canopy/blob/main/plugin/go/contract/error.go)

The runtime model is shared, but language implementations have their own build tooling, helper functions, and generated types. Do not assume every language exposes identical method signatures.

### Runtime model

A Template is an application process that communicates with the Canopy state machine. In the Go reference implementation, communication occurs through a local Unix socket using protobuf messages.

The Template does not manage consensus, peer-to-peer networking, block propagation, or its own database. Canopy invokes the Template as blocks and transactions are processed. The Template reads and writes persistent state through the runtime, then returns deterministic results.

This separation lets builders focus on application behavior. A Template can implement a guestbook, marketplace, game, registry, or community application without implementing the underlying blockchain node.

Every result must be deterministic. Given the same transaction and the same chain state, every validator must reach the same result. Do not depend on wall-clock time, random values that affect state, network calls, local files, or other inputs that can differ between nodes.

### Template configuration

At startup, the Go Template sends `PluginConfig` to Canopy during a handshake. This declares the Template’s identity, supported transaction types, protobuf descriptors, events, and owned state prefixes.

```
var ContractConfig = &PluginConfig{
    Name:                  "go_plugin_contract",
    Id:                    1,
    Version:               1,
    SupportedTransactions: []string{"send"},
    TransactionTypeUrls: []string{
        "type.googleapis.com/types.MessageSend",
    },
    EventTypeUrls: nil,
}
```

The Go Template populates `FileDescriptorProtos` during initialization. Do not manually recreate that behavior unless you are modifying the implementation itself.

| **Field**               | **Type**   | **Purpose**                                                                                                                  |
| ----------------------- | ---------- | ---------------------------------------------------------------------------------------------------------------------------- |
| `Name`                  | `string`   | A human-readable Template identifier, primarily used in logs and diagnostics.                                                |
| `Id`                    | `uint64`   | The Template identifier supplied to the runtime.                                                                             |
| `Version`               | `uint64`   | The Template version. Increment it when making a compatibility-relevant change.                                              |
| `SupportedTransactions` | `[]string` | The short names of transaction types the Template handles, such as `send` or `post_message`.                                 |
| `TransactionTypeUrls`   | `[]string` | The protobuf type URLs corresponding to supported transactions.                                                              |
| `FileDescriptorProtos`  | `[][]byte` | Serialized protobuf descriptors that allow Canopy to decode custom message types. The Go Template fills these automatically. |
| `EventTypeUrls`         | `[]string` | Protobuf type URLs for custom events emitted by the Template.                                                                |
| `CustomStatePrefixes`   | `[][]byte` | State-key prefixes owned by the Template’s custom records.                                                                   |

`SupportedTransactions` and `TransactionTypeUrls` are positional pairs. The entry at each index must describe the same transaction type. A mismatch can cause routing or decoding failures.

When adding a transaction, update its registration, protobuf schema, validation logic, delivery logic, state handling, and tests together.

### Lifecycle methods

Canopy invokes a Template at defined stages of block and transaction processing. The Go Template exposes these stages as `Genesis`, `BeginBlock`, `CheckTx`, `DeliverTx`, and `EndBlock`.

```
func (c *Contract) Genesis(request *PluginGenesisRequest) *PluginGenesisResponse
func (c *Contract) BeginBlock(request *PluginBeginRequest) *PluginBeginResponse
func (c *Contract) CheckTx(request *PluginCheckRequest) *PluginCheckResponse
func (c *Contract) DeliverTx(request *PluginDeliverRequest) *PluginDeliverResponse
func (c *Contract) EndBlock(request *PluginEndRequest) *PluginEndResponse
```

| **Stage**    | **Input**                         | **Purpose**                                                            |
| ------------ | --------------------------------- | ---------------------------------------------------------------------- |
| `Genesis`    | Genesis JSON bytes                | Initialize application state when a chain starts.                      |
| `BeginBlock` | Block height                      | Deterministic setup before transactions are applied.                   |
| `CheckTx`    | Transaction and height            | Early transaction validation and signer declaration.                   |
| `DeliverTx`  | Transaction and height            | Stateful transaction execution during block processing.                |
| `EndBlock`   | Block height and proposer address | Deterministic finalization after the block’s transactions are applied. |

#### Genesis

`Genesis` receives configured genesis JSON as bytes in `GenesisJson`. Use it to initialize application records that must exist before the first application transaction, such as parameters, initial registries, or state counters.

The default Go Template does not initialize application-specific state for you. Implement explicit genesis logic whenever your application relies on non-empty initial state.

| **Response field** | **Type**       | **Purpose**                                                       |
| ------------------ | -------------- | ----------------------------------------------------------------- |
| `Error`            | `*PluginError` | Return an error to halt initialization. Return `nil` for success. |

#### BeginBlock

`BeginBlock` receives the height of the block about to be applied. Use it for deterministic work that must occur before the block’s transactions, such as resetting a per-block value or checking a height-based application condition.

| Input field | Type     | Purpose                                      |
| ----------- | -------- | -------------------------------------------- |
| `Height`    | `uint64` | The height of the block about to be applied. |

| Response field | Type           | Purpose                                           |
| -------------- | -------------- | ------------------------------------------------- |
| `Events`       | `[]*Event`     | Optional events emitted at the start of a block.  |
| `Error`        | `*PluginError` | Return an error when block setup cannot complete. |

Do not use `BeginBlock` as a workaround for missing transaction context. The current protocol provides a height directly in both `PluginCheckRequest` and `PluginDeliverRequest`.

#### CheckTx

`CheckTx` is the early validation stage. It runs when a transaction reaches a node’s mempool.

| Input field | Type           | Purpose                                                                                    |
| ----------- | -------------- | ------------------------------------------------------------------------------------------ |
| `Tx`        | `*Transaction` | The transaction to validate. Decode `Tx.Msg` to retrieve the concrete application message. |
| `Height`    | `uint64`       | The execution height supplied with the check request.                                      |

| Response field      | Type           | Purpose                                                           |
| ------------------- | -------------- | ----------------------------------------------------------------- |
| `AuthorizedSigners` | `[][]byte`     | Addresses whose signatures must authorize the transaction.        |
| `Recipient`         | `[]byte`       | Optional recipient address, used for indexing where applicable.   |
| `Error`             | `*PluginError` | Return an error to reject the transaction before block inclusion. |

Use `CheckTx` to reject malformed messages, invalid addresses, unsupported transaction types, invalid field values, and transactions that clearly lack the required authorization.

`CheckTx` cannot write state. The current Go Template may perform limited state reads during validation, such as reading fee parameters. Keep those reads narrow and deterministic. State can change between mempool validation and block delivery, so authoritative state-dependent checks must be repeated in `DeliverTx`.

A typical Go dispatch pattern is:

```
msg, err := FromAny(request.Tx.Msg)
if err != nil {
    return &PluginCheckResponse{Error: err}
}

switch x := msg.(type) {
case *MessageSend:
    return c.CheckMessageSend(x)
case *MessagePost:
    return c.CheckMessagePost(x)
default:
    return &PluginCheckResponse{Error: ErrInvalidMessageCast()}
}
```

#### DeliverTx

`DeliverTx` receives a transaction and the height at which it is being processed. It is the stateful execution stage.

| **Input field** | **Type**       | **Purpose**                                                 |
| --------------- | -------------- | ----------------------------------------------------------- |
| `Tx`            | `*Transaction` | The transaction to execute.                                 |
| `Height`        | `uint64`       | The block height at which the transaction is being applied. |

| **Response field** | **Type**       | **Purpose**                                     |
| ------------------ | -------------- | ----------------------------------------------- |
| `Events`           | `[]*Event`     | Optional events emitted for the transaction.    |
| `Error`            | `*PluginError` | Return an error when execution cannot complete. |

Use `DeliverTx` to read the application and protocol records needed for a transaction, validate state-dependent conditions, calculate the resulting state, and write changed records.

A typical delivery flow is:

1. Decode the transaction’s protobuf `Any` payload.
2. Read the records needed to evaluate the transaction.
3. Validate all state-dependent conditions.
4. Calculate the complete new state.
5. Write the changed records.
6. Return any relevant event.

Do not depend only on `CheckTx` for important validation. `DeliverTx` is the authoritative place to validate rules that depend on current balances, record ownership, counters, or other mutable state.

#### EndBlock

`EndBlock` receives the finished block height and proposer address.

| **Input field**   | **Type** | **Purpose**                                           |
| ----------------- | -------- | ----------------------------------------------------- |
| `Height`          | `uint64` | The block height that has just completed.             |
| `ProposerAddress` | `[]byte` | The address of the validator that proposed the block. |

| **Response field** | **Type**       | **Purpose**                                              |
| ------------------ | -------------- | -------------------------------------------------------- |
| `Events`           | `[]*Event`     | Optional events emitted at the end of a block.           |
| `Error`            | `*PluginError` | Return an error when end-of-block logic cannot complete. |

Use `EndBlock` for deterministic work that belongs after all transactions in a block, such as height-based expiration, aggregate calculations, or end-of-block events. Do not use it for work that must happen once per transaction.

### State operations

Canopy owns the persistent state machine. A Template accesses state through runtime helpers rather than connecting directly to a database.

The Go Template provides `StateRead` and `StateWrite`.

```
func (p *Plugin) StateRead(
    c *Contract,
    request *PluginStateReadRequest,
) (*PluginStateReadResponse, *PluginError)

func (p *Plugin) StateWrite(
    c *Contract,
    request *PluginStateWriteRequest,
) (*PluginStateWriteResponse, *PluginError)
```

Batch related reads and writes. A transaction that moves a balance, creates an application record, and updates a counter should evaluate those values as one state transition, not as unrelated operations.

#### StateRead

`StateRead` can batch exact key lookups and prefix-based range reads in one request.

| **Field** | **Type**             | **Purpose**               |
| --------- | -------------------- | ------------------------- |
| `Keys`    | `[]*PluginKeyRead`   | Exact key lookups.        |
| `Ranges`  | `[]*PluginRangeRead` | Prefix-based range reads. |

**PluginKeyRead**

| **Field** | **Type** | **Purpose**                                                           |
| --------- | -------- | --------------------------------------------------------------------- |
| `QueryId` | `uint64` | A caller-assigned identifier used to match a response to its request. |
| `Key`     | `[]byte` | The exact state key to retrieve.                                      |

**PluginRangeRead**

| **Field** | **Type** | **Purpose**                                                           |
| --------- | -------- | --------------------------------------------------------------------- |
| `QueryId` | `uint64` | A caller-assigned identifier used to match a response to its request. |
| `Prefix`  | `[]byte` | The key prefix to scan.                                               |
| `Limit`   | `uint64` | The maximum number of matching records to return.                     |
| `Reverse` | `bool`   | Whether matching records are returned in reverse key order.           |

Always set a reasonable limit for an application-facing list. An unbounded range scan can become expensive as application state grows.

**PluginStateReadResponse**

| **Field** | **Type**              | **Purpose**                                                         |
| --------- | --------------------- | ------------------------------------------------------------------- |
| `Results` | `[]*PluginReadResult` | Results for point reads and range reads, matched through `QueryId`. |
| `Error`   | `*PluginError`        | A runtime error returned while executing the read request.          |

**PluginReadResult**

| **Field** | **Type**              | **Purpose**                                                                     |
| --------- | --------------------- | ------------------------------------------------------------------------------- |
| `QueryId` | `uint64`              | Matches the `QueryId` from the original read request.                           |
| `Entries` | `[]*PluginStateEntry` | Matching key-value records. An empty result means no matching record was found. |

**PluginStateEntry**

| **Field** | **Type** | **Purpose**                                                                               |
| --------- | -------- | ----------------------------------------------------------------------------------------- |
| `Key`     | `[]byte` | The state key.                                                                            |
| `Value`   | `[]byte` | Raw bytes stored at that key. Decode them using the serialization format for that record. |

#### Go example: batch point lookup

This pattern reads the sender account, recipient account, and fee pool in one request. It then matches each response by `QueryId`.

```
fromID := rand.Uint64()
toID := rand.Uint64()
feeID := rand.Uint64()

resp, err := c.plugin.StateRead(c, &PluginStateReadRequest{
    Keys: []*PluginKeyRead{
        {
            QueryId: fromID,
            Key:     KeyForAccount(msg.FromAddress),
        },
        {
            QueryId: toID,
            Key:     KeyForAccount(msg.ToAddress),
        },
        {
            QueryId: feeID,
            Key:     KeyForFeePool(c.Config.ChainId),
        },
    },
})
if err != nil {
    return &PluginDeliverResponse{Error: err}
}
if resp.Error != nil {
    return &PluginDeliverResponse{Error: resp.Error}
}

results := make(map[uint64]*PluginReadResult)
for _, result := range resp.Results {
    results[result.QueryId] = result
}

fromResult := results[fromID]
toResult := results[toID]
feeResult := results[feeID]

if fromResult == nil || len(fromResult.Entries) == 0 {
    return &PluginDeliverResponse{Error: ErrInsufficientFunds()}
}
if feeResult == nil || len(feeResult.Entries) == 0 {
    return &PluginDeliverResponse{Error: ErrFailedPluginRead(
        fmt.Errorf("fee pool not found"),
    )}
}

fromAccount := new(Account)
toAccount := new(Account)
feePool := new(Pool)

if err := Unmarshal(fromResult.Entries[0].Value, fromAccount); err != nil {
    return &PluginDeliverResponse{Error: err}
}
if toResult != nil && len(toResult.Entries) > 0 {
    if err := Unmarshal(toResult.Entries[0].Value, toAccount); err != nil {
        return &PluginDeliverResponse{Error: err}
    }
}
if err := Unmarshal(feeResult.Entries[0].Value, feePool); err != nil {
    return &PluginDeliverResponse{Error: err}
}
```

The important pattern is not the specific account and fee-pool logic. It is batching related reads, checking runtime errors, checking for missing entries, and matching results by `QueryId` instead of assuming response order.

#### StateWrite

`StateWrite` batches record updates and deletions.

| **Field** | **Type**            | **Purpose**                  |
| --------- | ------------------- | ---------------------------- |
| `Sets`    | `[]*PluginSetOp`    | Records to write or replace. |
| `Deletes` | `[]*PluginDeleteOp` | Records to remove.           |

**PluginSetOp**

| **Field** | **Type** | **Purpose**                                 |
| --------- | -------- | ------------------------------------------- |
| `Key`     | `[]byte` | The state key to write.                     |
| `Value`   | `[]byte` | The serialized record to store at that key. |

**PluginDeleteOp**

| **Field** | **Type** | **Purpose**              |
| --------- | -------- | ------------------------ |
| `Key`     | `[]byte` | The state key to remove. |

#### Go example: mixed writes and deletes

This pattern updates the recipient and fee pool, then either updates or removes the sender record.

```
fromBytes, err := Marshal(fromAccount)
if err != nil {
    return &PluginDeliverResponse{Error: err}
}

toBytes, err := Marshal(toAccount)
if err != nil {
    return &PluginDeliverResponse{Error: err}
}

feeBytes, err := Marshal(feePool)
if err != nil {
    return &PluginDeliverResponse{Error: err}
}

writeRequest := &PluginStateWriteRequest{
    Sets: []*PluginSetOp{
        {
            Key:   KeyForAccount(msg.ToAddress),
            Value: toBytes,
        },
        {
            Key:   KeyForFeePool(c.Config.ChainId),
            Value: feeBytes,
        },
    },
}

if fromAccount.Amount == 0 {
    writeRequest.Deletes = []*PluginDeleteOp{
        {
            Key: KeyForAccount(msg.FromAddress),
        },
    }
} else {
    writeRequest.Sets = append(writeRequest.Sets, &PluginSetOp{
        Key:   KeyForAccount(msg.FromAddress),
        Value: fromBytes,
    })
}

writeResp, err := c.plugin.StateWrite(c, writeRequest)
if err != nil {
    return &PluginDeliverResponse{Error: err}
}
if writeResp.Error != nil {
    return &PluginDeliverResponse{Error: writeResp.Error}
}
```

Use a stable serialization format for stored values. The Go Template uses protobuf messages. Once state exists on a chain, changing a record schema is a compatibility decision and should be planned, tested, and versioned carefully.

### State-key ownership

Canopy Templates share the state machine keyspace with core protocol records. Application records must use their own declared state prefixes.

The single-byte range `1` through `15` is reserved for core Canopy state. Do not use values in that range for application records.

Every application-owned prefix must be declared in `CustomStatePrefixes`. Canopy validates these declarations during the startup handshake. A prefix that collides with the reserved core range causes startup to fail before the Template processes blocks.

The current Go Template tutorial uses `100` and `101` as examples of valid custom prefixes:

```
var (
    postPrefix        = []byte{100}
    postCounterPrefix = []byte{101}
)

var ContractConfig = &PluginConfig{
    // Other Template configuration.
    CustomStatePrefixes: [][]byte{
        postPrefix,
        postCounterPrefix,
    },
}
```

Use a distinct prefix for each record family when that makes access patterns clearer. For example, a guestbook can use one prefix for posts and another for its counter or author index. Do not later reuse a prefix for an incompatible record type.

The Go Template provides `JoinLenPrefix` to construct unambiguous composite keys:

```
func KeyForPost(id uint64) []byte {
    idBytes := make([]byte, 8)
    binary.BigEndian.PutUint64(idBytes, id)

    return JoinLenPrefix(postPrefix, idBytes)
}
```

`JoinLenPrefix` length-prefixes the segments before combining them. This prevents collisions between key components of different lengths.

### Transactions and built-in types

A Canopy transaction wraps an application message in a protobuf `Any` value. The Template decodes that payload and routes it to the appropriate validation or delivery handler.

| **Transaction field** | **Purpose**                                                           |
| --------------------- | --------------------------------------------------------------------- |
| Message type          | Identifies the registered transaction type.                           |
| Message payload       | A protobuf `Any` containing the concrete application message.         |
| Signature             | Contains the public key and signature that authorize the transaction. |
| Created height        | Records the transaction’s creation height.                            |
| Time                  | Provides transaction creation time data.                              |
| Fee                   | The fee supplied with the transaction.                                |
| Memo                  | Optional transaction metadata.                                        |
| Network ID            | Identifies the target network.                                        |
| Chain ID              | Identifies the target chain.                                          |

Do not hard-code signature lengths or assume one cryptographic scheme in application documentation. Use the current language implementation and signing tools for the active network.

#### Account

The base Template defines an `Account` record for token ownership.

| **Field** | **Purpose**                    |
| --------- | ------------------------------ |
| `Address` | The account’s 20-byte address. |
| `Amount`  | The account’s token balance.   |

An account can include additional protocol fields over time. Use the current protobuf schema when working directly with account records.

#### Pool

A `Pool` is a protocol-controlled balance rather than an account controlled by a private key.

| **Field** | **Purpose**          |
| --------- | -------------------- |
| `Id`      | The pool identifier. |
| `Amount`  | The pool balance.    |

The base Go Template uses a pool for collected transaction fees.

#### Event

Events let a Template expose meaningful application state transitions to downstream consumers.

| **Field**       | **Purpose**                                                       |
| --------------- | ----------------------------------------------------------------- |
| Event type      | A short categorization label for the event.                       |
| Message payload | A typed protobuf payload for the event.                           |
| Height          | The block height associated with the event.                       |
| Reference       | The lifecycle or transaction reference associated with the event. |
| Chain ID        | The chain that emitted the event.                                 |
| Address         | The address most relevant to the event, where applicable.         |

Define event types deliberately. Keep event schemas stable and include only the data an indexer or application client needs.

### Fee parameters

The base Template reads protocol-controlled fee parameters from state. Its built-in fee parameters include the minimum fees for core transaction types.

An application that introduces a new transaction type may need its own fee policy. If that policy must be governed onchain, add it through the current protobuf and parameter conventions. Do not modify existing protobuf field numbers, reuse a field number for a new meaning, or rely on a hard-coded fee when the active network can change its parameters.

For a simpler application, use existing chain fee rules and validate the supplied fee in `CheckTx` and `DeliverTx` as appropriate.

### Error handling

The Go Template returns `PluginError` values with a numeric code, module, and message.

| **Field** | **Type** | **Purpose**                      |
| --------- | -------- | -------------------------------- |
| `Code`    | `uint64` | A machine-readable error code.   |
| `Module`  | `string` | The source module for the error. |
| `Msg`     | `string` | A human-readable error message.  |

The current base implementation reserves codes `1` through `14`.

| **Code** | **Meaning**                                            |
| -------- | ------------------------------------------------------ |
| `1`      | Template response timeout.                             |
| `2`      | Serialization failed.                                  |
| `3`      | Deserialization failed.                                |
| `4`      | State read failed.                                     |
| `5`      | State write failed.                                    |
| `6`      | A runtime response ID did not match a pending request. |
| `7`      | The runtime sent an unexpected message type.           |
| `8`      | The runtime sent an invalid message.                   |
| `9`      | Insufficient funds.                                    |
| `10`     | Failed to unpack a protobuf `Any` payload.             |
| `11`     | The message did not match a registered type.           |
| `12`     | Invalid address.                                       |
| `13`     | Invalid amount.                                        |
| `14`     | Transaction fee is below the required state value.     |

If an application defines additional error codes, use a separate documented range and preserve the meaning of a code over time. Clients should not have to infer application behavior from a changing error message.

### Lifecycle execution order

For a block containing two transactions, the conceptual order is:

```
BeginBlock(height = N)

DeliverTx(transaction 0)

DeliverTx(transaction 1)

EndBlock(height = N)
```

`CheckTx` is not part of block application. It occurs earlier, when a node receives a transaction for mempool validation. By the time `DeliverTx` runs, the transaction has already passed the relevant admission checks.

This is why a Template must distinguish early validation from authoritative stateful execution.

### Application-specific RPC

The built-in Canopy RPC exposes node and protocol data. A Template can also provide application-specific query endpoints, such as retrieving a post by ID or listing recent posts.

In the Go implementation, `QueryState` provides a detached, read-only state query path for custom RPC handlers. Unlike lifecycle state reads, it is designed for an application endpoint that is not currently handling a transaction or block event.

Keep custom endpoints narrow and intentional. A frontend should ask for “recent posts,” not scan raw key prefixes or decode storage values itself. Validate endpoint inputs, set limits on range queries, and write end-to-end tests for every endpoint.

The current implementation details are in the [Go Template runtime](https://github.com/canopy-network/canopy/blob/main/plugin/go/contract/plugin.go) and [Go Template tutorial](https://github.com/canopy-network/canopy/blob/main/plugin/go/TUTORIAL.md).

### Language implementations

Canopy provides Templates in Go, TypeScript, Python, Kotlin, and C#. Choose the language that best fits your team and development workflow.

The shared runtime model does not mean every implementation has identical method signatures, helper names, generated types, or build commands. Start with the implementation-specific source, then apply the runtime rules described on this page.

### Development checklist

Before treating an application feature as complete, confirm that it has:

* A registered transaction type and matching protobuf message.
* Deterministic validation and delivery logic.
* Custom state prefixes declared outside the reserved `1` through `15` range.
* Tests that submit the transaction, wait for inclusion, and verify the resulting state.
* A narrow custom query endpoint where the application interface needs one.
* Clear handling for rejected, failed, and successful transactions.

Next, review the [Economics of a Chain Launch](https://canopy-network.gitbook.io/docs/app-builder/economics-of-a-chain-launch).
