Solana is a high-performance blockchain platform gaining traction for its scalability and developer experience. This blog post dives into interacting with Solana using Go. We'll explore the basic functionalities like creating a wallet, importing existing one, fetching account balance, and transferring SOL tokens.
History
Solana, founded in 2020 by Anatoly Yakovenko, is a high-performance blockchain that has rapidly gained popularity within the blockchain community. Its architecture, designed to deliver exceptional speed provides scalability and low transaction costs. The core innovation lies in the unique combination of Proof of History and Proof of Stake, allowing the network to achieve consensus quickly and efficiently. The ability to handle thousands of transactions per second, coupled with its developer-friendly environment, makes it an attractive choice for those looking to build decentralized applications at scale.
On the other hand, the selection of a programming language for DApp development necessitates careful consideration of factors such as platform compatibility, developer community engagement, security features, performance, and interoperability. Various languages, including JavaScript, Rust, and Go, can be considered in this context.
Golang stands out as an optimal programming language for building decentralized applications (DApps) on the Solana blockchain. Go's strong support for concurrency aligns perfectly with Solana's design principles, allowing developers to take full advantage of the Solana’s fast transaction processing and scalability.
Prerequisites:
- Basic understanding of blockchain
- Basic understanding of Golang and a Go development environment
- Familiarity with Solana concepts
Choosing a Golang library for Solana:
As of now there is no official library to support interaction with Solana in Go, but there is popular community librairies such as :
-
gagliardetto/solana-go
(https://github.com/gagliardetto/solana-go) This library offers a user-friendly API for interacting with Solana's JSON RPC and WebSocket interfaces. -
blocto/solana-go-sdk
(https://github.com/blocto/solana-go-sdk) This is a comprehensive SDK for Solana development in Golang. It offers a wide range of functionalities.
In this article, we’ll use the most popular package : gagliardetto/solana-go
Creating a wallet
Let's start by creating a new Solana wallet. We'll use the solana.NewRandomPrivateKey
function to generate a new keypair:
package main
import (
"fmt"
"github.com/gagliardetto/solana-go"
)
func main() {
// Generate a new random keypair
privateKey, err := solana.NewRandomPrivateKey()
if err != nil {
panic(err)
}
// Get the public key from the private key
publicKey := privateKey.PublicKey()
// Print the public and private keys (**keep private key secure!**)
fmt.Println("Public Key:", publicKey)
fmt.Println("Private Key:", privateKey)
}
Running this code gives the following:
Importing an existing wallet
You might have to create a new wallet, but most of the time you will most likely import an existing one. To use an existing wallet, you'll need its private key in base58 format.
Here's an example using solana.PrivateKeyFromBase58
:
package main
import "github.com/gagliardetto/solana-go"
func main() {
// Replace with your actual private key in base58 format
privateKeyString := ".........."
// Parse the private key from base58
privateKey, err := solana.PrivateKeyFromBase58(privateKeyString)
if err != nil {
panic(err)
}
// Get the public key from the private key
publicKey := privateKey.PublicKey()
// Print the public and private keys
println("Public Key:", publicKey.String())
}
Running this code gives the following output:
Request airdrop and fetching wallet balance
For development and testing purposes, you might need test accounts with some SOL tokens to interact with the network. You could use an airdrop to get Solana token on the Devnet and Testnet. This can help you test your application's functionality.
It can be subject to rate limits when there is a high number of airdrops.
We can use the GetBalance
method to fetch the balance of an account (wallet):
package main
import (
"context"
"fmt"
"math/big"
"time"
"github.com/gagliardetto/solana-go"
"github.com/gagliardetto/solana-go/rpc"
)
func main() {
// Create a new account
account := solana.NewWallet()
fmt.Println("account private key:", account.PrivateKey)
fmt.Println("account public key:", account.PublicKey())
// Create a new RPC client on the DevNet
rpcClient := rpc.New(rpc.DevNet_RPC)
// Airdrop 1 SOL to the new account:
out, err := rpcClient.RequestAirdrop(
context.TODO(),
account.PublicKey(),
// 1 sol = 1000000000 lamports
solana.LAMPORTS_PER_SOL*1,
rpc.CommitmentFinalized,
)
if err != nil {
panic(err)
}
fmt.Println("airdrop transaction signature:", out)
time.Sleep(time.Second * 1)
// Get the balance of the account
balance, err := rpcClient.GetBalance(
context.TODO(),
account.PublicKey(),
rpc.CommitmentFinalized,
)
if err != nil {
panic(err)
}
var lamportsOnAccount = new(big.Float).SetUint64(uint64(balance.Value))
var solBalance = new(big.Float).Quo(lamportsOnAccount, new(big.Float).SetUint64(solana.LAMPORTS_PER_SOL))
fmt.Println("Wallet Balance:", solBalance, "SOL")
}
Running this code gives the following output:
Transfer Solana to another wallet
Transfers involve creating and sending a transaction. Here's a simple example with the wallet we requested airdrop on just before:
package main
import (
"context"
"fmt"
"os"
"github.com/gagliardetto/solana-go"
"github.com/gagliardetto/solana-go/programs/system"
"github.com/gagliardetto/solana-go/rpc"
confirm "github.com/gagliardetto/solana-go/rpc/sendAndConfirmTransaction"
"github.com/gagliardetto/solana-go/rpc/ws"
"github.com/gagliardetto/solana-go/text"
)
func main() {
// Create a new RPC client on the DevNet
rpcClient := rpc.New(rpc.DevNet_RPC)
// Create a new WS client (used for confirming transactions)
wsClient, err := ws.Connect(context.Background(), rpc.DevNet_WS)
if err != nil {
panic(err)
}
// Load the account that you will send funds FROM
accountFrom, err := solana.PrivateKeyFromBase58("..........")
if err != nil {
panic(err)
}
fmt.Println("accountFrom public key:", accountFrom.PublicKey())
// The public key of the account that you will send sol TO:
accountTo := solana.NewWallet()
fmt.Println("accountTo public key:", accountTo)
// Get the recent blockhash:
recent, err := rpcClient.GetRecentBlockhash(context.TODO(), rpc.CommitmentFinalized)
if err != nil {
panic(err)
}
tx, err := solana.NewTransaction(
[]solana.Instruction{
system.NewTransferInstruction(
// 1 sol = 1000000000 lamports
1e6, // 0.001 SOL,
accountFrom.PublicKey(),
accountTo.PublicKey(),
).Build(),
},
recent.Value.Blockhash,
solana.TransactionPayer(accountFrom.PublicKey()),
)
if err != nil {
panic(err)
}
_, err = tx.Sign(
func(key solana.PublicKey) *solana.PrivateKey {
if accountFrom.PublicKey().Equals(key) {
return &accountFrom
}
return nil
},
)
if err != nil {
panic(fmt.Errorf("unable to sign transaction: %w", err))
}
// Pretty print the transaction:
tx.EncodeTree(text.NewTreeEncoder(os.Stdout, "Transfer SOL"))
// Send transaction, and wait for confirmation:
sig, err := confirm.SendAndConfirmTransaction(
context.TODO(),
rpcClient,
wsClient,
tx,
)
if err != nil {
panic(err)
}
fmt.Println("Transaction confirmed:", sig)
}
Running this code gives the following output:
Conclusion
In this blog post, we've explored the basics of interacting with Solana using Golang.
This is just the first step on Solana development with Golang. The chosen library offers a wider range of functionalities beyond what we covered here. Remember, the Solana ecosystem is constantly evolving. Stay updated with the best practices to ensure your dApps remain robust and efficient.