{"file_path":"apps/tokenization/SygFactory/contracts/SygFactory.sol","creation_status":"success","source_code":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.30;\n\nimport {Initializable} from \"libs/Initializable/Initializable.sol\";\nimport {UUPSTimelockUpgradeable} from \"libs/UUPS/UUPSTimelockUpgradeable.sol\";\nimport {IERC20} from \"libs/ERC20/interfaces/IERC20.sol\";\nimport {IPausable} from \"libs/Pausable/interfaces/IPausable.sol\";\nimport {RolesManager} from \"./RolesManager.sol\";\nimport {TokenManager} from \"./TokenManager.sol\";\nimport {ISygToken} from \"apps/tokenization/SygToken/interfaces/ISygToken.sol\";\nimport {Uint8ArrayUtils} from \"libs/utils/array/Uint8ArrayUtils.sol\";\nimport {\n    ISygDestroyedImplementation\n} from \"apps/tokenization/SygTokenDestroyed/contracts/interfaces/ISygDestroyedImplementation.sol\";\n\n/**\n * @title SygFactory\n * @author SYGNUM AG - Blockchain team 3301\n * @notice Factory contract for deploying and managing SygToken instances with role-based access control\n * @dev This contract manages the creation and configuration of tokenized assets.\n *      It provides functionality for deploying new tokens, managing their permissions,\n *      and maintaining a registry of all created tokens.\n *      Uses UUPSTimelockUpgradeable for secure, delayed upgrades.\n */\n// TODO: Inherit from ISygFactory\n/**\n * @custom:security-contact blockchain@sygnum.com\n */\ncontract SygFactory is Initializable, UUPSTimelockUpgradeable, RolesManager, TokenManager, Uint8ArrayUtils {\n    /* ======== ERRORS ======== */\n\n    error ContractNotInitializedAfterUpgrade();\n    error InvalidDestroyedImplementation(address implementation);\n    error InvalidPlatformAdmin(address platformAdmin);\n    error InvalidRoleManager(address roleManager);\n\n    /* ======== INITIALIZER ======== */\n    \n    /**\n     * @notice Initializes the SygFactory contract\n     * @param _initialImplementation The initial implementation address\n     * @param _destroyedImplementation The destroyed implementation address for SygTokenDestroyed\n     * @param _platformAdmin The address to be assigned the PLATFORM_ADMIN role\n     * @param _roleManager The address to be assigned the ROLE_MANAGER role\n     * @param _timelockDelaySeconds The delay in seconds before upgrades can be executed\n     */\n    function initialize(\n        address _initialImplementation,\n        address _destroyedImplementation,\n        address _platformAdmin,\n        address _roleManager,\n        uint256 _timelockDelaySeconds\n    ) external onlyProxy initializer {\n        // Validate inputs\n        // Note: _initialImplementation validation happens in _tokenManager_init -> _updateSygTokenImplementation\n        if (_destroyedImplementation.code.length == 0 || !ISygDestroyedImplementation(_destroyedImplementation).isSygTokenDestroyedImplementation()) {\n            revert InvalidDestroyedImplementation(_destroyedImplementation);\n        }\n        if (_platformAdmin == address(0)) {\n            revert InvalidPlatformAdmin(_platformAdmin);\n        }\n        if (_roleManager == address(0)) {\n            revert InvalidRoleManager(_roleManager);\n        }\n\n        _tokenManager_init(_initialImplementation);\n        if (getSygTokenDestroyedImplementation == address(0)) {\n            getSygTokenDestroyedImplementation = _destroyedImplementation;\n        }\n        _UUPSTimelock_init(_timelockDelaySeconds);\n\n        /* ======== SET ROLES ======== */\n\n        // Set initial PLATFORM_ADMIN role\n        _addUserRole(DEFAULT_GROUP, _platformAdmin, uint8(Role.PLATFORM_ADMIN));\n        // Set initial roles for the role manager\n        _addUserRole(DEFAULT_GROUP, _roleManager, uint8(Role.ROLE_MANAGER));\n\n\n        /* ======== SYGFACTORY PERMISSIONS ======== */\n\n        // PUBLIC PERMISSIONS\n        _setPublicPermission(this.executeUpgrade.selector);\n        _setPublicPermission(this.executeTokenUpgrade.selector);\n\n        // Functions that can be called by PLATFORM_ADMIN only\n        _setRolesPermission(this.cancelUpgrade.selector, _uint8Array(uint8(Role.PLATFORM_ADMIN)));\n        _setRolesPermission(this.updateSygTokenImplementation.selector, _uint8Array(uint8(Role.PLATFORM_ADMIN)));\n        _setRolesPermission(this.upgradeToAndCall.selector, _uint8Array(uint8(Role.PLATFORM_ADMIN)));\n        _setRolesPermission(this.upgradeToken.selector, _uint8Array(uint8(Role.PLATFORM_ADMIN)));\n        _setRolesPermission(this.cancelTokenUpgrade.selector, _uint8Array(uint8(Role.PLATFORM_ADMIN)));\n        _setRolesPermission(this.destroyToken.selector, _uint8Array(uint8(Role.PLATFORM_ADMIN)));\n        _setRolesPermission(this.updatePlatformAdmin.selector, _uint8Array(uint8(Role.PLATFORM_ADMIN)));\n        _setRolesPermission(this.updateRoleManager.selector, _uint8Array(uint8(Role.PLATFORM_ADMIN)));\n        _setRolesPermission(this.updatePauser.selector, _uint8Array(uint8(Role.PLATFORM_ADMIN)));\n\n        // Functions that can be called by ROLE_MANAGER only\n        _setRolesPermission(this.updateOperator.selector, _uint8Array(uint8(Role.ROLE_MANAGER)));\n        _setRolesPermission(this.updateSystem.selector, _uint8Array(uint8(Role.ROLE_MANAGER)));\n        _setRolesPermission(this.enableCustomGroupId.selector, _uint8Array(uint8(Role.ROLE_MANAGER)));\n        _setRolesPermission(this.updateTokenGroupIds.selector, _uint8Array(uint8(Role.ROLE_MANAGER)));\n\n        // Functions that can be called by OPERATOR only\n        _setRolesPermission(this.deployToken.selector, _uint8Array(uint8(Role.OPERATOR)));\n\n        // Functions shared by multiple roles for managing token users\n        _setRolesPermission(this.updateTokenUser.selector, _uint8Array(uint8(Role.OPERATOR), uint8(Role.SYSTEM), uint8(Role.TOKEN)));\n        _setRolesPermission(this.blacklistTokenUser.selector, _uint8Array(uint8(Role.OPERATOR), uint8(Role.SYSTEM), uint8(Role.TOKEN)));\n        _setRolesPermission(this.updateMinterBurner.selector, _uint8Array(uint8(Role.ROLE_MANAGER), uint8(Role.TOKEN)));\n        _setRolesPermission(this.updateTokenUserManager.selector, _uint8Array(uint8(Role.ROLE_MANAGER), uint8(Role.TOKEN)));\n\n        /* ======== SYGTOKEN PERMISSIONS ======== */\n\n        // PUBLIC PERMISSIONS for ERC20 functions\n        _setPublicPermission(IERC20.transferFrom.selector);\n        _setPublicPermission(IERC20.permit.selector);\n\n        // Functions that can be called by PLATFORM_ADMIN only\n        _setRolesPermission(ISygToken.updatePricefeedOracleAddress.selector, _uint8Array(uint8(Role.PLATFORM_ADMIN)));\n\n        // Functions that can be called by ROLE_MANAGER only\n        _setRolesPermission(ISygToken.updateMinterBurner.selector, _uint8Array(uint8(Role.ROLE_MANAGER)));\n        _setRolesPermission(ISygToken.updateTokenUserManager.selector, _uint8Array(uint8(Role.ROLE_MANAGER)));\n\n        // Functions that can be called by OPERATOR only\n        _setRolesPermission(ISygToken.forcedTransfer.selector, _uint8Array(uint8(Role.OPERATOR)));\n        _setRolesPermission(ISygToken.updateTokenURI.selector, _uint8Array(uint8(Role.OPERATOR)));\n        _setRolesPermission(ISygToken.recoverFunds.selector, _uint8Array(uint8(Role.OPERATOR)));\n\n        // Functions that can be called by PAUSER only\n        _setRolesPermission(IPausable.pause.selector, _uint8Array(uint8(Role.PAUSER)));\n        _setRolesPermission(IPausable.unpause.selector, _uint8Array(uint8(Role.PAUSER)));\n\n        // Functions that can be called by TOKEN_USER only\n        _setRolesPermission(IERC20.transfer.selector, _uint8Array(uint8(Role.TOKEN_USER)));\n        _setRolesPermission(IERC20.approve.selector, _uint8Array(uint8(Role.TOKEN_USER)));\n\n        // Functions shared by multiple roles\n        _setRolesPermission(ISygToken.updateTokenUser.selector, _uint8Array(uint8(Role.OPERATOR), uint8(Role.TOKEN_USER_MANAGER)));\n        _setRolesPermission(ISygToken.blacklistTokenUser.selector, _uint8Array(uint8(Role.OPERATOR), uint8(Role.TOKEN_USER_MANAGER)));\n        _setRolesPermission(ISygToken.mint.selector, _uint8Array(uint8(Role.OPERATOR), uint8(Role.MINTER_BURNER), uint8(Role.SYSTEM)));\n        _setRolesPermission(ISygToken.burn.selector, _uint8Array(uint8(Role.OPERATOR), uint8(Role.MINTER_BURNER), uint8(Role.SYSTEM)));\n    }\n\n    /* ======== INTERNAL/PRIVATE STATE-CHANGING FUNCTIONS ======== */\n\n    /**\n     * @notice Loads applicable permission groups based on calling context\n     * @dev When called via staticcall from a token contract, msg.sender is the token address.\n     *      Uses this to look up token-specific groups from tokenInfoMap.\n     *      For direct calls to SygFactory, returns default group.\n     * param _callingUser The address attempting to perform the action\n     * param _functionSig The function being called\n     * @param _callingContract The contract that initiated the permission check\n     * @return Array of group IDs applicable for this authorization check\n     */\n    function _loadApplicablePermissionGroups(\n        address /* _callingUser */,\n        bytes4 /* _functionSig */,\n        address _callingContract\n    ) internal view virtual override returns (uint16[] memory) {\n        // Check if _callingContract is a known token\n        TokenInfo storage _info = tokenInfoMap[_callingContract];\n\n        // If not a registered token, return default group\n        if (_info.tokenAddress == address(0)) {\n            // Calling contract is not a token (direct call to SygFactory) - return default group\n            uint16[] memory _defaultGroup = new uint16[](1);\n            _defaultGroup[0] = DEFAULT_GROUP;\n            return _defaultGroup;\n        }\n\n        // Context is a registered token - return its groups\n        uint16 _groupIdCount = _info.groupIds[0];\n\n        // If no custom group, return standard groups\n        if (_info.customGroupId == 0) {\n            uint16[] memory _standardGroups = new uint16[](_groupIdCount);\n            for (uint16 _i = 0; _i < _groupIdCount; _i++) {\n                _standardGroups[_i] = _info.groupIds[_i + 1];\n            }\n            return _standardGroups;\n        }\n\n        // Include custom group\n        uint16[] memory _allGroups = new uint16[](_groupIdCount + 1);\n        for (uint16 _i = 0; _i < _groupIdCount; _i++) {\n            _allGroups[_i] = _info.groupIds[_i + 1];\n        }\n        _allGroups[_groupIdCount] = _info.customGroupId;\n        return _allGroups;\n    }\n\n    /**\n     * @notice Authorizes upgrade to a new implementation\n     * @dev Only callable by accounts with appropriate authorization\n     */\n    function _authorizeUpgrade(\n        address,\n        bytes memory\n    ) internal view virtual override requiresAuth {}\n\n    /**\n     * @notice Hook that is called before an upgrade is executed\n     * @dev Resets the initialized state and clears all configured permissions before upgrade.\n     *      This allows re-initialization with fresh permissions after upgrade.\n     *      Only resets when calldata is provided (i.e. re-initialization is expected).\n     *      Empty calldata allows code-only upgrades without re-initialization.\n     *      The _postUpgradeToAndCallUUPS hook enforces that initialization happened,\n     *      so passing non-initialize calldata will revert the entire transaction.\n     */\n    function _preUpgradeToAndCallUUPS(address, bytes memory _data) internal virtual override {\n        if (_data.length >= 4) {\n            _resetInitialized();\n            // Reset all permission configurations for SygFactory functions\n            _resetPermissions(this.executeUpgrade.selector);\n            _resetPermissions(this.executeTokenUpgrade.selector);\n            _resetPermissions(this.cancelUpgrade.selector);\n            _resetPermissions(this.updateSygTokenImplementation.selector);\n            _resetPermissions(this.upgradeToAndCall.selector);\n            _resetPermissions(this.upgradeToken.selector);\n            _resetPermissions(this.cancelTokenUpgrade.selector);\n            _resetPermissions(this.destroyToken.selector);\n            _resetPermissions(this.updatePlatformAdmin.selector);\n            _resetPermissions(this.updateRoleManager.selector);\n            _resetPermissions(this.updatePauser.selector);\n            _resetPermissions(this.updateOperator.selector);\n            _resetPermissions(this.updateSystem.selector);\n            _resetPermissions(this.enableCustomGroupId.selector);\n            _resetPermissions(this.updateTokenGroupIds.selector);\n            _resetPermissions(this.deployToken.selector);\n            _resetPermissions(this.updateTokenUser.selector);\n            _resetPermissions(this.blacklistTokenUser.selector);\n            _resetPermissions(this.updateMinterBurner.selector);\n            _resetPermissions(this.updateTokenUserManager.selector);\n            // Reset SygToken permissions\n            _resetPermissions(IERC20.transferFrom.selector);\n            _resetPermissions(IERC20.permit.selector);\n            _resetPermissions(ISygToken.updatePricefeedOracleAddress.selector);\n            _resetPermissions(ISygToken.updateMinterBurner.selector);\n            _resetPermissions(ISygToken.updateTokenUserManager.selector);\n            _resetPermissions(ISygToken.forcedTransfer.selector);\n            _resetPermissions(ISygToken.updateTokenURI.selector);\n            _resetPermissions(ISygToken.recoverFunds.selector);\n            _resetPermissions(IPausable.pause.selector);\n            _resetPermissions(IPausable.unpause.selector);\n            _resetPermissions(IERC20.transfer.selector);\n            _resetPermissions(IERC20.approve.selector);\n            _resetPermissions(ISygToken.updateTokenUser.selector);\n            _resetPermissions(ISygToken.blacklistTokenUser.selector);\n            _resetPermissions(ISygToken.mint.selector);\n            _resetPermissions(ISygToken.burn.selector);\n        }\n    }\n\n    /**\n     * @notice Hook that is called after an upgrade is executed\n     * @dev Enforces that initialization must happen after upgrade\n     */\n    function _postUpgradeToAndCallUUPS(\n        address,\n        bytes memory\n    ) internal virtual override {\n        // Enforce that initialization must happen after upgrade\n        if (!isInitialized()) {\n            revert ContractNotInitializedAfterUpgrade();\n        }\n    }\n}\n","deployed_bytecode":"0x608060405234801561000f575f5ffd5b5060043610610273575f3560e01c8063738a49de1161014f578063aaf10f42116100ce578063d79a2aee11610093578063f5fe57871161006e578063f5fe578714610587578063f7013ef61461059a578063ffb2f26f146105ad575f5ffd5b8063d79a2aee1461054e578063ebd4bca814610561578063f2563ef314610574575f5ffd5b8063aaf10f42146104fa578063ab78548014610502578063adcf358c14610515578063af2bedd014610528578063be3b78441461053b575f5ffd5b806389ce7a1d1161011457806389ce7a1d1461048b578063970f84821461049e57806398d2082f146104b1578063a03175af146104d4578063a87238e7146104e7575f5ffd5b8063738a49de1461043757806378143d561461044a5780637e8962141461045d57806385302cbe1461046557806386bed3e014610478575f5ffd5b806337126e19116101f55780634f1ef286116101ba5780635bbc340f116101955780635bbc340f146103f85780636d4ce27c146104115780636f06fa4314610424575f5ffd5b80634f1ef286146103c757806352d1902d146103da57806355f29166146103f0575f5ffd5b806337126e1914610373578063374627b314610386578063392e53cd14610399578063394340e8146103a157806349cd14d8146103b4575f5ffd5b80631f69565f1161023b5780631f69565f146103045780632092a68c14610327578063256f3c831461033a57806333999fa61461034d578063361fa22b14610360575f5ffd5b8063013fe08c14610277578063039e62d31461028c578063113e68d81461029f57806315b22d84146102c75780631f1f2dc8146102da575b5f5ffd5b61028a610285366004614831565b6105c0565b005b61028a61029a366004614943565b6106cf565b6102b26102ad3660046149a7565b610812565b60405190151581526020015b60405180910390f35b61028a6102d53660046149c0565b610854565b5f546102ec906001600160a01b031681565b6040516001600160a01b0390911681526020016102be565b6103176103123660046149c0565b610a71565b6040516102be9493929190614a0f565b6102b2610335366004614943565b610b8e565b6102b2610348366004614a64565b610bbf565b61028a61035b366004614b22565b610bd5565b61028a61036e366004614831565b610be3565b61028a6103813660046149c0565b610cbc565b61028a610394366004614831565b610d4f565b6102b2610db8565b6102b26103af366004614943565b610dd3565b61028a6103c2366004614831565b610dfc565b61028a6103d5366004614b22565b610e65565b6103e2610e77565b6040519081526020016102be565b61028a610e88565b6002546102ec906201000090046001600160a01b031681565b61028a61041f366004614831565b610ebe565b6102b2610432366004614943565b610f27565b6102b2610445366004614943565b610f50565b6102b2610458366004614943565b610f79565b61028a610fa2565b61028a6104733660046149c0565b610fd6565b6102b2610486366004614b78565b6111e1565b61028a6104993660046149c0565b6111fb565b6102b26104ac366004614943565b611391565b6104c46104bf366004614bab565b6113ba565b6040516102be9493929190614bf0565b61028a6104e23660046149c0565b6113d7565b61028a6104f5366004614831565b6114e9565b6102ec611552565b61028a610510366004614831565b611568565b61028a610523366004614831565b6115d1565b6103e2610536366004614c24565b6116a8565b61028a610549366004614b22565b6116e0565b61028a61055c366004614831565b611997565b6102ec61056f366004614c77565b611a64565b6102b2610582366004614943565b611c9e565b6102b2610595366004614943565b611cc7565b61028a6105a8366004614d4a565b611cf0565b6102b26105bb366004614943565b612197565b5f6105d7335f356001600160e01b031916306121bf565b90506105ef335f356001600160e01b03191683612440565b61060b576040516282b42960e81b815260040160405180910390fd5b6106413360015b60405190808252806020026020018201604052801561063b578160200160208202803683370190505b50610b8e565b1561068257335f90815260016020526040902054600160a01b900461ffff161561068257335f90815260016020526040902054600160a01b900461ffff1693505b8361ffff165f036106a657604051631b47388560e11b815260040160405180910390fd5b81156106bd576106b88484600761254a565b6106c9565b6106c984846007612628565b50505050565b5f6106e6335f356001600160e01b031916306121bf565b90506106fe335f356001600160e01b03191683612440565b61071a576040516282b42960e81b815260040160405180910390fd5b6001600160a01b038381165f90815260016020526040902054166107615760405163961c9a4f60e01b81526001600160a01b03841660048201526024015b60405180910390fd5b8151600f1015610791578151604051636bb2e11f60e01b81526004810191909152600f6024820152604401610758565b61079a82612707565b6107a3826127cb565b6001600160a01b0384165f9081526001602081905260409091206107cb9291019060106146f2565b50826001600160a01b03167f5e8f0efc4a01d8b6bf22a64c627f5ef24eff9ea6c8edde69e4ff9edaac80f7b5836040516108059190614daa565b60405180910390a2505050565b6001600160e01b031981165f9081527f31f703e9d9d60912870f8b3156da76d11cbd7f7db67e551ddf3ac9400fb05e01602052604081205460ff165b92915050565b5f61086b335f356001600160e01b031916306121bf565b9050610883335f356001600160e01b03191683612440565b61089f576040516282b42960e81b815260040160405180910390fd5b6001600160a01b038281165f90815260016020526040902054166108e15760405163961c9a4f60e01b81526001600160a01b0383166004820152602401610758565b6002546040805163278f794360e11b81526001600160a01b036201000090930483166004820181905260248201929092525f60448201529091841690634f1ef286906064015f604051808303815f87803b15801561093d575f5ffd5b505af115801561094f573d5f5f3e3d5ffd5b505050505f836001600160a01b031663aaf10f426040518163ffffffff1660e01b8152600401602060405180830381865afa158015610990573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906109b49190614dbc565b9050816001600160a01b0316816001600160a01b031603610a2b576001600160a01b038481165f8181526001602052604080822060020180546001600160a01b03191686861617905551928516927f303bc38f64dfc9f4fbff60b5d3435366303ce1d1d9c48b8a11c6f218f310fc8d9190a36106c9565b816001600160a01b0316846001600160a01b03167f42264a70118a67db1b37fa7a23a296c45dce0e13de6823e157cc595e61cc6bb660405160405180910390a350505050565b6001600160a01b0381165f908152600160208190526040822090810154606091839182919061ffff168067ffffffffffffffff811115610ab357610ab3614877565b604051908082528060200260200182016040528015610adc578160200160208202803683370190505b5094505f5b8161ffff168161ffff161015610b615782600101816001610b029190614dff565b61ffff1660108110610b1657610b16614dd7565b601091828204019190066002029054906101000a900461ffff16868261ffff1681518110610b4657610b46614dd7565b61ffff90921660209283029190910190910152600101610ae1565b505080546002909101546001600160a01b0380831698959750169450600160a01b900461ffff1692915050565b5f5f82515f14610b9e5782610ba9565b610ba9845f336121bf565b9050610bb781856006610bbf565b949350505050565b5f610bcb848484612843565b90505b9392505050565b610bdf82826128bf565b5050565b5f610bfa335f356001600160e01b031916306121bf565b9050610c12335f356001600160e01b03191683612440565b610c2e576040516282b42960e81b815260040160405180910390fd5b610c39336001610612565b15610c7a57335f90815260016020526040902054600160a01b900461ffff1615610c7a57335f90815260016020526040902054600160a01b900461ffff1693505b8361ffff165f03610c9e57604051631b47388560e11b815260040160405180910390fd5b8115610cb0576106b884846008612914565b6106c984846008612628565b5f610cd3335f356001600160e01b031916306121bf565b9050610ceb335f356001600160e01b03191683612440565b610d07576040516282b42960e81b815260040160405180910390fd5b5f546001600160a01b039081169083168103610d41576040516387e6e41f60e01b81526001600160a01b0384166004820152602401610758565b610d4a836129f2565b505050565b5f610d66335f356001600160e01b031916306121bf565b9050610d7e335f356001600160e01b03191683612440565b610d9a576040516282b42960e81b815260040160405180910390fd5b8115610dac576106b884846002612914565b6106c984846002612628565b5f610dce5f5160206156035f395f51905f525490565b905090565b5f5f82515f14610de35782610dee565b610dee845f336121bf565b9050610bb781856001610bbf565b5f610e13335f356001600160e01b031916306121bf565b9050610e2b335f356001600160e01b03191683612440565b610e47576040516282b42960e81b815260040160405180910390fd5b8115610e59576106b884846005612914565b6106c984846005612628565b610e6d612a72565b610bdf8282612b0d565b5f610e80612b29565b610dce612b72565b5f5160206156435f395f51905f5254610eb457604051630604a9ff60e01b815260040160405180910390fd5b610ebc612b8d565b565b5f610ed5335f356001600160e01b031916306121bf565b9050610eed335f356001600160e01b03191683612440565b610f09576040516282b42960e81b815260040160405180910390fd5b8115610f1b576106b884846001612914565b6106c984846001612628565b5f5f82515f14610f375782610f42565b610f42845f336121bf565b9050610bb781856002610bbf565b5f5f82515f14610f605782610f6b565b610f6b845f336121bf565b9050610bb781856003610bbf565b5f5f82515f14610f895782610f94565b610f94845f336121bf565b9050610bb781856008610bbf565b5f5160206156435f395f51905f5254610fce57604051630604a9ff60e01b815260040160405180910390fd5b610ebc612bd8565b5f610fed335f356001600160e01b031916306121bf565b9050611005335f356001600160e01b03191683612440565b611021576040516282b42960e81b815260040160405180910390fd5b6001600160a01b038281165f90815260016020526040902054166110635760405163961c9a4f60e01b81526001600160a01b0383166004820152602401610758565b816001600160a01b0316637e8962146040518163ffffffff1660e01b81526004015f604051808303815f87803b15801561109b575f5ffd5b505af11580156110ad573d5f5f3e3d5ffd5b505050505f826001600160a01b031663aaf10f426040518163ffffffff1660e01b8152600401602060405180830381865afa1580156110ee573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906111129190614dbc565b6001600160a01b038481165f908152600160205260409020600290810180546001600160a01b03191684841690811790915590549293506201000090920416900361119c57806001600160a01b0316836001600160a01b03167f303bc38f64dfc9f4fbff60b5d3435366303ce1d1d9c48b8a11c6f218f310fc8d60405160405180910390a3505050565b806001600160a01b0316836001600160a01b03167fcd23a18833879e1f289f88200345417e6288fcb73ad4cdfdbf7759d735444ae560405160405180910390a3505050565b5f5f6111ee8484336121bf565b9050610bb7848483612440565b5f611212335f356001600160e01b031916306121bf565b905061122a335f356001600160e01b03191683612440565b611246576040516282b42960e81b815260040160405180910390fd5b6001600160a01b038281165f90815260016020526040902054166112885760405163961c9a4f60e01b81526001600160a01b0383166004820152602401610758565b6001600160a01b0382165f90815260016020526040902054600160a01b900461ffff16156112d4576040516351957bc360e01b81526001600160a01b0383166004820152602401610758565b600280546001600160a01b0384165f908152600160205260408120805461ffff60a01b191661ffff938416600160a01b021790558254909116919061131883614e19565b82546101009290920a61ffff8181021990931691831602179091556001600160a01b0384165f81815260016020908152604091829020549151600160a01b90920490931681529092507f54772c673039dff7e67910f22733b5047dca1af1f182c0a1f0e6f3bc2a4f2d1491015b60405180910390a25050565b5f5f82515f146113a157826113ac565b6113ac845f336121bf565b9050610bb781856004610bbf565b5f60605f5f6113c885612c19565b93509350935093509193509193565b5f6113ee335f356001600160e01b031916306121bf565b9050611406335f356001600160e01b03191683612440565b611422576040516282b42960e81b815260040160405180910390fd5b6001600160a01b038281165f90815260016020526040902054166114645760405163961c9a4f60e01b81526001600160a01b0383166004820152602401610758565b816001600160a01b03166355f291666040518163ffffffff1660e01b81526004015f604051808303815f87803b15801561149c575f5ffd5b505af11580156114ae573d5f5f3e3d5ffd5b50506040516001600160a01b03851692507f3977fd47dfeb4e038abdaa6918d4adddff5d93223e1feace64d8c0921268f4a491505f90a25050565b5f611500335f356001600160e01b031916306121bf565b9050611518335f356001600160e01b03191683612440565b611534576040516282b42960e81b815260040160405180910390fd5b8115611546576106b884846003612914565b6106c984846003612628565b5f610dce5f5160206156235f395f51905f525490565b5f61157f335f356001600160e01b031916306121bf565b9050611597335f356001600160e01b03191683612440565b6115b3576040516282b42960e81b815260040160405180910390fd5b81156115c5576106b884846004612914565b6106c984846004612628565b5f6115e8335f356001600160e01b031916306121bf565b9050611600335f356001600160e01b03191683612440565b61161c576040516282b42960e81b815260040160405180910390fd5b611627336001610612565b1561166857335f90815260016020526040902054600160a01b900461ffff161561166857335f90815260016020526040902054600160a01b900461ffff1693505b8361ffff165f0361168c57604051631b47388560e11b815260040160405180910390fd5b811561169d576106b884845f612914565b6106c984845f612628565b61ffff82165f9081525f5160206155e35f395f51905f52602090815260408083206001600160a01b0385168452909152812054610bce565b5f6116f7335f356001600160e01b031916306121bf565b905061170f335f356001600160e01b03191683612440565b61172b576040516282b42960e81b815260040160405180910390fd5b6001600160a01b038381165f908152600160205260409020541661176d5760405163961c9a4f60e01b81526001600160a01b0384166004820152602401610758565b5f80546040805163557887a160e11b815290516001600160a01b03928316939287169163aaf10f429160048083019260209291908290030181865afa1580156117b8573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906117dc9190614dbc565b9050806001600160a01b0316826001600160a01b03160361181b576040516339cf62f760e11b81526001600160a01b0383166004820152602401610758565b60405163278f794360e11b81526001600160a01b03861690634f1ef286906118499085908890600401614e39565b5f604051808303815f87803b158015611860575f5ffd5b505af1158015611872573d5f5f3e3d5ffd5b505050505f856001600160a01b031663aaf10f426040518163ffffffff1660e01b8152600401602060405180830381865afa1580156118b3573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906118d79190614dbc565b9050826001600160a01b0316816001600160a01b03160361194e576001600160a01b038681165f8181526001602052604080822060020180546001600160a01b03191686861617905551928616927fcd23a18833879e1f289f88200345417e6288fcb73ad4cdfdbf7759d735444ae59190a361198f565b826001600160a01b0316866001600160a01b03167f5d3407b74538c58e3044d890bde1f03062a6becac8a311a18e9c8fb6874d70ea60405160405180910390a35b505050505050565b5f6119ae335f356001600160e01b031916306121bf565b90506119c6335f356001600160e01b03191683612440565b6119e2576040516282b42960e81b815260040160405180910390fd5b6119ed336001610612565b15611a2e57335f90815260016020526040902054600160a01b900461ffff1615611a2e57335f90815260016020526040902054600160a01b900461ffff1693505b8361ffff165f03611a5257604051631b47388560e11b815260040160405180910390fd5b81156106bd576106b884846007612914565b5f5f611a7c335f356001600160e01b031916306121bf565b9050611a94335f356001600160e01b03191683612440565b611ab0576040516282b42960e81b815260040160405180910390fd5b8351600f1015611ae0578351604051636bb2e11f60e01b81526004810191909152600f6024820152604401610758565b611ae984612707565b5f80546040516001600160a01b039091169190611b14908b908b908b908b9030908b90602401614e5a565b60408051601f198184030181529181526020820180516001600160e01b031663095f889360e21b179052519091505f9083908390611b5190614787565b611b5c929190614e39565b604051809103905ff080158015611b75573d5f5f3e3d5ffd5b50604080516080810182526001600160a01b03831681525f60208201529196508692508101611ba3896127cb565b81526001600160a01b038086166020928301528781165f9081526001808452604091829020855181549587015161ffff16600160a01b0275ffffffffffffffffffffffffffffffffffffffffffff199096169416939093179390931782558301519091611c15919083019060106146f2565b5060609190910151600290910180546001600160a01b0319166001600160a01b03909216919091179055611c4b5f866006612914565b846001600160a01b03167f10f8076b658baef34666707fb5cb59c631cc7f8cdec5a2c44ded627a1a264e9784848a604051611c8893929190614eba565b60405180910390a2505050509695505050505050565b5f5f82515f14611cae5782611cb9565b611cb9845f336121bf565b9050610bb781856007610bbf565b5f5f82515f14611cd75782611ce2565b611ce2845f336121bf565b9050610bb781856005610bbf565b611cf8612a72565b5f5160206156035f395f51905f525415611d255760405163f92ee8a960e01b815260040160405180910390fd5b611d2d612d45565b6001600160a01b0384163b1580611da15750836001600160a01b031663a4d50ceb6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611d7b573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611d9f9190614eed565b155b15611dca5760405163468e3dd960e11b81526001600160a01b0385166004820152602401610758565b6001600160a01b038316611dfc576040516316ba104f60e01b81526001600160a01b0384166004820152602401610758565b6001600160a01b038216611e2e57604051630c98f89360e41b81526001600160a01b0383166004820152602401610758565b611e3785612d80565b6002546201000090046001600160a01b0316611e7d576002805475ffffffffffffffffffffffffffffffffffffffff00001916620100006001600160a01b038716021790555b611e8681612dbd565b611e925f846003612914565b611e9e5f836004612914565b611eae631fa2588560e21b612dfd565b611ebe634298165f60e11b612dfd565b611ed8632af948b360e11b611ed36003612f1f565b612f6f565b611eed6337126e1960e01b611ed36003612f1f565b611f0263278f794360e11b611ed36003612f1f565b611f17632f8ede1160e21b611ed36003612f1f565b611f2c63a03175af60e01b611ed36003612f1f565b611f4163056c8b6160e21b611ed36003612f1f565b611f5663a87238e760e01b611ed36003612f1f565b611f6b630156f0a960e71b611ed36003612f1f565b611f8063374627b360e01b611ed36003612f1f565b611f95631b53389f60e21b611ed36004612f1f565b611faa630939a29b60e31b611ed36004612f1f565b611fbf6389ce7a1d60e01b611ed36004612f1f565b611fd463039e62d360e01b611ed36004612f1f565b611fe9631d7a979560e31b611ed36001612f1f565b612003636bcd157760e11b611ed360015b60056006613128565b612017624ff82360e21b611ed36001611ffa565b61202f632b73cd6360e21b611ed360045b60066131cb565b61204463361fa22b60e01b611ed36004612028565b6120546323b872dd60e01b612dfd565b61206463d505accf60e01b612dfd565b612079632207ef2760e11b611ed36003612f1f565b61208e632cb2cacf60e11b611ed36004612f1f565b6120a36317284fc160e21b611ed36004612f1f565b6120b8639fc1d0e760e01b611ed36001612f1f565b6120cd6398cd615360e01b611ed36001612f1f565b6120e2635d3590d560e01b611ed36001612f1f565b6120f7638456cb5960e01b611ed36002612f1f565b61210c631fa5d41d60e11b611ed36002612f1f565b61212163a9059cbb60e01b611ed36007612f1f565b61213663095ea7b360e01b611ed36007612f1f565b61214d623f151360e51b611ed360015b60086131cb565b61216263c9c16fbd60e01b611ed36001612146565b61217b6340c10f1960e01b611ed360015b5f6005613128565b612190632770a7eb60e21b611ed36001612173565b5050505050565b5f5f82515f146121a757826121b2565b6121b2845f336121bf565b9050610bb781855f610bbf565b6001600160a01b038082165f908152600160205260409020805460609216612231576040805160018082528183019092525f91602080830190803683370190505090505f815f8151811061221557612215614dd7565b61ffff909216602092830291909101909101529150610bce9050565b6001810154815461ffff91821691600160a01b909104165f03612325575f8161ffff1667ffffffffffffffff81111561226c5761226c614877565b604051908082528060200260200182016040528015612295578160200160208202803683370190505b5090505f5b8261ffff168161ffff16101561231a57836001018160016122bb9190614dff565b61ffff16601081106122cf576122cf614dd7565b601091828204019190066002029054906101000a900461ffff16828261ffff16815181106122ff576122ff614dd7565b61ffff9092166020928302919091019091015260010161229a565b509250610bce915050565b5f612331826001614dff565b61ffff1667ffffffffffffffff81111561234d5761234d614877565b604051908082528060200260200182016040528015612376578160200160208202803683370190505b5090505f5b8261ffff168161ffff1610156123fb578360010181600161239c9190614dff565b61ffff16601081106123b0576123b0614dd7565b601091828204019190066002029054906101000a900461ffff16828261ffff16815181106123e0576123e0614dd7565b61ffff9092166020928302919091019091015260010161237b565b508254815161ffff600160a01b909204821691839190851690811061242257612422614dd7565b61ffff90921660209283029190910190910152925050509392505050565b6001600160e01b031982165f9081527f31f703e9d9d60912870f8b3156da76d11cbd7f7db67e551ddf3ac9400fb05e0160205260408120545f5160206155e35f395f51905f529060ff1615612499576001915050610bce565b6001600160e01b031984165f908152600282016020526040902054806124c3575f92505050610bce565b5f6124ce8786613244565b9050806124e0575f9350505050610bce565b7faaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa81811690838316161561251a575f945050505050610bce565b50167f55555555555555555555555555555555555555555555555555555555555555551615159150509392505050565b608060ff821610612573576040516365861efd60e11b815260ff82166004820152602401610758565b5f5160206155e35f395f51905f525f8061258e600285614f08565b6125989190614f24565b60ff1690505f60016125ab600286614f08565b6125b59190614f24565b61ffff87165f818152602086815260408083206001600160a01b038b1680855292528083208054600160ff97881681811b909217908a1b191691829055915191965094891693927f59bc112e2d7da8911b22d274fb46cc65bcaab4e35bac23cf3808640bf327bd4891a450505050505050565b608060ff821610612651576040516365861efd60e11b815260ff82166004820152602401610758565b5f5160206155e35f395f51905f525f8061266c600285614f08565b6126769190614f24565b60ff1690505f6001612689600286614f08565b6126939190614f24565b61ffff87165f818152602086815260408083206001600160a01b038b16808552925280832080546001808a1b1990911660ff97881691821b191691829055915191965094891693927f79b7eb7267a13f56f8d84f7c730b37c1606e5e0065419ad74777c4f8db3e285c91a450505050505050565b5f5b8151811015610bdf575f61ffff1682828151811061272957612729614dd7565b602002602001015161ffff160361275357604051638be7579560e01b815260040160405180910390fd5b6103e861ffff1682828151811061276c5761276c614dd7565b602002602001015161ffff1611156127c35781818151811061279057612790614dd7565b602090810291909101015160405163d56310e960e01b815261ffff90911660048201526103e86024820152604401610758565b600101612709565b6127d3614794565b6127db614794565b825161ffff1681525f5b835181101561283c5783818151811061280057612800614dd7565b6020026020010151828260016128169190614f3d565b6010811061282657612826614dd7565b61ffff90921660209290920201526001016127e5565b5092915050565b5f608060ff83161061286d576040516365861efd60e11b815260ff83166004820152602401610758565b5f6128788486613244565b90505f80612887600286614f08565b6128919190614f24565b60ff169050600182821c161580159081906128b457506128b28787876132bf565b155b979650505050505050565b604051309033907f16f2354671bbabcb07367cc01a78931a00a812ee080430be4e8f1a76ab58212b905f90a333301461290a5760405162e6155960e41b815260040160405180910390fd5b610bdf8282613323565b608060ff82161061293d576040516365861efd60e11b815260ff82166004820152602401610758565b5f5160206155e35f395f51905f525f80612958600285614f08565b6129629190614f24565b60ff1690505f6001612975600286614f08565b61297f9190614f24565b61ffff87165f818152602086815260408083206001600160a01b038b16808552925280832080546001808a1b90911760ff97881691821b191691829055915191965094891693927fc644f1662a0174d36c039fb87d2529ba53a9908f6268144da8e9406457858cf191a450505050505050565b6129fb81613341565b612a2357604051630c76093760e01b81526001600160a01b0382166004820152602401610758565b5f80546001600160a01b038381166001600160a01b0319831681178455604051919092169283917ffed7864e1707baf60e613235dc8c5fd0619d03df5faff1ce7be98c30620124f29190a35050565b306001600160a01b037f00000000000000000000000011b2af61c6433e898f8a24138fc58318ce543aff161480612aef57507f00000000000000000000000011b2af61c6433e898f8a24138fc58318ce543aff6001600160a01b0316612ae35f5160206156235f395f51905f525490565b6001600160a01b031614155b15610ebc5760405163703e46dd60e11b815260040160405180910390fd5b612b15612a72565b612b1f828261338b565b610bdf82826133d6565b306001600160a01b037f00000000000000000000000011b2af61c6433e898f8a24138fc58318ce543aff1614610ebc5760405163703e46dd60e11b815260040160405180910390fd5b5f612b7b612b29565b505f5160206156235f395f51905f5290565b5f5160206156435f395f51905f5280545f612ba7826113ba565b50509150505f5f612bb7836133e0565b91509150612bc5828261338b565b612bce84613433565b50505f9092555050565b5f5160206156435f395f51905f5280545f612bf2826113ba565b50509150505f5f612c02836133e0565b91509150612c10828261338b565b612bce846134ee565b5f8181527f1626844f871e5080d673ecb96ea8a32c7bdf671ea137282b2886cd9d0fd9a70260209081526040808320815160808101835281546001600160a01b03168152600182015493810193909352600281018054606094869485945f5160206155c35f395f51905f529486949192840191612c9590614f50565b80601f0160208091040260200160405190810160405280929190818152602001828054612cc190614f50565b8015612d0c5780601f10612ce357610100808354040283529160200191612d0c565b820191905f5260205f20905b815481529060010190602001808311612cef57829003601f168201915b505050505081526020016003820154815250509050805f0151816040015182602001518360600151955095509550955050509193509193565b60015f5160206156035f395f51905f52556040517f5daa87a0e9463431830481fd4b6e3403442dfb9a12b9c07597e9f61d50b633c8905f90a1565b612d89816129f2565b60025461ffff165f03612dba57612da36103e86001614dff565b6002805461ffff191661ffff929092169190911790555b50565b7f2c9356b94ada349e65b0a691cd15e24beddfc13e05a26de25d9c64509c824401545f5160206156435f395f51905f52905f03610bdf57610bdf82613741565b6001600160e01b031981165f9081527f31f703e9d9d60912870f8b3156da76d11cbd7f7db67e551ddf3ac9400fb05e0360205260409020545f5160206155e35f395f51905f529060ff1615612e7057604051621414d760e21b81526001600160e01b031983166004820152602401610758565b6001600160e01b031982165f818152600383016020526040808220805460ff19166001179055517fd19986f002b4a71e2859179574de14eac9664b9d26d3045efee84efbce0ce3569190a26001600160e01b031982165f818152600183810160209081526040808420805460ff19168417905560028601825292839020548351928352908201527f26137813fc2dfaa8173dae8364111a1bd1788417316e36919840c78855dd003b9101611385565b6040805160018082528183019092526060915f91906020808301908036833701905050905082815f81518110612f5757612f57614dd7565b60ff9092166020928302919091019091015292915050565b80515f03612f905760405163fc3a4a2f60e01b815260040160405180910390fd5b6001600160e01b031982165f9081527f31f703e9d9d60912870f8b3156da76d11cbd7f7db67e551ddf3ac9400fb05e0360205260409020545f5160206155e35f395f51905f529060ff161561300357604051621414d760e21b81526001600160e01b031984166004820152602401610758565b6001600160e01b031983165f818152600383016020526040808220805460ff19166001179055517fd19986f002b4a71e2859179574de14eac9664b9d26d3045efee84efbce0ce3569190a25f805b83518110156130ba575f84828151811061306d5761306d614dd7565b60200260200101519050608060ff168160ff16106130a3576040516365861efd60e11b815260ff82166004820152602401610758565b6130af83826001613785565b925050600101613051565b506001600160e01b031984165f81815260028401602090815260408083208590556001860182529182902054825160ff909116151581529081018490527f26137813fc2dfaa8173dae8364111a1bd1788417316e36919840c78855dd003b910160405180910390a250505050565b604080516003808252608082019092526060915f919060208201848036833701905050905084815f8151811061316057613160614dd7565b602002602001019060ff16908160ff1681525050838160018151811061318857613188614dd7565b602002602001019060ff16908160ff168152505082816002815181106131b0576131b0614dd7565b60ff9092166020928302919091019091015290509392505050565b60408051600280825260608083018452925f92919060208301908036833701905050905083815f8151811061320257613202614dd7565b602002602001019060ff16908160ff1681525050828160018151811061322a5761322a614dd7565b60ff90921660209283029190910190910152905092915050565b5f5f5160206155e35f395f51905f5281805b84518110156132b657825f015f86838151811061327557613275614dd7565b60209081029190910181015161ffff1682528181019290925260409081015f9081206001600160a01b038a1682529092529020549190911790600101613256565b50949350505050565b5f608060ff8316106132e9576040516365861efd60e11b815260ff83166004820152602401610758565b5f6132f48486613244565b90505f6001613304600286614f08565b61330e9190614f24565b60ff169190911c600116151595945050505050565b61332d82826137f9565b6133378282613a55565b610bdf8282613aaa565b5f816001600160a01b03163b5f0361335a57505f919050565b61336382613acf565b61336e57505f919050565b61337782613b49565b61338257505f919050565b61084e82613b80565b5f6133a2335f356001600160e01b031916306121bf565b90506133ba335f356001600160e01b03191683612440565b610d4a576040516282b42960e81b815260040160405180910390fd5b610bdf8282613c42565b5f60606064835110156134065760405163fc69850b60e01b815260040160405180910390fd5b8251613416908490600490613cff565b8060200190518101906134299190614f88565b9094909350915050565b5f8181527f1626844f871e5080d673ecb96ea8a32c7bdf671ea137282b2886cd9d0fd9a70160205260409020545f5160206155c35f395f51905f529015610bdf575f8281526001808301602090815260408084208490556002808601909252832080546001600160a01b03191681559182018390559091906134b7908301826147b3565b505f60039190910181905560405183917f134fdd648feeaf30251f0157f9624ef8608ff9a042aad6d13e73f35d21d3f88d91a25050565b6134f781613d7e565b5f8181527f1626844f871e5080d673ecb96ea8a32c7bdf671ea137282b2886cd9d0fd9a70260209081526040808320815160808101835281546001600160a01b031681526001820154938101939093526002810180545f5160206155c35f395f51905f529594938401919061356b90614f50565b80601f016020809104026020016040519081016040528092919081815260200182805461359790614f50565b80156135e25780601f106135b9576101008083540402835291602001916135e2565b820191905f5260205f20905b8154815290600101906020018083116135c557829003601f168201915b50505091835250506003919091015460209182015281519082015160408301519293509091478211156136305760405162fae2d560e21b815260048101839052476024820152604401610758565b5f8681526001808701602090815260408084208490556002808a01909252832080546001600160a01b0319168155918201839055909190613673908301826147b3565b600382015f905550505f836001600160a01b031683836040516136969190615011565b5f6040518083038185875af1925050503d805f81146136d0576040519150601f19603f3d011682016040523d82523d5f602084013e6136d5565b606091505b50509050806136fa5760405163165a1d4160e01b815260048101889052602401610758565b867fbc76564aec85e3f50672da2d1ddca0390851fecefe61cd19d89dea91a8267829858585426040516137309493929190615027565b60405180910390a250505050505050565b805f03613761576040516327df2edd60e11b815260040160405180910390fd5b7f2c9356b94ada349e65b0a691cd15e24beddfc13e05a26de25d9c64509c82440155565b5f8080613793600286614f08565b61379d9190614f24565b60ff1690505f60016137b0600287614f08565b6137ba9190614f24565b60ff16905083156137db57600180821b90831b9690961795909517946137ef565b600180821b1990831b199690961695909516945b5093949350505050565b6004815110610bdf576138175f5f5160206156035f395f51905f5255565b613827631fa2588560e21b613dfb565b613837634298165f60e11b613dfb565b613847632af948b360e11b613dfb565b6138576337126e1960e01b613dfb565b61386763278f794360e11b613dfb565b613877632f8ede1160e21b613dfb565b61388763a03175af60e01b613dfb565b61389763056c8b6160e21b613dfb565b6138a763a87238e760e01b613dfb565b6138b7630156f0a960e71b613dfb565b6138c763374627b360e01b613dfb565b6138d7631b53389f60e21b613dfb565b6138e7630939a29b60e31b613dfb565b6138f76389ce7a1d60e01b613dfb565b61390763039e62d360e01b613dfb565b613917631d7a979560e31b613dfb565b613927636bcd157760e11b613dfb565b613936624ff82360e21b613dfb565b613946632b73cd6360e21b613dfb565b61395663361fa22b60e01b613dfb565b6139666323b872dd60e01b613dfb565b61397663d505accf60e01b613dfb565b613986632207ef2760e11b613dfb565b613996632cb2cacf60e11b613dfb565b6139a66317284fc160e21b613dfb565b6139b6639fc1d0e760e01b613dfb565b6139c66398cd615360e01b613dfb565b6139d6635d3590d560e01b613dfb565b6139e6638456cb5960e01b613dfb565b6139f6631fa5d41d60e11b613dfb565b613a0663a9059cbb60e01b613dfb565b613a1663095ea7b360e01b613dfb565b613a25623f151360e51b613dfb565b613a3563c9c16fbd60e01b613dfb565b613a456340c10f1960e01b613dfb565b610bdf632770a7eb60e21b613dfb565b613a5e82613ed0565b6040516001600160a01b038316907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b905f90a2805115613aa257610d4a8282613eea565b610bdf613f76565b613ab2610db8565b610bdf5760405163b3a31bfd60e01b815260040160405180910390fd5b5f6001600160a01b038216613ae557505f919050565b816001600160a01b03163b5f03613afd57505f919050565b5f5f613b0884613f95565b915091508115613b405780613b2057505f9392505050565b613b298461414a565b613b3657505f9392505050565b5060019392505050565b610bb784614215565b5f6001600160a01b038216613b5f57505f919050565b816001600160a01b03163b5f03613b7757505f919050565b61084e82614431565b60408051600481526024810182526020810180516001600160e01b0316635fa6e25b60e11b17905290515f91829182916001600160a01b03861691613bc59190615011565b5f60405180830381855afa9150503d805f8114613bfd576040519150601f19603f3d011682016040523d82523d5f602084013e613c02565b606091505b5091509150811580613c1657508051602014155b15613c2457505f9392505050565b5f81806020019051810190613c399190614eed565b95945050505050565b5f5160206156435f395f51905f525415613c6f5760405163e741fd4560e01b815260040160405180910390fd5b7f2c9356b94ada349e65b0a691cd15e24beddfc13e05a26de25d9c64509c824401545f5160206156435f395f51905f52905f90613cac9042614f3d565b90505f613cf6308686604051602401613cc6929190614e39565b60408051601f198184030181529190526020810180516001600160e01b03166319cccfd360e11b179052846144d4565b90925550505050565b6060613d0c8285516145ee565b9150613d1883836145ee565b9250613d24838361505f565b67ffffffffffffffff811115613d3c57613d3c614877565b6040519080825280601f01601f191660200182016040528015613d66576020820181803683370190505b509050828203836020860101602083015e9392505050565b5f8181527f1626844f871e5080d673ecb96ea8a32c7bdf671ea137282b2886cd9d0fd9a70160205260408120545f5160206155c35f395f51905f5291819003613dda57604051634b9aaa0960e11b815260040160405180910390fd5b80421015610d4a57604051630f0b32ff60e11b815260040160405180910390fd5b6001600160e01b031981165f8181527f31f703e9d9d60912870f8b3156da76d11cbd7f7db67e551ddf3ac9400fb05e0160209081526040808320805460ff199081169091557f31f703e9d9d60912870f8b3156da76d11cbd7f7db67e551ddf3ac9400fb05e0283528184208490557f31f703e9d9d60912870f8b3156da76d11cbd7f7db67e551ddf3ac9400fb05e039092528083208054909216909155515f5160206155e35f395f51905f5292917f6553ac4c75bde7ec33c8647fd04192e30cc2f6a1a8c73a07123396fd9b45f9b591a25050565b613ed981614603565b5f5160206156235f395f51905f5255565b60605f5f846001600160a01b031684604051613f069190615011565b5f60405180830381855af49150503d805f8114613f3e576040519150601f19603f3d011682016040523d82523d5f602084013e613f43565b606091505b509150915081610bb757805115613f5d5780518082602001fd5b60405163d6bda27560e01b815260040160405180910390fd5b3415610ebc5760405163b398979f60e01b815260040160405180910390fd5b6040516301ffc9a760e01b60248201525f908190819081906001600160a01b0386169060440160408051601f198184030181529181526020820180516001600160e01b03166301ffc9a760e01b17905251613ff09190615011565b5f60405180830381855afa9150503d805f8114614028576040519150601f19603f3d011682016040523d82523d5f602084013e61402d565b606091505b5091509150811580614040575060208151105b1561405157505f9485945092505050565b6040516001600160e01b031960248201525f9081906001600160a01b0388169060440160408051601f198184030181529181526020820180516001600160e01b03166301ffc9a760e01b179052516140a99190615011565b5f60405180830381855afa9150503d805f81146140e1576040519150601f19603f3d011682016040523d82523d5f602084013e6140e6565b606091505b50915091508115806140f9575060208151105b1561410d57506001965f9650945050505050565b5f818060200190518101906141229190614eed565b9050801561413a57506001975f975095505050505050565b5060019788975095505050505050565b6040516336372b0760e01b60248201525f90819081906001600160a01b0385169060440160408051601f198184030181529181526020820180516001600160e01b03166301ffc9a760e01b179052516141a39190615011565b5f60405180830381855afa9150503d805f81146141db576040519150601f19603f3d011682016040523d82523d5f602084013e6141e0565b606091505b50915091508115806141f3575060208151105b1561420157505f9392505050565b80806020019051810190610bb79190614eed565b60408051600481526024810182526020810180516001600160e01b03166318160ddd60e01b17905290515f91829182916001600160a01b0386169161425a9190615011565b5f60405180830381855afa9150503d805f8114614292576040519150601f19603f3d011682016040523d82523d5f602084013e614297565b606091505b50915091508115806142aa575060208151105b156142b857505f9392505050565b6040515f602482018190529081906001600160a01b0387169060440160408051601f198184030181529181526020820180516001600160e01b03166370a0823160e01b179052516143099190615011565b5f60405180830381855afa9150503d805f8114614341576040519150601f19603f3d011682016040523d82523d5f602084013e614346565b606091505b5091509150811580614359575060208151105b1561436957505f95945050505050565b6040515f60248201819052604482018190529081906001600160a01b0389169060640160408051601f198184030181529181526020820180516001600160e01b0316636eb1769f60e11b179052516143c19190615011565b5f60405180830381855afa9150503d805f81146143f9576040519150601f19603f3d011682016040523d82523d5f602084013e6143fe565b606091505b5091509150811580614411575060208151105b1561442357505f979650505050505050565b506001979650505050505050565b60408051600481526024810182526020810180516001600160e01b03166352d1902d60e01b17905290515f91829182916001600160a01b038616916144769190615011565b5f60405180830381855afa9150503d805f81146144ae576040519150601f19603f3d011682016040523d82523d5f602084013e6144b3565b606091505b50915091508115806144c6575060208151105b15613b3657505f9392505050565b5f8142106144f55760405163b7d0949760e01b815260040160405180910390fd5b5f5160206155c35f395f51905f5280543490825f61451283615072565b909155505081545f81815260018085016020908152604080842089905580516080810182526001600160a01b038c811682528184018881528284018d8152606084018d90528888526002808c0190965293909620825181546001600160a01b03191692169190911781559451938501939093555193965090929082019061459990826150ce565b5060608201518160030155905050827f8d6fedd41517ab865c3658696c50923b3493c8a93ffd016d919a599f03c7531b878388886040516145dd9493929190615027565b60405180910390a250509392505050565b5f8183106145fc5781610bce565b5090919050565b806001600160a01b03163b5f0361463857604051634c9c8ce360e01b81526001600160a01b0382166004820152602401610758565b806001600160a01b03166352d1902d6040518163ffffffff1660e01b8152600401602060405180830381865afa925050508015614692575060408051601f3d908101601f1916820190925261468f91810190615189565b60015b6146ba57604051634c9c8ce360e01b81526001600160a01b0382166004820152602401610758565b5f5160206156235f395f51905f528114610bdf57604051634c9c8ce360e01b81526001600160a01b0383166004820152602401610758565b600183019183908215614777579160200282015f5b8382111561474757835183826101000a81548161ffff021916908361ffff1602179055509260200192600201602081600101049283019260010302614707565b80156147755782816101000a81549061ffff0219169055600201602081600101049283019260010302614747565b505b506147839291506147e6565b5090565b610422806151a183390190565b6040518061020001604052806010906020820280368337509192915050565b5080546147bf90614f50565b5f825580601f106147ce575050565b601f0160209004905f5260205f2090810190612dba91905b5b80821115614783575f81556001016147e7565b803561ffff8116811461480b575f5ffd5b919050565b6001600160a01b0381168114612dba575f5ffd5b8015158114612dba575f5ffd5b5f5f5f60608486031215614843575f5ffd5b61484c846147fa565b9250602084013561485c81614810565b9150604084013561486c81614824565b809150509250925092565b634e487b7160e01b5f52604160045260245ffd5b604051601f8201601f1916810167ffffffffffffffff811182821017156148b4576148b4614877565b604052919050565b5f82601f8301126148cb575f5ffd5b813567ffffffffffffffff8111156148e5576148e5614877565b8060051b6148f56020820161488b565b91825260208185018101929081019086841115614910575f5ffd5b6020860192505b8383101561493957614928836147fa565b825260209283019290910190614917565b9695505050505050565b5f5f60408385031215614954575f5ffd5b823561495f81614810565b9150602083013567ffffffffffffffff81111561497a575f5ffd5b614986858286016148bc565b9150509250929050565b80356001600160e01b03198116811461480b575f5ffd5b5f602082840312156149b7575f5ffd5b610bce82614990565b5f602082840312156149d0575f5ffd5b8135610bce81614810565b5f8151808452602084019350602083015f5b828110156137ef57815161ffff168652602095860195909101906001016149ed565b6001600160a01b0385168152608060208201525f614a3060808301866149db565b90506001600160a01b038416604083015261ffff8316606083015295945050505050565b803560ff8116811461480b575f5ffd5b5f5f5f60608486031215614a76575f5ffd5b833567ffffffffffffffff811115614a8c575f5ffd5b614a98868287016148bc565b9350506020840135614aa981614810565b9150614ab760408501614a54565b90509250925092565b5f67ffffffffffffffff821115614ad957614ad9614877565b50601f01601f191660200190565b5f614af9614af484614ac0565b61488b565b9050828152838383011115614b0c575f5ffd5b828260208301375f602084830101529392505050565b5f5f60408385031215614b33575f5ffd5b8235614b3e81614810565b9150602083013567ffffffffffffffff811115614b59575f5ffd5b8301601f81018513614b69575f5ffd5b61498685823560208401614ae7565b5f5f60408385031215614b89575f5ffd5b8235614b9481614810565b9150614ba260208401614990565b90509250929050565b5f60208284031215614bbb575f5ffd5b5035919050565b5f81518084528060208401602086015e5f602082860101526020601f19601f83011685010191505092915050565b6001600160a01b0385168152608060208201525f614c116080830186614bc2565b6040830194909452506060015292915050565b5f5f60408385031215614c35575f5ffd5b614c3e836147fa565b91506020830135614c4e81614810565b809150509250929050565b5f82601f830112614c68575f5ffd5b610bce83833560208501614ae7565b5f5f5f5f5f5f60c08789031215614c8c575f5ffd5b863567ffffffffffffffff811115614ca2575f5ffd5b614cae89828a01614c59565b965050602087013567ffffffffffffffff811115614cca575f5ffd5b614cd689828a01614c59565b955050614ce560408801614a54565b9350606087013567ffffffffffffffff811115614d00575f5ffd5b614d0c89828a01614c59565b935050608087013567ffffffffffffffff811115614d28575f5ffd5b614d3489828a016148bc565b9699959850939692959460a09093013593505050565b5f5f5f5f5f60a08688031215614d5e575f5ffd5b8535614d6981614810565b94506020860135614d7981614810565b93506040860135614d8981614810565b92506060860135614d9981614810565b949793965091946080013592915050565b602081525f610bce60208301846149db565b5f60208284031215614dcc575f5ffd5b8151610bce81614810565b634e487b7160e01b5f52603260045260245ffd5b634e487b7160e01b5f52601160045260245ffd5b61ffff818116838216019081111561084e5761084e614deb565b5f61ffff821661ffff8103614e3057614e30614deb565b60010192915050565b6001600160a01b0383168152604060208201525f610bcb6040830184614bc2565b60c081525f614e6c60c0830189614bc2565b8281036020840152614e7e8189614bc2565b905060ff871660408401528281036060840152614e9b8187614bc2565b6001600160a01b03959095166080840152505060a00152949350505050565b6001600160a01b0384168152606060208201525f614edb6060830185614bc2565b828103604084015261493981856149db565b5f60208284031215614efd575f5ffd5b8151610bce81614824565b60ff818116838216029081169081811461283c5761283c614deb565b60ff818116838216019081111561084e5761084e614deb565b8082018082111561084e5761084e614deb565b600181811c90821680614f6457607f821691505b602082108103614f8257634e487b7160e01b5f52602260045260245ffd5b50919050565b5f5f60408385031215614f99575f5ffd5b8251614fa481614810565b602084015190925067ffffffffffffffff811115614fc0575f5ffd5b8301601f81018513614fd0575f5ffd5b8051614fde614af482614ac0565b818152866020838501011115614ff2575f5ffd5b8160208401602083015e5f602083830101528093505050509250929050565b5f82518060208501845e5f920191825250919050565b6001600160a01b0385168152836020820152608060408201525f61504e6080830185614bc2565b905082606083015295945050505050565b8181038181111561084e5761084e614deb565b5f6001820161508357615083614deb565b5060010190565b601f821115610d4a57805f5260205f20601f840160051c810160208510156150af5750805b601f840160051c820191505b81811015612190575f81556001016150bb565b815167ffffffffffffffff8111156150e8576150e8614877565b6150fc816150f68454614f50565b8461508a565b6020601f82116001811461512e575f83156151175750848201515b5f19600385901b1c1916600184901b178455612190565b5f84815260208120601f198516915b8281101561515d578785015182556020948501946001909201910161513d565b508482101561517a57868401515f19600387901b60f8161c191681555b50505050600190811b01905550565b5f60208284031215615199575f5ffd5b505191905056fe608060405260405161042238038061042283398101604081905261002291610268565b61002c8282610033565b5050610364565b61003c82610091565b6040516001600160a01b038316907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b905f90a28051156100855761008082826100ab565b505050565b61008d61013f565b5050565b61009a81610160565b5f5160206104025f395f51905f5255565b60605f5f846001600160a01b0316846040516100c79190610337565b5f60405180830381855af49150503d805f81146100ff576040519150601f19603f3d011682016040523d82523d5f602084013e610104565b606091505b5091509150816101375780511561011e5780518082602001fd5b60405163d6bda27560e01b815260040160405180910390fd5b949350505050565b341561015e5760405163b398979f60e01b815260040160405180910390fd5b565b806001600160a01b03163b5f0361019a57604051634c9c8ce360e01b81526001600160a01b03821660048201526024015b60405180910390fd5b806001600160a01b03166352d1902d6040518163ffffffff1660e01b8152600401602060405180830381865afa9250505080156101f4575060408051601f3d908101601f191682019092526101f19181019061034d565b60015b61021c57604051634c9c8ce360e01b81526001600160a01b0382166004820152602401610191565b5f5160206104025f395f51905f52811461008d57604051634c9c8ce360e01b81526001600160a01b0383166004820152602401610191565b634e487b7160e01b5f52604160045260245ffd5b5f5f60408385031215610279575f5ffd5b82516001600160a01b038116811461028f575f5ffd5b60208401519092506001600160401b038111156102aa575f5ffd5b8301601f810185136102ba575f5ffd5b80516001600160401b038111156102d3576102d3610254565b604051601f8201601f19908116603f011681016001600160401b038111828210171561030157610301610254565b604052818152828201602001871015610318575f5ffd5b8160208401602083015e5f602083830101528093505050509250929050565b5f82518060208501845e5f920191825250919050565b5f6020828403121561035d575f5ffd5b5051919050565b6092806103705f395ff3fe60806040526010600c6012565b603f565b005b5f603a7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5490565b905090565b365f5f375f5f365f845af43d5f5f3e8080156058573d5ff35b3d5ffdfea264697066735822122043d77147bce425ecaf176eaf1e360434e682a72986bc8058c86e150a074679c364736f6c634300081e0033360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc1626844f871e5080d673ecb96ea8a32c7bdf671ea137282b2886cd9d0fd9a70031f703e9d9d60912870f8b3156da76d11cbd7f7db67e551ddf3ac9400fb05e00112dd7b7d700a9bfd03edf4d4ce6f92b1f5f1a2fd81fde871f4db11384aa0b00360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc2c9356b94ada349e65b0a691cd15e24beddfc13e05a26de25d9c64509c824400a264697066735822122084b01218d808166e0da4c5496424823b3781e58eef949e4079b5bf51248414a364736f6c634300081e0033","optimization_enabled":true,"verified_twin_address_hash":null,"is_verified":true,"compiler_settings":{"evmVersion":"prague","metadata":{"appendCBOR":true,"bytecodeHash":"ipfs","useLiteralContent":false},"optimizer":{"enabled":true,"runs":300},"remappings":["@chainlink-contracts-1.3.0/=dependencies/@chainlink-contracts-1.3.0/","@gnosis-pm-safe-contracts-1.3.0/=dependencies/@gnosis-pm-safe-contracts-1.3.0/","forge-std/=dependencies/forge-std-1.9.4/src/","safe-global-safe-smart-account-1.4.1-3/=dependencies/safe-global-safe-smart-account-1.4.1-3/","solmate-6.8.0/=dependencies/solmate-6.8.0/","forge-std-1.9.4/=dependencies/forge-std-1.9.4/src/"],"viaIR":false},"optimization_runs":300,"sourcify_repo_url":null,"decoded_constructor_args":null,"compiler_version":"0.8.30+commit.73712a01","is_verified_via_verifier_alliance":true,"verified_at":"2026-04-17T11:06:28.356053Z","implementations":[],"proxy_type":null,"external_libraries":[],"creation_bytecode":"0x60a0604052306080523480156012575f5ffd5b506080516156986100395f395f8181612a7d01528181612aa60152612b3401526156985ff3fe608060405234801561000f575f5ffd5b5060043610610273575f3560e01c8063738a49de1161014f578063aaf10f42116100ce578063d79a2aee11610093578063f5fe57871161006e578063f5fe578714610587578063f7013ef61461059a578063ffb2f26f146105ad575f5ffd5b8063d79a2aee1461054e578063ebd4bca814610561578063f2563ef314610574575f5ffd5b8063aaf10f42146104fa578063ab78548014610502578063adcf358c14610515578063af2bedd014610528578063be3b78441461053b575f5ffd5b806389ce7a1d1161011457806389ce7a1d1461048b578063970f84821461049e57806398d2082f146104b1578063a03175af146104d4578063a87238e7146104e7575f5ffd5b8063738a49de1461043757806378143d561461044a5780637e8962141461045d57806385302cbe1461046557806386bed3e014610478575f5ffd5b806337126e19116101f55780634f1ef286116101ba5780635bbc340f116101955780635bbc340f146103f85780636d4ce27c146104115780636f06fa4314610424575f5ffd5b80634f1ef286146103c757806352d1902d146103da57806355f29166146103f0575f5ffd5b806337126e1914610373578063374627b314610386578063392e53cd14610399578063394340e8146103a157806349cd14d8146103b4575f5ffd5b80631f69565f1161023b5780631f69565f146103045780632092a68c14610327578063256f3c831461033a57806333999fa61461034d578063361fa22b14610360575f5ffd5b8063013fe08c14610277578063039e62d31461028c578063113e68d81461029f57806315b22d84146102c75780631f1f2dc8146102da575b5f5ffd5b61028a610285366004614831565b6105c0565b005b61028a61029a366004614943565b6106cf565b6102b26102ad3660046149a7565b610812565b60405190151581526020015b60405180910390f35b61028a6102d53660046149c0565b610854565b5f546102ec906001600160a01b031681565b6040516001600160a01b0390911681526020016102be565b6103176103123660046149c0565b610a71565b6040516102be9493929190614a0f565b6102b2610335366004614943565b610b8e565b6102b2610348366004614a64565b610bbf565b61028a61035b366004614b22565b610bd5565b61028a61036e366004614831565b610be3565b61028a6103813660046149c0565b610cbc565b61028a610394366004614831565b610d4f565b6102b2610db8565b6102b26103af366004614943565b610dd3565b61028a6103c2366004614831565b610dfc565b61028a6103d5366004614b22565b610e65565b6103e2610e77565b6040519081526020016102be565b61028a610e88565b6002546102ec906201000090046001600160a01b031681565b61028a61041f366004614831565b610ebe565b6102b2610432366004614943565b610f27565b6102b2610445366004614943565b610f50565b6102b2610458366004614943565b610f79565b61028a610fa2565b61028a6104733660046149c0565b610fd6565b6102b2610486366004614b78565b6111e1565b61028a6104993660046149c0565b6111fb565b6102b26104ac366004614943565b611391565b6104c46104bf366004614bab565b6113ba565b6040516102be9493929190614bf0565b61028a6104e23660046149c0565b6113d7565b61028a6104f5366004614831565b6114e9565b6102ec611552565b61028a610510366004614831565b611568565b61028a610523366004614831565b6115d1565b6103e2610536366004614c24565b6116a8565b61028a610549366004614b22565b6116e0565b61028a61055c366004614831565b611997565b6102ec61056f366004614c77565b611a64565b6102b2610582366004614943565b611c9e565b6102b2610595366004614943565b611cc7565b61028a6105a8366004614d4a565b611cf0565b6102b26105bb366004614943565b612197565b5f6105d7335f356001600160e01b031916306121bf565b90506105ef335f356001600160e01b03191683612440565b61060b576040516282b42960e81b815260040160405180910390fd5b6106413360015b60405190808252806020026020018201604052801561063b578160200160208202803683370190505b50610b8e565b1561068257335f90815260016020526040902054600160a01b900461ffff161561068257335f90815260016020526040902054600160a01b900461ffff1693505b8361ffff165f036106a657604051631b47388560e11b815260040160405180910390fd5b81156106bd576106b88484600761254a565b6106c9565b6106c984846007612628565b50505050565b5f6106e6335f356001600160e01b031916306121bf565b90506106fe335f356001600160e01b03191683612440565b61071a576040516282b42960e81b815260040160405180910390fd5b6001600160a01b038381165f90815260016020526040902054166107615760405163961c9a4f60e01b81526001600160a01b03841660048201526024015b60405180910390fd5b8151600f1015610791578151604051636bb2e11f60e01b81526004810191909152600f6024820152604401610758565b61079a82612707565b6107a3826127cb565b6001600160a01b0384165f9081526001602081905260409091206107cb9291019060106146f2565b50826001600160a01b03167f5e8f0efc4a01d8b6bf22a64c627f5ef24eff9ea6c8edde69e4ff9edaac80f7b5836040516108059190614daa565b60405180910390a2505050565b6001600160e01b031981165f9081527f31f703e9d9d60912870f8b3156da76d11cbd7f7db67e551ddf3ac9400fb05e01602052604081205460ff165b92915050565b5f61086b335f356001600160e01b031916306121bf565b9050610883335f356001600160e01b03191683612440565b61089f576040516282b42960e81b815260040160405180910390fd5b6001600160a01b038281165f90815260016020526040902054166108e15760405163961c9a4f60e01b81526001600160a01b0383166004820152602401610758565b6002546040805163278f794360e11b81526001600160a01b036201000090930483166004820181905260248201929092525f60448201529091841690634f1ef286906064015f604051808303815f87803b15801561093d575f5ffd5b505af115801561094f573d5f5f3e3d5ffd5b505050505f836001600160a01b031663aaf10f426040518163ffffffff1660e01b8152600401602060405180830381865afa158015610990573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906109b49190614dbc565b9050816001600160a01b0316816001600160a01b031603610a2b576001600160a01b038481165f8181526001602052604080822060020180546001600160a01b03191686861617905551928516927f303bc38f64dfc9f4fbff60b5d3435366303ce1d1d9c48b8a11c6f218f310fc8d9190a36106c9565b816001600160a01b0316846001600160a01b03167f42264a70118a67db1b37fa7a23a296c45dce0e13de6823e157cc595e61cc6bb660405160405180910390a350505050565b6001600160a01b0381165f908152600160208190526040822090810154606091839182919061ffff168067ffffffffffffffff811115610ab357610ab3614877565b604051908082528060200260200182016040528015610adc578160200160208202803683370190505b5094505f5b8161ffff168161ffff161015610b615782600101816001610b029190614dff565b61ffff1660108110610b1657610b16614dd7565b601091828204019190066002029054906101000a900461ffff16868261ffff1681518110610b4657610b46614dd7565b61ffff90921660209283029190910190910152600101610ae1565b505080546002909101546001600160a01b0380831698959750169450600160a01b900461ffff1692915050565b5f5f82515f14610b9e5782610ba9565b610ba9845f336121bf565b9050610bb781856006610bbf565b949350505050565b5f610bcb848484612843565b90505b9392505050565b610bdf82826128bf565b5050565b5f610bfa335f356001600160e01b031916306121bf565b9050610c12335f356001600160e01b03191683612440565b610c2e576040516282b42960e81b815260040160405180910390fd5b610c39336001610612565b15610c7a57335f90815260016020526040902054600160a01b900461ffff1615610c7a57335f90815260016020526040902054600160a01b900461ffff1693505b8361ffff165f03610c9e57604051631b47388560e11b815260040160405180910390fd5b8115610cb0576106b884846008612914565b6106c984846008612628565b5f610cd3335f356001600160e01b031916306121bf565b9050610ceb335f356001600160e01b03191683612440565b610d07576040516282b42960e81b815260040160405180910390fd5b5f546001600160a01b039081169083168103610d41576040516387e6e41f60e01b81526001600160a01b0384166004820152602401610758565b610d4a836129f2565b505050565b5f610d66335f356001600160e01b031916306121bf565b9050610d7e335f356001600160e01b03191683612440565b610d9a576040516282b42960e81b815260040160405180910390fd5b8115610dac576106b884846002612914565b6106c984846002612628565b5f610dce5f5160206156035f395f51905f525490565b905090565b5f5f82515f14610de35782610dee565b610dee845f336121bf565b9050610bb781856001610bbf565b5f610e13335f356001600160e01b031916306121bf565b9050610e2b335f356001600160e01b03191683612440565b610e47576040516282b42960e81b815260040160405180910390fd5b8115610e59576106b884846005612914565b6106c984846005612628565b610e6d612a72565b610bdf8282612b0d565b5f610e80612b29565b610dce612b72565b5f5160206156435f395f51905f5254610eb457604051630604a9ff60e01b815260040160405180910390fd5b610ebc612b8d565b565b5f610ed5335f356001600160e01b031916306121bf565b9050610eed335f356001600160e01b03191683612440565b610f09576040516282b42960e81b815260040160405180910390fd5b8115610f1b576106b884846001612914565b6106c984846001612628565b5f5f82515f14610f375782610f42565b610f42845f336121bf565b9050610bb781856002610bbf565b5f5f82515f14610f605782610f6b565b610f6b845f336121bf565b9050610bb781856003610bbf565b5f5f82515f14610f895782610f94565b610f94845f336121bf565b9050610bb781856008610bbf565b5f5160206156435f395f51905f5254610fce57604051630604a9ff60e01b815260040160405180910390fd5b610ebc612bd8565b5f610fed335f356001600160e01b031916306121bf565b9050611005335f356001600160e01b03191683612440565b611021576040516282b42960e81b815260040160405180910390fd5b6001600160a01b038281165f90815260016020526040902054166110635760405163961c9a4f60e01b81526001600160a01b0383166004820152602401610758565b816001600160a01b0316637e8962146040518163ffffffff1660e01b81526004015f604051808303815f87803b15801561109b575f5ffd5b505af11580156110ad573d5f5f3e3d5ffd5b505050505f826001600160a01b031663aaf10f426040518163ffffffff1660e01b8152600401602060405180830381865afa1580156110ee573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906111129190614dbc565b6001600160a01b038481165f908152600160205260409020600290810180546001600160a01b03191684841690811790915590549293506201000090920416900361119c57806001600160a01b0316836001600160a01b03167f303bc38f64dfc9f4fbff60b5d3435366303ce1d1d9c48b8a11c6f218f310fc8d60405160405180910390a3505050565b806001600160a01b0316836001600160a01b03167fcd23a18833879e1f289f88200345417e6288fcb73ad4cdfdbf7759d735444ae560405160405180910390a3505050565b5f5f6111ee8484336121bf565b9050610bb7848483612440565b5f611212335f356001600160e01b031916306121bf565b905061122a335f356001600160e01b03191683612440565b611246576040516282b42960e81b815260040160405180910390fd5b6001600160a01b038281165f90815260016020526040902054166112885760405163961c9a4f60e01b81526001600160a01b0383166004820152602401610758565b6001600160a01b0382165f90815260016020526040902054600160a01b900461ffff16156112d4576040516351957bc360e01b81526001600160a01b0383166004820152602401610758565b600280546001600160a01b0384165f908152600160205260408120805461ffff60a01b191661ffff938416600160a01b021790558254909116919061131883614e19565b82546101009290920a61ffff8181021990931691831602179091556001600160a01b0384165f81815260016020908152604091829020549151600160a01b90920490931681529092507f54772c673039dff7e67910f22733b5047dca1af1f182c0a1f0e6f3bc2a4f2d1491015b60405180910390a25050565b5f5f82515f146113a157826113ac565b6113ac845f336121bf565b9050610bb781856004610bbf565b5f60605f5f6113c885612c19565b93509350935093509193509193565b5f6113ee335f356001600160e01b031916306121bf565b9050611406335f356001600160e01b03191683612440565b611422576040516282b42960e81b815260040160405180910390fd5b6001600160a01b038281165f90815260016020526040902054166114645760405163961c9a4f60e01b81526001600160a01b0383166004820152602401610758565b816001600160a01b03166355f291666040518163ffffffff1660e01b81526004015f604051808303815f87803b15801561149c575f5ffd5b505af11580156114ae573d5f5f3e3d5ffd5b50506040516001600160a01b03851692507f3977fd47dfeb4e038abdaa6918d4adddff5d93223e1feace64d8c0921268f4a491505f90a25050565b5f611500335f356001600160e01b031916306121bf565b9050611518335f356001600160e01b03191683612440565b611534576040516282b42960e81b815260040160405180910390fd5b8115611546576106b884846003612914565b6106c984846003612628565b5f610dce5f5160206156235f395f51905f525490565b5f61157f335f356001600160e01b031916306121bf565b9050611597335f356001600160e01b03191683612440565b6115b3576040516282b42960e81b815260040160405180910390fd5b81156115c5576106b884846004612914565b6106c984846004612628565b5f6115e8335f356001600160e01b031916306121bf565b9050611600335f356001600160e01b03191683612440565b61161c576040516282b42960e81b815260040160405180910390fd5b611627336001610612565b1561166857335f90815260016020526040902054600160a01b900461ffff161561166857335f90815260016020526040902054600160a01b900461ffff1693505b8361ffff165f0361168c57604051631b47388560e11b815260040160405180910390fd5b811561169d576106b884845f612914565b6106c984845f612628565b61ffff82165f9081525f5160206155e35f395f51905f52602090815260408083206001600160a01b0385168452909152812054610bce565b5f6116f7335f356001600160e01b031916306121bf565b905061170f335f356001600160e01b03191683612440565b61172b576040516282b42960e81b815260040160405180910390fd5b6001600160a01b038381165f908152600160205260409020541661176d5760405163961c9a4f60e01b81526001600160a01b0384166004820152602401610758565b5f80546040805163557887a160e11b815290516001600160a01b03928316939287169163aaf10f429160048083019260209291908290030181865afa1580156117b8573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906117dc9190614dbc565b9050806001600160a01b0316826001600160a01b03160361181b576040516339cf62f760e11b81526001600160a01b0383166004820152602401610758565b60405163278f794360e11b81526001600160a01b03861690634f1ef286906118499085908890600401614e39565b5f604051808303815f87803b158015611860575f5ffd5b505af1158015611872573d5f5f3e3d5ffd5b505050505f856001600160a01b031663aaf10f426040518163ffffffff1660e01b8152600401602060405180830381865afa1580156118b3573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906118d79190614dbc565b9050826001600160a01b0316816001600160a01b03160361194e576001600160a01b038681165f8181526001602052604080822060020180546001600160a01b03191686861617905551928616927fcd23a18833879e1f289f88200345417e6288fcb73ad4cdfdbf7759d735444ae59190a361198f565b826001600160a01b0316866001600160a01b03167f5d3407b74538c58e3044d890bde1f03062a6becac8a311a18e9c8fb6874d70ea60405160405180910390a35b505050505050565b5f6119ae335f356001600160e01b031916306121bf565b90506119c6335f356001600160e01b03191683612440565b6119e2576040516282b42960e81b815260040160405180910390fd5b6119ed336001610612565b15611a2e57335f90815260016020526040902054600160a01b900461ffff1615611a2e57335f90815260016020526040902054600160a01b900461ffff1693505b8361ffff165f03611a5257604051631b47388560e11b815260040160405180910390fd5b81156106bd576106b884846007612914565b5f5f611a7c335f356001600160e01b031916306121bf565b9050611a94335f356001600160e01b03191683612440565b611ab0576040516282b42960e81b815260040160405180910390fd5b8351600f1015611ae0578351604051636bb2e11f60e01b81526004810191909152600f6024820152604401610758565b611ae984612707565b5f80546040516001600160a01b039091169190611b14908b908b908b908b9030908b90602401614e5a565b60408051601f198184030181529181526020820180516001600160e01b031663095f889360e21b179052519091505f9083908390611b5190614787565b611b5c929190614e39565b604051809103905ff080158015611b75573d5f5f3e3d5ffd5b50604080516080810182526001600160a01b03831681525f60208201529196508692508101611ba3896127cb565b81526001600160a01b038086166020928301528781165f9081526001808452604091829020855181549587015161ffff16600160a01b0275ffffffffffffffffffffffffffffffffffffffffffff199096169416939093179390931782558301519091611c15919083019060106146f2565b5060609190910151600290910180546001600160a01b0319166001600160a01b03909216919091179055611c4b5f866006612914565b846001600160a01b03167f10f8076b658baef34666707fb5cb59c631cc7f8cdec5a2c44ded627a1a264e9784848a604051611c8893929190614eba565b60405180910390a2505050509695505050505050565b5f5f82515f14611cae5782611cb9565b611cb9845f336121bf565b9050610bb781856007610bbf565b5f5f82515f14611cd75782611ce2565b611ce2845f336121bf565b9050610bb781856005610bbf565b611cf8612a72565b5f5160206156035f395f51905f525415611d255760405163f92ee8a960e01b815260040160405180910390fd5b611d2d612d45565b6001600160a01b0384163b1580611da15750836001600160a01b031663a4d50ceb6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611d7b573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611d9f9190614eed565b155b15611dca5760405163468e3dd960e11b81526001600160a01b0385166004820152602401610758565b6001600160a01b038316611dfc576040516316ba104f60e01b81526001600160a01b0384166004820152602401610758565b6001600160a01b038216611e2e57604051630c98f89360e41b81526001600160a01b0383166004820152602401610758565b611e3785612d80565b6002546201000090046001600160a01b0316611e7d576002805475ffffffffffffffffffffffffffffffffffffffff00001916620100006001600160a01b038716021790555b611e8681612dbd565b611e925f846003612914565b611e9e5f836004612914565b611eae631fa2588560e21b612dfd565b611ebe634298165f60e11b612dfd565b611ed8632af948b360e11b611ed36003612f1f565b612f6f565b611eed6337126e1960e01b611ed36003612f1f565b611f0263278f794360e11b611ed36003612f1f565b611f17632f8ede1160e21b611ed36003612f1f565b611f2c63a03175af60e01b611ed36003612f1f565b611f4163056c8b6160e21b611ed36003612f1f565b611f5663a87238e760e01b611ed36003612f1f565b611f6b630156f0a960e71b611ed36003612f1f565b611f8063374627b360e01b611ed36003612f1f565b611f95631b53389f60e21b611ed36004612f1f565b611faa630939a29b60e31b611ed36004612f1f565b611fbf6389ce7a1d60e01b611ed36004612f1f565b611fd463039e62d360e01b611ed36004612f1f565b611fe9631d7a979560e31b611ed36001612f1f565b612003636bcd157760e11b611ed360015b60056006613128565b612017624ff82360e21b611ed36001611ffa565b61202f632b73cd6360e21b611ed360045b60066131cb565b61204463361fa22b60e01b611ed36004612028565b6120546323b872dd60e01b612dfd565b61206463d505accf60e01b612dfd565b612079632207ef2760e11b611ed36003612f1f565b61208e632cb2cacf60e11b611ed36004612f1f565b6120a36317284fc160e21b611ed36004612f1f565b6120b8639fc1d0e760e01b611ed36001612f1f565b6120cd6398cd615360e01b611ed36001612f1f565b6120e2635d3590d560e01b611ed36001612f1f565b6120f7638456cb5960e01b611ed36002612f1f565b61210c631fa5d41d60e11b611ed36002612f1f565b61212163a9059cbb60e01b611ed36007612f1f565b61213663095ea7b360e01b611ed36007612f1f565b61214d623f151360e51b611ed360015b60086131cb565b61216263c9c16fbd60e01b611ed36001612146565b61217b6340c10f1960e01b611ed360015b5f6005613128565b612190632770a7eb60e21b611ed36001612173565b5050505050565b5f5f82515f146121a757826121b2565b6121b2845f336121bf565b9050610bb781855f610bbf565b6001600160a01b038082165f908152600160205260409020805460609216612231576040805160018082528183019092525f91602080830190803683370190505090505f815f8151811061221557612215614dd7565b61ffff909216602092830291909101909101529150610bce9050565b6001810154815461ffff91821691600160a01b909104165f03612325575f8161ffff1667ffffffffffffffff81111561226c5761226c614877565b604051908082528060200260200182016040528015612295578160200160208202803683370190505b5090505f5b8261ffff168161ffff16101561231a57836001018160016122bb9190614dff565b61ffff16601081106122cf576122cf614dd7565b601091828204019190066002029054906101000a900461ffff16828261ffff16815181106122ff576122ff614dd7565b61ffff9092166020928302919091019091015260010161229a565b509250610bce915050565b5f612331826001614dff565b61ffff1667ffffffffffffffff81111561234d5761234d614877565b604051908082528060200260200182016040528015612376578160200160208202803683370190505b5090505f5b8261ffff168161ffff1610156123fb578360010181600161239c9190614dff565b61ffff16601081106123b0576123b0614dd7565b601091828204019190066002029054906101000a900461ffff16828261ffff16815181106123e0576123e0614dd7565b61ffff9092166020928302919091019091015260010161237b565b508254815161ffff600160a01b909204821691839190851690811061242257612422614dd7565b61ffff90921660209283029190910190910152925050509392505050565b6001600160e01b031982165f9081527f31f703e9d9d60912870f8b3156da76d11cbd7f7db67e551ddf3ac9400fb05e0160205260408120545f5160206155e35f395f51905f529060ff1615612499576001915050610bce565b6001600160e01b031984165f908152600282016020526040902054806124c3575f92505050610bce565b5f6124ce8786613244565b9050806124e0575f9350505050610bce565b7faaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa81811690838316161561251a575f945050505050610bce565b50167f55555555555555555555555555555555555555555555555555555555555555551615159150509392505050565b608060ff821610612573576040516365861efd60e11b815260ff82166004820152602401610758565b5f5160206155e35f395f51905f525f8061258e600285614f08565b6125989190614f24565b60ff1690505f60016125ab600286614f08565b6125b59190614f24565b61ffff87165f818152602086815260408083206001600160a01b038b1680855292528083208054600160ff97881681811b909217908a1b191691829055915191965094891693927f59bc112e2d7da8911b22d274fb46cc65bcaab4e35bac23cf3808640bf327bd4891a450505050505050565b608060ff821610612651576040516365861efd60e11b815260ff82166004820152602401610758565b5f5160206155e35f395f51905f525f8061266c600285614f08565b6126769190614f24565b60ff1690505f6001612689600286614f08565b6126939190614f24565b61ffff87165f818152602086815260408083206001600160a01b038b16808552925280832080546001808a1b1990911660ff97881691821b191691829055915191965094891693927f79b7eb7267a13f56f8d84f7c730b37c1606e5e0065419ad74777c4f8db3e285c91a450505050505050565b5f5b8151811015610bdf575f61ffff1682828151811061272957612729614dd7565b602002602001015161ffff160361275357604051638be7579560e01b815260040160405180910390fd5b6103e861ffff1682828151811061276c5761276c614dd7565b602002602001015161ffff1611156127c35781818151811061279057612790614dd7565b602090810291909101015160405163d56310e960e01b815261ffff90911660048201526103e86024820152604401610758565b600101612709565b6127d3614794565b6127db614794565b825161ffff1681525f5b835181101561283c5783818151811061280057612800614dd7565b6020026020010151828260016128169190614f3d565b6010811061282657612826614dd7565b61ffff90921660209290920201526001016127e5565b5092915050565b5f608060ff83161061286d576040516365861efd60e11b815260ff83166004820152602401610758565b5f6128788486613244565b90505f80612887600286614f08565b6128919190614f24565b60ff169050600182821c161580159081906128b457506128b28787876132bf565b155b979650505050505050565b604051309033907f16f2354671bbabcb07367cc01a78931a00a812ee080430be4e8f1a76ab58212b905f90a333301461290a5760405162e6155960e41b815260040160405180910390fd5b610bdf8282613323565b608060ff82161061293d576040516365861efd60e11b815260ff82166004820152602401610758565b5f5160206155e35f395f51905f525f80612958600285614f08565b6129629190614f24565b60ff1690505f6001612975600286614f08565b61297f9190614f24565b61ffff87165f818152602086815260408083206001600160a01b038b16808552925280832080546001808a1b90911760ff97881691821b191691829055915191965094891693927fc644f1662a0174d36c039fb87d2529ba53a9908f6268144da8e9406457858cf191a450505050505050565b6129fb81613341565b612a2357604051630c76093760e01b81526001600160a01b0382166004820152602401610758565b5f80546001600160a01b038381166001600160a01b0319831681178455604051919092169283917ffed7864e1707baf60e613235dc8c5fd0619d03df5faff1ce7be98c30620124f29190a35050565b306001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000161480612aef57507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316612ae35f5160206156235f395f51905f525490565b6001600160a01b031614155b15610ebc5760405163703e46dd60e11b815260040160405180910390fd5b612b15612a72565b612b1f828261338b565b610bdf82826133d6565b306001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614610ebc5760405163703e46dd60e11b815260040160405180910390fd5b5f612b7b612b29565b505f5160206156235f395f51905f5290565b5f5160206156435f395f51905f5280545f612ba7826113ba565b50509150505f5f612bb7836133e0565b91509150612bc5828261338b565b612bce84613433565b50505f9092555050565b5f5160206156435f395f51905f5280545f612bf2826113ba565b50509150505f5f612c02836133e0565b91509150612c10828261338b565b612bce846134ee565b5f8181527f1626844f871e5080d673ecb96ea8a32c7bdf671ea137282b2886cd9d0fd9a70260209081526040808320815160808101835281546001600160a01b03168152600182015493810193909352600281018054606094869485945f5160206155c35f395f51905f529486949192840191612c9590614f50565b80601f0160208091040260200160405190810160405280929190818152602001828054612cc190614f50565b8015612d0c5780601f10612ce357610100808354040283529160200191612d0c565b820191905f5260205f20905b815481529060010190602001808311612cef57829003601f168201915b505050505081526020016003820154815250509050805f0151816040015182602001518360600151955095509550955050509193509193565b60015f5160206156035f395f51905f52556040517f5daa87a0e9463431830481fd4b6e3403442dfb9a12b9c07597e9f61d50b633c8905f90a1565b612d89816129f2565b60025461ffff165f03612dba57612da36103e86001614dff565b6002805461ffff191661ffff929092169190911790555b50565b7f2c9356b94ada349e65b0a691cd15e24beddfc13e05a26de25d9c64509c824401545f5160206156435f395f51905f52905f03610bdf57610bdf82613741565b6001600160e01b031981165f9081527f31f703e9d9d60912870f8b3156da76d11cbd7f7db67e551ddf3ac9400fb05e0360205260409020545f5160206155e35f395f51905f529060ff1615612e7057604051621414d760e21b81526001600160e01b031983166004820152602401610758565b6001600160e01b031982165f818152600383016020526040808220805460ff19166001179055517fd19986f002b4a71e2859179574de14eac9664b9d26d3045efee84efbce0ce3569190a26001600160e01b031982165f818152600183810160209081526040808420805460ff19168417905560028601825292839020548351928352908201527f26137813fc2dfaa8173dae8364111a1bd1788417316e36919840c78855dd003b9101611385565b6040805160018082528183019092526060915f91906020808301908036833701905050905082815f81518110612f5757612f57614dd7565b60ff9092166020928302919091019091015292915050565b80515f03612f905760405163fc3a4a2f60e01b815260040160405180910390fd5b6001600160e01b031982165f9081527f31f703e9d9d60912870f8b3156da76d11cbd7f7db67e551ddf3ac9400fb05e0360205260409020545f5160206155e35f395f51905f529060ff161561300357604051621414d760e21b81526001600160e01b031984166004820152602401610758565b6001600160e01b031983165f818152600383016020526040808220805460ff19166001179055517fd19986f002b4a71e2859179574de14eac9664b9d26d3045efee84efbce0ce3569190a25f805b83518110156130ba575f84828151811061306d5761306d614dd7565b60200260200101519050608060ff168160ff16106130a3576040516365861efd60e11b815260ff82166004820152602401610758565b6130af83826001613785565b925050600101613051565b506001600160e01b031984165f81815260028401602090815260408083208590556001860182529182902054825160ff909116151581529081018490527f26137813fc2dfaa8173dae8364111a1bd1788417316e36919840c78855dd003b910160405180910390a250505050565b604080516003808252608082019092526060915f919060208201848036833701905050905084815f8151811061316057613160614dd7565b602002602001019060ff16908160ff1681525050838160018151811061318857613188614dd7565b602002602001019060ff16908160ff168152505082816002815181106131b0576131b0614dd7565b60ff9092166020928302919091019091015290509392505050565b60408051600280825260608083018452925f92919060208301908036833701905050905083815f8151811061320257613202614dd7565b602002602001019060ff16908160ff1681525050828160018151811061322a5761322a614dd7565b60ff90921660209283029190910190910152905092915050565b5f5f5160206155e35f395f51905f5281805b84518110156132b657825f015f86838151811061327557613275614dd7565b60209081029190910181015161ffff1682528181019290925260409081015f9081206001600160a01b038a1682529092529020549190911790600101613256565b50949350505050565b5f608060ff8316106132e9576040516365861efd60e11b815260ff83166004820152602401610758565b5f6132f48486613244565b90505f6001613304600286614f08565b61330e9190614f24565b60ff169190911c600116151595945050505050565b61332d82826137f9565b6133378282613a55565b610bdf8282613aaa565b5f816001600160a01b03163b5f0361335a57505f919050565b61336382613acf565b61336e57505f919050565b61337782613b49565b61338257505f919050565b61084e82613b80565b5f6133a2335f356001600160e01b031916306121bf565b90506133ba335f356001600160e01b03191683612440565b610d4a576040516282b42960e81b815260040160405180910390fd5b610bdf8282613c42565b5f60606064835110156134065760405163fc69850b60e01b815260040160405180910390fd5b8251613416908490600490613cff565b8060200190518101906134299190614f88565b9094909350915050565b5f8181527f1626844f871e5080d673ecb96ea8a32c7bdf671ea137282b2886cd9d0fd9a70160205260409020545f5160206155c35f395f51905f529015610bdf575f8281526001808301602090815260408084208490556002808601909252832080546001600160a01b03191681559182018390559091906134b7908301826147b3565b505f60039190910181905560405183917f134fdd648feeaf30251f0157f9624ef8608ff9a042aad6d13e73f35d21d3f88d91a25050565b6134f781613d7e565b5f8181527f1626844f871e5080d673ecb96ea8a32c7bdf671ea137282b2886cd9d0fd9a70260209081526040808320815160808101835281546001600160a01b031681526001820154938101939093526002810180545f5160206155c35f395f51905f529594938401919061356b90614f50565b80601f016020809104026020016040519081016040528092919081815260200182805461359790614f50565b80156135e25780601f106135b9576101008083540402835291602001916135e2565b820191905f5260205f20905b8154815290600101906020018083116135c557829003601f168201915b50505091835250506003919091015460209182015281519082015160408301519293509091478211156136305760405162fae2d560e21b815260048101839052476024820152604401610758565b5f8681526001808701602090815260408084208490556002808a01909252832080546001600160a01b0319168155918201839055909190613673908301826147b3565b600382015f905550505f836001600160a01b031683836040516136969190615011565b5f6040518083038185875af1925050503d805f81146136d0576040519150601f19603f3d011682016040523d82523d5f602084013e6136d5565b606091505b50509050806136fa5760405163165a1d4160e01b815260048101889052602401610758565b867fbc76564aec85e3f50672da2d1ddca0390851fecefe61cd19d89dea91a8267829858585426040516137309493929190615027565b60405180910390a250505050505050565b805f03613761576040516327df2edd60e11b815260040160405180910390fd5b7f2c9356b94ada349e65b0a691cd15e24beddfc13e05a26de25d9c64509c82440155565b5f8080613793600286614f08565b61379d9190614f24565b60ff1690505f60016137b0600287614f08565b6137ba9190614f24565b60ff16905083156137db57600180821b90831b9690961795909517946137ef565b600180821b1990831b199690961695909516945b5093949350505050565b6004815110610bdf576138175f5f5160206156035f395f51905f5255565b613827631fa2588560e21b613dfb565b613837634298165f60e11b613dfb565b613847632af948b360e11b613dfb565b6138576337126e1960e01b613dfb565b61386763278f794360e11b613dfb565b613877632f8ede1160e21b613dfb565b61388763a03175af60e01b613dfb565b61389763056c8b6160e21b613dfb565b6138a763a87238e760e01b613dfb565b6138b7630156f0a960e71b613dfb565b6138c763374627b360e01b613dfb565b6138d7631b53389f60e21b613dfb565b6138e7630939a29b60e31b613dfb565b6138f76389ce7a1d60e01b613dfb565b61390763039e62d360e01b613dfb565b613917631d7a979560e31b613dfb565b613927636bcd157760e11b613dfb565b613936624ff82360e21b613dfb565b613946632b73cd6360e21b613dfb565b61395663361fa22b60e01b613dfb565b6139666323b872dd60e01b613dfb565b61397663d505accf60e01b613dfb565b613986632207ef2760e11b613dfb565b613996632cb2cacf60e11b613dfb565b6139a66317284fc160e21b613dfb565b6139b6639fc1d0e760e01b613dfb565b6139c66398cd615360e01b613dfb565b6139d6635d3590d560e01b613dfb565b6139e6638456cb5960e01b613dfb565b6139f6631fa5d41d60e11b613dfb565b613a0663a9059cbb60e01b613dfb565b613a1663095ea7b360e01b613dfb565b613a25623f151360e51b613dfb565b613a3563c9c16fbd60e01b613dfb565b613a456340c10f1960e01b613dfb565b610bdf632770a7eb60e21b613dfb565b613a5e82613ed0565b6040516001600160a01b038316907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b905f90a2805115613aa257610d4a8282613eea565b610bdf613f76565b613ab2610db8565b610bdf5760405163b3a31bfd60e01b815260040160405180910390fd5b5f6001600160a01b038216613ae557505f919050565b816001600160a01b03163b5f03613afd57505f919050565b5f5f613b0884613f95565b915091508115613b405780613b2057505f9392505050565b613b298461414a565b613b3657505f9392505050565b5060019392505050565b610bb784614215565b5f6001600160a01b038216613b5f57505f919050565b816001600160a01b03163b5f03613b7757505f919050565b61084e82614431565b60408051600481526024810182526020810180516001600160e01b0316635fa6e25b60e11b17905290515f91829182916001600160a01b03861691613bc59190615011565b5f60405180830381855afa9150503d805f8114613bfd576040519150601f19603f3d011682016040523d82523d5f602084013e613c02565b606091505b5091509150811580613c1657508051602014155b15613c2457505f9392505050565b5f81806020019051810190613c399190614eed565b95945050505050565b5f5160206156435f395f51905f525415613c6f5760405163e741fd4560e01b815260040160405180910390fd5b7f2c9356b94ada349e65b0a691cd15e24beddfc13e05a26de25d9c64509c824401545f5160206156435f395f51905f52905f90613cac9042614f3d565b90505f613cf6308686604051602401613cc6929190614e39565b60408051601f198184030181529190526020810180516001600160e01b03166319cccfd360e11b179052846144d4565b90925550505050565b6060613d0c8285516145ee565b9150613d1883836145ee565b9250613d24838361505f565b67ffffffffffffffff811115613d3c57613d3c614877565b6040519080825280601f01601f191660200182016040528015613d66576020820181803683370190505b509050828203836020860101602083015e9392505050565b5f8181527f1626844f871e5080d673ecb96ea8a32c7bdf671ea137282b2886cd9d0fd9a70160205260408120545f5160206155c35f395f51905f5291819003613dda57604051634b9aaa0960e11b815260040160405180910390fd5b80421015610d4a57604051630f0b32ff60e11b815260040160405180910390fd5b6001600160e01b031981165f8181527f31f703e9d9d60912870f8b3156da76d11cbd7f7db67e551ddf3ac9400fb05e0160209081526040808320805460ff199081169091557f31f703e9d9d60912870f8b3156da76d11cbd7f7db67e551ddf3ac9400fb05e0283528184208490557f31f703e9d9d60912870f8b3156da76d11cbd7f7db67e551ddf3ac9400fb05e039092528083208054909216909155515f5160206155e35f395f51905f5292917f6553ac4c75bde7ec33c8647fd04192e30cc2f6a1a8c73a07123396fd9b45f9b591a25050565b613ed981614603565b5f5160206156235f395f51905f5255565b60605f5f846001600160a01b031684604051613f069190615011565b5f60405180830381855af49150503d805f8114613f3e576040519150601f19603f3d011682016040523d82523d5f602084013e613f43565b606091505b509150915081610bb757805115613f5d5780518082602001fd5b60405163d6bda27560e01b815260040160405180910390fd5b3415610ebc5760405163b398979f60e01b815260040160405180910390fd5b6040516301ffc9a760e01b60248201525f908190819081906001600160a01b0386169060440160408051601f198184030181529181526020820180516001600160e01b03166301ffc9a760e01b17905251613ff09190615011565b5f60405180830381855afa9150503d805f8114614028576040519150601f19603f3d011682016040523d82523d5f602084013e61402d565b606091505b5091509150811580614040575060208151105b1561405157505f9485945092505050565b6040516001600160e01b031960248201525f9081906001600160a01b0388169060440160408051601f198184030181529181526020820180516001600160e01b03166301ffc9a760e01b179052516140a99190615011565b5f60405180830381855afa9150503d805f81146140e1576040519150601f19603f3d011682016040523d82523d5f602084013e6140e6565b606091505b50915091508115806140f9575060208151105b1561410d57506001965f9650945050505050565b5f818060200190518101906141229190614eed565b9050801561413a57506001975f975095505050505050565b5060019788975095505050505050565b6040516336372b0760e01b60248201525f90819081906001600160a01b0385169060440160408051601f198184030181529181526020820180516001600160e01b03166301ffc9a760e01b179052516141a39190615011565b5f60405180830381855afa9150503d805f81146141db576040519150601f19603f3d011682016040523d82523d5f602084013e6141e0565b606091505b50915091508115806141f3575060208151105b1561420157505f9392505050565b80806020019051810190610bb79190614eed565b60408051600481526024810182526020810180516001600160e01b03166318160ddd60e01b17905290515f91829182916001600160a01b0386169161425a9190615011565b5f60405180830381855afa9150503d805f8114614292576040519150601f19603f3d011682016040523d82523d5f602084013e614297565b606091505b50915091508115806142aa575060208151105b156142b857505f9392505050565b6040515f602482018190529081906001600160a01b0387169060440160408051601f198184030181529181526020820180516001600160e01b03166370a0823160e01b179052516143099190615011565b5f60405180830381855afa9150503d805f8114614341576040519150601f19603f3d011682016040523d82523d5f602084013e614346565b606091505b5091509150811580614359575060208151105b1561436957505f95945050505050565b6040515f60248201819052604482018190529081906001600160a01b0389169060640160408051601f198184030181529181526020820180516001600160e01b0316636eb1769f60e11b179052516143c19190615011565b5f60405180830381855afa9150503d805f81146143f9576040519150601f19603f3d011682016040523d82523d5f602084013e6143fe565b606091505b5091509150811580614411575060208151105b1561442357505f979650505050505050565b506001979650505050505050565b60408051600481526024810182526020810180516001600160e01b03166352d1902d60e01b17905290515f91829182916001600160a01b038616916144769190615011565b5f60405180830381855afa9150503d805f81146144ae576040519150601f19603f3d011682016040523d82523d5f602084013e6144b3565b606091505b50915091508115806144c6575060208151105b15613b3657505f9392505050565b5f8142106144f55760405163b7d0949760e01b815260040160405180910390fd5b5f5160206155c35f395f51905f5280543490825f61451283615072565b909155505081545f81815260018085016020908152604080842089905580516080810182526001600160a01b038c811682528184018881528284018d8152606084018d90528888526002808c0190965293909620825181546001600160a01b03191692169190911781559451938501939093555193965090929082019061459990826150ce565b5060608201518160030155905050827f8d6fedd41517ab865c3658696c50923b3493c8a93ffd016d919a599f03c7531b878388886040516145dd9493929190615027565b60405180910390a250509392505050565b5f8183106145fc5781610bce565b5090919050565b806001600160a01b03163b5f0361463857604051634c9c8ce360e01b81526001600160a01b0382166004820152602401610758565b806001600160a01b03166352d1902d6040518163ffffffff1660e01b8152600401602060405180830381865afa925050508015614692575060408051601f3d908101601f1916820190925261468f91810190615189565b60015b6146ba57604051634c9c8ce360e01b81526001600160a01b0382166004820152602401610758565b5f5160206156235f395f51905f528114610bdf57604051634c9c8ce360e01b81526001600160a01b0383166004820152602401610758565b600183019183908215614777579160200282015f5b8382111561474757835183826101000a81548161ffff021916908361ffff1602179055509260200192600201602081600101049283019260010302614707565b80156147755782816101000a81549061ffff0219169055600201602081600101049283019260010302614747565b505b506147839291506147e6565b5090565b610422806151a183390190565b6040518061020001604052806010906020820280368337509192915050565b5080546147bf90614f50565b5f825580601f106147ce575050565b601f0160209004905f5260205f2090810190612dba91905b5b80821115614783575f81556001016147e7565b803561ffff8116811461480b575f5ffd5b919050565b6001600160a01b0381168114612dba575f5ffd5b8015158114612dba575f5ffd5b5f5f5f60608486031215614843575f5ffd5b61484c846147fa565b9250602084013561485c81614810565b9150604084013561486c81614824565b809150509250925092565b634e487b7160e01b5f52604160045260245ffd5b604051601f8201601f1916810167ffffffffffffffff811182821017156148b4576148b4614877565b604052919050565b5f82601f8301126148cb575f5ffd5b813567ffffffffffffffff8111156148e5576148e5614877565b8060051b6148f56020820161488b565b91825260208185018101929081019086841115614910575f5ffd5b6020860192505b8383101561493957614928836147fa565b825260209283019290910190614917565b9695505050505050565b5f5f60408385031215614954575f5ffd5b823561495f81614810565b9150602083013567ffffffffffffffff81111561497a575f5ffd5b614986858286016148bc565b9150509250929050565b80356001600160e01b03198116811461480b575f5ffd5b5f602082840312156149b7575f5ffd5b610bce82614990565b5f602082840312156149d0575f5ffd5b8135610bce81614810565b5f8151808452602084019350602083015f5b828110156137ef57815161ffff168652602095860195909101906001016149ed565b6001600160a01b0385168152608060208201525f614a3060808301866149db565b90506001600160a01b038416604083015261ffff8316606083015295945050505050565b803560ff8116811461480b575f5ffd5b5f5f5f60608486031215614a76575f5ffd5b833567ffffffffffffffff811115614a8c575f5ffd5b614a98868287016148bc565b9350506020840135614aa981614810565b9150614ab760408501614a54565b90509250925092565b5f67ffffffffffffffff821115614ad957614ad9614877565b50601f01601f191660200190565b5f614af9614af484614ac0565b61488b565b9050828152838383011115614b0c575f5ffd5b828260208301375f602084830101529392505050565b5f5f60408385031215614b33575f5ffd5b8235614b3e81614810565b9150602083013567ffffffffffffffff811115614b59575f5ffd5b8301601f81018513614b69575f5ffd5b61498685823560208401614ae7565b5f5f60408385031215614b89575f5ffd5b8235614b9481614810565b9150614ba260208401614990565b90509250929050565b5f60208284031215614bbb575f5ffd5b5035919050565b5f81518084528060208401602086015e5f602082860101526020601f19601f83011685010191505092915050565b6001600160a01b0385168152608060208201525f614c116080830186614bc2565b6040830194909452506060015292915050565b5f5f60408385031215614c35575f5ffd5b614c3e836147fa565b91506020830135614c4e81614810565b809150509250929050565b5f82601f830112614c68575f5ffd5b610bce83833560208501614ae7565b5f5f5f5f5f5f60c08789031215614c8c575f5ffd5b863567ffffffffffffffff811115614ca2575f5ffd5b614cae89828a01614c59565b965050602087013567ffffffffffffffff811115614cca575f5ffd5b614cd689828a01614c59565b955050614ce560408801614a54565b9350606087013567ffffffffffffffff811115614d00575f5ffd5b614d0c89828a01614c59565b935050608087013567ffffffffffffffff811115614d28575f5ffd5b614d3489828a016148bc565b9699959850939692959460a09093013593505050565b5f5f5f5f5f60a08688031215614d5e575f5ffd5b8535614d6981614810565b94506020860135614d7981614810565b93506040860135614d8981614810565b92506060860135614d9981614810565b949793965091946080013592915050565b602081525f610bce60208301846149db565b5f60208284031215614dcc575f5ffd5b8151610bce81614810565b634e487b7160e01b5f52603260045260245ffd5b634e487b7160e01b5f52601160045260245ffd5b61ffff818116838216019081111561084e5761084e614deb565b5f61ffff821661ffff8103614e3057614e30614deb565b60010192915050565b6001600160a01b0383168152604060208201525f610bcb6040830184614bc2565b60c081525f614e6c60c0830189614bc2565b8281036020840152614e7e8189614bc2565b905060ff871660408401528281036060840152614e9b8187614bc2565b6001600160a01b03959095166080840152505060a00152949350505050565b6001600160a01b0384168152606060208201525f614edb6060830185614bc2565b828103604084015261493981856149db565b5f60208284031215614efd575f5ffd5b8151610bce81614824565b60ff818116838216029081169081811461283c5761283c614deb565b60ff818116838216019081111561084e5761084e614deb565b8082018082111561084e5761084e614deb565b600181811c90821680614f6457607f821691505b602082108103614f8257634e487b7160e01b5f52602260045260245ffd5b50919050565b5f5f60408385031215614f99575f5ffd5b8251614fa481614810565b602084015190925067ffffffffffffffff811115614fc0575f5ffd5b8301601f81018513614fd0575f5ffd5b8051614fde614af482614ac0565b818152866020838501011115614ff2575f5ffd5b8160208401602083015e5f602083830101528093505050509250929050565b5f82518060208501845e5f920191825250919050565b6001600160a01b0385168152836020820152608060408201525f61504e6080830185614bc2565b905082606083015295945050505050565b8181038181111561084e5761084e614deb565b5f6001820161508357615083614deb565b5060010190565b601f821115610d4a57805f5260205f20601f840160051c810160208510156150af5750805b601f840160051c820191505b81811015612190575f81556001016150bb565b815167ffffffffffffffff8111156150e8576150e8614877565b6150fc816150f68454614f50565b8461508a565b6020601f82116001811461512e575f83156151175750848201515b5f19600385901b1c1916600184901b178455612190565b5f84815260208120601f198516915b8281101561515d578785015182556020948501946001909201910161513d565b508482101561517a57868401515f19600387901b60f8161c191681555b50505050600190811b01905550565b5f60208284031215615199575f5ffd5b505191905056fe608060405260405161042238038061042283398101604081905261002291610268565b61002c8282610033565b5050610364565b61003c82610091565b6040516001600160a01b038316907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b905f90a28051156100855761008082826100ab565b505050565b61008d61013f565b5050565b61009a81610160565b5f5160206104025f395f51905f5255565b60605f5f846001600160a01b0316846040516100c79190610337565b5f60405180830381855af49150503d805f81146100ff576040519150601f19603f3d011682016040523d82523d5f602084013e610104565b606091505b5091509150816101375780511561011e5780518082602001fd5b60405163d6bda27560e01b815260040160405180910390fd5b949350505050565b341561015e5760405163b398979f60e01b815260040160405180910390fd5b565b806001600160a01b03163b5f0361019a57604051634c9c8ce360e01b81526001600160a01b03821660048201526024015b60405180910390fd5b806001600160a01b03166352d1902d6040518163ffffffff1660e01b8152600401602060405180830381865afa9250505080156101f4575060408051601f3d908101601f191682019092526101f19181019061034d565b60015b61021c57604051634c9c8ce360e01b81526001600160a01b0382166004820152602401610191565b5f5160206104025f395f51905f52811461008d57604051634c9c8ce360e01b81526001600160a01b0383166004820152602401610191565b634e487b7160e01b5f52604160045260245ffd5b5f5f60408385031215610279575f5ffd5b82516001600160a01b038116811461028f575f5ffd5b60208401519092506001600160401b038111156102aa575f5ffd5b8301601f810185136102ba575f5ffd5b80516001600160401b038111156102d3576102d3610254565b604051601f8201601f19908116603f011681016001600160401b038111828210171561030157610301610254565b604052818152828201602001871015610318575f5ffd5b8160208401602083015e5f602083830101528093505050509250929050565b5f82518060208501845e5f920191825250919050565b5f6020828403121561035d575f5ffd5b5051919050565b6092806103705f395ff3fe60806040526010600c6012565b603f565b005b5f603a7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5490565b905090565b365f5f375f5f365f845af43d5f5f3e8080156058573d5ff35b3d5ffdfea264697066735822122043d77147bce425ecaf176eaf1e360434e682a72986bc8058c86e150a074679c364736f6c634300081e0033360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc1626844f871e5080d673ecb96ea8a32c7bdf671ea137282b2886cd9d0fd9a70031f703e9d9d60912870f8b3156da76d11cbd7f7db67e551ddf3ac9400fb05e00112dd7b7d700a9bfd03edf4d4ce6f92b1f5f1a2fd81fde871f4db11384aa0b00360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc2c9356b94ada349e65b0a691cd15e24beddfc13e05a26de25d9c64509c824400a264697066735822122084b01218d808166e0da4c5496424823b3781e58eef949e4079b5bf51248414a364736f6c634300081e0033","name":"SygFactory","is_blueprint":false,"license_type":"none","is_fully_verified":true,"is_verified_via_eth_bytecode_db":true,"language":"solidity","evm_version":"prague","can_be_visualized_via_sol2uml":true,"is_verified_via_sourcify":false,"additional_sources":[{"file_path":"apps/UUPSProxy/contracts/UUPSProxy.sol","source_code":"// SPDX-License-Identifier: UNLICENSED\npragma solidity ^0.8.30;\n\nimport {UUPSUtils} from 'libs/UUPS/UUPSUtils.sol';\n\n/// @title UUPSProxy\n/// @notice Upgradeable proxy contract following the ERC-1967 and UUPS standards.\n/// @dev Stores the implementation address in a specific storage slot to avoid conflicts with the implementation's storage layout.\n/**\n * @custom:security-contact blockchain@sygnum.com\n */\ncontract UUPSProxy {\n\n    /* ======== Constructor ======== */\n    /// @notice Initializes the proxy with an initial implementation and optional setup call.\n    /// @param _implAddress The address of the initial implementation contract.\n    /// @param _data Optional initialization calldata to delegatecall to the implementation.\n    constructor(address _implAddress, bytes memory _data) payable {\n        UUPSUtils.upgradeToAndCall(_implAddress, _data);\n    }\n\n    /* ======== External/Public Functions ======== */\n    /// @dev Fallback function that delegates calls to the address returned by `_implementation()`.\n    /// Will run if no other function in the contract matches the call data.\n    fallback() external payable {\n        _delegate(_implementation());\n    }\n\n    /* ======== Internal  Functions ======== */\n    /// @notice Returns the current implementation address used by the proxy.\n    /// @return address of the current implementation contract.\n    function _implementation() internal view returns (address) {\n        return UUPSUtils.getImplementation();\n    }\n\n    /// @dev Delegates the current call to `_implementation`.\n    /// Copies calldata, performs delegatecall, and returns or reverts with the returned data.\n    /// @param _implAddress The address of the contract to delegatecall to.\n    function _delegate(address _implAddress) internal {\n        // Use assembly to copy calldata and delegatecall in a simplest way.\n        // The assembly code performs the following actions:\n        // 1. Copies the calldata from the current environment to memory.\n        // 2. Performs a delegatecall to the implementation contract.\n        // 3. Copies the return data from the delegatecall to memory.\n        // 4. Returns or reverts based on the success of the delegatecall.\n        // solhint-disable-next-line no-inline-assembly\n        assembly {\n            calldatacopy(0, 0, calldatasize())\n            let result := delegatecall(\n                gas(),\n                _implAddress,\n                0,\n                calldatasize(),\n                0,\n                0\n            )\n            returndatacopy(0, 0, returndatasize())\n            switch result\n            case 0 {\n                revert(0, returndatasize())\n            }\n            default {\n                return(0, returndatasize())\n            }\n        }\n    }\n}\n"},{"file_path":"apps/tokenization/SygFactory/contracts/RolesManager.sol","source_code":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.30;\n\nimport {StorageLayout} from \"./storage/StorageLayout.sol\";\nimport {PermissionManagerView} from \"libs/PermissionManager/PermissionManagerView.sol\";\n\n/**\n * @custom:security-contact blockchain@sygnum.com\n * @title RolesManager\n * @author SYGNUM AG - Blockchain team 3301\n * @notice Manages roles and permissions for the SygFactory with restricted permission management\n */\ncontract RolesManager is StorageLayout, PermissionManagerView {\n    /* ======== CONSTANTS ======== */\n\n    // Empty group array size used when checking if caller is a token contract\n    uint16 private constant ONE_ELEMENT_SIZE = 1;\n\n    /* ======== ERRORS ======== */\n\n    error InvalidGroupId();\n\n    /* ======== ROLE CHECK FUNCTIONS ======== */\n\n    /**\n     * @notice Checks if an account has platform admin role within any of the specified groups\n     * @param _account The account address to check\n     * @param _groupIds The group IDs to check against (empty array if called from token)\n     * @return True if the account is whitelisted for platform admin role in at least one of the\n     *         specified groups and not blacklisted for the same role in any of them\n     */\n    function isPlatformAdmin(address _account, uint16[] memory _groupIds) public view returns (bool) {\n        uint16[] memory _effectiveGroupIds = _groupIds.length == 0 ? _loadApplicablePermissionGroups(_account, bytes4(0), msg.sender) : _groupIds;\n        return doesUserHaveRole(_effectiveGroupIds, _account, uint8(Role.PLATFORM_ADMIN));\n    }\n\n    /**\n     * @notice Checks if an account has role manager role within any of the specified groups\n     * @param _account The account address to check\n     * @param _groupIds The group IDs to check against (empty array if called from token)\n     * @return True if the account is whitelisted for role manager role in at least one of the\n     *         specified groups AND not blacklisted for the same role in any group (blacklist supersedes whitelist)\n     */\n    function isRoleManager(address _account, uint16[] memory _groupIds) public view returns (bool) {\n        uint16[] memory _effectiveGroupIds = _groupIds.length == 0 ? _loadApplicablePermissionGroups(_account, bytes4(0), msg.sender) : _groupIds;\n        return doesUserHaveRole(_effectiveGroupIds, _account, uint8(Role.ROLE_MANAGER));\n    }\n\n    /**\n     * @notice Checks if an account has operator role within any of the specified groups\n     * @param _account The account address to check\n     * @param _groupIds The group IDs to check against (empty array if called from token)\n     * @return True if the account is whitelisted for operator role in at least one of the\n     *         specified groups AND not blacklisted for the same role in any group (blacklist supersedes whitelist)\n     */\n    function isOperator(address _account, uint16[] memory _groupIds) public view returns (bool) {\n        uint16[] memory _effectiveGroupIds = _groupIds.length == 0 ? _loadApplicablePermissionGroups(_account, bytes4(0), msg.sender) : _groupIds;\n        return doesUserHaveRole(_effectiveGroupIds, _account, uint8(Role.OPERATOR));\n    }\n\n    /**\n     * @notice Checks if an account has system role within any of the specified groups\n     * @param _account The account address to check\n     * @param _groupIds The group IDs to check against (empty array if called from token)\n     * @return True if the account is whitelisted for system role in at least one of the\n     *         specified groups AND not blacklisted for the same role in any group (blacklist supersedes whitelist)\n     */\n    function isSystem(address _account, uint16[] memory _groupIds) public view returns (bool) {\n        uint16[] memory _effectiveGroupIds = _groupIds.length == 0 ? _loadApplicablePermissionGroups(_account, bytes4(0), msg.sender) : _groupIds;\n        return doesUserHaveRole(_effectiveGroupIds, _account, uint8(Role.SYSTEM));\n    }\n\n    /**\n     * @notice Checks if an account has pauser role within any of the specified groups\n     * @param _account The account address to check\n     * @param _groupIds The group IDs to check against (empty array if called from token)\n     * @return True if the account is whitelisted for pauser role in at least one of the\n     *         specified groups AND not blacklisted for the same role in any group (blacklist supersedes whitelist)\n     */\n    function isPauser(address _account, uint16[] memory _groupIds) public view returns (bool) {\n        uint16[] memory _effectiveGroupIds = _groupIds.length == 0 ? _loadApplicablePermissionGroups(_account, bytes4(0), msg.sender) : _groupIds;\n        return doesUserHaveRole(_effectiveGroupIds, _account, uint8(Role.PAUSER));\n    }\n\n    /**\n     * @notice Checks if an account has minter/burner role within any of the specified groups\n     * @param _account The account address to check\n     * @param _groupIds The group IDs to check against (empty array if called from token)\n     * @return True if the account is whitelisted for minter/burner role in at least one of the\n     *         specified groups AND not blacklisted for the same role in any group (blacklist supersedes whitelist)\n     */\n    function isMinterBurner(address _account, uint16[] memory _groupIds) public view returns (bool) {\n        uint16[] memory _effectiveGroupIds = _groupIds.length == 0 ? _loadApplicablePermissionGroups(_account, bytes4(0), msg.sender) : _groupIds;\n        return doesUserHaveRole(_effectiveGroupIds, _account, uint8(Role.MINTER_BURNER));\n    }\n\n    /**\n     * @notice Checks if an account has token user manager role within any of the specified groups\n     * @param _account The account address to check\n     * @param _groupIds The group IDs to check against (empty array if called from token)\n     * @return True if the account is whitelisted for token user manager role in at least one of\n     *         the specified groups AND not blacklisted for the same role in any group (blacklist supersedes whitelist)\n     */\n    function isTokenUserManager(address _account, uint16[] memory _groupIds) public view returns (bool) {\n        uint16[] memory _effectiveGroupIds = _groupIds.length == 0 ? _loadApplicablePermissionGroups(_account, bytes4(0), msg.sender) : _groupIds;\n        return doesUserHaveRole(_effectiveGroupIds, _account, uint8(Role.TOKEN_USER_MANAGER));\n    }\n\n    /**\n     * @notice Checks if an account has token role within any of the specified groups\n     * @param _account The account address to check\n     * @param _groupIds The group IDs to check against (empty array if called from token)\n     * @return True if the account is whitelisted for token role in at least one of the\n     *         specified groups AND not blacklisted for the same role in any group (blacklist supersedes whitelist)\n     */\n    function isToken(address _account, uint16[] memory _groupIds) public view returns (bool) {\n        uint16[] memory _effectiveGroupIds = _groupIds.length == 0 ? _loadApplicablePermissionGroups(_account, bytes4(0), msg.sender) : _groupIds;\n        return doesUserHaveRole(_effectiveGroupIds, _account, uint8(Role.TOKEN));\n    }\n\n    /**\n     * @notice Checks if an account has token user role within any of the specified groups\n     * @param _account The account address to check\n     * @param _groupIds The group IDs to check against (empty array if called from token)\n     * @return True if the account is whitelisted for token user role in at least one of the\n     *         specified groups AND not blacklisted for the same role in any group (blacklist supersedes whitelist)\n     */\n    function isTokenUser(address _account, uint16[] memory _groupIds) public view returns (bool) {\n        uint16[] memory _effectiveGroupIds = _groupIds.length == 0 ? _loadApplicablePermissionGroups(_account, bytes4(0), msg.sender) : _groupIds;\n        return doesUserHaveRole(_effectiveGroupIds, _account, uint8(Role.TOKEN_USER));\n    }\n\n    /* ======== EXTERNAL/PUBLIC STATE-CHANGING FUNCTIONS ======== */\n\n    /**\n     * @notice Updates platform admin role for an account within a specific group\n     * @param _groupId The group ID for the account\n     * @param _account The account address to update\n     * @param _platformAdmin Whether to grant (true) or revoke (false) platform admin role\n     */\n    function updatePlatformAdmin(uint16 _groupId, address _account, bool _platformAdmin) external requiresAuth {\n        if (_platformAdmin) {\n            _addUserRole(_groupId, _account, uint8(Role.PLATFORM_ADMIN));\n        } else {\n            _resetUserRole(_groupId, _account, uint8(Role.PLATFORM_ADMIN));\n        }\n    }\n\n    /**\n     * @notice Updates role manager role for an account within a specific group\n     * @param _groupId The group ID for the account\n     * @param _account The account address to update\n     * @param _roleManager Whether to grant (true) or revoke (false) role manager role\n     */\n    function updateRoleManager(uint16 _groupId, address _account, bool _roleManager) external requiresAuth {\n        if (_roleManager) {\n            _addUserRole(_groupId, _account, uint8(Role.ROLE_MANAGER));\n        } else {\n            _resetUserRole(_groupId, _account, uint8(Role.ROLE_MANAGER));\n        }\n    }\n\n    /**\n     * @notice Updates operator role for an account within a specific group\n     * @param _groupId The group ID for the account\n     * @param _account The account address to update\n     * @param _operator Whether to grant (true) or revoke (false) operator role\n     */\n    function updateOperator(uint16 _groupId, address _account, bool _operator) external requiresAuth {\n        if (_operator) {\n            _addUserRole(_groupId, _account, uint8(Role.OPERATOR));\n        } else {\n            _resetUserRole(_groupId, _account, uint8(Role.OPERATOR));\n        }\n    }\n\n    /**\n     * @notice Updates system role for an account within a specific group\n     * @param _groupId The group ID for the account\n     * @param _account The account address to update\n     * @param _system Whether to grant (true) or revoke (false) system role\n     */\n    function updateSystem(uint16 _groupId, address _account, bool _system) external requiresAuth {\n        if (_system) {\n            _addUserRole(_groupId, _account, uint8(Role.SYSTEM));\n        } else {\n            _resetUserRole(_groupId, _account, uint8(Role.SYSTEM));\n        }\n    }\n\n    /**\n     * @notice Updates pauser role for an account within a specific group\n     * @param _groupId The group ID for the account\n     * @param _account The account address to update\n     * @param _pauser Whether to grant (true) or revoke (false) pauser role\n     */\n    function updatePauser(uint16 _groupId, address _account, bool _pauser) external requiresAuth {\n        if (_pauser) {\n            _addUserRole(_groupId, _account, uint8(Role.PAUSER));\n        } else {\n            _resetUserRole(_groupId, _account, uint8(Role.PAUSER));\n        }\n    }\n\n    /**\n     * @notice Updates minter/burner role for an account within a specific group\n     * @param _groupId The group ID for the account\n     * @param _account The account address to update\n     * @param _minterBurner Whether to grant (true) or revoke (false) minter/burner role\n     */\n    function updateMinterBurner(uint16 _groupId, address _account, bool _minterBurner) external requiresAuth {\n        if (isToken(msg.sender, new uint16[](ONE_ELEMENT_SIZE))) {\n            if (tokenInfoMap[msg.sender].customGroupId != 0) {\n                _groupId = tokenInfoMap[msg.sender].customGroupId;\n            }\n        }\n\n        if (_groupId == 0) revert InvalidGroupId();\n\n        if (_minterBurner) {\n            _addUserRole(_groupId, _account, uint8(Role.MINTER_BURNER));\n        } else {\n            _resetUserRole(_groupId, _account, uint8(Role.MINTER_BURNER));\n        }\n    }\n\n    /**\n     * @notice Updates token user manager role for an account within a specific group\n     * @param _groupId The group ID for the account\n     * @param _account The account address to update\n     * @param _tokenUserManager Whether to grant (true) or revoke (false) token user manager role\n     */\n    function updateTokenUserManager(uint16 _groupId, address _account, bool _tokenUserManager) external requiresAuth {\n        if (isToken(msg.sender, new uint16[](ONE_ELEMENT_SIZE))) {\n            if (tokenInfoMap[msg.sender].customGroupId != 0) {\n                _groupId = tokenInfoMap[msg.sender].customGroupId;\n            }\n        }\n\n        if (_groupId == 0) revert InvalidGroupId();\n\n        if (_tokenUserManager) {\n            _addUserRole(_groupId, _account, uint8(Role.TOKEN_USER_MANAGER));\n        } else {\n            _resetUserRole(_groupId, _account, uint8(Role.TOKEN_USER_MANAGER));\n        }\n    }\n\n    /**\n     * @notice Updates token user role for an account within a specific group\n     * @param _groupId The group ID for the account\n     * @param _account The account address to update\n     * @param _tokenUser Whether to grant (true) or revoke (false) token user role\n     */\n    function updateTokenUser(uint16 _groupId, address _account, bool _tokenUser) external requiresAuth {\n        if (isToken(msg.sender, new uint16[](ONE_ELEMENT_SIZE))) {\n            if (tokenInfoMap[msg.sender].customGroupId != 0) {\n                _groupId = tokenInfoMap[msg.sender].customGroupId;\n            }\n        }\n\n        if (_groupId == 0) revert InvalidGroupId();\n\n        if (_tokenUser) {\n            _addUserRole(_groupId, _account, uint8(Role.TOKEN_USER));\n        } else {\n            _resetUserRole(_groupId, _account, uint8(Role.TOKEN_USER));\n        }\n    }\n\n    /**\n     * @notice Blacklist a user from the token user role within a specific group\n     * @param _groupId The group ID for the account\n     * @param _account The account address to blacklist / unblacklist\n     * @param _blacklistTokenUser Whether to blacklist (true) or revoke the blacklist (false)\n     */\n    function blacklistTokenUser(uint16 _groupId, address _account, bool _blacklistTokenUser) external requiresAuth {\n        if (isToken(msg.sender, new uint16[](ONE_ELEMENT_SIZE))) {\n            if (tokenInfoMap[msg.sender].customGroupId != 0) {\n                _groupId = tokenInfoMap[msg.sender].customGroupId;\n            }\n        }\n\n        if (_groupId == 0) revert InvalidGroupId();\n\n        if (_blacklistTokenUser) {\n            _blacklistUserRole(_groupId, _account, uint8(Role.TOKEN_USER));\n        } else {\n            _resetUserRole(_groupId, _account, uint8(Role.TOKEN_USER));\n        }\n    }\n}"},{"file_path":"apps/tokenization/SygFactory/contracts/TokenManager.sol","source_code":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.30;\n\nimport {StorageLayout} from \"./storage/StorageLayout.sol\";\nimport {PermissionManagerCore} from \"libs/PermissionManager/PermissionManagerCore.sol\";\nimport {UUPSProxy} from \"apps/UUPSProxy/contracts/UUPSProxy.sol\";\nimport {IUUPSSynchronousUpgradeable} from \"libs/UUPS/interface/IUUPSSynchronousUpgradeable.sol\";\nimport {IUUPSAsynchronousUpgradeable} from \"libs/UUPS/interface/IUUPSAsynchronousUpgradeable.sol\";\nimport {SygTokenChecker} from \"apps/tokenization/SygToken/contracts/utils/SygTokenChecker.sol\";\n\n/**\n * @custom:security-contact blockchain@sygnum.com\n * @title TokenManager\n * @author SYGNUM AG - Blockchain team 3301\n * @notice Manages token deployment, upgrades, and configuration for SygToken instances\n * @dev This contract handles all token-related operations including deployment, upgrades.\n */\ncontract TokenManager is StorageLayout, PermissionManagerCore {\n    /* ======== CONSTANTS ======== */\n\n    uint16 private constant MANUAL_GROUP_RESERVE = 1000;\n    uint16 private constant MAX_GROUP_IDS = 15;\n\n    /* ======== ERRORS ======== */\n\n    error InvalidToken(address _token);\n    error AlreadyUpgraded(address _implementation);\n    error InvalidImplementation(address _implementation);\n    error CustomGroupIdAlreadyEnabled(address _token);\n    error TooManyGroupIds(uint256 provided, uint256 maximum);\n    error InvalidManualGroupId(uint16 groupId, uint16 maximum);\n    error GroupZeroReservedForFactory();\n    error SameImplementation(address _implementation);\n\n    /* ======== EVENTS ======== */\n\n    event SygTokenImplementationUpdated(address indexed oldImplementation, address indexed newImplementation);\n    event TokenDeployed(address indexed token, address implementation, bytes initData, uint16[] groupIds);\n    event TokenGroupIdsUpdated(address indexed token, uint16[] newGroupIds);\n    event TokenUpgradeScheduled(address indexed token, address indexed newImplementation);\n    event TokenUpgraded(address indexed token, address indexed newImplementation);\n    event TokenUpgradeCanceled(address indexed token);\n    event CustomGroupIdEnabled(address indexed token, uint16 customGroupId);\n    event TokenDestroyScheduled(address indexed token, address indexed destroyedImplementation);\n    event TokenDestroyed(address indexed token, address indexed destroyedImplementation);\n\n    /* ======== INITIALIZER ======== */\n\n    /**\n     * @notice Initializes the TokenManager with the initial implementation address\n     * @dev Internal function called during contract initialization to set up token deployment capabilities.\n     *      Only sets nextCustomGroupId if it has not been initialized yet (prevents reset on upgrades).\n     * @param _initialImplementation The address of the implementation contract used for token deployments\n     */\n    function _tokenManager_init(address _initialImplementation) internal {\n        _updateSygTokenImplementation(_initialImplementation);\n        if (nextCustomGroupId == 0) {\n            nextCustomGroupId = MANUAL_GROUP_RESERVE + 1;\n        }\n    }\n\n    /* ======== EXTERNAL/PUBLIC STATE-CHANGING FUNCTIONS ======== */\n\n    /**\n     * @notice Updates the SygToken implementation address\n     * @param _newImplementation The new implementation address\n     */\n    function updateSygTokenImplementation(address _newImplementation) external requiresAuth {\n        address _oldImplementation = getSygTokenImplementation;\n        if (_oldImplementation == _newImplementation) {\n            revert SameImplementation(_newImplementation);\n        }\n        _updateSygTokenImplementation(_newImplementation);\n    }\n\n    /**\n     * @notice Deploys a new token with specified token configuration and group IDs\n     * @param _name The name of the token\n     * @param _symbol The symbol of the token\n     * @param _decimals The number of decimals for the token\n     * @param _tokenURI The URI for token metadata\n     * @param _groupIds The manual group IDs for the token (must be in range 1-1000, group 0 is reserved for SygFactory administrative operations), maximum 15 IDs\n     * @param _timelockDelaySeconds The delay in seconds before upgrades can be executed on the token\n     * @return _token The deployed token address\n     */\n    function deployToken(\n        string memory _name,\n        string memory _symbol,\n        uint8 _decimals,\n        string memory _tokenURI,\n        uint16[] memory _groupIds,\n        uint256 _timelockDelaySeconds\n    ) external requiresAuth returns (address _token) {\n        if (_groupIds.length > MAX_GROUP_IDS) {\n            revert TooManyGroupIds(_groupIds.length, MAX_GROUP_IDS);\n        }\n        _validateManualGroupIds(_groupIds);\n\n        address _implementation = getSygTokenImplementation;\n        bytes memory _data = abi.encodeWithSignature(\n            \"initialize(string,string,uint8,string,address,uint256)\",\n            _name,\n            _symbol,\n            _decimals,\n            _tokenURI,\n            address(this),\n            _timelockDelaySeconds\n        );\n        UUPSProxy _tokenInstance = new UUPSProxy(_implementation, _data);\n        _token = address(_tokenInstance);\n\n        tokenInfoMap[_token] = TokenInfo({\n            tokenAddress: _token,\n            groupIds: _convertToFixedGroupIds(_groupIds),\n            implementationAddress: _implementation,\n            customGroupId: 0\n        });\n\n        _addUserRole(DEFAULT_GROUP, _token, uint8(Role.TOKEN));\n\n        emit TokenDeployed(_token, _implementation, _data, _groupIds);\n        return _token;\n    }\n\n    /**\n     * @notice Updates group IDs for a token\n     * @param _token The token address\n     * @param _groupIds The new group IDs for the token (must be in range 1-1000, group 0 is reserved for SygFactory administrative operations), maximum 15 IDs\n     */\n    function updateTokenGroupIds(address _token, uint16[] memory _groupIds) external requiresAuth {\n        if (tokenInfoMap[_token].tokenAddress == address(0)) {\n            revert InvalidToken(_token);\n        }\n        if (_groupIds.length > MAX_GROUP_IDS) {\n            revert TooManyGroupIds(_groupIds.length, MAX_GROUP_IDS);\n        }\n        _validateManualGroupIds(_groupIds);\n\n        tokenInfoMap[_token].groupIds = _convertToFixedGroupIds(_groupIds);\n        emit TokenGroupIdsUpdated(_token, _groupIds);\n    }\n\n    /**\n     * @notice Enables a custom group ID for a token\n     * @param _token The token address\n    */\n    function enableCustomGroupId(address _token) external requiresAuth {\n        if (tokenInfoMap[_token].tokenAddress == address(0)) {\n            revert InvalidToken(_token);\n        }\n        if (tokenInfoMap[_token].customGroupId != 0) {\n            revert CustomGroupIdAlreadyEnabled(_token);\n        }\n        tokenInfoMap[_token].customGroupId = nextCustomGroupId;\n        nextCustomGroupId++;\n        emit CustomGroupIdEnabled(_token, tokenInfoMap[_token].customGroupId);\n    }\n\n    /**\n     * @notice Upgrades a token to the latest implementation\n     * @dev Supports both synchronous (immediate) and asynchronous (delayed) UUPS upgrades.\n     *      For synchronous upgrades, the implementation is changed immediately and state is updated.\n     *      For asynchronous upgrades, the upgrade is scheduled and executeTokenUpgrade must be called later.\n     *      Detection is done by checking if the implementation address has changed after the upgrade call.\n     * @param _token The token address to upgrade\n     * @param _data The data to call on upgrade\n     */\n    function upgradeToken(address _token, bytes memory _data) external requiresAuth {\n        if (tokenInfoMap[_token].tokenAddress == address(0)) {\n            revert InvalidToken(_token);\n        }\n        address _newImpl = getSygTokenImplementation;\n        address _currentImpl = IUUPSSynchronousUpgradeable(_token).getImplementation();\n        if (_newImpl == _currentImpl) {\n            revert AlreadyUpgraded(_newImpl);\n        }\n\n        // Call upgradeToAndCall (works for both synchronous and asynchronous)\n        IUUPSSynchronousUpgradeable(_token).upgradeToAndCall(_newImpl, _data);\n\n        // Check if the implementation has actually changed\n        // - Synchronous: implementation changes immediately\n        // - Asynchronous: implementation remains unchanged (upgrade is scheduled)\n        address _actualImpl = IUUPSSynchronousUpgradeable(_token).getImplementation();\n        if (_actualImpl == _newImpl) {\n            tokenInfoMap[_token].implementationAddress = _actualImpl;\n            emit TokenUpgraded(_token, _newImpl);\n        } else {\n            emit TokenUpgradeScheduled(_token, _newImpl);\n        }\n    }\n\n    /**\n     * @notice Destroys a token by upgrading it to the destroyed implementation\n     * @dev Supports both synchronous (immediate) and asynchronous (delayed) UUPS upgrades.\n     *      For synchronous upgrades, the implementation is changed immediately and state is updated.\n     *      For asynchronous upgrades, the upgrade is scheduled and executeTokenUpgrade must be called later.\n     *      Detection is done by checking if the implementation address has changed after the upgrade call.\n     * @param _token The token address to destroy\n     */\n    function destroyToken(address _token) external requiresAuth {\n        if (tokenInfoMap[_token].tokenAddress == address(0)) {\n            revert InvalidToken(_token);\n        }\n\n        address _destroyedImpl = getSygTokenDestroyedImplementation;\n\n        // Call upgradeToAndCall with empty data (no re-initialization needed for destroyed implementation)\n        IUUPSSynchronousUpgradeable(_token).upgradeToAndCall(_destroyedImpl, \"\");\n\n        // Check if the implementation has actually changed\n        // - Synchronous: implementation changes immediately\n        // - Asynchronous: implementation remains unchanged (upgrade is scheduled)\n        address _actualImpl = IUUPSSynchronousUpgradeable(_token).getImplementation();\n        if (_actualImpl == _destroyedImpl) {\n            tokenInfoMap[_token].implementationAddress = _actualImpl;\n            emit TokenDestroyed(_token, _destroyedImpl);\n        } else {\n            emit TokenDestroyScheduled(_token, _destroyedImpl);\n        }\n    }\n\n    /**\n     * @notice Executes a scheduled token upgrade after the timelock delay\n     * @dev Only applicable for asynchronous (timelock) upgrades\n     *      Emits TokenDestroyed if the finalized implementation is the destroyed implementation,\n     *      otherwise emits TokenUpgraded\n     * @param _token The token address to complete the upgrade for\n     */\n    function executeTokenUpgrade(address _token) external requiresAuth {\n        if (tokenInfoMap[_token].tokenAddress == address(0)) {\n            revert InvalidToken(_token);\n        }\n        IUUPSAsynchronousUpgradeable(_token).executeUpgrade();\n        // Update the implementation address after successful execution\n        address _newImpl = IUUPSSynchronousUpgradeable(_token).getImplementation();\n        tokenInfoMap[_token].implementationAddress = _newImpl;\n\n        // Emit the appropriate event based on the finalized implementation\n        if (_newImpl == getSygTokenDestroyedImplementation) {\n            emit TokenDestroyed(_token, _newImpl);\n        } else {\n            emit TokenUpgraded(_token, _newImpl);\n        }\n    }\n\n    /**\n     * @notice Cancels a scheduled token upgrade\n     * @dev Only applicable for asynchronous (timelock) upgrades\n     * @param _token The token address to cancel the upgrade for\n     */\n    function cancelTokenUpgrade(address _token) external requiresAuth {\n        if (tokenInfoMap[_token].tokenAddress == address(0)) {\n            revert InvalidToken(_token);\n        }\n        IUUPSAsynchronousUpgradeable(_token).cancelUpgrade();\n        emit TokenUpgradeCanceled(_token);\n    }\n\n    /* ======== INTERNAL/PRIVATE STATE-CHANGING FUNCTIONS ======== */\n\n    /**\n     * @notice Validates that all group IDs are within the manual group range (1-1000) and not group 0\n     * @dev Group 0 (DEFAULT_GROUP) is reserved exclusively for SygFactory administrative operations\n     * @param _groupIds The array of group IDs to validate\n     */\n    function _validateManualGroupIds(uint16[] memory _groupIds) private pure {\n        for (uint256 _i; _i < _groupIds.length; _i++) {\n            if (_groupIds[_i] == DEFAULT_GROUP) {\n                revert GroupZeroReservedForFactory();\n            }\n            if (_groupIds[_i] > MANUAL_GROUP_RESERVE) {\n                revert InvalidManualGroupId(_groupIds[_i], MANUAL_GROUP_RESERVE);\n            }\n        }\n    }\n\n    /**\n     * @notice Converts dynamic array to fixed-size array for storage\n     * @param _groupIds The dynamic array of group IDs\n     * @return _groupIdsArray The fixed-size array with size in slot 0 and IDs in slots 1-MAX_GROUP_IDS\n     */\n    function _convertToFixedGroupIds(uint16[] memory _groupIds) private pure returns (uint16[16] memory) {\n        uint16[16] memory _groupIdsArray;\n        _groupIdsArray[0] = uint16(_groupIds.length); // Store size in first slot\n        for (uint256 _i; _i < _groupIds.length; _i++) {\n            _groupIdsArray[_i + 1] = _groupIds[_i]; // Store actual IDs starting from slot 1\n        }\n        return _groupIdsArray;\n    }\n\n    /**\n     * @notice Updates the SygToken implementation address (private version without auth)\n     * @param _newImplementation The new implementation address\n     */\n    function _updateSygTokenImplementation(address _newImplementation) private {\n        if (!SygTokenChecker.isSygToken(_newImplementation)) {\n            revert InvalidImplementation(_newImplementation);\n        }\n\n        address _oldImplementation = getSygTokenImplementation;\n        getSygTokenImplementation = _newImplementation;\n        emit SygTokenImplementationUpdated(_oldImplementation, _newImplementation);\n    }\n\n    /* ======== EXTERNAL/PUBLIC VIEW/PURE FUNCTIONS ======== */\n\n    /**\n     * @notice Gets token information with properly formatted group IDs\n     * @param _tokenAddress The address of the token\n     * @return tokenAddress The token address\n     * @return groupIds Dynamic array containing only the actual group IDs\n     * @return implementationAddress The implementation address\n     * @return customGroupId The custom group ID (0 if not enabled)\n     */\n    function getTokenInfo(address _tokenAddress) external view returns (\n        address tokenAddress,\n        uint16[] memory groupIds,\n        address implementationAddress,\n        uint16 customGroupId\n    ) {\n        TokenInfo storage _info = tokenInfoMap[_tokenAddress];\n        \n        // Get the actual number of group IDs from the first element\n        uint16 _groupIdCount = _info.groupIds[0];\n\n        // Create dynamic array with only the actual group IDs\n        groupIds = new uint16[](_groupIdCount);\n        for (uint16 _i; _i < _groupIdCount; _i++) {\n            groupIds[_i] = _info.groupIds[_i + 1];\n        }\n        \n        return (\n            _info.tokenAddress,\n            groupIds,\n            _info.implementationAddress,\n            _info.customGroupId\n        );\n    }\n}\n"},{"file_path":"apps/tokenization/SygFactory/contracts/storage/StorageLayout.sol","source_code":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.30;\n\nimport { StorageLayoutV1 } from \"./StorageLayoutV1.sol\";\n\n/**\n * @custom:security-contact blockchain@sygnum.com\n    * @title StorageLayout\n    * @author SYGNUM AG - Blockchain team 3301\n    * @notice This contract is used to store the state variables of SygFactory.sol and all its derivatives. The storage is separated from the logic to allow for a more flexible upgradeability pattern, avoiding storage collisions\n    * @dev CRITICAL: Do NOT add variables directly to this contract!\n    * Always create a new versioned base (StorageLayoutV2, StorageLayoutV3, etc.) that inherits from the previous version.\n    *\n    * Example upgrade path:\n    * 1. Create: abstract contract StorageLayoutV2 is StorageLayoutV1 { newVar; }\n    * 2. Update: abstract contract StorageLayout is StorageLayoutV2 {}\n*/\n\nabstract contract StorageLayout is StorageLayoutV1 {\n    // ⚠️ NEVER ADD VARIABLES HERE! See @dev comment above for upgrade instructions.\n}\n"},{"file_path":"apps/tokenization/SygFactory/contracts/storage/StorageLayoutV1.sol","source_code":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.30;\n\n/**\n * @custom:security-contact blockchain@sygnum.com\n * @title StorageLayoutV1\n * @notice Version 1 of storage layout for SygFactory\n * @dev CRITICAL: Once deployed, NEVER modify this contract!\n */\nabstract contract StorageLayoutV1 {\n\n    /* ======== CONSTANTS ======== */\n\n    uint16 internal constant DEFAULT_GROUP = 0;\n\n    /* ======== ENUMS ======== */\n\n    enum Role {\n        MINTER_BURNER,     // 0\n        OPERATOR,          // 1\n        PAUSER,            // 2\n        PLATFORM_ADMIN,    // 3\n        ROLE_MANAGER,      // 4\n        SYSTEM,            // 5\n        TOKEN,             // 6\n        TOKEN_USER,        // 7\n        TOKEN_USER_MANAGER // 8\n    }\n\n    /* ======== TYPE DECLARATIONS ======== */\n\n    struct TokenInfo {\n        address tokenAddress;\n        uint16 customGroupId;\n        uint16[16] groupIds; // groupIds[0] = size, groupIds[1..size] = actual group IDs, max 15 IDs\n        address implementationAddress;\n    }\n\n    /* ======== SygFactory Storage ======== */\n\n    address public getSygTokenImplementation;\n    mapping(address tokenAddress => TokenInfo info) internal tokenInfoMap;\n    uint16 internal nextCustomGroupId;\n    address public getSygTokenDestroyedImplementation;\n}\n"},{"file_path":"apps/tokenization/SygToken/contracts/utils/SygTokenChecker.sol","source_code":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.30;\n\nimport {ERC20Checker} from \"libs/ERC20/utils/ERC20Checker.sol\";\nimport {UUPSUpgradeableChecker} from \"libs/UUPS/utils/UUPSUpgradeableChecker.sol\";\n\n/**\n * @custom:security-contact blockchain@sygnum.com\n * @title SygTokenChecker\n * @author SYGNUM AG - Blockchain team 3301\n * @notice Utility library to check if a contract address implements the SygToken interface\n * @dev This library checks for SygToken-specific functions and uses ERC20Checker for ERC20 compliance\n *      and UUPSUpgradeableChecker for UUPS upgradeability verification.\n *      \n *      The checker verifies SygToken functions required for TokenManager integration:\n *      - UUPS upgradeability (via UUPSUpgradeableChecker)\n *      - ERC20 compliance (via ERC20Checker)\n *      \n *      Note: Individual SygToken functions (mint, burn, etc.) are not checked via staticcall\n *      because they have requiresAuth modifiers that will always fail for unauthorized callers.\n *      Instead, we rely on the presence of permission manager + ERC20 compliance as sufficient\n *      indicators of a valid SygToken implementation.\n */\nlibrary SygTokenChecker {\n    /**\n     * @notice Checks if a contract address implements the SygToken interface\n     * @dev Checks for SygToken-specific functions and ERC20 compliance.\n     * @param _tokenAddress The address to check\n     * @return isValidSygToken True if the address implements the SygToken interface, false otherwise\n     */\n    function isSygToken(\n        address _tokenAddress\n    ) internal view returns (bool isValidSygToken) {\n        // Check if address contains code (this also covers address(0))\n        if (_tokenAddress.code.length == 0) {\n            return false;\n        }\n\n        // Check ERC20 compliance\n        if (!ERC20Checker.isERC20(_tokenAddress)) {\n            return false;\n        }\n\n        // Check UUPS upgradeability\n        if (!UUPSUpgradeableChecker.isUUPSUpgradeable(_tokenAddress)) {\n            return false;\n        }\n\n        // Check SygToken-specific functions\n        return _checkSygTokenFunctions(_tokenAddress);\n    }\n\n    /**\n     * @notice Checks if a contract implements the SygToken interface through staticcall verification\n     * @dev Verifies the presence of SygToken-specific marker function\n     * @param _tokenAddress The address to check\n     * @return hasSygTokenFunctions True if SygToken functions are present, false otherwise\n     */\n    function _checkSygTokenFunctions(\n        address _tokenAddress\n    ) private view returns (bool hasSygTokenFunctions) {\n        // Check isSygTokenImplementation() marker function\n        // This is a non-reverting view function that returns true for valid SygToken implementations\n        (bool _success, bytes memory _returnData) = _tokenAddress\n            .staticcall(abi.encodeWithSignature(\"isSygTokenImplementation()\"));\n\n        // Validate call succeeded and return data is exactly 32 bytes (valid ABI-encoded bool)\n        if (!_success || _returnData.length != 32) {\n            return false;\n        }\n\n        // Decode and verify the function returns true\n        bool _isImplementation = abi.decode(_returnData, (bool));\n        return _isImplementation;\n    }\n}"},{"file_path":"apps/tokenization/SygToken/interfaces/ISygToken.sol","source_code":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.30;\n\nimport {IERC20} from \"libs/ERC20/interfaces/IERC20.sol\";\nimport {IPausable} from \"libs/Pausable/interfaces/IPausable.sol\";\n\n/**\n * @custom:security-contact blockchain@sygnum.com\n * @title ISygToken\n * @notice Interface for SygToken contract\n * @dev Extends IERC20 and IPausable and defines all public/external SygToken functions\n */\ninterface ISygToken is IERC20, IPausable {\n    /* ======== INITIALIZATION FUNCTIONS ======== */\n\n    /**\n     * @notice Initializes the SygToken contract\n     * @param _name The name of the token\n     * @param _symbol The symbol of the token\n     * @param _decimals The number of decimals for the token\n     * @param _tokenURI The URI for token metadata\n     * @param _permissionManagerAddress Address of the permission manager contract\n     */\n    function initialize(\n        string memory _name,\n        string memory _symbol,\n        uint8 _decimals,\n        string memory _tokenURI,\n        address _permissionManagerAddress\n    ) external;\n\n    /* ======== ROLE CHECKING FUNCTIONS (from RolesManager) ======== */\n\n    /**\n     * @notice Checks if an account is a role manager\n     * @param _account The account address to check\n     * @return True if the account is a role manager\n     */\n    function isRoleManager(address _account) external view returns (bool);\n\n    /**\n     * @notice Checks if an account is an operator\n     * @param _account The account address to check\n     * @return True if the account is an operator\n     */\n    function isOperator(address _account) external view returns (bool);\n\n    /**\n     * @notice Checks if an account is a system account\n     * @param _account The account address to check\n     * @return True if the account is a system account\n     */\n    function isSystem(address _account) external view returns (bool);\n\n    /**\n     * @notice Checks if an account is a pauser\n     * @param _account The account address to check\n     * @return True if the account is a pauser\n     */\n    function isPauser(address _account) external view returns (bool);\n\n    /**\n     * @notice Checks if an account is a minter/burner\n     * @param _account The account address to check\n     * @return True if the account is a minter/burner\n     */\n    function isMinterBurner(address _account) external view returns (bool);\n\n    /**\n     * @notice Checks if an account is a token user manager\n     * @param _account The account address to check\n     * @return True if the account is a token user manager\n     */\n    function isTokenUserManager(address _account) external view returns (bool);\n\n    /**\n     * @notice Checks if an account is a token user\n     * @param _account The account address to check\n     * @return True if the account is a token user\n     */\n    function isTokenUser(address _account) external view returns (bool);\n\n    /* ======== ROLE MANAGEMENT FUNCTIONS (from RolesManager) ======== */\n\n    /**\n     * @notice Updates the minter/burner status for a given account by calling SygFactory\n     * @param _account The address of the account to update\n     * @param _minterBurner True to give minter/burner role, false to remove it\n     */\n    function updateMinterBurner(address _account, bool _minterBurner) external;\n\n    /**\n     * @notice Updates the role manager status for a given account by calling SygFactory\n     * @param _account The address of the account to update\n     * @param _tokenUserManager True to give token user manager role, false to remove it\n     */\n    function updateTokenUserManager(address _account, bool _tokenUserManager) external;\n\n    /**\n     * @notice Updates the token user status for a given account by calling SygFactory\n     * @dev Requires custom group ID to be enabled (customGroupId != 0)\n     * @param _account The address of the account to update\n     * @param _tokenUser True to give token user role, false to remove it\n     */\n    function updateTokenUser(address _account, bool _tokenUser) external;\n\n    /**\n     * @notice Blacklist a user from the token user role for a given account by calling SygFactory\n     * @dev Requires custom group ID to be enabled (customGroupId != 0)\n     * @param _account The address of the account to update\n     * @param _blacklist True to blacklist the user, false to unblacklist\n     */\n    function blacklistTokenUser(address _account, bool _blacklist) external;\n\n    /* ======== TOKEN MANAGEMENT FUNCTIONS ======== */\n\n    /**\n     * @notice Mints tokens to a specified account\n     * @param _to The address to receive the minted tokens\n     * @param _amount The amount of tokens to mint (in wei)\n     */\n    function mint(address _to, uint256 _amount) external;\n\n    /**\n     * @notice Burns tokens from a specified address\n     * @param _from The address to burn tokens from\n     * @param _amount The amount of tokens to burn (in wei)\n     */\n    function burn(address _from, uint256 _amount) external;\n\n    /* ======== VIEW FUNCTIONS ======== */\n\n    /**\n     * @notice Gets the token URI\n     * @return The token URI string\n     */\n    function getTokenURI() external view returns (string memory);\n\n    /**\n     * @notice Gets the price feed oracle address\n     * @return The oracle address (can be zero)\n     */\n    function getPriceFeedOracleAddress() external view returns (address);\n\n    /**\n     * @notice Gets the latest asset price from an AggregatorV3Interface oracle\n     * @dev Only works when the configured oracle is an AggregatorV3Interface (not a bundle feed).\n     * @return _price The latest price as int256 (guaranteed positive)\n     * @return _updatedAt The timestamp of the last price update\n     */\n    function getPrice() external view returns (int256 _price, uint256 _updatedAt);\n\n    /**\n     * @notice Gets the latest bundle data from an IBundleAggregatorProxy oracle\n     * @dev Only works when the configured oracle is an IBundleAggregatorProxy (not a V3 price feed).\n     *      The returned bytes are an opaque payload whose schema depends on the specific feed being queried.\n     *      The caller is responsible for decoding the payload according to that feed's documentation.\n     * @return _bundleData The raw bundle data bytes\n     * @return _updatedAt The timestamp of the last bundle update\n     */\n    function getBundleData() external view returns (bytes memory _bundleData, uint256 _updatedAt);\n\n    /**\n     * @notice Gets the price feed decimals\n     * @dev For AggregatorV3Interface, returns a single-element array.\n     *      For IBundleAggregatorProxy, returns the bundleDecimals() array as-is.\n     * @return The decimals array\n     */\n    function getPriceFeedDecimals() external view returns (uint8[] memory);\n\n    /**\n     * @notice Gets the price feed description\n     * @dev Calls the oracle contract to get the description\n     * @return The price feed description string\n     */\n    function getPriceFeedDescription() external view returns (string memory);\n\n    /* ======== STATE-CHANGING FUNCTIONS ======== */\n\n    /**\n     * @notice Forcefully transfers tokens from any account to a whitelisted receiver\n     * @dev Only callable by OPERATOR role. Receiver must be whitelisted (TOKEN_USER).\n     * @param _from The sender address\n     * @param _to The recipient address (must be TOKEN_USER)\n     * @param _amount The amount to transfer\n     */\n    function forcedTransfer(\n        address _from,\n        address _to,\n        uint256 _amount\n    ) external;\n\n    /**\n     * @notice Updates the token URI\n     * @dev Only callable by OPERATOR role\n     * @param _newTokenURI The new token URI\n     */\n    function updateTokenURI(string memory _newTokenURI) external;\n\n    /**\n     * @notice Recovers ERC20 tokens or native currency locked in the contract\n     * @dev Only callable by OPERATOR role. Use address(0) for native currency.\n     * @param _token The token address (zero for native currency)\n     * @param _to The recipient address\n     * @param _amountWei The amount to recover in wei\n     */\n    function recoverFunds(\n        address _token,\n        address payable _to,\n        uint256 _amountWei\n    ) external;\n\n    /**\n     * @notice Updates the price feed oracle address\n     * @dev Only callable by PLATFORM_ADMIN role. Can be set to zero to disable oracle.\n     * @param _newPriceFeedOracleAddress The new oracle address (can be zero)\n     */\n    function updatePricefeedOracleAddress(address _newPriceFeedOracleAddress) external;\n}\n"},{"file_path":"apps/tokenization/SygTokenDestroyed/contracts/interfaces/ISygDestroyedImplementation.sol","source_code":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.30;\n\n/**\n * @title ISygDestroyedImplementation\n * @notice Interface for SygTokenDestroyed implementation\n * @dev This interface defines all external functions of a destroyed token implementation.\n *      It includes both the marker function for validation and the operational functions.\n */\ninterface ISygDestroyedImplementation {\n    /* ======== ERRORS ======== */\n\n    /**\n     * @notice Thrown when attempting any operation on a destroyed contract\n     */\n    error ContractDestroyed();\n\n    /* ======== EXTERNAL FUNCTIONS ======== */\n\n    /**\n     * @notice Recovers funds from the contract\n     * @dev If _token is address(0), recovers native token (ETH), else recovers ERC20 tokens\n     * @param _token The token address (zero for native)\n     * @param _to The recipient address\n     * @param _amount The amount to recover (in wei for native ETH, in token's smallest unit for ERC-20)\n     */\n    function recoverFunds(address _token, address payable _to, uint256 _amount) external;\n\n    /**\n     * @notice Marker function to identify SygTokenDestroyed implementations\n     * @dev Returns true if the contract is a valid SygTokenDestroyed implementation\n     * @return True if this is a SygTokenDestroyed implementation, false otherwise\n     */\n    function isSygTokenDestroyedImplementation() external pure returns (bool);\n}\n"},{"file_path":"libs/ERC20/interfaces/IERC20.sol","source_code":"// SPDX-License-Identifier: MIT\npragma solidity >=0.8.0;\n\n/**\n * @custom:security-contact blockchain@sygnum.com\n * @title IERC20\n * @author SYGNUM AG - Blockchain team 3301\n * @dev Interface of the ERC-20 standard as defined in the ERC with extensions:\n *   1. EIP-2612 permit functionality for gasless approvals\n *   2. Standard metadata functions (name, symbol, decimals)\n *\n * This interface matches the implementation in ERC20.sol\n */\ninterface IERC20 {\n    /* ========== EVENTS ========== */\n\n    /**\n     * @dev Emitted when `amount` tokens are moved from one account (`from`) to\n     * another (`to`).\n     *\n     * Note that `amount` may be zero.\n     */\n    event Transfer(address indexed from, address indexed to, uint256 amount);\n\n    /**\n     * @dev Emitted when the allowance of a `spender` for an `owner` is set by\n     * a call to {approve}. `amount` is the new allowance.\n     */\n    event Approval(address indexed owner, address indexed spender, uint256 amount);\n\n    /* ========== STATE VARIABLES ========== */\n\n    /**\n     * @dev Returns the name of the token.\n     * Part of the ERC20 optional metadata extension.\n     */\n    function name() external view returns (string memory);\n\n    /**\n     * @dev Returns the symbol of the token.\n     * Part of the ERC20 optional metadata extension.\n     */\n    function symbol() external view returns (string memory);\n\n    /**\n     * @dev Returns the decimals places of the token.\n     * Part of the ERC20 optional metadata extension.\n     */\n    function decimals() external view returns (uint8);\n\n    /**\n     * @dev Returns the value of tokens in existence.\n     */\n    function totalSupply() external view returns (uint256);\n\n    /**\n     * @dev Returns the value of tokens owned by `account`.\n     */\n    function balanceOf(address _account) external view returns (uint256);\n\n    /**\n     * @dev Returns the remaining number of tokens that `spender` will be\n     * allowed to spend on behalf of `owner` through {transferFrom}. This is\n     * zero by default.\n     *\n     * This value changes when {approve} or {transferFrom} are called.\n     */\n    function allowance(address _owner, address _spender) external view returns (uint256);\n\n    /**\n     * @dev Returns the current nonce for `owner`.\n     * This value is used in EIP-2612 permit function and must be\n     * included whenever a signature is generated.\n     *\n     * Every successful call to {permit} increases the owner's nonce by one,\n     * preventing signature reuse.\n     */\n    function nonces(address _owner) external view returns (uint256);\n    \n    /* ========== FUNCTIONS ========== */\n\n    /**\n     * @dev Moves a `value` amount of tokens from the caller's account to `to`.\n     *\n     * Returns a boolean value indicating whether the operation succeeded.\n     *\n     * Emits a {Transfer} event.\n     */\n    function transfer(address _to, uint256 _value) external returns (bool);\n\n    /**\n     * @dev Sets a `value` amount of tokens as the allowance of `spender` over the\n     * caller's tokens.\n     *\n     * Returns a boolean value indicating whether the operation succeeded.\n     *\n     * IMPORTANT: Beware that changing an allowance with this method brings the risk\n     * that someone may use both the old and the new allowance by unfortunate\n     * transaction ordering. One possible solution to mitigate this race\n     * condition is to first reduce the spender's allowance to 0 and set the\n     * desired value afterwards:\n     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\n     *\n     * Emits an {Approval} event.\n     */\n    function approve(address _spender, uint256 _value) external returns (bool);\n\n    /**\n     * @dev Moves a `value` amount of tokens from `from` to `to` using the\n     * allowance mechanism. `value` is then deducted from the caller's\n     * allowance.\n     *\n     * Returns a boolean value indicating whether the operation succeeded.\n     *\n     * Emits a {Transfer} event.\n     */\n    function transferFrom(address _from, address _to, uint256 _value) external returns (bool);\n\n    /**\n     * @dev EIP-2612 permit function to approve token spending with a signature\n     * instead of msg.sender calling approve directly.\n     *\n     * Sets `value` as the allowance of `spender` over `owner`'s tokens,\n     * given `owner`'s signed approval.\n     *\n     * Requirements:\n     * - `deadline` must not have expired\n     * - `v`, `r` and `s` must form a valid secp256k1 signature from `owner`\n     * - The signature must use the current nonce of `owner` (see {nonces})\n     *\n     * Emits an {Approval} event.\n     */\n    function permit(\n        address _owner,\n        address _spender,\n        uint256 _value,\n        uint256 _deadline,\n        uint8 _v,\n        bytes32 _r,\n        bytes32 _s\n    ) external;\n}"},{"file_path":"libs/ERC20/utils/ERC20Checker.sol","source_code":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.30;\n\n/**\n * @custom:security-contact blockchain@sygnum.com\n * @title ERC20Checker\n * @author SYGNUM AG - Blockchain team 3301\n * @notice Utility library to check if a contract address implements the core ERC20 interface (excluding optional extensions)\n * @dev This library first attempts to use ERC165 to check for ERC20 interface support. If ERC165 is properly\n *      implemented, its response is trusted. Only when ERC165 is not implemented does it fall back to\n *      staticcall verification of required ERC20 view functions.\n *      It is not a guarantee of full ERC20 compliance, but reduces the risk of misidentification.\n *\n *      Per EIP-20, the functions `name()`, `symbol()`, and `decimals()` are optional and are NOT checked here.\n *      Only the required ERC20 view functions are enforced in the fallback check.\n *\n *      State-changing functions (`transfer`, `transferFrom`, `approve`) are NOT checked here.\n *      This is because calling these functions with staticcall may cause some token contracts to revert,\n *      as they might include additional checks or logic that behaves differently in a static context.\n *      Additionally, some tokens may return different values or revert when called in a static context,\n *      which could lead to false negatives. For this reason, only view functions are checked for interface presence.\n */\nlibrary ERC20Checker {\n    bytes4 private constant INTERFACE_ID_ERC165 = 0x01ffc9a7;\n    bytes4 private constant INTERFACE_ID_ERC20 = 0x36372b07;\n    bytes4 private constant INTERFACE_ID_INVALID = 0xffffffff;\n\n    /**\n     * @notice Checks if a contract address implements the core ERC20 interface\n     * @dev First attempts ERC165 interface detection. If ERC165 is properly implemented, its response is trusted.\n     *      Only when ERC165 is not implemented does it fall back to staticcall verification of required ERC20 view functions.\n     *      Optional metadata functions (name, symbol, decimals) are NOT checked per EIP-20.\n     *      State-changing functions (transfer, transferFrom, approve) are NOT checked to avoid false negatives due to staticcall behavior.\n     * @param _tokenAddress The address to check\n     * @return isErc20 True if the address is likely an ERC20 contract, false otherwise\n     */\n    function isERC20(\n        address _tokenAddress\n    ) internal view returns (bool isErc20) {\n        if (_tokenAddress == address(0)) {\n            return false;\n        }\n\n        if (_tokenAddress.code.length == 0) {\n            return false;\n        }\n        \n        (bool _supportsERC165, bool _validERC165) = _isValidERC165(_tokenAddress);\n        if (_supportsERC165) {\n            if (!_validERC165) {\n                return false;\n            }\n            if (!_checkERC20ViaERC165(_tokenAddress)) {\n                return false;\n            }\n            return true;\n        }\n\n        return _checkERC20Functions(_tokenAddress);\n    }\n\n    /**\n     * @notice Checks if a contract properly implements ERC165 and returns (supportsERC165, validERC165)\n     * @dev Returns (false, false) if staticcall fails or returns malformed data (not ERC165, allow fallback)\n     *      Returns (true, false) if claims ERC165 but is invalid (do not fallback)\n     *      Returns (true, true) if valid ERC165\n     */\n    function _isValidERC165(\n        address _contractAddress\n    ) private view returns (bool _supportsERC165, bool _validERC165) {\n        (bool _success1, bytes memory _data1) = _contractAddress.staticcall(\n            abi.encodeWithSignature(\n                \"supportsInterface(bytes4)\",\n                INTERFACE_ID_ERC165\n            )\n        );\n        if (!_success1 || _data1.length < 32) {\n            // Not ERC165, allow fallback\n            return (false, false);\n        }\n\n        // Validate ERC165 implementation by checking it doesn't support invalid interface\n        (bool _success2, bytes memory _data2) = _contractAddress.staticcall(\n            abi.encodeWithSignature(\n                \"supportsInterface(bytes4)\",\n                INTERFACE_ID_INVALID\n            )\n        );\n        if (!_success2 || _data2.length < 32) {\n            // Claims ERC165 but invalid/malformed, treat as invalid, do not fallback\n            return (true, false);\n        }\n        bool _claimsInvalidSupport = abi.decode(_data2, (bool));\n        if (_claimsInvalidSupport) {\n            // Invalid ERC165 implementation - should never return true for 0xffffffff\n            return (true, false);\n        }\n        // Valid ERC165\n        return (true, true);\n    }\n\n    /**\n     * @notice Checks if a contract supports ERC20 interface via ERC165\n     * @dev Only call this function if _isValidERC165 returns true\n     * @param _contractAddress The address to check\n     * @return supportsERC20 True if the contract supports ERC20 interface, false otherwise\n     */\n    function _checkERC20ViaERC165(\n        address _contractAddress\n    ) private view returns (bool supportsERC20) {\n        (bool _success, bytes memory _data) = _contractAddress.staticcall(\n            abi.encodeWithSignature(\n                \"supportsInterface(bytes4)\",\n                INTERFACE_ID_ERC20\n            )\n        );\n\n        if (!_success || _data.length < 32) {\n            return false;\n        }\n\n        return abi.decode(_data, (bool));\n    }\n\n    /**\n     * @notice Checks if a contract implements the core ERC20 interface through staticcall verification\n     * @dev Verifies the presence of all required ERC20 view functions\n     * @param _tokenAddress The address to check\n     * @return hasERC20Functions True if all required ERC20 functions are present, false otherwise\n     */\n    function _checkERC20Functions(\n        address _tokenAddress\n    ) private view returns (bool hasERC20Functions) {\n        // Check totalSupply()\n        (bool _okTotalSupply, bytes memory _totalSupplyData) = _tokenAddress\n            .staticcall(abi.encodeWithSignature(\"totalSupply()\"));\n        if (!_okTotalSupply || _totalSupplyData.length < 32) return false;\n\n        // Check balanceOf(address)\n        (bool _okBalanceOf, bytes memory _balanceOfData) = _tokenAddress\n            .staticcall(\n                abi.encodeWithSignature(\"balanceOf(address)\", address(0))\n            );\n        if (!_okBalanceOf || _balanceOfData.length < 32) return false;\n\n        // Check allowance(address,address)\n        (bool _okAllowance, bytes memory _allowanceData) = _tokenAddress\n            .staticcall(\n                abi.encodeWithSignature(\n                    \"allowance(address,address)\",\n                    address(0),\n                    address(0)\n                )\n            );\n        if (!_okAllowance || _allowanceData.length < 32) return false;\n\n        return true;\n    }\n}\n"},{"file_path":"libs/Initializable/Initializable.sol","source_code":"// SPDX-License-Identifier: UNLICENSED\npragma solidity ^0.8.30;\n\nimport {InitializableView} from \"libs/Initializable/InitializableView.sol\";\n\n/**\n * @custom:security-contact blockchain@sygnum.com\n * @title Initializable\n * @notice Standard Initializable implementation that extends InitializableView\n * and exposes the public interface functions.\n * @dev This contract introduces an `initializer` modifier to prevent re-initialization of contracts,\n * ensuring that critical setup logic is executed only once, which is especially important in upgradeable contracts.\n */\nabstract contract Initializable is InitializableView {\n}\n"},{"file_path":"libs/Initializable/InitializableCore.sol","source_code":"// SPDX-License-Identifier: UNLICENSED\npragma solidity ^0.8.30;\n\n/**\n * @custom:security-contact blockchain@sygnum.com\n * @title InitializableCore\n * @notice Core implementation of initialization functionality with slot storage pattern\n * @dev This contract provides the core initialization logic without exposing public functions.\n * It uses a storage slot to track initialization status, minimizing gas costs and avoiding storage collisions.\n * This is the core implementation that should be inherited by contracts that need custom initialization logic.\n */\nabstract contract InitializableCore {\n    /* ======== Custom Errors ======== */\n    error InvalidInitialization();\n\n    /* ======== Events ======== */\n    event Initialized();\n\n    /* ======== State variables ======== */\n    /**\n     * @dev INITIALIZED_CORE_STORAGE_SLOT is a constant used to locate the initialization status in storage for InitializableCore.\n     * This ensures a unique storage slot for the initialized flag, minimizing collision risks with other storage variables in the contract.\n     * The value is calculated as keccak256(abi.encode(uint256(keccak256(\"InitializableCore.InitializedStorage\")) - 1)) & ~bytes32(uint256(0xff)).\n     * @custom:storage-location erc7201:InitializableCore.InitializedStorage\n     */\n    bytes32 private constant INITIALIZED_CORE_STORAGE_SLOT =\n        0x112dd7b7d700a9bfd03edf4d4ce6f92b1f5f1a2fd81fde871f4db11384aa0b00;\n\n    /* ======== Modifiers ======== */\n    /// @notice Ensures the initializer function can only be called once.\n    modifier initializer() {\n        if (_isInitialized()) {\n            revert InvalidInitialization();\n        }\n        _setInitialized();\n        _;\n    }\n\n    /* ======== Internal Pure/View Functions ======== */\n    /// @notice Checks if the contract has been initialized.\n    /// @return value True if the contract has been initialized, false otherwise.\n    function _isInitialized() internal view returns (bool value) {\n        // solhint-disable-next-line no-inline-assembly\n        assembly ('memory-safe') {\n            value := sload(INITIALIZED_CORE_STORAGE_SLOT)\n        }\n    }\n\n    /* ======== Internal/Private Storage Manipulation Functions ======== */\n    /// @notice Sets the initialized state of the contract.\n    /// @dev Uses assembly for gas efficiency when writing to the storage slot.\n    function _setInitialized() internal {\n        // solhint-disable-next-line no-inline-assembly\n        assembly ('memory-safe') {\n            sstore(INITIALIZED_CORE_STORAGE_SLOT, 1)\n        }\n        emit Initialized();\n    }\n\n    /// @notice Resets the initialized state of the contract.\n    function _resetInitialized() internal {\n        // solhint-disable-next-line no-inline-assembly\n        assembly ('memory-safe') {\n            sstore(INITIALIZED_CORE_STORAGE_SLOT, 0)\n        }\n    }\n}\n"},{"file_path":"libs/Initializable/InitializableView.sol","source_code":"// SPDX-License-Identifier: UNLICENSED\npragma solidity ^0.8.30;\n\nimport {InitializableCore} from \"libs/Initializable/InitializableCore.sol\";\n\n/**\n * @custom:security-contact blockchain@sygnum.com\n * @title InitializableView\n * @author SYGNUM AG - Blockchain team 3301\n * @notice View-only Initializable implementation that exposes initialization status\n * @dev This contract provides read-only access to initialization state without\n *      allowing initialization modifications. It's designed for contracts that need\n *      to query initialization status but should not modify state directly.\n */\nabstract contract InitializableView is InitializableCore {\n    /*//////////////////////////////////////////////////////////////\n                        EXTERNAL/PUBLIC VIEW FUNCTIONS\n    //////////////////////////////////////////////////////////////*/\n\n    /**\n     * @notice Returns the initialization status of the contract\n     * @return Whether the contract has been initialized\n     */\n    function isInitialized() public view returns (bool) {\n        return _isInitialized();\n    }\n}\n"},{"file_path":"libs/Pausable/interfaces/IPausable.sol","source_code":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.28;\n\n/**\n * @custom:security-contact blockchain@sygnum.com\n * @title IPausable\n * @notice Interface for pausable functionality\n * @dev Defines the external interface for contracts that can be paused and unpaused\n */\ninterface IPausable {\n    /* ======== VIEW FUNCTIONS ======== */\n\n    /**\n     * @notice Returns whether the contract is paused\n     * @return True if the contract is paused, false otherwise\n     */\n    function isPaused() external view returns (bool);\n\n    /* ======== STATE-CHANGING FUNCTIONS ======== */\n\n    /**\n     * @notice Pauses the contract\n     * @dev Only callable by authorized accounts (e.g., pausers)\n     */\n    function pause() external;\n\n    /**\n     * @notice Unpauses the contract\n     * @dev Only callable by authorized accounts (e.g., pausers)\n     */\n    function unpause() external;\n}"},{"file_path":"libs/PermissionManager/PermissionManagerCore.sol","source_code":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.28;\n\n/**\n * @custom:security-contact blockchain@sygnum.com\n * @title PermissionManagerCore\n * @author SYGNUM AG - Blockchain team 3301\n * @notice Core permission management implementation with slot storage pattern\n * @dev This contract provides the core functionality for role-based permissions with group-based access control.\n *      It uses slot storage pattern to manage role-based permissions with group support.\n *      It provides a comprehensive system for:\n *      - Assigning roles to users within specific groups\n *      - Configuring function-level permissions for roles\n *      - Setting public permissions for specific functions\n *      - Managing user whitelist/blacklist status for roles\n *\n *      The group model allows for logical separation of users across different departments\n *      or organizations. Each user can have different role assignments in different groups,\n *      enabling fine-grained access control across organizational boundaries.\n *\n *      Contracts that inherit from PermissionManagerCore must implement any additional\n *      authorization logic specific to their use case while leveraging the base\n *      permission management system provided here.\n *\n *      The system supports up to 128 different roles (0-127) per group and maintains separate\n *      role assignments for each user in each group. Each role has both whitelist and blacklist\n *      bits, allowing for granular permission control. Public functions can be accessed by\n *      anyone, regardless of their role assignments.\n */\nabstract contract PermissionManagerCore {\n    /* ======== STATE VARIABLES ======== */\n    \n    /**\n     * @dev PERMISSION_MANAGER_STORAGE_SLOT is a constant used to locate the PermissionManagerStorage struct in storage.\n     * This ensures a unique storage slot, minimizing collision risks with other storage variables in the contract.\n     * It's calculated as keccak256(abi.encode(uint256(keccak256(\"PermissionManagerCore.PermissionManagerStorage\")) - 1)) & ~bytes32(uint256(0xff)).\n     */\n    bytes32 private constant PERMISSION_MANAGER_CORE_STORAGE_SLOT =\n        0x31f703e9d9d60912870f8b3156da76d11cbd7f7db67e551ddf3ac9400fb05e00;\n\n    /// @custom:storage-location erc7201:PermissionManagerCore.PermissionManagerStorage\n    struct PermissionManagerStorage {\n        mapping(uint16 groupId => mapping(address user => bytes32 roles)) getRolesOnGroup;\n        mapping(bytes4 functionSig => bool isPublic) isPublicFunction;\n        mapping(bytes4 functionSig => bytes32 roles) getFunctionRoles;\n        mapping(bytes4 functionSig => bool isRegistered) isFunctionRegistered;\n    }\n\n    /* ========== CONSTANTS ========== */\n    uint8 internal constant WHITELIST_BIT_OFFSET = 0;\n    uint8 internal constant BLACKLIST_BIT_OFFSET = 1;\n    uint8 internal constant BITS_PER_ROLE = 2;\n    uint8 internal constant MAX_ROLES_PER_GROUP = 128;\n    bytes32 internal constant WHITELIST_MASK = 0x5555555555555555555555555555555555555555555555555555555555555555; // ...010101 (right-to-left: whitelist bits at positions 0,2,4,... for roles 0,1,2,... e.g., 000001 = whitelisted role 0, 000100 = whitelisted role 1)\n    bytes32 internal constant BLACKLIST_MASK = 0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA; // ...101010 (right-to-left: blacklist bits at positions 1,3,5,... for roles 0,1,2,... e.g., 000010 = blacklisted role 0, 001000 = blacklisted role 1)\n\n    /* ========== EVENTS ========== */\n\n    event UserRoleAdded(address indexed user, uint16 indexed group, uint8 indexed role);\n    event UserRoleBlacklisted(address indexed user, uint16 indexed group, uint8 indexed role);\n    event UserRoleReset(address indexed user, uint16 indexed group, uint8 indexed role);\n    event UserRolesReset(address indexed user, uint16 indexed group);\n    event FunctionRegistered(bytes4 indexed functionSig);\n    event FunctionPermissionUpdated(bytes4 indexed functionSig, bool isPublic, bytes32 roles);\n    event FunctionPermissionsReset(bytes4 indexed functionSig);\n\n    /* ========== ERRORS ========== */\n\n    error Unauthorized();\n    error InvalidRoleId(uint8 expectedRole);\n    error InvalidRole(address account, uint8 expectedRole);\n    error GroupMismatch();\n    error AddressAlreadyInGroup();\n    error FunctionHasNoPermissions(bytes4 functionSig);\n    error FunctionAlreadyRegistered(bytes4 functionSig);\n    error FunctionNotRegistered(bytes4 functionSig);\n    error FunctionHasRolePermissions(bytes4 functionSig);\n    error EmptyRolesArray();\n\n    /* ========== VIEW FUNCTIONS ========== */\n\n    /**\n     * @notice Returns the role bitmap for a user in a specific group\n     * @param _group The group identifier to check\n     * @param _user The address of the user to check\n     * @return The role bitmap of the user in the group\n     */\n    function _getUserRolesInGroup(uint16 _group, address _user) internal view returns (bytes32) {\n        PermissionManagerStorage storage $ = _getPermissionManagerStorage();\n        return $.getRolesOnGroup[_group][_user];\n    }\n\n    /**\n     * @notice Returns whether a function is public\n     * @param _functionSig The function signature to check\n     * @return Whether the function is public\n     */\n    function _isPublicFunction(bytes4 _functionSig) internal view returns (bool) {\n        PermissionManagerStorage storage $ = _getPermissionManagerStorage();\n        return $.isPublicFunction[_functionSig];\n    }\n\n    /**\n     * @notice Returns the roles that can call a function\n     * @param _functionSig The function signature to check\n     * @return The role bitmap for the function\n     */\n    function _getFunctionRoles(bytes4 _functionSig) internal view returns (bytes32) {\n        PermissionManagerStorage storage $ = _getPermissionManagerStorage();\n        return $.getFunctionRoles[_functionSig];\n    }\n\n    /**\n     * @notice Checks if a user is blacklisted for a specific role across multiple groups\n     * @dev Uses bitwise operations to check the blacklist bit for the role in merged roles.\n     *      Each role uses 2 bits: whitelist (even positions) and blacklist (odd positions).\n     *      Example: For role 0, checks bit 1; for role 1, checks bit 3.\n     *      Bit pattern examples (6 bits): 000010 = blacklisted role 0, 001000 = blacklisted role 1\n     * @param _groups Array of group identifiers to check\n     * @param _user The address of the user to check\n     * @param _role The role identifier to check (0-127)\n     * @return Boolean indicating whether the user is blacklisted for the role in any of the groups\n     */\n    function _isUserBlacklistedForRole(uint16[] memory _groups, address _user, uint8 _role) internal view returns (bool) {\n        if (_role >= MAX_ROLES_PER_GROUP) revert InvalidRoleId(_role);\n\n        bytes32 _mergedRoles = _getMergedUserRoles(_user, _groups);\n        uint256 _bitPosition = _role * BITS_PER_ROLE + BLACKLIST_BIT_OFFSET;\n        return (uint256(_mergedRoles) >> _bitPosition) & 1 != 0;\n    }\n\n    /**\n     * @notice Checks if a user has effective permission for a role across multiple groups (whitelisted and not blacklisted)\n     * @dev A user has effective permission if they are whitelisted and not blacklisted in any of the groups\n     * Uses bitwise operations to check the whitelist bit for the role in merged roles.\n     *      Each role uses 2 bits: whitelist (even positions) and blacklist (odd positions).\n     *      Example: For role 0, checks bit 0; for role 1, checks bit 2.\n     *      Bit pattern examples (6 bits): 000001 = whitelisted role 0, 000100 = whitelisted role 1\n     * @param _groups Array of group identifiers to check\n     * @param _user The address of the user to check\n     * @param _role The role identifier to check (0-127)\n     * @return Boolean indicating whether the user has effective permission for the role in any of the groups\n     */\n    function _doesUserHaveRole(uint16[] memory _groups, address _user, uint8 _role) internal view returns (bool) {\n        if (_role >= MAX_ROLES_PER_GROUP) revert InvalidRoleId(_role);\n\n        bytes32 _mergedRoles = _getMergedUserRoles(_user, _groups);\n        uint256 _bitPosition = _role * BITS_PER_ROLE + WHITELIST_BIT_OFFSET;\n        bool _isWhitelisted = (uint256(_mergedRoles) >> _bitPosition) & 1 != 0;\n        \n        return _isWhitelisted && !_isUserBlacklistedForRole(_groups, _user, _role);\n    }\n\n    /**\n     * @notice Checks if a specific role has permission to call a function\n     * @dev Uses bitwise operations to efficiently check permission assignment.\n     *      Checks the whitelist bit for the role in the function's permission bitmap.\n     *      Example: For role 0, checks bit 0; for role 1, checks bit 2.\n     *      Bit pattern examples (6 bits): 000001 = role 0 has permission, 000100 = role 1 has permission\n     * @param _role The role identifier to check (0-127)\n     * @param _functionSig The 4-byte function signature to check permissions for\n     * @return Boolean indicating whether the role has permission for the function\n     */\n    function _doesRoleHavePermission(uint8 _role, bytes4 _functionSig) internal view returns (bool) {\n        if (_role >= MAX_ROLES_PER_GROUP) revert InvalidRoleId(_role);\n\n        PermissionManagerStorage storage $ = _getPermissionManagerStorage();\n        uint256 _whitelistBitPosition = _role * BITS_PER_ROLE + WHITELIST_BIT_OFFSET;\n        return (uint256($.getFunctionRoles[_functionSig]) >> _whitelistBitPosition) & 1 != 0;\n    }\n\n    /**\n     * @notice Merges user roles across multiple groups using OR operation\n     * @dev Combines role bitmaps from all specified groups to create a unified view of user permissions\n     * @param _user The address of the user to get merged roles for\n     * @param _groups Array of group IDs to merge roles from\n     * @return The merged role bitmap combining all groups\n     */\n    function _getMergedUserRoles(address _user, uint16[] memory _groups) internal view returns (bytes32) {\n        PermissionManagerStorage storage $ = _getPermissionManagerStorage();\n        bytes32 _mergedRoles = bytes32(0);\n\n        for (uint256 _i; _i < _groups.length; _i++) {\n            _mergedRoles |= $.getRolesOnGroup[_groups[_i]][_user];\n        }\n\n        return _mergedRoles;\n    }\n\n    /* ========== AUTHORIZATION LOGIC ========== */\n\n    /**\n     * @notice Authorization modifier that automatically loads applicable permission groups\n     * @dev Uses the overridable _loadApplicablePermissionGroups() function to determine groups.\n     *      Passes address(this) as context to enable context-aware group loading.\n     */\n    modifier requiresAuth() {\n        uint16[] memory _groups = _loadApplicablePermissionGroups(msg.sender, msg.sig, address(this));\n        if (!_canCall(msg.sender, msg.sig, _groups)) revert Unauthorized();\n        _;\n    }\n\n    /**\n     * @notice Virtual function to load applicable permission groups for authorization\n     * @dev Can be overridden by inheriting contracts to provide custom group logic based on context\n     * param _callingUser The address attempting to perform the action\n     * param _functionSig The function being called\n     * param _callingContract The contract that initiated the permission check\n     * @return Array of group IDs to check for permissions\n     */\n    function _loadApplicablePermissionGroups(\n        address /* _callingUser */,\n        bytes4 /* _functionSig */,\n        address /* _callingContract */\n    ) internal view virtual returns (uint16[] memory) {\n        return new uint16[](1);  // Default to group 0\n    }\n\n    /**\n     * @notice Checks if a user can call a specific function across multiple groups\n     * @dev Authorization logic using bitwise operations:\n     *      1. Returns true immediately if the function is public\n     *      2. Merges user roles from all specified groups using OR operation\n     *      3. Extracts blacklisted roles using AND with BLACKLIST_MASK (101010...)\n     *      4. Denies access if any blacklisted roles have function permissions\n     *      5. Extracts whitelisted roles using AND with WHITELIST_MASK (010101...)\n     *      6. Grants access if any whitelisted roles have function permissions\n     *      Example: User with roles 000101 (whitelisted role 0, whitelisted role 2) can call function requiring role 0\n     *      Example with mergedRoles 000011: User has both whitelist and blacklist for role 0\n     *        - _onlyBlacklistedRolesMap = 000011 & 101010... = 000010 (blacklisted role 0)\n     *        - _onlyWhitelistedRolesMap = 000011 & 010101... = 000001 (whitelisted role 0)\n     *        - If function requires role 0, access is DENIED due to blacklist taking precedence\n     * @param _user The address of the user to check permissions for\n     * @param _functionSig The 4-byte function signature to check permissions for\n     * @param _groups Array of group IDs to check for permissions\n     * @return Boolean indicating whether the user can call the function\n     */\n    function _canCall(address _user, bytes4 _functionSig, uint16[] memory _groups) internal view returns (bool) {\n        PermissionManagerStorage storage $ = _getPermissionManagerStorage();\n\n        if ($.isPublicFunction[_functionSig]) return true;\n\n        bytes32 _functionRoles = $.getFunctionRoles[_functionSig];\n        if (_functionRoles == bytes32(0)) return false;\n\n        bytes32 _mergedRoles = _getMergedUserRoles(_user, _groups);\n        if (_mergedRoles == bytes32(0)) return false;\n\n        bytes32 _onlyBlacklistedRolesMap = _mergedRoles & BLACKLIST_MASK;\n        if (_onlyBlacklistedRolesMap & _functionRoles != bytes32(0)) return false;\n\n        bytes32 _onlyWhitelistedRolesMap = _mergedRoles & WHITELIST_MASK;\n        return (_onlyWhitelistedRolesMap & _functionRoles) != bytes32(0);\n    }\n\n    /* ========== PUBLIC PERMISSION CONFIGURATION LOGIC ========== */\n\n    /**\n     * @notice Sets a function to be publicly accessible\n     * @dev Initial setup for public permission. Must not be already registered.\n     *      Registers the function and sets it as public.\n     * @param _functionSig The 4-byte function selector to configure\n     */\n    function _setPublicPermission(bytes4 _functionSig) internal {\n        PermissionManagerStorage storage $ = _getPermissionManagerStorage();\n\n        // Check not already registered\n        if ($.isFunctionRegistered[_functionSig]) {\n            revert FunctionAlreadyRegistered(_functionSig);\n        }\n\n        // Register the function\n        $.isFunctionRegistered[_functionSig] = true;\n        emit FunctionRegistered(_functionSig);\n\n        // Set public permission to true\n        $.isPublicFunction[_functionSig] = true;\n\n        emit FunctionPermissionUpdated(_functionSig, true, $.getFunctionRoles[_functionSig]);\n    }\n\n    /**\n     * @notice Updates public permission for an already registered function\n     * @dev Can only update registered functions. Reverts if not registered.\n     *      If disabling public access and function has role permissions, reverts with FunctionHasRolePermissions.\n     *      When enabling public access, automatically clears any existing role permissions.\n     * @param _functionSig The 4-byte function selector to update\n     * @param _enabled Whether the function should be publicly accessible (true) or restricted (false)\n     */\n    function _updatePublicPermission(bytes4 _functionSig, bool _enabled) internal {\n        PermissionManagerStorage storage $ = _getPermissionManagerStorage();\n\n        // Check if registered - revert if not\n        if (!$.isFunctionRegistered[_functionSig]) {\n            revert FunctionNotRegistered(_functionSig);\n        }\n\n        // If disabling public access, ensure no role permissions exist\n        if ($.getFunctionRoles[_functionSig] != bytes32(0) && !_enabled) {\n            revert FunctionHasRolePermissions(_functionSig);\n        }\n\n        // When enabling public access, clear role permissions to avoid conflicts\n        $.getFunctionRoles[_functionSig] = bytes32(0);\n\n        // Update public permission\n        $.isPublicFunction[_functionSig] = _enabled;\n\n        emit FunctionPermissionUpdated(_functionSig, _enabled, $.getFunctionRoles[_functionSig]);\n    }\n\n    /* ========== USER ROLE ASSIGNMENT LOGIC ========== */\n\n    /**\n     * @notice Adds a user to a specific role in a group\n     * @dev Sets the whitelist bit for the role and clears blacklist bit if previously set.\n     *      Uses bitwise OR to set whitelist bit and bitwise AND with NOT to clear blacklist bit.\n     *      Example: For role 0, sets bit 0 and clears bit 1.\n     *      Bit pattern examples (6 bits): 000000 -> 000001 (add role 0), 000010 -> 000001 (clear blacklist, set whitelist)\n     * @param _group The group identifier\n     * @param _user The address of the user to add to role\n     * @param _role The role identifier to add (0-127)\n     */\n    function _addUserRole(uint16 _group, address _user, uint8 _role) internal {\n        if (_role >= MAX_ROLES_PER_GROUP) revert InvalidRoleId(_role);\n\n        PermissionManagerStorage storage $ = _getPermissionManagerStorage();\n        uint256 _whitelistBitPosition = _role * BITS_PER_ROLE + WHITELIST_BIT_OFFSET;\n        uint256 _blacklistBitPosition = _role * BITS_PER_ROLE + BLACKLIST_BIT_OFFSET;\n\n        bytes32 _roles = $.getRolesOnGroup[_group][_user];\n\n        // Set whitelist bit / Clear blacklist bit if set\n        _roles |= bytes32(1 << _whitelistBitPosition);\n        _roles &= ~bytes32(1 << _blacklistBitPosition);\n\n        $.getRolesOnGroup[_group][_user] = _roles;\n\n        emit UserRoleAdded(_user, _group, _role);\n    }\n\n    /**\n     * @notice Blacklists a user for a specific role in a group\n     * @dev Sets the blacklist bit for the role and clears whitelist bit if previously set.\n     *      Uses bitwise OR to set blacklist bit and bitwise AND with NOT to clear whitelist bit.\n     *      Example: For role 0, sets bit 1 and clears bit 0.\n     *      Bit pattern examples (6 bits): 000000 -> 000010 (blacklist role 0), 000001 -> 000010 (clear whitelist, set blacklist)\n     * @param _group The group identifier\n     * @param _user The address of the user to blacklist\n     * @param _role The role identifier to blacklist (0-127)\n     */\n    function _blacklistUserRole(uint16 _group, address _user, uint8 _role) internal {\n        if (_role >= MAX_ROLES_PER_GROUP) revert InvalidRoleId(_role);\n\n        PermissionManagerStorage storage $ = _getPermissionManagerStorage();\n        uint256 _whitelistBitPosition = _role * BITS_PER_ROLE + WHITELIST_BIT_OFFSET;\n        uint256 _blacklistBitPosition = _role * BITS_PER_ROLE + BLACKLIST_BIT_OFFSET;\n\n        bytes32 _roles = $.getRolesOnGroup[_group][_user];\n\n        // Set blacklist bit / Clear whitelist bit if set\n        _roles |= bytes32(1 << _blacklistBitPosition);\n        _roles &= ~bytes32(1 << _whitelistBitPosition);\n\n        $.getRolesOnGroup[_group][_user] = _roles;\n\n        emit UserRoleBlacklisted(_user, _group, _role);\n    }\n\n    /**\n     * @notice Resets a user's role to an uninitialized state (00) in a group\n     * @dev Clears both whitelist and blacklist bits for the role using bitwise AND with NOT.\n     *      This places the role back into an uninitialized state (neither added nor blacklisted).\n     *      Example: For role 0, clears bits 0 and 1.\n     *      Bit pattern examples (6 bits): 000011 -> 000000 (reset role 0), 001100 -> 000000 (reset role 1)\n     * @param _group The group identifier\n     * @param _user The address of the user\n     * @param _role The role identifier to reset (0-127)\n     */\n    function _resetUserRole(uint16 _group, address _user, uint8 _role) internal {\n        if (_role >= MAX_ROLES_PER_GROUP) revert InvalidRoleId(_role);\n\n        PermissionManagerStorage storage $ = _getPermissionManagerStorage();\n        uint256 _whitelistBitPosition = _role * BITS_PER_ROLE + WHITELIST_BIT_OFFSET;\n        uint256 _blacklistBitPosition = _role * BITS_PER_ROLE + BLACKLIST_BIT_OFFSET;\n\n        bytes32 _roles = $.getRolesOnGroup[_group][_user];\n\n        // Clear both bits to return to uninitialized state (00)\n        _roles &= ~bytes32(1 << _whitelistBitPosition);\n        _roles &= ~bytes32(1 << _blacklistBitPosition);\n\n        $.getRolesOnGroup[_group][_user] = _roles;\n\n        emit UserRoleReset(_user, _group, _role);\n    }\n\n    /* ========== ROLE PERMISSION CONFIGURATION LOGIC ========== */\n\n    /**\n     * @notice Sets permissions for multiple roles to call a specific function\n     * @dev Initial setup for role-based permissions. Must not be already registered.\n     *      Registers the function and sets role permissions.\n     * @param _functionSig The 4-byte function selector to configure\n     * @param _roleIds Array of role identifiers (0-127) that can call this function\n     */\n    function _setRolesPermission(\n        bytes4 _functionSig,\n        uint8[] memory _roleIds\n    ) internal {\n        if (_roleIds.length == 0) revert EmptyRolesArray();\n\n        PermissionManagerStorage storage $ = _getPermissionManagerStorage();\n\n        // Check not already registered\n        if ($.isFunctionRegistered[_functionSig]) {\n            revert FunctionAlreadyRegistered(_functionSig);\n        }\n\n        // Register the function\n        $.isFunctionRegistered[_functionSig] = true;\n        emit FunctionRegistered(_functionSig);\n\n        // Build the role permission bitmap for all roles\n        bytes32 _functionRoles;\n        for (uint256 _i = 0; _i < _roleIds.length; _i++) {\n            uint8 _role = _roleIds[_i];\n            if (_role >= MAX_ROLES_PER_GROUP) revert InvalidRoleId(_role);\n\n            _functionRoles = _applyRoleBits(_functionRoles, _role, true);\n        }\n\n        $.getFunctionRoles[_functionSig] = _functionRoles;\n\n        emit FunctionPermissionUpdated(_functionSig, $.isPublicFunction[_functionSig], _functionRoles);\n    }\n\n    /**\n     * @notice Updates permission for a single role on an existing function at runtime\n     * @dev Uses bitwise operations to add or remove role permission from the function role bitmap.\n     *      Can only update registered functions. Reverts if not registered.\n     *      When _enabled is true: Sets both whitelist and blacklist bits for the role.\n     *      When _enabled is false: Clears both whitelist and blacklist bits for the role.\n     * @param _functionSig The 4-byte function selector to update\n     * @param _role The role identifier (0-127) to update permission for\n     * @param _enabled Whether to grant permission (true) or revoke permission (false)\n     */\n    function _updateRolePermission(bytes4 _functionSig, uint8 _role, bool _enabled) internal {\n        if (_role >= MAX_ROLES_PER_GROUP) revert InvalidRoleId(_role);\n\n        PermissionManagerStorage storage $ = _getPermissionManagerStorage();\n\n        // Check if registered - revert if not\n        if (!$.isFunctionRegistered[_functionSig]) {\n            revert FunctionNotRegistered(_functionSig);\n        }\n        if ($.getFunctionRoles[_functionSig] == bytes32(0) && !_enabled) {\n            revert FunctionHasNoPermissions(_functionSig);\n        }\n        $.isPublicFunction[_functionSig] = false;\n\n        bytes32 _currentRoles = $.getFunctionRoles[_functionSig];\n\n        // Apply the role bits using the helper function\n        bytes32 _newRoles = _applyRoleBits(_currentRoles, _role, _enabled);\n\n        $.getFunctionRoles[_functionSig] = _newRoles;\n\n        emit FunctionPermissionUpdated(_functionSig, $.isPublicFunction[_functionSig], _newRoles);\n    }\n\n    /**\n     * @notice Resets permissions for a specific function\n     * @dev Clears both public and role-based permissions for the specified function.\n     *      Also unregisters the function to allow re-initialization.\n     *      WARNING: This does NOT reset user role assignments, only function permissions.\n     * @param _functionSig The 4-byte function selector to reset\n     */\n    function _resetPermissions(bytes4 _functionSig) internal {\n        PermissionManagerStorage storage $ = _getPermissionManagerStorage();\n\n        $.isPublicFunction[_functionSig] = false;\n        $.getFunctionRoles[_functionSig] = bytes32(0);\n        $.isFunctionRegistered[_functionSig] = false;\n\n        emit FunctionPermissionsReset(_functionSig);\n    }\n\n    /* ========== GROUP MANAGEMENT ========== */\n\n    /**\n     * @notice Removes all roles from a user in a specific group\n     * @dev Sets the user's role bitmap to zero for the specified group\n     * @param _group The group identifier\n     * @param _user The address of the user whose roles should be reset\n     */\n    function _resetUserRolesInGroup(uint16 _group, address _user) internal {\n        PermissionManagerStorage storage $ = _getPermissionManagerStorage();\n        $.getRolesOnGroup[_group][_user] = bytes32(0);\n        emit UserRolesReset(_user, _group);\n    }\n\n    /* ======== PRIVATE HELPER FUNCTIONS ======== */\n\n    /**\n     * @notice Private helper to apply or remove a role's permission bits to a function role bitmap\n     * @dev Calculates bit positions and applies OR/AND operations for whitelist and blacklist bits.\n     *      This function encapsulates the common bit manipulation logic used by both\n     *      _setRolesPermission and _updateRolePermission.\n     * @param _currentRoles The current role bitmap for the function\n     * @param _role The role identifier (0-127) - caller must validate this\n     * @param _enabled Whether to add (true) or remove (false) the role bits\n     * @return Updated role bitmap with the role bits added or removed\n     */\n    function _applyRoleBits(bytes32 _currentRoles, uint8 _role, bool _enabled) private pure returns (bytes32) {\n        uint256 _whitelistBitPosition = _role * BITS_PER_ROLE + WHITELIST_BIT_OFFSET;\n        uint256 _blacklistBitPosition = _role * BITS_PER_ROLE + BLACKLIST_BIT_OFFSET;\n\n        if (_enabled) {\n            // Add the role permission bits\n            _currentRoles |= bytes32(1 << _whitelistBitPosition);\n            _currentRoles |= bytes32(1 << _blacklistBitPosition);\n        } else {\n            // Remove the role permission bits\n            _currentRoles &= ~bytes32(1 << _whitelistBitPosition);\n            _currentRoles &= ~bytes32(1 << _blacklistBitPosition);\n        }\n\n        return _currentRoles;\n    }\n\n    /* ======== PRIVATE VIEW/PURE FUNCTIONS ======== */\n\n    /**\n     * @notice Retrieves the PermissionManagerStorage struct from the storage slot\n     * @dev Returns the PermissionManagerStorage struct\n     */\n    function _getPermissionManagerStorage()\n        private\n        pure\n        returns (PermissionManagerStorage storage $)\n    {\n        // Use assembly to simply slot the storage location into a variable\n        // solhint-disable-next-line no-inline-assembly\n        assembly {\n            $.slot := PERMISSION_MANAGER_CORE_STORAGE_SLOT\n        }\n    }\n}\n"},{"file_path":"libs/PermissionManager/PermissionManagerView.sol","source_code":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.28;\n\nimport {PermissionManagerCore} from \"libs/PermissionManager/PermissionManagerCore.sol\";\n\n/**\n * @custom:security-contact blockchain@sygnum.com\n * @title PermissionManagerView\n * @author SYGNUM AG - Blockchain team 3301\n * @notice View-only PermissionManager implementation that exposes only essential view functions\n * @dev This contract provides read-only access to the permission management system without\n *      state-changing functions. It's designed for contracts that need to query permissions\n *      but should not modify them directly.\n */\nabstract contract PermissionManagerView is PermissionManagerCore {\n    /*//////////////////////////////////////////////////////////////\n                        EXTERNAL/PUBLIC VIEW FUNCTIONS\n    //////////////////////////////////////////////////////////////*/\n\n    /**\n     * @notice Returns the role bitmap for a user in a specific group\n     * @param _group The group identifier to check\n     * @param _user The address of the user to check\n     * @return The role bitmap of the user in the group\n     */\n    function getUserRolesInGroup(uint16 _group, address _user) public view returns (bytes32) {\n        return _getUserRolesInGroup(_group, _user);\n    }\n\n    /**\n     * @notice Checks if a user has effective permission for a role across multiple groups (whitelisted and not blacklisted)\n     * @param _groups Array of group identifiers to check\n     * @param _user The address of the user to check\n     * @param _role The role identifier to check (0-127)\n     * @return Boolean indicating whether the user is whitelisted for the role in at least one of\n     *         the groups and not blacklisted for the same role in any of them\n     */\n    function doesUserHaveRole(uint16[] memory _groups, address _user, uint8 _role) public view returns (bool) {\n        return _doesUserHaveRole(_groups, _user, _role);\n    }\n\n    /**\n     * @notice Returns whether a function is public\n     * @param _functionSig The function signature to check\n     * @return Whether the function is public\n     */\n    function isPublicFunction(bytes4 _functionSig) public view returns (bool) {\n        return _isPublicFunction(_functionSig);\n    }\n\n    /**\n     * @notice Checks if a user can call a specific function\n     * @dev Automatically loads applicable permission groups based on context (msg.sender)\n     * @param _user The address of the user to check permissions for\n     * @param _functionSig The 4-byte function signature to check permissions for\n     * @return Boolean indicating whether the user can call the function\n     */\n    function canCall(address _user, bytes4 _functionSig) public view returns (bool) {\n        uint16[] memory _groups = _loadApplicablePermissionGroups(_user, _functionSig, msg.sender);\n        return _canCall(_user, _functionSig, _groups);\n    }\n}\n"},{"file_path":"libs/Timelock/Timelock.sol","source_code":"// SPDX-License-Identifier: UNLICENSED\npragma solidity ^0.8.30;\n\nimport {TimelockView} from \"./TimelockView.sol\";\n\n/**\n * @custom:security-contact blockchain@sygnum.com\n * @title Timelock\n * @author SYGNUM AG - Blockchain team 3301\n * @notice Standard Timelock implementation that extends TimelockView\n * and exposes the basic timelock interface functions.\n * @dev Abstract contract that provides a mechanism to delay the execution of transactions.\n */\nabstract contract Timelock is TimelockView {\n}\n"},{"file_path":"libs/Timelock/TimelockCore.sol","source_code":"// SPDX-License-Identifier: UNLICENSED\npragma solidity ^0.8.30;\n\n/**\n * @custom:security-contact blockchain@sygnum.com\n * @title TimelockCore\n * @author SYGNUM AG - Blockchain team 3301\n * @notice Core timelock implementation with slot storage pattern\n * @dev This abstract contract provides the core timelock functionality with only internal functions.\n * It uses the slot storage pattern for better encapsulation and storage conflict avoidance.\n */\nabstract contract TimelockCore {\n    /* ======== State variables ======== */\n    /** @notice TIMELOCK_CORE_STORAGE_SLOT is a constant used to locate the TimelockStorage struct in storage.\n     * This ensures a unique storage slot, minimizing collision risks with other storage variables in the contract.\n     * It's calculated as keccak256(abi.encode(uint256(keccak256(\"TimelockCore.TimelockStorage\")) - 1)) & ~bytes32(uint256(0xff)).\n     */\n    bytes32 private constant TIMELOCK_CORE_STORAGE_SLOT =\n        0x1626844f871e5080d673ecb96ea8a32c7bdf671ea137282b2886cd9d0fd9a700;\n\n    struct Operation {\n        address target;\n        uint256 value;\n        bytes data;\n        uint256 eta;\n    }\n\n    /// @custom:storage-location erc7201:TimelockCore.TimelockStorage\n    struct TimelockStorage {\n        // @notice operationCounter is incremented for each new operation to ensure unique IDs\n        uint256 operationCounter;\n        // @notice timelock maps the operation ID to the timestamp when it can be executed.\n        mapping(bytes32 operationId => uint256 eta) timelock;\n        mapping(bytes32 operationId => Operation operation) scheduledOperations;\n    }\n\n    /* ======== Custom Errors ======== */\n    error InvalidTimestamp();\n    error TimelockNotReached();\n    error TimelockDoesNotExist();\n    error TimeLockAlreadyExist();\n    error InsufficientFunds(uint256 given, uint256 expected);\n    error OperationExecutionFailed(bytes32 operationId);\n\n    /* ======== Events ======== */\n    /**\n     * @notice Emitted when an operation is scheduled.\n     * @param operationId The unique ID of the operation.\n     * @param target The address of the contract to call.\n     * @param value The amount of Ether (in wei) to send with the call.\n     * @param data The data to send with the call (ABI-encoded function arguments).\n     * @param eta The timestamp (in seconds since Unix epoch) after which the operation can be executed.\n     */\n    event Queued(\n        bytes32 indexed operationId,\n        address target,\n        uint256 value,\n        bytes data,\n        uint256 eta\n    );\n\n    /**\n     * @notice Emitted when an operation is executed.\n     * @param operationId The unique ID of the operation.\n     * @param target The address of the contract that was called.\n     * @param value The amount of Ether (in wei) that was sent with the call.\n     * @param data The data that was sent with the call.\n     * @param timestamp The timestamp (in seconds since Unix epoch) when the operation is executed.\n     */\n    event Executed(\n        bytes32 indexed operationId,\n        address target,\n        uint256 value,\n        bytes data,\n        uint256 timestamp\n    );\n\n    /**\n     * @notice Emitted when an operation is cancelled.\n     * @param operationId The unique ID of the operation.\n     */\n    event Canceled(bytes32 indexed operationId);\n\n    /* ======== Internal View/Pure Functions ======== */\n\n    /**\n     * @notice Returns the details of a scheduled timelock operation.\n     * @dev Retrieves the target address, calldata, value, and ETA for a given operation ID.\n     *      This function allows internal callers to inspect the parameters of a scheduled operation\n     *      before it is executed or cancelled.\n     * @param _operationId The unique ID of the scheduled operation.\n     * @return target The address of the contract to be called.\n     * @return data The calldata to be sent with the call.\n     * @return value The amount of Ether (in wei) to be sent with the call.\n     * @return eta The earliest timestamp at which the operation can be executed.\n     */\n    function _getScheduledOperationDetails(\n        bytes32 _operationId\n    )\n        internal\n        view\n        returns (address target, bytes memory data, uint256 value, uint256 eta)\n    {\n        TimelockStorage storage $ = _getTimelockStorage();\n        Operation memory _op = $.scheduledOperations[_operationId];\n        return (_op.target, _op.data, _op.value, _op.eta);\n    }\n\n    /**\n     * @notice Checks if a timelock operation is valid and ready for execution.\n     * @dev Reverts if the operation does not exist or if the required timelock delay has not yet passed.\n     * @param _operationId The unique ID of the operation to validate.\n     * @custom:reverts TimelockDoesNotExist if the operation is not scheduled.\n     * @custom:reverts TimelockNotReached if the timelock delay has not yet elapsed.\n     */\n    function _isExecutableOperation(bytes32 _operationId) internal view {\n        TimelockStorage storage $ = _getTimelockStorage();\n        uint256 _eta = $.timelock[_operationId];\n        if (_eta == 0) revert TimelockDoesNotExist();\n        if (_getBlockTimestamp() < _eta) revert TimelockNotReached();\n    }\n\n    /* ======== Internal State-Changing Functions ======== */\n    /**\n     * @notice Executes a timelock operation.\n     * @param _operationId The unique ID of the operation to execute.\n     */\n    function _executeScheduledOperation(bytes32 _operationId) internal {\n        _isExecutableOperation(_operationId);\n        TimelockStorage storage $ = _getTimelockStorage();\n\n        Operation memory _op = $.scheduledOperations[_operationId];\n        address _target = _op.target;\n        uint256 _value = _op.value;\n        bytes memory _data = _op.data;\n\n        if (address(this).balance < _value) {\n            revert InsufficientFunds(_value, address(this).balance);\n        }\n\n        // Remove the operation details and timelock details from storage BEFORE the external call\n        // to prevent reentrancy attacks (Checks-Effects-Interactions pattern)\n        delete $.timelock[_operationId];\n        delete $.scheduledOperations[_operationId];\n\n        // Call the target contract and handle the return value\n        // Note: You cannot call internal functions of other contracts using .call, delegatecall, or similar low-level calls.\n        // Internal functions are not part of the contract's ABI and are only accessible within the contract itself or its derived contracts.\n        // The following line can only call public or external functions:\n        (bool _success, ) = _target.call{value: _value}(_data);\n\n        if (!_success) {\n            revert OperationExecutionFailed(_operationId);\n        }\n\n        emit Executed(\n            _operationId,\n            _target,\n            _value,\n            _data,\n            _getBlockTimestamp()\n        );\n    }\n\n    /**\n     * @notice Schedules an operation to be executed after a delay.\n     * @param _target The address of the contract to call.\n     * @param _data The _data to send with the call (ABI-encoded function arguments).\n     * @param _eta The timestamp (in seconds since Unix epoch) after which the operation can be executed.\n     * @return operationId The unique ID of the timelock operation.\n     */\n    function _scheduleOperation(\n        address _target,\n        bytes memory _data,\n        uint256 _eta\n    ) internal returns (bytes32 operationId) {\n        if (_getBlockTimestamp() >= _eta) {\n            revert InvalidTimestamp();\n        }\n        TimelockStorage storage $ = _getTimelockStorage();\n        uint256 _value = msg.value;\n\n        // Generate unique operation ID from counter (starts at 1 to avoid bytes32(0))\n        $.operationCounter++;\n        operationId = bytes32($.operationCounter);\n\n        $.timelock[operationId] = _eta;\n        $.scheduledOperations[operationId] = Operation({\n            target: _target,\n            value: _value,\n            data: _data,\n            eta: _eta\n        });\n\n        // Emit the Queued event with the operation details\n        emit Queued(\n            operationId,\n            _target,\n            _value,\n            _data,\n            _eta\n        );\n    }\n\n    /**\n     * @notice Cancels a timelock operation.\n     * @param _operationId The unique ID of the operation to cancel.\n     */\n    function _cancelScheduledOperation(bytes32 _operationId) internal {\n        TimelockStorage storage $ = _getTimelockStorage();\n        if ($.timelock[_operationId] != 0) {\n            // Remove the scheduled time\n            delete $.timelock[_operationId];\n            // Remove the operation details\n            delete $.scheduledOperations[_operationId];\n\n            emit Canceled(_operationId);\n        }\n    }\n\n    /* ======== Private View/Pure Functions ======== */\n    /**\n     * @notice Retrieves the TimelockStorage struct from the storage slot.\n     * @dev Returns the TimelockStorage struct.\n     */\n    function _getTimelockStorage()\n        private\n        pure\n        returns (TimelockStorage storage $)\n    {\n        // Use assembly to simply slot the storage location into a variable\n        // solhint-disable-next-line no-inline-assembly\n        assembly {\n            $.slot := TIMELOCK_CORE_STORAGE_SLOT\n        }\n    }\n\n    /**\n     * Used to avoid multiple lines of solhint directives in a single line for better readability.\n     * @dev Returns the current block timestamp.\n     */\n    function _getBlockTimestamp() private view returns (uint256) {\n        // solhint-disable-next-line not-rely-on-time\n        return block.timestamp;\n    }\n}\n"},{"file_path":"libs/Timelock/TimelockView.sol","source_code":"// SPDX-License-Identifier: UNLICENSED\npragma solidity ^0.8.30;\n\nimport {TimelockCore} from \"libs/Timelock/TimelockCore.sol\";\n\n/**\n * @custom:security-contact blockchain@sygnum.com\n * @title TimelockView\n * @author SYGNUM AG - Blockchain team 3301\n * @notice View-only Timelock implementation that exposes operation details\n * @dev This contract provides read-only access to timelock operations without\n *      allowing modification. It's designed for contracts that need to query\n *      operation details but should not modify them directly.\n */\nabstract contract TimelockView is TimelockCore {\n    /*//////////////////////////////////////////////////////////////\n                        EXTERNAL/PUBLIC VIEW FUNCTIONS\n    //////////////////////////////////////////////////////////////*/\n\n    /**\n     * @notice Returns the details of a scheduled timelock operation\n     * @param _operationId The unique ID of the scheduled operation\n     * @return target The address of the contract to be called\n     * @return data The calldata to be sent with the call\n     * @return value The amount of Ether (in wei) to be sent with the call\n     * @return eta The earliest timestamp at which the operation can be executed\n     */\n    function getScheduledOperationDetails(\n        bytes32 _operationId\n    ) public view returns (address target, bytes memory data, uint256 value, uint256 eta) {\n        return _getScheduledOperationDetails(_operationId);\n    }\n}\n"},{"file_path":"libs/UUPS/UUPSTimelockUpgradeable.sol","source_code":"// SPDX-License-Identifier: UNLICENSED\npragma solidity ^0.8.30;\n\nimport {UUPSTimelockUpgradeableView} from './UUPSTimelockUpgradeableView.sol';\n\n/**\n * @custom:security-contact blockchain@sygnum.com\n * @title UUPSTimelockUpgradeable\n * @author Sygnum\n * @notice Standard UUPS Timelock implementation that extends UUPSTimelockUpgradeableView\n *         and exposes the public interface functions.\n *         Combines UUPS upgradeability with Timelock functionality for secure, delayed upgrades.\n *         Enables governance mechanisms where upgrades are executed only after a specified delay,\n *         providing a safety window for stakeholders.\n */\nabstract contract UUPSTimelockUpgradeable is UUPSTimelockUpgradeableView {\n\n    /* ======== External/Public State-Changing Functions ======== */\n\n    /**\n     * @notice Upgrades the implementation and optionally executes a function call.\n     * @param _newImplementation The address of the new implementation contract.\n     * @param _data Optional memory to execute after the upgrade.\n     */\n    function upgradeToAndCall(\n        address _newImplementation,\n        bytes memory _data\n    ) public onlyProxy {\n        _upgradeToAndCall(_newImplementation, _data);\n    }\n\n    /**\n     * @notice Executes the scheduled upgrade operation after the timelock delay has elapsed.\n     * @dev Finalizes the upgrade by executing the operation previously scheduled via `queueUpgrade`.\n     *      Checks that the upgrade is authorized and the timelock conditions are met before proceeding.\n     *      Upon successful execution, the upgrade operation is cleared from storage.\n     * @custom:reverts NoUpgradeScheduled if there is no upgrade operation in progress.\n     * @custom:reverts TimelockDoesNotExist if the scheduled operation does not exist.\n     * @custom:reverts TimelockNotReached if the timelock delay has not yet elapsed.\n     */\n    function executeUpgrade() public isUpgrading {\n        _executeUpgrade();\n    }\n\n    /**\n     * @notice Cancels a scheduled upgrade operation.\n     * @dev Allows cancellation of a previously scheduled upgrade operation at any time before it is finalized.\n     *      Removes the scheduled operation from storage, preventing it from being executed in the future.\n     *      If no upgrade is currently scheduled, this function has no effect.\n     * @custom:emits Canceled(bytes32 operationId) when the operation is successfully canceled.\n     */\n    function cancelUpgrade() public isUpgrading {\n        _cancelUpgrade();\n    }\n\n    /**\n     * @notice Executes the upgrade to a new implementation with optional initialization data.\n     * @dev Intended to be called internally by the contract itself as part of the timelock upgrade flow.\n     *      Performs the upgrade to the specified implementation and optionally executes additional setup logic.\n     *      Access control is enforced within the internal _callUpgradeToAndCallUUPS function via the `isInternalCall` modifier.\n     * @param _newImplementation The address of the new implementation contract.\n     * @param _data Optional initialization calldata to execute on the new implementation after the upgrade.\n     */\n    function callUpgradeToAndCallUUPS(\n        address _newImplementation,\n        bytes memory _data\n    ) public {\n        _callUpgradeToAndCallUUPS(_newImplementation, _data);\n    }\n}\n"},{"file_path":"libs/UUPS/UUPSTimelockUpgradeableCore.sol","source_code":"// SPDX-License-Identifier: UNLICENSED\npragma solidity ^0.8.30;\n\nimport {UUPSUpgradeableCore} from './UUPSUpgradeableCore.sol';\nimport {Timelock} from 'libs/Timelock/Timelock.sol';\nimport {BytesLib} from 'libs/utils/bytes/BytesLib.sol';\n\n/**\n * @custom:security-contact blockchain@sygnum.com\n * @title UUPSTimelockUpgradeableCore\n * @author Sygnum\n * @notice Core implementation that combines UUPS upgradeability with Timelock functionality for secure, delayed upgrades.\n *         Contains only internal functions for use by derived contracts.\n *         Enables governance mechanisms where upgrades are executed only after a specified delay,\n *         providing a safety window for stakeholders.\n *         Inherits from both `UUPSUpgradeableCore` for upgradeability and `Timelock` for delayed execution.\n */\nabstract contract UUPSTimelockUpgradeableCore is UUPSUpgradeableCore, Timelock {\n    using BytesLib for bytes;\n\n    /* ======== Custom Errors ======== */\n    error AlreadyUpgrading();\n    error NoUpgradeScheduled();\n    error UnAuthorizedCall();\n    error InvalidDelay();\n    error InvalidUpgradeCalldata();\n\n    event Callers(address indexed caller, address indexed context);\n\n    /* ======== State Variables ======== */\n    /**\n     * @dev Storage slot for tracking the upgrade operation state.\n     *      Prevents re-entrancy during the upgrade process.\n     *      keccak256(abi.encode(uint256(keccak256(\"UUPSTimelockUpgradeableCore.TimelockUpgradeableStorage\")) - 1)) & ~bytes32(uint256(0xff))\n     */\n    bytes32 private constant UUPS_TIMELOCK_UPGRADEABLE_CORE_STORAGE_SLOT =\n        0x2c9356b94ada349e65b0a691cd15e24beddfc13e05a26de25d9c64509c824400;\n\n    /// @custom:storage-location erc7201:UUPSTimelockUpgradeableCore.TimelockUpgradeableStorage\n    struct TimelockUpgradeableStorage {\n        /// @notice The hash of the currently scheduled upgrade operation, or zero if none.\n        bytes32 operationId;\n        /// @notice The delay in seconds before a queued operation can be executed.\n        uint256 delay;\n    }\n\n    /* ======== Modifiers ======== */\n\n    /// @notice Ensures the contract is not currently upgrading.\n    modifier isNotUpgrading() {\n        if (_isUpgrading()) {\n            revert AlreadyUpgrading();\n        }\n        _;\n    }\n\n    /// @notice Ensures the contract is currently upgrading.\n    modifier isUpgrading() {\n        if (!_isUpgrading()) {\n            revert NoUpgradeScheduled();\n        }\n        _;\n    }\n\n    /// @notice Restricts function to internal calls only.\n    modifier isInternalCall() {\n        emit Callers(msg.sender, address(this));\n        if (msg.sender != address(this)) revert UnAuthorizedCall();\n        _;\n    }\n\n    /* ======== Internal Initializer ======== */\n\n    /**\n     * @notice Initializes the UUPSTimelock with a specified delay.\n     * @dev Idempotent: Only initializes if delay is not already set (default value is 0).\n     *      This preserves existing delay during upgrades - new init data will not override existing delay.\n     * @param _delay The delay in seconds before a queued operation can be executed.\n     */\n    function _UUPSTimelock_init(uint256 _delay) internal {\n        TimelockUpgradeableStorage storage $ = _getTimelockUpgradeableStorage();\n        if ($.delay == 0) {\n            _setDelay(_delay);\n        }\n    }\n\n    /* ======== Internal State-Changing Functions ======== */\n\n    /**\n     * @notice Executes the scheduled upgrade operation after the timelock delay has elapsed.\n     * @dev Finalizes the upgrade by executing the operation previously scheduled via `queueUpgrade`.\n     *      Checks that the upgrade is authorized and the timelock conditions are met before proceeding.\n     *      Decodes the scheduled calldata to extract the new implementation address and initialization data,\n     *      then invokes _authorizeUpgrade before executing the operation.\n     *      Upon successful execution, the upgrade operation is cleared from storage.\n     * @custom:reverts NoUpgradeScheduled if there is no upgrade operation in progress.\n     * @custom:reverts TimelockDoesNotExist if the scheduled operation does not exist.\n     * @custom:reverts TimelockNotReached if the timelock delay has not yet elapsed.\n     * @custom:reverts InvalidUpgradeCalldata if the scheduled calldata is invalid.\n     */\n    function _executeUpgrade() internal {\n        TimelockUpgradeableStorage storage $ = _getTimelockUpgradeableStorage();\n        bytes32 _operationId = $.operationId;\n        (, bytes memory _data, , ) = getScheduledOperationDetails(_operationId);\n\n        // Decode the scheduled calldata to extract newImplementation and initData\n        (address _newImplementation, bytes memory _initData) = _decodeUpgradeCalldata(_data);\n\n        // Authorize the upgrade with the correct parameters\n        _authorizeUpgrade(_newImplementation, _initData);\n\n        _executeScheduledOperation(_operationId);\n        delete $.operationId;\n    }\n\n    /**\n     * @notice Cancels a scheduled upgrade operation.\n     * @dev Allows cancellation of a previously scheduled upgrade operation at any time before it is finalized.\n     *      Decodes the scheduled calldata to extract the new implementation address and initialization data,\n     *      then invokes _authorizeUpgrade before canceling the operation.\n     *      Removes the scheduled operation from storage, preventing it from being executed in the future.\n     *      If no upgrade is currently scheduled, this function has no effect.\n     * @custom:emits Canceled(bytes32 operationId) when the operation is successfully canceled.\n     * @custom:reverts InvalidUpgradeCalldata if the scheduled calldata is invalid.\n     */\n    function _cancelUpgrade() internal {\n        TimelockUpgradeableStorage storage $ = _getTimelockUpgradeableStorage();\n        bytes32 _operationId = $.operationId;\n        (, bytes memory _data, , ) = getScheduledOperationDetails(_operationId);\n\n        // Decode the scheduled calldata to extract newImplementation and initData\n        (address _newImplementation, bytes memory _initData) = _decodeUpgradeCalldata(_data);\n\n        // Authorize the upgrade cancellation with the correct parameters\n        _authorizeUpgrade(_newImplementation, _initData);\n\n        _cancelScheduledOperation(_operationId);\n        delete $.operationId;\n    }\n\n    /**\n     * @notice Executes the upgrade to a new implementation with optional initialization data.\n     * @dev Intended to be called internally by the contract itself as part of the timelock upgrade flow.\n     *      Performs the upgrade to the specified implementation and optionally executes additional setup logic.\n     *      Access is restricted to internal calls only via the `isInternalCall` modifier.\n     * @param _newImplementation The address of the new implementation contract.\n     * @param _data Optional initialization calldata to execute on the new implementation after the upgrade.\n     */\n    function _callUpgradeToAndCallUUPS(\n        address _newImplementation,\n        bytes memory _data\n    ) internal isInternalCall {\n        _upgradeToAndCallUUPS(_newImplementation, _data);\n    }\n\n    /**\n     * @notice Schedules the upgrade operation with the timelock.\n     * This is a overrideable function to be overridden by derived contracts to implement custom validations.\n     * @dev Schedules the upgrade operation to be executed after the timelock delay.\n     * @param _newImplementation The address of the new implementation contract.\n     * @param _data The data to be passed to the `callUpgradeToAndCallUUPS` function.\n     */\n    function _triggerUpgrade(\n        address _newImplementation,\n        bytes memory _data\n    ) internal virtual override {\n        _scheduleUpgrade(_newImplementation, _data);\n    }\n\n    /**\n     * @notice Schedules the upgrade operation with the timelock.\n     * @dev Encodes the upgrade operation and schedules it for execution after the delay.\n     * @param _newImplementation The address of the new implementation contract.\n     * @param _data The data to be passed to the `callUpgradeToAndCallUUPS` function.\n     */\n    function _scheduleUpgrade(\n        address _newImplementation,\n        bytes memory _data\n    ) internal isNotUpgrading {\n        TimelockUpgradeableStorage storage $ = _getTimelockUpgradeableStorage();\n        // Add the delay to the current block timestamp to get the scheduled execution time ahead of the current block.\n        // solhint-disable-next-line not-rely-on-time\n        uint256 _executionTimestamp = block.timestamp + $.delay;\n\n        bytes32 _operationId = _scheduleOperation(\n            address(this),\n            abi.encodeWithSignature(\n                \"callUpgradeToAndCallUUPS(address,bytes)\",\n                _newImplementation,\n                _data\n            ),\n            _executionTimestamp\n        );\n\n        $.operationId = _operationId;\n    }\n\n    /* ======== Private State-Changing Functions ======== */\n\n    /**\n     * @notice Sets the delay for the timelock.\n     * @dev Sets the delay in seconds before a queued operation can be executed.\n     * @param _delay The delay in seconds.\n     */\n    function _setDelay(uint256 _delay) private {\n        if (_delay == 0) revert InvalidDelay();\n        TimelockUpgradeableStorage storage $ = _getTimelockUpgradeableStorage();\n        $.delay = _delay;\n    }\n\n    /* ======== Private View/Pure Functions ======== */\n\n    /**\n     * @notice Decodes and validates scheduled upgrade calldata.\n     * @dev Extracts the new implementation address and initialization data from the scheduled operation calldata.\n     *      Validates that the function selector matches `callUpgradeToAndCallUUPS(address,bytes)`.\n     * @param _data The encoded calldata from the scheduled operation.\n     * @return _newImplementation The address of the new implementation contract.\n     * @return _initData The initialization calldata to be executed on the new implementation.\n     * @custom:reverts InvalidUpgradeCalldata if the function selector does not match or decoding fails.\n     */\n    function _decodeUpgradeCalldata(\n        bytes memory _data\n    ) private pure returns (address _newImplementation, bytes memory _initData) {\n        // Validate minimum length for callUpgradeToAndCallUUPS(address,bytes):\n        // - 4 bytes:  function selector\n        // - 32 bytes: address parameter (newImplementation)\n        // - 32 bytes: offset to dynamic bytes data (required by ABI encoding, points to byte 64)\n        // - 32 bytes: length of bytes array (can be 0 for empty initData)\n        // Total: 100 bytes minimum (data itself can be empty)\n        if (_data.length < 100) {\n            revert InvalidUpgradeCalldata();\n        }\n\n        // Decode the parameters\n        // Skip the first 4 bytes (selector) and decode (address, bytes)\n        (_newImplementation, _initData) = abi.decode(\n            _data.slice(4, _data.length),\n            (address, bytes)\n        );\n    }\n\n    /**\n     * @notice Checks if the contract is currently in the upgrading state.\n     * @return value `true` if the contract is upgrading, `false` otherwise.\n     */\n    function _isUpgrading() private view returns (bool) {\n        TimelockUpgradeableStorage storage $ = _getTimelockUpgradeableStorage();\n        return $.operationId != bytes32(0);\n    }\n\n    /**\n     * @dev Returns the TimelockUpgradeableStorage struct.\n     */\n    function _getTimelockUpgradeableStorage()\n        private\n        pure\n        returns (TimelockUpgradeableStorage storage $)\n    {\n        // Use assembly to simply slot the storage location into a variable\n        // solhint-disable-next-line no-inline-assembly\n        assembly {\n            $.slot := UUPS_TIMELOCK_UPGRADEABLE_CORE_STORAGE_SLOT\n        }\n    }\n}\n"},{"file_path":"libs/UUPS/UUPSTimelockUpgradeableView.sol","source_code":"// SPDX-License-Identifier: UNLICENSED\npragma solidity ^0.8.30;\n\nimport {UUPSTimelockUpgradeableCore} from './UUPSTimelockUpgradeableCore.sol';\nimport {UUPSUtils} from './UUPSUtils.sol';\nimport {IUUPSAsynchronousUpgradeable} from './interface/IUUPSAsynchronousUpgradeable.sol';\n\n/**\n * @custom:security-contact blockchain@sygnum.com\n * @title UUPSTimelockUpgradeableView\n * @author SYGNUM AG - Blockchain team 3301\n * @notice View-only UUPS Timelock upgrade implementation that exposes view functions\n * @dev This contract provides read-only access to UUPS Timelock upgrade state without\n *      allowing upgrade modifications. It's designed for contracts that need to query\n *      upgrade and timelock information but should not perform upgrades directly.\n *      Note: This implementation is independent of UUPSUpgradeableView due to Core\n *      layer inheritance differences (UUPSTimelockUpgradeableCore inherits from both\n *      UUPSUpgradeableCore and Timelock).\n */\nabstract contract UUPSTimelockUpgradeableView is UUPSTimelockUpgradeableCore, IUUPSAsynchronousUpgradeable {\n    /*//////////////////////////////////////////////////////////////\n                        EXTERNAL/PUBLIC VIEW FUNCTIONS\n    //////////////////////////////////////////////////////////////*/\n\n    /**\n     * @notice Returns the storage slot used by the implementation\n     * @dev Reverts if called through a proxy to prevent bricking\n     * @return The storage slot used by the implementation\n     */\n    function proxiableUUID() external view notDelegated returns (bytes32) {\n        return _proxiableUUID();\n    }\n\n    /**\n     * @notice Returns the current implementation address\n     * @dev Returns the address stored in the ERC-1967 implementation slot\n     * @return The address of the current implementation contract\n     */\n    function getImplementation() external view returns (address) {\n        return UUPSUtils.getImplementation();\n    }\n}\n"},{"file_path":"libs/UUPS/UUPSUpgradeableCore.sol","source_code":"// SPDX-License-Identifier: UNLICENSED\npragma solidity ^0.8.30;\n\nimport {IERC1822Proxiable} from './interface/IERC1822.sol';\nimport {UUPSUtils} from './UUPSUtils.sol';\n\n/**\n * @custom:security-contact blockchain@sygnum.com\n * @title UUPSUpgradeableCore\n * @author SYGNUM AG - Blockchain team 3301\n * @notice Core UUPS upgradeability implementation with only internal functions\n * @dev Provides upgradeability logic for UUPS proxies, including initialization and upgrade authorization.\n * Combines initialization and UUPS upgrade logic for use behind an ERC1967Proxy.\n * UUPS (Universal Upgradeable Proxy Standard) is a proxy pattern where the upgrade logic resides in the implementation contract itself,\n * rather than in the proxy. This contract provides the base functionality for UUPS upgradeability, including checks to ensure\n * that upgrades are authorized and that the implementation contract is valid.\n */\nabstract contract UUPSUpgradeableCore is IERC1822Proxiable {\n    /* ======== State Variables ======== */\n    /**\n     * @dev `__self` is an immutable state variable that stores the address of the contract instance.\n     * It is used to differentiate between direct calls to the implementation contract and calls through the proxy.\n     * This is crucial for security checks to prevent unauthorized access to upgrade functions.\n     * By storing the contract's own address, we can determine if a call is being made directly to the implementation\n     * or through the proxy, which is essential for enforcing access control and preventing unauthorized upgrades.\n     */\n    address private immutable __self = address(this);\n\n    /* ======== Modifiers ======== */\n    /**\n     * @dev Restricts function to only be called through a proxy.\n     * @notice This modifier checks that the function is being called via a delegate call through a proxy contract.\n     * It prevents direct calls to the implementation contract, ensuring that all calls go through the proxy.\n     */\n    modifier onlyProxy() {\n        _checkProxy();\n        _;\n    }\n\n    /**\n     * @dev Restricts function to only be called directly, not via delegatecall.\n     * @notice This modifier ensures that the function is called directly on the implementation contract,\n     * not through a delegate call. This is used to protect functions that should only be called by the implementation itself.\n     */\n    modifier notDelegated() {\n        _checkNotDelegated();\n        _;\n    }\n\n    /* ======== Custom Errors ======== */\n    error UUPSUnauthorizedCallContext();\n\n    /* ======== Internal View/Pure Functions ======== */\n\n    /**\n     * @notice Returns the storage slot used by the implementation.\n     * @dev Internal version for core functionality.\n     * @return bytes32 The storage slot used by the implementation.\n     */\n    function _proxiableUUID() internal view returns (bytes32) {\n        _checkNotDelegated();\n        return UUPSUtils.IMPLEMENTATION_SLOT;\n    }\n\n    /**\n     * @dev Ensures the function is called through a proxy with the correct implementation.\n     * @notice This function checks that the contract is being called via a delegate call through a proxy contract\n     * and that the implementation address matches the current contract address.\n     * It reverts with `UUPSUnauthorizedCallContext` if the conditions are not met.\n     */\n    function _checkProxy() internal view {\n        // If the address of `this` is equal to `__self`, it means the contract is being called directly, not through a proxy.\n        // Additionally, it verifies that the implementation address stored in the UUPSUtils matches the current contract address.\n        if (\n            address(this) == __self || UUPSUtils.getImplementation() != __self\n        ) {\n            revert UUPSUnauthorizedCallContext();\n        }\n    }\n\n    /**\n     * @dev Ensures the function is not called through delegatecall.\n     * @notice This function checks that the contract is being called directly and not through a delegate call.\n     * It reverts with `UUPSUnauthorizedCallContext` if the contract is being called through a delegate call.\n     */\n    function _checkNotDelegated() internal view {\n        // If the address of `this` is not equal to `__self`, it means the contract is being called through a delegate call.\n        if (address(this) != __self) {\n            revert UUPSUnauthorizedCallContext();\n        }\n    }\n\n    /* ======== Internal State-Changing Functions ======== */\n\n    /**\n     * @notice Upgrades the implementation and optionally executes a function call.\n     * @param _newImplementation The address of the new implementation contract.\n     * @param _data Optional memory to execute after the upgrade.\n     */\n    function _upgradeToAndCall(\n        address _newImplementation,\n        bytes memory _data\n    ) internal {\n        _checkProxy();\n        _authorizeUpgrade(_newImplementation, _data);\n        _triggerUpgrade(_newImplementation, _data);\n    }\n\n    /**\n     * @notice Must be overridden to include access restriction to the upgrade mechanism.\n     * validates the _newImplementation address and _data to perform any necessary checks on the input data\n     * @param _newImplementation The address of the new implementation contract.\n     * @param _data Optional memory to execute after the upgrade.\n     */\n    function _authorizeUpgrade(address _newImplementation,bytes memory _data) internal view virtual;\n\n    /**\n     * @dev Executes the upgrade.\n     * @notice This function is responsible for performing the actual upgrade of the contract.\n     * It calls the `_upgradeToAndCallUUPS` function to switch to the new implementation.\n     * @param _newImplementation The address of the new implementation contract.\n     * @param _data The _data to be passed to the new implementation contract.\n     */\n    function _triggerUpgrade(\n        address _newImplementation,\n        bytes memory _data\n    ) internal virtual {\n        _upgradeToAndCallUUPS(_newImplementation, _data);\n    }\n\n    /**\n     * @dev Performs an implementation upgrade with a security check for UUPS proxies and optional setup call.\n     * @param _newImplementation The address of the new implementation contract.\n     * @param _data Optional memory to execute after the upgrade.\n     */\n    function _upgradeToAndCallUUPS(\n        address _newImplementation,\n        bytes memory _data\n    ) internal {\n        _preUpgradeToAndCallUUPS(_newImplementation, _data);\n        UUPSUtils.upgradeToAndCall(_newImplementation, _data);\n        _postUpgradeToAndCallUUPS(_newImplementation, _data);\n    }\n\n    /**\n     * @notice Hook that is called before an upgrade is executed.\n     * @dev Can be overridden by derived contracts to implement custom logic before the upgrade is executed.\n     *      If the contract inherits from Initializable. Override this method to calls _resetInitialized().\n     * @param _newImplementation The address of the new implementation contract.\n     * @param _data The data that was passed during the upgrade.\n     */\n    function _preUpgradeToAndCallUUPS(address _newImplementation, bytes memory _data) internal virtual {}\n\n    /**\n     * @notice Hook that is called after an upgrade is executed.\n     * @dev Can be overridden by derived contracts to implement custom logic after the upgrade is executed.\n     *      For example, to check that the contract is properly initialized after the upgrade.\n     *      If the contract inherits from Initializable, this can be used to verify that isInitialized() returns true\n     *      when initialization data is required.\n     * @param _newImplementation The address of the new implementation contract.\n     * @param _data The data that was passed during the upgrade.\n     */\n    function _postUpgradeToAndCallUUPS(address _newImplementation, bytes memory _data) internal virtual {}\n}\n"},{"file_path":"libs/UUPS/UUPSUtils.sol","source_code":"// SPDX-License-Identifier: UNLICENSED\npragma solidity ^0.8.30;\n\nimport {IERC1967} from './interface/IERC1967.sol';\nimport {IERC1822Proxiable} from './interface/IERC1822.sol';\n\n/**\n * @custom:security-contact blockchain@sygnum.com\n * @title UUPSUtils\n * @notice Provides utility functions for UUPS proxies, including implementation retrieval, upgrade execution, and safety checks.\n * @dev This library offers a collection of functions designed to simplify the implementation of UUPS (Universal Upgradeable Proxy Standard) proxies.\n */\nlibrary UUPSUtils {\n    /* ======== Custom Errors ======== */\n    error ERC1967InvalidImplementation(address implementation);\n    error ERC1967NonPayable();\n    error FailedCall();\n\n    /* ======== Constants ======== */\n    /** @dev Storage slot for the implementation address (EIP-1967 standard).   \n     * bytes32(uint256(keccak256('eip1967.proxy.implementation')) - 1)\n     */\n    bytes32 internal constant IMPLEMENTATION_SLOT =\n        0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;\n\n    /** @dev The address of the current implementation. */\n    function getImplementation()\n        internal\n        view\n        returns (address implementation)\n    {\n        // Load the implementation address from the ERC-1967 slot.\n        // solhint-disable-next-line no-inline-assembly\n        assembly ('memory-safe') {\n            implementation := sload(IMPLEMENTATION_SLOT)\n        }\n    }\n\n    /**\n     * @notice Upgrades the implementation and optionally performs a setup call.\n     * @param _newImplementation The address of the new implementation contract.\n     * @param _data Optional initialization calldata to delegatecall to the new implementation.\n     */\n    function upgradeToAndCall(\n        address _newImplementation,\n        bytes memory _data\n    ) internal {\n        _setImplementation(_newImplementation);\n        emit IERC1967.Upgraded(_newImplementation);\n\n        if (_data.length > 0) {\n            _safeFunctionDelegateCall(_newImplementation, _data);\n        } else {\n            _checkNonPayable();\n        }\n    }\n\n    /** @notice Reverts if msg.value is not zero. */\n    function _checkNonPayable() private {\n        if (msg.value > 0) {\n            revert ERC1967NonPayable();\n        }\n    }\n\n    /**\n     * @notice Stores a new address in the ERC-1967 implementation slot.\n     * @param _newImplementation The address to store as the new implementation.\n     */\n    function _setImplementation(address _newImplementation) private {\n        _checkValidImplementation(_newImplementation);\n\n        // solhint-disable-next-line no-inline-assembly\n        assembly ('memory-safe') {\n            sstore(IMPLEMENTATION_SLOT, _newImplementation)\n        }\n    }\n\n    /**\n     * @dev Ensures the function is not called through delegatecall.\n     * @notice This function checks that the provided address is a valid UUPS implementation contract by verifying its proxiableUUID.\n     * It reverts with `UUPSUtils.ERC1967InvalidImplementation` if the provided address is not a valid implementation.\n     * @param _newImplementation The address of the new implementation contract.\n     */\n    function _checkValidImplementation(address _newImplementation) private view {\n        // Implementation address must contain code (this also covers address(0))\n        if (_newImplementation.code.length == 0) {\n            revert ERC1967InvalidImplementation(_newImplementation);\n        }\n        // Check that the new implementation exposes the proxiableUUID function and that it returns the expected value.\n        try IERC1822Proxiable(_newImplementation).proxiableUUID() returns (\n            bytes32 slot\n        ) {\n            if (slot != IMPLEMENTATION_SLOT) {\n                revert ERC1967InvalidImplementation(_newImplementation);\n            }\n        } catch {\n            revert ERC1967InvalidImplementation(_newImplementation);\n        }\n    }\n\n    /**\n     * @notice Performs a delegatecall to the target address with the given data and verifies the result.\n     * @param _target The address to delegatecall to.\n     * @param _data The data to send with the delegatecall.\n     * @return _returndata The data returned by the delegatecall.\n     * @dev Bubbles up revert reasons or custom errors if the call fails.\n     */\n    function _safeFunctionDelegateCall(\n        address _target,\n        bytes memory _data\n    ) private returns (bytes memory) {\n        // low level call is validate to prevent low-level call reentrancy or other low-level call errors\n        // solhint-disable-next-line avoid-low-level-calls\n        (bool _success, bytes memory _returndata) = _target.delegatecall(_data);\n\n        if (!_success) {\n            if (_returndata.length > 0) {\n                // solhint-disable-next-line no-inline-assembly\n                assembly ('memory-safe') {\n                    let returndata_size := mload(_returndata)\n                    revert(add(32, _returndata), returndata_size)\n                }\n            }\n            revert FailedCall();\n        }\n\n        return _returndata;\n    }\n}\n"},{"file_path":"libs/UUPS/interface/IERC1822.sol","source_code":"// SPDX-License-Identifier: UNLICENSED\npragma solidity ^0.8.30;\n\n/**\n * @custom:security-contact blockchain@sygnum.com\n * @dev ERC-1822: Universal Upgradeable Proxy Standard (UUPS) documents a method for upgradeability through a simplified\n * proxy whose upgrades are fully controlled by the current implementation.\n */\ninterface IERC1822Proxiable {\n    /**\n     * @dev Returns the storage slot that the proxiable contract assumes is being used to store the implementation\n     * address.\n     *\n     * IMPORTANT: A proxy pointing at a proxiable contract should not be considered proxiable itself, because this risks\n     * bricking a proxy that upgrades to it, by delegating to itself until out of gas. Thus it is critical that this\n     * function revert if invoked through a proxy.\n     */\n    function proxiableUUID() external view returns (bytes32);\n}\n"},{"file_path":"libs/UUPS/interface/IERC1967.sol","source_code":"// SPDX-License-Identifier: UNLICENSED\npragma solidity ^0.8.30;\n\n/**\n * @custom:security-contact blockchain@sygnum.com\n * @title IERC1967\n * @dev Interface of ERC1967 events emitted by proxies.\n *\n * These events are emitted when the proxy's implementation, admin, or beacon are changed.\n */\ninterface IERC1967 {\n    /**\n     * @dev Emitted when the implementation is upgraded.\n     * @param implementation The address of the new implementation.\n     */\n    event Upgraded(address indexed implementation);\n}\n"},{"file_path":"libs/UUPS/interface/IUUPSAsynchronousUpgradeable.sol","source_code":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.30;\n\nimport \"./IUUPSSynchronousUpgradeable.sol\";\n\n/**\n * @custom:security-contact blockchain@sygnum.com\n * @title IUUPSAsynchronousUpgradeable\n * @author SYGNUM AG - Blockchain team 3301\n * @notice Interface for asynchronous UUPS upgrades with timelock delay\n * @dev Contracts implementing this interface perform upgrades asynchronously,\n *      meaning the upgrade is scheduled when upgradeToAndCall is called and must be\n *      executed later via executeUpgrade after the timelock delay has elapsed.\n *      This provides a safety window for stakeholders to review pending upgrades.\n */\ninterface IUUPSAsynchronousUpgradeable is IUUPSSynchronousUpgradeable {\n    /**\n     * @notice Executes a previously scheduled upgrade after the timelock delay\n     * @dev Can only be called after the timelock delay has elapsed since scheduling\n     */\n    function executeUpgrade() external;\n\n    /**\n     * @notice Cancels a scheduled upgrade operation\n     * @dev Removes the scheduled upgrade before it is executed\n     */\n    function cancelUpgrade() external;\n}\n"},{"file_path":"libs/UUPS/interface/IUUPSSynchronousUpgradeable.sol","source_code":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.30;\n\n/**\n * @custom:security-contact blockchain@sygnum.com\n * @title IUUPSSynchronousUpgradeable\n * @author SYGNUM AG - Blockchain team 3301\n * @notice Interface for synchronous UUPS upgrades that complete immediately\n * @dev Contracts implementing this interface perform upgrades synchronously,\n *      meaning the implementation is changed immediately when upgradeToAndCall is called.\n *      This is the classic UUPS upgrade pattern without any delay mechanism.\n */\ninterface IUUPSSynchronousUpgradeable {\n    /**\n     * @notice Upgrades the implementation and optionally executes a function call\n     * @dev The upgrade happens immediately when this function is called\n     * @param _newImplementation The address of the new implementation contract\n     * @param _data Optional calldata to execute after the upgrade\n     */\n    function upgradeToAndCall(address _newImplementation, bytes memory _data) external;\n\n    /**\n     * @notice Returns the current implementation address\n     * @dev Returns the address stored in the ERC-1967 implementation slot\n     * @return The address of the current implementation contract\n     */\n    function getImplementation() external view returns (address);\n}\n"},{"file_path":"libs/UUPS/utils/UUPSUpgradeableChecker.sol","source_code":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.30;\n\n/**\n * @custom:security-contact blockchain@sygnum.com\n * @title UUPSUpgradeableChecker\n * @author SYGNUM AG - Blockchain team 3301\n * @notice Utility library to check if a contract address implements the UUPS Upgradeable interface\n * @dev This library checks for the presence of UUPS-specific functions that indicate\n *      a contract is UUPS upgradeable. Only view functions are checked to avoid issues\n *      with access control modifiers on upgrade functions.\n */\nlibrary UUPSUpgradeableChecker {\n    /**\n     * @notice Checks if a contract address implements the UUPS Upgradeable interface\n     * @dev Verifies the presence of UUPS-specific functions through staticcall.\n     * @param _contractAddress The address to check\n     * @return isUUPSUpgradeable True if the address implements the UUPS Upgradeable interface, false otherwise\n     */\n    function isUUPSUpgradeable(\n        address _contractAddress\n    ) internal view returns (bool) {\n        if (_contractAddress == address(0)) {\n            return false;\n        }\n\n        if (_contractAddress.code.length == 0) {\n            return false;\n        }\n\n        return _checkUUPSUpgradeableFunctions(_contractAddress);\n    }\n\n    /**\n     * @notice Checks if a contract implements the UUPS Upgradeable interface through staticcall verification\n     * @dev Verifies the presence of proxiableUUID() view function which is required for UUPS\n     * @param _contractAddress The address to check\n     * @return hasUUPSUpgradeableFunctions True if UUPS Upgradeable functions are present, false otherwise\n     */\n    function _checkUUPSUpgradeableFunctions(\n        address _contractAddress\n    ) private view returns (bool hasUUPSUpgradeableFunctions) {\n        // Check proxiableUUID() - this is the key function that identifies UUPS upgradeable contracts\n        (bool _okProxiableUUID, bytes memory _proxiableUUIDData) = _contractAddress\n            .staticcall(abi.encodeWithSignature(\"proxiableUUID()\"));\n        if (!_okProxiableUUID || _proxiableUUIDData.length < 32) return false;\n\n        return true;\n    }\n}"},{"file_path":"libs/utils/array/Uint8ArrayUtils.sol","source_code":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.28;\n\n/**\n * @custom:security-contact blockchain@sygnum.com\n * @title Uint8ArrayUtils\n * @author SYGNUM AG - Blockchain team 3301\n * @notice Utility functions for creating uint8 arrays\n * @dev Provides helper functions to simplify uint8 array creation in Solidity contracts.\n *      These overloaded functions eliminate the need for manual array creation in application code.\n */\nabstract contract Uint8ArrayUtils {\n    /* ======== HELPER FUNCTIONS ======== */\n\n    /**\n     * @notice Helper function to create a uint8 array from 1 value\n     * @param _value1 First value (required)\n     * @return Array containing single value\n     */\n    function _uint8Array(uint8 _value1) internal pure returns (uint8[] memory) {\n        uint8[] memory _values = new uint8[](1);\n        _values[0] = _value1;\n        return _values;\n    }\n\n    /**\n     * @notice Helper function to create a uint8 array from 2 values\n     */\n    function _uint8Array(uint8 _value1, uint8 _value2) internal pure returns (uint8[] memory) {\n        uint8[] memory _values = new uint8[](2);\n        _values[0] = _value1;\n        _values[1] = _value2;\n        return _values;\n    }\n\n    /**\n     * @notice Helper function to create a uint8 array from 3 values\n     */\n    function _uint8Array(uint8 _value1, uint8 _value2, uint8 _value3) internal pure returns (uint8[] memory) {\n        uint8[] memory _values = new uint8[](3);\n        _values[0] = _value1;\n        _values[1] = _value2;\n        _values[2] = _value3;\n        return _values;\n    }\n\n    /**\n     * @notice Helper function to create a uint8 array from 4 values\n     */\n    function _uint8Array(uint8 _value1, uint8 _value2, uint8 _value3, uint8 _value4) internal pure returns (uint8[] memory) {\n        uint8[] memory _values = new uint8[](4);\n        _values[0] = _value1;\n        _values[1] = _value2;\n        _values[2] = _value3;\n        _values[3] = _value4;\n        return _values;\n    }\n\n    /**\n     * @notice Helper function to create a uint8 array from 5 values\n     */\n    function _uint8Array(uint8 _value1, uint8 _value2, uint8 _value3, uint8 _value4, uint8 _value5) internal pure returns (uint8[] memory) {\n        uint8[] memory _values = new uint8[](5);\n        _values[0] = _value1;\n        _values[1] = _value2;\n        _values[2] = _value3;\n        _values[3] = _value4;\n        _values[4] = _value5;\n        return _values;\n    }\n\n    /**\n     * @notice Helper function to create a uint8 array from 6 values\n     */\n    function _uint8Array(uint8 _value1, uint8 _value2, uint8 _value3, uint8 _value4, uint8 _value5, uint8 _value6) internal pure returns (uint8[] memory) {\n        uint8[] memory _values = new uint8[](6);\n        _values[0] = _value1;\n        _values[1] = _value2;\n        _values[2] = _value3;\n        _values[3] = _value4;\n        _values[4] = _value5;\n        _values[5] = _value6;\n        return _values;\n    }\n\n    /**\n     * @notice Helper function to create a uint8 array from 7 values\n     */\n    function _uint8Array(uint8 _value1, uint8 _value2, uint8 _value3, uint8 _value4, uint8 _value5, uint8 _value6, uint8 _value7) internal pure returns (uint8[] memory) {\n        uint8[] memory _values = new uint8[](7);\n        _values[0] = _value1;\n        _values[1] = _value2;\n        _values[2] = _value3;\n        _values[3] = _value4;\n        _values[4] = _value5;\n        _values[5] = _value6;\n        _values[6] = _value7;\n        return _values;\n    }\n\n    /**\n     * @notice Helper function to create a uint8 array from 8 values\n     */\n    function _uint8Array(uint8 _value1, uint8 _value2, uint8 _value3, uint8 _value4, uint8 _value5, uint8 _value6, uint8 _value7, uint8 _value8) internal pure returns (uint8[] memory) {\n        uint8[] memory _values = new uint8[](8);\n        _values[0] = _value1;\n        _values[1] = _value2;\n        _values[2] = _value3;\n        _values[3] = _value4;\n        _values[4] = _value5;\n        _values[5] = _value6;\n        _values[6] = _value7;\n        _values[7] = _value8;\n        return _values;\n    }\n\n    /**\n     * @notice Helper function to create a uint8 array from 9 values\n     */\n    function _uint8Array(uint8 _value1, uint8 _value2, uint8 _value3, uint8 _value4, uint8 _value5, uint8 _value6, uint8 _value7, uint8 _value8, uint8 _value9) internal pure returns (uint8[] memory) {\n        uint8[] memory _values = new uint8[](9);\n        _values[0] = _value1;\n        _values[1] = _value2;\n        _values[2] = _value3;\n        _values[3] = _value4;\n        _values[4] = _value5;\n        _values[5] = _value6;\n        _values[6] = _value7;\n        _values[7] = _value8;\n        _values[8] = _value9;\n        return _values;\n    }\n\n    /**\n     * @notice Helper function to create a uint8 array from 10 values\n     */\n    function _uint8Array(uint8 _value1, uint8 _value2, uint8 _value3, uint8 _value4, uint8 _value5, uint8 _value6, uint8 _value7, uint8 _value8, uint8 _value9, uint8 _value10) internal pure returns (uint8[] memory) {\n        uint8[] memory _values = new uint8[](10);\n        _values[0] = _value1;\n        _values[1] = _value2;\n        _values[2] = _value3;\n        _values[3] = _value4;\n        _values[4] = _value5;\n        _values[5] = _value6;\n        _values[6] = _value7;\n        _values[7] = _value8;\n        _values[8] = _value9;\n        _values[9] = _value10;\n        return _values;\n    }\n}\n"},{"file_path":"libs/utils/bytes/BytesLib.sol","source_code":"// SPDX-License-Identifier: UNLICENSED\npragma solidity ^0.8.30;\n\n/**\n * @custom:security-contact blockchain@sygnum.com\n * @title BytesLib\n * @author Sygnum\n * @notice Library for efficient bytes manipulation operations.\n * @dev Provides utility functions for working with bytes arrays.\n */\nlibrary BytesLib {\n    /**\n     * @notice Slices a bytes array from start to end position.\n     * @dev Sanitizes input by clamping end to buffer length and start to end.\n     *      Uses mcopy for efficient memory copying.\n     * @param _data The bytes array to slice.\n     * @param _start The starting position (inclusive).\n     * @param _end The ending position (exclusive).\n     * @return _result The sliced bytes array.\n     */\n    function slice(\n        bytes memory _data,\n        uint256 _start,\n        uint256 _end\n    ) internal pure returns (bytes memory _result) {\n        // Sanitize inputs\n        _end = _min(_end, _data.length);\n        _start = _min(_start, _end);\n\n        // Allocate and copy\n        _result = new bytes(_end - _start);\n        // solhint-disable-next-line no-inline-assembly\n        assembly (\"memory-safe\") {\n            mcopy(add(_result, 0x20), add(add(_data, 0x20), _start), sub(_end, _start))\n        }\n    }\n\n    /**\n     * @notice Returns the minimum of two numbers.\n     * @dev Internal helper function for sanitizing slice bounds.\n     * @param _a First number.\n     * @param _b Second number.\n     * @return The smaller of the two numbers.\n     */\n    function _min(uint256 _a, uint256 _b) private pure returns (uint256) {\n        return _a < _b ? _a : _b;\n    }\n}\n"}],"certified":false,"conflicting_implementations":null,"abi":[{"inputs":[],"name":"AddressAlreadyInGroup","type":"error"},{"inputs":[{"internalType":"address","name":"_implementation","type":"address"}],"name":"AlreadyUpgraded","type":"error"},{"inputs":[],"name":"AlreadyUpgrading","type":"error"},{"inputs":[],"name":"ContractNotInitializedAfterUpgrade","type":"error"},{"inputs":[{"internalType":"address","name":"_token","type":"address"}],"name":"CustomGroupIdAlreadyEnabled","type":"error"},{"inputs":[{"internalType":"address","name":"implementation","type":"address"}],"name":"ERC1967InvalidImplementation","type":"error"},{"inputs":[],"name":"ERC1967NonPayable","type":"error"},{"inputs":[],"name":"EmptyRolesArray","type":"error"},{"inputs":[],"name":"FailedCall","type":"error"},{"inputs":[{"internalType":"bytes4","name":"functionSig","type":"bytes4"}],"name":"FunctionAlreadyRegistered","type":"error"},{"inputs":[{"internalType":"bytes4","name":"functionSig","type":"bytes4"}],"name":"FunctionHasNoPermissions","type":"error"},{"inputs":[{"internalType":"bytes4","name":"functionSig","type":"bytes4"}],"name":"FunctionHasRolePermissions","type":"error"},{"inputs":[{"internalType":"bytes4","name":"functionSig","type":"bytes4"}],"name":"FunctionNotRegistered","type":"error"},{"inputs":[],"name":"GroupMismatch","type":"error"},{"inputs":[],"name":"GroupZeroReservedForFactory","type":"error"},{"inputs":[{"internalType":"uint256","name":"given","type":"uint256"},{"internalType":"uint256","name":"expected","type":"uint256"}],"name":"InsufficientFunds","type":"error"},{"inputs":[],"name":"InvalidDelay","type":"error"},{"inputs":[{"internalType":"address","name":"implementation","type":"address"}],"name":"InvalidDestroyedImplementation","type":"error"},{"inputs":[],"name":"InvalidGroupId","type":"error"},{"inputs":[{"internalType":"address","name":"_implementation","type":"address"}],"name":"InvalidImplementation","type":"error"},{"inputs":[],"name":"InvalidInitialization","type":"error"},{"inputs":[{"internalType":"uint16","name":"groupId","type":"uint16"},{"internalType":"uint16","name":"maximum","type":"uint16"}],"name":"InvalidManualGroupId","type":"error"},{"inputs":[{"internalType":"address","name":"platformAdmin","type":"address"}],"name":"InvalidPlatformAdmin","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint8","name":"expectedRole","type":"uint8"}],"name":"InvalidRole","type":"error"},{"inputs":[{"internalType":"uint8","name":"expectedRole","type":"uint8"}],"name":"InvalidRoleId","type":"error"},{"inputs":[{"internalType":"address","name":"roleManager","type":"address"}],"name":"InvalidRoleManager","type":"error"},{"inputs":[],"name":"InvalidTimestamp","type":"error"},{"inputs":[{"internalType":"address","name":"_token","type":"address"}],"name":"InvalidToken","type":"error"},{"inputs":[],"name":"InvalidUpgradeCalldata","type":"error"},{"inputs":[],"name":"NoUpgradeScheduled","type":"error"},{"inputs":[{"internalType":"bytes32","name":"operationId","type":"bytes32"}],"name":"OperationExecutionFailed","type":"error"},{"inputs":[{"internalType":"address","name":"_implementation","type":"address"}],"name":"SameImplementation","type":"error"},{"inputs":[],"name":"TimeLockAlreadyExist","type":"error"},{"inputs":[],"name":"TimelockDoesNotExist","type":"error"},{"inputs":[],"name":"TimelockNotReached","type":"error"},{"inputs":[{"internalType":"uint256","name":"provided","type":"uint256"},{"internalType":"uint256","name":"maximum","type":"uint256"}],"name":"TooManyGroupIds","type":"error"},{"inputs":[],"name":"UUPSUnauthorizedCallContext","type":"error"},{"inputs":[],"name":"UnAuthorizedCall","type":"error"},{"inputs":[],"name":"Unauthorized","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"caller","type":"address"},{"indexed":true,"internalType":"address","name":"context","type":"address"}],"name":"Callers","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"operationId","type":"bytes32"}],"name":"Canceled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint16","name":"customGroupId","type":"uint16"}],"name":"CustomGroupIdEnabled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"operationId","type":"bytes32"},{"indexed":false,"internalType":"address","name":"target","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"data","type":"bytes"},{"indexed":false,"internalType":"uint256","name":"timestamp","type":"uint256"}],"name":"Executed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes4","name":"functionSig","type":"bytes4"},{"indexed":false,"internalType":"bool","name":"isPublic","type":"bool"},{"indexed":false,"internalType":"bytes32","name":"roles","type":"bytes32"}],"name":"FunctionPermissionUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes4","name":"functionSig","type":"bytes4"}],"name":"FunctionPermissionsReset","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes4","name":"functionSig","type":"bytes4"}],"name":"FunctionRegistered","type":"event"},{"anonymous":false,"inputs":[],"name":"Initialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"operationId","type":"bytes32"},{"indexed":false,"internalType":"address","name":"target","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"data","type":"bytes"},{"indexed":false,"internalType":"uint256","name":"eta","type":"uint256"}],"name":"Queued","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"oldImplementation","type":"address"},{"indexed":true,"internalType":"address","name":"newImplementation","type":"address"}],"name":"SygTokenImplementationUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"address","name":"implementation","type":"address"},{"indexed":false,"internalType":"bytes","name":"initData","type":"bytes"},{"indexed":false,"internalType":"uint16[]","name":"groupIds","type":"uint16[]"}],"name":"TokenDeployed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":true,"internalType":"address","name":"destroyedImplementation","type":"address"}],"name":"TokenDestroyScheduled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":true,"internalType":"address","name":"destroyedImplementation","type":"address"}],"name":"TokenDestroyed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint16[]","name":"newGroupIds","type":"uint16[]"}],"name":"TokenGroupIdsUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token","type":"address"}],"name":"TokenUpgradeCanceled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":true,"internalType":"address","name":"newImplementation","type":"address"}],"name":"TokenUpgradeScheduled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":true,"internalType":"address","name":"newImplementation","type":"address"}],"name":"TokenUpgraded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"implementation","type":"address"}],"name":"Upgraded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"uint16","name":"group","type":"uint16"},{"indexed":true,"internalType":"uint8","name":"role","type":"uint8"}],"name":"UserRoleAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"uint16","name":"group","type":"uint16"},{"indexed":true,"internalType":"uint8","name":"role","type":"uint8"}],"name":"UserRoleBlacklisted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"uint16","name":"group","type":"uint16"},{"indexed":true,"internalType":"uint8","name":"role","type":"uint8"}],"name":"UserRoleReset","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"uint16","name":"group","type":"uint16"}],"name":"UserRolesReset","type":"event"},{"inputs":[{"internalType":"uint16","name":"_groupId","type":"uint16"},{"internalType":"address","name":"_account","type":"address"},{"internalType":"bool","name":"_blacklistTokenUser","type":"bool"}],"name":"blacklistTokenUser","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_newImplementation","type":"address"},{"internalType":"bytes","name":"_data","type":"bytes"}],"name":"callUpgradeToAndCallUUPS","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_user","type":"address"},{"internalType":"bytes4","name":"_functionSig","type":"bytes4"}],"name":"canCall","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"}],"name":"cancelTokenUpgrade","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"cancelUpgrade","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"_name","type":"string"},{"internalType":"string","name":"_symbol","type":"string"},{"internalType":"uint8","name":"_decimals","type":"uint8"},{"internalType":"string","name":"_tokenURI","type":"string"},{"internalType":"uint16[]","name":"_groupIds","type":"uint16[]"},{"internalType":"uint256","name":"_timelockDelaySeconds","type":"uint256"}],"name":"deployToken","outputs":[{"internalType":"address","name":"_token","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"}],"name":"destroyToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint16[]","name":"_groups","type":"uint16[]"},{"internalType":"address","name":"_user","type":"address"},{"internalType":"uint8","name":"_role","type":"uint8"}],"name":"doesUserHaveRole","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"}],"name":"enableCustomGroupId","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"}],"name":"executeTokenUpgrade","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"executeUpgrade","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"getImplementation","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_operationId","type":"bytes32"}],"name":"getScheduledOperationDetails","outputs":[{"internalType":"address","name":"target","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"uint256","name":"eta","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getSygTokenDestroyedImplementation","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getSygTokenImplementation","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_tokenAddress","type":"address"}],"name":"getTokenInfo","outputs":[{"internalType":"address","name":"tokenAddress","type":"address"},{"internalType":"uint16[]","name":"groupIds","type":"uint16[]"},{"internalType":"address","name":"implementationAddress","type":"address"},{"internalType":"uint16","name":"customGroupId","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint16","name":"_group","type":"uint16"},{"internalType":"address","name":"_user","type":"address"}],"name":"getUserRolesInGroup","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_initialImplementation","type":"address"},{"internalType":"address","name":"_destroyedImplementation","type":"address"},{"internalType":"address","name":"_platformAdmin","type":"address"},{"internalType":"address","name":"_roleManager","type":"address"},{"internalType":"uint256","name":"_timelockDelaySeconds","type":"uint256"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"isInitialized","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"},{"internalType":"uint16[]","name":"_groupIds","type":"uint16[]"}],"name":"isMinterBurner","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"},{"internalType":"uint16[]","name":"_groupIds","type":"uint16[]"}],"name":"isOperator","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"},{"internalType":"uint16[]","name":"_groupIds","type":"uint16[]"}],"name":"isPauser","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"},{"internalType":"uint16[]","name":"_groupIds","type":"uint16[]"}],"name":"isPlatformAdmin","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes4","name":"_functionSig","type":"bytes4"}],"name":"isPublicFunction","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"},{"internalType":"uint16[]","name":"_groupIds","type":"uint16[]"}],"name":"isRoleManager","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"},{"internalType":"uint16[]","name":"_groupIds","type":"uint16[]"}],"name":"isSystem","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"},{"internalType":"uint16[]","name":"_groupIds","type":"uint16[]"}],"name":"isToken","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"},{"internalType":"uint16[]","name":"_groupIds","type":"uint16[]"}],"name":"isTokenUser","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"},{"internalType":"uint16[]","name":"_groupIds","type":"uint16[]"}],"name":"isTokenUserManager","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"proxiableUUID","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint16","name":"_groupId","type":"uint16"},{"internalType":"address","name":"_account","type":"address"},{"internalType":"bool","name":"_minterBurner","type":"bool"}],"name":"updateMinterBurner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint16","name":"_groupId","type":"uint16"},{"internalType":"address","name":"_account","type":"address"},{"internalType":"bool","name":"_operator","type":"bool"}],"name":"updateOperator","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint16","name":"_groupId","type":"uint16"},{"internalType":"address","name":"_account","type":"address"},{"internalType":"bool","name":"_pauser","type":"bool"}],"name":"updatePauser","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint16","name":"_groupId","type":"uint16"},{"internalType":"address","name":"_account","type":"address"},{"internalType":"bool","name":"_platformAdmin","type":"bool"}],"name":"updatePlatformAdmin","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint16","name":"_groupId","type":"uint16"},{"internalType":"address","name":"_account","type":"address"},{"internalType":"bool","name":"_roleManager","type":"bool"}],"name":"updateRoleManager","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_newImplementation","type":"address"}],"name":"updateSygTokenImplementation","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint16","name":"_groupId","type":"uint16"},{"internalType":"address","name":"_account","type":"address"},{"internalType":"bool","name":"_system","type":"bool"}],"name":"updateSystem","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"},{"internalType":"uint16[]","name":"_groupIds","type":"uint16[]"}],"name":"updateTokenGroupIds","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint16","name":"_groupId","type":"uint16"},{"internalType":"address","name":"_account","type":"address"},{"internalType":"bool","name":"_tokenUser","type":"bool"}],"name":"updateTokenUser","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint16","name":"_groupId","type":"uint16"},{"internalType":"address","name":"_account","type":"address"},{"internalType":"bool","name":"_tokenUserManager","type":"bool"}],"name":"updateTokenUserManager","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_newImplementation","type":"address"},{"internalType":"bytes","name":"_data","type":"bytes"}],"name":"upgradeToAndCall","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"},{"internalType":"bytes","name":"_data","type":"bytes"}],"name":"upgradeToken","outputs":[],"stateMutability":"nonpayable","type":"function"}],"is_changed_bytecode":false,"is_partially_verified":false,"constructor_args":null}