Browse Source

swaptxcreate: add `--router-gas` option; proto.eth.tx: new `total_gas` attr

The MMGen Project 7 months ago
parent
commit
aaaccc6bad

+ 2 - 0
mmgen/main_txcreate.py

@@ -59,6 +59,8 @@ opts_data = {
 			+                        calculated using network fee estimation.
 			et -g, --gas=N           Specify gas limit (integer)
 			-s -g, --gas=N           Specify gas limit for Ethereum (integer)
+			-s -G, --router-gas=N    Specify gas limit for Ethereum router contract
+			+                        (integer). Applicable only for swaps from token assets
 			-- -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

+ 2 - 0
mmgen/main_txdo.py

@@ -59,6 +59,8 @@ opts_data = {
 			+                         calculated using network fee estimation.
 			et -g, --gas=N            Specify gas limit (integer)
 			-s -g, --gas=N            Specify gas limit for Ethereum (integer)
+			-s -G, --router-gas=N     Specify gas limit for Ethereum router contract
+			+                         (integer). Applicable only for swaps from token assets
 			-- -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)

+ 4 - 3
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), from_unit='wei')
+		return self.proto.coin_amt(int(abs_fee.toWei() // self.total_gas), from_unit='wei')
 
-	# given rel fee (gasPrice) in wei, return absolute fee using self.gas (Ethereum-only method)
+	# given rel fee (gasPrice) in wei, return absolute fee using self.total_gas
 	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, from_unit='wei')
+		return self.proto.coin_amt(rel_fee * self.total_gas, from_unit='wei')
 
 	def is_replaceable(self):
 		return True
@@ -112,6 +112,7 @@ class Base(TxBase):
 
 class TokenBase(Base):
 	dfl_gas = 75000
+	dfl_router_gas = 150000
 	contract_desc = 'token contract'
 
 	def check_serialized_integrity(self):

+ 8 - 0
mmgen/proto/eth/tx/completed.py

@@ -25,6 +25,10 @@ class Completed(Base, TxBase.Completed):
 	def send_amt(self):
 		return self.outputs[0].amt if self.outputs else self.proto.coin_amt('0')
 
+	@property
+	def total_gas(self):
+		return self.txobj['startGas']
+
 	@property
 	def fee(self):
 		return self.fee_gasPrice2abs(self.txobj['gasPrice'].toWei())
@@ -53,3 +57,7 @@ class TokenCompleted(TokenBase, Completed):
 	@property
 	def change(self):
 		return self.sum_inputs() - self.send_amt
+
+	@property
+	def total_gas(self):
+		return self.txobj['startGas'] + (self.txobj['router_gas'] if self.is_swap else 0)

+ 4 - 3
mmgen/proto/eth/tx/info.py

@@ -14,7 +14,7 @@ proto.eth.tx.info: Ethereum transaction info class
 
 from ....tx.info import TxInfo
 from ....util import fmt, pp_fmt
-from ....color import yellow, blue, cyan, pink
+from ....color import red, yellow, blue, cyan, pink
 from ....addr import MMGenID
 from ....obj import Int
 
@@ -38,7 +38,7 @@ class TxInfo(TxInfo):
 			{toaddr}   {t}{t_mmid}{tvault}
 			Amount:    {a} {c}
 			Gas price: {g} Gwei
-			Gas limit: {G}
+			Gas limit: {G}{G_dec}
 			Nonce:     {n}
 			Data:      {d}
 		""".strip().replace('\t', '') + ('\nMemo:      {m}' if tx.is_swap else '')
@@ -57,7 +57,8 @@ 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      = Int(t['startGas']).hl(),
+			G      = Int(tx.total_gas).hl(),
+			G_dec  = red(f" ({t['startGas']} + {t['router_gas']})") if tokenswap else '',
 			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'
 

+ 14 - 2
mmgen/proto/eth/tx/new.py

@@ -37,6 +37,9 @@ class New(Base, TxBase.New):
 
 		self.gas = int(self.cfg.gas or self.dfl_gas)
 
+		if self.is_token and self.is_swap:
+			self.router_gas = int(self.cfg.router_gas or self.dfl_router_gas)
+
 		if self.cfg.contract_data:
 			m = "'--contract-data' option may not be used with token transaction"
 			assert 'Token' not in self.name, m
@@ -149,9 +152,13 @@ class New(Base, TxBase.New):
 		if not self.disable_fee_check:
 			assert self.usr_fee <= self.proto.max_tx_fee
 
-	# given rel fee and units, return absolute fee using self.gas
+	@property
+	def total_gas(self):
+		return self.gas
+
+	# given rel fee and units, return absolute fee using self.total_gas
 	def fee_rel2abs(self, tx_size, amt_in_units, unit):
-		return self.proto.coin_amt(int(amt_in_units * self.gas), from_unit=unit)
+		return self.proto.coin_amt(int(amt_in_units * self.total_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):
@@ -207,6 +214,10 @@ class TokenNew(TokenBase, New):
 	desc = 'transaction'
 	fee_is_approximate = True
 
+	@property
+	def total_gas(self):
+		return self.gas + (self.router_gas if self.is_swap else 0)
+
 	async def make_txobj(self): # called by create_serialized()
 		await super().make_txobj()
 		t = Token(self.cfg, self.proto, self.twctl.token, decimals=self.twctl.decimals)
@@ -216,6 +227,7 @@ class TokenNew(TokenBase, New):
 		o['token_to'] = o['to']
 		if self.is_swap:
 			o['expiry'] = self.quote_data.data['expiry']
+			o['router_gas'] = self.router_gas
 
 	def update_change_output(self, funds_left):
 		if self.outputs[0].is_chg:

+ 6 - 0
mmgen/proto/eth/tx/online.py

@@ -79,6 +79,12 @@ class TokenOnlineSigned(TokenSigned, OnlineSigned):
 		t = Token(self.cfg, self.proto, o['token_addr'], decimals=o['decimals'])
 		o['amt'] = t.transferdata2amt(o['data'])
 		o['token_to'] = t.transferdata2sendaddr(o['data'])
+		if self.is_swap:
+			from ..pyethereum.transactions import Transaction
+			from .. import rlp
+			etx = rlp.decode(bytes.fromhex(self.serialized2), Transaction)
+			d = etx.to_dict()
+			o['router_gas'] = d['startgas']
 
 class Sent(TxBase.Sent, OnlineSigned):
 	pass

+ 0 - 1
mmgen/proto/eth/tx/signed.py

@@ -44,7 +44,6 @@ class Signed(Completed, TxBase.Signed):
 			self.disable_fee_check = True
 		txid = CoinTxID(etx.hash.hex())
 		assert txid == self.coin_txid, "txid in tx.serialized doesn't match value in MMGen transaction file"
-		self.gas = o['startGas']
 		self.txobj = o
 		return d # 'token_addr', 'decimals' required by Token subclass
 

+ 3 - 3
mmgen/proto/eth/tx/unsigned.py

@@ -38,7 +38,6 @@ class Unsigned(Completed, TxBase.Unsigned):
 			'nonce':    ETHNonce(d['nonce']),
 			'chainId':  None if d['chainId'] == 'None' else Int(d['chainId']),
 			'data':     HexStr(d['data'])}
-		self.gas = o['startGas']
 		self.txobj = o
 		return d # 'token_addr', 'decimals' required by Token subclass
 
@@ -114,11 +113,12 @@ class TokenUnsigned(TokenCompleted, Unsigned):
 		o['token_to'] = o['to']
 		if self.is_swap:
 			o['expiry'] = Int(d['expiry'])
+			o['router_gas'] = Int(d['router_gas'])
 
 	async def do_sign(self, o, wif):
 		t = Token(self.cfg, self.proto, o['token_addr'], decimals=o['decimals'])
 		tdata = t.create_transfer_data(o['to'], o['amt'], op=self.token_op)
-		tx_in = t.make_tx_in(gas=self.gas, gasPrice=o['gasPrice'], nonce=o['nonce'], data=tdata)
+		tx_in = t.make_tx_in(gas=o['startGas'], gasPrice=o['gasPrice'], nonce=o['nonce'], data=tdata)
 		res = await t.txsign(tx_in, wif, o['from'], chain_id=o['chainId'])
 		self.serialized = res.txhex
 		self.coin_txid = res.txid
@@ -131,7 +131,7 @@ class TokenUnsigned(TokenCompleted, Unsigned):
 				self.swap_memo.encode(),
 				o['expiry'])
 			tx_in = c.make_tx_in(
-				gas = self.gas * (7 if self.cfg.test_suite else 2),
+				gas = o['router_gas'],
 				gasPrice = o['gasPrice'],
 				nonce = o['nonce'] + 1,
 				data = cdata)

+ 3 - 1
test/cmdtest_d/ethswap.py

@@ -447,7 +447,9 @@ class CmdTestEthSwapEth(CmdTestEthSwapMethods, CmdTestSwapMethods, CmdTestEthdev
 		return self._swaptxcreate_ui_common(t)
 
 	def swaptxcreate5b(self):
-		t = self._swaptxcreate(['ETH.MM1', '98.7654321', 'ETH', f'{dfl_sid}:E:12'])
+		t = self._swaptxcreate(
+			['ETH.MM1', '98.7654321', 'ETH', f'{dfl_sid}:E:12'],
+			add_opts = ['--gas=58000', '--router-gas=500000'])
 		return self._swaptxcreate_ui_common(t)
 
 	def swaptxsign1(self):

+ 3 - 0
test/overlay/fakemods/mmgen/proto/eth/tx/base.py

@@ -0,0 +1,3 @@
+from .base_orig import *
+
+TokenBase.dfl_router_gas = 525000