# Generating Solana Bindings
Source: https://docs.chain.link/cre/guides/workflow/using-solana-client/generating-bindings-go
Last Updated: 2026-10-19

> For the complete documentation index, see [llms.txt](/llms.txt).

To interact with a Solana program from your Go workflow, you generate **bindings** from the program's [Anchor IDL](https://www.anchor-lang.com/docs/references/idl). Bindings are typed Go structs and methods that handle Borsh encoding, account hashing, and report submission automatically.

For each Anchor struct in your IDL, the generator creates a `WriteReportFrom<StructName>()` method for onchain writes. For each event, it creates a `LogTrigger<EventName>Log()` method you can register as a [Solana Log
trigger](/cre/guides/workflow/using-triggers/solana-log-trigger-go). Your workflow calls these methods with the struct data (or filters) and the required accounts; the generated code handles everything else.

> **NOTE: CLI version requirements**
>
> Solana binding generation, read, write, and log trigger support require **CRE CLI v1.33.0 or later**. Run `cre
>   version` to check, then `cre update` if needed.

> **NOTE: TypeScript support**
>
> `cre generate-bindings solana` also supports TypeScript. See [Generating Solana Bindings
> (TypeScript)](/cre/guides/workflow/using-solana-client/generating-bindings-ts) for details.

## The generation process

The CRE CLI provides an automated binding generator that reads Anchor IDLs and creates corresponding Go packages.

The target language is **auto-detected** from your project files (presence of `go.mod` picks Go). You can also force Go explicitly with the `--language` flag:

```bash
cre generate-bindings solana --language go
```

Your project must already be a Go module (`go.mod` at the project root, as created by `cre init`). Without `go.mod`, the generator writes files but then fails when resolving Go dependencies.

### Step 1: Add your Anchor IDL

Place your program's Anchor IDL file in `contracts/solana/src/idl/`. The IDL is a JSON file that Anchor generates when you build your program:

```bash
# In your Anchor project
anchor build
# IDL is written to: target/idl/<program-name>.json
```

Copy it into your CRE project:

```bash
mkdir -p contracts/solana/src/idl
cp /path/to/your-anchor-project/target/idl/my_program.json contracts/solana/src/idl/
```

### Step 2: Generate the bindings

From your **project root**, run:

```bash
cre generate-bindings solana
```

This reads all `.json` IDL files in `contracts/solana/src/idl/` and generates Go packages in `contracts/solana/src/generated/`. For each IDL, the generator creates a package directory (for example `data_storage/`) with the binding sources, including:

- Struct types and `WriteReportFrom<StructName>()` methods
- A mock for testing without a live network

After generation, run `go mod tidy` if you need to sync new dependencies.

## Using generated bindings

### For onchain writes

For each struct in your IDL's `types` section, the generator creates a `WriteReportFrom<StructName>()` method that Borsh-encodes your data, builds the forwarder report, and submits it in one step. These methods return a `Promise`, which you must `.Await()` to get the result after consensus.

**Example: A minimal `cre_docs_receiver` IDL**

The following IDL is enough to generate a `WriteReportFromUserData()` helper. Save it as `contracts/solana/src/idl/cre_docs_receiver.json`.

```json
{
  "address": "7k8NypziCPqVY8GYyCw7aqaR5aamFAMdH5ZaHVrZCS94",
  "metadata": {
    "name": "cre_docs_receiver",
    "version": "0.1.0",
    "spec": "0.1.0",
    "description": "Receiver for cre-cli Solana forwarder simulation that logs a decoded UserData payload"
  },
  "instructions": [
    {
      "name": "on_report",
      "discriminator": [214, 173, 18, 221, 173, 148, 151, 208],
      "accounts": [
        { "name": "state" },
        {
          "name": "forwarder_authority",
          "docs": ["PDA signer supplied by the forwarder CPI."],
          "signer": true
        }
      ],
      "args": [
        { "name": "metadata", "type": "bytes" },
        { "name": "report", "type": "bytes" }
      ]
    }
  ],
  "events": [
    {
      "name": "ReceivedUserData",
      "discriminator": [154, 5, 186, 144, 208, 168, 118, 242]
    }
  ],
  "errors": [
    {
      "code": 6000,
      "name": "InvalidForwarderAuthority",
      "msg": "forwarder_authority is not the PDA for this state, receiver, and forwarder program"
    },
    {
      "code": 6001,
      "name": "InvalidUserData",
      "msg": "report payload could not be decoded as UserData"
    }
  ],
  "types": [
    {
      "name": "ReceivedUserData",
      "type": {
        "kind": "struct",
        "fields": [
          { "name": "forwarder", "type": "pubkey" },
          { "name": "forwarder_state", "type": "pubkey" },
          { "name": "forwarder_authority", "type": "pubkey" },
          { "name": "metadata_len", "type": "u32" },
          { "name": "report_len", "type": "u32" },
          { "name": "user", "type": { "defined": { "name": "UserData" } } }
        ]
      }
    },
    {
      "name": "UserData",
      "type": {
        "kind": "struct",
        "fields": [
          { "name": "key", "type": "string" },
          { "name": "value", "type": "string" }
        ]
      }
    }
  ]
}
```

After running `cre generate-bindings solana`, use the generated `cre_docs_receiver` package in your workflow:

```go
import (
	solanago "github.com/gagliardetto/solana-go"
	solana "github.com/smartcontractkit/cre-sdk-go/capabilities/blockchain/solana"

	"<project-name>/contracts/solana/src/generated/cre_docs_receiver"
)

// In your workflow handler...
client := &solana.Client{ChainSelector: config.ChainSelector}
ds, err := cre_docs_receiver.NewCreDocsReceiver(client)
if err != nil {
	return "", err
}

// remainingAccounts: Index 0 forwarderState, 1 forwarderAuthority, 2+ receiver accounts.
// cre_docs_receiver needs no receiver-specific accounts, so we pass just [state, authority].
accounts := []*solana.AccountMeta{
	{PublicKey: forwarderStatePk[:], IsWritable: false},
	{PublicKey: forwarderAuthPk[:], IsWritable: false},
}

// Pass the struct data directly — types are derived from the IDL
result, err := ds.WriteReportFromUserData(runtime, cre_docs_receiver.UserData{
	Key:   "price",
	Value: "500000",
}, accounts, &solana.ComputeConfig{ComputeLimit: 200_000}).Await()
```

For a full walkthrough including config, simulation, and production deployment, see [Writing to Solana](/cre/guides/workflow/using-solana-client/onchain-write-go).

> **NOTE: What does the WriteReport helper do?**
>
> The `WriteReportFromUserData` method Borsh-encodes the `UserData` struct, hashes `remainingAccounts` via
> `CalculateAccountsHash`, encodes a forwarder report and calls `runtime.GenerateReport()` with the `solana` encoder,
> calls the Solana client's `WriteReport` method to submit it, and returns a `Promise` that resolves with the
> transaction details once confirmed.

## What the CLI generates

The generator creates a Go package for each IDL file in `contracts/solana/src/generated/<program_name>/` (for example, `cre_docs_receiver/`).

What's inside depends on your IDL:

- **For all programs**:
  - A `<ProgramName>` struct and `New<ProgramName>(client)` constructor (program ID from the IDL).
  - Codec helpers for encoding structs.
- **For onchain reads** (each account in `accounts`):
  - A `Codec.Decode<AccountName>(data []byte) (*<AccountName>, error)` method that decodes raw account bytes returned by a read.
- **For onchain writes** (each struct in `types`):
  - A `WriteReportFrom<StructName>(runtime, input, remainingAccounts, computeConfig)` method that returns a `cre.Promise[*solana.WriteReportReply]` you `.Await()`.
  - A `WriteReportFrom<StructName>s(...)` variant for Borsh `Vec` payloads.
- **For log triggers** (each event in `events`):
  - A `LogTrigger<EventName>Log(chainSelector, filterName, filters, opts)` method that returns a `cre.Trigger` decoding to that event's data.
  - An `<EventName>Filters` struct for filtering on the event's top-level scalar fields. See [Solana Log
    Trigger](/cre/guides/workflow/using-triggers/solana-log-trigger-go).

## `accounts` layout

Every Solana write requires an `[]*solana.AccountMeta` slice. The Keystone Forwarder (and the Devnet mock forwarder used in simulation) expects:

| Index | Account              | `IsWritable`            | How to obtain                                                                                      |
| ----- | -------------------- | ----------------------- | -------------------------------------------------------------------------------------------------- |
| 0     | `forwarderState`     | `false`                 | Provided by Chainlink for the forwarder deployment (mock values are built into the CLI for Devnet) |
| 1     | `forwarderAuthority` | `false`                 | PDA derived from `["forwarder", forwarderState, receiverProgram]` under the forwarder program ID   |
| 2+    | Receiver accounts    | Depends on your program | Defined by your Anchor program's `on_report` instruction                                           |

| Field        | Type     | Description                                  |
| ------------ | -------- | -------------------------------------------- |
| `PublicKey`  | `[]byte` | 32-byte raw public key (decoded from base58) |
| `IsWritable` | `bool`   | Whether the account is writable              |

> **TIP: forwarderAuthority derivation**
>
> Use `github.com/gagliardetto/solana-go`'s `FindProgramAddress` to derive the `forwarderAuthority` PDA and store it in
> config. The seeds are: `[][]byte{[]byte("forwarder"), forwarderStateBytes, receiverProgramBytes}` under the forwarder
> program ID.

## Best practices

1. **Regenerate when needed**: Re-run `cre generate-bindings solana` whenever your Anchor IDL changes. Do not edit generated files by hand.
2. **Always pass `ComputeConfig`**: Solana simulation rejects a nil or zero `ComputeLimit`. Pass `&solana.ComputeConfig{ComputeLimit: 200_000}` (or another positive limit within the simulator's CU cap).
3. **Handle errors**: Always check errors from constructors and `.Await()`.
4. **Store addresses in config**: Put forwarder and account addresses in `config.staging.json` / `config.production.json`.
5. **`chainSelector` as string in config**: Use `json:"chainSelector,string"` on your config struct to avoid precision loss when unmarshalling `uint64` values from JSON.

## Where to go next

- [Writing to Solana](/cre/guides/workflow/using-solana-client/onchain-write-go) — Full write walkthrough (deploy, broadcast, production forwarder addresses)
- [Solana Client SDK Reference](/cre/reference/sdk/solana-client-go) — Full API reference for `solana.Client` and helper functions