ui: Add repeat offer feature.

Does not transfer the fee values yet.
2024-05-20_merge
tecnovert 2 years ago
parent c87f66a041
commit a6a6865e95
No known key found for this signature in database
GPG Key ID: 8ED6D8750C4E3F93
  1. 13
      basicswap/http_server.py
  2. 13
      basicswap/templates/offer.html
  3. 74
      basicswap/ui/page_offers.py

@ -9,7 +9,7 @@ import json
import traceback import traceback
import threading import threading
import http.client import http.client
import urllib.parse from urllib import parse
from http.server import BaseHTTPRequestHandler, HTTPServer from http.server import BaseHTTPRequestHandler, HTTPServer
from jinja2 import Environment, PackageLoader from jinja2 import Environment, PackageLoader
@ -90,10 +90,13 @@ def listExplorerActions(swap_client):
class HttpHandler(BaseHTTPRequestHandler): class HttpHandler(BaseHTTPRequestHandler):
def generate_form_id(self):
return os.urandom(8).hex()
def checkForm(self, post_string, name, messages): def checkForm(self, post_string, name, messages):
if post_string == '': if post_string == '':
return None return None
form_data = urllib.parse.parse_qs(post_string) form_data = parse.parse_qs(post_string)
form_id = form_data[b'formid'][0].decode('utf-8') form_id = form_data[b'formid'][0].decode('utf-8')
if self.server.last_form_id.get(name, None) == form_id: if self.server.last_form_id.get(name, None) == form_id:
messages.append('Prevented double submit for form {}.'.format(form_id)) messages.append('Prevented double submit for form {}.'.format(form_id))
@ -118,10 +121,11 @@ class HttpHandler(BaseHTTPRequestHandler):
if swap_client.debug: if swap_client.debug:
swap_client.log.error(traceback.format_exc()) swap_client.log.error(traceback.format_exc())
self.putHeaders(200, 'text/html')
return bytes(template.render( return bytes(template.render(
title=self.server.title, title=self.server.title,
h2=self.server.title, h2=self.server.title,
form_id=os.urandom(8).hex(), form_id=self.generate_form_id(),
**args_dict, **args_dict,
), 'UTF-8') ), 'UTF-8')
@ -535,7 +539,7 @@ class HttpHandler(BaseHTTPRequestHandler):
self.end_headers() self.end_headers()
def handle_http(self, status_code, path, post_string='', is_json=False): def handle_http(self, status_code, path, post_string='', is_json=False):
parsed = urllib.parse.urlparse(self.path) parsed = parse.urlparse(self.path)
url_split = parsed.path.split('/') url_split = parsed.path.split('/')
if post_string == '' and len(parsed.query) > 0: if post_string == '' and len(parsed.query) > 0:
post_string = parsed.query post_string = parsed.query
@ -595,7 +599,6 @@ class HttpHandler(BaseHTTPRequestHandler):
return self.page_error(str(ex)) return self.page_error(str(ex))
try: try:
self.putHeaders(status_code, 'text/html')
if len(url_split) > 1: if len(url_split) > 1:
page = url_split[1] page = url_split[1]
if page == 'active': if page == 'active':

@ -379,13 +379,22 @@ None
<span>New Bid</span> </button> <span>New Bid</span> </button>
</div> </div>
{% if data.sent == 'True' and data.was_revoked != true %}
{% if data.sent == 'True' %}
<div class="w-full md:w-auto p-1.5 ml-2">
<button name="repeat_offer" value="Repeat Offer" type="submit" class="flex flex-wrap justify-center w-full px-4 py-2.5 font-medium text-sm text-coolGray-500 hover:text-coolGray-600 border border-coolGray-200 hover:border-coolGray-300 bg-white rounded-md shadow-button focus:ring-0 focus:outline-none">
<span>Repeat Offer</span> </button>
</div>
{% if data.was_revoked != true %}
<div class="w-full md:w-auto p-1.5 ml-2"> <div class="w-full md:w-auto p-1.5 ml-2">
<button name="revoke_offer" value="Revoke Offer" type="submit" onclick="return confirmPopup();" class="flex flex-wrap justify-center w-full px-4 py-2.5 font-medium text-sm text-coolGray-500 hover:text-coolGray-600 border border-coolGray-200 hover:border-coolGray-300 bg-white rounded-md shadow-button focus:ring-0 focus:outline-none"> <button name="revoke_offer" value="Revoke Offer" type="submit" onclick="return confirmPopup();" class="flex flex-wrap justify-center w-full px-4 py-2.5 font-medium text-sm text-coolGray-500 hover:text-coolGray-600 border border-coolGray-200 hover:border-coolGray-300 bg-white rounded-md shadow-button focus:ring-0 focus:outline-none">
<span>Revoke order</span> </button> <span>Revoke Offer</span> </button>
</div> </div>
{% endif %}
{% endif %} {% endif %}
<!-- todo <!-- todo

@ -6,6 +6,7 @@
import traceback import traceback
from urllib import parse
from .util import ( from .util import (
PAGE_LIMIT, PAGE_LIMIT,
getCoinType, getCoinType,
@ -44,6 +45,15 @@ def value_or_none(v):
return v return v
def decode_offer_id(v):
try:
offer_id = bytes.fromhex(v)
ensure(len(offer_id) == 28, 'Bad offer ID')
return offer_id
except Exception:
raise ValueError('Bad offer ID')
def parseOfferFormData(swap_client, form_data, page_data, options={}): def parseOfferFormData(swap_client, form_data, page_data, options={}):
errors = [] errors = []
parsed_data = {} parsed_data = {}
@ -264,6 +274,51 @@ def postNewOffer(swap_client, form_data):
return postNewOfferFromParsed(swap_client, parsed_data) return postNewOfferFromParsed(swap_client, parsed_data)
def offer_to_post_string(self, swap_client, offer_id):
offer, xmr_offer = swap_client.getXmrOffer(offer_id)
ensure(offer, 'Unknown offer ID')
ci_from = swap_client.ci(offer.coin_from)
ci_to = swap_client.ci(offer.coin_to)
offer_data = {
'formid': self.generate_form_id(),
'addr_to': offer.addr_to,
'addr_from': offer.addr_from,
'coin_from': offer.coin_from,
'coin_to': offer.coin_to,
# TODO store fee conf, or pass directly
# 'fee_from_conf'
# 'fee_to_conf'
'amt_from': ci_from.format_amount(offer.amount_from),
'amt_bid_min': ci_from.format_amount(offer.min_bid_amount),
'rate': ci_to.format_amount(offer.rate),
'amt_to': ci_to.format_amount((offer.amount_from * offer.rate) // ci_from.COIN()),
'validhrs': offer.time_valid // (60 * 60),
}
swap_client.log.error('[rm] offer.amount_negotiable {}'.format(offer.amount_negotiable))
swap_client.log.error('[rm] offer.rate_negotiable {}'.format(offer.rate_negotiable))
if offer.amount_negotiable:
offer_data['amt_var'] = True
if offer.rate_negotiable:
offer_data['rate_var'] = True
if offer.lock_type == TxLockTypes.SEQUENCE_LOCK_TIME or offer.lock_type == TxLockTypes.ABS_LOCK_TIME:
if offer.lock_value > 60 * 60:
offer_data['lockhrs'] = offer.lock_value // (60 * 60)
else:
offer_data['lockhrs'] = offer.lock_value // 60
try:
strategy = swap_client.getLinkedStrategy(Concepts.OFFER, offer.offer_id)
swap_client.log.error('[rm] strategy {}'.format(strategy))
offer_data['automation_strat_id'] = strategy[0]
except Exception:
pass # None found
return parse.urlencode(offer_data).encode()
def page_newoffer(self, url_split, post_string): def page_newoffer(self, url_split, post_string):
server = self.server server = self.server
swap_client = server.swap_client swap_client = server.swap_client
@ -283,6 +338,13 @@ def page_newoffer(self, url_split, post_string):
'automation_strat_id': -1, 'automation_strat_id': -1,
'amt_bid_min': format_amount(1000, 8), 'amt_bid_min': format_amount(1000, 8),
} }
post_data = parse.parse_qs(post_string)
if 'offer_from' in post_data:
offer_from_id_hex = post_data['offer_from'][0]
offer_from_id = decode_offer_id(offer_from_id_hex)
post_string = offer_to_post_string(self, swap_client, offer_from_id)
form_data = self.checkForm(post_string, 'newoffer', err_messages) form_data = self.checkForm(post_string, 'newoffer', err_messages)
if form_data: if form_data:
@ -337,11 +399,7 @@ def page_newoffer(self, url_split, post_string):
def page_offer(self, url_split, post_string): def page_offer(self, url_split, post_string):
ensure(len(url_split) > 2, 'Offer ID not specified') ensure(len(url_split) > 2, 'Offer ID not specified')
try: offer_id = decode_offer_id(url_split[2])
offer_id = bytes.fromhex(url_split[2])
ensure(len(offer_id) == 28, 'Bad offer ID')
except Exception:
raise ValueError('Bad offer ID')
server = self.server server = self.server
swap_client = server.swap_client swap_client = server.swap_client
summary = swap_client.getSummary() summary = swap_client.getSummary()
@ -373,6 +431,12 @@ def page_offer(self, url_split, post_string):
messages.append('Offer revoked') messages.append('Offer revoked')
except Exception as ex: except Exception as ex:
messages.append('Revoke offer failed: ' + str(ex)) messages.append('Revoke offer failed: ' + str(ex))
elif b'repeat_offer' in form_data:
# Can't set the post data here as browsers will always resend the original post data when responding to redirects
self.send_response(302)
self.send_header('Location', '/newoffer?offer_from=' + offer_id.hex())
self.end_headers()
return bytes()
elif b'newbid' in form_data: elif b'newbid' in form_data:
show_bid_form = True show_bid_form = True
elif b'sendbid' in form_data: elif b'sendbid' in form_data:

Loading…
Cancel
Save