def do_send(self): if self.screen.is_pr: if self.payment_request.has_expired(): self.app.show_error(_('Payment request has expired')) return outputs = self.payment_request.get_outputs() else: address = str(self.screen.address) if not address: self.app.show_error( _('Recipient not specified.') + ' ' + _('Please scan a Bitcoin address or a payment request')) return if not Address.is_valid(address): self.app.show_error( _('Invalid Bitcoin Address') + ':\n' + address) return try: amount = self.app.get_amount(self.screen.amount) except: self.app.show_error( _('Invalid amount') + ':\n' + self.screen.amount) return outputs = [(bitcoin.TYPE_ADDRESS, Address.from_string(address), amount)] message = self.screen.message amount = sum(map(lambda x: x[2], outputs)) self._do_send(amount, message, outputs)
def onOk(self) -> None: #print("On OK...") address_str = cleanup_address_remove_colon(self.address.text) name = str(self.name.text).strip() if not Address.is_valid(address_str): gui.ElectrumGui.gui.show_error(_("Invalid Address"), title=self.title) return if not name: gui.ElectrumGui.gui.show_error(_("Name is empty"), title=self.title) return def doCB() -> None: cb = utils.get_callback(self, 'on_ok') if callable(cb): entry = None if name and address_str and Address.is_valid(address_str): address = Address.from_string(address_str) entry = ContactsEntry(name, address, address_str) cb(entry) self.autorelease() self.retain() self.presentingViewController.dismissViewControllerAnimated_completion_( True, doCB)
def create_menu(self, position): menu = QMenu() selected = self.selectedItems() if not selected: menu.addAction(_("New contact"), lambda: self.parent.new_contact_dialog()) menu.addAction(_("Import file"), lambda: self.import_contacts()) else: names = [item.text(0) for item in selected] keys = [item.text(1) for item in selected] column = self.currentColumn() column_title = self.headerItem().text(column) column_data = '\n'.join([item.text(column) for item in selected]) menu.addAction( _("Copy %s") % column_title, lambda: self.parent.app.clipboard().setText(column_data)) if column in self.editable_columns: item = self.currentItem() menu.addAction( _("Edit %s") % column_title, lambda: self.editItem(item, column)) menu.addAction(_("Pay to"), lambda: self.parent.payto_contacts(keys)) menu.addAction(_("Delete"), lambda: self.parent.delete_contacts(keys)) URLs = [ web.BE_URL(self.config, 'addr', Address.from_string(key)) for key in keys if Address.is_valid(key) ] if URLs: menu.addAction(_("View on block explorer"), lambda: [webbrowser.open(URL) for URL in URLs]) run_hook('create_contact_menu', menu, selected) menu.exec_(self.viewport().mapToGlobal(position))
def get_contacts() -> list: ''' Builds a list of ContactsEntry tuples: ContactsEntry = namedtuple("ContactsEntry", "name address address_str hist_entries") ''' t0 = time.time() parent = gui.ElectrumGui.gui wallet = parent.wallet if wallet is None: utils.NSLog("get_contacts: wallent was None, returning early") return list() c = wallet.contacts contacts = list() for addr,tupl in c.items(): typ, name = tupl if typ == 'address' and Address.is_valid(addr): address = Address.from_string(addr) hist_entries = build_contact_tx_list(address) entry = ContactsEntry(name, address, addr, hist_entries) contacts.append(entry) contacts.sort(key=lambda x: [x.name, x.address_str], reverse=False) utils.NSLog("get_contacts: fetched %d contacts in %f ms",len(contacts), (time.time()-t0)*1000.0) return contacts
def invoke(firstarg='0.0.0.0', sport='8787', upnp_str=None, addr_str=None): bindhost, *extrahosts = firstarg.split(',') if len(extrahosts) > 1: raise Exception("too many hosts") elif len(extrahosts) == 1: [ announcehost, ] = extrahosts else: announcehost = None port = int(sport) pnp = get_upnp() if upnp_str == 'upnp' else None if not pnp and not addr_str: # third arg may be addr_str, so swap the args addr_str = upnp_str upnp_str = None addr = None if addr_str: assert Address.is_valid( addr_str), "Invalid donation address specified" addr = Address.from_string(addr_str) return self.start_fusion_server(network, bindhost, port, upnp=pnp, announcehost=announcehost, donation_address=addr)
def on_qr(self, data): from electroncash.bitcoin import base_decode data = data.strip() if Address.is_valid(data): self.set_URI(data) return if data.startswith('bitcoincash:'): self.set_URI(data) return # try to decode transaction from electroncash.transaction import Transaction from electroncash.util import bh2u try: text = bh2u(base_decode(data, None, base=43)) tx = Transaction(text) tx.deserialize() if self.wallet: my_coins = self.wallet.get_spendable_coins(None, self.electrum_config) my_outpoints = [vin['prevout_hash'] + ':' + str(vin['prevout_n']) for vin in my_coins] for i, txin in enumerate(tx.inputs()): outpoint = txin['prevout_hash'] + ':' + str(txin['prevout_n']) if outpoint in my_outpoints: my_index = my_outpoints.index(outpoint) tx._inputs[i]['value'] = my_coins[my_index]['value'] except: tx = None if tx: self.tx_dialog(tx) return # show error self.show_error("Unable to decode QR data")
def resolve(self): self.is_alias, self.validated = False, False if self.hasFocus(): return if self.is_multiline(): # only supports single line entries atm return if self.is_pr: return key = str(self.toPlainText()) key = key.strip() # strip whitespaces if key == self.previous_payto: return self.previous_payto = key if not (('.' in key) and (not '<' in key) and (not ' ' in key)): return parts = key.split(sep=',') # assuming single line if parts and len(parts) > 0 and Address.is_valid(parts[0]): return try: data = self.win.contacts.resolve(key) except Exception as e: print_error(f'error resolving alias: {repr(e)}') return if not data: return address = data.get('address') name = data.get('name') _type = data.get('type') if _type != 'openalias': return address_str = None if isinstance(address, str): address_str = address elif isinstance(address, Address): address_str = address.to_ui_string() else: raise RuntimeError('unknown address type') self.is_alias = True new_url = key + ' <' + address_str + '>' self.setText(new_url) self.previous_payto = new_url self.win.contacts[key] = ('openalias', name) self.win.contact_list.update() self.setFrozen(True) self.validated = bool(data.get('validated')) if self.validated: self.setGreen() else: self.setExpired()
def donation_address(self, window) -> Optional[Tuple[str, Address]]: ''' Plugin API: Returns a tuple of (description, Address) or None. This is the donation address that we as a client got from the remote server (as opposed to the donation address we announce if we are a server). ''' if self.remote_donation_address and Address.is_valid( self.remote_donation_address): return (self.fullname() + " " + _("Server") + ": " + self.get_server()[0], Address.from_string(self.remote_donation_address))
def doCB() -> None: cb = utils.get_callback(self, 'on_ok') if callable(cb): entry = None if name and address_str and Address.is_valid(address_str): address = Address.from_string(address_str) entry = ContactsEntry(name, address, address_str) cb(entry) self.autorelease()
def resolve(self): self.is_alias = False if self.hasFocus(): return if self.is_multiline(): # only supports single line entries atm return if self.is_pr: return key = str(self.toPlainText()) if key == self.previous_payto: return self.previous_payto = key if not (('.' in key) and (not '<' in key) and (not ' ' in key)): return parts = key.split(sep=',') # assuming single lie if parts and len(parts) > 0 and Address.is_valid(parts[0]): return try: data = self.win.contacts.resolve(key) except: return if not data: return self.is_alias = True address = data.get('address') name = data.get('name') new_url = key + ' <' + address + '>' self.setText(new_url) self.previous_payto = new_url if isinstance(self.win.contacts, dict): # old contacts API self.win.contacts[key] = ('openalias', name) else: try: from electroncash.contacts import Contact self.win.contacts.add(Contact(name=name, address=key, type='openalias'), unique=True) except Exception as e: print_error( "[Custom PayToEdit] Could not determine contacts API, giving up:", repr(e)) self.win.contact_list.on_update() self.setFrozen(True) if data.get('type') == 'openalias': self.validated = data.get('validated') if self.validated: self.setGreen() else: self.setExpired() else: self.validated = None
def delete_history(self, address: Address) -> None: if isinstance(address, str) and Address.is_valid(address): address = Address.from_string(address) storage = self.parent.wallet.storage if self.parent.wallet else None if storage: with self.lock: addrstr = address.to_storage_string() k = 'contact_history_%s' % (addrstr) storage.put(k, None) self.print_error("Deleted %s" % addrstr)
def open_link(link): if Address.is_valid(link): addr = Address.from_string(link) if wallet.is_mine(addr): parent.show_address(addr) else: addr_URL = web.BE_URL(parent.config, 'addr', addr) if addr_URL: webopen(addr_URL) return if link.startswith('http'): webopen(link)
def _open_internal_link(self, target): ''' accepts either a str txid, str address, or a QUrl which should be of the bare form "txid" and/or "address" -- used by the clickable links in the inputs/outputs QTextBrowsers''' if isinstance(target, QUrl): target = target.toString(QUrl.None_) assert target if Address.is_valid(target): # target was an address, open address dialog self.main_window.show_address(Address.from_string(target), parent=self) else: # target was a txid, open new tx dialog self.main_window.do_process_from_txid(txid=target, parent=self)
def get_history(self, address: Address) -> list: ret = list() if isinstance(address, str) and Address.is_valid(address): address = Address.from_string(address) storage = self.parent.wallet.storage if self.parent.wallet else None if storage: addrstr = address.to_storage_string() k = 'contact_history_%s' % (addrstr) hdict = storage.get(k) if hdict: ret = list(hdict.values()) ret.sort(key=lambda x: x[1], reverse=True) return ret
def set_payment_address(address): self.payto_edit.payto_address = bitcoin.TYPE_ADDRESS, Address.from_string( address) self.value_payto_outputs = self.payto_edit.get_outputs(False) contact_name = None if address in window.wallet.contacts.keys(): contact_type, contact_name = window.wallet.contacts[address] if contact_name is not None: self.payto_edit.setText(contact_name + ' <' + address + '>') else: if Address.is_valid(address): address = Address.from_string(address).to_ui_string() self.payto_edit.setText(address)
def doReloadForKey(self, key: Any) -> Any: t0 = time.time() hist = list() unk = False duped = '' if isinstance(key, (type(None), list)): # the common case, 'None' or [Address] hist = get_history(domain=key) # contacts entires elif isinstance(key, contacts.ContactsEntry): hist = get_contact_history(key.address) elif isinstance(key, Address): # support for list-less single Address.. call self again with the proper format hist = self.get([key]) duped = ' (duped) ' elif isinstance(key, str): # support for string addresses or tx_hashes.. detect which and act accordingly if Address.is_valid(key): hist = self.get( [Address.from_string(key)] ) # recursively call self with propery list data type, which will end up calling get_history (it's ok -- this is to cache results uniformly!) duped = ' (duped) ' elif gui.ElectrumGui.gui.wallet and gui.ElectrumGui.gui.wallet.transactions.get( key, None): fullHist = self.get( None ) # recursively call self to get a full history (will be cached so it's ok!) try: hentry = fullHist.get_by_txid(key) hist.append(hentry) except KeyError: pass else: unk = True else: unk = True dstr = str(key) if not isinstance( key, contacts.ContactsEntry ) else '[ContactsEntry: ' + key.address_str + ']' if unk: utils.NSLog( "HistoryMgr: failed to retrieve any data for unknown domain=%s, returning empty list", dstr[:80]) else: time_taken = time.time() - t0 utils.NSLog( "HistoryMgr: refresh %d entries for domain=%s in %f ms%s (hist result type=%s)", len(hist), dstr[:80], time_taken * 1e3, duped, ''.join(list(str(type(hist)))[-19:-2])) gui.ElectrumGui.gui.refresh_cost('history', time_taken) return hist
def doReloadForKey(self, key: Any) -> Any: t0 = time.time() hist = list() unk = False duped = '' if isinstance(key, (type(None), list)): # the common case, 'None' or [Address] hist = get_history(domain=key) # contacts entires store history entries within themselves.. so just return that elif isinstance(key, contacts.ContactsEntry): hist = contacts.build_contact_tx_list( key.address ) # force refresh of tx's from wallet -- this will call us again with 'None' elif isinstance(key, Address): # support for list-less single Address.. call self again with the proper format hist = self.get([key]) duped = ' (duped) ' elif isinstance(key, str): # support for string addresses or tx_hashes.. detect which and act accordingly if Address.is_valid(key): hist = self.get( [Address.from_string(key)] ) # recursively call self with propery list data type, which will end up calling get_history (it's ok -- this is to cache results uniformly!) duped = ' (duped) ' elif gui.ElectrumGui.gui.wallet and gui.ElectrumGui.gui.wallet.transactions.get( key, None): fullHist = self.get( None ) # recursively call self to get a full history (will be cached so it's ok!) for hentry in fullHist: if hentry.tx_hash == key: hist.append(hentry) break else: unk = True else: unk = True dstr = str(key) if not isinstance( key, contacts.ContactsEntry ) else '[ContactsEntry: ' + key.address_str + ']' if unk: utils.NSLog( "HistoryMgr: failed to retrieve any data for unknown domain=%s, returning empty list", dstr[:80]) else: utils.NSLog( "HistoryMgr: refresh %d entries for domain=%s in %f ms%s", len(hist), dstr[:80], (time.time() - t0) * 1e3, duped) return hist
def reader_didScanResult_(self, reader, result) -> None: utils.NSLog("Reader data = '%s'", str(result)) result = cleanup_address_remove_colon(result) if not Address.is_valid(result): title = _("Invalid QR Code") message = _( "The QR code does not appear to be a valid BCH address.\nPlease try again." ) reader.stopScanning() gui.ElectrumGui.gui.show_error(title=title, message=message, onOk=lambda: reader.startScanning(), vc=self.qrvc) else: self.address.text = result self.readerDidCancel_(reader)
def make_unsigned_transaction(self, amount, fee, all_inputs, outputs, changes): ''' make unsigned transaction ''' dust = self.dust_threshold( ) # always 546 for now, but this call is here in case something more sophisticated happens in the future coins = {} tx_inputs = [] amounts = {} try: for player in all_inputs: inputs_coins = self.get_coins(all_inputs[player]) # if there are no coins on input it terminates the process if inputs_coins: coins[player] = inputs_coins else: return None except BaseException as e: self.print_error('make_unsigned_transaction:', repr(e)) return None for player, pubkey_utxos in coins.items(): amounts[player] = 0 for pubkey, utxos in pubkey_utxos.items(): for utxo in utxos: utxo['type'] = 'p2pkh' utxo['address'] = Address.from_pubkey(pubkey) utxo['pubkeys'] = [pubkey] utxo['x_pubkeys'] = [pubkey] utxo['prevout_hash'] = utxo['tx_hash'] utxo['prevout_n'] = utxo['tx_pos'] utxo['signatures'] = [None] utxo['num_sig'] = 1 tx_inputs.append(utxo) amounts[player] += utxo['value'] tx_inputs.sort(key=lambda x: x['prevout_hash'] + str(x["tx_pos"])) tx_outputs = [(TYPE_ADDRESS, Address.from_string(output), int(amount)) for output in outputs] transaction = Transaction.from_io(tx_inputs, tx_outputs, sign_schnorr=False) tx_changes = [ (TYPE_ADDRESS, Address.from_string(changes[player]), int(amounts[player] - amount - fee)) for player in sorted(changes) if Address.is_valid(changes[player]) and int(amounts[player] - amount - fee) >= dust ] transaction.add_outputs(tx_changes) return transaction
def check_sufficient_ammount(self): coin_amount = self.coinshuffle_inputs_list.get_selected_amount() self.coinshuffle_inputs_total_output.setText( self.window.format_amount_and_units(coin_amount)) shuffle_amount = self.coinshuffle_amount_radio.get_amount() fee = self.coinshuffle_fee_constant if shuffle_amount and fee: if coin_amount > (fee + shuffle_amount): self.coinshuffle_start_button.setEnabled(True) if self.coinshuffle_use_external_output.isChecked(): if not Address.is_valid( self.coinshuffle_external_output.text()): self.coinshuffle_start_button.setEnabled(False) else: self.coinshuffle_start_button.setEnabled(False) else: self.coinshuffle_start_button.setEnabled(False)
def get_full_contacts(self, include_pseudo: bool = True) -> List[Contact]: ''' Returns all the contacts, with the "My CashAcct" pseudo-contacts clobbering dupes of the same type that were manually added. Client code should scan for type == 'cashacct' and type == 'cashacct_W' ''' if include_pseudo: # filter out cachaccts that are "Wallet", as they will be added # at the end as pseudo contacts if they also appear in real contacts real_contacts = [contact for contact in self.parent.contacts.get_all(nocopy=True) if contact.type != 'cashacct' # accept anything that's not cashacct or not Address.is_valid(contact.address) # or if it is, it can have invalid address as it's clearly 'not mine" or not self.wallet.is_mine( # or if it's not mine Address.from_string(contact.address)) ] return real_contacts + self._make_wallet_cashacct_pseudo_contacts() else: return self.parent.contacts.get_all(nocopy=True)
def do_send(self): if not Address.is_valid(self.str_recipient): print(_('Invalid Bitcoin address')) return try: amount = int(Decimal(self.str_amount) * COIN) except Exception: print(_('Invalid Amount')) return try: fee = int(Decimal(self.str_fee) * COIN) except Exception: print(_('Invalid Fee')) return if self.wallet.use_encryption: password = self.password_dialog() if not password: return else: password = None c = "" while c != "y": c = input("ok to send (y/n)?") if c == "n": return try: tx = self.wallet.mktx([(TYPE_ADDRESS, self.str_recipient, amount)], password, self.config, fee) except Exception as e: print(str(e)) return if self.str_description: self.wallet.labels[tx.hash()] = self.str_description print(_("Please wait...")) status, msg = self.network.broadcast(tx) if status: print(_('Payment sent.')) #self.do_clear() #self.update_contacts_tab() else: print(_('Error'))
def set_label(self, wallet, addr_or_txid, label): ''' Catch when they actually paid -- wallet.set_label is called once a payment is done, passing us txid and the unique description we generated in prompt_pay_overdue_payment_occurrences() below. We use that unique description key to catch the payment that was made and thus know the payments were made, so we mark them as such. ''' self.print_error("set_label called", wallet, addr_or_txid, label) vals = self.will_possibly_pay.pop(label, None) if vals: wallet_name, payment_keys = vals if wallet.basename() == wallet_name and not Address.is_valid(addr_or_txid): # matches self.print_error("Payment occurrence matched, forgetting: ",addr_or_txid,label,wallet_name,payment_keys) self.forget_overdue_payment_occurrences(wallet_name, payment_keys, mark_paid = True) else: self.print_error("No match, putting it back in our dict") # doesn't match, remember this thing self.will_possibly_pay[label] = vals
def get_full_contacts(self, include_pseudo_types: List[str] = ['cashacct', 'lns']) -> List[Contact]: ''' Returns all the contacts, with the "My CashAcct" and "My LNS names" pseudo-contacts clobbering dupes of the same type that were manually added. Client code should scan for type == 'cashacct' and type == 'cashacct_W' also for type == 'lns' and type == 'lns_W ''' if not include_pseudo_types: return self.parent.contacts.get_all(nocopy=True) else: contacts = [contact for contact in self.parent.contacts.get_all(nocopy=True) if contact.type not in ['cashacct', 'lns'] # or if it is, it can have invalid address as it's clearly 'not mine" or not Address.is_valid(contact.address) or not self.wallet.is_mine(Address.from_string(contact.address))] if 'cashacct' in include_pseudo_types: contacts = contacts + self._make_wallet_cashacct_pseudo_contacts() if self.have_lns and 'lns' in include_pseudo_types: contacts = contacts + self._make_wallet_lns_pseudo_contacts() return contacts
def do_send(self): if not Address.is_valid(self.str_recipient): print(_('Invalid DeVault address')) return try: amount = int(PyDecimal(self.str_amount) * COIN) except Exception: print(_('Invalid Amount')) return try: fee = int(PyDecimal(self.str_fee) * COIN) except Exception: print(_('Invalid Fee')) return if self.wallet.has_password(): password = self.password_dialog() if not password: return else: password = None c = "" while c != "y": c = input("ok to send (y/n)?") if c == "n": return try: tx = self.wallet.mktx([(TYPE_ADDRESS, self.str_recipient, amount)], password, self.config, fee) except Exception as e: print(str(e)) return if self.str_description: self.wallet.labels[tx.txid()] = self.str_description print(_("Please wait...")) status, msg = self.network.broadcast_transaction(tx) if status: print(_('Payment sent.')) else: print(_('Error'))
def resolve(self): self.is_alias = False if self.hasFocus(): return if self.is_multiline(): # only supports single line entries atm return if self.is_pr: return key = str(self.toPlainText()) if key == self.previous_payto: return self.previous_payto = key if not (('.' in key) and (not '<' in key) and (not ' ' in key)): return parts = key.split(sep=',') # assuming single lie if parts and len(parts) > 0 and Address.is_valid(parts[0]): return try: data = self.win.contacts.resolve(key) except: return if not data: return self.is_alias = True address = data.get('address') name = data.get('name') new_url = key + ' <' + address + '>' self.setText(new_url) self.previous_payto = new_url #if self.win.config.get('openalias_autoadd') == 'checked': self.win.contacts[key] = ('openalias', name) self.win.contact_list.on_update() self.setFrozen(True) if data.get('type') == 'openalias': self.validated = data.get('validated') if self.validated: self.setGreen() else: self.setExpired() else: self.validated = None
def open_link(link): if Address.is_valid(link): addr = Address.from_string(link) if wallet.is_mine(addr): parent.show_address(addr) else: addr_URL = web.BE_URL(parent.config, web.ExplorerUrlParts.ADDR, addr) if addr_URL: webopen(addr_URL) return if link.startswith('http'): webopen(link) elif len(link) == 64: # 64 character txid tx = wallet.transactions.get(link) if tx: parent.show_transaction(tx, tx_desc=wallet.get_label(link)) else: parent.do_process_from_txid(txid=link, tx_desc=wallet.get_label(link)) return
def make_unsigned_transaction(self, amount, fee, all_inputs, outputs, changes): "make unsigned transaction" dust = dust_threshold(self.network) coins = {} tx_inputs = [] amounts = {} try: for player in all_inputs: inputs_coins = self.get_coins(all_inputs[player]) # if there is no coins on input it terminates the process if inputs_coins: coins[player] = inputs_coins else: return None except: return None for player, pubkey_utxos in coins.items(): amounts[player] = 0 for pubkey, utxos in pubkey_utxos.items(): for utxo in utxos: utxo['type'] = 'p2pkh' utxo['address'] = Address.from_pubkey(pubkey) utxo['pubkeys'] = [pubkey] utxo['x_pubkeys'] = [pubkey] utxo['prevout_hash'] = utxo['tx_hash'] utxo['prevout_n'] = utxo['tx_pos'] utxo['signatures'] = [None] utxo['num_sig'] = 1 tx_inputs.append(utxo) amounts[player] += utxo['value'] tx_inputs.sort(key=lambda x: x['prevout_hash'] + str(x["tx_pos"])) tx_outputs = [(TYPE_ADDRESS, Address.from_string(output), int(amount)) for output in outputs] transaction = Transaction.from_io(tx_inputs, tx_outputs) tx_changes = [ (TYPE_ADDRESS, Address.from_string(changes[player]), int(amounts[player] - amount - fee)) for player in sorted(changes) if Address.is_valid(changes[player]) and int(amounts[player] - amount - fee) >= dust ] transaction.add_outputs(tx_changes) return transaction
def do_send(self): if not Address.is_valid(self.str_recipient): self.show_message(_('Invalid Bitcoin address')) return try: amount = int(Decimal(self.str_amount) * COIN) except Exception: self.show_message(_('Invalid Amount')) return try: fee = int(Decimal(self.str_fee) * COIN) except Exception: self.show_message(_('Invalid Fee')) return if self.wallet.has_password(): password = self.password_dialog() if not password: return else: password = None try: tx = self.wallet.mktx([(TYPE_ADDRESS, self.str_recipient, amount)], password, self.config, fee) except Exception as e: self.show_message(str(e)) return if self.str_description: self.wallet.labels[tx.txid()] = self.str_description self.show_message(_("Please wait..."), getchar=False) status, msg = self.network.broadcast(tx) if status: self.show_message(_('Payment sent.')) self.do_clear() #self.update_contacts_tab() else: self.show_message(_('Error'))
def format_value(self, value, display_type=0): if display_type in (DISPLAY_AS_AMOUNT, DISPLAY_AS_AMOUNT_NO_UNITS): return self.window.format_amount(value, whitespaces=False) + ( (' ' + self.window.base_unit()) if display_type == DISPLAY_AS_AMOUNT else '') elif display_type == DISPLAY_AS_AMOUNT_FIAT: fx = self.window.fx units = fx.get_currency() if fx else '(FIAT)' return '%0.2f %s' % (float(value), units) elif display_type == DISPLAY_AS_ADDRESS: contact_name = self.format_contact(value) if contact_name is None: if Address.is_valid(value): contact_name = Address.from_string(value).to_ui_string() else: contact_name = value return contact_name elif display_type == DISPLAY_AS_DATETIME: if value is None: return "-" return datetime.datetime.fromtimestamp(value).strftime( "%Y-%m-%d %H:%M") return str(value)