SPL 的全稱是 Solana Program Library,它是在 Solana 鏈上運行的一系列程式。你可以把它想像成官方寫好的一些合約,讓開發者可以輕鬆調用功能。你可以參考這篇文章詳細了解這個概念。
今天我們會來體驗一下這個系列中的 Token Program,我們將會透過 Rust 製作 Cli 介面,讓我們可以發行自己的 Token。你可以在這裡參考這篇文章的範例程式碼。
初步認識 Solana
Solana 是一個開源的區塊鏈項目,它實現了高性能的抗審查區塊鏈,提供各種去中心化金融 (DeFi) 的解決方案。Solana 每秒交易量超過 5 萬筆,平均出塊時間約 2 秒(相較於以太坊每秒 15 筆,幣安智能幣約 100 筆)。由於金融交易的速度與安全需求,這些創新的速度讓 Solana 鏈快速被許多 Defi 以及區塊鏈項目喜愛。
Solana 帶來的重要創新之一,就是由 Anatoly Yakovenko 開發的歷史證明 (PoH) 共識機制。這個概念讓該 Solana 擁有極大的可規模性。
Solana 協定的目的,在於同時服務小型用戶與企業顧客。該協定的設計目標,就是要在降低交易成本的同時,仍然確保可規模性與高速處理。
SOL 是 Solana 的原生 Token,目前有兩種功能:
- 支付 Solana 的交易,智能合約的執行手續費
- 抵押成為節點驗證交易獲得獎勵。
你可以在以下的網站查閱更多資訊:
環境準備
讓我們來準備一下開發環境。
首先,請按以下步驟安裝 Rust:
brew install rustuprustup-init- 利用
rustc --version來驗證是否安裝成功 - 如果沒有找到
rustc,請嘗試設置環境變數export PATH="$HOME/.cargo/bin:$PATH" - 如果不使用
Homebrew,可以直接透過Rust官網安裝
然後,讓我們安裝 Solana 開發環境。Solana 目前有三種網路,分別是:
- Mainnet:
https://api.mainnet-beta.solana.com - Testnet:
https://api.testnet.solana.com - Devnet:
https://api.devnet.solana.com
我們也可以在本機上安裝自己的節點,這樣在開發測試上就會方便許多。如果只是想嚐鮮的讀者,可以考慮直接使用 Devnet 完成教學。而如果你是想深入 Solana 開發的話,就可以使用本地節點方便開發。
無論是使用本地節點或 Devnet,都需要安裝 Solana 開發環境節點與 Solana Tool。以下內容會以 Mac 為例子,其他平台的朋友請參考安裝指南。簡單來說,我們要先在 Terminal 上輸入指令:
bash sh -c "$(curl -sSfL https://release.solana.com/v1.7.10/install)"
安裝完成後會出現安裝位置,然後我們需要手動設置環境變數。比如說,如果安裝完後出現:
downloading v1.7.10 installer
Configuration: /home/solana/.config/solana/install/config.yml
Active release directory: /home/solana/.local/share/solana/install/active_release
* Release version: v1.7.10
* Release URL: https://github.com/solana-labs/solana/releases/download/v1.7.10/solana-release-x86_64-unknown-linux-gnu.tar.bz2
Update successful
我們就要設置 PATH="/home/solana/.local/share/solana/install/active_release/bin:${PATH}" 這樣的環境變數。
設置完成後,可以輸入 solana --version 來驗證是否安裝成功。
接下來,讓我們檢查一下機器上的錢包。我們應該會得到一個錢包地址 solana address,這就是我們本地機器上的錢包,所有在機器上進行的區塊鏈交易,我們都會使用本地機器錢包來進行交易。
solana address 發生錯誤,請使用 solana config get 確認設定,其中 Keypair Path 就是本地錢包位置。如果沒有此設定,可以使用 solana-keygen new 建立。最後是 cargo,如同所有語言平台一樣,Rust 也有自己的套件依賴管理。cargo 之於 Rust,就如同 npm 之於 node.js、cocoapods 之於 Swift 一樣。我們將透過 cargo 來建立一個 Rust 專案:
1. 安裝專案產生器 cargo install cargo-generate
2. 創建專案 cargo new spl-token

3. 用你習慣的 IDE 開啟資料夾

我們可以看到目錄結構中有:
src:這是撰寫程式原始碼的地方Cargo.toml,Cargo.lock如同Podfile,Podfile.lock守護著我們的套件依賴管理。

安裝依賴
在開始之前,我們需要先安裝將會使用到的依賴。讓我們打開 Cargo.toml,在當中加入以下的 dependencies:
[dependencies]
structopt = "0.3.21"
indicatif = "0.16.2"
log = "0.4.14"
env_logger = "0.9.0"
solana-sdk = "1.6.10"
solana-client = "1.6.10"
solana-cli-config = "1.6.10"
spl-token = { version = "3.1.0", features = ["no-entrypoint"] }
[dev-dependencies]
assert_cmd = "2.0.0"
predicates = "2.0.1"
tempfile = "3.2.0"
如下所示:

來介紹一下這些依賴的用途:
structopt:可以方便地將命令行解析成一個Struct,這對於我們Cli的結構上很便利。indicatif:可以幫助Cli顯示進度條資訊,你可以在這裡看看範例。log:可以印出日誌env_logger:可以加入環境設定來顯示日誌solana_sdk:可以操作Solana區塊鏈的API接口solana-clientsolana-cli-config
spl-token:可以操作Solana Program Library
撰寫原始碼
接著,讓我們在 src 內創建一個名為 create_spl_token 的資料夾,並且新增兩個檔案:
// /src/create_spl_token/mod.rs
use solana_sdk::{
message::Message,
program_pack::Pack,
pubkey::Pubkey,
signature::{Keypair, Signer},
system_instruction::create_account,
transaction::Transaction,
};
use spl_token::instruction::initialize_account;
use spl_token::instruction::initialize_mint;
mod utils;
// If you want to use spl-token library and rust code to create SPL Token, please refer to the following code.
pub fn main(decimals: u8) {
let my_keypair = utils::load_config_keypair();
let my_pubkey = my_keypair.pubkey();
let token_account_size = spl_token::state::Mint::LEN;
let rpc_client = utils::new_rpc_client();
let token_balance = rpc_client
.get_minimum_balance_for_rent_exemption(token_account_size)
.expect("failed to get min balance");
let new_account_keypair = Keypair::new(); // New random keypair
let new_account_pubkey = new_account_keypair.pubkey();
// 創建帳戶交易
let create_account_instruction = create_account(
&my_pubkey,
&new_account_pubkey,
token_balance,
token_account_size as u64,
&spl_token::ID,
);
// mint token 交易
let initialize_mint_instruction = initialize_mint(
&spl_token::ID,
&new_account_pubkey,
&my_pubkey,
None,
decimals,
)
.unwrap();
// 發送交易
let rpc_client = utils::new_rpc_client();
let (recent_blockhash, _fee_calculator) = rpc_client
.get_recent_blockhash()
.expect("failed to get recent blockhash");
let tx = Transaction::new(
&[&my_keypair, &new_account_keypair],
Message::new(
&[create_account_instruction, initialize_mint_instruction],
Some(&my_pubkey),
),
recent_blockhash,
);
rpc_client
.send_and_confirm_transaction_with_spinner(&tx)
.expect("tx failed");
println!("Created Token Mint: {}", new_account_pubkey);
println!("Transaction Signature: {}", utils::tx_signature(&tx));
}
// /src/create_spl_token/utils.rs
use solana_client::rpc_client::RpcClient;
use solana_sdk::{
commitment_config::CommitmentConfig,
signature::{read_keypair_file, Keypair},
transaction::Transaction,
};
pub fn tx_signature(tx: &Transaction) -> String {
tx.signatures
.first()
.expect("transaction not signed")
.to_string()
}
pub fn load_config_keypair() -> Keypair {
let config_path = solana_cli_config::CONFIG_FILE.as_ref().unwrap();
let cli_config =
solana_cli_config::Config::load(config_path).expect("failed to load config file");
read_keypair_file(cli_config.keypair_path).expect("failed to load keypair")
}
pub fn new_rpc_client() -> RpcClient {
let config_path = solana_cli_config::CONFIG_FILE.as_ref().unwrap();
let cli_config =
solana_cli_config::Config::load(config_path).expect("failed to load config file");
RpcClient::new_with_commitment(cli_config.json_rpc_url, CommitmentConfig::confirmed())
}
這個主要的程式碼很簡單,我們先創建 Token 的 Account,然後再創建 mint 我們的 Token,最後發送交易。
這邊我們需要先釐清 SLP Token 的一些機制。這 Token 部分與其他區塊鏈有點不同。在其他區塊鏈上,比如是以太坊 ETH,它的 Token 最常見標準是 ERC20,在轉帳的時候,其實 ETH address 與 ERC20 Token 的地址基本上是一樣的,就如下圖所示:

但在 Solana 鏈上,每個 SPL Token 都有自己的 Token Account,每個地址底下可以有多個 SPL Token Account,有點像是在銀行中又開立了外幣存款那種感覺:

接下來,我們要了解一些 Token 的基本操作,大概可以分成下列幾類:
transfer:Token 的轉帳操作mint:Token 的鑄造burn:Token 的銷毀authority:操作 Token 的授權者
如果想深入了解 SPL Token 的機制,可以參考這篇文章。
要創建一個 Token,流程就會是:創建 Token -> 鑄造 Token。感覺有點像是發行美元(定義),然後印製美元(供應量)一樣。
好的!這邊完成之後,我們要到 src/main 來修改一下程式碼,讓我們可以呼叫指令。
// /src/main.rs
#[macro_use]
extern crate log;
extern crate env_logger;
use env_logger::{Builder, Target};
use log::{debug, error, info, warn};
use std::env;
use structopt::StructOpt;
use std::num::ParseIntError;
mod create_spl_token;
// 入口
fn main() {
let mut builder = Builder::from_default_env();
builder.target(Target::Stdout);
builder.init();
let args = Cli::from_args();
// 分析指令,並且執行
match args.cmd {
Command::SPL(value) => match value.spl_operating {
SPLOperating::CreateToken(info) => create_spl_token::main(info.decimals),
},
}
info!("success!");
}
#[derive(Debug, StructOpt)]
struct Cli {
#[structopt(subcommand)] // Note that we mark a field as a subcommand
cmd: Command,
}
#[derive(Debug, StructOpt)]
enum Command {
/// SPL
SPL(SPL),
}
#[derive(Debug, StructOpt)]
struct SPL {
/// SPL Operating
#[structopt(subcommand)] // Note that we mark a field as a subcommand
spl_operating: SPLOperating,
}
// subsubcommand!
#[derive(Debug, StructOpt)]
enum SPLOperating {
/// Create Token
CreateToken(CreateToken),
}
#[derive(Debug, StructOpt)]
struct CreateToken {
/// decimals
#[structopt(short, default_value = "6", parse(try_from_str = parse_hex))]
decimals: u8,
}
fn parse_hex(src: &str) -> Result<u8, ParseIntError> {
u8::from_str_radix(src, 16)
}
這邊的程式碼很簡單,我們製作了一個 Cli 的入口,然後分析指令,並且執行我們剛剛寫好的 create_spl_token。
我們可以在terminal 嘗試執行一下指令。讓我們邊把 Solana 節點設定指向Devnet。solana config set --url https://api.devnet.solana.com
應該會看到一段輸出,顯示目前的 RPC URL: https://devnet.solana.com。
接著我們在專案內執行。RUST_LOG=debug cargo run spl create-token
接著應該會看到一段輸出。

然後我們把 Created Token Mint: 6uze7gNUwRSEARX49k8oiF3FdZXXXXXXXXXXX 中的地址,貼到區塊鏈遊覽器查詢。記得我們要使用Devnet。

恭喜!到這裡我們已經完成透過 Rust 製作一個 Cli 介面,並且發行 SPL Token 的工作了!
總結
我們透過了Rust 製作一個 Cli 介面,並且發行 SPL Token,在其中我們了解了 Solana 鏈上的 Token 機制。了解 Token 的基本生命週期,以及 Token Account 的機制。所有的程式碼都可以在這邊找到,祝你有個美好的 Coding 夜晚,我們下次見。當然如果你有任何問題,歡迎在任何地方與我聯絡!