feat(vault): add user vault events decoding in indexer
This commit is contained in:
+39
-14
@@ -8,8 +8,42 @@ from src.core.abis import CONTROLLER_ABI, MARKET_ABI
|
|||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
# Vault Factory & User Vault Events ABI
|
||||||
|
VAULT_EVENTS_ABI = [
|
||||||
|
{
|
||||||
|
"anonymous": False,
|
||||||
|
"inputs": [
|
||||||
|
{"indexed": True, "name": "user", "type": "address"},
|
||||||
|
{"indexed": True, "name": "vault", "type": "address"},
|
||||||
|
{"indexed": False, "name": "vaultIndex", "type": "uint256"}
|
||||||
|
],
|
||||||
|
"name": "VaultCreated",
|
||||||
|
"type": "event"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"anonymous": False,
|
||||||
|
"inputs": [
|
||||||
|
{"indexed": True, "name": "token", "type": "address"},
|
||||||
|
{"indexed": True, "name": "from", "type": "address"},
|
||||||
|
{"indexed": False, "name": "amount", "type": "uint256"}
|
||||||
|
],
|
||||||
|
"name": "Deposited",
|
||||||
|
"type": "event"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"anonymous": False,
|
||||||
|
"inputs": [
|
||||||
|
{"indexed": True, "name": "token", "type": "address"},
|
||||||
|
{"indexed": True, "name": "to", "type": "address"},
|
||||||
|
{"indexed": False, "name": "amount", "type": "uint256"}
|
||||||
|
],
|
||||||
|
"name": "Withdrawn",
|
||||||
|
"type": "event"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
class EVMDecoder:
|
class EVMDecoder:
|
||||||
"""EVM ABI 日志规范化解码器 (Layer 2: Decoding)"""
|
"""EVM ABI 日志规范化解码器 (包含 Controller, Market 与 User Vault)"""
|
||||||
|
|
||||||
def __init__(self, chain_id: int):
|
def __init__(self, chain_id: int):
|
||||||
self.chain_id = chain_id
|
self.chain_id = chain_id
|
||||||
@@ -19,8 +53,7 @@ class EVMDecoder:
|
|||||||
def _build_topic_maps(self):
|
def _build_topic_maps(self):
|
||||||
self.event_abi_map = {}
|
self.event_abi_map = {}
|
||||||
|
|
||||||
# 解析 Controller 与 Market ABI 中的 events
|
for abi in CONTROLLER_ABI + MARKET_ABI + VAULT_EVENTS_ABI:
|
||||||
for abi in CONTROLLER_ABI + MARKET_ABI:
|
|
||||||
if abi.get("type") == "event":
|
if abi.get("type") == "event":
|
||||||
name = abi.get("name")
|
name = abi.get("name")
|
||||||
inputs = abi.get("inputs", [])
|
inputs = abi.get("inputs", [])
|
||||||
@@ -30,7 +63,6 @@ class EVMDecoder:
|
|||||||
self.event_abi_map[topic0] = abi
|
self.event_abi_map[topic0] = abi
|
||||||
|
|
||||||
def decode_log(self, log: Dict[str, Any], block_timestamp: int = 0) -> Optional[NormalizedEvent]:
|
def decode_log(self, log: Dict[str, Any], block_timestamp: int = 0) -> Optional[NormalizedEvent]:
|
||||||
"""将原始 EVM 日志解析为统一的 NormalizedEvent"""
|
|
||||||
topics = log.get("topics", [])
|
topics = log.get("topics", [])
|
||||||
if not topics:
|
if not topics:
|
||||||
return None
|
return None
|
||||||
@@ -46,19 +78,16 @@ class EVMDecoder:
|
|||||||
block_number = log.get("blockNumber", 0)
|
block_number = log.get("blockNumber", 0)
|
||||||
log_index = log.get("logIndex", 0)
|
log_index = log.get("logIndex", 0)
|
||||||
|
|
||||||
# 解码 Indexed 参数与 Non-Indexed Data
|
|
||||||
payload: Dict[str, Any] = {}
|
payload: Dict[str, Any] = {}
|
||||||
indexed_inputs = [i for i in abi.get("inputs", []) if i.get("indexed")]
|
indexed_inputs = [i for i in abi.get("inputs", []) if i.get("indexed")]
|
||||||
non_indexed_inputs = [i for i in abi.get("inputs", []) if not i.get("indexed")]
|
non_indexed_inputs = [i for i in abi.get("inputs", []) if not i.get("indexed")]
|
||||||
|
|
||||||
# 1. Indexed topics
|
|
||||||
for idx, inp in enumerate(indexed_inputs):
|
for idx, inp in enumerate(indexed_inputs):
|
||||||
if idx + 1 < len(topics):
|
if idx + 1 < len(topics):
|
||||||
raw_topic = topics[idx + 1]
|
raw_topic = topics[idx + 1]
|
||||||
t_hex = raw_topic.hex() if isinstance(raw_topic, bytes) else raw_topic
|
t_hex = raw_topic.hex() if isinstance(raw_topic, bytes) else raw_topic
|
||||||
payload[inp["name"]] = t_hex
|
payload[inp["name"]] = t_hex
|
||||||
|
|
||||||
# 2. Non-indexed data
|
|
||||||
data = log.get("data", "0x")
|
data = log.get("data", "0x")
|
||||||
if isinstance(data, str) and data.startswith("0x"):
|
if isinstance(data, str) and data.startswith("0x"):
|
||||||
data_bytes = bytes.fromhex(data[2:])
|
data_bytes = bytes.fromhex(data[2:])
|
||||||
@@ -79,7 +108,6 @@ class EVMDecoder:
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Failed to decode non-indexed data for {event_name}: {e}")
|
logger.error(f"Failed to decode non-indexed data for {event_name}: {e}")
|
||||||
|
|
||||||
# 标准化 Event Type
|
|
||||||
type_mapping = {
|
type_mapping = {
|
||||||
"DeployMarket": "MARKET_DEPLOYED",
|
"DeployMarket": "MARKET_DEPLOYED",
|
||||||
"SetOutcome": "OUTCOME_RESOLVED",
|
"SetOutcome": "OUTCOME_RESOLVED",
|
||||||
@@ -87,12 +115,9 @@ class EVMDecoder:
|
|||||||
"Mint": "ORDER_MINT",
|
"Mint": "ORDER_MINT",
|
||||||
"Redeem": "ORDER_REDEEM",
|
"Redeem": "ORDER_REDEEM",
|
||||||
"Claim": "POSITION_CLAIMED",
|
"Claim": "POSITION_CLAIMED",
|
||||||
"SetProtocolFeeRate": "GOV_FEE_RATE_UPDATED",
|
"VaultCreated": "VAULT_CREATED",
|
||||||
"SetTreasury": "GOV_TREASURY_UPDATED",
|
"Deposited": "VAULT_DEPOSITED",
|
||||||
"SetCentralWallet": "GOV_CENTRAL_WALLET_UPDATED",
|
"Withdrawn": "VAULT_WITHDRAWN"
|
||||||
"SetCreatorShare": "GOV_CREATOR_SHARE_UPDATED",
|
|
||||||
"Paused": "PROTOCOL_PAUSED",
|
|
||||||
"Unpaused": "PROTOCOL_UNPAUSED",
|
|
||||||
}
|
}
|
||||||
|
|
||||||
normalized_type = type_mapping.get(event_name, f"EVM_{event_name.upper()}")
|
normalized_type = type_mapping.get(event_name, f"EVM_{event_name.upper()}")
|
||||||
|
|||||||
Reference in New Issue
Block a user