diff --git a/newsfragments/3247.feature.rst b/newsfragments/3247.feature.rst new file mode 100644 index 0000000000..9f107d8a31 --- /dev/null +++ b/newsfragments/3247.feature.rst @@ -0,0 +1 @@ +Add support for ``eth_getRawTransactionByHash`` RPC method diff --git a/tests/integration/test_ethereum_tester.py b/tests/integration/test_ethereum_tester.py index b24af0bb49..997ec2e02c 100644 --- a/tests/integration/test_ethereum_tester.py +++ b/tests/integration/test_ethereum_tester.py @@ -316,6 +316,26 @@ class TestEthereumTesterEthModule(EthModuleTest): EthModuleTest.test_eth_call_with_override_code, TypeError, ) + test_eth_getBlockReceipts_hash = not_implemented( + EthModuleTest.test_eth_getBlockReceipts_hash, + MethodUnavailable, + ) + test_eth_getBlockReceipts_not_found = not_implemented( + EthModuleTest.test_eth_getBlockReceipts_not_found, + MethodUnavailable, + ) + test_eth_getBlockReceipts_with_integer = not_implemented( + EthModuleTest.test_eth_getBlockReceipts_with_integer, + MethodUnavailable, + ) + test_eth_getBlockReceipts_safe = not_implemented( + EthModuleTest.test_eth_getBlockReceipts_safe, + MethodUnavailable, + ) + test_eth_getBlockReceipts_finalized = not_implemented( + EthModuleTest.test_eth_getBlockReceipts_finalized, + MethodUnavailable, + ) def test_eth_getBlockByHash_pending(self, w3: "Web3") -> None: block = w3.eth.get_block("pending") @@ -632,27 +652,11 @@ def test_eth_send_transaction_no_gas(self, eth_tester, w3, unlocked_account): def test_eth_send_transaction_no_max_fee(self, eth_tester, w3, unlocked_account): super().test_eth_send_transaction_no_max_fee(w3, unlocked_account) - def test_eth_getBlockByNumber_safe( - self, w3: "Web3", empty_block: BlockData - ) -> None: - super().test_eth_getBlockByNumber_safe(w3, empty_block) - - def test_eth_getBlockByNumber_finalized( - self, w3: "Web3", empty_block: BlockData - ) -> None: - super().test_eth_getBlockByNumber_finalized(w3, empty_block) - - def test_eth_fee_history(self, w3: "Web3") -> None: - super().test_eth_fee_history(w3) - def test_eth_fee_history_with_integer( self, w3: "Web3", empty_block: BlockData ) -> None: super().test_eth_fee_history_with_integer(w3, empty_block) - def test_eth_fee_history_with_no_reward_percentiles(self, w3: "Web3") -> None: - super().test_eth_fee_history_no_reward_percentiles(w3) - def test_eth_get_balance_with_block_identifier(self, w3: "Web3") -> None: w3.testing.mine() miner_address = w3.eth.get_block(1)["miner"] diff --git a/web3/_utils/method_formatters.py b/web3/_utils/method_formatters.py index bf667d970a..64b6d8821d 100644 --- a/web3/_utils/method_formatters.py +++ b/web3/_utils/method_formatters.py @@ -516,6 +516,7 @@ def apply_list_to_array_formatter(formatter: Any) -> Callable[..., Any]: ), RPC.eth_getBalance: apply_formatter_at_index(to_hex_if_integer, 1), RPC.eth_getBlockByNumber: apply_formatter_at_index(to_hex_if_integer, 0), + RPC.eth_getBlockReceipts: apply_formatter_at_index(to_hex_if_integer, 0), RPC.eth_getBlockTransactionCountByNumber: apply_formatter_at_index( to_hex_if_integer, 0, @@ -723,6 +724,7 @@ def subscription_formatter(value: Any) -> Union[HexBytes, HexStr, Dict[str, Any] RPC.eth_getBalance: to_integer_if_hex, RPC.eth_getBlockByHash: apply_formatter_if(is_not_null, block_formatter), RPC.eth_getBlockByNumber: apply_formatter_if(is_not_null, block_formatter), + RPC.eth_getBlockReceipts: apply_formatter_to_array(receipt_formatter), RPC.eth_getBlockTransactionCountByHash: to_integer_if_hex, RPC.eth_getBlockTransactionCountByNumber: to_integer_if_hex, RPC.eth_getCode: HexBytes, @@ -900,6 +902,7 @@ def raise_transaction_not_found_with_index( NULL_RESULT_FORMATTERS: Dict[RPCEndpoint, Callable[..., Any]] = { RPC.eth_getBlockByHash: raise_block_not_found, RPC.eth_getBlockByNumber: raise_block_not_found, + RPC.eth_getBlockReceipts: raise_block_not_found, RPC.eth_getBlockTransactionCountByHash: raise_block_not_found, RPC.eth_getBlockTransactionCountByNumber: raise_block_not_found, RPC.eth_getUncleCountByBlockHash: raise_block_not_found, diff --git a/web3/_utils/module_testing/eth_module.py b/web3/_utils/module_testing/eth_module.py index d534ab5b80..3651a5ff6f 100644 --- a/web3/_utils/module_testing/eth_module.py +++ b/web3/_utils/module_testing/eth_module.py @@ -977,6 +977,39 @@ async def test_eth_getBlockByNumber_finalized( assert block is not None assert isinstance(block["number"], int) + @pytest.mark.asyncio + async def test_eth_getBlockReceipts_hash( + self, async_w3: "AsyncWeb3", async_empty_block: BlockData + ) -> None: + receipts = await async_w3.eth.get_block_receipts(async_empty_block["hash"]) + assert isinstance(receipts, list) + + @pytest.mark.asyncio + async def test_eth_getBlockReceipts_not_found(self, async_w3: "AsyncWeb3") -> None: + with pytest.raises(BlockNotFound): + await async_w3.eth.get_block_receipts(UNKNOWN_HASH) + + @pytest.mark.asyncio + async def test_eth_getBlockReceipts_with_integer( + self, async_w3: "AsyncWeb3", async_empty_block: BlockData + ) -> None: + receipts = await async_w3.eth.get_block_receipts(async_empty_block["number"]) + assert isinstance(receipts, list) + + @pytest.mark.asyncio + async def test_eth_getBlockReceipts_safe( + self, async_w3: "AsyncWeb3", async_empty_block: BlockData + ) -> None: + receipts = await async_w3.eth.get_block_receipts("safe") + assert isinstance(receipts, list) + + @pytest.mark.asyncio + async def test_eth_getBlockReceipts_finalized( + self, async_w3: "AsyncWeb3", async_empty_block: BlockData + ) -> None: + receipts = await async_w3.eth.get_block_receipts("finalized") + assert isinstance(receipts, list) + @pytest.mark.asyncio async def test_eth_get_block_by_number_full_transactions( self, async_w3: "AsyncWeb3", async_block_with_txn: BlockData @@ -4224,6 +4257,34 @@ def test_eth_getBlockByNumber_full_transactions( transaction = block["transactions"][0] assert transaction["hash"] == block_with_txn["transactions"][0] # type: ignore + def test_eth_getBlockReceipts_hash( + self, w3: "Web3", empty_block: BlockData + ) -> None: + receipts = w3.eth.get_block_receipts(empty_block["hash"]) + assert isinstance(receipts, list) + + def test_eth_getBlockReceipts_not_found(self, w3: "Web3") -> None: + with pytest.raises(BlockNotFound): + w3.eth.get_block_receipts(UNKNOWN_HASH) + + def test_eth_getBlockReceipts_with_integer( + self, w3: "Web3", empty_block: BlockData + ) -> None: + receipts = w3.eth.get_block_receipts(empty_block["number"]) + assert isinstance(receipts, list) + + def test_eth_getBlockReceipts_safe( + self, w3: "Web3", empty_block: BlockData + ) -> None: + receipts = w3.eth.get_block_receipts("safe") + assert isinstance(receipts, list) + + def test_eth_getBlockReceipts_finalized( + self, w3: "Web3", empty_block: BlockData + ) -> None: + receipts = w3.eth.get_block_receipts("finalized") + assert isinstance(receipts, list) + def test_eth_getTransactionByHash(self, w3: "Web3", mined_txn_hash: HexStr) -> None: transaction = w3.eth.get_transaction(mined_txn_hash) assert is_dict(transaction) diff --git a/web3/_utils/rpc_abi.py b/web3/_utils/rpc_abi.py index 774f2adcce..f1c0d8bd1e 100644 --- a/web3/_utils/rpc_abi.py +++ b/web3/_utils/rpc_abi.py @@ -57,6 +57,7 @@ class RPC: eth_getBalance = RPCEndpoint("eth_getBalance") eth_getBlockByHash = RPCEndpoint("eth_getBlockByHash") eth_getBlockByNumber = RPCEndpoint("eth_getBlockByNumber") + eth_getBlockReceipts = RPCEndpoint("eth_getBlockReceipts") eth_getBlockTransactionCountByHash = RPCEndpoint( "eth_getBlockTransactionCountByHash" ) diff --git a/web3/eth/async_eth.py b/web3/eth/async_eth.py index 09eadbf209..7eae1673cb 100644 --- a/web3/eth/async_eth.py +++ b/web3/eth/async_eth.py @@ -75,6 +75,7 @@ BlockData, BlockIdentifier, BlockParams, + BlockReceipts, CreateAccessListResponse, FeeHistory, FilterParams, @@ -440,6 +441,20 @@ async def get_block( ) -> BlockData: return await self._get_block(block_identifier, full_transactions) + # eth_getBlockReceipts + + _get_block_receipts: Method[ + Callable[[BlockIdentifier], Awaitable[BlockReceipts]] + ] = Method( + RPC.eth_getBlockReceipts, + mungers=[default_root_munger], + ) + + async def get_block_receipts( + self, block_identifier: BlockIdentifier + ) -> BlockReceipts: + return await self._get_block_receipts(block_identifier) + # eth_getBalance _get_balance: Method[ diff --git a/web3/eth/eth.py b/web3/eth/eth.py index 630c9a3e27..206a35779a 100644 --- a/web3/eth/eth.py +++ b/web3/eth/eth.py @@ -72,6 +72,7 @@ BlockData, BlockIdentifier, BlockParams, + BlockReceipts, CreateAccessListResponse, FeeHistory, FilterParams, @@ -411,6 +412,16 @@ def get_block( ) -> BlockData: return self._get_block(block_identifier, full_transactions) + # eth_getBlockReceipts + + _get_block_receipts: Method[Callable[[BlockIdentifier], BlockReceipts]] = Method( + RPC.eth_getBlockReceipts, + mungers=[default_root_munger], + ) + + def get_block_receipts(self, block_identifier: BlockIdentifier) -> BlockReceipts: + return self._get_block_receipts(block_identifier) + # eth_getBalance _get_balance: Method[ diff --git a/web3/types.py b/web3/types.py index f5092f7a88..736baac06b 100644 --- a/web3/types.py +++ b/web3/types.py @@ -381,6 +381,8 @@ class FeeHistory(TypedDict): }, ) +BlockReceipts = List[TxReceipt] + class SignedTx(TypedDict, total=False): raw: bytes