contract ExchangeStateV1 is OwnableOperatorRole {

    // keccak256(OrderKey) => completed
    mapping(bytes32 => uint256) public completed;

    /// @notice Get the amount of selled tokens.
    /// @param key - the `OrderKey` struct.
    /// @return Selled tokens count for the order.
    function getCompleted(ExchangeDomainV1.OrderKey calldata key) view external returns (uint256) {
        return completed[getCompletedKey(key)];
    }

    /// @notice Sets the new amount of selled tokens. Can be called only by the contract operator.
    /// @param key - the `OrderKey` struct.
    /// @param newCompleted - The new value to set.
    function setCompleted(ExchangeDomainV1.OrderKey calldata key, uint256 newCompleted) external onlyOperator {
        completed[getCompletedKey(key)] = newCompleted;
    }

    /// @notice Encode order key to use as the mapping key.
    /// @param key - the `OrderKey` struct.
    /// @return Encoded order key.
    function getCompletedKey(ExchangeDomainV1.OrderKey memory key) pure public returns (bytes32) {
        return keccak256(abi.encodePacked(key.owner, key.sellAsset.token, key.sellAsset.tokenId, key.buyAsset.token, key.buyAsset.tokenId, key.salt));
    }
}
/// @title ExchangeStateV1
/// @notice Tracks the amount of selled tokens in the order.
function getCompleted(ExchangeDomainV1.OrderKey calldata key) view external returns (uint256) {
    return completed[getCompletedKey(key)];
}

/// @notice Get the amount of selled tokens.
/// @param key - the `OrderKey` struct.
/// @return Selled tokens count for the order.