-
-
Notifications
You must be signed in to change notification settings - Fork 53
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Refactor ABI processing, fix filtering events by signature (#1080)
- Loading branch information
1 parent
a63070e
commit 7e34379
Showing
21 changed files
with
552 additions
and
347 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
from __future__ import annotations | ||
|
||
from abc import ABC | ||
from abc import abstractmethod | ||
from typing import TYPE_CHECKING | ||
|
||
if TYPE_CHECKING: | ||
from dipdup.package import DipDupPackage | ||
|
||
|
||
class AbiManager(ABC): | ||
def __init__(self, package: DipDupPackage) -> None: | ||
self._package = package | ||
|
||
@abstractmethod | ||
def load(self) -> None: ... |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,144 @@ | ||
from __future__ import annotations | ||
|
||
from functools import cache | ||
from typing import TYPE_CHECKING | ||
from typing import Any | ||
from typing import TypedDict | ||
|
||
import orjson | ||
|
||
from dipdup.abi import AbiManager | ||
from dipdup.exceptions import FrameworkException | ||
from dipdup.utils import json_dumps | ||
from dipdup.utils import touch | ||
|
||
if TYPE_CHECKING: | ||
from starknet_py.abi.v2 import Abi # type: ignore[import-untyped] | ||
from starknet_py.cairo.data_types import CairoType # type: ignore[import-untyped] | ||
from starknet_py.cairo.data_types import EventType | ||
from starknet_py.serialization import PayloadSerializer # type: ignore[import-untyped] | ||
|
||
from dipdup.package import DipDupPackage | ||
|
||
|
||
class CairoEventAbi(TypedDict): | ||
name: str | ||
event_identifier: str | ||
members: dict[str, CairoType] | ||
serializer: PayloadSerializer | ||
|
||
|
||
class CairoAbi(TypedDict): | ||
events: list[CairoEventAbi] | ||
|
||
|
||
def _convert_type(type_: CairoType) -> str: | ||
# TODO: Support all types | ||
return { | ||
'FeltType': 'integer', | ||
'UintType': 'integer', | ||
'BoolType': 'boolean', | ||
}[type.__class__.__name__] | ||
|
||
|
||
def _jsonschema_from_event(event: EventType) -> dict[str, Any]: | ||
# TODO: Unpack nested types (starknet.py could do that) | ||
return { | ||
'$schema': 'http://json-schema.org/draft/2019-09/schema#', | ||
'type': 'object', | ||
'properties': {key: {'type': _convert_type(value)} for key, value in event.types.items()}, | ||
'required': tuple(event.types.keys()), | ||
'additionalProperties': False, | ||
} | ||
|
||
|
||
def sn_keccak(x: str) -> str: | ||
from Crypto.Hash import keccak | ||
|
||
# NOTE: Create keccak256 hash in bytes and return hex representation of the first 250 bits. | ||
keccak_hash = keccak.new(data=x.encode('ascii'), digest_bits=256).digest() | ||
return f'0x{int.from_bytes(keccak_hash, "big") & (1 << 248) - 1:x}' | ||
|
||
|
||
@cache | ||
def _loaded_abis(package: DipDupPackage) -> dict[str, Abi]: | ||
|
||
from starknet_py.abi.v2 import AbiParser | ||
from starknet_py.abi.v2 import AbiParsingError | ||
|
||
result = {} | ||
for abi_path in package.cairo_abi_paths: | ||
abi = orjson.loads(abi_path.read_bytes()) | ||
|
||
try: | ||
parsed_abi = AbiParser(abi).parse() | ||
except AbiParsingError as e: | ||
raise e | ||
|
||
parsed_abi.events = {k.split('::')[-1]: v for k, v in parsed_abi.events.items()} | ||
result[abi_path.parent.stem] = parsed_abi | ||
return result | ||
|
||
|
||
def convert_abi(package: DipDupPackage) -> dict[str, CairoAbi]: | ||
from starknet_py.serialization import serializer_for_event | ||
|
||
abi_by_typename: dict[str, CairoAbi] = {} | ||
|
||
for contract_typename, parsed_abi in _loaded_abis(package).items(): | ||
converted_abi: CairoAbi = { | ||
'events': [], | ||
} | ||
|
||
for name, event_type in parsed_abi.events.items(): | ||
if name in converted_abi['events']: | ||
raise NotImplementedError('Multiple events with the same name are not supported') | ||
converted_abi['events'].append( | ||
CairoEventAbi( | ||
name=name, | ||
event_identifier=sn_keccak(name), | ||
members=event_type.types, | ||
serializer=serializer_for_event(event_type), | ||
) | ||
) | ||
abi_by_typename[contract_typename] = converted_abi | ||
|
||
return abi_by_typename | ||
|
||
|
||
def abi_to_jsonschemas( | ||
package: DipDupPackage, | ||
events: set[str], | ||
) -> None: | ||
for contract_typename, parsed_abi in _loaded_abis(package).items(): | ||
for event_name in events: | ||
if event_name not in parsed_abi.events: | ||
continue | ||
|
||
schema = _jsonschema_from_event(parsed_abi.events[event_name]) | ||
schema_path = package.schemas / contract_typename / 'starknet_events' / f'{event_name}.json' | ||
touch(schema_path) | ||
schema_path.write_bytes(json_dumps(schema)) | ||
|
||
|
||
class CairoAbiManager(AbiManager): | ||
def __init__(self, package: DipDupPackage) -> None: | ||
super().__init__(package) | ||
self._abis: dict[str, CairoAbi] = {} | ||
self.get_event_abi = cache(self.get_event_abi) # type: ignore[method-assign] | ||
|
||
def load(self) -> None: | ||
self._abis = convert_abi(self._package) | ||
|
||
def get_event_abi( | ||
self, | ||
typename: str, | ||
name: str, | ||
) -> CairoEventAbi: | ||
typename_abi = self._abis[typename] | ||
for event_abi in typename_abi['events']: | ||
if name != event_abi['name']: | ||
continue | ||
return event_abi | ||
|
||
raise FrameworkException(f'Event `{name}` not found in `{typename}`') |
Oops, something went wrong.