{
  "language": "Solidity",
  "sources": {
    "@openzeppelin/contracts/access/AccessControl.sol": {
      "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.0.0) (access/AccessControl.sol)\n\npragma solidity ^0.8.20;\n\nimport {IAccessControl} from \"./IAccessControl.sol\";\nimport {Context} from \"../utils/Context.sol\";\nimport {ERC165} from \"../utils/introspection/ERC165.sol\";\n\n/**\n * @dev Contract module that allows children to implement role-based access\n * control mechanisms. This is a lightweight version that doesn't allow enumerating role\n * members except through off-chain means by accessing the contract event logs. Some\n * applications may benefit from on-chain enumerability, for those cases see\n * {AccessControlEnumerable}.\n *\n * Roles are referred to by their `bytes32` identifier. These should be exposed\n * in the external API and be unique. The best way to achieve this is by\n * using `public constant` hash digests:\n *\n * ```solidity\n * bytes32 public constant MY_ROLE = keccak256(\"MY_ROLE\");\n * ```\n *\n * Roles can be used to represent a set of permissions. To restrict access to a\n * function call, use {hasRole}:\n *\n * ```solidity\n * function foo() public {\n *     require(hasRole(MY_ROLE, msg.sender));\n *     ...\n * }\n * ```\n *\n * Roles can be granted and revoked dynamically via the {grantRole} and\n * {revokeRole} functions. Each role has an associated admin role, and only\n * accounts that have a role's admin role can call {grantRole} and {revokeRole}.\n *\n * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means\n * that only accounts with this role will be able to grant or revoke other\n * roles. More complex role relationships can be created by using\n * {_setRoleAdmin}.\n *\n * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to\n * grant and revoke this role. Extra precautions should be taken to secure\n * accounts that have been granted it. We recommend using {AccessControlDefaultAdminRules}\n * to enforce additional security measures for this role.\n */\nabstract contract AccessControl is Context, IAccessControl, ERC165 {\n    struct RoleData {\n        mapping(address account => bool) hasRole;\n        bytes32 adminRole;\n    }\n\n    mapping(bytes32 role => RoleData) private _roles;\n\n    bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;\n\n    /**\n     * @dev Modifier that checks that an account has a specific role. Reverts\n     * with an {AccessControlUnauthorizedAccount} error including the required role.\n     */\n    modifier onlyRole(bytes32 role) {\n        _checkRole(role);\n        _;\n    }\n\n    /**\n     * @dev See {IERC165-supportsInterface}.\n     */\n    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {\n        return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId);\n    }\n\n    /**\n     * @dev Returns `true` if `account` has been granted `role`.\n     */\n    function hasRole(bytes32 role, address account) public view virtual returns (bool) {\n        return _roles[role].hasRole[account];\n    }\n\n    /**\n     * @dev Reverts with an {AccessControlUnauthorizedAccount} error if `_msgSender()`\n     * is missing `role`. Overriding this function changes the behavior of the {onlyRole} modifier.\n     */\n    function _checkRole(bytes32 role) internal view virtual {\n        _checkRole(role, _msgSender());\n    }\n\n    /**\n     * @dev Reverts with an {AccessControlUnauthorizedAccount} error if `account`\n     * is missing `role`.\n     */\n    function _checkRole(bytes32 role, address account) internal view virtual {\n        if (!hasRole(role, account)) {\n            revert AccessControlUnauthorizedAccount(account, role);\n        }\n    }\n\n    /**\n     * @dev Returns the admin role that controls `role`. See {grantRole} and\n     * {revokeRole}.\n     *\n     * To change a role's admin, use {_setRoleAdmin}.\n     */\n    function getRoleAdmin(bytes32 role) public view virtual returns (bytes32) {\n        return _roles[role].adminRole;\n    }\n\n    /**\n     * @dev Grants `role` to `account`.\n     *\n     * If `account` had not been already granted `role`, emits a {RoleGranted}\n     * event.\n     *\n     * Requirements:\n     *\n     * - the caller must have ``role``'s admin role.\n     *\n     * May emit a {RoleGranted} event.\n     */\n    function grantRole(bytes32 role, address account) public virtual onlyRole(getRoleAdmin(role)) {\n        _grantRole(role, account);\n    }\n\n    /**\n     * @dev Revokes `role` from `account`.\n     *\n     * If `account` had been granted `role`, emits a {RoleRevoked} event.\n     *\n     * Requirements:\n     *\n     * - the caller must have ``role``'s admin role.\n     *\n     * May emit a {RoleRevoked} event.\n     */\n    function revokeRole(bytes32 role, address account) public virtual onlyRole(getRoleAdmin(role)) {\n        _revokeRole(role, account);\n    }\n\n    /**\n     * @dev Revokes `role` from the calling account.\n     *\n     * Roles are often managed via {grantRole} and {revokeRole}: this function's\n     * purpose is to provide a mechanism for accounts to lose their privileges\n     * if they are compromised (such as when a trusted device is misplaced).\n     *\n     * If the calling account had been revoked `role`, emits a {RoleRevoked}\n     * event.\n     *\n     * Requirements:\n     *\n     * - the caller must be `callerConfirmation`.\n     *\n     * May emit a {RoleRevoked} event.\n     */\n    function renounceRole(bytes32 role, address callerConfirmation) public virtual {\n        if (callerConfirmation != _msgSender()) {\n            revert AccessControlBadConfirmation();\n        }\n\n        _revokeRole(role, callerConfirmation);\n    }\n\n    /**\n     * @dev Sets `adminRole` as ``role``'s admin role.\n     *\n     * Emits a {RoleAdminChanged} event.\n     */\n    function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {\n        bytes32 previousAdminRole = getRoleAdmin(role);\n        _roles[role].adminRole = adminRole;\n        emit RoleAdminChanged(role, previousAdminRole, adminRole);\n    }\n\n    /**\n     * @dev Attempts to grant `role` to `account` and returns a boolean indicating if `role` was granted.\n     *\n     * Internal function without access restriction.\n     *\n     * May emit a {RoleGranted} event.\n     */\n    function _grantRole(bytes32 role, address account) internal virtual returns (bool) {\n        if (!hasRole(role, account)) {\n            _roles[role].hasRole[account] = true;\n            emit RoleGranted(role, account, _msgSender());\n            return true;\n        } else {\n            return false;\n        }\n    }\n\n    /**\n     * @dev Attempts to revoke `role` to `account` and returns a boolean indicating if `role` was revoked.\n     *\n     * Internal function without access restriction.\n     *\n     * May emit a {RoleRevoked} event.\n     */\n    function _revokeRole(bytes32 role, address account) internal virtual returns (bool) {\n        if (hasRole(role, account)) {\n            _roles[role].hasRole[account] = false;\n            emit RoleRevoked(role, account, _msgSender());\n            return true;\n        } else {\n            return false;\n        }\n    }\n}\n"
    },
    "@openzeppelin/contracts/access/IAccessControl.sol": {
      "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.0.0) (access/IAccessControl.sol)\n\npragma solidity ^0.8.20;\n\n/**\n * @dev External interface of AccessControl declared to support ERC165 detection.\n */\ninterface IAccessControl {\n    /**\n     * @dev The `account` is missing a role.\n     */\n    error AccessControlUnauthorizedAccount(address account, bytes32 neededRole);\n\n    /**\n     * @dev The caller of a function is not the expected one.\n     *\n     * NOTE: Don't confuse with {AccessControlUnauthorizedAccount}.\n     */\n    error AccessControlBadConfirmation();\n\n    /**\n     * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`\n     *\n     * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite\n     * {RoleAdminChanged} not being emitted signaling this.\n     */\n    event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);\n\n    /**\n     * @dev Emitted when `account` is granted `role`.\n     *\n     * `sender` is the account that originated the contract call, an admin role\n     * bearer except when using {AccessControl-_setupRole}.\n     */\n    event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);\n\n    /**\n     * @dev Emitted when `account` is revoked `role`.\n     *\n     * `sender` is the account that originated the contract call:\n     *   - if using `revokeRole`, it is the admin role bearer\n     *   - if using `renounceRole`, it is the role bearer (i.e. `account`)\n     */\n    event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);\n\n    /**\n     * @dev Returns `true` if `account` has been granted `role`.\n     */\n    function hasRole(bytes32 role, address account) external view returns (bool);\n\n    /**\n     * @dev Returns the admin role that controls `role`. See {grantRole} and\n     * {revokeRole}.\n     *\n     * To change a role's admin, use {AccessControl-_setRoleAdmin}.\n     */\n    function getRoleAdmin(bytes32 role) external view returns (bytes32);\n\n    /**\n     * @dev Grants `role` to `account`.\n     *\n     * If `account` had not been already granted `role`, emits a {RoleGranted}\n     * event.\n     *\n     * Requirements:\n     *\n     * - the caller must have ``role``'s admin role.\n     */\n    function grantRole(bytes32 role, address account) external;\n\n    /**\n     * @dev Revokes `role` from `account`.\n     *\n     * If `account` had been granted `role`, emits a {RoleRevoked} event.\n     *\n     * Requirements:\n     *\n     * - the caller must have ``role``'s admin role.\n     */\n    function revokeRole(bytes32 role, address account) external;\n\n    /**\n     * @dev Revokes `role` from the calling account.\n     *\n     * Roles are often managed via {grantRole} and {revokeRole}: this function's\n     * purpose is to provide a mechanism for accounts to lose their privileges\n     * if they are compromised (such as when a trusted device is misplaced).\n     *\n     * If the calling account had been granted `role`, emits a {RoleRevoked}\n     * event.\n     *\n     * Requirements:\n     *\n     * - the caller must be `callerConfirmation`.\n     */\n    function renounceRole(bytes32 role, address callerConfirmation) external;\n}\n"
    },
    "@openzeppelin/contracts/token/ERC20/IERC20.sol": {
      "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/IERC20.sol)\n\npragma solidity ^0.8.20;\n\n/**\n * @dev Interface of the ERC20 standard as defined in the EIP.\n */\ninterface IERC20 {\n    /**\n     * @dev Emitted when `value` tokens are moved from one account (`from`) to\n     * another (`to`).\n     *\n     * Note that `value` may be zero.\n     */\n    event Transfer(address indexed from, address indexed to, uint256 value);\n\n    /**\n     * @dev Emitted when the allowance of a `spender` for an `owner` is set by\n     * a call to {approve}. `value` is the new allowance.\n     */\n    event Approval(address indexed owner, address indexed spender, uint256 value);\n\n    /**\n     * @dev Returns the value of tokens in existence.\n     */\n    function totalSupply() external view returns (uint256);\n\n    /**\n     * @dev Returns the value of tokens owned by `account`.\n     */\n    function balanceOf(address account) external view returns (uint256);\n\n    /**\n     * @dev Moves a `value` amount of tokens from the caller's account to `to`.\n     *\n     * Returns a boolean value indicating whether the operation succeeded.\n     *\n     * Emits a {Transfer} event.\n     */\n    function transfer(address to, uint256 value) external returns (bool);\n\n    /**\n     * @dev Returns the remaining number of tokens that `spender` will be\n     * allowed to spend on behalf of `owner` through {transferFrom}. This is\n     * zero by default.\n     *\n     * This value changes when {approve} or {transferFrom} are called.\n     */\n    function allowance(address owner, address spender) external view returns (uint256);\n\n    /**\n     * @dev Sets a `value` amount of tokens as the allowance of `spender` over the\n     * caller's tokens.\n     *\n     * Returns a boolean value indicating whether the operation succeeded.\n     *\n     * IMPORTANT: Beware that changing an allowance with this method brings the risk\n     * that someone may use both the old and the new allowance by unfortunate\n     * transaction ordering. One possible solution to mitigate this race\n     * condition is to first reduce the spender's allowance to 0 and set the\n     * desired value afterwards:\n     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\n     *\n     * Emits an {Approval} event.\n     */\n    function approve(address spender, uint256 value) external returns (bool);\n\n    /**\n     * @dev Moves a `value` amount of tokens from `from` to `to` using the\n     * allowance mechanism. `value` is then deducted from the caller's\n     * allowance.\n     *\n     * Returns a boolean value indicating whether the operation succeeded.\n     *\n     * Emits a {Transfer} event.\n     */\n    function transferFrom(address from, address to, uint256 value) external returns (bool);\n}\n"
    },
    "@openzeppelin/contracts/utils/Context.sol": {
      "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)\n\npragma solidity ^0.8.20;\n\n/**\n * @dev Provides information about the current execution context, including the\n * sender of the transaction and its data. While these are generally available\n * via msg.sender and msg.data, they should not be accessed in such a direct\n * manner, since when dealing with meta-transactions the account sending and\n * paying for execution may not be the actual sender (as far as an application\n * is concerned).\n *\n * This contract is only required for intermediate, library-like contracts.\n */\nabstract contract Context {\n    function _msgSender() internal view virtual returns (address) {\n        return msg.sender;\n    }\n\n    function _msgData() internal view virtual returns (bytes calldata) {\n        return msg.data;\n    }\n\n    function _contextSuffixLength() internal view virtual returns (uint256) {\n        return 0;\n    }\n}\n"
    },
    "@openzeppelin/contracts/utils/introspection/ERC165.sol": {
      "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/ERC165.sol)\n\npragma solidity ^0.8.20;\n\nimport {IERC165} from \"./IERC165.sol\";\n\n/**\n * @dev Implementation of the {IERC165} interface.\n *\n * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check\n * for the additional interface id that will be supported. For example:\n *\n * ```solidity\n * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {\n *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);\n * }\n * ```\n */\nabstract contract ERC165 is IERC165 {\n    /**\n     * @dev See {IERC165-supportsInterface}.\n     */\n    function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {\n        return interfaceId == type(IERC165).interfaceId;\n    }\n}\n"
    },
    "@openzeppelin/contracts/utils/introspection/IERC165.sol": {
      "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/IERC165.sol)\n\npragma solidity ^0.8.20;\n\n/**\n * @dev Interface of the ERC165 standard, as defined in the\n * https://eips.ethereum.org/EIPS/eip-165[EIP].\n *\n * Implementers can declare support of contract interfaces, which can then be\n * queried by others ({ERC165Checker}).\n *\n * For an implementation, see {ERC165}.\n */\ninterface IERC165 {\n    /**\n     * @dev Returns true if this contract implements the interface defined by\n     * `interfaceId`. See the corresponding\n     * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]\n     * to learn more about how these ids are created.\n     *\n     * This function call must use less than 30 000 gas.\n     */\n    function supportsInterface(bytes4 interfaceId) external view returns (bool);\n}\n"
    },
    "contracts/interfaces/ICertificateVerifier.sol": {
      "content": "// SPDX-License-Identifier: MIT\npragma solidity 0.8.24;\n\ninterface ICertificateVerifier {\n    struct Signature {\n        bytes32 r;\n        bytes32 s;\n        uint8 v;\n    }\n\n    struct EMPCertificate {\n        string identifier;\n        string name;\n        string marktfunktion;\n        string lieferant;\n        string bilanzkreis;\n        address owner;\n    }\n\n    struct CPOCertificate {\n        string identifier;\n        string name;\n        address owner;\n    }\n\n    struct OtherCertificate {\n        string identifier;\n        string name;\n        address owner;\n    }\n\n    function verifyEMP(\n        bytes memory certificateData,\n        bytes memory signature\n    ) external view returns (address, EMPCertificate memory, Signature memory);\n\n    function verifyCPO(\n        bytes memory certificateData,\n        bytes memory signature\n    ) external view returns (address, CPOCertificate memory, Signature memory);\n\n    function verifyOther(\n        bytes memory certificateData,\n        bytes memory signature\n    ) external view returns (address, OtherCertificate memory, Signature memory);\n}\n"
    },
    "contracts/interfaces/IProviderOracle.sol": {
      "content": "// SPDX-License-Identifier: MIT\npragma solidity 0.8.24;\n\ninterface IProviderOracle {\n    struct Provider {\n        string name;\n        string identifier;\n    }\n\n    function getProvider(\n        string memory identifier\n    ) external view returns (Provider memory);\n\n    function addProvider(Provider memory provider) external;\n}\n"
    },
    "contracts/IOcnCvManager.sol": {
      "content": "// SPDX-License-Identifier: GPL-3.0\npragma solidity 0.8.24;\n\ninterface IOcnCvManager {\n    /* ********************************** */\n    /*               ENUMS                */\n    /* ********************************** */\n\n    enum CvStatus { NOT_VERIFIED, APPROVED, FAILED }\n\n}\n"
    },
    "contracts/IOcnPaymentManager.sol": {
      "content": "// SPDX-License-Identifier: GPL-3.0\npragma solidity 0.8.24;\n\nimport {IERC20} from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\ninterface IOcnPaymentManager {\n    /* ********************************** */\n    /*               ENUMS                */\n    /* ********************************** */\n\n    enum PaymentStatus { PENDING, PAYMENT_UP_TO_DATE, INSUFFICIENT_FUNDS, INACTIVE }\n\n    /* ********************************** */\n    /*               EVENTS               */\n    /* ********************************** */\n\n    event OwnershipTransferred(address indexed oldAdmin, address indexed  newAdmin);\n    event PartyStaked(address stakedBy, address party, uint256 amount);\n    event StakeWithdrawn(address indexed party, uint256 amount);\n\n    /* ********************************** */\n    /*            FUNCTIONS               */\n    /* ********************************** */\n\n    function initialize(address _euroStablecoin, uint256 _fundingYearlyAmount, address _operator) external;\n    function pay(address party) external;\n    function withdrawToRegistryOperator(address party) external;\n    function getPaymentStatus(address party) external view returns (PaymentStatus);\n    function getPaymentBlock(address party) external view returns (uint256, uint256);\n\n    /* ********************************** */\n    /*       STORAGE VARIABLES            */\n    /* ********************************** */\n\n    function euroStablecoin() external view returns (IERC20);\n    function stakedFunds(address party) external view returns (uint256);\n    function stakingBlock(address party) external view returns (uint256);\n\n    /* ********************************** */\n    /*  ERRORS                            */\n    /* ********************************** */\n\n    error StakeAlreadyDeposited();\n    error InsufficientAllowance();\n    error TransferFailed();\n    error NoFundsStaked();\n    error WithdrawalNotAllowed();\n}\n"
    },
    "contracts/OcnRegistry.sol": {
      "content": "// SPDX-License-Identifier: GPL-3.0\npragma solidity 0.8.24;\n\nimport {IOcnPaymentManager} from \"./IOcnPaymentManager.sol\";\nimport {IOcnCvManager} from \"./IOcnCvManager.sol\";\nimport \"./interfaces/ICertificateVerifier.sol\";\nimport \"./interfaces/IProviderOracle.sol\";\n\nimport {AccessControl} from \"@openzeppelin/contracts/access/AccessControl.sol\";\n\ncontract OcnRegistry is AccessControl {\n    /* ********************************** */\n    /*       STORAGE VARIABLES            */\n    /* ********************************** */\n\n    string private prefix;\n\n    // OCN Node Operator Listings\n    mapping(address => string) private nodeOf;\n    mapping(string => bool) private uniqueDomains;\n    mapping(address => bool) private uniqueOperators;\n    address[] private operators;\n\n    // OCPI Party Listings\n    enum Role {\n        CPO,\n        EMSP,\n        NAP,\n        NSP,\n        OTHER,\n        SCSP\n    }\n\n    struct RoleDetails {\n        bytes certificateData;\n        bytes signature;\n        Role role;\n    }\n\n    struct PartyDetails {\n        bytes2 countryCode;\n        bytes3 partyId;\n        Role[] roles;\n        string name;\n        string url;\n        IOcnPaymentManager.PaymentStatus paymentStatus;\n        IOcnCvManager.CvStatus cvStatus;\n        bool active;\n        uint256 partyIndex;\n    }\n\n    mapping(bytes2 => mapping(bytes3 => address)) private uniqueParties;\n    mapping(address => bool) private uniquePartyAddresses;\n    mapping(address => PartyDetails) private partyOf;\n    mapping(address => address) private operatorOf;\n    mapping(address => bool) private allowedCertificateVerifiers;\n    address[] private parties;\n\n    IOcnPaymentManager public paymentManager;\n    ICertificateVerifier public certificateVerifier;\n\n    // Providers Oracle\n    mapping(Role => IProviderOracle) private roleOracle;\n\n    /* ********************************** */\n    /*          CUSTOM ERRORS             */\n    /* ********************************** */\n    error EmptyDomainName(string reason);\n    error DomainNameAlreadyRegistered(string reason);\n    error EmptyCountryCode(string reason);\n    error EmptyPartyId(string reason);\n    error NoRolesProvided(string reason);\n    error EmptyOperator(string reason);\n    error PartyAlreadyRegistered(string reason);\n    error PartyNotRegistered(string reason);\n    error SignerMismatch(string reason);\n    error InvalidCertificate(address verifier, string reason);\n    error ProviderNotFound(Role role, string reason);\n    error CerificateOwnerMismatch(string reason);\n\n    /* ********************************** */\n    /*               EVENTS               */\n    /* ********************************** */\n\n    event OwnershipTransferred(address indexed oldAdmin, address indexed newAdmin);\n    event OperatorUpdate(address indexed operator, string domain);\n    event PartyUpdate(bytes2 countryCode, bytes3 partyId, address indexed partyAddress, Role[] roles, string name, string url, IOcnPaymentManager.PaymentStatus paymentStatus, IOcnCvManager.CvStatus cvStatus, bool active, address indexed operatorAddress);\n\n    event PartyDelete(bytes2 countryCode, bytes3 partyId, address indexed partyAddress, Role[] roles, string name, string url, IOcnPaymentManager.PaymentStatus paymentStatus, IOcnCvManager.CvStatus cvStatus, bool active);\n\n    /* ********************************** */\n    /*          INITIALIZER               */\n    /* ********************************** */\n\n    constructor(address _paymentManager, address _certificateVerifier) {\n        prefix = \"\\u0019Ethereum Signed Message:\\n32\";\n        paymentManager = IOcnPaymentManager(_paymentManager);\n        certificateVerifier = ICertificateVerifier(_certificateVerifier);\n        _grantRole(DEFAULT_ADMIN_ROLE, msg.sender);\n    }\n\n    function transferOwnership(address newOwner) public onlyRole(DEFAULT_ADMIN_ROLE) {\n        grantRole(DEFAULT_ADMIN_ROLE, newOwner);\n        revokeRole(DEFAULT_ADMIN_ROLE, msg.sender);\n\n        emit OwnershipTransferred(msg.sender, newOwner);\n    }\n\n    function adminDeleteOperator(address operator) public onlyRole(DEFAULT_ADMIN_ROLE) {\n        deleteNode(operator);\n    }\n\n    function adminDeleteParty(bytes2 countryCode, bytes3 partyId) public onlyRole(DEFAULT_ADMIN_ROLE) {\n        address party = uniqueParties[countryCode][partyId];\n        deleteParty(party);\n    }\n\n    function setNode(address operator, string memory domain) private {\n        if (bytes(domain).length == 0) {\n            revert EmptyDomainName(\"Cannot set empty domain name. Use deleteNode method instead.\");\n        }\n        if (uniqueDomains[domain]) {\n            revert DomainNameAlreadyRegistered(\"Domain name already registered.\");\n        }\n        uniqueDomains[domain] = true;\n\n        if (!uniqueOperators[operator]) {\n            operators.push(operator);\n        }\n\n        uniqueOperators[operator] = true;\n        nodeOf[operator] = domain;\n        emit OperatorUpdate(operator, domain);\n    }\n\n    function setNode(string memory domain) public {\n        setNode(msg.sender, domain);\n    }\n\n    function setNodeRaw(address operator, string memory domain, uint8 v, bytes32 r, bytes32 s) public {\n        bytes32 paramHash = keccak256(abi.encodePacked(operator, domain));\n        address signer = ecrecover(keccak256(abi.encodePacked(prefix, paramHash)), v, r, s);\n        setNode(signer, domain);\n    }\n\n    function deleteNode(address operator) private {\n        string memory domain = nodeOf[operator];\n        if (bytes(domain).length == 0) {\n            revert EmptyDomainName(\"Cannot delete node that does not exist.\");\n        }\n        uniqueDomains[domain] = false;\n        delete nodeOf[operator];\n        emit OperatorUpdate(operator, \"\");\n    }\n\n    function deleteNode() public {\n        deleteNode(msg.sender);\n    }\n\n    function deleteNodeRaw(address operator, uint8 v, bytes32 r, bytes32 s) public {\n        bytes32 paramHash = keccak256(abi.encodePacked(operator));\n        address signer = ecrecover(keccak256(abi.encodePacked(prefix, paramHash)), v, r, s);\n        deleteNode(signer);\n    }\n\n    function getNode(address operator) public view returns (string memory) {\n        return nodeOf[operator];\n    }\n\n    function getNodeOperators() public view returns (address[] memory) {\n        return operators;\n    }\n\n    function setParty(bytes2 countryCode, bytes3 partyId, RoleDetails[] memory roles, address operator, string memory name, string memory url) public {\n        if (countryCode == bytes2(0)) {\n            revert EmptyCountryCode(\"Cannot set empty country_code. Use deleteParty method instead.\");\n        }\n        if (partyId == bytes3(0)) {\n            revert EmptyPartyId(\"Cannot set empty party_id. Use deleteParty method instead.\");\n        }\n        if (roles.length == 0) {\n            revert NoRolesProvided(\"No roles provided.\");\n        }\n        if (operator == address(0)) {\n            revert EmptyOperator(\"Cannot set empty operator. Use deleteParty method instead.\");\n        }\n\n        Role[] memory verifiedRoles = new Role[](roles.length);\n        address credentialOwner = address(0);\n\n        // VC verification (All roles must be verified)\n        for (uint8 i = 0; i < roles.length; i++) {\n            RoleDetails memory roleDetails = roles[i];\n            (string memory certificateIdentifier, address owner) = verifyCertificate(roleDetails);\n\n            if (credentialOwner == address(0)) {\n                credentialOwner = owner;\n            }\n\n            if (credentialOwner != owner) {\n                revert CerificateOwnerMismatch(\"Certificates have different owners\");\n            }\n\n            IProviderOracle oracle = roleOracle[roleDetails.role];\n            if (address(oracle) != address(0)) {\n                IProviderOracle.Provider memory provider = oracle.getProvider(certificateIdentifier);\n                if (!compareIdentifiers(certificateIdentifier, provider.identifier)) {\n                    revert ProviderNotFound(roleDetails.role, \"Not active in oracle\");\n                }\n            }\n\n            verifiedRoles[i] = roleDetails.role;\n        }\n\n        address registeredParty = uniqueParties[countryCode][partyId];\n        if (registeredParty != address(0) && registeredParty != credentialOwner) {\n            revert PartyAlreadyRegistered(\"Party with country_code/party_id already registered under different address.\");\n        }\n        uniqueParties[countryCode][partyId] = credentialOwner;\n        if (bytes(nodeOf[operator]).length == 0) {\n            revert PartyNotRegistered(\"Provided operator not registered.\");\n        }\n\n        uint256 partyIndex = partyOf[credentialOwner].partyIndex;\n        if (!uniquePartyAddresses[credentialOwner]) {\n            parties.push(credentialOwner);\n            // get last index of the array\n            partyIndex = parties.length - 1;\n        }\n\n        uniquePartyAddresses[credentialOwner] = true;\n\n        IOcnPaymentManager.PaymentStatus paymentStatus = paymentManager.getPaymentStatus(credentialOwner);\n        partyOf[credentialOwner] = PartyDetails(countryCode, partyId, verifiedRoles, name, url, paymentStatus, IOcnCvManager.CvStatus.NOT_VERIFIED, true, partyIndex);\n        operatorOf[credentialOwner] = operator;\n\n        PartyDetails memory details = partyOf[credentialOwner];\n        emit PartyUpdate(details.countryCode, details.partyId, credentialOwner, details.roles, details.name, details.url, details.paymentStatus, details.cvStatus, details.active, operator);\n    }\n\n    function setPartyRaw(address party, bytes2 countryCode, bytes3 partyId, RoleDetails[] memory roles, address operator, string memory name, string memory url, uint8 v, bytes32 r, bytes32 s) public {\n        bytes memory rolesBytes = \"\";\n        for (uint8 i = 0; i < roles.length; i++) {\n            RoleDetails memory roleDetails = roles[i];\n            rolesBytes = abi.encodePacked(rolesBytes, roleDetails.certificateData, roleDetails.signature, roleDetails.role);\n        }\n        bytes32 rolesHash = keccak256(rolesBytes);\n        bytes32 paramHash = keccak256(abi.encodePacked(party, countryCode, partyId, rolesHash, operator, name, url));\n        address signer = ecrecover(keccak256(abi.encodePacked(prefix, paramHash)), v, r, s);\n        if (signer != party) {\n            revert SignerMismatch(\"Signer and provided party address different.\");\n        }\n        setParty(countryCode, partyId, roles, operator, name, url);\n    }\n\n    function deleteParty(address party) private {\n        if (operatorOf[party] == address(0)) {\n            revert PartyNotRegistered(\"Cannot delete party that does not exist. No operator found for given party.\");\n        }\n        delete operatorOf[party];\n        PartyDetails memory details = partyOf[party];\n        delete uniqueParties[details.countryCode][details.partyId];\n        delete partyOf[party];\n        delete parties[details.partyIndex];\n        uniquePartyAddresses[party] = false;\n\n        emit PartyDelete(details.countryCode, details.partyId, party, details.roles, details.name, details.url, details.paymentStatus, details.cvStatus, details.active);\n    }\n\n    function deleteParty() public {\n        deleteParty(msg.sender);\n    }\n\n    function deletePartyRaw(address party, uint8 v, bytes32 r, bytes32 s) public {\n        bytes32 paramHash = keccak256(abi.encodePacked(party));\n        address signer = ecrecover(keccak256(abi.encodePacked(prefix, paramHash)), v, r, s);\n        if (signer != party) {\n            revert SignerMismatch(\"Signer and provided party address different.\");\n        }\n        deleteParty(signer);\n    }\n\n    function getOperatorByAddress(address party) public view returns (address operator, string memory domain) {\n        operator = operatorOf[party];\n        domain = nodeOf[operator];\n    }\n\n    function getOperatorByOcpi(bytes2 countryCode, bytes3 partyId) public view returns (address operator, string memory domain) {\n        address party = uniqueParties[countryCode][partyId];\n        operator = operatorOf[party];\n        domain = nodeOf[operator];\n    }\n\n    function getPartyDetailsByAddress(address _partyAddress) public view returns (address partyAddress, bytes2 countryCode, bytes3 partyId, Role[] memory roles, IOcnPaymentManager.PaymentStatus paymentStatus, address operatorAddress, string memory name, string memory url, bool active) {\n        PartyDetails storage details = partyOf[_partyAddress];\n        partyAddress = _partyAddress;\n        countryCode = details.countryCode;\n        partyId = details.partyId;\n        roles = details.roles;\n        paymentStatus = details.paymentStatus;\n        operatorAddress = operatorOf[_partyAddress];\n        name = details.name;\n        url = details.url;\n        active = details.active;\n    }\n\n    function getPartyDetailsByOcpi(bytes2 _countryCode, bytes3 _partyId) public view returns (address partyAddress, bytes2 countryCode, bytes3 partyId, Role[] memory roles, IOcnPaymentManager.PaymentStatus paymentStatus, address operatorAddress, string memory name, string memory url, bool active, uint256 stakingBlock) {\n        partyAddress = uniqueParties[_countryCode][_partyId];\n        (stakingBlock, ) = paymentManager.getPaymentBlock(partyAddress);\n        PartyDetails storage details = partyOf[partyAddress];\n        countryCode = details.countryCode;\n        partyId = details.partyId;\n        roles = details.roles;\n        paymentStatus = paymentManager.getPaymentStatus(partyAddress);\n        operatorAddress = operatorOf[partyAddress];\n        name = details.name;\n        url = details.url;\n        active = details.active;\n    }\n\n    function getPartiesCount() public view returns (uint256) {\n        return parties.length;\n    }\n\n    function getParties() public view returns (address[] memory) {\n        return parties;\n    }\n\n    function getPartiesByOperator(address operator) public view returns (address[] memory) {\n        address[] memory filteredParties = new address[](parties.length);\n        uint32 count = 0;\n        for (uint32 i = 0; i < parties.length; i++) {\n            if (operatorOf[parties[i]] == operator) {\n                filteredParties[count] = parties[i];\n                count++;\n            }\n        }\n        address[] memory result = new address[](count);\n        for (uint32 i = 0; i < count; i++) {\n            result[i] = filteredParties[i];\n        }\n        return result;\n    }\n\n    function getPartiesByRole(Role role) public view returns (address[] memory) {\n        address[] memory filteredParties = new address[](parties.length);\n        uint32 count = 0;\n        for (uint32 i = 0; i < parties.length; i++) {\n            PartyDetails memory details = partyOf[parties[i]];\n            for (uint32 j = 0; j < details.roles.length; j++) {\n                if (details.roles[j] == role) {\n                    filteredParties[count] = parties[i];\n                    count++;\n                    break;\n                }\n            }\n        }\n        address[] memory result = new address[](count);\n        for (uint32 i = 0; i < count; i++) {\n            result[i] = filteredParties[i];\n        }\n        return result;\n    }\n\n    function setVerifier(address verifier) public onlyRole(DEFAULT_ADMIN_ROLE) {\n        require(verifier != address(0), \"Invalid verifier address\");\n        require(!allowedCertificateVerifiers[verifier], \"Verifier already allowed\");\n\n        allowedCertificateVerifiers[verifier] = true;\n    }\n\n    function removeVerifier(address verifier) public onlyRole(DEFAULT_ADMIN_ROLE) {\n        require(verifier != address(0), \"Invalid verifier address\");\n        require(allowedCertificateVerifiers[verifier], \"Verifier not currently allowed\");\n\n        allowedCertificateVerifiers[verifier] = false;\n    }\n\n    function isAllowedVerifier(address verifier) public view returns (bool) {\n        return allowedCertificateVerifiers[verifier];\n    }\n\n    function setProviderOracle(Role role, address oracleAddress) public onlyRole(DEFAULT_ADMIN_ROLE) {\n        roleOracle[role] = IProviderOracle(oracleAddress);\n    }\n\n    function verifyCertificate(RoleDetails memory roleDetails) private view returns (string memory, address) {\n        if (roleDetails.role == Role.EMSP) {\n            (address verifier, ICertificateVerifier.EMPCertificate memory certificate, ) = certificateVerifier.verifyEMP(roleDetails.certificateData, roleDetails.signature);\n            if (!isAllowedVerifier(verifier)) {\n                revert InvalidCertificate(verifier, \"Invalid EMP certificate\");\n            }\n            return (certificate.identifier, certificate.owner);\n        } else if (roleDetails.role == Role.CPO) {\n            (address verifier, ICertificateVerifier.CPOCertificate memory certificate, ) = certificateVerifier.verifyCPO(roleDetails.certificateData, roleDetails.signature);\n            if (!isAllowedVerifier(verifier)) {\n                revert InvalidCertificate(verifier, \"Invalid CPO certificate\");\n            }\n            return (certificate.identifier, certificate.owner);\n        } else {\n            (address verifier, ICertificateVerifier.OtherCertificate memory certificate, ) = certificateVerifier.verifyOther(roleDetails.certificateData, roleDetails.signature);\n            if (!isAllowedVerifier(verifier)) {\n                revert InvalidCertificate(verifier, \"Invalid Other certificate\");\n            }\n            return (certificate.identifier, certificate.owner);\n        }\n    }\n\n    function compareIdentifiers(string memory a, string memory b) private pure returns (bool) {\n        return keccak256(abi.encodePacked(a)) == keccak256(abi.encodePacked(b));\n    }\n}\n"
    }
  },
  "settings": {
    "optimizer": {
      "enabled": true,
      "runs": 200
    },
    "evmVersion": "paris",
    "outputSelection": {
      "*": {
        "*": [
          "abi",
          "evm.bytecode",
          "evm.deployedBytecode",
          "evm.methodIdentifiers",
          "metadata",
          "storageLayout",
          "devdoc",
          "userdoc",
          "evm.gasEstimates"
        ],
        "": [
          "ast"
        ]
      }
    },
    "metadata": {
      "useLiteralContent": true
    }
  }
}