Solidity Assembly – How to Decode a Nested encodePacked

abiencoderv2assemblydecodingsolidityyul

The structure of the encoding is

    abi.encodePacked(
       abi.encodePacked(
          address,
          uint256,
          uint256,
          address,
          uint256,
          address,
          uint32,
          uint16
       ),
       abi.encodePacked(
          address,
          uint256,
          uint256
       ),
       address,
       uint256
    )

(They only save 2 slots, I'm not sure why they did it this way)

I want to decode the first encode packed (called the 'offer' which is 162 bytes) from the entire thing.

I wrote this code which isn't working, though the bottom assembly block works for a non-nested encodePacked (abi.encode(encodePacked(..),encodePacked(...),...)

        bytes memory data; // = abi.encodePacked(abi.encodePacked(...),abi.encodePacked(...),address,uint256)

        bytes memory offer;

        assembly {
            offer := mload(add(offer,162))
        }
        
        address denomination;
        uint principal;
        uint repaymentWithFee;
        address collection;
        uint nft_id;
        address referrer;
        uint32 duration;
        uint16 adminFee_bps;
        
        assembly { 
            denomination := mload(add(offer,20))
            principal := mload(add(offer,52))
            repaymentWithFee := mload(add(offer,84))
            collection := mload(add(offer,104))
            nft_id := mload(add(offer,136))
            referrer := mload(add(offer,156))
            duration := mload(add(offer,160))
            adminFee_bps := mload(add(offer,162))
        }

Best Answer

I'm not sure what you were trying to do with offer := mload(add(offer,162)). In any case the second assembly block you have is correct, you only need to mload directly from data.

The first arguments of data are indeed denomination, principal, etc., since everything is packed. There's no bytes memory offer argument inside data that we need to decode, you only have that in the case data = abi.encode(encodePacked(..),encodePacked(...),...).

This works:

struct Offer {
    address denomination;
    uint principal;
    uint repaymentWithFee;
    address collection;
    uint nft_id;
    address referrer;
    uint32 duration;
    uint16 adminFee_bps;
}

contract DecodePacked {
    function testDecodePacked() external pure returns(Offer memory) {
        bytes memory data = packExample();

        address denomination;
        uint principal;
        uint repaymentWithFee;
        address collection;
        uint nft_id;
        address referrer;
        uint32 duration;
        uint16 adminFee_bps;

        assembly { 
            denomination := mload(add(data,20))
            principal := mload(add(data,52))
            repaymentWithFee := mload(add(data,84))
            collection := mload(add(data,104))
            nft_id := mload(add(data,136))
            referrer := mload(add(data,156))
            duration := mload(add(data,160))
            adminFee_bps := mload(add(data,162))
        }

        return Offer(
            denomination, principal, repaymentWithFee, collection, nft_id, referrer, duration, adminFee_bps
        );
    }

    // returns an example for nested encodePacked
    function packExample() public pure returns(bytes memory) {
        return abi.encodePacked(
            abi.encodePacked(
                address(0x01),
                uint256(0x02),
                uint256(0x03),
                address(0x04),
                uint256(0x05),
                address(0x06),
                uint32(0x07),
                uint16(0x08)
            ),
            abi.encodePacked(
                address(0x09),
                uint256(0x10),
                uint256(0x11)
            ),
            address(0x12),
            uint256(0x13)
        );
    }
}
Related Topic