Mumbai Testnet

Contract

0xB983B38dd4620419E5214A3bFE2E7Ed827859586

Overview

MATIC Balance

Polygon PoS Chain LogoPolygon PoS Chain LogoPolygon PoS Chain Logo0 MATIC

Multichain Info

N/A
Transaction Hash
Method
Block
From
To
Value
0x61012060314365972023-01-26 16:40:55427 days ago1674751255IN
 Create: ScannerToScannerPoolMigration
0 MATIC0.003312411.50000001

Parent Txn Hash Block From To Value
View All Internal Transactions
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
ScannerToScannerPoolMigration

Compiler Version
v0.8.9+commit.e5eed63a

Optimization Enabled:
Yes with 200 runs

Other Settings:
default evmVersion

Contract Source Code (Solidity Standard Json-Input format)

File 1 of 56 : ScannerToNodeRunnerMigration.sol
// SPDX-License-Identifier: UNLICENSED
// See Forta Network License: https://github.com/forta-network/forta-contracts/blob/master/LICENSE.md

pragma solidity ^0.8.9;

import "../BaseComponentUpgradeable.sol";
import "./ScannerRegistry.sol";
import "../scanner_pools/ScannerPoolRegistry.sol";
import "../staking/IStakeMigrator.sol";

/**
 * Migration of ScannerRegistry to ScannerPoolRegistry
 */
contract ScannerToScannerPoolMigration is BaseComponentUpgradeable {
    /** Contract version */
    string public constant version = "0.1.0";
    uint256 public constant SCANNER_POOL_NOT_MIGRATED = 0;

    /// @custom:oz-upgrades-unsafe-allow state-variable-immutable
    ScannerRegistry public immutable scannerNodeRegistry;
    /// @custom:oz-upgrades-unsafe-allow state-variable-immutable
    ScannerPoolRegistry public immutable scannerPoolRegistry;
    /// @custom:oz-upgrades-unsafe-allow state-variable-immutable
    IStakeMigrator public immutable stakeMigrator;
    /// chainId => scannerPoolAddress => scannerPoolId
    mapping(uint256 => mapping(address => uint256)) private _migratedScannerPools;

    event MigrationExecuted(uint256 scannersMigrated, uint256 scannersIgnored, uint256 indexed scannerPoolId, bool mintedScannerPool);

    error NotOwnerOfScannerPool(address pretender, uint256 scannerPoolId);
    error WrongScannerChainId(uint256 expected, uint256 provided, address scanner);
    error WrongScannerPoolChainId(uint256 expected, uint256 provided, uint256 scannerPoolId);
    error ScannerPoolAlreadyMigrated(uint256 scannerPoolId);

    /// @custom:oz-upgrades-unsafe-allow constructor
    constructor(
        address _forwarder,
        address _scannerNodeRegistry,
        address _scannerPoolRegistry,
        address _stakeMigrator
    ) initializer ForwardedContext(_forwarder) {
        if (_scannerNodeRegistry == address(0)) revert ZeroAddress("_scannerNodeRegistry");
        if (_scannerPoolRegistry == address(0)) revert ZeroAddress("_scannerPoolRegistry");
        if (_stakeMigrator == address(0)) revert ZeroAddress("_stakeMigrator");

        scannerNodeRegistry = ScannerRegistry(_scannerNodeRegistry);
        scannerPoolRegistry = ScannerPoolRegistry(_scannerPoolRegistry);
        stakeMigrator = IStakeMigrator(_stakeMigrator);
    }

    /**
     * @notice Initializer method, access point to initialize inheritance tree.
     * @param __manager address of AccessManager.
     */
    function initialize(address __manager) public initializer {
        __BaseComponentUpgradeable_init(__manager);
    }

    /**
     * @notice Method to self migrate from the old ScannerRegistry NFTs to a single ScannerPoolRegistry NFT, per chain.
     * WARNING: ScannerNodeRegistry's manager addresses will not be migrated, please user ScannerPoolRegistry's methods to set them again.
     * @param scanners array of scanner addresses to be migrated.
     * All the scanners willing to migrate (optingOutOfMigration flags set to false) ScannerRegistry ERC721 identified by the uint256(address)
     * in the input array will be:
     * - Registered in ScannerPoolRegistry to the scannerPoolId either indicated or generated, with the same chainId and metadata.
     * - Deleted in ScannerNodeRegistry. The ERC721 will be burned, disabled flags and managers deleted from storage.
     * Scanners with optingOutOfMigration flags == true will be ignored (opted out), and will stay in ScannerNodeRegistry.
     * At migration end, they will stop receiving work and rewards.
     * @param scannerPoolId If set as 0, a new ScannerPoolRegistry ERC721 will be minted to scannerPool, otherwise it must be set as a
     * valid ScannerPoolRegistry ERC721 id owned by scannerPool.
     * @return ScannerPoolRegistry ERC721 id the scanners are migrated to.
     */
    function selfMigrate(
        address[] calldata scanners,
        uint256 scannerPoolId,
        uint256 chainId
    ) external returns (uint256) {
        return _migrate(scanners, scannerPoolId, _msgSender(), chainId);
    }

    /**
     * @notice Method to migrate from the old ScannerRegistry NFTs to a single ScannerPoolRegistry NFT, executed by an address with the role
     * MIGRATION_EXECUTOR_ROLE.
     * WARNING: ScannerNodeRegistry's manager addresses will not be migrated, please user ScannerPoolRegistry's methods to set them again.
     * @param scanners array of scanner addresses to be migrated.
     * All the scanners willing to migrate (optingOutOfMigration flags set to false) ScannerRegistry ERC721 identified by the uint256(address)
     * in the input array will be:
     * - Registered in ScannerPoolRegistry to the scannerPoolId either indicated or generated, with the same chainId and metadata.
     * - Deleted in ScannerNodeRegistry. The ERC721 will be burned, disabled flags and managers deleted from storage.
     * Scanners with with optingOutOfMigration flags == true will be ignored (opted out), and will stay in ScannerNodeRegistry.
     * @param scannerPoolId If set as 0, a new ScannerPoolRegistry ERC721 will be minted to scannerPool, otherwise it must be set as a
     * valid ScannerPoolRegistry ERC721 id owned by scannerPool.
     * @param scannerPool address that owns the scanners and will own the ScannerPoolRegistry ERC721
     * @return ScannerPoolRegistry ERC721 id the scanners are migrated to.
     */
    function migrate(
        address[] calldata scanners,
        uint256 scannerPoolId,
        address scannerPool,
        uint256 chainId
    ) external onlyRole(MIGRATION_EXECUTOR_ROLE) returns (uint256) {
        return _migrate(scanners, scannerPoolId, scannerPool, chainId);
    }

    function _migrate(
        address[] calldata scanners,
        uint256 inputScannerPoolId,
        address scannerPool,
        uint256 chainId
    ) private returns (uint256) {
        uint256 scannerPoolId = _getScannerPoolIdOrMint(scannerPool, inputScannerPoolId, chainId);
        uint256 total = scanners.length;
        uint256 scannersMigrated = 0;
        for (uint256 i = 0; i < total; i++) {
            uint256 scannerId = scannerNodeRegistry.scannerAddressToId(scanners[i]);
            if (scannerNodeRegistry.ownerOf(scannerId) != scannerPool) revert SenderNotOwner(scannerPool, scannerId);
            
            (string memory metadata, uint256 disabledFlags) = _checksScanner(scannerId, chainId, scanners[i]);

            if (!scannerNodeRegistry.optingOutOfMigration(scannerId)) {
                _migrateRegistries(scanners[i], disabledFlags, scannerPoolId, chainId, metadata);
                {
                    stakeMigrator.migrate(SCANNER_SUBJECT, scannerId, SCANNER_POOL_SUBJECT, scannerPoolId, scannerPool);

                }
                scannersMigrated++;
            }
        }
        emit MigrationExecuted(scannersMigrated, total - scannersMigrated, scannerPoolId, inputScannerPoolId == SCANNER_POOL_NOT_MIGRATED);
        return scannerPoolId;
    }

    function _checksScanner(uint256 scannerId, uint256 chainId, address scanner) private view returns (string memory metadata, uint256 disabledFlags) {
        (, , uint256 scannerChainId, string memory data, , uint256 flags) = scannerNodeRegistry.getScannerState(scannerId);
        if (scannerChainId != chainId) revert WrongScannerChainId(chainId, scannerChainId, scanner);
        return(data, flags);
    }


    function _migrateRegistries(
        address scanner,
        uint256 disabledFlags,
        uint256 scannerPoolId,
        uint256 chainId,
        string memory metadata
    ) private {
        scannerPoolRegistry.registerMigratedScannerNode(
            ScannerPoolRegistryCore.ScannerNodeRegistration({ scanner: scanner, scannerPoolId: scannerPoolId, chainId: chainId, metadata: metadata, timestamp: block.timestamp }),
            disabledFlags != 0
        );
        scannerNodeRegistry.deregisterScannerNode(scannerNodeRegistry.scannerAddressToId(scanner));
        
    }

    function _getScannerPoolIdOrMint(
        address scannerPool,
        uint256 scannerPoolId,
        uint256 chainId
    ) private returns (uint256) {
        if (scannerPoolId == SCANNER_POOL_NOT_MIGRATED) {
            if (_migratedScannerPools[chainId][scannerPool] != 0) {
                revert ScannerPoolAlreadyMigrated(_migratedScannerPools[chainId][scannerPool]);
            } else {
                uint256 newScannerPoolId = scannerPoolRegistry.registerMigratedScannerPool(scannerPool, chainId);
                _migratedScannerPools[chainId][scannerPool] = newScannerPoolId;
                return newScannerPoolId;
            }
        } else if (scannerPoolRegistry.ownerOf(scannerPoolId) != scannerPool) {
            revert NotOwnerOfScannerPool(scannerPool, scannerPoolId);
        } else if (scannerPoolRegistry.monitoredChainId(scannerPoolId) != chainId) {
            revert WrongScannerPoolChainId(chainId, scannerPoolRegistry.monitoredChainId(scannerPoolId), scannerPoolId);
        }
        return scannerPoolId;
    }

    /**
     *  50
     * - 1 _migratedScannerPools;
     * --------------------------
     *  49 __gap
     */
    uint256[49] private __gap;
}

File 2 of 56 : BaseComponentUpgradeable.sol
// SPDX-License-Identifier: UNLICENSED
// See Forta Network License: https://github.com/forta-network/forta-contracts/blob/master/LICENSE.md

pragma solidity ^0.8.9;

import "@openzeppelin/contracts/utils/Multicall.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
import "./Roles.sol";
import "./utils/AccessManaged.sol";
import "./utils/IVersioned.sol";
import "./utils/ForwardedContext.sol";
import "./utils/Routed.sol";
import "../tools/ENSReverseRegistration.sol";

/**
 * @dev The Forta platform is composed of "component" smart contracts that are upgradeable, share a common access
 * control scheme and can send use routed hooks to signal one another. They also support the multicall pattern.
 *
 * This contract contains the base of Forta components. Contracts that inherit this component must call
 * - __BaseComponentUpgradeable_init(address manager)
 * in their initialization process.
 */
abstract contract BaseComponentUpgradeable is
    ForwardedContext,
    AccessManagedUpgradeable,
    RoutedUpgradeable,
    Multicall,
    UUPSUpgradeable,
    IVersioned
{

    function __BaseComponentUpgradeable_init(address __manager) internal initializer {
        __AccessManaged_init(__manager);
        __UUPSUpgradeable_init();
    }
    
    // Access control for the upgrade process
    function _authorizeUpgrade(address newImplementation) internal virtual override onlyRole(UPGRADER_ROLE) {
    }

    // Allow the upgrader to set ENS reverse registration
    function setName(address ensRegistry, string calldata ensName) public onlyRole(ENS_MANAGER_ROLE) {
        ENSReverseRegistration.setName(ensRegistry, ensName);
    }

    /**
     * @notice Helper to get either msg msg.sender if not a meta transaction, signer of forwarder metatx if it is.
     * @inheritdoc ForwardedContext
     */
    function _msgSender() internal view virtual override(ContextUpgradeable, ForwardedContext) returns (address sender) {
        return super._msgSender();
    }

    /**
     * @notice Helper to get msg.data if not a meta transaction, forwarder data in metatx if it is.
     * @inheritdoc ForwardedContext
     */
    function _msgData() internal view virtual override(ContextUpgradeable, ForwardedContext) returns (bytes calldata) {
        return super._msgData();
    }

    uint256[50] private __gap;
}

File 3 of 56 : IStakeMigrator.sol
// SPDX-License-Identifier: UNLICENSED
// See Forta Network License: https://github.com/forta-network/forta-contracts/blob/master/LICENSE.md

pragma solidity ^0.8.9;

interface IStakeMigrator {
    function migrate(
        uint8 oldSubjectType,
        uint256 oldSubject,
        uint8 newSubjectType,
        uint256 newSubject,
        address staker
    ) external;
}

File 4 of 56 : ScannerPoolRegistry.sol
// SPDX-License-Identifier: UNLICENSED
// See Forta Network License: https://github.com/forta-network/forta-contracts/blob/master/LICENSE.md

pragma solidity ^0.8.9;

import "../BaseComponentUpgradeable.sol";
import "./ScannerPoolRegistryCore.sol";
import "./ScannerPoolRegistryManaged.sol";

/**
 * ERC721 Registry of Scanner Pools. Each scanner ScannerPool EOA controls a number of Scanner Nodes through the ownership of this NFT,
 * represented by their EOA address.
 * The Scanner Pool must register themselves, then register scanner addresses to be controlled by their scannerPoolId (incremental uint).
 * Registered Scanner Pools can also assign managers to manage the scanners.
 * Each Scanner Pool has a single "chainId" for all the scanners, and each scanner has metadata (string that can point to a URL, IPFS…).
 * Scanner Pool owners and managers can update said metadata.
 * Scanner Nodes can be enabled or disabled by:
 * - the Scanner itself,
 * - the ScannerPool owner
 * - any of the scanner managers
 * If the scannerId is staked under the minimum stake, it can’t be `enabled()` and `isEnabled()` will return false, regardless of the disabled flag.
 * If the scanner is not registered, `isEnabled()` will return false.
 * A Scanner Node that is not enabled will not receive work (bot assignments)
 */
contract ScannerPoolRegistry is BaseComponentUpgradeable, ScannerPoolRegistryCore, ScannerPoolRegistryManaged {
    string public constant version = "0.1.0";

    /// @custom:oz-upgrades-unsafe-allow constructor
    constructor(address forwarder, address stakeAllocator) initializer ForwardedContext(forwarder) ScannerPoolRegistryCore(stakeAllocator) {}

    /**
     * @notice Initializer method, access point to initialize inheritance tree.
     * @param __manager address of AccessManager.
     * @param __name ERC721 token name.
     * @param __symbol ERC721 token symbol.
     * @param __stakeSubjectGateway address of StakeSubjectGateway
     * @param __registrationDelay amount of time allowed from scanner signing a ScannerNodeRegistration request and it's execution
     */
    function initialize(
        address __manager,
        string calldata __name,
        string calldata __symbol,
        address __stakeSubjectGateway,
        uint256 __registrationDelay
    ) public initializer {
        __BaseComponentUpgradeable_init(__manager);
        __ScannerPoolRegistryCore_init(__name, __symbol, __stakeSubjectGateway, __registrationDelay);
    }

    function registerMigratedScannerPool(address scannerPoolAddress, uint256 chainId) external onlyRole(SCANNER_2_SCANNER_POOL_MIGRATOR_ROLE) returns (uint256 scannerPoolId) {
        return _registerScannerPool(scannerPoolAddress, chainId);
    }

    function registerMigratedScannerNode(ScannerNodeRegistration calldata req, bool disabled) external onlyRole(SCANNER_2_SCANNER_POOL_MIGRATOR_ROLE) {
        _registerScannerNode(req);
        if (disabled) {
            _setScannerDisableFlag(req.scanner, true);
        }
    }

    /**
     * @notice disambiguation of _canSetEnableState, adding SCANNER_2_SCANNER_POOL_MIGRATOR_ROLE to the allowed setters.
     * @inheritdoc ScannerPoolRegistryManaged
     */
    function _canSetEnableState(address scanner) internal view virtual override(ScannerPoolRegistryCore, ScannerPoolRegistryManaged) returns (bool) {
        return super._canSetEnableState(scanner) || hasRole(SCANNER_2_SCANNER_POOL_MIGRATOR_ROLE, _msgSender());
    }

    /**
     * @notice Helper to get either msg msg.sender if not a meta transaction, signer of forwarder metatx if it is.
     * @inheritdoc ForwardedContext
     */
    function _msgSender() internal view virtual override(BaseComponentUpgradeable, ScannerPoolRegistryCore) returns (address sender) {
        return super._msgSender();
    }

    /**
     * @notice Helper to get msg.data if not a meta transaction, forwarder data in metatx if it is.
     * @inheritdoc ForwardedContext
     */
    function _msgData() internal view virtual override(BaseComponentUpgradeable, ScannerPoolRegistryCore) returns (bytes calldata) {
        return super._msgData();
    }

    uint256[50] private __gap;
}

File 5 of 56 : ScannerRegistry.sol
// SPDX-License-Identifier: UNLICENSED
// See Forta Network License: https://github.com/forta-network/forta-contracts/blob/master/LICENSE.md

pragma solidity ^0.8.9;

import "../BaseComponentUpgradeable.sol";
import "./ScannerRegistryCore.sol";
import "./ScannerRegistryManaged.sol";
import "./ScannerRegistryEnable.sol";
import "./ScannerRegistryMetadata.sol";

contract ScannerRegistry is BaseComponentUpgradeable, ScannerRegistryCore, ScannerRegistryManaged, ScannerRegistryEnable, ScannerRegistryMetadata {
    event DeregisteredScanner(uint256 scannerId);
    event ConfiguredMigration(uint256 sunsettingTime, address scannerPoolRegistry);

    string public constant version = "0.1.4";

    mapping(uint256 => bool) public optingOutOfMigration;
    uint256 public sunsettingTime;
    ScannerPoolRegistry public scannerPoolRegistry;

    /// @custom:oz-upgrades-unsafe-allow constructor
    constructor(address forwarder) initializer ForwardedContext(forwarder) {}

    error CannotDeregister(uint256 scannerId);

    /**
     * @notice Initializer method, access point to initialize inheritance tree.
     * @param __manager address of AccessManager.
     * @param __name ERC721 token name.
     * @param __symbol ERC721 token symbol.
     */
    function initialize(
        address __manager,
        string calldata __name,
        string calldata __symbol
    ) public initializer {
        __BaseComponentUpgradeable_init(__manager);
        __ERC721_init(__name, __symbol);
    }

    /**
     * @notice Gets all scanner properties and state
     * @param scannerId ERC721 token id of the scanner.
     * @return registered true if scanner exists.
     * @return owner address.
     * @return chainId the scanner is monitoring.
     * @return metadata IPFS pointer for the scanner's JSON metadata.
     * @return enabled true if staked over minimum and not disabled.
     * @return disabledFlags 0 if not disabled, Permission if disabled.
     */
    function getScannerState(uint256 scannerId)
        external
        view
        returns (
            bool registered,
            address owner,
            uint256 chainId,
            string memory metadata,
            bool enabled,
            uint256 disabledFlags
        )
    {
        // If migration has started, and scanner has migrated, return ScannerPoolRegistry values
        if (scannerPoolRegistry.isScannerRegistered(address(uint160(scannerId)))) {
            return _getScannerStateFromScannerPool(scannerId);
        } else {
            return _getScannerState(scannerId);
        }
    }

    function _getScannerStateFromScannerPool(uint256 scannerId)
        private
        view
        returns (
            bool registered,
            address owner,
            uint256 chainId,
            string memory metadata,
            bool enabled,
            uint256 disabledFlags
        )
    {
        bool disabled;
        (registered, owner, chainId, metadata, enabled, disabled) = scannerPoolRegistry.getScannerState(address(uint160(scannerId)));
        if (disabled) {
            disabledFlags = 1;
        }
        return (registered, owner, chainId, metadata, enabled, disabledFlags);
    }

    function _getScannerState(uint256 scannerId)
        private
        view
        returns (
            bool registered,
            address owner,
            uint256 chainId,
            string memory metadata,
            bool enabled,
            uint256 disabledFlags
        )
    {
        (registered, owner, chainId, metadata) = super.getScanner(scannerId);
        return (registered, owner, chainId, metadata, isEnabled(scannerId), _getDisableFlags(scannerId));
    }

    function _getScannerFromScannerPool(uint256 scannerId)
        private
        view
        returns (
            bool registered,
            address owner,
            uint256 chainId,
            string memory metadata
        )
    {
        (registered, owner, chainId, metadata, , ) = scannerPoolRegistry.getScannerState(address(uint160(scannerId)));
        return (registered, owner, chainId, metadata);
    }

    function getScanner(uint256 scannerId)
        public
        view
        virtual
        override
        returns (
            bool registered,
            address owner,
            uint256 chainId,
            string memory metadata
        )
    {
        // If migration has started, and scanner has migrated, return ScannerPoolRegistry values
        if (scannerPoolRegistry.isScannerRegistered(address(uint160(scannerId)))) {
            return _getScannerFromScannerPool(scannerId);
        } else {
            return super.getScanner(scannerId);
        }
    }

    function isEnabled(uint256 scannerId) public view virtual override returns (bool) {
        // after migration, return false
        if (hasMigrationEnded()) {
            return false;
            // During migration, return ScannerPoolRegistry value if scannerId is migrated
        } else if (scannerPoolRegistry.isScannerRegistered(address(uint160(scannerId)))) {
            return scannerPoolRegistry.isScannerOperational(address(uint160(scannerId)));
            // Return ScannerRegistry value if migration has not started or if is not yet migrated
        } else {
            return super.isEnabled(scannerId);
        }
    }

    function hasMigrationEnded() public view returns (bool) {
        return sunsettingTime < block.timestamp;
    }

    function deregisterScannerNode(uint256 scannerId) external onlyRole(SCANNER_2_SCANNER_POOL_MIGRATOR_ROLE) {
        if (optingOutOfMigration[scannerId]) revert CannotDeregister(scannerId);
        _burn(scannerId);
        delete _disabled[scannerId];
        delete _managers[scannerId];
        delete _scannerMetadata[scannerId];
        emit DeregisteredScanner(scannerId);
    }

    /**
     * Declares preference for migration from ScanerRegistry to ScannerPoolRegistry. Default is yes.
     * @param scannerId ERC721 id
     * @param isOut true if the scanner does not want to be migrated to the ScannerPoolRegistry (and deleted)
     */
    function setMigrationPrefrence(uint256 scannerId, bool isOut) external onlyOwnerOf(scannerId) {
        optingOutOfMigration[scannerId] = isOut;
    }

    /*********** Admin methods ***********/

    /**
     * Configures migration params
     * @param _sunsettingTime time after which the scanners won't be operational (isEnabled will return false) and will not get bot assignments or rewards.
     * @param _scannerPoolRegistry new registry, for compatibility for off chain components during migration
     */
    function configureMigration(uint256 _sunsettingTime, address _scannerPoolRegistry) external onlyRole(DEFAULT_ADMIN_ROLE) {
        if (_sunsettingTime == 0) revert ZeroAmount("_sunsettingTime");
        if (_scannerPoolRegistry == address(0)) revert ZeroAddress("_scannerPoolRegistry");
        sunsettingTime = _sunsettingTime;
        scannerPoolRegistry = ScannerPoolRegistry(_scannerPoolRegistry);
        emit ConfiguredMigration(sunsettingTime, _scannerPoolRegistry);
    }

    /**
     * @dev inheritance disambiguation for _getStakeThreshold
     * see ScannerRegistryMetadata
     */
    function _getStakeThreshold(uint256 subject)
        internal
        view
        virtual
        override(ScannerRegistryCore, ScannerRegistryMetadata)
        returns (StakeThreshold memory)
    {
        return super._getStakeThreshold(subject);
    }

    /**
     * @notice Helper to get either msg msg.sender if not a meta transaction, signer of forwarder metatx if it is.
     * @inheritdoc ForwardedContext
     */
    function _msgSender()
        internal
        view
        virtual
        override(BaseComponentUpgradeable, ScannerRegistryCore, ScannerRegistryEnable)
        returns (address sender)
    {
        return super._msgSender();
    }

    /**
     * @notice Helper to get msg.data if not a meta transaction, forwarder data in metatx if it is.
     * @inheritdoc ForwardedContext
     */
    function _msgData()
        internal
        view
        virtual
        override(BaseComponentUpgradeable, ScannerRegistryCore, ScannerRegistryEnable)
        returns (bytes calldata)
    {
        return super._msgData();
    }

    /**
     *  50
     * - 1 optingOutOfMigration;
     * - 1 sunsettingTime;
     * - 1 scannerPoolRegistry;
     * --------------------------
     *  47 __gap
     */
    uint256[47] private __gap;
}

File 6 of 56 : Roles.sol
// SPDX-License-Identifier: UNLICENSED
// See Forta Network License: https://github.com/forta-network/forta-contracts/blob/master/LICENSE.md

pragma solidity ^0.8.9;

// These are the roles used in the components of the Forta system, except
// Forta token itself, that needs to define it's own roles for consistency across chains

bytes32 constant DEFAULT_ADMIN_ROLE = bytes32(0);

// Routing
bytes32 constant ROUTER_ADMIN_ROLE = keccak256("ROUTER_ADMIN_ROLE");
// Base component
bytes32 constant ENS_MANAGER_ROLE = keccak256("ENS_MANAGER_ROLE");
bytes32 constant UPGRADER_ROLE = keccak256("UPGRADER_ROLE");
// Registries
bytes32 constant AGENT_ADMIN_ROLE = keccak256("AGENT_ADMIN_ROLE");
bytes32 constant SCANNER_ADMIN_ROLE = keccak256("SCANNER_ADMIN_ROLE");
bytes32 constant SCANNER_POOL_ADMIN_ROLE = keccak256("SCANNER_POOL_ADMIN_ROLE");
bytes32 constant SCANNER_2_SCANNER_POOL_MIGRATOR_ROLE = keccak256("SCANNER_2_SCANNER_POOL_MIGRATOR_ROLE"); 
bytes32 constant DISPATCHER_ROLE = keccak256("DISPATCHER_ROLE");
bytes32 constant MIGRATION_EXECUTOR_ROLE = keccak256("MIGRATION_EXECUTOR_ROLE");

// Staking
bytes32 constant SLASHER_ROLE = keccak256("SLASHER_ROLE");
bytes32 constant SWEEPER_ROLE = keccak256("SWEEPER_ROLE");
bytes32 constant REWARDER_ROLE = keccak256("REWARDER_ROLE");
bytes32 constant SLASHING_ARBITER_ROLE = keccak256("SLASHING_ARBITER_ROLE");
bytes32 constant STAKING_CONTRACT_ROLE = keccak256("STAKING_CONTRACT_ROLE");
bytes32 constant STAKING_ADMIN_ROLE = keccak256("STAKING_ADMIN_ROLE");
bytes32 constant ALLOCATOR_CONTRACT_ROLE = keccak256("ALLOCATOR_CONTRACT_ROLE");

// Scanner Node Version
bytes32 constant SCANNER_VERSION_ROLE = keccak256("SCANNER_VERSION_ROLE");
bytes32 constant SCANNER_BETA_VERSION_ROLE = keccak256("SCANNER_BETA_VERSION_ROLE");

File 7 of 56 : IVersioned.sol
// SPDX-License-Identifier: UNLICENSED
// See Forta Network License: https://github.com/forta-network/forta-contracts/blob/master/LICENSE.md

pragma solidity ^0.8.9;

interface IVersioned {
    function version() external returns(string memory v);
}

File 8 of 56 : ENSReverseRegistration.sol
// SPDX-License-Identifier: UNLICENSED
// See Forta Network License: https://github.com/forta-network/forta-contracts/blob/master/LICENSE.md

pragma solidity ^0.8.9;

import { ENS } from "@ensdomains/ens-contracts/contracts/registry/ENS.sol";

interface IReverseRegistrar {
    function ADDR_REVERSE_NODE() external view returns (bytes32);
    function ens() external view returns (ENS);
    function defaultResolver() external view returns (address);
    function claim(address) external returns (bytes32);
    function claimWithResolver(address, address) external returns (bytes32);
    function setName(string calldata) external returns (bytes32);
    function node(address) external pure returns (bytes32);
}

library ENSReverseRegistration {
    // namehash('addr.reverse')
    bytes32 internal constant ADDR_REVERSE_NODE = 0x91d1777781884d03a6757a803996e38de2a42967fb37eeaca72729271025a9e2;

    function setName(address ensregistry, string calldata ensname) internal {
        IReverseRegistrar(ENS(ensregistry).owner(ADDR_REVERSE_NODE)).setName(ensname);
    }
}

File 9 of 56 : AccessManaged.sol
// SPDX-License-Identifier: UNLICENSED
// See Forta Network License: https://github.com/forta-network/forta-contracts/blob/master/LICENSE.md

pragma solidity ^0.8.9;

import "@openzeppelin/contracts/access/IAccessControl.sol";
import "@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/introspection/ERC165CheckerUpgradeable.sol";
import "../Roles.sol";
import "../../errors/GeneralErrors.sol";

abstract contract AccessManagedUpgradeable is ContextUpgradeable {

    using ERC165CheckerUpgradeable for address;

    IAccessControl private _accessControl;

    event AccessManagerUpdated(address indexed newAddressManager);
    error MissingRole(bytes32 role, address account);

    /**
     * @notice Checks if _msgSender() has `role`, reverts if not.
     * @param role the role to be tested, defined in Roles.sol and set in AccessManager instance.
     */
    modifier onlyRole(bytes32 role) {
        if (!hasRole(role, _msgSender())) {
            revert MissingRole(role, _msgSender());
        }
        _;
    }

    /**
     * @notice Initializer method, access point to initialize inheritance tree.
     * @param manager address of AccessManager.
     */
    function __AccessManaged_init(address manager) internal initializer {
        if (!manager.supportsInterface(type(IAccessControl).interfaceId)) revert UnsupportedInterface("IAccessControl");
        _accessControl = IAccessControl(manager);
        emit AccessManagerUpdated(manager);
    }

    /**
     * @notice Checks if `account has `role` assigned.
     * @param role the role to be tested, defined in Roles.sol and set in AccessManager instance.
     * @param account the address to be tested for the role.
     * @return return true if account has role, false otherwise.
     */
    function hasRole(bytes32 role, address account) internal view returns (bool) {
        return _accessControl.hasRole(role, account);
    }

    /**
     * @notice Sets AccessManager instance. Restricted to DEFAULT_ADMIN_ROLE
     * @param newManager address of the new instance of AccessManager.
     */
    function setAccessManager(address newManager) public onlyRole(DEFAULT_ADMIN_ROLE) {
        if (!newManager.supportsInterface(type(IAccessControl).interfaceId)) revert UnsupportedInterface("IAccessControl");
        _accessControl = IAccessControl(newManager);
        emit AccessManagerUpdated(newManager);
    }

    /**
     *  50
     * - 1 _accessControl;
     * --------------------------
     *  49 __gap
     */
    uint256[49] private __gap;
}

File 10 of 56 : Routed.sol
// SPDX-License-Identifier: UNLICENSED
// See Forta Network License: https://github.com/forta-network/forta-contracts/blob/master/LICENSE.md

pragma solidity ^0.8.9;

import "./AccessManaged.sol";

/**
 * Since Router is deprecated, we are keeping RoutedUpgradeable in this state to preserve storage
 * layout in deployed `BaseComponentUpgradeable` contracts.
 */
abstract contract RoutedUpgradeable is AccessManagedUpgradeable {

    /// @custom:oz-retyped-from contract IRouter
    /// @custom:oz-renamed-from _router
    address private _deprecated_router;

    event RouterUpdated(address indexed router);

    /// Sets Router instance to address(0).
    function disableRouter() public {
        if (_deprecated_router == address(0)) {
            revert ZeroAddress("_deprecated_router");
        }
        _deprecated_router = address(0);
        emit RouterUpdated(address(0));
    }

    /**
     *  50
     * - 1 _deprecated_router;
     * --------------------------
     *  49 __gap
     */
    uint256[49] private __gap;
}

File 11 of 56 : ForwardedContext.sol
// SPDX-License-Identifier: UNLICENSED
// See Forta Network License: https://github.com/forta-network/forta-contracts/blob/master/LICENSE.md

pragma solidity ^0.8.9;

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

abstract contract ForwardedContext is ContextUpgradeable {

    /// @custom:oz-upgrades-unsafe-allow state-variable-immutable
    address private immutable _trustedForwarder;

    uint256 private constant ADDRESS_SIZE_BYTES = 20;

    /// @custom:oz-upgrades-unsafe-allow constructor
    constructor(address trustedForwarder) {
        // WARNING: do not set this address to other than a deployed Forwarder instance.
        // Forwarder is critical infrastructure with priviledged address, it is safe for the limited
        // functionality of the Forwarder contract, any other EOA or contract could be a security issue.
        _trustedForwarder = trustedForwarder;
    }

    /// Checks if `forwarder` address provided is the trustedForwarder set in the constructor.
    function isTrustedForwarder(address forwarder) public view virtual returns (bool) {
        return forwarder == _trustedForwarder;
    }

    /**
     * @notice Gets sender of the transaction of signer if meta transaction.
     * @dev If the tx is sent by the trusted forwarded, we assume it is a meta transaction and 
     * the signer address is encoded in the last 20 bytes of msg.data.
     * @return sender address of sender of the transaction of signer if meta transaction.
     */
    function _msgSender() internal view virtual override returns (address sender) {
        if (isTrustedForwarder(msg.sender)) {
            return address(bytes20(msg.data[msg.data.length - ADDRESS_SIZE_BYTES: msg.data.length]));
        } else {
            return super._msgSender();
        }
    }

    /**
     * @notice Gets msg.data of the transaction or meta-tx.
     * @dev If the tx is sent by the trusted forwarded, we assume it is a meta transaction and 
     * msg.data must have the signer address (encoded in the last 20 bytes of msg.data) removed.
     * @return msg.data of the transaction of msg.data - signer address if meta transaction.
     */
    function _msgData() internal view virtual override returns (bytes calldata) {
        if (isTrustedForwarder(msg.sender)) {
            return msg.data[:msg.data.length - ADDRESS_SIZE_BYTES];
        } else {
            return super._msgData();
        }
    }
}

File 12 of 56 : Multicall.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (utils/Multicall.sol)

pragma solidity ^0.8.0;

import "./Address.sol";

/**
 * @dev Provides a function to batch together multiple calls in a single external call.
 *
 * _Available since v4.1._
 */
abstract contract Multicall {
    /**
     * @dev Receives and executes a batch of function calls on this contract.
     */
    function multicall(bytes[] calldata data) external virtual returns (bytes[] memory results) {
        results = new bytes[](data.length);
        for (uint256 i = 0; i < data.length; i++) {
            results[i] = Address.functionDelegateCall(address(this), data[i]);
        }
        return results;
    }
}

File 13 of 56 : UUPSUpgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (proxy/utils/UUPSUpgradeable.sol)

pragma solidity ^0.8.0;

import "../../interfaces/draft-IERC1822Upgradeable.sol";
import "../ERC1967/ERC1967UpgradeUpgradeable.sol";
import "./Initializable.sol";

/**
 * @dev An upgradeability mechanism designed for UUPS proxies. The functions included here can perform an upgrade of an
 * {ERC1967Proxy}, when this contract is set as the implementation behind such a proxy.
 *
 * A security mechanism ensures that an upgrade does not turn off upgradeability accidentally, although this risk is
 * reinstated if the upgrade retains upgradeability but removes the security mechanism, e.g. by replacing
 * `UUPSUpgradeable` with a custom implementation of upgrades.
 *
 * The {_authorizeUpgrade} function must be overridden to include access restriction to the upgrade mechanism.
 *
 * _Available since v4.1._
 */
abstract contract UUPSUpgradeable is Initializable, IERC1822ProxiableUpgradeable, ERC1967UpgradeUpgradeable {
    function __UUPSUpgradeable_init() internal onlyInitializing {
    }

    function __UUPSUpgradeable_init_unchained() internal onlyInitializing {
    }
    /// @custom:oz-upgrades-unsafe-allow state-variable-immutable state-variable-assignment
    address private immutable __self = address(this);

    /**
     * @dev Check that the execution is being performed through a delegatecall call and that the execution context is
     * a proxy contract with an implementation (as defined in ERC1967) pointing to self. This should only be the case
     * for UUPS and transparent proxies that are using the current contract as their implementation. Execution of a
     * function through ERC1167 minimal proxies (clones) would not normally pass this test, but is not guaranteed to
     * fail.
     */
    modifier onlyProxy() {
        require(address(this) != __self, "Function must be called through delegatecall");
        require(_getImplementation() == __self, "Function must be called through active proxy");
        _;
    }

    /**
     * @dev Check that the execution is not being performed through a delegate call. This allows a function to be
     * callable on the implementing contract but not through proxies.
     */
    modifier notDelegated() {
        require(address(this) == __self, "UUPSUpgradeable: must not be called through delegatecall");
        _;
    }

    /**
     * @dev Implementation of the ERC1822 {proxiableUUID} function. This returns the storage slot used by the
     * implementation. It is used to validate that the this implementation remains valid after an upgrade.
     *
     * IMPORTANT: A proxy pointing at a proxiable contract should not be considered proxiable itself, because this risks
     * bricking a proxy that upgrades to it, by delegating to itself until out of gas. Thus it is critical that this
     * function revert if invoked through a proxy. This is guaranteed by the `notDelegated` modifier.
     */
    function proxiableUUID() external view virtual override notDelegated returns (bytes32) {
        return _IMPLEMENTATION_SLOT;
    }

    /**
     * @dev Upgrade the implementation of the proxy to `newImplementation`.
     *
     * Calls {_authorizeUpgrade}.
     *
     * Emits an {Upgraded} event.
     */
    function upgradeTo(address newImplementation) external virtual onlyProxy {
        _authorizeUpgrade(newImplementation);
        _upgradeToAndCallUUPS(newImplementation, new bytes(0), false);
    }

    /**
     * @dev Upgrade the implementation of the proxy to `newImplementation`, and subsequently execute the function call
     * encoded in `data`.
     *
     * Calls {_authorizeUpgrade}.
     *
     * Emits an {Upgraded} event.
     */
    function upgradeToAndCall(address newImplementation, bytes memory data) external payable virtual onlyProxy {
        _authorizeUpgrade(newImplementation);
        _upgradeToAndCallUUPS(newImplementation, data, true);
    }

    /**
     * @dev Function that should revert when `msg.sender` is not authorized to upgrade the contract. Called by
     * {upgradeTo} and {upgradeToAndCall}.
     *
     * Normally, this function will use an xref:access.adoc[access control] modifier such as {Ownable-onlyOwner}.
     *
     * ```solidity
     * function _authorizeUpgrade(address) internal override onlyOwner {}
     * ```
     */
    function _authorizeUpgrade(address newImplementation) internal virtual;

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

File 14 of 56 : ENS.sol
pragma solidity >=0.8.4;

interface ENS {

    // Logged when the owner of a node assigns a new owner to a subnode.
    event NewOwner(bytes32 indexed node, bytes32 indexed label, address owner);

    // Logged when the owner of a node transfers ownership to a new account.
    event Transfer(bytes32 indexed node, address owner);

    // Logged when the resolver for a node changes.
    event NewResolver(bytes32 indexed node, address resolver);

    // Logged when the TTL of a node changes
    event NewTTL(bytes32 indexed node, uint64 ttl);

    // Logged when an operator is added or removed.
    event ApprovalForAll(address indexed owner, address indexed operator, bool approved);

    function setRecord(bytes32 node, address owner, address resolver, uint64 ttl) external virtual;
    function setSubnodeRecord(bytes32 node, bytes32 label, address owner, address resolver, uint64 ttl) external virtual;
    function setSubnodeOwner(bytes32 node, bytes32 label, address owner) external virtual returns(bytes32);
    function setResolver(bytes32 node, address resolver) external virtual;
    function setOwner(bytes32 node, address owner) external virtual;
    function setTTL(bytes32 node, uint64 ttl) external virtual;
    function setApprovalForAll(address operator, bool approved) external virtual;
    function owner(bytes32 node) external virtual view returns (address);
    function resolver(bytes32 node) external virtual view returns (address);
    function ttl(bytes32 node) external virtual view returns (uint64);
    function recordExists(bytes32 node) external virtual view returns (bool);
    function isApprovedForAll(address owner, address operator) external virtual view returns (bool);
}

File 15 of 56 : GeneralErrors.sol
// SPDX-License-Identifier: UNLICENSED
// See Forta Network License: https://github.com/forta-network/forta-contracts/blob/master/LICENSE.md

pragma solidity ^0.8.9;

error ZeroAddress(string name);
error ZeroAmount(string name);
error EmptyArray(string name);
error EmptyString(string name);
error UnorderedArray(string name);
error DifferentLengthArray(string array1, string array2);
error ArrayTooBig(uint256 length, uint256 max);
error StringTooLarge(uint256 length, uint256 max);
error AmountTooLarge(uint256 amount, uint256 max);
error AmountTooSmall(uint256 amount, uint256 min);

error UnsupportedInterface(string name);

error SenderNotOwner(address sender, uint256 ownedId);
error DoesNotHaveAccess(address sender, string access);

// Permission here refers to XXXRegistry.sol Permission enums
error DoesNotHavePermission(address sender, uint8 permission, uint256 id);

File 16 of 56 : ContextUpgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)

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

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

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

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

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

File 17 of 56 : IAccessControl.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (access/IAccessControl.sol)

pragma solidity ^0.8.0;

/**
 * @dev External interface of AccessControl declared to support ERC165 detection.
 */
interface IAccessControl {
    /**
     * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`
     *
     * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite
     * {RoleAdminChanged} not being emitted signaling this.
     *
     * _Available since v3.1._
     */
    event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);

    /**
     * @dev Emitted when `account` is granted `role`.
     *
     * `sender` is the account that originated the contract call, an admin role
     * bearer except when using {AccessControl-_setupRole}.
     */
    event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);

    /**
     * @dev Emitted when `account` is revoked `role`.
     *
     * `sender` is the account that originated the contract call:
     *   - if using `revokeRole`, it is the admin role bearer
     *   - if using `renounceRole`, it is the role bearer (i.e. `account`)
     */
    event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);

    /**
     * @dev Returns `true` if `account` has been granted `role`.
     */
    function hasRole(bytes32 role, address account) external view returns (bool);

    /**
     * @dev Returns the admin role that controls `role`. See {grantRole} and
     * {revokeRole}.
     *
     * To change a role's admin, use {AccessControl-_setRoleAdmin}.
     */
    function getRoleAdmin(bytes32 role) external view returns (bytes32);

    /**
     * @dev Grants `role` to `account`.
     *
     * If `account` had not been already granted `role`, emits a {RoleGranted}
     * event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     */
    function grantRole(bytes32 role, address account) external;

    /**
     * @dev Revokes `role` from `account`.
     *
     * If `account` had been granted `role`, emits a {RoleRevoked} event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     */
    function revokeRole(bytes32 role, address account) external;

    /**
     * @dev Revokes `role` from the calling account.
     *
     * Roles are often managed via {grantRole} and {revokeRole}: this function's
     * purpose is to provide a mechanism for accounts to lose their privileges
     * if they are compromised (such as when a trusted device is misplaced).
     *
     * If the calling account had been granted `role`, emits a {RoleRevoked}
     * event.
     *
     * Requirements:
     *
     * - the caller must be `account`.
     */
    function renounceRole(bytes32 role, address account) external;
}

File 18 of 56 : ERC165CheckerUpgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165Checker.sol)

pragma solidity ^0.8.0;

import "./IERC165Upgradeable.sol";

/**
 * @dev Library used to query support of an interface declared via {IERC165}.
 *
 * Note that these functions return the actual result of the query: they do not
 * `revert` if an interface is not supported. It is up to the caller to decide
 * what to do in these cases.
 */
library ERC165CheckerUpgradeable {
    // As per the EIP-165 spec, no interface should ever match 0xffffffff
    bytes4 private constant _INTERFACE_ID_INVALID = 0xffffffff;

    /**
     * @dev Returns true if `account` supports the {IERC165} interface,
     */
    function supportsERC165(address account) internal view returns (bool) {
        // Any contract that implements ERC165 must explicitly indicate support of
        // InterfaceId_ERC165 and explicitly indicate non-support of InterfaceId_Invalid
        return
            _supportsERC165Interface(account, type(IERC165Upgradeable).interfaceId) &&
            !_supportsERC165Interface(account, _INTERFACE_ID_INVALID);
    }

    /**
     * @dev Returns true if `account` supports the interface defined by
     * `interfaceId`. Support for {IERC165} itself is queried automatically.
     *
     * See {IERC165-supportsInterface}.
     */
    function supportsInterface(address account, bytes4 interfaceId) internal view returns (bool) {
        // query support of both ERC165 as per the spec and support of _interfaceId
        return supportsERC165(account) && _supportsERC165Interface(account, interfaceId);
    }

    /**
     * @dev Returns a boolean array where each value corresponds to the
     * interfaces passed in and whether they're supported or not. This allows
     * you to batch check interfaces for a contract where your expectation
     * is that some interfaces may not be supported.
     *
     * See {IERC165-supportsInterface}.
     *
     * _Available since v3.4._
     */
    function getSupportedInterfaces(address account, bytes4[] memory interfaceIds)
        internal
        view
        returns (bool[] memory)
    {
        // an array of booleans corresponding to interfaceIds and whether they're supported or not
        bool[] memory interfaceIdsSupported = new bool[](interfaceIds.length);

        // query support of ERC165 itself
        if (supportsERC165(account)) {
            // query support of each interface in interfaceIds
            for (uint256 i = 0; i < interfaceIds.length; i++) {
                interfaceIdsSupported[i] = _supportsERC165Interface(account, interfaceIds[i]);
            }
        }

        return interfaceIdsSupported;
    }

    /**
     * @dev Returns true if `account` supports all the interfaces defined in
     * `interfaceIds`. Support for {IERC165} itself is queried automatically.
     *
     * Batch-querying can lead to gas savings by skipping repeated checks for
     * {IERC165} support.
     *
     * See {IERC165-supportsInterface}.
     */
    function supportsAllInterfaces(address account, bytes4[] memory interfaceIds) internal view returns (bool) {
        // query support of ERC165 itself
        if (!supportsERC165(account)) {
            return false;
        }

        // query support of each interface in _interfaceIds
        for (uint256 i = 0; i < interfaceIds.length; i++) {
            if (!_supportsERC165Interface(account, interfaceIds[i])) {
                return false;
            }
        }

        // all interfaces supported
        return true;
    }

    /**
     * @notice Query if a contract implements an interface, does not check ERC165 support
     * @param account The address of the contract to query for support of an interface
     * @param interfaceId The interface identifier, as specified in ERC-165
     * @return true if the contract at account indicates support of the interface with
     * identifier interfaceId, false otherwise
     * @dev Assumes that account contains a contract that supports ERC165, otherwise
     * the behavior of this method is undefined. This precondition can be checked
     * with {supportsERC165}.
     * Interface identification is specified in ERC-165.
     */
    function _supportsERC165Interface(address account, bytes4 interfaceId) private view returns (bool) {
        bytes memory encodedParams = abi.encodeWithSelector(IERC165Upgradeable.supportsInterface.selector, interfaceId);
        (bool success, bytes memory result) = account.staticcall{gas: 30000}(encodedParams);
        if (result.length < 32) return false;
        return success && abi.decode(result, (bool));
    }
}

File 19 of 56 : Initializable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (proxy/utils/Initializable.sol)

pragma solidity ^0.8.2;

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

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

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

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

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

    /**
     * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
     * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
     * used to initialize parent contracts.
     *
     * `initializer` is equivalent to `reinitializer(1)`, so a reinitializer may be used after the original
     * initialization step. This is essential to configure modules that are added through upgrades and that require
     * initialization.
     *
     * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
     * a contract, executing them in the right order is up to the developer or operator.
     */
    modifier reinitializer(uint8 version) {
        require(!_initializing && _initialized < version, "Initializable: contract is already initialized");
        _initialized = version;
        _initializing = true;
        _;
        _initializing = false;
        emit Initialized(version);
    }

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

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

File 20 of 56 : AddressUpgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (utils/Address.sol)

pragma solidity ^0.8.1;

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

        return account.code.length > 0;
    }

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

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

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

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

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

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

        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResult(success, returndata, errorMessage);
    }

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

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        require(isContract(target), "Address: static call to non-contract");

        (bool success, bytes memory returndata) = target.staticcall(data);
        return verifyCallResult(success, returndata, errorMessage);
    }

    /**
     * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
     * revert reason using the provided one.
     *
     * _Available since v4.3._
     */
    function verifyCallResult(
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal pure returns (bytes memory) {
        if (success) {
            return returndata;
        } else {
            // Look for revert reason and bubble it up if present
            if (returndata.length > 0) {
                // The easiest way to bubble the revert reason is using memory via assembly
                /// @solidity memory-safe-assembly
                assembly {
                    let returndata_size := mload(returndata)
                    revert(add(32, returndata), returndata_size)
                }
            } else {
                revert(errorMessage);
            }
        }
    }
}

File 21 of 56 : IERC165Upgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC165 standard, as defined in the
 * https://eips.ethereum.org/EIPS/eip-165[EIP].
 *
 * Implementers can declare support of contract interfaces, which can then be
 * queried by others ({ERC165Checker}).
 *
 * For an implementation, see {ERC165}.
 */
interface IERC165Upgradeable {
    /**
     * @dev Returns true if this contract implements the interface defined by
     * `interfaceId`. See the corresponding
     * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
     * to learn more about how these ids are created.
     *
     * This function call must use less than 30 000 gas.
     */
    function supportsInterface(bytes4 interfaceId) external view returns (bool);
}

File 22 of 56 : Address.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (utils/Address.sol)

pragma solidity ^0.8.1;

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

        return account.code.length > 0;
    }

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

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

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

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

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

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

        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResult(success, returndata, errorMessage);
    }

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

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        require(isContract(target), "Address: static call to non-contract");

        (bool success, bytes memory returndata) = target.staticcall(data);
        return verifyCallResult(success, returndata, errorMessage);
    }

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

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        require(isContract(target), "Address: delegate call to non-contract");

        (bool success, bytes memory returndata) = target.delegatecall(data);
        return verifyCallResult(success, returndata, errorMessage);
    }

    /**
     * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
     * revert reason using the provided one.
     *
     * _Available since v4.3._
     */
    function verifyCallResult(
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal pure returns (bytes memory) {
        if (success) {
            return returndata;
        } else {
            // Look for revert reason and bubble it up if present
            if (returndata.length > 0) {
                // The easiest way to bubble the revert reason is using memory via assembly
                /// @solidity memory-safe-assembly
                assembly {
                    let returndata_size := mload(returndata)
                    revert(add(32, returndata), returndata_size)
                }
            } else {
                revert(errorMessage);
            }
        }
    }
}

File 23 of 56 : draft-IERC1822Upgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (interfaces/draft-IERC1822.sol)

pragma solidity ^0.8.0;

/**
 * @dev ERC1822: Universal Upgradeable Proxy Standard (UUPS) documents a method for upgradeability through a simplified
 * proxy whose upgrades are fully controlled by the current implementation.
 */
interface IERC1822ProxiableUpgradeable {
    /**
     * @dev Returns the storage slot that the proxiable contract assumes is being used to store the implementation
     * address.
     *
     * IMPORTANT: A proxy pointing at a proxiable contract should not be considered proxiable itself, because this risks
     * bricking a proxy that upgrades to it, by delegating to itself until out of gas. Thus it is critical that this
     * function revert if invoked through a proxy.
     */
    function proxiableUUID() external view returns (bytes32);
}

File 24 of 56 : ERC1967UpgradeUpgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (proxy/ERC1967/ERC1967Upgrade.sol)

pragma solidity ^0.8.2;

import "../beacon/IBeaconUpgradeable.sol";
import "../../interfaces/draft-IERC1822Upgradeable.sol";
import "../../utils/AddressUpgradeable.sol";
import "../../utils/StorageSlotUpgradeable.sol";
import "../utils/Initializable.sol";

/**
 * @dev This abstract contract provides getters and event emitting update functions for
 * https://eips.ethereum.org/EIPS/eip-1967[EIP1967] slots.
 *
 * _Available since v4.1._
 *
 * @custom:oz-upgrades-unsafe-allow delegatecall
 */
abstract contract ERC1967UpgradeUpgradeable is Initializable {
    function __ERC1967Upgrade_init() internal onlyInitializing {
    }

    function __ERC1967Upgrade_init_unchained() internal onlyInitializing {
    }
    // This is the keccak-256 hash of "eip1967.proxy.rollback" subtracted by 1
    bytes32 private constant _ROLLBACK_SLOT = 0x4910fdfa16fed3260ed0e7147f7cc6da11a60208b5b9406d12a635614ffd9143;

    /**
     * @dev Storage slot with the address of the current implementation.
     * This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1, and is
     * validated in the constructor.
     */
    bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;

    /**
     * @dev Emitted when the implementation is upgraded.
     */
    event Upgraded(address indexed implementation);

    /**
     * @dev Returns the current implementation address.
     */
    function _getImplementation() internal view returns (address) {
        return StorageSlotUpgradeable.getAddressSlot(_IMPLEMENTATION_SLOT).value;
    }

    /**
     * @dev Stores a new address in the EIP1967 implementation slot.
     */
    function _setImplementation(address newImplementation) private {
        require(AddressUpgradeable.isContract(newImplementation), "ERC1967: new implementation is not a contract");
        StorageSlotUpgradeable.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
    }

    /**
     * @dev Perform implementation upgrade
     *
     * Emits an {Upgraded} event.
     */
    function _upgradeTo(address newImplementation) internal {
        _setImplementation(newImplementation);
        emit Upgraded(newImplementation);
    }

    /**
     * @dev Perform implementation upgrade with additional setup call.
     *
     * Emits an {Upgraded} event.
     */
    function _upgradeToAndCall(
        address newImplementation,
        bytes memory data,
        bool forceCall
    ) internal {
        _upgradeTo(newImplementation);
        if (data.length > 0 || forceCall) {
            _functionDelegateCall(newImplementation, data);
        }
    }

    /**
     * @dev Perform implementation upgrade with security checks for UUPS proxies, and additional setup call.
     *
     * Emits an {Upgraded} event.
     */
    function _upgradeToAndCallUUPS(
        address newImplementation,
        bytes memory data,
        bool forceCall
    ) internal {
        // Upgrades from old implementations will perform a rollback test. This test requires the new
        // implementation to upgrade back to the old, non-ERC1822 compliant, implementation. Removing
        // this special case will break upgrade paths from old UUPS implementation to new ones.
        if (StorageSlotUpgradeable.getBooleanSlot(_ROLLBACK_SLOT).value) {
            _setImplementation(newImplementation);
        } else {
            try IERC1822ProxiableUpgradeable(newImplementation).proxiableUUID() returns (bytes32 slot) {
                require(slot == _IMPLEMENTATION_SLOT, "ERC1967Upgrade: unsupported proxiableUUID");
            } catch {
                revert("ERC1967Upgrade: new implementation is not UUPS");
            }
            _upgradeToAndCall(newImplementation, data, forceCall);
        }
    }

    /**
     * @dev Storage slot with the admin of the contract.
     * This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1, and is
     * validated in the constructor.
     */
    bytes32 internal constant _ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;

    /**
     * @dev Emitted when the admin account has changed.
     */
    event AdminChanged(address previousAdmin, address newAdmin);

    /**
     * @dev Returns the current admin.
     */
    function _getAdmin() internal view returns (address) {
        return StorageSlotUpgradeable.getAddressSlot(_ADMIN_SLOT).value;
    }

    /**
     * @dev Stores a new address in the EIP1967 admin slot.
     */
    function _setAdmin(address newAdmin) private {
        require(newAdmin != address(0), "ERC1967: new admin is the zero address");
        StorageSlotUpgradeable.getAddressSlot(_ADMIN_SLOT).value = newAdmin;
    }

    /**
     * @dev Changes the admin of the proxy.
     *
     * Emits an {AdminChanged} event.
     */
    function _changeAdmin(address newAdmin) internal {
        emit AdminChanged(_getAdmin(), newAdmin);
        _setAdmin(newAdmin);
    }

    /**
     * @dev The storage slot of the UpgradeableBeacon contract which defines the implementation for this proxy.
     * This is bytes32(uint256(keccak256('eip1967.proxy.beacon')) - 1)) and is validated in the constructor.
     */
    bytes32 internal constant _BEACON_SLOT = 0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50;

    /**
     * @dev Emitted when the beacon is upgraded.
     */
    event BeaconUpgraded(address indexed beacon);

    /**
     * @dev Returns the current beacon.
     */
    function _getBeacon() internal view returns (address) {
        return StorageSlotUpgradeable.getAddressSlot(_BEACON_SLOT).value;
    }

    /**
     * @dev Stores a new beacon in the EIP1967 beacon slot.
     */
    function _setBeacon(address newBeacon) private {
        require(AddressUpgradeable.isContract(newBeacon), "ERC1967: new beacon is not a contract");
        require(
            AddressUpgradeable.isContract(IBeaconUpgradeable(newBeacon).implementation()),
            "ERC1967: beacon implementation is not a contract"
        );
        StorageSlotUpgradeable.getAddressSlot(_BEACON_SLOT).value = newBeacon;
    }

    /**
     * @dev Perform beacon upgrade with additional setup call. Note: This upgrades the address of the beacon, it does
     * not upgrade the implementation contained in the beacon (see {UpgradeableBeacon-_setImplementation} for that).
     *
     * Emits a {BeaconUpgraded} event.
     */
    function _upgradeBeaconToAndCall(
        address newBeacon,
        bytes memory data,
        bool forceCall
    ) internal {
        _setBeacon(newBeacon);
        emit BeaconUpgraded(newBeacon);
        if (data.length > 0 || forceCall) {
            _functionDelegateCall(IBeaconUpgradeable(newBeacon).implementation(), data);
        }
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function _functionDelegateCall(address target, bytes memory data) private returns (bytes memory) {
        require(AddressUpgradeable.isContract(target), "Address: delegate call to non-contract");

        // solhint-disable-next-line avoid-low-level-calls
        (bool success, bytes memory returndata) = target.delegatecall(data);
        return AddressUpgradeable.verifyCallResult(success, returndata, "Address: low-level delegate call failed");
    }

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

File 25 of 56 : StorageSlotUpgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (utils/StorageSlot.sol)

pragma solidity ^0.8.0;

/**
 * @dev Library for reading and writing primitive types to specific storage slots.
 *
 * Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts.
 * This library helps with reading and writing to such slots without the need for inline assembly.
 *
 * The functions in this library return Slot structs that contain a `value` member that can be used to read or write.
 *
 * Example usage to set ERC1967 implementation slot:
 * ```
 * contract ERC1967 {
 *     bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
 *
 *     function _getImplementation() internal view returns (address) {
 *         return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
 *     }
 *
 *     function _setImplementation(address newImplementation) internal {
 *         require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract");
 *         StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
 *     }
 * }
 * ```
 *
 * _Available since v4.1 for `address`, `bool`, `bytes32`, and `uint256`._
 */
library StorageSlotUpgradeable {
    struct AddressSlot {
        address value;
    }

    struct BooleanSlot {
        bool value;
    }

    struct Bytes32Slot {
        bytes32 value;
    }

    struct Uint256Slot {
        uint256 value;
    }

    /**
     * @dev Returns an `AddressSlot` with member `value` located at `slot`.
     */
    function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := slot
        }
    }

    /**
     * @dev Returns an `BooleanSlot` with member `value` located at `slot`.
     */
    function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := slot
        }
    }

    /**
     * @dev Returns an `Bytes32Slot` with member `value` located at `slot`.
     */
    function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := slot
        }
    }

    /**
     * @dev Returns an `Uint256Slot` with member `value` located at `slot`.
     */
    function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := slot
        }
    }
}

File 26 of 56 : IBeaconUpgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (proxy/beacon/IBeacon.sol)

pragma solidity ^0.8.0;

/**
 * @dev This is the interface that {BeaconProxy} expects of its beacon.
 */
interface IBeaconUpgradeable {
    /**
     * @dev Must return an address that can be used as a delegate call target.
     *
     * {BeaconProxy} will check that this address is a contract.
     */
    function implementation() external view returns (address);
}

File 27 of 56 : ScannerPoolRegistryManaged.sol
// SPDX-License-Identifier: UNLICENSED
// See Forta Network License: https://github.com/forta-network/forta-contracts/blob/master/LICENSE.md

pragma solidity ^0.8.9;

import "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";

import "./ScannerPoolRegistryCore.sol";

abstract contract ScannerPoolRegistryManaged is ScannerPoolRegistryCore {
    using EnumerableSet for EnumerableSet.AddressSet;

    mapping(uint256 => EnumerableSet.AddressSet) private _managers;

    event ManagerEnabled(uint256 indexed scannerPoolId, address indexed manager, bool enabled);

    error SenderNotManager(address sender, uint256 scannerPoolId);

    /**
     * @notice Checks sender (or metatx signer) is manager of the scanner pool token.
     * @param scannerPoolId ERC721 token id of the ScannerPool
     */
    modifier onlyManagerOf(uint256 scannerPoolId) {
        if (!isManager(scannerPoolId, _msgSender())) revert SenderNotManager(_msgSender(), scannerPoolId);
        _;
    }

    /**
     * @notice Checks if address is defined as a manager for a ScannerPool's registered Scanner Nodes.
     * @param scannerPoolId ERC721 token id of the ScannerPool
     * @param manager address to check.
     * @return true if defined as manager for ScannerPool, false otherwise.
     */
    function isManager(uint256 scannerPoolId, address manager) public view returns (bool) {
        return _managers[scannerPoolId].contains(manager);
    }

    /**
     * @notice Gets total managers defined for a ScannerPool's registered Scanner Nodes.
     * @dev helper for external iteration.
     * @param scannerPoolId ERC721 token id of the ScannerPool
     * @return total managers defined for a ScannerPool.
     */
    function getManagerCount(uint256 scannerPoolId) public view virtual returns (uint256) {
        return _managers[scannerPoolId].length();
    }

    /**
     * @notice Gets manager address at certain position of the ScannerPool's registered Scanner Nodes.
     * @dev helper for external iteration.
     * @param scannerPoolId ERC721 token id of the ScannerPool
     * @param index position in the set.
     * @return address of the manager at index.
     */
    function getManagerAt(uint256 scannerPoolId, uint256 index) public view virtual returns (address) {
        return _managers[scannerPoolId].at(index);
    }

    /**
     * @notice Adds or removes a manager to a certain ScannerPool's registered Scanner Nodes. Restricted to ScannerPoolRegistry owner.
     * @param scannerPoolId ERC721 token id of the ScannerPool
     * @param manager address to be added or removed from manager list for the ScannerPool.
     * @param enable true for adding, false for removing.
     */
    function setManager(uint256 scannerPoolId, address manager, bool enable) public onlyScannerPool(scannerPoolId) {
        if (enable) {
            _managers[scannerPoolId].add(manager);
        } else {
            _managers[scannerPoolId].remove(manager);
        }
        emit ManagerEnabled(scannerPoolId, manager, enable);
    }

    function _canSetEnableState(address scanner) internal virtual override view returns (bool) {
        return super._canSetEnableState(scanner) || isManager(_scannerNodes[scanner].scannerPoolId, _msgSender());
    }

    /**
     *  50
     * - 1 _managers
     * --------------------------
     *  49 __gap
     */
    uint256[49] private __gap;
}

File 28 of 56 : ScannerPoolRegistryCore.sol
// SPDX-License-Identifier: UNLICENSED
// See Forta Network License: https://github.com/forta-network/forta-contracts/blob/master/LICENSE.md

pragma solidity ^0.8.9;

import "../BaseComponentUpgradeable.sol";
import "../staking/allocation/IStakeAllocator.sol";
import "../staking/stake_subjects/DelegatedStakeSubject.sol";
import "../../errors/GeneralErrors.sol";

import "@openzeppelin/contracts/utils/math/Math.sol";
import "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC721/ERC721Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC721/extensions/ERC721EnumerableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/CountersUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/cryptography/draft-EIP712Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/cryptography/SignatureCheckerUpgradeable.sol";

abstract contract ScannerPoolRegistryCore is BaseComponentUpgradeable, ERC721Upgradeable, ERC721EnumerableUpgradeable, DelegatedStakeSubjectUpgradeable, EIP712Upgradeable {
    using CountersUpgradeable for CountersUpgradeable.Counter;
    using EnumerableSet for EnumerableSet.AddressSet;

    struct ScannerNode {
        bool registered;
        bool disabled;
        uint256 scannerPoolId;
        uint256 chainId;
        string metadata;
    }
    struct ScannerNodeRegistration {
        address scanner;
        uint256 scannerPoolId;
        uint256 chainId;
        string metadata;
        uint256 timestamp;
    }

    bytes32 private constant _SCANNERNODEREGISTRATION_TYPEHASH =
        keccak256("ScannerNodeRegistration(address scanner,uint256 scannerPoolId,uint256 chainId,string metadata,uint256 timestamp)");
    /// @custom:oz-upgrades-unsafe-allow state-variable-immutable
    IStakeAllocator private immutable _stakeAllocator;

    /// scannerPoolIds is a sequential autoincremented uint
    CountersUpgradeable.Counter private _scannerPoolIdCounter;
    /// ScannerNode data for each scanner address;
    mapping(address => ScannerNode) internal _scannerNodes;
    /// Set of Scanner Node addresses each scannerPoolId owns;
    mapping(uint256 => EnumerableSet.AddressSet) private _scannerNodeOwnership;
    /// Count of enabled scanners per scannerPoolId (scannerPoolId => total Enabled Scanners)
    mapping(uint256 => uint256) private _enabledScanners;
    /// StakeThreshold of ScannerPools
    mapping(uint256 => StakeThreshold) private _scannerStakeThresholds;
    /// scannerPoolId => chainId. Limitation necessary to calculate stake allocations.
    mapping(uint256 => uint256) private _scannerPoolChainId;
    /// Maximum amount of time allowed from scanner signing a ScannerNodeRegistration and its execution by ScannerPool
    uint256 public registrationDelay;

    event ScannerUpdated(uint256 indexed scannerId, uint256 indexed chainId, string metadata, uint256 scannerPool);
    event ManagedStakeThresholdChanged(uint256 indexed chainId, uint256 min, uint256 max, bool activated);
    event RegistrationDelaySet(uint256 delay);
    // TODO: discuss with the dev team if it breaks compatibility to change 'enabled' too 'operational'
    event ScannerEnabled(uint256 indexed scannerId, bool indexed enabled, address sender, bool disableFlag);
    event EnabledScannersChanged(uint256 indexed scannerPoolId, uint256 enabledScanners);
    event ScannerPoolRegistered(uint256 indexed scannerPoolId, uint256 indexed chainId);

    error ScannerPoolNotRegistered(uint256 scannerPoolId);
    error ScannerExists(address scanner);
    error ScannerNotRegistered(address scanner);
    error PublicRegistrationDisabled(uint256 chainId);
    error RegisteringTooLate();
    error SignatureDoesNotMatch();
    error CannotSetScannerActivation();
    error SenderNotScannerPool(address sender, uint256 scannerPoolId);
    error ChainIdMismatch(uint256 expected, uint256 provided);
    error ActionShutsDownPool();

    /**
     * @notice Checks sender (or metatx signer) is owner of the ScannerPoolRegistry ERC721 with ID scannerPoolId.
     * @param scannerPoolId ERC721 token id of the ScannerPool.
     */
    modifier onlyScannerPool(uint256 scannerPoolId) {
        if (_msgSender() != ownerOf(scannerPoolId)) revert SenderNotScannerPool(_msgSender(), scannerPoolId);
        _;
    }

    modifier onlyRegisteredScanner(address scanner) {
        if (!isScannerRegistered(scanner)) revert ScannerNotRegistered(scanner);
        _;
    }

    /// @custom:oz-upgrades-unsafe-allow constructor
    constructor(address __stakeAllocator) {
        if (__stakeAllocator == address(0)) revert ZeroAddress("__stakeAllocator");
        _stakeAllocator = IStakeAllocator(__stakeAllocator);
    }

    /**
     * @notice Initializer method
     * @param __name ERC721 token name.
     * @param __symbol ERC721 token symbol.
     * @param __stakeSubjectGateway address of StakeSubjectGateway
     * @param __registrationDelay amount of time allowed from scanner signing a ScannerNodeRegistration and it's execution by ScannerPool
     */
    function __ScannerPoolRegistryCore_init(
        string calldata __name,
        string calldata __symbol,
        address __stakeSubjectGateway,
        uint256 __registrationDelay
    ) internal initializer {
        __ERC721_init(__name, __symbol);
        __ERC721Enumerable_init();
        __EIP712_init("ScannerPoolRegistry", "1");
        __StakeSubjectUpgradeable_init(__stakeSubjectGateway);

        _setRegistrationDelay(__registrationDelay);
    }

    // ************* ScannerPool Ownership *************

    /**
     * @notice Checks if scannerPoolId has been registered (minted).
     * @param scannerPoolId ERC721 token id of the ScannerPool.
     * @return true if scannerPoolId exists, false otherwise.
     */
    function isRegistered(uint256 scannerPoolId) public view override returns (bool) {
        return _exists(scannerPoolId);
    }

    /**
     * @notice mints a ScannerPoolRegistry ERC721 NFT to sender
     * Transferring ownership of a ScannerPoolRegistry NFT will transfer ownership of all its registered
     * Scanner Node addresses
     * @return scannerPoolId (autoincremented uint)
     */
    function registerScannerPool(uint256 chainId) external returns (uint256 scannerPoolId) {
        return _registerScannerPool(_msgSender(), chainId);
    }

    function _registerScannerPool(address scannerPoolAddress, uint256 chainId) internal returns (uint256 scannerPoolId) {
        if (scannerPoolAddress == address(0)) revert ZeroAddress("scannerPoolAddress");
        if (chainId == 0) revert ZeroAmount("chainId");
        _scannerPoolIdCounter.increment();
        scannerPoolId = _scannerPoolIdCounter.current();
        _safeMint(scannerPoolAddress, scannerPoolId);
        _scannerPoolChainId[scannerPoolId] = chainId;
        emit ScannerPoolRegistered(scannerPoolId, chainId);
        return scannerPoolId;
    }

    function monitoredChainId(uint256 scannerPoolId) public view returns (uint256) {
        return _scannerPoolChainId[scannerPoolId];
    }

    // ************* Scanner Ownership *************

    /**
     * @notice Checks if scanner address has been registered
     * @param scanner address.
     * @return true if scanner is registered, false otherwise.
     */
    function isScannerRegistered(address scanner) public view returns (bool) {
        return _scannerNodes[scanner].registered;
    }

    /**
     * @notice Checks if scanner address has been registered to a specific scannerPoolId
     * @param scanner address.
     * @param scannerPoolId ERC721 token id of the ScannerPool.
     * @return true if scanner is registered to scannerPoolId, false otherwise.
     */
    function isScannerRegisteredTo(address scanner, uint256 scannerPoolId) public view returns (bool) {
        return _scannerNodeOwnership[scannerPoolId].contains(scanner);
    }

    /**
     * @notice Method to register a Scanner Node and associate it with a scannerPoolId. Before executing this method,
     * make sure to have enough FORT staked by the owner of the Scanner Pool to be allocated to the new scanner,
     * then register a scanner with Forta Scan Node CLI and obtain the parameters for this methods by executing forta auth.
     * Follow the instructions here https://docs.forta.network/en/latest/scanner-quickstart/
     * This method will try to allocate stake from unallocated stake if necessary.
     * Individual ownership of a scaner node is not transferrable.
     * A scanner node can be disabled, but not unregistered
     * @param req ScannerNodeRegistration struct with the Scanner Node data.
     * @param signature ERC712 signature, result from signed req by the scanner.
     */
    function registerScannerNode(ScannerNodeRegistration calldata req, bytes calldata signature) external onlyScannerPool(req.scannerPoolId) {
        if (req.timestamp + registrationDelay < block.timestamp) revert RegisteringTooLate();
        if (
            !SignatureCheckerUpgradeable.isValidSignatureNow(
                req.scanner,
                _hashTypedDataV4(
                    keccak256(abi.encode(_SCANNERNODEREGISTRATION_TYPEHASH, req.scanner, req.scannerPoolId, req.chainId, keccak256(abi.encodePacked(req.metadata)), req.timestamp))
                ),
                signature
            )
        ) revert SignatureDoesNotMatch();
        _registerScannerNode(req);
        // Not called inside _registerScannerNode because it would break registerMigratedScannerNode()
        _allocationOnAddedEnabledScanner(req.scannerPoolId);
    }

    /**
     * @notice Allocates unallocated stake if a there is a new scanner enabled on the pool and not enough allocated stake.
     * If there is not enough unallocated stake, the method will revert.
     * @dev this MUST be called after incrementing _enabledScanners
     * @param scannerPoolId ERC721 id of the node runner
     */
    function _allocationOnAddedEnabledScanner(uint256 scannerPoolId) private {
        uint256 unallocatedStake = _stakeAllocator.unallocatedStakeFor(SCANNER_POOL_SUBJECT, scannerPoolId);
        uint256 allocatedStake = _stakeAllocator.allocatedStakeFor(SCANNER_POOL_SUBJECT, scannerPoolId);
        uint256 min = _scannerStakeThresholds[_scannerPoolChainId[scannerPoolId]].min;
        if (allocatedStake / _enabledScanners[scannerPoolId] >  min) {
            return;
        }
        if ((unallocatedStake + allocatedStake) / _enabledScanners[scannerPoolId] < min) {
            revert ActionShutsDownPool();
        }
        _stakeAllocator.allocateOwnStake(SCANNER_POOL_SUBJECT, scannerPoolId, unallocatedStake);
    }

    function _registerScannerNode(ScannerNodeRegistration calldata req) internal {
        if (isScannerRegistered(req.scanner)) revert ScannerExists(req.scanner);
        if (_scannerPoolChainId[req.scannerPoolId] != req.chainId)
            revert ChainIdMismatch(_scannerPoolChainId[req.scannerPoolId], req.chainId);
        _scannerNodes[req.scanner] = ScannerNode({ registered: true, disabled: false, scannerPoolId: req.scannerPoolId, chainId: req.chainId, metadata: req.metadata });
        // It is safe to ignore add()'s returned bool, since isScannerRegistered() already checks for duplicates.
        !_scannerNodeOwnership[req.scannerPoolId].add(req.scanner);
        emit ScannerUpdated(scannerAddressToId(req.scanner), req.chainId, req.metadata, req.scannerPoolId);
        _addEnabledScanner(req.scannerPoolId);
    }

    /**
     * @notice Method to update a registered Scanner Node metadata string. Only the ScannerPool that owns the scanner can update.
     * @param scanner address.
     * @param metadata IPFS string pointing to Scanner Node metadata.
     */
    function updateScannerMetadata(address scanner, string calldata metadata) external {
        if (!isScannerRegistered(scanner)) revert ScannerNotRegistered(scanner);
        // Not using onlyScannerPool(_scannerNodes[scanner].scannerPoolId) to improve error readability.
        // If the scanner is not registered, onlyOwner would be first and emit "ERC721: invalid token ID", which is too cryptic.
        if (_msgSender() != ownerOf(_scannerNodes[scanner].scannerPoolId)) {
            revert SenderNotScannerPool(_msgSender(), _scannerNodes[scanner].scannerPoolId);
        }
        _scannerNodes[scanner].metadata = metadata;
        emit ScannerUpdated(scannerAddressToId(scanner), _scannerNodes[scanner].chainId, metadata, _scannerNodes[scanner].scannerPoolId);
    }

    /**
     * @notice gets the amount of Scanner Nodes ever registered to a ScannerPool Id.
     * Useful for external iteration.
     * @param scannerPoolId ERC721 token id of the ScannerPool.
     */
    function totalScannersRegistered(uint256 scannerPoolId) public view returns (uint256) {
        return _scannerNodeOwnership[scannerPoolId].length();
    }

    /**
     * @notice gets the Scanner Node address at index registered to scannerPoolId
     * Useful for external iteration.
     * @param scannerPoolId ERC721 token id of the ScannerPool.
     * @param index of the registered Scanner Node. Must be lower than totalScannersRegistered(scannerPoolId)
     */
    function registeredScannerAtIndex(uint256 scannerPoolId, uint256 index) external view returns (ScannerNode memory) {
        return _scannerNodes[_scannerNodeOwnership[scannerPoolId].at(index)];
    }

    /**
     * @notice gets the Scanner Node data struct at index registered to scannerPoolId
     * Useful for external iteration.
     * @param scannerPoolId ERC721 token id of the ScannerPool.
     * @param index of the registered Scanner Node. Must be lower than totalScannersRegistered(scannerPoolId)
     */
    function registeredScannerAddressAtIndex(uint256 scannerPoolId, uint256 index) external view returns (address) {
        return _scannerNodeOwnership[scannerPoolId].at(index);
    }

    // ************* Converters *************

    /// Converts scanner address to uint256 for FortaStaking Token Id.
    function scannerAddressToId(address scanner) public pure returns (uint256) {
        return uint256(uint160(scanner));
    }

    /// Converts FortaStaking uint256 id to address.
    function scannerIdToAddress(uint256 scannerId) public pure returns (address) {
        return address(uint160(scannerId));
    }

    // ************* Scanner Disabling *************

    /// Gets if the disabled flag has been set for a Scanner Node Address
    function isScannerDisabled(address scanner) public view returns (bool) {
        return _scannerNodes[scanner].disabled;
    }

    /**
     * @notice Checks if the Scanner Node is considered operational by the Forta Network, and is thus eligible for bot (Agent) assignment.
     * @param scanner address
     * @return true if:
     * - Scanner Node is registered AND
     * - Scanner Node's disabled flag is not set (is false) AND
     * - (Scanner Node has more than minimum stake allocated to it OR staking is not activated for the Scanner Node's chain)
     */
    function isScannerOperational(address scanner) public view returns (bool) {
        ScannerNode storage node = _scannerNodes[scanner];
        StakeThreshold storage stake = _scannerStakeThresholds[node.chainId];
        return (node.registered && !node.disabled && (!stake.activated || _isScannerStakedOverMin(scanner)) && _exists(node.scannerPoolId));
    }

    /// returns true if one more enabled scanner (or one registration) would put ALL scanners under min threshold, (not operational)
    function willNewScannerShutdownPool(uint256 scannerPoolId) public view returns (bool) {
        uint256 unallocatedStake = _stakeAllocator.unallocatedStakeFor(SCANNER_POOL_SUBJECT, scannerPoolId);
        uint256 allocatedStake = _stakeAllocator.allocatedStakeFor(SCANNER_POOL_SUBJECT, scannerPoolId);
        uint256 min = _scannerStakeThresholds[_scannerPoolChainId[scannerPoolId]].min;
        return (allocatedStake + unallocatedStake) / (_enabledScanners[scannerPoolId] + 1) < min;
    }

    /// Returns true if the owner of NodeRegistry (DELEGATED) has staked over min for scanner, false otherwise.
    function _isScannerStakedOverMin(address scanner) internal view returns (bool) {
        ScannerNode storage node = _scannerNodes[scanner];
        StakeThreshold storage stake = _scannerStakeThresholds[node.chainId];
        return _stakeAllocator.allocatedStakePerManaged(SCANNER_POOL_SUBJECT, node.scannerPoolId) >= stake.min;
    }

    /**
     * @notice Checks if sender or meta-tx sender is allowed to set disabled flag for a Scanner Node
     * @param scanner address
     * @return true if _msgSender() is the ScannerPool owning the Scanner or the Scanner Node itself
     */
    function _canSetEnableState(address scanner) internal view virtual returns (bool) {
        return _msgSender() == scanner || ownerOf(_scannerNodes[scanner].scannerPoolId) == _msgSender();
    }

    /**
     * @notice Sets Scanner Node disabled flag to false.
     * It's not possible to re-enable a Scanner Node if allocatedStake / enabled scanners < min.
     * If there is enough unallocated stake, this method will allocate it. If not, it will revert.
     * @param scanner address
     */
    function enableScanner(address scanner) public onlyRegisteredScanner(scanner) {
        if (!_canSetEnableState(scanner)) revert CannotSetScannerActivation();
        _addEnabledScanner(_scannerNodes[scanner].scannerPoolId);
        _allocationOnAddedEnabledScanner(_scannerNodes[scanner].scannerPoolId);
        _setScannerDisableFlag(scanner, false);
    }

    /**
     * @notice Sets Scanner Node disabled flag to true. This will result in the scanner unlinking from assigned bots (process happens off-chain
     * in Assigner software) and not being able to be linked to any bot until re-enabled.
     * @param scanner address
     */
    function disableScanner(address scanner) public onlyRegisteredScanner(scanner) {
        if (!_canSetEnableState(scanner)) revert CannotSetScannerActivation();
        _removeEnabledScanner(_scannerNodes[scanner].scannerPoolId);
        _setScannerDisableFlag(scanner, true);
    }

    function _setScannerDisableFlag(address scanner, bool value) internal {
        _scannerNodes[scanner].disabled = value;
        emit ScannerEnabled(scannerAddressToId(scanner), isScannerOperational(scanner), _msgSender(), value);
    }

    function _addEnabledScanner(uint256 scannerPoolId) private {
        _enabledScanners[scannerPoolId] += 1;
        emit EnabledScannersChanged(scannerPoolId, _enabledScanners[scannerPoolId]);
    }

    function _removeEnabledScanner(uint256 scannerPoolId) private {
        _enabledScanners[scannerPoolId] -= 1;
        emit EnabledScannersChanged(scannerPoolId, _enabledScanners[scannerPoolId]);
    }

    // ************* Scanner Getters *************

    /// Gets ScannerNode struct for address
    function getScanner(address scanner) public view returns (ScannerNode memory) {
        return _scannerNodes[scanner];
    }

    /// Gets ScannerNode data for address
    function getScannerState(address scanner)
        external
        view
        returns (
            bool registered,
            address owner,
            uint256 chainId,
            string memory metadata,
            bool operational,
            bool disabled
        )
    {
        ScannerNode memory scannerNode = getScanner(scanner);
        return (
            scannerNode.registered,
            scannerNode.registered ? ownerOf(scannerNode.scannerPoolId) : address(0),
            scannerNode.chainId,
            scannerNode.metadata,
            isScannerOperational(scanner),
            scannerNode.disabled
        );
    }

    // ************* DelegatedStakeSubjectUpgradeable *************

    /**
     * @notice Sets stake parameters (min, max, activated) for scanners. Restricted to SCANNER_POOL_ADMIN_ROLE
     * @param newStakeThreshold struct with stake parameters.
     * @param chainId scanned chain the thresholds applies to.
     */
    function setManagedStakeThreshold(StakeThreshold calldata newStakeThreshold, uint256 chainId) external onlyRole(SCANNER_POOL_ADMIN_ROLE) {
        if (chainId == 0) revert ZeroAmount("chainId");
        if (newStakeThreshold.max <= newStakeThreshold.min) revert StakeThresholdMaxLessOrEqualMin();
        emit ManagedStakeThresholdChanged(chainId, newStakeThreshold.min, newStakeThreshold.max, newStakeThreshold.activated);
        _scannerStakeThresholds[chainId] = newStakeThreshold;
    }

    /**
     * @notice Getter for StakeThreshold for the scanner with id `subject`
     */
    function getManagedStakeThreshold(uint256 managedId) public view returns (StakeThreshold memory) {
        return _scannerStakeThresholds[_scannerPoolChainId[managedId]];
    }

    /// Total scanners registered to a ScannerPool
    function getTotalManagedSubjects(uint256 subject) public view virtual override returns (uint256) {
        return _enabledScanners[subject];
    }

    // ************* Privilege setters ***************

    /// Sets maximum delay between execution of forta auth in Scan Node CLI and execution of registerScanner() in this contract
    function setRegistrationDelay(uint256 delay) external onlyRole(SCANNER_POOL_ADMIN_ROLE) {
        _setRegistrationDelay(delay);
    }

    function _setRegistrationDelay(uint256 delay) internal {
        if (delay == 0) revert ZeroAmount("delay");
        registrationDelay = delay;
        emit RegistrationDelaySet(delay);
    }

    // ************* Inheritance Overrides *************

    function _beforeTokenTransfer(
        address from,
        address to,
        uint256 tokenId
    ) internal override(ERC721Upgradeable, ERC721EnumerableUpgradeable) {
        super._beforeTokenTransfer(from, to, tokenId);
    }

    function supportsInterface(bytes4 interfaceId) public view override(ERC721Upgradeable, ERC721EnumerableUpgradeable) returns (bool) {
        return super.supportsInterface(interfaceId);
    }

    /**
     * @notice Helper to get either msg msg.sender if not a meta transaction, signer of forwarder metatx if it is.
     * @inheritdoc ForwardedContext
     */
    function _msgSender() internal view virtual override(BaseComponentUpgradeable, ContextUpgradeable) returns (address sender) {
        return super._msgSender();
    }

    /**
     * @notice Helper to get msg.data if not a meta transaction, forwarder data in metatx if it is.
     * @inheritdoc ForwardedContext
     */
    function _msgData() internal view virtual override(BaseComponentUpgradeable, ContextUpgradeable) returns (bytes calldata) {
        return super._msgData();
    }

    /**
     * @notice disambiguation of ownerOf.
     * @inheritdoc ERC721Upgradeable
     */
    function ownerOf(uint256 subject) public view virtual override(IStakeSubject, ERC721Upgradeable) returns (address) {
        return super.ownerOf(subject);
    }

    /**
     *  50
     * - 5 DelegatedStakeSubjectUpgradeable (to match with older registries)
     * - 1 _scannerPoolIdCounter;
     * - 1 _scannerNodes;
     * - 1 _scannerNodeOwnership
     * - 1 _enabledScanners
     * - 1 _scannerStakeThresholds
     * - 1 _scannerPoolChainId
     * - 1 registrationDelay
     * --------------------------
     *  38 __gap
     */
    uint256[38] private __gap;
}

File 29 of 56 : EnumerableSet.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (utils/structs/EnumerableSet.sol)

pragma solidity ^0.8.0;

/**
 * @dev Library for managing
 * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
 * types.
 *
 * Sets have the following properties:
 *
 * - Elements are added, removed, and checked for existence in constant time
 * (O(1)).
 * - Elements are enumerated in O(n). No guarantees are made on the ordering.
 *
 * ```
 * contract Example {
 *     // Add the library methods
 *     using EnumerableSet for EnumerableSet.AddressSet;
 *
 *     // Declare a set state variable
 *     EnumerableSet.AddressSet private mySet;
 * }
 * ```
 *
 * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)
 * and `uint256` (`UintSet`) are supported.
 *
 * [WARNING]
 * ====
 *  Trying to delete such a structure from storage will likely result in data corruption, rendering the structure unusable.
 *  See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info.
 *
 *  In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an array of EnumerableSet.
 * ====
 */
library EnumerableSet {
    // To implement this library for multiple types with as little code
    // repetition as possible, we write it in terms of a generic Set type with
    // bytes32 values.
    // The Set implementation uses private functions, and user-facing
    // implementations (such as AddressSet) are just wrappers around the
    // underlying Set.
    // This means that we can only create new EnumerableSets for types that fit
    // in bytes32.

    struct Set {
        // Storage of set values
        bytes32[] _values;
        // Position of the value in the `values` array, plus 1 because index 0
        // means a value is not in the set.
        mapping(bytes32 => uint256) _indexes;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function _add(Set storage set, bytes32 value) private returns (bool) {
        if (!_contains(set, value)) {
            set._values.push(value);
            // The value is stored at length-1, but we add 1 to all indexes
            // and use 0 as a sentinel value
            set._indexes[value] = set._values.length;
            return true;
        } else {
            return false;
        }
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function _remove(Set storage set, bytes32 value) private returns (bool) {
        // We read and store the value's index to prevent multiple reads from the same storage slot
        uint256 valueIndex = set._indexes[value];

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

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

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

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

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

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

            return true;
        } else {
            return false;
        }
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function _contains(Set storage set, bytes32 value) private view returns (bool) {
        return set._indexes[value] != 0;
    }

    /**
     * @dev Returns the number of values on the set. O(1).
     */
    function _length(Set storage set) private view returns (uint256) {
        return set._values.length;
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function _at(Set storage set, uint256 index) private view returns (bytes32) {
        return set._values[index];
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function _values(Set storage set) private view returns (bytes32[] memory) {
        return set._values;
    }

    // Bytes32Set

    struct Bytes32Set {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
        return _add(set._inner, value);
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {
        return _remove(set._inner, value);
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
        return _contains(set._inner, value);
    }

    /**
     * @dev Returns the number of values in the set. O(1).
     */
    function length(Bytes32Set storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
        return _at(set._inner, index);
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {
        return _values(set._inner);
    }

    // AddressSet

    struct AddressSet {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(AddressSet storage set, address value) internal returns (bool) {
        return _add(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(AddressSet storage set, address value) internal returns (bool) {
        return _remove(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(AddressSet storage set, address value) internal view returns (bool) {
        return _contains(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Returns the number of values in the set. O(1).
     */
    function length(AddressSet storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(AddressSet storage set, uint256 index) internal view returns (address) {
        return address(uint160(uint256(_at(set._inner, index))));
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(AddressSet storage set) internal view returns (address[] memory) {
        bytes32[] memory store = _values(set._inner);
        address[] memory result;

        /// @solidity memory-safe-assembly
        assembly {
            result := store
        }

        return result;
    }

    // UintSet

    struct UintSet {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(UintSet storage set, uint256 value) internal returns (bool) {
        return _add(set._inner, bytes32(value));
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(UintSet storage set, uint256 value) internal returns (bool) {
        return _remove(set._inner, bytes32(value));
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(UintSet storage set, uint256 value) internal view returns (bool) {
        return _contains(set._inner, bytes32(value));
    }

    /**
     * @dev Returns the number of values on the set. O(1).
     */
    function length(UintSet storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(UintSet storage set, uint256 index) internal view returns (uint256) {
        return uint256(_at(set._inner, index));
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(UintSet storage set) internal view returns (uint256[] memory) {
        bytes32[] memory store = _values(set._inner);
        uint256[] memory result;

        /// @solidity memory-safe-assembly
        assembly {
            result := store
        }

        return result;
    }
}

File 30 of 56 : IStakeAllocator.sol
// SPDX-License-Identifier: UNLICENSED
// See Forta Network License: https://github.com/forta-network/forta-contracts/blob/master/LICENSE.md

pragma solidity ^0.8.9;

interface IStakeAllocator {
    function depositAllocation(
        uint256 activeSharesId,
        uint8 subjectType,
        uint256 subject,
        address allocator,
        uint256 stakeAmount,
        uint256 sharesAmount
    ) external;

    function withdrawAllocation(
        uint256 activeSharesId,
        uint8 subjectType,
        uint256 subject,
        address allocator,
        uint256 stakeAmount,
        uint256 sharesAmount
    ) external;

    function allocatedStakeFor(uint8 subjectType, uint256 subject) external view returns (uint256);
    function allocatedStakePerManaged(uint8 subjectType, uint256 subject) external view returns (uint256);
    function unallocatedStakeFor(uint8 subjectType, uint256 subject) external view returns (uint256);

    function allocateOwnStake(uint8 subjectType, uint256 subject, uint256 amount) external;
    function didTransferShares(uint256 sharesId, uint8 subjectType, address from, address to, uint256 sharesAmount) external;
}

File 31 of 56 : DelegatedStakeSubject.sol
// SPDX-License-Identifier: UNLICENSED
// See Forta Network License: https://github.com/forta-network/forta-contracts/blob/master/LICENSE.md

pragma solidity ^0.8.9;

import "../../../errors/GeneralErrors.sol";
import "./IDelegatedStakeSubject.sol";
import "./IStakeSubjectGateway.sol";
import "../SubjectTypeValidator.sol";
import "../../Roles.sol";
import "../../utils/AccessManaged.sol";

abstract contract DelegatedStakeSubjectUpgradeable is AccessManagedUpgradeable, IDelegatedStakeSubject {
    IStakeSubjectGateway private _subjectGateway;

    event SubjectHandlerUpdated(address indexed newHandler);
    error StakedUnderMinimum(uint256 subject);

    /*
     * @dev: For contracts made StakeAwareUpgradeable via upgrade, initializer call is not available.
     * Use setSubjectHandler(subjectGateway) when upgrading instead.
     * @param subjectGateway address.
     */
    function __StakeSubjectUpgradeable_init(address subjectGateway) internal initializer {
        _setSubjectHandler(subjectGateway);
    }

    /// Stake controller setter, restricted to DEFAULT_ADMIN_ROLE
    function setSubjectHandler(address subjectGateway) public onlyRole(DEFAULT_ADMIN_ROLE) {
        _setSubjectHandler(subjectGateway);
    }

    /// Getter for subjectGateway
    function getSubjectHandler() public view returns (IStakeSubjectGateway) {
        return _subjectGateway;
    }

    /// Internal setter for subjectGateway, emits subjectGatewayUpdated
    function _setSubjectHandler(address subjectGateway) private {
        if (subjectGateway == address(0)) revert ZeroAddress("subjectGateway");
        _subjectGateway = IStakeSubjectGateway(subjectGateway);
        emit SubjectHandlerUpdated(subjectGateway);
    }

    /**
     *   5 (Not 50, since it was part of an upgrade of XXXRegistryCore)
     * - 1 _subjectGateway;
     * --------------------------
     *   4 __gap
     */
    uint256[4] private __gap;
}

File 32 of 56 : ERC721Upgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (token/ERC721/ERC721.sol)

pragma solidity ^0.8.0;

import "./IERC721Upgradeable.sol";
import "./IERC721ReceiverUpgradeable.sol";
import "./extensions/IERC721MetadataUpgradeable.sol";
import "../../utils/AddressUpgradeable.sol";
import "../../utils/ContextUpgradeable.sol";
import "../../utils/StringsUpgradeable.sol";
import "../../utils/introspection/ERC165Upgradeable.sol";
import "../../proxy/utils/Initializable.sol";

/**
 * @dev Implementation of https://eips.ethereum.org/EIPS/eip-721[ERC721] Non-Fungible Token Standard, including
 * the Metadata extension, but not including the Enumerable extension, which is available separately as
 * {ERC721Enumerable}.
 */
contract ERC721Upgradeable is Initializable, ContextUpgradeable, ERC165Upgradeable, IERC721Upgradeable, IERC721MetadataUpgradeable {
    using AddressUpgradeable for address;
    using StringsUpgradeable for uint256;

    // Token name
    string private _name;

    // Token symbol
    string private _symbol;

    // Mapping from token ID to owner address
    mapping(uint256 => address) private _owners;

    // Mapping owner address to token count
    mapping(address => uint256) private _balances;

    // Mapping from token ID to approved address
    mapping(uint256 => address) private _tokenApprovals;

    // Mapping from owner to operator approvals
    mapping(address => mapping(address => bool)) private _operatorApprovals;

    /**
     * @dev Initializes the contract by setting a `name` and a `symbol` to the token collection.
     */
    function __ERC721_init(string memory name_, string memory symbol_) internal onlyInitializing {
        __ERC721_init_unchained(name_, symbol_);
    }

    function __ERC721_init_unchained(string memory name_, string memory symbol_) internal onlyInitializing {
        _name = name_;
        _symbol = symbol_;
    }

    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165Upgradeable, IERC165Upgradeable) returns (bool) {
        return
            interfaceId == type(IERC721Upgradeable).interfaceId ||
            interfaceId == type(IERC721MetadataUpgradeable).interfaceId ||
            super.supportsInterface(interfaceId);
    }

    /**
     * @dev See {IERC721-balanceOf}.
     */
    function balanceOf(address owner) public view virtual override returns (uint256) {
        require(owner != address(0), "ERC721: address zero is not a valid owner");
        return _balances[owner];
    }

    /**
     * @dev See {IERC721-ownerOf}.
     */
    function ownerOf(uint256 tokenId) public view virtual override returns (address) {
        address owner = _owners[tokenId];
        require(owner != address(0), "ERC721: invalid token ID");
        return owner;
    }

    /**
     * @dev See {IERC721Metadata-name}.
     */
    function name() public view virtual override returns (string memory) {
        return _name;
    }

    /**
     * @dev See {IERC721Metadata-symbol}.
     */
    function symbol() public view virtual override returns (string memory) {
        return _symbol;
    }

    /**
     * @dev See {IERC721Metadata-tokenURI}.
     */
    function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {
        _requireMinted(tokenId);

        string memory baseURI = _baseURI();
        return bytes(baseURI).length > 0 ? string(abi.encodePacked(baseURI, tokenId.toString())) : "";
    }

    /**
     * @dev Base URI for computing {tokenURI}. If set, the resulting URI for each
     * token will be the concatenation of the `baseURI` and the `tokenId`. Empty
     * by default, can be overridden in child contracts.
     */
    function _baseURI() internal view virtual returns (string memory) {
        return "";
    }

    /**
     * @dev See {IERC721-approve}.
     */
    function approve(address to, uint256 tokenId) public virtual override {
        address owner = ERC721Upgradeable.ownerOf(tokenId);
        require(to != owner, "ERC721: approval to current owner");

        require(
            _msgSender() == owner || isApprovedForAll(owner, _msgSender()),
            "ERC721: approve caller is not token owner nor approved for all"
        );

        _approve(to, tokenId);
    }

    /**
     * @dev See {IERC721-getApproved}.
     */
    function getApproved(uint256 tokenId) public view virtual override returns (address) {
        _requireMinted(tokenId);

        return _tokenApprovals[tokenId];
    }

    /**
     * @dev See {IERC721-setApprovalForAll}.
     */
    function setApprovalForAll(address operator, bool approved) public virtual override {
        _setApprovalForAll(_msgSender(), operator, approved);
    }

    /**
     * @dev See {IERC721-isApprovedForAll}.
     */
    function isApprovedForAll(address owner, address operator) public view virtual override returns (bool) {
        return _operatorApprovals[owner][operator];
    }

    /**
     * @dev See {IERC721-transferFrom}.
     */
    function transferFrom(
        address from,
        address to,
        uint256 tokenId
    ) public virtual override {
        //solhint-disable-next-line max-line-length
        require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: caller is not token owner nor approved");

        _transfer(from, to, tokenId);
    }

    /**
     * @dev See {IERC721-safeTransferFrom}.
     */
    function safeTransferFrom(
        address from,
        address to,
        uint256 tokenId
    ) public virtual override {
        safeTransferFrom(from, to, tokenId, "");
    }

    /**
     * @dev See {IERC721-safeTransferFrom}.
     */
    function safeTransferFrom(
        address from,
        address to,
        uint256 tokenId,
        bytes memory data
    ) public virtual override {
        require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: caller is not token owner nor approved");
        _safeTransfer(from, to, tokenId, data);
    }

    /**
     * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
     * are aware of the ERC721 protocol to prevent tokens from being forever locked.
     *
     * `data` is additional data, it has no specified format and it is sent in call to `to`.
     *
     * This internal function is equivalent to {safeTransferFrom}, and can be used to e.g.
     * implement alternative mechanisms to perform token transfer, such as signature-based.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must exist and be owned by `from`.
     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function _safeTransfer(
        address from,
        address to,
        uint256 tokenId,
        bytes memory data
    ) internal virtual {
        _transfer(from, to, tokenId);
        require(_checkOnERC721Received(from, to, tokenId, data), "ERC721: transfer to non ERC721Receiver implementer");
    }

    /**
     * @dev Returns whether `tokenId` exists.
     *
     * Tokens can be managed by their owner or approved accounts via {approve} or {setApprovalForAll}.
     *
     * Tokens start existing when they are minted (`_mint`),
     * and stop existing when they are burned (`_burn`).
     */
    function _exists(uint256 tokenId) internal view virtual returns (bool) {
        return _owners[tokenId] != address(0);
    }

    /**
     * @dev Returns whether `spender` is allowed to manage `tokenId`.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     */
    function _isApprovedOrOwner(address spender, uint256 tokenId) internal view virtual returns (bool) {
        address owner = ERC721Upgradeable.ownerOf(tokenId);
        return (spender == owner || isApprovedForAll(owner, spender) || getApproved(tokenId) == spender);
    }

    /**
     * @dev Safely mints `tokenId` and transfers it to `to`.
     *
     * Requirements:
     *
     * - `tokenId` must not exist.
     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function _safeMint(address to, uint256 tokenId) internal virtual {
        _safeMint(to, tokenId, "");
    }

    /**
     * @dev Same as {xref-ERC721-_safeMint-address-uint256-}[`_safeMint`], with an additional `data` parameter which is
     * forwarded in {IERC721Receiver-onERC721Received} to contract recipients.
     */
    function _safeMint(
        address to,
        uint256 tokenId,
        bytes memory data
    ) internal virtual {
        _mint(to, tokenId);
        require(
            _checkOnERC721Received(address(0), to, tokenId, data),
            "ERC721: transfer to non ERC721Receiver implementer"
        );
    }

    /**
     * @dev Mints `tokenId` and transfers it to `to`.
     *
     * WARNING: Usage of this method is discouraged, use {_safeMint} whenever possible
     *
     * Requirements:
     *
     * - `tokenId` must not exist.
     * - `to` cannot be the zero address.
     *
     * Emits a {Transfer} event.
     */
    function _mint(address to, uint256 tokenId) internal virtual {
        require(to != address(0), "ERC721: mint to the zero address");
        require(!_exists(tokenId), "ERC721: token already minted");

        _beforeTokenTransfer(address(0), to, tokenId);

        _balances[to] += 1;
        _owners[tokenId] = to;

        emit Transfer(address(0), to, tokenId);

        _afterTokenTransfer(address(0), to, tokenId);
    }

    /**
     * @dev Destroys `tokenId`.
     * The approval is cleared when the token is burned.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     *
     * Emits a {Transfer} event.
     */
    function _burn(uint256 tokenId) internal virtual {
        address owner = ERC721Upgradeable.ownerOf(tokenId);

        _beforeTokenTransfer(owner, address(0), tokenId);

        // Clear approvals
        _approve(address(0), tokenId);

        _balances[owner] -= 1;
        delete _owners[tokenId];

        emit Transfer(owner, address(0), tokenId);

        _afterTokenTransfer(owner, address(0), tokenId);
    }

    /**
     * @dev Transfers `tokenId` from `from` to `to`.
     *  As opposed to {transferFrom}, this imposes no restrictions on msg.sender.
     *
     * Requirements:
     *
     * - `to` cannot be the zero address.
     * - `tokenId` token must be owned by `from`.
     *
     * Emits a {Transfer} event.
     */
    function _transfer(
        address from,
        address to,
        uint256 tokenId
    ) internal virtual {
        require(ERC721Upgradeable.ownerOf(tokenId) == from, "ERC721: transfer from incorrect owner");
        require(to != address(0), "ERC721: transfer to the zero address");

        _beforeTokenTransfer(from, to, tokenId);

        // Clear approvals from the previous owner
        _approve(address(0), tokenId);

        _balances[from] -= 1;
        _balances[to] += 1;
        _owners[tokenId] = to;

        emit Transfer(from, to, tokenId);

        _afterTokenTransfer(from, to, tokenId);
    }

    /**
     * @dev Approve `to` to operate on `tokenId`
     *
     * Emits an {Approval} event.
     */
    function _approve(address to, uint256 tokenId) internal virtual {
        _tokenApprovals[tokenId] = to;
        emit Approval(ERC721Upgradeable.ownerOf(tokenId), to, tokenId);
    }

    /**
     * @dev Approve `operator` to operate on all of `owner` tokens
     *
     * Emits an {ApprovalForAll} event.
     */
    function _setApprovalForAll(
        address owner,
        address operator,
        bool approved
    ) internal virtual {
        require(owner != operator, "ERC721: approve to caller");
        _operatorApprovals[owner][operator] = approved;
        emit ApprovalForAll(owner, operator, approved);
    }

    /**
     * @dev Reverts if the `tokenId` has not been minted yet.
     */
    function _requireMinted(uint256 tokenId) internal view virtual {
        require(_exists(tokenId), "ERC721: invalid token ID");
    }

    /**
     * @dev Internal function to invoke {IERC721Receiver-onERC721Received} on a target address.
     * The call is not executed if the target address is not a contract.
     *
     * @param from address representing the previous owner of the given token ID
     * @param to target address that will receive the tokens
     * @param tokenId uint256 ID of the token to be transferred
     * @param data bytes optional data to send along with the call
     * @return bool whether the call correctly returned the expected magic value
     */
    function _checkOnERC721Received(
        address from,
        address to,
        uint256 tokenId,
        bytes memory data
    ) private returns (bool) {
        if (to.isContract()) {
            try IERC721ReceiverUpgradeable(to).onERC721Received(_msgSender(), from, tokenId, data) returns (bytes4 retval) {
                return retval == IERC721ReceiverUpgradeable.onERC721Received.selector;
            } catch (bytes memory reason) {
                if (reason.length == 0) {
                    revert("ERC721: transfer to non ERC721Receiver implementer");
                } else {
                    /// @solidity memory-safe-assembly
                    assembly {
                        revert(add(32, reason), mload(reason))
                    }
                }
            }
        } else {
            return true;
        }
    }

    /**
     * @dev Hook that is called before any token transfer. This includes minting
     * and burning.
     *
     * Calling conditions:
     *
     * - When `from` and `to` are both non-zero, ``from``'s `tokenId` will be
     * transferred to `to`.
     * - When `from` is zero, `tokenId` will be minted for `to`.
     * - When `to` is zero, ``from``'s `tokenId` will be burned.
     * - `from` and `to` are never both zero.
     *
     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     */
    function _beforeTokenTransfer(
        address from,
        address to,
        uint256 tokenId
    ) internal virtual {}

    /**
     * @dev Hook that is called after any transfer of tokens. This includes
     * minting and burning.
     *
     * Calling conditions:
     *
     * - when `from` and `to` are both non-zero.
     * - `from` and `to` are never both zero.
     *
     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     */
    function _afterTokenTransfer(
        address from,
        address to,
        uint256 tokenId
    ) internal virtual {}

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

File 33 of 56 : CountersUpgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Counters.sol)

pragma solidity ^0.8.0;

/**
 * @title Counters
 * @author Matt Condon (@shrugs)
 * @dev Provides counters that can only be incremented, decremented or reset. This can be used e.g. to track the number
 * of elements in a mapping, issuing ERC721 ids, or counting request ids.
 *
 * Include with `using Counters for Counters.Counter;`
 */
library CountersUpgradeable {
    struct Counter {
        // This variable should never be directly accessed by users of the library: interactions must be restricted to
        // the library's function. As of Solidity v0.5.2, this cannot be enforced, though there is a proposal to add
        // this feature: see https://github.com/ethereum/solidity/issues/4637
        uint256 _value; // default: 0
    }

    function current(Counter storage counter) internal view returns (uint256) {
        return counter._value;
    }

    function increment(Counter storage counter) internal {
        unchecked {
            counter._value += 1;
        }
    }

    function decrement(Counter storage counter) internal {
        uint256 value = counter._value;
        require(value > 0, "Counter: decrement overflow");
        unchecked {
            counter._value = value - 1;
        }
    }

    function reset(Counter storage counter) internal {
        counter._value = 0;
    }
}

File 34 of 56 : draft-EIP712Upgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/cryptography/draft-EIP712.sol)

pragma solidity ^0.8.0;

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

/**
 * @dev https://eips.ethereum.org/EIPS/eip-712[EIP 712] is a standard for hashing and signing of typed structured data.
 *
 * The encoding specified in the EIP is very generic, and such a generic implementation in Solidity is not feasible,
 * thus this contract does not implement the encoding itself. Protocols need to implement the type-specific encoding
 * they need in their contracts using a combination of `abi.encode` and `keccak256`.
 *
 * This contract implements the EIP 712 domain separator ({_domainSeparatorV4}) that is used as part of the encoding
 * scheme, and the final step of the encoding to obtain the message digest that is then signed via ECDSA
 * ({_hashTypedDataV4}).
 *
 * The implementation of the domain separator was designed to be as efficient as possible while still properly updating
 * the chain id to protect against replay attacks on an eventual fork of the chain.
 *
 * NOTE: This contract implements the version of the encoding known as "v4", as implemented by the JSON RPC method
 * https://docs.metamask.io/guide/signing-data.html[`eth_signTypedDataV4` in MetaMask].
 *
 * _Available since v3.4._
 *
 * @custom:storage-size 52
 */
abstract contract EIP712Upgradeable is Initializable {
    /* solhint-disable var-name-mixedcase */
    bytes32 private _HASHED_NAME;
    bytes32 private _HASHED_VERSION;
    bytes32 private constant _TYPE_HASH = keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)");

    /* solhint-enable var-name-mixedcase */

    /**
     * @dev Initializes the domain separator and parameter caches.
     *
     * The meaning of `name` and `version` is specified in
     * https://eips.ethereum.org/EIPS/eip-712#definition-of-domainseparator[EIP 712]:
     *
     * - `name`: the user readable name of the signing domain, i.e. the name of the DApp or the protocol.
     * - `version`: the current major version of the signing domain.
     *
     * NOTE: These parameters cannot be changed except through a xref:learn::upgrading-smart-contracts.adoc[smart
     * contract upgrade].
     */
    function __EIP712_init(string memory name, string memory version) internal onlyInitializing {
        __EIP712_init_unchained(name, version);
    }

    function __EIP712_init_unchained(string memory name, string memory version) internal onlyInitializing {
        bytes32 hashedName = keccak256(bytes(name));
        bytes32 hashedVersion = keccak256(bytes(version));
        _HASHED_NAME = hashedName;
        _HASHED_VERSION = hashedVersion;
    }

    /**
     * @dev Returns the domain separator for the current chain.
     */
    function _domainSeparatorV4() internal view returns (bytes32) {
        return _buildDomainSeparator(_TYPE_HASH, _EIP712NameHash(), _EIP712VersionHash());
    }

    function _buildDomainSeparator(
        bytes32 typeHash,
        bytes32 nameHash,
        bytes32 versionHash
    ) private view returns (bytes32) {
        return keccak256(abi.encode(typeHash, nameHash, versionHash, block.chainid, address(this)));
    }

    /**
     * @dev Given an already https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstruct[hashed struct], this
     * function returns the hash of the fully encoded EIP712 message for this domain.
     *
     * This hash can be used together with {ECDSA-recover} to obtain the signer of a message. For example:
     *
     * ```solidity
     * bytes32 digest = _hashTypedDataV4(keccak256(abi.encode(
     *     keccak256("Mail(address to,string contents)"),
     *     mailTo,
     *     keccak256(bytes(mailContents))
     * )));
     * address signer = ECDSA.recover(digest, signature);
     * ```
     */
    function _hashTypedDataV4(bytes32 structHash) internal view virtual returns (bytes32) {
        return ECDSAUpgradeable.toTypedDataHash(_domainSeparatorV4(), structHash);
    }

    /**
     * @dev The hash of the name parameter for the EIP712 domain.
     *
     * NOTE: This function reads from storage by default, but can be redefined to return a constant value if gas costs
     * are a concern.
     */
    function _EIP712NameHash() internal virtual view returns (bytes32) {
        return _HASHED_NAME;
    }

    /**
     * @dev The hash of the version parameter for the EIP712 domain.
     *
     * NOTE: This function reads from storage by default, but can be redefined to return a constant value if gas costs
     * are a concern.
     */
    function _EIP712VersionHash() internal virtual view returns (bytes32) {
        return _HASHED_VERSION;
    }

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

File 35 of 56 : Math.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (utils/math/Math.sol)

pragma solidity ^0.8.0;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

        // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
        // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
        // `msb(a) <= a < 2*msb(a)`.
        // We also know that `k`, the position of the most significant bit, is such that `msb(a) = 2**k`.
        // This gives `2**k < a <= 2**(k+1)` → `2**(k/2) <= sqrt(a) < 2 ** (k/2+1)`.
        // Using an algorithm similar to the msb conmputation, we are able to compute `result = 2**(k/2)` which is a
        // good first aproximation of `sqrt(a)` with at least 1 correct bit.
        uint256 result = 1;
        uint256 x = a;
        if (x >> 128 > 0) {
            x >>= 128;
            result <<= 64;
        }
        if (x >> 64 > 0) {
            x >>= 64;
            result <<= 32;
        }
        if (x >> 32 > 0) {
            x >>= 32;
            result <<= 16;
        }
        if (x >> 16 > 0) {
            x >>= 16;
            result <<= 8;
        }
        if (x >> 8 > 0) {
            x >>= 8;
            result <<= 4;
        }
        if (x >> 4 > 0) {
            x >>= 4;
            result <<= 2;
        }
        if (x >> 2 > 0) {
            result <<= 1;
        }

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

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

File 36 of 56 : SignatureCheckerUpgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (utils/cryptography/SignatureChecker.sol)

pragma solidity ^0.8.0;

import "./ECDSAUpgradeable.sol";
import "../AddressUpgradeable.sol";
import "../../interfaces/IERC1271Upgradeable.sol";

/**
 * @dev Signature verification helper that can be used instead of `ECDSA.recover` to seamlessly support both ECDSA
 * signatures from externally owned accounts (EOAs) as well as ERC1271 signatures from smart contract wallets like
 * Argent and Gnosis Safe.
 *
 * _Available since v4.1._
 */
library SignatureCheckerUpgradeable {
    /**
     * @dev Checks if a signature is valid for a given signer and data hash. If the signer is a smart contract, the
     * signature is validated against that smart contract using ERC1271, otherwise it's validated using `ECDSA.recover`.
     *
     * NOTE: Unlike ECDSA signatures, contract signatures are revocable, and the outcome of this function can thus
     * change through time. It could return true at block N and false at block N+1 (or the opposite).
     */
    function isValidSignatureNow(
        address signer,
        bytes32 hash,
        bytes memory signature
    ) internal view returns (bool) {
        (address recovered, ECDSAUpgradeable.RecoverError error) = ECDSAUpgradeable.tryRecover(hash, signature);
        if (error == ECDSAUpgradeable.RecoverError.NoError && recovered == signer) {
            return true;
        }

        (bool success, bytes memory result) = signer.staticcall(
            abi.encodeWithSelector(IERC1271Upgradeable.isValidSignature.selector, hash, signature)
        );
        return (success && result.length == 32 && abi.decode(result, (bytes4)) == IERC1271Upgradeable.isValidSignature.selector);
    }
}

File 37 of 56 : ERC721EnumerableUpgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC721/extensions/ERC721Enumerable.sol)

pragma solidity ^0.8.0;

import "../ERC721Upgradeable.sol";
import "./IERC721EnumerableUpgradeable.sol";
import "../../../proxy/utils/Initializable.sol";

/**
 * @dev This implements an optional extension of {ERC721} defined in the EIP that adds
 * enumerability of all the token ids in the contract as well as all token ids owned by each
 * account.
 */
abstract contract ERC721EnumerableUpgradeable is Initializable, ERC721Upgradeable, IERC721EnumerableUpgradeable {
    function __ERC721Enumerable_init() internal onlyInitializing {
    }

    function __ERC721Enumerable_init_unchained() internal onlyInitializing {
    }
    // Mapping from owner to list of owned token IDs
    mapping(address => mapping(uint256 => uint256)) private _ownedTokens;

    // Mapping from token ID to index of the owner tokens list
    mapping(uint256 => uint256) private _ownedTokensIndex;

    // Array with all token ids, used for enumeration
    uint256[] private _allTokens;

    // Mapping from token id to position in the allTokens array
    mapping(uint256 => uint256) private _allTokensIndex;

    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override(IERC165Upgradeable, ERC721Upgradeable) returns (bool) {
        return interfaceId == type(IERC721EnumerableUpgradeable).interfaceId || super.supportsInterface(interfaceId);
    }

    /**
     * @dev See {IERC721Enumerable-tokenOfOwnerByIndex}.
     */
    function tokenOfOwnerByIndex(address owner, uint256 index) public view virtual override returns (uint256) {
        require(index < ERC721Upgradeable.balanceOf(owner), "ERC721Enumerable: owner index out of bounds");
        return _ownedTokens[owner][index];
    }

    /**
     * @dev See {IERC721Enumerable-totalSupply}.
     */
    function totalSupply() public view virtual override returns (uint256) {
        return _allTokens.length;
    }

    /**
     * @dev See {IERC721Enumerable-tokenByIndex}.
     */
    function tokenByIndex(uint256 index) public view virtual override returns (uint256) {
        require(index < ERC721EnumerableUpgradeable.totalSupply(), "ERC721Enumerable: global index out of bounds");
        return _allTokens[index];
    }

    /**
     * @dev Hook that is called before any token transfer. This includes minting
     * and burning.
     *
     * Calling conditions:
     *
     * - When `from` and `to` are both non-zero, ``from``'s `tokenId` will be
     * transferred to `to`.
     * - When `from` is zero, `tokenId` will be minted for `to`.
     * - When `to` is zero, ``from``'s `tokenId` will be burned.
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     *
     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     */
    function _beforeTokenTransfer(
        address from,
        address to,
        uint256 tokenId
    ) internal virtual override {
        super._beforeTokenTransfer(from, to, tokenId);

        if (from == address(0)) {
            _addTokenToAllTokensEnumeration(tokenId);
        } else if (from != to) {
            _removeTokenFromOwnerEnumeration(from, tokenId);
        }
        if (to == address(0)) {
            _removeTokenFromAllTokensEnumeration(tokenId);
        } else if (to != from) {
            _addTokenToOwnerEnumeration(to, tokenId);
        }
    }

    /**
     * @dev Private function to add a token to this extension's ownership-tracking data structures.
     * @param to address representing the new owner of the given token ID
     * @param tokenId uint256 ID of the token to be added to the tokens list of the given address
     */
    function _addTokenToOwnerEnumeration(address to, uint256 tokenId) private {
        uint256 length = ERC721Upgradeable.balanceOf(to);
        _ownedTokens[to][length] = tokenId;
        _ownedTokensIndex[tokenId] = length;
    }

    /**
     * @dev Private function to add a token to this extension's token tracking data structures.
     * @param tokenId uint256 ID of the token to be added to the tokens list
     */
    function _addTokenToAllTokensEnumeration(uint256 tokenId) private {
        _allTokensIndex[tokenId] = _allTokens.length;
        _allTokens.push(tokenId);
    }

    /**
     * @dev Private function to remove a token from this extension's ownership-tracking data structures. Note that
     * while the token is not assigned a new owner, the `_ownedTokensIndex` mapping is _not_ updated: this allows for
     * gas optimizations e.g. when performing a transfer operation (avoiding double writes).
     * This has O(1) time complexity, but alters the order of the _ownedTokens array.
     * @param from address representing the previous owner of the given token ID
     * @param tokenId uint256 ID of the token to be removed from the tokens list of the given address
     */
    function _removeTokenFromOwnerEnumeration(address from, uint256 tokenId) private {
        // To prevent a gap in from's tokens array, we store the last token in the index of the token to delete, and
        // then delete the last slot (swap and pop).

        uint256 lastTokenIndex = ERC721Upgradeable.balanceOf(from) - 1;
        uint256 tokenIndex = _ownedTokensIndex[tokenId];

        // When the token to delete is the last token, the swap operation is unnecessary
        if (tokenIndex != lastTokenIndex) {
            uint256 lastTokenId = _ownedTokens[from][lastTokenIndex];

            _ownedTokens[from][tokenIndex] = lastTokenId; // Move the last token to the slot of the to-delete token
            _ownedTokensIndex[lastTokenId] = tokenIndex; // Update the moved token's index
        }

        // This also deletes the contents at the last position of the array
        delete _ownedTokensIndex[tokenId];
        delete _ownedTokens[from][lastTokenIndex];
    }

    /**
     * @dev Private function to remove a token from this extension's token tracking data structures.
     * This has O(1) time complexity, but alters the order of the _allTokens array.
     * @param tokenId uint256 ID of the token to be removed from the tokens list
     */
    function _removeTokenFromAllTokensEnumeration(uint256 tokenId) private {
        // To prevent a gap in the tokens array, we store the last token in the index of the token to delete, and
        // then delete the last slot (swap and pop).

        uint256 lastTokenIndex = _allTokens.length - 1;
        uint256 tokenIndex = _allTokensIndex[tokenId];

        // When the token to delete is the last token, the swap operation is unnecessary. However, since this occurs so
        // rarely (when the last minted token is burnt) that we still do the swap here to avoid the gas cost of adding
        // an 'if' statement (like in _removeTokenFromOwnerEnumeration)
        uint256 lastTokenId = _allTokens[lastTokenIndex];

        _allTokens[tokenIndex] = lastTokenId; // Move the last token to the slot of the to-delete token
        _allTokensIndex[lastTokenId] = tokenIndex; // Update the moved token's index

        // This also deletes the contents at the last position of the array
        delete _allTokensIndex[tokenId];
        _allTokens.pop();
    }

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

File 38 of 56 : IStakeSubjectGateway.sol
// SPDX-License-Identifier: UNLICENSED
// See Forta Network License: https://github.com/forta-network/forta-contracts/blob/master/LICENSE.md

pragma solidity ^0.8.9;

import "./IStakeSubject.sol";

interface IStakeSubjectGateway {
    event StakeSubjectChanged(address newHandler, address oldHandler);
    function setStakeSubject(uint8 subjectType, address subject) external;
    function getStakeSubject(uint8 subjectType) external view returns (address);
    function activeStakeFor(uint8 subjectType, uint256 subject) external view returns (uint256);
    function maxStakeFor(uint8 subjectType, uint256 subject) external view returns (uint256);
    function minStakeFor(uint8 subjectType, uint256 subject) external view returns (uint256);
    function totalStakeFor(uint8 subjectType, uint256 subject) external view returns (uint256);
    function isStakeActivatedFor(uint8 subjectType, uint256 subject) external view returns (bool);
    function maxManagedStakeFor(uint8 subjectType, uint256 subject) external view returns (uint256);
    function minManagedStakeFor(uint8 subjectType, uint256 subject) external view returns (uint256);
    function totalManagedSubjects(uint8 subjectType, uint256 subject) external view returns (uint256);
    function canManageAllocation(uint8 subjectType, uint256 subject, address allocator) external view returns (bool);
    function ownerOf(uint8 subjectType, uint256 subject) external view returns (address);
}

File 39 of 56 : SubjectTypeValidator.sol
// SPDX-License-Identifier: UNLICENSED
// See Forta Network License: https://github.com/forta-network/forta-contracts/blob/master/LICENSE.md

pragma solidity ^0.8.9;

uint8 constant UNDEFINED_SUBJECT = 255;
uint8 constant SCANNER_SUBJECT = 0;
uint8 constant AGENT_SUBJECT = 1;
uint8 constant SCANNER_POOL_SUBJECT = 2;
uint8 constant DELEGATOR_SCANNER_POOL_SUBJECT = 3;

/**
 * Defines the types of staking Subject Types, their agency and relationships.
 * There are different types of subject type agency:
 * - MANAGED --> Cannot be staked on directly, allocation of stake is controlled by their manager, a DELEGATED type
 * - DIRECT --> Can be staked on by multiple different stakers
 * - DELEGATED --> Can be staked on by the owner of the relevant Registry entry. Manages MANAGED subjects.
 * - DELEGATOR --> TBD
 *
 * The current Subject Types and their Agency:
 * - SCANNER_SUBJECT --> MANAGED
 * - AGENT_SUBJECT (detection bots) --> DIRECT
 * - SCANNER_POOL_SUBJECT --> DELEGATED
 *
 */
contract SubjectTypeValidator {
    enum SubjectStakeAgency {
        UNDEFINED,
        DIRECT,
        DELEGATED,
        DELEGATOR,
        MANAGED
    }

    error InvalidSubjectType(uint8 subjectType);
    error ForbiddenForType(uint8 subjectType, SubjectStakeAgency provided, SubjectStakeAgency expected);

    /**
     * @dev check if `subjectType` belongs to the defined SUBJECT_TYPES
     * @param subjectType is not an enum because some contracts using subjectTypes are not
     * upgradeable (StakingEscrow)
     */
    modifier onlyValidSubjectType(uint8 subjectType) {
        if (subjectType != SCANNER_SUBJECT && subjectType != AGENT_SUBJECT && subjectType != SCANNER_POOL_SUBJECT && subjectType != DELEGATOR_SCANNER_POOL_SUBJECT)
            revert InvalidSubjectType(subjectType);
        _;
    }

    modifier onlyAgencyType(uint8 subjectType, SubjectStakeAgency expected) {
        if (getSubjectTypeAgency(subjectType) != expected) revert ForbiddenForType(subjectType, getSubjectTypeAgency(subjectType), expected);
        _;
    }

    modifier notAgencyType(uint8 subjectType, SubjectStakeAgency forbidden) {
        if (getSubjectTypeAgency(subjectType) == forbidden) revert ForbiddenForType(subjectType, getSubjectTypeAgency(subjectType), forbidden);
        _;
    }

    function getSubjectTypeAgency(uint8 subjectType) public pure returns (SubjectStakeAgency) {
        if (subjectType == AGENT_SUBJECT) {
            return SubjectStakeAgency.DIRECT;
        } else if (subjectType == SCANNER_POOL_SUBJECT) {
            return SubjectStakeAgency.DELEGATED;
        } else if (subjectType == DELEGATOR_SCANNER_POOL_SUBJECT) {
            return SubjectStakeAgency.DELEGATOR;
        } else if (subjectType == SCANNER_SUBJECT) {
            return SubjectStakeAgency.MANAGED;
        }
        return SubjectStakeAgency.UNDEFINED;
    }

    function getDelegatorSubjectType(uint8 subjectType) public pure returns (uint8) {
        if (subjectType == SCANNER_POOL_SUBJECT) {
            return DELEGATOR_SCANNER_POOL_SUBJECT;
        }
        return UNDEFINED_SUBJECT;
    }

    function getDelegatedSubjectType(uint8 subjectType) public pure returns (uint8) {
        if (subjectType == DELEGATOR_SCANNER_POOL_SUBJECT) {
            return SCANNER_POOL_SUBJECT;
        }
        return UNDEFINED_SUBJECT;
    }
}

File 40 of 56 : IDelegatedStakeSubject.sol
// SPDX-License-Identifier: UNLICENSED
// See Forta Network License: https://github.com/forta-network/forta-contracts/blob/master/LICENSE.md

pragma solidity ^0.8.9;

import "./IStakeSubject.sol";

interface IDelegatedStakeSubject is IStakeSubject {
    function getTotalManagedSubjects(uint256 managerId) external view returns(uint256);
    function getManagedStakeThreshold(uint256 managedId) external view returns(StakeThreshold memory);
}

File 41 of 56 : IStakeSubject.sol
// SPDX-License-Identifier: UNLICENSED
// See Forta Network License: https://github.com/forta-network/forta-contracts/blob/master/LICENSE.md

pragma solidity ^0.8.9;

interface IStakeSubject {
    struct StakeThreshold {
        uint256 min;
        uint256 max;
        bool activated;
    }
    error StakeThresholdMaxLessOrEqualMin();
    
    function isRegistered(uint256 subject) external view returns(bool);
    function ownerOf(uint256 subject) external view returns (address);
}

File 42 of 56 : StringsUpgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (utils/Strings.sol)

pragma solidity ^0.8.0;

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

    /**
     * @dev Converts a `uint256` to its ASCII `string` decimal representation.
     */
    function toString(uint256 value) internal pure returns (string memory) {
        // Inspired by OraclizeAPI's implementation - MIT licence
        // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol

        if (value == 0) {
            return "0";
        }
        uint256 temp = value;
        uint256 digits;
        while (temp != 0) {
            digits++;
            temp /= 10;
        }
        bytes memory buffer = new bytes(digits);
        while (value != 0) {
            digits -= 1;
            buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));
            value /= 10;
        }
        return string(buffer);
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
     */
    function toHexString(uint256 value) internal pure returns (string memory) {
        if (value == 0) {
            return "0x00";
        }
        uint256 temp = value;
        uint256 length = 0;
        while (temp != 0) {
            length++;
            temp >>= 8;
        }
        return toHexString(value, length);
    }

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

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

File 43 of 56 : IERC721ReceiverUpgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC721/IERC721Receiver.sol)

pragma solidity ^0.8.0;

/**
 * @title ERC721 token receiver interface
 * @dev Interface for any contract that wants to support safeTransfers
 * from ERC721 asset contracts.
 */
interface IERC721ReceiverUpgradeable {
    /**
     * @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom}
     * by `operator` from `from`, this function is called.
     *
     * It must return its Solidity selector to confirm the token transfer.
     * If any other value is returned or the interface is not implemented by the recipient, the transfer will be reverted.
     *
     * The selector can be obtained in Solidity with `IERC721Receiver.onERC721Received.selector`.
     */
    function onERC721Received(
        address operator,
        address from,
        uint256 tokenId,
        bytes calldata data
    ) external returns (bytes4);
}

File 44 of 56 : ERC165Upgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)

pragma solidity ^0.8.0;

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

/**
 * @dev Implementation of the {IERC165} interface.
 *
 * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
 * for the additional interface id that will be supported. For example:
 *
 * ```solidity
 * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
 *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
 * }
 * ```
 *
 * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
 */
abstract contract ERC165Upgradeable is Initializable, IERC165Upgradeable {
    function __ERC165_init() internal onlyInitializing {
    }

    function __ERC165_init_unchained() internal onlyInitializing {
    }
    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
        return interfaceId == type(IERC165Upgradeable).interfaceId;
    }

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

File 45 of 56 : IERC721Upgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (token/ERC721/IERC721.sol)

pragma solidity ^0.8.0;

import "../../utils/introspection/IERC165Upgradeable.sol";

/**
 * @dev Required interface of an ERC721 compliant contract.
 */
interface IERC721Upgradeable is IERC165Upgradeable {
    /**
     * @dev Emitted when `tokenId` token is transferred from `from` to `to`.
     */
    event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);

    /**
     * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.
     */
    event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);

    /**
     * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.
     */
    event ApprovalForAll(address indexed owner, address indexed operator, bool approved);

    /**
     * @dev Returns the number of tokens in ``owner``'s account.
     */
    function balanceOf(address owner) external view returns (uint256 balance);

    /**
     * @dev Returns the owner of the `tokenId` token.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     */
    function ownerOf(uint256 tokenId) external view returns (address owner);

    /**
     * @dev Safely transfers `tokenId` token from `from` to `to`.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must exist and be owned by `from`.
     * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function safeTransferFrom(
        address from,
        address to,
        uint256 tokenId,
        bytes calldata data
    ) external;

    /**
     * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
     * are aware of the ERC721 protocol to prevent tokens from being forever locked.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must exist and be owned by `from`.
     * - If the caller is not `from`, it must have been allowed to move this token by either {approve} or {setApprovalForAll}.
     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function safeTransferFrom(
        address from,
        address to,
        uint256 tokenId
    ) external;

    /**
     * @dev Transfers `tokenId` token from `from` to `to`.
     *
     * WARNING: Usage of this method is discouraged, use {safeTransferFrom} whenever possible.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must be owned by `from`.
     * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(
        address from,
        address to,
        uint256 tokenId
    ) external;

    /**
     * @dev Gives permission to `to` to transfer `tokenId` token to another account.
     * The approval is cleared when the token is transferred.
     *
     * Only a single account can be approved at a time, so approving the zero address clears previous approvals.
     *
     * Requirements:
     *
     * - The caller must own the token or be an approved operator.
     * - `tokenId` must exist.
     *
     * Emits an {Approval} event.
     */
    function approve(address to, uint256 tokenId) external;

    /**
     * @dev Approve or remove `operator` as an operator for the caller.
     * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.
     *
     * Requirements:
     *
     * - The `operator` cannot be the caller.
     *
     * Emits an {ApprovalForAll} event.
     */
    function setApprovalForAll(address operator, bool _approved) external;

    /**
     * @dev Returns the account approved for `tokenId` token.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     */
    function getApproved(uint256 tokenId) external view returns (address operator);

    /**
     * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
     *
     * See {setApprovalForAll}
     */
    function isApprovedForAll(address owner, address operator) external view returns (bool);
}

File 46 of 56 : IERC721MetadataUpgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC721/extensions/IERC721Metadata.sol)

pragma solidity ^0.8.0;

import "../IERC721Upgradeable.sol";

/**
 * @title ERC-721 Non-Fungible Token Standard, optional metadata extension
 * @dev See https://eips.ethereum.org/EIPS/eip-721
 */
interface IERC721MetadataUpgradeable is IERC721Upgradeable {
    /**
     * @dev Returns the token collection name.
     */
    function name() external view returns (string memory);

    /**
     * @dev Returns the token collection symbol.
     */
    function symbol() external view returns (string memory);

    /**
     * @dev Returns the Uniform Resource Identifier (URI) for `tokenId` token.
     */
    function tokenURI(uint256 tokenId) external view returns (string memory);
}

File 47 of 56 : ECDSAUpgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (utils/cryptography/ECDSA.sol)

pragma solidity ^0.8.0;

import "../StringsUpgradeable.sol";

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

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

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

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

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

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

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

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

        return (signer, RecoverError.NoError);
    }

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

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

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

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

File 48 of 56 : IERC1271Upgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (interfaces/IERC1271.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC1271 standard signature validation method for
 * contracts as defined in https://eips.ethereum.org/EIPS/eip-1271[ERC-1271].
 *
 * _Available since v4.1._
 */
interface IERC1271Upgradeable {
    /**
     * @dev Should return whether the signature provided is valid for the provided data
     * @param hash      Hash of the data to be signed
     * @param signature Signature byte array associated with _data
     */
    function isValidSignature(bytes32 hash, bytes memory signature) external view returns (bytes4 magicValue);
}

File 49 of 56 : IERC721EnumerableUpgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC721/extensions/IERC721Enumerable.sol)

pragma solidity ^0.8.0;

import "../IERC721Upgradeable.sol";

/**
 * @title ERC-721 Non-Fungible Token Standard, optional enumeration extension
 * @dev See https://eips.ethereum.org/EIPS/eip-721
 */
interface IERC721EnumerableUpgradeable is IERC721Upgradeable {
    /**
     * @dev Returns the total amount of tokens stored by the contract.
     */
    function totalSupply() external view returns (uint256);

    /**
     * @dev Returns a token ID owned by `owner` at a given `index` of its token list.
     * Use along with {balanceOf} to enumerate all of ``owner``'s tokens.
     */
    function tokenOfOwnerByIndex(address owner, uint256 index) external view returns (uint256);

    /**
     * @dev Returns a token ID at a given `index` of all the tokens stored by the contract.
     * Use along with {totalSupply} to enumerate all tokens.
     */
    function tokenByIndex(uint256 index) external view returns (uint256);
}

File 50 of 56 : ScannerRegistryCore.sol
// SPDX-License-Identifier: UNLICENSED
// See Forta Network License: https://github.com/forta-network/forta-contracts/blob/master/LICENSE.md

pragma solidity ^0.8.9;

import "@openzeppelin/contracts-upgradeable/token/ERC721/ERC721Upgradeable.sol";

import "../BaseComponentUpgradeable.sol";
import "../staking/stake_subjects/DirectStakeSubject.sol";
import "../../errors/GeneralErrors.sol";
import "../scanner_pools/ScannerPoolRegistry.sol";

abstract contract ScannerRegistryCore is
    BaseComponentUpgradeable,
    ERC721Upgradeable,
    DirectStakeSubjectUpgradeable
{
    mapping(uint256 => StakeThreshold) internal _stakeThresholds;
    
    event ScannerUpdated(uint256 indexed scannerId, uint256 indexed chainId, string metadata);
    event StakeThresholdChanged(uint256 indexed chainId, uint256 min, uint256 max, bool activated);
    
    error ScannerNotRegistered(address scanner);

    /**
     * @notice Checks sender (or metatx signer) is owner of the scanner token.
     * @param scannerId ERC721 token id of the scanner.
     */
    modifier onlyOwnerOf(uint256 scannerId) {
        if (_msgSender() != ownerOf(scannerId)) revert SenderNotOwner(_msgSender(), scannerId);
        _;
    }

    /**
     * @notice Checks if scannerId has been registered (minted).
     * @param scannerId ERC721 token id of the scanner.
     * @return true if scannerId exists, false otherwise.
     */
    function isRegistered(uint256 scannerId) public view override returns(bool) {
        return _exists(scannerId);
    }

    
    /// Converts scanner address to uint256 for ERC721 Token Id.
    function scannerAddressToId(address scanner) public pure returns(uint256) {
        return uint256(uint160(scanner));
    }

    /**
     * @notice Sets stake parameters (min, max, activated) for a `chainId`. Restricted to SCANNER_ADMIN_ROLE
     * @param newStakeThreshold struct with stake parameters.
     * @param chainId chain the parameters will affect.
     */
    function setStakeThreshold(StakeThreshold calldata newStakeThreshold, uint256 chainId) external onlyRole(SCANNER_ADMIN_ROLE) {
        if (newStakeThreshold.max <= newStakeThreshold.min) revert StakeThresholdMaxLessOrEqualMin();
        emit StakeThresholdChanged(chainId, newStakeThreshold.min, newStakeThreshold.max, newStakeThreshold.activated);
        _stakeThresholds[chainId] = newStakeThreshold;
    }

    /**
     * @dev internal getter for _getStakeThreshold, inheriting contracts may define logic to associate
     * a scanner with a StakeThreshold
     */
    function _getStakeThreshold(uint256 subject) internal virtual view returns(StakeThreshold memory);

    /**
     * @notice Getter for StakeThreshold for the scanner with id `subject`
     */
    function getStakeThreshold(uint256 subject) external view returns(StakeThreshold memory) {
        return _getStakeThreshold(subject);
    }

    /**
     * Checks if scanner is staked over minimum stake
     * @param scannerId scanner
     * @return true if scanner is staked over the minimum threshold for that chainId and is registered,
     * or staking is not yet enabled (stakeController = 0).
     * false otherwise
     */
    function _isStakedOverMin(uint256 scannerId) internal virtual override view returns(bool) {
        if (address(getSubjectHandler()) == address(0) || !_getStakeThreshold(scannerId).activated) {
            return true;
        }
        return getSubjectHandler().activeStakeFor(SCANNER_SUBJECT, scannerId) >= _getStakeThreshold(scannerId).min && _exists(scannerId);
    }


    /**
     * @notice Helper to get either msg msg.sender if not a meta transaction, signer of forwarder metatx if it is.
     * @inheritdoc ForwardedContext
     */
    function _msgSender() internal view virtual override(BaseComponentUpgradeable, ContextUpgradeable) returns (address sender) {
        return super._msgSender();
    }

    /**
     * @notice Helper to get msg.data if not a meta transaction, forwarder data in metatx if it is.
     * @inheritdoc ForwardedContext
     */
    function _msgData() internal view virtual override(BaseComponentUpgradeable, ContextUpgradeable) returns (bytes calldata) {
        return super._msgData();
    }

    /**
     * @notice disambiguation of ownerOf.
     * @inheritdoc ERC721Upgradeable
     */
    function ownerOf(uint256 subject) public view virtual override(DirectStakeSubjectUpgradeable, ERC721Upgradeable) returns (address) {
        return super.ownerOf(subject);
    }

    /**
     *  50
     * - 5 StakeSubjectUpgradeable;
     * - 1 _stakeThresholds;
     * --------------------------
     *  44 __gap
     */
    uint256[44] private __gap;
}

File 51 of 56 : ScannerRegistryEnable.sol
// SPDX-License-Identifier: UNLICENSED
// See Forta Network License: https://github.com/forta-network/forta-contracts/blob/master/LICENSE.md

pragma solidity ^0.8.9;

import "@openzeppelin/contracts/utils/structs/BitMaps.sol";
import "./ScannerRegistryManaged.sol";

/**
* @dev ScannerRegistry methods and state handling disabling and enabling scanners, and
* recognizing stake changes that might disable a scanner.
* NOTE: This contract was deployed before StakeAwareUpgradeable was created, so __StakeAwareUpgradeable_init
* is not called.
*/
abstract contract ScannerRegistryEnable is ScannerRegistryManaged {
    using BitMaps for BitMaps.BitMap;

    enum Permission {
        ADMIN,
        SELF,
        OWNER,
        MANAGER,
        length
    }

    mapping(uint256 => BitMaps.BitMap) internal _disabled;

    event ScannerEnabled(uint256 indexed scannerId, bool indexed enabled, Permission permission, bool value);

    /**
    * @notice Check if scanner is enabled
    * @param scannerId ERC721 token id of the scanner.
    * @return true if the scanner is registered, has not been disabled, and is staked over minimum value.
    * Returns false if otherwise
    */
    function isEnabled(uint256 scannerId) public view virtual returns (bool) {
        return isRegistered(scannerId) &&
            _getDisableFlags(scannerId) == 0 &&
            _isStakedOverMin(scannerId); 
    }

    /**
     * @notice Public method to enable a scanner, if caller has permission. Scanner must be staked over minimum defined
     * for the scanner's chainId.
     * @param scannerId ERC721 token id of the scanner.
     * @param permission the caller claims to have.
     */
    function enableScanner(uint256 scannerId, Permission permission) public virtual {
        if (!_hasPermission(scannerId, permission)) revert DoesNotHavePermission(_msgSender(), uint8(permission), scannerId);
        _enable(scannerId, permission, true);
    }

    /**
     * @notice Public method to disable a scanner, if caller has permission.
     * @param scannerId ERC721 token id of the scanner.
     * @param permission the caller claims to have.
     */
    function disableScanner(uint256 scannerId, Permission permission) public virtual {
        if (!_hasPermission(scannerId, permission)) revert DoesNotHavePermission(_msgSender(), uint8(permission), scannerId);
        _enable(scannerId, permission, false);
    }

    /**
     * Get the disabled flags for an agentId. Permission (uint8) is used for indexing, so we don't
     * need to loop. 
     * If not disabled, all flags will be 0
     * @param scannerId ERC721 token id of the scanner.
     * @return uint256 containing the byte flags.
     */
    function _getDisableFlags(uint256 scannerId) internal view returns (uint256) {
        return _disabled[scannerId]._data[0];
    }

    /**
     * @notice Method that does permission checks.
     * @dev AccessManager is not used since the permission is specific for scannerId
     * @param scannerId ERC721 token id of the scanner.
     * @param permission the caller claims to have.
     * @return true if (ADMIN and _msgSender() has SCANNER_ADMIN_ROLE), if _msgSender() is the scanner itself, its owner
     * or manager for each respective permission, false otherwise.
     */
    function _hasPermission(uint256 scannerId, Permission permission) internal view returns (bool) {
        if (permission == Permission.ADMIN)   { return hasRole(SCANNER_ADMIN_ROLE, _msgSender()); }
        if (permission == Permission.SELF)    { return uint256(uint160(_msgSender())) == scannerId; }
        if (permission == Permission.OWNER)   { return _msgSender() == ownerOf(scannerId); }
        if (permission == Permission.MANAGER) { return isManager(scannerId, _msgSender()); }
        return false;
    }

    /**
     * @notice Internal method to enable a scanner.
     * @dev will trigger _before and _after enable hooks within the inheritance tree.
     * @param scannerId ERC721 token id of the scanner.
     * @param permission the caller claims to have.
     * @param enable true for enabling, false for disabling
     */
    function _enable(uint256 scannerId, Permission permission, bool enable) internal {
        if (!isRegistered(scannerId)) revert ScannerNotRegistered(address(uint160(scannerId)));
        _disabled[scannerId].setTo(uint8(permission), !enable);
        emit ScannerEnabled(scannerId, isEnabled(scannerId), permission, enable);
    }

    /**
     * Obligatory inheritance dismambiguation of ForwardedContext's _msgSender()
     * @return sender msg.sender if not a meta transaction, signer of forwarder metatx if it is.
     */
    function _msgSender() internal view virtual override(ScannerRegistryCore) returns (address sender) {
        return super._msgSender();
    }
    /**
     * Obligatory inheritance dismambiguation of ForwardedContext's _msgSender()
     * @return sender msg.data if not a meta transaction, forwarder data in metatx if it is.
     */
    function _msgData() internal view virtual override(ScannerRegistryCore) returns (bytes calldata) {
        return super._msgData();
    }

    /**
     *  50
     * - 1 _disabled;
     * --------------------------
     *  49 __gap
     */
    uint256[49] private __gap;
}

File 52 of 56 : ScannerRegistryMetadata.sol
// SPDX-License-Identifier: UNLICENSED
// See Forta Network License: https://github.com/forta-network/forta-contracts/blob/master/LICENSE.md

pragma solidity ^0.8.9;

import "./ScannerRegistryCore.sol";

abstract contract ScannerRegistryMetadata is ScannerRegistryCore {
    struct ScannerMetadata {
        uint256 chainId;
        string metadata;
    }

    mapping(uint256 => ScannerMetadata) internal _scannerMetadata;

    /**
     * @notice Gets all scanner properties.
     * @param scannerId ERC721 token id of the scanner.
     * @return registered true if scanner exists.
     * @return owner address.
     * @return chainId the scanner is monitoring.
     * @return metadata IPFS pointer for the scanner's JSON metadata.
     */
    function getScanner(uint256 scannerId) public virtual view returns (bool registered, address owner, uint256 chainId, string memory metadata) {
        bool exists = _exists(scannerId);
        return (
            exists,
            exists ? ownerOf(scannerId) : address(0),
            _scannerMetadata[scannerId].chainId,
            _scannerMetadata[scannerId].metadata
        );
    }

    /**
     * @notice Gets scanner chain Ids.
     * @param scannerId ERC721 token id of the scanner.
     * @return chainId the scanner is monitoring.
     */
    function getScannerChainId(uint256 scannerId) public view returns (uint256) {
        return _scannerMetadata[scannerId].chainId;
    }
    
    
    /**
     * @dev checks the StakeThreshold for the chainId the scanner with id `subject` was registered to monitor.
     * @param subject ERC721 token id of the scanner.
     * @return StakeThreshold registered for `chainId`, or StakeThreshold(0,0,false) if `chainId` not found.
     */
    function _getStakeThreshold(uint256 subject) override virtual internal view returns(StakeThreshold memory) {
        return _stakeThresholds[getScannerChainId(subject)];
    }

    /**
     *  50
     * - 1 _scannerMetadata;
     * --------------------------
     *  49 __gap
     */
    uint256[49] private __gap;
}

File 53 of 56 : ScannerRegistryManaged.sol
// SPDX-License-Identifier: UNLICENSED
// See Forta Network License: https://github.com/forta-network/forta-contracts/blob/master/LICENSE.md

pragma solidity ^0.8.9;

import "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";

import "./ScannerRegistryCore.sol";

abstract contract ScannerRegistryManaged is ScannerRegistryCore {
    using EnumerableSet for EnumerableSet.AddressSet;

    mapping(uint256 => EnumerableSet.AddressSet) internal _managers;

    event ManagerEnabled(uint256 indexed scannerId, address indexed manager, bool enabled);

    error SenderNotManager(address sender, uint256 scannerId);

    /**
     * @notice Checks sender (or metatx signer) is manager of the scanner token.
     * @param scannerId ERC721 token id of the scanner.
     */
    modifier onlyManagerOf(uint256 scannerId) {
        if (!_managers[scannerId].contains(_msgSender())) revert SenderNotManager(_msgSender(), scannerId);
        _;
    }

    /**
     * @notice Checks if address is defined as a manager for a scanner.
     * @param scannerId ERC721 token id of the scanner.
     * @param manager address to check.
     * @return true if defined as manager for scanner, false otherwise.
     */
    function isManager(uint256 scannerId, address manager) public view virtual returns (bool) {
        return _managers[scannerId].contains(manager);
    }

    /**
     * @notice Gets total managers defined for a scanner.
     * @dev helper for external iteration.
     * @param scannerId ERC721 token id of the scanner.
     * @return total managers defined for a scanner.
     */
    function getManagerCount(uint256 scannerId) public view virtual returns (uint256) {
        return _managers[scannerId].length();
    }

    /**
     * @notice Gets manager address at certain position of the scanner's manager set.
     * @dev helper for external iteration.
     * @param scannerId ERC721 token id of the scanner.
     * @param index position in the set.
     * @return address of the manager at index.
     */
    function getManagerAt(uint256 scannerId, uint256 index) public view virtual returns (address) {
        return _managers[scannerId].at(index);
    }

    /**
     *  50
     * - 1 _managers;
     * --------------------------
     *  49 __gap
     */
    uint256[49] private __gap;
}

File 54 of 56 : DirectStakeSubject.sol
// SPDX-License-Identifier: UNLICENSED
// See Forta Network License: https://github.com/forta-network/forta-contracts/blob/master/LICENSE.md

pragma solidity ^0.8.9;

import "../../../errors/GeneralErrors.sol";
import "./IStakeSubjectGateway.sol";
import "./IDirectStakeSubject.sol";
import "../SubjectTypeValidator.sol";
import "../../Roles.sol";
import "../../utils/AccessManaged.sol";

abstract contract DirectStakeSubjectUpgradeable is AccessManagedUpgradeable, IDirectStakeSubject {
    /// @custom:oz-renamed-from _stakeController
    /// @custom:oz-retyped-from IStakeController
    IStakeSubjectGateway private _subjectGateway;

    event SubjectHandlerUpdated(address indexed newHandler);

    error StakedUnderMinimum(uint256 subject);

    /*
     * @dev: For contracts made StakeAwareUpgradeable via upgrade, initializer call is not available.
     * Use setSubjectHandler(subjectGateway) when upgrading instead.
     * @param subjectGateway address.
     */
    function __StakeSubjectUpgradeable_init(address subjectGateway) internal initializer {
        _setSubjectHandler(subjectGateway);
    }

    /// Stake controller setter, restricted to DEFAULT_ADMIN_ROLE
    function setSubjectHandler(address subjectGateway) public onlyRole(DEFAULT_ADMIN_ROLE) {
        _setSubjectHandler(subjectGateway);
    }

    /// Getter for subjectGateway
    function getSubjectHandler() public view returns (IStakeSubjectGateway) {
        return _subjectGateway;
    }

    /// Internal setter for subjectGateway, emits subjectGatewayUpdated
    function _setSubjectHandler(address subjectGateway) private {
        if (subjectGateway == address(0)) revert ZeroAddress("subjectGateway");
        _subjectGateway = IStakeSubjectGateway(subjectGateway);
        emit SubjectHandlerUpdated(subjectGateway);
    }

    /// Returns true if `subject` amount of staked tokens is bigger or equal the minimum stake set
    /// for it. It's for contracts implementing `StakeSubjectUpgradeable` to decide what that means.
    function isStakedOverMin(uint256 subject) external view virtual override returns (bool) {
        return _isStakedOverMin(subject);
    }

    function _isStakedOverMin(uint256 subject) internal view virtual returns (bool);
    
    function ownerOf(uint256 subject) external view virtual returns (address);

    /**
     *   5 (Not 50, since it was part of an upgrade of XXXRegistryCore)
     * - 1 _subjectGateway;
     * --------------------------
     *   4 __gap
     */
    uint256[4] private __gap;
}

File 55 of 56 : IDirectStakeSubject.sol
// SPDX-License-Identifier: UNLICENSED
// See Forta Network License: https://github.com/forta-network/forta-contracts/blob/master/LICENSE.md

pragma solidity ^0.8.9;

import "./IStakeSubject.sol";

interface IDirectStakeSubject is IStakeSubject {
    function getStakeThreshold(uint256 subject) external view returns (StakeThreshold memory);
    function isStakedOverMin(uint256 subject) external view returns (bool);
}

File 56 of 56 : BitMaps.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/structs/BitMaps.sol)
pragma solidity ^0.8.0;

/**
 * @dev Library for managing uint256 to bool mapping in a compact and efficient way, providing the keys are sequential.
 * Largelly inspired by Uniswap's https://github.com/Uniswap/merkle-distributor/blob/master/contracts/MerkleDistributor.sol[merkle-distributor].
 */
library BitMaps {
    struct BitMap {
        mapping(uint256 => uint256) _data;
    }

    /**
     * @dev Returns whether the bit at `index` is set.
     */
    function get(BitMap storage bitmap, uint256 index) internal view returns (bool) {
        uint256 bucket = index >> 8;
        uint256 mask = 1 << (index & 0xff);
        return bitmap._data[bucket] & mask != 0;
    }

    /**
     * @dev Sets the bit at `index` to the boolean `value`.
     */
    function setTo(
        BitMap storage bitmap,
        uint256 index,
        bool value
    ) internal {
        if (value) {
            set(bitmap, index);
        } else {
            unset(bitmap, index);
        }
    }

    /**
     * @dev Sets the bit at `index`.
     */
    function set(BitMap storage bitmap, uint256 index) internal {
        uint256 bucket = index >> 8;
        uint256 mask = 1 << (index & 0xff);
        bitmap._data[bucket] |= mask;
    }

    /**
     * @dev Unsets the bit at `index`.
     */
    function unset(BitMap storage bitmap, uint256 index) internal {
        uint256 bucket = index >> 8;
        uint256 mask = 1 << (index & 0xff);
        bitmap._data[bucket] &= ~mask;
    }
}

Settings
{
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "libraries": {}
}

Contract ABI

[{"inputs":[{"internalType":"address","name":"_forwarder","type":"address"},{"internalType":"address","name":"_scannerNodeRegistry","type":"address"},{"internalType":"address","name":"_scannerPoolRegistry","type":"address"},{"internalType":"address","name":"_stakeMigrator","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"MissingRole","type":"error"},{"inputs":[{"internalType":"address","name":"pretender","type":"address"},{"internalType":"uint256","name":"scannerPoolId","type":"uint256"}],"name":"NotOwnerOfScannerPool","type":"error"},{"inputs":[{"internalType":"uint256","name":"scannerPoolId","type":"uint256"}],"name":"ScannerPoolAlreadyMigrated","type":"error"},{"inputs":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"uint256","name":"ownedId","type":"uint256"}],"name":"SenderNotOwner","type":"error"},{"inputs":[{"internalType":"string","name":"name","type":"string"}],"name":"UnsupportedInterface","type":"error"},{"inputs":[{"internalType":"uint256","name":"expected","type":"uint256"},{"internalType":"uint256","name":"provided","type":"uint256"},{"internalType":"address","name":"scanner","type":"address"}],"name":"WrongScannerChainId","type":"error"},{"inputs":[{"internalType":"uint256","name":"expected","type":"uint256"},{"internalType":"uint256","name":"provided","type":"uint256"},{"internalType":"uint256","name":"scannerPoolId","type":"uint256"}],"name":"WrongScannerPoolChainId","type":"error"},{"inputs":[{"internalType":"string","name":"name","type":"string"}],"name":"ZeroAddress","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"newAddressManager","type":"address"}],"name":"AccessManagerUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"previousAdmin","type":"address"},{"indexed":false,"internalType":"address","name":"newAdmin","type":"address"}],"name":"AdminChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"beacon","type":"address"}],"name":"BeaconUpgraded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint8","name":"version","type":"uint8"}],"name":"Initialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"scannersMigrated","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"scannersIgnored","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"scannerPoolId","type":"uint256"},{"indexed":false,"internalType":"bool","name":"mintedScannerPool","type":"bool"}],"name":"MigrationExecuted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"router","type":"address"}],"name":"RouterUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"implementation","type":"address"}],"name":"Upgraded","type":"event"},{"inputs":[],"name":"SCANNER_POOL_NOT_MIGRATED","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"disableRouter","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"__manager","type":"address"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"forwarder","type":"address"}],"name":"isTrustedForwarder","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"scanners","type":"address[]"},{"internalType":"uint256","name":"scannerPoolId","type":"uint256"},{"internalType":"address","name":"scannerPool","type":"address"},{"internalType":"uint256","name":"chainId","type":"uint256"}],"name":"migrate","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes[]","name":"data","type":"bytes[]"}],"name":"multicall","outputs":[{"internalType":"bytes[]","name":"results","type":"bytes[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"proxiableUUID","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"scannerNodeRegistry","outputs":[{"internalType":"contract ScannerRegistry","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"scannerPoolRegistry","outputs":[{"internalType":"contract ScannerPoolRegistry","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"scanners","type":"address[]"},{"internalType":"uint256","name":"scannerPoolId","type":"uint256"},{"internalType":"uint256","name":"chainId","type":"uint256"}],"name":"selfMigrate","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newManager","type":"address"}],"name":"setAccessManager","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"ensRegistry","type":"address"},{"internalType":"string","name":"ensName","type":"string"}],"name":"setName","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"stakeMigrator","outputs":[{"internalType":"contract IStakeMigrator","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newImplementation","type":"address"}],"name":"upgradeTo","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newImplementation","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"upgradeToAndCall","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"version","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"}]

6101206040523060a0523480156200001657600080fd5b5060405162002a3038038062002a308339810160408190526200003991620002b8565b6001600160a01b038416608052600054610100900460ff1615808015620000675750600054600160ff909116105b8062000097575062000084306200028c60201b620009fd1760201c565b15801562000097575060005460ff166001145b620001005760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b60648201526084015b60405180910390fd5b6000805460ff19166001179055801562000124576000805461ff0019166101001790555b6001600160a01b0384166200017d5760405163eac0d38960e01b815260206004820152601460248201527f5f7363616e6e65724e6f646552656769737472790000000000000000000000006044820152606401620000f7565b6001600160a01b038316620001d65760405163eac0d38960e01b815260206004820152601460248201527f5f7363616e6e6572506f6f6c52656769737472790000000000000000000000006044820152606401620000f7565b6001600160a01b038216620002205760405163eac0d38960e01b815260206004820152600e60248201526d2fb9ba30b5b2a6b4b3b930ba37b960911b6044820152606401620000f7565b6001600160a01b0380851660c05283811660e052821661010052801562000281576000805461ff0019169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b505050505062000315565b6001600160a01b03163b151590565b80516001600160a01b0381168114620002b357600080fd5b919050565b60008060008060808587031215620002cf57600080fd5b620002da856200029b565b9350620002ea602086016200029b565b9250620002fa604086016200029b565b91506200030a606086016200029b565b905092959194509250565b60805160a05160c05160e05161010051612666620003ca6000396000818161010a0152611088015260008181610310015281816113ee015281816114a7015281816115940152818161163301526117e101526000818161037901528181610d9701528181610e7001528181610f94015281816116f001526118af01526000818161042601528181610466015281816105060152818161054601526105d901526000818161021c015261123301526126666000f3fe6080604052600436106100f35760003560e01c806379d096621161008a578063cde574fe11610059578063cde574fe146102fe578063d858a7e514610332578063ee0fd8e014610347578063f72a13561461036757600080fd5b806379d096621461027c578063ac9650d814610291578063c4d66de8146102be578063c9580804146102de57600080fd5b806352d1902d116100c657806352d1902d1461019e57806354fd4d50146101c1578063572b6c05146101ff57806374485ca91461025c57600080fd5b80630b13ac23146100f85780633121db1c146101495780633659cfe61461016b5780634f1ef2861461018b575b600080fd5b34801561010457600080fd5b5061012c7f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b0390911681526020015b60405180910390f35b34801561015557600080fd5b50610169610164366004611e37565b61039b565b005b34801561017757600080fd5b50610169610186366004611ebc565b61041b565b610169610199366004611f48565b6104fb565b3480156101aa57600080fd5b506101b36105cc565b604051908152602001610140565b3480156101cd57600080fd5b506101f2604051806040016040528060058152602001640302e312e360dc1b81525081565b6040516101409190612033565b34801561020b57600080fd5b5061024c61021a366004611ebc565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0390811691161490565b6040519015158152602001610140565b34801561026857600080fd5b506101b3610277366004612092565b61067f565b34801561028857600080fd5b506101b3600081565b34801561029d57600080fd5b506102b16102ac3660046120e3565b61069e565b6040516101409190612125565b3480156102ca57600080fd5b506101696102d9366004611ebc565b610794565b3480156102ea57600080fd5b506101696102f9366004611ebc565b610860565b34801561030a57600080fd5b5061012c7f000000000000000000000000000000000000000000000000000000000000000081565b34801561033e57600080fd5b5061016961091e565b34801561035357600080fd5b506101b3610362366004612187565b6109a9565b34801561037357600080fd5b5061012c7f000000000000000000000000000000000000000000000000000000000000000081565b7f664245c7af190fec316596e8231f724e8171b1966cfcd124347ac5a66c34f64a6103cd816103c8610a0c565b610a1b565b61040a57806103da610a0c565b6040516301d4003760e61b815260048101929092526001600160a01b031660248201526044015b60405180910390fd5b610415848484610aa7565b50505050565b306001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614156104645760405162461bcd60e51b8152600401610401906121ee565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166104ad6000805160206125ea833981519152546001600160a01b031690565b6001600160a01b0316146104d35760405162461bcd60e51b81526004016104019061223a565b6104dc81610bbd565b604080516000808252602082019092526104f891839190610bf7565b50565b306001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614156105445760405162461bcd60e51b8152600401610401906121ee565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031661058d6000805160206125ea833981519152546001600160a01b031690565b6001600160a01b0316146105b35760405162461bcd60e51b81526004016104019061223a565b6105bc82610bbd565b6105c882826001610bf7565b5050565b6000306001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000161461066c5760405162461bcd60e51b815260206004820152603860248201527f555550535570677261646561626c653a206d757374206e6f742062652063616c60448201527f6c6564207468726f7567682064656c656761746563616c6c00000000000000006064820152608401610401565b506000805160206125ea83398151915290565b600061069585858561068f610a0c565b86610d76565b95945050505050565b60608167ffffffffffffffff8111156106b9576106b9611ed9565b6040519080825280602002602001820160405280156106ec57816020015b60608152602001906001900390816106d75790505b50905060005b8281101561078c5761075c3085858481811061071057610710612286565b9050602002810190610722919061229c565b8080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061116492505050565b82828151811061076e5761076e612286565b60200260200101819052508080610784906122f9565b9150506106f2565b505b92915050565b600054610100900460ff16158080156107b45750600054600160ff909116105b806107ce5750303b1580156107ce575060005460ff166001145b6107ea5760405162461bcd60e51b815260040161040190612314565b6000805460ff19166001179055801561080d576000805461ff0019166101001790555b61081682611189565b80156105c8576000805461ff0019169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb3847402498906020015b60405180910390a15050565b600061086e816103c8610a0c565b61087b57806103da610a0c565b6108956001600160a01b038316637965db0b60e01b611213565b6108d3576040516301a1fdbb60e41b815260206004820152600e60248201526d125058d8d95cdcd0dbdb9d1c9bdb60921b6044820152606401610401565b603380546001600160a01b0319166001600160a01b0384169081179091556040517fa5bc17e575e3b53b23d0e93e121a5a66d1de4d5edb4dfde6027b14d79b7f2b9c90600090a25050565b6065546001600160a01b031661096c5760405163eac0d38960e01b81526020600482015260126024820152712fb232b83932b1b0ba32b22fb937baba32b960711b6044820152606401610401565b606580546001600160a01b03191690556040516000907f7aed1d3e8155a07ccf395e44ea3109a0e2d6c9b29bbbe9f142d9790596f4dc80908290a2565b60007f318f8ec0d7342c96d3016c69d3ed9440e994b0c381996cea64a80256bc76aed16109d8816103c8610a0c565b6109e557806103da610a0c565b6109f28787878787610d76565b979650505050505050565b6001600160a01b03163b151590565b6000610a1661122f565b905090565b603354604051632474521560e21b8152600481018490526001600160a01b03838116602483015260009216906391d148549060440160206040518083038186803b158015610a6857600080fd5b505afa158015610a7c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610aa09190612377565b9392505050565b6040516302571be360e01b81527f91d1777781884d03a6757a803996e38de2a42967fb37eeaca72729271025a9e260048201526001600160a01b038416906302571be39060240160206040518083038186803b158015610b0657600080fd5b505afa158015610b1a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b3e9190612392565b6001600160a01b031663c47f002783836040518363ffffffff1660e01b8152600401610b6b9291906123af565b602060405180830381600087803b158015610b8557600080fd5b505af1158015610b99573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061041591906123de565b7f189ab7a9244df0848122154315af71fe140f3db0fe014031783b0946b8c9d2e3610bea816103c8610a0c565b6105c857806103da610a0c565b7f4910fdfa16fed3260ed0e7147f7cc6da11a60208b5b9406d12a635614ffd91435460ff1615610c2f57610c2a83611292565b505050565b826001600160a01b03166352d1902d6040518163ffffffff1660e01b815260040160206040518083038186803b158015610c6857600080fd5b505afa925050508015610c98575060408051601f3d908101601f19168201909252610c95918101906123de565b60015b610cfb5760405162461bcd60e51b815260206004820152602e60248201527f45524331393637557067726164653a206e657720696d706c656d656e7461746960448201526d6f6e206973206e6f74205555505360901b6064820152608401610401565b6000805160206125ea8339815191528114610d6a5760405162461bcd60e51b815260206004820152602960248201527f45524331393637557067726164653a20756e737570706f727465642070726f786044820152681a58589b195555525160ba1b6064820152608401610401565b50610c2a83838361132e565b600080610d84848685611353565b9050856000805b828110156111095760007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166382fe1bcc8c8c85818110610dd657610dd6612286565b9050602002016020810190610deb9190611ebc565b6040516001600160e01b031960e084901b1681526001600160a01b03909116600482015260240160206040518083038186803b158015610e2a57600080fd5b505afa158015610e3e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e6291906123de565b9050876001600160a01b03167f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316636352211e836040518263ffffffff1660e01b8152600401610ebc91815260200190565b60206040518083038186803b158015610ed457600080fd5b505afa158015610ee8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f0c9190612392565b6001600160a01b031614610f455760405163871d7af360e01b81526001600160a01b038916600482015260248101829052604401610401565b600080610f79838a8f8f88818110610f5f57610f5f612286565b9050602002016020810190610f749190611ebc565b6116e6565b604051636eb41f7760e01b81526004810186905291935091507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690636eb41f779060240160206040518083038186803b158015610fde57600080fd5b505afa158015610ff2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110169190612377565b6110f35761104d8d8d8681811061102f5761102f612286565b90506020020160208101906110449190611ebc565b82898c866117df565b60405163093af6a960e01b8152600060048201526024810184905260026044820152606481018890526001600160a01b038b811660848301527f0000000000000000000000000000000000000000000000000000000000000000169063093af6a99060a401600060405180830381600087803b1580156110cc57600080fd5b505af11580156110e0573d6000803e3d6000fd5b5050505084806110ef906122f9565b9550505b5050508080611101906122f9565b915050610d8b565b50827fe179f684b4b38725831cb54955bc8284389f0b5279e7f4104127a95036efda028261113781866123f7565b6040805192835260208301919091528a159082015260600160405180910390a25090979650505050505050565b6060610aa0838360405180606001604052806027815260200161260a6027913961198a565b600054610100900460ff16158080156111a95750600054600160ff909116105b806111c35750303b1580156111c3575060005460ff166001145b6111df5760405162461bcd60e51b815260040161040190612314565b6000805460ff191660011790558015611202576000805461ff0019166101001790555b61120b82611a28565b610816611b82565b600061121e83611bef565b8015610aa05750610aa08383611c22565b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031633141561128d576000366112706014826123f7565b61127c9236929061240e565b61128591612438565b60601c905090565b503390565b6001600160a01b0381163b6112ff5760405162461bcd60e51b815260206004820152602d60248201527f455243313936373a206e657720696d706c656d656e746174696f6e206973206e60448201526c1bdd08184818dbdb9d1c9858dd609a1b6064820152608401610401565b6000805160206125ea83398151915280546001600160a01b0319166001600160a01b0392909216919091179055565b61133783611d01565b6000825111806113445750805b15610c2a576104158383611d41565b60008261149b57600082815261012d602090815260408083206001600160a01b0388168452909152902054156113c557600082815261012d602090815260408083206001600160a01b03881684529091529081902054905163c41714c560e01b81526004810191909152602401610401565b604051630eb9a5f960e11b81526001600160a01b038581166004830152602482018490526000917f000000000000000000000000000000000000000000000000000000000000000090911690631d734bf290604401602060405180830381600087803b15801561143457600080fd5b505af1158015611448573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061146c91906123de565b600084815261012d602090815260408083206001600160a01b038a16845290915290208190559150610aa09050565b836001600160a01b03167f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316636352211e856040518263ffffffff1660e01b81526004016114f391815260200190565b60206040518083038186803b15801561150b57600080fd5b505afa15801561151f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115439190612392565b6001600160a01b03161461157c5760405163070ef60f60e51b81526001600160a01b038516600482015260248101849052604401610401565b60405163e873c9dd60e01b81526004810184905282907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063e873c9dd9060240160206040518083038186803b1580156115de57600080fd5b505afa1580156115f2573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061161691906123de565b146116de5760405163e873c9dd60e01b81526004810184905282907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063e873c9dd9060240160206040518083038186803b15801561167d57600080fd5b505afa158015611691573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116b591906123de565b60405163702c9cbf60e11b81526004810192909252602482015260448101849052606401610401565b509092915050565b60606000806000807f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166337fea0e0896040518263ffffffff1660e01b815260040161173c91815260200190565b60006040518083038186803b15801561175457600080fd5b505afa158015611768573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052611790919081019061246d565b9550509450945050508683146117d257604051635afe552560e01b815260048101889052602481018490526001600160a01b0387166044820152606401610401565b9097909650945050505050565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663474dd8216040518060a00160405280886001600160a01b031681526020018681526020018581526020018481526020014281525086600014156040518363ffffffff1660e01b815260040161186092919061252c565b600060405180830381600087803b15801561187a57600080fd5b505af115801561188e573d6000803e3d6000fd5b50506040516320bf86f360e21b81526001600160a01b0388811660048301527f00000000000000000000000000000000000000000000000000000000000000001692506339c7ac21915082906382fe1bcc9060240160206040518083038186803b1580156118fb57600080fd5b505afa15801561190f573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061193391906123de565b6040518263ffffffff1660e01b815260040161195191815260200190565b600060405180830381600087803b15801561196b57600080fd5b505af115801561197f573d6000803e3d6000fd5b505050505050505050565b60606001600160a01b0384163b6119b35760405162461bcd60e51b815260040161040190612587565b600080856001600160a01b0316856040516119ce91906125cd565b600060405180830381855af49150503d8060008114611a09576040519150601f19603f3d011682016040523d82523d6000602084013e611a0e565b606091505b5091509150611a1e828286611de9565b9695505050505050565b600054610100900460ff1615808015611a485750600054600160ff909116105b80611a625750303b158015611a62575060005460ff166001145b611a7e5760405162461bcd60e51b815260040161040190612314565b6000805460ff191660011790558015611aa1576000805461ff0019166101001790555b611abb6001600160a01b038316637965db0b60e01b611213565b611af9576040516301a1fdbb60e41b815260206004820152600e60248201526d125058d8d95cdcd0dbdb9d1c9bdb60921b6044820152606401610401565b603380546001600160a01b0319166001600160a01b0384169081179091556040517fa5bc17e575e3b53b23d0e93e121a5a66d1de4d5edb4dfde6027b14d79b7f2b9c90600090a280156105c8576000805461ff0019169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb384740249890602001610854565b600054610100900460ff16611bed5760405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201526a6e697469616c697a696e6760a81b6064820152608401610401565b565b6000611c02826301ffc9a760e01b611c22565b801561078e5750611c1b826001600160e01b0319611c22565b1592915050565b604080516001600160e01b0319831660248083019190915282518083039091018152604490910182526020810180516001600160e01b03166301ffc9a760e01b179052905160009190829081906001600160a01b0387169061753090611c899086906125cd565b6000604051808303818686fa925050503d8060008114611cc5576040519150601f19603f3d011682016040523d82523d6000602084013e611cca565b606091505b5091509150602081511015611ce5576000935050505061078e565b818015611a1e575080806020019051810190611a1e9190612377565b611d0a81611292565b6040516001600160a01b038216907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b90600090a250565b60606001600160a01b0383163b611d6a5760405162461bcd60e51b815260040161040190612587565b600080846001600160a01b031684604051611d8591906125cd565b600060405180830381855af49150503d8060008114611dc0576040519150601f19603f3d011682016040523d82523d6000602084013e611dc5565b606091505b5091509150610695828260405180606001604052806027815260200161260a602791395b60608315611df8575081610aa0565b825115611e085782518084602001fd5b8160405162461bcd60e51b81526004016104019190612033565b6001600160a01b03811681146104f857600080fd5b600080600060408486031215611e4c57600080fd5b8335611e5781611e22565b9250602084013567ffffffffffffffff80821115611e7457600080fd5b818601915086601f830112611e8857600080fd5b813581811115611e9757600080fd5b876020828501011115611ea957600080fd5b6020830194508093505050509250925092565b600060208284031215611ece57600080fd5b8135610aa081611e22565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f1916810167ffffffffffffffff81118282101715611f1857611f18611ed9565b604052919050565b600067ffffffffffffffff821115611f3a57611f3a611ed9565b50601f01601f191660200190565b60008060408385031215611f5b57600080fd5b8235611f6681611e22565b9150602083013567ffffffffffffffff811115611f8257600080fd5b8301601f81018513611f9357600080fd5b8035611fa6611fa182611f20565b611eef565b818152866020838501011115611fbb57600080fd5b816020840160208301376000602083830101528093505050509250929050565b60005b83811015611ff6578181015183820152602001611fde565b838111156104155750506000910152565b6000815180845261201f816020860160208601611fdb565b601f01601f19169290920160200192915050565b602081526000610aa06020830184612007565b60008083601f84011261205857600080fd5b50813567ffffffffffffffff81111561207057600080fd5b6020830191508360208260051b850101111561208b57600080fd5b9250929050565b600080600080606085870312156120a857600080fd5b843567ffffffffffffffff8111156120bf57600080fd5b6120cb87828801612046565b90989097506020870135966040013595509350505050565b600080602083850312156120f657600080fd5b823567ffffffffffffffff81111561210d57600080fd5b61211985828601612046565b90969095509350505050565b6000602080830181845280855180835260408601915060408160051b870101925083870160005b8281101561217a57603f19888603018452612168858351612007565b9450928501929085019060010161214c565b5092979650505050505050565b60008060008060006080868803121561219f57600080fd5b853567ffffffffffffffff8111156121b657600080fd5b6121c288828901612046565b9096509450506020860135925060408601356121dd81611e22565b949793965091946060013592915050565b6020808252602c908201527f46756e6374696f6e206d7573742062652063616c6c6564207468726f7567682060408201526b19195b1959d85d1958d85b1b60a21b606082015260800190565b6020808252602c908201527f46756e6374696f6e206d7573742062652063616c6c6564207468726f7567682060408201526b6163746976652070726f787960a01b606082015260800190565b634e487b7160e01b600052603260045260246000fd5b6000808335601e198436030181126122b357600080fd5b83018035915067ffffffffffffffff8211156122ce57600080fd5b60200191503681900382131561208b57600080fd5b634e487b7160e01b600052601160045260246000fd5b600060001982141561230d5761230d6122e3565b5060010190565b6020808252602e908201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160408201526d191e481a5b9a5d1a585b1a5e995960921b606082015260800190565b8051801515811461237257600080fd5b919050565b60006020828403121561238957600080fd5b610aa082612362565b6000602082840312156123a457600080fd5b8151610aa081611e22565b60208152816020820152818360408301376000818301604090810191909152601f909201601f19160101919050565b6000602082840312156123f057600080fd5b5051919050565b600082821015612409576124096122e3565b500390565b6000808585111561241e57600080fd5b8386111561242b57600080fd5b5050820193919092039150565b6bffffffffffffffffffffffff1981358181169160148510156124655780818660140360031b1b83161692505b505092915050565b60008060008060008060c0878903121561248657600080fd5b61248f87612362565b9550602087015161249f81611e22565b60408801516060890151919650945067ffffffffffffffff8111156124c357600080fd5b8701601f810189136124d457600080fd5b80516124e2611fa182611f20565b8181528a60208385010111156124f757600080fd5b612508826020830160208601611fdb565b945061251991505060808801612362565b915060a087015190509295509295509295565b6040815260018060a01b03835116604082015260208301516060820152604083015160808201526000606084015160a08084015261256d60e0840182612007565b6080959095015160c0840152505090151560209091015290565b60208082526026908201527f416464726573733a2064656c65676174652063616c6c20746f206e6f6e2d636f6040820152651b9d1c9858dd60d21b606082015260800190565b600082516125df818460208701611fdb565b919091019291505056fe360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564a2646970667358221220610b5fb9d6f3d5a2fe2510d91cc519f34c230f5bf6ae131ceaf062bfaf2df9a964736f6c634300080900330000000000000000000000004e29cea6d64be860f5eaba110686dcb585f393d6000000000000000000000000569c785b4744e582c65a12827726fc03c8d08a4a000000000000000000000000fd745747ec40b439fee9248ae9d57eb846f7ebf500000000000000000000000064d5192f03bd98db1de2aa8b4abac5419eac32ce

Deployed Bytecode

0x6080604052600436106100f35760003560e01c806379d096621161008a578063cde574fe11610059578063cde574fe146102fe578063d858a7e514610332578063ee0fd8e014610347578063f72a13561461036757600080fd5b806379d096621461027c578063ac9650d814610291578063c4d66de8146102be578063c9580804146102de57600080fd5b806352d1902d116100c657806352d1902d1461019e57806354fd4d50146101c1578063572b6c05146101ff57806374485ca91461025c57600080fd5b80630b13ac23146100f85780633121db1c146101495780633659cfe61461016b5780634f1ef2861461018b575b600080fd5b34801561010457600080fd5b5061012c7f00000000000000000000000064d5192f03bd98db1de2aa8b4abac5419eac32ce81565b6040516001600160a01b0390911681526020015b60405180910390f35b34801561015557600080fd5b50610169610164366004611e37565b61039b565b005b34801561017757600080fd5b50610169610186366004611ebc565b61041b565b610169610199366004611f48565b6104fb565b3480156101aa57600080fd5b506101b36105cc565b604051908152602001610140565b3480156101cd57600080fd5b506101f2604051806040016040528060058152602001640302e312e360dc1b81525081565b6040516101409190612033565b34801561020b57600080fd5b5061024c61021a366004611ebc565b7f0000000000000000000000004e29cea6d64be860f5eaba110686dcb585f393d66001600160a01b0390811691161490565b6040519015158152602001610140565b34801561026857600080fd5b506101b3610277366004612092565b61067f565b34801561028857600080fd5b506101b3600081565b34801561029d57600080fd5b506102b16102ac3660046120e3565b61069e565b6040516101409190612125565b3480156102ca57600080fd5b506101696102d9366004611ebc565b610794565b3480156102ea57600080fd5b506101696102f9366004611ebc565b610860565b34801561030a57600080fd5b5061012c7f000000000000000000000000fd745747ec40b439fee9248ae9d57eb846f7ebf581565b34801561033e57600080fd5b5061016961091e565b34801561035357600080fd5b506101b3610362366004612187565b6109a9565b34801561037357600080fd5b5061012c7f000000000000000000000000569c785b4744e582c65a12827726fc03c8d08a4a81565b7f664245c7af190fec316596e8231f724e8171b1966cfcd124347ac5a66c34f64a6103cd816103c8610a0c565b610a1b565b61040a57806103da610a0c565b6040516301d4003760e61b815260048101929092526001600160a01b031660248201526044015b60405180910390fd5b610415848484610aa7565b50505050565b306001600160a01b037f000000000000000000000000b983b38dd4620419e5214a3bfe2e7ed8278595861614156104645760405162461bcd60e51b8152600401610401906121ee565b7f000000000000000000000000b983b38dd4620419e5214a3bfe2e7ed8278595866001600160a01b03166104ad6000805160206125ea833981519152546001600160a01b031690565b6001600160a01b0316146104d35760405162461bcd60e51b81526004016104019061223a565b6104dc81610bbd565b604080516000808252602082019092526104f891839190610bf7565b50565b306001600160a01b037f000000000000000000000000b983b38dd4620419e5214a3bfe2e7ed8278595861614156105445760405162461bcd60e51b8152600401610401906121ee565b7f000000000000000000000000b983b38dd4620419e5214a3bfe2e7ed8278595866001600160a01b031661058d6000805160206125ea833981519152546001600160a01b031690565b6001600160a01b0316146105b35760405162461bcd60e51b81526004016104019061223a565b6105bc82610bbd565b6105c882826001610bf7565b5050565b6000306001600160a01b037f000000000000000000000000b983b38dd4620419e5214a3bfe2e7ed827859586161461066c5760405162461bcd60e51b815260206004820152603860248201527f555550535570677261646561626c653a206d757374206e6f742062652063616c60448201527f6c6564207468726f7567682064656c656761746563616c6c00000000000000006064820152608401610401565b506000805160206125ea83398151915290565b600061069585858561068f610a0c565b86610d76565b95945050505050565b60608167ffffffffffffffff8111156106b9576106b9611ed9565b6040519080825280602002602001820160405280156106ec57816020015b60608152602001906001900390816106d75790505b50905060005b8281101561078c5761075c3085858481811061071057610710612286565b9050602002810190610722919061229c565b8080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061116492505050565b82828151811061076e5761076e612286565b60200260200101819052508080610784906122f9565b9150506106f2565b505b92915050565b600054610100900460ff16158080156107b45750600054600160ff909116105b806107ce5750303b1580156107ce575060005460ff166001145b6107ea5760405162461bcd60e51b815260040161040190612314565b6000805460ff19166001179055801561080d576000805461ff0019166101001790555b61081682611189565b80156105c8576000805461ff0019169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb3847402498906020015b60405180910390a15050565b600061086e816103c8610a0c565b61087b57806103da610a0c565b6108956001600160a01b038316637965db0b60e01b611213565b6108d3576040516301a1fdbb60e41b815260206004820152600e60248201526d125058d8d95cdcd0dbdb9d1c9bdb60921b6044820152606401610401565b603380546001600160a01b0319166001600160a01b0384169081179091556040517fa5bc17e575e3b53b23d0e93e121a5a66d1de4d5edb4dfde6027b14d79b7f2b9c90600090a25050565b6065546001600160a01b031661096c5760405163eac0d38960e01b81526020600482015260126024820152712fb232b83932b1b0ba32b22fb937baba32b960711b6044820152606401610401565b606580546001600160a01b03191690556040516000907f7aed1d3e8155a07ccf395e44ea3109a0e2d6c9b29bbbe9f142d9790596f4dc80908290a2565b60007f318f8ec0d7342c96d3016c69d3ed9440e994b0c381996cea64a80256bc76aed16109d8816103c8610a0c565b6109e557806103da610a0c565b6109f28787878787610d76565b979650505050505050565b6001600160a01b03163b151590565b6000610a1661122f565b905090565b603354604051632474521560e21b8152600481018490526001600160a01b03838116602483015260009216906391d148549060440160206040518083038186803b158015610a6857600080fd5b505afa158015610a7c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610aa09190612377565b9392505050565b6040516302571be360e01b81527f91d1777781884d03a6757a803996e38de2a42967fb37eeaca72729271025a9e260048201526001600160a01b038416906302571be39060240160206040518083038186803b158015610b0657600080fd5b505afa158015610b1a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b3e9190612392565b6001600160a01b031663c47f002783836040518363ffffffff1660e01b8152600401610b6b9291906123af565b602060405180830381600087803b158015610b8557600080fd5b505af1158015610b99573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061041591906123de565b7f189ab7a9244df0848122154315af71fe140f3db0fe014031783b0946b8c9d2e3610bea816103c8610a0c565b6105c857806103da610a0c565b7f4910fdfa16fed3260ed0e7147f7cc6da11a60208b5b9406d12a635614ffd91435460ff1615610c2f57610c2a83611292565b505050565b826001600160a01b03166352d1902d6040518163ffffffff1660e01b815260040160206040518083038186803b158015610c6857600080fd5b505afa925050508015610c98575060408051601f3d908101601f19168201909252610c95918101906123de565b60015b610cfb5760405162461bcd60e51b815260206004820152602e60248201527f45524331393637557067726164653a206e657720696d706c656d656e7461746960448201526d6f6e206973206e6f74205555505360901b6064820152608401610401565b6000805160206125ea8339815191528114610d6a5760405162461bcd60e51b815260206004820152602960248201527f45524331393637557067726164653a20756e737570706f727465642070726f786044820152681a58589b195555525160ba1b6064820152608401610401565b50610c2a83838361132e565b600080610d84848685611353565b9050856000805b828110156111095760007f000000000000000000000000569c785b4744e582c65a12827726fc03c8d08a4a6001600160a01b03166382fe1bcc8c8c85818110610dd657610dd6612286565b9050602002016020810190610deb9190611ebc565b6040516001600160e01b031960e084901b1681526001600160a01b03909116600482015260240160206040518083038186803b158015610e2a57600080fd5b505afa158015610e3e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e6291906123de565b9050876001600160a01b03167f000000000000000000000000569c785b4744e582c65a12827726fc03c8d08a4a6001600160a01b0316636352211e836040518263ffffffff1660e01b8152600401610ebc91815260200190565b60206040518083038186803b158015610ed457600080fd5b505afa158015610ee8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f0c9190612392565b6001600160a01b031614610f455760405163871d7af360e01b81526001600160a01b038916600482015260248101829052604401610401565b600080610f79838a8f8f88818110610f5f57610f5f612286565b9050602002016020810190610f749190611ebc565b6116e6565b604051636eb41f7760e01b81526004810186905291935091507f000000000000000000000000569c785b4744e582c65a12827726fc03c8d08a4a6001600160a01b031690636eb41f779060240160206040518083038186803b158015610fde57600080fd5b505afa158015610ff2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110169190612377565b6110f35761104d8d8d8681811061102f5761102f612286565b90506020020160208101906110449190611ebc565b82898c866117df565b60405163093af6a960e01b8152600060048201526024810184905260026044820152606481018890526001600160a01b038b811660848301527f00000000000000000000000064d5192f03bd98db1de2aa8b4abac5419eac32ce169063093af6a99060a401600060405180830381600087803b1580156110cc57600080fd5b505af11580156110e0573d6000803e3d6000fd5b5050505084806110ef906122f9565b9550505b5050508080611101906122f9565b915050610d8b565b50827fe179f684b4b38725831cb54955bc8284389f0b5279e7f4104127a95036efda028261113781866123f7565b6040805192835260208301919091528a159082015260600160405180910390a25090979650505050505050565b6060610aa0838360405180606001604052806027815260200161260a6027913961198a565b600054610100900460ff16158080156111a95750600054600160ff909116105b806111c35750303b1580156111c3575060005460ff166001145b6111df5760405162461bcd60e51b815260040161040190612314565b6000805460ff191660011790558015611202576000805461ff0019166101001790555b61120b82611a28565b610816611b82565b600061121e83611bef565b8015610aa05750610aa08383611c22565b60007f0000000000000000000000004e29cea6d64be860f5eaba110686dcb585f393d66001600160a01b031633141561128d576000366112706014826123f7565b61127c9236929061240e565b61128591612438565b60601c905090565b503390565b6001600160a01b0381163b6112ff5760405162461bcd60e51b815260206004820152602d60248201527f455243313936373a206e657720696d706c656d656e746174696f6e206973206e60448201526c1bdd08184818dbdb9d1c9858dd609a1b6064820152608401610401565b6000805160206125ea83398151915280546001600160a01b0319166001600160a01b0392909216919091179055565b61133783611d01565b6000825111806113445750805b15610c2a576104158383611d41565b60008261149b57600082815261012d602090815260408083206001600160a01b0388168452909152902054156113c557600082815261012d602090815260408083206001600160a01b03881684529091529081902054905163c41714c560e01b81526004810191909152602401610401565b604051630eb9a5f960e11b81526001600160a01b038581166004830152602482018490526000917f000000000000000000000000fd745747ec40b439fee9248ae9d57eb846f7ebf590911690631d734bf290604401602060405180830381600087803b15801561143457600080fd5b505af1158015611448573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061146c91906123de565b600084815261012d602090815260408083206001600160a01b038a16845290915290208190559150610aa09050565b836001600160a01b03167f000000000000000000000000fd745747ec40b439fee9248ae9d57eb846f7ebf56001600160a01b0316636352211e856040518263ffffffff1660e01b81526004016114f391815260200190565b60206040518083038186803b15801561150b57600080fd5b505afa15801561151f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115439190612392565b6001600160a01b03161461157c5760405163070ef60f60e51b81526001600160a01b038516600482015260248101849052604401610401565b60405163e873c9dd60e01b81526004810184905282907f000000000000000000000000fd745747ec40b439fee9248ae9d57eb846f7ebf56001600160a01b03169063e873c9dd9060240160206040518083038186803b1580156115de57600080fd5b505afa1580156115f2573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061161691906123de565b146116de5760405163e873c9dd60e01b81526004810184905282907f000000000000000000000000fd745747ec40b439fee9248ae9d57eb846f7ebf56001600160a01b03169063e873c9dd9060240160206040518083038186803b15801561167d57600080fd5b505afa158015611691573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116b591906123de565b60405163702c9cbf60e11b81526004810192909252602482015260448101849052606401610401565b509092915050565b60606000806000807f000000000000000000000000569c785b4744e582c65a12827726fc03c8d08a4a6001600160a01b03166337fea0e0896040518263ffffffff1660e01b815260040161173c91815260200190565b60006040518083038186803b15801561175457600080fd5b505afa158015611768573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052611790919081019061246d565b9550509450945050508683146117d257604051635afe552560e01b815260048101889052602481018490526001600160a01b0387166044820152606401610401565b9097909650945050505050565b7f000000000000000000000000fd745747ec40b439fee9248ae9d57eb846f7ebf56001600160a01b031663474dd8216040518060a00160405280886001600160a01b031681526020018681526020018581526020018481526020014281525086600014156040518363ffffffff1660e01b815260040161186092919061252c565b600060405180830381600087803b15801561187a57600080fd5b505af115801561188e573d6000803e3d6000fd5b50506040516320bf86f360e21b81526001600160a01b0388811660048301527f000000000000000000000000569c785b4744e582c65a12827726fc03c8d08a4a1692506339c7ac21915082906382fe1bcc9060240160206040518083038186803b1580156118fb57600080fd5b505afa15801561190f573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061193391906123de565b6040518263ffffffff1660e01b815260040161195191815260200190565b600060405180830381600087803b15801561196b57600080fd5b505af115801561197f573d6000803e3d6000fd5b505050505050505050565b60606001600160a01b0384163b6119b35760405162461bcd60e51b815260040161040190612587565b600080856001600160a01b0316856040516119ce91906125cd565b600060405180830381855af49150503d8060008114611a09576040519150601f19603f3d011682016040523d82523d6000602084013e611a0e565b606091505b5091509150611a1e828286611de9565b9695505050505050565b600054610100900460ff1615808015611a485750600054600160ff909116105b80611a625750303b158015611a62575060005460ff166001145b611a7e5760405162461bcd60e51b815260040161040190612314565b6000805460ff191660011790558015611aa1576000805461ff0019166101001790555b611abb6001600160a01b038316637965db0b60e01b611213565b611af9576040516301a1fdbb60e41b815260206004820152600e60248201526d125058d8d95cdcd0dbdb9d1c9bdb60921b6044820152606401610401565b603380546001600160a01b0319166001600160a01b0384169081179091556040517fa5bc17e575e3b53b23d0e93e121a5a66d1de4d5edb4dfde6027b14d79b7f2b9c90600090a280156105c8576000805461ff0019169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb384740249890602001610854565b600054610100900460ff16611bed5760405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201526a6e697469616c697a696e6760a81b6064820152608401610401565b565b6000611c02826301ffc9a760e01b611c22565b801561078e5750611c1b826001600160e01b0319611c22565b1592915050565b604080516001600160e01b0319831660248083019190915282518083039091018152604490910182526020810180516001600160e01b03166301ffc9a760e01b179052905160009190829081906001600160a01b0387169061753090611c899086906125cd565b6000604051808303818686fa925050503d8060008114611cc5576040519150601f19603f3d011682016040523d82523d6000602084013e611cca565b606091505b5091509150602081511015611ce5576000935050505061078e565b818015611a1e575080806020019051810190611a1e9190612377565b611d0a81611292565b6040516001600160a01b038216907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b90600090a250565b60606001600160a01b0383163b611d6a5760405162461bcd60e51b815260040161040190612587565b600080846001600160a01b031684604051611d8591906125cd565b600060405180830381855af49150503d8060008114611dc0576040519150601f19603f3d011682016040523d82523d6000602084013e611dc5565b606091505b5091509150610695828260405180606001604052806027815260200161260a602791395b60608315611df8575081610aa0565b825115611e085782518084602001fd5b8160405162461bcd60e51b81526004016104019190612033565b6001600160a01b03811681146104f857600080fd5b600080600060408486031215611e4c57600080fd5b8335611e5781611e22565b9250602084013567ffffffffffffffff80821115611e7457600080fd5b818601915086601f830112611e8857600080fd5b813581811115611e9757600080fd5b876020828501011115611ea957600080fd5b6020830194508093505050509250925092565b600060208284031215611ece57600080fd5b8135610aa081611e22565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f1916810167ffffffffffffffff81118282101715611f1857611f18611ed9565b604052919050565b600067ffffffffffffffff821115611f3a57611f3a611ed9565b50601f01601f191660200190565b60008060408385031215611f5b57600080fd5b8235611f6681611e22565b9150602083013567ffffffffffffffff811115611f8257600080fd5b8301601f81018513611f9357600080fd5b8035611fa6611fa182611f20565b611eef565b818152866020838501011115611fbb57600080fd5b816020840160208301376000602083830101528093505050509250929050565b60005b83811015611ff6578181015183820152602001611fde565b838111156104155750506000910152565b6000815180845261201f816020860160208601611fdb565b601f01601f19169290920160200192915050565b602081526000610aa06020830184612007565b60008083601f84011261205857600080fd5b50813567ffffffffffffffff81111561207057600080fd5b6020830191508360208260051b850101111561208b57600080fd5b9250929050565b600080600080606085870312156120a857600080fd5b843567ffffffffffffffff8111156120bf57600080fd5b6120cb87828801612046565b90989097506020870135966040013595509350505050565b600080602083850312156120f657600080fd5b823567ffffffffffffffff81111561210d57600080fd5b61211985828601612046565b90969095509350505050565b6000602080830181845280855180835260408601915060408160051b870101925083870160005b8281101561217a57603f19888603018452612168858351612007565b9450928501929085019060010161214c565b5092979650505050505050565b60008060008060006080868803121561219f57600080fd5b853567ffffffffffffffff8111156121b657600080fd5b6121c288828901612046565b9096509450506020860135925060408601356121dd81611e22565b949793965091946060013592915050565b6020808252602c908201527f46756e6374696f6e206d7573742062652063616c6c6564207468726f7567682060408201526b19195b1959d85d1958d85b1b60a21b606082015260800190565b6020808252602c908201527f46756e6374696f6e206d7573742062652063616c6c6564207468726f7567682060408201526b6163746976652070726f787960a01b606082015260800190565b634e487b7160e01b600052603260045260246000fd5b6000808335601e198436030181126122b357600080fd5b83018035915067ffffffffffffffff8211156122ce57600080fd5b60200191503681900382131561208b57600080fd5b634e487b7160e01b600052601160045260246000fd5b600060001982141561230d5761230d6122e3565b5060010190565b6020808252602e908201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160408201526d191e481a5b9a5d1a585b1a5e995960921b606082015260800190565b8051801515811461237257600080fd5b919050565b60006020828403121561238957600080fd5b610aa082612362565b6000602082840312156123a457600080fd5b8151610aa081611e22565b60208152816020820152818360408301376000818301604090810191909152601f909201601f19160101919050565b6000602082840312156123f057600080fd5b5051919050565b600082821015612409576124096122e3565b500390565b6000808585111561241e57600080fd5b8386111561242b57600080fd5b5050820193919092039150565b6bffffffffffffffffffffffff1981358181169160148510156124655780818660140360031b1b83161692505b505092915050565b60008060008060008060c0878903121561248657600080fd5b61248f87612362565b9550602087015161249f81611e22565b60408801516060890151919650945067ffffffffffffffff8111156124c357600080fd5b8701601f810189136124d457600080fd5b80516124e2611fa182611f20565b8181528a60208385010111156124f757600080fd5b612508826020830160208601611fdb565b945061251991505060808801612362565b915060a087015190509295509295509295565b6040815260018060a01b03835116604082015260208301516060820152604083015160808201526000606084015160a08084015261256d60e0840182612007565b6080959095015160c0840152505090151560209091015290565b60208082526026908201527f416464726573733a2064656c65676174652063616c6c20746f206e6f6e2d636f6040820152651b9d1c9858dd60d21b606082015260800190565b600082516125df818460208701611fdb565b919091019291505056fe360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564a2646970667358221220610b5fb9d6f3d5a2fe2510d91cc519f34c230f5bf6ae131ceaf062bfaf2df9a964736f6c63430008090033

Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)

0000000000000000000000004e29cea6d64be860f5eaba110686dcb585f393d6000000000000000000000000569c785b4744e582c65a12827726fc03c8d08a4a000000000000000000000000fd745747ec40b439fee9248ae9d57eb846f7ebf500000000000000000000000064d5192f03bd98db1de2aa8b4abac5419eac32ce

-----Decoded View---------------
Arg [0] : _forwarder (address): 0x4E29Cea6D64be860f5eAba110686DcB585f393D6
Arg [1] : _scannerNodeRegistry (address): 0x569c785b4744E582c65A12827726FC03C8D08A4a
Arg [2] : _scannerPoolRegistry (address): 0xfD745747eC40B439feE9248Ae9D57EB846F7eBF5
Arg [3] : _stakeMigrator (address): 0x64d5192F03bD98dB1De2AA8B4abAC5419eaC32CE

-----Encoded View---------------
4 Constructor Arguments found :
Arg [0] : 0000000000000000000000004e29cea6d64be860f5eaba110686dcb585f393d6
Arg [1] : 000000000000000000000000569c785b4744e582c65a12827726fc03c8d08a4a
Arg [2] : 000000000000000000000000fd745747ec40b439fee9248ae9d57eb846f7ebf5
Arg [3] : 00000000000000000000000064d5192f03bd98db1de2aa8b4abac5419eac32ce


Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

Block Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading

Validator Index Block Amount
View All Withdrawals

Txn Hash Block Value Eth2 PubKey Valid
View All Deposits
[ Download: CSV Export  ]

A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.