3

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))
        }

3 Answers 3

1

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)
        );
    }
}
1

It looks like the issue with that is, you're not extracting the 'offer' (first nested encodePacked) off the data variable correctly. You are only initializing an empty bytes memory offer and then trying to load data from it. Instead of it, you should first copy the data from the main data variable to offer variable and then go with the decoding.

you should replace your first assembly line code with the code below:

bytes memory offer = new bytes(162);

assembly {
    let dataPtr := add(data, 32) // Skips length field
    let offerPtr := add(offer, 32) // Skips length field
    mstore(offer, 162) // Sets length of the offer
    for { let i := 0 } lt(i, 162) { i := add(i, 32) } {
        mstore(add(offerPtr, i), mload(add(dataPtr, i)))
    }
}
1

The issue with your code is that you're not properly extracting the first encodePacked part from the entire data. You should first extract the nested encodePacked data from the main data, and then decode the extracted data.

Try the following code in which I've fixed the extraction of the nested encodePacked data from data by copying the 162 bytes from data to the offer variable. Also, I've adjusted the byte offsets for the last two variables (duration and adminFee_bps) because you were using the wrong values.

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

bytes memory offer = new bytes(162);

assembly {
    mstore(add(offer, 32), mload(add(data, 32)))
    mstore(add(offer, 64), mload(add(data, 64)))
    mstore(add(offer, 96), mload(add(data, 96)))
    mstore(add(offer,128), mload(add(data,128)))
    mstore(add(offer,160), mload(add(data,160)))
}

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

assembly { 
    denomination := mload(add(offer, 32))
    principal := mload(add(offer, 64))
    repaymentWithFee := mload(add(offer, 96))
    collection := mload(add(offer,128))
    nft_id := mload(add(offer,160))
    referrer := mload(add(offer,180))
    duration := mload(add(offer,184))
    adminFee_bps := mload(add(offer,186))
}

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