diff --git a/.sqlx/query-56485f1d902b775a7d9702dfe3da13b35f3ec2f47bdccd481116f663d3a5c3bc.json b/.sqlx/query-56485f1d902b775a7d9702dfe3da13b35f3ec2f47bdccd481116f663d3a5c3bc.json new file mode 100644 index 0000000..df58d5f --- /dev/null +++ b/.sqlx/query-56485f1d902b775a7d9702dfe3da13b35f3ec2f47bdccd481116f663d3a5c3bc.json @@ -0,0 +1,262 @@ +{ + "db_name": "PostgreSQL", + "query": "WITH\n canonical_blocks AS (\n SELECT\n *\n FROM\n blocks\n WHERE\n chain_status='canonical'\n ),\n max_canonical_height AS (\n SELECT\n max(HEIGHT) AS max_height\n FROM\n canonical_blocks\n ),\n pending_blocks AS (\n SELECT\n b.*\n FROM\n blocks AS b,\n max_canonical_height AS m\n WHERE\n b.height>m.max_height\n AND b.chain_status='pending'\n ),\n blocks AS (\n SELECT\n *\n FROM\n canonical_blocks\n UNION ALL\n SELECT\n *\n FROM\n pending_blocks\n ),\n zkapp_commands_info AS (\n SELECT\n zc.id,\n zc.memo,\n zc.hash,\n pk_fee_payer.value AS fee_payer,\n pk_update_body.value AS pk_update_body,\n zfpb.fee,\n zfpb.valid_until,\n zfpb.nonce,\n bzc.sequence_no,\n bzc.status AS \"status: TransactionStatus\",\n zaub.account_identifier_id,\n zaub.update_id,\n zaub.balance_change,\n zaub.increment_nonce,\n zaub.events_id,\n zaub.actions_id,\n zaub.call_data_id,\n zaub.call_depth,\n zaub.zkapp_network_precondition_id,\n zaub.zkapp_account_precondition_id,\n zaub.zkapp_valid_while_precondition_id,\n zaub.use_full_commitment,\n zaub.implicit_account_creation_fee,\n zaub.may_use_token AS \"may_use_token: MayUseToken\",\n zaub.authorization_kind AS \"authorization_kind: AuthorizationKindType\",\n zaub.verification_key_hash_id,\n bzc.block_id,\n b.state_hash,\n b.height,\n ARRAY(\n SELECT\n unnest(zauf.failures)\n FROM\n zkapp_account_update_failures AS zauf\n WHERE\n zauf.id=ANY (bzc.failure_reasons_ids)\n ) AS failure_reasons\n FROM\n zkapp_commands AS zc\n INNER JOIN blocks_zkapp_commands AS bzc ON zc.id=bzc.zkapp_command_id\n INNER JOIN zkapp_fee_payer_body AS zfpb ON zc.zkapp_fee_payer_body_id=zfpb.id\n INNER JOIN public_keys AS pk_fee_payer ON zfpb.public_key_id=pk_fee_payer.id\n INNER JOIN blocks AS b ON bzc.block_id=b.id\n LEFT JOIN zkapp_account_update AS zau ON zau.id=ANY (zc.zkapp_account_updates_ids)\n INNER JOIN zkapp_account_update_body AS zaub ON zau.body_id=zaub.id\n INNER JOIN account_identifiers AS ai_update_body ON zaub.account_identifier_id=ai_update_body.id\n INNER JOIN public_keys AS pk_update_body ON ai_update_body.public_key_id=pk_update_body.id\n INNER JOIN tokens AS token_update_body ON ai_update_body.token_id=token_update_body.id\n WHERE\n (\n $1>=b.height\n OR $1 IS NULL\n )\n AND (\n $2=zc.hash\n OR $2 IS NULL\n )\n AND (\n (\n (\n $3=pk_fee_payer.value\n AND $4=''\n )\n OR (\n $3=pk_update_body.value\n AND $4=token_update_body.value\n )\n )\n OR (\n $3 IS NULL\n AND $4 IS NULL\n )\n )\n AND (\n $5=bzc.status\n OR $5 IS NULL\n )\n AND (\n $6=bzc.status\n OR $6 IS NULL\n )\n AND (\n (\n $7=pk_fee_payer.value\n OR $7=pk_update_body.value\n )\n OR $7 IS NULL\n )\n ),\n zkapp_commands_ids AS (\n SELECT DISTINCT\n id,\n block_id,\n sequence_no\n FROM\n zkapp_commands_info\n ),\n id_count AS (\n SELECT\n count(*) AS total_count\n FROM\n zkapp_commands_ids\n )\nSELECT\n zc.*,\n id_count.total_count\nFROM\n id_count,\n (\n SELECT\n *\n FROM\n zkapp_commands_ids\n ORDER BY\n block_id,\n id,\n sequence_no\n LIMIT\n $8\n OFFSET\n $9\n ) AS ids\n INNER JOIN zkapp_commands_info AS zc ON ids.id=zc.id\n AND ids.block_id=zc.block_id\n AND ids.sequence_no=zc.sequence_no\nORDER BY\n ids.block_id,\n ids.id,\n ids.sequence_no\n", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "id", + "type_info": "Int4" + }, + { + "ordinal": 1, + "name": "memo", + "type_info": "Text" + }, + { + "ordinal": 2, + "name": "hash", + "type_info": "Text" + }, + { + "ordinal": 3, + "name": "fee_payer", + "type_info": "Text" + }, + { + "ordinal": 4, + "name": "pk_update_body", + "type_info": "Text" + }, + { + "ordinal": 5, + "name": "fee", + "type_info": "Text" + }, + { + "ordinal": 6, + "name": "valid_until", + "type_info": "Int8" + }, + { + "ordinal": 7, + "name": "nonce", + "type_info": "Int8" + }, + { + "ordinal": 8, + "name": "sequence_no", + "type_info": "Int4" + }, + { + "ordinal": 9, + "name": "status: TransactionStatus", + "type_info": { + "Custom": { + "name": "transaction_status", + "kind": { + "Enum": [ + "applied", + "failed" + ] + } + } + } + }, + { + "ordinal": 10, + "name": "account_identifier_id", + "type_info": "Int4" + }, + { + "ordinal": 11, + "name": "update_id", + "type_info": "Int4" + }, + { + "ordinal": 12, + "name": "balance_change", + "type_info": "Text" + }, + { + "ordinal": 13, + "name": "increment_nonce", + "type_info": "Bool" + }, + { + "ordinal": 14, + "name": "events_id", + "type_info": "Int4" + }, + { + "ordinal": 15, + "name": "actions_id", + "type_info": "Int4" + }, + { + "ordinal": 16, + "name": "call_data_id", + "type_info": "Int4" + }, + { + "ordinal": 17, + "name": "call_depth", + "type_info": "Int4" + }, + { + "ordinal": 18, + "name": "zkapp_network_precondition_id", + "type_info": "Int4" + }, + { + "ordinal": 19, + "name": "zkapp_account_precondition_id", + "type_info": "Int4" + }, + { + "ordinal": 20, + "name": "zkapp_valid_while_precondition_id", + "type_info": "Int4" + }, + { + "ordinal": 21, + "name": "use_full_commitment", + "type_info": "Bool" + }, + { + "ordinal": 22, + "name": "implicit_account_creation_fee", + "type_info": "Bool" + }, + { + "ordinal": 23, + "name": "may_use_token: MayUseToken", + "type_info": { + "Custom": { + "name": "may_use_token", + "kind": { + "Enum": [ + "No", + "ParentsOwnToken", + "InheritFromParent" + ] + } + } + } + }, + { + "ordinal": 24, + "name": "authorization_kind: AuthorizationKindType", + "type_info": { + "Custom": { + "name": "authorization_kind_type", + "kind": { + "Enum": [ + "None_given", + "Signature", + "Proof" + ] + } + } + } + }, + { + "ordinal": 25, + "name": "verification_key_hash_id", + "type_info": "Int4" + }, + { + "ordinal": 26, + "name": "block_id", + "type_info": "Int4" + }, + { + "ordinal": 27, + "name": "state_hash", + "type_info": "Text" + }, + { + "ordinal": 28, + "name": "height", + "type_info": "Int8" + }, + { + "ordinal": 29, + "name": "failure_reasons", + "type_info": "TextArray" + }, + { + "ordinal": 30, + "name": "total_count", + "type_info": "Int8" + } + ], + "parameters": { + "Left": [ + "Int8", + "Text", + "Text", + "Text", + { + "Custom": { + "name": "transaction_status", + "kind": { + "Enum": [ + "applied", + "failed" + ] + } + } + }, + { + "Custom": { + "name": "transaction_status", + "kind": { + "Enum": [ + "applied", + "failed" + ] + } + } + }, + "Text", + "Int8", + "Int8" + ] + }, + "nullable": [ + false, + false, + false, + false, + false, + false, + true, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + true, + false, + false, + false, + false, + true, + false, + null, + null, + null, + null + ] + }, + "hash": "56485f1d902b775a7d9702dfe3da13b35f3ec2f47bdccd481116f663d3a5c3bc" +} diff --git a/sql/indexer_zkapp_commands.sql b/sql/indexer_zkapp_commands.sql index 1bcabcc..9fd3d9e 100644 --- a/sql/indexer_zkapp_commands.sql +++ b/sql/indexer_zkapp_commands.sql @@ -40,11 +40,12 @@ WITH zc.memo, zc.hash, pk_fee_payer.value AS fee_payer, + pk_update_body.value AS pk_update_body, zfpb.fee, zfpb.valid_until, zfpb.nonce, bzc.sequence_no, - bzc.status, + bzc.status AS "status: TransactionStatus", zaub.account_identifier_id, zaub.update_id, zaub.balance_change, @@ -58,8 +59,8 @@ WITH zaub.zkapp_valid_while_precondition_id, zaub.use_full_commitment, zaub.implicit_account_creation_fee, - zaub.may_use_token, - zaub.authorization_kind, + zaub.may_use_token AS "may_use_token: MayUseToken", + zaub.authorization_kind AS "authorization_kind: AuthorizationKindType", zaub.verification_key_hash_id, bzc.block_id, b.state_hash, @@ -153,7 +154,9 @@ FROM id, sequence_no LIMIT - 5 + $8 + OFFSET + $9 ) AS ids INNER JOIN zkapp_commands_info AS zc ON ids.id=zc.id AND ids.block_id=zc.block_id diff --git a/src/api/search_transactions.rs b/src/api/search_transactions.rs index b33222c..8babf40 100644 --- a/src/api/search_transactions.rs +++ b/src/api/search_transactions.rs @@ -1,14 +1,16 @@ +use std::collections::HashMap; + use coinbase_mesh::models::{ - AccountIdentifier, BlockIdentifier, BlockTransaction, SearchTransactionsRequest, SearchTransactionsResponse, - Transaction, TransactionIdentifier, + AccountIdentifier, BlockIdentifier, BlockTransaction, Operation, SearchTransactionsRequest, + SearchTransactionsResponse, Transaction, TransactionIdentifier, }; use convert_case::{Case, Casing}; -use serde_json::json; +use serde_json::{json, Map, Value}; use sqlx::FromRow; use crate::{ - operation, util::DEFAULT_TOKEN_ID, ChainStatus, InternalCommandType, MinaMesh, MinaMeshError, OperationType, - TransactionStatus, UserCommandType, + operation, util::DEFAULT_TOKEN_ID, AuthorizationKindType, ChainStatus, InternalCommandType, MayUseToken, MinaMesh, + MinaMeshError, OperationType, TransactionStatus, UserCommandType, }; impl MinaMesh { @@ -48,6 +50,23 @@ impl MinaMesh { total_count += internal_commands_total_count; } + // ZkApp Commands + if limit > total_count { + // if we are below the limit, fetch zkapp commands + (offset, limit) = adjust_limit_and_offset(limit, offset, txs_len); + let zkapp_commands = self.fetch_zkapp_commands(&req, offset, limit).await?; + let zkapp_commands_len = zkapp_commands.len() as i64; + let zkapp_commands_total_count = zkapp_commands.first().and_then(|ic| ic.total_count).unwrap_or(0); + transactions.extend(zkapp_commands_to_block_transactions(zkapp_commands)); + txs_len += zkapp_commands_len; + total_count += zkapp_commands_total_count; + } else { + // otherwise only fetch the first zkapp command to get the total count + let zkapp_commands = self.fetch_zkapp_commands(&req, 0, 1).await?; + let zkapp_commands_total_count = zkapp_commands.first().and_then(|ic| ic.total_count).unwrap_or(0); + total_count += zkapp_commands_total_count; + } + let next_offset = original_offset + txs_len; let response = SearchTransactionsResponse { transactions, @@ -112,15 +131,138 @@ impl MinaMesh { Ok(internal_commands) } - #[allow(dead_code)] async fn fetch_zkapp_commands( &self, - _req: &SearchTransactionsRequest, - ) -> Result, MinaMeshError> { - unimplemented!() + req: &SearchTransactionsRequest, + offset: i64, + limit: i64, + ) -> Result, MinaMeshError> { + let query_params = SearchTransactionsQueryParams::try_from(req.clone())?; + + let zkapp_commands = sqlx::query_file_as!( + ZkAppCommand, + "sql/indexer_zkapp_commands.sql", + query_params.max_block, + query_params.transaction_hash, + query_params.account_identifier, + query_params.token_id, + query_params.status as Option, + query_params.success_status as Option, + query_params.address, + limit, + offset + ) + .fetch_all(&self.pg_pool) + .await?; + + Ok(zkapp_commands) } } +#[allow(dead_code)] +#[derive(FromRow)] +pub struct ZkAppCommand { + pub id: Option, + pub memo: Option, + pub hash: String, + pub fee_payer: String, + pub pk_update_body: String, + pub fee: Option, + pub valid_until: Option, + pub nonce: Option, + pub sequence_no: i32, + pub status: TransactionStatus, + pub account_identifier_id: Option, + pub update_id: Option, + pub balance_change: Option, + pub increment_nonce: bool, + pub events_id: Option, + pub actions_id: Option, + pub call_data_id: Option, + pub call_depth: Option, + pub zkapp_network_precondition_id: Option, + pub zkapp_account_precondition_id: Option, + pub zkapp_valid_while_precondition_id: Option, + pub use_full_commitment: bool, + pub implicit_account_creation_fee: bool, + pub may_use_token: MayUseToken, + pub authorization_kind: AuthorizationKindType, + pub verification_key_hash_id: Option, + pub block_id: Option, + pub state_hash: Option, + pub height: Option, + pub failure_reasons: Option>, + pub total_count: Option, +} + +pub fn zkapp_commands_to_block_transactions(commands: Vec) -> Vec { + let mut block_map: HashMap<(i64, String), HashMap>> = HashMap::new(); + + for command in commands { + // Group by block identifier (block index and block hash) + let block_key = (command.height.unwrap_or(0), command.state_hash.clone().unwrap_or_default()); + let tx_hash = command.hash.clone(); + + // Initialize or update the operation list for this transaction + let operations = block_map.entry(block_key).or_default().entry(tx_hash.clone()).or_default(); + + // Add fee operation (zkapp_fee_payer_dec) + if operations.is_empty() { + operations.push(operation( + 0, + Some(&format!("-{}", command.fee.unwrap_or("0".to_string()))), + &AccountIdentifier { + address: command.fee_payer.clone(), + metadata: Some(json!({ "token_id": DEFAULT_TOKEN_ID })), + sub_account: None, + }, + OperationType::ZkappFeePayerDec, + Some(&TransactionStatus::Applied), + None, + None, + )); + } + + // Add zkapp balance update operation + operations.push(operation( + 0, + command.balance_change.as_ref(), + &AccountIdentifier { + address: command.pk_update_body.clone(), + metadata: Some(json!({ "token_id": DEFAULT_TOKEN_ID })), + sub_account: None, + }, + OperationType::ZkappBalanceUpdate, + Some(&command.status), + None, + None, + )); + } + + let mut result = Vec::new(); + for ((block_index, block_hash), tx_map) in block_map { + for (tx_hash, mut operations) in tx_map { + // Ensure the operations are correctly indexed + for (i, operation) in operations.iter_mut().enumerate() { + operation.operation_identifier.index = i as i64; + } + + let transaction = BlockTransaction { + block_identifier: Box::new(BlockIdentifier { index: block_index, hash: block_hash.clone() }), + transaction: Box::new(Transaction { + transaction_identifier: Box::new(TransactionIdentifier { hash: tx_hash.clone() }), + operations, + metadata: None, + related_transactions: None, + }), + }; + result.push(transaction); + } + } + + result +} + #[derive(Debug, FromRow)] pub struct InternalCommand { pub id: Option, @@ -304,18 +446,35 @@ impl From for BlockTransaction { sub_account: None, }; + // Construct operations_metadata + let mut operations_metadata = Map::new(); + if let Some(failure_reason) = user_command.failure_reason.clone() { + operations_metadata.insert("reason".to_string(), json!(failure_reason)); + } + let operations_metadata_value = + if operations_metadata.is_empty() { None } else { Some(Value::Object(operations_metadata)) }; + + // Construct transaction metadata + let mut transaction_metadata = Map::new(); + transaction_metadata.insert("nonce".to_string(), json!(user_command.nonce)); + if !decoded_memo.is_empty() { + transaction_metadata.insert("memo".to_string(), json!(decoded_memo)); + } + let transaction_metadata_value = + if transaction_metadata.is_empty() { None } else { Some(Value::Object(transaction_metadata)) }; + let mut operations = Vec::new(); let mut operation_index = 0; // Operation 1: Fee Payment operations.push(operation( operation_index, - Some(&format!("-{}", user_command.fee.unwrap_or_else(|| "0".to_string()))), + Some(&format!("-{}", user_command.fee.unwrap_or("0".to_string()))), fee_payer_account_id, OperationType::FeePayment, - Some(&user_command.status), - None, + Some(&TransactionStatus::Applied), None, + operations_metadata_value.as_ref(), )); operation_index += 1; @@ -329,7 +488,7 @@ impl From for BlockTransaction { OperationType::AccountCreationFeeViaPayment, Some(&user_command.status), None, - None, + operations_metadata_value.as_ref(), )); operation_index += 1; @@ -346,7 +505,7 @@ impl From for BlockTransaction { OperationType::PaymentSourceDec, Some(&user_command.status), None, - None, + operations_metadata_value.as_ref(), )); operation_index += 1; @@ -359,7 +518,7 @@ impl From for BlockTransaction { OperationType::PaymentReceiverInc, Some(&user_command.status), Some(vec![operation_index - 1]), - None, + operations_metadata_value.as_ref(), )); } @@ -372,7 +531,7 @@ impl From for BlockTransaction { OperationType::DelegateChange, Some(&user_command.status), None, - Some(json!({ "delegate_change_target": user_command.receiver })), + Some(&json!({ "delegate_change_target": user_command.receiver })), )); } } @@ -383,10 +542,7 @@ impl From for BlockTransaction { transaction_identifier: Box::new(TransactionIdentifier::new(user_command.hash)), operations, related_transactions: None, - metadata: match decoded_memo.as_str() { - "" => None, - _ => Some(json!({ "memo": decoded_memo })), - }, + metadata: transaction_metadata_value, }; BlockTransaction::new(block_identifier, transaction) } diff --git a/src/error.rs b/src/error.rs index ffd6047..cb4886a 100644 --- a/src/error.rs +++ b/src/error.rs @@ -7,7 +7,7 @@ use thiserror::Error; #[derive(Error, Debug, PartialEq)] pub enum MinaMeshError { - #[error("SQL failure")] + #[error("SQL failure: {0}")] Sql(String), #[error("JSON parse error")] diff --git a/src/operation.rs b/src/operation.rs index d56999e..cbcf625 100644 --- a/src/operation.rs +++ b/src/operation.rs @@ -10,7 +10,7 @@ pub fn operation( operation_type: OperationType, status: Option<&TransactionStatus>, related_operations: Option>, - metadata: Option, + metadata: Option<&serde_json::Value>, ) -> Operation { Operation { operation_identifier: Box::new(OperationIdentifier::new(ident)), @@ -23,6 +23,6 @@ pub fn operation( .map(|items| items.iter().map(|item| OperationIdentifier::new(item.to_owned())).collect()), coin_change: None, r#type: operation_type.to_string().to_case(Case::Snake), - metadata, + metadata: metadata.cloned(), } } diff --git a/src/types.rs b/src/types.rs index 1322802..14a1d96 100644 --- a/src/types.rs +++ b/src/types.rs @@ -64,3 +64,22 @@ pub enum OperationType { ZkappFeePayerDec, ZkappBalanceUpdate, } + +#[derive(Type, Debug, PartialEq, Eq, Serialize, Display)] +#[sqlx(type_name = "may_use_token")] +pub enum MayUseToken { + No, + ParentsOwnToken, + InheritFromParent, +} + +#[derive(Type, Debug, PartialEq, Eq, Serialize, Display)] +#[sqlx(type_name = "authorization_kind_type")] +pub enum AuthorizationKindType { + #[sqlx(rename = "None_given")] + NoneGiven, + #[sqlx(rename = "Signature")] + Signature, + #[sqlx(rename = "Proof")] + Proof, +} diff --git a/tests/search_transactions.rs b/tests/search_transactions.rs index 7f2ede8..97cd69b 100644 --- a/tests/search_transactions.rs +++ b/tests/search_transactions.rs @@ -11,9 +11,8 @@ async fn search_transactions_specified() -> Result<()> { let request = SearchTransactionsRequest { network_identifier: Box::new(NetworkIdentifier::new("mina".to_string(), "mainnet".to_string())), - // cspell:disable + // cspell:disable-next-line address: Some("B62qkd6yYALkQMq2SFd5B57bJbGBMA2QuGtLPMzRhhnvexRtVRycZWP".to_string()), - // cspell:enable limit: Some(5), offset: Some(0), ..Default::default() @@ -66,3 +65,45 @@ async fn search_transactions_internal_command() -> Result<()> { assert_debug_snapshot!(response); Ok(()) } + +#[tokio::test] +async fn search_transactions_zkapp_success() -> Result<()> { + let mina_mesh = MinaMeshConfig::from_env().to_mina_mesh().await?; + + let request = SearchTransactionsRequest { + network_identifier: Box::new(NetworkIdentifier::new("mina".to_string(), "testnet".to_string())), + transaction_identifier: Some(Box::new(TransactionIdentifier::new( + // cspell:disable-next-line + "5JvFoEyvuPu9zmi4bDGbhqsakre2SPQU1KKbeh2Lk5uC9eYrc2h2".to_string(), + ))), + limit: Some(1), + ..Default::default() + }; + + let response = mina_mesh.search_transactions(request).await; + + assert!(response.is_ok()); + assert_debug_snapshot!(response); + Ok(()) +} + +#[tokio::test] +async fn search_transactions_zkapp_failed() -> Result<()> { + let mina_mesh = MinaMeshConfig::from_env().to_mina_mesh().await?; + + let request = SearchTransactionsRequest { + network_identifier: Box::new(NetworkIdentifier::new("mina".to_string(), "testnet".to_string())), + transaction_identifier: Some(Box::new(TransactionIdentifier::new( + // cspell:disable-next-line + "5JujBt8rnKheA7CHBnTwUDXrHtQxqPB9LL5Q8y4KwLjPBsBSJuSE".to_string(), + ))), + limit: Some(3), + ..Default::default() + }; + + let response = mina_mesh.search_transactions(request).await; + + assert!(response.is_ok()); + assert_debug_snapshot!(response); + Ok(()) +} diff --git a/tests/snapshots/search_transactions__search_transactions_failed.snap b/tests/snapshots/search_transactions__search_transactions_failed.snap index ae3f596..d4b3418 100644 --- a/tests/snapshots/search_transactions__search_transactions_failed.snap +++ b/tests/snapshots/search_transactions__search_transactions_failed.snap @@ -23,7 +23,7 @@ Ok( related_operations: None, type: "fee_payment", status: Some( - "Failed", + "Success", ), account: Some( AccountIdentifier { @@ -48,7 +48,11 @@ Ok( }, ), coin_change: None, - metadata: None, + metadata: Some( + Object { + "reason": String("Amount_insufficient_to_create_account"), + }, + ), }, Operation { operation_identifier: OperationIdentifier { @@ -83,7 +87,11 @@ Ok( }, ), coin_change: None, - metadata: None, + metadata: Some( + Object { + "reason": String("Amount_insufficient_to_create_account"), + }, + ), }, Operation { operation_identifier: OperationIdentifier { @@ -125,13 +133,18 @@ Ok( }, ), coin_change: None, - metadata: None, + metadata: Some( + Object { + "reason": String("Amount_insufficient_to_create_account"), + }, + ), }, ], related_transactions: None, metadata: Some( Object { "memo": String("BeepBoop"), + "nonce": Number(0), }, ), }, @@ -154,7 +167,7 @@ Ok( related_operations: None, type: "fee_payment", status: Some( - "Failed", + "Success", ), account: Some( AccountIdentifier { @@ -179,7 +192,11 @@ Ok( }, ), coin_change: None, - metadata: None, + metadata: Some( + Object { + "reason": String("Amount_insufficient_to_create_account"), + }, + ), }, Operation { operation_identifier: OperationIdentifier { @@ -214,7 +231,11 @@ Ok( }, ), coin_change: None, - metadata: None, + metadata: Some( + Object { + "reason": String("Amount_insufficient_to_create_account"), + }, + ), }, Operation { operation_identifier: OperationIdentifier { @@ -256,13 +277,18 @@ Ok( }, ), coin_change: None, - metadata: None, + metadata: Some( + Object { + "reason": String("Amount_insufficient_to_create_account"), + }, + ), }, ], related_transactions: None, metadata: Some( Object { "memo": String("BeepBoop"), + "nonce": Number(1), }, ), }, @@ -285,7 +311,7 @@ Ok( related_operations: None, type: "fee_payment", status: Some( - "Failed", + "Success", ), account: Some( AccountIdentifier { @@ -310,7 +336,11 @@ Ok( }, ), coin_change: None, - metadata: None, + metadata: Some( + Object { + "reason": String("Amount_insufficient_to_create_account"), + }, + ), }, Operation { operation_identifier: OperationIdentifier { @@ -345,7 +375,11 @@ Ok( }, ), coin_change: None, - metadata: None, + metadata: Some( + Object { + "reason": String("Amount_insufficient_to_create_account"), + }, + ), }, Operation { operation_identifier: OperationIdentifier { @@ -387,13 +421,18 @@ Ok( }, ), coin_change: None, - metadata: None, + metadata: Some( + Object { + "reason": String("Amount_insufficient_to_create_account"), + }, + ), }, ], related_transactions: None, metadata: Some( Object { "memo": String("BeepBoop"), + "nonce": Number(2), }, ), }, @@ -416,7 +455,7 @@ Ok( related_operations: None, type: "fee_payment", status: Some( - "Failed", + "Success", ), account: Some( AccountIdentifier { @@ -441,7 +480,11 @@ Ok( }, ), coin_change: None, - metadata: None, + metadata: Some( + Object { + "reason": String("Amount_insufficient_to_create_account"), + }, + ), }, Operation { operation_identifier: OperationIdentifier { @@ -476,7 +519,11 @@ Ok( }, ), coin_change: None, - metadata: None, + metadata: Some( + Object { + "reason": String("Amount_insufficient_to_create_account"), + }, + ), }, Operation { operation_identifier: OperationIdentifier { @@ -518,13 +565,18 @@ Ok( }, ), coin_change: None, - metadata: None, + metadata: Some( + Object { + "reason": String("Amount_insufficient_to_create_account"), + }, + ), }, ], related_transactions: None, metadata: Some( Object { "memo": String("BeepBoop"), + "nonce": Number(0), }, ), }, @@ -547,7 +599,7 @@ Ok( related_operations: None, type: "fee_payment", status: Some( - "Failed", + "Success", ), account: Some( AccountIdentifier { @@ -572,7 +624,11 @@ Ok( }, ), coin_change: None, - metadata: None, + metadata: Some( + Object { + "reason": String("Amount_insufficient_to_create_account"), + }, + ), }, Operation { operation_identifier: OperationIdentifier { @@ -607,7 +663,11 @@ Ok( }, ), coin_change: None, - metadata: None, + metadata: Some( + Object { + "reason": String("Amount_insufficient_to_create_account"), + }, + ), }, Operation { operation_identifier: OperationIdentifier { @@ -649,13 +709,18 @@ Ok( }, ), coin_change: None, - metadata: None, + metadata: Some( + Object { + "reason": String("Amount_insufficient_to_create_account"), + }, + ), }, ], related_transactions: None, metadata: Some( Object { "memo": String("BeepBoop"), + "nonce": Number(1), }, ), }, diff --git a/tests/snapshots/search_transactions__search_transactions_specified.snap b/tests/snapshots/search_transactions__search_transactions_specified.snap index cd08d48..0853263 100644 --- a/tests/snapshots/search_transactions__search_transactions_specified.snap +++ b/tests/snapshots/search_transactions__search_transactions_specified.snap @@ -164,7 +164,11 @@ Ok( }, ], related_transactions: None, - metadata: None, + metadata: Some( + Object { + "nonce": Number(61), + }, + ), }, }, BlockTransaction { @@ -291,7 +295,11 @@ Ok( }, ], related_transactions: None, - metadata: None, + metadata: Some( + Object { + "nonce": Number(0), + }, + ), }, }, BlockTransaction { @@ -418,7 +426,11 @@ Ok( }, ], related_transactions: None, - metadata: None, + metadata: Some( + Object { + "nonce": Number(62), + }, + ), }, }, BlockTransaction { @@ -497,7 +509,11 @@ Ok( }, ], related_transactions: None, - metadata: None, + metadata: Some( + Object { + "nonce": Number(1), + }, + ), }, }, BlockTransaction { @@ -576,7 +592,11 @@ Ok( }, ], related_transactions: None, - metadata: None, + metadata: Some( + Object { + "nonce": Number(2), + }, + ), }, }, ], diff --git a/tests/snapshots/search_transactions__search_transactions_zkapp_failed.snap b/tests/snapshots/search_transactions__search_transactions_zkapp_failed.snap new file mode 100644 index 0000000..f5aa899 --- /dev/null +++ b/tests/snapshots/search_transactions__search_transactions_zkapp_failed.snap @@ -0,0 +1,252 @@ +--- +source: tests/search_transactions.rs +expression: response +--- +Ok( + SearchTransactionsResponse { + transactions: [ + BlockTransaction { + block_identifier: BlockIdentifier { + index: 356949, + hash: "3NLViPD1oW4XgMsSXsorkhphgREkBfNgAfu2EGFTKSH5EUV9YDNe", + }, + transaction: Transaction { + transaction_identifier: TransactionIdentifier { + hash: "5JujBt8rnKheA7CHBnTwUDXrHtQxqPB9LL5Q8y4KwLjPBsBSJuSE", + }, + operations: [ + Operation { + operation_identifier: OperationIdentifier { + index: 0, + network_index: None, + }, + related_operations: None, + type: "zkapp_fee_payer_dec", + status: Some( + "Success", + ), + account: Some( + AccountIdentifier { + address: "B62qjGsPY47SMkTykivPBAU3riS9gvMMrGr7ve6ynoHJNBzAhQmtoBn", + sub_account: None, + metadata: Some( + Object { + "token_id": String("wSHV2S4qX9jFsLjQo8r1BsMLH2ZRKsZx6EJd1sbozGPieEC4Jf"), + }, + ), + }, + ), + amount: Some( + Amount { + value: "-100000000", + currency: Currency { + symbol: "MINA", + decimals: 9, + metadata: None, + }, + metadata: None, + }, + ), + coin_change: None, + metadata: None, + }, + Operation { + operation_identifier: OperationIdentifier { + index: 1, + network_index: None, + }, + related_operations: None, + type: "zkapp_balance_update", + status: Some( + "Failed", + ), + account: Some( + AccountIdentifier { + address: "B62qide3tEVFgzLFo72rhrF4p8Luv6R5NAmvwo83SnZGnwV9Q6fiVTK", + sub_account: None, + metadata: Some( + Object { + "token_id": String("wSHV2S4qX9jFsLjQo8r1BsMLH2ZRKsZx6EJd1sbozGPieEC4Jf"), + }, + ), + }, + ), + amount: Some( + Amount { + value: "0", + currency: Currency { + symbol: "MINA", + decimals: 9, + metadata: None, + }, + metadata: None, + }, + ), + coin_change: None, + metadata: None, + }, + Operation { + operation_identifier: OperationIdentifier { + index: 2, + network_index: None, + }, + related_operations: None, + type: "zkapp_balance_update", + status: Some( + "Failed", + ), + account: Some( + AccountIdentifier { + address: "B62qjGsPY47SMkTykivPBAU3riS9gvMMrGr7ve6ynoHJNBzAhQmtoBn", + sub_account: None, + metadata: Some( + Object { + "token_id": String("wSHV2S4qX9jFsLjQo8r1BsMLH2ZRKsZx6EJd1sbozGPieEC4Jf"), + }, + ), + }, + ), + amount: Some( + Amount { + value: "0", + currency: Currency { + symbol: "MINA", + decimals: 9, + metadata: None, + }, + metadata: None, + }, + ), + coin_change: None, + metadata: None, + }, + ], + related_transactions: None, + metadata: None, + }, + }, + BlockTransaction { + block_identifier: BlockIdentifier { + index: 356949, + hash: "3NKL6D7kcvH28odK9QpCqvKuHjx2jm2BhfKjPVkvLLyAoKKMD3JF", + }, + transaction: Transaction { + transaction_identifier: TransactionIdentifier { + hash: "5JujBt8rnKheA7CHBnTwUDXrHtQxqPB9LL5Q8y4KwLjPBsBSJuSE", + }, + operations: [ + Operation { + operation_identifier: OperationIdentifier { + index: 0, + network_index: None, + }, + related_operations: None, + type: "zkapp_fee_payer_dec", + status: Some( + "Success", + ), + account: Some( + AccountIdentifier { + address: "B62qjGsPY47SMkTykivPBAU3riS9gvMMrGr7ve6ynoHJNBzAhQmtoBn", + sub_account: None, + metadata: Some( + Object { + "token_id": String("wSHV2S4qX9jFsLjQo8r1BsMLH2ZRKsZx6EJd1sbozGPieEC4Jf"), + }, + ), + }, + ), + amount: Some( + Amount { + value: "-100000000", + currency: Currency { + symbol: "MINA", + decimals: 9, + metadata: None, + }, + metadata: None, + }, + ), + coin_change: None, + metadata: None, + }, + Operation { + operation_identifier: OperationIdentifier { + index: 1, + network_index: None, + }, + related_operations: None, + type: "zkapp_balance_update", + status: Some( + "Failed", + ), + account: Some( + AccountIdentifier { + address: "B62qide3tEVFgzLFo72rhrF4p8Luv6R5NAmvwo83SnZGnwV9Q6fiVTK", + sub_account: None, + metadata: Some( + Object { + "token_id": String("wSHV2S4qX9jFsLjQo8r1BsMLH2ZRKsZx6EJd1sbozGPieEC4Jf"), + }, + ), + }, + ), + amount: Some( + Amount { + value: "0", + currency: Currency { + symbol: "MINA", + decimals: 9, + metadata: None, + }, + metadata: None, + }, + ), + coin_change: None, + metadata: None, + }, + Operation { + operation_identifier: OperationIdentifier { + index: 2, + network_index: None, + }, + related_operations: None, + type: "zkapp_balance_update", + status: Some( + "Failed", + ), + account: Some( + AccountIdentifier { + address: "B62qjGsPY47SMkTykivPBAU3riS9gvMMrGr7ve6ynoHJNBzAhQmtoBn", + sub_account: None, + metadata: Some( + Object { + "token_id": String("wSHV2S4qX9jFsLjQo8r1BsMLH2ZRKsZx6EJd1sbozGPieEC4Jf"), + }, + ), + }, + ), + amount: Some( + Amount { + value: "0", + currency: Currency { + symbol: "MINA", + decimals: 9, + metadata: None, + }, + metadata: None, + }, + ), + coin_change: None, + metadata: None, + }, + ], + related_transactions: None, + metadata: None, + }, + }, + ], + total_count: 2, + next_offset: None, + }, +) diff --git a/tests/snapshots/search_transactions__search_transactions_zkapp_success.snap b/tests/snapshots/search_transactions__search_transactions_zkapp_success.snap new file mode 100644 index 0000000..fdbc122 --- /dev/null +++ b/tests/snapshots/search_transactions__search_transactions_zkapp_success.snap @@ -0,0 +1,132 @@ +--- +source: tests/search_transactions.rs +expression: response +--- +Ok( + SearchTransactionsResponse { + transactions: [ + BlockTransaction { + block_identifier: BlockIdentifier { + index: 337818, + hash: "3NKdrqcGAwSgfK6T8az4cauXExHs7D9eKC6Ywotm8qEeudPLxepG", + }, + transaction: Transaction { + transaction_identifier: TransactionIdentifier { + hash: "5JvFoEyvuPu9zmi4bDGbhqsakre2SPQU1KKbeh2Lk5uC9eYrc2h2", + }, + operations: [ + Operation { + operation_identifier: OperationIdentifier { + index: 0, + network_index: None, + }, + related_operations: None, + type: "zkapp_fee_payer_dec", + status: Some( + "Success", + ), + account: Some( + AccountIdentifier { + address: "B62qjGsPY47SMkTykivPBAU3riS9gvMMrGr7ve6ynoHJNBzAhQmtoBn", + sub_account: None, + metadata: Some( + Object { + "token_id": String("wSHV2S4qX9jFsLjQo8r1BsMLH2ZRKsZx6EJd1sbozGPieEC4Jf"), + }, + ), + }, + ), + amount: Some( + Amount { + value: "-10000000", + currency: Currency { + symbol: "MINA", + decimals: 9, + metadata: None, + }, + metadata: None, + }, + ), + coin_change: None, + metadata: None, + }, + Operation { + operation_identifier: OperationIdentifier { + index: 1, + network_index: None, + }, + related_operations: None, + type: "zkapp_balance_update", + status: Some( + "Success", + ), + account: Some( + AccountIdentifier { + address: "B62qqieAv9AQCsLHfinPESXB9exSJBpTVLpgkjqWT9cSGBc67pBkNxs", + sub_account: None, + metadata: Some( + Object { + "token_id": String("wSHV2S4qX9jFsLjQo8r1BsMLH2ZRKsZx6EJd1sbozGPieEC4Jf"), + }, + ), + }, + ), + amount: Some( + Amount { + value: "0", + currency: Currency { + symbol: "MINA", + decimals: 9, + metadata: None, + }, + metadata: None, + }, + ), + coin_change: None, + metadata: None, + }, + Operation { + operation_identifier: OperationIdentifier { + index: 2, + network_index: None, + }, + related_operations: None, + type: "zkapp_balance_update", + status: Some( + "Success", + ), + account: Some( + AccountIdentifier { + address: "B62qjGsPY47SMkTykivPBAU3riS9gvMMrGr7ve6ynoHJNBzAhQmtoBn", + sub_account: None, + metadata: Some( + Object { + "token_id": String("wSHV2S4qX9jFsLjQo8r1BsMLH2ZRKsZx6EJd1sbozGPieEC4Jf"), + }, + ), + }, + ), + amount: Some( + Amount { + value: "0", + currency: Currency { + symbol: "MINA", + decimals: 9, + metadata: None, + }, + metadata: None, + }, + ), + coin_change: None, + metadata: None, + }, + ], + related_transactions: None, + metadata: None, + }, + }, + ], + total_count: 1, + next_offset: None, + }, +)