diff --git a/crates/by-chain/icepick-solana/src/lib.rs b/crates/by-chain/icepick-solana/src/lib.rs index 5de2762..04241e6 100644 --- a/crates/by-chain/icepick-solana/src/lib.rs +++ b/crates/by-chain/icepick-solana/src/lib.rs @@ -14,7 +14,27 @@ pub enum Error {} #[derive(Serialize, Deserialize, Debug)] #[serde(rename_all = "kebab-case")] -pub struct GetBlockhash {} +pub enum Cluster { + Devnet, + Testnet, + Mainnet, +} + +impl std::fmt::Display for Cluster { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Cluster::Devnet => f.write_str("devnet"), + Cluster::Testnet => f.write_str("testnet"), + Cluster::Mainnet => f.write_str("mainnet"), + } + } +} + +#[derive(Serialize, Deserialize, Debug)] +#[serde(rename_all = "kebab-case")] +pub struct GetBlockhash { + cluster: Option, +} #[derive(Serialize, Deserialize, Debug)] #[serde(rename_all = "kebab-case")] @@ -45,7 +65,9 @@ pub struct Sign {} #[derive(Serialize, Deserialize, Debug)] #[serde(rename_all = "kebab-case")] -pub struct Broadcast {} +pub struct Broadcast { + cluster: Option, +} #[derive(Serialize, Deserialize, Debug)] #[serde(rename_all = "kebab-case")] @@ -92,6 +114,11 @@ impl Module for Solana { type Request = Request; fn describe_operations() -> Vec { + let cluster = Argument { + name: "cluster".to_string(), + description: "The cluster to interact with (mainnet, testnet, devnet)".to_string(), + r#type: ArgumentType::Optional, + }; let account = Argument { name: "from-account".to_string(), description: "The derivation account used for the transaction.".to_string(), @@ -130,7 +157,7 @@ impl Module for Solana { icepick_module::help::Operation { name: "get-blockhash".to_string(), description: "Get the latest blockhash".to_string(), - arguments: vec![], + arguments: vec![cluster.clone()], }, icepick_module::help::Operation { name: "generate-wallet".to_string(), @@ -195,16 +222,17 @@ impl Module for Solana { icepick_module::help::Operation { name: "broadcast".to_string(), description: "Broadcast a signed transaction".to_string(), - arguments: vec![], + arguments: vec![cluster.clone()], }, ] } fn handle_request(request: Self::Request) -> Result { match request.operation { - Operation::GetBlockhash(_) => { - let devnet = "https://api.devnet.solana.com"; - let client = solana_rpc_client::rpc_client::RpcClient::new(devnet); + Operation::GetBlockhash(GetBlockhash { cluster }) => { + let cluster = cluster.unwrap_or(Cluster::Mainnet); + let cluster_url = format!("https://api.{cluster}.solana.com"); + let client = solana_rpc_client::rpc_client::RpcClient::new(cluster_url); let response = client.get_latest_blockhash().unwrap(); Ok(serde_json::json!({ "blob": response.to_string(), @@ -305,18 +333,18 @@ impl Module for Solana { "blob": transaction, })) } - Operation::Broadcast(_) => { + Operation::Broadcast(Broadcast { cluster }) => { + let cluster = cluster.unwrap_or(Cluster::Mainnet); + let cluster_url = format!("https://api.{cluster}.solana.com"); + let blob = request.blob.expect("passed in instruction blob"); let transaction: solana_sdk::transaction::Transaction = serde_json::from_value(blob).expect("valid message blob"); transaction.verify().expect("invalid signatures"); - // TODO: make this a CLI option - let devnet = "https://api.devnet.solana.com"; - let client = solana_rpc_client::rpc_client::RpcClient::new(devnet); + let client = solana_rpc_client::rpc_client::RpcClient::new(cluster_url); let _simulated_response = client.simulate_transaction(&transaction).unwrap(); let response = client.send_and_confirm_transaction(&transaction); - Ok( - match response { + Ok(match response { Ok(s) => { serde_json::json!({ "blob": {