txsend: add --wait and --txhex-idx options; related cleanups

This commit is contained in:
The MMGen Project 2025-04-26 10:38:55 +00:00
commit 69a39fad5e
Signed by: mmgen
GPG key ID: 3F8B1861E32B7DA2
14 changed files with 200 additions and 154 deletions

View file

@ -78,7 +78,10 @@ opts_data = {
-- -q, --quiet Suppress warnings; overwrite files without prompting
-- -s, --send Sign and send the transaction (the default if seed
+ data is provided)
-- -T, --txhex-idx=N Send only part N of a multi-part transaction.
+ Indexing begins with one.
-- -v, --verbose Produce more verbose output
e- -w, --wait Wait for transaction confirmation
-- -W, --allow-non-wallet-swap Allow signing of swap transactions that send funds
+ to non-wallet addresses
-- -x, --proxy=P Fetch the swap quote via SOCKS5 proxy P (host:port)

View file

@ -90,11 +90,14 @@ opts_data = {
+ according to BIP 125)
-s -s, --swap-proto Swap protocol to use (Default: {x_dfl},
+ Choices: {x_all})
-- -T, --txhex-idx=N Send only part N of a multi-part transaction.
+ Indexing begins with one.
-- -u, --subseeds= n The number of subseed pairs to scan for (default: {ss},
+ maximum: {ss_max}). Only the default or first supplied
+ wallet is scanned for subseeds.
-- -v, --verbose Produce more verbose output
b- -V, --vsize-adj= f Adjust transaction's estimated vsize by factor 'f'
e- -w, --wait Wait for transaction confirmation
-s -x, --proxy=P Fetch the swap quote via SOCKS5 proxy P (host:port)
e- -X, --cached-balances Use cached balances
-- -y, --yes Answer 'yes' to prompts, suppress non-essential output

View file

@ -51,13 +51,16 @@ opts_data = {
action has been successfully sent out-of-band.
-n, --tx-proxy=P Send transaction via public TX proxy P (supported proxies:
{tx_proxies}). This is done via a publicly accessible web
page, so no API key or registration is required
page, so no API key or registration is required.
-q, --quiet Suppress warnings; overwrite files without prompting
-r, --receipt Print the receipt of the sent transaction (Ethereum only)
-s, --status Get status of a sent transaction (or current transaction,
whether sent or unsent, when used with --autosign)
-t, --test Test whether the transaction can be sent without sending it
-T, --txhex-idx=N Send only part N of a multi-part transaction. Indexing
begins with one.
-v, --verbose Be more verbose
-w, --wait Wait for transaction confirmation (Ethereum only)
-x, --proxy=P Connect to TX proxy via SOCKS5 proxy P (host:port)
-y, --yes Answer 'yes' to prompts, suppress non-essential output
"""
@ -141,22 +144,15 @@ async def main():
await tx.post_send(asi)
sys.exit(0)
if cfg.status:
if tx.coin_txid:
cfg._util.qmsg(f'{tx.proto.coin} txid: {tx.coin_txid.hl()}')
retval = await tx.status.display(usr_req=True, return_exit_val=True)
if cfg.verbose:
tx.info.view_with_prompt('View transaction details?', pause=False)
sys.exit(retval)
if not cfg.status or cfg.receipt:
if tx.is_swap and not tx.check_swap_expiry():
die(1, 'Swap quote has expired. Please re-create the transaction')
if tx.is_swap and not tx.check_swap_expiry():
die(1, 'Swap quote has expired. Please re-create the transaction')
if not (cfg.yes or cfg.receipt):
tx.info.view_with_prompt('View transaction details?')
if tx.add_comment(): # edits an existing comment, returns true if changed
if not cfg.autosign:
tx.file.write(ask_write_default_yes=True)
if not cfg.yes:
tx.info.view_with_prompt('View transaction details?')
if tx.add_comment(): # edits an existing comment, returns true if changed
if not cfg.autosign:
tx.file.write(ask_write_default_yes=True)
await tx.send(cfg, asi)

View file

@ -76,8 +76,8 @@ class OnlineSigned(Signed, TxBase.OnlineSigned):
msg(orange('\n'+errmsg))
die(2, f'{m}{nl}Send of MMGen transaction {self.txid} failed')
def post_write(self):
pass
async def post_network_send(self, coin_txid):
return True
class Sent(TxBase.Sent, OnlineSigned):
pass

View file

@ -15,20 +15,19 @@ proto.btc.tx.status: Bitcoin transaction status class
import time
from ....tx import status as TxBase
from ....util import msg, suf, die
from ....util import msg, suf
from ....util2 import format_elapsed_hr
class Status(TxBase.Status):
async def display(self, *, usr_req=False, return_exit_val=False):
async def display(self, *, idx=''):
def do_exit(retval, message):
if return_exit_val:
def do_return(exitval, message):
if message:
msg(message)
return retval
else:
die(retval, message)
return exitval
assert idx == '', f'multiple txhex not supported for {self.tx.proto}'
tx = self.tx
class r:
@ -82,25 +81,23 @@ class Status(TxBase.Status):
return False
if await is_in_mempool():
if usr_req:
d = await tx.rpc.icall(
'gettransaction',
txid = tx.coin_txid,
include_watchonly = True,
verbose = False)
rep = ('' if d.get('bip125-replaceable') == 'yes' else 'NOT ') + 'replaceable'
t = d['timereceived']
if tx.cfg.quiet:
msg('Transaction is in mempool')
else:
msg(f'TX status: in mempool, {rep}')
msg('Sent {} ({})'.format(time.strftime('%c', time.gmtime(t)), format_elapsed_hr(t)))
d = await tx.rpc.icall(
'gettransaction',
txid = tx.coin_txid,
include_watchonly = True,
verbose = False)
rep = ('' if d.get('bip125-replaceable') == 'yes' else 'NOT ') + 'replaceable'
t = d['timereceived']
if tx.cfg.quiet:
msg('Transaction is in mempool')
else:
msg('Warning: transaction is in mempool!')
msg(f'TX status: in mempool, {rep}')
msg('Sent {} ({})'.format(time.strftime('%c', time.gmtime(t)), format_elapsed_hr(t)))
return do_return(0, '')
elif await is_in_wallet():
return do_exit(0, f'Transaction has {r.confs} confirmation{suf(r.confs)}')
return do_return(0, f'Transaction has {r.confs} confirmation{suf(r.confs)}')
elif await is_in_utxos():
return do_exit(4, 'ERROR: transaction is in the blockchain (but not in the tracking wallet)!')
return do_return(4, 'ERROR: transaction is in the blockchain (but not in the tracking wallet)!')
elif await is_replaced():
msg('Transaction has been replaced')
msg('Replacement transaction ' + (
@ -117,4 +114,4 @@ class Status(TxBase.Status):
d.append({})
for txid, mp_entry in zip(r.replacing_txs, d):
msg(f' {txid}' + (' in mempool' if 'height' in mp_entry else ''))
return do_exit(0, '')
return do_return(0, '')

View file

@ -54,11 +54,29 @@ class Base(TxBase):
def is_replaceable(self):
return True
# used for testing only:
async def get_receipt(self, txid):
rx = await self.rpc.call('eth_getTransactionReceipt', '0x'+txid) # -> null if pending
if not rx:
async def get_receipt(self, txid, *, receipt_only=False):
import asyncio
from ....util import msg, msg_r
for n in range(60):
rx = await self.rpc.call('eth_getTransactionReceipt', '0x'+txid) # -> null if pending
if rx or not self.cfg.wait:
break
if n == 0:
msg_r('Waiting for first confirmation..')
await asyncio.sleep(1)
msg_r('.')
if rx:
if n:
msg('OK')
if receipt_only:
return rx
else:
if self.cfg.wait:
msg('timeout exceeded!')
return None
tx = await self.rpc.call('eth_getTransactionByHash', '0x'+txid)
return namedtuple('exec_status',
['status', 'gas_sent', 'gas_used', 'gas_price', 'contract_addr', 'tx', 'rx'])(

View file

@ -13,7 +13,7 @@ proto.eth.tx.online: Ethereum online signed transaction class
"""
from ....util import msg, die
from ....color import orange
from ....color import yellow, green, orange
from ....tx import online as TxBase
from .. import erigon_sleep
from .signed import Signed, TokenSigned
@ -42,9 +42,25 @@ class OnlineSigned(Signed, TxBase.OnlineSigned):
await erigon_sleep(self)
return ret.removeprefix('0x')
def post_write(self):
if 'token_addr' in self.txobj and not self.txobj['to']:
msg('Contract address: {}'.format(self.txobj['token_addr'].hl(0)))
async def post_network_send(self, coin_txid):
res = await self.get_receipt(coin_txid)
if not res:
if self.cfg.wait:
msg('{} {} {}'.format(
yellow('Send of'),
coin_txid.hl(),
yellow('failed? (failed to get receipt)')))
return False
msg(f'Gas sent: {res.gas_sent.hl()}\n'
f'Gas used: {res.gas_used.hl()}')
if res.status == 0:
if res.gas_used == res.gas_sent:
msg(yellow('All gas was used!'))
die(1, f'Send of {coin_txid.hl()} failed (status=0)')
msg(f'Status: {green(str(res.status))}')
if res.contract_addr:
msg('Contract address: {}'.format(res.contract_addr.hl(0)))
return True
class TokenOnlineSigned(TokenSigned, OnlineSigned):

View file

@ -13,31 +13,33 @@ proto.eth.tx.status: Ethereum transaction status class
"""
from ....tx import status as TxBase
from ....util import msg, Msg, die, suf, capfirst
from ....util import msg, suf, capfirst
class Status(TxBase.Status):
async def display(self, *, usr_req=False, return_exit_val=False, print_receipt=False, idx=''):
async def display(self, *, idx=''):
def do_exit(retval, message):
if return_exit_val:
def do_return(exitval, message):
if message:
msg(message)
return retval
else:
die(retval, message)
return exitval
tx = self.tx
coin_txid = '0x' + getattr(tx, f'coin_txid{idx}')
tx_desc = 'transaction' + (f' {idx}' if idx else '')
async def is_in_mempool():
if not 'full_node' in tx.rpc.caps:
return False
if tx.rpc.daemon.id in ('parity', 'openethereum'):
pool = [x['hash'] for x in await tx.rpc.call('parity_pendingTransactions')]
return coin_txid in [x['hash'] for x in await tx.rpc.call('parity_pendingTransactions')]
elif tx.rpc.daemon.id in ('geth', 'reth', 'erigon'):
def gen(key):
for e in res[key].values():
for v in e.values():
yield v['hash']
res = await tx.rpc.call('txpool_content')
pool = list(res['pending']) + list(res['queued'])
return coin_txid in pool
return coin_txid in list(gen('queued')) + list(gen('pending'))
async def is_in_wallet():
d = await tx.rpc.call('eth_getTransactionReceipt', coin_txid)
@ -50,26 +52,18 @@ class Status(TxBase.Status):
rx = d)
if await is_in_mempool():
msg(
'Transaction is in mempool' if usr_req else
'Warning: transaction is in mempool!')
return
return do_return(0, f'{capfirst(tx_desc)} is in mempool')
if usr_req or print_receipt:
ret = await is_in_wallet()
if print_receipt:
import json
Msg(json.dumps(ret.rx, indent=4))
return not ret.exec_status
if ret:
if tx.txobj['data'] and not tx.is_swap:
cd = capfirst(tx.contract_desc)
if ret.exec_status == 0:
msg(f'{cd} failed to execute!')
else:
msg(f'{cd} successfully executed with status {ret.exec_status}')
return do_exit(0, f'Transaction has {ret.confs} confirmation{suf(ret.confs)}')
return do_exit(1, 'Transaction is neither in mempool nor blockchain!')
if res := await is_in_wallet():
if tx.txobj['data'] and not tx.is_swap:
cd = capfirst(tx.contract_desc)
msg(f'{cd} failed to execute!' if res.exec_status == 0 else
f'{cd} successfully executed with status {res.exec_status}')
return do_return(
int(not res.exec_status),
f'{capfirst(tx_desc)} has {res.confs} confirmation{suf(res.confs)}')
return do_return(1, f'{capfirst(tx_desc)} is neither in mempool nor blockchain!')
class TokenStatus(Status):
pass

View file

@ -12,6 +12,11 @@
tx.online: online signed transaction class
"""
import sys, time, asyncio
from ..util import msg, Msg, ymsg, make_timestr, die
from ..color import pink, yellow
from .signed import Signed, AutomountSigned
class OnlineSigned(Signed):
@ -22,10 +27,7 @@ class OnlineSigned(Signed):
return _base_proto_subclass('Status', 'status', self.proto)(self)
def check_swap_expiry(self):
import time
from ..util import msg, make_timestr
from ..util2 import format_elapsed_hr
from ..color import pink, yellow
expiry = self.swap_quote_expiry
now = int(time.time())
t_rem = expiry - now
@ -36,8 +38,7 @@ class OnlineSigned(Signed):
c = make_timestr(expiry)))
return t_rem >= 0
def confirm_send(self):
from ..util import msg
def confirm_send(self, idxs):
from ..ui import confirm_or_raise
confirm_or_raise(
cfg = self.cfg,
@ -45,6 +46,8 @@ class OnlineSigned(Signed):
action = f'broadcast this transaction to the {self.proto.coin} {self.proto.network.upper()} network',
expect = 'YES' if self.cfg.quiet or self.cfg.yes else 'YES, I REALLY WANT TO DO THIS')
msg('Sending transaction')
if len(idxs) > 1 and getattr(self, 'coin_txid2', None) and self.is_swap:
ymsg('Warning: two transactions (approval and router) will be broadcast to the network')
async def post_send(self, asi):
from . import SentTX
@ -55,21 +58,40 @@ class OnlineSigned(Signed):
outdir = asi.txauto_dir if asi else None,
ask_overwrite = False,
ask_write = False)
tx2.post_write()
async def send(self, cfg, asi):
if not (cfg.receipt or cfg.dump_hex or cfg.test):
self.confirm_send()
status_exitval = None
sent_status = None
all_ok = True
idxs = ['', '2']
for idx in ('', '2'):
if cfg.txhex_idx:
if getattr(self, 'coin_txid2', None):
if cfg.txhex_idx in ('1', '2'):
idxs = ['' if cfg.txhex_idx == '1' else cfg.txhex_idx]
else:
die(1, f'{cfg.txhex_idx}: invalid parameter for --txhex-idx (must be 1 or 2)')
else:
die(1, 'Transaction has only one part, so --txhex-idx makes no sense')
if not (cfg.status or cfg.receipt or cfg.dump_hex or cfg.test):
self.confirm_send(idxs)
for idx in idxs:
if coin_txid := getattr(self, f'coin_txid{idx}', None):
txhex = getattr(self, f'serialized{idx}')
if cfg.receipt:
import sys
sys.exit(await self.status.display(print_receipt=True, idx=idx))
if cfg.status:
cfg._util.qmsg(f'{self.proto.coin} txid: {coin_txid.hl()}')
if cfg.verbose:
await self.post_network_send(coin_txid)
status_exitval = await self.status.display(idx=idx)
elif cfg.receipt:
if res := await self.get_receipt(coin_txid, receipt_only=True):
import json
Msg(json.dumps(res, indent=4))
else:
msg(f'Unable to get receipt for TX {coin_txid.hl()}')
elif cfg.dump_hex:
from ..fileutil import write_data_to_file
write_data_to_file(
@ -80,29 +102,43 @@ class OnlineSigned(Signed):
ask_overwrite = False,
ask_tty = False)
elif cfg.tx_proxy:
if idx != '' and not cfg.test_suite:
await asyncio.sleep(2)
from .tx_proxy import send_tx
msg(f'Sending TX: {coin_txid.hl()}')
if ret := send_tx(cfg, txhex):
if ret != coin_txid:
from ..util import ymsg
ymsg(f'Warning: txid mismatch (after sending) ({ret} != {coin_txid})')
sent_status = 'confirm_post_send'
elif cfg.test:
await self.test_sendable(txhex)
else: # node send
msg(f'Sending TX: {coin_txid.hl()}')
if not cfg.bogus_send:
if idx != '':
await asyncio.sleep(1)
ret = await self.send_with_node(txhex)
assert ret == coin_txid, f'txid mismatch (after sending) ({ret} != {coin_txid})'
desc = 'BOGUS transaction NOT' if cfg.bogus_send else 'Transaction'
from ..util import msg
msg(desc + ' sent: ' + coin_txid.hl())
sent_status = 'no_confirm_post_send'
if sent_status:
if cfg.wait and sent_status:
res = await self.post_network_send(coin_txid)
if all_ok:
all_ok = res
if not cfg.txhex_idx and sent_status and all_ok:
from ..ui import keypress_confirm
if sent_status == 'no_confirm_post_send' or not asi or keypress_confirm(
cfg, 'Mark transaction as sent on removable device?'):
await self.post_send(asi)
if status_exitval is not None:
if cfg.verbose:
self.info.view_with_prompt('View transaction details?', pause=False)
sys.exit(status_exitval)
class AutomountOnlineSigned(AutomountSigned, OnlineSigned):
pass

View file

@ -71,14 +71,14 @@ class TxProxyClient:
assert len(res) == 1, 'more than one matching form!'
return res[0]
def cache_fn(self, desc):
return f'{self.name}-{desc}.html'
def cache_fn(self, desc, *, extra_desc=None):
return '{}-{}{}.html'.format(self.name, desc, f'-{extra_desc}' if extra_desc else '')
def save_response(self, data, desc):
def save_response(self, data, desc, *, extra_desc=None):
from ..fileutil import write_data_to_file
write_data_to_file(
self.cfg,
self.cache_fn(desc),
self.cache_fn(desc, extra_desc=extra_desc),
data,
desc = f'{desc} page from {orange(self.host)}')
@ -200,7 +200,7 @@ def send_tx(cfg, txhex):
msg('done')
msg('Transaction ' + (f'sent: {txid.hl()}' if txid else 'send failed'))
c.save_response(result_text, 'result')
c.save_response(result_text, 'result', extra_desc=txid)
return txid

View file

@ -46,6 +46,7 @@ ignore = [
"test/*.py" = [ "F401" ] # imported but unused
"test/colortest.py" = [ "F403", "F405" ] # `import *` used
"test/tooltest2.py" = [ "F403", "F405" ] # `import *` used
"test/overlay/tree/*" = [ "ALL" ]
[tool.pylint.format]
indent-string = "\t"

View file

@ -208,9 +208,7 @@ class CmdTestEthBump(CmdTestEthBumpMethods, CmdTestEthSwapMethods, CmdTestSwapMe
('token_deploy_a', 'deploying ERC20 token MM1 (SafeMath)'),
('token_deploy_b', 'deploying ERC20 token MM1 (Owned)'),
('token_deploy_c', 'deploying ERC20 token MM1 (Token)'),
('wait_reth2', 'waiting for block'),
('token_fund_user', 'transferring token funds from dev to user'),
('wait6', 'waiting for block'),
('token_addrgen', 'generating token addresses'),
('token_addrimport', 'importing token addresses using token address (MM1)'),
('token_bal1', 'the token balance'),
@ -219,7 +217,7 @@ class CmdTestEthBump(CmdTestEthBumpMethods, CmdTestEthSwapMethods, CmdTestSwapMe
'creating, signing, sending, bumping and resending a token transaction (fee-bump only)',
('token_txdo1', 'creating, signing and sending a token transaction'),
('token_txbump1', 'bumping the token transaction (fee-bump)'),
('wait7', 'waiting for block'),
('wait6', 'waiting for block'),
('token_bal2', 'the token balance'),
),
'token_new_outputs': (
@ -228,7 +226,7 @@ class CmdTestEthBump(CmdTestEthBumpMethods, CmdTestEthSwapMethods, CmdTestSwapMe
('token_txbump2', 'creating a replacement token transaction (new outputs)'),
('token_txbump2sign', 'signing the replacement transaction'),
('token_txbump2send', 'sending the replacement transaction'),
('wait8', 'waiting for block'),
('wait7', 'waiting for block'),
('token_bal3', 'the token balance'),
)
}
@ -338,8 +336,7 @@ class CmdTestEthBump(CmdTestEthBumpMethods, CmdTestEthSwapMethods, CmdTestSwapMe
def wait_reth1(self):
return self._wait_for_block() if self.daemon.id == 'reth' else 'silent'
wait_reth2 = wait_reth1
wait1 = wait2 = wait3 = wait4 = wait5 = wait6 = wait7 = wait8 = CmdTestEthBumpMethods._wait_for_block
wait1 = wait2 = wait3 = wait4 = wait5 = wait6 = wait7 = CmdTestEthBumpMethods._wait_for_block
txsign1 = txsign2 = txbump1sign = txbump2sign = CmdTestEthBumpMethods._txsign
txsend1 = txsend2 = txbump1send = txbump2send = CmdTestEthBumpMethods._txsend

View file

@ -240,8 +240,8 @@ class CmdTestEthdevMethods:
gas,
mmgen_cmd = 'txdo',
gas_price = '8G',
num = None,
get_receipt = True):
num = None):
keyfile = joinpath(self.tmpdir, dfl_devkey_fn)
fn = joinpath(self.tmpdir, 'mm'+str(num), key+'.bin')
args = [
@ -250,8 +250,8 @@ class CmdTestEthdevMethods:
f'--gas={gas}',
f'--contract-data={fn}',
f'--inputs={dfl_devaddr}',
'--yes',
]
'--yes'
] + (['--wait'] if mmgen_cmd == 'txdo' else [])
contract_addr = self._get_contract_address(dfl_devaddr)
if key == 'Token':
@ -271,36 +271,30 @@ class CmdTestEthdevMethods:
txfile = txfile.replace('.rawtx', '.sigtx')
t = self.spawn('mmgen-txsend',
self.eth_opts + [txfile], no_msg=True, no_passthru_opts=['coin'])
self.eth_opts + ['--wait', txfile], no_msg=True, no_passthru_opts=['coin'])
txid = self.txsend_ui_common(t,
self.txsend_ui_common(t,
caller = mmgen_cmd,
quiet = mmgen_cmd == 'txdo' or not self.cfg.debug,
contract_addr = contract_addr,
bogus_send = False)
_ = strip_ansi_escapes(t.expect_getend('Contract address: '))
assert _ == contract_addr, f'Contract address mismatch: {_} != {contract_addr}'
if get_receipt:
if (await self.get_tx_receipt(txid)).status == 0:
die(2, f'Contract {num}:{key} failed to deploy. Aborting')
if key == 'Token':
imsg(f'\nToken MM{num} deployed!')
return t
async def _token_deploy_math(self, *, num, get_receipt=True, mmgen_cmd='txdo'):
async def _token_deploy_math(self, *, num, mmgen_cmd='txdo'):
return await self._token_deploy(
num=num, key='SafeMath', gas=500_000, get_receipt=get_receipt, mmgen_cmd=mmgen_cmd)
num=num, key='SafeMath', gas=500_000, mmgen_cmd=mmgen_cmd)
async def _token_deploy_owned(self, *, num, get_receipt=True):
async def _token_deploy_owned(self, *, num):
return await self._token_deploy(
num=num, key='Owned', gas=1_000_000, get_receipt=get_receipt)
num=num, key='Owned', gas=1_000_000)
async def _token_deploy_token(self, *, num, get_receipt=True):
async def _token_deploy_token(self, *, num):
return await self._token_deploy(
num=num, key='Token', gas=4_000_000, gas_price='7G', get_receipt=get_receipt)
num=num, key='Token', gas=4_000_000, gas_price='7G')
def _token_bal_check(self, *, pat):
return self._bal_check(pat=pat, add_opts=['--token=MM1'])
@ -315,7 +309,7 @@ class CmdTestEthdevMethods:
caller = cmd,
file_desc = 'Unsigned automount transaction')
async def _token_transfer_ops(self, *, op, mm_idxs, amt=1000, get_receipt=True, sid=dfl_sid):
async def _token_transfer_ops(self, *, op, mm_idxs, amt=1000, sid=dfl_sid):
self.spawn(msg_only=True)
from mmgen.tool.wallet import tool_cmd
usr_mmaddrs = [f'{sid}:E:{i}' for i in mm_idxs]
@ -338,9 +332,14 @@ class CmdTestEthdevMethods:
key = dfl_devkey,
gas = self.proto.coin_amt(120000, from_unit='wei'),
gasPrice = self.proto.coin_amt(8, from_unit='Gwei'))
if get_receipt and (await self.get_tx_receipt(txid)).status == 0:
die(2, 'Transfer of token funds failed. Aborting')
rpc = await self.rpc
for n in range(50): # long delay for txbump
rx = await rpc.call('eth_getTransactionReceipt', '0x' + txid) # -> null if pending
if rx:
break
await asyncio.sleep(0.5)
if not rx:
die(1, 'tx receipt timeout exceeded')
async def show_bals(rpc):
for i in range(len(usr_mmaddrs)):
@ -1114,13 +1113,13 @@ class CmdTestEthdev(CmdTestEthdevMethods, CmdTestBase, CmdTestShared):
def bal3(self):
return self.bal(n='3')
def tx_status(self, ext, expect_str, expect_str2='', exit_val=0):
def tx_status(self, ext, *, expect_str, expect_str2='', add_opts=[], exit_val=0):
self.mining_delay()
ext = ext.format('' if self.cfg.debug_utf8 else '')
txfile = self.get_file_with_ext(ext, no_dot=True)
t = self.spawn(
'mmgen-txsend',
self.eth_opts + ['--status', txfile],
self.eth_opts + add_opts + ['--status', txfile],
no_passthru_opts = ['coin'],
exit_val = exit_val)
t.expect(expect_str)
@ -1129,7 +1128,10 @@ class CmdTestEthdev(CmdTestEthdevMethods, CmdTestBase, CmdTestShared):
return t
def tx_status1(self):
return self.tx_status(ext='2.345,50000]{}.regtest.sigtx', expect_str='has 1 confirmation')
return self.tx_status(
ext = '2.345,50000]{}.regtest.sigtx',
add_opts = ['--verbose'],
expect_str = 'has 1 confirmation')
def tx_status1a(self):
return self.tx_status(ext='2.345,50000]{}.regtest.sigtx', expect_str='has 2 confirmations')
@ -1333,22 +1335,6 @@ class CmdTestEthdev(CmdTestEthdevMethods, CmdTestBase, CmdTestShared):
token_data = {'name':'MMGen Token 2', 'symbol':'MM2', 'supply':10**18, 'decimals':10}
return self.token_compile(token_data)
async def get_tx_receipt(self, txid):
if self.daemon.id in ('geth', 'reth'): # workaround for mining race condition in dev mode
await asyncio.sleep(1 if self.daemon.id == 'reth' else 0.5)
from mmgen.tx import NewTX
tx = await NewTX(cfg=self.cfg, proto=self.proto, target='tx')
tx.rpc = await self.rpc
res = await tx.get_receipt(txid)
if not res:
die(1, f'Error getting receipt for transaction {txid}')
imsg(f'Gas sent: {res.gas_sent.hl():<9} {(res.gas_sent*res.gas_price).hl2(encl="()")}')
imsg(f'Gas used: {res.gas_used.hl():<9} {(res.gas_used*res.gas_price).hl2(encl="()")}')
imsg(f'Gas price: {res.gas_price.hl()}')
if res.gas_used == res.gas_sent:
omsg(yellow('Warning: all gas was used!'))
return res
async def token_deploy1a(self):
return await self._token_deploy_math(num=1)

View file

@ -34,20 +34,19 @@ def {name}(self):
class CmdTestEthSwapMethods:
async def token_deploy_a(self):
return await self._token_deploy_math(num=1, get_receipt=False)
return await self._token_deploy_math(num=1)
async def token_deploy_b(self):
return await self._token_deploy_owned(num=1, get_receipt=False)
return await self._token_deploy_owned(num=1)
async def token_deploy_c(self):
return await self._token_deploy_token(num=1, get_receipt=False)
return await self._token_deploy_token(num=1)
def token_fund_user(self):
return self._token_transfer_ops(
op = 'fund_user',
mm_idxs = [1],
amt = self.token_fund_amt,
get_receipt = False)
amt = self.token_fund_amt)
def token_addrgen(self):
return self._token_addrgen(mm_idxs=[1], naddrs=5)