1152 words
6 minutes

理解 SPL Token:Solana 的通用代币标准

一、什么是 SPL Token?#

在以太坊中,ERC-20 是最常见的代币标准。

在 Solana 上,对应的标准是 SPL Token(Solana Program Library Token)

SPL Token 并不是单个智能合约,而是一套系统标准,

由一个官方 Program(Token Program)负责管理代币的生命周期。

项目说明
Program IDTokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA
功能创建代币、铸造、转账、冻结、销毁
底层结构每个代币都有 Mint Account;每个持有人都有 Token Account
存储形式所有代币余额都存储在独立的账户数据中,而非 Program 内部

简单说:

  • Mint Account 是代币的定义(类似 ERC-20 合约)。
  • Token Account 是用户的余额存储位置。

二、代币账户的结构#

每种 SPL Token 都有一组对应的账户结构:

类型说明
Mint Account定义代币(总发行量、精度、小数位等)
Token Account存储某个用户的代币余额
Associated Token Account (ATA)系统推荐的 Token Account 生成方式,自动与用户钱包地址绑定

在实际开发中,我们几乎总是使用 ATA(Associated Token Account),

因为它能确保每个钱包地址对同一种代币只有一个账户。


三、准备工作#

安装依赖:

Terminal window
go get github.com/gagliardetto/[email protected]
go get github.com/gagliardetto/[email protected]

设置环境:

package main
import (
"context"
"fmt"
"log"
"github.com/gagliardetto/solana-go"
"github.com/gagliardetto/solana-go/programs/associated-token-account"
"github.com/gagliardetto/solana-go/programs/system"
"github.com/gagliardetto/solana-go/programs/token"
"github.com/gagliardetto/solana-go/rpc"
)

四、创建新代币(Mint)#

以下代码展示如何在 Devnet 上创建一个新代币(Mint Account)。

func main() {
ctx := context.Background()
client := rpc.New(rpc.DevNet_RPC)
// 加载钱包(即代币创建者)
creator, err := solana.PrivateKeyFromSolanaKeygenFile("/Users/alan/.config/solana/devnet.json")
if err != nil {
log.Fatalf("加载钱包失败: %v", err)
}
// 创建 Mint 账户(新代币定义)
mint := solana.NewWallet()
// 获取区块哈希
recent, err := client.GetLatestBlockhash(ctx, rpc.CommitmentFinalized)
if err != nil {
log.Fatalf("获取哈希失败: %v", err)
}
// 构建交易
tx, err := solana.NewTransaction(
[]solana.Instruction{
// 创建账户
system.NewCreateAccountInstruction(
2_039_280, // 租金
token.MintAccountSize, // 账户大小
token.ProgramID, // 所属 Program
creator.PublicKey(),
mint.PublicKey(),
).Build(),
// 初始化 Mint
token.NewInitializeMint2Instruction(
9, // 精度(小数位数)
creator.PublicKey(), // 铸造权限
nil, // 冻结权限(可选)
mint.PublicKey(), // Mint 地址
token.ProgramID,
).Build(),
},
recent.Value.Blockhash,
creator.PublicKey(),
)
if err != nil {
log.Fatalf("构建交易失败: %v", err)
}
// 签名并发送
_, err = tx.Sign(func(pub solana.PublicKey) *solana.PrivateKey {
switch {
case pub.Equals(creator.PublicKey()):
return &creator
case pub.Equals(mint.PublicKey()):
return &mint.PrivateKey
default:
return nil
}
})
if err != nil {
log.Fatalf("签名失败: %v", err)
}
sig, err := client.SendTransaction(ctx, tx)
if err != nil {
log.Fatalf("发送失败: %v", err)
}
fmt.Println("成功创建代币 Mint:", mint.PublicKey())
fmt.Println("交易签名:", sig)
}

运行后,你将拥有一个新的代币定义(Mint Account)。

可以在 Solana Explorer 中搜索 mint.PublicKey() 查看详情。


五、创建持有人账户(Associated Token Account)#

每个用户想要持有该代币,都需要一个 Token Account。

使用 Associated Token Program 可以自动生成:

ata, _, err := associatedtokenaccount.GetAssociatedTokenAddress(
creator.PublicKey(), // 用户钱包地址
mint.PublicKey(), // 对应代币的 Mint 地址
)
if err != nil {
log.Fatalf("生成 ATA 失败: %v", err)
}
instruction := associatedtokenaccount.NewCreateInstruction(
creator.PublicKey(), // 资助账户
ata, // 目标 ATA
creator.PublicKey(), // 拥有者
mint.PublicKey(), // 代币
).Build()

该指令会自动创建一个符合标准的代币账户。

注意: ATA 的地址是可预测的,因此无需存数据库。

你随时可以通过同样方法计算出来。


六、铸造代币(Mint To)#

一旦 Mint 创建完成,就可以给任意账户“铸造”代币:

mintInstruction := token.NewMintToInstruction(
mint.PublicKey(), // 代币 Mint 地址
ata, // 接收账户
creator.PublicKey(), // 铸造权限
1_000_000_000, // 数量(考虑精度)
).Build(

再组合交易发送即可。


七、查询余额#

查询某个 Token Account 的余额,只需调用 RPC:

balance, err := client.GetTokenAccountBalance(ctx, ata, rpc.CommitmentFinalized)
if err != nil {
log.Fatalf("查询余额失败: %v", err)
}
fmt.Println(" 当前余额:", balance.Value.UiAmountString)

八、执行转账#

Token Program 的 Transfer 指令允许用户从一个 Token Account 向另一个账户发送代币:

instruction := token.NewTransferInstruction(
amount, // 转账数量
senderATA, // 发送方代币账户
receiverATA, // 接收方代币账户
sender.PublicKey(), // 授权签名者
nil, // 多签(可选)
token.ProgramID,
).Build()

再像前面那样签名并广播。

这与 System Program 的 SOL 转账几乎一致,只是多了 Token Account 参与。


九、事件与监听(gRPC)#

在生产环境中,我们通常需要监听某个 Mint 的交易事件。

Solana 支持通过 gRPC 订阅日志流

stream, err := client.LogsSubscribe(ctx, rpc.LogsSubscribeFilterMentions{
Mentions: []solana.PublicKey{token.ProgramID},
}, rpc.CommitmentFinalized)
if err != nil {
log.Fatalf("订阅失败: %v", err)
}
for {
msg, err := stream.Recv()
if err != nil {
log.Fatal("接收失败:", err)
}
fmt.Println(" Token Program 日志:", msg.Value.Logs)
}

通过这种方式,可以捕获任意代币的转账、铸造等链上事件。


十、总结#

内容概念示例函数
创建代币Mint AccountNewInitializeMint2Instruction
创建代币账户ATAassociatedtokenaccount.NewCreateInstruction
铸造代币Mint Totoken.NewMintToInstruction
转账Token Transfertoken.NewTransferInstruction
查询余额RPC 调用GetTokenAccountBalance
事件监听gRPC 日志流LogsSubscribe

Share

If this article helped you, please share it with others!

理解 SPL Token:Solana 的通用代币标准
https://blog.ithuo.net/posts/solana-spl-token-introduction/
Author
Derick
Published at
2024-12-17
License
CC BY-NC-SA 4.0
Last updated on 2024-12-17,405 days ago

Some content may be outdated

Table of Contents