Stefdelec
Stefdelec

Reputation: 2811

How to pass list of addresses or another function as bytes in Solidity?

In Uniswap smart contract there's this method:

function uniswapV2Call(address sender, uint amount0, uint amount1, bytes calldata data)

I'd like to call it an pass addresses as data or another function.

function uniswapV2Call('0x233', 1, 0, ['0x33','0x44'])

or

function uniswapV2Call('0x233', 1, 0, anotherfunction)

Is there anyway to do it?

Upvotes: 2

Views: 2548

Answers (1)

Petr Hejda
Petr Hejda

Reputation: 43521

On-chain call from another contract

You can use abi.encode and abi.encodePacked to encode the data to bytes.

pragma solidity ^0.8.0;

contract MyContract {
    function uniswapV2Call(address sender, uint amount0, uint amount1, bytes calldata data) external {
    }
    
    function passArray() external {
        bytes memory data = abi.encode([
            address(0x33),
            address(0x44)
        ]);
        this.uniswapV2Call(address(0x233), 1, 0, data);
    }
    
    function passFuncionCall() external {
        bytes memory data = abi.encodePacked(
            bytes4(keccak256('anotherfunction(uint256,address)')), // function signature
            abi.encode(
                123, // `anotherfunction()` arguments
                address(0x45)
            )
        );
        this.uniswapV2Call(address(0x233), 1, 0, data);
    }
}

abi.encode leaves the leading/trailing (depending on the datatype) zeros of each argument, while abi.encodePacked trims them.

So the combination of encodePacked and encode in the passFunctionCall example returns

0x
58de5f3c // function signature
000000000000000000000000000000000000000000000000000000000000007b // 1st argument
0000000000000000000000000000000000000000000000000000000000000045 // 2nd argument

If you only encoded all params, it would leave the trailing zeros for the bytes4 type, which you don't want in this case, because the recipient would read (hex)"64 zeros" instead of (hex)"7b" as the first argument, the other argument would be incorrect as well, and the (hex)"45" would be ignored.

0x
58de5f3c00000000000000000000000000000000000000000000000000000000
000000000000000000000000000000000000000000000000000000000000007b
0000000000000000000000000000000000000000000000000000000000000045

// same data, just differently formatted for readability

0x
58de5f3c // function signature
0000000000000000000000000000000000000000000000000000000000000000 // 1st argument
0000000000000000000000000000000000000000000000000000007b00000000 // 2nd argument
00000000000000000000000000000000000000000000000000000045 // ignored

Off-chain generate the bytes argument.

You can use web3.eth.abi.encodeParameter() for example to encode the address array

const data = web3.eth.abi.encodeParameter('address[2]', ['0x0000000000000000000000000000000000000033','0x0000000000000000000000000000000000000044']);

returns

0x
0000000000000000000000000000000000000000000000000000000000000033
0000000000000000000000000000000000000000000000000000000000000044

Or you can use the encodeFunctionCall() to get bytes for the function call.

const data = web3.eth.abi.encodeFunctionCall({
    name: 'anotherfunction',
    type: 'function',
    inputs: [{
        type: 'uint256',
        name: ''
    },{
        type: 'address',
        name: ''
    }]
}, ['123', '0x0000000000000000000000000000000000000045']);

returns

0x
58de5f3c // function signature
000000000000000000000000000000000000000000000000000000000000007b // 1st argument
0000000000000000000000000000000000000000000000000000000000000045 // 2nd argument

Upvotes: 4

Related Questions