Amoy Testnet

Contract Diff Checker

Contract Name:
NameRegistryV2

Contract Source Code:

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (access/Ownable.sol)

pragma solidity ^0.8.0;

import "../utils/ContextUpgradeable.sol";
import "../proxy/utils/Initializable.sol";

/**
 * @dev Contract module which provides a basic access control mechanism, where
 * there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * By default, the owner account will be the one that deploys the contract. This
 * can later be changed with {transferOwnership}.
 *
 * This module is used through inheritance. It will make available the modifier
 * `onlyOwner`, which can be applied to your functions to restrict their use to
 * the owner.
 */
abstract contract OwnableUpgradeable is Initializable, ContextUpgradeable {
    address private _owner;

    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

    /**
     * @dev Initializes the contract setting the deployer as the initial owner.
     */
    function __Ownable_init() internal onlyInitializing {
        __Ownable_init_unchained();
    }

    function __Ownable_init_unchained() internal onlyInitializing {
        _transferOwnership(_msgSender());
    }

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        _checkOwner();
        _;
    }

    /**
     * @dev Returns the address of the current owner.
     */
    function owner() public view virtual returns (address) {
        return _owner;
    }

    /**
     * @dev Throws if the sender is not the owner.
     */
    function _checkOwner() internal view virtual {
        require(owner() == _msgSender(), "Ownable: caller is not the owner");
    }

    /**
     * @dev Leaves the contract without owner. It will not be possible to call
     * `onlyOwner` functions anymore. Can only be called by the current owner.
     *
     * NOTE: Renouncing ownership will leave the contract without an owner,
     * thereby removing any functionality that is only available to the owner.
     */
    function renounceOwnership() public virtual onlyOwner {
        _transferOwnership(address(0));
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public virtual onlyOwner {
        require(newOwner != address(0), "Ownable: new owner is the zero address");
        _transferOwnership(newOwner);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Internal function without access restriction.
     */
    function _transferOwnership(address newOwner) internal virtual {
        address oldOwner = _owner;
        _owner = newOwner;
        emit OwnershipTransferred(oldOwner, newOwner);
    }

    /**
     * @dev This empty reserved space is put in place to allow future versions to add new
     * variables without shifting down storage in the inheritance chain.
     * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
     */
    uint256[49] private __gap;
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.1) (proxy/utils/Initializable.sol)

pragma solidity ^0.8.2;

import "../../utils/AddressUpgradeable.sol";

/**
 * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
 * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
 * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
 * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
 *
 * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be
 * reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in
 * case an upgrade adds a module that needs to be initialized.
 *
 * For example:
 *
 * [.hljs-theme-light.nopadding]
 * ```
 * contract MyToken is ERC20Upgradeable {
 *     function initialize() initializer public {
 *         __ERC20_init("MyToken", "MTK");
 *     }
 * }
 * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {
 *     function initializeV2() reinitializer(2) public {
 *         __ERC20Permit_init("MyToken");
 *     }
 * }
 * ```
 *
 * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
 * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
 *
 * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
 * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
 *
 * [CAUTION]
 * ====
 * Avoid leaving a contract uninitialized.
 *
 * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
 * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke
 * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:
 *
 * [.hljs-theme-light.nopadding]
 * ```
 * /// @custom:oz-upgrades-unsafe-allow constructor
 * constructor() {
 *     _disableInitializers();
 * }
 * ```
 * ====
 */
abstract contract Initializable {
    /**
     * @dev Indicates that the contract has been initialized.
     * @custom:oz-retyped-from bool
     */
    uint8 private _initialized;

    /**
     * @dev Indicates that the contract is in the process of being initialized.
     */
    bool private _initializing;

    /**
     * @dev Triggered when the contract has been initialized or reinitialized.
     */
    event Initialized(uint8 version);

    /**
     * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
     * `onlyInitializing` functions can be used to initialize parent contracts.
     *
     * Similar to `reinitializer(1)`, except that functions marked with `initializer` can be nested in the context of a
     * constructor.
     *
     * Emits an {Initialized} event.
     */
    modifier initializer() {
        bool isTopLevelCall = !_initializing;
        require(
            (isTopLevelCall && _initialized < 1) || (!AddressUpgradeable.isContract(address(this)) && _initialized == 1),
            "Initializable: contract is already initialized"
        );
        _initialized = 1;
        if (isTopLevelCall) {
            _initializing = true;
        }
        _;
        if (isTopLevelCall) {
            _initializing = false;
            emit Initialized(1);
        }
    }

    /**
     * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
     * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
     * used to initialize parent contracts.
     *
     * A reinitializer may be used after the original initialization step. This is essential to configure modules that
     * are added through upgrades and that require initialization.
     *
     * When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`
     * cannot be nested. If one is invoked in the context of another, execution will revert.
     *
     * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
     * a contract, executing them in the right order is up to the developer or operator.
     *
     * WARNING: setting the version to 255 will prevent any future reinitialization.
     *
     * Emits an {Initialized} event.
     */
    modifier reinitializer(uint8 version) {
        require(!_initializing && _initialized < version, "Initializable: contract is already initialized");
        _initialized = version;
        _initializing = true;
        _;
        _initializing = false;
        emit Initialized(version);
    }

    /**
     * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
     * {initializer} and {reinitializer} modifiers, directly or indirectly.
     */
    modifier onlyInitializing() {
        require(_initializing, "Initializable: contract is not initializing");
        _;
    }

    /**
     * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
     * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
     * to any version. It is recommended to use this to lock implementation contracts that are designed to be called
     * through proxies.
     *
     * Emits an {Initialized} event the first time it is successfully executed.
     */
    function _disableInitializers() internal virtual {
        require(!_initializing, "Initializable: contract is initializing");
        if (_initialized < type(uint8).max) {
            _initialized = type(uint8).max;
            emit Initialized(type(uint8).max);
        }
    }

    /**
     * @dev Returns the highest version that has been initialized. See {reinitializer}.
     */
    function _getInitializedVersion() internal view returns (uint8) {
        return _initialized;
    }

    /**
     * @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.
     */
    function _isInitializing() internal view returns (bool) {
        return _initializing;
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20Upgradeable {
    /**
     * @dev Emitted when `value` tokens are moved from one account (`from`) to
     * another (`to`).
     *
     * Note that `value` may be zero.
     */
    event Transfer(address indexed from, address indexed to, uint256 value);

    /**
     * @dev Emitted when the allowance of a `spender` for an `owner` is set by
     * a call to {approve}. `value` is the new allowance.
     */
    event Approval(address indexed owner, address indexed spender, uint256 value);

    /**
     * @dev Returns the amount of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

    /**
     * @dev Returns the amount of tokens owned by `account`.
     */
    function balanceOf(address account) external view returns (uint256);

    /**
     * @dev Moves `amount` tokens from the caller's account to `to`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address to, uint256 amount) external returns (bool);

    /**
     * @dev Returns the remaining number of tokens that `spender` will be
     * allowed to spend on behalf of `owner` through {transferFrom}. This is
     * zero by default.
     *
     * This value changes when {approve} or {transferFrom} are called.
     */
    function allowance(address owner, address spender) external view returns (uint256);

    /**
     * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * IMPORTANT: Beware that changing an allowance with this method brings the risk
     * that someone may use both the old and the new allowance by unfortunate
     * transaction ordering. One possible solution to mitigate this race
     * condition is to first reduce the spender's allowance to 0 and set the
     * desired value afterwards:
     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
     *
     * Emits an {Approval} event.
     */
    function approve(address spender, uint256 amount) external returns (bool);

    /**
     * @dev Moves `amount` tokens from `from` to `to` using the
     * allowance mechanism. `amount` is then deducted from the caller's
     * allowance.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(
        address from,
        address to,
        uint256 amount
    ) external returns (bool);
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/Address.sol)

pragma solidity ^0.8.1;

/**
 * @dev Collection of functions related to the address type
 */
library AddressUpgradeable {
    /**
     * @dev Returns true if `account` is a contract.
     *
     * [IMPORTANT]
     * ====
     * It is unsafe to assume that an address for which this function returns
     * false is an externally-owned account (EOA) and not a contract.
     *
     * Among others, `isContract` will return false for the following
     * types of addresses:
     *
     *  - an externally-owned account
     *  - a contract in construction
     *  - an address where a contract will be created
     *  - an address where a contract lived, but was destroyed
     * ====
     *
     * [IMPORTANT]
     * ====
     * You shouldn't rely on `isContract` to protect against flash loan attacks!
     *
     * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
     * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
     * constructor.
     * ====
     */
    function isContract(address account) internal view returns (bool) {
        // This method relies on extcodesize/address.code.length, which returns 0
        // for contracts in construction, since the code is only stored at the end
        // of the constructor execution.

        return account.code.length > 0;
    }

    /**
     * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
     * `recipient`, forwarding all available gas and reverting on errors.
     *
     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
     * of certain opcodes, possibly making contracts go over the 2300 gas limit
     * imposed by `transfer`, making them unable to receive funds via
     * `transfer`. {sendValue} removes this limitation.
     *
     * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
     *
     * IMPORTANT: because control is transferred to `recipient`, care must be
     * taken to not create reentrancy vulnerabilities. Consider using
     * {ReentrancyGuard} or the
     * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
     */
    function sendValue(address payable recipient, uint256 amount) internal {
        require(address(this).balance >= amount, "Address: insufficient balance");

        (bool success, ) = recipient.call{value: amount}("");
        require(success, "Address: unable to send value, recipient may have reverted");
    }

    /**
     * @dev Performs a Solidity function call using a low level `call`. A
     * plain `call` is an unsafe replacement for a function call: use this
     * function instead.
     *
     * If `target` reverts with a revert reason, it is bubbled up by this
     * function (like regular Solidity function calls).
     *
     * Returns the raw returned data. To convert to the expected return value,
     * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
     *
     * Requirements:
     *
     * - `target` must be a contract.
     * - calling `target` with `data` must not revert.
     *
     * _Available since v3.1._
     */
    function functionCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0, "Address: low-level call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
     * `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but also transferring `value` wei to `target`.
     *
     * Requirements:
     *
     * - the calling contract must have an ETH balance of at least `value`.
     * - the called Solidity function must be `payable`.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value
    ) internal returns (bytes memory) {
        return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
    }

    /**
     * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
     * with `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value,
        string memory errorMessage
    ) internal returns (bytes memory) {
        require(address(this).balance >= value, "Address: insufficient balance for call");
        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
        return functionStaticCall(target, data, "Address: low-level static call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        (bool success, bytes memory returndata) = target.staticcall(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

    /**
     * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
     * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
     *
     * _Available since v4.8._
     */
    function verifyCallResultFromTarget(
        address target,
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        if (success) {
            if (returndata.length == 0) {
                // only check isContract if the call was successful and the return data is empty
                // otherwise we already know that it was a contract
                require(isContract(target), "Address: call to non-contract");
            }
            return returndata;
        } else {
            _revert(returndata, errorMessage);
        }
    }

    /**
     * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
     * revert reason or using the provided one.
     *
     * _Available since v4.3._
     */
    function verifyCallResult(
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal pure returns (bytes memory) {
        if (success) {
            return returndata;
        } else {
            _revert(returndata, errorMessage);
        }
    }

    function _revert(bytes memory returndata, string memory errorMessage) private pure {
        // Look for revert reason and bubble it up if present
        if (returndata.length > 0) {
            // The easiest way to bubble the revert reason is using memory via assembly
            /// @solidity memory-safe-assembly
            assembly {
                let returndata_size := mload(returndata)
                revert(add(32, returndata), returndata_size)
            }
        } else {
            revert(errorMessage);
        }
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)

pragma solidity ^0.8.0;
import "../proxy/utils/Initializable.sol";

/**
 * @dev Provides information about the current execution context, including the
 * sender of the transaction and its data. While these are generally available
 * via msg.sender and msg.data, they should not be accessed in such a direct
 * manner, since when dealing with meta-transactions the account sending and
 * paying for execution may not be the actual sender (as far as an application
 * is concerned).
 *
 * This contract is only required for intermediate, library-like contracts.
 */
abstract contract ContextUpgradeable is Initializable {
    function __Context_init() internal onlyInitializing {
    }

    function __Context_init_unchained() internal onlyInitializing {
    }
    function _msgSender() internal view virtual returns (address) {
        return msg.sender;
    }

    function _msgData() internal view virtual returns (bytes calldata) {
        return msg.data;
    }

    /**
     * @dev This empty reserved space is put in place to allow future versions to add new
     * variables without shifting down storage in the inheritance chain.
     * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
     */
    uint256[50] private __gap;
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/cryptography/ECDSA.sol)

pragma solidity ^0.8.0;

import "../StringsUpgradeable.sol";

/**
 * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
 *
 * These functions can be used to verify that a message was signed by the holder
 * of the private keys of a given address.
 */
library ECDSAUpgradeable {
    enum RecoverError {
        NoError,
        InvalidSignature,
        InvalidSignatureLength,
        InvalidSignatureS,
        InvalidSignatureV // Deprecated in v4.8
    }

    function _throwError(RecoverError error) private pure {
        if (error == RecoverError.NoError) {
            return; // no error: do nothing
        } else if (error == RecoverError.InvalidSignature) {
            revert("ECDSA: invalid signature");
        } else if (error == RecoverError.InvalidSignatureLength) {
            revert("ECDSA: invalid signature length");
        } else if (error == RecoverError.InvalidSignatureS) {
            revert("ECDSA: invalid signature 's' value");
        }
    }

    /**
     * @dev Returns the address that signed a hashed message (`hash`) with
     * `signature` or error string. This address can then be used for verification purposes.
     *
     * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
     * this function rejects them by requiring the `s` value to be in the lower
     * half order, and the `v` value to be either 27 or 28.
     *
     * IMPORTANT: `hash` _must_ be the result of a hash operation for the
     * verification to be secure: it is possible to craft signatures that
     * recover to arbitrary addresses for non-hashed data. A safe way to ensure
     * this is by receiving a hash of the original message (which may otherwise
     * be too long), and then calling {toEthSignedMessageHash} on it.
     *
     * Documentation for signature generation:
     * - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js]
     * - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers]
     *
     * _Available since v4.3._
     */
    function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError) {
        if (signature.length == 65) {
            bytes32 r;
            bytes32 s;
            uint8 v;
            // ecrecover takes the signature parameters, and the only way to get them
            // currently is to use assembly.
            /// @solidity memory-safe-assembly
            assembly {
                r := mload(add(signature, 0x20))
                s := mload(add(signature, 0x40))
                v := byte(0, mload(add(signature, 0x60)))
            }
            return tryRecover(hash, v, r, s);
        } else {
            return (address(0), RecoverError.InvalidSignatureLength);
        }
    }

    /**
     * @dev Returns the address that signed a hashed message (`hash`) with
     * `signature`. This address can then be used for verification purposes.
     *
     * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
     * this function rejects them by requiring the `s` value to be in the lower
     * half order, and the `v` value to be either 27 or 28.
     *
     * IMPORTANT: `hash` _must_ be the result of a hash operation for the
     * verification to be secure: it is possible to craft signatures that
     * recover to arbitrary addresses for non-hashed data. A safe way to ensure
     * this is by receiving a hash of the original message (which may otherwise
     * be too long), and then calling {toEthSignedMessageHash} on it.
     */
    function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
        (address recovered, RecoverError error) = tryRecover(hash, signature);
        _throwError(error);
        return recovered;
    }

    /**
     * @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately.
     *
     * See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures]
     *
     * _Available since v4.3._
     */
    function tryRecover(
        bytes32 hash,
        bytes32 r,
        bytes32 vs
    ) internal pure returns (address, RecoverError) {
        bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);
        uint8 v = uint8((uint256(vs) >> 255) + 27);
        return tryRecover(hash, v, r, s);
    }

    /**
     * @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately.
     *
     * _Available since v4.2._
     */
    function recover(
        bytes32 hash,
        bytes32 r,
        bytes32 vs
    ) internal pure returns (address) {
        (address recovered, RecoverError error) = tryRecover(hash, r, vs);
        _throwError(error);
        return recovered;
    }

    /**
     * @dev Overload of {ECDSA-tryRecover} that receives the `v`,
     * `r` and `s` signature fields separately.
     *
     * _Available since v4.3._
     */
    function tryRecover(
        bytes32 hash,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) internal pure returns (address, RecoverError) {
        // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
        // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
        // the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most
        // signatures from current libraries generate a unique signature with an s-value in the lower half order.
        //
        // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
        // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
        // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
        // these malleable signatures as well.
        if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
            return (address(0), RecoverError.InvalidSignatureS);
        }

        // If the signature is valid (and not malleable), return the signer address
        address signer = ecrecover(hash, v, r, s);
        if (signer == address(0)) {
            return (address(0), RecoverError.InvalidSignature);
        }

        return (signer, RecoverError.NoError);
    }

    /**
     * @dev Overload of {ECDSA-recover} that receives the `v`,
     * `r` and `s` signature fields separately.
     */
    function recover(
        bytes32 hash,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) internal pure returns (address) {
        (address recovered, RecoverError error) = tryRecover(hash, v, r, s);
        _throwError(error);
        return recovered;
    }

    /**
     * @dev Returns an Ethereum Signed Message, created from a `hash`. This
     * produces hash corresponding to the one signed with the
     * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
     * JSON-RPC method as part of EIP-191.
     *
     * See {recover}.
     */
    function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32) {
        // 32 is the length in bytes of hash,
        // enforced by the type signature above
        return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", hash));
    }

    /**
     * @dev Returns an Ethereum Signed Message, created from `s`. This
     * produces hash corresponding to the one signed with the
     * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
     * JSON-RPC method as part of EIP-191.
     *
     * See {recover}.
     */
    function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32) {
        return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n", StringsUpgradeable.toString(s.length), s));
    }

    /**
     * @dev Returns an Ethereum Signed Typed Data, created from a
     * `domainSeparator` and a `structHash`. This produces hash corresponding
     * to the one signed with the
     * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`]
     * JSON-RPC method as part of EIP-712.
     *
     * See {recover}.
     */
    function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32) {
        return keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash));
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/math/Math.sol)

pragma solidity ^0.8.0;

/**
 * @dev Standard math utilities missing in the Solidity language.
 */
library MathUpgradeable {
    enum Rounding {
        Down, // Toward negative infinity
        Up, // Toward infinity
        Zero // Toward zero
    }

    /**
     * @dev Returns the largest of two numbers.
     */
    function max(uint256 a, uint256 b) internal pure returns (uint256) {
        return a > b ? a : b;
    }

    /**
     * @dev Returns the smallest of two numbers.
     */
    function min(uint256 a, uint256 b) internal pure returns (uint256) {
        return a < b ? a : b;
    }

    /**
     * @dev Returns the average of two numbers. The result is rounded towards
     * zero.
     */
    function average(uint256 a, uint256 b) internal pure returns (uint256) {
        // (a + b) / 2 can overflow.
        return (a & b) + (a ^ b) / 2;
    }

    /**
     * @dev Returns the ceiling of the division of two numbers.
     *
     * This differs from standard division with `/` in that it rounds up instead
     * of rounding down.
     */
    function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
        // (a + b - 1) / b can overflow on addition, so we distribute.
        return a == 0 ? 0 : (a - 1) / b + 1;
    }

    /**
     * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
     * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv)
     * with further edits by Uniswap Labs also under MIT license.
     */
    function mulDiv(
        uint256 x,
        uint256 y,
        uint256 denominator
    ) internal pure returns (uint256 result) {
        unchecked {
            // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
            // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
            // variables such that product = prod1 * 2^256 + prod0.
            uint256 prod0; // Least significant 256 bits of the product
            uint256 prod1; // Most significant 256 bits of the product
            assembly {
                let mm := mulmod(x, y, not(0))
                prod0 := mul(x, y)
                prod1 := sub(sub(mm, prod0), lt(mm, prod0))
            }

            // Handle non-overflow cases, 256 by 256 division.
            if (prod1 == 0) {
                return prod0 / denominator;
            }

            // Make sure the result is less than 2^256. Also prevents denominator == 0.
            require(denominator > prod1);

            ///////////////////////////////////////////////
            // 512 by 256 division.
            ///////////////////////////////////////////////

            // Make division exact by subtracting the remainder from [prod1 prod0].
            uint256 remainder;
            assembly {
                // Compute remainder using mulmod.
                remainder := mulmod(x, y, denominator)

                // Subtract 256 bit number from 512 bit number.
                prod1 := sub(prod1, gt(remainder, prod0))
                prod0 := sub(prod0, remainder)
            }

            // Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1.
            // See https://cs.stackexchange.com/q/138556/92363.

            // Does not overflow because the denominator cannot be zero at this stage in the function.
            uint256 twos = denominator & (~denominator + 1);
            assembly {
                // Divide denominator by twos.
                denominator := div(denominator, twos)

                // Divide [prod1 prod0] by twos.
                prod0 := div(prod0, twos)

                // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
                twos := add(div(sub(0, twos), twos), 1)
            }

            // Shift in bits from prod1 into prod0.
            prod0 |= prod1 * twos;

            // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
            // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
            // four bits. That is, denominator * inv = 1 mod 2^4.
            uint256 inverse = (3 * denominator) ^ 2;

            // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works
            // in modular arithmetic, doubling the correct bits in each step.
            inverse *= 2 - denominator * inverse; // inverse mod 2^8
            inverse *= 2 - denominator * inverse; // inverse mod 2^16
            inverse *= 2 - denominator * inverse; // inverse mod 2^32
            inverse *= 2 - denominator * inverse; // inverse mod 2^64
            inverse *= 2 - denominator * inverse; // inverse mod 2^128
            inverse *= 2 - denominator * inverse; // inverse mod 2^256

            // Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
            // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
            // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
            // is no longer required.
            result = prod0 * inverse;
            return result;
        }
    }

    /**
     * @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
     */
    function mulDiv(
        uint256 x,
        uint256 y,
        uint256 denominator,
        Rounding rounding
    ) internal pure returns (uint256) {
        uint256 result = mulDiv(x, y, denominator);
        if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {
            result += 1;
        }
        return result;
    }

    /**
     * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down.
     *
     * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
     */
    function sqrt(uint256 a) internal pure returns (uint256) {
        if (a == 0) {
            return 0;
        }

        // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
        //
        // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
        // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
        //
        // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
        // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
        // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
        //
        // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
        uint256 result = 1 << (log2(a) >> 1);

        // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
        // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
        // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
        // into the expected uint128 result.
        unchecked {
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            return min(result, a / result);
        }
    }

    /**
     * @notice Calculates sqrt(a), following the selected rounding direction.
     */
    function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = sqrt(a);
            return result + (rounding == Rounding.Up && result * result < a ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 2, rounded down, of a positive value.
     * Returns 0 if given 0.
     */
    function log2(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >> 128 > 0) {
                value >>= 128;
                result += 128;
            }
            if (value >> 64 > 0) {
                value >>= 64;
                result += 64;
            }
            if (value >> 32 > 0) {
                value >>= 32;
                result += 32;
            }
            if (value >> 16 > 0) {
                value >>= 16;
                result += 16;
            }
            if (value >> 8 > 0) {
                value >>= 8;
                result += 8;
            }
            if (value >> 4 > 0) {
                value >>= 4;
                result += 4;
            }
            if (value >> 2 > 0) {
                value >>= 2;
                result += 2;
            }
            if (value >> 1 > 0) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 2, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log2(value);
            return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 10, rounded down, of a positive value.
     * Returns 0 if given 0.
     */
    function log10(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >= 10**64) {
                value /= 10**64;
                result += 64;
            }
            if (value >= 10**32) {
                value /= 10**32;
                result += 32;
            }
            if (value >= 10**16) {
                value /= 10**16;
                result += 16;
            }
            if (value >= 10**8) {
                value /= 10**8;
                result += 8;
            }
            if (value >= 10**4) {
                value /= 10**4;
                result += 4;
            }
            if (value >= 10**2) {
                value /= 10**2;
                result += 2;
            }
            if (value >= 10**1) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 10, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log10(value);
            return result + (rounding == Rounding.Up && 10**result < value ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 256, rounded down, of a positive value.
     * Returns 0 if given 0.
     *
     * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
     */
    function log256(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >> 128 > 0) {
                value >>= 128;
                result += 16;
            }
            if (value >> 64 > 0) {
                value >>= 64;
                result += 8;
            }
            if (value >> 32 > 0) {
                value >>= 32;
                result += 4;
            }
            if (value >> 16 > 0) {
                value >>= 16;
                result += 2;
            }
            if (value >> 8 > 0) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 10, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log256(value);
            return result + (rounding == Rounding.Up && 1 << (result * 8) < value ? 1 : 0);
        }
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/Strings.sol)

pragma solidity ^0.8.0;

import "./math/MathUpgradeable.sol";

/**
 * @dev String operations.
 */
library StringsUpgradeable {
    bytes16 private constant _SYMBOLS = "0123456789abcdef";
    uint8 private constant _ADDRESS_LENGTH = 20;

    /**
     * @dev Converts a `uint256` to its ASCII `string` decimal representation.
     */
    function toString(uint256 value) internal pure returns (string memory) {
        unchecked {
            uint256 length = MathUpgradeable.log10(value) + 1;
            string memory buffer = new string(length);
            uint256 ptr;
            /// @solidity memory-safe-assembly
            assembly {
                ptr := add(buffer, add(32, length))
            }
            while (true) {
                ptr--;
                /// @solidity memory-safe-assembly
                assembly {
                    mstore8(ptr, byte(mod(value, 10), _SYMBOLS))
                }
                value /= 10;
                if (value == 0) break;
            }
            return buffer;
        }
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
     */
    function toHexString(uint256 value) internal pure returns (string memory) {
        unchecked {
            return toHexString(value, MathUpgradeable.log256(value) + 1);
        }
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
     */
    function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
        bytes memory buffer = new bytes(2 * length + 2);
        buffer[0] = "0";
        buffer[1] = "x";
        for (uint256 i = 2 * length + 1; i > 1; --i) {
            buffer[i] = _SYMBOLS[value & 0xf];
            value >>= 4;
        }
        require(value == 0, "Strings: hex length insufficient");
        return string(buffer);
    }

    /**
     * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation.
     */
    function toHexString(address addr) internal pure returns (string memory) {
        return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);
    }
}

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.18;

library EnumerableSet {
    struct StringSet {
        string[] _values;
        mapping(string => uint256) _indexes;
    }

    function add(StringSet storage set, string memory value) internal returns (bool) {
        if (!contains(set, value)) {
            set._values.push(value);
            // The value is stored at length-1, but we add 1 to all indexes
            // and use 0 as a sentinel value
            set._indexes[value] = set._values.length;
            return true;
        } else {
            return false;
        }
    }

    function remove(StringSet storage set, string memory value) internal returns (bool) {
        // We read and store the value's index to prevent multiple reads from the same storage slot
        uint256 valueIndex = set._indexes[value];

        if (valueIndex != 0) {
            // Equivalent to contains(set, value)
            // To delete an element from the _values array in O(1), we swap the element to delete with the last one in
            // the array, and then remove the last element (sometimes called as 'swap and pop').
            // This modifies the order of the array, as noted in {at}.

            uint256 toDeleteIndex = valueIndex - 1;
            uint256 lastIndex = set._values.length - 1;

            if (lastIndex != toDeleteIndex) {
                string memory lastValue = set._values[lastIndex];

                // Move the last value to the index where the value to delete is
                set._values[toDeleteIndex] = lastValue;
                // Update the index for the moved value
                set._indexes[lastValue] = valueIndex; // Replace lastValue's index to valueIndex
            }

            // Delete the slot where the moved value was stored
            set._values.pop();

            // Delete the index for the deleted slot
            delete set._indexes[value];

            return true;
        } else {
            return false;
        }
    }

    function clear(StringSet storage set) internal {
        uint len = set._values.length;
        if (len > 0) {
            string memory value;
            for (uint i; i < len;) {
                value = set._values[i];
                delete set._indexes[value];
                unchecked { ++i; }
            }
            delete set._values;
        }
    }

    function contains(StringSet storage set, string memory value) internal view returns (bool) {
        return set._indexes[value] != 0;
    }

    function length(StringSet storage set) internal view returns (uint256) {
        return set._values.length;
    }

    function at(StringSet storage set, uint256 index) internal view returns (string memory) {
        return set._values[index];
    }

    function values(StringSet storage set) internal view returns (string[] memory) {
        return set._values;
    }

}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;

/**
 * Strings Library
 * 
 * In summary this is a simple library of string functions which make simple 
 * string operations less tedious in solidity.
 * 
 * Please be aware these functions can be quite gas heavy so use them only when
 * necessary not to clog the blockchain with expensive transactions.
 * 
 */
library StringLib {
    /**
     * Upper
     * 
     * Converts all the values of a string to their corresponding upper case
     * value.
     * 
     * @param _base When being used for a data type this is the extended object
     *              otherwise this is the string base to convert to upper case
     * @return string 
     */
    function upper(string memory _base)
        internal
        pure
        returns (string memory) {
        bytes memory _baseBytes = bytes(_base);
        for (uint i; i < _baseBytes.length;) {
            _baseBytes[i] = _upper(_baseBytes[i]);
            unchecked { ++i; }
        }
        return string(_baseBytes);
    }

    /**
     * Lower
     * 
     * Converts all the values of a string to their corresponding lower case
     * value.
     * 
     * @param _base When being used for a data type this is the extended object
     *              otherwise this is the string base to convert to lower case
     * @return string 
     */
    function lower(string memory _base)
        internal
        pure
        returns (string memory) {
        bytes memory _baseBytes = bytes(_base);
        for (uint i; i < _baseBytes.length;) {
            _baseBytes[i] = _lower(_baseBytes[i]);
            unchecked { ++i; }
        }
        return string(_baseBytes);
    }

    /**
     * Upper
     * 
     * Convert an alphabetic character to upper case and return the original
     * value when not alphabetic
     * 
     * @param _b1 The byte to be converted to upper case
     * @return bytes1 The converted value if the passed value was alphabetic
     *                and in a lower case otherwise returns the original value
     */
    function _upper(bytes1 _b1)
        private
        pure
        returns (bytes1) {

        if (_b1 >= 0x61 && _b1 <= 0x7A) {
            return bytes1(uint8(_b1) - 32);
        }

        return _b1;
    }

    /**
     * Lower
     * 
     * Convert an alphabetic character to lower case and return the original
     * value when not alphabetic
     * 
     * @param _b1 The byte to be converted to lower case
     * @return bytes1 The converted value if the passed value was alphabetic
     *                and in a upper case otherwise returns the original value
     */
    function _lower(bytes1 _b1)
        private
        pure
        returns (bytes1) {

        if (_b1 >= 0x41 && _b1 <= 0x5A) {
            return bytes1(uint8(_b1) + 32);
        }

        return _b1;
    }
}

//SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;

interface INameRegistry {
    /**
     * @notice Emitted when a name is registered
     * @param name Registred name
     * @param DID DID that registered the `name`
     */
    event Register(string indexed name, address indexed DID);

    /**
     * @notice Emitted when a name is unregistered
     * @param name Unregistered name
     * @param DID DID that unregistered the `name`
     */
    event Unregister(string indexed name, address indexed DID);

    /**
     * @notice Emitted when the contract owner added a suffix
     * @param suffix Added suffix
     */
    event AddSuffix(string indexed suffix);

    /**
     * @notice Emitted when the contract owner update the maximum count of names per DID
     * @param from Previous value
     * @param to Updated value
     */
    event UpdateMaxNamesPerDID(uint from, uint to);

    /**
     * @notice Return the nonce of given DID
     * @param did DID address
     * @return uint nonce of the DID
     */
    function nonce(address did) external view returns(uint);

    /**
     * @notice Register a name
     * @dev Only the names with valid suffix, that is registered in the contract, are available
     * @param name Name to be registered
     * @param did DID address
     * @param signature Signature of parameters signed by the `did`'s private key

     */
    function register(string calldata name, address did, bytes calldata signature) external;

    /**
     * @notice Unregister a name
     * @param name Name to be unregistered. Should be registered before.
     * @param did DID address.
     * @param signature Signature of parameters signed by the `did`'s private key
     */
    function unregister(string calldata name, address did, bytes calldata signature) external;

    /**
     * @notice Find the DID of the given name
     * @dev If the `name` is not registered before, transaction will be reverted
     * @param name Name registered to a DID
     * @return address DID address of the given name
     */
    function findDID(string memory name) external view returns(address);

    /**
     * @notice Get the list of registered names
     * @dev If the `did` has no names registered before, the transaction will be reverted
     * @param did DID address
     * @return string[] List of names
     */
    function getUserNameList(address did) external view returns(string[] memory);

    /**
     * @notice Add a suffix
     * @dev Only the contract owner can add/remove a suffix.
     * @param suffix - Suffix to be added
     */
    function addSuffix(string memory suffix) external payable;

    /**
     * @notice Check the given suffix is valid
     * @param suffix Suffix to be checked
     * @return bool true if valid
     */
    function isValidSuffix(string calldata suffix) external view returns(bool);

    /**
     * @notice Return array of valid suffixes
     * @return string[] List of suffixes
     */
    function getSuffixList() external view returns(string[] memory);

    /**
     * @notice Update maximum number of names per DID
     * @param count Value to be updated
     */
    function updateMaxNamesPerDID(uint count) external payable;
}

//SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;

import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";

// Verida contract dependencies
import "@verida/common-contract/contracts/EnumerableSet.sol";
import "@verida/common-contract/contracts/StringLib.sol";
import "./INameRegistry.sol";
import "../VeridaDataVerificationLib.sol";

/**
 * @title Verida NameRegistry contract
 */
contract NameRegistry is  INameRegistry, OwnableUpgradeable {

    using EnumerableSet for EnumerableSet.StringSet;
    using StringLib for string;

    /**
     * @notice nonce for did
     */
    mapping(address => uint) internal _nonce;

    /**
     * @notice username to did
     */
    mapping(string => address) internal _nameToDID;
    
    /** 
     * @notice DID to username list
     */
    mapping(address => EnumerableSet.StringSet) internal _DIDInfoList;

    /**
     * @notice Allowed suffix list
     */
    EnumerableSet.StringSet internal suffixList;

    /**
     * @notice Maximum names per DID.
     */
    uint public maxNamesPerDID;

    /**
     * @notice Gap for later use
     */
    uint256[20] private __gap;

    // Custom errors
    error InvalidAddress();
    error InvalidSuffix();
    // error InvalidSignature();
    error InvalidName();
    error LimitedNameCount();
    error InvalidNameCount();

    /**
     * @notice Initialize
     */
    function initialize() public initializer {
        __Ownable_init();

        maxNamesPerDID = 1;

        string memory suffix = "vda";
        suffixList.add(suffix);
        emit AddSuffix(suffix);
    }

    /**
     * @dev See {INameRegistry}
     */
    function nonce(address did) external view virtual override returns(uint) {
        return _nonce[did];
    }

    /**
     * @dev See {INameRegistry}
     */
    function register(string calldata name, address did, bytes calldata signature) external virtual override {
        assembly {
            if iszero(did) {
                let ptr := mload(0x40)
                mstore(ptr, 0xe6c4247b00000000000000000000000000000000000000000000000000000000)
                revert(ptr, 0x4) //revert InvalidAddress()
            }
        }
        if(!_isValidSuffix(name)) {
            revert InvalidSuffix();
        }

        {
            uint didNonce = _nonce[did];
            bytes memory paramData = abi.encodePacked(
                name,
                did,
                didNonce
            );

            if (!VeridaDataVerificationLib.validateSignature(paramData, signature, did)) {
                revert InvalidSignature();
            }
            ++_nonce[did];
        }

        string memory _name = name.lower();
        {
            // Check _nameToDID[_name] is zero
            address _nameDID = _nameToDID[_name];
            assembly {
                if eq(iszero(_nameDID), 0) {
                    let ptr := mload(0x40)
                    mstore(ptr, 0x430f13b300000000000000000000000000000000000000000000000000000000)
                    revert(ptr, 0x4) //revert InvalidName()
                }
            }
        }
        
        EnumerableSet.StringSet storage didUserNameList = _DIDInfoList[did];

        if (didUserNameList.length() >= maxNamesPerDID) {
            revert LimitedNameCount();
        }
        
        _nameToDID[_name] = did;
        didUserNameList.add(_name);

        emit Register(name, did);
    }

    /**
     * @dev See {INameRegistry}
     */
    function unregister(string calldata name, address did, bytes calldata signature) external virtual {
        assembly {
            if iszero(did) {
                let ptr := mload(0x40)
                mstore(ptr, 0xe6c4247b00000000000000000000000000000000000000000000000000000000)
                revert(ptr, 0x4) // revert InvalidAddress()
            }
        }
        
        {
            uint didNonce = _nonce[did];
            bytes memory paramData = abi.encodePacked(
                name,
                did,
                didNonce
            );

            if (!VeridaDataVerificationLib.validateSignature(paramData, signature, did)) {
                revert InvalidSignature();
            }
            ++_nonce[did];
        }
        
        string memory _name = name.lower();

        address nameDID = _nameToDID[_name];
        assembly {
            if iszero(nameDID) {
                let ptr := mload(0x40)
                mstore(ptr, 0x430f13b300000000000000000000000000000000000000000000000000000000)
                revert(ptr, 0x4) // revert InvalidName()
            }
        }

        if (nameDID != did) {
            revert InvalidAddress();
        }
        
        EnumerableSet.StringSet storage didUserNameList = _DIDInfoList[nameDID];

        delete _nameToDID[_name];
        didUserNameList.remove(_name);

        emit Unregister(name, nameDID);
    }

    /**
     * @dev See {INameRegistry}
     */
    function findDID(string memory name) external view virtual override returns(address) {
        name = name.lower();

        address nameDID = _nameToDID[name];
        assembly {
            if iszero(nameDID) {
                let ptr := mload(0x40)
                mstore(ptr, 0x430f13b300000000000000000000000000000000000000000000000000000000)
                revert(ptr, 0x4) // revert InvalidName()
            }
        }

        return nameDID;
    }

    /**
     * @dev See {INameRegistry}
     */
    function getUserNameList(address did) external view virtual override returns(string[] memory) {
        EnumerableSet.StringSet storage didUserNameList = _DIDInfoList[did];

        uint256 length = didUserNameList.length();
        if (length == 0) {
            revert InvalidAddress();
        }

        string[] memory userNameList = new string[](length);

        for (uint i; i < length;) {
            userNameList[i] = didUserNameList.at(i);
            unchecked { ++i; }
        }

        return userNameList;
    }

    /**
     * @dev See {INameRegistry}
     */
    function addSuffix(string memory suffix) external virtual payable override onlyOwner {
        suffix = suffix.lower();

        if (suffixList.contains(suffix)) {
            revert InvalidSuffix();
        }

        suffixList.add(suffix);

        emit AddSuffix(suffix);
    }

    /**
     * @notice Check whether name has valid suffix
     * @dev Check all the letters of name inside _getSuffix() function
     * @param name - name to check
     * @return result
     */
    function _isValidSuffix(string calldata name) internal view virtual returns(bool) {
        string memory suffix = _getSuffix(name);
        return suffixList.contains(suffix);
    }

    /**
     * @dev See {INameRegistry}
     */
    function isValidSuffix(string calldata suffix) external view virtual override returns(bool) {
        string memory lower = suffix.lower();
        return suffixList.contains(lower);
    }

    /**
     * @notice Get Suffix from name
     * @dev Rejected if name contains invalid characters or not found suffix.
     * @param name - Input name
     * @return suffix - return suffix in bytes32
     */
    function _getSuffix(string calldata name) internal pure virtual returns(string memory suffix) {
        string memory _name = name.lower();
        bytes memory nameBytes = bytes(_name);
        if (nameBytes.length == 0) {
            revert InvalidName();
        }

        uint len = nameBytes.length;

        uint startIndex = len;
        uint index;
        uint8 dotCount;
        while (index < len && dotCount < 2 && _isValidCharacter(nameBytes[index])) {
            // Find a "."
            unchecked {
                if (nameBytes[index] == 0x2E) {
                    startIndex = index + 1;
                    ++dotCount;
                }

                ++index;
            }
        }
        if (startIndex >= len) {
            revert InvalidName();
        }

        if (dotCount > 1 || index != len || startIndex <= 2 || startIndex >= 34) {
            revert InvalidName();
        }

        bytes memory suffixBytes = new bytes(len - startIndex);

        for (index = startIndex; index < len;) {
            suffixBytes[index - startIndex] = nameBytes[index];
            unchecked { ++index; }
        }

        suffix = string(suffixBytes);
    }

    /**
     * @dev See {INameRegistry}
     */
    function getSuffixList() external view virtual override returns(string[] memory) {
        uint len = suffixList.length();
        string[] memory list = new string[](len);
        for (uint i; i < len;) {
            list[i] = suffixList.at(i);
            unchecked {
                ++i;
            }
        }
        return list;
    }

    /**
     * @notice Check whether character is allowed in NameRegistry
     * @param char - one byte from name string value
     * @return - true if valid.
     */
    function _isValidCharacter(bytes1 char) internal pure virtual returns(bool) {
        if (char >= 0x61 && char <= 0x7a)
            return true;
        if (char >= 0x30 && char <= 0x39)
            return true;
        if (char ==0x5f || char == 0x2d || char == 0x2e)
            return true;
        return false;
    }

    /**
     * @dev See {INameRegistry}
     */
    function updateMaxNamesPerDID(uint count) external virtual payable onlyOwner {
        uint orgValue = maxNamesPerDID;
        if (count <= orgValue) {
            revert InvalidNameCount();
        }
        maxNamesPerDID = count;

        emit UpdateMaxNamesPerDID(orgValue, count);
    }
}

//SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;

import "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol";

interface INameRegistryV2 {
    /**
     * @notice Struct for App meta data
     * @param key Key of item. Ex : "domain"
     * @param value Value of item. Ex: "verida.network"
     */
    struct AppMetaDataItem {
        string key;
        string value;
    }

    /**
     * @notice Emitted when a app is registered
     * @param did DID address
     * @param ownerName Owner name
     * @param appName App name
     * @param metadata Array of key/value pairs
     */
    event RegisterApp(
        address did,
        string ownerName,
        string appName,
        AppMetaDataItem[] metadata
    );

    /**
     * @notice Emitted when an App is de-registered
     * @param did DID address
     * @param ownerName Owner name
     * @param appName App name
     */
    event DeregisterApp(
        address did,
        string ownerName,
        string appName
    );

    /**
     * @notice Emitted when a meta data item of an App is updated
     * @param did DID address
     * @param ownerName Owner's name
     * @param appName App name
     * @param item Meta data item updated
     */
    event UpdateApp(
        address did, 
        string ownerName,
        string appName,
        AppMetaDataItem item
    );

    /**
     * @notice Emitted when the app registering feature enabled/disabled
     * @param enabled true if enabled.
     */
    event AppRegisterEnabled(bool enabled);

    /**
     * @notice Emitted when the token address set
     * @param tokenAddr Token address set
     */
    event SetTokenAddress(address tokenAddr);

    /**
     * @notice Emitted when the app registering fee is updated
     * @param from Original fee
     * @param to New fee
     */
    event UpdateAppRegisterFee(uint from, uint to);

    /**
     * @notice Register an app
     * @dev Need to deposit VDA token as fee. Fee is definec by Verida - Contract owner
     *      metadata should contain an item that the key is "domain".
     * @param did DID address
     * @param ownerName Owner's name. Only alphanumeric characters including spaces
     * @param appName App name. Only alphanumeric characters including space
     * @param metadata Array of key/value pairs
     * @param requestSignature The request parameters signed by the `didAddress` private key
     * @param requestProof Used to verify request
     */
    function registerApp(
        address did,
        string calldata ownerName,
        string calldata appName,
        AppMetaDataItem[] calldata metadata,
        bytes calldata requestSignature,
        bytes calldata requestProof
    ) external;

    /**
     * @notice Deregister an app
     * @param did DID address
     * @param ownerName Owner's name
     * @param appName App name
     * @param requestSignature The request parameters signed by the `didAddress` private key
     * @param requestProof Used to verify request
     */
    function deregisterApp(
        address did, 
        string calldata ownerName, 
        string calldata appName,
        bytes calldata requestSignature,
        bytes calldata requestProof
    ) external;

    /**
     * @notice Update the meta data item
     * @param did DID address
     * @param ownerName Owner's name
     * @param appName App name
     * @param item Meta data item to be updated
     * @param requestSignature The request parameters signed by the `didAddress` private key
     * @param requestProof Used to verify request
     */
    function updateApp(
        address did, 
        string calldata ownerName,
        string calldata appName,
        AppMetaDataItem calldata item,
        bytes calldata requestSignature,
        bytes calldata requestProof
    ) external;

    /**
     * @notice Get an app with given owner & app names
     * @param ownerName Owner's name
     * @param appName App name
     * @return address DID
     * @return AppMetaDataItem[] Array of meta data items
     */
    function getApp(string calldata ownerName, string calldata appName) external view returns(address, AppMetaDataItem[] memory);


    /**
     * @notice Set the Verida Token address
     * @dev Only the contract owner is allowed to call this function.
            This function is required because the original `NameRegistry` contract has no Token address
     * @param tokenAddr Address of Verida Token
     */
    function setTokenAddress(IERC20Upgradeable tokenAddr) external payable;

    /**
     * @notice Get the Verida token address
     * @return address Verida token address
     */
    function getTokenAddress() external view returns(address);

    /**
     * @notice Update fee for registering an app
     * @dev Only the contract owner is allowed
     * @param feeAmount Amount of VDA token
     */
    function updateAppRegisterFee(uint feeAmount) external payable;

    /**
     * @notice Return the fee for registering an app
     * @return uint Amount of tokens for fee
     */
    function getAppRegisterFee() external view returns(uint);

    /**
     * @notice Enable/disable the app registering feature
     * @dev Only the contract owner is allowe.
            The contract owner should enable the app registering feature after fee set.
     * @param isEnabled true if enabling, otherwise false
     */
    function setAppRegisterEnabled(bool isEnabled) external payable;

    /**
     * @notice Return whether App registering is enabled
     * @return bool true if enabled.
     */
    function isAppRegisterEnabled() external view returns(bool);

    /**
     * @notice Returns the contract version
     * @return string Contract version
     */
    function getVersion() external pure returns(string memory);

}

//SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;

import "../v1/NameRegistry.sol";
import "./INameRegistryV2.sol";

// import "hardhat/console.sol";

contract NameRegistryV2 is NameRegistry, INameRegistryV2 {

    using EnumerableSet for EnumerableSet.StringSet;
    using StringLib for string;

    /**
     * @notice Used to managed the regisgered status of ownerName and appName
     * @param registered true if registered before
     * @param DID matched DID
     */
    struct OwnerNameInfo {
        bool registered;
        address DID;
    }
    
    /**
     * @notice DID to owner name
     */
    mapping (address => string) internal _didOwnerName;

    /**
     * @notice owner name registered status
     * @dev Owner name is unique in the contract. 1:1 matched to the DID
     */
    mapping (string => OwnerNameInfo) internal _ownerNameInfo;

    /**
     * @notice DID to App name list
     */
    mapping (address => EnumerableSet.StringSet) internal _didApps;

    /**
     * @notice Meta data list of a DID's appName
     * @dev App name is unique in a DID's app name list. DID => app name => 
     */
    mapping (address => mapping(string => AppMetaDataItem[])) internal _didAppMetaData;

    /**
     * @notice Verida token address
     * @dev Used to pay the fees while registering an application
     */
    IERC20Upgradeable internal vdaToken;

    /**
     * @notice Fee for registering app
     */
    uint internal appRegisterFee;

    bool internal _isAppRegisterEnabled;

    /*
     * @notice Gap for later use
     */
    uint256[50] private __gap;

    error AppRegisterNotEnabled();
    error InvalidOwnerName();
    error DuplicatedOwnerName();
    error InvalidAppName();
    error DuplicatedAppName();
    error InvalidDomainName();
    error AppNotFound(bool isInvalidOwner, bool isInvalidApp);
    error NoDomainInAppMetaData();
    error InvalidValue();
    error TokenAddressNotSet();
    error AppRegisterFeeNotSet();

    /**
     * @notice Check a lower-cased string whether contains [a-z] and allowed special characters
     * @param input Lower-cased string
     * @param allowedCharacters String of allowed special characters
     * @return bool true if valid
     */
    function _isValidString(string memory input, string memory allowedCharacters) internal pure virtual returns(bool) {
        bytes memory inputBytes = bytes(input);
        uint len = inputBytes.length;
        bytes1 char;
        if (len == 0) {
            return false;
        }
        bytes memory specialChars = bytes(allowedCharacters);
        uint specLen = specialChars.length;

        unchecked {
            for (uint i; i < len;) {
                char = inputBytes[i];
                if (!(char >= 0x61 && char <= 0x7a) && !(char >= 0x30 && char <= 0x39)) {
                    bool isSpecChar;
                    for (uint j; j < specLen;) {
                        if (char == specialChars[j]) {
                            isSpecChar = true;
                            break;
                        }
                        ++j;
                    }
                    if (!isSpecChar) {
                        return false;
                    }
                }
                ++i;
            }
        }

        return true;
    }

    /**
     * @notice Validate the owner name in the `registerApp()` function
     * @param did DID
     * @param ownerName Wwner name to be registered
     * @return string Lowercased owner name
     */
    function _validateOwnerName(address did, string calldata ownerName) internal virtual returns(string memory) {
        string memory _ownerName;
        _ownerName = ownerName.lower();

        // Check validity of characters
        if (!_isValidString(_ownerName, " ")) {
            revert InvalidOwnerName();
        }
        
        OwnerNameInfo storage status = _ownerNameInfo[_ownerName];
        // Check owner name is registered
        if (status.registered) {
            if (status.DID == did) {
                return _ownerName;
            } else {
                revert DuplicatedOwnerName();
            }
        }

        // Check DID has an owner name
        if (bytes(_didOwnerName[did]).length != 0) {
            // If DID has no owner name registered
            revert InvalidOwnerName();
        }

        return _ownerName;
    }

    /**
     * @notice Validate the app name in the `registerApp()` function
     * @param did DID
     * @param appName App name to be registered
     * @return string Lowercased app name
     */
    function _validateAppName(address did, string calldata appName) internal virtual returns(string memory) {
        string memory _appName;
        // Check validity of characters
        _appName = appName.lower();

        if (!_isValidString(_appName, " ")) {
            revert InvalidAppName();
        }

        if (_didApps[did].contains(_appName)) {
            revert DuplicatedAppName();
        }

        return _appName;
    }

    /**
     * @notice Validate `metadata` of the `registerApp()` function and returns packed data
     * @dev The packed meta data is used to verify the request
     * @param metadata App meta data
     * @return bytes Packed meta data
     */
    function _getAppMetaDataPacked(AppMetaDataItem[] calldata metadata) internal pure virtual returns(bytes memory) {
        uint len = metadata.length;
        bytes memory ret;
        bool isDomainIncluded;

        bytes32 domainKey = keccak256(bytes("domain"));

        for (uint i; i < len;) {
            if (keccak256(bytes(metadata[i].key)) == domainKey) {
                isDomainIncluded = true;
                if (!_isValidString(metadata[i].value, "_-")) {
                    revert InvalidDomainName();
                }
            }
            ret = abi.encodePacked(ret, metadata[i].key, metadata[i].value);
            unchecked {
                ++i;
            }
        }

        if (!isDomainIncluded) {
            revert NoDomainInAppMetaData();
        }

        return ret;
    }

    /**
     * @notice Verify whether a given request is valid. Verifies the nonce of the DID making the request.
     * @dev Verify the signature & proof signed by valid signers
     * @param did DID that made the request. Nonce will be incremented against this DID to avoid replay attacks.
     * @param params Parameters of the message.
     * @param signature A signature that matches sign(${didSignAddress}, params)
     * @param proof Proof A signature that matches sign(did, `${didAddress}${didSignAddress}`)
     */
    function verifyRequest(
        address did, 
        bytes memory params, 
        bytes memory signature, 
        bytes memory proof
    ) internal virtual {
        // Verify the nonce is valid by including it in the unsignedData to be checked
        uint didNonce = _nonce[did];
        bytes memory unsignedParams = abi.encodePacked(
            params,
            didNonce
        );

        address[] memory signers = new address[](1);
        signers[0] = did;

        // Verify the params were signed by the DID making the request
        VeridaDataVerificationLib.verifyDataWithSigners(
            unsignedParams,
            signature,
            proof,
            signers
        );

        // Increment the nonce to prevent replay attacks
        ++_nonce[did];
    }

    /**
     * @notice Receive App registering fee
     * @dev Used in the `registerApp()` function
     *      No need to check token address here because `enableAppRegister()` checked it
     * @param from Address that pays the fee
     */
    function _receiveAppFee(address from) internal virtual {
        // To-do check fee can be 0
        if (appRegisterFee > 0) {
            vdaToken.transferFrom(from, address(this), appRegisterFee);
        }
    }

    /**
     * @notice Register app info to storage
     * @dev Used in the `registerApp()` function
     */
    function _registerAppInfo(
        address did,
        string memory ownerName,
        string memory appName,
        AppMetaDataItem[] calldata metadata
    ) internal virtual {
        // Register owner name
        _didOwnerName[did] = ownerName;
        _ownerNameInfo[ownerName].registered = true;
        _ownerNameInfo[ownerName].DID = did;

        // Register app name
        _didApps[did].add(appName);

        AppMetaDataItem[] storage appData = _didAppMetaData[did][appName];
        uint len = metadata.length;
        for (uint i; i < len;) {
            appData.push(metadata[i]);
            unchecked {
                ++i;
            }
        }
    }
    
    /**
     * @dev See {INameRegistryV2}
     */
    function registerApp(
        address did,
        string calldata ownerName,
        string calldata appName,
        AppMetaDataItem[] calldata metadata,
        bytes calldata requestSignature,
        bytes calldata requestProof
    ) external virtual override {
        string memory _ownerName;
        string memory _appName;

        if (!_isAppRegisterEnabled) {
            revert AppRegisterNotEnabled();
        }

        // Check input values and verify request
        {
            _ownerName = _validateOwnerName(did, ownerName);
            _appName = _validateAppName(did, appName);

            bytes memory params = _getAppMetaDataPacked(metadata);
            params = abi.encodePacked(did, ownerName, appName, params);

            verifyRequest(did, params, requestSignature, requestProof);
        }
        
        _receiveAppFee(tx.origin);

        _registerAppInfo(did, _ownerName, _appName, metadata);

        emit RegisterApp(did, ownerName, appName, metadata);
    }

    /**
     * @notice Check the owner name & app name are registered to the DID
     * @dev Used in `deregisterApp()` and `updateApp()` functions
     * @param did DID
     * @param ownerName Owner name - lowercased
     * @param appName App name - lowercased
     */
    function _validateExistingApp(
        address did,
        string memory ownerName,
        string memory appName
    ) internal view {

        if (_ownerNameInfo[ownerName].DID != did) {
            revert AppNotFound(true, false);
        }

        if (!_didApps[did].contains(appName)) {
            revert AppNotFound(false, true);
        }
    }

    /**
     * @dev See {INameRegistryV2}
     */
    function deregisterApp(
        address did, 
        string calldata ownerName, 
        string calldata appName,
        bytes calldata requestSignature,
        bytes calldata requestProof
    ) external virtual override {
        string memory _ownerName;
        string memory _appName;

        _ownerName = ownerName.lower();
        _appName = appName.lower();

        
        // verify request
        {
            _validateExistingApp(did, _ownerName, _appName);

            bytes memory params = abi.encodePacked(did, ownerName, appName);
            verifyRequest(did, params, requestSignature, requestProof);
        }

        // de-register
        delete _didOwnerName[did];
        delete _ownerNameInfo[_ownerName];

        _didApps[did].remove(_appName);
        delete _didAppMetaData[did][_appName];

        emit DeregisterApp(did, _ownerName, _appName);
    }

    /**
     * @notice Compare 2 strings
     * @param left string
     * @param right string
     * @return bool true if equal
     */
    function _isSameString(string memory left, string memory right) internal pure returns(bool) {
        return keccak256(abi.encodePacked(left)) == keccak256(abi.encodePacked(right));
    }

    /**
     * @dev See {INameRegistryV2}
     */
    function updateApp(
        address did, 
        string calldata ownerName,
        string calldata appName,
        AppMetaDataItem calldata item,
        bytes calldata requestSignature,
        bytes calldata requestProof
    ) external virtual override {
        string memory _ownerName;
        string memory _appName;

        _ownerName = ownerName.lower();
        _appName = appName.lower();

        
        // verify request
        {
            _validateExistingApp(did, _ownerName, _appName);

            bytes memory params = abi.encodePacked(did, ownerName, appName);
            params = abi.encodePacked(params, item.key, item.value);
            verifyRequest(did, params, requestSignature, requestProof);
        }

        AppMetaDataItem[] storage itemList = _didAppMetaData[did][_appName];
        uint len = itemList.length;
        bool isExistingItem;
        for (uint i; i < len;) {
            if (_isSameString(itemList[i].key, item.key)) {
                isExistingItem = true;
                itemList[i].value = item.value;
                break;
            }
            unchecked {
                ++i;
            }
        }
        if (!isExistingItem) {
            itemList.push(item);
        }

        emit UpdateApp(did, _ownerName, _appName, item);
    }

    /**
     * @dev See {INameRegistryV2}
     */
    function getApp(string calldata ownerName, string calldata appName) external view virtual override returns(address, AppMetaDataItem[] memory) {
        string memory _ownerName;
        string memory _appName;
        _ownerName = ownerName.lower();
        _appName = appName.lower();


        if (!_ownerNameInfo[_ownerName].registered) {
            revert InvalidOwnerName();
        }

        address did;
        did = _ownerNameInfo[_ownerName].DID;

        if (!_didApps[did].contains(_appName)) {
            revert InvalidAppName();
        }

        return (did, _didAppMetaData[did][_appName]);
    }


    /**
     * @dev See {INameRegistryV2}
     *      Once token address is set, it will never be set as zero address
     */
    function setTokenAddress(IERC20Upgradeable tokenAddr) external virtual payable override onlyOwner {
        assembly {
            if iszero(tokenAddr) {
                let ptr := mload(0x40)
                mstore(ptr, 0xaa7feadc00000000000000000000000000000000000000000000000000000000)
                revert(ptr, 0x4) //revert InvalidValue()
            }
        }

        if (address(vdaToken) == address(tokenAddr)) {
            revert InvalidValue();
        }

        vdaToken = tokenAddr;

        emit SetTokenAddress(address(tokenAddr));
    }

    /**
     * @dev See {INameRegistryV2}
     */
    function getTokenAddress() external view virtual override returns(address) {
        return address(vdaToken);
    }

    /**
     * @dev See {INameRegistryV2}
     *      Once fee is set, it will never be updated as 0
     */
    function updateAppRegisterFee(uint feeAmount) external virtual payable override onlyOwner {
        if (feeAmount == appRegisterFee || feeAmount == 0) {
            revert InvalidValue();
        }

        uint orgFee = appRegisterFee;
        appRegisterFee = feeAmount;

        emit UpdateAppRegisterFee(orgFee, appRegisterFee);
    }

    /**
     * @dev See {INameRegistryV2}
     */
    function getAppRegisterFee() external view virtual override returns(uint) {
        return appRegisterFee;
    }

    /**
     * @dev See {INameRegistryV2}
     */
    function setAppRegisterEnabled(bool isEnabled) external virtual payable override onlyOwner {
        if (_isAppRegisterEnabled == isEnabled) {
            revert InvalidValue();
        }

        if (isEnabled) {
            address tokenAddr =  address(vdaToken);
            assembly {
                if iszero(tokenAddr) {
                    let ptr := mload(0x40)
                    mstore(ptr, 0x898921d600000000000000000000000000000000000000000000000000000000)
                    revert(ptr, 0x4) //revert TokenAddressNotSet()
                }
            }
        }

        if (appRegisterFee == 0) {
            revert AppRegisterFeeNotSet();
        }

        _isAppRegisterEnabled = isEnabled;

        emit AppRegisterEnabled(_isAppRegisterEnabled);
    }

    /**
     * @dev See {INameRegistryV2}
     */
    function isAppRegisterEnabled() external view virtual override returns(bool) {
        return _isAppRegisterEnabled;
    }

    /**
     * @dev See {INameRegistryV2}
     */
    function getVersion() external pure virtual override returns(string memory) {
        return "2.0";
    }
}

//SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;

import "@openzeppelin/contracts-upgradeable/utils/cryptography/ECDSAUpgradeable.sol";

error NoSigners();
error InvalidSignature();

library VeridaDataVerificationLib {
  function getSignerAddress(
    bytes memory _unsignedData,
    bytes calldata _signature
  ) internal pure returns (address) {
    bytes32 unsignedDataHash = keccak256(_unsignedData);
    return ECDSAUpgradeable.recover(unsignedDataHash, _signature);
  }

  function validateSignature(
    bytes memory _unsignedData,
    bytes calldata _signature,
    address _signerAddress
  ) internal pure returns (bool result) {
    address signerAddress = getSignerAddress(_unsignedData, _signature);
    result = signerAddress == _signerAddress;
    assembly {
      if iszero(signerAddress) {
        result := false
      }
    }
  }

  /**
    * Verify any data is signed by a particular array of DID addresses
    * @dev Copied from `VDAVerificationContract` to verify request
    * @param data Any type of raw data
    * @param signature Data signed by a Verida application context signing key
    * @param proof Signed proof that a Verida DID controls a Verida application context signing key
    * @param validSigners Array of did addresses that are valid signers of data
    */
  function verifyDataWithSigners(
      bytes memory data, 
      bytes memory signature,
      bytes memory proof,
      address[] memory validSigners
  ) internal pure {
    if (validSigners.length == 0) {
        revert NoSigners();
    }

    if (data.length == 0 || signature.length == 0 || proof.length == 0) {
        revert InvalidSignature();
    }

    bytes32 dataHash = keccak256(data);
    address contextSigner = ECDSAUpgradeable.recover(dataHash, signature);
    string memory strContextSigner = StringsUpgradeable.toHexString(uint256(uint160(contextSigner)));

    bool isVerified;
    uint index;

    while (index < validSigners.length && !isVerified) {
        address account = validSigners[index];

        string memory strAccount = StringsUpgradeable.toHexString(uint256(uint160(account)));
        bytes memory proofString = abi.encodePacked(
            strAccount,
            strContextSigner
        );
        bytes32 proofHash = keccak256(proofString);
        address didSigner = ECDSAUpgradeable.recover(proofHash, proof);

        if (didSigner == account) {
            isVerified = true;
            break;
        }
        unchecked { ++index; }
    }

    if (!isVerified) {
        revert InvalidSignature();
    }
  }
}

Please enter a contract address above to load the contract details and source code.

Context size (optional):