Solana笔记 11.案例:写一个SPL标准代币

跟我一起从0开始学习Solana合约开发,一起实操,一起做项目。这是一个系列文章,系统地记录了我的学习笔记。

熟悉以太坊的同学知道,创建 ERC-20 代币需要编写一个继承自 OpenZeppelin 库(@openzeppelin/contracts)的 ERC-20 合约,并定义代币的名称、符号、供应量等。然而,在 Solana 中创建代币和以太坊有很大的不同。

今天我们通过实际操作创建和发行一个 SPL 标准代币,深入理解 Solana 的代币管理,并了解其核心概念。

安装 SPL Token CLI

在 Solana 上,创建和管理 SPL 代币的过程相对简化,因为 Solana 提供了一个现成的 SPL Token Program开发者不需要编写复杂的智能合约代码,只需使用现成的命令行工具或库

为了使用 SPL Token Program 创建 SPL 代币,我们需要使用 Solana 提供的命令行工具 spl-token,现在来安装这个工具。

注意:需要你提前安装好 rustsolana 环境,如果你还没有安装,请参考本系列文章第一篇。

运行以下命令:

cargo install spl-token-cli

创建代币

使用 spl-token 命令行工具创建一个新的代币。创建代币时,Solana 会为你生成一个 铸币账户(Mint Account),这个账户存储代币的必要信息(例如供应量、授权账户等)。

运行以下命令,创建一个新的代币:

spl-token create-token

输出如下:

Creating token G8Z8ArrhUnNYZ1ZjvoNpECSR67Ug5RTbYzewvgcUp3h3 under program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA

Address:  G8Z8ArrhUnNYZ1ZjvoNpECSR67Ug5RTbYzewvgcUp3h3
Decimals:  9

Signature: 4JTsCKe9RkN8RPjPpU9piiS9GfVFm4gqgL3KwszZcdWpRzgwg7LtyUKwdD517Xcdi3WumBdRBBLQCCsDDTwZLfW

生成的 Address(G8Z8Ar...)是代币的铸币账户地址(Mint Address),用于存储代币信息,如供应量和授权账户,代表了一种代币。

新创建的代币初始供应量为 0,可通过以下命令验证:

spl-token supply G8Z8ArrhUnNYZ1ZjvoNpECSR67Ug5RTbYzewvgcUp3h3 

# 输出:0

在铸造代币之前,需要先创建一个账户,来持有新代币的余额。

创建 Token Account

在为代币铸造供应量之前,需要创建一个 Token Account,用于存储代币余额。每个 Token Account 关联特定代币(Mint)。运行以下命令创建账户:

spl-token create-account G8Z8ArrhUnNYZ1ZjvoNpECSR67Ug5RTbYzewvgcUp3h3

输出如下:

Creating account Dx31MHuMRdf14mr73c6Bn7oKCVMP8nqvv5EJX7m8pES7

Signature: 4FUTpTBmMZ1mmnWngVZqMU3xyF3XQ2S4CYLmW7bms6ba8ALe4a4s4Dvrv4NdeKPSXAhTaDRs4TV9EtiqwL2cgucX

生成的地址 Dx31MH... 就是代币账户地址,用于存储上文创建的代币(即铸币账户)的余额。现在,这个代币账户的余额是 0

铸造(Mint)代币

铸造代币的过程会增加铸币账户的供应量(Supply)代币账户的余额(Balance)。以下是铸造命令格式:

spl-token mint <mint_address> <amount> <token_account_address>

例如,为刚创建的代币账户铸造 100 个代币:

spl-token mint G8Z8ArrhUnNYZ1ZjvoNpECSR67Ug5RTbYzewvgcUp3h3 100 Dx31MHuMRdf14mr73c6Bn7oKCVMP8nqvv5EJX7m8pES7

输出如下:

Minting 100 tokens
  Token: G8Z8ArrhUnNYZ1ZjvoNpECSR67Ug5RTbYzewvgcUp3h3
  Recipient: Dx31MHuMRdf14mr73c6Bn7oKCVMP8nqvv5EJX7m8pES7

Signature: 3eetz3xP3nNdTqNzGPrWcvroYXJUHJSm53xHgNcfSvBA8Hk4Qmz6y7QPVcgw3ABWBu8a5fnB7moWYgBhj3DLbiSU

此时可通过以下命令查看铸币账户供应量和代币账户余额:

spl-token supply G8Z8ArrhUnNYZ1ZjvoNpECSR67Ug5RTbYzewvgcUp3h3  # 输出:100

spl-token balance G8Z8ArrhUnNYZ1ZjvoNpECSR67Ug5RTbYzewvgcUp3h3 # 输出:100

查询代币和账户信息

查看代币账户信息:

spl-token display Dx31MHuMRdf14mr73c6Bn7oKCVMP8nqvv5EJX7m8pES7

输出如下:

SPL Token Account
  Address: Dx31MHuMRdf14mr73c6Bn7oKCVMP8nqvv5EJX7m8pES7
  Program: TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA
  Balance: 100
  Decimals: 9
  Mint: G8Z8ArrhUnNYZ1ZjvoNpECSR67Ug5RTbYzewvgcUp3h3
  Owner: EZVWcdQu3qVMEhTikTLyQZoBaR9KXWwyxhpiTugRe5Rk
  State: Initialized
  Delegation: (not set)
  Close authority: (not set)

查看铸币账户信息:

spl-token display G8Z8ArrhUnNYZ1ZjvoNpECSR67Ug5RTbYzewvgcUp3h3

输出如下:

SPL Token Mint
  Address: G8Z8ArrhUnNYZ1ZjvoNpECSR67Ug5RTbYzewvgcUp3h3
  Program: TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA
  Supply: 100000000000
  Decimals: 9
  Mint authority: EZVWcdQu3qVMEhTikTLyQZoBaR9KXWwyxhpiTugRe5Rk
  Freeze authority: (not set)

使用区块浏览器查看代币信息

除了使用 spl-token display <mint_address> 查看代币信息,你还可以打开 Devnet 的区块浏览器查看我们刚刚创建的代币,如下网址:

<https://explorer.solana.com/address/G8Z8ArrhUnNYZ1ZjvoNpECSR67Ug5RTbYzewvgcUp3h3?cluster=devnet>

查看持有的全部代币

spl-token accounts

运行命令,输出我持有的全部代币:

Token                                         Balance
-----------------------------------------------------
G8Z8ArrhUnNYZ1ZjvoNpECSR67Ug5RTbYzewvgcUp3h3  100

转账代币

使用 spl-token 工具可以进行代币的转账、查询余额等各种操作。

给钱包转账代币

我们创建一个新钱包,来测试钱包之间的代币转账。使用 solana-keygen 命令生成一个新的钱包地址:

# 这里指定 id-01.json 是为了和默认的 id.json 进行区分
solana-keygen new -o ~/.config/solana/id-01.json

输出示例:

pubkey: GEpoPXXoyWi3TZeZmvFgheoej2kdBMGXV7tDetUWKGtu

现在,我们向这个新钱包转账:

spl-token transfer G8Z8ArrhUnNYZ1ZjvoNpECSR67Ug5RTbYzewvgcUp3h3 50 GEpoPXXoyWi3TZeZmvFgheoej2kdBMGXV7tDetUWKGtu

输出报错了,内容如下:

Transfer 50 tokens
  Sender: Dx31MHuMRdf14mr73c6Bn7oKCVMP8nqvv5EJX7m8pES7
  Recipient: GEpoPXXoyWi3TZeZmvFgheoej2kdBMGXV7tDetUWKGtu
Error: "Error: The recipient address is not funded. Add `--allow-unfunded-recipient` to complete the transfer."

这个错误是因为接收地址是我们刚刚创建的,还没有代币账户来存储余额,通常,有两种方法解决:

  1. 让接收者自己创建代币账户

首先,切换默认钱包至新创建的钱包:

# 设置钱包
solana config set --keypair ~/.config/solana/id-01.json

# 查看当前配置钱包
solana address

# 查看当前所有配置信息
solana config get

然后,创建代币账户:

spl-token create-account G8Z8ArrhUnNYZ1ZjvoNpECSR67Ug5RTbYzewvgcUp3h3

之后,你就可以切换回之前的钱包,给这个新钱包转账了。

  1. 发送者为接收者创建代币账户

这种方法需要发送者资助一些 SOL 给接收者,因为创建代币账户是需要支付租金的。代币账户虽然是接收方的,但创建时需要有人支付这笔费用。发送方选择为接收方代付这部分费用,因此称为“fund”。

运行以下命令:

# --fund-recipient 表示资助接收者 SOL 用于支付代币账户租金
# --allow-unfunded-recipient 表示允许自动为接收者创建代币账户
spl-token transfer --fund-recipient G8Z8ArrhUnNYZ1ZjvoNpECSR67Ug5RTbYzewvgcUp3h3 50 GEpoPXXoyWi3TZeZmvFgheoej2kdBMGXV7tDetUWKGtu --allow-unfunded-recipient

输出内容如下:

Transfer 50 tokens
  Sender: Dx31MHuMRdf14mr73c6Bn7oKCVMP8nqvv5EJX7m8pES7
  Recipient: GEpoPXXoyWi3TZeZmvFgheoej2kdBMGXV7tDetUWKGtu
  Recipient associated token account: CW3FRMnpBAorPADHtaYLW4EonGDY75JNjSCBDtg4PZb6
  Funding recipient: CW3FRMnpBAorPADHtaYLW4EonGDY75JNjSCBDtg4PZb6

Signature: 2bBQK9Tf7y55s9ELHTxfqJgaDqEE9Q8BTHZ9sdLmGmFktSzexmLUe5DGEEvtrWTZSY4K35nxJrkkBS2vRBgZsjik

通过输出信息可以看到,已经自动为接收者创建了一个代币账户,并成功转账了 50 个代币。

给代币账户转账代币

除了给钱包(已关联代币账户)转账,也可以直接给已存在的代币账户转账。

例如,向刚刚新创建的代币账户转账:

spl-token transfer G8Z8ArrhUnNYZ1ZjvoNpECSR67Ug5RTbYzewvgcUp3h3 50 CW3FRMnpBAorPADHtaYLW4EonGDY75JNjSCBDtg4PZb6

输出如下:

Transfer 50 tokens
  Sender: Dx31MHuMRdf14mr73c6Bn7oKCVMP8nqvv5EJX7m8pES7
  Recipient: CW3FRMnpBAorPADHtaYLW4EonGDY75JNjSCBDtg4PZb6

Signature: 4PKH4QUemGJeM2N6bxV36tTa1gUnd2UBMYRXENm41EEgzBVChGzpY7uKr7KMzbE11iAEERECMJXsXfEdAskjPN1f

现在,我们查看这个代币账户的余额看看效果,运行以下命令:

spl-token display CW3FRMnpBAorPADHtaYLW4EonGDY75JNjSCBDtg4PZb6

输出如下:

SPL Token Account
  Address: CW3FRMnpBAorPADHtaYLW4EonGDY75JNjSCBDtg4PZb6
  Program: TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA
  Balance: 100
  Decimals: 9
  Mint: G8Z8ArrhUnNYZ1ZjvoNpECSR67Ug5RTbYzewvgcUp3h3
  Owner: GEpoPXXoyWi3TZeZmvFgheoej2kdBMGXV7tDetUWKGtu
  State: Initialized
  Delegation: (not set)
  Close authority: (not set)

可以看到,余额是 100 个代币,符合预期,因为第一次向钱包转账了 50 个,第二次直接向代币账户转账了 50 个。

配置元数据

我们的代币还没有元数据,比如 namesymboluri 等,由于我们的代币是由 Token Program (经典 SPL Token Program) 创建的,要想添加元数据,需要使用 Metaplex Token Metadata Program 才能实现,而官方已经推荐使用新的代币标准( 即 Token-2022 Program )。

Token-2022 是对 经典 SPL Token Program 的增强版本,支持更丰富的功能,可以直接为代币设置名称(name)符号(symbol)资源定位符(URI)等。

因此,我们重新创建一个 Token-2022 Program 的代币。

创建一个 Token-2022 Program 的代币

# -p 指定 Token-2022 Program 的地址
# --enable-metadata 启用元数据
spl-token create-token -p TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb --enable-metadata

输出为:

Creating token AQtCRnZtcD4B9Z4ovd9qEhe1cJo5EvDNB2rtq3tKeemD under program TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb
To initialize metadata inside the mint, please run `spl-token initialize-metadata AQtCRnZtcD4B9Z4ovd9qEhe1cJo5EvDNB2rtq3tKeemD &lt;YOUR_TOKEN_NAME> &lt;YOUR_TOKEN_SYMBOL> &lt;YOUR_TOKEN_URI>`, and sign with the mint authority.

Address:  AQtCRnZtcD4B9Z4ovd9qEhe1cJo5EvDNB2rtq3tKeemD
Decimals:  9

Signature: 5aE4od7ks3NNckwt3oBXDo9UFfHfLyMLTj4CsYyM8YJK1toDypkvAXTvp4nwQoa62vA49MgrhapUywzKg8cDUYGo

为新代币初始化元数据

根据提示命令,我们为新代币添加 namesymbol

spl-token initialize-metadata AQtCRnZtcD4B9Z4ovd9qEhe1cJo5EvDNB2rtq3tKeemD "My SPL Token" "MST" ""

初始化元数据之后,查看代币信息,运行命令:

spl-token display AQtCRnZtcD4B9Z4ovd9qEhe1cJo5EvDNB2rtq3tKeemD

输出如下:

SPL Token Mint
  Address: AQtCRnZtcD4B9Z4ovd9qEhe1cJo5EvDNB2rtq3tKeemD
  Program: TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb
  Supply: 0
  Decimals: 9
  Mint authority: EZVWcdQu3qVMEhTikTLyQZoBaR9KXWwyxhpiTugRe5Rk
  Freeze authority: (not set)
Extensions
  Metadata Pointer:
    Authority: EZVWcdQu3qVMEhTikTLyQZoBaR9KXWwyxhpiTugRe5Rk
    Metadata address: AQtCRnZtcD4B9Z4ovd9qEhe1cJo5EvDNB2rtq3tKeemD
  Metadata:
    Update Authority: EZVWcdQu3qVMEhTikTLyQZoBaR9KXWwyxhpiTugRe5Rk
    Mint: AQtCRnZtcD4B9Z4ovd9qEhe1cJo5EvDNB2rtq3tKeemD
    Name: My SPL Token
    Symbol: MST
    URI: (not set)

创建代币账户,存储这个新代币的余额

spl-token create-account AQtCRnZtcD4B9Z4ovd9qEhe1cJo5EvDNB2rtq3tKeemD

运行命令,输出如下:

Creating account ExgvFXbD6W29GijUpwsLsHEiUNeBMxVCbUeTAnqVchoE

Signature: AgBFDkasrAvd1owFGn59oJY2DPT7Gh5Vn5tGhjSmqCiv8AfHBSve9qSQfL8oQwvh6XL7bXJpCHszS6AcPQR7bnw

查看代币账户,运行命令:

spl-token display ExgvFXbD6W29GijUpwsLsHEiUNeBMxVCbUeTAnqVchoE

输出如下:

SPL Token Account
  Address: ExgvFXbD6W29GijUpwsLsHEiUNeBMxVCbUeTAnqVchoE
  Program: TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb
  Balance: 0
  Decimals: 9
  Mint: AQtCRnZtcD4B9Z4ovd9qEhe1cJo5EvDNB2rtq3tKeemD
  Owner: EZVWcdQu3qVMEhTikTLyQZoBaR9KXWwyxhpiTugRe5Rk
  State: Initialized
  Delegation: (not set)
  Close authority: (not set)
Extensions:
  Immutable owner

配置区块浏览器(name、logo)

现在,你打开区块浏览器查看这个新代币,发现 logo 的位置依然显示 Unknown Token,这是为什么呢?

因为我们刚刚没有配置 uri!在 Solana 的代币元数据标准中,URI 字段通常用于存储指向 JSON 文件的链接。这个 JSON 文件包含代币的详细信息,例如 name、symbol 和 logo URL。

选择 Web3 存储服务

市面上有众多 Web3 存储服务,例如:IPFSWeb3.StorageNFT.StorageArweavePinata 等。它们大都有免费使用的空间,为了避免绑定银行卡,我选择使用 Pinata

在 <https://pinata.cloud/> 注册成功后,上传文件获得 IPFS CID,然后就可以通过 https://ipfs.io/ipfs/{IPFS_CID} 公开访问我们的资源了。

Pinata 的使用自行研究即可,非常简单。

  1. 上传 Logo 图标

选择一个 logo 图标,通过 Pinata 网站上传,获得 IPFS_CID。

  1. 上传 JSON 文件

我们的代币元数据如下:

{
  "name": "My SPL Token",
  "symbol": "MST",
  "description": "A pig token.",
  "image": "https://ipfs.io/ipfs/{IPFS_CID}",
  "external_url": "",
  "attributes": [],
  "properties": {
    "files": [
      {
        "uri": "https://ipfs.io/ipfs/{IPFS_CID}",
        "type": "image/png"
      }
    ]
  }
}

这里的 image 字段写你的 logo 访问地址,然后按照同样的步骤上传至 Pinata。

更新代币元数据

获得 ipfs 链接后,我们使用以下命令更新代币的 URI 字段:

spl-token update-metadata AQtCRnZtcD4B9Z4ovd9qEhe1cJo5EvDNB2rtq3tKeemD uri "https://ipfs.io/ipfs/{Your_Pinata_CID}"

更新成功后,再次打开 Solana 区块浏览器,就可以正常看到我们配置的 Logo 和 Name 了。

总结

在 Solana 中,创建代币无需编写智能合约。借助现有的 spl-token 工具,Solana 提供了强大的内置程序和命令行工具,大大简化了代币的创建与管理流程。

通过今天的学习与实践,相信你已经熟悉了创建和发行 Solana 代币的核心步骤。至此,本文内容告一段落。

如果你对 Web3 前沿探索Go 后端技术,以及产品与哲学的深度思考感兴趣,可以关注我的公众号:认知那些事。每一篇文章,都是我精心打磨的干货。扫码关注,一起探索技术与思想的广阔世界。

认知那些事.png

点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
认知那些事
认知那些事
0x2b62...95a0
人立于天地之间,必然有我们的出路。