Source code for stellar_sdk.memo

import abc
from typing import Union

from . import xdr as stellar_xdr
from .exceptions import MemoInvalidException
from .utils import hex_to_bytes

__all__ = ["Memo", "NoneMemo", "TextMemo", "IdMemo", "HashMemo", "ReturnHashMemo"]


[docs] class Memo(object, metaclass=abc.ABCMeta): """The :class:`Memo` object, which represents the base class for memos for use with Stellar transactions. The memo for a transaction contains optional extra information about the transaction taking place. It is the responsibility of the client to interpret this value. See the following implementations that serve a more practical use with the library: * :class:`NoneMemo` - No memo. * :class:`TextMemo` - A string encoded using either ASCII or UTF-8, up to 28-bytes long. * :class:`IdMemo` - A 64 bit unsigned integer. * :class:`HashMemo` - A 32 byte hash. * :class:`RetHashMemo` - A 32 byte hash intended to be interpreted as the hash of the transaction the sender is refunding. See `Stellar's documentation on Transactions <https://developers.stellar.org/docs/glossary/transactions/#memo>`__ for more information on how memos are used within transactions, as well as information on the available types of memos. """
[docs] @abc.abstractmethod def to_xdr_object(self) -> stellar_xdr.Memo: """Creates an XDR Memo object that represents this :class:`Memo`."""
[docs] @staticmethod def from_xdr_object(xdr_object: stellar_xdr.Memo) -> "Memo": """Returns an Memo object from XDR memo object.""" xdr_types = { stellar_xdr.MemoType.MEMO_TEXT: TextMemo, stellar_xdr.MemoType.MEMO_ID: IdMemo, stellar_xdr.MemoType.MEMO_HASH: HashMemo, stellar_xdr.MemoType.MEMO_RETURN: ReturnHashMemo, stellar_xdr.MemoType.MEMO_NONE: NoneMemo, } memo_cls = xdr_types.get(xdr_object.type, NoneMemo) return memo_cls.from_xdr_object(xdr_object) # type: ignore[attr-defined]
@abc.abstractmethod def __hash__(self): pass # pragma: no cover @abc.abstractmethod def __eq__(self, other: object) -> bool: pass # pragma: no cover
[docs] class NoneMemo(Memo): """The :class:`NoneMemo`, which represents no memo for a transaction."""
[docs] @classmethod def from_xdr_object(cls, xdr_object: stellar_xdr.Memo) -> "NoneMemo": """Returns an :class:`NoneMemo` object from XDR memo object.""" return cls()
[docs] def to_xdr_object(self) -> stellar_xdr.Memo: """Creates an XDR Memo object that represents this :class:`NoneMemo`.""" return stellar_xdr.Memo(type=stellar_xdr.MemoType.MEMO_NONE)
def __hash__(self): return hash(None) def __eq__(self, other: object) -> bool: if not isinstance(other, self.__class__): return NotImplemented return True def __str__(self): return "<NoneMemo>"
[docs] class TextMemo(Memo): """The :class:`TextMemo`, which represents ``MEMO_TEXT`` in a transaction. :param text: A string encoded using either ASCII or UTF-8, up to 28-bytes long. Note, `text` can be anything, see `this issue <https://github.com/stellar/new-docs/issues/555>`__ for more information. :raises: :exc:`MemoInvalidException <stellar_sdk.exceptions.MemoInvalidException>`: if ``text`` is not a valid text memo. """ def __init__(self, text: Union[str, bytes]) -> None: if not isinstance(text, bytes): text = bytes(text, encoding="utf-8") self.memo_text: bytes = text length = len(self.memo_text) if length > 28: raise MemoInvalidException( f"Text should be <= 28 bytes (ascii encoded), got {length} bytes." )
[docs] @classmethod def from_xdr_object(cls, xdr_object: stellar_xdr.Memo) -> "TextMemo": """Returns an :class:`TextMemo` object from XDR memo object.""" assert xdr_object.text is not None return cls(bytes(xdr_object.text))
[docs] def to_xdr_object(self) -> stellar_xdr.Memo: """Creates an XDR Memo object that represents this :class:`TextMemo`.""" return stellar_xdr.Memo( type=stellar_xdr.MemoType.MEMO_TEXT, text=self.memo_text )
def __hash__(self): return hash(self.memo_text) def __eq__(self, other: object) -> bool: if not isinstance(other, self.__class__): return NotImplemented return self.memo_text == other.memo_text def __str__(self): return f"<TextMemo [memo={self.memo_text}]>"
[docs] class IdMemo(Memo): """The :class:`IdMemo` which represents ``MEMO_ID`` in a transaction. :param memo_id: A 64 bit unsigned integer. :raises: :exc:`MemoInvalidException <stellar_sdk.exceptions.MemoInvalidException>`: if ``id`` is not a valid id memo. """ def __init__(self, memo_id: int) -> None: if memo_id < 0 or memo_id > 2**64 - 1: raise MemoInvalidException( "IdMemo is an unsigned 64-bit integer and the max valid value is 18446744073709551615." ) self.memo_id: int = memo_id
[docs] @classmethod def from_xdr_object(cls, xdr_object: stellar_xdr.Memo) -> "IdMemo": """Returns an :class:`IdMemo` object from XDR memo object.""" assert xdr_object.id is not None return cls(xdr_object.id.uint64)
[docs] def to_xdr_object(self) -> stellar_xdr.Memo: """Creates an XDR Memo object that represents this :class:`IdMemo`.""" return stellar_xdr.Memo( type=stellar_xdr.MemoType.MEMO_ID, id=stellar_xdr.Uint64(self.memo_id) )
def __hash__(self): return hash(self.memo_id) def __eq__(self, other: object) -> bool: if not isinstance(other, self.__class__): return NotImplemented return self.memo_id == other.memo_id def __str__(self): return f"<IdMemo [memo={self.memo_id}]>"
[docs] class HashMemo(Memo): """The :class:`HashMemo` which represents ``MEMO_HASH`` in a transaction. :param memo_hash: A 32 byte hash hex encoded string. :raises: :exc:`MemoInvalidException <stellar_sdk.exceptions.MemoInvalidException>`: if ``memo_hash`` is not a valid hash memo. """ def __init__(self, memo_hash: Union[bytes, str]) -> None: memo_hash = hex_to_bytes(memo_hash) length = len(memo_hash) if length != 32: raise MemoInvalidException( f"The length of HashMemo should be 32 bytes, got {length} bytes." ) self.memo_hash: bytes = memo_hash
[docs] @classmethod def from_xdr_object(cls, xdr_object: stellar_xdr.Memo) -> "HashMemo": """Returns an :class:`HashMemo` object from XDR memo object.""" assert xdr_object.hash is not None return cls(xdr_object.hash.hash)
[docs] def to_xdr_object(self) -> stellar_xdr.Memo: """Creates an XDR Memo object that represents this :class:`HashMemo`.""" return stellar_xdr.Memo( type=stellar_xdr.MemoType.MEMO_HASH, hash=stellar_xdr.Hash(self.memo_hash) )
def __hash__(self): return hash(self.memo_hash) def __eq__(self, other: object) -> bool: if not isinstance(other, self.__class__): return NotImplemented return self.memo_hash == other.memo_hash def __str__(self): return f"<HashMemo [memo={self.memo_hash}]>"
[docs] class ReturnHashMemo(Memo): """The :class:`ReturnHashMemo` which represents ``MEMO_RETURN`` in a transaction. MEMO_RETURN is typically used with refunds/returns over the network - it is a 32 byte hash intended to be interpreted as the hash of the transaction the sender is refunding. :param memo_return: A 32 byte hash or hex encoded string intended to be interpreted as the hash of the transaction the sender is refunding. :raises: :exc:`MemoInvalidException <stellar_sdk.exceptions.MemoInvalidException>`: if ``memo_return`` is not a valid return hash memo. """ def __init__(self, memo_return: Union[bytes, str]) -> None: memo_return = hex_to_bytes(memo_return) length = len(memo_return) if length != 32: raise MemoInvalidException( f"The length of ReturnHashMemo should be 32 bytes, got {length} bytes." ) self.memo_return: bytes = memo_return
[docs] @classmethod def from_xdr_object(cls, xdr_object: stellar_xdr.Memo) -> "ReturnHashMemo": """Returns an :class:`ReturnHashMemo` object from XDR memo object.""" assert xdr_object.ret_hash is not None return cls(xdr_object.ret_hash.hash)
[docs] def to_xdr_object(self) -> stellar_xdr.Memo: """Creates an XDR Memo object that represents this :class:`ReturnHashMemo`.""" return stellar_xdr.Memo( type=stellar_xdr.MemoType.MEMO_RETURN, ret_hash=stellar_xdr.Hash(self.memo_return), )
def __hash__(self): return hash(self.memo_return) def __eq__(self, other: object) -> bool: if not isinstance(other, self.__class__): return NotImplemented return self.memo_return == other.memo_return def __str__(self): return f"<ReturnHashMemo [memo={self.memo_return}]>"