From 8d33c6d4a9ad570f0bbf5eab1f6003e772be41fe Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Wed, 7 May 2025 18:24:07 +0000 Subject: [PATCH] proto.eth.tx: make `self.gas` an integer --- mmgen/main_txcreate.py | 3 ++- mmgen/main_txdo.py | 3 ++- mmgen/proto/eth/contract.py | 3 ++- mmgen/proto/eth/tx/base.py | 4 +-- mmgen/proto/eth/tx/info.py | 3 ++- mmgen/proto/eth/tx/new.py | 11 +++----- mmgen/proto/eth/tx/signed.py | 2 +- mmgen/proto/eth/tx/unsigned.py | 8 +++--- test/cmdtest_d/ethdev.py | 2 +- test/cmdtest_d/ethswap.py | 46 +++++++++++++++++++++++++--------- 10 files changed, 55 insertions(+), 30 deletions(-) diff --git a/mmgen/main_txcreate.py b/mmgen/main_txcreate.py index 1aa8a004..9062f83b 100755 --- a/mmgen/main_txcreate.py +++ b/mmgen/main_txcreate.py @@ -57,7 +57,8 @@ opts_data = { + {fu} (an integer followed by {fl}). + See FEE SPECIFICATION below. If omitted, fee will be + calculated using network fee estimation. - e- -g, --gas= g Specify start gas amount in Wei + et -g, --gas=N Specify gas limit (integer) + -s -g, --gas=N Specify gas limit for Ethereum (integer) -- -i, --info Display {a_info} and exit -- -I, --inputs= i Specify transaction inputs (comma-separated list of + MMGen IDs or coin addresses). Note that ALL unspent diff --git a/mmgen/main_txdo.py b/mmgen/main_txdo.py index 85c4a5da..9472b598 100755 --- a/mmgen/main_txdo.py +++ b/mmgen/main_txdo.py @@ -57,7 +57,8 @@ opts_data = { + {fu} (an integer followed by {fl!r}). + See FEE SPECIFICATION below. If omitted, fee will be + calculated using network fee estimation. - e- -g, --gas= g Specify start gas amount in Wei + et -g, --gas=N Specify gas limit (integer) + -s -g, --gas=N Specify gas limit for Ethereum (integer) -- -H, --hidden-incog-input-params=f,o Read hidden incognito data from file + 'f' at offset 'o' (comma-separated) -- -i, --in-fmt= f Input is from wallet format 'f' (see FMT CODES below) diff --git a/mmgen/proto/eth/contract.py b/mmgen/proto/eth/contract.py index 9d35c03d..c7436b23 100755 --- a/mmgen/proto/eth/contract.py +++ b/mmgen/proto/eth/contract.py @@ -67,9 +67,10 @@ class Contract: return ret def make_tx_in(self, *, gas, gasPrice, nonce, data): + assert isinstance(gas, int), f'{type(gas)}: incorrect type for ‘gas’ (must be an int)' return { 'to': bytes.fromhex(self.addr), - 'startgas': gas.toWei(), + 'startgas': gas, 'gasprice': gasPrice.toWei(), 'value': 0, 'nonce': nonce, diff --git a/mmgen/proto/eth/tx/base.py b/mmgen/proto/eth/tx/base.py index 5ef1e4dd..4172a6cb 100755 --- a/mmgen/proto/eth/tx/base.py +++ b/mmgen/proto/eth/tx/base.py @@ -44,12 +44,12 @@ class Base(TxBase): # given absolute fee in ETH, return gas price in ETH def fee_abs2gasprice(self, abs_fee): - return self.proto.coin_amt(int(abs_fee.toWei() // self.gas.toWei()), from_unit='wei') + return self.proto.coin_amt(int(abs_fee.toWei() // self.gas), from_unit='wei') # given rel fee (gasPrice) in wei, return absolute fee using self.gas (Ethereum-only method) def fee_gasPrice2abs(self, rel_fee): assert isinstance(rel_fee, int), f'{rel_fee!r}: incorrect type for fee estimate (not an integer)' - return self.proto.coin_amt(rel_fee * self.gas.toWei(), from_unit='wei') + return self.proto.coin_amt(rel_fee * self.gas, from_unit='wei') def is_replaceable(self): return True diff --git a/mmgen/proto/eth/tx/info.py b/mmgen/proto/eth/tx/info.py index e1ae93ee..ee44a7ae 100755 --- a/mmgen/proto/eth/tx/info.py +++ b/mmgen/proto/eth/tx/info.py @@ -16,6 +16,7 @@ from ....tx.info import TxInfo from ....util import fmt, pp_fmt from ....color import yellow, blue, cyan, pink from ....addr import MMGenID +from ....obj import Int class TxInfo(TxInfo): txinfo_hdr_fs = '{hdr}\n ID={i} ({a} {c}) Sig={s} Locktime={l}\n' @@ -56,7 +57,7 @@ class TxInfo(TxInfo): m = pink(tx.swap_memo) if tx.is_swap else None, c = tx.proto.dcoin if len(tx.outputs) else '', g = yellow(tx.pretty_fmt_fee(t['gasPrice'].to_unit('Gwei'))), - G = yellow(tx.pretty_fmt_fee(t['startGas'].to_unit('Kwei'))), + G = Int(t['startGas']).hl(), f_mmid = mmid_disp(tx.inputs[0]), t_mmid = mmid_disp(tx.outputs[0]) if tx.outputs and not tx.is_swap else '') + '\n\n' diff --git a/mmgen/proto/eth/tx/new.py b/mmgen/proto/eth/tx/new.py index 3b129132..dd56610b 100755 --- a/mmgen/proto/eth/tx/new.py +++ b/mmgen/proto/eth/tx/new.py @@ -35,10 +35,7 @@ class New(Base, TxBase.New): super().__init__(*args, **kwargs) - if self.cfg.gas: - self.gas = self.proto.coin_amt(int(self.cfg.gas), from_unit='wei') - else: - self.gas = self.proto.coin_amt(self.dfl_gas, from_unit='wei') + self.gas = int(self.cfg.gas or self.dfl_gas) if self.cfg.contract_data: m = "'--contract-data' option may not be used with token transaction" @@ -82,12 +79,12 @@ class New(Base, TxBase.New): def set_gas_with_data(self, data): if not self.is_token: - self.gas = self.proto.coin_amt(self.dfl_gas + self.byte_cost * len(data), from_unit='wei') + self.gas = self.dfl_gas + self.byte_cost * len(data) # one-shot method def adj_gas_with_extra_data_len(self, extra_data_len): if not (self.is_token or hasattr(self, '_gas_adjusted')): - self.gas += self.proto.coin_amt(self.byte_cost * extra_data_len, from_unit='wei') + self.gas += self.byte_cost * extra_data_len self._gas_adjusted = True async def process_cmdline_args(self, cmd_args, ad_f, ad_w): @@ -154,7 +151,7 @@ class New(Base, TxBase.New): # given rel fee and units, return absolute fee using self.gas def fee_rel2abs(self, tx_size, amt_in_units, unit): - return self.proto.coin_amt(int(amt_in_units * self.gas.toWei()), from_unit=unit) + return self.proto.coin_amt(int(amt_in_units * self.gas), from_unit=unit) # given fee estimate (gas price) in wei, return absolute fee, adjusting by self.cfg.fee_adjust def fee_est2abs(self, net_fee): diff --git a/mmgen/proto/eth/tx/signed.py b/mmgen/proto/eth/tx/signed.py index eef93bc8..2903f280 100755 --- a/mmgen/proto/eth/tx/signed.py +++ b/mmgen/proto/eth/tx/signed.py @@ -35,7 +35,7 @@ class Signed(Completed, TxBase.Signed): 'to': CoinAddr(self.proto, d['to']) if d['to'] else None, 'amt': self.proto.coin_amt(d['value'], from_unit='wei'), 'gasPrice': self.proto.coin_amt(d['gasprice'], from_unit='wei'), - 'startGas': self.proto.coin_amt(d['startgas'], from_unit='wei'), + 'startGas': d['startgas'], 'nonce': ETHNonce(d['nonce']), 'data': HexStr(d['data']) } if o['data'] and not o['to']: # token- or contract-creating transaction diff --git a/mmgen/proto/eth/tx/unsigned.py b/mmgen/proto/eth/tx/unsigned.py index 2d38f743..708876fd 100755 --- a/mmgen/proto/eth/tx/unsigned.py +++ b/mmgen/proto/eth/tx/unsigned.py @@ -32,7 +32,9 @@ class Unsigned(Completed, TxBase.Unsigned): 'to': CoinAddr(self.proto, d['to']) if d['to'] else None, 'amt': self.proto.coin_amt(d['amt']), 'gasPrice': self.proto.coin_amt(d['gasPrice']), - 'startGas': self.proto.coin_amt(d['startGas']), + 'startGas': ( + self.proto.coin_amt(d['startGas']).toWei() if '.' in d['startGas'] # for backward compat + else int(d['startGas'])), 'nonce': ETHNonce(d['nonce']), 'chainId': None if d['chainId'] == 'None' else Int(d['chainId']), 'data': HexStr(d['data'])} @@ -43,7 +45,7 @@ class Unsigned(Completed, TxBase.Unsigned): async def do_sign(self, o, wif): o_conv = { 'to': bytes.fromhex(o['to'] or ''), - 'startgas': o['startGas'].toWei(), + 'startgas': o['startGas'], 'gasprice': o['gasPrice'].toWei(), 'value': o['amt'].toWei() if o['amt'] else 0, 'nonce': o['nonce'], @@ -129,7 +131,7 @@ class TokenUnsigned(TokenCompleted, Unsigned): self.swap_memo.encode(), o['expiry']) tx_in = c.make_tx_in( - gas = self.gas * (7.8 if self.cfg.test_suite else 2), + gas = self.gas * (7 if self.cfg.test_suite else 2), gasPrice = o['gasPrice'], nonce = o['nonce'] + 1, data = cdata) diff --git a/test/cmdtest_d/ethdev.py b/test/cmdtest_d/ethdev.py index 697c813d..1243dfd6 100755 --- a/test/cmdtest_d/ethdev.py +++ b/test/cmdtest_d/ethdev.py @@ -340,7 +340,7 @@ class CmdTestEthdevMethods: to_addr = usr_addrs[i], amt = amt, key = dfl_devkey, - gas = self.proto.coin_amt(120000, from_unit='wei'), + gas = 120000, gasPrice = self.proto.coin_amt(8, from_unit='Gwei')) rpc = await self.rpc for n in range(50): # long delay for txbump diff --git a/test/cmdtest_d/ethswap.py b/test/cmdtest_d/ethswap.py index 7a48e76d..0211ca34 100755 --- a/test/cmdtest_d/ethswap.py +++ b/test/cmdtest_d/ethswap.py @@ -83,14 +83,15 @@ class CmdTestEthSwapMethods: def token_fund_user(self): return self._token_transfer_ops( op = 'fund_user', - mm_idxs = [1], + mm_idxs = [1, 2, 12], + token_addr = 'token_addr1', amt = self.token_fund_amt) def token_addrgen(self): - return self._token_addrgen(mm_idxs=[1], naddrs=5) + return self._token_addrgen(mm_idxs=[1], naddrs=12) def token_addrimport(self): - return self._token_addrimport('token_addr1', '1-5', expect='5/5') + return self._token_addrimport('token_addr1', '1-12', expect='12/12') def token_addrimport_inbound(self): token_addr = self.read_from_tmpfile('token_addr1').strip() @@ -176,9 +177,10 @@ class CmdTestEthSwap(CmdTestSwapMethods, CmdTestRegtest): ), 'eth_fund': ( 'funding the ETH tracking wallet', - ('eth_fund_mmgen_addr1', ''), - ('eth_fund_mmgen_addr2', ''), - ('eth_bal1', ''), + ('eth_fund_mmgen_addr1', ''), + ('eth_fund_mmgen_addr1b', ''), + ('eth_fund_mmgen_addr2', ''), + ('eth_bal1', ''), ), 'token_init': ( 'deploying tokens and initializing the ETH token tracking wallet', @@ -227,7 +229,8 @@ class CmdTestEthSwap(CmdTestSwapMethods, CmdTestRegtest): 'eth_token_swap': ( 'swap operations (ETH -> ERC20, ERC20 -> BTC, ERC20 -> ETH)', # ETH -> MM1 - ('eth_swaptxcreate3', ''), + ('eth_swaptxcreate3a', ''), + ('eth_swaptxcreate3b', ''), ('eth_swaptxsign3', ''), ('eth_swaptxsend3', ''), ('eth_swaptxmemo3', ''), @@ -240,7 +243,8 @@ class CmdTestEthSwap(CmdTestSwapMethods, CmdTestRegtest): ('eth_swaptxreceipt4', ''), ('eth_token_bal2', ''), # MM1 -> ETH - ('eth_swaptxcreate5', ''), + ('eth_swaptxcreate5a', ''), + ('eth_swaptxcreate5b', ''), ('eth_swaptxsign5', ''), ('eth_etherscan_server_start', ''), ('eth_swaptxsend5_test', ''), @@ -343,6 +347,7 @@ class CmdTestEthSwapEth(CmdTestEthSwapMethods, CmdTestSwapMethods, CmdTestEthdev cmd_group_in = CmdTestEthdev.cmd_group_in + ( # eth_fund: ('fund_mmgen_addr1', 'funding user address :1)'), + ('fund_mmgen_addr1b', 'funding user address :3)'), ('fund_mmgen_addr2', 'funding user address :11)'), ('bal1', 'the ETH balance'), # eth_swap: @@ -367,7 +372,8 @@ class CmdTestEthSwapEth(CmdTestEthSwapMethods, CmdTestSwapMethods, CmdTestEthdev # eth_token_swap: # ETH -> MM1 - ('swaptxcreate3', 'creating an ETH->MM1 swap transaction'), + ('swaptxcreate3a', 'creating an ETH->MM1 swap transaction'), + ('swaptxcreate3b', 'creating an ETH->MM1 swap transaction (specific address)'), ('swaptxsign3', 'signing the transaction'), ('swaptxsend3', 'sending the transaction'), ('swaptxmemo3', 'the memo of the sent transaction'), @@ -382,7 +388,8 @@ class CmdTestEthSwapEth(CmdTestEthSwapMethods, CmdTestSwapMethods, CmdTestEthdev ('token_bal2', 'the token balance'), # MM1 -> ETH - ('swaptxcreate5', 'creating an MM1->ETH swap transaction'), + ('swaptxcreate5a', 'creating an MM1->ETH swap transaction'), + ('swaptxcreate5b', 'creating an MM1->ETH swap transaction (specific address)'), ('swaptxsign5', 'signing the transaction'), ('etherscan_server_start', 'starting the Etherscan server'), ('swaptxsend5_test', 'testing the transaction via Etherscan'), @@ -392,6 +399,9 @@ class CmdTestEthSwapEth(CmdTestEthSwapMethods, CmdTestSwapMethods, CmdTestEthdev ('etherscan_server_stop', 'stopping the Etherscan server'), ) + def fund_mmgen_addr1b(self): + return self._fund_mmgen_addr(arg=f'{dfl_sid}:E:3,0.001') + def swaptxcreate1(self): t = self._swaptxcreate(['ETH', '8.765', 'BTC']) t.expect('OK? (Y/n): ', 'y') @@ -404,7 +414,13 @@ class CmdTestEthSwapEth(CmdTestEthSwapMethods, CmdTestSwapMethods, CmdTestEthdev add_opts = ['--trade-limit=3%']), expect = ':2019e4/1/0') - def swaptxcreate3(self): + def swaptxcreate3a(self): + t = self._swaptxcreate(['ETH', '0.7654321', 'ETH.MM1']) + t.expect(f'{dfl_sid}:E:4') # check that correct unused address was found + t.expect('(Y/n): ', 'y') + return self._swaptxcreate_ui_common(t) + + def swaptxcreate3b(self): t = self._swaptxcreate(['ETH', '8.765', 'ETH.MM1', f'{dfl_sid}:E:5']) return self._swaptxcreate_ui_common(t) @@ -424,7 +440,13 @@ class CmdTestEthSwapEth(CmdTestEthSwapMethods, CmdTestSwapMethods, CmdTestEthdev t = self._swaptxcreate(['ETH.MM1', '87.654321', 'BTC', f'{dfl_sid}:C:2']) return self._swaptxcreate_ui_common(t) - def swaptxcreate5(self): + def swaptxcreate5a(self): + t = self._swaptxcreate(['ETH.MM1', '98.7654321', 'ETH']) + t.expect(f'{dfl_sid}:E:13') # check that correct unused address was found + t.expect('(Y/n): ', 'y') + return self._swaptxcreate_ui_common(t) + + def swaptxcreate5b(self): t = self._swaptxcreate(['ETH.MM1', '98.7654321', 'ETH', f'{dfl_sid}:E:12']) return self._swaptxcreate_ui_common(t)