icepick/crates/by-chain/icepick-spacemesh/src/lib.rs

173 lines
4.7 KiB
Rust

use icepick_module::Module;
use serde::{Deserialize, Serialize};
use spacemesh::bech32::{self, Hrp};
use std::str::FromStr;
#[derive(Serialize, Deserialize, PartialEq, Eq, Clone, Debug, Default)]
#[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!(),
}
}
}