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 base64::decode(&account_data.account.data.0).map_err(|e| {
115 RpcError::CustomError(format!("Failed to decode base64 data: {}", e))
116 })?
117 } else {
118 return Err(RpcError::CustomError(format!(
119 "Unsupported encoding: {}",
120 account_data.account.data.1
121 )));
122 };
123
124 let account = Account {
125 lamports: account_data.account.lamports,
126 data,
127 owner,
128 executable: account_data.account.executable,
129 rent_epoch: account_data.account.rent_epoch,
130 };
131
132 accounts.insert(pubkey, account);
133 }
134 }
135
136 Ok(accounts)
137}
138
139pub fn load_account_from_dir(pubkey: &Pubkey, prefix: Option<&str>) -> Result<Account, RpcError> {
142 let accounts_dir = find_accounts_dir().ok_or_else(|| {
143 RpcError::CustomError(
144 "Failed to find accounts directory. Make sure light CLI is installed.".to_string(),
145 )
146 })?;
147
148 let filename = if let Some(prefix) = prefix {
149 format!("{}_{}.json", prefix, pubkey)
150 } else {
151 format!("{}.json", pubkey)
152 };
153 let path = accounts_dir.join(&filename);
154
155 let contents = fs::read_to_string(&path).map_err(|e| {
156 RpcError::CustomError(format!("Failed to read account file {:?}: {}", path, e))
157 })?;
158
159 let account_data: AccountData = serde_json::from_str(&contents).map_err(|e| {
160 RpcError::CustomError(format!(
161 "Failed to parse account JSON from {:?}: {}",
162 path, e
163 ))
164 })?;
165
166 let owner = account_data
167 .account
168 .owner
169 .parse::<Pubkey>()
170 .map_err(|e| RpcError::CustomError(format!("Invalid owner pubkey: {}", e)))?;
171
172 let data = if account_data.account.data.1 == "base64" {
174 base64::decode(&account_data.account.data.0)
175 .map_err(|e| RpcError::CustomError(format!("Failed to decode base64 data: {}", e)))?
176 } else {
177 return Err(RpcError::CustomError(format!(
178 "Unsupported encoding: {}",
179 account_data.account.data.1
180 )));
181 };
182
183 Ok(Account {
184 lamports: account_data.account.lamports,
185 data,
186 owner,
187 executable: account_data.account.executable,
188 rent_epoch: account_data.account.rent_epoch,
189 })
190}