light_program_test/utils/
load_accounts.rs1use std::{collections::HashMap, fs, path::PathBuf};
2
3use light_client::rpc::RpcError;
4use serde::{Deserialize, Serialize};
5use solana_sdk::{account::Account, pubkey::Pubkey};
6
7#[derive(Debug, Serialize, Deserialize)]
8struct AccountData {
9 pubkey: String,
10 account: AccountInfo,
11}
12
13#[derive(Debug, Serialize, Deserialize)]
14struct AccountInfo {
15 lamports: u64,
16 data: (String, String), owner: String,
18 executable: bool,
19 #[serde(rename = "rentEpoch")]
20 rent_epoch: u64,
21}
22
23pub fn find_accounts_dir() -> Option<PathBuf> {
24 #[cfg(not(feature = "devenv"))]
25 {
26 use std::process::Command;
27 let output = Command::new("which")
28 .arg("light")
29 .output()
30 .expect("Failed to execute 'which light'");
31
32 if !output.status.success() {
33 return None;
34 }
35
36 let light_path = String::from_utf8_lossy(&output.stdout).trim().to_string();
37 let mut light_bin_path = PathBuf::from(light_path);
38 light_bin_path.pop();
39
40 let accounts_dir =
41 light_bin_path.join("../lib/node_modules/@lightprotocol/zk-compression-cli/accounts");
42
43 Some(accounts_dir.canonicalize().unwrap_or(accounts_dir))
44 }
45 #[cfg(feature = "devenv")]
46 {
47 println!("Use only in light protocol monorepo. Using 'git rev-parse --show-toplevel' to find the accounts directory");
48 let light_protocol_toplevel = String::from_utf8_lossy(
49 &std::process::Command::new("git")
50 .arg("rev-parse")
51 .arg("--show-toplevel")
52 .output()
53 .expect("Failed to get top-level directory")
54 .stdout,
55 )
56 .trim()
57 .to_string();
58
59 let accounts_path = PathBuf::from(format!("{}/cli/accounts/", light_protocol_toplevel));
62 Some(accounts_path)
63 }
64}
65
66pub fn load_all_accounts_from_dir() -> Result<HashMap<Pubkey, Account>, RpcError> {
69 let accounts_dir = find_accounts_dir().ok_or_else(|| {
70 RpcError::CustomError(
71 "Failed to find accounts directory. Make sure light CLI is installed.".to_string(),
72 )
73 })?;
74
75 let mut accounts = HashMap::new();
76
77 let entries = fs::read_dir(&accounts_dir).map_err(|e| {
78 RpcError::CustomError(format!(
79 "Failed to read accounts directory at {:?}: {}",
80 accounts_dir, e
81 ))
82 })?;
83
84 for entry in entries {
85 let entry = entry
86 .map_err(|e| RpcError::CustomError(format!("Failed to read directory entry: {}", e)))?;
87 let path = entry.path();
88
89 if path.extension().and_then(|s| s.to_str()) == Some("json") {
90 let contents = fs::read_to_string(&path).map_err(|e| {
91 RpcError::CustomError(format!("Failed to read file {:?}: {}", path, e))
92 })?;
93
94 let account_data: AccountData = serde_json::from_str(&contents).map_err(|e| {
95 RpcError::CustomError(format!(
96 "Failed to parse account JSON from {:?}: {}",
97 path, e
98 ))
99 })?;
100
101 let pubkey = account_data
102 .pubkey
103 .parse::<Pubkey>()
104 .map_err(|e| RpcError::CustomError(format!("Invalid pubkey: {}", e)))?;
105
106 let owner = account_data
107 .account
108 .owner
109 .parse::<Pubkey>()
110 .map_err(|e| RpcError::CustomError(format!("Invalid owner pubkey: {}", e)))?;
111
112 let data = if account_data.account.data.1 == "base64" {
114 use base64::{engine::general_purpose, Engine as _};
115 general_purpose::STANDARD
116 .decode(&account_data.account.data.0)
117 .map_err(|e| {
118 RpcError::CustomError(format!("Failed to decode base64 data: {}", e))
119 })?
120 } else {
121 return Err(RpcError::CustomError(format!(
122 "Unsupported encoding: {}",
123 account_data.account.data.1
124 )));
125 };
126
127 let account = Account {
128 lamports: account_data.account.lamports,
129 data,
130 owner,
131 executable: account_data.account.executable,
132 rent_epoch: account_data.account.rent_epoch,
133 };
134
135 accounts.insert(pubkey, account);
136 }
137 }
138
139 Ok(accounts)
140}