async def ask_transfer_mosaic( ctx: Context, common: NEMTransactionCommon, transfer: NEMTransfer, mosaic: NEMMosaic ) -> None: if is_nem_xem_mosaic(mosaic): return definition = get_mosaic_definition(mosaic.namespace, mosaic.mosaic, common.network) mosaic_quantity = mosaic.quantity * transfer.amount // NEM_MOSAIC_AMOUNT_DIVISOR if definition: await confirm_properties( ctx, "confirm_mosaic", title="Confirm mosaic", props=[ ( "Confirm transfer of", format_amount(mosaic_quantity, definition.divisibility) + definition.ticker, ), ("of", definition.name), ], ) if definition.levy is not None: levy_fee = _get_levy_fee(definition.levy, mosaic_quantity) levy_msg = ( format_amount(levy_fee, definition.divisibility) + definition.ticker ) await confirm_properties( ctx, "confirm_mosaic_levy", title="Confirm mosaic", props=[ ("Confirm mosaic\nlevy fee of", levy_msg), ], ) else: await confirm_action( ctx, "confirm_mosaic_unknown", title="Confirm mosaic", action="Unknown mosaic!", description="Divisibility and levy cannot be shown for unknown mosaics", icon=ui.ICON_SEND, icon_color=ui.RED, br_code=ButtonRequestType.ConfirmOutput, ) await confirm_properties( ctx, "confirm_mosaic_transfer", title="Confirm mosaic", props=[ ("Confirm transfer of", f"{mosaic_quantity} raw units"), ("of", f"{mosaic.namespace}.{mosaic.mosaic}"), ], )
async def authorize_coinjoin(ctx: wire.Context, msg: AuthorizeCoinJoin) -> Success: # We cannot use the @with_keychain decorator here, because we need the keychain # to survive the function exit. The ownership of the keychain is transferred to # the CoinJoinAuthorization object, which takes care of its destruction. keychain, coin = await get_keychain_for_coin(ctx, msg.coin_name) try: if len(msg.coordinator) > _MAX_COORDINATOR_LEN or not all( 32 <= ord(x) <= 126 for x in msg.coordinator ): raise wire.DataError("Invalid coordinator name.") if not msg.address_n: raise wire.DataError("Empty path not allowed.") await validate_path( ctx, addresses.validate_full_path, keychain, msg.address_n + [0] * BIP32_WALLET_DEPTH, coin.curve_name, coin=coin, script_type=msg.script_type, ) text = Text("Authorize CoinJoin", ui.ICON_RECOVERY) text.normal("Do you really want to") text.normal("take part in a CoinJoin") text.normal("transaction at:") text.mono(msg.coordinator) await require_confirm(ctx, text) text = Text("Authorize CoinJoin", ui.ICON_RECOVERY) if msg.fee_per_anonymity is not None: text.normal("Fee per anonymity set:") text.bold( "{} %".format( format_amount(msg.fee_per_anonymity, FEE_PER_ANONYMITY_DECIMALS) ) ) text.normal("Maximum total fees:") text.bold( "{} {}".format( format_amount(msg.max_total_fee, coin.decimals), coin.coin_shortcut ) ) await require_hold_to_confirm(ctx, text) set_authorization(CoinJoinAuthorization(msg, keychain, coin)) except BaseException: keychain.__del__() raise return Success(message="CoinJoin authorized")
async def require_confirm_tx(ctx, to, value): text = Text("Confirm sending", ui.ICON_SEND, ui.GREEN) text.bold(format_amount(value, helpers.DECIMALS) + " XRP") text.normal("to") text.mono(*split_address(to)) return await require_hold_to_confirm(ctx, text, ButtonRequestType.SignTx)
async def require_confirm_final(ctx, fee: int): text = Text("Final confirm", ui.ICON_SEND, ui.GREEN) text.normal("Sign this transaction") text.bold("and pay %s XEM" % format_amount(fee, NEM_MAX_DIVISIBILITY)) text.normal("for network fee?") # we use SignTx, not ConfirmOutput, for compatibility with T1 await require_hold_to_confirm(ctx, text, ButtonRequestType.SignTx)
async def authorize_coinjoin(ctx: wire.Context, msg: AuthorizeCoinJoin, keychain: Keychain, coin: CoinInfo) -> Success: if len(msg.coordinator) > _MAX_COORDINATOR_LEN or not all( 32 <= ord(x) <= 126 for x in msg.coordinator): raise wire.DataError("Invalid coordinator name.") if msg.max_rounds > _MAX_ROUNDS and safety_checks.is_strict(): raise wire.DataError("The number of rounds is unexpectedly large.") if (msg.max_coordinator_fee_rate > _MAX_COORDINATOR_FEE_RATE and safety_checks.is_strict()): raise wire.DataError( "The coordination fee rate is unexpectedly large.") if msg.max_fee_per_kvbyte > 10 * coin.maxfee_kb and safety_checks.is_strict( ): raise wire.DataError("The fee per vbyte is unexpectedly large.") if not msg.address_n: raise wire.DataError("Empty path not allowed.") await confirm_action( ctx, "coinjoin_coordinator", title="Authorize CoinJoin", description= "Do you really want to take part in a CoinJoin transaction at:\n{}", description_param=msg.coordinator, description_param_font=ui.MONO, icon=ui.ICON_RECOVERY, ) max_fee_per_vbyte = format_amount(msg.max_fee_per_kvbyte, 3) await confirm_coinjoin(ctx, coin.coin_name, msg.max_rounds, max_fee_per_vbyte) validation_path = msg.address_n + [0] * BIP32_WALLET_DEPTH await validate_path( ctx, keychain, validation_path, validate_path_against_script_type(coin, address_n=validation_path, script_type=msg.script_type), ) if msg.max_fee_per_kvbyte > coin.maxfee_kb: await confirm_metadata( ctx, "fee_over_threshold", "High mining fee", "The mining fee of\n{} sats/vbyte\nis unexpectedly high.", max_fee_per_vbyte, ButtonRequestType.FeeOverThreshold, ) authorization.set(msg) return Success(message="CoinJoin authorized")
def make_input_output_pages(msg: BinanceInputOutput, direction): for coin in msg.coins: items.append(( direction, format_amount(coin.amount, helpers.DECIMALS) + " " + coin.denom, msg.address, ))
async def require_confirm_fee(ctx, action: str, fee: int): content = ( ui.NORMAL, action, ui.BOLD, "%s XEM" % format_amount(fee, NEM_MAX_DIVISIBILITY), ) await require_confirm_content(ctx, "Confirm fee", content)
def test_format_amount(self): VECTORS = [ (123456, 3, "123.456"), (4242, 7, "0.0004242"), (-123456, 3, "-123.456"), (-4242, 7, "-0.0004242"), ] for v in VECTORS: self.assertEqual(strings.format_amount(v[0], v[1]), v[2])
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 confirm_sending_token_ascii(ctx: wire.Context, token: CardanoTokenType, token_number: int) -> None: page1 = Text("Confirm transaction", ui.ICON_SEND, ui.GREEN) page1.normal("Asset #%s name (ASCII):" % (token_number)) page1.bold(token.asset_name_bytes.decode("ascii")) page1.normal("Amount sent:") page1.bold(format_amount(token.amount, 0)) await require_confirm(ctx, page1)
async def require_confirm_fee(ctx, fee): await confirm_metadata( ctx, "confirm_fee", title="Confirm fee", content="Transaction fee:\n{}", param=format_amount(fee, helpers.DECIMALS) + " XRP", hide_continue=True, br_code=ButtonRequestType.ConfirmOutput, )
def _get_levy_msg(mosaic_definition, quantity: int, network: int) -> str: levy_definition = get_mosaic_definition( mosaic_definition["levy_namespace"], mosaic_definition["levy_mosaic"], network) if mosaic_definition["levy"] == NEMMosaicLevy.MosaicLevy_Absolute: levy_fee = mosaic_definition["fee"] else: levy_fee = (quantity * mosaic_definition["fee"] // NEM_LEVY_PERCENTILE_DIVISOR_ABSOLUTE) return (format_amount(levy_fee, levy_definition["divisibility"]) + levy_definition["ticker"])
async def authorize_coinjoin(ctx: wire.Context, msg: AuthorizeCoinJoin) -> Success: # We cannot use the @with_keychain decorator here, because we need the keychain # to survive the function exit. The ownership of the keychain is transferred to # the CoinJoinAuthorization object, which takes care of its destruction. keychain, coin = await get_keychain_for_coin(ctx, msg.coin_name) try: if len(msg.coordinator) > _MAX_COORDINATOR_LEN or not all( 32 <= ord(x) <= 126 for x in msg.coordinator): raise wire.DataError("Invalid coordinator name.") if not msg.address_n: raise wire.DataError("Empty path not allowed.") validation_path = msg.address_n + [0] * BIP32_WALLET_DEPTH await validate_path( ctx, keychain, validation_path, validate_path_against_script_type(coin, address_n=validation_path, script_type=msg.script_type), ) await confirm_action( ctx, "coinjoin_coordinator", title="Authorize CoinJoin", description= "Do you really want to take part in a CoinJoin transaction at:\n{}", description_param=msg.coordinator, description_param_font=ui.MONO, icon=ui.ICON_RECOVERY, ) fee_per_anonymity = None if msg.fee_per_anonymity is not None: fee_per_anonymity = format_amount(msg.fee_per_anonymity, FEE_PER_ANONYMITY_DECIMALS) await confirm_coinjoin( ctx, fee_per_anonymity, format_coin_amount(msg.max_total_fee, coin, msg.amount_unit), ) set_authorization(CoinJoinAuthorization(msg, keychain, coin)) except BaseException: keychain.__del__() raise return Success(message="CoinJoin authorized")
async def require_confirm_order(ctx: Context, msg: BinanceOrderMsg) -> None: if msg.side == BinanceOrderSide.BUY: side = "Buy" elif msg.side == BinanceOrderSide.SELL: side = "Sell" else: side = "Unknown" await confirm_properties( ctx, "confirm_order", title="Confirm order", props=[ ("Sender address:", str(msg.sender)), ("Pair:", str(msg.symbol)), ("Side:", side), ("Quantity:", format_amount(msg.quantity, helpers.DECIMALS)), ("Price:", format_amount(msg.price, helpers.DECIMALS)), ], hold=True, br_code=ButtonRequestType.SignTx, )
async def require_confirm_order(ctx, msg: BinanceOrderMsg): page1 = Text("Confirm order 1/3", ui.ICON_SEND, icon_color=ui.GREEN) page1.normal("Sender address:") page1.bold(msg.sender) page2 = Text("Confirm order 2/3", ui.ICON_SEND, icon_color=ui.GREEN) page2.normal("Pair:") page2.bold(msg.symbol) page2.normal("Side:") if msg.side == BinanceOrderSide.BUY: page2.bold("Buy") elif msg.side == BinanceOrderSide.SELL: page2.bold("Sell") page3 = Text("Confirm order 3/3", ui.ICON_SEND, icon_color=ui.GREEN) page3.normal("Quantity:") page3.bold(format_amount(msg.quantity, helpers.DECIMALS)) page3.normal("Price:") page3.bold(format_amount(msg.price, helpers.DECIMALS)) return await require_hold_to_confirm(ctx, Paginated([page1, page2, page3]), ButtonRequestType.SignTx)
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
def parse(data: bytes) -> bool: if not is_valid(data): return None tx_version, tx_type = unpack(">HH", data[4:8]) if tx_version == 0 and tx_type == 0 and len(data) == 20: # OMNI simple send currency, amount = unpack(">IQ", data[8:20]) suffix, divisible = currencies.get(currency, ("UNKN", False)) return "Simple send of %s %s" % ( format_amount(amount, 8 if divisible else 0), suffix, ) else: # unknown OMNI transaction return "Unknown transaction"
def format_coin_amount(amount: int, coin: CoinInfo, amount_unit: EnumTypeAmountUnit) -> str: decimals, shortcut = coin.decimals, coin.coin_shortcut if amount_unit == AmountUnit.SATOSHI: decimals = 0 shortcut = "sat " + shortcut elif amount_unit == AmountUnit.MICROBITCOIN and decimals >= 6: decimals -= 6 shortcut = "u" + shortcut elif amount_unit == AmountUnit.MILLIBITCOIN and decimals >= 3: decimals -= 3 shortcut = "m" + shortcut # we don't need to do anything for AmountUnit.BITCOIN return "%s %s" % (format_amount(amount, decimals), shortcut)
async def confirm_sending_token_bundle( ctx: wire.Context, token_bundle: list[CardanoAssetGroupType]) -> None: for token_group in token_bundle: for token in token_group.tokens: page1 = Text("Confirm transaction", ui.ICON_SEND, ui.GREEN) page1.normal("Asset fingerprint:") page1.bold( format_asset_fingerprint( policy_id=token_group.policy_id, asset_name_bytes=token.asset_name_bytes, )) 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]))
def format_ethereum_amount(value: int, token, chain_id: int, tx_type=None): if token: if token is tokens.UNKNOWN_TOKEN: return "Unknown token value" suffix = token[2] decimals = token[3] else: suffix = networks.shortcut_by_chain_id(chain_id, tx_type) decimals = 18 # Don't want to display wei values for tokens with small decimal numbers if decimals > 9 and value < 10**(decimals - 9): suffix = "Wei " + suffix decimals = 0 return "%s %s" % (format_amount(value, decimals), suffix)
async def authorize_coinjoin(ctx: wire.Context, msg: AuthorizeCoinJoin, keychain: Keychain, coin: CoinInfo) -> Success: if len(msg.coordinator) > _MAX_COORDINATOR_LEN or not all( 32 <= ord(x) <= 126 for x in msg.coordinator): raise wire.DataError("Invalid coordinator name.") if not msg.address_n: raise wire.DataError("Empty path not allowed.") validation_path = msg.address_n + [0] * BIP32_WALLET_DEPTH await validate_path( ctx, keychain, validation_path, validate_path_against_script_type(coin, address_n=validation_path, script_type=msg.script_type), ) await confirm_action( ctx, "coinjoin_coordinator", title="Authorize CoinJoin", description= "Do you really want to take part in a CoinJoin transaction at:\n{}", description_param=msg.coordinator, description_param_font=ui.MONO, icon=ui.ICON_RECOVERY, ) if msg.fee_per_anonymity: fee_per_anonymity: str | None = format_amount( msg.fee_per_anonymity, FEE_PER_ANONYMITY_DECIMALS) else: fee_per_anonymity = None await confirm_coinjoin( ctx, fee_per_anonymity, format_coin_amount(msg.max_total_fee, coin, msg.amount_unit), ) authorization.set(msg) return Success(message="CoinJoin authorized")
async def confirm_sending_token(ctx: wire.Context, policy_id: bytes, token: CardanoToken) -> None: assert token.amount is not None # _validate_token await confirm_properties( ctx, "confirm_token", title="Confirm transaction", props=[ ( "Asset fingerprint:", format_asset_fingerprint( policy_id=policy_id, asset_name_bytes=token.asset_name_bytes, ), ), ("Amount sent:", format_amount(token.amount, 0)), ], br_code=ButtonRequestType.Other, )
async def ask_transfer_mosaic( ctx, common: NEMTransactionCommon, transfer: NEMTransfer, mosaic: NEMMosaic ): if is_nem_xem_mosaic(mosaic.namespace, mosaic.mosaic): return definition = get_mosaic_definition(mosaic.namespace, mosaic.mosaic, common.network) mosaic_quantity = mosaic.quantity * transfer.amount / NEM_MOSAIC_AMOUNT_DIVISOR if definition: msg = Text("Confirm mosaic", ui.ICON_SEND, ui.GREEN) msg.normal("Confirm transfer of") msg.bold( format_amount(mosaic_quantity, definition["divisibility"]) + definition["ticker"] ) msg.normal("of") msg.bold(definition["name"]) await require_confirm(ctx, msg, ButtonRequestType.ConfirmOutput) if "levy" in definition and "fee" in definition: levy_msg = _get_levy_msg(definition, mosaic_quantity, common.network) msg = Text("Confirm mosaic", ui.ICON_SEND, ui.GREEN) msg.normal("Confirm mosaic", "levy fee of") msg.bold(levy_msg) await require_confirm(ctx, msg, ButtonRequestType.ConfirmOutput) else: msg = Text("Confirm mosaic", ui.ICON_SEND, ui.RED) msg.bold("Unknown mosaic!") msg.normal("Divisibility and levy") msg.normal("cannot be shown for") msg.normal("unknown mosaics") await require_confirm(ctx, msg, ButtonRequestType.ConfirmOutput) msg = Text("Confirm mosaic", ui.ICON_SEND, ui.GREEN) msg.normal("Confirm transfer of") msg.bold("%s raw units" % mosaic_quantity) msg.normal("of") msg.bold("%s.%s" % (mosaic.namespace, mosaic.mosaic)) await require_confirm(ctx, msg, ButtonRequestType.ConfirmOutput)
def format_coin_amount(amount: int) -> str: return "%s %s" % (format_amount(amount, 6), "ADA")
def format_coin_amount(amount: int, coin: coininfo.CoinInfo) -> str: return "%s %s" % (format_amount(amount, coin.decimals), coin.coin_shortcut)
async def _require_confirm_transfer(ctx, recipient, value): text = Text("Confirm transfer", ui.ICON_SEND, ui.GREEN) text.bold("Send %s XEM" % format_amount(value, NEM_MAX_DIVISIBILITY)) text.normal("to") text.mono(*split_address(recipient)) await require_confirm(ctx, text, ButtonRequestType.ConfirmOutput)
def format_amount(value): return "%s XMR" % strings.format_amount(value, 12)
def format_amount(amount: int, ticker=True) -> str: t = "" if ticker: t = " XLM" return strings.format_amount(amount, consts.AMOUNT_DECIMALS) + t
def format_coin_amount(value): return "%s LSK" % format_amount(value, 8)
def format_coin_amount(amount): return "%s %s" % (format_amount(amount, 6), "ADA")