Skip to content

Commit 920d02e

Browse files
Fix parsing EVM tuples (#1182)
1 parent 36c760a commit 920d02e

File tree

9 files changed

+74
-34
lines changed

9 files changed

+74
-34
lines changed

CHANGELOG.md

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,11 @@ Releases prior to 7.0 has been removed from this file to declutter search result
1010

1111
### Fixed
1212

13-
- subsquid: Fixed float type for `timestamp` field on event / transaction deserialization.
14-
- subsquid: Fixed empty field base conversion on event deserialization.
13+
- evm: Fixed sending JSONRPC requests via web3.py provider.
14+
- evm: Fixed parsing tuple types in ABI.
15+
- evm.subsquid: Fixed type of `timestamp` field of event/transaction models.
16+
- evm.subsquid: Fixed empty field base conversion on event deserialization.
17+
- starknet: Fixed parsing contract addresses starting with `0x0`.
1518

1619
## [8.1.3] - 2024-12-20
1720

docs/16.thanks.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,9 @@ We are grateful to all the people who helped us with the project.
3131
- [Florian PAUTOT](https://github.com/0x666c6f)
3232
- [gdsoumya](https://github.com/gdsoumya)
3333
- [Göran Sandström](https://github.com/veqtor)
34+
- [hoka](https://github.com/hokaxbt)
3435
- [Igor Sereda](https://github.com/igorsereda)
36+
- [Ilia Batii](https://github.com/baitcode)
3537
- [Javier Graciá Carpio](https://github.com/jagracar)
3638
- [JoE11-y](https://github.com/JoE11-y)
3739
- [Karan Dua](https://github.com/Karantezsure)
@@ -49,4 +51,10 @@ We are grateful to all the people who helped us with the project.
4951
- [Soham Das](https://github.com/tosoham)
5052
- [tomsib2001](https://github.com/tomsib2001)
5153

54+
Also, these people helped heavily with [pysignalr](https://github.com/baking-bad/), a library we have developed to use in DipDup:
55+
56+
- [Caio Barbieri](https://github.com/caiolombello)
57+
- [MichaelMKKelly](https://github.com/MichaelMKKelly)
58+
- [Ola Lidholm](https://github.com/olalid)
59+
5260
If we forgot to mention you, or you want to update your record, please, open an issue or pull request.

docs/7.references/2.config.md

Lines changed: 1 addition & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -140,24 +140,6 @@ description: "Config file reference"
140140

141141
<dl class="py class">
142142

143-
## dipdup.config.DatasourceConfig
144-
145-
<em class="property"><span class="pre">class</span><span class="w"> </span></em><span class="sig-prename descclassname"><span class="pre">dipdup.config.</span></span><span class="sig-name descname"><span class="pre">DatasourceConfig</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="o"><span class="pre">*</span></span><span class="n"><span class="pre">args</span></span></em><span class="sig-paren">)</span></dt>
146-
<dd><p>Base class for datasource configs</p>
147-
<dl class="field-list simple">
148-
<dt class="field-odd" style="color: var(--txt-primary);">Parameters<span class="colon">:</span></dt>
149-
<dd class="field-odd"><ul class="simple">
150-
<li><p><strong>kind</strong> – Defined by child class</p></li>
151-
<li><p><strong>url</strong> – URL of the API</p></li>
152-
<li><p><strong>http</strong> – HTTP connection tunables</p></li>
153-
154-
</ul>
155-
</dd>
156-
</dl>
157-
</dd></dl>
158-
159-
<dl class="py class">
160-
161143
## dipdup.config.evm.EvmContractConfig
162144

163145
<em class="property"><span class="pre">class</span><span class="w"> </span></em><span class="sig-prename descclassname"><span class="pre">dipdup.config.evm.</span></span><span class="sig-name descname"><span class="pre">EvmContractConfig</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="o"><span class="pre">*</span></span><span class="n"><span class="pre">args</span></span></em><span class="sig-paren">)</span></dt>
@@ -439,7 +421,7 @@ description: "Config file reference"
439421

440422
<dl class="py class">
441423

442-
## id0
424+
## dipdup.config.DatasourceConfig
443425

444426
<em class="property"><span class="pre">class</span><span class="w"> </span></em><span class="sig-prename descclassname"><span class="pre">dipdup.config.</span></span><span class="sig-name descname"><span class="pre">DatasourceConfig</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="o"><span class="pre">*</span></span><span class="n"><span class="pre">args</span></span></em><span class="sig-paren">)</span></dt>
445427
<dd><p>Base class for datasource configs</p>

docs/9.release-notes/_8.0_changelog.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,15 +36,20 @@
3636
- database: Fixed concurrency issue when using `get_or_create` method.
3737
- evm.events: Fixed matching logs when filtering by topic0.
3838
- evm.events: Improve fetching event batches from node.
39+
- evm.subsquid: Fixed empty field base conversion on event deserialization.
40+
- evm.subsquid: Fixed type of `timestamp` field of event/transaction models.
3941
- evm.subsquid: Fixed typo in `iter_events` method name.
4042
- evm: Fixed crash when contract ABI contains overloaded methods.
43+
- evm: Fixed parsing tuple types in ABI.
44+
- evm: Fixed sending JSONRPC requests via web3.py provider.
4145
- install: Fixed reinstalling package when `--force` flag is used.
4246
- models: Fixed `CachedModel` preloading.
4347
- models: Fixed setting default value for `Meta.maxsize`.
4448
- package: Create package in-place if cwd equals package name.
4549
- performance: Add index name to fetcher and realtime queues.
4650
- performance: Fixed estimation indexing speed in levels per second.
4751
- starknet.events: Fixed filtering events by key.
52+
- starknet: Fixed parsing contract addresses starting with `0x0`.
4853
- subsquid: Fixed missing entry in `dipdup_head` internal table.
4954
- tezos.big_maps: Fixed logging status message in `skip_history` mode.
5055
- tezos.big_maps: Respect order of handlers in `skip_history` mode.

docs/config.rst

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,11 @@
22

33
.. autoclass:: dipdup.config.DipDupConfig
44

5-
.. autoclass:: dipdup.config.AbiDatasourceConfig
65
.. autoclass:: dipdup.config.abi_etherscan.AbiEtherscanDatasourceConfig
76
.. autoclass:: dipdup.config.AdvancedConfig
87
.. autoclass:: dipdup.config.ApiConfig
98
.. autoclass:: dipdup.config.coinbase.CoinbaseDatasourceConfig
109
.. autoclass:: dipdup.config.ContractConfig
11-
.. autoclass:: dipdup.config.DatasourceConfig
1210
.. autoclass:: dipdup.config.evm.EvmContractConfig
1311
.. autoclass:: dipdup.config.evm_node.EvmNodeDatasourceConfig
1412
.. autoclass:: dipdup.config.evm_events.EvmEventsHandlerConfig

src/dipdup/abi/evm.py

Lines changed: 30 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,6 @@
2929
'bytes': 'string',
3030
'bool': 'boolean',
3131
'string': 'string',
32-
# TODO: arrays and tuples
33-
# https://docs.soliditylang.org/en/develop/abi-spec.html#types
3432
'tuple': 'object',
3533
}
3634

@@ -52,12 +50,25 @@ def _convert_name(name: str) -> str:
5250

5351

5452
def jsonschema_from_abi(abi: dict[str, Any]) -> dict[str, Any]:
53+
properties, required = {}, []
54+
for item in abi['inputs']:
55+
name = _convert_name(item['name'])
56+
if item['type'] == 'tuple':
57+
properties[name] = {
58+
'type': 'object',
59+
'properties': {
60+
_convert_name(i['name']): {'type': _convert_type(i['type'])} for i in item['components']
61+
},
62+
}
63+
else:
64+
properties[name] = {'type': _convert_type(item['type'])}
65+
required.append(name)
66+
5567
return {
5668
'$schema': 'http://json-schema.org/draft/2019-09/schema#',
5769
'type': 'object',
58-
'properties': {_convert_name(i['name']): {'type': _convert_type(i['type'])} for i in abi['inputs']},
59-
'required': [_convert_name(i['name']) for i in abi['inputs']],
60-
'additionalProperties': False,
70+
'properties': properties,
71+
'required': required,
6172
}
6273

6374

@@ -108,12 +119,17 @@ def _convert_abi(abi_path: Path) -> EvmAbi:
108119
)
109120
)
110121
elif abi_item['type'] == 'event':
111-
inputs = tuple((i['type'], i['indexed']) for i in abi_item['inputs'])
122+
inputs = []
123+
for item in abi_item['inputs']:
124+
if (type_ := item['type']) == 'tuple':
125+
type_ = '(' + ','.join(c['type'] for c in item['components']) + ')'
126+
inputs.append((type_, item['indexed']))
127+
112128
events.append(
113129
EvmEventAbi(
114130
name=abi_item['name'],
115131
topic0=topic0_from_abi(abi_item),
116-
inputs=inputs,
132+
inputs=tuple(inputs),
117133
topic_count=len([i for i in inputs if i[1]]),
118134
)
119135
)
@@ -178,7 +194,13 @@ def topic0_from_abi(event: dict[str, Any]) -> str:
178194
if event.get('type') != 'event':
179195
raise FrameworkException(f'`{event["name"]}` is not an event')
180196

181-
signature = f'{event["name"]}({",".join([i["type"] for i in event["inputs"]])})'
197+
types = []
198+
from eth_utils.abi import collapse_if_tuple
199+
200+
for input in event['inputs']:
201+
types.append(collapse_if_tuple(input))
202+
203+
signature = f'{event["name"]}({",".join(types)})'
182204
return '0x' + eth_utils.crypto.keccak(text=signature).hex()
183205

184206

src/dipdup/indexes/evm_events/matcher.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,13 @@ def decode_event_data(
4040

4141
non_indexed_bytes = decode_hex(data)
4242
if non_indexed_bytes:
43-
non_indexed_values = iter(decode_abi(tuple(n for n, i in inputs if not i), non_indexed_bytes))
43+
non_indexed_values = iter(
44+
decode_abi(
45+
types=tuple(n for n, i in inputs if not i),
46+
data=non_indexed_bytes,
47+
strict=False,
48+
)
49+
)
4450
else:
4551
# NOTE: Node truncates trailing zeros in event data
4652
non_indexed_values = cycle((0,))
@@ -81,6 +87,7 @@ def prepare_event_handler_args(
8187
type_=type_,
8288
data=data,
8389
plain=True,
90+
nested=True,
8491
)
8592
return EvmEvent(
8693
data=matched_event,

src/dipdup/indexes/evm_transactions/matcher.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,10 @@ def prepare_transaction_handler_args(
4949
name=handler_config.method,
5050
signature=handler_config.signature,
5151
)['inputs']
52+
from eth_utils.abi import collapse_if_tuple
53+
5254
data = decode_abi(
53-
types=tuple(input['type'] for input in inputs),
55+
types=tuple(collapse_if_tuple(input) for input in inputs),
5456
data=decode_hex(matched_transaction.input[10:]),
5557
strict=False,
5658
)

src/dipdup/utils.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -200,13 +200,26 @@ def parse_object(
200200
type_: type[ObjectT],
201201
data: Mapping[str, Any] | Sequence[Any] | None,
202202
plain: bool = False,
203+
nested: bool = False,
203204
) -> ObjectT:
204205
try:
205206
if plain is False or data is None:
206207
return type_.model_validate(data)
207208

208209
model_keys = tuple(field.alias or key for key, field in type_.model_fields.items())
209-
return type_(**dict(zip(model_keys, data, strict=True)))
210+
model_dict = dict(zip(model_keys, data, strict=True))
211+
212+
if nested:
213+
for k, v in model_dict.items():
214+
if not isinstance(v, list | tuple):
215+
continue
216+
217+
# NOTE: Might be `from_` or other reserved keyword
218+
field_k = '{k}_ ' if k not in type_.model_fields else k
219+
nested_type = type_.model_fields[field_k].annotation
220+
model_dict[k] = parse_object(nested_type, v, plain=True) # type: ignore[arg-type]
221+
222+
return type_(**model_dict)
210223
except ValidationError as e:
211224
raise InvalidDataError(f'Failed to parse: {e.errors()}', type_, data) from e
212225
except ValueError as e:

0 commit comments

Comments
 (0)