add wallet generation to solana

This commit is contained in:
Ryan Heywood 2024-11-26 17:06:58 -05:00
parent 10cda7824c
commit e28047de92
Signed by: ryan
GPG Key ID: 8E401478A3FBEF72
4 changed files with 62 additions and 6 deletions

1
Cargo.lock generated
View File

@ -1067,6 +1067,7 @@ dependencies = [
name = "icepick-solana"
version = "0.1.0"
dependencies = [
"ed25519-dalek 1.0.1",
"icepick-module",
"serde",
"serde_json",

View File

@ -4,6 +4,7 @@ version = "0.1.0"
edition = "2021"
[dependencies]
ed25519-dalek = "=1.0.1"
icepick-module = { version = "0.1.0", path = "../../icepick-module" }
serde = { workspace = true, features = ["derive"] }
serde_json.workspace = true

View File

@ -3,6 +3,7 @@ use icepick_module::{
Module,
};
use serde::{Deserialize, Serialize};
use solana_sdk::signer::Signer;
use std::str::FromStr;
// 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)]
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)]
#[serde(rename_all = "kebab-case")]
pub struct Transfer {
@ -45,6 +56,8 @@ pub struct Request {
#[derive(Serialize, Deserialize, Debug)]
#[serde(tag = "operation", content = "values", rename_all = "kebab-case")]
pub enum Operation {
GenerateWallet(GenerateWallet),
GetWalletAddress(GetWalletAddress),
Transfer(Transfer),
Sign(Sign),
}
@ -92,6 +105,21 @@ impl Module for Solana {
r#type: ArgumentType::Required,
};
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 {
name: "transfer".to_string(),
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> {
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 {
amount,
from_account,
@ -167,7 +216,10 @@ impl Module for Solana {
match (&fee_payer, &fee_payer_address) {
(Some(payer), Some(address)) => {
// 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) => {
// Use the transaction account
@ -186,7 +238,9 @@ impl Module for Solana {
);
let transaction = solana_sdk::transaction::Transaction::new_unsigned(message);
// 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![];
requested_accounts.push(from_account | 1 << 31);
if let Some((account, _)) = &payer_account_and_pk {
@ -197,11 +251,11 @@ impl Module for Solana {
"derivation-accounts": requested_accounts,
}))
}
Operation::Sign(Sign {}) => {
Operation::Sign(_) => {
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");
let keys = request.derived_keys.unwrap_or_default();
let _keys = request.derived_keys.unwrap_or_default();
Ok(serde_json::json!({
"blob": []
}))

View File

@ -8,7 +8,7 @@ use std::{
pub fn get_command(bin_name: &str) -> (&str, Vec<&str>) {
if std::env::vars().any(|(k, _)| &k == "ICEPICK_USE_CARGO") {
("cargo", vec!["run", "--bin", bin_name, "--"])
("cargo", vec!["run", "-q", "--bin", bin_name, "--"])
} else {
(bin_name, vec![])
}