SDK Reference: Solana Client
This page provides a reference for solana.Client, the Go SDK interface for reading from and writing to Solana programs. It covers the client type, the bindings package helpers, and the chain selector reference for Solana networks.
Import paths
import (
solana "github.com/smartcontractkit/cre-sdk-go/capabilities/blockchain/solana"
"github.com/smartcontractkit/cre-sdk-go/capabilities/blockchain/solana/bindings"
)
Client instantiation
import solana "github.com/smartcontractkit/cre-sdk-go/capabilities/blockchain/solana"
// Solana Mainnet using the package constant
client := &solana.Client{ChainSelector: solana.SolanaMainnet}
// Solana Devnet
client := &solana.Client{ChainSelector: solana.SolanaDevnet}
// Or use ChainSelectorFromName
selector, err := solana.ChainSelectorFromName("solana-mainnet")
client := &solana.Client{ChainSelector: selector}
Chain selector constants
| Constant | Value |
|---|---|
solana.SolanaMainnet | 124615329519749607 |
solana.SolanaDevnet | 16423721717087811551 |
ChainSelectorFromName()
func ChainSelectorFromName(name string) (uint64, error)
Accepts "solana-mainnet" or "solana-devnet". Returns an error for unknown names.
Write methods
Client.WriteReport()
Submits a cryptographically signed report to a Solana program via the Keystone Forwarder. In most workflows, you call generated binding methods (WriteReportFrom<StructName>()) rather than this method directly.
Signature:
func (c *Client) WriteReport(runtime cre.Runtime, input *WriteCreReportRequest) cre.Promise[*WriteReportReply]
WriteCreReportRequest
| Field | Type | Description |
|---|---|---|
RemainingAccounts | []*AccountMeta | Ordered accounts required by the forwarder and receiver |
Receiver | []byte | 32-byte raw program ID of the receiver program |
ComputeConfig | *ComputeConfig | Optional compute budget configuration |
Report | *cre.Report | The signed report returned by runtime.Report() |
AccountMeta
| Field | Type | Description |
|---|---|---|
PublicKey | []byte | 32-byte raw public key |
IsWritable | bool | Whether the account is writable |
ComputeConfig
| Field | Type | Description |
|---|---|---|
ComputeUnitLimit | uint32 | Maximum compute units for the transaction (default: 290000) |
WriteReportReply
| Field | Type | Description |
|---|---|---|
TxStatus | TxStatus | TX_STATUS_SUCCESS, TX_STATUS_ABORTED, or TX_STATUS_FATAL |
TxSignature | []byte | Transaction signature bytes, accessed via GetTxSignature() (optional) |
ErrorMessage | string | Error message if the transaction failed (optional) |
Usage example
The low-level call, shown for reference. In most workflows, use a generated binding instead.
import (
"encoding/base58"
"time"
"github.com/smartcontractkit/cre-sdk-go/cre"
solana "github.com/smartcontractkit/cre-sdk-go/capabilities/blockchain/solana"
"github.com/smartcontractkit/cre-sdk-go/capabilities/blockchain/solana/bindings"
binary "github.com/gagliardetto/binary"
)
client := &solana.Client{ChainSelector: solana.SolanaMainnet}
forwarderStateBytes, _ := base58.Decode("ForwarderStateBase58...")
forwarderAuthBytes, _ := base58.Decode("ForwarderAuthBase58...")
reportStateBytes, _ := base58.Decode("ReportStateBase58...")
receiverBytes, _ := base58.Decode("ReceiverProgramBase58...")
accounts := []*solana.AccountMeta{
{PublicKey: forwarderStateBytes, IsWritable: false},
{PublicKey: forwarderAuthBytes, IsWritable: false},
{PublicKey: reportStateBytes, IsWritable: true},
}
// 1. Borsh-encode your payload (generated bindings do this automatically)
type PriceData struct {
Price uint64
Timestamp int64
}
var buf bytes.Buffer
enc := binary.NewBorshEncoder(&buf)
enc.Encode(PriceData{Price: 500000, Timestamp: time.Now().Unix()})
payload := buf.Bytes()
// 2. Compute the account hash and build the forwarder report
accountHash := bindings.CalculateAccountsHash(accounts)
forwarderReport := bindings.ForwarderReport{AccountHash: accountHash, Payload: payload}
encoded, err := forwarderReport.Marshal()
// 3. Generate a signed report
report := runtime.Report(cre.ReportRequest{
EncodedPayload: base64.StdEncoding.EncodeToString(encoded),
EncoderName: "solana",
SigningAlgo: "ecdsa",
HashingAlgo: "keccak256",
}).Result()
// 4. Submit the report
result := client.WriteReport(runtime, &solana.WriteCreReportRequest{
RemainingAccounts: accounts,
Receiver: receiverBytes,
Report: report,
}).Result()
Read methods
solana.Client exposes read methods for fetching consensus-verified data from Solana accounts and programs, served by a ContractReader on the Solana capability DON. There is no generated per-account "fetch" helper — you call these directly, then decode the result with the generated Codec.Decode<AccountName>() for your account type.
Client.GetAccountInfoWithOpts()
Fetches a single account's info, including its raw data.
Signature:
func (c *Client) GetAccountInfoWithOpts(runtime cre.Runtime, input *GetAccountInfoWithOptsRequest) cre.Promise[*GetAccountInfoWithOptsReply]
GetAccountInfoWithOptsRequest
| Field | Type | Description |
|---|---|---|
Account | []byte | 32-byte account public key. |
Opts | *GetAccountInfoOpts | Encoding, commitment, and slicing options. |
GetAccountInfoOpts
| Field | Type | Description |
|---|---|---|
Encoding | solana.EncodingType | ENCODING_TYPE_BASE58 (accounts under 129 bytes only), ENCODING_TYPE_BASE64 (any size), ENCODING_TYPE_BASE64_ZSTD, ENCODING_TYPE_JSON_PARSED, or ENCODING_TYPE_JSON. |
Commitment | solana.CommitmentType | COMMITMENT_TYPE_FINALIZED, COMMITMENT_TYPE_CONFIRMED, or COMMITMENT_TYPE_PROCESSED. |
DataSlice | *solana.DataSlice | Optional {Offset, Length} window to fetch only part of the account data. |
MinContextSlot | uint64 | Optional lower-bound slot for the read. |
GetAccountInfoWithOptsReply
| Field | Type | Description |
|---|---|---|
Value | *solana.Account | The account, or nil if it does not exist. |
Account
| Field | Type | Description |
|---|---|---|
Lamports | uint64 | Balance in lamports. |
Owner | []byte | 32-byte program ID that owns the account. |
Data | *solana.DataBytesOrJSON | Account data. Use GetRaw() for the encoded bytes (matches your requested Encoding) or GetJson() when using ENCODING_TYPE_JSON_PARSED/ENCODING_TYPE_JSON. |
Executable | bool | true if this is a program account. |
RentEpoch | *pb.BigInt | Next rent epoch. |
Space | uint64 | Data length in bytes. |
Usage: see the Onchain Read guide for a full,
step-by-step example, including decoding the returned bytes with a generated Codec.Decode<AccountName>().
Other read methods
solana.Client also exposes these lower-level read methods, following the same runtime, *XRequest) cre.Promise[*XReply] shape:
| Method | Description |
|---|---|
GetBalance() | Lamport balance of an account. |
GetBlock() | A block's contents at a given slot. |
GetMultipleAccountsWithOpts() | GetAccountInfoWithOpts(), batched for multiple accounts. |
GetProgramAccounts() | All accounts owned by a program, with optional filters. |
GetSignatureStatuses() | Confirmation status for one or more transaction signatures. |
GetSlotHeight() | The current slot height. |
GetTransaction() | A confirmed transaction's details, given its signature. |
GetFeeForMessage() | The fee a compiled message would cost. |
bindings package
The bindings package (github.com/smartcontractkit/cre-sdk-go/capabilities/blockchain/solana/bindings) provides the serialization primitives used by generated binding code. You can also use them directly when writing low-level Solana write logic.
ForwarderReport
Represents the Borsh-serialized report format expected by the Keystone Forwarder program.
type ForwarderReport struct {
AccountHash [32]byte
Payload []byte
}
ForwarderReport.Marshal()
Serializes the report into Borsh bytes: [32-byte accountHash][u32-LE payload length][payload].
func (obj ForwarderReport) Marshal() ([]byte, error)
Usage:
report := bindings.ForwarderReport{
AccountHash: bindings.CalculateAccountsHash(accounts),
Payload: borshEncodedPayload,
}
encoded, err := report.Marshal()
CalculateAccountsHash()
Computes the SHA-256 hash of the concatenated 32-byte public keys of the given accounts. This hash is embedded in the report payload and verified on-chain by the Keystone Forwarder.
func CalculateAccountsHash(accs []*solana.AccountMeta) [32]byte
nil entries in the slice are skipped. Account order matters: swapping two accounts produces a different hash.
Usage:
accounts := []*solana.AccountMeta{
{PublicKey: forwarderStateBytes},
{PublicKey: forwarderAuthBytes},
{PublicKey: reportStateBytes},
}
hash := bindings.CalculateAccountsHash(accounts)
// → [32]byte
Chain selectors
| Network | String Name | Numeric ID |
|---|---|---|
| Solana Mainnet | solana-mainnet | 124615329519749607 |
| Solana Devnet | solana-devnet | 16423721717087811551 |
In your workflow config (e.g., config.staging.json), store the numeric ID as a string to avoid precision loss, and use the json:",string" struct tag:
{
"chainSelector": "124615329519749607"
}
type Config struct {
ChainSelector uint64 `json:"chainSelector,string"`
}
Related resources
- Solana Read & Write Capabilities: Architecture reference and account layout
- Generating Solana Bindings: How to generate typed Go structs from your Anchor IDL
- Reading from Solana guide: Step-by-step tutorial
- Writing to Solana guide: Step-by-step tutorial
- Solana Log Trigger reference: Filter configuration and payload fields
- Supported Networks: Chain selector values for all supported networks