Welcome to the eth-abi documentation!
The eth-abi
library provides low level utilities for converting python
values to and from solidity’s binary ABI format.
For detailed information on ABI types and their encodings, refer to the solidity ABI specification.
Credit
Though much of the code has been revised, the eth-abi
library was
originally extracted from the pyethereum
library which was authored by
Vitalik Buterin.
Table of Contents
Encoding
Encoding Python Values
Python values can be encoded into binary values for a given ABI type as follows:
>>> from eth_abi import encode
>>> # encode a single ABI type
>>> encode(['uint256'], [12345])
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0009'
>>> # encode multiple ABI types
>>> encode(['bytes32', 'bytes32'], [b'a', b'b'])
b'a\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00b\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
>>> # encode a single tuple type with two `bytes32` types
>>> encode(['(bytes32,bytes32)'], [(b'a', b'b')])
b'a\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00b\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
The eth_abi.encoding.BaseEncoder.encode()
function provides an API for
encoding python values into binary values for ABI types. It accepts a sequence of ABI
type strings as the first argument and a sequence of python values to be encoded into
the respective ABI types as the second argument.
Checking for Encodability
It is also possible to check whether or not a certain python value is encodable
for a given ABI type using is_encodable
:
>>> from eth_abi import is_encodable
>>> is_encodable('int', 2)
True
>>> is_encodable('int', 'foo')
False
>>> is_encodable('(int,bool)', (0, True))
True
>>> is_encodable('(int,bool)', (0, 0))
False
Non-Standard Packed Mode Encoding
Warning
Non-standard packed mode encoding is an experimental feature in the eth-abi library. Use at your own risk and please report any problems at https://github.com/ethereum/eth-abi/issues.
In certain cases, the Solidity programming language uses a non-standard packed encoding. You can encode values in this format like so:
>>> from eth_abi.packed import encode_packed
>>> # encode_packed for a single ABI type
>>> encode_packed(['uint32'], [12345])
b'\x00\x0009'
>>> # encode_packed for multiple ABI types
>>> encode_packed(['int8[]', 'uint32'], ([1, 2, 3, 4], 12345))
b'\x01\x02\x03\x04\x00\x0009'
>>> # encode_packed for a tuple with `uint8[]` and `uint32` types
>>> encode_packed(['(int8[],uint32)'], [([1, 2, 3, 4], 12345)])
b'\x01\x02\x03\x04\x00\x0009'
More information about this encoding format is available at https://solidity.readthedocs.io/en/develop/abi-spec.html#non-standard-packed-mode.
Decoding
Decoding ABI Values
Binary values for a given ABI type can be decoded into python values as follows:
>>> from eth_abi import decode
>>> # decode a single ABI type
>>> decode(['uint256'], b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0009')
(12345,)
>>> # decode multiple ABI types
>>> decode(['bytes1', 'bytes1'], b'a\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00b\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')
(b'a', b'b')
>>> # decode a single tuple type with two `bytes1` types
>>> decode(['(bytes1,bytes1)'], b'a\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00b\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')
((b'a', b'b'),)
The eth_abi.decoding.BaseDecoder.decode()
function provides an API for
decoding binary values for ABI types into python values. It accepts a sequence of
ABI type strings as the first argument and the binary data to be decoded, as a python
bytes
or bytearray
value, as the second argument.
Strict Mode
By default, the decoder will raise an exception if the binary data to be decoded
is not padded to the next 32-byte boundary. This behavior can be disabled for the
ByteStringDecoder
(the default decoder for bytes
and string
types) by
passing strict=False
to the eth_abi.abi.decode()
method. Turning off
strict mode will also ignore any trailing bytes beyond the data size specified. This
means that if there is any padding, the validation for only empty bytes in the padding
area is also ignored.
>>> from eth_abi import abi
>>> # decode a bytes value without strict mode
>>> hex_val = (
... # offset to data is 32 bytes:
... "0000000000000000000000000000000000000000000000000000000000000020"
... # length of data is 1 byte:
... "0000000000000000000000000000000000000000000000000000000000000001"
... # b"\x01" with less than 32 bytes of padding
... # and not strictly padded with only zero bytes:
... "0100000000000001020300"
... )
>>> (decoded,) = abi.decode(['bytes'], bytes.fromhex(hex_val), strict=False)
>>> decoded
b'\x01'
Registry
The eth-abi
library uses a central registry to route encoding/decoding
operations for different ABI types to an appropriate encoder/decoder callable
or class. Using the registry, the coding behavior of any ABI type can be
customized and additional coding behavior for new ABI types can be added.
Adding Simple Types
Here’s an example of how you might add support for a simple “null” type using callables:
from eth_abi.exceptions import EncodingError, DecodingError
from eth_abi.registry import registry
# Define and register the coders
NULL_ENCODING = b'\x00' * 32
def encode_null(x):
if x is not None:
raise EncodingError('Unsupported value')
return NULL_ENCODING
def decode_null(stream):
if stream.read(32) != NULL_ENCODING:
raise DecodingError('Not enough data or wrong data')
return None
registry.register('null', encode_null, decode_null)
# Try them out
from eth_abi import encode, decode
assert encode(['null'], [None]) == NULL_ENCODING
(decoded_null_val,) = decode(['null'], NULL_ENCODING)
assert decoded_null_val is None
encoded_tuple = encode(['(int,null)'], [(1, None)])
assert encoded_tuple == b'\x00' * 31 + b'\x01' + NULL_ENCODING
(decoded_tuple,) = decode(['(int,null)'], encoded_tuple)
assert decoded_tuple == (1, None)
In the above example, we define two coder callables and register them to handle
exact matches against the 'null'
type string. We do this by calling
register
on the registry object.
When a call is made to one of the coding functions (such as
encode()
or decode()
),
the type string which is provided (which we’ll call query
) is sent to the registry.
This query
will be checked against every registration in the registry. Since we
created a registration for the exact type string 'null'
, coding operations for that
type string will be routed to the encoder and decoder which were provided by
the call to register
. This also works when the registered type string
appears in a compound type as with the tuple type in the example.
Note
As a safety measure, the registry will raise an exception if more than one registration matches a query. Take care to ensure that your custom registrations don’t conflict with existing ones.
Adding More Complex Types
Sometimes, it’s convenient to register a single class to handle encodings or
decodings for a range of types. For example, we shouldn’t have to make
separate registrations for the 'uint256'
and 'uint8'
types or for the
'(int,bool)'
and '(int,int)'
types. For cases like this, we can make
registrations for custom subclasses of BaseEncoder
and
BaseDecoder
.
Let’s say we want to modify our “null” type above so that we can specify the number of 32-byte words that the encoded null value will occupy in the data stream. We could do that in the following way:
from eth_abi.decoding import BaseDecoder
from eth_abi.encoding import BaseEncoder
from eth_abi.exceptions import EncodingError, DecodingError
from eth_abi.registry import registry
# Define and register the coders
NULL_ENCODING = b'\x00' * 32
class EncodeNull(BaseEncoder):
word_width = None
@classmethod
def from_type_str(cls, type_str, registry):
word_width = int(type_str[4:])
return cls(word_width=word_width)
def encode(self, value):
self.validate_value(value)
return NULL_ENCODING * self.word_width
def validate_value(self, value):
if value is not None:
raise EncodingError('Unsupported value')
class DecodeNull(BaseDecoder):
word_width = None
@classmethod
def from_type_str(cls, type_str, registry):
word_width = int(type_str[4:])
return cls(word_width=word_width)
def decode(self, stream):
byts = stream.read(32 * self.word_width)
if byts != NULL_ENCODING * self.word_width:
raise DecodingError('Not enough data or wrong data')
return None
registry.register(
lambda x: x.startswith('null'),
EncodeNull,
DecodeNull,
label='null',
)
# Try them out
from eth_abi import encode, decode
assert encode(['null2'], [None]) == NULL_ENCODING * 2
(decoded_null_val,) = decode(['null2'], NULL_ENCODING * 2)
assert decoded_null_val is None
encoded_tuple = encode(['(int,null2)'], [(1, None)])
assert encoded_tuple == b'\x00' * 31 + b'\x01' + NULL_ENCODING * 2
(decoded_tuple,) = decode(['(int,null2)'], encoded_tuple)
assert decoded_tuple == (1, None)
There are a few differences here from our first example. Now, we are providing
a type string matcher function instead of a literal type string with our call
to register
. Also, we are not using simple callables for our coding
functions. We have created two custom coder classes which inherit from
BaseEncoder
and BaseDecoder
respectively. Additionally, we have
given a label to this registration in case we want to easily delete the
registration later.
The matcher function lambda x: x.startswith('null')
accepts a query type
string and returns True
or False
to indicate if the query should be
matched with our registration. If a query is uniquely matched with our
registration in this way, the registry then calls from_type_str
on our
EncodeNull
or DecodeNull
class to obtain an appropriate instance of the
class based on any additional information contained in the type string. In
this example, that additional information is the number that appears at the end
of the type string (e.g. '2'
in 'null2'
). Through this process, the
registry can determine an encoder or decoder for any type string of the form
'null<M>'
.
There are a few more details here that are worth explaining.
Both of our coder subclasses have some similar aspects. They both have a class
property word_width
. They also have the same implementation for the
from_type_str
method. The BaseEncoder
and BaseDecoder
classes both inherit from BaseCoder
which causes any keyword arguments
passed to __init__
to be used to set the value of properties on an instance
if a class property with the same name is found. This is why our
implementations of from_type_str
instantiate our coder classes with the
keyword argument word_width
. Using this pattern, coder classes can
describe what “settings” they support while providing an easy way to assign
values to those settings. Both of our coder classes use the same settings.
The settings are initialized from the type string in the same way. Therefore,
they have the same implementation for from_type_str
. For clarity, the
same word_width
property and from_type_str
implementation appear in
both classes but they could also have been extracted out into a mixin class.
Our coder classes also implement the BaseEncoder.encode
and
BaseDecoder.decode
methods. These methods work in the same way as the
simple callable coders in our first example except that they have access to the
settings which were extracted from the type string when the class was
instantiated via the from_type_str
method by the registry. This allows
them to handle null values of an arbitrary width in the data stream. As with
the callable coders, the BaseEncoder.encode
and
BaseDecoder.decode
implementations are polite and raise an appropriate
exception when anything goes wrong. EncodeNull
does this via an
implementation of BaseEncoder.validate_value
. For encoder classes, it
is necessary to implement this method since it is used by the
is_encodable
function to determine if a value is encodable without doing
the extra work of encoding it. For certain data types, this can be more
efficient than simply attempting to encode a value.
Handling Malformed Strings
Sometimes a string
we receive is malformed, i.e. not utf-8 decodeable.
This will throw an error by default, but we can adjust how it is handled by
registering a new decoder with our preferred handler.
The StringDecoder
class uses the Python bytes.decode()
method at its core,
which accepts an errors
argument to define how un-decodeable bytes are handled.
StringDecoder
uses errors=strict
by default, but can also accept
surrogateescape
, ignore
, replace
, or backslashreplace
. You can read
more about each of these options in the Python
docs.
The ability to handle malformed strings is only available for decoding. It is assumed that attempting to encode a malformed string indicates user error.
from eth_abi import decode, encode
from eth_abi.decoding import StringDecoder
from eth_abi.encoding import TextStringEncoder
from eth_abi.registry import registry
# encode a string
test_string = encode(["string"], ["cat"])
assert (test_string == b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00 \x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03cat\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
)
# insert an un-decodeable byte
bad_string = test_string[:65] + b"\xff" + test_string[66:]
# verify the original test_string decodes properly
assert decode(["string"], test_string) == ("cat",)
# default `StringDecoder` will throw an error
try:
decode(["string"], bad_string)
except UnicodeDecodeError as e:
assert "'utf-8' codec can't decode byte 0xff" in str(e)
# If we want to handle un-decodeable strings, we can register multiple string
# decoders, each with its own `handle_string_errors` option
registry.register(
"surrogateescape_string",
TextStringEncoder,
StringDecoder(handle_string_errors="surrogateescape")
)
registry.register(
"backslashreplace_string",
TextStringEncoder,
StringDecoder(handle_string_errors="backslashreplace"),
)
assert decode(["surrogateescape_string"], bad_string) == ("c\udcfft",)
assert decode(["backslashreplace_string"], bad_string) == ("c\\xfft",)
Codecs
Though the default registry can be customized by making additional coder
registrations or by unregistering existing coders (see Registry),
sometimes a user might wish to create their own registry entirely. In that
case, they can still use the usual API for encoding and decoding values (see
Encoding and Decoding) with their own registry by using the
ABICodec
or ABIEncoder
class.
Using a Custom Registry
Here’s an example of how you might add support for a simple “null” type using a custom registry while continuing to use the porcelain encoding and decoding API:
from eth_abi.codec import ABICodec
from eth_abi.exceptions import EncodingError, DecodingError
from eth_abi.registry import ABIRegistry
# Define and register the coders
NULL_ENCODING = b'\x00' * 32
def encode_null(x):
if x is not None:
raise EncodingError('Unsupported value')
return NULL_ENCODING
def decode_null(stream):
if stream.read(32) != NULL_ENCODING:
raise DecodingError('Not enough data or wrong data')
return None
registry = ABIRegistry()
registry.register('null', encode_null, decode_null)
# Try them out
codec = ABICodec(registry)
assert codec.encode(['null'], [None]) == NULL_ENCODING
(decoded_null_val,) = codec.decode(['null'], NULL_ENCODING)
assert decoded_null_val is None
In the above example, we define two coder callables and register them to handle
exact matches against the 'null'
type string in a custom registry. For
more information about coder registrations, see
Adding Simple Types.
We then create a custom codec object with our custom registry and use this to encode and decode byte sequences. This allows us to continue using the porcelain API (described in the Encoding and Decoding sections) with our custom registry.
Copying an Existing Registry
Sometimes, it’s more convenient to use an existing registry but with only one or two small modifications. This can be done via a registry’s copying or cloning capability coupled with the use of a custom codec:
from eth_abi.codec import ABICodec
from eth_abi.registry import registry as default_registry
registry = default_registry.copy()
registry.unregister('address')
codec = ABICodec(registry)
try:
codec.encode(['address'], [None])
except ValueError:
pass
else:
# We shouldn't reach this since the above code will cause an exception
raise Exception('unreachable')
default_codec = ABICodec(default_registry)
# The default registry is unaffected since a copy was made
assert (
default_codec.encode(['address'], ['0x' + 'ff' * 20]) ==
b'\x00' * 12 + b'\xff' * 20
)
Using a Custom Stream Class
If a user wishes to customize the behavior of the internal stream class used for decoding, they can do the following:
from eth_abi.codec import ABIEncoder, ABIDecoder
from eth_abi.registry import registry
class MyStream:
# Custom behavior...
pass
class MyDecoder(ABIDecoder):
stream_class = MyStream
class MyCodec(ABIEncoder, MyDecoder):
pass
codec = MyCodec(registry)
Nested Dynamic Arrays
The eth-abi
library supports the Solidity ABIv2 encoding format for nested
dynamic arrays. This means that values for data types such as the following
are legal and encodable/decodable: int[][]
, string[]
, string[2]
,
etc.
Grammar
The eth-abi
library exposes its type string parsing and normalization
facilities as part of its public API.
Parsing a Type String
Here are some examples of how you might parse a type string into a simple AST and do various operations with the results:
>>> from eth_abi.grammar import ABIType, BasicType, TupleType, parse
>>> tuple_type = parse('(int256,bytes,ufixed128x18,bool[])[2]')
>>> # Checking if a type is a tuple or a basic type
>>> isinstance(tuple_type, ABIType)
True
>>> isinstance(tuple_type, TupleType)
True
>>> [isinstance(i, ABIType) for i in tuple_type.components]
[True, True, True, True]
>>> [isinstance(i, BasicType) for i in tuple_type.components]
[True, True, True, True]
>>> int_type, bytes_type, ufixed_type, bool_type = tuple_type.components
>>> # Inspecting parts of types
>>> len(tuple_type.components)
4
>>> tuple_type.arrlist
((2,),)
>>> int_type.base, int_type.sub, int_type.arrlist
('int', 256, None)
>>> bytes_type.base, bytes_type.sub, bytes_type.arrlist
('bytes', None, None)
>>> ufixed_type.base, ufixed_type.sub, ufixed_type.arrlist
('ufixed', (128, 18), None)
>>> bool_type.base, bool_type.sub, bool_type.arrlist
('bool', None, ((),))
>>> # Checking for arrays or dynamicism
>>> tuple_type.is_array, tuple_type.is_dynamic
(True, True)
>>> int_type.is_array, int_type.is_dynamic
(False, False)
>>> bytes_type.is_array, bytes_type.is_dynamic
(False, True)
>>> ufixed_type.is_array, ufixed_type.is_dynamic
(False, False)
>>> bool_type.is_array, bool_type.is_dynamic
(True, True)
Checking Types for Validity
Types can be checked for validity. For example, uint9
is not a valid type
because the bit-width of int
types must be a multiple of 8
:
>>> from eth_abi.grammar import parse
>>> basic_type = parse('uint9')
>>> # The basic type is not valid because the int type's bit-width is not valid
>>> basic_type.validate()
Traceback (most recent call last):
...
eth_abi.exceptions.ABITypeError: For 'uint9' type at column 1 in 'uint9': integer size must be multiple of 8
>>> tuple_type = parse('(bool,uint9)')
>>> # The tuple type is not valid because it contains an int type with an invalid bit-width
>>> tuple_type.validate()
Traceback (most recent call last):
...
eth_abi.exceptions.ABITypeError: For 'uint9' type at column 7 in '(bool,uint9)': integer size must be multiple of 8
Normalizing Type Strings
Type strings can be normalized to their canonical form. This amounts to
converting type aliases like uint
to uint256
and so forth:
>>> from eth_abi.grammar import normalize
>>> normalize('uint')
'uint256'
>>> normalize('(uint,(ufixed,function))')
'(uint256,(ufixed128x18,bytes24))'
Internally, eth-abi
will only normalize type strings just before creating
coders for a type. This is done automatically such that type strings passed to
eth-abi
do not need to be normalized before hand.
Tools
The eth_abi.tools
module provides extra resources to users of eth-abi
that are not required for typical use. It can be installed with pip
as an
extra requirement:
python -m pip install "eth-abi[tools]"
ABI Type Strategies
The tools
module provides the get_abi_strategy()
function. This function returns a hypothesis strategy (value generator) for any
given ABI type specified by its canonical string representation:
>>> from eth_abi.tools import get_abi_strategy
>>> uint_st = get_abi_strategy('uint8')
>>> uint_st
integers(min_value=0, max_value=255)
>>> uint_list_st = get_abi_strategy('uint8[2]')
>>> uint_list_st
lists(elements=integers(min_value=0, max_value=255), min_size=2, max_size=2)
>>> fixed_st = get_abi_strategy('fixed8x1')
>>> fixed_st
decimals(min_value=-128, max_value=127, places=0).map(scale_by_Eneg1)
>>> tuple_st = get_abi_strategy('(bool,string)')
>>> tuple_st
tuples(booleans(), text())
Hypothesis strategies can be used to conduct property testing on contract code. For more information on property testing, visit the Hypothesis homepage or the Hypothesis readthedocs site.
API
eth_abi.abi module
eth_abi.base module
- class eth_abi.base.BaseCoder(**kwargs)
Bases:
object
Base class for all encoder and decoder classes.
- classmethod from_type_str(type_str, registry)
Used by
ABIRegistry
to get an appropriate encoder or decoder instance for the given type string and type registry.
eth_abi.codec module
- class eth_abi.codec.ABICodec(registry: ABIRegistry)
Bases:
ABIEncoder
,ABIDecoder
- class eth_abi.codec.ABIDecoder(registry: ABIRegistry)
Bases:
BaseABICoder
Wraps a registry to provide last-mile decoding functionality.
- decode(types: Iterable[str], data: bytes | bytearray, strict: bool = True) Tuple[Any, ...]
Decodes the binary value
data
as a sequence of values of the ABI types intypes
via the head-tail mechanism into a tuple of equivalent python values.- Parameters:
types – A list or tuple of string representations of the ABI types that will be used for decoding e.g.
('uint256', 'bytes[]', '(int,int)')
data – The binary value to be decoded.
strict – If
False
, dynamic-type decoders will ignore validations such as making sure the data is padded to a multiple of 32 bytes or checking that padding bytes are zero / empty.False
is how the Solidity ABI decoder currently works. However,True
is the default for the eth-abi library.
- Returns:
A tuple of equivalent python values for the ABI values represented in
data
.
- stream_class
alias of
ContextFramesBytesIO
- class eth_abi.codec.ABIEncoder(registry: ABIRegistry)
Bases:
BaseABICoder
Wraps a registry to provide last-mile encoding functionality.
- encode(types: Iterable[str], args: Iterable[Any]) bytes
Encodes the python values in
args
as a sequence of binary values of the ABI types intypes
via the head-tail mechanism.- Parameters:
types – A list or tuple of string representations of the ABI types that will be used for encoding e.g.
('uint256', 'bytes[]', '(int,int)')
args – A list or tuple of python values to be encoded.
- Returns:
The head-tail encoded binary representation of the python values in
args
as values of the ABI types intypes
.
- is_encodable(typ: str, arg: Any) bool
Determines if the python value
arg
is encodable as a value of the ABI typetyp
.- Parameters:
typ – A string representation for the ABI type against which the python value
arg
will be checked e.g.'uint256'
,'bytes[]'
,'(int,int)'
, etc.arg – The python value whose encodability should be checked.
- Returns:
True
ifarg
is encodable as a value of the ABI typetyp
. Otherwise,False
.
- is_encodable_type(typ: str) bool
Returns
True
if values for the ABI typetyp
can be encoded by this codec.- Parameters:
typ – A string representation for the ABI type that will be checked for encodability e.g.
'uint256'
,'bytes[]'
,'(int,int)'
, etc.- Returns:
True
if values fortyp
can be encoded by this codec. Otherwise,False
.
- class eth_abi.codec.BaseABICoder(registry: ABIRegistry)
Bases:
object
Base class for porcelain coding APIs. These are classes which wrap instances of
ABIRegistry
to provide last-mile coding functionality.
eth_abi.decoding module
- class eth_abi.decoding.BaseDecoder(**kwargs)
Bases:
BaseCoder
Base class for all decoder classes. Subclass this if you want to define a custom decoder class. Subclasses must also implement
BaseCoder.from_type_str
.- abstract decode(stream: ContextFramesBytesIO) Any
Decodes the given stream of bytes into a python value. Should raise
exceptions.DecodingError
if a python value cannot be decoded from the given byte stream.
eth_abi.encoding module
- class eth_abi.encoding.BaseEncoder(**kwargs)
Bases:
BaseCoder
Base class for all encoder classes. Subclass this if you want to define a custom encoder class. Subclasses must also implement
BaseCoder.from_type_str
.- abstract encode(value: Any) bytes
Encodes the given value as a sequence of bytes. Should raise
exceptions.EncodingError
ifvalue
cannot be encoded.
- classmethod invalidate_value(value: ~typing.Any, exc: ~typing.Type[Exception] = <class 'eth_abi.exceptions.EncodingTypeError'>, msg: str | None = None) None
Throws a standard exception for when a value is not encodable by an encoder.
- abstract validate_value(value: Any) None
Checks whether or not the given value can be encoded by this encoder. If the given value cannot be encoded, must raise
exceptions.EncodingError
.
eth_abi.exceptions module
- exception eth_abi.exceptions.ABITypeError
Bases:
ValueError
Raised when a parsed ABI type has inconsistent properties; for example, when trying to parse the type string
'uint7'
(which has a bit-width that is not congruent with zero modulo eight).
- exception eth_abi.exceptions.DecodingError
Bases:
Exception
Base exception for any error that occurs during decoding.
- exception eth_abi.exceptions.EncodingError
Bases:
Exception
Base exception for any error that occurs during encoding.
- exception eth_abi.exceptions.EncodingTypeError
Bases:
EncodingError
Raised when trying to encode a python value whose type is not supported for the output ABI type.
- exception eth_abi.exceptions.IllegalValue
Bases:
EncodingError
Raised when trying to encode a python value with the correct type but with a value that is not considered legal for the output ABI type.
fixed128x19_encoder(Decimal('NaN')) # cannot encode NaN
- exception eth_abi.exceptions.InsufficientDataBytes
Bases:
DecodingError
Raised when there are insufficient data to decode a value for a given ABI type.
- exception eth_abi.exceptions.InvalidPointer
Bases:
DecodingError
Raised when the pointer to a value in the ABI encoding is invalid.
- exception eth_abi.exceptions.MultipleEntriesFound
Bases:
ValueError
,PredicateMappingError
Raised when multiple registrations are found for a type string in a registry’s internal mapping. This error is non-recoverable and indicates that a registry was configured incorrectly. Registrations are expected to cover completely distinct ranges of type strings.
Warning
In a future version of
eth-abi
, this error class will no longer inherit fromValueError
.
- exception eth_abi.exceptions.NoEntriesFound
Bases:
ValueError
,PredicateMappingError
Raised when no registration is found for a type string in a registry’s internal mapping.
Warning
In a future version of
eth-abi
, this error class will no longer inherit fromValueError
.
- exception eth_abi.exceptions.NonEmptyPaddingBytes
Bases:
DecodingError
Raised when the padding bytes of an ABI value are malformed.
- exception eth_abi.exceptions.ParseError(text, pos=-1, expr=None)
Bases:
ParseError
Raised when an ABI type string cannot be parsed.
- exception eth_abi.exceptions.PredicateMappingError
Bases:
Exception
Raised when an error occurs in a registry’s internal mapping.
- exception eth_abi.exceptions.ValueOutOfBounds
Bases:
IllegalValue
Raised when trying to encode a python value with the correct type but with a value that appears outside the range of valid values for the output ABI type.
ufixed8x1_encoder(Decimal('25.6')) # out of bounds
eth_abi.registry module
- class eth_abi.registry.ABIRegistry
- copy()
Copies a registry such that new registrations can be made or existing registrations can be unregistered without affecting any instance from which a copy was obtained. This is useful if an existing registry fulfills most of a user’s needs but requires one or two modifications. In that case, a copy of that registry can be obtained and the necessary changes made without affecting the original registry.
- has_encoder(type_str: str) bool
Returns
True
if an encoder is found for the given type stringtype_str
. Otherwise, returnsFalse
. RaisesMultipleEntriesFound
if multiple encoders are found.
- register(lookup: str | Callable[[str], bool], encoder: Callable[[Any], bytes] | Type[BaseEncoder], decoder: Callable[[ContextFramesBytesIO], Any] | Type[BaseDecoder], label: str | None = None) None
Registers the given
encoder
anddecoder
under the givenlookup
. A unique string label may be optionally provided that can be used to refer to the registration by name.- Parameters:
lookup – A type string or type string matcher function (predicate). When the registry is queried with a type string
query
to determine which encoder or decoder to use,query
will be checked against every registration in the registry. If a registration was created with a type string forlookup
, it will be considered a match iflookup == query
. If a registration was created with a matcher function forlookup
, it will be considered a match iflookup(query) is True
. If more than one registration is found to be a match, then an exception is raised.encoder – An encoder callable or class to use if
lookup
matches a query. Ifencoder
is a callable, it must accept a python value and return abytes
value. Ifencoder
is a class, it must be a valid subclass ofencoding.BaseEncoder
and must also implement thefrom_type_str
method onbase.BaseCoder
.decoder – A decoder callable or class to use if
lookup
matches a query. Ifdecoder
is a callable, it must accept a stream-like object of bytes and return a python value. Ifdecoder
is a class, it must be a valid subclass ofdecoding.BaseDecoder
and must also implement thefrom_type_str
method onbase.BaseCoder
.label – An optional label that can be used to refer to this registration by name. This label can be used to unregister an entry in the registry via the
unregister
method and its variants.
- register_decoder(lookup: str | Callable[[str], bool], decoder: Callable[[ContextFramesBytesIO], Any] | Type[BaseDecoder], label: str | None = None) None
Registers the given
decoder
under the givenlookup
. A unique string label may be optionally provided that can be used to refer to the registration by name. For more information about arguments, refer toregister
.
- register_encoder(lookup: str | Callable[[str], bool], encoder: Callable[[Any], bytes] | Type[BaseEncoder], label: str | None = None) None
Registers the given
encoder
under the givenlookup
. A unique string label may be optionally provided that can be used to refer to the registration by name. For more information about arguments, refer toregister
.
- unregister(label: str | None) None
Unregisters the entries in the encoder and decoder registries which have the label
label
.
- unregister_decoder(lookup_or_label: str | Callable[[str], bool]) None
Unregisters a decoder in the registry with the given lookup or label. If
lookup_or_label
is a string, the decoder with the labellookup_or_label
will be unregistered. If it is an function, the decoder with the lookup functionlookup_or_label
will be unregistered.
- unregister_encoder(lookup_or_label: str | Callable[[str], bool]) None
Unregisters an encoder in the registry with the given lookup or label. If
lookup_or_label
is a string, the encoder with the labellookup_or_label
will be unregistered. If it is an function, the encoder with the lookup functionlookup_or_label
will be unregistered.
eth_abi.grammar module
- class eth_abi.grammar.ABIType(arrlist=None, node=None)
Base class for results of type string parsing operations.
- arrlist
The list of array dimensions for a parsed type. Equal to
None
if type string has no array dimensions.
- property is_array
Equal to
True
if a type is an array type (i.e. if it has an array dimension list). Otherwise, equal toFalse
.
- property is_dynamic
Equal to
True
if a type has a dynamically sized encoding. Otherwise, equal toFalse
.
- property item_type
If this type is an array type, equal to an appropriate
ABIType
instance for the array’s items.
- node
The parsimonious
Node
instance associated with this parsed type. Used to generate error messages for invalid types.
- to_type_str()
Returns the string representation of an ABI type. This will be equal to the type string from which it was created.
- validate()
Validates the properties of an ABI type against the solidity ABI spec:
https://solidity.readthedocs.io/en/develop/abi-spec.html
Raises
ABITypeError
if validation fails.
- class eth_abi.grammar.TupleType(components, arrlist=None, *, node=None)
Represents the result of parsing a tuple type string e.g. “(int,bool)”.
- property is_dynamic
Equal to
True
if a type has a dynamically sized encoding. Otherwise, equal toFalse
.
- property item_type
If this type is an array type, equal to an appropriate
ABIType
instance for the array’s items.
- to_type_str()
Returns the string representation of an ABI type. This will be equal to the type string from which it was created.
- validate()
Validates the properties of an ABI type against the solidity ABI spec:
https://solidity.readthedocs.io/en/develop/abi-spec.html
Raises
ABITypeError
if validation fails.
- class eth_abi.grammar.BasicType(base, sub=None, arrlist=None, *, node=None)
Represents the result of parsing a basic type string e.g. “uint”, “address”, “ufixed128x19[][2]”.
- base
The base of a basic type e.g. “uint” for “uint256” etc.
- property is_dynamic
Equal to
True
if a type has a dynamically sized encoding. Otherwise, equal toFalse
.
- property item_type
If this type is an array type, equal to an appropriate
ABIType
instance for the array’s items.
- sub
The sub type of a basic type e.g.
256
for “uint256” or(128, 18)
for “ufixed128x18” etc. Equal toNone
if type string has no sub type.
- to_type_str()
Returns the string representation of an ABI type. This will be equal to the type string from which it was created.
- validate()
Validates the properties of an ABI type against the solidity ABI spec:
https://solidity.readthedocs.io/en/develop/abi-spec.html
Raises
ABITypeError
if validation fails.
- eth_abi.grammar.normalize(type_str)
Normalizes a type string into its canonical version e.g. the type string ‘int’ becomes ‘int256’, etc.
- Parameters:
type_str – The type string to be normalized.
- Returns:
The canonical version of the input type string.
- eth_abi.grammar.parse(type_str)
Parses a type string into an appropriate instance of
ABIType
. If a type string cannot be parsed, throwsParseError
.- Parameters:
type_str – The type string to be parsed.
- Returns:
An instance of
ABIType
containing information about the parsed type string.
eth_abi.tools module
- eth_abi.tools.get_abi_strategy(type_str: str) SearchStrategy
Returns a hypothesis strategy for the given ABI type.
- Parameters:
type_str – The canonical string representation of the ABI type for which a hypothesis strategy should be returned.
- Returns:
A hypothesis strategy for generating Python values that are encodable as values of the given ABI type.
Release Notes
eth-abi v5.1.0 (2024-04-01)
Internal Changes - for eth-abi Contributors
Upgrade parsimonious from
0.9
to0.10
, which is 15% faster (#231)Add
python 3.12
support, add all-format docs tests and nightly CI runs, reorg tests file structure to match CI grouping (#232)
eth-abi v5.0.1 (2024-03-04)
Bugfixes
Internal Changes - for eth-abi Contributors
Clear mypy
misc
-type errors and add top-levelpy.typed
file back (#221)
eth-abi v5.0.0 (2024-01-09)
Breaking Changes
Drop python 3.7 support (#217)
Internal Changes - for eth-abi Contributors
eth-abi v4.2.1 (2023-09-13)
Internal Changes - for eth-abi contributors
Add
build.os
section to readthedocs build settings (#213)
Miscellaneous changes
eth-abi v4.2.0 (2023-08-28)
Features
Allow turning off abi decoder “strict mode” when calling
abi.decode()
. (#198)
Bugfixes
Validate against zero-sized
tuple
types / empty Solidity structs. (#212)
eth-abi v4.1.0 (2023-06-08)
Features
updated StringDecoder class to allow user-defined handling of malformed strings, handle with strict by default (#187)
Internal Changes - for eth-abi contributors
eth-abi v4.0.0 (2023-03-22)
No significant changes.
eth-abi v4.0.0-beta.3 (2023-03-20)
Breaking Changes
Upgrade Parsimonious dependency to allow >=0.9,<0.10 (#201)
eth-abi v4.0.0-beta.2 (2022-11-21)
Features
Add support for Python 3.11 (#194)
Miscellaneous changes
eth-abi v4.0.0-beta.1 (2022-09-27)
Bugfixes
Reconcile differences in 32-byte padding between eth-abi encoders for dynamic types and Solidity’s abi.encode() for 0 or empty values (#158)
Breaking Changes
Remove
encode_abi_single()
,encode_packed_single()
, anddecode_abi_single()
. Renameencode_abi()
,encode_abi_packed()
, anddecode_abi()
toencode()
,encode_packed()
, anddecode()
, respectively. (#161)
Miscellaneous changes
eth-abi v3.0.1 (2022-07-18)
Deprecations
Add
DeprecationWarning
forencode_abi()
,encode_single()
,decode_abi()
, anddecode_single()
and add temporary versions ofabi.encode()
andabi.decode()
so users can start making these changes early. (#165)
Miscellaneous changes
eth_abi 3.0.0 (2022-01-19)
Features
Bugfixes
Catch ABITypeError exceptions when checking
has_encoder
(#148)
Improved Documentation
Fix broken badges in README (#144)
Miscellaneous changes
eth-abi v2.1.1 (2020-02-27)
Bugfixes
If subclassing
eth_abi.decoding.ContextFramesBytesIO.seek()
, the new method was not being used byseek_in_frame()
. Now it will be. (#139)
Internal Changes - for eth_abi contributors
Merged in project template, for changes in release scripts, docs, release notes, etc. (#140)
v2.1.0
Added support for “byte” alias for “bytes1” type.
Added support for custom stream class in
ABIDecoder
. See Using a Custom Stream Class.
v2.0.0
Includes all changes from v2.0.0 beta and alpha versions.
v2.0.0-beta.9
Added
eth_abi.tools
submodule with extra requirements installable withpip install eth-abi[tools]
. See Tools.
v2.0.0-beta.8
Added
has_encoder()
andis_encodable_type()
to facilitate checking for type validity against coder registrations.
v2.0.0-beta.7
Released March 24, 2019
Fixed an issue that caused custom types containing capital letters to be unparseable.
Removed PyPy support.
Added Python 3.7 support.
v2.0.0-beta.6
Added the grammar module to the public API. See Grammar.
Updated string API for the
ABIType
. Type strings forABIType
instances are now obtained via theto_type_str()
method instead of by invoking the builtin Pythonstr
function with an instance ofABIType
.
v2.0.0-beta.5
Added registry copying functionality to facilitate modification of the default registry. See Copying an Existing Registry.
v2.0.0-beta.4
Update eth-typing requirement to
>=2.0.0,<3.0.0
.
v2.0.0-beta.3
Added codec API to facilitate use of custom registries. See Codecs.
v2.0.0-beta.2
Released October 16, 2018
Bugfixes
Was accidentally allowing eth-typing v2. Now it requires eth-typing v1 only.
v2.0.0-beta.1
New Features
Added support for nested dynamic arrays from the Solidity version 2 ABI
Added support for non-standard packed mode encoding
Added support for tuple array types e.g.
(int,int)[]
Backwards Incompatible Changes
The
encode_single()
anddecode_single()
functions no longer accept type tuples to identify ABI types. Only type strings are accepted.The
collapse_type()
function has been removed. People who still wish to use this function should replicate its logic locally and where needed.The
process_type()
function has been removed in favor of theparse()
function. This should make the parsing API more consistent with the new parsimonious parser.
v2.0.0-alpha.1
Released July 19, 2018
v1.3.0
Released December 6, 2018
Bugfixes
Resolved an issue that was preventing discovery of type hints.
Misc
Updated eth-typing dependency version to
>=2.0.0,<3.0.0
.
v1.2.2
Released October 18, 2018
Bugfixes
Expand parsimonious dependency from v0.8.0 to v0.8.*
v1.2.1
Released October 16, 2018
Bugfixes
Was accidentally allowing eth-typing v2. Now it requires eth-typing v1 only. (backport from v2)
v1.2.0
Released August 28, 2018
New Features
Backported and added support for nested dynamic arrays from the Solidity version 2 ABI
v1.1.1
Released May 10, 2018
Bugfixes
is_encodable()
now returnsFalse
if aDecimal
has too many digits to be encoded in the givenfixed<M>x<N>
type. (It was previously raising aValueError
)Raise an
EncodingTypeError
instead of aTypeError
when trying to encode afloat
into afixed<M>x<N>
type.
v1.1.0
Released May 8, 2018
New Features
Added a Registry API (docs in progress) for looking up encoders by ABI type
Added support for types: tuple and fixedMxN
Added new is_encodable check for whether a value can be encoded with the given ABI type
Bugfixes
Fix RealDecoder bug that allowed values other than 32 bytes
Fix bug that accepted
stringN
as a valid ABI type. Strings may not have a fixed length.Stricter value checking when encoding a Decimal (Make sure it’s not a NaN)
Fix typos in “missing property” exceptions
Misc
Precompile regexes, for performance & clarity
Test fixups and switch to CircleCI
Readme improvements
Performance improvements
Drop Python 2 support cruft
v1.0.0
Released Feb 28, 2018
Confirmed pypy3 compatibility
Add support for eth-utils v1.0.0-beta2 and v1.0.1 stable
Testing improvements
v1.0.0-beta.0
Released Feb 5, 2018
Drop py2 support
Add support for eth-utils v1-beta1
v0.5.0
Rename to
eth-abi
for consistency across github/pypi/python-module
v0.4.4
Better error messages for decoder errors.
v0.4.3
Bugfix for
process_type
to support byte string type arrguments
v0.4.2
process_type
now auto-expands all types which have omittied their sizes.
v0.4.1
Support for
function
types.
v0.3.1
Bugfix for small signed integer and real encoding/decoding
v0.3.1
Bugfix for faulty release.
v0.3.0
Depart from the original pyethereum encoding/decoding logic.
Fully rewritten encoder and decoder functionality.
v0.2.2
Fix a handful of bytes encoding issues.
v0.2.1
Use pyrlp utility functions for big_endian int operations
v0.2.0
Bugfixes from upstream pyethereum repository for encoding/decoding
Python 3 Support
v0.1.0
Initial release