From 13623ed9e565f3dc4e2c5fd5db26c461ffd53080 Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Thu, 8 Oct 2026 13:12:03 +0000 Subject: [PATCH] mmgen-swaptxcreate: add `--quote` option MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Display a swap quote and exit without creating a transaction. All data is from the remote endpoint — no running daemons are required. --- mmgen/data/version | 2 +- mmgen/main_txcreate.py | 1 + mmgen/swap/proto/thorchain/thornode.py | 12 ++++++------ mmgen/tx/new_swap.py | 19 ++++++++++++++----- test/cmdtest_d/swap.py | 10 ++++++++++ 5 files changed, 32 insertions(+), 12 deletions(-) diff --git a/mmgen/data/version b/mmgen/data/version index 1b1edcf4..e124d748 100644 --- a/mmgen/data/version +++ b/mmgen/data/version @@ -1 +1 @@ -16.3.0dev14 +16.3.0dev15 diff --git a/mmgen/main_txcreate.py b/mmgen/main_txcreate.py index 8565ec49..05f43e7c 100644 --- a/mmgen/main_txcreate.py +++ b/mmgen/main_txcreate.py @@ -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) diff --git a/mmgen/swap/proto/thorchain/thornode.py b/mmgen/swap/proto/thorchain/thornode.py index 8c985bb7..065a7cd3 100644 --- a/mmgen/swap/proto/thorchain/thornode.py +++ b/mmgen/swap/proto/thorchain/thornode.py @@ -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)} diff --git a/mmgen/tx/new_swap.py b/mmgen/tx/new_swap.py index 9ca1bb47..117a482d 100644 --- a/mmgen/tx/new_swap.py +++ b/mmgen/tx/new_swap.py @@ -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 diff --git a/test/cmdtest_d/swap.py b/test/cmdtest_d/swap.py index f1893788..3f24ef41 100644 --- a/test/cmdtest_d/swap.py +++ b/test/cmdtest_d/swap.py @@ -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')