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
1 change: 1 addition & 0 deletions newsfragments/2082.feature.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
eth_signTransaction support for eip-1559 params 'maxFeePerGas' and 'maxPriorityFeePerGas'
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@
install_requires=[
"aiohttp>=3.7.4.post0,<4",
"eth-abi>=2.0.0b6,<3.0.0",
"eth-account>=0.5.3,<0.6.0",
"eth-account>=0.5.5,<0.6.0",
"eth-hash[pycryptodome]>=0.2.0,<1.0.0",
"eth-typing>=2.0.0,<3.0.0",
"eth-utils>=1.9.5,<2.0.0",
Expand Down
51 changes: 50 additions & 1 deletion tests/core/eth-module/test_accounts.py
Original file line number Diff line number Diff line change
Expand Up @@ -290,8 +290,54 @@ def test_eth_account_sign(acct,
232940010090391255679819602567388136081614408698362277324138554019997613600,
38,
),
(
{
"gas": 100000,
"maxFeePerGas": 2000000000,
"maxPriorityFeePerGas": 2000000000,
"data": "0x5544",
"nonce": "0x2",
"to": "0x96216849c49358B10257cb55b28eA603c874b05E",
"value": "0x5af3107a4000",
"type": "0x2",
"accessList": (
(
"0x0000000000000000000000000000000000000001",
(
"0x0100000000000000000000000000000000000000000000000000000000000000",
),
),
),
"chainId": 1,
},
'0x4c0883a69102937d6231471b5dbb6204fe5129617082792ae468d01a3f362318',
HexBytes('0x02f8ac010284773594008477359400830186a09496216849c49358b10257cb55b28ea603c874b05e865af3107a4000825544f838f7940000000000000000000000000000000000000001e1a0010000000000000000000000000000000000000000000000000000000000000001a0c5217aec4d576a1b5097c5054ed0157f762dd018f5c2195f0d0190ddca9445c5a0230a35968164ab4318a7042ca0ad4b4178a0d24c2cc000e13dfa24c206317935'), # noqa: 501
HexBytes('0xd42048e2a34957ab81f093d757be86453197bec95780d509fb3545d295227a34'),
89164785507787893778245375805371571349289793451450966346444410690555998389701,
15848988021237185855591648888808312289463551127752258139478457465342262343989,
1,
),
(
{
"gas": '0x186a0',
"maxFeePerGas": '0x77359400',
"maxPriorityFeePerGas": '0x77359400',
"data": "0x5544",
"nonce": "0x2",
"to": "0x96216849c49358B10257cb55b28eA603c874b05E",
"value": "0x5af3107a4000",
"type": "0x2",
"chainId": 1,
},
'0x4c0883a69102937d6231471b5dbb6204fe5129617082792ae468d01a3f362318',
HexBytes('0x02f873010284773594008477359400830186a09496216849c49358b10257cb55b28ea603c874b05e865af3107a4000825544c080a07cfcd6472c4a29c566df07851c4708cb02523f71075221cc8e668908db58fde0a03b9185c7a719a99f2822dda5ed94c9f90641bd75578f093f15ceb71fc6ba85fc'), # noqa: 501
HexBytes('0x20a46a15e0b39815b44f01f3261a8741b095ed2bb4235339fa0e461251f9dc95'),
56533517577187861348991333001994723803575055479068550930552639362970253065696,
26943574205696802233481851589848298424411617815775925360566652975894417278460,
0,
)
),
ids=['web3js_example', '31byte_r_and_s'],
ids=['web3js_example', '31byte_r_and_s', 'eip_1559_example', 'eip_1559_hex_fees'],
)
def test_eth_account_sign_transaction(acct, txn, private_key, expected_raw_tx, tx_hash, r, s, v):
signed = acct.sign_transaction(txn, private_key)
Expand All @@ -304,6 +350,9 @@ def test_eth_account_sign_transaction(acct, txn, private_key, expected_raw_tx, t
account = acct.from_key(private_key)
assert account.sign_transaction(txn) == signed

expected_sender = acct.from_key(private_key).address
assert acct.recover_transaction(signed.rawTransaction) == expected_sender


@pytest.mark.parametrize(
'transaction_info',
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 @@ -254,7 +254,15 @@ class TestEthereumTesterEthModule(EthModuleTest):
EthModuleTest.test_eth_signTransaction_deprecated,
ValueError
)
test_eth_sign_transaction_legacy = not_implemented(
EthModuleTest.test_eth_sign_transaction_legacy,
ValueError
)
test_eth_sign_transaction = not_implemented(EthModuleTest.test_eth_sign_transaction, ValueError)
test_eth_sign_transaction_hex_fees = not_implemented(
EthModuleTest.test_eth_sign_transaction_hex_fees,
ValueError
)
test_eth_sign_transaction_ens_names = not_implemented(
EthModuleTest.test_eth_sign_transaction_ens_names, ValueError
)
Expand Down
1 change: 1 addition & 0 deletions web3/_utils/method_formatters.py
Original file line number Diff line number Diff line change
Expand Up @@ -411,6 +411,7 @@ def apply_list_to_array_formatter(formatter: Any) -> Callable[..., Any]:
(is_length(2), estimate_gas_with_block_id),
)),
RPC.eth_sendTransaction: apply_formatter_at_index(transaction_param_formatter, 0),
RPC.eth_signTransaction: apply_formatter_at_index(transaction_param_formatter, 0),
RPC.eth_getProof: apply_formatter_at_index(to_hex_if_integer, 2),
# personal
RPC.personal_importRawKey: apply_formatter_at_index(
Expand Down
62 changes: 59 additions & 3 deletions web3/_utils/module_testing/eth_module.py
Original file line number Diff line number Diff line change
Expand Up @@ -939,7 +939,11 @@ def test_invalid_eth_sign_typed_data(
json.loads(invalid_typed_message)
)

def test_eth_sign_transaction(self, web3: "Web3", unlocked_account: ChecksumAddress) -> None:
def test_eth_sign_transaction_legacy(
self,
web3: "Web3",
unlocked_account: ChecksumAddress
) -> None:
txn_params: TxParams = {
'from': unlocked_account,
'to': unlocked_account,
Expand All @@ -957,6 +961,56 @@ def test_eth_sign_transaction(self, web3: "Web3", unlocked_account: ChecksumAddr
assert result['tx']['gasPrice'] == txn_params['gasPrice']
assert result['tx']['nonce'] == txn_params['nonce']

def test_eth_sign_transaction(
self,
web3: "Web3",
unlocked_account: ChecksumAddress
) -> None:
txn_params: TxParams = {
'from': unlocked_account,
'to': unlocked_account,
'value': Wei(1),
'gas': Wei(21000),
'maxFeePerGas': web3.toWei(2, 'gwei'),
'maxPriorityFeePerGas': web3.toWei(1, 'gwei'),
'nonce': Nonce(0),
}
result = web3.eth.sign_transaction(txn_params)
signatory_account = web3.eth.account.recover_transaction(result['raw'])
assert unlocked_account == signatory_account
assert result['tx']['to'] == txn_params['to']
assert result['tx']['value'] == txn_params['value']
assert result['tx']['gas'] == txn_params['gas']
assert result['tx']['maxFeePerGas'] == txn_params['maxFeePerGas']
assert result['tx']['maxPriorityFeePerGas'] == txn_params['maxPriorityFeePerGas']
assert result['tx']['nonce'] == txn_params['nonce']

def test_eth_sign_transaction_hex_fees(
self,
web3: "Web3",
unlocked_account: ChecksumAddress
) -> None:
txn_params: TxParams = {
'from': unlocked_account,
'to': unlocked_account,
'value': Wei(1),
'gas': Wei(21000),
'maxFeePerGas': hex(web3.toWei(2, 'gwei')),
'maxPriorityFeePerGas': hex(web3.toWei(1, 'gwei')),
'nonce': Nonce(0),
}
result = web3.eth.sign_transaction(txn_params)
signatory_account = web3.eth.account.recover_transaction(result['raw'])
assert unlocked_account == signatory_account
assert result['tx']['to'] == txn_params['to']
assert result['tx']['value'] == txn_params['value']
assert result['tx']['gas'] == txn_params['gas']
assert result['tx']['maxFeePerGas'] == int(str(txn_params['maxFeePerGas']), 16)
assert result['tx']['maxPriorityFeePerGas'] == int(
str(txn_params['maxPriorityFeePerGas']), 16
)
assert result['tx']['nonce'] == txn_params['nonce']

def test_eth_signTransaction_deprecated(self,
web3: "Web3",
unlocked_account: ChecksumAddress) -> None:
Expand Down Expand Up @@ -988,7 +1042,8 @@ def test_eth_sign_transaction_ens_names(
'to': 'unlocked-account.eth',
'value': Wei(1),
'gas': Wei(21000),
'gasPrice': web3.eth.gas_price,
'maxFeePerGas': web3.toWei(2, 'gwei'),
'maxPriorityFeePerGas': web3.toWei(1, 'gwei'),
'nonce': Nonce(0),
}
result = web3.eth.sign_transaction(txn_params)
Expand All @@ -997,7 +1052,8 @@ def test_eth_sign_transaction_ens_names(
assert result['tx']['to'] == unlocked_account
assert result['tx']['value'] == txn_params['value']
assert result['tx']['gas'] == txn_params['gas']
assert result['tx']['gasPrice'] == txn_params['gasPrice']
assert result['tx']['maxFeePerGas'] == txn_params['maxFeePerGas']
assert result['tx']['maxPriorityFeePerGas'] == txn_params['maxPriorityFeePerGas']
assert result['tx']['nonce'] == txn_params['nonce']

def test_eth_send_transaction_addr_checksum_required(
Expand Down
11 changes: 5 additions & 6 deletions web3/middleware/signing.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,8 @@
Any,
Callable,
Collection,
Dict,
Iterable,
NoReturn,
Tuple,
TypeVar,
Union,
)
Expand Down Expand Up @@ -77,10 +76,10 @@ def is_eth_key(value: Any) -> bool:
@to_dict
def gen_normalized_accounts(
val: Union[_PrivateKey, Collection[_PrivateKey]]
) -> Iterable[Dict[ChecksumAddress, Account]]:
) -> Iterable[Tuple[ChecksumAddress, LocalAccount]]:
if isinstance(val, (list, tuple, set,)):
for i in val:
account: Account = to_account(i)
account: LocalAccount = to_account(i)
yield account.address, account
else:
account = to_account(val)
Expand All @@ -89,7 +88,7 @@ def gen_normalized_accounts(


@singledispatch
def to_account(val: Any) -> NoReturn:
def to_account(val: Any) -> LocalAccount:
raise TypeError(
"key must be one of the types: "
"eth_keys.datatype.PrivateKey, eth_account.signers.local.LocalAccount, "
Expand All @@ -102,7 +101,7 @@ def _(val: T) -> T:
return val


def private_key_to_account(val: _PrivateKey) -> Account:
def private_key_to_account(val: _PrivateKey) -> LocalAccount:
normalized_key = key_normalizer(val)
return Account.from_key(normalized_key)

Expand Down