如何用Go创建一个Solana钱包

2,324 阅读8分钟

你是否曾想过使用Go探索区块链开发?或者想建立由Solana网络驱动的Go应用程序?如果你有,这篇文章就是为你准备的。

区块链是支持加密货币和去中心化应用的技术。它仍处于早期阶段,在一些行业中获得了大规模的采用,导致了新的工作角色和机会。根据ZipRecruiter的数据,区块链开发人员的平均年收入为154,550美元。

开始进行区块链开发的最简单方法是建立一个加密货币钱包,以存储代币和创建交易。现有几个区块链,我最喜欢的一个是Solana,因为它有丰富的生态系统,速度快,而且对开发者友好。

在这篇文章中,我们将学习如何使用Go与Solana网络互动,并建立一个加密货币钱包,从头开始存储、接收和转移硬币。

前提条件

要遵循和理解本教程,你将需要以下条件。

什么是Solana?

Solana($SOL)是全球最快的去中心化区块链,强调速度、可扩展性和用户友好性。它还拥有加密货币领域发展最快的生态系统,有400多个项目,包括Defi、NFTs、Web3等等

Solana通过提供以下内容从许多其他区块链中脱颖而出。

  • 轻松而高效的可扩展性
  • 微薄的交易成本(始终低于0.01美元)
  • 快速的网络和区块速度(每秒处理高达50,000个交易)
  • 抗审查(Solana应用程序将永远自由运行)

一家领先的网络安全公司Kudelski Security已经对Solana的软件架构进行了审计,结果可在此查阅。你也可以在Solana Beach上查看Solana网络的统计数据。

开始使用Go和Solana

安装solana-go-sdk包,这是一个用于Solana的Go软件开发工具包(SDK)。它允许Go应用程序与Solana网络互动,包括使用SolanaJSON RPC API提供的所有方法。

第一步:设置你的开发环境

在你的文本编辑器或IDE中创建一个新的Go项目,并初始化你的go.mod 文件。你可以自由地使用你的软件包的任何名称。

go mod init solana-wallet

go.mod file after initializing the package

第2步:安装Solana SDK for Go

在你的项目中安装solana-go-sdk包。在终端中,输入以下内容。

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

go.mod file after installing solana-go-sdk

第3步:连接到Solana网络

将Solana SDK包导入你的应用程序中,然后创建一个连接到Solana Mainnet网络的RPC客户端实例。

创建一个名为main.go 的文件,并在其中保存以下代码。

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)
}

在这段代码中,我们将Solana SDK中的clientclient/rpc 模块导入到应用程序中,以创建一个RPC客户端并连接到Solana网络。

然后,我们使用GetVersion() 方法,通过使用context.TODO() 提供一个请求上下文让服务器接受,来检索当前节点上运行的Solana版本。

最后,我们使用从GetVersion() 返回的err 变量检查操作中的错误,并从response 变量显示网络版本。如果代码运行没有任何错误,说明你已经成功地用Go连接到Solana Mainnet网络。

如果Go由于"missing go.sum entry" 错误而无法运行main.go 文件,你需要在终端运行go mod tidy 命令来修复丢失的模块条目。

使用Go与Solana网络进行交互

创建一个新的Solana钱包

Solana SDK提供了一个types.NewAccount() 函数,可以返回一个新生成的Solana钱包。让我们看看如何使用它。

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)
}

在这里,我们使用了Solana SDK中的types.NewAccount() 函数,它可以生成新的钱包,然后打印出它的钱包地址(base58的公钥)和私钥(字节片)。

导入一个Solana钱包

Solana SDK提供了三个函数来导入Solana钱包。每个函数都返回一个types.Account 对象,代表一个使用不同形式的私钥的Solana钱包(base58、字节片和十六进制值)。

// 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("")

让我们看看如何通过其私钥恢复一个Solana钱包。

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())
}

在这里,我们使用types.NewAcccount() 函数创建了一个新的钱包,然后使用types.AccountFromBytes() 函数导入钱包并比较它们的地址。如果它们相等,钱包导入就成功了。

取出Solana钱包的余额

因为我们正在创建新的钱包,他们的余额将永远是零。Solana在其JSON RPC API中提供了一个requestAirdrop()方法,为开发目的请求硬币。让我们看看如何使用它。

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)
}

我们使用RequestAirdrop() ,通过提供请求上下文、钱包公钥(base58)和lamport金额,为Solana钱包请求Devnet币。

RequestAirdrop() 函数接受以lamport为单位的amount 参数,这是SOL的最小分数单位,类似于比特币的Satoshi。

1 lamport ~ 0.000000001 SOL。

你可以使用Solana Devnet Explorer来跟踪你的交易状态。

solana devnet explorer

接下来,使用Solana SDK提供的GetBalance() 方法,从Solana钱包的地址获取其拥有的SOL数量。让我们看看如何使用它。

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)
}

将SOLana转移到另一个钱包

在Solana上进行转账之前,你必须遵循四个步骤,以确保你的交易在网络上被创建。

首先,使用GetRecentBlockhash() 功能从网络上检索最新的区块哈希值。

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

然后,用检索到的区块哈希值和交易签字人的公钥制作一个转账信息。

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
)

转移信息包含交易发送方、接收方和金额的公钥。也可以在一个交易中进行多次转账。

用转账信息和交易签字人创建一个交易。

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

交易签字人是为该交易支付费用的账户。可以让另一个账户支付交易费用,但该账户也必须签署该交易。

接下来,将交易发送到网络,像这样。

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

将交易发送到网络后,你会收到它的哈希值,我们可以用它来追踪这笔交易。这里是完整的转账代码。

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)
}

用Go创建一个Solana钱包

现在你已经学会了如何与Solana网络互动,让我们扩展我们之前创建的代码块,用Go建立一个功能齐全的加密货币钱包。

让我们从为钱包创建一个自定义类型开始。在你的main.go 文件中保存以下代码。

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
}

我们需要一个函数来生成Wallet 类型的新实例,以便我们可以使用它。在main.go 文件中添加以下代码。

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

我们可以用我们的应用程序创建新的钱包,通过添加一个函数,使用他们的私钥导入现有的Solana钱包。将以下代码添加到main.go 文件中。

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
}

现在我们有了创建和导入Solana钱包账户的工作代码。让我们为Wallet 类型创建一些方法。

我们将首先添加一个请求SOL空投和检索钱包余额的函数。在main.go 文件中添加以下代码。

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
}

接下来,我们将创建一个函数,将SOL从我们的钱包转移到Solana网络上的其他钱包。将以下代码添加到main.go 文件中。

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
}

现在,我们已经为Wallet 类型创建了所有的方法。让我们来测试一下。

// 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())

结论

通过探索使用Solana和Go的区块链开发世界,你以最小的努力建立了一个加密货币钱包。我们看到了如何使用solana-go-sdk 包创建和检索Solana钱包,存储、接收和转移硬币。