clean up fees a bit
This commit is contained in:
parent
d77e522721
commit
4ddda74dad
|
@ -3,7 +3,6 @@ from kivy.factory import Factory
|
|||
from kivy.properties import ObjectProperty
|
||||
from kivy.lang import Builder
|
||||
|
||||
from electrum.util import fee_levels
|
||||
from electrum_gui.kivy.i18n import _
|
||||
|
||||
Builder.load_string('''
|
||||
|
@ -29,7 +28,11 @@ Builder.load_string('''
|
|||
text: _('New Fee')
|
||||
value: ''
|
||||
Label:
|
||||
id: tooltip
|
||||
id: tooltip1
|
||||
text: ''
|
||||
size_hint_y: None
|
||||
Label:
|
||||
id: tooltip2
|
||||
text: ''
|
||||
size_hint_y: None
|
||||
Slider:
|
||||
|
@ -72,39 +75,39 @@ class BumpFeeDialog(Factory.Popup):
|
|||
self.tx_size = size
|
||||
self.callback = callback
|
||||
self.config = app.electrum_config
|
||||
self.fee_step = self.config.max_fee_rate() / 10
|
||||
self.dynfees = self.config.is_dynfee() and self.app.network
|
||||
self.mempool = self.config.use_mempool_fees()
|
||||
self.dynfees = self.config.is_dynfee() and self.app.network and self.config.has_dynamic_fees_ready()
|
||||
self.ids.old_fee.value = self.app.format_amount_and_units(self.init_fee)
|
||||
self.update_slider()
|
||||
self.update_text()
|
||||
|
||||
def update_text(self):
|
||||
value = int(self.ids.slider.value)
|
||||
self.ids.new_fee.value = self.app.format_amount_and_units(self.get_fee())
|
||||
if self.dynfees:
|
||||
value = int(self.ids.slider.value)
|
||||
self.ids.tooltip.text = fee_levels[value]
|
||||
fee = self.get_fee()
|
||||
self.ids.new_fee.value = self.app.format_amount_and_units(fee)
|
||||
pos = int(self.ids.slider.value)
|
||||
fee_rate = self.get_fee_rate()
|
||||
text, tooltip = self.config.get_fee_text(pos, self.dynfees, self.mempool, fee_rate)
|
||||
self.ids.tooltip1.text = text
|
||||
self.ids.tooltip2.text = tooltip
|
||||
|
||||
def update_slider(self):
|
||||
slider = self.ids.slider
|
||||
maxp, pos, fee_rate = self.config.get_fee_slider(self.dynfees, self.mempool)
|
||||
slider.range = (0, maxp)
|
||||
slider.step = 1
|
||||
slider.value = pos
|
||||
|
||||
def get_fee_rate(self):
|
||||
pos = int(self.ids.slider.value)
|
||||
if self.dynfees:
|
||||
slider.range = (0, 4)
|
||||
slider.step = 1
|
||||
slider.value = 3
|
||||
fee_rate = self.config.depth_to_fee(pos) if self.mempool else self.config.eta_to_fee(pos)
|
||||
else:
|
||||
slider.range = (1, 10)
|
||||
slider.step = 1
|
||||
rate = self.init_fee*1000//self.tx_size
|
||||
slider.value = min( rate * 2 // self.fee_step, 10)
|
||||
fee_rate = self.config.static_fee(pos)
|
||||
return fee_rate
|
||||
|
||||
def get_fee(self):
|
||||
value = int(self.ids.slider.value)
|
||||
if self.dynfees:
|
||||
if self.config.has_fee_estimates():
|
||||
dynfee = self.config.dynfee(value)
|
||||
return int(dynfee * self.tx_size // 1000)
|
||||
else:
|
||||
return int(value*self.fee_step * self.tx_size // 1000)
|
||||
fee_rate = self.get_fee_rate()
|
||||
return int(fee_rate * self.tx_size // 1000)
|
||||
|
||||
def on_ok(self):
|
||||
new_fee = self.get_fee()
|
||||
|
|
|
@ -3,7 +3,6 @@ from kivy.factory import Factory
|
|||
from kivy.properties import ObjectProperty
|
||||
from kivy.lang import Builder
|
||||
|
||||
from electrum.util import fee_levels
|
||||
from electrum_gui.kivy.i18n import _
|
||||
|
||||
Builder.load_string('''
|
||||
|
|
|
@ -8,7 +8,6 @@ from electrum.i18n import languages
|
|||
from electrum_gui.kivy.i18n import _
|
||||
from electrum.plugins import run_hook
|
||||
from electrum import coinchooser
|
||||
from electrum.util import fee_levels
|
||||
|
||||
from .choice_dialog import ChoiceDialog
|
||||
|
||||
|
|
|
@ -1512,7 +1512,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError):
|
|||
x_fee_address, x_fee_amount = x_fee
|
||||
msg.append( _("Additional fees") + ": " + self.format_amount_and_units(x_fee_amount) )
|
||||
|
||||
confirm_rate = 2 * self.config.max_fee_rate()
|
||||
confirm_rate = simple_config.FEERATE_WARNING_HIGH_FEE
|
||||
if fee > confirm_rate * tx.estimated_size() / 1000:
|
||||
msg.append(_('Warning') + ': ' + _("The fee for this transaction seems unusually high."))
|
||||
|
||||
|
|
|
@ -33,6 +33,7 @@ from PyQt5.QtWidgets import *
|
|||
from electrum.bitcoin import base_encode
|
||||
from electrum.i18n import _
|
||||
from electrum.plugins import run_hook
|
||||
from electrum import simple_config
|
||||
|
||||
from electrum.util import bfh
|
||||
from electrum.wallet import UnrelatedTransactionException
|
||||
|
@ -236,9 +237,13 @@ class TxDialog(QDialog, MessageBoxMixin):
|
|||
else:
|
||||
amount_str = _("Amount sent:") + ' %s'% format_amount(-amount) + ' ' + base_unit
|
||||
size_str = _("Size:") + ' %d bytes'% size
|
||||
fee_str = _("Fee") + ': %s'% (format_amount(fee) + ' ' + base_unit if fee is not None else _('unknown'))
|
||||
fee_str = _("Fee") + ': %s' % (format_amount(fee) + ' ' + base_unit if fee is not None else _('unknown'))
|
||||
if fee is not None:
|
||||
fee_str += ' ( %s ) '% self.main_window.format_fee_rate(fee/size*1000)
|
||||
fee_rate = fee/size*1000
|
||||
fee_str += ' ( %s ) ' % self.main_window.format_fee_rate(fee_rate)
|
||||
confirm_rate = simple_config.FEERATE_WARNING_HIGH_FEE
|
||||
if fee_rate > confirm_rate:
|
||||
fee_str += ' - ' + _('Warning') + ': ' + _("high fee") + '!'
|
||||
self.amount_label.setText(amount_str)
|
||||
self.fee_label.setText(fee_str)
|
||||
self.size_label.setText(size_str)
|
||||
|
|
|
@ -108,10 +108,6 @@ NetworkConstants.set_mainnet()
|
|||
|
||||
################################## transactions
|
||||
|
||||
FEE_STEP = 10000
|
||||
MAX_FEE_RATE = 300000
|
||||
|
||||
|
||||
COINBASE_MATURITY = 100
|
||||
COIN = 100000000
|
||||
|
||||
|
|
|
@ -5,14 +5,22 @@ import os
|
|||
import stat
|
||||
|
||||
from copy import deepcopy
|
||||
|
||||
from .util import (user_dir, print_error, PrintError,
|
||||
NoDynamicFeeEstimates, format_satoshis)
|
||||
|
||||
from .bitcoin import MAX_FEE_RATE
|
||||
from .i18n import _
|
||||
|
||||
FEE_ETA_TARGETS = [25, 10, 5, 2]
|
||||
FEE_DEPTH_TARGETS = [10000000, 5000000, 2000000, 1000000, 500000, 200000, 100000]
|
||||
|
||||
# satoshi per kbyte
|
||||
FEERATE_MAX_DYNAMIC = 1500000
|
||||
FEERATE_WARNING_HIGH_FEE = 600000
|
||||
FEERATE_FALLBACK_STATIC_FEE = 150000
|
||||
FEERATE_DEFAULT_RELAY = 1000
|
||||
FEERATE_STATIC_VALUES = [5000, 10000, 20000, 30000, 50000, 70000, 100000, 150000, 200000, 300000]
|
||||
|
||||
|
||||
config = None
|
||||
|
||||
|
||||
|
@ -39,7 +47,6 @@ class SimpleConfig(PrintError):
|
|||
2. User configuration (in the user's config directory)
|
||||
They are taken in order (1. overrides config options set in 2.)
|
||||
"""
|
||||
fee_rates = [5000, 10000, 20000, 30000, 50000, 70000, 100000, 150000, 200000, 300000]
|
||||
|
||||
def __init__(self, options=None, read_user_config_function=None,
|
||||
read_user_dir_function=None):
|
||||
|
@ -261,13 +268,19 @@ class SimpleConfig(PrintError):
|
|||
path = wallet.storage.path
|
||||
self.set_key('gui_last_wallet', path)
|
||||
|
||||
def max_fee_rate(self):
|
||||
f = self.get('max_fee_rate', MAX_FEE_RATE)
|
||||
if f==0:
|
||||
f = MAX_FEE_RATE
|
||||
return f
|
||||
def impose_hard_limits_on_fee(func):
|
||||
def get_fee_within_limits(self, *args, **kwargs):
|
||||
fee = func(self, *args, **kwargs)
|
||||
if fee is None:
|
||||
return fee
|
||||
fee = min(FEERATE_MAX_DYNAMIC, fee)
|
||||
fee = max(FEERATE_DEFAULT_RELAY, fee)
|
||||
return fee
|
||||
return get_fee_within_limits
|
||||
|
||||
@impose_hard_limits_on_fee
|
||||
def eta_to_fee(self, i):
|
||||
"""Returns fee in sat/kbyte."""
|
||||
if i < 4:
|
||||
j = FEE_ETA_TARGETS[i]
|
||||
fee = self.fee_estimates.get(j)
|
||||
|
@ -276,8 +289,6 @@ class SimpleConfig(PrintError):
|
|||
fee = self.fee_estimates.get(2)
|
||||
if fee is not None:
|
||||
fee += fee/2
|
||||
if fee is not None:
|
||||
fee = min(5*MAX_FEE_RATE, fee)
|
||||
return fee
|
||||
|
||||
def fee_to_depth(self, target_fee):
|
||||
|
@ -290,7 +301,9 @@ class SimpleConfig(PrintError):
|
|||
return 0
|
||||
return depth
|
||||
|
||||
@impose_hard_limits_on_fee
|
||||
def depth_to_fee(self, i):
|
||||
"""Returns fee in sat/kbyte."""
|
||||
target = self.depth_target(i)
|
||||
depth = 0
|
||||
for fee, s in self.mempool_fees:
|
||||
|
@ -305,6 +318,8 @@ class SimpleConfig(PrintError):
|
|||
return FEE_DEPTH_TARGETS[i]
|
||||
|
||||
def eta_target(self, i):
|
||||
if i == len(FEE_ETA_TARGETS):
|
||||
return 1
|
||||
return FEE_ETA_TARGETS[i]
|
||||
|
||||
def fee_to_eta(self, fee_per_kb):
|
||||
|
@ -320,7 +335,12 @@ class SimpleConfig(PrintError):
|
|||
return "%.1f MB from tip"%(depth/1000000)
|
||||
|
||||
def eta_tooltip(self, x):
|
||||
return 'Low fee' if x < 0 else 'Within %d blocks'%x
|
||||
if x < 0:
|
||||
return _('Low fee')
|
||||
elif x == 1:
|
||||
return _('In the next block')
|
||||
else:
|
||||
return _('Within {} blocks').format(x)
|
||||
|
||||
def get_fee_status(self):
|
||||
dyn = self.is_dynfee()
|
||||
|
@ -331,6 +351,10 @@ class SimpleConfig(PrintError):
|
|||
return target
|
||||
|
||||
def get_fee_text(self, pos, dyn, mempool, fee_rate):
|
||||
"""Returns (text, tooltip) where
|
||||
text is what we target: static fee / num blocks to confirm in / mempool depth
|
||||
tooltip is the corresponding estimate (e.g. num blocks for a static fee)
|
||||
"""
|
||||
rate_str = (format_satoshis(fee_rate/1000, False, 0, 0, False) + ' sat/byte') if fee_rate is not None else 'unknown'
|
||||
if dyn:
|
||||
if mempool:
|
||||
|
@ -342,18 +366,14 @@ class SimpleConfig(PrintError):
|
|||
tooltip = rate_str
|
||||
else:
|
||||
text = rate_str
|
||||
if mempool:
|
||||
if self.has_fee_mempool():
|
||||
depth = self.fee_to_depth(fee_rate)
|
||||
tooltip = self.depth_tooltip(depth)
|
||||
else:
|
||||
tooltip = ''
|
||||
if mempool and self.has_fee_mempool():
|
||||
depth = self.fee_to_depth(fee_rate)
|
||||
tooltip = self.depth_tooltip(depth)
|
||||
elif not mempool and self.has_fee_etas():
|
||||
eta = self.fee_to_eta(fee_rate)
|
||||
tooltip = self.eta_tooltip(eta)
|
||||
else:
|
||||
if self.has_fee_etas():
|
||||
eta = self.fee_to_eta(fee_rate)
|
||||
tooltip = self.eta_tooltip(eta)
|
||||
else:
|
||||
tooltip = ''
|
||||
tooltip = ''
|
||||
return text, tooltip
|
||||
|
||||
def get_depth_level(self):
|
||||
|
@ -361,7 +381,7 @@ class SimpleConfig(PrintError):
|
|||
return min(maxp, self.get('depth_level', 2))
|
||||
|
||||
def get_fee_level(self):
|
||||
maxp = len(FEE_ETA_TARGETS) - 1
|
||||
maxp = len(FEE_ETA_TARGETS) # not (-1) to have "next block"
|
||||
return min(maxp, self.get('fee_level', 2))
|
||||
|
||||
def get_fee_slider(self, dyn, mempool):
|
||||
|
@ -372,7 +392,7 @@ class SimpleConfig(PrintError):
|
|||
fee_rate = self.depth_to_fee(pos)
|
||||
else:
|
||||
pos = self.get_fee_level()
|
||||
maxp = len(FEE_ETA_TARGETS) - 1
|
||||
maxp = len(FEE_ETA_TARGETS) # not (-1) to have "next block"
|
||||
fee_rate = self.eta_to_fee(pos)
|
||||
else:
|
||||
fee_rate = self.fee_per_kb()
|
||||
|
@ -380,12 +400,11 @@ class SimpleConfig(PrintError):
|
|||
maxp = 9
|
||||
return maxp, pos, fee_rate
|
||||
|
||||
|
||||
def static_fee(self, i):
|
||||
return self.fee_rates[i]
|
||||
return FEERATE_STATIC_VALUES[i]
|
||||
|
||||
def static_fee_index(self, value):
|
||||
dist = list(map(lambda x: abs(x - value), self.fee_rates))
|
||||
dist = list(map(lambda x: abs(x - value), FEERATE_STATIC_VALUES))
|
||||
return min(range(len(dist)), key=dist.__getitem__)
|
||||
|
||||
def has_fee_etas(self):
|
||||
|
@ -394,6 +413,12 @@ class SimpleConfig(PrintError):
|
|||
def has_fee_mempool(self):
|
||||
return bool(self.mempool_fees)
|
||||
|
||||
def has_dynamic_fees_ready(self):
|
||||
if self.use_mempool_fees():
|
||||
return self.has_fee_mempool()
|
||||
else:
|
||||
return self.has_fee_etas()
|
||||
|
||||
def is_dynfee(self):
|
||||
return bool(self.get('dynamic_fees', True))
|
||||
|
||||
|
@ -410,7 +435,7 @@ class SimpleConfig(PrintError):
|
|||
else:
|
||||
fee_rate = self.eta_to_fee(self.get_fee_level())
|
||||
else:
|
||||
fee_rate = self.get('fee_per_kb', self.max_fee_rate()/2)
|
||||
fee_rate = self.get('fee_per_kb', FEERATE_FALLBACK_STATIC_FEE)
|
||||
return fee_rate
|
||||
|
||||
def fee_per_byte(self):
|
||||
|
|
|
@ -41,7 +41,6 @@ def inv_dict(d):
|
|||
|
||||
|
||||
base_units = {'BTC':8, 'mBTC':5, 'uBTC':2}
|
||||
fee_levels = [_('Within 25 blocks'), _('Within 10 blocks'), _('Within 5 blocks'), _('Within 2 blocks'), _('In the next block')]
|
||||
|
||||
def normalize_version(v):
|
||||
return [int(x) for x in re.sub(r'(\.0+)*$','', v).split(".")]
|
||||
|
|
|
@ -78,9 +78,9 @@ TX_HEIGHT_UNCONFIRMED = 0
|
|||
|
||||
|
||||
def relayfee(network):
|
||||
RELAY_FEE = 1000
|
||||
from .simple_config import FEERATE_DEFAULT_RELAY
|
||||
MAX_RELAY_FEE = 50000
|
||||
f = network.relay_fee if network and network.relay_fee else RELAY_FEE
|
||||
f = network.relay_fee if network and network.relay_fee else FEERATE_DEFAULT_RELAY
|
||||
return min(f, MAX_RELAY_FEE)
|
||||
|
||||
def dust_threshold(network):
|
||||
|
|
Loading…
Reference in New Issue