0

Lets says we have Contract A that needs to call Contract B at a certain address which the requirements that Contract A doesn't know the exact parameter structure the function in that Contract B needs & assumes the user will provide the correct parameters.

So far I have the following.

function callExternalContractWithPacked(address _contract, address _testAddr) external {
        bytes memory arg = abi.encode(_testAddr);
        (bool success, bytes memory data) = _contract.call(
            abi.encodeWithSignature("setAddr(address)", arg)
        );
        emit Response(success, data);
}

While testing the abi.encode theory I passed a test address into the function & encoded it using abi.encode although there was a serious problem. Once the function ran & called the other contract, the address set on Contract B was completely different from the address added to the function before encoding.

How can I properly pass a argument of bytes into abi.encodeWithSignature without the data becoming corrupt & incorrect than the desired input.

During my search I came across encodeFunctionCall :: Web3js but would like the same result within solidity. What are your thoughts?

1 Answer 1

2

Your problem is that you're using abi.encode with abi.encodeWithSignature, and encoding your address twice.

abi.encode takes your address and pads it with zeros to make it 32 bytes long since EVM works with 32 bytes slots only.

abi.encodeWithSignature, on the other hand, takes the signature of the function you're calling, along with its arguments, and encodes them too.

In your code, your encoded values look like this:

0xd1d80fdf
0000000000000000000000000000000000000000000000000000000000000020
0000000000000000000000000000000000000000000000000000000000000020
0000000000000000000000005b38da6a701c568545dcfcb03fcb875f56beddc4

The first line 0xd1d80fdf is the function signature. The next three lines represent your double encoded values.

What you want is just:

0xd1d80fdf
0000000000000000000000005b38da6a701c568545dcfcb03fcb875f56beddc4

Your address follows the function signature, padded with zeros to fit 32 bytes.

To fix the issue, use only abi.encodeWithSignature to encode the function signature and address, don't use abi.encode before that.

7
  • Ahh that makes sense with the dual encoding bug, but how do I use abi.encodeWithSignature with an unknown amount of arguments so that it can work with a wide rang of different function calls?
    – CoderMan
    Commented May 24, 2023 at 19:09
  • Since abi.encodeWithSignature return bytes memory couldn't I just skip the encoding within the contract & encode outside the contract with the proper function definition & args & pass them directly into the _contract.call() statement?
    – CoderMan
    Commented May 24, 2023 at 19:21
  • 1
    You can encode the data to send without using abi.encodeWithSignature. All you have to do is first encode your values, but don't forget to put the signature of the function you're calling first d1d80fdf Then _contract.call(encodedValue).
    – Adam Boudj
    Commented May 25, 2023 at 5:35
  • Thank you for the response, how should I encode the values? How do I add the function hash to the encodedValues?
    – CoderMan
    Commented May 25, 2023 at 6:03
  • 1
    You can do something like: bytes memory arg = abi.encode(_testAddr); return bytes.concat(bytes4(0xd1d80fdf), arg);
    – Adam Boudj
    Commented May 25, 2023 at 8:58

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.