SDK Reference: Solana Client
This page provides a reference for solana.Client, the Go SDK interface for writing data to Solana programs via the Keystone Forwarder. 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()
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 Write Capability: Architecture reference and account layout
- Generating Solana Bindings: How to generate typed Go structs from your Anchor IDL
- Writing to Solana guide: Step-by-step tutorial
- Supported Networks: Chain selector values for all supported networks