Repository: ethereum/eth-abi

Issue:
Padding of empty `bytes`
What is the reason for padding with 32 zeros here for an empty bytes array?
https://github.com/ethereum/eth-abi/blob/decaadcdf7a33020846aafd9ba36859c1ab82909/eth_abi/encoding.py#L535

I think it's different from what happens here, namely no padding
https://github.com/ethereumjs/ethereumjs-abi/blob/ee3994657fa7a427238e6ba92a84d0b529bbcde0/lib/index.js#L146

I know the latter is deprecated but it seems this is how it is done by the opensea.io frontend. 

Are both correct but according to different standards/versions?

Thank you.

Discussion:

Patch:
diff --git a/eth_abi/encoding.py b/eth_abi/encoding.py
index c6b8e3f..487b621 100644
--- a/eth_abi/encoding.py
+++ b/eth_abi/encoding.py
@@ -159,7 +159,6 @@ class TupleEncoder(BaseEncoder):
         )
 
         encoded_value = b''.join(head_chunks + tuple(tail_chunks))
-
         return encoded_value
 
     @parse_tuple_type_str
@@ -530,16 +529,12 @@ class ByteStringEncoder(BaseEncoder):
     @classmethod
     def encode(cls, value):
         cls.validate_value(value)
+        value_length = len(value)
 
-        if not value:
-            padded_value = b'\x00' * 32
-        else:
-            padded_value = zpad_right(value, ceil32(len(value)))
-
-        encoded_size = encode_uint_256(len(value))
-        encoded_value = encoded_size + padded_value
+        encoded_size = encode_uint_256(value_length)
+        padded_value = zpad_right(value, ceil32(value_length))
 
-        return encoded_value
+        return encoded_size + padded_value
 
     @parse_type_str('bytes')
     def from_type_str(cls, abi_type, registry):
@@ -568,16 +563,12 @@ class TextStringEncoder(BaseEncoder):
         cls.validate_value(value)
 
         value_as_bytes = codecs.encode(value, 'utf8')
+        value_length = len(value_as_bytes)
 
-        if not value_as_bytes:
-            padded_value = b'\x00' * 32
-        else:
-            padded_value = zpad_right(value_as_bytes, ceil32(len(value_as_bytes)))
-
-        encoded_size = encode_uint_256(len(value_as_bytes))
-        encoded_value = encoded_size + padded_value
+        encoded_size = encode_uint_256(value_length)
+        padded_value = zpad_right(value_as_bytes, ceil32(value_length))
 
-        return encoded_value
+        return encoded_size + padded_value
 
     @parse_type_str('string')
     def from_type_str(cls, abi_type, registry):
@@ -619,7 +610,7 @@ class BaseArrayEncoder(BaseEncoder):
         tail_chunks = tuple(item_encoder(i) for i in value)
 
         items_are_dynamic = getattr(item_encoder, 'is_dynamic', False)
-        if not items_are_dynamic:
+        if not items_are_dynamic or len(value) == 0:
             return b''.join(tail_chunks)
 
         head_length = 32 * len(value)
diff --git a/newsfragments/158.bugfix.rst b/newsfragments/158.bugfix.rst
new file mode 100644
index 0000000..7e1f528
--- /dev/null
+++ b/newsfragments/158.bugfix.rst
@@ -0,0 +1,1 @@
+Reconcile differences in 32-byte padding between eth-abi encoders for dynamic types and Solidity's abi.encode() for 0 or empty values
\ No newline at end of file


Test patch:
diff --git a/tests/abi/test_decode_abi.py b/tests/abi/test_decode_abi.py
index 9800c93..a08d5af 100644
--- a/tests/abi/test_decode_abi.py
+++ b/tests/abi/test_decode_abi.py
@@ -16,10 +16,10 @@ from ..common.unit import (
 
 
 @pytest.mark.parametrize(
-    'type_str,expected,abi_encoding,_',
+    'type_str,expected,_1,abi_encoding,_2',
     CORRECT_TUPLE_ENCODINGS,
 )
-def test_decode_abi(type_str, expected, abi_encoding, _):
+def test_decode_abi(type_str, expected, _1, abi_encoding, _2):
     abi_type = parse(type_str)
     if abi_type.arrlist is not None:
         pytest.skip('ABI coding functions do not support array types')
diff --git a/tests/abi/test_decode_single.py b/tests/abi/test_decode_single.py
index e11d972..6bb1be9 100644
--- a/tests/abi/test_decode_single.py
+++ b/tests/abi/test_decode_single.py
@@ -10,10 +10,10 @@ from ..common.unit import (
 
 
 @pytest.mark.parametrize(
-    'typ,expected,abi_encoding,_',
+    'typ,expected,_1,abi_encoding,_2',
     CORRECT_SINGLE_ENCODINGS,
 )
-def test_decode_single(typ, expected, abi_encoding, _):
+def test_decode_single(typ, expected, _1, abi_encoding, _2):
     actual = decode_single(typ, abi_encoding)
     assert actual == expected
 
diff --git a/tests/abi/test_encode_abi.py b/tests/abi/test_encode_abi.py
index e52d67d..761e2b0 100644
--- a/tests/abi/test_encode_abi.py
+++ b/tests/abi/test_encode_abi.py
@@ -9,19 +9,62 @@ from eth_abi.grammar import (
 
 from ..common.unit import (
     CORRECT_TUPLE_ENCODINGS,
+    words,
 )
 
 
 @pytest.mark.parametrize(
-    'type_str,python_value,abi_encoding,_',
+    'tuple_type,python_value,_1,encoded_list_of_types,_2',
     CORRECT_TUPLE_ENCODINGS,
 )
-def test_encode_abi(type_str, python_value, abi_encoding, _):
-    abi_type = parse(type_str)
+def test_encode_abi_as_list_of_types(tuple_type, python_value, _1, encoded_list_of_types, _2):
+    abi_type = parse(tuple_type)
     if abi_type.arrlist is not None:
         pytest.skip('ABI coding functions do not support array types')
 
-    types = [t.to_type_str() for t in abi_type.components]
+    # assert different types encoded correctly as a list
+    # e.g. encode_abi(['bytes32[]', 'uint256'], ([b'a', b'b'], 22))
+    #
+    # compare to solidity:
+    #   bytes32 a = 0x6100000000000000000000000000000000000000000000000000000000000000;
+    #   bytes32 b = 0x6200000000000000000000000000000000000000000000000000000000000000;
+    #   bytes32[] arr = [a,b];
+    #   uint256 num = 22;
+    #
+    #   abi.encode(arr,num);
+    separated_list_of_types = [t.to_type_str() for t in abi_type.components]
+    eth_abi_encoded = encode_abi(separated_list_of_types, python_value)
+    assert eth_abi_encoded == encoded_list_of_types
 
-    actual = encode_abi(types, python_value)
-    assert actual == abi_encoding
+
+@pytest.mark.parametrize(
+    'tuple_type,python_value,is_dynamic,encoded_list_of_types,_2',
+    CORRECT_TUPLE_ENCODINGS,
+)
+def test_encode_abi_as_single_tuple_type(
+    tuple_type, python_value, is_dynamic, encoded_list_of_types, _2
+):
+    # assert the tuple type is encoded correctly
+    # e.g. encode_abi(['(bytes32[],uint256)'], [([b'a', b'b'], 22)])
+    #
+    # compare to solidity:
+    #   struct TupleExample {
+    #     bytes32[] arg1;
+    #     uint256 arg2;
+    #   }
+    #   bytes32 a = 0x6100000000000000000000000000000000000000000000000000000000000000;
+    #   bytes32 b = 0x6200000000000000000000000000000000000000000000000000000000000000;
+    #   bytes32[] arr = [a,b];
+    #   uint256 num = 22;
+    #
+    #   abi.encode(TupleExample(arr,num));
+    eth_abi_encoded = encode_abi([tuple_type], [python_value])
+
+    encoded_tuple_type = (
+        # 32 bytes offset for dynamic tuple types
+        b''.join([words('20'), encoded_list_of_types]) if is_dynamic
+
+        # no offset for static tuples so same encoding as if encoding a list of the types
+        else encoded_list_of_types
+    )
+    assert eth_abi_encoded == encoded_tuple_type
diff --git a/tests/abi/test_encode_single.py b/tests/abi/test_encode_single.py
index bfdf50c..b8cfb93 100644
--- a/tests/abi/test_encode_single.py
+++ b/tests/abi/test_encode_single.py
@@ -10,9 +10,9 @@ from ..common.unit import (
 
 
 @pytest.mark.parametrize(
-    'typ,python_value,abi_encoding,_',
+    'typ,python_value,_1,single_type_encoding,_2',
     CORRECT_SINGLE_ENCODINGS,
 )
-def test_encode_single(typ, python_value, abi_encoding, _):
+def test_encode_single(typ, python_value, _1, single_type_encoding, _2):
     actual = encode_single(typ, python_value)
-    assert actual == abi_encoding
+    assert actual == single_type_encoding
diff --git a/tests/abi/test_is_encodable.py b/tests/abi/test_is_encodable.py
index 797015f..d10295a 100644
--- a/tests/abi/test_is_encodable.py
+++ b/tests/abi/test_is_encodable.py
@@ -18,10 +18,10 @@ from tests.common.unit import (
 
 
 @pytest.mark.parametrize(
-    'type_str,python_value,_1,_2',
+    'type_str,python_value,_1,_2,_3',
     CORRECT_SINGLE_ENCODINGS,
 )
-def test_is_encodable_returns_true(type_str, python_value, _1, _2):
+def test_is_encodable_returns_true(type_str, python_value, _1, _2, _3):
     assert is_encodable(type_str, python_value)
 
 
diff --git a/tests/abi/test_is_encodable_type.py b/tests/abi/test_is_encodable_type.py
index da6aaa2..dffc719 100644
--- a/tests/abi/test_is_encodable_type.py
+++ b/tests/abi/test_is_encodable_type.py
@@ -9,10 +9,10 @@ from tests.common.unit import (
 
 
 @pytest.mark.parametrize(
-    'type_str,_python_value,_1,_2',
+    'type_str,_python_value,_1,_2,_3',
     CORRECT_SINGLE_ENCODINGS,
 )
-def test_is_encodable_type_returns_true(type_str, _python_value, _1, _2):
+def test_is_encodable_type_returns_true(type_str, _python_value, _1, _2, _3):
     assert is_encodable_type(type_str)
 
 
diff --git a/tests/common/unit.py b/tests/common/unit.py
index afe7fa8..ae7f2c3 100644
--- a/tests/common/unit.py
+++ b/tests/common/unit.py
@@ -69,15 +69,16 @@ def words(*descriptions: str) -> bytes:
 
 
 CORRECT_TUPLE_ENCODINGS = [
-    # (type string, python value, abi encoding, packed encoding)
+    # (type string, python value, is_dynamic, abi encoding, packed encoding)
 
     # Empty tuples
     (
-        '()', (), b'', b'',
+        '()', (), False, b'', b'',
     ),
     (
         '((),((),((),())))',
         ((), ((), ((), ()))),
+        False,
         b'',
         b'',
     ),
@@ -86,18 +87,21 @@ CORRECT_TUPLE_ENCODINGS = [
     (
         '(uint32)',
         (6,),
+        False,
         words('6'),
         words('6 (4 wide)'),
     ),
     (
         '(uint32,uint32)',
         (2 ** 32 - 1, 2 ** 32 - 1),
+        False,
         words('ffffffff', 'ffffffff'),
         words('f<f (4 wide)', 'f<f (4 wide)')
     ),
     (
         '(bytes32,bytes32)',
         (zpad32_right(b'a'), zpad32_right(b'b')),
+        False,
         words('61>0', '62>0'),
         words('61>0', '62>0'),
     ),
@@ -109,6 +113,7 @@ CORRECT_TUPLE_ENCODINGS = [
             zpad32_right(b'stupid pink animal'),
             0,
         ),
+        False,
         words(
             '82a978b3f5962a5b0957d9ee9eef472ee55b42f1',
             '1',
@@ -124,33 +129,111 @@ CORRECT_TUPLE_ENCODINGS = [
     ),
 
     # Dynamic tuples
+
+    # tuple w/ empty dynamic array
+    (
+        '(string[])', ((),), True, words('20', '0'), b'',
+    ),
+    (
+        '(bytes[])', ((),), True, words('20', '0'), b'',
+    ),
+    (
+        '(bytes32[])', ((),), True, words('20', '0'), b'',
+    ),
+    (
+        '(address[])', ((),), True, words('20', '0'), b'',
+    ),
+    (
+        '(int[])', ((),), True, words('20', '0'), b'',
+    ),
+    (
+        '(uint[])', ((),), True, words('20', '0'), b'',
+    ),
+    (
+        '(uint8[])', ((),), True, words('20', '0'), b'',
+    ),
+    (
+        '(uint256[])', ((),), True, words('20', '0'), b'',
+    ),
+
+    # nested tuple w/ empty dynamic arrays
+    (
+        '((string[]))', (((),),), True, words('20', '20', '0'), b'',
+    ),
+    (
+        '((bytes[]))', (((),),), True, words('20', '20', '0'), b'',
+    ),
+    (
+        '((bytes32[]))', (((),),), True, words('20', '20', '0'), b'',
+    ),
+    (
+        '((address[]))', (((),),), True, words('20', '20', '0'), b'',
+    ),
+    (
+        '((int[]))', (((),),), True, words('20', '20', '0'), b'',
+    ),
+    (
+        '((uint8[]))', (((),),), True, words('20', '20', '0'), b'',
+    ),
+    (
+        '((uint256[]))', (((),),), True, words('20', '20', '0'), b'',
+    ),
+
+    # sanity check / consistency - doubly nested tuples with empty dynamic array
+    (
+        '(((string[])))', ((((),),),), True, words('20', '20', '20', '0'), b'',
+    ),
+    (
+        '(((bytes[])))', ((((),),),), True, words('20', '20', '20', '0'), b'',
+    ),
+    (
+        '(((bytes32[])))', ((((),),),), True, words('20', '20', '20', '0'), b'',
+    ),
+    (
+        '(((address[])))', ((((),),),), True, words('20', '20', '20', '0'), b'',
+    ),
+    (
+        '(((int[])))', ((((),),),), True, words('20', '20', '20', '0'), b'',
+    ),
+    (
+        '(((uint8[])))', ((((),),),), True, words('20', '20', '20', '0'), b'',
+    ),
+    (
+        '(((uint256[])))', ((((),),),), True, words('20', '20', '20', '0'), b'',
+    ),
+
     (
         '(bytes32[])',
         ((zpad32_right(b'a'), zpad32_right(b'b')),),
+        True,
         words('20', '2', '61>0', '62>0'),
         words('61>0', '62>0'),
     ),
     (
         '(uint256,bytes)',
         (0, b''),
-        words('0', '40', '0', '0'),
+        True,
+        words('0', '40', '0'),
         words('0'),
     ),
     (
         '(int,(int,int[]))',
         (1, (2, (3, 3))),
+        True,
         words('1', '40', '2', '40', '2', '3', '3'),
         words('1', '2', '3', '3'),
     ),
     (
         '((int[],int),int)',
         (((1, 1), 2), 3),
+        True,
         words('40', '3', '40', '2', '2', '1', '1'),
         words('1', '1', '2', '3'),
     ),
     (
         '((bytes,bool),(bytes,bool))',
         ((b'david attenborough', False), (b'boaty mcboatface', True)),
+        True,
         words(
             '40',  # offset of first (bytes,bool)
             'c0',  # offset of second (bytes,bool)
@@ -175,6 +258,7 @@ CORRECT_TUPLE_ENCODINGS = [
     (
         '((int,int)[])',
         (((1, 2), (3, 4)),),
+        True,
         words('20', '2', '1', '2', '3', '4'),
         words('1', '2', '3', '4'),
     ),
@@ -189,6 +273,7 @@ CORRECT_TUPLE_ENCODINGS = [
             (8, 9),
             10,
         ),
+        True,
         words(
             '80',  # offset of dynamic tuple
             '8',  # outer tuple static tuple
@@ -215,6 +300,7 @@ CORRECT_TUPLE_ENCODINGS = [
             ((3, 4), (5, 6)),
             ((7, 8), (9, 10), (11, 12)),
         ),
+        True,
         words(
             '3',  # size of outer dynamic list
             '60',  # offset of first dynamic list
@@ -246,6 +332,7 @@ CORRECT_TUPLE_ENCODINGS = [
                 ((5, 6), (7, 8), (9, 10)),
             ),
         ),
+        True,
         words(
             '20',  # offset of constant size array
             '40',  # offset of first dynamic list of tuples
@@ -269,139 +356,146 @@ CORRECT_TUPLE_ENCODINGS = [
 
 CORRECT_SINGLE_ENCODINGS = CORRECT_TUPLE_ENCODINGS + [
     #####
-    # (type string, python value, abi encoding, packed encoding)
+    # (type string, python value, _, abi encoding, packed encoding)
     #####
 
     # uint<M>
-    ('uint8', 255, words('ff'), b'\xff'),
-    ('uint8', 21, words('15'), b'\x15'),
-    ('uint8', 1, words('1'), b'\x01'),
-    ('uint256', 2 ** 256 - 1, words('f<f'), words('f<f')),
-    ('uint256', 2 ** 256 - 100, words('f<9c'), words('f<9c')),
-    ('uint256', 21, words('15'), words('15')),
-    ('uint256', 1, words('1'), words('1')),
+    ('uint8', 255, None, words('ff'), b'\xff'),
+    ('uint8', 21, None, words('15'), b'\x15'),
+    ('uint8', 1, None, words('1'), b'\x01'),
+    ('uint256', 2 ** 256 - 1, None, words('f<f'), words('f<f')),
+    ('uint256', 2 ** 256 - 100, None, words('f<9c'), words('f<9c')),
+    ('uint256', 21, None, words('15'), words('15')),
+    ('uint256', 1, None, words('1'), words('1')),
 
     # int<M>
-    ('int8', 21, words('15'), b'\x15'),
-    ('int8', 1, words('1'), b'\x01'),
-    ('int8', -1, words('f<f'), b'\xff'),
-    ('int8', -100, words('f<9c'), b'\x9c'),
-    ('int256', 21, words('15'), words('15')),
-    ('int256', 1, words('1'), words('1')),
-    ('int256', -1, words('f<f'), words('f<f')),
-    ('int256', -100, words('f<9c'), words('f<9c')),
+    ('int8', 21, None, words('15'), b'\x15'),
+    ('int8', 1, None, words('1'), b'\x01'),
+    ('int8', -1, None, words('f<f'), b'\xff'),
+    ('int8', -100, None, words('f<9c'), b'\x9c'),
+    ('int256', 21, None, words('15'), words('15')),
+    ('int256', 1, None, words('1'), words('1')),
+    ('int256', -1, None, words('f<f'), words('f<f')),
+    ('int256', -100, None, words('f<9c'), words('f<9c')),
 
     # address
     (
         'address',
         '0x0000000000000000000000000000000000000000',
+        None,
         words('0'),
         words('0 (20 wide)'),
     ),
     (
         'address',
         '0xd3cda913deb6f67967b99d67acdfa1712c293601',
+        None,
         words('d3cda913deb6f67967b99d67acdfa1712c293601'),
         decode_hex('d3cda913deb6f67967b99d67acdfa1712c293601'),
     ),
     (
         'address',
         '0x0005c901078781c232a2a521c2af7980f8385ee9',
+        None,
         words('0005c901078781c232a2a521c2af7980f8385ee9'),
         decode_hex('0005c901078781c232a2a521c2af7980f8385ee9'),
     ),
     (
         'address',
         '0x5c901078781c232a2a521c2af7980f8385ee9000',
+        None,
         words('5c901078781c232a2a521c2af7980f8385ee9000'),
         decode_hex('5c901078781c232a2a521c2af7980f8385ee9000'),
     ),
 
     # uint, int
-    ('uint', 2 ** 256 - 1, words('f<f'), words('f<f')),
-    ('uint', 2 ** 256 - 100, words('f<9c'), words('f<9c')),
-    ('uint', 21, words('15'), words('15')),
-    ('uint', 1, words('1'), words('1')),
-    ('int', 21, words('15'), words('15')),
-    ('int', 1, words('1'), words('1')),
-    ('int', -1, words('f<f'), words('f<f')),
-    ('int', -100, words('f<9c'), words('f<9c')),
+    ('uint', 2 ** 256 - 1, None, words('f<f'), words('f<f')),
+    ('uint', 2 ** 256 - 100, None, words('f<9c'), words('f<9c')),
+    ('uint', 21, None, words('15'), words('15')),
+    ('uint', 1, None, words('1'), words('1')),
+    ('int', 21, None, words('15'), words('15')),
+    ('int', 1, None, words('1'), words('1')),
+    ('int', -1, None, words('f<f'), words('f<f')),
+    ('int', -100, None, words('f<9c'), words('f<9c')),
 
     # bool
-    ('bool', True, words('1'), b'\x01'),
-    ('bool', False, words('0'), b'\x00'),
+    ('bool', True, None, words('1'), b'\x01'),
+    ('bool', False, None, words('0'), b'\x00'),
 
     # fixed<M>x<N>
-    ('fixed8x1', Decimal('127e-1'), words('7f'), b'\x7f'),
-    ('fixed8x1', Decimal('1e-1'), words('1'), b'\x01'),
-    ('fixed8x1', Decimal('0'), words('0'), b'\x00'),
-    ('fixed8x1', Decimal('-1e-1'), words('f<f'), b'\xff'),
-    ('fixed8x1', Decimal('-128e-1'), words('f<80'), b'\x80'),
-
-    ('fixed128x18', Decimal('127e-18'), words('7f'), words('7f (16 wide)')),
-    ('fixed128x18', Decimal('1e-18'), words('1'), words('1 (16 wide)')),
-    ('fixed128x18', Decimal('0'), words('0'), words('0 (16 wide)')),
-    ('fixed128x18', Decimal('-1e-18'), words('f<f'), words('f<f (16 wide)')),
-    ('fixed128x18', Decimal('-128e-18'), words('f<80'), words('f<80 (16 wide)')),
-
-    ('fixed256x80', Decimal('127e-80'), words('7f'), words('7f')),
-    ('fixed256x80', Decimal('1e-80'), words('1'), words('1')),
-    ('fixed256x80', Decimal('0'), words('0'), words('0')),
-    ('fixed256x80', Decimal('-1e-80'), words('f<f'), words('f<f')),
-    ('fixed256x80', Decimal('-128e-80'), words('f<80'), words('f<80')),
+    ('fixed8x1', Decimal('127e-1'), None, words('7f'), b'\x7f'),
+    ('fixed8x1', Decimal('1e-1'), None, words('1'), b'\x01'),
+    ('fixed8x1', Decimal('0'), None, words('0'), b'\x00'),
+    ('fixed8x1', Decimal('-1e-1'), None, words('f<f'), b'\xff'),
+    ('fixed8x1', Decimal('-128e-1'), None, words('f<80'), b'\x80'),
+
+    ('fixed128x18', Decimal('127e-18'), None, words('7f'), words('7f (16 wide)')),
+    ('fixed128x18', Decimal('1e-18'), None, words('1'), words('1 (16 wide)')),
+    ('fixed128x18', Decimal('0'), None, words('0'), words('0 (16 wide)')),
+    ('fixed128x18', Decimal('-1e-18'), None, words('f<f'), words('f<f (16 wide)')),
+    ('fixed128x18', Decimal('-128e-18'), None, words('f<80'), words('f<80 (16 wide)')),
+
+    ('fixed256x80', Decimal('127e-80'), None, words('7f'), words('7f')),
+    ('fixed256x80', Decimal('1e-80'), None, words('1'), words('1')),
+    ('fixed256x80', Decimal('0'), None, words('0'), words('0')),
+    ('fixed256x80', Decimal('-1e-80'), None, words('f<f'), words('f<f')),
+    ('fixed256x80', Decimal('-128e-80'), None, words('f<80'), words('f<80')),
 
     # ufixed<M>x<N>
-    ('ufixed8x1', Decimal('255e-1'), words('ff'), b'\xff'),
-    ('ufixed8x1', Decimal('254e-1'), words('fe'), b'\xfe'),
-    ('ufixed8x1', Decimal('1e-1'), words('1'), b'\x01'),
-    ('ufixed8x1', Decimal('0'), words('0'), b'\x00'),
+    ('ufixed8x1', Decimal('255e-1'), None, words('ff'), b'\xff'),
+    ('ufixed8x1', Decimal('254e-1'), None, words('fe'), b'\xfe'),
+    ('ufixed8x1', Decimal('1e-1'), None, words('1'), b'\x01'),
+    ('ufixed8x1', Decimal('0'), None, words('0'), b'\x00'),
 
-    ('ufixed128x18', Decimal('255e-18'), words('ff'), words('ff (16 wide)')),
-    ('ufixed128x18', Decimal('254e-18'), words('fe'), words('fe (16 wide)')),
-    ('ufixed128x18', Decimal('1e-18'), words('1'), words('1 (16 wide)')),
-    ('ufixed128x18', Decimal('0'), words('0'), words('0 (16 wide)')),
+    ('ufixed128x18', Decimal('255e-18'), None, words('ff'), words('ff (16 wide)')),
+    ('ufixed128x18', Decimal('254e-18'), None, words('fe'), words('fe (16 wide)')),
+    ('ufixed128x18', Decimal('1e-18'), None, words('1'), words('1 (16 wide)')),
+    ('ufixed128x18', Decimal('0'), None, words('0'), words('0 (16 wide)')),
 
-    ('ufixed256x80', Decimal('255e-80'), words('ff'), words('ff')),
-    ('ufixed256x80', Decimal('254e-80'), words('fe'), words('fe')),
-    ('ufixed256x80', Decimal('1e-80'), words('1'), words('1')),
-    ('ufixed256x80', Decimal('0'), words('0'), words('0')),
+    ('ufixed256x80', Decimal('255e-80'), None, words('ff'), words('ff')),
+    ('ufixed256x80', Decimal('254e-80'), None, words('fe'), words('fe')),
+    ('ufixed256x80', Decimal('1e-80'), None, words('1'), words('1')),
+    ('ufixed256x80', Decimal('0'), None, words('0'), words('0')),
 
     # fixed, ufixed
-    ('fixed', Decimal('127e-18'), words('7f'), words('7f (16 wide)')),
-    ('fixed', Decimal('1e-18'), words('1'), words('1 (16 wide)')),
-    ('fixed', Decimal('0'), words('0'), words('0 (16 wide)')),
-    ('fixed', Decimal('-1e-18'), words('f<f'), words('f<f (16 wide)')),
-    ('fixed', Decimal('-128e-18'), words('f<80'), words('f<80 (16 wide)')),
-    ('ufixed', Decimal('255e-18'), words('ff'), words('ff (16 wide)')),
-    ('ufixed', Decimal('254e-18'), words('fe'), words('fe (16 wide)')),
-    ('ufixed', Decimal('1e-18'), words('1'), words('1 (16 wide)')),
-    ('ufixed', Decimal('0'), words('0'), words('0 (16 wide)')),
+    ('fixed', Decimal('127e-18'), None, words('7f'), words('7f (16 wide)')),
+    ('fixed', Decimal('1e-18'), None, words('1'), words('1 (16 wide)')),
+    ('fixed', Decimal('0'), None, words('0'), words('0 (16 wide)')),
+    ('fixed', Decimal('-1e-18'), None, words('f<f'), words('f<f (16 wide)')),
+    ('fixed', Decimal('-128e-18'), None, words('f<80'), words('f<80 (16 wide)')),
+    ('ufixed', Decimal('255e-18'), None, words('ff'), words('ff (16 wide)')),
+    ('ufixed', Decimal('254e-18'), None, words('fe'), words('fe (16 wide)')),
+    ('ufixed', Decimal('1e-18'), None, words('1'), words('1 (16 wide)')),
+    ('ufixed', Decimal('0'), None, words('0'), words('0 (16 wide)')),
 
     # bytes<M>
-    ('bytes32', zpad32_right(b'test'), words('74657374>0'), zpad32_right(b'test')),
+    ('bytes32', zpad32_right(b'test'), None, words('74657374>0'), zpad32_right(b'test')),
     (
         'bytes32',
         zpad32_right(b'abcdefghijklmnopqrstuvwxyz'),
+        None,
         words('6162636465666768696a6b6c6d6e6f707172737475767778797a>0'),
         words('6162636465666768696a6b6c6d6e6f707172737475767778797a>0'),
     ),
     (
         'bytes32',
         zpad32_right(b'0123456789!@#$%^&*()'),
+        None,
         words('3031323334353637383921402324255e262a2829>0'),
         words('3031323334353637383921402324255e262a2829>0'),
     ),
     (
         'bytes32',
         zpad32_right(b'abc' + 5 * b'\x00' + b'abc'),
+        None,
         words('6162630000000000616263>0'),
         words('6162630000000000616263>0'),
     ),
-    ('bytes1', b'a', words('61>0'), b'a'),
+    ('bytes1', b'a', None, words('61>0'), b'a'),
 
     # bytes
-    ('bytes', b'', words('0', '0'), b''),
-    ('bytes', b'\xde', words('1', 'de>0'), b'\xde'),
+    ('bytes', b'', None, words('0'), b''),
+    ('bytes', b'\xde', None, words('1', 'de>0'), b'\xde'),
 ]
 
 NOT_ENCODABLE = [
diff --git a/tests/encoding/test_encoder_properties.py b/tests/encoding/test_encoder_properties.py
index 55cb1e4..0fd4d7c 100644
--- a/tests/encoding/test_encoder_properties.py
+++ b/tests/encoding/test_encoder_properties.py
@@ -272,7 +272,7 @@ def test_encode_byte_string(string_value):
         (
             zpad_right(string_value, ceil32(len(string_value)))
             if string_value
-            else b'\x00' * 32
+            else b''
         )
     )
     encoded_value = encoder(string_value)
@@ -307,7 +307,7 @@ def test_encode_text_string(string_value):
                 ceil32(len(string_value_as_bytes)),
             )
             if string_value
-            else b'\x00' * 32
+            else b''
         )
     )
     encoded_value = encoder(string_value)
@@ -441,6 +441,10 @@ def test_tuple_encoder():
         UnsignedIntegerEncoder(value_bit_size=256),
         ByteStringEncoder(),
     ))
-    expected = decode_hex('0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000')  # noqa: E501
+    expected = decode_hex(
+        '0000000000000000000000000000000000000000000000000000000000000000'
+        '0000000000000000000000000000000000000000000000000000000000000040'
+        '0000000000000000000000000000000000000000000000000000000000000000'
+    )
     actual = encoder((0, b''))
     assert actual == expected
diff --git a/tests/packed/test_encode_abi_packed.py b/tests/packed/test_encode_abi_packed.py
index 437b80b..bc7c8e8 100644
--- a/tests/packed/test_encode_abi_packed.py
+++ b/tests/packed/test_encode_abi_packed.py
@@ -12,10 +12,10 @@ from tests.common.unit import (
 
 
 @pytest.mark.parametrize(
-    'type_str,python_value,_,packed_encoding',
+    'type_str,python_value,_1,_2,packed_encoding',
     CORRECT_TUPLE_ENCODINGS,
 )
-def test_encode_abi(type_str, python_value, _, packed_encoding):
+def test_encode_abi(type_str, python_value, _1, _2, packed_encoding):
     abi_type = parse(type_str)
     if abi_type.arrlist is not None:
         pytest.skip('ABI coding functions do not support array types')
diff --git a/tests/packed/test_encode_single_packed.py b/tests/packed/test_encode_single_packed.py
index a11f10a..eb2aaeb 100644
--- a/tests/packed/test_encode_single_packed.py
+++ b/tests/packed/test_encode_single_packed.py
@@ -9,9 +9,9 @@ from tests.common.unit import (
 
 
 @pytest.mark.parametrize(
-    'typ,python_value,_,packed_encoding',
+    'typ,python_value,_1,_2,packed_encoding',
     CORRECT_SINGLE_ENCODINGS,
 )
-def test_encode_single(typ, python_value, _, packed_encoding):
+def test_encode_single(typ, python_value, _1, _2, packed_encoding):
     actual = encode_single_packed(typ, python_value)
     assert actual == packed_encoding
diff --git a/tests/packed/test_is_encodable_packed.py b/tests/packed/test_is_encodable_packed.py
index 4d22c41..ddc5c6f 100644
--- a/tests/packed/test_is_encodable_packed.py
+++ b/tests/packed/test_is_encodable_packed.py
@@ -18,10 +18,10 @@ from tests.common.unit import (
 
 
 @pytest.mark.parametrize(
-    'type_str,python_value,_1,_2',
+    'type_str,python_value,_1,_2,_3',
     CORRECT_SINGLE_ENCODINGS,
 )
-def test_is_encodable_returns_true(type_str, python_value, _1, _2):
+def test_is_encodable_returns_true(type_str, python_value, _1, _2, _3):
     assert is_encodable_packed(type_str, python_value)