refactor to make kebab-case (almost) entirely an Icepick thing rather than a thing per-interface

This commit is contained in:
Ryan Heywood 2024-12-19 15:44:10 -05:00
parent d7e61b4ae1
commit a18282d107
Signed by: ryan
GPG Key ID: 8E401478A3FBEF72
3 changed files with 84 additions and 42 deletions

View File

@ -106,29 +106,24 @@ impl std::fmt::Display for Cluster {
} }
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Debug)]
#[serde(rename_all = "kebab-case")]
pub struct GetBlockhash { pub struct GetBlockhash {
cluster: Option<Cluster>, cluster: Option<Cluster>,
} }
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Debug)]
#[serde(rename_all = "kebab-case")]
pub struct GenerateWallet { pub struct GenerateWallet {
account: Option<String>, account: Option<String>,
} }
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Debug)]
#[serde(rename_all = "kebab-case")]
pub struct GetWalletAddress {} pub struct GetWalletAddress {}
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Debug)]
#[serde(rename_all = "kebab-case")]
pub struct GetTokenInfo { pub struct GetTokenInfo {
token: String, token: String,
} }
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Debug)]
#[serde(rename_all = "kebab-case")]
pub struct Transfer { pub struct Transfer {
amount: String, amount: String,
to_address: String, to_address: String,
@ -140,7 +135,6 @@ pub struct Transfer {
} }
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Debug)]
#[serde(rename_all = "kebab-case")]
pub struct CreateTokenAccount { pub struct CreateTokenAccount {
funder_address: Option<String>, funder_address: Option<String>,
wallet_address: String, wallet_address: String,
@ -148,7 +142,6 @@ pub struct CreateTokenAccount {
} }
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Debug)]
#[serde(rename_all = "kebab-case")]
pub struct TransferToken { pub struct TransferToken {
amount: String, amount: String,
token_address: String, token_address: String,
@ -162,19 +155,16 @@ pub struct TransferToken {
} }
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Debug)]
#[serde(rename_all = "kebab-case")]
pub struct Sign { pub struct Sign {
blockhash: String, blockhash: String,
} }
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Debug)]
#[serde(rename_all = "kebab-case")]
pub struct Broadcast { pub struct Broadcast {
cluster: Option<Cluster>, cluster: Option<Cluster>,
} }
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Debug)]
#[serde(rename_all = "kebab-case")]
pub struct Request { pub struct Request {
// NOTE: Can't use the proper XPrv type from Keyfork because Solana's a big stinky // NOTE: Can't use the proper XPrv type from Keyfork because Solana's a big stinky
// and adds in its own derivation constructs that cause type conflicts. // and adds in its own derivation constructs that cause type conflicts.
@ -188,7 +178,7 @@ 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")]
pub enum Operation { pub enum Operation {
GetBlockhash(GetBlockhash), GetBlockhash(GetBlockhash),
GenerateWallet(GenerateWallet), GenerateWallet(GenerateWallet),
@ -406,14 +396,16 @@ impl Module for Solana {
let client = solana_rpc_client::rpc_client::RpcClient::new(cluster_url); let client = solana_rpc_client::rpc_client::RpcClient::new(cluster_url);
let response = client.get_latest_blockhash().unwrap(); let response = client.get_latest_blockhash().unwrap();
Ok(serde_json::json!({ Ok(serde_json::json!({
"blob": response.to_string(), "blob": {
"blockhash": response.to_string(),
},
})) }))
} }
Operation::GenerateWallet(GenerateWallet { account }) => { Operation::GenerateWallet(GenerateWallet { account }) => {
let account = u32::from_str(account.as_deref().unwrap_or("0")).unwrap(); let account = u32::from_str(account.as_deref().unwrap_or("0")).unwrap();
Ok(serde_json::json!({ Ok(serde_json::json!({
"blob": null, "blob": {},
"derivation-accounts": [(account | 1 << 31)], "derivation_accounts": [(account | 1 << 31)],
})) }))
} }
Operation::GetWalletAddress(_) => { Operation::GetWalletAddress(_) => {
@ -422,7 +414,9 @@ impl Module for Solana {
let keypair = Self::keypair_from_bytes(key); let keypair = Self::keypair_from_bytes(key);
let pubkey = keypair.pubkey(); let pubkey = keypair.pubkey();
Ok(serde_json::json!({ Ok(serde_json::json!({
"blob": pubkey.to_string(), "blob": {
"pubkey": pubkey.to_string(),
}
})) }))
} }
Operation::GetTokenInfo(GetTokenInfo { token }) => { Operation::GetTokenInfo(GetTokenInfo { token }) => {
@ -442,7 +436,8 @@ impl Module for Solana {
} }
}), }),
None => serde_json::json!({ None => serde_json::json!({
"blob": null, "blob": {},
"error": "key was not found!",
}), }),
}) })
} }
@ -499,8 +494,10 @@ impl Module for Solana {
requested_accounts.push(*account | 1 << 31); requested_accounts.push(*account | 1 << 31);
} }
Ok(serde_json::json!({ Ok(serde_json::json!({
"blob": transaction, "blob": {
"derivation-accounts": requested_accounts, "transaction": transaction,
},
"derivation_accounts": requested_accounts,
})) }))
} }
Operation::CreateTokenAccount(CreateTokenAccount { Operation::CreateTokenAccount(CreateTokenAccount {
@ -528,8 +525,10 @@ impl Module for Solana {
let transaction = solana_sdk::transaction::Transaction::new_unsigned(message); let transaction = solana_sdk::transaction::Transaction::new_unsigned(message);
#[allow(clippy::identity_op)] #[allow(clippy::identity_op)]
Ok(serde_json::json!({ Ok(serde_json::json!({
"blob": transaction, "blob": {
"derivation-accounts": [0u32 | 1 << 31], "transaction": transaction,
},
"derivation_accounts": [0u32 | 1 << 31],
})) }))
} }
Operation::TransferToken(TransferToken { Operation::TransferToken(TransferToken {
@ -619,14 +618,19 @@ impl Module for Solana {
#[allow(clippy::identity_op)] #[allow(clippy::identity_op)]
Ok(serde_json::json!({ Ok(serde_json::json!({
"blob": transaction, "blob": {
"derivation-accounts": [0u32 | 1 << 31], "transaction": transaction,
},
"derivation_accounts": [0u32 | 1 << 31],
})) }))
} }
Operation::Sign(Sign { blockhash }) => { Operation::Sign(Sign { blockhash }) => {
let blob = request.blob.expect("passed in instruction blob"); let transaction = request
.blob
.and_then(|b| b.get("transaction").cloned())
.expect("was given transaction");
let mut transaction: solana_sdk::transaction::Transaction = let mut transaction: solana_sdk::transaction::Transaction =
serde_json::from_value(blob).expect("valid message blob"); serde_json::from_value(transaction).expect("valid message blob");
let keys = request let keys = request
.derived_keys .derived_keys
.unwrap_or_default() .unwrap_or_default()
@ -639,16 +643,21 @@ impl Module for Solana {
.try_sign(&keys, hash) .try_sign(&keys, hash)
.expect("not enough keys provided"); .expect("not enough keys provided");
Ok(serde_json::json!({ Ok(serde_json::json!({
"blob": transaction, "blob": {
"transaction": transaction,
}
})) }))
} }
Operation::Broadcast(Broadcast { cluster }) => { Operation::Broadcast(Broadcast { cluster }) => {
let cluster = cluster.unwrap_or(Cluster::MainnetBeta); let cluster = cluster.unwrap_or(Cluster::MainnetBeta);
let cluster_url = format!("https://api.{cluster}.solana.com"); let cluster_url = format!("https://api.{cluster}.solana.com");
let blob = request.blob.expect("passed in instruction blob"); let transaction = request
.blob
.and_then(|b| b.get("transaction").cloned())
.expect("was given transaction");
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(transaction).expect("valid message blob");
transaction.verify().expect("invalid signatures"); transaction.verify().expect("invalid signatures");
let client = solana_rpc_client::rpc_client::RpcClient::new(cluster_url); let client = solana_rpc_client::rpc_client::RpcClient::new(cluster_url);
let _simulated_response = client.simulate_transaction(&transaction).unwrap(); let _simulated_response = client.simulate_transaction(&transaction).unwrap();

View File

@ -124,7 +124,7 @@ pub fn do_cli_thing() {
let derivation_accounts = cli_input let derivation_accounts = cli_input
.as_ref() .as_ref()
.and_then(|json| json.get("derivation-accounts")); .and_then(|json| json.get("derivation_accounts"));
let matches = icepick_command.get_matches(); let matches = icepick_command.get_matches();
if let Some((module, matches)) = matches.subcommand() { if let Some((module, matches)) = matches.subcommand() {
@ -134,44 +134,77 @@ pub fn do_cli_thing() {
.find(|(name, ..)| *name == module) .find(|(name, ..)| *name == module)
.and_then(|(.., operations)| operations.iter().find(|o| o.name == subcommand)) .and_then(|(.., operations)| operations.iter().find(|o| o.name == subcommand))
{ {
let mut args = std::collections::HashMap::<&String, Option<&String>>::with_capacity( let mut args = std::collections::HashMap::<String, Option<&String>>::with_capacity(
operation.arguments.len(), operation.arguments.len(),
); );
for arg in &operation.arguments { for arg in &operation.arguments {
args.insert(&arg.name, matches.get_one::<String>(&arg.name)); args.insert(
arg.name.replace('-', "_"),
matches.get_one::<String>(&arg.name),
);
} }
let (algo, path) = config.modules.iter().find_map(|fmodule| { let (algo, path) = config
if fmodule.name == module { .modules
return Some((fmodule.algorithm.clone(), fmodule.derivation_prefix.clone())) .iter()
} .find_map(|fmodule| {
None if fmodule.name == module {
}).unwrap(); return Some((
fmodule.algorithm.clone(),
fmodule.derivation_prefix.clone(),
));
}
None
})
.unwrap();
let mut derived_keys: Vec<Vec<u8>> = vec![]; let mut derived_keys: Vec<Vec<u8>> = vec![];
if let Some(accounts) = derivation_accounts { if let Some(accounts) = derivation_accounts {
let accounts: Vec<keyfork_derive_util::DerivationIndex> = let accounts: Vec<keyfork_derive_util::DerivationIndex> =
serde_json::from_value(accounts.clone()) serde_json::from_value(accounts.clone())
.expect("valid derivation-accounts"); .expect("valid derivation_accounts");
let mut client = let mut client =
keyforkd_client::Client::discover_socket().expect("keyforkd started"); keyforkd_client::Client::discover_socket().expect("keyforkd started");
for account in accounts { for account in accounts {
let request = keyfork_derive_util::request::DerivationRequest::new(algo.clone(), &path.clone().chain_push(account)); let request = keyfork_derive_util::request::DerivationRequest::new(
algo.clone(),
&path.clone().chain_push(account),
);
let request = keyforkd_models::Request::Derivation(request); let request = keyforkd_models::Request::Derivation(request);
let response = client.request(&request).expect("valid derivation"); let response = client.request(&request).expect("valid derivation");
match response { match response {
keyforkd_models::Response::Derivation(keyfork_derive_util::request::DerivationResponse { data, .. }) => { keyforkd_models::Response::Derivation(
keyfork_derive_util::request::DerivationResponse { data, .. },
) => {
derived_keys.push(data.to_vec()); derived_keys.push(data.to_vec());
}, }
_ => panic!("Unexpected response"), _ => panic!("Unexpected response"),
} }
} }
} }
// in the event this is not PascalCase, this would be false.
// we set this to true to capitalize the first character.
let mut last_char_was_dash = true;
let subcommand = subcommand
.chars()
.filter_map(|c| {
if last_char_was_dash {
last_char_was_dash = false;
return Some(c.to_ascii_uppercase());
}
if c == '-' {
last_char_was_dash = true;
None
} else {
Some(c)
}
})
.collect::<String>();
let json = serde_json::json!({ let json = serde_json::json!({
"operation": subcommand, "operation": subcommand,
"values": args, "values": args,
"derived-keys": derived_keys, "derived_keys": derived_keys,
"blob": blob, "blob": blob,
}); });
let bin = commands let bin = commands

View File

@ -11,7 +11,7 @@ printf "%s" "Amount of token to transfer: "
read token_amount read token_amount
echo "Acquiring blockhash..." echo "Acquiring blockhash..."
blockhash="$(icepick sol get-blockhash --cluster devnet | jq -r .blob)" blockhash="$(icepick sol get-blockhash --cluster devnet | jq -r .blob.blockhash)"
echo "Saving information to file" echo "Saving information to file"