1

I'm using Web3.py, and I'm trying to build a transaction to call a contract function where one of the arguments is of type "bytes". I want to call the function with a zero-length byte array.

However, when I call buildTransaction(), the zero-length byte array is encoded as a sequence of 128 zeros. I instead want a sequence of 64 zeros (representing the 32-byte length of the zero-length array, as per the Ethereum encoding specs).

I have tried using "0x", HexBytes('0x'), HexBytes(''), '', and b'' as the parameter, without any success.

Does anyone know how to accomplish what I need?

Edit: Here is a simple example to showcase what I mean.

test.json:

[
  {
    "constant": false,
    "inputs": [
      {
        "name": "test",
        "type": "bytes"
      }
    ],
    "name": "function",
    "outputs": [],
    "payable": true,
    "stateMutability": "payable",
    "type": "function"
  }
]

test.py:

#!/usr/bin/python3.9

import json, web3

dummy_address = '0xcbDc0bca6706da81d66e9928d3575dAd4F1a929b'

with open('/home/ubuntu/test.json', 'r') as file:
    abi = json.load(file)

w3 = web3.Web3(web3.Web3.HTTPProvider('https://mainnet.infura.io/v3/my_key'))

contract = w3.eth.contract(dummy_address, abi = abi)

params = { 'chainId': 1, 'type': 2, 'value': 0, 'from': dummy_address, 'gas': 1, 'maxFeePerGas': 1, 'maxPriorityFeePerGas': 1, 'nonce': 0 }

txn = contract.functions.function('0x').buildTransaction(params)
print(txn['data'])

output:

0x4f626cf1000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000

"4f626cf1" is the function selector, "0000000000000000000000000000000000000000000000000000000000000020" is the data location, and that is followed by 128 zeros (instead of the desired 64 zeros)

1 Answer 1

2

Well I ended up figuring this out myself I think. In case anyone else runs into this issue, here is the relevant code at site-packages/eth_abi/encoding.py:

    def encode(cls, value):
        cls.validate_value(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

        return encoded_value

That 5th line can be changed to

padded_value = b''

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.