From 67ef5d39875b019c71868f472d8890e0dac0d6d4 Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Sat, 26 Apr 2025 10:38:55 +0000 Subject: [PATCH] mmgen-swaptxcreate: new `--list-assets` option; related cleanups --- mmgen/main_txcreate.py | 10 +++- mmgen/swap/asset.py | 47 +++++++++++++++++-- mmgen/swap/proto/thorchain/asset.py | 20 ++++---- mmgen/tx/new_swap.py | 11 ++++- test/cmdtest_d/swap.py | 9 ++++ .../mmgen/swap/proto/thorchain/asset.py | 21 ++++++--- 6 files changed, 97 insertions(+), 21 deletions(-) diff --git a/mmgen/main_txcreate.py b/mmgen/main_txcreate.py index f8122c12..a99e45fa 100755 --- a/mmgen/main_txcreate.py +++ b/mmgen/main_txcreate.py @@ -22,7 +22,7 @@ mmgen-txcreate: Create a cryptocoin transaction with MMGen- and/or non-MMGen """ from .cfg import gc, Config -from .util import fmt_list, async_run +from .util import Msg, fmt_list, async_run target = gc.prog_name.split('-')[1].removesuffix('create') @@ -73,6 +73,7 @@ opts_data = { + according to BIP 125) -s -s, --swap-proto Swap protocol to use (Default: {x_dfl}, + Choices: {x_all}) + -s -S, --list-assets List available swap assets -- -v, --verbose Produce more verbose output b- -V, --vsize-adj= f Adjust transaction's estimated vsize by factor 'f' -s -x, --proxy=P Fetch the swap quote via SOCKS5 proxy ‘P’ (host:port) @@ -104,6 +105,13 @@ opts_data = { cfg = Config(opts_data=opts_data) +if cfg.list_assets: + import sys + from .tx.new_swap import get_swap_proto_mod + sp = get_swap_proto_mod(cfg.swap_proto) + Msg('AVAILABLE SWAP ASSETS:\n' + sp.SwapAsset('BTC', 'send').fmt_assets_data(indent=' ')) + sys.exit(0) + if not (cfg.info or cfg.contract_data) and len(cfg._args) < {'tx': 1, 'swaptx': 2}[target]: cfg._usage() diff --git a/mmgen/swap/asset.py b/mmgen/swap/asset.py index 7151cf70..4a922db0 100644 --- a/mmgen/swap/asset.py +++ b/mmgen/swap/asset.py @@ -18,19 +18,56 @@ from ..util import die class SwapAsset: - _ad = namedtuple('swap_asset_data', ['desc', 'name', 'full_name', 'abbr']) + _ad = namedtuple('swap_asset_data', ['desc', 'name', 'full_name', 'abbr', 'tested']) assets_data = {} - send = () - recv = () + evm_contracts = {} + unsupported = () + blacklisted = {} evm_chains = () + def fmt_assets_data(self, indent=''): + + def gen_good(): + fs = '%s{:10} {:23} {:9} {}' % indent + yield fs.format('ASSET', 'DESCRIPTION', 'STATUS', 'CONTRACT ADDRESS') + for k, v in self.assets_data.items(): + if not k in self.blacklisted: + if k in self.send or k in self.recv: + yield fs.format( + k, + v.desc, + 'tested' if v.tested else 'untested', + self.evm_contracts.get(k,'-')) + + def gen_bad(): + if self.blacklisted: + fs = '%s{:10} {:23} {}' % indent + yield '\n\nBlacklisted assets:' + yield fs.format('ASSET', 'DESCRIPTION', 'REASON') + for k, v in self.blacklisted.items(): + yield fs.format(k, self.assets_data[k].desc, v) + + return '\n'.join(gen_good()) + '\n'.join(gen_bad()) + @classmethod - def get_full_name(self, s): - for d in self.assets_data.values(): + def get_full_name(cls, s): + for d in cls.assets_data.values(): if s in (d.abbr, d.full_name): return d.full_name or f'{d.name}.{d.name}' die('SwapAssetError', f'{s!r}: unrecognized asset name or abbreviation') + @property + def tested(self): + return [k for k, v in self.assets_data.items() if v.tested] + + @property + def send(self): + return set(self.assets_data) - set(self.unsupported) - set(self.blacklisted) + + @property + def recv(self): + return set(self.assets_data) - set(self.unsupported) - set(self.blacklisted) + @property def chain(self): return self.data.full_name.split('.', 1)[0] if self.data.full_name else self.name diff --git a/mmgen/swap/proto/thorchain/asset.py b/mmgen/swap/proto/thorchain/asset.py index cd90022b..f3185dcb 100644 --- a/mmgen/swap/proto/thorchain/asset.py +++ b/mmgen/swap/proto/thorchain/asset.py @@ -18,14 +18,18 @@ class THORChainSwapAsset(SwapAsset): _ad = SwapAsset._ad assets_data = { - 'BTC': _ad('Bitcoin', 'BTC', None, 'b'), - 'LTC': _ad('Litecoin', 'LTC', None, 'l'), - 'BCH': _ad('Bitcoin Cash', 'BCH', None, 'c'), - 'ETH': _ad('Ethereum', 'ETH', None, 'e'), - 'DOGE': _ad('Dogecoin', 'DOGE', None, 'd'), - 'RUNE': _ad('Rune (THORChain)', 'RUNE', 'THOR.RUNE', 'r'), + 'BTC': _ad('Bitcoin', 'BTC', None, 'b', True), + 'LTC': _ad('Litecoin', 'LTC', None, 'l', True), + 'BCH': _ad('Bitcoin Cash', 'BCH', None, 'c', True), + 'ETH': _ad('Ethereum', 'ETH', None, 'e', True), + 'DOGE': _ad('Dogecoin', 'DOGE', None, 'd', False), + 'RUNE': _ad('Rune (THORChain)', 'RUNE', 'THOR.RUNE', 'r', False), } - send = ('BTC', 'LTC', 'BCH', 'ETH') - recv = ('BTC', 'LTC', 'BCH', 'ETH') + evm_contracts = {} + + unsupported = ('DOGE', 'RUNE') + + blacklisted = {} + evm_chains = ('ETH', 'AVAX', 'BSC', 'BASE') diff --git a/mmgen/tx/new_swap.py b/mmgen/tx/new_swap.py index 2d7036d2..cea99ce7 100755 --- a/mmgen/tx/new_swap.py +++ b/mmgen/tx/new_swap.py @@ -107,7 +107,7 @@ class NewSwap(New): arg = get_arg() # arg 3: chg_spec (change address spec) - if args.send_amt and not (self.proto.is_evm or arg in sp.SwapAsset.recv): # is change arg + if args.send_amt and not (self.proto.is_evm or arg in sa.recv): # is change arg nonlocal chg_output chg_output = await self.get_chg_output(arg, addrfiles) arg = get_arg() @@ -124,12 +124,21 @@ class NewSwap(New): self.cfg._usage() sp = self.swap_proto_mod + sa = sp.SwapAsset('BTC', 'send') args_in = list(cmd_args) args = CmdlineArgs() chg_output = None await parse() + for a in (self.send_asset, self.recv_asset): + if a.name not in sa.tested: + from ..util import msg, ymsg + from ..term import get_char + ymsg(f'Warning: {a.direction} asset {a.name} is untested by the MMGen Project') + get_char('Press any key to continue: ') + msg('') + if args.send_amt and not (chg_output or self.proto.is_evm): chg_output = await self.get_chg_output(None, addrfiles) diff --git a/test/cmdtest_d/swap.py b/test/cmdtest_d/swap.py index 6057f107..483ddc6a 100755 --- a/test/cmdtest_d/swap.py +++ b/test/cmdtest_d/swap.py @@ -272,6 +272,7 @@ class CmdTestSwap(CmdTestSwapMethods, CmdTestRegtest, CmdTestAutosignThreaded): need_daemon = True cmd_group_in = ( + ('list_assets', 'listing swap assets'), ('subgroup.init_data', []), ('subgroup.data', ['init_data']), ('subgroup.init_swap', []), @@ -416,6 +417,14 @@ class CmdTestSwap(CmdTestSwapMethods, CmdTestRegtest, CmdTestAutosignThreaded): def sid(self): return self._user_sid('bob') + def list_assets(self): + t = self.spawn('mmgen-swaptxcreate', ['--list-assets']) + t.expect('AVAILABLE') + t.expect('ETH.MM1') + t.expect('Blacklisted') + t.expect('ETH.JUNK') + return t + def walletcreate_bob(self): dest = Path(self.tr.data_dir, 'regtest', 'bob') dest.mkdir(exist_ok=True) diff --git a/test/overlay/fakemods/mmgen/swap/proto/thorchain/asset.py b/test/overlay/fakemods/mmgen/swap/proto/thorchain/asset.py index d04e8be9..39f55a54 100755 --- a/test/overlay/fakemods/mmgen/swap/proto/thorchain/asset.py +++ b/test/overlay/fakemods/mmgen/swap/proto/thorchain/asset.py @@ -3,12 +3,21 @@ from .asset_orig import * class overlay_fake_THORChainSwapAsset: assets_data = { - 'ETH.MM1': THORChainSwapAsset._ad('MM1 Token (ETH)', None, 'ETH.MM1', None), - 'ETH.USDT': THORChainSwapAsset._ad('Tether (ETH)', None, 'ETH.USDT', None) + 'ETH.USDT': THORChainSwapAsset._ad('Tether (ETH)', None, 'ETH.USDT', None, True), + 'ETH.MM1': THORChainSwapAsset._ad('MM1 Token (ETH)', None, 'ETH.MM1', None, True), + 'ETH.JUNK': THORChainSwapAsset._ad('Junk Token (ETH)', None, 'ETH.JUNK', None, True), + 'ETH.NONE': THORChainSwapAsset._ad('Unavailable Token (ETH)', None, 'ETH.NONE', None, True) + } + evm_contracts = { + 'ETH.MM1': 'deadbeefdeadbeefdeadbeefdeadbeefdeadbeef' + } + unsupported = ('ETH.NONE',) + + blacklisted = { + 'ETH.JUNK': 'Because it’s junk', } - send = ('ETH.MM1',) - recv = ('ETH.MM1', 'ETH.USDT') THORChainSwapAsset.assets_data |= overlay_fake_THORChainSwapAsset.assets_data -THORChainSwapAsset.send += overlay_fake_THORChainSwapAsset.send -THORChainSwapAsset.recv += overlay_fake_THORChainSwapAsset.recv +THORChainSwapAsset.unsupported += overlay_fake_THORChainSwapAsset.unsupported +THORChainSwapAsset.blacklisted.update(overlay_fake_THORChainSwapAsset.blacklisted) +THORChainSwapAsset.evm_contracts.update(overlay_fake_THORChainSwapAsset.evm_contracts)