173 lines
4.7 KiB
Rust
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, 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!(),
|
||
|
}
|
||
|
}
|
||
|
}
|