Blockchain

透過 Rust 製作 Cli 介面 在 Solana 創建屬於自己的 Token

透過 Rust 製作 Cli 介面 在 Solana 創建屬於自己的 Token
透過 Rust 製作 Cli 介面 在 Solana 創建屬於自己的 Token
In: Blockchain

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,目前有兩種功能:

  1. 支付 Solana 的交易,智能合約的執行手續費
  2. 抵押成為節點驗證交易獲得獎勵。

備註:智能合約是一種電腦間的交易協定,區塊鏈上的所有使用者都可以看到基於區塊鏈的智能合約。

你可以在以下的網站查閱更多資訊:

環境準備

讓我們來準備一下開發環境。

首先,請按以下步驟安裝 Rust

  1. brew install rustup
  2. rustup-init
  3. 利用 rustc --version 來驗證是否安裝成功
  4. 如果沒有找到 rustc,請嘗試設置環境變數 export PATH="$HOME/.cargo/bin:$PATH"
  5. 如果不使用 Homebrew,可以直接透過 Rust 官網安裝

然後,讓我們安裝 Solana 開發環境。Solana 目前有三種網路,分別是:

  1. Mainnet:https://api.mainnet-beta.solana.com
  2. Testnet:https://api.testnet.solana.com
  3. 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.jscocoapods 之於 Swift 一樣。我們將透過 cargo 來建立一個 Rust 專案:

1. 安裝專案產生器 cargo install cargo-generate

2. 創建專案 cargo new spl-token

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

我們可以看到目錄結構中有:

  • src:這是撰寫程式原始碼的地方
  • Cargo.tomlCargo.lock 如同 PodfilePodfile.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-client
    • solana-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())
}

這個主要的程式碼很簡單,我們先創建 TokenAccount,然後再創建 mint 我們的 Token,最後發送交易。

這邊我們需要先釐清 SLP Token 的一些機制。這 Token 部分與其他區塊鏈有點不同。在其他區塊鏈上,比如是以太坊 ETH,它的 Token 最常見標準是 ERC20,在轉帳的時候,其實 ETH addressERC20 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 節點設定指向Devnetsolana 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 夜晚,我們下次見。當然如果你有任何問題,歡迎在任何地方與我聯絡!

作者
Yu Hao Chen
自認為終身學習者,對多領域都有濃厚興趣,喜歡探討各種事物。目前專職軟體開發,系統架構設計,企業解決方案。最喜歡 iOS with Swift。
評論
更多來自 AppCoda 中文版
透過 Rust 製作 Cli 介面 在 Solana 創建屬於自己的 Token
Blockchain

透過 Rust 製作 Cli 介面 在 Solana 創建屬於自己的 Token

SPL 的全稱是 Solana Program Library,它是在 Solana 鏈上運行的一系列程式。你可以把它想像成官方寫好的一些合約,讓開發者可以輕鬆調用功能。你可以參考這篇文章詳細了解這個概念。 今天我們會來體驗一下這個系列中的 Token Program,我們將會透過 Rust 製作 Cli 介面,讓我們可以發行自己的
很好! 你已成功註冊。
歡迎回來! 你已成功登入。
你已成功訂閱 AppCoda 中文版 電子報。
你的連結已失效。
成功! 請檢查你的電子郵件以獲取用於登入的連結。
好! 你的付費資料已更新。
你的付費方式並未更新。