0

I'm doing a test and trying to send bytes data to a contract so it can parse, store in variables, and return a value.

Solidity code

pragma solidity 0.6.12;

contract testbyte {

function sendBytes(bytes calldata _data) external pure returns (address) {
    (uint amountIn, uint amountOut, address pool) = abi.decode(_data, (uint, uint, address));

    return pool;


}

}

I was looking at this answer here but passing the bytes data was done in Javascript. Parsing solidity bytes to params

Python code

w3 = Web3(Web3.HTTPProvider(node_provider))

I collect all data, abi, etc in between.

data = { 'amountIn': 1, 'amountOut': 2, 'pool': 'pool address here' }

I usually do w3.toBytes(text='') to pass nothing on mainnet contracts.

bytes_data = w3.toBytes(text=str(data))

pool = contract.functions.sendBytes(bytes_data).call() print(pool)

I get the bytes output

b"{'amountIn': 1, 'amountOut': 2, 'pool': 'pool address here'}"

If I try to pass that the contract reverts and if I try via remix I get

transact to testbyte.sendBytes errored: Error encoding arguments: Error: invalid arrayify value (argument="value", value="{'amountIn': 1, 'amountOut': 2, 'pool': 'pool address here'}", code=INVALID_ARGUMENT, version=bytes/5.5.0)

From researching I can only guess that my byte input is incorrect, or something in my contract is wrong, but I'm unsure.

UPDATE 1:

After looking at example in comment I ended up here: web3py encode method call parameters

I tried using the second method but I get contract reverted.

test_encode = encode_abi(['uint', 'uint', 'address'], [1, 2, '0xAD5ce863aE3E4E9394Ab43d4ba0D80f419F61789'])

contract.functions.sendBytes(test_encode).call()

When I try the first method mixed with the second

contract.encodeABI(fn_name='sendBytes', args=[test_encode])

It returns

0x4c93893a0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000ad5ce863ae3e4e9394ab43d4ba0d80f419f61789

Randomly I input that into remix it return values, but all wrong values.

So I am still confused, the js portion seemed easier. Recovering data from Bytes using Assembly

UPDATE 2:

This code works when I take the output and input it into remix

test_encode = encode_abi(['uint', 'uint', 'address'], [1, 2, '0xAD5ce863aE3E4E9394Ab43d4ba0D80f419F61789'],)

contract.encodeABI(fn_name='sendBytes', args=[test_encode], data=test_encode)

output:

0x00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000ad5ce863ae3e4e9394ab43d4ba0d80f419f617890000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000ad5ce863ae3e4e9394ab43d4ba0d80f419f61789

When entered into remix it returns the right values! But now I don't know how to pass the data in a web3.py call. I keep getting execution reverted.

dekubaka
  • 51
  • 1
  • 4
  • The input data is encoded as json. Search for a json decoder in solidity. I'd recommend to change how the input data is encoded. Decoding a json string in solidity will be expensive since it is string manipulation. If possible I'd recommend to use a struct as input data that way the compiler will generate the decoder. – Ismael Feb 16 '22 at 03:48
  • @Ismael When you say change input data do you mean the data I'm sending to the function or the bytes parameter itself? I'd prefer to send input via bytes. I can send an array like [1,2,pool] but I'm not sure how I would send a struct via web3 py to the contract to decode. – dekubaka Feb 16 '22 at 14:05
  • If you want to send byte see the example in the answer https://ethereum.stackexchange.com/questions/119342/recovering-data-from-bytes-using-assembly/119519 – Ismael Feb 16 '22 at 15:19
  • @Ismael Updated post. I looked into this but it's in JavaScript. I am still having trouble passing the bytes data. Not sure where I'm going wrong. – dekubaka Feb 16 '22 at 21:47
  • @Ismael went down a rabbit hole after your link and I got it now! – dekubaka Feb 16 '22 at 23:53

1 Answers1

0

Found the answer. It seems that my contract address was wrong in my .env file (whoops). After I corrected that I used the code below.

from eth_abi import encode_abi

test_encode = encode_abi(['uint', 'uint', 'address'], [1, 2, '0xAD5ce863aE3E4E9394Ab43d4ba0D80f419F61789'],) # Basically map the types to the values

pool = contract.functions.sendBytes(test_encode).call() print(pool)

outputs

['0xAD5ce863aE3E4E9394Ab43d4ba0D80f419F61789']

If you want to use arrays you can do

test_encode = encode_abi(['uint[]', 'uint[]', 'address'], [[1,2], [3,4], '0xAD5ce863aE3E4E9394Ab43d4ba0D80f419F61789'],)

I could not use

contract.encodeABI(fn_name='sendBytes', args=[test_encode], data=test_encode)

I believe I was using it wrong, and the data it outputs is a string. When you input the value into remix it works. I can only assume remix handles things differently. Maybe someone can explain how to use this properly in the comments.

dekubaka
  • 51
  • 1
  • 4