1pub mod client;
37pub mod config;
38pub mod constants;
39pub mod deposit;
40pub mod deposit_spl;
41pub mod encryption;
42pub mod error;
43pub mod get_utxos;
44pub mod get_utxos_spl;
45pub mod keypair;
46pub mod merkle_tree;
47pub mod poseidon;
48pub mod prover;
49pub mod prover_rust;
50pub mod storage;
51pub mod utxo;
52pub mod utils;
53pub mod withdraw;
54pub mod withdraw_spl;
55
56pub use client::PrivacyCash;
58pub use config::{Config, SupportedToken};
59pub use constants::*;
60pub use error::{PrivacyCashError, Result};
61pub use keypair::ZkKeypair;
62pub use utxo::{Utxo, Balance, SplBalance};
63
64pub use solana_sdk::{
66 pubkey::Pubkey,
67 signature::{Keypair, Signer},
68};
69
70use std::str::FromStr;
75
76#[derive(Debug, Clone)]
78pub struct SendPrivatelyResult {
79 pub deposit_signature: String,
81 pub withdraw_signature: String,
83 pub amount_deposited: u64,
85 pub amount_received: u64,
87 pub total_fees: u64,
89 pub recipient: String,
91 pub token: String,
93}
94
95pub async fn send_privately(
131 private_key: &str,
132 recipient: &str,
133 amount: f64,
134 token: &str,
135 rpc_url: Option<&str>,
136) -> Result<SendPrivatelyResult> {
137 let key_bytes = bs58::decode(private_key)
139 .into_vec()
140 .map_err(|e| PrivacyCashError::InvalidInput(format!("Invalid private key: {}", e)))?;
141 let keypair = Keypair::from_bytes(&key_bytes)
142 .map_err(|e| PrivacyCashError::InvalidInput(format!("Invalid keypair: {}", e)))?;
143
144 let recipient_pubkey = Pubkey::from_str(recipient)
146 .map_err(|e| PrivacyCashError::InvalidInput(format!("Invalid recipient: {}", e)))?;
147
148 let rpc = rpc_url.unwrap_or("https://api.mainnet-beta.solana.com");
150 let client = PrivacyCash::new(rpc, keypair)?;
151
152 let token_lower = token.to_lowercase();
153
154 match token_lower.as_str() {
155 "sol" => {
156 let lamports = (amount * 1_000_000_000.0) as u64;
157
158 log::info!("Step 1/3: Depositing {} SOL...", amount);
160 let deposit_result = client.deposit(lamports).await?;
161 log::info!("Deposit TX: {}", deposit_result.signature);
162
163 log::info!("Step 2/3: Waiting for indexer (5 seconds)...");
165 tokio::time::sleep(tokio::time::Duration::from_secs(5)).await;
166
167 log::info!("Step 3/3: Withdrawing to recipient...");
169 let withdraw_result = client.withdraw_all(Some(&recipient_pubkey)).await?;
170 log::info!("Withdraw TX: {}", withdraw_result.signature);
171
172 Ok(SendPrivatelyResult {
173 deposit_signature: deposit_result.signature,
174 withdraw_signature: withdraw_result.signature,
175 amount_deposited: lamports,
176 amount_received: withdraw_result.amount_in_lamports,
177 total_fees: lamports.saturating_sub(withdraw_result.amount_in_lamports),
178 recipient: recipient.to_string(),
179 token: "sol".to_string(),
180 })
181 }
182 "usdc" => {
183 let base_units = (amount * 1_000_000.0) as u64;
184
185 log::info!("Step 1/3: Depositing {} USDC...", amount);
187 let deposit_result = client.deposit_usdc(base_units).await?;
188 log::info!("Deposit TX: {}", deposit_result.signature);
189
190 log::info!("Step 2/3: Waiting for indexer (5 seconds)...");
192 tokio::time::sleep(tokio::time::Duration::from_secs(5)).await;
193
194 log::info!("Step 3/3: Withdrawing to recipient...");
196 let withdraw_result = client.withdraw_all_usdc(Some(&recipient_pubkey)).await?;
197 log::info!("Withdraw TX: {}", withdraw_result.signature);
198
199 Ok(SendPrivatelyResult {
200 deposit_signature: deposit_result.signature,
201 withdraw_signature: withdraw_result.signature,
202 amount_deposited: base_units,
203 amount_received: withdraw_result.base_units,
204 total_fees: base_units.saturating_sub(withdraw_result.base_units),
205 recipient: recipient.to_string(),
206 token: "usdc".to_string(),
207 })
208 }
209 "usdt" => {
210 let base_units = (amount * 1_000_000.0) as u64;
211
212 log::info!("Step 1/3: Depositing {} USDT...", amount);
214 let deposit_result = client.deposit_usdt(base_units).await?;
215 log::info!("Deposit TX: {}", deposit_result.signature);
216
217 log::info!("Step 2/3: Waiting for indexer (5 seconds)...");
219 tokio::time::sleep(tokio::time::Duration::from_secs(5)).await;
220
221 log::info!("Step 3/3: Withdrawing to recipient...");
223 let withdraw_result = client.withdraw_all_spl(&USDT_MINT, Some(&recipient_pubkey)).await?;
224 log::info!("Withdraw TX: {}", withdraw_result.signature);
225
226 Ok(SendPrivatelyResult {
227 deposit_signature: deposit_result.signature,
228 withdraw_signature: withdraw_result.signature,
229 amount_deposited: base_units,
230 amount_received: withdraw_result.base_units,
231 total_fees: base_units.saturating_sub(withdraw_result.base_units),
232 recipient: recipient.to_string(),
233 token: "usdt".to_string(),
234 })
235 }
236 _ => Err(PrivacyCashError::InvalidInput(format!(
237 "Unsupported token: {}. Use 'sol', 'usdc', or 'usdt'",
238 token
239 ))),
240 }
241}