async def _slip39_show_share_words(ctx, share_index, share_words): first, chunks, last = _slip39_split_share_into_pages(share_words) if share_index is None: header_title = "Recovery seed" else: header_title = "Recovery share #%s" % (share_index + 1) header_icon = ui.ICON_RESET pages = [] # ui page components shares_words_check = [] # check we display correct data # first page text = Text(header_title, header_icon) text.bold("Write down these") text.bold("%s words:" % len(share_words)) text.br_half() for index, word in first: text.mono("%s. %s" % (index + 1, word)) shares_words_check.append(word) pages.append(text) # middle pages for chunk in chunks: text = Text(header_title, header_icon) for index, word in chunk: text.mono("%s. %s" % (index + 1, word)) shares_words_check.append(word) pages.append(text) # last page text = Text(header_title, header_icon) for index, word in last: text.mono("%s. %s" % (index + 1, word)) shares_words_check.append(word) text.br_half() text.bold("I confirm that I wrote") text.bold("down all %s words." % len(share_words)) pages.append(text) # pagination paginated = Paginated(pages) if __debug__: word_pages = [first] + chunks + [last] def export_displayed_words(): # export currently displayed mnemonic words into debuglink debug.reset_current_words = [ w for _, w in word_pages[paginated.page] ] paginated.on_change = export_displayed_words export_displayed_words() # make sure we display correct data utils.ensure(share_words == shares_words_check) # confirm the share await hold_to_confirm(ctx, paginated) # TODO: customize the loader here
async def show_address( ctx: wire.Context, address: str, address_type: EnumTypeCardanoAddressType, path: List[int], network: int = None, ) -> bool: """ Custom show_address function is needed because cardano addresses don't fit on a single screen. """ path_str = address_n_to_str(path) t1 = Text(path_str, ui.ICON_RECEIVE, ui.GREEN) if network is not None: t1.normal("%s network" % protocol_magics.to_ui_string(network)) t1.normal("%s address" % ADDRESS_TYPE_NAMES[address_type]) address_lines = list(chunks(address, 17)) t1.bold(address_lines[0]) t1.bold(address_lines[1]) t1.bold(address_lines[2]) pages = [t1] + _paginate_lines(address_lines, 3, path_str, ui.ICON_RECEIVE) return await confirm( ctx, Paginated(pages), code=ButtonRequestType.Address, cancel="QR", cancel_style=ButtonDefault, )
async def require_confirm_transfer(ctx, msg: BinanceTransferMsg): def make_input_output_pages(msg: BinanceInputOutput, direction): pages = [] for coin in msg.coins: coin_page = Text("Confirm " + direction, ui.ICON_SEND, icon_color=ui.GREEN) coin_page.bold( format_amount(coin.amount, helpers.DECIMALS) + " " + coin.denom) coin_page.normal("to") coin_page.mono(*split_address(msg.address)) pages.append(coin_page) return pages pages = [] for txinput in msg.inputs: pages.extend(make_input_output_pages(txinput, "input")) for txoutput in msg.outputs: pages.extend(make_input_output_pages(txoutput, "output")) return await hold_to_confirm(ctx, Paginated(pages), ButtonRequestType.ConfirmOutput)
async def show_remaining_shares( ctx: wire.Context, groups: List[int, List[str]], # remaining + list 3 words shares_remaining: List[int], group_threshold: int, ) -> None: pages = [] for remaining, group in groups: if 0 < remaining < MAX_SHARE_COUNT: text = Text("Remaining Shares") if remaining > 1: text.bold("%s more shares starting" % remaining) else: text.bold("%s more share starting" % remaining) for word in group: text.normal(word) pages.append(text) elif (remaining == MAX_SHARE_COUNT and shares_remaining.count(0) < group_threshold): text = Text("Remaining Shares") groups_remaining = group_threshold - shares_remaining.count(0) if groups_remaining > 1: text.bold("%s more groups starting" % groups_remaining) elif groups_remaining > 0: text.bold("%s more group starting" % groups_remaining) for word in group: text.normal(word) pages.append(text) return await confirm(ctx, Paginated(pages), cancel=None)
async def confirm_stake_pool_owners( ctx: wire.Context, keychain: seed.keychain, owners: List[CardanoPoolOwnerType], network_id: int, ) -> None: pages = [] for index, owner in enumerate(owners, 1): page = Text("Confirm transaction", ui.ICON_SEND, ui.GREEN) page.normal("Pool owner #%d:" % (index)) if owner.staking_key_path: page.bold(address_n_to_str(owner.staking_key_path)) page.normal( encode_human_readable_address( pack_reward_address_bytes( get_public_key_hash(keychain, owner.staking_key_path), network_id, ))) else: page.bold( encode_human_readable_address( pack_reward_address_bytes(owner.staking_key_hash, network_id))) pages.append(page) await require_confirm(ctx, Paginated(pages))
async def show_remaining_shares( ctx: wire.GenericContext, groups: Iterable[Tuple[int, Tuple[str, ...]]], # remaining + list 3 words shares_remaining: List[int], group_threshold: int, ) -> None: pages = [] # type: List[ui.Component] for remaining, group in groups: if 0 < remaining < MAX_SHARE_COUNT: text = Text("Remaining Shares") text.bold( strings.format_plural("{count} more {plural} starting", remaining, "share")) for word in group: text.normal(word) pages.append(text) elif (remaining == MAX_SHARE_COUNT and shares_remaining.count(0) < group_threshold): text = Text("Remaining Shares") groups_remaining = group_threshold - shares_remaining.count(0) text.bold( strings.format_plural("{count} more {plural} starting", groups_remaining, "group")) for word in group: text.normal(word) pages.append(text) await confirm(ctx, Paginated(pages), cancel=None)
async def confirm_transaction(ctx, amount: int, fee: int, protocol_magic: int, ttl: int, has_metadata: bool) -> None: pages = [] page1 = Text("Confirm transaction", ui.ICON_SEND, ui.GREEN) page1.normal("Transaction amount:") page1.bold(format_coin_amount(amount)) page1.normal("Transaction fee:") page1.bold(format_coin_amount(fee)) pages.append(page1) page2 = Text("Confirm transaction", ui.ICON_SEND, ui.GREEN) page2.normal("Network:") page2.bold(protocol_magics.to_ui_string(protocol_magic)) page2.normal("Transaction TTL:") page2.bold(str(ttl)) pages.append(page2) if has_metadata: page3 = Text("Confirm transaction", ui.ICON_SEND, ui.GREEN) page3.normal("Transaction contains") page3.normal("metadata") pages.append(page3) await require_hold_to_confirm(ctx, Paginated(pages))
async def show_warning_address_foreign_staking_key( ctx: wire.Context, account_path: List[int], staking_account_path: List[int], staking_key_hash: bytes, ) -> None: page1 = Text("Warning", ui.ICON_WRONG, ui.RED) page1.normal("Stake rights associated") page1.normal("with this address do") page1.normal("not match your") page1.normal("account %s:" % format_account_number(account_path)) page1.bold(address_n_to_str(account_path)) page2 = Text("Warning", ui.ICON_WRONG, ui.RED) if staking_account_path: page2.normal("Stake account %s:" % format_account_number(staking_account_path)) page2.bold(address_n_to_str(staking_account_path)) page2.br_half() else: page2.normal("Staking key:") page2.bold(hexlify(staking_key_hash).decode()) page2.normal("Continue?") await require_confirm(ctx, Paginated([page1, page2]))
async def confirm_transaction( ctx, amount: int, fee: int, protocol_magic: int, ttl: Optional[int], validity_interval_start: Optional[int], has_metadata: bool, is_network_id_verifiable: bool, ) -> None: pages = [] page1 = Text("Confirm transaction", ui.ICON_SEND, ui.GREEN) page1.normal("Transaction amount:") page1.bold(format_coin_amount(amount)) page1.normal("Transaction fee:") page1.bold(format_coin_amount(fee)) pages.append(page1) page2 = Text("Confirm transaction", ui.ICON_SEND, ui.GREEN) if is_network_id_verifiable: page2.normal("Network:") page2.bold(protocol_magics.to_ui_string(protocol_magic)) page2.normal("Valid since: %s" % format_optional_int(validity_interval_start)) page2.normal("TTL: %s" % format_optional_int(ttl)) pages.append(page2) if has_metadata: page3 = Text("Confirm transaction", ui.ICON_SEND, ui.GREEN) page3.normal("Transaction contains") page3.normal("metadata") pages.append(page3) await require_hold_to_confirm(ctx, Paginated(pages))
async def confirm_sending_token_hex(ctx: wire.Context, token: CardanoTokenType, token_number: int) -> None: page1 = Text("Confirm transaction", ui.ICON_SEND, ui.GREEN) page1.bold("Asset #%s name (hex):" % (token_number)) page1.mono(hexlify(token.asset_name_bytes).decode()) page2 = Text("Confirm transaction", ui.ICON_SEND, ui.GREEN) page2.normal("Amount sent:") page2.bold(format_amount(token.amount, 0)) await require_confirm(ctx, Paginated([page1, page2]))
async def _require_confirm_paginated(ctx, header, fields, per_page): pages = [] for page in chunks(fields, per_page): if header == "Arbitrary data": text = Text(header, ui.ICON_WIPE, ui.RED) else: text = Text(header, ui.ICON_CONFIRM, ui.GREEN) text.mono(*page) pages.append(text) await require_confirm(ctx, Paginated(pages), ButtonRequestType.ConfirmOutput)
async def _bip39_show_mnemonic(ctx, words: list): # split mnemonic words into pages PER_PAGE = const(4) words = list(enumerate(words)) words = list(utils.chunks(words, PER_PAGE)) # display the pages, with a confirmation dialog on the last one pages = [_get_mnemonic_page(page) for page in words] paginated = Paginated(pages) if __debug__: def export_displayed_words(): # export currently displayed mnemonic words into debuglink debug.reset_current_words = [w for _, w in words[paginated.page]] paginated.on_change = export_displayed_words export_displayed_words() await hold_to_confirm(ctx, paginated, ButtonRequestType.ResetDevice)
async def _require_confirm_properties(ctx, definition: NEMMosaicDefinition): # TODO: we should send a button request here pages = _get_mosaic_properties(definition) pages[-1] = Confirm(pages[-1]) paginated = Paginated(pages) if __debug__: result = await ctx.wait(paginated, confirm_signal) else: result = await ctx.wait(paginated) if result is not CONFIRMED: raise wire.ActionCancelled("Action cancelled")
async def confirm_transaction(ctx, amount, fee, network_name): t1 = Text("Confirm transaction", ui.ICON_SEND, ui.GREEN) t1.normal("Total amount:") t1.bold(format_coin_amount(amount)) t1.normal("including fee:") t1.bold(format_coin_amount(fee)) t2 = Text("Confirm transaction", ui.ICON_SEND, ui.GREEN) t2.normal("Network:") t2.bold(network_name) await require_hold_to_confirm(ctx, Paginated([t1, t2]))
async def confirm_transaction(ctx, amount: int, fee: int, protocol_magic: int): t1 = Text("Confirm transaction", ui.ICON_SEND, ui.GREEN) t1.normal("Transaction amount:") t1.bold(format_coin_amount(amount)) t1.normal("Transaction fee:") t1.bold(format_coin_amount(fee)) t2 = Text("Confirm transaction", ui.ICON_SEND, ui.GREEN) t2.normal("Network:") t2.bold(protocol_magics.to_ui_string(protocol_magic)) await require_hold_to_confirm(ctx, Paginated([t1, t2]))
async def confirm_sending(ctx: wire.Context, amount: int, to: str): t1 = Text("Confirm transaction", ui.ICON_SEND, ui.GREEN) t1.normal("Confirm sending:") t1.bold(format_coin_amount(amount)) t1.normal("to") to_lines = list(chunks(to, 17)) t1.bold(to_lines[0]) pages = [t1] + _paginate_lines(to_lines, 1, "Confirm transaction", ui.ICON_SEND) await require_confirm(ctx, Paginated(pages))
async def require_confirm_cancel(ctx, msg: BinanceCancelMsg): page1 = Text("Confirm cancel 1/2", ui.ICON_SEND, icon_color=ui.GREEN) page1.normal("Sender address:") page1.bold(msg.sender) page1.normal("Pair:") page1.bold(msg.symbol) page2 = Text("Confirm cancel 2/2", ui.ICON_SEND, icon_color=ui.GREEN) page2.normal("Order ID:") page2.bold(msg.refid) return await hold_to_confirm(ctx, Paginated([page1, page2]), ButtonRequestType.SignTx)
async def show_address( ctx: wire.Context, address: str, address_type: EnumTypeCardanoAddressType, path: List[int], network: str = None, ) -> bool: """ Custom show_address function is needed because cardano addresses don't fit on a single screen. """ address_type_label = "%s address" % ADDRESS_TYPE_NAMES[address_type] page1 = Text(address_type_label, ui.ICON_RECEIVE, ui.GREEN) lines_per_page = 5 lines_used_on_first_page = 0 # assemble first page to be displayed (path + network + whatever part of the address fits) if network is not None: page1.normal("%s network" % network) lines_used_on_first_page += 1 path_str = address_n_to_str(path) page1.mono(path_str) lines_used_on_first_page = min( lines_used_on_first_page + math.ceil(len(path_str) / _MAX_MONO_LINE), lines_per_page, ) address_lines = list(chunks(address, 17)) for address_line in address_lines[:lines_per_page - lines_used_on_first_page]: page1.bold(address_line) # append remaining pages containing the rest of the address pages = [page1] + _paginate_lines( address_lines, lines_per_page - lines_used_on_first_page, address_type_label, ui.ICON_RECEIVE, lines_per_page, ) return await confirm( ctx, Paginated(pages), code=ButtonRequestType.Address, cancel="QR", cancel_style=ButtonDefault, )
async def show_xpub(ctx: wire.Context, xpub: str, desc: str, cancel: str) -> bool: pages = [] # type: List[ui.Component] for lines in chunks(list(chunks(xpub, 16)), 5): text = Text(desc, ui.ICON_RECEIVE, ui.GREEN) text.mono(*lines) pages.append(text) return await confirm( ctx, Paginated(pages), code=ButtonRequestType.PublicKey, cancel=cancel, cancel_style=ButtonDefault, )
async def require_confirm_proposals(ctx, proposals): if len(proposals) > 1: title = "Submit proposals" else: title = "Submit proposal" pages = [] for page, proposal in enumerate(proposals): text = Text(title, ui.ICON_SEND, icon_color=ui.PURPLE) text.bold("Proposal {}: ".format(page + 1)) text.mono(*split_proposal(proposal)) pages.append(text) paginated = Paginated(pages) await require_confirm(ctx, paginated, ButtonRequestType.SignTx)
async def show_warning_tx_different_staking_account( ctx: wire.Context, staking_account_path: List[int], amount: int, ): t1 = Text("Confirm transaction", ui.ICON_SEND, ui.GREEN) t1.normal("Change address staking") t1.normal("rights do not match") t1.normal("the current account.") t2 = Text("Confirm transaction", ui.ICON_SEND, ui.GREEN) t2.normal("Staking account:") t2.bold(address_n_to_str(staking_account_path)) t2.normal("Change amount:") t2.bold(format_coin_amount(amount)) await require_confirm(ctx, Paginated([t1, t2]))
async def show_warning_tx_staking_key_hash( ctx: wire.Context, staking_key_hash: bytes, amount: int, ): t1 = Text("Confirm transaction", ui.ICON_SEND, ui.GREEN) t1.normal("Change address staking") t1.normal("rights do not match") t1.normal("the current account.") t2 = Text("Confirm transaction", ui.ICON_SEND, ui.GREEN) t2.normal("Staking key hash:") t2.mono(*chunks(hexlify(staking_key_hash), 17)) t3 = Text("Confirm transaction", ui.ICON_SEND, ui.GREEN) t3.normal("Change amount:") t3.bold(format_coin_amount(amount)) await require_confirm(ctx, Paginated([t1, t2, t3]))
async def confirm_certificate(ctx: wire.Context, certificate: CardanoTxCertificateType) -> bool: pages = [] t1 = Text("Confirm transaction", ui.ICON_SEND, ui.GREEN) t1.normal("Confirm:") t1.bold(CERTIFICATE_TYPE_NAMES[certificate.type]) t1.normal("for account:") t1.bold(address_n_to_str(to_account_path(certificate.path))) pages.append(t1) if certificate.type == CardanoCertificateType.STAKE_DELEGATION: t2 = Text("Confirm transaction", ui.ICON_SEND, ui.GREEN) t2.normal("to pool:") t2.bold(hexlify(certificate.pool).decode()) pages.append(t2) await require_confirm(ctx, Paginated(pages))
async def show_warning_tx_pointer_address( ctx: wire.Context, pointer: CardanoBlockchainPointerType, amount: int, ): t1 = Text("Confirm transaction", ui.ICON_SEND, ui.GREEN) t1.normal("Change address has a") t1.normal("pointer with staking") t1.normal("rights.") t2 = Text("Confirm transaction", ui.ICON_SEND, ui.GREEN) t2.normal("Pointer:") t2.bold( "%s, %s, %s" % (pointer.block_index, pointer.tx_index, pointer.certificate_index) ) t2.normal("Change amount:") t2.bold(format_coin_amount(amount)) await require_confirm(ctx, Paginated([t1, t2]))
def paginate_text( text: str, header: str, font: int = ui.NORMAL, header_icon: str = ui.ICON_DEFAULT, icon_color: int = ui.ORANGE_ICON, break_words: bool = False, ) -> Union[Text, Paginated]: span = Span(text, 0, font, break_words=break_words) if span.count_lines() <= TEXT_MAX_LINES: result = Text( header, header_icon=header_icon, icon_color=icon_color, new_lines=False, ) result.content = [font, text] return result else: pages: List[ui.Component] = [] span.reset(text, 0, font, break_words=break_words, line_width=204) while span.has_more_content(): # advance to first line of the page span.next_line() page = Text( header, header_icon=header_icon, icon_color=icon_color, new_lines=False, content_offset=0, char_offset=span.start, line_width=204, render_page_overflow=False, ) page.content = [font, text] pages.append(page) # roll over the remaining lines on the page for _ in range(TEXT_MAX_LINES - 1): span.next_line() return Paginated(pages)
async def require_confirm_order(ctx, msg: BinanceOrderMsg): page1 = Text("Confirm order", ui.ICON_SEND, icon_color=ui.GREEN) page1.normal("Sender address:") page1.bold(msg.sender) page2 = Text("Confirm order", ui.ICON_SEND, icon_color=ui.GREEN) page2.normal("side:") if msg.side == BinanceOrderSide.BUY: page2.bold("buy") elif msg.side == BinanceOrderSide.SELL: page2.bold("sell") page3 = Text("Confirm order", ui.ICON_SEND, icon_color=ui.GREEN) page3.normal("Quantity:") page3.bold(str(msg.quantity)) page3.normal("Price:") page3.bold(str(msg.price)) return await hold_to_confirm(ctx, Paginated([page1, page2, page3]), ButtonRequestType.SignTx)
async def confirm_sending(ctx, amount, to): to_lines = list(chunks(to, 17)) t1 = Text("Confirm transaction", ui.ICON_SEND, ui.GREEN) t1.normal("Confirm sending:") t1.bold(format_coin_amount(amount)) t1.normal("to:") t1.bold(to_lines[0]) PER_PAGE = const(4) pages = [t1] if len(to_lines) > 1: to_pages = list(chunks(to_lines[1:], PER_PAGE)) for page in to_pages: t = Text("Confirm transaction", ui.ICON_SEND, ui.GREEN) for line in page: t.bold(line) pages.append(t) await require_confirm(ctx, Paginated(pages))
async def confirm_sending( ctx: wire.Context, ada_amount: int, token_bundle: List[CardanoAssetGroupType], to: str, ) -> None: for token_group in token_bundle: await confirm_sending_token_group(ctx, token_group) page1 = Text("Confirm transaction", ui.ICON_SEND, ui.GREEN) page1.normal("Confirm sending:") page1.bold(format_coin_amount(ada_amount)) page1.normal("to") to_lines = list(chunks(to, 17)) page1.bold(to_lines[0]) pages = [page1] + _paginate_lines(to_lines, 1, "Confirm transaction", ui.ICON_SEND) await require_confirm(ctx, Paginated(pages))
async def confirm_stake_pool_metadata( ctx: wire.Context, metadata: Optional[CardanoPoolMetadataType], ) -> None: if metadata is None: page1 = Text("Confirm transaction", ui.ICON_SEND, ui.GREEN) page1.normal("Pool has no metadata") page1.normal("(anonymous pool)") await require_confirm(ctx, page1) return page1 = Text("Confirm transaction", ui.ICON_SEND, ui.GREEN) page1.normal("Pool metadata url:") page1.bold(metadata.url) page2 = Text("Confirm transaction", ui.ICON_SEND, ui.GREEN) page2.normal("Pool metadata hash:") page2.bold(hexlify(metadata.hash).decode()) await require_confirm(ctx, Paginated([page1, page2]))
async def confirm_certificate(ctx: wire.Context, certificate: CardanoTxCertificateType) -> None: # stake pool registration requires custom confirmation logic not covered # in this call assert certificate.type != CardanoCertificateType.STAKE_POOL_REGISTRATION pages = [] page1 = Text("Confirm transaction", ui.ICON_SEND, ui.GREEN) page1.normal("Confirm:") page1.bold(CERTIFICATE_TYPE_NAMES[certificate.type]) page1.normal("for account:") page1.bold(address_n_to_str(to_account_path(certificate.path))) pages.append(page1) if certificate.type == CardanoCertificateType.STAKE_DELEGATION: page2 = Text("Confirm transaction", ui.ICON_SEND, ui.GREEN) page2.normal("to pool:") page2.bold(hexlify(certificate.pool).decode()) pages.append(page2) await require_confirm(ctx, Paginated(pages))