Skip to content

Commit 982aa06

Browse files
committed
feat(evm): add hyperliquid function
1 parent 7638df9 commit 982aa06

File tree

6 files changed

+173
-1
lines changed

6 files changed

+173
-1
lines changed

api/read/hyperliquid.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import os # noqa: D100
2+
3+
from common.metrics_handler import BaseVercelHandler, MetricsHandler
4+
from config.defaults import MetricsServiceConfig
5+
from metrics.arbitrum import (
6+
HTTPAccBalanceLatencyMetric,
7+
HTTPBlockNumberLatencyMetric,
8+
HTTPDebugTraceBlockByNumberLatencyMetric,
9+
HTTPDebugTraceTxLatencyMetric,
10+
HTTPEthCallLatencyMetric,
11+
HTTPGetLogsLatencyMetric,
12+
HTTPTxReceiptLatencyMetric,
13+
)
14+
15+
METRIC_NAME = f"{MetricsServiceConfig.METRIC_PREFIX}response_latency_seconds"
16+
ALLOWED_REGIONS: list[str] = [
17+
"fra1", # Frankfurt (EU)
18+
"sfo1", # San Francisco (US West)
19+
# "sin1", # Singapore
20+
# "kix1", # Osaka (JP)
21+
"hnd1", # Tokyo (JP)
22+
]
23+
24+
METRICS = (
25+
[
26+
(HTTPBlockNumberLatencyMetric, METRIC_NAME),
27+
(HTTPEthCallLatencyMetric, METRIC_NAME),
28+
(HTTPAccBalanceLatencyMetric, METRIC_NAME),
29+
(HTTPDebugTraceBlockByNumberLatencyMetric, METRIC_NAME),
30+
(HTTPDebugTraceTxLatencyMetric, METRIC_NAME),
31+
(HTTPTxReceiptLatencyMetric, METRIC_NAME),
32+
(HTTPGetLogsLatencyMetric, METRIC_NAME),
33+
]
34+
if os.getenv("VERCEL_REGION") in ALLOWED_REGIONS # System env var, standard name
35+
else []
36+
)
37+
38+
39+
class handler(BaseVercelHandler):
40+
metrics_handler = MetricsHandler("Hyperliquid", METRICS)

api/support/update_state.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
"base",
2020
"arbitrum",
2121
"bnb",
22+
"hyperliquid",
2223
]
2324
ALLOWED_PROVIDERS: set[str] = {
2425
"Chainstack"

common/state/blockchain_fetcher.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,7 @@ async def fetch_latest_data(self, blockchain: str) -> BlockchainData:
236236
"base",
237237
"arbitrum",
238238
"bnb",
239+
"hyperliquid",
239240
):
240241
return await self._fetch_evm_data(blockchain)
241242
elif blockchain.lower() == "solana":

config/defaults.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ class MetricsServiceConfig:
3939
"ton": (1555200, 1572480),
4040
"arbitrum": (7200, 10000),
4141
"bnb": (7200, 10000),
42+
"hyperliquid": (3600, 7200),
4243
}
4344

4445

metrics/hyperliquid.py

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
"""Hyperliquid EVM metrics implementation for HTTP endpoints."""
2+
3+
from common.metric_types import HttpCallLatencyMetricBase
4+
5+
6+
class HTTPEthCallLatencyMetric(HttpCallLatencyMetricBase):
7+
"""Collects response time for eth_call simulation."""
8+
9+
@property
10+
def method(self) -> str:
11+
return "eth_call"
12+
13+
@staticmethod
14+
def get_params_from_state(state_data: dict) -> list:
15+
"""Get eth_call parameters for Wrapped HYPE total supply query."""
16+
return [
17+
{
18+
"to": "0x5555555555555555555555555555555555555555",
19+
"data": "0x18160ddd",
20+
},
21+
"latest",
22+
]
23+
24+
25+
class HTTPTxReceiptLatencyMetric(HttpCallLatencyMetricBase):
26+
"""Collects latency for transaction receipt retrieval."""
27+
28+
@property
29+
def method(self) -> str:
30+
return "eth_getTransactionReceipt"
31+
32+
@staticmethod
33+
def validate_state(state_data: dict) -> bool:
34+
"""Validate blockchain state contains transaction hash."""
35+
return bool(state_data and state_data.get("tx"))
36+
37+
@staticmethod
38+
def get_params_from_state(state_data: dict) -> list:
39+
"""Get parameters using transaction hash from state."""
40+
return [state_data["tx"]]
41+
42+
43+
class HTTPAccBalanceLatencyMetric(HttpCallLatencyMetricBase):
44+
"""Collects latency for account balance queries."""
45+
46+
@property
47+
def method(self) -> str:
48+
return "eth_getBalance"
49+
50+
@staticmethod
51+
def validate_state(state_data: dict) -> bool:
52+
"""Validates that required block number (hex) exists in state data."""
53+
return bool(state_data and state_data.get("old_block"))
54+
55+
@staticmethod
56+
def get_params_from_state(state_data: dict) -> list:
57+
"""Get parameters with fixed monitoring address."""
58+
return ["0xFC1286EeddF81d6955eDAd5C8D99B8Aa32F3D2AA", state_data["old_block"]]
59+
60+
61+
class HTTPDebugTraceTxLatencyMetric(HttpCallLatencyMetricBase):
62+
"""Collects latency for transaction tracing."""
63+
64+
@property
65+
def method(self) -> str:
66+
return "debug_traceTransaction"
67+
68+
@staticmethod
69+
def validate_state(state_data: dict) -> bool:
70+
"""Validate blockchain state contains transaction hash."""
71+
return bool(state_data and state_data.get("tx"))
72+
73+
@staticmethod
74+
def get_params_from_state(state_data: dict) -> list:
75+
"""Get parameters using transaction hash from state."""
76+
return [state_data["tx"], {"tracer": "callTracer"}]
77+
78+
79+
class HTTPDebugTraceBlockByNumberLatencyMetric(HttpCallLatencyMetricBase):
80+
"""Collects call latency for the `debug_traceBlockByNumber` method."""
81+
82+
@property
83+
def method(self) -> str:
84+
return "debug_traceBlockByNumber"
85+
86+
@staticmethod
87+
def get_params_from_state(state_data: dict) -> list:
88+
"""Get fixed parameters for latest block tracing."""
89+
return ["latest", {"tracer": "callTracer"}]
90+
91+
92+
class HTTPBlockNumberLatencyMetric(HttpCallLatencyMetricBase):
93+
"""Collects call latency for the `eth_blockNumber` method."""
94+
95+
@property
96+
def method(self) -> str:
97+
return "eth_blockNumber"
98+
99+
@staticmethod
100+
def get_params_from_state(state_data: dict) -> list:
101+
"""Get empty parameter list for block number query."""
102+
return []
103+
104+
105+
class HTTPGetLogsLatencyMetric(HttpCallLatencyMetricBase):
106+
"""Collects call latency for the eth_getLogs method."""
107+
108+
@property
109+
def method(self) -> str:
110+
return "eth_getLogs"
111+
112+
@staticmethod
113+
def get_params_from_state(state_data: dict) -> list:
114+
"""Get parameters for USDC transfer logs from recent block range."""
115+
from_block_hex = state_data["old_block"]
116+
from_block_int = int(from_block_hex, 16)
117+
to_block_int: int = max(0, from_block_int + 100)
118+
to_block_hex: str = hex(to_block_int)
119+
120+
return [
121+
{
122+
"fromBlock": from_block_hex,
123+
"toBlock": to_block_hex,
124+
"address": "0x5555555555555555555555555555555555555555", # Wrapped HYPE
125+
"topics": [
126+
" 0x7fcf532c15f0a6db0bd6d0e038bea71d30d808c7d98cb3bf7268a95bf5081b65" # Withdrawal event
127+
],
128+
}
129+
]

tests/test_api_read.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ def main() -> None:
2929
setup_environment()
3030

3131
# Import handler after environment setup
32-
from api.read.ethereum import handler
32+
from api.read.hyperliquid import handler
3333

3434
server = HTTPServer(("localhost", 8000), handler)
3535
print("Server started at http://localhost:8000")

0 commit comments

Comments
 (0)