mirror of
https://github.com/basicswap/basicswap.git
synced 2024-12-23 03:49:25 +00:00
Dynamic fee selection.
Display xmr offer fees. Display bid events. html create offer uses correct coin amount scales.
This commit is contained in:
parent
2346858145
commit
3c4c2c528f
7 changed files with 93 additions and 23 deletions
|
@ -183,7 +183,7 @@ class EventTypes(IntEnum):
|
|||
|
||||
|
||||
class EventLogTypes(IntEnum):
|
||||
FAILED_TX_B_PUBLISH = auto()
|
||||
FAILED_TX_B_LOCK_PUBLISH = auto()
|
||||
LOCK_TX_A_PUBLISHED = auto()
|
||||
LOCK_TX_B_PUBLISHED = auto()
|
||||
FAILED_TX_B_SPEND = auto()
|
||||
|
@ -308,6 +308,19 @@ def getLockName(lock_type):
|
|||
return 'time'
|
||||
|
||||
|
||||
def describeEventEntry(event_type, event_msg):
|
||||
if event_type == EventLogTypes.FAILED_TX_B_LOCK_PUBLISH:
|
||||
return 'Failed to publish lock tx b'
|
||||
if event_type == EventLogTypes.FAILED_TX_B_LOCK_PUBLISH:
|
||||
return 'Failed to publish lock tx b'
|
||||
if event_type == EventLogTypes.LOCK_TX_A_PUBLISHED:
|
||||
return 'Lock tx a published'
|
||||
if event_type == EventLogTypes.LOCK_TX_B_PUBLISHED:
|
||||
return 'Lock tx b published'
|
||||
if event_type == EventLogTypes.FAILED_TX_B_SPEND:
|
||||
return 'Failed to publish lock tx b spend'
|
||||
|
||||
|
||||
def getExpectedSequence(lockType, lockVal, coin_type):
|
||||
assert(lockVal >= 1), 'Bad lockVal'
|
||||
if lockType == SEQUENCE_LOCK_BLOCKS:
|
||||
|
@ -995,8 +1008,13 @@ class BasicSwap(BaseApp):
|
|||
# Delay before the follower can spend from the chain a lock refund tx
|
||||
xmr_offer.lock_time_2 = getExpectedSequence(lock_type, lock_value, coin_from)
|
||||
|
||||
# TODO: Dynamic fee selection
|
||||
xmr_offer.a_fee_rate = make_int(0.00032595, self.ci(coin_from).exp())
|
||||
# TODO: max fee warning?
|
||||
chain_client_settings = self.getChainClientSettings(coin_from)
|
||||
lock_tx_fee_premium = chain_client_settings.get('lock_tx_fee_premium', 0.0)
|
||||
|
||||
xmr_offer.a_fee_rate = make_int(self.getFeeRateForCoin(coin_from) + lock_tx_fee_premium, self.ci(coin_from).exp())
|
||||
|
||||
# Unused: TODO - Set priority?
|
||||
# xmr_offer.b_fee_rate = make_int(0.0012595, self.ci(coin_to).exp())
|
||||
xmr_offer.b_fee_rate = make_int(0.00002, self.ci(coin_to).exp()) # abs fee
|
||||
|
||||
|
@ -1210,9 +1228,9 @@ class BasicSwap(BaseApp):
|
|||
return self.callcoinrpc(coin_type, 'getnetworkinfo')['relayfee']
|
||||
|
||||
def getFeeRateForCoin(self, coin_type):
|
||||
# TODO: Per coin settings to override feerate
|
||||
override_feerate = self.coin_clients[coin_type].get('override_feerate', None)
|
||||
if override_feerate:
|
||||
self.log.debug('Fee rate override used for %s: %f', str(coin_type), override_feerate)
|
||||
return override_feerate
|
||||
|
||||
return self.ci(coin_type).get_fee_rate()
|
||||
|
@ -1559,6 +1577,23 @@ class BasicSwap(BaseApp):
|
|||
session.remove()
|
||||
self.mxDB.release()
|
||||
|
||||
def list_bid_events(self, bid_id):
|
||||
self.mxDB.acquire()
|
||||
events = []
|
||||
try:
|
||||
session = scoped_session(self.session_factory)
|
||||
query_str = 'SELECT created_at, event_type, event_msg FROM eventlog ' + \
|
||||
'WHERE linked_type = {} AND linked_id = x\'{}\' '.format(TableTypes.BID, bid_id.hex())
|
||||
q = self.engine.execute(query_str)
|
||||
|
||||
for row in q:
|
||||
events.append({'at': row[0], 'desc': describeEventEntry(row[1], row[2])})
|
||||
return events
|
||||
finally:
|
||||
session.close()
|
||||
session.remove()
|
||||
self.mxDB.release()
|
||||
|
||||
def acceptBid(self, bid_id):
|
||||
self.log.info('Accepting bid %s', bid_id.hex())
|
||||
|
||||
|
@ -3822,6 +3857,8 @@ class BasicSwap(BaseApp):
|
|||
|
||||
bid.setState(BidStates.XMR_SWAP_HAVE_SCRIPT_COIN_SPEND_TX)
|
||||
self.watchXmrSwap(bid, offer, xmr_swap)
|
||||
self.logBidEvent(bid, EventLogTypes.LOCK_TX_A_PUBLISHED, '', session)
|
||||
|
||||
self.saveBidInSession(bid_id, bid, session, xmr_swap)
|
||||
|
||||
def sendXmrBidCoinBLockTx(self, bid_id, session):
|
||||
|
@ -3852,7 +3889,7 @@ class BasicSwap(BaseApp):
|
|||
b_lock_tx_id = ci_to.publishBLockTx(xmr_swap.pkbv, xmr_swap.pkbs, bid.amount_to, xmr_offer.b_fee_rate)
|
||||
except Exception as ex:
|
||||
error_msg = 'publishBLockTx failed for bid {} with error {}'.format(bid_id.hex(), str(ex))
|
||||
num_retries = self.countBidEvents(bid, EventLogTypes.FAILED_TX_B_PUBLISH, session)
|
||||
num_retries = self.countBidEvents(bid, EventLogTypes.FAILED_TX_B_LOCK_PUBLISH, session)
|
||||
if num_retries > 0:
|
||||
error_msg += ', retry no. {}'.format(num_retries)
|
||||
self.log.error(error_msg)
|
||||
|
@ -3866,7 +3903,7 @@ class BasicSwap(BaseApp):
|
|||
self.setBidError(bid_id, bid, 'publishBLockTx failed: ' + str(ex), save_bid=False)
|
||||
self.saveBidInSession(bid_id, bid, session, xmr_swap, save_in_progress=offer)
|
||||
|
||||
self.logBidEvent(bid, EventLogTypes.FAILED_TX_B_PUBLISH, str_error, session)
|
||||
self.logBidEvent(bid, EventLogTypes.FAILED_TX_B_LOCK_PUBLISH, str_error, session)
|
||||
return
|
||||
|
||||
self.log.debug('Submitted lock txn %s to %s chain for bid %s', b_lock_tx_id.hex(), chainparams[coin_to]['name'], bid_id.hex())
|
||||
|
@ -3876,6 +3913,7 @@ class BasicSwap(BaseApp):
|
|||
txid=b_lock_tx_id,
|
||||
)
|
||||
bid.xmr_b_lock_tx.setState(TxStates.TX_NONE)
|
||||
self.logBidEvent(bid, EventLogTypes.LOCK_TX_B_PUBLISHED, '', session)
|
||||
|
||||
self.saveBidInSession(bid_id, bid, session, xmr_swap, save_in_progress=offer)
|
||||
|
||||
|
|
|
@ -59,7 +59,9 @@ class Offer(Base):
|
|||
from_feerate = sa.Column(sa.BigInteger)
|
||||
to_feerate = sa.Column(sa.BigInteger)
|
||||
|
||||
# Local fields
|
||||
auto_accept_bids = sa.Column(sa.Boolean)
|
||||
withdraw_to_addr = sa.Column(sa.String) # Address to spend lock tx to - address from wallet if empty TODO
|
||||
|
||||
state = sa.Column(sa.Integer)
|
||||
states = sa.Column(sa.LargeBinary) # Packed states and times
|
||||
|
@ -87,6 +89,7 @@ class Bid(Base):
|
|||
expire_at = sa.Column(sa.BigInteger)
|
||||
bid_addr = sa.Column(sa.String)
|
||||
proof_address = sa.Column(sa.String)
|
||||
withdraw_to_addr = sa.Column(sa.String) # Address to spend lock tx to - address from wallet if empty TODO
|
||||
|
||||
recovered_secret = sa.Column(sa.LargeBinary)
|
||||
amount_to = sa.Column(sa.BigInteger) # amount * offer.rate
|
||||
|
|
|
@ -17,6 +17,7 @@ from jinja2 import Environment, PackageLoader
|
|||
from . import __version__
|
||||
from .util import (
|
||||
dumpj,
|
||||
make_int,
|
||||
)
|
||||
from .chainparams import (
|
||||
chainparams,
|
||||
|
@ -300,6 +301,7 @@ class HttpHandler(BaseHTTPRequestHandler):
|
|||
|
||||
try:
|
||||
coin_from = Coins(int(form_data[b'coin_from'][0]))
|
||||
ci_from = swap_client.ci(coin_from)
|
||||
except Exception:
|
||||
raise ValueError('Unknown Coin From')
|
||||
try:
|
||||
|
@ -308,11 +310,11 @@ class HttpHandler(BaseHTTPRequestHandler):
|
|||
except Exception:
|
||||
raise ValueError('Unknown Coin To')
|
||||
|
||||
value_from = inputAmount(form_data[b'amt_from'][0].decode('utf-8'))
|
||||
value_to = inputAmount(form_data[b'amt_to'][0].decode('utf-8'))
|
||||
value_from = inputAmount(form_data[b'amt_from'][0].decode('utf-8'), ci_from)
|
||||
value_to = inputAmount(form_data[b'amt_to'][0].decode('utf-8'), ci_to)
|
||||
|
||||
min_bid = int(value_from)
|
||||
rate = int((value_to / value_from) * ci_to.COIN())
|
||||
rate = int((value_to / value_from) * ci_from.COIN())
|
||||
autoaccept = True if b'autoaccept' in form_data else False
|
||||
lock_seconds = int(form_data[b'lockhrs'][0]) * 60 * 60
|
||||
# TODO: More accurate rate
|
||||
|
@ -357,7 +359,7 @@ class HttpHandler(BaseHTTPRequestHandler):
|
|||
except Exception:
|
||||
raise ValueError('Bad offer ID')
|
||||
swap_client = self.server.swap_client
|
||||
offer = swap_client.getOffer(offer_id)
|
||||
offer, xmr_offer = swap_client.getXmrOffer(offer_id)
|
||||
assert(offer), 'Unknown offer ID'
|
||||
|
||||
messages = []
|
||||
|
@ -374,16 +376,12 @@ class HttpHandler(BaseHTTPRequestHandler):
|
|||
|
||||
sent_bid_id = swap_client.postBid(offer_id, offer.amount_from, addr_send_from=addr_from).hex()
|
||||
|
||||
coin_from = Coins(offer.coin_from)
|
||||
coin_to = Coins(offer.coin_to)
|
||||
ci_from = swap_client.ci(coin_from)
|
||||
ci_to = swap_client.ci(coin_to)
|
||||
ticker_from = swap_client.getTicker(coin_from)
|
||||
ticker_to = swap_client.getTicker(coin_to)
|
||||
ci_from = swap_client.ci(Coins(offer.coin_from))
|
||||
ci_to = swap_client.ci(Coins(offer.coin_to))
|
||||
|
||||
data = {
|
||||
'tla_from': swap_client.getTicker(coin_from),
|
||||
'tla_to': swap_client.getTicker(coin_to),
|
||||
'tla_from': ci_from.ticker(),
|
||||
'tla_to': ci_to.ticker(),
|
||||
'state': strOfferState(offer.state),
|
||||
'coin_from': ci_from.coin_name(),
|
||||
'coin_to': ci_to.coin_name(),
|
||||
|
@ -399,6 +397,13 @@ class HttpHandler(BaseHTTPRequestHandler):
|
|||
'show_bid_form': show_bid_form,
|
||||
}
|
||||
|
||||
if xmr_offer:
|
||||
int_fee_rate_now = make_int(ci_from.get_fee_rate(), ci_from.exp())
|
||||
data['xmr_type'] = True
|
||||
data['a_fee_rate'] = ci_from.format_amount(xmr_offer.a_fee_rate)
|
||||
data['a_fee_rate_verify'] = ci_from.format_amount(int_fee_rate_now)
|
||||
data['a_fee_warn'] = xmr_offer.a_fee_rate < int_fee_rate_now
|
||||
|
||||
if offer.was_sent:
|
||||
data['auto_accept'] = 'True' if offer.auto_accept_bids else 'False'
|
||||
|
||||
|
|
|
@ -68,5 +68,13 @@
|
|||
{% endfor %}
|
||||
</table>
|
||||
|
||||
<h4>Events</h4>
|
||||
<table>
|
||||
<tr><th>At</th><th>Event</th></tr>
|
||||
{% for e in data.events %}
|
||||
<tr><td>{{ e.at | formatts }}</td><td>{{ e.desc }}</td></tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
|
||||
<p><a href="/">home</a></p>
|
||||
</body></html>
|
||||
|
|
|
@ -29,8 +29,15 @@
|
|||
{% if data.sent == 'True' %}
|
||||
<tr><td>Auto Accept Bids</td><td>{{ data.auto_accept }}</td></tr>
|
||||
{% endif %}
|
||||
|
||||
{% if data.xmr_type == true %}
|
||||
<tr><td>Chain A offer fee rate</td><td>{{ data.a_fee_rate }}</td></tr>
|
||||
<tr><td>Chain A local fee rate</td><td>{{ data.a_fee_rate_verify }} {% if data.a_fee_warn == true %} WARNING {% endif %}</td></tr>
|
||||
{% endif %}
|
||||
</table>
|
||||
|
||||
|
||||
|
||||
<form method="post">
|
||||
{% if data.show_bid_form %}
|
||||
<br/><h4>New Bid</h4>
|
||||
|
|
|
@ -25,17 +25,17 @@ from .basicswap import (
|
|||
PAGE_LIMIT = 50
|
||||
|
||||
|
||||
def validateAmountString(amount):
|
||||
def validateAmountString(amount, ci):
|
||||
if type(amount) != str:
|
||||
return
|
||||
ar = amount.split('.')
|
||||
if len(ar) > 1 and len(ar[1]) > 8:
|
||||
if len(ar) > 1 and len(ar[1]) > ci.exp():
|
||||
raise ValueError('Too many decimal places in amount {}'.format(amount))
|
||||
|
||||
|
||||
def inputAmount(amount_str):
|
||||
validateAmountString(amount_str)
|
||||
return make_int(amount_str)
|
||||
def inputAmount(amount_str, ci):
|
||||
validateAmountString(amount_str, ci)
|
||||
return make_int(amount_str, ci.exp())
|
||||
|
||||
|
||||
def setCoinFilter(form_data, field_name):
|
||||
|
@ -169,4 +169,7 @@ def describeBid(swap_client, bid, offer, edit_bid, show_txns):
|
|||
data['initiate_tx_spend'] = getTxSpendHex(bid, TxTypes.ITX)
|
||||
data['participate_tx_spend'] = getTxSpendHex(bid, TxTypes.PTX)
|
||||
|
||||
if offer.swap_type == SwapTypes.XMR_SWAP:
|
||||
data['events'] = swap_client.list_bid_events(bid.bid_id)
|
||||
|
||||
return data
|
||||
|
|
|
@ -200,6 +200,9 @@ class Test(unittest.TestCase):
|
|||
assert('100.00000000' == format_amount(amount_from, scale_from))
|
||||
assert('10.000000000000' == format_amount(amount_to, scale_to))
|
||||
|
||||
rate_check = int((amount_to / amount_from) * (10 ** scale_from))
|
||||
assert(rate == rate_check)
|
||||
|
||||
scale_from = 12
|
||||
scale_to = 8
|
||||
amount_from = 1 * (10 ** scale_from)
|
||||
|
@ -209,6 +212,9 @@ class Test(unittest.TestCase):
|
|||
assert('1.000000000000' == format_amount(amount_from, scale_from))
|
||||
assert('12.00000000' == format_amount(amount_to, scale_to))
|
||||
|
||||
rate_check = int((amount_to / amount_from) * (10 ** scale_from))
|
||||
assert(rate == rate_check)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
|
Loading…
Reference in a new issue