Base Infrastructure¶
Shared base classes and utilities used by all connectors.
BaseReceiptParser¶
almanak.framework.connectors.base.BaseReceiptParser
¶
Bases: ABC
Abstract base class for receipt parsers using template method pattern.
This class implements the common receipt parsing flow and provides hook methods for protocol-specific customization. Subclasses must implement: - _decode_log_data(): Protocol-specific log decoding - _create_event(): Create protocol-specific event object - _build_result(): Build protocol-specific result object
The template method parse_receipt() handles: - Transaction status validation - Log iteration and filtering - Event creation and collection - Error handling
Attributes:
| Name | Type | Description |
|---|---|---|
registry |
EventRegistry for topic lookups (optional) |
|
known_topics |
Set of known topic signatures (optional) |
|
SUPPORTED_EXTRACTIONS |
frozenset[str]
|
Class-level frozenset declaring which extraction fields this parser supports (e.g., {"swap_amounts", "position_id"}). Used by ResultEnricher to warn when expected fields are unsupported. |
Example
from almanak.framework.connectors.base import BaseReceiptParser
class MyProtocolParser(BaseReceiptParser[MyEvent, MyResult]): ... def init(self): ... super().init(registry=my_registry) ... ... def _decode_log_data(self, event_name, topics, data, contract_address): ... # Decode protocol-specific log data ... if event_name == "Swap": ... return { ... "amount_in": HexDecoder.decode_uint256(data, 0), ... "amount_out": HexDecoder.decode_uint256(data, 32), ... } ... return {} ... ... def _create_event(self, event_name, log_index, tx_hash, ...): ... # Create protocol-specific event object ... return MyEvent(...) ... ... def _build_result(self, events, receipt, **kwargs): ... # Build protocol-specific result ... return MyResult(success=True, events=events)
Initialize the base parser.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
registry
|
EventRegistry | None
|
EventRegistry for topic lookups (optional) |
None
|
known_topics
|
set[str] | None
|
Set of known topic signatures (optional, used if no registry) |
None
|
parse_receipt
¶
Parse a transaction receipt (template method).
This is the main entry point that implements the template method pattern. It handles common parsing logic and calls hook methods for protocol-specific customization.
Flow: 1. Validate transaction status 2. Extract transaction metadata 3. Iterate through logs 4. For each log: a. Check if event is known b. Decode log data (via _decode_log_data hook) c. Create event object (via _create_event hook) 5. Build final result (via _build_result hook)
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
receipt
|
dict[str, Any]
|
Transaction receipt dict from web3.py containing: - transactionHash: Transaction hash (bytes or hex string) - blockNumber: Block number (int) - status: Transaction status (1=success, 0=reverted) - logs: List of log dicts |
required |
**kwargs
|
Additional protocol-specific parameters |
{}
|
Returns:
| Type | Description |
|---|---|
TResult
|
Protocol-specific result object |
Raises:
| Type | Description |
|---|---|
Exception
|
If parsing fails critically (caught and returned in result) |
EventRegistry¶
almanak.framework.connectors.base.EventRegistry
¶
Registry for managing event topic mappings.
This class manages the mapping between event topic signatures (keccak256 hashes) and event names/types. Provides fast lookup and validation methods.
Attributes:
| Name | Type | Description |
|---|---|---|
event_topics |
Mapping from event name to topic signature |
|
topic_to_event |
dict[str, str]
|
Reverse mapping from topic signature to event name |
event_type_map |
Mapping from event name to enum type |
|
known_topics |
Set of all known topic signatures (for fast lookup) |
Example
from enum import Enum from almanak.framework.connectors.base import EventRegistry
class MyEventType(Enum): ... SWAP = "SWAP" ... MINT = "MINT"
EVENT_TOPICS = { ... "Swap": "0xc42079...", ... "Mint": "0x7a5308...", ... }
EVENT_NAME_TO_TYPE = { ... "Swap": MyEventType.SWAP, ... "Mint": MyEventType.MINT, ... }
registry = EventRegistry(EVENT_TOPICS, EVENT_NAME_TO_TYPE) registry.get_event_name("0xc42079...") 'Swap' registry.get_event_type("Swap")
Initialize the event registry.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
event_topics
|
dict[str, str]
|
Mapping from event name to topic signature Example: {"Swap": "0xc42079...", "Mint": "0x7a5308..."} |
required |
event_type_map
|
dict[str, Any]
|
Mapping from event name to enum type Example: {"Swap": MyEventType.SWAP, "Mint": MyEventType.MINT} |
required |
get_event_name
¶
Get event name from topic signature.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
topic
|
str
|
Event topic signature (keccak256 hash) |
required |
Returns:
| Type | Description |
|---|---|
str | None
|
Event name or None if topic is unknown |
Example
registry.get_event_name("0xc42079...") 'Swap'
get_event_type
¶
Get event type enum from event name.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
event_name
|
str
|
Event name (e.g., "Swap") |
required |
Returns:
| Type | Description |
|---|---|
Any | None
|
Event type enum or None if name is unknown |
Example
registry.get_event_type("Swap")
get_event_type_from_topic
¶
Get event type enum directly from topic signature.
Convenience method that combines get_event_name() and get_event_type().
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
topic
|
str
|
Event topic signature (keccak256 hash) |
required |
Returns:
| Type | Description |
|---|---|
Any | None
|
Event type enum or None if topic is unknown |
Example
registry.get_event_type_from_topic("0xc42079...")
is_known_event
¶
Check if a topic is a known event.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
topic
|
str
|
Event topic signature to check |
required |
Returns:
| Type | Description |
|---|---|
bool
|
True if topic is in the registry |
Example
registry.is_known_event("0xc42079...") True registry.is_known_event("0xunknown...") False
get_topic_signature
¶
Get topic signature from event name.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
event_name
|
str
|
Event name (e.g., "Swap") |
required |
Returns:
| Type | Description |
|---|---|
str | None
|
Topic signature or None if name is unknown |
Example
registry.get_topic_signature("Swap") '0xc42079...'
__contains__
¶
Check if topic is in registry (allows 'in' operator).
HexDecoder¶
almanak.framework.connectors.base.HexDecoder
¶
Static utilities for decoding hex-encoded event data.
This class provides methods for decoding common EVM types from hex strings, including: - Addresses (20 bytes) - Unsigned integers: uint256, uint160, uint128 - Signed integers: int256, int128, int24 - Dynamic arrays (for batch events) - Raw bytes32 values
All methods are static and handle both bytes and string inputs.
normalize_hex
staticmethod
¶
Normalize a hex value to a string without '0x' prefix.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
value
|
Any
|
Bytes or string value to normalize |
required |
Returns:
| Type | Description |
|---|---|
str
|
Hex string without '0x' prefix |
topic_to_address
staticmethod
¶
Convert a log topic to an Ethereum address.
Topics are 32 bytes, but addresses are only 20 bytes. This extracts the last 20 bytes as the address.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
topic
|
Any
|
Topic value (bytes or hex string) |
required |
Returns:
| Type | Description |
|---|---|
str
|
Lowercase address with '0x' prefix, or empty string if topic is empty |
Example
HexDecoder.topic_to_address( ... "0x000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48" ... ) '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48'
topic_to_bytes32
staticmethod
¶
Convert a log topic to a bytes32 hex string.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
topic
|
Any
|
Topic value (bytes or hex string) |
required |
Returns:
| Type | Description |
|---|---|
str
|
Full 32-byte hex string with '0x' prefix |
Example
HexDecoder.topic_to_bytes32(b'\x00' * 31 + b'\x01') '0x0000000000000000000000000000000000000000000000000000000000000001'
decode_uint256
staticmethod
¶
Decode an unsigned 256-bit integer from hex string.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
hex_str
|
str
|
Hex string to decode (with or without '0x') |
required |
offset
|
int
|
Byte offset to start reading from |
0
|
Returns:
| Type | Description |
|---|---|
int
|
Decoded unsigned integer value |
Example
HexDecoder.decode_uint256("0x00000000000000000000000000000000000000000000000000000000000003e8") 1000
decode_uint160
staticmethod
¶
Decode an unsigned 160-bit integer from hex string.
Used for Uniswap V3 sqrtPriceX96 which is uint160.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
hex_str
|
str
|
Hex string to decode (with or without '0x') |
required |
offset
|
int
|
Byte offset to start reading from |
0
|
Returns:
| Type | Description |
|---|---|
int
|
Decoded unsigned integer value |
decode_uint128
staticmethod
¶
Decode an unsigned 128-bit integer from hex string.
Used for Uniswap V3 liquidity which is uint128.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
hex_str
|
str
|
Hex string to decode (with or without '0x') |
required |
offset
|
int
|
Byte offset to start reading from |
0
|
Returns:
| Type | Description |
|---|---|
int
|
Decoded unsigned integer value |
decode_int256
staticmethod
¶
Decode a signed 256-bit integer from hex string.
Handles two's complement representation for negative numbers.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
hex_str
|
str
|
Hex string to decode (with or without '0x') |
required |
offset
|
int
|
Byte offset to start reading from |
0
|
Returns:
| Type | Description |
|---|---|
int
|
Decoded signed integer value (can be negative) |
decode_int128
staticmethod
¶
Decode a signed 128-bit integer from hex string.
Used by Curve for token amounts. Handles two's complement.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
hex_str
|
str
|
Hex string to decode (with or without '0x') |
required |
offset
|
int
|
Byte offset to start reading from |
0
|
Returns:
| Type | Description |
|---|---|
int
|
Decoded signed integer value (can be negative) |
decode_int24
staticmethod
¶
Decode a signed 24-bit integer from hex string.
Used for Uniswap V3 ticks. Value is stored in 256-bit slot but only uses 24 bits. Handles two's complement.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
hex_str
|
str
|
Hex string to decode (with or without '0x') |
required |
offset
|
int
|
Byte offset to start reading from |
0
|
Returns:
| Type | Description |
|---|---|
int
|
Decoded signed integer value (can be negative) |
decode_dynamic_array
staticmethod
¶
Decode a dynamic array from hex string.
Dynamic arrays in EVM events are encoded as: - Offset to array data (32 bytes) - Array length (32 bytes) - Array elements (32 bytes each)
Used by TraderJoe V2 for bin IDs and Polymarket for batch transfers.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
hex_str
|
str
|
Hex string to decode (with or without '0x') |
required |
offset
|
int
|
Byte offset to start reading array offset |
0
|
Returns:
| Type | Description |
|---|---|
list[int]
|
List of decoded uint256 values |
Example
Array [1, 2, 3] encoded in event data¶
data = "0x" + "0" * 64 + "0" * 62 + "03" + "0" * 62 + "01" + "0" * 62 + "02" + "0" * 62 + "03" HexDecoder.decode_dynamic_array(data, 0) [1, 2, 3]
decode_address_from_data
staticmethod
¶
Decode an address from event data (not indexed topic).
Unlike indexed addresses which are in topics, non-indexed addresses appear in the data section as 32-byte values with leading zeros.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
hex_str
|
str
|
Hex string to decode (with or without '0x') |
required |
offset
|
int
|
Byte offset to start reading from |
0
|
Returns:
| Type | Description |
|---|---|
str
|
Lowercase address with '0x' prefix |
Example
HexDecoder.decode_address_from_data( ... "0x000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48" ... ) '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48'
split_into_chunks
staticmethod
¶
Split hex string into chunks of specified size.
Useful for parsing event data with multiple parameters.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
hex_str
|
str
|
Hex string to split (with or without '0x') |
required |
chunk_size
|
int
|
Size of each chunk in hex characters (default 64 = 32 bytes) |
64
|
Returns:
| Type | Description |
|---|---|
list[str]
|
List of hex chunks |
Example
data = "0x" + "0" * 64 + "1" * 64 chunks = HexDecoder.split_into_chunks(data) len(chunks) 2