diff --git a/newsfragments/2082.feature.rst b/newsfragments/2082.feature.rst new file mode 100644 index 0000000000..c1e028f246 --- /dev/null +++ b/newsfragments/2082.feature.rst @@ -0,0 +1 @@ +eth_signTransaction support for eip-1559 params 'maxFeePerGas' and 'maxPriorityFeePerGas' \ No newline at end of file diff --git a/setup.py b/setup.py index 0bfb6ea8d7..6e70ccc2f4 100644 --- a/setup.py +++ b/setup.py @@ -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", diff --git a/tests/core/eth-module/test_accounts.py b/tests/core/eth-module/test_accounts.py index ba72483805..3f49984017 100644 --- a/tests/core/eth-module/test_accounts.py +++ b/tests/core/eth-module/test_accounts.py @@ -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) @@ -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', diff --git a/tests/integration/test_ethereum_tester.py b/tests/integration/test_ethereum_tester.py index 80d80eb37a..7dac70280f 100644 --- a/tests/integration/test_ethereum_tester.py +++ b/tests/integration/test_ethereum_tester.py @@ -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 ) diff --git a/web3/_utils/method_formatters.py b/web3/_utils/method_formatters.py index e489b324e3..abbf21cd1b 100644 --- a/web3/_utils/method_formatters.py +++ b/web3/_utils/method_formatters.py @@ -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( diff --git a/web3/_utils/module_testing/eth_module.py b/web3/_utils/module_testing/eth_module.py index d1eafb651e..c529533147 100644 --- a/web3/_utils/module_testing/eth_module.py +++ b/web3/_utils/module_testing/eth_module.py @@ -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, @@ -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: @@ -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) @@ -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(