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

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

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

The Solana CRE capability is write-only. For each Anchor struct in your IDL, the generator creates a `writeReportFrom<StructName>()` method on the binding class. Your workflow calls this method with the struct data and the required accounts; the generated code handles everything else.

> **NOTE: CLI version requirement**
>
> TypeScript Solana binding generation and Solana write simulation require **CRE CLI v1.24.0 or later**. Run `cre
>   version` to check, then `cre update` if needed.

## The generation process

The CRE CLI reads your IDL files and generates a typed class with the write helpers your workflow needs.

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

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

### Step 1: Add your Anchor IDL

Place your program's Anchor IDL file in `my-workflow/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 or symlink it into your CRE project:

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

### Step 2: Generate the bindings

From your **workflow directory**, run:

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

This reads all `.json` IDL files in `contracts/solana/src/idl/` and generates TypeScript files in `contracts/solana/ts/generated/` (relative to your workflow directory). For each IDL, three files are generated:

- `<ProgramName>.ts` — The typed binding class with `writeReportFrom<StructName>()` methods
- `<ProgramName>_mock.ts` — A mock for testing without a live network
- `index.ts` — A barrel re-exporting everything

## 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.

**Example: A minimal `DataStorage` IDL**

The following IDL is enough to generate a `writeReportFromUserData()` helper. Save it as `my-workflow/contracts/solana/src/idl/data_storage.json`.

```json
{
  "address": "64Q1Xu7AEbc2xgaN21zZU1y1mkaLJNnP5cHFkthGuW31",
  "metadata": {
    "name": "data_storage",
    "version": "0.1.0",
    "spec": "0.1.0",
    "description": "Minimal CRE Solana write example IDL"
  },
  "instructions": [
    {
      "name": "on_report",
      "discriminator": [214, 173, 18, 221, 173, 148, 151, 208],
      "accounts": [],
      "args": [
        { "name": "_metadata", "type": "bytes" },
        { "name": "payload", "type": "bytes" }
      ]
    }
  ],
  "accounts": [],
  "events": [],
  "errors": [],
  "types": [
    {
      "name": "UserData",
      "type": {
        "kind": "struct",
        "fields": [
          { "name": "key", "type": "string" },
          { "name": "value", "type": "string" }
        ]
      }
    }
  ]
}
```

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

```typescript
import { SolanaClient, solanaAccountMeta, type Runtime } from "@chainlink/cre-sdk"
import { DataStorage } from "./contracts/solana/ts/generated/DataStorage"

// In your workflow handler...
const client = new SolanaClient(BigInt(config.chainSelector))
const ds = new DataStorage(client)

// remainingAccounts: Index 0 forwarderState, 1 forwarderAuthority, 2+ receiver accounts
const remainingAccounts = [
  solanaAccountMeta(config.forwarderState, false),
  solanaAccountMeta(config.forwarderAuthority, false),
  solanaAccountMeta(config.reportState, true),
]

// Pass the struct data directly — types are derived from the IDL
const result = ds.writeReportFromUserData(runtime, { key: "price", value: "500000" }, remainingAccounts, {
  computeLimit: 200_000,
})
```

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

> **NOTE: What does writeReportFrom do?**
>
> The generated method Borsh-encodes the struct with `@solana/codecs`, hashes `remainingAccounts` via
> `calculateAccountsHash`, encodes a forwarder report and calls `runtime.report()` with `prepareSolanaReportRequest`,
> calls `SolanaClient.writeReport()` to submit it, and returns the transaction result synchronously (after consensus).

## What the CLI generates

For each IDL file, the generator creates three files in `contracts/solana/ts/generated/` (inside your workflow directory):

- `<ProgramName>.ts` — The main binding class
- `<ProgramName>_mock.ts` — A mock implementation for testing
- `index.ts` — A barrel re-exporting everything

What's inside depends on your IDL:

- **For all programs**:
  - A typed `<PROGRAM>_IDL` constant and `<PROGRAM>_PROGRAM_ID` from the IDL address.
  - A `<ProgramName>` class with a `writeReport(runtime, payload, remainingAccounts, computeConfig?)` base method.
- **For onchain writes** (each struct in `types`):
  - A `writeReportFrom<StructName>(runtime, input, remainingAccounts, computeConfig?)` method.
  - A `writeReportFrom<StructName>s(...)` variant for Borsh `Vec` payloads.

You can import from the barrel file to keep imports clean:

```typescript
import { DataStorage } from "./contracts/solana/ts/generated"
```

## `remainingAccounts` layout

Every Solana write requires a `remainingAccounts` array. The Keystone Forwarder (and the Devnet mock forwarder used in simulation) expects:

| Index | Account              | Writable                | How to obtain                                                                                      |
| ----- | -------------------- | ----------------------- | -------------------------------------------------------------------------------------------------- |
| 0     | `forwarderState`     | No                      | Provided by Chainlink for the forwarder deployment (mock values are built into the CLI for Devnet) |
| 1     | `forwarderAuthority` | No                      | 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                                           |

The `forwarderAuthority` PDA derivation in TypeScript using `@solana/addresses`:

```typescript
import { getProgramDerivedAddress, address, getAddressEncoder } from "@solana/addresses"

const enc = getAddressEncoder()
const [forwarderAuthority] = await getProgramDerivedAddress({
  programAddress: address(forwarderProgramId),
  seeds: [Buffer.from("forwarder"), enc.encode(address(forwarderState)), enc.encode(address(receiverProgramId))],
})
```

> **TIP: PDA derivation**
>
> Derive the `forwarderAuthority` PDA once and store it in config. The seeds are: `["forwarder", forwarderState bytes,
>   receiverProgram bytes]` 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 missing or zero `computeLimit`. Pass `{ computeLimit: 200_000 }` (or another positive limit within the simulator's CU cap).
3. **Store addresses in config**: Put forwarder and account addresses in `config.staging.json` / `config.production.json` rather than hard-coding them.
4. **`chainSelector` as string in config**: Store the chain selector as a stringified number in JSON to avoid precision loss, then convert with `BigInt()` in workflow code.
5. **Use explicit `--language` in CI**: If your project has both `go.mod` and `package.json`, pass `--language typescript` explicitly.

## Where to go next

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