-
Notifications
You must be signed in to change notification settings - Fork 9
Expand file tree
/
Copy pathutils.py
More file actions
128 lines (102 loc) · 3.49 KB
/
utils.py
File metadata and controls
128 lines (102 loc) · 3.49 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
import re
from typing import Any
TRANSACTION_KEYS_TO_KEEP = [
"hash",
"block_number",
"block_timestamp",
"from_address",
"to_address",
"value",
"decodedData",
]
EVENT_KEYS_TO_KEEP = [
"block_number",
"block_timestamp",
"address",
"transaction_hash",
"transaction_index",
"log_index",
"topics",
"data",
"decodedData",
]
def extract_digits(value: int | str) -> int:
"""Extract the integer value from a string or return the integer directly."""
if isinstance(value, int):
return value
value_str = str(value).strip("\"'")
digit_match = re.search(r"\d+", value_str)
if not digit_match:
raise ValueError(f"Input '{value}' does not contain any digits")
extracted_digits = digit_match.group()
if not extracted_digits.isdigit():
raise ValueError(f"Extracted value '{extracted_digits}' is not a valid digit string")
return int(extracted_digits)
def is_encoded(encoded_data: str) -> bool:
"""Check if a string is a valid hexadecimal value."""
encoded_data = encoded_data.removeprefix("0x")
try:
bytes.fromhex(encoded_data)
return True
except ValueError:
return False
def clean_resolve(out: dict[str, Any]):
"""Clean the response from the resolve function."""
if "transactions" in out["data"]:
for transaction in out["data"]["transactions"]:
for key in list(transaction.keys()):
if key not in TRANSACTION_KEYS_TO_KEEP:
transaction.pop(key, None)
if "events" in out["data"]:
for event in out["data"]["events"]:
for key in list(event.keys()):
if key not in EVENT_KEYS_TO_KEEP:
event.pop(key, None)
return out
def filter_response_keys(items: list[dict[str, Any]], keys_to_keep: list[str] | None) -> list[dict[str, Any]]:
"""Filter the response items to only include the specified keys"""
if not keys_to_keep:
return items
for item in items:
keys_to_remove = [key for key in item if key not in keys_to_keep]
for key in keys_to_remove:
item.pop(key, None)
return items
# Aggregation function validation
VALID_EVENT_AGGREGATIONS = [
"count()",
"countDistinct(address)",
"countDistinct(contract_address)",
"countDistinct(transaction_hash)",
"min(block_number)",
"max(block_number)",
"min(block_timestamp)",
"max(block_timestamp)",
]
VALID_TRANSACTION_AGGREGATIONS = [
"count()",
"countDistinct(from_address)",
"countDistinct(to_address)",
"countDistinct(contract_address)",
"sum(value)",
"avg(value)",
"min(value)",
"max(value)",
"min(block_number)",
"max(block_number)",
"min(block_timestamp)",
"max(block_timestamp)",
]
def validate_aggregation(agg: str, valid_aggregations: list[str]) -> str:
"""Validate an aggregation function string."""
# Handle aliases like "count() as event_count"
base_agg = agg.split(" as ")[0].strip()
if base_agg not in valid_aggregations:
raise ValueError(f"Invalid aggregation function: {base_agg}. Valid options: {valid_aggregations}")
return agg
def validate_event_aggregation(agg: str) -> str:
"""Validate an event aggregation function."""
return validate_aggregation(agg, VALID_EVENT_AGGREGATIONS)
def validate_transaction_aggregation(agg: str) -> str:
"""Validate a transaction aggregation function."""
return validate_aggregation(agg, VALID_TRANSACTION_AGGREGATIONS)