def update_amounts(self, ): # Update the other two dependent amounts based on user-provided ones. # This uses floats. wbyprice = self.want_price_cb.isChecked() gbyprice = self.give_price_cb.isChecked() if wbyprice or gbyprice: if self.primaryprice is self.price1_e: try: price = float(self.price1_e.text()) iprice = invert(price) except: self.price2_e.setText('') price = None else: self.price2_e.setText(self.format_price(iprice)) else: try: iprice = float(self.price2_e.text()) price = invert(iprice) except: self.price1_e.setText('') price = None else: self.price1_e.setText(self.format_price(price)) if wbyprice: try: a = price * 1e8 * float(self.give_amount_e.text()) self.want_amount_e.setText( format_satoshis_plain_nofloat(a)) except: self.want_amount_e.setText('') else: try: a = iprice * 1e8 * float(self.want_amount_e.text()) self.give_amount_e.setText( format_satoshis_plain_nofloat(a)) except: self.give_amount_e.setText('') else: try: wa = float(self.want_amount_e.text()) ga = float(self.give_amount_e.text()) except: self.price1_e.setText('') self.price2_e.setText('') else: self.price1_e.setText(self.format_price(wa * invert(ga))) self.price2_e.setText(self.format_price(ga * invert(wa)))
def read_from_offerinfo(self, offerinfo): tick1 = offerinfo.want_ticker tick2 = offerinfo.give_ticker self.pi.want_crypto_cb.setCurrentIndex( crypto_list_by_bytes.index(tick1)) self.pi.give_crypto_cb.setCurrentIndex( crypto_list_by_bytes.index(tick2)) if offerinfo.want_amount is not None: self.pi.want_amount_e.setText( format_satoshis_plain_nofloat(offerinfo.want_amount)) if offerinfo.give_amount is not None: self.pi.give_amount_e.setText( format_satoshis_plain_nofloat(offerinfo.give_amount)) self.want_rtime = offerinfo.want_rtime self.give_rtime = offerinfo.give_rtime self.salt = offerinfo.salt self.pi.update_amounts()
def on_update(self): self.clear() self.coins, self.spent = self.halfswapcon.get_coins() self.unspent = self.coins.copy() for c in self.spent: self.unspent.pop(c) wallet = self.halfswapcon.wallet def conf_info(tx_hash): height, conf, timestamp = wallet.get_tx_height(tx_hash) status, status_str = wallet.get_tx_status(tx_hash, height, conf, timestamp) if status not in self.statusIcons: self.statusIcons[status] = QIcon(":icons/" + TX_ICONS[status]) return self.statusIcons[status], status, conf for c, (height, v) in self.coins.items(): amount = format_satoshis_plain_nofloat(v) item = SortableTreeWidgetItem(['', amount, c, '']) icon, status, conf = conf_info(c.split(':')[0]) item.setData(0, SortableTreeWidgetItem.DataRole, (status, conf)) item.setToolTip( 0, str(conf) + " confirmation" + ("s" if conf != 1 else "")) item.setIcon(0, icon) item.setFont(1, QFont(MONOSPACE_FONT)) item.setFont(2, QFont(MONOSPACE_FONT)) item.setData(0, Qt.UserRole, c) tx_hash_spent = self.spent.get(c) item.setData(3, Qt.UserRole, tx_hash_spent) if tx_hash_spent: icon, status, conf = conf_info(tx_hash_spent) item.setIcon(3, icon) item.setToolTip( 3, str(conf) + " confirmation" + ("s" if conf != 1 else "")) self.addTopLevelItem(item)
def on_update(self): item = self.currentItem() current_tx = item.data(0, Qt.UserRole) if item else None self.clear() cmw = self.parent.cmw wallet = self.wallet chanaddress = cmw.address pmw = self.parent.pmw if pmw: mypubkey = pmw.key.pubkey else: mypubkey = None # internal function to be called within loop below def putitem(i, datastr): item = SortableTreeWidgetItem([ '', status_str, 'me' if from_me else from_pubkey[-3:].hex(), datastr, ]) if status not in self.statusIcons: self.statusIcons[status] = QIcon(":icons/" + TX_ICONS[status]) icon = self.statusIcons[status] item.setIcon(0, icon) item.setData(0, SortableTreeWidgetItem.DataRole, (status, conf)) item.setToolTip( 0, str(conf) + " confirmation" + ("s" if conf != 1 else "")) item.setData(0, Qt.UserRole, tx_hash) item.setData(2, Qt.UserRole, from_pubkey) item.setToolTip(3, '<p>%s</p>' % (escape(datastr), )) self.insertTopLevelItem(0, item) if current_tx == tx_hash: self.setCurrentItem(item) return item for tx_hash, height in wallet.get_address_history(chanaddress): info = cmw.messageinfo.get(tx_hash) if not info: continue height, conf, timestamp = wallet.get_tx_height(tx_hash) status, status_str = wallet.get_tx_status(tx_hash, height, conf, timestamp) from_pubkey = info['src'] from_me = (from_pubkey == mypubkey) if info['status'] == 'processing': # tx needs to be verified putitem(0, 'verifying') continue messagebytes = info.get('message') if messagebytes is None: putitem(0, '?') continue try: tag = messagebytes[:2] assert len(messagebytes) == 18 if tag == b'\0\0': amtw = int.from_bytes(messagebytes[2:10], 'big') amtg = int.from_bytes(messagebytes[10:18], 'big') nw = self.parent.n1 ng = self.parent.n2 data = (False, amtw, amtg) elif tag == b'\0\1': amtg = int.from_bytes(messagebytes[2:10], 'big') amtw = int.from_bytes(messagebytes[10:18], 'big') ng = self.parent.n1 nw = self.parent.n2 data = (True, amtw, amtg) item = putitem( 0, 'Wants %s %s for %s %s' % ( format_satoshis_plain_nofloat(amtw), nw, format_satoshis_plain_nofloat(amtg), ng, )) item.setData(3, Qt.UserRole, data) except: item = putitem(0, 'raw:' + messagebytes.hex())
def setAmount(self, amount): if amount is None: self.setText(" ") # Space forces repaint in case units changed else: self.setText(format_satoshis_plain_nofloat(amount, self.decimal_point()))
def mint_token(self, preview=False): decimals = int(self.token_dec.value()) mint_baton_vout = 2 if self.token_baton_to_e.text( ) != '' and not self.token_fixed_supply_cb.isChecked() else None init_mint_qty = self.token_qty_e.get_amount() if init_mint_qty is None: self.show_message(_("Invalid token quantity entered.")) return if init_mint_qty > (2**64) - 1: maxqty = format_satoshis_plain_nofloat((2**64) - 1, decimals) self.show_message( _("Token output quantity is too large. Maximum %s.") % (maxqty, )) return outputs = [] try: token_id_hex = self.token_id_e.text() token_type = self.wallet.token_types[token_id_hex]['class'] slp_op_return_msg = buildMintOpReturnOutput_V1( token_id_hex, mint_baton_vout, init_mint_qty, token_type) outputs.append(slp_op_return_msg) except OPReturnTooLarge: self.show_message( _("Optional string text causiing OP_RETURN greater than 223 bytes." )) return except Exception as e: traceback.print_exc(file=sys.stdout) self.show_message(str(e)) return try: addr = self.parse_address(self.token_pay_to_e.text()) outputs.append((TYPE_ADDRESS, addr, 546)) except: self.show_message( _("Enter a Mint Receiver Address in SLP address format.")) return if not self.token_fixed_supply_cb.isChecked(): try: addr = self.parse_address(self.token_baton_to_e.text()) outputs.append((TYPE_ADDRESS, addr, 546)) except: self.show_message( _("Enter a Baton Address in SLP address format.")) return # IMPORTANT: set wallet.sedn_slpTokenId to None to guard tokens during this transaction self.main_window.token_type_combo.setCurrentIndex(0) assert self.main_window.slp_token_id == None coins = self.main_window.get_coins() fee = None try: baton_input = self.main_window.wallet.get_slp_token_baton( self.token_id_e.text()) except SlpNoMintingBatonFound as e: self.show_message(_("No baton exists for this token.")) return desired_fee_rate = 1.0 # sats/B, just init this value for paranoia try: tx = self.main_window.wallet.make_unsigned_transaction( coins, outputs, self.main_window.config, fee, None) desired_fee_rate = tx.get_fee() / tx.estimated_size( ) # remember the fee coin chooser & wallet gave us as a fee rate so we may use it below after adding baton to adjust fee downward to this rate. except NotEnoughFunds: self.show_message(_("Insufficient funds")) return except ExcessiveFee: self.show_message(_("Your fee is too high. Max is 50 sat/byte.")) return except BaseException as e: traceback.print_exc(file=sys.stdout) self.show_message(str(e)) return # Find & Add baton to tx inputs try: baton_utxo = self.main_window.wallet.get_slp_token_baton( self.token_id_e.text()) except SlpNoMintingBatonFound: self.show_message( _("There is no minting baton found for this token.")) return tx.add_inputs([baton_utxo]) for txin in tx._inputs: self.main_window.wallet.add_input_info(txin) def tx_adjust_change_amount_based_on_baton_amount( tx, desired_fee_rate): ''' adjust change amount (based on amount added from baton) ''' if len(tx._outputs) not in (3, 4): # no change, or a tx shape we don't know about self.print_error(f"Unkown tx shape, not adjusting fee!") return chg = tx._outputs[ -1] # change is always the last output due to BIP_LI01 sorting assert len(chg) == 3, "Expected tx output to be of length 3" if not self.main_window.wallet.is_mine(chg[1]): self.print_error( f"Unkown change address {chg[1]}, not adjusting fee!") return chg_amt = chg[2] if chg_amt <= 546: # if change is 546, then the BIP_LI01 sorting doesn't guarantee # change output is at the end.. so we don't know which was # changed based on the heuristics this code relies on.. so.. # Abort! Abort! self.print_error( "Could not determine change output, not adjusting fee!") return curr_fee, curr_size = tx.get_fee(), tx.estimated_size() fee_rate = curr_fee / curr_size diff = math.ceil((fee_rate - desired_fee_rate) * curr_size) if diff > 0: tx._outputs[-1] = (chg[0], chg[1], chg[2] + diff ) # adjust the output self.print_error( f"Added {diff} sats to change to maintain fee rate of {desired_fee_rate:0.2f}, new fee: {tx.get_fee()}" ) tx_adjust_change_amount_based_on_baton_amount(tx, desired_fee_rate) if preview: show_transaction(tx, self.main_window, None, False, self) return msg = [] if self.main_window.wallet.has_password(): msg.append("") msg.append(_("Enter your password to proceed")) password = self.main_window.password_dialog('\n'.join(msg)) if not password: return else: password = None tx_desc = None def sign_done(success): if success: if not tx.is_complete(): show_transaction(tx, self.main_window, None, False, self) self.main_window.do_clear() else: self.main_window.broadcast_transaction(tx, tx_desc) self.main_window.sign_tx_with_password(tx, sign_done, password) self.mint_button.setDisabled(True) self.close()
def mint_token(self, preview=False): decimals = int(self.token_dec.value()) mint_baton_vout = 2 if self.token_baton_to_e.text( ) != '' and not self.token_fixed_supply_cb.isChecked() else None init_mint_qty = self.token_qty_e.get_amount() if init_mint_qty is None: self.show_message(_("Invalid token quantity entered.")) return if init_mint_qty > (2**64) - 1: maxqty = format_satoshis_plain_nofloat((2**64) - 1, decimals) self.show_message( _("Token output quantity is too large. Maximum %s.") % (maxqty, )) return outputs = [] try: token_id_hex = self.token_id_e.text() slp_op_return_msg = buildMintOpReturnOutput_V1( token_id_hex, mint_baton_vout, init_mint_qty) outputs.append(slp_op_return_msg) except OPReturnTooLarge: self.show_message( _("Optional string text causiing OP_RETURN greater than 223 bytes." )) return except Exception as e: traceback.print_exc(file=sys.stdout) self.show_message(str(e)) return try: addr = self.parse_address(self.token_pay_to_e.text()) outputs.append((TYPE_ADDRESS, addr, 546)) except: self.show_message( _("Enter a Mint Receiver Address in SLP address format.")) return if not self.token_fixed_supply_cb.isChecked(): try: addr = self.parse_address(self.token_baton_to_e.text()) outputs.append((TYPE_ADDRESS, addr, 546)) except: self.show_message( _("Enter a Baton Address in SLP address format.")) return # IMPORTANT: set wallet.sedn_slpTokenId to None to guard tokens during this transaction self.main_window.token_type_combo.setCurrentIndex(0) assert self.main_window.slp_token_id == None coins = self.main_window.get_coins() fee = None try: baton_input = self.main_window.wallet.get_slp_token_baton( self.token_id_e.text()) except SlpNoMintingBatonFound as e: self.show_message(_("No baton exists for this token.")) return try: tx = self.main_window.wallet.make_unsigned_transaction( coins, outputs, self.main_window.config, fee, None) except NotEnoughFunds: self.show_message(_("Insufficient funds")) return except ExcessiveFee: self.show_message(_("Your fee is too high. Max is 50 sat/byte.")) return except BaseException as e: traceback.print_exc(file=sys.stdout) self.show_message(str(e)) return # Find & Add baton to tx inputs try: baton_utxo = self.main_window.wallet.get_slp_token_baton( self.token_id_e.text()) except SlpNoMintingBatonFound: self.show_message( _("There is no minting baton found for this token.")) return tx.add_inputs([baton_utxo]) for txin in tx._inputs: self.main_window.wallet.add_input_info(txin) # TODO: adjust change amount (based on amount added from baton) if preview: show_transaction(tx, self.main_window, None, False, self) return msg = [] if self.main_window.wallet.has_password(): msg.append("") msg.append(_("Enter your password to proceed")) password = self.main_window.password_dialog('\n'.join(msg)) if not password: return else: password = None tx_desc = None def sign_done(success): if success: if not tx.is_complete(): show_transaction(tx, self.main_window, None, False, self) self.main_window.do_clear() else: self.main_window.broadcast_transaction(tx, tx_desc) self.main_window.sign_tx_with_password(tx, sign_done, password) self.mint_button.setDisabled(True) self.close()
def create_token(self, preview=False, multisig_tx_to_sign=None): token_name = self.token_name_e.text( ) if self.token_name_e.text() != '' else None ticker = self.token_ticker_e.text( ) if self.token_ticker_e.text() != '' else None token_document_url = self.token_url_e.text( ) if self.token_url_e.text() != '' else None token_document_hash_hex = self.token_dochash_e.text( ) if self.token_dochash_e.text() != '' else None decimals = int(self.token_ds_e.value()) mint_baton_vout = 2 if self.token_baton_to_e.text( ) != '' and not self.token_fixed_supply_cb.isChecked() else None init_mint_qty = self.token_qty_e.get_amount() if init_mint_qty is None: self.show_message(_("Invalid token quantity entered.")) return if init_mint_qty > (2**64) - 1: maxqty = format_satoshis_plain_nofloat((2**64) - 1, decimals) self.show_message( _("Token output quantity is too large. Maximum %s.") % (maxqty, )) return if token_document_hash_hex != None: if len(token_document_hash_hex) != 64: self.show_message( _("Token document hash must be a 32 byte hexidecimal string or left empty." )) return outputs = [] try: slp_op_return_msg = buildGenesisOpReturnOutput_V1( ticker, token_name, token_document_url, token_document_hash_hex, decimals, mint_baton_vout, init_mint_qty, token_type=self.token_type) outputs.append(slp_op_return_msg) except OPReturnTooLarge: self.show_message( _("Optional string text causiing OP_RETURN greater than 223 bytes." )) return except Exception as e: traceback.print_exc(file=sys.stdout) self.show_message(str(e)) return try: addr = self.parse_address(self.token_pay_to_e.text()) outputs.append((TYPE_ADDRESS, addr, 546)) except: self.show_message( _("Must have Receiver Address in simpleledger format.")) return if not self.token_fixed_supply_cb.isChecked( ) and not self.nft_parent_id: try: addr = self.parse_address(self.token_baton_to_e.text()) outputs.append((TYPE_ADDRESS, addr, 546)) except: self.show_message( _("Must have Baton Address in simpleledger format.")) return # IMPORTANT: set wallet.sedn_slpTokenId to None to guard tokens during this transaction self.main_window.token_type_combo.setCurrentIndex(0) assert self.main_window.slp_token_id == None coins = self.main_window.get_coins() fee = None try: selected_coin = None if self.nft_parent_id and multisig_tx_to_sign: tx = multisig_tx_to_sign parent_txo_hash = multisig_tx_to_sign.inputs( )[0]['prevout_hash'] parent_txo_n = multisig_tx_to_sign.inputs()[0]['prevout_n'] slp_coins = self.wallet.get_slp_utxos( self.nft_parent_id, domain=None, exclude_frozen=True, confirmed_only=self.main_window.config.get( 'confirmed_only', False), slp_include_invalid=False, slp_include_baton=False) try: for coin in slp_coins: if coin['prevout_hash'] == parent_txo_hash \ and coin['prevout_n'] == parent_txo_n \ and coin['token_value'] != "MINT_BATON": selected_coin = coin # total_burn_amt += coin['token_value'] except KeyError: pass elif self.nft_parent_id: selected_coin = get_nft_parent_coin(self.nft_parent_id, self.main_window) if selected_coin: tx = self.main_window.wallet.make_unsigned_transaction( coins, outputs, self.main_window.config, fee, None, mandatory_coins=[selected_coin]) else: raise Exception( 'Must have a parent NFT coin with value of 1 first.') else: tx = self.main_window.wallet.make_unsigned_transaction( coins, outputs, self.main_window.config, fee, None) except NotEnoughFunds: self.show_message(_("Insufficient funds")) return except ExcessiveFee: self.show_message(_("Your fee is too high. Max is 50 sat/byte.")) return except BaseException as e: traceback.print_exc(file=sys.stdout) self.show_message(str(e)) return if preview: if self.nft_parent_id: show_transaction(tx, self.main_window, None, False, self, slp_coins_to_burn=[selected_coin], slp_amt_to_burn=1) else: show_transaction(tx, self.main_window, None, False, self) return msg = [] if self.main_window.wallet.has_password(): msg.append("") msg.append(_("Enter your password to proceed")) password = self.main_window.password_dialog('\n'.join(msg)) if not password: return else: password = None tx_desc = None def sign_done(success): if success: if not tx.is_complete(): show_transaction(tx, self.main_window, None, False, self) self.main_window.do_clear() else: token_id = tx.txid() if self.token_name_e.text() == '': wallet_name = tx.txid()[0:5] else: wallet_name = self.token_name_e.text()[0:20] # Check for duplication error d = self.wallet.token_types.get(token_id) for tid, d in self.wallet.token_types.items(): if d['name'] == wallet_name and tid != token_id: wallet_name = wallet_name + "-" + token_id[:3] break self.broadcast_transaction(tx, self.token_name_e.text(), wallet_name) if self.nft_parent_id: self.sign_tx_with_password(tx, sign_done, password, slp_coins_to_burn=[selected_coin], slp_amt_to_burn=1) else: self.sign_tx_with_password(tx, sign_done, password)
def __init__(self, app, config, swapper, amount1, amount2): # probably need config # top level window QDialog.__init__(self, parent=None) # self.wallet = parent.wallet self.app = app self.config = config self.swapper = swapper currency1 = swapper.network1.currency currency2 = swapper.network2.currency shownamount1 = format_satoshis_plain_nofloat(amount1) + " " + currency1 shownamount2 = format_satoshis_plain_nofloat(amount2) + " " + currency2 youstr = _("You agreed to offer %s at this address:") themstr = _("They agreed to offer %s at this address:") self.setWindowTitle(_("Atomic Swap")) self.setMinimumWidth(800) vbox = QVBoxLayout() self.setLayout(vbox) self.party_info_label = QLabel( _("You are party A.") if swapper. i_am_party_A else _("You are party B.")) self.party_info_label.setAlignment(Qt.AlignCenter) self.party_info_label.setTextFormat(Qt.RichText) vbox.addWidget(self.party_info_label) self.phase_info_label = QLabel("") self.phase_info_label.setAlignment(Qt.AlignCenter) self.phase_info_label.setTextFormat(Qt.RichText) vbox.addWidget(self.phase_info_label) self.instruction_label = QLabel("") self.instruction_label.setAlignment(Qt.AlignCenter) self.instruction_label.setTextFormat(Qt.RichText) vbox.addWidget(self.instruction_label) ## # Split parts below ## hbox = QHBoxLayout() vbox.addLayout(hbox) vboxleft = QVBoxLayout() hbox.addLayout(vboxleft) vboxright = QVBoxLayout() hbox.addLayout(vboxright) vboxleft.addWidget( QLabel( (youstr if swapper.i_am_party_A else themstr) % shownamount1)) addr_e = QLineEdit(str(self.swapper.contract1.address)) addr_e.setReadOnly(True) vboxleft.addWidget(addr_e) vboxleft.addWidget(QLabel(_("Coins at this address") + ':')) self.leftutxolist = SwapUTXOList(self, swapper.sc1) vboxleft.addWidget(self.leftutxolist) self.time_remaining_1_e = QLabel() vboxleft.addWidget(self.time_remaining_1_e) hbox = QHBoxLayout() vboxleft.addLayout(hbox) if swapper.i_am_party_A: b = QPushButton(_("Refund")) b.clicked.connect(self.refund) hbox.addWidget(b) else: self.redeem_button = QPushButton(_("Redeem")) self.redeem_button.clicked.connect(self.redeem) hbox.addWidget(self.redeem_button) vboxright.addWidget( QLabel( (themstr if swapper.i_am_party_A else youstr) % shownamount2)) addr_e = QLineEdit(str(self.swapper.contract2.address)) addr_e.setReadOnly(True) vboxright.addWidget(addr_e) vboxright.addWidget(QLabel(_("Coins at this address") + ':')) self.rightutxolist = SwapUTXOList(self, swapper.sc2) vboxright.addWidget(self.rightutxolist) self.time_remaining_2_e = QLabel() vboxright.addWidget(self.time_remaining_2_e) hbox = QHBoxLayout() vboxright.addLayout(hbox) if swapper.i_am_party_A: self.redeem_button = QPushButton(_("Redeem")) self.redeem_button.clicked.connect(self.redeem) hbox.addWidget(self.redeem_button) else: b = QPushButton(_("Refund")) b.clicked.connect(self.refund) hbox.addWidget(b) swapper.network1.register_callback(self.leftutxolist.on_network, ['updated', 'verified']) swapper.network2.register_callback(self.rightutxolist.on_network, ['updated', 'verified']) self.timer = QTimer() self.timer.setInterval(1000) self.timer.timeout.connect(self.timed_update) self.timer.start() self.timed_update() # call once to start
def on_update(self): item = self.currentItem() current_tx, current_i = item.data(0, Qt.UserRole) if item else (None, None) self.clear() wallet = self.parent.wallet pmw = self.parent.pmw key = self.parent.key mypubkey = key.pubkey myaddress = key.address # internal function to be called within loop below def putitem(i, typ, datastr): if to_me: to_str = 'me' elif to_pubkey: to_str = to_pubkey[-3:].hex() else: to_str = 'unk' item = SortableTreeWidgetItem([ '', status_str, 'me' if from_me else from_pubkey[-3:].hex(), to_str, typ, datastr, ]) if status not in self.statusIcons: self.statusIcons[status] = QIcon(":icons/" + TX_ICONS[status]) icon = self.statusIcons[status] item.setIcon(0, icon) item.setData(0, SortableTreeWidgetItem.DataRole, (status, conf)) item.setToolTip( 0, str(conf) + " confirmation" + ("s" if conf != 1 else "")) item.setData(0, Qt.UserRole, (tx_hash, i)) item.setData(2, Qt.UserRole, from_pubkey) item.setData(3, Qt.UserRole, to_pubkey) item.setToolTip(5, '<p>%s</p>' % (escape(datastr), )) self.insertTopLevelItem(0, item) if current_tx == tx_hash and current_i == i: self.setCurrentItem(item) return item for tx_hash, height in wallet.get_address_history(myaddress): info = pmw.messageinfo.get(tx_hash) if not info: continue height, conf, timestamp = wallet.get_tx_height(tx_hash) status, status_str = wallet.get_tx_status(tx_hash, height, conf, timestamp) from_pubkey = info['src'] dest_addr = info['dst'] from_me = (from_pubkey == mypubkey) to_me = (dest_addr == myaddress) if to_me: to_pubkey = mypubkey else: to_pubkey = pmw.known_pubkeys.get(dest_addr) if info['status'] == 'processing': # tx needs to be verified putitem(0, '-', 'verifying') continue messagebytes = info.get('message') if messagebytes is None: putitem(0, '?', '') continue try: osm = openswap.OpenSwapMessage.from_bytes(messagebytes) except Exception as e: try: message = repr(messagebytes.decode('utf8')) except: message = messagebytes.hex() putitem(0, 'raw', "raw: " + message) continue for i, pak in enumerate(osm.packets): if isinstance(pak, openswap.PacketPad): # skip padding continue if isinstance(pak, openswap.PacketOffer) and not from_me: # save incoming offer packets self.incoming_offers[(from_pubkey, pak.offer_info)] = pak try: datastr = pak.to_ui_string() except Exception as e: print(e) datastr = str(pak) if isinstance(pak, openswap.PacketOffer): remain = pak.expire_time - time.time() if remain > 0: expstr = _("%d minutes remain") % (round(remain / 60.)) else: expstr = _("expired") if from_me: fmtstr = _('You offer %s%s to get %s%s (%s)') else: fmtstr = _('They offer %s%s to get %s%s (%s)') datastr = fmtstr % ( format_satoshis_plain_nofloat( pak.offer_info.give_amount), pak.offer_info.give_ticker.decode('utf8'), format_satoshis_plain_nofloat( pak.offer_info.want_amount), pak.offer_info.want_ticker.decode('utf8'), expstr, ) item = putitem(i, 'OS', datastr) item.setData(5, Qt.UserRole, pak)