ETH Price: $2,509.17 (-0.80%)

Transaction Decoder

Block:
21994255 at Mar-07-2025 09:58:11 AM +UTC
Transaction Fee:
0.001269971762888391 ETH $3.19
Gas Used:
415,169 Gas / 3.058927239 Gwei

Emitted Events:

68 GnosisSafeProxy.0x141df868a6331af528e38c83b7aa03edc19be66e37ae67f9285bf4f8e3c6a1a8( 0x141df868a6331af528e38c83b7aa03edc19be66e37ae67f9285bf4f8e3c6a1a8, 0x000000000000000000000000a6b71e26c5e0845f74c812102ca7114b6a896ab2, 0000000000000000000000000000000000000000000000000000000000000080, 0000000000000000000000000000000000000000000000000000000000000001, 0000000000000000000000000000000000000000000000000000000000000000, 000000000000000000000000f48f2b2d2a534e402487b3ee7c18c33aec0fe5e4, 0000000000000000000000000000000000000000000000000000000000000001, 000000000000000000000000398030c56cd8c046a573c1c1fd22574bf837b5ab )
69 GnosisSafeProxyFactory.ProxyCreation( proxy=GnosisSafeProxy, singleton=GnosisSafe )
70 ServiceRegistry.CreateMultisigWithAgents( serviceId=44, multisig=GnosisSafeProxy )
71 ServiceRegistry.DeployService( serviceId=44 )
72 ServiceManagerToken.CreateMultisig( multisig=GnosisSafeProxy )

Account State Difference:

  Address   Before After State Difference Code
0x353c5165...E72aFc15b
0.003092011384433071 Eth
Nonce: 9
0.00182203962154468 Eth
Nonce: 10
0.001269971762888391
(Titan Builder)
13.71116928248895757 Eth13.71220720498895757 Eth0.0010379225
0x48b6af7B...f54e775cA
0x692b53aF...B68F3AC86
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 2428443625105081582559204222041278247441700609795002665090055939283027467997641458691785997361476925183291499207336642763346590243745492380720635885219447537792976952551989114034525862470838559734956624223602344484460523390253307270174272155235234202969395821539804621800291071303890788251503181826836738595692199058491516848780521398593510200695595558485912431339701413565763187183244164254117324631184440164403
0xa6B71E26...b6a896AB2
(Safe: Proxy Factory 1.3.0 Default)

Execution Trace

ServiceManagerToken.deploy( serviceId=44, multisigImplementation=0x46C0D07F55d4F9B5Eed2Fc9680B5953e5fd7b461, data=0x0000000000000000000000000000000000000000F48F2B2D2A534E402487B3EE7C18C33AEC0FE5E40000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000067CAC2EC ) => ( multisig=0x692b53aF59428c396F4Dfd831edA603B68F3AC86 )
  • ServiceRegistry.deploy( serviceOwner=0x353c516564CA891B5db47693a3796CCE72aFc15b, serviceId=44, multisigImplementation=0x46C0D07F55d4F9B5Eed2Fc9680B5953e5fd7b461, data=0x0000000000000000000000000000000000000000F48F2B2D2A534E402487B3EE7C18C33AEC0FE5E40000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000067CAC2EC ) => ( multisig=0x692b53aF59428c396F4Dfd831edA603B68F3AC86 )
    • GnosisSafeMultisig.create( owners=[0x398030C56cD8C046a573c1C1FD22574BF837B5ab], threshold=1, data=0x0000000000000000000000000000000000000000F48F2B2D2A534E402487B3EE7C18C33AEC0FE5E40000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000067CAC2EC ) => ( multisig=0x692b53aF59428c396F4Dfd831edA603B68F3AC86 )
      • GnosisSafeProxyFactory.createProxyWithNonce( _singleton=0xd9Db270c1B5E3Bd161E8c8503c55cEABeE709552, initializer=0xB63E800D0000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000140000000000000000000000000F48F2B2D2A534E402487B3EE7C18C33AEC0FE5E40000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000398030C56CD8C046A573C1C1FD22574BF837B5AB0000000000000000000000000000000000000000000000000000000000000000, saltNonce=1741341420 ) => ( proxy=0x692b53aF59428c396F4Dfd831edA603B68F3AC86 )
        • GnosisSafeProxy.60806040( )
        • GnosisSafeProxy.b63e800d( )
          • GnosisSafe.setup( _owners=[0x398030C56cD8C046a573c1C1FD22574BF837B5ab], _threshold=1, to=0x0000000000000000000000000000000000000000, data=0x, fallbackHandler=0xf48f2B2d2a534e402487b3ee7C18c33Aec0Fe5e4, paymentToken=0x0000000000000000000000000000000000000000, payment=0, paymentReceiver=0x0000000000000000000000000000000000000000 )
          • AgentRegistry.calculateSubComponents( unitIds=[49] ) => ( subComponentIds=[1] )
            File 1 of 7: ServiceManagerToken
            // SPDX-License-Identifier: MIT
            pragma solidity ^0.8.15;
            import "./interfaces/IErrorsRegistries.sol";
            /// @title Generic Manager - Smart contract for generic registry manager template
            /// @author Aleksandr Kuperman - <[email protected]>
            abstract contract GenericManager is IErrorsRegistries {
                event OwnerUpdated(address indexed owner);
                event Pause(address indexed owner);
                event Unpause(address indexed owner);
                // Owner address
                address public owner;
                // Pause switch
                bool public paused;
                /// @dev Changes the owner address.
                /// @param newOwner Address of a new owner.
                function changeOwner(address newOwner) external virtual {
                    // Check for the ownership
                    if (msg.sender != owner) {
                        revert OwnerOnly(msg.sender, owner);
                    }
                    // Check for the zero address
                    if (newOwner == address(0)) {
                        revert ZeroAddress();
                    }
                    owner = newOwner;
                    emit OwnerUpdated(newOwner);
                }
                /// @dev Pauses the contract.
                function pause() external virtual {
                    // Check for the ownership
                    if (msg.sender != owner) {
                        revert OwnerOnly(msg.sender, owner);
                    }
                    paused = true;
                    emit Pause(msg.sender);
                }
                /// @dev Unpauses the contract.
                function unpause() external virtual {
                    // Check for the ownership
                    if (msg.sender != owner) {
                        revert OwnerOnly(msg.sender, owner);
                    }
                    paused = false;
                    emit Unpause(msg.sender);
                }
            }
            // SPDX-License-Identifier: MIT
            pragma solidity ^0.8.15;
            /// @dev Errors.
            interface IErrorsRegistries {
                /// @dev Only `manager` has a privilege, but the `sender` was provided.
                /// @param sender Sender address.
                /// @param manager Required sender address as a manager.
                error ManagerOnly(address sender, address manager);
                /// @dev Only `owner` has a privilege, but the `sender` was provided.
                /// @param sender Sender address.
                /// @param owner Required sender address as an owner.
                error OwnerOnly(address sender, address owner);
                /// @dev Hash already exists in the records.
                error HashExists();
                /// @dev Provided zero address.
                error ZeroAddress();
                /// @dev Agent Id is not correctly provided for the current routine.
                /// @param agentId Component Id.
                error WrongAgentId(uint256 agentId);
                /// @dev Wrong length of two arrays.
                /// @param numValues1 Number of values in a first array.
                /// @param numValues2 Numberf of values in a second array.
                error WrongArrayLength(uint256 numValues1, uint256 numValues2);
                /// @dev Canonical agent Id is not found.
                /// @param agentId Canonical agent Id.
                error AgentNotFound(uint256 agentId);
                /// @dev Component Id is not found.
                /// @param componentId Component Id.
                error ComponentNotFound(uint256 componentId);
                /// @dev Multisig threshold is out of bounds.
                /// @param currentThreshold Current threshold value.
                /// @param minThreshold Minimum possible threshold value.
                /// @param maxThreshold Maximum possible threshold value.
                error WrongThreshold(uint256 currentThreshold, uint256 minThreshold, uint256 maxThreshold);
                /// @dev Agent instance is already registered with a specified `operator`.
                /// @param operator Operator that registered an instance.
                error AgentInstanceRegistered(address operator);
                /// @dev Wrong operator is specified when interacting with a specified `serviceId`.
                /// @param serviceId Service Id.
                error WrongOperator(uint256 serviceId);
                /// @dev Operator has no registered instances in the service.
                /// @param operator Operator address.
                /// @param serviceId Service Id.
                error OperatorHasNoInstances(address operator, uint256 serviceId);
                /// @dev Canonical `agentId` is not found as a part of `serviceId`.
                /// @param agentId Canonical agent Id.
                /// @param serviceId Service Id.
                error AgentNotInService(uint256 agentId, uint256 serviceId);
                /// @dev The contract is paused.
                error Paused();
                /// @dev Zero value when it has to be different from zero.
                error ZeroValue();
                /// @dev Value overflow.
                /// @param provided Overflow value.
                /// @param max Maximum possible value.
                error Overflow(uint256 provided, uint256 max);
                /// @dev Service must be inactive.
                /// @param serviceId Service Id.
                error ServiceMustBeInactive(uint256 serviceId);
                /// @dev All the agent instance slots for a specific `serviceId` are filled.
                /// @param serviceId Service Id.
                error AgentInstancesSlotsFilled(uint256 serviceId);
                /// @dev Wrong state of a service.
                /// @param state Service state.
                /// @param serviceId Service Id.
                error WrongServiceState(uint256 state, uint256 serviceId);
                /// @dev Only own service multisig is allowed.
                /// @param provided Provided address.
                /// @param expected Expected multisig address.
                /// @param serviceId Service Id.
                error OnlyOwnServiceMultisig(address provided, address expected, uint256 serviceId);
                /// @dev Multisig is not whitelisted.
                /// @param multisig Address of a multisig implementation.
                error UnauthorizedMultisig(address multisig);
                /// @dev Incorrect deposit provided for the registration activation.
                /// @param sent Sent amount.
                /// @param expected Expected amount.
                /// @param serviceId Service Id.
                error IncorrectRegistrationDepositValue(uint256 sent, uint256 expected, uint256 serviceId);
                /// @dev Insufficient value provided for the agent instance bonding.
                /// @param sent Sent amount.
                /// @param expected Expected amount.
                /// @param serviceId Service Id.
                error IncorrectAgentBondingValue(uint256 sent, uint256 expected, uint256 serviceId);
                /// @dev Failure of a transfer.
                /// @param token Address of a token.
                /// @param from Address `from`.
                /// @param to Address `to`.
                /// @param value Value.
                error TransferFailed(address token, address from, address to, uint256 value);
                /// @dev Caught reentrancy violation.
                error ReentrancyGuard();
            }
            // SPDX-License-Identifier: MIT
            pragma solidity ^0.8.15;
            /// @dev Required interface for the service manipulation.
            interface IService{
                struct AgentParams {
                    // Number of agent instances
                    uint32 slots;
                    // Bond per agent instance
                    uint96 bond;
                }
                /// @dev Creates a new service.
                /// @param serviceOwner Individual that creates and controls a service.
                /// @param configHash IPFS hash pointing to the config metadata.
                /// @param agentIds Canonical agent Ids in a sorted ascending order.
                /// @param agentParams Number of agent instances and required bond to register an instance in the service.
                /// @param threshold Signers threshold for a multisig composed by agent instances.
                /// @return serviceId Created service Id.
                function create(
                    address serviceOwner,
                    bytes32 configHash,
                    uint32[] memory agentIds,
                    AgentParams[] memory agentParams,
                    uint32 threshold
                ) external returns (uint256 serviceId);
                /// @dev Updates a service in a CRUD way.
                /// @param serviceOwner Individual that creates and controls a service.
                /// @param configHash IPFS hash pointing to the config metadata.
                /// @param agentIds Canonical agent Ids in a sorted ascending order.
                /// @param agentParams Number of agent instances and required bond to register an instance in the service.
                /// @param threshold Signers threshold for a multisig composed by agent instances.
                /// @param serviceId Service Id to be updated.
                /// @return success True, if function executed successfully.
                function update(
                    address serviceOwner,
                    bytes32 configHash,
                    uint32[] memory agentIds,
                    AgentParams[] memory agentParams,
                    uint32 threshold,
                    uint256 serviceId
                ) external returns (bool success);
                /// @dev Activates the service.
                /// @param serviceOwner Individual that creates and controls a service.
                /// @param serviceId Correspondent service Id.
                /// @return success True, if function executed successfully.
                function activateRegistration(address serviceOwner, uint256 serviceId) external payable returns (bool success);
                /// @dev Registers agent instances.
                /// @param operator Address of the operator.
                /// @param serviceId Service Id to be updated.
                /// @param agentInstances Agent instance addresses.
                /// @param agentIds Canonical Ids of the agent correspondent to the agent instance.
                /// @return success True, if function executed successfully.
                function registerAgents(
                    address operator,
                    uint256 serviceId,
                    address[] memory agentInstances,
                    uint32[] memory agentIds
                ) external payable returns (bool success);
                /// @dev Creates multisig instance controlled by the set of service agent instances and deploys the service.
                /// @param serviceOwner Individual that creates and controls a service.
                /// @param serviceId Correspondent service Id.
                /// @param multisigImplementation Multisig implementation address.
                /// @param data Data payload for the multisig creation.
                /// @return multisig Address of the created multisig.
                function deploy(
                    address serviceOwner,
                    uint256 serviceId,
                    address multisigImplementation,
                    bytes memory data
                ) external returns (address multisig);
                /// @dev Terminates the service.
                /// @param serviceOwner Owner of the service.
                /// @param serviceId Service Id to be updated.
                /// @return success True, if function executed successfully.
                /// @return refund Refund to return to the serviceOwner.
                function terminate(address serviceOwner, uint256 serviceId) external returns (bool success, uint256 refund);
                /// @dev Unbonds agent instances of the operator from the service.
                /// @param operator Operator of agent instances.
                /// @param serviceId Service Id.
                /// @return success True, if function executed successfully.
                /// @return refund The amount of refund returned to the operator.
                function unbond(address operator, uint256 serviceId) external returns (bool success, uint256 refund);
            }
            // SPDX-License-Identifier: MIT
            pragma solidity ^0.8.19;
            /// @dev Interface for the service registration token utility manipulation.
            interface IServiceTokenUtility {
                /// @dev Creates a record with the token-related information for the specified service.
                /// @param serviceId Service Id.
                /// @param token Token address.
                /// @param agentIds Set of agent Ids.
                /// @param bonds Set of correspondent bonds.
                function createWithToken(
                    uint256 serviceId,
                    address token,
                    uint32[] memory agentIds,
                    uint256[] memory bonds
                ) external;
                /// @dev Resets a record with token and security deposit data.
                /// @param serviceId Service Id.
                function resetServiceToken(uint256 serviceId) external;
                /// @dev Deposit a token security deposit for the service registration after its activation.
                /// @param serviceId Service Id.
                /// @return isTokenSecured True if the service Id is token secured, false if ETH secured otherwise.
                function activateRegistrationTokenDeposit(uint256 serviceId) external returns (bool isTokenSecured);
                /// @dev Deposits bonded tokens from the operator during the agent instance registration.
                /// @param operator Operator address.
                /// @param serviceId Service Id.
                /// @param agentIds Set of agent Ids for corresponding agent instances opertor is registering.
                /// @return isTokenSecured True if the service Id is token secured, false if ETH secured otherwise.
                function registerAgentsTokenDeposit(
                    address operator,
                    uint256 serviceId,
                    uint32[] memory agentIds
                ) external returns (bool isTokenSecured);
                /// @dev Withdraws a token security deposit to the service owner after the service termination.
                /// @param serviceId Service Id.
                /// @return securityRefund Returned token security deposit, or zero if the service is ETH-secured.
                function terminateTokenRefund(uint256 serviceId) external returns (uint256 securityRefund);
                /// @dev Withdraws bonded tokens to the operator during the unbond phase.
                /// @param operator Operator address.
                /// @param serviceId Service Id.
                /// @return refund Returned bonded token amount, or zero if the service is ETH-secured.
                function unbondTokenRefund(address operator, uint256 serviceId) external returns (uint256 refund);
                /// @dev Gets service token secured status.
                /// @param serviceId Service Id.
                /// @return True if the service Id is token secured.
                function isTokenSecuredService(uint256 serviceId) external view returns (bool);
            }
            // SPDX-License-Identifier: MIT
            pragma solidity ^0.8.19;
            import {GenericManager} from "./GenericManager.sol";
            import {OperatorSignedHashes} from "./utils/OperatorSignedHashes.sol";
            import "./interfaces/IService.sol";
            import "./interfaces/IServiceTokenUtility.sol";
            // Operator whitelist interface
            interface IOperatorWhitelist {
                /// @dev Gets operator whitelisting status.
                /// @param serviceId Service Id.
                /// @param operator Operator address.
                /// @return status Whitelisting status.
                function isOperatorWhitelisted(uint256 serviceId, address operator) external view returns (bool status);
            }
            // Generic token interface
            interface IToken {
                /// @dev Gets the owner of the token Id.
                /// @param tokenId Token Id.
                /// @return Token Id owner address.
                function ownerOf(uint256 tokenId) external view returns (address);
            }
            /// @title Service Manager - Periphery smart contract for managing services with custom ERC20 tokens or ETH
            /// @author Aleksandr Kuperman - <[email protected]>
            /// @author AL
            contract ServiceManagerToken is GenericManager, OperatorSignedHashes {
                event OperatorWhitelistUpdated(address indexed operatorWhitelist);
                event CreateMultisig(address indexed multisig);
                // Service Registry address
                address public immutable serviceRegistry;
                // Service Registry Token Utility address
                address public immutable serviceRegistryTokenUtility;
                // A well-known representation of ETH as an address
                address public constant ETH_TOKEN_ADDRESS = address(0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE);
                // Bond wrapping constant
                uint96 public constant BOND_WRAPPER = 1;
                // Operator whitelist address
                address public operatorWhitelist;
                /// @dev ServiceRegistryTokenUtility constructor.
                /// @param _serviceRegistry Service Registry contract address.
                /// @param _serviceRegistryTokenUtility Service Registry Token Utility contract address.
                constructor(address _serviceRegistry, address _serviceRegistryTokenUtility, address _operatorWhitelist)
                    OperatorSignedHashes("Service Manager Token", "1.1.1")
                {
                    // Check for the Service Registry related contract zero addresses
                    if (_serviceRegistry == address(0) || _serviceRegistryTokenUtility == address(0)) {
                        revert ZeroAddress();
                    }
                    serviceRegistry = _serviceRegistry;
                    serviceRegistryTokenUtility = _serviceRegistryTokenUtility;
                    operatorWhitelist = _operatorWhitelist;
                    owner = msg.sender;
                }
                /// @dev Sets the operator whitelist contract address.
                /// @param newOperatorWhitelist New operator whitelist contract address.
                function setOperatorWhitelist(address newOperatorWhitelist) external {
                    // Check for the contract ownership
                    if (msg.sender != owner) {
                        revert OwnerOnly(msg.sender, owner);
                    }
                    operatorWhitelist = newOperatorWhitelist;
                    emit OperatorWhitelistUpdated(newOperatorWhitelist);
                }
                /// @dev Creates a new service.
                /// @param serviceOwner Individual that creates and controls a service.
                /// @param token ERC20 token address for the security deposit, or ETH.
                /// @param configHash IPFS hash pointing to the config metadata.
                /// @param agentIds Canonical agent Ids.
                /// @param agentParams Number of agent instances and required bond to register an instance in the service.
                /// @param threshold Threshold for a multisig composed by agents.
                /// @return serviceId Created service Id.
                function create(
                    address serviceOwner,
                    address token,
                    bytes32 configHash,
                    uint32[] memory agentIds,
                    IService.AgentParams[] memory agentParams,
                    uint32 threshold
                ) external returns (uint256 serviceId)
                {
                    // Check if the minting is paused
                    if (paused) {
                        revert Paused();
                    }
                    // Check for the zero address
                    if (token == address(0)) {
                        revert ZeroAddress();
                    }
                    // Check for the custom ERC20 token or ETH based bond
                    if (token == ETH_TOKEN_ADDRESS) {
                        // Call the original ServiceRegistry contract function
                        serviceId = IService(serviceRegistry).create(serviceOwner, configHash, agentIds, agentParams, threshold);
                    } else {
                        // Wrap agent params with just 1 WEI bond going to the original ServiceRegistry contract,
                        // and actual token bonds being recorded with the ServiceRegistryTokenUtility contract
                        uint256 numAgents = agentParams.length;
                        uint256[] memory bonds = new uint256[](numAgents);
                        for (uint256 i = 0; i < numAgents; ++i) {
                            // Check for the zero bond value
                            if (agentParams[i].bond == 0) {
                                revert ZeroValue();
                            }
                            // Copy actual bond values for each agent Id
                            bonds[i] = agentParams[i].bond;
                            // Wrap bonds with the BOND_WRAPPER value for the original ServiceRegistry contract
                            agentParams[i].bond = BOND_WRAPPER;
                        }
                        // Call the original ServiceRegistry contract function
                        serviceId = IService(serviceRegistry).create(serviceOwner, configHash, agentIds, agentParams, threshold);
                        // Create a token-related record for the service
                        IServiceTokenUtility(serviceRegistryTokenUtility).createWithToken(serviceId, token, agentIds, bonds);
                    }
                }
                /// @dev Updates a service in a CRUD way.
                /// @param token ERC20 token address for the security deposit, or ETH.
                /// @param configHash IPFS hash pointing to the config metadata.
                /// @param agentIds Canonical agent Ids.
                /// @param agentParams Number of agent instances and required bond to register an instance in the service.
                /// @param threshold Threshold for a multisig composed by agents.
                /// @param serviceId Service Id to be updated.
                /// @return success True, if function executed successfully.
                function update(
                    address token,
                    bytes32 configHash,
                    uint32[] memory agentIds,
                    IService.AgentParams[] memory agentParams,
                    uint32 threshold,
                    uint256 serviceId
                ) external returns (bool success)
                {
                    // Check for the zero address
                    if (token == address(0)) {
                        revert ZeroAddress();
                    }
                    uint256 numAgents = agentParams.length;
                    if (token == ETH_TOKEN_ADDRESS) {
                        // If any of the slots is a non-zero, the correspondent bond cannot be zero
                        for (uint256 i = 0; i < numAgents; ++i) {
                            // Check for the zero bond value
                            if (agentParams[i].slots > 0 && agentParams[i].bond == 0) {
                                    revert ZeroValue();
                            }
                        }
                        // Call the original ServiceRegistry contract function
                        success = IService(serviceRegistry).update(msg.sender, configHash, agentIds, agentParams, threshold, serviceId);
                        // Reset the service token-based data
                        // This function still needs to be called as the previous token could be a custom ERC20 token
                        IServiceTokenUtility(serviceRegistryTokenUtility).resetServiceToken(serviceId);
                    } else {
                        // Wrap agent params with just 1 WEI bond going to the original ServiceRegistry contract,
                        // and actual token bonds being recorded with the ServiceRegistryTokenUtility contract
                        uint256[] memory bonds = new uint256[](numAgents);
                        for (uint256 i = 0; i < numAgents; ++i) {
                            // Copy actual bond values for each agent Id that has at least one slot in the updated service
                            if (agentParams[i].slots > 0) {
                                // Check for the zero bond value
                                if (agentParams[i].bond == 0) {
                                    revert ZeroValue();
                                }
                                bonds[i] = agentParams[i].bond;
                                // Wrap bonds with the BOND_WRAPPER value for the original ServiceRegistry contract
                                agentParams[i].bond = BOND_WRAPPER;
                            }
                        }
                        // Call the original ServiceRegistry contract function
                        success = IService(serviceRegistry).update(msg.sender, configHash, agentIds, agentParams, threshold, serviceId);
                        // Update relevant data in the ServiceRegistryTokenUtility contract
                        // We follow the optimistic design where existing bonds are just overwritten without a clearing
                        // bond values of agent Ids that are not going to be used in the service. This is coming from the fact
                        // that all the checks are done on the original ServiceRegistry side
                        IServiceTokenUtility(serviceRegistryTokenUtility).createWithToken(serviceId, token, agentIds, bonds);
                    }
                }
                /// @dev Activates the service and its sensitive components.
                /// @param serviceId Correspondent service Id.
                /// @return success True, if function executed successfully.
                function activateRegistration(uint256 serviceId) external payable returns (bool success) {
                    // Record the actual ERC20 security deposit
                    bool isTokenSecured = IServiceTokenUtility(serviceRegistryTokenUtility).activateRegistrationTokenDeposit(serviceId);
                    // Activate registration in the original ServiceRegistry contract
                    if (isTokenSecured) {
                        // If the service Id is based on the ERC20 token, the provided value to the standard registration is 1
                        success = IService(serviceRegistry).activateRegistration{value: BOND_WRAPPER}(msg.sender, serviceId);
                    } else {
                        // Otherwise follow the standard msg.value path
                        success = IService(serviceRegistry).activateRegistration{value: msg.value}(msg.sender, serviceId);
                    }
                }
                /// @dev Registers agent instances.
                /// @param serviceId Service Id to be updated.
                /// @param agentInstances Agent instance addresses.
                /// @param agentIds Canonical Ids of the agent correspondent to the agent instance.
                /// @return success True, if function executed successfully.
                function registerAgents(
                    uint256 serviceId,
                    address[] memory agentInstances,
                    uint32[] memory agentIds
                ) external payable returns (bool success) {
                    if (operatorWhitelist != address(0)) {
                        // Check if the operator is whitelisted
                        if (!IOperatorWhitelist(operatorWhitelist).isOperatorWhitelisted(serviceId, msg.sender)) {
                            revert WrongOperator(serviceId);
                        }
                    }
                    // Record the actual ERC20 bond
                    bool isTokenSecured = IServiceTokenUtility(serviceRegistryTokenUtility).registerAgentsTokenDeposit(msg.sender,
                        serviceId, agentIds);
                    // Register agent instances in a main ServiceRegistry contract
                    if (isTokenSecured) {
                        // If the service Id is based on the ERC20 token, the provided value to the standard registration is 1
                        // multiplied by the number of agent instances
                        success = IService(serviceRegistry).registerAgents{value: agentInstances.length * BOND_WRAPPER}(msg.sender,
                            serviceId, agentInstances, agentIds);
                    } else {
                        // Otherwise follow the standard msg.value path
                        success = IService(serviceRegistry).registerAgents{value: msg.value}(msg.sender, serviceId, agentInstances, agentIds);
                    }
                }
                /// @dev Creates multisig instance controlled by the set of service agent instances and deploys the service.
                /// @param serviceId Correspondent service Id.
                /// @param multisigImplementation Multisig implementation address.
                /// @param data Data payload for the multisig creation.
                /// @return multisig Address of the created multisig.
                function deploy(
                    uint256 serviceId,
                    address multisigImplementation,
                    bytes memory data
                ) external returns (address multisig)
                {
                    multisig = IService(serviceRegistry).deploy(msg.sender, serviceId, multisigImplementation, data);
                    emit CreateMultisig(multisig);
                }
                /// @dev Terminates the service.
                /// @param serviceId Service Id.
                /// @return success True, if function executed successfully.
                /// @return refund Refund for the service owner.
                function terminate(uint256 serviceId) external returns (bool success, uint256 refund) {
                    // Withdraw the ERC20 token if the service is token-based
                    uint256 tokenRefund = IServiceTokenUtility(serviceRegistryTokenUtility).terminateTokenRefund(serviceId);
                    // Terminate the service with the regular service registry routine
                    (success, refund) = IService(serviceRegistry).terminate(msg.sender, serviceId);
                    // If the service is token-based, the actual refund is provided via the serviceRegistryTokenUtility contract
                    if (tokenRefund > 0) {
                        refund = tokenRefund;
                    }
                }
                /// @dev Unbonds agent instances of the operator from the service.
                /// @param serviceId Service Id.
                /// @return success True, if function executed successfully.
                /// @return refund The amount of refund returned to the operator.
                function unbond(uint256 serviceId) external returns (bool success, uint256 refund) {
                    // Withdraw the ERC20 token if the service is token-based
                    uint256 tokenRefund = IServiceTokenUtility(serviceRegistryTokenUtility).unbondTokenRefund(msg.sender, serviceId);
                    // Unbond with the regular service registry routine
                    (success, refund) = IService(serviceRegistry).unbond(msg.sender, serviceId);
                    // If the service is token-based, the actual refund is provided via the serviceRegistryTokenUtility contract
                    if (tokenRefund > 0) {
                        refund = tokenRefund;
                    }
                }
                /// @dev Unbonds agent instances of the operator by the service owner via the operator's pre-signed message hash.
                /// @notice Note that this function accounts for the operator being the EOA, or the contract that has an
                ///         isValidSignature() function that would confirm the message hash was signed by the operator contract.
                ///         Otherwise, if the message hash has been pre-approved, the corresponding map of hashes is going to
                ///         to verify the signed hash, similar to the Safe contract implementation in v1.3.0:
                ///         https://github.com/safe-global/safe-contracts/blob/186a21a74b327f17fc41217a927dea7064f74604/contracts/GnosisSafe.sol#L240-L304
                ///         Also note that only the service owner is able to call this function on behalf of the operator.
                /// @param operator Operator address that signed the unbond message hash.
                /// @param serviceId Service Id.
                /// @param signature Signature byte array associated with operator message hash signature.
                /// @return success True, if the function executed successfully.
                /// @return refund The amount of refund returned to the operator.
                function unbondWithSignature(
                    address operator,
                    uint256 serviceId,
                    bytes memory signature
                ) external returns (bool success, uint256 refund)
                {
                    // Check the service owner
                    address serviceOwner = IToken(serviceRegistry).ownerOf(serviceId);
                    if (msg.sender != serviceOwner) {
                        revert OwnerOnly(msg.sender, serviceOwner);
                    }
                    // Get the (operator | serviceId) nonce for the unbond message
                    // Push a pair of key defining variables into one key. Service Id or operator are not enough by themselves
                    // as another service might use the operator address at the same time frame
                    // operator occupies first 160 bits
                    uint256 operatorService = uint256(uint160(operator));
                    // serviceId occupies next 32 bits
                    operatorService |= serviceId << 160;
                    uint256 nonce = mapOperatorUnbondNonces[operatorService];
                    // Get the unbond message hash
                    bytes32 msgHash = getUnbondHash(operator, serviceOwner, serviceId, nonce);
                    // Verify the signed hash against the operator address
                    _verifySignedHash(operator, msgHash, signature);
                    // Update corresponding nonce value
                    nonce++;
                    mapOperatorUnbondNonces[operatorService] = nonce;
                    // Withdraw the ERC20 token if the service is token-based
                    uint256 tokenRefund = IServiceTokenUtility(serviceRegistryTokenUtility).unbondTokenRefund(operator, serviceId);
                    // Unbond with the regular service registry routine
                    (success, refund) = IService(serviceRegistry).unbond(operator, serviceId);
                    // If the service is token-based, the actual refund is provided via the serviceRegistryTokenUtility contract
                    if (tokenRefund > 0) {
                        refund = tokenRefund;
                    }
                }
                /// @dev Registers agent instances of the operator by the service owner via the operator's pre-signed message hash.
                /// @notice Note that this function accounts for the operator being the EOA, or the contract that has an
                ///         isValidSignature() function that would confirm the message hash was signed by the operator contract.
                ///         Otherwise, if the message hash has been pre-approved, the corresponding map of hashes is going to
                ///         to verify the signed hash, similar to the Safe contract implementation in v1.3.0:
                ///         https://github.com/safe-global/safe-contracts/blob/186a21a74b327f17fc41217a927dea7064f74604/contracts/GnosisSafe.sol#L240-L304
                ///         Also note that only the service owner is able to call this function on behalf of the operator.
                /// @param operator Operator address that signed the register agents message hash.
                /// @param serviceId Service Id.
                /// @param agentInstances Agent instance addresses.
                /// @param agentIds Canonical Ids of the agent correspondent to the agent instance.
                /// @param signature Signature byte array associated with operator message hash signature.
                /// @return success True, if the the function executed successfully.
                function registerAgentsWithSignature(
                    address operator,
                    uint256 serviceId,
                    address[] memory agentInstances,
                    uint32[] memory agentIds,
                    bytes memory signature
                ) external payable returns (bool success) {
                    // Check the service owner
                    address serviceOwner = IToken(serviceRegistry).ownerOf(serviceId);
                    if (msg.sender != serviceOwner) {
                        revert OwnerOnly(msg.sender, serviceOwner);
                    }
                    // Get the (operator | serviceId) nonce for the registerAgents message
                    // Push a pair of key defining variables into one key. Service Id or operator are not enough by themselves
                    // as another service might use the operator address at the same time frame
                    // operator occupies first 160 bits
                    uint256 operatorService = uint256(uint160(operator));
                    // serviceId occupies next 32 bits as serviceId is limited by the 2^32 - 1 value
                    operatorService |= serviceId << 160;
                    uint256 nonce = mapOperatorRegisterAgentsNonces[operatorService];
                    // Get register agents message hash
                    bytes32 msgHash = getRegisterAgentsHash(operator, serviceOwner, serviceId, agentInstances, agentIds, nonce);
                    // Verify the signed hash against the operator address
                    _verifySignedHash(operator, msgHash, signature);
                    // Update corresponding nonce value
                    nonce++;
                    mapOperatorRegisterAgentsNonces[operatorService] = nonce;
                    // Record the actual ERC20 bond
                    bool isTokenSecured = IServiceTokenUtility(serviceRegistryTokenUtility).registerAgentsTokenDeposit(operator,
                        serviceId, agentIds);
                    // Register agent instances in a main ServiceRegistry contract
                    if (isTokenSecured) {
                        // If the service Id is based on the ERC20 token, the provided value to the standard registration is 1
                        // multiplied by the number of agent instances
                        success = IService(serviceRegistry).registerAgents{value: agentInstances.length * BOND_WRAPPER}(operator,
                            serviceId, agentInstances, agentIds);
                    } else {
                        // Otherwise follow the standard msg.value path
                        success = IService(serviceRegistry).registerAgents{value: msg.value}(operator, serviceId, agentInstances, agentIds);
                    }
                }
            }
            // SPDX-License-Identifier: MIT
            pragma solidity ^0.8.19;
            interface ISignatureValidator {
                /// @dev Should return whether the signature provided is valid for the provided hash.
                /// @notice MUST return the bytes4 magic value 0x1626ba7e when function passes.
                ///         MUST NOT modify state (using STATICCALL for solc < 0.5, view modifier for solc > 0.5).
                ///         MUST allow external calls.
                /// @param hash Hash of the data to be signed.
                /// @param signature Signature byte array associated with hash.
                /// @return magicValue bytes4 magic value.
                function isValidSignature(bytes32 hash, bytes memory signature) external view returns (bytes4 magicValue);
            }
            /// @dev Provided zero address.
            error ZeroOperatorAddress();
            /// @dev Incorrect signature length provided.
            /// @param signature Signature bytes.
            /// @param provided Provided signature length.
            /// @param expected Expected signature length.
            error IncorrectSignatureLength(bytes signature, uint256 provided, uint256 expected);
            /// @dev Hash is not validated.
            /// @param operator Operator contract address.
            /// @param msgHash Message hash.
            /// @param signature Signature bytes associated with the message hash.
            error HashNotValidated(address operator, bytes32 msgHash, bytes signature);
            /// @dev Hash is not approved.
            /// @param operator Operator address.
            /// @param msgHash Message hash.
            /// @param signature Signature bytes associated with the message hash.
            error HashNotApproved(address operator, bytes32 msgHash, bytes signature);
            /// @dev Obtained wrong operator address.
            /// @param provided Provided address.
            /// @param expected Expected address.
            error WrongOperatorAddress(address provided, address expected);
            /// @title OperatorSignedHashes - Smart contract for managing operator signed hashes
            /// @author AL
            /// @author Aleksandr Kuperman - <[email protected]>
            contract OperatorSignedHashes {
                event OperatorHashApproved(address indexed operator, bytes32 hash);
                // Value for the contract signature validation: bytes4(keccak256("isValidSignature(bytes32,bytes)")
                bytes4 constant internal MAGIC_VALUE = 0x1626ba7e;
                // Domain separator type hash
                bytes32 public constant DOMAIN_SEPARATOR_TYPE_HASH =
                    keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)");
                // Unbond type hash
                bytes32 public constant UNBOND_TYPE_HASH =
                    keccak256("Unbond(address operator,address serviceOwner,uint256 serviceId,uint256 nonce)");
                // Register agents type hash
                bytes32 public constant REGISTER_AGENTS_TYPE_HASH =
                    keccak256("RegisterAgents(address operator,address serviceOwner,uint256 serviceId,bytes32 agentsData,uint256 nonce)");
                // Original domain separator value
                bytes32 public immutable domainSeparator;
                // Original chain Id
                uint256 public immutable chainId;
                // Name hash
                bytes32 public immutable nameHash;
                // Version hash
                bytes32 public immutable versionHash;
                // Name of a signing domain
                string public name;
                // Version of a signing domain
                string public version;
                // Map of operator address and serviceId => unbond nonce
                mapping(uint256 => uint256) public mapOperatorUnbondNonces;
                // Map of operator address and serviceId => register agents nonce
                mapping(uint256 => uint256) public mapOperatorRegisterAgentsNonces;
                // Mapping operator address => approved hashes status
                mapping(address => mapping(bytes32 => bool)) public mapOperatorApprovedHashes;
                /// @dev Contract constructor.
                /// @param _name Name of a signing domain.
                /// @param _version Version of a signing domain.
                constructor(string memory _name, string memory _version) {
                    name = _name;
                    version = _version;
                    nameHash = keccak256(bytes(_name));
                    versionHash = keccak256(bytes(_version));
                    chainId = block.chainid;
                    domainSeparator = _computeDomainSeparator();
                }
                /// @dev Verifies provided message hash against its signature.
                /// @param operator Operator address.
                /// @param msgHash Message hash.
                /// @param signature Signature bytes associated with the signed message hash.
                function _verifySignedHash(address operator, bytes32 msgHash, bytes memory signature) internal view {
                    // Check for the operator zero address
                    if (operator == address(0)) {
                        revert ZeroOperatorAddress();
                    }
                    // Check for the signature length
                    if (signature.length != 65) {
                        revert IncorrectSignatureLength(signature, signature.length, 65);
                    }
                    // Decode the signature
                    uint8 v = uint8(signature[64]);
                    // For the correct ecrecover() function execution, the v value must be set to {0,1} + 27
                    // Although v in a very rare case can be equal to {2,3} (with a probability of 3.73e-37%)
                    // If v is set to just 0 or 1 when signing  by the EOA, it is most likely signed by the ledger and must be adjusted
                    if (v < 4 && operator.code.length == 0) {
                        // In case of a non-contract, adjust v to follow the standard ecrecover case
                        v += 27;
                    }
                    bytes32 r;
                    bytes32 s;
                    // solhint-disable-next-line no-inline-assembly
                    assembly {
                        r := mload(add(signature, 32))
                        s := mload(add(signature, 64))
                    }
                    address recOperator;
                    // Go through signature cases based on the value of v
                    if (v == 4) {
                        // Contract signature case, where the address of the contract is encoded into r
                        recOperator = address(uint160(uint256(r)));
                        // Check for the signature validity in the contract
                        if (ISignatureValidator(recOperator).isValidSignature(msgHash, signature) != MAGIC_VALUE) {
                            revert HashNotValidated(recOperator, msgHash, signature);
                        }
                    } else if (v == 5) {
                        // Case of an approved hash, where the address of the operator is encoded into r
                        recOperator = address(uint160(uint256(r)));
                        // Hashes have been pre-approved by the operator via a separate message, see operatorApproveHash() function
                        if (!mapOperatorApprovedHashes[recOperator][msgHash]) {
                            revert HashNotApproved(recOperator, msgHash, signature);
                        }
                    } else {
                        // Case of ecrecover with the message hash for EOA signatures
                        recOperator = ecrecover(msgHash, v, r, s);
                    }
                    // Final check is for the operator address itself
                    if (recOperator != operator) {
                        revert WrongOperatorAddress(recOperator, operator);
                    }
                }
                /// @dev Approves message hash for the operator address.
                /// @param hash Provided message hash to approve.
                function operatorApproveHash(bytes32 hash) external {
                    mapOperatorApprovedHashes[msg.sender][hash] = true;
                    emit OperatorHashApproved(msg.sender, hash);
                }
                /// @dev Computes domain separator hash.
                /// @return Hash of the domain separator based on its name, version, chain Id and contract address.
                function _computeDomainSeparator() internal view returns (bytes32) {
                    return keccak256(
                        abi.encode(
                            DOMAIN_SEPARATOR_TYPE_HASH,
                            nameHash,
                            versionHash,
                            block.chainid,
                            address(this)
                        )
                    );
                }
                /// @dev Gets the already computed domain separator of recomputes one if the chain Id is different.
                /// @return Original or recomputed domain separator.
                function getDomainSeparator() public view returns (bytes32) {
                    return block.chainid == chainId ? domainSeparator : _computeDomainSeparator();
                }
                /// @dev Gets the unbond message hash for the operator.
                /// @param operator Operator address.
                /// @param serviceOwner Service owner address.
                /// @param serviceId Service Id.
                /// @param nonce Nonce for the unbond message from the pair of (operator | service Id).
                /// @return Computed message hash.
                function getUnbondHash(
                    address operator,
                    address serviceOwner,
                    uint256 serviceId,
                    uint256 nonce
                ) public view returns (bytes32)
                {
                    return keccak256(
                        abi.encodePacked(
                            "\\x19\\x01",
                            getDomainSeparator(),
                            keccak256(
                                abi.encode(
                                    UNBOND_TYPE_HASH,
                                    operator,
                                    serviceOwner,
                                    serviceId,
                                    nonce
                                )
                            )
                        )
                    );
                }
                /// @dev Gets the register agents message hash for the operator.
                /// @param operator Operator address.
                /// @param serviceOwner Service owner address.
                /// @param serviceId Service Id.
                /// @param agentInstances Agent instance addresses operator is going to register.
                /// @param agentIds Agent Ids corresponding to each agent instance address.
                /// @param nonce Nonce for the register agents message from the pair of (operator | service Id).
                /// @return Computed message hash.
                function getRegisterAgentsHash(
                    address operator,
                    address serviceOwner,
                    uint256 serviceId,
                    address[] memory agentInstances,
                    uint32[] memory agentIds,
                    uint256 nonce
                ) public view returns (bytes32)
                {
                    return keccak256(
                        abi.encodePacked(
                            "\\x19\\x01",
                            getDomainSeparator(),
                            keccak256(
                                abi.encode(
                                    REGISTER_AGENTS_TYPE_HASH,
                                    operator,
                                    serviceOwner,
                                    serviceId,
                                    keccak256(abi.encode(agentInstances, agentIds)),
                                    nonce
                                )
                            )
                        )
                    );
                }
                /// @dev Checks if the hash provided by the operator is approved.
                /// @param operator Operator address.
                /// @param hash Message hash.
                /// @return True, if the hash provided by the operator is approved.
                function isOperatorHashApproved(address operator, bytes32 hash) external view returns (bool) {
                    return mapOperatorApprovedHashes[operator][hash];
                }
                /// @dev Gets the (operator | service Id) nonce for the unbond message data.
                /// @param operator Operator address.
                /// @param serviceId Service Id.
                /// @return nonce Obtained nonce.
                function getOperatorUnbondNonce(address operator, uint256 serviceId) external view returns (uint256 nonce) {
                    // operator occupies first 160 bits
                    uint256 operatorService = uint256(uint160(operator));
                    // serviceId occupies next 32 bits as serviceId is limited by the 2^32 - 1 value
                    operatorService |= serviceId << 160;
                    nonce = mapOperatorUnbondNonces[operatorService];
                }
                /// @dev Gets the (operator | service Id) nonce for the register agents message data.
                /// @param operator Operator address.
                /// @param serviceId Service Id.
                /// @return nonce Obtained nonce.
                function getOperatorRegisterAgentsNonce(address operator, uint256 serviceId) external view returns (uint256 nonce) {
                    // operator occupies first 160 bits
                    uint256 operatorService = uint256(uint160(operator));
                    // serviceId occupies next 32 bits as serviceId is limited by the 2^32 - 1 value
                    operatorService |= serviceId << 160;
                    nonce = mapOperatorRegisterAgentsNonces[operatorService];
                }
            }
            

            File 2 of 7: GnosisSafeProxy
            // SPDX-License-Identifier: LGPL-3.0-only
            pragma solidity >=0.7.0 <0.9.0;
            
            /// @title IProxy - Helper interface to access masterCopy of the Proxy on-chain
            /// @author Richard Meissner - <[email protected]>
            interface IProxy {
                function masterCopy() external view returns (address);
            }
            
            /// @title GnosisSafeProxy - Generic proxy contract allows to execute all transactions applying the code of a master contract.
            /// @author Stefan George - <[email protected]>
            /// @author Richard Meissner - <[email protected]>
            contract GnosisSafeProxy {
                // singleton always needs to be first declared variable, to ensure that it is at the same location in the contracts to which calls are delegated.
                // To reduce deployment costs this variable is internal and needs to be retrieved via `getStorageAt`
                address internal singleton;
            
                /// @dev Constructor function sets address of singleton contract.
                /// @param _singleton Singleton address.
                constructor(address _singleton) {
                    require(_singleton != address(0), "Invalid singleton address provided");
                    singleton = _singleton;
                }
            
                /// @dev Fallback function forwards all transactions and returns all received return data.
                fallback() external payable {
                    // solhint-disable-next-line no-inline-assembly
                    assembly {
                        let _singleton := and(sload(0), 0xffffffffffffffffffffffffffffffffffffffff)
                        // 0xa619486e == keccak("masterCopy()"). The value is right padded to 32-bytes with 0s
                        if eq(calldataload(0), 0xa619486e00000000000000000000000000000000000000000000000000000000) {
                            mstore(0, _singleton)
                            return(0, 0x20)
                        }
                        calldatacopy(0, 0, calldatasize())
                        let success := delegatecall(gas(), _singleton, 0, calldatasize(), 0, 0)
                        returndatacopy(0, 0, returndatasize())
                        if eq(success, 0) {
                            revert(0, returndatasize())
                        }
                        return(0, returndatasize())
                    }
                }
            }
            
            /// @title Proxy Factory - Allows to create new proxy contact and execute a message call to the new proxy within one transaction.
            /// @author Stefan George - <[email protected]>
            contract GnosisSafeProxyFactory {
                event ProxyCreation(GnosisSafeProxy proxy, address singleton);
            
                /// @dev Allows to create new proxy contact and execute a message call to the new proxy within one transaction.
                /// @param singleton Address of singleton contract.
                /// @param data Payload for message call sent to new proxy contract.
                function createProxy(address singleton, bytes memory data) public returns (GnosisSafeProxy proxy) {
                    proxy = new GnosisSafeProxy(singleton);
                    if (data.length > 0)
                        // solhint-disable-next-line no-inline-assembly
                        assembly {
                            if eq(call(gas(), proxy, 0, add(data, 0x20), mload(data), 0, 0), 0) {
                                revert(0, 0)
                            }
                        }
                    emit ProxyCreation(proxy, singleton);
                }
            
                /// @dev Allows to retrieve the runtime code of a deployed Proxy. This can be used to check that the expected Proxy was deployed.
                function proxyRuntimeCode() public pure returns (bytes memory) {
                    return type(GnosisSafeProxy).runtimeCode;
                }
            
                /// @dev Allows to retrieve the creation code used for the Proxy deployment. With this it is easily possible to calculate predicted address.
                function proxyCreationCode() public pure returns (bytes memory) {
                    return type(GnosisSafeProxy).creationCode;
                }
            
                /// @dev Allows to create new proxy contact using CREATE2 but it doesn't run the initializer.
                ///      This method is only meant as an utility to be called from other methods
                /// @param _singleton Address of singleton contract.
                /// @param initializer Payload for message call sent to new proxy contract.
                /// @param saltNonce Nonce that will be used to generate the salt to calculate the address of the new proxy contract.
                function deployProxyWithNonce(
                    address _singleton,
                    bytes memory initializer,
                    uint256 saltNonce
                ) internal returns (GnosisSafeProxy proxy) {
                    // If the initializer changes the proxy address should change too. Hashing the initializer data is cheaper than just concatinating it
                    bytes32 salt = keccak256(abi.encodePacked(keccak256(initializer), saltNonce));
                    bytes memory deploymentData = abi.encodePacked(type(GnosisSafeProxy).creationCode, uint256(uint160(_singleton)));
                    // solhint-disable-next-line no-inline-assembly
                    assembly {
                        proxy := create2(0x0, add(0x20, deploymentData), mload(deploymentData), salt)
                    }
                    require(address(proxy) != address(0), "Create2 call failed");
                }
            
                /// @dev Allows to create new proxy contact and execute a message call to the new proxy within one transaction.
                /// @param _singleton Address of singleton contract.
                /// @param initializer Payload for message call sent to new proxy contract.
                /// @param saltNonce Nonce that will be used to generate the salt to calculate the address of the new proxy contract.
                function createProxyWithNonce(
                    address _singleton,
                    bytes memory initializer,
                    uint256 saltNonce
                ) public returns (GnosisSafeProxy proxy) {
                    proxy = deployProxyWithNonce(_singleton, initializer, saltNonce);
                    if (initializer.length > 0)
                        // solhint-disable-next-line no-inline-assembly
                        assembly {
                            if eq(call(gas(), proxy, 0, add(initializer, 0x20), mload(initializer), 0, 0), 0) {
                                revert(0, 0)
                            }
                        }
                    emit ProxyCreation(proxy, _singleton);
                }
            
                /// @dev Allows to create new proxy contact, execute a message call to the new proxy and call a specified callback within one transaction
                /// @param _singleton Address of singleton contract.
                /// @param initializer Payload for message call sent to new proxy contract.
                /// @param saltNonce Nonce that will be used to generate the salt to calculate the address of the new proxy contract.
                /// @param callback Callback that will be invoced after the new proxy contract has been successfully deployed and initialized.
                function createProxyWithCallback(
                    address _singleton,
                    bytes memory initializer,
                    uint256 saltNonce,
                    IProxyCreationCallback callback
                ) public returns (GnosisSafeProxy proxy) {
                    uint256 saltNonceWithCallback = uint256(keccak256(abi.encodePacked(saltNonce, callback)));
                    proxy = createProxyWithNonce(_singleton, initializer, saltNonceWithCallback);
                    if (address(callback) != address(0)) callback.proxyCreated(proxy, _singleton, initializer, saltNonce);
                }
            
                /// @dev Allows to get the address for a new proxy contact created via `createProxyWithNonce`
                ///      This method is only meant for address calculation purpose when you use an initializer that would revert,
                ///      therefore the response is returned with a revert. When calling this method set `from` to the address of the proxy factory.
                /// @param _singleton Address of singleton contract.
                /// @param initializer Payload for message call sent to new proxy contract.
                /// @param saltNonce Nonce that will be used to generate the salt to calculate the address of the new proxy contract.
                function calculateCreateProxyWithNonceAddress(
                    address _singleton,
                    bytes calldata initializer,
                    uint256 saltNonce
                ) external returns (GnosisSafeProxy proxy) {
                    proxy = deployProxyWithNonce(_singleton, initializer, saltNonce);
                    revert(string(abi.encodePacked(proxy)));
                }
            }
            
            interface IProxyCreationCallback {
                function proxyCreated(
                    GnosisSafeProxy proxy,
                    address _singleton,
                    bytes calldata initializer,
                    uint256 saltNonce
                ) external;
            }

            File 3 of 7: GnosisSafeProxyFactory
            // SPDX-License-Identifier: LGPL-3.0-only
            pragma solidity >=0.7.0 <0.9.0;
            /// @title IProxy - Helper interface to access masterCopy of the Proxy on-chain
            /// @author Richard Meissner - <[email protected]>
            interface IProxy {
                function masterCopy() external view returns (address);
            }
            /// @title GnosisSafeProxy - Generic proxy contract allows to execute all transactions applying the code of a master contract.
            /// @author Stefan George - <[email protected]>
            /// @author Richard Meissner - <[email protected]>
            contract GnosisSafeProxy {
                // singleton always needs to be first declared variable, to ensure that it is at the same location in the contracts to which calls are delegated.
                // To reduce deployment costs this variable is internal and needs to be retrieved via `getStorageAt`
                address internal singleton;
                /// @dev Constructor function sets address of singleton contract.
                /// @param _singleton Singleton address.
                constructor(address _singleton) {
                    require(_singleton != address(0), "Invalid singleton address provided");
                    singleton = _singleton;
                }
                /// @dev Fallback function forwards all transactions and returns all received return data.
                fallback() external payable {
                    // solhint-disable-next-line no-inline-assembly
                    assembly {
                        let _singleton := and(sload(0), 0xffffffffffffffffffffffffffffffffffffffff)
                        // 0xa619486e == keccak("masterCopy()"). The value is right padded to 32-bytes with 0s
                        if eq(calldataload(0), 0xa619486e00000000000000000000000000000000000000000000000000000000) {
                            mstore(0, _singleton)
                            return(0, 0x20)
                        }
                        calldatacopy(0, 0, calldatasize())
                        let success := delegatecall(gas(), _singleton, 0, calldatasize(), 0, 0)
                        returndatacopy(0, 0, returndatasize())
                        if eq(success, 0) {
                            revert(0, returndatasize())
                        }
                        return(0, returndatasize())
                    }
                }
            }
            // SPDX-License-Identifier: LGPL-3.0-only
            pragma solidity >=0.7.0 <0.9.0;
            import "./GnosisSafeProxy.sol";
            import "./IProxyCreationCallback.sol";
            /// @title Proxy Factory - Allows to create new proxy contact and execute a message call to the new proxy within one transaction.
            /// @author Stefan George - <[email protected]>
            contract GnosisSafeProxyFactory {
                event ProxyCreation(GnosisSafeProxy proxy, address singleton);
                /// @dev Allows to create new proxy contact and execute a message call to the new proxy within one transaction.
                /// @param singleton Address of singleton contract.
                /// @param data Payload for message call sent to new proxy contract.
                function createProxy(address singleton, bytes memory data) public returns (GnosisSafeProxy proxy) {
                    proxy = new GnosisSafeProxy(singleton);
                    if (data.length > 0)
                        // solhint-disable-next-line no-inline-assembly
                        assembly {
                            if eq(call(gas(), proxy, 0, add(data, 0x20), mload(data), 0, 0), 0) {
                                revert(0, 0)
                            }
                        }
                    emit ProxyCreation(proxy, singleton);
                }
                /// @dev Allows to retrieve the runtime code of a deployed Proxy. This can be used to check that the expected Proxy was deployed.
                function proxyRuntimeCode() public pure returns (bytes memory) {
                    return type(GnosisSafeProxy).runtimeCode;
                }
                /// @dev Allows to retrieve the creation code used for the Proxy deployment. With this it is easily possible to calculate predicted address.
                function proxyCreationCode() public pure returns (bytes memory) {
                    return type(GnosisSafeProxy).creationCode;
                }
                /// @dev Allows to create new proxy contact using CREATE2 but it doesn't run the initializer.
                ///      This method is only meant as an utility to be called from other methods
                /// @param _singleton Address of singleton contract.
                /// @param initializer Payload for message call sent to new proxy contract.
                /// @param saltNonce Nonce that will be used to generate the salt to calculate the address of the new proxy contract.
                function deployProxyWithNonce(
                    address _singleton,
                    bytes memory initializer,
                    uint256 saltNonce
                ) internal returns (GnosisSafeProxy proxy) {
                    // If the initializer changes the proxy address should change too. Hashing the initializer data is cheaper than just concatinating it
                    bytes32 salt = keccak256(abi.encodePacked(keccak256(initializer), saltNonce));
                    bytes memory deploymentData = abi.encodePacked(type(GnosisSafeProxy).creationCode, uint256(uint160(_singleton)));
                    // solhint-disable-next-line no-inline-assembly
                    assembly {
                        proxy := create2(0x0, add(0x20, deploymentData), mload(deploymentData), salt)
                    }
                    require(address(proxy) != address(0), "Create2 call failed");
                }
                /// @dev Allows to create new proxy contact and execute a message call to the new proxy within one transaction.
                /// @param _singleton Address of singleton contract.
                /// @param initializer Payload for message call sent to new proxy contract.
                /// @param saltNonce Nonce that will be used to generate the salt to calculate the address of the new proxy contract.
                function createProxyWithNonce(
                    address _singleton,
                    bytes memory initializer,
                    uint256 saltNonce
                ) public returns (GnosisSafeProxy proxy) {
                    proxy = deployProxyWithNonce(_singleton, initializer, saltNonce);
                    if (initializer.length > 0)
                        // solhint-disable-next-line no-inline-assembly
                        assembly {
                            if eq(call(gas(), proxy, 0, add(initializer, 0x20), mload(initializer), 0, 0), 0) {
                                revert(0, 0)
                            }
                        }
                    emit ProxyCreation(proxy, _singleton);
                }
                /// @dev Allows to create new proxy contact, execute a message call to the new proxy and call a specified callback within one transaction
                /// @param _singleton Address of singleton contract.
                /// @param initializer Payload for message call sent to new proxy contract.
                /// @param saltNonce Nonce that will be used to generate the salt to calculate the address of the new proxy contract.
                /// @param callback Callback that will be invoced after the new proxy contract has been successfully deployed and initialized.
                function createProxyWithCallback(
                    address _singleton,
                    bytes memory initializer,
                    uint256 saltNonce,
                    IProxyCreationCallback callback
                ) public returns (GnosisSafeProxy proxy) {
                    uint256 saltNonceWithCallback = uint256(keccak256(abi.encodePacked(saltNonce, callback)));
                    proxy = createProxyWithNonce(_singleton, initializer, saltNonceWithCallback);
                    if (address(callback) != address(0)) callback.proxyCreated(proxy, _singleton, initializer, saltNonce);
                }
                /// @dev Allows to get the address for a new proxy contact created via `createProxyWithNonce`
                ///      This method is only meant for address calculation purpose when you use an initializer that would revert,
                ///      therefore the response is returned with a revert. When calling this method set `from` to the address of the proxy factory.
                /// @param _singleton Address of singleton contract.
                /// @param initializer Payload for message call sent to new proxy contract.
                /// @param saltNonce Nonce that will be used to generate the salt to calculate the address of the new proxy contract.
                function calculateCreateProxyWithNonceAddress(
                    address _singleton,
                    bytes calldata initializer,
                    uint256 saltNonce
                ) external returns (GnosisSafeProxy proxy) {
                    proxy = deployProxyWithNonce(_singleton, initializer, saltNonce);
                    revert(string(abi.encodePacked(proxy)));
                }
            }
            // SPDX-License-Identifier: LGPL-3.0-only
            pragma solidity >=0.7.0 <0.9.0;
            import "./GnosisSafeProxy.sol";
            interface IProxyCreationCallback {
                function proxyCreated(
                    GnosisSafeProxy proxy,
                    address _singleton,
                    bytes calldata initializer,
                    uint256 saltNonce
                ) external;
            }
            

            File 4 of 7: ServiceRegistry
            // SPDX-License-Identifier: MIT
            pragma solidity ^0.8.15;
            import "./GenericRegistry.sol";
            import "./interfaces/IMultisig.sol";
            import "./interfaces/IRegistry.sol";
            // This struct is 128 bits in total
            struct AgentParams {
                // Number of agent instances. This number is limited by the number of agent instances
                uint32 slots;
                // Bond per agent instance. This is enough for 1b+ ETH or 1e27
                uint96 bond;
            }
            // This struct is 192 bits in total
            struct AgentInstance {
                // Address of an agent instance
                address instance;
                // Canonical agent Id. This number is limited by the max number of agent Ids (see UnitRegistry contract)
                uint32 agentId;
            }
            /// @title Service Registry - Smart contract for registering services
            /// @author Aleksandr Kuperman - <[email protected]>
            contract ServiceRegistry is GenericRegistry {
                event DrainerUpdated(address indexed drainer);
                event Deposit(address indexed sender, uint256 amount);
                event Refund(address indexed receiver, uint256 amount);
                event CreateService(uint256 indexed serviceId);
                event UpdateService(uint256 indexed serviceId, bytes32 configHash);
                event RegisterInstance(address indexed operator, uint256 indexed serviceId, address indexed agentInstance, uint256 agentId);
                event CreateMultisigWithAgents(uint256 indexed serviceId, address indexed multisig);
                event ActivateRegistration(uint256 indexed serviceId);
                event TerminateService(uint256 indexed serviceId);
                event OperatorSlashed(uint256 amount, address indexed operator, uint256 indexed serviceId);
                event OperatorUnbond(address indexed operator, uint256 indexed serviceId);
                event DeployService(uint256 indexed serviceId);
                event Drain(address indexed drainer, uint256 amount);
                enum ServiceState {
                    NonExistent,
                    PreRegistration,
                    ActiveRegistration,
                    FinishedRegistration,
                    Deployed,
                    TerminatedBonded
                }
                // Service parameters
                struct Service {
                    // Registration activation deposit
                    // This is enough for 1b+ ETH or 1e27
                    uint96 securityDeposit;
                    // Multisig address for agent instances
                    address multisig;
                    // IPFS hashes pointing to the config metadata
                    bytes32 configHash;
                    // Agent instance signers threshold: must no less than ceil((n * 2 + 1) / 3) of all the agent instances combined
                    // This number will be enough to have ((2^32 - 1) * 3 - 1) / 2, which is bigger than 6.44b
                    uint32 threshold;
                    // Total number of agent instances. We assume that the number of instances is bounded by 2^32 - 1
                    uint32 maxNumAgentInstances;
                    // Actual number of agent instances. This number is less or equal to maxNumAgentInstances
                    uint32 numAgentInstances;
                    // Service state
                    ServiceState state;
                    // Canonical agent Ids for the service. Individual agent Id is bounded by the max number of agent Id
                    uint32[] agentIds;
                }
                // Agent Registry address
                address public immutable agentRegistry;
                // The amount of funds slashed. This is enough for 1b+ ETH or 1e27
                uint96 public slashedFunds;
                // Drainer address: set by the government and is allowed to drain ETH funds accumulated in this contract
                address public drainer;
                // Service registry version number
                string public constant VERSION = "1.0.0";
                // Map of service Id => set of IPFS hashes pointing to the config metadata
                mapping (uint256 => bytes32[]) public mapConfigHashes;
                // Map of operator address and serviceId => set of registered agent instance addresses
                mapping(uint256 => AgentInstance[]) public mapOperatorAndServiceIdAgentInstances;
                // Service Id and canonical agent Id => number of agent instances and correspondent instance registration bond
                mapping(uint256 => AgentParams) public mapServiceAndAgentIdAgentParams;
                // Actual agent instance addresses. Service Id and canonical agent Id => Set of agent instance addresses.
                mapping(uint256 => address[]) public mapServiceAndAgentIdAgentInstances;
                // Map of operator address and serviceId => agent instance bonding / escrow balance
                mapping(uint256 => uint96) public mapOperatorAndServiceIdOperatorBalances;
                // Map of agent instance address => service id it is registered with and operator address that supplied the instance
                mapping (address => address) public mapAgentInstanceOperators;
                // Map of service Id => set of unique component Ids
                // Updated during the service deployment via deploy() function
                mapping (uint256 => uint32[]) public mapServiceIdSetComponentIds;
                // Map of service Id => set of unique agent Ids
                mapping (uint256 => uint32[]) public mapServiceIdSetAgentIds;
                // Map of policy for multisig implementations
                mapping (address => bool) public mapMultisigs;
                // Map of service counter => service
                mapping (uint256 => Service) public mapServices;
                /// @dev Service registry constructor.
                /// @param _name Service contract name.
                /// @param _symbol Agent contract symbol.
                /// @param _baseURI Agent registry token base URI.
                /// @param _agentRegistry Agent registry address.
                constructor(string memory _name, string memory _symbol, string memory _baseURI, address _agentRegistry)
                    ERC721(_name, _symbol)
                {
                    baseURI = _baseURI;
                    agentRegistry = _agentRegistry;
                    owner = msg.sender;
                }
                /// @dev Changes the drainer.
                /// @param newDrainer Address of a drainer.
                function changeDrainer(address newDrainer) external {
                    if (msg.sender != owner) {
                        revert OwnerOnly(msg.sender, owner);
                    }
                    // Check for the zero address
                    if (newDrainer == address(0)) {
                        revert ZeroAddress();
                    }
                    drainer = newDrainer;
                    emit DrainerUpdated(newDrainer);
                }
                /// @dev Going through basic initial service checks.
                /// @param configHash IPFS hash pointing to the config metadata.
                /// @param agentIds Canonical agent Ids.
                /// @param agentParams Number of agent instances and required required bond to register an instance in the service.
                function _initialChecks(
                    bytes32 configHash,
                    uint32[] memory agentIds,
                    AgentParams[] memory agentParams
                ) private view
                {
                    // Check for the non-zero hash value
                    if (configHash == 0) {
                        revert ZeroValue();
                    }
                    // Checking for non-empty arrays and correct number of values in them
                    if (agentIds.length == 0 || agentIds.length != agentParams.length) {
                        revert WrongArrayLength(agentIds.length, agentParams.length);
                    }
                    // Check for duplicate canonical agent Ids
                    uint256 agentTotalSupply = IRegistry(agentRegistry).totalSupply();
                    uint256 lastId;
                    for (uint256 i = 0; i < agentIds.length; i++) {
                        if (agentIds[i] < (lastId + 1) || agentIds[i] > agentTotalSupply) {
                            revert WrongAgentId(agentIds[i]);
                        }
                        lastId = agentIds[i];
                    }
                }
                /// @dev Sets the service data.
                /// @param service A service instance to fill the data for.
                /// @param agentIds Canonical agent Ids.
                /// @param agentParams Number of agent instances and required required bond to register an instance in the service.
                /// @param size Size of a canonical agent ids set.
                /// @param serviceId ServiceId.
                function _setServiceData(
                    Service memory service,
                    uint32[] memory agentIds,
                    AgentParams[] memory agentParams,
                    uint256 size,
                    uint256 serviceId
                ) private
                {
                    // Security deposit
                    uint96 securityDeposit;
                    // Add canonical agent Ids for the service and the slots map
                    service.agentIds = new uint32[](size);
                    for (uint256 i = 0; i < size; i++) {
                        service.agentIds[i] = agentIds[i];
                        // Push a pair of key defining variables into one key. Service or agent Ids are not enough by themselves
                        // As with other units, we assume that the system is not expected to support more than than 2^32-1 services
                        // Need to carefully check pairings, since it's hard to find if something is incorrectly misplaced bitwise
                        // serviceId occupies first 32 bits
                        uint256 serviceAgent = serviceId;
                        // agentId takes the second 32 bits
                        serviceAgent |= uint256(agentIds[i]) << 32;
                        mapServiceAndAgentIdAgentParams[serviceAgent] = agentParams[i];
                        service.maxNumAgentInstances += agentParams[i].slots;
                        // Security deposit is the maximum of the canonical agent registration bond
                        if (agentParams[i].bond > securityDeposit) {
                            securityDeposit = agentParams[i].bond;
                        }
                    }
                    service.securityDeposit = securityDeposit;
                    // Check for the correct threshold: no less than ceil((n * 2 + 1) / 3) of all the agent instances combined
                    uint256 checkThreshold = uint256(service.maxNumAgentInstances * 2 + 1);
                    if (checkThreshold % 3 == 0) {
                        checkThreshold = checkThreshold / 3;
                    } else {
                        checkThreshold = checkThreshold / 3 + 1;
                    }
                    if (service.threshold < checkThreshold || service.threshold > service.maxNumAgentInstances) {
                        revert WrongThreshold(service.threshold, checkThreshold, service.maxNumAgentInstances);
                    }
                }
                /// @dev Creates a new service.
                /// @param serviceOwner Individual that creates and controls a service.
                /// @param configHash IPFS hash pointing to the config metadata.
                /// @param agentIds Canonical agent Ids in a sorted ascending order.
                /// @param agentParams Number of agent instances and required required bond to register an instance in the service.
                /// @param threshold Signers threshold for a multisig composed by agent instances.
                /// @return serviceId Created service Id.
                function create(
                    address serviceOwner,
                    bytes32 configHash,
                    uint32[] memory agentIds,
                    AgentParams[] memory agentParams,
                    uint32 threshold
                ) external returns (uint256 serviceId)
                {
                    // Reentrancy guard
                    if (_locked > 1) {
                        revert ReentrancyGuard();
                    }
                    _locked = 2;
                    // Check for the manager privilege for a service management
                    if (manager != msg.sender) {
                        revert ManagerOnly(msg.sender, manager);
                    }
                    // Check for the non-empty service owner address
                    if (serviceOwner == address(0)) {
                        revert ZeroAddress();
                    }
                    // Execute initial checks
                    _initialChecks(configHash, agentIds, agentParams);
                    // Check that there are no zero number of slots for a specific canonical agent id and no zero registration bond
                    for (uint256 i = 0; i < agentIds.length; i++) {
                        if (agentParams[i].slots == 0 || agentParams[i].bond == 0) {
                            revert ZeroValue();
                        }
                    }
                    // Create a new service Id
                    serviceId = totalSupply;
                    serviceId++;
                    // Set high-level data components of the service instance
                    Service memory service;
                    // Updating high-level data components of the service
                    service.threshold = threshold;
                    // Assigning the initial hash
                    service.configHash = configHash;
                    // Set the initial service state
                    service.state = ServiceState.PreRegistration;
                    // Set service data
                    _setServiceData(service, agentIds, agentParams, agentIds.length, serviceId);
                    mapServices[serviceId] = service;
                    totalSupply = serviceId;
                    // Mint the service instance to the service owner and record the service structure
                    _safeMint(serviceOwner, serviceId);
                    emit CreateService(serviceId);
                    _locked = 1;
                }
                /// @dev Updates a service in a CRUD way.
                /// @param serviceOwner Individual that creates and controls a service.
                /// @param configHash IPFS hash pointing to the config metadata.
                /// @param agentIds Canonical agent Ids in a sorted ascending order.
                /// @param agentParams Number of agent instances and required required bond to register an instance in the service.
                /// @param threshold Signers threshold for a multisig composed by agent instances.
                /// @param serviceId Service Id to be updated.
                /// @return success True, if function executed successfully.
                function update(
                    address serviceOwner,
                    bytes32 configHash,
                    uint32[] memory agentIds,
                    AgentParams[] memory agentParams,
                    uint32 threshold,
                    uint256 serviceId
                ) external returns (bool success)
                {
                    // Check for the manager privilege for a service management
                    if (manager != msg.sender) {
                        revert ManagerOnly(msg.sender, manager);
                    }
                    // Check for the service ownership
                    address actualOwner = ownerOf(serviceId);
                    if (actualOwner != serviceOwner) {
                        revert OwnerOnly(serviceOwner, actualOwner);
                    }
                    Service memory service = mapServices[serviceId];
                    if (service.state != ServiceState.PreRegistration) {
                        revert WrongServiceState(uint256(service.state), serviceId);
                    }
                    // Execute initial checks
                    _initialChecks(configHash, agentIds, agentParams);
                    // Updating high-level data components of the service
                    service.threshold = threshold;
                    service.maxNumAgentInstances = 0;
                    // Collect non-zero canonical agent ids and slots / costs, remove any canonical agent Ids from the params map
                    uint32[] memory newAgentIds = new uint32[](agentIds.length);
                    AgentParams[] memory newAgentParams = new AgentParams[](agentIds.length);
                    uint256 size;
                    for (uint256 i = 0; i < agentIds.length; i++) {
                        if (agentParams[i].slots == 0) {
                            // Push a pair of key defining variables into one key. Service or agent Ids are not enough by themselves
                            // serviceId occupies first 32 bits, agentId gets the next 32 bits
                            uint256 serviceAgent = serviceId;
                            serviceAgent |= uint256(agentIds[i]) << 32;
                            delete mapServiceAndAgentIdAgentParams[serviceAgent];
                        } else {
                            newAgentIds[size] = agentIds[i];
                            newAgentParams[size] = agentParams[i];
                            size++;
                        }
                    }
                    // Check if the previous hash is the same / hash was not updated
                    bytes32 lastConfigHash = service.configHash;
                    if (lastConfigHash != configHash) {
                        mapConfigHashes[serviceId].push(lastConfigHash);
                        service.configHash = configHash;
                    }
                    // Set service data and record the modified service struct
                    _setServiceData(service, newAgentIds, newAgentParams, size, serviceId);
                    mapServices[serviceId] = service;
                    emit UpdateService(serviceId, configHash);
                    success = true;
                }
                /// @dev Activates the service.
                /// @param serviceOwner Individual that creates and controls a service.
                /// @param serviceId Correspondent service Id.
                /// @return success True, if function executed successfully.
                function activateRegistration(address serviceOwner, uint256 serviceId) external payable returns (bool success)
                {
                    // Check for the manager privilege for a service management
                    if (manager != msg.sender) {
                        revert ManagerOnly(msg.sender, manager);
                    }
                    // Check for the service ownership
                    address actualOwner = ownerOf(serviceId);
                    if (actualOwner != serviceOwner) {
                        revert OwnerOnly(serviceOwner, actualOwner);
                    }
                    Service storage service = mapServices[serviceId];
                    // Service must be inactive
                    if (service.state != ServiceState.PreRegistration) {
                        revert ServiceMustBeInactive(serviceId);
                    }
                    if (msg.value != service.securityDeposit) {
                        revert IncorrectRegistrationDepositValue(msg.value, service.securityDeposit, serviceId);
                    }
                    // Activate the agent instance registration
                    service.state = ServiceState.ActiveRegistration;
                    emit ActivateRegistration(serviceId);
                    success = true;
                }
                /// @dev Registers agent instances.
                /// @param operator Address of the operator.
                /// @param serviceId Service Id to be updated.
                /// @param agentInstances Agent instance addresses.
                /// @param agentIds Canonical Ids of the agent correspondent to the agent instance.
                /// @return success True, if function executed successfully.
                function registerAgents(
                    address operator,
                    uint256 serviceId,
                    address[] memory agentInstances,
                    uint32[] memory agentIds
                ) external payable returns (bool success)
                {
                    // Check for the manager privilege for a service management
                    if (manager != msg.sender) {
                        revert ManagerOnly(msg.sender, manager);
                    }
                    // Check if the length of canonical agent instance addresses array and ids array have the same length
                    if (agentInstances.length != agentIds.length) {
                        revert WrongArrayLength(agentInstances.length, agentIds.length);
                    }
                    Service storage service = mapServices[serviceId];
                    // The service has to be active to register agents
                    if (service.state != ServiceState.ActiveRegistration) {
                        revert WrongServiceState(uint256(service.state), serviceId);
                    }
                    // Check for the sufficient amount of bond fee is provided
                    uint256 numAgents = agentInstances.length;
                    uint256 totalBond = 0;
                    for (uint256 i = 0; i < numAgents; ++i) {
                        // Check if canonical agent Id exists in the service
                        // Push a pair of key defining variables into one key. Service or agent Ids are not enough by themselves
                        // serviceId occupies first 32 bits, agentId gets the next 32 bits
                        uint256 serviceAgent = serviceId;
                        serviceAgent |= uint256(agentIds[i]) << 32;
                        AgentParams memory agentParams = mapServiceAndAgentIdAgentParams[serviceAgent];
                        if (agentParams.slots == 0) {
                            revert AgentNotInService(agentIds[i], serviceId);
                        }
                        totalBond += agentParams.bond;
                    }
                    if (msg.value != totalBond) {
                        revert IncorrectAgentBondingValue(msg.value, totalBond, serviceId);
                    }
                    // Operator address must not be used as an agent instance anywhere else
                    if (mapAgentInstanceOperators[operator] != address(0)) {
                        revert WrongOperator(serviceId);
                    }
                    // Push a pair of key defining variables into one key. Service Id or operator are not enough by themselves
                    // operator occupies first 160 bits
                    uint256 operatorService = uint256(uint160(operator));
                    // serviceId occupies next 32 bits assuming it is not greater than 2^32 - 1 in value
                    operatorService |= serviceId << 160;
                    for (uint256 i = 0; i < numAgents; ++i) {
                        address agentInstance = agentInstances[i];
                        uint32 agentId = agentIds[i];
                        // Operator address must be different from agent instance one
                        if (operator == agentInstance) {
                            revert WrongOperator(serviceId);
                        }
                        // Check if the agent instance is already engaged with another service
                        if (mapAgentInstanceOperators[agentInstance] != address(0)) {
                            revert AgentInstanceRegistered(mapAgentInstanceOperators[agentInstance]);
                        }
                        // Check if there is an empty slot for the agent instance in this specific service
                        // serviceId occupies first 32 bits, agentId gets the next 32 bits
                        uint256 serviceAgent = serviceId;
                        serviceAgent |= uint256(agentIds[i]) << 32;
                        if (mapServiceAndAgentIdAgentInstances[serviceAgent].length == mapServiceAndAgentIdAgentParams[serviceAgent].slots) {
                            revert AgentInstancesSlotsFilled(serviceId);
                        }
                        // Add agent instance and operator and set the instance engagement
                        mapServiceAndAgentIdAgentInstances[serviceAgent].push(agentInstance);
                        mapOperatorAndServiceIdAgentInstances[operatorService].push(AgentInstance(agentInstance, agentId));
                        service.numAgentInstances++;
                        mapAgentInstanceOperators[agentInstance] = operator;
                        emit RegisterInstance(operator, serviceId, agentInstance, agentId);
                    }
                    // If the service agent instance capacity is reached, the service becomes finished-registration
                    if (service.numAgentInstances == service.maxNumAgentInstances) {
                        service.state = ServiceState.FinishedRegistration;
                    }
                    // Update operator's bonding balance
                    mapOperatorAndServiceIdOperatorBalances[operatorService] += uint96(msg.value);
                    emit Deposit(operator, msg.value);
                    success = true;
                }
                /// @dev Creates multisig instance controlled by the set of service agent instances and deploys the service.
                /// @param serviceOwner Individual that creates and controls a service.
                /// @param serviceId Correspondent service Id.
                /// @param multisigImplementation Multisig implementation address.
                /// @param data Data payload for the multisig creation.
                /// @return multisig Address of the created multisig.
                function deploy(
                    address serviceOwner,
                    uint256 serviceId,
                    address multisigImplementation,
                    bytes memory data
                ) external returns (address multisig)
                {
                    // Reentrancy guard
                    if (_locked > 1) {
                        revert ReentrancyGuard();
                    }
                    _locked = 2;
                    // Check for the manager privilege for a service management
                    if (manager != msg.sender) {
                        revert ManagerOnly(msg.sender, manager);
                    }
                    // Check for the service ownership
                    address actualOwner = ownerOf(serviceId);
                    if (actualOwner != serviceOwner) {
                        revert OwnerOnly(serviceOwner, actualOwner);
                    }
                    // Check for the whitelisted multisig implementation
                    if (!mapMultisigs[multisigImplementation]) {
                        revert UnauthorizedMultisig(multisigImplementation);
                    }
                    Service storage service = mapServices[serviceId];
                    if (service.state != ServiceState.FinishedRegistration) {
                        revert WrongServiceState(uint256(service.state), serviceId);
                    }
                    // Get all agent instances for the multisig
                    address[] memory agentInstances = _getAgentInstances(service, serviceId);
                    // Create a multisig with agent instances
                    multisig = IMultisig(multisigImplementation).create(agentInstances, service.threshold, data);
                    // Update maps of service Id to subcomponent and agent Ids
                    mapServiceIdSetAgentIds[serviceId] = service.agentIds;
                    mapServiceIdSetComponentIds[serviceId] = IRegistry(agentRegistry).calculateSubComponents(service.agentIds);
                    service.multisig = multisig;
                    service.state = ServiceState.Deployed;
                    emit CreateMultisigWithAgents(serviceId, multisig);
                    emit DeployService(serviceId);
                    _locked = 1;
                }
                /// @dev Slashes a specified agent instance.
                /// @param agentInstances Agent instances to slash.
                /// @param amounts Correspondent amounts to slash.
                /// @param serviceId Service Id.
                /// @return success True, if function executed successfully.
                function slash(address[] memory agentInstances, uint96[] memory amounts, uint256 serviceId) external
                    returns (bool success)
                {
                    // Check if the service is deployed
                    // Since we do not kill (burn) services, we want this check to happen in a right service state.
                    // If the service is deployed, it definitely exists and is running. We do not want this function to be abused
                    // when the service was deployed, then terminated, then in a sleep mode or before next deployment somebody
                    // could use this function and try to slash operators.
                    Service memory service = mapServices[serviceId];
                    if (service.state != ServiceState.Deployed) {
                        revert WrongServiceState(uint256(service.state), serviceId);
                    }
                    // Check for the array size
                    if (agentInstances.length != amounts.length) {
                        revert WrongArrayLength(agentInstances.length, amounts.length);
                    }
                    // Only the multisig of a correspondent address can slash its agent instances
                    if (msg.sender != service.multisig) {
                        revert OnlyOwnServiceMultisig(msg.sender, service.multisig, serviceId);
                    }
                    // Loop over each agent instance
                    uint256 numInstancesToSlash = agentInstances.length;
                    for (uint256 i = 0; i < numInstancesToSlash; ++i) {
                        // Get the service Id from the agentInstance map
                        address operator = mapAgentInstanceOperators[agentInstances[i]];
                        // Push a pair of key defining variables into one key. Service Id or operator are not enough by themselves
                        // operator occupies first 160 bits
                        uint256 operatorService = uint256(uint160(operator));
                        // serviceId occupies next 32 bits
                        operatorService |= serviceId << 160;
                        // Slash the balance of the operator, make sure it does not go below zero
                        uint96 balance = mapOperatorAndServiceIdOperatorBalances[operatorService];
                        if ((amounts[i] + 1) > balance) {
                            // We cannot add to the slashed amount more than the balance of the operator
                            slashedFunds += balance;
                            balance = 0;
                        } else {
                            slashedFunds += amounts[i];
                            balance -= amounts[i];
                        }
                        mapOperatorAndServiceIdOperatorBalances[operatorService] = balance;
                        emit OperatorSlashed(amounts[i], operator, serviceId);
                    }
                    success = true;
                }
                /// @dev Terminates the service.
                /// @param serviceOwner Owner of the service.
                /// @param serviceId Service Id to be updated.
                /// @return success True, if function executed successfully.
                /// @return refund Refund to return to the service owner.
                function terminate(address serviceOwner, uint256 serviceId) external returns (bool success, uint256 refund)
                {
                    // Reentrancy guard
                    if (_locked > 1) {
                        revert ReentrancyGuard();
                    }
                    _locked = 2;
                    // Check for the manager privilege for a service management
                    if (manager != msg.sender) {
                        revert ManagerOnly(msg.sender, manager);
                    }
                    // Check for the service ownership
                    address actualOwner = ownerOf(serviceId);
                    if (actualOwner != serviceOwner) {
                        revert OwnerOnly(serviceOwner, actualOwner);
                    }
                    Service storage service = mapServices[serviceId];
                    // Check if the service is already terminated
                    if (service.state == ServiceState.PreRegistration || service.state == ServiceState.TerminatedBonded) {
                        revert WrongServiceState(uint256(service.state), serviceId);
                    }
                    // Define the state of the service depending on the number of bonded agent instances
                    if (service.numAgentInstances > 0) {
                        service.state = ServiceState.TerminatedBonded;
                    } else {
                        service.state = ServiceState.PreRegistration;
                    }
                    
                    // Delete the sensitive data
                    delete mapServiceIdSetComponentIds[serviceId];
                    delete mapServiceIdSetAgentIds[serviceId];
                    for (uint256 i = 0; i < service.agentIds.length; ++i) {
                        // serviceId occupies first 32 bits, agentId gets the next 32 bits
                        uint256 serviceAgent = serviceId;
                        serviceAgent |= uint256(service.agentIds[i]) << 32;
                        delete mapServiceAndAgentIdAgentInstances[serviceAgent];
                    }
                    // Return registration deposit back to the service owner
                    refund = service.securityDeposit;
                    // By design, the refund is always a non-zero value, so no check is needed here fo that
                    (bool result, ) = serviceOwner.call{value: refund}("");
                    if (!result) {
                        revert TransferFailed(address(0), address(this), serviceOwner, refund);
                    }
                    emit Refund(serviceOwner, refund);
                    emit TerminateService(serviceId);
                    success = true;
                    _locked = 1;
                }
                /// @dev Unbonds agent instances of the operator from the service.
                /// @param operator Operator of agent instances.
                /// @param serviceId Service Id.
                /// @return success True, if function executed successfully.
                /// @return refund The amount of refund returned to the operator.
                function unbond(address operator, uint256 serviceId) external returns (bool success, uint256 refund) {
                    // Reentrancy guard
                    if (_locked > 1) {
                        revert ReentrancyGuard();
                    }
                    _locked = 2;
                    // Check for the manager privilege for a service management
                    if (manager != msg.sender) {
                        revert ManagerOnly(msg.sender, manager);
                    }
                    // Checks if the operator address is not zero
                    if (operator == address(0)) {
                        revert ZeroAddress();
                    }
                    Service storage service = mapServices[serviceId];
                    // Service can only be in the terminated-bonded state or expired-registration in order to proceed
                    if (service.state != ServiceState.TerminatedBonded) {
                        revert WrongServiceState(uint256(service.state), serviceId);
                    }
                    // Check for the operator and unbond all its agent instances
                    // Push a pair of key defining variables into one key. Service Id or operator are not enough by themselves
                    // operator occupies first 160 bits
                    uint256 operatorService = uint256(uint160(operator));
                    // serviceId occupies next 32 bits
                    operatorService |= serviceId << 160;
                    AgentInstance[] memory agentInstances = mapOperatorAndServiceIdAgentInstances[operatorService];
                    uint256 numAgentsUnbond = agentInstances.length;
                    if (numAgentsUnbond == 0) {
                        revert OperatorHasNoInstances(operator, serviceId);
                    }
                    // Subtract number of unbonded agent instances
                    service.numAgentInstances -= uint32(numAgentsUnbond);
                    // When number of instances is equal to zero, all the operators have unbonded and the service is moved into
                    // the PreRegistration state, from where it can be updated / start registration / get deployed again
                    if (service.numAgentInstances == 0) {
                        service.state = ServiceState.PreRegistration;
                    }
                    // else condition is redundant here, since the service is either in the TerminatedBonded state, or moved
                    // into the PreRegistration state and unbonding is not possible before the new TerminatedBonded state is reached
                    // Calculate registration refund and free all agent instances
                    for (uint256 i = 0; i < numAgentsUnbond; i++) {
                        // serviceId occupies first 32 bits, agentId gets the next 32 bits
                        uint256 serviceAgent = serviceId;
                        serviceAgent |= uint256(agentInstances[i].agentId) << 32;
                        refund += mapServiceAndAgentIdAgentParams[serviceAgent].bond;
                        // Clean-up the sensitive data such that it is not reused later
                        delete mapAgentInstanceOperators[agentInstances[i].instance];
                    }
                    // Clean all the operator agent instances records for this service
                    delete mapOperatorAndServiceIdAgentInstances[operatorService];
                    // Calculate the refund
                    uint96 balance = mapOperatorAndServiceIdOperatorBalances[operatorService];
                    // This situation is possible if the operator was slashed for the agent instance misbehavior
                    if (refund > balance) {
                        refund = balance;
                    }
                    // Refund the operator
                    if (refund > 0) {
                        // Operator's balance is essentially zero after the refund
                        mapOperatorAndServiceIdOperatorBalances[operatorService] = 0;
                        // Send the refund
                        (bool result, ) = operator.call{value: refund}("");
                        if (!result) {
                            revert TransferFailed(address(0), address(this), operator, refund);
                        }
                        emit Refund(operator, refund);
                    }
                    emit OperatorUnbond(operator, serviceId);
                    success = true;
                    _locked = 1;
                }
                /// @dev Gets the service instance.
                /// @param serviceId Service Id.
                /// @return service Corresponding Service struct.
                function getService(uint256 serviceId) external view returns (Service memory service) {
                    service = mapServices[serviceId];
                }
                /// @dev Gets service agent parameters: number of agent instances (slots) and a bond amount.
                /// @param serviceId Service Id.
                /// @return numAgentIds Number of canonical agent Ids in the service.
                /// @return agentParams Set of agent parameters for each canonical agent Id.
                function getAgentParams(uint256 serviceId) external view
                    returns (uint256 numAgentIds, AgentParams[] memory agentParams)
                {
                    Service memory service = mapServices[serviceId];
                    numAgentIds = service.agentIds.length;
                    agentParams = new AgentParams[](numAgentIds);
                    for (uint256 i = 0; i < numAgentIds; ++i) {
                        uint256 serviceAgent = serviceId;
                        serviceAgent |= uint256(service.agentIds[i]) << 32;
                        agentParams[i] = mapServiceAndAgentIdAgentParams[serviceAgent];
                    }
                }
                /// @dev Lists all the instances of a given canonical agent Id if the service.
                /// @param serviceId Service Id.
                /// @param agentId Canonical agent Id.
                /// @return numAgentInstances Number of agent instances.
                /// @return agentInstances Set of agent instances for a specified canonical agent Id.
                function getInstancesForAgentId(uint256 serviceId, uint256 agentId) external view
                    returns (uint256 numAgentInstances, address[] memory agentInstances)
                {
                    uint256 serviceAgent = serviceId;
                    serviceAgent |= agentId << 32;
                    numAgentInstances = mapServiceAndAgentIdAgentInstances[serviceAgent].length;
                    agentInstances = new address[](numAgentInstances);
                    for (uint256 i = 0; i < numAgentInstances; i++) {
                        agentInstances[i] = mapServiceAndAgentIdAgentInstances[serviceAgent][i];
                    }
                }
                /// @dev Gets all agent instances.
                /// @param service Service instance.
                /// @param serviceId ServiceId.
                /// @return agentInstances Pre-allocated list of agent instance addresses.
                function _getAgentInstances(Service memory service, uint256 serviceId) private view
                    returns (address[] memory agentInstances)
                {
                    agentInstances = new address[](service.numAgentInstances);
                    uint256 count;
                    for (uint256 i = 0; i < service.agentIds.length; i++) {
                        // serviceId occupies first 32 bits, agentId gets the next 32 bits
                        uint256 serviceAgent = serviceId;
                        serviceAgent |= uint256(service.agentIds[i]) << 32;
                        for (uint256 j = 0; j < mapServiceAndAgentIdAgentInstances[serviceAgent].length; j++) {
                            agentInstances[count] = mapServiceAndAgentIdAgentInstances[serviceAgent][j];
                            count++;
                        }
                    }
                }
                /// @dev Gets service agent instances.
                /// @param serviceId ServiceId.
                /// @return numAgentInstances Number of agent instances.
                /// @return agentInstances Pre-allocated list of agent instance addresses.
                function getAgentInstances(uint256 serviceId) external view
                    returns (uint256 numAgentInstances, address[] memory agentInstances)
                {
                    Service memory service = mapServices[serviceId];
                    agentInstances = _getAgentInstances(service, serviceId);
                    numAgentInstances = agentInstances.length;
                }
                /// @dev Gets previous service config hashes.
                /// @param serviceId Service Id.
                /// @return numHashes Number of hashes.
                /// @return configHashes The list of previous component hashes (excluding the current one).
                function getPreviousHashes(uint256 serviceId) external view
                    returns (uint256 numHashes, bytes32[] memory configHashes)
                {
                    configHashes = mapConfigHashes[serviceId];
                    numHashes = configHashes.length;
                }
                /// @dev Gets the full set of linearized components / canonical agent Ids for a specified service.
                /// @notice The service must be / have been deployed in order to get the actual data.
                /// @param serviceId Service Id.
                /// @return numUnitIds Number of component / agent Ids.
                /// @return unitIds Set of component / agent Ids.
                function getUnitIdsOfService(IRegistry.UnitType unitType, uint256 serviceId) external view
                    returns (uint256 numUnitIds, uint32[] memory unitIds)
                {
                    if (unitType == IRegistry.UnitType.Component) {
                        unitIds = mapServiceIdSetComponentIds[serviceId];
                    } else {
                        unitIds = mapServiceIdSetAgentIds[serviceId];
                    }
                    numUnitIds = unitIds.length;
                }
                /// @dev Gets the operator's balance in a specific service.
                /// @param operator Operator address.
                /// @param serviceId Service Id.
                /// @return balance The balance of the operator.
                function getOperatorBalance(address operator, uint256 serviceId) external view returns (uint256 balance)
                {
                    uint256 operatorService = uint256(uint160(operator));
                    operatorService |= serviceId << 160;
                    balance = mapOperatorAndServiceIdOperatorBalances[operatorService];
                }
                /// @dev Controls multisig implementation address permission.
                /// @param multisig Address of a multisig implementation.
                /// @param permission Grant or revoke permission.
                /// @return success True, if function executed successfully.
                function changeMultisigPermission(address multisig, bool permission) external returns (bool success) {
                    // Check for the contract ownership
                    if (msg.sender != owner) {
                        revert OwnerOnly(msg.sender, owner);
                    }
                    if (multisig == address(0)) {
                        revert ZeroAddress();
                    }
                    mapMultisigs[multisig] = permission;
                    success = true;
                }
                /// @dev Drains slashed funds.
                /// @return amount Drained amount.
                function drain() external returns (uint256 amount) {
                    // Reentrancy guard
                    if (_locked > 1) {
                        revert ReentrancyGuard();
                    }
                    _locked = 2;
                    // Check for the drainer address
                    if (msg.sender != drainer) {
                        revert ManagerOnly(msg.sender, drainer);
                    }
                    // Drain the slashed funds
                    amount = slashedFunds;
                    if (amount > 0) {
                        slashedFunds = 0;
                        // Send the refund
                        (bool result, ) = msg.sender.call{value: amount}("");
                        if (!result) {
                            revert TransferFailed(address(0), address(this), msg.sender, amount);
                        }
                        emit Drain(msg.sender, amount);
                    }
                    _locked = 1;
                }
                /// @dev Gets the hash of the service.
                /// @param serviceId Service Id.
                /// @return Service hash.
                function _getUnitHash(uint256 serviceId) internal view override returns (bytes32) {
                    return mapServices[serviceId].configHash;
                }
            }
            // SPDX-License-Identifier: MIT
            pragma solidity ^0.8.15;
            import "../lib/solmate/src/tokens/ERC721.sol";
            import "./interfaces/IErrorsRegistries.sol";
            /// @title Generic Registry - Smart contract for generic registry template
            /// @author Aleksandr Kuperman - <[email protected]>
            abstract contract GenericRegistry is IErrorsRegistries, ERC721 {
                event OwnerUpdated(address indexed owner);
                event ManagerUpdated(address indexed manager);
                event BaseURIChanged(string baseURI);
                // Owner address
                address public owner;
                // Unit manager
                address public manager;
                // Base URI
                string public baseURI;
                // Unit counter
                uint256 public totalSupply;
                // Reentrancy lock
                uint256 internal _locked = 1;
                // To better understand the CID anatomy, please refer to: https://proto.school/anatomy-of-a-cid/05
                // CID = <multibase_encoding>multibase_encoding(<cid-version><multicodec><multihash-algorithm><multihash-length><multihash-hash>)
                // CID prefix = <multibase_encoding>multibase_encoding(<cid-version><multicodec><multihash-algorithm><multihash-length>)
                // to complement the multibase_encoding(<multihash-hash>)
                // multibase_encoding = base16 = "f"
                // cid-version = version 1 = "0x01"
                // multicodec = dag-pb = "0x70"
                // multihash-algorithm = sha2-256 = "0x12"
                // multihash-length = 256 bits = "0x20"
                string public constant CID_PREFIX = "f01701220";
                /// @dev Changes the owner address.
                /// @param newOwner Address of a new owner.
                function changeOwner(address newOwner) external virtual {
                    // Check for the ownership
                    if (msg.sender != owner) {
                        revert OwnerOnly(msg.sender, owner);
                    }
                    // Check for the zero address
                    if (newOwner == address(0)) {
                        revert ZeroAddress();
                    }
                    owner = newOwner;
                    emit OwnerUpdated(newOwner);
                }
                /// @dev Changes the unit manager.
                /// @param newManager Address of a new unit manager.
                function changeManager(address newManager) external virtual {
                    if (msg.sender != owner) {
                        revert OwnerOnly(msg.sender, owner);
                    }
                    // Check for the zero address
                    if (newManager == address(0)) {
                        revert ZeroAddress();
                    }
                    manager = newManager;
                    emit ManagerUpdated(newManager);
                }
                /// @dev Checks for the unit existence.
                /// @notice Unit counter starts from 1.
                /// @param unitId Unit Id.
                /// @return true if the unit exists, false otherwise.
                function exists(uint256 unitId) external view virtual returns (bool) {
                    return unitId > 0 && unitId < (totalSupply + 1);
                }
                
                /// @dev Sets unit base URI.
                /// @param bURI Base URI string.
                function setBaseURI(string memory bURI) external virtual {
                    // Check for the ownership
                    if (msg.sender != owner) {
                        revert OwnerOnly(msg.sender, owner);
                    }
                    // Check for the zero value
                    if (bytes(bURI).length == 0) {
                        revert ZeroValue();
                    }
                    baseURI = bURI;
                    emit BaseURIChanged(bURI);
                }
                /// @dev Gets the valid unit Id from the provided index.
                /// @notice Unit counter starts from 1.
                /// @param id Unit counter.
                /// @return unitId Unit Id.
                function tokenByIndex(uint256 id) external view virtual returns (uint256 unitId) {
                    unitId = id + 1;
                    if (unitId > totalSupply) {
                        revert Overflow(unitId, totalSupply);
                    }
                }
                // Open sourced from: https://stackoverflow.com/questions/67893318/solidity-how-to-represent-bytes32-as-string
                /// @dev Converts bytes16 input data to hex16.
                /// @notice This method converts bytes into the same bytes-character hex16 representation.
                /// @param data bytes16 input data.
                /// @return result hex16 conversion from the input bytes16 data.
                function _toHex16(bytes16 data) internal pure returns (bytes32 result) {
                    result = bytes32 (data) & 0xFFFFFFFFFFFFFFFF000000000000000000000000000000000000000000000000 |
                    (bytes32 (data) & 0x0000000000000000FFFFFFFFFFFFFFFF00000000000000000000000000000000) >> 64;
                    result = result & 0xFFFFFFFF000000000000000000000000FFFFFFFF000000000000000000000000 |
                    (result & 0x00000000FFFFFFFF000000000000000000000000FFFFFFFF0000000000000000) >> 32;
                    result = result & 0xFFFF000000000000FFFF000000000000FFFF000000000000FFFF000000000000 |
                    (result & 0x0000FFFF000000000000FFFF000000000000FFFF000000000000FFFF00000000) >> 16;
                    result = result & 0xFF000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000 |
                    (result & 0x00FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF0000) >> 8;
                    result = (result & 0xF000F000F000F000F000F000F000F000F000F000F000F000F000F000F000F000) >> 4 |
                    (result & 0x0F000F000F000F000F000F000F000F000F000F000F000F000F000F000F000F00) >> 8;
                    result = bytes32 (0x3030303030303030303030303030303030303030303030303030303030303030 +
                    uint256 (result) +
                        (uint256 (result) + 0x0606060606060606060606060606060606060606060606060606060606060606 >> 4 &
                        0x0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F) * 39);
                }
                /// @dev Gets the hash of the unit.
                /// @param unitId Unit Id.
                /// @return Unit hash.
                function _getUnitHash(uint256 unitId) internal view virtual returns (bytes32);
                /// @dev Returns unit token URI.
                /// @notice Expected multicodec: dag-pb; hashing function: sha2-256, with base16 encoding and leading CID_PREFIX removed.
                /// @param unitId Unit Id.
                /// @return Unit token URI string.
                function tokenURI(uint256 unitId) public view virtual override returns (string memory) {
                    bytes32 unitHash = _getUnitHash(unitId);
                    // Parse 2 parts of bytes32 into left and right hex16 representation, and concatenate into string
                    // adding the base URI and a cid prefix for the full base16 multibase prefix IPFS hash representation
                    return string(abi.encodePacked(baseURI, CID_PREFIX, _toHex16(bytes16(unitHash)),
                        _toHex16(bytes16(unitHash << 128))));
                }
            }
            // SPDX-License-Identifier: MIT
            pragma solidity ^0.8.15;
            /// @dev Generic multisig.
            interface IMultisig {
                /// @dev Creates a multisig.
                /// @param owners Set of multisig owners.
                /// @param threshold Number of required confirmations for a multisig transaction.
                /// @param data Packed data related to the creation of a chosen multisig.
                /// @return multisig Address of a created multisig.
                function create(
                    address[] memory owners,
                    uint256 threshold,
                    bytes memory data
                ) external returns (address multisig);
            }// SPDX-License-Identifier: MIT
            pragma solidity ^0.8.15;
            /// @dev Required interface for the component / agent manipulation.
            interface IRegistry {
                enum UnitType {
                    Component,
                    Agent
                }
                /// @dev Creates component / agent.
                /// @param unitOwner Owner of the component / agent.
                /// @param unitHash IPFS hash of the component / agent.
                /// @param dependencies Set of component dependencies in a sorted ascending order.
                /// @return The id of a minted component / agent.
                function create(
                    address unitOwner,
                    bytes32 unitHash,
                    uint32[] memory dependencies
                ) external returns (uint256);
                /// @dev Updates the component / agent hash.
                /// @param owner Owner of the component / agent.
                /// @param unitId Unit Id.
                /// @param unitHash Updated IPFS hash of the component / agent.
                /// @return success True, if function executed successfully.
                function updateHash(address owner, uint256 unitId, bytes32 unitHash) external returns (bool success);
                /// @dev Gets subcomponents of a provided unit Id from a local public map.
                /// @param unitId Unit Id.
                /// @return subComponentIds Set of subcomponents.
                /// @return numSubComponents Number of subcomponents.
                function getLocalSubComponents(uint256 unitId) external view returns (uint32[] memory subComponentIds, uint256 numSubComponents);
                /// @dev Calculates the set of subcomponent Ids.
                /// @param unitIds Set of unit Ids.
                /// @return subComponentIds Subcomponent Ids.
                function calculateSubComponents(uint32[] memory unitIds) external view returns (uint32[] memory subComponentIds);
                /// @dev Gets updated component / agent hashes.
                /// @param unitId Unit Id.
                /// @return numHashes Number of hashes.
                /// @return unitHashes The list of component / agent hashes.
                function getUpdatedHashes(uint256 unitId) external view returns (uint256 numHashes, bytes32[] memory unitHashes);
                /// @dev Gets the total supply of components / agents.
                /// @return Total supply.
                function totalSupply() external view returns (uint256);
            }
            // SPDX-License-Identifier: MIT
            pragma solidity >=0.8.0;
            /// @notice Modern, minimalist, and gas efficient ERC-721 implementation.
            /// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/tokens/ERC721.sol)
            abstract contract ERC721 {
                /*//////////////////////////////////////////////////////////////
                                             EVENTS
                //////////////////////////////////////////////////////////////*/
                event Transfer(address indexed from, address indexed to, uint256 indexed id);
                event Approval(address indexed owner, address indexed spender, uint256 indexed id);
                event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
                /*//////////////////////////////////////////////////////////////
                                     METADATA STORAGE/LOGIC
                //////////////////////////////////////////////////////////////*/
                string public name;
                string public symbol;
                function tokenURI(uint256 id) public view virtual returns (string memory);
                /*//////////////////////////////////////////////////////////////
                                  ERC721 BALANCE/OWNER STORAGE
                //////////////////////////////////////////////////////////////*/
                mapping(uint256 => address) internal _ownerOf;
                mapping(address => uint256) internal _balanceOf;
                function ownerOf(uint256 id) public view virtual returns (address owner) {
                    require((owner = _ownerOf[id]) != address(0), "NOT_MINTED");
                }
                function balanceOf(address owner) public view virtual returns (uint256) {
                    require(owner != address(0), "ZERO_ADDRESS");
                    return _balanceOf[owner];
                }
                /*//////////////////////////////////////////////////////////////
                                     ERC721 APPROVAL STORAGE
                //////////////////////////////////////////////////////////////*/
                mapping(uint256 => address) public getApproved;
                mapping(address => mapping(address => bool)) public isApprovedForAll;
                /*//////////////////////////////////////////////////////////////
                                           CONSTRUCTOR
                //////////////////////////////////////////////////////////////*/
                constructor(string memory _name, string memory _symbol) {
                    name = _name;
                    symbol = _symbol;
                }
                /*//////////////////////////////////////////////////////////////
                                          ERC721 LOGIC
                //////////////////////////////////////////////////////////////*/
                function approve(address spender, uint256 id) public virtual {
                    address owner = _ownerOf[id];
                    require(msg.sender == owner || isApprovedForAll[owner][msg.sender], "NOT_AUTHORIZED");
                    getApproved[id] = spender;
                    emit Approval(owner, spender, id);
                }
                function setApprovalForAll(address operator, bool approved) public virtual {
                    isApprovedForAll[msg.sender][operator] = approved;
                    emit ApprovalForAll(msg.sender, operator, approved);
                }
                function transferFrom(
                    address from,
                    address to,
                    uint256 id
                ) public virtual {
                    require(from == _ownerOf[id], "WRONG_FROM");
                    require(to != address(0), "INVALID_RECIPIENT");
                    require(
                        msg.sender == from || isApprovedForAll[from][msg.sender] || msg.sender == getApproved[id],
                        "NOT_AUTHORIZED"
                    );
                    // Underflow of the sender's balance is impossible because we check for
                    // ownership above and the recipient's balance can't realistically overflow.
                    unchecked {
                        _balanceOf[from]--;
                        _balanceOf[to]++;
                    }
                    _ownerOf[id] = to;
                    delete getApproved[id];
                    emit Transfer(from, to, id);
                }
                function safeTransferFrom(
                    address from,
                    address to,
                    uint256 id
                ) public virtual {
                    transferFrom(from, to, id);
                    if (to.code.length != 0)
                        require(
                            ERC721TokenReceiver(to).onERC721Received(msg.sender, from, id, "") ==
                                ERC721TokenReceiver.onERC721Received.selector,
                            "UNSAFE_RECIPIENT"
                        );
                }
                function safeTransferFrom(
                    address from,
                    address to,
                    uint256 id,
                    bytes calldata data
                ) public virtual {
                    transferFrom(from, to, id);
                    if (to.code.length != 0)
                        require(
                            ERC721TokenReceiver(to).onERC721Received(msg.sender, from, id, data) ==
                                ERC721TokenReceiver.onERC721Received.selector,
                            "UNSAFE_RECIPIENT"
                        );
                }
                /*//////////////////////////////////////////////////////////////
                                          ERC165 LOGIC
                //////////////////////////////////////////////////////////////*/
                function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {
                    return
                        interfaceId == 0x01ffc9a7 || // ERC165 Interface ID for ERC165
                        interfaceId == 0x80ac58cd || // ERC165 Interface ID for ERC721
                        interfaceId == 0x5b5e139f; // ERC165 Interface ID for ERC721Metadata
                }
                /*//////////////////////////////////////////////////////////////
                                    INTERNAL MINT/BURN LOGIC
                //////////////////////////////////////////////////////////////*/
                function _mint(address to, uint256 id) internal virtual {
                    require(to != address(0), "INVALID_RECIPIENT");
                    require(_ownerOf[id] == address(0), "ALREADY_MINTED");
                    // Counter overflow is incredibly unrealistic.
                    unchecked {
                        _balanceOf[to]++;
                    }
                    _ownerOf[id] = to;
                    emit Transfer(address(0), to, id);
                }
                function _burn(uint256 id) internal virtual {
                    address owner = _ownerOf[id];
                    require(owner != address(0), "NOT_MINTED");
                    // Ownership check above ensures no underflow.
                    unchecked {
                        _balanceOf[owner]--;
                    }
                    delete _ownerOf[id];
                    delete getApproved[id];
                    emit Transfer(owner, address(0), id);
                }
                /*//////////////////////////////////////////////////////////////
                                    INTERNAL SAFE MINT LOGIC
                //////////////////////////////////////////////////////////////*/
                function _safeMint(address to, uint256 id) internal virtual {
                    _mint(to, id);
                    if (to.code.length != 0)
                        require(
                            ERC721TokenReceiver(to).onERC721Received(msg.sender, address(0), id, "") ==
                                ERC721TokenReceiver.onERC721Received.selector,
                            "UNSAFE_RECIPIENT"
                        );
                }
                function _safeMint(
                    address to,
                    uint256 id,
                    bytes memory data
                ) internal virtual {
                    _mint(to, id);
                    if (to.code.length != 0)
                        require(
                            ERC721TokenReceiver(to).onERC721Received(msg.sender, address(0), id, data) ==
                                ERC721TokenReceiver.onERC721Received.selector,
                            "UNSAFE_RECIPIENT"
                        );
                }
            }
            /// @notice A generic interface for a contract which properly accepts ERC721 tokens.
            /// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/tokens/ERC721.sol)
            abstract contract ERC721TokenReceiver {
                function onERC721Received(
                    address,
                    address,
                    uint256,
                    bytes calldata
                ) external virtual returns (bytes4) {
                    return ERC721TokenReceiver.onERC721Received.selector;
                }
            }
            // SPDX-License-Identifier: MIT
            pragma solidity ^0.8.15;
            /// @dev Errors.
            interface IErrorsRegistries {
                /// @dev Only `manager` has a privilege, but the `sender` was provided.
                /// @param sender Sender address.
                /// @param manager Required sender address as a manager.
                error ManagerOnly(address sender, address manager);
                /// @dev Only `owner` has a privilege, but the `sender` was provided.
                /// @param sender Sender address.
                /// @param owner Required sender address as an owner.
                error OwnerOnly(address sender, address owner);
                /// @dev Hash already exists in the records.
                error HashExists();
                /// @dev Provided zero address.
                error ZeroAddress();
                /// @dev Agent Id is not correctly provided for the current routine.
                /// @param agentId Component Id.
                error WrongAgentId(uint256 agentId);
                /// @dev Wrong length of two arrays.
                /// @param numValues1 Number of values in a first array.
                /// @param numValues2 Numberf of values in a second array.
                error WrongArrayLength(uint256 numValues1, uint256 numValues2);
                /// @dev Canonical agent Id is not found.
                /// @param agentId Canonical agent Id.
                error AgentNotFound(uint256 agentId);
                /// @dev Component Id is not found.
                /// @param componentId Component Id.
                error ComponentNotFound(uint256 componentId);
                /// @dev Multisig threshold is out of bounds.
                /// @param currentThreshold Current threshold value.
                /// @param minThreshold Minimum possible threshold value.
                /// @param maxThreshold Maximum possible threshold value.
                error WrongThreshold(uint256 currentThreshold, uint256 minThreshold, uint256 maxThreshold);
                /// @dev Agent instance is already registered with a specified `operator`.
                /// @param operator Operator that registered an instance.
                error AgentInstanceRegistered(address operator);
                /// @dev Wrong operator is specified when interacting with a specified `serviceId`.
                /// @param serviceId Service Id.
                error WrongOperator(uint256 serviceId);
                /// @dev Operator has no registered instances in the service.
                /// @param operator Operator address.
                /// @param serviceId Service Id.
                error OperatorHasNoInstances(address operator, uint256 serviceId);
                /// @dev Canonical `agentId` is not found as a part of `serviceId`.
                /// @param agentId Canonical agent Id.
                /// @param serviceId Service Id.
                error AgentNotInService(uint256 agentId, uint256 serviceId);
                /// @dev The contract is paused.
                error Paused();
                /// @dev Zero value when it has to be different from zero.
                error ZeroValue();
                /// @dev Value overflow.
                /// @param provided Overflow value.
                /// @param max Maximum possible value.
                error Overflow(uint256 provided, uint256 max);
                /// @dev Service must be inactive.
                /// @param serviceId Service Id.
                error ServiceMustBeInactive(uint256 serviceId);
                /// @dev All the agent instance slots for a specific `serviceId` are filled.
                /// @param serviceId Service Id.
                error AgentInstancesSlotsFilled(uint256 serviceId);
                /// @dev Wrong state of a service.
                /// @param state Service state.
                /// @param serviceId Service Id.
                error WrongServiceState(uint256 state, uint256 serviceId);
                /// @dev Only own service multisig is allowed.
                /// @param provided Provided address.
                /// @param expected Expected multisig address.
                /// @param serviceId Service Id.
                error OnlyOwnServiceMultisig(address provided, address expected, uint256 serviceId);
                /// @dev Multisig is not whitelisted.
                /// @param multisig Address of a multisig implementation.
                error UnauthorizedMultisig(address multisig);
                /// @dev Incorrect deposit provided for the registration activation.
                /// @param sent Sent amount.
                /// @param expected Expected amount.
                /// @param serviceId Service Id.
                error IncorrectRegistrationDepositValue(uint256 sent, uint256 expected, uint256 serviceId);
                /// @dev Insufficient value provided for the agent instance bonding.
                /// @param sent Sent amount.
                /// @param expected Expected amount.
                /// @param serviceId Service Id.
                error IncorrectAgentBondingValue(uint256 sent, uint256 expected, uint256 serviceId);
                /// @dev Failure of a transfer.
                /// @param token Address of a token.
                /// @param from Address `from`.
                /// @param to Address `to`.
                /// @param value Value.
                error TransferFailed(address token, address from, address to, uint256 value);
                /// @dev Caught reentrancy violation.
                error ReentrancyGuard();
            }
            

            File 5 of 7: GnosisSafeMultisig
            // SPDX-License-Identifier: MIT
            pragma solidity ^0.8.15;
            // Gnosis Safe Proxy Factory interface extracted from the mainnet: https://etherscan.io/address/0xa6b71e26c5e0845f74c812102ca7114b6a896ab2#code#F2#L61
            interface IGnosisSafeProxyFactory {
                /// @dev Allows to create new proxy contact and execute a message call to the new proxy within one transaction.
                /// @param _singleton Address of singleton contract.
                /// @param initializer Payload for message call sent to new proxy contract.
                /// @param saltNonce Nonce that will be used to generate the salt to calculate the address of the new proxy contract.
                function createProxyWithNonce(
                    address _singleton,
                    bytes memory initializer,
                    uint256 saltNonce
                ) external returns (address proxy);
            }
            /// @dev Provided incorrect data length.
            /// @param expected Expected minimum data length.
            /// @param provided Provided data length.
            error IncorrectDataLength(uint256 expected, uint256 provided);
            /// @title Gnosis Safe - Smart contract for Gnosis Safe multisig implementation of a generic multisig interface
            /// @author Aleksandr Kuperman - <[email protected]>
            contract GnosisSafeMultisig {
                // Selector of the Gnosis Safe setup function
                bytes4 public constant GNOSIS_SAFE_SETUP_SELECTOR = 0xb63e800d;
                // Default data size to be parsed and passed to the Gnosis Safe Factory without payload
                uint256 public constant DEFAULT_DATA_LENGTH = 144;
                // Gnosis Safe
                address payable public immutable gnosisSafe;
                // Gnosis Safe Factory
                address public immutable gnosisSafeProxyFactory;
                /// @dev GnosisSafeMultisig constructor.
                /// @param _gnosisSafe Gnosis Safe address.
                /// @param _gnosisSafeProxyFactory Gnosis Safe proxy factory address.
                constructor (address payable _gnosisSafe, address _gnosisSafeProxyFactory) {
                    gnosisSafe = _gnosisSafe;
                    gnosisSafeProxyFactory = _gnosisSafeProxyFactory;
                }
                /// @dev Parses (unpacks) the data to gnosis safe specific parameters.
                /// @notice If the provided data is not empty, its length must be at least 144 bytes to be parsed correctly.
                /// @param data Packed data related to the creation of a gnosis safe multisig.
                function _parseData(bytes memory data) internal pure
                    returns (address to, address fallbackHandler, address paymentToken, address payable paymentReceiver,
                        uint256 payment, uint256 nonce, bytes memory payload)
                {
                    uint256 dataLength = data.length;
                    if (dataLength > 0) {
                        // Check for the correct data length
                        if (dataLength < DEFAULT_DATA_LENGTH) {
                            revert IncorrectDataLength(DEFAULT_DATA_LENGTH, dataLength);
                        }
                        // Read the first 144 bytes of data
                        assembly {
                            // Read all the addresses first (80 bytes)
                            let offset := 20
                            to := mload(add(data, offset))
                            offset := add(offset, 20)
                            fallbackHandler := mload(add(data, offset))
                            offset := add(offset, 20)
                            paymentToken := mload(add(data, offset))
                            offset := add(offset, 20)
                            paymentReceiver := mload(add(data, offset))
                            // Read all the uints (64 more bytes, a total of 144 bytes)
                            offset := add(offset, 32)
                            payment := mload(add(data, offset))
                            offset := add(offset, 32)
                            nonce := mload(add(data, offset))
                        }
                        // Read the payload, if provided
                        if (dataLength > DEFAULT_DATA_LENGTH) {
                            uint256 payloadLength = dataLength - DEFAULT_DATA_LENGTH;
                            payload = new bytes(payloadLength);
                            for (uint256 i = 0; i < payloadLength; ++i) {
                                payload[i] = data[i + DEFAULT_DATA_LENGTH];
                            }
                        }
                    }
                }
                /// @dev Creates a gnosis safe multisig.
                /// @param owners Set of multisig owners.
                /// @param threshold Number of required confirmations for a multisig transaction.
                /// @param data Packed data related to the creation of a chosen multisig.
                /// @return multisig Address of a created multisig.
                function create(
                    address[] memory owners,
                    uint256 threshold,
                    bytes memory data
                ) external returns (address multisig)
                {
                    // Parse the data into gnosis-specific set of variables
                    (address to, address fallbackHandler, address paymentToken, address payable paymentReceiver, uint256 payment,
                        uint256 nonce, bytes memory payload) = _parseData(data);
                    // Encode the gnosis setup function parameters
                    bytes memory safeParams = abi.encodeWithSelector(GNOSIS_SAFE_SETUP_SELECTOR, owners, threshold,
                        to, payload, fallbackHandler, paymentToken, payment, paymentReceiver);
                    // Create a gnosis safe multisig via the proxy factory
                    multisig = IGnosisSafeProxyFactory(gnosisSafeProxyFactory).createProxyWithNonce(gnosisSafe, safeParams, nonce);
                }
            }

            File 6 of 7: GnosisSafe
            // SPDX-License-Identifier: LGPL-3.0-only
            pragma solidity >=0.7.0 <0.9.0;
            import "./base/ModuleManager.sol";
            import "./base/OwnerManager.sol";
            import "./base/FallbackManager.sol";
            import "./base/GuardManager.sol";
            import "./common/EtherPaymentFallback.sol";
            import "./common/Singleton.sol";
            import "./common/SignatureDecoder.sol";
            import "./common/SecuredTokenTransfer.sol";
            import "./common/StorageAccessible.sol";
            import "./interfaces/ISignatureValidator.sol";
            import "./external/GnosisSafeMath.sol";
            /// @title Gnosis Safe - A multisignature wallet with support for confirmations using signed messages based on ERC191.
            /// @author Stefan George - <[email protected]>
            /// @author Richard Meissner - <[email protected]>
            contract GnosisSafe is
                EtherPaymentFallback,
                Singleton,
                ModuleManager,
                OwnerManager,
                SignatureDecoder,
                SecuredTokenTransfer,
                ISignatureValidatorConstants,
                FallbackManager,
                StorageAccessible,
                GuardManager
            {
                using GnosisSafeMath for uint256;
                string public constant VERSION = "1.3.0";
                // keccak256(
                //     "EIP712Domain(uint256 chainId,address verifyingContract)"
                // );
                bytes32 private constant DOMAIN_SEPARATOR_TYPEHASH = 0x47e79534a245952e8b16893a336b85a3d9ea9fa8c573f3d803afb92a79469218;
                // keccak256(
                //     "SafeTx(address to,uint256 value,bytes data,uint8 operation,uint256 safeTxGas,uint256 baseGas,uint256 gasPrice,address gasToken,address refundReceiver,uint256 nonce)"
                // );
                bytes32 private constant SAFE_TX_TYPEHASH = 0xbb8310d486368db6bd6f849402fdd73ad53d316b5a4b2644ad6efe0f941286d8;
                event SafeSetup(address indexed initiator, address[] owners, uint256 threshold, address initializer, address fallbackHandler);
                event ApproveHash(bytes32 indexed approvedHash, address indexed owner);
                event SignMsg(bytes32 indexed msgHash);
                event ExecutionFailure(bytes32 txHash, uint256 payment);
                event ExecutionSuccess(bytes32 txHash, uint256 payment);
                uint256 public nonce;
                bytes32 private _deprecatedDomainSeparator;
                // Mapping to keep track of all message hashes that have been approve by ALL REQUIRED owners
                mapping(bytes32 => uint256) public signedMessages;
                // Mapping to keep track of all hashes (message or transaction) that have been approve by ANY owners
                mapping(address => mapping(bytes32 => uint256)) public approvedHashes;
                // This constructor ensures that this contract can only be used as a master copy for Proxy contracts
                constructor() {
                    // By setting the threshold it is not possible to call setup anymore,
                    // so we create a Safe with 0 owners and threshold 1.
                    // This is an unusable Safe, perfect for the singleton
                    threshold = 1;
                }
                /// @dev Setup function sets initial storage of contract.
                /// @param _owners List of Safe owners.
                /// @param _threshold Number of required confirmations for a Safe transaction.
                /// @param to Contract address for optional delegate call.
                /// @param data Data payload for optional delegate call.
                /// @param fallbackHandler Handler for fallback calls to this contract
                /// @param paymentToken Token that should be used for the payment (0 is ETH)
                /// @param payment Value that should be paid
                /// @param paymentReceiver Adddress that should receive the payment (or 0 if tx.origin)
                function setup(
                    address[] calldata _owners,
                    uint256 _threshold,
                    address to,
                    bytes calldata data,
                    address fallbackHandler,
                    address paymentToken,
                    uint256 payment,
                    address payable paymentReceiver
                ) external {
                    // setupOwners checks if the Threshold is already set, therefore preventing that this method is called twice
                    setupOwners(_owners, _threshold);
                    if (fallbackHandler != address(0)) internalSetFallbackHandler(fallbackHandler);
                    // As setupOwners can only be called if the contract has not been initialized we don't need a check for setupModules
                    setupModules(to, data);
                    if (payment > 0) {
                        // To avoid running into issues with EIP-170 we reuse the handlePayment function (to avoid adjusting code of that has been verified we do not adjust the method itself)
                        // baseGas = 0, gasPrice = 1 and gas = payment => amount = (payment + 0) * 1 = payment
                        handlePayment(payment, 0, 1, paymentToken, paymentReceiver);
                    }
                    emit SafeSetup(msg.sender, _owners, _threshold, to, fallbackHandler);
                }
                /// @dev Allows to execute a Safe transaction confirmed by required number of owners and then pays the account that submitted the transaction.
                ///      Note: The fees are always transferred, even if the user transaction fails.
                /// @param to Destination address of Safe transaction.
                /// @param value Ether value of Safe transaction.
                /// @param data Data payload of Safe transaction.
                /// @param operation Operation type of Safe transaction.
                /// @param safeTxGas Gas that should be used for the Safe transaction.
                /// @param baseGas Gas costs that are independent of the transaction execution(e.g. base transaction fee, signature check, payment of the refund)
                /// @param gasPrice Gas price that should be used for the payment calculation.
                /// @param gasToken Token address (or 0 if ETH) that is used for the payment.
                /// @param refundReceiver Address of receiver of gas payment (or 0 if tx.origin).
                /// @param signatures Packed signature data ({bytes32 r}{bytes32 s}{uint8 v})
                function execTransaction(
                    address to,
                    uint256 value,
                    bytes calldata data,
                    Enum.Operation operation,
                    uint256 safeTxGas,
                    uint256 baseGas,
                    uint256 gasPrice,
                    address gasToken,
                    address payable refundReceiver,
                    bytes memory signatures
                ) public payable virtual returns (bool success) {
                    bytes32 txHash;
                    // Use scope here to limit variable lifetime and prevent `stack too deep` errors
                    {
                        bytes memory txHashData =
                            encodeTransactionData(
                                // Transaction info
                                to,
                                value,
                                data,
                                operation,
                                safeTxGas,
                                // Payment info
                                baseGas,
                                gasPrice,
                                gasToken,
                                refundReceiver,
                                // Signature info
                                nonce
                            );
                        // Increase nonce and execute transaction.
                        nonce++;
                        txHash = keccak256(txHashData);
                        checkSignatures(txHash, txHashData, signatures);
                    }
                    address guard = getGuard();
                    {
                        if (guard != address(0)) {
                            Guard(guard).checkTransaction(
                                // Transaction info
                                to,
                                value,
                                data,
                                operation,
                                safeTxGas,
                                // Payment info
                                baseGas,
                                gasPrice,
                                gasToken,
                                refundReceiver,
                                // Signature info
                                signatures,
                                msg.sender
                            );
                        }
                    }
                    // We require some gas to emit the events (at least 2500) after the execution and some to perform code until the execution (500)
                    // We also include the 1/64 in the check that is not send along with a call to counteract potential shortings because of EIP-150
                    require(gasleft() >= ((safeTxGas * 64) / 63).max(safeTxGas + 2500) + 500, "GS010");
                    // Use scope here to limit variable lifetime and prevent `stack too deep` errors
                    {
                        uint256 gasUsed = gasleft();
                        // If the gasPrice is 0 we assume that nearly all available gas can be used (it is always more than safeTxGas)
                        // We only substract 2500 (compared to the 3000 before) to ensure that the amount passed is still higher than safeTxGas
                        success = execute(to, value, data, operation, gasPrice == 0 ? (gasleft() - 2500) : safeTxGas);
                        gasUsed = gasUsed.sub(gasleft());
                        // If no safeTxGas and no gasPrice was set (e.g. both are 0), then the internal tx is required to be successful
                        // This makes it possible to use `estimateGas` without issues, as it searches for the minimum gas where the tx doesn't revert
                        require(success || safeTxGas != 0 || gasPrice != 0, "GS013");
                        // We transfer the calculated tx costs to the tx.origin to avoid sending it to intermediate contracts that have made calls
                        uint256 payment = 0;
                        if (gasPrice > 0) {
                            payment = handlePayment(gasUsed, baseGas, gasPrice, gasToken, refundReceiver);
                        }
                        if (success) emit ExecutionSuccess(txHash, payment);
                        else emit ExecutionFailure(txHash, payment);
                    }
                    {
                        if (guard != address(0)) {
                            Guard(guard).checkAfterExecution(txHash, success);
                        }
                    }
                }
                function handlePayment(
                    uint256 gasUsed,
                    uint256 baseGas,
                    uint256 gasPrice,
                    address gasToken,
                    address payable refundReceiver
                ) private returns (uint256 payment) {
                    // solhint-disable-next-line avoid-tx-origin
                    address payable receiver = refundReceiver == address(0) ? payable(tx.origin) : refundReceiver;
                    if (gasToken == address(0)) {
                        // For ETH we will only adjust the gas price to not be higher than the actual used gas price
                        payment = gasUsed.add(baseGas).mul(gasPrice < tx.gasprice ? gasPrice : tx.gasprice);
                        require(receiver.send(payment), "GS011");
                    } else {
                        payment = gasUsed.add(baseGas).mul(gasPrice);
                        require(transferToken(gasToken, receiver, payment), "GS012");
                    }
                }
                /**
                 * @dev Checks whether the signature provided is valid for the provided data, hash. Will revert otherwise.
                 * @param dataHash Hash of the data (could be either a message hash or transaction hash)
                 * @param data That should be signed (this is passed to an external validator contract)
                 * @param signatures Signature data that should be verified. Can be ECDSA signature, contract signature (EIP-1271) or approved hash.
                 */
                function checkSignatures(
                    bytes32 dataHash,
                    bytes memory data,
                    bytes memory signatures
                ) public view {
                    // Load threshold to avoid multiple storage loads
                    uint256 _threshold = threshold;
                    // Check that a threshold is set
                    require(_threshold > 0, "GS001");
                    checkNSignatures(dataHash, data, signatures, _threshold);
                }
                /**
                 * @dev Checks whether the signature provided is valid for the provided data, hash. Will revert otherwise.
                 * @param dataHash Hash of the data (could be either a message hash or transaction hash)
                 * @param data That should be signed (this is passed to an external validator contract)
                 * @param signatures Signature data that should be verified. Can be ECDSA signature, contract signature (EIP-1271) or approved hash.
                 * @param requiredSignatures Amount of required valid signatures.
                 */
                function checkNSignatures(
                    bytes32 dataHash,
                    bytes memory data,
                    bytes memory signatures,
                    uint256 requiredSignatures
                ) public view {
                    // Check that the provided signature data is not too short
                    require(signatures.length >= requiredSignatures.mul(65), "GS020");
                    // There cannot be an owner with address 0.
                    address lastOwner = address(0);
                    address currentOwner;
                    uint8 v;
                    bytes32 r;
                    bytes32 s;
                    uint256 i;
                    for (i = 0; i < requiredSignatures; i++) {
                        (v, r, s) = signatureSplit(signatures, i);
                        if (v == 0) {
                            // If v is 0 then it is a contract signature
                            // When handling contract signatures the address of the contract is encoded into r
                            currentOwner = address(uint160(uint256(r)));
                            // Check that signature data pointer (s) is not pointing inside the static part of the signatures bytes
                            // This check is not completely accurate, since it is possible that more signatures than the threshold are send.
                            // Here we only check that the pointer is not pointing inside the part that is being processed
                            require(uint256(s) >= requiredSignatures.mul(65), "GS021");
                            // Check that signature data pointer (s) is in bounds (points to the length of data -> 32 bytes)
                            require(uint256(s).add(32) <= signatures.length, "GS022");
                            // Check if the contract signature is in bounds: start of data is s + 32 and end is start + signature length
                            uint256 contractSignatureLen;
                            // solhint-disable-next-line no-inline-assembly
                            assembly {
                                contractSignatureLen := mload(add(add(signatures, s), 0x20))
                            }
                            require(uint256(s).add(32).add(contractSignatureLen) <= signatures.length, "GS023");
                            // Check signature
                            bytes memory contractSignature;
                            // solhint-disable-next-line no-inline-assembly
                            assembly {
                                // The signature data for contract signatures is appended to the concatenated signatures and the offset is stored in s
                                contractSignature := add(add(signatures, s), 0x20)
                            }
                            require(ISignatureValidator(currentOwner).isValidSignature(data, contractSignature) == EIP1271_MAGIC_VALUE, "GS024");
                        } else if (v == 1) {
                            // If v is 1 then it is an approved hash
                            // When handling approved hashes the address of the approver is encoded into r
                            currentOwner = address(uint160(uint256(r)));
                            // Hashes are automatically approved by the sender of the message or when they have been pre-approved via a separate transaction
                            require(msg.sender == currentOwner || approvedHashes[currentOwner][dataHash] != 0, "GS025");
                        } else if (v > 30) {
                            // If v > 30 then default va (27,28) has been adjusted for eth_sign flow
                            // To support eth_sign and similar we adjust v and hash the messageHash with the Ethereum message prefix before applying ecrecover
                            currentOwner = ecrecover(keccak256(abi.encodePacked("\\x19Ethereum Signed Message:\
            32", dataHash)), v - 4, r, s);
                        } else {
                            // Default is the ecrecover flow with the provided data hash
                            // Use ecrecover with the messageHash for EOA signatures
                            currentOwner = ecrecover(dataHash, v, r, s);
                        }
                        require(currentOwner > lastOwner && owners[currentOwner] != address(0) && currentOwner != SENTINEL_OWNERS, "GS026");
                        lastOwner = currentOwner;
                    }
                }
                /// @dev Allows to estimate a Safe transaction.
                ///      This method is only meant for estimation purpose, therefore the call will always revert and encode the result in the revert data.
                ///      Since the `estimateGas` function includes refunds, call this method to get an estimated of the costs that are deducted from the safe with `execTransaction`
                /// @param to Destination address of Safe transaction.
                /// @param value Ether value of Safe transaction.
                /// @param data Data payload of Safe transaction.
                /// @param operation Operation type of Safe transaction.
                /// @return Estimate without refunds and overhead fees (base transaction and payload data gas costs).
                /// @notice Deprecated in favor of common/StorageAccessible.sol and will be removed in next version.
                function requiredTxGas(
                    address to,
                    uint256 value,
                    bytes calldata data,
                    Enum.Operation operation
                ) external returns (uint256) {
                    uint256 startGas = gasleft();
                    // We don't provide an error message here, as we use it to return the estimate
                    require(execute(to, value, data, operation, gasleft()));
                    uint256 requiredGas = startGas - gasleft();
                    // Convert response to string and return via error message
                    revert(string(abi.encodePacked(requiredGas)));
                }
                /**
                 * @dev Marks a hash as approved. This can be used to validate a hash that is used by a signature.
                 * @param hashToApprove The hash that should be marked as approved for signatures that are verified by this contract.
                 */
                function approveHash(bytes32 hashToApprove) external {
                    require(owners[msg.sender] != address(0), "GS030");
                    approvedHashes[msg.sender][hashToApprove] = 1;
                    emit ApproveHash(hashToApprove, msg.sender);
                }
                /// @dev Returns the chain id used by this contract.
                function getChainId() public view returns (uint256) {
                    uint256 id;
                    // solhint-disable-next-line no-inline-assembly
                    assembly {
                        id := chainid()
                    }
                    return id;
                }
                function domainSeparator() public view returns (bytes32) {
                    return keccak256(abi.encode(DOMAIN_SEPARATOR_TYPEHASH, getChainId(), this));
                }
                /// @dev Returns the bytes that are hashed to be signed by owners.
                /// @param to Destination address.
                /// @param value Ether value.
                /// @param data Data payload.
                /// @param operation Operation type.
                /// @param safeTxGas Gas that should be used for the safe transaction.
                /// @param baseGas Gas costs for that are independent of the transaction execution(e.g. base transaction fee, signature check, payment of the refund)
                /// @param gasPrice Maximum gas price that should be used for this transaction.
                /// @param gasToken Token address (or 0 if ETH) that is used for the payment.
                /// @param refundReceiver Address of receiver of gas payment (or 0 if tx.origin).
                /// @param _nonce Transaction nonce.
                /// @return Transaction hash bytes.
                function encodeTransactionData(
                    address to,
                    uint256 value,
                    bytes calldata data,
                    Enum.Operation operation,
                    uint256 safeTxGas,
                    uint256 baseGas,
                    uint256 gasPrice,
                    address gasToken,
                    address refundReceiver,
                    uint256 _nonce
                ) public view returns (bytes memory) {
                    bytes32 safeTxHash =
                        keccak256(
                            abi.encode(
                                SAFE_TX_TYPEHASH,
                                to,
                                value,
                                keccak256(data),
                                operation,
                                safeTxGas,
                                baseGas,
                                gasPrice,
                                gasToken,
                                refundReceiver,
                                _nonce
                            )
                        );
                    return abi.encodePacked(bytes1(0x19), bytes1(0x01), domainSeparator(), safeTxHash);
                }
                /// @dev Returns hash to be signed by owners.
                /// @param to Destination address.
                /// @param value Ether value.
                /// @param data Data payload.
                /// @param operation Operation type.
                /// @param safeTxGas Fas that should be used for the safe transaction.
                /// @param baseGas Gas costs for data used to trigger the safe transaction.
                /// @param gasPrice Maximum gas price that should be used for this transaction.
                /// @param gasToken Token address (or 0 if ETH) that is used for the payment.
                /// @param refundReceiver Address of receiver of gas payment (or 0 if tx.origin).
                /// @param _nonce Transaction nonce.
                /// @return Transaction hash.
                function getTransactionHash(
                    address to,
                    uint256 value,
                    bytes calldata data,
                    Enum.Operation operation,
                    uint256 safeTxGas,
                    uint256 baseGas,
                    uint256 gasPrice,
                    address gasToken,
                    address refundReceiver,
                    uint256 _nonce
                ) public view returns (bytes32) {
                    return keccak256(encodeTransactionData(to, value, data, operation, safeTxGas, baseGas, gasPrice, gasToken, refundReceiver, _nonce));
                }
            }
            // SPDX-License-Identifier: LGPL-3.0-only
            pragma solidity >=0.7.0 <0.9.0;
            import "../common/Enum.sol";
            /// @title Executor - A contract that can execute transactions
            /// @author Richard Meissner - <[email protected]>
            contract Executor {
                function execute(
                    address to,
                    uint256 value,
                    bytes memory data,
                    Enum.Operation operation,
                    uint256 txGas
                ) internal returns (bool success) {
                    if (operation == Enum.Operation.DelegateCall) {
                        // solhint-disable-next-line no-inline-assembly
                        assembly {
                            success := delegatecall(txGas, to, add(data, 0x20), mload(data), 0, 0)
                        }
                    } else {
                        // solhint-disable-next-line no-inline-assembly
                        assembly {
                            success := call(txGas, to, value, add(data, 0x20), mload(data), 0, 0)
                        }
                    }
                }
            }
            // SPDX-License-Identifier: LGPL-3.0-only
            pragma solidity >=0.7.0 <0.9.0;
            import "../common/SelfAuthorized.sol";
            /// @title Fallback Manager - A contract that manages fallback calls made to this contract
            /// @author Richard Meissner - <[email protected]>
            contract FallbackManager is SelfAuthorized {
                event ChangedFallbackHandler(address handler);
                // keccak256("fallback_manager.handler.address")
                bytes32 internal constant FALLBACK_HANDLER_STORAGE_SLOT = 0x6c9a6c4a39284e37ed1cf53d337577d14212a4870fb976a4366c693b939918d5;
                function internalSetFallbackHandler(address handler) internal {
                    bytes32 slot = FALLBACK_HANDLER_STORAGE_SLOT;
                    // solhint-disable-next-line no-inline-assembly
                    assembly {
                        sstore(slot, handler)
                    }
                }
                /// @dev Allows to add a contract to handle fallback calls.
                ///      Only fallback calls without value and with data will be forwarded.
                ///      This can only be done via a Safe transaction.
                /// @param handler contract to handle fallbacks calls.
                function setFallbackHandler(address handler) public authorized {
                    internalSetFallbackHandler(handler);
                    emit ChangedFallbackHandler(handler);
                }
                // solhint-disable-next-line payable-fallback,no-complex-fallback
                fallback() external {
                    bytes32 slot = FALLBACK_HANDLER_STORAGE_SLOT;
                    // solhint-disable-next-line no-inline-assembly
                    assembly {
                        let handler := sload(slot)
                        if iszero(handler) {
                            return(0, 0)
                        }
                        calldatacopy(0, 0, calldatasize())
                        // The msg.sender address is shifted to the left by 12 bytes to remove the padding
                        // Then the address without padding is stored right after the calldata
                        mstore(calldatasize(), shl(96, caller()))
                        // Add 20 bytes for the address appended add the end
                        let success := call(gas(), handler, 0, 0, add(calldatasize(), 20), 0, 0)
                        returndatacopy(0, 0, returndatasize())
                        if iszero(success) {
                            revert(0, returndatasize())
                        }
                        return(0, returndatasize())
                    }
                }
            }
            // SPDX-License-Identifier: LGPL-3.0-only
            pragma solidity >=0.7.0 <0.9.0;
            import "../common/Enum.sol";
            import "../common/SelfAuthorized.sol";
            interface Guard {
                function checkTransaction(
                    address to,
                    uint256 value,
                    bytes memory data,
                    Enum.Operation operation,
                    uint256 safeTxGas,
                    uint256 baseGas,
                    uint256 gasPrice,
                    address gasToken,
                    address payable refundReceiver,
                    bytes memory signatures,
                    address msgSender
                ) external;
                function checkAfterExecution(bytes32 txHash, bool success) external;
            }
            /// @title Fallback Manager - A contract that manages fallback calls made to this contract
            /// @author Richard Meissner - <[email protected]>
            contract GuardManager is SelfAuthorized {
                event ChangedGuard(address guard);
                // keccak256("guard_manager.guard.address")
                bytes32 internal constant GUARD_STORAGE_SLOT = 0x4a204f620c8c5ccdca3fd54d003badd85ba500436a431f0cbda4f558c93c34c8;
                /// @dev Set a guard that checks transactions before execution
                /// @param guard The address of the guard to be used or the 0 address to disable the guard
                function setGuard(address guard) external authorized {
                    bytes32 slot = GUARD_STORAGE_SLOT;
                    // solhint-disable-next-line no-inline-assembly
                    assembly {
                        sstore(slot, guard)
                    }
                    emit ChangedGuard(guard);
                }
                function getGuard() internal view returns (address guard) {
                    bytes32 slot = GUARD_STORAGE_SLOT;
                    // solhint-disable-next-line no-inline-assembly
                    assembly {
                        guard := sload(slot)
                    }
                }
            }
            // SPDX-License-Identifier: LGPL-3.0-only
            pragma solidity >=0.7.0 <0.9.0;
            import "../common/Enum.sol";
            import "../common/SelfAuthorized.sol";
            import "./Executor.sol";
            /// @title Module Manager - A contract that manages modules that can execute transactions via this contract
            /// @author Stefan George - <[email protected]>
            /// @author Richard Meissner - <[email protected]>
            contract ModuleManager is SelfAuthorized, Executor {
                event EnabledModule(address module);
                event DisabledModule(address module);
                event ExecutionFromModuleSuccess(address indexed module);
                event ExecutionFromModuleFailure(address indexed module);
                address internal constant SENTINEL_MODULES = address(0x1);
                mapping(address => address) internal modules;
                function setupModules(address to, bytes memory data) internal {
                    require(modules[SENTINEL_MODULES] == address(0), "GS100");
                    modules[SENTINEL_MODULES] = SENTINEL_MODULES;
                    if (to != address(0))
                        // Setup has to complete successfully or transaction fails.
                        require(execute(to, 0, data, Enum.Operation.DelegateCall, gasleft()), "GS000");
                }
                /// @dev Allows to add a module to the whitelist.
                ///      This can only be done via a Safe transaction.
                /// @notice Enables the module `module` for the Safe.
                /// @param module Module to be whitelisted.
                function enableModule(address module) public authorized {
                    // Module address cannot be null or sentinel.
                    require(module != address(0) && module != SENTINEL_MODULES, "GS101");
                    // Module cannot be added twice.
                    require(modules[module] == address(0), "GS102");
                    modules[module] = modules[SENTINEL_MODULES];
                    modules[SENTINEL_MODULES] = module;
                    emit EnabledModule(module);
                }
                /// @dev Allows to remove a module from the whitelist.
                ///      This can only be done via a Safe transaction.
                /// @notice Disables the module `module` for the Safe.
                /// @param prevModule Module that pointed to the module to be removed in the linked list
                /// @param module Module to be removed.
                function disableModule(address prevModule, address module) public authorized {
                    // Validate module address and check that it corresponds to module index.
                    require(module != address(0) && module != SENTINEL_MODULES, "GS101");
                    require(modules[prevModule] == module, "GS103");
                    modules[prevModule] = modules[module];
                    modules[module] = address(0);
                    emit DisabledModule(module);
                }
                /// @dev Allows a Module to execute a Safe transaction without any further confirmations.
                /// @param to Destination address of module transaction.
                /// @param value Ether value of module transaction.
                /// @param data Data payload of module transaction.
                /// @param operation Operation type of module transaction.
                function execTransactionFromModule(
                    address to,
                    uint256 value,
                    bytes memory data,
                    Enum.Operation operation
                ) public virtual returns (bool success) {
                    // Only whitelisted modules are allowed.
                    require(msg.sender != SENTINEL_MODULES && modules[msg.sender] != address(0), "GS104");
                    // Execute transaction without further confirmations.
                    success = execute(to, value, data, operation, gasleft());
                    if (success) emit ExecutionFromModuleSuccess(msg.sender);
                    else emit ExecutionFromModuleFailure(msg.sender);
                }
                /// @dev Allows a Module to execute a Safe transaction without any further confirmations and return data
                /// @param to Destination address of module transaction.
                /// @param value Ether value of module transaction.
                /// @param data Data payload of module transaction.
                /// @param operation Operation type of module transaction.
                function execTransactionFromModuleReturnData(
                    address to,
                    uint256 value,
                    bytes memory data,
                    Enum.Operation operation
                ) public returns (bool success, bytes memory returnData) {
                    success = execTransactionFromModule(to, value, data, operation);
                    // solhint-disable-next-line no-inline-assembly
                    assembly {
                        // Load free memory location
                        let ptr := mload(0x40)
                        // We allocate memory for the return data by setting the free memory location to
                        // current free memory location + data size + 32 bytes for data size value
                        mstore(0x40, add(ptr, add(returndatasize(), 0x20)))
                        // Store the size
                        mstore(ptr, returndatasize())
                        // Store the data
                        returndatacopy(add(ptr, 0x20), 0, returndatasize())
                        // Point the return data to the correct memory location
                        returnData := ptr
                    }
                }
                /// @dev Returns if an module is enabled
                /// @return True if the module is enabled
                function isModuleEnabled(address module) public view returns (bool) {
                    return SENTINEL_MODULES != module && modules[module] != address(0);
                }
                /// @dev Returns array of modules.
                /// @param start Start of the page.
                /// @param pageSize Maximum number of modules that should be returned.
                /// @return array Array of modules.
                /// @return next Start of the next page.
                function getModulesPaginated(address start, uint256 pageSize) external view returns (address[] memory array, address next) {
                    // Init array with max page size
                    array = new address[](pageSize);
                    // Populate return array
                    uint256 moduleCount = 0;
                    address currentModule = modules[start];
                    while (currentModule != address(0x0) && currentModule != SENTINEL_MODULES && moduleCount < pageSize) {
                        array[moduleCount] = currentModule;
                        currentModule = modules[currentModule];
                        moduleCount++;
                    }
                    next = currentModule;
                    // Set correct size of returned array
                    // solhint-disable-next-line no-inline-assembly
                    assembly {
                        mstore(array, moduleCount)
                    }
                }
            }
            // SPDX-License-Identifier: LGPL-3.0-only
            pragma solidity >=0.7.0 <0.9.0;
            import "../common/SelfAuthorized.sol";
            /// @title OwnerManager - Manages a set of owners and a threshold to perform actions.
            /// @author Stefan George - <[email protected]>
            /// @author Richard Meissner - <[email protected]>
            contract OwnerManager is SelfAuthorized {
                event AddedOwner(address owner);
                event RemovedOwner(address owner);
                event ChangedThreshold(uint256 threshold);
                address internal constant SENTINEL_OWNERS = address(0x1);
                mapping(address => address) internal owners;
                uint256 internal ownerCount;
                uint256 internal threshold;
                /// @dev Setup function sets initial storage of contract.
                /// @param _owners List of Safe owners.
                /// @param _threshold Number of required confirmations for a Safe transaction.
                function setupOwners(address[] memory _owners, uint256 _threshold) internal {
                    // Threshold can only be 0 at initialization.
                    // Check ensures that setup function can only be called once.
                    require(threshold == 0, "GS200");
                    // Validate that threshold is smaller than number of added owners.
                    require(_threshold <= _owners.length, "GS201");
                    // There has to be at least one Safe owner.
                    require(_threshold >= 1, "GS202");
                    // Initializing Safe owners.
                    address currentOwner = SENTINEL_OWNERS;
                    for (uint256 i = 0; i < _owners.length; i++) {
                        // Owner address cannot be null.
                        address owner = _owners[i];
                        require(owner != address(0) && owner != SENTINEL_OWNERS && owner != address(this) && currentOwner != owner, "GS203");
                        // No duplicate owners allowed.
                        require(owners[owner] == address(0), "GS204");
                        owners[currentOwner] = owner;
                        currentOwner = owner;
                    }
                    owners[currentOwner] = SENTINEL_OWNERS;
                    ownerCount = _owners.length;
                    threshold = _threshold;
                }
                /// @dev Allows to add a new owner to the Safe and update the threshold at the same time.
                ///      This can only be done via a Safe transaction.
                /// @notice Adds the owner `owner` to the Safe and updates the threshold to `_threshold`.
                /// @param owner New owner address.
                /// @param _threshold New threshold.
                function addOwnerWithThreshold(address owner, uint256 _threshold) public authorized {
                    // Owner address cannot be null, the sentinel or the Safe itself.
                    require(owner != address(0) && owner != SENTINEL_OWNERS && owner != address(this), "GS203");
                    // No duplicate owners allowed.
                    require(owners[owner] == address(0), "GS204");
                    owners[owner] = owners[SENTINEL_OWNERS];
                    owners[SENTINEL_OWNERS] = owner;
                    ownerCount++;
                    emit AddedOwner(owner);
                    // Change threshold if threshold was changed.
                    if (threshold != _threshold) changeThreshold(_threshold);
                }
                /// @dev Allows to remove an owner from the Safe and update the threshold at the same time.
                ///      This can only be done via a Safe transaction.
                /// @notice Removes the owner `owner` from the Safe and updates the threshold to `_threshold`.
                /// @param prevOwner Owner that pointed to the owner to be removed in the linked list
                /// @param owner Owner address to be removed.
                /// @param _threshold New threshold.
                function removeOwner(
                    address prevOwner,
                    address owner,
                    uint256 _threshold
                ) public authorized {
                    // Only allow to remove an owner, if threshold can still be reached.
                    require(ownerCount - 1 >= _threshold, "GS201");
                    // Validate owner address and check that it corresponds to owner index.
                    require(owner != address(0) && owner != SENTINEL_OWNERS, "GS203");
                    require(owners[prevOwner] == owner, "GS205");
                    owners[prevOwner] = owners[owner];
                    owners[owner] = address(0);
                    ownerCount--;
                    emit RemovedOwner(owner);
                    // Change threshold if threshold was changed.
                    if (threshold != _threshold) changeThreshold(_threshold);
                }
                /// @dev Allows to swap/replace an owner from the Safe with another address.
                ///      This can only be done via a Safe transaction.
                /// @notice Replaces the owner `oldOwner` in the Safe with `newOwner`.
                /// @param prevOwner Owner that pointed to the owner to be replaced in the linked list
                /// @param oldOwner Owner address to be replaced.
                /// @param newOwner New owner address.
                function swapOwner(
                    address prevOwner,
                    address oldOwner,
                    address newOwner
                ) public authorized {
                    // Owner address cannot be null, the sentinel or the Safe itself.
                    require(newOwner != address(0) && newOwner != SENTINEL_OWNERS && newOwner != address(this), "GS203");
                    // No duplicate owners allowed.
                    require(owners[newOwner] == address(0), "GS204");
                    // Validate oldOwner address and check that it corresponds to owner index.
                    require(oldOwner != address(0) && oldOwner != SENTINEL_OWNERS, "GS203");
                    require(owners[prevOwner] == oldOwner, "GS205");
                    owners[newOwner] = owners[oldOwner];
                    owners[prevOwner] = newOwner;
                    owners[oldOwner] = address(0);
                    emit RemovedOwner(oldOwner);
                    emit AddedOwner(newOwner);
                }
                /// @dev Allows to update the number of required confirmations by Safe owners.
                ///      This can only be done via a Safe transaction.
                /// @notice Changes the threshold of the Safe to `_threshold`.
                /// @param _threshold New threshold.
                function changeThreshold(uint256 _threshold) public authorized {
                    // Validate that threshold is smaller than number of owners.
                    require(_threshold <= ownerCount, "GS201");
                    // There has to be at least one Safe owner.
                    require(_threshold >= 1, "GS202");
                    threshold = _threshold;
                    emit ChangedThreshold(threshold);
                }
                function getThreshold() public view returns (uint256) {
                    return threshold;
                }
                function isOwner(address owner) public view returns (bool) {
                    return owner != SENTINEL_OWNERS && owners[owner] != address(0);
                }
                /// @dev Returns array of owners.
                /// @return Array of Safe owners.
                function getOwners() public view returns (address[] memory) {
                    address[] memory array = new address[](ownerCount);
                    // populate return array
                    uint256 index = 0;
                    address currentOwner = owners[SENTINEL_OWNERS];
                    while (currentOwner != SENTINEL_OWNERS) {
                        array[index] = currentOwner;
                        currentOwner = owners[currentOwner];
                        index++;
                    }
                    return array;
                }
            }
            // SPDX-License-Identifier: LGPL-3.0-only
            pragma solidity >=0.7.0 <0.9.0;
            /// @title Enum - Collection of enums
            /// @author Richard Meissner - <[email protected]>
            contract Enum {
                enum Operation {Call, DelegateCall}
            }
            // SPDX-License-Identifier: LGPL-3.0-only
            pragma solidity >=0.7.0 <0.9.0;
            /// @title EtherPaymentFallback - A contract that has a fallback to accept ether payments
            /// @author Richard Meissner - <[email protected]>
            contract EtherPaymentFallback {
                event SafeReceived(address indexed sender, uint256 value);
                /// @dev Fallback function accepts Ether transactions.
                receive() external payable {
                    emit SafeReceived(msg.sender, msg.value);
                }
            }
            // SPDX-License-Identifier: LGPL-3.0-only
            pragma solidity >=0.7.0 <0.9.0;
            /// @title SecuredTokenTransfer - Secure token transfer
            /// @author Richard Meissner - <[email protected]>
            contract SecuredTokenTransfer {
                /// @dev Transfers a token and returns if it was a success
                /// @param token Token that should be transferred
                /// @param receiver Receiver to whom the token should be transferred
                /// @param amount The amount of tokens that should be transferred
                function transferToken(
                    address token,
                    address receiver,
                    uint256 amount
                ) internal returns (bool transferred) {
                    // 0xa9059cbb - keccack("transfer(address,uint256)")
                    bytes memory data = abi.encodeWithSelector(0xa9059cbb, receiver, amount);
                    // solhint-disable-next-line no-inline-assembly
                    assembly {
                        // We write the return value to scratch space.
                        // See https://docs.soliditylang.org/en/v0.7.6/internals/layout_in_memory.html#layout-in-memory
                        let success := call(sub(gas(), 10000), token, 0, add(data, 0x20), mload(data), 0, 0x20)
                        switch returndatasize()
                            case 0 {
                                transferred := success
                            }
                            case 0x20 {
                                transferred := iszero(or(iszero(success), iszero(mload(0))))
                            }
                            default {
                                transferred := 0
                            }
                    }
                }
            }
            // SPDX-License-Identifier: LGPL-3.0-only
            pragma solidity >=0.7.0 <0.9.0;
            /// @title SelfAuthorized - authorizes current contract to perform actions
            /// @author Richard Meissner - <[email protected]>
            contract SelfAuthorized {
                function requireSelfCall() private view {
                    require(msg.sender == address(this), "GS031");
                }
                modifier authorized() {
                    // This is a function call as it minimized the bytecode size
                    requireSelfCall();
                    _;
                }
            }
            // SPDX-License-Identifier: LGPL-3.0-only
            pragma solidity >=0.7.0 <0.9.0;
            /// @title SignatureDecoder - Decodes signatures that a encoded as bytes
            /// @author Richard Meissner - <[email protected]>
            contract SignatureDecoder {
                /// @dev divides bytes signature into `uint8 v, bytes32 r, bytes32 s`.
                /// @notice Make sure to peform a bounds check for @param pos, to avoid out of bounds access on @param signatures
                /// @param pos which signature to read. A prior bounds check of this parameter should be performed, to avoid out of bounds access
                /// @param signatures concatenated rsv signatures
                function signatureSplit(bytes memory signatures, uint256 pos)
                    internal
                    pure
                    returns (
                        uint8 v,
                        bytes32 r,
                        bytes32 s
                    )
                {
                    // The signature format is a compact form of:
                    //   {bytes32 r}{bytes32 s}{uint8 v}
                    // Compact means, uint8 is not padded to 32 bytes.
                    // solhint-disable-next-line no-inline-assembly
                    assembly {
                        let signaturePos := mul(0x41, pos)
                        r := mload(add(signatures, add(signaturePos, 0x20)))
                        s := mload(add(signatures, add(signaturePos, 0x40)))
                        // Here we are loading the last 32 bytes, including 31 bytes
                        // of 's'. There is no 'mload8' to do this.
                        //
                        // 'byte' is not working due to the Solidity parser, so lets
                        // use the second best option, 'and'
                        v := and(mload(add(signatures, add(signaturePos, 0x41))), 0xff)
                    }
                }
            }
            // SPDX-License-Identifier: LGPL-3.0-only
            pragma solidity >=0.7.0 <0.9.0;
            /// @title Singleton - Base for singleton contracts (should always be first super contract)
            ///         This contract is tightly coupled to our proxy contract (see `proxies/GnosisSafeProxy.sol`)
            /// @author Richard Meissner - <[email protected]>
            contract Singleton {
                // singleton always needs to be first declared variable, to ensure that it is at the same location as in the Proxy contract.
                // It should also always be ensured that the address is stored alone (uses a full word)
                address private singleton;
            }
            // SPDX-License-Identifier: LGPL-3.0-only
            pragma solidity >=0.7.0 <0.9.0;
            /// @title StorageAccessible - generic base contract that allows callers to access all internal storage.
            /// @notice See https://github.com/gnosis/util-contracts/blob/bb5fe5fb5df6d8400998094fb1b32a178a47c3a1/contracts/StorageAccessible.sol
            contract StorageAccessible {
                /**
                 * @dev Reads `length` bytes of storage in the currents contract
                 * @param offset - the offset in the current contract's storage in words to start reading from
                 * @param length - the number of words (32 bytes) of data to read
                 * @return the bytes that were read.
                 */
                function getStorageAt(uint256 offset, uint256 length) public view returns (bytes memory) {
                    bytes memory result = new bytes(length * 32);
                    for (uint256 index = 0; index < length; index++) {
                        // solhint-disable-next-line no-inline-assembly
                        assembly {
                            let word := sload(add(offset, index))
                            mstore(add(add(result, 0x20), mul(index, 0x20)), word)
                        }
                    }
                    return result;
                }
                /**
                 * @dev Performs a delegetecall on a targetContract in the context of self.
                 * Internally reverts execution to avoid side effects (making it static).
                 *
                 * This method reverts with data equal to `abi.encode(bool(success), bytes(response))`.
                 * Specifically, the `returndata` after a call to this method will be:
                 * `success:bool || response.length:uint256 || response:bytes`.
                 *
                 * @param targetContract Address of the contract containing the code to execute.
                 * @param calldataPayload Calldata that should be sent to the target contract (encoded method name and arguments).
                 */
                function simulateAndRevert(address targetContract, bytes memory calldataPayload) external {
                    // solhint-disable-next-line no-inline-assembly
                    assembly {
                        let success := delegatecall(gas(), targetContract, add(calldataPayload, 0x20), mload(calldataPayload), 0, 0)
                        mstore(0x00, success)
                        mstore(0x20, returndatasize())
                        returndatacopy(0x40, 0, returndatasize())
                        revert(0, add(returndatasize(), 0x40))
                    }
                }
            }
            // SPDX-License-Identifier: LGPL-3.0-only
            pragma solidity >=0.7.0 <0.9.0;
            /**
             * @title GnosisSafeMath
             * @dev Math operations with safety checks that revert on error
             * Renamed from SafeMath to GnosisSafeMath to avoid conflicts
             * TODO: remove once open zeppelin update to solc 0.5.0
             */
            library GnosisSafeMath {
                /**
                 * @dev Multiplies two numbers, reverts on overflow.
                 */
                function mul(uint256 a, uint256 b) internal pure returns (uint256) {
                    // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
                    // benefit is lost if 'b' is also tested.
                    // See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522
                    if (a == 0) {
                        return 0;
                    }
                    uint256 c = a * b;
                    require(c / a == b);
                    return c;
                }
                /**
                 * @dev Subtracts two numbers, reverts on overflow (i.e. if subtrahend is greater than minuend).
                 */
                function sub(uint256 a, uint256 b) internal pure returns (uint256) {
                    require(b <= a);
                    uint256 c = a - b;
                    return c;
                }
                /**
                 * @dev Adds two numbers, reverts on overflow.
                 */
                function add(uint256 a, uint256 b) internal pure returns (uint256) {
                    uint256 c = a + b;
                    require(c >= a);
                    return c;
                }
                /**
                 * @dev Returns the largest of two numbers.
                 */
                function max(uint256 a, uint256 b) internal pure returns (uint256) {
                    return a >= b ? a : b;
                }
            }
            // SPDX-License-Identifier: LGPL-3.0-only
            pragma solidity >=0.7.0 <0.9.0;
            contract ISignatureValidatorConstants {
                // bytes4(keccak256("isValidSignature(bytes,bytes)")
                bytes4 internal constant EIP1271_MAGIC_VALUE = 0x20c13b0b;
            }
            abstract contract ISignatureValidator is ISignatureValidatorConstants {
                /**
                 * @dev Should return whether the signature provided is valid for the provided data
                 * @param _data Arbitrary length data signed on the behalf of address(this)
                 * @param _signature Signature byte array associated with _data
                 *
                 * MUST return the bytes4 magic value 0x20c13b0b when function passes.
                 * MUST NOT modify state (using STATICCALL for solc < 0.5, view modifier for solc > 0.5)
                 * MUST allow external calls
                 */
                function isValidSignature(bytes memory _data, bytes memory _signature) public view virtual returns (bytes4);
            }
            

            File 7 of 7: AgentRegistry
            // SPDX-License-Identifier: MIT
            pragma solidity ^0.8.15;
            import "./UnitRegistry.sol";
            import "./interfaces/IRegistry.sol";
            /// @title Agent Registry - Smart contract for registering agents
            /// @author Aleksandr Kuperman - <[email protected]>
            contract AgentRegistry is UnitRegistry {
                // Component registry
                address public immutable componentRegistry;
                // Agent registry version number
                string public constant VERSION = "1.0.0";
                /// @dev Agent registry constructor.
                /// @param _name Agent registry contract name.
                /// @param _symbol Agent registry contract symbol.
                /// @param _baseURI Agent registry token base URI.
                /// @param _componentRegistry Component registry address.
                constructor(string memory _name, string memory _symbol, string memory _baseURI, address _componentRegistry)
                    UnitRegistry(UnitType.Agent)
                    ERC721(_name, _symbol)
                {
                    baseURI = _baseURI;
                    componentRegistry = _componentRegistry;
                    owner = msg.sender;
                }
                /// @dev Checks provided component dependencies.
                /// @param dependencies Set of component dependencies.
                function _checkDependencies(uint32[] memory dependencies, uint32) internal virtual override {
                    // Check that the agent has at least one component
                    if (dependencies.length == 0) {
                        revert ZeroValue();
                    }
                    // Get the components total supply
                    uint32 componentTotalSupply = uint32(IRegistry(componentRegistry).totalSupply());
                    uint32 lastId;
                    for (uint256 iDep = 0; iDep < dependencies.length; ++iDep) {
                        if (dependencies[iDep] < (lastId + 1) || dependencies[iDep] > componentTotalSupply) {
                            revert ComponentNotFound(dependencies[iDep]);
                        }
                        lastId = dependencies[iDep];
                    }
                }
                /// @dev Gets linearized set of subcomponents of a provided unit Id and a type of a component.
                /// @notice (0) For components this means getting the linearized map of components from the componentRegistry contract.
                /// @notice (1) For agents this means getting the linearized map of components from the local map of subcomponents.
                /// @param subcomponentsFromType Type of the unit: component or agent.
                /// @param unitId Component Id.
                /// @return subComponentIds Set of subcomponents.
                function _getSubComponents(UnitType subcomponentsFromType, uint32 unitId) internal view virtual override
                    returns (uint32[] memory subComponentIds)
                {
                    // Self contract (agent registry) can only call subcomponents calculation from the component level (0)
                    // Otherwise, the subcomponents are already written into the corresponding subcomponents map
                    if (subcomponentsFromType == UnitType.Component) {
                        (subComponentIds, ) = IRegistry(componentRegistry).getLocalSubComponents(uint256(unitId));
                    } else {
                        subComponentIds = mapSubComponents[uint256(unitId)];
                    }
                }
                /// @dev Calculates the set of subcomponent Ids.
                /// @notice We assume that the external callers calculate subcomponents from the higher unit hierarchy level: agents.
                /// @param unitIds Unit Ids.
                /// @return subComponentIds Subcomponent Ids.
                function calculateSubComponents(uint32[] memory unitIds) external view returns (uint32[] memory subComponentIds)
                {
                    subComponentIds = _calculateSubComponents(UnitType.Agent, unitIds);
                }
            }
            // SPDX-License-Identifier: MIT
            pragma solidity ^0.8.15;
            import "./GenericRegistry.sol";
            /// @title Unit Registry - Smart contract for registering generalized units / units
            /// @author Aleksandr Kuperman - <[email protected]>
            abstract contract UnitRegistry is GenericRegistry {
                event CreateUnit(uint256 unitId, UnitType uType, bytes32 unitHash);
                event UpdateUnitHash(uint256 unitId, UnitType uType, bytes32 unitHash);
                enum UnitType {
                    Component,
                    Agent
                }
                // Unit parameters
                struct Unit {
                    // Primary IPFS hash of the unit
                    bytes32 unitHash;
                    // Set of component dependencies (agents are also based on components)
                    // We assume that the system is expected to support no more than 2^32-1 components
                    uint32[] dependencies;
                }
                // Type of the unit: component or unit
                UnitType public immutable unitType;
                // Map of unit Id => set of updated IPFS hashes
                mapping(uint256 => bytes32[]) public mapUnitIdHashes;
                // Map of unit Id => set of subcomponents (possible to derive from any registry)
                mapping(uint256 => uint32[]) public mapSubComponents;
                // Map of unit Id => unit
                mapping(uint256 => Unit) public mapUnits;
                constructor(UnitType _unitType) {
                    unitType = _unitType;
                }
                /// @dev Checks the provided component dependencies.
                /// @param dependencies Set of component dependencies.
                /// @param maxUnitId Maximum unit Id.
                function _checkDependencies(uint32[] memory dependencies, uint32 maxUnitId) internal virtual;
                /// @dev Creates unit.
                /// @param unitOwner Owner of the unit.
                /// @param unitHash IPFS CID hash of the unit.
                /// @param dependencies Set of unit dependencies in a sorted ascending order (unit Ids).
                /// @return unitId The id of a minted unit.
                function create(address unitOwner, bytes32 unitHash, uint32[] memory dependencies)
                    external virtual returns (uint256 unitId)
                {
                    // Reentrancy guard
                    if (_locked > 1) {
                        revert ReentrancyGuard();
                    }
                    _locked = 2;
                    // Check for the manager privilege for a unit creation
                    if (manager != msg.sender) {
                        revert ManagerOnly(msg.sender, manager);
                    }
                    // Checks for a non-zero owner address
                    if(unitOwner == address(0)) {
                        revert ZeroAddress();
                    }
                    // Check for the non-zero hash value
                    if (unitHash == 0) {
                        revert ZeroValue();
                    }
                    
                    // Check for dependencies validity: must be already allocated, must not repeat
                    unitId = totalSupply;
                    _checkDependencies(dependencies, uint32(unitId));
                    // Unit with Id = 0 is left empty not to do additional checks for the index zero
                    unitId++;
                    // Initialize the unit and mint its token
                    Unit storage unit = mapUnits[unitId];
                    unit.unitHash = unitHash;
                    unit.dependencies = dependencies;
                    // Update the map of subcomponents with calculated subcomponents for the new unit Id
                    // In order to get the correct set of subcomponents, we need to differentiate between the callers of this function
                    // Self contract (unit registry) can only call subcomponents calculation from the component level
                    uint32[] memory subComponentIds = _calculateSubComponents(UnitType.Component, dependencies);
                    // We need to add a current component Id to the set of subcomponents if the unit is a component
                    // For example, if component 3 (c3) has dependencies of [c1, c2], then the subcomponents will return [c1, c2].
                    // The resulting set will be [c1, c2, c3]. So we write into the map of component subcomponents: c3=>[c1, c2, c3].
                    // This is done such that the subcomponents start getting explored, and when the agent calls its subcomponents,
                    // it would have [c1, c2, c3] right away instead of adding c3 manually and then (for services) checking
                    // if another agent also has c3 as a component dependency. The latter will consume additional computation.
                    if (unitType == UnitType.Component) {
                        uint256 numSubComponents = subComponentIds.length;
                        uint32[] memory addSubComponentIds = new uint32[](numSubComponents + 1);
                        for (uint256 i = 0; i < numSubComponents; ++i) {
                            addSubComponentIds[i] = subComponentIds[i];
                        }
                        // Adding self component Id
                        addSubComponentIds[numSubComponents] = uint32(unitId);
                        subComponentIds = addSubComponentIds;
                    }
                    mapSubComponents[unitId] = subComponentIds;
                    // Set total supply to the unit Id number
                    totalSupply = unitId;
                    // Safe mint is needed since contracts can create units as well
                    _safeMint(unitOwner, unitId);
                    emit CreateUnit(unitId, unitType, unitHash);
                    _locked = 1;
                }
                /// @dev Updates the unit hash.
                /// @param unitOwner Owner of the unit.
                /// @param unitId Unit Id.
                /// @param unitHash Updated IPFS hash of the unit.
                /// @return success True, if function executed successfully.
                function updateHash(address unitOwner, uint256 unitId, bytes32 unitHash) external virtual
                    returns (bool success)
                {
                    // Check the manager privilege for a unit modification
                    if (manager != msg.sender) {
                        revert ManagerOnly(msg.sender, manager);
                    }
                    // Checking the unit ownership
                    if (ownerOf(unitId) != unitOwner) {
                        if (unitType == UnitType.Component) {
                            revert ComponentNotFound(unitId);
                        } else {
                            revert AgentNotFound(unitId);
                        }
                    }
                    // Check for the hash value
                    if (unitHash == 0) {
                        revert ZeroValue();
                    }
                    mapUnitIdHashes[unitId].push(unitHash);
                    success = true;
                    emit UpdateUnitHash(unitId, unitType, unitHash);
                }
                /// @dev Gets the unit instance.
                /// @param unitId Unit Id.
                /// @return unit Corresponding Unit struct.
                function getUnit(uint256 unitId) external view virtual returns (Unit memory unit) {
                    unit = mapUnits[unitId];
                }
                /// @dev Gets unit dependencies.
                /// @param unitId Unit Id.
                /// @return numDependencies The number of units in the dependency list.
                /// @return dependencies The list of unit dependencies.
                function getDependencies(uint256 unitId) external view virtual
                    returns (uint256 numDependencies, uint32[] memory dependencies)
                {
                    Unit memory unit = mapUnits[unitId];
                    return (unit.dependencies.length, unit.dependencies);
                }
                /// @dev Gets updated unit hashes.
                /// @param unitId Unit Id.
                /// @return numHashes Number of hashes.
                /// @return unitHashes The list of updated unit hashes (without the primary one).
                function getUpdatedHashes(uint256 unitId) external view virtual
                    returns (uint256 numHashes, bytes32[] memory unitHashes)
                {
                    unitHashes = mapUnitIdHashes[unitId];
                    return (unitHashes.length, unitHashes);
                }
                /// @dev Gets the set of subcomponent Ids from a local map of subcomponent.
                /// @param unitId Component Id.
                /// @return subComponentIds Set of subcomponent Ids.
                /// @return numSubComponents Number of subcomponents.
                function getLocalSubComponents(uint256 unitId) external view
                    returns (uint32[] memory subComponentIds, uint256 numSubComponents)
                {
                    subComponentIds = mapSubComponents[uint256(unitId)];
                    numSubComponents = subComponentIds.length;
                }
                /// @dev Gets subcomponents of a provided unit Id.
                /// @param subcomponentsFromType Type of the unit: component or agent.
                /// @param unitId Unit Id.
                /// @return subComponentIds Set of subcomponents.
                function _getSubComponents(UnitType subcomponentsFromType, uint32 unitId) internal view virtual
                    returns (uint32[] memory subComponentIds);
                /// @dev Calculates the set of subcomponent Ids.
                /// @param subcomponentsFromType Type of the unit: component or agent.
                /// @param unitIds Unit Ids.
                /// @return subComponentIds Subcomponent Ids.
                function _calculateSubComponents(UnitType subcomponentsFromType, uint32[] memory unitIds) internal view virtual
                    returns (uint32[] memory subComponentIds)
                {
                    uint32 numUnits = uint32(unitIds.length);
                    // Array of numbers of components per each unit Id
                    uint32[] memory numComponents = new uint32[](numUnits);
                    // 2D array of all the sets of components per each unit Id
                    uint32[][] memory components = new uint32[][](numUnits);
                    // Get total possible number of components and lists of components
                    uint32 maxNumComponents;
                    for (uint32 i = 0; i < numUnits; ++i) {
                        // Get subcomponents for each unit Id based on the subcomponentsFromType
                        components[i] = _getSubComponents(subcomponentsFromType, unitIds[i]);
                        numComponents[i] = uint32(components[i].length);
                        maxNumComponents += numComponents[i];
                    }
                    // Lists of components are sorted, take unique values in ascending order
                    uint32[] memory allComponents = new uint32[](maxNumComponents);
                    // Processed component counter
                    uint32[] memory processedComponents = new uint32[](numUnits);
                    // Minimal component Id
                    uint32 minComponent;
                    // Overall component counter
                    uint32 counter;
                    // Iterate until we process all components, at the maximum of the sum of all the components in all units
                    for (counter = 0; counter < maxNumComponents; ++counter) {
                        // Index of a minimal component
                        uint32 minIdxComponent;
                        // Amount of components identified as the next minimal component number
                        uint32 numComponentsCheck;
                        uint32 tryMinComponent = type(uint32).max;
                        // Assemble an array of all first components from each component array
                        for (uint32 i = 0; i < numUnits; ++i) {
                            // Either get a component that has a higher id than the last one ore reach the end of the processed Ids
                            for (; processedComponents[i] < numComponents[i]; ++processedComponents[i]) {
                                if (minComponent < components[i][processedComponents[i]]) {
                                    // Out of those component Ids that are higher than the last one, pick the minimal one
                                    if (components[i][processedComponents[i]] < tryMinComponent) {
                                        tryMinComponent = components[i][processedComponents[i]];
                                        minIdxComponent = i;
                                    }
                                    // If we found a minimal component Id, we increase the counter and break to start the search again
                                    numComponentsCheck++;
                                    break;
                                }
                            }
                        }
                        minComponent = tryMinComponent;
                        // If minimal component Id is greater than the last one, it should be added, otherwise we reached the end
                        if (numComponentsCheck > 0) {
                            allComponents[counter] = minComponent;
                            processedComponents[minIdxComponent]++;
                        } else {
                            break;
                        }
                    }
                    // Return the exact set of found subcomponents with the counter length
                    subComponentIds = new uint32[](counter);
                    for (uint32 i = 0; i < counter; ++i) {
                        subComponentIds[i] = allComponents[i];
                    }
                }
                /// @dev Gets the hash of the unit.
                /// @param unitId Unit Id.
                /// @return Unit hash.
                function _getUnitHash(uint256 unitId) internal view override returns (bytes32) {
                    return mapUnits[unitId].unitHash;
                }
            }
            // SPDX-License-Identifier: MIT
            pragma solidity ^0.8.15;
            /// @dev Required interface for the component / agent manipulation.
            interface IRegistry {
                enum UnitType {
                    Component,
                    Agent
                }
                /// @dev Creates component / agent.
                /// @param unitOwner Owner of the component / agent.
                /// @param unitHash IPFS hash of the component / agent.
                /// @param dependencies Set of component dependencies in a sorted ascending order.
                /// @return The id of a minted component / agent.
                function create(
                    address unitOwner,
                    bytes32 unitHash,
                    uint32[] memory dependencies
                ) external returns (uint256);
                /// @dev Updates the component / agent hash.
                /// @param owner Owner of the component / agent.
                /// @param unitId Unit Id.
                /// @param unitHash Updated IPFS hash of the component / agent.
                /// @return success True, if function executed successfully.
                function updateHash(address owner, uint256 unitId, bytes32 unitHash) external returns (bool success);
                /// @dev Gets subcomponents of a provided unit Id from a local public map.
                /// @param unitId Unit Id.
                /// @return subComponentIds Set of subcomponents.
                /// @return numSubComponents Number of subcomponents.
                function getLocalSubComponents(uint256 unitId) external view returns (uint32[] memory subComponentIds, uint256 numSubComponents);
                /// @dev Calculates the set of subcomponent Ids.
                /// @param unitIds Set of unit Ids.
                /// @return subComponentIds Subcomponent Ids.
                function calculateSubComponents(uint32[] memory unitIds) external view returns (uint32[] memory subComponentIds);
                /// @dev Gets updated component / agent hashes.
                /// @param unitId Unit Id.
                /// @return numHashes Number of hashes.
                /// @return unitHashes The list of component / agent hashes.
                function getUpdatedHashes(uint256 unitId) external view returns (uint256 numHashes, bytes32[] memory unitHashes);
                /// @dev Gets the total supply of components / agents.
                /// @return Total supply.
                function totalSupply() external view returns (uint256);
            }
            // SPDX-License-Identifier: MIT
            pragma solidity ^0.8.15;
            import "../lib/solmate/src/tokens/ERC721.sol";
            import "./interfaces/IErrorsRegistries.sol";
            /// @title Generic Registry - Smart contract for generic registry template
            /// @author Aleksandr Kuperman - <[email protected]>
            abstract contract GenericRegistry is IErrorsRegistries, ERC721 {
                event OwnerUpdated(address indexed owner);
                event ManagerUpdated(address indexed manager);
                event BaseURIChanged(string baseURI);
                // Owner address
                address public owner;
                // Unit manager
                address public manager;
                // Base URI
                string public baseURI;
                // Unit counter
                uint256 public totalSupply;
                // Reentrancy lock
                uint256 internal _locked = 1;
                // To better understand the CID anatomy, please refer to: https://proto.school/anatomy-of-a-cid/05
                // CID = <multibase_encoding>multibase_encoding(<cid-version><multicodec><multihash-algorithm><multihash-length><multihash-hash>)
                // CID prefix = <multibase_encoding>multibase_encoding(<cid-version><multicodec><multihash-algorithm><multihash-length>)
                // to complement the multibase_encoding(<multihash-hash>)
                // multibase_encoding = base16 = "f"
                // cid-version = version 1 = "0x01"
                // multicodec = dag-pb = "0x70"
                // multihash-algorithm = sha2-256 = "0x12"
                // multihash-length = 256 bits = "0x20"
                string public constant CID_PREFIX = "f01701220";
                /// @dev Changes the owner address.
                /// @param newOwner Address of a new owner.
                function changeOwner(address newOwner) external virtual {
                    // Check for the ownership
                    if (msg.sender != owner) {
                        revert OwnerOnly(msg.sender, owner);
                    }
                    // Check for the zero address
                    if (newOwner == address(0)) {
                        revert ZeroAddress();
                    }
                    owner = newOwner;
                    emit OwnerUpdated(newOwner);
                }
                /// @dev Changes the unit manager.
                /// @param newManager Address of a new unit manager.
                function changeManager(address newManager) external virtual {
                    if (msg.sender != owner) {
                        revert OwnerOnly(msg.sender, owner);
                    }
                    // Check for the zero address
                    if (newManager == address(0)) {
                        revert ZeroAddress();
                    }
                    manager = newManager;
                    emit ManagerUpdated(newManager);
                }
                /// @dev Checks for the unit existence.
                /// @notice Unit counter starts from 1.
                /// @param unitId Unit Id.
                /// @return true if the unit exists, false otherwise.
                function exists(uint256 unitId) external view virtual returns (bool) {
                    return unitId > 0 && unitId < (totalSupply + 1);
                }
                
                /// @dev Sets unit base URI.
                /// @param bURI Base URI string.
                function setBaseURI(string memory bURI) external virtual {
                    // Check for the ownership
                    if (msg.sender != owner) {
                        revert OwnerOnly(msg.sender, owner);
                    }
                    // Check for the zero value
                    if (bytes(bURI).length == 0) {
                        revert ZeroValue();
                    }
                    baseURI = bURI;
                    emit BaseURIChanged(bURI);
                }
                /// @dev Gets the valid unit Id from the provided index.
                /// @notice Unit counter starts from 1.
                /// @param id Unit counter.
                /// @return unitId Unit Id.
                function tokenByIndex(uint256 id) external view virtual returns (uint256 unitId) {
                    unitId = id + 1;
                    if (unitId > totalSupply) {
                        revert Overflow(unitId, totalSupply);
                    }
                }
                // Open sourced from: https://stackoverflow.com/questions/67893318/solidity-how-to-represent-bytes32-as-string
                /// @dev Converts bytes16 input data to hex16.
                /// @notice This method converts bytes into the same bytes-character hex16 representation.
                /// @param data bytes16 input data.
                /// @return result hex16 conversion from the input bytes16 data.
                function _toHex16(bytes16 data) internal pure returns (bytes32 result) {
                    result = bytes32 (data) & 0xFFFFFFFFFFFFFFFF000000000000000000000000000000000000000000000000 |
                    (bytes32 (data) & 0x0000000000000000FFFFFFFFFFFFFFFF00000000000000000000000000000000) >> 64;
                    result = result & 0xFFFFFFFF000000000000000000000000FFFFFFFF000000000000000000000000 |
                    (result & 0x00000000FFFFFFFF000000000000000000000000FFFFFFFF0000000000000000) >> 32;
                    result = result & 0xFFFF000000000000FFFF000000000000FFFF000000000000FFFF000000000000 |
                    (result & 0x0000FFFF000000000000FFFF000000000000FFFF000000000000FFFF00000000) >> 16;
                    result = result & 0xFF000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000 |
                    (result & 0x00FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF0000) >> 8;
                    result = (result & 0xF000F000F000F000F000F000F000F000F000F000F000F000F000F000F000F000) >> 4 |
                    (result & 0x0F000F000F000F000F000F000F000F000F000F000F000F000F000F000F000F00) >> 8;
                    result = bytes32 (0x3030303030303030303030303030303030303030303030303030303030303030 +
                    uint256 (result) +
                        (uint256 (result) + 0x0606060606060606060606060606060606060606060606060606060606060606 >> 4 &
                        0x0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F) * 39);
                }
                /// @dev Gets the hash of the unit.
                /// @param unitId Unit Id.
                /// @return Unit hash.
                function _getUnitHash(uint256 unitId) internal view virtual returns (bytes32);
                /// @dev Returns unit token URI.
                /// @notice Expected multicodec: dag-pb; hashing function: sha2-256, with base16 encoding and leading CID_PREFIX removed.
                /// @param unitId Unit Id.
                /// @return Unit token URI string.
                function tokenURI(uint256 unitId) public view virtual override returns (string memory) {
                    bytes32 unitHash = _getUnitHash(unitId);
                    // Parse 2 parts of bytes32 into left and right hex16 representation, and concatenate into string
                    // adding the base URI and a cid prefix for the full base16 multibase prefix IPFS hash representation
                    return string(abi.encodePacked(baseURI, CID_PREFIX, _toHex16(bytes16(unitHash)),
                        _toHex16(bytes16(unitHash << 128))));
                }
            }
            // SPDX-License-Identifier: MIT
            pragma solidity >=0.8.0;
            /// @notice Modern, minimalist, and gas efficient ERC-721 implementation.
            /// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/tokens/ERC721.sol)
            abstract contract ERC721 {
                /*//////////////////////////////////////////////////////////////
                                             EVENTS
                //////////////////////////////////////////////////////////////*/
                event Transfer(address indexed from, address indexed to, uint256 indexed id);
                event Approval(address indexed owner, address indexed spender, uint256 indexed id);
                event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
                /*//////////////////////////////////////////////////////////////
                                     METADATA STORAGE/LOGIC
                //////////////////////////////////////////////////////////////*/
                string public name;
                string public symbol;
                function tokenURI(uint256 id) public view virtual returns (string memory);
                /*//////////////////////////////////////////////////////////////
                                  ERC721 BALANCE/OWNER STORAGE
                //////////////////////////////////////////////////////////////*/
                mapping(uint256 => address) internal _ownerOf;
                mapping(address => uint256) internal _balanceOf;
                function ownerOf(uint256 id) public view virtual returns (address owner) {
                    require((owner = _ownerOf[id]) != address(0), "NOT_MINTED");
                }
                function balanceOf(address owner) public view virtual returns (uint256) {
                    require(owner != address(0), "ZERO_ADDRESS");
                    return _balanceOf[owner];
                }
                /*//////////////////////////////////////////////////////////////
                                     ERC721 APPROVAL STORAGE
                //////////////////////////////////////////////////////////////*/
                mapping(uint256 => address) public getApproved;
                mapping(address => mapping(address => bool)) public isApprovedForAll;
                /*//////////////////////////////////////////////////////////////
                                           CONSTRUCTOR
                //////////////////////////////////////////////////////////////*/
                constructor(string memory _name, string memory _symbol) {
                    name = _name;
                    symbol = _symbol;
                }
                /*//////////////////////////////////////////////////////////////
                                          ERC721 LOGIC
                //////////////////////////////////////////////////////////////*/
                function approve(address spender, uint256 id) public virtual {
                    address owner = _ownerOf[id];
                    require(msg.sender == owner || isApprovedForAll[owner][msg.sender], "NOT_AUTHORIZED");
                    getApproved[id] = spender;
                    emit Approval(owner, spender, id);
                }
                function setApprovalForAll(address operator, bool approved) public virtual {
                    isApprovedForAll[msg.sender][operator] = approved;
                    emit ApprovalForAll(msg.sender, operator, approved);
                }
                function transferFrom(
                    address from,
                    address to,
                    uint256 id
                ) public virtual {
                    require(from == _ownerOf[id], "WRONG_FROM");
                    require(to != address(0), "INVALID_RECIPIENT");
                    require(
                        msg.sender == from || isApprovedForAll[from][msg.sender] || msg.sender == getApproved[id],
                        "NOT_AUTHORIZED"
                    );
                    // Underflow of the sender's balance is impossible because we check for
                    // ownership above and the recipient's balance can't realistically overflow.
                    unchecked {
                        _balanceOf[from]--;
                        _balanceOf[to]++;
                    }
                    _ownerOf[id] = to;
                    delete getApproved[id];
                    emit Transfer(from, to, id);
                }
                function safeTransferFrom(
                    address from,
                    address to,
                    uint256 id
                ) public virtual {
                    transferFrom(from, to, id);
                    if (to.code.length != 0)
                        require(
                            ERC721TokenReceiver(to).onERC721Received(msg.sender, from, id, "") ==
                                ERC721TokenReceiver.onERC721Received.selector,
                            "UNSAFE_RECIPIENT"
                        );
                }
                function safeTransferFrom(
                    address from,
                    address to,
                    uint256 id,
                    bytes calldata data
                ) public virtual {
                    transferFrom(from, to, id);
                    if (to.code.length != 0)
                        require(
                            ERC721TokenReceiver(to).onERC721Received(msg.sender, from, id, data) ==
                                ERC721TokenReceiver.onERC721Received.selector,
                            "UNSAFE_RECIPIENT"
                        );
                }
                /*//////////////////////////////////////////////////////////////
                                          ERC165 LOGIC
                //////////////////////////////////////////////////////////////*/
                function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {
                    return
                        interfaceId == 0x01ffc9a7 || // ERC165 Interface ID for ERC165
                        interfaceId == 0x80ac58cd || // ERC165 Interface ID for ERC721
                        interfaceId == 0x5b5e139f; // ERC165 Interface ID for ERC721Metadata
                }
                /*//////////////////////////////////////////////////////////////
                                    INTERNAL MINT/BURN LOGIC
                //////////////////////////////////////////////////////////////*/
                function _mint(address to, uint256 id) internal virtual {
                    require(to != address(0), "INVALID_RECIPIENT");
                    require(_ownerOf[id] == address(0), "ALREADY_MINTED");
                    // Counter overflow is incredibly unrealistic.
                    unchecked {
                        _balanceOf[to]++;
                    }
                    _ownerOf[id] = to;
                    emit Transfer(address(0), to, id);
                }
                function _burn(uint256 id) internal virtual {
                    address owner = _ownerOf[id];
                    require(owner != address(0), "NOT_MINTED");
                    // Ownership check above ensures no underflow.
                    unchecked {
                        _balanceOf[owner]--;
                    }
                    delete _ownerOf[id];
                    delete getApproved[id];
                    emit Transfer(owner, address(0), id);
                }
                /*//////////////////////////////////////////////////////////////
                                    INTERNAL SAFE MINT LOGIC
                //////////////////////////////////////////////////////////////*/
                function _safeMint(address to, uint256 id) internal virtual {
                    _mint(to, id);
                    if (to.code.length != 0)
                        require(
                            ERC721TokenReceiver(to).onERC721Received(msg.sender, address(0), id, "") ==
                                ERC721TokenReceiver.onERC721Received.selector,
                            "UNSAFE_RECIPIENT"
                        );
                }
                function _safeMint(
                    address to,
                    uint256 id,
                    bytes memory data
                ) internal virtual {
                    _mint(to, id);
                    if (to.code.length != 0)
                        require(
                            ERC721TokenReceiver(to).onERC721Received(msg.sender, address(0), id, data) ==
                                ERC721TokenReceiver.onERC721Received.selector,
                            "UNSAFE_RECIPIENT"
                        );
                }
            }
            /// @notice A generic interface for a contract which properly accepts ERC721 tokens.
            /// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/tokens/ERC721.sol)
            abstract contract ERC721TokenReceiver {
                function onERC721Received(
                    address,
                    address,
                    uint256,
                    bytes calldata
                ) external virtual returns (bytes4) {
                    return ERC721TokenReceiver.onERC721Received.selector;
                }
            }
            // SPDX-License-Identifier: MIT
            pragma solidity ^0.8.15;
            /// @dev Errors.
            interface IErrorsRegistries {
                /// @dev Only `manager` has a privilege, but the `sender` was provided.
                /// @param sender Sender address.
                /// @param manager Required sender address as a manager.
                error ManagerOnly(address sender, address manager);
                /// @dev Only `owner` has a privilege, but the `sender` was provided.
                /// @param sender Sender address.
                /// @param owner Required sender address as an owner.
                error OwnerOnly(address sender, address owner);
                /// @dev Hash already exists in the records.
                error HashExists();
                /// @dev Provided zero address.
                error ZeroAddress();
                /// @dev Agent Id is not correctly provided for the current routine.
                /// @param agentId Component Id.
                error WrongAgentId(uint256 agentId);
                /// @dev Wrong length of two arrays.
                /// @param numValues1 Number of values in a first array.
                /// @param numValues2 Numberf of values in a second array.
                error WrongArrayLength(uint256 numValues1, uint256 numValues2);
                /// @dev Canonical agent Id is not found.
                /// @param agentId Canonical agent Id.
                error AgentNotFound(uint256 agentId);
                /// @dev Component Id is not found.
                /// @param componentId Component Id.
                error ComponentNotFound(uint256 componentId);
                /// @dev Multisig threshold is out of bounds.
                /// @param currentThreshold Current threshold value.
                /// @param minThreshold Minimum possible threshold value.
                /// @param maxThreshold Maximum possible threshold value.
                error WrongThreshold(uint256 currentThreshold, uint256 minThreshold, uint256 maxThreshold);
                /// @dev Agent instance is already registered with a specified `operator`.
                /// @param operator Operator that registered an instance.
                error AgentInstanceRegistered(address operator);
                /// @dev Wrong operator is specified when interacting with a specified `serviceId`.
                /// @param serviceId Service Id.
                error WrongOperator(uint256 serviceId);
                /// @dev Operator has no registered instances in the service.
                /// @param operator Operator address.
                /// @param serviceId Service Id.
                error OperatorHasNoInstances(address operator, uint256 serviceId);
                /// @dev Canonical `agentId` is not found as a part of `serviceId`.
                /// @param agentId Canonical agent Id.
                /// @param serviceId Service Id.
                error AgentNotInService(uint256 agentId, uint256 serviceId);
                /// @dev The contract is paused.
                error Paused();
                /// @dev Zero value when it has to be different from zero.
                error ZeroValue();
                /// @dev Value overflow.
                /// @param provided Overflow value.
                /// @param max Maximum possible value.
                error Overflow(uint256 provided, uint256 max);
                /// @dev Service must be inactive.
                /// @param serviceId Service Id.
                error ServiceMustBeInactive(uint256 serviceId);
                /// @dev All the agent instance slots for a specific `serviceId` are filled.
                /// @param serviceId Service Id.
                error AgentInstancesSlotsFilled(uint256 serviceId);
                /// @dev Wrong state of a service.
                /// @param state Service state.
                /// @param serviceId Service Id.
                error WrongServiceState(uint256 state, uint256 serviceId);
                /// @dev Only own service multisig is allowed.
                /// @param provided Provided address.
                /// @param expected Expected multisig address.
                /// @param serviceId Service Id.
                error OnlyOwnServiceMultisig(address provided, address expected, uint256 serviceId);
                /// @dev Multisig is not whitelisted.
                /// @param multisig Address of a multisig implementation.
                error UnauthorizedMultisig(address multisig);
                /// @dev Incorrect deposit provided for the registration activation.
                /// @param sent Sent amount.
                /// @param expected Expected amount.
                /// @param serviceId Service Id.
                error IncorrectRegistrationDepositValue(uint256 sent, uint256 expected, uint256 serviceId);
                /// @dev Insufficient value provided for the agent instance bonding.
                /// @param sent Sent amount.
                /// @param expected Expected amount.
                /// @param serviceId Service Id.
                error IncorrectAgentBondingValue(uint256 sent, uint256 expected, uint256 serviceId);
                /// @dev Failure of a transfer.
                /// @param token Address of a token.
                /// @param from Address `from`.
                /// @param to Address `to`.
                /// @param value Value.
                error TransferFailed(address token, address from, address to, uint256 value);
                /// @dev Caught reentrancy violation.
                error ReentrancyGuard();
            }