Generating Solana Bindings
To interact with a Solana program from your Go workflow, you generate bindings from the program's Anchor IDL. Bindings are typed Go structs and methods 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. Your workflow calls this method with the struct data and the required accounts; the generated code handles everything else.
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:
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:
# In your Anchor project
anchor build
# IDL is written to: target/idl/<program-name>.json
Copy it into your CRE project:
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:
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 DataStorage IDL
The following IDL is enough to generate a WriteReportFromUserData() helper. Save it as contracts/solana/src/idl/data_storage.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 data_storage package in your workflow:
import (
solanago "github.com/gagliardetto/solana-go"
solana "github.com/smartcontractkit/cre-sdk-go/capabilities/blockchain/solana"
"<project-name>/contracts/solana/src/generated/data_storage"
)
// In your workflow handler...
client := &solana.Client{ChainSelector: config.ChainSelector}
ds, err := data_storage.NewDataStorage(client)
if err != nil {
return "", err
}
// remainingAccounts: Index 0 forwarderState, 1 forwarderAuthority, 2+ receiver accounts
accounts := []*solana.AccountMeta{
{PublicKey: forwarderStatePk[:], IsWritable: false},
{PublicKey: forwarderAuthPk[:], IsWritable: false},
{PublicKey: reportStatePk[:], IsWritable: true},
}
// Pass the struct data directly — types are derived from the IDL
result, err := ds.WriteReportFromUserData(runtime, data_storage.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.
What the CLI generates
The generator creates a Go package for each IDL file in contracts/solana/src/generated/<program_name>/.
What's inside depends on your IDL:
- For all programs:
- A
<ProgramName>struct andNew<ProgramName>(client)constructor (program ID from the IDL). - Codec helpers for encoding structs.
- A
- For onchain writes (each struct in
types):- A
WriteReportFrom<StructName>(runtime, input, remainingAccounts, computeConfig)method that returns acre.Promise[*solana.WriteReportReply]you.Await(). - A
WriteReportFrom<StructName>s(...)variant for BorshVecpayloads.
- A
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 |
Best practices
- Regenerate when needed: Re-run
cre generate-bindings solanawhenever your Anchor IDL changes. Do not edit generated files by hand. - Always pass
ComputeConfig: Solana simulation rejects a nil or zeroComputeLimit. Pass&solana.ComputeConfig{ComputeLimit: 200_000}(or another positive limit within the simulator's CU cap). - Handle errors: Always check errors from constructors and
.Await(). - Store addresses in config: Put forwarder and account addresses in
config.staging.json/config.production.json. chainSelectoras string in config: Usejson:"chainSelector,string"on your config struct to avoid precision loss when unmarshallinguint64values from JSON.
Where to go next
- Writing to Solana — Full write walkthrough (deploy, broadcast, production forwarder addresses)
- Solana Client SDK Reference — Full API reference for
solana.Clientand helper functions