Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
__pycache__
.ruff_cache

.vercel
*.vercel
Expand All @@ -11,4 +12,4 @@ venv

api/test
endpoints*.json
endpoints
endpoints
29 changes: 29 additions & 0 deletions api/read/arbitrum.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
from common.metrics_handler import BaseVercelHandler, MetricsHandler
from config.defaults import MetricsServiceConfig
from metrics.arbitrum import (
HTTPAccBalanceLatencyMetric,
HTTPBlockNumberLatencyMetric,
HTTPDebugTraceBlockByNumberLatencyMetric,
HTTPDebugTraceTxLatencyMetric,
HTTPEthCallLatencyMetric,
HTTPTxReceiptLatencyMetric,
)
from metrics.ethereum import (
WSBlockLatencyMetric,
)

metric_name = f"{MetricsServiceConfig.METRIC_PREFIX}response_latency_seconds"

METRICS = [
(WSBlockLatencyMetric, metric_name),
(HTTPBlockNumberLatencyMetric, metric_name),
(HTTPEthCallLatencyMetric, metric_name),
(HTTPAccBalanceLatencyMetric, metric_name),
(HTTPDebugTraceBlockByNumberLatencyMetric, metric_name),
(HTTPDebugTraceTxLatencyMetric, metric_name),
(HTTPTxReceiptLatencyMetric, metric_name),
]


class handler(BaseVercelHandler):
metrics_handler = MetricsHandler("Arbitrum", METRICS)
29 changes: 29 additions & 0 deletions api/read/bnbsc.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
from common.metrics_handler import BaseVercelHandler, MetricsHandler
from config.defaults import MetricsServiceConfig
from metrics.bnbsc import (
HTTPAccBalanceLatencyMetric,
HTTPBlockNumberLatencyMetric,
HTTPDebugTraceBlockByNumberLatencyMetric,
HTTPDebugTraceTxLatencyMetric,
HTTPEthCallLatencyMetric,
HTTPTxReceiptLatencyMetric,
)
from metrics.ethereum import (
WSBlockLatencyMetric,
)

metric_name = f"{MetricsServiceConfig.METRIC_PREFIX}response_latency_seconds"

METRICS = [
(WSBlockLatencyMetric, metric_name),
(HTTPBlockNumberLatencyMetric, metric_name),
(HTTPEthCallLatencyMetric, metric_name),
(HTTPAccBalanceLatencyMetric, metric_name),
(HTTPDebugTraceBlockByNumberLatencyMetric, metric_name),
(HTTPDebugTraceTxLatencyMetric, metric_name),
(HTTPTxReceiptLatencyMetric, metric_name),
]


class handler(BaseVercelHandler):
metrics_handler = MetricsHandler("BNB Smart Chain", METRICS)
9 changes: 8 additions & 1 deletion api/support/update_state.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,14 @@
from common.state.blockchain_fetcher import BlockchainData, BlockchainDataFetcher
from common.state.blockchain_state import BlockchainState

SUPPORTED_BLOCKCHAINS: list[str] = ["ethereum", "solana", "ton", "base"]
SUPPORTED_BLOCKCHAINS: list[str] = [
"ethereum",
"solana",
"ton",
"base",
"arbitrum",
"bnb smart chain",
]
ALLOWED_PROVIDERS: set[str] = {
"Chainstack"
} # To reduce number of RPC calls, use only one provider here
Expand Down
2 changes: 1 addition & 1 deletion common/metrics_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ async def handle(self) -> tuple[str, str]:
if metrics_text:
await self.push_to_grafana(metrics_text)
else:
logging.warning("Noting to push to Grafana.")
logging.warning("Nothing to push to Grafana.")

return "done", metrics_text

Expand Down
15 changes: 11 additions & 4 deletions common/state/blockchain_fetcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ async def _fetch_evm_data(self, blockchain: str) -> BlockchainData:

latest_number = int(latest_block["number"], 16)
offset_range = MetricsServiceConfig.BLOCK_OFFSET_RANGES.get(
blockchain.lower(), (20, 100)
blockchain.lower(), (7200, 14400)
)
offset = random.randint(offset_range[0], offset_range[1])
old_number = max(0, latest_number - offset)
Expand Down Expand Up @@ -162,7 +162,7 @@ async def _fetch_solana_data(self) -> BlockchainData:
tx_sig = latest_block.get("signatures", [""])[0]

offset_range = MetricsServiceConfig.BLOCK_OFFSET_RANGES.get(
"solana", (100, 1000)
"solana", (432000, 648000)
)
offset = random.randint(offset_range[0], offset_range[1])
target_slot = max(0, actual_latest_slot - offset)
Expand All @@ -189,7 +189,9 @@ async def _fetch_ton_data(self) -> BlockchainData:
if not isinstance(last_block, dict):
return BlockchainData.empty()

offset_range = MetricsServiceConfig.BLOCK_OFFSET_RANGES.get("ton", (10, 50))
offset_range = MetricsServiceConfig.BLOCK_OFFSET_RANGES.get(
"ton", (1555200, 1572480)
)
offset = random.randint(offset_range[0], offset_range[1])
old_seqno = max(0, last_block["seqno"] - offset)

Expand Down Expand Up @@ -229,7 +231,12 @@ async def _fetch_ton_data(self) -> BlockchainData:
async def fetch_latest_data(self, blockchain: str) -> BlockchainData:
"""Fetches latest block and transaction data for specified blockchain."""
try:
if blockchain.lower() in ("ethereum", "base"):
if blockchain.lower() in (
"ethereum",
"base",
"arbitrum",
"bnb smart chain",
):
return await self._fetch_evm_data(blockchain)
elif blockchain.lower() == "solana":
return await self._fetch_solana_data()
Expand Down
2 changes: 2 additions & 0 deletions config/defaults.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ class MetricsServiceConfig:
"base": (7200, 14400),
"solana": (432000, 648000),
"ton": (1555200, 1572480),
"arbitrum": (7200, 14400),
"bnb smart chain": (7200, 14400),
}


Expand Down
102 changes: 102 additions & 0 deletions metrics/arbitrum.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
"""Base EVM metrics implementation for HTTP endpoints."""

from common.metric_types import HttpCallLatencyMetricBase


class HTTPEthCallLatencyMetric(HttpCallLatencyMetricBase):
"""Collects response time for eth_call simulation."""

@property
def method(self) -> str:
return "eth_call"

@staticmethod
def get_params_from_state(state_data: dict) -> list:
"""Get eth_call parameters for Aave Pool Addresses Provider query."""
return [
{
"to": "0xa97684ead0e402dC232d5A977953DF7ECBaB3CDb",
"data": "0x026b1d5f0000000000000000000000000000000000000000000000000000000000000000",
},
"latest",
]


class HTTPTxReceiptLatencyMetric(HttpCallLatencyMetricBase):
"""Collects latency for transaction receipt retrieval."""

@property
def method(self) -> str:
return "eth_getTransactionReceipt"

@staticmethod
def validate_state(state_data: dict) -> bool:
"""Validate blockchain state contains transaction hash."""
return bool(state_data and state_data.get("tx"))

@staticmethod
def get_params_from_state(state_data: dict) -> list:
"""Get parameters using transaction hash from state."""
return [state_data["tx"]]


class HTTPAccBalanceLatencyMetric(HttpCallLatencyMetricBase):
"""Collects latency for account balance queries."""

@property
def method(self) -> str:
return "eth_getBalance"

@staticmethod
def validate_state(state_data: dict) -> bool:
"""Validates that required block number (hex) exists in state data."""
return bool(state_data and state_data.get("old_block"))

@staticmethod
def get_params_from_state(state_data: dict) -> list:
"""Get parameters with fixed monitoring address."""
return ["0x794a61358D6845594F94dc1DB02A252b5b4814aD", state_data["old_block"]]


class HTTPDebugTraceTxLatencyMetric(HttpCallLatencyMetricBase):
"""Collects latency for transaction tracing."""

@property
def method(self) -> str:
return "debug_traceTransaction"

@staticmethod
def validate_state(state_data: dict) -> bool:
"""Validate blockchain state contains transaction hash."""
return bool(state_data and state_data.get("tx"))

@staticmethod
def get_params_from_state(state_data: dict) -> list:
"""Get parameters using transaction hash from state."""
return [state_data["tx"], {"tracer": "callTracer"}]


class HTTPDebugTraceBlockByNumberLatencyMetric(HttpCallLatencyMetricBase):
"""Collects call latency for the `debug_traceBlockByNumber` method."""

@property
def method(self) -> str:
return "debug_traceBlockByNumber"

@staticmethod
def get_params_from_state(state_data: dict) -> list:
"""Get fixed parameters for latest block tracing."""
return ["latest", {"tracer": "callTracer"}]


class HTTPBlockNumberLatencyMetric(HttpCallLatencyMetricBase):
"""Collects call latency for the `eth_blockNumber` method."""

@property
def method(self) -> str:
return "eth_blockNumber"

@staticmethod
def get_params_from_state(state_data: dict) -> list:
"""Get empty parameter list for block number query."""
return []
102 changes: 102 additions & 0 deletions metrics/bnbsc.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
"""Base EVM metrics implementation for HTTP endpoints."""

from common.metric_types import HttpCallLatencyMetricBase


class HTTPEthCallLatencyMetric(HttpCallLatencyMetricBase):
"""Collects response time for eth_call simulation."""

@property
def method(self) -> str:
return "eth_call"

@staticmethod
def get_params_from_state(state_data: dict) -> list:
"""Get eth_call parameters for Aave Pool Addresses Provider query."""
return [
{
"to": "0xff75B6da14FfbbfD355Daf7a2731456b3562Ba6D",
"data": "0x026b1d5f0000000000000000000000000000000000000000000000000000000000000000",
},
"latest",
]


class HTTPTxReceiptLatencyMetric(HttpCallLatencyMetricBase):
"""Collects latency for transaction receipt retrieval."""

@property
def method(self) -> str:
return "eth_getTransactionReceipt"

@staticmethod
def validate_state(state_data: dict) -> bool:
"""Validate blockchain state contains transaction hash."""
return bool(state_data and state_data.get("tx"))

@staticmethod
def get_params_from_state(state_data: dict) -> list:
"""Get parameters using transaction hash from state."""
return [state_data["tx"]]


class HTTPAccBalanceLatencyMetric(HttpCallLatencyMetricBase):
"""Collects latency for account balance queries."""

@property
def method(self) -> str:
return "eth_getBalance"

@staticmethod
def validate_state(state_data: dict) -> bool:
"""Validates that required block number (hex) exists in state data."""
return bool(state_data and state_data.get("old_block"))

@staticmethod
def get_params_from_state(state_data: dict) -> list:
"""Get parameters with fixed monitoring address."""
return ["0x6807dc923806fE8Fd134338EABCA509979a7e0cB", state_data["old_block"]]


class HTTPDebugTraceTxLatencyMetric(HttpCallLatencyMetricBase):
"""Collects latency for transaction tracing."""

@property
def method(self) -> str:
return "debug_traceTransaction"

@staticmethod
def validate_state(state_data: dict) -> bool:
"""Validate blockchain state contains transaction hash."""
return bool(state_data and state_data.get("tx"))

@staticmethod
def get_params_from_state(state_data: dict) -> list:
"""Get parameters using transaction hash from state."""
return [state_data["tx"], {"tracer": "callTracer"}]


class HTTPDebugTraceBlockByNumberLatencyMetric(HttpCallLatencyMetricBase):
"""Collects call latency for the `debug_traceBlockByNumber` method."""

@property
def method(self) -> str:
return "debug_traceBlockByNumber"

@staticmethod
def get_params_from_state(state_data: dict) -> list:
"""Get fixed parameters for latest block tracing."""
return ["latest", {"tracer": "callTracer"}]


class HTTPBlockNumberLatencyMetric(HttpCallLatencyMetricBase):
"""Collects call latency for the `eth_blockNumber` method."""

@property
def method(self) -> str:
return "eth_blockNumber"

@staticmethod
def get_params_from_state(state_data: dict) -> list:
"""Get empty parameter list for block number query."""
return []
2 changes: 1 addition & 1 deletion tests/test_api_read.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ def main() -> None:
setup_environment()

# Import handler after environment setup
from api.read.ton import handler
from api.read.bnbsc import handler

server = HTTPServer(("localhost", 8000), handler)
print("Server started at http://localhost:8000")
Expand Down
Loading