diff --git a/basicswap/basicswap.py b/basicswap/basicswap.py index 9f57bb8..7705e14 100644 --- a/basicswap/basicswap.py +++ b/basicswap/basicswap.py @@ -444,10 +444,10 @@ class BasicSwap(BaseApp): if connection_type == 'rpc': if 'rpcauth' in chain_client_settings: rpcauth = chain_client_settings['rpcauth'] - self.log.debug('Read %s rpc credentials from json settings', coin) + self.log.debug(f'Read {Coins(coin).name} rpc credentials from json settings') elif 'rpcpassword' in chain_client_settings: rpcauth = chain_client_settings['rpcuser'] + ':' + chain_client_settings['rpcpassword'] - self.log.debug('Read %s rpc credentials from json settings', coin) + self.log.debug(f'Read {Coins(coin).name} rpc credentials from json settings') session = scoped_session(self.session_factory) try: @@ -698,7 +698,7 @@ class BasicSwap(BaseApp): pidfilename += 'd' pidfilepath = os.path.join(self.getChainDatadirPath(coin), pidfilename + '.pid') - self.log.debug('Reading %s rpc credentials from auth cookie %s', coin, authcookiepath) + self.log.debug('Reading %s rpc credentials from auth cookie %s', Coins(coin).name, authcookiepath) # Wait for daemon to start # Test pids to ensure authcookie is read for the correct process datadir_pid = -1 @@ -1948,7 +1948,7 @@ class BasicSwap(BaseApp): return txid def cacheNewAddressForCoin(self, coin_type): - self.log.debug('cacheNewAddressForCoin %s', coin_type) + self.log.debug('cacheNewAddressForCoin %s', Coins(coin_type).name) key_str = 'receive_addr_' + self.ci(coin_type).coin_name().lower() addr = self.getReceiveAddressForCoin(coin_type) self.setStringKV(key_str, addr) @@ -2012,7 +2012,7 @@ class BasicSwap(BaseApp): raise ValueError('Wallet seed doesn\'t match expected.') def getCachedAddressForCoin(self, coin_type): - self.log.debug('getCachedAddressForCoin %s', coin_type) + self.log.debug('getCachedAddressForCoin %s', Coins(coin_type).name) # TODO: auto refresh after used ci = self.ci(coin_type) @@ -2032,7 +2032,7 @@ class BasicSwap(BaseApp): return addr def cacheNewStealthAddressForCoin(self, coin_type): - self.log.debug('cacheNewStealthAddressForCoin %s', coin_type) + self.log.debug('cacheNewStealthAddressForCoin %s', Coins(coin_type).name) if coin_type == Coins.LTC_MWEB: coin_type = Coins.LTC @@ -2043,7 +2043,7 @@ class BasicSwap(BaseApp): return addr def getCachedStealthAddressForCoin(self, coin_type): - self.log.debug('getCachedStealthAddressForCoin %s', coin_type) + self.log.debug('getCachedStealthAddressForCoin %s', Coins(coin_type).name) if coin_type == Coins.LTC_MWEB: coin_type = Coins.LTC @@ -2546,7 +2546,7 @@ class BasicSwap(BaseApp): lock_value = self.callcoinrpc(coin_from, 'getblockcount') + offer.lock_value else: lock_value = self.getTime() + offer.lock_value - self.log.debug('Initiate %s lock_value %d %d', coin_from, offer.lock_value, lock_value) + self.log.debug('Initiate %s lock_value %d %d', ci_from.coin_name(), offer.lock_value, lock_value) script = atomic_swap_1.buildContractScript(lock_value, secret_hash, bid.pkhash_buyer, pkhash_refund, OpCodes.OP_CHECKLOCKTIMEVERIFY) p2sh = self.callcoinrpc(Coins.PART, 'decodescript', [script.hex()])['p2sh'] @@ -3161,12 +3161,12 @@ class BasicSwap(BaseApp): cblock_hash = block_header_at['hash'] cblock_height = block_header_at['height'] - self.log.debug('Setting lock value from height of block %s %s', coin_to, cblock_hash) + self.log.debug('Setting lock value from height of block %s %s', Coins(coin_to).name, cblock_hash) contract_lock_value = cblock_height + lock_value else: - self.log.debug('Setting lock value from time of block %s %s', coin_from, initiate_tx_block_hash) + self.log.debug('Setting lock value from time of block %s %s', Coins(coin_from).name, initiate_tx_block_hash) contract_lock_value = initiate_tx_block_time + lock_value - self.log.debug('participate %s lock_value %d %d', coin_to, lock_value, contract_lock_value) + self.log.debug('participate %s lock_value %d %d', Coins(coin_to).name, lock_value, contract_lock_value) participate_script = atomic_swap_1.buildContractScript(contract_lock_value, secret_hash, pkhash_seller, pkhash_buyer_refund, OpCodes.OP_CHECKLOCKTIMEVERIFY) return participate_script @@ -3965,7 +3965,7 @@ class BasicSwap(BaseApp): bid.participate_tx.conf = found['depth'] index = found['index'] if bid.participate_tx is None or bid.participate_tx.txid is None: - self.log.debug('Found bid %s participate txn %s in chain %s', bid_id.hex(), found['txid'], coin_to) + self.log.debug('Found bid %s participate txn %s in chain %s', bid_id.hex(), found['txid'], Coins(coin_to).name) self.addParticipateTxn(bid_id, bid, coin_to, found['txid'], found['index'], found['height']) bid.setPTxState(TxStates.TX_SENT) save_bid = True @@ -4265,7 +4265,7 @@ class BasicSwap(BaseApp): def checkForSpends(self, coin_type, c): # assert (self.mxDB.locked()) - self.log.debug('checkForSpends %s', coin_type) + self.log.debug('checkForSpends %s', Coins(coin_type).name) # TODO: Check for spends on watchonly txns where possible @@ -7375,7 +7375,7 @@ class BasicSwap(BaseApp): return self._is_encrypted, self._is_locked def lookupRates(self, coin_from, coin_to, output_array=False): - self.log.debug('lookupRates {}, {}'.format(coin_from, coin_to)) + self.log.debug('lookupRates {}, {}'.format(coin_from, Coins(coin_to).name)) rate_sources = self.settings.get('rate_sources', {}) ci_from = self.ci(int(coin_from)) diff --git a/basicswap/interface/btc.py b/basicswap/interface/btc.py index dbade0b..b3debc5 100644 --- a/basicswap/interface/btc.py +++ b/basicswap/interface/btc.py @@ -1378,8 +1378,6 @@ class BTCInterface(CoinInterface): for u in unspent: if u.get('spendable', False) is False: continue - if u.get('solveable', False) is False: - continue if 'address' not in u: continue if 'desc' in u: @@ -1411,7 +1409,6 @@ class BTCInterface(CoinInterface): def getProofOfFunds(self, amount_for, extra_commit_bytes): # TODO: Lock unspent and use same output/s to fund bid unspent_addr = self.getUnspentsByAddr() - sign_for_addr = None for addr, value in unspent_addr.items(): if value >= amount_for: diff --git a/basicswap/interface/ltc.py b/basicswap/interface/ltc.py index da84e8b..ddcb4ed 100644 --- a/basicswap/interface/ltc.py +++ b/basicswap/interface/ltc.py @@ -51,6 +51,31 @@ class LTCInterface(BTCInterface): rv['mweb_immature'] = mweb_info['immature_balance'] return rv + def getUnspentsByAddr(self): + unspent_addr = dict() + unspent = self.rpc_wallet('listunspent') + for u in unspent: + if u.get('spendable', False) is False: + continue + if u.get('solvable', False) is False: # Filter out mweb outputs + continue + if 'address' not in u: + continue + if 'desc' in u: + desc = u['desc'] + if self.using_segwit: + if self.use_p2shp2wsh(): + if not desc.startswith('sh(wpkh'): + continue + else: + if not desc.startswith('wpkh'): + continue + else: + if not desc.startswith('pkh'): + continue + unspent_addr[u['address']] = unspent_addr.get(u['address'], 0) + self.make_int(u['amount'], r=1) + return unspent_addr + class LTCInterfaceMWEB(LTCInterface): @staticmethod diff --git a/basicswap/templates/offer_new_1.html b/basicswap/templates/offer_new_1.html index 4d5aa85..e97c33e 100644 --- a/basicswap/templates/offer_new_1.html +++ b/basicswap/templates/offer_new_1.html @@ -111,7 +111,7 @@ <option{% if data.addr_from==a[0] %} selected{% endif %} value="{{ a[0] }}">{{ a[0] }} {{ a[1] }}</option> {% endfor %} <option{% if data.addr_from=="-1" %} selected{% endif %} value="-1">New Address</option> - </select> + </select> </div> </div> </div> @@ -149,7 +149,7 @@ <div class="flex flex-wrap -m-3"> <div class="w-full md:w-1/2 p-3"> <p class="mb-1.5 font-medium text-base text-coolGray-800 dark:text-white">Select Coin You Send</p> - <div class="custom-select"> + <div class="custom-select"> <div class="relative"> {{ input_down_arrow_offer_svg | safe }} <select class="select hover:border-blue-500 pl-10 bg-gray-50 text-gray-900 appearance-none pr-10 dark:bg-gray-500 dark:text-white border border-gray-300 dark:border-gray-400 dark:text-gray-50 dark:placeholder-gray-400 text-sm rounded-lg outline-none focus:ring-blue-500 focus:border-blue-500 block w-full p-5 bold focus:ring-0" id="coin_from" name="coin_from" onchange="set_rate('coin_from');"> @@ -182,7 +182,7 @@ <div class="w-full md:flex-1 p-3"> <div class="flex flex-wrap -m-3"> <div class="w-full md:w-1/2 p-3"> - <p class="mb-1.5 font-medium text-base text-coolGray-800 dark:text-white">Select Coin You Get</p> + <p class="mb-1.5 font-medium text-base text-coolGray-800 dark:text-white">Select Coin You Get</p> <div class="custom-select"> <div class="relative"> {{ input_down_arrow_offer_svg | safe }} @@ -343,22 +343,22 @@ } } } - + function lookup_rates() { const coin_from = document.getElementById('coin_from').value; const coin_to = document.getElementById('coin_to').value; - + if (coin_from === '-1' || coin_to === '-1') { alert('Coins from and to must be set first.'); return; } - + const selectedCoin = (coin_from === '15') ? '3' : coin_from; - + inner_html = '<p>Updating...</p>'; document.getElementById('rates_display').innerHTML = inner_html; document.querySelector(".pricejsonhidden").classList.remove("hidden"); - + const xhr_rates = new XMLHttpRequest(); xhr_rates.onreadystatechange = function() { if (xhr_rates.readyState === XMLHttpRequest.DONE) { @@ -369,19 +369,19 @@ } } }; - + xhr_rates.open('POST', '/json/rates'); xhr_rates.setRequestHeader('Content-type', 'application/x-www-form-urlencoded'); xhr_rates.send('coin_from=' + selectedCoin + '&coin_to=' + coin_to); } - + function getRateInferred(event) { event.preventDefault(); // Prevent default form submission behavior - + const coin_from = document.getElementById('coin_from').value; const coin_to = document.getElementById('coin_to').value; const params = 'coin_from=' + encodeURIComponent(coin_from) + '&coin_to=' + encodeURIComponent(coin_to); - + const xhr_rates = new XMLHttpRequest(); xhr_rates.onreadystatechange = function() { if (xhr_rates.readyState === XMLHttpRequest.DONE) { @@ -400,14 +400,14 @@ } } }; - + xhr_rates.open('POST', '/json/rates'); xhr_rates.setRequestHeader('Content-type', 'application/x-www-form-urlencoded'); xhr_rates.send(params); } - + document.getElementById('get_rate_inferred_button').addEventListener('click', getRateInferred); - + function set_swap_type_enabled(coin_from, coin_to, swap_type) { const adaptor_sig_only_coins = ['6' /* XMR */, '8' /* PART_ANON */, '7' /* PART_BLIND */, '13' /* FIRO */]; const secret_hash_only_coins = ['11' /* PIVX */, '12' /* DASH */]; @@ -442,7 +442,7 @@ swap_type_hidden.parentNode.removeChild(swap_type_hidden); } } - + function set_rate(value_changed) { const coin_from = document.getElementById('coin_from').value; const coin_to = document.getElementById('coin_to').value; @@ -450,19 +450,26 @@ const amt_to = document.getElementById('amt_to').value; const rate = document.getElementById('rate').value; const lock_rate = rate == '' ? false : document.getElementById('rate_lock').checked; - + const swap_type = document.getElementById('swap_type'); set_swap_type_enabled(coin_from, coin_to, swap_type); - + if (coin_from == '-1' || coin_to == '-1') { return; } params = 'coin_from=' + coin_from + '&coin_to=' + coin_to; if (value_changed == 'rate' || (lock_rate && value_changed == 'amt_from') || (amt_to == '' && value_changed == 'amt_from')) { - if (amt_from == '' || rate == '') { + if (rate == '' || (amt_from == '' && amt_to == '')) { return; + } else + if (amt_from == '' && amt_to != '') { + if (value_changed == 'amt_from') { // Don't try and set a value just cleared + return; + } + params += '&rate=' + rate + '&amt_to=' + amt_to; + } else { + params += '&rate=' + rate + '&amt_from=' + amt_from; } - params += '&rate=' + rate + '&amt_from=' + amt_from; } else if (lock_rate && value_changed == 'amt_to') { if (amt_to == '' || rate == '') { @@ -479,7 +486,7 @@ xhr_rate.setRequestHeader('Content-type', 'application/x-www-form-urlencoded'); xhr_rate.send(params); } - + document.addEventListener("DOMContentLoaded", function() { const coin_from = document.getElementById('coin_from').value; const coin_to = document.getElementById('coin_to').value; @@ -492,4 +499,4 @@ {% include 'footer.html' %} </div> </body> -</html> \ No newline at end of file +</html> diff --git a/doc/release-notes.md b/doc/release-notes.md index 6d5cf01..f91a757 100644 --- a/doc/release-notes.md +++ b/doc/release-notes.md @@ -11,6 +11,7 @@ - All for all known coins. - Comma separated list of coin tickers to show. - basicswap-run will rewrite litecoin.conf file to add config required to run Litecoin 0.21.3 in pruned mode. +- On new offers page a blank amount-from is auto-filled from amount-to and rate. 0.12.7 diff --git a/tests/basicswap/selenium/test_offer.py b/tests/basicswap/selenium/test_offer.py index a1bb066..9dd39d9 100644 --- a/tests/basicswap/selenium/test_offer.py +++ b/tests/basicswap/selenium/test_offer.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- -# Copyright (c) 2023 tecnovert +# Copyright (c) 2023-2024 tecnovert # Distributed under the MIT software license, see the accompanying # file LICENSE or http://www.opensource.org/licenses/mit-license.php. @@ -29,8 +29,39 @@ def test_offer(driver): select = Select(driver.find_element(By.ID, 'coin_to')) select.select_by_visible_text('Monero') - driver.find_element(By.NAME, 'amt_from').send_keys('1') - driver.find_element(By.NAME, 'amt_to').send_keys('2') + amt_from = driver.find_element(By.NAME, 'amt_from') + amt_to = driver.find_element(By.NAME, 'amt_to') + rate = driver.find_element(By.ID, 'rate') + amt_from.send_keys('1') + amt_to.send_keys('2') + amt_from.click() + time.sleep(0.5) + rate_value = rate.get_attribute('value') + assert (float(rate_value) == 2.0) + + rate.clear() + rate.send_keys('3') + amt_from.click() + time.sleep(0.5) + amt_to_value = amt_to.get_attribute('value') + assert (float(amt_to_value) == 3.0) + + amt_from.clear() + amt_from.send_keys('2') + amt_to.click() + time.sleep(0.5) + amt_to_value = amt_to.get_attribute('value') + assert (float(amt_to_value) == 6.0) + + amt_from.clear() + amt_to.clear() + rate.clear() + amt_to.send_keys('2') + rate.send_keys('2') + amt_to.click() + time.sleep(0.2) + amt_from_value = amt_from.get_attribute('value') + assert (float(amt_from_value) == 1.0) driver.find_element(By.NAME, 'continue').click() WebDriverWait(driver, 20).until(EC.element_to_be_clickable((By.NAME, 'check_offer'))).click() diff --git a/tests/basicswap/test_ltc_xmr.py b/tests/basicswap/test_ltc_xmr.py index 2007760..d78ee88 100644 --- a/tests/basicswap/test_ltc_xmr.py +++ b/tests/basicswap/test_ltc_xmr.py @@ -182,6 +182,7 @@ class TestLTC(BasicSwapTest): require_amount: int = ci1.make_int(1) unspent_addr = ci1.getUnspentsByAddr() + assert (len(unspent_addr) > 0) for addr, _ in unspent_addr.items(): if 'mweb1' in addr: raise ValueError('getUnspentsByAddr should exclude mweb UTXOs.')