1

I've been trying to read the sales in this transaction.

Tried to read "Execution721MakerFeePacked" event But could not understand anything from it.

Tried seeing what NFTs got transferred and what eth got transferred but don't know which eth transfer is for which NFT sale.

Anyone has any ideas, or knows how to decode Execution721MakerFeePacked?

1 Answer 1

0

Blur Exchange packs the purchase data into 256-bit integers. It can be seen here:

function packTokenIdListingIndexTrader(
        uint256 tokenId,
        uint256 listingIndex,
        address trader
    ) private pure returns (uint256) {
        return (tokenId << (21 * 8)) | (listingIndex << (20 * 8)) | uint160(trader);
    }

    function packTypePriceCollection(
        OrderType orderType,
        uint256 price,
        address collection
    ) private pure returns (uint256) {
        return (uint256(orderType) << (31 * 8)) | (price << (20 * 8)) | uint160(collection);
    }

    function packFee(FeeRate memory fee) private pure returns (uint256) {
        return (uint256(fee.rate) << (20 * 8)) | uint160(fee.recipient);
    }

Then it emits an event like this:

emit Execution721MakerFeePacked(
                    orderHash,
                    packTokenIdListingIndexTrader(transfer.id, listingIndex, transfer.trader),
                    packTypePriceCollection(orderType, price, transfer.collection),
                    packFee(makerFee)
                );

You can reverse the binary left shift by shifting bits in a reversed direction like this:

// SPDX-License-Identifier:
pragma solidity ^0.8.13;

contract Test {

    function packTokenIdListingIndexTrader(
        uint256 tokenId,
        uint256 listingIndex,
        address trader
    ) private pure returns (uint256) {
        return (tokenId << (21 * 8)) | (listingIndex << (20 * 8)) | uint160(trader);
    }

    function unpackTokenIdListingIndexTrader(uint256 encoded) private pure returns(
        uint256 tokenId,
        uint256 listingIndex,
        address trader
    ) {
        tokenId = uint256(int(encoded) >> 21 * 8);

        listingIndex = uint256(int(encoded) >> 20 * 8);

        trader = address(uint160(uint256((int(encoded)))));
    }


    function testEncode() external view returns (uint256 encoded) {
        uint256 tokenId = 10;
        uint256 listingIndex = 1314567;
        address trader = address(this);

        encoded = packTokenIdListingIndexTrader(tokenId, listingIndex, trader);
    }

    function testDecode(uint256 encoded) external pure returns (uint256 tokenId, uint256 listingIndex, address trader) {
        (tokenId, listingIndex, trader) = unpackTokenIdListingIndexTrader(encoded);
    }
}

The above example shows how you can approach decoding params like:

  • transfer.id
  • listingIndex
  • transfer.trader

From this integer: 2208575616974937256795984653690746098943044201191461016

(The source is this transaction that you linked: https://etherscan.io/tx/0x91a17fe807cf87eefba600ada8dd85049e16872db99cdbcfa2454650e0157e9a#eventlog)

Etherscan event number screesnhot

Remix event decoding screenshot


My code snippet shows that from this transaction these fields can be interpreted as follows below.

*********************************************************************
Field         | TYPE    | DECODED VALUE
---------------------------------------------------------------------
Token ID      | uint256 | 5903
Listing index | uint256 | 1511168
Trader        | address | 0xc28D30dD716A40b0c19215b732ce7Be0E80A5098
*********************************************************************

A similar approach can be applied to decoding the price data from the second integer param in the Execution721MakerFeePacked event here.


Here's a code snippet that demonstrates how you can decode a uint-256-bit-packed number that contains an ENUM, a price value and a collection address with Solidity:

// SPDX-License-Identifier:
pragma solidity ^0.8.13;

contract Test {

    enum OrderType {
        ASK,
        BID
    }

    function packTypePriceCollection(
        OrderType orderType,
        uint256 price,
        address collection
    ) private pure returns (uint256) {
        return (uint256(orderType) << (31 * 8)) | (price << (20 * 8)) | uint160(collection);
    }

    function unpackTypePriceCollection(uint256 encoded) private pure returns(
        uint256 orderType,
        uint256 price,
        address collection
    ) {
        orderType = uint256(int(encoded) >> 31 * 8); // no way to convert integer to enum explicitly in Solidity (OrderType[uint256(int(encoded) >> 31 * 8)]; gives an error)

        price = uint256(int(encoded) >> 20 * 8);

        collection = address(uint160(uint256((int(encoded)))));
    }


    function testEncode() external view returns (uint256 encoded) {
        OrderType tokenId = OrderType.ASK;
        uint256 price = 1 ether;
        address collection = address(this);

        encoded = packTypePriceCollection(tokenId, price, collection);
    }

    function testDecode(uint256 encoded) external pure returns (uint256 orderType, uint256 price, address collection) {
        (orderType, price, collection) = unpackTypePriceCollection(encoded);
    }
}

Remix arbitrary test result's screenshot


So collectionPriceSide becomes this in a decoded form:

******************************************************************
OrderType  | uint256 | 0
Price      | uint256 | 233000000000000000
Collection | address | 0x8943C7bAC1914C9A7ABa750Bf2B6B09Fd21037E0
******************************************************************

That's how you can do that on-chain with Solidity. Any bitwise shift can be reversed with an antagonist BINARY operator.


You can deploy these UNPACK helpers on mainnet. These are view-only and can serve as a utility for you to decode the transaction data based on the events you receive.

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.