add wallet generation to solana
This commit is contained in:
parent
10cda7824c
commit
e28047de92
|
@ -1067,6 +1067,7 @@ dependencies = [
|
||||||
name = "icepick-solana"
|
name = "icepick-solana"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"ed25519-dalek 1.0.1",
|
||||||
"icepick-module",
|
"icepick-module",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
|
|
@ -4,6 +4,7 @@ version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
ed25519-dalek = "=1.0.1"
|
||||||
icepick-module = { version = "0.1.0", path = "../../icepick-module" }
|
icepick-module = { version = "0.1.0", path = "../../icepick-module" }
|
||||||
serde = { workspace = true, features = ["derive"] }
|
serde = { workspace = true, features = ["derive"] }
|
||||||
serde_json.workspace = true
|
serde_json.workspace = true
|
||||||
|
|
|
@ -3,6 +3,7 @@ use icepick_module::{
|
||||||
Module,
|
Module,
|
||||||
};
|
};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
use solana_sdk::signer::Signer;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
// How does this not exist in solana_sdk.
|
// How does this not exist in solana_sdk.
|
||||||
|
@ -11,6 +12,16 @@ const LAMPORTS_PER_SOL: u64 = 1_000_000_000;
|
||||||
#[derive(thiserror::Error, Debug)]
|
#[derive(thiserror::Error, Debug)]
|
||||||
pub enum Error {}
|
pub enum Error {}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
|
#[serde(rename_all = "kebab-case")]
|
||||||
|
pub struct GenerateWallet {
|
||||||
|
account: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
|
#[serde(rename_all = "kebab-case")]
|
||||||
|
pub struct GetWalletAddress {}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
#[serde(rename_all = "kebab-case")]
|
#[serde(rename_all = "kebab-case")]
|
||||||
pub struct Transfer {
|
pub struct Transfer {
|
||||||
|
@ -45,6 +56,8 @@ pub struct Request {
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
#[serde(tag = "operation", content = "values", rename_all = "kebab-case")]
|
#[serde(tag = "operation", content = "values", rename_all = "kebab-case")]
|
||||||
pub enum Operation {
|
pub enum Operation {
|
||||||
|
GenerateWallet(GenerateWallet),
|
||||||
|
GetWalletAddress(GetWalletAddress),
|
||||||
Transfer(Transfer),
|
Transfer(Transfer),
|
||||||
Sign(Sign),
|
Sign(Sign),
|
||||||
}
|
}
|
||||||
|
@ -92,6 +105,21 @@ impl Module for Solana {
|
||||||
r#type: ArgumentType::Required,
|
r#type: ArgumentType::Required,
|
||||||
};
|
};
|
||||||
vec![
|
vec![
|
||||||
|
icepick_module::help::Operation {
|
||||||
|
name: "generate-wallet".to_string(),
|
||||||
|
description: "Generate the derivation index for a wallet.".to_string(),
|
||||||
|
arguments: vec![Argument {
|
||||||
|
name: "account".to_string(),
|
||||||
|
description: "The derivation account used for generating the wallet."
|
||||||
|
.to_string(),
|
||||||
|
r#type: ArgumentType::Optional,
|
||||||
|
}],
|
||||||
|
},
|
||||||
|
icepick_module::help::Operation {
|
||||||
|
name: "get-wallet-address".to_string(),
|
||||||
|
description: "Get the address for a given wallet.".to_string(),
|
||||||
|
arguments: vec![],
|
||||||
|
},
|
||||||
icepick_module::help::Operation {
|
icepick_module::help::Operation {
|
||||||
name: "transfer".to_string(),
|
name: "transfer".to_string(),
|
||||||
description: "Transfer SOL from a Keyfork wallet to an external wallet."
|
description: "Transfer SOL from a Keyfork wallet to an external wallet."
|
||||||
|
@ -142,6 +170,27 @@ impl Module for Solana {
|
||||||
|
|
||||||
fn handle_request(request: Self::Request) -> Result<serde_json::Value, Self::Error> {
|
fn handle_request(request: Self::Request) -> Result<serde_json::Value, Self::Error> {
|
||||||
match request.operation {
|
match request.operation {
|
||||||
|
Operation::GenerateWallet(GenerateWallet { account }) => {
|
||||||
|
let account = u32::from_str(&account).expect("account index");
|
||||||
|
Ok(serde_json::json!({
|
||||||
|
"blob": null,
|
||||||
|
"derivation-accounts": [(account | 1 << 31)],
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
Operation::GetWalletAddress(_) => {
|
||||||
|
use ed25519_dalek::{PublicKey, SecretKey};
|
||||||
|
// NOTE: panics if doesn't exist
|
||||||
|
let key = request.derived_keys.unwrap()[0];
|
||||||
|
let secret_key = SecretKey::from_bytes(&key).unwrap();
|
||||||
|
let mut bytes = [0u8; 64];
|
||||||
|
bytes[..32].clone_from_slice(&key);
|
||||||
|
bytes[32..].clone_from_slice(PublicKey::from(&secret_key).as_bytes());
|
||||||
|
let pk = solana_sdk::signer::keypair::Keypair::from_bytes(&bytes).unwrap();
|
||||||
|
let pk = pk.pubkey();
|
||||||
|
Ok(serde_json::json!({
|
||||||
|
"blob": pk.to_string(),
|
||||||
|
}))
|
||||||
|
}
|
||||||
Operation::Transfer(Transfer {
|
Operation::Transfer(Transfer {
|
||||||
amount,
|
amount,
|
||||||
from_account,
|
from_account,
|
||||||
|
@ -167,7 +216,10 @@ impl Module for Solana {
|
||||||
match (&fee_payer, &fee_payer_address) {
|
match (&fee_payer, &fee_payer_address) {
|
||||||
(Some(payer), Some(address)) => {
|
(Some(payer), Some(address)) => {
|
||||||
// Use the provided account
|
// Use the provided account
|
||||||
Some((u32::from_str(payer).unwrap(), Pubkey::from_str(address).unwrap()))
|
Some((
|
||||||
|
u32::from_str(payer).unwrap(),
|
||||||
|
Pubkey::from_str(address).unwrap(),
|
||||||
|
))
|
||||||
}
|
}
|
||||||
(None, None) => {
|
(None, None) => {
|
||||||
// Use the transaction account
|
// Use the transaction account
|
||||||
|
@ -186,7 +238,9 @@ impl Module for Solana {
|
||||||
);
|
);
|
||||||
let transaction = solana_sdk::transaction::Transaction::new_unsigned(message);
|
let transaction = solana_sdk::transaction::Transaction::new_unsigned(message);
|
||||||
// TODO: error handling from_str
|
// TODO: error handling from_str
|
||||||
let from_account = from_account.and_then(|a| u32::from_str(&a).ok()).unwrap_or(0);
|
let from_account = from_account
|
||||||
|
.and_then(|a| u32::from_str(&a).ok())
|
||||||
|
.unwrap_or(0);
|
||||||
let mut requested_accounts = vec![];
|
let mut requested_accounts = vec![];
|
||||||
requested_accounts.push(from_account | 1 << 31);
|
requested_accounts.push(from_account | 1 << 31);
|
||||||
if let Some((account, _)) = &payer_account_and_pk {
|
if let Some((account, _)) = &payer_account_and_pk {
|
||||||
|
@ -197,11 +251,11 @@ impl Module for Solana {
|
||||||
"derivation-accounts": requested_accounts,
|
"derivation-accounts": requested_accounts,
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
Operation::Sign(Sign {}) => {
|
Operation::Sign(_) => {
|
||||||
let blob = request.blob.expect("passed in instruction blob");
|
let blob = request.blob.expect("passed in instruction blob");
|
||||||
let transaction: solana_sdk::transaction::Transaction =
|
let _transaction: solana_sdk::transaction::Transaction =
|
||||||
serde_json::from_value(blob).expect("valid message blob");
|
serde_json::from_value(blob).expect("valid message blob");
|
||||||
let keys = request.derived_keys.unwrap_or_default();
|
let _keys = request.derived_keys.unwrap_or_default();
|
||||||
Ok(serde_json::json!({
|
Ok(serde_json::json!({
|
||||||
"blob": []
|
"blob": []
|
||||||
}))
|
}))
|
||||||
|
|
|
@ -8,7 +8,7 @@ use std::{
|
||||||
|
|
||||||
pub fn get_command(bin_name: &str) -> (&str, Vec<&str>) {
|
pub fn get_command(bin_name: &str) -> (&str, Vec<&str>) {
|
||||||
if std::env::vars().any(|(k, _)| &k == "ICEPICK_USE_CARGO") {
|
if std::env::vars().any(|(k, _)| &k == "ICEPICK_USE_CARGO") {
|
||||||
("cargo", vec!["run", "--bin", bin_name, "--"])
|
("cargo", vec!["run", "-q", "--bin", bin_name, "--"])
|
||||||
} else {
|
} else {
|
||||||
(bin_name, vec![])
|
(bin_name, vec![])
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue