from __future__ import annotations
from dataclasses import dataclass, field
from typing import Any, Dict, Generic, List, Mapping, Optional, TypeVar
_T = TypeVar("_T")
__all__ = [
"EventWrapper",
"event_wrapper_from_wire",
"CaptureWrapper",
"capture_wrapper_from_wire",
"VaultId",
"vault_id_from_wire",
"vault_id_patch_from_wire",
"VaultBalance",
"vault_balance_from_wire",
"vault_balance_patch_from_wire",
"Vault",
"vault_from_wire",
"vault_patch_from_wire",
]
def _snake_key(key: str) -> str:
out = []
for index, ch in enumerate(key):
if ch.isascii() and ch.isupper():
if index != 0:
out.append("_")
out.append(ch.lower())
else:
out.append(ch)
return "".join(out)
def _mapping(value: Any, context: str) -> Dict[str, Any]:
if not isinstance(value, Mapping):
raise TypeError(
f"{context} payload must be a mapping, got {type(value).__name__}"
)
return {_snake_key(key): item for key, item in value.items()}
def _to_int(value: Any) -> Optional[int]:
if value is None:
return None
if isinstance(value, bool):
return int(value)
if isinstance(value, int):
return value
if isinstance(value, (str, float)):
return int(value)
raise TypeError(f"Cannot convert {type(value).__name__} to int")
def _to_int_list(value: Any) -> Optional[List[Optional[int]]]:
if value is None:
return None
return [_to_int(item) for item in value]
def _convert(value: Any, converter: Any) -> Any:
if value is None:
return None
return converter(value)
def _convert_list(value: Any, converter: Any) -> Any:
if value is None:
return None
return [converter(item) for item in value]
def _convert_capture(value: Any, converter: Any) -> Any:
if value is None:
return None
return capture_wrapper_from_wire(value, converter)
def _convert_capture_list(value: Any, converter: Any) -> Any:
if value is None:
return None
return [capture_wrapper_from_wire(item, converter) for item in value]
def _convert_event(value: Any, converter: Any) -> Any:
if value is None:
return None
return event_wrapper_from_wire(value, converter)
def _convert_event_list(value: Any, converter: Any) -> Any:
if value is None:
return None
return [event_wrapper_from_wire(item, converter) for item in value]
def _require(data: Mapping[str, Any], key: str, context: str) -> Any:
if key not in data:
raise ValueError(f"{context} payload is missing required field '{key}'")
return data[key]
@dataclass
class EventWrapper(Generic[_T]):
timestamp: int = 0
data: Optional[_T] = None
slot: Optional[int] = None
signature: Optional[str] = None
event_index: Optional[int] = None
ix_path: Optional[str] = None
def event_wrapper_from_wire(value: Any, converter: Any = None) -> EventWrapper:
data = _mapping(value, "EventWrapper")
inner = data.get("data")
return EventWrapper(
timestamp=_to_int(data.get("timestamp")) or 0,
data=_convert(inner, converter) if converter is not None else inner,
slot=_to_int(data.get("slot")),
signature=data.get("signature"),
event_index=_to_int(data.get("event_index")),
ix_path=data.get("ix_path"),
)
@dataclass
class CaptureWrapper(Generic[_T]):
timestamp: int = 0
account_address: Optional[str] = None
data: Optional[_T] = None
slot: Optional[int] = None
signature: Optional[str] = None
def capture_wrapper_from_wire(value: Any, converter: Any = None) -> CaptureWrapper:
data = _mapping(value, "CaptureWrapper")
inner = data.get("data")
return CaptureWrapper(
timestamp=_to_int(data.get("timestamp")) or 0,
account_address=data.get("account_address"),
data=_convert(inner, converter) if converter is not None else inner,
slot=_to_int(data.get("slot")),
signature=data.get("signature"),
)
@dataclass
class VaultId:
address: Optional[str] = None
def vault_id_from_wire(value: Any) -> VaultId:
data = _mapping(value, "VaultId")
return VaultId(
address=data.get("address"),
)
def vault_id_patch_from_wire(value: Any) -> Dict[str, Any]:
data = _mapping(value, "VaultId patch")
out: Dict[str, Any] = {}
if "address" in data:
out["address"] = data["address"]
return out
@dataclass
class VaultBalance:
amount: Optional[int] = None
def vault_balance_from_wire(value: Any) -> VaultBalance:
data = _mapping(value, "VaultBalance")
return VaultBalance(
amount=_to_int(data.get("amount")),
)
def vault_balance_patch_from_wire(value: Any) -> Dict[str, Any]:
data = _mapping(value, "VaultBalance patch")
out: Dict[str, Any] = {}
if "amount" in data:
out["amount"] = _to_int(data["amount"])
return out
@dataclass
class Vault:
id: VaultId = field(default_factory=VaultId)
balance: VaultBalance = field(default_factory=VaultBalance)
def vault_from_wire(value: Any) -> Vault:
data = _mapping(value, "Vault")
return Vault(
id=vault_id_from_wire(data.get("id") or {}),
balance=vault_balance_from_wire(data.get("balance") or {}),
)
def vault_patch_from_wire(value: Any) -> Dict[str, Any]:
data = _mapping(value, "Vault patch")
out: Dict[str, Any] = {}
if "id" in data:
out["id"] = vault_id_patch_from_wire(data["id"] or {})
if "balance" in data:
out["balance"] = vault_balance_patch_from_wire(data["balance"] or {})
return out