From ed77c9661eebf093414d92f4adf22cca2e122bd8 Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Fri, 21 Mar 2025 09:39:38 +0300 Subject: [PATCH] tx.new_swap: add network-estimated fee display; related cleanups --- mmgen/proto/btc/tx/new.py | 13 ++++++++----- mmgen/proto/eth/tx/new.py | 14 ++++++++++---- mmgen/swap/proto/thorchain/thornode.py | 3 ++- mmgen/tx/bump.py | 2 +- mmgen/tx/new.py | 15 +++++++++++---- mmgen/tx/new_swap.py | 4 ++-- 6 files changed, 34 insertions(+), 17 deletions(-) diff --git a/mmgen/proto/btc/tx/new.py b/mmgen/proto/btc/tx/new.py index 0b7ad853..8f685ed6 100755 --- a/mmgen/proto/btc/tx/new.py +++ b/mmgen/proto/btc/tx/new.py @@ -54,6 +54,9 @@ class New(Base, TxNew): t = fe_type)) self._fee_estimate_fail_warning_shown = True + def network_fee_to_unit_disp(self, net_fee): + return '{} sat/byte'.format(net_fee.fee.to_unit('satoshi') // 1024) + async def get_rel_fee_from_network(self): try: ret = await self.rpc.call( @@ -71,22 +74,22 @@ class New(Base, TxNew): if fee_per_kb is None: self.warn_fee_estimate_fail(fe_type) - return fee_per_kb, fe_type + return self._net_fee(fee_per_kb, fe_type) # given tx size, rel fee and units, return absolute fee def fee_rel2abs(self, tx_size, amt_in_units, unit): return self.proto.coin_amt(int(amt_in_units * tx_size), from_unit=unit) # given network fee estimate in BTC/kB, return absolute fee using estimated tx size - def fee_est2abs(self, fee_per_kb, *, fe_type=None): + def fee_est2abs(self, net_fee): tx_size = self.estimate_size() - ret = self.proto.coin_amt('1') * (fee_per_kb * self.cfg.fee_adjust * tx_size / 1024) + ret = self.proto.coin_amt('1') * (net_fee.fee * self.cfg.fee_adjust * tx_size / 1024) if self.cfg.verbose: msg(fmt(f""" - {fe_type.upper()} fee for {self.cfg.fee_estimate_confs} confirmations: {fee_per_kb} {self.coin}/kB + {net_fee.type.upper()} fee for {self.cfg.fee_estimate_confs} confirmations: {net_fee.fee} {self.coin}/kB TX size (estimated): {tx_size} bytes Fee adjustment factor: {self.cfg.fee_adjust:.2f} - Absolute fee (fee_per_kb * adj_factor * tx_size / 1024): {ret} {self.coin} + Absolute fee (net_fee.fee * adj_factor * tx_size / 1024): {ret} {self.coin} """).strip()) return ret diff --git a/mmgen/proto/eth/tx/new.py b/mmgen/proto/eth/tx/new.py index 8de5a2c2..a18d7c6f 100755 --- a/mmgen/proto/eth/tx/new.py +++ b/mmgen/proto/eth/tx/new.py @@ -115,9 +115,15 @@ class New(Base, TxBase.New): def network_estimated_fee_label(self): return 'Network-estimated' + def network_fee_to_unit_disp(self, net_fee): + return '{} Gwei'.format(self.pretty_fmt_fee( + self.proto.coin_amt(net_fee.fee, from_unit='wei').to_unit('Gwei'))) + # get rel_fee (gas price) from network, return in native wei async def get_rel_fee_from_network(self): - return Int(await self.rpc.call('eth_gasPrice'), base=16), 'eth_gasPrice' + return self._net_fee( + Int(await self.rpc.call('eth_gasPrice'), base=16), + 'eth_gasPrice') def check_chg_addr_is_wallet_addr(self): pass @@ -131,10 +137,10 @@ class New(Base, TxBase.New): return self.proto.coin_amt(int(amt_in_units * self.gas.toWei()), from_unit=unit) # given fee estimate (gas price) in wei, return absolute fee, adjusting by self.cfg.fee_adjust - def fee_est2abs(self, rel_fee, *, fe_type=None): - ret = self.fee_gasPrice2abs(rel_fee) * self.cfg.fee_adjust + def fee_est2abs(self, net_fee): + ret = self.fee_gasPrice2abs(net_fee.fee) * self.cfg.fee_adjust if self.cfg.verbose: - msg(f'Estimated fee: {ret} ETH') + msg(f'Estimated fee: {net_fee.fee} ETH') return ret def convert_and_check_fee(self, fee, desc): diff --git a/mmgen/swap/proto/thorchain/thornode.py b/mmgen/swap/proto/thorchain/thornode.py index a1fad5f7..07b170b4 100755 --- a/mmgen/swap/proto/thorchain/thornode.py +++ b/mmgen/swap/proto/thorchain/thornode.py @@ -70,7 +70,7 @@ class Thornode: from ....util import pp_fmt, die die(2, pp_fmt(self.data)) - def format_quote(self, trade_limit, usr_trade_limit, *, deduct_est_fee=False): + async def format_quote(self, trade_limit, usr_trade_limit, *, deduct_est_fee=False): from ....util import make_timestr, ymsg from ....util2 import format_elapsed_hr from ....color import blue, green, cyan, pink, orange, redbg, yelbg, grnbg @@ -137,6 +137,7 @@ class Thornode: Reverse rate: {(in_amt / out_amt).hl()} {in_coin}/{out_coin} Recommended minimum in amount: {min_in_amt.hl()} {in_coin} Recommended fee: {pink(d['recommended_gas_rate'])} {pink(gas_unit_disp)} + Network-estimated fee: {await self.tx.network_fee_disp()} (from node) Fees: Total: {fees_t.hl()} {out_coin} ({pink(fees_pct_disp)}) Slippage: {pink(slip_pct_disp)} diff --git a/mmgen/tx/bump.py b/mmgen/tx/bump.py index fd5760d7..630b5df2 100755 --- a/mmgen/tx/bump.py +++ b/mmgen/tx/bump.py @@ -86,7 +86,7 @@ class Bump(Completed, NewSwap): if self.is_swap: self.recv_proto = self.check_swap_memo().proto self.process_swap_options() - fee_hint = self.update_vault_output(self.send_amt) + fee_hint = await self.update_vault_output(self.send_amt) else: fee_hint = None diff --git a/mmgen/tx/new.py b/mmgen/tx/new.py index 74ad8019..bc999373 100755 --- a/mmgen/tx/new.py +++ b/mmgen/tx/new.py @@ -87,6 +87,7 @@ class New(Base): """ chg_autoselected = False _funds_available = namedtuple('funds_available', ['is_positive', 'amt']) + _net_fee = namedtuple('network_fee_estimate', ['fee', 'type']) def warn_insufficient_funds(self, amt, coin): msg(self.msg_insufficient_funds.format(amt.hl(), coin)) @@ -402,14 +403,20 @@ class New(Base): self.copy_inputs_from_tw(sel_unspent) # makes self.inputs return True + async def network_fee_disp(self): + res = await self.get_rel_fee_from_network() + return pink( + 'N/A' if res.fee is None else + self.network_fee_to_unit_disp(res)) + async def get_fee(self, fee, outputs_sum, start_fee_desc): if fee: self.usr_fee = self.get_usr_fee_interactive(fee, desc=start_fee_desc) else: - fee_per_kb, fe_type = await self.get_rel_fee_from_network() + res = await self.get_rel_fee_from_network() self.usr_fee = self.get_usr_fee_interactive( - None if fee_per_kb is None else self.fee_est2abs(fee_per_kb, fe_type=fe_type), + None if res.fee is None else self.fee_est2abs(res), desc = self.network_estimated_fee_label) funds = await self.get_funds_available(self.usr_fee, outputs_sum) @@ -486,7 +493,7 @@ class New(Base): continue fee_hint = None if self.is_swap: - fee_hint = self.update_vault_output( + fee_hint = await self.update_vault_output( self.vault_output.amt or self.sum_inputs(), deduct_est_fee = self.vault_output == self.chg_output) desc = 'User-selected' if self.cfg.fee else 'Recommended' if fee_hint else None @@ -506,7 +513,7 @@ class New(Base): self.add_comment() # edits an existing comment if self.is_swap: - self.update_vault_output(self.vault_output.amt) + await self.update_vault_output(self.vault_output.amt) await self.create_serialized(locktime=locktime) # creates self.txid too diff --git a/mmgen/tx/new_swap.py b/mmgen/tx/new_swap.py index 64f8e516..7021d85a 100755 --- a/mmgen/tx/new_swap.py +++ b/mmgen/tx/new_swap.py @@ -162,7 +162,7 @@ class NewSwap(New): o['addr'] = addr self.outputs[vault_idx] = self.Output(self.proto, **o) - def update_vault_output(self, amt, *, deduct_est_fee=False): + async def update_vault_output(self, amt, *, deduct_est_fee=False): sp = get_swap_proto_mod(self.swap_proto) c = sp.rpc_client(self, amt) @@ -182,7 +182,7 @@ class NewSwap(New): c.get_quote() trade_limit = get_trade_limit() self.cfg._util.qmsg('OK') - msg(c.format_quote(trade_limit, self.usr_trade_limit, deduct_est_fee=deduct_est_fee)) + msg(await c.format_quote(trade_limit, self.usr_trade_limit, deduct_est_fee=deduct_est_fee)) ch = get_char('Press ‘r’ to refresh quote, any other key to continue: ') msg('') if ch not in 'Rr':