Source code for stellar_sdk.asset

import re
from typing import Dict, Optional, Type, Union

from . import xdr as stellar_xdr
from .exceptions import AssetCodeInvalidError, AssetIssuerInvalidError
from .keypair import Keypair
from .network import Network
from .strkey import StrKey
from .utils import sha256

__all__ = ["Asset"]


[docs] class Asset: """The :class:`Asset` object, which represents an asset and its corresponding issuer on the Stellar network. The following example shows how to create an Asset object:: from stellar_sdk import Asset native_asset = Asset.native() # You can also create a native asset through Asset("XLM"). credit_alphanum4_asset = Asset("USD", "GBSKJPM2FM6O2C6GVZNAUAMGXZ6I4QIUPMNWVDN2NZULPWWTV3GI2SOX") credit_alphanum12_asset = Asset("BANANA", "GA6VT2PDD73TNNRYLPJPJYAAI7EGKBATZ7V562S7XY7TJD4GNOXRG6OS") print(f"Asset type: {credit_alphanum4_asset.type}\\n" f"Asset code: {credit_alphanum4_asset.code}\\n" f"Asset issuer: {credit_alphanum4_asset.issuer}\\n" f"Is native asset: {credit_alphanum4_asset.is_native()}") For more information about the formats used for asset codes and how issuers work on Stellar's network, see `Stellar's guide on assets`_. :param code: The asset code, in the formats specified in `Stellar's guide on assets`_. :param issuer: The account ID of the issuer. Note if the currency is the native currency (XLM (Lumens)), no issuer is necessary. :raises: | :exc:`AssetCodeInvalidError <stellar_sdk.exceptions.AssetCodeInvalidError>`: if ``code`` is invalid. | :exc:`AssetIssuerInvalidError <stellar_sdk.exceptions.AssetIssuerInvalidError>`: if ``issuer`` is not a valid ed25519 public key. .. _Stellar's guide on assets: https://developers.stellar.org/docs/glossary/assets/ """ def __init__(self, code: str, issuer: Optional[str] = None) -> None: Asset.check_if_asset_code_is_valid(code) if code != "XLM" and issuer is None: raise AssetIssuerInvalidError( "The issuer cannot be `None` except for the native asset." ) if issuer is not None and not StrKey.is_valid_ed25519_public_key(issuer): raise AssetIssuerInvalidError("The issuer should be a correct public key.") self.code: str = code self.issuer: Optional[str] = issuer self._type: str = self.guess_asset_type()
[docs] @staticmethod def check_if_asset_code_is_valid(code: str) -> None: """Check whether the `code` passed in by the user is a valid asset code, if not, an exception will be thrown. :param code: The asset code. :raises: :exc:`AssetCodeInvalidError <stellar_sdk.exceptions.AssetCodeInvalidError>`: if ``code`` is invalid. """ asset_code_re = re.compile(r"^[a-zA-Z0-9]{1,12}$") if not asset_code_re.match(code): raise AssetCodeInvalidError( "Asset code is invalid (maximum alphanumeric, 12 characters at max)." )
@property def type(self) -> str: """Return the type of the asset, can be one of following types: `native`, `credit_alphanum4` or `credit_alphanum12` :return: The type of the asset. """ return self._type @type.setter def type(self, v) -> None: raise AttributeError("Asset type is immutable.")
[docs] def contract_id(self, network_passphrase: str) -> str: """Return the contract Id for the asset contract. :param network_passphrase: The network where the asset is located. :return: The contract Id for the asset contract. """ network_id_hash = stellar_xdr.Hash(Network(network_passphrase).network_id()) preimage = stellar_xdr.HashIDPreimage( stellar_xdr.EnvelopeType.ENVELOPE_TYPE_CONTRACT_ID, contract_id=stellar_xdr.HashIDPreimageContractID( network_id=network_id_hash, contract_id_preimage=stellar_xdr.ContractIDPreimage( stellar_xdr.ContractIDPreimageType.CONTRACT_ID_PREIMAGE_FROM_ASSET, from_asset=self.to_xdr_object(), ), ), ) contract_id = sha256(preimage.to_xdr_bytes()) return StrKey.encode_contract(contract_id)
[docs] def guess_asset_type(self) -> str: """Return the type of the asset, Can be one of following types: ``native``, ``credit_alphanum4`` or ``credit_alphanum12``. :return: The type of the asset. """ if self.code == "XLM" and self.issuer is None: asset_type = "native" elif len(self.code) > 4: asset_type = "credit_alphanum12" else: asset_type = "credit_alphanum4" return asset_type
[docs] def to_dict(self) -> dict: """Generate a dict for this object's attributes. :return: A dict representing an :class:`Asset` """ rv: Dict[str, Optional[str]] = {"type": self.type} if not self.is_native(): rv["code"] = self.code rv["issuer"] = self.issuer return rv
[docs] @classmethod def native(cls) -> "Asset": """Returns an asset object for the native asset. :return: An asset object for the native asset. """ return cls("XLM")
[docs] def is_native(self) -> bool: """Return ``Ture`` if the :class:`Asset` object is the native asset. :return: ``True`` if the asset object is native, ``False`` otherwise. """ return self.issuer is None
[docs] def to_xdr_object(self) -> stellar_xdr.Asset: """Returns the xdr object for this asset. :return: XDR Asset object """ asset_xdr = self._to_xdr_object(stellar_xdr.Asset) assert isinstance(asset_xdr, stellar_xdr.Asset) return asset_xdr
[docs] def to_change_trust_asset_xdr_object(self) -> stellar_xdr.ChangeTrustAsset: """Returns the xdr object for this asset. :return: XDR ChangeTrustAsset object """ asset_xdr = self._to_xdr_object(stellar_xdr.ChangeTrustAsset) assert isinstance(asset_xdr, stellar_xdr.ChangeTrustAsset) return asset_xdr
[docs] def to_trust_line_asset_xdr_object(self) -> stellar_xdr.TrustLineAsset: """Returns the xdr object for this asset. :return: XDR TrustLineAsset object """ asset_xdr = self._to_xdr_object(stellar_xdr.TrustLineAsset) assert isinstance(asset_xdr, stellar_xdr.TrustLineAsset) return asset_xdr
def _to_xdr_object( self, xdr_asset: Union[ Type[stellar_xdr.Asset], Type[stellar_xdr.ChangeTrustAsset], Type[stellar_xdr.TrustLineAsset], ], ) -> Union[ stellar_xdr.Asset, stellar_xdr.ChangeTrustAsset, stellar_xdr.TrustLineAsset ]: if self.is_native(): asset_type = stellar_xdr.AssetType.ASSET_TYPE_NATIVE return xdr_asset(type=asset_type) else: assert self.issuer is not None length = len(self.code) pad_length = 4 - length if length <= 4 else 12 - length asset_code = bytearray(self.code, "ascii") + b"\x00" * pad_length issuer = Keypair.from_public_key(self.issuer).xdr_account_id() if length <= 4: xdr_type = stellar_xdr.AssetType.ASSET_TYPE_CREDIT_ALPHANUM4 asset_code4 = stellar_xdr.AssetCode4(asset_code) alpha_num4 = stellar_xdr.AlphaNum4(asset_code4, issuer) return xdr_asset(type=xdr_type, alpha_num4=alpha_num4) else: xdr_type = stellar_xdr.AssetType.ASSET_TYPE_CREDIT_ALPHANUM12 asset_code12 = stellar_xdr.AssetCode12(asset_code) alpha_num12 = stellar_xdr.AlphaNum12(asset_code12, issuer) return xdr_asset(type=xdr_type, alpha_num12=alpha_num12)
[docs] @classmethod def from_xdr_object( cls, xdr_object: Union[ stellar_xdr.Asset, stellar_xdr.ChangeTrustAsset, stellar_xdr.TrustLineAsset ], ) -> "Asset": """Create a :class:`Asset` from an XDR Asset/ChangeTrustAsset/TrustLineAsset object. Please note that this function only supports processing the following types of assets: - ASSET_TYPE_NATIVE - ASSET_TYPE_CREDIT_ALPHANUM4 - ASSET_TYPE_CREDIT_ALPHANUM12 :param xdr_object: The XDR Asset/ChangeTrustAsset/TrustLineAsset object. :return: A new :class:`Asset` object from the given XDR object. """ if xdr_object.type == stellar_xdr.AssetType.ASSET_TYPE_NATIVE: return Asset.native() elif xdr_object.type == stellar_xdr.AssetType.ASSET_TYPE_CREDIT_ALPHANUM4: assert xdr_object.alpha_num4 is not None assert xdr_object.alpha_num4.issuer.account_id.ed25519 is not None issuer = StrKey.encode_ed25519_public_key( xdr_object.alpha_num4.issuer.account_id.ed25519.uint256 ) code = xdr_object.alpha_num4.asset_code.asset_code4.decode().rstrip("\x00") elif xdr_object.type == stellar_xdr.AssetType.ASSET_TYPE_CREDIT_ALPHANUM12: assert xdr_object.alpha_num12 is not None assert xdr_object.alpha_num12.issuer.account_id.ed25519 is not None issuer = StrKey.encode_ed25519_public_key( xdr_object.alpha_num12.issuer.account_id.ed25519.uint256 ) code = xdr_object.alpha_num12.asset_code.asset_code12.decode().rstrip( "\x00" ) else: raise ValueError(f"Invalid asset type: {xdr_object.type}") return cls(code, issuer)
def __hash__(self) -> int: return hash((self.type, self.code, self.issuer)) def __eq__(self, other: object) -> bool: if not isinstance(other, self.__class__): return NotImplemented return self.code == other.code and self.issuer == other.issuer def __str__(self): return f"<Asset [code={self.code}, issuer={self.issuer}, type={self.type}]>"