Solomon Esenyi Python/Golang developer and Technical Writer with a passion for open-source, cryptography, and serverless technologies.

How to create a Solana wallet with Go

7 min read 2162

Have you ever wanted to explore blockchain development using Go? Or wanted to build Go applications powered by the Solana network? If you have, this article is for you.

Blockchain is the technology powering cryptocurrencies and decentralized applications. It is still in its early stages and is gaining massive adoption in several industries, leading to new job roles and opportunities. The average blockchain developer earns $154,550 annually, according to ZipRecruiter.

The simplest way to get started with blockchain development is by building a cryptocurrency wallet to store tokens and create transactions. There are several existing blockchains, and one of my favorites is Solana because of its rich ecosystem, speed, and developer friendliness.

In this article, we will learn how to interact with the Solana network using Go and build a crypto wallet to store, receive, and transfer coins from scratch.

Prerequisites

To follow and understand this tutorial, you will need the following:

What is Solana?

Solana ($SOL) is the fastest globally decentralized blockchain, emphasizing speed, scalability, and user-friendliness. It also has the fastest-growing ecosystem in the crypto space with over 400 projects, including Defi, NFTs, Web3, and many more.

Solana stands out from many other blockchains by offering the following:

  • Easy and efficient scalability
  • Meager transaction costs (always less than $0.01)
  • Fast network and block speeds (handling up to 50,000 transactions per second)
  • Censorship resistance (Solana applications will run freely forever)

A leading cybersecurity firm, Kudelski Security, has audited Solana’s software architecture, and the results are available here. You can also view the Solana network statistics on Solana Beach.

Getting started with Go and Solana

Install the solana-go-sdk package, which is a Go Software Development Kit (SDK) for Solana. It allows Go applications to interact with the Solana network, including using all the methods provided by the Solana JSON RPC API.

Step 1: Set up your development environment

Create a new Go project in your text editor or IDE and initialize your go.mod file. You are free to use any name for your package:

We made a custom demo for .
No really. Click here to check it out.

go mod init solana-wallet

go.mod file after initializing the package

Step 2: Install the Solana SDK for Go

Install the solana-go-sdk package in your project. In the terminal, type the following:

go get -u github.com/portto/solana-go-sdk

go.mod file after installing solana-go-sdk

Step 3: Connect to the Solana network

Import the Solana SDK package into your application, then create an RPC client instance connected to the Solana Mainnet network.

Create a file named main.go and save the following code in it:

package main

import (
        "context"
        "fmt"
        "github.com/portto/solana-go-sdk/client"
        "github.com/portto/solana-go-sdk/client/rpc"
)

func main() {
        // create a RPC client
        c := client.NewClient(rpc.MainnetRPCEndpoint)

        // get the current running Solana version
        response, err := c.GetVersion(context.TODO())
        if err != nil {
                panic(err)
        }

        fmt.Println("version", response.SolanaCore)
}

In this code, we imported the client and client/rpc modules from the Solana SDK into the application to create an RPC client and connect to the Solana network.

Then, we used the GetVersion() method to retrieve the current Solana versions running on the node by supplying a request context using context.TODO() for the server to accept.

Finally, we checked for errors in the operation using the err variable returned from GetVersion() and displayed the network version from the response variable. If the code runs without any errors, it means you have successfully connected to the Solana Mainnet network with Go.

If Go fails to run the main.go file due to "missing go.sum entry" errors, you need to run the go mod tidy command in your terminal to fix the missing module entries.

Interacting with the Solana network using Go

Creating a new Solana wallet

The Solana SDK provides a types.NewAccount() function that returns a newly generated Solana wallet. Let’s see how to use it:

package main

import (
        "fmt"
        "github.com/portto/solana-go-sdk/types"
)

func main() {
        // create a new wallet using types.NewAccount()
        wallet := types.NewAccount()

        // display the wallet public and private keys
        fmt.Println("Wallet Address:", wallet.PublicKey.ToBase58())
        fmt.Println("Private Key:", wallet.PrivateKey)
}

Here, we used the types.NewAccount() function in the Solana SDK that generates new wallets, then prints out its wallet address (public key in base58) and private key (byte slice).

Importing a Solana wallet

The Solana SDK provides three functions to import Solana wallets. Each of them returns a types.Account object representing a Solana wallet using its private key in different forms (base58, byte slice, and hex value):

// import a wallet with base58 private key
types.AccountFromBase58("")

// import a wallet with bytes slice private key
types.AccountFromBytes([]byte{})

// import a wallet with hex private key
types.AccountFromHex("")

Let’s see how to recover a Solana wallet from its private key:

package main

import (
        "fmt"
        "github.com/portto/solana-go-sdk/types"
)

func main() {
        // create a new wallet
        wallet := types.NewAccount()
        fmt.Println("Wallet Address:", wallet.PublicKey.ToBase58())

        // import the wallet using its private key
        importedWallet, err := types.AccountFromBytes(wallet.PrivateKey)

        // check for errors
        if err != nil {
                panic(err)
        }

        // display the imported wallet public and private keys
        fmt.Println("Imported Wallet Address:", importedWallet.PublicKey.ToBase58())
}

Here, we created a new wallet using the types.NewAcccount() function, then used the types.AccountFromBytes() function to import the wallet and compared their addresses. If they are equal, the wallet import is successful.

Fetching the balance of a Solana wallet

Because we are creating new wallets, their balances will always be zero. Solana provides a requestAirdrop() method in its JSON RPC API to request coins for development purposes. Let’s see how to use it:

package main

import (
        "context"
        "fmt"
        "github.com/portto/solana-go-sdk/client"
        "github.com/portto/solana-go-sdk/client/rpc"
        "github.com/portto/solana-go-sdk/types"
)

func main() {
        // create a RPC client
        c := client.NewClient(rpc.DevnetRPCEndpoint)

        // create a new wallet
        wallet := types.NewAccount()

        // request for 1 SOL airdrop using RequestAirdrop()
        txhash, err := c.RequestAirdrop(
                context.TODO(), // request context
                wallet.PublicKey.ToBase58(), // wallet address requesting airdrop
                1e9, // amount of SOL in lamport
        )

        // check for errors
        if err != nil {
                panic(err)
        }

        fmt.Println("Transaction Hash:", txhash)
}

We used the RequestAirdrop() function to request Devnet coins for a Solana wallet by providing a request context, wallet public key (in base58), and lamport amount.

The RequestAirdrop() function accepts the amount parameter in lamport, the smallest fractional unit of SOL, similar to satoshi for Bitcoin.

1 lamport ~ 0.000000001 SOL.

You can use the Solana Devnet Explorer to track the status of your transactions.

solana devnet explorer

Next, use the GetBalance() method provided by the Solana SDK to fetch the amount of SOL owned by the Solana wallet from its wallet address. Let’s see how to use it:

package main

import (
        "context"
        "fmt"
        "github.com/portto/solana-go-sdk/client"
        "github.com/portto/solana-go-sdk/client/rpc"
)

func main() {
        // create a RPC client
        c := client.NewClient(rpc.DevnetRPCEndpoint)

        // fetch the balance using GetBalance()
        balance, err := c.GetBalance(
                context.TODO(), // request context
                "8LdDAFdGuvZdhhnheUv9jVtiv9wQT3eTk2E46FodZP38", // wallet to fetch balance for
        )

        // check for errors
        if err != nil {
                panic(err)
        }

        fmt.Println("Wallet Balance in Lamport:", balance)
        fmt.Println("Wallet Balance in SOL:", balance/1e9)
}

Transferring Solana to another wallet

Before performing transfers on Solana, you must follow four steps to ensure your transaction is created on the network.

First, retrieve the most recent block hash from the network using the GetRecentBlockhash() function:

response, err := c.GetRecentBlockhash(context.TODO())

Then, make a transfer message with the retrieved block hash and public key of the transaction signer:

message := types.NewMessage(
        wallet.PublicKey, // public key of the transaction signer
        []types.Instruction{
                sysprog.Transfer(
                        wallet.PublicKey, // public key of the transaction sender
                        common.PublicKeyFromString(to), // wallet address of the transaction receiver
                        1e9, // transaction amount in lamport
                ),
        },
        response.Blockhash, // recent block hash
)

A transfer message contains the public keys of the transaction sender, receiver, and amount. It is also possible to have multiple transfers in a single transaction.

Create a transaction with the transfer message and transaction signer:

tx, err := types.NewTransaction(message, []types.Account{wallet, wallet})

The transaction signer is the account that pays the fee for that transaction. It is possible to let another account pay the transaction fee, but that account must also sign the transaction.

Next, send the transaction to the network, like so:

txhash, err := c.SendTransaction2(context.TODO(), tx)

After sending the transaction to the network, you will receive its hash that we can use to track the transaction. Here’s the complete transfer code:

package main

import (
        "context"
        "fmt"
        "github.com/portto/solana-go-sdk/client"
        "github.com/portto/solana-go-sdk/client/rpc"
        "github.com/portto/solana-go-sdk/common"
        "github.com/portto/solana-go-sdk/program/sysprog"
        "github.com/portto/solana-go-sdk/types"
)

func main() {
        // create a RPC client
        c := client.NewClient(rpc.DevnetRPCEndpoint)

        // import a wallet with Devnet balance
        wallet, _ := types.AccountFromBytes([]byte{})

        // fetch the most recent blockhash
        response, err := c.GetRecentBlockhash(context.TODO())
        if err != nil {
                panic(err)
        }

        // make a transfer message with the latest block hash
        message := types.NewMessage(
                wallet.PublicKey, // public key of the transaction signer
                []types.Instruction{
                        sysprog.Transfer(
                                wallet.PublicKey, // public key of the transaction sender
                                common.PublicKeyFromString("8t88TuqUxDMVpYGHcVoXnBCAH7TPrdZ7ydr4xqcNu2Ym"), // wallet address of the transaction receiver
                                1e9, // transaction amount in lamport
                        ),
                },
                response.Blockhash, // recent block hash
        )

        // create a transaction with the message and TX signer
        tx, err := types.NewTransaction(message, []types.Account{wallet, wallet})
        if err != nil {
                panic(err)
        }

        // send the transaction to the blockchain
        txhash, err := c.SendTransaction2(context.TODO(), tx)
        if err != nil {
                panic(err)
        }

        fmt.Println("Transaction Hash:", txhash)
}

Creating a Solana wallet with Go

Now that you have learned how to interact with the Solana network, let’s extend the code blocks we created earlier to build a fully functional cryptocurrency wallet using Go.

Let’s start by creating a custom type for the wallet. Save the following code in your main.go file:

package main

import (
        "context"
        "github.com/portto/solana-go-sdk/client"
        "github.com/portto/solana-go-sdk/common"
        "github.com/portto/solana-go-sdk/program/sysprog"
        "github.com/portto/solana-go-sdk/types"
)

type Wallet struct {
        account types.Account
        c       *client.Client
}

We need a function to generate new instances of the Wallet type so we can use it. Add the following code to the main.go file:

func CreateNewWallet(RPCEndpoint string) Wallet {
        return Wallet{
                types.NewAccount(),
                client.NewClient(RPCEndpoint),
        }
}

We can create new wallets with our application by adding a function that imports existing Solana wallets using their private key. Add the following code to the main.go file:

func ImportOldWallet(privateKey []byte, RPCEndpoint string) (Wallet, error) {
        wallet, err := types.AccountFromBytes(privateKey)
        if err != nil {
                return Wallet{}, err
        }

        return Wallet{
                wallet,
                client.NewClient(RPCEndpoint),
        }, nil
}

Now we have working code that creates and imports Solana wallet accounts. Let’s create some methods for the Wallet type.

We’ll begin by adding a function that requests SOL airdrops and retrieves the wallet balance. Add the following code to the main.go file:

func (w Wallet) RequestAirdrop(amount uint64) (string, error) {
        // request for SOL using RequestAirdrop()
        txhash, err := w.c.RequestAirdrop(
                context.TODO(),                 // request context
                w.account.PublicKey.ToBase58(), // wallet address requesting airdrop
                amount,                         // amount of SOL in lamport
        )
        if err != nil {
                return "", err
        }

        return txhash, nil
}

func (w Wallet) GetBalance() (uint64, error) {
        // fetch the balance using GetBalance()
        balance, err := w.c.GetBalance(
                context.TODO(),                 // request context
                w.account.PublicKey.ToBase58(), // wallet to fetch balance for
        )
        if err != nil {
                return 0, nil
        }

        return balance, nil
}

Next, we’ll create a function to transfer SOL from our wallet to other wallets on the Solana network. Add the following code to the main.go file:

func (w Wallet) Transfer(receiver string, amount uint64) (string, error) {
        // fetch the most recent blockhash
        response, err := w.c.GetRecentBlockhash(context.TODO())
        if err != nil {
                return "", err
        }

        // make a transfer message with the latest block hash
        message := types.NewMessage(
                w.account.PublicKey, // public key of the transaction signer
                []types.Instruction{
                        sysprog.Transfer(
                                w.account.PublicKey,                  // public key of the transaction sender
                                common.PublicKeyFromString(receiver), // wallet address of the transaction receiver
                                amount,                               // transaction amount in lamport
                        ),
                },
                response.Blockhash, // recent block hash
        )

        // create a transaction with the message and TX signer
        tx, err := types.NewTransaction(message, []types.Account{w.account, w.account})
        if err != nil {
                return "", err
        }

        // send the transaction to the blockchain
        txhash, err := w.c.SendTransaction2(context.TODO(), tx)
        if err != nil {
                return "", err
        }

        return txhash, nil
}

Now that we have created all the methods for the Wallet type. Let’s test it out:

// create a new wallet
wallet := CreateNewWallet(rpc.DevnetRPCEndpoint)

// request for an airdrop
fmt.Println(wallet.RequestAirdrop(1e9))

// make transfer to another wallet
fmt.Println(wallet.Transfer("8t88TuqUxDMVpYGHcVoXnBCAH7TPrdZ7ydr4xqcNu2Ym", 5e8))

// fetch wallet balance
fmt.Println(wallet.GetBalance())

Conclusion

By exploring the world of blockchain development using Solana and Go, you built a cryptocurrency wallet with minimal effort. We saw how to create and retrieve Solana wallets, store, receive, and transfer coins using the solana-go-sdk package.

The source code of the Solana wallet is available as a GitHub Gist. Also, you can visit the official documentation for Solana and the solana-go-sdk to explore and build amazing things with your newly acquired knowledge.

: Full visibility into your web apps

LogRocket is a frontend application monitoring solution that lets you replay problems as if they happened in your own browser. Instead of guessing why errors happen, or asking users for screenshots and log dumps, LogRocket lets you replay the session to quickly understand what went wrong. It works perfectly with any app, regardless of framework, and has plugins to log additional context from Redux, Vuex, and @ngrx/store.

In addition to logging Redux actions and state, LogRocket records console logs, JavaScript errors, stacktraces, network requests/responses with headers + bodies, browser metadata, and custom logs. It also instruments the DOM to record the HTML and CSS on the page, recreating pixel-perfect videos of even the most complex single-page apps.

.
Solomon Esenyi Python/Golang developer and Technical Writer with a passion for open-source, cryptography, and serverless technologies.

Leave a Reply