From d5f6ea6814e562a744a99156e2df0e3d7d2ea74b Mon Sep 17 00:00:00 2001 From: Alexander Polakov Date: Mon, 12 Feb 2024 15:23:14 +0400 Subject: [PATCH 1/4] feat: return best guess about txs --- parse/src/lib.rs | 44 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 42 insertions(+), 2 deletions(-) diff --git a/parse/src/lib.rs b/parse/src/lib.rs index 52690d64..130eee9e 100644 --- a/parse/src/lib.rs +++ b/parse/src/lib.rs @@ -1,5 +1,6 @@ use thiserror::Error; +use common::evm_loader::types::Address; use common::solana_sdk::signature::Signature; use common::types::{NeonTxInfo, SolanaTransaction}; @@ -38,14 +39,53 @@ pub fn parse(transaction: SolanaTransaction) -> Result, Error> { let _meta_info = SolTxMetaInfo { ident: sig_slot_info, }; - for ix in tx.message.instructions() { + let mut txs = Vec::new(); + for (idx, ix) in tx.message.instructions().iter().enumerate() { let neon_tx = transaction::parse(&ix.data)?; tracing::info!("neon tx {:?}", neon_tx); + if let Some(neon_tx) = neon_tx { + txs.push((idx, neon_tx)); + } } let log_info = match log::parse(transaction.log_messages) { Ok(log) => log, Err(err) => panic!("log parsing error {:?}", err), }; tracing::info!("log info {:?}", log_info); - Ok(Vec::new()) + let mut tx_infos = Vec::new(); + for (idx, tx) in txs { + let tx_info = NeonTxInfo { + tx_type: 0, // TODO + neon_signature: log_info.sig.map(hex::encode).unwrap_or_default(), // TODO + from: Address::default(), // TODO + contract: None, // TODO + transaction: tx, + events: log_info.event_list.clone(), // TODO + gas_used: log_info + .ret + .as_ref() + .map(|r| r.gas_used) + .unwrap_or_default(), // TODO + sum_gas_used: log_info + .ix + .as_ref() + .map(|i| i.total_gas_used) + .unwrap_or_default(), // TODO: + // unclear what this is + sol_signature: String::default(), // TODO: should be in input? + sol_slot: transaction.slot, + sol_tx_idx: transaction.tx_idx, + sol_ix_idx: idx as u64, + sol_ix_inner_idx: 0, // TODO: what is this? + status: log_info.ret.as_ref().map(|r| r.status).unwrap_or_default(), // TODO + is_cancelled: log_info + .ret + .as_ref() + .map(|r| r.is_canceled) + .unwrap_or_default(), // TODO + is_completed: false, // TODO: ??? + }; + tx_infos.push(tx_info); + } + Ok(tx_infos) } From 5ccc6cecd18a4ffd89ddd1f57c5cc7d1176a91ba Mon Sep 17 00:00:00 2001 From: Alexander Polakov Date: Mon, 12 Feb 2024 17:34:21 +0400 Subject: [PATCH 2/4] chore: add test for reference decoding from neon proxy --- parse/src/lib.rs | 171 +++++++++++++++--- parse/src/log.rs | 16 +- ...6iUSjtLrtNn5QZh54bz5brWMonccG7WHA4Wp5.json | 29 +++ 3 files changed, 189 insertions(+), 27 deletions(-) create mode 100644 parse/tests/data/reference/2FSmsnCJYenPWsbrK1vFpkhgeFGKXC13gB3wDwqqyUfEnZmWpEJ6iUSjtLrtNn5QZh54bz5brWMonccG7WHA4Wp5.json diff --git a/parse/src/lib.rs b/parse/src/lib.rs index 130eee9e..c3810dde 100644 --- a/parse/src/lib.rs +++ b/parse/src/lib.rs @@ -1,9 +1,12 @@ +use common::solana_sdk::transaction::VersionedTransaction; use thiserror::Error; -use common::evm_loader::types::Address; +use common::evm_loader::types::{Address, Transaction}; use common::solana_sdk::signature::Signature; use common::types::{NeonTxInfo, SolanaTransaction}; +use self::log::NeonLogInfo; + mod log; mod transaction; @@ -30,15 +33,7 @@ struct SolTxMetaInfo { pub ident: SolTxSigSlotInfo, } -pub fn parse(transaction: SolanaTransaction) -> Result, Error> { - let SolanaTransaction { slot, tx, .. } = transaction; - let sig_slot_info = SolTxSigSlotInfo { - signature: tx.signatures[0], - block_slot: slot, - }; - let _meta_info = SolTxMetaInfo { - ident: sig_slot_info, - }; +fn parse_transactions(tx: VersionedTransaction) -> Result, Error> { let mut txs = Vec::new(); for (idx, ix) in tx.message.instructions().iter().enumerate() { let neon_tx = transaction::parse(&ix.data)?; @@ -47,13 +42,23 @@ pub fn parse(transaction: SolanaTransaction) -> Result, Error> { txs.push((idx, neon_tx)); } } - let log_info = match log::parse(transaction.log_messages) { - Ok(log) => log, - Err(err) => panic!("log parsing error {:?}", err), - }; - tracing::info!("log info {:?}", log_info); + Ok(txs) +} + +fn merge_logs_transactions( + txs: Vec<(usize, Transaction)>, + log_info: NeonLogInfo, + slot: u64, + tx_idx: u64, +) -> Vec { let mut tx_infos = Vec::new(); for (idx, tx) in txs { + let canceled = log_info + .ret + .as_ref() + .map(|r| r.is_canceled) + .unwrap_or_default(); // TODO + let tx_info = NeonTxInfo { tx_type: 0, // TODO neon_signature: log_info.sig.map(hex::encode).unwrap_or_default(), // TODO @@ -70,22 +75,136 @@ pub fn parse(transaction: SolanaTransaction) -> Result, Error> { .ix .as_ref() .map(|i| i.total_gas_used) - .unwrap_or_default(), // TODO: - // unclear what this is - sol_signature: String::default(), // TODO: should be in input? - sol_slot: transaction.slot, - sol_tx_idx: transaction.tx_idx, + .unwrap_or_default(), // TODO: unclear what this is + sol_signature: String::default(), // TODO: should be in input? + sol_slot: slot, + sol_tx_idx: tx_idx, sol_ix_idx: idx as u64, sol_ix_inner_idx: 0, // TODO: what is this? status: log_info.ret.as_ref().map(|r| r.status).unwrap_or_default(), // TODO - is_cancelled: log_info - .ret - .as_ref() - .map(|r| r.is_canceled) - .unwrap_or_default(), // TODO - is_completed: false, // TODO: ??? + is_cancelled: canceled, + is_completed: !canceled, // TODO: ??? }; tx_infos.push(tx_info); } + tx_infos +} + +pub fn parse(transaction: SolanaTransaction) -> Result, Error> { + let SolanaTransaction { + slot, tx, tx_idx, .. + } = transaction; + let sig_slot_info = SolTxSigSlotInfo { + signature: tx.signatures[0], + block_slot: slot, + }; + let _meta_info = SolTxMetaInfo { + ident: sig_slot_info, + }; + let neon_txs = parse_transactions(tx)?; + + let log_info = match log::parse(transaction.log_messages) { + Ok(log) => log, + Err(err) => panic!("log parsing error {:?}", err), + }; + let tx_infos = merge_logs_transactions(neon_txs, log_info, slot, tx_idx); Ok(tx_infos) } + +#[cfg(test)] +mod tests { + use super::*; + use common::solana_transaction_status::EncodedTransactionWithStatusMeta; + use serde::Deserialize; + use std::collections::HashMap; + use test_log::test; + + #[allow(dead_code)] + #[derive(Debug, Deserialize)] + struct ReferenceRow { + neon_sig: String, + tx_type: u8, + from_addr: String, + sol_sig: String, + sol_ix_idx: u64, + sol_ix_inner_idx: Option, + block_slot: u64, + tx_idx: u64, + nonce: String, + gas_price: String, + gas_limit: String, + value: String, + gas_used: String, + sum_gas_used: String, + to_addr: String, + contract: Option, + status: String, + is_canceled: bool, + is_completed: bool, + } + + type Reference = HashMap>; + + #[test] + fn parse_2f() { + let transaction_path = "tests/data/2FSmsnCJYenPWsbrK1vFpkhgeFGKXC13gB3wDwqqyUfEnZmWpEJ6iUSjtLrtNn5QZh54bz5brWMonccG7WHA4Wp5.json"; + let reference_path = "tests/data/reference/2FSmsnCJYenPWsbrK1vFpkhgeFGKXC13gB3wDwqqyUfEnZmWpEJ6iUSjtLrtNn5QZh54bz5brWMonccG7WHA4Wp5.json"; + + let encoded: EncodedTransactionWithStatusMeta = + serde_json::from_str(&std::fs::read_to_string(transaction_path).unwrap()).unwrap(); + let tx = encoded.transaction.decode().unwrap(); + let neon_txs = parse_transactions(tx).unwrap(); + let logs = match encoded.meta.unwrap().log_messages { + common::solana_transaction_status::option_serializer::OptionSerializer::Some(logs) => { + logs + } + _ => panic!("no logs"), + }; + let logs = log::parse(logs).unwrap(); + let neon_tx_infos = merge_logs_transactions(neon_txs, logs, 276140928, 3); + let references: Vec<_> = + serde_json::from_str::(&std::fs::read_to_string(reference_path).unwrap()) + .unwrap() + .into_values() + .flatten() + .collect(); + assert_eq!(neon_tx_infos.len(), references.len()); + for (info, refr) in neon_tx_infos.iter().zip(references) { + let neon_sig = format!("0x{}", info.neon_signature); + assert_eq!(refr.neon_sig, neon_sig); + assert_eq!(refr.tx_type, info.tx_type); + // fails as we don't set from address + //assert_eq!(refr.from_addr, format!("0x{}", info.from)); + // fails as we don't set signature + //assert_eq!(refr.sol_sig, info.sol_signature); + assert_eq!(refr.sol_ix_idx, info.sol_ix_idx); + // fails as we don't set inner index + //assert_eq!(refr.sol_ix_inner_idx, Some(info.sol_ix_inner_idx)); + assert_eq!(refr.block_slot, info.sol_slot); + assert_eq!(refr.tx_idx, info.sol_tx_idx); + assert_eq!(refr.nonce, format!("{:#0x}", info.transaction.nonce())); + assert_eq!( + refr.gas_price, + format!("{:#0x}", info.transaction.gas_price()) + ); + assert_eq!( + refr.gas_limit, + format!("{:#0x}", info.transaction.gas_limit()) + ); + assert_eq!(refr.value, format!("{:#0x}", info.transaction.value())); + assert_eq!(refr.gas_used, format!("{:#0x}", info.gas_used)); + // fails for unknown reason + //assert_eq!(refr.sum_gas_used, format!("{:#0x}", info.sum_gas_used)); + // fails for unknown reason + // assert_eq!( + // refr.to_addr, + // format!("0x{}", info.transaction.target().unwrap()) + // ); + assert_eq!(refr.contract, info.contract.map(|c| format!("0x{}", c))); + // fails for unknown reason + //assert_eq!(refr.status, format!("{:#0x}", info.status)); + assert_eq!(refr.is_canceled, info.is_cancelled); + assert_eq!(refr.is_completed, info.is_completed); + } + } +} diff --git a/parse/src/log.rs b/parse/src/log.rs index 5c1de61a..168bd936 100644 --- a/parse/src/log.rs +++ b/parse/src/log.rs @@ -60,6 +60,7 @@ pub struct NeonLogTxIx { #[derive(Debug, Copy, Clone)] enum Mnemonic { + Miner, Hash, Return, Log(u8), @@ -271,6 +272,11 @@ impl Mnemonic { is_canceled: false, }) } + + // TODO + fn decode_miner(_input: &str) { + warn!("miner opcode not implemented yet") + } } #[derive(Debug, Error)] @@ -283,6 +289,7 @@ impl FromStr for Mnemonic { fn from_str(s: &str) -> Result { match s { "HASH" => Ok(Mnemonic::Hash), + "MINER" => Ok(Mnemonic::Miner), "RETURN" => Ok(Mnemonic::Return), "ENTER" => Ok(Mnemonic::Enter), "EXIT" => Ok(Mnemonic::Exit), @@ -292,7 +299,10 @@ impl FromStr for Mnemonic { let n = n.parse().map_err(|_| BadMnemonic)?; Ok(Mnemonic::Log(n)) } - _ => Err(BadMnemonic), + s => { + println!("Invalid mnemonic {}", s); + Err(BadMnemonic) + } } } } @@ -363,6 +373,10 @@ pub fn parse(lines: impl IntoIterator>) -> Result { + // TODO + Mnemonic::decode_miner(rest); + } } } } diff --git a/parse/tests/data/reference/2FSmsnCJYenPWsbrK1vFpkhgeFGKXC13gB3wDwqqyUfEnZmWpEJ6iUSjtLrtNn5QZh54bz5brWMonccG7WHA4Wp5.json b/parse/tests/data/reference/2FSmsnCJYenPWsbrK1vFpkhgeFGKXC13gB3wDwqqyUfEnZmWpEJ6iUSjtLrtNn5QZh54bz5brWMonccG7WHA4Wp5.json new file mode 100644 index 00000000..05edc205 --- /dev/null +++ b/parse/tests/data/reference/2FSmsnCJYenPWsbrK1vFpkhgeFGKXC13gB3wDwqqyUfEnZmWpEJ6iUSjtLrtNn5QZh54bz5brWMonccG7WHA4Wp5.json @@ -0,0 +1,29 @@ +{ +"SELECT neon_sig, tx_type, from_addr, sol_sig, sol_ix_idx, sol_ix_inner_idx, block_slot, tx_idx, nonce, gas_price, gas_limit, value, gas_used, sum_gas_used, to_addr, contract, status, is_canceled, is_completed, v, r, s, calldata, logs\nFROM public.neon_transactions where sol_sig = '2FSmsnCJYenPWsbrK1vFpkhgeFGKXC13gB3wDwqqyUfEnZmWpEJ6iUSjtLrtNn5QZh54bz5brWMonccG7WHA4Wp5' limit 5": [ + { + "neon_sig" : "0xc6a0f130f273b774b1725b058a0f9be2b2486816542cf53a7e426220f7b613d1", + "tx_type" : 0, + "from_addr" : "0x9115cb3a0415731e921635fcbd2b6dfbe6f0995e", + "sol_sig" : "2FSmsnCJYenPWsbrK1vFpkhgeFGKXC13gB3wDwqqyUfEnZmWpEJ6iUSjtLrtNn5QZh54bz5brWMonccG7WHA4Wp5", + "sol_ix_idx" : 2, + "sol_ix_inner_idx" : null, + "block_slot" : 276140928, + "tx_idx" : 3, + "nonce" : "0xe3e", + "gas_price" : "0x122e18d945", + "gas_limit" : "0x61a8", + "value" : "0x695b1a0d000", + "gas_used" : "0x2710", + "sum_gas_used" : "0x9c40", + "to_addr" : "0x0543a5311319ea73b867c39c33c4d9ba5d6e517b", + "contract" : null, + "status" : "0x1", + "is_canceled" : false, + "is_completed" : true, + "v" : "0x1d3581bf", + "r" : "0xd0b4714cb793966390c1f52ee45b3311a0e604b05125254a23bf15fccd12f7b3", + "s" : "0x72312df0810b665af85901aa8a0e3a0f1babfcad3772605940ec57be80a97f3b", + "calldata" : "0xfe1e0a70edc32599c9c63e6ffe1b0c4faa20e0dafb4e55576fac4c109b4a7bd0", + "logs" : [{"event_type": 101, "is_hidden": true, "address": "0x0543a5311319ea73b867c39c33c4d9ba5d6e517b", "topic_list": [], "data": "0x", "sol_sig": "2FSmsnCJYenPWsbrK1vFpkhgeFGKXC13gB3wDwqqyUfEnZmWpEJ6iUSjtLrtNn5QZh54bz5brWMonccG7WHA4Wp5", "idx": 2, "inner_idx": null, "total_gas_used": 10000, "is_reverted": false, "event_level": 1, "event_order": 1, "neon_sig": "0xc6a0f130f273b774b1725b058a0f9be2b2486816542cf53a7e426220f7b613d1", "block_hash": "0x4b40b04fb905d2a39070fc15cbe94d972d511090534098b43b26b51b76e3d9aa", "block_slot": 276140928, "neon_tx_idx": 3, "block_log_idx": null, "neon_tx_log_idx": null}, {"event_type": 201, "is_hidden": true, "address": "0x0543a5311319ea73b867c39c33c4d9ba5d6e517b", "topic_list": [], "data": "0x", "sol_sig": "2FSmsnCJYenPWsbrK1vFpkhgeFGKXC13gB3wDwqqyUfEnZmWpEJ6iUSjtLrtNn5QZh54bz5brWMonccG7WHA4Wp5", "idx": 2, "inner_idx": null, "total_gas_used": 10001, "is_reverted": false, "event_level": 1, "event_order": 2, "neon_sig": "0xc6a0f130f273b774b1725b058a0f9be2b2486816542cf53a7e426220f7b613d1", "block_hash": "0x4b40b04fb905d2a39070fc15cbe94d972d511090534098b43b26b51b76e3d9aa", "block_slot": 276140928, "neon_tx_idx": 3, "block_log_idx": null, "neon_tx_log_idx": null}, {"event_type": 300, "is_hidden": true, "address": "0x", "topic_list": [], "data": "0x01", "sol_sig": "2FSmsnCJYenPWsbrK1vFpkhgeFGKXC13gB3wDwqqyUfEnZmWpEJ6iUSjtLrtNn5QZh54bz5brWMonccG7WHA4Wp5", "idx": 2, "inner_idx": null, "total_gas_used": 15000, "is_reverted": false, "event_level": 0, "event_order": 3, "neon_sig": "0xc6a0f130f273b774b1725b058a0f9be2b2486816542cf53a7e426220f7b613d1", "block_hash": "0x4b40b04fb905d2a39070fc15cbe94d972d511090534098b43b26b51b76e3d9aa", "block_slot": 276140928, "neon_tx_idx": 3, "block_log_idx": null, "neon_tx_log_idx": null}] + } +]} From 490c99b8830d2cbf6d5e03bd081ae974c7303f1b Mon Sep 17 00:00:00 2001 From: Alexander Polakov Date: Mon, 12 Feb 2024 18:17:00 +0400 Subject: [PATCH 3/4] feat: properly fill event list --- common/src/types.rs | 25 +++++++++++++++++++ parse/src/lib.rs | 59 +++++++++++++++++++++++++++++++++++++++++++++ parse/src/log.rs | 32 ++++++++++++++++++++++++ 3 files changed, 116 insertions(+) diff --git a/common/src/types.rs b/common/src/types.rs index e2f4d2e9..655211bd 100644 --- a/common/src/types.rs +++ b/common/src/types.rs @@ -78,6 +78,7 @@ pub struct NeonTxInfo { /// Event kinds can be logged to solana transaction logs. #[derive(Debug, Clone, Copy)] +#[repr(u32)] pub enum EventKind { Log = 1, @@ -101,6 +102,30 @@ pub enum EventKind { Cancel = 301, } +impl EventKind { + pub fn is_exit(&self) -> bool { + matches!( + self, + EventKind::ExitStop + | EventKind::ExitReturn + | EventKind::ExitSelfDestruct + | EventKind::ExitRevert + ) + } + + pub fn is_start(&self) -> bool { + matches!( + self, + EventKind::EnterCall + | EventKind::EnterCallCode + | EventKind::EnterStaticCall + | EventKind::EnterDelegateCall + | EventKind::EnterCreate + | EventKind::EnterCreate2 + ) + } +} + // ===== Alternative Event ===== // #[derive(Debug, Clone)] // #[repr(u8)] diff --git a/parse/src/lib.rs b/parse/src/lib.rs index c3810dde..76f53a19 100644 --- a/parse/src/lib.rs +++ b/parse/src/lib.rs @@ -141,10 +141,61 @@ mod tests { status: String, is_canceled: bool, is_completed: bool, + v: String, + r: String, + s: String, + calldata: String, + logs: Vec, + } + + #[derive(Debug, Deserialize)] + struct ReferenceEvent { + event_type: u32, + is_hidden: bool, + address: String, + topic_list: Vec, + data: String, + sol_sig: String, + idx: u64, + inner_idx: Option, + total_gas_used: u64, + is_reverted: bool, + event_level: u64, + event_order: u64, + neon_sig: String, + block_hash: String, + block_slot: u64, + neon_tx_idx: u64, + block_log_idx: Option, + neon_tx_log_idx: Option, } type Reference = HashMap>; + fn check_events(ref_logs: &[ReferenceEvent], my_logs: &[common::types::EventLog]) { + for (ref_log, my_log) in ref_logs.iter().zip(my_logs.iter()) { + println!("event type {:?} {}", my_log.event_type, ref_log.event_type); + assert_eq!(ref_log.event_type, my_log.event_type as u32); + assert_eq!(ref_log.is_hidden, my_log.is_hidden); + assert_eq!(ref_log.event_level, my_log.level); + assert_eq!(ref_log.event_order, my_log.order); + + assert_eq!( + ref_log.address, + my_log.address.map(|a| a.to_string()).unwrap_or_default() + ); + assert_eq!( + ref_log.topic_list, + my_log + .topic_list + .iter() + .map(|t| format!("0x{}", t)) + .collect::>() + ); + assert_eq!(ref_log.data, format!("0x{}", hex::encode(&my_log.data))); + } + } + #[test] fn parse_2f() { let transaction_path = "tests/data/2FSmsnCJYenPWsbrK1vFpkhgeFGKXC13gB3wDwqqyUfEnZmWpEJ6iUSjtLrtNn5QZh54bz5brWMonccG7WHA4Wp5.json"; @@ -205,6 +256,14 @@ mod tests { //assert_eq!(refr.status, format!("{:#0x}", info.status)); assert_eq!(refr.is_canceled, info.is_cancelled); assert_eq!(refr.is_completed, info.is_completed); + + // TODO: we don't have v,r,s fields for some reason? + + assert_eq!( + refr.calldata, + format!("0x{}", hex::encode(info.transaction.call_data())) + ); + check_events(&refr.logs, &info.events); } } } diff --git a/parse/src/log.rs b/parse/src/log.rs index 168bd936..46e45e09 100644 --- a/parse/src/log.rs +++ b/parse/src/log.rs @@ -380,6 +380,38 @@ pub fn parse(lines: impl IntoIterator>) -> Result Date: Mon, 12 Feb 2024 18:48:40 +0400 Subject: [PATCH 4/4] chore: forgot to add file --- ...gB3wDwqqyUfEnZmWpEJ6iUSjtLrtNn5QZh54bz5brWMonccG7WHA4Wp5.json | 1 + 1 file changed, 1 insertion(+) create mode 100644 parse/tests/data/2FSmsnCJYenPWsbrK1vFpkhgeFGKXC13gB3wDwqqyUfEnZmWpEJ6iUSjtLrtNn5QZh54bz5brWMonccG7WHA4Wp5.json diff --git a/parse/tests/data/2FSmsnCJYenPWsbrK1vFpkhgeFGKXC13gB3wDwqqyUfEnZmWpEJ6iUSjtLrtNn5QZh54bz5brWMonccG7WHA4Wp5.json b/parse/tests/data/2FSmsnCJYenPWsbrK1vFpkhgeFGKXC13gB3wDwqqyUfEnZmWpEJ6iUSjtLrtNn5QZh54bz5brWMonccG7WHA4Wp5.json new file mode 100644 index 00000000..035ac102 --- /dev/null +++ b/parse/tests/data/2FSmsnCJYenPWsbrK1vFpkhgeFGKXC13gB3wDwqqyUfEnZmWpEJ6iUSjtLrtNn5QZh54bz5brWMonccG7WHA4Wp5.json @@ -0,0 +1 @@ +{"blockTime":1706592175,"meta":{"computeUnitsConsumed":92092,"err":null,"fee":5000,"innerInstructions":[{"index":2,"instructions":[{"accounts":[0,2],"data":"3Bxs4PckVVt51W8w","programIdIndex":5,"stackHeight":2}]}],"loadedAddresses":{"readonly":[],"writable":[]},"logMessages":["Program ComputeBudget111111111111111111111111111111 invoke [1]","Program ComputeBudget111111111111111111111111111111 success","Program ComputeBudget111111111111111111111111111111 invoke [1]","Program ComputeBudget111111111111111111111111111111 success","Program eeLSJgWzzxrqKv1UxtRVVH8FX3qCQWUs9QuAjJpETGU invoke [1]","Program log: Instruction: Execute Transaction from Instruction","Program data: SEFTSA== xqDxMPJzt3SxclsFig+b4rJIaBZULPU6fkJiIPe2E9E=","Program data: TUlORVI= 9ohQV7C9v4wbdLR91d1fK/hkAcc=","Program data: RU5URVI= Q0FMTA== BUOlMRMZ6nO4Z8OcM8TZul1uUXs=","Program data: RVhJVA== U1RPUA==","Program 11111111111111111111111111111111 invoke [2]","Program 11111111111111111111111111111111 success","Program data: R0FT ECcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA= ECcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=","Program log: exit_status=0x11","Program data: UkVUVVJO EQ==","Program eeLSJgWzzxrqKv1UxtRVVH8FX3qCQWUs9QuAjJpETGU consumed 91736 of 1399700 compute units","Program eeLSJgWzzxrqKv1UxtRVVH8FX3qCQWUs9QuAjJpETGU success"],"postBalances":[2134989240,1378080,757150880,1378080,1378080,1,1,1141440,34911360],"postTokenBalances":[],"preBalances":[2134999240,1378080,757145880,1378080,1378080,1,1,1141440,34911360],"preTokenBalances":[],"rewards":[],"status":{"Ok":null}},"slot":276140928,"transaction":["AT55KMj0PTzjD72PR0klsjwEwLR6SkQUAZirH14uALMtK0mygcBVZAlMB//XgU1/yaDhoX/2hJCVRpTNCJSO6wYBAAQJJalKmpZXwhy8e2gAB88mdD30or9ZZsQtrDlpBXTN8GI3RouEAA9+9KzHJY/DnxVVJXnngVcLLFi6OTvKqofNiUbCxNc83ByGAxR105h8w4WCERGlm9eDmfZmrxBhYG2Vj+13Ha4gxD8sJ6wYsJYlIqWtlU1HWI/QCVFQ0pEFvVTBt9dhN7H2HVrOVuLT47hD/kz0xB/uquJRkmfKrZy3+QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwZGb+UhFzL/7K26csOb57yM5bvF9xJrLEObOkAAAAAJpLRy2fLFNxdeUmvu7aq2doyAgA7b9ztEEPSKkdZRwbj9ty/ydffLtYK4hD2iPCOeBGETp5viUn4/4P9qunOImVIwMtA7CeK9oxfWJ9yL64zwdIATQewgHxBVnFJw1XQDBgAFAQAABAAGAAUCwFwVAAcHAAIEBQEDCJcBMkYAAAD4kIIOPoUSLhjZRYJhqJQFQ6UxExnqc7hnw5wzxNm6XW5Re4YGlbGg0ACg/h4KcO3DJZnJxj5v/hsMT6og4Nr7TlVXb6xMEJtKe9CEHTWBv6DQtHFMt5OWY5DB9S7kWzMRoOYEsFElJUojvxX8zRL3s6ByMS3wgQtmWvhZAaqKDjoPG6v8rTdyYFlA7Fe+gKl/Ow==","base64"]}