Browse Source

swaptxcreate, swaptxdo: create entry points, NewSwap tx class

The MMGen Project 3 weeks ago
parent
commit
e4b6d0536c

+ 16 - 0
cmds/mmgen-swaptxcreate

@@ -0,0 +1,16 @@
+#!/usr/bin/env python3
+#
+# MMGen Wallet, a terminal-based cryptocurrency wallet
+# Copyright (C)2013-2025 The MMGen Project <mmgen@tuta.io>
+# Licensed under the GNU General Public License, Version 3:
+#   https://www.gnu.org/licenses
+# Public project repositories:
+#   https://github.com/mmgen/mmgen-wallet
+#   https://gitlab.com/mmgen/mmgen-wallet
+
+"""
+mmgen-swaptxcreate: Create an unsigned DEX swap transaction with MMGen or non-MMGen inputs
+"""
+
+from mmgen.main import launch
+launch(mod='txcreate')

+ 16 - 0
cmds/mmgen-swaptxdo

@@ -0,0 +1,16 @@
+#!/usr/bin/env python3
+#
+# MMGen Wallet, a terminal-based cryptocurrency wallet
+# Copyright (C)2013-2025 The MMGen Project <mmgen@tuta.io>
+# Licensed under the GNU General Public License, Version 3:
+#   https://www.gnu.org/licenses
+# Public project repositories:
+#   https://github.com/mmgen/mmgen-wallet
+#   https://gitlab.com/mmgen/mmgen-wallet
+
+"""
+mmgen-swaptxdo: Create, sign and broadcast a DEX swap transaction
+"""
+
+from mmgen.main import launch
+launch(mod='txdo')

+ 1 - 0
mmgen/cfg.py

@@ -376,6 +376,7 @@ class Config(Lockable):
 	_autoset_opts = {
 	_autoset_opts = {
 		'fee_estimate_mode': _ov('nocase_pfx', ['conservative', 'economical']),
 		'fee_estimate_mode': _ov('nocase_pfx', ['conservative', 'economical']),
 		'rpc_backend':       _ov('nocase_pfx', ['auto', 'httplib', 'curl', 'aiohttp', 'requests']),
 		'rpc_backend':       _ov('nocase_pfx', ['auto', 'httplib', 'curl', 'aiohttp', 'requests']),
+		'swap_proto':        _ov('nocase_pfx', ['thorchain']),
 	}
 	}
 
 
 	_auto_typeset_opts = {
 	_auto_typeset_opts = {

+ 2 - 2
mmgen/help/__init__.py

@@ -46,10 +46,10 @@ def show_hash_presets(cfg):
 
 
 def gen_arg_tuple(cfg, func, text):
 def gen_arg_tuple(cfg, func, text):
 
 
-	def help_notes(k):
+	def help_notes(k, *args, **kwargs):
 		import importlib
 		import importlib
 		return getattr(importlib.import_module(
 		return getattr(importlib.import_module(
-			f'{cfg._opts.help_pkg}.help_notes').help_notes(proto, cfg), k)()
+			f'{cfg._opts.help_pkg}.help_notes').help_notes(proto, cfg), k)(*args, **kwargs)
 
 
 	def help_mod(modname):
 	def help_mod(modname):
 		import importlib
 		import importlib

+ 3 - 1
mmgen/help/help_notes.py

@@ -20,8 +20,10 @@ class help_notes:
 		self.proto = proto
 		self.proto = proto
 		self.cfg = cfg
 		self.cfg = cfg
 
 
-	def txcreate_args(self):
+	def txcreate_args(self, target):
 		return (
 		return (
+			'COIN1 [AMT CHG_ADDR] COIN2 [ADDR]'
+				if target == 'swaptx' else
 			'[ADDR,AMT ... | DATA_SPEC] ADDR <change addr, addrlist ID or addr type>'
 			'[ADDR,AMT ... | DATA_SPEC] ADDR <change addr, addrlist ID or addr type>'
 				if self.proto.base_proto == 'Bitcoin' else
 				if self.proto.base_proto == 'Bitcoin' else
 			'ADDR,AMT')
 			'ADDR,AMT')

+ 19 - 7
mmgen/main_txcreate.py

@@ -24,11 +24,19 @@ mmgen-txcreate: Create a cryptocoin transaction with MMGen- and/or non-MMGen
 from .cfg import gc, Config
 from .cfg import gc, Config
 from .util import fmt_list, async_run
 from .util import fmt_list, async_run
 
 
+target = gc.prog_name.split('-')[1].removesuffix('create')
+
 opts_data = {
 opts_data = {
-	'filter_codes': ['-'],
+	'filter_codes': {
+		'tx':     ['-', 't'],
+		'swaptx': ['-', 's'],
+	}[target],
 	'sets': [('yes', True, 'quiet', True)],
 	'sets': [('yes', True, 'quiet', True)],
 	'text': {
 	'text': {
-		'desc': f'Create a transaction with outputs to specified coin or {gc.proj_name} addresses',
+		'desc': {
+			'tx':     f'Create a transaction with outputs to specified coin or {gc.proj_name} addresses',
+			'swaptx': f'Create a DEX swap transaction with {gc.proj_name} inputs and outputs',
+		}[target],
 		'usage':   '[opts] {u_args} [addr file ...]',
 		'usage':   '[opts] {u_args} [addr file ...]',
 		'options': """
 		'options': """
 			-- -h, --help            Print this help message
 			-- -h, --help            Print this help message
@@ -54,15 +62,17 @@ opts_data = {
 			-- -I, --inputs=      i  Specify transaction inputs (comma-separated list of
 			-- -I, --inputs=      i  Specify transaction inputs (comma-separated list of
 			+                        MMGen IDs or coin addresses).  Note that ALL unspent
 			+                        MMGen IDs or coin addresses).  Note that ALL unspent
 			+                        outputs associated with each address will be included.
 			+                        outputs associated with each address will be included.
-			b- -l, --locktime=    t  Lock time (block height or unix seconds) (default: 0)
+			bt -l, --locktime=    t  Lock time (block height or unix seconds) (default: 0)
 			b- -L, --autochg-ignore-labels Ignore labels when autoselecting change addresses
 			b- -L, --autochg-ignore-labels Ignore labels when autoselecting change addresses
 			-- -m, --minconf=     n  Minimum number of confirmations required to spend
 			-- -m, --minconf=     n  Minimum number of confirmations required to spend
 			+                        outputs (default: 1)
 			+                        outputs (default: 1)
 			-- -q, --quiet           Suppress warnings; overwrite files without prompting
 			-- -q, --quiet           Suppress warnings; overwrite files without prompting
-			b- -R, --no-rbf          Make transaction non-replaceable (non-replace-by-fee
+			bt -R, --no-rbf          Make transaction non-replaceable (non-replace-by-fee
 			+                        according to BIP 125)
 			+                        according to BIP 125)
 			-- -v, --verbose         Produce more verbose output
 			-- -v, --verbose         Produce more verbose output
 			b- -V, --vsize-adj=   f  Adjust transaction's estimated vsize by factor 'f'
 			b- -V, --vsize-adj=   f  Adjust transaction's estimated vsize by factor 'f'
+			-s -x, --swap-proto      Swap protocol to use (Default: {x_dfl},
+			+                        Choices: {x_all})
 			-- -y, --yes             Answer 'yes' to prompts, suppress non-essential output
 			-- -y, --yes             Answer 'yes' to prompts, suppress non-essential output
 			e- -X, --cached-balances Use cached balances
 			e- -X, --cached-balances Use cached balances
 		""",
 		""",
@@ -70,7 +80,7 @@ opts_data = {
 	},
 	},
 	'code': {
 	'code': {
 		'usage': lambda cfg, proto, help_notes, s: s.format(
 		'usage': lambda cfg, proto, help_notes, s: s.format(
-			u_args = help_notes('txcreate_args')),
+			u_args = help_notes('txcreate_args', target)),
 		'options': lambda cfg, proto, help_notes, s: s.format(
 		'options': lambda cfg, proto, help_notes, s: s.format(
 			cfg    = cfg,
 			cfg    = cfg,
 			cu     = proto.coin,
 			cu     = proto.coin,
@@ -78,7 +88,9 @@ opts_data = {
 			fu     = help_notes('rel_fee_desc'),
 			fu     = help_notes('rel_fee_desc'),
 			fl     = help_notes('fee_spec_letters'),
 			fl     = help_notes('fee_spec_letters'),
 			fe_all = fmt_list(cfg._autoset_opts['fee_estimate_mode'].choices, fmt='no_spc'),
 			fe_all = fmt_list(cfg._autoset_opts['fee_estimate_mode'].choices, fmt='no_spc'),
-			fe_dfl = cfg._autoset_opts['fee_estimate_mode'].choices[0]),
+			fe_dfl = cfg._autoset_opts['fee_estimate_mode'].choices[0],
+			x_all = fmt_list(cfg._autoset_opts['swap_proto'].choices, fmt='no_spc'),
+			x_dfl = cfg._autoset_opts['swap_proto'].choices[0]),
 		'notes': lambda cfg, help_notes, s: s.format(
 		'notes': lambda cfg, help_notes, s: s.format(
 			c      = help_notes('txcreate'),
 			c      = help_notes('txcreate'),
 			F      = help_notes('fee'),
 			F      = help_notes('fee'),
@@ -98,7 +110,7 @@ async def main():
 		Signable.automount_transaction(asi).check_create_ok()
 		Signable.automount_transaction(asi).check_create_ok()
 
 
 	from .tx import NewTX
 	from .tx import NewTX
-	tx1 = await NewTX(cfg=cfg, proto=cfg._proto)
+	tx1 = await NewTX(cfg=cfg, proto=cfg._proto, target=target)
 
 
 	from .rpc import rpc_init
 	from .rpc import rpc_init
 	tx1.rpc = await rpc_init(cfg)
 	tx1.rpc = await rpc_init(cfg)

+ 19 - 7
mmgen/main_txdo.py

@@ -24,11 +24,19 @@ from .cfg import gc, Config
 from .util import die, fmt_list, async_run
 from .util import die, fmt_list, async_run
 from .subseed import SubSeedIdxRange
 from .subseed import SubSeedIdxRange
 
 
+target = gc.prog_name.split('-')[1].removesuffix('do')
+
 opts_data = {
 opts_data = {
-	'filter_codes': ['-'],
+	'filter_codes': {
+		'tx':     ['-', 't'],
+		'swaptx': ['-', 's'],
+	}[target],
 	'sets': [('yes', True, 'quiet', True)],
 	'sets': [('yes', True, 'quiet', True)],
 	'text': {
 	'text': {
-		'desc': f'Create, sign and send an {gc.proj_name} transaction',
+		'desc': {
+			'tx':     f'Create, sign and send an {gc.proj_name} transaction',
+			'swaptx': f'Create, sign and send a DEX swap transaction with {gc.proj_name} inputs and outputs',
+		}[target],
 		'usage':   '[opts] {u_args} [addr file ...] [seed source ...]',
 		'usage':   '[opts] {u_args} [addr file ...] [seed source ...]',
 		'options': """
 		'options': """
 			-- -h, --help             Print this help message
 			-- -h, --help             Print this help message
@@ -62,7 +70,7 @@ opts_data = {
 			-- -k, --keys-from-file=f Provide additional keys for non-{pnm} addresses
 			-- -k, --keys-from-file=f Provide additional keys for non-{pnm} addresses
 			-- -K, --keygen-backend=n Use backend 'n' for public key generation.  Options
 			-- -K, --keygen-backend=n Use backend 'n' for public key generation.  Options
 			+                         for {coin_id}: {kgs}
 			+                         for {coin_id}: {kgs}
-			b- -l, --locktime=      t Lock time (block height or unix seconds) (default: 0)
+			bt -l, --locktime=      t Lock time (block height or unix seconds) (default: 0)
 			b- -L, --autochg-ignore-labels Ignore labels when autoselecting change addresses
 			b- -L, --autochg-ignore-labels Ignore labels when autoselecting change addresses
 			-- -m, --minconf=n        Minimum number of confirmations required to spend
 			-- -m, --minconf=n        Minimum number of confirmations required to spend
 			+                         outputs (default: 1)
 			+                         outputs (default: 1)
@@ -75,7 +83,7 @@ opts_data = {
 			-- -p, --hash-preset=   p Use the scrypt hash parameters defined by preset 'p'
 			-- -p, --hash-preset=   p Use the scrypt hash parameters defined by preset 'p'
 			+                         for password hashing (default: '{gc.dfl_hash_preset}')
 			+                         for password hashing (default: '{gc.dfl_hash_preset}')
 			-- -P, --passwd-file=   f Get {pnm} wallet passphrase from file 'f'
 			-- -P, --passwd-file=   f Get {pnm} wallet passphrase from file 'f'
-			b- -R, --no-rbf           Make transaction non-replaceable (non-replace-by-fee
+			bt -R, --no-rbf           Make transaction non-replaceable (non-replace-by-fee
 			+                         according to BIP 125)
 			+                         according to BIP 125)
 			-- -q, --quiet            Suppress warnings; overwrite files without prompting
 			-- -q, --quiet            Suppress warnings; overwrite files without prompting
 			-- -u, --subseeds=      n The number of subseed pairs to scan for (default: {ss},
 			-- -u, --subseeds=      n The number of subseed pairs to scan for (default: {ss},
@@ -83,6 +91,8 @@ opts_data = {
 			+                         wallet is scanned for subseeds.
 			+                         wallet is scanned for subseeds.
 			-- -v, --verbose          Produce more verbose output
 			-- -v, --verbose          Produce more verbose output
 			b- -V, --vsize-adj=     f Adjust transaction's estimated vsize by factor 'f'
 			b- -V, --vsize-adj=     f Adjust transaction's estimated vsize by factor 'f'
+			-s -x, --swap-proto       Swap protocol to use (Default: {x_dfl},
+			+                         Choices: {x_all})
 			e- -X, --cached-balances  Use cached balances
 			e- -X, --cached-balances  Use cached balances
 			-- -y, --yes              Answer 'yes' to prompts, suppress non-essential output
 			-- -y, --yes              Answer 'yes' to prompts, suppress non-essential output
 			-- -z, --show-hash-presets Show information on available hash presets
 			-- -z, --show-hash-presets Show information on available hash presets
@@ -103,7 +113,7 @@ column below:
 	},
 	},
 	'code': {
 	'code': {
 		'usage': lambda cfg, proto, help_notes, s: s.format(
 		'usage': lambda cfg, proto, help_notes, s: s.format(
-			u_args  = help_notes('txcreate_args')),
+			u_args  = help_notes('txcreate_args', target)),
 		'options': lambda cfg, proto, help_notes, s: s.format(
 		'options': lambda cfg, proto, help_notes, s: s.format(
 			gc      = gc,
 			gc      = gc,
 			cfg     = cfg,
 			cfg     = cfg,
@@ -119,7 +129,9 @@ column below:
 			ss      = help_notes('dfl_subseeds'),
 			ss      = help_notes('dfl_subseeds'),
 			ss_max  = SubSeedIdxRange.max_idx,
 			ss_max  = SubSeedIdxRange.max_idx,
 			fe_all  = fmt_list(cfg._autoset_opts['fee_estimate_mode'].choices, fmt='no_spc'),
 			fe_all  = fmt_list(cfg._autoset_opts['fee_estimate_mode'].choices, fmt='no_spc'),
-			fe_dfl  = cfg._autoset_opts['fee_estimate_mode'].choices[0]),
+			fe_dfl  = cfg._autoset_opts['fee_estimate_mode'].choices[0],
+			x_all   = fmt_list(cfg._autoset_opts['swap_proto'].choices, fmt='no_spc'),
+			x_dfl   = cfg._autoset_opts['swap_proto'].choices[0]),
 		'notes': lambda cfg, help_notes, s: s.format(
 		'notes': lambda cfg, help_notes, s: s.format(
 			c       = help_notes('txcreate'),
 			c       = help_notes('txcreate'),
 			F       = help_notes('fee'),
 			F       = help_notes('fee'),
@@ -139,7 +151,7 @@ seed_files = get_seed_files(cfg, cfg._args)
 
 
 async def main():
 async def main():
 
 
-	tx1 = await NewTX(cfg=cfg, proto=cfg._proto)
+	tx1 = await NewTX(cfg=cfg, proto=cfg._proto, target=target)
 
 
 	from .rpc import rpc_init
 	from .rpc import rpc_init
 	tx1.rpc = await rpc_init(cfg)
 	tx1.rpc = await rpc_init(cfg)

+ 27 - 0
mmgen/proto/btc/tx/new_swap.py

@@ -0,0 +1,27 @@
+#!/usr/bin/env python3
+#
+# MMGen Wallet, a terminal-based cryptocurrency wallet
+# Copyright (C)2013-2024 The MMGen Project <mmgen@tuta.io>
+# Licensed under the GNU General Public License, Version 3:
+#   https://www.gnu.org/licenses
+# Public project repositories:
+#   https://github.com/mmgen/mmgen-wallet
+#   https://gitlab.com/mmgen/mmgen-wallet
+
+"""
+proto.btc.tx.new_swap: Bitcoin new swap transaction class
+"""
+
+from ....tx.new_swap import NewSwap as TxNewSwap
+from .new import New
+
+class NewSwap(New, TxNewSwap):
+	desc = 'Bitcoin swap transaction'
+
+	async def process_swap_cmd_args(self, cmd_args):
+		import sys
+		from ....util import msg
+		msg(' '.join(cmd_args))
+		sys.exit(0)
+		raise NotImplementedError('Work in Progress!')
+		return cmd_args

+ 4 - 0
mmgen/tx/__init__.py

@@ -45,6 +45,9 @@ def _get_cls_info(clsname, modname, kwargs):
 			die(1, f'{ext!r}: unrecognized file extension for CompletedTX')
 			die(1, f'{ext!r}: unrecognized file extension for CompletedTX')
 		clsname = cls.__name__
 		clsname = cls.__name__
 		modname = cls.__module__.rsplit('.', maxsplit=1)[-1]
 		modname = cls.__module__.rsplit('.', maxsplit=1)[-1]
+	elif clsname == 'New' and kwargs['target'] == 'swaptx':
+		clsname = 'NewSwap'
+		modname = 'new_swap'
 
 
 	kwargs['proto'] = proto
 	kwargs['proto'] = proto
 
 
@@ -94,6 +97,7 @@ BaseTX         = _get('Base',     'base')
 UnsignedTX     = _get('Unsigned', 'unsigned')
 UnsignedTX     = _get('Unsigned', 'unsigned')
 
 
 NewTX          = _get_async('New',          'new')
 NewTX          = _get_async('New',          'new')
+NewSwapTX      = _get_async('NewSwap',      'new_swap')
 CompletedTX    = _get_async('Completed',    'completed')
 CompletedTX    = _get_async('Completed',    'completed')
 SignedTX       = _get_async('Signed',       'signed')
 SignedTX       = _get_async('Signed',       'signed')
 OnlineSignedTX = _get_async('OnlineSigned', 'online')
 OnlineSignedTX = _get_async('OnlineSigned', 'online')

+ 6 - 0
mmgen/tx/new.py

@@ -82,6 +82,10 @@ class New(Base):
 	chg_autoselected = False
 	chg_autoselected = False
 	_funds_available = namedtuple('funds_available', ['is_positive', 'amt'])
 	_funds_available = namedtuple('funds_available', ['is_positive', 'amt'])
 
 
+	def __init__(self, *args, target=None, **kwargs):
+		self.target = target
+		super().__init__(*args, **kwargs)
+
 	def warn_insufficient_funds(self, amt, coin):
 	def warn_insufficient_funds(self, amt, coin):
 		msg(self.msg_insufficient_funds.format(amt.hl(), coin))
 		msg(self.msg_insufficient_funds.format(amt.hl(), coin))
 
 
@@ -412,6 +416,8 @@ class New(Base):
 			ad_f, cmd_args = self.get_addrdata_from_files(cmd_args) # pops from end of cmd_args
 			ad_f, cmd_args = self.get_addrdata_from_files(cmd_args) # pops from end of cmd_args
 			from ..addrdata import TwAddrData
 			from ..addrdata import TwAddrData
 			ad_w = await TwAddrData(self.cfg, self.proto, twctl=self.twctl)
 			ad_w = await TwAddrData(self.cfg, self.proto, twctl=self.twctl)
+			if self.target == 'swaptx':
+				cmd_args = await self.process_swap_cmd_args(cmd_args)
 			await self.process_cmd_args(cmd_args, ad_f, ad_w)
 			await self.process_cmd_args(cmd_args, ad_f, ad_w)
 
 
 		from ..ui import do_license_msg
 		from ..ui import do_license_msg

+ 22 - 0
mmgen/tx/new_swap.py

@@ -0,0 +1,22 @@
+#!/usr/bin/env python3
+#
+# MMGen Wallet, a terminal-based cryptocurrency wallet
+# Copyright (C)2013-2024 The MMGen Project <mmgen@tuta.io>
+# Licensed under the GNU General Public License, Version 3:
+#   https://www.gnu.org/licenses
+# Public project repositories:
+#   https://github.com/mmgen/mmgen-wallet
+#   https://gitlab.com/mmgen/mmgen-wallet
+
+"""
+tx.new_swap: new swap transaction class
+"""
+
+from .new import New
+
+class NewSwap(New):
+	desc = 'swap transaction'
+
+	async def process_swap_cmd_args(self, cmd_args):
+		raise NotImplementedError('Work in Progress!')
+		return cmd_args

+ 2 - 0
setup.cfg

@@ -107,6 +107,8 @@ scripts =
 	cmds/mmgen-seedjoin
 	cmds/mmgen-seedjoin
 	cmds/mmgen-seedsplit
 	cmds/mmgen-seedsplit
 	cmds/mmgen-subwalletgen
 	cmds/mmgen-subwalletgen
+	cmds/mmgen-swaptxcreate
+	cmds/mmgen-swaptxdo
 	cmds/mmgen-tool
 	cmds/mmgen-tool
 	cmds/mmgen-txbump
 	cmds/mmgen-txbump
 	cmds/mmgen-txcreate
 	cmds/mmgen-txcreate

+ 1 - 1
test/cmdtest_d/ct_ethdev.py

@@ -975,7 +975,7 @@ class CmdTestEthdev(CmdTestBase, CmdTestShared):
 		if self.daemon.id == 'geth': # yet another Geth bug
 		if self.daemon.id == 'geth': # yet another Geth bug
 			await asyncio.sleep(0.5)
 			await asyncio.sleep(0.5)
 		from mmgen.tx import NewTX
 		from mmgen.tx import NewTX
-		tx = await NewTX(cfg=cfg, proto=self.proto)
+		tx = await NewTX(cfg=cfg, proto=self.proto, target='tx')
 		tx.rpc = await self.rpc
 		tx.rpc = await self.rpc
 		res = await tx.get_receipt(txid)
 		res = await tx.get_receipt(txid)
 		imsg(f'Gas sent:  {res.gas_sent.hl():<9} {(res.gas_sent*res.gas_price).hl2(encl="()")}')
 		imsg(f'Gas sent:  {res.gas_sent.hl():<9} {(res.gas_sent*res.gas_price).hl2(encl="()")}')

+ 1 - 1
test/cmdtest_d/ct_help.py

@@ -148,7 +148,7 @@ class CmdTestHelp(CmdTestBase):
 			scripts = (
 			scripts = (
 				'walletgen', 'walletconv', 'walletchk', 'passchg', 'subwalletgen',
 				'walletgen', 'walletconv', 'walletchk', 'passchg', 'subwalletgen',
 				'addrgen', 'keygen', 'passgen',
 				'addrgen', 'keygen', 'passgen',
-				'txsign', 'txdo', 'txbump'),
+				'txdo', 'swaptxdo', 'txsign', 'txbump'),
 			expect = 'Available parameters.*Preset',
 			expect = 'Available parameters.*Preset',
 			pager  = False)
 			pager  = False)
 
 

+ 27 - 4
test/cmdtest_d/ct_swap.py

@@ -26,7 +26,8 @@ class CmdTestSwap(CmdTestRegtest):
 		('setup',             'regtest (Bob and Alice) mode setup'),
 		('setup',             'regtest (Bob and Alice) mode setup'),
 		('subgroup.init_bob', []),
 		('subgroup.init_bob', []),
 		('subgroup.fund_bob', ['init_bob']),
 		('subgroup.fund_bob', ['init_bob']),
-		('subgroup.data',     ['init_bob']),
+		('subgroup.data',     ['fund_bob']),
+		('subgroup.swap',     ['fund_bob']),
 		('stop',              'stopping regtest daemon'),
 		('stop',              'stopping regtest daemon'),
 	)
 	)
 	cmd_subgroups = {
 	cmd_subgroups = {
@@ -38,8 +39,9 @@ class CmdTestSwap(CmdTestRegtest):
 		),
 		),
 		'fund_bob': (
 		'fund_bob': (
 			'funding Bob’s wallet',
 			'funding Bob’s wallet',
-			('fund_bob', 'funding Bob’s wallet'),
-			('bob_bal1', 'Bob’s balance'),
+			('fund_bob1', 'funding Bob’s wallet (bech32)'),
+			('fund_bob2', 'funding Bob’s wallet (native Segwit)'),
+			('bob_bal',   'displaying Bob’s balance'),
 		),
 		),
 		'data': (
 		'data': (
 			'OP_RETURN data operations',
 			'OP_RETURN data operations',
@@ -51,6 +53,11 @@ class CmdTestSwap(CmdTestRegtest):
 			('data_tx2_do',      'Creating and sending a transaction with OP_RETURN data (binary)'),
 			('data_tx2_do',      'Creating and sending a transaction with OP_RETURN data (binary)'),
 			('data_tx2_chk',     'Checking the sent transaction'),
 			('data_tx2_chk',     'Checking the sent transaction'),
 			('generate3',        'Generate 3 blocks'),
 			('generate3',        'Generate 3 blocks'),
+			('bob_listaddrs',    'Display Bob’s addresses'),
+		),
+		'swap': (
+			'Swap operations',
+			('bob_swaptxcreate1', 'Create a swap transaction'),
 		),
 		),
 	}
 	}
 
 
@@ -70,9 +77,15 @@ class CmdTestSwap(CmdTestRegtest):
 	def addrimport_bob(self):
 	def addrimport_bob(self):
 		return self.addrimport('bob', mmtypes=['S', 'B'])
 		return self.addrimport('bob', mmtypes=['S', 'B'])
 
 
-	def fund_bob(self):
+	def fund_bob1(self):
 		return self.fund_wallet('bob', 'B', '500')
 		return self.fund_wallet('bob', 'B', '500')
 
 
+	def fund_bob2(self):
+		return self.fund_wallet('bob', 'S', '500')
+
+	def bob_bal(self):
+		return self.user_bal('bob', '1000')
+
 	def data_tx1_create(self):
 	def data_tx1_create(self):
 		return self._data_tx_create('1', 'B:2', 'B:3', 'data', sample1)
 		return self._data_tx_create('1', 'B:2', 'B:3', 'data', sample1)
 
 
@@ -149,3 +162,13 @@ class CmdTestSwap(CmdTestRegtest):
 
 
 	def generate3(self):
 	def generate3(self):
 		return self.generate(3)
 		return self.generate(3)
+
+	def bob_listaddrs(self):
+		t = self.spawn('mmgen-tool', ['--bob', 'listaddresses'])
+		return t
+
+	def bob_swaptxcreate1(self):
+		t = self.spawn(
+			'mmgen-swaptxcreate',
+			['-d', self.tmpdir, '-B', '--bob', 'BTC', '1.234', f'{self.sid}:S:3', 'LTC'])
+		return t

+ 1 - 1
test/daemontest_d/ut_tx.py

@@ -114,7 +114,7 @@ class unit_tests:
 		d.start()
 		d.start()
 
 
 		proto = init_proto(cfg, 'btc', need_amt=True)
 		proto = init_proto(cfg, 'btc', need_amt=True)
-		await NewTX(cfg=cfg, proto=proto)
+		await NewTX(cfg=cfg, proto=proto, target='tx')
 
 
 		d.stop()
 		d.stop()
 		d.remove_datadir()
 		d.remove_datadir()