Source code for agora.model.transaction

import base64
from enum import IntEnum
from typing import List, Optional

from agoraapi.transaction.v3 import transaction_service_pb2 as tx_pb
from agoraapi.transaction.v4 import transaction_service_pb2 as tx_pb_v4
from kin_base import transaction_envelope as te, memo as stellar_memo

from agora import solana
from agora.error import TransactionErrors
from agora.keys import PublicKey
from agora.model.invoice import InvoiceList
from agora.model.memo import AgoraMemo
from agora.model.payment import ReadOnlyPayment
from agora.model.transaction_type import TransactionType


[docs]class TransactionState(IntEnum): UNKNOWN = 0 SUCCESS = 1 FAILED = 2 PENDING = 3
[docs] @staticmethod def from_proto(state: tx_pb.GetTransactionResponse.State): if state == tx_pb.GetTransactionResponse.State.SUCCESS: return TransactionState.SUCCESS return TransactionState.UNKNOWN
[docs] @staticmethod def from_proto_v4(state: tx_pb_v4.GetTransactionResponse.State): if state == tx_pb_v4.GetTransactionResponse.State.SUCCESS: return TransactionState.SUCCESS if state == tx_pb_v4.GetTransactionResponse.State.FAILED: return TransactionState.FAILED if state == tx_pb_v4.GetTransactionResponse.State.PENDING: return TransactionState.PENDING return TransactionState.UNKNOWN
[docs]class TransactionData: """The :class:`TransactionData <TransactionData>` object, which contains information about the payments in a transaction. :param tx_id: Either a 32-byte transaction hash, or a 64-byte transaction signature. :param payments: (optional) A list of :class:`ReadOnlyPayment <agora.model.payment.ReadOnlyPayment>` objects. :param error: (optional)) A :class:`TransactionError <agora.error.TransactionError>` object that contains extra details about why a transaction failed. If present, it indicates that the transaction failed. """ def __init__( self, tx_id: bytes, transaction_state: TransactionState, payments: List[ReadOnlyPayment] = None, error: Optional[TransactionErrors] = None, ): self.tx_id = tx_id self.transaction_state = transaction_state self.payments = payments if payments else [] self.error = error def __eq__(self, other): if not isinstance(other, TransactionData): return False return (self.tx_id == other.tx_id and all(payment == other.payments[idx] for idx, payment in enumerate(self.payments)) and self.error == other.error) def __repr__(self): return f'{self.__class__.__name__}(' \ f'tx_id={self.tx_id}, payments={[p for p in self.payments]!r}, error={self.error!r})'
[docs] @classmethod def from_proto( cls, item: tx_pb_v4.HistoryItem, state: tx_pb_v4.GetTransactionResponse.State ) -> 'TransactionData': payments = [] if item.invoice_list and item.invoice_list.invoices: if len(item.payments) != len(item.invoice_list.invoices): raise ValueError('number of invoices does not match number of payments') il = InvoiceList.from_proto(item.invoice_list) else: il = None tx_type = TransactionType.UNKNOWN memo = None tx_errors = None if item.solana_transaction.value: solana_tx = solana.Transaction.unmarshal(item.solana_transaction.value) program_idx = solana_tx.message.instructions[0].program_index if solana_tx.message.accounts[program_idx] == solana.MEMO_PROGRAM_KEY: decompiled_memo = solana.decompile_memo(solana_tx.message, 0) memo_data = decompiled_memo.data.decode('utf-8') try: agora_memo = AgoraMemo.from_b64_string(memo_data) tx_type = agora_memo.tx_type() except ValueError: memo = memo_data tx_errors = TransactionErrors.from_solana_tx(solana_tx, item.transaction_error, item.transaction_id.value) elif item.stellar_transaction.envelope_xdr: env = te.TransactionEnvelope.from_xdr(base64.b64encode(item.stellar_transaction.envelope_xdr)) tx = env.tx if isinstance(tx.memo, stellar_memo.HashMemo): try: agora_memo = AgoraMemo.from_base_memo(tx.memo) tx_type = agora_memo.tx_type() except ValueError: pass elif isinstance(tx.memo, stellar_memo.TextMemo): memo = tx.memo.text.decode() tx_errors = TransactionErrors.from_stellar_tx(env, item.transaction_error, item.transaction_id.value) for idx, p in enumerate(item.payments): inv = il.invoices[idx] if il and il.invoices else None payments.append(ReadOnlyPayment(PublicKey(p.source.value), PublicKey(p.destination.value), tx_type, p.amount, invoice=inv, memo=memo)) return cls( item.transaction_id.value, TransactionState.from_proto_v4(state), payments, error=tx_errors, )