3

I have two addresses, _token1 and _token2. I would like to pack them into bytes data to pass it to a function.

bytes memory params = abi.encode(_token1, _token2);

The obtained params is the concatenation of zero-padded data.

This can be decoded in a different function like this:

(address token1, address token2) = abi.decode(params, (address, address));

Since one address is 20 bytes, which is smaller than 32 bytes, we can get a short bytes by abi.encodePacked(_token1, _token2). My question is how can I decode this encodePacked data?

1

2 Answers 2

7

You can do it only in assembly.

This is an example using two addresses. packed is formed by 32 bytes for length + 20 bytes for first address + 20 bytes for second address. By doing mload(add(packed, ADDRESS_LENGTH)) we end up reading 12+20 bytes, where the last 20 is the first address; since the variable x is of type address, the first 12 bytes are automatically dropped. Same reasoning for the second address.

    uint256 constant ADDRESS_LENGTH = 0x14;

    function testDecodePacked(address _x, address _y) external view returns(address, address) {
        bytes memory packed = abi.encodePacked(_x, _y);

        address x;
        address y;
        assembly {
            x := mload(add(packed, ADDRESS_LENGTH))
            y := mload(add(packed, mul(ADDRESS_LENGTH, 2)))
        }

        require((x == _x) && (y == _y));
        return (x, y);
    }
2
  • 1
    Thank you! now I understand how it works. packed is a byte sequence which is encodes similar to string. the first 32 bytes are almost zero except for the length information. x is loaded from the position of 20, but the first 12 bytes are discarded, so it is essentially loaded from 32 to 51. Similarly, y is loaded from 40, the first 12 bytes are discarded, so it is loaded from 52 to 71.
    – ywat
    Jan 23, 2023 at 19:01
  • Perfect!!! :) Thank you!
    – Mila A
    Mar 26 at 12:01
1

You can do it without using assembly.

You need to pass the encoded bytes as calldata as function argument. This is a static data type thus it can be sliced as a bytes array.

uint256 constant ADDRESS_LENGTH = 0x14;

function testDecodePacked(address addr1, address addr2) public {
    bytes memory packed = abi.encodePacked(addr1, addr2);
    (address addr1_, address addr2_) = this.decodePacked(packed);
    assert(addr1_ == addr1);
    assert(addr2_ == addr2);
}

function decodePacked(bytes calldata packed) external pure returns (address addr1, address addr2) {
    addr1 = address(uint160(uint256(bytes32(packed[:ADDRESS_LENGTH]) >> 96)));
    addr2 = address(uint160(uint256(bytes32(packed[ADDRESS_LENGTH:]) >> 96)));
}

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.