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

ConstantValue
solana.SolanaMainnet124615329519749607
solana.SolanaDevnet16423721717087811551

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

FieldTypeDescription
RemainingAccounts[]*AccountMetaOrdered accounts required by the forwarder and receiver
Receiver[]byte32-byte raw program ID of the receiver program
ComputeConfig*ComputeConfigOptional compute budget configuration
Report*cre.ReportThe signed report returned by runtime.Report()

AccountMeta

FieldTypeDescription
PublicKey[]byte32-byte raw public key
IsWritableboolWhether the account is writable

ComputeConfig

FieldTypeDescription
ComputeUnitLimituint32Maximum compute units for the transaction (default: 290000)

WriteReportReply

FieldTypeDescription
TxStatusTxStatusTX_STATUS_SUCCESS, TX_STATUS_ABORTED, or TX_STATUS_FATAL
TxSignature[]byteTransaction signature bytes, accessed via GetTxSignature() (optional)
ErrorMessagestringError 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

NetworkString NameNumeric ID
Solana Mainnetsolana-mainnet124615329519749607
Solana Devnetsolana-devnet16423721717087811551

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"`
}

Get the latest Chainlink content straight to your inbox.