1use anyhow::{Context, Result};
2use solana_sdk::{
3 pubkey::Pubkey,
4 signature::Keypair,
5 signer::Signer,
6};
7use std::str::FromStr;
8
9const DEFAULT_PROGRAM_ID: &str = "PercQhVBxXnVCaAhfrPZFc2dVZcQANnwEYroogLJFwm";
10const DEFAULT_RPC_URL: &str = "https://api.devnet.solana.com";
11
12pub struct ChainConfig {
13 pub rpc_url: String,
14 pub keypair: Keypair,
15 pub program_id: Pubkey,
16}
17
18impl ChainConfig {
19 pub fn new(
20 rpc_url: Option<&str>,
21 keypair_path: Option<&str>,
22 program_id: Option<&str>,
23 ) -> Result<Self> {
24 let rpc_url = match rpc_url {
25 Some("devnet") => DEFAULT_RPC_URL.to_string(),
26 Some("mainnet") => "https://api.mainnet-beta.solana.com".to_string(),
27 Some("localhost" | "local") => "http://127.0.0.1:8899".to_string(),
28 Some(url) => url.to_string(),
29 None => DEFAULT_RPC_URL.to_string(),
30 };
31
32 let keypair_path = keypair_path
33 .map(|s| s.to_string())
34 .unwrap_or_else(|| {
35 let home = std::env::var("HOME").unwrap_or_default();
36 format!("{home}/.config/solana/id.json")
37 });
38
39 let keypair_bytes = std::fs::read(&keypair_path)
40 .with_context(|| format!("Failed to read keypair from {keypair_path}"))?;
41 let keypair_json: Vec<u8> = serde_json::from_slice(&keypair_bytes)
42 .with_context(|| "Invalid keypair JSON format")?;
43 let keypair = Keypair::try_from(keypair_json.as_slice())
44 .map_err(|e| anyhow::anyhow!("Invalid keypair bytes: {e}"))?;
45
46 let program_id = match program_id {
47 Some(id) => Pubkey::from_str(id)
48 .with_context(|| format!("Invalid program ID: {id}"))?,
49 None => Pubkey::from_str(DEFAULT_PROGRAM_ID).unwrap(),
50 };
51
52 Ok(Self {
53 rpc_url,
54 keypair,
55 program_id,
56 })
57 }
58
59 pub fn authority(&self) -> Pubkey {
60 self.keypair.pubkey()
61 }
62
63 pub fn market_pda(&self) -> (Pubkey, u8) {
64 Pubkey::find_program_address(
65 &[b"market", self.authority().as_ref()],
66 &self.program_id,
67 )
68 }
69}