Go

Use full service RPC for fetching latest blockhash.

package main

import (
	"bytes"
	"context"
	"encoding/base64"
	"fmt"
	"io"
	"net/http"

	"github.com/gagliardetto/solana-go"
	"github.com/gagliardetto/solana-go/programs/system"
	"github.com/gagliardetto/solana-go/rpc"
)

const (
	NOZOMI_ENDPOINT     = "https://nozomi.temporal.xyz/api/sendTransaction2?c=<YOUR_API_KEY>"
	NOZOMI_TIP          = "TEMPaMeCRFAS9EKF53Jd6KpHxgL47uWLcpFArU1Fanq"
	MIN_TIP_AMOUNT      = 1_000_000
	SOLANA_RPC_ENDPOINT = "https://api.mainnet-beta.solana.com"
)

func sendNozomiTx(
	ctx context.Context,
	ixns []solana.Instruction,
	signer solana.PrivateKey,
	nozomiEndpoint string,
	solanaRpcClient *rpc.Client,
) error {
	nozomiTipPubkey := solana.MustPublicKeyFromBase58(NOZOMI_TIP)
	tipIxn := system.NewTransferInstruction(
		MIN_TIP_AMOUNT,
		signer.PublicKey(),
		nozomiTipPubkey,
	).Build()
	ixns = append(ixns, tipIxn)

	recentBlockhash, err := solanaRpcClient.GetLatestBlockhash(ctx, rpc.CommitmentFinalized)
	if err != nil {
		return fmt.Errorf("failed to get latest blockhash: %w", err)
	}

	tx, err := solana.NewTransaction(
		ixns,
		recentBlockhash.Value.Blockhash,
		solana.TransactionPayer(signer.PublicKey()),
	)
	if err != nil {
		return fmt.Errorf("failed to create transaction: %w", err)
	}

	_, err = tx.Sign(func(key solana.PublicKey) *solana.PrivateKey {
		if key.Equals(signer.PublicKey()) {
			return &signer
		}
		return nil
	})
	if err != nil {
		return fmt.Errorf("failed to sign transaction: %w", err)
	}

	txBytes, err := tx.MarshalBinary()
	if err != nil {
		return fmt.Errorf("failed to marshal transaction: %w", err)
	}

	txBase64 := base64.StdEncoding.EncodeToString(txBytes)

	req, err := http.NewRequestWithContext(ctx, http.MethodPost, nozomiEndpoint, bytes.NewBufferString(txBase64))
	if err != nil {
		return fmt.Errorf("failed to create request: %w", err)
	}

	req.Header.Set("Content-Type", "text/plain")

	client := &http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		return fmt.Errorf("failed to send transaction: %w", err)
	}
	defer resp.Body.Close()

	// api v2 does not return a signature, just check for success
	if resp.StatusCode < 200 || resp.StatusCode >= 300 {
		body, _ := io.ReadAll(resp.Body)
		return fmt.Errorf("transaction failed with status %d: %s", resp.StatusCode, string(body))
	}

	fmt.Println("Transaction sent successfully")
	return nil
}

func buildIxns() []solana.Instruction {
	// your instruction building logic here..
	return []solana.Instruction{}
}

func main() {
	ctx := context.Background()

	solanaRpcClient := rpc.New(SOLANA_RPC_ENDPOINT)

	// replace with actual keypair loading logic
	signer := solana.NewWallet()

	ixns := buildIxns()

	err := sendNozomiTx(ctx, ixns, signer.PrivateKey, NOZOMI_ENDPOINT, solanaRpcClient)
	if err != nil {
		panic(err)
	}
}

Last updated