ETH_ABI(1)

NAME
eth_abi - Ethereum Contract Interface (ABI) Utility 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 <https://solidity.readthedocs.io/en/latest/abi-spec.html>.

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.

INSTALLATION

    python -m pip install Ethereum Contract Interface (ABI) Utility

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() <https://docs.python.org/3.10/library/stdtypes.html#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 <https://docs.python.org/3/library/codecs.html#error-handlers>.

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 <#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 <#encoding> and Decoding <#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 <#registry-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 <#encoding> and Decoding <#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(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 <https://hypothesis.works> or the Hypothesis readthedocs site <https://hypothesis.readthedocs.io/en/latest/>.

  API

  eth_abi.abi module

  eth_abi.base module

  eth_abi.codec module

  eth_abi.decoding module

  eth_abi.encoding module

  eth_abi.exceptions module

  eth_abi.registry module

  eth_abi.grammar module

  eth_abi.tools module

  Release Notes

  eth-abi v5.2.0 (2025-01-14)

  Features

•   Merge template, adding py313 support, replace bumpversion with bump-my-version, raise required hypothesis versions. (#241 <https://github.com/ethereum/eth-abi/issues/241>)

  Internal Changes - for eth-abi Contributors

•   Merge template, fixing docs CI and other various updates (#236 <https://github.com/ethereum/eth-abi/issues/236>)

  eth-abi v5.1.0 (2024-04-01)

  Internal Changes - for eth-abi Contributors

•   Upgrade parsimonious <https://github.com/erikrose/parsimonious> from 0.9 to 0.10, which is 15% faster (#231 <https://github.com/ethereum/eth-abi/issues/231>)

•   Add python 3.12 support, add all-format docs tests and nightly CI runs, reorg tests file structure to match CI grouping (#232 <https://github.com/ethereum/eth-abi/issues/232>)

  eth-abi v5.0.1 (2024-03-04)

  Bugfixes

•   During decoding, verify all pointers in arrays and tuples point to a valid location in the payload (#226 <https://github.com/ethereum/eth-abi/issues/226>)

•   Fix memory leak warning in NodeVisitor and ABIRegistry (#230 <https://github.com/ethereum/eth-abi/issues/230>)

  Internal Changes - for eth-abi Contributors

•   Clear mypy misc-type errors and add top-level py.typed file back (#221 <https://github.com/ethereum/eth-abi/issues/221>)

  eth-abi v5.0.0 (2024-01-09)

  Breaking Changes

•   Drop python 3.7 support (#217 <https://github.com/ethereum/eth-abi/issues/217>)

  Internal Changes - for eth-abi Contributors

•   Merge updates from the project template, notably, use pre-commit for linting and change the name of the master branch to main (#217 <https://github.com/ethereum/eth-abi/issues/217>)

•   Corrected format of booleans in pyproject.toml and added a test for the presence of the eth_abi.__version__ attribute (#219 <https://github.com/ethereum/eth-abi/issues/219>)

  eth-abi v4.2.1 (2023-09-13)

  Internal Changes - for eth-abi contributors

•   Add build.os section to readthedocs build settings (#213 <https://github.com/ethereum/eth-abi/issues/213>)

  Miscellaneous changes

•   #214 <https://github.com/ethereum/eth-abi/issues/214>

  eth-abi v4.2.0 (2023-08-28)

  Features

•   Allow turning off abi decoder "strict mode" when calling abi.decode(). (#198 <https://github.com/ethereum/eth-abi/issues/198>)

  Bugfixes

•   Validate against zero-sized tuple types / empty Solidity structs. (#212 <https://github.com/ethereum/eth-abi/issues/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 <https://github.com/ethereum/eth-abi/issues/187>)

  Internal Changes - for eth-abi contributors

•   remove unused docs deps, bump version of remaining (#203 <https://github.com/ethereum/eth-abi/issues/203>)

•   Moved requirements-docs.txt info into .readthedocs.yml (#204 <https://github.com/ethereum/eth-abi/issues/204>)

•   pull in updates from the python project template (#205 <https://github.com/ethereum/eth-abi/issues/205>)

•   Updated CI process to handle tox issue caused by virtualenv update (#208 <https://github.com/ethereum/eth-abi/issues/208>)

  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 <https://github.com/ethereum/eth-abi/issues/201>)

  eth-abi v4.0.0-beta.2 (2022-11-21)

  Features

•   Add support for Python 3.11 (#194 <https://github.com/ethereum/eth-abi/issues/194>)

  Miscellaneous changes

•   #196 <https://github.com/ethereum/eth-abi/issues/196>

  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 <https://github.com/ethereum/eth-abi/issues/158>)

  Breaking Changes

•   Remove encode_abi_single(), encode_packed_single(), and decode_abi_single(). Rename encode_abi(), encode_abi_packed(), and decode_abi() to encode(), encode_packed(), and decode(), respectively. (#161 <https://github.com/ethereum/eth-abi/issues/161>)

  Miscellaneous changes

•   #161 <https://github.com/ethereum/eth-abi/issues/161>, #166 <https://github.com/ethereum/eth-abi/issues/166>, #167 <https://github.com/ethereum/eth-abi/issues/167>, #168 <https://github.com/ethereum/eth-abi/issues/168>, #177 <https://github.com/ethereum/eth-abi/issues/177>, #183 <https://github.com/ethereum/eth-abi/issues/183>, #186 <https://github.com/ethereum/eth-abi/issues/186>

  eth-abi v3.0.1 (2022-07-18)

  Deprecations

•   Add DeprecationWarning for encode_abi(), encode_single(), decode_abi(), and decode_single() and add temporary versions of abi.encode() and abi.decode() so users can start making these changes early. (#165 <https://github.com/ethereum/eth-abi/issues/165>)

  Miscellaneous changes

•   #165 <https://github.com/ethereum/eth-abi/issues/165>, #166 <https://github.com/ethereum/eth-abi/issues/166>, #172 <https://github.com/ethereum/eth-abi/issues/172>, #177 <https://github.com/ethereum/eth-abi/issues/177>, #178 <https://github.com/ethereum/eth-abi/issues/178>

  eth_abi 3.0.0 (2022-01-19)

  Features

•   Add support for python 3.8 (#145 <https://github.com/ethereum/eth-abi/issues/145>)

•   Add support for Python 3.8. Includes updating mypy and flake8 version requirements (#155 <https://github.com/ethereum/eth-abi/issues/155>)

•   Drop Python 3.6 support, add Python 3.9 and 3.10 support. Update any dependencies accordingly (#156 <https://github.com/ethereum/eth-abi/issues/156>)

  Bugfixes

•   Catch ABITypeError exceptions when checking has_encoder (#148 <https://github.com/ethereum/eth-abi/issues/148>)

  Improved Documentation

•   Fix broken badges in README (#144 <https://github.com/ethereum/eth-abi/issues/144>)

  Miscellaneous changes

•   #123 <https://github.com/ethereum/eth-abi/issues/123>, #154 <https://github.com/ethereum/eth-abi/issues/154>

  eth-abi v2.1.1 (2020-02-27)

  Bugfixes

•   If subclassing eth_abi.decoding.ContextFramesBytesIO.seek(), the new method was not being used by seek_in_frame(). Now it will be. (#139 <https://github.com/ethereum/eth-abi/issues/139>)

  Internal Changes - for eth_abi contributors

•   Merged in project template, for changes in release scripts, docs, release notes, etc. (#140 <https://github.com/ethereum/eth-abi/issues/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 <#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 with pip install eth-abi[tools]. See Tools <#tools>.

  v2.0.0-beta.8

•   Added has_encoder() and is_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 <#grammar>.

•   Updated string API for the ABIType. Type strings for ABIType instances are now obtained via the to_type_str() method instead of by invoking the builtin Python str function with an instance of ABIType.

  v2.0.0-beta.5

•   Added registry copying functionality to facilitate modification of the default registry. See Copying an Existing Registry <#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 <#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() and decode_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 the parse() function. This should make the parsing API more consistent with the new parsimonious parser.

  v2.0.0-alpha.1

Released July 19, 2018

•   Backwards Incompatible Changes

•   decode_single() called with ABI type 'string' will now return a python str <https://docs.python.org/3.10/library/stdtypes.html#str> instead of bytes <https://docs.python.org/3.10/library/stdtypes.html#bytes>.

•   Support for the legacy real and ureal types has been removed

•   Bugfixes

•   Simple callable encoders work again

•   Misc

•   Various documentation updates and type annotations

  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 returns False if a Decimal <https://docs.python.org/3.10/library/decimal.html#decimal.Decimal> has too many digits to be encoded in the given fixed<M>x<N> type. (It was previously raising a ValueError <https://docs.python.org/3.10/library/exceptions.html#ValueError>)

•   Raise an EncodingTypeError instead of a TypeError <https://docs.python.org/3.10/library/exceptions.html#TypeError> when trying to encode a float <https://docs.python.org/3.10/library/functions.html#float> into a fixed<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

  Contributing

Thank you for your interest in contributing! We welcome all contributions no matter their size. Please read along to learn how to get started. If you get stuck, feel free to ask for help in Ethereum Python Discord server <https://discord.gg/GHryRvPB84>.

  Setting the stage

To get started, fork the repository to your own github account, then clone it to your development machine:

    git clone git@github.com:your-github-username/eth-abi.git

Next, install the development dependencies. We recommend using a virtual environment, such as virtualenv <https://virtualenv.pypa.io/en/stable/>.

    cd eth-abi
    virtualenv -p python venv
    . venv/bin/activate
    python -m pip install -e ".[dev]"
    pre-commit install

  Running the tests

A great way to explore the code base is to run the tests.

We can run all tests with:

    pytest tests

  Code Style

We use pre-commit <https://pre-commit.com/> to enforce a consistent code style across the library. This tool runs automatically with every commit, but you can also run it manually with:

    make lint

If you need to make a commit that skips the pre-commit checks, you can do so with git commit --no-verify.

This library uses type hints, which are enforced by the mypy tool (part of the pre-commit checks). All new code is required to land with type hints, with the exception of code within the tests directory.

  Documentation

Good documentation will lead to quicker adoption and happier users. Please check out our guide on how to create documentation for the Python Ethereum ecosystem <https://github.com/ethereum/snake-charmers-tactical-manual/blob/main/documentation.md>.

  Pull Requests

It's a good idea to make pull requests early on. A pull request represents the start of a discussion, and doesn't necessarily need to be the final, finished submission.

GitHub's documentation for working on pull requests is available here <https://docs.github.com/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/about-pull-requests>.

Once you've made a pull request, take a look at the Circle CI build status in the GitHub interface and make sure all tests are passing. In general pull requests that do not pass the CI build yet won't get reviewed unless explicitly requested.

If the pull request introduces changes that should be reflected in the release notes, please add a newsfragment file as explained here <https://github.com/ethereum/eth-abi/blob/main/newsfragments/README.md>.

If possible, the change to the release notes file should be included in the commit that introduces the feature or bugfix.

  Releasing

Releases are typically done from the main branch, except when releasing a beta (in which case the beta is released from main, and the previous stable branch is released from said branch).

  Final test before each release

Before releasing a new version, build and test the package that will be released:

    git checkout main && git pull
    make package-test

This will build the package and install it in a temporary virtual environment. Follow the instructions to activate the venv and test whatever you think is important.

You can also preview the release notes:

    towncrier --draft

  Build the release notes

Before bumping the version number, build the release notes. You must include the part of the version to bump (see below), which changes how the version number will show in the release notes.

    make notes bump=$$VERSION_PART_TO_BUMP$$

If there are any errors, be sure to re-run make notes until it works.

  Push the release to github & pypi

After confirming that the release package looks okay, release a new version:

    make release bump=$$VERSION_PART_TO_BUMP$$

This command will:

•   Bump the version number as specified in .pyproject.toml and setup.py.

•   Create a git commit and tag for the new version.

•   Build the package.

•   Push the commit and tag to github.

•   Push the new package files to pypi.

  Which version part to bump

$$VERSION_PART_TO_BUMP$$ must be one of: major, minor, patch, stage, or devnum.

The version format for this repo is {major}.{minor}.{patch} for stable, and {major}.{minor}.{patch}-{stage}.{devnum} for unstable (stage can be alpha or beta).

If you are in a beta version, make release bump=stage will switch to a stable.

To issue an unstable version when the current version is stable, specify the new version explicitly, like make release bump="--new-version 4.0.0-alpha.1"

You can see what the result of bumping any particular version part would be with bump-my-version show-bump

  Code of Conduct

  Our Pledge

In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to make participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation.

  Our Standards

Examples of behavior that contributes to creating a positive environment include:

•   Using welcoming and inclusive language

•   Being respectful of differing viewpoints and experiences

•   Gracefully accepting constructive criticism

•   Focusing on what is best for the community

•   Showing empathy towards other community members

    Examples of unacceptable behavior by participants include:

•   The use of sexualized language or imagery and unwelcome sexual attention or advances

•   Trolling, insulting/derogatory comments, and personal or political attacks

•   Public or private harassment

•   Publishing others' private information, such as a physical or electronic address, without explicit permission

•   Other conduct which could reasonably be considered inappropriate in a professional setting

  Our Responsibilities

Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.

Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.

  Scope

This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.

  Enforcement

Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at <snakecharmers@ethereum.org>. All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.

Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.

  Attribution

This Code of Conduct is adapted from the Contributor Covenant <https://www.contributor-covenant.org>, version 1.4, available at <https://www.contributor-covenant.org/version/1/4/code-of-conduct.html>

Author
The Ethereum Foundation

Copyright
2016-2020, 2022-2023, The Ethereum Foundation