faster-eth-abi 5.2.3__cp314-cp314-macosx_11_0_arm64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- a1f8aa123fabc88e2b56__mypyc.cpython-314-darwin.so +0 -0
- faster_eth_abi/__init__.py +12 -0
- faster_eth_abi/abi.cpython-314-darwin.so +0 -0
- faster_eth_abi/abi.py +17 -0
- faster_eth_abi/base.py +41 -0
- faster_eth_abi/codec.py +167 -0
- faster_eth_abi/constants.cpython-314-darwin.so +0 -0
- faster_eth_abi/constants.py +7 -0
- faster_eth_abi/decoding.py +563 -0
- faster_eth_abi/encoding.py +699 -0
- faster_eth_abi/exceptions.py +115 -0
- faster_eth_abi/from_type_str.cpython-314-darwin.so +0 -0
- faster_eth_abi/from_type_str.py +135 -0
- faster_eth_abi/grammar.py +467 -0
- faster_eth_abi/io.py +103 -0
- faster_eth_abi/packed.cpython-314-darwin.so +0 -0
- faster_eth_abi/packed.py +15 -0
- faster_eth_abi/py.typed +0 -0
- faster_eth_abi/registry.py +640 -0
- faster_eth_abi/tools/__init__.cpython-314-darwin.so +0 -0
- faster_eth_abi/tools/__init__.py +3 -0
- faster_eth_abi/tools/_strategies.cpython-314-darwin.so +0 -0
- faster_eth_abi/tools/_strategies.py +237 -0
- faster_eth_abi/utils/__init__.cpython-314-darwin.so +0 -0
- faster_eth_abi/utils/__init__.py +0 -0
- faster_eth_abi/utils/numeric.cpython-314-darwin.so +0 -0
- faster_eth_abi/utils/numeric.py +86 -0
- faster_eth_abi/utils/padding.cpython-314-darwin.so +0 -0
- faster_eth_abi/utils/padding.py +22 -0
- faster_eth_abi/utils/string.cpython-314-darwin.so +0 -0
- faster_eth_abi/utils/string.py +19 -0
- faster_eth_abi/utils/validation.cpython-314-darwin.so +0 -0
- faster_eth_abi/utils/validation.py +22 -0
- faster_eth_abi-5.2.3.dist-info/METADATA +95 -0
- faster_eth_abi-5.2.3.dist-info/RECORD +38 -0
- faster_eth_abi-5.2.3.dist-info/WHEEL +6 -0
- faster_eth_abi-5.2.3.dist-info/licenses/LICENSE +21 -0
- faster_eth_abi-5.2.3.dist-info/top_level.txt +3 -0
@@ -0,0 +1,467 @@
|
|
1
|
+
import functools
|
2
|
+
import re
|
3
|
+
from typing import (
|
4
|
+
Any,
|
5
|
+
Final,
|
6
|
+
NoReturn,
|
7
|
+
Optional,
|
8
|
+
Sequence,
|
9
|
+
Tuple,
|
10
|
+
TypeVar,
|
11
|
+
final,
|
12
|
+
)
|
13
|
+
|
14
|
+
from eth_typing import (
|
15
|
+
TypeStr,
|
16
|
+
)
|
17
|
+
import parsimonious
|
18
|
+
from parsimonious import (
|
19
|
+
expressions,
|
20
|
+
)
|
21
|
+
from parsimonious.nodes import (
|
22
|
+
Node,
|
23
|
+
)
|
24
|
+
from typing_extensions import (
|
25
|
+
Self,
|
26
|
+
)
|
27
|
+
|
28
|
+
from faster_eth_abi.exceptions import (
|
29
|
+
ABITypeError,
|
30
|
+
ParseError,
|
31
|
+
)
|
32
|
+
|
33
|
+
grammar: Final = parsimonious.Grammar(
|
34
|
+
r"""
|
35
|
+
type = tuple_type / basic_type
|
36
|
+
|
37
|
+
tuple_type = components arrlist?
|
38
|
+
components = non_zero_tuple
|
39
|
+
|
40
|
+
non_zero_tuple = "(" type next_type* ")"
|
41
|
+
next_type = "," type
|
42
|
+
|
43
|
+
basic_type = base sub? arrlist?
|
44
|
+
|
45
|
+
base = alphas
|
46
|
+
|
47
|
+
sub = two_size / digits
|
48
|
+
two_size = (digits "x" digits)
|
49
|
+
|
50
|
+
arrlist = (const_arr / dynam_arr)+
|
51
|
+
const_arr = "[" digits "]"
|
52
|
+
dynam_arr = "[]"
|
53
|
+
|
54
|
+
alphas = ~"[A-Za-z]+"
|
55
|
+
digits = ~"[1-9][0-9]*"
|
56
|
+
"""
|
57
|
+
)
|
58
|
+
|
59
|
+
|
60
|
+
@final
|
61
|
+
class NodeVisitor(parsimonious.NodeVisitor): # type: ignore [misc]
|
62
|
+
"""
|
63
|
+
Parsimonious node visitor which performs both parsing of type strings and
|
64
|
+
post-processing of parse trees. Parsing operations are cached.
|
65
|
+
"""
|
66
|
+
|
67
|
+
def __init__(self):
|
68
|
+
self.parse = functools.lru_cache(maxsize=None)(self._parse_uncached)
|
69
|
+
|
70
|
+
grammar = grammar
|
71
|
+
|
72
|
+
def visit_non_zero_tuple(self, node, visited_children):
|
73
|
+
# Ignore left and right parens
|
74
|
+
_, first, rest, _ = visited_children
|
75
|
+
|
76
|
+
return (first,) + rest
|
77
|
+
|
78
|
+
def visit_tuple_type(self, node, visited_children):
|
79
|
+
components, arrlist = visited_children
|
80
|
+
|
81
|
+
return TupleType(components, arrlist, node=node)
|
82
|
+
|
83
|
+
def visit_next_type(self, node, visited_children):
|
84
|
+
# Ignore comma
|
85
|
+
_, abi_type = visited_children
|
86
|
+
|
87
|
+
return abi_type
|
88
|
+
|
89
|
+
def visit_basic_type(self, node, visited_children):
|
90
|
+
base, sub, arrlist = visited_children
|
91
|
+
|
92
|
+
return BasicType(base, sub, arrlist, node=node)
|
93
|
+
|
94
|
+
def visit_two_size(self, node, visited_children):
|
95
|
+
# Ignore "x"
|
96
|
+
first, _, second = visited_children
|
97
|
+
|
98
|
+
return first, second
|
99
|
+
|
100
|
+
def visit_const_arr(self, node, visited_children):
|
101
|
+
# Ignore left and right brackets
|
102
|
+
_, int_value, _ = visited_children
|
103
|
+
|
104
|
+
return (int_value,)
|
105
|
+
|
106
|
+
def visit_dynam_arr(self, node, visited_children):
|
107
|
+
return ()
|
108
|
+
|
109
|
+
def visit_alphas(self, node, visited_children):
|
110
|
+
return node.text
|
111
|
+
|
112
|
+
def visit_digits(self, node, visited_children):
|
113
|
+
return int(node.text)
|
114
|
+
|
115
|
+
def generic_visit(self, node, visited_children):
|
116
|
+
expr = node.expr
|
117
|
+
if isinstance(expr, expressions.OneOf):
|
118
|
+
# Unwrap value chosen from alternatives
|
119
|
+
return visited_children[0]
|
120
|
+
|
121
|
+
if isinstance(expr, expressions.Quantifier) and expr.min == 0 and expr.max == 1:
|
122
|
+
# Unwrap optional value or return `None`
|
123
|
+
if len(visited_children) != 0:
|
124
|
+
return visited_children[0]
|
125
|
+
|
126
|
+
return None
|
127
|
+
|
128
|
+
return tuple(visited_children)
|
129
|
+
|
130
|
+
def _parse_uncached(self, type_str, **kwargs):
|
131
|
+
"""
|
132
|
+
Parses a type string into an appropriate instance of
|
133
|
+
:class:`~faster_eth_abi.grammar.ABIType`. If a type string cannot be parsed,
|
134
|
+
throws :class:`~faster_eth_abi.exceptions.ParseError`.
|
135
|
+
|
136
|
+
:param type_str: The type string to be parsed.
|
137
|
+
:returns: An instance of :class:`~faster_eth_abi.grammar.ABIType` containing
|
138
|
+
information about the parsed type string.
|
139
|
+
"""
|
140
|
+
if not isinstance(type_str, str):
|
141
|
+
raise TypeError(f"Can only parse string values: got {type(type_str)}")
|
142
|
+
|
143
|
+
try:
|
144
|
+
return super().parse(type_str, **kwargs)
|
145
|
+
except parsimonious.ParseError as e:
|
146
|
+
# This is a good place to add some better messaging around the type string.
|
147
|
+
# If this logic grows any bigger, we should abstract it to its own function.
|
148
|
+
if "()" in type_str:
|
149
|
+
# validate against zero-sized tuple types
|
150
|
+
raise ValueError(
|
151
|
+
'Zero-sized tuple types "()" are not supported.'
|
152
|
+
) from None
|
153
|
+
|
154
|
+
raise ParseError(e.text, e.pos, e.expr) from e
|
155
|
+
|
156
|
+
|
157
|
+
visitor: Final = NodeVisitor()
|
158
|
+
|
159
|
+
|
160
|
+
class ABIType:
|
161
|
+
"""
|
162
|
+
Base class for results of type string parsing operations.
|
163
|
+
"""
|
164
|
+
|
165
|
+
__slots__ = ("arrlist", "node")
|
166
|
+
|
167
|
+
def __init__(
|
168
|
+
self, arrlist: Optional[Sequence] = None, node: Optional[Node] = None
|
169
|
+
) -> None:
|
170
|
+
self.arrlist: Final = arrlist
|
171
|
+
"""
|
172
|
+
The list of array dimensions for a parsed type. Equal to ``None`` if
|
173
|
+
type string has no array dimensions.
|
174
|
+
"""
|
175
|
+
|
176
|
+
self.node: Final = node
|
177
|
+
"""
|
178
|
+
The parsimonious ``Node`` instance associated with this parsed type.
|
179
|
+
Used to generate error messages for invalid types.
|
180
|
+
"""
|
181
|
+
|
182
|
+
def __repr__(self) -> str: # pragma: no cover
|
183
|
+
return f"<{type(self).__qualname__} {self.to_type_str()!r}>"
|
184
|
+
|
185
|
+
def __eq__(self, other: Any) -> bool:
|
186
|
+
# Two ABI types are equal if their string representations are equal
|
187
|
+
return type(self) is type(other) and self.to_type_str() == other.to_type_str()
|
188
|
+
|
189
|
+
def to_type_str(self) -> TypeStr: # pragma: no cover
|
190
|
+
"""
|
191
|
+
Returns the string representation of an ABI type. This will be equal to
|
192
|
+
the type string from which it was created.
|
193
|
+
"""
|
194
|
+
raise NotImplementedError("Must implement `to_type_str`")
|
195
|
+
|
196
|
+
@property
|
197
|
+
def item_type(self) -> Self:
|
198
|
+
"""
|
199
|
+
If this type is an array type, equal to an appropriate
|
200
|
+
:class:`~faster_eth_abi.grammar.ABIType` instance for the array's items.
|
201
|
+
"""
|
202
|
+
raise NotImplementedError("Must implement `item_type`")
|
203
|
+
|
204
|
+
def validate(self) -> None: # pragma: no cover
|
205
|
+
"""
|
206
|
+
Validates the properties of an ABI type against the solidity ABI spec:
|
207
|
+
|
208
|
+
https://solidity.readthedocs.io/en/develop/abi-spec.html
|
209
|
+
|
210
|
+
Raises :class:`~faster_eth_abi.exceptions.ABITypeError` if validation fails.
|
211
|
+
"""
|
212
|
+
raise NotImplementedError("Must implement `validate`")
|
213
|
+
|
214
|
+
def invalidate(self, error_msg: str) -> NoReturn:
|
215
|
+
# Invalidates an ABI type with the given error message. Expects that a
|
216
|
+
# parsimonious node was provided from the original parsing operation
|
217
|
+
# that yielded this type.
|
218
|
+
node = self.node
|
219
|
+
|
220
|
+
raise ABITypeError(
|
221
|
+
f"For '{node.text}' type at column {node.start + 1} "
|
222
|
+
f"in '{node.full_text}': {error_msg}"
|
223
|
+
)
|
224
|
+
|
225
|
+
@property
|
226
|
+
def is_array(self) -> bool:
|
227
|
+
"""
|
228
|
+
Equal to ``True`` if a type is an array type (i.e. if it has an array
|
229
|
+
dimension list). Otherwise, equal to ``False``.
|
230
|
+
"""
|
231
|
+
return self.arrlist is not None
|
232
|
+
|
233
|
+
@property
|
234
|
+
def is_dynamic(self) -> bool:
|
235
|
+
"""
|
236
|
+
Equal to ``True`` if a type has a dynamically sized encoding.
|
237
|
+
Otherwise, equal to ``False``.
|
238
|
+
"""
|
239
|
+
raise NotImplementedError("Must implement `is_dynamic`")
|
240
|
+
|
241
|
+
@property
|
242
|
+
def _has_dynamic_arrlist(self) -> bool:
|
243
|
+
return self.is_array and any(len(dim) == 0 for dim in self.arrlist)
|
244
|
+
|
245
|
+
|
246
|
+
TComp = TypeVar("TComp", bound=ABIType)
|
247
|
+
|
248
|
+
|
249
|
+
@final
|
250
|
+
class TupleType(ABIType):
|
251
|
+
"""
|
252
|
+
Represents the result of parsing a tuple type string e.g. "(int,bool)".
|
253
|
+
"""
|
254
|
+
|
255
|
+
__slots__ = ("components",)
|
256
|
+
|
257
|
+
def __init__(
|
258
|
+
self,
|
259
|
+
components: Tuple[TComp, ...],
|
260
|
+
arrlist: Optional[Sequence] = None,
|
261
|
+
*,
|
262
|
+
node: Optional[Node] = None,
|
263
|
+
) -> None:
|
264
|
+
super().__init__(arrlist, node)
|
265
|
+
|
266
|
+
self.components: Final = components
|
267
|
+
"""
|
268
|
+
A tuple of :class:`~faster_eth_abi.grammar.ABIType` instances for each of the
|
269
|
+
tuple type's components.
|
270
|
+
"""
|
271
|
+
|
272
|
+
def to_type_str(self) -> TypeStr:
|
273
|
+
arrlist = self.arrlist
|
274
|
+
|
275
|
+
if isinstance(arrlist, tuple):
|
276
|
+
arrlist = "".join(repr(list(a)) for a in arrlist)
|
277
|
+
else:
|
278
|
+
arrlist = ""
|
279
|
+
|
280
|
+
return f"({','.join(c.to_type_str() for c in self.components)}){arrlist}"
|
281
|
+
|
282
|
+
@property
|
283
|
+
def item_type(self) -> Self:
|
284
|
+
if not self.is_array:
|
285
|
+
raise ValueError(
|
286
|
+
f"Cannot determine item type for non-array type '{self.to_type_str()}'"
|
287
|
+
)
|
288
|
+
|
289
|
+
return type(self)(
|
290
|
+
self.components,
|
291
|
+
self.arrlist[:-1] or None, # type: ignore [index]
|
292
|
+
node=self.node,
|
293
|
+
)
|
294
|
+
|
295
|
+
def validate(self) -> None:
|
296
|
+
for c in self.components:
|
297
|
+
c.validate()
|
298
|
+
|
299
|
+
@property
|
300
|
+
def is_dynamic(self) -> bool:
|
301
|
+
if self._has_dynamic_arrlist:
|
302
|
+
return True
|
303
|
+
|
304
|
+
return any(c.is_dynamic for c in self.components)
|
305
|
+
|
306
|
+
|
307
|
+
@final
|
308
|
+
class BasicType(ABIType):
|
309
|
+
"""
|
310
|
+
Represents the result of parsing a basic type string e.g. "uint", "address",
|
311
|
+
"ufixed128x19[][2]".
|
312
|
+
"""
|
313
|
+
|
314
|
+
__slots__ = ("base", "sub")
|
315
|
+
|
316
|
+
def __init__(
|
317
|
+
self,
|
318
|
+
base: str,
|
319
|
+
sub: Any = None,
|
320
|
+
arrlist: Optional[Sequence] = None,
|
321
|
+
*,
|
322
|
+
node: Optional[Node] = None,
|
323
|
+
) -> None:
|
324
|
+
super().__init__(arrlist, node)
|
325
|
+
|
326
|
+
self.base: Final = base
|
327
|
+
"""The base of a basic type e.g. "uint" for "uint256" etc."""
|
328
|
+
|
329
|
+
self.sub: Final = sub
|
330
|
+
"""
|
331
|
+
The sub type of a basic type e.g. ``256`` for "uint256" or ``(128, 18)``
|
332
|
+
for "ufixed128x18" etc. Equal to ``None`` if type string has no sub
|
333
|
+
type.
|
334
|
+
"""
|
335
|
+
|
336
|
+
def to_type_str(self) -> TypeStr:
|
337
|
+
sub, arrlist = self.sub, self.arrlist
|
338
|
+
|
339
|
+
if isinstance(sub, int):
|
340
|
+
substr = str(sub)
|
341
|
+
elif isinstance(sub, tuple):
|
342
|
+
substr = "x".join(str(s) for s in sub)
|
343
|
+
else:
|
344
|
+
substr = ""
|
345
|
+
|
346
|
+
if isinstance(arrlist, tuple):
|
347
|
+
return self.base + substr + "".join(repr(list(a)) for a in arrlist)
|
348
|
+
else:
|
349
|
+
return self.base + substr
|
350
|
+
|
351
|
+
@property
|
352
|
+
def item_type(self) -> Self:
|
353
|
+
if not self.is_array:
|
354
|
+
raise ValueError(
|
355
|
+
f"Cannot determine item type for non-array type '{self.to_type_str()}'"
|
356
|
+
)
|
357
|
+
|
358
|
+
return type(self)(
|
359
|
+
self.base,
|
360
|
+
self.sub,
|
361
|
+
self.arrlist[:-1] or None, # type: ignore [index]
|
362
|
+
node=self.node,
|
363
|
+
)
|
364
|
+
|
365
|
+
@property
|
366
|
+
def is_dynamic(self) -> bool:
|
367
|
+
if self._has_dynamic_arrlist:
|
368
|
+
return True
|
369
|
+
|
370
|
+
if self.base == "string":
|
371
|
+
return True
|
372
|
+
|
373
|
+
if self.base == "bytes" and self.sub is None:
|
374
|
+
return True
|
375
|
+
|
376
|
+
return False
|
377
|
+
|
378
|
+
def validate(self) -> None:
|
379
|
+
base, sub = self.base, self.sub
|
380
|
+
|
381
|
+
# Check validity of string type
|
382
|
+
if base == "string":
|
383
|
+
if sub is not None:
|
384
|
+
self.invalidate("string type cannot have suffix")
|
385
|
+
|
386
|
+
# Check validity of bytes type
|
387
|
+
elif base == "bytes":
|
388
|
+
if not (sub is None or isinstance(sub, int)):
|
389
|
+
self.invalidate(
|
390
|
+
"bytes type must have either no suffix or a numerical suffix"
|
391
|
+
)
|
392
|
+
|
393
|
+
if isinstance(sub, int) and sub > 32:
|
394
|
+
self.invalidate("maximum 32 bytes for fixed-length bytes")
|
395
|
+
|
396
|
+
# Check validity of integer type
|
397
|
+
elif base in ("int", "uint"):
|
398
|
+
if not isinstance(sub, int):
|
399
|
+
self.invalidate("integer type must have numerical suffix")
|
400
|
+
|
401
|
+
if sub < 8 or sub > 256:
|
402
|
+
self.invalidate("integer size out of bounds (max 256 bits)")
|
403
|
+
|
404
|
+
if sub % 8 != 0:
|
405
|
+
self.invalidate("integer size must be multiple of 8")
|
406
|
+
|
407
|
+
# Check validity of fixed type
|
408
|
+
elif base in ("fixed", "ufixed"):
|
409
|
+
if not isinstance(sub, tuple):
|
410
|
+
self.invalidate(
|
411
|
+
"fixed type must have suffix of form <bits>x<exponent>, "
|
412
|
+
"e.g. 128x19",
|
413
|
+
)
|
414
|
+
|
415
|
+
bits, minus_e = sub
|
416
|
+
|
417
|
+
if bits < 8 or bits > 256:
|
418
|
+
self.invalidate("fixed size out of bounds (max 256 bits)")
|
419
|
+
|
420
|
+
if bits % 8 != 0:
|
421
|
+
self.invalidate("fixed size must be multiple of 8")
|
422
|
+
|
423
|
+
if minus_e < 1 or minus_e > 80:
|
424
|
+
self.invalidate(
|
425
|
+
f"fixed exponent size out of bounds, {minus_e} must be in 1-80"
|
426
|
+
)
|
427
|
+
|
428
|
+
# Check validity of hash type
|
429
|
+
elif base == "hash":
|
430
|
+
if not isinstance(sub, int):
|
431
|
+
self.invalidate("hash type must have numerical suffix")
|
432
|
+
|
433
|
+
# Check validity of address type
|
434
|
+
elif base == "address":
|
435
|
+
if sub is not None:
|
436
|
+
self.invalidate("address cannot have suffix")
|
437
|
+
|
438
|
+
|
439
|
+
TYPE_ALIASES: Final = {
|
440
|
+
"int": "int256",
|
441
|
+
"uint": "uint256",
|
442
|
+
"fixed": "fixed128x18",
|
443
|
+
"ufixed": "ufixed128x18",
|
444
|
+
"function": "bytes24",
|
445
|
+
"byte": "bytes1",
|
446
|
+
}
|
447
|
+
|
448
|
+
TYPE_ALIAS_RE: Final = re.compile(
|
449
|
+
rf"\b({'|'.join(re.escape(a) for a in TYPE_ALIASES.keys())})\b"
|
450
|
+
)
|
451
|
+
|
452
|
+
|
453
|
+
def normalize(type_str: TypeStr) -> TypeStr:
|
454
|
+
"""
|
455
|
+
Normalizes a type string into its canonical version e.g. the type string
|
456
|
+
'int' becomes 'int256', etc.
|
457
|
+
|
458
|
+
:param type_str: The type string to be normalized.
|
459
|
+
:returns: The canonical version of the input type string.
|
460
|
+
"""
|
461
|
+
return TYPE_ALIAS_RE.sub(
|
462
|
+
lambda match: TYPE_ALIASES[match.group(0)],
|
463
|
+
type_str,
|
464
|
+
)
|
465
|
+
|
466
|
+
|
467
|
+
parse: Final = visitor.parse
|
faster_eth_abi/io.py
ADDED
@@ -0,0 +1,103 @@
|
|
1
|
+
from io import (
|
2
|
+
BytesIO,
|
3
|
+
)
|
4
|
+
from typing import (
|
5
|
+
TYPE_CHECKING,
|
6
|
+
Any,
|
7
|
+
Final,
|
8
|
+
List,
|
9
|
+
Tuple,
|
10
|
+
final,
|
11
|
+
)
|
12
|
+
|
13
|
+
from mypy_extensions import (
|
14
|
+
mypyc_attr,
|
15
|
+
)
|
16
|
+
|
17
|
+
if TYPE_CHECKING:
|
18
|
+
from _typeshed import (
|
19
|
+
ReadableBuffer,
|
20
|
+
)
|
21
|
+
|
22
|
+
|
23
|
+
@final
|
24
|
+
@mypyc_attr(allow_interpreted_subclasses=True)
|
25
|
+
class ContextFramesBytesIO(BytesIO):
|
26
|
+
"""
|
27
|
+
A byte stream which can track a series of contextual frames in a stack. This
|
28
|
+
data structure is necessary to perform nested decodings using the
|
29
|
+
:py:class:``HeadTailDecoder`` since offsets present in head sections are
|
30
|
+
relative only to a particular encoded object. These offsets can only be
|
31
|
+
used to locate a position in a decoding stream if they are paired with a
|
32
|
+
contextual offset that establishes the position of the object in which they
|
33
|
+
are found.
|
34
|
+
|
35
|
+
For example, consider the encoding of a value for the following type::
|
36
|
+
|
37
|
+
type: (int,(int,int[]))
|
38
|
+
value: (1,(2,[3,3]))
|
39
|
+
|
40
|
+
There are two tuples in this type: one inner and one outer. The inner tuple
|
41
|
+
type contains a dynamic type ``int[]`` and, therefore, is itself dynamic.
|
42
|
+
This means that its value encoding will be placed in the tail section of the
|
43
|
+
outer tuple's encoding. Furthermore, the inner tuple's encoding will,
|
44
|
+
itself, contain a tail section with the encoding for ``[3,3]``. All
|
45
|
+
together, the encoded value of ``(1,(2,[3,3]))`` would look like this (the
|
46
|
+
data values are normally 32 bytes wide but have been truncated to remove the
|
47
|
+
redundant zeros at the beginnings of their encodings)::
|
48
|
+
|
49
|
+
offset data
|
50
|
+
--------------------------
|
51
|
+
^ 0 0x01
|
52
|
+
| 32 0x40 <-- Offset of object A in global frame (64)
|
53
|
+
-----|--------------------
|
54
|
+
Global frame ^ 64 0x02 <-- Beginning of object A (64 w/offset 0 = 64)
|
55
|
+
| | 96 0x40 <-- Offset of object B in frame of object A (64)
|
56
|
+
-----|-Object A's frame---
|
57
|
+
| | 128 0x02 <-- Beginning of object B (64 w/offset 64 = 128)
|
58
|
+
| | 160 0x03
|
59
|
+
v v 192 0x03
|
60
|
+
--------------------------
|
61
|
+
|
62
|
+
Note that the offset of object B is encoded as 64 which only specifies the
|
63
|
+
beginning of its encoded value relative to the beginning of object A's
|
64
|
+
encoding. Globally, object B is located at offset 128. In order to make
|
65
|
+
sense out of object B's offset, it needs to be positioned in the context of
|
66
|
+
its enclosing object's frame (object A).
|
67
|
+
"""
|
68
|
+
|
69
|
+
def __init__(self, initial_bytes: "ReadableBuffer"):
|
70
|
+
super().__init__(initial_bytes)
|
71
|
+
|
72
|
+
self._frames: Final[List[Tuple[int, int]]] = []
|
73
|
+
self._total_offset = 0
|
74
|
+
|
75
|
+
def seek_in_frame(self, pos: int, *args: Any, **kwargs: Any) -> None:
|
76
|
+
"""
|
77
|
+
Seeks relative to the total offset of the current contextual frames.
|
78
|
+
"""
|
79
|
+
self.seek(self._total_offset + pos, *args, **kwargs)
|
80
|
+
|
81
|
+
def push_frame(self, offset: int) -> None:
|
82
|
+
"""
|
83
|
+
Pushes a new contextual frame onto the stack with the given offset and a
|
84
|
+
return position at the current cursor position then seeks to the new
|
85
|
+
total offset.
|
86
|
+
"""
|
87
|
+
self._frames.append((offset, self.tell()))
|
88
|
+
self._total_offset += offset
|
89
|
+
|
90
|
+
self.seek_in_frame(0)
|
91
|
+
|
92
|
+
def pop_frame(self):
|
93
|
+
"""
|
94
|
+
Pops the current contextual frame off of the stack and returns the
|
95
|
+
cursor to the frame's return position.
|
96
|
+
"""
|
97
|
+
try:
|
98
|
+
offset, return_pos = self._frames.pop()
|
99
|
+
except IndexError:
|
100
|
+
raise IndexError("no frames to pop")
|
101
|
+
self._total_offset -= offset
|
102
|
+
|
103
|
+
self.seek(return_pos)
|
Binary file
|
faster_eth_abi/packed.py
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
from typing import (
|
2
|
+
Final,
|
3
|
+
)
|
4
|
+
|
5
|
+
from .codec import (
|
6
|
+
ABIEncoder,
|
7
|
+
)
|
8
|
+
from .registry import (
|
9
|
+
registry_packed,
|
10
|
+
)
|
11
|
+
|
12
|
+
default_encoder_packed: Final = ABIEncoder(registry_packed)
|
13
|
+
|
14
|
+
encode_packed: Final = default_encoder_packed.encode
|
15
|
+
is_encodable_packed: Final = default_encoder_packed.is_encodable
|
faster_eth_abi/py.typed
ADDED
File without changes
|