1use super::args;
2use crate::{
3 commands::global,
4 config::network,
5 rpc,
6 xdr::{self, Hash, SorobanTransactionMetaExt, TransactionEnvelope, TransactionMeta},
7};
8use clap::{command, Parser};
9use prettytable::{
10 format::{FormatBuilder, LinePosition, LineSeparator, TableFormat},
11 Cell, Row, Table,
12};
13use serde::{Deserialize, Serialize};
14use soroban_rpc::GetTransactionResponse;
15
16#[derive(Parser, Debug, Clone)]
17#[group(skip)]
18pub struct Cmd {
19 #[command(flatten)]
20 args: args::Args,
21
22 #[arg(long, default_value = "table")]
24 pub output: FeeOutputFormat,
25}
26
27#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, clap::ValueEnum, Default)]
28pub enum FeeOutputFormat {
29 Json,
31 JsonFormatted,
33 #[default]
35 Table,
36}
37
38#[derive(thiserror::Error, Debug)]
39pub enum Error {
40 #[error(transparent)]
41 Network(#[from] network::Error),
42 #[error(transparent)]
43 Serde(#[from] serde_json::Error),
44 #[error(transparent)]
45 Xdr(#[from] xdr::Error),
46 #[error(transparent)]
47 Args(#[from] args::Error),
48 #[error("{message}")]
49 NotSupported { message: String },
50 #[error("transaction {tx_hash} not found on {network} network")]
51 NotFound { tx_hash: Hash, network: String },
52 #[error(transparent)]
53 Rpc(#[from] rpc::Error),
54 #[error("{field} is None, expected it to be Some")]
55 None { field: String },
56}
57
58const DEFAULT_FEE_VALUE: i64 = 0;
59const FEE_CHARGED_TITLE: &str = "Fee Charged";
60const RESOURCE_FEE_TITLE: &str = "Resource Fee";
61const INCLUSION_FEE_TITLE: &str = "Inclusion Fee";
62const NON_REFUNDABLE_TITLE: &str = "Non-Refundable";
63const REFUNDABLE_TITLE: &str = "Refundable";
64const FEE_PROPOSED_TITLE: &str = "Fee Proposed";
65const REFUNDED_TITLE: &str = "Refunded";
66const NON_REFUNDABLE_COMPONENTS: &str = "\n\ncpu instructions\nstorage read/write\ntx size";
67const REFUNDABLE_COMPONENTS: &str = "\n\nreturn value\nstorage rent\nevents";
68
69impl Cmd {
70 pub async fn run(&self, global_args: &global::Args) -> Result<(), Error> {
71 let resp = self.args.fetch_transaction(global_args).await?;
72 let fee_table = FeeTable::new_from_transaction_response(&resp)?;
73 match self.output {
74 FeeOutputFormat::Json => {
75 println!("{}", serde_json::to_string(&fee_table)?);
76 }
77 FeeOutputFormat::JsonFormatted => {
78 println!("{}", serde_json::to_string_pretty(&fee_table)?);
79 }
80 FeeOutputFormat::Table => {
81 fee_table.print();
82 }
83 }
84
85 Ok(())
86 }
87}
88
89#[derive(Serialize, Deserialize, Debug)]
90pub struct FeeTable {
91 pub fee_charged: i64,
92 pub resource_fee_charged: i64,
93 pub inclusion_fee_charged: i64,
94 pub non_refundable_resource_fee_charged: i64,
95 pub refundable_resource_fee_charged: i64,
96 pub max_fee: i64,
97 pub max_resource_fee: i64,
98}
99
100impl FeeTable {
101 fn new_from_transaction_response(resp: &GetTransactionResponse) -> Result<Self, Error> {
102 let tx_result = resp.result.clone().ok_or(Error::None {
103 field: "tx_result".to_string(),
104 })?; let tx_meta = resp.result_meta.clone().ok_or(Error::None {
106 field: "tx_meta".to_string(),
107 })?; let tx_envelope = resp.envelope.clone().ok_or(Error::None {
109 field: "tx_envelope".to_string(),
110 })?; let fee_charged = tx_result.fee_charged;
113 let (non_refundable_resource_fee_charged, refundable_resource_fee_charged) =
114 Self::resource_fees_charged(&tx_meta);
115
116 let (max_fee, max_resource_fee) = Self::max_fees(&tx_envelope);
117
118 let resource_fee_charged =
119 non_refundable_resource_fee_charged + refundable_resource_fee_charged;
120 let inclusion_fee_charged = fee_charged - resource_fee_charged;
121 Ok(FeeTable {
122 fee_charged,
123 resource_fee_charged,
124 inclusion_fee_charged,
125 non_refundable_resource_fee_charged,
126 refundable_resource_fee_charged,
127 max_fee,
128 max_resource_fee,
129 })
130 }
131
132 fn max_fees(tx_envelope: &TransactionEnvelope) -> (i64, i64) {
133 match tx_envelope {
134 TransactionEnvelope::TxV0(transaction_v0_envelope) => {
135 let fee = transaction_v0_envelope.tx.fee;
136 (fee.into(), DEFAULT_FEE_VALUE)
137 }
138 TransactionEnvelope::Tx(transaction_v1_envelope) => {
139 let tx = transaction_v1_envelope.tx.clone();
140 let fee = tx.fee;
141 let resource_fee = match tx.ext {
142 xdr::TransactionExt::V0 => DEFAULT_FEE_VALUE,
143 xdr::TransactionExt::V1(soroban_transaction_data) => {
144 soroban_transaction_data.resource_fee
145 }
146 };
147
148 (fee.into(), resource_fee)
149 }
150 TransactionEnvelope::TxFeeBump(fee_bump_transaction_envelope) => {
151 let fee = fee_bump_transaction_envelope.tx.fee;
152 (fee, DEFAULT_FEE_VALUE)
153 }
154 }
155 }
156
157 fn resource_fees_charged(tx_meta: &TransactionMeta) -> (i64, i64) {
158 let (non_refundable_resource_fee_charged, refundable_resource_fee_charged) =
159 match tx_meta.clone() {
160 TransactionMeta::V0(_) | TransactionMeta::V1(_) | TransactionMeta::V2(_) => {
161 (DEFAULT_FEE_VALUE, DEFAULT_FEE_VALUE)
162 }
163 TransactionMeta::V3(meta) => {
164 if let Some(soroban_meta) = meta.soroban_meta {
165 match soroban_meta.ext {
166 SorobanTransactionMetaExt::V0 => (DEFAULT_FEE_VALUE, DEFAULT_FEE_VALUE),
167 SorobanTransactionMetaExt::V1(v1) => (
168 v1.total_non_refundable_resource_fee_charged,
169 v1.total_refundable_resource_fee_charged,
170 ),
171 }
172 } else {
173 (DEFAULT_FEE_VALUE, DEFAULT_FEE_VALUE)
174 }
175 }
176 TransactionMeta::V4(meta) => {
177 if let Some(soroban_meta) = meta.soroban_meta {
178 match soroban_meta.ext {
179 SorobanTransactionMetaExt::V0 => (DEFAULT_FEE_VALUE, DEFAULT_FEE_VALUE),
180 SorobanTransactionMetaExt::V1(v1) => (
181 v1.total_non_refundable_resource_fee_charged,
182 v1.total_refundable_resource_fee_charged,
183 ),
184 }
185 } else {
186 (DEFAULT_FEE_VALUE, DEFAULT_FEE_VALUE)
187 }
188 }
189 };
190
191 (
192 non_refundable_resource_fee_charged,
193 refundable_resource_fee_charged,
194 )
195 }
196
197 fn should_include_resource_fees(&self) -> bool {
198 self.resource_fee_charged != 0 || self.max_resource_fee != 0
199 }
200
201 fn proposed_inclusion_fee(&self) -> i64 {
202 self.max_fee - self.max_resource_fee
203 }
204
205 fn refunded(&self) -> i64 {
206 self.max_fee - self.fee_charged
207 }
208
209 fn refundable_fee_proposed(&self) -> i64 {
210 self.max_resource_fee - self.non_refundable_resource_fee_charged
211 }
212
213 fn table(&self) -> Table {
214 let mut table = Table::new();
215 table.set_format(Self::table_format());
216
217 table.add_row(Row::new(vec![Cell::new(&format!(
219 "{FEE_PROPOSED_TITLE}: {}",
220 self.max_fee
221 ))
222 .with_hspan(4)]));
223
224 table.add_row(Row::new(vec![
225 Cell::new(&format!(
226 "{}: {}",
227 INCLUSION_FEE_TITLE,
228 self.proposed_inclusion_fee()
229 )),
230 Cell::new(&format!("{RESOURCE_FEE_TITLE}: {}", self.max_resource_fee)).with_hspan(3),
231 ]));
232
233 table.add_row(Row::new(vec![
234 Cell::new(&format!(
235 "{}: {}",
236 INCLUSION_FEE_TITLE,
237 self.proposed_inclusion_fee()
238 )),
239 Cell::new(&format!(
240 "{NON_REFUNDABLE_TITLE}: {}{}",
241 self.non_refundable_resource_fee_charged, NON_REFUNDABLE_COMPONENTS
242 )),
243 Cell::new(&format!(
244 "{REFUNDABLE_TITLE}: {}{}",
245 self.refundable_fee_proposed(),
246 REFUNDABLE_COMPONENTS
247 ))
248 .with_hspan(2),
249 ]));
250
251 table.add_row(Row::new(vec![Cell::new("👆 Proposed Fee 👇 Final Fee")
253 .style_spec("c")
254 .with_hspan(4)]));
255
256 if self.should_include_resource_fees() {
258 table.add_row(Row::new(vec![
259 Cell::new(&format!(
260 "{INCLUSION_FEE_TITLE}: {}",
261 self.inclusion_fee_charged
262 )),
263 Cell::new(&format!(
264 "{NON_REFUNDABLE_TITLE}: {}",
265 self.non_refundable_resource_fee_charged
266 )),
267 Cell::new(&format!(
268 "{REFUNDABLE_TITLE}: {}",
269 self.refundable_resource_fee_charged
270 )),
271 Cell::new(&format!("{REFUNDED_TITLE}: {}", self.refunded())),
272 ]));
273
274 table.add_row(Row::new(vec![
275 Cell::new(&format!(
276 "{INCLUSION_FEE_TITLE}: {}",
277 self.inclusion_fee_charged
278 )),
279 Cell::new(&format!(
280 "{}: {}",
281 RESOURCE_FEE_TITLE, self.resource_fee_charged
282 ))
283 .with_hspan(2),
284 Cell::new(&format!("{REFUNDED_TITLE}: {}", self.refunded())),
285 ]));
286 }
287
288 table.add_row(Row::new(vec![
289 Cell::new(&format!("{FEE_CHARGED_TITLE}: {}", self.fee_charged)).with_hspan(3),
290 Cell::new(&format!("{REFUNDED_TITLE}: {}", self.refunded())),
291 ]));
292
293 table
294 }
295
296 fn print(&self) {
297 self.table().printstd();
298 }
299
300 fn table_format() -> TableFormat {
301 FormatBuilder::new()
302 .column_separator('│')
303 .borders('│')
304 .separators(&[LinePosition::Top], LineSeparator::new('─', '─', '┌', '┐'))
305 .separators(
306 &[LinePosition::Intern],
307 LineSeparator::new('─', '─', '├', '┤'),
308 )
309 .separators(
310 &[LinePosition::Bottom],
311 LineSeparator::new('─', '─', '└', '┘'),
312 )
313 .padding(1, 1)
314 .build()
315 }
316}
317
318#[cfg(test)]
319mod test {
320 use soroban_rpc::GetTransactionResponse;
321
322 use super::*;
323
324 #[test]
325 #[ignore]
326 fn soroban_tx_fee_table() {
327 let resp = soroban_tx_response().unwrap();
328 let fee_table = FeeTable::new_from_transaction_response(&resp).unwrap();
329
330 let expected_fee_charged = 185_119;
331 let expected_non_refundable_charged = 59_343;
332 let expected_refundable_charged = 125_676;
333 let expected_resource_fee_charged =
334 expected_non_refundable_charged + expected_refundable_charged;
335 let expected_inclusion_fee_charged = expected_fee_charged - expected_resource_fee_charged;
336 let expected_max_fee = 248_869;
337 let expected_max_resource_fee = 248_769;
338
339 assert_eq!(fee_table.fee_charged, expected_fee_charged);
340 assert_eq!(
341 fee_table.resource_fee_charged,
342 expected_resource_fee_charged
343 );
344 assert_eq!(
345 fee_table.non_refundable_resource_fee_charged,
346 expected_non_refundable_charged
347 );
348 assert_eq!(
349 fee_table.refundable_resource_fee_charged,
350 expected_refundable_charged
351 );
352 assert_eq!(
353 fee_table.inclusion_fee_charged,
354 expected_inclusion_fee_charged
355 );
356 assert_eq!(fee_table.max_fee, expected_max_fee);
357 assert_eq!(fee_table.max_resource_fee, expected_max_resource_fee);
358 }
359
360 #[test]
361 #[ignore]
362 fn classic_tx_fee_table() {
363 let resp = classic_tx_response().unwrap();
364 let fee_table = FeeTable::new_from_transaction_response(&resp).unwrap();
365
366 let expected_fee_charged = 100;
367 let expected_non_refundable_charged = DEFAULT_FEE_VALUE;
368 let expected_refundable_charged = DEFAULT_FEE_VALUE;
369 let expected_resource_fee_charged =
370 expected_non_refundable_charged + expected_refundable_charged;
371 let expected_inclusion_fee_charged = expected_fee_charged - expected_resource_fee_charged;
372 let expected_max_fee = 100;
373 let expected_max_resource_fee = DEFAULT_FEE_VALUE;
374
375 assert_eq!(fee_table.fee_charged, expected_fee_charged);
376 assert_eq!(
377 fee_table.resource_fee_charged,
378 expected_resource_fee_charged
379 );
380 assert_eq!(
381 fee_table.non_refundable_resource_fee_charged,
382 expected_non_refundable_charged
383 );
384 assert_eq!(
385 fee_table.refundable_resource_fee_charged,
386 expected_refundable_charged
387 );
388 assert_eq!(
389 fee_table.inclusion_fee_charged,
390 expected_inclusion_fee_charged
391 );
392 assert_eq!(fee_table.max_fee, expected_max_fee);
393 assert_eq!(fee_table.max_resource_fee, expected_max_resource_fee);
394 }
395
396 #[test]
397 #[ignore]
398 fn fee_bump_tx_fee_table() {
399 let resp = fee_bump_tx_response().unwrap();
400 let fee_table = FeeTable::new_from_transaction_response(&resp).unwrap();
401
402 let expected_fee_charged = 200;
403 let expected_non_refundable_charged = DEFAULT_FEE_VALUE;
404 let expected_refundable_charged = DEFAULT_FEE_VALUE;
405 let expected_resource_fee_charged =
406 expected_non_refundable_charged + expected_refundable_charged;
407 let expected_inclusion_fee_charged = expected_fee_charged - expected_resource_fee_charged;
408 let expected_max_fee = 400;
409 let expected_max_resource_fee = DEFAULT_FEE_VALUE;
410
411 assert_eq!(fee_table.fee_charged, expected_fee_charged);
412 assert_eq!(
413 fee_table.resource_fee_charged,
414 expected_resource_fee_charged
415 );
416 assert_eq!(
417 fee_table.non_refundable_resource_fee_charged,
418 expected_non_refundable_charged
419 );
420 assert_eq!(
421 fee_table.refundable_resource_fee_charged,
422 expected_refundable_charged
423 );
424 assert_eq!(
425 fee_table.inclusion_fee_charged,
426 expected_inclusion_fee_charged
427 );
428 assert_eq!(fee_table.max_fee, expected_max_fee);
429 assert_eq!(fee_table.max_resource_fee, expected_max_resource_fee);
430 }
431
432 fn soroban_tx_response() -> Result<GetTransactionResponse, serde_json::Error> {
433 serde_json::from_str(SOROBAN_TX_RESPONSE)
434 }
435
436 fn classic_tx_response() -> Result<GetTransactionResponse, serde_json::Error> {
437 serde_json::from_str(CLASSIC_TX_RESPONSE)
438 }
439
440 fn fee_bump_tx_response() -> Result<GetTransactionResponse, serde_json::Error> {
441 serde_json::from_str(FEE_BUMP_TX_RESPONSE)
442 }
443
444 const SOROBAN_TX_RESPONSE: &str = r#"{"status":"SUCCESS","envelope":{"tx":{"tx":{"source_account":"GCBVAIKUZELFVCV6S7KBS47SF2DQQXTN63TJM7D3CNZ7PD6RCDTBYULI","fee":248869,"seq_num":197568495619,"cond":"none","memo":"none","operations":[{"source_account":null,"body":{"invoke_host_function":{"host_function":{"invoke_contract":{"contract_address":"CDJJ2YDDNWVVY6AFSN2UFLMG33Z2IE2ZVYCLU4FFEAVCICLF62IXO44D","function_name":"inc","args":[]}},"auth":[]}}}],"ext":{"v1":{"ext":"v0","resources":{"footprint":{"read_only":[{"contract_data":{"contract":"CDJJ2YDDNWVVY6AFSN2UFLMG33Z2IE2ZVYCLU4FFEAVCICLF62IXO44D","key":"ledger_key_contract_instance","durability":"persistent"}},{"contract_code":{"hash":"e54e59e63d77364714a001d2c968e811d8eafe96b725781458fb8b21acf6d50e"}}],"read_write":[{"contract_data":{"contract":"CDJJ2YDDNWVVY6AFSN2UFLMG33Z2IE2ZVYCLU4FFEAVCICLF62IXO44D","key":{"symbol":"COUNTER"},"durability":"persistent"}}]},"instructions":2092625,"read_bytes":7928,"write_bytes":80},"resource_fee":248769}}},"signatures":[{"hint":"d110e61c","signature":"6b2d9fba82e01a84129582815c554f7b158ea678aeb0eceaa596f21640e1ee926441c2ffebb4eeba81ac48b3c021e8435b6b6c61071a61e498aadcb4e7a04b08"}]}},"result":{"fee_charged":185119,"result":{"tx_success":[{"op_inner":{"invoke_host_function":{"success":"df071a249d03fc2f22313f75c734a254bbea03124cea77001704db0670b2fc02"}}}]},"ext":"v0"},"result_meta":{"v3":{"ext":"v0","tx_changes_before":[{"state":{"last_modified_ledger_seq":54,"data":{"account":{"account_id":"GCBVAIKUZELFVCV6S7KBS47SF2DQQXTN63TJM7D3CNZ7PD6RCDTBYULI","balance":99988036662,"seq_num":197568495618,"num_sub_entries":0,"inflation_dest":null,"flags":0,"home_domain":"","thresholds":"01000000","signers":[],"ext":{"v1":{"liabilities":{"buying":0,"selling":0},"ext":{"v2":{"num_sponsored":0,"num_sponsoring":0,"signer_sponsoring_i_ds":[],"ext":{"v3":{"ext":"v0","seq_ledger":51,"seq_time":1750166268}}}}}}}},"ext":"v0"}},{"updated":{"last_modified_ledger_seq":54,"data":{"account":{"account_id":"GCBVAIKUZELFVCV6S7KBS47SF2DQQXTN63TJM7D3CNZ7PD6RCDTBYULI","balance":99988036662,"seq_num":197568495619,"num_sub_entries":0,"inflation_dest":null,"flags":0,"home_domain":"","thresholds":"01000000","signers":[],"ext":{"v1":{"liabilities":{"buying":0,"selling":0},"ext":{"v2":{"num_sponsored":0,"num_sponsoring":0,"signer_sponsoring_i_ds":[],"ext":{"v3":{"ext":"v0","seq_ledger":54,"seq_time":1750166271}}}}}}}},"ext":"v0"}}],"operations":[{"changes":[{"created":{"last_modified_ledger_seq":54,"data":{"ttl":{"key_hash":"5ac6e64993239b0eba43a13feecffb99ffdbd359624949f275cc0aa417fc75bd","live_until_ledger_seq":2073653}},"ext":"v0"}},{"created":{"last_modified_ledger_seq":54,"data":{"contract_data":{"ext":"v0","contract":"CDJJ2YDDNWVVY6AFSN2UFLMG33Z2IE2ZVYCLU4FFEAVCICLF62IXO44D","key":{"symbol":"COUNTER"},"durability":"persistent","val":{"u32":1}}},"ext":"v0"}}]}],"tx_changes_after":[{"state":{"last_modified_ledger_seq":54,"data":{"account":{"account_id":"GCBVAIKUZELFVCV6S7KBS47SF2DQQXTN63TJM7D3CNZ7PD6RCDTBYULI","balance":99988036662,"seq_num":197568495619,"num_sub_entries":0,"inflation_dest":null,"flags":0,"home_domain":"","thresholds":"01000000","signers":[],"ext":{"v1":{"liabilities":{"buying":0,"selling":0},"ext":{"v2":{"num_sponsored":0,"num_sponsoring":0,"signer_sponsoring_i_ds":[],"ext":{"v3":{"ext":"v0","seq_ledger":54,"seq_time":1750166271}}}}}}}},"ext":"v0"}},{"updated":{"last_modified_ledger_seq":54,"data":{"account":{"account_id":"GCBVAIKUZELFVCV6S7KBS47SF2DQQXTN63TJM7D3CNZ7PD6RCDTBYULI","balance":99988100412,"seq_num":197568495619,"num_sub_entries":0,"inflation_dest":null,"flags":0,"home_domain":"","thresholds":"01000000","signers":[],"ext":{"v1":{"liabilities":{"buying":0,"selling":0},"ext":{"v2":{"num_sponsored":0,"num_sponsoring":0,"signer_sponsoring_i_ds":[],"ext":{"v3":{"ext":"v0","seq_ledger":54,"seq_time":1750166271}}}}}}}},"ext":"v0"}}],"soroban_meta":{"ext":{"v1":{"ext":"v0","total_non_refundable_resource_fee_charged":59343,"total_refundable_resource_fee_charged":125676,"rent_fee_charged":125597}},"events":[],"return_value":{"u32":1},"diagnostic_events":[{"in_successful_contract_call":true,"event":{"ext":"v0","contract_id":null,"type_":"diagnostic","body":{"v0":{"topics":[{"symbol":"fn_call"},{"bytes":"d29d60636dab5c7805937542ad86def3a41359ae04ba70a5202a240965f69177"},{"symbol":"inc"}],"data":"void"}}}},{"in_successful_contract_call":true,"event":{"ext":"v0","contract_id":"d29d60636dab5c7805937542ad86def3a41359ae04ba70a5202a240965f69177","type_":"diagnostic","body":{"v0":{"topics":[{"symbol":"log"}],"data":{"vec":[{"string":"count: {}"},{"u32":0}]}}}}},{"in_successful_contract_call":true,"event":{"ext":"v0","contract_id":"d29d60636dab5c7805937542ad86def3a41359ae04ba70a5202a240965f69177","type_":"diagnostic","body":{"v0":{"topics":[{"symbol":"fn_return"},{"symbol":"inc"}],"data":{"u32":1}}}}},{"in_successful_contract_call":false,"event":{"ext":"v0","contract_id":null,"type_":"diagnostic","body":{"v0":{"topics":[{"symbol":"core_metrics"},{"symbol":"read_entry"}],"data":{"u64":3}}}}},{"in_successful_contract_call":false,"event":{"ext":"v0","contract_id":null,"type_":"diagnostic","body":{"v0":{"topics":[{"symbol":"core_metrics"},{"symbol":"write_entry"}],"data":{"u64":1}}}}},{"in_successful_contract_call":false,"event":{"ext":"v0","contract_id":null,"type_":"diagnostic","body":{"v0":{"topics":[{"symbol":"core_metrics"},{"symbol":"ledger_read_byte"}],"data":{"u64":7928}}}}},{"in_successful_contract_call":false,"event":{"ext":"v0","contract_id":null,"type_":"diagnostic","body":{"v0":{"topics":[{"symbol":"core_metrics"},{"symbol":"ledger_write_byte"}],"data":{"u64":80}}}}},{"in_successful_contract_call":false,"event":{"ext":"v0","contract_id":null,"type_":"diagnostic","body":{"v0":{"topics":[{"symbol":"core_metrics"},{"symbol":"read_key_byte"}],"data":{"u64":144}}}}},{"in_successful_contract_call":false,"event":{"ext":"v0","contract_id":null,"type_":"diagnostic","body":{"v0":{"topics":[{"symbol":"core_metrics"},{"symbol":"write_key_byte"}],"data":{"u64":0}}}}},{"in_successful_contract_call":false,"event":{"ext":"v0","contract_id":null,"type_":"diagnostic","body":{"v0":{"topics":[{"symbol":"core_metrics"},{"symbol":"read_data_byte"}],"data":{"u64":104}}}}},{"in_successful_contract_call":false,"event":{"ext":"v0","contract_id":null,"type_":"diagnostic","body":{"v0":{"topics":[{"symbol":"core_metrics"},{"symbol":"write_data_byte"}],"data":{"u64":80}}}}},{"in_successful_contract_call":false,"event":{"ext":"v0","contract_id":null,"type_":"diagnostic","body":{"v0":{"topics":[{"symbol":"core_metrics"},{"symbol":"read_code_byte"}],"data":{"u64":7824}}}}},{"in_successful_contract_call":false,"event":{"ext":"v0","contract_id":null,"type_":"diagnostic","body":{"v0":{"topics":[{"symbol":"core_metrics"},{"symbol":"write_code_byte"}],"data":{"u64":0}}}}},{"in_successful_contract_call":false,"event":{"ext":"v0","contract_id":null,"type_":"diagnostic","body":{"v0":{"topics":[{"symbol":"core_metrics"},{"symbol":"emit_event"}],"data":{"u64":0}}}}},{"in_successful_contract_call":false,"event":{"ext":"v0","contract_id":null,"type_":"diagnostic","body":{"v0":{"topics":[{"symbol":"core_metrics"},{"symbol":"emit_event_byte"}],"data":{"u64":0}}}}},{"in_successful_contract_call":false,"event":{"ext":"v0","contract_id":null,"type_":"diagnostic","body":{"v0":{"topics":[{"symbol":"core_metrics"},{"symbol":"cpu_insn"}],"data":{"u64":2000326}}}}},{"in_successful_contract_call":false,"event":{"ext":"v0","contract_id":null,"type_":"diagnostic","body":{"v0":{"topics":[{"symbol":"core_metrics"},{"symbol":"mem_byte"}],"data":{"u64":1487598}}}}},{"in_successful_contract_call":false,"event":{"ext":"v0","contract_id":null,"type_":"diagnostic","body":{"v0":{"topics":[{"symbol":"core_metrics"},{"symbol":"invoke_time_nsecs"}],"data":{"u64":158917}}}}},{"in_successful_contract_call":false,"event":{"ext":"v0","contract_id":null,"type_":"diagnostic","body":{"v0":{"topics":[{"symbol":"core_metrics"},{"symbol":"max_rw_key_byte"}],"data":{"u64":60}}}}},{"in_successful_contract_call":false,"event":{"ext":"v0","contract_id":null,"type_":"diagnostic","body":{"v0":{"topics":[{"symbol":"core_metrics"},{"symbol":"max_rw_data_byte"}],"data":{"u64":104}}}}},{"in_successful_contract_call":false,"event":{"ext":"v0","contract_id":null,"type_":"diagnostic","body":{"v0":{"topics":[{"symbol":"core_metrics"},{"symbol":"max_rw_code_byte"}],"data":{"u64":7824}}}}},{"in_successful_contract_call":false,"event":{"ext":"v0","contract_id":null,"type_":"diagnostic","body":{"v0":{"topics":[{"symbol":"core_metrics"},{"symbol":"max_emit_event_byte"}],"data":{"u64":0}}}}}]}}}}"#;
445
446 const CLASSIC_TX_RESPONSE: &str = r#"{"status":"SUCCESS","envelope":{"tx":{"tx":{"source_account":"GCBVAIKUZELFVCV6S7KBS47SF2DQQXTN63TJM7D3CNZ7PD6RCDTBYULI","fee":100,"seq_num":197568495620,"cond":"none","memo":"none","operations":[{"source_account":null,"body":{"manage_data":{"data_name":"test","data_value":"abcdef"}}}],"ext":"v0"},"signatures":[{"hint":"d110e61c","signature":"89be4c9f86a3aa19de242f54b69e95894c451f0fa6e8c6f7ad7bb353e08c6aefafb45e73746340e54f87aa9112aee2e7424b81289e0a7756c3e024406d0cdf0a"}]}},"result":{"fee_charged":100,"result":{"tx_success":[{"op_inner":{"manage_data":"success"}}]},"ext":"v0"},"result_meta":{"v3":{"ext":"v0","tx_changes_before":[{"state":{"last_modified_ledger_seq":15678,"data":{"account":{"account_id":"GCBVAIKUZELFVCV6S7KBS47SF2DQQXTN63TJM7D3CNZ7PD6RCDTBYULI","balance":99988100312,"seq_num":197568495619,"num_sub_entries":0,"inflation_dest":null,"flags":0,"home_domain":"","thresholds":"01000000","signers":[],"ext":{"v1":{"liabilities":{"buying":0,"selling":0},"ext":{"v2":{"num_sponsored":0,"num_sponsoring":0,"signer_sponsoring_i_ds":[],"ext":{"v3":{"ext":"v0","seq_ledger":54,"seq_time":1750166271}}}}}}}},"ext":"v0"}},{"updated":{"last_modified_ledger_seq":15678,"data":{"account":{"account_id":"GCBVAIKUZELFVCV6S7KBS47SF2DQQXTN63TJM7D3CNZ7PD6RCDTBYULI","balance":99988100312,"seq_num":197568495620,"num_sub_entries":0,"inflation_dest":null,"flags":0,"home_domain":"","thresholds":"01000000","signers":[],"ext":{"v1":{"liabilities":{"buying":0,"selling":0},"ext":{"v2":{"num_sponsored":0,"num_sponsoring":0,"signer_sponsoring_i_ds":[],"ext":{"v3":{"ext":"v0","seq_ledger":15678,"seq_time":1750185606}}}}}}}},"ext":"v0"}}],"operations":[{"changes":[{"created":{"last_modified_ledger_seq":15678,"data":{"data":{"account_id":"GCBVAIKUZELFVCV6S7KBS47SF2DQQXTN63TJM7D3CNZ7PD6RCDTBYULI","data_name":"test","data_value":"abcdef","ext":"v0"}},"ext":"v0"}},{"state":{"last_modified_ledger_seq":15678,"data":{"account":{"account_id":"GCBVAIKUZELFVCV6S7KBS47SF2DQQXTN63TJM7D3CNZ7PD6RCDTBYULI","balance":99988100312,"seq_num":197568495620,"num_sub_entries":0,"inflation_dest":null,"flags":0,"home_domain":"","thresholds":"01000000","signers":[],"ext":{"v1":{"liabilities":{"buying":0,"selling":0},"ext":{"v2":{"num_sponsored":0,"num_sponsoring":0,"signer_sponsoring_i_ds":[],"ext":{"v3":{"ext":"v0","seq_ledger":15678,"seq_time":1750185606}}}}}}}},"ext":"v0"}},{"updated":{"last_modified_ledger_seq":15678,"data":{"account":{"account_id":"GCBVAIKUZELFVCV6S7KBS47SF2DQQXTN63TJM7D3CNZ7PD6RCDTBYULI","balance":99988100312,"seq_num":197568495620,"num_sub_entries":1,"inflation_dest":null,"flags":0,"home_domain":"","thresholds":"01000000","signers":[],"ext":{"v1":{"liabilities":{"buying":0,"selling":0},"ext":{"v2":{"num_sponsored":0,"num_sponsoring":0,"signer_sponsoring_i_ds":[],"ext":{"v3":{"ext":"v0","seq_ledger":15678,"seq_time":1750185606}}}}}}}},"ext":"v0"}}]}],"tx_changes_after":[],"soroban_meta":null}}}"#;
447
448 const FEE_BUMP_TX_RESPONSE: &str = r#"{"status":"SUCCESS","envelope":{"tx_fee_bump":{"tx":{"fee_source":"GDBFMEGF2EVTNISNTYVOOYGXAEP5A353YJCPDRGUH3L6GMIDATR4BWY6","fee":400,"inner_tx":{"tx":{"tx":{"source_account":"GCBVAIKUZELFVCV6S7KBS47SF2DQQXTN63TJM7D3CNZ7PD6RCDTBYULI","fee":100,"seq_num":197568495621,"cond":{"time":{"min_time":0,"max_time":1750189754}},"memo":"none","operations":[{"source_account":null,"body":{"payment":{"destination":"GDN2KA5HJ55DDXBKBBAPGWPXXAWEMLSPZIH3B2G4N7ERRUL7SN5BIRAX","asset":"native","amount":100000000}}}],"ext":"v0"},"signatures":[{"hint":"d110e61c","signature":"cfc3a142055e944995dcb1246bdbe84cd3f834ce8ae7a26ceeaeaa4edaee168628ed50d285d111d632d983a71fc305056e87d50ca50c80c193ed580398f43a0c"}]}},"ext":"v0"},"signatures":[{"hint":"0304e3c0","signature":"cc4d0d4a92ac2dbaeb7724c01713b4428ce48f61d263a70493e04cb9fadccd25342775200b1e1c27719971ce0f98ce44d0e2655491d432938aa22c92bf64df07"}]}},"result":{"fee_charged":200,"result":{"tx_fee_bump_inner_success":{"transaction_hash":"95a59fd736d69126705692a877c78881a33c28fc50684e61189b6b8bfa02e646","result":{"fee_charged":100,"result":{"tx_success":[{"op_inner":{"payment":"success"}}]},"ext":"v0"}}},"ext":"v0"},"result_meta":{"v3":{"ext":"v0","tx_changes_before":[{"state":{"last_modified_ledger_seq":18731,"data":{"account":{"account_id":"GDBFMEGF2EVTNISNTYVOOYGXAEP5A353YJCPDRGUH3L6GMIDATR4BWY6","balance":99999999800,"seq_num":80427557584896,"num_sub_entries":0,"inflation_dest":null,"flags":0,"home_domain":"","thresholds":"01000000","signers":[],"ext":"v0"}},"ext":"v0"}},{"updated":{"last_modified_ledger_seq":18731,"data":{"account":{"account_id":"GDBFMEGF2EVTNISNTYVOOYGXAEP5A353YJCPDRGUH3L6GMIDATR4BWY6","balance":99999999800,"seq_num":80427557584896,"num_sub_entries":0,"inflation_dest":null,"flags":0,"home_domain":"","thresholds":"01000000","signers":[],"ext":"v0"}},"ext":"v0"}},{"state":{"last_modified_ledger_seq":15678,"data":{"account":{"account_id":"GCBVAIKUZELFVCV6S7KBS47SF2DQQXTN63TJM7D3CNZ7PD6RCDTBYULI","balance":99988100312,"seq_num":197568495620,"num_sub_entries":1,"inflation_dest":null,"flags":0,"home_domain":"","thresholds":"01000000","signers":[],"ext":{"v1":{"liabilities":{"buying":0,"selling":0},"ext":{"v2":{"num_sponsored":0,"num_sponsoring":0,"signer_sponsoring_i_ds":[],"ext":{"v3":{"ext":"v0","seq_ledger":15678,"seq_time":1750185606}}}}}}}},"ext":"v0"}},{"updated":{"last_modified_ledger_seq":18731,"data":{"account":{"account_id":"GCBVAIKUZELFVCV6S7KBS47SF2DQQXTN63TJM7D3CNZ7PD6RCDTBYULI","balance":99988100312,"seq_num":197568495621,"num_sub_entries":1,"inflation_dest":null,"flags":0,"home_domain":"","thresholds":"01000000","signers":[],"ext":{"v1":{"liabilities":{"buying":0,"selling":0},"ext":{"v2":{"num_sponsored":0,"num_sponsoring":0,"signer_sponsoring_i_ds":[],"ext":{"v3":{"ext":"v0","seq_ledger":18731,"seq_time":1750189724}}}}}}}},"ext":"v0"}}],"operations":[{"changes":[{"state":{"last_modified_ledger_seq":18731,"data":{"account":{"account_id":"GCBVAIKUZELFVCV6S7KBS47SF2DQQXTN63TJM7D3CNZ7PD6RCDTBYULI","balance":99988100312,"seq_num":197568495621,"num_sub_entries":1,"inflation_dest":null,"flags":0,"home_domain":"","thresholds":"01000000","signers":[],"ext":{"v1":{"liabilities":{"buying":0,"selling":0},"ext":{"v2":{"num_sponsored":0,"num_sponsoring":0,"signer_sponsoring_i_ds":[],"ext":{"v3":{"ext":"v0","seq_ledger":18731,"seq_time":1750189724}}}}}}}},"ext":"v0"}},{"updated":{"last_modified_ledger_seq":18731,"data":{"account":{"account_id":"GCBVAIKUZELFVCV6S7KBS47SF2DQQXTN63TJM7D3CNZ7PD6RCDTBYULI","balance":99888100312,"seq_num":197568495621,"num_sub_entries":1,"inflation_dest":null,"flags":0,"home_domain":"","thresholds":"01000000","signers":[],"ext":{"v1":{"liabilities":{"buying":0,"selling":0},"ext":{"v2":{"num_sponsored":0,"num_sponsoring":0,"signer_sponsoring_i_ds":[],"ext":{"v3":{"ext":"v0","seq_ledger":18731,"seq_time":1750189724}}}}}}}},"ext":"v0"}},{"state":{"last_modified_ledger_seq":18687,"data":{"account":{"account_id":"GDN2KA5HJ55DDXBKBBAPGWPXXAWEMLSPZIH3B2G4N7ERRUL7SN5BIRAX","balance":100000000000,"seq_num":80260053860352,"num_sub_entries":0,"inflation_dest":null,"flags":0,"home_domain":"","thresholds":"01000000","signers":[],"ext":"v0"}},"ext":"v0"}},{"updated":{"last_modified_ledger_seq":18731,"data":{"account":{"account_id":"GDN2KA5HJ55DDXBKBBAPGWPXXAWEMLSPZIH3B2G4N7ERRUL7SN5BIRAX","balance":100100000000,"seq_num":80260053860352,"num_sub_entries":0,"inflation_dest":null,"flags":0,"home_domain":"","thresholds":"01000000","signers":[],"ext":"v0"}},"ext":"v0"}}]}],"tx_changes_after":[],"soroban_meta":null}}}"#;
449}