ETH Price: $2,666.59 (+5.61%)

Transaction Decoder

Block:
18771724 at Dec-12-2023 05:54:47 PM +UTC
Transaction Fee:
0.008412454391543774 ETH $22.43
Gas Used:
160,082 Gas / 52.550907607 Gwei

Emitted Events:

230 TransparentUpgradeableProxy.0xd1ad6f13ecf90412417f16481b7455bda8aa6ec0133a68cdab069f9b7bd623f9( 0xd1ad6f13ecf90412417f16481b7455bda8aa6ec0133a68cdab069f9b7bd623f9, 0x0000000000000000000000006fb8d531294cdd7d664d25589917196975444310, 0000000000000000000000000000000000000000000000000000000000000001, 0000000000000000000000000000000000000000000000000000000000000001 )
231 TransparentUpgradeableProxy.0xdf9a51129930649214df58f542322c245372c6f2fa6fc3c9b40d927c597cd230( 0xdf9a51129930649214df58f542322c245372c6f2fa6fc3c9b40d927c597cd230, 0x0000000000000000000000006fb8d531294cdd7d664d25589917196975444310, 0000000000000000000000000000000000000000000000000000000000000001, 0000000000000000000000000000000000000000000000000000000065733275 )

Account State Difference:

  Address   Before After State Difference Code
2.578249466670603282 Eth2.578265474870603282 Eth0.0000160082
0x4B5057B2...fF2FDaCad 5.423960367000464765 Eth5.445732020543771845 Eth0.02177165354330708
0x6Fb8d531...975444310
0.433856127944461016 Eth
Nonce: 717
0.403672020009610162 Eth
Nonce: 718
0.030184107934850854
0xC549d87E...4f7CA86eb
(StrongBlock: NodePack Proxy)

Execution Trace

ETH 0.02488188976377952 TransparentUpgradeableProxy.c290d691( )
  • ETH 0.02488188976377952 NodePackV7.pay( _packType=1 ) => ( 21771653543307080 )
    • TransparentUpgradeableProxy.958434d2( )
      • StrongNFTPackBonusV2.getBonus( _entity=0x6Fb8d531294Cdd7D664D25589917196975444310, _packType=1, _from=1699640111, _to=1702403687 ) => ( 0 )
        • TransparentUpgradeableProxy.b48f0428( )
          • NodePackV7.getPackId( _entity=0x6Fb8d531294Cdd7D664D25589917196975444310, _packType=1 ) => ( 0x6FB8D531294CDD7D664D25589917196975444310000000010000000000000001 )
          • ETH 0.02177165354330708 0x4b5057b2c87ec9e7c047fb00c0e406dff2fdacad.CALL( )
          • ETH 0.00311023622047244 0x6fb8d531294cdd7d664d25589917196975444310.CALL( )
            pay[NodePackV7 (ln:299)]
            File 1 of 4: TransparentUpgradeableProxy
            // SPDX-License-Identifier: MIT
            pragma solidity ^0.7.0;
            /**
             * @dev This abstract contract provides a fallback function that delegates all calls to another contract using the EVM
             * instruction `delegatecall`. We refer to the second contract as the _implementation_ behind the proxy, and it has to
             * be specified by overriding the virtual {_implementation} function.
             * 
             * Additionally, delegation to the implementation can be triggered manually through the {_fallback} function, or to a
             * different contract through the {_delegate} function.
             * 
             * The success and return data of the delegated call will be returned back to the caller of the proxy.
             */
            abstract contract Proxy {
                /**
                 * @dev Delegates the current call to `implementation`.
                 * 
                 * This function does not return to its internall call site, it will return directly to the external caller.
                 */
                function _delegate(address implementation) internal {
                    // solhint-disable-next-line no-inline-assembly
                    assembly {
                        // Copy msg.data. We take full control of memory in this inline assembly
                        // block because it will not return to Solidity code. We overwrite the
                        // Solidity scratch pad at memory position 0.
                        calldatacopy(0, 0, calldatasize())
                        // Call the implementation.
                        // out and outsize are 0 because we don't know the size yet.
                        let result := delegatecall(gas(), implementation, 0, calldatasize(), 0, 0)
                        // Copy the returned data.
                        returndatacopy(0, 0, returndatasize())
                        switch result
                        // delegatecall returns 0 on error.
                        case 0 { revert(0, returndatasize()) }
                        default { return(0, returndatasize()) }
                    }
                }
                /**
                 * @dev This is a virtual function that should be overriden so it returns the address to which the fallback function
                 * and {_fallback} should delegate.
                 */
                function _implementation() internal virtual view returns (address);
                /**
                 * @dev Delegates the current call to the address returned by `_implementation()`.
                 * 
                 * This function does not return to its internall call site, it will return directly to the external caller.
                 */
                function _fallback() internal {
                    _beforeFallback();
                    _delegate(_implementation());
                }
                /**
                 * @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if no other
                 * function in the contract matches the call data.
                 */
                fallback () payable external {
                    _fallback();
                }
                /**
                 * @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if call data
                 * is empty.
                 */
                receive () payable external {
                    _fallback();
                }
                /**
                 * @dev Hook that is called before falling back to the implementation. Can happen as part of a manual `_fallback`
                 * call, or as part of the Solidity `fallback` or `receive` functions.
                 * 
                 * If overriden should call `super._beforeFallback()`.
                 */
                function _beforeFallback() internal virtual {
                }
            }
            // SPDX-License-Identifier: MIT
            pragma solidity ^0.7.0;
            import "./UpgradeableProxy.sol";
            /**
             * @dev This contract implements a proxy that is upgradeable by an admin.
             * 
             * To avoid https://medium.com/nomic-labs-blog/malicious-backdoors-in-ethereum-proxies-62629adf3357[proxy selector
             * clashing], which can potentially be used in an attack, this contract uses the
             * https://blog.openzeppelin.com/the-transparent-proxy-pattern/[transparent proxy pattern]. This pattern implies two
             * things that go hand in hand:
             * 
             * 1. If any account other than the admin calls the proxy, the call will be forwarded to the implementation, even if
             * that call matches one of the admin functions exposed by the proxy itself.
             * 2. If the admin calls the proxy, it can access the admin functions, but its calls will never be forwarded to the
             * implementation. If the admin tries to call a function on the implementation it will fail with an error that says
             * "admin cannot fallback to proxy target".
             * 
             * These properties mean that the admin account can only be used for admin actions like upgrading the proxy or changing
             * the admin, so it's best if it's a dedicated account that is not used for anything else. This will avoid headaches due
             * to sudden errors when trying to call a function from the proxy implementation.
             * 
             * Our recommendation is for the dedicated account to be an instance of the {ProxyAdmin} contract. If set up this way,
             * you should think of the `ProxyAdmin` instance as the real administrative inerface of your proxy.
             */
            contract TransparentUpgradeableProxy is UpgradeableProxy {
                /**
                 * @dev Initializes an upgradeable proxy managed by `_admin`, backed by the implementation at `_logic`, and
                 * optionally initialized with `_data` as explained in {UpgradeableProxy-constructor}.
                 */
                constructor(address initialLogic, address initialAdmin, bytes memory _data) payable UpgradeableProxy(initialLogic, _data) {
                    assert(_ADMIN_SLOT == bytes32(uint256(keccak256("eip1967.proxy.admin")) - 1));
                    _setAdmin(initialAdmin);
                }
                /**
                 * @dev Emitted when the admin account has changed.
                 */
                event AdminChanged(address previousAdmin, address newAdmin);
                /**
                 * @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 private constant _ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;
                /**
                 * @dev Modifier used internally that will delegate the call to the implementation unless the sender is the admin.
                 */
                modifier ifAdmin() {
                    if (msg.sender == _admin()) {
                        _;
                    } else {
                        _fallback();
                    }
                }
                /**
                 * @dev Returns the current admin.
                 * 
                 * NOTE: Only the admin can call this function. See {ProxyAdmin-getProxyAdmin}.
                 * 
                 * TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using the
                 * https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call.
                 * `0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103`
                 */
                function admin() external ifAdmin returns (address) {
                    return _admin();
                }
                /**
                 * @dev Returns the current implementation.
                 * 
                 * NOTE: Only the admin can call this function. See {ProxyAdmin-getProxyImplementation}.
                 * 
                 * TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using the
                 * https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call.
                 * `0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc`
                 */
                function implementation() external ifAdmin returns (address) {
                    return _implementation();
                }
                /**
                 * @dev Changes the admin of the proxy.
                 * 
                 * Emits an {AdminChanged} event.
                 * 
                 * NOTE: Only the admin can call this function. See {ProxyAdmin-changeProxyAdmin}.
                 */
                function changeAdmin(address newAdmin) external ifAdmin {
                    require(newAdmin != address(0), "TransparentUpgradeableProxy: new admin is the zero address");
                    emit AdminChanged(_admin(), newAdmin);
                    _setAdmin(newAdmin);
                }
                /**
                 * @dev Upgrade the implementation of the proxy.
                 * 
                 * NOTE: Only the admin can call this function. See {ProxyAdmin-upgrade}.
                 */
                function upgradeTo(address newImplementation) external ifAdmin {
                    _upgradeTo(newImplementation);
                }
                /**
                 * @dev Upgrade the implementation of the proxy, and then call a function from the new implementation as specified
                 * by `data`, which should be an encoded function call. This is useful to initialize new storage variables in the
                 * proxied contract.
                 * 
                 * NOTE: Only the admin can call this function. See {ProxyAdmin-upgradeAndCall}.
                 */
                function upgradeToAndCall(address newImplementation, bytes calldata data) external payable ifAdmin {
                    _upgradeTo(newImplementation);
                    // solhint-disable-next-line avoid-low-level-calls
                    (bool success,) = newImplementation.delegatecall(data);
                    require(success);
                }
                /**
                 * @dev Returns the current admin.
                 */
                function _admin() internal view returns (address adm) {
                    bytes32 slot = _ADMIN_SLOT;
                    // solhint-disable-next-line no-inline-assembly
                    assembly {
                        adm := sload(slot)
                    }
                }
                /**
                 * @dev Stores a new address in the EIP1967 admin slot.
                 */
                function _setAdmin(address newAdmin) private {
                    bytes32 slot = _ADMIN_SLOT;
                    // solhint-disable-next-line no-inline-assembly
                    assembly {
                        sstore(slot, newAdmin)
                    }
                }
                /**
                 * @dev Makes sure the admin cannot access the fallback function. See {Proxy-_beforeFallback}.
                 */
                function _beforeFallback() internal override virtual {
                    require(msg.sender != _admin(), "TransparentUpgradeableProxy: admin cannot fallback to proxy target");
                    super._beforeFallback();
                }
            }
            // SPDX-License-Identifier: MIT
            pragma solidity ^0.7.0;
            import "./Proxy.sol";
            import "../utils/Address.sol";
            /**
             * @dev This contract implements an upgradeable proxy. It is upgradeable because calls are delegated to an
             * implementation address that can be changed. This address is stored in storage in the location specified by
             * https://eips.ethereum.org/EIPS/eip-1967[EIP1967], so that it doesn't conflict with the storage layout of the
             * implementation behind the proxy.
             * 
             * Upgradeability is only provided internally through {_upgradeTo}. For an externally upgradeable proxy see
             * {TransparentUpgradeableProxy}.
             */
            contract UpgradeableProxy is Proxy {
                /**
                 * @dev Initializes the upgradeable proxy with an initial implementation specified by `_logic`.
                 * 
                 * If `_data` is nonempty, it's used as data in a delegate call to `_logic`. This will typically be an encoded
                 * function call, and allows initializating the storage of the proxy like a Solidity constructor.
                 */
                constructor(address _logic, bytes memory _data) payable {
                    assert(_IMPLEMENTATION_SLOT == bytes32(uint256(keccak256("eip1967.proxy.implementation")) - 1));
                    _setImplementation(_logic);
                    if(_data.length > 0) {
                        // solhint-disable-next-line avoid-low-level-calls
                        (bool success,) = _logic.delegatecall(_data);
                        require(success);
                    }
                }
                /**
                 * @dev Emitted when the implementation is upgraded.
                 */
                event Upgraded(address indexed implementation);
                /**
                 * @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 private constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
                /**
                 * @dev Returns the current implementation address.
                 */
                function _implementation() internal override view returns (address impl) {
                    bytes32 slot = _IMPLEMENTATION_SLOT;
                    // solhint-disable-next-line no-inline-assembly
                    assembly {
                        impl := sload(slot)
                    }
                }
                /**
                 * @dev Upgrades the proxy to a new implementation.
                 * 
                 * Emits an {Upgraded} event.
                 */
                function _upgradeTo(address newImplementation) internal {
                    _setImplementation(newImplementation);
                    emit Upgraded(newImplementation);
                }
                /**
                 * @dev Stores a new address in the EIP1967 implementation slot.
                 */
                function _setImplementation(address newImplementation) private {
                    require(Address.isContract(newImplementation), "UpgradeableProxy: new implementation is not a contract");
                    bytes32 slot = _IMPLEMENTATION_SLOT;
                    // solhint-disable-next-line no-inline-assembly
                    assembly {
                        sstore(slot, newImplementation)
                    }
                }
            }
            // SPDX-License-Identifier: MIT
            pragma solidity ^0.7.0;
            /**
             * @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
                 * ====
                 */
                function isContract(address account) internal view returns (bool) {
                    // According to EIP-1052, 0x0 is the value returned for not-yet created accounts
                    // and 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 is returned
                    // for accounts without code, i.e. `keccak256('')`
                    bytes32 codehash;
                    bytes32 accountHash = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470;
                    // solhint-disable-next-line no-inline-assembly
                    assembly { codehash := extcodehash(account) }
                    return (codehash != accountHash && codehash != 0x0);
                }
                /**
                 * @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");
                    // solhint-disable-next-line avoid-low-level-calls, avoid-call-value
                    (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");
                    return _functionCallWithValue(target, data, value, errorMessage);
                }
                function _functionCallWithValue(address target, bytes memory data, uint256 weiValue, string memory errorMessage) private returns (bytes memory) {
                    require(isContract(target), "Address: call to non-contract");
                    // solhint-disable-next-line avoid-low-level-calls
                    (bool success, bytes memory returndata) = target.call{ value: weiValue }(data);
                    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
                            // solhint-disable-next-line no-inline-assembly
                            assembly {
                                let returndata_size := mload(returndata)
                                revert(add(32, returndata), returndata_size)
                            }
                        } else {
                            revert(errorMessage);
                        }
                    }
                }
            }
            

            File 2 of 4: NodePackV7
            // SPDX-License-Identifier: MIT
            pragma solidity 0.8.9;
            import "./lib/AdminAccess.sol";
            import "./interfaces/IERC20.sol";
            import "./interfaces/INodePackV3.sol";
            import "./interfaces/IStrongPool.sol";
            import "./interfaces/IStrongNFTPackBonus.sol";
            import "./lib/InternalCalls.sol";
            import "./lib/SbMath.sol";
            contract NodePackV7 is AdminAccess, INodePackV3, InternalCalls {
              uint constant public PACK_TYPE_NODE_REWARD_LIFETIME = 0;
              uint constant public PACK_TYPE_NODE_REWARD_PER_SECOND = 1;
              uint constant public PACK_TYPE_FEE_STRONG = 2;
              uint constant public PACK_TYPE_FEE_CREATE = 3;
              uint constant public PACK_TYPE_FEE_RECURRING = 4;
              uint constant public PACK_TYPE_FEE_CLAIMING_NUMERATOR = 5;
              uint constant public PACK_TYPE_FEE_CLAIMING_DENOMINATOR = 6;
              uint constant public PACK_TYPE_RECURRING_CYCLE_SECONDS = 7;
              uint constant public PACK_TYPE_GRACE_PERIOD_SECONDS = 8;
              uint constant public PACK_TYPE_PAY_CYCLES_LIMIT = 9;
              uint constant public PACK_TYPE_NODES_LIMIT = 10;
              event Created(address indexed entity, uint packType, uint nodesCount, bool usedCredit, uint timestamp, address migratedFrom);
              event AddedNodes(address indexed entity, uint packType, uint nodesCount, uint totalNodesCount, bool usedCredit, uint timestamp, address migratedFrom);
              event MigratedNodes(address indexed entity, uint packType, uint nodesCount, uint lastPaidAt, uint rewardsDue, uint totalClaimed, address migratedFrom, uint timestamp);
              event MaturedNodes(address indexed entity, uint packType, uint maturedCount);
              event Paid(address indexed entity, uint packType, uint timestamp);
              event Claimed(address indexed entity, uint packType, uint reward);
              event SetNodeFeeCollector(address payable collector);
              event SetFeeCollector(address payable collector);
              event SetTakeStrongBips(uint bips);
              event SetNFTBonusContract(address strongNFTBonus);
              event SetServiceContractEnabled(address service, bool enabled);
              event SetPackTypeActive(uint packType, bool active);
              event SetPackTypeSetting(uint packType, uint settingId, uint value);
              event SetPackTypeHasSettings(uint packType, bool hasSettings);
              IERC20 public strongToken;
              IStrongNFTPackBonus public strongNFTBonus;
              uint public totalNodes;
              uint public totalMaturedNodes;
              uint public totalPacks;
              uint public totalPackTypes;
              uint public takeStrongBips;
              address payable public claimFeeCollector;
              address payable public nodeFeeCollector;
              mapping(address => uint) public entityNodeCount;
              mapping(address => uint) public entityCreditUsed;
              mapping(bytes => uint) public entityPackCreatedAt;
              mapping(bytes => uint) public entityPackLastPaidAt;
              mapping(bytes => uint) public entityPackLastClaimedAt;
              mapping(bytes => uint) public entityPackTotalNodeCount;
              mapping(bytes => uint) public entityPackMaturedNodeCount;
              mapping(bytes => uint) public entityPackRewardDue;
              mapping(bytes => uint) public entityPackClaimedTotal;
              mapping(bytes => uint) public entityPackClaimedMatured;
              mapping(uint => bool) public packTypeActive;
              mapping(uint => bool) public packTypeHasSettings;
              mapping(uint => mapping(uint => uint)) public packTypeSettings;
              mapping(address => bool) private serviceContractEnabled;
              function init(
                IERC20 _strongToken,
                IStrongNFTPackBonus _strongNFTBonus,
                address payable _nodeFeeCollector,
                address payable _claimFeeCollector
              ) external onlyRole(adminControl.SUPER_ADMIN()) {
                require(_claimFeeCollector != address(0), "no address");
                strongToken = _strongToken;
                strongNFTBonus = _strongNFTBonus;
                nodeFeeCollector = _nodeFeeCollector;
                claimFeeCollector = _claimFeeCollector;
                InternalCalls.init();
              }
              //
              // Getters
              // -------------------------------------------------------------------------------------------------------------------
              function canPackBePaid(address _entity, uint _packType) public view returns (bool) {
                return doesPackExist(_entity, _packType) && !hasPackExpired(_entity, _packType) && !hasMaxPayments(_entity, _packType);
              }
              function doesPackExist(address _entity, uint _packType) public view returns (bool) {
                return entityPackLastPaidAt[getPackId(_entity, _packType)] > 0;
              }
              function isPackPastDue(address _entity, uint _packType) public view returns (bool) {
                bytes memory id = getPackId(_entity, _packType);
                uint lastPaidAt = entityPackLastPaidAt[id];
                return block.timestamp > (lastPaidAt + getRecurringPaymentCycle(_packType));
              }
              function hasMaxPayments(address _entity, uint _packType) public view returns (bool) {
                bytes memory id = getPackId(_entity, _packType);
                uint lastPaidAt = entityPackLastPaidAt[id];
                uint recurringPaymentCycle = getRecurringPaymentCycle(_packType);
                uint limit = block.timestamp + recurringPaymentCycle * getPayCyclesLimit(_packType);
                return lastPaidAt + recurringPaymentCycle >= limit;
              }
              function hasPackExpired(address _entity, uint _packType) public view returns (bool) {
                bytes memory id = getPackId(_entity, _packType);
                uint lastPaidAt = entityPackLastPaidAt[id];
                if (lastPaidAt == 0) return true;
                return block.timestamp > (lastPaidAt + getRecurringPaymentCycle(_packType) + getGracePeriod(_packType));
              }
              function getClaimingFee(address _entity, uint _packType, uint _timestamp) public view returns (uint) {
                return getRewardAt(_entity, _packType, _timestamp, true) * getClaimingFeeNumerator(_packType) / getClaimingFeeDenominator(_packType);
              }
              function getPacksClaimingFee(address _entity, uint _timestamp) external view returns (uint) {
                uint fee = 0;
                for (uint packType = 1; packType <= totalPackTypes; packType++) {
                  fee = fee + getClaimingFee(_entity, packType, _timestamp);
                }
                return fee;
              }
              function getPackId(address _entity, uint _packType) public pure returns (bytes memory) {
                uint id = _packType != 0 ? _packType : 1;
                return abi.encodePacked(_entity, uint32(id), uint64(1));
              }
              function getEntityPackTotalNodeCount(address _entity, uint _packType) external view returns (uint) {
                return entityPackTotalNodeCount[getPackId(_entity, _packType)];
              }
              function getEntityPackMaturedNodeCount(address _entity, uint _packType) external view returns (uint) {
                return entityPackMaturedNodeCount[getPackId(_entity, _packType)];
              }
              function getEntityPackActiveNodeCount(address _entity, uint _packType) public view returns (uint) {
                bytes memory id = getPackId(_entity, _packType);
                return entityPackTotalNodeCount[id] - entityPackMaturedNodeCount[id];
              }
              function getEntityPackLifetimeRewards(address _entity, uint _packType) public view returns (uint) {
                return getNodeRewardLifetime(_packType) * entityPackTotalNodeCount[getPackId(_entity, _packType)];
              }
              function getEntityPackClaimedMaturedRewards(address _entity, uint _packType) public view returns (uint) {
                return entityPackClaimedMatured[getPackId(_entity, _packType)];
              }
              function getEntityPackClaimedTotalRewards(address _entity, uint _packType) public view returns (uint) {
                return entityPackClaimedTotal[getPackId(_entity, _packType)];
              }
              function getEntityPackAccruedTotalRewards(address _entity, uint _packType) public view returns (uint) {
                return entityPackClaimedTotal[getPackId(_entity, _packType)] + getRewardAt(_entity, _packType, block.timestamp, true);
              }
              function getPackLastPaidAt(address _entity, uint _packType) external view returns (uint) {
                return entityPackLastPaidAt[getPackId(_entity, _packType)];
              }
              function getNodeCreateFee(address _entity, uint _packType) public view returns (uint) {
                uint fee = getCreatingFeeInWei(_packType);
                uint lastPaidAt = entityPackLastPaidAt[getPackId(_entity, _packType)];
                if (lastPaidAt == 0) return fee;
                if (isPackPastDue(_entity, _packType)) return fee;
                if (hasPackExpired(_entity, _packType)) return 0;
                uint payCycleSeconds = getRecurringPaymentCycle(_packType);
                uint dueInSeconds = lastPaidAt + payCycleSeconds - block.timestamp;
                return dueInSeconds * fee / payCycleSeconds;
              }
              function getRecurringFee(address _entity, uint _packType) public view returns (uint) {
                return getRecurringFeeInWei(_packType) * getEntityPackActiveNodeCount(_entity, _packType);
              }
              function getPacksRecurringFee(address _entity) external view returns (uint) {
                uint fee = 0;
                for (uint packType = 1; packType <= totalPackTypes; packType++) {
                  if (canPackBePaid(_entity, packType)) fee = fee + getRecurringFee(_entity, packType);
                }
                return fee;
              }
              function getReward(address _entity, uint _packType) external view returns (uint) {
                return getRewardAt(_entity, _packType, block.timestamp, true);
              }
              function getRewardAt(address _entity, uint _packType, uint _timestamp, bool _addBonus) public view returns (uint) {
                bytes memory id = getPackId(_entity, _packType);
                uint lastClaimedAt = entityPackLastClaimedAt[id];
                uint registeredAt = entityPackCreatedAt[id];
                if (!doesPackExist(_entity, _packType)) return 0;
                if (hasPackExpired(_entity, _packType)) return 0;
                if (_timestamp > block.timestamp) return 0;
                if (_timestamp < lastClaimedAt) return 0;
                if (_timestamp <= registeredAt) return 0;
                uint secondsPassed = lastClaimedAt > 0 ? _timestamp - lastClaimedAt : _timestamp - registeredAt;
                uint maxReward = getEntityPackLifetimeRewards(_entity, _packType);
                uint reward = secondsPassed * getNodeRewardPerSecond(_packType) * getEntityPackActiveNodeCount(_entity, _packType);
                uint bonus = _addBonus ? getBonusAt(_entity, _packType, _timestamp) : 0;
                uint totalReward = reward + bonus + entityPackRewardDue[id];
                if (entityPackClaimedTotal[id] >= maxReward) {
                  return 0;
                }
                if ((entityPackClaimedTotal[id] + totalReward) >= maxReward) {
                  totalReward = maxReward - entityPackClaimedTotal[id];
                }
                return totalReward;
              }
              function getBonusAt(address _entity, uint _packType, uint _timestamp) public view returns (uint) {
                if (address(strongNFTBonus) == address(0)) return 0;
                bytes memory id = getPackId(_entity, _packType);
                uint lastClaimedAt = entityPackLastClaimedAt[id] != 0 ? entityPackLastClaimedAt[id] : entityPackCreatedAt[id];
                return strongNFTBonus.getBonus(_entity, _packType, lastClaimedAt, _timestamp);
              }
              function getEntityRewards(address _entity, uint _timestamp) public view returns (uint) {
                uint reward = 0;
                for (uint packType = 1; packType <= totalPackTypes; packType++) {
                  reward = reward + getRewardAt(_entity, packType, _timestamp > 0 ? _timestamp : block.timestamp, true);
                }
                return reward;
              }
              function getEntityCreditAvailable(address _entity, uint _timestamp) public view returns (uint) {
                return getEntityRewards(_entity, _timestamp) - entityCreditUsed[_entity];
              }
              function getRewardBalance() external view returns (uint) {
                return strongToken.balanceOf(address(this));
              }
              //
              // Actions
              // -------------------------------------------------------------------------------------------------------------------
              function create(uint _packType, uint _nodeCount, bool _useCredit) external payable {
                uint fee = getNodeCreateFee(msg.sender, _packType) * _nodeCount;
                uint strongFee = getStrongFeeInWei(_packType) * _nodeCount;
                uint packTypeLimit = getNodesLimit(_packType);
                uint timestamp = block.timestamp;
                bytes memory id = getPackId(msg.sender, _packType);
                require(packTypeActive[_packType], "invalid type");
                require(packTypeLimit == 0 || (entityPackTotalNodeCount[id] + _nodeCount) <= packTypeLimit, "over limit");
                require(_nodeCount >= 1, "invalid node count");
                require(msg.value >= fee, "invalid fee");
                if (address(strongNFTBonus) != address(0)) {
                  strongNFTBonus.setEntityPackBonusSaved(msg.sender, _packType);
                }
                totalNodes += _nodeCount;
                entityNodeCount[msg.sender] += _nodeCount;
                if (entityPackTotalNodeCount[id] == 0) {
                  entityPackCreatedAt[id] = timestamp;
                  entityPackLastPaidAt[id] = timestamp;
                  entityPackTotalNodeCount[id] += _nodeCount;
                  totalPacks += 1;
                  emit Created(msg.sender, _packType, _nodeCount, _useCredit, block.timestamp, address(0));
                }
                else {
                  require(!hasPackExpired(msg.sender, _packType), "pack expired");
                  updatePackState(msg.sender, _packType, true);
                  entityPackTotalNodeCount[id] += _nodeCount;
                  emit AddedNodes(msg.sender, _packType, _nodeCount, entityPackTotalNodeCount[id], _useCredit, block.timestamp, address(0));
                }
                if (_useCredit) {
                  require(getEntityCreditAvailable(msg.sender, block.timestamp) >= strongFee, "not enough");
                  entityCreditUsed[msg.sender] += strongFee;
                } else {
                  uint takeStrong = strongFee * takeStrongBips / 10000;
                  if (takeStrong > 0) {
                    require(strongToken.transferFrom(msg.sender, nodeFeeCollector, takeStrong), "transfer failed");
                  }
                  if (strongFee > takeStrong) {
                    require(strongToken.transferFrom(msg.sender, address(this), strongFee - takeStrong), "transfer failed");
                  }
                }
                sendValue(nodeFeeCollector, msg.value);
              }
              function claim(uint _packType, uint _timestamp, address _toStrongPool) public payable returns (uint) {
                address entity = msg.sender == address(strongNFTBonus) ? tx.origin : msg.sender;
                bytes memory id = getPackId(entity, _packType);
                uint lastClaimedAt = entityPackLastClaimedAt[id] != 0 ? entityPackLastClaimedAt[id] : entityPackCreatedAt[id];
                require(doesPackExist(entity, _packType), "doesnt exist");
                require(!hasPackExpired(entity, _packType), "pack expired");
                require(!isPackPastDue(entity, _packType), "past due");
                require(_timestamp <= block.timestamp, "bad timestamp");
                require(lastClaimedAt + 900 < _timestamp, "too soon");
                uint reward = getRewardAt(entity, _packType, _timestamp, true);
                require(reward > 0, "no reward");
                require(strongToken.balanceOf(address(this)) >= reward, "over balance");
                uint fee = reward * getClaimingFeeNumerator(_packType) / getClaimingFeeDenominator(_packType);
                require(msg.value >= fee, "invalid fee");
                entityPackLastClaimedAt[id] = _timestamp;
                entityPackClaimedTotal[id] += reward;
                entityPackRewardDue[id] = 0;
                emit Claimed(entity, _packType, reward);
                if (entityCreditUsed[msg.sender] > 0) {
                  if (entityCreditUsed[msg.sender] > reward) {
                    entityCreditUsed[msg.sender] = entityCreditUsed[msg.sender] - reward;
                    reward = 0;
                  } else {
                    reward = reward - entityCreditUsed[msg.sender];
                    entityCreditUsed[msg.sender] = 0;
                  }
                }
                updatePackState(msg.sender, _packType, false);
                if (address(strongNFTBonus) != address(0)) {
                  strongNFTBonus.resetEntityPackBonusSaved(id);
                }
                if (reward > 0) {
                  if (_toStrongPool != address(0)) IStrongPool(_toStrongPool).mineFor(entity, reward);
                  else require(strongToken.transfer(entity, reward), "transfer failed");
                }
                sendValue(claimFeeCollector, fee);
                if (isUserCall() && msg.value > fee) sendValue(payable(msg.sender), msg.value - fee);
                return fee;
              }
              function claimAll(uint _timestamp, address _toStrongPool) external payable makesInternalCalls {
                require(entityNodeCount[msg.sender] > 0, "no nodes");
                uint valueLeft = msg.value;
                for (uint packType = 1; packType <= totalPackTypes; packType++) {
                  uint reward = getRewardAt(msg.sender, packType, _timestamp, true);
                  if (reward > 0) {
                    require(valueLeft >= 0, "not enough");
                    uint paid = claim(packType, _timestamp, _toStrongPool);
                    valueLeft = valueLeft - paid;
                  }
                }
                if (valueLeft > 0) sendValue(payable(msg.sender), valueLeft);
              }
              function pay(uint _packType) public payable returns (uint) {
                require(canPackBePaid(msg.sender, _packType), "cant pay");
                updatePackState(msg.sender, _packType, true);
                bytes memory id = getPackId(msg.sender, _packType);
                uint fee = getRecurringFeeInWei(_packType) * getEntityPackActiveNodeCount(msg.sender, _packType);
                require(msg.value >= fee, "invalid fee");
                entityPackLastPaidAt[id] = entityPackLastPaidAt[id] + getRecurringPaymentCycle(_packType);
                emit Paid(msg.sender, _packType, entityPackLastPaidAt[id]);
                sendValue(nodeFeeCollector, fee);
                if (isUserCall() && msg.value > fee) sendValue(payable(msg.sender), msg.value - fee);
                return fee;
              }
              function payAll() external payable makesInternalCalls {
                require(entityNodeCount[msg.sender] > 0, "no packs");
                uint valueLeft = msg.value;
                for (uint packType = 1; packType <= totalPackTypes; packType++) {
                  if (!canPackBePaid(msg.sender, packType)) continue;
                  require(valueLeft > 0, "not enough");
                  uint paid = pay(packType);
                  valueLeft = valueLeft - paid;
                }
                if (valueLeft > 0) sendValue(payable(msg.sender), valueLeft);
              }
              //
              // Admin
              // -------------------------------------------------------------------------------------------------------------------
              function deposit(uint _amount) external onlyRole(adminControl.SUPER_ADMIN()) {
                require(_amount > 0);
                require(strongToken.transferFrom(msg.sender, address(this), _amount), "transfer failed");
              }
              function withdraw(address _destination, uint _amount) external onlyRole(adminControl.SUPER_ADMIN()) {
                require(_amount > 0);
                require(strongToken.balanceOf(address(this)) >= _amount, "over balance");
                require(strongToken.transfer(_destination, _amount), "transfer failed");
              }
              function approveStrongPool(IStrongPool _strongPool, uint _amount) external onlyRole(adminControl.SUPER_ADMIN()) {
                require(strongToken.approve(address(_strongPool), _amount), "approve failed");
              }
              function setNodeFeeCollector(address payable _nodeFeeCollector) external onlyRole(adminControl.SUPER_ADMIN()) {
                require(_nodeFeeCollector != address(0));
                nodeFeeCollector = _nodeFeeCollector;
                emit SetNodeFeeCollector(_nodeFeeCollector);
              }
              function setClaimFeeCollector(address payable _claimFeeCollector) external onlyRole(adminControl.SUPER_ADMIN()) {
                require(_claimFeeCollector != address(0));
                claimFeeCollector = _claimFeeCollector;
                emit SetFeeCollector(_claimFeeCollector);
              }
              function setNFTBonusContract(address _contract) external onlyRole(adminControl.SERVICE_ADMIN()) {
                strongNFTBonus = IStrongNFTPackBonus(_contract);
                emit SetNFTBonusContract(_contract);
              }
              function setTakeStrongBips(uint _bips) external onlyRole(adminControl.SUPER_ADMIN()) {
                require(_bips <= 10000, "invalid value");
                takeStrongBips = _bips;
                emit SetTakeStrongBips(_bips);
              }
              function updateEntityPackLastPaidAt(address _entity, uint _packType, uint _lastPaidAt) external onlyRole(adminControl.SERVICE_ADMIN()) {
                bytes memory id = getPackId(_entity, _packType);
                entityPackLastPaidAt[id] = _lastPaidAt;
              }
              //
              // Settings
              // -------------------------------------------------------------------------------------------------------------------
              function getCustomSettingOrDefaultIfZero(uint _packType, uint _setting) internal view returns (uint) {
                return packTypeHasSettings[_packType] && packTypeSettings[_packType][_setting] > 0
                ? packTypeSettings[_packType][_setting]
                : packTypeSettings[0][_setting];
              }
              function getNodeRewardLifetime(uint _packType) public view returns (uint) {
                return getCustomSettingOrDefaultIfZero(_packType, PACK_TYPE_NODE_REWARD_LIFETIME);
              }
              function getNodeRewardPerSecond(uint _packType) public view returns (uint) {
                return getCustomSettingOrDefaultIfZero(_packType, PACK_TYPE_NODE_REWARD_PER_SECOND);
              }
              function getClaimingFeeNumerator(uint _packType) public view returns (uint) {
                return getCustomSettingOrDefaultIfZero(_packType, PACK_TYPE_FEE_CLAIMING_NUMERATOR);
              }
              function getClaimingFeeDenominator(uint _packType) public view returns (uint) {
                return getCustomSettingOrDefaultIfZero(_packType, PACK_TYPE_FEE_CLAIMING_DENOMINATOR);
              }
              function getCreatingFeeInWei(uint _packType) public view returns (uint) {
                return getCustomSettingOrDefaultIfZero(_packType, PACK_TYPE_FEE_CREATE);
              }
              function getRecurringFeeInWei(uint _packType) public view returns (uint) {
                return getCustomSettingOrDefaultIfZero(_packType, PACK_TYPE_FEE_RECURRING);
              }
              function getStrongFeeInWei(uint _packType) public view returns (uint) {
                return getCustomSettingOrDefaultIfZero(_packType, PACK_TYPE_FEE_STRONG);
              }
              function getRecurringPaymentCycle(uint _packType) public view returns (uint) {
                return getCustomSettingOrDefaultIfZero(_packType, PACK_TYPE_RECURRING_CYCLE_SECONDS);
              }
              function getGracePeriod(uint _packType) public view returns (uint) {
                return getCustomSettingOrDefaultIfZero(_packType, PACK_TYPE_GRACE_PERIOD_SECONDS);
              }
              function getPayCyclesLimit(uint _packType) public view returns (uint) {
                return getCustomSettingOrDefaultIfZero(_packType, PACK_TYPE_PAY_CYCLES_LIMIT);
              }
              function getNodesLimit(uint _packType) public view returns (uint) {
                return getCustomSettingOrDefaultIfZero(_packType, PACK_TYPE_NODES_LIMIT);
              }
              // -------------------------------------------------------------------------------------------------------------------
              function setPackTypeActive(uint _packType, bool _active) external onlyRole(adminControl.SERVICE_ADMIN()) {
                // Pack type 0 is being used as a placeholder for the default settings for pack types that don't have custom ones,
                // So it shouldn't be activated and used to create nodes
                require(_packType > 0, "invalid type");
                packTypeActive[_packType] = _active;
                if (totalPackTypes < _packType && _active) {
                  totalPackTypes = _packType;
                }
                emit SetPackTypeActive(_packType, _active);
              }
              function setPackTypeHasSettings(uint _packType, bool _hasSettings) external onlyRole(adminControl.SERVICE_ADMIN()) {
                packTypeHasSettings[_packType] = _hasSettings;
                emit SetPackTypeHasSettings(_packType, _hasSettings);
              }
              function setPackTypeSetting(uint _packType, uint _settingId, uint _value) external onlyRole(adminControl.SERVICE_ADMIN()) {
                packTypeHasSettings[_packType] = true;
                packTypeSettings[_packType][_settingId] = _value;
                emit SetPackTypeSetting(_packType, _settingId, _value);
              }
              function setServiceContractEnabled(address _contract, bool _enabled) external onlyRole(adminControl.SERVICE_ADMIN()) {
                serviceContractEnabled[_contract] = _enabled;
                emit SetServiceContractEnabled(_contract, _enabled);
              }
              // -------------------------------------------------------------------------------------------------------------------
              function sendValue(address payable recipient, uint amount) internal {
                require(address(this).balance >= amount, "insufficient balance");
                // solhint-disable-next-line avoid-low-level-calls, avoid-call-value
                (bool success,) = recipient.call{value : amount}("");
                require(success, "send failed");
              }
              function updatePackState(address _entity, uint _packType) external {
                require(msg.sender == address(strongNFTBonus), "invalid sender");
                updatePackState(_entity, _packType, true);
              }
              function updatePackState(address _entity, uint _packType, bool _saveRewardsDue) internal {
                bytes memory id = getPackId(_entity, _packType);
                uint rewardDue = getRewardAt(_entity, _packType, block.timestamp, true);
                uint accruedTotal = entityPackClaimedTotal[id] + rewardDue;
                uint nodeLifetimeReward = getNodeRewardLifetime(_packType);
                uint maturedNodesTotal = accruedTotal / nodeLifetimeReward;
                uint maturedNodesNew = maturedNodesTotal > entityPackMaturedNodeCount[id] ? maturedNodesTotal - entityPackMaturedNodeCount[id] : 0;
                if (_saveRewardsDue) {
                  entityPackRewardDue[id] = rewardDue;
                  entityPackLastClaimedAt[id] = block.timestamp;
                }
                if (maturedNodesNew > 0) {
                  entityPackMaturedNodeCount[id] += maturedNodesNew;
                  entityPackClaimedMatured[id] += maturedNodesNew * nodeLifetimeReward;
                  totalMaturedNodes += maturedNodesNew;
                  emit MaturedNodes(_entity, _packType, maturedNodesNew);
                }
              }
              //
              // Migration
              // -------------------------------------------------------------------------------------------------------------------
              function migrateNodes(address _entity, uint _packType, uint _nodeCount, uint _lastPaidAt, uint _rewardsDue, uint _totalClaimed) external returns (bool) {
                require(serviceContractEnabled[msg.sender], "no service");
                require(packTypeActive[_packType], "invalid type");
                require(!doesPackExist(_entity, _packType) || !hasPackExpired(_entity, _packType), "pack expired");
                bytes memory id = getPackId(_entity, _packType);
                totalNodes += _nodeCount;
                entityNodeCount[_entity] += _nodeCount;
                if (entityPackCreatedAt[id] == 0) {
                  entityPackCreatedAt[id] = block.timestamp;
                  entityPackLastPaidAt[id] = _lastPaidAt > 0 ? _lastPaidAt : block.timestamp;
                  totalPacks += 1;
                  emit Created(_entity, _packType, _nodeCount, false, block.timestamp, msg.sender);
                }
                else {
                  updatePackState(_entity, _packType, true);
                  if (_lastPaidAt > 0) {
                    entityPackLastPaidAt[id] = ((entityPackLastPaidAt[id] * entityPackTotalNodeCount[id]) + (_lastPaidAt * _nodeCount)) / (entityPackTotalNodeCount[id] + _nodeCount);
                  }
                  emit AddedNodes(_entity, _packType, _nodeCount, entityPackTotalNodeCount[id], false, block.timestamp, msg.sender);
                }
                entityPackTotalNodeCount[id] += _nodeCount;
                entityPackClaimedTotal[id] += _totalClaimed;
                entityPackRewardDue[id] += _rewardsDue;
                if (entityPackTotalNodeCount[id] > _nodeCount) {
                  updatePackState(_entity, _packType, true);
                }
                emit MigratedNodes(_entity, _packType, _nodeCount, _lastPaidAt, _rewardsDue, _totalClaimed, msg.sender, block.timestamp);
                return true;
              }
            }
            // SPDX-License-Identifier: MIT
            pragma solidity >=0.6.0;
            import "../interfaces/IAdminControl.sol";
            abstract contract AdminAccess {
              IAdminControl public adminControl;
              modifier onlyRole(uint8 _role) {
                require(address(adminControl) == address(0) || adminControl.hasRole(_role, msg.sender), "no access");
                _;
              }
              function addAdminControlContract(IAdminControl _contract) external onlyRole(0) {
                adminControl = _contract;
              }
            }
            // SPDX-License-Identifier: MIT
            pragma solidity >=0.6.0;
            /**
             * @dev Interface of the ERC20 standard as defined in the EIP.
             */
            interface IERC20 {
              /**
               * @dev Returns the amount of tokens in existence.
               */
              function totalSupply() external view returns (uint256);
              /**
               * @dev Returns the amount of tokens owned by `account`.
               */
              function balanceOf(address account) external view returns (uint256);
              /**
               * @dev Moves `amount` tokens from the caller's account to `recipient`.
               *
               * Returns a boolean value indicating whether the operation succeeded.
               *
               * Emits a {Transfer} event.
               */
              function transfer(address recipient, uint256 amount) external returns (bool);
              /**
               * @dev Returns the remaining number of tokens that `spender` will be
               * allowed to spend on behalf of `owner` through {transferFrom}. This is
               * zero by default.
               *
               * This value changes when {approve} or {transferFrom} are called.
               */
              function allowance(address owner, address spender) external view returns (uint256);
              /**
               * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
               *
               * Returns a boolean value indicating whether the operation succeeded.
               *
               * IMPORTANT: Beware that changing an allowance with this method brings the risk
               * that someone may use both the old and the new allowance by unfortunate
               * transaction ordering. One possible solution to mitigate this race
               * condition is to first reduce the spender's allowance to 0 and set the
               * desired value afterwards:
               * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
               *
               * Emits an {Approval} event.
               */
              function approve(address spender, uint256 amount) external returns (bool);
              /**
               * @dev Moves `amount` tokens from `sender` to `recipient` using the
               * allowance mechanism. `amount` is then deducted from the caller's
               * allowance.
               *
               * Returns a boolean value indicating whether the operation succeeded.
               *
               * Emits a {Transfer} event.
               */
              function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
              /**
               * @dev Emitted when `value` tokens are moved from one account (`from`) to
               * another (`to`).
               *
               * Note that `value` may be zero.
               */
              event Transfer(address indexed from, address indexed to, uint256 value);
              /**
               * @dev Emitted when the allowance of a `spender` for an `owner` is set by
               * a call to {approve}. `value` is the new allowance.
               */
              event Approval(address indexed owner, address indexed spender, uint256 value);
            }
            // SPDX-License-Identifier: MIT
            pragma solidity >=0.6.0;
            interface INodePackV3 {
              function doesPackExist(address entity, uint packId) external view returns (bool);
              function hasPackExpired(address entity, uint packId) external view returns (bool);
              function claim(uint packId, uint timestamp, address toStrongPool) external payable returns (uint);
            //  function getBonusAt(address _entity, uint _packType, uint _timestamp) external view returns (uint);
              function getPackId(address _entity, uint _packType) external pure returns (bytes memory);
              function getEntityPackTotalNodeCount(address _entity, uint _packType) external view returns (uint);
              function getEntityPackActiveNodeCount(address _entity, uint _packType) external view returns (uint);
              function migrateNodes(address _entity, uint _nodeType, uint _nodeCount, uint _lastPaidAt, uint _rewardsDue, uint _totalClaimed) external returns (bool);
            //  function addPackRewardDue(address _entity, uint _packType, uint _rewardDue) external;
              function updatePackState(address _entity, uint _packType) external;
            }
            // SPDX-License-Identifier: MIT
            pragma solidity >=0.6.0;
            interface IStrongPool {
              function mineFor(address miner, uint256 amount) external;
            }
            // SPDX-License-Identifier: MIT
            pragma solidity 0.8.9;
            interface IStrongNFTPackBonus {
              function getBonus(address _entity, uint _packType, uint _from, uint _to) external view returns (uint);
              function setEntityPackBonusSaved(address _entity, uint _packType) external;
              function resetEntityPackBonusSaved(bytes memory _packId) external;
            }
            // SPDX-License-Identifier: MIT
            pragma solidity >=0.6.0;
            import "./Context.sol";
            abstract contract InternalCalls is Context {
              uint private constant _NOT_MAKING_INTERNAL_CALLS = 1;
              uint private constant _MAKING_INTERNAL_CALLS = 2;
              uint private _internal_calls_status;
              modifier makesInternalCalls() {
                _internal_calls_status = _MAKING_INTERNAL_CALLS;
                _;
                _internal_calls_status = _NOT_MAKING_INTERNAL_CALLS;
              }
              function init() internal {
                _internal_calls_status = _NOT_MAKING_INTERNAL_CALLS;
              }
              function isInternalCall() internal view returns (bool) {
                return _internal_calls_status == _MAKING_INTERNAL_CALLS;
              }
              function isContractCall() internal view returns (bool) {
                return _msgSender() != tx.origin;
              }
              function isUserCall() internal view returns (bool) {
                return !isInternalCall() && !isContractCall();
              }
            }
            // SPDX-License-Identifier: MIT
            pragma solidity >=0.8.0;
            library SbMath {
              uint internal constant DECIMAL_PRECISION = 1e18;
              /*
              * Multiply two decimal numbers and use normal rounding rules:
              * -round product up if 19'th mantissa digit >= 5
              * -round product down if 19'th mantissa digit < 5
              *
              * Used only inside the exponentiation, _decPow().
              */
              function decMul(uint x, uint y) internal pure returns (uint decProd) {
                uint prod_xy = x * y;
                decProd = (prod_xy + (DECIMAL_PRECISION / 2)) / DECIMAL_PRECISION;
              }
              /*
              * _decPow: Exponentiation function for 18-digit decimal base, and integer exponent n.
              *
              * Uses the efficient "exponentiation by squaring" algorithm. O(log(n)) complexity.
              *
              * The exponent is capped to avoid reverting due to overflow. The cap 525600000 equals
              * "minutes in 1000 years": 60 * 24 * 365 * 1000
              */
              function _decPow(uint _base, uint _minutes) internal pure returns (uint) {
                if (_minutes > 525_600_000) _minutes = 525_600_000;  // cap to avoid overflow
                if (_minutes == 0) return DECIMAL_PRECISION;
                uint y = DECIMAL_PRECISION;
                uint x = _base;
                uint n = _minutes;
                // Exponentiation-by-squaring
                while (n > 1) {
                  if (n % 2 == 0) {
                    x = decMul(x, x);
                    n = n / 2;
                  } else { // if (n % 2 != 0)
                    y = decMul(x, y);
                    x = decMul(x, x);
                    n = (n - 1) / 2;
                  }
                }
                return decMul(x, y);
              }
            }
            // SPDX-License-Identifier: MIT
            pragma solidity >=0.6.0;
            interface IAdminControl {
              function hasRole(uint8 _role, address _account) external view returns (bool);
              function SUPER_ADMIN() external view returns (uint8);
              function ADMIN() external view returns (uint8);
              function SERVICE_ADMIN() external view returns (uint8);
            }
            // SPDX-License-Identifier: MIT
            pragma solidity >=0.6.0;
            /*
             * @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 GSN 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 Context {
                function _msgSender() internal view virtual returns (address) {
                    return msg.sender;
                }
                function _msgData() internal view virtual returns (bytes memory) {
                    this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
                    return msg.data;
                }
            }
            

            File 3 of 4: TransparentUpgradeableProxy
            // SPDX-License-Identifier: MIT
            pragma solidity ^0.7.0;
            /**
             * @dev This abstract contract provides a fallback function that delegates all calls to another contract using the EVM
             * instruction `delegatecall`. We refer to the second contract as the _implementation_ behind the proxy, and it has to
             * be specified by overriding the virtual {_implementation} function.
             * 
             * Additionally, delegation to the implementation can be triggered manually through the {_fallback} function, or to a
             * different contract through the {_delegate} function.
             * 
             * The success and return data of the delegated call will be returned back to the caller of the proxy.
             */
            abstract contract Proxy {
                /**
                 * @dev Delegates the current call to `implementation`.
                 * 
                 * This function does not return to its internall call site, it will return directly to the external caller.
                 */
                function _delegate(address implementation) internal {
                    // solhint-disable-next-line no-inline-assembly
                    assembly {
                        // Copy msg.data. We take full control of memory in this inline assembly
                        // block because it will not return to Solidity code. We overwrite the
                        // Solidity scratch pad at memory position 0.
                        calldatacopy(0, 0, calldatasize())
                        // Call the implementation.
                        // out and outsize are 0 because we don't know the size yet.
                        let result := delegatecall(gas(), implementation, 0, calldatasize(), 0, 0)
                        // Copy the returned data.
                        returndatacopy(0, 0, returndatasize())
                        switch result
                        // delegatecall returns 0 on error.
                        case 0 { revert(0, returndatasize()) }
                        default { return(0, returndatasize()) }
                    }
                }
                /**
                 * @dev This is a virtual function that should be overriden so it returns the address to which the fallback function
                 * and {_fallback} should delegate.
                 */
                function _implementation() internal virtual view returns (address);
                /**
                 * @dev Delegates the current call to the address returned by `_implementation()`.
                 * 
                 * This function does not return to its internall call site, it will return directly to the external caller.
                 */
                function _fallback() internal {
                    _beforeFallback();
                    _delegate(_implementation());
                }
                /**
                 * @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if no other
                 * function in the contract matches the call data.
                 */
                fallback () payable external {
                    _fallback();
                }
                /**
                 * @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if call data
                 * is empty.
                 */
                receive () payable external {
                    _fallback();
                }
                /**
                 * @dev Hook that is called before falling back to the implementation. Can happen as part of a manual `_fallback`
                 * call, or as part of the Solidity `fallback` or `receive` functions.
                 * 
                 * If overriden should call `super._beforeFallback()`.
                 */
                function _beforeFallback() internal virtual {
                }
            }
            // SPDX-License-Identifier: MIT
            pragma solidity ^0.7.0;
            import "./UpgradeableProxy.sol";
            /**
             * @dev This contract implements a proxy that is upgradeable by an admin.
             * 
             * To avoid https://medium.com/nomic-labs-blog/malicious-backdoors-in-ethereum-proxies-62629adf3357[proxy selector
             * clashing], which can potentially be used in an attack, this contract uses the
             * https://blog.openzeppelin.com/the-transparent-proxy-pattern/[transparent proxy pattern]. This pattern implies two
             * things that go hand in hand:
             * 
             * 1. If any account other than the admin calls the proxy, the call will be forwarded to the implementation, even if
             * that call matches one of the admin functions exposed by the proxy itself.
             * 2. If the admin calls the proxy, it can access the admin functions, but its calls will never be forwarded to the
             * implementation. If the admin tries to call a function on the implementation it will fail with an error that says
             * "admin cannot fallback to proxy target".
             * 
             * These properties mean that the admin account can only be used for admin actions like upgrading the proxy or changing
             * the admin, so it's best if it's a dedicated account that is not used for anything else. This will avoid headaches due
             * to sudden errors when trying to call a function from the proxy implementation.
             * 
             * Our recommendation is for the dedicated account to be an instance of the {ProxyAdmin} contract. If set up this way,
             * you should think of the `ProxyAdmin` instance as the real administrative inerface of your proxy.
             */
            contract TransparentUpgradeableProxy is UpgradeableProxy {
                /**
                 * @dev Initializes an upgradeable proxy managed by `_admin`, backed by the implementation at `_logic`, and
                 * optionally initialized with `_data` as explained in {UpgradeableProxy-constructor}.
                 */
                constructor(address initialLogic, address initialAdmin, bytes memory _data) payable UpgradeableProxy(initialLogic, _data) {
                    assert(_ADMIN_SLOT == bytes32(uint256(keccak256("eip1967.proxy.admin")) - 1));
                    _setAdmin(initialAdmin);
                }
                /**
                 * @dev Emitted when the admin account has changed.
                 */
                event AdminChanged(address previousAdmin, address newAdmin);
                /**
                 * @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 private constant _ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;
                /**
                 * @dev Modifier used internally that will delegate the call to the implementation unless the sender is the admin.
                 */
                modifier ifAdmin() {
                    if (msg.sender == _admin()) {
                        _;
                    } else {
                        _fallback();
                    }
                }
                /**
                 * @dev Returns the current admin.
                 * 
                 * NOTE: Only the admin can call this function. See {ProxyAdmin-getProxyAdmin}.
                 * 
                 * TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using the
                 * https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call.
                 * `0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103`
                 */
                function admin() external ifAdmin returns (address) {
                    return _admin();
                }
                /**
                 * @dev Returns the current implementation.
                 * 
                 * NOTE: Only the admin can call this function. See {ProxyAdmin-getProxyImplementation}.
                 * 
                 * TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using the
                 * https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call.
                 * `0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc`
                 */
                function implementation() external ifAdmin returns (address) {
                    return _implementation();
                }
                /**
                 * @dev Changes the admin of the proxy.
                 * 
                 * Emits an {AdminChanged} event.
                 * 
                 * NOTE: Only the admin can call this function. See {ProxyAdmin-changeProxyAdmin}.
                 */
                function changeAdmin(address newAdmin) external ifAdmin {
                    require(newAdmin != address(0), "TransparentUpgradeableProxy: new admin is the zero address");
                    emit AdminChanged(_admin(), newAdmin);
                    _setAdmin(newAdmin);
                }
                /**
                 * @dev Upgrade the implementation of the proxy.
                 * 
                 * NOTE: Only the admin can call this function. See {ProxyAdmin-upgrade}.
                 */
                function upgradeTo(address newImplementation) external ifAdmin {
                    _upgradeTo(newImplementation);
                }
                /**
                 * @dev Upgrade the implementation of the proxy, and then call a function from the new implementation as specified
                 * by `data`, which should be an encoded function call. This is useful to initialize new storage variables in the
                 * proxied contract.
                 * 
                 * NOTE: Only the admin can call this function. See {ProxyAdmin-upgradeAndCall}.
                 */
                function upgradeToAndCall(address newImplementation, bytes calldata data) external payable ifAdmin {
                    _upgradeTo(newImplementation);
                    // solhint-disable-next-line avoid-low-level-calls
                    (bool success,) = newImplementation.delegatecall(data);
                    require(success);
                }
                /**
                 * @dev Returns the current admin.
                 */
                function _admin() internal view returns (address adm) {
                    bytes32 slot = _ADMIN_SLOT;
                    // solhint-disable-next-line no-inline-assembly
                    assembly {
                        adm := sload(slot)
                    }
                }
                /**
                 * @dev Stores a new address in the EIP1967 admin slot.
                 */
                function _setAdmin(address newAdmin) private {
                    bytes32 slot = _ADMIN_SLOT;
                    // solhint-disable-next-line no-inline-assembly
                    assembly {
                        sstore(slot, newAdmin)
                    }
                }
                /**
                 * @dev Makes sure the admin cannot access the fallback function. See {Proxy-_beforeFallback}.
                 */
                function _beforeFallback() internal override virtual {
                    require(msg.sender != _admin(), "TransparentUpgradeableProxy: admin cannot fallback to proxy target");
                    super._beforeFallback();
                }
            }
            // SPDX-License-Identifier: MIT
            pragma solidity ^0.7.0;
            import "./Proxy.sol";
            import "../utils/Address.sol";
            /**
             * @dev This contract implements an upgradeable proxy. It is upgradeable because calls are delegated to an
             * implementation address that can be changed. This address is stored in storage in the location specified by
             * https://eips.ethereum.org/EIPS/eip-1967[EIP1967], so that it doesn't conflict with the storage layout of the
             * implementation behind the proxy.
             * 
             * Upgradeability is only provided internally through {_upgradeTo}. For an externally upgradeable proxy see
             * {TransparentUpgradeableProxy}.
             */
            contract UpgradeableProxy is Proxy {
                /**
                 * @dev Initializes the upgradeable proxy with an initial implementation specified by `_logic`.
                 * 
                 * If `_data` is nonempty, it's used as data in a delegate call to `_logic`. This will typically be an encoded
                 * function call, and allows initializating the storage of the proxy like a Solidity constructor.
                 */
                constructor(address _logic, bytes memory _data) payable {
                    assert(_IMPLEMENTATION_SLOT == bytes32(uint256(keccak256("eip1967.proxy.implementation")) - 1));
                    _setImplementation(_logic);
                    if(_data.length > 0) {
                        // solhint-disable-next-line avoid-low-level-calls
                        (bool success,) = _logic.delegatecall(_data);
                        require(success);
                    }
                }
                /**
                 * @dev Emitted when the implementation is upgraded.
                 */
                event Upgraded(address indexed implementation);
                /**
                 * @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 private constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
                /**
                 * @dev Returns the current implementation address.
                 */
                function _implementation() internal override view returns (address impl) {
                    bytes32 slot = _IMPLEMENTATION_SLOT;
                    // solhint-disable-next-line no-inline-assembly
                    assembly {
                        impl := sload(slot)
                    }
                }
                /**
                 * @dev Upgrades the proxy to a new implementation.
                 * 
                 * Emits an {Upgraded} event.
                 */
                function _upgradeTo(address newImplementation) internal {
                    _setImplementation(newImplementation);
                    emit Upgraded(newImplementation);
                }
                /**
                 * @dev Stores a new address in the EIP1967 implementation slot.
                 */
                function _setImplementation(address newImplementation) private {
                    require(Address.isContract(newImplementation), "UpgradeableProxy: new implementation is not a contract");
                    bytes32 slot = _IMPLEMENTATION_SLOT;
                    // solhint-disable-next-line no-inline-assembly
                    assembly {
                        sstore(slot, newImplementation)
                    }
                }
            }
            // SPDX-License-Identifier: MIT
            pragma solidity ^0.7.0;
            /**
             * @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
                 * ====
                 */
                function isContract(address account) internal view returns (bool) {
                    // According to EIP-1052, 0x0 is the value returned for not-yet created accounts
                    // and 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 is returned
                    // for accounts without code, i.e. `keccak256('')`
                    bytes32 codehash;
                    bytes32 accountHash = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470;
                    // solhint-disable-next-line no-inline-assembly
                    assembly { codehash := extcodehash(account) }
                    return (codehash != accountHash && codehash != 0x0);
                }
                /**
                 * @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");
                    // solhint-disable-next-line avoid-low-level-calls, avoid-call-value
                    (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");
                    return _functionCallWithValue(target, data, value, errorMessage);
                }
                function _functionCallWithValue(address target, bytes memory data, uint256 weiValue, string memory errorMessage) private returns (bytes memory) {
                    require(isContract(target), "Address: call to non-contract");
                    // solhint-disable-next-line avoid-low-level-calls
                    (bool success, bytes memory returndata) = target.call{ value: weiValue }(data);
                    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
                            // solhint-disable-next-line no-inline-assembly
                            assembly {
                                let returndata_size := mload(returndata)
                                revert(add(32, returndata), returndata_size)
                            }
                        } else {
                            revert(errorMessage);
                        }
                    }
                }
            }
            

            File 4 of 4: StrongNFTPackBonusV2
            //SPDX-License-Identifier: Unlicensed
            pragma solidity 0.8.9;
            import "./interfaces/IERC1155Preset.sol";
            import "./interfaces/INodePackV3.sol";
            import "./lib/SafeMath.sol";
            import "./lib/ERC1155Receiver.sol";
            import "./lib/AdminAccess.sol";
            contract StrongNFTPackBonusV2 is AdminAccess {
              event Staked(address indexed entity, uint tokenId, uint packType, uint timestamp);
              event Unstaked(address indexed entity, uint tokenId, uint packType, uint timestamp);
              event SetPackTypeNFTBonus(uint packType, string bonusName, uint value);
              IERC1155Preset public CERC1155;
              INodePackV3 public nodePack;
              bool public initDone;
              mapping(bytes4 => bool) private _supportedInterfaces;
              string[] public nftBonusNames;
              mapping(string => uint) public nftBonusLowerBound;
              mapping(string => uint) public nftBonusUpperBound;
              mapping(string => uint) public nftBonusEffectiveAt;
              mapping(string => uint) public nftBonusNodesLimit;
              mapping(uint => mapping(string => uint)) public packTypeNFTBonus;
              mapping(uint => address) public nftIdStakedToEntity;
              mapping(uint => uint) public nftIdStakedToPackType;
              mapping(address => uint[]) public entityStakedNftIds;
              mapping(bytes => uint[]) public entityPackStakedNftIds;
              mapping(bytes => uint) public entityPackStakedAt;
              mapping(bytes => uint) public entityPackBonusSaved;
              function init(address _nftContract) external onlyRole(adminControl.SUPER_ADMIN()) {
                require(initDone == false, "init done");
                _registerInterface(0x01ffc9a7);
                _registerInterface(
                  ERC1155Receiver(address(0)).onERC1155Received.selector ^
                  ERC1155Receiver(address(0)).onERC1155BatchReceived.selector
                );
                CERC1155 = IERC1155Preset(_nftContract);
                initDone = true;
              }
              //
              // Getters
              // -------------------------------------------------------------------------------------------------------------------
              function isNftStaked(uint _nftId) public view returns (bool) {
                return nftIdStakedToPackType[_nftId] != 0;
              }
              function getStakedNftIds(address _entity) public view returns (uint[] memory) {
                return entityStakedNftIds[_entity];
              }
              function getPackStakedNftIds(address _entity, uint _packType) public view returns (uint[] memory) {
                bytes memory id = nodePack.getPackId(_entity, _packType);
                return entityPackStakedNftIds[id];
              }
              function getNftBonusNames() public view returns (string[] memory) {
                return nftBonusNames;
              }
              function getNftBonusNodesLimit(uint _nftId) public view returns (uint) {
                return nftBonusNodesLimit[getNftBonusName(_nftId)];
              }
              function getNftBonusName(uint _nftId) public view returns (string memory) {
                for (uint i = 0; i < nftBonusNames.length; i++) {
                  if (_nftId >= nftBonusLowerBound[nftBonusNames[i]] && _nftId <= nftBonusUpperBound[nftBonusNames[i]]) {
                    return nftBonusNames[i];
                  }
                }
                return "";
              }
              function getNftBonusValue(uint _packType, string memory _bonusName) public view returns (uint) {
                return packTypeNFTBonus[_packType][_bonusName] > 0
                ? packTypeNFTBonus[_packType][_bonusName]
                : packTypeNFTBonus[0][_bonusName];
              }
              function getBonus(address _entity, uint _packType, uint _from, uint _to) public view returns (uint) {
                uint[] memory nftIds = getPackStakedNftIds(_entity, _packType);
                if (nftIds.length == 0) return 0;
                bytes memory id = nodePack.getPackId(_entity, _packType);
                if (entityPackStakedAt[id] == 0) return 0;
                uint bonus = entityPackBonusSaved[id];
                string memory bonusName = "";
                uint startFrom = 0;
                uint nftNodeLimitCount = 0;
                uint boostedNodesCount = 0;
                uint entityPackTotalNodeCount = nodePack.getEntityPackActiveNodeCount(_entity, _packType);
                for (uint i = 0; i < nftIds.length; i++) {
                  if (boostedNodesCount >= entityPackTotalNodeCount) break;
                  bonusName = getNftBonusName(nftIds[i]);
                  if (keccak256(abi.encode(bonusName)) == keccak256(abi.encode(""))) return 0;
                  if (nftBonusEffectiveAt[bonusName] == 0) continue;
                  if (CERC1155.balanceOf(address(this), nftIds[i]) == 0) continue;
                  nftNodeLimitCount = getNftBonusNodesLimit(nftIds[i]);
                  if (boostedNodesCount + nftNodeLimitCount > entityPackTotalNodeCount) {
                    nftNodeLimitCount = entityPackTotalNodeCount - boostedNodesCount;
                  }
                  boostedNodesCount += nftNodeLimitCount;
                  startFrom = entityPackStakedAt[id] > _from ? entityPackStakedAt[id] : _from;
                  if (startFrom < nftBonusEffectiveAt[bonusName]) {
                    startFrom = nftBonusEffectiveAt[bonusName];
                  }
                  if (startFrom >= _to) continue;
                  bonus += (_to - startFrom) * getNftBonusValue(_packType, bonusName) * nftNodeLimitCount;
                }
                return bonus;
              }
              //
              // Staking
              // -------------------------------------------------------------------------------------------------------------------
              function stakeNFT(uint _nftId, uint _packType) public payable {
                string memory bonusName = getNftBonusName(_nftId);
                require(keccak256(abi.encode(bonusName)) != keccak256(abi.encode("")), "not eligible");
                require(CERC1155.balanceOf(msg.sender, _nftId) != 0, "not enough");
                require(nftIdStakedToEntity[_nftId] == address(0), "already staked");
                require(nodePack.doesPackExist(msg.sender, _packType), "pack doesnt exist");
                bytes memory id = nodePack.getPackId(msg.sender, _packType);
                entityPackBonusSaved[id] = getBonus(msg.sender, _packType, entityPackStakedAt[id], block.timestamp);
                nftIdStakedToPackType[_nftId] = _packType;
                nftIdStakedToEntity[_nftId] = msg.sender;
                entityPackStakedAt[id] = block.timestamp;
                entityStakedNftIds[msg.sender].push(_nftId);
                entityPackStakedNftIds[id].push(_nftId);
                CERC1155.safeTransferFrom(msg.sender, address(this), _nftId, 1, bytes(""));
                emit Staked(msg.sender, _nftId, _packType, block.timestamp);
              }
              function unStakeNFT(uint _nftId, uint _packType, uint _timestamp) public {
                require(nftIdStakedToEntity[_nftId] != address(0), "not staked");
                require(nftIdStakedToEntity[_nftId] == msg.sender, "not staker");
                require(nftIdStakedToPackType[_nftId] == _packType, "wrong pack");
                nodePack.updatePackState(msg.sender, _packType);
                bytes memory id = nodePack.getPackId(msg.sender, _packType);
                nftIdStakedToPackType[_nftId] = 0;
                nftIdStakedToEntity[_nftId] = address(0);
                for (uint i = 0; i < entityStakedNftIds[msg.sender].length; i++) {
                  if (entityStakedNftIds[msg.sender][i] == _nftId) {
                    _deleteIndex(entityStakedNftIds[msg.sender], i);
                    break;
                  }
                }
                for (uint i = 0; i < entityPackStakedNftIds[id].length; i++) {
                  if (entityPackStakedNftIds[id][i] == _nftId) {
                    _deleteIndex(entityPackStakedNftIds[id], i);
                    break;
                  }
                }
                CERC1155.safeTransferFrom(address(this), msg.sender, _nftId, 1, bytes(""));
                emit Unstaked(msg.sender, _nftId, _packType, _timestamp);
              }
              //
              // Admin
              // -------------------------------------------------------------------------------------------------------------------
              function updateBonus(string memory _name, uint _lowerBound, uint _upperBound, uint _effectiveAt, uint _nodesLimit) public onlyRole(adminControl.SERVICE_ADMIN()) {
                bool alreadyExists = false;
                for (uint i = 0; i < nftBonusNames.length; i++) {
                  if (keccak256(abi.encode(nftBonusNames[i])) == keccak256(abi.encode(_name))) {
                    alreadyExists = true;
                  }
                }
                if (!alreadyExists) {
                  nftBonusNames.push(_name);
                }
                nftBonusLowerBound[_name] = _lowerBound;
                nftBonusUpperBound[_name] = _upperBound;
                nftBonusEffectiveAt[_name] = _effectiveAt != 0 ? _effectiveAt : block.timestamp;
                nftBonusNodesLimit[_name] = _nodesLimit;
              }
              function setPackTypeNFTBonus(uint _packType, string memory _bonusName, uint _value) external onlyRole(adminControl.SERVICE_ADMIN()) {
                packTypeNFTBonus[_packType][_bonusName] = _value;
                emit SetPackTypeNFTBonus(_packType, _bonusName, _value);
              }
              function updateNftContract(address _nftContract) external onlyRole(adminControl.SUPER_ADMIN()) {
                CERC1155 = IERC1155Preset(_nftContract);
              }
              function updateNodePackContract(address _contract) external onlyRole(adminControl.SUPER_ADMIN()) {
                nodePack = INodePackV3(_contract);
              }
              function updateEntityPackStakedAt(address _entity, uint _packType, uint _timestamp) public onlyRole(adminControl.SERVICE_ADMIN()) {
                bytes memory id = nodePack.getPackId(_entity, _packType);
                entityPackStakedAt[id] = _timestamp;
              }
              function setEntityPackBonusSaved(address _entity, uint _packType) external {
                require(msg.sender == address(nodePack), "not allowed");
                bytes memory id = nodePack.getPackId(_entity, _packType);
                entityPackBonusSaved[id] = getBonus(_entity, _packType, entityPackStakedAt[id], block.timestamp);
                entityPackStakedAt[id] = block.timestamp;
              }
              function resetEntityPackBonusSaved(bytes memory _packId) external {
                require(msg.sender == address(nodePack), "not allowed");
                entityPackBonusSaved[_packId] = 0;
              }
              //
              // ERC1155 support
              // -------------------------------------------------------------------------------------------------------------------
              function onERC1155Received(address, address, uint, uint, bytes memory) public virtual returns (bytes4) {
                return this.onERC1155Received.selector;
              }
              function onERC1155BatchReceived(address, address, uint[] memory, uint[] memory, bytes memory) public virtual returns (bytes4) {
                return this.onERC1155BatchReceived.selector;
              }
              function supportsInterface(bytes4 interfaceId) public view returns (bool) {
                return _supportedInterfaces[interfaceId];
              }
              function _registerInterface(bytes4 interfaceId) internal virtual {
                require(interfaceId != 0xffffffff, "ERC165: invalid interface id");
                _supportedInterfaces[interfaceId] = true;
              }
              function _deleteIndex(uint[] storage array, uint index) internal {
                uint lastIndex = array.length - 1;
                uint lastEntry = array[lastIndex];
                if (index == lastIndex) {
                  array.pop();
                } else {
                  array[index] = lastEntry;
                  array.pop();
                }
              }
            }
            // SPDX-License-Identifier: MIT
            pragma solidity >=0.6.0;
            /**
             * @dev Required interface of an ERC1155 compliant contract, as defined in the
             * https://eips.ethereum.org/EIPS/eip-1155[EIP].
             *
             * _Available since v3.1._
             */
            interface IERC1155Preset {
                /**
                 * @dev Emitted when `value` tokens of token type `id` are transferred from `from` to `to` by `operator`.
                 */
                event TransferSingle(address indexed operator, address indexed from, address indexed to, uint256 id, uint256 value);
                /**
                 * @dev Equivalent to multiple {TransferSingle} events, where `operator`, `from` and `to` are the same for all
                 * transfers.
                 */
                event TransferBatch(address indexed operator, address indexed from, address indexed to, uint256[] ids, uint256[] values);
                /**
                 * @dev Emitted when `account` grants or revokes permission to `operator` to transfer their tokens, according to
                 * `approved`.
                 */
                event ApprovalForAll(address indexed account, address indexed operator, bool approved);
                /**
                 * @dev Emitted when the URI for token type `id` changes to `value`, if it is a non-programmatic URI.
                 *
                 * If an {URI} event was emitted for `id`, the standard
                 * https://eips.ethereum.org/EIPS/eip-1155#metadata-extensions[guarantees] that `value` will equal the value
                 * returned by {IERC1155MetadataURI-uri}.
                 */
                event URI(string value, uint256 indexed id);
                /**
                 * @dev Returns the amount of tokens of token type `id` owned by `account`.
                 *
                 * Requirements:
                 *
                 * - `account` cannot be the zero address.
                 */
                function balanceOf(address account, uint256 id) external view returns (uint256);
                /**
                 * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {balanceOf}.
                 *
                 * Requirements:
                 *
                 * - `accounts` and `ids` must have the same length.
                 */
                function balanceOfBatch(address[] calldata accounts, uint256[] calldata ids) external view returns (uint256[] memory);
                /**
                 * @dev Grants or revokes permission to `operator` to transfer the caller's tokens, according to `approved`,
                 *
                 * Emits an {ApprovalForAll} event.
                 *
                 * Requirements:
                 *
                 * - `operator` cannot be the caller.
                 */
                function setApprovalForAll(address operator, bool approved) external;
                /**
                 * @dev Returns true if `operator` is approved to transfer ``account``'s tokens.
                 *
                 * See {setApprovalForAll}.
                 */
                function isApprovedForAll(address account, address operator) external view returns (bool);
                /**
                 * @dev Transfers `amount` tokens of token type `id` from `from` to `to`.
                 *
                 * Emits a {TransferSingle} event.
                 *
                 * Requirements:
                 *
                 * - `to` cannot be the zero address.
                 * - If the caller is not `from`, it must be have been approved to spend ``from``'s tokens via {setApprovalForAll}.
                 * - `from` must have a balance of tokens of type `id` of at least `amount`.
                 * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the
                 * acceptance magic value.
                 */
                function safeTransferFrom(address from, address to, uint256 id, uint256 amount, bytes calldata data) external;
                /**
                 * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {safeTransferFrom}.
                 *
                 * Emits a {TransferBatch} event.
                 *
                 * Requirements:
                 *
                 * - `ids` and `amounts` must have the same length.
                 * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the
                 * acceptance magic value.
                 */
                function safeBatchTransferFrom(address from, address to, uint256[] calldata ids, uint256[] calldata amounts, bytes calldata data) external;
                /**
                 * @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);
                /**
                 * @dev Creates `amount` new tokens for `to`, of token type `id`.
                 *
                 * See {ERC1155-_mint}.
                 *
                 * Requirements:
                 *
                 * - the caller must have the `MINTER_ROLE`.
                 */
                function mint(address to, uint256 id, uint256 amount, bytes memory data) external;
                /**
                 * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] variant of {mint}.
                 */
                function mintBatch(address to, uint256[] memory ids, uint256[] memory amounts, bytes memory data) external;
                function getOwnerIdByIndex(address owner, uint256 index) external view returns (uint256);
                function getOwnerIdIndex(address owner, uint256 id) external view returns (uint256);
            }
            // SPDX-License-Identifier: MIT
            pragma solidity >=0.6.0;
            interface INodePackV3 {
              function doesPackExist(address entity, uint packId) external view returns (bool);
              function hasPackExpired(address entity, uint packId) external view returns (bool);
              function claim(uint packId, uint timestamp, address toStrongPool) external payable returns (uint);
            //  function getBonusAt(address _entity, uint _packType, uint _timestamp) external view returns (uint);
              function getPackId(address _entity, uint _packType) external pure returns (bytes memory);
              function getEntityPackTotalNodeCount(address _entity, uint _packType) external view returns (uint);
              function getEntityPackActiveNodeCount(address _entity, uint _packType) external view returns (uint);
              function migrateNodes(address _entity, uint _nodeType, uint _nodeCount, uint _lastPaidAt, uint _rewardsDue, uint _totalClaimed) external returns (bool);
            //  function addPackRewardDue(address _entity, uint _packType, uint _rewardDue) external;
              function updatePackState(address _entity, uint _packType) external;
            }
            // SPDX-License-Identifier: MIT
            pragma solidity >=0.6.0;
            /**
             * @dev Wrappers over Solidity's arithmetic operations with added overflow
             * checks.
             *
             * Arithmetic operations in Solidity wrap on overflow. This can easily result
             * in bugs, because programmers usually assume that an overflow raises an
             * error, which is the standard behavior in high level programming languages.
             * `SafeMath` restores this intuition by reverting the transaction when an
             * operation overflows.
             *
             * Using this library instead of the unchecked operations eliminates an entire
             * class of bugs, so it's recommended to use it always.
             */
            library SafeMath {
              /**
               * @dev Returns the addition of two unsigned integers, with an overflow flag.
               *
               * _Available since v3.4._
               */
              function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
                uint256 c = a + b;
                if (c < a) return (false, 0);
                return (true, c);
              }
              /**
               * @dev Returns the substraction of two unsigned integers, with an overflow flag.
               *
               * _Available since v3.4._
               */
              function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
                if (b > a) return (false, 0);
                return (true, a - b);
              }
              /**
               * @dev Returns the multiplication of two unsigned integers, with an overflow flag.
               *
               * _Available since v3.4._
               */
              function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
                // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
                // benefit is lost if 'b' is also tested.
                // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
                if (a == 0) return (true, 0);
                uint256 c = a * b;
                if (c / a != b) return (false, 0);
                return (true, c);
              }
              /**
               * @dev Returns the division of two unsigned integers, with a division by zero flag.
               *
               * _Available since v3.4._
               */
              function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
                if (b == 0) return (false, 0);
                return (true, a / b);
              }
              /**
               * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
               *
               * _Available since v3.4._
               */
              function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
                if (b == 0) return (false, 0);
                return (true, a % b);
              }
              /**
               * @dev Returns the addition of two unsigned integers, reverting on
               * overflow.
               *
               * Counterpart to Solidity's `+` operator.
               *
               * Requirements:
               *
               * - Addition cannot overflow.
               */
              function add(uint256 a, uint256 b) internal pure returns (uint256) {
                uint256 c = a + b;
                require(c >= a, "SafeMath: addition overflow");
                return c;
              }
              /**
               * @dev Returns the subtraction of two unsigned integers, reverting on
               * overflow (when the result is negative).
               *
               * Counterpart to Solidity's `-` operator.
               *
               * Requirements:
               *
               * - Subtraction cannot overflow.
               */
              function sub(uint256 a, uint256 b) internal pure returns (uint256) {
                require(b <= a, "SafeMath: subtraction overflow");
                return a - b;
              }
              /**
               * @dev Returns the multiplication of two unsigned integers, reverting on
               * overflow.
               *
               * Counterpart to Solidity's `*` operator.
               *
               * Requirements:
               *
               * - Multiplication cannot overflow.
               */
              function mul(uint256 a, uint256 b) internal pure returns (uint256) {
                if (a == 0) return 0;
                uint256 c = a * b;
                require(c / a == b, "SafeMath: multiplication overflow");
                return c;
              }
              /**
               * @dev Returns the integer division of two unsigned integers, reverting on
               * division by zero. The result is rounded towards zero.
               *
               * Counterpart to Solidity's `/` operator. Note: this function uses a
               * `revert` opcode (which leaves remaining gas untouched) while Solidity
               * uses an invalid opcode to revert (consuming all remaining gas).
               *
               * Requirements:
               *
               * - The divisor cannot be zero.
               */
              function div(uint256 a, uint256 b) internal pure returns (uint256) {
                require(b > 0, "SafeMath: division by zero");
                return a / b;
              }
              /**
               * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
               * reverting when dividing by zero.
               *
               * Counterpart to Solidity's `%` operator. This function uses a `revert`
               * opcode (which leaves remaining gas untouched) while Solidity uses an
               * invalid opcode to revert (consuming all remaining gas).
               *
               * Requirements:
               *
               * - The divisor cannot be zero.
               */
              function mod(uint256 a, uint256 b) internal pure returns (uint256) {
                require(b > 0, "SafeMath: modulo by zero");
                return a % b;
              }
              /**
               * @dev Returns the subtraction of two unsigned integers, reverting with custom message on
               * overflow (when the result is negative).
               *
               * CAUTION: This function is deprecated because it requires allocating memory for the error
               * message unnecessarily. For custom revert reasons use {trySub}.
               *
               * Counterpart to Solidity's `-` operator.
               *
               * Requirements:
               *
               * - Subtraction cannot overflow.
               */
              function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
                require(b <= a, errorMessage);
                return a - b;
              }
              /**
               * @dev Returns the integer division of two unsigned integers, reverting with custom message on
               * division by zero. The result is rounded towards zero.
               *
               * CAUTION: This function is deprecated because it requires allocating memory for the error
               * message unnecessarily. For custom revert reasons use {tryDiv}.
               *
               * Counterpart to Solidity's `/` operator. Note: this function uses a
               * `revert` opcode (which leaves remaining gas untouched) while Solidity
               * uses an invalid opcode to revert (consuming all remaining gas).
               *
               * Requirements:
               *
               * - The divisor cannot be zero.
               */
              function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
                require(b > 0, errorMessage);
                return a / b;
              }
              /**
               * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
               * reverting with custom message when dividing by zero.
               *
               * CAUTION: This function is deprecated because it requires allocating memory for the error
               * message unnecessarily. For custom revert reasons use {tryMod}.
               *
               * Counterpart to Solidity's `%` operator. This function uses a `revert`
               * opcode (which leaves remaining gas untouched) while Solidity uses an
               * invalid opcode to revert (consuming all remaining gas).
               *
               * Requirements:
               *
               * - The divisor cannot be zero.
               */
              function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
                require(b > 0, errorMessage);
                return a % b;
              }
            }
            // SPDX-License-Identifier: MIT
            pragma solidity >=0.6.0 <=0.8.9;
            import "../interfaces/IERC1155Receiver.sol";
            import "./ERC165.sol";
            /**
             * @dev _Available since v3.1._
             */
            abstract contract ERC1155Receiver is ERC165, IERC1155Receiver {
                constructor() internal {
                    _registerInterface(
                        ERC1155Receiver(address(0)).onERC1155Received.selector ^
                        ERC1155Receiver(address(0)).onERC1155BatchReceived.selector
                    );
                }
            }
            // SPDX-License-Identifier: MIT
            pragma solidity >=0.6.0;
            import "../interfaces/IAdminControl.sol";
            abstract contract AdminAccess {
              IAdminControl public adminControl;
              modifier onlyRole(uint8 _role) {
                require(address(adminControl) == address(0) || adminControl.hasRole(_role, msg.sender), "no access");
                _;
              }
              function addAdminControlContract(IAdminControl _contract) external onlyRole(0) {
                adminControl = _contract;
              }
            }
            // SPDX-License-Identifier: MIT
            pragma solidity >=0.6.0 <=0.8.9;
            import "./IERC165.sol";
            /**
             * _Available since v3.1._
             */
            interface IERC1155Receiver is IERC165 {
                /**
                    @dev Handles the receipt of a single ERC1155 token type. This function is
                    called at the end of a `safeTransferFrom` after the balance has been updated.
                    To accept the transfer, this must return
                    `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))`
                    (i.e. 0xf23a6e61, or its own function selector).
                    @param operator The address which initiated the transfer (i.e. msg.sender)
                    @param from The address which previously owned the token
                    @param id The ID of the token being transferred
                    @param value The amount of tokens being transferred
                    @param data Additional data with no specified format
                    @return `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))` if transfer is allowed
                */
                function onERC1155Received(
                    address operator,
                    address from,
                    uint256 id,
                    uint256 value,
                    bytes calldata data
                )
                    external
                    returns(bytes4);
                /**
                    @dev Handles the receipt of a multiple ERC1155 token types. This function
                    is called at the end of a `safeBatchTransferFrom` after the balances have
                    been updated. To accept the transfer(s), this must return
                    `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))`
                    (i.e. 0xbc197c81, or its own function selector).
                    @param operator The address which initiated the batch transfer (i.e. msg.sender)
                    @param from The address which previously owned the token
                    @param ids An array containing ids of each token being transferred (order and length must match values array)
                    @param values An array containing amounts of each token being transferred (order and length must match ids array)
                    @param data Additional data with no specified format
                    @return `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))` if transfer is allowed
                */
                function onERC1155BatchReceived(
                    address operator,
                    address from,
                    uint256[] calldata ids,
                    uint256[] calldata values,
                    bytes calldata data
                )
                    external
                    returns(bytes4);
            }
            // SPDX-License-Identifier: MIT
            pragma solidity >=0.6.0 <=0.8.9;
            import "../interfaces/IERC165.sol";
            /**
             * @dev Implementation of the {IERC165} interface.
             *
             * Contracts may inherit from this and call {_registerInterface} to declare
             * their support of an interface.
             */
            abstract contract ERC165 is IERC165 {
                /*
                 * bytes4(keccak256('supportsInterface(bytes4)')) == 0x01ffc9a7
                 */
                bytes4 private constant _INTERFACE_ID_ERC165 = 0x01ffc9a7;
                /**
                 * @dev Mapping of interface ids to whether or not it's supported.
                 */
                mapping(bytes4 => bool) private _supportedInterfaces;
                constructor () internal {
                    // Derived contracts need only register support for their own interfaces,
                    // we register support for ERC165 itself here
                    _registerInterface(_INTERFACE_ID_ERC165);
                }
                /**
                 * @dev See {IERC165-supportsInterface}.
                 *
                 * Time complexity O(1), guaranteed to always use less than 30 000 gas.
                 */
                function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
                    return _supportedInterfaces[interfaceId];
                }
                /**
                 * @dev Registers the contract as an implementer of the interface defined by
                 * `interfaceId`. Support of the actual ERC165 interface is automatic and
                 * registering its interface id is not required.
                 *
                 * See {IERC165-supportsInterface}.
                 *
                 * Requirements:
                 *
                 * - `interfaceId` cannot be the ERC165 invalid interface (`0xffffffff`).
                 */
                function _registerInterface(bytes4 interfaceId) internal virtual {
                    require(interfaceId != 0xffffffff, "ERC165: invalid interface id");
                    _supportedInterfaces[interfaceId] = true;
                }
            }
            // SPDX-License-Identifier: MIT
            pragma solidity >=0.6.0 <=0.8.9;
            /**
             * @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 IERC165 {
                /**
                 * @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);
            }
            // SPDX-License-Identifier: MIT
            pragma solidity >=0.6.0;
            interface IAdminControl {
              function hasRole(uint8 _role, address _account) external view returns (bool);
              function SUPER_ADMIN() external view returns (uint8);
              function ADMIN() external view returns (uint8);
              function SERVICE_ADMIN() external view returns (uint8);
            }