Skip to content

Commit adb410e

Browse files
committed
Improve the logging of test_multipayout_ERC20_web3_tester
1 parent 1128778 commit adb410e

File tree

5 files changed

+109
-21
lines changed

5 files changed

+109
-21
lines changed

README.org

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1994,6 +1994,22 @@ sets of 20 words is difficult, error-prone and time consuming.
19941994
address: the transaction must contain specific information about the source UTXOs (Unspent
19951995
TransaXion Outputs) being spent, which is of course unavailable in advance.
19961996

1997+
** Testing =MultiPayoutERC20=
1998+
1999+
Put some TGOR (Test Goerli Ethereeum) tokens into the "zoo zoo ... wrong" Ethereum account on the
2000+
Goerli testnet. This is (of course) a well-known account, and the funds will disappear pretty
2001+
quickly, but should give you time to run the tests successfully.
2002+
2003+
You can mint TGOR for free, at:
2004+
: https://faucet.quicknode.com/ethereum/goerli
2005+
: https://goerlifaucet.com/
2006+
2007+
Transfer about 0.1 TGOR to the "zoo zoo ... wrong" test account:
2008+
: 0x667AcC3Fc27A8EbcDA66E7E01ceCA179d407ce00
2009+
2010+
Then, run:
2011+
: make test # or: make unit-test_multipayout_ERC20_web3_tester
2012+
19972013
* Building & Installing
19982014

19992015
The =python-slip39= project is tested under both homebrew:

slip39/invoice/ethereum.py

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -530,7 +530,7 @@ def GAS_USD( self ):
530530
531531
>>> gas, spend = 100000, 1.50 # USD$
532532
>>> ETH = Etherscan( "Ethereum )
533-
>>> maxFeePerGas = spend / ETH.GAS_USD ETH.GAS_WEI
533+
>>> maxFeePerGas = spend / ETH.GAS_USD ETH.GAS_WEI (TODO)
534534
535535
"""
536536
return self.ETH_USD * self.GAS_GWEI / self.ETH_GWEI
@@ -860,7 +860,12 @@ def _abi_key( self, name, path=None ):
860860
def _update( self ):
861861
pass
862862

863-
def _gas_price( self, gas, fail_fast=None, max_factor=None ):
863+
def _gas_price(
864+
self,
865+
gas, # Estimated Gas required
866+
fail_fast = None, # If we predict failure due to gas * price, fail now
867+
max_factor = None # How much can Gas price increase before failing Tx?
868+
):
864869
"""Establish maxPriorityFeePerGas Gas fees for a transaction, optionally w/ a computed
865870
maxFeePerGas for the given estimated (max) amount of Gas required by the transaction.
866871
@@ -881,7 +886,9 @@ def _gas_price( self, gas, fail_fast=None, max_factor=None ):
881886
"""
882887

883888
# Find out what the network thinks the required Gas fees need to be. This could be a
884-
# Testnet like Goerli, with abnormally low Gas prices.
889+
# Testnet like Goerli, with abnormally low Gas prices. But, for a real Ethereum Mainnet
890+
# transaction, it will be an estimate of the latest block's gas price estimates for the next
891+
# block.
885892
latest = self._w3.eth.get_block( 'latest' )
886893
base_fee = latest['baseFeePerGas']
887894
max_priority_fee = self._w3.eth.max_priority_fee

slip39/invoice/multipayout_test.py

Lines changed: 81 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -426,30 +426,88 @@ def test_multipayout_ERC20_web3_tester( testnet, provider, chain_id, src, src_pr
426426
latest = w3.eth.get_block('latest')
427427
print( f"{testnet:10}: Web3 Latest block: {json.dumps( latest, indent=4, default=str )}" )
428428

429+
430+
def gas_pricing(
431+
of = f"{testnet:10}: Web3 Tester",
432+
gas = None,
433+
**kwds,
434+
):
435+
"""Estimate what the gas will cost in USD$, given the transaction's parameters (None if unknown).
436+
437+
Defaults to the gas of a standard ETH transfer.
438+
439+
If an old-style fixed gasPrice is given, use that.
440+
441+
Otherwise, looks for EIP-1559 maxPriorityFeePerGas and maxFeePerGas.
442+
443+
"""
444+
if gas is None:
445+
gas = 21000
446+
if 'gasPrice' in kwds:
447+
# Old fixed gas pricing
448+
est_gas_wei = int(kwds['gasPrice'])
449+
max_cost_gwei = int(gas) * est_gas_wei / ETH.GWEI_WEI
450+
max_cost_eth = max_cost_gwei / ETH.ETH_GWEI
451+
max_cost_usd = max_cost_eth * ETH.ETH_USD
452+
print( "{} Gas Price at USD${:9,.2f}/ETH: fee wei/gas fixed {:.4f}: cost per {:,d} Gas: {:,.4f} gwei == {:,.6f} ETH == USD${:10,.6f} ({})".format( # noqa: E501
453+
of,
454+
ETH.ETH_USD,
455+
est_gas_wei,
456+
int(gas),
457+
max_cost_gwei,
458+
max_cost_eth,
459+
max_cost_usd,
460+
ETH.STATUS or 'estimated',
461+
))
462+
elif 'maxFeePerGas' in kwds or 'baseFeePerGas' in kwds:
463+
# New EIP-1599 gas pricing
464+
max_gas_wei = int(kwds.get( 'maxFeePerGas', kwds.get( 'baseFeePerGas' )))
465+
max_cost_gwei = int(gas) * max_gas_wei / ETH.GWEI_WEI
466+
max_cost_eth = max_cost_gwei / ETH.ETH_GWEI
467+
max_cost_usd = max_cost_eth * ETH.ETH_USD
468+
print( "{} Gas Price at USD${:9,.2f}/ETH: fee gwei/gas max. {:.4f}: cost per {:,d} Gas: {:,.4f} gwei == {:,.6f} ETH == USD${:10,.6f} ({})".format( # noqa: E501
469+
of,
470+
ETH.ETH_USD,
471+
max_gas_wei,
472+
int(gas),
473+
max_cost_gwei,
474+
max_cost_eth,
475+
max_cost_usd,
476+
ETH.STATUS or 'estimated',
477+
))
478+
else:
479+
max_cost_usd = None # Unknown
480+
print( "{} Gas Price at USD${:9,.2f}/ETH: fee gwei/gas unknown from {} ({})".format( # noqa: E501
481+
of,
482+
ETH.ETH_USD,
483+
', '.join( f"{k} == {v!r}" for k,v in kwds.items() ),
484+
ETH.STATUS or 'estimated',
485+
))
486+
487+
return max_cost_usd
488+
429489
# Ask the connected Ethereum testnet what it thinks gas prices are. This will
430490
# (usually) be a Testnet, where gas prices are artificially low vs. the real Ethereum
431491
# network.
432492
max_priority_fee = w3.eth.max_priority_fee
433493
base_fee = latest['baseFeePerGas']
434-
est_gas_wei = base_fee + max_priority_fee
435-
max_gas_wei = base_fee * 2 + max_priority_fee # fail transaction if gas prices go wild
436-
print( "{:10}: Web3 Tester Gas Price at USD${:9,.2f}/ETH: fee gwei/gas est. base (latest): {:9,.2f} priority: {:9,.2f}; cost per {:,d} Gas: {:,.4f} gwei == USD${:9,.2f}; max: USD${:9,.2f} ({})".format( # noqa: E501
437-
testnet, ETH.ETH_USD,
438-
base_fee / ETH.GWEI_WEI, max_priority_fee / ETH.GWEI_WEI,
439-
100000,
440-
100000 * est_gas_wei / ETH.GWEI_WEI,
441-
100000 * est_gas_wei * ETH.ETH_USD / ETH.ETH_WEI,
442-
100000 * max_gas_wei * ETH.ETH_USD / ETH.ETH_WEI, ETH.STATUS or 'estimated',
443-
))
494+
gas = 21000
495+
496+
gas_pricing(
497+
gas = gas,
498+
maxPriorityFeePerGas = max_priority_fee,
499+
baseFeePerGas = base_fee,
500+
)
444501

502+
max_gas_wei = base_fee * 2 + max_priority_fee
445503
gas_price_testnet = dict(
446504
maxFeePerGas = max_gas_wei, # If we want to fail if base fee + priority fee exceeds some limits
447505
maxPriorityFeePerGas = max_priority_fee,
448506
)
449-
print( f"{testnet:10}: Gas Pricing EIP-1559 for max $1.50 per 21,000 Gas transaction: {json.dumps( gas_price_testnet )}" )
507+
print( f"{testnet:10}: Gas Pricing EIP-1559 for max $1.50 per 21,000 Gas transaction (using Testnet Gas pricing): {json.dumps( gas_price_testnet )}" )
450508

451509
# Let's say we're willing to pay up to $1.50 for a standard Ethereum transfer costing 21,000 gas
452-
max_usd_per_gas = 1.50 / 21000
510+
max_usd_per_gas = 1.50 / gas
453511

454512
print( f"{testnet:10}: Web3 Tester Accounts, start of test:" )
455513
for a in ( src, ) + tuple( destination ):
@@ -503,14 +561,21 @@ def test_multipayout_ERC20_web3_tester( testnet, provider, chain_id, src, src_pr
503561

504562
MultiPayoutERC20_contract = w3.eth.contract( abi=mp_ERC20_abi, bytecode=mp_ERC20_bytecode )
505563

506-
gas = 3000000
564+
gas = 1400000 # 1388019 Gas is actual cost, as of 20230910
507565
spend = gas * max_usd_per_gas
508566
gas_price = ETH.maxPriorityFeePerGas( spend=spend, gas=gas ) if ETH.UPDATED else gas_price_testnet
509-
mc_cons_hash = MultiPayoutERC20_contract.constructor( payees, tokens ).transact({
567+
mc_cons_tx = {
510568
'from': src,
511569
'nonce': w3.eth.get_transaction_count( src ),
512570
'gas': gas,
513-
} | gas_price )
571+
} | gas_price
572+
573+
gas_pricing( f"{testnet:10}: Web3 Tester Construct MultiPayoutERC20", **mc_cons_tx)
574+
575+
print( f"{testnet:10}: Web3 Tester Construct MultiPayoutERC20 Tx (using {'Mainnet' if ETH.UPDATED else 'Testnet'} Gas pricing): {json.dumps(mc_cons_tx, indent=4)}" )
576+
# Let's see what this would cost, using the estimated gas:
577+
578+
mc_cons_hash = MultiPayoutERC20_contract.constructor( payees, tokens ).transact( mc_cons_tx )
514579
print( f"{testnet:10}: Web3 Tester Construct MultiPayoutERC20 hash: {mc_cons_hash.hex()}" )
515580
mc_cons = w3.eth.get_transaction( mc_cons_hash )
516581
print( f"{testnet:10}: Web3 Tester Construct MultiPayoutERC20 transaction: {json.dumps( mc_cons, indent=4, default=str )}" )
@@ -534,7 +599,7 @@ def tx_gas_cost( tx, receipt ):
534599
return base_fee + prio_fee
535600

536601
gas_cost = tx_gas_cost( mc_cons, mc_cons_receipt )
537-
print( "{:10}: Web3 Tester Construct MultiPayoutERC20 Gas Used: {} == {:7.4f}Gwei == USD${:9,.2f} ({}): ${:7.6f}/byte".format(
602+
print( "{:10}: Web3 Tester Construct MultiPayoutERC20 Gas Used: {} == {:7.4f}Gwei == USD${:10,.6f} ({}): ${:7.6f}/byte".format(
538603
testnet,
539604
mc_cons_receipt.gasUsed,
540605
mc_cons_receipt.gasUsed * gas_cost / ETH.GWEI_WEI,

slip39/version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
__version_info__ = ( 10, 2, 1 )
1+
__version_info__ = ( 10, 3, 0 )
22
__version__ = '.'.join( map( str, __version_info__ ))

0 commit comments

Comments
 (0)