📚

Sui DevnetでRustを使ってfaucetする

2023/01/21に公開

faucetするというのは、特定のアカウントでsuiを取得するというニュアンスで使っています。

devnetでは基本的に基軸通貨のairdropなどの機能を提供していますが、記事執筆時点ではdiscordコミュニティに参加して『devnet-faucet』チャンネル上で!faucet {address}とすることでsuiを取得することが可能です。

ただ、どうせだったらコードで実装しておいたほうがdevnetで遊ぶのなら何かと便利でしょうし、RustでSuiの学習も兼ねて実装してみた次第です。

Cargo.toml
[package]
name = "app"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
sui-sdk = { git = "https://github.com/MystenLabs/sui" }
sui-keys = { git = "https://github.com/MystenLabs/sui" }
sui-faucet = { git = "https://github.com/MystenLabs/sui" }
serde = { version = "1.0.144", features = ["derive"] }
serde_json = "1.0.88"
tokio = { version = "1", features = ["full"] }
reqwest = { version = "0.11.13", features = ["blocking", "json"] }
anyhow = { version = "1.0.64", features = ["backtrace"] }
fastcrypto = { git = "https://github.com/MystenLabs/fastcrypto", rev = "f451422b7f15e75e055a1830cbe5d1547fa87b74" }
dirs = "4.0.0"
main.rs
use std::path::PathBuf;
use sui_faucet::FaucetResponse;
use sui_keys::keystore::AccountKeystore;
use sui_sdk::types::{crypto::SignatureScheme, base_types::SuiAddress};
use sui_keys::keystore::FileBasedKeystore;
use std::collections::HashMap;
use fastcrypto::encoding::{Encoding, Hex};
#[tokio::main]
async fn main() {

    let keystore_path = get_keystore_path();
    let keystore = FileBasedKeystore::new(&keystore_path).unwrap();
    let (address, phrase, _) = create_new_keypair(keystore);
    
    let _ = request_sui_coins(address).await;

    println!("{} {} ", address, phrase);

}

fn get_keystore_path() -> PathBuf {
    match dirs::home_dir() {
        Some(v) => v.join(".sui").join("sui_config").join("sui.keystore"),
        None => panic!("Cannot obtain home directory path"),
    }
}

fn create_new_keypair(mut keystore: FileBasedKeystore) -> (SuiAddress, String, SignatureScheme)  {
    keystore.generate_and_add_new_key(
        SignatureScheme::ED25519, 
        None
    ).unwrap()
}

async fn request_sui_coins(request_address: SuiAddress) -> FaucetResponse {
    let remote_url = "https://faucet.devnet.sui.io";
    let gas_url = format!("{}/gas", remote_url);
    let data = HashMap::from([("recipient", Hex::encode(request_address))]);
    let map = HashMap::from([("FixedAmountRequest", data)]);

    let response = reqwest::Client::new()
        .post(&gas_url)
        .json(&map)
        .send()
        .await
        .unwrap_or_else(|e| panic!("Failed to talk to remote faucet {:?}: {:?}", gas_url, e));
    
    let full_bytes = response.bytes().await.unwrap();
    let faucet_response: FaucetResponse = serde_json::from_slice(&full_bytes)
        .map_err(|e| anyhow::anyhow!("json deser failed with bytes {:?}: {e}", full_bytes))
        .unwrap();

    if let Some(error) = faucet_response.error {
        panic!("Failed to get gas tokens with error: {}", error)
    };

    faucet_response
}

ちなみにですが、高頻度のリクエストをすることは出来ないようになっており、request_sui_coins()の部分でステータスコード429でエラーとなってしまいます。

発信元を見ているような雰囲気で、一度大量にアカウントを作成してそれぞれのアカウントでリクエストを送る方法を試してみましたが、同じく429となりました。ちゃんとしてました

個人的にSuiはまだmainnetローンチされていませんが、Aptosに比べると開発リソースが整っており開発体験が良いのが気に入ってます。

Discussion