0
# Transaction Status
1
2
Transaction metadata, execution results, and status tracking including error handling, parsed transaction data, and confirmation information. This provides comprehensive tools for monitoring transaction lifecycle and analyzing execution outcomes.
3
4
## Capabilities
5
6
### Transaction Status and Confirmation
7
8
Core transaction status information including confirmation levels and execution metadata.
9
10
```python { .api }
11
class TransactionStatus:
12
"""
13
Transaction confirmation status and metadata.
14
"""
15
def __init__(
16
self,
17
slot: int,
18
confirmations: Optional[int],
19
status: Optional['TransactionErrorType'],
20
confirmation_status: Optional['TransactionConfirmationStatus']
21
):
22
"""
23
Create transaction status.
24
25
Parameters:
26
- slot: int, slot where transaction was processed
27
- confirmations: Optional[int], number of confirmations (None if not confirmed)
28
- status: Optional[TransactionErrorType], error status (None if successful)
29
- confirmation_status: Optional[TransactionConfirmationStatus], confirmation level
30
"""
31
32
@property
33
def slot(self) -> int:
34
"""Slot where transaction was processed."""
35
36
@property
37
def confirmations(self) -> Optional[int]:
38
"""Number of confirmations (None if not confirmed)."""
39
40
@property
41
def status(self) -> Optional['TransactionErrorType']:
42
"""Error status (None if successful)."""
43
44
@property
45
def confirmation_status(self) -> Optional['TransactionConfirmationStatus']:
46
"""Current confirmation level."""
47
48
def is_successful(self) -> bool:
49
"""
50
Check if transaction was successful.
51
52
Returns:
53
bool, True if transaction succeeded
54
"""
55
56
def is_confirmed(self) -> bool:
57
"""
58
Check if transaction is confirmed.
59
60
Returns:
61
bool, True if transaction has confirmations
62
"""
63
64
class TransactionConfirmationStatus:
65
"""Transaction confirmation level enumeration."""
66
Processed: 'TransactionConfirmationStatus' # Transaction processed
67
Confirmed: 'TransactionConfirmationStatus' # Transaction confirmed
68
Finalized: 'TransactionConfirmationStatus' # Transaction finalized
69
70
def __str__(self) -> str:
71
"""String representation of confirmation status."""
72
```
73
74
### Transaction Metadata
75
76
Comprehensive transaction execution metadata including fees, logs, and state changes.
77
78
```python { .api }
79
class UiTransactionStatusMeta:
80
"""
81
Transaction execution metadata in UI-friendly format.
82
"""
83
def __init__(
84
self,
85
err: Optional['TransactionErrorType'],
86
fee: int,
87
pre_balances: List[int],
88
post_balances: List[int],
89
inner_instructions: Optional[List['UiInnerInstructions']],
90
log_messages: Optional[List[str]],
91
pre_token_balances: Optional[List['UiTransactionTokenBalance']],
92
post_token_balances: Optional[List['UiTransactionTokenBalance']],
93
rewards: Optional[List['Reward']],
94
loaded_addresses: Optional['UiLoadedAddresses'],
95
return_data: Optional['TransactionReturnData'],
96
compute_units_consumed: Optional[int]
97
):
98
"""
99
Create transaction metadata.
100
101
Parameters:
102
- err: Optional[TransactionErrorType], transaction error (None if successful)
103
- fee: int, transaction fee in lamports
104
- pre_balances: List[int], account balances before execution
105
- post_balances: List[int], account balances after execution
106
- inner_instructions: Optional[List], cross-program invocation instructions
107
- log_messages: Optional[List[str]], program log messages
108
- pre_token_balances: Optional[List], token balances before execution
109
- post_token_balances: Optional[List], token balances after execution
110
- rewards: Optional[List[Reward]], epoch rewards (if any)
111
- loaded_addresses: Optional[UiLoadedAddresses], addresses loaded from lookup tables
112
- return_data: Optional[TransactionReturnData], program return data
113
- compute_units_consumed: Optional[int], compute units used
114
"""
115
116
@property
117
def err(self) -> Optional['TransactionErrorType']:
118
"""Transaction error (None if successful)."""
119
120
@property
121
def fee(self) -> int:
122
"""Transaction fee in lamports."""
123
124
@property
125
def pre_balances(self) -> List[int]:
126
"""Account balances before execution."""
127
128
@property
129
def post_balances(self) -> List[int]:
130
"""Account balances after execution."""
131
132
@property
133
def inner_instructions(self) -> Optional[List['UiInnerInstructions']]:
134
"""Cross-program invocation instructions."""
135
136
@property
137
def log_messages(self) -> Optional[List[str]]:
138
"""Program log messages."""
139
140
@property
141
def pre_token_balances(self) -> Optional[List['UiTransactionTokenBalance']]:
142
"""Token balances before execution."""
143
144
@property
145
def post_token_balances(self) -> Optional[List['UiTransactionTokenBalance']]:
146
"""Token balances after execution."""
147
148
@property
149
def rewards(self) -> Optional[List['Reward']]:
150
"""Epoch rewards (if any)."""
151
152
@property
153
def loaded_addresses(self) -> Optional['UiLoadedAddresses']:
154
"""Addresses loaded from lookup tables."""
155
156
@property
157
def return_data(self) -> Optional['TransactionReturnData']:
158
"""Program return data."""
159
160
@property
161
def compute_units_consumed(self) -> Optional[int]:
162
"""Compute units used."""
163
164
def is_successful(self) -> bool:
165
"""Check if transaction was successful."""
166
167
def get_balance_changes(self) -> List[int]:
168
"""
169
Calculate balance changes for each account.
170
171
Returns:
172
List[int], balance change for each account (post - pre)
173
"""
174
175
def get_fee_payer_balance_change(self) -> int:
176
"""
177
Get balance change for fee payer (first account).
178
179
Returns:
180
int, fee payer balance change including fees
181
"""
182
```
183
184
### Token Balance Changes
185
186
Token-specific balance tracking and change analysis.
187
188
```python { .api }
189
class UiTransactionTokenBalance:
190
"""
191
Token balance information for a transaction account.
192
"""
193
def __init__(
194
self,
195
account_index: int,
196
mint: str,
197
ui_token_amount: 'UiTokenAmount',
198
owner: Optional[str],
199
program_id: Optional[str]
200
):
201
"""
202
Create token balance information.
203
204
Parameters:
205
- account_index: int, index of account in transaction
206
- mint: str, token mint address
207
- ui_token_amount: UiTokenAmount, token amount with decimals
208
- owner: Optional[str], token account owner
209
- program_id: Optional[str], token program ID
210
"""
211
212
@property
213
def account_index(self) -> int:
214
"""Index of account in transaction."""
215
216
@property
217
def mint(self) -> str:
218
"""Token mint address."""
219
220
@property
221
def ui_token_amount(self) -> 'UiTokenAmount':
222
"""Token amount with decimals."""
223
224
@property
225
def owner(self) -> Optional[str]:
226
"""Token account owner."""
227
228
@property
229
def program_id(self) -> Optional[str]:
230
"""Token program ID."""
231
232
def calculate_token_balance_changes(
233
pre_balances: List['UiTransactionTokenBalance'],
234
post_balances: List['UiTransactionTokenBalance']
235
) -> List[tuple[str, str, float]]:
236
"""
237
Calculate token balance changes across transaction.
238
239
Parameters:
240
- pre_balances: List, token balances before transaction
241
- post_balances: List, token balances after transaction
242
243
Returns:
244
List[tuple[str, str, float]], (mint, owner, change_amount) for each change
245
"""
246
```
247
248
### Inner Instructions and Cross-Program Invocations
249
250
Detailed tracking of cross-program invocations and nested instruction execution.
251
252
```python { .api }
253
class UiInnerInstructions:
254
"""
255
Inner instructions from cross-program invocations.
256
"""
257
def __init__(self, index: int, instructions: List['UiInstruction']):
258
"""
259
Create inner instructions container.
260
261
Parameters:
262
- index: int, index of parent instruction that generated these
263
- instructions: List[UiInstruction], list of inner instructions
264
"""
265
266
@property
267
def index(self) -> int:
268
"""Index of parent instruction."""
269
270
@property
271
def instructions(self) -> List['UiInstruction']:
272
"""List of inner instructions."""
273
274
class UiInstruction:
275
"""
276
UI representation of an instruction (union type).
277
"""
278
# This is a union of UiParsedInstruction and UiCompiledInstruction
279
280
class UiParsedInstruction:
281
"""
282
Union of parsed and partially decoded instructions.
283
"""
284
# This is a union of ParsedInstruction and UiPartiallyDecodedInstruction
285
286
class ParsedInstruction:
287
"""
288
Fully parsed instruction with program-specific interpretation.
289
"""
290
def __init__(
291
self,
292
program: str,
293
program_id: Pubkey,
294
parsed: dict
295
):
296
"""
297
Create parsed instruction.
298
299
Parameters:
300
- program: str, program name (e.g., "spl-token")
301
- program_id: Pubkey, program ID that was invoked
302
- parsed: dict, program-specific parsed data
303
"""
304
305
@property
306
def program(self) -> str:
307
"""Program name."""
308
309
@property
310
def program_id(self) -> Pubkey:
311
"""Program ID that was invoked."""
312
313
@property
314
def parsed(self) -> dict:
315
"""Program-specific parsed data."""
316
317
class UiPartiallyDecodedInstruction:
318
"""
319
Partially decoded instruction with raw accounts and data.
320
"""
321
def __init__(
322
self,
323
program_id: Pubkey,
324
accounts: List[Pubkey],
325
data: str,
326
stack_height: Optional[int]
327
):
328
"""
329
Create partially decoded instruction.
330
331
Parameters:
332
- program_id: Pubkey, program that was invoked
333
- accounts: List[Pubkey], accounts passed to instruction
334
- data: str, base58-encoded instruction data
335
- stack_height: Optional[int], call stack depth
336
"""
337
338
@property
339
def program_id(self) -> Pubkey:
340
"""Program that was invoked."""
341
342
@property
343
def accounts(self) -> List[Pubkey]:
344
"""Accounts passed to instruction."""
345
346
@property
347
def data(self) -> str:
348
"""Base58-encoded instruction data."""
349
350
@property
351
def stack_height(self) -> Optional[int]:
352
"""Call stack depth."""
353
354
class UiCompiledInstruction:
355
"""
356
Compiled instruction with account indexes.
357
"""
358
def __init__(self, program_id_index: int, accounts: List[int], data: str):
359
"""
360
Create compiled instruction.
361
362
Parameters:
363
- program_id_index: int, index of program ID in account keys
364
- accounts: List[int], account indexes
365
- data: str, base58-encoded instruction data
366
"""
367
368
@property
369
def program_id_index(self) -> int:
370
"""Program ID index in account keys."""
371
372
@property
373
def accounts(self) -> List[int]:
374
"""Account indexes."""
375
376
@property
377
def data(self) -> str:
378
"""Base58-encoded instruction data."""
379
```
380
381
### Rewards and Incentives
382
383
Epoch reward distribution and staking incentive tracking.
384
385
```python { .api }
386
class Reward:
387
"""
388
Individual reward from epoch distribution.
389
"""
390
def __init__(
391
self,
392
pubkey: str,
393
lamports: int,
394
post_balance: int,
395
reward_type: Optional['RewardType'],
396
commission: Optional[int]
397
):
398
"""
399
Create reward information.
400
401
Parameters:
402
- pubkey: str, recipient account address
403
- lamports: int, reward amount in lamports
404
- post_balance: int, account balance after reward
405
- reward_type: Optional[RewardType], type of reward
406
- commission: Optional[int], validator commission (if applicable)
407
"""
408
409
@property
410
def pubkey(self) -> str:
411
"""Recipient account address."""
412
413
@property
414
def lamports(self) -> int:
415
"""Reward amount in lamports."""
416
417
@property
418
def post_balance(self) -> int:
419
"""Account balance after reward."""
420
421
@property
422
def reward_type(self) -> Optional['RewardType']:
423
"""Type of reward."""
424
425
@property
426
def commission(self) -> Optional[int]:
427
"""Validator commission (if applicable)."""
428
429
class RewardType:
430
"""Reward type enumeration."""
431
Fee: 'RewardType' # Transaction fees
432
Rent: 'RewardType' # Rent collection
433
Staking: 'RewardType' # Staking rewards
434
Voting: 'RewardType' # Voting rewards
435
436
def __str__(self) -> str:
437
"""String representation of reward type."""
438
```
439
440
### Transaction Encoding and Details
441
442
Transaction representation options and detail levels for different use cases.
443
444
```python { .api }
445
class TransactionDetails:
446
"""Transaction detail level for responses."""
447
Full: 'TransactionDetails' # Include all transaction details
448
Accounts: 'TransactionDetails' # Include only account keys
449
Signatures: 'TransactionDetails' # Include only signatures
450
None_: 'TransactionDetails' # Exclude transaction details
451
452
class UiTransactionEncoding:
453
"""Transaction encoding format for responses."""
454
Json: 'UiTransactionEncoding' # JSON format with parsed data
455
JsonParsed: 'UiTransactionEncoding' # JSON with program-specific parsing
456
Base58: 'UiTransactionEncoding' # Base58 encoded
457
Base64: 'UiTransactionEncoding' # Base64 encoded
458
459
class TransactionBinaryEncoding:
460
"""Binary transaction encoding format."""
461
Base58: 'TransactionBinaryEncoding' # Base58 encoded
462
Base64: 'TransactionBinaryEncoding' # Base64 encoded
463
```
464
465
### Transaction Return Data
466
467
Program return data and execution results.
468
469
```python { .api }
470
class TransactionReturnData:
471
"""
472
Return data from program execution.
473
"""
474
def __init__(self, program_id: Pubkey, data: bytes):
475
"""
476
Create transaction return data.
477
478
Parameters:
479
- program_id: Pubkey, program that returned the data
480
- data: bytes, returned data
481
"""
482
483
@property
484
def program_id(self) -> Pubkey:
485
"""Program that returned the data."""
486
487
@property
488
def data(self) -> bytes:
489
"""Returned data."""
490
491
def decode_string(self) -> str:
492
"""
493
Decode return data as UTF-8 string.
494
495
Returns:
496
str, decoded string data
497
498
Raises:
499
- UnicodeDecodeError: if data is not valid UTF-8
500
"""
501
502
def decode_json(self) -> dict:
503
"""
504
Decode return data as JSON.
505
506
Returns:
507
dict, parsed JSON data
508
509
Raises:
510
- JSONDecodeError: if data is not valid JSON
511
"""
512
```
513
514
### Address Lookup Table Support
515
516
Address lookup table metadata and loaded address information.
517
518
```python { .api }
519
class UiLoadedAddresses:
520
"""
521
Addresses loaded from lookup tables in versioned transactions.
522
"""
523
def __init__(self, writable: List[str], readonly: List[str]):
524
"""
525
Create loaded addresses information.
526
527
Parameters:
528
- writable: List[str], writable addresses from lookup tables
529
- readonly: List[str], readonly addresses from lookup tables
530
"""
531
532
@property
533
def writable(self) -> List[str]:
534
"""Writable addresses from lookup tables."""
535
536
@property
537
def readonly(self) -> List[str]:
538
"""Readonly addresses from lookup tables."""
539
540
def total_loaded(self) -> int:
541
"""
542
Get total number of loaded addresses.
543
544
Returns:
545
int, total loaded addresses (writable + readonly)
546
"""
547
548
class UiAddressTableLookup:
549
"""
550
Address table lookup reference in UI format.
551
"""
552
def __init__(
553
self,
554
account_key: str,
555
writable_indexes: List[int],
556
readonly_indexes: List[int]
557
):
558
"""
559
Create address table lookup.
560
561
Parameters:
562
- account_key: str, lookup table account address
563
- writable_indexes: List[int], indexes of writable addresses
564
- readonly_indexes: List[int], indexes of readonly addresses
565
"""
566
567
@property
568
def account_key(self) -> str:
569
"""Lookup table account address."""
570
571
@property
572
def writable_indexes(self) -> List[int]:
573
"""Indexes of writable addresses."""
574
575
@property
576
def readonly_indexes(self) -> List[int]:
577
"""Indexes of readonly addresses."""
578
```
579
580
## Usage Examples
581
582
### Transaction Status Monitoring
583
584
```python
585
from solders.transaction_status import TransactionStatus, TransactionConfirmationStatus
586
from solders.rpc.requests import GetSignatureStatuses
587
588
# Monitor transaction confirmation
589
def monitor_transaction_status(signatures: List[Signature]) -> dict:
590
"""Monitor status of multiple transactions."""
591
status_request = GetSignatureStatuses(signatures)
592
593
# After RPC call, process responses
594
results = {}
595
596
# Example processing (would get actual response from RPC)
597
for i, signature in enumerate(signatures):
598
# Parse status response
599
status = TransactionStatus(
600
slot=100000000,
601
confirmations=31, # Number of confirmations
602
status=None, # None means success
603
confirmation_status=TransactionConfirmationStatus.Finalized
604
)
605
606
results[str(signature)] = {
607
'successful': status.is_successful(),
608
'confirmed': status.is_confirmed(),
609
'slot': status.slot,
610
'confirmations': status.confirmations,
611
'confirmation_level': str(status.confirmation_status)
612
}
613
614
return results
615
616
# Wait for transaction confirmation
617
def wait_for_confirmation(signature: Signature, target_confirmations: int = 31):
618
"""Wait for transaction to reach target confirmation level."""
619
import time
620
621
while True:
622
status_request = GetSignatureStatuses([signature])
623
# Get response from RPC...
624
625
# Example status check (would parse actual RPC response)
626
if status and status.confirmations and status.confirmations >= target_confirmations:
627
print(f"Transaction confirmed with {status.confirmations} confirmations")
628
return True
629
630
time.sleep(1) # Wait 1 second before checking again
631
```
632
633
### Transaction Metadata Analysis
634
635
```python
636
from solders.transaction_status import UiTransactionStatusMeta
637
638
def analyze_transaction_execution(meta: UiTransactionStatusMeta):
639
"""Analyze transaction execution results."""
640
641
# Check success/failure
642
if meta.is_successful():
643
print("Transaction executed successfully")
644
else:
645
print(f"Transaction failed with error: {meta.err}")
646
647
# Analyze fees and balance changes
648
print(f"Transaction fee: {meta.fee} lamports ({meta.fee / 1e9:.9f} SOL)")
649
650
balance_changes = meta.get_balance_changes()
651
for i, change in enumerate(balance_changes):
652
if change != 0:
653
print(f"Account {i} balance change: {change:+,} lamports")
654
655
# Fee payer analysis
656
fee_payer_change = meta.get_fee_payer_balance_change()
657
print(f"Fee payer total change: {fee_payer_change:+,} lamports (including fees)")
658
659
# Compute unit consumption
660
if meta.compute_units_consumed:
661
print(f"Compute units used: {meta.compute_units_consumed:,}")
662
efficiency = (meta.compute_units_consumed / 200000) * 100 # Assuming 200k limit
663
print(f"Compute efficiency: {efficiency:.1f}% of limit")
664
665
# Log analysis
666
if meta.log_messages:
667
print(f"Program logs ({len(meta.log_messages)} messages):")
668
for log in meta.log_messages[:5]: # Show first 5 logs
669
print(f" {log}")
670
if len(meta.log_messages) > 5:
671
print(f" ... and {len(meta.log_messages) - 5} more")
672
673
def analyze_token_transfers(meta: UiTransactionStatusMeta):
674
"""Analyze token transfers in transaction."""
675
if not meta.pre_token_balances or not meta.post_token_balances:
676
print("No token balance information available")
677
return
678
679
# Calculate token changes
680
token_changes = calculate_token_balance_changes(
681
meta.pre_token_balances,
682
meta.post_token_balances
683
)
684
685
print(f"Token transfers ({len(token_changes)} changes):")
686
for mint, owner, change in token_changes:
687
if change != 0:
688
print(f" {owner}: {change:+.6f} tokens (mint: {mint})")
689
```
690
691
### Inner Instruction Analysis
692
693
```python
694
def analyze_cross_program_invocations(meta: UiTransactionStatusMeta):
695
"""Analyze cross-program invocations and inner instructions."""
696
if not meta.inner_instructions:
697
print("No cross-program invocations")
698
return
699
700
print(f"Cross-program invocations: {len(meta.inner_instructions)} instruction groups")
701
702
for inner_group in meta.inner_instructions:
703
print(f"\nInstruction {inner_group.index} invoked {len(inner_group.instructions)} inner instructions:")
704
705
for inner_ix in inner_group.instructions:
706
if isinstance(inner_ix, ParsedInstruction):
707
print(f" {inner_ix.program}: {inner_ix.parsed.get('type', 'unknown')}")
708
elif isinstance(inner_ix, UiPartiallyDecodedInstruction):
709
print(f" Program {inner_ix.program_id}: {len(inner_ix.accounts)} accounts")
710
if inner_ix.stack_height:
711
print(f" Stack height: {inner_ix.stack_height}")
712
713
def trace_program_execution(meta: UiTransactionStatusMeta):
714
"""Trace program execution through logs and inner instructions."""
715
execution_trace = []
716
717
# Add main instructions (would need transaction data)
718
# execution_trace.append("Main instruction: ...")
719
720
# Add inner instructions
721
if meta.inner_instructions:
722
for inner_group in meta.inner_instructions:
723
for inner_ix in inner_group.instructions:
724
if isinstance(inner_ix, ParsedInstruction):
725
execution_trace.append(f"CPI: {inner_ix.program}")
726
727
# Correlate with logs
728
if meta.log_messages:
729
for log in meta.log_messages:
730
if "invoke" in log.lower():
731
execution_trace.append(f"Log: {log}")
732
733
print("Execution trace:")
734
for i, event in enumerate(execution_trace):
735
print(f"{i+1:2d}. {event}")
736
```
737
738
### Reward Distribution Analysis
739
740
```python
741
def analyze_epoch_rewards(rewards: List[Reward]):
742
"""Analyze epoch reward distribution."""
743
if not rewards:
744
print("No rewards in this transaction")
745
return
746
747
total_rewards = sum(reward.lamports for reward in rewards)
748
print(f"Total rewards distributed: {total_rewards:,} lamports ({total_rewards / 1e9:.6f} SOL)")
749
750
# Group by reward type
751
by_type = {}
752
for reward in rewards:
753
reward_type = str(reward.reward_type) if reward.reward_type else "Unknown"
754
if reward_type not in by_type:
755
by_type[reward_type] = []
756
by_type[reward_type].append(reward)
757
758
for reward_type, type_rewards in by_type.items():
759
type_total = sum(r.lamports for r in type_rewards)
760
print(f"{reward_type}: {len(type_rewards)} recipients, {type_total:,} lamports")
761
762
# Show largest rewards
763
sorted_rewards = sorted(rewards, key=lambda r: r.lamports, reverse=True)
764
print(f"\nTop rewards:")
765
for reward in sorted_rewards[:5]:
766
print(f" {reward.pubkey}: {reward.lamports:,} lamports")
767
if reward.commission:
768
print(f" Commission: {reward.commission}%")
769
```
770
771
### Transaction Return Data Processing
772
773
```python
774
def process_return_data(return_data: TransactionReturnData):
775
"""Process program return data."""
776
print(f"Return data from program: {return_data.program_id}")
777
print(f"Data length: {len(return_data.data)} bytes")
778
779
# Try to decode as string
780
try:
781
text_data = return_data.decode_string()
782
print(f"String data: {text_data}")
783
except UnicodeDecodeError:
784
print("Data is not valid UTF-8 text")
785
786
# Try to decode as JSON
787
try:
788
json_data = return_data.decode_json()
789
print(f"JSON data: {json_data}")
790
except:
791
print("Data is not valid JSON")
792
793
# Show raw bytes (first 50 bytes)
794
hex_data = return_data.data[:50].hex()
795
print(f"Raw data (hex): {hex_data}{'...' if len(return_data.data) > 50 else ''}")
796
```
797
798
### Address Lookup Table Analysis
799
800
```python
801
def analyze_loaded_addresses(loaded_addresses: UiLoadedAddresses):
802
"""Analyze addresses loaded from lookup tables."""
803
total = loaded_addresses.total_loaded()
804
print(f"Loaded addresses: {total} total")
805
print(f" Writable: {len(loaded_addresses.writable)}")
806
print(f" Readonly: {len(loaded_addresses.readonly)}")
807
808
# Calculate compression savings
809
# Each address is 32 bytes, lookup table reference is much smaller
810
bytes_saved = total * 32 - (total * 1) # Simplified calculation
811
print(f"Approximate bytes saved: {bytes_saved}")
812
813
# Show some addresses
814
if loaded_addresses.writable:
815
print("Writable addresses:")
816
for addr in loaded_addresses.writable[:3]:
817
print(f" {addr}")
818
if len(loaded_addresses.writable) > 3:
819
print(f" ... and {len(loaded_addresses.writable) - 3} more")
820
821
def calculate_transaction_efficiency(meta: UiTransactionStatusMeta):
822
"""Calculate transaction efficiency metrics."""
823
metrics = {
824
'fee_per_account': meta.fee / len(meta.pre_balances) if meta.pre_balances else 0,
825
'accounts_modified': sum(1 for change in meta.get_balance_changes() if change != 0),
826
'inner_instruction_count': 0,
827
'log_message_count': len(meta.log_messages) if meta.log_messages else 0
828
}
829
830
if meta.inner_instructions:
831
metrics['inner_instruction_count'] = sum(
832
len(group.instructions) for group in meta.inner_instructions
833
)
834
835
if meta.compute_units_consumed:
836
metrics['compute_efficiency'] = meta.compute_units_consumed / 200000 # Assuming 200k limit
837
838
# Fee efficiency
839
if meta.compute_units_consumed:
840
metrics['fee_per_cu'] = meta.fee / meta.compute_units_consumed
841
842
return metrics
843
```
844
845
## Error Analysis
846
847
### Transaction Error Types
848
849
```python { .api }
850
# Union types for transaction errors
851
TransactionErrorType = Union[
852
TransactionErrorFieldless,
853
TransactionErrorInstructionError,
854
TransactionErrorDuplicateInstruction,
855
TransactionErrorInsufficientFundsForRent,
856
TransactionErrorProgramExecutionTemporarilyRestricted
857
]
858
859
InstructionErrorType = Union[
860
InstructionErrorFieldless,
861
InstructionErrorCustom,
862
InstructionErrorBorshIO
863
]
864
```
865
866
### Error Processing
867
868
```python
869
def analyze_transaction_error(error: TransactionErrorType):
870
"""Analyze and explain transaction error."""
871
if isinstance(error, TransactionErrorInstructionError):
872
instruction_index = error.instruction_index
873
instruction_error = error.error
874
875
print(f"Instruction {instruction_index} failed:")
876
877
if isinstance(instruction_error, InstructionErrorCustom):
878
print(f" Custom error code: {instruction_error.code}")
879
elif isinstance(instruction_error, InstructionErrorFieldless):
880
print(f" Error type: {instruction_error}")
881
else:
882
print(f" Error: {instruction_error}")
883
884
elif isinstance(error, TransactionErrorInsufficientFundsForRent):
885
account_index = error.account_index
886
print(f"Account {account_index} has insufficient funds for rent")
887
888
else:
889
print(f"Transaction error: {error}")
890
891
def suggest_error_fixes(error: TransactionErrorType) -> List[str]:
892
"""Suggest fixes for common transaction errors."""
893
suggestions = []
894
895
if "InsufficientFunds" in str(error):
896
suggestions.append("Add more SOL to the fee payer account")
897
suggestions.append("Reduce transaction complexity to lower fees")
898
899
elif "AccountNotFound" in str(error):
900
suggestions.append("Ensure all referenced accounts exist")
901
suggestions.append("Create missing accounts before transaction")
902
903
elif "InvalidAccountData" in str(error):
904
suggestions.append("Check account data format and size")
905
suggestions.append("Ensure account is owned by correct program")
906
907
elif "CustomError" in str(error):
908
suggestions.append("Check program documentation for error codes")
909
suggestions.append("Verify instruction parameters")
910
911
return suggestions
912
```
913
914
## Performance Monitoring
915
916
### Transaction Performance Metrics
917
918
```python
919
def calculate_performance_metrics(meta: UiTransactionStatusMeta) -> dict:
920
"""Calculate transaction performance metrics."""
921
metrics = {}
922
923
# Cost efficiency
924
metrics['cost_per_account'] = meta.fee / len(meta.pre_balances)
925
926
# Compute efficiency
927
if meta.compute_units_consumed:
928
metrics['compute_units_used'] = meta.compute_units_consumed
929
metrics['compute_utilization'] = meta.compute_units_consumed / 1400000 # Max CU
930
metrics['cost_per_compute_unit'] = meta.fee / meta.compute_units_consumed
931
932
# Complexity metrics
933
metrics['accounts_involved'] = len(meta.pre_balances)
934
metrics['accounts_modified'] = len([c for c in meta.get_balance_changes() if c != 0])
935
936
if meta.inner_instructions:
937
metrics['cpi_count'] = sum(len(group.instructions) for group in meta.inner_instructions)
938
metrics['max_stack_depth'] = max(
939
ix.stack_height or 1
940
for group in meta.inner_instructions
941
for ix in group.instructions
942
if hasattr(ix, 'stack_height') and ix.stack_height
943
)
944
945
return metrics
946
```