2025-02-09 06:35:37 +00:00
|
|
|
use icepick_module::Module;
|
|
|
|
use serde::{Deserialize, Serialize};
|
|
|
|
use spacemesh::bech32::{self, Hrp};
|
|
|
|
use std::str::FromStr;
|
|
|
|
|
2025-02-14 01:34:42 +00:00
|
|
|
#[derive(Serialize, Deserialize, PartialEq, Eq, Clone, Debug, Default)]
|
2025-02-09 06:35:37 +00:00
|
|
|
#[serde(rename_all = "kebab-case")]
|
|
|
|
pub enum Cluster {
|
|
|
|
Testnet,
|
|
|
|
#[default]
|
|
|
|
Mainnet,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Cluster {
|
|
|
|
fn hrp(&self) -> bech32::Hrp {
|
|
|
|
match self {
|
|
|
|
Cluster::Testnet => Hrp::parse("stest").unwrap(),
|
|
|
|
Cluster::Mainnet => Hrp::parse("sm").unwrap(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl std::str::FromStr for Cluster {
|
|
|
|
type Err = &'static str;
|
|
|
|
|
|
|
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
|
|
match s {
|
|
|
|
"testnet" => Ok(Self::Testnet),
|
|
|
|
"mainnet" => Ok(Self::Mainnet),
|
|
|
|
_ => Err("Invalid value"),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl std::fmt::Display for Cluster {
|
|
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
|
|
match self {
|
|
|
|
Cluster::Testnet => f.write_str("testnet"),
|
|
|
|
Cluster::Mainnet => f.write_str("mainnet"),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(thiserror::Error, Debug)]
|
|
|
|
pub enum Error {}
|
|
|
|
|
|
|
|
#[derive(Serialize, Deserialize, Debug)]
|
|
|
|
pub struct GenerateWallet {
|
|
|
|
account: Option<String>,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Serialize, Deserialize, Debug)]
|
|
|
|
pub struct GetWalletAddress {
|
|
|
|
pubkey: [u8; 32],
|
|
|
|
cluster: Option<Cluster>,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Serialize, Deserialize, Debug)]
|
|
|
|
pub struct GetAccountData {
|
|
|
|
account: String,
|
|
|
|
cluster: Option<Cluster>,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Serialize, Deserialize, Debug)]
|
|
|
|
pub struct AwaitFunds {
|
|
|
|
address: String,
|
|
|
|
amount: String,
|
|
|
|
cluster: Option<Cluster>,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Serialize, Deserialize, Debug)]
|
|
|
|
#[serde(tag = "operation", content = "values", rename_all = "kebab-case")]
|
|
|
|
pub enum Operation {
|
|
|
|
GenerateWallet(GenerateWallet),
|
|
|
|
GetWalletAddress(GetWalletAddress),
|
|
|
|
AwaitFunds(AwaitFunds),
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Serialize, Deserialize, Debug)]
|
|
|
|
pub struct Request {
|
|
|
|
derived_keys: Option<Vec<[u8; 32]>>,
|
|
|
|
|
|
|
|
#[serde(flatten)]
|
|
|
|
operation: Operation,
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn run_async<F: std::future::Future>(f: F) -> F::Output {
|
|
|
|
tokio::runtime::Builder::new_current_thread()
|
|
|
|
.enable_all()
|
|
|
|
.build()
|
|
|
|
.unwrap()
|
|
|
|
.block_on(f)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub struct Spacemesh;
|
|
|
|
|
|
|
|
impl Module for Spacemesh {
|
|
|
|
type Error = Error;
|
|
|
|
type Request = Request;
|
|
|
|
|
|
|
|
fn describe_operations() -> Vec<icepick_module::help::Operation> {
|
|
|
|
use icepick_module::help::*;
|
|
|
|
|
|
|
|
let account = Argument::builder()
|
|
|
|
.name("account")
|
|
|
|
.description("The derivation index for the account.")
|
|
|
|
.r#type(ArgumentType::Optional)
|
|
|
|
.build();
|
|
|
|
|
|
|
|
let cluster = Argument::builder()
|
|
|
|
.name("cluster")
|
|
|
|
.description("Spacemesh cluster to interact with (mainnet, testnet).")
|
|
|
|
.r#type(ArgumentType::Required)
|
|
|
|
.build();
|
|
|
|
|
|
|
|
let generate_wallet = Operation::builder()
|
|
|
|
.name("generate-wallet")
|
|
|
|
.description("Generate a wallet for the given account.")
|
|
|
|
.build()
|
|
|
|
.argument(&account);
|
|
|
|
|
|
|
|
let get_wallet_address = Operation::builder()
|
|
|
|
.name("get-wallet-address")
|
|
|
|
.description("Get the address for a given wallet.")
|
|
|
|
.build()
|
|
|
|
.argument(&cluster)
|
|
|
|
.argument(
|
|
|
|
&Argument::builder()
|
|
|
|
.name("wallet_pubkey")
|
|
|
|
.description("Public key of the wallet.")
|
|
|
|
.r#type(ArgumentType::Required)
|
|
|
|
.build(),
|
|
|
|
);
|
|
|
|
|
|
|
|
vec![generate_wallet, get_wallet_address]
|
|
|
|
}
|
|
|
|
|
|
|
|
fn handle_request(request: Self::Request) -> Result<serde_json::Value, Self::Error> {
|
|
|
|
let Request {
|
|
|
|
operation,
|
|
|
|
derived_keys: _,
|
|
|
|
} = request;
|
|
|
|
|
|
|
|
match operation {
|
|
|
|
Operation::GenerateWallet(GenerateWallet { account }) => {
|
|
|
|
let account = u32::from_str(account.as_deref().unwrap_or("0")).unwrap();
|
|
|
|
|
|
|
|
Ok(serde_json::json!({
|
|
|
|
"blob": {},
|
|
|
|
"derivation_accounts": [(account | 1 << 31)],
|
|
|
|
}))
|
|
|
|
}
|
|
|
|
Operation::GetWalletAddress(GetWalletAddress { pubkey, cluster }) => {
|
|
|
|
use spacemesh::wallet::AsAddress;
|
|
|
|
let account = pubkey.as_address();
|
|
|
|
let hrp = cluster.unwrap_or_default().hrp();
|
|
|
|
let address = bech32::encode(hrp, &account).unwrap();
|
|
|
|
Ok(serde_json::json!({
|
|
|
|
"blob": {
|
|
|
|
"address": address,
|
|
|
|
},
|
|
|
|
"derivation_accounts": [],
|
|
|
|
}))
|
|
|
|
}
|
|
|
|
Operation::AwaitFunds(AwaitFunds {
|
|
|
|
address,
|
|
|
|
amount,
|
|
|
|
cluster,
|
|
|
|
}) => todo!(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|