1use std::fs;
2
3use crate::common::paths::path;
4use anyhow::{Result, anyhow};
5use serde::Deserialize;
6use solana_client::rpc_client::RpcClient;
7
8use solana_sdk::{
9 program_pack::Pack,
10 pubkey::Pubkey,
11 signer::{
12 Signer,
13 keypair::{Keypair, read_keypair_file},
14 },
15 system_instruction,
16 transaction::Transaction,
17};
18
19use spl_associated_token_account::{
20 get_associated_token_address, instruction::create_associated_token_account,
21};
22use spl_token::state::Mint;
23
24#[derive(Deserialize)]
25struct AppConfig {
26 json_rpc_url: String,
27 keypair_path: String,
28}
29
30pub struct SplForgeClient {
31 pub client: RpcClient,
32 pub signer: Keypair,
33}
34
35impl SplForgeClient {
36 pub fn new() -> Result<Self> {
37 let config_path = path().map_err(|e| anyhow!("Failed to get config path: {}", e))?;
38 let config_contents = fs::read_to_string(&config_path)
39 .map_err(|e| anyhow!("Failed to read config file at {:?}: {}", config_path, e))?;
40 let app_config: AppConfig = serde_json::from_str(&config_contents)
41 .map_err(|e| anyhow!("Failed to parse config file: {}", e))?;
42 let client = RpcClient::new(app_config.json_rpc_url);
43 let signer = read_keypair_file(&app_config.keypair_path).map_err(|e| {
44 anyhow!(
45 "Failed to read keypair file at {}: {}",
46 app_config.keypair_path,
47 e
48 )
49 })?;
50 Ok(Self { client, signer })
51 }
52
53 pub fn create_mint_account(
54 &self,
55 decimals: u8,
56 mint_authority: Pubkey,
57 freeze_authority: Option<Pubkey>,
58 ) -> Result<Pubkey> {
59 let mint_account = Keypair::new();
60 println!("Creating mint account: {}", mint_account.pubkey());
61 let mint_size = Mint::LEN;
63 println!("Mint account size: {}", mint_size);
64 let rent = self
65 .client
66 .get_minimum_balance_for_rent_exemption(mint_size)?;
67 println!("Rent exemption amount: {}", rent);
68
69 let create_account_ix = system_instruction::create_account(
70 &self.signer.pubkey(),
71 &mint_account.pubkey(),
72 rent,
73 mint_size as u64,
74 &spl_token::id(),
75 );
76
77 let initialize_mint_ix = spl_token::instruction::initialize_mint(
78 &spl_token::id(),
79 &mint_account.pubkey(),
80 &mint_authority,
81 freeze_authority.as_ref(),
82 decimals,
83 )?;
84
85 let recent_blockhash = self.client.get_latest_blockhash()?;
86 let transaction = Transaction::new_signed_with_payer(
87 &[create_account_ix, initialize_mint_ix],
88 Some(&self.signer.pubkey()),
89 &[&self.signer, &mint_account],
90 recent_blockhash,
91 );
92 self.client.send_and_confirm_transaction(&transaction)?;
93 Ok(mint_account.pubkey())
94 }
95
96 pub fn create_metadata_account(
97 &self,
98 mint: Pubkey,
99 name: String,
100 symbol: String,
101 uri: String,
102 is_mutable: bool,
103 ) -> Result<()> {
104 let _ = (mint, name, symbol, uri, is_mutable);
105 Err(anyhow!(
106 "metadata creation is temporarily disabled while dependency versions are being stabilized"
107 ))
108 }
109
110 pub fn mint_to(&self, mint: Pubkey, amount: u64) -> Result<()> {
111 let signer_pubkey = self.signer.pubkey();
112 let destination_ata = get_associated_token_address(&signer_pubkey, &mint);
113 let mut instructions = vec![];
114
115 if self.client.get_account(&destination_ata).is_err() {
117 instructions.push(create_associated_token_account(
118 &signer_pubkey,
119 &signer_pubkey,
120 &mint,
121 &spl_token::id(),
122 ));
123 }
124
125 let mint_account = self.client.get_account(&mint)?;
127 let mint_data = Mint::unpack(&mint_account.data)?;
128 let scaled_amount = amount * 10_u64.pow(mint_data.decimals as u32);
129
130 instructions.push(spl_token::instruction::mint_to(
131 &spl_token::id(),
132 &mint,
133 &destination_ata,
134 &signer_pubkey,
135 &[],
136 scaled_amount,
137 )?);
138
139 let recent_blockhash = self.client.get_latest_blockhash()?;
140 let transaction = Transaction::new_signed_with_payer(
141 &instructions,
142 Some(&signer_pubkey),
143 &[&self.signer],
144 recent_blockhash,
145 );
146 self.client.send_and_confirm_transaction(&transaction)?;
147 Ok(())
148 }
149
150 pub fn revoke_mint_authority(&self, mint: Pubkey) -> Result<()> {
151 let signer_pubkey = self.signer.pubkey();
152 let revoke_ix = spl_token::instruction::set_authority(
153 &spl_token::id(),
154 &mint,
155 None,
156 spl_token::instruction::AuthorityType::MintTokens,
157 &signer_pubkey,
158 &[&signer_pubkey],
159 )?;
160
161 let recent_blockhash = self.client.get_latest_blockhash()?;
162 let transaction = Transaction::new_signed_with_payer(
163 &[revoke_ix],
164 Some(&signer_pubkey),
165 &[&self.signer],
166 recent_blockhash,
167 );
168 self.client.send_and_confirm_transaction(&transaction)?;
169 Ok(())
170 }
171}