stellar: operations layout

pull/25/head
Tomas Susanka 6 years ago
parent a26aaec953
commit 2af33a6893

@ -1,7 +1,11 @@
from trezor.messages import wire_types
from micropython import const
# source: https://github.com/stellar/go/blob/master/xdr/Stellar-transaction.x
STELLAR_CURVE = 'ed25519'
TX_TYPE = bytearray('\x00\x00\x00\x02')
# source: https://github.com/stellar/go/blob/3d2c1defe73dbfed00146ebe0e8d7e07ce4bb1b6/xdr/Stellar-transaction.x#L16
# Inflation not supported see https://github.com/trezor/trezor-core/issues/202#issuecomment-393342089
op_codes = {
'StellarAccountMergeOp': const(8),
'StellarAllowTrustOp': const(7),
@ -30,23 +34,36 @@ op_wire_types = [
wire_types.StellarSetOptionsOp,
]
ASSET_TYPE_CREDIT_ALPHANUM4 = const(1)
ASSET_TYPE_CREDIT_ALPHANUM12 = const(2)
ASSET_TYPE_NATIVE = const(0)
ASSET_TYPE_ALPHANUM4 = const(1)
ASSET_TYPE_ALPHANUM12 = const(2)
# https://www.stellar.org/developers/guides/concepts/accounts.html#balance
# https://github.com/stellar/go/blob/master/amount/main.go
# https://github.com/stellar/go/blob/3d2c1defe73dbfed00146ebe0e8d7e07ce4bb1b6/amount/main.go#L23
AMOUNT_DIVISIBILITY = const(7)
# https://github.com/stellar/go/blob/master/network/main.go
NETWORK_PASSPHRASE_PUBLIC = 'Public Global Stellar Network ; September 2015'
NETWORK_PASSPHRASE_TESTNET = 'Test SDF Network ; September 2015'
# https://www.stellar.org/developers/guides/concepts/accounts.html#flags
FLAG_AUTH_REQUIRED = const(1)
FLAG_AUTH_REVOCABLE = const(2)
FLAG_AUTH_IMMUTABLE = const(4)
FLAGS_MAX_SIZE = 7
# https://github.com/stellar/go/blob/e0ffe19f58879d3c31e2976b97a5bf10e13a337b/xdr/Stellar-transaction.x#L275
MEMO_TYPE_NONE = 0
MEMO_TYPE_TEXT = 1
MEMO_TYPE_ID = 2
MEMO_TYPE_HASH = 3
MEMO_TYPE_RETURN = 4
# https://github.com/stellar/go/blob/3d2c1defe73dbfed00146ebe0e8d7e07ce4bb1b6/xdr/xdr_generated.go#L156
SIGN_TYPE_ACCOUNT = const(0)
SIGN_TYPE_PRE_AUTH = const(1)
SIGN_TYPE_HASH = const(2)
def get_op_code(msg) -> int:
if msg.__qualname__ not in op_codes:

@ -4,13 +4,13 @@ from apps.stellar import helpers
from trezor import ui
from trezor.messages import ButtonRequestType
from trezor.ui.text import Text
from trezor.utils import chunks, format_amount
from trezor import utils
async def require_confirm_init(ctx, pubkey: bytes, network_passphrase: str):
content = Text('Confirm Stellar', ui.ICON_SEND,
ui.NORMAL, 'Initialize singing with',
ui.MONO, *format_address(pubkey),
ui.MONO, *split(format_address(pubkey)),
icon_color=ui.GREEN)
await require_confirm(ctx, content, ButtonRequestType.ConfirmOutput)
network = get_network_warning(network_passphrase)
@ -32,8 +32,7 @@ async def require_confirm_memo(ctx, memo_type: int, memo_text: str):
elif memo_type == consts.MEMO_TYPE_RETURN:
title = 'Memo (RETURN)'
else: # MEMO_TYPE_NONE
title = 'No memo set!'
# todo ugly
title = 'No memo set!' # todo format this as ui.NORMAL not MONO
memo_text = 'Important: Many exchanges require a memo when depositing'
content = Text('Confirm memo', ui.ICON_CONFIRM,
ui.BOLD, title,
@ -49,20 +48,40 @@ async def require_confirm_final(ctx, fee: int, num_operations: int):
content = Text('Final confirm', ui.ICON_SEND,
ui.NORMAL, 'Sign this transaction',
ui.NORMAL, 'made up of ' + op_str,
ui.BOLD, 'and pay ' + format_amount(fee, consts.AMOUNT_DIVISIBILITY) + ' XLM',
ui.BOLD, 'and pay ' + format_amount(fee),
ui.NORMAL, 'for fee?',
icon_color=ui.GREEN)
# we use SignTx, not ConfirmOutput, for compatibility with T1
await require_hold_to_confirm(ctx, content, ButtonRequestType.SignTx)
def format_amount(amount: int, ticker=True) -> str:
t = ''
if ticker:
t = ' XLM'
return utils.format_amount(amount, consts.AMOUNT_DIVISIBILITY) + t
def format_address(pubkey: bytes) -> str:
address = helpers.address_from_public_key(pubkey)
return split(address)
return helpers.address_from_public_key(pubkey)
def split(text):
return chunks(text, 17)
return utils.chunks(text, 17)
# todo merge with nem
def trim(payload: str, length: int, dots=True) -> str:
if len(payload) > length:
if dots:
return payload[:length - 2] + '..'
return payload[:length - 2]
return payload
# todo merge with nem
def trim_to_rows(payload: str, rows: int=1) -> str:
return trim(payload, rows * 17)
def get_network_warning(network_passphrase: str):

@ -1,192 +0,0 @@
from trezor.messages.StellarAccountMergeOp import StellarAccountMergeOp
from trezor.messages.StellarAssetType import StellarAssetType
from trezor.messages.StellarAllowTrustOp import StellarAllowTrustOp
from trezor.messages.StellarBumpSequenceOp import StellarBumpSequenceOp
from trezor.messages.StellarChangeTrustOp import StellarChangeTrustOp
from trezor.messages.StellarCreateAccountOp import StellarCreateAccountOp
from trezor.messages.StellarCreatePassiveOfferOp import StellarCreatePassiveOfferOp
from trezor.messages.StellarManageDataOp import StellarManageDataOp
from trezor.messages.StellarManageOfferOp import StellarManageOfferOp
from trezor.messages.StellarPathPaymentOp import StellarPathPaymentOp
from trezor.messages.StellarPaymentOp import StellarPaymentOp
from trezor.messages.StellarSetOptionsOp import StellarSetOptionsOp
from apps.stellar.consts import *
from apps.stellar.writers import *
# todo layout
def serialize_op(w, op):
_serialize_account(w, op.source_account)
write_uint32(w, get_op_code(op))
if isinstance(op, StellarAccountMergeOp):
serialize_account_merge_op(w, op)
elif isinstance(op, StellarAllowTrustOp):
serialize_allow_trust_op(w, op)
elif isinstance(op, StellarBumpSequenceOp):
serialize_bump_sequence_op(w, op)
elif isinstance(op, StellarChangeTrustOp):
serialize_change_trust_op(w, op)
elif isinstance(op, StellarCreateAccountOp):
serialize_create_account_op(w, op)
elif isinstance(op, StellarCreatePassiveOfferOp):
serialize_create_passive_offer_op(w, op)
elif isinstance(op, StellarManageDataOp):
serialize_manage_data_op(w, op)
elif isinstance(op, StellarManageOfferOp):
serialize_manage_offer_op(w, op)
elif isinstance(op, StellarPathPaymentOp):
serialize_path_payment_op(w, op)
elif isinstance(op, StellarPaymentOp):
serialize_payment_op(w, op)
elif isinstance(op, StellarSetOptionsOp):
serialize_set_options_op(w, op)
else:
raise ValueError('Stellar: unknown operation')
def serialize_account_merge_op(w, msg: StellarAccountMergeOp):
write_pubkey(w, msg.destination_account)
def serialize_allow_trust_op(w, msg: StellarAllowTrustOp):
# trustor account (the account being allowed to access the asset)
write_pubkey(w, msg.trusted_account)
write_uint32(w, msg.asset_type)
_serialize_asset_code(w, msg.asset_type, msg.asset_code)
write_bool(w, msg.is_authorized)
def serialize_bump_sequence_op(w, msg: StellarBumpSequenceOp):
write_uint64(w, msg.bump_to)
def serialize_change_trust_op(w, msg: StellarChangeTrustOp):
_serialize_asset(w, msg.asset)
write_uint64(w, msg.limit)
def serialize_create_account_op(w, msg: StellarCreateAccountOp):
write_pubkey(w, msg.new_account)
write_uint64(w, msg.starting_balance)
def serialize_create_passive_offer_op(w, msg: StellarCreatePassiveOfferOp):
_serialize_asset(w, msg.selling_asset)
_serialize_asset(w, msg.buying_asset)
write_uint64(w, msg.amount)
write_uint32(w, msg.price_n)
write_uint32(w, msg.price_d)
def serialize_manage_data_op(w, msg: StellarManageDataOp):
if len(msg.key) > 64:
raise ValueError('Stellar: max length of a key is 64 bytes')
write_string(w, msg.key)
write_bool(w, bool(msg.value))
if msg.value:
write_uint32(w, len(msg.value))
write_bytes(w, msg.value)
def serialize_manage_offer_op(w, msg: StellarManageOfferOp):
_serialize_asset(w, msg.selling_asset)
_serialize_asset(w, msg.buying_asset)
write_uint64(w, msg.amount) # amount to sell
write_uint32(w, msg.price_n) # numerator
write_uint32(w, msg.price_d) # denominator
write_uint64(w, msg.offer_id)
def serialize_path_payment_op(w, msg: StellarPathPaymentOp):
_serialize_asset(w, msg.send_asset)
write_uint64(w, msg.send_max)
write_pubkey(w, msg.destination_account)
_serialize_asset(w, msg.destination_asset)
write_uint64(w, msg.destination_amount)
write_uint32(w, len(msg.paths))
for p in msg.paths:
_serialize_asset(w, p)
def serialize_payment_op(w, msg: StellarPaymentOp):
write_pubkey(w, msg.destination_account)
_serialize_asset(w, msg.asset)
write_uint64(w, msg.amount)
def serialize_set_options_op(w, msg: StellarSetOptionsOp):
# inflation destination
write_bool(w, bool(msg.inflation_destination_account))
if msg.inflation_destination_account:
write_pubkey(w, msg.inflation_destination_account)
# clear flags
write_bool(w, bool(msg.clear_flags))
if msg.clear_flags:
write_uint32(w, msg.clear_flags)
# set flags
write_bool(w, bool(msg.set_flags))
if msg.set_flags:
write_uint32(w, msg.set_flags)
# account thresholds
write_bool(w, bool(msg.master_weight))
if msg.master_weight:
write_uint32(w, msg.master_weight)
write_bool(w, bool(msg.low_threshold))
if msg.low_threshold:
write_uint32(w, msg.low_threshold)
write_bool(w, bool(msg.medium_threshold))
if msg.medium_threshold:
write_uint32(w, msg.medium_threshold)
write_bool(w, bool(msg.high_threshold))
if msg.high_threshold:
write_uint32(w, msg.high_threshold)
# home domain
write_bool(w, bool(msg.home_domain))
if msg.home_domain:
if len(msg.home_domain) > 32:
raise ValueError('Stellar: max length of a home domain is 32 bytes')
write_string(w, msg.home_domain)
# signer
write_bool(w, bool(msg.signer_type))
if msg.signer_type:
# signer type
write_uint32(w, msg.signer_type)
write_bytes(w, msg.signer_key)
write_uint32(w, msg.signer_weight)
def _serialize_account(w, source_account: bytes):
if source_account is None:
write_bool(w, False)
return
write_pubkey(w, source_account)
def _serialize_asset_code(w, asset_type: int, asset_code: str):
code = bytearray(asset_code)
if asset_type == ASSET_TYPE_CREDIT_ALPHANUM4:
# pad with zeros to 4 chars
write_bytes(w, code + bytearray([0] * (4 - len(code))))
elif asset_type == ASSET_TYPE_CREDIT_ALPHANUM12:
# pad with zeros to 12 chars
write_bytes(w, code + bytearray([0] * (12 - len(code))))
else:
raise ValueError('Stellar: invalid asset type')
def _serialize_asset(w, asset: StellarAssetType):
write_uint32(w, asset.type)
_serialize_asset_code(w, asset.type, asset.code)
write_pubkey(w, asset.issuer)

@ -0,0 +1,44 @@
from apps.stellar.operations.serialize import *
from apps.stellar.operations.layout import *
async def operation(ctx, w, op):
if op.source_account:
await confirm_source_account(ctx, op.source_account)
serialize_account(w, op.source_account)
write_uint32(w, get_op_code(op))
if isinstance(op, StellarAccountMergeOp):
await confirm_account_merge_op(ctx, op)
serialize_account_merge_op(w, op)
elif isinstance(op, StellarAllowTrustOp):
await confirm_allow_trust_op(ctx, op)
serialize_allow_trust_op(w, op)
elif isinstance(op, StellarBumpSequenceOp):
await confirm_bump_sequence_op(ctx, op)
serialize_bump_sequence_op(w, op)
elif isinstance(op, StellarChangeTrustOp):
await confirm_change_trust_op(ctx, op)
serialize_change_trust_op(w, op)
elif isinstance(op, StellarCreateAccountOp):
await confirm_create_account_op(ctx, op)
serialize_create_account_op(w, op)
elif isinstance(op, StellarCreatePassiveOfferOp):
await confirm_create_passive_offer_op(ctx, op)
serialize_create_passive_offer_op(w, op)
elif isinstance(op, StellarManageDataOp):
await confirm_manage_data_op(ctx, op)
serialize_manage_data_op(w, op)
elif isinstance(op, StellarManageOfferOp):
await confirm_manage_offer_op(ctx, op)
serialize_manage_offer_op(w, op)
elif isinstance(op, StellarPathPaymentOp):
await confirm_path_payment_op(ctx, op)
serialize_path_payment_op(w, op)
elif isinstance(op, StellarPaymentOp):
await confirm_payment_op(ctx, op)
serialize_payment_op(w, op)
elif isinstance(op, StellarSetOptionsOp):
await confirm_set_options_op(ctx, op)
serialize_set_options_op(w, op)
else:
raise ValueError('Stellar: unknown operation')

@ -0,0 +1,267 @@
from apps.stellar.layout import *
from trezor.messages import ButtonRequestType
from trezor.ui.text import Text
from trezor.messages.StellarAccountMergeOp import StellarAccountMergeOp
from trezor.messages.StellarAssetType import StellarAssetType
from trezor.messages.StellarAllowTrustOp import StellarAllowTrustOp
from trezor.messages.StellarBumpSequenceOp import StellarBumpSequenceOp
from trezor.messages.StellarChangeTrustOp import StellarChangeTrustOp
from trezor.messages.StellarCreateAccountOp import StellarCreateAccountOp
from trezor.messages.StellarCreatePassiveOfferOp import StellarCreatePassiveOfferOp
from trezor.messages.StellarManageDataOp import StellarManageDataOp
from trezor.messages.StellarManageOfferOp import StellarManageOfferOp
from trezor.messages.StellarPathPaymentOp import StellarPathPaymentOp
from trezor.messages.StellarPaymentOp import StellarPaymentOp
from trezor.messages.StellarSetOptionsOp import StellarSetOptionsOp
from ubinascii import hexlify
async def confirm_source_account(ctx, source_account: bytes):
content = Text('Confirm operation', ui.ICON_CONFIRM,
ui.BOLD, 'Source account:',
ui.MONO, *split(format_address(source_account)),
icon_color=ui.GREEN)
await require_confirm(ctx, content, ButtonRequestType.ConfirmOutput)
async def confirm_allow_trust_op(ctx, op: StellarAllowTrustOp):
if op.is_authorized:
text = 'Allow'
else:
text = 'Revoke'
content = Text('Confirm operation', ui.ICON_CONFIRM,
ui.BOLD, text + ' Trust',
ui.NORMAL, "of '" + op.asset_code + "' by:",
ui.MONO, *split(trim_to_rows(format_address(op.trusted_account), 3)),
icon_color=ui.GREEN)
await require_confirm(ctx, content, ButtonRequestType.ConfirmOutput)
async def confirm_account_merge_op(ctx, op: StellarAccountMergeOp):
content = Text('Confirm operation', ui.ICON_CONFIRM,
ui.BOLD, 'Account Merge',
ui.NORMAL, 'All XLM will be sent to:',
ui.MONO, *split(trim_to_rows(format_address(op.destination_account), 3)),
icon_color=ui.GREEN)
await require_confirm(ctx, content, ButtonRequestType.ConfirmOutput)
async def confirm_bump_sequence_op(ctx, op: StellarBumpSequenceOp):
content = Text('Confirm operation', ui.ICON_CONFIRM,
ui.BOLD, 'Bump Sequence',
ui.NORMAL, 'Set sequence to',
ui.MONO, str(op.bump_to),
icon_color=ui.GREEN)
await require_confirm(ctx, content, ButtonRequestType.ConfirmOutput)
async def confirm_change_trust_op(ctx, op: StellarChangeTrustOp):
if op.limit == 0:
text = 'Delete'
else:
text = 'Add'
content = Text('Confirm operation', ui.ICON_CONFIRM,
ui.BOLD, text + ' Trust',
ui.NORMAL, 'Asset: ' + op.asset.code,
ui.NORMAL, 'Amount: ' + format_amount(op.limit, ticker=False),
icon_color=ui.GREEN)
await require_confirm(ctx, content, ButtonRequestType.ConfirmOutput)
await confirm_asset_issuer(ctx, op.asset)
async def confirm_create_account_op(ctx, op: StellarCreateAccountOp):
content = Text('Confirm operation', ui.ICON_CONFIRM,
ui.BOLD, 'Create Account',
ui.NORMAL, 'with ' + format_amount(op.starting_balance),
ui.MONO, *split(trim_to_rows(format_address(op.new_account), 3)),
icon_color=ui.GREEN)
await require_confirm(ctx, content, ButtonRequestType.ConfirmOutput)
async def confirm_create_passive_offer_op(ctx, op: StellarCreatePassiveOfferOp):
if op.amount == 0:
text = 'Delete Passive Offer'
else:
text = 'New Passive Offer'
await _confirm_offer(ctx, text, op)
async def confirm_manage_offer_op(ctx, op: StellarManageOfferOp):
if op.offer_id == 0:
text = 'New Offer'
else:
if op.amount == 0:
text = 'Delete'
else:
text = 'Update'
text += ' #' + str(op.offer_id)
await _confirm_offer(ctx, text, op)
# todo scale? this is inconsistent with T1 (op.amount should? use divisibility)
async def _confirm_offer(ctx, text, op):
content = Text('Confirm operation', ui.ICON_CONFIRM,
ui.BOLD, text,
ui.NORMAL, 'Sell ' + str(op.amount) + ' ' + op.selling_asset.code,
ui.NORMAL, 'For ' + str(op.price_n / op.price_d),
ui.NORMAL, 'Per ' + format_asset_code(op.buying_asset),
icon_color=ui.GREEN)
await require_confirm(ctx, content, ButtonRequestType.ConfirmOutput)
await confirm_asset_issuer(ctx, op.selling_asset)
await confirm_asset_issuer(ctx, op.buying_asset)
# todo done
async def confirm_manage_data_op(ctx, op: StellarManageDataOp):
from trezor.crypto.hashlib import sha256
if op.value:
text = 'Set'
else:
text = 'Clear'
content = Text('Confirm operation', ui.ICON_CONFIRM,
ui.BOLD, text + ' data value key',
ui.MONO, *split(op.key),
icon_color=ui.GREEN)
await require_confirm(ctx, content, ButtonRequestType.ConfirmOutput)
if op.value:
digest = sha256(op.value).digest()
digest_str = hexlify(digest).decode()
content = Text('Confirm operation', ui.ICON_CONFIRM,
ui.BOLD, ' Value (SHA-256):',
ui.MONO, *split(digest_str),
icon_color=ui.GREEN)
await require_confirm(ctx, content, ButtonRequestType.ConfirmOutput)
async def confirm_path_payment_op(ctx, op: StellarPathPaymentOp):
content = Text('Confirm operation', ui.ICON_CONFIRM,
ui.BOLD, 'Path Pay ' + format_amount(op.destination_amount, ticker=False),
ui.BOLD, format_asset_code(op.destination_asset) + ' to:',
ui.MONO, *split(trim_to_rows(format_address(op.destination_account), 3)),
icon_color=ui.GREEN)
await require_confirm(ctx, content, ButtonRequestType.ConfirmOutput)
await confirm_asset_issuer(ctx, op.destination_asset)
# confirm what the sender is using to pay
content = Text('Confirm operation', ui.ICON_CONFIRM,
ui.NORMAL, 'Pay using',
ui.BOLD, format_amount(op.send_max, ticker=False),
ui.BOLD, format_asset_code(op.send_asset),
ui.NORMAL, 'This amount is debited',
ui.NORMAL, 'from your account.',
icon_color=ui.GREEN)
await require_confirm(ctx, content, ButtonRequestType.ConfirmOutput)
await confirm_asset_issuer(ctx, op.send_asset)
async def confirm_payment_op(ctx, op: StellarPaymentOp):
content = Text('Confirm operation', ui.ICON_CONFIRM,
ui.BOLD, 'Pay ' + format_amount(op.amount, ticker=False),
ui.BOLD, format_asset_code(op.asset) + ' to:',
ui.MONO, *split(trim_to_rows(format_address(op.destination_account), 3)),
icon_color=ui.GREEN)
await require_confirm(ctx, content, ButtonRequestType.ConfirmOutput)
await confirm_asset_issuer(ctx, op.asset)
async def confirm_set_options_op(ctx, op: StellarSetOptionsOp):
if op.inflation_destination_account:
content = Text('Confirm operation', ui.ICON_CONFIRM,
ui.BOLD, 'Set Inflation Destination',
ui.MONO, *split(format_address(op.inflation_destination_account)),
icon_color=ui.GREEN)
await require_confirm(ctx, content, ButtonRequestType.ConfirmOutput)
if op.clear_flags:
text = _format_flags(op.clear_flags)
content = Text('Confirm operation', ui.ICON_CONFIRM,
ui.BOLD, 'Clear Flags',
ui.MONO, *text,
icon_color=ui.GREEN)
await require_confirm(ctx, content, ButtonRequestType.ConfirmOutput)
if op.set_flags:
text = _format_flags(op.set_flags)
content = Text('Confirm operation', ui.ICON_CONFIRM,
ui.BOLD, 'Set Flags',
ui.MONO, *text,
icon_color=ui.GREEN)
await require_confirm(ctx, content, ButtonRequestType.ConfirmOutput)
thresholds = _format_thresholds(op)
if thresholds:
content = Text('Confirm operation', ui.ICON_CONFIRM,
ui.BOLD, 'Account Thresholds',
ui.MONO, *thresholds,
icon_color=ui.GREEN)
await require_confirm(ctx, content, ButtonRequestType.ConfirmOutput)
if op.home_domain:
content = Text('Confirm operation', ui.ICON_CONFIRM,
ui.BOLD, 'Home Domain',
ui.MONO, *split(op.home_domain),
icon_color=ui.GREEN)
await require_confirm(ctx, content, ButtonRequestType.ConfirmOutput)
if op.signer_type is not None:
if op.signer_weight > 0:
text = 'Add Signer'
else:
text = 'Remove Signer'
if op.signer_type == consts.SIGN_TYPE_ACCOUNT:
text += ' (acc)'
content = Text('Confirm operation', ui.ICON_CONFIRM,
ui.BOLD, text,
ui.MONO, *split(format_address(op.signer_key)),
icon_color=ui.GREEN)
await require_confirm(ctx, content, ButtonRequestType.ConfirmOutput)
elif op.signer_type in [consts.SIGN_TYPE_PRE_AUTH, consts.SIGN_TYPE_HASH]:
if op.signer_type == consts.SIGN_TYPE_PRE_AUTH:
text += ' (auth)'
else:
text += ' (hash)'
content = Text('Confirm operation', ui.ICON_CONFIRM,
ui.BOLD, text,
ui.MONO, *split(hexlify(op.signer_key).decode()),
icon_color=ui.GREEN)
await require_confirm(ctx, content, ButtonRequestType.ConfirmOutput)
else:
raise ValueError('Stellar: invalid signer type')
def _format_thresholds(op: StellarSetOptionsOp) -> ():
text = ()
if op.master_weight is not None:
text += ('Master Weight: ' + str(op.master_weight), )
if op.low_threshold is not None:
text += ('Low: ' + str(op.low_threshold), )
if op.medium_threshold is not None:
text += ('Medium: ' + str(op.medium_threshold), )
if op.high_threshold is not None:
text += ('High: ' + str(op.high_threshold), )
return text
def _format_flags(flags: int) -> ():
if flags > consts.FLAGS_MAX_SIZE:
raise ValueError('Stellar: invalid')
text = ()
if flags & consts.FLAG_AUTH_REQUIRED:
text += ('AUTH_REQUIRED', )
if flags & consts.FLAG_AUTH_REVOCABLE:
text += ('AUTH_REVOCABLE', )
if flags & consts.FLAG_AUTH_IMMUTABLE:
text += ('AUTH_IMMUTABLE', )
return text
def format_asset_code(asset: StellarAssetType) -> str:
if asset is None or asset.type == consts.ASSET_TYPE_NATIVE:
return 'XLM (native)'
return asset.code
async def confirm_asset_issuer(ctx, asset: StellarAssetType):
if asset is None or asset.type == consts.ASSET_TYPE_NATIVE:
return
content = Text('Confirm issuer', ui.ICON_CONFIRM,
ui.BOLD, asset.code + ' issuer:',
ui.MONO, *split(format_address(asset.issuer)),
icon_color=ui.GREEN)
await require_confirm(ctx, content, ButtonRequestType.ConfirmOutput)
Loading…
Cancel
Save