From 6f94d665586be580b423485a474c658b99674b9d Mon Sep 17 00:00:00 2001 From: Sergey Timoshin Date: Sun, 26 Jan 2025 22:39:34 +0100 Subject: [PATCH] refactor: remove photon_photon client, add missing methods to indexer --- Cargo.lock | 10 + forester/Cargo.toml | 1 + forester/src/indexer_type.rs | 5 +- forester/src/lib.rs | 1 - forester/src/main.rs | 6 +- forester/src/photon_indexer.rs | 254 --------- forester/src/utils.rs | 7 - forester/tests/test_utils.rs | 16 +- .../compressed-token-test/tests/test.rs | 274 +++++++--- program-tests/system-cpi-test/tests/test.rs | 91 +++- program-tests/utils/src/e2e_test_env.rs | 10 +- program-tests/utils/src/spl.rs | 7 +- sdk-libs/client/Cargo.toml | 1 + sdk-libs/client/src/indexer/mod.rs | 259 ++++++++- sdk-libs/client/src/indexer/photon_indexer.rs | 512 ++++++++++++++++++ sdk-libs/client/src/lib.rs | 1 - sdk-libs/client/src/photon_rpc/error.rs | 48 -- sdk-libs/client/src/photon_rpc/mod.rs | 12 - sdk-libs/client/src/photon_rpc/models.rs | 133 ----- .../client/src/photon_rpc/photon_client.rs | 413 -------------- sdk-libs/client/src/photon_rpc/types.rs | 48 -- ...ccount_balance_post_200_response_result.rs | 4 +- sdk-libs/photon-api/src/models/account.rs | 16 +- .../photon-api/src/models/account_data.rs | 4 +- .../src/models/token_account_balance.rs | 4 +- .../photon-api/src/models/token_balance.rs | 4 +- sdk-libs/photon-api/src/models/token_data.rs | 4 +- sdk-libs/program-test/Cargo.toml | 2 + .../program-test/src/indexer/extensions.rs | 5 - .../program-test/src/indexer/test_indexer.rs | 197 ++++++- 30 files changed, 1259 insertions(+), 1090 deletions(-) delete mode 100644 forester/src/photon_indexer.rs create mode 100644 sdk-libs/client/src/indexer/photon_indexer.rs delete mode 100644 sdk-libs/client/src/photon_rpc/error.rs delete mode 100644 sdk-libs/client/src/photon_rpc/mod.rs delete mode 100644 sdk-libs/client/src/photon_rpc/models.rs delete mode 100644 sdk-libs/client/src/photon_rpc/photon_client.rs delete mode 100644 sdk-libs/client/src/photon_rpc/types.rs diff --git a/Cargo.lock b/Cargo.lock index e1040631a5..1fd7ad6e34 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -700,6 +700,12 @@ dependencies = [ "rustc-demangle", ] +[[package]] +name = "base64" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2015e3793554aa5b6007e3a72959e84c1070039e74f13dde08fa64afe1ddd892" + [[package]] name = "base64" version = "0.12.3" @@ -1954,6 +1960,7 @@ dependencies = [ "light-sdk", "light-system-program", "light-test-utils", + "log", "photon-api", "prometheus", "reqwest", @@ -2908,6 +2915,7 @@ name = "light-client" version = "0.9.1" dependencies = [ "async-trait", + "base64 0.13.1", "bb8", "borsh 0.10.3", "light-compressed-token", @@ -3080,6 +3088,7 @@ dependencies = [ "account-compression", "anchor-lang", "async-trait", + "base64 0.2.1", "borsh 0.10.3", "forester-utils", "light-batched-merkle-tree", @@ -3098,6 +3107,7 @@ dependencies = [ "log", "num-bigint 0.4.6", "num-traits", + "photon-api", "reqwest", "solana-banks-client", "solana-program-test", diff --git a/forester/Cargo.toml b/forester/Cargo.toml index 3857b85ba1..94857a1733 100644 --- a/forester/Cargo.toml +++ b/forester/Cargo.toml @@ -26,6 +26,7 @@ light-program-test = { workspace = true} solana-transaction-status = { workspace = true } url = "2.2" bb8 = { workspace = true } +log = { workspace = true } serde_json = "1.0" serde = { version = "1.0", features = ["derive"] } diff --git a/forester/src/indexer_type.rs b/forester/src/indexer_type.rs index 68d891d76a..2be5d85070 100644 --- a/forester/src/indexer_type.rs +++ b/forester/src/indexer_type.rs @@ -3,7 +3,9 @@ use std::{any::Any, sync::Arc}; use async_trait::async_trait; use forester_utils::forester_epoch::TreeAccounts; use light_client::{ - indexer::{Indexer, StateMerkleTreeAccounts, StateMerkleTreeBundle}, + indexer::{ + photon_indexer::PhotonIndexer, Indexer, StateMerkleTreeAccounts, StateMerkleTreeBundle, + }, rpc::RpcConnection, }; use light_hasher::Poseidon; @@ -17,7 +19,6 @@ use tracing::info; use crate::{ errors::ForesterError, - photon_indexer::PhotonIndexer, rollover::{perform_address_merkle_tree_rollover, perform_state_merkle_tree_rollover_forester}, ForesterConfig, }; diff --git a/forester/src/lib.rs b/forester/src/lib.rs index 25ea7e6c0e..59f0c2aceb 100644 --- a/forester/src/lib.rs +++ b/forester/src/lib.rs @@ -10,7 +10,6 @@ pub mod helius_priority_fee_types; mod indexer_type; pub mod metrics; pub mod pagerduty; -pub mod photon_indexer; pub mod pubsub_client; pub mod queue_helpers; pub mod rollover; diff --git a/forester/src/main.rs b/forester/src/main.rs index 51ae5fa3e7..0b99486472 100644 --- a/forester/src/main.rs +++ b/forester/src/main.rs @@ -6,12 +6,14 @@ use forester::{ errors::ForesterError, forester_status, metrics::register_metrics, - photon_indexer::PhotonIndexer, run_pipeline, telemetry::setup_telemetry, ForesterConfig, }; -use light_client::rpc::{RpcConnection, SolanaRpcConnection}; +use light_client::{ + indexer::photon_indexer::PhotonIndexer, + rpc::{RpcConnection, SolanaRpcConnection}, +}; use tokio::{ signal::ctrl_c, sync::{mpsc, oneshot}, diff --git a/forester/src/photon_indexer.rs b/forester/src/photon_indexer.rs deleted file mode 100644 index abbbf61b80..0000000000 --- a/forester/src/photon_indexer.rs +++ /dev/null @@ -1,254 +0,0 @@ -use std::fmt::Debug; - -use account_compression::initialize_address_merkle_tree::Pubkey; -use async_trait::async_trait; -use light_client::{ - indexer::{ - AddressMerkleTreeBundle, Indexer, IndexerError, LeafIndexInfo, MerkleProof, - NewAddressProofWithContext, ProofOfLeaf, - }, - rpc::RpcConnection, -}; -use light_sdk::proof::ProofRpcResult; -use photon_api::{ - apis::configuration::{ApiKey, Configuration}, - models::{AddressWithTree, GetCompressedAccountsByOwnerPostRequestParams}, -}; -use solana_sdk::bs58; -use tracing::debug; - -use crate::utils::decode_hash; - -pub struct PhotonIndexer { - configuration: Configuration, - #[allow(dead_code)] - rpc: R, -} - -impl PhotonIndexer { - pub fn new(path: String, api_key: Option, rpc: R) -> Self { - let configuration = Configuration { - base_path: path, - api_key: api_key.map(|key| ApiKey { - prefix: Some("api-key".to_string()), - key, - }), - ..Default::default() - }; - - PhotonIndexer { configuration, rpc } - } -} - -impl Debug for PhotonIndexer { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("PhotonIndexer") - .field("configuration", &self.configuration) - .finish() - } -} - -#[async_trait] -impl Indexer for PhotonIndexer { - async fn get_queue_elements( - &self, - _pubkey: [u8; 32], - _batch: u64, - _start_offset: u64, - _end_offset: u64, - ) -> Result, IndexerError> { - unimplemented!() - } - - fn get_subtrees(&self, _merkle_tree_pubkey: [u8; 32]) -> Result, IndexerError> { - unimplemented!() - } - - async fn create_proof_for_compressed_accounts( - &mut self, - _compressed_accounts: Option>, - _state_merkle_tree_pubkeys: Option>, - _new_addresses: Option<&[[u8; 32]]>, - _address_merkle_tree_pubkeys: Option>, - _rpc: &mut R, - ) -> ProofRpcResult { - todo!() - } - async fn get_multiple_compressed_account_proofs( - &self, - hashes: Vec, - ) -> Result, IndexerError> { - debug!("Getting proofs for {:?}", hashes); - let request: photon_api::models::GetMultipleCompressedAccountProofsPostRequest = - photon_api::models::GetMultipleCompressedAccountProofsPostRequest { - params: hashes, - ..Default::default() - }; - - let result = photon_api::apis::default_api::get_multiple_compressed_account_proofs_post( - &self.configuration, - request, - ) - .await; - - match result { - Ok(response) => { - match response.result { - Some(result) => { - let proofs = result - .value - .iter() - .map(|x| { - let mut proof_result_value = x.proof.clone(); - proof_result_value.truncate(proof_result_value.len() - 10); // Remove canopy - let proof: Vec<[u8; 32]> = - proof_result_value.iter().map(|x| decode_hash(x)).collect(); - MerkleProof { - hash: x.hash.clone(), - leaf_index: x.leaf_index, - merkle_tree: x.merkle_tree.clone(), - proof, - root_seq: x.root_seq, - } - }) - .collect(); - - Ok(proofs) - } - None => { - let error = response.error.unwrap(); - Err(IndexerError::Custom(error.message.unwrap())) - } - } - } - Err(e) => Err(IndexerError::Custom(e.to_string())), - } - } - - async fn get_compressed_accounts_by_owner( - &self, - owner: &Pubkey, - ) -> Result, IndexerError> { - let request = photon_api::models::GetCompressedAccountsByOwnerPostRequest { - params: Box::from(GetCompressedAccountsByOwnerPostRequestParams { - cursor: None, - data_slice: None, - filters: None, - limit: None, - owner: owner.to_string(), - }), - ..Default::default() - }; - - let result = photon_api::apis::default_api::get_compressed_accounts_by_owner_post( - &self.configuration, - request, - ) - .await - .unwrap(); - - let accs = result.result.unwrap().value; - let mut hashes = Vec::new(); - for acc in accs.items { - hashes.push(acc.hash); - } - - Ok(hashes) - } - - async fn get_multiple_new_address_proofs( - &self, - merkle_tree_pubkey: [u8; 32], - addresses: Vec<[u8; 32]>, - ) -> Result>, IndexerError> { - let params: Vec = addresses - .iter() - .map(|x| AddressWithTree { - address: bs58::encode(x).into_string(), - tree: bs58::encode(&merkle_tree_pubkey).into_string(), - }) - .collect(); - - let request = photon_api::models::GetMultipleNewAddressProofsV2PostRequest { - params, - ..Default::default() - }; - - debug!("Request: {:?}", request); - - let result = photon_api::apis::default_api::get_multiple_new_address_proofs_v2_post( - &self.configuration, - request, - ) - .await; - - debug!("Response: {:?}", result); - - if result.is_err() { - return Err(IndexerError::Custom(result.err().unwrap().to_string())); - } - - let photon_proofs = result.unwrap().result.unwrap().value; - // net height 16 = height(26) - canopy(10) - let mut proofs: Vec> = Vec::new(); - for photon_proof in photon_proofs { - let tree_pubkey = decode_hash(&photon_proof.merkle_tree); - let low_address_value = decode_hash(&photon_proof.lower_range_address); - let next_address_value = decode_hash(&photon_proof.higher_range_address); - let proof = NewAddressProofWithContext { - merkle_tree: tree_pubkey, - low_address_index: photon_proof.low_element_leaf_index as u64, - low_address_value, - low_address_next_index: photon_proof.next_index as u64, - low_address_next_value: next_address_value, - low_address_proof: { - let mut proof_vec: Vec<[u8; 32]> = photon_proof - .proof - .iter() - .map(|x: &String| decode_hash(x)) - .collect(); - proof_vec.truncate(proof_vec.len() - 10); // Remove canopy - let mut proof_arr = [[0u8; 32]; 16]; - proof_arr.copy_from_slice(&proof_vec); - proof_arr - }, - root: decode_hash(&photon_proof.root), - root_seq: photon_proof.root_seq, - new_low_element: None, - new_element: None, - new_element_next_value: None, - }; - proofs.push(proof); - } - - Ok(proofs) - } - - async fn get_multiple_new_address_proofs_h40( - &self, - _merkle_tree_pubkey: [u8; 32], - _addresses: Vec<[u8; 32]>, - ) -> Result>, IndexerError> { - unimplemented!() - } - - fn get_proofs_by_indices( - &mut self, - _merkle_tree_pubkey: Pubkey, - _indices: &[u64], - ) -> Vec { - todo!() - } - - fn get_leaf_indices_tx_hashes( - &mut self, - _merkle_tree_pubkey: Pubkey, - _zkp_batch_size: usize, - ) -> Vec { - todo!() - } - - fn get_address_merkle_trees(&self) -> &Vec { - todo!() - } -} diff --git a/forester/src/utils.rs b/forester/src/utils.rs index 449977116c..b77a81f216 100644 --- a/forester/src/utils.rs +++ b/forester/src/utils.rs @@ -7,13 +7,6 @@ use light_registry::{ }; use tracing::debug; -pub fn decode_hash(account: &str) -> [u8; 32] { - let bytes = bs58::decode(account).into_vec().unwrap(); - let mut arr = [0u8; 32]; - arr.copy_from_slice(&bytes); - arr -} - pub async fn get_protocol_config(rpc: &mut R) -> ProtocolConfig { let authority_pda = get_protocol_config_pda_address(); let protocol_config_account = rpc diff --git a/forester/tests/test_utils.rs b/forester/tests/test_utils.rs index 4cfbbc6ca5..c91d68dbc2 100644 --- a/forester/tests/test_utils.rs +++ b/forester/tests/test_utils.rs @@ -2,12 +2,14 @@ use account_compression::initialize_address_merkle_tree::Pubkey; use forester::{ config::{ExternalServicesConfig, GeneralConfig}, metrics::register_metrics, - photon_indexer::PhotonIndexer, telemetry::setup_telemetry, ForesterConfig, }; use light_client::{ - indexer::{Indexer, IndexerError, NewAddressProofWithContext}, + indexer::{ + photon_indexer::PhotonIndexer, Base58Conversions, Hash, Indexer, IndexerError, + NewAddressProofWithContext, + }, rpc::RpcConnection, }; use light_program_test::{indexer::TestIndexerExtensions, test_env::get_test_env_accounts}; @@ -223,14 +225,14 @@ pub async fn assert_account_proofs_for_photon_and_test_indexer< user_pubkey: &Pubkey, photon_indexer: &PhotonIndexer, ) { - let accs: Result, IndexerError> = + let accs: Result, IndexerError> = indexer.get_compressed_accounts_by_owner(user_pubkey).await; for account_hash in accs.unwrap() { let photon_result = photon_indexer - .get_multiple_compressed_account_proofs(vec![account_hash.clone()]) + .get_multiple_compressed_account_proofs(vec![account_hash.to_base58()]) .await; let test_indexer_result = indexer - .get_multiple_compressed_account_proofs(vec![account_hash.clone()]) + .get_multiple_compressed_account_proofs(vec![account_hash.to_base58()]) .await; if photon_result.is_err() { @@ -245,7 +247,9 @@ pub async fn assert_account_proofs_for_photon_and_test_indexer< let test_indexer_result = test_indexer_result.unwrap(); debug!( "assert proofs for account: {} photon result: {:?} test indexer result: {:?}", - account_hash, photon_result, test_indexer_result + account_hash.to_base58(), + photon_result, + test_indexer_result ); assert_eq!(photon_result.len(), test_indexer_result.len()); diff --git a/program-tests/compressed-token-test/tests/test.rs b/program-tests/compressed-token-test/tests/test.rs index 5a361c1696..c44d194e37 100644 --- a/program-tests/compressed-token-test/tests/test.rs +++ b/program-tests/compressed-token-test/tests/test.rs @@ -658,8 +658,10 @@ async fn test_wrapped_sol() { None, ) .await; - let input_compressed_accounts = - test_indexer.get_compressed_token_accounts_by_owner(&payer.pubkey()); + let input_compressed_accounts = test_indexer + .get_compressed_token_accounts_by_owner(&payer.pubkey(), None) + .await + .unwrap(); decompress_test( &payer, &mut rpc, @@ -1443,8 +1445,10 @@ async fn perform_transfer_22_test( for _ in 0..outputs { recipients.push(Pubkey::new_unique()); } - let input_compressed_accounts = - test_indexer.get_compressed_token_accounts_by_owner(&sender.pubkey()); + let input_compressed_accounts = test_indexer + .get_compressed_token_accounts_by_owner(&sender.pubkey(), None) + .await + .unwrap(); let equal_amount = (amount * inputs as u64) / outputs as u64; let rest_amount = (amount * inputs as u64) % outputs as u64; let mut output_amounts = vec![equal_amount; outputs - 1]; @@ -1522,8 +1526,10 @@ async fn test_decompression() { .await .unwrap(); println!("4"); - let input_compressed_account = - test_indexer.get_compressed_token_accounts_by_owner(&sender.pubkey()); + let input_compressed_account = test_indexer + .get_compressed_token_accounts_by_owner(&sender.pubkey(), None) + .await + .unwrap(); decompress_test( &sender, &mut context, @@ -1666,8 +1672,11 @@ async fn test_mint_to_and_burn_from_all_token_pools() { iterator }; for i in iterator { - let input_compressed_account = - test_indexer.get_compressed_token_accounts_by_owner(&payer.pubkey())[0].clone(); + let input_compressed_account = test_indexer + .get_compressed_token_accounts_by_owner(&payer.pubkey(), None) + .await + .unwrap()[0] + .clone(); let change_account_merkle_tree = input_compressed_account .compressed_account .merkle_context @@ -1762,7 +1771,9 @@ async fn test_multiple_decompression() { iterator.shuffle(rng); for i in iterator { let input_compressed_account = test_indexer - .get_compressed_token_accounts_by_owner(&sender.pubkey()) + .get_compressed_token_accounts_by_owner(&sender.pubkey(), None) + .await + .unwrap() .iter() .filter(|x| x.token_data.amount != 0) .collect::>()[0] @@ -1810,7 +1821,9 @@ async fn test_multiple_decompression() { // Decompress from all token pools { let input_compressed_accounts = test_indexer - .get_compressed_token_accounts_by_owner(&sender.pubkey())[0..4] + .get_compressed_token_accounts_by_owner(&sender.pubkey(), None) + .await + .unwrap()[0..4] .to_vec(); let amount = input_compressed_accounts .iter() @@ -1835,7 +1848,9 @@ async fn test_multiple_decompression() { ) .await; let input_compressed_accounts = test_indexer - .get_compressed_token_accounts_by_owner(&sender.pubkey()) + .get_compressed_token_accounts_by_owner(&sender.pubkey(), None) + .await + .unwrap() .iter() .filter(|x| x.token_data.amount != 0) .collect::>()[0] @@ -1905,8 +1920,10 @@ async fn test_delegation( .await; // 1. Delegate tokens { - let input_compressed_accounts = - test_indexer.get_compressed_token_accounts_by_owner(&sender.pubkey()); + let input_compressed_accounts = test_indexer + .get_compressed_token_accounts_by_owner(&sender.pubkey(), None) + .await + .unwrap(); let delegated_compressed_account_merkle_tree = input_compressed_accounts[0] .compressed_account .merkle_context @@ -1929,8 +1946,10 @@ async fn test_delegation( let recipient = Pubkey::new_unique(); // 2. Transfer partial delegated amount { - let input_compressed_accounts = - test_indexer.get_compressed_token_accounts_by_owner(&sender.pubkey()); + let input_compressed_accounts = test_indexer + .get_compressed_token_accounts_by_owner(&sender.pubkey(), None) + .await + .unwrap(); let input_compressed_accounts = input_compressed_accounts .iter() .filter(|x| x.token_data.delegate.is_some()) @@ -1955,8 +1974,10 @@ async fn test_delegation( } // 3. Transfer full delegated amount { - let input_compressed_accounts = - test_indexer.get_compressed_token_accounts_by_owner(&sender.pubkey()); + let input_compressed_accounts = test_indexer + .get_compressed_token_accounts_by_owner(&sender.pubkey(), None) + .await + .unwrap(); let input_compressed_accounts = input_compressed_accounts .iter() .filter(|x| x.token_data.delegate.is_some()) @@ -2039,8 +2060,10 @@ async fn test_delegation_mixed() { .await; // 1. Delegate tokens { - let input_compressed_accounts = - test_indexer.get_compressed_token_accounts_by_owner(&sender.pubkey()); + let input_compressed_accounts = test_indexer + .get_compressed_token_accounts_by_owner(&sender.pubkey(), None) + .await + .unwrap(); let delegated_compressed_account_merkle_tree = input_compressed_accounts[0] .compressed_account .merkle_context @@ -2063,15 +2086,19 @@ async fn test_delegation_mixed() { let recipient = Pubkey::new_unique(); // 2. Transfer partial delegated amount with delegate change account { - let input_compressed_accounts = - test_indexer.get_compressed_token_accounts_by_owner(&sender.pubkey()); + let input_compressed_accounts = test_indexer + .get_compressed_token_accounts_by_owner(&sender.pubkey(), None) + .await + .unwrap(); let mut input_compressed_accounts = input_compressed_accounts .iter() .filter(|x| x.token_data.delegate.is_some()) .cloned() .collect::>(); - let delegate_input_compressed_accounts = - test_indexer.get_compressed_token_accounts_by_owner(&delegate.pubkey()); + let delegate_input_compressed_accounts = test_indexer + .get_compressed_token_accounts_by_owner(&delegate.pubkey(), None) + .await + .unwrap(); input_compressed_accounts .extend_from_slice(&[delegate_input_compressed_accounts[0].clone()]); let delegate_lamports = delegate_input_compressed_accounts[0] @@ -2102,15 +2129,19 @@ async fn test_delegation_mixed() { let recipient = Pubkey::new_unique(); // 3. Transfer partial delegated amount without delegate change account { - let input_compressed_accounts = - test_indexer.get_compressed_token_accounts_by_owner(&sender.pubkey()); + let input_compressed_accounts = test_indexer + .get_compressed_token_accounts_by_owner(&sender.pubkey(), None) + .await + .unwrap(); let mut input_compressed_accounts = input_compressed_accounts .iter() .filter(|x| x.token_data.delegate.is_some()) .cloned() .collect::>(); - let delegate_input_compressed_accounts = - test_indexer.get_compressed_token_accounts_by_owner(&delegate.pubkey()); + let delegate_input_compressed_accounts = test_indexer + .get_compressed_token_accounts_by_owner(&delegate.pubkey(), None) + .await + .unwrap(); input_compressed_accounts .extend_from_slice(&[delegate_input_compressed_accounts[0].clone()]); let delegate_input_amount = input_compressed_accounts @@ -2143,15 +2174,19 @@ async fn test_delegation_mixed() { } // 3. Transfer full delegated amount { - let input_compressed_accounts = - test_indexer.get_compressed_token_accounts_by_owner(&sender.pubkey()); + let input_compressed_accounts = test_indexer + .get_compressed_token_accounts_by_owner(&sender.pubkey(), None) + .await + .unwrap(); let mut input_compressed_accounts = input_compressed_accounts .iter() .filter(|x| x.token_data.delegate.is_some()) .cloned() .collect::>(); - let delegate_input_compressed_accounts = - test_indexer.get_compressed_token_accounts_by_owner(&delegate.pubkey()); + let delegate_input_compressed_accounts = test_indexer + .get_compressed_token_accounts_by_owner(&delegate.pubkey(), None) + .await + .unwrap(); input_compressed_accounts.extend_from_slice(&delegate_input_compressed_accounts); let input_amount = input_compressed_accounts @@ -2254,8 +2289,10 @@ async fn test_approve_failing() { ) .await; - let input_compressed_accounts = - test_indexer.get_compressed_token_accounts_by_owner(&sender.pubkey()); + let input_compressed_accounts = test_indexer + .get_compressed_token_accounts_by_owner(&sender.pubkey(), None) + .await + .unwrap(); let delegated_amount = 1000u64; let delegated_compressed_account_merkle_tree = input_compressed_accounts[0] .compressed_account @@ -2552,8 +2589,10 @@ async fn test_revoke(num_inputs: usize, mint_amount: u64, delegated_amount: u64) .await; // 1. Delegate tokens { - let input_compressed_accounts = - test_indexer.get_compressed_token_accounts_by_owner(&sender.pubkey()); + let input_compressed_accounts = test_indexer + .get_compressed_token_accounts_by_owner(&sender.pubkey(), None) + .await + .unwrap(); for input in input_compressed_accounts.iter() { let input_compressed_accounts = vec![input.clone()]; let delegated_compressed_account_merkle_tree = input_compressed_accounts[0] @@ -2578,7 +2617,9 @@ async fn test_revoke(num_inputs: usize, mint_amount: u64, delegated_amount: u64) // 2. Revoke { let input_compressed_accounts = test_indexer - .get_compressed_token_accounts_by_owner(&sender.pubkey()) + .get_compressed_token_accounts_by_owner(&sender.pubkey(), None) + .await + .unwrap() .iter() .filter(|x| x.token_data.delegate.is_some()) .cloned() @@ -2672,8 +2713,10 @@ async fn test_revoke_failing() { .await; // Delegate tokens { - let input_compressed_accounts = - test_indexer.get_compressed_token_accounts_by_owner(&sender.pubkey()); + let input_compressed_accounts = test_indexer + .get_compressed_token_accounts_by_owner(&sender.pubkey(), None) + .await + .unwrap(); let delegated_amount = 1000u64; let delegated_compressed_account_merkle_tree = input_compressed_accounts[0] .compressed_account @@ -2694,8 +2737,10 @@ async fn test_revoke_failing() { .await; } - let input_compressed_accounts = - test_indexer.get_compressed_token_accounts_by_owner(&sender.pubkey()); + let input_compressed_accounts = test_indexer + .get_compressed_token_accounts_by_owner(&sender.pubkey(), None) + .await + .unwrap(); let input_compressed_accounts = input_compressed_accounts .iter() .filter(|x| x.token_data.delegate.is_some()) @@ -2898,8 +2943,10 @@ async fn test_burn() { .await; // 1. Burn tokens { - let input_compressed_accounts = - test_indexer.get_compressed_token_accounts_by_owner(&sender.pubkey()); + let input_compressed_accounts = test_indexer + .get_compressed_token_accounts_by_owner(&sender.pubkey(), None) + .await + .unwrap(); let burn_amount = 1000u64; let change_account_merkle_tree = input_compressed_accounts[0] .compressed_account @@ -2921,8 +2968,10 @@ async fn test_burn() { } // 2. Delegate tokens { - let input_compressed_accounts = - test_indexer.get_compressed_token_accounts_by_owner(&sender.pubkey()); + let input_compressed_accounts = test_indexer + .get_compressed_token_accounts_by_owner(&sender.pubkey(), None) + .await + .unwrap(); let delegated_amount = 1000u64; let delegated_compressed_account_merkle_tree = input_compressed_accounts[0] .compressed_account @@ -2944,8 +2993,10 @@ async fn test_burn() { } // 3. Burn delegated tokens { - let input_compressed_accounts = - test_indexer.get_compressed_token_accounts_by_owner(&sender.pubkey()); + let input_compressed_accounts = test_indexer + .get_compressed_token_accounts_by_owner(&sender.pubkey(), None) + .await + .unwrap(); let input_compressed_accounts = input_compressed_accounts .iter() .filter(|x| x.token_data.delegate.is_some()) @@ -2972,8 +3023,10 @@ async fn test_burn() { } // 3. Burn all delegated tokens { - let input_compressed_accounts = - test_indexer.get_compressed_token_accounts_by_owner(&sender.pubkey()); + let input_compressed_accounts = test_indexer + .get_compressed_token_accounts_by_owner(&sender.pubkey(), None) + .await + .unwrap(); let input_compressed_accounts = input_compressed_accounts .iter() .filter(|x| x.token_data.delegate.is_some()) @@ -3018,7 +3071,9 @@ async fn test_burn() { .await .unwrap(); let input_compressed_accounts = test_indexer - .get_compressed_token_accounts_by_owner(&sender.pubkey()) + .get_compressed_token_accounts_by_owner(&sender.pubkey(), None) + .await + .unwrap() .iter() .filter(|x| x.token_data.amount != 0) .cloned() @@ -3065,7 +3120,9 @@ async fn test_burn() { let slot = rpc.get_slot().await.unwrap(); test_indexer.add_event_and_compressed_accounts(slot, &event); let input_compressed_accounts = test_indexer - .get_compressed_token_accounts_by_owner(&sender.pubkey()) + .get_compressed_token_accounts_by_owner(&sender.pubkey(), None) + .await + .unwrap() .iter() .filter(|x| x.token_data.amount != 0) .cloned() @@ -3147,8 +3204,10 @@ async fn failing_tests_burn() { .await; // Delegate tokens { - let input_compressed_accounts = - test_indexer.get_compressed_token_accounts_by_owner(&sender.pubkey()); + let input_compressed_accounts = test_indexer + .get_compressed_token_accounts_by_owner(&sender.pubkey(), None) + .await + .unwrap(); let delegated_amount = 1000u64; let delegated_compressed_account_merkle_tree = input_compressed_accounts[0] .compressed_account @@ -3170,8 +3229,10 @@ async fn failing_tests_burn() { } // 1. invalid proof { - let input_compressed_accounts = - test_indexer.get_compressed_token_accounts_by_owner(&sender.pubkey()); + let input_compressed_accounts = test_indexer + .get_compressed_token_accounts_by_owner(&sender.pubkey(), None) + .await + .unwrap(); let burn_amount = 1; let change_account_merkle_tree = input_compressed_accounts[0] .compressed_account @@ -3198,8 +3259,10 @@ async fn failing_tests_burn() { } // 2. Signer is delegate but token data has no delegate. { - let input_compressed_accounts = - test_indexer.get_compressed_token_accounts_by_owner(&sender.pubkey()); + let input_compressed_accounts = test_indexer + .get_compressed_token_accounts_by_owner(&sender.pubkey(), None) + .await + .unwrap(); let burn_amount = 1; let change_account_merkle_tree = input_compressed_accounts[0] .compressed_account @@ -3230,8 +3293,10 @@ async fn failing_tests_burn() { } // 3. Signer is delegate but token data has no delegate. { - let input_compressed_accounts = - test_indexer.get_compressed_token_accounts_by_owner(&sender.pubkey()); + let input_compressed_accounts = test_indexer + .get_compressed_token_accounts_by_owner(&sender.pubkey(), None) + .await + .unwrap(); let input_compressed_accounts = input_compressed_accounts .iter() .filter(|x| x.token_data.delegate.is_some()) @@ -3263,8 +3328,10 @@ async fn failing_tests_burn() { } // 4. invalid authority (use delegate as authority) { - let input_compressed_accounts = - test_indexer.get_compressed_token_accounts_by_owner(&sender.pubkey()); + let input_compressed_accounts = test_indexer + .get_compressed_token_accounts_by_owner(&sender.pubkey(), None) + .await + .unwrap(); let burn_amount = 1; let change_account_merkle_tree = input_compressed_accounts[0] .compressed_account @@ -3295,8 +3362,10 @@ async fn failing_tests_burn() { } // 5. invalid mint { - let input_compressed_accounts = - test_indexer.get_compressed_token_accounts_by_owner(&sender.pubkey()); + let input_compressed_accounts = test_indexer + .get_compressed_token_accounts_by_owner(&sender.pubkey(), None) + .await + .unwrap(); let burn_amount = 1; let change_account_merkle_tree = input_compressed_accounts[0] .compressed_account @@ -3328,8 +3397,10 @@ async fn failing_tests_burn() { } // 6. invalid change merkle tree { - let input_compressed_accounts = - test_indexer.get_compressed_token_accounts_by_owner(&sender.pubkey()); + let input_compressed_accounts = test_indexer + .get_compressed_token_accounts_by_owner(&sender.pubkey(), None) + .await + .unwrap(); let burn_amount = 1; let invalid_change_account_merkle_tree = input_compressed_accounts[0] .compressed_account @@ -3361,8 +3432,10 @@ async fn failing_tests_burn() { } // 6. invalid token pool (not initialized) { - let input_compressed_accounts = - test_indexer.get_compressed_token_accounts_by_owner(&sender.pubkey()); + let input_compressed_accounts = test_indexer + .get_compressed_token_accounts_by_owner(&sender.pubkey(), None) + .await + .unwrap(); let burn_amount = 1; let invalid_change_account_merkle_tree = input_compressed_accounts[0] .compressed_account @@ -3389,8 +3462,10 @@ async fn failing_tests_burn() { } // 7. invalid token pool (invalid mint) { - let input_compressed_accounts = - test_indexer.get_compressed_token_accounts_by_owner(&sender.pubkey()); + let input_compressed_accounts = test_indexer + .get_compressed_token_accounts_by_owner(&sender.pubkey(), None) + .await + .unwrap(); let burn_amount = 1; let invalid_change_account_merkle_tree = input_compressed_accounts[0] .compressed_account @@ -3469,8 +3544,10 @@ async fn test_freeze_and_thaw(mint_amount: u64, delegated_amount: u64) { .await; // 1. Freeze tokens { - let input_compressed_accounts = - test_indexer.get_compressed_token_accounts_by_owner(&sender.pubkey()); + let input_compressed_accounts = test_indexer + .get_compressed_token_accounts_by_owner(&sender.pubkey(), None) + .await + .unwrap(); let output_merkle_tree = input_compressed_accounts[0] .compressed_account .merkle_context @@ -3488,8 +3565,10 @@ async fn test_freeze_and_thaw(mint_amount: u64, delegated_amount: u64) { } // 2. Thaw tokens { - let input_compressed_accounts = - test_indexer.get_compressed_token_accounts_by_owner(&sender.pubkey()); + let input_compressed_accounts = test_indexer + .get_compressed_token_accounts_by_owner(&sender.pubkey(), None) + .await + .unwrap(); let input_compressed_accounts = input_compressed_accounts .iter() .filter(|x| x.token_data.state == AccountState::Frozen) @@ -3511,8 +3590,10 @@ async fn test_freeze_and_thaw(mint_amount: u64, delegated_amount: u64) { } // 3. Delegate tokens { - let input_compressed_accounts = - test_indexer.get_compressed_token_accounts_by_owner(&sender.pubkey()); + let input_compressed_accounts = test_indexer + .get_compressed_token_accounts_by_owner(&sender.pubkey(), None) + .await + .unwrap(); let delegated_compressed_account_merkle_tree = input_compressed_accounts[0] .compressed_account .merkle_context @@ -3533,8 +3614,10 @@ async fn test_freeze_and_thaw(mint_amount: u64, delegated_amount: u64) { } // 4. Freeze delegated tokens { - let input_compressed_accounts = - test_indexer.get_compressed_token_accounts_by_owner(&sender.pubkey()); + let input_compressed_accounts = test_indexer + .get_compressed_token_accounts_by_owner(&sender.pubkey(), None) + .await + .unwrap(); let output_merkle_tree = input_compressed_accounts[0] .compressed_account .merkle_context @@ -3552,8 +3635,10 @@ async fn test_freeze_and_thaw(mint_amount: u64, delegated_amount: u64) { } // 5. Thaw delegated tokens { - let input_compressed_accounts = - test_indexer.get_compressed_token_accounts_by_owner(&sender.pubkey()); + let input_compressed_accounts = test_indexer + .get_compressed_token_accounts_by_owner(&sender.pubkey(), None) + .await + .unwrap(); let input_compressed_accounts = input_compressed_accounts .iter() .filter(|x| x.token_data.state == AccountState::Frozen) @@ -3638,8 +3723,11 @@ async fn test_failing_freeze() { ) .await; - let input_compressed_accounts = - vec![test_indexer.get_compressed_token_accounts_by_owner(&sender.pubkey())[0].clone()]; + let input_compressed_accounts = vec![test_indexer + .get_compressed_token_accounts_by_owner(&sender.pubkey(), None) + .await + .unwrap()[0] + .clone()]; let outputs_merkle_tree = input_compressed_accounts[0] .compressed_account .merkle_context @@ -3798,7 +3886,9 @@ async fn test_failing_freeze() { ) .await; let input_compressed_accounts = vec![test_indexer - .get_compressed_token_accounts_by_owner(&sender.pubkey()) + .get_compressed_token_accounts_by_owner(&sender.pubkey(), None) + .await + .unwrap() .iter() .filter(|x| x.token_data.state == AccountState::Frozen) .cloned() @@ -3913,7 +4003,9 @@ async fn test_failing_thaw() { // Freeze tokens { let input_compressed_accounts = vec![test_indexer - .get_compressed_token_accounts_by_owner(&sender.pubkey())[0] + .get_compressed_token_accounts_by_owner(&sender.pubkey(), None) + .await + .unwrap()[0] .clone()]; let output_merkle_tree = input_compressed_accounts[0] .compressed_account @@ -3931,8 +4023,10 @@ async fn test_failing_thaw() { .await; } - let input_compressed_accounts = - test_indexer.get_compressed_token_accounts_by_owner(&sender.pubkey()); + let input_compressed_accounts = test_indexer + .get_compressed_token_accounts_by_owner(&sender.pubkey(), None) + .await + .unwrap(); let input_compressed_accounts = input_compressed_accounts .iter() .filter(|x| x.token_data.state == AccountState::Frozen) @@ -4086,8 +4180,10 @@ async fn test_failing_thaw() { } // 4. thaw compressed account which is not frozen { - let input_compressed_accounts = - test_indexer.get_compressed_token_accounts_by_owner(&sender.pubkey()); + let input_compressed_accounts = test_indexer + .get_compressed_token_accounts_by_owner(&sender.pubkey(), None) + .await + .unwrap(); let input_compressed_accounts = input_compressed_accounts .iter() .filter(|x| x.token_data.state == AccountState::Initialized) @@ -4225,8 +4321,10 @@ async fn test_failing_decompression() { ) .await .unwrap(); - let input_compressed_account = - test_indexer.get_compressed_token_accounts_by_owner(&sender.pubkey()); + let input_compressed_account = test_indexer + .get_compressed_token_accounts_by_owner(&sender.pubkey(), None) + .await + .unwrap(); let decompress_amount = amount - 1000; // Test 1: invalid decompress account { diff --git a/program-tests/system-cpi-test/tests/test.rs b/program-tests/system-cpi-test/tests/test.rs index f3061192f3..ed49967be3 100644 --- a/program-tests/system-cpi-test/tests/test.rs +++ b/program-tests/system-cpi-test/tests/test.rs @@ -986,10 +986,12 @@ async fn only_test_create_pda() { ) .await; - let compressed_account = test_indexer.get_compressed_token_accounts_by_owner(&payer.pubkey()) - [0] - .compressed_account - .clone(); + let compressed_account = test_indexer + .get_compressed_token_accounts_by_owner(&payer.pubkey(), None) + .await + .unwrap()[0] + .compressed_account + .clone(); let compressed_account = sdk_to_program_compressed_account_with_merkle_context(compressed_account); // Failing 4 input account that is not owned by signer ---------------------------------------------- @@ -1062,8 +1064,11 @@ async fn only_test_create_pda() { ) .await .unwrap(); - let compressed_token_account_data = - test_indexer.get_compressed_token_accounts_by_owner(&payer.pubkey())[0].clone(); + let compressed_token_account_data = test_indexer + .get_compressed_token_accounts_by_owner(&payer.pubkey(), None) + .await + .unwrap()[0] + .clone(); // Failing 10 provide cpi context account but cpi context has a different proof ---------------------------------------------- perform_with_input_accounts( &mut test_indexer, @@ -1190,16 +1195,22 @@ async fn test_approve_revoke_burn_freeze_thaw_with_cpi_context() { .unwrap(); let delegate = Keypair::new(); - let ref_compressed_token_data = - test_indexer.get_compressed_token_accounts_by_owner(&payer.pubkey())[0].clone(); + let ref_compressed_token_data = test_indexer + .get_compressed_token_accounts_by_owner(&payer.pubkey(), None) + .await + .unwrap()[0] + .clone(); // 1. Approve functional with cpi context { let compressed_account = test_indexer.get_compressed_accounts_with_merkle_context_by_owner(&ID)[0].clone(); let compressed_account = sdk_to_program_compressed_account_with_merkle_context(compressed_account); - let compressed_token_data = - test_indexer.get_compressed_token_accounts_by_owner(&payer.pubkey())[0].clone(); + let compressed_token_data = test_indexer + .get_compressed_token_accounts_by_owner(&payer.pubkey(), None) + .await + .unwrap()[0] + .clone(); perform_with_input_accounts( &mut test_indexer, &mut rpc, @@ -1212,8 +1223,11 @@ async fn test_approve_revoke_burn_freeze_thaw_with_cpi_context() { ) .await .unwrap(); - let compressed_token_data = - test_indexer.get_compressed_token_accounts_by_owner(&payer.pubkey())[0].clone(); + let compressed_token_data = test_indexer + .get_compressed_token_accounts_by_owner(&payer.pubkey(), None) + .await + .unwrap()[0] + .clone(); let mut ref_data = ref_compressed_token_data.token_data.clone(); ref_data.delegate = Some(delegate.pubkey()); assert_eq!(compressed_token_data.token_data, ref_data); @@ -1229,7 +1243,9 @@ async fn test_approve_revoke_burn_freeze_thaw_with_cpi_context() { let compressed_account = sdk_to_program_compressed_account_with_merkle_context(compressed_account); let compressed_token_data = test_indexer - .get_compressed_token_accounts_by_owner(&payer.pubkey()) + .get_compressed_token_accounts_by_owner(&payer.pubkey(), None) + .await + .unwrap() .iter() .filter(|x| x.token_data.delegate.is_some()) .collect::>()[0] @@ -1246,8 +1262,11 @@ async fn test_approve_revoke_burn_freeze_thaw_with_cpi_context() { ) .await .unwrap(); - let compressed_token_data = - test_indexer.get_compressed_token_accounts_by_owner(&payer.pubkey())[0].clone(); + let compressed_token_data = test_indexer + .get_compressed_token_accounts_by_owner(&payer.pubkey(), None) + .await + .unwrap()[0] + .clone(); let ref_data = ref_compressed_token_data.token_data.clone(); assert_eq!(compressed_token_data.token_data, ref_data); } @@ -1257,8 +1276,11 @@ async fn test_approve_revoke_burn_freeze_thaw_with_cpi_context() { test_indexer.get_compressed_accounts_with_merkle_context_by_owner(&ID)[0].clone(); let compressed_account = sdk_to_program_compressed_account_with_merkle_context(compressed_account); - let compressed_token_data = - test_indexer.get_compressed_token_accounts_by_owner(&payer.pubkey())[0].clone(); + let compressed_token_data = test_indexer + .get_compressed_token_accounts_by_owner(&payer.pubkey(), None) + .await + .unwrap()[0] + .clone(); perform_with_input_accounts( &mut test_indexer, &mut rpc, @@ -1271,8 +1293,11 @@ async fn test_approve_revoke_burn_freeze_thaw_with_cpi_context() { ) .await .unwrap(); - let compressed_token_data = - test_indexer.get_compressed_token_accounts_by_owner(&payer.pubkey())[0].clone(); + let compressed_token_data = test_indexer + .get_compressed_token_accounts_by_owner(&payer.pubkey(), None) + .await + .unwrap()[0] + .clone(); let mut ref_data = ref_compressed_token_data.token_data.clone(); ref_data.state = AccountState::Frozen; assert_eq!(compressed_token_data.token_data, ref_data); @@ -1283,8 +1308,11 @@ async fn test_approve_revoke_burn_freeze_thaw_with_cpi_context() { test_indexer.get_compressed_accounts_with_merkle_context_by_owner(&ID)[0].clone(); let compressed_account = sdk_to_program_compressed_account_with_merkle_context(compressed_account); - let compressed_token_data = - test_indexer.get_compressed_token_accounts_by_owner(&payer.pubkey())[0].clone(); + let compressed_token_data = test_indexer + .get_compressed_token_accounts_by_owner(&payer.pubkey(), None) + .await + .unwrap()[0] + .clone(); perform_with_input_accounts( &mut test_indexer, &mut rpc, @@ -1297,8 +1325,11 @@ async fn test_approve_revoke_burn_freeze_thaw_with_cpi_context() { ) .await .unwrap(); - let compressed_token_data = - test_indexer.get_compressed_token_accounts_by_owner(&payer.pubkey())[0].clone(); + let compressed_token_data = test_indexer + .get_compressed_token_accounts_by_owner(&payer.pubkey(), None) + .await + .unwrap()[0] + .clone(); let ref_data = ref_compressed_token_data.token_data.clone(); assert_eq!(compressed_token_data.token_data, ref_data); } @@ -1306,8 +1337,11 @@ async fn test_approve_revoke_burn_freeze_thaw_with_cpi_context() { { let compressed_account = test_indexer.get_compressed_accounts_with_merkle_context_by_owner(&ID)[0].clone(); - let compressed_token_data = - test_indexer.get_compressed_token_accounts_by_owner(&payer.pubkey())[0].clone(); + let compressed_token_data = test_indexer + .get_compressed_token_accounts_by_owner(&payer.pubkey(), None) + .await + .unwrap()[0] + .clone(); perform_with_input_accounts( &mut test_indexer, &mut rpc, @@ -1320,8 +1354,11 @@ async fn test_approve_revoke_burn_freeze_thaw_with_cpi_context() { ) .await .unwrap(); - let compressed_token_data = - test_indexer.get_compressed_token_accounts_by_owner(&payer.pubkey())[0].clone(); + let compressed_token_data = test_indexer + .get_compressed_token_accounts_by_owner(&payer.pubkey(), None) + .await + .unwrap()[0] + .clone(); let mut ref_data = ref_compressed_token_data.token_data.clone(); ref_data.amount = 1; assert_eq!(compressed_token_data.token_data, ref_data); diff --git a/program-tests/utils/src/e2e_test_env.rs b/program-tests/utils/src/e2e_test_env.rs index 0b59424d01..bae3cb1204 100644 --- a/program-tests/utils/src/e2e_test_env.rs +++ b/program-tests/utils/src/e2e_test_env.rs @@ -2348,7 +2348,11 @@ where delegate: Option, frozen: bool, ) -> (Pubkey, Vec) { - let user_token_accounts = &mut self.indexer.get_compressed_token_accounts_by_owner(user); + let user_token_accounts = &mut self + .indexer + .get_compressed_token_accounts_by_owner(user, None) + .await + .unwrap(); // clean up dust so that we don't run into issues that account balances are too low user_token_accounts.retain(|t| t.token_data.amount > 1000); let mut token_accounts_with_mint; @@ -2380,7 +2384,9 @@ where // filter for token accounts with the same version and mint token_accounts_with_mint = self .indexer - .get_compressed_token_accounts_by_owner(user) + .get_compressed_token_accounts_by_owner(user, None) + .await + .unwrap() .iter() .filter(|token_account| { let version = self diff --git a/program-tests/utils/src/spl.rs b/program-tests/utils/src/spl.rs index 9109d0be2e..67a88f0425 100644 --- a/program-tests/utils/src/spl.rs +++ b/program-tests/utils/src/spl.rs @@ -1013,8 +1013,11 @@ pub async fn perform_compress_spl_token_account< slot, &program_to_sdk_public_transaction_event(event.clone()), ); - let created_compressed_token_account = - test_indexer.get_compressed_token_accounts_by_owner(&token_owner.pubkey())[0].clone(); + let created_compressed_token_account = test_indexer + .get_compressed_token_accounts_by_owner(&token_owner.pubkey(), None) + .await + .unwrap()[0] + .clone(); let expected_token_data = TokenData { amount: pre_token_account_amount - remaining_amount.unwrap_or_default(), mint: *mint, diff --git a/sdk-libs/client/Cargo.toml b/sdk-libs/client/Cargo.toml index 66dd9df838..4142f28345 100644 --- a/sdk-libs/client/Cargo.toml +++ b/sdk-libs/client/Cargo.toml @@ -33,6 +33,7 @@ thiserror = { workspace = true } num-bigint = { workspace = true } num-traits = { workspace = true } reqwest = { workspace = true } +base64 = "0.13.1" [dev-dependencies] light-test-utils = { workspace = true, features=["devenv"]} diff --git a/sdk-libs/client/src/indexer/mod.rs b/sdk-libs/client/src/indexer/mod.rs index cb7b61b88d..86c880c52f 100644 --- a/sdk-libs/client/src/indexer/mod.rs +++ b/sdk-libs/client/src/indexer/mod.rs @@ -1,4 +1,4 @@ -use std::fmt::Debug; +use std::{fmt::Debug, str::FromStr}; use async_trait::async_trait; use light_concurrent_merkle_tree::light_hasher::Poseidon; @@ -7,13 +7,23 @@ use light_indexed_merkle_tree::{ reference::IndexedMerkleTree, }; use light_merkle_tree_reference::MerkleTree; -use light_sdk::proof::ProofRpcResult; +use light_sdk::{ + compressed_account::{ + CompressedAccount, CompressedAccountData, CompressedAccountWithMerkleContext, + }, + merkle_context::MerkleContext, + proof::ProofRpcResult, + token::{AccountState, TokenData, TokenDataWithMerkleContext}, +}; use num_bigint::BigUint; -use solana_sdk::pubkey::Pubkey; +use photon_api::models::{Account, CompressedProofWithContext, TokenAccountList, TokenBalanceList}; +use solana_sdk::{bs58, pubkey::Pubkey}; use thiserror::Error; use crate::rpc::RpcConnection; +pub mod photon_indexer; + #[derive(Error, Debug)] pub enum IndexerError { #[error("RPC Error: {0}")] @@ -33,6 +43,51 @@ pub struct ProofOfLeaf { pub proof: Vec<[u8; 32]>, } +pub type Address = [u8; 32]; +pub type Hash = [u8; 32]; + +pub struct AddressWithTree { + pub address: Address, + pub tree: Pubkey, +} + +pub trait Base58Conversions { + fn to_base58(&self) -> String; + fn from_base58(s: &str) -> Result + where + Self: Sized; + fn to_bytes(&self) -> [u8; 32]; + fn from_bytes(bytes: &[u8]) -> Result + where + Self: Sized; +} + +impl Base58Conversions for [u8; 32] { + fn to_base58(&self) -> String { + bs58::encode(self).into_string() + } + + fn from_base58(s: &str) -> Result { + let bytes = bs58::decode(s) + .into_vec() + .map_err(|e| IndexerError::Custom(e.to_string()))?; + + let mut arr = [0u8; 32]; + arr.copy_from_slice(&bytes); + Ok(arr) + } + + fn to_bytes(&self) -> [u8; 32] { + *self + } + + fn from_bytes(bytes: &[u8]) -> Result { + let mut arr = [0u8; 32]; + arr.copy_from_slice(bytes); + Ok(arr) + } +} + #[async_trait] pub trait Indexer: Sync + Send + Debug + 'static { /// Returns queue elements from the queue with the given pubkey. For input @@ -66,6 +121,47 @@ pub trait Indexer: Sync + Send + Debug + 'static { async fn get_compressed_accounts_by_owner( &self, owner: &Pubkey, + ) -> Result, IndexerError>; + + async fn get_compressed_account( + &self, + address: Option
, + hash: Option, + ) -> Result; + + async fn get_compressed_token_accounts_by_owner( + &self, + owner: &Pubkey, + mint: Option, + ) -> Result, IndexerError>; + + async fn get_compressed_account_balance( + &self, + address: Option
, + hash: Option, + ) -> Result; + + async fn get_compressed_token_account_balance( + &self, + address: Option
, + hash: Option, + ) -> Result; + + async fn get_multiple_compressed_accounts( + &self, + addresses: Option>, + hashes: Option>, + ) -> Result, IndexerError>; + + async fn get_compressed_token_balances_by_owner( + &self, + owner: &Pubkey, + mint: Option, + ) -> Result; + + async fn get_compression_signatures_for_account( + &self, + hash: Hash, ) -> Result, IndexerError>; async fn get_multiple_new_address_proofs( @@ -80,6 +176,12 @@ pub trait Indexer: Sync + Send + Debug + 'static { addresses: Vec<[u8; 32]>, ) -> Result>, IndexerError>; + async fn get_validity_proof( + &self, + hashes: Vec, + new_addresses_with_trees: Vec, + ) -> Result; + fn get_proofs_by_indices( &mut self, merkle_tree_pubkey: Pubkey, @@ -158,3 +260,154 @@ pub struct AddressMerkleTreeBundle { pub accounts: AddressMerkleTreeAccounts, pub queue_elements: Vec<[u8; 32]>, } + +pub trait IntoPhotonAccount { + fn into_photon_account(self) -> photon_api::models::account::Account; +} + +pub trait IntoPhotonTokenAccount { + fn into_photon_token_account(self) -> photon_api::models::token_acccount::TokenAcccount; +} + +impl IntoPhotonAccount for CompressedAccountWithMerkleContext { + fn into_photon_account(self) -> photon_api::models::account::Account { + let address = self.compressed_account.address.map(|a| a.to_base58()); + + let hash = self + .compressed_account + .hash::( + &self.merkle_context.merkle_tree_pubkey, + &self.merkle_context.leaf_index, + ) + .unwrap() + .to_base58(); + + let mut account_data = None; + if let Some(data) = &self.compressed_account.data { + let data_bs64 = base64::encode(&*data.data); + let discriminator = u64::from_be_bytes(data.discriminator); + account_data = Some(Box::new(photon_api::models::account_data::AccountData { + data: data_bs64, + discriminator, + data_hash: data.data_hash.to_base58(), + })); + } + + photon_api::models::account::Account { + address, + hash: hash.to_string(), + lamports: self.compressed_account.lamports, + data: account_data, + owner: self.compressed_account.owner.to_string(), + seq: 0, + slot_created: 0, + leaf_index: self.merkle_context.leaf_index, + tree: self.merkle_context.merkle_tree_pubkey.to_string(), + } + } +} + +impl IntoPhotonTokenAccount for TokenDataWithMerkleContext { + fn into_photon_token_account(self) -> photon_api::models::token_acccount::TokenAcccount { + let base_account = self.compressed_account.into_photon_account(); + + let mut tlv = None; + if let Some(tlv_vec) = &self.token_data.tlv { + tlv = Some(base64::encode(tlv_vec.as_slice())); + } + + let token_data = photon_api::models::token_data::TokenData { + mint: self.token_data.mint.to_string(), + owner: self.token_data.owner.to_string(), + amount: self.token_data.amount, + delegate: self.token_data.delegate.map(|d| d.to_string()), + state: match self.token_data.state { + AccountState::Initialized => { + photon_api::models::account_state::AccountState::Initialized + } + AccountState::Frozen => photon_api::models::account_state::AccountState::Frozen, + }, + tlv, + }; + + photon_api::models::token_acccount::TokenAcccount { + account: Box::new(base_account), + token_data: Box::new(token_data), + } + } +} + +pub struct LocalPhotonAccount(Account); + +impl TryFrom for CompressedAccountWithMerkleContext { + type Error = Box; + + fn try_from(local_account: LocalPhotonAccount) -> Result { + let account = local_account.0; + let merkle_context = MerkleContext { + merkle_tree_pubkey: Pubkey::from_str(&account.tree)?, + nullifier_queue_pubkey: Default::default(), + leaf_index: account.leaf_index, + queue_index: None, + }; + + let mut compressed_account = CompressedAccount { + address: account + .address + .map(|a| <[u8; 32]>::from_base58(&a).unwrap()), + lamports: account.lamports, + owner: Pubkey::from_str(&account.owner)?, + data: None, + }; + + if let Some(data) = account.data { + compressed_account.data = Some(CompressedAccountData { + discriminator: data.discriminator.to_be_bytes(), + data: base64::decode(&data.data)?, + data_hash: <[u8; 32]>::from_base58(&data.data_hash)?, + }); + } + + Ok(CompressedAccountWithMerkleContext { + compressed_account, + merkle_context, + }) + } +} + +pub trait FromPhotonTokenAccountList { + fn into_token_data_vec(self) -> Vec; +} + +impl FromPhotonTokenAccountList for TokenAccountList { + fn into_token_data_vec(self) -> Vec { + self.items + .into_iter() + .map(|item| { + let token_data = TokenData { + mint: Pubkey::from_str(&item.token_data.mint).unwrap(), + owner: Pubkey::from_str(&item.token_data.owner).unwrap(), + amount: item.token_data.amount, + delegate: item + .token_data + .delegate + .map(|d| Pubkey::from_str(&d).unwrap()), + state: match item.token_data.state { + photon_api::models::AccountState::Initialized => AccountState::Initialized, + photon_api::models::AccountState::Frozen => AccountState::Frozen, + }, + tlv: item.token_data.tlv.map(|t| base64::decode(&t).unwrap()), + }; + + let compressed_account = + CompressedAccountWithMerkleContext::try_from(LocalPhotonAccount(*item.account)) + .unwrap(); + + TokenDataWithMerkleContext { + token_data, + compressed_account, + } + }) + .collect() + } +} diff --git a/sdk-libs/client/src/indexer/photon_indexer.rs b/sdk-libs/client/src/indexer/photon_indexer.rs new file mode 100644 index 0000000000..e77bd0a1fc --- /dev/null +++ b/sdk-libs/client/src/indexer/photon_indexer.rs @@ -0,0 +1,512 @@ +use std::fmt::Debug; + +use async_trait::async_trait; +use light_sdk::{proof::ProofRpcResult, token::TokenDataWithMerkleContext}; +use photon_api::{ + apis::configuration::{ApiKey, Configuration}, + models::{ + Account, CompressedProofWithContext, GetCompressedAccountsByOwnerPostRequestParams, + TokenBalanceList, + }, +}; +use solana_program::pubkey::Pubkey; +use solana_sdk::bs58; + +use crate::{ + indexer::{ + Address, AddressMerkleTreeBundle, AddressWithTree, Base58Conversions, + FromPhotonTokenAccountList, Hash, Indexer, IndexerError, LeafIndexInfo, MerkleProof, + NewAddressProofWithContext, ProofOfLeaf, + }, + rpc::RpcConnection, +}; + +pub struct PhotonIndexer { + configuration: Configuration, + #[allow(dead_code)] + rpc: R, +} + +impl PhotonIndexer { + pub fn new(path: String, api_key: Option, rpc: R) -> Self { + let configuration = Configuration { + base_path: path, + api_key: api_key.map(|key| ApiKey { + prefix: Some("api-key".to_string()), + key, + }), + ..Default::default() + }; + + PhotonIndexer { configuration, rpc } + } + + fn build_account_params( + &self, + address: Option
, + hash: Option, + ) -> Result { + if address.is_none() && hash.is_none() { + return Err(IndexerError::Custom( + "Either address or hash must be provided".to_string(), + )); + } + + if address.is_some() && hash.is_some() { + return Err(IndexerError::Custom( + "Only one of address or hash must be provided".to_string(), + )); + } + + Ok(photon_api::models::GetCompressedAccountPostRequestParams { + address: address.map(|x| Some(x.to_base58())), + hash: hash.map(|x| Some(x.to_base58())), + }) + } +} + +impl Debug for PhotonIndexer { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("PhotonIndexer") + .field("configuration", &self.configuration) + .finish() + } +} + +#[async_trait] +impl Indexer for PhotonIndexer { + async fn get_queue_elements( + &self, + _pubkey: [u8; 32], + _batch: u64, + _start_offset: u64, + _end_offset: u64, + ) -> Result, IndexerError> { + unimplemented!() + } + + fn get_subtrees(&self, _merkle_tree_pubkey: [u8; 32]) -> Result, IndexerError> { + unimplemented!() + } + + async fn create_proof_for_compressed_accounts( + &mut self, + _compressed_accounts: Option>, + _state_merkle_tree_pubkeys: Option>, + _new_addresses: Option<&[[u8; 32]]>, + _address_merkle_tree_pubkeys: Option>, + _rpc: &mut R, + ) -> ProofRpcResult { + todo!() + } + + async fn get_multiple_compressed_account_proofs( + &self, + hashes: Vec, + ) -> Result, IndexerError> { + let request: photon_api::models::GetMultipleCompressedAccountProofsPostRequest = + photon_api::models::GetMultipleCompressedAccountProofsPostRequest { + params: hashes, + ..Default::default() + }; + + let result = photon_api::apis::default_api::get_multiple_compressed_account_proofs_post( + &self.configuration, + request, + ) + .await; + + match result { + Ok(response) => { + match response.result { + Some(result) => { + let proofs = result + .value + .iter() + .map(|x| { + let mut proof_result_value = x.proof.clone(); + proof_result_value.truncate(proof_result_value.len() - 10); // Remove canopy + let proof: Vec<[u8; 32]> = proof_result_value + .iter() + .map(|x| Hash::from_base58(x).unwrap()) + .collect(); + MerkleProof { + hash: x.hash.clone(), + leaf_index: x.leaf_index, + merkle_tree: x.merkle_tree.clone(), + proof, + root_seq: x.root_seq, + } + }) + .collect(); + + Ok(proofs) + } + None => { + let error = response.error.unwrap(); + Err(IndexerError::Custom(error.message.unwrap())) + } + } + } + Err(e) => Err(IndexerError::Custom(e.to_string())), + } + } + + async fn get_compressed_accounts_by_owner( + &self, + owner: &Pubkey, + ) -> Result, IndexerError> { + let request = photon_api::models::GetCompressedAccountsByOwnerPostRequest { + params: Box::from(GetCompressedAccountsByOwnerPostRequestParams { + cursor: None, + data_slice: None, + filters: None, + limit: None, + owner: owner.to_string(), + }), + ..Default::default() + }; + + let result = photon_api::apis::default_api::get_compressed_accounts_by_owner_post( + &self.configuration, + request, + ) + .await + .unwrap(); + + let accs = result.result.unwrap().value; + let mut hashes = Vec::new(); + for acc in accs.items { + hashes.push(acc.hash); + } + + Ok(hashes + .iter() + .map(|x| Hash::from_base58(x).unwrap()) + .collect()) + } + + async fn get_compressed_account( + &self, + address: Option
, + hash: Option, + ) -> Result { + let params = self.build_account_params(address, hash)?; + let request = photon_api::models::GetCompressedAccountPostRequest { + params: Box::new(params), + ..Default::default() + }; + + let result = photon_api::apis::default_api::get_compressed_account_post( + &self.configuration, + request, + ) + .await + .map_err(|e| IndexerError::Custom(e.to_string()))?; + + match result.result { + Some(result) => { + if let Some(acc) = result.value { + Ok(*acc) + } else { + Err(IndexerError::Custom("Missing account".to_string())) + } + } + None => Err(IndexerError::Custom("Missing result".to_string())), + } + } + + async fn get_compressed_token_accounts_by_owner( + &self, + owner: &Pubkey, + mint: Option, + ) -> Result, IndexerError> { + let request = photon_api::models::GetCompressedTokenAccountsByOwnerPostRequest { + params: Box::new( + photon_api::models::GetCompressedTokenAccountsByOwnerPostRequestParams { + owner: owner.to_string(), + mint: mint.map(|x| Some(x.to_string())), + cursor: None, + limit: None, + }, + ), + ..Default::default() + }; + + let result = photon_api::apis::default_api::get_compressed_token_accounts_by_owner_post( + &self.configuration, + request, + ) + .await + .map_err(|e| IndexerError::Custom(e.to_string()))?; + + match result.result { + Some(result) => Ok(result.value.into_token_data_vec()), + None => Err(IndexerError::Custom("Missing result".to_string())), + } + } + + async fn get_compressed_account_balance( + &self, + address: Option
, + hash: Option, + ) -> Result { + let params = self.build_account_params(address, hash)?; + let request = photon_api::models::GetCompressedAccountBalancePostRequest { + params: Box::new(params), + ..Default::default() + }; + + let result = photon_api::apis::default_api::get_compressed_account_balance_post( + &self.configuration, + request, + ) + .await + .map_err(|e| IndexerError::Custom(e.to_string()))?; + + match result.result { + Some(result) => Ok(result.value), + None => Err(IndexerError::Custom("Missing result".to_string())), + } + } + + async fn get_compressed_token_account_balance( + &self, + address: Option
, + hash: Option, + ) -> Result { + let request = photon_api::models::GetCompressedTokenAccountBalancePostRequest { + params: Box::new(photon_api::models::GetCompressedAccountPostRequestParams { + address: address.map(|x| Some(x.to_base58())), + hash: hash.map(|x| Some(x.to_base58())), + }), + ..Default::default() + }; + + let result = photon_api::apis::default_api::get_compressed_token_account_balance_post( + &self.configuration, + request, + ) + .await + .map_err(|e| IndexerError::Custom(e.to_string()))?; + + match result.result { + Some(result) => Ok(result.value.amount), + None => Err(IndexerError::Custom("Missing result".to_string())), + } + } + + async fn get_multiple_compressed_accounts( + &self, + addresses: Option>, + hashes: Option>, + ) -> Result, IndexerError> { + let request = photon_api::models::GetMultipleCompressedAccountsPostRequest { + params: Box::new( + photon_api::models::GetMultipleCompressedAccountsPostRequestParams { + addresses: addresses.map(|x| Some(x.iter().map(|x| x.to_base58()).collect())), + hashes: hashes.map(|x| Some(x.iter().map(|x| x.to_base58()).collect())), + }, + ), + ..Default::default() + }; + + let result = photon_api::apis::default_api::get_multiple_compressed_accounts_post( + &self.configuration, + request, + ) + .await + .map_err(|e| IndexerError::Custom(e.to_string()))?; + + match result.result { + Some(result) => Ok(result.value.items), + None => Err(IndexerError::Custom("Missing result".to_string())), + } + } + + async fn get_compressed_token_balances_by_owner( + &self, + owner: &Pubkey, + mint: Option, + ) -> Result { + let request = photon_api::models::GetCompressedTokenBalancesByOwnerPostRequest { + params: Box::new( + photon_api::models::GetCompressedTokenAccountsByOwnerPostRequestParams { + owner: owner.to_string(), + mint: mint.map(|x| Some(x.to_string())), + cursor: None, + limit: None, + }, + ), + ..Default::default() + }; + + let result = photon_api::apis::default_api::get_compressed_token_balances_by_owner_post( + &self.configuration, + request, + ) + .await + .map_err(|e| IndexerError::Custom(e.to_string()))?; + + match result.result { + Some(result) => Ok(*result.value), + None => Err(IndexerError::Custom("Missing result".to_string())), + } + } + + async fn get_compression_signatures_for_account( + &self, + hash: Hash, + ) -> Result, IndexerError> { + let request = photon_api::models::GetCompressionSignaturesForAccountPostRequest { + params: Box::new( + photon_api::models::GetCompressedAccountProofPostRequestParams { + hash: hash.to_base58(), + }, + ), + ..Default::default() + }; + + let result = photon_api::apis::default_api::get_compression_signatures_for_account_post( + &self.configuration, + request, + ) + .await + .map_err(|e| IndexerError::Custom(e.to_string()))?; + + match result.result { + Some(result) => Ok(result + .value + .items + .iter() + .map(|x| x.signature.clone()) + .collect()), + None => Err(IndexerError::Custom("Missing result".to_string())), + } + } + + async fn get_multiple_new_address_proofs( + &self, + merkle_tree_pubkey: [u8; 32], + addresses: Vec<[u8; 32]>, + ) -> Result>, IndexerError> { + let params: Vec = addresses + .iter() + .map(|x| photon_api::models::address_with_tree::AddressWithTree { + address: bs58::encode(x).into_string(), + tree: bs58::encode(&merkle_tree_pubkey).into_string(), + }) + .collect(); + + let request = photon_api::models::GetMultipleNewAddressProofsV2PostRequest { + params, + ..Default::default() + }; + + let result = photon_api::apis::default_api::get_multiple_new_address_proofs_v2_post( + &self.configuration, + request, + ) + .await; + + if result.is_err() { + return Err(IndexerError::Custom(result.err().unwrap().to_string())); + } + + let photon_proofs = result.unwrap().result.unwrap().value; + // net height 16 = height(26) - canopy(10) + let mut proofs: Vec> = Vec::new(); + for photon_proof in photon_proofs { + let tree_pubkey = Hash::from_base58(&photon_proof.merkle_tree)?; + let low_address_value = Hash::from_base58(&photon_proof.lower_range_address)?; + let next_address_value = Hash::from_base58(&photon_proof.higher_range_address)?; + let proof = NewAddressProofWithContext { + merkle_tree: tree_pubkey, + low_address_index: photon_proof.low_element_leaf_index as u64, + low_address_value, + low_address_next_index: photon_proof.next_index as u64, + low_address_next_value: next_address_value, + low_address_proof: { + let mut proof_vec: Vec<[u8; 32]> = photon_proof + .proof + .iter() + .map(|x: &String| Hash::from_base58(x)) + .collect::, IndexerError>>()?; + proof_vec.truncate(proof_vec.len() - 10); // Remove canopy + let mut proof_arr = [[0u8; 32]; 16]; + proof_arr.copy_from_slice(&proof_vec); + proof_arr + }, + root: Hash::from_base58(&photon_proof.root)?, + root_seq: photon_proof.root_seq, + new_low_element: None, + new_element: None, + new_element_next_value: None, + }; + proofs.push(proof); + } + + Ok(proofs) + } + + async fn get_multiple_new_address_proofs_h40( + &self, + _merkle_tree_pubkey: [u8; 32], + _addresses: Vec<[u8; 32]>, + ) -> Result>, IndexerError> { + unimplemented!() + } + + async fn get_validity_proof( + &self, + hashes: Vec, + new_addresses_with_trees: Vec, + ) -> Result { + let request = photon_api::models::GetValidityProofPostRequest { + params: Box::new(photon_api::models::GetValidityProofPostRequestParams { + hashes: Some(hashes.iter().map(|x| x.to_base58()).collect()), + new_addresses: None, + new_addresses_with_trees: Some( + new_addresses_with_trees + .iter() + .map(|x| photon_api::models::AddressWithTree { + address: x.address.to_base58(), + tree: x.tree.to_string(), + }) + .collect(), + ), + }), + ..Default::default() + }; + + let result = + photon_api::apis::default_api::get_validity_proof_post(&self.configuration, request) + .await + .map_err(|e| IndexerError::Custom(e.to_string()))?; + + match result.result { + Some(result) => Ok(*result.value), + None => Err(IndexerError::Custom("Missing result".to_string())), + } + } + + fn get_proofs_by_indices( + &mut self, + _merkle_tree_pubkey: Pubkey, + _indices: &[u64], + ) -> Vec { + todo!() + } + + fn get_leaf_indices_tx_hashes( + &mut self, + _merkle_tree_pubkey: Pubkey, + _zkp_batch_size: usize, + ) -> Vec { + todo!() + } + + fn get_address_merkle_trees(&self) -> &Vec { + todo!() + } +} diff --git a/sdk-libs/client/src/lib.rs b/sdk-libs/client/src/lib.rs index f7cfda17be..955c9b1ee5 100644 --- a/sdk-libs/client/src/lib.rs +++ b/sdk-libs/client/src/lib.rs @@ -1,5 +1,4 @@ pub mod indexer; -pub mod photon_rpc; pub mod rpc; pub mod rpc_pool; pub mod transaction_params; diff --git a/sdk-libs/client/src/photon_rpc/error.rs b/sdk-libs/client/src/photon_rpc/error.rs deleted file mode 100644 index 307e62c0b1..0000000000 --- a/sdk-libs/client/src/photon_rpc/error.rs +++ /dev/null @@ -1,48 +0,0 @@ -use photon_api::{ - apis::{ - default_api::{ - GetCompressedAccountPostError, GetCompressedAccountProofPostError, - GetLatestCompressionSignaturesPostError, GetMultipleCompressedAccountProofsPostError, - GetMultipleNewAddressProofsV2PostError, GetTransactionWithCompressionInfoPostError, - }, - Error as PhotonError, - }, - models::GetCompressedAccountPost429Response, -}; -use thiserror::Error; - -#[derive(Debug, Error)] -pub enum PhotonClientError { - #[error(transparent)] - GetMultipleCompressedAccountProofsError( - #[from] PhotonError, - ), - #[error(transparent)] - GetCompressedAccountsByOwnerError(#[from] PhotonError), - #[error(transparent)] - GetMultipleNewAddressProofsError(#[from] PhotonError), - #[error(transparent)] - GetCompressedAccountError(#[from] PhotonError), - #[error(transparent)] - GetCompressedAccountProofError(#[from] PhotonError), - #[error(transparent)] - GetTransactionWithCompressionInfoError( - #[from] PhotonError, - ), - #[error(transparent)] - GetLatestCompressionSignaturesError( - #[from] PhotonError, - ), - #[error("Decode error: {0}")] - DecodeError(String), - #[error("Invalid parameter: {0}")] - InvalidParameter(String), - #[error("Network error: {0}")] - NetworkError(#[from] reqwest::Error), - #[error("Rate limit exceeded")] - RateLimitExceeded, - #[error("Missing required field: {0}")] - MissingField(String), - #[error("Invalid response format: {0}")] - InvalidResponse(String), -} diff --git a/sdk-libs/client/src/photon_rpc/mod.rs b/sdk-libs/client/src/photon_rpc/mod.rs deleted file mode 100644 index 7ce91f23f3..0000000000 --- a/sdk-libs/client/src/photon_rpc/mod.rs +++ /dev/null @@ -1,12 +0,0 @@ -mod error; -mod models; -mod photon_client; -mod types; - -pub use error::PhotonClientError; -pub use models::{ - AccountBalance, CompressedAccount, CompressedAccountResponse, TokenAccountBalance, - TokenAccountBalanceResponse, -}; -pub use photon_client::PhotonClient; -pub use types::{Address, AddressWithTree, Base58Conversions, Hash}; diff --git a/sdk-libs/client/src/photon_rpc/models.rs b/sdk-libs/client/src/photon_rpc/models.rs deleted file mode 100644 index 564095ea54..0000000000 --- a/sdk-libs/client/src/photon_rpc/models.rs +++ /dev/null @@ -1,133 +0,0 @@ -use crate::photon_rpc::types::{Base58Conversions, Hash}; - -#[derive(Debug)] -pub struct CompressedAccount { - pub hash: Hash, - pub data: String, - pub owner: String, - pub lamports: u64, - pub executable: bool, - pub rent_epoch: u64, -} - -#[derive(Debug)] -pub struct TokenAccountBalance { - pub amount: String, -} - -#[derive(Debug)] -pub struct AccountBalance { - pub lamports: u64, -} - -#[derive(Debug)] -pub struct CompressedAccountResponse { - pub context: ResponseContext, - pub value: CompressedAccount, -} - -#[derive(Debug)] -pub struct CompressedAccountsResponse { - pub context: ResponseContext, - pub value: Vec, -} - -#[derive(Debug)] -pub struct TokenAccountBalanceResponse { - pub context: ResponseContext, - pub value: TokenAccountBalance, -} - -#[derive(Debug)] -pub struct AccountBalanceResponse { - pub context: ResponseContext, - pub value: AccountBalance, -} - -#[derive(Debug)] -pub struct ResponseContext { - pub slot: u64, -} - -impl From for ResponseContext { - fn from(ctx: photon_api::models::Context) -> Self { - ResponseContext { - slot: ctx.slot as u64, - } - } -} - -impl From - for CompressedAccountResponse -{ - fn from(result: photon_api::models::GetCompressedAccountPost200ResponseResult) -> Self { - let account = result.value.as_ref().unwrap(); - CompressedAccountResponse { - context: ResponseContext::from(*result.context), - value: CompressedAccount { - hash: Hash::from_base58(&account.hash).unwrap(), - data: account - .data - .as_ref() - .map_or(String::new(), |d| d.data.clone()), - owner: account.owner.clone(), - lamports: account.lamports as u64, - executable: false, - rent_epoch: account.slot_created as u64, - }, - } - } -} - -impl From - for TokenAccountBalanceResponse -{ - fn from( - result: photon_api::models::GetCompressedTokenAccountBalancePost200ResponseResult, - ) -> Self { - TokenAccountBalanceResponse { - context: ResponseContext::from(*result.context), - value: TokenAccountBalance { - amount: result.value.amount.to_string(), - }, - } - } -} - -impl From - for AccountBalanceResponse -{ - fn from(result: photon_api::models::GetCompressedAccountBalancePost200ResponseResult) -> Self { - AccountBalanceResponse { - context: ResponseContext::from(*result.context), - value: AccountBalance { - lamports: result.value as u64, - }, - } - } -} - -impl From - for CompressedAccountsResponse -{ - fn from( - result: photon_api::models::GetMultipleCompressedAccountsPost200ResponseResult, - ) -> Self { - CompressedAccountsResponse { - context: ResponseContext::from(*result.context), - value: result - .value - .items - .iter() - .map(|acc| CompressedAccount { - hash: Hash::from_base58(&acc.hash).unwrap(), - data: acc.data.as_ref().map_or(String::new(), |d| d.data.clone()), - owner: acc.owner.clone(), - lamports: acc.lamports as u64, - executable: false, - rent_epoch: acc.slot_created as u64, - }) - .collect(), - } - } -} diff --git a/sdk-libs/client/src/photon_rpc/photon_client.rs b/sdk-libs/client/src/photon_rpc/photon_client.rs deleted file mode 100644 index 65620a3e01..0000000000 --- a/sdk-libs/client/src/photon_rpc/photon_client.rs +++ /dev/null @@ -1,413 +0,0 @@ -use photon_api::{ - apis::configuration::{ApiKey, Configuration}, - models::GetCompressedAccountsByOwnerPostRequestParams, -}; -use solana_sdk::{bs58, pubkey::Pubkey}; - -use super::{ - models::{AccountBalanceResponse, CompressedAccountsResponse}, - types::AddressWithTree, - Address, Base58Conversions, CompressedAccountResponse, Hash, PhotonClientError, - TokenAccountBalanceResponse, -}; -use crate::indexer::{MerkleProof, NewAddressProofWithContext}; - -#[derive(Debug)] -pub struct PhotonClient { - config: Configuration, -} - -impl PhotonClient { - pub fn new(url: String) -> Self { - let mut config = Configuration::new(); - config.base_path = url; - PhotonClient { config } - } - - pub fn new_with_auth(url: String, api_key: String) -> Self { - let mut config = Configuration::new(); - config.base_path = url; - config.api_key = Some(ApiKey { - key: api_key, - prefix: None, - }); - PhotonClient { config } - } - - pub async fn get_multiple_compressed_account_proofs( - &self, - hashes: Vec, - ) -> Result, PhotonClientError> { - let request = photon_api::models::GetMultipleCompressedAccountProofsPostRequest { - params: hashes.iter().map(|h| h.to_base58()).collect(), - ..Default::default() - }; - - let result = photon_api::apis::default_api::get_multiple_compressed_account_proofs_post( - &self.config, - request, - ) - .await?; - - match result.result { - Some(result) => { - let proofs = result - .value - .iter() - .map(|x| { - let mut proof_result_value = x.proof.clone(); - proof_result_value.truncate(proof_result_value.len() - 10); - let proof = proof_result_value - .iter() - .map(|x| Hash::from_base58(x).unwrap()) - .collect(); - MerkleProof { - hash: x.hash.clone(), - leaf_index: x.leaf_index, - merkle_tree: x.merkle_tree.clone(), - proof, - root_seq: x.root_seq, - } - }) - .collect(); - Ok(proofs) - } - None => Err(PhotonClientError::DecodeError("Missing result".to_string())), - } - } - - pub async fn get_rpc_compressed_accounts_by_owner( - &self, - owner: &Pubkey, - ) -> Result, PhotonClientError> { - let request = photon_api::models::GetCompressedAccountsByOwnerPostRequest { - params: Box::from(GetCompressedAccountsByOwnerPostRequestParams { - cursor: None, - data_slice: None, - filters: None, - limit: None, - owner: owner.to_string(), - }), - ..Default::default() - }; - - let result = photon_api::apis::default_api::get_compressed_accounts_by_owner_post( - &self.config, - request, - ) - .await - .unwrap(); - - let accs = result.result.unwrap().value; - let mut hashes = Vec::new(); - for acc in accs.items { - hashes.push(acc.hash); - } - - Ok(hashes - .iter() - .map(|x| Hash::from_base58(x).unwrap()) - .collect()) - } - - pub async fn get_multiple_new_address_proofs( - &self, - merkle_tree_pubkey: Pubkey, - addresses: Vec
, - ) -> Result>, PhotonClientError> { - let params: Vec = addresses - .iter() - .map(|x| photon_api::models::AddressWithTree { - address: bs58::encode(x).into_string(), - tree: bs58::encode(&merkle_tree_pubkey).into_string(), - }) - .collect(); - - let request = photon_api::models::GetMultipleNewAddressProofsV2PostRequest { - params, - ..Default::default() - }; - - let result = photon_api::apis::default_api::get_multiple_new_address_proofs_v2_post( - &self.config, - request, - ) - .await; - - if result.is_err() { - return Err(PhotonClientError::GetMultipleNewAddressProofsError( - result.err().unwrap(), - )); - } - - let photon_proofs = result.unwrap().result.unwrap().value; - let mut proofs: Vec> = Vec::new(); - for photon_proof in photon_proofs { - let tree_pubkey = Hash::from_base58(&photon_proof.merkle_tree).unwrap(); - let low_address_value = Hash::from_base58(&photon_proof.lower_range_address).unwrap(); - let next_address_value = Hash::from_base58(&photon_proof.higher_range_address).unwrap(); - let proof = NewAddressProofWithContext { - merkle_tree: tree_pubkey, - low_address_index: photon_proof.low_element_leaf_index as u64, - low_address_value, - low_address_next_index: photon_proof.next_index as u64, - low_address_next_value: next_address_value, - low_address_proof: { - let mut proof_vec: Vec<[u8; 32]> = photon_proof - .proof - .iter() - .map(|x: &String| Hash::from_base58(x).unwrap()) - .collect(); - proof_vec.truncate(proof_vec.len() - 10); // Remove canopy - let mut proof_arr = [[0u8; 32]; 16]; - proof_arr.copy_from_slice(&proof_vec); - proof_arr - }, - root: Hash::from_base58(&photon_proof.root).unwrap(), - root_seq: photon_proof.root_seq, - new_low_element: None, - new_element: None, - new_element_next_value: None, - }; - proofs.push(proof); - } - - Ok(proofs) - } - - pub async fn get_validity_proof( - &self, - hashes: Vec, - new_addresses_with_trees: Vec, - ) -> Result { - let request = photon_api::models::GetValidityProofPostRequest { - params: Box::new(photon_api::models::GetValidityProofPostRequestParams { - hashes: Some(hashes.iter().map(|x| x.to_base58()).collect()), - new_addresses: None, - new_addresses_with_trees: Some( - new_addresses_with_trees - .iter() - .map(|x| photon_api::models::AddressWithTree { - address: x.address.to_base58(), - tree: x.tree.to_string(), - }) - .collect(), - ), - }), - ..Default::default() - }; - - let result = photon_api::apis::default_api::get_validity_proof_post(&self.config, request) - .await - .map_err(|e| PhotonClientError::DecodeError(e.to_string()))?; - - match result.result { - Some(result) => Ok(*result), - None => Err(PhotonClientError::DecodeError("Missing result".to_string())), - } - } - - pub async fn get_compressed_account( - &self, - address: Option
, - hash: Option, - ) -> Result { - let params = self.build_account_params(address, hash)?; - let request = photon_api::models::GetCompressedAccountPostRequest { - params: Box::new(params), - ..Default::default() - }; - - let result = - photon_api::apis::default_api::get_compressed_account_post(&self.config, request) - .await - .map_err(|e| PhotonClientError::DecodeError(e.to_string()))?; - - Self::handle_result(result.result).map(|r| CompressedAccountResponse::from(*r)) - } - - pub async fn get_compressed_token_accounts_by_owner( - &self, - owner: &Pubkey, - mint: Option, - ) -> Result< - photon_api::models::GetCompressedTokenAccountsByDelegatePost200ResponseResult, - PhotonClientError, - > { - let request = photon_api::models::GetCompressedTokenAccountsByOwnerPostRequest { - params: Box::new( - photon_api::models::GetCompressedTokenAccountsByOwnerPostRequestParams { - owner: owner.to_string(), - mint: mint.map(|x| Some(x.to_string())), - cursor: None, - limit: None, - }, - ), - ..Default::default() - }; - - let result = photon_api::apis::default_api::get_compressed_token_accounts_by_owner_post( - &self.config, - request, - ) - .await - .map_err(|e| PhotonClientError::DecodeError(e.to_string()))?; - - Self::handle_result(result.result).map(|r| *r) - } - - pub async fn get_compressed_account_balance( - &self, - address: Option
, - hash: Option, - ) -> Result { - let params = self.build_account_params(address, hash)?; - let request = photon_api::models::GetCompressedAccountBalancePostRequest { - params: Box::new(params), - ..Default::default() - }; - - let result = photon_api::apis::default_api::get_compressed_account_balance_post( - &self.config, - request, - ) - .await - .map_err(|e| PhotonClientError::DecodeError(e.to_string()))?; - - Self::handle_result(result.result).map(|r| AccountBalanceResponse::from(*r)) - } - - pub async fn get_compressed_token_account_balance( - &self, - address: Option
, - hash: Option, - ) -> Result { - let request = photon_api::models::GetCompressedTokenAccountBalancePostRequest { - params: Box::new(photon_api::models::GetCompressedAccountPostRequestParams { - address: address.map(|x| Some(x.to_base58())), - hash: hash.map(|x| Some(x.to_base58())), - }), - ..Default::default() - }; - - let result = photon_api::apis::default_api::get_compressed_token_account_balance_post( - &self.config, - request, - ) - .await - .map_err(|e| PhotonClientError::DecodeError(e.to_string()))?; - - Self::handle_result(result.result).map(|r| TokenAccountBalanceResponse::from(*r)) - } - - pub async fn get_compressed_token_balances_by_owner( - &self, - owner: &Pubkey, - mint: Option, - ) -> Result< - photon_api::models::GetCompressedTokenBalancesByOwnerPost200ResponseResult, - PhotonClientError, - > { - let request = photon_api::models::GetCompressedTokenBalancesByOwnerPostRequest { - params: Box::new( - photon_api::models::GetCompressedTokenAccountsByOwnerPostRequestParams { - owner: owner.to_string(), - mint: mint.map(|x| Some(x.to_string())), - cursor: None, - limit: None, - }, - ), - ..Default::default() - }; - - let result = photon_api::apis::default_api::get_compressed_token_balances_by_owner_post( - &self.config, - request, - ) - .await - .map_err(|e| PhotonClientError::DecodeError(e.to_string()))?; - - Self::handle_result(result.result).map(|r| *r) - } - - pub async fn get_compression_signatures_for_account( - &self, - hash: Hash, - ) -> Result< - photon_api::models::GetCompressionSignaturesForAccountPost200ResponseResult, - PhotonClientError, - > { - let request = photon_api::models::GetCompressionSignaturesForAccountPostRequest { - params: Box::new( - photon_api::models::GetCompressedAccountProofPostRequestParams { - hash: hash.to_base58(), - }, - ), - ..Default::default() - }; - - let result = photon_api::apis::default_api::get_compression_signatures_for_account_post( - &self.config, - request, - ) - .await - .map_err(|e| PhotonClientError::DecodeError(e.to_string()))?; - - Self::handle_result(result.result).map(|r| *r) - } - - pub async fn get_multiple_compressed_accounts( - &self, - addresses: Option>, - hashes: Option>, - ) -> Result { - let request = photon_api::models::GetMultipleCompressedAccountsPostRequest { - params: Box::new( - photon_api::models::GetMultipleCompressedAccountsPostRequestParams { - addresses: addresses.map(|x| Some(x.iter().map(|x| x.to_base58()).collect())), - hashes: hashes.map(|x| Some(x.iter().map(|x| x.to_base58()).collect())), - }, - ), - ..Default::default() - }; - - let result = photon_api::apis::default_api::get_multiple_compressed_accounts_post( - &self.config, - request, - ) - .await - .map_err(|e| PhotonClientError::DecodeError(e.to_string()))?; - - Self::handle_result(result.result).map(|r| CompressedAccountsResponse::from(*r)) - } - - fn handle_result(result: Option) -> Result { - match result { - Some(result) => Ok(result), - None => Err(PhotonClientError::DecodeError("Missing result".to_string())), - } - } - - fn build_account_params( - &self, - address: Option
, - hash: Option, - ) -> Result { - if address.is_none() && hash.is_none() { - return Err(PhotonClientError::DecodeError( - "Either address or hash must be provided".to_string(), - )); - } - - if address.is_some() && hash.is_some() { - return Err(PhotonClientError::DecodeError( - "Only one of address or hash must be provided".to_string(), - )); - } - - Ok(photon_api::models::GetCompressedAccountPostRequestParams { - address: address.map(|x| Some(x.to_base58())), - hash: hash.map(|x| Some(x.to_base58())), - }) - } -} diff --git a/sdk-libs/client/src/photon_rpc/types.rs b/sdk-libs/client/src/photon_rpc/types.rs deleted file mode 100644 index 03038d0a68..0000000000 --- a/sdk-libs/client/src/photon_rpc/types.rs +++ /dev/null @@ -1,48 +0,0 @@ -use solana_sdk::{bs58, pubkey::Pubkey}; - -use super::PhotonClientError; - -pub type Address = [u8; 32]; -pub type Hash = [u8; 32]; - -pub struct AddressWithTree { - pub address: Address, - pub tree: Pubkey, -} - -pub trait Base58Conversions { - fn to_base58(&self) -> String; - fn from_base58(s: &str) -> Result - where - Self: Sized; - fn to_bytes(&self) -> [u8; 32]; - fn from_bytes(bytes: &[u8]) -> Result - where - Self: Sized; -} - -impl Base58Conversions for [u8; 32] { - fn to_base58(&self) -> String { - bs58::encode(self).into_string() - } - - fn from_base58(s: &str) -> Result { - let bytes = bs58::decode(s) - .into_vec() - .map_err(|e| PhotonClientError::DecodeError(e.to_string()))?; - - let mut arr = [0u8; 32]; - arr.copy_from_slice(&bytes); - Ok(arr) - } - - fn to_bytes(&self) -> [u8; 32] { - *self - } - - fn from_bytes(bytes: &[u8]) -> Result { - let mut arr = [0u8; 32]; - arr.copy_from_slice(bytes); - Ok(arr) - } -} diff --git a/sdk-libs/photon-api/src/models/_get_compressed_account_balance_post_200_response_result.rs b/sdk-libs/photon-api/src/models/_get_compressed_account_balance_post_200_response_result.rs index 815575d795..06c4691e57 100644 --- a/sdk-libs/photon-api/src/models/_get_compressed_account_balance_post_200_response_result.rs +++ b/sdk-libs/photon-api/src/models/_get_compressed_account_balance_post_200_response_result.rs @@ -15,13 +15,13 @@ pub struct GetCompressedAccountBalancePost200ResponseResult { #[serde(rename = "context")] pub context: Box, #[serde(rename = "value")] - pub value: i32, + pub value: u64, } impl GetCompressedAccountBalancePost200ResponseResult { pub fn new( context: models::Context, - value: i32, + value: u64, ) -> GetCompressedAccountBalancePost200ResponseResult { GetCompressedAccountBalancePost200ResponseResult { context: Box::new(context), diff --git a/sdk-libs/photon-api/src/models/account.rs b/sdk-libs/photon-api/src/models/account.rs index 182a0e4baa..f5351ddf43 100644 --- a/sdk-libs/photon-api/src/models/account.rs +++ b/sdk-libs/photon-api/src/models/account.rs @@ -21,16 +21,16 @@ pub struct Account { #[serde(rename = "hash")] pub hash: String, #[serde(rename = "lamports")] - pub lamports: i32, + pub lamports: u64, #[serde(rename = "leafIndex")] - pub leaf_index: i32, + pub leaf_index: u32, /// A Solana public key represented as a base58 string. #[serde(rename = "owner")] pub owner: String, #[serde(rename = "seq")] - pub seq: i32, + pub seq: u32, #[serde(rename = "slotCreated")] - pub slot_created: i32, + pub slot_created: u32, /// A Solana public key represented as a base58 string. #[serde(rename = "tree")] pub tree: String, @@ -39,11 +39,11 @@ pub struct Account { impl Account { pub fn new( hash: String, - lamports: i32, - leaf_index: i32, + lamports: u64, + leaf_index: u32, owner: String, - seq: i32, - slot_created: i32, + seq: u32, + slot_created: u32, tree: String, ) -> Account { Account { diff --git a/sdk-libs/photon-api/src/models/account_data.rs b/sdk-libs/photon-api/src/models/account_data.rs index ab9bf1abbe..36b34b3b8c 100644 --- a/sdk-libs/photon-api/src/models/account_data.rs +++ b/sdk-libs/photon-api/src/models/account_data.rs @@ -19,11 +19,11 @@ pub struct AccountData { #[serde(rename = "dataHash")] pub data_hash: String, #[serde(rename = "discriminator")] - pub discriminator: i32, + pub discriminator: u64, } impl AccountData { - pub fn new(data: String, data_hash: String, discriminator: i32) -> AccountData { + pub fn new(data: String, data_hash: String, discriminator: u64) -> AccountData { AccountData { data, data_hash, diff --git a/sdk-libs/photon-api/src/models/token_account_balance.rs b/sdk-libs/photon-api/src/models/token_account_balance.rs index cf9f13c897..a63007663b 100644 --- a/sdk-libs/photon-api/src/models/token_account_balance.rs +++ b/sdk-libs/photon-api/src/models/token_account_balance.rs @@ -13,11 +13,11 @@ use crate::models; #[derive(Clone, Default, Debug, PartialEq, Serialize, Deserialize)] pub struct TokenAccountBalance { #[serde(rename = "amount")] - pub amount: i32, + pub amount: u64, } impl TokenAccountBalance { - pub fn new(amount: i32) -> TokenAccountBalance { + pub fn new(amount: u64) -> TokenAccountBalance { TokenAccountBalance { amount } } } diff --git a/sdk-libs/photon-api/src/models/token_balance.rs b/sdk-libs/photon-api/src/models/token_balance.rs index 89af62bbfb..2dbfcf350b 100644 --- a/sdk-libs/photon-api/src/models/token_balance.rs +++ b/sdk-libs/photon-api/src/models/token_balance.rs @@ -13,14 +13,14 @@ use crate::models; #[derive(Clone, Default, Debug, PartialEq, Serialize, Deserialize)] pub struct TokenBalance { #[serde(rename = "balance")] - pub balance: i32, + pub balance: u64, /// A Solana public key represented as a base58 string. #[serde(rename = "mint")] pub mint: String, } impl TokenBalance { - pub fn new(balance: i32, mint: String) -> TokenBalance { + pub fn new(balance: u64, mint: String) -> TokenBalance { TokenBalance { balance, mint } } } diff --git a/sdk-libs/photon-api/src/models/token_data.rs b/sdk-libs/photon-api/src/models/token_data.rs index 36b2606d5d..86b1b2336d 100644 --- a/sdk-libs/photon-api/src/models/token_data.rs +++ b/sdk-libs/photon-api/src/models/token_data.rs @@ -13,7 +13,7 @@ use crate::models; #[derive(Clone, Default, Debug, PartialEq, Serialize, Deserialize)] pub struct TokenData { #[serde(rename = "amount")] - pub amount: i32, + pub amount: u64, /// A Solana public key represented as a base58 string. #[serde(rename = "delegate", skip_serializing_if = "Option::is_none")] pub delegate: Option, @@ -31,7 +31,7 @@ pub struct TokenData { } impl TokenData { - pub fn new(amount: i32, mint: String, owner: String, state: models::AccountState) -> TokenData { + pub fn new(amount: u64, mint: String, owner: String, state: models::AccountState) -> TokenData { TokenData { amount, delegate: None, diff --git a/sdk-libs/program-test/Cargo.toml b/sdk-libs/program-test/Cargo.toml index 10cdcc4f1b..9c503ce961 100644 --- a/sdk-libs/program-test/Cargo.toml +++ b/sdk-libs/program-test/Cargo.toml @@ -8,6 +8,7 @@ default = [] devenv = [] [dependencies] +base64 = "0.2" light-client = { workspace = true } light-prover-client = { workspace = true } light-sdk = { workspace = true } @@ -19,6 +20,7 @@ light-registry = { workspace = true } light-system-program = { workspace = true } light-compressed-token = { workspace = true } light-utils = { workspace = true } +photon-api = { workspace = true } account-compression = { workspace = true } forester-utils = { workspace = true } solana-sdk = { workspace = true } diff --git a/sdk-libs/program-test/src/indexer/extensions.rs b/sdk-libs/program-test/src/indexer/extensions.rs index 6766f6fa3f..36aaac5b7d 100644 --- a/sdk-libs/program-test/src/indexer/extensions.rs +++ b/sdk-libs/program-test/src/indexer/extensions.rs @@ -69,11 +69,6 @@ pub trait TestIndexerExtensions: Indexer { owner: &Pubkey, ) -> Vec; - fn get_compressed_token_accounts_by_owner( - &self, - owner: &Pubkey, - ) -> Vec; - fn add_state_bundle(&mut self, state_bundle: StateMerkleTreeBundle); fn add_event_and_compressed_accounts( diff --git a/sdk-libs/program-test/src/indexer/test_indexer.rs b/sdk-libs/program-test/src/indexer/test_indexer.rs index 93c8a539af..af54c53e80 100644 --- a/sdk-libs/program-test/src/indexer/test_indexer.rs +++ b/sdk-libs/program-test/src/indexer/test_indexer.rs @@ -17,9 +17,9 @@ use light_batched_merkle_tree::{ }; use light_client::{ indexer::{ - AddressMerkleTreeAccounts, AddressMerkleTreeBundle, Indexer, IndexerError, LeafIndexInfo, - MerkleProof, NewAddressProofWithContext, ProofOfLeaf, StateMerkleTreeAccounts, - StateMerkleTreeBundle, + Address, AddressMerkleTreeAccounts, AddressMerkleTreeBundle, AddressWithTree, Hash, + Indexer, IndexerError, IntoPhotonAccount, LeafIndexInfo, MerkleProof, + NewAddressProofWithContext, ProofOfLeaf, StateMerkleTreeAccounts, StateMerkleTreeBundle, }, rpc::{merkle_tree::MerkleTreeExt, RpcConnection}, transaction_params::FeeConfig, @@ -62,6 +62,7 @@ use light_utils::{ use log::{info, warn}; use num_bigint::{BigInt, BigUint}; use num_traits::FromBytes; +use photon_api::models::TokenBalance; use reqwest::Client; use solana_sdk::{ bs58, @@ -369,17 +370,177 @@ where async fn get_compressed_accounts_by_owner( &self, owner: &Pubkey, - ) -> Result, IndexerError> { + ) -> Result, IndexerError> { let result = self.get_compressed_accounts_with_merkle_context_by_owner(owner); - let mut hashes: Vec = Vec::new(); + let mut hashes: Vec = Vec::new(); for account in result.iter() { let hash = account.hash().unwrap(); - let bs58_hash = bs58::encode(hash).into_string(); - hashes.push(bs58_hash); + hashes.push(hash); } Ok(hashes) } + async fn get_compressed_account( + &self, + address: Option
, + hash: Option, + ) -> Result { + let account = match (address, hash) { + (Some(address), _) => self.compressed_accounts.iter().find(|acc| { + acc.compressed_account + .address + .map_or(false, |acc_addr| acc_addr == address) + }), + (_, Some(hash)) => self.compressed_accounts.iter().find(|acc| { + acc.compressed_account + .hash::( + &acc.merkle_context.merkle_tree_pubkey, + &acc.merkle_context.leaf_index, + ) + .map_or(false, |acc_hash| acc_hash == hash) + }), + (None, None) => { + return Err(IndexerError::Custom( + "Either address or hash must be provided".to_string(), + )) + } + }; + + account + .map(|acc| acc.clone().into_photon_account()) + .ok_or_else(|| IndexerError::Custom("Account not found".to_string())) + } + + async fn get_compressed_token_accounts_by_owner( + &self, + owner: &Pubkey, + mint: Option, + ) -> Result, IndexerError> { + let accounts = self + .token_compressed_accounts + .iter() + .filter(|acc| { + acc.token_data.owner == *owner && mint.map_or(true, |m| acc.token_data.mint == m) + }) + .cloned() + .collect(); + + Ok(accounts) + } + + async fn get_compressed_account_balance( + &self, + address: Option
, + hash: Option, + ) -> Result { + let account = self.get_compressed_account(address, hash).await?; + Ok(account.lamports) + } + + async fn get_compressed_token_account_balance( + &self, + address: Option
, + hash: Option, + ) -> Result { + let account = match (address, hash) { + (Some(address), _) => self.token_compressed_accounts.iter().find(|acc| { + acc.compressed_account + .compressed_account + .address + .map_or(false, |acc_addr| acc_addr == address) + }), + (_, Some(hash)) => self.token_compressed_accounts.iter().find(|acc| { + acc.compressed_account + .compressed_account + .hash::( + &acc.compressed_account.merkle_context.merkle_tree_pubkey, + &acc.compressed_account.merkle_context.leaf_index, + ) + .map_or(false, |acc_hash| acc_hash == hash) + }), + (None, None) => { + return Err(IndexerError::Custom( + "Either address or hash must be provided".to_string(), + )) + } + }; + + account + .map(|acc| acc.token_data.amount) + .ok_or_else(|| IndexerError::Custom("Token account not found".to_string())) + } + + async fn get_multiple_compressed_accounts( + &self, + addresses: Option>, + hashes: Option>, + ) -> Result, IndexerError> { + match (addresses, hashes) { + (Some(addresses), _) => { + let accounts = self + .compressed_accounts + .iter() + .filter(|acc| { + acc.compressed_account + .address + .map_or(false, |addr| addresses.contains(&addr)) + }) + .map(|acc| acc.clone().into_photon_account()) + .collect(); + Ok(accounts) + } + (_, Some(hashes)) => { + let accounts = self + .compressed_accounts + .iter() + .filter(|acc| { + acc.compressed_account + .hash::( + &acc.merkle_context.merkle_tree_pubkey, + &acc.merkle_context.leaf_index, + ) + .map_or(false, |hash| hashes.contains(&hash)) + }) + .map(|acc| acc.clone().into_photon_account()) + .collect(); + Ok(accounts) + } + (None, None) => Err(IndexerError::Custom( + "Either addresses or hashes must be provided".to_string(), + )), + } + } + + async fn get_compressed_token_balances_by_owner( + &self, + owner: &Pubkey, + mint: Option, + ) -> Result { + let balances: Vec = self + .token_compressed_accounts + .iter() + .filter(|acc| { + acc.token_data.owner == *owner && mint.map_or(true, |m| acc.token_data.mint == m) + }) + .map(|acc| photon_api::models::token_balance::TokenBalance { + balance: acc.token_data.amount, + mint: acc.token_data.mint.to_string(), + }) + .collect(); + + Ok(photon_api::models::token_balance_list::TokenBalanceList { + cursor: None, + token_balances: balances, + }) + } + + async fn get_compression_signatures_for_account( + &self, + _hash: Hash, + ) -> Result, IndexerError> { + todo!() + } + async fn get_multiple_new_address_proofs( &self, merkle_tree_pubkey: [u8; 32], @@ -398,6 +559,17 @@ where .await } + async fn get_validity_proof( + &self, + _hashes: Vec, + _new_addresses_with_trees: Vec, + ) -> Result< + photon_api::models::compressed_proof_with_context::CompressedProofWithContext, + IndexerError, + > { + todo!() + } + fn get_proofs_by_indices( &mut self, merkle_tree_pubkey: Pubkey, @@ -674,17 +846,6 @@ where .collect() } - fn get_compressed_token_accounts_by_owner( - &self, - owner: &Pubkey, - ) -> Vec { - self.token_compressed_accounts - .iter() - .filter(|x| x.token_data.owner == *owner) - .cloned() - .collect() - } - fn add_state_bundle(&mut self, state_bundle: StateMerkleTreeBundle) { self.get_state_merkle_trees_mut().push(state_bundle); }