Solidity Encoding – Encode Arbitrary Amount of Parameters for encodeWithSignature Without Adding to Function Definition

abibytescontract-invocationencodingsolidity

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?

Best Answer

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.