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
					
				@ -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 New Issue
	
	Block a user