0

I'm currently testing a few things using the Solidity assembly. Basically, what I want to do is

  1. Pack several variables of different type together into one bytes array i.e. similar to what abi.encodePacked would do (I assume)
  2. Write the opposite code that will split the packed message and extract the original variables

The message layout might be something like this

  • 32 bytes :: id
  • 20 bytes :: addressA
  • 20 bytes :: addressB
  • 4 bytes :: nonce

In essence, I would like to pack the above four variable into on single bytes using assembly and return the it from the function. At the other end, I would also like to use some assembly code to extract those four variables from the packed bytes value.

The encode function will be function(uint256, address, address, uint8) The decode function will be function(bytes data) returns(uint256, address, address, uint8

I have done some research about using assembly but unfortunately I couldn't find and easy way to accomplish that.

1
pragma solidity 0.5.16;

contract Test {
    function encode(uint256 a, address b, address c, uint8 d) public view returns(bytes memory) {
        bytes memory data = new bytes(80);
        
        assembly {
            mstore(
                add(
                    data,
                    32
                ),
                a
            )
            
            mstore(
                add(
                    data,
                    64
                ),
                shl(96, b)
            )
            
            mstore(
                add(
                    data,
                    84
                ),
                shl(96, c)
            )
            
            mstore(
                add(
                    data,
                    104
                ),
                shl(248, d)
            )
        }
        
        return data;
    }
    
    function decode(bytes memory data) public view returns(uint256 a, address b, address c, uint8 d) {
        assembly {
            a := mload(
                add(
                    data,
                    32
                )
            )
            
            b := mload(
                add(
                    data,
                    52
                )
            )
            
            c := mload(
                add(
                    data,
                    72
                )
            )
            
            d := mload(
                add(
                    data,
                    73
                )
            )
        }
    }
}

Let me know if you have any questions about it.

4
  • Thanks for the answer. Can you explain what the shl(96, b) does?
    – ppoliani
    Nov 26 '20 at 15:55
  • 1
    mstore works with 32 bytes for each operation. b is an address type requiring 20 bytes and address type being little endian is converted into 32 bytes by appending 12 bytes of 0 values (or 96 0s) to the left of it. We left shift the address by 96 bits so that the appended 12 bytes are replaced by the content of address and the address is stored in 20 bytes of memory instead of 32 bytes. Nov 26 '20 at 16:11
  • wow that's a great explanation. Thanks mate. I tried something similar doing the following. Will this work? ``` bytes32 messageId = keccak256(abi.encodePacked(address(this), nonce)); // total size = 32 (messageID) + 20 (caller address) + 20 (contract address) + 8 (gas) bytes memory message = new bytes(80); assembly { mstore(add(message, 80), gasLimit) mstore(add(message, 72), contractAddr) mstore(add(message, 52), caller()) mstore(add(message, 32), messageId) } ```
    – ppoliani
    Nov 26 '20 at 16:30
  • it does not look correct to me. Nov 26 '20 at 16:51

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.