Skip to content

Commit 4893947

Browse files
authored
Add eth_getRawTransactionByHash (#2051)
1 parent c28a93b commit 4893947

File tree

11 files changed

+98
-2
lines changed

11 files changed

+98
-2
lines changed

docs/web3.eth.rst

+15-1
Original file line numberDiff line numberDiff line change
@@ -576,6 +576,20 @@ The following methods are available on the ``web3.eth`` namespace.
576576
:attr:`~web3.eth.Eth.get_transaction`
577577

578578

579+
.. py:method:: Eth.get_raw_transaction(transaction_hash)
580+
581+
* Delegates to ``eth_getRawTransactionByHash`` RPC Method
582+
583+
Returns the raw form of transaction specified by ``transaction_hash``.
584+
585+
If no transaction is found, ``TransactionNotFound`` is raised.
586+
587+
.. code-block:: python
588+
589+
>>> web3.eth.get_raw_transaction('0x86fbfe56cce542ff0a2a2716c31675a0c9c43701725c4a751d20ee2ddf8a733d')
590+
HexBytes('0xf86907843b9aca0082520894dc544d1aa88ff8bbd2f2aec754b1f1e99e1812fd018086eecac466e115a0f9db4e25484b28f486b247a372708d4cd0643fc63e604133afac577f4cc1eab8a044841d84e799d4dc18ba146816a937e8a0be8bc296bd8bb8aea126de5e627e06')
591+
592+
579593
.. py:method:: Eth.getTransactionFromBlock(block_identifier, transaction_index)
580594
581595
.. note:: This method is deprecated in EIP 1474.
@@ -748,7 +762,7 @@ The following methods are available on the ``web3.eth`` namespace.
748762
that goes to the miner
749763
* ``gasPrice``: ``integer`` - Integer of the gasPrice used for each paid gas
750764
**LEGACY** - unless you have good reason to, use ``maxFeePerGas``
751-
and ``maxPriorityFeePerGas`` instead.
765+
and ``maxPriorityFeePerGas`` instead.
752766
* ``value``: ``integer`` - (optional) Integer of the value send with this
753767
transaction
754768
* ``data``: ``bytes or text`` - The compiled code of a contract OR the hash

newsfragments/2039.feature.rst

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Add support for eth_getRawTransactionByHash RPC method

tests/core/manager/test_response_formatters.py

+27
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,12 @@
77
from web3._utils.method_formatters import (
88
raise_block_not_found,
99
raise_block_not_found_for_uncle_at_index,
10+
raise_transaction_not_found,
1011
)
1112
from web3.exceptions import (
1213
BlockNotFound,
1314
ContractLogicError,
15+
TransactionNotFound,
1416
)
1517

1618
ERROR_RESPONSE = {
@@ -24,6 +26,7 @@
2426

2527

2628
NONE_RESPONSE = {"jsonrpc": "2.0", "id": 1, "result": None}
29+
ZERO_X_RESPONSE = {"jsonrpc": "2.0", "id": 1, "result": '0x'}
2730

2831

2932
def raise_contract_logic_error(response):
@@ -89,6 +92,14 @@ def raise_contract_logic_error(response):
8992
raise_block_not_found_for_uncle_at_index,
9093
BlockNotFound,
9194
),
95+
(
96+
# 0x response gets handled the same as a None response
97+
ZERO_X_RESPONSE,
98+
('0x03'),
99+
identity,
100+
raise_transaction_not_found,
101+
TransactionNotFound,
102+
),
92103
],
93104
)
94105
def test_formatted_response_raises_errors(web3,
@@ -123,6 +134,14 @@ def test_formatted_response_raises_errors(web3,
123134
BlockNotFound,
124135
"Uncle at index: 0 of block with id: '0x01' not found."
125136
),
137+
(
138+
ZERO_X_RESPONSE,
139+
('0x01',),
140+
identity,
141+
raise_transaction_not_found,
142+
TransactionNotFound,
143+
"Transaction with hash: '0x01' not found."
144+
),
126145
],
127146
)
128147
def test_formatted_response_raises_correct_error_message(response,
@@ -148,6 +167,14 @@ def test_formatted_response_raises_correct_error_message(response,
148167
identity,
149168
NONE_RESPONSE['result'],
150169
),
170+
(
171+
# Response with a result of 0x doesn't raise if there is no null result formatter
172+
ZERO_X_RESPONSE,
173+
('0x03'),
174+
identity,
175+
identity,
176+
ZERO_X_RESPONSE['result'],
177+
),
151178
],
152179
)
153180
def test_formatted_response(response,

tests/integration/test_ethereum_tester.py

+4
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,10 @@ class TestEthereumTesterEthModule(EthModuleTest):
264264
test_eth_submitWork_deprecated = not_implemented(
265265
EthModuleTest.test_eth_submitWork_deprecated, ValueError)
266266
test_eth_submit_work = not_implemented(EthModuleTest.test_eth_submit_work, ValueError)
267+
test_eth_get_raw_transaction = not_implemented(
268+
EthModuleTest.test_eth_get_raw_transaction, ValueError)
269+
test_eth_get_raw_transaction_raises_error = not_implemented(
270+
EthModuleTest.test_eth_get_raw_transaction, ValueError)
267271

268272
def test_eth_getBlockByHash_pending(
269273
self, web3: "Web3"

web3/_utils/method_formatters.py

+2
Original file line numberDiff line numberDiff line change
@@ -450,6 +450,7 @@ def apply_list_to_array_formatter(formatter: Any) -> Callable[..., Any]:
450450
RPC.eth_getFilterLogs: filter_result_formatter,
451451
RPC.eth_getLogs: filter_result_formatter,
452452
RPC.eth_getProof: apply_formatter_if(is_not_null, proof_formatter),
453+
RPC.eth_getRawTransactionByHash: HexBytes,
453454
RPC.eth_getStorageAt: HexBytes,
454455
RPC.eth_getTransactionByBlockHashAndIndex: apply_formatter_if(
455456
is_not_null,
@@ -657,6 +658,7 @@ def raise_transaction_not_found_with_index(params: Tuple[BlockIdentifier, int])
657658
RPC.eth_getTransactionByBlockHashAndIndex: raise_transaction_not_found_with_index,
658659
RPC.eth_getTransactionByBlockNumberAndIndex: raise_transaction_not_found_with_index,
659660
RPC.eth_getTransactionReceipt: raise_transaction_not_found,
661+
RPC.eth_getRawTransactionByHash: raise_transaction_not_found,
660662
}
661663

662664

web3/_utils/module_testing/eth_module.py

+26
Original file line numberDiff line numberDiff line change
@@ -360,6 +360,20 @@ async def test_eth_getBlockByNumber_full_transactions(
360360
transaction = block['transactions'][0]
361361
assert transaction['hash'] == block_with_txn['transactions'][0]
362362

363+
@pytest.mark.asyncio
364+
async def test_eth_get_raw_transaction(
365+
self, async_w3: "Web3", mined_txn_hash: HexStr
366+
) -> None:
367+
raw_transaction = await async_w3.eth.get_raw_transaction(mined_txn_hash) # type: ignore
368+
assert is_bytes(raw_transaction)
369+
370+
@pytest.mark.asyncio
371+
async def test_eth_get_raw_transaction_raises_error(
372+
self, web3: "Web3", mined_txn_hash: HexStr
373+
) -> None:
374+
with pytest.raises(TransactionNotFound, match=f"Transaction with hash: '{UNKNOWN_HASH}'"):
375+
web3.eth.get_raw_transaction(UNKNOWN_HASH)
376+
363377

364378
class EthModuleTest:
365379
def test_eth_protocol_version(self, web3: "Web3") -> None:
@@ -2168,3 +2182,15 @@ def test_eth_submitWork_deprecated(self, web3: "Web3") -> None:
21682182
match="submitWork is deprecated in favor of submit_work"):
21692183
result = web3.eth.submitWork(nonce, pow_hash, mix_digest)
21702184
assert result is False
2185+
2186+
def test_eth_get_raw_transaction(
2187+
self, web3: "Web3", mined_txn_hash: HexStr
2188+
) -> None:
2189+
raw_transaction = web3.eth.get_raw_transaction(mined_txn_hash)
2190+
assert is_bytes(raw_transaction)
2191+
2192+
def test_eth_get_raw_transaction_raises_error(
2193+
self, web3: "Web3", mined_txn_hash: HexStr
2194+
) -> None:
2195+
with pytest.raises(TransactionNotFound, match=f"Transaction with hash: '{UNKNOWN_HASH}'"):
2196+
web3.eth.get_raw_transaction(UNKNOWN_HASH)

web3/_utils/rpc_abi.py

+2
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ class RPC:
5757
eth_getFilterLogs = RPCEndpoint("eth_getFilterLogs")
5858
eth_getLogs = RPCEndpoint("eth_getLogs")
5959
eth_getProof = RPCEndpoint("eth_getProof")
60+
eth_getRawTransactionByHash = RPCEndpoint("eth_getRawTransactionByHash")
6061
eth_getStorageAt = RPCEndpoint("eth_getStorageAt")
6162
eth_getTransactionByBlockHashAndIndex = RPCEndpoint("eth_getTransactionByBlockHashAndIndex")
6263
eth_getTransactionByBlockNumberAndIndex = RPCEndpoint("eth_getTransactionByBlockNumberAndIndex")
@@ -175,6 +176,7 @@ class RPC:
175176
'eth_getBlockTransactionCountByHash': ['bytes32'],
176177
'eth_getCode': ['address', None],
177178
'eth_getLogs': FILTER_PARAMS_ABIS,
179+
'eth_getRawTransactionByHash': ['bytes32'],
178180
'eth_getStorageAt': ['address', 'uint', None],
179181
'eth_getProof': ['address', 'uint[]', None],
180182
'eth_getTransactionByBlockHashAndIndex': ['bytes32', 'uint'],

web3/eth.py

+12
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,11 @@ def send_transaction_munger(self, transaction: TxParams) -> Tuple[TxParams]:
132132
mungers=[default_root_munger]
133133
)
134134

135+
_get_raw_transaction: Method[Callable[[_Hash32], HexBytes]] = Method(
136+
RPC.eth_getRawTransactionByHash,
137+
mungers=[default_root_munger]
138+
)
139+
135140
def _generate_gas_price(self, transaction_params: Optional[TxParams] = None) -> Optional[Wei]:
136141
if self.gasPriceStrategy:
137142
return self.gasPriceStrategy(self.web3, transaction_params)
@@ -205,6 +210,10 @@ async def get_transaction(self, transaction_hash: _Hash32) -> TxData:
205210
# types ignored b/c mypy conflict with BlockingEth properties
206211
return await self._get_transaction(transaction_hash) # type: ignore
207212

213+
async def get_raw_transaction(self, transaction_hash: _Hash32) -> TxData:
214+
# types ignored b/c mypy conflict with BlockingEth properties
215+
return await self._get_raw_transaction(transaction_hash) # type: ignore
216+
208217
async def generate_gas_price(
209218
self, transaction_params: Optional[TxParams] = None
210219
) -> Optional[Wei]:
@@ -501,6 +510,9 @@ def get_block(
501510
def get_transaction(self, transaction_hash: _Hash32) -> TxData:
502511
return self._get_transaction(transaction_hash)
503512

513+
def get_raw_transaction(self, transaction_hash: _Hash32) -> _Hash32:
514+
return self._get_raw_transaction(transaction_hash)
515+
504516
def getTransactionFromBlock(
505517
self, block_identifier: BlockIdentifier, transaction_index: int
506518
) -> NoReturn:

web3/manager.py

+7-1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@
1717
from eth_utils.toolz import (
1818
pipe,
1919
)
20+
from hexbytes import (
21+
HexBytes,
22+
)
2023

2124
from web3._utils.decorators import (
2225
deprecated_for,
@@ -53,6 +56,9 @@
5356
from web3 import Web3 # noqa: F401
5457

5558

59+
NULL_RESPONSES = [None, HexBytes('0x'), '0x']
60+
61+
5662
def apply_error_formatters(
5763
error_formatters: Callable[..., Any],
5864
response: RPCResponse,
@@ -160,7 +166,7 @@ def formatted_response(
160166
if "error" in response:
161167
apply_error_formatters(error_formatters, response)
162168
raise ValueError(response["error"])
163-
elif response['result'] is None:
169+
elif response['result'] in NULL_RESPONSES:
164170
# null_result_formatters raise either a BlockNotFound
165171
# or a TransactionNotFound error, depending on the method called
166172
apply_null_result_formatters(null_result_formatters, response, params)

web3/middleware/cache.py

+1
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@
6565
'eth_getTransactionByBlockHashAndIndex',
6666
# 'eth_getTransactionByBlockNumberAndIndex',
6767
# 'eth_getTransactionReceipt',
68+
'eth_getRawTransactionByHash',
6869
'eth_getUncleByBlockHashAndIndex',
6970
# 'eth_getUncleByBlockNumberAndIndex',
7071
# 'eth_getCompilers',

web3/middleware/exception_retry_request.py

+1
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
'eth_getTransactionByBlockNumberAndIndex',
5252
'eth_getTransactionReceipt',
5353
'eth_getTransactionCount',
54+
'eth_getRawTransactionByHash',
5455
'eth_call',
5556
'eth_estimateGas',
5657
'eth_newBlockFilter',

0 commit comments

Comments
 (0)