Skip to content
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
44 changes: 39 additions & 5 deletions docs/web3.eth.rst
Original file line number Diff line number Diff line change
Expand Up @@ -729,13 +729,20 @@ The following methods are available on the ``web3.eth`` namespace.
The ``transaction`` parameter should be a dictionary with the following fields.

* ``from``: ``bytes or text``, checksum address or ENS name - (optional, default:
``web3.eth.defaultAccount``) The address the transaction is send from.
``web3.eth.defaultAccount``) The address the transaction is sent from.
* ``to``: ``bytes or text``, checksum address or ENS name - (optional when creating new
contract) The address the transaction is directed to.
* ``gas``: ``integer`` - (optional, default: 90000) Integer of the gas
* ``gas``: ``integer`` - (optional) Integer of the gas
provided for the transaction execution. It will return unused gas.
* ``gasPrice``: ``integer`` - (optional, default: To-Be-Determined) Integer
of the gasPrice used for each paid gas
* ``maxFeePerGas``: ``integer or hex`` - (optional) maximum amount you're willing
to pay, inclusive of ``baseFeePerGas`` and ``maxPriorityFeePerGas``. The difference
between ``maxFeePerGas`` and ``baseFeePerGas + maxPriorityFeePerGas`` is refunded
to the user.
* ``maxPriorityFeePerGas``: ``integer or hex`` - (optional) the part of the fee
that goes to the miner
* ``gasPrice``: ``integer`` - Integer of the gasPrice used for each paid gas
**LEGACY** - unless you have good reason to, use ``maxFeePerGas``
and ``maxPriorityFeePerGas`` instead.
* ``value``: ``integer`` - (optional) Integer of the value send with this
transaction
* ``data``: ``bytes or text`` - The compiled code of a contract OR the hash
Expand All @@ -754,7 +761,34 @@ The following methods are available on the ``web3.eth`` namespace.

.. code-block:: python

>>> web3.eth.send_transaction({'to': '0xd3CdA913deB6f67967B99D67aCDFa1712C293601', 'from': web3.eth.coinbase, 'value': 12345})
# simple example (Web3.py determines gas and fee)
>>> web3.eth.send_transaction({
'to': '0xd3CdA913deB6f67967B99D67aCDFa1712C293601',
'from': web3.eth.coinbase,
'value': 12345
})

# EIP 1559-style transaction
HexBytes('0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331')
>>> web3.eth.send_transaction({
'to': '0xd3CdA913deB6f67967B99D67aCDFa1712C293601',
'from': web3.eth.coinbase,
'value': 12345,
'gas': 21000,
'maxFeePerGas': web3.toWei(250, 'gwei'),
'maxPriorityFeePerGas': web3.toWei(2, 'gwei'),
})
HexBytes('0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331')

# Legacy transaction (less efficient)
HexBytes('0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331')
>>> web3.eth.send_transaction({
'to': '0xd3CdA913deB6f67967B99D67aCDFa1712C293601',
'from': web3.eth.coinbase,
'value': 12345,
'gas': 21000,
'gasPrice': web3.toWei(50, 'gwei'),
})
HexBytes('0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331')

.. py:method:: Eth.sendTransaction(transaction)
Expand Down
1 change: 1 addition & 0 deletions newsfragments/2033.feature.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Adds support for EIP 1559 transaction keys: `maxFeePerGas` and `maxPriorityFeePerGas`
1 change: 1 addition & 0 deletions tests/core/middleware/test_gas_price_strategy.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ def the_gas_price_strategy_middleware(web3):
return initialized


@pytest.mark.skip(reason="London TODO: generate_gas_price updates")
def test_gas_price_generated(the_gas_price_strategy_middleware):
the_gas_price_strategy_middleware.web3.eth.generate_gas_price.return_value = 5
method = 'eth_sendTransaction'
Expand Down
250 changes: 250 additions & 0 deletions tests/integration/generate_fixtures/common_1559.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,250 @@
import contextlib
import os
import shutil
import signal
import socket
import subprocess
import tempfile
import time

from eth_utils import (
is_checksum_address,
to_text,
)

from web3.exceptions import (
TransactionNotFound,
)

COINBASE = '0xdc544d1aa88ff8bbd2f2aec754b1f1e99e1812fd'
COINBASE_PK = '0x58d23b55bc9cdce1f18c2500f40ff4ab7245df9a89505e9b1fa4851f623d241d'

KEYFILE_DATA = '{"address":"dc544d1aa88ff8bbd2f2aec754b1f1e99e1812fd","crypto":{"cipher":"aes-128-ctr","ciphertext":"52e06bc9397ea9fa2f0dae8de2b3e8116e92a2ecca9ad5ff0061d1c449704e98","cipherparams":{"iv":"aa5d0a5370ef65395c1a6607af857124"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"9fdf0764eb3645ffc184e166537f6fe70516bf0e34dc7311dea21f100f0c9263"},"mac":"4e0b51f42b865c15c485f4faefdd1f01a38637e5247f8c75ffe6a8c0eba856f6"},"id":"5a6124e0-10f1-4c1c-ae3e-d903eacb740a","version":3}' # noqa: E501

KEYFILE_PW = 'web3py-test'
KEYFILE_FILENAME = 'UTC--2017-08-24T19-42-47.517572178Z--dc544d1aa88ff8bbd2f2aec754b1f1e99e1812fd' # noqa: E501

RAW_TXN_ACCOUNT = '0x39EEed73fb1D3855E90Cbd42f348b3D7b340aAA6'

UNLOCKABLE_PRIVATE_KEY = '0x392f63a79b1ff8774845f3fa69de4a13800a59e7083f5187f1558f0797ad0f01'
UNLOCKABLE_ACCOUNT = '0x12efdc31b1a8fa1a1e756dfd8a1601055c971e13'
UNLOCKABLE_ACCOUNT_PW = KEYFILE_PW

GENESIS_DATA = {
"config": {
"chainId": 131277322940537, # the string 'web3py' as an integer
"homesteadBlock": 0,
"byzantiumBlock": 0,
"constantinopleBlock": 0,
"eip150Block": 0,
"eip155Block": 0,
"eip158Block": 0,
"istanbulBlock": 0,
"petersburgBlock": 0,
"berlinBlock": 0,
"londonBlock": 0,
},
"nonce": "0x0000000000000042",
"alloc": {
COINBASE: {"balance": "1000000000000000000000000000"},
UNLOCKABLE_ACCOUNT: {"balance": "1000000000000000000000000000"},
RAW_TXN_ACCOUNT: {"balance": "1000000000000000000000000000"},
"0000000000000000000000000000000000000001": {"balance": "1"},
"0000000000000000000000000000000000000002": {"balance": "1"},
"0000000000000000000000000000000000000003": {"balance": "1"},
"0000000000000000000000000000000000000004": {"balance": "1"},
"0000000000000000000000000000000000000005": {"balance": "1"},
"0000000000000000000000000000000000000006": {"balance": "1"},
},
"timestamp": "0x00",
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"extraData": "0x3535353535353535353535353535353535353535353535353535353535353535",
"gasLimit": "0x3b9aca00", # 1,000,000,000
"difficulty": "0x10000",
"mixhash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"coinbase": COINBASE
}


def ensure_path_exists(dir_path):
"""
Make sure that a path exists
"""
if not os.path.exists(dir_path):
os.makedirs(dir_path)
return True
return False


@contextlib.contextmanager
def tempdir():
dir_path = tempfile.mkdtemp()
try:
yield dir_path
finally:
shutil.rmtree(dir_path)


def get_geth_binary():
from geth.install import (
get_executable_path,
install_geth,
)

if 'GETH_BINARY' in os.environ:
return os.environ['GETH_BINARY']
elif 'GETH_VERSION' in os.environ:
geth_version = os.environ['GETH_VERSION']
_geth_binary = get_executable_path(geth_version)
if not os.path.exists(_geth_binary):
install_geth(geth_version)
assert os.path.exists(_geth_binary)
return _geth_binary
else:
return 'geth'


def wait_for_popen(proc, timeout):
start = time.time()
while time.time() < start + timeout:
if proc.poll() is None:
time.sleep(0.01)
else:
break


def kill_proc_gracefully(proc):
if proc.poll() is None:
proc.send_signal(signal.SIGINT)
wait_for_popen(proc, 13)

if proc.poll() is None:
proc.terminate()
wait_for_popen(proc, 5)

if proc.poll() is None:
proc.kill()
wait_for_popen(proc, 2)


def wait_for_socket(ipc_path, timeout=30):
start = time.time()
while time.time() < start + timeout:
try:
sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
sock.connect(ipc_path)
sock.settimeout(timeout)
except (FileNotFoundError, socket.error):
time.sleep(0.01)
else:
break


@contextlib.contextmanager
def get_geth_process(geth_binary,
datadir,
genesis_file_path,
ipc_path,
port,
networkid,
skip_init=False):
if not skip_init:
init_datadir_command = (
geth_binary,
'--datadir', datadir,
'init',
genesis_file_path,
)
print(' '.join(init_datadir_command))
subprocess.check_output(
init_datadir_command,
stdin=subprocess.PIPE,
stderr=subprocess.PIPE,
)

run_geth_command = (
geth_binary,
'--datadir', datadir,
'--ipcpath', ipc_path,
'--nodiscover',
'--port', port,
'--networkid', networkid,
'--etherbase', COINBASE[2:],
)
print(' '.join(run_geth_command))
try:
proc = get_process(run_geth_command)
yield proc
finally:
kill_proc_gracefully(proc)
output, errors = proc.communicate()
print(
"Geth Process Exited:\n"
"stdout:{0}\n\n"
"stderr:{1}\n\n".format(
to_text(output),
to_text(errors),
)
)


def get_process(run_command):
proc = subprocess.Popen(
run_command,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
)
return proc


def mine_block(web3):
origin_block_number = web3.eth.block_number

start_time = time.time()
web3.geth.miner.start(1)
while time.time() < start_time + 120:
block_number = web3.eth.block_number
if block_number > origin_block_number:
web3.geth.miner.stop()
return block_number
else:
time.sleep(0.1)
else:
raise ValueError("No block mined during wait period")


def mine_transaction_hash(web3, txn_hash):
start_time = time.time()
web3.geth.miner.start(1)
while time.time() < start_time + 120:
try:
receipt = web3.eth.get_transaction_receipt(txn_hash)
except TransactionNotFound:
continue
if receipt is not None:
web3.geth.miner.stop()
return receipt
else:
time.sleep(0.1)
else:
raise ValueError("Math contract deploy transaction not mined during wait period")


def deploy_contract(web3, name, factory):
web3.geth.personal.unlock_account(web3.eth.coinbase, KEYFILE_PW)
deploy_txn_hash = factory.constructor().transact(
{
"from": web3.eth.coinbase,
"maxFeePerGas": hex(100000000),
"maxPriorityFeePerGas": hex(1),
"gas": 2000000,
}
)
print('{0}_CONTRACT_DEPLOY_HASH: '.format(name.upper()), deploy_txn_hash)
deploy_receipt = mine_transaction_hash(web3, deploy_txn_hash)
print('{0}_CONTRACT_DEPLOY_TRANSACTION_MINED'.format(name.upper()))
contract_address = deploy_receipt['contractAddress']
assert is_checksum_address(contract_address)
print('{0}_CONTRACT_ADDRESS:'.format(name.upper()), contract_address)
return deploy_receipt
2 changes: 1 addition & 1 deletion tests/integration/generate_fixtures/go_ethereum.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
valmap,
)

import common
import common_1559 as common
from tests.utils import (
get_open_port,
)
Expand Down
Binary file added tests/integration/geth-london-fixture.zip
Binary file not shown.
21 changes: 13 additions & 8 deletions tests/integration/go_ethereum/common.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from concurrent.futures._base import (
TimeoutError as FuturesTimeoutError,
)
# from concurrent.futures._base import (
# TimeoutError as FuturesTimeoutError,
# )
import pytest

from web3._utils.module_testing import ( # noqa: F401
Expand All @@ -20,16 +20,21 @@ def _check_web3_clientVersion(self, client_version):


class GoEthereumEthModuleTest(EthModuleTest):
@pytest.mark.xfail(
strict=False,
raises=FuturesTimeoutError,
reason='Sometimes a TimeoutError is hit when waiting for the txn to be mined',
)
# @pytest.mark.xfail(
# strict=False,
# raises=FuturesTimeoutError,
# reason='Sometimes a TimeoutError is hit when waiting for the txn to be mined',
# )
@pytest.mark.skip(reason="London TODO: crashes on [address_conversion_func1]")
def test_eth_replace_transaction_already_mined(self, web3, unlocked_account_dual_type):
web3.geth.miner.start()
super().test_eth_replace_transaction_already_mined(web3, unlocked_account_dual_type)
web3.geth.miner.stop()

@pytest.mark.skip(reason="London TODO: pending call isn't found")
def test_eth_call_old_contract_state(self, web3, math_contract, unlocked_account):
super().test_eth_call_old_contract_state(web3, math_contract, unlocked_account)

@pytest.mark.xfail(reason='eth_signTypedData has not been released in geth')
def test_eth_sign_typed_data(self, web3, unlocked_account_dual_type):
super().test_eth_sign_typed_data(web3, unlocked_account_dual_type)
Expand Down
3 changes: 2 additions & 1 deletion tests/integration/go_ethereum/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@

KEYFILE_PW = 'web3py-test'

GETH_FIXTURE_ZIP = 'geth-1.10.4-fixture.zip'
GETH_FIXTURE_ZIP = 'geth-london-fixture.zip'
# GETH_FIXTURE_ZIP = 'geth-1-10-4-fixture.zip'


@pytest.fixture(scope='module')
Expand Down
Loading