Skip to content

WIP: No way to specify block number for eth_estimateGas #1046

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 9 additions & 3 deletions docs/web3.eth.rst
Original file line number Diff line number Diff line change
Expand Up @@ -632,22 +632,28 @@ The following methods are available on the ``web3.eth`` namespace.
In most cases it is better to make contract function call through the :py:class:`web3.contract.Contract` interface.


.. py:method:: Eth.estimateGas(transaction)
.. py:method:: Eth.estimateGas(transaction, block_identifier=None)

* Delegates to ``eth_estimateGas`` RPC Method

Executes the given transaction locally without creating a new transaction
on the blockchain. Returns amount of gas consumed by execution which can
be used as a gas estimate.

The ``transaction`` parameter is handled in the same manner as the
:meth:`~web3.eth.Eth.sendTransaction()` method.
The ``transaction`` and ``block_identifier`` parameters are handled in the
same manner as the :meth:`~web3.eth.call()` method.

.. code-block:: python

>>> web3.eth.estimateGas({'to': '0xd3cda913deb6f67967b99d67acdfa1712c293601', 'from': web3.eth.coinbase, 'value': 12345})
21000

.. note::
The parameter ``block_identifier`` is not enabled in geth nodes,
hence passing a value of ``block_identifier`` when connected to a geth
nodes would result in an error like: ``ValueError: {'code': -32602, 'message': 'too many arguments, want at most 1'}``


.. py:method:: Eth.generateGasPrice(transaction_params=None)

Uses the selected gas price strategy to calculate a gas price. This method
Expand Down
8 changes: 8 additions & 0 deletions tests/integration/go_ethereum/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,14 @@ def test_eth_modifyTransaction(self, web3, unlocked_account):
pytest.xfail('Needs ability to efficiently control mining')
super().test_eth_modifyTransaction(web3, unlocked_account)

def test_eth_estimateGas_with_block(self,
web3,
unlocked_account_dual_type):
pytest.xfail('Block identifier has not been implemented in geth')
super().test_eth_estimateGas_with_block(
web3, unlocked_account_dual_type
)


class GoEthereumVersionModuleTest(VersionModuleTest):
pass
Expand Down
8 changes: 8 additions & 0 deletions tests/integration/test_ethereum_tester.py
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,14 @@ def test_eth_getStorageAt(self, web3, emitter_contract_address):
pytest.xfail('json-rpc method is not implemented on eth-tester')
super().test_eth_getStorageAt(web3, emitter_contract_address)

def test_eth_estimateGas_with_block(self,
web3,
unlocked_account_dual_type):
pytest.xfail('Block identifier has not been implemented in eth-tester')
super().test_eth_estimateGas_with_block(
web3, unlocked_account_dual_type
)


class TestEthereumTesterVersionModule(VersionModuleTest):
pass
Expand Down
5 changes: 5 additions & 0 deletions web3/_utils/abi.py
Original file line number Diff line number Diff line change
Expand Up @@ -356,6 +356,11 @@ def is_string_type(abi_type):
return abi_type == 'string'


@curry
def is_length(target_length, value):
return len(value) == target_length


def size_of_type(abi_type):
"""
Returns size in bits of abi_type
Expand Down
11 changes: 11 additions & 0 deletions web3/_utils/module_testing/eth_module.py
Original file line number Diff line number Diff line change
Expand Up @@ -469,6 +469,17 @@ def test_eth_estimateGas(self, web3, unlocked_account_dual_type):
assert is_integer(gas_estimate)
assert gas_estimate > 0

def test_eth_estimateGas_with_block(self,
web3,
unlocked_account_dual_type):
gas_estimate = web3.eth.estimateGas({
'from': unlocked_account_dual_type,
'to': unlocked_account_dual_type,
'value': 1,
}, 'latest')
assert is_integer(gas_estimate)
assert gas_estimate > 0

def test_eth_getBlockByHash(self, web3, empty_block):
block = web3.eth.getBlock(empty_block['hash'])
assert block['hash'] == empty_block['hash']
Expand Down
9 changes: 7 additions & 2 deletions web3/eth.py
Original file line number Diff line number Diff line change
Expand Up @@ -294,14 +294,19 @@ def call(self, transaction, block_identifier=None):
[transaction, block_identifier],
)

def estimateGas(self, transaction):
def estimateGas(self, transaction, block_identifier=None):
# TODO: move to middleware
if 'from' not in transaction and is_checksum_address(self.defaultAccount):
transaction = assoc(transaction, 'from', self.defaultAccount)

if block_identifier is None:
params = [transaction]
else:
params = [transaction, block_identifier]

return self.web3.manager.request_blocking(
"eth_estimateGas",
[transaction],
params,
)

def filter(self, filter_params=None, filter_id=None):
Expand Down
14 changes: 13 additions & 1 deletion web3/middleware/pythonic.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@
HexBytes,
)

from web3._utils.abi import (
is_length,
)
from web3._utils.encoding import (
hexstr_if_str,
to_hex,
Expand Down Expand Up @@ -244,6 +247,12 @@ def to_hexbytes(num_bytes, val, variable_length=False):
apply_formatters_to_dict(TRANSACTION_PARAM_FORMATTERS),
)

estimate_gas_without_block_id = apply_formatter_at_index(transaction_param_formatter, 0)
estimate_gas_with_block_id = combine_argument_formatters(
transaction_param_formatter,
block_number_formatter,
)


pythonic_middleware = construct_formatting_middleware(
request_formatters={
Expand Down Expand Up @@ -273,7 +282,10 @@ def to_hexbytes(num_bytes, val, variable_length=False):
transaction_param_formatter,
block_number_formatter,
),
'eth_estimateGas': apply_formatter_at_index(transaction_param_formatter, 0),
'eth_estimateGas': apply_one_of_formatters((
(estimate_gas_without_block_id, is_length(1)),
(estimate_gas_with_block_id, is_length(2)),
)),
'eth_sendTransaction': apply_formatter_at_index(transaction_param_formatter, 0),
# personal
'personal_importRawKey': apply_formatter_at_index(
Expand Down