Skip to content

Commit 11ab07d

Browse files
echonet: refactor alchemy get_block_by_number, return raw value (#10659)
1 parent 54b1163 commit 11ab07d

File tree

3 files changed

+51
-54
lines changed

3 files changed

+51
-54
lines changed

echonet/l1_client.py

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -119,22 +119,21 @@ def get_block_by_number(self, block_number: str) -> Optional[Dict]:
119119
}
120120

121121
request_func = functools.partial(requests.post, self.rpc_url, json=payload)
122-
result = self._run_request_with_retry(
122+
return self._run_request_with_retry(
123123
request_func=request_func,
124124
additional_log_context={"url": self.rpc_url, "block_number": block_number},
125125
)
126126

127-
if result is None:
128-
return None
129-
130-
return result.get("result")
131-
132127
def get_timestamp_of_block(self, block_number: str) -> Optional[int]:
133128
"""
134129
Get block timestamp by block number using eth_getBlockByNumber RPC method.
135130
Tries up to retries_count times. On failure, logs an error and returns None.
136131
"""
137-
block = self.get_block_by_number(block_number)
132+
response = self.get_block_by_number(block_number)
133+
if response is None:
134+
return None
135+
136+
block = response.get("result")
138137
if block is None:
139138
# Block not found
140139
return None

echonet/tests/test_l1_client.py

Lines changed: 18 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -57,82 +57,45 @@ def test_get_logs_when_rpc_result_is_empty(self, mock_post):
5757
self.assertEqual(mock_post.call_count, 1)
5858
self.assertEqual(logs, empty_response)
5959

60-
@patch("l1_client.requests.post")
61-
def test_get_timestamp_of_block_retries_after_failure_and_succeeds(self, mock_post):
62-
request_exception = requests.RequestException("some error")
63-
64-
successful_response = Mock()
65-
successful_response.raise_for_status.return_value = None
66-
successful_response.json.return_value = {"result": {"timestamp": "0x20"}} # 32
67-
68-
mock_post.side_effect = [request_exception, successful_response]
69-
70-
client = L1Client(api_key="api_key")
71-
result = client.get_timestamp_of_block(
72-
block_number=L1TestUtils.BLOCK_NUMBER_HEX,
73-
)
74-
75-
self.assertEqual(mock_post.call_count, 2)
76-
self.assertEqual(result, 32)
77-
78-
@patch("l1_client.requests.post")
79-
def test_get_timestamp_of_block_returns_none_when_rpc_result_is_empty(self, mock_post):
80-
response_ok = Mock()
81-
response_ok.raise_for_status.return_value = None
82-
response_ok.json.return_value = {"result": None}
83-
84-
mock_post.return_value = response_ok
85-
86-
client = L1Client(api_key="api_key")
87-
result = client.get_timestamp_of_block(
88-
block_number=L1TestUtils.BLOCK_NUMBER_HEX,
89-
)
90-
91-
self.assertEqual(mock_post.call_count, 1)
92-
self.assertIsNone(result)
93-
9460
@patch("l1_client.requests.post")
9561
def test_get_block_by_number_retries_after_failure_and_succeeds(self, mock_post):
9662
request_exception = requests.RequestException("some error")
97-
block_info = {
98-
"number": L1TestUtils.BLOCK_NUMBER_HEX,
99-
"timestamp": "0x5f5e100",
100-
}
10163

10264
successful_response = Mock()
10365
successful_response.raise_for_status.return_value = None
104-
successful_response.json.return_value = {"result": block_info}
66+
successful_response.json.return_value = L1TestUtils.BLOCK_RPC_RESPONSE
10567

10668
mock_post.side_effect = [request_exception, successful_response]
10769

10870
client = L1Client(api_key="api_key")
10971
result = client.get_block_by_number(L1TestUtils.BLOCK_NUMBER_HEX)
11072

11173
self.assertEqual(mock_post.call_count, 2)
112-
self.assertEqual(result, block_info)
74+
self.assertEqual(result, L1TestUtils.BLOCK_RPC_RESPONSE)
11375

11476
@patch("l1_client.requests.post")
11577
def test_get_block_by_number_returns_none_when_rpc_result_is_empty(self, mock_post):
78+
empty_response = L1TestUtils.block_rpc_response_with_block(None)
11679
response_ok = Mock()
11780
response_ok.raise_for_status.return_value = None
118-
response_ok.json.return_value = {"result": None}
81+
response_ok.json.return_value = empty_response
11982

12083
mock_post.return_value = response_ok
12184

12285
client = L1Client(api_key="api_key")
12386
result = client.get_block_by_number(block_number=L1TestUtils.BLOCK_NUMBER_HEX)
12487

12588
self.assertEqual(mock_post.call_count, 1)
126-
self.assertIsNone(result)
89+
self.assertEqual(result, empty_response)
12790

12891
@patch.object(L1Client, "get_block_by_number")
12992
def test_get_timestamp_of_block_returns_int_timestamp(self, mock_get_block_by_number):
130-
mock_get_block_by_number.return_value = {"timestamp": "0x5f5e100"}
93+
mock_get_block_by_number.return_value = L1TestUtils.BLOCK_RPC_RESPONSE
13194

13295
client = L1Client(api_key="api_key")
13396
result = client.get_timestamp_of_block(L1TestUtils.BLOCK_NUMBER_HEX)
13497

135-
self.assertEqual(result, int("0x5f5e100", 16))
98+
self.assertEqual(result, L1TestUtils.BLOCK_TIMESTAMP)
13699
mock_get_block_by_number.assert_called_once_with(L1TestUtils.BLOCK_NUMBER_HEX)
137100

138101
@patch.object(L1Client, "get_block_by_number")
@@ -146,6 +109,17 @@ def test_get_timestamp_of_block_returns_none_when_block_not_found(
146109

147110
self.assertIsNone(result)
148111

112+
@patch.object(L1Client, "get_block_by_number")
113+
def test_get_timestamp_of_block_returns_none_when_result_is_none(
114+
self, mock_get_block_by_number
115+
):
116+
mock_get_block_by_number.return_value = L1TestUtils.block_rpc_response_with_block(None)
117+
118+
client = L1Client(api_key="api_key")
119+
result = client.get_timestamp_of_block(block_number=L1TestUtils.BLOCK_NUMBER_HEX)
120+
121+
self.assertIsNone(result)
122+
149123
def test_decode_log_success(self):
150124
result = L1Client.decode_log_response(L1TestUtils.LOG)
151125

echonet/tests/test_utils.py

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ class L1TestUtils:
77

88
BLOCK_NUMBER = 23911042
99
BLOCK_NUMBER_HEX = hex(BLOCK_NUMBER)
10+
BLOCK_TIMESTAMP = 1764500447
11+
BLOCK_TIMESTAMP_HEX = hex(BLOCK_TIMESTAMP)
1012
BLOCK_RANGE = [BLOCK_NUMBER - 10, BLOCK_NUMBER + 10]
1113
NONCE = 0x19B255
1214

@@ -30,7 +32,7 @@ class L1TestUtils:
3032
"0000000000000000000000000000000000000000000000000000000000000000",
3133
"blockHash": "0xb33512d13e1a2ff4f3aa6e799a4a2455249be5198760a3f41300a8362d802bf8",
3234
"blockNumber": BLOCK_NUMBER_HEX,
33-
"blockTimestamp": "0x692c23df",
35+
"blockTimestamp": BLOCK_TIMESTAMP_HEX,
3436
"transactionHash": "0x726df509fdd23a944f923a6fc18e80cbe7300a54aa34f8e6bd77e9961ca6ce52",
3537
"transactionIndex": "0x4f",
3638
"logIndex": "0x7b",
@@ -44,6 +46,19 @@ class L1TestUtils:
4446
"result": [LOG],
4547
}
4648

49+
# Block entry (the "result" content)
50+
BLOCK = {
51+
"number": BLOCK_NUMBER_HEX,
52+
"timestamp": BLOCK_TIMESTAMP_HEX,
53+
}
54+
55+
# Full JSON-RPC response from get_block_by_number
56+
BLOCK_RPC_RESPONSE = {
57+
"jsonrpc": "2.0",
58+
"id": "1",
59+
"result": BLOCK,
60+
}
61+
4762
L1_EVENT = L1Client.L1Event(
4863
contract_address="0x616757a151c21f9be8775098d591c2807316d992bbc3bb1a5c1821630589256",
4964
entry_point_selector=0x1B64B1B3B690B43B9B514FB81377518F4039CD3E4F4914D8A6BDF01D679FB19,
@@ -58,8 +73,8 @@ class L1TestUtils:
5873
nonce=NONCE,
5974
fee=0x1308ABA4ADE2,
6075
l1_tx_hash="0x726df509fdd23a944f923a6fc18e80cbe7300a54aa34f8e6bd77e9961ca6ce52",
61-
block_timestamp=1764500447,
62-
block_number=23911042,
76+
block_timestamp=BLOCK_TIMESTAMP,
77+
block_number=BLOCK_NUMBER,
6378
)
6479

6580
# L1_HANDLER tx from feeder gateway, expected to match the L1_EVENT.
@@ -105,3 +120,12 @@ def logs_rpc_response_with_logs(logs: list) -> dict:
105120
"id": "1",
106121
"result": logs,
107122
}
123+
124+
@staticmethod
125+
def block_rpc_response_with_block(block: dict) -> dict:
126+
"""Returns a full JSON-RPC response with the given block."""
127+
return {
128+
"jsonrpc": "2.0",
129+
"id": "1",
130+
"result": block,
131+
}

0 commit comments

Comments
 (0)