mmgen-swaptxcreate: add --quote option

Display a swap quote and exit without creating a transaction.

All data is from the remote endpoint — no running daemons are required.
This commit is contained in:
The MMGen Project 2026-10-08 13:12:03 +00:00
commit 13623ed9e5
Signed by: mmgen
GPG key ID: 3F8B1861E32B7DA2
5 changed files with 32 additions and 12 deletions

View file

@ -1 +1 @@
16.3.0dev14
16.3.0dev15

View file

@ -78,6 +78,7 @@ opts_data = {
+ Valid parameters: {tp}.
+ If option is omitted, the default priority will be used
-- -q, --quiet Suppress warnings; overwrite files without prompting
-s -Q, --quote Get a swap quote and exit. Do not create transaction
-s -r, --stream-interval=N Set block interval for streaming swap (default: {si})
bt -R, --no-rbf Make transaction non-replaceable (non-replace-by-fee
+ according to BIP 125)

View file

@ -51,7 +51,7 @@ class Thornode:
else:
self.data = self.rpc.get_quote(self.tx.send_asset, self.tx.recv_asset, self.in_amt)
async def format_quote(self, *, deduct_est_fee=False):
async def format_quote(self, *, deduct_est_fee=False, skip_est_fee=False):
from ....util import make_timestr, ymsg
from ....util2 import format_elapsed_hr
from ....color import blue, green, cyan, pink, orange, redbg, yelbg, grnbg
@ -116,11 +116,11 @@ class Thornode:
Vault address: {}""".format(cyan(self.inbound_address))
fee_info = '' if not tx.proto.has_usr_fee else """
Recommended fee: {} {}
Network-estimated fee: {} (from node)""".format(
pink(d['recommended_gas_rate']),
pink(_.disp if (_ := gas_unit_data.get(gas_unit)) else gas_unit),
await self.tx.network_fee_disp())
Recommended fee: {} {}""".format(
pink(d['recommended_gas_rate']),
pink(_.disp if (_ := gas_unit_data.get(gas_unit)) else gas_unit)) + (
'' if skip_est_fee else """
Network-estimated fee: {} (from node)""".format(await self.tx.network_fee_disp()))
return f"""
{cyan(hdr)}

View file

@ -10,11 +10,11 @@
tx.new_swap: new swap transaction class
"""
import time
import sys, time
from collections import namedtuple
from ..amt import UniAmt
from ..util import msg, ymsg
from ..util import msg, ymsg, die
from ..swap.util import get_swap_asset, init_swap_proto
from .new import New
@ -101,7 +101,8 @@ class NewSwap(New):
self.proto.is_vm or
arg in sa.recv): # is change arg
nonlocal chg_output
chg_output = await self.get_chg_output(arg, addrfiles)
if not self.cfg.quote:
chg_output = await self.get_chg_output(arg, addrfiles)
arg = get_arg()
# arg 4: recv_coin
@ -125,6 +126,14 @@ class NewSwap(New):
self.swap_cfg = self.swap_proto_mod.SwapCfg(self.cfg)
if self.cfg.quote:
if not args.send_amt:
die(1, 'With --quote, you must supply a send amount')
await self.display_quote(
self.swap_proto_mod.rpc_client(self, args.send_amt),
skip_est_fee = True)
sys.exit(0)
self.check_swap_network()
for asset in (self.send_asset, self.recv_asset):
@ -170,13 +179,13 @@ class NewSwap(New):
return tuple(ret + [f'data:{memo}'])
async def display_quote(self, qd, *, deduct_est_fee=False):
async def display_quote(self, qd, *, deduct_est_fee=False, skip_est_fee=False):
self.cfg._util.qmsg(f'Retrieving data from {qd.rpc.swap_api.host}...')
qd.get_quote(self.swap_cfg)
self.cfg._util.qmsg('OK')
self.swap_quote_refresh_time = time.time()
await self.set_gas(to_addr=qd.router if self.is_token else None)
msg(await qd.format_quote(deduct_est_fee=deduct_est_fee))
msg(await qd.format_quote(deduct_est_fee=deduct_est_fee, skip_est_fee=skip_est_fee))
def update_vault_addr(self, qd, *, addr='inbound_address'):
vault_idx = self.vault_idx

View file

@ -333,6 +333,7 @@ class CmdTestSwap(
('swaptxcreate5', 'creating a swap transaction (chg and dest by addrlist ID)'),
('swaptxcreate6', 'creating a swap transaction (dest is non-wallet addr)'),
('swaptxcreate7', 'creating a swap transaction (coin-amt-coin)'),
('swaptxcreate8', 'creating a swap transaction (quote only)'),
),
'create_bad': (
'Swap TX create operations: error handling',
@ -345,6 +346,7 @@ class CmdTestSwap(
('swaptxcreate_bad7', 'creating a swap transaction (bad, bad user input, user exit)'),
('swaptxcreate_bad8', 'creating a swap transaction (bad, non-MMGen change address)'),
('swaptxcreate_bad9', 'creating a swap transaction (bad, invalid addrtype)'),
('swaptxcreate_bad10', 'creating a swap transaction (bad, --quote with no amount)'),
),
'create_neterror': (
'Swap TX create operations: network error handling',
@ -532,6 +534,11 @@ class CmdTestSwap(
t.expect('OK? (Y/n): ', 'y')
return self._swaptxcreate_ui_common(t, expect=':0/3/0')
def swaptxcreate8(self):
t = self._swaptxcreate(['BCH', '0.56789', 'LTC'], add_opts=['--quote', '--no-quiet'])
t.expect('SWAP QUOTE')
return t
def swaptxcreate_bad1(self):
t = self._swaptxcreate_bad(
['BCH', '1.234', f'{self.sid}:C:3', 'LTC', f'{self.sid}:S:1'],
@ -576,6 +583,9 @@ class CmdTestSwap(
def swaptxcreate_bad9(self):
return self._swaptxcreate_bad(['BCH', '1.234', 'S', 'LTC', 'B'], exit_val=2, expect1='invalid command-')
def swaptxcreate_bad10(self):
return self._swaptxcreate_bad(['BCH', 'LTC'], add_opts=['--quote'], exit_val=1, expect1='send amount')
def swaptxcreate_neterror1(self):
return self._swaptxcreate_neterror('ltc_global_paused', 'Trading is paused on the')