From 03fdea6b201a53732c6c65f0c47b442169067887 Mon Sep 17 00:00:00 2001 From: Sergey Timoshin Date: Wed, 4 Mar 2026 10:13:31 +0000 Subject: [PATCH 01/12] fix: filter input account_transactions for unknown trees (#335) --- .../get_validity_proof/prover/helpers.rs | 3 - src/ingester/fetchers/grpc.rs | 15 +++- src/ingester/parser/state_update.rs | 72 ++++++++++++++++++- 3 files changed, 81 insertions(+), 9 deletions(-) diff --git a/src/api/method/get_validity_proof/prover/helpers.rs b/src/api/method/get_validity_proof/prover/helpers.rs index dae5283b..ef2741bf 100644 --- a/src/api/method/get_validity_proof/prover/helpers.rs +++ b/src/api/method/get_validity_proof/prover/helpers.rs @@ -232,7 +232,6 @@ mod tests { hex::decode(padded).unwrap().try_into().unwrap() } - /// combined proofs incorrectly returned non_inclusion_chain instead of /// hash(inclusion_chain, non_inclusion_chain). #[test] @@ -251,10 +250,8 @@ mod tests { let non_inclusion_chain = create_two_inputs_hash_chain(&[address_root], &[address]).unwrap(); - let old_result = non_inclusion_chain; - let correct_result = create_two_inputs_hash_chain(&[inclusion_chain], &[non_inclusion_chain]).unwrap(); diff --git a/src/ingester/fetchers/grpc.rs b/src/ingester/fetchers/grpc.rs index e78add8a..c07bf029 100644 --- a/src/ingester/fetchers/grpc.rs +++ b/src/ingester/fetchers/grpc.rs @@ -17,7 +17,9 @@ use solana_pubkey::Pubkey as SdkPubkey; use solana_signature::Signature; use tokio::time::sleep; use tracing::error; -use yellowstone_grpc_client::{ClientTlsConfig, GeyserGrpcBuilderResult, GeyserGrpcClient, Interceptor}; +use yellowstone_grpc_client::{ + ClientTlsConfig, GeyserGrpcBuilderResult, GeyserGrpcClient, Interceptor, +}; use yellowstone_grpc_proto::convert_from::create_tx_error; use yellowstone_grpc_proto::geyser::{ subscribe_update::UpdateOneof, CommitmentLevel, SubscribeRequest, SubscribeRequestPing, @@ -158,7 +160,11 @@ fn is_healthy(slot: u64) -> bool { (LATEST_SLOT.load(Ordering::SeqCst) as i64 - slot as i64) <= HEALTH_CHECK_SLOT_DISTANCE } -fn get_grpc_block_stream(endpoint: String, auth_header: String, mut last_indexed_slot: Option) -> impl Stream { +fn get_grpc_block_stream( + endpoint: String, + auth_header: String, + mut last_indexed_slot: Option, +) -> impl Stream { stream! { loop { let mut grpc_tx; @@ -255,7 +261,10 @@ fn generate_random_string(len: usize) -> String { } fn get_block_subscribe_request(from_slot: Option) -> SubscribeRequest { - info!("Subscribing to gRPC block stream from slot {}", from_slot.unwrap_or(0)); + info!( + "Subscribing to gRPC block stream from slot {}", + from_slot.unwrap_or(0) + ); SubscribeRequest { blocks: HashMap::from_iter(vec![( generate_random_string(20), diff --git a/src/ingester/parser/state_update.rs b/src/ingester/parser/state_update.rs index aa7a22bc..1d25014d 100644 --- a/src/ingester/parser/state_update.rs +++ b/src/ingester/parser/state_update.rs @@ -185,9 +185,48 @@ impl StateUpdate { // Track which account hashes we're keeping for filtering account_transactions later let mut kept_account_hashes = HashSet::new(); - // Add input (spent) account hashes - these don't have tree info but should be kept - // for account_transactions tracking - kept_account_hashes.extend(self.in_accounts.iter().cloned()); + // Only keep in_accounts whose hashes exist in the accounts table. + // Input accounts from unknown trees were never persisted as outputs, + // so referencing them in account_transactions would violate the FK constraint. + if !self.in_accounts.is_empty() { + let hash_bytes: Vec> = self.in_accounts.iter().map(|h| h.to_vec()).collect(); + let placeholders: Vec = + (1..=hash_bytes.len()).map(|i| format!("${}", i)).collect(); + let sql = format!( + "SELECT hash FROM accounts WHERE hash IN ({})", + placeholders.join(", ") + ); + let values: Vec = hash_bytes + .iter() + .map(|b| sea_orm::Value::Bytes(Some(Box::new(b.clone())))) + .collect(); + let stmt = sea_orm::Statement::from_sql_and_values( + sea_orm::DatabaseBackend::Postgres, + &sql, + values, + ); + let rows = txn.query_all(stmt).await.map_err(|e| { + PhotonApiError::UnexpectedError(format!( + "Failed to query existing input accounts: {}", + e + )) + })?; + let existing_hashes: HashSet = rows + .iter() + .filter_map(|row| { + let bytes: Vec = row.try_get("", "hash").ok()?; + Hash::try_from(bytes).ok() + }) + .collect(); + let filtered_count = self.in_accounts.len() - existing_hashes.len(); + if filtered_count > 0 { + debug!( + "Filtered {} input account hashes not found in accounts table", + filtered_count + ); + } + kept_account_hashes.extend(existing_hashes); + } // Filter out_accounts let out_accounts: Vec<_> = self @@ -576,4 +615,31 @@ mod tests { .iter() .any(|tx| tx.hash == unknown_hash)); } + + #[tokio::test] + async fn test_filter_by_known_trees_filters_input_account_transactions_for_missing_accounts() { + let db = setup_test_db().await; + + let mut state_update = StateUpdate::new(); + + // Simulate input accounts that were never persisted (from unknown trees in earlier txs) + let missing_hash = crate::common::typedefs::hash::Hash::new_unique(); + state_update.in_accounts.insert(missing_hash.clone()); + + // Add an account_transaction referencing the missing input hash + state_update + .account_transactions + .insert(AccountTransaction { + hash: missing_hash.clone(), + signature: Signature::default(), + }); + + // Filter the state update + let result = state_update.filter_by_known_trees(&db).await.unwrap(); + + // The missing input hash should NOT be in kept_account_hashes, + // so the account_transaction referencing it should be filtered out. + // This prevents FK violations in persist_account_transactions. + assert_eq!(result.state_update.account_transactions.len(), 0); + } } From c7a87b049d0bb79b506b9e61210a3c2a0bb07ad1 Mon Sep 17 00:00:00 2001 From: Sergey Timoshin Date: Wed, 4 Mar 2026 19:21:23 +0000 Subject: [PATCH 02/12] feat: discover unknown trees during ingestion via RPC When the indexer encounters a tree not in tree_metadata during transaction parsing, fetch the tree's on-chain account via RPC, add it to tree_metadata, and proceed with indexing instead of skipping. This prevents data gaps for legitimate trees created after startup. --- .../get_transaction_with_compression_info.rs | 10 +++-- src/ingester/indexer/mod.rs | 2 +- src/ingester/mod.rs | 23 +++++++--- src/ingester/parser/mod.rs | 8 ++-- src/ingester/parser/tree_info.rs | 30 +++++++++++++ src/ingester/parser/tx_event_parser.rs | 45 +++++++++++++------ src/ingester/parser/tx_event_parser_v2.rs | 4 +- src/monitor/mod.rs | 2 +- src/monitor/tree_metadata_sync.rs | 9 ++-- .../batched_state_tree_tests.rs | 2 + tests/integration_tests/e2e_tests.rs | 43 ++++++++++++------ tests/integration_tests/mock_tests.rs | 8 ++++ tests/integration_tests/utils.rs | 14 +++--- 13 files changed, 149 insertions(+), 51 deletions(-) diff --git a/src/api/method/get_transaction_with_compression_info.rs b/src/api/method/get_transaction_with_compression_info.rs index 3507446c..22a0c542 100644 --- a/src/api/method/get_transaction_with_compression_info.rs +++ b/src/api/method/get_transaction_with_compression_info.rs @@ -210,6 +210,7 @@ pub async fn get_transaction_helper( conn: &DatabaseConnection, signature: SerializableSignature, txn: EncodedConfirmedTransactionWithStatusMeta, + rpc_client: &RpcClient, ) -> Result { // Ignore if tx failed or meta is missed let meta = txn.transaction.meta.as_ref(); @@ -223,7 +224,7 @@ pub async fn get_transaction_helper( let tx_info: TransactionInfo = clone_tx(&txn).try_into().map_err(|_e| { PhotonApiError::UnexpectedError(format!("Failed to convert transaction {}", signature.0)) })?; - let status_update = parse_transaction(conn, &tx_info, slot) + let status_update = parse_transaction(conn, &tx_info, slot, rpc_client) .await .map_err(|_e| { PhotonApiError::UnexpectedError(format!("Failed to parse transaction {}", signature.0)) @@ -276,7 +277,7 @@ pub async fn get_transaction_with_compression_info( request: GetTransactionRequest, ) -> Result { let txn = fetch_transaction_from_rpc(rpc_client, &request.signature).await?; - get_transaction_helper(conn, request.signature, txn).await + get_transaction_helper(conn, request.signature, txn, rpc_client).await } fn parse_optional_token_data_v2( @@ -339,6 +340,7 @@ pub async fn get_transaction_helper_v2( conn: &DatabaseConnection, signature: SerializableSignature, txn: EncodedConfirmedTransactionWithStatusMeta, + rpc_client: &RpcClient, ) -> Result { // Ignore if tx failed or meta is missed let meta = txn.transaction.meta.as_ref(); @@ -353,7 +355,7 @@ pub async fn get_transaction_helper_v2( PhotonApiError::UnexpectedError(format!("Failed to convert transaction {}", signature.0)) })?; - let status_update = parse_transaction(conn, &tx_info, slot) + let status_update = parse_transaction(conn, &tx_info, slot, rpc_client) .await .map_err(|_e| { PhotonApiError::UnexpectedError(format!("Failed to parse transaction {}", signature.0)) @@ -395,5 +397,5 @@ pub async fn get_transaction_with_compression_info_v2( request: GetTransactionRequest, ) -> Result { let txn = fetch_transaction_from_rpc(rpc_client, &request.signature).await?; - get_transaction_helper_v2(conn, request.signature, txn).await + get_transaction_helper_v2(conn, request.signature, txn, rpc_client).await } diff --git a/src/ingester/indexer/mod.rs b/src/ingester/indexer/mod.rs index fa696d56..b414f6df 100644 --- a/src/ingester/indexer/mod.rs +++ b/src/ingester/indexer/mod.rs @@ -71,7 +71,7 @@ pub async fn index_block_stream( while let Some(blocks) = block_stream.next().await { let last_slot_in_block = blocks.last().unwrap().metadata.slot; - index_block_batch_with_infinite_retries(db.as_ref(), blocks).await; + index_block_batch_with_infinite_retries(db.as_ref(), blocks, rpc_client.as_ref()).await; for slot in (last_indexed_slot + 1)..(last_slot_in_block + 1) { let blocks_indexed = slot - last_indexed_slot_at_start; diff --git a/src/ingester/mod.rs b/src/ingester/mod.rs index c9b953d0..4d137e69 100644 --- a/src/ingester/mod.rs +++ b/src/ingester/mod.rs @@ -14,6 +14,7 @@ use sea_orm::EntityTrait; use sea_orm::QueryTrait; use sea_orm::Set; use sea_orm::TransactionTrait; +use solana_client::nonblocking::rpc_client::RpcClient; use self::parser::state_update::StateUpdate; use self::persist::persist_state_update; @@ -32,18 +33,28 @@ pub mod typedefs; async fn derive_block_state_update( conn: &DatabaseConnection, block: &BlockInfo, + rpc_client: &RpcClient, ) -> Result { let mut state_updates: Vec = Vec::new(); for transaction in &block.transactions { - state_updates.push(parse_transaction(conn, transaction, block.metadata.slot).await?); + state_updates + .push(parse_transaction(conn, transaction, block.metadata.slot, rpc_client).await?); } Ok(StateUpdate::merge_updates(state_updates)) } -pub async fn index_block(db: &DatabaseConnection, block: &BlockInfo) -> Result<(), IngesterError> { +pub async fn index_block( + db: &DatabaseConnection, + block: &BlockInfo, + rpc_client: &RpcClient, +) -> Result<(), IngesterError> { let txn = db.begin().await?; index_block_metadatas(&txn, vec![&block.metadata]).await?; - persist_state_update(&txn, derive_block_state_update(db, block).await?).await?; + persist_state_update( + &txn, + derive_block_state_update(db, block, rpc_client).await?, + ) + .await?; txn.commit().await?; Ok(()) } @@ -84,6 +95,7 @@ async fn index_block_metadatas( pub async fn index_block_batch( db: &DatabaseConnection, block_batch: &Vec, + rpc_client: &RpcClient, ) -> Result<(), IngesterError> { let blocks_len = block_batch.len(); let tx = db.begin().await?; @@ -91,7 +103,7 @@ pub async fn index_block_batch( index_block_metadatas(&tx, block_metadatas).await?; let mut state_updates = Vec::new(); for block in block_batch { - state_updates.push(derive_block_state_update(db, block).await?); + state_updates.push(derive_block_state_update(db, block, rpc_client).await?); } persist::persist_state_update(&tx, StateUpdate::merge_updates(state_updates)).await?; metric! { @@ -104,9 +116,10 @@ pub async fn index_block_batch( pub async fn index_block_batch_with_infinite_retries( db: &DatabaseConnection, block_batch: Vec, + rpc_client: &RpcClient, ) { loop { - match index_block_batch(db, &block_batch).await { + match index_block_batch(db, &block_batch, rpc_client).await { Ok(()) => return, Err(e) => { let start_block = block_batch.first().unwrap().metadata.slot; diff --git a/src/ingester/parser/mod.rs b/src/ingester/parser/mod.rs index dc241ed9..1697527b 100644 --- a/src/ingester/parser/mod.rs +++ b/src/ingester/parser/mod.rs @@ -1,4 +1,5 @@ use merkle_tree_events_parser::parse_merkle_tree_event; +use solana_client::nonblocking::rpc_client::RpcClient; use solana_pubkey::Pubkey; use std::sync::OnceLock; use tx_event_parser::parse_public_transaction_event_v1; @@ -43,12 +44,11 @@ pub fn set_compression_program_id(program_id_str: &str) -> Result<(), String> { pub const EXPECTED_TREE_OWNER: Option = Some(pubkey!("24rt4RgeyjUCWGS2eF7L7gyNMuz6JWdqYpAvb1KRoHxs")); -const SKIP_UNKNOWN_TREES: bool = true; - pub async fn parse_transaction( conn: &T, tx: &TransactionInfo, slot: u64, + rpc_client: &RpcClient, ) -> Result where T: sea_orm::ConnectionTrait + sea_orm::TransactionTrait, @@ -83,7 +83,8 @@ where if let Some(event) = parse_public_transaction_event_v2(&program_ids, &vec_instructions_data, vec_accounts) { - let state_update = create_state_update_v2(conn, tx.signature, slot, event).await?; + let state_update = + create_state_update_v2(conn, tx.signature, slot, event, rpc_client).await?; is_compression_transaction = true; state_updates.push(state_update); } else { @@ -127,6 +128,7 @@ where slot, instruction, &ordered_instructions[noop_index], + rpc_client, ) .await? { diff --git a/src/ingester/parser/tree_info.rs b/src/ingester/parser/tree_info.rs index fbae8143..55d1589f 100644 --- a/src/ingester/parser/tree_info.rs +++ b/src/ingester/parser/tree_info.rs @@ -1,7 +1,10 @@ use crate::api::error::PhotonApiError; use crate::dao::generated::{prelude::*, tree_metadata}; +use crate::ingester::error::IngesterError; +use crate::monitor::tree_metadata_sync; use light_compressed_account::TreeType; use sea_orm::{ColumnTrait, ConnectionTrait, EntityTrait, QueryFilter, TransactionTrait}; +use solana_client::nonblocking::rpc_client::RpcClient; use solana_pubkey::Pubkey; use std::str::FromStr; @@ -216,3 +219,30 @@ impl TreeInfo { }) } } + +pub async fn discover_tree( + rpc_client: &RpcClient, + conn: &T, + pubkey: &Pubkey, + slot: u64, +) -> Result, IngesterError> +where + T: ConnectionTrait + TransactionTrait, +{ + let mut account = rpc_client.get_account(pubkey).await.map_err(|e| { + IngesterError::ParserError(format!("RPC error fetching tree {}: {}", pubkey, e)) + })?; + match tree_metadata_sync::process_tree_account(conn, *pubkey, &mut account, slot).await { + Ok(true) => { + log::info!("Discovered and synced new tree: {}", pubkey); + TreeInfo::get_by_pubkey(conn, pubkey) + .await + .map_err(|e| IngesterError::ParserError(e.to_string())) + } + Ok(false) => Ok(None), + Err(e) => { + log::warn!("Failed to process discovered tree {}: {}", pubkey, e); + Ok(None) + } + } +} diff --git a/src/ingester/parser/tx_event_parser.rs b/src/ingester/parser/tx_event_parser.rs index c7c7662b..7e2eb124 100644 --- a/src/ingester/parser/tx_event_parser.rs +++ b/src/ingester/parser/tx_event_parser.rs @@ -2,12 +2,13 @@ use crate::common::typedefs::account::AccountWithContext; use crate::ingester::error::IngesterError; use crate::ingester::parser::indexer_events::PublicTransactionEvent; use crate::ingester::parser::state_update::{AccountTransaction, StateUpdate}; -use crate::ingester::parser::tree_info::TreeInfo; +use crate::ingester::parser::tree_info::{discover_tree, TreeInfo}; use crate::ingester::parser::{get_compression_program_id, NOOP_PROGRAM_ID}; use crate::ingester::typedefs::block_info::{Instruction, TransactionInfo}; use borsh::BorshDeserialize; use light_compressed_account::TreeType; use log::info; +use solana_client::nonblocking::rpc_client::RpcClient; use solana_signature::Signature; use std::collections::HashMap; @@ -17,6 +18,7 @@ pub async fn parse_public_transaction_event_v1( slot: u64, compression_instruction: &Instruction, noop_instruction: &Instruction, + rpc_client: &RpcClient, ) -> Result, IngesterError> where T: sea_orm::ConnectionTrait + sea_orm::TransactionTrait, @@ -39,9 +41,15 @@ where e )) })?; - create_state_update_v1(conn, tx.signature, slot, public_transaction_event.into()) - .await - .map(Some) + create_state_update_v1( + conn, + tx.signature, + slot, + public_transaction_event.into(), + rpc_client, + ) + .await + .map(Some) } else { Ok(None) } @@ -52,6 +60,7 @@ pub async fn create_state_update_v1( tx: Signature, slot: u64, transaction_event: PublicTransactionEvent, + rpc_client: &RpcClient, ) -> Result where T: sea_orm::ConnectionTrait + sea_orm::TransactionTrait, @@ -79,18 +88,26 @@ where .await .map_err(|e| IngesterError::ParserError(format!("Failed to get tree info: {}", e)))? { - Some(info) => info, - None => { - if super::SKIP_UNKNOWN_TREES { - log::warn!("Skipping unknown tree: {}", tree_solana); + Some(info) => { + log::debug!( + "tx_event_parser tree={}, tree_type={:?}, queue={}", + tree_solana, + info.tree_type, + info.queue + ); + info + } + None => match discover_tree(rpc_client, conn, &tree_solana, slot).await { + Ok(Some(info)) => info, + Ok(None) => { + log::debug!("Tree {} not discoverable, skipping", tree_solana); continue; - } else { - return Err(IngesterError::ParserError(format!( - "Missing queue for tree: {}", - tree_solana - ))); } - } + Err(e) => { + log::warn!("Failed to discover tree {}: {}", tree_solana, e); + continue; + } + }, }; let mut seq = None; diff --git a/src/ingester/parser/tx_event_parser_v2.rs b/src/ingester/parser/tx_event_parser_v2.rs index e879c8ba..6c536071 100644 --- a/src/ingester/parser/tx_event_parser_v2.rs +++ b/src/ingester/parser/tx_event_parser_v2.rs @@ -7,6 +7,7 @@ use crate::ingester::parser::indexer_events::{ }; use crate::ingester::parser::state_update::{AccountTransaction, StateUpdate}; use crate::ingester::parser::tx_event_parser::create_state_update_v1; +use solana_client::nonblocking::rpc_client::RpcClient; use super::state_update::AddressQueueUpdate; use crate::common::typedefs::hash::Hash; @@ -126,6 +127,7 @@ pub async fn create_state_update_v2( tx: Signature, slot: u64, transaction_event: Vec, + rpc_client: &RpcClient, ) -> Result where T: sea_orm::ConnectionTrait + sea_orm::TransactionTrait, @@ -136,7 +138,7 @@ where let mut state_updates = Vec::new(); for event in transaction_event.iter() { let mut state_update_event = - create_state_update_v1(conn, tx, slot, event.clone().event).await?; + create_state_update_v1(conn, tx, slot, event.clone().event, rpc_client).await?; state_update_event .batch_nullify_context diff --git a/src/monitor/mod.rs b/src/monitor/mod.rs index faf3b57c..45071def 100644 --- a/src/monitor/mod.rs +++ b/src/monitor/mod.rs @@ -201,7 +201,7 @@ async fn load_db_tree_roots_with_infinite_retry(db: &DatabaseConnection) -> Vec< Hash::try_from(model.hash.clone()).unwrap(), ) }) - .collect() + .collect(); } Err(e) => { log::error!("Error loading tree roots: {}", e); diff --git a/src/monitor/tree_metadata_sync.rs b/src/monitor/tree_metadata_sync.rs index a0e2d0e7..4e12fcaa 100644 --- a/src/monitor/tree_metadata_sync.rs +++ b/src/monitor/tree_metadata_sync.rs @@ -78,12 +78,15 @@ pub async fn sync_tree_metadata( Ok(()) } -pub async fn process_tree_account( - db: &DatabaseConnection, +pub async fn process_tree_account( + db: &C, pubkey: Pubkey, account: &mut Account, slot: u64, -) -> Result { +) -> Result +where + C: ConnectionTrait, +{ if let Ok(data) = process_v1_state_account(account) { if !check_tree_owner(&data.owner) { debug!( diff --git a/tests/integration_tests/batched_state_tree_tests.rs b/tests/integration_tests/batched_state_tree_tests.rs index f910396b..6a6dc3f3 100644 --- a/tests/integration_tests/batched_state_tree_tests.rs +++ b/tests/integration_tests/batched_state_tree_tests.rs @@ -102,6 +102,7 @@ async fn test_batched_tree_transactions( &setup.db_conn, SerializableSignature(Signature::from_str(signature).unwrap()), transaction, + setup.client.as_ref(), ) .await .unwrap() @@ -734,6 +735,7 @@ async fn test_batched_tree_token_transactions( &setup.db_conn, SerializableSignature(Signature::from_str(&signatures[1]).unwrap()), transaction, + setup.client.as_ref(), ) .await .unwrap() diff --git a/tests/integration_tests/e2e_tests.rs b/tests/integration_tests/e2e_tests.rs index 59c523eb..a3fd9625 100644 --- a/tests/integration_tests/e2e_tests.rs +++ b/tests/integration_tests/e2e_tests.rs @@ -79,6 +79,7 @@ fn all_indexing_methodologies( }, ..Default::default() }, + rpc_client.as_ref(), ).await .unwrap(); @@ -263,14 +264,14 @@ async fn test_e2e_mint_and_transfer_transactions( cached_fetch_transaction(&setup.name, setup.client.clone(), txn_signature).await; let txn_signature = SerializableSignature(Signature::from_str(txn_signature).unwrap()); // Test get transaction - let parsed_transaction: photon_indexer::api::method::get_transaction_with_compression_info::GetTransactionResponse = get_transaction_helper(&setup.db_conn, txn_signature.clone(), txn).await.unwrap(); + let parsed_transaction: photon_indexer::api::method::get_transaction_with_compression_info::GetTransactionResponse = get_transaction_helper(&setup.db_conn, txn_signature.clone(), txn, setup.client.as_ref()).await.unwrap(); assert_json_snapshot!( format!("{}-{}-transaction", name.clone(), txn_name), parsed_transaction ); // V2 Test for Transactions - let parsed_transaction_v2: photon_indexer::api::method::get_transaction_with_compression_info::GetTransactionResponseV2 = get_transaction_helper_v2(&setup.db_conn, txn_signature, txn_clone).await.unwrap(); + let parsed_transaction_v2: photon_indexer::api::method::get_transaction_with_compression_info::GetTransactionResponseV2 = get_transaction_helper_v2(&setup.db_conn, txn_signature, txn_clone, setup.client.as_ref()).await.unwrap(); assert_json_snapshot!( format!("{}-{}-transaction-v2", name.clone(), txn_name), parsed_transaction_v2 @@ -498,7 +499,9 @@ async fn test_index_block_metadata( let slot = 254170887; let block = cached_fetch_block(&setup.name, setup.client.clone(), slot).await; - index_block(&setup.db_conn, &block).await.unwrap(); + index_block(&setup.db_conn, &block, setup.client.as_ref()) + .await + .unwrap(); let filter = blocks::Column::Slot.eq(block.metadata.slot); let block_model = blocks::Entity::find() @@ -524,12 +527,16 @@ async fn test_index_block_metadata( assert_eq!(block_model.block_time, 1710441678); // Verify that we don't get an error if we try to index the same block again - index_block(&setup.db_conn, &block).await.unwrap(); + index_block(&setup.db_conn, &block, setup.client.as_ref()) + .await + .unwrap(); assert_eq!(setup.api.get_indexer_slot().await.unwrap().0, slot); // Verify that get_indexer_slot() gets updated a new block is indexed. let block = cached_fetch_block(&setup.name, setup.client.clone(), slot + 1).await; - index_block(&setup.db_conn, &block).await.unwrap(); + index_block(&setup.db_conn, &block, setup.client.as_ref()) + .await + .unwrap(); assert_eq!(setup.api.get_indexer_slot().await.unwrap().0, slot + 1); } @@ -552,7 +559,9 @@ async fn test_get_latest_non_voting_signatures( let slot = 270893658; let block = cached_fetch_block(&setup.name, setup.client.clone(), slot).await; - index_block(&setup.db_conn, &block).await.unwrap(); + index_block(&setup.db_conn, &block, setup.client.as_ref()) + .await + .unwrap(); let all_nonvoting_transactions = setup .api .get_latest_non_voting_signatures(GetLatestSignaturesRequest { @@ -587,7 +596,9 @@ async fn test_get_latest_non_voting_signatures_with_failures( let slot = 279620356; let block = cached_fetch_block(&setup.name, setup.client.clone(), slot).await; - index_block(&setup.db_conn, &block).await.unwrap(); + index_block(&setup.db_conn, &block, setup.client.as_ref()) + .await + .unwrap(); let all_nonvoting_transactions = setup .api .get_latest_non_voting_signatures(GetLatestSignaturesRequest { @@ -715,9 +726,10 @@ async fn test_transaction_with_tree_rollover_fee( "2cBtegqLxQztcngNF4qWGZYEuGiwFvmSpak4dqNaGHHQRDBGuYg24ZSG54BpRaWS5Cr4v6AWLV42FWvEjQk2ESWy"; let txn = cached_fetch_transaction(&name, setup.client.clone(), txn).await; let tx_info: TransactionInfo = txn.try_into().unwrap(); - let status_update = parse_transaction(setup.db_conn.as_ref(), &tx_info, 0) - .await - .unwrap(); + let status_update = + parse_transaction(setup.db_conn.as_ref(), &tx_info, 0, setup.client.as_ref()) + .await + .unwrap(); // Assert that status update has at least one account assert!(status_update.out_accounts.len() > 0); } @@ -755,9 +767,14 @@ async fn test_index_compress_and_close_with_tlv( let txn = cached_fetch_transaction(&name, setup.client.clone(), txn_sig).await; let tx_info: TransactionInfo = txn.try_into().unwrap(); - let state_update = parse_transaction(setup.db_conn.as_ref(), &tx_info, slot) - .await - .expect("parse_transaction must not fail on CompressAndClose with TLV"); + let state_update = parse_transaction( + setup.db_conn.as_ref(), + &tx_info, + slot, + setup.client.as_ref(), + ) + .await + .expect("parse_transaction must not fail on CompressAndClose with TLV"); assert!( !state_update.out_accounts.is_empty(), diff --git a/tests/integration_tests/mock_tests.rs b/tests/integration_tests/mock_tests.rs index 90dbd59a..dd268669 100644 --- a/tests/integration_tests/mock_tests.rs +++ b/tests/integration_tests/mock_tests.rs @@ -74,6 +74,7 @@ async fn test_persist_state_update_basic( }, ..Default::default() }, + setup.client.as_ref(), ) .await .unwrap(); @@ -164,6 +165,7 @@ async fn test_multiple_accounts( }, ..Default::default() }, + setup.client.as_ref(), ) .await .unwrap(); @@ -447,6 +449,7 @@ async fn test_persist_token_data( }, ..Default::default() }, + setup.client.as_ref(), ) .await .unwrap(); @@ -1094,6 +1097,7 @@ async fn test_get_multiple_new_address_proofs( }, ..Default::default() }, + setup.client.as_ref(), ) .await .unwrap(); @@ -1136,6 +1140,7 @@ async fn test_get_multiple_new_address_proofs_interop( }, ..Default::default() }, + setup.client.as_ref(), ) .await .unwrap(); @@ -1217,6 +1222,7 @@ async fn load_test(#[values(DatabaseBackend::Postgres)] db_backend: DatabaseBack }, ..Default::default() }, + setup.client.as_ref(), ) .await .unwrap(); @@ -1283,6 +1289,7 @@ async fn test_persisted_state_trees_bug_with_latter_smaller_seq_values( }, ..Default::default() }, + setup.client.as_ref(), ) .await .unwrap(); @@ -1369,6 +1376,7 @@ async fn test_gpa_filters( }, ..Default::default() }, + setup.client.as_ref(), ) .await .unwrap(); diff --git a/tests/integration_tests/utils.rs b/tests/integration_tests/utils.rs index 56379358..4ea0738e 100644 --- a/tests/integration_tests/utils.rs +++ b/tests/integration_tests/utils.rs @@ -554,9 +554,9 @@ pub async fn index_transaction( rpc_client: Arc, tx: &str, ) { - let tx = cached_fetch_transaction(test_name, rpc_client, tx).await; - let tx_info: TransactionInfo = tx.try_into().unwrap(); - let state_update = parse_transaction(db_conn.as_ref(), &tx_info, 0) + let tx_data = cached_fetch_transaction(test_name, rpc_client.clone(), tx).await; + let tx_info: TransactionInfo = tx_data.try_into().unwrap(); + let state_update = parse_transaction(db_conn.as_ref(), &tx_info, 0, rpc_client.as_ref()) .await .unwrap(); persist_state_update_using_connection(db_conn.as_ref(), state_update) @@ -577,9 +577,10 @@ pub async fn index_multiple_transactions( } let mut state_updates = Vec::new(); for transaction_info in transactions_infos { - let tx_state_update = parse_transaction(db_conn.as_ref(), &transaction_info, 0) - .await - .unwrap(); + let tx_state_update = + parse_transaction(db_conn.as_ref(), &transaction_info, 0, rpc_client.as_ref()) + .await + .unwrap(); state_updates.push(tx_state_update); } let state_update = StateUpdate::merge_updates(state_updates); @@ -672,6 +673,7 @@ pub async fn index( }, ..Default::default() }, + rpc_client.as_ref(), ) .await .unwrap(); From 9f5190421ebc890d39fadfe34bc0579996ae45da Mon Sep 17 00:00:00 2001 From: Sergey Timoshin Date: Wed, 4 Mar 2026 13:58:21 +0000 Subject: [PATCH 03/12] fix: skip unknown trees in root validation monitor Filter load_db_tree_roots to only check trees that exist in tree_metadata (which already filters by EXPECTED_TREE_OWNER). This suppresses noisy root mismatch errors for external trees. --- src/monitor/mod.rs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/monitor/mod.rs b/src/monitor/mod.rs index 45071def..b88b4107 100644 --- a/src/monitor/mod.rs +++ b/src/monitor/mod.rs @@ -193,8 +193,26 @@ async fn load_db_tree_roots_with_infinite_retry(db: &DatabaseConnection) -> Vec< .await; match models { Ok(models) => { + // Filter to only include trees that exist in tree_metadata + // (which already filters by EXPECTED_TREE_OWNER during sync). + // This avoids root mismatch errors for unknown/external trees. + let known_trees = { + use crate::dao::generated::tree_metadata; + match tree_metadata::Entity::find().all(db).await { + Ok(metadata) => metadata + .into_iter() + .map(|m| m.tree_pubkey) + .collect::>(), + Err(e) => { + log::error!("Error loading tree metadata: {}", e); + tokio::time::sleep(tokio::time::Duration::from_millis(100)).await; + continue; + } + } + }; return models .iter() + .filter(|model| known_trees.contains(&model.tree)) .map(|model| { ( Pubkey::try_from(model.tree.clone()).unwrap(), From aef15b2eb85f2e9a074590117370ea8fcfc57198 Mon Sep 17 00:00:00 2001 From: Sergey Timoshin Date: Tue, 3 Mar 2026 14:10:01 +0000 Subject: [PATCH 04/12] feat: infrastructure updates - migrations, ingester, prover API key, DAO schema, monitor, CLI args, dep updates - Add prover_api_key parameter to PhotonApi and validity proof methods - Add max_connections parameter to run_server - Add new migrations: onchain_pubkey, ata_owner, backfill_mint, ata_owner_index - Update ingester with startup cleanup, improved parsing, and persist logic - Update DAO generated models (accounts, token_accounts, queue_hash_chains, transactions) - Update monitor with queue monitoring and tree metadata sync improvements - Consolidate OpenAPI specs into single api.yaml - Fix OpenAPI AllOf handling and iterator usage - Update dependencies in Cargo.toml/Cargo.lock - Add token_layout module and update token_data types - Update snapshot loader/snapshotter and tree validator --- Cargo.lock | 2029 ++++++++++++++--- Cargo.toml | 21 +- src/api/api.rs | 19 +- .../get_compressed_accounts_by_owner/v1.rs | 2 +- .../get_compressed_accounts_by_owner/v2.rs | 2 +- .../get_compressed_token_balances_by_owner.rs | 3 +- .../method/get_multiple_new_address_proofs.rs | 4 +- src/api/method/get_queue_elements.rs | 82 +- .../get_validity_proof/prover/helpers.rs | 64 +- .../method/get_validity_proof/prover/prove.rs | 18 +- .../get_validity_proof/prover/structs.rs | 33 +- src/api/method/get_validity_proof/v1.rs | 5 +- src/api/method/get_validity_proof/v2.rs | 4 +- src/api/method/utils.rs | 6 + src/api/rpc_server.rs | 7 +- src/common/mod.rs | 1 + src/common/token_layout.rs | 18 + src/common/typedefs/account/context.rs | 9 +- src/common/typedefs/account/mod.rs | 5 +- src/common/typedefs/account/v1.rs | 17 +- src/common/typedefs/account/v2.rs | 25 +- src/common/typedefs/token_data.rs | 325 +-- src/dao/generated/accounts.rs | 15 +- src/dao/generated/queue_hash_chains.rs | 2 + src/dao/generated/token_accounts.rs | 1 + src/dao/generated/transactions.rs | 8 + src/dao/helpers.rs | 82 +- src/ingester/fetchers/mod.rs | 14 +- src/ingester/fetchers/poller.rs | 6 +- src/ingester/indexer/mod.rs | 8 +- src/ingester/mod.rs | 1 + src/ingester/parser/indexer_events.rs | 63 +- src/ingester/parser/mod.rs | 77 +- src/ingester/parser/state_update.rs | 5 + src/ingester/parser/tx_event_parser.rs | 52 +- src/ingester/parser/tx_event_parser_v2.rs | 8 +- src/ingester/persist/mod.rs | 143 +- .../persist/persisted_batch_event/address.rs | 2 +- .../persist/persisted_batch_event/append.rs | 4 +- .../persist/persisted_batch_event/mod.rs | 8 +- .../persist/persisted_batch_event/nullify.rs | 10 +- .../persist/persisted_indexed_merkle_tree.rs | 8 +- src/ingester/persist/persisted_state_tree.rs | 6 +- src/ingester/persist/spend.rs | 2 +- src/ingester/startup_cleanup.rs | 138 ++ src/ingester/typedefs/block_info.rs | 8 +- src/main.rs | 47 +- .../custom/custom20250211_000002_solayer2.rs | 4 +- .../custom/custom20252201_000001_init.rs | 4 +- .../standard/m20220101_000001_init.rs | 2 +- .../standard/m20240914_000005_init.rs | 2 +- .../standard/m20241008_000006_init.rs | 2 +- .../standard/m20250206_000007_init.rs | 2 +- ...1_000001_optimize_nullifier_queue_index.rs | 2 +- .../m20260127_000001_add_onchain_pubkey.rs | 57 + .../m20260201_000002_add_ata_owner.rs | 35 + ...210_000001_backfill_mint_onchain_pubkey.rs | 131 ++ .../m20260210_000002_add_ata_owner_index.rs | 56 + src/migration/migrations/standard/mod.rs | 9 +- src/migration/model/table.rs | 2 + src/monitor/mod.rs | 39 +- src/monitor/queue_monitor.rs | 35 +- src/monitor/tree_metadata_sync.rs | 14 +- src/openapi/mod.rs | 5 +- src/openapi/specs/api.yaml | 721 ++++-- src/openapi/specs/getCompressedAccount.yaml | 165 -- .../specs/getCompressedAccountBalance.yaml | 117 - .../specs/getCompressedAccountProof.yaml | 130 -- .../specs/getCompressedAccountsByOwner.yaml | 787 ------- .../specs/getCompressedBalanceByOwner.yaml | 1143 ---------- .../specs/getCompressedMintTokenHolders.yaml | 140 -- .../getCompressedTokenAccountBalance.yaml | 931 -------- .../getCompressedTokenAccountsByDelegate.yaml | 671 ------ .../getCompressedTokenAccountsByOwner.yaml | 552 ----- .../getCompressedTokenBalancesByOwner.yaml | 1292 ----------- .../getCompressedTokenBalancesByOwnerV2.yaml | 144 -- .../getCompressionSignaturesForAccount.yaml | 1515 ------------ .../getCompressionSignaturesForAddress.yaml | 1637 ------------- .../getCompressionSignaturesForOwner.yaml | 1750 -------------- ...getCompressionSignaturesForTokenOwner.yaml | 1863 --------------- src/openapi/specs/getIndexerHealth.yaml | 69 - src/openapi/specs/getIndexerSlot.yaml | 67 - .../specs/getLatestCompressionSignatures.yaml | 139 -- .../specs/getLatestNonVotingSignatures.yaml | 139 -- .../getMultipleCompressedAccountProofs.yaml | 129 -- .../specs/getMultipleCompressedAccounts.yaml | 866 ------- .../specs/getMultipleNewAddressProofs.yaml | 139 -- .../specs/getMultipleNewAddressProofsV2.yaml | 150 -- src/openapi/specs/getQueueElements.yaml | 89 - .../getTransactionWithCompressionInfo.yaml | 1569 ------------- src/openapi/specs/getValidityProof.yaml | 169 -- src/openapi/specs/openapitools.json | 7 - src/snapshot/loader/main.rs | 2 +- src/snapshot/mod.rs | 5 +- src/snapshot/snapshotter/main.rs | 2 +- src/tools/tree_validator/main.rs | 1 - .../batched_state_tree_tests.rs | 29 +- tests/integration_tests/e2e_tests.rs | 3 +- tests/integration_tests/mock_tests.rs | 34 +- tests/integration_tests/prod_tests.rs | 4 +- tests/integration_tests/utils.rs | 28 +- 101 files changed, 3681 insertions(+), 17335 deletions(-) create mode 100644 src/common/token_layout.rs create mode 100644 src/ingester/startup_cleanup.rs create mode 100644 src/migration/migrations/standard/m20260127_000001_add_onchain_pubkey.rs create mode 100644 src/migration/migrations/standard/m20260201_000002_add_ata_owner.rs create mode 100644 src/migration/migrations/standard/m20260210_000001_backfill_mint_onchain_pubkey.rs create mode 100644 src/migration/migrations/standard/m20260210_000002_add_ata_owner_index.rs delete mode 100644 src/openapi/specs/getCompressedAccount.yaml delete mode 100644 src/openapi/specs/getCompressedAccountBalance.yaml delete mode 100644 src/openapi/specs/getCompressedAccountProof.yaml delete mode 100644 src/openapi/specs/getCompressedAccountsByOwner.yaml delete mode 100644 src/openapi/specs/getCompressedBalanceByOwner.yaml delete mode 100644 src/openapi/specs/getCompressedMintTokenHolders.yaml delete mode 100644 src/openapi/specs/getCompressedTokenAccountBalance.yaml delete mode 100644 src/openapi/specs/getCompressedTokenAccountsByDelegate.yaml delete mode 100644 src/openapi/specs/getCompressedTokenAccountsByOwner.yaml delete mode 100644 src/openapi/specs/getCompressedTokenBalancesByOwner.yaml delete mode 100644 src/openapi/specs/getCompressedTokenBalancesByOwnerV2.yaml delete mode 100644 src/openapi/specs/getCompressionSignaturesForAccount.yaml delete mode 100644 src/openapi/specs/getCompressionSignaturesForAddress.yaml delete mode 100644 src/openapi/specs/getCompressionSignaturesForOwner.yaml delete mode 100644 src/openapi/specs/getCompressionSignaturesForTokenOwner.yaml delete mode 100644 src/openapi/specs/getIndexerHealth.yaml delete mode 100644 src/openapi/specs/getIndexerSlot.yaml delete mode 100644 src/openapi/specs/getLatestCompressionSignatures.yaml delete mode 100644 src/openapi/specs/getLatestNonVotingSignatures.yaml delete mode 100644 src/openapi/specs/getMultipleCompressedAccountProofs.yaml delete mode 100644 src/openapi/specs/getMultipleCompressedAccounts.yaml delete mode 100644 src/openapi/specs/getMultipleNewAddressProofs.yaml delete mode 100644 src/openapi/specs/getMultipleNewAddressProofsV2.yaml delete mode 100644 src/openapi/specs/getQueueElements.yaml delete mode 100644 src/openapi/specs/getTransactionWithCompressionInfo.yaml delete mode 100644 src/openapi/specs/getValidityProof.yaml delete mode 100644 src/openapi/specs/openapitools.json diff --git a/Cargo.lock b/Cargo.lock index 2c8641f3..7b1d9aea 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -61,7 +61,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6b71300ed93a9dff1c3231c3f1417e242e3da38529ebc32f828bc8560bf4a2a" dependencies = [ "ahash 0.8.12", - "solana-epoch-schedule", + "solana-epoch-schedule 3.0.0", "solana-hash 3.1.0", "solana-pubkey 3.0.0", "solana-sha256-hasher 3.1.0", @@ -76,7 +76,7 @@ checksum = "ac34d0410a2a015df7d45d092449c7ec59264081d05f18c7f305ccf7c81bd3b7" dependencies = [ "agave-feature-set", "solana-pubkey 3.0.0", - "solana-sdk-ids", + "solana-sdk-ids 3.1.0", ] [[package]] @@ -273,7 +273,7 @@ dependencies = [ "ark-std 0.5.0", "educe", "fnv", - "hashbrown 0.15.5", + "hashbrown 0.15.2", "itertools 0.13.0", "num-bigint 0.4.6", "num-integer", @@ -392,7 +392,7 @@ dependencies = [ "ark-std 0.5.0", "educe", "fnv", - "hashbrown 0.15.5", + "hashbrown 0.15.2", ] [[package]] @@ -1444,6 +1444,26 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "console_error_panic_hook" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc" +dependencies = [ + "cfg-if", + "wasm-bindgen", +] + +[[package]] +name = "console_log" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e89f72f65e8501878b8a004d5a1afb780987e2ce2b4532c562e367a72c57499f" +dependencies = [ + "log", + "web-sys", +] + [[package]] name = "const-oid" version = "0.9.6" @@ -1874,7 +1894,7 @@ dependencies = [ "ed25519", "rand_core 0.6.4", "serde", - "sha2", + "sha2 0.10.9", "subtle", "zeroize", ] @@ -2096,6 +2116,12 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + [[package]] name = "foreign-types" version = "0.3.2" @@ -2280,6 +2306,17 @@ dependencies = [ "winapi", ] +[[package]] +name = "getrandom" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.9.0+wasi-snapshot-preview1", +] + [[package]] name = "getrandom" version = "0.2.16" @@ -2289,7 +2326,7 @@ dependencies = [ "cfg-if", "js-sys", "libc", - "wasi", + "wasi 0.11.1+wasi-snapshot-preview1", "wasm-bindgen", ] @@ -2450,11 +2487,13 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.15.5" +version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" dependencies = [ "allocator-api2", + "equivalent", + "foldhash", ] [[package]] @@ -3257,6 +3296,52 @@ dependencies = [ "redox_syscall 0.5.18", ] +[[package]] +name = "libsecp256k1" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9d220bc1feda2ac231cb78c3d26f27676b8cf82c96971f7aeef3d0cf2797c73" +dependencies = [ + "arrayref", + "base64 0.12.3", + "digest 0.9.0", + "libsecp256k1-core", + "libsecp256k1-gen-ecmult", + "libsecp256k1-gen-genmult", + "rand 0.7.3", + "serde", + "sha2 0.9.9", +] + +[[package]] +name = "libsecp256k1-core" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0f6ab710cec28cef759c5f18671a27dae2a5f952cdaaee1d8e2908cb2478a80" +dependencies = [ + "crunchy", + "digest 0.9.0", + "subtle", +] + +[[package]] +name = "libsecp256k1-gen-ecmult" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccab96b584d38fac86a83f07e659f0deafd0253dc096dab5a36d53efe653c5c3" +dependencies = [ + "libsecp256k1-core", +] + +[[package]] +name = "libsecp256k1-gen-genmult" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67abfe149395e3aa1c48a2beb32b068e2334402df8181f818d3aee2b304c4f5d" +dependencies = [ + "libsecp256k1-core", +] + [[package]] name = "libsqlite3-sys" version = "0.24.2" @@ -3270,18 +3355,27 @@ dependencies = [ [[package]] name = "light-account-checks" -version = "0.5.1" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e70bd1a384bff312860d4b60a0ccd814c36b88ce91966192dd88a35e595f7a2" +checksum = "34d74dd13535c6014abb4cac694e14083b206a7f6cf1bbbc9611aa5c2e11cdd1" dependencies = [ "thiserror 2.0.17", ] +[[package]] +name = "light-array-map" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bdd13b18028ac9d80d0a987551c0dad7fe81be8140e87cc9d568b80f3728203" +dependencies = [ + "tinyvec", +] + [[package]] name = "light-batched-merkle-tree" -version = "0.6.1" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "421977e990b1edb39a0973ed0d0e919313517585e5dcb0e8faded1396c7cce12" +checksum = "bb07ef41d9d99db94072d556767a93d774323bc34940db349555abc4504de015" dependencies = [ "aligned-sized", "borsh 0.10.4", @@ -3299,9 +3393,9 @@ dependencies = [ [[package]] name = "light-bloom-filter" -version = "0.5.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8a609e3c9179f0ae8488cc70c5413c86dfd97dad7ad85fee2ad8da2d0a11e61" +checksum = "380b8cf734ccf5fbc1f5fed8e5308b57ebde6774d9304c167bcb0de2854412d8" dependencies = [ "bitvec", "num-bigint 0.4.6", @@ -3322,9 +3416,9 @@ dependencies = [ [[package]] name = "light-compressed-account" -version = "0.6.2" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a9cecda1680283ecba0f8ef64073d36998f057ac5aa96569dc8e74d6a37a3a2" +checksum = "adab5a8dddd9675306b0a808900578ed60b40b5c5457e330e28c9cb51361118d" dependencies = [ "borsh 0.10.4", "bytemuck", @@ -3337,6 +3431,28 @@ dependencies = [ "zerocopy", ] +[[package]] +name = "light-compressible" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0905b52848daea4cb7edbc9ddca0a8d5c4f1d864e89eff0aca3b6fc2a9edbb2" +dependencies = [ + "aligned-sized", + "borsh 0.10.4", + "bytemuck", + "light-account-checks", + "light-compressed-account", + "light-hasher", + "light-macros", + "light-program-profiler", + "light-zero-copy", + "pinocchio-pubkey", + "solana-pubkey 2.4.0", + "solana-rent 2.2.1", + "thiserror 2.0.17", + "zerocopy", +] + [[package]] name = "light-concurrent-merkle-tree" version = "4.0.1" @@ -3352,13 +3468,14 @@ dependencies = [ [[package]] name = "light-event" -version = "0.1.1" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "554228ce821d9c3b789f1c675f79663fe3872c77a3527e1e20e5f2f268eeb02d" +checksum = "ae607b61749246439e9e302a75dc565c98d6b9a81efcb1c6dd5e755a24b427ab" dependencies = [ "borsh 0.10.4", "light-compressed-account", "light-hasher", + "light-token-interface", "light-zero-copy", "thiserror 2.0.17", ] @@ -3372,9 +3489,9 @@ dependencies = [ "ark-bn254 0.5.0", "ark-ff 0.5.0", "borsh 0.10.4", - "light-poseidon", + "light-poseidon 0.3.0", "num-bigint 0.4.6", - "sha2", + "sha2 0.10.9", "sha3", "thiserror 2.0.17", "tinyvec", @@ -3422,9 +3539,9 @@ dependencies = [ [[package]] name = "light-merkle-tree-metadata" -version = "0.6.0" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6474045d3358e952f11a6cec44b9280ae127446a2b8246b3fdb29b83fda4e6b7" +checksum = "7623246ad0015345f6a88471f554381e24635b770fe4b15b38384daddf1d2f5f" dependencies = [ "borsh 0.10.4", "bytemuck", @@ -3458,6 +3575,18 @@ dependencies = [ "thiserror 1.0.69", ] +[[package]] +name = "light-poseidon" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47a1ccadd0bb5a32c196da536fd72c59183de24a055f6bf0513bf845fefab862" +dependencies = [ + "ark-bn254 0.5.0", + "ark-ff 0.5.0", + "num-bigint 0.4.6", + "thiserror 1.0.69", +] + [[package]] name = "light-profiler-macro" version = "0.1.1" @@ -3478,11 +3607,55 @@ dependencies = [ "light-profiler-macro", ] +[[package]] +name = "light-sdk-types" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d60036a8f1af0d3aa651780aa57ae2cdca3fa0f16acecda71c98f81a53e37c9" +dependencies = [ + "borsh 0.10.4", + "bytemuck", + "light-account-checks", + "light-compressed-account", + "light-compressible", + "light-hasher", + "light-macros", + "solana-msg 2.2.1", + "thiserror 2.0.17", +] + +[[package]] +name = "light-token-interface" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a457179188d9b6b7208a8a63a1fea851f818adfde3faaa262474bdcd3e3c7d" +dependencies = [ + "aligned-sized", + "borsh 0.10.4", + "bytemuck", + "light-array-map", + "light-compressed-account", + "light-compressible", + "light-hasher", + "light-macros", + "light-program-profiler", + "light-zero-copy", + "pinocchio", + "pinocchio-pubkey", + "solana-account-info 2.3.0", + "solana-pubkey 2.4.0", + "spl-pod 0.5.1", + "spl-token-2022", + "thiserror 2.0.17", + "tinyvec", + "zerocopy", +] + [[package]] name = "light-verifier" -version = "5.0.0" +version = "9.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da621c1996c02f10a263eb5a26f03ad34c074f3f07abf566318985f53f5a26d9" +checksum = "65a33dc6b302f2cfb7319006f1234f1d86c992ac1991ade7c97653828b7f1735" dependencies = [ "groth16-solana", "light-compressed-account", @@ -3491,9 +3664,9 @@ dependencies = [ [[package]] name = "light-zero-copy" -version = "0.5.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a8862f463792fd60ae8f5dc418150c16213e302e19d54fba0694cf8515be5ff" +checksum = "a5621fb515e14af46148699c0b65334aabe230a1d2cbd06736ccc7a408c8a4af" dependencies = [ "light-zero-copy-derive", "zerocopy", @@ -3501,9 +3674,9 @@ dependencies = [ [[package]] name = "light-zero-copy-derive" -version = "0.5.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8af086d52100b3cab1f2993b146adc7a69fa6aaa878ae4c19514c77c50304379" +checksum = "41c46425e5c7ab5203ff5c86ae2615b169cca55f9283f5f60f5dd74143be6934" dependencies = [ "lazy_static", "proc-macro2", @@ -3664,7 +3837,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69d83b0086dc8ecf3ce9ae2874b2d1290252e2a30720bea58a5c6639b0092873" dependencies = [ "libc", - "wasi", + "wasi 0.11.1+wasi-snapshot-preview1", "windows-sys 0.61.2", ] @@ -4150,7 +4323,9 @@ dependencies = [ "light-indexed-merkle-tree", "light-merkle-tree-metadata", "light-merkle-tree-reference", - "light-poseidon", + "light-poseidon 0.4.0", + "light-sdk-types", + "light-token-interface", "light-zero-copy", "log", "num-bigint 0.4.6", @@ -4166,15 +4341,18 @@ dependencies = [ "serde", "serde_json", "serial_test", - "solana-account", + "solana-account 3.2.0", "solana-bn254 3.1.2", "solana-client", - "solana-clock", + "solana-clock 3.0.0", "solana-commitment-config", + "solana-program-option 3.0.0", + "solana-program-pack 3.0.0", "solana-pubkey 3.0.0", - "solana-signature", + "solana-signature 3.1.0", "solana-transaction", "solana-transaction-status", + "spl-token-interface", "sqlx", "thiserror 1.0.69", "tokio", @@ -4219,6 +4397,23 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +[[package]] +name = "pinocchio" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b971851087bc3699b001954ad02389d50c41405ece3548cbcafc88b3e20017a" + +[[package]] +name = "pinocchio-pubkey" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb0225638cadcbebae8932cb7f49cb5da7c15c21beb19f048f05a5ca7d93f065" +dependencies = [ + "five8_const 0.1.4", + "pinocchio", + "sha2-const-stable", +] + [[package]] name = "piper" version = "0.2.4" @@ -4495,7 +4690,7 @@ dependencies = [ "libc", "once_cell", "raw-cpuid", - "wasi", + "wasi 0.11.1+wasi-snapshot-preview1", "web-sys", "winapi", ] @@ -4588,6 +4783,19 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" +[[package]] +name = "rand" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" +dependencies = [ + "getrandom 0.1.16", + "libc", + "rand_chacha 0.2.2", + "rand_core 0.5.1", + "rand_hc", +] + [[package]] name = "rand" version = "0.8.5" @@ -4609,6 +4817,16 @@ dependencies = [ "rand_core 0.9.3", ] +[[package]] +name = "rand_chacha" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" +dependencies = [ + "ppv-lite86", + "rand_core 0.5.1", +] + [[package]] name = "rand_chacha" version = "0.3.1" @@ -4629,6 +4847,15 @@ dependencies = [ "rand_core 0.9.3", ] +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +dependencies = [ + "getrandom 0.1.16", +] + [[package]] name = "rand_core" version = "0.6.4" @@ -4647,6 +4874,15 @@ dependencies = [ "getrandom 0.3.4", ] +[[package]] +name = "rand_hc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +dependencies = [ + "rand_core 0.5.1", +] + [[package]] name = "raw-cpuid" version = "11.6.0" @@ -4980,7 +5216,7 @@ dependencies = [ "serde", "serde_derive", "serde_json", - "sha2", + "sha2 0.10.9", "thiserror 1.0.69", "time", "tokio", @@ -5579,6 +5815,19 @@ dependencies = [ "digest 0.10.7", ] +[[package]] +name = "sha2" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" +dependencies = [ + "block-buffer 0.9.0", + "cfg-if", + "cpufeatures", + "digest 0.9.0", + "opaque-debug", +] + [[package]] name = "sha2" version = "0.10.9" @@ -5590,6 +5839,12 @@ dependencies = [ "digest 0.10.7", ] +[[package]] +name = "sha2-const-stable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f179d4e11094a893b82fff208f74d448a7512f99f5a0acbd5c679b705f83ed9" + [[package]] name = "sha3" version = "0.10.8" @@ -5739,6 +5994,19 @@ dependencies = [ "sha-1", ] +[[package]] +name = "solana-account" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f949fe4edaeaea78c844023bfc1c898e0b1f5a100f8a8d2d0f85d0a7b090258" +dependencies = [ + "solana-account-info 2.3.0", + "solana-clock 2.2.2", + "solana-instruction 2.3.3", + "solana-pubkey 2.4.0", + "solana-sdk-ids 2.2.1", +] + [[package]] name = "solana-account" version = "3.2.0" @@ -5749,12 +6017,12 @@ dependencies = [ "serde", "serde_bytes", "serde_derive", - "solana-account-info", - "solana-clock", + "solana-account-info 3.1.0", + "solana-clock 3.0.0", "solana-instruction-error", "solana-pubkey 3.0.0", - "solana-sdk-ids", - "solana-sysvar", + "solana-sdk-ids 3.1.0", + "solana-sysvar 3.1.0", ] [[package]] @@ -5770,31 +6038,31 @@ dependencies = [ "bv", "serde", "serde_json", - "solana-account", + "solana-account 3.2.0", "solana-account-decoder-client-types", - "solana-address-lookup-table-interface", - "solana-clock", + "solana-address-lookup-table-interface 3.0.0", + "solana-clock 3.0.0", "solana-config-interface", - "solana-epoch-schedule", - "solana-fee-calculator", - "solana-instruction", - "solana-loader-v3-interface", - "solana-nonce", - "solana-program-option", - "solana-program-pack", + "solana-epoch-schedule 3.0.0", + "solana-fee-calculator 3.0.0", + "solana-instruction 3.1.0", + "solana-loader-v3-interface 6.1.0", + "solana-nonce 3.0.0", + "solana-program-option 3.0.0", + "solana-program-pack 3.0.0", "solana-pubkey 3.0.0", - "solana-rent", - "solana-sdk-ids", - "solana-slot-hashes", - "solana-slot-history", - "solana-stake-interface", - "solana-sysvar", - "solana-vote-interface", + "solana-rent 3.0.0", + "solana-sdk-ids 3.1.0", + "solana-slot-hashes 3.0.0", + "solana-slot-history 3.0.0", + "solana-stake-interface 2.0.1", + "solana-sysvar 3.1.0", + "solana-vote-interface 4.0.4", "spl-generic-token", "spl-token-2022-interface", - "spl-token-group-interface", + "spl-token-group-interface 0.7.1", "spl-token-interface", - "spl-token-metadata-interface", + "spl-token-metadata-interface 0.8.0", "thiserror 2.0.17", "zstd", ] @@ -5809,11 +6077,24 @@ dependencies = [ "bs58 0.5.1", "serde", "serde_json", - "solana-account", + "solana-account 3.2.0", "solana-pubkey 3.0.0", "zstd", ] +[[package]] +name = "solana-account-info" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8f5152a288ef1912300fc6efa6c2d1f9bb55d9398eb6c72326360b8063987da" +dependencies = [ + "bincode", + "serde", + "solana-program-error 2.2.2", + "solana-program-memory 2.3.1", + "solana-pubkey 2.4.0", +] + [[package]] name = "solana-account-info" version = "3.1.0" @@ -5821,8 +6102,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc3397241392f5756925029acaa8515dc70fcbe3d8059d4885d7d6533baf64fd" dependencies = [ "solana-address 2.0.0", - "solana-program-error", - "solana-program-memory", + "solana-program-error 3.0.0", + "solana-program-memory 3.1.0", ] [[package]] @@ -5850,11 +6131,28 @@ dependencies = [ "serde_derive", "solana-atomic-u64 3.0.0", "solana-define-syscall 4.0.1", - "solana-program-error", + "solana-program-error 3.0.0", "solana-sanitize 3.0.1", "solana-sha256-hasher 3.1.0", ] +[[package]] +name = "solana-address-lookup-table-interface" +version = "2.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1673f67efe870b64a65cb39e6194be5b26527691ce5922909939961a6e6b395" +dependencies = [ + "bincode", + "bytemuck", + "serde", + "serde_derive", + "solana-clock 2.2.2", + "solana-instruction 2.3.3", + "solana-pubkey 2.4.0", + "solana-sdk-ids 2.2.1", + "solana-slot-hashes 2.2.1", +] + [[package]] name = "solana-address-lookup-table-interface" version = "3.0.0" @@ -5865,12 +6163,12 @@ dependencies = [ "bytemuck", "serde", "serde_derive", - "solana-clock", - "solana-instruction", + "solana-clock 3.0.0", + "solana-instruction 3.1.0", "solana-instruction-error", "solana-pubkey 3.0.0", - "solana-sdk-ids", - "solana-slot-hashes", + "solana-sdk-ids 3.1.0", + "solana-slot-hashes 3.0.0", ] [[package]] @@ -5892,37 +6190,81 @@ dependencies = [ ] [[package]] -name = "solana-bn254" -version = "2.2.2" +name = "solana-big-mod-exp" +version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4420f125118732833f36facf96a27e7b78314b2d642ba07fa9ffdacd8d79e243" +checksum = "75db7f2bbac3e62cfd139065d15bcda9e2428883ba61fc8d27ccb251081e7567" dependencies = [ - "ark-bn254 0.4.0", - "ark-ec 0.4.2", - "ark-ff 0.4.2", - "ark-serialize 0.4.2", - "bytemuck", + "num-bigint 0.4.6", + "num-traits", "solana-define-syscall 2.3.0", - "thiserror 2.0.17", ] [[package]] -name = "solana-bn254" -version = "3.1.2" +name = "solana-bincode" +version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d08583be08d2d5f19aa21efbb6fbdb968ba7fd0de74562441437a7d776772bf" +checksum = "19a3787b8cf9c9fe3dd360800e8b70982b9e5a8af9e11c354b6665dd4a003adc" dependencies = [ - "ark-bn254 0.4.0", - "ark-ec 0.4.2", - "ark-ff 0.4.2", - "ark-serialize 0.4.2", - "bytemuck", - "solana-define-syscall 3.0.0", - "thiserror 2.0.17", + "bincode", + "serde", + "solana-instruction 2.3.3", ] [[package]] -name = "solana-borsh" +name = "solana-blake3-hasher" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1a0801e25a1b31a14494fc80882a036be0ffd290efc4c2d640bfcca120a4672" +dependencies = [ + "blake3", + "solana-define-syscall 2.3.0", + "solana-hash 2.3.0", + "solana-sanitize 2.2.1", +] + +[[package]] +name = "solana-bn254" +version = "2.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4420f125118732833f36facf96a27e7b78314b2d642ba07fa9ffdacd8d79e243" +dependencies = [ + "ark-bn254 0.4.0", + "ark-ec 0.4.2", + "ark-ff 0.4.2", + "ark-serialize 0.4.2", + "bytemuck", + "solana-define-syscall 2.3.0", + "thiserror 2.0.17", +] + +[[package]] +name = "solana-bn254" +version = "3.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d08583be08d2d5f19aa21efbb6fbdb968ba7fd0de74562441437a7d776772bf" +dependencies = [ + "ark-bn254 0.4.0", + "ark-ec 0.4.2", + "ark-ff 0.4.2", + "ark-serialize 0.4.2", + "bytemuck", + "solana-define-syscall 3.0.0", + "thiserror 2.0.17", +] + +[[package]] +name = "solana-borsh" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "718333bcd0a1a7aed6655aa66bef8d7fb047944922b2d3a18f49cbc13e73d004" +dependencies = [ + "borsh 0.10.4", + "borsh 1.5.7", +] + +[[package]] +name = "solana-borsh" version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc402b16657abbfa9991cd5cbfac5a11d809f7e7d28d3bb291baeb088b39060e" @@ -5946,16 +6288,16 @@ dependencies = [ "log", "quinn", "rayon", - "solana-account", + "solana-account 3.2.0", "solana-client-traits", "solana-commitment-config", "solana-connection-cache", "solana-epoch-info", "solana-hash 3.1.0", - "solana-instruction", + "solana-instruction 3.1.0", "solana-keypair", "solana-measure", - "solana-message", + "solana-message 3.0.1", "solana-net-utils", "solana-pubkey 3.0.0", "solana-pubsub-client", @@ -5964,13 +6306,13 @@ dependencies = [ "solana-rpc-client", "solana-rpc-client-api", "solana-rpc-client-nonce-utils", - "solana-signature", - "solana-signer", + "solana-signature 3.1.0", + "solana-signer 3.0.0", "solana-streamer", "solana-time-utils", "solana-tpu-client", "solana-transaction", - "solana-transaction-error", + "solana-transaction-error 3.0.0", "solana-transaction-status-client-types", "solana-udp-client", "thiserror 2.0.17", @@ -5984,19 +6326,32 @@ version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08618ed587e128105510c54ae3e456b9a06d674d8640db75afe66dad65cb4e02" dependencies = [ - "solana-account", + "solana-account 3.2.0", "solana-commitment-config", "solana-epoch-info", "solana-hash 3.1.0", - "solana-instruction", + "solana-instruction 3.1.0", "solana-keypair", - "solana-message", + "solana-message 3.0.1", "solana-pubkey 3.0.0", - "solana-signature", - "solana-signer", - "solana-system-interface", + "solana-signature 3.1.0", + "solana-signer 3.0.0", + "solana-system-interface 2.0.0", "solana-transaction", - "solana-transaction-error", + "solana-transaction-error 3.0.0", +] + +[[package]] +name = "solana-clock" +version = "2.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bb482ab70fced82ad3d7d3d87be33d466a3498eb8aa856434ff3c0dfc2e2e31" +dependencies = [ + "serde", + "serde_derive", + "solana-sdk-ids 2.2.1", + "solana-sdk-macro 2.2.1", + "solana-sysvar-id 2.2.1", ] [[package]] @@ -6007,9 +6362,9 @@ checksum = "fb62e9381182459a4520b5fe7fb22d423cae736239a6427fc398a88743d0ed59" dependencies = [ "serde", "serde_derive", - "solana-sdk-ids", - "solana-sdk-macro", - "solana-sysvar-id", + "solana-sdk-ids 3.1.0", + "solana-sdk-macro 3.0.0", + "solana-sysvar-id 3.1.0", ] [[package]] @@ -6040,12 +6395,12 @@ dependencies = [ "bincode", "serde", "serde_derive", - "solana-account", - "solana-instruction", + "solana-account 3.2.0", + "solana-instruction 3.1.0", "solana-pubkey 3.0.0", - "solana-sdk-ids", - "solana-short-vec", - "solana-system-interface", + "solana-sdk-ids 3.1.0", + "solana-short-vec 3.1.0", + "solana-system-interface 2.0.0", ] [[package]] @@ -6066,23 +6421,51 @@ dependencies = [ "solana-measure", "solana-metrics", "solana-time-utils", - "solana-transaction-error", + "solana-transaction-error 3.0.0", "thiserror 2.0.17", "tokio", ] +[[package]] +name = "solana-cpi" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8dc71126edddc2ba014622fc32d0f5e2e78ec6c5a1e0eb511b85618c09e9ea11" +dependencies = [ + "solana-account-info 2.3.0", + "solana-define-syscall 2.3.0", + "solana-instruction 2.3.3", + "solana-program-error 2.2.2", + "solana-pubkey 2.4.0", + "solana-stable-layout 2.2.1", +] + [[package]] name = "solana-cpi" version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4dea26709d867aada85d0d3617db0944215c8bb28d3745b912de7db13a23280c" dependencies = [ - "solana-account-info", + "solana-account-info 3.1.0", "solana-define-syscall 4.0.1", - "solana-instruction", - "solana-program-error", + "solana-instruction 3.1.0", + "solana-program-error 3.0.0", "solana-pubkey 4.0.0", - "solana-stable-layout", + "solana-stable-layout 3.0.0", +] + +[[package]] +name = "solana-curve25519" +version = "2.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eae4261b9a8613d10e77ac831a8fa60b6fa52b9b103df46d641deff9f9812a23" +dependencies = [ + "bytemuck", + "bytemuck_derive", + "curve25519-dalek", + "solana-define-syscall 2.3.0", + "subtle", + "thiserror 2.0.17", ] [[package]] @@ -6126,6 +6509,17 @@ version = "4.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57e5b1c0bc1d4a4d10c88a4100499d954c09d3fecfae4912c1a074dff68b1738" +[[package]] +name = "solana-derivation-path" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "939756d798b25c5ec3cca10e06212bdca3b1443cb9bb740a38124f58b258737b" +dependencies = [ + "derivation-path", + "qstring", + "uriparse", +] + [[package]] name = "solana-derivation-path" version = "3.0.0" @@ -6147,6 +6541,20 @@ dependencies = [ "serde_derive", ] +[[package]] +name = "solana-epoch-rewards" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b575d3dd323b9ea10bb6fe89bf6bf93e249b215ba8ed7f68f1a3633f384db7" +dependencies = [ + "serde", + "serde_derive", + "solana-hash 2.3.0", + "solana-sdk-ids 2.2.1", + "solana-sdk-macro 2.2.1", + "solana-sysvar-id 2.2.1", +] + [[package]] name = "solana-epoch-rewards" version = "3.0.0" @@ -6156,9 +6564,22 @@ dependencies = [ "serde", "serde_derive", "solana-hash 3.1.0", - "solana-sdk-ids", - "solana-sdk-macro", - "solana-sysvar-id", + "solana-sdk-ids 3.1.0", + "solana-sdk-macro 3.0.0", + "solana-sysvar-id 3.1.0", +] + +[[package]] +name = "solana-epoch-schedule" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fce071fbddecc55d727b1d7ed16a629afe4f6e4c217bc8d00af3b785f6f67ed" +dependencies = [ + "serde", + "serde_derive", + "solana-sdk-ids 2.2.1", + "solana-sdk-macro 2.2.1", + "solana-sysvar-id 2.2.1", ] [[package]] @@ -6169,9 +6590,49 @@ checksum = "6e5481e72cc4d52c169db73e4c0cd16de8bc943078aac587ec4817a75cc6388f" dependencies = [ "serde", "serde_derive", - "solana-sdk-ids", - "solana-sdk-macro", - "solana-sysvar-id", + "solana-sdk-ids 3.1.0", + "solana-sdk-macro 3.0.0", + "solana-sysvar-id 3.1.0", +] + +[[package]] +name = "solana-example-mocks" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84461d56cbb8bb8d539347151e0525b53910102e4bced875d49d5139708e39d3" +dependencies = [ + "serde", + "serde_derive", + "solana-address-lookup-table-interface 2.2.2", + "solana-clock 2.2.2", + "solana-hash 2.3.0", + "solana-instruction 2.3.3", + "solana-keccak-hasher", + "solana-message 2.4.0", + "solana-nonce 2.2.1", + "solana-pubkey 2.4.0", + "solana-sdk-ids 2.2.1", + "solana-system-interface 1.0.0", + "thiserror 2.0.17", +] + +[[package]] +name = "solana-feature-gate-interface" +version = "2.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43f5c5382b449e8e4e3016fb05e418c53d57782d8b5c30aa372fc265654b956d" +dependencies = [ + "bincode", + "serde", + "serde_derive", + "solana-account 2.2.1", + "solana-account-info 2.3.0", + "solana-instruction 2.3.3", + "solana-program-error 2.2.2", + "solana-pubkey 2.4.0", + "solana-rent 2.2.1", + "solana-sdk-ids 2.2.1", + "solana-system-interface 1.0.0", ] [[package]] @@ -6182,9 +6643,20 @@ checksum = "7347ab62e6d47a82e340c865133795b394feea7c2b2771d293f57691c6544c3f" dependencies = [ "serde", "serde_derive", - "solana-program-error", + "solana-program-error 3.0.0", "solana-pubkey 3.0.0", - "solana-sdk-ids", + "solana-sdk-ids 3.1.0", +] + +[[package]] +name = "solana-fee-calculator" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d89bc408da0fb3812bc3008189d148b4d3e08252c79ad810b245482a3f70cd8d" +dependencies = [ + "log", + "serde", + "serde_derive", ] [[package]] @@ -6204,8 +6676,13 @@ version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5b96e9f0300fa287b545613f007dfe20043d7812bee255f418c1eb649c93b63" dependencies = [ + "borsh 1.5.7", + "bytemuck", + "bytemuck_derive", "five8 0.2.1", "js-sys", + "serde", + "serde_derive", "solana-atomic-u64 2.2.1", "solana-sanitize 2.2.1", "wasm-bindgen", @@ -6242,6 +6719,25 @@ version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e92f37a14e7c660628752833250dd3dcd8e95309876aee751d7f8769a27947c6" +[[package]] +name = "solana-instruction" +version = "2.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bab5682934bd1f65f8d2c16f21cb532526fcc1a09f796e2cacdb091eee5774ad" +dependencies = [ + "bincode", + "borsh 1.5.7", + "getrandom 0.2.16", + "js-sys", + "num-traits", + "serde", + "serde_derive", + "serde_json", + "solana-define-syscall 2.3.0", + "solana-pubkey 2.4.0", + "wasm-bindgen", +] + [[package]] name = "solana-instruction" version = "3.1.0" @@ -6266,7 +6762,24 @@ dependencies = [ "num-traits", "serde", "serde_derive", - "solana-program-error", + "solana-program-error 3.0.0", +] + +[[package]] +name = "solana-instructions-sysvar" +version = "2.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0e85a6fad5c2d0c4f5b91d34b8ca47118fc593af706e523cdbedf846a954f57" +dependencies = [ + "bitflags 2.10.0", + "solana-account-info 2.3.0", + "solana-instruction 2.3.3", + "solana-program-error 2.2.2", + "solana-pubkey 2.4.0", + "solana-sanitize 2.2.1", + "solana-sdk-ids 2.2.1", + "solana-serialize-utils 2.2.1", + "solana-sysvar-id 2.2.1", ] [[package]] @@ -6276,15 +6789,27 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ddf67876c541aa1e21ee1acae35c95c6fbc61119814bfef70579317a5e26955" dependencies = [ "bitflags 2.10.0", - "solana-account-info", - "solana-instruction", + "solana-account-info 3.1.0", + "solana-instruction 3.1.0", "solana-instruction-error", - "solana-program-error", + "solana-program-error 3.0.0", "solana-pubkey 3.0.0", "solana-sanitize 3.0.1", - "solana-sdk-ids", - "solana-serialize-utils", - "solana-sysvar-id", + "solana-sdk-ids 3.1.0", + "solana-serialize-utils 3.1.0", + "solana-sysvar-id 3.1.0", +] + +[[package]] +name = "solana-keccak-hasher" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7aeb957fbd42a451b99235df4942d96db7ef678e8d5061ef34c9b34cae12f79" +dependencies = [ + "sha3", + "solana-define-syscall 2.3.0", + "solana-hash 2.3.0", + "solana-sanitize 2.2.1", ] [[package]] @@ -6297,9 +6822,22 @@ dependencies = [ "five8 1.0.0", "rand 0.8.5", "solana-address 2.0.0", - "solana-seed-phrase", - "solana-signature", - "solana-signer", + "solana-seed-phrase 3.0.0", + "solana-signature 3.1.0", + "solana-signer 3.0.0", +] + +[[package]] +name = "solana-last-restart-slot" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a6360ac2fdc72e7463565cd256eedcf10d7ef0c28a1249d261ec168c1b55cdd" +dependencies = [ + "serde", + "serde_derive", + "solana-sdk-ids 2.2.1", + "solana-sdk-macro 2.2.1", + "solana-sysvar-id 2.2.1", ] [[package]] @@ -6310,9 +6848,23 @@ checksum = "dcda154ec827f5fc1e4da0af3417951b7e9b8157540f81f936c4a8b1156134d0" dependencies = [ "serde", "serde_derive", - "solana-sdk-ids", - "solana-sdk-macro", - "solana-sysvar-id", + "solana-sdk-ids 3.1.0", + "solana-sdk-macro 3.0.0", + "solana-sysvar-id 3.1.0", +] + +[[package]] +name = "solana-loader-v2-interface" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8ab08006dad78ae7cd30df8eea0539e207d08d91eaefb3e1d49a446e1c49654" +dependencies = [ + "serde", + "serde_bytes", + "serde_derive", + "solana-instruction 2.3.3", + "solana-pubkey 2.4.0", + "solana-sdk-ids 2.2.1", ] [[package]] @@ -6324,9 +6876,24 @@ dependencies = [ "serde", "serde_bytes", "serde_derive", - "solana-instruction", + "solana-instruction 3.1.0", "solana-pubkey 3.0.0", - "solana-sdk-ids", + "solana-sdk-ids 3.1.0", +] + +[[package]] +name = "solana-loader-v3-interface" +version = "5.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f7162a05b8b0773156b443bccd674ea78bb9aa406325b467ea78c06c99a63a2" +dependencies = [ + "serde", + "serde_bytes", + "serde_derive", + "solana-instruction 2.3.3", + "solana-pubkey 2.4.0", + "solana-sdk-ids 2.2.1", + "solana-system-interface 1.0.0", ] [[package]] @@ -6338,10 +6905,25 @@ dependencies = [ "serde", "serde_bytes", "serde_derive", - "solana-instruction", + "solana-instruction 3.1.0", "solana-pubkey 3.0.0", - "solana-sdk-ids", - "solana-system-interface", + "solana-sdk-ids 3.1.0", + "solana-system-interface 2.0.0", +] + +[[package]] +name = "solana-loader-v4-interface" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "706a777242f1f39a83e2a96a2a6cb034cb41169c6ecbee2cf09cb873d9659e7e" +dependencies = [ + "serde", + "serde_bytes", + "serde_derive", + "solana-instruction 2.3.3", + "solana-pubkey 2.4.0", + "solana-sdk-ids 2.2.1", + "solana-system-interface 1.0.0", ] [[package]] @@ -6350,6 +6932,29 @@ version = "3.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f96102f7c7c9f21cba06453b2274a55f279f5c4a0201ddef63df940db9c7bf61" +[[package]] +name = "solana-message" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1796aabce376ff74bf89b78d268fa5e683d7d7a96a0a4e4813ec34de49d5314b" +dependencies = [ + "bincode", + "blake3", + "lazy_static", + "serde", + "serde_derive", + "solana-bincode", + "solana-hash 2.3.0", + "solana-instruction 2.3.3", + "solana-pubkey 2.4.0", + "solana-sanitize 2.2.1", + "solana-sdk-ids 2.2.1", + "solana-short-vec 2.2.1", + "solana-system-interface 1.0.0", + "solana-transaction-error 2.2.1", + "wasm-bindgen", +] + [[package]] name = "solana-message" version = "3.0.1" @@ -6363,11 +6968,11 @@ dependencies = [ "serde_derive", "solana-address 1.1.0", "solana-hash 3.1.0", - "solana-instruction", + "solana-instruction 3.1.0", "solana-sanitize 3.0.1", - "solana-sdk-ids", - "solana-short-vec", - "solana-transaction-error", + "solana-sdk-ids 3.1.0", + "solana-short-vec 3.1.0", + "solana-transaction-error 3.0.0", ] [[package]] @@ -6386,6 +6991,15 @@ dependencies = [ "thiserror 2.0.17", ] +[[package]] +name = "solana-msg" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f36a1a14399afaabc2781a1db09cb14ee4cc4ee5c7a5a3cfcc601811379a8092" +dependencies = [ + "solana-define-syscall 2.3.0", +] + [[package]] name = "solana-msg" version = "3.0.0" @@ -6395,6 +7009,12 @@ dependencies = [ "solana-define-syscall 3.0.0", ] +[[package]] +name = "solana-native-token" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61515b880c36974053dd499c0510066783f0cc6ac17def0c7ef2a244874cf4a9" + [[package]] name = "solana-net-utils" version = "3.1.1" @@ -6418,6 +7038,20 @@ dependencies = [ "url", ] +[[package]] +name = "solana-nonce" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "703e22eb185537e06204a5bd9d509b948f0066f2d1d814a6f475dafb3ddf1325" +dependencies = [ + "serde", + "serde_derive", + "solana-fee-calculator 2.2.1", + "solana-hash 2.3.0", + "solana-pubkey 2.4.0", + "solana-sha256-hasher 2.3.0", +] + [[package]] name = "solana-nonce" version = "3.0.0" @@ -6426,7 +7060,7 @@ checksum = "abbdc6c8caf1c08db9f36a50967539d0f72b9f1d4aea04fec5430f532e5afadc" dependencies = [ "serde", "serde_derive", - "solana-fee-calculator", + "solana-fee-calculator 3.0.0", "solana-hash 3.1.0", "solana-pubkey 3.0.0", "solana-sha256-hasher 3.1.0", @@ -6476,30 +7110,138 @@ dependencies = [ "rayon", "serde", "solana-hash 3.1.0", - "solana-message", + "solana-message 3.0.1", "solana-metrics", "solana-packet", "solana-pubkey 3.0.0", "solana-rayon-threadlimit", - "solana-sdk-ids", - "solana-short-vec", - "solana-signature", + "solana-sdk-ids 3.1.0", + "solana-short-vec 3.1.0", + "solana-signature 3.1.0", "solana-time-utils", "solana-transaction-context", ] +[[package]] +name = "solana-program" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98eca145bd3545e2fbb07166e895370576e47a00a7d824e325390d33bf467210" +dependencies = [ + "bincode", + "blake3", + "borsh 0.10.4", + "borsh 1.5.7", + "bs58 0.5.1", + "bytemuck", + "console_error_panic_hook", + "console_log", + "getrandom 0.2.16", + "lazy_static", + "log", + "memoffset", + "num-bigint 0.4.6", + "num-derive", + "num-traits", + "rand 0.8.5", + "serde", + "serde_bytes", + "serde_derive", + "solana-account-info 2.3.0", + "solana-address-lookup-table-interface 2.2.2", + "solana-atomic-u64 2.2.1", + "solana-big-mod-exp", + "solana-bincode", + "solana-blake3-hasher", + "solana-borsh 2.2.1", + "solana-clock 2.2.2", + "solana-cpi 2.2.1", + "solana-decode-error", + "solana-define-syscall 2.3.0", + "solana-epoch-rewards 2.2.1", + "solana-epoch-schedule 2.2.1", + "solana-example-mocks", + "solana-feature-gate-interface 2.2.2", + "solana-fee-calculator 2.2.1", + "solana-hash 2.3.0", + "solana-instruction 2.3.3", + "solana-instructions-sysvar 2.2.2", + "solana-keccak-hasher", + "solana-last-restart-slot 2.2.1", + "solana-loader-v2-interface 2.2.1", + "solana-loader-v3-interface 5.0.0", + "solana-loader-v4-interface", + "solana-message 2.4.0", + "solana-msg 2.2.1", + "solana-native-token", + "solana-nonce 2.2.1", + "solana-program-entrypoint 2.3.0", + "solana-program-error 2.2.2", + "solana-program-memory 2.3.1", + "solana-program-option 2.2.1", + "solana-program-pack 2.2.1", + "solana-pubkey 2.4.0", + "solana-rent 2.2.1", + "solana-sanitize 2.2.1", + "solana-sdk-ids 2.2.1", + "solana-sdk-macro 2.2.1", + "solana-secp256k1-recover", + "solana-serde-varint 2.2.2", + "solana-serialize-utils 2.2.1", + "solana-sha256-hasher 2.3.0", + "solana-short-vec 2.2.1", + "solana-slot-hashes 2.2.1", + "solana-slot-history 2.2.1", + "solana-stable-layout 2.2.1", + "solana-stake-interface 1.2.1", + "solana-system-interface 1.0.0", + "solana-sysvar 2.3.0", + "solana-sysvar-id 2.2.1", + "solana-vote-interface 2.2.6", + "thiserror 2.0.17", + "wasm-bindgen", +] + +[[package]] +name = "solana-program-entrypoint" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32ce041b1a0ed275290a5008ee1a4a6c48f5054c8a3d78d313c08958a06aedbd" +dependencies = [ + "solana-account-info 2.3.0", + "solana-msg 2.2.1", + "solana-program-error 2.2.2", + "solana-pubkey 2.4.0", +] + [[package]] name = "solana-program-entrypoint" version = "3.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "84c9b0a1ff494e05f503a08b3d51150b73aa639544631e510279d6375f290997" dependencies = [ - "solana-account-info", + "solana-account-info 3.1.0", "solana-define-syscall 4.0.1", - "solana-program-error", + "solana-program-error 3.0.0", "solana-pubkey 4.0.0", ] +[[package]] +name = "solana-program-error" +version = "2.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ee2e0217d642e2ea4bee237f37bd61bb02aec60da3647c48ff88f6556ade775" +dependencies = [ + "borsh 1.5.7", + "num-traits", + "serde", + "serde_derive", + "solana-decode-error", + "solana-instruction 2.3.3", + "solana-msg 2.2.1", + "solana-pubkey 2.4.0", +] + [[package]] name = "solana-program-error" version = "3.0.0" @@ -6509,6 +7251,15 @@ dependencies = [ "borsh 1.5.7", ] +[[package]] +name = "solana-program-memory" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a5426090c6f3fd6cfdc10685322fede9ca8e5af43cd6a59e98bfe4e91671712" +dependencies = [ + "solana-define-syscall 2.3.0", +] + [[package]] name = "solana-program-memory" version = "3.1.0" @@ -6518,19 +7269,34 @@ dependencies = [ "solana-define-syscall 4.0.1", ] +[[package]] +name = "solana-program-option" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc677a2e9bc616eda6dbdab834d463372b92848b2bfe4a1ed4e4b4adba3397d0" + [[package]] name = "solana-program-option" version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e7b4ddb464f274deb4a497712664c3b612e3f5f82471d4e47710fc4ab1c3095" +[[package]] +name = "solana-program-pack" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "319f0ef15e6e12dc37c597faccb7d62525a509fec5f6975ecb9419efddeb277b" +dependencies = [ + "solana-program-error 2.2.2", +] + [[package]] name = "solana-program-pack" version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c169359de21f6034a63ebf96d6b380980307df17a8d371344ff04a883ec4e9d0" dependencies = [ - "solana-program-error", + "solana-program-error 3.0.0", ] [[package]] @@ -6539,12 +7305,18 @@ version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b62adb9c3261a052ca1f999398c388f1daf558a1b492f60a6d9e64857db4ff1" dependencies = [ + "borsh 0.10.4", + "borsh 1.5.7", + "bytemuck", + "bytemuck_derive", "curve25519-dalek", "five8 0.2.1", "five8_const 0.1.4", "getrandom 0.2.16", "js-sys", "num-traits", + "serde", + "serde_derive", "solana-atomic-u64 2.2.1", "solana-decode-error", "solana-define-syscall 2.3.0", @@ -6585,10 +7357,10 @@ dependencies = [ "serde", "serde_json", "solana-account-decoder-client-types", - "solana-clock", + "solana-clock 3.0.0", "solana-pubkey 3.0.0", "solana-rpc-client-types", - "solana-signature", + "solana-signature 3.1.0", "thiserror 2.0.17", "tokio", "tokio-stream", @@ -6619,10 +7391,10 @@ dependencies = [ "solana-pubkey 3.0.0", "solana-quic-definitions", "solana-rpc-client-api", - "solana-signer", + "solana-signer 3.0.0", "solana-streamer", "solana-tls-utils", - "solana-transaction-error", + "solana-transaction-error 3.0.0", "thiserror 2.0.17", "tokio", ] @@ -6646,6 +7418,19 @@ dependencies = [ "num_cpus", ] +[[package]] +name = "solana-rent" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1aea8fdea9de98ca6e8c2da5827707fb3842833521b528a713810ca685d2480" +dependencies = [ + "serde", + "serde_derive", + "solana-sdk-ids 2.2.1", + "solana-sdk-macro 2.2.1", + "solana-sysvar-id 2.2.1", +] + [[package]] name = "solana-rent" version = "3.0.0" @@ -6654,9 +7439,9 @@ checksum = "b702d8c43711e3c8a9284a4f1bbc6a3de2553deb25b0c8142f9a44ef0ce5ddc1" dependencies = [ "serde", "serde_derive", - "solana-sdk-ids", - "solana-sdk-macro", - "solana-sysvar-id", + "solana-sdk-ids 3.1.0", + "solana-sdk-macro 3.0.0", + "solana-sysvar-id 3.1.0", ] [[package]] @@ -6687,25 +7472,25 @@ dependencies = [ "semver", "serde", "serde_json", - "solana-account", + "solana-account 3.2.0", "solana-account-decoder", "solana-account-decoder-client-types", - "solana-clock", + "solana-clock 3.0.0", "solana-commitment-config", "solana-epoch-info", - "solana-epoch-schedule", - "solana-feature-gate-interface", + "solana-epoch-schedule 3.0.0", + "solana-feature-gate-interface 3.0.0", "solana-hash 3.1.0", - "solana-instruction", - "solana-message", + "solana-instruction 3.1.0", + "solana-message 3.0.1", "solana-pubkey 3.0.0", "solana-rpc-client-api", - "solana-signature", + "solana-signature 3.1.0", "solana-transaction", - "solana-transaction-error", + "solana-transaction-error 3.0.0", "solana-transaction-status-client-types", "solana-version", - "solana-vote-interface", + "solana-vote-interface 4.0.4", "tokio", ] @@ -6722,10 +7507,10 @@ dependencies = [ "serde", "serde_json", "solana-account-decoder-client-types", - "solana-clock", + "solana-clock 3.0.0", "solana-rpc-client-types", - "solana-signer", - "solana-transaction-error", + "solana-signer 3.0.0", + "solana-transaction-error 3.0.0", "solana-transaction-status-client-types", "thiserror 2.0.17", ] @@ -6736,14 +7521,14 @@ version = "3.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ede5005bd6f29d131fdaeae736348b5c6dc238a4be42550ade03d27d32ebf676" dependencies = [ - "solana-account", + "solana-account 3.2.0", "solana-commitment-config", "solana-hash 3.1.0", - "solana-message", - "solana-nonce", + "solana-message 3.0.1", + "solana-nonce 3.0.0", "solana-pubkey 3.0.0", "solana-rpc-client", - "solana-sdk-ids", + "solana-sdk-ids 3.1.0", "thiserror 2.0.17", ] @@ -6758,16 +7543,16 @@ dependencies = [ "semver", "serde", "serde_json", - "solana-account", + "solana-account 3.2.0", "solana-account-decoder-client-types", "solana-address 1.1.0", - "solana-clock", + "solana-clock 3.0.0", "solana-commitment-config", - "solana-fee-calculator", + "solana-fee-calculator 3.0.0", "solana-inflation", "solana-reward-info", "solana-transaction", - "solana-transaction-error", + "solana-transaction-error 3.0.0", "solana-transaction-status-client-types", "solana-version", "spl-generic-token", @@ -6800,6 +7585,15 @@ dependencies = [ "thiserror 2.0.17", ] +[[package]] +name = "solana-sdk-ids" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c5d8b9cc68d5c88b062a33e23a6466722467dde0035152d8fb1afbcdf350a5f" +dependencies = [ + "solana-pubkey 2.4.0", +] + [[package]] name = "solana-sdk-ids" version = "3.1.0" @@ -6809,6 +7603,18 @@ dependencies = [ "solana-address 2.0.0", ] +[[package]] +name = "solana-sdk-macro" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86280da8b99d03560f6ab5aca9de2e38805681df34e0bb8f238e69b29433b9df" +dependencies = [ + "bs58 0.5.1", + "proc-macro2", + "quote", + "syn 2.0.110", +] + [[package]] name = "solana-sdk-macro" version = "3.0.0" @@ -6821,13 +7627,53 @@ dependencies = [ "syn 2.0.110", ] +[[package]] +name = "solana-secp256k1-recover" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baa3120b6cdaa270f39444f5093a90a7b03d296d362878f7a6991d6de3bbe496" +dependencies = [ + "libsecp256k1", + "solana-define-syscall 2.3.0", + "thiserror 2.0.17", +] + +[[package]] +name = "solana-security-txt" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "156bb61a96c605fa124e052d630dba2f6fb57e08c7d15b757e1e958b3ed7b3fe" +dependencies = [ + "hashbrown 0.15.2", +] + +[[package]] +name = "solana-seed-derivable" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3beb82b5adb266c6ea90e5cf3967235644848eac476c5a1f2f9283a143b7c97f" +dependencies = [ + "solana-derivation-path 2.2.1", +] + [[package]] name = "solana-seed-derivable" version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ff7bdb72758e3bec33ed0e2658a920f1f35dfb9ed576b951d20d63cb61ecd95c" dependencies = [ - "solana-derivation-path", + "solana-derivation-path 3.0.0", +] + +[[package]] +name = "solana-seed-phrase" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36187af2324f079f65a675ec22b31c24919cb4ac22c79472e85d819db9bbbc15" +dependencies = [ + "hmac", + "pbkdf2", + "sha2 0.10.9", ] [[package]] @@ -6838,7 +7684,7 @@ checksum = "dc905b200a95f2ea9146e43f2a7181e3aeb55de6bc12afb36462d00a3c7310de" dependencies = [ "hmac", "pbkdf2", - "sha2", + "sha2 0.10.9", ] [[package]] @@ -6850,6 +7696,15 @@ dependencies = [ "serde", ] +[[package]] +name = "solana-serde-varint" +version = "2.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a7e155eba458ecfb0107b98236088c3764a09ddf0201ec29e52a0be40857113" +dependencies = [ + "serde", +] + [[package]] name = "solana-serde-varint" version = "3.0.0" @@ -6859,6 +7714,17 @@ dependencies = [ "serde", ] +[[package]] +name = "solana-serialize-utils" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "817a284b63197d2b27afdba829c5ab34231da4a9b4e763466a003c40ca4f535e" +dependencies = [ + "solana-instruction 2.3.3", + "solana-pubkey 2.4.0", + "solana-sanitize 2.2.1", +] + [[package]] name = "solana-serialize-utils" version = "3.1.0" @@ -6876,7 +7742,7 @@ version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5aa3feb32c28765f6aa1ce8f3feac30936f16c5c3f7eb73d63a5b8f6f8ecdc44" dependencies = [ - "sha2", + "sha2 0.10.9", "solana-define-syscall 2.3.0", "solana-hash 2.3.0", ] @@ -6887,11 +7753,20 @@ version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "db7dc3011ea4c0334aaaa7e7128cb390ecf546b28d412e9bf2064680f57f588f" dependencies = [ - "sha2", + "sha2 0.10.9", "solana-define-syscall 4.0.1", "solana-hash 4.0.1", ] +[[package]] +name = "solana-short-vec" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c54c66f19b9766a56fa0057d060de8378676cb64987533fa088861858fc5a69" +dependencies = [ + "serde", +] + [[package]] name = "solana-short-vec" version = "3.1.0" @@ -6901,6 +7776,16 @@ dependencies = [ "serde_core", ] +[[package]] +name = "solana-signature" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64c8ec8e657aecfc187522fc67495142c12f35e55ddeca8698edbb738b8dbd8c" +dependencies = [ + "five8 0.2.1", + "solana-sanitize 2.2.1", +] + [[package]] name = "solana-signature" version = "3.1.0" @@ -6915,6 +7800,17 @@ dependencies = [ "solana-sanitize 3.0.1", ] +[[package]] +name = "solana-signer" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c41991508a4b02f021c1342ba00bcfa098630b213726ceadc7cb032e051975b" +dependencies = [ + "solana-pubkey 2.4.0", + "solana-signature 2.3.0", + "solana-transaction-error 2.2.1", +] + [[package]] name = "solana-signer" version = "3.0.0" @@ -6922,8 +7818,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5bfea97951fee8bae0d6038f39a5efcb6230ecdfe33425ac75196d1a1e3e3235" dependencies = [ "solana-pubkey 3.0.0", - "solana-signature", - "solana-transaction-error", + "solana-signature 3.1.0", + "solana-transaction-error 3.0.0", +] + +[[package]] +name = "solana-slot-hashes" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c8691982114513763e88d04094c9caa0376b867a29577939011331134c301ce" +dependencies = [ + "serde", + "serde_derive", + "solana-hash 2.3.0", + "solana-sdk-ids 2.2.1", + "solana-sysvar-id 2.2.1", ] [[package]] @@ -6935,8 +7844,21 @@ dependencies = [ "serde", "serde_derive", "solana-hash 3.1.0", - "solana-sdk-ids", - "solana-sysvar-id", + "solana-sdk-ids 3.1.0", + "solana-sysvar-id 3.1.0", +] + +[[package]] +name = "solana-slot-history" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97ccc1b2067ca22754d5283afb2b0126d61eae734fc616d23871b0943b0d935e" +dependencies = [ + "bv", + "serde", + "serde_derive", + "solana-sdk-ids 2.2.1", + "solana-sysvar-id 2.2.1", ] [[package]] @@ -6948,8 +7870,18 @@ dependencies = [ "bv", "serde", "serde_derive", - "solana-sdk-ids", - "solana-sysvar-id", + "solana-sdk-ids 3.1.0", + "solana-sysvar-id 3.1.0", +] + +[[package]] +name = "solana-stable-layout" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f14f7d02af8f2bc1b5efeeae71bc1c2b7f0f65cd75bcc7d8180f2c762a57f54" +dependencies = [ + "solana-instruction 2.3.3", + "solana-pubkey 2.4.0", ] [[package]] @@ -6958,10 +7890,31 @@ version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1da74507795b6e8fb60b7c7306c0c36e2c315805d16eaaf479452661234685ac" dependencies = [ - "solana-instruction", + "solana-instruction 3.1.0", "solana-pubkey 3.0.0", ] +[[package]] +name = "solana-stake-interface" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5269e89fde216b4d7e1d1739cf5303f8398a1ff372a81232abbee80e554a838c" +dependencies = [ + "borsh 0.10.4", + "borsh 1.5.7", + "num-traits", + "serde", + "serde_derive", + "solana-clock 2.2.2", + "solana-cpi 2.2.1", + "solana-decode-error", + "solana-instruction 2.3.3", + "solana-program-error 2.2.2", + "solana-pubkey 2.4.0", + "solana-system-interface 1.0.0", + "solana-sysvar-id 2.2.1", +] + [[package]] name = "solana-stake-interface" version = "2.0.1" @@ -6971,14 +7924,14 @@ dependencies = [ "num-traits", "serde", "serde_derive", - "solana-clock", - "solana-cpi", - "solana-instruction", - "solana-program-error", + "solana-clock 3.0.0", + "solana-cpi 3.1.0", + "solana-instruction 3.1.0", + "solana-program-error 3.0.0", "solana-pubkey 3.0.0", - "solana-system-interface", - "solana-sysvar", - "solana-sysvar-id", + "solana-system-interface 2.0.0", + "solana-sysvar 3.1.0", + "solana-sysvar-id 3.1.0", ] [[package]] @@ -7017,11 +7970,11 @@ dependencies = [ "solana-perf", "solana-pubkey 3.0.0", "solana-quic-definitions", - "solana-signature", - "solana-signer", + "solana-signature 3.1.0", + "solana-signer 3.0.0", "solana-time-utils", "solana-tls-utils", - "solana-transaction-error", + "solana-transaction-error 3.0.0", "solana-transaction-metrics-tracker", "thiserror 2.0.17", "tokio", @@ -7039,9 +7992,25 @@ checksum = "d848a90245dbaffeb8c43492eb902c2b988200a1b59b3959435d17abcea3eb3d" name = "solana-svm-type-overrides" version = "3.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cddcdb9981c7838ceb16bb97929c5cab015b0bdcb12243720000f8e44c9a5af2" +checksum = "cddcdb9981c7838ceb16bb97929c5cab015b0bdcb12243720000f8e44c9a5af2" +dependencies = [ + "rand 0.8.5", +] + +[[package]] +name = "solana-system-interface" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94d7c18cb1a91c6be5f5a8ac9276a1d7c737e39a21beba9ea710ab4b9c63bc90" dependencies = [ - "rand 0.8.5", + "js-sys", + "num-traits", + "serde", + "serde_derive", + "solana-decode-error", + "solana-instruction 2.3.3", + "solana-pubkey 2.4.0", + "wasm-bindgen", ] [[package]] @@ -7053,12 +8022,49 @@ dependencies = [ "num-traits", "serde", "serde_derive", - "solana-instruction", - "solana-msg", - "solana-program-error", + "solana-instruction 3.1.0", + "solana-msg 3.0.0", + "solana-program-error 3.0.0", "solana-pubkey 3.0.0", ] +[[package]] +name = "solana-sysvar" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8c3595f95069f3d90f275bb9bd235a1973c4d059028b0a7f81baca2703815db" +dependencies = [ + "base64 0.22.1", + "bincode", + "bytemuck", + "bytemuck_derive", + "lazy_static", + "serde", + "serde_derive", + "solana-account-info 2.3.0", + "solana-clock 2.2.2", + "solana-define-syscall 2.3.0", + "solana-epoch-rewards 2.2.1", + "solana-epoch-schedule 2.2.1", + "solana-fee-calculator 2.2.1", + "solana-hash 2.3.0", + "solana-instruction 2.3.3", + "solana-instructions-sysvar 2.2.2", + "solana-last-restart-slot 2.2.1", + "solana-program-entrypoint 2.3.0", + "solana-program-error 2.2.2", + "solana-program-memory 2.3.1", + "solana-pubkey 2.4.0", + "solana-rent 2.2.1", + "solana-sanitize 2.2.1", + "solana-sdk-ids 2.2.1", + "solana-sdk-macro 2.2.1", + "solana-slot-hashes 2.2.1", + "solana-slot-history 2.2.1", + "solana-stake-interface 1.2.1", + "solana-sysvar-id 2.2.1", +] + [[package]] name = "solana-sysvar" version = "3.1.0" @@ -7070,25 +8076,35 @@ dependencies = [ "lazy_static", "serde", "serde_derive", - "solana-account-info", - "solana-clock", + "solana-account-info 3.1.0", + "solana-clock 3.0.0", "solana-define-syscall 4.0.1", - "solana-epoch-rewards", - "solana-epoch-schedule", - "solana-fee-calculator", + "solana-epoch-rewards 3.0.0", + "solana-epoch-schedule 3.0.0", + "solana-fee-calculator 3.0.0", "solana-hash 4.0.1", - "solana-instruction", - "solana-last-restart-slot", - "solana-program-entrypoint", - "solana-program-error", - "solana-program-memory", + "solana-instruction 3.1.0", + "solana-last-restart-slot 3.0.0", + "solana-program-entrypoint 3.1.1", + "solana-program-error 3.0.0", + "solana-program-memory 3.1.0", "solana-pubkey 4.0.0", - "solana-rent", - "solana-sdk-ids", - "solana-sdk-macro", - "solana-slot-hashes", - "solana-slot-history", - "solana-sysvar-id", + "solana-rent 3.0.0", + "solana-sdk-ids 3.1.0", + "solana-sdk-macro 3.0.0", + "solana-slot-hashes 3.0.0", + "solana-slot-history 3.0.0", + "solana-sysvar-id 3.1.0", +] + +[[package]] +name = "solana-sysvar-id" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5762b273d3325b047cfda250787f8d796d781746860d5d0a746ee29f3e8812c1" +dependencies = [ + "solana-pubkey 2.4.0", + "solana-sdk-ids 2.2.1", ] [[package]] @@ -7098,7 +8114,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "17358d1e9a13e5b9c2264d301102126cf11a47fd394cdf3dec174fe7bc96e1de" dependencies = [ "solana-address 2.0.0", - "solana-sdk-ids", + "solana-sdk-ids 3.1.0", ] [[package]] @@ -7116,7 +8132,7 @@ dependencies = [ "rustls 0.23.35", "solana-keypair", "solana-pubkey 3.0.0", - "solana-signer", + "solana-signer 3.0.0", "x509-parser", ] @@ -7134,22 +8150,22 @@ dependencies = [ "log", "rayon", "solana-client-traits", - "solana-clock", + "solana-clock 3.0.0", "solana-commitment-config", "solana-connection-cache", - "solana-epoch-schedule", + "solana-epoch-schedule 3.0.0", "solana-measure", - "solana-message", + "solana-message 3.0.1", "solana-net-utils", "solana-pubkey 3.0.0", "solana-pubsub-client", "solana-quic-definitions", "solana-rpc-client", "solana-rpc-client-api", - "solana-signature", - "solana-signer", + "solana-signature 3.1.0", + "solana-signer 3.0.0", "solana-transaction", - "solana-transaction-error", + "solana-transaction-error 3.0.0", "thiserror 2.0.17", "tokio", ] @@ -7165,15 +8181,15 @@ dependencies = [ "serde_derive", "solana-address 2.0.0", "solana-hash 4.0.1", - "solana-instruction", + "solana-instruction 3.1.0", "solana-instruction-error", - "solana-message", + "solana-message 3.0.1", "solana-sanitize 3.0.1", - "solana-sdk-ids", - "solana-short-vec", - "solana-signature", - "solana-signer", - "solana-transaction-error", + "solana-sdk-ids 3.1.0", + "solana-short-vec 3.1.0", + "solana-signature 3.1.0", + "solana-signer 3.0.0", + "solana-transaction-error 3.0.0", ] [[package]] @@ -7184,13 +8200,23 @@ checksum = "1bd55fe81fbc36ee00fde8233764b1f60c141e93a069932f126b707a515b8199" dependencies = [ "bincode", "serde", - "solana-account", - "solana-instruction", - "solana-instructions-sysvar", + "solana-account 3.2.0", + "solana-instruction 3.1.0", + "solana-instructions-sysvar 3.0.0", "solana-pubkey 3.0.0", - "solana-rent", + "solana-rent 3.0.0", "solana-sbpf", - "solana-sdk-ids", + "solana-sdk-ids 3.1.0", +] + +[[package]] +name = "solana-transaction-error" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "222a9dc8fdb61c6088baab34fc3a8b8473a03a7a5fd404ed8dd502fa79b67cb1" +dependencies = [ + "solana-instruction 2.3.3", + "solana-sanitize 2.2.1", ] [[package]] @@ -7217,8 +8243,8 @@ dependencies = [ "rand 0.8.5", "solana-packet", "solana-perf", - "solana-short-vec", - "solana-signature", + "solana-short-vec 3.1.0", + "solana-signature 3.1.0", ] [[package]] @@ -7237,30 +8263,30 @@ dependencies = [ "serde", "serde_json", "solana-account-decoder", - "solana-address-lookup-table-interface", - "solana-clock", + "solana-address-lookup-table-interface 3.0.0", + "solana-clock 3.0.0", "solana-hash 3.1.0", - "solana-instruction", - "solana-loader-v2-interface", - "solana-loader-v3-interface", - "solana-message", - "solana-program-option", + "solana-instruction 3.1.0", + "solana-loader-v2-interface 3.0.0", + "solana-loader-v3-interface 6.1.0", + "solana-message 3.0.1", + "solana-program-option 3.0.0", "solana-pubkey 3.0.0", "solana-reward-info", - "solana-sdk-ids", - "solana-signature", - "solana-stake-interface", - "solana-system-interface", + "solana-sdk-ids 3.1.0", + "solana-signature 3.1.0", + "solana-stake-interface 2.0.1", + "solana-system-interface 2.0.0", "solana-transaction", - "solana-transaction-error", + "solana-transaction-error 3.0.0", "solana-transaction-status-client-types", - "solana-vote-interface", + "solana-vote-interface 4.0.4", "spl-associated-token-account-interface", "spl-memo-interface", "spl-token-2022-interface", - "spl-token-group-interface", + "spl-token-group-interface 0.7.1", "spl-token-interface", - "spl-token-metadata-interface", + "spl-token-metadata-interface 0.8.0", "thiserror 2.0.17", ] @@ -7277,14 +8303,14 @@ dependencies = [ "serde_json", "solana-account-decoder-client-types", "solana-commitment-config", - "solana-instruction", - "solana-message", + "solana-instruction 3.1.0", + "solana-message 3.0.1", "solana-pubkey 3.0.0", "solana-reward-info", - "solana-signature", + "solana-signature 3.1.0", "solana-transaction", "solana-transaction-context", - "solana-transaction-error", + "solana-transaction-error 3.0.0", "thiserror 2.0.17", ] @@ -7299,7 +8325,7 @@ dependencies = [ "solana-keypair", "solana-net-utils", "solana-streamer", - "solana-transaction-error", + "solana-transaction-error 3.0.0", "thiserror 2.0.17", "tokio", ] @@ -7315,7 +8341,31 @@ dependencies = [ "semver", "serde", "solana-sanitize 3.0.1", - "solana-serde-varint", + "solana-serde-varint 3.0.0", +] + +[[package]] +name = "solana-vote-interface" +version = "2.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b80d57478d6599d30acc31cc5ae7f93ec2361a06aefe8ea79bc81739a08af4c3" +dependencies = [ + "bincode", + "num-derive", + "num-traits", + "serde", + "serde_derive", + "solana-clock 2.2.2", + "solana-decode-error", + "solana-hash 2.3.0", + "solana-instruction 2.3.3", + "solana-pubkey 2.4.0", + "solana-rent 2.2.1", + "solana-sdk-ids 2.2.1", + "solana-serde-varint 2.2.2", + "solana-serialize-utils 2.2.1", + "solana-short-vec 2.2.1", + "solana-system-interface 1.0.0", ] [[package]] @@ -7331,17 +8381,53 @@ dependencies = [ "serde", "serde_derive", "serde_with", - "solana-clock", + "solana-clock 3.0.0", "solana-hash 3.1.0", - "solana-instruction", + "solana-instruction 3.1.0", "solana-instruction-error", "solana-pubkey 3.0.0", - "solana-rent", - "solana-sdk-ids", - "solana-serde-varint", - "solana-serialize-utils", - "solana-short-vec", - "solana-system-interface", + "solana-rent 3.0.0", + "solana-sdk-ids 3.1.0", + "solana-serde-varint 3.0.0", + "solana-serialize-utils 3.1.0", + "solana-short-vec 3.1.0", + "solana-system-interface 2.0.0", +] + +[[package]] +name = "solana-zk-sdk" +version = "2.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97b9fc6ec37d16d0dccff708ed1dd6ea9ba61796700c3bb7c3b401973f10f63b" +dependencies = [ + "aes-gcm-siv", + "base64 0.22.1", + "bincode", + "bytemuck", + "bytemuck_derive", + "curve25519-dalek", + "itertools 0.12.1", + "js-sys", + "merlin", + "num-derive", + "num-traits", + "rand 0.8.5", + "serde", + "serde_derive", + "serde_json", + "sha3", + "solana-derivation-path 2.2.1", + "solana-instruction 2.3.3", + "solana-pubkey 2.4.0", + "solana-sdk-ids 2.2.1", + "solana-seed-derivable 2.2.1", + "solana-seed-phrase 2.2.1", + "solana-signature 2.3.0", + "solana-signer 2.2.1", + "subtle", + "thiserror 2.0.17", + "wasm-bindgen", + "zeroize", ] [[package]] @@ -7367,14 +8453,14 @@ dependencies = [ "serde_derive", "serde_json", "sha3", - "solana-derivation-path", - "solana-instruction", + "solana-derivation-path 3.0.0", + "solana-instruction 3.1.0", "solana-pubkey 3.0.0", - "solana-sdk-ids", - "solana-seed-derivable", - "solana-seed-phrase", - "solana-signature", - "solana-signer", + "solana-sdk-ids 3.1.0", + "solana-seed-derivable 3.0.0", + "solana-seed-phrase 3.0.0", + "solana-signature 3.1.0", + "solana-signer 3.0.0", "subtle", "thiserror 2.0.17", "wasm-bindgen", @@ -7422,10 +8508,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6433917b60441d68d99a17e121d9db0ea15a9a69c0e5afa34649cf5ba12612f" dependencies = [ "borsh 1.5.7", - "solana-instruction", + "solana-instruction 3.1.0", "solana-pubkey 3.0.0", ] +[[package]] +name = "spl-discriminator" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7398da23554a31660f17718164e31d31900956054f54f52d5ec1be51cb4f4b3" +dependencies = [ + "bytemuck", + "solana-program-error 2.2.2", + "solana-sha256-hasher 2.3.0", + "spl-discriminator-derive", +] + [[package]] name = "spl-discriminator" version = "0.5.1" @@ -7433,7 +8531,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d48cc11459e265d5b501534144266620289720b4c44522a47bc6b63cd295d2f3" dependencies = [ "bytemuck", - "solana-program-error", + "solana-program-error 3.0.0", "solana-sha256-hasher 3.1.0", "spl-discriminator-derive", ] @@ -7457,11 +8555,24 @@ checksum = "5d1dbc82ab91422345b6df40a79e2b78c7bce1ebb366da323572dd60b7076b67" dependencies = [ "proc-macro2", "quote", - "sha2", + "sha2 0.10.9", "syn 2.0.110", "thiserror 1.0.69", ] +[[package]] +name = "spl-elgamal-registry" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce0f668975d2b0536e8a8fd60e56a05c467f06021dae037f1d0cfed0de2e231d" +dependencies = [ + "bytemuck", + "solana-program", + "solana-zk-sdk 2.3.13", + "spl-pod 0.5.1", + "spl-token-confidential-transfer-proof-extraction 0.2.1", +] + [[package]] name = "spl-generic-token" version = "2.0.1" @@ -7472,16 +8583,50 @@ dependencies = [ "solana-pubkey 3.0.0", ] +[[package]] +name = "spl-memo" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f09647c0974e33366efeb83b8e2daebb329f0420149e74d3a4bd2c08cf9f7cb" +dependencies = [ + "solana-account-info 2.3.0", + "solana-instruction 2.3.3", + "solana-msg 2.2.1", + "solana-program-entrypoint 2.3.0", + "solana-program-error 2.2.2", + "solana-pubkey 2.4.0", +] + [[package]] name = "spl-memo-interface" version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d4e2aedd58f858337fa609af5ad7100d4a243fdaf6a40d6eb4c28c5f19505d3" dependencies = [ - "solana-instruction", + "solana-instruction 3.1.0", "solana-pubkey 3.0.0", ] +[[package]] +name = "spl-pod" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d994afaf86b779104b4a95ba9ca75b8ced3fdb17ee934e38cb69e72afbe17799" +dependencies = [ + "borsh 1.5.7", + "bytemuck", + "bytemuck_derive", + "num-derive", + "num-traits", + "solana-decode-error", + "solana-msg 2.2.1", + "solana-program-error 2.2.2", + "solana-program-option 2.2.1", + "solana-pubkey 2.4.0", + "solana-zk-sdk 2.3.13", + "thiserror 2.0.17", +] + [[package]] name = "spl-pod" version = "0.7.1" @@ -7494,10 +8639,100 @@ dependencies = [ "num-derive", "num-traits", "num_enum", - "solana-program-error", - "solana-program-option", + "solana-program-error 3.0.0", + "solana-program-option 3.0.0", "solana-pubkey 3.0.0", - "solana-zk-sdk", + "solana-zk-sdk 4.0.0", + "thiserror 2.0.17", +] + +[[package]] +name = "spl-program-error" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d39b5186f42b2b50168029d81e58e800b690877ef0b30580d107659250da1d1" +dependencies = [ + "num-derive", + "num-traits", + "solana-program", + "spl-program-error-derive", + "thiserror 1.0.69", +] + +[[package]] +name = "spl-program-error-derive" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d375dd76c517836353e093c2dbb490938ff72821ab568b545fd30ab3256b3e" +dependencies = [ + "proc-macro2", + "quote", + "sha2 0.10.9", + "syn 2.0.110", +] + +[[package]] +name = "spl-tlv-account-resolution" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd99ff1e9ed2ab86e3fd582850d47a739fec1be9f4661cba1782d3a0f26805f3" +dependencies = [ + "bytemuck", + "num-derive", + "num-traits", + "solana-account-info 2.3.0", + "solana-decode-error", + "solana-instruction 2.3.3", + "solana-msg 2.2.1", + "solana-program-error 2.2.2", + "solana-pubkey 2.4.0", + "spl-discriminator 0.4.1", + "spl-pod 0.5.1", + "spl-program-error", + "spl-type-length-value 0.7.0", + "thiserror 1.0.69", +] + +[[package]] +name = "spl-token" +version = "7.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed320a6c934128d4f7e54fe00e16b8aeaecf215799d060ae14f93378da6dc834" +dependencies = [ + "arrayref", + "bytemuck", + "num-derive", + "num-traits", + "num_enum", + "solana-program", + "thiserror 1.0.69", +] + +[[package]] +name = "spl-token-2022" +version = "7.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9048b26b0df0290f929ff91317c83db28b3ef99af2b3493dd35baa146774924c" +dependencies = [ + "arrayref", + "bytemuck", + "num-derive", + "num-traits", + "num_enum", + "solana-program", + "solana-security-txt", + "solana-zk-sdk 2.3.13", + "spl-elgamal-registry", + "spl-memo", + "spl-pod 0.5.1", + "spl-token", + "spl-token-confidential-transfer-ciphertext-arithmetic", + "spl-token-confidential-transfer-proof-extraction 0.2.1", + "spl-token-confidential-transfer-proof-generation 0.3.0", + "spl-token-group-interface 0.5.0", + "spl-token-metadata-interface 0.6.0", + "spl-transfer-hook-interface", + "spl-type-length-value 0.7.0", "thiserror 2.0.17", ] @@ -7512,20 +8747,46 @@ dependencies = [ "num-derive", "num-traits", "num_enum", - "solana-account-info", - "solana-instruction", - "solana-program-error", - "solana-program-option", - "solana-program-pack", + "solana-account-info 3.1.0", + "solana-instruction 3.1.0", + "solana-program-error 3.0.0", + "solana-program-option 3.0.0", + "solana-program-pack 3.0.0", "solana-pubkey 3.0.0", - "solana-sdk-ids", - "solana-zk-sdk", - "spl-pod", - "spl-token-confidential-transfer-proof-extraction", - "spl-token-confidential-transfer-proof-generation", - "spl-token-group-interface", - "spl-token-metadata-interface", - "spl-type-length-value", + "solana-sdk-ids 3.1.0", + "solana-zk-sdk 4.0.0", + "spl-pod 0.7.1", + "spl-token-confidential-transfer-proof-extraction 0.5.1", + "spl-token-confidential-transfer-proof-generation 0.5.1", + "spl-token-group-interface 0.7.1", + "spl-token-metadata-interface 0.8.0", + "spl-type-length-value 0.9.0", + "thiserror 2.0.17", +] + +[[package]] +name = "spl-token-confidential-transfer-ciphertext-arithmetic" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "170378693c5516090f6d37ae9bad2b9b6125069be68d9acd4865bbe9fc8499fd" +dependencies = [ + "base64 0.22.1", + "bytemuck", + "solana-curve25519 2.3.13", + "solana-zk-sdk 2.3.13", +] + +[[package]] +name = "spl-token-confidential-transfer-proof-extraction" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eff2d6a445a147c9d6dd77b8301b1e116c8299601794b558eafa409b342faf96" +dependencies = [ + "bytemuck", + "solana-curve25519 2.3.13", + "solana-program", + "solana-zk-sdk 2.3.13", + "spl-pod 0.5.1", "thiserror 2.0.17", ] @@ -7536,16 +8797,27 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "879a9ebad0d77383d3ea71e7de50503554961ff0f4ef6cbca39ad126e6f6da3a" dependencies = [ "bytemuck", - "solana-account-info", - "solana-curve25519", - "solana-instruction", - "solana-instructions-sysvar", - "solana-msg", - "solana-program-error", + "solana-account-info 3.1.0", + "solana-curve25519 3.1.1", + "solana-instruction 3.1.0", + "solana-instructions-sysvar 3.0.0", + "solana-msg 3.0.0", + "solana-program-error 3.0.0", "solana-pubkey 3.0.0", - "solana-sdk-ids", - "solana-zk-sdk", - "spl-pod", + "solana-sdk-ids 3.1.0", + "solana-zk-sdk 4.0.0", + "spl-pod 0.7.1", + "thiserror 2.0.17", +] + +[[package]] +name = "spl-token-confidential-transfer-proof-generation" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e3597628b0d2fe94e7900fd17cdb4cfbb31ee35c66f82809d27d86e44b2848b" +dependencies = [ + "curve25519-dalek", + "solana-zk-sdk 2.3.13", "thiserror 2.0.17", ] @@ -7556,10 +8828,29 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a0cd59fce3dc00f563c6fa364d67c3f200d278eae681f4dc250240afcfe044b1" dependencies = [ "curve25519-dalek", - "solana-zk-sdk", + "solana-zk-sdk 4.0.0", "thiserror 2.0.17", ] +[[package]] +name = "spl-token-group-interface" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d595667ed72dbfed8c251708f406d7c2814a3fa6879893b323d56a10bedfc799" +dependencies = [ + "bytemuck", + "num-derive", + "num-traits", + "solana-decode-error", + "solana-instruction 2.3.3", + "solana-msg 2.2.1", + "solana-program-error 2.2.2", + "solana-pubkey 2.4.0", + "spl-discriminator 0.4.1", + "spl-pod 0.5.1", + "thiserror 1.0.69", +] + [[package]] name = "spl-token-group-interface" version = "0.7.1" @@ -7570,11 +8861,11 @@ dependencies = [ "num-derive", "num-traits", "num_enum", - "solana-instruction", - "solana-program-error", + "solana-instruction 3.1.0", + "solana-program-error 3.0.0", "solana-pubkey 3.0.0", - "spl-discriminator", - "spl-pod", + "spl-discriminator 0.5.1", + "spl-pod 0.7.1", "thiserror 2.0.17", ] @@ -7589,15 +8880,36 @@ dependencies = [ "num-derive", "num-traits", "num_enum", - "solana-instruction", - "solana-program-error", - "solana-program-option", - "solana-program-pack", + "solana-instruction 3.1.0", + "solana-program-error 3.0.0", + "solana-program-option 3.0.0", + "solana-program-pack 3.0.0", "solana-pubkey 3.0.0", - "solana-sdk-ids", + "solana-sdk-ids 3.1.0", "thiserror 2.0.17", ] +[[package]] +name = "spl-token-metadata-interface" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfb9c89dbc877abd735f05547dcf9e6e12c00c11d6d74d8817506cab4c99fdbb" +dependencies = [ + "borsh 1.5.7", + "num-derive", + "num-traits", + "solana-borsh 2.2.1", + "solana-decode-error", + "solana-instruction 2.3.3", + "solana-msg 2.2.1", + "solana-program-error 2.2.2", + "solana-pubkey 2.4.0", + "spl-discriminator 0.4.1", + "spl-pod 0.5.1", + "spl-type-length-value 0.7.0", + "thiserror 1.0.69", +] + [[package]] name = "spl-token-metadata-interface" version = "0.8.0" @@ -7607,16 +8919,59 @@ dependencies = [ "borsh 1.5.7", "num-derive", "num-traits", - "solana-borsh", - "solana-instruction", - "solana-program-error", + "solana-borsh 3.0.0", + "solana-instruction 3.1.0", + "solana-program-error 3.0.0", "solana-pubkey 3.0.0", - "spl-discriminator", - "spl-pod", - "spl-type-length-value", + "spl-discriminator 0.5.1", + "spl-pod 0.7.1", + "spl-type-length-value 0.9.0", "thiserror 2.0.17", ] +[[package]] +name = "spl-transfer-hook-interface" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4aa7503d52107c33c88e845e1351565050362c2314036ddf19a36cd25137c043" +dependencies = [ + "arrayref", + "bytemuck", + "num-derive", + "num-traits", + "solana-account-info 2.3.0", + "solana-cpi 2.2.1", + "solana-decode-error", + "solana-instruction 2.3.3", + "solana-msg 2.2.1", + "solana-program-error 2.2.2", + "solana-pubkey 2.4.0", + "spl-discriminator 0.4.1", + "spl-pod 0.5.1", + "spl-program-error", + "spl-tlv-account-resolution", + "spl-type-length-value 0.7.0", + "thiserror 1.0.69", +] + +[[package]] +name = "spl-type-length-value" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba70ef09b13af616a4c987797870122863cba03acc4284f226a4473b043923f9" +dependencies = [ + "bytemuck", + "num-derive", + "num-traits", + "solana-account-info 2.3.0", + "solana-decode-error", + "solana-msg 2.2.1", + "solana-program-error 2.2.2", + "spl-discriminator 0.4.1", + "spl-pod 0.5.1", + "thiserror 1.0.69", +] + [[package]] name = "spl-type-length-value" version = "0.9.0" @@ -7627,11 +8982,11 @@ dependencies = [ "num-derive", "num-traits", "num_enum", - "solana-account-info", - "solana-msg", - "solana-program-error", - "spl-discriminator", - "spl-pod", + "solana-account-info 3.1.0", + "solana-msg 3.0.0", + "solana-program-error 3.0.0", + "spl-discriminator 0.5.1", + "spl-pod 0.7.1", "thiserror 2.0.17", ] @@ -7702,7 +9057,7 @@ dependencies = [ "serde", "serde_json", "sha1", - "sha2", + "sha2 0.10.9", "smallvec", "sqlformat", "sqlx-rt", @@ -7731,7 +9086,7 @@ dependencies = [ "quote", "serde", "serde_json", - "sha2", + "sha2 0.10.9", "sqlx-core", "sqlx-rt", "syn 1.0.109", @@ -8716,6 +10071,12 @@ dependencies = [ "try-lock", ] +[[package]] +name = "wasi" +version = "0.9.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" + [[package]] name = "wasi" version = "0.11.1+wasi-snapshot-preview1" @@ -9375,16 +10736,16 @@ dependencies = [ "prost", "prost-types", "protobuf-src", - "solana-account", + "solana-account 3.2.0", "solana-account-decoder", - "solana-clock", + "solana-clock 3.0.0", "solana-hash 3.1.0", - "solana-message", + "solana-message 3.0.1", "solana-pubkey 3.0.0", - "solana-signature", + "solana-signature 3.1.0", "solana-transaction", "solana-transaction-context", - "solana-transaction-error", + "solana-transaction-error 3.0.0", "solana-transaction-status", "tonic", "tonic-build", diff --git a/Cargo.toml b/Cargo.toml index 9b082a8f..22e8fa03 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -84,16 +84,25 @@ solana-bn254 = "3.1.2" solana-commitment-config = "3.0.0" solana-transaction-status = "3.0.8" +solana-program-option = "3.0.0" +solana-program-pack = "3.0.0" +spl-token-interface = "2.0.0" -light-zero-copy = { version = "0.5",default-features = false} + + +light-zero-copy = { version = "0.6", default-features = false} light-concurrent-merkle-tree = {version = "4", default-features = false } -light-batched-merkle-tree = { version = "0.6", default-features = false} -light-merkle-tree-metadata = { version = "0.6", default-features = false } -light-compressed-account = { version = "0.6", default-features = false } +light-batched-merkle-tree = { version = "0.10", default-features = false} +light-merkle-tree-metadata = { version = "0.10", default-features = false } +light-compressed-account = { version = "0.10.1", default-features = false } light-hasher = { version = "5", features = ["poseidon", "keccak", "sha256"], default-features = false} -light-poseidon = "0.3.0" +light-poseidon = "0.4.0" light-indexed-merkle-tree = { version = "4", default-features = false } -light-event = "0.1" +light-event = "0.21" + +light-token-interface = "0.4" +light-sdk-types = "0.20" + sqlx = { version = "0.6.2", features = [ "macros", diff --git a/src/api/api.rs b/src/api/api.rs index ab6c03a9..e323c2cd 100644 --- a/src/api/api.rs +++ b/src/api/api.rs @@ -101,6 +101,7 @@ pub struct PhotonApi { db_conn: Arc, rpc_client: Arc, prover_url: String, + prover_api_key: Option, } impl PhotonApi { @@ -108,11 +109,13 @@ impl PhotonApi { db_conn: Arc, rpc_client: Arc, prover_url: String, + prover_api_key: Option, ) -> Self { Self { db_conn, rpc_client, prover_url, + prover_api_key, } } } @@ -363,14 +366,26 @@ impl PhotonApi { &self, request: GetValidityProofRequest, ) -> Result { - get_validity_proof(self.db_conn.as_ref(), &self.prover_url, request).await + get_validity_proof( + self.db_conn.as_ref(), + &self.prover_url, + self.prover_api_key.as_deref(), + request, + ) + .await } pub async fn get_validity_proof_v2( &self, request: GetValidityProofRequestV2, ) -> Result { - get_validity_proof_v2(self.db_conn.as_ref(), &self.prover_url, request).await + get_validity_proof_v2( + self.db_conn.as_ref(), + &self.prover_url, + self.prover_api_key.as_deref(), + request, + ) + .await } pub async fn get_latest_compression_signatures( diff --git a/src/api/method/get_compressed_accounts_by_owner/v1.rs b/src/api/method/get_compressed_accounts_by_owner/v1.rs index 19decc47..5e7825e1 100644 --- a/src/api/method/get_compressed_accounts_by_owner/v1.rs +++ b/src/api/method/get_compressed_accounts_by_owner/v1.rs @@ -39,7 +39,7 @@ pub async fn get_compressed_accounts_by_owner( query_builder.build_base_query(conn, &request)?; let columns = format!( - "hash, {}, data_hash, address, owner, tree, leaf_index, seq, slot_created, spent, prev_spent, lamports, discriminator, queue, in_output_queue, nullifier_queue_index, nullified_in_tree, nullifier, tx_hash, tree_type", + "hash, {}, data_hash, address, onchain_pubkey, owner, tree, leaf_index, seq, slot_created, spent, prev_spent, lamports, discriminator, queue, in_output_queue, nullifier_queue_index, nullified_in_tree, nullifier, tx_hash, tree_type", query_builder.data_column ); diff --git a/src/api/method/get_compressed_accounts_by_owner/v2.rs b/src/api/method/get_compressed_accounts_by_owner/v2.rs index c7f86684..a953224b 100644 --- a/src/api/method/get_compressed_accounts_by_owner/v2.rs +++ b/src/api/method/get_compressed_accounts_by_owner/v2.rs @@ -41,7 +41,7 @@ pub async fn get_compressed_accounts_by_owner_v2( query_builder.build_base_query(conn, &request)?; let columns = format!( - "hash, {}, data_hash, address, owner, tree, queue, in_output_queue, nullifier_queue_index, tx_hash, nullifier, leaf_index, seq, slot_created, spent, prev_spent, lamports, discriminator, nullified_in_tree, tree_type", + "hash, {}, data_hash, address, onchain_pubkey, owner, tree, queue, in_output_queue, nullifier_queue_index, tx_hash, nullifier, leaf_index, seq, slot_created, spent, prev_spent, lamports, discriminator, nullified_in_tree, tree_type", query_builder.data_column ); diff --git a/src/api/method/get_compressed_token_balances_by_owner.rs b/src/api/method/get_compressed_token_balances_by_owner.rs index 6298c610..0b4a01e5 100644 --- a/src/api/method/get_compressed_token_balances_by_owner.rs +++ b/src/api/method/get_compressed_token_balances_by_owner.rs @@ -71,8 +71,7 @@ pub async fn get_compressed_token_balances_by_owner( // Only use mint > cursor_mint if we're not filtering by a specific mint // If filtering by a specific mint, the cursor should be ignored or we'd get no results if mint.is_none() { - filter = - filter.and(token_owner_balances::Column::Mint.gt::>(cursor_mint.into())); + filter = filter.and(token_owner_balances::Column::Mint.gt::>(cursor_mint)); } // If a specific mint is provided, we can't paginate within that mint // because there's only one record per owner-mint combination in token_owner_balances diff --git a/src/api/method/get_multiple_new_address_proofs.rs b/src/api/method/get_multiple_new_address_proofs.rs index 9ad38568..485a0e92 100644 --- a/src/api/method/get_multiple_new_address_proofs.rs +++ b/src/api/method/get_multiple_new_address_proofs.rs @@ -1,9 +1,9 @@ use light_compressed_account::TreeType; +use light_sdk_types::constants::ADDRESS_TREE_V1; use sea_orm::{ ConnectionTrait, DatabaseConnection, DatabaseTransaction, Statement, TransactionTrait, }; use serde::{Deserialize, Serialize}; -use solana_pubkey::{pubkey, Pubkey}; use utoipa::ToSchema; use crate::api::error::PhotonApiError; @@ -17,8 +17,6 @@ use std::collections::HashMap; pub const MAX_ADDRESSES: usize = 1000; -pub const ADDRESS_TREE_V1: Pubkey = pubkey!("amt1Ayt45jfbdw5YSo7iz6WZxUmnZsQTYXy82hVwyC2"); - #[derive(Debug, Clone, Serialize, Deserialize, ToSchema, PartialEq, Eq)] #[serde(deny_unknown_fields, rename_all = "camelCase")] #[allow(non_snake_case)] diff --git a/src/api/method/get_queue_elements.rs b/src/api/method/get_queue_elements.rs index 4d4d80fd..622941f7 100644 --- a/src/api/method/get_queue_elements.rs +++ b/src/api/method/get_queue_elements.rs @@ -28,6 +28,8 @@ use utoipa::ToSchema; const MAX_QUEUE_ELEMENTS: u16 = 30_000; const MAX_QUEUE_ELEMENTS_SQLITE: u16 = 500; +const MAX_SQL_PARAMS: usize = 30_000; + /// Encode tree node position as a single u64 /// Format: [level: u8][position: 56 bits] /// Level 0 = leaves, Level tree_height-1 = root @@ -236,13 +238,26 @@ pub async fn get_queue_elements( let (nodes, initial_root, root_seq) = merge_state_queue_proofs(&output_proof_data, &input_proof_data)?; - Some(StateQueueData { - nodes, - initial_root, - root_seq, - output_queue, - input_queue, - }) + let has_output_data = output_queue + .as_ref() + .map(|oq| !oq.leaf_indices.is_empty()) + .unwrap_or(false); + let has_input_data = input_queue + .as_ref() + .map(|iq| !iq.leaf_indices.is_empty()) + .unwrap_or(false); + + if has_output_data || has_input_data { + Some(StateQueueData { + nodes, + initial_root, + root_seq, + output_queue, + input_queue, + }) + } else { + None + } } else { None }; @@ -821,12 +836,16 @@ async fn fetch_address_queue_v2( let mut current_hash = hashed_leaf; for (level, sibling_hash) in proof.proof.iter().enumerate() { - let sibling_pos = if pos % 2 == 0 { pos + 1 } else { pos - 1 }; + let sibling_pos = if pos.is_multiple_of(2) { + pos + 1 + } else { + pos - 1 + }; let sibling_idx = encode_node_index(level as u8, sibling_pos, tree_info.height as u8); nodes_map.insert(sibling_idx, sibling_hash.clone()); - let parent_hash = if pos % 2 == 0 { + let parent_hash = if pos.is_multiple_of(2) { Poseidon::hashv(&[¤t_hash.0, &sibling_hash.0]) } else { Poseidon::hashv(&[&sibling_hash.0, ¤t_hash.0]) @@ -982,7 +1001,11 @@ fn deduplicate_nodes_from_refs( // Walk up the proof path, storing sibling hashes and path node hashes from DB for (level, sibling_hash) in proof_ctx.proof.iter().enumerate() { - let sibling_pos = if pos % 2 == 0 { pos + 1 } else { pos - 1 }; + let sibling_pos = if pos.is_multiple_of(2) { + pos + 1 + } else { + pos - 1 + }; // Store the sibling (from proof) let sibling_idx = encode_node_index(level as u8, sibling_pos, tree_height); @@ -1052,24 +1075,29 @@ async fn fetch_path_nodes_from_db( return Ok(HashMap::new()); } - let path_nodes = state_trees::Entity::find() - .filter( - state_trees::Column::Tree - .eq(tree_bytes) - .and(state_trees::Column::NodeIdx.is_in(all_path_indices)), - ) - .all(tx) - .await - .map_err(|e| { - PhotonApiError::UnexpectedError(format!("Failed to fetch path nodes: {}", e)) - })?; - let mut result = HashMap::new(); - for node in path_nodes { - let hash = Hash::try_from(node.hash).map_err(|e| { - PhotonApiError::UnexpectedError(format!("Invalid hash in path node: {}", e)) - })?; - result.insert(node.node_idx, hash); + + let tree_bytes_ref = tree_bytes.clone(); + + for chunk in all_path_indices.chunks(MAX_SQL_PARAMS) { + let path_nodes = state_trees::Entity::find() + .filter( + state_trees::Column::Tree + .eq(tree_bytes_ref.clone()) + .and(state_trees::Column::NodeIdx.is_in(chunk.to_vec())), + ) + .all(tx) + .await + .map_err(|e| { + PhotonApiError::UnexpectedError(format!("Failed to fetch path nodes: {}", e)) + })?; + + for node in path_nodes { + let hash = Hash::try_from(node.hash).map_err(|e| { + PhotonApiError::UnexpectedError(format!("Invalid hash in path node: {}", e)) + })?; + result.insert(node.node_idx, hash); + } } Ok(result) diff --git a/src/api/method/get_validity_proof/prover/helpers.rs b/src/api/method/get_validity_proof/prover/helpers.rs index ef2741bf..fc14723b 100644 --- a/src/api/method/get_validity_proof/prover/helpers.rs +++ b/src/api/method/get_validity_proof/prover/helpers.rs @@ -13,48 +13,32 @@ use light_compressed_account::hash_chain::create_two_inputs_hash_chain; pub fn convert_non_inclusion_merkle_proof_to_hex( non_inclusion_merkle_proof_inputs: Vec, ) -> Vec { - let mut inputs: Vec = Vec::new(); - for i in 0..non_inclusion_merkle_proof_inputs.len() { - let input = NonInclusionHexInputsForProver { - root: hash_to_hex(&non_inclusion_merkle_proof_inputs[i].root), - value: pubkey_to_hex(&non_inclusion_merkle_proof_inputs[i].address), - path_index: non_inclusion_merkle_proof_inputs[i].lowElementLeafIndex, - path_elements: non_inclusion_merkle_proof_inputs[i] - .proof - .iter() - .map(hash_to_hex) - .collect(), - next_index: non_inclusion_merkle_proof_inputs[i].nextIndex, - leaf_lower_range_value: pubkey_to_hex( - &non_inclusion_merkle_proof_inputs[i].lowerRangeAddress, - ), - leaf_higher_range_value: pubkey_to_hex( - &non_inclusion_merkle_proof_inputs[i].higherRangeAddress, - ), - }; - inputs.push(input); - } - inputs + non_inclusion_merkle_proof_inputs + .iter() + .map(|input| NonInclusionHexInputsForProver { + root: hash_to_hex(&input.root), + value: pubkey_to_hex(&input.address), + path_index: input.lowElementLeafIndex, + path_elements: input.proof.iter().map(hash_to_hex).collect(), + next_index: input.nextIndex, + leaf_lower_range_value: pubkey_to_hex(&input.lowerRangeAddress), + leaf_higher_range_value: pubkey_to_hex(&input.higherRangeAddress), + }) + .collect() } pub fn convert_inclusion_proofs_to_hex( inclusion_proof_inputs: Vec, ) -> Vec { - let mut inputs: Vec = Vec::new(); - for i in 0..inclusion_proof_inputs.len() { - let input = InclusionHexInputsForProver { - root: hash_to_hex(&inclusion_proof_inputs[i].root), - path_index: inclusion_proof_inputs[i].leaf_index, - path_elements: inclusion_proof_inputs[i] - .proof - .iter() - .map(hash_to_hex) - .collect(), - leaf: hash_to_hex(&inclusion_proof_inputs[i].hash), - }; - inputs.push(input); - } - inputs + inclusion_proof_inputs + .iter() + .map(|input| InclusionHexInputsForProver { + root: hash_to_hex(&input.root), + path_index: input.leaf_index, + path_elements: input.proof.iter().map(hash_to_hex).collect(), + leaf: hash_to_hex(&input.hash), + }) + .collect() } pub fn hash_to_hex(hash: &Hash) -> String { @@ -71,11 +55,7 @@ fn pubkey_to_hex(pubkey: &SerializablePubkey) -> String { } pub fn deserialize_hex_string_to_bytes(hex_str: &str) -> Result, PhotonApiError> { - let hex_str = if hex_str.starts_with("0x") { - &hex_str[2..] - } else { - hex_str - }; + let hex_str = hex_str.strip_prefix("0x").unwrap_or(hex_str); let hex_str = format!("{:0>64}", hex_str); hex::decode(hex_str) diff --git a/src/api/method/get_validity_proof/prover/prove.rs b/src/api/method/get_validity_proof/prover/prove.rs index e5f28f6e..f4d3d6f1 100644 --- a/src/api/method/get_validity_proof/prover/prove.rs +++ b/src/api/method/get_validity_proof/prover/prove.rs @@ -24,6 +24,7 @@ pub(crate) async fn generate_proof( db_new_address_proofs: Vec, root_history_capacity: u64, prover_url: &str, + prover_api_key: Option<&str>, ) -> Result { let state_tree_height = if db_account_proofs.is_empty() { 0 @@ -106,15 +107,18 @@ pub(crate) async fn generate_proof( PhotonApiError::UnexpectedError(format!("Error serializing prover request: {}", e)) })?; - let res = client + let mut request_builder = client .post(&prover_request_url) .body(json_body) - .header("Content-Type", "application/json") - .send() - .await - .map_err(|e| { - PhotonApiError::UnexpectedError(format!("Error sending request to prover: {}", e)) - })?; + .header("Content-Type", "application/json"); + + if let Some(api_key) = prover_api_key { + request_builder = request_builder.header("Authorization", format!("Bearer {}", api_key)); + } + + let res = request_builder.send().await.map_err(|e| { + PhotonApiError::UnexpectedError(format!("Error sending request to prover: {}", e)) + })?; if !res.status().is_success() { return Err(PhotonApiError::UnexpectedError(format!( diff --git a/src/api/method/get_validity_proof/prover/structs.rs b/src/api/method/get_validity_proof/prover/structs.rs index b2017a75..5d7f02b6 100644 --- a/src/api/method/get_validity_proof/prover/structs.rs +++ b/src/api/method/get_validity_proof/prover/structs.rs @@ -2,7 +2,6 @@ use crate::ingester::parser::tree_info::TreeInfo; use jsonrpsee_core::Serialize; use num_traits::identities::Zero; use serde::Deserialize; -use utoipa::ToSchema; #[derive(Debug, Clone)] pub(crate) struct AccountProofDetail { @@ -66,13 +65,43 @@ pub(crate) struct ProofABC { pub c: [u8; 64], } -#[derive(Serialize, Deserialize, Default, ToSchema, Debug, Clone)] +#[derive(Serialize, Deserialize, Default, Debug, Clone)] pub struct CompressedProof { pub a: Vec, pub b: Vec, pub c: Vec, } +impl<'__s> utoipa::ToSchema<'__s> for CompressedProof { + fn schema() -> (&'__s str, utoipa::openapi::RefOr) { + use utoipa::openapi::*; + // Vec is serialized by serde as a JSON array of integers. + // utoipa's default renders Vec as string/binary which is wrong. + let byte_array = || { + RefOr::T(Schema::Array( + ArrayBuilder::new() + .items(RefOr::T(Schema::Object( + ObjectBuilder::new() + .schema_type(SchemaType::Integer) + .build(), + ))) + .build(), + )) + }; + let schema = Schema::Object( + ObjectBuilder::new() + .property("a", byte_array()) + .required("a") + .property("b", byte_array()) + .required("b") + .property("c", byte_array()) + .required("c") + .build(), + ); + ("CompressedProof", RefOr::T(schema)) + } +} + #[derive(Serialize, Deserialize, Debug)] #[serde(rename_all = "camelCase")] pub struct HexBatchInputsForProver { diff --git a/src/api/method/get_validity_proof/v1.rs b/src/api/method/get_validity_proof/v1.rs index a8688484..ddbc2ac1 100644 --- a/src/api/method/get_validity_proof/v1.rs +++ b/src/api/method/get_validity_proof/v1.rs @@ -1,5 +1,5 @@ use crate::api::method::get_multiple_new_address_proofs::{ - get_multiple_new_address_proofs_helper, AddressWithTree, ADDRESS_TREE_V1, MAX_ADDRESSES, + get_multiple_new_address_proofs_helper, AddressWithTree, MAX_ADDRESSES, }; use crate::api::method::get_validity_proof::prover::prove::generate_proof; use crate::api::method::get_validity_proof::CompressedProof; @@ -11,6 +11,7 @@ use crate::{ api::error::PhotonApiError, common::typedefs::serializable_pubkey::SerializablePubkey, }; use jsonrpsee_core::Serialize; +use light_sdk_types::constants::ADDRESS_TREE_V1; use sea_orm::{ColumnTrait, DatabaseConnection, EntityTrait, QueryFilter, TransactionTrait}; use serde::Deserialize; use utoipa::ToSchema; @@ -57,6 +58,7 @@ pub struct GetValidityProofResponse { pub async fn get_validity_proof( conn: &DatabaseConnection, prover_url: &str, + prover_api_key: Option<&str>, mut request: GetValidityProofRequest, ) -> Result { if request.hashes.is_empty() @@ -149,6 +151,7 @@ pub async fn get_validity_proof( db_new_address_proofs, root_history_capacity, prover_url, + prover_api_key, ) .await?; diff --git a/src/api/method/get_validity_proof/v2.rs b/src/api/method/get_validity_proof/v2.rs index a4c054a8..c085ca28 100644 --- a/src/api/method/get_validity_proof/v2.rs +++ b/src/api/method/get_validity_proof/v2.rs @@ -130,6 +130,7 @@ impl From> for RootIndex { pub async fn get_validity_proof_v2( conn: &DatabaseConnection, prover_url: &str, + prover_api_key: Option<&str>, request: GetValidityProofRequestV2, ) -> Result { if request.hashes.is_empty() && request.new_addresses_with_trees.is_empty() { @@ -244,7 +245,7 @@ pub async fn get_validity_proof_v2( // Try to get tree from prove-by-index accounts accounts_for_prove_by_index_inputs .iter() - .find_map(|opt_acc| opt_acc.as_ref().map(|acc| acc.merkle_context.tree.clone())) + .find_map(|opt_acc| opt_acc.as_ref().map(|acc| acc.merkle_context.tree)) }; let root_history_capacity = if let Some(tree_pubkey) = tree_pubkey { @@ -281,6 +282,7 @@ pub async fn get_validity_proof_v2( db_new_address_proofs_for_prover, root_history_capacity, prover_url, + prover_api_key, ) .await?; diff --git a/src/api/method/utils.rs b/src/api/method/utils.rs index 1cf78467..56240c5d 100644 --- a/src/api/method/utils.rs +++ b/src/api/method/utils.rs @@ -35,6 +35,12 @@ pub fn parse_decimal(value: Decimal) -> Result { .map_err(|_| PhotonApiError::UnexpectedError("Invalid decimal value".to_string())) } +pub fn parse_account_discriminator( + discriminator: Option, +) -> Result, PhotonApiError> { + discriminator.map(parse_decimal).transpose() +} + pub(crate) fn parse_leaf_index(leaf_index: i64) -> Result { leaf_index .try_into() diff --git a/src/api/rpc_server.rs b/src/api/rpc_server.rs index 231648ca..5cc11dd8 100644 --- a/src/api/rpc_server.rs +++ b/src/api/rpc_server.rs @@ -10,7 +10,11 @@ use tower_http::cors::{Any, CorsLayer}; use super::api::PhotonApi; -pub async fn run_server(api: PhotonApi, port: u16) -> Result { +pub async fn run_server( + api: PhotonApi, + port: u16, + max_connections: u32, +) -> Result { let addr = SocketAddr::from(([0, 0, 0, 0], port)); let cors = CorsLayer::new() .allow_methods([Method::POST, Method::GET]) @@ -21,6 +25,7 @@ pub async fn run_server(api: PhotonApi, port: u16) -> Result PathBuf { diff --git a/src/common/token_layout.rs b/src/common/token_layout.rs new file mode 100644 index 00000000..1376fb3e --- /dev/null +++ b/src/common/token_layout.rs @@ -0,0 +1,18 @@ +/// Shared constants for Light Token account byte layouts. +/// +/// SPL token account base length. +pub const SPL_TOKEN_ACCOUNT_BASE_LEN: usize = 165; +/// Account type marker byte offset in token/mint account data. +pub const TOKEN_ACCOUNT_TYPE_OFFSET: usize = SPL_TOKEN_ACCOUNT_BASE_LEN; +/// 1-indexed equivalent for SQL substring functions. +pub const TOKEN_ACCOUNT_TYPE_OFFSET_SQL: usize = TOKEN_ACCOUNT_TYPE_OFFSET + 1; + +/// AccountType::Mint discriminator value. +pub const ACCOUNT_TYPE_MINT: u8 = 1; + +/// Compressed mint PDA offset range `[84..116]` (32 bytes). +pub const COMPRESSED_MINT_PDA_OFFSET: usize = 84; +pub const COMPRESSED_MINT_PDA_LEN: usize = 32; +pub const COMPRESSED_MINT_PDA_END: usize = COMPRESSED_MINT_PDA_OFFSET + COMPRESSED_MINT_PDA_LEN; +/// 1-indexed equivalent for SQL substring functions. +pub const COMPRESSED_MINT_PDA_OFFSET_SQL: usize = COMPRESSED_MINT_PDA_OFFSET + 1; diff --git a/src/common/typedefs/account/context.rs b/src/common/typedefs/account/context.rs index eb041626..73dca488 100644 --- a/src/common/typedefs/account/context.rs +++ b/src/common/typedefs/account/context.rs @@ -1,5 +1,5 @@ use crate::api::error::PhotonApiError; -use crate::api::method::utils::{parse_decimal, parse_leaf_index}; +use crate::api::method::utils::{parse_account_discriminator, parse_decimal, parse_leaf_index}; use crate::common::typedefs::account::{Account, AccountData}; use crate::common::typedefs::bs64_string::Base64String; use crate::common::typedefs::hash::Hash; @@ -17,7 +17,7 @@ use utoipa::ToSchema; /// - Internal (state_updates,..) /// - GetTransactionWithCompressionInfo (internally) /// - GetTransactionWithCompressionInfoV2 (internally) -/// All endpoints return AccountV2. +/// All endpoints return AccountV2. #[derive(Debug, Clone, PartialEq, Eq, Serialize, ToSchema, Default)] #[serde(deny_unknown_fields, rename_all = "camelCase")] pub struct AccountContext { @@ -107,11 +107,12 @@ impl TryFrom for AccountWithContext { type Error = PhotonApiError; fn try_from(account: Model) -> Result { - let data = match (account.data, account.data_hash, account.discriminator) { + let parsed_discriminator = parse_account_discriminator(account.discriminator.clone())?; + let data = match (account.data, account.data_hash, parsed_discriminator) { (Some(data), Some(data_hash), Some(discriminator)) => Some(AccountData { data: Base64String(data), data_hash: data_hash.try_into()?, - discriminator: UnsignedInteger(parse_decimal(discriminator)?), + discriminator: UnsignedInteger(discriminator), }), (None, None, None) => None, _ => { diff --git a/src/common/typedefs/account/mod.rs b/src/common/typedefs/account/mod.rs index a508a289..1a8b7f63 100644 --- a/src/common/typedefs/account/mod.rs +++ b/src/common/typedefs/account/mod.rs @@ -3,5 +3,8 @@ mod v1; mod v2; pub use context::{AccountContext, AccountWithContext}; -pub use v1::{Account, AccountData}; +pub use v1::{ + Account, AccountData, C_TOKEN_DISCRIMINATOR_V1, C_TOKEN_DISCRIMINATOR_V2, + C_TOKEN_DISCRIMINATOR_V3, +}; pub use v2::AccountV2; diff --git a/src/common/typedefs/account/v1.rs b/src/common/typedefs/account/v1.rs index bed21dd3..60f63312 100644 --- a/src/common/typedefs/account/v1.rs +++ b/src/common/typedefs/account/v1.rs @@ -1,5 +1,5 @@ use crate::api::error::PhotonApiError; -use crate::api::method::utils::parse_decimal; +use crate::api::method::utils::{parse_account_discriminator, parse_decimal}; use crate::common::typedefs::bs64_string::Base64String; use crate::common::typedefs::hash::Hash; use crate::common::typedefs::serializable_pubkey::SerializablePubkey; @@ -7,12 +7,16 @@ use crate::common::typedefs::token_data::TokenData; use crate::common::typedefs::unsigned_integer::UnsignedInteger; use crate::dao::generated::accounts::Model; use crate::ingester::error::IngesterError; -use crate::ingester::persist::COMPRESSED_TOKEN_PROGRAM; +use crate::ingester::persist::LIGHT_TOKEN_PROGRAM_ID; use jsonrpsee_core::Serialize; +use light_sdk_types::TOKEN_COMPRESSED_ACCOUNT_DISCRIMINATOR; use utoipa::ToSchema; -pub const C_TOKEN_DISCRIMINATOR_V1: [u8; 8] = [2, 0, 0, 0, 0, 0, 0, 0]; +/// Re-export V1 discriminator from light-sdk-types under local naming convention. +pub const C_TOKEN_DISCRIMINATOR_V1: [u8; 8] = TOKEN_COMPRESSED_ACCOUNT_DISCRIMINATOR; +/// V2: batched Merkle trees (not yet exported from SDK crates) pub const C_TOKEN_DISCRIMINATOR_V2: [u8; 8] = [0, 0, 0, 0, 0, 0, 0, 3]; +/// V3/ShaFlat: SHA256 flat hash with TLV extensions (not yet exported from SDK crates) pub const C_TOKEN_DISCRIMINATOR_V3: [u8; 8] = [0, 0, 0, 0, 0, 0, 0, 4]; #[derive(Debug, Clone, PartialEq, Eq, Serialize, ToSchema, Default)] @@ -37,7 +41,7 @@ impl Account { pub fn parse_token_data(&self) -> Result, IngesterError> { match self.data.as_ref() { Some(data) - if self.owner.0 == COMPRESSED_TOKEN_PROGRAM && data.is_c_token_discriminator() => + if self.owner.0 == LIGHT_TOKEN_PROGRAM_ID && data.is_c_token_discriminator() => { let data_slice = data.data.0.as_slice(); let token_data = TokenData::parse(data_slice).map_err(|e| { @@ -71,11 +75,12 @@ impl TryFrom for Account { type Error = PhotonApiError; fn try_from(account: Model) -> Result { - let data = match (account.data, account.data_hash, account.discriminator) { + let parsed_discriminator = parse_account_discriminator(account.discriminator.clone())?; + let data = match (account.data, account.data_hash, parsed_discriminator) { (Some(data), Some(data_hash), Some(discriminator)) => Some(AccountData { data: Base64String(data), data_hash: data_hash.try_into()?, - discriminator: UnsignedInteger(parse_decimal(discriminator)?), + discriminator: UnsignedInteger(discriminator), }), (None, None, None) => None, _ => { diff --git a/src/common/typedefs/account/v2.rs b/src/common/typedefs/account/v2.rs index 41ebb424..dba9a545 100644 --- a/src/common/typedefs/account/v2.rs +++ b/src/common/typedefs/account/v2.rs @@ -1,6 +1,6 @@ use crate::api::error::PhotonApiError; use crate::api::method::get_validity_proof::MerkleContextV2; -use crate::api::method::utils::parse_decimal; +use crate::api::method::utils::{parse_account_discriminator, parse_decimal}; use crate::common::typedefs::account::{AccountData, AccountWithContext}; use crate::common::typedefs::bs64_string::Base64String; use crate::common::typedefs::hash::Hash; @@ -9,7 +9,7 @@ use crate::common::typedefs::token_data::TokenData; use crate::common::typedefs::unsigned_integer::UnsignedInteger; use crate::dao::generated::accounts::Model; use crate::ingester::error::IngesterError; -use crate::ingester::persist::COMPRESSED_TOKEN_PROGRAM; +use crate::ingester::persist::LIGHT_TOKEN_PROGRAM_ID; use serde::Serialize; use utoipa::ToSchema; @@ -41,7 +41,7 @@ impl AccountV2 { pub fn parse_token_data(&self) -> Result, IngesterError> { match self.data.as_ref() { Some(data) - if self.owner.0 == COMPRESSED_TOKEN_PROGRAM && data.is_c_token_discriminator() => + if self.owner.0 == LIGHT_TOKEN_PROGRAM_ID && data.is_c_token_discriminator() => { let data_slice = data.data.0.as_slice(); let token_data = TokenData::parse(data_slice).map_err(|e| { @@ -58,11 +58,12 @@ impl TryFrom for AccountV2 { type Error = PhotonApiError; fn try_from(account: Model) -> Result { - let data = match (account.data, account.data_hash, account.discriminator) { + let parsed_discriminator = parse_account_discriminator(account.discriminator.clone())?; + let data = match (account.data, account.data_hash, parsed_discriminator) { (Some(data), Some(data_hash), Some(discriminator)) => Some(AccountData { data: Base64String(data), data_hash: data_hash.try_into()?, - discriminator: UnsignedInteger(parse_decimal(discriminator)?), + discriminator: UnsignedInteger(discriminator), }), (None, None, None) => None, _ => { @@ -90,7 +91,11 @@ impl TryFrom for AccountV2 { merkle_context: MerkleContextV2 { tree_type: account.tree_type.map(|t| t as u16).unwrap_or(0), tree: account.tree.try_into()?, - queue: account.queue.unwrap_or_default().try_into()?, + queue: account + .queue + .map(SerializablePubkey::try_from) + .transpose()? + .unwrap_or_default(), cpi_context: None, next_tree_context: None, }, @@ -102,9 +107,9 @@ impl From<&AccountWithContext> for AccountV2 { fn from(x: &AccountWithContext) -> Self { AccountV2 { hash: x.account.hash.clone(), - address: x.account.address.clone(), + address: x.account.address, data: x.account.data.clone(), - owner: x.account.owner.clone(), + owner: x.account.owner, lamports: x.account.lamports, leaf_index: x.account.leaf_index, seq: x.account.seq, @@ -112,8 +117,8 @@ impl From<&AccountWithContext> for AccountV2 { prove_by_index: x.context.in_output_queue, merkle_context: MerkleContextV2 { tree_type: x.context.tree_type, - tree: x.account.tree.clone(), - queue: x.context.queue.clone(), + tree: x.account.tree, + queue: x.context.queue, cpi_context: None, next_tree_context: None, }, diff --git a/src/common/typedefs/token_data.rs b/src/common/typedefs/token_data.rs index b91e9a53..16d5b801 100644 --- a/src/common/typedefs/token_data.rs +++ b/src/common/typedefs/token_data.rs @@ -44,13 +44,11 @@ pub struct TokenData { pub delegate: Option, /// The account's state pub state: AccountState, - /// TokenExtension TLV data (raw bytes, opaque to the indexer) + /// TokenExtension TLV data pub tlv: Option, } impl TokenData { - /// Deserializes base fields via Borsh, then reads TLV as raw bytes (1-byte - /// option tag + remaining bytes). pub fn parse(data: &[u8]) -> Result { let mut buf = data; @@ -66,17 +64,19 @@ impl TokenData { let option_tag = buf[0]; buf = &buf[1..]; - match option_tag { - 0 => None, - 1 if !buf.is_empty() => Some(Base64String(buf.to_vec())), - other => { + if option_tag == 0 { + None + } else if option_tag == 1 && !buf.is_empty() { + Some(Base64String(buf.to_vec())) + } else { + if option_tag != 0 && option_tag != 1 { log::warn!( - "Unexpected TLV: option_tag={}, remaining_bytes={}", - other, + "Unknown TLV option_tag={} with {} bytes remaining", + option_tag, buf.len() ); - None } + None } }; @@ -114,177 +114,218 @@ mod tests { use super::*; use solana_pubkey::Pubkey; - fn build_onchain_token_data_bytes(has_delegate: bool, tlv: Option<&[u8]>) -> Vec { - let mut bytes = Vec::new(); - bytes.extend_from_slice(&[0x11u8; 32]); // mint - bytes.extend_from_slice(&[0x22u8; 32]); // owner - bytes.extend_from_slice(&100u64.to_le_bytes()); // amount - if has_delegate { - bytes.push(1); - bytes.extend_from_slice(&[0x33u8; 32]); - } else { - bytes.push(0); - } - bytes.push(0); // state = initialized - match tlv { - Some(tlv_data) => { - bytes.push(1); // Option tag = Some - bytes.extend_from_slice(tlv_data); - } - None => { - bytes.push(0); // Option tag = None - } + fn sample_token_data(tlv: Option>) -> TokenData { + TokenData { + mint: SerializablePubkey::from(Pubkey::new_unique()), + owner: SerializablePubkey::from(Pubkey::new_unique()), + amount: UnsignedInteger(42), + delegate: Some(SerializablePubkey::from(Pubkey::new_unique())), + state: AccountState::initialized, + tlv: tlv.map(Base64String), } - bytes - } - - fn build_compressed_only_tlv() -> Vec { - let mut tlv = Vec::new(); - tlv.extend_from_slice(&1u32.to_le_bytes()); // vec len = 1 element - tlv.push(31); // ExtensionStruct enum variant = CompressedOnly - tlv.extend_from_slice(&500u64.to_le_bytes()); // delegated_amount - tlv.extend_from_slice(&0u64.to_le_bytes()); // withheld_transfer_fee - tlv.push(1); // is_ata - tlv } #[test] - fn parse_v1_no_delegate_no_tlv() { - let bytes = build_onchain_token_data_bytes(false, None); - let td = TokenData::parse(&bytes).unwrap(); - assert_eq!(td.mint.0, Pubkey::from([0x11; 32])); - assert_eq!(td.owner.0, Pubkey::from([0x22; 32])); - assert_eq!(td.amount.0, 100); - assert!(td.delegate.is_none()); - assert_eq!(td.state, AccountState::initialized); - assert!(td.tlv.is_none()); - } + fn serialize_parse_roundtrip_without_tlv() { + let token_data = sample_token_data(None); - #[test] - fn parse_v1_with_delegate_no_tlv() { - let bytes = build_onchain_token_data_bytes(true, None); - let td = TokenData::parse(&bytes).unwrap(); - assert_eq!(td.delegate.unwrap().0, Pubkey::from([0x33; 32])); - assert!(td.tlv.is_none()); - } + let mut bytes = Vec::new(); + borsh::BorshSerialize::serialize(&token_data, &mut bytes).unwrap(); - #[test] - fn parse_v3_with_compressed_only_tlv() { - let tlv_raw = build_compressed_only_tlv(); - let bytes = build_onchain_token_data_bytes(true, Some(&tlv_raw)); - - let td = TokenData::parse(&bytes).unwrap(); - assert_eq!(td.amount.0, 100); - assert!(td.delegate.is_some()); - let tlv = td.tlv.unwrap(); - assert_eq!(tlv.0, tlv_raw); + let parsed = TokenData::parse(&bytes).unwrap(); + assert_eq!(parsed, token_data); } #[test] - fn parse_v3_no_delegate_with_tlv() { - let tlv_raw = build_compressed_only_tlv(); - let bytes = build_onchain_token_data_bytes(false, Some(&tlv_raw)); - - let td = TokenData::parse(&bytes).unwrap(); - assert!(td.delegate.is_none()); - assert!(td.tlv.is_some()); - assert_eq!(td.tlv.unwrap().0, tlv_raw); - } + fn serialize_parse_roundtrip_with_tlv_raw_bytes() { + // Starts with a vec-length-like prefix to ensure serializer doesn't add another one. + let raw_tlv = vec![2, 0, 0, 0, 7, 8, 9, 10]; + let token_data = sample_token_data(Some(raw_tlv.clone())); - #[derive(Debug, BorshDeserialize)] - struct OldTokenData { - pub mint: SerializablePubkey, - pub owner: SerializablePubkey, - pub amount: UnsignedInteger, - pub delegate: Option, - pub state: AccountState, - pub tlv: Option, - } + let mut bytes = Vec::new(); + borsh::BorshSerialize::serialize(&token_data, &mut bytes).unwrap(); - #[test] - fn old_try_from_slice_fails_with_not_all_bytes_read() { - let tlv_raw = build_compressed_only_tlv(); - let bytes = build_onchain_token_data_bytes(true, Some(&tlv_raw)); - - let err = OldTokenData::try_from_slice(&bytes) - .expect_err("old BorshDeserialize must fail on V3 TLV data"); - - assert_eq!(err.kind(), std::io::ErrorKind::InvalidData); - assert!( - err.to_string().contains("Not all bytes read"), - "Expected exact production error 'Not all bytes read', got: {}", - err - ); + // Fixed fields: mint(32) + owner(32) + amount(8) + delegate_option(1+32) + state(1) + let fixed_len = 32 + 32 + 8 + 33 + 1; + assert_eq!(bytes[fixed_len], 1); // tlv option tag + assert_eq!(&bytes[(fixed_len + 1)..], raw_tlv.as_slice()); + + let parsed = TokenData::parse(&bytes).unwrap(); + assert_eq!(parsed, token_data); } #[test] - fn new_parse_succeeds_where_old_try_from_slice_fails() { - let tlv_raw = build_compressed_only_tlv(); - let bytes = build_onchain_token_data_bytes(true, Some(&tlv_raw)); - - // Old code fails: - assert!(OldTokenData::try_from_slice(&bytes).is_err()); - // New code succeeds and captures TLV: - let td = TokenData::parse(&bytes).unwrap(); - assert_eq!(td.tlv.unwrap().0, tlv_raw); + fn serialize_parse_roundtrip_without_delegate() { + let token_data = TokenData { + mint: SerializablePubkey::from(Pubkey::new_unique()), + owner: SerializablePubkey::from(Pubkey::new_unique()), + amount: UnsignedInteger(123456789), + delegate: None, + state: AccountState::frozen, + tlv: None, + }; + + let mut bytes = Vec::new(); + borsh::BorshSerialize::serialize(&token_data, &mut bytes).unwrap(); + + // Fixed fields: mint(32) + owner(32) + amount(8) + delegate_option(1) + state(1) + tlv_option(1) = 75 + assert_eq!(bytes.len(), 75); + + let parsed = TokenData::parse(&bytes).unwrap(); + assert_eq!(parsed, token_data); } #[test] - fn roundtrip_no_tlv() { - let original = TokenData { - mint: SerializablePubkey(Pubkey::new_unique()), - owner: SerializablePubkey(Pubkey::new_unique()), - amount: UnsignedInteger(42), + fn serialize_tlv_option_none_explicit() { + let token_data = TokenData { + mint: SerializablePubkey::from([1u8; 32]), + owner: SerializablePubkey::from([2u8; 32]), + amount: UnsignedInteger(100), delegate: None, state: AccountState::initialized, tlv: None, }; + let mut bytes = Vec::new(); - borsh::BorshSerialize::serialize(&original, &mut bytes).unwrap(); + borsh::BorshSerialize::serialize(&token_data, &mut bytes).unwrap(); + + // TLV should serialize as a single 0 byte at the end + assert_eq!(bytes.len(), 75); // 32+32+8+1+1+1 = 75 + assert_eq!(*bytes.last().unwrap(), 0); // Last byte should be 0 (None option) + let parsed = TokenData::parse(&bytes).unwrap(); - assert_eq!(parsed, original); + assert_eq!(parsed.tlv, None); } #[test] - fn roundtrip_with_tlv() { - let original = TokenData { - mint: SerializablePubkey(Pubkey::new_unique()), - owner: SerializablePubkey(Pubkey::new_unique()), - amount: UnsignedInteger(1_000_000), - delegate: Some(SerializablePubkey(Pubkey::new_unique())), + fn serialize_empty_tlv_vec() { + let token_data = TokenData { + mint: SerializablePubkey::from([3u8; 32]), + owner: SerializablePubkey::from([4u8; 32]), + amount: UnsignedInteger(999), + delegate: Some(SerializablePubkey::from([5u8; 32])), state: AccountState::frozen, - tlv: Some(Base64String(vec![0xAA, 0xBB, 0xCC])), + tlv: Some(Base64String(vec![])), }; + let mut bytes = Vec::new(); - borsh::BorshSerialize::serialize(&original, &mut bytes).unwrap(); + borsh::BorshSerialize::serialize(&token_data, &mut bytes).unwrap(); + + // Fixed + tlv option tag (1) + let fixed_len = 32 + 32 + 8 + 33 + 1; + assert_eq!(bytes[fixed_len], 1); // Option::Some tag + assert_eq!(bytes.len(), fixed_len + 1); // No data after the tag + + // NOTE: parse() treats empty TLV (option=1, no data) as None per line 69 let parsed = TokenData::parse(&bytes).unwrap(); - assert_eq!(parsed, original); + assert_eq!(parsed.tlv, None); // parse behavior: empty TLV -> None } #[test] - fn byte_layout_no_delegate_no_tlv() { - let bytes = build_onchain_token_data_bytes(false, None); - // mint(32) + owner(32) + amount(8) + delegate_none(1) + state(1) + tlv_none(1) = 75 - assert_eq!(bytes.len(), 75); - let td = TokenData::parse(&bytes).unwrap(); - assert!(td.tlv.is_none()); + fn byte_layout_consistency() { + // Verify exact byte layout matches what parse() expects + let mint = [11u8; 32]; + let owner = [22u8; 32]; + let amount = 42u64; + let delegate = [33u8; 32]; + let state = 1u8; // frozen + let tlv_data = vec![0xAA, 0xBB, 0xCC]; + + let token_data = TokenData { + mint: SerializablePubkey::from(mint), + owner: SerializablePubkey::from(owner), + amount: UnsignedInteger(amount), + delegate: Some(SerializablePubkey::from(delegate)), + state: AccountState::frozen, + tlv: Some(Base64String(tlv_data.clone())), + }; + + let mut bytes = Vec::new(); + borsh::BorshSerialize::serialize(&token_data, &mut bytes).unwrap(); + + // Verify manual layout + assert_eq!(&bytes[0..32], &mint); + assert_eq!(&bytes[32..64], &owner); + assert_eq!(&bytes[64..72], &amount.to_le_bytes()); + assert_eq!(bytes[72], 1); // delegate option tag + assert_eq!(&bytes[73..105], &delegate); + assert_eq!(bytes[105], state); + assert_eq!(bytes[106], 1); // tlv option tag + assert_eq!(&bytes[107..], &tlv_data[..]); } #[test] - fn byte_layout_with_delegate_no_tlv() { - let bytes = build_onchain_token_data_bytes(true, None); - // mint(32) + owner(32) + amount(8) + delegate_some(1+32) + state(1) + tlv_none(1) = 107 + fn compatibility_with_light_token_format() { + // This test ensures our serialization matches the light program's expected format + // based on the TokenData struct from light-token-interface + let token_data = TokenData { + mint: SerializablePubkey::from([0x10; 32]), + owner: SerializablePubkey::from([0x20; 32]), + amount: UnsignedInteger(1_000_000), + delegate: Some(SerializablePubkey::from([0x30; 32])), + state: AccountState::initialized, + tlv: None, + }; + + let mut bytes = Vec::new(); + borsh::BorshSerialize::serialize(&token_data, &mut bytes).unwrap(); + + // Verify the format: + // 32 bytes: mint pubkey + // 32 bytes: owner pubkey + // 8 bytes: amount (u64 LE) + // 1 byte: delegate option (1 = Some) + // 32 bytes: delegate pubkey + // 1 byte: state (0 = initialized) + // 1 byte: tlv option (0 = None) assert_eq!(bytes.len(), 107); + + let parsed = TokenData::parse(&bytes).unwrap(); + assert_eq!(parsed, token_data); + } + + #[test] + fn various_amounts() { + for amount in [0u64, 1, 1000, u64::MAX] { + let token_data = TokenData { + mint: SerializablePubkey::from(Pubkey::new_unique()), + owner: SerializablePubkey::from(Pubkey::new_unique()), + amount: UnsignedInteger(amount), + delegate: None, + state: AccountState::initialized, + tlv: None, + }; + + let mut bytes = Vec::new(); + borsh::BorshSerialize::serialize(&token_data, &mut bytes).unwrap(); + let parsed = TokenData::parse(&bytes).unwrap(); + + assert_eq!(parsed.amount.0, amount); + } } #[test] - fn byte_layout_with_delegate_and_tlv() { - let tlv_raw = build_compressed_only_tlv(); - // tlv_raw: u32(4) + discriminant(1) + u64(8) + u64(8) + u8(1) = 22 - assert_eq!(tlv_raw.len(), 22); - let bytes = build_onchain_token_data_bytes(true, Some(&tlv_raw)); - // 107 (with delegate, no tlv excl the None byte) - 1 (remove None byte) - // + 1 (Some tag) + 22 (tlv_raw) = 129 - assert_eq!(bytes.len(), 129); + fn various_tlv_sizes() { + for tlv_size in [0, 1, 10, 100, 1000] { + let tlv_data = vec![0x42u8; tlv_size]; + let token_data = TokenData { + mint: SerializablePubkey::from(Pubkey::new_unique()), + owner: SerializablePubkey::from(Pubkey::new_unique()), + amount: UnsignedInteger(100), + delegate: None, + state: AccountState::initialized, + tlv: if tlv_size == 0 { + None + } else { + Some(Base64String(tlv_data.clone())) + }, + }; + + let mut bytes = Vec::new(); + borsh::BorshSerialize::serialize(&token_data, &mut bytes).unwrap(); + let parsed = TokenData::parse(&bytes).unwrap(); + + assert_eq!(parsed, token_data); + } } } diff --git a/src/dao/generated/accounts.rs b/src/dao/generated/accounts.rs index c968a218..e53aa511 100644 --- a/src/dao/generated/accounts.rs +++ b/src/dao/generated/accounts.rs @@ -19,8 +19,6 @@ pub struct Model { pub prev_spent: Option, #[sea_orm(column_type = "Decimal(Some((23, 0)))")] pub lamports: Decimal, - #[sea_orm(column_type = "Decimal(Some((23, 0)))", nullable)] - pub discriminator: Option, pub tree_type: Option, pub nullified_in_tree: bool, pub nullifier_queue_index: Option, @@ -28,14 +26,25 @@ pub struct Model { pub queue: Option>, pub nullifier: Option>, pub tx_hash: Option>, + pub onchain_pubkey: Option>, + #[sea_orm(column_type = "Decimal(Some((23, 0)))", nullable)] + pub discriminator: Option, } #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] pub enum Relation { - #[sea_orm(has_many = "super::token_accounts::Entity")] + #[sea_orm(has_many = "super::account_transactions::Entity")] + AccountTransactions, + #[sea_orm(has_one = "super::token_accounts::Entity")] TokenAccounts, } +impl Related for Entity { + fn to() -> RelationDef { + Relation::AccountTransactions.def() + } +} + impl Related for Entity { fn to() -> RelationDef { Relation::TokenAccounts.def() diff --git a/src/dao/generated/queue_hash_chains.rs b/src/dao/generated/queue_hash_chains.rs index c892083d..e0f5ff4f 100644 --- a/src/dao/generated/queue_hash_chains.rs +++ b/src/dao/generated/queue_hash_chains.rs @@ -1,3 +1,5 @@ +//! `SeaORM` Entity. Generated by sea-orm-codegen 0.10.6 + use sea_orm::entity::prelude::*; #[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)] diff --git a/src/dao/generated/token_accounts.rs b/src/dao/generated/token_accounts.rs index e9604cba..99dc815a 100644 --- a/src/dao/generated/token_accounts.rs +++ b/src/dao/generated/token_accounts.rs @@ -16,6 +16,7 @@ pub struct Model { #[sea_orm(column_type = "Decimal(Some((23, 0)))")] pub amount: Decimal, pub tlv: Option>, + pub ata_owner: Option>, } #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] diff --git a/src/dao/generated/transactions.rs b/src/dao/generated/transactions.rs index 977fc8de..b2d9f46a 100644 --- a/src/dao/generated/transactions.rs +++ b/src/dao/generated/transactions.rs @@ -15,6 +15,8 @@ pub struct Model { #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] pub enum Relation { + #[sea_orm(has_many = "super::account_transactions::Entity")] + AccountTransactions, #[sea_orm( belongs_to = "super::blocks::Entity", from = "Column::Slot", @@ -25,6 +27,12 @@ pub enum Relation { Blocks, } +impl Related for Entity { + fn to() -> RelationDef { + Relation::AccountTransactions.def() + } +} + impl Related for Entity { fn to() -> RelationDef { Relation::Blocks.def() diff --git a/src/dao/helpers.rs b/src/dao/helpers.rs index b9d26f82..837af562 100644 --- a/src/dao/helpers.rs +++ b/src/dao/helpers.rs @@ -4,7 +4,10 @@ use crate::dao::generated::{accounts, state_trees}; use sea_orm::{ColumnTrait, DatabaseConnection, EntityTrait, QueryFilter}; use std::collections::HashMap; -/// Finds accounts by multiple hashes, optionally filtering by spent status +/// Maximum number of parameters allowed in a single PostgreSQL query. +const MAX_SQL_PARAMS: usize = 30_000; + +/// Finds accounts by multiple hashes, optionally filtering by spent status. pub async fn find_accounts_by_hashes( conn: &DatabaseConnection, hashes: &[Hash], @@ -12,18 +15,24 @@ pub async fn find_accounts_by_hashes( ) -> Result, accounts::Model>, sea_orm::DbErr> { let raw_hashes: Vec> = hashes.iter().map(|h| h.to_vec()).collect(); - let mut query = accounts::Entity::find().filter(accounts::Column::Hash.is_in(raw_hashes)); + let mut result = HashMap::new(); - if let Some(spent) = spent_filter { - query = query.filter(accounts::Column::Spent.eq(spent)); - } + for chunk in raw_hashes.chunks(MAX_SQL_PARAMS) { + let mut query = + accounts::Entity::find().filter(accounts::Column::Hash.is_in(chunk.to_vec())); + + if let Some(spent) = spent_filter { + query = query.filter(accounts::Column::Spent.eq(spent)); + } + + let accounts = query.all(conn).await?; - let accounts = query.all(conn).await?; + for account in accounts { + result.insert(account.hash.clone(), account); + } + } - Ok(accounts - .into_iter() - .map(|account| (account.hash.clone(), account)) - .collect()) + Ok(result) } /// Finds accounts by multiple addresses, optionally filtering by spent status @@ -34,18 +43,24 @@ pub async fn find_accounts_by_addresses( ) -> Result, accounts::Model>, sea_orm::DbErr> { let raw_addresses: Vec> = addresses.iter().map(|addr| addr.to_bytes_vec()).collect(); - let mut query = accounts::Entity::find().filter(accounts::Column::Address.is_in(raw_addresses)); + let mut result = HashMap::new(); - if let Some(spent) = spent_filter { - query = query.filter(accounts::Column::Spent.eq(spent)); - } + for chunk in raw_addresses.chunks(MAX_SQL_PARAMS) { + let mut query = + accounts::Entity::find().filter(accounts::Column::Address.is_in(chunk.to_vec())); + + if let Some(spent) = spent_filter { + query = query.filter(accounts::Column::Spent.eq(spent)); + } + + let accounts = query.all(conn).await?; - let accounts = query.all(conn).await?; + for account in accounts { + result.insert(account.address.clone().unwrap_or_default(), account); + } + } - Ok(accounts - .into_iter() - .map(|account| (account.address.clone().unwrap_or_default(), account)) - .collect()) + Ok(result) } /// Finds leaf nodes in state_trees by multiple hashes @@ -55,19 +70,24 @@ pub async fn find_leaf_nodes_by_hashes( ) -> Result, state_trees::Model>, sea_orm::DbErr> { let raw_hashes: Vec> = hashes.iter().map(|h| h.to_vec()).collect(); - let leaf_nodes = state_trees::Entity::find() - .filter( - state_trees::Column::Hash - .is_in(raw_hashes) - .and(state_trees::Column::Level.eq(0)), - ) - .all(conn) - .await?; + let mut result = HashMap::new(); + + for chunk in raw_hashes.chunks(MAX_SQL_PARAMS) { + let leaf_nodes = state_trees::Entity::find() + .filter( + state_trees::Column::Hash + .is_in(chunk.to_vec()) + .and(state_trees::Column::Level.eq(0)), + ) + .all(conn) + .await?; + + for node in leaf_nodes { + result.insert(node.hash.clone(), node); + } + } - Ok(leaf_nodes - .into_iter() - .map(|node| (node.hash.clone(), node)) - .collect()) + Ok(result) } /// Finds a single account by hash diff --git a/src/ingester/fetchers/mod.rs b/src/ingester/fetchers/mod.rs index cc3235da..54854719 100644 --- a/src/ingester/fetchers/mod.rs +++ b/src/ingester/fetchers/mod.rs @@ -45,21 +45,15 @@ impl BlockStreamConfig { stream! { if let Some(grpc_stream) = grpc_stream { pin_mut!(grpc_stream); - loop { - match grpc_stream.next().await { - Some(blocks) => yield blocks, - None => break, - } + while let Some(blocks) = grpc_stream.next().await { + yield blocks; } } if let Some(poller_stream) = poller_stream { pin_mut!(poller_stream); - loop { - match poller_stream.next().await { - Some(blocks) => yield blocks, - None => break, - } + while let Some(blocks) = poller_stream.next().await { + yield blocks; } } } diff --git a/src/ingester/fetchers/poller.rs b/src/ingester/fetchers/poller.rs index 171eb67c..084083f1 100644 --- a/src/ingester/fetchers/poller.rs +++ b/src/ingester/fetchers/poller.rs @@ -77,11 +77,7 @@ fn pop_cached_blocks_to_index( mut last_indexed_slot: u64, ) -> (Vec, u64) { let mut blocks = Vec::new(); - loop { - let min_slot = match block_cache.keys().min() { - Some(&slot) => slot, - None => break, - }; + while let Some(&min_slot) = block_cache.keys().min() { let block: &BlockInfo = block_cache.get(&min_slot).unwrap(); if block.metadata.parent_slot == last_indexed_slot { last_indexed_slot = block.metadata.slot; diff --git a/src/ingester/indexer/mod.rs b/src/ingester/indexer/mod.rs index b414f6df..5f5c49af 100644 --- a/src/ingester/indexer/mod.rs +++ b/src/ingester/indexer/mod.rs @@ -56,11 +56,7 @@ pub async fn index_block_stream( pin_mut!(block_stream); let current_slot = end_slot.unwrap_or(fetch_current_slot_with_infinite_retry(&rpc_client).await); - let number_of_blocks_to_backfill = if current_slot > last_indexed_slot_at_start { - current_slot - last_indexed_slot_at_start - } else { - 0 - }; + let number_of_blocks_to_backfill = current_slot.saturating_sub(last_indexed_slot_at_start); info!( "Backfilling historical blocks. Current number of blocks to backfill: {}", number_of_blocks_to_backfill @@ -76,7 +72,7 @@ pub async fn index_block_stream( for slot in (last_indexed_slot + 1)..(last_slot_in_block + 1) { let blocks_indexed = slot - last_indexed_slot_at_start; if blocks_indexed < number_of_blocks_to_backfill { - if blocks_indexed % PRE_BACKFILL_FREQUENCY == 0 { + if blocks_indexed.is_multiple_of(PRE_BACKFILL_FREQUENCY) { info!( "Backfilled {} / {} blocks", blocks_indexed, number_of_blocks_to_backfill diff --git a/src/ingester/mod.rs b/src/ingester/mod.rs index 4d137e69..651ff062 100644 --- a/src/ingester/mod.rs +++ b/src/ingester/mod.rs @@ -28,6 +28,7 @@ pub mod fetchers; pub mod indexer; pub mod parser; pub mod persist; +pub mod startup_cleanup; pub mod typedefs; async fn derive_block_state_update( diff --git a/src/ingester/parser/indexer_events.rs b/src/ingester/parser/indexer_events.rs index 6a92a3f6..0cb917b7 100644 --- a/src/ingester/parser/indexer_events.rs +++ b/src/ingester/parser/indexer_events.rs @@ -2,7 +2,7 @@ /// to avoid having to import all of Light's dependencies. use borsh::{BorshDeserialize, BorshSerialize}; use light_compressed_account::Pubkey; -use light_event::event::{BatchNullifyContext, NewAddress}; +use light_event::event::{AssociatedTokenAccountOwnerInfo, BatchNullifyContext, NewAddress}; #[derive(Debug, PartialEq, Eq, Default, Clone, BorshSerialize, BorshDeserialize)] pub struct OutputCompressedAccountWithPackedContext { @@ -20,7 +20,7 @@ pub struct MerkleTreeSequenceNumberV2 { #[derive(Debug, Clone, BorshSerialize, BorshDeserialize, Default, Eq, PartialEq)] pub struct MerkleTreeSequenceNumberV1 { - pub pubkey: Pubkey, + pub tree_pubkey: Pubkey, pub seq: u64, } @@ -33,7 +33,7 @@ pub enum MerkleTreeSequenceNumber { impl MerkleTreeSequenceNumber { pub fn tree_pubkey(&self) -> Pubkey { match self { - MerkleTreeSequenceNumber::V1(x) => x.pubkey, + MerkleTreeSequenceNumber::V1(x) => x.tree_pubkey, MerkleTreeSequenceNumber::V2(x) => x.tree_pubkey, } } @@ -45,6 +45,7 @@ impl MerkleTreeSequenceNumber { } } +/// Current version of PublicTransactionEvent (with ata_owners) #[derive(Debug, Clone, BorshSerialize, BorshDeserialize, Default, PartialEq, Eq)] pub struct PublicTransactionEvent { pub input_compressed_account_hashes: Vec<[u8; 32]>, @@ -54,10 +55,64 @@ pub struct PublicTransactionEvent { pub sequence_numbers: Vec, pub relay_fee: Option, pub is_compress: bool, - pub compression_lamports: Option, + pub compress_or_decompress_lamports: Option, pub pubkey_array: Vec, // TODO: remove(data can just be written into a compressed account) pub message: Option>, + /// ATA owner info for compressed ATAs (output_index -> wallet_owner_pubkey). + /// Only populated for compress_and_close operations where is_ata=true. + pub ata_owners: Vec, +} + +/// Legacy version of PublicTransactionEvent (without ata_owners, with compression_lamports) +#[derive(Debug, Clone, BorshSerialize, BorshDeserialize, Default, PartialEq, Eq)] +pub struct PublicTransactionEventLegacy { + pub input_compressed_account_hashes: Vec<[u8; 32]>, + pub output_compressed_account_hashes: Vec<[u8; 32]>, + pub output_compressed_accounts: Vec, + pub output_leaf_indices: Vec, + pub sequence_numbers: Vec, + pub relay_fee: Option, + pub is_compress: bool, + pub compression_lamports: Option, + pub pubkey_array: Vec, + pub message: Option>, +} + +impl From for PublicTransactionEvent { + fn from(legacy: PublicTransactionEventLegacy) -> Self { + Self { + input_compressed_account_hashes: legacy.input_compressed_account_hashes, + output_compressed_account_hashes: legacy.output_compressed_account_hashes, + output_compressed_accounts: legacy.output_compressed_accounts, + output_leaf_indices: legacy.output_leaf_indices, + sequence_numbers: legacy.sequence_numbers, + relay_fee: legacy.relay_fee, + is_compress: legacy.is_compress, + compress_or_decompress_lamports: legacy.compression_lamports, + pubkey_array: legacy.pubkey_array, + message: legacy.message, + ata_owners: Vec::new(), + } + } +} + +impl PublicTransactionEvent { + /// Deserialize from bytes, trying the current format first, then falling back to legacy. + pub fn deserialize_versioned(data: &mut &[u8]) -> Result { + let data_copy = *data; + + if let Ok(event) = Self::deserialize(data) { + if data.is_empty() { + return Ok(event); + } + } + + // Fall back to legacy version + *data = data_copy; + let legacy = PublicTransactionEventLegacy::deserialize(data)?; + Ok(legacy.into()) + } } #[derive(Debug, Clone)] diff --git a/src/ingester/parser/mod.rs b/src/ingester/parser/mod.rs index 1697527b..15a0840d 100644 --- a/src/ingester/parser/mod.rs +++ b/src/ingester/parser/mod.rs @@ -89,52 +89,49 @@ where state_updates.push(state_update); } else { for (index, instruction) in ordered_instructions.iter().enumerate() { - if ordered_instructions.len() - index > 1 { - if get_compression_program_id() == instruction.program_id { - // Look for a NOOP_PROGRAM_ID instruction after one or two SYSTEM_PROGRAM instructions - // We handle up to two system program instructions in the case where we also have to pay a tree rollover fee - let mut noop_instruction_index = None; - let mut system_program_count = 0; - let mut all_intermediate_are_system = true; - - // Search for the NOOP instruction, ensuring we find at least one SYSTEM_PROGRAM but no more than two - for i in (index + 1)..ordered_instructions.len() { - let current_instruction = &ordered_instructions[i]; - - if current_instruction.program_id == NOOP_PROGRAM_ID { - noop_instruction_index = Some(i); - break; - } else if current_instruction.program_id == SYSTEM_PROGRAM { - system_program_count += 1; - if system_program_count > 2 { - all_intermediate_are_system = false; - break; - } - } else { + if ordered_instructions.len() - index > 1 + && get_compression_program_id() == instruction.program_id + { + // Look for a NOOP_PROGRAM_ID instruction after one or two SYSTEM_PROGRAM instructions + // We handle up to two system program instructions in the case where we also have to pay a tree rollover fee + let mut noop_instruction_index = None; + let mut system_program_count = 0; + let mut all_intermediate_are_system = true; + + // Search for the NOOP instruction, ensuring we find at least one SYSTEM_PROGRAM but no more than two + for (i, current_instruction) in + ordered_instructions.iter().enumerate().skip(index + 1) + { + if current_instruction.program_id == NOOP_PROGRAM_ID { + noop_instruction_index = Some(i); + break; + } else if current_instruction.program_id == SYSTEM_PROGRAM { + system_program_count += 1; + if system_program_count > 2 { all_intermediate_are_system = false; break; } + } else { + all_intermediate_are_system = false; + break; } + } - // If we found a NOOP instruction, exactly one or two SYSTEM_PROGRAM instructions, and all intermediates were valid - if let Some(noop_index) = noop_instruction_index { - if system_program_count >= 1 - && system_program_count <= 2 - && all_intermediate_are_system + // If we found a NOOP instruction, exactly one or two SYSTEM_PROGRAM instructions, and all intermediates were valid + if let Some(noop_index) = noop_instruction_index { + if (1..=2).contains(&system_program_count) && all_intermediate_are_system { + if let Some(state_update) = parse_public_transaction_event_v1( + conn, + tx, + slot, + instruction, + &ordered_instructions[noop_index], + rpc_client, + ) + .await? { - if let Some(state_update) = parse_public_transaction_event_v1( - conn, - tx, - slot, - instruction, - &ordered_instructions[noop_index], - rpc_client, - ) - .await? - { - is_compression_transaction = true; - state_updates.push(state_update); - } + is_compression_transaction = true; + state_updates.push(state_update); } } } diff --git a/src/ingester/parser/state_update.rs b/src/ingester/parser/state_update.rs index 1d25014d..20512542 100644 --- a/src/ingester/parser/state_update.rs +++ b/src/ingester/parser/state_update.rs @@ -103,6 +103,9 @@ pub struct StateUpdate { // v2 input accounts that are inserted into the input queue pub batch_nullify_context: Vec, pub batch_new_addresses: Vec, + /// ATA owner info for compressed ATAs (account_hash -> wallet_owner_pubkey). + /// Used to populate the ata_owner column in token_accounts table. + pub ata_owners: HashMap, } /// Result of filtering a StateUpdate by known trees @@ -324,6 +327,7 @@ impl StateUpdate { batch_merkle_tree_events, batch_nullify_context: self.batch_nullify_context, batch_new_addresses, + ata_owners: self.ata_owners, }; Ok(FilteredStateUpdate { @@ -371,6 +375,7 @@ impl StateUpdate { merged .batch_nullify_context .extend(update.batch_nullify_context); + merged.ata_owners.extend(update.ata_owners); } merged diff --git a/src/ingester/parser/tx_event_parser.rs b/src/ingester/parser/tx_event_parser.rs index 7e2eb124..f61b7766 100644 --- a/src/ingester/parser/tx_event_parser.rs +++ b/src/ingester/parser/tx_event_parser.rs @@ -5,7 +5,6 @@ use crate::ingester::parser::state_update::{AccountTransaction, StateUpdate}; use crate::ingester::parser::tree_info::{discover_tree, TreeInfo}; use crate::ingester::parser::{get_compression_program_id, NOOP_PROGRAM_ID}; use crate::ingester::typedefs::block_info::{Instruction, TransactionInfo}; -use borsh::BorshDeserialize; use light_compressed_account::TreeType; use log::info; use solana_client::nonblocking::rpc_client::RpcClient; @@ -32,20 +31,19 @@ where slot, tx.signature ); - let public_transaction_event = PublicTransactionEvent::deserialize( - &mut noop_instruction.data.as_slice(), - ) - .map_err(|e| { - IngesterError::ParserError(format!( - "Failed to deserialize PublicTransactionEvent: {}", - e - )) - })?; + let public_transaction_event = + PublicTransactionEvent::deserialize_versioned(&mut noop_instruction.data.as_slice()) + .map_err(|e| { + IngesterError::ParserError(format!( + "Failed to deserialize PublicTransactionEvent: {}", + e + )) + })?; create_state_update_v1( conn, tx.signature, slot, - public_transaction_event.into(), + public_transaction_event, rpc_client, ) .await @@ -69,18 +67,31 @@ where let mut tree_to_seq_number = transaction_event .sequence_numbers .iter() - .map(|seq| (seq.pubkey, seq.seq)) + .map(|seq| (seq.tree_pubkey, seq.seq)) .collect::>(); - for hash in transaction_event.input_compressed_account_hashes { - state_update.in_accounts.insert(hash.into()); + for hash in transaction_event.input_compressed_account_hashes.iter() { + state_update.in_accounts.insert((*hash).into()); } - for ((out_account, hash), leaf_index) in transaction_event + // Build index from output_index to ATA owner + let ata_owner_by_index: HashMap = transaction_event + .ata_owners + .iter() + .map(|info| { + ( + info.output_index, + solana_pubkey::Pubkey::new_from_array(info.wallet_owner.to_bytes()), + ) + }) + .collect(); + + for (output_index, ((out_account, hash), leaf_index)) in transaction_event .output_compressed_accounts - .into_iter() - .zip(transaction_event.output_compressed_account_hashes) + .iter() + .zip(transaction_event.output_compressed_account_hashes.iter()) .zip(transaction_event.output_leaf_indices.iter()) + .enumerate() { let tree = transaction_event.pubkey_array[out_account.merkle_tree_index as usize]; let tree_solana = solana_pubkey::Pubkey::new_from_array(tree.to_bytes()); @@ -128,7 +139,7 @@ where let enriched_account = AccountWithContext::new( out_account.compressed_account.clone(), - &hash, + hash, tree_pubkey, queue_pubkey, *leaf_index, @@ -141,6 +152,11 @@ where tree_and_queue.tree_type, ); + // If this output has an ATA owner, map the account hash to the owner + if let Some(ata_owner) = ata_owner_by_index.get(&(output_index as u8)) { + state_update.ata_owners.insert((*hash).into(), *ata_owner); + } + state_update.out_accounts.push(enriched_account); } diff --git a/src/ingester/parser/tx_event_parser_v2.rs b/src/ingester/parser/tx_event_parser_v2.rs index 6c536071..bf37e784 100644 --- a/src/ingester/parser/tx_event_parser_v2.rs +++ b/src/ingester/parser/tx_event_parser_v2.rs @@ -26,8 +26,7 @@ pub fn parse_public_transaction_event_v2( instructions: &[Vec], accounts: Vec>, ) -> Option> { - let light_program_ids: Vec = - program_ids.iter().map(|p| to_light_pubkey(p)).collect(); + let light_program_ids: Vec = program_ids.iter().map(to_light_pubkey).collect(); let light_accounts: Vec> = accounts .into_iter() .map(|acc_vec| { @@ -76,17 +75,18 @@ pub fn parse_public_transaction_event_v2( .sequence_numbers .iter() .map(|x| MerkleTreeSequenceNumberV1 { - pubkey: x.tree_pubkey, + tree_pubkey: x.tree_pubkey, seq: x.seq, }) .collect(), relay_fee: public_transaction_event.event.relay_fee, is_compress: public_transaction_event.event.is_compress, - compression_lamports: public_transaction_event + compress_or_decompress_lamports: public_transaction_event .event .compress_or_decompress_lamports, pubkey_array: public_transaction_event.event.pubkey_array, message: public_transaction_event.event.message, + ata_owners: public_transaction_event.event.ata_owners, }; let batch_public_transaction_event = BatchPublicTransactionEvent { diff --git a/src/ingester/persist/mod.rs b/src/ingester/persist/mod.rs index 2d64cd1f..d6f89651 100644 --- a/src/ingester/persist/mod.rs +++ b/src/ingester/persist/mod.rs @@ -2,7 +2,13 @@ use super::{error, parser::state_update::AccountTransaction}; use crate::ingester::parser::state_update::{AddressQueueUpdate, StateUpdate}; use crate::{ api::method::utils::PAGE_LIMIT, - common::typedefs::{hash::Hash, token_data::TokenData}, + common::{ + token_layout::{ + ACCOUNT_TYPE_MINT, COMPRESSED_MINT_PDA_END, COMPRESSED_MINT_PDA_OFFSET, + TOKEN_ACCOUNT_TYPE_OFFSET, + }, + typedefs::{hash::Hash, token_data::TokenData}, + }, dao::generated::{ account_transactions, accounts, state_tree_histories, state_trees, token_accounts, transactions, @@ -50,7 +56,11 @@ pub use self::leaf_node_proof::{ get_multiple_compressed_leaf_proofs_from_full_leaf_info, }; -pub const COMPRESSED_TOKEN_PROGRAM: Pubkey = pubkey!("cTokenmWW8bLPjZEBAUgYy3zKxQZW6VKi7bqNFEVv3m"); +pub const LIGHT_TOKEN_PROGRAM_ID: Pubkey = pubkey!("cTokenmWW8bLPjZEBAUgYy3zKxQZW6VKi7bqNFEVv3m"); + +/// Discriminator for decompressed PDA accounts: [255, 255, 255, 255, 255, 255, 255, 0] +/// This matches DECOMPRESSED_PDA_DISCRIMINATOR from light_compressible. +pub const DECOMPRESSED_ACCOUNT_DISCRIMINATOR: u64 = 0x00FFFFFFFFFFFFFF; // To avoid exceeding the 64k total parameter limit pub const MAX_SQL_INSERTS: usize = 500; @@ -82,7 +92,7 @@ pub async fn persist_state_update( batch_merkle_tree_events, batch_nullify_context, batch_new_addresses, - .. + ata_owners, } = state_update; let input_accounts_len = in_accounts.len(); @@ -104,7 +114,7 @@ pub async fn persist_state_update( debug!("Persisting output accounts..."); for chunk in out_accounts.chunks(MAX_SQL_INSERTS) { - append_output_accounts(txn, chunk).await?; + append_output_accounts(txn, chunk, &ata_owners).await?; } debug!("Persisting spent accounts..."); @@ -173,7 +183,7 @@ pub async fn persist_state_update( .map_err(|e| IngesterError::ParserError(format!("Invalid tree pubkey: {}", e)))?; nodes_by_tree .entry(tree_pubkey) - .or_insert_with(Vec::new) + .or_default() .push((leaf_node, signature)); } @@ -277,6 +287,8 @@ async fn persist_state_tree_history( pub struct EnrichedTokenAccount { pub token_data: TokenData, pub hash: Hash, + /// The wallet owner pubkey that the ATA is derived from (for compressed ATAs) + pub ata_owner: Option, } #[derive(Debug)] @@ -423,14 +435,18 @@ async fn insert_addresses_into_queues( async fn append_output_accounts( txn: &DatabaseTransaction, out_accounts: &[AccountWithContext], + ata_owners: &HashMap, ) -> Result<(), IngesterError> { let mut account_models = Vec::new(); let mut token_accounts = Vec::new(); for account in out_accounts { + let onchain_pubkey = extract_onchain_pubkey(account); + account_models.push(accounts::ActiveModel { hash: Set(account.account.hash.to_vec()), address: Set(account.account.address.map(|x| x.to_bytes_vec())), + onchain_pubkey: Set(onchain_pubkey), discriminator: Set(account .account .data @@ -456,9 +472,11 @@ async fn append_output_accounts( }); if let Some(token_data) = account.account.parse_token_data()? { + let ata_owner = ata_owners.get(&account.account.hash).copied(); token_accounts.push(EnrichedTokenAccount { token_data, hash: account.account.hash.clone(), + ata_owner, }); } } @@ -488,6 +506,27 @@ async fn append_output_accounts( Ok(()) } +fn extract_onchain_pubkey(account: &AccountWithContext) -> Option> { + let data = account.account.data.as_ref()?; + + // Decompressed PDA accounts store the on-chain pubkey in the first 32 bytes. + if data.discriminator.0 == DECOMPRESSED_ACCOUNT_DISCRIMINATOR && data.data.0.len() >= 32 { + return Some(data.data.0[..32].to_vec()); + } + + // Compressed mints store the mint PDA at [84..116], and have account_type marker + // ACCOUNT_TYPE_MINT at byte TOKEN_ACCOUNT_TYPE_OFFSET. + if account.account.owner.0 == LIGHT_TOKEN_PROGRAM_ID + && !data.is_c_token_discriminator() + && data.data.0.len() > TOKEN_ACCOUNT_TYPE_OFFSET + && data.data.0[TOKEN_ACCOUNT_TYPE_OFFSET] == ACCOUNT_TYPE_MINT + { + return Some(data.data.0[COMPRESSED_MINT_PDA_OFFSET..COMPRESSED_MINT_PDA_END].to_vec()); + } + + None +} + pub async fn persist_token_accounts( txn: &DatabaseTransaction, token_accounts: Vec, @@ -495,7 +534,11 @@ pub async fn persist_token_accounts( let token_models = token_accounts .into_iter() .map( - |EnrichedTokenAccount { token_data, hash }| token_accounts::ActiveModel { + |EnrichedTokenAccount { + token_data, + hash, + ata_owner, + }| token_accounts::ActiveModel { hash: Set(hash.into()), mint: Set(token_data.mint.to_bytes_vec()), owner: Set(token_data.owner.to_bytes_vec()), @@ -505,6 +548,7 @@ pub async fn persist_token_accounts( spent: Set(false), prev_spent: Set(None), tlv: Set(token_data.tlv.map(|t| t.0)), + ata_owner: Set(ata_owner.map(|o| o.to_bytes().to_vec())), }, ) .collect::>(); @@ -631,3 +675,90 @@ async fn persist_account_transactions( Ok(()) } + +#[cfg(test)] +mod tests { + use super::*; + use crate::common::typedefs::account::{ + Account, AccountContext, AccountData, C_TOKEN_DISCRIMINATOR_V2, + }; + use crate::common::typedefs::bs64_string::Base64String; + use crate::common::typedefs::serializable_pubkey::SerializablePubkey; + use crate::common::typedefs::unsigned_integer::UnsignedInteger; + + fn sample_account_with_context( + owner: Pubkey, + discriminator: u64, + data: Vec, + ) -> AccountWithContext { + AccountWithContext { + account: Account { + hash: Hash::default(), + address: None, + data: Some(AccountData { + discriminator: UnsignedInteger(discriminator), + data: Base64String(data), + data_hash: Hash::default(), + }), + owner: SerializablePubkey::from(owner), + lamports: UnsignedInteger(0), + tree: SerializablePubkey::default(), + leaf_index: UnsignedInteger(0), + seq: None, + slot_created: UnsignedInteger(0), + }, + context: AccountContext { + queue: SerializablePubkey::default(), + in_output_queue: true, + spent: false, + nullified_in_tree: false, + nullifier_queue_index: None, + nullifier: None, + tx_hash: None, + tree_type: 0, + }, + } + } + + #[test] + fn test_extract_onchain_pubkey_for_decompressed_pda() { + let expected = (0u8..32).collect::>(); + let mut data = expected.clone(); + data.extend_from_slice(&[9u8; 8]); + + let account = sample_account_with_context( + Pubkey::new_unique(), + DECOMPRESSED_ACCOUNT_DISCRIMINATOR, + data, + ); + + assert_eq!(extract_onchain_pubkey(&account), Some(expected)); + } + + #[test] + fn test_extract_onchain_pubkey_for_compressed_mint() { + let mut data = vec![0u8; TOKEN_ACCOUNT_TYPE_OFFSET + 1]; + let expected = [7u8; 32]; + data[COMPRESSED_MINT_PDA_OFFSET..COMPRESSED_MINT_PDA_END].copy_from_slice(&expected); + data[TOKEN_ACCOUNT_TYPE_OFFSET] = ACCOUNT_TYPE_MINT; + + let account = sample_account_with_context(LIGHT_TOKEN_PROGRAM_ID, 0, data); + + assert_eq!(extract_onchain_pubkey(&account), Some(expected.to_vec())); + } + + #[test] + fn test_extract_onchain_pubkey_skips_ctoken_discriminator() { + let mut data = vec![0u8; TOKEN_ACCOUNT_TYPE_OFFSET + 1]; + data[COMPRESSED_MINT_PDA_OFFSET..COMPRESSED_MINT_PDA_END].copy_from_slice(&[8u8; 32]); + data[TOKEN_ACCOUNT_TYPE_OFFSET] = ACCOUNT_TYPE_MINT; + + let account = sample_account_with_context( + LIGHT_TOKEN_PROGRAM_ID, + u64::from_le_bytes(C_TOKEN_DISCRIMINATOR_V2), + data, + ); + + assert_eq!(extract_onchain_pubkey(&account), None); + } +} diff --git a/src/ingester/persist/persisted_batch_event/address.rs b/src/ingester/persist/persisted_batch_event/address.rs index 2acf92f4..0723077f 100644 --- a/src/ingester/persist/persisted_batch_event/address.rs +++ b/src/ingester/persist/persisted_batch_event/address.rs @@ -131,7 +131,7 @@ pub async fn persist_batch_address_append_event( Ok(()) } -async fn cleanup_stale_address_queue_entries( +pub async fn cleanup_stale_address_queue_entries( txn: &DatabaseTransaction, batch_address_append_event: &BatchEvent, queue_end: i64, diff --git a/src/ingester/persist/persisted_batch_event/append.rs b/src/ingester/persist/persisted_batch_event/append.rs index 010e7d1c..ceda96e3 100644 --- a/src/ingester/persist/persisted_batch_event/append.rs +++ b/src/ingester/persist/persisted_batch_event/append.rs @@ -24,7 +24,7 @@ pub async fn persist_batch_append_event( .saturating_sub(batch_append_event.old_next_index) as usize; let current_next_index = - get_tree_next_index(txn, &batch_append_event.merkle_tree_pubkey.to_vec()).await?; + get_tree_next_index(txn, batch_append_event.merkle_tree_pubkey.as_ref()).await?; // If old_next_index doesn't match current state, check if already processed if batch_append_event.old_next_index != current_next_index { @@ -53,7 +53,7 @@ pub async fn persist_batch_append_event( let accounts = fetch_accounts_to_append( txn, - &batch_append_event.merkle_tree_pubkey.to_vec(), + batch_append_event.merkle_tree_pubkey.as_ref(), batch_append_event.old_next_index as i64, batch_append_event.new_next_index as i64, ) diff --git a/src/ingester/persist/persisted_batch_event/mod.rs b/src/ingester/persist/persisted_batch_event/mod.rs index 6e635cb8..f95e5631 100644 --- a/src/ingester/persist/persisted_batch_event/mod.rs +++ b/src/ingester/persist/persisted_batch_event/mod.rs @@ -11,7 +11,7 @@ use log::debug; use sea_orm::DatabaseTransaction; use solana_pubkey::Pubkey; -use self::address::persist_batch_address_append_event; +use self::address::{cleanup_stale_address_queue_entries, persist_batch_address_append_event}; use self::append::persist_batch_append_event; use self::helpers::{deduplicate_events, persist_leaf_nodes_chunked, ZKP_BATCH_SIZE}; use self::nullify::persist_batch_nullify_event; @@ -19,7 +19,7 @@ use self::sequence::should_process_event; /// We need to find the events of the same tree: /// - order them by sequence number and execute them in order -/// HashMap> +/// HashMap> /// - execute a single function call to persist all changed nodes pub async fn persist_batch_events( txn: &DatabaseTransaction, @@ -74,6 +74,10 @@ pub async fn persist_batch_events( "Skipping already processed event with sequence {}", _event_seq ); + if let MerkleTreeEvent::BatchAddressAppend(batch_event) = event { + let queue_end = (batch_event.new_next_index as i64) - 1; + cleanup_stale_address_queue_entries(txn, batch_event, queue_end).await?; + } continue; } diff --git a/src/ingester/persist/persisted_batch_event/nullify.rs b/src/ingester/persist/persisted_batch_event/nullify.rs index 215fa631..8df56eef 100644 --- a/src/ingester/persist/persisted_batch_event/nullify.rs +++ b/src/ingester/persist/persisted_batch_event/nullify.rs @@ -17,7 +17,7 @@ use super::sequence::update_root_sequence; /// Persists a batch nullify event. /// 1. Create leaf nodes with nullifier as leaf. /// 2. Mark elements as nullified in tree -/// and remove them from the database nullifier queue. +/// and remove them from the database nullifier queue. pub async fn persist_batch_nullify_event( txn: &DatabaseTransaction, batch_nullify_event: &BatchEvent, @@ -38,7 +38,7 @@ pub async fn persist_batch_nullify_event( // Count accounts that are spent but not yet nullified in tree let queue_count = count_accounts_ready_to_nullify( txn, - &batch_nullify_event.merkle_tree_pubkey.to_vec(), + batch_nullify_event.merkle_tree_pubkey.as_ref(), queue_start, queue_end, ) @@ -56,7 +56,7 @@ pub async fn persist_batch_nullify_event( // Check if this is a re-indexing scenario (accounts already nullified) let already_processed = check_nullify_already_processed( txn, - &batch_nullify_event.merkle_tree_pubkey.to_vec(), + batch_nullify_event.merkle_tree_pubkey.as_ref(), queue_start, queue_end, ) @@ -90,7 +90,7 @@ pub async fn persist_batch_nullify_event( // Partial accounts found - gather more info for error message let in_queue_count = count_nullify_accounts_in_output_queue( txn, - &batch_nullify_event.merkle_tree_pubkey.to_vec(), + batch_nullify_event.merkle_tree_pubkey.as_ref(), queue_start, queue_end, ) @@ -112,7 +112,7 @@ pub async fn persist_batch_nullify_event( let accounts = fetch_accounts_to_nullify( txn, - &batch_nullify_event.merkle_tree_pubkey.to_vec(), + batch_nullify_event.merkle_tree_pubkey.as_ref(), queue_start, queue_end, ) diff --git a/src/ingester/persist/persisted_indexed_merkle_tree.rs b/src/ingester/persist/persisted_indexed_merkle_tree.rs index c2874b09..da095047 100644 --- a/src/ingester/persist/persisted_indexed_merkle_tree.rs +++ b/src/ingester/persist/persisted_indexed_merkle_tree.rs @@ -235,13 +235,9 @@ pub async fn persist_indexed_tree_updates( .iter() .map(|x| { Ok(LeafNode { - tree: SerializablePubkey::try_from(x.tree).map_err(|e| { - IngesterError::DatabaseError(format!("Failed to serialize pubkey: {}", e)) - })?, + tree: SerializablePubkey::from(x.tree), leaf_index: x.leaf.index as u32, - hash: Hash::try_from(x.hash).map_err(|e| { - IngesterError::DatabaseError(format!("Failed to serialize hash: {}", e)) - })?, + hash: Hash::from(x.hash), seq: Option::from(x.seq as u32), }) }) diff --git a/src/ingester/persist/persisted_state_tree.rs b/src/ingester/persist/persisted_state_tree.rs index 6e65fb6f..d3e0ed2f 100644 --- a/src/ingester/persist/persisted_state_tree.rs +++ b/src/ingester/persist/persisted_state_tree.rs @@ -102,7 +102,8 @@ where for (tree, index) in leaf_nodes_locations.iter() { result.entry((tree.clone(), *index)).or_insert_with(|| { let tree_height_with_level = tree_height + 1; - let model = state_trees::Model { + + state_trees::Model { tree: tree.clone(), level: get_level_by_node_index(*index, tree_height_with_level), node_idx: *index, @@ -111,8 +112,7 @@ where .to_vec(), leaf_idx: None, seq: None, - }; - model + } }); } } diff --git a/src/ingester/persist/spend.rs b/src/ingester/persist/spend.rs index 3a377d69..5ae46b9c 100644 --- a/src/ingester/persist/spend.rs +++ b/src/ingester/persist/spend.rs @@ -10,7 +10,7 @@ use sea_orm::QueryFilter; use sea_orm::{ColumnTrait, ConnectionTrait, DatabaseTransaction, EntityTrait, QueryTrait}; /// 1. Mark the input accounts as spent. -/// (From both V1 and V2 (batched) trees) +/// (From both V1 and V2 (batched) trees) /// 2. Update account compressed sol balances. /// 3. Update compressed token account balances. pub async fn spend_input_accounts( diff --git a/src/ingester/startup_cleanup.rs b/src/ingester/startup_cleanup.rs new file mode 100644 index 00000000..ceabdede --- /dev/null +++ b/src/ingester/startup_cleanup.rs @@ -0,0 +1,138 @@ +use crate::dao::generated::{address_queues, indexed_trees, tree_metadata}; +use light_compressed_account::TreeType; +use log::{debug, info}; +use sea_orm::{ + ColumnTrait, DatabaseConnection, EntityTrait, PaginatorTrait, QueryFilter, QueryOrder, +}; + +/// Cleans up stale address queue entries on startup. +/// +/// For each AddressV2 tree: +/// 1. Get the current next_index from indexed_trees (MAX(leaf_index) + 1) +/// 2. Delete address_queue entries where queue_index < next_index - 1 +/// +/// This handles cases where photon was restarted or re-indexed and stale +/// queue entries remain from batches that were already processed. +pub async fn cleanup_stale_address_queues( + db: &DatabaseConnection, +) -> Result<(), Box> { + info!("Starting address queue cleanup..."); + + let address_trees = tree_metadata::Entity::find() + .filter(tree_metadata::Column::TreeType.eq(TreeType::AddressV2 as i32)) + .all(db) + .await?; + + if address_trees.is_empty() { + debug!("No AddressV2 trees found, skipping cleanup"); + return Ok(()); + } + + let mut total_deleted = 0u64; + + for tree in address_trees { + let tree_pubkey = &tree.tree_pubkey; + + // Get current next_index from indexed_trees (MAX(leaf_index) + 1) + let current_next_index = indexed_trees::Entity::find() + .filter(indexed_trees::Column::Tree.eq(tree_pubkey.clone())) + .order_by_desc(indexed_trees::Column::LeafIndex) + .one(db) + .await? + .map(|t| t.leaf_index + 1) + .unwrap_or(1); + + // Address queue indices are 0-based, tree indices are 1-based + // So queue entries with queue_index < current_next_index - 1 are stale + let queue_threshold = current_next_index - 1; + + if queue_threshold <= 0 { + debug!( + "Tree {}: next_index={}, no cleanup needed", + bs58::encode(tree_pubkey).into_string(), + current_next_index + ); + continue; + } + + let stale_count = address_queues::Entity::find() + .filter( + address_queues::Column::Tree + .eq(tree_pubkey.clone()) + .and(address_queues::Column::QueueIndex.lt(queue_threshold)), + ) + .count(db) + .await?; + + if stale_count == 0 { + debug!( + "Tree {}: next_index={}, no stale entries", + bs58::encode(tree_pubkey).into_string(), + current_next_index + ); + continue; + } + + let delete_result = address_queues::Entity::delete_many() + .filter( + address_queues::Column::Tree + .eq(tree_pubkey.clone()) + .and(address_queues::Column::QueueIndex.lt(queue_threshold)), + ) + .exec(db) + .await?; + + let deleted = delete_result.rows_affected; + total_deleted += deleted; + + info!( + "Tree {}: deleted {} stale queue entries (queue_index < {}, next_index={})", + bs58::encode(tree_pubkey).into_string(), + deleted, + queue_threshold, + current_next_index + ); + } + + if total_deleted > 0 { + info!( + "Address queue cleanup complete: deleted {} total stale entries", + total_deleted + ); + } else { + info!("Address queue cleanup complete: no stale entries found"); + } + + let duplicate_deleted = cleanup_duplicate_addresses(db).await?; + if duplicate_deleted > 0 { + info!( + "Duplicate address cleanup: deleted {} addresses already in indexed_trees", + duplicate_deleted + ); + } + + Ok(()) +} + +/// Cleans up address_queues entries where the address already exists in indexed_trees. +async fn cleanup_duplicate_addresses( + db: &DatabaseConnection, +) -> Result> { + use sea_orm::{ConnectionTrait, DatabaseBackend, Statement}; + + info!("Checking for duplicate addresses in queue..."); + + let backend = db.get_database_backend(); + let sql = match backend { + DatabaseBackend::Postgres | DatabaseBackend::Sqlite => { + "DELETE FROM address_queues WHERE address IN (SELECT value FROM indexed_trees)" + } + _ => return Err("Unsupported database backend".into()), + }; + + let result = db + .execute(Statement::from_string(backend, sql.to_string())) + .await?; + + Ok(result.rows_affected()) +} diff --git a/src/ingester/typedefs/block_info.rs b/src/ingester/typedefs/block_info.rs index c2faf6c5..4f5dc50f 100644 --- a/src/ingester/typedefs/block_info.rs +++ b/src/ingester/typedefs/block_info.rs @@ -209,12 +209,12 @@ pub fn parse_instruction_groups( .instructions() .iter() .map(|ix| { - let program_id = accounts[ix.program_id_index as usize].clone(); + let program_id = accounts[ix.program_id_index as usize]; let data = ix.data.clone(); let instruction_accounts: Vec = ix .accounts .iter() - .map(|account_index| accounts[*account_index as usize].clone()) + .map(|account_index| accounts[*account_index as usize]) .collect(); InstructionGroup { @@ -236,14 +236,14 @@ pub fn parse_instruction_groups( match ui_instruction { UiInstruction::Compiled(ui_compiled_instruction) => { let program_id = - accounts[ui_compiled_instruction.program_id_index as usize].clone(); + accounts[ui_compiled_instruction.program_id_index as usize]; let data = bs58::decode(&ui_compiled_instruction.data) .into_vec() .map_err(|e| IngesterError::ParserError(e.to_string()))?; let instruction_accounts: Vec = ui_compiled_instruction .accounts .iter() - .map(|account_index| accounts[*account_index as usize].clone()) + .map(|account_index| accounts[*account_index as usize]) .collect(); instruction_groups[index as usize] .inner_instructions diff --git a/src/main.rs b/src/main.rs index 2b028eef..3b5e031a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,7 +5,7 @@ use async_stream::stream; use clap::Parser; use futures::pin_mut; use jsonrpsee::server::ServerHandle; -use log::{error, info}; +use log::{error, info, warn}; use photon_indexer::api::{self, api::PhotonApi}; use photon_indexer::common::{ @@ -73,6 +73,10 @@ struct Args { #[arg(long, default_value = "http://127.0.0.1:3001")] prover_url: String, + /// API key for the Light Prover service + #[arg(long)] + prover_api_key: Option, + /// Snasphot directory #[arg(long, default_value = None)] snapshot_dir: Option, @@ -107,16 +111,25 @@ struct Args { /// If provided, metrics will be sent to the specified statsd server. #[arg(long, default_value = None)] metrics_endpoint: Option, + + /// Max concurrent HTTP connections for the JSON-RPC server (jsonrpsee). + /// Connections beyond this limit receive HTTP 429. + #[arg(long, default_value_t = 1024)] + max_http_connections: u32, } async fn start_api_server( db: Arc, rpc_client: Arc, prover_url: String, + prover_api_key: Option, api_port: u16, + max_http_connections: u32, ) -> ServerHandle { - let api = PhotonApi::new(db, rpc_client, prover_url); - api::rpc_server::run_server(api, api_port).await.unwrap() + let api = PhotonApi::new(db, rpc_client, prover_url, prover_api_key); + api::rpc_server::run_server(api, api_port, max_http_connections) + .await + .unwrap() } async fn setup_temporary_sqlite_database_pool(max_connections: u32) -> SqlitePool { @@ -219,6 +232,14 @@ async fn main() { info!("Running migrations..."); Migrator::up(db_conn.as_ref(), None).await.unwrap(); } + + if let Err(e) = + photon_indexer::ingester::startup_cleanup::cleanup_stale_address_queues(db_conn.as_ref()) + .await + { + error!("Failed to cleanup stale address queues: {}", e); + } + let is_rpc_node_local = args.rpc_url.contains("127.0.0.1"); let rpc_client = get_rpc_client(&args.rpc_url); @@ -295,6 +316,19 @@ async fn main() { } false => { info!("Starting indexer..."); + + info!("Syncing tree metadata..."); + if let Err(e) = photon_indexer::monitor::tree_metadata_sync::sync_tree_metadata( + rpc_client.as_ref(), + db_conn.as_ref(), + ) + .await + { + warn!("Failed to sync tree metadata on startup: {}. Will retry in background monitor.", e); + } else { + info!("Tree metadata sync completed successfully"); + } + // For localnet we can safely use a large batch size to speed up indexing. let max_concurrent_block_fetches = match args.max_concurrent_block_fetches { Some(max_concurrent_block_fetches) => max_concurrent_block_fetches, @@ -348,7 +382,10 @@ async fn main() { } }; - info!("Starting API server with port {}...", args.port); + info!( + "Starting API server with port {}, max_http_connections={}...", + args.port, args.max_http_connections + ); let api_handler = if args.disable_api { None } else { @@ -357,7 +394,9 @@ async fn main() { db_conn.clone(), rpc_client.clone(), args.prover_url, + args.prover_api_key, args.port, + args.max_http_connections, ) .await, ) diff --git a/src/migration/migrations/custom/custom20250211_000002_solayer2.rs b/src/migration/migrations/custom/custom20250211_000002_solayer2.rs index f66725da..e517700c 100644 --- a/src/migration/migrations/custom/custom20250211_000002_solayer2.rs +++ b/src/migration/migrations/custom/custom20250211_000002_solayer2.rs @@ -8,7 +8,7 @@ use crate::migration::model::table::Accounts; #[derive(DeriveMigrationName)] pub struct Migration; -async fn execute_sql<'a>(manager: &SchemaManager<'_>, sql: &str) -> Result<(), DbErr> { +async fn execute_sql(manager: &SchemaManager<'_>, sql: &str) -> Result<(), DbErr> { manager .get_connection() .execute(Statement::from_string( @@ -22,7 +22,7 @@ async fn execute_sql<'a>(manager: &SchemaManager<'_>, sql: &str) -> Result<(), D #[async_trait::async_trait] impl MigrationTrait for Migration { async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> { - let solayer_accounts = vec![ + let solayer_accounts = [ "ARDPkhymCbfdan375FCgPnBJQvUfHeb7nHVdBfwWSxrp", "2sYfW81EENCMe415CPhE2XzBA5iQf4TXRs31W1KP63YT", ]; diff --git a/src/migration/migrations/custom/custom20252201_000001_init.rs b/src/migration/migrations/custom/custom20252201_000001_init.rs index d09d7859..3579a58d 100644 --- a/src/migration/migrations/custom/custom20252201_000001_init.rs +++ b/src/migration/migrations/custom/custom20252201_000001_init.rs @@ -9,7 +9,7 @@ use crate::migration::model::table::Accounts; #[derive(DeriveMigrationName)] pub struct Migration; -async fn execute_sql<'a>(manager: &SchemaManager<'_>, sql: &str) -> Result<(), DbErr> { +async fn execute_sql(manager: &SchemaManager<'_>, sql: &str) -> Result<(), DbErr> { manager .get_connection() .execute(Statement::from_string( @@ -23,7 +23,7 @@ async fn execute_sql<'a>(manager: &SchemaManager<'_>, sql: &str) -> Result<(), D #[async_trait::async_trait] impl MigrationTrait for Migration { async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> { - let solayer_accounts = vec![ + let solayer_accounts = [ "S1ay5sk6FVkvsNFZShMw2YK3nfgJZ8tpBBGuHWDZ266", "2sYfW81EENCMe415CPhE2XzBA5iQf4TXRs31W1KP63YT", ]; diff --git a/src/migration/migrations/standard/m20220101_000001_init.rs b/src/migration/migrations/standard/m20220101_000001_init.rs index 1f5fa7ff..1b09ace6 100644 --- a/src/migration/migrations/standard/m20220101_000001_init.rs +++ b/src/migration/migrations/standard/m20220101_000001_init.rs @@ -12,7 +12,7 @@ use super::super::super::model::table::{ #[derive(DeriveMigrationName)] pub struct Migration; -async fn execute_sql<'a>(manager: &SchemaManager<'_>, sql: &str) -> Result<(), DbErr> { +async fn execute_sql(manager: &SchemaManager<'_>, sql: &str) -> Result<(), DbErr> { manager .get_connection() .execute(Statement::from_string( diff --git a/src/migration/migrations/standard/m20240914_000005_init.rs b/src/migration/migrations/standard/m20240914_000005_init.rs index ff552875..bc136511 100644 --- a/src/migration/migrations/standard/m20240914_000005_init.rs +++ b/src/migration/migrations/standard/m20240914_000005_init.rs @@ -6,7 +6,7 @@ use crate::migration::model::table::AccountTransactions; #[derive(DeriveMigrationName)] pub struct Migration; -async fn execute_sql<'a>(manager: &SchemaManager<'_>, sql: &str) -> Result<(), DbErr> { +async fn execute_sql(manager: &SchemaManager<'_>, sql: &str) -> Result<(), DbErr> { manager .get_connection() .execute(Statement::from_string( diff --git a/src/migration/migrations/standard/m20241008_000006_init.rs b/src/migration/migrations/standard/m20241008_000006_init.rs index 5b13e74c..888ad799 100644 --- a/src/migration/migrations/standard/m20241008_000006_init.rs +++ b/src/migration/migrations/standard/m20241008_000006_init.rs @@ -6,7 +6,7 @@ use crate::migration::model::table::TokenOwnerBalances; #[derive(DeriveMigrationName)] pub struct Migration; -async fn execute_sql<'a>(manager: &SchemaManager<'_>, sql: &str) -> Result<(), DbErr> { +async fn execute_sql(manager: &SchemaManager<'_>, sql: &str) -> Result<(), DbErr> { manager .get_connection() .execute(Statement::from_string( diff --git a/src/migration/migrations/standard/m20250206_000007_init.rs b/src/migration/migrations/standard/m20250206_000007_init.rs index ca777af6..060d957d 100644 --- a/src/migration/migrations/standard/m20250206_000007_init.rs +++ b/src/migration/migrations/standard/m20250206_000007_init.rs @@ -7,7 +7,7 @@ use sea_orm_migration::{ #[derive(DeriveMigrationName)] pub struct Migration; -async fn execute_sql<'a>(manager: &SchemaManager<'_>, sql: &str) -> Result<(), DbErr> { +async fn execute_sql(manager: &SchemaManager<'_>, sql: &str) -> Result<(), DbErr> { manager .get_connection() .execute(Statement::from_string( diff --git a/src/migration/migrations/standard/m20251021_000001_optimize_nullifier_queue_index.rs b/src/migration/migrations/standard/m20251021_000001_optimize_nullifier_queue_index.rs index 8ccc669c..10ccdc3e 100644 --- a/src/migration/migrations/standard/m20251021_000001_optimize_nullifier_queue_index.rs +++ b/src/migration/migrations/standard/m20251021_000001_optimize_nullifier_queue_index.rs @@ -4,7 +4,7 @@ use sea_orm_migration::prelude::*; #[derive(DeriveMigrationName)] pub struct Migration; -async fn execute_sql<'a>(manager: &SchemaManager<'_>, sql: &str) -> Result<(), DbErr> { +async fn execute_sql(manager: &SchemaManager<'_>, sql: &str) -> Result<(), DbErr> { manager .get_connection() .execute(Statement::from_string( diff --git a/src/migration/migrations/standard/m20260127_000001_add_onchain_pubkey.rs b/src/migration/migrations/standard/m20260127_000001_add_onchain_pubkey.rs new file mode 100644 index 00000000..059151c9 --- /dev/null +++ b/src/migration/migrations/standard/m20260127_000001_add_onchain_pubkey.rs @@ -0,0 +1,57 @@ +use sea_orm_migration::{ + prelude::*, + sea_orm::{ConnectionTrait, Statement}, +}; + +use super::super::super::model::table::Accounts; + +#[derive(DeriveMigrationName)] +pub struct Migration; + +async fn execute_sql(manager: &SchemaManager<'_>, sql: &str) -> Result<(), DbErr> { + manager + .get_connection() + .execute(Statement::from_string( + manager.get_database_backend(), + sql.to_string(), + )) + .await?; + Ok(()) +} + +#[async_trait::async_trait] +impl MigrationTrait for Migration { + async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> { + manager + .alter_table( + Table::alter() + .table(Accounts::Table) + .add_column(ColumnDef::new(Accounts::OnchainPubkey).binary().null()) + .to_owned(), + ) + .await?; + + execute_sql( + manager, + "CREATE INDEX accounts_onchain_pubkey_idx ON accounts (onchain_pubkey) WHERE NOT spent AND onchain_pubkey IS NOT NULL;", + ) + .await?; + + Ok(()) + } + + async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> { + execute_sql(manager, "DROP INDEX IF EXISTS accounts_onchain_pubkey_idx;").await?; + + manager + .alter_table( + Table::alter() + .table(Accounts::Table) + .drop_column(Accounts::OnchainPubkey) + .to_owned(), + ) + .await?; + + Ok(()) + } +} diff --git a/src/migration/migrations/standard/m20260201_000002_add_ata_owner.rs b/src/migration/migrations/standard/m20260201_000002_add_ata_owner.rs new file mode 100644 index 00000000..7a93ea2e --- /dev/null +++ b/src/migration/migrations/standard/m20260201_000002_add_ata_owner.rs @@ -0,0 +1,35 @@ +use sea_orm_migration::prelude::*; + +use super::super::super::model::table::TokenAccounts; + +#[derive(DeriveMigrationName)] +pub struct Migration; + +#[async_trait::async_trait] +impl MigrationTrait for Migration { + async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> { + manager + .alter_table( + Table::alter() + .table(TokenAccounts::Table) + .add_column(ColumnDef::new(TokenAccounts::AtaOwner).binary().null()) + .to_owned(), + ) + .await?; + + Ok(()) + } + + async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> { + manager + .alter_table( + Table::alter() + .table(TokenAccounts::Table) + .drop_column(TokenAccounts::AtaOwner) + .to_owned(), + ) + .await?; + + Ok(()) + } +} diff --git a/src/migration/migrations/standard/m20260210_000001_backfill_mint_onchain_pubkey.rs b/src/migration/migrations/standard/m20260210_000001_backfill_mint_onchain_pubkey.rs new file mode 100644 index 00000000..eb4e0474 --- /dev/null +++ b/src/migration/migrations/standard/m20260210_000001_backfill_mint_onchain_pubkey.rs @@ -0,0 +1,131 @@ +use sea_orm_migration::{ + prelude::*, + sea_orm::{ConnectionTrait, DatabaseBackend, Statement}, +}; + +use crate::common::token_layout::{ + COMPRESSED_MINT_PDA_LEN, COMPRESSED_MINT_PDA_OFFSET_SQL, TOKEN_ACCOUNT_TYPE_OFFSET, + TOKEN_ACCOUNT_TYPE_OFFSET_SQL, +}; + +#[derive(DeriveMigrationName)] +pub struct Migration; + +/// Raw 32-byte pubkey for cTokenmWW8bLPjZEBAUgYy3zKxQZW6VKi7bqNFEVv3m +const LIGHT_TOKEN_PROGRAM_ID_HEX: &str = + "0915a35723794e8fb65d075b6b72699c38dd02e5948b75b0e5a0418e80975b44"; + +fn backfill_mint_onchain_pubkey_sql(backend: DatabaseBackend) -> Result { + match backend { + DatabaseBackend::Postgres => Ok(format!( + "UPDATE accounts \ + SET onchain_pubkey = substring(data from {COMPRESSED_MINT_PDA_OFFSET_SQL} for {COMPRESSED_MINT_PDA_LEN}) \ + WHERE onchain_pubkey IS NULL \ + AND spent = false \ + AND data IS NOT NULL \ + AND length(data) > {TOKEN_ACCOUNT_TYPE_OFFSET} \ + AND substring(data from {TOKEN_ACCOUNT_TYPE_OFFSET_SQL} for 1) = E'\\\\x01' \ + AND owner = E'\\\\x{LIGHT_TOKEN_PROGRAM_ID_HEX}'" + )), + DatabaseBackend::Sqlite => Ok(format!( + "UPDATE accounts \ + SET onchain_pubkey = substr(data, {COMPRESSED_MINT_PDA_OFFSET_SQL}, {COMPRESSED_MINT_PDA_LEN}) \ + WHERE onchain_pubkey IS NULL \ + AND spent = 0 \ + AND data IS NOT NULL \ + AND length(data) > {TOKEN_ACCOUNT_TYPE_OFFSET} \ + AND substr(data, {TOKEN_ACCOUNT_TYPE_OFFSET_SQL}, 1) = X'01' \ + AND owner = X'{LIGHT_TOKEN_PROGRAM_ID_HEX}'" + )), + _ => Err(DbErr::Custom("Unsupported database backend".to_string())), + } +} + +fn clear_backfilled_mint_onchain_pubkey_sql(backend: DatabaseBackend) -> Result { + match backend { + DatabaseBackend::Postgres => Ok(format!( + "UPDATE accounts \ + SET onchain_pubkey = NULL \ + WHERE onchain_pubkey IS NOT NULL \ + AND spent = false \ + AND data IS NOT NULL \ + AND length(data) > {TOKEN_ACCOUNT_TYPE_OFFSET} \ + AND substring(data from {TOKEN_ACCOUNT_TYPE_OFFSET_SQL} for 1) = E'\\\\x01' \ + AND owner = E'\\\\x{LIGHT_TOKEN_PROGRAM_ID_HEX}' \ + AND onchain_pubkey = substring(data from {COMPRESSED_MINT_PDA_OFFSET_SQL} for {COMPRESSED_MINT_PDA_LEN})" + )), + DatabaseBackend::Sqlite => Ok(format!( + "UPDATE accounts \ + SET onchain_pubkey = NULL \ + WHERE onchain_pubkey IS NOT NULL \ + AND spent = 0 \ + AND data IS NOT NULL \ + AND length(data) > {TOKEN_ACCOUNT_TYPE_OFFSET} \ + AND substr(data, {TOKEN_ACCOUNT_TYPE_OFFSET_SQL}, 1) = X'01' \ + AND owner = X'{LIGHT_TOKEN_PROGRAM_ID_HEX}' \ + AND onchain_pubkey = substr(data, {COMPRESSED_MINT_PDA_OFFSET_SQL}, {COMPRESSED_MINT_PDA_LEN})" + )), + _ => Err(DbErr::Custom("Unsupported database backend".to_string())), + } +} + +#[async_trait::async_trait] +impl MigrationTrait for Migration { + async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> { + let backend = manager.get_database_backend(); + + // Backfill onchain_pubkey for compressed mint accounts. + // Compressed mints have: + // - owner = LIGHT_TOKEN_PROGRAM_ID + // - data length > 165 bytes + // - account_type byte at offset 165 (0-indexed) == 0x01 (Mint) + // The mint PDA is at data bytes [84..116] (0-indexed), which is + // substring starting at byte 85 (1-indexed) for 32 bytes. + let sql = backfill_mint_onchain_pubkey_sql(backend)?; + + manager + .get_connection() + .execute(Statement::from_string(backend, sql)) + .await?; + + Ok(()) + } + + async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> { + let backend = manager.get_database_backend(); + + // Clear only values this migration backfilled, preserving independently-updated rows. + let sql = clear_backfilled_mint_onchain_pubkey_sql(backend)?; + + manager + .get_connection() + .execute(Statement::from_string(backend, sql)) + .await?; + + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_backfill_sql_uses_shared_offsets() { + let pg_sql = backfill_mint_onchain_pubkey_sql(DatabaseBackend::Postgres).unwrap(); + assert!(pg_sql.contains(&format!( + "substring(data from {COMPRESSED_MINT_PDA_OFFSET_SQL} for {COMPRESSED_MINT_PDA_LEN})" + ))); + assert!(pg_sql.contains(&format!("length(data) > {TOKEN_ACCOUNT_TYPE_OFFSET}"))); + assert!(pg_sql.contains(&format!( + "substring(data from {TOKEN_ACCOUNT_TYPE_OFFSET_SQL} for 1)" + ))); + } + + #[test] + fn test_down_sql_only_clears_backfilled_values() { + let sqlite_sql = clear_backfilled_mint_onchain_pubkey_sql(DatabaseBackend::Sqlite).unwrap(); + assert!(sqlite_sql.contains("onchain_pubkey = substr(data,")); + assert!(sqlite_sql.contains("SET onchain_pubkey = NULL")); + } +} diff --git a/src/migration/migrations/standard/m20260210_000002_add_ata_owner_index.rs b/src/migration/migrations/standard/m20260210_000002_add_ata_owner_index.rs new file mode 100644 index 00000000..6eec4ab8 --- /dev/null +++ b/src/migration/migrations/standard/m20260210_000002_add_ata_owner_index.rs @@ -0,0 +1,56 @@ +use sea_orm_migration::{ + prelude::*, + sea_orm::{ConnectionTrait, DatabaseBackend, Statement}, +}; + +#[derive(DeriveMigrationName)] +pub struct Migration; + +const ATA_OWNER_INDEX_NAME: &str = "token_accounts_spent_ata_owner_idx"; + +fn create_index_sql(backend: DatabaseBackend) -> Result { + match backend { + DatabaseBackend::Postgres => Ok(format!( + "CREATE INDEX IF NOT EXISTS {ATA_OWNER_INDEX_NAME} \ + ON token_accounts (spent, ata_owner) \ + WHERE ata_owner IS NOT NULL;" + )), + DatabaseBackend::Sqlite => Ok(format!( + "CREATE INDEX IF NOT EXISTS {ATA_OWNER_INDEX_NAME} \ + ON token_accounts (spent, ata_owner);" + )), + _ => Err(DbErr::Custom("Unsupported database backend".to_string())), + } +} + +fn drop_index_sql(backend: DatabaseBackend) -> Result { + match backend { + DatabaseBackend::Postgres | DatabaseBackend::Sqlite => { + Ok(format!("DROP INDEX IF EXISTS {ATA_OWNER_INDEX_NAME};")) + } + _ => Err(DbErr::Custom("Unsupported database backend".to_string())), + } +} + +#[async_trait::async_trait] +impl MigrationTrait for Migration { + async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> { + let backend = manager.get_database_backend(); + let sql = create_index_sql(backend)?; + manager + .get_connection() + .execute(Statement::from_string(backend, sql)) + .await?; + Ok(()) + } + + async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> { + let backend = manager.get_database_backend(); + let sql = drop_index_sql(backend)?; + manager + .get_connection() + .execute(Statement::from_string(backend, sql)) + .await?; + Ok(()) + } +} diff --git a/src/migration/migrations/standard/mod.rs b/src/migration/migrations/standard/mod.rs index c58450f7..ef2486ed 100644 --- a/src/migration/migrations/standard/mod.rs +++ b/src/migration/migrations/standard/mod.rs @@ -14,7 +14,10 @@ pub mod m20250909_000001_add_queue_hash_chains; pub mod m20250910_000002_add_v2_queue_indexes; pub mod m20250923_000001_add_tree_metadata; pub mod m20251021_000001_optimize_nullifier_queue_index; - +pub mod m20260127_000001_add_onchain_pubkey; +pub mod m20260201_000002_add_ata_owner; +pub mod m20260210_000001_backfill_mint_onchain_pubkey; +pub mod m20260210_000002_add_ata_owner_index; pub fn get_standard_migrations() -> Vec> { vec![ Box::new(m20220101_000001_init::Migration), @@ -31,5 +34,9 @@ pub fn get_standard_migrations() -> Vec> { Box::new(m20250910_000002_add_v2_queue_indexes::Migration), Box::new(m20250923_000001_add_tree_metadata::Migration), Box::new(m20251021_000001_optimize_nullifier_queue_index::Migration), + Box::new(m20260127_000001_add_onchain_pubkey::Migration), + Box::new(m20260201_000002_add_ata_owner::Migration), + Box::new(m20260210_000001_backfill_mint_onchain_pubkey::Migration), + Box::new(m20260210_000002_add_ata_owner_index::Migration), ] } diff --git a/src/migration/model/table.rs b/src/migration/model/table.rs index 12b0c025..09ad7e39 100644 --- a/src/migration/model/table.rs +++ b/src/migration/model/table.rs @@ -16,6 +16,7 @@ pub enum Accounts { Table, Hash, Address, + OnchainPubkey, Data, DataHash, Owner, @@ -45,6 +46,7 @@ pub enum TokenAccounts { Tlv, Spent, PrevSpent, + AtaOwner, } #[derive(Copy, Clone, Iden)] diff --git a/src/monitor/mod.rs b/src/monitor/mod.rs index b88b4107..91a5f266 100644 --- a/src/monitor/mod.rs +++ b/src/monitor/mod.rs @@ -5,7 +5,7 @@ pub mod v1_tree_accounts; use std::{ sync::{ - atomic::{AtomicU64, Ordering}, + atomic::{AtomicBool, AtomicU64, Ordering}, Arc, }, time::Duration, @@ -42,6 +42,15 @@ use std::mem; const CHUNK_SIZE: usize = 100; pub static LATEST_SLOT: Lazy> = Lazy::new(|| Arc::new(AtomicU64::new(0))); +static TREE_VALIDATION_IN_PROGRESS: AtomicBool = AtomicBool::new(false); + +struct TreeValidationGuard; + +impl Drop for TreeValidationGuard { + fn drop(&mut self) { + TREE_VALIDATION_IN_PROGRESS.store(false, Ordering::SeqCst); + } +} async fn fetch_last_indexed_slot_with_infinite_retry(db: &DatabaseConnection) -> u64 { loop { @@ -77,11 +86,7 @@ pub fn continously_monitor_photon( let latest_slot = LATEST_SLOT.load(Ordering::SeqCst); let last_indexed_slot = fetch_last_indexed_slot_with_infinite_retry(db.as_ref()).await; - let lag = if latest_slot > last_indexed_slot { - latest_slot - last_indexed_slot - } else { - 0 - }; + let lag = latest_slot.saturating_sub(last_indexed_slot); metric! { statsd_gauge!("indexing_lag", lag); } @@ -94,14 +99,20 @@ pub fn continously_monitor_photon( error!("Indexing lag is too high: {}", lag); } } else { - let db_clone = db.clone(); - let rpc_clone = rpc_client.clone(); + if TREE_VALIDATION_IN_PROGRESS + .compare_exchange(false, true, Ordering::SeqCst, Ordering::SeqCst) + .is_ok() + { + let db_clone = db.clone(); + let rpc_clone = rpc_client.clone(); - tokio::spawn(async move { - let tree_roots = - load_db_tree_roots_with_infinite_retry(db_clone.as_ref()).await; - validate_tree_roots(rpc_clone.as_ref(), tree_roots).await; - }); + tokio::spawn(async move { + let _validation_guard = TreeValidationGuard; + let tree_roots = + load_db_tree_roots_with_infinite_retry(db_clone.as_ref()).await; + validate_tree_roots(rpc_clone.as_ref(), tree_roots).await; + }); + } // Spawn parallel verification tasks for each V2 tree let v2_trees = match queue_monitor::collect_v2_trees(db.as_ref()).await { @@ -142,7 +153,7 @@ pub fn continously_monitor_photon( } pub async fn update_latest_slot(rpc_client: &RpcClient) { - let slot = fetch_current_slot_with_infinite_retry(&rpc_client).await; + let slot = fetch_current_slot_with_infinite_retry(rpc_client).await; LATEST_SLOT.fetch_max(slot, Ordering::SeqCst); } diff --git a/src/monitor/queue_monitor.rs b/src/monitor/queue_monitor.rs index 81cceaf3..d53b6dd4 100644 --- a/src/monitor/queue_monitor.rs +++ b/src/monitor/queue_monitor.rs @@ -97,7 +97,7 @@ async fn verify_output_queue_hash_chains( batch_metadata.zkp_batch_size, currently_processing_batch_index, current_batch.get_num_inserted_zkps(), - current_batch.get_num_inserted_zkp_batch(), + current_batch.get_num_inserted_zkp_batch() > 0, ) .await } @@ -139,7 +139,7 @@ async fn verify_input_queue_hash_chains( queue_batches.zkp_batch_size, pending_batch_index, pending_batch.get_num_inserted_zkps(), - pending_batch.get_num_inserted_zkp_batch(), + pending_batch.get_num_inserted_zkp_batch() > 0, ) .await } @@ -185,11 +185,12 @@ async fn verify_address_queue_hash_chains( queue_batches.zkp_batch_size, pending_batch_index, current_batch.get_num_inserted_zkps(), - current_batch.get_num_inserted_zkp_batch(), + current_batch.get_num_inserted_zkp_batch() > 0, ) .await } +#[allow(clippy::too_many_arguments)] async fn verify_queue_hash_chains( db: &DatabaseConnection, queue_type: QueueType, @@ -199,23 +200,28 @@ async fn verify_queue_hash_chains( zkp_batch_size: u64, pending_batch_index: usize, num_inserted_zkps: u64, - num_inserted_in_current_zkp: u64, + has_partial_zkp_batch: bool, ) -> Result<(), Vec> { let mut divergences = Vec::new(); - if num_inserted_in_current_zkp > 0 && num_inserted_in_current_zkp < zkp_batch_size { + let on_chain_batch_hash_chains = &on_chain_hash_chains[pending_batch_index]; + let total_on_chain_zkps = on_chain_batch_hash_chains.len() as u64; + if num_inserted_zkps >= total_on_chain_zkps { debug!( - "Skipping ZKP verification for tree {} type {:?} - incomplete batch: {}/{} elements", - tree_pubkey, queue_type, num_inserted_in_current_zkp, zkp_batch_size + "Tree {} type {:?}: All {} hash chains already inserted, skipping validation", + tree_pubkey, queue_type, total_on_chain_zkps ); return Ok(()); } - let on_chain_batch_hash_chains = &on_chain_hash_chains[pending_batch_index]; - let total_on_chain_zkps = on_chain_batch_hash_chains.len() as u64; - if num_inserted_zkps >= total_on_chain_zkps { + let chains_to_check = if has_partial_zkp_batch { + total_on_chain_zkps.saturating_sub(1) + } else { + total_on_chain_zkps + }; + if num_inserted_zkps >= chains_to_check { debug!( - "Tree {} type {:?}: All {} hash chains already inserted, skipping validation", + "Tree {} type {:?}: {} hash chains, skipping partial batch validation", tree_pubkey, queue_type, total_on_chain_zkps ); return Ok(()); @@ -224,7 +230,8 @@ async fn verify_queue_hash_chains( let on_chain_chains: Vec<[u8; 32]> = on_chain_batch_hash_chains .iter() .skip(num_inserted_zkps as usize) - .map(|h| *h) + .take((chains_to_check - num_inserted_zkps) as usize) + .copied() .collect(); if on_chain_chains.is_empty() { @@ -543,9 +550,9 @@ pub fn log_divergence(divergence: &HashChainDivergence) { ); error!( " Expected: {}", - hex::encode(&divergence.expected_hash_chain) + hex::encode(divergence.expected_hash_chain) ); - error!(" On-chain: {}", hex::encode(&divergence.actual_hash_chain)); + error!(" On-chain: {}", hex::encode(divergence.actual_hash_chain)); } pub async fn verify_single_queue( diff --git a/src/monitor/tree_metadata_sync.rs b/src/monitor/tree_metadata_sync.rs index 4e12fcaa..dbbd8ae8 100644 --- a/src/monitor/tree_metadata_sync.rs +++ b/src/monitor/tree_metadata_sync.rs @@ -96,6 +96,10 @@ where return Ok(false); } + debug!( + "Parsed as V1 state tree: {} (queue={}, owner={})", + pubkey, data.queue_pubkey, data.owner + ); upsert_tree_metadata(db, pubkey, TreeType::StateV1, &data, slot).await?; info!( "Synced V1 state tree {} with height {}, root_history_capacity {}, seq {}, next_idx {}", @@ -127,7 +131,7 @@ where let data = TreeAccountData { queue_pubkey: Pubkey::new_from_array(metadata.metadata.associated_queue.to_bytes()), root_history_capacity: metadata.root_history_capacity as usize, - height: tree_account.height as u32, + height: tree_account.height, sequence_number: metadata.sequence_number, next_index: metadata.next_index, owner: Pubkey::new_from_array(metadata.metadata.access_metadata.owner.to_bytes()), @@ -141,6 +145,10 @@ where return Ok(false); } + debug!( + "Parsed as V2 state tree: {} (queue={}, owner={})", + pubkey, data.queue_pubkey, data.owner + ); upsert_tree_metadata(db, pubkey, TreeType::StateV2, &data, slot).await?; info!( @@ -157,7 +165,7 @@ where let data = TreeAccountData { queue_pubkey: pubkey, // For V2 address trees, queue == tree root_history_capacity: metadata.root_history_capacity as usize, - height: tree_account.height as u32, + height: tree_account.height, sequence_number: metadata.sequence_number, next_index: metadata.next_index, owner: Pubkey::new_from_array(metadata.metadata.access_metadata.owner.to_bytes()), @@ -253,6 +261,8 @@ where .on_conflict( sea_orm::sea_query::OnConflict::column(tree_metadata::Column::TreePubkey) .update_columns([ + tree_metadata::Column::QueuePubkey, + tree_metadata::Column::TreeType, tree_metadata::Column::SequenceNumber, tree_metadata::Column::NextIndex, tree_metadata::Column::LastSyncedSlot, diff --git a/src/openapi/mod.rs b/src/openapi/mod.rs index c9498dec..5bfadf77 100644 --- a/src/openapi/mod.rs +++ b/src/openapi/mod.rs @@ -301,7 +301,8 @@ fn fix_examples_for_allOf_references(schema: RefOr) -> RefOr { } _ => schema, }), - Schema::AllOf(ref all_of) => all_of.items[0].clone(), + Schema::AllOf(ref all_of) if all_of.items.len() == 1 => all_of.items[0].clone(), + Schema::AllOf(_) => RefOr::T(schema), _ => RefOr::T(schema), }, RefOr::Ref(_) => schema, @@ -343,7 +344,7 @@ fn find_all_components(schema: RefOr) -> HashSet { ref_location .ref_location .split('/') - .last() + .next_back() .unwrap() .to_string(), ); diff --git a/src/openapi/specs/api.yaml b/src/openapi/specs/api.yaml index f857ec24..57b51647 100644 --- a/src/openapi/specs/api.yaml +++ b/src/openapi/specs/api.yaml @@ -4,12 +4,12 @@ info: description: Solana indexer for general compression license: name: Apache-2.0 - version: 0.50.0 + version: 0.51.2 servers: - url: https://devnet.helius-rpc.com?api-key= paths: - /getBatchAddressUpdateInfo: - summary: getBatchAddressUpdateInfo + /getAccountInterface: + summary: getAccountInterface post: requestBody: content: @@ -36,19 +36,15 @@ paths: type: string description: The name of the method to invoke. enum: - - getBatchAddressUpdateInfo + - getAccountInterface params: type: object + description: Request for getAccountInterface required: - - tree - - batchSize + - address properties: - batchSize: - type: integer - format: uint16 - minimum: 0 - tree: - $ref: '#/components/schemas/Hash' + address: + $ref: '#/components/schemas/SerializablePubkey' additionalProperties: false required: true responses: @@ -81,33 +77,14 @@ paths: - '2.0' result: type: object + description: Response for getAccountInterface required: - context - - startIndex - - addresses - - nonInclusionProofs - - subtrees properties: - addresses: - type: array - items: - $ref: '#/components/schemas/AddressSeq' context: $ref: '#/components/schemas/Context' - nonInclusionProofs: - type: array - items: - $ref: '#/components/schemas/MerkleContextWithNewAddressProof' - startIndex: - type: integer - format: uint64 - minimum: 0 - subtrees: - type: array - items: - type: string - format: binary - additionalProperties: false + value: + $ref: '#/components/schemas/AccountInterface' '429': description: Exceeded rate limit. content: @@ -3004,6 +2981,128 @@ paths: type: string jsonrpc: type: string + /getMultipleAccountInterfaces: + summary: getMultipleAccountInterfaces + post: + requestBody: + content: + application/json: + schema: + type: object + required: + - jsonrpc + - id + - method + - params + properties: + id: + type: string + description: An ID to identify the request. + enum: + - test-account + jsonrpc: + type: string + description: The version of the JSON-RPC protocol. + enum: + - '2.0' + method: + type: string + description: The name of the method to invoke. + enum: + - getMultipleAccountInterfaces + params: + type: object + description: Request for getMultipleAccountInterfaces + required: + - addresses + properties: + addresses: + type: array + items: + $ref: '#/components/schemas/SerializablePubkey' + description: List of account addresses to look up (max 100) + additionalProperties: false + required: true + responses: + '200': + description: '' + content: + application/json: + schema: + type: object + required: + - jsonrpc + - id + properties: + error: + type: object + properties: + code: + type: integer + message: + type: string + id: + type: string + description: An ID to identify the response. + enum: + - test-account + jsonrpc: + type: string + description: The version of the JSON-RPC protocol. + enum: + - '2.0' + result: + type: object + description: Response for getMultipleAccountInterfaces + required: + - context + - value + properties: + context: + $ref: '#/components/schemas/Context' + value: + type: array + items: + allOf: + - $ref: '#/components/schemas/AccountInterface' + nullable: true + description: List of account results (Some for found accounts, None for not found) + '429': + description: Exceeded rate limit. + content: + application/json: + schema: + type: object + properties: + error: + type: object + properties: + code: + type: integer + message: + type: string + id: + type: string + jsonrpc: + type: string + '500': + description: The server encountered an unexpected condition that prevented it from fulfilling the request. + content: + application/json: + schema: + type: object + properties: + error: + type: object + properties: + code: + type: integer + message: + type: string + id: + type: string + jsonrpc: + type: string /getMultipleCompressedAccountProofs: summary: getMultipleCompressedAccountProofs post: @@ -3261,8 +3360,8 @@ paths: default: addresses: null hashes: - - 1111111QLbz7JHiBTspS962RLKV8GndWFwiEaqKM - - 1111111ogCyDbaRMvkdsHB3qfdyFYaG1WtRUAfdh + - 11157t3sqMV725NVRLrVQbAu98Jjfk1uCKehJnXXQs + - 1117mWrzzrZr312ebPDHu8tbfMwFNvCvMbr6WepCNG properties: addresses: type: array @@ -3278,8 +3377,8 @@ paths: example: addresses: null hashes: - - 1111111QLbz7JHiBTspS962RLKV8GndWFwiEaqKM - - 1111111ogCyDbaRMvkdsHB3qfdyFYaG1WtRUAfdh + - 11157t3sqMV725NVRLrVQbAu98Jjfk1uCKehJnXXQs + - 1117mWrzzrZr312ebPDHu8tbfMwFNvCvMbr6WepCNG required: true responses: '200': @@ -3391,8 +3490,8 @@ paths: default: addresses: null hashes: - - 11111112D1oxKts8YPdTJRG5FzxTNpMtWmq8hkVx3 - - 11111112cMQwSC9qirWGjZM6gLGwW69X22mqwLLGP + - 1119DWteoLSdjvrT6g6L8C2PfDD2faiTQUpsjY2RiF + - 111BuZ6b86gm7XhxjvTakhRvxSMjXp2GqgifkNUmDK properties: addresses: type: array @@ -3408,8 +3507,8 @@ paths: example: addresses: null hashes: - - 11111112D1oxKts8YPdTJRG5FzxTNpMtWmq8hkVx3 - - 11111112cMQwSC9qirWGjZM6gLGwW69X22mqwLLGP + - 1119DWteoLSdjvrT6g6L8C2PfDD2faiTQUpsjY2RiF + - 111BuZ6b86gm7XhxjvTakhRvxSMjXp2GqgifkNUmDK required: true responses: '200': @@ -3740,28 +3839,20 @@ paths: required: - tree properties: - tree: - $ref: '#/components/schemas/Hash' - outputQueueStartIndex: - type: integer - format: uint64 - nullable: true - minimum: 0 - outputQueueLimit: - type: integer - format: uint16 + addressQueue: + allOf: + - $ref: '#/components/schemas/QueueRequest' nullable: true - minimum: 0 - inputQueueStartIndex: - type: integer - format: uint64 + inputQueue: + allOf: + - $ref: '#/components/schemas/QueueRequest' nullable: true - minimum: 0 - inputQueueLimit: - type: integer - format: uint16 + outputQueue: + allOf: + - $ref: '#/components/schemas/QueueRequest' nullable: true - minimum: 0 + tree: + $ref: '#/components/schemas/Hash' additionalProperties: false required: true responses: @@ -3797,29 +3888,130 @@ paths: required: - context properties: + addressQueue: + $ref: '#/components/schemas/AddressQueueData' context: $ref: '#/components/schemas/Context' - outputQueueElements: - type: array - nullable: true - items: - $ref: '#/components/schemas/GetQueueElementsResponseValue' - outputQueueIndex: + stateQueue: + $ref: '#/components/schemas/StateQueueData' + additionalProperties: false + '429': + description: Exceeded rate limit. + content: + application/json: + schema: + type: object + properties: + error: + type: object + properties: + code: type: integer - format: uint64 - nullable: true - minimum: 0 - inputQueueElements: + message: + type: string + id: + type: string + jsonrpc: + type: string + '500': + description: The server encountered an unexpected condition that prevented it from fulfilling the request. + content: + application/json: + schema: + type: object + properties: + error: + type: object + properties: + code: + type: integer + message: + type: string + id: + type: string + jsonrpc: + type: string + /getQueueInfo: + summary: getQueueInfo + post: + requestBody: + content: + application/json: + schema: + type: object + required: + - jsonrpc + - id + - method + - params + properties: + id: + type: string + description: An ID to identify the request. + enum: + - test-account + jsonrpc: + type: string + description: The version of the JSON-RPC protocol. + enum: + - '2.0' + method: + type: string + description: The name of the method to invoke. + enum: + - getQueueInfo + params: + type: object + properties: + trees: + type: array + items: + type: string + nullable: true + additionalProperties: false + required: true + responses: + '200': + description: '' + content: + application/json: + schema: + type: object + required: + - jsonrpc + - id + properties: + error: + type: object + properties: + code: + type: integer + message: + type: string + id: + type: string + description: An ID to identify the response. + enum: + - test-account + jsonrpc: + type: string + description: The version of the JSON-RPC protocol. + enum: + - '2.0' + result: + type: object + required: + - queues + - slot + properties: + queues: type: array - nullable: true items: - $ref: '#/components/schemas/GetQueueElementsResponseValue' - inputQueueIndex: + $ref: '#/components/schemas/QueueInfo' + slot: type: integer format: uint64 - nullable: true minimum: 0 - additionalProperties: false '429': description: Exceeded rate limit. content: @@ -4417,6 +4609,23 @@ components: discriminator: $ref: '#/components/schemas/UnsignedInteger' additionalProperties: false + AccountInterface: + type: object + description: Unified account interface — works for both on-chain and compressed accounts + required: + - key + - account + properties: + account: + $ref: '#/components/schemas/SolanaAccountData' + cold: + type: array + items: + $ref: '#/components/schemas/AccountV2' + description: Compressed accounts associated with this pubkey + nullable: true + key: + $ref: '#/components/schemas/SerializablePubkey' AccountList: type: object required: @@ -4440,6 +4649,27 @@ components: allOf: - $ref: '#/components/schemas/AccountV2' nullable: true + AccountProofInputs: + type: object + required: + - hash + - root + - rootIndex + - leafIndex + - merkleContext + properties: + hash: + type: string + leafIndex: + type: integer + format: uint64 + minimum: 0 + merkleContext: + $ref: '#/components/schemas/MerkleContextV2' + root: + type: string + rootIndex: + $ref: '#/components/schemas/RootIndex' AccountState: type: string enum: @@ -4512,18 +4742,93 @@ components: type: array items: $ref: '#/components/schemas/AddressWithTree' - AddressSeq: + AddressProofInputs: type: object required: - address - - seq + - root + - rootIndex + - merkleContext properties: address: - $ref: '#/components/schemas/SerializablePubkey' - seq: + type: string + merkleContext: + $ref: '#/components/schemas/MerkleContextV2' + root: + type: string + rootIndex: + type: integer + format: uint16 + minimum: 0 + AddressQueueData: + type: object + required: + - addresses + - queueIndices + - nodes + - lowElementIndices + - lowElementValues + - lowElementNextIndices + - lowElementNextValues + - leavesHashChains + - initialRoot + - startIndex + - subtrees + - rootSeq + properties: + addresses: + type: array + items: + $ref: '#/components/schemas/SerializablePubkey' + initialRoot: + $ref: '#/components/schemas/Hash' + leavesHashChains: + type: array + items: + $ref: '#/components/schemas/Hash' + lowElementIndices: + type: array + items: + type: integer + format: uint64 + minimum: 0 + lowElementNextIndices: + type: array + items: + type: integer + format: uint64 + minimum: 0 + lowElementNextValues: + type: array + items: + $ref: '#/components/schemas/Hash' + lowElementValues: + type: array + items: + $ref: '#/components/schemas/Hash' + nodes: + type: array + items: + $ref: '#/components/schemas/Node' + description: Deduplicated tree nodes - clients reconstruct proofs from these using low_element_indices + queueIndices: + type: array + items: + type: integer + format: uint64 + minimum: 0 + rootSeq: type: integer format: uint64 minimum: 0 + startIndex: + type: integer + format: uint64 + minimum: 0 + subtrees: + type: array + items: + $ref: '#/components/schemas/Hash' additionalProperties: false AddressWithTree: type: object @@ -4578,14 +4883,17 @@ components: - c properties: a: - type: string - format: binary + type: array + items: + type: integer b: - type: string - format: binary + type: array + items: + type: integer c: - type: string - format: binary + type: array + items: + type: integer CompressedProofWithContext: type: object required: @@ -4625,36 +4933,19 @@ components: CompressedProofWithContextV2: type: object required: - - roots - - rootIndices - - leafIndices - - leaves - - merkleContexts + - accounts + - addresses properties: - compressedProof: - $ref: '#/components/schemas/CompressedProof' - leafIndices: + accounts: type: array items: - type: integer - format: uint32 - minimum: 0 - leaves: - type: array - items: - type: string - merkleContexts: - type: array - items: - $ref: '#/components/schemas/MerkleContextV2' - rootIndices: - type: array - items: - $ref: '#/components/schemas/RootIndex' - roots: + $ref: '#/components/schemas/AccountProofInputs' + addresses: type: array items: - type: string + $ref: '#/components/schemas/AddressProofInputs' + compressedProof: + $ref: '#/components/schemas/CompressedProof' CompressionInfoV2: type: object required: @@ -4758,44 +5049,52 @@ components: treeContext: $ref: '#/components/schemas/TreeContextInfo' additionalProperties: false - GetQueueElementsResponseValue: + Hash: + type: string + description: A 32-byte hash represented as a base58 string. + example: 11111112cMQwSC9qirWGjZM6gLGwW69X22mqwLLGP + InputQueueData: type: object required: - - proof - - root - - leafIndex - - leaf - - tree - - rootSeq - - accountHash + - leafIndices + - accountHashes + - leaves + - txHashes + - nullifiers + - firstQueueIndex + - leavesHashChains properties: - accountHash: - $ref: '#/components/schemas/Hash' - leaf: - $ref: '#/components/schemas/Hash' - leafIndex: - type: integer - format: uint64 - minimum: 0 - proof: + accountHashes: type: array items: $ref: '#/components/schemas/Hash' - root: - $ref: '#/components/schemas/Hash' - rootSeq: + firstQueueIndex: type: integer format: uint64 minimum: 0 - tree: - $ref: '#/components/schemas/Hash' - txHash: - $ref: '#/components/schemas/Hash' + leafIndices: + type: array + items: + type: integer + format: uint64 + minimum: 0 + leaves: + type: array + items: + $ref: '#/components/schemas/Hash' + leavesHashChains: + type: array + items: + $ref: '#/components/schemas/Hash' + nullifiers: + type: array + items: + $ref: '#/components/schemas/Hash' + txHashes: + type: array + items: + $ref: '#/components/schemas/Hash' additionalProperties: false - Hash: - type: string - description: A 32-byte hash represented as a base58 string. - example: 11111112cMQwSC9qirWGjZM6gLGwW69X22mqwLLGP Limit: type: integer format: uint64 @@ -4870,6 +5169,58 @@ components: format: uint64 minimum: 0 additionalProperties: false + Node: + type: object + description: A tree node with its encoded index and hash + required: + - index + - hash + properties: + hash: + $ref: '#/components/schemas/Hash' + index: + type: integer + format: uint64 + description: 'Encoded node index: (level << 56) | position' + minimum: 0 + additionalProperties: false + OutputQueueData: + type: object + required: + - leafIndices + - accountHashes + - leaves + - firstQueueIndex + - nextIndex + - leavesHashChains + properties: + accountHashes: + type: array + items: + $ref: '#/components/schemas/Hash' + firstQueueIndex: + type: integer + format: uint64 + minimum: 0 + leafIndices: + type: array + items: + type: integer + format: uint64 + minimum: 0 + leaves: + type: array + items: + $ref: '#/components/schemas/Hash' + leavesHashChains: + type: array + items: + $ref: '#/components/schemas/Hash' + nextIndex: + type: integer + format: uint64 + minimum: 0 + additionalProperties: false OwnerBalance: type: object required: @@ -4938,6 +5289,47 @@ components: type: array items: $ref: '#/components/schemas/SignatureInfo' + QueueInfo: + type: object + required: + - tree + - queue + - queueType + - queueSize + properties: + queue: + type: string + queueSize: + type: integer + format: uint64 + minimum: 0 + queueType: + type: integer + format: uint8 + minimum: 0 + tree: + type: string + QueueRequest: + type: object + description: Parameters for requesting queue elements + required: + - limit + properties: + limit: + type: integer + format: uint16 + minimum: 0 + startIndex: + type: integer + format: uint64 + nullable: true + minimum: 0 + zkpBatchSize: + type: integer + format: uint16 + nullable: true + minimum: 0 + additionalProperties: false RootIndex: type: object required: @@ -4953,8 +5345,8 @@ components: SerializablePubkey: type: string description: A Solana public key represented as a base58 string. - default: 111111131h1vYVSYuKP6AhS86fbRdMw9XHiZAvAaj - example: 111111131h1vYVSYuKP6AhS86fbRdMw9XHiZAvAaj + default: 111FJo4zLAGU9nzTWa6EnbV4VAmtG4FR8kcokrtZYr + example: 111FJo4zLAGU9nzTWa6EnbV4VAmtG4FR8kcokrtZYr SerializableSignature: type: string description: A Solana transaction signature. @@ -5007,6 +5399,53 @@ components: $ref: '#/components/schemas/SerializableSignature' slot: $ref: '#/components/schemas/UnsignedInteger' + SolanaAccountData: + type: object + description: Nested Solana account fields (matches getAccountInfo shape) + required: + - lamports + - data + - owner + - executable + - rentEpoch + - space + properties: + data: + $ref: '#/components/schemas/Base64String' + executable: + type: boolean + lamports: + $ref: '#/components/schemas/UnsignedInteger' + owner: + $ref: '#/components/schemas/SerializablePubkey' + rentEpoch: + $ref: '#/components/schemas/UnsignedInteger' + space: + $ref: '#/components/schemas/UnsignedInteger' + StateQueueData: + type: object + description: State queue data with shared tree nodes for output and input queues + required: + - initialRoot + - rootSeq + properties: + initialRoot: + $ref: '#/components/schemas/Hash' + inputQueue: + $ref: '#/components/schemas/InputQueueData' + nodes: + type: array + items: + $ref: '#/components/schemas/Node' + description: Shared deduplicated tree nodes for state queues (output + input) + outputQueue: + $ref: '#/components/schemas/OutputQueueData' + rootSeq: + type: integer + format: uint64 + description: Sequence number of the root + minimum: 0 + additionalProperties: false TokenAccount: type: object required: diff --git a/src/openapi/specs/getCompressedAccount.yaml b/src/openapi/specs/getCompressedAccount.yaml deleted file mode 100644 index 5c8a72c2..00000000 --- a/src/openapi/specs/getCompressedAccount.yaml +++ /dev/null @@ -1,165 +0,0 @@ -openapi: 3.0.3 -info: - title: photon-indexer - description: Solana indexer for general compression - license: - name: Apache-2.0 - version: 0.50.0 -servers: -- url: https://mainnet.helius-rpc.com?api-key= -paths: - /getCompressedAccount: - summary: getCompressedAccount - post: - requestBody: - content: - application/json: - schema: - type: object - required: - - jsonrpc - - id - - method - - params - properties: - id: - type: string - description: An ID to identify the request. - enum: - - test-account - jsonrpc: - type: string - description: The version of the JSON-RPC protocol. - enum: - - '2.0' - method: - type: string - description: The name of the method to invoke. - enum: - - getCompressedAccount - params: - type: object - description: Request for compressed account data - default: - address: null - hash: '11111111111111111111111111111111' - properties: - address: - allOf: - - $ref: '#/components/schemas/SerializablePubkey' - nullable: true - hash: - allOf: - - $ref: '#/components/schemas/Hash' - nullable: true - additionalProperties: false - example: - address: null - hash: '11111111111111111111111111111111' - required: true - responses: - '200': - description: '' - content: - application/json: - schema: - type: object - required: - - context - properties: - context: - $ref: '#/components/schemas/Context' - value: - $ref: '#/components/schemas/Account' - additionalProperties: false - '429': - description: Exceeded rate limit. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '500': - description: The server encountered an unexpected condition that prevented it from fulfilling the request. - content: - application/json: - schema: - type: object - properties: - error: - type: string -components: - schemas: - Account: - type: object - required: - - hash - - owner - - lamports - - tree - - leafIndex - - seq - - slotCreated - properties: - address: - $ref: '#/components/schemas/SerializablePubkey' - data: - $ref: '#/components/schemas/AccountData' - hash: - $ref: '#/components/schemas/Hash' - lamports: - $ref: '#/components/schemas/UnsignedInteger' - leafIndex: - $ref: '#/components/schemas/UnsignedInteger' - owner: - $ref: '#/components/schemas/SerializablePubkey' - seq: - $ref: '#/components/schemas/UnsignedInteger' - slotCreated: - $ref: '#/components/schemas/UnsignedInteger' - tree: - $ref: '#/components/schemas/SerializablePubkey' - additionalProperties: false - AccountData: - type: object - required: - - discriminator - - data - - dataHash - properties: - data: - $ref: '#/components/schemas/Base64String' - dataHash: - $ref: '#/components/schemas/Hash' - discriminator: - $ref: '#/components/schemas/UnsignedInteger' - additionalProperties: false - Base64String: - type: string - description: A base 64 encoded string. - default: SGVsbG8sIFdvcmxkIQ== - example: SGVsbG8sIFdvcmxkIQ== - Context: - type: object - required: - - slot - properties: - slot: - type: integer - default: 100 - example: 100 - Hash: - type: string - description: A 32-byte hash represented as a base58 string. - example: 11111112cMQwSC9qirWGjZM6gLGwW69X22mqwLLGP - SerializablePubkey: - type: string - description: A Solana public key represented as a base58 string. - default: 11111112D1oxKts8YPdTJRG5FzxTNpMtWmq8hkVx3 - example: 11111112D1oxKts8YPdTJRG5FzxTNpMtWmq8hkVx3 - UnsignedInteger: - type: integer - default: 100 - example: 100 diff --git a/src/openapi/specs/getCompressedAccountBalance.yaml b/src/openapi/specs/getCompressedAccountBalance.yaml deleted file mode 100644 index c81337be..00000000 --- a/src/openapi/specs/getCompressedAccountBalance.yaml +++ /dev/null @@ -1,117 +0,0 @@ -openapi: 3.0.3 -info: - title: photon-indexer - description: Solana indexer for general compression - license: - name: Apache-2.0 - version: 0.50.0 -servers: -- url: https://mainnet.helius-rpc.com?api-key= -paths: - /: - summary: getCompressedAccountBalance - post: - requestBody: - content: - application/json: - schema: - type: object - required: - - jsonrpc - - id - - method - - params - properties: - id: - type: string - description: An ID to identify the request. - enum: - - test-account - jsonrpc: - type: string - description: The version of the JSON-RPC protocol. - enum: - - '2.0' - method: - type: string - description: The name of the method to invoke. - enum: - - getCompressedAccountBalance - params: - type: object - description: Request for compressed account data - default: - address: null - hash: '11111111111111111111111111111111' - properties: - address: - allOf: - - $ref: '#/components/schemas/SerializablePubkey' - nullable: true - hash: - allOf: - - $ref: '#/components/schemas/Hash' - nullable: true - additionalProperties: false - example: - address: null - hash: '11111111111111111111111111111111' - required: true - responses: - '200': - description: '' - content: - application/json: - schema: - type: object - required: - - context - - value - properties: - context: - $ref: '#/components/schemas/Context' - value: - $ref: '#/components/schemas/UnsignedInteger' - additionalProperties: false - '429': - description: Exceeded rate limit. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '500': - description: The server encountered an unexpected condition that prevented it from fulfilling the request. - content: - application/json: - schema: - type: object - properties: - error: - type: string -components: - schemas: - Context: - type: object - required: - - slot - properties: - slot: - type: integer - default: 100 - example: 100 - Hash: - type: string - description: A 32-byte hash represented as a base58 string. - example: 11111112cMQwSC9qirWGjZM6gLGwW69X22mqwLLGP - SerializablePubkey: - type: string - description: A Solana public key represented as a base58 string. - default: 11111112cMQwSC9qirWGjZM6gLGwW69X22mqwLLGP - example: 11111112cMQwSC9qirWGjZM6gLGwW69X22mqwLLGP - UnsignedInteger: - type: integer - default: 100 - example: 100 diff --git a/src/openapi/specs/getCompressedAccountProof.yaml b/src/openapi/specs/getCompressedAccountProof.yaml deleted file mode 100644 index 3fe5e364..00000000 --- a/src/openapi/specs/getCompressedAccountProof.yaml +++ /dev/null @@ -1,130 +0,0 @@ -openapi: 3.0.3 -info: - title: photon-indexer - description: Solana indexer for general compression - license: - name: Apache-2.0 - version: 0.50.0 -servers: -- url: https://mainnet.helius-rpc.com?api-key= -paths: - /: - summary: getCompressedAccountProof - post: - requestBody: - content: - application/json: - schema: - type: object - required: - - jsonrpc - - id - - method - - params - properties: - id: - type: string - description: An ID to identify the request. - enum: - - test-account - jsonrpc: - type: string - description: The version of the JSON-RPC protocol. - enum: - - '2.0' - method: - type: string - description: The name of the method to invoke. - enum: - - getCompressedAccountProof - params: - type: object - required: - - hash - properties: - hash: - $ref: '#/components/schemas/Hash' - required: true - responses: - '200': - description: '' - content: - application/json: - schema: - type: object - required: - - context - - value - properties: - context: - $ref: '#/components/schemas/Context' - value: - $ref: '#/components/schemas/MerkleProofWithContext' - additionalProperties: false - '429': - description: Exceeded rate limit. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '500': - description: The server encountered an unexpected condition that prevented it from fulfilling the request. - content: - application/json: - schema: - type: object - properties: - error: - type: string -components: - schemas: - Context: - type: object - required: - - slot - properties: - slot: - type: integer - default: 100 - example: 100 - Hash: - type: string - description: A 32-byte hash represented as a base58 string. - example: 11111112cMQwSC9qirWGjZM6gLGwW69X22mqwLLGP - MerkleProofWithContext: - type: object - required: - - proof - - root - - leafIndex - - hash - - merkleTree - - rootSeq - properties: - hash: - $ref: '#/components/schemas/Hash' - leafIndex: - type: integer - format: int32 - minimum: 0 - merkleTree: - $ref: '#/components/schemas/SerializablePubkey' - proof: - type: array - items: - $ref: '#/components/schemas/Hash' - root: - $ref: '#/components/schemas/Hash' - rootSeq: - type: integer - format: int64 - minimum: 0 - additionalProperties: false - SerializablePubkey: - type: string - description: A Solana public key represented as a base58 string. - default: 11111117353mdUKehx9GW6JNHznGt5oSZs9fWkVkB - example: 11111117353mdUKehx9GW6JNHznGt5oSZs9fWkVkB diff --git a/src/openapi/specs/getCompressedAccountsByOwner.yaml b/src/openapi/specs/getCompressedAccountsByOwner.yaml deleted file mode 100644 index fed347a9..00000000 --- a/src/openapi/specs/getCompressedAccountsByOwner.yaml +++ /dev/null @@ -1,787 +0,0 @@ -openapi: 3.0.3 -info: - title: photon-indexer - description: Solana indexer for general compression - license: - name: Apache-2.0 - version: 0.50.0 -servers: -- url: https://mainnet.helius-rpc.com?api-key= -paths: - /getCompressedAccount: - summary: getCompressedAccount - post: - requestBody: - content: - application/json: - schema: - type: object - required: - - jsonrpc - - id - - method - - params - properties: - id: - type: string - description: An ID to identify the request. - enum: - - test-account - jsonrpc: - type: string - description: The version of the JSON-RPC protocol. - enum: - - '2.0' - method: - type: string - description: The name of the method to invoke. - enum: - - getCompressedAccount - params: - type: object - description: Request for compressed account data - default: - address: null - hash: '11111111111111111111111111111111' - properties: - address: - allOf: - - $ref: '#/components/schemas/SerializablePubkey' - nullable: true - hash: - allOf: - - $ref: '#/components/schemas/Hash' - nullable: true - additionalProperties: false - example: - address: null - hash: '11111111111111111111111111111111' - required: true - responses: - '200': - description: '' - content: - application/json: - schema: - type: object - required: - - context - - value - properties: - context: - $ref: '#/components/schemas/Context' - value: - $ref: '#/components/schemas/Account' - '400': - description: Invalid request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '401': - description: Unauthorized request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '403': - description: Request was forbidden. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '404': - description: The specified resource was not found. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '429': - description: Exceeded rate limit. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '500': - description: The server encountered an unexpected condition that prevented it from fulfilling the request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - /getCompressedAccountProof: - summary: getCompressedAccountProof - post: - requestBody: - content: - application/json: - schema: - type: object - required: - - jsonrpc - - id - - method - - params - properties: - id: - type: string - description: An ID to identify the request. - enum: - - test-account - jsonrpc: - type: string - description: The version of the JSON-RPC protocol. - enum: - - '2.0' - method: - type: string - description: The name of the method to invoke. - enum: - - getCompressedAccountProof - params: - type: object - required: - - hash - properties: - hash: - $ref: '#/components/schemas/Hash' - required: true - responses: - '200': - description: '' - content: - application/json: - schema: - type: object - required: - - context - - value - properties: - context: - $ref: '#/components/schemas/Context' - value: - $ref: '#/components/schemas/MerkleProofWithContext' - '400': - description: Invalid request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '401': - description: Unauthorized request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '403': - description: Request was forbidden. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '404': - description: The specified resource was not found. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '429': - description: Exceeded rate limit. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '500': - description: The server encountered an unexpected condition that prevented it from fulfilling the request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - /getCompressedAccountsByOwner: - summary: getCompressedAccountsByOwner - post: - requestBody: - content: - application/json: - schema: - type: object - required: - - jsonrpc - - id - - method - - params - properties: - id: - type: string - description: An ID to identify the request. - enum: - - test-account - jsonrpc: - type: string - description: The version of the JSON-RPC protocol. - enum: - - '2.0' - method: - type: string - description: The name of the method to invoke. - enum: - - getCompressedAccountsByOwner - params: - type: object - required: - - owner - properties: - cursor: - allOf: - - $ref: '#/components/schemas/Hash' - nullable: true - dataSlice: - allOf: - - $ref: '#/components/schemas/DataSlice' - nullable: true - filters: - type: array - items: - $ref: '#/components/schemas/FilterSelector' - limit: - allOf: - - $ref: '#/components/schemas/Limit' - nullable: true - owner: - $ref: '#/components/schemas/SerializablePubkey' - additionalProperties: false - required: true - responses: - '200': - description: '' - content: - application/json: - schema: - type: object - required: - - context - - value - properties: - context: - $ref: '#/components/schemas/Context' - value: - $ref: '#/components/schemas/PaginatedAccountList' - additionalProperties: false - '429': - description: Exceeded rate limit. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '500': - description: The server encountered an unexpected condition that prevented it from fulfilling the request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - /getCompressedTokenAccountsByDelegate: - summary: getCompressedTokenAccountsByDelegate - post: - requestBody: - content: - application/json: - schema: - type: object - required: - - jsonrpc - - id - - method - - params - properties: - id: - type: string - description: An ID to identify the request. - enum: - - test-account - jsonrpc: - type: string - description: The version of the JSON-RPC protocol. - enum: - - '2.0' - method: - type: string - description: The name of the method to invoke. - enum: - - getCompressedTokenAccountsByDelegate - params: - type: object - required: - - delegate - properties: - cursor: - allOf: - - $ref: '#/components/schemas/Base58String' - nullable: true - delegate: - $ref: '#/components/schemas/SerializablePubkey' - limit: - allOf: - - $ref: '#/components/schemas/Limit' - nullable: true - mint: - allOf: - - $ref: '#/components/schemas/SerializablePubkey' - nullable: true - additionalProperties: false - required: true - responses: - '200': - description: '' - content: - application/json: - schema: - type: object - required: - - context - - value - properties: - context: - $ref: '#/components/schemas/Context' - value: - $ref: '#/components/schemas/TokenAccountList' - '400': - description: Invalid request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '401': - description: Unauthorized request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '403': - description: Request was forbidden. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '404': - description: The specified resource was not found. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '429': - description: Exceeded rate limit. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '500': - description: The server encountered an unexpected condition that prevented it from fulfilling the request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - /getCompressedTokenAccountsByOwner: - summary: getCompressedTokenAccountsByOwner - post: - requestBody: - content: - application/json: - schema: - type: object - required: - - jsonrpc - - id - - method - - params - properties: - id: - type: string - description: An ID to identify the request. - enum: - - test-account - jsonrpc: - type: string - description: The version of the JSON-RPC protocol. - enum: - - '2.0' - method: - type: string - description: The name of the method to invoke. - enum: - - getCompressedTokenAccountsByOwner - params: - type: object - required: - - owner - properties: - cursor: - allOf: - - $ref: '#/components/schemas/Base58String' - nullable: true - limit: - allOf: - - $ref: '#/components/schemas/Limit' - nullable: true - mint: - allOf: - - $ref: '#/components/schemas/SerializablePubkey' - nullable: true - owner: - $ref: '#/components/schemas/SerializablePubkey' - additionalProperties: false - required: true - responses: - '200': - description: '' - content: - application/json: - schema: - type: object - required: - - context - - value - properties: - context: - $ref: '#/components/schemas/Context' - value: - $ref: '#/components/schemas/TokenAccountList' - '400': - description: Invalid request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '401': - description: Unauthorized request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '403': - description: Request was forbidden. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '404': - description: The specified resource was not found. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '429': - description: Exceeded rate limit. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '500': - description: The server encountered an unexpected condition that prevented it from fulfilling the request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - /getMultipleCompressedAccountProofs: - summary: getMultipleCompressedAccountProofs - post: - requestBody: - content: - application/json: - schema: - type: object - required: - - jsonrpc - - id - - method - - params - properties: - id: - type: string - description: An ID to identify the request. - enum: - - test-account - jsonrpc: - type: string - description: The version of the JSON-RPC protocol. - enum: - - '2.0' - method: - type: string - description: The name of the method to invoke. - enum: - - getMultipleCompressedAccountProofs - params: - type: array - items: - $ref: '#/components/schemas/Hash' - required: true - responses: - '200': - description: '' - content: - application/json: - schema: - type: object - required: - - context - - value - properties: - context: - $ref: '#/components/schemas/Context' - value: - type: array - items: - $ref: '#/components/schemas/MerkleProofWithContext' - '400': - description: Invalid request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '401': - description: Unauthorized request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '403': - description: Request was forbidden. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '404': - description: The specified resource was not found. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '429': - description: Exceeded rate limit. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '500': - description: The server encountered an unexpected condition that prevented it from fulfilling the request. - content: - application/json: - schema: - type: object - properties: - error: - type: string -components: - schemas: - Account: - type: object - required: - - hash - - owner - - lamports - - tree - - leafIndex - - seq - - slotCreated - properties: - address: - $ref: '#/components/schemas/SerializablePubkey' - data: - $ref: '#/components/schemas/AccountData' - hash: - $ref: '#/components/schemas/Hash' - lamports: - $ref: '#/components/schemas/UnsignedInteger' - leafIndex: - $ref: '#/components/schemas/UnsignedInteger' - owner: - $ref: '#/components/schemas/SerializablePubkey' - seq: - $ref: '#/components/schemas/UnsignedInteger' - slotCreated: - $ref: '#/components/schemas/UnsignedInteger' - tree: - $ref: '#/components/schemas/SerializablePubkey' - additionalProperties: false - AccountData: - type: object - required: - - discriminator - - data - - dataHash - properties: - data: - $ref: '#/components/schemas/Base64String' - dataHash: - $ref: '#/components/schemas/Hash' - discriminator: - $ref: '#/components/schemas/UnsignedInteger' - additionalProperties: false - Base58String: - type: string - description: A base 58 encoded string. - default: 3J98t1WpEZ73CNm - example: 3J98t1WpEZ73CNm - Base64String: - type: string - description: A base 64 encoded string. - default: SGVsbG8sIFdvcmxkIQ== - example: SGVsbG8sIFdvcmxkIQ== - Context: - type: object - required: - - slot - properties: - slot: - type: integer - default: 100 - example: 100 - DataSlice: - type: object - required: - - offset - - length - properties: - length: - type: integer - minimum: 0 - offset: - type: integer - minimum: 0 - FilterSelector: - type: object - properties: - memcmp: - $ref: '#/components/schemas/Memcmp' - Hash: - type: string - description: A 32-byte hash represented as a base58 string. - example: 11111112cMQwSC9qirWGjZM6gLGwW69X22mqwLLGP - Limit: - type: integer - format: int64 - minimum: 0 - Memcmp: - type: object - required: - - offset - - bytes - properties: - bytes: - $ref: '#/components/schemas/Base58String' - offset: - type: integer - minimum: 0 - PaginatedAccountList: - type: object - required: - - items - properties: - cursor: - $ref: '#/components/schemas/Hash' - items: - type: array - items: - $ref: '#/components/schemas/Account' - additionalProperties: false - SerializablePubkey: - type: string - description: A Solana public key represented as a base58 string. - default: 11111114d3RrygbPdAtMuFnDmzsN8T5fYKVQ7FVr7 - example: 11111114d3RrygbPdAtMuFnDmzsN8T5fYKVQ7FVr7 - UnsignedInteger: - type: integer - default: 100 - example: 100 diff --git a/src/openapi/specs/getCompressedBalanceByOwner.yaml b/src/openapi/specs/getCompressedBalanceByOwner.yaml deleted file mode 100644 index 5262bc87..00000000 --- a/src/openapi/specs/getCompressedBalanceByOwner.yaml +++ /dev/null @@ -1,1143 +0,0 @@ -openapi: 3.0.3 -info: - title: photon-indexer - description: Solana indexer for general compression - license: - name: Apache-2.0 - version: 0.50.0 -servers: -- url: https://mainnet.helius-rpc.com?api-key= -paths: - /getCompressedAccount: - summary: getCompressedAccount - post: - requestBody: - content: - application/json: - schema: - type: object - required: - - jsonrpc - - id - - method - - params - properties: - id: - type: string - description: An ID to identify the request. - enum: - - test-account - jsonrpc: - type: string - description: The version of the JSON-RPC protocol. - enum: - - '2.0' - method: - type: string - description: The name of the method to invoke. - enum: - - getCompressedAccount - params: - type: object - description: Request for compressed account data - default: - address: null - hash: '11111111111111111111111111111111' - properties: - address: - allOf: - - $ref: '#/components/schemas/SerializablePubkey' - nullable: true - hash: - allOf: - - $ref: '#/components/schemas/Hash' - nullable: true - additionalProperties: false - example: - address: null - hash: '11111111111111111111111111111111' - required: true - responses: - '200': - description: '' - content: - application/json: - schema: - type: object - required: - - context - - value - properties: - context: - $ref: '#/components/schemas/Context' - value: - $ref: '#/components/schemas/Account' - '400': - description: Invalid request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '401': - description: Unauthorized request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '403': - description: Request was forbidden. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '404': - description: The specified resource was not found. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '429': - description: Exceeded rate limit. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '500': - description: The server encountered an unexpected condition that prevented it from fulfilling the request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - /getCompressedAccountProof: - summary: getCompressedAccountProof - post: - requestBody: - content: - application/json: - schema: - type: object - required: - - jsonrpc - - id - - method - - params - properties: - id: - type: string - description: An ID to identify the request. - enum: - - test-account - jsonrpc: - type: string - description: The version of the JSON-RPC protocol. - enum: - - '2.0' - method: - type: string - description: The name of the method to invoke. - enum: - - getCompressedAccountProof - params: - type: object - required: - - hash - properties: - hash: - $ref: '#/components/schemas/Hash' - required: true - responses: - '200': - description: '' - content: - application/json: - schema: - type: object - required: - - context - - value - properties: - context: - $ref: '#/components/schemas/Context' - value: - $ref: '#/components/schemas/MerkleProofWithContext' - '400': - description: Invalid request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '401': - description: Unauthorized request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '403': - description: Request was forbidden. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '404': - description: The specified resource was not found. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '429': - description: Exceeded rate limit. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '500': - description: The server encountered an unexpected condition that prevented it from fulfilling the request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - /getCompressedAccountsByOwner: - summary: getCompressedAccountsByOwner - post: - requestBody: - content: - application/json: - schema: - type: object - required: - - jsonrpc - - id - - method - - params - properties: - id: - type: string - description: An ID to identify the request. - enum: - - test-account - jsonrpc: - type: string - description: The version of the JSON-RPC protocol. - enum: - - '2.0' - method: - type: string - description: The name of the method to invoke. - enum: - - getCompressedAccountsByOwner - params: - type: object - required: - - owner - properties: - cursor: - allOf: - - $ref: '#/components/schemas/Hash' - nullable: true - limit: - allOf: - - $ref: '#/components/schemas/Limit' - nullable: true - owner: - $ref: '#/components/schemas/SerializablePubkey' - additionalProperties: false - required: true - responses: - '200': - description: '' - content: - application/json: - schema: - type: object - required: - - context - - value - properties: - context: - $ref: '#/components/schemas/Context' - value: - $ref: '#/components/schemas/PaginatedAccountList' - '400': - description: Invalid request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '401': - description: Unauthorized request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '403': - description: Request was forbidden. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '404': - description: The specified resource was not found. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '429': - description: Exceeded rate limit. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '500': - description: The server encountered an unexpected condition that prevented it from fulfilling the request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - /getCompressedBalance: - summary: getCompressedBalance - post: - requestBody: - content: - application/json: - schema: - type: object - required: - - jsonrpc - - id - - method - - params - properties: - id: - type: string - description: An ID to identify the request. - enum: - - test-account - jsonrpc: - type: string - description: The version of the JSON-RPC protocol. - enum: - - '2.0' - method: - type: string - description: The name of the method to invoke. - enum: - - getCompressedBalance - params: - type: object - description: Request for compressed account data - default: - address: null - hash: '11111111111111111111111111111111' - properties: - address: - allOf: - - $ref: '#/components/schemas/SerializablePubkey' - nullable: true - hash: - allOf: - - $ref: '#/components/schemas/Hash' - nullable: true - additionalProperties: false - example: - address: null - hash: '11111111111111111111111111111111' - required: true - responses: - '200': - description: '' - content: - application/json: - schema: - type: object - required: - - context - - value - properties: - context: - $ref: '#/components/schemas/Context' - value: - $ref: '#/components/schemas/UnsignedInteger' - '400': - description: Invalid request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '401': - description: Unauthorized request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '403': - description: Request was forbidden. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '404': - description: The specified resource was not found. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '429': - description: Exceeded rate limit. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '500': - description: The server encountered an unexpected condition that prevented it from fulfilling the request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - /getCompressedBalanceByOwner: - summary: getCompressedBalanceByOwner - post: - requestBody: - content: - application/json: - schema: - type: object - required: - - jsonrpc - - id - - method - - params - properties: - id: - type: string - description: An ID to identify the request. - enum: - - test-account - jsonrpc: - type: string - description: The version of the JSON-RPC protocol. - enum: - - '2.0' - method: - type: string - description: The name of the method to invoke. - enum: - - getCompressedBalanceByOwner - params: - type: object - required: - - owner - properties: - owner: - $ref: '#/components/schemas/SerializablePubkey' - required: true - responses: - '200': - description: '' - content: - application/json: - schema: - type: object - required: - - context - - value - properties: - context: - $ref: '#/components/schemas/Context' - value: - $ref: '#/components/schemas/UnsignedInteger' - additionalProperties: false - '429': - description: Exceeded rate limit. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '500': - description: The server encountered an unexpected condition that prevented it from fulfilling the request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - /getCompressedTokenAccountBalance: - summary: getCompressedTokenAccountBalance - post: - requestBody: - content: - application/json: - schema: - type: object - required: - - jsonrpc - - id - - method - - params - properties: - id: - type: string - description: An ID to identify the request. - enum: - - test-account - jsonrpc: - type: string - description: The version of the JSON-RPC protocol. - enum: - - '2.0' - method: - type: string - description: The name of the method to invoke. - enum: - - getCompressedTokenAccountBalance - params: - type: object - description: Request for compressed account data - default: - address: null - hash: '11111111111111111111111111111111' - properties: - address: - allOf: - - $ref: '#/components/schemas/SerializablePubkey' - nullable: true - hash: - allOf: - - $ref: '#/components/schemas/Hash' - nullable: true - additionalProperties: false - example: - address: null - hash: '11111111111111111111111111111111' - required: true - responses: - '200': - description: '' - content: - application/json: - schema: - type: object - required: - - context - - value - properties: - context: - $ref: '#/components/schemas/Context' - value: - $ref: '#/components/schemas/TokenAccountBalance' - '400': - description: Invalid request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '401': - description: Unauthorized request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '403': - description: Request was forbidden. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '404': - description: The specified resource was not found. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '429': - description: Exceeded rate limit. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '500': - description: The server encountered an unexpected condition that prevented it from fulfilling the request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - /getCompressedTokenAccountsByDelegate: - summary: getCompressedTokenAccountsByDelegate - post: - requestBody: - content: - application/json: - schema: - type: object - required: - - jsonrpc - - id - - method - - params - properties: - id: - type: string - description: An ID to identify the request. - enum: - - test-account - jsonrpc: - type: string - description: The version of the JSON-RPC protocol. - enum: - - '2.0' - method: - type: string - description: The name of the method to invoke. - enum: - - getCompressedTokenAccountsByDelegate - params: - type: object - required: - - delegate - properties: - cursor: - allOf: - - $ref: '#/components/schemas/Base58String' - nullable: true - delegate: - $ref: '#/components/schemas/SerializablePubkey' - limit: - allOf: - - $ref: '#/components/schemas/Limit' - nullable: true - mint: - allOf: - - $ref: '#/components/schemas/SerializablePubkey' - nullable: true - additionalProperties: false - required: true - responses: - '200': - description: '' - content: - application/json: - schema: - type: object - required: - - context - - value - properties: - context: - $ref: '#/components/schemas/Context' - value: - $ref: '#/components/schemas/TokenAccountList' - '400': - description: Invalid request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '401': - description: Unauthorized request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '403': - description: Request was forbidden. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '404': - description: The specified resource was not found. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '429': - description: Exceeded rate limit. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '500': - description: The server encountered an unexpected condition that prevented it from fulfilling the request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - /getCompressedTokenAccountsByOwner: - summary: getCompressedTokenAccountsByOwner - post: - requestBody: - content: - application/json: - schema: - type: object - required: - - jsonrpc - - id - - method - - params - properties: - id: - type: string - description: An ID to identify the request. - enum: - - test-account - jsonrpc: - type: string - description: The version of the JSON-RPC protocol. - enum: - - '2.0' - method: - type: string - description: The name of the method to invoke. - enum: - - getCompressedTokenAccountsByOwner - params: - type: object - required: - - owner - properties: - cursor: - allOf: - - $ref: '#/components/schemas/Base58String' - nullable: true - limit: - allOf: - - $ref: '#/components/schemas/Limit' - nullable: true - mint: - allOf: - - $ref: '#/components/schemas/SerializablePubkey' - nullable: true - owner: - $ref: '#/components/schemas/SerializablePubkey' - additionalProperties: false - required: true - responses: - '200': - description: '' - content: - application/json: - schema: - type: object - required: - - context - - value - properties: - context: - $ref: '#/components/schemas/Context' - value: - $ref: '#/components/schemas/TokenAccountList' - '400': - description: Invalid request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '401': - description: Unauthorized request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '403': - description: Request was forbidden. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '404': - description: The specified resource was not found. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '429': - description: Exceeded rate limit. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '500': - description: The server encountered an unexpected condition that prevented it from fulfilling the request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - /getMultipleCompressedAccountProofs: - summary: getMultipleCompressedAccountProofs - post: - requestBody: - content: - application/json: - schema: - type: object - required: - - jsonrpc - - id - - method - - params - properties: - id: - type: string - description: An ID to identify the request. - enum: - - test-account - jsonrpc: - type: string - description: The version of the JSON-RPC protocol. - enum: - - '2.0' - method: - type: string - description: The name of the method to invoke. - enum: - - getMultipleCompressedAccountProofs - params: - type: array - items: - $ref: '#/components/schemas/Hash' - required: true - responses: - '200': - description: '' - content: - application/json: - schema: - type: object - required: - - context - - value - properties: - context: - $ref: '#/components/schemas/Context' - value: - type: array - items: - $ref: '#/components/schemas/MerkleProofWithContext' - '400': - description: Invalid request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '401': - description: Unauthorized request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '403': - description: Request was forbidden. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '404': - description: The specified resource was not found. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '429': - description: Exceeded rate limit. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '500': - description: The server encountered an unexpected condition that prevented it from fulfilling the request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - /getMultipleCompressedAccounts: - summary: getMultipleCompressedAccounts - post: - requestBody: - content: - application/json: - schema: - type: object - required: - - jsonrpc - - id - - method - - params - properties: - id: - type: string - description: An ID to identify the request. - enum: - - test-account - jsonrpc: - type: string - description: The version of the JSON-RPC protocol. - enum: - - '2.0' - method: - type: string - description: The name of the method to invoke. - enum: - - getMultipleCompressedAccounts - params: - type: object - description: Request for compressed account data - default: - addresses: null - hashes: - - 1111111QLbz7JHiBTspS962RLKV8GndWFwiEaqKM - - 1111111ogCyDbaRMvkdsHB3qfdyFYaG1WtRUAfdh - properties: - addresses: - type: array - items: - $ref: '#/components/schemas/SerializablePubkey' - nullable: true - hashes: - type: array - items: - $ref: '#/components/schemas/Hash' - nullable: true - additionalProperties: false - example: - addresses: null - hashes: - - 1111111QLbz7JHiBTspS962RLKV8GndWFwiEaqKM - - 1111111ogCyDbaRMvkdsHB3qfdyFYaG1WtRUAfdh - required: true - responses: - '200': - description: '' - content: - application/json: - schema: - type: object - required: - - context - - value - properties: - context: - $ref: '#/components/schemas/Context' - value: - $ref: '#/components/schemas/AccountList' - '400': - description: Invalid request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '401': - description: Unauthorized request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '403': - description: Request was forbidden. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '404': - description: The specified resource was not found. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '429': - description: Exceeded rate limit. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '500': - description: The server encountered an unexpected condition that prevented it from fulfilling the request. - content: - application/json: - schema: - type: object - properties: - error: - type: string -components: - schemas: - Context: - type: object - required: - - slot - properties: - slot: - type: integer - default: 100 - example: 100 - SerializablePubkey: - type: string - description: A Solana public key represented as a base58 string. - default: 11111113R2cuenjG5nFubqX9Wzuukdin2YfGQVzu5 - example: 11111113R2cuenjG5nFubqX9Wzuukdin2YfGQVzu5 - UnsignedInteger: - type: integer - default: 100 - example: 100 diff --git a/src/openapi/specs/getCompressedMintTokenHolders.yaml b/src/openapi/specs/getCompressedMintTokenHolders.yaml deleted file mode 100644 index dcc1ad4a..00000000 --- a/src/openapi/specs/getCompressedMintTokenHolders.yaml +++ /dev/null @@ -1,140 +0,0 @@ -openapi: 3.0.3 -info: - title: photon-indexer - description: Solana indexer for general compression - license: - name: Apache-2.0 - version: 0.50.0 -servers: -- url: https://mainnet.helius-rpc.com?api-key= -paths: - /: - summary: getCompressedMintTokenHolders - post: - requestBody: - content: - application/json: - schema: - type: object - required: - - jsonrpc - - id - - method - - params - properties: - id: - type: string - description: An ID to identify the request. - enum: - - test-account - jsonrpc: - type: string - description: The version of the JSON-RPC protocol. - enum: - - '2.0' - method: - type: string - description: The name of the method to invoke. - enum: - - getCompressedMintTokenHolders - params: - type: object - required: - - mint - properties: - cursor: - allOf: - - $ref: '#/components/schemas/Base58String' - nullable: true - limit: - allOf: - - $ref: '#/components/schemas/Limit' - nullable: true - mint: - $ref: '#/components/schemas/SerializablePubkey' - additionalProperties: false - required: true - responses: - '200': - description: '' - content: - application/json: - schema: - type: object - required: - - context - - value - properties: - context: - $ref: '#/components/schemas/Context' - value: - $ref: '#/components/schemas/OwnerBalanceList' - additionalProperties: false - '429': - description: Exceeded rate limit. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '500': - description: The server encountered an unexpected condition that prevented it from fulfilling the request. - content: - application/json: - schema: - type: object - properties: - error: - type: string -components: - schemas: - Base58String: - type: string - description: A base 58 encoded string. - default: 3J98t1WpEZ73CNm - example: 3J98t1WpEZ73CNm - Context: - type: object - required: - - slot - properties: - slot: - type: integer - default: 100 - example: 100 - Limit: - type: integer - format: int64 - minimum: 0 - OwnerBalance: - type: object - required: - - owner - - balance - properties: - balance: - $ref: '#/components/schemas/UnsignedInteger' - owner: - $ref: '#/components/schemas/SerializablePubkey' - OwnerBalanceList: - type: object - required: - - items - properties: - cursor: - $ref: '#/components/schemas/Base58String' - items: - type: array - items: - $ref: '#/components/schemas/OwnerBalance' - SerializablePubkey: - type: string - description: A Solana public key represented as a base58 string. - default: 111111152P2r5yt6odmBLPsFCLBrFisJ3aS7LqLAT - example: 111111152P2r5yt6odmBLPsFCLBrFisJ3aS7LqLAT - UnsignedInteger: - type: integer - default: 100 - example: 100 diff --git a/src/openapi/specs/getCompressedTokenAccountBalance.yaml b/src/openapi/specs/getCompressedTokenAccountBalance.yaml deleted file mode 100644 index e7e98873..00000000 --- a/src/openapi/specs/getCompressedTokenAccountBalance.yaml +++ /dev/null @@ -1,931 +0,0 @@ -openapi: 3.0.3 -info: - title: photon-indexer - description: Solana indexer for general compression - license: - name: Apache-2.0 - version: 0.50.0 -servers: -- url: https://mainnet.helius-rpc.com?api-key= -paths: - /getCompressedAccount: - summary: getCompressedAccount - post: - requestBody: - content: - application/json: - schema: - type: object - required: - - jsonrpc - - id - - method - - params - properties: - id: - type: string - description: An ID to identify the request. - enum: - - test-account - jsonrpc: - type: string - description: The version of the JSON-RPC protocol. - enum: - - '2.0' - method: - type: string - description: The name of the method to invoke. - enum: - - getCompressedAccount - params: - type: object - description: Request for compressed account data - default: - address: null - hash: '11111111111111111111111111111111' - properties: - address: - allOf: - - $ref: '#/components/schemas/SerializablePubkey' - nullable: true - hash: - allOf: - - $ref: '#/components/schemas/Hash' - nullable: true - additionalProperties: false - example: - address: null - hash: '11111111111111111111111111111111' - required: true - responses: - '200': - description: '' - content: - application/json: - schema: - type: object - required: - - context - - value - properties: - context: - $ref: '#/components/schemas/Context' - value: - $ref: '#/components/schemas/Account' - '400': - description: Invalid request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '401': - description: Unauthorized request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '403': - description: Request was forbidden. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '404': - description: The specified resource was not found. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '429': - description: Exceeded rate limit. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '500': - description: The server encountered an unexpected condition that prevented it from fulfilling the request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - /getCompressedAccountProof: - summary: getCompressedAccountProof - post: - requestBody: - content: - application/json: - schema: - type: object - required: - - jsonrpc - - id - - method - - params - properties: - id: - type: string - description: An ID to identify the request. - enum: - - test-account - jsonrpc: - type: string - description: The version of the JSON-RPC protocol. - enum: - - '2.0' - method: - type: string - description: The name of the method to invoke. - enum: - - getCompressedAccountProof - params: - type: object - required: - - hash - properties: - hash: - $ref: '#/components/schemas/Hash' - required: true - responses: - '200': - description: '' - content: - application/json: - schema: - type: object - required: - - context - - value - properties: - context: - $ref: '#/components/schemas/Context' - value: - $ref: '#/components/schemas/MerkleProofWithContext' - '400': - description: Invalid request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '401': - description: Unauthorized request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '403': - description: Request was forbidden. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '404': - description: The specified resource was not found. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '429': - description: Exceeded rate limit. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '500': - description: The server encountered an unexpected condition that prevented it from fulfilling the request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - /getCompressedAccountsByOwner: - summary: getCompressedAccountsByOwner - post: - requestBody: - content: - application/json: - schema: - type: object - required: - - jsonrpc - - id - - method - - params - properties: - id: - type: string - description: An ID to identify the request. - enum: - - test-account - jsonrpc: - type: string - description: The version of the JSON-RPC protocol. - enum: - - '2.0' - method: - type: string - description: The name of the method to invoke. - enum: - - getCompressedAccountsByOwner - params: - type: object - required: - - owner - properties: - cursor: - allOf: - - $ref: '#/components/schemas/Hash' - nullable: true - limit: - allOf: - - $ref: '#/components/schemas/Limit' - nullable: true - owner: - $ref: '#/components/schemas/SerializablePubkey' - additionalProperties: false - required: true - responses: - '200': - description: '' - content: - application/json: - schema: - type: object - required: - - context - - value - properties: - context: - $ref: '#/components/schemas/Context' - value: - $ref: '#/components/schemas/PaginatedAccountList' - '400': - description: Invalid request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '401': - description: Unauthorized request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '403': - description: Request was forbidden. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '404': - description: The specified resource was not found. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '429': - description: Exceeded rate limit. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '500': - description: The server encountered an unexpected condition that prevented it from fulfilling the request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - /getCompressedTokenAccountBalance: - summary: getCompressedTokenAccountBalance - post: - requestBody: - content: - application/json: - schema: - type: object - required: - - jsonrpc - - id - - method - - params - properties: - id: - type: string - description: An ID to identify the request. - enum: - - test-account - jsonrpc: - type: string - description: The version of the JSON-RPC protocol. - enum: - - '2.0' - method: - type: string - description: The name of the method to invoke. - enum: - - getCompressedTokenAccountBalance - params: - type: object - description: Request for compressed account data - default: - address: null - hash: '11111111111111111111111111111111' - properties: - address: - allOf: - - $ref: '#/components/schemas/SerializablePubkey' - nullable: true - hash: - allOf: - - $ref: '#/components/schemas/Hash' - nullable: true - additionalProperties: false - example: - address: null - hash: '11111111111111111111111111111111' - required: true - responses: - '200': - description: '' - content: - application/json: - schema: - type: object - required: - - context - - value - properties: - context: - $ref: '#/components/schemas/Context' - value: - $ref: '#/components/schemas/TokenAccountBalance' - additionalProperties: false - '429': - description: Exceeded rate limit. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '500': - description: The server encountered an unexpected condition that prevented it from fulfilling the request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - /getCompressedTokenAccountsByDelegate: - summary: getCompressedTokenAccountsByDelegate - post: - requestBody: - content: - application/json: - schema: - type: object - required: - - jsonrpc - - id - - method - - params - properties: - id: - type: string - description: An ID to identify the request. - enum: - - test-account - jsonrpc: - type: string - description: The version of the JSON-RPC protocol. - enum: - - '2.0' - method: - type: string - description: The name of the method to invoke. - enum: - - getCompressedTokenAccountsByDelegate - params: - type: object - required: - - delegate - properties: - cursor: - allOf: - - $ref: '#/components/schemas/Base58String' - nullable: true - delegate: - $ref: '#/components/schemas/SerializablePubkey' - limit: - allOf: - - $ref: '#/components/schemas/Limit' - nullable: true - mint: - allOf: - - $ref: '#/components/schemas/SerializablePubkey' - nullable: true - additionalProperties: false - required: true - responses: - '200': - description: '' - content: - application/json: - schema: - type: object - required: - - context - - value - properties: - context: - $ref: '#/components/schemas/Context' - value: - $ref: '#/components/schemas/TokenAccountList' - '400': - description: Invalid request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '401': - description: Unauthorized request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '403': - description: Request was forbidden. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '404': - description: The specified resource was not found. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '429': - description: Exceeded rate limit. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '500': - description: The server encountered an unexpected condition that prevented it from fulfilling the request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - /getCompressedTokenAccountsByOwner: - summary: getCompressedTokenAccountsByOwner - post: - requestBody: - content: - application/json: - schema: - type: object - required: - - jsonrpc - - id - - method - - params - properties: - id: - type: string - description: An ID to identify the request. - enum: - - test-account - jsonrpc: - type: string - description: The version of the JSON-RPC protocol. - enum: - - '2.0' - method: - type: string - description: The name of the method to invoke. - enum: - - getCompressedTokenAccountsByOwner - params: - type: object - required: - - owner - properties: - cursor: - allOf: - - $ref: '#/components/schemas/Base58String' - nullable: true - limit: - allOf: - - $ref: '#/components/schemas/Limit' - nullable: true - mint: - allOf: - - $ref: '#/components/schemas/SerializablePubkey' - nullable: true - owner: - $ref: '#/components/schemas/SerializablePubkey' - additionalProperties: false - required: true - responses: - '200': - description: '' - content: - application/json: - schema: - type: object - required: - - context - - value - properties: - context: - $ref: '#/components/schemas/Context' - value: - $ref: '#/components/schemas/TokenAccountList' - '400': - description: Invalid request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '401': - description: Unauthorized request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '403': - description: Request was forbidden. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '404': - description: The specified resource was not found. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '429': - description: Exceeded rate limit. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '500': - description: The server encountered an unexpected condition that prevented it from fulfilling the request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - /getMultipleCompressedAccountProofs: - summary: getMultipleCompressedAccountProofs - post: - requestBody: - content: - application/json: - schema: - type: object - required: - - jsonrpc - - id - - method - - params - properties: - id: - type: string - description: An ID to identify the request. - enum: - - test-account - jsonrpc: - type: string - description: The version of the JSON-RPC protocol. - enum: - - '2.0' - method: - type: string - description: The name of the method to invoke. - enum: - - getMultipleCompressedAccountProofs - params: - type: array - items: - $ref: '#/components/schemas/Hash' - required: true - responses: - '200': - description: '' - content: - application/json: - schema: - type: object - required: - - context - - value - properties: - context: - $ref: '#/components/schemas/Context' - value: - type: array - items: - $ref: '#/components/schemas/MerkleProofWithContext' - '400': - description: Invalid request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '401': - description: Unauthorized request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '403': - description: Request was forbidden. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '404': - description: The specified resource was not found. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '429': - description: Exceeded rate limit. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '500': - description: The server encountered an unexpected condition that prevented it from fulfilling the request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - /getMultipleCompressedAccounts: - summary: getMultipleCompressedAccounts - post: - requestBody: - content: - application/json: - schema: - type: object - required: - - jsonrpc - - id - - method - - params - properties: - id: - type: string - description: An ID to identify the request. - enum: - - test-account - jsonrpc: - type: string - description: The version of the JSON-RPC protocol. - enum: - - '2.0' - method: - type: string - description: The name of the method to invoke. - enum: - - getMultipleCompressedAccounts - params: - type: object - description: Request for compressed account data - default: - addresses: null - hashes: - - 1111111QLbz7JHiBTspS962RLKV8GndWFwiEaqKM - - 1111111ogCyDbaRMvkdsHB3qfdyFYaG1WtRUAfdh - properties: - addresses: - type: array - items: - $ref: '#/components/schemas/SerializablePubkey' - nullable: true - hashes: - type: array - items: - $ref: '#/components/schemas/Hash' - nullable: true - additionalProperties: false - example: - addresses: null - hashes: - - 1111111QLbz7JHiBTspS962RLKV8GndWFwiEaqKM - - 1111111ogCyDbaRMvkdsHB3qfdyFYaG1WtRUAfdh - required: true - responses: - '200': - description: '' - content: - application/json: - schema: - type: object - required: - - context - - value - properties: - context: - $ref: '#/components/schemas/Context' - value: - $ref: '#/components/schemas/AccountList' - '400': - description: Invalid request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '401': - description: Unauthorized request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '403': - description: Request was forbidden. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '404': - description: The specified resource was not found. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '429': - description: Exceeded rate limit. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '500': - description: The server encountered an unexpected condition that prevented it from fulfilling the request. - content: - application/json: - schema: - type: object - properties: - error: - type: string -components: - schemas: - Context: - type: object - required: - - slot - properties: - slot: - type: integer - default: 100 - example: 100 - Hash: - type: string - description: A 32-byte hash represented as a base58 string. - example: 11111112cMQwSC9qirWGjZM6gLGwW69X22mqwLLGP - SerializablePubkey: - type: string - description: A Solana public key represented as a base58 string. - default: 111111131h1vYVSYuKP6AhS86fbRdMw9XHiZAvAaj - example: 111111131h1vYVSYuKP6AhS86fbRdMw9XHiZAvAaj - TokenAccountBalance: - type: object - required: - - amount - properties: - amount: - $ref: '#/components/schemas/UnsignedInteger' - additionalProperties: false - UnsignedInteger: - type: integer - default: 100 - example: 100 diff --git a/src/openapi/specs/getCompressedTokenAccountsByDelegate.yaml b/src/openapi/specs/getCompressedTokenAccountsByDelegate.yaml deleted file mode 100644 index 9f1e56d6..00000000 --- a/src/openapi/specs/getCompressedTokenAccountsByDelegate.yaml +++ /dev/null @@ -1,671 +0,0 @@ -openapi: 3.0.3 -info: - title: photon-indexer - description: Solana indexer for general compression - license: - name: Apache-2.0 - version: 0.50.0 -servers: -- url: https://mainnet.helius-rpc.com?api-key= -paths: - /getCompressedAccount: - summary: getCompressedAccount - post: - requestBody: - content: - application/json: - schema: - type: object - required: - - jsonrpc - - id - - method - - params - properties: - id: - type: string - description: An ID to identify the request. - enum: - - test-account - jsonrpc: - type: string - description: The version of the JSON-RPC protocol. - enum: - - '2.0' - method: - type: string - description: The name of the method to invoke. - enum: - - getCompressedAccount - params: - type: object - description: Request for compressed account data - default: - address: null - hash: '11111111111111111111111111111111' - properties: - address: - allOf: - - $ref: '#/components/schemas/SerializablePubkey' - nullable: true - hash: - allOf: - - $ref: '#/components/schemas/Hash' - nullable: true - additionalProperties: false - example: - address: null - hash: '11111111111111111111111111111111' - required: true - responses: - '200': - description: '' - content: - application/json: - schema: - type: object - required: - - context - - value - properties: - context: - $ref: '#/components/schemas/Context' - value: - $ref: '#/components/schemas/Account' - '400': - description: Invalid request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '401': - description: Unauthorized request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '403': - description: Request was forbidden. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '404': - description: The specified resource was not found. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '429': - description: Exceeded rate limit. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '500': - description: The server encountered an unexpected condition that prevented it from fulfilling the request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - /getCompressedAccountProof: - summary: getCompressedAccountProof - post: - requestBody: - content: - application/json: - schema: - type: object - required: - - jsonrpc - - id - - method - - params - properties: - id: - type: string - description: An ID to identify the request. - enum: - - test-account - jsonrpc: - type: string - description: The version of the JSON-RPC protocol. - enum: - - '2.0' - method: - type: string - description: The name of the method to invoke. - enum: - - getCompressedAccountProof - params: - type: object - required: - - hash - properties: - hash: - $ref: '#/components/schemas/Hash' - required: true - responses: - '200': - description: '' - content: - application/json: - schema: - type: object - required: - - context - - value - properties: - context: - $ref: '#/components/schemas/Context' - value: - $ref: '#/components/schemas/MerkleProofWithContext' - '400': - description: Invalid request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '401': - description: Unauthorized request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '403': - description: Request was forbidden. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '404': - description: The specified resource was not found. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '429': - description: Exceeded rate limit. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '500': - description: The server encountered an unexpected condition that prevented it from fulfilling the request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - /getCompressedTokenAccountsByDelegate: - summary: getCompressedTokenAccountsByDelegate - post: - requestBody: - content: - application/json: - schema: - type: object - required: - - jsonrpc - - id - - method - - params - properties: - id: - type: string - description: An ID to identify the request. - enum: - - test-account - jsonrpc: - type: string - description: The version of the JSON-RPC protocol. - enum: - - '2.0' - method: - type: string - description: The name of the method to invoke. - enum: - - getCompressedTokenAccountsByDelegate - params: - type: object - required: - - delegate - properties: - cursor: - allOf: - - $ref: '#/components/schemas/Base58String' - nullable: true - delegate: - $ref: '#/components/schemas/SerializablePubkey' - limit: - allOf: - - $ref: '#/components/schemas/Limit' - nullable: true - mint: - allOf: - - $ref: '#/components/schemas/SerializablePubkey' - nullable: true - additionalProperties: false - required: true - responses: - '200': - description: '' - content: - application/json: - schema: - type: object - required: - - context - - value - properties: - context: - $ref: '#/components/schemas/Context' - value: - $ref: '#/components/schemas/TokenAccountList' - additionalProperties: false - '429': - description: Exceeded rate limit. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '500': - description: The server encountered an unexpected condition that prevented it from fulfilling the request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - /getCompressedTokenAccountsByOwner: - summary: getCompressedTokenAccountsByOwner - post: - requestBody: - content: - application/json: - schema: - type: object - required: - - jsonrpc - - id - - method - - params - properties: - id: - type: string - description: An ID to identify the request. - enum: - - test-account - jsonrpc: - type: string - description: The version of the JSON-RPC protocol. - enum: - - '2.0' - method: - type: string - description: The name of the method to invoke. - enum: - - getCompressedTokenAccountsByOwner - params: - type: object - required: - - owner - properties: - cursor: - allOf: - - $ref: '#/components/schemas/Base58String' - nullable: true - limit: - allOf: - - $ref: '#/components/schemas/Limit' - nullable: true - mint: - allOf: - - $ref: '#/components/schemas/SerializablePubkey' - nullable: true - owner: - $ref: '#/components/schemas/SerializablePubkey' - additionalProperties: false - required: true - responses: - '200': - description: '' - content: - application/json: - schema: - type: object - required: - - context - - value - properties: - context: - $ref: '#/components/schemas/Context' - value: - $ref: '#/components/schemas/TokenAccountList' - '400': - description: Invalid request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '401': - description: Unauthorized request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '403': - description: Request was forbidden. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '404': - description: The specified resource was not found. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '429': - description: Exceeded rate limit. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '500': - description: The server encountered an unexpected condition that prevented it from fulfilling the request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - /getMultipleCompressedAccountProofs: - summary: getMultipleCompressedAccountProofs - post: - requestBody: - content: - application/json: - schema: - type: object - required: - - jsonrpc - - id - - method - - params - properties: - id: - type: string - description: An ID to identify the request. - enum: - - test-account - jsonrpc: - type: string - description: The version of the JSON-RPC protocol. - enum: - - '2.0' - method: - type: string - description: The name of the method to invoke. - enum: - - getMultipleCompressedAccountProofs - params: - type: array - items: - $ref: '#/components/schemas/Hash' - required: true - responses: - '200': - description: '' - content: - application/json: - schema: - type: object - required: - - context - - value - properties: - context: - $ref: '#/components/schemas/Context' - value: - type: array - items: - $ref: '#/components/schemas/MerkleProofWithContext' - '400': - description: Invalid request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '401': - description: Unauthorized request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '403': - description: Request was forbidden. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '404': - description: The specified resource was not found. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '429': - description: Exceeded rate limit. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '500': - description: The server encountered an unexpected condition that prevented it from fulfilling the request. - content: - application/json: - schema: - type: object - properties: - error: - type: string -components: - schemas: - Account: - type: object - required: - - hash - - owner - - lamports - - tree - - leafIndex - - seq - - slotCreated - properties: - address: - $ref: '#/components/schemas/SerializablePubkey' - data: - $ref: '#/components/schemas/AccountData' - hash: - $ref: '#/components/schemas/Hash' - lamports: - $ref: '#/components/schemas/UnsignedInteger' - leafIndex: - $ref: '#/components/schemas/UnsignedInteger' - owner: - $ref: '#/components/schemas/SerializablePubkey' - seq: - $ref: '#/components/schemas/UnsignedInteger' - slotCreated: - $ref: '#/components/schemas/UnsignedInteger' - tree: - $ref: '#/components/schemas/SerializablePubkey' - additionalProperties: false - AccountData: - type: object - required: - - discriminator - - data - - dataHash - properties: - data: - $ref: '#/components/schemas/Base64String' - dataHash: - $ref: '#/components/schemas/Hash' - discriminator: - $ref: '#/components/schemas/UnsignedInteger' - additionalProperties: false - AccountState: - type: string - enum: - - initialized - - frozen - Base58String: - type: string - description: A base 58 encoded string. - default: 3J98t1WpEZ73CNm - example: 3J98t1WpEZ73CNm - Base64String: - type: string - description: A base 64 encoded string. - default: SGVsbG8sIFdvcmxkIQ== - example: SGVsbG8sIFdvcmxkIQ== - Context: - type: object - required: - - slot - properties: - slot: - type: integer - default: 100 - example: 100 - Hash: - type: string - description: A 32-byte hash represented as a base58 string. - example: 11111112cMQwSC9qirWGjZM6gLGwW69X22mqwLLGP - Limit: - type: integer - format: int64 - minimum: 0 - SerializablePubkey: - type: string - description: A Solana public key represented as a base58 string. - default: 11111116EPqoQskEM2Pddp8KTL9JdYEBZMGF3aq7V - example: 11111116EPqoQskEM2Pddp8KTL9JdYEBZMGF3aq7V - TokenAcccount: - type: object - required: - - account - - tokenData - properties: - account: - $ref: '#/components/schemas/Account' - tokenData: - $ref: '#/components/schemas/TokenData' - additionalProperties: false - TokenAccountList: - type: object - required: - - items - properties: - cursor: - $ref: '#/components/schemas/Base58String' - items: - type: array - items: - $ref: '#/components/schemas/TokenAcccount' - TokenData: - type: object - required: - - mint - - owner - - amount - - state - properties: - amount: - $ref: '#/components/schemas/UnsignedInteger' - delegate: - $ref: '#/components/schemas/SerializablePubkey' - mint: - $ref: '#/components/schemas/SerializablePubkey' - owner: - $ref: '#/components/schemas/SerializablePubkey' - state: - $ref: '#/components/schemas/AccountState' - tlv: - $ref: '#/components/schemas/Base64String' - UnsignedInteger: - type: integer - default: 100 - example: 100 diff --git a/src/openapi/specs/getCompressedTokenAccountsByOwner.yaml b/src/openapi/specs/getCompressedTokenAccountsByOwner.yaml deleted file mode 100644 index a7ec38b5..00000000 --- a/src/openapi/specs/getCompressedTokenAccountsByOwner.yaml +++ /dev/null @@ -1,552 +0,0 @@ -openapi: 3.0.3 -info: - title: photon-indexer - description: Solana indexer for general compression - license: - name: Apache-2.0 - version: 0.50.0 -servers: -- url: https://mainnet.helius-rpc.com?api-key= -paths: - /getCompressedAccount: - summary: getCompressedAccount - post: - requestBody: - content: - application/json: - schema: - type: object - required: - - jsonrpc - - id - - method - - params - properties: - id: - type: string - description: An ID to identify the request. - enum: - - test-account - jsonrpc: - type: string - description: The version of the JSON-RPC protocol. - enum: - - '2.0' - method: - type: string - description: The name of the method to invoke. - enum: - - getCompressedAccount - params: - type: object - description: Request for compressed account data - default: - address: null - hash: '11111111111111111111111111111111' - properties: - address: - allOf: - - $ref: '#/components/schemas/SerializablePubkey' - nullable: true - hash: - allOf: - - $ref: '#/components/schemas/Hash' - nullable: true - additionalProperties: false - example: - address: null - hash: '11111111111111111111111111111111' - required: true - responses: - '200': - description: '' - content: - application/json: - schema: - type: object - required: - - context - - value - properties: - context: - $ref: '#/components/schemas/Context' - value: - $ref: '#/components/schemas/Account' - '400': - description: Invalid request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '401': - description: Unauthorized request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '403': - description: Request was forbidden. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '404': - description: The specified resource was not found. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '429': - description: Exceeded rate limit. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '500': - description: The server encountered an unexpected condition that prevented it from fulfilling the request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - /getCompressedAccountProof: - summary: getCompressedAccountProof - post: - requestBody: - content: - application/json: - schema: - type: object - required: - - jsonrpc - - id - - method - - params - properties: - id: - type: string - description: An ID to identify the request. - enum: - - test-account - jsonrpc: - type: string - description: The version of the JSON-RPC protocol. - enum: - - '2.0' - method: - type: string - description: The name of the method to invoke. - enum: - - getCompressedAccountProof - params: - type: object - required: - - hash - properties: - hash: - $ref: '#/components/schemas/Hash' - required: true - responses: - '200': - description: '' - content: - application/json: - schema: - type: object - required: - - context - - value - properties: - context: - $ref: '#/components/schemas/Context' - value: - $ref: '#/components/schemas/MerkleProofWithContext' - '400': - description: Invalid request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '401': - description: Unauthorized request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '403': - description: Request was forbidden. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '404': - description: The specified resource was not found. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '429': - description: Exceeded rate limit. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '500': - description: The server encountered an unexpected condition that prevented it from fulfilling the request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - /getCompressedTokenAccountsByOwner: - summary: getCompressedTokenAccountsByOwner - post: - requestBody: - content: - application/json: - schema: - type: object - required: - - jsonrpc - - id - - method - - params - properties: - id: - type: string - description: An ID to identify the request. - enum: - - test-account - jsonrpc: - type: string - description: The version of the JSON-RPC protocol. - enum: - - '2.0' - method: - type: string - description: The name of the method to invoke. - enum: - - getCompressedTokenAccountsByOwner - params: - type: object - required: - - owner - properties: - cursor: - allOf: - - $ref: '#/components/schemas/Base58String' - nullable: true - limit: - allOf: - - $ref: '#/components/schemas/Limit' - nullable: true - mint: - allOf: - - $ref: '#/components/schemas/SerializablePubkey' - nullable: true - owner: - $ref: '#/components/schemas/SerializablePubkey' - additionalProperties: false - required: true - responses: - '200': - description: '' - content: - application/json: - schema: - type: object - required: - - context - - value - properties: - context: - $ref: '#/components/schemas/Context' - value: - $ref: '#/components/schemas/TokenAccountList' - additionalProperties: false - '429': - description: Exceeded rate limit. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '500': - description: The server encountered an unexpected condition that prevented it from fulfilling the request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - /getMultipleCompressedAccountProofs: - summary: getMultipleCompressedAccountProofs - post: - requestBody: - content: - application/json: - schema: - type: object - required: - - jsonrpc - - id - - method - - params - properties: - id: - type: string - description: An ID to identify the request. - enum: - - test-account - jsonrpc: - type: string - description: The version of the JSON-RPC protocol. - enum: - - '2.0' - method: - type: string - description: The name of the method to invoke. - enum: - - getMultipleCompressedAccountProofs - params: - type: array - items: - $ref: '#/components/schemas/Hash' - required: true - responses: - '200': - description: '' - content: - application/json: - schema: - type: object - required: - - context - - value - properties: - context: - $ref: '#/components/schemas/Context' - value: - type: array - items: - $ref: '#/components/schemas/MerkleProofWithContext' - '400': - description: Invalid request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '401': - description: Unauthorized request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '403': - description: Request was forbidden. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '404': - description: The specified resource was not found. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '429': - description: Exceeded rate limit. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '500': - description: The server encountered an unexpected condition that prevented it from fulfilling the request. - content: - application/json: - schema: - type: object - properties: - error: - type: string -components: - schemas: - Account: - type: object - required: - - hash - - owner - - lamports - - tree - - leafIndex - - seq - - slotCreated - properties: - address: - $ref: '#/components/schemas/SerializablePubkey' - data: - $ref: '#/components/schemas/AccountData' - hash: - $ref: '#/components/schemas/Hash' - lamports: - $ref: '#/components/schemas/UnsignedInteger' - leafIndex: - $ref: '#/components/schemas/UnsignedInteger' - owner: - $ref: '#/components/schemas/SerializablePubkey' - seq: - $ref: '#/components/schemas/UnsignedInteger' - slotCreated: - $ref: '#/components/schemas/UnsignedInteger' - tree: - $ref: '#/components/schemas/SerializablePubkey' - additionalProperties: false - AccountData: - type: object - required: - - discriminator - - data - - dataHash - properties: - data: - $ref: '#/components/schemas/Base64String' - dataHash: - $ref: '#/components/schemas/Hash' - discriminator: - $ref: '#/components/schemas/UnsignedInteger' - additionalProperties: false - AccountState: - type: string - enum: - - initialized - - frozen - Base58String: - type: string - description: A base 58 encoded string. - default: 3J98t1WpEZ73CNm - example: 3J98t1WpEZ73CNm - Base64String: - type: string - description: A base 64 encoded string. - default: SGVsbG8sIFdvcmxkIQ== - example: SGVsbG8sIFdvcmxkIQ== - Context: - type: object - required: - - slot - properties: - slot: - type: integer - default: 100 - example: 100 - Hash: - type: string - description: A 32-byte hash represented as a base58 string. - example: 11111112cMQwSC9qirWGjZM6gLGwW69X22mqwLLGP - Limit: - type: integer - format: int64 - minimum: 0 - SerializablePubkey: - type: string - description: A Solana public key represented as a base58 string. - default: 11111115q4EpJaTXAZWpCg3J2zppWGSZ46KXozzo9 - example: 11111115q4EpJaTXAZWpCg3J2zppWGSZ46KXozzo9 - TokenAcccount: - type: object - required: - - account - - tokenData - properties: - account: - $ref: '#/components/schemas/Account' - tokenData: - $ref: '#/components/schemas/TokenData' - additionalProperties: false - TokenAccountList: - type: object - required: - - items - properties: - cursor: - $ref: '#/components/schemas/Base58String' - items: - type: array - items: - $ref: '#/components/schemas/TokenAcccount' - TokenData: - type: object - required: - - mint - - owner - - amount - - state - properties: - amount: - $ref: '#/components/schemas/UnsignedInteger' - delegate: - $ref: '#/components/schemas/SerializablePubkey' - mint: - $ref: '#/components/schemas/SerializablePubkey' - owner: - $ref: '#/components/schemas/SerializablePubkey' - state: - $ref: '#/components/schemas/AccountState' - tlv: - $ref: '#/components/schemas/Base64String' - UnsignedInteger: - type: integer - default: 100 - example: 100 diff --git a/src/openapi/specs/getCompressedTokenBalancesByOwner.yaml b/src/openapi/specs/getCompressedTokenBalancesByOwner.yaml deleted file mode 100644 index 5ea55809..00000000 --- a/src/openapi/specs/getCompressedTokenBalancesByOwner.yaml +++ /dev/null @@ -1,1292 +0,0 @@ -openapi: 3.0.3 -info: - title: photon-indexer - description: Solana indexer for general compression - license: - name: Apache-2.0 - version: 0.50.0 -servers: -- url: https://mainnet.helius-rpc.com?api-key= -paths: - /getCompressedAccount: - summary: getCompressedAccount - post: - requestBody: - content: - application/json: - schema: - type: object - required: - - jsonrpc - - id - - method - - params - properties: - id: - type: string - description: An ID to identify the request. - enum: - - test-account - jsonrpc: - type: string - description: The version of the JSON-RPC protocol. - enum: - - '2.0' - method: - type: string - description: The name of the method to invoke. - enum: - - getCompressedAccount - params: - type: object - description: Request for compressed account data - default: - address: null - hash: '11111111111111111111111111111111' - properties: - address: - allOf: - - $ref: '#/components/schemas/SerializablePubkey' - nullable: true - hash: - allOf: - - $ref: '#/components/schemas/Hash' - nullable: true - additionalProperties: false - example: - address: null - hash: '11111111111111111111111111111111' - required: true - responses: - '200': - description: '' - content: - application/json: - schema: - type: object - required: - - context - - value - properties: - context: - $ref: '#/components/schemas/Context' - value: - $ref: '#/components/schemas/Account' - '400': - description: Invalid request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '401': - description: Unauthorized request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '403': - description: Request was forbidden. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '404': - description: The specified resource was not found. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '429': - description: Exceeded rate limit. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '500': - description: The server encountered an unexpected condition that prevented it from fulfilling the request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - /getCompressedAccountProof: - summary: getCompressedAccountProof - post: - requestBody: - content: - application/json: - schema: - type: object - required: - - jsonrpc - - id - - method - - params - properties: - id: - type: string - description: An ID to identify the request. - enum: - - test-account - jsonrpc: - type: string - description: The version of the JSON-RPC protocol. - enum: - - '2.0' - method: - type: string - description: The name of the method to invoke. - enum: - - getCompressedAccountProof - params: - type: object - required: - - hash - properties: - hash: - $ref: '#/components/schemas/Hash' - required: true - responses: - '200': - description: '' - content: - application/json: - schema: - type: object - required: - - context - - value - properties: - context: - $ref: '#/components/schemas/Context' - value: - $ref: '#/components/schemas/MerkleProofWithContext' - '400': - description: Invalid request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '401': - description: Unauthorized request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '403': - description: Request was forbidden. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '404': - description: The specified resource was not found. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '429': - description: Exceeded rate limit. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '500': - description: The server encountered an unexpected condition that prevented it from fulfilling the request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - /getCompressedAccountsByOwner: - summary: getCompressedAccountsByOwner - post: - requestBody: - content: - application/json: - schema: - type: object - required: - - jsonrpc - - id - - method - - params - properties: - id: - type: string - description: An ID to identify the request. - enum: - - test-account - jsonrpc: - type: string - description: The version of the JSON-RPC protocol. - enum: - - '2.0' - method: - type: string - description: The name of the method to invoke. - enum: - - getCompressedAccountsByOwner - params: - type: object - required: - - owner - properties: - cursor: - allOf: - - $ref: '#/components/schemas/Hash' - nullable: true - limit: - allOf: - - $ref: '#/components/schemas/Limit' - nullable: true - owner: - $ref: '#/components/schemas/SerializablePubkey' - additionalProperties: false - required: true - responses: - '200': - description: '' - content: - application/json: - schema: - type: object - required: - - context - - value - properties: - context: - $ref: '#/components/schemas/Context' - value: - $ref: '#/components/schemas/PaginatedAccountList' - '400': - description: Invalid request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '401': - description: Unauthorized request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '403': - description: Request was forbidden. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '404': - description: The specified resource was not found. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '429': - description: Exceeded rate limit. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '500': - description: The server encountered an unexpected condition that prevented it from fulfilling the request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - /getCompressedBalance: - summary: getCompressedBalance - post: - requestBody: - content: - application/json: - schema: - type: object - required: - - jsonrpc - - id - - method - - params - properties: - id: - type: string - description: An ID to identify the request. - enum: - - test-account - jsonrpc: - type: string - description: The version of the JSON-RPC protocol. - enum: - - '2.0' - method: - type: string - description: The name of the method to invoke. - enum: - - getCompressedBalance - params: - type: object - description: Request for compressed account data - default: - address: null - hash: '11111111111111111111111111111111' - properties: - address: - allOf: - - $ref: '#/components/schemas/SerializablePubkey' - nullable: true - hash: - allOf: - - $ref: '#/components/schemas/Hash' - nullable: true - additionalProperties: false - example: - address: null - hash: '11111111111111111111111111111111' - required: true - responses: - '200': - description: '' - content: - application/json: - schema: - type: object - required: - - context - - value - properties: - context: - $ref: '#/components/schemas/Context' - value: - $ref: '#/components/schemas/UnsignedInteger' - '400': - description: Invalid request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '401': - description: Unauthorized request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '403': - description: Request was forbidden. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '404': - description: The specified resource was not found. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '429': - description: Exceeded rate limit. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '500': - description: The server encountered an unexpected condition that prevented it from fulfilling the request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - /getCompressedBalanceByOwner: - summary: getCompressedBalanceByOwner - post: - requestBody: - content: - application/json: - schema: - type: object - required: - - jsonrpc - - id - - method - - params - properties: - id: - type: string - description: An ID to identify the request. - enum: - - test-account - jsonrpc: - type: string - description: The version of the JSON-RPC protocol. - enum: - - '2.0' - method: - type: string - description: The name of the method to invoke. - enum: - - getCompressedBalanceByOwner - params: - type: object - required: - - owner - properties: - owner: - $ref: '#/components/schemas/SerializablePubkey' - required: true - responses: - '200': - description: '' - content: - application/json: - schema: - type: object - required: - - context - - value - properties: - context: - $ref: '#/components/schemas/Context' - value: - $ref: '#/components/schemas/UnsignedInteger' - '400': - description: Invalid request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '401': - description: Unauthorized request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '403': - description: Request was forbidden. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '404': - description: The specified resource was not found. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '429': - description: Exceeded rate limit. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '500': - description: The server encountered an unexpected condition that prevented it from fulfilling the request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - /getCompressedTokenAccountBalance: - summary: getCompressedTokenAccountBalance - post: - requestBody: - content: - application/json: - schema: - type: object - required: - - jsonrpc - - id - - method - - params - properties: - id: - type: string - description: An ID to identify the request. - enum: - - test-account - jsonrpc: - type: string - description: The version of the JSON-RPC protocol. - enum: - - '2.0' - method: - type: string - description: The name of the method to invoke. - enum: - - getCompressedTokenAccountBalance - params: - type: object - description: Request for compressed account data - default: - address: null - hash: '11111111111111111111111111111111' - properties: - address: - allOf: - - $ref: '#/components/schemas/SerializablePubkey' - nullable: true - hash: - allOf: - - $ref: '#/components/schemas/Hash' - nullable: true - additionalProperties: false - example: - address: null - hash: '11111111111111111111111111111111' - required: true - responses: - '200': - description: '' - content: - application/json: - schema: - type: object - required: - - context - - value - properties: - context: - $ref: '#/components/schemas/Context' - value: - $ref: '#/components/schemas/TokenAccountBalance' - '400': - description: Invalid request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '401': - description: Unauthorized request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '403': - description: Request was forbidden. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '404': - description: The specified resource was not found. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '429': - description: Exceeded rate limit. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '500': - description: The server encountered an unexpected condition that prevented it from fulfilling the request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - /getCompressedTokenAccountsByDelegate: - summary: getCompressedTokenAccountsByDelegate - post: - requestBody: - content: - application/json: - schema: - type: object - required: - - jsonrpc - - id - - method - - params - properties: - id: - type: string - description: An ID to identify the request. - enum: - - test-account - jsonrpc: - type: string - description: The version of the JSON-RPC protocol. - enum: - - '2.0' - method: - type: string - description: The name of the method to invoke. - enum: - - getCompressedTokenAccountsByDelegate - params: - type: object - required: - - delegate - properties: - cursor: - allOf: - - $ref: '#/components/schemas/Base58String' - nullable: true - delegate: - $ref: '#/components/schemas/SerializablePubkey' - limit: - allOf: - - $ref: '#/components/schemas/Limit' - nullable: true - mint: - allOf: - - $ref: '#/components/schemas/SerializablePubkey' - nullable: true - additionalProperties: false - required: true - responses: - '200': - description: '' - content: - application/json: - schema: - type: object - required: - - context - - value - properties: - context: - $ref: '#/components/schemas/Context' - value: - $ref: '#/components/schemas/TokenAccountList' - '400': - description: Invalid request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '401': - description: Unauthorized request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '403': - description: Request was forbidden. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '404': - description: The specified resource was not found. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '429': - description: Exceeded rate limit. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '500': - description: The server encountered an unexpected condition that prevented it from fulfilling the request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - /getCompressedTokenAccountsByOwner: - summary: getCompressedTokenAccountsByOwner - post: - requestBody: - content: - application/json: - schema: - type: object - required: - - jsonrpc - - id - - method - - params - properties: - id: - type: string - description: An ID to identify the request. - enum: - - test-account - jsonrpc: - type: string - description: The version of the JSON-RPC protocol. - enum: - - '2.0' - method: - type: string - description: The name of the method to invoke. - enum: - - getCompressedTokenAccountsByOwner - params: - type: object - required: - - owner - properties: - cursor: - allOf: - - $ref: '#/components/schemas/Base58String' - nullable: true - limit: - allOf: - - $ref: '#/components/schemas/Limit' - nullable: true - mint: - allOf: - - $ref: '#/components/schemas/SerializablePubkey' - nullable: true - owner: - $ref: '#/components/schemas/SerializablePubkey' - additionalProperties: false - required: true - responses: - '200': - description: '' - content: - application/json: - schema: - type: object - required: - - context - - value - properties: - context: - $ref: '#/components/schemas/Context' - value: - $ref: '#/components/schemas/TokenAccountList' - '400': - description: Invalid request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '401': - description: Unauthorized request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '403': - description: Request was forbidden. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '404': - description: The specified resource was not found. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '429': - description: Exceeded rate limit. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '500': - description: The server encountered an unexpected condition that prevented it from fulfilling the request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - /getCompressedTokenBalancesByOwner: - summary: getCompressedTokenBalancesByOwner - post: - requestBody: - content: - application/json: - schema: - type: object - required: - - jsonrpc - - id - - method - - params - properties: - id: - type: string - description: An ID to identify the request. - enum: - - test-account - jsonrpc: - type: string - description: The version of the JSON-RPC protocol. - enum: - - '2.0' - method: - type: string - description: The name of the method to invoke. - enum: - - getCompressedTokenBalancesByOwner - params: - type: object - required: - - owner - properties: - cursor: - allOf: - - $ref: '#/components/schemas/Base58String' - nullable: true - limit: - allOf: - - $ref: '#/components/schemas/Limit' - nullable: true - mint: - allOf: - - $ref: '#/components/schemas/SerializablePubkey' - nullable: true - owner: - $ref: '#/components/schemas/SerializablePubkey' - additionalProperties: false - required: true - responses: - '200': - description: '' - content: - application/json: - schema: - type: object - required: - - context - - value - properties: - context: - $ref: '#/components/schemas/Context' - value: - $ref: '#/components/schemas/TokenBalanceList' - additionalProperties: false - '429': - description: Exceeded rate limit. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '500': - description: The server encountered an unexpected condition that prevented it from fulfilling the request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - /getMultipleCompressedAccountProofs: - summary: getMultipleCompressedAccountProofs - post: - requestBody: - content: - application/json: - schema: - type: object - required: - - jsonrpc - - id - - method - - params - properties: - id: - type: string - description: An ID to identify the request. - enum: - - test-account - jsonrpc: - type: string - description: The version of the JSON-RPC protocol. - enum: - - '2.0' - method: - type: string - description: The name of the method to invoke. - enum: - - getMultipleCompressedAccountProofs - params: - type: array - items: - $ref: '#/components/schemas/Hash' - required: true - responses: - '200': - description: '' - content: - application/json: - schema: - type: object - required: - - context - - value - properties: - context: - $ref: '#/components/schemas/Context' - value: - type: array - items: - $ref: '#/components/schemas/MerkleProofWithContext' - '400': - description: Invalid request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '401': - description: Unauthorized request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '403': - description: Request was forbidden. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '404': - description: The specified resource was not found. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '429': - description: Exceeded rate limit. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '500': - description: The server encountered an unexpected condition that prevented it from fulfilling the request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - /getMultipleCompressedAccounts: - summary: getMultipleCompressedAccounts - post: - requestBody: - content: - application/json: - schema: - type: object - required: - - jsonrpc - - id - - method - - params - properties: - id: - type: string - description: An ID to identify the request. - enum: - - test-account - jsonrpc: - type: string - description: The version of the JSON-RPC protocol. - enum: - - '2.0' - method: - type: string - description: The name of the method to invoke. - enum: - - getMultipleCompressedAccounts - params: - type: object - description: Request for compressed account data - default: - addresses: null - hashes: - - 1111111QLbz7JHiBTspS962RLKV8GndWFwiEaqKM - - 1111111ogCyDbaRMvkdsHB3qfdyFYaG1WtRUAfdh - properties: - addresses: - type: array - items: - $ref: '#/components/schemas/SerializablePubkey' - nullable: true - hashes: - type: array - items: - $ref: '#/components/schemas/Hash' - nullable: true - additionalProperties: false - example: - addresses: null - hashes: - - 1111111QLbz7JHiBTspS962RLKV8GndWFwiEaqKM - - 1111111ogCyDbaRMvkdsHB3qfdyFYaG1WtRUAfdh - required: true - responses: - '200': - description: '' - content: - application/json: - schema: - type: object - required: - - context - - value - properties: - context: - $ref: '#/components/schemas/Context' - value: - $ref: '#/components/schemas/AccountList' - '400': - description: Invalid request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '401': - description: Unauthorized request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '403': - description: Request was forbidden. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '404': - description: The specified resource was not found. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '429': - description: Exceeded rate limit. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '500': - description: The server encountered an unexpected condition that prevented it from fulfilling the request. - content: - application/json: - schema: - type: object - properties: - error: - type: string -components: - schemas: - Base58String: - type: string - description: A base 58 encoded string. - default: 3J98t1WpEZ73CNm - example: 3J98t1WpEZ73CNm - Context: - type: object - required: - - slot - properties: - slot: - type: integer - default: 100 - example: 100 - Limit: - type: integer - format: int64 - minimum: 0 - SerializablePubkey: - type: string - description: A Solana public key represented as a base58 string. - default: 11111113pNDtm61yGF8j2ycAwLEPsuWQXobye5qDR - example: 11111113pNDtm61yGF8j2ycAwLEPsuWQXobye5qDR - TokenBalance: - type: object - required: - - mint - - balance - properties: - balance: - $ref: '#/components/schemas/UnsignedInteger' - mint: - $ref: '#/components/schemas/SerializablePubkey' - TokenBalanceList: - type: object - required: - - token_balances - properties: - cursor: - $ref: '#/components/schemas/Base58String' - token_balances: - type: array - items: - $ref: '#/components/schemas/TokenBalance' - UnsignedInteger: - type: integer - default: 100 - example: 100 diff --git a/src/openapi/specs/getCompressedTokenBalancesByOwnerV2.yaml b/src/openapi/specs/getCompressedTokenBalancesByOwnerV2.yaml deleted file mode 100644 index 71d1dddc..00000000 --- a/src/openapi/specs/getCompressedTokenBalancesByOwnerV2.yaml +++ /dev/null @@ -1,144 +0,0 @@ -openapi: 3.0.3 -info: - title: photon-indexer - description: Solana indexer for general compression - license: - name: Apache-2.0 - version: 0.50.0 -servers: -- url: https://mainnet.helius-rpc.com?api-key= -paths: - /: - summary: getCompressedTokenBalancesByOwnerV2 - post: - requestBody: - content: - application/json: - schema: - type: object - required: - - jsonrpc - - id - - method - - params - properties: - id: - type: string - description: An ID to identify the request. - enum: - - test-account - jsonrpc: - type: string - description: The version of the JSON-RPC protocol. - enum: - - '2.0' - method: - type: string - description: The name of the method to invoke. - enum: - - getCompressedTokenBalancesByOwnerV2 - params: - type: object - required: - - owner - properties: - cursor: - allOf: - - $ref: '#/components/schemas/Base58String' - nullable: true - limit: - allOf: - - $ref: '#/components/schemas/Limit' - nullable: true - mint: - allOf: - - $ref: '#/components/schemas/SerializablePubkey' - nullable: true - owner: - $ref: '#/components/schemas/SerializablePubkey' - additionalProperties: false - required: true - responses: - '200': - description: '' - content: - application/json: - schema: - type: object - required: - - context - - value - properties: - context: - $ref: '#/components/schemas/Context' - value: - $ref: '#/components/schemas/TokenBalanceListV2' - additionalProperties: false - '429': - description: Exceeded rate limit. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '500': - description: The server encountered an unexpected condition that prevented it from fulfilling the request. - content: - application/json: - schema: - type: object - properties: - error: - type: string -components: - schemas: - Base58String: - type: string - description: A base 58 encoded string. - default: 3J98t1WpEZ73CNm - example: 3J98t1WpEZ73CNm - Context: - type: object - required: - - slot - properties: - slot: - type: integer - default: 100 - example: 100 - Limit: - type: integer - format: int64 - minimum: 0 - SerializablePubkey: - type: string - description: A Solana public key represented as a base58 string. - default: 11111114DhpssPJgSi1YU7hCMfYt1BJ334YgsffXm - example: 11111114DhpssPJgSi1YU7hCMfYt1BJ334YgsffXm - TokenBalance: - type: object - required: - - mint - - balance - properties: - balance: - $ref: '#/components/schemas/UnsignedInteger' - mint: - $ref: '#/components/schemas/SerializablePubkey' - TokenBalanceListV2: - type: object - required: - - items - properties: - cursor: - $ref: '#/components/schemas/Base58String' - items: - type: array - items: - $ref: '#/components/schemas/TokenBalance' - UnsignedInteger: - type: integer - default: 100 - example: 100 diff --git a/src/openapi/specs/getCompressionSignaturesForAccount.yaml b/src/openapi/specs/getCompressionSignaturesForAccount.yaml deleted file mode 100644 index e73a2d12..00000000 --- a/src/openapi/specs/getCompressionSignaturesForAccount.yaml +++ /dev/null @@ -1,1515 +0,0 @@ -openapi: 3.0.3 -info: - title: photon-indexer - description: Solana indexer for general compression - license: - name: Apache-2.0 - version: 0.50.0 -servers: -- url: https://mainnet.helius-rpc.com?api-key= -paths: - /getCompressedAccount: - summary: getCompressedAccount - post: - requestBody: - content: - application/json: - schema: - type: object - required: - - jsonrpc - - id - - method - - params - properties: - id: - type: string - description: An ID to identify the request. - enum: - - test-account - jsonrpc: - type: string - description: The version of the JSON-RPC protocol. - enum: - - '2.0' - method: - type: string - description: The name of the method to invoke. - enum: - - getCompressedAccount - params: - type: object - description: Request for compressed account data - default: - address: null - hash: '11111111111111111111111111111111' - properties: - address: - allOf: - - $ref: '#/components/schemas/SerializablePubkey' - nullable: true - hash: - allOf: - - $ref: '#/components/schemas/Hash' - nullable: true - additionalProperties: false - example: - address: null - hash: '11111111111111111111111111111111' - required: true - responses: - '200': - description: '' - content: - application/json: - schema: - type: object - required: - - context - - value - properties: - context: - $ref: '#/components/schemas/Context' - value: - $ref: '#/components/schemas/Account' - '400': - description: Invalid request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '401': - description: Unauthorized request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '403': - description: Request was forbidden. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '404': - description: The specified resource was not found. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '429': - description: Exceeded rate limit. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '500': - description: The server encountered an unexpected condition that prevented it from fulfilling the request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - /getCompressedAccountProof: - summary: getCompressedAccountProof - post: - requestBody: - content: - application/json: - schema: - type: object - required: - - jsonrpc - - id - - method - - params - properties: - id: - type: string - description: An ID to identify the request. - enum: - - test-account - jsonrpc: - type: string - description: The version of the JSON-RPC protocol. - enum: - - '2.0' - method: - type: string - description: The name of the method to invoke. - enum: - - getCompressedAccountProof - params: - type: object - required: - - hash - properties: - hash: - $ref: '#/components/schemas/Hash' - required: true - responses: - '200': - description: '' - content: - application/json: - schema: - type: object - required: - - context - - value - properties: - context: - $ref: '#/components/schemas/Context' - value: - $ref: '#/components/schemas/MerkleProofWithContext' - '400': - description: Invalid request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '401': - description: Unauthorized request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '403': - description: Request was forbidden. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '404': - description: The specified resource was not found. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '429': - description: Exceeded rate limit. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '500': - description: The server encountered an unexpected condition that prevented it from fulfilling the request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - /getCompressedAccountsByOwner: - summary: getCompressedAccountsByOwner - post: - requestBody: - content: - application/json: - schema: - type: object - required: - - jsonrpc - - id - - method - - params - properties: - id: - type: string - description: An ID to identify the request. - enum: - - test-account - jsonrpc: - type: string - description: The version of the JSON-RPC protocol. - enum: - - '2.0' - method: - type: string - description: The name of the method to invoke. - enum: - - getCompressedAccountsByOwner - params: - type: object - required: - - owner - properties: - cursor: - allOf: - - $ref: '#/components/schemas/Hash' - nullable: true - limit: - allOf: - - $ref: '#/components/schemas/Limit' - nullable: true - owner: - $ref: '#/components/schemas/SerializablePubkey' - additionalProperties: false - required: true - responses: - '200': - description: '' - content: - application/json: - schema: - type: object - required: - - context - - value - properties: - context: - $ref: '#/components/schemas/Context' - value: - $ref: '#/components/schemas/PaginatedAccountList' - '400': - description: Invalid request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '401': - description: Unauthorized request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '403': - description: Request was forbidden. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '404': - description: The specified resource was not found. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '429': - description: Exceeded rate limit. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '500': - description: The server encountered an unexpected condition that prevented it from fulfilling the request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - /getCompressedBalance: - summary: getCompressedBalance - post: - requestBody: - content: - application/json: - schema: - type: object - required: - - jsonrpc - - id - - method - - params - properties: - id: - type: string - description: An ID to identify the request. - enum: - - test-account - jsonrpc: - type: string - description: The version of the JSON-RPC protocol. - enum: - - '2.0' - method: - type: string - description: The name of the method to invoke. - enum: - - getCompressedBalance - params: - type: object - description: Request for compressed account data - default: - address: null - hash: '11111111111111111111111111111111' - properties: - address: - allOf: - - $ref: '#/components/schemas/SerializablePubkey' - nullable: true - hash: - allOf: - - $ref: '#/components/schemas/Hash' - nullable: true - additionalProperties: false - example: - address: null - hash: '11111111111111111111111111111111' - required: true - responses: - '200': - description: '' - content: - application/json: - schema: - type: object - required: - - context - - value - properties: - context: - $ref: '#/components/schemas/Context' - value: - $ref: '#/components/schemas/UnsignedInteger' - '400': - description: Invalid request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '401': - description: Unauthorized request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '403': - description: Request was forbidden. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '404': - description: The specified resource was not found. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '429': - description: Exceeded rate limit. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '500': - description: The server encountered an unexpected condition that prevented it from fulfilling the request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - /getCompressedBalanceByOwner: - summary: getCompressedBalanceByOwner - post: - requestBody: - content: - application/json: - schema: - type: object - required: - - jsonrpc - - id - - method - - params - properties: - id: - type: string - description: An ID to identify the request. - enum: - - test-account - jsonrpc: - type: string - description: The version of the JSON-RPC protocol. - enum: - - '2.0' - method: - type: string - description: The name of the method to invoke. - enum: - - getCompressedBalanceByOwner - params: - type: object - required: - - owner - properties: - owner: - $ref: '#/components/schemas/SerializablePubkey' - required: true - responses: - '200': - description: '' - content: - application/json: - schema: - type: object - required: - - context - - value - properties: - context: - $ref: '#/components/schemas/Context' - value: - $ref: '#/components/schemas/UnsignedInteger' - '400': - description: Invalid request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '401': - description: Unauthorized request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '403': - description: Request was forbidden. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '404': - description: The specified resource was not found. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '429': - description: Exceeded rate limit. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '500': - description: The server encountered an unexpected condition that prevented it from fulfilling the request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - /getCompressedTokenAccountBalance: - summary: getCompressedTokenAccountBalance - post: - requestBody: - content: - application/json: - schema: - type: object - required: - - jsonrpc - - id - - method - - params - properties: - id: - type: string - description: An ID to identify the request. - enum: - - test-account - jsonrpc: - type: string - description: The version of the JSON-RPC protocol. - enum: - - '2.0' - method: - type: string - description: The name of the method to invoke. - enum: - - getCompressedTokenAccountBalance - params: - type: object - description: Request for compressed account data - default: - address: null - hash: '11111111111111111111111111111111' - properties: - address: - allOf: - - $ref: '#/components/schemas/SerializablePubkey' - nullable: true - hash: - allOf: - - $ref: '#/components/schemas/Hash' - nullable: true - additionalProperties: false - example: - address: null - hash: '11111111111111111111111111111111' - required: true - responses: - '200': - description: '' - content: - application/json: - schema: - type: object - required: - - context - - value - properties: - context: - $ref: '#/components/schemas/Context' - value: - $ref: '#/components/schemas/TokenAccountBalance' - '400': - description: Invalid request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '401': - description: Unauthorized request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '403': - description: Request was forbidden. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '404': - description: The specified resource was not found. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '429': - description: Exceeded rate limit. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '500': - description: The server encountered an unexpected condition that prevented it from fulfilling the request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - /getCompressedTokenAccountsByDelegate: - summary: getCompressedTokenAccountsByDelegate - post: - requestBody: - content: - application/json: - schema: - type: object - required: - - jsonrpc - - id - - method - - params - properties: - id: - type: string - description: An ID to identify the request. - enum: - - test-account - jsonrpc: - type: string - description: The version of the JSON-RPC protocol. - enum: - - '2.0' - method: - type: string - description: The name of the method to invoke. - enum: - - getCompressedTokenAccountsByDelegate - params: - type: object - required: - - delegate - properties: - cursor: - allOf: - - $ref: '#/components/schemas/Base58String' - nullable: true - delegate: - $ref: '#/components/schemas/SerializablePubkey' - limit: - allOf: - - $ref: '#/components/schemas/Limit' - nullable: true - mint: - allOf: - - $ref: '#/components/schemas/SerializablePubkey' - nullable: true - additionalProperties: false - required: true - responses: - '200': - description: '' - content: - application/json: - schema: - type: object - required: - - context - - value - properties: - context: - $ref: '#/components/schemas/Context' - value: - $ref: '#/components/schemas/TokenAccountList' - '400': - description: Invalid request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '401': - description: Unauthorized request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '403': - description: Request was forbidden. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '404': - description: The specified resource was not found. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '429': - description: Exceeded rate limit. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '500': - description: The server encountered an unexpected condition that prevented it from fulfilling the request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - /getCompressedTokenAccountsByOwner: - summary: getCompressedTokenAccountsByOwner - post: - requestBody: - content: - application/json: - schema: - type: object - required: - - jsonrpc - - id - - method - - params - properties: - id: - type: string - description: An ID to identify the request. - enum: - - test-account - jsonrpc: - type: string - description: The version of the JSON-RPC protocol. - enum: - - '2.0' - method: - type: string - description: The name of the method to invoke. - enum: - - getCompressedTokenAccountsByOwner - params: - type: object - required: - - owner - properties: - cursor: - allOf: - - $ref: '#/components/schemas/Base58String' - nullable: true - limit: - allOf: - - $ref: '#/components/schemas/Limit' - nullable: true - mint: - allOf: - - $ref: '#/components/schemas/SerializablePubkey' - nullable: true - owner: - $ref: '#/components/schemas/SerializablePubkey' - additionalProperties: false - required: true - responses: - '200': - description: '' - content: - application/json: - schema: - type: object - required: - - context - - value - properties: - context: - $ref: '#/components/schemas/Context' - value: - $ref: '#/components/schemas/TokenAccountList' - '400': - description: Invalid request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '401': - description: Unauthorized request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '403': - description: Request was forbidden. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '404': - description: The specified resource was not found. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '429': - description: Exceeded rate limit. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '500': - description: The server encountered an unexpected condition that prevented it from fulfilling the request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - /getCompressedTokenBalancesByOwner: - summary: getCompressedTokenBalancesByOwner - post: - requestBody: - content: - application/json: - schema: - type: object - required: - - jsonrpc - - id - - method - - params - properties: - id: - type: string - description: An ID to identify the request. - enum: - - test-account - jsonrpc: - type: string - description: The version of the JSON-RPC protocol. - enum: - - '2.0' - method: - type: string - description: The name of the method to invoke. - enum: - - getCompressedTokenBalancesByOwner - params: - type: object - required: - - owner - properties: - cursor: - allOf: - - $ref: '#/components/schemas/Base58String' - nullable: true - limit: - allOf: - - $ref: '#/components/schemas/Limit' - nullable: true - mint: - allOf: - - $ref: '#/components/schemas/SerializablePubkey' - nullable: true - owner: - $ref: '#/components/schemas/SerializablePubkey' - required: true - responses: - '200': - description: '' - content: - application/json: - schema: - type: object - required: - - context - - value - properties: - context: - $ref: '#/components/schemas/Context' - value: - $ref: '#/components/schemas/TokenBalanceList' - '400': - description: Invalid request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '401': - description: Unauthorized request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '403': - description: Request was forbidden. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '404': - description: The specified resource was not found. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '429': - description: Exceeded rate limit. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '500': - description: The server encountered an unexpected condition that prevented it from fulfilling the request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - /getCompressionSignaturesForAccount: - summary: getCompressionSignaturesForAccount - post: - requestBody: - content: - application/json: - schema: - type: object - required: - - jsonrpc - - id - - method - - params - properties: - id: - type: string - description: An ID to identify the request. - enum: - - test-account - jsonrpc: - type: string - description: The version of the JSON-RPC protocol. - enum: - - '2.0' - method: - type: string - description: The name of the method to invoke. - enum: - - getCompressionSignaturesForAccount - params: - type: object - required: - - hash - properties: - hash: - $ref: '#/components/schemas/Hash' - required: true - responses: - '200': - description: '' - content: - application/json: - schema: - type: object - required: - - context - - value - properties: - context: - $ref: '#/components/schemas/Context' - value: - $ref: '#/components/schemas/SignatureInfoList' - additionalProperties: false - '429': - description: Exceeded rate limit. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '500': - description: The server encountered an unexpected condition that prevented it from fulfilling the request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - /getMultipleCompressedAccountProofs: - summary: getMultipleCompressedAccountProofs - post: - requestBody: - content: - application/json: - schema: - type: object - required: - - jsonrpc - - id - - method - - params - properties: - id: - type: string - description: An ID to identify the request. - enum: - - test-account - jsonrpc: - type: string - description: The version of the JSON-RPC protocol. - enum: - - '2.0' - method: - type: string - description: The name of the method to invoke. - enum: - - getMultipleCompressedAccountProofs - params: - type: array - items: - $ref: '#/components/schemas/Hash' - required: true - responses: - '200': - description: '' - content: - application/json: - schema: - type: object - required: - - context - - value - properties: - context: - $ref: '#/components/schemas/Context' - value: - type: array - items: - $ref: '#/components/schemas/MerkleProofWithContext' - '400': - description: Invalid request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '401': - description: Unauthorized request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '403': - description: Request was forbidden. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '404': - description: The specified resource was not found. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '429': - description: Exceeded rate limit. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '500': - description: The server encountered an unexpected condition that prevented it from fulfilling the request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - /getMultipleCompressedAccounts: - summary: getMultipleCompressedAccounts - post: - requestBody: - content: - application/json: - schema: - type: object - required: - - jsonrpc - - id - - method - - params - properties: - id: - type: string - description: An ID to identify the request. - enum: - - test-account - jsonrpc: - type: string - description: The version of the JSON-RPC protocol. - enum: - - '2.0' - method: - type: string - description: The name of the method to invoke. - enum: - - getMultipleCompressedAccounts - params: - type: object - description: Request for compressed account data - default: - addresses: null - hashes: - - 1111111QLbz7JHiBTspS962RLKV8GndWFwiEaqKM - - 1111111ogCyDbaRMvkdsHB3qfdyFYaG1WtRUAfdh - properties: - addresses: - type: array - items: - $ref: '#/components/schemas/SerializablePubkey' - nullable: true - hashes: - type: array - items: - $ref: '#/components/schemas/Hash' - nullable: true - additionalProperties: false - example: - addresses: null - hashes: - - 1111111QLbz7JHiBTspS962RLKV8GndWFwiEaqKM - - 1111111ogCyDbaRMvkdsHB3qfdyFYaG1WtRUAfdh - required: true - responses: - '200': - description: '' - content: - application/json: - schema: - type: object - required: - - context - - value - properties: - context: - $ref: '#/components/schemas/Context' - value: - $ref: '#/components/schemas/AccountList' - '400': - description: Invalid request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '401': - description: Unauthorized request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '403': - description: Request was forbidden. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '404': - description: The specified resource was not found. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '429': - description: Exceeded rate limit. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '500': - description: The server encountered an unexpected condition that prevented it from fulfilling the request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - /getTransactionWithCompressionInfo: - summary: getTransactionWithCompressionInfo - post: - requestBody: - content: - application/json: - schema: - type: object - required: - - jsonrpc - - id - - method - - params - properties: - id: - type: string - description: An ID to identify the request. - enum: - - test-account - jsonrpc: - type: string - description: The version of the JSON-RPC protocol. - enum: - - '2.0' - method: - type: string - description: The name of the method to invoke. - enum: - - getTransactionWithCompressionInfo - params: - type: object - required: - - signature - properties: - signature: - $ref: '#/components/schemas/SerializableSignature' - required: true - responses: - '200': - description: '' - content: - application/json: - schema: - type: object - description: A Solana transaction with additional compression information - properties: - compression_info: - type: object - required: - - closed_accounts - - opened_accounts - properties: - closed_accounts: - type: array - items: - $ref: '#/components/schemas/AccountWithOptionalTokenData' - opened_accounts: - type: array - items: - $ref: '#/components/schemas/AccountWithOptionalTokenData' - transaction: - type: object - description: An encoded confirmed transaction with status meta - '400': - description: Invalid request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '401': - description: Unauthorized request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '403': - description: Request was forbidden. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '404': - description: The specified resource was not found. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '429': - description: Exceeded rate limit. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '500': - description: The server encountered an unexpected condition that prevented it from fulfilling the request. - content: - application/json: - schema: - type: object - properties: - error: - type: string -components: - schemas: - Context: - type: object - required: - - slot - properties: - slot: - type: integer - default: 100 - example: 100 - Hash: - type: string - description: A 32-byte hash represented as a base58 string. - example: 11111112cMQwSC9qirWGjZM6gLGwW69X22mqwLLGP - SerializableSignature: - type: string - description: A Solana transaction signature. - default: 5J8H5sTvEhnGcB4R8K1n7mfoiWUD9RzPVGES7e3WxC7c - example: 5J8H5sTvEhnGcB4R8K1n7mfoiWUD9RzPVGES7e3WxC7c - SignatureInfo: - type: object - required: - - signature - - slot - - blockTime - properties: - blockTime: - $ref: '#/components/schemas/UnixTimestamp' - signature: - $ref: '#/components/schemas/SerializableSignature' - slot: - $ref: '#/components/schemas/UnsignedInteger' - SignatureInfoList: - type: object - required: - - items - properties: - items: - type: array - items: - $ref: '#/components/schemas/SignatureInfo' - UnixTimestamp: - type: integer - description: An Unix timestamp (seconds) - default: 1714081554 - example: 1714081554 - UnsignedInteger: - type: integer - default: 100 - example: 100 diff --git a/src/openapi/specs/getCompressionSignaturesForAddress.yaml b/src/openapi/specs/getCompressionSignaturesForAddress.yaml deleted file mode 100644 index f3f9850c..00000000 --- a/src/openapi/specs/getCompressionSignaturesForAddress.yaml +++ /dev/null @@ -1,1637 +0,0 @@ -openapi: 3.0.3 -info: - title: photon-indexer - description: Solana indexer for general compression - license: - name: Apache-2.0 - version: 0.50.0 -servers: -- url: https://mainnet.helius-rpc.com?api-key= -paths: - /getCompressedAccount: - summary: getCompressedAccount - post: - requestBody: - content: - application/json: - schema: - type: object - required: - - jsonrpc - - id - - method - - params - properties: - id: - type: string - description: An ID to identify the request. - enum: - - test-account - jsonrpc: - type: string - description: The version of the JSON-RPC protocol. - enum: - - '2.0' - method: - type: string - description: The name of the method to invoke. - enum: - - getCompressedAccount - params: - type: object - description: Request for compressed account data - default: - address: null - hash: '11111111111111111111111111111111' - properties: - address: - allOf: - - $ref: '#/components/schemas/SerializablePubkey' - nullable: true - hash: - allOf: - - $ref: '#/components/schemas/Hash' - nullable: true - additionalProperties: false - example: - address: null - hash: '11111111111111111111111111111111' - required: true - responses: - '200': - description: '' - content: - application/json: - schema: - type: object - required: - - context - - value - properties: - context: - $ref: '#/components/schemas/Context' - value: - $ref: '#/components/schemas/Account' - '400': - description: Invalid request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '401': - description: Unauthorized request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '403': - description: Request was forbidden. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '404': - description: The specified resource was not found. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '429': - description: Exceeded rate limit. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '500': - description: The server encountered an unexpected condition that prevented it from fulfilling the request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - /getCompressedAccountProof: - summary: getCompressedAccountProof - post: - requestBody: - content: - application/json: - schema: - type: object - required: - - jsonrpc - - id - - method - - params - properties: - id: - type: string - description: An ID to identify the request. - enum: - - test-account - jsonrpc: - type: string - description: The version of the JSON-RPC protocol. - enum: - - '2.0' - method: - type: string - description: The name of the method to invoke. - enum: - - getCompressedAccountProof - params: - type: object - required: - - hash - properties: - hash: - $ref: '#/components/schemas/Hash' - required: true - responses: - '200': - description: '' - content: - application/json: - schema: - type: object - required: - - context - - value - properties: - context: - $ref: '#/components/schemas/Context' - value: - $ref: '#/components/schemas/MerkleProofWithContext' - '400': - description: Invalid request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '401': - description: Unauthorized request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '403': - description: Request was forbidden. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '404': - description: The specified resource was not found. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '429': - description: Exceeded rate limit. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '500': - description: The server encountered an unexpected condition that prevented it from fulfilling the request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - /getCompressedAccountsByOwner: - summary: getCompressedAccountsByOwner - post: - requestBody: - content: - application/json: - schema: - type: object - required: - - jsonrpc - - id - - method - - params - properties: - id: - type: string - description: An ID to identify the request. - enum: - - test-account - jsonrpc: - type: string - description: The version of the JSON-RPC protocol. - enum: - - '2.0' - method: - type: string - description: The name of the method to invoke. - enum: - - getCompressedAccountsByOwner - params: - type: object - required: - - owner - properties: - cursor: - allOf: - - $ref: '#/components/schemas/Hash' - nullable: true - limit: - allOf: - - $ref: '#/components/schemas/Limit' - nullable: true - owner: - $ref: '#/components/schemas/SerializablePubkey' - additionalProperties: false - required: true - responses: - '200': - description: '' - content: - application/json: - schema: - type: object - required: - - context - - value - properties: - context: - $ref: '#/components/schemas/Context' - value: - $ref: '#/components/schemas/PaginatedAccountList' - '400': - description: Invalid request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '401': - description: Unauthorized request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '403': - description: Request was forbidden. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '404': - description: The specified resource was not found. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '429': - description: Exceeded rate limit. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '500': - description: The server encountered an unexpected condition that prevented it from fulfilling the request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - /getCompressedBalance: - summary: getCompressedBalance - post: - requestBody: - content: - application/json: - schema: - type: object - required: - - jsonrpc - - id - - method - - params - properties: - id: - type: string - description: An ID to identify the request. - enum: - - test-account - jsonrpc: - type: string - description: The version of the JSON-RPC protocol. - enum: - - '2.0' - method: - type: string - description: The name of the method to invoke. - enum: - - getCompressedBalance - params: - type: object - description: Request for compressed account data - default: - address: null - hash: '11111111111111111111111111111111' - properties: - address: - allOf: - - $ref: '#/components/schemas/SerializablePubkey' - nullable: true - hash: - allOf: - - $ref: '#/components/schemas/Hash' - nullable: true - additionalProperties: false - example: - address: null - hash: '11111111111111111111111111111111' - required: true - responses: - '200': - description: '' - content: - application/json: - schema: - type: object - required: - - context - - value - properties: - context: - $ref: '#/components/schemas/Context' - value: - $ref: '#/components/schemas/UnsignedInteger' - '400': - description: Invalid request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '401': - description: Unauthorized request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '403': - description: Request was forbidden. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '404': - description: The specified resource was not found. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '429': - description: Exceeded rate limit. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '500': - description: The server encountered an unexpected condition that prevented it from fulfilling the request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - /getCompressedBalanceByOwner: - summary: getCompressedBalanceByOwner - post: - requestBody: - content: - application/json: - schema: - type: object - required: - - jsonrpc - - id - - method - - params - properties: - id: - type: string - description: An ID to identify the request. - enum: - - test-account - jsonrpc: - type: string - description: The version of the JSON-RPC protocol. - enum: - - '2.0' - method: - type: string - description: The name of the method to invoke. - enum: - - getCompressedBalanceByOwner - params: - type: object - required: - - owner - properties: - owner: - $ref: '#/components/schemas/SerializablePubkey' - required: true - responses: - '200': - description: '' - content: - application/json: - schema: - type: object - required: - - context - - value - properties: - context: - $ref: '#/components/schemas/Context' - value: - $ref: '#/components/schemas/UnsignedInteger' - '400': - description: Invalid request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '401': - description: Unauthorized request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '403': - description: Request was forbidden. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '404': - description: The specified resource was not found. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '429': - description: Exceeded rate limit. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '500': - description: The server encountered an unexpected condition that prevented it from fulfilling the request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - /getCompressedTokenAccountBalance: - summary: getCompressedTokenAccountBalance - post: - requestBody: - content: - application/json: - schema: - type: object - required: - - jsonrpc - - id - - method - - params - properties: - id: - type: string - description: An ID to identify the request. - enum: - - test-account - jsonrpc: - type: string - description: The version of the JSON-RPC protocol. - enum: - - '2.0' - method: - type: string - description: The name of the method to invoke. - enum: - - getCompressedTokenAccountBalance - params: - type: object - description: Request for compressed account data - default: - address: null - hash: '11111111111111111111111111111111' - properties: - address: - allOf: - - $ref: '#/components/schemas/SerializablePubkey' - nullable: true - hash: - allOf: - - $ref: '#/components/schemas/Hash' - nullable: true - additionalProperties: false - example: - address: null - hash: '11111111111111111111111111111111' - required: true - responses: - '200': - description: '' - content: - application/json: - schema: - type: object - required: - - context - - value - properties: - context: - $ref: '#/components/schemas/Context' - value: - $ref: '#/components/schemas/TokenAccountBalance' - '400': - description: Invalid request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '401': - description: Unauthorized request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '403': - description: Request was forbidden. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '404': - description: The specified resource was not found. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '429': - description: Exceeded rate limit. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '500': - description: The server encountered an unexpected condition that prevented it from fulfilling the request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - /getCompressedTokenAccountsByDelegate: - summary: getCompressedTokenAccountsByDelegate - post: - requestBody: - content: - application/json: - schema: - type: object - required: - - jsonrpc - - id - - method - - params - properties: - id: - type: string - description: An ID to identify the request. - enum: - - test-account - jsonrpc: - type: string - description: The version of the JSON-RPC protocol. - enum: - - '2.0' - method: - type: string - description: The name of the method to invoke. - enum: - - getCompressedTokenAccountsByDelegate - params: - type: object - required: - - delegate - properties: - cursor: - allOf: - - $ref: '#/components/schemas/Base58String' - nullable: true - delegate: - $ref: '#/components/schemas/SerializablePubkey' - limit: - allOf: - - $ref: '#/components/schemas/Limit' - nullable: true - mint: - allOf: - - $ref: '#/components/schemas/SerializablePubkey' - nullable: true - additionalProperties: false - required: true - responses: - '200': - description: '' - content: - application/json: - schema: - type: object - required: - - context - - value - properties: - context: - $ref: '#/components/schemas/Context' - value: - $ref: '#/components/schemas/TokenAccountList' - '400': - description: Invalid request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '401': - description: Unauthorized request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '403': - description: Request was forbidden. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '404': - description: The specified resource was not found. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '429': - description: Exceeded rate limit. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '500': - description: The server encountered an unexpected condition that prevented it from fulfilling the request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - /getCompressedTokenAccountsByOwner: - summary: getCompressedTokenAccountsByOwner - post: - requestBody: - content: - application/json: - schema: - type: object - required: - - jsonrpc - - id - - method - - params - properties: - id: - type: string - description: An ID to identify the request. - enum: - - test-account - jsonrpc: - type: string - description: The version of the JSON-RPC protocol. - enum: - - '2.0' - method: - type: string - description: The name of the method to invoke. - enum: - - getCompressedTokenAccountsByOwner - params: - type: object - required: - - owner - properties: - cursor: - allOf: - - $ref: '#/components/schemas/Base58String' - nullable: true - limit: - allOf: - - $ref: '#/components/schemas/Limit' - nullable: true - mint: - allOf: - - $ref: '#/components/schemas/SerializablePubkey' - nullable: true - owner: - $ref: '#/components/schemas/SerializablePubkey' - additionalProperties: false - required: true - responses: - '200': - description: '' - content: - application/json: - schema: - type: object - required: - - context - - value - properties: - context: - $ref: '#/components/schemas/Context' - value: - $ref: '#/components/schemas/TokenAccountList' - '400': - description: Invalid request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '401': - description: Unauthorized request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '403': - description: Request was forbidden. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '404': - description: The specified resource was not found. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '429': - description: Exceeded rate limit. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '500': - description: The server encountered an unexpected condition that prevented it from fulfilling the request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - /getCompressedTokenBalancesByOwner: - summary: getCompressedTokenBalancesByOwner - post: - requestBody: - content: - application/json: - schema: - type: object - required: - - jsonrpc - - id - - method - - params - properties: - id: - type: string - description: An ID to identify the request. - enum: - - test-account - jsonrpc: - type: string - description: The version of the JSON-RPC protocol. - enum: - - '2.0' - method: - type: string - description: The name of the method to invoke. - enum: - - getCompressedTokenBalancesByOwner - params: - type: object - required: - - owner - properties: - cursor: - allOf: - - $ref: '#/components/schemas/Base58String' - nullable: true - limit: - allOf: - - $ref: '#/components/schemas/Limit' - nullable: true - mint: - allOf: - - $ref: '#/components/schemas/SerializablePubkey' - nullable: true - owner: - $ref: '#/components/schemas/SerializablePubkey' - required: true - responses: - '200': - description: '' - content: - application/json: - schema: - type: object - required: - - context - - value - properties: - context: - $ref: '#/components/schemas/Context' - value: - $ref: '#/components/schemas/TokenBalanceList' - '400': - description: Invalid request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '401': - description: Unauthorized request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '403': - description: Request was forbidden. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '404': - description: The specified resource was not found. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '429': - description: Exceeded rate limit. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '500': - description: The server encountered an unexpected condition that prevented it from fulfilling the request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - /getCompressionSignaturesForAccount: - summary: getCompressionSignaturesForAccount - post: - requestBody: - content: - application/json: - schema: - type: object - required: - - jsonrpc - - id - - method - - params - properties: - id: - type: string - description: An ID to identify the request. - enum: - - test-account - jsonrpc: - type: string - description: The version of the JSON-RPC protocol. - enum: - - '2.0' - method: - type: string - description: The name of the method to invoke. - enum: - - getCompressionSignaturesForAccount - params: - type: object - required: - - hash - properties: - hash: - $ref: '#/components/schemas/Hash' - required: true - responses: - '200': - description: '' - content: - application/json: - schema: - type: object - required: - - context - - value - properties: - context: - $ref: '#/components/schemas/Context' - value: - $ref: '#/components/schemas/SignatureInfoList' - '400': - description: Invalid request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '401': - description: Unauthorized request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '403': - description: Request was forbidden. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '404': - description: The specified resource was not found. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '429': - description: Exceeded rate limit. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '500': - description: The server encountered an unexpected condition that prevented it from fulfilling the request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - /getCompressionSignaturesForAddress: - summary: getCompressionSignaturesForAddress - post: - requestBody: - content: - application/json: - schema: - type: object - required: - - jsonrpc - - id - - method - - params - properties: - id: - type: string - description: An ID to identify the request. - enum: - - test-account - jsonrpc: - type: string - description: The version of the JSON-RPC protocol. - enum: - - '2.0' - method: - type: string - description: The name of the method to invoke. - enum: - - getCompressionSignaturesForAddress - params: - type: object - required: - - address - properties: - address: - $ref: '#/components/schemas/SerializablePubkey' - cursor: - type: string - nullable: true - limit: - allOf: - - $ref: '#/components/schemas/Limit' - nullable: true - additionalProperties: false - required: true - responses: - '200': - description: '' - content: - application/json: - schema: - type: object - required: - - context - - value - properties: - context: - $ref: '#/components/schemas/Context' - value: - $ref: '#/components/schemas/PaginatedSignatureInfoList' - additionalProperties: false - '429': - description: Exceeded rate limit. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '500': - description: The server encountered an unexpected condition that prevented it from fulfilling the request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - /getMultipleCompressedAccountProofs: - summary: getMultipleCompressedAccountProofs - post: - requestBody: - content: - application/json: - schema: - type: object - required: - - jsonrpc - - id - - method - - params - properties: - id: - type: string - description: An ID to identify the request. - enum: - - test-account - jsonrpc: - type: string - description: The version of the JSON-RPC protocol. - enum: - - '2.0' - method: - type: string - description: The name of the method to invoke. - enum: - - getMultipleCompressedAccountProofs - params: - type: array - items: - $ref: '#/components/schemas/Hash' - required: true - responses: - '200': - description: '' - content: - application/json: - schema: - type: object - required: - - context - - value - properties: - context: - $ref: '#/components/schemas/Context' - value: - type: array - items: - $ref: '#/components/schemas/MerkleProofWithContext' - '400': - description: Invalid request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '401': - description: Unauthorized request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '403': - description: Request was forbidden. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '404': - description: The specified resource was not found. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '429': - description: Exceeded rate limit. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '500': - description: The server encountered an unexpected condition that prevented it from fulfilling the request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - /getMultipleCompressedAccounts: - summary: getMultipleCompressedAccounts - post: - requestBody: - content: - application/json: - schema: - type: object - required: - - jsonrpc - - id - - method - - params - properties: - id: - type: string - description: An ID to identify the request. - enum: - - test-account - jsonrpc: - type: string - description: The version of the JSON-RPC protocol. - enum: - - '2.0' - method: - type: string - description: The name of the method to invoke. - enum: - - getMultipleCompressedAccounts - params: - type: object - description: Request for compressed account data - default: - addresses: null - hashes: - - 1111111QLbz7JHiBTspS962RLKV8GndWFwiEaqKM - - 1111111ogCyDbaRMvkdsHB3qfdyFYaG1WtRUAfdh - properties: - addresses: - type: array - items: - $ref: '#/components/schemas/SerializablePubkey' - nullable: true - hashes: - type: array - items: - $ref: '#/components/schemas/Hash' - nullable: true - additionalProperties: false - example: - addresses: null - hashes: - - 1111111QLbz7JHiBTspS962RLKV8GndWFwiEaqKM - - 1111111ogCyDbaRMvkdsHB3qfdyFYaG1WtRUAfdh - required: true - responses: - '200': - description: '' - content: - application/json: - schema: - type: object - required: - - context - - value - properties: - context: - $ref: '#/components/schemas/Context' - value: - $ref: '#/components/schemas/AccountList' - '400': - description: Invalid request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '401': - description: Unauthorized request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '403': - description: Request was forbidden. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '404': - description: The specified resource was not found. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '429': - description: Exceeded rate limit. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '500': - description: The server encountered an unexpected condition that prevented it from fulfilling the request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - /getTransactionWithCompressionInfo: - summary: getTransactionWithCompressionInfo - post: - requestBody: - content: - application/json: - schema: - type: object - required: - - jsonrpc - - id - - method - - params - properties: - id: - type: string - description: An ID to identify the request. - enum: - - test-account - jsonrpc: - type: string - description: The version of the JSON-RPC protocol. - enum: - - '2.0' - method: - type: string - description: The name of the method to invoke. - enum: - - getTransactionWithCompressionInfo - params: - type: object - required: - - signature - properties: - signature: - $ref: '#/components/schemas/SerializableSignature' - required: true - responses: - '200': - description: '' - content: - application/json: - schema: - type: object - description: A Solana transaction with additional compression information - properties: - compression_info: - type: object - required: - - closed_accounts - - opened_accounts - properties: - closed_accounts: - type: array - items: - $ref: '#/components/schemas/AccountWithOptionalTokenData' - opened_accounts: - type: array - items: - $ref: '#/components/schemas/AccountWithOptionalTokenData' - transaction: - type: object - description: An encoded confirmed transaction with status meta - '400': - description: Invalid request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '401': - description: Unauthorized request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '403': - description: Request was forbidden. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '404': - description: The specified resource was not found. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '429': - description: Exceeded rate limit. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '500': - description: The server encountered an unexpected condition that prevented it from fulfilling the request. - content: - application/json: - schema: - type: object - properties: - error: - type: string -components: - schemas: - Context: - type: object - required: - - slot - properties: - slot: - type: integer - default: 100 - example: 100 - Limit: - type: integer - format: int64 - minimum: 0 - PaginatedSignatureInfoList: - type: object - required: - - items - properties: - cursor: - type: string - nullable: true - items: - type: array - items: - $ref: '#/components/schemas/SignatureInfo' - SerializablePubkey: - type: string - description: A Solana public key represented as a base58 string. - default: 11111119T6fgHG3unjQB6vpWozhBdiXDbQovvFVeF - example: 11111119T6fgHG3unjQB6vpWozhBdiXDbQovvFVeF - SerializableSignature: - type: string - description: A Solana transaction signature. - default: 5J8H5sTvEhnGcB4R8K1n7mfoiWUD9RzPVGES7e3WxC7c - example: 5J8H5sTvEhnGcB4R8K1n7mfoiWUD9RzPVGES7e3WxC7c - SignatureInfo: - type: object - required: - - signature - - slot - - blockTime - properties: - blockTime: - $ref: '#/components/schemas/UnixTimestamp' - signature: - $ref: '#/components/schemas/SerializableSignature' - slot: - $ref: '#/components/schemas/UnsignedInteger' - UnixTimestamp: - type: integer - description: An Unix timestamp (seconds) - default: 1714081554 - example: 1714081554 - UnsignedInteger: - type: integer - default: 100 - example: 100 diff --git a/src/openapi/specs/getCompressionSignaturesForOwner.yaml b/src/openapi/specs/getCompressionSignaturesForOwner.yaml deleted file mode 100644 index 5b18e040..00000000 --- a/src/openapi/specs/getCompressionSignaturesForOwner.yaml +++ /dev/null @@ -1,1750 +0,0 @@ -openapi: 3.0.3 -info: - title: photon-indexer - description: Solana indexer for general compression - license: - name: Apache-2.0 - version: 0.50.0 -servers: -- url: https://mainnet.helius-rpc.com?api-key= -paths: - /getCompressedAccount: - summary: getCompressedAccount - post: - requestBody: - content: - application/json: - schema: - type: object - required: - - jsonrpc - - id - - method - - params - properties: - id: - type: string - description: An ID to identify the request. - enum: - - test-account - jsonrpc: - type: string - description: The version of the JSON-RPC protocol. - enum: - - '2.0' - method: - type: string - description: The name of the method to invoke. - enum: - - getCompressedAccount - params: - type: object - description: Request for compressed account data - default: - address: null - hash: '11111111111111111111111111111111' - properties: - address: - allOf: - - $ref: '#/components/schemas/SerializablePubkey' - nullable: true - hash: - allOf: - - $ref: '#/components/schemas/Hash' - nullable: true - additionalProperties: false - example: - address: null - hash: '11111111111111111111111111111111' - required: true - responses: - '200': - description: '' - content: - application/json: - schema: - type: object - required: - - context - - value - properties: - context: - $ref: '#/components/schemas/Context' - value: - $ref: '#/components/schemas/Account' - '400': - description: Invalid request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '401': - description: Unauthorized request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '403': - description: Request was forbidden. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '404': - description: The specified resource was not found. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '429': - description: Exceeded rate limit. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '500': - description: The server encountered an unexpected condition that prevented it from fulfilling the request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - /getCompressedAccountProof: - summary: getCompressedAccountProof - post: - requestBody: - content: - application/json: - schema: - type: object - required: - - jsonrpc - - id - - method - - params - properties: - id: - type: string - description: An ID to identify the request. - enum: - - test-account - jsonrpc: - type: string - description: The version of the JSON-RPC protocol. - enum: - - '2.0' - method: - type: string - description: The name of the method to invoke. - enum: - - getCompressedAccountProof - params: - type: object - required: - - hash - properties: - hash: - $ref: '#/components/schemas/Hash' - required: true - responses: - '200': - description: '' - content: - application/json: - schema: - type: object - required: - - context - - value - properties: - context: - $ref: '#/components/schemas/Context' - value: - $ref: '#/components/schemas/MerkleProofWithContext' - '400': - description: Invalid request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '401': - description: Unauthorized request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '403': - description: Request was forbidden. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '404': - description: The specified resource was not found. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '429': - description: Exceeded rate limit. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '500': - description: The server encountered an unexpected condition that prevented it from fulfilling the request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - /getCompressedAccountsByOwner: - summary: getCompressedAccountsByOwner - post: - requestBody: - content: - application/json: - schema: - type: object - required: - - jsonrpc - - id - - method - - params - properties: - id: - type: string - description: An ID to identify the request. - enum: - - test-account - jsonrpc: - type: string - description: The version of the JSON-RPC protocol. - enum: - - '2.0' - method: - type: string - description: The name of the method to invoke. - enum: - - getCompressedAccountsByOwner - params: - type: object - required: - - owner - properties: - cursor: - allOf: - - $ref: '#/components/schemas/Hash' - nullable: true - limit: - allOf: - - $ref: '#/components/schemas/Limit' - nullable: true - owner: - $ref: '#/components/schemas/SerializablePubkey' - additionalProperties: false - required: true - responses: - '200': - description: '' - content: - application/json: - schema: - type: object - required: - - context - - value - properties: - context: - $ref: '#/components/schemas/Context' - value: - $ref: '#/components/schemas/PaginatedAccountList' - '400': - description: Invalid request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '401': - description: Unauthorized request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '403': - description: Request was forbidden. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '404': - description: The specified resource was not found. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '429': - description: Exceeded rate limit. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '500': - description: The server encountered an unexpected condition that prevented it from fulfilling the request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - /getCompressedBalance: - summary: getCompressedBalance - post: - requestBody: - content: - application/json: - schema: - type: object - required: - - jsonrpc - - id - - method - - params - properties: - id: - type: string - description: An ID to identify the request. - enum: - - test-account - jsonrpc: - type: string - description: The version of the JSON-RPC protocol. - enum: - - '2.0' - method: - type: string - description: The name of the method to invoke. - enum: - - getCompressedBalance - params: - type: object - description: Request for compressed account data - default: - address: null - hash: '11111111111111111111111111111111' - properties: - address: - allOf: - - $ref: '#/components/schemas/SerializablePubkey' - nullable: true - hash: - allOf: - - $ref: '#/components/schemas/Hash' - nullable: true - additionalProperties: false - example: - address: null - hash: '11111111111111111111111111111111' - required: true - responses: - '200': - description: '' - content: - application/json: - schema: - type: object - required: - - context - - value - properties: - context: - $ref: '#/components/schemas/Context' - value: - $ref: '#/components/schemas/UnsignedInteger' - '400': - description: Invalid request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '401': - description: Unauthorized request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '403': - description: Request was forbidden. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '404': - description: The specified resource was not found. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '429': - description: Exceeded rate limit. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '500': - description: The server encountered an unexpected condition that prevented it from fulfilling the request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - /getCompressedBalanceByOwner: - summary: getCompressedBalanceByOwner - post: - requestBody: - content: - application/json: - schema: - type: object - required: - - jsonrpc - - id - - method - - params - properties: - id: - type: string - description: An ID to identify the request. - enum: - - test-account - jsonrpc: - type: string - description: The version of the JSON-RPC protocol. - enum: - - '2.0' - method: - type: string - description: The name of the method to invoke. - enum: - - getCompressedBalanceByOwner - params: - type: object - required: - - owner - properties: - owner: - $ref: '#/components/schemas/SerializablePubkey' - required: true - responses: - '200': - description: '' - content: - application/json: - schema: - type: object - required: - - context - - value - properties: - context: - $ref: '#/components/schemas/Context' - value: - $ref: '#/components/schemas/UnsignedInteger' - '400': - description: Invalid request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '401': - description: Unauthorized request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '403': - description: Request was forbidden. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '404': - description: The specified resource was not found. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '429': - description: Exceeded rate limit. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '500': - description: The server encountered an unexpected condition that prevented it from fulfilling the request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - /getCompressedTokenAccountBalance: - summary: getCompressedTokenAccountBalance - post: - requestBody: - content: - application/json: - schema: - type: object - required: - - jsonrpc - - id - - method - - params - properties: - id: - type: string - description: An ID to identify the request. - enum: - - test-account - jsonrpc: - type: string - description: The version of the JSON-RPC protocol. - enum: - - '2.0' - method: - type: string - description: The name of the method to invoke. - enum: - - getCompressedTokenAccountBalance - params: - type: object - description: Request for compressed account data - default: - address: null - hash: '11111111111111111111111111111111' - properties: - address: - allOf: - - $ref: '#/components/schemas/SerializablePubkey' - nullable: true - hash: - allOf: - - $ref: '#/components/schemas/Hash' - nullable: true - additionalProperties: false - example: - address: null - hash: '11111111111111111111111111111111' - required: true - responses: - '200': - description: '' - content: - application/json: - schema: - type: object - required: - - context - - value - properties: - context: - $ref: '#/components/schemas/Context' - value: - $ref: '#/components/schemas/TokenAccountBalance' - '400': - description: Invalid request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '401': - description: Unauthorized request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '403': - description: Request was forbidden. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '404': - description: The specified resource was not found. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '429': - description: Exceeded rate limit. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '500': - description: The server encountered an unexpected condition that prevented it from fulfilling the request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - /getCompressedTokenAccountsByDelegate: - summary: getCompressedTokenAccountsByDelegate - post: - requestBody: - content: - application/json: - schema: - type: object - required: - - jsonrpc - - id - - method - - params - properties: - id: - type: string - description: An ID to identify the request. - enum: - - test-account - jsonrpc: - type: string - description: The version of the JSON-RPC protocol. - enum: - - '2.0' - method: - type: string - description: The name of the method to invoke. - enum: - - getCompressedTokenAccountsByDelegate - params: - type: object - required: - - delegate - properties: - cursor: - allOf: - - $ref: '#/components/schemas/Base58String' - nullable: true - delegate: - $ref: '#/components/schemas/SerializablePubkey' - limit: - allOf: - - $ref: '#/components/schemas/Limit' - nullable: true - mint: - allOf: - - $ref: '#/components/schemas/SerializablePubkey' - nullable: true - additionalProperties: false - required: true - responses: - '200': - description: '' - content: - application/json: - schema: - type: object - required: - - context - - value - properties: - context: - $ref: '#/components/schemas/Context' - value: - $ref: '#/components/schemas/TokenAccountList' - '400': - description: Invalid request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '401': - description: Unauthorized request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '403': - description: Request was forbidden. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '404': - description: The specified resource was not found. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '429': - description: Exceeded rate limit. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '500': - description: The server encountered an unexpected condition that prevented it from fulfilling the request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - /getCompressedTokenAccountsByOwner: - summary: getCompressedTokenAccountsByOwner - post: - requestBody: - content: - application/json: - schema: - type: object - required: - - jsonrpc - - id - - method - - params - properties: - id: - type: string - description: An ID to identify the request. - enum: - - test-account - jsonrpc: - type: string - description: The version of the JSON-RPC protocol. - enum: - - '2.0' - method: - type: string - description: The name of the method to invoke. - enum: - - getCompressedTokenAccountsByOwner - params: - type: object - required: - - owner - properties: - cursor: - allOf: - - $ref: '#/components/schemas/Base58String' - nullable: true - limit: - allOf: - - $ref: '#/components/schemas/Limit' - nullable: true - mint: - allOf: - - $ref: '#/components/schemas/SerializablePubkey' - nullable: true - owner: - $ref: '#/components/schemas/SerializablePubkey' - additionalProperties: false - required: true - responses: - '200': - description: '' - content: - application/json: - schema: - type: object - required: - - context - - value - properties: - context: - $ref: '#/components/schemas/Context' - value: - $ref: '#/components/schemas/TokenAccountList' - '400': - description: Invalid request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '401': - description: Unauthorized request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '403': - description: Request was forbidden. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '404': - description: The specified resource was not found. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '429': - description: Exceeded rate limit. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '500': - description: The server encountered an unexpected condition that prevented it from fulfilling the request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - /getCompressedTokenBalancesByOwner: - summary: getCompressedTokenBalancesByOwner - post: - requestBody: - content: - application/json: - schema: - type: object - required: - - jsonrpc - - id - - method - - params - properties: - id: - type: string - description: An ID to identify the request. - enum: - - test-account - jsonrpc: - type: string - description: The version of the JSON-RPC protocol. - enum: - - '2.0' - method: - type: string - description: The name of the method to invoke. - enum: - - getCompressedTokenBalancesByOwner - params: - type: object - required: - - owner - properties: - cursor: - allOf: - - $ref: '#/components/schemas/Base58String' - nullable: true - limit: - allOf: - - $ref: '#/components/schemas/Limit' - nullable: true - mint: - allOf: - - $ref: '#/components/schemas/SerializablePubkey' - nullable: true - owner: - $ref: '#/components/schemas/SerializablePubkey' - required: true - responses: - '200': - description: '' - content: - application/json: - schema: - type: object - required: - - context - - value - properties: - context: - $ref: '#/components/schemas/Context' - value: - $ref: '#/components/schemas/TokenBalanceList' - '400': - description: Invalid request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '401': - description: Unauthorized request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '403': - description: Request was forbidden. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '404': - description: The specified resource was not found. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '429': - description: Exceeded rate limit. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '500': - description: The server encountered an unexpected condition that prevented it from fulfilling the request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - /getCompressionSignaturesForAccount: - summary: getCompressionSignaturesForAccount - post: - requestBody: - content: - application/json: - schema: - type: object - required: - - jsonrpc - - id - - method - - params - properties: - id: - type: string - description: An ID to identify the request. - enum: - - test-account - jsonrpc: - type: string - description: The version of the JSON-RPC protocol. - enum: - - '2.0' - method: - type: string - description: The name of the method to invoke. - enum: - - getCompressionSignaturesForAccount - params: - type: object - required: - - hash - properties: - hash: - $ref: '#/components/schemas/Hash' - required: true - responses: - '200': - description: '' - content: - application/json: - schema: - type: object - required: - - context - - value - properties: - context: - $ref: '#/components/schemas/Context' - value: - $ref: '#/components/schemas/SignatureInfoList' - '400': - description: Invalid request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '401': - description: Unauthorized request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '403': - description: Request was forbidden. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '404': - description: The specified resource was not found. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '429': - description: Exceeded rate limit. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '500': - description: The server encountered an unexpected condition that prevented it from fulfilling the request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - /getCompressionSignaturesForAddress: - summary: getCompressionSignaturesForAddress - post: - requestBody: - content: - application/json: - schema: - type: object - required: - - jsonrpc - - id - - method - - params - properties: - id: - type: string - description: An ID to identify the request. - enum: - - test-account - jsonrpc: - type: string - description: The version of the JSON-RPC protocol. - enum: - - '2.0' - method: - type: string - description: The name of the method to invoke. - enum: - - getCompressionSignaturesForAddress - params: - type: object - required: - - address - properties: - address: - $ref: '#/components/schemas/SerializablePubkey' - cursor: - type: string - nullable: true - limit: - allOf: - - $ref: '#/components/schemas/Limit' - nullable: true - required: true - responses: - '200': - description: '' - content: - application/json: - schema: - type: object - required: - - context - - value - properties: - context: - $ref: '#/components/schemas/Context' - value: - $ref: '#/components/schemas/PaginatedSignatureInfoList' - '400': - description: Invalid request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '401': - description: Unauthorized request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '403': - description: Request was forbidden. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '404': - description: The specified resource was not found. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '429': - description: Exceeded rate limit. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '500': - description: The server encountered an unexpected condition that prevented it from fulfilling the request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - /getCompressionSignaturesForOwner: - summary: getCompressionSignaturesForOwner - post: - requestBody: - content: - application/json: - schema: - type: object - required: - - jsonrpc - - id - - method - - params - properties: - id: - type: string - description: An ID to identify the request. - enum: - - test-account - jsonrpc: - type: string - description: The version of the JSON-RPC protocol. - enum: - - '2.0' - method: - type: string - description: The name of the method to invoke. - enum: - - getCompressionSignaturesForOwner - params: - type: object - required: - - owner - properties: - cursor: - type: string - nullable: true - limit: - allOf: - - $ref: '#/components/schemas/Limit' - nullable: true - owner: - $ref: '#/components/schemas/SerializablePubkey' - additionalProperties: false - required: true - responses: - '200': - description: '' - content: - application/json: - schema: - type: object - required: - - context - - value - properties: - context: - $ref: '#/components/schemas/Context' - value: - $ref: '#/components/schemas/PaginatedSignatureInfoList' - additionalProperties: false - '429': - description: Exceeded rate limit. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '500': - description: The server encountered an unexpected condition that prevented it from fulfilling the request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - /getMultipleCompressedAccountProofs: - summary: getMultipleCompressedAccountProofs - post: - requestBody: - content: - application/json: - schema: - type: object - required: - - jsonrpc - - id - - method - - params - properties: - id: - type: string - description: An ID to identify the request. - enum: - - test-account - jsonrpc: - type: string - description: The version of the JSON-RPC protocol. - enum: - - '2.0' - method: - type: string - description: The name of the method to invoke. - enum: - - getMultipleCompressedAccountProofs - params: - type: array - items: - $ref: '#/components/schemas/Hash' - required: true - responses: - '200': - description: '' - content: - application/json: - schema: - type: object - required: - - context - - value - properties: - context: - $ref: '#/components/schemas/Context' - value: - type: array - items: - $ref: '#/components/schemas/MerkleProofWithContext' - '400': - description: Invalid request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '401': - description: Unauthorized request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '403': - description: Request was forbidden. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '404': - description: The specified resource was not found. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '429': - description: Exceeded rate limit. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '500': - description: The server encountered an unexpected condition that prevented it from fulfilling the request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - /getMultipleCompressedAccounts: - summary: getMultipleCompressedAccounts - post: - requestBody: - content: - application/json: - schema: - type: object - required: - - jsonrpc - - id - - method - - params - properties: - id: - type: string - description: An ID to identify the request. - enum: - - test-account - jsonrpc: - type: string - description: The version of the JSON-RPC protocol. - enum: - - '2.0' - method: - type: string - description: The name of the method to invoke. - enum: - - getMultipleCompressedAccounts - params: - type: object - description: Request for compressed account data - default: - addresses: null - hashes: - - 1111111QLbz7JHiBTspS962RLKV8GndWFwiEaqKM - - 1111111ogCyDbaRMvkdsHB3qfdyFYaG1WtRUAfdh - properties: - addresses: - type: array - items: - $ref: '#/components/schemas/SerializablePubkey' - nullable: true - hashes: - type: array - items: - $ref: '#/components/schemas/Hash' - nullable: true - additionalProperties: false - example: - addresses: null - hashes: - - 1111111QLbz7JHiBTspS962RLKV8GndWFwiEaqKM - - 1111111ogCyDbaRMvkdsHB3qfdyFYaG1WtRUAfdh - required: true - responses: - '200': - description: '' - content: - application/json: - schema: - type: object - required: - - context - - value - properties: - context: - $ref: '#/components/schemas/Context' - value: - $ref: '#/components/schemas/AccountList' - '400': - description: Invalid request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '401': - description: Unauthorized request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '403': - description: Request was forbidden. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '404': - description: The specified resource was not found. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '429': - description: Exceeded rate limit. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '500': - description: The server encountered an unexpected condition that prevented it from fulfilling the request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - /getTransactionWithCompressionInfo: - summary: getTransactionWithCompressionInfo - post: - requestBody: - content: - application/json: - schema: - type: object - required: - - jsonrpc - - id - - method - - params - properties: - id: - type: string - description: An ID to identify the request. - enum: - - test-account - jsonrpc: - type: string - description: The version of the JSON-RPC protocol. - enum: - - '2.0' - method: - type: string - description: The name of the method to invoke. - enum: - - getTransactionWithCompressionInfo - params: - type: object - required: - - signature - properties: - signature: - $ref: '#/components/schemas/SerializableSignature' - required: true - responses: - '200': - description: '' - content: - application/json: - schema: - type: object - description: A Solana transaction with additional compression information - properties: - compression_info: - type: object - required: - - closed_accounts - - opened_accounts - properties: - closed_accounts: - type: array - items: - $ref: '#/components/schemas/AccountWithOptionalTokenData' - opened_accounts: - type: array - items: - $ref: '#/components/schemas/AccountWithOptionalTokenData' - transaction: - type: object - description: An encoded confirmed transaction with status meta - '400': - description: Invalid request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '401': - description: Unauthorized request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '403': - description: Request was forbidden. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '404': - description: The specified resource was not found. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '429': - description: Exceeded rate limit. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '500': - description: The server encountered an unexpected condition that prevented it from fulfilling the request. - content: - application/json: - schema: - type: object - properties: - error: - type: string -components: - schemas: - Context: - type: object - required: - - slot - properties: - slot: - type: integer - default: 100 - example: 100 - Limit: - type: integer - format: int64 - minimum: 0 - PaginatedSignatureInfoList: - type: object - required: - - items - properties: - cursor: - type: string - nullable: true - items: - type: array - items: - $ref: '#/components/schemas/SignatureInfo' - SerializablePubkey: - type: string - description: A Solana public key represented as a base58 string. - default: 11111119rSGfPZLcyCGzY4uYEL1fkzJr6fke9qKxb - example: 11111119rSGfPZLcyCGzY4uYEL1fkzJr6fke9qKxb - SerializableSignature: - type: string - description: A Solana transaction signature. - default: 5J8H5sTvEhnGcB4R8K1n7mfoiWUD9RzPVGES7e3WxC7c - example: 5J8H5sTvEhnGcB4R8K1n7mfoiWUD9RzPVGES7e3WxC7c - SignatureInfo: - type: object - required: - - signature - - slot - - blockTime - properties: - blockTime: - $ref: '#/components/schemas/UnixTimestamp' - signature: - $ref: '#/components/schemas/SerializableSignature' - slot: - $ref: '#/components/schemas/UnsignedInteger' - UnixTimestamp: - type: integer - description: An Unix timestamp (seconds) - default: 1714081554 - example: 1714081554 - UnsignedInteger: - type: integer - default: 100 - example: 100 diff --git a/src/openapi/specs/getCompressionSignaturesForTokenOwner.yaml b/src/openapi/specs/getCompressionSignaturesForTokenOwner.yaml deleted file mode 100644 index b5407929..00000000 --- a/src/openapi/specs/getCompressionSignaturesForTokenOwner.yaml +++ /dev/null @@ -1,1863 +0,0 @@ -openapi: 3.0.3 -info: - title: photon-indexer - description: Solana indexer for general compression - license: - name: Apache-2.0 - version: 0.50.0 -servers: -- url: https://mainnet.helius-rpc.com?api-key= -paths: - /getCompressedAccount: - summary: getCompressedAccount - post: - requestBody: - content: - application/json: - schema: - type: object - required: - - jsonrpc - - id - - method - - params - properties: - id: - type: string - description: An ID to identify the request. - enum: - - test-account - jsonrpc: - type: string - description: The version of the JSON-RPC protocol. - enum: - - '2.0' - method: - type: string - description: The name of the method to invoke. - enum: - - getCompressedAccount - params: - type: object - description: Request for compressed account data - default: - address: null - hash: '11111111111111111111111111111111' - properties: - address: - allOf: - - $ref: '#/components/schemas/SerializablePubkey' - nullable: true - hash: - allOf: - - $ref: '#/components/schemas/Hash' - nullable: true - additionalProperties: false - example: - address: null - hash: '11111111111111111111111111111111' - required: true - responses: - '200': - description: '' - content: - application/json: - schema: - type: object - required: - - context - - value - properties: - context: - $ref: '#/components/schemas/Context' - value: - $ref: '#/components/schemas/Account' - '400': - description: Invalid request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '401': - description: Unauthorized request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '403': - description: Request was forbidden. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '404': - description: The specified resource was not found. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '429': - description: Exceeded rate limit. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '500': - description: The server encountered an unexpected condition that prevented it from fulfilling the request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - /getCompressedAccountProof: - summary: getCompressedAccountProof - post: - requestBody: - content: - application/json: - schema: - type: object - required: - - jsonrpc - - id - - method - - params - properties: - id: - type: string - description: An ID to identify the request. - enum: - - test-account - jsonrpc: - type: string - description: The version of the JSON-RPC protocol. - enum: - - '2.0' - method: - type: string - description: The name of the method to invoke. - enum: - - getCompressedAccountProof - params: - type: object - required: - - hash - properties: - hash: - $ref: '#/components/schemas/Hash' - required: true - responses: - '200': - description: '' - content: - application/json: - schema: - type: object - required: - - context - - value - properties: - context: - $ref: '#/components/schemas/Context' - value: - $ref: '#/components/schemas/MerkleProofWithContext' - '400': - description: Invalid request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '401': - description: Unauthorized request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '403': - description: Request was forbidden. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '404': - description: The specified resource was not found. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '429': - description: Exceeded rate limit. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '500': - description: The server encountered an unexpected condition that prevented it from fulfilling the request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - /getCompressedAccountsByOwner: - summary: getCompressedAccountsByOwner - post: - requestBody: - content: - application/json: - schema: - type: object - required: - - jsonrpc - - id - - method - - params - properties: - id: - type: string - description: An ID to identify the request. - enum: - - test-account - jsonrpc: - type: string - description: The version of the JSON-RPC protocol. - enum: - - '2.0' - method: - type: string - description: The name of the method to invoke. - enum: - - getCompressedAccountsByOwner - params: - type: object - required: - - owner - properties: - cursor: - allOf: - - $ref: '#/components/schemas/Hash' - nullable: true - limit: - allOf: - - $ref: '#/components/schemas/Limit' - nullable: true - owner: - $ref: '#/components/schemas/SerializablePubkey' - additionalProperties: false - required: true - responses: - '200': - description: '' - content: - application/json: - schema: - type: object - required: - - context - - value - properties: - context: - $ref: '#/components/schemas/Context' - value: - $ref: '#/components/schemas/PaginatedAccountList' - '400': - description: Invalid request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '401': - description: Unauthorized request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '403': - description: Request was forbidden. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '404': - description: The specified resource was not found. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '429': - description: Exceeded rate limit. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '500': - description: The server encountered an unexpected condition that prevented it from fulfilling the request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - /getCompressedBalance: - summary: getCompressedBalance - post: - requestBody: - content: - application/json: - schema: - type: object - required: - - jsonrpc - - id - - method - - params - properties: - id: - type: string - description: An ID to identify the request. - enum: - - test-account - jsonrpc: - type: string - description: The version of the JSON-RPC protocol. - enum: - - '2.0' - method: - type: string - description: The name of the method to invoke. - enum: - - getCompressedBalance - params: - type: object - description: Request for compressed account data - default: - address: null - hash: '11111111111111111111111111111111' - properties: - address: - allOf: - - $ref: '#/components/schemas/SerializablePubkey' - nullable: true - hash: - allOf: - - $ref: '#/components/schemas/Hash' - nullable: true - additionalProperties: false - example: - address: null - hash: '11111111111111111111111111111111' - required: true - responses: - '200': - description: '' - content: - application/json: - schema: - type: object - required: - - context - - value - properties: - context: - $ref: '#/components/schemas/Context' - value: - $ref: '#/components/schemas/UnsignedInteger' - '400': - description: Invalid request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '401': - description: Unauthorized request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '403': - description: Request was forbidden. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '404': - description: The specified resource was not found. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '429': - description: Exceeded rate limit. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '500': - description: The server encountered an unexpected condition that prevented it from fulfilling the request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - /getCompressedBalanceByOwner: - summary: getCompressedBalanceByOwner - post: - requestBody: - content: - application/json: - schema: - type: object - required: - - jsonrpc - - id - - method - - params - properties: - id: - type: string - description: An ID to identify the request. - enum: - - test-account - jsonrpc: - type: string - description: The version of the JSON-RPC protocol. - enum: - - '2.0' - method: - type: string - description: The name of the method to invoke. - enum: - - getCompressedBalanceByOwner - params: - type: object - required: - - owner - properties: - owner: - $ref: '#/components/schemas/SerializablePubkey' - required: true - responses: - '200': - description: '' - content: - application/json: - schema: - type: object - required: - - context - - value - properties: - context: - $ref: '#/components/schemas/Context' - value: - $ref: '#/components/schemas/UnsignedInteger' - '400': - description: Invalid request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '401': - description: Unauthorized request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '403': - description: Request was forbidden. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '404': - description: The specified resource was not found. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '429': - description: Exceeded rate limit. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '500': - description: The server encountered an unexpected condition that prevented it from fulfilling the request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - /getCompressedTokenAccountBalance: - summary: getCompressedTokenAccountBalance - post: - requestBody: - content: - application/json: - schema: - type: object - required: - - jsonrpc - - id - - method - - params - properties: - id: - type: string - description: An ID to identify the request. - enum: - - test-account - jsonrpc: - type: string - description: The version of the JSON-RPC protocol. - enum: - - '2.0' - method: - type: string - description: The name of the method to invoke. - enum: - - getCompressedTokenAccountBalance - params: - type: object - description: Request for compressed account data - default: - address: null - hash: '11111111111111111111111111111111' - properties: - address: - allOf: - - $ref: '#/components/schemas/SerializablePubkey' - nullable: true - hash: - allOf: - - $ref: '#/components/schemas/Hash' - nullable: true - additionalProperties: false - example: - address: null - hash: '11111111111111111111111111111111' - required: true - responses: - '200': - description: '' - content: - application/json: - schema: - type: object - required: - - context - - value - properties: - context: - $ref: '#/components/schemas/Context' - value: - $ref: '#/components/schemas/TokenAccountBalance' - '400': - description: Invalid request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '401': - description: Unauthorized request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '403': - description: Request was forbidden. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '404': - description: The specified resource was not found. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '429': - description: Exceeded rate limit. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '500': - description: The server encountered an unexpected condition that prevented it from fulfilling the request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - /getCompressedTokenAccountsByDelegate: - summary: getCompressedTokenAccountsByDelegate - post: - requestBody: - content: - application/json: - schema: - type: object - required: - - jsonrpc - - id - - method - - params - properties: - id: - type: string - description: An ID to identify the request. - enum: - - test-account - jsonrpc: - type: string - description: The version of the JSON-RPC protocol. - enum: - - '2.0' - method: - type: string - description: The name of the method to invoke. - enum: - - getCompressedTokenAccountsByDelegate - params: - type: object - required: - - delegate - properties: - cursor: - allOf: - - $ref: '#/components/schemas/Base58String' - nullable: true - delegate: - $ref: '#/components/schemas/SerializablePubkey' - limit: - allOf: - - $ref: '#/components/schemas/Limit' - nullable: true - mint: - allOf: - - $ref: '#/components/schemas/SerializablePubkey' - nullable: true - additionalProperties: false - required: true - responses: - '200': - description: '' - content: - application/json: - schema: - type: object - required: - - context - - value - properties: - context: - $ref: '#/components/schemas/Context' - value: - $ref: '#/components/schemas/TokenAccountList' - '400': - description: Invalid request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '401': - description: Unauthorized request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '403': - description: Request was forbidden. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '404': - description: The specified resource was not found. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '429': - description: Exceeded rate limit. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '500': - description: The server encountered an unexpected condition that prevented it from fulfilling the request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - /getCompressedTokenAccountsByOwner: - summary: getCompressedTokenAccountsByOwner - post: - requestBody: - content: - application/json: - schema: - type: object - required: - - jsonrpc - - id - - method - - params - properties: - id: - type: string - description: An ID to identify the request. - enum: - - test-account - jsonrpc: - type: string - description: The version of the JSON-RPC protocol. - enum: - - '2.0' - method: - type: string - description: The name of the method to invoke. - enum: - - getCompressedTokenAccountsByOwner - params: - type: object - required: - - owner - properties: - cursor: - allOf: - - $ref: '#/components/schemas/Base58String' - nullable: true - limit: - allOf: - - $ref: '#/components/schemas/Limit' - nullable: true - mint: - allOf: - - $ref: '#/components/schemas/SerializablePubkey' - nullable: true - owner: - $ref: '#/components/schemas/SerializablePubkey' - additionalProperties: false - required: true - responses: - '200': - description: '' - content: - application/json: - schema: - type: object - required: - - context - - value - properties: - context: - $ref: '#/components/schemas/Context' - value: - $ref: '#/components/schemas/TokenAccountList' - '400': - description: Invalid request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '401': - description: Unauthorized request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '403': - description: Request was forbidden. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '404': - description: The specified resource was not found. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '429': - description: Exceeded rate limit. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '500': - description: The server encountered an unexpected condition that prevented it from fulfilling the request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - /getCompressedTokenBalancesByOwner: - summary: getCompressedTokenBalancesByOwner - post: - requestBody: - content: - application/json: - schema: - type: object - required: - - jsonrpc - - id - - method - - params - properties: - id: - type: string - description: An ID to identify the request. - enum: - - test-account - jsonrpc: - type: string - description: The version of the JSON-RPC protocol. - enum: - - '2.0' - method: - type: string - description: The name of the method to invoke. - enum: - - getCompressedTokenBalancesByOwner - params: - type: object - required: - - owner - properties: - cursor: - allOf: - - $ref: '#/components/schemas/Base58String' - nullable: true - limit: - allOf: - - $ref: '#/components/schemas/Limit' - nullable: true - mint: - allOf: - - $ref: '#/components/schemas/SerializablePubkey' - nullable: true - owner: - $ref: '#/components/schemas/SerializablePubkey' - required: true - responses: - '200': - description: '' - content: - application/json: - schema: - type: object - required: - - context - - value - properties: - context: - $ref: '#/components/schemas/Context' - value: - $ref: '#/components/schemas/TokenBalanceList' - '400': - description: Invalid request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '401': - description: Unauthorized request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '403': - description: Request was forbidden. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '404': - description: The specified resource was not found. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '429': - description: Exceeded rate limit. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '500': - description: The server encountered an unexpected condition that prevented it from fulfilling the request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - /getCompressionSignaturesForAccount: - summary: getCompressionSignaturesForAccount - post: - requestBody: - content: - application/json: - schema: - type: object - required: - - jsonrpc - - id - - method - - params - properties: - id: - type: string - description: An ID to identify the request. - enum: - - test-account - jsonrpc: - type: string - description: The version of the JSON-RPC protocol. - enum: - - '2.0' - method: - type: string - description: The name of the method to invoke. - enum: - - getCompressionSignaturesForAccount - params: - type: object - required: - - hash - properties: - hash: - $ref: '#/components/schemas/Hash' - required: true - responses: - '200': - description: '' - content: - application/json: - schema: - type: object - required: - - context - - value - properties: - context: - $ref: '#/components/schemas/Context' - value: - $ref: '#/components/schemas/SignatureInfoList' - '400': - description: Invalid request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '401': - description: Unauthorized request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '403': - description: Request was forbidden. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '404': - description: The specified resource was not found. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '429': - description: Exceeded rate limit. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '500': - description: The server encountered an unexpected condition that prevented it from fulfilling the request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - /getCompressionSignaturesForAddress: - summary: getCompressionSignaturesForAddress - post: - requestBody: - content: - application/json: - schema: - type: object - required: - - jsonrpc - - id - - method - - params - properties: - id: - type: string - description: An ID to identify the request. - enum: - - test-account - jsonrpc: - type: string - description: The version of the JSON-RPC protocol. - enum: - - '2.0' - method: - type: string - description: The name of the method to invoke. - enum: - - getCompressionSignaturesForAddress - params: - type: object - required: - - address - properties: - address: - $ref: '#/components/schemas/SerializablePubkey' - cursor: - type: string - nullable: true - limit: - allOf: - - $ref: '#/components/schemas/Limit' - nullable: true - required: true - responses: - '200': - description: '' - content: - application/json: - schema: - type: object - required: - - context - - value - properties: - context: - $ref: '#/components/schemas/Context' - value: - $ref: '#/components/schemas/PaginatedSignatureInfoList' - '400': - description: Invalid request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '401': - description: Unauthorized request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '403': - description: Request was forbidden. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '404': - description: The specified resource was not found. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '429': - description: Exceeded rate limit. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '500': - description: The server encountered an unexpected condition that prevented it from fulfilling the request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - /getCompressionSignaturesForOwner: - summary: getCompressionSignaturesForOwner - post: - requestBody: - content: - application/json: - schema: - type: object - required: - - jsonrpc - - id - - method - - params - properties: - id: - type: string - description: An ID to identify the request. - enum: - - test-account - jsonrpc: - type: string - description: The version of the JSON-RPC protocol. - enum: - - '2.0' - method: - type: string - description: The name of the method to invoke. - enum: - - getCompressionSignaturesForOwner - params: - type: object - required: - - owner - properties: - cursor: - type: string - nullable: true - limit: - allOf: - - $ref: '#/components/schemas/Limit' - nullable: true - owner: - $ref: '#/components/schemas/SerializablePubkey' - required: true - responses: - '200': - description: '' - content: - application/json: - schema: - type: object - required: - - context - - value - properties: - context: - $ref: '#/components/schemas/Context' - value: - $ref: '#/components/schemas/PaginatedSignatureInfoList' - '400': - description: Invalid request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '401': - description: Unauthorized request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '403': - description: Request was forbidden. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '404': - description: The specified resource was not found. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '429': - description: Exceeded rate limit. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '500': - description: The server encountered an unexpected condition that prevented it from fulfilling the request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - /getCompressionSignaturesForTokenOwner: - summary: getCompressionSignaturesForTokenOwner - post: - requestBody: - content: - application/json: - schema: - type: object - required: - - jsonrpc - - id - - method - - params - properties: - id: - type: string - description: An ID to identify the request. - enum: - - test-account - jsonrpc: - type: string - description: The version of the JSON-RPC protocol. - enum: - - '2.0' - method: - type: string - description: The name of the method to invoke. - enum: - - getCompressionSignaturesForTokenOwner - params: - type: object - required: - - owner - properties: - cursor: - type: string - nullable: true - limit: - allOf: - - $ref: '#/components/schemas/Limit' - nullable: true - owner: - $ref: '#/components/schemas/SerializablePubkey' - additionalProperties: false - required: true - responses: - '200': - description: '' - content: - application/json: - schema: - type: object - required: - - context - - value - properties: - context: - $ref: '#/components/schemas/Context' - value: - $ref: '#/components/schemas/PaginatedSignatureInfoList' - additionalProperties: false - '429': - description: Exceeded rate limit. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '500': - description: The server encountered an unexpected condition that prevented it from fulfilling the request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - /getMultipleCompressedAccountProofs: - summary: getMultipleCompressedAccountProofs - post: - requestBody: - content: - application/json: - schema: - type: object - required: - - jsonrpc - - id - - method - - params - properties: - id: - type: string - description: An ID to identify the request. - enum: - - test-account - jsonrpc: - type: string - description: The version of the JSON-RPC protocol. - enum: - - '2.0' - method: - type: string - description: The name of the method to invoke. - enum: - - getMultipleCompressedAccountProofs - params: - type: array - items: - $ref: '#/components/schemas/Hash' - required: true - responses: - '200': - description: '' - content: - application/json: - schema: - type: object - required: - - context - - value - properties: - context: - $ref: '#/components/schemas/Context' - value: - type: array - items: - $ref: '#/components/schemas/MerkleProofWithContext' - '400': - description: Invalid request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '401': - description: Unauthorized request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '403': - description: Request was forbidden. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '404': - description: The specified resource was not found. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '429': - description: Exceeded rate limit. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '500': - description: The server encountered an unexpected condition that prevented it from fulfilling the request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - /getMultipleCompressedAccounts: - summary: getMultipleCompressedAccounts - post: - requestBody: - content: - application/json: - schema: - type: object - required: - - jsonrpc - - id - - method - - params - properties: - id: - type: string - description: An ID to identify the request. - enum: - - test-account - jsonrpc: - type: string - description: The version of the JSON-RPC protocol. - enum: - - '2.0' - method: - type: string - description: The name of the method to invoke. - enum: - - getMultipleCompressedAccounts - params: - type: object - description: Request for compressed account data - default: - addresses: null - hashes: - - 1111111QLbz7JHiBTspS962RLKV8GndWFwiEaqKM - - 1111111ogCyDbaRMvkdsHB3qfdyFYaG1WtRUAfdh - properties: - addresses: - type: array - items: - $ref: '#/components/schemas/SerializablePubkey' - nullable: true - hashes: - type: array - items: - $ref: '#/components/schemas/Hash' - nullable: true - additionalProperties: false - example: - addresses: null - hashes: - - 1111111QLbz7JHiBTspS962RLKV8GndWFwiEaqKM - - 1111111ogCyDbaRMvkdsHB3qfdyFYaG1WtRUAfdh - required: true - responses: - '200': - description: '' - content: - application/json: - schema: - type: object - required: - - context - - value - properties: - context: - $ref: '#/components/schemas/Context' - value: - $ref: '#/components/schemas/AccountList' - '400': - description: Invalid request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '401': - description: Unauthorized request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '403': - description: Request was forbidden. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '404': - description: The specified resource was not found. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '429': - description: Exceeded rate limit. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '500': - description: The server encountered an unexpected condition that prevented it from fulfilling the request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - /getTransactionWithCompressionInfo: - summary: getTransactionWithCompressionInfo - post: - requestBody: - content: - application/json: - schema: - type: object - required: - - jsonrpc - - id - - method - - params - properties: - id: - type: string - description: An ID to identify the request. - enum: - - test-account - jsonrpc: - type: string - description: The version of the JSON-RPC protocol. - enum: - - '2.0' - method: - type: string - description: The name of the method to invoke. - enum: - - getTransactionWithCompressionInfo - params: - type: object - required: - - signature - properties: - signature: - $ref: '#/components/schemas/SerializableSignature' - required: true - responses: - '200': - description: '' - content: - application/json: - schema: - type: object - description: A Solana transaction with additional compression information - properties: - compression_info: - type: object - required: - - closed_accounts - - opened_accounts - properties: - closed_accounts: - type: array - items: - $ref: '#/components/schemas/AccountWithOptionalTokenData' - opened_accounts: - type: array - items: - $ref: '#/components/schemas/AccountWithOptionalTokenData' - transaction: - type: object - description: An encoded confirmed transaction with status meta - '400': - description: Invalid request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '401': - description: Unauthorized request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '403': - description: Request was forbidden. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '404': - description: The specified resource was not found. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '429': - description: Exceeded rate limit. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '500': - description: The server encountered an unexpected condition that prevented it from fulfilling the request. - content: - application/json: - schema: - type: object - properties: - error: - type: string -components: - schemas: - Context: - type: object - required: - - slot - properties: - slot: - type: integer - default: 100 - example: 100 - Limit: - type: integer - format: int64 - minimum: 0 - PaginatedSignatureInfoList: - type: object - required: - - items - properties: - cursor: - type: string - nullable: true - items: - type: array - items: - $ref: '#/components/schemas/SignatureInfo' - SerializablePubkey: - type: string - description: A Solana public key represented as a base58 string. - default: 1111111AFmseVrdL9f9oyCzZefL9tG6UbvhMPRAGw - example: 1111111AFmseVrdL9f9oyCzZefL9tG6UbvhMPRAGw - SerializableSignature: - type: string - description: A Solana transaction signature. - default: 5J8H5sTvEhnGcB4R8K1n7mfoiWUD9RzPVGES7e3WxC7c - example: 5J8H5sTvEhnGcB4R8K1n7mfoiWUD9RzPVGES7e3WxC7c - SignatureInfo: - type: object - required: - - signature - - slot - - blockTime - properties: - blockTime: - $ref: '#/components/schemas/UnixTimestamp' - signature: - $ref: '#/components/schemas/SerializableSignature' - slot: - $ref: '#/components/schemas/UnsignedInteger' - UnixTimestamp: - type: integer - description: An Unix timestamp (seconds) - default: 1714081554 - example: 1714081554 - UnsignedInteger: - type: integer - default: 100 - example: 100 diff --git a/src/openapi/specs/getIndexerHealth.yaml b/src/openapi/specs/getIndexerHealth.yaml deleted file mode 100644 index d40866cb..00000000 --- a/src/openapi/specs/getIndexerHealth.yaml +++ /dev/null @@ -1,69 +0,0 @@ -openapi: 3.0.3 -info: - title: photon-indexer - description: Solana indexer for general compression - license: - name: Apache-2.0 - version: 0.50.0 -servers: -- url: https://mainnet.helius-rpc.com?api-key= -paths: - /: - summary: getIndexerHealth - post: - requestBody: - content: - application/json: - schema: - type: object - required: - - jsonrpc - - id - - method - properties: - id: - type: string - description: An ID to identify the request. - enum: - - test-account - jsonrpc: - type: string - description: The version of the JSON-RPC protocol. - enum: - - '2.0' - method: - type: string - description: The name of the method to invoke. - enum: - - getIndexerHealth - required: true - responses: - '200': - description: '' - content: - application/json: - schema: - type: string - description: ok if healthy - default: ok - enum: - - ok - '429': - description: Exceeded rate limit. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '500': - description: The server encountered an unexpected condition that prevented it from fulfilling the request. - content: - application/json: - schema: - type: object - properties: - error: - type: string -components: {} diff --git a/src/openapi/specs/getIndexerSlot.yaml b/src/openapi/specs/getIndexerSlot.yaml deleted file mode 100644 index 29edaa6d..00000000 --- a/src/openapi/specs/getIndexerSlot.yaml +++ /dev/null @@ -1,67 +0,0 @@ -openapi: 3.0.3 -info: - title: photon-indexer - description: Solana indexer for general compression - license: - name: Apache-2.0 - version: 0.50.0 -servers: -- url: https://mainnet.helius-rpc.com?api-key= -paths: - /: - summary: getIndexerSlot - post: - requestBody: - content: - application/json: - schema: - type: object - required: - - jsonrpc - - id - - method - properties: - id: - type: string - description: An ID to identify the request. - enum: - - test-account - jsonrpc: - type: string - description: The version of the JSON-RPC protocol. - enum: - - '2.0' - method: - type: string - description: The name of the method to invoke. - enum: - - getIndexerSlot - required: true - responses: - '200': - description: '' - content: - application/json: - schema: - type: integer - default: 100 - example: 100 - '429': - description: Exceeded rate limit. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '500': - description: The server encountered an unexpected condition that prevented it from fulfilling the request. - content: - application/json: - schema: - type: object - properties: - error: - type: string -components: {} diff --git a/src/openapi/specs/getLatestCompressionSignatures.yaml b/src/openapi/specs/getLatestCompressionSignatures.yaml deleted file mode 100644 index 902f34b9..00000000 --- a/src/openapi/specs/getLatestCompressionSignatures.yaml +++ /dev/null @@ -1,139 +0,0 @@ -openapi: 3.0.3 -info: - title: photon-indexer - description: Solana indexer for general compression - license: - name: Apache-2.0 - version: 0.50.0 -servers: -- url: https://mainnet.helius-rpc.com?api-key= -paths: - /: - summary: getLatestCompressionSignatures - post: - requestBody: - content: - application/json: - schema: - type: object - required: - - jsonrpc - - id - - method - - params - properties: - id: - type: string - description: An ID to identify the request. - enum: - - test-account - jsonrpc: - type: string - description: The version of the JSON-RPC protocol. - enum: - - '2.0' - method: - type: string - description: The name of the method to invoke. - enum: - - getLatestCompressionSignatures - params: - type: object - properties: - cursor: - type: string - nullable: true - limit: - allOf: - - $ref: '#/components/schemas/Limit' - nullable: true - additionalProperties: false - required: true - responses: - '200': - description: '' - content: - application/json: - schema: - type: object - required: - - context - - value - properties: - context: - $ref: '#/components/schemas/Context' - value: - $ref: '#/components/schemas/PaginatedSignatureInfoList' - additionalProperties: false - '429': - description: Exceeded rate limit. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '500': - description: The server encountered an unexpected condition that prevented it from fulfilling the request. - content: - application/json: - schema: - type: object - properties: - error: - type: string -components: - schemas: - Context: - type: object - required: - - slot - properties: - slot: - type: integer - default: 100 - example: 100 - Limit: - type: integer - format: int64 - minimum: 0 - PaginatedSignatureInfoList: - type: object - required: - - items - properties: - cursor: - type: string - nullable: true - items: - type: array - items: - $ref: '#/components/schemas/SignatureInfo' - SerializableSignature: - type: string - description: A Solana transaction signature. - default: 5J8H5sTvEhnGcB4R8K1n7mfoiWUD9RzPVGES7e3WxC7c - example: 5J8H5sTvEhnGcB4R8K1n7mfoiWUD9RzPVGES7e3WxC7c - SignatureInfo: - type: object - required: - - signature - - slot - - blockTime - properties: - blockTime: - $ref: '#/components/schemas/UnixTimestamp' - signature: - $ref: '#/components/schemas/SerializableSignature' - slot: - $ref: '#/components/schemas/UnsignedInteger' - UnixTimestamp: - type: integer - description: An Unix timestamp (seconds) - default: 1714081554 - example: 1714081554 - UnsignedInteger: - type: integer - default: 100 - example: 100 diff --git a/src/openapi/specs/getLatestNonVotingSignatures.yaml b/src/openapi/specs/getLatestNonVotingSignatures.yaml deleted file mode 100644 index 004c673a..00000000 --- a/src/openapi/specs/getLatestNonVotingSignatures.yaml +++ /dev/null @@ -1,139 +0,0 @@ -openapi: 3.0.3 -info: - title: photon-indexer - description: Solana indexer for general compression - license: - name: Apache-2.0 - version: 0.50.0 -servers: -- url: https://mainnet.helius-rpc.com?api-key= -paths: - /: - summary: getLatestNonVotingSignatures - post: - requestBody: - content: - application/json: - schema: - type: object - required: - - jsonrpc - - id - - method - - params - properties: - id: - type: string - description: An ID to identify the request. - enum: - - test-account - jsonrpc: - type: string - description: The version of the JSON-RPC protocol. - enum: - - '2.0' - method: - type: string - description: The name of the method to invoke. - enum: - - getLatestNonVotingSignatures - params: - type: object - properties: - cursor: - type: string - nullable: true - limit: - allOf: - - $ref: '#/components/schemas/Limit' - nullable: true - additionalProperties: false - required: true - responses: - '200': - description: '' - content: - application/json: - schema: - type: object - required: - - context - - value - properties: - context: - $ref: '#/components/schemas/Context' - value: - $ref: '#/components/schemas/SignatureInfoListWithError' - additionalProperties: false - '429': - description: Exceeded rate limit. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '500': - description: The server encountered an unexpected condition that prevented it from fulfilling the request. - content: - application/json: - schema: - type: object - properties: - error: - type: string -components: - schemas: - Context: - type: object - required: - - slot - properties: - slot: - type: integer - default: 100 - example: 100 - Limit: - type: integer - format: int64 - minimum: 0 - SerializableSignature: - type: string - description: A Solana transaction signature. - default: 5J8H5sTvEhnGcB4R8K1n7mfoiWUD9RzPVGES7e3WxC7c - example: 5J8H5sTvEhnGcB4R8K1n7mfoiWUD9RzPVGES7e3WxC7c - SignatureInfoListWithError: - type: object - required: - - items - properties: - items: - type: array - items: - $ref: '#/components/schemas/SignatureInfoWithError' - SignatureInfoWithError: - type: object - required: - - signature - - slot - - blockTime - properties: - blockTime: - $ref: '#/components/schemas/UnixTimestamp' - error: - type: string - nullable: true - signature: - $ref: '#/components/schemas/SerializableSignature' - slot: - $ref: '#/components/schemas/UnsignedInteger' - UnixTimestamp: - type: integer - description: An Unix timestamp (seconds) - default: 1714081554 - example: 1714081554 - UnsignedInteger: - type: integer - default: 100 - example: 100 diff --git a/src/openapi/specs/getMultipleCompressedAccountProofs.yaml b/src/openapi/specs/getMultipleCompressedAccountProofs.yaml deleted file mode 100644 index c520f8f9..00000000 --- a/src/openapi/specs/getMultipleCompressedAccountProofs.yaml +++ /dev/null @@ -1,129 +0,0 @@ -openapi: 3.0.3 -info: - title: photon-indexer - description: Solana indexer for general compression - license: - name: Apache-2.0 - version: 0.50.0 -servers: -- url: https://mainnet.helius-rpc.com?api-key= -paths: - /: - summary: getMultipleCompressedAccountProofs - post: - requestBody: - content: - application/json: - schema: - type: object - required: - - jsonrpc - - id - - method - - params - properties: - id: - type: string - description: An ID to identify the request. - enum: - - test-account - jsonrpc: - type: string - description: The version of the JSON-RPC protocol. - enum: - - '2.0' - method: - type: string - description: The name of the method to invoke. - enum: - - getMultipleCompressedAccountProofs - params: - type: array - items: - $ref: '#/components/schemas/Hash' - required: true - responses: - '200': - description: '' - content: - application/json: - schema: - type: object - required: - - context - - value - properties: - context: - $ref: '#/components/schemas/Context' - value: - type: array - items: - $ref: '#/components/schemas/MerkleProofWithContext' - additionalProperties: false - '429': - description: Exceeded rate limit. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '500': - description: The server encountered an unexpected condition that prevented it from fulfilling the request. - content: - application/json: - schema: - type: object - properties: - error: - type: string -components: - schemas: - Context: - type: object - required: - - slot - properties: - slot: - type: integer - default: 100 - example: 100 - Hash: - type: string - description: A 32-byte hash represented as a base58 string. - example: 11111112cMQwSC9qirWGjZM6gLGwW69X22mqwLLGP - MerkleProofWithContext: - type: object - required: - - proof - - root - - leafIndex - - hash - - merkleTree - - rootSeq - properties: - hash: - $ref: '#/components/schemas/Hash' - leafIndex: - type: integer - format: int32 - minimum: 0 - merkleTree: - $ref: '#/components/schemas/SerializablePubkey' - proof: - type: array - items: - $ref: '#/components/schemas/Hash' - root: - $ref: '#/components/schemas/Hash' - rootSeq: - type: integer - format: int64 - minimum: 0 - additionalProperties: false - SerializablePubkey: - type: string - description: A Solana public key represented as a base58 string. - default: 11111117SQekjmcMtR25wEPPiL6m1Mb5586NkLL4X - example: 11111117SQekjmcMtR25wEPPiL6m1Mb5586NkLL4X diff --git a/src/openapi/specs/getMultipleCompressedAccounts.yaml b/src/openapi/specs/getMultipleCompressedAccounts.yaml deleted file mode 100644 index de54f52d..00000000 --- a/src/openapi/specs/getMultipleCompressedAccounts.yaml +++ /dev/null @@ -1,866 +0,0 @@ -openapi: 3.0.3 -info: - title: photon-indexer - description: Solana indexer for general compression - license: - name: Apache-2.0 - version: 0.50.0 -servers: -- url: https://mainnet.helius-rpc.com?api-key= -paths: - /getCompressedAccount: - summary: getCompressedAccount - post: - requestBody: - content: - application/json: - schema: - type: object - required: - - jsonrpc - - id - - method - - params - properties: - id: - type: string - description: An ID to identify the request. - enum: - - test-account - jsonrpc: - type: string - description: The version of the JSON-RPC protocol. - enum: - - '2.0' - method: - type: string - description: The name of the method to invoke. - enum: - - getCompressedAccount - params: - type: object - description: Request for compressed account data - default: - address: null - hash: '11111111111111111111111111111111' - properties: - address: - allOf: - - $ref: '#/components/schemas/SerializablePubkey' - nullable: true - hash: - allOf: - - $ref: '#/components/schemas/Hash' - nullable: true - additionalProperties: false - example: - address: null - hash: '11111111111111111111111111111111' - required: true - responses: - '200': - description: '' - content: - application/json: - schema: - type: object - required: - - context - - value - properties: - context: - $ref: '#/components/schemas/Context' - value: - $ref: '#/components/schemas/Account' - '400': - description: Invalid request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '401': - description: Unauthorized request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '403': - description: Request was forbidden. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '404': - description: The specified resource was not found. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '429': - description: Exceeded rate limit. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '500': - description: The server encountered an unexpected condition that prevented it from fulfilling the request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - /getCompressedAccountProof: - summary: getCompressedAccountProof - post: - requestBody: - content: - application/json: - schema: - type: object - required: - - jsonrpc - - id - - method - - params - properties: - id: - type: string - description: An ID to identify the request. - enum: - - test-account - jsonrpc: - type: string - description: The version of the JSON-RPC protocol. - enum: - - '2.0' - method: - type: string - description: The name of the method to invoke. - enum: - - getCompressedAccountProof - params: - type: object - required: - - hash - properties: - hash: - $ref: '#/components/schemas/Hash' - required: true - responses: - '200': - description: '' - content: - application/json: - schema: - type: object - required: - - context - - value - properties: - context: - $ref: '#/components/schemas/Context' - value: - $ref: '#/components/schemas/MerkleProofWithContext' - '400': - description: Invalid request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '401': - description: Unauthorized request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '403': - description: Request was forbidden. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '404': - description: The specified resource was not found. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '429': - description: Exceeded rate limit. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '500': - description: The server encountered an unexpected condition that prevented it from fulfilling the request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - /getCompressedAccountsByOwner: - summary: getCompressedAccountsByOwner - post: - requestBody: - content: - application/json: - schema: - type: object - required: - - jsonrpc - - id - - method - - params - properties: - id: - type: string - description: An ID to identify the request. - enum: - - test-account - jsonrpc: - type: string - description: The version of the JSON-RPC protocol. - enum: - - '2.0' - method: - type: string - description: The name of the method to invoke. - enum: - - getCompressedAccountsByOwner - params: - type: object - required: - - owner - properties: - cursor: - allOf: - - $ref: '#/components/schemas/Hash' - nullable: true - limit: - allOf: - - $ref: '#/components/schemas/Limit' - nullable: true - owner: - $ref: '#/components/schemas/SerializablePubkey' - additionalProperties: false - required: true - responses: - '200': - description: '' - content: - application/json: - schema: - type: object - required: - - context - - value - properties: - context: - $ref: '#/components/schemas/Context' - value: - $ref: '#/components/schemas/PaginatedAccountList' - '400': - description: Invalid request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '401': - description: Unauthorized request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '403': - description: Request was forbidden. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '404': - description: The specified resource was not found. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '429': - description: Exceeded rate limit. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '500': - description: The server encountered an unexpected condition that prevented it from fulfilling the request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - /getCompressedTokenAccountsByDelegate: - summary: getCompressedTokenAccountsByDelegate - post: - requestBody: - content: - application/json: - schema: - type: object - required: - - jsonrpc - - id - - method - - params - properties: - id: - type: string - description: An ID to identify the request. - enum: - - test-account - jsonrpc: - type: string - description: The version of the JSON-RPC protocol. - enum: - - '2.0' - method: - type: string - description: The name of the method to invoke. - enum: - - getCompressedTokenAccountsByDelegate - params: - type: object - required: - - delegate - properties: - cursor: - allOf: - - $ref: '#/components/schemas/Base58String' - nullable: true - delegate: - $ref: '#/components/schemas/SerializablePubkey' - limit: - allOf: - - $ref: '#/components/schemas/Limit' - nullable: true - mint: - allOf: - - $ref: '#/components/schemas/SerializablePubkey' - nullable: true - additionalProperties: false - required: true - responses: - '200': - description: '' - content: - application/json: - schema: - type: object - required: - - context - - value - properties: - context: - $ref: '#/components/schemas/Context' - value: - $ref: '#/components/schemas/TokenAccountList' - '400': - description: Invalid request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '401': - description: Unauthorized request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '403': - description: Request was forbidden. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '404': - description: The specified resource was not found. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '429': - description: Exceeded rate limit. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '500': - description: The server encountered an unexpected condition that prevented it from fulfilling the request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - /getCompressedTokenAccountsByOwner: - summary: getCompressedTokenAccountsByOwner - post: - requestBody: - content: - application/json: - schema: - type: object - required: - - jsonrpc - - id - - method - - params - properties: - id: - type: string - description: An ID to identify the request. - enum: - - test-account - jsonrpc: - type: string - description: The version of the JSON-RPC protocol. - enum: - - '2.0' - method: - type: string - description: The name of the method to invoke. - enum: - - getCompressedTokenAccountsByOwner - params: - type: object - required: - - owner - properties: - cursor: - allOf: - - $ref: '#/components/schemas/Base58String' - nullable: true - limit: - allOf: - - $ref: '#/components/schemas/Limit' - nullable: true - mint: - allOf: - - $ref: '#/components/schemas/SerializablePubkey' - nullable: true - owner: - $ref: '#/components/schemas/SerializablePubkey' - additionalProperties: false - required: true - responses: - '200': - description: '' - content: - application/json: - schema: - type: object - required: - - context - - value - properties: - context: - $ref: '#/components/schemas/Context' - value: - $ref: '#/components/schemas/TokenAccountList' - '400': - description: Invalid request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '401': - description: Unauthorized request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '403': - description: Request was forbidden. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '404': - description: The specified resource was not found. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '429': - description: Exceeded rate limit. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '500': - description: The server encountered an unexpected condition that prevented it from fulfilling the request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - /getMultipleCompressedAccountProofs: - summary: getMultipleCompressedAccountProofs - post: - requestBody: - content: - application/json: - schema: - type: object - required: - - jsonrpc - - id - - method - - params - properties: - id: - type: string - description: An ID to identify the request. - enum: - - test-account - jsonrpc: - type: string - description: The version of the JSON-RPC protocol. - enum: - - '2.0' - method: - type: string - description: The name of the method to invoke. - enum: - - getMultipleCompressedAccountProofs - params: - type: array - items: - $ref: '#/components/schemas/Hash' - required: true - responses: - '200': - description: '' - content: - application/json: - schema: - type: object - required: - - context - - value - properties: - context: - $ref: '#/components/schemas/Context' - value: - type: array - items: - $ref: '#/components/schemas/MerkleProofWithContext' - '400': - description: Invalid request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '401': - description: Unauthorized request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '403': - description: Request was forbidden. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '404': - description: The specified resource was not found. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '429': - description: Exceeded rate limit. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '500': - description: The server encountered an unexpected condition that prevented it from fulfilling the request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - /getMultipleCompressedAccounts: - summary: getMultipleCompressedAccounts - post: - requestBody: - content: - application/json: - schema: - type: object - required: - - jsonrpc - - id - - method - - params - properties: - id: - type: string - description: An ID to identify the request. - enum: - - test-account - jsonrpc: - type: string - description: The version of the JSON-RPC protocol. - enum: - - '2.0' - method: - type: string - description: The name of the method to invoke. - enum: - - getMultipleCompressedAccounts - params: - type: object - description: Request for compressed account data - default: - addresses: null - hashes: - - 1111111QLbz7JHiBTspS962RLKV8GndWFwiEaqKM - - 1111111ogCyDbaRMvkdsHB3qfdyFYaG1WtRUAfdh - properties: - addresses: - type: array - items: - $ref: '#/components/schemas/SerializablePubkey' - nullable: true - hashes: - type: array - items: - $ref: '#/components/schemas/Hash' - nullable: true - additionalProperties: false - example: - addresses: null - hashes: - - 1111111QLbz7JHiBTspS962RLKV8GndWFwiEaqKM - - 1111111ogCyDbaRMvkdsHB3qfdyFYaG1WtRUAfdh - required: true - responses: - '200': - description: '' - content: - application/json: - schema: - type: object - required: - - context - - value - properties: - context: - $ref: '#/components/schemas/Context' - value: - $ref: '#/components/schemas/AccountList' - additionalProperties: false - '429': - description: Exceeded rate limit. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '500': - description: The server encountered an unexpected condition that prevented it from fulfilling the request. - content: - application/json: - schema: - type: object - properties: - error: - type: string -components: - schemas: - Account: - type: object - required: - - hash - - owner - - lamports - - tree - - leafIndex - - seq - - slotCreated - properties: - address: - $ref: '#/components/schemas/SerializablePubkey' - data: - $ref: '#/components/schemas/AccountData' - hash: - $ref: '#/components/schemas/Hash' - lamports: - $ref: '#/components/schemas/UnsignedInteger' - leafIndex: - $ref: '#/components/schemas/UnsignedInteger' - owner: - $ref: '#/components/schemas/SerializablePubkey' - seq: - $ref: '#/components/schemas/UnsignedInteger' - slotCreated: - $ref: '#/components/schemas/UnsignedInteger' - tree: - $ref: '#/components/schemas/SerializablePubkey' - additionalProperties: false - AccountData: - type: object - required: - - discriminator - - data - - dataHash - properties: - data: - $ref: '#/components/schemas/Base64String' - dataHash: - $ref: '#/components/schemas/Hash' - discriminator: - $ref: '#/components/schemas/UnsignedInteger' - additionalProperties: false - AccountList: - type: object - required: - - items - properties: - items: - type: array - items: - allOf: - - $ref: '#/components/schemas/Account' - nullable: true - additionalProperties: false - Base64String: - type: string - description: A base 64 encoded string. - default: SGVsbG8sIFdvcmxkIQ== - example: SGVsbG8sIFdvcmxkIQ== - Context: - type: object - required: - - slot - properties: - slot: - type: integer - default: 100 - example: 100 - Hash: - type: string - description: A 32-byte hash represented as a base58 string. - example: 11111112cMQwSC9qirWGjZM6gLGwW69X22mqwLLGP - SerializablePubkey: - type: string - description: A Solana public key represented as a base58 string. - default: 11111115RidqCHAoz6dzmXxGcfWLNzevYqNpaRAUo - example: 11111115RidqCHAoz6dzmXxGcfWLNzevYqNpaRAUo - UnsignedInteger: - type: integer - default: 100 - example: 100 diff --git a/src/openapi/specs/getMultipleNewAddressProofs.yaml b/src/openapi/specs/getMultipleNewAddressProofs.yaml deleted file mode 100644 index 95efb887..00000000 --- a/src/openapi/specs/getMultipleNewAddressProofs.yaml +++ /dev/null @@ -1,139 +0,0 @@ -openapi: 3.0.3 -info: - title: photon-indexer - description: Solana indexer for general compression - license: - name: Apache-2.0 - version: 0.50.0 -servers: -- url: https://mainnet.helius-rpc.com?api-key= -paths: - /: - summary: getMultipleNewAddressProofs - post: - requestBody: - content: - application/json: - schema: - type: object - required: - - jsonrpc - - id - - method - - params - properties: - id: - type: string - description: An ID to identify the request. - enum: - - test-account - jsonrpc: - type: string - description: The version of the JSON-RPC protocol. - enum: - - '2.0' - method: - type: string - description: The name of the method to invoke. - enum: - - getMultipleNewAddressProofs - params: - type: array - items: - $ref: '#/components/schemas/SerializablePubkey' - required: true - responses: - '200': - description: '' - content: - application/json: - schema: - type: object - required: - - context - - value - properties: - context: - $ref: '#/components/schemas/Context' - value: - type: array - items: - $ref: '#/components/schemas/MerkleContextWithNewAddressProof' - '429': - description: Exceeded rate limit. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '500': - description: The server encountered an unexpected condition that prevented it from fulfilling the request. - content: - application/json: - schema: - type: object - properties: - error: - type: string -components: - schemas: - Context: - type: object - required: - - slot - properties: - slot: - type: integer - default: 100 - example: 100 - Hash: - type: string - description: A 32-byte hash represented as a base58 string. - example: 11111112cMQwSC9qirWGjZM6gLGwW69X22mqwLLGP - MerkleContextWithNewAddressProof: - type: object - required: - - root - - address - - lowerRangeAddress - - higherRangeAddress - - nextIndex - - proof - - merkleTree - - rootSeq - - lowElementLeafIndex - properties: - address: - $ref: '#/components/schemas/SerializablePubkey' - higherRangeAddress: - $ref: '#/components/schemas/SerializablePubkey' - lowElementLeafIndex: - type: integer - format: int32 - minimum: 0 - lowerRangeAddress: - $ref: '#/components/schemas/SerializablePubkey' - merkleTree: - $ref: '#/components/schemas/SerializablePubkey' - nextIndex: - type: integer - format: int32 - minimum: 0 - proof: - type: array - items: - $ref: '#/components/schemas/Hash' - root: - $ref: '#/components/schemas/Hash' - rootSeq: - type: integer - format: int64 - minimum: 0 - additionalProperties: false - SerializablePubkey: - type: string - description: A Solana public key represented as a base58 string. - default: 11111117qkFjr4u54stuNNUR8fRF8dNhaP35yvANs - example: 11111117qkFjr4u54stuNNUR8fRF8dNhaP35yvANs diff --git a/src/openapi/specs/getMultipleNewAddressProofsV2.yaml b/src/openapi/specs/getMultipleNewAddressProofsV2.yaml deleted file mode 100644 index 399efde3..00000000 --- a/src/openapi/specs/getMultipleNewAddressProofsV2.yaml +++ /dev/null @@ -1,150 +0,0 @@ -openapi: 3.0.3 -info: - title: photon-indexer - description: Solana indexer for general compression - license: - name: Apache-2.0 - version: 0.50.0 -servers: -- url: https://mainnet.helius-rpc.com?api-key= -paths: - /: - summary: getMultipleNewAddressProofsV2 - post: - requestBody: - content: - application/json: - schema: - type: object - required: - - jsonrpc - - id - - method - - params - properties: - id: - type: string - description: An ID to identify the request. - enum: - - test-account - jsonrpc: - type: string - description: The version of the JSON-RPC protocol. - enum: - - '2.0' - method: - type: string - description: The name of the method to invoke. - enum: - - getMultipleNewAddressProofsV2 - params: - type: array - items: - $ref: '#/components/schemas/AddressWithTree' - required: true - responses: - '200': - description: '' - content: - application/json: - schema: - type: object - required: - - context - - value - properties: - context: - $ref: '#/components/schemas/Context' - value: - type: array - items: - $ref: '#/components/schemas/MerkleContextWithNewAddressProof' - '429': - description: Exceeded rate limit. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '500': - description: The server encountered an unexpected condition that prevented it from fulfilling the request. - content: - application/json: - schema: - type: object - properties: - error: - type: string -components: - schemas: - AddressWithTree: - type: object - required: - - address - - tree - properties: - address: - $ref: '#/components/schemas/SerializablePubkey' - tree: - $ref: '#/components/schemas/SerializablePubkey' - additionalProperties: false - Context: - type: object - required: - - slot - properties: - slot: - type: integer - default: 100 - example: 100 - Hash: - type: string - description: A 32-byte hash represented as a base58 string. - example: 11111112cMQwSC9qirWGjZM6gLGwW69X22mqwLLGP - MerkleContextWithNewAddressProof: - type: object - required: - - root - - address - - lowerRangeAddress - - higherRangeAddress - - nextIndex - - proof - - merkleTree - - rootSeq - - lowElementLeafIndex - properties: - address: - $ref: '#/components/schemas/SerializablePubkey' - higherRangeAddress: - $ref: '#/components/schemas/SerializablePubkey' - lowElementLeafIndex: - type: integer - format: int32 - minimum: 0 - lowerRangeAddress: - $ref: '#/components/schemas/SerializablePubkey' - merkleTree: - $ref: '#/components/schemas/SerializablePubkey' - nextIndex: - type: integer - format: int32 - minimum: 0 - proof: - type: array - items: - $ref: '#/components/schemas/Hash' - root: - $ref: '#/components/schemas/Hash' - rootSeq: - type: integer - format: int64 - minimum: 0 - additionalProperties: false - SerializablePubkey: - type: string - description: A Solana public key represented as a base58 string. - default: 11111118F5rixNBnFLmioWZSYzjjFuAL5dyoDVzhD - example: 11111118F5rixNBnFLmioWZSYzjjFuAL5dyoDVzhD diff --git a/src/openapi/specs/getQueueElements.yaml b/src/openapi/specs/getQueueElements.yaml deleted file mode 100644 index bfe4aa0a..00000000 --- a/src/openapi/specs/getQueueElements.yaml +++ /dev/null @@ -1,89 +0,0 @@ -paths: - /: - post: - requestBody: - content: - application/json: - schema: - type: object - required: - - jsonrpc - - id - - method - - params - properties: - id: - type: string - description: An ID to identify the request. - enum: - - test-account - jsonrpc: - type: string - description: The version of the JSON-RPC protocol. - enum: - - '2.0' - method: - type: string - description: The name of the method to invoke. - enum: - - getQueueElements - params: - type: object - required: - - queue - - batch - - startOffset - - endOffset - properties: - queue: - $ref: '#/components/schemas/Hash' - batch: - $ref: '#/components/schemas/UnsignedInteger' - startOffset: - $ref: '#/components/schemas/UnsignedInteger' - endOffset: - $ref: '#/components/schemas/UnsignedInteger' - additionalProperties: false - required: true - responses: - '200': - description: '' - content: - application/json: - schema: - type: object - required: - - context - - value - properties: - context: - $ref: '#/components/schemas/Context' - value: - type: array - items: - $ref: '#/components/schemas/Hash' - '429': - description: Exceeded rate limit. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '500': - description: The server encountered an unexpected condition that prevented it from fulfilling the request. - content: - application/json: - schema: - type: object - properties: - error: - type: string -components: - schemas: - Hash: - type: string - description: A base58 encoded hash. - default: "11111111111111111111111111111111" - example: "11111111111111111111111111111111" \ No newline at end of file diff --git a/src/openapi/specs/getTransactionWithCompressionInfo.yaml b/src/openapi/specs/getTransactionWithCompressionInfo.yaml deleted file mode 100644 index a7e00abf..00000000 --- a/src/openapi/specs/getTransactionWithCompressionInfo.yaml +++ /dev/null @@ -1,1569 +0,0 @@ -openapi: 3.0.3 -info: - title: photon-indexer - description: Solana indexer for general compression - license: - name: Apache-2.0 - version: 0.50.0 -servers: -- url: https://mainnet.helius-rpc.com?api-key= -paths: - /getCompressedAccount: - summary: getCompressedAccount - post: - requestBody: - content: - application/json: - schema: - type: object - required: - - jsonrpc - - id - - method - - params - properties: - id: - type: string - description: An ID to identify the request. - enum: - - test-account - jsonrpc: - type: string - description: The version of the JSON-RPC protocol. - enum: - - '2.0' - method: - type: string - description: The name of the method to invoke. - enum: - - getCompressedAccount - params: - type: object - description: Request for compressed account data - default: - address: null - hash: '11111111111111111111111111111111' - properties: - address: - allOf: - - $ref: '#/components/schemas/SerializablePubkey' - nullable: true - hash: - allOf: - - $ref: '#/components/schemas/Hash' - nullable: true - additionalProperties: false - example: - address: null - hash: '11111111111111111111111111111111' - required: true - responses: - '200': - description: '' - content: - application/json: - schema: - type: object - required: - - context - - value - properties: - context: - $ref: '#/components/schemas/Context' - value: - $ref: '#/components/schemas/Account' - '400': - description: Invalid request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '401': - description: Unauthorized request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '403': - description: Request was forbidden. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '404': - description: The specified resource was not found. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '429': - description: Exceeded rate limit. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '500': - description: The server encountered an unexpected condition that prevented it from fulfilling the request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - /getCompressedAccountProof: - summary: getCompressedAccountProof - post: - requestBody: - content: - application/json: - schema: - type: object - required: - - jsonrpc - - id - - method - - params - properties: - id: - type: string - description: An ID to identify the request. - enum: - - test-account - jsonrpc: - type: string - description: The version of the JSON-RPC protocol. - enum: - - '2.0' - method: - type: string - description: The name of the method to invoke. - enum: - - getCompressedAccountProof - params: - type: object - required: - - hash - properties: - hash: - $ref: '#/components/schemas/Hash' - required: true - responses: - '200': - description: '' - content: - application/json: - schema: - type: object - required: - - context - - value - properties: - context: - $ref: '#/components/schemas/Context' - value: - $ref: '#/components/schemas/MerkleProofWithContext' - '400': - description: Invalid request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '401': - description: Unauthorized request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '403': - description: Request was forbidden. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '404': - description: The specified resource was not found. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '429': - description: Exceeded rate limit. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '500': - description: The server encountered an unexpected condition that prevented it from fulfilling the request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - /getCompressedAccountsByOwner: - summary: getCompressedAccountsByOwner - post: - requestBody: - content: - application/json: - schema: - type: object - required: - - jsonrpc - - id - - method - - params - properties: - id: - type: string - description: An ID to identify the request. - enum: - - test-account - jsonrpc: - type: string - description: The version of the JSON-RPC protocol. - enum: - - '2.0' - method: - type: string - description: The name of the method to invoke. - enum: - - getCompressedAccountsByOwner - params: - type: object - required: - - owner - properties: - cursor: - allOf: - - $ref: '#/components/schemas/Hash' - nullable: true - limit: - allOf: - - $ref: '#/components/schemas/Limit' - nullable: true - owner: - $ref: '#/components/schemas/SerializablePubkey' - additionalProperties: false - required: true - responses: - '200': - description: '' - content: - application/json: - schema: - type: object - required: - - context - - value - properties: - context: - $ref: '#/components/schemas/Context' - value: - $ref: '#/components/schemas/PaginatedAccountList' - '400': - description: Invalid request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '401': - description: Unauthorized request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '403': - description: Request was forbidden. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '404': - description: The specified resource was not found. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '429': - description: Exceeded rate limit. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '500': - description: The server encountered an unexpected condition that prevented it from fulfilling the request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - /getCompressedBalance: - summary: getCompressedBalance - post: - requestBody: - content: - application/json: - schema: - type: object - required: - - jsonrpc - - id - - method - - params - properties: - id: - type: string - description: An ID to identify the request. - enum: - - test-account - jsonrpc: - type: string - description: The version of the JSON-RPC protocol. - enum: - - '2.0' - method: - type: string - description: The name of the method to invoke. - enum: - - getCompressedBalance - params: - type: object - description: Request for compressed account data - default: - address: null - hash: '11111111111111111111111111111111' - properties: - address: - allOf: - - $ref: '#/components/schemas/SerializablePubkey' - nullable: true - hash: - allOf: - - $ref: '#/components/schemas/Hash' - nullable: true - additionalProperties: false - example: - address: null - hash: '11111111111111111111111111111111' - required: true - responses: - '200': - description: '' - content: - application/json: - schema: - type: object - required: - - context - - value - properties: - context: - $ref: '#/components/schemas/Context' - value: - $ref: '#/components/schemas/UnsignedInteger' - '400': - description: Invalid request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '401': - description: Unauthorized request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '403': - description: Request was forbidden. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '404': - description: The specified resource was not found. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '429': - description: Exceeded rate limit. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '500': - description: The server encountered an unexpected condition that prevented it from fulfilling the request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - /getCompressedBalanceByOwner: - summary: getCompressedBalanceByOwner - post: - requestBody: - content: - application/json: - schema: - type: object - required: - - jsonrpc - - id - - method - - params - properties: - id: - type: string - description: An ID to identify the request. - enum: - - test-account - jsonrpc: - type: string - description: The version of the JSON-RPC protocol. - enum: - - '2.0' - method: - type: string - description: The name of the method to invoke. - enum: - - getCompressedBalanceByOwner - params: - type: object - required: - - owner - properties: - owner: - $ref: '#/components/schemas/SerializablePubkey' - required: true - responses: - '200': - description: '' - content: - application/json: - schema: - type: object - required: - - context - - value - properties: - context: - $ref: '#/components/schemas/Context' - value: - $ref: '#/components/schemas/UnsignedInteger' - '400': - description: Invalid request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '401': - description: Unauthorized request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '403': - description: Request was forbidden. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '404': - description: The specified resource was not found. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '429': - description: Exceeded rate limit. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '500': - description: The server encountered an unexpected condition that prevented it from fulfilling the request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - /getCompressedTokenAccountBalance: - summary: getCompressedTokenAccountBalance - post: - requestBody: - content: - application/json: - schema: - type: object - required: - - jsonrpc - - id - - method - - params - properties: - id: - type: string - description: An ID to identify the request. - enum: - - test-account - jsonrpc: - type: string - description: The version of the JSON-RPC protocol. - enum: - - '2.0' - method: - type: string - description: The name of the method to invoke. - enum: - - getCompressedTokenAccountBalance - params: - type: object - description: Request for compressed account data - default: - address: null - hash: '11111111111111111111111111111111' - properties: - address: - allOf: - - $ref: '#/components/schemas/SerializablePubkey' - nullable: true - hash: - allOf: - - $ref: '#/components/schemas/Hash' - nullable: true - additionalProperties: false - example: - address: null - hash: '11111111111111111111111111111111' - required: true - responses: - '200': - description: '' - content: - application/json: - schema: - type: object - required: - - context - - value - properties: - context: - $ref: '#/components/schemas/Context' - value: - $ref: '#/components/schemas/TokenAccountBalance' - '400': - description: Invalid request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '401': - description: Unauthorized request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '403': - description: Request was forbidden. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '404': - description: The specified resource was not found. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '429': - description: Exceeded rate limit. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '500': - description: The server encountered an unexpected condition that prevented it from fulfilling the request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - /getCompressedTokenAccountsByDelegate: - summary: getCompressedTokenAccountsByDelegate - post: - requestBody: - content: - application/json: - schema: - type: object - required: - - jsonrpc - - id - - method - - params - properties: - id: - type: string - description: An ID to identify the request. - enum: - - test-account - jsonrpc: - type: string - description: The version of the JSON-RPC protocol. - enum: - - '2.0' - method: - type: string - description: The name of the method to invoke. - enum: - - getCompressedTokenAccountsByDelegate - params: - type: object - required: - - delegate - properties: - cursor: - allOf: - - $ref: '#/components/schemas/Base58String' - nullable: true - delegate: - $ref: '#/components/schemas/SerializablePubkey' - limit: - allOf: - - $ref: '#/components/schemas/Limit' - nullable: true - mint: - allOf: - - $ref: '#/components/schemas/SerializablePubkey' - nullable: true - additionalProperties: false - required: true - responses: - '200': - description: '' - content: - application/json: - schema: - type: object - required: - - context - - value - properties: - context: - $ref: '#/components/schemas/Context' - value: - $ref: '#/components/schemas/TokenAccountList' - '400': - description: Invalid request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '401': - description: Unauthorized request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '403': - description: Request was forbidden. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '404': - description: The specified resource was not found. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '429': - description: Exceeded rate limit. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '500': - description: The server encountered an unexpected condition that prevented it from fulfilling the request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - /getCompressedTokenAccountsByOwner: - summary: getCompressedTokenAccountsByOwner - post: - requestBody: - content: - application/json: - schema: - type: object - required: - - jsonrpc - - id - - method - - params - properties: - id: - type: string - description: An ID to identify the request. - enum: - - test-account - jsonrpc: - type: string - description: The version of the JSON-RPC protocol. - enum: - - '2.0' - method: - type: string - description: The name of the method to invoke. - enum: - - getCompressedTokenAccountsByOwner - params: - type: object - required: - - owner - properties: - cursor: - allOf: - - $ref: '#/components/schemas/Base58String' - nullable: true - limit: - allOf: - - $ref: '#/components/schemas/Limit' - nullable: true - mint: - allOf: - - $ref: '#/components/schemas/SerializablePubkey' - nullable: true - owner: - $ref: '#/components/schemas/SerializablePubkey' - additionalProperties: false - required: true - responses: - '200': - description: '' - content: - application/json: - schema: - type: object - required: - - context - - value - properties: - context: - $ref: '#/components/schemas/Context' - value: - $ref: '#/components/schemas/TokenAccountList' - '400': - description: Invalid request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '401': - description: Unauthorized request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '403': - description: Request was forbidden. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '404': - description: The specified resource was not found. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '429': - description: Exceeded rate limit. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '500': - description: The server encountered an unexpected condition that prevented it from fulfilling the request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - /getCompressedTokenBalancesByOwner: - summary: getCompressedTokenBalancesByOwner - post: - requestBody: - content: - application/json: - schema: - type: object - required: - - jsonrpc - - id - - method - - params - properties: - id: - type: string - description: An ID to identify the request. - enum: - - test-account - jsonrpc: - type: string - description: The version of the JSON-RPC protocol. - enum: - - '2.0' - method: - type: string - description: The name of the method to invoke. - enum: - - getCompressedTokenBalancesByOwner - params: - type: object - required: - - owner - properties: - cursor: - allOf: - - $ref: '#/components/schemas/Base58String' - nullable: true - limit: - allOf: - - $ref: '#/components/schemas/Limit' - nullable: true - mint: - allOf: - - $ref: '#/components/schemas/SerializablePubkey' - nullable: true - owner: - $ref: '#/components/schemas/SerializablePubkey' - required: true - responses: - '200': - description: '' - content: - application/json: - schema: - type: object - required: - - context - - value - properties: - context: - $ref: '#/components/schemas/Context' - value: - $ref: '#/components/schemas/TokenBalanceList' - '400': - description: Invalid request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '401': - description: Unauthorized request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '403': - description: Request was forbidden. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '404': - description: The specified resource was not found. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '429': - description: Exceeded rate limit. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '500': - description: The server encountered an unexpected condition that prevented it from fulfilling the request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - /getCompressionSignaturesForAccount: - summary: getCompressionSignaturesForAccount - post: - requestBody: - content: - application/json: - schema: - type: object - required: - - jsonrpc - - id - - method - - params - properties: - id: - type: string - description: An ID to identify the request. - enum: - - test-account - jsonrpc: - type: string - description: The version of the JSON-RPC protocol. - enum: - - '2.0' - method: - type: string - description: The name of the method to invoke. - enum: - - getCompressionSignaturesForAccount - params: - type: object - required: - - hash - properties: - hash: - $ref: '#/components/schemas/Hash' - required: true - responses: - '200': - description: '' - content: - application/json: - schema: - type: object - required: - - context - - value - properties: - context: - $ref: '#/components/schemas/Context' - value: - $ref: '#/components/schemas/SignatureInfoList' - '400': - description: Invalid request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '401': - description: Unauthorized request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '403': - description: Request was forbidden. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '404': - description: The specified resource was not found. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '429': - description: Exceeded rate limit. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '500': - description: The server encountered an unexpected condition that prevented it from fulfilling the request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - /getMultipleCompressedAccountProofs: - summary: getMultipleCompressedAccountProofs - post: - requestBody: - content: - application/json: - schema: - type: object - required: - - jsonrpc - - id - - method - - params - properties: - id: - type: string - description: An ID to identify the request. - enum: - - test-account - jsonrpc: - type: string - description: The version of the JSON-RPC protocol. - enum: - - '2.0' - method: - type: string - description: The name of the method to invoke. - enum: - - getMultipleCompressedAccountProofs - params: - type: array - items: - $ref: '#/components/schemas/Hash' - required: true - responses: - '200': - description: '' - content: - application/json: - schema: - type: object - required: - - context - - value - properties: - context: - $ref: '#/components/schemas/Context' - value: - type: array - items: - $ref: '#/components/schemas/MerkleProofWithContext' - '400': - description: Invalid request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '401': - description: Unauthorized request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '403': - description: Request was forbidden. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '404': - description: The specified resource was not found. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '429': - description: Exceeded rate limit. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '500': - description: The server encountered an unexpected condition that prevented it from fulfilling the request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - /getMultipleCompressedAccounts: - summary: getMultipleCompressedAccounts - post: - requestBody: - content: - application/json: - schema: - type: object - required: - - jsonrpc - - id - - method - - params - properties: - id: - type: string - description: An ID to identify the request. - enum: - - test-account - jsonrpc: - type: string - description: The version of the JSON-RPC protocol. - enum: - - '2.0' - method: - type: string - description: The name of the method to invoke. - enum: - - getMultipleCompressedAccounts - params: - type: object - description: Request for compressed account data - default: - addresses: null - hashes: - - 1111111QLbz7JHiBTspS962RLKV8GndWFwiEaqKM - - 1111111ogCyDbaRMvkdsHB3qfdyFYaG1WtRUAfdh - properties: - addresses: - type: array - items: - $ref: '#/components/schemas/SerializablePubkey' - nullable: true - hashes: - type: array - items: - $ref: '#/components/schemas/Hash' - nullable: true - additionalProperties: false - example: - addresses: null - hashes: - - 1111111QLbz7JHiBTspS962RLKV8GndWFwiEaqKM - - 1111111ogCyDbaRMvkdsHB3qfdyFYaG1WtRUAfdh - required: true - responses: - '200': - description: '' - content: - application/json: - schema: - type: object - required: - - context - - value - properties: - context: - $ref: '#/components/schemas/Context' - value: - $ref: '#/components/schemas/AccountList' - '400': - description: Invalid request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '401': - description: Unauthorized request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '403': - description: Request was forbidden. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '404': - description: The specified resource was not found. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '429': - description: Exceeded rate limit. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '500': - description: The server encountered an unexpected condition that prevented it from fulfilling the request. - content: - application/json: - schema: - type: object - properties: - error: - type: string - /getTransactionWithCompressionInfo: - summary: getTransactionWithCompressionInfo - post: - requestBody: - content: - application/json: - schema: - type: object - required: - - jsonrpc - - id - - method - - params - properties: - id: - type: string - description: An ID to identify the request. - enum: - - test-account - jsonrpc: - type: string - description: The version of the JSON-RPC protocol. - enum: - - '2.0' - method: - type: string - description: The name of the method to invoke. - enum: - - getTransactionWithCompressionInfo - params: - type: object - required: - - signature - properties: - signature: - $ref: '#/components/schemas/SerializableSignature' - additionalProperties: false - required: true - responses: - '200': - description: '' - content: - application/json: - schema: - type: object - description: A Solana transaction with additional compression information - properties: - compression_info: - type: object - required: - - closedAccounts - - openedAccounts - properties: - closedAccounts: - type: array - items: - $ref: '#/components/schemas/AccountWithOptionalTokenData' - openedAccounts: - type: array - items: - $ref: '#/components/schemas/AccountWithOptionalTokenData' - additionalProperties: false - transaction: - type: object - description: An encoded confirmed transaction with status meta - '429': - description: Exceeded rate limit. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '500': - description: The server encountered an unexpected condition that prevented it from fulfilling the request. - content: - application/json: - schema: - type: object - properties: - error: - type: string -components: - schemas: - Account: - type: object - required: - - hash - - owner - - lamports - - tree - - leafIndex - - seq - - slotCreated - properties: - address: - $ref: '#/components/schemas/SerializablePubkey' - data: - $ref: '#/components/schemas/AccountData' - hash: - $ref: '#/components/schemas/Hash' - lamports: - $ref: '#/components/schemas/UnsignedInteger' - leafIndex: - $ref: '#/components/schemas/UnsignedInteger' - owner: - $ref: '#/components/schemas/SerializablePubkey' - seq: - $ref: '#/components/schemas/UnsignedInteger' - slotCreated: - $ref: '#/components/schemas/UnsignedInteger' - tree: - $ref: '#/components/schemas/SerializablePubkey' - additionalProperties: false - AccountData: - type: object - required: - - discriminator - - data - - dataHash - properties: - data: - $ref: '#/components/schemas/Base64String' - dataHash: - $ref: '#/components/schemas/Hash' - discriminator: - $ref: '#/components/schemas/UnsignedInteger' - additionalProperties: false - AccountState: - type: string - enum: - - initialized - - frozen - AccountWithOptionalTokenData: - type: object - required: - - account - properties: - account: - $ref: '#/components/schemas/Account' - optionalTokenData: - $ref: '#/components/schemas/TokenData' - additionalProperties: false - Base64String: - type: string - description: A base 64 encoded string. - default: SGVsbG8sIFdvcmxkIQ== - example: SGVsbG8sIFdvcmxkIQ== - Hash: - type: string - description: A 32-byte hash represented as a base58 string. - example: 11111112cMQwSC9qirWGjZM6gLGwW69X22mqwLLGP - SerializablePubkey: - type: string - description: A Solana public key represented as a base58 string. - default: 11111116djSnXB2wXVGT4xDLsfTnkp1p4cCxHAfRq - example: 11111116djSnXB2wXVGT4xDLsfTnkp1p4cCxHAfRq - SerializableSignature: - type: string - description: A Solana transaction signature. - default: 5J8H5sTvEhnGcB4R8K1n7mfoiWUD9RzPVGES7e3WxC7c - example: 5J8H5sTvEhnGcB4R8K1n7mfoiWUD9RzPVGES7e3WxC7c - TokenData: - type: object - required: - - mint - - owner - - amount - - state - properties: - amount: - $ref: '#/components/schemas/UnsignedInteger' - delegate: - $ref: '#/components/schemas/SerializablePubkey' - mint: - $ref: '#/components/schemas/SerializablePubkey' - owner: - $ref: '#/components/schemas/SerializablePubkey' - state: - $ref: '#/components/schemas/AccountState' - tlv: - $ref: '#/components/schemas/Base64String' - UnsignedInteger: - type: integer - default: 100 - example: 100 diff --git a/src/openapi/specs/getValidityProof.yaml b/src/openapi/specs/getValidityProof.yaml deleted file mode 100644 index d2abd4eb..00000000 --- a/src/openapi/specs/getValidityProof.yaml +++ /dev/null @@ -1,169 +0,0 @@ -openapi: 3.0.3 -info: - title: photon-indexer - description: Solana indexer for general compression - license: - name: Apache-2.0 - version: 0.50.0 -servers: -- url: https://mainnet.helius-rpc.com?api-key= -paths: - /: - summary: getValidityProof - post: - requestBody: - content: - application/json: - schema: - type: object - required: - - jsonrpc - - id - - method - - params - properties: - id: - type: string - description: An ID to identify the request. - enum: - - test-account - jsonrpc: - type: string - description: The version of the JSON-RPC protocol. - enum: - - '2.0' - method: - type: string - description: The name of the method to invoke. - enum: - - getValidityProof - params: - type: object - properties: - hashes: - type: array - items: - $ref: '#/components/schemas/Hash' - newAddressesWithTrees: - type: array - items: - $ref: '#/components/schemas/AddressWithTree' - additionalProperties: false - required: true - responses: - '200': - description: '' - content: - application/json: - schema: - type: object - required: - - value - - context - properties: - context: - $ref: '#/components/schemas/Context' - value: - $ref: '#/components/schemas/CompressedProofWithContext' - additionalProperties: false - '429': - description: Exceeded rate limit. - content: - application/json: - schema: - type: object - properties: - error: - type: string - '500': - description: The server encountered an unexpected condition that prevented it from fulfilling the request. - content: - application/json: - schema: - type: object - properties: - error: - type: string -components: - schemas: - AddressWithTree: - type: object - required: - - address - - tree - properties: - address: - $ref: '#/components/schemas/SerializablePubkey' - tree: - $ref: '#/components/schemas/SerializablePubkey' - additionalProperties: false - CompressedProof: - type: object - required: - - a - - b - - c - properties: - a: - type: string - format: binary - b: - type: string - format: binary - c: - type: string - format: binary - CompressedProofWithContext: - type: object - required: - - compressedProof - - roots - - rootIndices - - leafIndices - - leaves - - merkleTrees - properties: - compressedProof: - $ref: '#/components/schemas/CompressedProof' - leafIndices: - type: array - items: - type: integer - format: int32 - minimum: 0 - leaves: - type: array - items: - type: string - merkleTrees: - type: array - items: - type: string - rootIndices: - type: array - items: - type: integer - format: int64 - minimum: 0 - roots: - type: array - items: - type: string - Context: - type: object - required: - - slot - properties: - slot: - type: integer - default: 100 - example: 100 - Hash: - type: string - description: A 32-byte hash represented as a base58 string. - example: 11111112cMQwSC9qirWGjZM6gLGwW69X22mqwLLGP - SerializablePubkey: - type: string - description: A Solana public key represented as a base58 string. - default: 11111118eRTi4fUVRoeYEeeTyL4DPAwxatvWT5q1Z - example: 11111118eRTi4fUVRoeYEeeTyL4DPAwxatvWT5q1Z diff --git a/src/openapi/specs/openapitools.json b/src/openapi/specs/openapitools.json deleted file mode 100644 index cfe74d51..00000000 --- a/src/openapi/specs/openapitools.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "$schema": "./node_modules/@openapitools/openapi-generator-cli/config.schema.json", - "spaces": 2, - "generator-cli": { - "version": "7.5.0" - } -} diff --git a/src/snapshot/loader/main.rs b/src/snapshot/loader/main.rs index fb44270e..482e0a7f 100644 --- a/src/snapshot/loader/main.rs +++ b/src/snapshot/loader/main.rs @@ -36,7 +36,7 @@ async fn main() -> anyhow::Result<()> { let http_client = reqwest::Client::new(); // Call the download snapshot endpoint let response = http_client - .get(&format!("{}/download", args.snapshot_server_url)) + .get(format!("{}/download", args.snapshot_server_url)) .send() .await .unwrap(); diff --git a/src/snapshot/mod.rs b/src/snapshot/mod.rs index 6493ca91..5ad7f985 100644 --- a/src/snapshot/mod.rs +++ b/src/snapshot/mod.rs @@ -1,7 +1,7 @@ use std::{ env::temp_dir, fs::{self, File, OpenOptions}, - io::{BufReader, Error, ErrorKind, Read, Write}, + io::{BufReader, Error, Read, Write}, path::PathBuf, pin::Pin, sync::Arc, @@ -157,8 +157,7 @@ impl R2DirectoryAdapter { pin_mut!(byte_stream); // Create a stream that converts `Result` to `Result, S3Error>` - let byte_stream = - byte_stream.map(|bytes| bytes.map_err(|e| Error::new(ErrorKind::Other, e))); + let byte_stream = byte_stream.map(|bytes| bytes.map_err(Error::other)); let mut stream_reader = StreamReader { stream: byte_stream, diff --git a/src/snapshot/snapshotter/main.rs b/src/snapshot/snapshotter/main.rs index 7abe3420..45ef5a6d 100644 --- a/src/snapshot/snapshotter/main.rs +++ b/src/snapshot/snapshotter/main.rs @@ -115,7 +115,7 @@ async fn stream_bytes( let byte_stream = byte_stream.map(|bytes| { bytes.map_err(|e| { error!("Error reading byte: {:?}", e); - io::Error::new(io::ErrorKind::Other, "Stream Error") + io::Error::other("Stream Error") }) }); diff --git a/src/tools/tree_validator/main.rs b/src/tools/tree_validator/main.rs index 45139460..e70729de 100644 --- a/src/tools/tree_validator/main.rs +++ b/src/tools/tree_validator/main.rs @@ -9,7 +9,6 @@ use photon_indexer::{ }; use solana_pubkey::Pubkey; use std::str::FromStr; -use tokio; #[derive(Parser)] struct Args { diff --git a/tests/integration_tests/batched_state_tree_tests.rs b/tests/integration_tests/batched_state_tree_tests.rs index 6a6dc3f3..0af34045 100644 --- a/tests/integration_tests/batched_state_tree_tests.rs +++ b/tests/integration_tests/batched_state_tree_tests.rs @@ -17,7 +17,7 @@ use photon_indexer::common::typedefs::serializable_pubkey::SerializablePubkey; use photon_indexer::common::typedefs::serializable_signature::SerializableSignature; use photon_indexer::common::typedefs::token_data::TokenData; use photon_indexer::common::typedefs::unsigned_integer::UnsignedInteger; -use photon_indexer::ingester::persist::COMPRESSED_TOKEN_PROGRAM; +use photon_indexer::ingester::persist::LIGHT_TOKEN_PROGRAM_ID; use serial_test::serial; use solana_pubkey::Pubkey; use solana_signature::Signature; @@ -392,21 +392,22 @@ async fn test_batched_tree_transactions( println!("pre input queue len {}", pre_input_len,); // Insert 1 batch. - let pre_output_queue = pre_output_queue_elements + if let Some(pre_output_queue) = pre_output_queue_elements .state_queue .as_ref() .and_then(|sq| sq.output_queue.as_ref()) - .unwrap(); - let slice_length = pre_output_queue.leaves.len().min(10); - for idx in 0..slice_length { - let leaf_index = pre_output_queue.leaf_indices[idx]; - let leaf_hash = &pre_output_queue.leaves[idx]; - let leaf = event_merkle_tree.leaf(leaf_index as usize); - if leaf == [0u8; 32] { - event_merkle_tree - .update(&leaf_hash.0, leaf_index as usize) - .unwrap(); - println!("append leaf index {}", leaf_index); + { + let slice_length = pre_output_queue.leaves.len().min(10); + for idx in 0..slice_length { + let leaf_index = pre_output_queue.leaf_indices[idx]; + let leaf_hash = &pre_output_queue.leaves[idx]; + let leaf = event_merkle_tree.leaf(leaf_index as usize); + if leaf == [0u8; 32] { + event_merkle_tree + .update(&leaf_hash.0, leaf_index as usize) + .unwrap(); + println!("append leaf index {}", leaf_index); + } } } } @@ -684,7 +685,7 @@ async fn test_batched_tree_token_transactions( assert_eq!(account.account.lamports, UnsignedInteger(1_000_000)); assert_eq!( account.account.owner, - SerializablePubkey::from(COMPRESSED_TOKEN_PROGRAM) + SerializablePubkey::from(LIGHT_TOKEN_PROGRAM_ID) ); assert_eq!(account.account.leaf_index.0, i as u64); assert_eq!(account.account.seq, None); diff --git a/tests/integration_tests/e2e_tests.rs b/tests/integration_tests/e2e_tests.rs index a3fd9625..1e26810a 100644 --- a/tests/integration_tests/e2e_tests.rs +++ b/tests/integration_tests/e2e_tests.rs @@ -620,8 +620,9 @@ async fn test_get_latest_non_voting_signatures_with_failures( async fn test_nullfiier_and_address_queue_transactions( #[values(DatabaseBackend::Sqlite, DatabaseBackend::Postgres)] db_backend: DatabaseBackend, ) { + use light_sdk_types::constants::ADDRESS_TREE_V1; use photon_indexer::api::method::get_multiple_new_address_proofs::{ - AddressListWithTrees, AddressWithTree, ADDRESS_TREE_V1, + AddressListWithTrees, AddressWithTree, }; let name = trim_test_name(function_name!()); diff --git a/tests/integration_tests/mock_tests.rs b/tests/integration_tests/mock_tests.rs index dd268669..a319c050 100644 --- a/tests/integration_tests/mock_tests.rs +++ b/tests/integration_tests/mock_tests.rs @@ -566,7 +566,7 @@ async fn test_persist_token_data( lamports: Set(Decimal::from(10)), slot_created: Set(slot), leaf_index: Set(i as i64), - discriminator: Set(Some(Decimal::from(1))), + discriminator: Set(Some(Decimal::from(1u64))), data_hash: Set(Some(Hash::new_unique().to_vec())), tree: Set(Pubkey::new_unique().to_bytes().to_vec()), queue: Set(Some(Pubkey::new_unique().to_bytes().to_vec())), @@ -578,6 +578,7 @@ async fn test_persist_token_data( token_datas.push(EnrichedTokenAccount { hash, token_data: token_data.clone(), + ata_owner: None, }); } @@ -963,6 +964,13 @@ async fn test_persisted_state_trees( } } +/// Helper to create a 32-byte padded value from a single byte for AddressV2 tests +fn padded_value(val: u8) -> Vec { + let mut v = vec![0u8; 31]; + v.push(val); + v +} + #[named] #[rstest] #[tokio::test] @@ -981,7 +989,10 @@ async fn test_indexed_merkle_trees( .await .unwrap(); - let values = (0..num_nodes).map(|i| vec![i * 4 + 1]).collect(); + // Use 32-byte padded values for AddressV2 hash compatibility + let values: Vec> = (0..num_nodes) + .map(|i| padded_value((i * 4 + 1) as u8)) + .collect(); let tree_height = 33; // prev. 4 // Create tree info cache - convert SerializablePubkey to Pubkey @@ -1015,7 +1026,7 @@ async fn test_indexed_merkle_trees( &setup.db_conn.begin().await.unwrap(), tree.to_bytes_vec(), tree_height, - vec![3], + padded_value(3), ) .await .unwrap(); @@ -1023,9 +1034,9 @@ async fn test_indexed_merkle_trees( let expected_model = indexed_trees::Model { tree: tree.to_bytes_vec(), leaf_index: 1, - value: vec![1], + value: padded_value(1), next_index: 2, - next_value: vec![5], + next_value: padded_value(5), seq: Some(0), }; @@ -1035,7 +1046,7 @@ async fn test_indexed_merkle_trees( .await .unwrap(); - let values = vec![vec![3]]; + let values = vec![padded_value(3)]; multi_append( &txn, @@ -1056,7 +1067,7 @@ async fn test_indexed_merkle_trees( &setup.db_conn.begin().await.unwrap(), tree.to_bytes_vec(), tree_height, - vec![4], + padded_value(4), ) .await .unwrap(); @@ -1064,9 +1075,9 @@ async fn test_indexed_merkle_trees( let expected_model = indexed_trees::Model { tree: tree.to_bytes_vec(), leaf_index: 3, - value: vec![3], + value: padded_value(3), next_index: 2, - next_value: vec![5], + next_value: padded_value(5), seq: Some(1), }; @@ -1119,10 +1130,11 @@ async fn test_get_multiple_new_address_proofs( async fn test_get_multiple_new_address_proofs_interop( #[values(DatabaseBackend::Sqlite, DatabaseBackend::Postgres)] db_backend: DatabaseBackend, ) { + use light_sdk_types::constants::ADDRESS_TREE_V1; use photon_indexer::api::method::{ get_multiple_new_address_proofs::{ get_multiple_new_address_proofs, get_multiple_new_address_proofs_v2, AddressList, - AddressListWithTrees, AddressWithTree, ADDRESS_TREE_V1, + AddressListWithTrees, AddressWithTree, }, get_validity_proof::CompressedProof, }; @@ -1157,6 +1169,7 @@ async fn test_get_multiple_new_address_proofs_interop( let mut validity_proof = get_validity_proof( &setup.db_conn, &setup.prover_url, + None, GetValidityProofRequest { new_addresses: addresses.clone(), new_addresses_with_trees: vec![], @@ -1190,6 +1203,7 @@ async fn test_get_multiple_new_address_proofs_interop( let mut validity_proof_v2 = get_validity_proof_v2( &setup.db_conn, &setup.prover_url, + None, GetValidityProofRequestV2 { new_addresses_with_trees: addresses_with_trees.clone(), hashes: vec![], diff --git a/tests/integration_tests/prod_tests.rs b/tests/integration_tests/prod_tests.rs index f8eeece0..ef60ac61 100644 --- a/tests/integration_tests/prod_tests.rs +++ b/tests/integration_tests/prod_tests.rs @@ -29,7 +29,7 @@ async fn test_incorrect_root_bug() { let devnet_db = Arc::new(SqlxPostgresConnector::from_sqlx_postgres_pool(pool)); let rpc_client = get_rpc_client("https://api.devnet.solana.com"); let prover_url = "http://localhost:3001"; - let api = PhotonApi::new(devnet_db.clone(), rpc_client, prover_url.to_string()); + let api = PhotonApi::new(devnet_db.clone(), rpc_client, prover_url.to_string(), None); let response = api .get_compressed_accounts_by_owner(GetCompressedAccountsByOwnerRequest { @@ -55,7 +55,7 @@ async fn test_mainnet_fra_invalid_address_tree_bug() { let devnet_db = Arc::new(SqlxPostgresConnector::from_sqlx_postgres_pool(pool)); let rpc_client = get_rpc_client("https://api.mainnet-beta.solana.com"); let prover_url = "http://localhost:3001"; - let api = PhotonApi::new(devnet_db.clone(), rpc_client, prover_url.to_string()); + let api = PhotonApi::new(devnet_db.clone(), rpc_client, prover_url.to_string(), None); let response = api .get_multiple_new_address_proofs_v2(AddressListWithTrees(vec![AddressWithTree { diff --git a/tests/integration_tests/utils.rs b/tests/integration_tests/utils.rs index 4ea0738e..98f82b20 100644 --- a/tests/integration_tests/utils.rs +++ b/tests/integration_tests/utils.rs @@ -108,7 +108,30 @@ pub async fn populate_test_tree_metadata(db: &DatabaseConnection) { 32, 2400, ), - // V2 Address Tree + // V2 State Trees (batched) - used by forester indexer_interface test + ( + "bmt1LryLZUMmF7ZtqESaw7wifBXLfXHQYoE4GAmrahU", + "oq1na8gojfdUhsfCpyjNt6h4JaDWtHf1yQj4koBWfto", + TreeType::StateV2, + 32, + 2400, + ), + ( + "bmt2UxoBxB9xWev4BkLvkGdapsz6sZGkzViPNph7VFi", + "oq2UkeMsJLfXt2QHzim242SUi3nvjJs8Pn7Eac9H9vg", + TreeType::StateV2, + 32, + 2400, + ), + // V2 State Tree - used by light-protocol default config (test_indexer_interface) + ( + "bmt5yU97jC88YXTuSukYHa8Z5Bi2ZDUtmzfkDTA2mG2", + "oq5oh5ZR3yGomuQgFduNDzjtGvVWfDRGLuDVjv9a96P", + TreeType::StateV2, + 32, + 2400, + ), + // V2 Address Trees ( "EzKE84aVTkCUhDHLELqyJaq1Y7UVVmqxXqZjVHwHY3rK", "EzKE84aVTkCUhDHLELqyJaq1Y7UVVmqxXqZjVHwHY3rK", @@ -225,7 +248,7 @@ pub async fn setup_with_options(name: String, opts: TestSetupOptions) -> TestSet }; let client = get_rpc_client(&rpc_url); let prover_url = "http://127.0.0.1:3001".to_string(); - let api = PhotonApi::new(db_conn.clone(), client.clone(), prover_url.clone()); + let api = PhotonApi::new(db_conn.clone(), client.clone(), prover_url.clone(), None); TestSetup { name, db_conn, @@ -559,6 +582,7 @@ pub async fn index_transaction( let state_update = parse_transaction(db_conn.as_ref(), &tx_info, 0, rpc_client.as_ref()) .await .unwrap(); + persist_state_update_using_connection(db_conn.as_ref(), state_update) .await .unwrap(); From 6c7813b0b99b373153c1615148528280335cc55c Mon Sep 17 00:00:00 2001 From: Sergey Timoshin Date: Tue, 3 Mar 2026 17:38:02 +0000 Subject: [PATCH 05/12] refactor: remove unused backfill migration for mint onchain pubkey --- src/common/token_layout.rs | 4 - ...210_000001_backfill_mint_onchain_pubkey.rs | 131 ------------------ src/migration/migrations/standard/mod.rs | 2 - 3 files changed, 137 deletions(-) delete mode 100644 src/migration/migrations/standard/m20260210_000001_backfill_mint_onchain_pubkey.rs diff --git a/src/common/token_layout.rs b/src/common/token_layout.rs index 1376fb3e..9f4ee1c4 100644 --- a/src/common/token_layout.rs +++ b/src/common/token_layout.rs @@ -4,8 +4,6 @@ pub const SPL_TOKEN_ACCOUNT_BASE_LEN: usize = 165; /// Account type marker byte offset in token/mint account data. pub const TOKEN_ACCOUNT_TYPE_OFFSET: usize = SPL_TOKEN_ACCOUNT_BASE_LEN; -/// 1-indexed equivalent for SQL substring functions. -pub const TOKEN_ACCOUNT_TYPE_OFFSET_SQL: usize = TOKEN_ACCOUNT_TYPE_OFFSET + 1; /// AccountType::Mint discriminator value. pub const ACCOUNT_TYPE_MINT: u8 = 1; @@ -14,5 +12,3 @@ pub const ACCOUNT_TYPE_MINT: u8 = 1; pub const COMPRESSED_MINT_PDA_OFFSET: usize = 84; pub const COMPRESSED_MINT_PDA_LEN: usize = 32; pub const COMPRESSED_MINT_PDA_END: usize = COMPRESSED_MINT_PDA_OFFSET + COMPRESSED_MINT_PDA_LEN; -/// 1-indexed equivalent for SQL substring functions. -pub const COMPRESSED_MINT_PDA_OFFSET_SQL: usize = COMPRESSED_MINT_PDA_OFFSET + 1; diff --git a/src/migration/migrations/standard/m20260210_000001_backfill_mint_onchain_pubkey.rs b/src/migration/migrations/standard/m20260210_000001_backfill_mint_onchain_pubkey.rs deleted file mode 100644 index eb4e0474..00000000 --- a/src/migration/migrations/standard/m20260210_000001_backfill_mint_onchain_pubkey.rs +++ /dev/null @@ -1,131 +0,0 @@ -use sea_orm_migration::{ - prelude::*, - sea_orm::{ConnectionTrait, DatabaseBackend, Statement}, -}; - -use crate::common::token_layout::{ - COMPRESSED_MINT_PDA_LEN, COMPRESSED_MINT_PDA_OFFSET_SQL, TOKEN_ACCOUNT_TYPE_OFFSET, - TOKEN_ACCOUNT_TYPE_OFFSET_SQL, -}; - -#[derive(DeriveMigrationName)] -pub struct Migration; - -/// Raw 32-byte pubkey for cTokenmWW8bLPjZEBAUgYy3zKxQZW6VKi7bqNFEVv3m -const LIGHT_TOKEN_PROGRAM_ID_HEX: &str = - "0915a35723794e8fb65d075b6b72699c38dd02e5948b75b0e5a0418e80975b44"; - -fn backfill_mint_onchain_pubkey_sql(backend: DatabaseBackend) -> Result { - match backend { - DatabaseBackend::Postgres => Ok(format!( - "UPDATE accounts \ - SET onchain_pubkey = substring(data from {COMPRESSED_MINT_PDA_OFFSET_SQL} for {COMPRESSED_MINT_PDA_LEN}) \ - WHERE onchain_pubkey IS NULL \ - AND spent = false \ - AND data IS NOT NULL \ - AND length(data) > {TOKEN_ACCOUNT_TYPE_OFFSET} \ - AND substring(data from {TOKEN_ACCOUNT_TYPE_OFFSET_SQL} for 1) = E'\\\\x01' \ - AND owner = E'\\\\x{LIGHT_TOKEN_PROGRAM_ID_HEX}'" - )), - DatabaseBackend::Sqlite => Ok(format!( - "UPDATE accounts \ - SET onchain_pubkey = substr(data, {COMPRESSED_MINT_PDA_OFFSET_SQL}, {COMPRESSED_MINT_PDA_LEN}) \ - WHERE onchain_pubkey IS NULL \ - AND spent = 0 \ - AND data IS NOT NULL \ - AND length(data) > {TOKEN_ACCOUNT_TYPE_OFFSET} \ - AND substr(data, {TOKEN_ACCOUNT_TYPE_OFFSET_SQL}, 1) = X'01' \ - AND owner = X'{LIGHT_TOKEN_PROGRAM_ID_HEX}'" - )), - _ => Err(DbErr::Custom("Unsupported database backend".to_string())), - } -} - -fn clear_backfilled_mint_onchain_pubkey_sql(backend: DatabaseBackend) -> Result { - match backend { - DatabaseBackend::Postgres => Ok(format!( - "UPDATE accounts \ - SET onchain_pubkey = NULL \ - WHERE onchain_pubkey IS NOT NULL \ - AND spent = false \ - AND data IS NOT NULL \ - AND length(data) > {TOKEN_ACCOUNT_TYPE_OFFSET} \ - AND substring(data from {TOKEN_ACCOUNT_TYPE_OFFSET_SQL} for 1) = E'\\\\x01' \ - AND owner = E'\\\\x{LIGHT_TOKEN_PROGRAM_ID_HEX}' \ - AND onchain_pubkey = substring(data from {COMPRESSED_MINT_PDA_OFFSET_SQL} for {COMPRESSED_MINT_PDA_LEN})" - )), - DatabaseBackend::Sqlite => Ok(format!( - "UPDATE accounts \ - SET onchain_pubkey = NULL \ - WHERE onchain_pubkey IS NOT NULL \ - AND spent = 0 \ - AND data IS NOT NULL \ - AND length(data) > {TOKEN_ACCOUNT_TYPE_OFFSET} \ - AND substr(data, {TOKEN_ACCOUNT_TYPE_OFFSET_SQL}, 1) = X'01' \ - AND owner = X'{LIGHT_TOKEN_PROGRAM_ID_HEX}' \ - AND onchain_pubkey = substr(data, {COMPRESSED_MINT_PDA_OFFSET_SQL}, {COMPRESSED_MINT_PDA_LEN})" - )), - _ => Err(DbErr::Custom("Unsupported database backend".to_string())), - } -} - -#[async_trait::async_trait] -impl MigrationTrait for Migration { - async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> { - let backend = manager.get_database_backend(); - - // Backfill onchain_pubkey for compressed mint accounts. - // Compressed mints have: - // - owner = LIGHT_TOKEN_PROGRAM_ID - // - data length > 165 bytes - // - account_type byte at offset 165 (0-indexed) == 0x01 (Mint) - // The mint PDA is at data bytes [84..116] (0-indexed), which is - // substring starting at byte 85 (1-indexed) for 32 bytes. - let sql = backfill_mint_onchain_pubkey_sql(backend)?; - - manager - .get_connection() - .execute(Statement::from_string(backend, sql)) - .await?; - - Ok(()) - } - - async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> { - let backend = manager.get_database_backend(); - - // Clear only values this migration backfilled, preserving independently-updated rows. - let sql = clear_backfilled_mint_onchain_pubkey_sql(backend)?; - - manager - .get_connection() - .execute(Statement::from_string(backend, sql)) - .await?; - - Ok(()) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_backfill_sql_uses_shared_offsets() { - let pg_sql = backfill_mint_onchain_pubkey_sql(DatabaseBackend::Postgres).unwrap(); - assert!(pg_sql.contains(&format!( - "substring(data from {COMPRESSED_MINT_PDA_OFFSET_SQL} for {COMPRESSED_MINT_PDA_LEN})" - ))); - assert!(pg_sql.contains(&format!("length(data) > {TOKEN_ACCOUNT_TYPE_OFFSET}"))); - assert!(pg_sql.contains(&format!( - "substring(data from {TOKEN_ACCOUNT_TYPE_OFFSET_SQL} for 1)" - ))); - } - - #[test] - fn test_down_sql_only_clears_backfilled_values() { - let sqlite_sql = clear_backfilled_mint_onchain_pubkey_sql(DatabaseBackend::Sqlite).unwrap(); - assert!(sqlite_sql.contains("onchain_pubkey = substr(data,")); - assert!(sqlite_sql.contains("SET onchain_pubkey = NULL")); - } -} diff --git a/src/migration/migrations/standard/mod.rs b/src/migration/migrations/standard/mod.rs index ef2486ed..83701669 100644 --- a/src/migration/migrations/standard/mod.rs +++ b/src/migration/migrations/standard/mod.rs @@ -16,7 +16,6 @@ pub mod m20250923_000001_add_tree_metadata; pub mod m20251021_000001_optimize_nullifier_queue_index; pub mod m20260127_000001_add_onchain_pubkey; pub mod m20260201_000002_add_ata_owner; -pub mod m20260210_000001_backfill_mint_onchain_pubkey; pub mod m20260210_000002_add_ata_owner_index; pub fn get_standard_migrations() -> Vec> { vec![ @@ -36,7 +35,6 @@ pub fn get_standard_migrations() -> Vec> { Box::new(m20251021_000001_optimize_nullifier_queue_index::Migration), Box::new(m20260127_000001_add_onchain_pubkey::Migration), Box::new(m20260201_000002_add_ata_owner::Migration), - Box::new(m20260210_000001_backfill_mint_onchain_pubkey::Migration), Box::new(m20260210_000002_add_ata_owner_index::Migration), ] } From 0f7e46c70b8600b9acd20e58917bee5ea2be9a08 Mon Sep 17 00:00:00 2001 From: Sergey Timoshin Date: Tue, 3 Mar 2026 17:40:02 +0000 Subject: [PATCH 06/12] refactor: rename compressed mint PDA constants to light mint PDA for clarity --- src/common/token_layout.rs | 6 +++--- src/ingester/persist/mod.rs | 9 ++++----- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/common/token_layout.rs b/src/common/token_layout.rs index 9f4ee1c4..3310bce5 100644 --- a/src/common/token_layout.rs +++ b/src/common/token_layout.rs @@ -9,6 +9,6 @@ pub const TOKEN_ACCOUNT_TYPE_OFFSET: usize = SPL_TOKEN_ACCOUNT_BASE_LEN; pub const ACCOUNT_TYPE_MINT: u8 = 1; /// Compressed mint PDA offset range `[84..116]` (32 bytes). -pub const COMPRESSED_MINT_PDA_OFFSET: usize = 84; -pub const COMPRESSED_MINT_PDA_LEN: usize = 32; -pub const COMPRESSED_MINT_PDA_END: usize = COMPRESSED_MINT_PDA_OFFSET + COMPRESSED_MINT_PDA_LEN; +pub const LIGHT_MINT_PDA_OFFSET: usize = 84; +pub const LIGHT_MINT_PDA_LEN: usize = 32; +pub const LIGHT_MINT_PDA_END: usize = LIGHT_MINT_PDA_OFFSET + LIGHT_MINT_PDA_LEN; diff --git a/src/ingester/persist/mod.rs b/src/ingester/persist/mod.rs index d6f89651..4345288c 100644 --- a/src/ingester/persist/mod.rs +++ b/src/ingester/persist/mod.rs @@ -4,8 +4,7 @@ use crate::{ api::method::utils::PAGE_LIMIT, common::{ token_layout::{ - ACCOUNT_TYPE_MINT, COMPRESSED_MINT_PDA_END, COMPRESSED_MINT_PDA_OFFSET, - TOKEN_ACCOUNT_TYPE_OFFSET, + ACCOUNT_TYPE_MINT, LIGHT_MINT_PDA_END, LIGHT_MINT_PDA_OFFSET, TOKEN_ACCOUNT_TYPE_OFFSET, }, typedefs::{hash::Hash, token_data::TokenData}, }, @@ -521,7 +520,7 @@ fn extract_onchain_pubkey(account: &AccountWithContext) -> Option> { && data.data.0.len() > TOKEN_ACCOUNT_TYPE_OFFSET && data.data.0[TOKEN_ACCOUNT_TYPE_OFFSET] == ACCOUNT_TYPE_MINT { - return Some(data.data.0[COMPRESSED_MINT_PDA_OFFSET..COMPRESSED_MINT_PDA_END].to_vec()); + return Some(data.data.0[LIGHT_MINT_PDA_OFFSET..LIGHT_MINT_PDA_END].to_vec()); } None @@ -739,7 +738,7 @@ mod tests { fn test_extract_onchain_pubkey_for_compressed_mint() { let mut data = vec![0u8; TOKEN_ACCOUNT_TYPE_OFFSET + 1]; let expected = [7u8; 32]; - data[COMPRESSED_MINT_PDA_OFFSET..COMPRESSED_MINT_PDA_END].copy_from_slice(&expected); + data[LIGHT_MINT_PDA_OFFSET..LIGHT_MINT_PDA_END].copy_from_slice(&expected); data[TOKEN_ACCOUNT_TYPE_OFFSET] = ACCOUNT_TYPE_MINT; let account = sample_account_with_context(LIGHT_TOKEN_PROGRAM_ID, 0, data); @@ -750,7 +749,7 @@ mod tests { #[test] fn test_extract_onchain_pubkey_skips_ctoken_discriminator() { let mut data = vec![0u8; TOKEN_ACCOUNT_TYPE_OFFSET + 1]; - data[COMPRESSED_MINT_PDA_OFFSET..COMPRESSED_MINT_PDA_END].copy_from_slice(&[8u8; 32]); + data[LIGHT_MINT_PDA_OFFSET..LIGHT_MINT_PDA_END].copy_from_slice(&[8u8; 32]); data[TOKEN_ACCOUNT_TYPE_OFFSET] = ACCOUNT_TYPE_MINT; let account = sample_account_with_context( From e824964cdb3b7acdd5c7399d5f6c0d4643191144 Mon Sep 17 00:00:00 2001 From: Sergey Timoshin Date: Tue, 3 Mar 2026 17:44:43 +0000 Subject: [PATCH 07/12] refactor: remove parse_account_discriminator function and update usages for clarity --- src/api/method/utils.rs | 6 ------ src/common/typedefs/account/context.rs | 4 ++-- src/common/typedefs/account/v1.rs | 4 ++-- src/common/typedefs/account/v2.rs | 4 ++-- 4 files changed, 6 insertions(+), 12 deletions(-) diff --git a/src/api/method/utils.rs b/src/api/method/utils.rs index 56240c5d..1cf78467 100644 --- a/src/api/method/utils.rs +++ b/src/api/method/utils.rs @@ -35,12 +35,6 @@ pub fn parse_decimal(value: Decimal) -> Result { .map_err(|_| PhotonApiError::UnexpectedError("Invalid decimal value".to_string())) } -pub fn parse_account_discriminator( - discriminator: Option, -) -> Result, PhotonApiError> { - discriminator.map(parse_decimal).transpose() -} - pub(crate) fn parse_leaf_index(leaf_index: i64) -> Result { leaf_index .try_into() diff --git a/src/common/typedefs/account/context.rs b/src/common/typedefs/account/context.rs index 73dca488..bc848c3a 100644 --- a/src/common/typedefs/account/context.rs +++ b/src/common/typedefs/account/context.rs @@ -1,5 +1,5 @@ use crate::api::error::PhotonApiError; -use crate::api::method::utils::{parse_account_discriminator, parse_decimal, parse_leaf_index}; +use crate::api::method::utils::{parse_decimal, parse_leaf_index}; use crate::common::typedefs::account::{Account, AccountData}; use crate::common::typedefs::bs64_string::Base64String; use crate::common::typedefs::hash::Hash; @@ -107,7 +107,7 @@ impl TryFrom for AccountWithContext { type Error = PhotonApiError; fn try_from(account: Model) -> Result { - let parsed_discriminator = parse_account_discriminator(account.discriminator.clone())?; + let parsed_discriminator = account.discriminator.map(parse_decimal).transpose()?; let data = match (account.data, account.data_hash, parsed_discriminator) { (Some(data), Some(data_hash), Some(discriminator)) => Some(AccountData { data: Base64String(data), diff --git a/src/common/typedefs/account/v1.rs b/src/common/typedefs/account/v1.rs index 60f63312..051f6461 100644 --- a/src/common/typedefs/account/v1.rs +++ b/src/common/typedefs/account/v1.rs @@ -1,5 +1,5 @@ use crate::api::error::PhotonApiError; -use crate::api::method::utils::{parse_account_discriminator, parse_decimal}; +use crate::api::method::utils::parse_decimal; use crate::common::typedefs::bs64_string::Base64String; use crate::common::typedefs::hash::Hash; use crate::common::typedefs::serializable_pubkey::SerializablePubkey; @@ -75,7 +75,7 @@ impl TryFrom for Account { type Error = PhotonApiError; fn try_from(account: Model) -> Result { - let parsed_discriminator = parse_account_discriminator(account.discriminator.clone())?; + let parsed_discriminator = account.discriminator.map(parse_decimal).transpose()?; let data = match (account.data, account.data_hash, parsed_discriminator) { (Some(data), Some(data_hash), Some(discriminator)) => Some(AccountData { data: Base64String(data), diff --git a/src/common/typedefs/account/v2.rs b/src/common/typedefs/account/v2.rs index dba9a545..9a492ada 100644 --- a/src/common/typedefs/account/v2.rs +++ b/src/common/typedefs/account/v2.rs @@ -1,6 +1,6 @@ use crate::api::error::PhotonApiError; use crate::api::method::get_validity_proof::MerkleContextV2; -use crate::api::method::utils::{parse_account_discriminator, parse_decimal}; +use crate::api::method::utils::parse_decimal; use crate::common::typedefs::account::{AccountData, AccountWithContext}; use crate::common::typedefs::bs64_string::Base64String; use crate::common::typedefs::hash::Hash; @@ -58,7 +58,7 @@ impl TryFrom for AccountV2 { type Error = PhotonApiError; fn try_from(account: Model) -> Result { - let parsed_discriminator = parse_account_discriminator(account.discriminator.clone())?; + let parsed_discriminator = account.discriminator.map(parse_decimal).transpose()?; let data = match (account.data, account.data_hash, parsed_discriminator) { (Some(data), Some(data_hash), Some(discriminator)) => Some(AccountData { data: Base64String(data), From 37f5e22ad87841a5942e5be11a3fb42bc3d26ffa Mon Sep 17 00:00:00 2001 From: Sergey Timoshin Date: Tue, 3 Mar 2026 18:40:08 +0000 Subject: [PATCH 08/12] feat: add discriminator_v2 field and update related logic for account processing --- .../get_compressed_accounts_by_owner/v1.rs | 2 +- .../get_compressed_accounts_by_owner/v2.rs | 2 +- src/common/typedefs/account/context.rs | 10 +++++- src/common/typedefs/account/v1.rs | 10 +++++- src/common/typedefs/account/v2.rs | 10 +++++- src/dao/generated/accounts.rs | 1 + src/ingester/persist/mod.rs | 5 +++ .../m20260220_000001_add_discriminator_v2.rs | 31 +++++++++++++++++++ src/migration/migrations/standard/mod.rs | 2 ++ src/migration/model/table.rs | 1 + 10 files changed, 69 insertions(+), 5 deletions(-) create mode 100644 src/migration/migrations/standard/m20260220_000001_add_discriminator_v2.rs diff --git a/src/api/method/get_compressed_accounts_by_owner/v1.rs b/src/api/method/get_compressed_accounts_by_owner/v1.rs index 5e7825e1..367e09d5 100644 --- a/src/api/method/get_compressed_accounts_by_owner/v1.rs +++ b/src/api/method/get_compressed_accounts_by_owner/v1.rs @@ -39,7 +39,7 @@ pub async fn get_compressed_accounts_by_owner( query_builder.build_base_query(conn, &request)?; let columns = format!( - "hash, {}, data_hash, address, onchain_pubkey, owner, tree, leaf_index, seq, slot_created, spent, prev_spent, lamports, discriminator, queue, in_output_queue, nullifier_queue_index, nullified_in_tree, nullifier, tx_hash, tree_type", + "hash, {}, data_hash, address, onchain_pubkey, owner, tree, leaf_index, seq, slot_created, spent, prev_spent, lamports, discriminator, discriminator_v2, queue, in_output_queue, nullifier_queue_index, nullified_in_tree, nullifier, tx_hash, tree_type", query_builder.data_column ); diff --git a/src/api/method/get_compressed_accounts_by_owner/v2.rs b/src/api/method/get_compressed_accounts_by_owner/v2.rs index a953224b..b241380b 100644 --- a/src/api/method/get_compressed_accounts_by_owner/v2.rs +++ b/src/api/method/get_compressed_accounts_by_owner/v2.rs @@ -41,7 +41,7 @@ pub async fn get_compressed_accounts_by_owner_v2( query_builder.build_base_query(conn, &request)?; let columns = format!( - "hash, {}, data_hash, address, onchain_pubkey, owner, tree, queue, in_output_queue, nullifier_queue_index, tx_hash, nullifier, leaf_index, seq, slot_created, spent, prev_spent, lamports, discriminator, nullified_in_tree, tree_type", + "hash, {}, data_hash, address, onchain_pubkey, owner, tree, queue, in_output_queue, nullifier_queue_index, tx_hash, nullifier, leaf_index, seq, slot_created, spent, prev_spent, lamports, discriminator, discriminator_v2, nullified_in_tree, tree_type", query_builder.data_column ); diff --git a/src/common/typedefs/account/context.rs b/src/common/typedefs/account/context.rs index bc848c3a..3141348d 100644 --- a/src/common/typedefs/account/context.rs +++ b/src/common/typedefs/account/context.rs @@ -107,7 +107,15 @@ impl TryFrom for AccountWithContext { type Error = PhotonApiError; fn try_from(account: Model) -> Result { - let parsed_discriminator = account.discriminator.map(parse_decimal).transpose()?; + let parsed_discriminator = match account.discriminator_v2 { + Some(bytes) => { + let arr: [u8; 8] = bytes.try_into().map_err(|_| { + PhotonApiError::UnexpectedError("Invalid discriminator_v2 length".to_string()) + })?; + Some(u64::from_le_bytes(arr)) + } + None => account.discriminator.map(parse_decimal).transpose()?, + }; let data = match (account.data, account.data_hash, parsed_discriminator) { (Some(data), Some(data_hash), Some(discriminator)) => Some(AccountData { data: Base64String(data), diff --git a/src/common/typedefs/account/v1.rs b/src/common/typedefs/account/v1.rs index 051f6461..7ab15a8c 100644 --- a/src/common/typedefs/account/v1.rs +++ b/src/common/typedefs/account/v1.rs @@ -75,7 +75,15 @@ impl TryFrom for Account { type Error = PhotonApiError; fn try_from(account: Model) -> Result { - let parsed_discriminator = account.discriminator.map(parse_decimal).transpose()?; + let parsed_discriminator = match account.discriminator_v2 { + Some(bytes) => { + let arr: [u8; 8] = bytes.try_into().map_err(|_| { + PhotonApiError::UnexpectedError("Invalid discriminator_v2 length".to_string()) + })?; + Some(u64::from_le_bytes(arr)) + } + None => account.discriminator.map(parse_decimal).transpose()?, + }; let data = match (account.data, account.data_hash, parsed_discriminator) { (Some(data), Some(data_hash), Some(discriminator)) => Some(AccountData { data: Base64String(data), diff --git a/src/common/typedefs/account/v2.rs b/src/common/typedefs/account/v2.rs index 9a492ada..d809a56e 100644 --- a/src/common/typedefs/account/v2.rs +++ b/src/common/typedefs/account/v2.rs @@ -58,7 +58,15 @@ impl TryFrom for AccountV2 { type Error = PhotonApiError; fn try_from(account: Model) -> Result { - let parsed_discriminator = account.discriminator.map(parse_decimal).transpose()?; + let parsed_discriminator = match account.discriminator_v2 { + Some(bytes) => { + let arr: [u8; 8] = bytes.try_into().map_err(|_| { + PhotonApiError::UnexpectedError("Invalid discriminator_v2 length".to_string()) + })?; + Some(u64::from_le_bytes(arr)) + } + None => account.discriminator.map(parse_decimal).transpose()?, + }; let data = match (account.data, account.data_hash, parsed_discriminator) { (Some(data), Some(data_hash), Some(discriminator)) => Some(AccountData { data: Base64String(data), diff --git a/src/dao/generated/accounts.rs b/src/dao/generated/accounts.rs index e53aa511..bb49e407 100644 --- a/src/dao/generated/accounts.rs +++ b/src/dao/generated/accounts.rs @@ -27,6 +27,7 @@ pub struct Model { pub nullifier: Option>, pub tx_hash: Option>, pub onchain_pubkey: Option>, + pub discriminator_v2: Option>, #[sea_orm(column_type = "Decimal(Some((23, 0)))", nullable)] pub discriminator: Option, } diff --git a/src/ingester/persist/mod.rs b/src/ingester/persist/mod.rs index 4345288c..544a47c1 100644 --- a/src/ingester/persist/mod.rs +++ b/src/ingester/persist/mod.rs @@ -446,6 +446,11 @@ async fn append_output_accounts( hash: Set(account.account.hash.to_vec()), address: Set(account.account.address.map(|x| x.to_bytes_vec())), onchain_pubkey: Set(onchain_pubkey), + discriminator_v2: Set(account + .account + .data + .as_ref() + .map(|x| x.discriminator.0.to_le_bytes().to_vec())), discriminator: Set(account .account .data diff --git a/src/migration/migrations/standard/m20260220_000001_add_discriminator_v2.rs b/src/migration/migrations/standard/m20260220_000001_add_discriminator_v2.rs new file mode 100644 index 00000000..7adc61a9 --- /dev/null +++ b/src/migration/migrations/standard/m20260220_000001_add_discriminator_v2.rs @@ -0,0 +1,31 @@ +use sea_orm_migration::prelude::*; + +use super::super::super::model::table::Accounts; + +#[derive(DeriveMigrationName)] +pub struct Migration; + +#[async_trait::async_trait] +impl MigrationTrait for Migration { + async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> { + manager + .alter_table( + Table::alter() + .table(Accounts::Table) + .add_column(ColumnDef::new(Accounts::DiscriminatorV2).binary().null()) + .to_owned(), + ) + .await + } + + async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> { + manager + .alter_table( + Table::alter() + .table(Accounts::Table) + .drop_column(Accounts::DiscriminatorV2) + .to_owned(), + ) + .await + } +} diff --git a/src/migration/migrations/standard/mod.rs b/src/migration/migrations/standard/mod.rs index 83701669..53527423 100644 --- a/src/migration/migrations/standard/mod.rs +++ b/src/migration/migrations/standard/mod.rs @@ -17,6 +17,7 @@ pub mod m20251021_000001_optimize_nullifier_queue_index; pub mod m20260127_000001_add_onchain_pubkey; pub mod m20260201_000002_add_ata_owner; pub mod m20260210_000002_add_ata_owner_index; +pub mod m20260220_000001_add_discriminator_v2; pub fn get_standard_migrations() -> Vec> { vec![ Box::new(m20220101_000001_init::Migration), @@ -36,5 +37,6 @@ pub fn get_standard_migrations() -> Vec> { Box::new(m20260127_000001_add_onchain_pubkey::Migration), Box::new(m20260201_000002_add_ata_owner::Migration), Box::new(m20260210_000002_add_ata_owner_index::Migration), + Box::new(m20260220_000001_add_discriminator_v2::Migration), ] } diff --git a/src/migration/model/table.rs b/src/migration/model/table.rs index 09ad7e39..8c2ef0b4 100644 --- a/src/migration/model/table.rs +++ b/src/migration/model/table.rs @@ -17,6 +17,7 @@ pub enum Accounts { Hash, Address, OnchainPubkey, + DiscriminatorV2, Data, DataHash, Owner, From 4972998163a36aeeae855d351fe282a8892f387d Mon Sep 17 00:00:00 2001 From: Sergey Timoshin Date: Tue, 3 Mar 2026 14:25:01 +0000 Subject: [PATCH 09/12] feat: add Account Interface APIs (getAccountInterface, getMultipleAccountInterfaces) - Add getAccountInterface and getMultipleAccountInterfaces RPC methods - Implement racing logic for compressed vs on-chain account resolution - Add interface types (AccountInterface, SolanaAccountData) - Register new methods in RPC server and OpenAPI spec - Add integration tests with snapshot testing - Add test transaction data for indexer interface --- src/api/api.rs | 31 + .../method/interface/get_account_interface.rs | 22 + .../get_multiple_account_interfaces.rs | 107 ++ src/api/method/interface/mod.rs | 8 + src/api/method/interface/racing.rs | 1424 +++++++++++++++++ src/api/method/interface/types.rs | 87 + src/api/method/mod.rs | 1 + src/api/rpc_server.rs | 21 + src/openapi/mod.rs | 4 + ...2eSZuEdjKrqw9ez2yhxJt5U7S8LYdrUdSq1KKZid4X | 158 ++ ...2hDxNxEWoiybvn4p7ua2nw3scYeNo6htYCSuBYviFd | 157 ++ ...iqFp6pXAB9fdrFHq66u6zvv3ddY5xLAzpmREASatnB | 114 ++ ...bufv4nTbqrp7ZQ5fpopHh7pU9RnimvvR31XeckFt9F | 158 ++ ...USeWpegrdfvrPkGA8qxfBFpu5ru9DjTFktRSB6w4s1 | 115 ++ tests/integration_tests/interface_tests.rs | 222 +++ tests/integration_tests/main.rs | 1 + ...ace_compressed_only-account-interface.snap | 11 + ...terface_nonexistent-account-interface.snap | 11 + ...nterfaces-multiple-account-interfaces.snap | 15 + ...face_by_owner-token-account-interface.snap | 11 + ...mpressed_only-token-account-interface.snap | 11 + 21 files changed, 2689 insertions(+) create mode 100644 src/api/method/interface/get_account_interface.rs create mode 100644 src/api/method/interface/get_multiple_account_interfaces.rs create mode 100644 src/api/method/interface/mod.rs create mode 100644 src/api/method/interface/racing.rs create mode 100644 src/api/method/interface/types.rs create mode 100644 tests/data/transactions/indexer_interface/2Q8KnAuf9TuPThbkEFZp6tfFC9bsGBVEpvoDJzLZAAEDKi2eSZuEdjKrqw9ez2yhxJt5U7S8LYdrUdSq1KKZid4X create mode 100644 tests/data/transactions/indexer_interface/2YTv5hjSmRAgfwoNHdc4DRDFWW7fqQb57f9s8Rxtu9u6jA2hDxNxEWoiybvn4p7ua2nw3scYeNo6htYCSuBYviFd create mode 100644 tests/data/transactions/indexer_interface/2afJTiZyNEMvrKJasbDFqPaTaLWMttjAHnBLgm7CizqzqsiqFp6pXAB9fdrFHq66u6zvv3ddY5xLAzpmREASatnB create mode 100644 tests/data/transactions/indexer_interface/5bLkzWasPYAivyrVvrt5UN3amycT3MSdNB2evCEMyuW1Ajbufv4nTbqrp7ZQ5fpopHh7pU9RnimvvR31XeckFt9F create mode 100644 tests/data/transactions/indexer_interface/628ZqqrNWuVUfHF2seH7XQEZqBJtWA8NaAjcVeH96XWaubUSeWpegrdfvrPkGA8qxfBFpu5ru9DjTFktRSB6w4s1 create mode 100644 tests/integration_tests/interface_tests.rs create mode 100644 tests/integration_tests/snapshots/integration_tests__interface_tests__get_account_interface_compressed_only-account-interface.snap create mode 100644 tests/integration_tests/snapshots/integration_tests__interface_tests__get_account_interface_nonexistent-account-interface.snap create mode 100644 tests/integration_tests/snapshots/integration_tests__interface_tests__get_multiple_account_interfaces-multiple-account-interfaces.snap create mode 100644 tests/integration_tests/snapshots/integration_tests__interface_tests__get_token_account_interface_by_owner-token-account-interface.snap create mode 100644 tests/integration_tests/snapshots/integration_tests__interface_tests__get_token_account_interface_compressed_only-token-account-interface.snap diff --git a/src/api/api.rs b/src/api/api.rs index e323c2cd..c382db1a 100644 --- a/src/api/api.rs +++ b/src/api/api.rs @@ -85,6 +85,11 @@ use crate::api::method::get_validity_proof::{ GetValidityProofRequestDocumentation, GetValidityProofRequestV2, GetValidityProofResponse, GetValidityProofResponseV2, }; +use crate::api::method::interface::{ + get_account_interface, get_multiple_account_interfaces, GetAccountInterfaceRequest, + GetAccountInterfaceResponse, GetMultipleAccountInterfacesRequest, + GetMultipleAccountInterfacesResponse, +}; use crate::api::method::utils::{ AccountBalanceResponse, GetLatestSignaturesRequest, GetNonPaginatedSignaturesResponse, GetNonPaginatedSignaturesResponseWithError, GetPaginatedSignaturesResponse, HashRequest, @@ -402,6 +407,21 @@ impl PhotonApi { get_latest_non_voting_signatures(self.db_conn.as_ref(), request).await } + // Interface endpoints - race hot (on-chain) and cold (compressed) lookups + pub async fn get_account_interface( + &self, + request: GetAccountInterfaceRequest, + ) -> Result { + get_account_interface(&self.db_conn, &self.rpc_client, request).await + } + + pub async fn get_multiple_account_interfaces( + &self, + request: GetMultipleAccountInterfacesRequest, + ) -> Result { + get_multiple_account_interfaces(&self.db_conn, &self.rpc_client, request).await + } + pub fn method_api_specs() -> Vec { vec![ OpenApiSpec { @@ -591,6 +611,17 @@ impl PhotonApi { request: None, response: UnsignedInteger::schema().1, }, + // Interface endpoints + OpenApiSpec { + name: "getAccountInterface".to_string(), + request: Some(GetAccountInterfaceRequest::schema().1), + response: GetAccountInterfaceResponse::schema().1, + }, + OpenApiSpec { + name: "getMultipleAccountInterfaces".to_string(), + request: Some(GetMultipleAccountInterfacesRequest::schema().1), + response: GetMultipleAccountInterfacesResponse::schema().1, + }, ] } } diff --git a/src/api/method/interface/get_account_interface.rs b/src/api/method/interface/get_account_interface.rs new file mode 100644 index 00000000..cc6aa514 --- /dev/null +++ b/src/api/method/interface/get_account_interface.rs @@ -0,0 +1,22 @@ +use sea_orm::DatabaseConnection; +use solana_client::nonblocking::rpc_client::RpcClient; + +use crate::api::error::PhotonApiError; +use crate::common::typedefs::context::Context; + +use super::racing::race_hot_cold; +use super::types::{GetAccountInterfaceRequest, GetAccountInterfaceResponse}; + +/// Get account data from either on-chain or compressed sources. +/// Races both lookups and returns the result with the higher slot. +pub async fn get_account_interface( + conn: &DatabaseConnection, + rpc_client: &RpcClient, + request: GetAccountInterfaceRequest, +) -> Result { + let context = Context::extract(conn).await?; + + let value = race_hot_cold(rpc_client, conn, &request.address, None).await?; + + Ok(GetAccountInterfaceResponse { context, value }) +} diff --git a/src/api/method/interface/get_multiple_account_interfaces.rs b/src/api/method/interface/get_multiple_account_interfaces.rs new file mode 100644 index 00000000..576b8cf9 --- /dev/null +++ b/src/api/method/interface/get_multiple_account_interfaces.rs @@ -0,0 +1,107 @@ +use sea_orm::DatabaseConnection; +use solana_client::nonblocking::rpc_client::RpcClient; +use tokio::sync::Semaphore; + +use crate::api::error::PhotonApiError; +use crate::common::typedefs::context::Context; +use crate::common::typedefs::serializable_pubkey::SerializablePubkey; + +use super::racing::{get_distinct_owners_with_addresses, race_hot_cold}; +use super::types::{ + AccountInterface, GetMultipleAccountInterfacesRequest, GetMultipleAccountInterfacesResponse, + MAX_BATCH_SIZE, +}; + +/// Maximum concurrent hot+cold lookups per batch request. +const MAX_CONCURRENT_LOOKUPS: usize = 20; + +/// Get multiple account data from either on-chain or compressed sources. +/// Returns one unified AccountInterface shape for every input pubkey. +pub async fn get_multiple_account_interfaces( + conn: &DatabaseConnection, + rpc_client: &RpcClient, + request: GetMultipleAccountInterfacesRequest, +) -> Result { + if request.addresses.len() > MAX_BATCH_SIZE { + return Err(PhotonApiError::ValidationError(format!( + "Batch size {} exceeds maximum of {}", + request.addresses.len(), + MAX_BATCH_SIZE + ))); + } + + if request.addresses.is_empty() { + return Err(PhotonApiError::ValidationError( + "At least one address must be provided".to_string(), + )); + } + + let context = Context::extract(conn).await?; + + let distinct_owners = get_distinct_owners_with_addresses(conn) + .await + .map_err(PhotonApiError::DatabaseError)?; + + let semaphore = Semaphore::new(MAX_CONCURRENT_LOOKUPS); + let futures: Vec<_> = request + .addresses + .iter() + .map(|address| async { + let _permit = semaphore.acquire().await.unwrap(); + race_hot_cold(rpc_client, conn, address, Some(&distinct_owners)).await + }) + .collect(); + + let results = futures::future::join_all(futures).await; + + let value = collect_batch_results(&request.addresses, results)?; + + Ok(GetMultipleAccountInterfacesResponse { context, value }) +} + +fn collect_batch_results( + addresses: &[SerializablePubkey], + results: Vec, PhotonApiError>>, +) -> Result>, PhotonApiError> { + let mut value = Vec::with_capacity(results.len()); + for (i, result) in results.into_iter().enumerate() { + match result { + // Includes Ok(None): account not found is returned as None. + Ok(account) => value.push(account), + // Only actual lookup failures abort the entire batch call. + Err(e) => { + log::error!( + "Failed to fetch interface for address {:?} (index {}): {:?}", + addresses.get(i), + i, + e + ); + return Err(e); + } + } + } + Ok(value) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn collect_batch_results_keeps_none_for_not_found_accounts() { + let addresses = vec![SerializablePubkey::default(), SerializablePubkey::default()]; + let results = vec![Ok(None), Ok(None)]; + + let value = collect_batch_results(&addresses, results).expect("expected success"); + assert_eq!(value, vec![None, None]); + } + + #[test] + fn collect_batch_results_returns_error_for_actual_failure() { + let addresses = vec![SerializablePubkey::default()]; + let results = vec![Err(PhotonApiError::UnexpectedError("boom".to_string()))]; + + let err = collect_batch_results(&addresses, results).expect_err("expected error"); + assert_eq!(err, PhotonApiError::UnexpectedError("boom".to_string())); + } +} diff --git a/src/api/method/interface/mod.rs b/src/api/method/interface/mod.rs new file mode 100644 index 00000000..0be4588f --- /dev/null +++ b/src/api/method/interface/mod.rs @@ -0,0 +1,8 @@ +pub mod get_account_interface; +pub mod get_multiple_account_interfaces; +pub mod racing; +pub mod types; + +pub use get_account_interface::get_account_interface; +pub use get_multiple_account_interfaces::get_multiple_account_interfaces; +pub use types::*; diff --git a/src/api/method/interface/racing.rs b/src/api/method/interface/racing.rs new file mode 100644 index 00000000..8ed98f2f --- /dev/null +++ b/src/api/method/interface/racing.rs @@ -0,0 +1,1424 @@ +use std::collections::HashMap; +use std::time::Duration; + +use crate::api::error::PhotonApiError; +use crate::common::typedefs::account::AccountV2; +use crate::common::typedefs::bs64_string::Base64String; +use crate::common::typedefs::hash::Hash; +use crate::common::typedefs::serializable_pubkey::SerializablePubkey; +use crate::common::typedefs::token_data::AccountState; +use crate::common::typedefs::unsigned_integer::UnsignedInteger; +use crate::dao::generated::{accounts, token_accounts}; +use light_compressed_account::address::derive_address; +use light_hasher::{sha256::Sha256BE, Hasher}; +use light_sdk_types::constants::ADDRESS_TREE_V2; +use sea_orm::prelude::Decimal; +use sea_orm::{ColumnTrait, Condition, DatabaseConnection, EntityTrait, QueryFilter}; +use solana_account::Account as SolanaAccount; +use solana_client::nonblocking::rpc_client::RpcClient; +use solana_commitment_config::CommitmentConfig; +use solana_program_option::COption; +use solana_program_pack::Pack; +use solana_pubkey::Pubkey; +use spl_token_interface::state::Account as SplTokenAccount; +use spl_token_interface::state::AccountState as SplAccountState; +use tokio::time::timeout; + +use crate::common::typedefs::token_data::TokenData; +use crate::ingester::persist::DECOMPRESSED_ACCOUNT_DISCRIMINATOR; + +use super::types::{AccountInterface, SolanaAccountData, DB_TIMEOUT_MS, RPC_TIMEOUT_MS}; + +/// Result from a hot (on-chain RPC) lookup. +#[derive(Debug)] +pub struct HotLookupResult { + pub account: Option, + pub slot: u64, +} + +/// Result from a cold (compressed DB) lookup. +#[derive(Debug)] +pub struct ColdLookupResult { + pub accounts: Vec, + /// Map from account hash → wallet owner bytes (from ata_owner in token_accounts table). + pub token_wallet_owners: HashMap, +} + +/// Perform a hot lookup via Solana RPC. +pub async fn hot_lookup( + rpc_client: &RpcClient, + address: &Pubkey, +) -> Result { + let result = timeout( + Duration::from_millis(RPC_TIMEOUT_MS), + rpc_client.get_account_with_commitment(address, CommitmentConfig::confirmed()), + ) + .await; + + match result { + Ok(Ok(response)) => Ok(HotLookupResult { + account: response.value, + slot: response.context.slot, + }), + Ok(Err(e)) => Err(PhotonApiError::UnexpectedError(format!("RPC error: {}", e))), + Err(_) => Err(PhotonApiError::UnexpectedError("RPC timeout".to_string())), + } +} + +/// Perform a cold lookup from the compressed accounts database. +/// +/// The lookup aggregates all compressed accounts associated with the queried pubkey: +/// 1) direct onchain pubkey matches (decompressed account linkage), +/// 2) derived compressed address matches (V2 address tree only), +/// 3) token account owner / ata_owner matches (can return multiple accounts). +pub async fn cold_lookup( + conn: &DatabaseConnection, + address: &SerializablePubkey, + distinct_owners: Option<&[Vec]>, +) -> Result { + let (models, token_wallet_owners) = find_cold_models(conn, address, distinct_owners).await?; + + let mut accounts_v2 = Vec::with_capacity(models.len()); + for model in models { + let account = AccountV2::try_from(model)?; + accounts_v2.push(account); + } + + Ok(ColdLookupResult { + accounts: accounts_v2, + token_wallet_owners, + }) +} + +fn has_decompressed_placeholder_shape(model: &accounts::Model) -> bool { + let Some(data) = model.data.as_ref() else { + return false; + }; + let Some(onchain_pubkey) = model.onchain_pubkey.as_ref() else { + return false; + }; + + data.len() == 32 && onchain_pubkey.len() == 32 && data.as_slice() == onchain_pubkey.as_slice() +} + +fn has_decompressed_placeholder_hash(model: &accounts::Model) -> bool { + let Some(onchain_pubkey) = model.onchain_pubkey.as_ref() else { + return false; + }; + let Some(data_hash) = model.data_hash.as_ref() else { + return false; + }; + + if onchain_pubkey.len() != 32 || data_hash.len() != 32 { + return false; + } + + match Sha256BE::hash(onchain_pubkey) { + Ok(expected_hash) => expected_hash.as_slice() == data_hash.as_slice(), + Err(_) => false, + } +} + +fn has_decompressed_placeholder_discriminator(model: &accounts::Model) -> bool { + let expected_disc = Decimal::from(DECOMPRESSED_ACCOUNT_DISCRIMINATOR); + let sqlite_rounded_disc = Decimal::from((DECOMPRESSED_ACCOUNT_DISCRIMINATOR as f64) as u64); + model.discriminator == Some(expected_disc) || model.discriminator == Some(sqlite_rounded_disc) +} + +fn is_decompressed_placeholder_model(model: &accounts::Model) -> bool { + if !has_decompressed_placeholder_shape(model) { + return false; + } + + if model.data_hash.is_some() && !has_decompressed_placeholder_hash(model) { + return false; + } + + has_decompressed_placeholder_discriminator(model) +} + +async fn find_cold_models( + conn: &DatabaseConnection, + address: &SerializablePubkey, + distinct_owners: Option<&[Vec]>, +) -> Result<(Vec, HashMap), PhotonApiError> { + let address_bytes: Vec = (*address).into(); + let pda_seed = address.0.to_bytes(); + + let mut by_hash: HashMap, accounts::Model> = HashMap::new(); + let mut token_wallet_owners: HashMap = HashMap::new(); + + // 1) Direct onchain pubkey linkage. + let onchain_result = timeout( + Duration::from_millis(DB_TIMEOUT_MS), + accounts::Entity::find() + .filter(accounts::Column::Spent.eq(false)) + .filter(accounts::Column::OnchainPubkey.eq(address_bytes.clone())) + .all(conn), + ) + .await; + + match onchain_result { + Ok(Ok(models)) => { + for model in models { + by_hash.insert(model.hash.clone(), model); + } + } + Ok(Err(e)) => return Err(PhotonApiError::DatabaseError(e)), + Err(_) => { + return Err(PhotonApiError::UnexpectedError( + "Database timeout".to_string(), + )) + } + } + + // 2) Derived-address fallback (V2 only). + // Use pre-fetched owners when available (batch path), otherwise query. + let owned_owners; + let owners: &[Vec] = match distinct_owners { + Some(cached) => cached, + None => { + let owners_result = timeout( + Duration::from_millis(DB_TIMEOUT_MS), + get_distinct_owners_with_addresses(conn), + ) + .await; + + owned_owners = match owners_result { + Ok(Ok(o)) => o, + Ok(Err(e)) => return Err(PhotonApiError::DatabaseError(e)), + Err(_) => { + return Err(PhotonApiError::UnexpectedError( + "Database timeout getting owners".to_string(), + )) + } + }; + &owned_owners + } + }; + + if !owners.is_empty() { + // Derived address lookups use V2 address tree only — compressible + // programs (the only users of this path) always create accounts + // with ADDRESS_TREE_V2. + let derived_addresses: Vec> = owners + .iter() + .filter_map(|owner| owner.as_slice().try_into().ok()) + .map(|owner_bytes: [u8; 32]| { + derive_address(&pda_seed, &ADDRESS_TREE_V2, &owner_bytes).to_vec() + }) + .collect(); + + if !derived_addresses.is_empty() { + let derived_result = timeout( + Duration::from_millis(DB_TIMEOUT_MS), + accounts::Entity::find() + .filter( + Condition::all() + .add(accounts::Column::Spent.eq(false)) + .add(accounts::Column::Address.is_in(derived_addresses)), + ) + .all(conn), + ) + .await; + + match derived_result { + Ok(Ok(models)) => { + for model in models { + by_hash.insert(model.hash.clone(), model); + } + } + Ok(Err(e)) => return Err(PhotonApiError::DatabaseError(e)), + Err(_) => { + return Err(PhotonApiError::UnexpectedError( + "Database timeout during derived address lookup".to_string(), + )) + } + } + } + } + + // 3) Token account linkage by token owner / ata_owner. + let token_result = timeout( + Duration::from_millis(DB_TIMEOUT_MS), + token_accounts::Entity::find() + .filter(token_accounts::Column::Spent.eq(false)) + .filter( + Condition::any() + .add(token_accounts::Column::AtaOwner.eq(address_bytes.clone())) + .add(token_accounts::Column::Owner.eq(address_bytes)), + ) + .find_also_related(accounts::Entity) + .all(conn), + ) + .await; + + match token_result { + Ok(Ok(rows)) => { + for (token, maybe_account) in rows { + if let Some(model) = maybe_account { + if !model.spent { + if let Some(ata_owner) = token.ata_owner { + match ( + Hash::try_from(model.hash.clone()), + <[u8; 32]>::try_from(ata_owner.as_slice()), + ) { + (Ok(hash), Ok(owner)) => { + token_wallet_owners.insert(hash, owner); + } + _ => log::warn!( + "Skipping invalid token wallet owner entry: hash_len={}, owner_len={}", + model.hash.len(), + ata_owner.len() + ), + } + } + by_hash.insert(model.hash.clone(), model); + } + } + } + } + Ok(Err(e)) => return Err(PhotonApiError::DatabaseError(e)), + Err(_) => { + return Err(PhotonApiError::UnexpectedError( + "Database timeout during token lookup".to_string(), + )) + } + } + + // Filter out decompressed PDA placeholders — they are bookmarks in the + // Merkle tree, not truly cold accounts. + // Sort by hash for deterministic ordering across identical queries. + let mut models: Vec<_> = by_hash + .into_values() + .filter(|m| !is_decompressed_placeholder_model(m)) + .collect(); + models.sort_by(|a, b| a.hash.cmp(&b.hash)); + Ok((models, token_wallet_owners)) +} + +/// Get distinct owners from accounts that have derived addresses. +/// These are accounts from compressible programs (their address is derived from PDA + tree + owner). +/// This is typically a small set since most programs aren't compressible. +pub async fn get_distinct_owners_with_addresses( + conn: &DatabaseConnection, +) -> Result>, sea_orm::DbErr> { + use sea_orm::{FromQueryResult, QuerySelect}; + + #[derive(FromQueryResult)] + struct OwnerResult { + owner: Vec, + } + + let owners: Vec = accounts::Entity::find() + .select_only() + .column(accounts::Column::Owner) + .distinct() + .filter(accounts::Column::Spent.eq(false)) + .filter(accounts::Column::Address.is_not_null()) + .into_model::() + .all(conn) + .await?; + + Ok(owners.into_iter().map(|o| o.owner).collect()) +} + +fn hot_to_solana_account_data(account: &SolanaAccount) -> SolanaAccountData { + SolanaAccountData { + lamports: UnsignedInteger(account.lamports), + data: Base64String(account.data.clone()), + owner: SerializablePubkey::from(account.owner.to_bytes()), + executable: account.executable, + rent_epoch: UnsignedInteger(account.rent_epoch), + space: UnsignedInteger(account.data.len() as u64), + } +} + +/// Build the 165-byte SPL Token Account layout from compressed TokenData + +/// corrected wallet owner. +fn build_spl_token_account_bytes(token_data: &TokenData, wallet_owner: &[u8; 32]) -> Vec { + let spl_state = match token_data.state { + AccountState::initialized => SplAccountState::Initialized, + AccountState::frozen => SplAccountState::Frozen, + }; + + let spl_account = SplTokenAccount { + mint: Pubkey::from(token_data.mint.0.to_bytes()), + owner: Pubkey::from(*wallet_owner), + amount: token_data.amount.0, + delegate: match &token_data.delegate { + Some(d) => COption::Some(Pubkey::from(d.0.to_bytes())), + None => COption::None, + }, + state: spl_state, + is_native: COption::None, + delegated_amount: 0, + close_authority: COption::None, + }; + + let mut buf = vec![0u8; SplTokenAccount::LEN]; + SplTokenAccount::pack(spl_account, &mut buf).expect("buffer is exactly LEN bytes"); + buf +} + +fn cold_to_synthetic_account_data( + account: &AccountV2, + wallet_owner: Option<&[u8]>, +) -> SolanaAccountData { + // For token accounts, always synthesize 165-byte SPL layout. + // Prefer ATA wallet owner when available, else fall back to compressed token owner. + if let Ok(Some(token_data)) = account.parse_token_data() { + let owner_arr = match wallet_owner.and_then(|bytes| <&[u8; 32]>::try_from(bytes).ok()) { + Some(owner) => *owner, + None => { + if let Some(owner) = wallet_owner { + log::debug!( + "Invalid ata_owner length for token account {}, using compressed owner: {}", + account.hash, + owner.len() + ); + } + token_data.owner.0.to_bytes() + } + }; + + let spl_bytes = build_spl_token_account_bytes(&token_data, &owner_arr); + let space = spl_bytes.len() as u64; + return SolanaAccountData { + lamports: account.lamports, + data: Base64String(spl_bytes), + // Preserve the original program owner from the cold account model. + // This can be LIGHT token program and, depending on account source, + // may also be SPL Token / Token-2022. + owner: account.owner, + executable: false, + rent_epoch: UnsignedInteger(0), + space: UnsignedInteger(space), + }; + } + + if wallet_owner + .map(|bytes| <&[u8; 32]>::try_from(bytes).is_err()) + .unwrap_or(false) + { + log::debug!( + "Ignoring invalid wallet owner length for non-token cold account: {:?}", + wallet_owner.map(|v| v.len()) + ); + } + + let full_data = account + .data + .as_ref() + .map(|d| d.data.0.clone()) + .unwrap_or_default(); + + let space = full_data.len() as u64; + SolanaAccountData { + lamports: account.lamports, + data: Base64String(full_data), + owner: account.owner, + executable: false, + rent_epoch: UnsignedInteger(0), + space: UnsignedInteger(space), + } +} + +fn parse_hot_spl_token(data: &[u8]) -> Option { + SplTokenAccount::unpack(data).ok() +} + +fn build_interface( + address: SerializablePubkey, + account_data: SolanaAccountData, + cold_accounts: Vec, +) -> AccountInterface { + AccountInterface { + key: address, + account: account_data, + cold: (!cold_accounts.is_empty()).then_some(cold_accounts), + } +} + +/// Race hot and cold lookups, returning a single unified interface shape. +/// +/// Account selection policy: +/// 1) If hot exists with lamports > 0 and hot slot >= newest cold slot, use hot account view. +/// 2) Otherwise, if cold exists, synthesize account view from newest cold account. +/// 3) Include all cold accounts in `cold` whenever they exist. +pub async fn race_hot_cold( + rpc_client: &RpcClient, + conn: &DatabaseConnection, + address: &SerializablePubkey, + distinct_owners: Option<&[Vec]>, +) -> Result, PhotonApiError> { + let pubkey = Pubkey::from(address.0.to_bytes()); + + let (hot_result, cold_result) = tokio::join!( + hot_lookup(rpc_client, &pubkey), + cold_lookup(conn, address, distinct_owners) + ); + + resolve_race_result(hot_result, cold_result, *address) +} + +fn resolve_race_result( + hot_result: Result, + cold_result: Result, + address: SerializablePubkey, +) -> Result, PhotonApiError> { + match (hot_result, cold_result) { + (Ok(hot), Ok(cold)) => Ok(resolve_single_race( + hot.account.as_ref(), + &cold.accounts, + hot.slot, + address, + &cold.token_wallet_owners, + )), + (Ok(hot), Err(e)) => { + log::debug!("Cold lookup failed, using hot result: {:?}", e); + Ok(hot + .account + .as_ref() + .filter(|account| account.lamports > 0) + .map(|account| { + build_interface(address, hot_to_solana_account_data(account), vec![]) + })) + } + (Err(e), Ok(cold)) => { + log::debug!("Hot lookup failed, using cold result: {:?}", e); + Ok(resolve_single_race( + None, + &cold.accounts, + 0, + address, + &cold.token_wallet_owners, + )) + } + (Err(hot_err), Err(cold_err)) => { + log::warn!( + "Both hot and cold lookups failed. Hot: {:?}, Cold: {:?}", + hot_err, + cold_err + ); + Err(hot_err) + } + } +} + +/// Build a synthetic `SolanaAccountData` from cold accounts, optionally +/// including a hot on-chain account's balance. +/// +/// For fungible tokens: if **all** cold accounts parse as token accounts with +/// the same mint (and, when provided, the hot account shares that mint) their +/// amounts are summed into a single SPL Token layout. When a hot account is +/// present it is used as the base for the synthetic view (it already carries the +/// correct wallet owner, delegate, state, etc.). Otherwise the newest cold +/// account (by slot) provides those fields. +/// +/// For everything else (non-token accounts, mixed mints, parse failures) the +/// function falls back to the hot account (if present and newer) or the newest +/// cold account. +fn cold_accounts_to_synthetic( + cold_accounts: &[AccountV2], + token_wallet_owners: &HashMap, + hot_account: Option<&SolanaAccount>, + hot_slot: u64, +) -> SolanaAccountData { + debug_assert!(!cold_accounts.is_empty()); + + // Try to parse every cold account as a token account. + let parsed: Vec<(&AccountV2, TokenData)> = cold_accounts + .iter() + .filter_map(|acc| acc.parse_token_data().ok().flatten().map(|td| (acc, td))) + .collect(); + + // All cold accounts must be token accounts with the same mint. + if !parsed.is_empty() && parsed.len() == cold_accounts.len() { + let first_mint = &parsed[0].1.mint; + let all_same_mint = parsed.iter().all(|(_, td)| td.mint == *first_mint); + + if all_same_mint { + // If a hot account is provided, it must also be an SPL token with + // the same mint; otherwise skip aggregation entirely. + let hot_contribution = match hot_account { + Some(hot) => match parse_hot_spl_token(&hot.data) { + Some(spl) if spl.mint.to_bytes() == first_mint.0.to_bytes() => Some(spl), + Some(_) => None, // different mint — can't aggregate + None => None, // not a token account + }, + None => None, + }; + + // When hot exists but doesn't match, fall through to fallback. + if hot_account.is_none() || hot_contribution.is_some() { + let cold_total: u64 = parsed.iter().map(|(_, td)| td.amount.0).sum(); + let hot_amount = hot_contribution.as_ref().map_or(0, |spl| spl.amount); + let total_amount = cold_total + hot_amount; + + // When a hot account matches, use it as the base for the + // synthetic view — it already has the correct wallet owner, + // delegate, state, etc. Unpack, set aggregated amount, repack. + if let (Some(hot), Some(mut spl)) = (hot_account, hot_contribution) { + spl.amount = total_amount; + let mut spl_bytes = vec![0u8; SplTokenAccount::LEN]; + SplTokenAccount::pack(spl, &mut spl_bytes) + .expect("buffer is exactly LEN bytes"); + let space = spl_bytes.len() as u64; + return SolanaAccountData { + lamports: UnsignedInteger(hot.lamports), + data: Base64String(spl_bytes), + owner: SerializablePubkey::from(hot.owner.to_bytes()), + executable: false, + rent_epoch: UnsignedInteger(0), + space: UnsignedInteger(space), + }; + } + + // Cold-only path: build from newest cold account. + let (newest_acc, newest_td) = parsed + .iter() + .max_by_key(|(acc, _)| acc.slot_created.0) + .unwrap(); + + let wallet_owner_bytes = token_wallet_owners.get(&newest_acc.hash); + let owner_arr = match wallet_owner_bytes + .and_then(|bytes| <&[u8; 32]>::try_from(bytes.as_slice()).ok()) + { + Some(owner) => *owner, + None => newest_td.owner.0.to_bytes(), + }; + + let aggregated = TokenData { + mint: newest_td.mint, + owner: newest_td.owner, + amount: UnsignedInteger(total_amount), + delegate: newest_td.delegate, + state: newest_td.state, + tlv: newest_td.tlv.clone(), + }; + + let spl_bytes = build_spl_token_account_bytes(&aggregated, &owner_arr); + let space = spl_bytes.len() as u64; + return SolanaAccountData { + lamports: newest_acc.lamports, + data: Base64String(spl_bytes), + owner: newest_acc.owner, + executable: false, + rent_epoch: UnsignedInteger(0), + space: UnsignedInteger(space), + }; + } + } + } + + // Fallback: compare hot slot vs newest cold slot to pick the fresher view. + let newest = cold_accounts + .iter() + .max_by_key(|acc| acc.slot_created.0) + .unwrap(); + + if let Some(hot) = hot_account { + if hot_slot >= newest.slot_created.0 { + return hot_to_solana_account_data(hot); + } + } + + let wallet_owner = token_wallet_owners.get(&newest.hash); + cold_to_synthetic_account_data(newest, wallet_owner.map(|v| v.as_slice())) +} + +fn resolve_single_race( + hot_account: Option<&SolanaAccount>, + cold_accounts: &[AccountV2], + hot_slot: u64, + address: SerializablePubkey, + token_wallet_owners: &HashMap, +) -> Option { + let hot = hot_account.filter(|account| account.lamports > 0); + let has_cold = !cold_accounts.is_empty(); + + let account_data = match (hot, has_cold) { + (Some(hot), true) => { + cold_accounts_to_synthetic(cold_accounts, token_wallet_owners, Some(hot), hot_slot) + } + (Some(hot), false) => hot_to_solana_account_data(hot), + (None, true) => cold_accounts_to_synthetic(cold_accounts, token_wallet_owners, None, 0), + (None, false) => return None, + }; + + Some(build_interface( + address, + account_data, + cold_accounts.to_vec(), + )) +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::api::method::get_validity_proof::MerkleContextV2; + use crate::common::typedefs::account::AccountData; + use crate::common::typedefs::account::C_TOKEN_DISCRIMINATOR_V2; + use crate::common::typedefs::hash::Hash; + use crate::common::typedefs::token_data::AccountState; + use crate::ingester::persist::LIGHT_TOKEN_PROGRAM_ID; + use solana_program_pack::Pack; + use spl_token_interface::state::Account as SplTokenAccount; + + fn sample_cold(slot_created: u64, lamports: u64) -> AccountV2 { + AccountV2 { + hash: Hash::default(), + address: Some(SerializablePubkey::default()), + data: Some(AccountData { + discriminator: UnsignedInteger(0x0807060504030201), + data: Base64String(vec![100, 200]), + data_hash: Hash::default(), + }), + owner: SerializablePubkey::default(), + lamports: UnsignedInteger(lamports), + leaf_index: UnsignedInteger(0), + seq: Some(UnsignedInteger(1)), + slot_created: UnsignedInteger(slot_created), + prove_by_index: false, + merkle_context: MerkleContextV2 { + tree_type: 3, + tree: SerializablePubkey::default(), + queue: SerializablePubkey::default(), + cpi_context: None, + next_tree_context: None, + }, + } + } + + /// Build a cold AccountV2 that looks like a compressed token account. + /// Uses LIGHT_TOKEN_PROGRAM_ID as owner and a c_token discriminator. + fn sample_token_cold(slot_created: u64, lamports: u64, token_data: &TokenData) -> AccountV2 { + sample_token_cold_with_hash(slot_created, lamports, token_data, Hash::default()) + } + + fn sample_token_cold_with_hash( + slot_created: u64, + lamports: u64, + token_data: &TokenData, + hash: Hash, + ) -> AccountV2 { + use borsh::BorshSerialize; + let mut data_bytes = Vec::new(); + token_data.serialize(&mut data_bytes).unwrap(); + + let discriminator = u64::from_le_bytes(C_TOKEN_DISCRIMINATOR_V2); + + AccountV2 { + hash, + address: Some(SerializablePubkey::default()), + data: Some(AccountData { + discriminator: UnsignedInteger(discriminator), + data: Base64String(data_bytes), + data_hash: Hash::default(), + }), + owner: SerializablePubkey::from(LIGHT_TOKEN_PROGRAM_ID), + lamports: UnsignedInteger(lamports), + leaf_index: UnsignedInteger(0), + seq: Some(UnsignedInteger(1)), + slot_created: UnsignedInteger(slot_created), + prove_by_index: false, + merkle_context: MerkleContextV2 { + tree_type: 3, + tree: SerializablePubkey::default(), + queue: SerializablePubkey::default(), + cpi_context: None, + next_tree_context: None, + }, + } + } + + #[test] + fn test_resolve_single_race_prefers_hot_when_newer_or_equal() { + let hot = SolanaAccount { + lamports: 1000, + data: vec![1, 2, 3], + owner: Pubkey::new_unique(), + executable: false, + rent_epoch: 0, + }; + let cold = sample_cold(100, 500); + + let result = resolve_single_race( + Some(&hot), + std::slice::from_ref(&cold), + 200, + SerializablePubkey::default(), + &HashMap::new(), + ) + .expect("expected Some interface"); + + assert_eq!(result.account.lamports.0, 1000); + assert!(result.cold.is_some()); + assert_eq!(result.cold.unwrap().len(), 1); + } + + #[test] + fn test_resolve_single_race_prefers_newer_cold_by_slot() { + let hot = SolanaAccount { + lamports: 1000, + data: vec![1, 2, 3], + owner: Pubkey::new_unique(), + executable: false, + rent_epoch: 0, + }; + let cold = sample_cold(300, 777); + + let result = resolve_single_race( + Some(&hot), + std::slice::from_ref(&cold), + 200, + SerializablePubkey::default(), + &HashMap::new(), + ) + .expect("expected Some interface"); + + assert_eq!(result.account.lamports.0, 777); + assert!(result.cold.is_some()); + } + + #[test] + fn test_resolve_single_race_falls_back_to_cold_when_hot_deleted() { + let hot = SolanaAccount { + lamports: 0, + data: vec![], + owner: Pubkey::new_unique(), + executable: false, + rent_epoch: 0, + }; + let cold = sample_cold(100, 500); + + let result = resolve_single_race( + Some(&hot), + std::slice::from_ref(&cold), + 200, + SerializablePubkey::default(), + &HashMap::new(), + ) + .expect("expected Some interface"); + + assert_eq!(result.account.lamports.0, 500); + assert!(result.cold.is_some()); + } + + #[test] + fn test_resolve_single_race_only_hot() { + let hot = SolanaAccount { + lamports: 1000, + data: vec![1, 2, 3], + owner: Pubkey::new_unique(), + executable: false, + rent_epoch: 0, + }; + + let result = resolve_single_race( + Some(&hot), + &[], + 200, + SerializablePubkey::default(), + &HashMap::new(), + ) + .expect("expected Some interface"); + + assert_eq!(result.account.lamports.0, 1000); + assert!(result.cold.is_none()); + } + + #[test] + fn test_resolve_single_race_only_cold() { + let cold = sample_cold(100, 555); + + let result = resolve_single_race( + None, + std::slice::from_ref(&cold), + 0, + SerializablePubkey::default(), + &HashMap::new(), + ) + .expect("expected Some interface"); + + assert_eq!(result.account.lamports.0, 555); + assert!(result.cold.is_some()); + } + + #[test] + fn test_resolve_single_race_neither() { + let result = + resolve_single_race(None, &[], 0, SerializablePubkey::default(), &HashMap::new()); + assert!(result.is_none()); + } + + // ============ SPL Token Account reconstruction tests ============ + + #[test] + fn test_build_spl_token_account_bytes_basic() { + let mint = Pubkey::new_unique(); + let wallet_owner = [42u8; 32]; + let token_data = TokenData { + mint: SerializablePubkey::from(mint), + owner: SerializablePubkey::from([99u8; 32]), // compressed owner (not wallet) + amount: UnsignedInteger(1_000_000), + delegate: None, + state: AccountState::initialized, + tlv: None, + }; + + let bytes = build_spl_token_account_bytes(&token_data, &wallet_owner); + + assert_eq!(bytes.len(), SplTokenAccount::LEN); + let parsed = SplTokenAccount::unpack(&bytes).expect("valid SPL layout"); + assert_eq!(parsed.mint, mint); + assert_eq!(parsed.owner, Pubkey::from(wallet_owner)); + assert_eq!(parsed.amount, 1_000_000); + assert!(parsed.delegate.is_none()); + assert_eq!( + parsed.state, + spl_token_interface::state::AccountState::Initialized + ); + assert!(parsed.is_native.is_none()); + assert_eq!(parsed.delegated_amount, 0); + assert!(parsed.close_authority.is_none()); + } + + #[test] + fn test_build_spl_token_account_bytes_with_delegate() { + let mint = Pubkey::new_unique(); + let wallet_owner = [10u8; 32]; + let delegate_key = Pubkey::new_unique(); + let token_data = TokenData { + mint: SerializablePubkey::from(mint), + owner: SerializablePubkey::from([99u8; 32]), + amount: UnsignedInteger(500), + delegate: Some(SerializablePubkey::from(delegate_key)), + state: AccountState::frozen, + tlv: None, + }; + + let bytes = build_spl_token_account_bytes(&token_data, &wallet_owner); + + assert_eq!(bytes.len(), SplTokenAccount::LEN); + let parsed = SplTokenAccount::unpack(&bytes).expect("valid SPL layout"); + assert_eq!(parsed.amount, 500); + assert_eq!( + parsed.delegate, + solana_program_option::COption::Some(delegate_key) + ); + assert_eq!( + parsed.state, + spl_token_interface::state::AccountState::Frozen + ); + } + + #[test] + fn test_cold_to_synthetic_token_with_wallet_owner() { + let mint = Pubkey::new_unique(); + let wallet_owner = [55u8; 32]; + let token_data = TokenData { + mint: SerializablePubkey::from(mint), + owner: SerializablePubkey::from([99u8; 32]), + amount: UnsignedInteger(42), + delegate: None, + state: AccountState::initialized, + tlv: None, + }; + + let cold = sample_token_cold(100, 1_000_000, &token_data); + let result = cold_to_synthetic_account_data(&cold, Some(&wallet_owner)); + + // Should produce 165-byte SPL layout + let parsed = SplTokenAccount::unpack(&result.data.0).expect("valid SPL layout"); + assert_eq!(result.space.0, SplTokenAccount::LEN as u64); + // Program owner should be preserved from cold account. + assert_eq!( + result.owner, + SerializablePubkey::from(LIGHT_TOKEN_PROGRAM_ID) + ); + assert_eq!(parsed.owner, Pubkey::from(wallet_owner)); + } + + #[test] + fn test_cold_to_synthetic_token_without_wallet_owner() { + let mint = Pubkey::new_unique(); + let compressed_owner = [99u8; 32]; + let token_data = TokenData { + mint: SerializablePubkey::from(mint), + owner: SerializablePubkey::from(compressed_owner), + amount: UnsignedInteger(42), + delegate: None, + state: AccountState::initialized, + tlv: None, + }; + + let cold = sample_token_cold(100, 1_000_000, &token_data); + // No wallet owner → should fall back to compressed token owner. + let result = cold_to_synthetic_account_data(&cold, None); + + let parsed = SplTokenAccount::unpack(&result.data.0).expect("valid SPL layout"); + assert_eq!(result.space.0, SplTokenAccount::LEN as u64); + assert_eq!( + result.owner, + SerializablePubkey::from(LIGHT_TOKEN_PROGRAM_ID) + ); + assert_eq!(parsed.owner, Pubkey::from(compressed_owner)); + } + + #[test] + fn test_cold_to_synthetic_token_invalid_wallet_owner_uses_compressed_owner() { + let mint = Pubkey::new_unique(); + let compressed_owner = [77u8; 32]; + let token_data = TokenData { + mint: SerializablePubkey::from(mint), + owner: SerializablePubkey::from(compressed_owner), + amount: UnsignedInteger(42), + delegate: None, + state: AccountState::initialized, + tlv: None, + }; + + let cold = sample_token_cold(100, 1_000_000, &token_data); + let invalid_owner = [1u8; 31]; + let result = cold_to_synthetic_account_data(&cold, Some(&invalid_owner)); + + let parsed = SplTokenAccount::unpack(&result.data.0).expect("valid SPL layout"); + assert_eq!(parsed.owner, Pubkey::from(compressed_owner)); + } + + #[test] + fn test_cold_to_synthetic_non_token() { + let cold = sample_cold(100, 500); + let wallet_owner = [55u8; 32]; + + // Non-token account with wallet_owner should be unaffected + let result = cold_to_synthetic_account_data(&cold, Some(&wallet_owner)); + + // Should use fallback (discriminator + data), NOT 165-byte SPL layout + assert_ne!(result.data.0.len(), SplTokenAccount::LEN); + // Owner should remain as the account's owner (default pubkey) + assert_eq!(result.owner, SerializablePubkey::default()); + } + + // ============ Decompressed placeholder filtering tests ============ + + use crate::dao::generated::accounts; + use sea_orm::prelude::Decimal; + + fn make_account_model( + hash_byte: u8, + discriminator: Option, + data: Option>, + data_hash: Option>, + onchain_pubkey: Option>, + ) -> accounts::Model { + accounts::Model { + hash: vec![hash_byte; 32], + address: None, + discriminator, + data, + data_hash, + tree: vec![], + leaf_index: 0, + seq: Some(1), + slot_created: 100, + owner: vec![0u8; 32], + lamports: Decimal::from(0), + spent: false, + prev_spent: Some(false), + tx_hash: None, + onchain_pubkey, + tree_type: Some(3), + nullified_in_tree: false, + nullifier_queue_index: None, + in_output_queue: false, + queue: None, + nullifier: None, + } + } + + #[test] + fn test_find_cold_models_filters_decompressed_placeholders() { + let pda = vec![1u8; 32]; + let data_hash = Sha256BE::hash(&pda).unwrap().to_vec(); + let placeholder = make_account_model( + 1, + Some(Decimal::from(DECOMPRESSED_ACCOUNT_DISCRIMINATOR)), + Some(pda.clone()), + Some(data_hash), + Some(pda), + ); + let normal = make_account_model( + 2, + Some(Decimal::from(0x0807060504030201u64)), + None, + None, + None, + ); + let no_disc = make_account_model(3, None, None, None, None); + + // Simulate the filtering logic from find_cold_models. + let by_hash: HashMap, accounts::Model> = vec![ + (placeholder.hash.clone(), placeholder), + (normal.hash.clone(), normal), + (no_disc.hash.clone(), no_disc), + ] + .into_iter() + .collect(); + + let filtered: Vec<_> = by_hash + .into_values() + .filter(|m| !is_decompressed_placeholder_model(m)) + .collect(); + + // Placeholder should be filtered out, normal and no-disc should remain. + assert_eq!(filtered.len(), 2); + let hashes: Vec<&Vec> = filtered.iter().map(|m| &m.hash).collect(); + assert!(hashes.contains(&&vec![2u8; 32])); + assert!(hashes.contains(&&vec![3u8; 32])); + } + + #[test] + fn test_find_cold_models_filters_sqlite_rounded_decompressed_placeholder() { + // SQLite REAL rounding shifts this discriminator by +1 for large u64 values. + let rounded_disc = Decimal::from(DECOMPRESSED_ACCOUNT_DISCRIMINATOR + 1); + let pda = vec![9u8; 32]; + let data_hash = Sha256BE::hash(&pda).unwrap().to_vec(); + let placeholder = make_account_model( + 9, + Some(rounded_disc), + Some(pda.clone()), + Some(data_hash), + Some(pda.clone()), + ); + + assert!(is_decompressed_placeholder_model(&placeholder)); + } + + #[test] + fn test_find_cold_models_filters_legacy_sqlite_rounded_placeholder_without_disc_bytes() { + let rounded_disc = Decimal::from(DECOMPRESSED_ACCOUNT_DISCRIMINATOR + 1); + let pda = vec![8u8; 32]; + let placeholder = + make_account_model(8, Some(rounded_disc), Some(pda.clone()), None, Some(pda)); + + assert!(is_decompressed_placeholder_model(&placeholder)); + } + + #[test] + fn test_find_cold_models_keeps_non_placeholder_with_onchain_pubkey() { + // Model shape similar to compressed mint linkage: has onchain_pubkey but + // does not store it in the first 32 bytes of data. + let rounded_disc = Decimal::from(DECOMPRESSED_ACCOUNT_DISCRIMINATOR + 1); + let mut mint_data = vec![0u8; 120]; + let onchain = vec![7u8; 32]; + mint_data[84..116].copy_from_slice(&onchain); + + let model = make_account_model(7, Some(rounded_disc), Some(mint_data), None, Some(onchain)); + assert!(!is_decompressed_placeholder_model(&model)); + } + + // ============ Fungible token amount aggregation tests ============ + + #[test] + fn test_resolve_aggregates_fungible_token_amounts() { + let mint = Pubkey::new_unique(); + let wallet_owner = [55u8; 32]; + let hash1 = Hash::try_from(vec![1u8; 32]).unwrap(); + let hash2 = Hash::try_from(vec![2u8; 32]).unwrap(); + let hash3 = Hash::try_from(vec![3u8; 32]).unwrap(); + + let td1 = TokenData { + mint: SerializablePubkey::from(mint), + owner: SerializablePubkey::from([99u8; 32]), + amount: UnsignedInteger(100), + delegate: None, + state: AccountState::initialized, + tlv: None, + }; + let td2 = TokenData { + mint: SerializablePubkey::from(mint), + owner: SerializablePubkey::from([99u8; 32]), + amount: UnsignedInteger(250), + delegate: None, + state: AccountState::initialized, + tlv: None, + }; + let td3 = TokenData { + mint: SerializablePubkey::from(mint), + owner: SerializablePubkey::from([99u8; 32]), + amount: UnsignedInteger(650), + delegate: None, + state: AccountState::initialized, + tlv: None, + }; + + let cold1 = sample_token_cold_with_hash(100, 0, &td1, hash1); + let cold2 = sample_token_cold_with_hash(200, 0, &td2, hash2); + let cold3 = sample_token_cold_with_hash(300, 0, &td3, hash3.clone()); + + let cold_accounts = vec![cold1, cold2, cold3]; + + // Map newest hash → wallet owner + let mut token_wallet_owners = HashMap::new(); + token_wallet_owners.insert(hash3, wallet_owner); + + let result = resolve_single_race( + None, + &cold_accounts, + 0, + SerializablePubkey::default(), + &token_wallet_owners, + ) + .expect("expected Some interface"); + + // Primary view should have aggregated amount = 100 + 250 + 650 = 1000 + let parsed = SplTokenAccount::unpack(&result.account.data.0).expect("valid SPL layout"); + assert_eq!(parsed.amount, 1000); + assert_eq!(parsed.owner, Pubkey::from(wallet_owner)); + + // All cold accounts should be included + assert_eq!(result.cold.as_ref().unwrap().len(), 3); + } + + #[test] + fn test_resolve_mixed_mints_uses_newest() { + let mint_a = Pubkey::new_unique(); + let mint_b = Pubkey::new_unique(); + + let hash1 = Hash::try_from(vec![1u8; 32]).unwrap(); + let hash2 = Hash::try_from(vec![2u8; 32]).unwrap(); + + let td1 = TokenData { + mint: SerializablePubkey::from(mint_a), + owner: SerializablePubkey::from([99u8; 32]), + amount: UnsignedInteger(100), + delegate: None, + state: AccountState::initialized, + tlv: None, + }; + let td2 = TokenData { + mint: SerializablePubkey::from(mint_b), + owner: SerializablePubkey::from([99u8; 32]), + amount: UnsignedInteger(200), + delegate: None, + state: AccountState::initialized, + tlv: None, + }; + + let cold1 = sample_token_cold_with_hash(100, 0, &td1, hash1); + let cold2 = sample_token_cold_with_hash(200, 0, &td2, hash2.clone()); + + let cold_accounts = vec![cold1, cold2]; + + let mut token_wallet_owners = HashMap::new(); + token_wallet_owners.insert(hash2, [55u8; 32]); + + let result = resolve_single_race( + None, + &cold_accounts, + 0, + SerializablePubkey::default(), + &token_wallet_owners, + ) + .expect("expected Some interface"); + + // Mixed mints → fallback to newest by slot (cold2, amount=200) + let parsed = SplTokenAccount::unpack(&result.account.data.0).expect("valid SPL layout"); + assert_eq!(parsed.amount, 200); + } + + #[test] + fn test_resolve_single_token_unchanged() { + let mint = Pubkey::new_unique(); + let wallet_owner = [55u8; 32]; + let hash1 = Hash::try_from(vec![1u8; 32]).unwrap(); + + let td = TokenData { + mint: SerializablePubkey::from(mint), + owner: SerializablePubkey::from([99u8; 32]), + amount: UnsignedInteger(42), + delegate: None, + state: AccountState::initialized, + tlv: None, + }; + + let cold = sample_token_cold_with_hash(100, 1_000_000, &td, hash1.clone()); + let cold_accounts = vec![cold]; + + let mut token_wallet_owners = HashMap::new(); + token_wallet_owners.insert(hash1, wallet_owner); + + let result = resolve_single_race( + None, + &cold_accounts, + 0, + SerializablePubkey::default(), + &token_wallet_owners, + ) + .expect("expected Some interface"); + + // Single token account → amount should be 42, unchanged + let parsed = SplTokenAccount::unpack(&result.account.data.0).expect("valid SPL layout"); + assert_eq!(parsed.amount, 42); + assert_eq!(parsed.owner, Pubkey::from(wallet_owner)); + } + + // ============ Hot + cold aggregation tests ============ + + /// Helper to build a hot SolanaAccount that looks like an SPL token ATA. + fn sample_hot_token_account(mint: &Pubkey, owner: &Pubkey, amount: u64) -> SolanaAccount { + use solana_program_option::COption; + use spl_token_interface::state::AccountState as SplAccountState; + + let spl_account = SplTokenAccount { + mint: *mint, + owner: *owner, + amount, + delegate: COption::None, + state: SplAccountState::Initialized, + is_native: COption::None, + delegated_amount: 0, + close_authority: COption::None, + }; + + let mut data = vec![0u8; SplTokenAccount::LEN]; + SplTokenAccount::pack(spl_account, &mut data).unwrap(); + + // Use the real SPL Token program id. + let spl_token_program = Pubkey::try_from(spl_token_interface::ID.as_ref()).unwrap(); + + SolanaAccount { + lamports: 2_039_280, // typical rent-exempt ATA + data, + owner: spl_token_program, + executable: false, + rent_epoch: 0, + } + } + + #[test] + fn test_resolve_aggregates_hot_and_cold_token_amounts() { + let mint = Pubkey::new_unique(); + let wallet_owner = Pubkey::new_unique(); + let hash1 = Hash::try_from(vec![1u8; 32]).unwrap(); + let hash2 = Hash::try_from(vec![2u8; 32]).unwrap(); + + // Hot ATA with 500 tokens. + let hot = sample_hot_token_account(&mint, &wallet_owner, 500); + + // Two cold compressed token accounts, 100 + 400 tokens. + let td1 = TokenData { + mint: SerializablePubkey::from(mint), + owner: SerializablePubkey::from([99u8; 32]), + amount: UnsignedInteger(100), + delegate: None, + state: AccountState::initialized, + tlv: None, + }; + let td2 = TokenData { + mint: SerializablePubkey::from(mint), + owner: SerializablePubkey::from([99u8; 32]), + amount: UnsignedInteger(400), + delegate: None, + state: AccountState::initialized, + tlv: None, + }; + + let cold1 = sample_token_cold_with_hash(100, 0, &td1, hash1); + let cold2 = sample_token_cold_with_hash(200, 0, &td2, hash2); + let cold_accounts = vec![cold1, cold2]; + + let result = resolve_single_race( + Some(&hot), + &cold_accounts, + 300, // hot_slot + SerializablePubkey::default(), + &HashMap::new(), + ) + .expect("expected Some interface"); + + // Primary view should have aggregated amount = 500 (hot) + 100 + 400 (cold) = 1000 + let parsed = SplTokenAccount::unpack(&result.account.data.0).expect("valid SPL layout"); + assert_eq!(parsed.amount, 1000); + // Wallet owner should come from the hot ATA. + assert_eq!(parsed.owner, wallet_owner); + assert_eq!(parsed.mint, mint); + // Cold accounts still included. + assert_eq!(result.cold.as_ref().unwrap().len(), 2); + } + + #[test] + fn test_resolve_hot_token_different_mint_from_cold_uses_hot() { + let mint_hot = Pubkey::new_unique(); + let mint_cold = Pubkey::new_unique(); + let wallet_owner = Pubkey::new_unique(); + let hash1 = Hash::try_from(vec![1u8; 32]).unwrap(); + + let hot = sample_hot_token_account(&mint_hot, &wallet_owner, 500); + + let td = TokenData { + mint: SerializablePubkey::from(mint_cold), + owner: SerializablePubkey::from([99u8; 32]), + amount: UnsignedInteger(300), + delegate: None, + state: AccountState::initialized, + tlv: None, + }; + let cold = sample_token_cold_with_hash(100, 0, &td, hash1); + + let result = resolve_single_race( + Some(&hot), + std::slice::from_ref(&cold), + 300, + SerializablePubkey::default(), + &HashMap::new(), + ) + .expect("expected Some interface"); + + // Different mints → fallback to hot account (since hot is present). + let parsed = SplTokenAccount::unpack(&result.account.data.0).expect("valid SPL layout"); + assert_eq!(parsed.amount, 500); + assert_eq!(parsed.mint, mint_hot); + } + + #[test] + fn test_resolve_hot_non_token_with_cold_tokens_uses_hot() { + let mint = Pubkey::new_unique(); + let hash1 = Hash::try_from(vec![1u8; 32]).unwrap(); + + // Hot is not a token account (random data, too short for SPL). + let hot = SolanaAccount { + lamports: 1000, + data: vec![1, 2, 3], + owner: Pubkey::new_unique(), + executable: false, + rent_epoch: 0, + }; + + let td = TokenData { + mint: SerializablePubkey::from(mint), + owner: SerializablePubkey::from([99u8; 32]), + amount: UnsignedInteger(300), + delegate: None, + state: AccountState::initialized, + tlv: None, + }; + let cold = sample_token_cold_with_hash(100, 0, &td, hash1); + + let result = resolve_single_race( + Some(&hot), + std::slice::from_ref(&cold), + 300, + SerializablePubkey::default(), + &HashMap::new(), + ) + .expect("expected Some interface"); + + // Hot is not a token → can't aggregate → fallback to hot. + assert_eq!(result.account.lamports.0, 1000); + assert_eq!(result.account.data.0, vec![1, 2, 3]); + } +} diff --git a/src/api/method/interface/types.rs b/src/api/method/interface/types.rs new file mode 100644 index 00000000..42ba4a8d --- /dev/null +++ b/src/api/method/interface/types.rs @@ -0,0 +1,87 @@ +use serde::{Deserialize, Serialize}; +use solana_pubkey::{pubkey, Pubkey}; +use utoipa::ToSchema; + +use crate::common::typedefs::account::AccountV2; +use crate::common::typedefs::bs64_string::Base64String; +use crate::common::typedefs::context::Context; +use crate::common::typedefs::serializable_pubkey::SerializablePubkey; +use crate::common::typedefs::unsigned_integer::UnsignedInteger; + +/// Nested Solana account fields (matches getAccountInfo shape) +#[derive(Debug, Clone, PartialEq, Eq, Serialize, ToSchema)] +#[serde(rename_all = "camelCase")] +pub struct SolanaAccountData { + pub lamports: UnsignedInteger, + pub data: Base64String, + pub owner: SerializablePubkey, + pub executable: bool, + pub rent_epoch: UnsignedInteger, + pub space: UnsignedInteger, +} + +/// Unified account interface — works for both on-chain and compressed accounts +#[derive(Debug, Clone, PartialEq, Eq, Serialize, ToSchema)] +#[serde(rename_all = "camelCase")] +pub struct AccountInterface { + /// The queried Solana pubkey + pub key: SerializablePubkey, + /// Standard Solana account fields (hot view or synthetic cold view) + pub account: SolanaAccountData, + /// Compressed accounts associated with this pubkey + pub cold: Option>, +} + +// ============ Request Types ============ + +/// Request for getAccountInterface +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, ToSchema, Default)] +#[serde(deny_unknown_fields, rename_all = "camelCase")] +pub struct GetAccountInterfaceRequest { + /// The account address to look up + pub address: SerializablePubkey, +} + +/// Request for getMultipleAccountInterfaces +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, ToSchema, Default)] +#[serde(deny_unknown_fields, rename_all = "camelCase")] +pub struct GetMultipleAccountInterfacesRequest { + /// List of account addresses to look up (max 100) + pub addresses: Vec, +} + +// ============ Response Types ============ + +/// Response for getAccountInterface +#[derive(Debug, Clone, PartialEq, Eq, Serialize, ToSchema)] +#[serde(rename_all = "camelCase")] +pub struct GetAccountInterfaceResponse { + /// Current context (slot) + pub context: Context, + /// The account data, or None if not found + pub value: Option, +} + +/// Response for getMultipleAccountInterfaces +#[derive(Debug, Clone, PartialEq, Eq, Serialize, ToSchema)] +#[serde(rename_all = "camelCase")] +pub struct GetMultipleAccountInterfacesResponse { + /// Current context (slot) + pub context: Context, + /// List of account results (Some for found accounts, None for not found) + pub value: Vec>, +} + +// ============ Constants ============ + +/// Maximum number of accounts that can be looked up in a single batch request +pub const MAX_BATCH_SIZE: usize = 100; + +/// RPC timeout in milliseconds for hot lookups +pub const RPC_TIMEOUT_MS: u64 = 5000; + +/// Database timeout in milliseconds for cold lookups +pub const DB_TIMEOUT_MS: u64 = 3000; + +/// SPL Token program ID (on-chain Token program) +pub const SPL_TOKEN_PROGRAM_ID: Pubkey = pubkey!("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"); diff --git a/src/api/method/mod.rs b/src/api/method/mod.rs index 50073948..93a4496a 100644 --- a/src/api/method/mod.rs +++ b/src/api/method/mod.rs @@ -24,5 +24,6 @@ pub mod get_queue_elements; pub mod get_queue_info; pub mod get_transaction_with_compression_info; pub mod get_validity_proof; +pub mod interface; pub mod utils; diff --git a/src/api/rpc_server.rs b/src/api/rpc_server.rs index 5cc11dd8..b9649121 100644 --- a/src/api/rpc_server.rs +++ b/src/api/rpc_server.rs @@ -425,5 +425,26 @@ fn build_rpc_module(api_and_indexer: PhotonApi) -> Result, }, )?; + // Interface endpoints - race hot (on-chain) and cold (compressed) lookups + module.register_async_method( + "getAccountInterface", + |rpc_params, rpc_context| async move { + let api = rpc_context.as_ref(); + let payload = rpc_params.parse()?; + api.get_account_interface(payload).await.map_err(Into::into) + }, + )?; + + module.register_async_method( + "getMultipleAccountInterfaces", + |rpc_params, rpc_context| async move { + let api = rpc_context.as_ref(); + let payload = rpc_params.parse()?; + api.get_multiple_account_interfaces(payload) + .await + .map_err(Into::into) + }, + )?; + Ok(module) } diff --git a/src/openapi/mod.rs b/src/openapi/mod.rs index 5bfadf77..214d133e 100644 --- a/src/openapi/mod.rs +++ b/src/openapi/mod.rs @@ -33,6 +33,7 @@ use crate::api::method::get_validity_proof::{ AccountProofInputs, AddressProofInputs, CompressedProof, CompressedProofWithContext, CompressedProofWithContextV2, MerkleContextV2, RootIndex, TreeContextInfo, }; +use crate::api::method::interface::types::{AccountInterface, SolanaAccountData}; use crate::api::method::utils::PaginatedSignatureInfoList; use crate::api::method::utils::SignatureInfo; use crate::api::method::utils::SignatureInfoList; @@ -148,6 +149,9 @@ const JSON_CONTENT_TYPE: &str = "application/json"; TreeContextInfo, GetCompressedAccountProofResponseValue, GetCompressedAccountProofResponseValueV2, + // Interface types + AccountInterface, + SolanaAccountData, )))] struct ApiDoc; diff --git a/tests/data/transactions/indexer_interface/2Q8KnAuf9TuPThbkEFZp6tfFC9bsGBVEpvoDJzLZAAEDKi2eSZuEdjKrqw9ez2yhxJt5U7S8LYdrUdSq1KKZid4X b/tests/data/transactions/indexer_interface/2Q8KnAuf9TuPThbkEFZp6tfFC9bsGBVEpvoDJzLZAAEDKi2eSZuEdjKrqw9ez2yhxJt5U7S8LYdrUdSq1KKZid4X new file mode 100644 index 00000000..6514a3a3 --- /dev/null +++ b/tests/data/transactions/indexer_interface/2Q8KnAuf9TuPThbkEFZp6tfFC9bsGBVEpvoDJzLZAAEDKi2eSZuEdjKrqw9ez2yhxJt5U7S8LYdrUdSq1KKZid4X @@ -0,0 +1,158 @@ +{ + "slot": 200, + "transaction": [ + "AkX10M+vjtXvXVcyhHxy20yOQNg+i6YhaLQWTY9OjscFDTMeotSI3DKM8Scq2juC8isiS40iKcSP+MhwBK7TNARXGFC8jgns1c5ti9DfHEHnEEUFtf6CPX3hhcUuLbmNQuFPO/XOom3OP1WtMwxmUk4Hynw8nTXiWQQ8OkkCPJsJAgEIDjwnoQx7ujHJcfKDkl7hQKUz/Hvi6qrsfBpPmC20LrGTUl3YAEgdiOAbZtUAebWqVueellDUwIToGo3jD8DY3VUIpumYsF/pK5ig2fd52dvosvPu9ngTKHvrpSZJgJFovQv/FayGQswvkPcc7/1XU+ilA3gepE4uMS/o0kSPDPCgDI2bLhfzRRfeR7LAd9xMdGmgOSCB64sMQmncryF2aMP/FEBUY9NQC5zeHFjcA5wRMaQnaOChzXgQrzecV5U53QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABqdV+CE5BU1EJLFa8MQwzy9Lf5h5OtoSUtSPNmbGy84JFaNXI3lOj7ZdB1trcmmcON0C5ZSLdbDloEGOgJdbRAksNuwi9ReDAP20Sqpq/8/wpG4cvGQcDj7QnaE7nvUIHuvEjPsyZLvDS/XFpag0/GdQAG8phtlvI1vIh1703USIrUFUKyGRFyFIIDtR9PTMC/j/zeXfXPye4yA/xd8IK+bJGLC9fM+RVESKer9qjzl4KaHo/Qin8NCzqrh4Uvwy+7ModRBcroW1tER2B+2/4UUnv7QjkRykBCO/0VJ6JnUN20rUZ+rp7CRfJxPQChsery38xTizp4HM/11v+0giagEIDgcBAAsFBAAMCg0JBgMCswJnAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAEAAAAHAAAAAAABJU17ueh1/feW4mxAqYqnn5xlI4gq8MetfQ2bCq+4U1ehgkf0jk6O8I5ArokNry+qwiWAIUrsmgfHjDnt8IUoCwmMOUiE6moo8MN5F0Ny/UIy52CpO8nCjC2M50yAgC/UJ19v2t3/LsIYbb6ly0PJAcgFlk2L7IebDm5T1PNJivUAAQAAAAAAAAAACQMA/xRAVGPTUAuc3hxY3AOcETGkJ2jgoc14EK83nFeVOd1SXdgASB2I4Btm1QB5tapW556WUNTAhOgajeMPwNjdVfwBPCehDHu6Mclx8oOSXuFApTP8e+Lqqux8Gk+YLbQusZMBPCehDHu6Mclx8oOSXuFApTP8e+Lqqux8Gk+YLbQusZMA", + "base64" + ], + "meta": { + "err": null, + "status": { + "Ok": null + }, + "fee": 10000, + "preBalances": [ + 99994966515, + 0, + 331204641, + 29687444, + 997278640, + 0, + 1, + 1141440, + 1141440, + 1141440, + 1614720, + 3048480, + 0, + 0 + ], + "postBalances": [ + 99994930513, + 0, + 331214641, + 29692446, + 994557280, + 2732360, + 1, + 1141440, + 1141440, + 1141440, + 1614720, + 3048480, + 0, + 0 + ], + "innerInstructions": [ + { + "index": 0, + "instructions": [ + { + "programIdIndex": 6, + "accounts": [ + 4, + 5 + ], + "data": "11113xKfKE6p9J9ueSoGmavScokzpTedhAJrNMacgmywBmT1tm1vFGSKi8kPhX5guiprAo", + "stackHeight": 2 + }, + { + "programIdIndex": 6, + "accounts": [ + 0, + 5 + ], + "data": "3Bxs4iNAP7JpFgdV", + "stackHeight": 2 + }, + { + "programIdIndex": 7, + "accounts": [ + 0, + 12, + 10, + 13, + 9, + 6, + 3, + 2 + ], + "data": "SsreHtLmPZSmph3vsdxZMpf2WYPqC95uk5eE3MNA6WrSEc5Xyqb3DcHgPyM1FFK6D9iPD85Fd2zguWvfFuP372c3adQKWzTYHdNzPGhDSpBQaZu3oyZ8MBWFxGNCLZvCChsAKT3BfuhpA4pP7iSeVMwuMcrtEabo4ziZQfUBpBugTaZiF19Jc316RxiWvFPtMu5t635jj3TPdCyNr48a9fy5soUGmXoSLbvKktWacRpoyv2Tz1XPsaNJT4PKDH4tzJYb9mKNFCMcn3ekjf83JCCBeNufWnQ23w6UaG1WE8VRyVJPdBLzqQrpafqsaPYttf99VWDGfG3gPaNLgtKQnpcyjFefu77GmNHk5FnPpFvZGmW4HGuB8M6WkNjpZRcpGxUAVELPFBJmWNo2weSENcadsubjQgHLCrzNaXPJVoKopZmTsAhzercbZXMymDkwiXiUajT2Q1HUdCTJFqXggVNSrzamtULVnChxFH31iUUDXmQ7jppZJqV7nyJhrzmKJ6BQxCj5hvJQL5qdP2ixafJk5d", + "stackHeight": 2 + }, + { + "programIdIndex": 6, + "accounts": [ + 0, + 2 + ], + "data": "3Bxs43ZMjSRQLs6o", + "stackHeight": 3 + }, + { + "programIdIndex": 6, + "accounts": [ + 0, + 3 + ], + "data": "3Bxs41C4bWBEDcNb", + "stackHeight": 3 + }, + { + "programIdIndex": 6, + "accounts": [ + 0, + 3 + ], + "data": "3Bxs4PckVVt51W8w", + "stackHeight": 3 + }, + { + "programIdIndex": 9, + "accounts": [ + 13, + 10, + 2, + 3 + ], + "data": "HDtpqY3uCYv8VK5FAXvJVKJiyeFUY1KJsHnwwLbivMrxEE5JmPBPHoCLNUJzwdQDk68HHfELvRZtMSVoM6iTvvEjFFWfzhRiXykKqnaMan7tthkyuLTKgq6BwoDffh2QjRvuT7Q3doiRs6xoEKDk7WAjPPRgSVVp8j16x45uJeWjXYDAbkL6EebJwvQSTTiTmdnmdLuYEAd46K8Ama2KFffMuDmqwjSGHqsEKwasYR8Z3RDggqtdZeJ5LJahZsQhTEj6WXuCp9fg2p4vCYYgXXYBX2XT2yTK8rRZ8wZTSCyuuVcCyfhjNCVLspbzhHVWNH1Lres54TWWNgJ57wA6YbGcUnRfDiuUPrtTE2Kt27tGXpf9aznCCPDzys52hKru3Bh2GnkVEa4J7sV", + "stackHeight": 3 + } + ] + } + ], + "logMessages": [ + "Program cTokenmWW8bLPjZEBAUgYy3zKxQZW6VKi7bqNFEVv3m invoke [1]", + "Program log: MintAction", + "Program 11111111111111111111111111111111 invoke [2]", + "Program 11111111111111111111111111111111 success", + "Program 11111111111111111111111111111111 invoke [2]", + "Program 11111111111111111111111111111111 success", + "Program SySTEM1eSU2p4BGQfQpimFEWWSC1XDFeun3Nqzz3rT7 invoke [2]", + "Program log: invoke_cpi_with_read_only", + "Program log: mode V2", + "Program 11111111111111111111111111111111 invoke [3]", + "Program 11111111111111111111111111111111 success", + "Program 11111111111111111111111111111111 invoke [3]", + "Program 11111111111111111111111111111111 success", + "Program 11111111111111111111111111111111 invoke [3]", + "Program 11111111111111111111111111111111 success", + "Program compr6CUsB5m2jS4Y3831ztGSTnDpnKJTKS95d64XVq invoke [3]", + "Program log: Instruction: InsertIntoQueues", + "Program compr6CUsB5m2jS4Y3831ztGSTnDpnKJTKS95d64XVq consumed 7887 of 68064 compute units", + "Program compr6CUsB5m2jS4Y3831ztGSTnDpnKJTKS95d64XVq success", + "Program SySTEM1eSU2p4BGQfQpimFEWWSC1XDFeun3Nqzz3rT7 consumed 121394 of 181534 compute units", + "Program SySTEM1eSU2p4BGQfQpimFEWWSC1XDFeun3Nqzz3rT7 success", + "Program cTokenmWW8bLPjZEBAUgYy3zKxQZW6VKi7bqNFEVv3m consumed 139956 of 200000 compute units", + "Program cTokenmWW8bLPjZEBAUgYy3zKxQZW6VKi7bqNFEVv3m success" + ], + "preTokenBalances": [], + "postTokenBalances": [], + "rewards": [], + "loadedAddresses": { + "writable": [], + "readonly": [] + }, + "computeUnitsConsumed": 139956 + }, + "blockTime": 1769454113 +} \ No newline at end of file diff --git a/tests/data/transactions/indexer_interface/2YTv5hjSmRAgfwoNHdc4DRDFWW7fqQb57f9s8Rxtu9u6jA2hDxNxEWoiybvn4p7ua2nw3scYeNo6htYCSuBYviFd b/tests/data/transactions/indexer_interface/2YTv5hjSmRAgfwoNHdc4DRDFWW7fqQb57f9s8Rxtu9u6jA2hDxNxEWoiybvn4p7ua2nw3scYeNo6htYCSuBYviFd new file mode 100644 index 00000000..66968c8a --- /dev/null +++ b/tests/data/transactions/indexer_interface/2YTv5hjSmRAgfwoNHdc4DRDFWW7fqQb57f9s8Rxtu9u6jA2hDxNxEWoiybvn4p7ua2nw3scYeNo6htYCSuBYviFd @@ -0,0 +1,157 @@ +{ + "slot": 181, + "transaction": [ + "AU0mgPKoIcxqWWNDOxB0mQnROtQjP4uGrnPyvhrrS/Ns/RdY4AnJxBcRWOF1dc+KdHTxFWoYdL1+BMr3IWkfzgwBAAoOPCehDHu6Mclx8oOSXuFApTP8e+Lqqux8Gk+YLbQusZML/xWshkLML5D3HO/9V1PopQN4HqROLjEv6NJEjwzwoA3papgQCCfpwR3bIM+xFmRoYlGnMDhgngGQUuDiXFK0lom6RB9jsvMqIWoCd4Ulg7euwYGDqc2I4P4ScSwQhQgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMGRm/lIRcy/+ytunLDm+e8jOW7xfcSayxDmzpAAAAABqdV+CE5BU1EJLFa8MQwzy9Lf5h5OtoSUtSPNmbGy84G3fbh12Whk9nL4UbO63msHLSF7V9bN5E6jPWFfv8AqQkVo1cjeU6Ptl0HW2tyaZw43QLllIt1sOWgQY6Al1tECSw27CL1F4MA/bRKqmr/z/Ckbhy8ZBwOPtCdoTue9QgLvA/Au0fKL3TEES6UqxPPo8Y05dwX6ssDzRojzX54fB7rxIz7MmS7w0v1xaWoNPxnUABvKYbZbyNbyIde9N1E5skYsL18z5FURIp6v2qPOXgpoej9CKfw0LOquHhS/DL7syh1EFyuhbW0RHYH7b/hRSe/tCORHKQEI7/RUnomdf427ipsEwFFqy88r4hvgc1jxHYducXzE7h0qGBGX02oAgUABQJAQg8ACA8AAAwDAgcGCwoNCQEIBAhh8SIwuiWze8ACAAAAvXb5WzGk9JaBZcitjB/xcMqTTXU1My2mkuHY7C6jjy7iIFXC69uRm/KOi+tBbbgYh8BI6jB4XeQTMyvupurcpwIAAAAAypo7AAAAAABlzR0AAAAAAA==", + "base64" + ], + "meta": { + "err": null, + "status": { + "Ok": null + }, + "fee": 5000, + "preBalances": [ + 99995027520, + 29677440, + 2039280, + 2923200, + 1, + 1, + 1141440, + 929020800, + 1141440, + 1141440, + 1141440, + 1614720, + 0, + 0 + ], + "postBalances": [ + 99995017518, + 29682442, + 2039280, + 2923200, + 1, + 1, + 1141440, + 929020800, + 1141440, + 1141440, + 1141440, + 1614720, + 0, + 0 + ], + "innerInstructions": [ + { + "index": 1, + "instructions": [ + { + "programIdIndex": 7, + "accounts": [ + 3, + 2, + 0 + ], + "data": "6AmSbVFP5kF9", + "stackHeight": 2 + }, + { + "programIdIndex": 6, + "accounts": [ + 0, + 12, + 11, + 10, + 13, + 9, + 8, + 6, + 6, + 4, + 6, + 1 + ], + "data": "GTTgHALwTd7DpQtTbhDPe6Etuj33yy5U6jgYa2WWYaV5bxbWNAoYdv6GqxzfqaSCM5hfpmMaS2XqiKaBpmhGziUyBwjEmtxfYUJKhK7rf8rTopmkctMexb3fYyQP2GHm41uaogWrVYgBHDCZrm2mQpmet157csi1YDjpkM4QNFks6rXCKZyJsU64atn2qSSsqVFuFXuNRcuiFSbU9h1xW354vfPAaDj8TP7TVosEgyoRemqnZ6a9HTh43uiCJgfaop36ghCh2RDcJnQLy4Tmo2FqaqA3qwq97SqA71Qpa7xnfX3oLLwCyZ5scn6YLd3RXr5vNhqxk2CkWKZZV4arwmDJG3SW7N1nWDjXewrUo2aA9jrWHD72R73bhZi3iZA3PLFk9iQTeMJZm3JP1aT652osgroGsJ1Qddq35gBbCHQRa9M5vv3B8fHAhh34UmfKvA6YNgAjS6Hv7Zzw5W91LgsKZDhUsnWDTM", + "stackHeight": 2 + }, + { + "programIdIndex": 4, + "accounts": [ + 0, + 1 + ], + "data": "3Bxs4Px9qXVhzify", + "stackHeight": 3 + }, + { + "programIdIndex": 9, + "accounts": [ + 13, + 11, + 1 + ], + "data": "9d3AmkaMU85XDt4fU7UFAkrqRj3M5LhLS5fHMv6N4JJL5sZD7yoaCiaCdPo2PyUeapwFnA8Y132N9LAPWc2GV1KTFFwtRvfzgpaJKEbtWX25RDQTrTvRKYYkPDvb7DytLhT5ULmYZXuzf2tYQxnwwJgest8dqv2dJpraxQEdE4Bqx7FaGJTze3Z7fRjQKJJ4s7gNnwAvEkxewwbqTQXNhX32sSnxkZ8Y8jXfF8s8Juwr9osmY249FDqjQbmgvdEGqkd9gbLfGgC3e6EHbMBs2JtcEj4bYYLEx5eWHd", + "stackHeight": 3 + } + ] + } + ], + "logMessages": [ + "Program ComputeBudget111111111111111111111111111111 invoke [1]", + "Program ComputeBudget111111111111111111111111111111 success", + "Program cTokenmWW8bLPjZEBAUgYy3zKxQZW6VKi7bqNFEVv3m invoke [1]", + "Program log: Instruction: MintTo", + "Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA invoke [2]", + "Program log: Instruction: MintTo", + "Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA consumed 4538 of 986662 compute units", + "Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA success", + "Program SySTEM1eSU2p4BGQfQpimFEWWSC1XDFeun3Nqzz3rT7 invoke [2]", + "Program 11111111111111111111111111111111 invoke [3]", + "Program 11111111111111111111111111111111 success", + "Program compr6CUsB5m2jS4Y3831ztGSTnDpnKJTKS95d64XVq invoke [3]", + "Program log: Instruction: InsertIntoQueues", + "Program compr6CUsB5m2jS4Y3831ztGSTnDpnKJTKS95d64XVq consumed 5020 of 956018 compute units", + "Program compr6CUsB5m2jS4Y3831ztGSTnDpnKJTKS95d64XVq success", + "Program SySTEM1eSU2p4BGQfQpimFEWWSC1XDFeun3Nqzz3rT7 consumed 21167 of 972129 compute units", + "Program SySTEM1eSU2p4BGQfQpimFEWWSC1XDFeun3Nqzz3rT7 success", + "Program cTokenmWW8bLPjZEBAUgYy3zKxQZW6VKi7bqNFEVv3m consumed 49171 of 999850 compute units", + "Program cTokenmWW8bLPjZEBAUgYy3zKxQZW6VKi7bqNFEVv3m success" + ], + "preTokenBalances": [ + { + "accountIndex": 2, + "mint": "B8dxn19gmQFB7g1wHsiN2R5jkqEWw14B3CFNQZ9tDwbm", + "uiTokenAmount": { + "uiAmount": null, + "decimals": 2, + "amount": "0", + "uiAmountString": "0" + }, + "owner": "GXtd2izAiMJPwMEjfgTRH3d7k9mjn4Jq3JrWFv9gySYy", + "programId": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" + } + ], + "postTokenBalances": [ + { + "accountIndex": 2, + "mint": "B8dxn19gmQFB7g1wHsiN2R5jkqEWw14B3CFNQZ9tDwbm", + "uiTokenAmount": { + "uiAmount": 15000000.0, + "decimals": 2, + "amount": "1500000000", + "uiAmountString": "15000000" + }, + "owner": "GXtd2izAiMJPwMEjfgTRH3d7k9mjn4Jq3JrWFv9gySYy", + "programId": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" + } + ], + "rewards": [], + "loadedAddresses": { + "writable": [], + "readonly": [] + }, + "computeUnitsConsumed": 49321 + }, + "blockTime": 1769454104 +} \ No newline at end of file diff --git a/tests/data/transactions/indexer_interface/2afJTiZyNEMvrKJasbDFqPaTaLWMttjAHnBLgm7CizqzqsiqFp6pXAB9fdrFHq66u6zvv3ddY5xLAzpmREASatnB b/tests/data/transactions/indexer_interface/2afJTiZyNEMvrKJasbDFqPaTaLWMttjAHnBLgm7CizqzqsiqFp6pXAB9fdrFHq66u6zvv3ddY5xLAzpmREASatnB new file mode 100644 index 00000000..85666707 --- /dev/null +++ b/tests/data/transactions/indexer_interface/2afJTiZyNEMvrKJasbDFqPaTaLWMttjAHnBLgm7CizqzqsiqFp6pXAB9fdrFHq66u6zvv3ddY5xLAzpmREASatnB @@ -0,0 +1,114 @@ +{ + "slot": 208, + "transaction": [ + "AU8LXvOcRJuGCjHXPKqHFyWVyEl7rT90u4shh2Vy9uNYNig5cL0OePQA16cEVuIOXcewwrnIxH89mrCiV8tWrAABAAgNPCehDHu6Mclx8oOSXuFApTP8e+Lqqux8Gk+YLbQusZMI6H6PkrgpCuD+a6ghBiFJ+7bEwI+4xaykgT6BErceDwv/FayGQswvkPcc7/1XU+ilA3gepE4uMS/o0kSPDPCgDI2bLhfzRRfeR7LAd9xMdGmgOSCB64sMQmncryF2aMP/FEBUY9NQC5zeHFjcA5wRMaQnaOChzXgQrzecV5U53QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABqdV+CE5BU1EJLFa8MQwzy9Lf5h5OtoSUtSPNmbGy84JFaNXI3lOj7ZdB1trcmmcON0C5ZSLdbDloEGOgJdbRAksNuwi9ReDAP20Sqpq/8/wpG4cvGQcDj7QnaE7nvUIHuvEjPsyZLvDS/XFpag0/GdQAG8phtlvI1vIh1703USIrUFUKyGRFyFIIDtR9PTMC/j/zeXfXPye4yA/xd8IK+bJGLC9fM+RVESKer9qjzl4KaHo/Qin8NCzqrh4Uvwy+7ModRBcroW1tER2B+2/4UUnv7QjkRykBCO/0VJ6JnUiA7TT6TASDX8cJIvdrUQV4V/RQJjaJBFxQv0uKK3+CgEHDgYACgQDAAsJDAgFAgECFGcDAAAAAQAAAAAAAQAAAAgAAAAA", + "base64" + ], + "meta": { + "err": null, + "status": { + "Ok": null + }, + "fee": 5000, + "preBalances": [ + 99994930513, + 331184640, + 29692446, + 994557280, + 2732360, + 1, + 1141440, + 1141440, + 1141440, + 1614720, + 3048480, + 0, + 0 + ], + "postBalances": [ + 99994920512, + 331184640, + 29697447, + 997289640, + 0, + 1, + 1141440, + 1141440, + 1141440, + 1614720, + 3048480, + 0, + 0 + ], + "innerInstructions": [ + { + "index": 0, + "instructions": [ + { + "programIdIndex": 6, + "accounts": [ + 0, + 11, + 9, + 12, + 8, + 5, + 2, + 1, + 2 + ], + "data": "7yPYWt3P1KLPqjnnhJNfj5DorRne6QD8zS9JUUYH4kJpXGovUoiE6HxphCA6gsTLyzN2ch4UUJXkk86r6mthoWQvFC3bYagFdqeRCsJkmezxzSJeWmGRJGNMeJ9sTXq2jDJCx8UP57yWiigktnJszKufAmgveJGD8VxrpnHgTPeQaaB1V6TTNP11FBcDypXyJmuSZkktnDZCo7AN8W3LAZBkiyu47F3z3Xodf7E4HVKD3UXv1Wy5GkS2qnkyWy6V9NQvPaK2xcrVLUGT1DmUSfptTTPa7ndBqDwqrGAc2EFMBW2cAFVxh1eWNrFtN6Hw5AqDEXy9FeV3NqPJdmgX2UB81jkdZPeiD9RoWdkK6UFAbQr17k2X6cysEqycpcmpimxLwKfwtTTVyrgNWiTcuC6PmhZdqD8j8sNrwredsq7pRBndp39RVvmvRNxuVVKw3wmVrmavgq4bVeFukqABddKsL6pjoRESq5WxPqnJiTGdF1uPRXVBgU4wx9cvRPMHHWLo4XTtJM86PPZEq7JC2sUkU36ntPSUUVE36KSCTZa3mGFXBYkqjHgYpJsUERL2BFFg6sZViyPqeS1ncQ7s8YTsmxvHXC2ss27MmnzvJjUYybnHxWgcuLdjwACJ5VU88sPGZscaHhHmWcaQuaPJ7sPFQitGBYLmDYnirNgi2cf58BxZ4PG9vDS7PPJeTzv9yL8h5YP3qGWJwiakjS2LF1rdx9FCM85KbFYLU7LUQk24Ej", + "stackHeight": 2 + }, + { + "programIdIndex": 5, + "accounts": [ + 0, + 2 + ], + "data": "3Bxs4PnTAWgtW7QT", + "stackHeight": 3 + }, + { + "programIdIndex": 8, + "accounts": [ + 12, + 9, + 2, + 2, + 1 + ], + "data": "95YB9JH8MTYtuZfgNwFoqXrHxz6iXNSKY1qkjTcBMMeHjhd7su25YYcxsur46vsYc4kvTLvUGUB1E2EmdLVQzNBiyrMqCRy82cF6QSUzwTvHFrkC9LSD49oYxj89q7W1UAdFFvB6nrfud9pEUsDGM8sRYKYu3HqkPs5FYBpbAA2yvUEFvSqnRXpbYxpxFgPJ8EGrQrcsW5pVHvrixd89yT1P2Mpzaacg8e9SQRjZS3t2qWHmmnwutPE2AEpy9jszxwjbgNvcUkj18drGNNfKox7Dn64QSWNDmb9CKqh7rska5Lp1yghsDRnQUuh6MLaLust5qEKUHDK568Cih2T83NeZasP84hce5UEDcoinBEtKrSjoFUB7vm9oERjjwDn9bBUoATx31CYHiokexpQwpT", + "stackHeight": 3 + } + ] + } + ], + "logMessages": [ + "Program cTokenmWW8bLPjZEBAUgYy3zKxQZW6VKi7bqNFEVv3m invoke [1]", + "Program log: MintAction", + "Program SySTEM1eSU2p4BGQfQpimFEWWSC1XDFeun3Nqzz3rT7 invoke [2]", + "Program log: invoke_cpi_with_read_only", + "Program log: mode V2", + "Program 11111111111111111111111111111111 invoke [3]", + "Program 11111111111111111111111111111111 success", + "Program compr6CUsB5m2jS4Y3831ztGSTnDpnKJTKS95d64XVq invoke [3]", + "Program log: Instruction: InsertIntoQueues", + "Program compr6CUsB5m2jS4Y3831ztGSTnDpnKJTKS95d64XVq consumed 9419 of 171642 compute units", + "Program compr6CUsB5m2jS4Y3831ztGSTnDpnKJTKS95d64XVq success", + "Program SySTEM1eSU2p4BGQfQpimFEWWSC1XDFeun3Nqzz3rT7 consumed 28244 of 190430 compute units", + "Program SySTEM1eSU2p4BGQfQpimFEWWSC1XDFeun3Nqzz3rT7 success", + "Program cTokenmWW8bLPjZEBAUgYy3zKxQZW6VKi7bqNFEVv3m consumed 37908 of 200000 compute units", + "Program cTokenmWW8bLPjZEBAUgYy3zKxQZW6VKi7bqNFEVv3m success" + ], + "preTokenBalances": [], + "postTokenBalances": [], + "rewards": [], + "loadedAddresses": { + "writable": [], + "readonly": [] + }, + "computeUnitsConsumed": 37908 + }, + "blockTime": 1769454116 +} \ No newline at end of file diff --git a/tests/data/transactions/indexer_interface/5bLkzWasPYAivyrVvrt5UN3amycT3MSdNB2evCEMyuW1Ajbufv4nTbqrp7ZQ5fpopHh7pU9RnimvvR31XeckFt9F b/tests/data/transactions/indexer_interface/5bLkzWasPYAivyrVvrt5UN3amycT3MSdNB2evCEMyuW1Ajbufv4nTbqrp7ZQ5fpopHh7pU9RnimvvR31XeckFt9F new file mode 100644 index 00000000..66a67be0 --- /dev/null +++ b/tests/data/transactions/indexer_interface/5bLkzWasPYAivyrVvrt5UN3amycT3MSdNB2evCEMyuW1Ajbufv4nTbqrp7ZQ5fpopHh7pU9RnimvvR31XeckFt9F @@ -0,0 +1,158 @@ +{ + "slot": 193, + "transaction": [ + "AuWunKtcv7kqto1NtjY8HIuzotEs+lg1kijLpU+PBwo0mYt51Ln4bh1F9NOgiGpRmUty4UE6FueiV0W9DTjXYwqAsevIRqtuTRPENNSzGW7y2vIVX3wxPdQTp9JRwz/57LvsV3lUbmBUaKE2HIG2K4KQyyaLMP5npN/xHkt+paMAAgEIDjwnoQx7ujHJcfKDkl7hQKUz/Hvi6qrsfBpPmC20LrGTMtb6DSl+WkiPAtcrxA8NVI+tc289204ixlOq983dKEcIpumYsF/pK5ig2fd52dvosvPu9ngTKHvrpSZJgJFovQv/FayGQswvkPcc7/1XU+ilA3gepE4uMS/o0kSPDPCgDI2bLhfzRRfeR7LAd9xMdGmgOSCB64sMQmncryF2aMOqHn30jJgOMQTkdobcfQiPsJtmK5kNekZQUhC5RGMHSAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABqdV+CE5BU1EJLFa8MQwzy9Lf5h5OtoSUtSPNmbGy84JFaNXI3lOj7ZdB1trcmmcON0C5ZSLdbDloEGOgJdbRAksNuwi9ReDAP20Sqpq/8/wpG4cvGQcDj7QnaE7nvUIHuvEjPsyZLvDS/XFpag0/GdQAG8phtlvI1vIh1703USIrUFUKyGRFyFIIDtR9PTMC/j/zeXfXPye4yA/xd8IK+bJGLC9fM+RVESKer9qjzl4KaHo/Qin8NCzqrh4Uvwy+7ModRBcroW1tER2B+2/4UUnv7QjkRykBCO/0VJ6JnVMF4x44Hz7GtbAJgLPgC0V51Frv1yJL2Zbk7+uCvjBgQEIDgcBAAsFBAAMCg0JBgMCkwJnAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAEAAAAHAAAAAAABAZi6vOM5+jYyfzW0tiOzPz0FS26iRcNK0P98UId9FU6ruLyzZ2rzuMC5iE4jD2hI3y6+KgtNCdbEHDjqzRXQOSdyPKepwSiCmdX4eYgL+4yMbYaifBspG1KAotUoacNrrXqc78QvOFGYjsG7eoaMFRcbW7ulIBRLim0QA0PLp/YAAQAAAAAAAAAABgMAqh599IyYDjEE5HaG3H0Ij7CbZiuZDXpGUFIQuURjB0gy1voNKX5aSI8C1yvEDw1Uj61zbz3bTiLGU6r3zd0oR/8BPCehDHu6Mclx8oOSXuFApTP8e+Lqqux8Gk+YLbQusZMAAA==", + "base64" + ], + "meta": { + "err": null, + "status": { + "Ok": null + }, + "fee": 10000, + "preBalances": [ + 99995002517, + 0, + 331194641, + 29682442, + 1000000000, + 0, + 1, + 1141440, + 1141440, + 1141440, + 1614720, + 3048480, + 0, + 0 + ], + "postBalances": [ + 99994966515, + 0, + 331204641, + 29687444, + 997278640, + 2732360, + 1, + 1141440, + 1141440, + 1141440, + 1614720, + 3048480, + 0, + 0 + ], + "innerInstructions": [ + { + "index": 0, + "instructions": [ + { + "programIdIndex": 6, + "accounts": [ + 4, + 5 + ], + "data": "11113xKfKE6p9J9ueSoGmavScokzpTedhAJrNMacgmywBmT1tm1vFGSKi8kPhX5guiprAo", + "stackHeight": 2 + }, + { + "programIdIndex": 6, + "accounts": [ + 0, + 5 + ], + "data": "3Bxs4iNAP7JpFgdV", + "stackHeight": 2 + }, + { + "programIdIndex": 7, + "accounts": [ + 0, + 12, + 10, + 13, + 9, + 6, + 3, + 2 + ], + "data": "SsreHtLmPZSmph3vsdxZMpf2WYPqC95uk5eE3MNA6WrSEc5Xyqb3DcHgPyM1FFK6D9iPD85Fd2zguWaLho3EC7ujP4ng4EZ3qCbzGSWxnrNRiC3USzXkjCy83WiMTkvQ7Z9SFADiEZpFS9fuQqWVz5eyy1Mtno2i8WyQ3HW1VjNkUHDjkJxHLavNCn8UQEhrYqoRGr2r2ufXusxvKwFzbKFi64EUSEMFzA2iGQ3gBjNPeByjUAJ4dbZUX4Cyhh8VcBqTXUx6B6UmNTZ3SDYFKidJVarjCDfwVKa4RGenMJZ3b8wFAVzu9FyFLtoSp5yPoBg8LkkbkYXQjnXxaNvaCFNy7vJQkDDU6Zi46EVVx7XmJhAgUMP4x65xMn41dyf1T85BsZKK1fmfqCqbfQn3bod5jbQWiUUvzbrDeqUuYx1oVHQ5uLnPVvmBBCeJy6HWLyD4CfFE3oZRgMfK6aarzBRrDhTVyswtnKf4ZDhsPPqXhSxNkKWNjZ3XkLxqjWqmW3ZwUW7HxU1vP9BDysFVutwqAP", + "stackHeight": 2 + }, + { + "programIdIndex": 6, + "accounts": [ + 0, + 2 + ], + "data": "3Bxs43ZMjSRQLs6o", + "stackHeight": 3 + }, + { + "programIdIndex": 6, + "accounts": [ + 0, + 3 + ], + "data": "3Bxs41C4bWBEDcNb", + "stackHeight": 3 + }, + { + "programIdIndex": 6, + "accounts": [ + 0, + 3 + ], + "data": "3Bxs4PckVVt51W8w", + "stackHeight": 3 + }, + { + "programIdIndex": 9, + "accounts": [ + 13, + 10, + 2, + 3 + ], + "data": "HDtpqY3uCYv8VK5FAXvJVKJiyeFUY1KJsHnwwLbivMrxEE5JmPBPHoCLNUJzwdQDk68HHfEMf5Xnt1Fm2otBYmxvscTYkmVupf9G1BrBgdoRqPzYqx9KMYqykNczWyupQCxSuTTDrFqeWjtygFFH8qSZzYBwBTmiiVYbcBe1Bo8GQRsfmZ4uiWQoFtJWtyyk8rhqMSfUHtgAvjoPN2d9hWXW5nafb4sPr9zDxb3NkLdp8ej8Fdogyh8czH81eUUvActkPVZTi75zyaoNPvKqVtW7Bg1pAodLD4kKkHhHndp7QtD9X9vV37ZMgU5cwMXcah9vV7RhqVfgeCXAygbQBu9wcPkBnPfDyqXn2rNKQRcheoWwqxj2J4koibvtUtbH9mJioDyutZ1oF1R", + "stackHeight": 3 + } + ] + } + ], + "logMessages": [ + "Program cTokenmWW8bLPjZEBAUgYy3zKxQZW6VKi7bqNFEVv3m invoke [1]", + "Program log: MintAction", + "Program 11111111111111111111111111111111 invoke [2]", + "Program 11111111111111111111111111111111 success", + "Program 11111111111111111111111111111111 invoke [2]", + "Program 11111111111111111111111111111111 success", + "Program SySTEM1eSU2p4BGQfQpimFEWWSC1XDFeun3Nqzz3rT7 invoke [2]", + "Program log: invoke_cpi_with_read_only", + "Program log: mode V2", + "Program 11111111111111111111111111111111 invoke [3]", + "Program 11111111111111111111111111111111 success", + "Program 11111111111111111111111111111111 invoke [3]", + "Program 11111111111111111111111111111111 success", + "Program 11111111111111111111111111111111 invoke [3]", + "Program 11111111111111111111111111111111 success", + "Program compr6CUsB5m2jS4Y3831ztGSTnDpnKJTKS95d64XVq invoke [3]", + "Program log: Instruction: InsertIntoQueues", + "Program compr6CUsB5m2jS4Y3831ztGSTnDpnKJTKS95d64XVq consumed 7887 of 72596 compute units", + "Program compr6CUsB5m2jS4Y3831ztGSTnDpnKJTKS95d64XVq success", + "Program SySTEM1eSU2p4BGQfQpimFEWWSC1XDFeun3Nqzz3rT7 consumed 121394 of 186066 compute units", + "Program SySTEM1eSU2p4BGQfQpimFEWWSC1XDFeun3Nqzz3rT7 success", + "Program cTokenmWW8bLPjZEBAUgYy3zKxQZW6VKi7bqNFEVv3m consumed 135424 of 200000 compute units", + "Program cTokenmWW8bLPjZEBAUgYy3zKxQZW6VKi7bqNFEVv3m success" + ], + "preTokenBalances": [], + "postTokenBalances": [], + "rewards": [], + "loadedAddresses": { + "writable": [], + "readonly": [] + }, + "computeUnitsConsumed": 135424 + }, + "blockTime": 1769454109 +} \ No newline at end of file diff --git a/tests/data/transactions/indexer_interface/628ZqqrNWuVUfHF2seH7XQEZqBJtWA8NaAjcVeH96XWaubUSeWpegrdfvrPkGA8qxfBFpu5ru9DjTFktRSB6w4s1 b/tests/data/transactions/indexer_interface/628ZqqrNWuVUfHF2seH7XQEZqBJtWA8NaAjcVeH96XWaubUSeWpegrdfvrPkGA8qxfBFpu5ru9DjTFktRSB6w4s1 new file mode 100644 index 00000000..7143dbc5 --- /dev/null +++ b/tests/data/transactions/indexer_interface/628ZqqrNWuVUfHF2seH7XQEZqBJtWA8NaAjcVeH96XWaubUSeWpegrdfvrPkGA8qxfBFpu5ru9DjTFktRSB6w4s1 @@ -0,0 +1,115 @@ +{ + "slot": 192, + "transaction": [ + "AfsPUmjOEBICNrhP7/Bg2o9koAwZ2oY8a/MQzvBpCrlc8YqlThsS2hSNtXJqkoBYwVPiwwdah77JQjbepbbS4gABAAkLPCehDHu6Mclx8oOSXuFApTP8e+Lqqux8Gk+YLbQusZMIpumYsF/pK5ig2fd52dvosvPu9ngTKHvrpSZJgJFovQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwZGb+UhFzL/7K26csOb57yM5bvF9xJrLEObOkAAAAAGp1X4ITkFTUQksVrwxDDPL0t/mHk62hJS1I82ZsbLzgksNuwi9ReDAP20Sqpq/8/wpG4cvGQcDj7QnaE7nvUIC7wPwLtHyi90xBEulKsTz6PGNOXcF+rLA80aI81+eHwe68SM+zJku8NL9cWlqDT8Z1AAbymG2W8jW8iHXvTdRNWem8sa08HDC9sJmzqsOsj+S+dqH6i3TLNx6mW/Y5xi9f/NHLoyuMDzJdu5uE5yCqPBB+d1u+u44BM7lLKWQ/L7syh1EFyuhbW0RHYH7b/hRSe/tCORHKQEI7/RUnomdQflONqnQEnTSXyD+ptXOtcIdrRa5K5HsdBhsmpzu8yWAgMABQJAQg8ACAoABAUKBwYICQIBgAIx1L+BJ8IrxPMAAABWL6OmFd9cCAD/1Z6byxrTwcML2wmbOqw6yP5L52ofqLdMs3HqZb9jnGIAAAAAAAAAAAAAAQAAAAEmb4gY4t+qkKEhVaTDn5Ilgei4BYNQdfyEHk1ly1JGxA60J22rap8E3REII0w22VHmKvaSG7I+NrP42R6HXdBsLbZ0BS0Gp/MT4S8hwuMMh/E3GVOMexZ7Oq1NgrNu0juDiEZXj/+M6ysKKbZcbac4fWisuPBckRrLrs185dp0wQEAAAAqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKgAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/", + "base64" + ], + "meta": { + "err": null, + "status": { + "Ok": null + }, + "fee": 5000, + "preBalances": [ + 99995017518, + 331184640, + 1, + 1, + 1141440, + 1141440, + 1141440, + 1614720, + 1141440, + 0, + 0 + ], + "postBalances": [ + 99995002517, + 331194641, + 1, + 1, + 1141440, + 1141440, + 1141440, + 1614720, + 1141440, + 0, + 0 + ], + "innerInstructions": [ + { + "index": 1, + "instructions": [ + { + "programIdIndex": 4, + "accounts": [ + 0, + 9, + 7, + 6, + 10, + 5, + 8, + 4, + 4, + 2, + 4, + 1 + ], + "data": "BqT7gkM6QBUXLrebMuy9CaECRJVBDqCrrkzhkYSUzDvv98DeYSB9aE7c7gULTn24ZQLCzP11o1hX4SUYQaJC3V6xgkhkK3EZPddj4RKWAZQABjBp5nCzEZuZtWLpyagyQgUk9RvPxzGVZMXkR3gqsunjMy8oDT73cv41srUqyc56kRTSLvkRwzqizowJKdWUFxsWbt6FCWPEGBFw79WPV3FBvmA4kWVNM2jTuKhm5Zr422BJdp2VcxgTjY4efecNV3JjUGv3nZ6SX2jVRTBEFDzC1xhXcN7n3Bb5cTMPbWyChs9WyTcMM4vJijHm6pVAecWQjEkUxmQs", + "stackHeight": 2 + }, + { + "programIdIndex": 2, + "accounts": [ + 0, + 1 + ], + "data": "3Bxs43j4QTEDqUNK", + "stackHeight": 3 + }, + { + "programIdIndex": 5, + "accounts": [ + 10, + 7, + 1 + ], + "data": "gK3e1kzqySfsx4tv3nzLMZFLDuiod2Yo4Ayk9tULRFFquKTNa1tMWGhsXsKvAdGNkc4anw77fExSM5RvUkhM5bjJ8zuEXpJ7WD8bDCXHyUbDFS3dtLq3FfiVPsWj5AoNQfS27FS3mMVcMLuSgTUWSMYndJ6CJG3xi3HVTGCzdP8oiNCmobqQVdqPALH8Gsud8AsEYzBTuCkJXk18QKgXnk7KkZYNMyRxYEMohxpD5ofNTTD", + "stackHeight": 3 + } + ] + } + ], + "logMessages": [ + "Program ComputeBudget111111111111111111111111111111 invoke [1]", + "Program ComputeBudget111111111111111111111111111111 success", + "Program FNt7byTHev1k5x2cXZLBr8TdWiC3zoP5vcnZR4P682Uy invoke [1]", + "Program log: Instruction: InvokeCpi", + "Program consumption: 996329 units remaining", + "Program consumption: 993542 units remaining", + "Program SySTEM1eSU2p4BGQfQpimFEWWSC1XDFeun3Nqzz3rT7 invoke [2]", + "Program log: invoke_cpi_with_read_only", + "Program log: mode Anchor", + "Program 11111111111111111111111111111111 invoke [3]", + "Program 11111111111111111111111111111111 success", + "Program compr6CUsB5m2jS4Y3831ztGSTnDpnKJTKS95d64XVq invoke [3]", + "Program log: Instruction: InsertIntoQueues", + "Program compr6CUsB5m2jS4Y3831ztGSTnDpnKJTKS95d64XVq consumed 4712 of 883793 compute units", + "Program compr6CUsB5m2jS4Y3831ztGSTnDpnKJTKS95d64XVq success", + "Program SySTEM1eSU2p4BGQfQpimFEWWSC1XDFeun3Nqzz3rT7 consumed 110338 of 989382 compute units", + "Program SySTEM1eSU2p4BGQfQpimFEWWSC1XDFeun3Nqzz3rT7 success", + "Program FNt7byTHev1k5x2cXZLBr8TdWiC3zoP5vcnZR4P682Uy consumed 121427 of 999850 compute units", + "Program FNt7byTHev1k5x2cXZLBr8TdWiC3zoP5vcnZR4P682Uy success" + ], + "preTokenBalances": [], + "postTokenBalances": [], + "rewards": [], + "loadedAddresses": { + "writable": [], + "readonly": [] + }, + "computeUnitsConsumed": 121577 + }, + "blockTime": 1769454109 +} \ No newline at end of file diff --git a/tests/integration_tests/interface_tests.rs b/tests/integration_tests/interface_tests.rs new file mode 100644 index 00000000..f753b3e1 --- /dev/null +++ b/tests/integration_tests/interface_tests.rs @@ -0,0 +1,222 @@ +use std::sync::Arc; + +use function_name::named; +use futures::{pin_mut, StreamExt}; +use photon_indexer::api::method::interface::{ + GetAccountInterfaceRequest, GetMultipleAccountInterfacesRequest, +}; +use photon_indexer::common::typedefs::serializable_pubkey::SerializablePubkey; +use photon_indexer::ingester::index_block; +use photon_indexer::ingester::typedefs::block_info::{BlockInfo, BlockMetadata}; +use sea_orm::DatabaseConnection; +use serial_test::serial; +use solana_client::nonblocking::rpc_client::RpcClient; + +use crate::utils::*; + +/// Directory containing interface test transaction data. +/// Generated by forester's test_indexer_interface test. +const INTERFACE_TEST_DATA_DIR: &str = "indexer_interface"; + +/// Helper to set up indexing methodologies for interface tests. +fn all_indexing_methodologies_for_interface( + db_conn: Arc, + rpc_client: Arc, + txns: &[&str], +) -> impl futures::Stream { + let txs = txns.iter().map(|x| x.to_string()).collect::>(); + async_stream::stream! { + reset_tables(db_conn.as_ref()).await.unwrap(); + populate_test_tree_metadata(db_conn.as_ref()).await; + index_block( + db_conn.as_ref(), + &BlockInfo { + metadata: BlockMetadata { + slot: 0, + ..Default::default() + }, + ..Default::default() + }, + ) + .await + .unwrap(); + + for tx in &txs { + index_transaction(INTERFACE_TEST_DATA_DIR, db_conn.clone(), rpc_client.clone(), tx).await; + } + yield (); + } +} + +/// Test getAccountInterface resolves compressed token accounts by token owner pubkey. +#[named] +#[rstest] +#[tokio::test] +#[serial] +async fn test_get_account_interface_token_owner( + #[values(DatabaseBackend::Sqlite, DatabaseBackend::Postgres)] db_backend: DatabaseBackend, +) { + let name = trim_test_name(function_name!()); + let setup = setup_with_options( + name.clone(), + TestSetupOptions { + network: Network::Localnet, + db_backend, + }, + ) + .await; + + // Mint compressed tokens to Bob and Charlie + let mint_tokens_tx = + "2YTv5hjSmRAgfwoNHdc4DRDFWW7fqQb57f9s8Rxtu9u6jA2hDxNxEWoiybvn4p7ua2nw3scYeNo6htYCSuBYviFd"; + + // Bob's pubkey is the token owner. Interface lookup should return + // synthetic account data plus compressed token accounts in `cold`. + let bob_pubkey = + SerializablePubkey::try_from("DkbH1tracp6nxSQLrHQqJwVE7NaDAAb7eGKzfB9TwBdF").unwrap(); + + let txs = [mint_tokens_tx]; + let indexing = + all_indexing_methodologies_for_interface(setup.db_conn.clone(), setup.client.clone(), &txs); + pin_mut!(indexing); + + while let Some(_) = indexing.next().await { + // getAccountInterface must support token-owner lookups. + let result = setup + .api + .get_account_interface(GetAccountInterfaceRequest { + address: bob_pubkey, + }) + .await + .unwrap(); + + let value = result.value.clone().expect("expected interface value"); + let cold = value.cold.expect("expected compressed token accounts"); + assert!( + !cold.is_empty(), + "expected at least one compressed token account" + ); + } +} + +/// Test getMultipleAccountInterfaces with mixed addresses. +/// +/// Tests batch lookup with addresses that don't exist in compressed DB. +#[named] +#[rstest] +#[tokio::test] +#[serial] +async fn test_get_multiple_account_interfaces( + #[values(DatabaseBackend::Sqlite, DatabaseBackend::Postgres)] db_backend: DatabaseBackend, +) { + let name = trim_test_name(function_name!()); + let setup = setup_with_options( + name.clone(), + TestSetupOptions { + network: Network::Localnet, + db_backend, + }, + ) + .await; + + // Mint compressed tokens to Bob and Charlie + let mint_tokens_tx = + "2YTv5hjSmRAgfwoNHdc4DRDFWW7fqQb57f9s8Rxtu9u6jA2hDxNxEWoiybvn4p7ua2nw3scYeNo6htYCSuBYviFd"; + + // Token owner pubkeys. + let bob_pubkey = + SerializablePubkey::try_from("DkbH1tracp6nxSQLrHQqJwVE7NaDAAb7eGKzfB9TwBdF").unwrap(); + let charlie_pubkey = + SerializablePubkey::try_from("GDhjk4DmDQ8bFWqrdcLGaJcYBDnq73ExjGyZYrca6nDc").unwrap(); + // Use a random address that definitely doesn't exist on-chain or compressed + // (default pubkey 11111... is the system program which exists on-chain) + let nonexistent_address = + SerializablePubkey::try_from("DeadDeadDeadDeadDeadDeadDeadDeadDeadDeadDead").unwrap(); + + let txs = [mint_tokens_tx]; + let indexing = + all_indexing_methodologies_for_interface(setup.db_conn.clone(), setup.client.clone(), &txs); + pin_mut!(indexing); + + while let Some(_) = indexing.next().await { + // Test getMultipleAccountInterfaces + let result = setup + .api + .get_multiple_account_interfaces(GetMultipleAccountInterfacesRequest { + addresses: vec![bob_pubkey, charlie_pubkey, nonexistent_address], + }) + .await + .unwrap(); + + // Both owner pubkeys should resolve to token-backed interfaces. + assert_eq!(result.value.len(), 3); + assert!(result.value[0] + .as_ref() + .and_then(|v| v.cold.as_ref()) + .is_some_and(|cold| !cold.is_empty())); + assert!(result.value[1] + .as_ref() + .and_then(|v| v.cold.as_ref()) + .is_some_and(|cold| !cold.is_empty())); + assert!(result.value[2].is_none()); + } +} + +/// Test batch size validation for getMultipleAccountInterfaces. +#[named] +#[rstest] +#[tokio::test] +#[serial] +async fn test_get_multiple_account_interfaces_validation( + #[values(DatabaseBackend::Sqlite, DatabaseBackend::Postgres)] db_backend: DatabaseBackend, +) { + let name = trim_test_name(function_name!()); + let setup = setup_with_options( + name.clone(), + TestSetupOptions { + network: Network::Localnet, + db_backend, + }, + ) + .await; + + // Test empty request - should fail + let empty_result = setup + .api + .get_multiple_account_interfaces(GetMultipleAccountInterfacesRequest { addresses: vec![] }) + .await; + + assert!(empty_result.is_err()); + + // Test over limit - should fail (MAX_BATCH_SIZE is 100) + let over_limit: Vec = + (0..101).map(|_| SerializablePubkey::default()).collect(); + + let over_limit_result = setup + .api + .get_multiple_account_interfaces(GetMultipleAccountInterfacesRequest { + addresses: over_limit, + }) + .await; + + assert!(over_limit_result.is_err()); +} + +/* +## Test Data + +Test data is generated by forester's test_indexer_interface test: + cd light-protocol/forester && cargo test -p forester --test test_indexer_interface -- --nocapture + +Then exported using: + cargo xtask export-photon-test-data --test-name indexer_interface + +The test creates: +1. SPL Mint (on-chain) - standard mint for token operations +2. Compressed token accounts (via mint_to) - these have NO address field +3. Registered v2 address in batched address tree - for address tree verification +4. Compressible token accounts - on-chain accounts that can be compressed + +Transaction files are stored in tests/data/transactions/indexer_interface/ + +*/ diff --git a/tests/integration_tests/main.rs b/tests/integration_tests/main.rs index 78bad68e..b6691a99 100644 --- a/tests/integration_tests/main.rs +++ b/tests/integration_tests/main.rs @@ -6,6 +6,7 @@ mod batch_append_nullified_test; mod batched_address_tree_tests; mod batched_state_tree_tests; mod e2e_tests; +mod interface_tests; mod merkle_tree_deserialization; mod mock_tests; mod monitor_tests; diff --git a/tests/integration_tests/snapshots/integration_tests__interface_tests__get_account_interface_compressed_only-account-interface.snap b/tests/integration_tests/snapshots/integration_tests__interface_tests__get_account_interface_compressed_only-account-interface.snap new file mode 100644 index 00000000..b3875ed4 --- /dev/null +++ b/tests/integration_tests/snapshots/integration_tests__interface_tests__get_account_interface_compressed_only-account-interface.snap @@ -0,0 +1,11 @@ +--- +source: tests/integration_tests/interface_tests.rs +assertion_line: 107 +expression: result +--- +{ + "context": { + "slot": 0 + }, + "value": null +} diff --git a/tests/integration_tests/snapshots/integration_tests__interface_tests__get_account_interface_nonexistent-account-interface.snap b/tests/integration_tests/snapshots/integration_tests__interface_tests__get_account_interface_nonexistent-account-interface.snap new file mode 100644 index 00000000..a9bd9223 --- /dev/null +++ b/tests/integration_tests/snapshots/integration_tests__interface_tests__get_account_interface_nonexistent-account-interface.snap @@ -0,0 +1,11 @@ +--- +source: tests/integration_tests/interface_tests.rs +assertion_line: 106 +expression: result +--- +{ + "context": { + "slot": 0 + }, + "value": null +} diff --git a/tests/integration_tests/snapshots/integration_tests__interface_tests__get_multiple_account_interfaces-multiple-account-interfaces.snap b/tests/integration_tests/snapshots/integration_tests__interface_tests__get_multiple_account_interfaces-multiple-account-interfaces.snap new file mode 100644 index 00000000..3ee0f30e --- /dev/null +++ b/tests/integration_tests/snapshots/integration_tests__interface_tests__get_multiple_account_interfaces-multiple-account-interfaces.snap @@ -0,0 +1,15 @@ +--- +source: tests/integration_tests/interface_tests.rs +assertion_line: 230 +expression: result +--- +{ + "context": { + "slot": 0 + }, + "value": [ + null, + null, + null + ] +} diff --git a/tests/integration_tests/snapshots/integration_tests__interface_tests__get_token_account_interface_by_owner-token-account-interface.snap b/tests/integration_tests/snapshots/integration_tests__interface_tests__get_token_account_interface_by_owner-token-account-interface.snap new file mode 100644 index 00000000..06f65e54 --- /dev/null +++ b/tests/integration_tests/snapshots/integration_tests__interface_tests__get_token_account_interface_by_owner-token-account-interface.snap @@ -0,0 +1,11 @@ +--- +source: tests/integration_tests/interface_tests.rs +assertion_line: 161 +expression: result +--- +{ + "context": { + "slot": 0 + }, + "value": null +} diff --git a/tests/integration_tests/snapshots/integration_tests__interface_tests__get_token_account_interface_compressed_only-token-account-interface.snap b/tests/integration_tests/snapshots/integration_tests__interface_tests__get_token_account_interface_compressed_only-token-account-interface.snap new file mode 100644 index 00000000..a280f25a --- /dev/null +++ b/tests/integration_tests/snapshots/integration_tests__interface_tests__get_token_account_interface_compressed_only-token-account-interface.snap @@ -0,0 +1,11 @@ +--- +source: tests/integration_tests/interface_tests.rs +assertion_line: 157 +expression: result +--- +{ + "context": { + "slot": 0 + }, + "value": null +} From 69a6a555ba866801de6ec9eb7c6240d47464f87b Mon Sep 17 00:00:00 2001 From: Sergey Timoshin Date: Tue, 3 Mar 2026 15:30:38 +0000 Subject: [PATCH 10/12] fix: remove redundant comment for SPL Token program ID in types.rs --- src/api/method/interface/types.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/api/method/interface/types.rs b/src/api/method/interface/types.rs index 42ba4a8d..737979a9 100644 --- a/src/api/method/interface/types.rs +++ b/src/api/method/interface/types.rs @@ -81,7 +81,4 @@ pub const MAX_BATCH_SIZE: usize = 100; pub const RPC_TIMEOUT_MS: u64 = 5000; /// Database timeout in milliseconds for cold lookups -pub const DB_TIMEOUT_MS: u64 = 3000; - -/// SPL Token program ID (on-chain Token program) -pub const SPL_TOKEN_PROGRAM_ID: Pubkey = pubkey!("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"); +pub const DB_TIMEOUT_MS: u64 = 3000; \ No newline at end of file From acbd5c52a74674cca581bb31fbfe7263fd32c2b8 Mon Sep 17 00:00:00 2001 From: Sergey Timoshin Date: Tue, 3 Mar 2026 18:47:05 +0000 Subject: [PATCH 11/12] feat: add discriminator_v2 field to account interface and update related test structure --- src/api/method/interface/racing.rs | 1 + src/api/method/interface/types.rs | 3 +-- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/api/method/interface/racing.rs b/src/api/method/interface/racing.rs index 8ed98f2f..fa5efeb5 100644 --- a/src/api/method/interface/racing.rs +++ b/src/api/method/interface/racing.rs @@ -1030,6 +1030,7 @@ mod tests { prev_spent: Some(false), tx_hash: None, onchain_pubkey, + discriminator_v2: None, tree_type: Some(3), nullified_in_tree: false, nullifier_queue_index: None, diff --git a/src/api/method/interface/types.rs b/src/api/method/interface/types.rs index 737979a9..d120bab8 100644 --- a/src/api/method/interface/types.rs +++ b/src/api/method/interface/types.rs @@ -1,5 +1,4 @@ use serde::{Deserialize, Serialize}; -use solana_pubkey::{pubkey, Pubkey}; use utoipa::ToSchema; use crate::common::typedefs::account::AccountV2; @@ -81,4 +80,4 @@ pub const MAX_BATCH_SIZE: usize = 100; pub const RPC_TIMEOUT_MS: u64 = 5000; /// Database timeout in milliseconds for cold lookups -pub const DB_TIMEOUT_MS: u64 = 3000; \ No newline at end of file +pub const DB_TIMEOUT_MS: u64 = 3000; From cfc1a7572d485b2e21677e45436fb6cdc727b919 Mon Sep 17 00:00:00 2001 From: Sergey Timoshin Date: Wed, 4 Mar 2026 20:44:46 +0000 Subject: [PATCH 12/12] fix: add missing rpc_client arg to interface_tests index_block call --- tests/integration_tests/interface_tests.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/integration_tests/interface_tests.rs b/tests/integration_tests/interface_tests.rs index f753b3e1..f93f1736 100644 --- a/tests/integration_tests/interface_tests.rs +++ b/tests/integration_tests/interface_tests.rs @@ -37,6 +37,7 @@ fn all_indexing_methodologies_for_interface( }, ..Default::default() }, + &rpc_client, ) .await .unwrap();