6.3 Query Interface

The Record Category Registry provides some public functions for querying the registry:


    /*//////////////////////////////////////////////////////////////////////////
    | PUBLIC FUNCTIONS
    //////////////////////////////////////////////////////////////////////////*/

    /**
     * @notice Returns the category bitmap for a record hash for a specific protocol scope
     * @param scope The protocol scope identifier
     * @param recordHash The hash of the record event
     * @return categoryBitmap The category bitmap for the record hash
     */
    function getCategoryBitmap(
        uint256 scope,
        bytes32 recordHash
    ) public view returns (bytes32 categoryBitmap) {
        (bool exists, bytes32 bitmap) = scopeRecordCategories[scope].tryGet(
            recordHash
        );
        if (!exists) {
            revert RecordNotFound(scope, recordHash);
        }
        return bitmap;
    }

    /**
     * @notice Returns the category bitmap for a record hash at a given index
     *          for a specific protocol scope
     * @param scope The protocol scope identifier
     * @param index The index of the record hash
     * @return recordHash recordHash at the given index
     * @return categoryBitmap The category bitmap for the record hash
     */
    function getRecordHashAndCategoryAt(
        uint256 scope,
        uint256 index
    ) public view returns (bytes32 recordHash, bytes32 categoryBitmap) {
        return scopeRecordCategories[scope].at(index);
    }

    /**
     * @notice Return the category bitmap for a record hash
     *          for a specific protocol scope
     # @dev does not revert if the record hash does not exist
     * @param scope The protocol scope identifier
     * @param recordHash The hash of the record event
     * @return exists A boolean indicating if the record hash exists
     * @return categoryBitmap The category bitmap for the record hash
     */
    function tryGetCategoryBitmap(
        uint256 scope,
        bytes32 recordHash
    ) public view returns (bool exists, bytes32 categoryBitmap) {
        return scopeRecordCategories[scope].tryGet(recordHash);
    }

    /**
     * @notice Returns the record hashes and their categories for a specific protocol scope between
     *         a given range
     * @param scope The protocol scope identifier
     * @param from The start index of the range
     * @param to The end index of the range
     * @return recordHashes The record hashes for the given range
     * @return categoryBitmaps The category bitmaps for the given range
     */
    function getRecordHashesAndCategories(
        uint256 scope,
        uint256 from,
        uint256 to
    )
        public
        view
        returns (
            bytes32[] memory recordHashes,
            bytes32[] memory categoryBitmaps
        )
    {
        require(
            from < to && to <= scopeRecordCategories[scope].length(),
            "Invalid range"
        );
        uint256 length = to - from;
        recordHashes = new bytes32[](length);
        categoryBitmaps = new bytes32[](length);

        for (uint256 i = 0; i < length; i++) {
            (recordHashes[i], categoryBitmaps[i]) = scopeRecordCategories[scope]
                .at(from + i);
        }
    }

    /**
     * @notice Returns the last record hash & it's category and the merkle-root
     *         for a specific protocol scope
     * @param scope The protocol scope identifier
     * @return root The merkle root for the protocol scope
     * @return recordHash The hash of the last known record event
     * @return categoryBitmap The category bitmap for the last known record event
     * @return index The index of the last known record event
     */
    function getLatestForScope(
        uint256 scope
    )
        public
        view
        returns (
            uint256 root,
            bytes32 recordHash,
            bytes32 categoryBitmap,
            uint256 index
        )
    {
        root = scopeRecordMerkleTrees[scope]._root();
        index = scopeRecordCategories[scope].length();
        if (index > 0) {
            (recordHash, categoryBitmap) = scopeRecordCategories[scope].at(
                index - 1
            );
        }
    }


function _applyPredicate(
    PredicateType predicateType,
    bytes32 characteristicFunction,
    bytes32 elementProperties
) internal pure returns (bool satisfiesPredicate) {
    if (predicateType == PredicateType.Intersection) {
        satisfiesPredicate =
            (elementProperties & characteristicFunction) ==
            characteristicFunction;
    } else if (predicateType == PredicateType.Union) {
        satisfiesPredicate =
            (elementProperties & characteristicFunction) != 0;
    } else if (predicateType == PredicateType.Complement) {
        satisfiesPredicate =
            (elementProperties & characteristicFunction) == 0;
    }
}

function applyPredicate(
    uint256 domain,
    bytes32[] calldata subset,
    bytes32 characteristicFunction,
    PredicateType predicateType
) public view returns (bytes32[] memory elements, uint256 setCardinality) {
    bytes32[] memory satisfyingElements = new bytes32[](subset.length);
    for (uint256 i = 0; i < subset.length; i++) {
        bytes32 element = subset[i];
        (bool isMember, bytes32 elementProperties) = tryGetCategoryBitmap(
            domain,
            element
        );
        if (!isMember) {
            continue;
        }
        if (
            _applyPredicate(
                predicateType,
                characteristicFunction,
                elementProperties
            )
        ) {
            satisfyingElements[setCardinality] = element;
            setCardinality++;
        }
    }
    assembly {
        mstore(satisfyingElements, setCardinality)
    }
    return (satisfyingElements, setCardinality);
}

Predicate types:

  • 0: Intersection (all bits in categoryMask must be set)
  • 1: Union (at least one bit in categoryMask must be set)
  • 2: Complement (no bits in categoryMask should be set)