ETH Price: $2,640.96 (+3.56%)

Transaction Decoder

Block:
16152335 at Dec-10-2022 05:53:11 AM +UTC
Transaction Fee:
0.008625054639710832 ETH $22.78
Gas Used:
582,117 Gas / 14.816702896 Gwei

Emitted Events:

133 0xb772b306fd2c989765c6c02e93f7c4d3b5c62319.0x1cf3b03a6cf19fa2baba4df148e9dcabedea7f8a5c07840e207e5c089be95d3e( 0x1cf3b03a6cf19fa2baba4df148e9dcabedea7f8a5c07840e207e5c089be95d3e, 0x000000000000000000000000a1766584a3331a043532ad858b1396a07b26042f )
134 0xb772b306fd2c989765c6c02e93f7c4d3b5c62319.0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0( 0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0, 0x0000000000000000000000000000000000000000000000000000000000000000, 0x000000000000000000000000b787ad2ce37bbbf69841713f484f049e056b2db1 )
135 0xb772b306fd2c989765c6c02e93f7c4d3b5c62319.0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0( 0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0, 0x000000000000000000000000b787ad2ce37bbbf69841713f484f049e056b2db1, 0x0000000000000000000000008dee25e6409b8e50d21a404e016c75ae24eee833 )
136 0xb772b306fd2c989765c6c02e93f7c4d3b5c62319.0x7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb3847402498( 0x7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb3847402498, 0000000000000000000000000000000000000000000000000000000000000001 )
137 TransparentUpgradeableProxy.0xc55059ac2447c53f8a54711332dfb23f761bb2fb354c6bc56adf94d2494f51af( 0xc55059ac2447c53f8a54711332dfb23f761bb2fb354c6bc56adf94d2494f51af, 0000000000000000000000008dee25e6409b8e50d21a404e016c75ae24eee833, 0000000000000000000000000000000000000000000000000000000000000100, 0000000000000000000000000000000000000000000000000000000000000140, 0000000000000000000000000000000000000000000000000000000000000180, 00000000000000000000000000000000000000000000000000000000000001c0, 0000000000000000000000000000000000000000000000000000000000000001, 0000000000000000000000000000000000000000000000000000000000000001, 000000000000000000000000b772b306fd2c989765c6c02e93f7c4d3b5c62319, 000000000000000000000000000000000000000000000000000000000000000d, 5342542050726f64205465737400000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000003, 5350540000000000000000000000000000000000000000000000000000000000, 000000000000000000000000000000000000000000000000000000000000001d, 687474703a2f2f6c6f63616c686f73742f636f6e74726163742d757269000000, 0000000000000000000000000000000000000000000000000000000000000018, 687474703a2f2f6c6f63616c686f73742f6173736574732f0000000000000000 )

Account State Difference:

  Address   Before After State Difference Code
0x8dEE25E6...e24eEE833
0.067637986 Eth
Nonce: 4
0.059012931360289168 Eth
Nonce: 5
0.008625054639710832
0xB772b306...3b5C62319
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 187562029666179973737192625929148351059617740431021769961872005080562458579627406174008894881352778307853902628057325843793734829175408726121900945738200330494094942429130018757005607577418822831551410176809086984727269363875192520129994313721369065466273936949071740677186382944523609526748450889768530473156983909288050234435311942745410077657445341108083391979535663947285459045100153348488798242819912391451161427293866056606524399085887626715801620855911785893217002927112926943481984830093108584816418942247260304487302785609483871157805667956711050954127779612582757211000513665128761630112701507946935807358920055676247230621013321611098094186255195953942070380387086554849420802223356459236606863685672181841666051492942733482747463896758843610973366190702351151726385579902969678131810564944132573669441185338749579450314941046849587669184547304828789660710541097729821315343388002356368844698138848685936368688264735511026535956019493938000416062351800018881975013948799624324549166900527704526442161440818576983250003474187603025793830813882579332711503849884923800717133924079681997010777256596425220073103349519939625221965201716346332477235828466438162303585265938280670616115677990858097768782040733198956807023873663764545781815380843919676583346844368464910471132581329548977678047715520334256056867135858056616930517058672690948340735242338663661244261857460545182534691982547582660662143105074512947659459843837629615849123293804944969177507819272835638889309539042585877582513078919897951117166254037123562875328463230249435301653439127184361083772552602309450692629290701848486668042461508474846108125229766695561727978096406062675886802299635476078552240557757532120942686218456034110881510293521043286581786148300547245396534536881054981771814152854596770714051223288728853125328037924563320600513358415198186141794616505440511106308183696024040015166467802864762145373419694796070936696852031641391156569024471061378583483872748050224777449081033465894952963771075072639653501284450921676851
0xB787Ad2c...e056B2dB1
(bloXroute: Max Profit Builder)
2.455565881516273163 Eth2.456439057016273163 Eth0.0008731755

Execution Trace

TransparentUpgradeableProxy.2851eee1( )
  • SBTFactory.createProxy( owner=0x8dEE25E6409b8e50D21a404e016C75ae24eEE833, name=SBT Prod Test, symbol=SPT, contractURI=http://localhost/contract-uri, baseURI=http://localhost/assets/, claimable=True )
    • 0xb772b306fd2c989765c6c02e93f7c4d3b5c62319.60806040( )
      • 0xa1766584a3331a043532ad858b1396a07b26042f.STATICCALL( )
        • 0xba4e92267b9083e36d24c1bd8e7dd53f3d328855.STATICCALL( )
        • 0xa1766584a3331a043532ad858b1396a07b26042f.STATICCALL( )
          • 0xba4e92267b9083e36d24c1bd8e7dd53f3d328855.STATICCALL( )
          • SBT.initialize( owner=0x8dEE25E6409b8e50D21a404e016C75ae24eEE833, name_=SBT Prod Test, symbol_=SPT, contractURI_=http://localhost/contract-uri, baseURI=http://localhost/assets/, claimable_=True )
            File 1 of 3: TransparentUpgradeableProxy
            // SPDX-License-Identifier: MIT
            pragma solidity ^0.8.0;
            import "@openzeppelin/contracts/proxy/beacon/BeaconProxy.sol";
            import "@openzeppelin/contracts/proxy/beacon/UpgradeableBeacon.sol";
            import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol";
            import "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol";
            import "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol";
            // Kept for backwards compatibility with older versions of Hardhat and Truffle plugins.
            contract AdminUpgradeabilityProxy is TransparentUpgradeableProxy {
                constructor(address logic, address admin, bytes memory data) payable TransparentUpgradeableProxy(logic, admin, data) {}
            }
            // SPDX-License-Identifier: MIT
            pragma solidity ^0.8.0;
            import "./IBeacon.sol";
            import "../Proxy.sol";
            import "../ERC1967/ERC1967Upgrade.sol";
            /**
             * @dev This contract implements a proxy that gets the implementation address for each call from a {UpgradeableBeacon}.
             *
             * The beacon address is stored in storage slot `uint256(keccak256('eip1967.proxy.beacon')) - 1`, so that it doesn't
             * conflict with the storage layout of the implementation behind the proxy.
             *
             * _Available since v3.4._
             */
            contract BeaconProxy is Proxy, ERC1967Upgrade {
                /**
                 * @dev Initializes the proxy with `beacon`.
                 *
                 * If `data` is nonempty, it's used as data in a delegate call to the implementation returned by the beacon. This
                 * will typically be an encoded function call, and allows initializating the storage of the proxy like a Solidity
                 * constructor.
                 *
                 * Requirements:
                 *
                 * - `beacon` must be a contract with the interface {IBeacon}.
                 */
                constructor(address beacon, bytes memory data) payable {
                    assert(_BEACON_SLOT == bytes32(uint256(keccak256("eip1967.proxy.beacon")) - 1));
                    _upgradeBeaconToAndCall(beacon, data, false);
                }
                /**
                 * @dev Returns the current beacon address.
                 */
                function _beacon() internal view virtual returns (address) {
                    return _getBeacon();
                }
                /**
                 * @dev Returns the current implementation address of the associated beacon.
                 */
                function _implementation() internal view virtual override returns (address) {
                    return IBeacon(_getBeacon()).implementation();
                }
                /**
                 * @dev Changes the proxy to use a new beacon. Deprecated: see {_upgradeBeaconToAndCall}.
                 *
                 * If `data` is nonempty, it's used as data in a delegate call to the implementation returned by the beacon.
                 *
                 * Requirements:
                 *
                 * - `beacon` must be a contract.
                 * - The implementation returned by `beacon` must be a contract.
                 */
                function _setBeacon(address beacon, bytes memory data) internal virtual {
                    _upgradeBeaconToAndCall(beacon, data, false);
                }
            }
            // SPDX-License-Identifier: MIT
            pragma solidity ^0.8.0;
            import "./IBeacon.sol";
            import "../../access/Ownable.sol";
            import "../../utils/Address.sol";
            /**
             * @dev This contract is used in conjunction with one or more instances of {BeaconProxy} to determine their
             * implementation contract, which is where they will delegate all function calls.
             *
             * An owner is able to change the implementation the beacon points to, thus upgrading the proxies that use this beacon.
             */
            contract UpgradeableBeacon is IBeacon, Ownable {
                address private _implementation;
                /**
                 * @dev Emitted when the implementation returned by the beacon is changed.
                 */
                event Upgraded(address indexed implementation);
                /**
                 * @dev Sets the address of the initial implementation, and the deployer account as the owner who can upgrade the
                 * beacon.
                 */
                constructor(address implementation_) {
                    _setImplementation(implementation_);
                }
                /**
                 * @dev Returns the current implementation address.
                 */
                function implementation() public view virtual override returns (address) {
                    return _implementation;
                }
                /**
                 * @dev Upgrades the beacon to a new implementation.
                 *
                 * Emits an {Upgraded} event.
                 *
                 * Requirements:
                 *
                 * - msg.sender must be the owner of the contract.
                 * - `newImplementation` must be a contract.
                 */
                function upgradeTo(address newImplementation) public virtual onlyOwner {
                    _setImplementation(newImplementation);
                    emit Upgraded(newImplementation);
                }
                /**
                 * @dev Sets the implementation contract address for this beacon
                 *
                 * Requirements:
                 *
                 * - `newImplementation` must be a contract.
                 */
                function _setImplementation(address newImplementation) private {
                    require(Address.isContract(newImplementation), "UpgradeableBeacon: implementation is not a contract");
                    _implementation = newImplementation;
                }
            }
            // SPDX-License-Identifier: MIT
            pragma solidity ^0.8.0;
            import "../Proxy.sol";
            import "./ERC1967Upgrade.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.
             */
            contract ERC1967Proxy is Proxy, ERC1967Upgrade {
                /**
                 * @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));
                    _upgradeToAndCall(_logic, _data, false);
                }
                /**
                 * @dev Returns the current implementation address.
                 */
                function _implementation() internal view virtual override returns (address impl) {
                    return ERC1967Upgrade._getImplementation();
                }
            }
            // SPDX-License-Identifier: MIT
            pragma solidity ^0.8.0;
            import "../ERC1967/ERC1967Proxy.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 interface of your proxy.
             */
            contract TransparentUpgradeableProxy is ERC1967Proxy {
                /**
                 * @dev Initializes an upgradeable proxy managed by `_admin`, backed by the implementation at `_logic`, and
                 * optionally initialized with `_data` as explained in {ERC1967Proxy-constructor}.
                 */
                constructor(address _logic, address admin_, bytes memory _data) payable ERC1967Proxy(_logic, _data) {
                    assert(_ADMIN_SLOT == bytes32(uint256(keccak256("eip1967.proxy.admin")) - 1));
                    _changeAdmin(admin_);
                }
                /**
                 * @dev Modifier used internally that will delegate the call to the implementation unless the sender is the admin.
                 */
                modifier ifAdmin() {
                    if (msg.sender == _getAdmin()) {
                        _;
                    } 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 admin_) {
                    admin_ = _getAdmin();
                }
                /**
                 * @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 implementation_) {
                    implementation_ = _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 virtual ifAdmin {
                    _changeAdmin(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 {
                    _upgradeToAndCall(newImplementation, bytes(""), false);
                }
                /**
                 * @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 {
                    _upgradeToAndCall(newImplementation, data, true);
                }
                /**
                 * @dev Returns the current admin.
                 */
                function _admin() internal view virtual returns (address) {
                    return _getAdmin();
                }
                /**
                 * @dev Makes sure the admin cannot access the fallback function. See {Proxy-_beforeFallback}.
                 */
                function _beforeFallback() internal virtual override {
                    require(msg.sender != _getAdmin(), "TransparentUpgradeableProxy: admin cannot fallback to proxy target");
                    super._beforeFallback();
                }
            }
            // SPDX-License-Identifier: MIT
            pragma solidity ^0.8.0;
            import "./TransparentUpgradeableProxy.sol";
            import "../../access/Ownable.sol";
            /**
             * @dev This is an auxiliary contract meant to be assigned as the admin of a {TransparentUpgradeableProxy}. For an
             * explanation of why you would want to use this see the documentation for {TransparentUpgradeableProxy}.
             */
            contract ProxyAdmin is Ownable {
                /**
                 * @dev Returns the current implementation of `proxy`.
                 *
                 * Requirements:
                 *
                 * - This contract must be the admin of `proxy`.
                 */
                function getProxyImplementation(TransparentUpgradeableProxy proxy) public view virtual returns (address) {
                    // We need to manually run the static call since the getter cannot be flagged as view
                    // bytes4(keccak256("implementation()")) == 0x5c60da1b
                    (bool success, bytes memory returndata) = address(proxy).staticcall(hex"5c60da1b");
                    require(success);
                    return abi.decode(returndata, (address));
                }
                /**
                 * @dev Returns the current admin of `proxy`.
                 *
                 * Requirements:
                 *
                 * - This contract must be the admin of `proxy`.
                 */
                function getProxyAdmin(TransparentUpgradeableProxy proxy) public view virtual returns (address) {
                    // We need to manually run the static call since the getter cannot be flagged as view
                    // bytes4(keccak256("admin()")) == 0xf851a440
                    (bool success, bytes memory returndata) = address(proxy).staticcall(hex"f851a440");
                    require(success);
                    return abi.decode(returndata, (address));
                }
                /**
                 * @dev Changes the admin of `proxy` to `newAdmin`.
                 *
                 * Requirements:
                 *
                 * - This contract must be the current admin of `proxy`.
                 */
                function changeProxyAdmin(TransparentUpgradeableProxy proxy, address newAdmin) public virtual onlyOwner {
                    proxy.changeAdmin(newAdmin);
                }
                /**
                 * @dev Upgrades `proxy` to `implementation`. See {TransparentUpgradeableProxy-upgradeTo}.
                 *
                 * Requirements:
                 *
                 * - This contract must be the admin of `proxy`.
                 */
                function upgrade(TransparentUpgradeableProxy proxy, address implementation) public virtual onlyOwner {
                    proxy.upgradeTo(implementation);
                }
                /**
                 * @dev Upgrades `proxy` to `implementation` and calls a function on the new implementation. See
                 * {TransparentUpgradeableProxy-upgradeToAndCall}.
                 *
                 * Requirements:
                 *
                 * - This contract must be the admin of `proxy`.
                 */
                function upgradeAndCall(TransparentUpgradeableProxy proxy, address implementation, bytes memory data) public payable virtual onlyOwner {
                    proxy.upgradeToAndCall{value: msg.value}(implementation, data);
                }
            }
            // SPDX-License-Identifier: MIT
            pragma solidity ^0.8.0;
            /**
             * @dev This is the interface that {BeaconProxy} expects of its beacon.
             */
            interface IBeacon {
                /**
                 * @dev Must return an address that can be used as a delegate call target.
                 *
                 * {BeaconProxy} will check that this address is a contract.
                 */
                function implementation() external view returns (address);
            }
            // SPDX-License-Identifier: MIT
            pragma solidity ^0.8.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 virtual {
                    // 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 view virtual 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 virtual {
                    _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 () external payable virtual {
                    _fallback();
                }
                /**
                 * @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if call data
                 * is empty.
                 */
                receive () external payable virtual {
                    _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.8.2;
            import "../beacon/IBeacon.sol";
            import "../../utils/Address.sol";
            import "../../utils/StorageSlot.sol";
            /**
             * @dev This abstract contract provides getters and event emitting update functions for
             * https://eips.ethereum.org/EIPS/eip-1967[EIP1967] slots.
             *
             * _Available since v4.1._
             *
             * @custom:oz-upgrades-unsafe-allow delegatecall
             */
            abstract contract ERC1967Upgrade {
                // This is the keccak-256 hash of "eip1967.proxy.rollback" subtracted by 1
                bytes32 private constant _ROLLBACK_SLOT = 0x4910fdfa16fed3260ed0e7147f7cc6da11a60208b5b9406d12a635614ffd9143;
                /**
                 * @dev Storage slot with the address of the current implementation.
                 * This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1, and is
                 * validated in the constructor.
                 */
                bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
                /**
                 * @dev Emitted when the implementation is upgraded.
                 */
                event Upgraded(address indexed implementation);
                /**
                 * @dev Returns the current implementation address.
                 */
                function _getImplementation() internal view returns (address) {
                    return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
                }
                /**
                 * @dev Stores a new address in the EIP1967 implementation slot.
                 */
                function _setImplementation(address newImplementation) private {
                    require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract");
                    StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
                }
                /**
                 * @dev Perform implementation upgrade
                 *
                 * Emits an {Upgraded} event.
                 */
                function _upgradeTo(address newImplementation) internal {
                    _setImplementation(newImplementation);
                    emit Upgraded(newImplementation);
                }
                /**
                 * @dev Perform implementation upgrade with additional setup call.
                 *
                 * Emits an {Upgraded} event.
                 */
                function _upgradeToAndCall(address newImplementation, bytes memory data, bool forceCall) internal {
                    _setImplementation(newImplementation);
                    emit Upgraded(newImplementation);
                    if (data.length > 0 || forceCall) {
                        Address.functionDelegateCall(newImplementation, data);
                    }
                }
                /**
                 * @dev Perform implementation upgrade with security checks for UUPS proxies, and additional setup call.
                 *
                 * Emits an {Upgraded} event.
                 */
                function _upgradeToAndCallSecure(address newImplementation, bytes memory data, bool forceCall) internal {
                    address oldImplementation = _getImplementation();
                    // Initial upgrade and setup call
                    _setImplementation(newImplementation);
                    if (data.length > 0 || forceCall) {
                        Address.functionDelegateCall(newImplementation, data);
                    }
                    // Perform rollback test if not already in progress
                    StorageSlot.BooleanSlot storage rollbackTesting = StorageSlot.getBooleanSlot(_ROLLBACK_SLOT);
                    if (!rollbackTesting.value) {
                        // Trigger rollback using upgradeTo from the new implementation
                        rollbackTesting.value = true;
                        Address.functionDelegateCall(
                            newImplementation,
                            abi.encodeWithSignature(
                                "upgradeTo(address)",
                                oldImplementation
                            )
                        );
                        rollbackTesting.value = false;
                        // Check rollback was effective
                        require(oldImplementation == _getImplementation(), "ERC1967Upgrade: upgrade breaks further upgrades");
                        // Finally reset to the new implementation and log the upgrade
                        _setImplementation(newImplementation);
                        emit Upgraded(newImplementation);
                    }
                }
                /**
                 * @dev Perform beacon upgrade with additional setup call. Note: This upgrades the address of the beacon, it does
                 * not upgrade the implementation contained in the beacon (see {UpgradeableBeacon-_setImplementation} for that).
                 *
                 * Emits a {BeaconUpgraded} event.
                 */
                function _upgradeBeaconToAndCall(address newBeacon, bytes memory data, bool forceCall) internal {
                    _setBeacon(newBeacon);
                    emit BeaconUpgraded(newBeacon);
                    if (data.length > 0 || forceCall) {
                        Address.functionDelegateCall(IBeacon(newBeacon).implementation(), data);
                    }
                }
                /**
                 * @dev Storage slot with the admin of the contract.
                 * This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1, and is
                 * validated in the constructor.
                 */
                bytes32 internal constant _ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;
                /**
                 * @dev Emitted when the admin account has changed.
                 */
                event AdminChanged(address previousAdmin, address newAdmin);
                /**
                 * @dev Returns the current admin.
                 */
                function _getAdmin() internal view returns (address) {
                    return StorageSlot.getAddressSlot(_ADMIN_SLOT).value;
                }
                /**
                 * @dev Stores a new address in the EIP1967 admin slot.
                 */
                function _setAdmin(address newAdmin) private {
                    require(newAdmin != address(0), "ERC1967: new admin is the zero address");
                    StorageSlot.getAddressSlot(_ADMIN_SLOT).value = newAdmin;
                }
                /**
                 * @dev Changes the admin of the proxy.
                 *
                 * Emits an {AdminChanged} event.
                 */
                function _changeAdmin(address newAdmin) internal {
                    emit AdminChanged(_getAdmin(), newAdmin);
                    _setAdmin(newAdmin);
                }
                /**
                 * @dev The storage slot of the UpgradeableBeacon contract which defines the implementation for this proxy.
                 * This is bytes32(uint256(keccak256('eip1967.proxy.beacon')) - 1)) and is validated in the constructor.
                 */
                bytes32 internal constant _BEACON_SLOT = 0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50;
                /**
                 * @dev Emitted when the beacon is upgraded.
                 */
                event BeaconUpgraded(address indexed beacon);
                /**
                 * @dev Returns the current beacon.
                 */
                function _getBeacon() internal view returns (address) {
                    return StorageSlot.getAddressSlot(_BEACON_SLOT).value;
                }
                /**
                 * @dev Stores a new beacon in the EIP1967 beacon slot.
                 */
                function _setBeacon(address newBeacon) private {
                    require(
                        Address.isContract(newBeacon),
                        "ERC1967: new beacon is not a contract"
                    );
                    require(
                        Address.isContract(IBeacon(newBeacon).implementation()),
                        "ERC1967: beacon implementation is not a contract"
                    );
                    StorageSlot.getAddressSlot(_BEACON_SLOT).value = newBeacon;
                }
            }
            // SPDX-License-Identifier: MIT
            pragma solidity ^0.8.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) {
                    // This method relies on extcodesize, which returns 0 for contracts in
                    // construction, since the code is only stored at the end of the
                    // constructor execution.
                    uint256 size;
                    // solhint-disable-next-line no-inline-assembly
                    assembly { size := extcodesize(account) }
                    return size > 0;
                }
                /**
                 * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
                 * `recipient`, forwarding all available gas and reverting on errors.
                 *
                 * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
                 * of certain opcodes, possibly making contracts go over the 2300 gas limit
                 * imposed by `transfer`, making them unable to receive funds via
                 * `transfer`. {sendValue} removes this limitation.
                 *
                 * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
                 *
                 * IMPORTANT: because control is transferred to `recipient`, care must be
                 * taken to not create reentrancy vulnerabilities. Consider using
                 * {ReentrancyGuard} or the
                 * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
                 */
                function sendValue(address payable recipient, uint256 amount) internal {
                    require(address(this).balance >= amount, "Address: insufficient balance");
                    // 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");
                    require(isContract(target), "Address: call to non-contract");
                    // solhint-disable-next-line avoid-low-level-calls
                    (bool success, bytes memory returndata) = target.call{ value: value }(data);
                    return _verifyCallResult(success, returndata, errorMessage);
                }
                /**
                 * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                 * but performing a static call.
                 *
                 * _Available since v3.3._
                 */
                function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
                    return functionStaticCall(target, data, "Address: low-level static call failed");
                }
                /**
                 * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
                 * but performing a static call.
                 *
                 * _Available since v3.3._
                 */
                function functionStaticCall(address target, bytes memory data, string memory errorMessage) internal view returns (bytes memory) {
                    require(isContract(target), "Address: static call to non-contract");
                    // solhint-disable-next-line avoid-low-level-calls
                    (bool success, bytes memory returndata) = target.staticcall(data);
                    return _verifyCallResult(success, returndata, errorMessage);
                }
                /**
                 * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                 * but performing a delegate call.
                 *
                 * _Available since v3.4._
                 */
                function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
                    return functionDelegateCall(target, data, "Address: low-level delegate call failed");
                }
                /**
                 * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
                 * but performing a delegate call.
                 *
                 * _Available since v3.4._
                 */
                function functionDelegateCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
                    require(isContract(target), "Address: delegate call to non-contract");
                    // solhint-disable-next-line avoid-low-level-calls
                    (bool success, bytes memory returndata) = target.delegatecall(data);
                    return _verifyCallResult(success, returndata, errorMessage);
                }
                function _verifyCallResult(bool success, bytes memory returndata, string memory errorMessage) private pure returns(bytes memory) {
                    if (success) {
                        return returndata;
                    } else {
                        // Look for revert reason and bubble it up if present
                        if (returndata.length > 0) {
                            // The easiest way to bubble the revert reason is using memory via assembly
                            // solhint-disable-next-line no-inline-assembly
                            assembly {
                                let returndata_size := mload(returndata)
                                revert(add(32, returndata), returndata_size)
                            }
                        } else {
                            revert(errorMessage);
                        }
                    }
                }
            }
            // SPDX-License-Identifier: MIT
            pragma solidity ^0.8.0;
            /**
             * @dev Library for reading and writing primitive types to specific storage slots.
             *
             * Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts.
             * This library helps with reading and writing to such slots without the need for inline assembly.
             *
             * The functions in this library return Slot structs that contain a `value` member that can be used to read or write.
             *
             * Example usage to set ERC1967 implementation slot:
             * ```
             * contract ERC1967 {
             *     bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
             *
             *     function _getImplementation() internal view returns (address) {
             *         return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
             *     }
             *
             *     function _setImplementation(address newImplementation) internal {
             *         require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract");
             *         StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
             *     }
             * }
             * ```
             *
             * _Available since v4.1 for `address`, `bool`, `bytes32`, and `uint256`._
             */
            library StorageSlot {
                struct AddressSlot {
                    address value;
                }
                struct BooleanSlot {
                    bool value;
                }
                struct Bytes32Slot {
                    bytes32 value;
                }
                struct Uint256Slot {
                    uint256 value;
                }
                /**
                 * @dev Returns an `AddressSlot` with member `value` located at `slot`.
                 */
                function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {
                    assembly {
                        r.slot := slot
                    }
                }
                /**
                 * @dev Returns an `BooleanSlot` with member `value` located at `slot`.
                 */
                function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {
                    assembly {
                        r.slot := slot
                    }
                }
                /**
                 * @dev Returns an `Bytes32Slot` with member `value` located at `slot`.
                 */
                function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {
                    assembly {
                        r.slot := slot
                    }
                }
                /**
                 * @dev Returns an `Uint256Slot` with member `value` located at `slot`.
                 */
                function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {
                    assembly {
                        r.slot := slot
                    }
                }
            }
            // SPDX-License-Identifier: MIT
            pragma solidity ^0.8.0;
            import "../utils/Context.sol";
            /**
             * @dev Contract module which provides a basic access control mechanism, where
             * there is an account (an owner) that can be granted exclusive access to
             * specific functions.
             *
             * By default, the owner account will be the one that deploys the contract. This
             * can later be changed with {transferOwnership}.
             *
             * This module is used through inheritance. It will make available the modifier
             * `onlyOwner`, which can be applied to your functions to restrict their use to
             * the owner.
             */
            abstract contract Ownable is Context {
                address private _owner;
                event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
                /**
                 * @dev Initializes the contract setting the deployer as the initial owner.
                 */
                constructor () {
                    address msgSender = _msgSender();
                    _owner = msgSender;
                    emit OwnershipTransferred(address(0), msgSender);
                }
                /**
                 * @dev Returns the address of the current owner.
                 */
                function owner() public view virtual returns (address) {
                    return _owner;
                }
                /**
                 * @dev Throws if called by any account other than the owner.
                 */
                modifier onlyOwner() {
                    require(owner() == _msgSender(), "Ownable: caller is not the owner");
                    _;
                }
                /**
                 * @dev Leaves the contract without owner. It will not be possible to call
                 * `onlyOwner` functions anymore. Can only be called by the current owner.
                 *
                 * NOTE: Renouncing ownership will leave the contract without an owner,
                 * thereby removing any functionality that is only available to the owner.
                 */
                function renounceOwnership() public virtual onlyOwner {
                    emit OwnershipTransferred(_owner, address(0));
                    _owner = address(0);
                }
                /**
                 * @dev Transfers ownership of the contract to a new account (`newOwner`).
                 * Can only be called by the current owner.
                 */
                function transferOwnership(address newOwner) public virtual onlyOwner {
                    require(newOwner != address(0), "Ownable: new owner is the zero address");
                    emit OwnershipTransferred(_owner, newOwner);
                    _owner = newOwner;
                }
            }
            // SPDX-License-Identifier: MIT
            pragma solidity ^0.8.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 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 calldata) {
                    this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
                    return msg.data;
                }
            }
            

            File 2 of 3: SBTFactory
            // SPDX-License-Identifier: MIT
            pragma solidity ^0.8.16;
            import "@openzeppelin/contracts-upgradeable/utils/CountersUpgradeable.sol";
            import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
            import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
            import "@openzeppelin/contracts/proxy/beacon/BeaconProxy.sol";
            import "./SBT.sol";
            import "./Beacon.sol";
            contract SBTFactory is Initializable, OwnableUpgradeable {
                using CountersUpgradeable for CountersUpgradeable.Counter;
                Beacon internal _beacon;
                mapping(uint256 => address) internal _proxies;
                CountersUpgradeable.Counter internal _proxyIdCounter;
                event ProxyCreated(
                    address owner,
                    string name,
                    string symbol,
                    string contractUri,
                    string baseUri,
                    bool claimable,
                    uint256 proxyIndex,
                    address proxyAddress
                );
                /// @custom:oz-upgrades-unsafe-allow constructor
                constructor() {
                    _disableInitializers();
                }
                function initialize(address implContract) public initializer {
                    _beacon = new Beacon(implContract);
                    __Ownable_init();
                }
                /// @notice Create SBT proxy contract
                /// @param owner: SBT contract owner
                /// @param name: SBT contract name
                /// @param symbol: SBT contract symbol
                /// @param contractURI: SBT contract contractURI
                /// @param baseURI: SBT contract baseURI of tokenURI
                /// @param claimable: SBT token claimable
                function createProxy(
                    address owner,
                    string memory name,
                    string memory symbol,
                    string memory contractURI,
                    string memory baseURI,
                    bool claimable
                ) external virtual {
                    BeaconProxy proxy = new BeaconProxy(
                        address(_beacon),
                        abi.encodeWithSelector(SBT.initialize.selector, owner, name, symbol, contractURI, baseURI, claimable)
                    );
                    _proxyIdCounter.increment();
                    uint256 index = _proxyIdCounter.current();
                    _proxies[index] = address(proxy);
                    emit ProxyCreated(owner, name, symbol, contractURI, baseURI, claimable, index, address(proxy));
                }
                /// @notice get the current implementation contract address
                function getImplementation() public view returns (address) {
                    return _beacon.implementation();
                }
                /// @notice get the beacon contract address
                function getBeacon() public view returns (address) {
                    return address(_beacon);
                }
                /// @notice get the beacon proxy contract address by index
                function getProxy(uint256 index) public view returns (address) {
                    return _proxies[index];
                }
                /// @notice get the beacon proxies count
                function getProxyCount() public view returns (uint256) {
                    return _proxyIdCounter.current();
                }
                /// @notice upgrade all beacon proxies
                /// @param implContract: new implementation contract address
                function upgradeProxies(address implContract) external onlyOwner {
                    _beacon.updateContract(implContract);
                }
            }
            // SPDX-License-Identifier: MIT
            pragma solidity ^0.8.16;
            import "@openzeppelin/contracts-upgradeable/utils/introspection/ERC165Upgradeable.sol";
            import "@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol";
            import "@openzeppelin/contracts-upgradeable/utils/StringsUpgradeable.sol";
            import "@openzeppelin/contracts-upgradeable/utils/CountersUpgradeable.sol";
            import "@openzeppelin/contracts-upgradeable/utils/structs/EnumerableSetUpgradeable.sol";
            import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
            import "@openzeppelin/contracts-upgradeable/utils/cryptography/SignatureCheckerUpgradeable.sol";
            import "@openzeppelin/contracts-upgradeable/utils/cryptography/draft-EIP712Upgradeable.sol";
            import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
            import "./IERC721Metadata.sol";
            import "./IERC4671.sol";
            import "./IERC5192.sol";
            import "./IERC4973.sol";
            import "./ISBT.sol";
            import "./ISBTReceiver.sol";
            contract SBT is
                Initializable,
                OwnableUpgradeable,
                ERC165Upgradeable,
                EIP712Upgradeable,
                IERC721Metadata,
                IERC4671,
                IERC5192,
                IERC4973,
                ISBT
            {
                using AddressUpgradeable for address;
                using StringsUpgradeable for uint256;
                using CountersUpgradeable for CountersUpgradeable.Counter;
                using EnumerableSetUpgradeable for EnumerableSetUpgradeable.UintSet;
                struct Token {
                    address owner;
                    uint256 expiration;
                    bool revoked;
                }
                string public constant VERSION = "1";
                bytes32 public constant AGREEMENT_TYPEHASH = keccak256("Agreement(address active,address passive,string tokenURI)");
                bool public claimable;
                // Token name
                string private _name;
                // Token symbol
                string private _symbol;
                address[] internal _minters;
                CountersUpgradeable.Counter internal _tokenIdCounter;
                string internal _baseTokenURI;
                string internal _contractURI;
                // Mapping from token ID to Token
                mapping(uint256 => Token) internal _tokens;
                // Mapping from holder address to their (enumerable) set of owned tokens
                mapping(address => EnumerableSetUpgradeable.UintSet) internal _holderTokens;
                // Optional mapping for token URIs
                mapping(uint256 => string) internal _tokenURIs;
                /// prevent implementation-contract init() from getting called
                /// @custom:oz-upgrades-unsafe-allow constructor
                constructor() {
                    _disableInitializers();
                }
                // function as a constructor's alternative
                function initialize(
                    address owner,
                    string memory name_,
                    string memory symbol_,
                    string memory contractURI_,
                    string memory baseURI,
                    bool claimable_
                ) public initializer {
                    __Ownable_init();
                    // Factory's transfering proxy ownership
                    transferOwnership(owner);
                    _name = name_;
                    _symbol = symbol_;
                    _contractURI = contractURI_;
                    claimable = claimable_;
                    _baseTokenURI = baseURI;
                    // set default minters
                    _minters.push(owner);
                    __Context_init();
                    __EIP712_init(name_, VERSION);
                }
                /**
                 * @dev See {IERC721Metadata-name}.
                 */
                function name() external view override returns (string memory) {
                    return _name;
                }
                /**
                 * @dev See {IERC721Metadata-symbol}.
                 */
                function symbol() external view override returns (string memory) {
                    return _symbol;
                }
                /**
                 * @dev See {IERC721Metadata-tokenURI}.
                 */
                function tokenURI(uint256 tokenId) external view virtual override returns (string memory) {
                    _requireMinted(tokenId);
                    string memory _tokenURI = _tokenURIs[tokenId];
                    if (bytes(_tokenURI).length > 0) {
                        return _tokenURI;
                    }
                    return bytes(_baseTokenURI).length > 0 ? string(abi.encodePacked(_baseTokenURI, tokenId.toString())) : "";
                }
                /**
                 * @dev See {IERC4671-isValid}.
                 */
                function isValid(uint256 tokenId) external view override returns (bool) {
                    /* solhint-disable */
                    return (_exists(tokenId) &&
                        !_tokens[tokenId].revoked &&
                        (_tokens[tokenId].expiration == 0 || _tokens[tokenId].expiration > block.timestamp));
                }
                /**
                 * @dev See {IERC4671-hasValid}.
                 */
                function hasValid(address owner) external view override returns (bool) {
                    return this.balanceOf(owner) > 0;
                }
                /**
                 * @dev See {IERC4671-balanceOf}.
                 */
                function balanceOf(address owner) external view override(IERC4671, IERC4973) returns (uint256) {
                    require(owner != address(0), "Invalid owner");
                    uint256 count = 0;
                    uint256 len = _holderTokens[owner].length();
                    for (uint256 i = 0; i < len; i++) {
                        uint256 tokenId = _holderTokens[owner].at(i);
                        if (this.isValid(tokenId)) count++;
                    }
                    return count;
                }
                /**
                 * @dev See {IERC4671-ownerOf}.
                 */
                function ownerOf(uint256 tokenId) external view override(IERC4671, IERC4973) returns (address) {
                    _requireMinted(tokenId);
                    return _tokens[tokenId].owner;
                }
                /**
                 * @dev See {IERC165-supportsInterface}.
                 */
                function supportsInterface(bytes4 interfaceId)
                    public
                    view
                    virtual
                    override(ERC165Upgradeable, IERC165)
                    returns (bool)
                {
                    return
                        interfaceId == type(IERC721Metadata).interfaceId ||
                        interfaceId == type(IERC4671).interfaceId ||
                        interfaceId == type(IERC5192).interfaceId ||
                        interfaceId == type(IERC4973).interfaceId ||
                        super.supportsInterface(interfaceId);
                }
                /// @dev See {IERC5192-locked}.
                /// @return always return True
                function locked(uint256 tokenId) external view override returns (bool) {
                    _requireMinted(tokenId);
                    return true;
                }
                /**
                 * @dev See {IERC4973-unequip}.
                 */
                function unequip(uint256 tokenId) external override {
                    _requireMinted(tokenId);
                    _burn(tokenId);
                }
                /**
                 * @dev See {IERC4973-give}.
                 */
                function give(
                    address to,
                    string calldata uri,
                    bytes calldata signature
                ) external override returns (uint256) {
                    require(_msgSender() != to, "cannot give from self");
                    uint256 tokenId = _safeCheckAgreement(_msgSender(), to, uri, signature);
                    _safeMint(to, tokenId);
                    _setTokenURI(tokenId, uri);
                    return tokenId;
                }
                /**
                 * @dev See {IERC4973-take}.
                 */
                function take(
                    address from,
                    string calldata uri,
                    bytes calldata signature
                ) external override returns (uint256) {
                    _requireClaimable();
                    require(_msgSender() != from, "cannot take from self");
                    uint256 tokenId = _safeCheckAgreement(_msgSender(), from, uri, signature);
                    _safeMint(from, tokenId);
                    _setTokenURI(tokenId, uri);
                    return tokenId;
                }
                // ============================================================
                // SBT Standard
                // ============================================================
                /// @notice Mark the token as revoked
                /// @param tokenId Identifier of the token
                function revoke(uint256 tokenId, string calldata reason) external virtual onlyOwner {
                    _requireMinted(tokenId);
                    require(!_tokens[tokenId].revoked, "Token already revoked");
                    _tokens[tokenId].revoked = true;
                    // // TODO: consider remove token from _holderTokens
                    // _holderTokens[_tokens[tokenId].owner].remove(tokenId);
                    // emit event {IERC4671-Revoked}
                    emit Revoked(_tokens[tokenId].owner, tokenId);
                    emit RevokedByReason(_tokens[tokenId].owner, tokenId, reason);
                }
                /// @notice claim a SBT
                /// @dev TODO: User should be able to claim token by proof/signature!?
                /// @dev TODO: This is free for claiming?
                function claim() external virtual {
                    _requireClaimable();
                    _tokenIdCounter.increment();
                    uint256 newTokenId = _tokenIdCounter.current();
                    _safeMint(_msgSender(), newTokenId);
                }
                /// @notice mint to receivers
                /// @dev TODO: This is free for claiming?
                /// @param receivers The SBT receiver list
                /// @param expiration expired block timestamp
                function mint(address[] calldata receivers, uint256 expiration) external virtual onlyMinter {
                    require(expiration == 0 || expiration > block.timestamp, "Invalid expiration");
                    for (uint256 i = 0; i < receivers.length; i++) {
                        _tokenIdCounter.increment();
                        uint256 newTokenId = _tokenIdCounter.current();
                        _safeMint(receivers[i], newTokenId);
                        if (expiration > 0) {
                            _tokens[newTokenId].expiration = expiration;
                            emit ExpirationSet(newTokenId, expiration);
                        }
                    }
                }
                /// @notice burn a SBT
                /// @dev only owner/winner can burn owned SBT
                /// @param tokenId SBT ID
                function burn(uint256 tokenId) external virtual {
                    _burn(tokenId);
                }
                /// @notice set SBT expiration
                /// @param tokenId SBT ID
                /// @param expiration expired block timestamp
                function setExpiration(uint256 tokenId, uint256 expiration) external virtual onlyOwner {
                    _requireMinted(tokenId);
                    require(!_tokens[tokenId].revoked, "Token already revoked");
                    require(expiration == 0 || expiration > block.timestamp, "Invalid expiration");
                    _tokens[tokenId].expiration = expiration;
                    emit ExpirationSet(tokenId, expiration);
                }
                /**
                 * @dev If set, the resulting URI for each token will be the concatenation of the `baseURI` and the `tokenId`. Empty by default
                 */
                function setBaseURI(string calldata baseURI) external virtual onlyOwner {
                    _baseTokenURI = baseURI;
                    emit BaseURISet(_baseTokenURI);
                }
                /// @notice set a minter list
                /// @param minters list of minter address
                function setMinters(address[] calldata minters) external onlyOwner {
                    _minters = minters;
                }
                /// @notice enable/disable claiming SBT
                /// @param claimable_ flag to enable/disable claiming SBT
                function setClaimable(bool claimable_) external onlyOwner {
                    claimable = claimable_;
                    emit ClaimableSet(claimable);
                }
                /// @notice Return the contract URI
                /// @return contractUri The contract URI.
                function contractURI() external view returns (string memory) {
                    return _contractURI;
                }
                /// @notice Set the contract URI
                /// @param contractURI_ The contract URI to be set
                function setContractURI(string calldata contractURI_) external onlyOwner {
                    _contractURI = contractURI_;
                    emit ContractURISet(_contractURI);
                }
                function _mint(address to, uint256 tokenId) internal virtual {
                    require(to != address(0), "Mint to the zero address");
                    require(!_exists(tokenId), "Token already minted");
                    _tokens[tokenId].owner = to;
                    _holderTokens[to].add(tokenId);
                    // emit event {IERC5192-Locked}
                    emit Locked(tokenId);
                    // emit event {IERC4671-Minted}
                    emit Minted(to, tokenId);
                    // emit event {IERC4973-Transfer}
                    emit Transfer(address(0), to, tokenId);
                }
                function _burn(uint256 tokenId) internal virtual {
                    address owner = _tokens[tokenId].owner;
                    require(_msgSender() == owner, "Invalid owner");
                    _holderTokens[owner].remove(tokenId);
                    delete _tokens[tokenId];
                    if (bytes(_tokenURIs[tokenId]).length != 0) {
                        delete _tokenURIs[tokenId];
                    }
                    emit Transfer(owner, address(0), tokenId);
                    emit Burned(tokenId);
                }
                /**
                 * @dev Safely mints `tokenId` and transfers it to `to`.
                 *
                 * Requirements:
                 *
                 * - `tokenId` must not exist.
                 * - If `to` refers to a smart contract, it must implement {ISBTReceiver-onSBTReceived}, which is called upon a safe transfer.
                 *
                 * Emits a {Transfer} event.
                 */
                function _safeMint(address to, uint256 tokenId) internal virtual {
                    _safeMint(to, tokenId, "");
                }
                /**
                 * @dev Same as {_safeMint-address-uint256-}[`_safeMint`], with an additional `data` parameter which is
                 * forwarded in {ISBTReceiver-onSBTReceived} to contract recipients.
                 */
                function _safeMint(
                    address to,
                    uint256 tokenId,
                    bytes memory data
                ) internal virtual {
                    _mint(to, tokenId);
                    require(_checkOnSBTReceived(address(0), to, tokenId, data), "transfer to non SBTReceiver implementer");
                }
                function _setTokenURI(uint256 tokenId, string memory _tokenURI) internal virtual {
                    require(_exists(tokenId), "URI set of nonexistent token");
                    _tokenURIs[tokenId] = _tokenURI;
                }
                /**
                 * @dev Internal function to invoke {ISBTReceiver-onSBTReceived} on a target address.
                 * The call is not executed if the target address is not a contract.
                 *
                 * @param from address representing the previous owner of the given token ID
                 * @param to target address that will receive the tokens
                 * @param tokenId uint256 ID of the token to be transferred
                 * @param data bytes optional data to send along with the call
                 * @return bool whether the call correctly returned the expected magic value
                 */
                function _checkOnSBTReceived(
                    address from,
                    address to,
                    uint256 tokenId,
                    bytes memory data
                ) private returns (bool) {
                    /* solhint-disable */
                    if (to.isContract()) {
                        try ISBTReceiver(to).onSBTReceived(_msgSender(), from, tokenId, data) returns (bytes4 retval) {
                            return retval == ISBTReceiver.onSBTReceived.selector;
                        } catch (bytes memory reason) {
                            if (reason.length == 0) {
                                revert("transfer to non SBTReceiver implementer");
                            } else {
                                /// @solidity memory-safe-assembly
                                assembly {
                                    revert(add(32, reason), mload(reason))
                                }
                            }
                        }
                    } else {
                        return true;
                    }
                }
                function _exists(uint256 tokenId) internal view virtual returns (bool) {
                    Token memory token = _tokens[tokenId];
                    return token.owner != address(0);
                }
                /**
                 * @dev Reverts if the `tokenId` has not been minted yet.
                 */
                function _requireMinted(uint256 tokenId) internal view {
                    require(_exists(tokenId), "Invalid token ID");
                }
                /**
                 * @dev Reverts if _claimable is false.
                 */
                function _requireClaimable() internal view {
                    require(claimable, "Not allowed to claim");
                }
                function _existsMinter(address minter) internal view returns (bool) {
                    for (uint256 i = 0; i < _minters.length; i++) {
                        if (_minters[i] == minter) {
                            return true;
                        }
                    }
                    return false;
                }
                function _safeCheckAgreement(
                    address active,
                    address passive,
                    string calldata uri,
                    bytes calldata signature
                ) internal virtual returns (uint256) {
                    bytes32 hash = _hashTypedDataV4(
                        keccak256(abi.encode(AGREEMENT_TYPEHASH, active, passive, keccak256(bytes(uri))))
                    );
                    uint256 tokenId = uint256(hash);
                    require(SignatureCheckerUpgradeable.isValidSignatureNow(passive, hash, signature), "invalid signature");
                    return tokenId;
                }
                modifier onlyMinter() {
                    require(_existsMinter(_msgSender()), "Restricted to minters");
                    _;
                }
            }
            // SPDX-License-Identifier: UNLICENSED
            pragma solidity ^0.8.0;
            import "@openzeppelin/contracts/proxy/beacon/UpgradeableBeacon.sol";
            import "@openzeppelin/contracts/access/Ownable.sol";
            contract Beacon is Ownable {
                UpgradeableBeacon private immutable _beacon;
                address public implementationContract;
                event BeaconUpgradedTo(address oldImpl, address newImpl);
                constructor(address impl) {
                    _beacon = new UpgradeableBeacon(impl);
                    implementationContract = impl;
                }
                function updateContract(address impl) public onlyOwner {
                    _beacon.upgradeTo(impl);
                    emit BeaconUpgradedTo(implementationContract, impl);
                    implementationContract = impl;
                }
                function implementation() public view returns (address) {
                    return _beacon.implementation();
                }
            }
            // SPDX-License-Identifier: MIT
            // OpenZeppelin Contracts v4.4.1 (utils/Counters.sol)
            pragma solidity ^0.8.0;
            /**
             * @title Counters
             * @author Matt Condon (@shrugs)
             * @dev Provides counters that can only be incremented, decremented or reset. This can be used e.g. to track the number
             * of elements in a mapping, issuing ERC721 ids, or counting request ids.
             *
             * Include with `using Counters for Counters.Counter;`
             */
            library CountersUpgradeable {
                struct Counter {
                    // This variable should never be directly accessed by users of the library: interactions must be restricted to
                    // the library's function. As of Solidity v0.5.2, this cannot be enforced, though there is a proposal to add
                    // this feature: see https://github.com/ethereum/solidity/issues/4637
                    uint256 _value; // default: 0
                }
                function current(Counter storage counter) internal view returns (uint256) {
                    return counter._value;
                }
                function increment(Counter storage counter) internal {
                    unchecked {
                        counter._value += 1;
                    }
                }
                function decrement(Counter storage counter) internal {
                    uint256 value = counter._value;
                    require(value > 0, "Counter: decrement overflow");
                    unchecked {
                        counter._value = value - 1;
                    }
                }
                function reset(Counter storage counter) internal {
                    counter._value = 0;
                }
            }
            // SPDX-License-Identifier: MIT
            // OpenZeppelin Contracts (last updated v4.7.0) (proxy/beacon/BeaconProxy.sol)
            pragma solidity ^0.8.0;
            import "./IBeacon.sol";
            import "../Proxy.sol";
            import "../ERC1967/ERC1967Upgrade.sol";
            /**
             * @dev This contract implements a proxy that gets the implementation address for each call from an {UpgradeableBeacon}.
             *
             * The beacon address is stored in storage slot `uint256(keccak256('eip1967.proxy.beacon')) - 1`, so that it doesn't
             * conflict with the storage layout of the implementation behind the proxy.
             *
             * _Available since v3.4._
             */
            contract BeaconProxy is Proxy, ERC1967Upgrade {
                /**
                 * @dev Initializes the proxy with `beacon`.
                 *
                 * If `data` is nonempty, it's used as data in a delegate call to the implementation returned by the beacon. This
                 * will typically be an encoded function call, and allows initializing the storage of the proxy like a Solidity
                 * constructor.
                 *
                 * Requirements:
                 *
                 * - `beacon` must be a contract with the interface {IBeacon}.
                 */
                constructor(address beacon, bytes memory data) payable {
                    _upgradeBeaconToAndCall(beacon, data, false);
                }
                /**
                 * @dev Returns the current beacon address.
                 */
                function _beacon() internal view virtual returns (address) {
                    return _getBeacon();
                }
                /**
                 * @dev Returns the current implementation address of the associated beacon.
                 */
                function _implementation() internal view virtual override returns (address) {
                    return IBeacon(_getBeacon()).implementation();
                }
                /**
                 * @dev Changes the proxy to use a new beacon. Deprecated: see {_upgradeBeaconToAndCall}.
                 *
                 * If `data` is nonempty, it's used as data in a delegate call to the implementation returned by the beacon.
                 *
                 * Requirements:
                 *
                 * - `beacon` must be a contract.
                 * - The implementation returned by `beacon` must be a contract.
                 */
                function _setBeacon(address beacon, bytes memory data) internal virtual {
                    _upgradeBeaconToAndCall(beacon, data, false);
                }
            }
            // SPDX-License-Identifier: MIT
            // OpenZeppelin Contracts (last updated v4.7.0) (access/Ownable.sol)
            pragma solidity ^0.8.0;
            import "../utils/ContextUpgradeable.sol";
            import "../proxy/utils/Initializable.sol";
            /**
             * @dev Contract module which provides a basic access control mechanism, where
             * there is an account (an owner) that can be granted exclusive access to
             * specific functions.
             *
             * By default, the owner account will be the one that deploys the contract. This
             * can later be changed with {transferOwnership}.
             *
             * This module is used through inheritance. It will make available the modifier
             * `onlyOwner`, which can be applied to your functions to restrict their use to
             * the owner.
             */
            abstract contract OwnableUpgradeable is Initializable, ContextUpgradeable {
                address private _owner;
                event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
                /**
                 * @dev Initializes the contract setting the deployer as the initial owner.
                 */
                function __Ownable_init() internal onlyInitializing {
                    __Ownable_init_unchained();
                }
                function __Ownable_init_unchained() internal onlyInitializing {
                    _transferOwnership(_msgSender());
                }
                /**
                 * @dev Throws if called by any account other than the owner.
                 */
                modifier onlyOwner() {
                    _checkOwner();
                    _;
                }
                /**
                 * @dev Returns the address of the current owner.
                 */
                function owner() public view virtual returns (address) {
                    return _owner;
                }
                /**
                 * @dev Throws if the sender is not the owner.
                 */
                function _checkOwner() internal view virtual {
                    require(owner() == _msgSender(), "Ownable: caller is not the owner");
                }
                /**
                 * @dev Leaves the contract without owner. It will not be possible to call
                 * `onlyOwner` functions anymore. Can only be called by the current owner.
                 *
                 * NOTE: Renouncing ownership will leave the contract without an owner,
                 * thereby removing any functionality that is only available to the owner.
                 */
                function renounceOwnership() public virtual onlyOwner {
                    _transferOwnership(address(0));
                }
                /**
                 * @dev Transfers ownership of the contract to a new account (`newOwner`).
                 * Can only be called by the current owner.
                 */
                function transferOwnership(address newOwner) public virtual onlyOwner {
                    require(newOwner != address(0), "Ownable: new owner is the zero address");
                    _transferOwnership(newOwner);
                }
                /**
                 * @dev Transfers ownership of the contract to a new account (`newOwner`).
                 * Internal function without access restriction.
                 */
                function _transferOwnership(address newOwner) internal virtual {
                    address oldOwner = _owner;
                    _owner = newOwner;
                    emit OwnershipTransferred(oldOwner, newOwner);
                }
                /**
                 * @dev This empty reserved space is put in place to allow future versions to add new
                 * variables without shifting down storage in the inheritance chain.
                 * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
                 */
                uint256[49] private __gap;
            }
            // SPDX-License-Identifier: MIT
            // OpenZeppelin Contracts (last updated v4.8.0) (proxy/utils/Initializable.sol)
            pragma solidity ^0.8.2;
            import "../../utils/AddressUpgradeable.sol";
            /**
             * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
             * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
             * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
             * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
             *
             * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be
             * reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in
             * case an upgrade adds a module that needs to be initialized.
             *
             * For example:
             *
             * [.hljs-theme-light.nopadding]
             * ```
             * contract MyToken is ERC20Upgradeable {
             *     function initialize() initializer public {
             *         __ERC20_init("MyToken", "MTK");
             *     }
             * }
             * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {
             *     function initializeV2() reinitializer(2) public {
             *         __ERC20Permit_init("MyToken");
             *     }
             * }
             * ```
             *
             * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
             * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
             *
             * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
             * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
             *
             * [CAUTION]
             * ====
             * Avoid leaving a contract uninitialized.
             *
             * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
             * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke
             * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:
             *
             * [.hljs-theme-light.nopadding]
             * ```
             * /// @custom:oz-upgrades-unsafe-allow constructor
             * constructor() {
             *     _disableInitializers();
             * }
             * ```
             * ====
             */
            abstract contract Initializable {
                /**
                 * @dev Indicates that the contract has been initialized.
                 * @custom:oz-retyped-from bool
                 */
                uint8 private _initialized;
                /**
                 * @dev Indicates that the contract is in the process of being initialized.
                 */
                bool private _initializing;
                /**
                 * @dev Triggered when the contract has been initialized or reinitialized.
                 */
                event Initialized(uint8 version);
                /**
                 * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
                 * `onlyInitializing` functions can be used to initialize parent contracts.
                 *
                 * Similar to `reinitializer(1)`, except that functions marked with `initializer` can be nested in the context of a
                 * constructor.
                 *
                 * Emits an {Initialized} event.
                 */
                modifier initializer() {
                    bool isTopLevelCall = !_initializing;
                    require(
                        (isTopLevelCall && _initialized < 1) || (!AddressUpgradeable.isContract(address(this)) && _initialized == 1),
                        "Initializable: contract is already initialized"
                    );
                    _initialized = 1;
                    if (isTopLevelCall) {
                        _initializing = true;
                    }
                    _;
                    if (isTopLevelCall) {
                        _initializing = false;
                        emit Initialized(1);
                    }
                }
                /**
                 * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
                 * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
                 * used to initialize parent contracts.
                 *
                 * A reinitializer may be used after the original initialization step. This is essential to configure modules that
                 * are added through upgrades and that require initialization.
                 *
                 * When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`
                 * cannot be nested. If one is invoked in the context of another, execution will revert.
                 *
                 * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
                 * a contract, executing them in the right order is up to the developer or operator.
                 *
                 * WARNING: setting the version to 255 will prevent any future reinitialization.
                 *
                 * Emits an {Initialized} event.
                 */
                modifier reinitializer(uint8 version) {
                    require(!_initializing && _initialized < version, "Initializable: contract is already initialized");
                    _initialized = version;
                    _initializing = true;
                    _;
                    _initializing = false;
                    emit Initialized(version);
                }
                /**
                 * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
                 * {initializer} and {reinitializer} modifiers, directly or indirectly.
                 */
                modifier onlyInitializing() {
                    require(_initializing, "Initializable: contract is not initializing");
                    _;
                }
                /**
                 * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
                 * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
                 * to any version. It is recommended to use this to lock implementation contracts that are designed to be called
                 * through proxies.
                 *
                 * Emits an {Initialized} event the first time it is successfully executed.
                 */
                function _disableInitializers() internal virtual {
                    require(!_initializing, "Initializable: contract is initializing");
                    if (_initialized < type(uint8).max) {
                        _initialized = type(uint8).max;
                        emit Initialized(type(uint8).max);
                    }
                }
                /**
                 * @dev Internal function that returns the initialized version. Returns `_initialized`
                 */
                function _getInitializedVersion() internal view returns (uint8) {
                    return _initialized;
                }
                /**
                 * @dev Internal function that returns the initialized version. Returns `_initializing`
                 */
                function _isInitializing() internal view returns (bool) {
                    return _initializing;
                }
            }
            // SPDX-License-Identifier: MIT
            pragma solidity ^0.8.16;
            interface IERC721Metadata {
                function name() external view returns (string memory);
                function symbol() external view returns (string memory);
                function tokenURI(uint256 tokenId) external view returns (string memory);
            }
            // SPDX-License-Identifier: MIT
            pragma solidity ^0.8.16;
            import "@openzeppelin/contracts/utils/introspection/IERC165.sol";
            ///  Note: the ERC-165 identifier for this interface is 0xa511533d.
            interface IERC4671 is IERC165 {
                /// Event emitted when a token `tokenId` is minted for `owner`
                event Minted(address owner, uint256 tokenId);
                /// Event emitted when token `tokenId` of `owner` is revoked
                event Revoked(address owner, uint256 tokenId);
                /// @notice Count all tokens assigned to an owner
                /// @param owner Address for whom to query the balance
                /// @return Number of tokens owned by `owner`
                function balanceOf(address owner) external view returns (uint256);
                /// @notice Get owner of a token
                /// @param tokenId Identifier of the token
                /// @return Address of the owner of `tokenId`
                function ownerOf(uint256 tokenId) external view returns (address);
                /// @notice Check if a token hasn't been revoked
                /// @param tokenId Identifier of the token
                /// @return True if the token is valid, false otherwise
                function isValid(uint256 tokenId) external view returns (bool);
                /// @notice Check if an address owns a valid token in the contract
                /// @param owner Address for whom to check the ownership
                /// @return True if `owner` has a valid token, false otherwise
                function hasValid(address owner) external view returns (bool);
            }
            // SPDX-License-Identifier: CC0-1.0
            pragma solidity ^0.8.16;
            ///  Note: the ERC-165 identifier for this interface is 0xb45a3c0e.
            interface IERC5192 {
                /// @notice Emitted when the locking status is changed to locked.
                /// @dev If a token is minted and the status is locked, this event should be emitted.
                /// @param tokenId The identifier for a token.
                event Locked(uint256 tokenId);
                /// @notice Emitted when the locking status is changed to unlocked.
                /// @dev If a token is minted and the status is unlocked, this event should be emitted.
                /// @param tokenId The identifier for a token.
                event Unlocked(uint256 tokenId);
                /// @notice Returns the locking status of an Soulbound Token
                /// @dev SBTs assigned to zero address are considered invalid, and queries
                /// about them do throw.
                /// @param tokenId The identifier for an SBT.
                function locked(uint256 tokenId) external view returns (bool);
            }
            // SPDX-License-Identifier: CC0-1.0
            pragma solidity ^0.8.16;
            /// @title Account-bound tokens
            /// @dev See https://eips.ethereum.org/EIPS/eip-4973
            /// Note: the ERC-165 identifier for this interface is 0x8d7bac72
            interface IERC4973 {
                /// @dev This emits when ownership of any ABT changes by any mechanism.
                ///  This event emits when ABTs are given or equipped and unequipped
                ///  (`to` == 0).
                event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
                /// @notice Count all ABTs assigned to an owner
                /// @dev ABTs assigned to the zero address are considered invalid, and this
                ///  function throws for queries about the zero address.
                /// @param owner An address for whom to query the balance
                /// @return The number of ABTs owned by `address owner`, possibly zero
                function balanceOf(address owner) external view returns (uint256);
                /// @notice Find the address bound to an ERC4973 account-bound token
                /// @dev ABTs assigned to zero address are considered invalid, and queries
                ///  about them do throw.
                /// @param tokenId The identifier for an ABT.
                /// @return The address of the owner bound to the ABT.
                function ownerOf(uint256 tokenId) external view returns (address);
                /// @notice Removes the `uint256 tokenId` from an account. At any time, an
                ///  ABT receiver must be able to disassociate themselves from an ABT
                ///  publicly through calling this function. After successfully executing this
                ///  function, given the parameters for calling `function give` or
                ///  `function take` a token must be re-equipable.
                /// @dev Must emit a `event Transfer` with the `address to` field pointing to
                ///  the zero address.
                /// @param tokenId The identifier for an ABT.
                function unequip(uint256 tokenId) external;
                /// @notice Creates and transfers the ownership of an ABT from the
                ///  transaction's `msg.sender` to `address to`.
                /// @dev Throws unless `bytes signature` represents an EIP-2098 Compact
                ///  Signature of the EIP-712 structured data hash
                ///  `Agreement(address active,address passive,string tokenURI)` expressing
                ///  `address to`'s explicit agreement to be publicly associated with
                ///  `msg.sender` and `string tokenURI`. A unique `uint256 tokenId` must be
                ///  generated by type-casting the `bytes32` EIP-712 structured data hash to a
                ///  `uint256`. If `bytes signature` is empty or `address to` is a contract,
                ///  an EIP-1271-compatible call to `function isValidSignatureNow(...)` must
                ///  be made to `address to`. A successful execution must result in the
                ///  `event Transfer(msg.sender, to, tokenId)`. Once an ABT exists as an
                ///  `uint256 tokenId` in the contract, `function give(...)` must throw.
                /// @param to The receiver of the ABT.
                /// @param uri A distinct Uniform Resource Identifier (URI) for a given ABT.
                /// @param signature A EIP-2098-compatible Compact Signature of the EIP-712
                ///  structured data hash
                ///  `Agreement(address active,address passive,string tokenURI)` signed by
                ///  `address to`.
                /// @return A unique `uint256 tokenId` generated by type-casting the `bytes32`
                ///  EIP-712 structured data hash to a `uint256`.
                function give(
                    address to,
                    string calldata uri,
                    bytes calldata signature
                ) external returns (uint256);
                /// @notice Creates and transfers the ownership of an ABT from an
                /// `address from` to the transaction's `msg.sender`.
                /// @dev Throws unless `bytes signature` represents an EIP-2098 Compact
                ///  Signature of the EIP-712 structured data hash
                ///  `Agreement(address active,address passive,string tokenURI)` expressing
                ///  `address from`'s explicit agreement to be publicly associated with
                ///  `msg.sender` and `string tokenURI`. A unique `uint256 tokenId` must be
                ///  generated by type-casting the `bytes32` EIP-712 structured data hash to a
                ///  `uint256`. If `bytes signature` is empty or `address from` is a contract,
                ///  an EIP-1271-compatible call to `function isValidSignatureNow(...)` must
                ///  be made to `address from`. A successful execution must result in the
                ///  emission of an `event Transfer(from, msg.sender, tokenId)`. Once an ABT
                ///  exists as an `uint256 tokenId` in the contract, `function take(...)` must
                ///  throw.
                /// @param from The origin of the ABT.
                /// @param uri A distinct Uniform Resource Identifier (URI) for a given ABT.
                /// @param signature A EIP-2098-compatible Compact Signature of the EIP-712
                ///  structured data hash
                ///  `Agreement(address active,address passive,string tokenURI)` signed by
                ///  `address from`.
                /// @return A unique `uint256 tokenId` generated by type-casting the `bytes32`
                ///  EIP-712 structured data hash to a `uint256`.
                function take(
                    address from,
                    string calldata uri,
                    bytes calldata signature
                ) external returns (uint256);
            }
            // SPDX-License-Identifier: MIT
            pragma solidity ^0.8.0;
            /**
             * @title CAN Soulbound token inteface
             */
            interface ISBT {
                /// @dev emit when the contract uri is changed.
                event ContractURISet(string contractURI);
                /// @dev emit when the base URI is changed.
                event BaseURISet(string baseURI);
                /// @dev emit when the token revoked with reason
                event RevokedByReason(address owner, uint256 tokenId, string reason);
                /// @dev emit when the token is set expiration
                event ExpirationSet(uint256 tokenId, uint256 expiration);
                /// @dev emit when the contract is set claimable
                event ClaimableSet(bool claimable);
                /// @dev emit when the token is burned
                event Burned(uint256 tokenId);
                /// @dev mark the token as revoked by contract owner only
                function revoke(uint256 tokenId, string calldata reason) external;
                /// @dev claim a SBT
                function claim() external;
                /// @dev mint to receivers
                function mint(address[] calldata receivers, uint256 expiration) external;
                /// @dev burn a SBT
                function burn(uint256 tokenId) external;
                /// @dev set a minter list
                function setMinters(address[] calldata minters) external;
                /// @dev enable/disable claiming SBT
                function setClaimable(bool claimable) external;
                /// @dev set SBT expiration
                function setExpiration(uint256 tokenId, uint256 expiration) external;
                /// @dev set the base token URI
                function setBaseURI(string calldata baseURI) external;
                /// @dev set the contract URI
                function setContractURI(string calldata contractURI_) external;
                /// @dev get the contract URI
                function contractURI() external view returns (string memory);
            }
            // SPDX-License-Identifier: MIT
            // Inspired by OpenZeppelin Contracts (last updated v4.6.0) (token/ERC721/IERC721Receiver.sol)
            pragma solidity ^0.8.0;
            /**
             * @title Soulbound token receiver interface
             */
            interface ISBTReceiver {
                /**
                 *
                 * It must return its Solidity selector to confirm the token transfer.
                 * If any other value is returned or the interface is not implemented by the recipient, the transfer will be reverted.
                 *
                 * The selector can be obtained in Solidity with `ISBTReceiver.onSBTReceived.selector`.
                 */
                function onSBTReceived(
                    address operator,
                    address from,
                    uint256 tokenId,
                    bytes calldata data
                ) external returns (bytes4);
            }
            // SPDX-License-Identifier: MIT
            // OpenZeppelin Contracts (last updated v4.8.0) (utils/Address.sol)
            pragma solidity ^0.8.1;
            /**
             * @dev Collection of functions related to the address type
             */
            library AddressUpgradeable {
                /**
                 * @dev Returns true if `account` is a contract.
                 *
                 * [IMPORTANT]
                 * ====
                 * It is unsafe to assume that an address for which this function returns
                 * false is an externally-owned account (EOA) and not a contract.
                 *
                 * Among others, `isContract` will return false for the following
                 * types of addresses:
                 *
                 *  - an externally-owned account
                 *  - a contract in construction
                 *  - an address where a contract will be created
                 *  - an address where a contract lived, but was destroyed
                 * ====
                 *
                 * [IMPORTANT]
                 * ====
                 * You shouldn't rely on `isContract` to protect against flash loan attacks!
                 *
                 * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
                 * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
                 * constructor.
                 * ====
                 */
                function isContract(address account) internal view returns (bool) {
                    // This method relies on extcodesize/address.code.length, which returns 0
                    // for contracts in construction, since the code is only stored at the end
                    // of the constructor execution.
                    return account.code.length > 0;
                }
                /**
                 * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
                 * `recipient`, forwarding all available gas and reverting on errors.
                 *
                 * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
                 * of certain opcodes, possibly making contracts go over the 2300 gas limit
                 * imposed by `transfer`, making them unable to receive funds via
                 * `transfer`. {sendValue} removes this limitation.
                 *
                 * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
                 *
                 * IMPORTANT: because control is transferred to `recipient`, care must be
                 * taken to not create reentrancy vulnerabilities. Consider using
                 * {ReentrancyGuard} or the
                 * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
                 */
                function sendValue(address payable recipient, uint256 amount) internal {
                    require(address(this).balance >= amount, "Address: insufficient balance");
                    (bool success, ) = recipient.call{value: amount}("");
                    require(success, "Address: unable to send value, recipient may have reverted");
                }
                /**
                 * @dev Performs a Solidity function call using a low level `call`. A
                 * plain `call` is an unsafe replacement for a function call: use this
                 * function instead.
                 *
                 * If `target` reverts with a revert reason, it is bubbled up by this
                 * function (like regular Solidity function calls).
                 *
                 * Returns the raw returned data. To convert to the expected return value,
                 * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
                 *
                 * Requirements:
                 *
                 * - `target` must be a contract.
                 * - calling `target` with `data` must not revert.
                 *
                 * _Available since v3.1._
                 */
                function functionCall(address target, bytes memory data) internal returns (bytes memory) {
                    return functionCallWithValue(target, data, 0, "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");
                    (bool success, bytes memory returndata) = target.call{value: value}(data);
                    return verifyCallResultFromTarget(target, success, returndata, errorMessage);
                }
                /**
                 * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                 * but performing a static call.
                 *
                 * _Available since v3.3._
                 */
                function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
                    return functionStaticCall(target, data, "Address: low-level static call failed");
                }
                /**
                 * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
                 * but performing a static call.
                 *
                 * _Available since v3.3._
                 */
                function functionStaticCall(
                    address target,
                    bytes memory data,
                    string memory errorMessage
                ) internal view returns (bytes memory) {
                    (bool success, bytes memory returndata) = target.staticcall(data);
                    return verifyCallResultFromTarget(target, success, returndata, errorMessage);
                }
                /**
                 * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
                 * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
                 *
                 * _Available since v4.8._
                 */
                function verifyCallResultFromTarget(
                    address target,
                    bool success,
                    bytes memory returndata,
                    string memory errorMessage
                ) internal view returns (bytes memory) {
                    if (success) {
                        if (returndata.length == 0) {
                            // only check isContract if the call was successful and the return data is empty
                            // otherwise we already know that it was a contract
                            require(isContract(target), "Address: call to non-contract");
                        }
                        return returndata;
                    } else {
                        _revert(returndata, errorMessage);
                    }
                }
                /**
                 * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
                 * revert reason or using the provided one.
                 *
                 * _Available since v4.3._
                 */
                function verifyCallResult(
                    bool success,
                    bytes memory returndata,
                    string memory errorMessage
                ) internal pure returns (bytes memory) {
                    if (success) {
                        return returndata;
                    } else {
                        _revert(returndata, errorMessage);
                    }
                }
                function _revert(bytes memory returndata, string memory errorMessage) private pure {
                    // Look for revert reason and bubble it up if present
                    if (returndata.length > 0) {
                        // The easiest way to bubble the revert reason is using memory via assembly
                        /// @solidity memory-safe-assembly
                        assembly {
                            let returndata_size := mload(returndata)
                            revert(add(32, returndata), returndata_size)
                        }
                    } else {
                        revert(errorMessage);
                    }
                }
            }
            // SPDX-License-Identifier: MIT
            // OpenZeppelin Contracts (last updated v4.8.0) (utils/Strings.sol)
            pragma solidity ^0.8.0;
            import "./math/MathUpgradeable.sol";
            /**
             * @dev String operations.
             */
            library StringsUpgradeable {
                bytes16 private constant _SYMBOLS = "0123456789abcdef";
                uint8 private constant _ADDRESS_LENGTH = 20;
                /**
                 * @dev Converts a `uint256` to its ASCII `string` decimal representation.
                 */
                function toString(uint256 value) internal pure returns (string memory) {
                    unchecked {
                        uint256 length = MathUpgradeable.log10(value) + 1;
                        string memory buffer = new string(length);
                        uint256 ptr;
                        /// @solidity memory-safe-assembly
                        assembly {
                            ptr := add(buffer, add(32, length))
                        }
                        while (true) {
                            ptr--;
                            /// @solidity memory-safe-assembly
                            assembly {
                                mstore8(ptr, byte(mod(value, 10), _SYMBOLS))
                            }
                            value /= 10;
                            if (value == 0) break;
                        }
                        return buffer;
                    }
                }
                /**
                 * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
                 */
                function toHexString(uint256 value) internal pure returns (string memory) {
                    unchecked {
                        return toHexString(value, MathUpgradeable.log256(value) + 1);
                    }
                }
                /**
                 * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
                 */
                function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
                    bytes memory buffer = new bytes(2 * length + 2);
                    buffer[0] = "0";
                    buffer[1] = "x";
                    for (uint256 i = 2 * length + 1; i > 1; --i) {
                        buffer[i] = _SYMBOLS[value & 0xf];
                        value >>= 4;
                    }
                    require(value == 0, "Strings: hex length insufficient");
                    return string(buffer);
                }
                /**
                 * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation.
                 */
                function toHexString(address addr) internal pure returns (string memory) {
                    return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);
                }
            }
            // SPDX-License-Identifier: MIT
            // OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)
            pragma solidity ^0.8.0;
            import "./IERC165Upgradeable.sol";
            import "../../proxy/utils/Initializable.sol";
            /**
             * @dev Implementation of the {IERC165} interface.
             *
             * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
             * for the additional interface id that will be supported. For example:
             *
             * ```solidity
             * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
             *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
             * }
             * ```
             *
             * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
             */
            abstract contract ERC165Upgradeable is Initializable, IERC165Upgradeable {
                function __ERC165_init() internal onlyInitializing {
                }
                function __ERC165_init_unchained() internal onlyInitializing {
                }
                /**
                 * @dev See {IERC165-supportsInterface}.
                 */
                function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
                    return interfaceId == type(IERC165Upgradeable).interfaceId;
                }
                /**
                 * @dev This empty reserved space is put in place to allow future versions to add new
                 * variables without shifting down storage in the inheritance chain.
                 * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
                 */
                uint256[50] private __gap;
            }
            // SPDX-License-Identifier: MIT
            // OpenZeppelin Contracts (last updated v4.8.0) (utils/cryptography/SignatureChecker.sol)
            pragma solidity ^0.8.0;
            import "./ECDSAUpgradeable.sol";
            import "../AddressUpgradeable.sol";
            import "../../interfaces/IERC1271Upgradeable.sol";
            /**
             * @dev Signature verification helper that can be used instead of `ECDSA.recover` to seamlessly support both ECDSA
             * signatures from externally owned accounts (EOAs) as well as ERC1271 signatures from smart contract wallets like
             * Argent and Gnosis Safe.
             *
             * _Available since v4.1._
             */
            library SignatureCheckerUpgradeable {
                /**
                 * @dev Checks if a signature is valid for a given signer and data hash. If the signer is a smart contract, the
                 * signature is validated against that smart contract using ERC1271, otherwise it's validated using `ECDSA.recover`.
                 *
                 * NOTE: Unlike ECDSA signatures, contract signatures are revocable, and the outcome of this function can thus
                 * change through time. It could return true at block N and false at block N+1 (or the opposite).
                 */
                function isValidSignatureNow(
                    address signer,
                    bytes32 hash,
                    bytes memory signature
                ) internal view returns (bool) {
                    (address recovered, ECDSAUpgradeable.RecoverError error) = ECDSAUpgradeable.tryRecover(hash, signature);
                    if (error == ECDSAUpgradeable.RecoverError.NoError && recovered == signer) {
                        return true;
                    }
                    (bool success, bytes memory result) = signer.staticcall(
                        abi.encodeWithSelector(IERC1271Upgradeable.isValidSignature.selector, hash, signature)
                    );
                    return (success &&
                        result.length == 32 &&
                        abi.decode(result, (bytes32)) == bytes32(IERC1271Upgradeable.isValidSignature.selector));
                }
            }
            // SPDX-License-Identifier: MIT
            // OpenZeppelin Contracts (last updated v4.8.0) (utils/structs/EnumerableSet.sol)
            // This file was procedurally generated from scripts/generate/templates/EnumerableSet.js.
            pragma solidity ^0.8.0;
            /**
             * @dev Library for managing
             * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
             * types.
             *
             * Sets have the following properties:
             *
             * - Elements are added, removed, and checked for existence in constant time
             * (O(1)).
             * - Elements are enumerated in O(n). No guarantees are made on the ordering.
             *
             * ```
             * contract Example {
             *     // Add the library methods
             *     using EnumerableSet for EnumerableSet.AddressSet;
             *
             *     // Declare a set state variable
             *     EnumerableSet.AddressSet private mySet;
             * }
             * ```
             *
             * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)
             * and `uint256` (`UintSet`) are supported.
             *
             * [WARNING]
             * ====
             * Trying to delete such a structure from storage will likely result in data corruption, rendering the structure
             * unusable.
             * See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info.
             *
             * In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an
             * array of EnumerableSet.
             * ====
             */
            library EnumerableSetUpgradeable {
                // To implement this library for multiple types with as little code
                // repetition as possible, we write it in terms of a generic Set type with
                // bytes32 values.
                // The Set implementation uses private functions, and user-facing
                // implementations (such as AddressSet) are just wrappers around the
                // underlying Set.
                // This means that we can only create new EnumerableSets for types that fit
                // in bytes32.
                struct Set {
                    // Storage of set values
                    bytes32[] _values;
                    // Position of the value in the `values` array, plus 1 because index 0
                    // means a value is not in the set.
                    mapping(bytes32 => uint256) _indexes;
                }
                /**
                 * @dev Add a value to a set. O(1).
                 *
                 * Returns true if the value was added to the set, that is if it was not
                 * already present.
                 */
                function _add(Set storage set, bytes32 value) private returns (bool) {
                    if (!_contains(set, value)) {
                        set._values.push(value);
                        // The value is stored at length-1, but we add 1 to all indexes
                        // and use 0 as a sentinel value
                        set._indexes[value] = set._values.length;
                        return true;
                    } else {
                        return false;
                    }
                }
                /**
                 * @dev Removes a value from a set. O(1).
                 *
                 * Returns true if the value was removed from the set, that is if it was
                 * present.
                 */
                function _remove(Set storage set, bytes32 value) private returns (bool) {
                    // We read and store the value's index to prevent multiple reads from the same storage slot
                    uint256 valueIndex = set._indexes[value];
                    if (valueIndex != 0) {
                        // Equivalent to contains(set, value)
                        // To delete an element from the _values array in O(1), we swap the element to delete with the last one in
                        // the array, and then remove the last element (sometimes called as 'swap and pop').
                        // This modifies the order of the array, as noted in {at}.
                        uint256 toDeleteIndex = valueIndex - 1;
                        uint256 lastIndex = set._values.length - 1;
                        if (lastIndex != toDeleteIndex) {
                            bytes32 lastValue = set._values[lastIndex];
                            // Move the last value to the index where the value to delete is
                            set._values[toDeleteIndex] = lastValue;
                            // Update the index for the moved value
                            set._indexes[lastValue] = valueIndex; // Replace lastValue's index to valueIndex
                        }
                        // Delete the slot where the moved value was stored
                        set._values.pop();
                        // Delete the index for the deleted slot
                        delete set._indexes[value];
                        return true;
                    } else {
                        return false;
                    }
                }
                /**
                 * @dev Returns true if the value is in the set. O(1).
                 */
                function _contains(Set storage set, bytes32 value) private view returns (bool) {
                    return set._indexes[value] != 0;
                }
                /**
                 * @dev Returns the number of values on the set. O(1).
                 */
                function _length(Set storage set) private view returns (uint256) {
                    return set._values.length;
                }
                /**
                 * @dev Returns the value stored at position `index` in the set. O(1).
                 *
                 * Note that there are no guarantees on the ordering of values inside the
                 * array, and it may change when more values are added or removed.
                 *
                 * Requirements:
                 *
                 * - `index` must be strictly less than {length}.
                 */
                function _at(Set storage set, uint256 index) private view returns (bytes32) {
                    return set._values[index];
                }
                /**
                 * @dev Return the entire set in an array
                 *
                 * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
                 * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
                 * this function has an unbounded cost, and using it as part of a state-changing function may render the function
                 * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
                 */
                function _values(Set storage set) private view returns (bytes32[] memory) {
                    return set._values;
                }
                // Bytes32Set
                struct Bytes32Set {
                    Set _inner;
                }
                /**
                 * @dev Add a value to a set. O(1).
                 *
                 * Returns true if the value was added to the set, that is if it was not
                 * already present.
                 */
                function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
                    return _add(set._inner, value);
                }
                /**
                 * @dev Removes a value from a set. O(1).
                 *
                 * Returns true if the value was removed from the set, that is if it was
                 * present.
                 */
                function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {
                    return _remove(set._inner, value);
                }
                /**
                 * @dev Returns true if the value is in the set. O(1).
                 */
                function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
                    return _contains(set._inner, value);
                }
                /**
                 * @dev Returns the number of values in the set. O(1).
                 */
                function length(Bytes32Set storage set) internal view returns (uint256) {
                    return _length(set._inner);
                }
                /**
                 * @dev Returns the value stored at position `index` in the set. O(1).
                 *
                 * Note that there are no guarantees on the ordering of values inside the
                 * array, and it may change when more values are added or removed.
                 *
                 * Requirements:
                 *
                 * - `index` must be strictly less than {length}.
                 */
                function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
                    return _at(set._inner, index);
                }
                /**
                 * @dev Return the entire set in an array
                 *
                 * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
                 * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
                 * this function has an unbounded cost, and using it as part of a state-changing function may render the function
                 * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
                 */
                function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {
                    bytes32[] memory store = _values(set._inner);
                    bytes32[] memory result;
                    /// @solidity memory-safe-assembly
                    assembly {
                        result := store
                    }
                    return result;
                }
                // AddressSet
                struct AddressSet {
                    Set _inner;
                }
                /**
                 * @dev Add a value to a set. O(1).
                 *
                 * Returns true if the value was added to the set, that is if it was not
                 * already present.
                 */
                function add(AddressSet storage set, address value) internal returns (bool) {
                    return _add(set._inner, bytes32(uint256(uint160(value))));
                }
                /**
                 * @dev Removes a value from a set. O(1).
                 *
                 * Returns true if the value was removed from the set, that is if it was
                 * present.
                 */
                function remove(AddressSet storage set, address value) internal returns (bool) {
                    return _remove(set._inner, bytes32(uint256(uint160(value))));
                }
                /**
                 * @dev Returns true if the value is in the set. O(1).
                 */
                function contains(AddressSet storage set, address value) internal view returns (bool) {
                    return _contains(set._inner, bytes32(uint256(uint160(value))));
                }
                /**
                 * @dev Returns the number of values in the set. O(1).
                 */
                function length(AddressSet storage set) internal view returns (uint256) {
                    return _length(set._inner);
                }
                /**
                 * @dev Returns the value stored at position `index` in the set. O(1).
                 *
                 * Note that there are no guarantees on the ordering of values inside the
                 * array, and it may change when more values are added or removed.
                 *
                 * Requirements:
                 *
                 * - `index` must be strictly less than {length}.
                 */
                function at(AddressSet storage set, uint256 index) internal view returns (address) {
                    return address(uint160(uint256(_at(set._inner, index))));
                }
                /**
                 * @dev Return the entire set in an array
                 *
                 * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
                 * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
                 * this function has an unbounded cost, and using it as part of a state-changing function may render the function
                 * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
                 */
                function values(AddressSet storage set) internal view returns (address[] memory) {
                    bytes32[] memory store = _values(set._inner);
                    address[] memory result;
                    /// @solidity memory-safe-assembly
                    assembly {
                        result := store
                    }
                    return result;
                }
                // UintSet
                struct UintSet {
                    Set _inner;
                }
                /**
                 * @dev Add a value to a set. O(1).
                 *
                 * Returns true if the value was added to the set, that is if it was not
                 * already present.
                 */
                function add(UintSet storage set, uint256 value) internal returns (bool) {
                    return _add(set._inner, bytes32(value));
                }
                /**
                 * @dev Removes a value from a set. O(1).
                 *
                 * Returns true if the value was removed from the set, that is if it was
                 * present.
                 */
                function remove(UintSet storage set, uint256 value) internal returns (bool) {
                    return _remove(set._inner, bytes32(value));
                }
                /**
                 * @dev Returns true if the value is in the set. O(1).
                 */
                function contains(UintSet storage set, uint256 value) internal view returns (bool) {
                    return _contains(set._inner, bytes32(value));
                }
                /**
                 * @dev Returns the number of values in the set. O(1).
                 */
                function length(UintSet storage set) internal view returns (uint256) {
                    return _length(set._inner);
                }
                /**
                 * @dev Returns the value stored at position `index` in the set. O(1).
                 *
                 * Note that there are no guarantees on the ordering of values inside the
                 * array, and it may change when more values are added or removed.
                 *
                 * Requirements:
                 *
                 * - `index` must be strictly less than {length}.
                 */
                function at(UintSet storage set, uint256 index) internal view returns (uint256) {
                    return uint256(_at(set._inner, index));
                }
                /**
                 * @dev Return the entire set in an array
                 *
                 * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
                 * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
                 * this function has an unbounded cost, and using it as part of a state-changing function may render the function
                 * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
                 */
                function values(UintSet storage set) internal view returns (uint256[] memory) {
                    bytes32[] memory store = _values(set._inner);
                    uint256[] memory result;
                    /// @solidity memory-safe-assembly
                    assembly {
                        result := store
                    }
                    return result;
                }
            }
            // SPDX-License-Identifier: MIT
            // OpenZeppelin Contracts (last updated v4.8.0) (utils/cryptography/draft-EIP712.sol)
            pragma solidity ^0.8.0;
            // EIP-712 is Final as of 2022-08-11. This file is deprecated.
            import "./EIP712Upgradeable.sol";
            // SPDX-License-Identifier: MIT
            // OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)
            pragma solidity ^0.8.0;
            /**
             * @dev Interface of the ERC165 standard, as defined in the
             * https://eips.ethereum.org/EIPS/eip-165[EIP].
             *
             * Implementers can declare support of contract interfaces, which can then be
             * queried by others ({ERC165Checker}).
             *
             * For an implementation, see {ERC165}.
             */
            interface 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
            // OpenZeppelin Contracts v4.4.1 (utils/Context.sol)
            pragma solidity ^0.8.0;
            import "../proxy/utils/Initializable.sol";
            /**
             * @dev Provides information about the current execution context, including the
             * sender of the transaction and its data. While these are generally available
             * via msg.sender and msg.data, they should not be accessed in such a direct
             * manner, since when dealing with meta-transactions the account sending and
             * paying for execution may not be the actual sender (as far as an application
             * is concerned).
             *
             * This contract is only required for intermediate, library-like contracts.
             */
            abstract contract ContextUpgradeable is Initializable {
                function __Context_init() internal onlyInitializing {
                }
                function __Context_init_unchained() internal onlyInitializing {
                }
                function _msgSender() internal view virtual returns (address) {
                    return msg.sender;
                }
                function _msgData() internal view virtual returns (bytes calldata) {
                    return msg.data;
                }
                /**
                 * @dev This empty reserved space is put in place to allow future versions to add new
                 * variables without shifting down storage in the inheritance chain.
                 * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
                 */
                uint256[50] private __gap;
            }
            // SPDX-License-Identifier: MIT
            // OpenZeppelin Contracts (last updated v4.8.0) (utils/math/Math.sol)
            pragma solidity ^0.8.0;
            /**
             * @dev Standard math utilities missing in the Solidity language.
             */
            library MathUpgradeable {
                enum Rounding {
                    Down, // Toward negative infinity
                    Up, // Toward infinity
                    Zero // Toward zero
                }
                /**
                 * @dev Returns the largest of two numbers.
                 */
                function max(uint256 a, uint256 b) internal pure returns (uint256) {
                    return a > b ? a : b;
                }
                /**
                 * @dev Returns the smallest of two numbers.
                 */
                function min(uint256 a, uint256 b) internal pure returns (uint256) {
                    return a < b ? a : b;
                }
                /**
                 * @dev Returns the average of two numbers. The result is rounded towards
                 * zero.
                 */
                function average(uint256 a, uint256 b) internal pure returns (uint256) {
                    // (a + b) / 2 can overflow.
                    return (a & b) + (a ^ b) / 2;
                }
                /**
                 * @dev Returns the ceiling of the division of two numbers.
                 *
                 * This differs from standard division with `/` in that it rounds up instead
                 * of rounding down.
                 */
                function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
                    // (a + b - 1) / b can overflow on addition, so we distribute.
                    return a == 0 ? 0 : (a - 1) / b + 1;
                }
                /**
                 * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
                 * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv)
                 * with further edits by Uniswap Labs also under MIT license.
                 */
                function mulDiv(
                    uint256 x,
                    uint256 y,
                    uint256 denominator
                ) internal pure returns (uint256 result) {
                    unchecked {
                        // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
                        // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
                        // variables such that product = prod1 * 2^256 + prod0.
                        uint256 prod0; // Least significant 256 bits of the product
                        uint256 prod1; // Most significant 256 bits of the product
                        assembly {
                            let mm := mulmod(x, y, not(0))
                            prod0 := mul(x, y)
                            prod1 := sub(sub(mm, prod0), lt(mm, prod0))
                        }
                        // Handle non-overflow cases, 256 by 256 division.
                        if (prod1 == 0) {
                            return prod0 / denominator;
                        }
                        // Make sure the result is less than 2^256. Also prevents denominator == 0.
                        require(denominator > prod1);
                        ///////////////////////////////////////////////
                        // 512 by 256 division.
                        ///////////////////////////////////////////////
                        // Make division exact by subtracting the remainder from [prod1 prod0].
                        uint256 remainder;
                        assembly {
                            // Compute remainder using mulmod.
                            remainder := mulmod(x, y, denominator)
                            // Subtract 256 bit number from 512 bit number.
                            prod1 := sub(prod1, gt(remainder, prod0))
                            prod0 := sub(prod0, remainder)
                        }
                        // Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1.
                        // See https://cs.stackexchange.com/q/138556/92363.
                        // Does not overflow because the denominator cannot be zero at this stage in the function.
                        uint256 twos = denominator & (~denominator + 1);
                        assembly {
                            // Divide denominator by twos.
                            denominator := div(denominator, twos)
                            // Divide [prod1 prod0] by twos.
                            prod0 := div(prod0, twos)
                            // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
                            twos := add(div(sub(0, twos), twos), 1)
                        }
                        // Shift in bits from prod1 into prod0.
                        prod0 |= prod1 * twos;
                        // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
                        // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
                        // four bits. That is, denominator * inv = 1 mod 2^4.
                        uint256 inverse = (3 * denominator) ^ 2;
                        // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works
                        // in modular arithmetic, doubling the correct bits in each step.
                        inverse *= 2 - denominator * inverse; // inverse mod 2^8
                        inverse *= 2 - denominator * inverse; // inverse mod 2^16
                        inverse *= 2 - denominator * inverse; // inverse mod 2^32
                        inverse *= 2 - denominator * inverse; // inverse mod 2^64
                        inverse *= 2 - denominator * inverse; // inverse mod 2^128
                        inverse *= 2 - denominator * inverse; // inverse mod 2^256
                        // Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
                        // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
                        // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
                        // is no longer required.
                        result = prod0 * inverse;
                        return result;
                    }
                }
                /**
                 * @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
                 */
                function mulDiv(
                    uint256 x,
                    uint256 y,
                    uint256 denominator,
                    Rounding rounding
                ) internal pure returns (uint256) {
                    uint256 result = mulDiv(x, y, denominator);
                    if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {
                        result += 1;
                    }
                    return result;
                }
                /**
                 * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down.
                 *
                 * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
                 */
                function sqrt(uint256 a) internal pure returns (uint256) {
                    if (a == 0) {
                        return 0;
                    }
                    // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
                    //
                    // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
                    // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
                    //
                    // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
                    // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
                    // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
                    //
                    // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
                    uint256 result = 1 << (log2(a) >> 1);
                    // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
                    // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
                    // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
                    // into the expected uint128 result.
                    unchecked {
                        result = (result + a / result) >> 1;
                        result = (result + a / result) >> 1;
                        result = (result + a / result) >> 1;
                        result = (result + a / result) >> 1;
                        result = (result + a / result) >> 1;
                        result = (result + a / result) >> 1;
                        result = (result + a / result) >> 1;
                        return min(result, a / result);
                    }
                }
                /**
                 * @notice Calculates sqrt(a), following the selected rounding direction.
                 */
                function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
                    unchecked {
                        uint256 result = sqrt(a);
                        return result + (rounding == Rounding.Up && result * result < a ? 1 : 0);
                    }
                }
                /**
                 * @dev Return the log in base 2, rounded down, of a positive value.
                 * Returns 0 if given 0.
                 */
                function log2(uint256 value) internal pure returns (uint256) {
                    uint256 result = 0;
                    unchecked {
                        if (value >> 128 > 0) {
                            value >>= 128;
                            result += 128;
                        }
                        if (value >> 64 > 0) {
                            value >>= 64;
                            result += 64;
                        }
                        if (value >> 32 > 0) {
                            value >>= 32;
                            result += 32;
                        }
                        if (value >> 16 > 0) {
                            value >>= 16;
                            result += 16;
                        }
                        if (value >> 8 > 0) {
                            value >>= 8;
                            result += 8;
                        }
                        if (value >> 4 > 0) {
                            value >>= 4;
                            result += 4;
                        }
                        if (value >> 2 > 0) {
                            value >>= 2;
                            result += 2;
                        }
                        if (value >> 1 > 0) {
                            result += 1;
                        }
                    }
                    return result;
                }
                /**
                 * @dev Return the log in base 2, following the selected rounding direction, of a positive value.
                 * Returns 0 if given 0.
                 */
                function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
                    unchecked {
                        uint256 result = log2(value);
                        return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0);
                    }
                }
                /**
                 * @dev Return the log in base 10, rounded down, of a positive value.
                 * Returns 0 if given 0.
                 */
                function log10(uint256 value) internal pure returns (uint256) {
                    uint256 result = 0;
                    unchecked {
                        if (value >= 10**64) {
                            value /= 10**64;
                            result += 64;
                        }
                        if (value >= 10**32) {
                            value /= 10**32;
                            result += 32;
                        }
                        if (value >= 10**16) {
                            value /= 10**16;
                            result += 16;
                        }
                        if (value >= 10**8) {
                            value /= 10**8;
                            result += 8;
                        }
                        if (value >= 10**4) {
                            value /= 10**4;
                            result += 4;
                        }
                        if (value >= 10**2) {
                            value /= 10**2;
                            result += 2;
                        }
                        if (value >= 10**1) {
                            result += 1;
                        }
                    }
                    return result;
                }
                /**
                 * @dev Return the log in base 10, following the selected rounding direction, of a positive value.
                 * Returns 0 if given 0.
                 */
                function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
                    unchecked {
                        uint256 result = log10(value);
                        return result + (rounding == Rounding.Up && 10**result < value ? 1 : 0);
                    }
                }
                /**
                 * @dev Return the log in base 256, rounded down, of a positive value.
                 * Returns 0 if given 0.
                 *
                 * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
                 */
                function log256(uint256 value) internal pure returns (uint256) {
                    uint256 result = 0;
                    unchecked {
                        if (value >> 128 > 0) {
                            value >>= 128;
                            result += 16;
                        }
                        if (value >> 64 > 0) {
                            value >>= 64;
                            result += 8;
                        }
                        if (value >> 32 > 0) {
                            value >>= 32;
                            result += 4;
                        }
                        if (value >> 16 > 0) {
                            value >>= 16;
                            result += 2;
                        }
                        if (value >> 8 > 0) {
                            result += 1;
                        }
                    }
                    return result;
                }
                /**
                 * @dev Return the log in base 10, following the selected rounding direction, of a positive value.
                 * Returns 0 if given 0.
                 */
                function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
                    unchecked {
                        uint256 result = log256(value);
                        return result + (rounding == Rounding.Up && 1 << (result * 8) < value ? 1 : 0);
                    }
                }
            }
            // SPDX-License-Identifier: MIT
            // OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)
            pragma solidity ^0.8.0;
            /**
             * @dev Interface of the ERC165 standard, as defined in the
             * https://eips.ethereum.org/EIPS/eip-165[EIP].
             *
             * Implementers can declare support of contract interfaces, which can then be
             * queried by others ({ERC165Checker}).
             *
             * For an implementation, see {ERC165}.
             */
            interface IERC165Upgradeable {
                /**
                 * @dev Returns true if this contract implements the interface defined by
                 * `interfaceId`. See the corresponding
                 * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
                 * to learn more about how these ids are created.
                 *
                 * This function call must use less than 30 000 gas.
                 */
                function supportsInterface(bytes4 interfaceId) external view returns (bool);
            }
            // SPDX-License-Identifier: MIT
            // OpenZeppelin Contracts v4.4.1 (interfaces/IERC1271.sol)
            pragma solidity ^0.8.0;
            /**
             * @dev Interface of the ERC1271 standard signature validation method for
             * contracts as defined in https://eips.ethereum.org/EIPS/eip-1271[ERC-1271].
             *
             * _Available since v4.1._
             */
            interface IERC1271Upgradeable {
                /**
                 * @dev Should return whether the signature provided is valid for the provided data
                 * @param hash      Hash of the data to be signed
                 * @param signature Signature byte array associated with _data
                 */
                function isValidSignature(bytes32 hash, bytes memory signature) external view returns (bytes4 magicValue);
            }
            // SPDX-License-Identifier: MIT
            // OpenZeppelin Contracts (last updated v4.8.0) (utils/cryptography/ECDSA.sol)
            pragma solidity ^0.8.0;
            import "../StringsUpgradeable.sol";
            /**
             * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
             *
             * These functions can be used to verify that a message was signed by the holder
             * of the private keys of a given address.
             */
            library ECDSAUpgradeable {
                enum RecoverError {
                    NoError,
                    InvalidSignature,
                    InvalidSignatureLength,
                    InvalidSignatureS,
                    InvalidSignatureV // Deprecated in v4.8
                }
                function _throwError(RecoverError error) private pure {
                    if (error == RecoverError.NoError) {
                        return; // no error: do nothing
                    } else if (error == RecoverError.InvalidSignature) {
                        revert("ECDSA: invalid signature");
                    } else if (error == RecoverError.InvalidSignatureLength) {
                        revert("ECDSA: invalid signature length");
                    } else if (error == RecoverError.InvalidSignatureS) {
                        revert("ECDSA: invalid signature 's' value");
                    }
                }
                /**
                 * @dev Returns the address that signed a hashed message (`hash`) with
                 * `signature` or error string. This address can then be used for verification purposes.
                 *
                 * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
                 * this function rejects them by requiring the `s` value to be in the lower
                 * half order, and the `v` value to be either 27 or 28.
                 *
                 * IMPORTANT: `hash` _must_ be the result of a hash operation for the
                 * verification to be secure: it is possible to craft signatures that
                 * recover to arbitrary addresses for non-hashed data. A safe way to ensure
                 * this is by receiving a hash of the original message (which may otherwise
                 * be too long), and then calling {toEthSignedMessageHash} on it.
                 *
                 * Documentation for signature generation:
                 * - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js]
                 * - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers]
                 *
                 * _Available since v4.3._
                 */
                function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError) {
                    if (signature.length == 65) {
                        bytes32 r;
                        bytes32 s;
                        uint8 v;
                        // ecrecover takes the signature parameters, and the only way to get them
                        // currently is to use assembly.
                        /// @solidity memory-safe-assembly
                        assembly {
                            r := mload(add(signature, 0x20))
                            s := mload(add(signature, 0x40))
                            v := byte(0, mload(add(signature, 0x60)))
                        }
                        return tryRecover(hash, v, r, s);
                    } else {
                        return (address(0), RecoverError.InvalidSignatureLength);
                    }
                }
                /**
                 * @dev Returns the address that signed a hashed message (`hash`) with
                 * `signature`. This address can then be used for verification purposes.
                 *
                 * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
                 * this function rejects them by requiring the `s` value to be in the lower
                 * half order, and the `v` value to be either 27 or 28.
                 *
                 * IMPORTANT: `hash` _must_ be the result of a hash operation for the
                 * verification to be secure: it is possible to craft signatures that
                 * recover to arbitrary addresses for non-hashed data. A safe way to ensure
                 * this is by receiving a hash of the original message (which may otherwise
                 * be too long), and then calling {toEthSignedMessageHash} on it.
                 */
                function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
                    (address recovered, RecoverError error) = tryRecover(hash, signature);
                    _throwError(error);
                    return recovered;
                }
                /**
                 * @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately.
                 *
                 * See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures]
                 *
                 * _Available since v4.3._
                 */
                function tryRecover(
                    bytes32 hash,
                    bytes32 r,
                    bytes32 vs
                ) internal pure returns (address, RecoverError) {
                    bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);
                    uint8 v = uint8((uint256(vs) >> 255) + 27);
                    return tryRecover(hash, v, r, s);
                }
                /**
                 * @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately.
                 *
                 * _Available since v4.2._
                 */
                function recover(
                    bytes32 hash,
                    bytes32 r,
                    bytes32 vs
                ) internal pure returns (address) {
                    (address recovered, RecoverError error) = tryRecover(hash, r, vs);
                    _throwError(error);
                    return recovered;
                }
                /**
                 * @dev Overload of {ECDSA-tryRecover} that receives the `v`,
                 * `r` and `s` signature fields separately.
                 *
                 * _Available since v4.3._
                 */
                function tryRecover(
                    bytes32 hash,
                    uint8 v,
                    bytes32 r,
                    bytes32 s
                ) internal pure returns (address, RecoverError) {
                    // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
                    // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
                    // the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most
                    // signatures from current libraries generate a unique signature with an s-value in the lower half order.
                    //
                    // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
                    // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
                    // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
                    // these malleable signatures as well.
                    if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
                        return (address(0), RecoverError.InvalidSignatureS);
                    }
                    // If the signature is valid (and not malleable), return the signer address
                    address signer = ecrecover(hash, v, r, s);
                    if (signer == address(0)) {
                        return (address(0), RecoverError.InvalidSignature);
                    }
                    return (signer, RecoverError.NoError);
                }
                /**
                 * @dev Overload of {ECDSA-recover} that receives the `v`,
                 * `r` and `s` signature fields separately.
                 */
                function recover(
                    bytes32 hash,
                    uint8 v,
                    bytes32 r,
                    bytes32 s
                ) internal pure returns (address) {
                    (address recovered, RecoverError error) = tryRecover(hash, v, r, s);
                    _throwError(error);
                    return recovered;
                }
                /**
                 * @dev Returns an Ethereum Signed Message, created from a `hash`. This
                 * produces hash corresponding to the one signed with the
                 * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
                 * JSON-RPC method as part of EIP-191.
                 *
                 * See {recover}.
                 */
                function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32) {
                    // 32 is the length in bytes of hash,
                    // enforced by the type signature above
                    return keccak256(abi.encodePacked("\\x19Ethereum Signed Message:\
            32", hash));
                }
                /**
                 * @dev Returns an Ethereum Signed Message, created from `s`. This
                 * produces hash corresponding to the one signed with the
                 * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
                 * JSON-RPC method as part of EIP-191.
                 *
                 * See {recover}.
                 */
                function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32) {
                    return keccak256(abi.encodePacked("\\x19Ethereum Signed Message:\
            ", StringsUpgradeable.toString(s.length), s));
                }
                /**
                 * @dev Returns an Ethereum Signed Typed Data, created from a
                 * `domainSeparator` and a `structHash`. This produces hash corresponding
                 * to the one signed with the
                 * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`]
                 * JSON-RPC method as part of EIP-712.
                 *
                 * See {recover}.
                 */
                function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32) {
                    return keccak256(abi.encodePacked("\\x19\\x01", domainSeparator, structHash));
                }
            }
            // SPDX-License-Identifier: MIT
            // OpenZeppelin Contracts (last updated v4.8.0) (utils/cryptography/EIP712.sol)
            pragma solidity ^0.8.0;
            import "./ECDSAUpgradeable.sol";
            import "../../proxy/utils/Initializable.sol";
            /**
             * @dev https://eips.ethereum.org/EIPS/eip-712[EIP 712] is a standard for hashing and signing of typed structured data.
             *
             * The encoding specified in the EIP is very generic, and such a generic implementation in Solidity is not feasible,
             * thus this contract does not implement the encoding itself. Protocols need to implement the type-specific encoding
             * they need in their contracts using a combination of `abi.encode` and `keccak256`.
             *
             * This contract implements the EIP 712 domain separator ({_domainSeparatorV4}) that is used as part of the encoding
             * scheme, and the final step of the encoding to obtain the message digest that is then signed via ECDSA
             * ({_hashTypedDataV4}).
             *
             * The implementation of the domain separator was designed to be as efficient as possible while still properly updating
             * the chain id to protect against replay attacks on an eventual fork of the chain.
             *
             * NOTE: This contract implements the version of the encoding known as "v4", as implemented by the JSON RPC method
             * https://docs.metamask.io/guide/signing-data.html[`eth_signTypedDataV4` in MetaMask].
             *
             * _Available since v3.4._
             *
             * @custom:storage-size 52
             */
            abstract contract EIP712Upgradeable is Initializable {
                /* solhint-disable var-name-mixedcase */
                bytes32 private _HASHED_NAME;
                bytes32 private _HASHED_VERSION;
                bytes32 private constant _TYPE_HASH = keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)");
                /* solhint-enable var-name-mixedcase */
                /**
                 * @dev Initializes the domain separator and parameter caches.
                 *
                 * The meaning of `name` and `version` is specified in
                 * https://eips.ethereum.org/EIPS/eip-712#definition-of-domainseparator[EIP 712]:
                 *
                 * - `name`: the user readable name of the signing domain, i.e. the name of the DApp or the protocol.
                 * - `version`: the current major version of the signing domain.
                 *
                 * NOTE: These parameters cannot be changed except through a xref:learn::upgrading-smart-contracts.adoc[smart
                 * contract upgrade].
                 */
                function __EIP712_init(string memory name, string memory version) internal onlyInitializing {
                    __EIP712_init_unchained(name, version);
                }
                function __EIP712_init_unchained(string memory name, string memory version) internal onlyInitializing {
                    bytes32 hashedName = keccak256(bytes(name));
                    bytes32 hashedVersion = keccak256(bytes(version));
                    _HASHED_NAME = hashedName;
                    _HASHED_VERSION = hashedVersion;
                }
                /**
                 * @dev Returns the domain separator for the current chain.
                 */
                function _domainSeparatorV4() internal view returns (bytes32) {
                    return _buildDomainSeparator(_TYPE_HASH, _EIP712NameHash(), _EIP712VersionHash());
                }
                function _buildDomainSeparator(
                    bytes32 typeHash,
                    bytes32 nameHash,
                    bytes32 versionHash
                ) private view returns (bytes32) {
                    return keccak256(abi.encode(typeHash, nameHash, versionHash, block.chainid, address(this)));
                }
                /**
                 * @dev Given an already https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstruct[hashed struct], this
                 * function returns the hash of the fully encoded EIP712 message for this domain.
                 *
                 * This hash can be used together with {ECDSA-recover} to obtain the signer of a message. For example:
                 *
                 * ```solidity
                 * bytes32 digest = _hashTypedDataV4(keccak256(abi.encode(
                 *     keccak256("Mail(address to,string contents)"),
                 *     mailTo,
                 *     keccak256(bytes(mailContents))
                 * )));
                 * address signer = ECDSA.recover(digest, signature);
                 * ```
                 */
                function _hashTypedDataV4(bytes32 structHash) internal view virtual returns (bytes32) {
                    return ECDSAUpgradeable.toTypedDataHash(_domainSeparatorV4(), structHash);
                }
                /**
                 * @dev The hash of the name parameter for the EIP712 domain.
                 *
                 * NOTE: This function reads from storage by default, but can be redefined to return a constant value if gas costs
                 * are a concern.
                 */
                function _EIP712NameHash() internal virtual view returns (bytes32) {
                    return _HASHED_NAME;
                }
                /**
                 * @dev The hash of the version parameter for the EIP712 domain.
                 *
                 * NOTE: This function reads from storage by default, but can be redefined to return a constant value if gas costs
                 * are a concern.
                 */
                function _EIP712VersionHash() internal virtual view returns (bytes32) {
                    return _HASHED_VERSION;
                }
                /**
                 * @dev This empty reserved space is put in place to allow future versions to add new
                 * variables without shifting down storage in the inheritance chain.
                 * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
                 */
                uint256[50] private __gap;
            }
            // SPDX-License-Identifier: MIT
            // OpenZeppelin Contracts (last updated v4.7.0) (access/Ownable.sol)
            pragma solidity ^0.8.0;
            import "../utils/Context.sol";
            /**
             * @dev Contract module which provides a basic access control mechanism, where
             * there is an account (an owner) that can be granted exclusive access to
             * specific functions.
             *
             * By default, the owner account will be the one that deploys the contract. This
             * can later be changed with {transferOwnership}.
             *
             * This module is used through inheritance. It will make available the modifier
             * `onlyOwner`, which can be applied to your functions to restrict their use to
             * the owner.
             */
            abstract contract Ownable is Context {
                address private _owner;
                event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
                /**
                 * @dev Initializes the contract setting the deployer as the initial owner.
                 */
                constructor() {
                    _transferOwnership(_msgSender());
                }
                /**
                 * @dev Throws if called by any account other than the owner.
                 */
                modifier onlyOwner() {
                    _checkOwner();
                    _;
                }
                /**
                 * @dev Returns the address of the current owner.
                 */
                function owner() public view virtual returns (address) {
                    return _owner;
                }
                /**
                 * @dev Throws if the sender is not the owner.
                 */
                function _checkOwner() internal view virtual {
                    require(owner() == _msgSender(), "Ownable: caller is not the owner");
                }
                /**
                 * @dev Leaves the contract without owner. It will not be possible to call
                 * `onlyOwner` functions anymore. Can only be called by the current owner.
                 *
                 * NOTE: Renouncing ownership will leave the contract without an owner,
                 * thereby removing any functionality that is only available to the owner.
                 */
                function renounceOwnership() public virtual onlyOwner {
                    _transferOwnership(address(0));
                }
                /**
                 * @dev Transfers ownership of the contract to a new account (`newOwner`).
                 * Can only be called by the current owner.
                 */
                function transferOwnership(address newOwner) public virtual onlyOwner {
                    require(newOwner != address(0), "Ownable: new owner is the zero address");
                    _transferOwnership(newOwner);
                }
                /**
                 * @dev Transfers ownership of the contract to a new account (`newOwner`).
                 * Internal function without access restriction.
                 */
                function _transferOwnership(address newOwner) internal virtual {
                    address oldOwner = _owner;
                    _owner = newOwner;
                    emit OwnershipTransferred(oldOwner, newOwner);
                }
            }
            // SPDX-License-Identifier: MIT
            // OpenZeppelin Contracts v4.4.1 (proxy/beacon/UpgradeableBeacon.sol)
            pragma solidity ^0.8.0;
            import "./IBeacon.sol";
            import "../../access/Ownable.sol";
            import "../../utils/Address.sol";
            /**
             * @dev This contract is used in conjunction with one or more instances of {BeaconProxy} to determine their
             * implementation contract, which is where they will delegate all function calls.
             *
             * An owner is able to change the implementation the beacon points to, thus upgrading the proxies that use this beacon.
             */
            contract UpgradeableBeacon is IBeacon, Ownable {
                address private _implementation;
                /**
                 * @dev Emitted when the implementation returned by the beacon is changed.
                 */
                event Upgraded(address indexed implementation);
                /**
                 * @dev Sets the address of the initial implementation, and the deployer account as the owner who can upgrade the
                 * beacon.
                 */
                constructor(address implementation_) {
                    _setImplementation(implementation_);
                }
                /**
                 * @dev Returns the current implementation address.
                 */
                function implementation() public view virtual override returns (address) {
                    return _implementation;
                }
                /**
                 * @dev Upgrades the beacon to a new implementation.
                 *
                 * Emits an {Upgraded} event.
                 *
                 * Requirements:
                 *
                 * - msg.sender must be the owner of the contract.
                 * - `newImplementation` must be a contract.
                 */
                function upgradeTo(address newImplementation) public virtual onlyOwner {
                    _setImplementation(newImplementation);
                    emit Upgraded(newImplementation);
                }
                /**
                 * @dev Sets the implementation contract address for this beacon
                 *
                 * Requirements:
                 *
                 * - `newImplementation` must be a contract.
                 */
                function _setImplementation(address newImplementation) private {
                    require(Address.isContract(newImplementation), "UpgradeableBeacon: implementation is not a contract");
                    _implementation = newImplementation;
                }
            }
            // SPDX-License-Identifier: MIT
            // OpenZeppelin Contracts v4.4.1 (utils/Context.sol)
            pragma solidity ^0.8.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 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 calldata) {
                    return msg.data;
                }
            }
            // SPDX-License-Identifier: MIT
            // OpenZeppelin Contracts (last updated v4.8.0) (utils/Address.sol)
            pragma solidity ^0.8.1;
            /**
             * @dev Collection of functions related to the address type
             */
            library Address {
                /**
                 * @dev Returns true if `account` is a contract.
                 *
                 * [IMPORTANT]
                 * ====
                 * It is unsafe to assume that an address for which this function returns
                 * false is an externally-owned account (EOA) and not a contract.
                 *
                 * Among others, `isContract` will return false for the following
                 * types of addresses:
                 *
                 *  - an externally-owned account
                 *  - a contract in construction
                 *  - an address where a contract will be created
                 *  - an address where a contract lived, but was destroyed
                 * ====
                 *
                 * [IMPORTANT]
                 * ====
                 * You shouldn't rely on `isContract` to protect against flash loan attacks!
                 *
                 * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
                 * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
                 * constructor.
                 * ====
                 */
                function isContract(address account) internal view returns (bool) {
                    // This method relies on extcodesize/address.code.length, which returns 0
                    // for contracts in construction, since the code is only stored at the end
                    // of the constructor execution.
                    return account.code.length > 0;
                }
                /**
                 * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
                 * `recipient`, forwarding all available gas and reverting on errors.
                 *
                 * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
                 * of certain opcodes, possibly making contracts go over the 2300 gas limit
                 * imposed by `transfer`, making them unable to receive funds via
                 * `transfer`. {sendValue} removes this limitation.
                 *
                 * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
                 *
                 * IMPORTANT: because control is transferred to `recipient`, care must be
                 * taken to not create reentrancy vulnerabilities. Consider using
                 * {ReentrancyGuard} or the
                 * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
                 */
                function sendValue(address payable recipient, uint256 amount) internal {
                    require(address(this).balance >= amount, "Address: insufficient balance");
                    (bool success, ) = recipient.call{value: amount}("");
                    require(success, "Address: unable to send value, recipient may have reverted");
                }
                /**
                 * @dev Performs a Solidity function call using a low level `call`. A
                 * plain `call` is an unsafe replacement for a function call: use this
                 * function instead.
                 *
                 * If `target` reverts with a revert reason, it is bubbled up by this
                 * function (like regular Solidity function calls).
                 *
                 * Returns the raw returned data. To convert to the expected return value,
                 * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
                 *
                 * Requirements:
                 *
                 * - `target` must be a contract.
                 * - calling `target` with `data` must not revert.
                 *
                 * _Available since v3.1._
                 */
                function functionCall(address target, bytes memory data) internal returns (bytes memory) {
                    return functionCallWithValue(target, data, 0, "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");
                    (bool success, bytes memory returndata) = target.call{value: value}(data);
                    return verifyCallResultFromTarget(target, success, returndata, errorMessage);
                }
                /**
                 * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                 * but performing a static call.
                 *
                 * _Available since v3.3._
                 */
                function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
                    return functionStaticCall(target, data, "Address: low-level static call failed");
                }
                /**
                 * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
                 * but performing a static call.
                 *
                 * _Available since v3.3._
                 */
                function functionStaticCall(
                    address target,
                    bytes memory data,
                    string memory errorMessage
                ) internal view returns (bytes memory) {
                    (bool success, bytes memory returndata) = target.staticcall(data);
                    return verifyCallResultFromTarget(target, success, returndata, errorMessage);
                }
                /**
                 * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                 * but performing a delegate call.
                 *
                 * _Available since v3.4._
                 */
                function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
                    return functionDelegateCall(target, data, "Address: low-level delegate call failed");
                }
                /**
                 * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
                 * but performing a delegate call.
                 *
                 * _Available since v3.4._
                 */
                function functionDelegateCall(
                    address target,
                    bytes memory data,
                    string memory errorMessage
                ) internal returns (bytes memory) {
                    (bool success, bytes memory returndata) = target.delegatecall(data);
                    return verifyCallResultFromTarget(target, success, returndata, errorMessage);
                }
                /**
                 * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
                 * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
                 *
                 * _Available since v4.8._
                 */
                function verifyCallResultFromTarget(
                    address target,
                    bool success,
                    bytes memory returndata,
                    string memory errorMessage
                ) internal view returns (bytes memory) {
                    if (success) {
                        if (returndata.length == 0) {
                            // only check isContract if the call was successful and the return data is empty
                            // otherwise we already know that it was a contract
                            require(isContract(target), "Address: call to non-contract");
                        }
                        return returndata;
                    } else {
                        _revert(returndata, errorMessage);
                    }
                }
                /**
                 * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
                 * revert reason or using the provided one.
                 *
                 * _Available since v4.3._
                 */
                function verifyCallResult(
                    bool success,
                    bytes memory returndata,
                    string memory errorMessage
                ) internal pure returns (bytes memory) {
                    if (success) {
                        return returndata;
                    } else {
                        _revert(returndata, errorMessage);
                    }
                }
                function _revert(bytes memory returndata, string memory errorMessage) private pure {
                    // Look for revert reason and bubble it up if present
                    if (returndata.length > 0) {
                        // The easiest way to bubble the revert reason is using memory via assembly
                        /// @solidity memory-safe-assembly
                        assembly {
                            let returndata_size := mload(returndata)
                            revert(add(32, returndata), returndata_size)
                        }
                    } else {
                        revert(errorMessage);
                    }
                }
            }
            // SPDX-License-Identifier: MIT
            // OpenZeppelin Contracts v4.4.1 (proxy/beacon/IBeacon.sol)
            pragma solidity ^0.8.0;
            /**
             * @dev This is the interface that {BeaconProxy} expects of its beacon.
             */
            interface IBeacon {
                /**
                 * @dev Must return an address that can be used as a delegate call target.
                 *
                 * {BeaconProxy} will check that this address is a contract.
                 */
                function implementation() external view returns (address);
            }
            // SPDX-License-Identifier: MIT
            // OpenZeppelin Contracts (last updated v4.6.0) (proxy/Proxy.sol)
            pragma solidity ^0.8.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 internal call site, it will return directly to the external caller.
                 */
                function _delegate(address implementation) internal virtual {
                    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 overridden so it returns the address to which the fallback function
                 * and {_fallback} should delegate.
                 */
                function _implementation() internal view virtual returns (address);
                /**
                 * @dev Delegates the current call to the address returned by `_implementation()`.
                 *
                 * This function does not return to its internal call site, it will return directly to the external caller.
                 */
                function _fallback() internal virtual {
                    _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() external payable virtual {
                    _fallback();
                }
                /**
                 * @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if call data
                 * is empty.
                 */
                receive() external payable virtual {
                    _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 overridden should call `super._beforeFallback()`.
                 */
                function _beforeFallback() internal virtual {}
            }
            // SPDX-License-Identifier: MIT
            // OpenZeppelin Contracts (last updated v4.5.0) (proxy/ERC1967/ERC1967Upgrade.sol)
            pragma solidity ^0.8.2;
            import "../beacon/IBeacon.sol";
            import "../../interfaces/draft-IERC1822.sol";
            import "../../utils/Address.sol";
            import "../../utils/StorageSlot.sol";
            /**
             * @dev This abstract contract provides getters and event emitting update functions for
             * https://eips.ethereum.org/EIPS/eip-1967[EIP1967] slots.
             *
             * _Available since v4.1._
             *
             * @custom:oz-upgrades-unsafe-allow delegatecall
             */
            abstract contract ERC1967Upgrade {
                // This is the keccak-256 hash of "eip1967.proxy.rollback" subtracted by 1
                bytes32 private constant _ROLLBACK_SLOT = 0x4910fdfa16fed3260ed0e7147f7cc6da11a60208b5b9406d12a635614ffd9143;
                /**
                 * @dev Storage slot with the address of the current implementation.
                 * This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1, and is
                 * validated in the constructor.
                 */
                bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
                /**
                 * @dev Emitted when the implementation is upgraded.
                 */
                event Upgraded(address indexed implementation);
                /**
                 * @dev Returns the current implementation address.
                 */
                function _getImplementation() internal view returns (address) {
                    return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
                }
                /**
                 * @dev Stores a new address in the EIP1967 implementation slot.
                 */
                function _setImplementation(address newImplementation) private {
                    require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract");
                    StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
                }
                /**
                 * @dev Perform implementation upgrade
                 *
                 * Emits an {Upgraded} event.
                 */
                function _upgradeTo(address newImplementation) internal {
                    _setImplementation(newImplementation);
                    emit Upgraded(newImplementation);
                }
                /**
                 * @dev Perform implementation upgrade with additional setup call.
                 *
                 * Emits an {Upgraded} event.
                 */
                function _upgradeToAndCall(
                    address newImplementation,
                    bytes memory data,
                    bool forceCall
                ) internal {
                    _upgradeTo(newImplementation);
                    if (data.length > 0 || forceCall) {
                        Address.functionDelegateCall(newImplementation, data);
                    }
                }
                /**
                 * @dev Perform implementation upgrade with security checks for UUPS proxies, and additional setup call.
                 *
                 * Emits an {Upgraded} event.
                 */
                function _upgradeToAndCallUUPS(
                    address newImplementation,
                    bytes memory data,
                    bool forceCall
                ) internal {
                    // Upgrades from old implementations will perform a rollback test. This test requires the new
                    // implementation to upgrade back to the old, non-ERC1822 compliant, implementation. Removing
                    // this special case will break upgrade paths from old UUPS implementation to new ones.
                    if (StorageSlot.getBooleanSlot(_ROLLBACK_SLOT).value) {
                        _setImplementation(newImplementation);
                    } else {
                        try IERC1822Proxiable(newImplementation).proxiableUUID() returns (bytes32 slot) {
                            require(slot == _IMPLEMENTATION_SLOT, "ERC1967Upgrade: unsupported proxiableUUID");
                        } catch {
                            revert("ERC1967Upgrade: new implementation is not UUPS");
                        }
                        _upgradeToAndCall(newImplementation, data, forceCall);
                    }
                }
                /**
                 * @dev Storage slot with the admin of the contract.
                 * This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1, and is
                 * validated in the constructor.
                 */
                bytes32 internal constant _ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;
                /**
                 * @dev Emitted when the admin account has changed.
                 */
                event AdminChanged(address previousAdmin, address newAdmin);
                /**
                 * @dev Returns the current admin.
                 */
                function _getAdmin() internal view returns (address) {
                    return StorageSlot.getAddressSlot(_ADMIN_SLOT).value;
                }
                /**
                 * @dev Stores a new address in the EIP1967 admin slot.
                 */
                function _setAdmin(address newAdmin) private {
                    require(newAdmin != address(0), "ERC1967: new admin is the zero address");
                    StorageSlot.getAddressSlot(_ADMIN_SLOT).value = newAdmin;
                }
                /**
                 * @dev Changes the admin of the proxy.
                 *
                 * Emits an {AdminChanged} event.
                 */
                function _changeAdmin(address newAdmin) internal {
                    emit AdminChanged(_getAdmin(), newAdmin);
                    _setAdmin(newAdmin);
                }
                /**
                 * @dev The storage slot of the UpgradeableBeacon contract which defines the implementation for this proxy.
                 * This is bytes32(uint256(keccak256('eip1967.proxy.beacon')) - 1)) and is validated in the constructor.
                 */
                bytes32 internal constant _BEACON_SLOT = 0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50;
                /**
                 * @dev Emitted when the beacon is upgraded.
                 */
                event BeaconUpgraded(address indexed beacon);
                /**
                 * @dev Returns the current beacon.
                 */
                function _getBeacon() internal view returns (address) {
                    return StorageSlot.getAddressSlot(_BEACON_SLOT).value;
                }
                /**
                 * @dev Stores a new beacon in the EIP1967 beacon slot.
                 */
                function _setBeacon(address newBeacon) private {
                    require(Address.isContract(newBeacon), "ERC1967: new beacon is not a contract");
                    require(
                        Address.isContract(IBeacon(newBeacon).implementation()),
                        "ERC1967: beacon implementation is not a contract"
                    );
                    StorageSlot.getAddressSlot(_BEACON_SLOT).value = newBeacon;
                }
                /**
                 * @dev Perform beacon upgrade with additional setup call. Note: This upgrades the address of the beacon, it does
                 * not upgrade the implementation contained in the beacon (see {UpgradeableBeacon-_setImplementation} for that).
                 *
                 * Emits a {BeaconUpgraded} event.
                 */
                function _upgradeBeaconToAndCall(
                    address newBeacon,
                    bytes memory data,
                    bool forceCall
                ) internal {
                    _setBeacon(newBeacon);
                    emit BeaconUpgraded(newBeacon);
                    if (data.length > 0 || forceCall) {
                        Address.functionDelegateCall(IBeacon(newBeacon).implementation(), data);
                    }
                }
            }
            // SPDX-License-Identifier: MIT
            // OpenZeppelin Contracts (last updated v4.7.0) (utils/StorageSlot.sol)
            pragma solidity ^0.8.0;
            /**
             * @dev Library for reading and writing primitive types to specific storage slots.
             *
             * Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts.
             * This library helps with reading and writing to such slots without the need for inline assembly.
             *
             * The functions in this library return Slot structs that contain a `value` member that can be used to read or write.
             *
             * Example usage to set ERC1967 implementation slot:
             * ```
             * contract ERC1967 {
             *     bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
             *
             *     function _getImplementation() internal view returns (address) {
             *         return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
             *     }
             *
             *     function _setImplementation(address newImplementation) internal {
             *         require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract");
             *         StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
             *     }
             * }
             * ```
             *
             * _Available since v4.1 for `address`, `bool`, `bytes32`, and `uint256`._
             */
            library StorageSlot {
                struct AddressSlot {
                    address value;
                }
                struct BooleanSlot {
                    bool value;
                }
                struct Bytes32Slot {
                    bytes32 value;
                }
                struct Uint256Slot {
                    uint256 value;
                }
                /**
                 * @dev Returns an `AddressSlot` with member `value` located at `slot`.
                 */
                function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {
                    /// @solidity memory-safe-assembly
                    assembly {
                        r.slot := slot
                    }
                }
                /**
                 * @dev Returns an `BooleanSlot` with member `value` located at `slot`.
                 */
                function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {
                    /// @solidity memory-safe-assembly
                    assembly {
                        r.slot := slot
                    }
                }
                /**
                 * @dev Returns an `Bytes32Slot` with member `value` located at `slot`.
                 */
                function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {
                    /// @solidity memory-safe-assembly
                    assembly {
                        r.slot := slot
                    }
                }
                /**
                 * @dev Returns an `Uint256Slot` with member `value` located at `slot`.
                 */
                function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {
                    /// @solidity memory-safe-assembly
                    assembly {
                        r.slot := slot
                    }
                }
            }
            // SPDX-License-Identifier: MIT
            // OpenZeppelin Contracts (last updated v4.5.0) (interfaces/draft-IERC1822.sol)
            pragma solidity ^0.8.0;
            /**
             * @dev ERC1822: Universal Upgradeable Proxy Standard (UUPS) documents a method for upgradeability through a simplified
             * proxy whose upgrades are fully controlled by the current implementation.
             */
            interface IERC1822Proxiable {
                /**
                 * @dev Returns the storage slot that the proxiable contract assumes is being used to store the implementation
                 * address.
                 *
                 * IMPORTANT: A proxy pointing at a proxiable contract should not be considered proxiable itself, because this risks
                 * bricking a proxy that upgrades to it, by delegating to itself until out of gas. Thus it is critical that this
                 * function revert if invoked through a proxy.
                 */
                function proxiableUUID() external view returns (bytes32);
            }
            

            File 3 of 3: SBT
            // SPDX-License-Identifier: MIT
            pragma solidity ^0.8.16;
            import "@openzeppelin/contracts-upgradeable/utils/introspection/ERC165Upgradeable.sol";
            import "@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol";
            import "@openzeppelin/contracts-upgradeable/utils/StringsUpgradeable.sol";
            import "@openzeppelin/contracts-upgradeable/utils/CountersUpgradeable.sol";
            import "@openzeppelin/contracts-upgradeable/utils/structs/EnumerableSetUpgradeable.sol";
            import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
            import "@openzeppelin/contracts-upgradeable/utils/cryptography/SignatureCheckerUpgradeable.sol";
            import "@openzeppelin/contracts-upgradeable/utils/cryptography/draft-EIP712Upgradeable.sol";
            import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
            import "./IERC721Metadata.sol";
            import "./IERC4671.sol";
            import "./IERC5192.sol";
            import "./IERC4973.sol";
            import "./ISBT.sol";
            import "./ISBTReceiver.sol";
            contract SBT is
                Initializable,
                OwnableUpgradeable,
                ERC165Upgradeable,
                EIP712Upgradeable,
                IERC721Metadata,
                IERC4671,
                IERC5192,
                IERC4973,
                ISBT
            {
                using AddressUpgradeable for address;
                using StringsUpgradeable for uint256;
                using CountersUpgradeable for CountersUpgradeable.Counter;
                using EnumerableSetUpgradeable for EnumerableSetUpgradeable.UintSet;
                struct Token {
                    address owner;
                    uint256 expiration;
                    bool revoked;
                }
                string public constant VERSION = "1";
                bytes32 public constant AGREEMENT_TYPEHASH = keccak256("Agreement(address active,address passive,string tokenURI)");
                bool public claimable;
                // Token name
                string private _name;
                // Token symbol
                string private _symbol;
                address[] internal _minters;
                CountersUpgradeable.Counter internal _tokenIdCounter;
                string internal _baseTokenURI;
                string internal _contractURI;
                // Mapping from token ID to Token
                mapping(uint256 => Token) internal _tokens;
                // Mapping from holder address to their (enumerable) set of owned tokens
                mapping(address => EnumerableSetUpgradeable.UintSet) internal _holderTokens;
                // Optional mapping for token URIs
                mapping(uint256 => string) internal _tokenURIs;
                /// prevent implementation-contract init() from getting called
                /// @custom:oz-upgrades-unsafe-allow constructor
                constructor() {
                    _disableInitializers();
                }
                // function as a constructor's alternative
                function initialize(
                    address owner,
                    string memory name_,
                    string memory symbol_,
                    string memory contractURI_,
                    string memory baseURI,
                    bool claimable_
                ) public initializer {
                    __Ownable_init();
                    // Factory's transfering proxy ownership
                    transferOwnership(owner);
                    _name = name_;
                    _symbol = symbol_;
                    _contractURI = contractURI_;
                    claimable = claimable_;
                    _baseTokenURI = baseURI;
                    // set default minters
                    _minters.push(owner);
                    __Context_init();
                    __EIP712_init(name_, VERSION);
                }
                /**
                 * @dev See {IERC721Metadata-name}.
                 */
                function name() external view override returns (string memory) {
                    return _name;
                }
                /**
                 * @dev See {IERC721Metadata-symbol}.
                 */
                function symbol() external view override returns (string memory) {
                    return _symbol;
                }
                /**
                 * @dev See {IERC721Metadata-tokenURI}.
                 */
                function tokenURI(uint256 tokenId) external view virtual override returns (string memory) {
                    _requireMinted(tokenId);
                    string memory _tokenURI = _tokenURIs[tokenId];
                    if (bytes(_tokenURI).length > 0) {
                        return _tokenURI;
                    }
                    return bytes(_baseTokenURI).length > 0 ? string(abi.encodePacked(_baseTokenURI, tokenId.toString())) : "";
                }
                /**
                 * @dev See {IERC4671-isValid}.
                 */
                function isValid(uint256 tokenId) external view override returns (bool) {
                    /* solhint-disable */
                    return (_exists(tokenId) &&
                        !_tokens[tokenId].revoked &&
                        (_tokens[tokenId].expiration == 0 || _tokens[tokenId].expiration > block.timestamp));
                }
                /**
                 * @dev See {IERC4671-hasValid}.
                 */
                function hasValid(address owner) external view override returns (bool) {
                    return this.balanceOf(owner) > 0;
                }
                /**
                 * @dev See {IERC4671-balanceOf}.
                 */
                function balanceOf(address owner) external view override(IERC4671, IERC4973) returns (uint256) {
                    require(owner != address(0), "Invalid owner");
                    uint256 count = 0;
                    uint256 len = _holderTokens[owner].length();
                    for (uint256 i = 0; i < len; i++) {
                        uint256 tokenId = _holderTokens[owner].at(i);
                        if (this.isValid(tokenId)) count++;
                    }
                    return count;
                }
                /**
                 * @dev See {IERC4671-ownerOf}.
                 */
                function ownerOf(uint256 tokenId) external view override(IERC4671, IERC4973) returns (address) {
                    _requireMinted(tokenId);
                    return _tokens[tokenId].owner;
                }
                /**
                 * @dev See {IERC165-supportsInterface}.
                 */
                function supportsInterface(bytes4 interfaceId)
                    public
                    view
                    virtual
                    override(ERC165Upgradeable, IERC165)
                    returns (bool)
                {
                    return
                        interfaceId == type(IERC721Metadata).interfaceId ||
                        interfaceId == type(IERC4671).interfaceId ||
                        interfaceId == type(IERC5192).interfaceId ||
                        interfaceId == type(IERC4973).interfaceId ||
                        super.supportsInterface(interfaceId);
                }
                /// @dev See {IERC5192-locked}.
                /// @return always return True
                function locked(uint256 tokenId) external view override returns (bool) {
                    _requireMinted(tokenId);
                    return true;
                }
                /**
                 * @dev See {IERC4973-unequip}.
                 */
                function unequip(uint256 tokenId) external override {
                    _requireMinted(tokenId);
                    _burn(tokenId);
                }
                /**
                 * @dev See {IERC4973-give}.
                 */
                function give(
                    address to,
                    string calldata uri,
                    bytes calldata signature
                ) external override returns (uint256) {
                    require(_msgSender() != to, "cannot give from self");
                    uint256 tokenId = _safeCheckAgreement(_msgSender(), to, uri, signature);
                    _safeMint(to, tokenId);
                    _setTokenURI(tokenId, uri);
                    return tokenId;
                }
                /**
                 * @dev See {IERC4973-take}.
                 */
                function take(
                    address from,
                    string calldata uri,
                    bytes calldata signature
                ) external override returns (uint256) {
                    _requireClaimable();
                    require(_msgSender() != from, "cannot take from self");
                    uint256 tokenId = _safeCheckAgreement(_msgSender(), from, uri, signature);
                    _safeMint(from, tokenId);
                    _setTokenURI(tokenId, uri);
                    return tokenId;
                }
                // ============================================================
                // SBT Standard
                // ============================================================
                /// @notice Mark the token as revoked
                /// @param tokenId Identifier of the token
                function revoke(uint256 tokenId, string calldata reason) external virtual onlyOwner {
                    _requireMinted(tokenId);
                    require(!_tokens[tokenId].revoked, "Token already revoked");
                    _tokens[tokenId].revoked = true;
                    // // TODO: consider remove token from _holderTokens
                    // _holderTokens[_tokens[tokenId].owner].remove(tokenId);
                    // emit event {IERC4671-Revoked}
                    emit Revoked(_tokens[tokenId].owner, tokenId);
                    emit RevokedByReason(_tokens[tokenId].owner, tokenId, reason);
                }
                /// @notice claim a SBT
                /// @dev TODO: User should be able to claim token by proof/signature!?
                /// @dev TODO: This is free for claiming?
                function claim() external virtual {
                    _requireClaimable();
                    _tokenIdCounter.increment();
                    uint256 newTokenId = _tokenIdCounter.current();
                    _safeMint(_msgSender(), newTokenId);
                }
                /// @notice mint to receivers
                /// @dev TODO: This is free for claiming?
                /// @param receivers The SBT receiver list
                /// @param expiration expired block timestamp
                function mint(address[] calldata receivers, uint256 expiration) external virtual onlyMinter {
                    require(expiration == 0 || expiration > block.timestamp, "Invalid expiration");
                    for (uint256 i = 0; i < receivers.length; i++) {
                        _tokenIdCounter.increment();
                        uint256 newTokenId = _tokenIdCounter.current();
                        _safeMint(receivers[i], newTokenId);
                        if (expiration > 0) {
                            _tokens[newTokenId].expiration = expiration;
                            emit ExpirationSet(newTokenId, expiration);
                        }
                    }
                }
                /// @notice burn a SBT
                /// @dev only owner/winner can burn owned SBT
                /// @param tokenId SBT ID
                function burn(uint256 tokenId) external virtual {
                    _burn(tokenId);
                }
                /// @notice set SBT expiration
                /// @param tokenId SBT ID
                /// @param expiration expired block timestamp
                function setExpiration(uint256 tokenId, uint256 expiration) external virtual onlyOwner {
                    _requireMinted(tokenId);
                    require(!_tokens[tokenId].revoked, "Token already revoked");
                    require(expiration == 0 || expiration > block.timestamp, "Invalid expiration");
                    _tokens[tokenId].expiration = expiration;
                    emit ExpirationSet(tokenId, expiration);
                }
                /**
                 * @dev If set, the resulting URI for each token will be the concatenation of the `baseURI` and the `tokenId`. Empty by default
                 */
                function setBaseURI(string calldata baseURI) external virtual onlyOwner {
                    _baseTokenURI = baseURI;
                    emit BaseURISet(_baseTokenURI);
                }
                /// @notice set a minter list
                /// @param minters list of minter address
                function setMinters(address[] calldata minters) external onlyOwner {
                    _minters = minters;
                }
                /// @notice enable/disable claiming SBT
                /// @param claimable_ flag to enable/disable claiming SBT
                function setClaimable(bool claimable_) external onlyOwner {
                    claimable = claimable_;
                    emit ClaimableSet(claimable);
                }
                /// @notice Return the contract URI
                /// @return contractUri The contract URI.
                function contractURI() external view returns (string memory) {
                    return _contractURI;
                }
                /// @notice Set the contract URI
                /// @param contractURI_ The contract URI to be set
                function setContractURI(string calldata contractURI_) external onlyOwner {
                    _contractURI = contractURI_;
                    emit ContractURISet(_contractURI);
                }
                function _mint(address to, uint256 tokenId) internal virtual {
                    require(to != address(0), "Mint to the zero address");
                    require(!_exists(tokenId), "Token already minted");
                    _tokens[tokenId].owner = to;
                    _holderTokens[to].add(tokenId);
                    // emit event {IERC5192-Locked}
                    emit Locked(tokenId);
                    // emit event {IERC4671-Minted}
                    emit Minted(to, tokenId);
                    // emit event {IERC4973-Transfer}
                    emit Transfer(address(0), to, tokenId);
                }
                function _burn(uint256 tokenId) internal virtual {
                    address owner = _tokens[tokenId].owner;
                    require(_msgSender() == owner, "Invalid owner");
                    _holderTokens[owner].remove(tokenId);
                    delete _tokens[tokenId];
                    if (bytes(_tokenURIs[tokenId]).length != 0) {
                        delete _tokenURIs[tokenId];
                    }
                    emit Transfer(owner, address(0), tokenId);
                    emit Burned(tokenId);
                }
                /**
                 * @dev Safely mints `tokenId` and transfers it to `to`.
                 *
                 * Requirements:
                 *
                 * - `tokenId` must not exist.
                 * - If `to` refers to a smart contract, it must implement {ISBTReceiver-onSBTReceived}, which is called upon a safe transfer.
                 *
                 * Emits a {Transfer} event.
                 */
                function _safeMint(address to, uint256 tokenId) internal virtual {
                    _safeMint(to, tokenId, "");
                }
                /**
                 * @dev Same as {_safeMint-address-uint256-}[`_safeMint`], with an additional `data` parameter which is
                 * forwarded in {ISBTReceiver-onSBTReceived} to contract recipients.
                 */
                function _safeMint(
                    address to,
                    uint256 tokenId,
                    bytes memory data
                ) internal virtual {
                    _mint(to, tokenId);
                    require(_checkOnSBTReceived(address(0), to, tokenId, data), "transfer to non SBTReceiver implementer");
                }
                function _setTokenURI(uint256 tokenId, string memory _tokenURI) internal virtual {
                    require(_exists(tokenId), "URI set of nonexistent token");
                    _tokenURIs[tokenId] = _tokenURI;
                }
                /**
                 * @dev Internal function to invoke {ISBTReceiver-onSBTReceived} on a target address.
                 * The call is not executed if the target address is not a contract.
                 *
                 * @param from address representing the previous owner of the given token ID
                 * @param to target address that will receive the tokens
                 * @param tokenId uint256 ID of the token to be transferred
                 * @param data bytes optional data to send along with the call
                 * @return bool whether the call correctly returned the expected magic value
                 */
                function _checkOnSBTReceived(
                    address from,
                    address to,
                    uint256 tokenId,
                    bytes memory data
                ) private returns (bool) {
                    /* solhint-disable */
                    if (to.isContract()) {
                        try ISBTReceiver(to).onSBTReceived(_msgSender(), from, tokenId, data) returns (bytes4 retval) {
                            return retval == ISBTReceiver.onSBTReceived.selector;
                        } catch (bytes memory reason) {
                            if (reason.length == 0) {
                                revert("transfer to non SBTReceiver implementer");
                            } else {
                                /// @solidity memory-safe-assembly
                                assembly {
                                    revert(add(32, reason), mload(reason))
                                }
                            }
                        }
                    } else {
                        return true;
                    }
                }
                function _exists(uint256 tokenId) internal view virtual returns (bool) {
                    Token memory token = _tokens[tokenId];
                    return token.owner != address(0);
                }
                /**
                 * @dev Reverts if the `tokenId` has not been minted yet.
                 */
                function _requireMinted(uint256 tokenId) internal view {
                    require(_exists(tokenId), "Invalid token ID");
                }
                /**
                 * @dev Reverts if _claimable is false.
                 */
                function _requireClaimable() internal view {
                    require(claimable, "Not allowed to claim");
                }
                function _existsMinter(address minter) internal view returns (bool) {
                    for (uint256 i = 0; i < _minters.length; i++) {
                        if (_minters[i] == minter) {
                            return true;
                        }
                    }
                    return false;
                }
                function _safeCheckAgreement(
                    address active,
                    address passive,
                    string calldata uri,
                    bytes calldata signature
                ) internal virtual returns (uint256) {
                    bytes32 hash = _hashTypedDataV4(
                        keccak256(abi.encode(AGREEMENT_TYPEHASH, active, passive, keccak256(bytes(uri))))
                    );
                    uint256 tokenId = uint256(hash);
                    require(SignatureCheckerUpgradeable.isValidSignatureNow(passive, hash, signature), "invalid signature");
                    return tokenId;
                }
                modifier onlyMinter() {
                    require(_existsMinter(_msgSender()), "Restricted to minters");
                    _;
                }
            }
            // SPDX-License-Identifier: MIT
            pragma solidity ^0.8.16;
            interface IERC721Metadata {
                function name() external view returns (string memory);
                function symbol() external view returns (string memory);
                function tokenURI(uint256 tokenId) external view returns (string memory);
            }
            // SPDX-License-Identifier: CC0-1.0
            pragma solidity ^0.8.16;
            ///  Note: the ERC-165 identifier for this interface is 0xb45a3c0e.
            interface IERC5192 {
                /// @notice Emitted when the locking status is changed to locked.
                /// @dev If a token is minted and the status is locked, this event should be emitted.
                /// @param tokenId The identifier for a token.
                event Locked(uint256 tokenId);
                /// @notice Emitted when the locking status is changed to unlocked.
                /// @dev If a token is minted and the status is unlocked, this event should be emitted.
                /// @param tokenId The identifier for a token.
                event Unlocked(uint256 tokenId);
                /// @notice Returns the locking status of an Soulbound Token
                /// @dev SBTs assigned to zero address are considered invalid, and queries
                /// about them do throw.
                /// @param tokenId The identifier for an SBT.
                function locked(uint256 tokenId) external view returns (bool);
            }
            // SPDX-License-Identifier: MIT
            pragma solidity ^0.8.16;
            import "@openzeppelin/contracts/utils/introspection/IERC165.sol";
            ///  Note: the ERC-165 identifier for this interface is 0xa511533d.
            interface IERC4671 is IERC165 {
                /// Event emitted when a token `tokenId` is minted for `owner`
                event Minted(address owner, uint256 tokenId);
                /// Event emitted when token `tokenId` of `owner` is revoked
                event Revoked(address owner, uint256 tokenId);
                /// @notice Count all tokens assigned to an owner
                /// @param owner Address for whom to query the balance
                /// @return Number of tokens owned by `owner`
                function balanceOf(address owner) external view returns (uint256);
                /// @notice Get owner of a token
                /// @param tokenId Identifier of the token
                /// @return Address of the owner of `tokenId`
                function ownerOf(uint256 tokenId) external view returns (address);
                /// @notice Check if a token hasn't been revoked
                /// @param tokenId Identifier of the token
                /// @return True if the token is valid, false otherwise
                function isValid(uint256 tokenId) external view returns (bool);
                /// @notice Check if an address owns a valid token in the contract
                /// @param owner Address for whom to check the ownership
                /// @return True if `owner` has a valid token, false otherwise
                function hasValid(address owner) external view returns (bool);
            }
            // SPDX-License-Identifier: CC0-1.0
            pragma solidity ^0.8.16;
            /// @title Account-bound tokens
            /// @dev See https://eips.ethereum.org/EIPS/eip-4973
            /// Note: the ERC-165 identifier for this interface is 0x8d7bac72
            interface IERC4973 {
                /// @dev This emits when ownership of any ABT changes by any mechanism.
                ///  This event emits when ABTs are given or equipped and unequipped
                ///  (`to` == 0).
                event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
                /// @notice Count all ABTs assigned to an owner
                /// @dev ABTs assigned to the zero address are considered invalid, and this
                ///  function throws for queries about the zero address.
                /// @param owner An address for whom to query the balance
                /// @return The number of ABTs owned by `address owner`, possibly zero
                function balanceOf(address owner) external view returns (uint256);
                /// @notice Find the address bound to an ERC4973 account-bound token
                /// @dev ABTs assigned to zero address are considered invalid, and queries
                ///  about them do throw.
                /// @param tokenId The identifier for an ABT.
                /// @return The address of the owner bound to the ABT.
                function ownerOf(uint256 tokenId) external view returns (address);
                /// @notice Removes the `uint256 tokenId` from an account. At any time, an
                ///  ABT receiver must be able to disassociate themselves from an ABT
                ///  publicly through calling this function. After successfully executing this
                ///  function, given the parameters for calling `function give` or
                ///  `function take` a token must be re-equipable.
                /// @dev Must emit a `event Transfer` with the `address to` field pointing to
                ///  the zero address.
                /// @param tokenId The identifier for an ABT.
                function unequip(uint256 tokenId) external;
                /// @notice Creates and transfers the ownership of an ABT from the
                ///  transaction's `msg.sender` to `address to`.
                /// @dev Throws unless `bytes signature` represents an EIP-2098 Compact
                ///  Signature of the EIP-712 structured data hash
                ///  `Agreement(address active,address passive,string tokenURI)` expressing
                ///  `address to`'s explicit agreement to be publicly associated with
                ///  `msg.sender` and `string tokenURI`. A unique `uint256 tokenId` must be
                ///  generated by type-casting the `bytes32` EIP-712 structured data hash to a
                ///  `uint256`. If `bytes signature` is empty or `address to` is a contract,
                ///  an EIP-1271-compatible call to `function isValidSignatureNow(...)` must
                ///  be made to `address to`. A successful execution must result in the
                ///  `event Transfer(msg.sender, to, tokenId)`. Once an ABT exists as an
                ///  `uint256 tokenId` in the contract, `function give(...)` must throw.
                /// @param to The receiver of the ABT.
                /// @param uri A distinct Uniform Resource Identifier (URI) for a given ABT.
                /// @param signature A EIP-2098-compatible Compact Signature of the EIP-712
                ///  structured data hash
                ///  `Agreement(address active,address passive,string tokenURI)` signed by
                ///  `address to`.
                /// @return A unique `uint256 tokenId` generated by type-casting the `bytes32`
                ///  EIP-712 structured data hash to a `uint256`.
                function give(
                    address to,
                    string calldata uri,
                    bytes calldata signature
                ) external returns (uint256);
                /// @notice Creates and transfers the ownership of an ABT from an
                /// `address from` to the transaction's `msg.sender`.
                /// @dev Throws unless `bytes signature` represents an EIP-2098 Compact
                ///  Signature of the EIP-712 structured data hash
                ///  `Agreement(address active,address passive,string tokenURI)` expressing
                ///  `address from`'s explicit agreement to be publicly associated with
                ///  `msg.sender` and `string tokenURI`. A unique `uint256 tokenId` must be
                ///  generated by type-casting the `bytes32` EIP-712 structured data hash to a
                ///  `uint256`. If `bytes signature` is empty or `address from` is a contract,
                ///  an EIP-1271-compatible call to `function isValidSignatureNow(...)` must
                ///  be made to `address from`. A successful execution must result in the
                ///  emission of an `event Transfer(from, msg.sender, tokenId)`. Once an ABT
                ///  exists as an `uint256 tokenId` in the contract, `function take(...)` must
                ///  throw.
                /// @param from The origin of the ABT.
                /// @param uri A distinct Uniform Resource Identifier (URI) for a given ABT.
                /// @param signature A EIP-2098-compatible Compact Signature of the EIP-712
                ///  structured data hash
                ///  `Agreement(address active,address passive,string tokenURI)` signed by
                ///  `address from`.
                /// @return A unique `uint256 tokenId` generated by type-casting the `bytes32`
                ///  EIP-712 structured data hash to a `uint256`.
                function take(
                    address from,
                    string calldata uri,
                    bytes calldata signature
                ) external returns (uint256);
            }
            // SPDX-License-Identifier: MIT
            pragma solidity ^0.8.0;
            /**
             * @title CAN Soulbound token inteface
             */
            interface ISBT {
                /// @dev emit when the contract uri is changed.
                event ContractURISet(string contractURI);
                /// @dev emit when the base URI is changed.
                event BaseURISet(string baseURI);
                /// @dev emit when the token revoked with reason
                event RevokedByReason(address owner, uint256 tokenId, string reason);
                /// @dev emit when the token is set expiration
                event ExpirationSet(uint256 tokenId, uint256 expiration);
                /// @dev emit when the contract is set claimable
                event ClaimableSet(bool claimable);
                /// @dev emit when the token is burned
                event Burned(uint256 tokenId);
                /// @dev mark the token as revoked by contract owner only
                function revoke(uint256 tokenId, string calldata reason) external;
                /// @dev claim a SBT
                function claim() external;
                /// @dev mint to receivers
                function mint(address[] calldata receivers, uint256 expiration) external;
                /// @dev burn a SBT
                function burn(uint256 tokenId) external;
                /// @dev set a minter list
                function setMinters(address[] calldata minters) external;
                /// @dev enable/disable claiming SBT
                function setClaimable(bool claimable) external;
                /// @dev set SBT expiration
                function setExpiration(uint256 tokenId, uint256 expiration) external;
                /// @dev set the base token URI
                function setBaseURI(string calldata baseURI) external;
                /// @dev set the contract URI
                function setContractURI(string calldata contractURI_) external;
                /// @dev get the contract URI
                function contractURI() external view returns (string memory);
            }
            // SPDX-License-Identifier: MIT
            // Inspired by OpenZeppelin Contracts (last updated v4.6.0) (token/ERC721/IERC721Receiver.sol)
            pragma solidity ^0.8.0;
            /**
             * @title Soulbound token receiver interface
             */
            interface ISBTReceiver {
                /**
                 *
                 * It must return its Solidity selector to confirm the token transfer.
                 * If any other value is returned or the interface is not implemented by the recipient, the transfer will be reverted.
                 *
                 * The selector can be obtained in Solidity with `ISBTReceiver.onSBTReceived.selector`.
                 */
                function onSBTReceived(
                    address operator,
                    address from,
                    uint256 tokenId,
                    bytes calldata data
                ) external returns (bytes4);
            }
            // SPDX-License-Identifier: MIT
            // OpenZeppelin Contracts (last updated v4.8.0) (utils/Strings.sol)
            pragma solidity ^0.8.0;
            import "./math/MathUpgradeable.sol";
            /**
             * @dev String operations.
             */
            library StringsUpgradeable {
                bytes16 private constant _SYMBOLS = "0123456789abcdef";
                uint8 private constant _ADDRESS_LENGTH = 20;
                /**
                 * @dev Converts a `uint256` to its ASCII `string` decimal representation.
                 */
                function toString(uint256 value) internal pure returns (string memory) {
                    unchecked {
                        uint256 length = MathUpgradeable.log10(value) + 1;
                        string memory buffer = new string(length);
                        uint256 ptr;
                        /// @solidity memory-safe-assembly
                        assembly {
                            ptr := add(buffer, add(32, length))
                        }
                        while (true) {
                            ptr--;
                            /// @solidity memory-safe-assembly
                            assembly {
                                mstore8(ptr, byte(mod(value, 10), _SYMBOLS))
                            }
                            value /= 10;
                            if (value == 0) break;
                        }
                        return buffer;
                    }
                }
                /**
                 * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
                 */
                function toHexString(uint256 value) internal pure returns (string memory) {
                    unchecked {
                        return toHexString(value, MathUpgradeable.log256(value) + 1);
                    }
                }
                /**
                 * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
                 */
                function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
                    bytes memory buffer = new bytes(2 * length + 2);
                    buffer[0] = "0";
                    buffer[1] = "x";
                    for (uint256 i = 2 * length + 1; i > 1; --i) {
                        buffer[i] = _SYMBOLS[value & 0xf];
                        value >>= 4;
                    }
                    require(value == 0, "Strings: hex length insufficient");
                    return string(buffer);
                }
                /**
                 * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation.
                 */
                function toHexString(address addr) internal pure returns (string memory) {
                    return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);
                }
            }
            // SPDX-License-Identifier: MIT
            // OpenZeppelin Contracts v4.4.1 (utils/Counters.sol)
            pragma solidity ^0.8.0;
            /**
             * @title Counters
             * @author Matt Condon (@shrugs)
             * @dev Provides counters that can only be incremented, decremented or reset. This can be used e.g. to track the number
             * of elements in a mapping, issuing ERC721 ids, or counting request ids.
             *
             * Include with `using Counters for Counters.Counter;`
             */
            library CountersUpgradeable {
                struct Counter {
                    // This variable should never be directly accessed by users of the library: interactions must be restricted to
                    // the library's function. As of Solidity v0.5.2, this cannot be enforced, though there is a proposal to add
                    // this feature: see https://github.com/ethereum/solidity/issues/4637
                    uint256 _value; // default: 0
                }
                function current(Counter storage counter) internal view returns (uint256) {
                    return counter._value;
                }
                function increment(Counter storage counter) internal {
                    unchecked {
                        counter._value += 1;
                    }
                }
                function decrement(Counter storage counter) internal {
                    uint256 value = counter._value;
                    require(value > 0, "Counter: decrement overflow");
                    unchecked {
                        counter._value = value - 1;
                    }
                }
                function reset(Counter storage counter) internal {
                    counter._value = 0;
                }
            }
            // SPDX-License-Identifier: MIT
            // OpenZeppelin Contracts (last updated v4.7.0) (access/Ownable.sol)
            pragma solidity ^0.8.0;
            import "../utils/ContextUpgradeable.sol";
            import "../proxy/utils/Initializable.sol";
            /**
             * @dev Contract module which provides a basic access control mechanism, where
             * there is an account (an owner) that can be granted exclusive access to
             * specific functions.
             *
             * By default, the owner account will be the one that deploys the contract. This
             * can later be changed with {transferOwnership}.
             *
             * This module is used through inheritance. It will make available the modifier
             * `onlyOwner`, which can be applied to your functions to restrict their use to
             * the owner.
             */
            abstract contract OwnableUpgradeable is Initializable, ContextUpgradeable {
                address private _owner;
                event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
                /**
                 * @dev Initializes the contract setting the deployer as the initial owner.
                 */
                function __Ownable_init() internal onlyInitializing {
                    __Ownable_init_unchained();
                }
                function __Ownable_init_unchained() internal onlyInitializing {
                    _transferOwnership(_msgSender());
                }
                /**
                 * @dev Throws if called by any account other than the owner.
                 */
                modifier onlyOwner() {
                    _checkOwner();
                    _;
                }
                /**
                 * @dev Returns the address of the current owner.
                 */
                function owner() public view virtual returns (address) {
                    return _owner;
                }
                /**
                 * @dev Throws if the sender is not the owner.
                 */
                function _checkOwner() internal view virtual {
                    require(owner() == _msgSender(), "Ownable: caller is not the owner");
                }
                /**
                 * @dev Leaves the contract without owner. It will not be possible to call
                 * `onlyOwner` functions anymore. Can only be called by the current owner.
                 *
                 * NOTE: Renouncing ownership will leave the contract without an owner,
                 * thereby removing any functionality that is only available to the owner.
                 */
                function renounceOwnership() public virtual onlyOwner {
                    _transferOwnership(address(0));
                }
                /**
                 * @dev Transfers ownership of the contract to a new account (`newOwner`).
                 * Can only be called by the current owner.
                 */
                function transferOwnership(address newOwner) public virtual onlyOwner {
                    require(newOwner != address(0), "Ownable: new owner is the zero address");
                    _transferOwnership(newOwner);
                }
                /**
                 * @dev Transfers ownership of the contract to a new account (`newOwner`).
                 * Internal function without access restriction.
                 */
                function _transferOwnership(address newOwner) internal virtual {
                    address oldOwner = _owner;
                    _owner = newOwner;
                    emit OwnershipTransferred(oldOwner, newOwner);
                }
                /**
                 * @dev This empty reserved space is put in place to allow future versions to add new
                 * variables without shifting down storage in the inheritance chain.
                 * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
                 */
                uint256[49] private __gap;
            }
            // SPDX-License-Identifier: MIT
            // OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)
            pragma solidity ^0.8.0;
            import "./IERC165Upgradeable.sol";
            import "../../proxy/utils/Initializable.sol";
            /**
             * @dev Implementation of the {IERC165} interface.
             *
             * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
             * for the additional interface id that will be supported. For example:
             *
             * ```solidity
             * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
             *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
             * }
             * ```
             *
             * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
             */
            abstract contract ERC165Upgradeable is Initializable, IERC165Upgradeable {
                function __ERC165_init() internal onlyInitializing {
                }
                function __ERC165_init_unchained() internal onlyInitializing {
                }
                /**
                 * @dev See {IERC165-supportsInterface}.
                 */
                function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
                    return interfaceId == type(IERC165Upgradeable).interfaceId;
                }
                /**
                 * @dev This empty reserved space is put in place to allow future versions to add new
                 * variables without shifting down storage in the inheritance chain.
                 * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
                 */
                uint256[50] private __gap;
            }
            // SPDX-License-Identifier: MIT
            // OpenZeppelin Contracts (last updated v4.8.0) (utils/Address.sol)
            pragma solidity ^0.8.1;
            /**
             * @dev Collection of functions related to the address type
             */
            library AddressUpgradeable {
                /**
                 * @dev Returns true if `account` is a contract.
                 *
                 * [IMPORTANT]
                 * ====
                 * It is unsafe to assume that an address for which this function returns
                 * false is an externally-owned account (EOA) and not a contract.
                 *
                 * Among others, `isContract` will return false for the following
                 * types of addresses:
                 *
                 *  - an externally-owned account
                 *  - a contract in construction
                 *  - an address where a contract will be created
                 *  - an address where a contract lived, but was destroyed
                 * ====
                 *
                 * [IMPORTANT]
                 * ====
                 * You shouldn't rely on `isContract` to protect against flash loan attacks!
                 *
                 * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
                 * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
                 * constructor.
                 * ====
                 */
                function isContract(address account) internal view returns (bool) {
                    // This method relies on extcodesize/address.code.length, which returns 0
                    // for contracts in construction, since the code is only stored at the end
                    // of the constructor execution.
                    return account.code.length > 0;
                }
                /**
                 * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
                 * `recipient`, forwarding all available gas and reverting on errors.
                 *
                 * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
                 * of certain opcodes, possibly making contracts go over the 2300 gas limit
                 * imposed by `transfer`, making them unable to receive funds via
                 * `transfer`. {sendValue} removes this limitation.
                 *
                 * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
                 *
                 * IMPORTANT: because control is transferred to `recipient`, care must be
                 * taken to not create reentrancy vulnerabilities. Consider using
                 * {ReentrancyGuard} or the
                 * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
                 */
                function sendValue(address payable recipient, uint256 amount) internal {
                    require(address(this).balance >= amount, "Address: insufficient balance");
                    (bool success, ) = recipient.call{value: amount}("");
                    require(success, "Address: unable to send value, recipient may have reverted");
                }
                /**
                 * @dev Performs a Solidity function call using a low level `call`. A
                 * plain `call` is an unsafe replacement for a function call: use this
                 * function instead.
                 *
                 * If `target` reverts with a revert reason, it is bubbled up by this
                 * function (like regular Solidity function calls).
                 *
                 * Returns the raw returned data. To convert to the expected return value,
                 * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
                 *
                 * Requirements:
                 *
                 * - `target` must be a contract.
                 * - calling `target` with `data` must not revert.
                 *
                 * _Available since v3.1._
                 */
                function functionCall(address target, bytes memory data) internal returns (bytes memory) {
                    return functionCallWithValue(target, data, 0, "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");
                    (bool success, bytes memory returndata) = target.call{value: value}(data);
                    return verifyCallResultFromTarget(target, success, returndata, errorMessage);
                }
                /**
                 * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                 * but performing a static call.
                 *
                 * _Available since v3.3._
                 */
                function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
                    return functionStaticCall(target, data, "Address: low-level static call failed");
                }
                /**
                 * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
                 * but performing a static call.
                 *
                 * _Available since v3.3._
                 */
                function functionStaticCall(
                    address target,
                    bytes memory data,
                    string memory errorMessage
                ) internal view returns (bytes memory) {
                    (bool success, bytes memory returndata) = target.staticcall(data);
                    return verifyCallResultFromTarget(target, success, returndata, errorMessage);
                }
                /**
                 * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
                 * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
                 *
                 * _Available since v4.8._
                 */
                function verifyCallResultFromTarget(
                    address target,
                    bool success,
                    bytes memory returndata,
                    string memory errorMessage
                ) internal view returns (bytes memory) {
                    if (success) {
                        if (returndata.length == 0) {
                            // only check isContract if the call was successful and the return data is empty
                            // otherwise we already know that it was a contract
                            require(isContract(target), "Address: call to non-contract");
                        }
                        return returndata;
                    } else {
                        _revert(returndata, errorMessage);
                    }
                }
                /**
                 * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
                 * revert reason or using the provided one.
                 *
                 * _Available since v4.3._
                 */
                function verifyCallResult(
                    bool success,
                    bytes memory returndata,
                    string memory errorMessage
                ) internal pure returns (bytes memory) {
                    if (success) {
                        return returndata;
                    } else {
                        _revert(returndata, errorMessage);
                    }
                }
                function _revert(bytes memory returndata, string memory errorMessage) private pure {
                    // Look for revert reason and bubble it up if present
                    if (returndata.length > 0) {
                        // The easiest way to bubble the revert reason is using memory via assembly
                        /// @solidity memory-safe-assembly
                        assembly {
                            let returndata_size := mload(returndata)
                            revert(add(32, returndata), returndata_size)
                        }
                    } else {
                        revert(errorMessage);
                    }
                }
            }
            // SPDX-License-Identifier: MIT
            // OpenZeppelin Contracts (last updated v4.8.0) (utils/structs/EnumerableSet.sol)
            // This file was procedurally generated from scripts/generate/templates/EnumerableSet.js.
            pragma solidity ^0.8.0;
            /**
             * @dev Library for managing
             * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
             * types.
             *
             * Sets have the following properties:
             *
             * - Elements are added, removed, and checked for existence in constant time
             * (O(1)).
             * - Elements are enumerated in O(n). No guarantees are made on the ordering.
             *
             * ```
             * contract Example {
             *     // Add the library methods
             *     using EnumerableSet for EnumerableSet.AddressSet;
             *
             *     // Declare a set state variable
             *     EnumerableSet.AddressSet private mySet;
             * }
             * ```
             *
             * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)
             * and `uint256` (`UintSet`) are supported.
             *
             * [WARNING]
             * ====
             * Trying to delete such a structure from storage will likely result in data corruption, rendering the structure
             * unusable.
             * See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info.
             *
             * In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an
             * array of EnumerableSet.
             * ====
             */
            library EnumerableSetUpgradeable {
                // To implement this library for multiple types with as little code
                // repetition as possible, we write it in terms of a generic Set type with
                // bytes32 values.
                // The Set implementation uses private functions, and user-facing
                // implementations (such as AddressSet) are just wrappers around the
                // underlying Set.
                // This means that we can only create new EnumerableSets for types that fit
                // in bytes32.
                struct Set {
                    // Storage of set values
                    bytes32[] _values;
                    // Position of the value in the `values` array, plus 1 because index 0
                    // means a value is not in the set.
                    mapping(bytes32 => uint256) _indexes;
                }
                /**
                 * @dev Add a value to a set. O(1).
                 *
                 * Returns true if the value was added to the set, that is if it was not
                 * already present.
                 */
                function _add(Set storage set, bytes32 value) private returns (bool) {
                    if (!_contains(set, value)) {
                        set._values.push(value);
                        // The value is stored at length-1, but we add 1 to all indexes
                        // and use 0 as a sentinel value
                        set._indexes[value] = set._values.length;
                        return true;
                    } else {
                        return false;
                    }
                }
                /**
                 * @dev Removes a value from a set. O(1).
                 *
                 * Returns true if the value was removed from the set, that is if it was
                 * present.
                 */
                function _remove(Set storage set, bytes32 value) private returns (bool) {
                    // We read and store the value's index to prevent multiple reads from the same storage slot
                    uint256 valueIndex = set._indexes[value];
                    if (valueIndex != 0) {
                        // Equivalent to contains(set, value)
                        // To delete an element from the _values array in O(1), we swap the element to delete with the last one in
                        // the array, and then remove the last element (sometimes called as 'swap and pop').
                        // This modifies the order of the array, as noted in {at}.
                        uint256 toDeleteIndex = valueIndex - 1;
                        uint256 lastIndex = set._values.length - 1;
                        if (lastIndex != toDeleteIndex) {
                            bytes32 lastValue = set._values[lastIndex];
                            // Move the last value to the index where the value to delete is
                            set._values[toDeleteIndex] = lastValue;
                            // Update the index for the moved value
                            set._indexes[lastValue] = valueIndex; // Replace lastValue's index to valueIndex
                        }
                        // Delete the slot where the moved value was stored
                        set._values.pop();
                        // Delete the index for the deleted slot
                        delete set._indexes[value];
                        return true;
                    } else {
                        return false;
                    }
                }
                /**
                 * @dev Returns true if the value is in the set. O(1).
                 */
                function _contains(Set storage set, bytes32 value) private view returns (bool) {
                    return set._indexes[value] != 0;
                }
                /**
                 * @dev Returns the number of values on the set. O(1).
                 */
                function _length(Set storage set) private view returns (uint256) {
                    return set._values.length;
                }
                /**
                 * @dev Returns the value stored at position `index` in the set. O(1).
                 *
                 * Note that there are no guarantees on the ordering of values inside the
                 * array, and it may change when more values are added or removed.
                 *
                 * Requirements:
                 *
                 * - `index` must be strictly less than {length}.
                 */
                function _at(Set storage set, uint256 index) private view returns (bytes32) {
                    return set._values[index];
                }
                /**
                 * @dev Return the entire set in an array
                 *
                 * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
                 * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
                 * this function has an unbounded cost, and using it as part of a state-changing function may render the function
                 * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
                 */
                function _values(Set storage set) private view returns (bytes32[] memory) {
                    return set._values;
                }
                // Bytes32Set
                struct Bytes32Set {
                    Set _inner;
                }
                /**
                 * @dev Add a value to a set. O(1).
                 *
                 * Returns true if the value was added to the set, that is if it was not
                 * already present.
                 */
                function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
                    return _add(set._inner, value);
                }
                /**
                 * @dev Removes a value from a set. O(1).
                 *
                 * Returns true if the value was removed from the set, that is if it was
                 * present.
                 */
                function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {
                    return _remove(set._inner, value);
                }
                /**
                 * @dev Returns true if the value is in the set. O(1).
                 */
                function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
                    return _contains(set._inner, value);
                }
                /**
                 * @dev Returns the number of values in the set. O(1).
                 */
                function length(Bytes32Set storage set) internal view returns (uint256) {
                    return _length(set._inner);
                }
                /**
                 * @dev Returns the value stored at position `index` in the set. O(1).
                 *
                 * Note that there are no guarantees on the ordering of values inside the
                 * array, and it may change when more values are added or removed.
                 *
                 * Requirements:
                 *
                 * - `index` must be strictly less than {length}.
                 */
                function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
                    return _at(set._inner, index);
                }
                /**
                 * @dev Return the entire set in an array
                 *
                 * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
                 * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
                 * this function has an unbounded cost, and using it as part of a state-changing function may render the function
                 * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
                 */
                function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {
                    bytes32[] memory store = _values(set._inner);
                    bytes32[] memory result;
                    /// @solidity memory-safe-assembly
                    assembly {
                        result := store
                    }
                    return result;
                }
                // AddressSet
                struct AddressSet {
                    Set _inner;
                }
                /**
                 * @dev Add a value to a set. O(1).
                 *
                 * Returns true if the value was added to the set, that is if it was not
                 * already present.
                 */
                function add(AddressSet storage set, address value) internal returns (bool) {
                    return _add(set._inner, bytes32(uint256(uint160(value))));
                }
                /**
                 * @dev Removes a value from a set. O(1).
                 *
                 * Returns true if the value was removed from the set, that is if it was
                 * present.
                 */
                function remove(AddressSet storage set, address value) internal returns (bool) {
                    return _remove(set._inner, bytes32(uint256(uint160(value))));
                }
                /**
                 * @dev Returns true if the value is in the set. O(1).
                 */
                function contains(AddressSet storage set, address value) internal view returns (bool) {
                    return _contains(set._inner, bytes32(uint256(uint160(value))));
                }
                /**
                 * @dev Returns the number of values in the set. O(1).
                 */
                function length(AddressSet storage set) internal view returns (uint256) {
                    return _length(set._inner);
                }
                /**
                 * @dev Returns the value stored at position `index` in the set. O(1).
                 *
                 * Note that there are no guarantees on the ordering of values inside the
                 * array, and it may change when more values are added or removed.
                 *
                 * Requirements:
                 *
                 * - `index` must be strictly less than {length}.
                 */
                function at(AddressSet storage set, uint256 index) internal view returns (address) {
                    return address(uint160(uint256(_at(set._inner, index))));
                }
                /**
                 * @dev Return the entire set in an array
                 *
                 * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
                 * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
                 * this function has an unbounded cost, and using it as part of a state-changing function may render the function
                 * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
                 */
                function values(AddressSet storage set) internal view returns (address[] memory) {
                    bytes32[] memory store = _values(set._inner);
                    address[] memory result;
                    /// @solidity memory-safe-assembly
                    assembly {
                        result := store
                    }
                    return result;
                }
                // UintSet
                struct UintSet {
                    Set _inner;
                }
                /**
                 * @dev Add a value to a set. O(1).
                 *
                 * Returns true if the value was added to the set, that is if it was not
                 * already present.
                 */
                function add(UintSet storage set, uint256 value) internal returns (bool) {
                    return _add(set._inner, bytes32(value));
                }
                /**
                 * @dev Removes a value from a set. O(1).
                 *
                 * Returns true if the value was removed from the set, that is if it was
                 * present.
                 */
                function remove(UintSet storage set, uint256 value) internal returns (bool) {
                    return _remove(set._inner, bytes32(value));
                }
                /**
                 * @dev Returns true if the value is in the set. O(1).
                 */
                function contains(UintSet storage set, uint256 value) internal view returns (bool) {
                    return _contains(set._inner, bytes32(value));
                }
                /**
                 * @dev Returns the number of values in the set. O(1).
                 */
                function length(UintSet storage set) internal view returns (uint256) {
                    return _length(set._inner);
                }
                /**
                 * @dev Returns the value stored at position `index` in the set. O(1).
                 *
                 * Note that there are no guarantees on the ordering of values inside the
                 * array, and it may change when more values are added or removed.
                 *
                 * Requirements:
                 *
                 * - `index` must be strictly less than {length}.
                 */
                function at(UintSet storage set, uint256 index) internal view returns (uint256) {
                    return uint256(_at(set._inner, index));
                }
                /**
                 * @dev Return the entire set in an array
                 *
                 * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
                 * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
                 * this function has an unbounded cost, and using it as part of a state-changing function may render the function
                 * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
                 */
                function values(UintSet storage set) internal view returns (uint256[] memory) {
                    bytes32[] memory store = _values(set._inner);
                    uint256[] memory result;
                    /// @solidity memory-safe-assembly
                    assembly {
                        result := store
                    }
                    return result;
                }
            }
            // SPDX-License-Identifier: MIT
            // OpenZeppelin Contracts (last updated v4.8.0) (utils/cryptography/SignatureChecker.sol)
            pragma solidity ^0.8.0;
            import "./ECDSAUpgradeable.sol";
            import "../AddressUpgradeable.sol";
            import "../../interfaces/IERC1271Upgradeable.sol";
            /**
             * @dev Signature verification helper that can be used instead of `ECDSA.recover` to seamlessly support both ECDSA
             * signatures from externally owned accounts (EOAs) as well as ERC1271 signatures from smart contract wallets like
             * Argent and Gnosis Safe.
             *
             * _Available since v4.1._
             */
            library SignatureCheckerUpgradeable {
                /**
                 * @dev Checks if a signature is valid for a given signer and data hash. If the signer is a smart contract, the
                 * signature is validated against that smart contract using ERC1271, otherwise it's validated using `ECDSA.recover`.
                 *
                 * NOTE: Unlike ECDSA signatures, contract signatures are revocable, and the outcome of this function can thus
                 * change through time. It could return true at block N and false at block N+1 (or the opposite).
                 */
                function isValidSignatureNow(
                    address signer,
                    bytes32 hash,
                    bytes memory signature
                ) internal view returns (bool) {
                    (address recovered, ECDSAUpgradeable.RecoverError error) = ECDSAUpgradeable.tryRecover(hash, signature);
                    if (error == ECDSAUpgradeable.RecoverError.NoError && recovered == signer) {
                        return true;
                    }
                    (bool success, bytes memory result) = signer.staticcall(
                        abi.encodeWithSelector(IERC1271Upgradeable.isValidSignature.selector, hash, signature)
                    );
                    return (success &&
                        result.length == 32 &&
                        abi.decode(result, (bytes32)) == bytes32(IERC1271Upgradeable.isValidSignature.selector));
                }
            }
            // SPDX-License-Identifier: MIT
            // OpenZeppelin Contracts (last updated v4.8.0) (utils/cryptography/draft-EIP712.sol)
            pragma solidity ^0.8.0;
            // EIP-712 is Final as of 2022-08-11. This file is deprecated.
            import "./EIP712Upgradeable.sol";
            // SPDX-License-Identifier: MIT
            // OpenZeppelin Contracts (last updated v4.8.0) (proxy/utils/Initializable.sol)
            pragma solidity ^0.8.2;
            import "../../utils/AddressUpgradeable.sol";
            /**
             * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
             * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
             * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
             * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
             *
             * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be
             * reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in
             * case an upgrade adds a module that needs to be initialized.
             *
             * For example:
             *
             * [.hljs-theme-light.nopadding]
             * ```
             * contract MyToken is ERC20Upgradeable {
             *     function initialize() initializer public {
             *         __ERC20_init("MyToken", "MTK");
             *     }
             * }
             * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {
             *     function initializeV2() reinitializer(2) public {
             *         __ERC20Permit_init("MyToken");
             *     }
             * }
             * ```
             *
             * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
             * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
             *
             * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
             * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
             *
             * [CAUTION]
             * ====
             * Avoid leaving a contract uninitialized.
             *
             * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
             * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke
             * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:
             *
             * [.hljs-theme-light.nopadding]
             * ```
             * /// @custom:oz-upgrades-unsafe-allow constructor
             * constructor() {
             *     _disableInitializers();
             * }
             * ```
             * ====
             */
            abstract contract Initializable {
                /**
                 * @dev Indicates that the contract has been initialized.
                 * @custom:oz-retyped-from bool
                 */
                uint8 private _initialized;
                /**
                 * @dev Indicates that the contract is in the process of being initialized.
                 */
                bool private _initializing;
                /**
                 * @dev Triggered when the contract has been initialized or reinitialized.
                 */
                event Initialized(uint8 version);
                /**
                 * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
                 * `onlyInitializing` functions can be used to initialize parent contracts.
                 *
                 * Similar to `reinitializer(1)`, except that functions marked with `initializer` can be nested in the context of a
                 * constructor.
                 *
                 * Emits an {Initialized} event.
                 */
                modifier initializer() {
                    bool isTopLevelCall = !_initializing;
                    require(
                        (isTopLevelCall && _initialized < 1) || (!AddressUpgradeable.isContract(address(this)) && _initialized == 1),
                        "Initializable: contract is already initialized"
                    );
                    _initialized = 1;
                    if (isTopLevelCall) {
                        _initializing = true;
                    }
                    _;
                    if (isTopLevelCall) {
                        _initializing = false;
                        emit Initialized(1);
                    }
                }
                /**
                 * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
                 * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
                 * used to initialize parent contracts.
                 *
                 * A reinitializer may be used after the original initialization step. This is essential to configure modules that
                 * are added through upgrades and that require initialization.
                 *
                 * When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`
                 * cannot be nested. If one is invoked in the context of another, execution will revert.
                 *
                 * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
                 * a contract, executing them in the right order is up to the developer or operator.
                 *
                 * WARNING: setting the version to 255 will prevent any future reinitialization.
                 *
                 * Emits an {Initialized} event.
                 */
                modifier reinitializer(uint8 version) {
                    require(!_initializing && _initialized < version, "Initializable: contract is already initialized");
                    _initialized = version;
                    _initializing = true;
                    _;
                    _initializing = false;
                    emit Initialized(version);
                }
                /**
                 * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
                 * {initializer} and {reinitializer} modifiers, directly or indirectly.
                 */
                modifier onlyInitializing() {
                    require(_initializing, "Initializable: contract is not initializing");
                    _;
                }
                /**
                 * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
                 * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
                 * to any version. It is recommended to use this to lock implementation contracts that are designed to be called
                 * through proxies.
                 *
                 * Emits an {Initialized} event the first time it is successfully executed.
                 */
                function _disableInitializers() internal virtual {
                    require(!_initializing, "Initializable: contract is initializing");
                    if (_initialized < type(uint8).max) {
                        _initialized = type(uint8).max;
                        emit Initialized(type(uint8).max);
                    }
                }
                /**
                 * @dev Internal function that returns the initialized version. Returns `_initialized`
                 */
                function _getInitializedVersion() internal view returns (uint8) {
                    return _initialized;
                }
                /**
                 * @dev Internal function that returns the initialized version. Returns `_initializing`
                 */
                function _isInitializing() internal view returns (bool) {
                    return _initializing;
                }
            }
            // SPDX-License-Identifier: MIT
            // OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)
            pragma solidity ^0.8.0;
            /**
             * @dev Interface of the ERC165 standard, as defined in the
             * https://eips.ethereum.org/EIPS/eip-165[EIP].
             *
             * Implementers can declare support of contract interfaces, which can then be
             * queried by others ({ERC165Checker}).
             *
             * For an implementation, see {ERC165}.
             */
            interface 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
            // OpenZeppelin Contracts (last updated v4.8.0) (utils/math/Math.sol)
            pragma solidity ^0.8.0;
            /**
             * @dev Standard math utilities missing in the Solidity language.
             */
            library MathUpgradeable {
                enum Rounding {
                    Down, // Toward negative infinity
                    Up, // Toward infinity
                    Zero // Toward zero
                }
                /**
                 * @dev Returns the largest of two numbers.
                 */
                function max(uint256 a, uint256 b) internal pure returns (uint256) {
                    return a > b ? a : b;
                }
                /**
                 * @dev Returns the smallest of two numbers.
                 */
                function min(uint256 a, uint256 b) internal pure returns (uint256) {
                    return a < b ? a : b;
                }
                /**
                 * @dev Returns the average of two numbers. The result is rounded towards
                 * zero.
                 */
                function average(uint256 a, uint256 b) internal pure returns (uint256) {
                    // (a + b) / 2 can overflow.
                    return (a & b) + (a ^ b) / 2;
                }
                /**
                 * @dev Returns the ceiling of the division of two numbers.
                 *
                 * This differs from standard division with `/` in that it rounds up instead
                 * of rounding down.
                 */
                function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
                    // (a + b - 1) / b can overflow on addition, so we distribute.
                    return a == 0 ? 0 : (a - 1) / b + 1;
                }
                /**
                 * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
                 * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv)
                 * with further edits by Uniswap Labs also under MIT license.
                 */
                function mulDiv(
                    uint256 x,
                    uint256 y,
                    uint256 denominator
                ) internal pure returns (uint256 result) {
                    unchecked {
                        // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
                        // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
                        // variables such that product = prod1 * 2^256 + prod0.
                        uint256 prod0; // Least significant 256 bits of the product
                        uint256 prod1; // Most significant 256 bits of the product
                        assembly {
                            let mm := mulmod(x, y, not(0))
                            prod0 := mul(x, y)
                            prod1 := sub(sub(mm, prod0), lt(mm, prod0))
                        }
                        // Handle non-overflow cases, 256 by 256 division.
                        if (prod1 == 0) {
                            return prod0 / denominator;
                        }
                        // Make sure the result is less than 2^256. Also prevents denominator == 0.
                        require(denominator > prod1);
                        ///////////////////////////////////////////////
                        // 512 by 256 division.
                        ///////////////////////////////////////////////
                        // Make division exact by subtracting the remainder from [prod1 prod0].
                        uint256 remainder;
                        assembly {
                            // Compute remainder using mulmod.
                            remainder := mulmod(x, y, denominator)
                            // Subtract 256 bit number from 512 bit number.
                            prod1 := sub(prod1, gt(remainder, prod0))
                            prod0 := sub(prod0, remainder)
                        }
                        // Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1.
                        // See https://cs.stackexchange.com/q/138556/92363.
                        // Does not overflow because the denominator cannot be zero at this stage in the function.
                        uint256 twos = denominator & (~denominator + 1);
                        assembly {
                            // Divide denominator by twos.
                            denominator := div(denominator, twos)
                            // Divide [prod1 prod0] by twos.
                            prod0 := div(prod0, twos)
                            // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
                            twos := add(div(sub(0, twos), twos), 1)
                        }
                        // Shift in bits from prod1 into prod0.
                        prod0 |= prod1 * twos;
                        // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
                        // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
                        // four bits. That is, denominator * inv = 1 mod 2^4.
                        uint256 inverse = (3 * denominator) ^ 2;
                        // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works
                        // in modular arithmetic, doubling the correct bits in each step.
                        inverse *= 2 - denominator * inverse; // inverse mod 2^8
                        inverse *= 2 - denominator * inverse; // inverse mod 2^16
                        inverse *= 2 - denominator * inverse; // inverse mod 2^32
                        inverse *= 2 - denominator * inverse; // inverse mod 2^64
                        inverse *= 2 - denominator * inverse; // inverse mod 2^128
                        inverse *= 2 - denominator * inverse; // inverse mod 2^256
                        // Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
                        // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
                        // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
                        // is no longer required.
                        result = prod0 * inverse;
                        return result;
                    }
                }
                /**
                 * @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
                 */
                function mulDiv(
                    uint256 x,
                    uint256 y,
                    uint256 denominator,
                    Rounding rounding
                ) internal pure returns (uint256) {
                    uint256 result = mulDiv(x, y, denominator);
                    if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {
                        result += 1;
                    }
                    return result;
                }
                /**
                 * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down.
                 *
                 * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
                 */
                function sqrt(uint256 a) internal pure returns (uint256) {
                    if (a == 0) {
                        return 0;
                    }
                    // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
                    //
                    // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
                    // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
                    //
                    // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
                    // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
                    // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
                    //
                    // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
                    uint256 result = 1 << (log2(a) >> 1);
                    // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
                    // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
                    // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
                    // into the expected uint128 result.
                    unchecked {
                        result = (result + a / result) >> 1;
                        result = (result + a / result) >> 1;
                        result = (result + a / result) >> 1;
                        result = (result + a / result) >> 1;
                        result = (result + a / result) >> 1;
                        result = (result + a / result) >> 1;
                        result = (result + a / result) >> 1;
                        return min(result, a / result);
                    }
                }
                /**
                 * @notice Calculates sqrt(a), following the selected rounding direction.
                 */
                function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
                    unchecked {
                        uint256 result = sqrt(a);
                        return result + (rounding == Rounding.Up && result * result < a ? 1 : 0);
                    }
                }
                /**
                 * @dev Return the log in base 2, rounded down, of a positive value.
                 * Returns 0 if given 0.
                 */
                function log2(uint256 value) internal pure returns (uint256) {
                    uint256 result = 0;
                    unchecked {
                        if (value >> 128 > 0) {
                            value >>= 128;
                            result += 128;
                        }
                        if (value >> 64 > 0) {
                            value >>= 64;
                            result += 64;
                        }
                        if (value >> 32 > 0) {
                            value >>= 32;
                            result += 32;
                        }
                        if (value >> 16 > 0) {
                            value >>= 16;
                            result += 16;
                        }
                        if (value >> 8 > 0) {
                            value >>= 8;
                            result += 8;
                        }
                        if (value >> 4 > 0) {
                            value >>= 4;
                            result += 4;
                        }
                        if (value >> 2 > 0) {
                            value >>= 2;
                            result += 2;
                        }
                        if (value >> 1 > 0) {
                            result += 1;
                        }
                    }
                    return result;
                }
                /**
                 * @dev Return the log in base 2, following the selected rounding direction, of a positive value.
                 * Returns 0 if given 0.
                 */
                function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
                    unchecked {
                        uint256 result = log2(value);
                        return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0);
                    }
                }
                /**
                 * @dev Return the log in base 10, rounded down, of a positive value.
                 * Returns 0 if given 0.
                 */
                function log10(uint256 value) internal pure returns (uint256) {
                    uint256 result = 0;
                    unchecked {
                        if (value >= 10**64) {
                            value /= 10**64;
                            result += 64;
                        }
                        if (value >= 10**32) {
                            value /= 10**32;
                            result += 32;
                        }
                        if (value >= 10**16) {
                            value /= 10**16;
                            result += 16;
                        }
                        if (value >= 10**8) {
                            value /= 10**8;
                            result += 8;
                        }
                        if (value >= 10**4) {
                            value /= 10**4;
                            result += 4;
                        }
                        if (value >= 10**2) {
                            value /= 10**2;
                            result += 2;
                        }
                        if (value >= 10**1) {
                            result += 1;
                        }
                    }
                    return result;
                }
                /**
                 * @dev Return the log in base 10, following the selected rounding direction, of a positive value.
                 * Returns 0 if given 0.
                 */
                function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
                    unchecked {
                        uint256 result = log10(value);
                        return result + (rounding == Rounding.Up && 10**result < value ? 1 : 0);
                    }
                }
                /**
                 * @dev Return the log in base 256, rounded down, of a positive value.
                 * Returns 0 if given 0.
                 *
                 * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
                 */
                function log256(uint256 value) internal pure returns (uint256) {
                    uint256 result = 0;
                    unchecked {
                        if (value >> 128 > 0) {
                            value >>= 128;
                            result += 16;
                        }
                        if (value >> 64 > 0) {
                            value >>= 64;
                            result += 8;
                        }
                        if (value >> 32 > 0) {
                            value >>= 32;
                            result += 4;
                        }
                        if (value >> 16 > 0) {
                            value >>= 16;
                            result += 2;
                        }
                        if (value >> 8 > 0) {
                            result += 1;
                        }
                    }
                    return result;
                }
                /**
                 * @dev Return the log in base 10, following the selected rounding direction, of a positive value.
                 * Returns 0 if given 0.
                 */
                function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
                    unchecked {
                        uint256 result = log256(value);
                        return result + (rounding == Rounding.Up && 1 << (result * 8) < value ? 1 : 0);
                    }
                }
            }
            // SPDX-License-Identifier: MIT
            // OpenZeppelin Contracts v4.4.1 (utils/Context.sol)
            pragma solidity ^0.8.0;
            import "../proxy/utils/Initializable.sol";
            /**
             * @dev Provides information about the current execution context, including the
             * sender of the transaction and its data. While these are generally available
             * via msg.sender and msg.data, they should not be accessed in such a direct
             * manner, since when dealing with meta-transactions the account sending and
             * paying for execution may not be the actual sender (as far as an application
             * is concerned).
             *
             * This contract is only required for intermediate, library-like contracts.
             */
            abstract contract ContextUpgradeable is Initializable {
                function __Context_init() internal onlyInitializing {
                }
                function __Context_init_unchained() internal onlyInitializing {
                }
                function _msgSender() internal view virtual returns (address) {
                    return msg.sender;
                }
                function _msgData() internal view virtual returns (bytes calldata) {
                    return msg.data;
                }
                /**
                 * @dev This empty reserved space is put in place to allow future versions to add new
                 * variables without shifting down storage in the inheritance chain.
                 * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
                 */
                uint256[50] private __gap;
            }
            // SPDX-License-Identifier: MIT
            // OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)
            pragma solidity ^0.8.0;
            /**
             * @dev Interface of the ERC165 standard, as defined in the
             * https://eips.ethereum.org/EIPS/eip-165[EIP].
             *
             * Implementers can declare support of contract interfaces, which can then be
             * queried by others ({ERC165Checker}).
             *
             * For an implementation, see {ERC165}.
             */
            interface IERC165Upgradeable {
                /**
                 * @dev Returns true if this contract implements the interface defined by
                 * `interfaceId`. See the corresponding
                 * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
                 * to learn more about how these ids are created.
                 *
                 * This function call must use less than 30 000 gas.
                 */
                function supportsInterface(bytes4 interfaceId) external view returns (bool);
            }
            // SPDX-License-Identifier: MIT
            // OpenZeppelin Contracts v4.4.1 (interfaces/IERC1271.sol)
            pragma solidity ^0.8.0;
            /**
             * @dev Interface of the ERC1271 standard signature validation method for
             * contracts as defined in https://eips.ethereum.org/EIPS/eip-1271[ERC-1271].
             *
             * _Available since v4.1._
             */
            interface IERC1271Upgradeable {
                /**
                 * @dev Should return whether the signature provided is valid for the provided data
                 * @param hash      Hash of the data to be signed
                 * @param signature Signature byte array associated with _data
                 */
                function isValidSignature(bytes32 hash, bytes memory signature) external view returns (bytes4 magicValue);
            }
            // SPDX-License-Identifier: MIT
            // OpenZeppelin Contracts (last updated v4.8.0) (utils/cryptography/ECDSA.sol)
            pragma solidity ^0.8.0;
            import "../StringsUpgradeable.sol";
            /**
             * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
             *
             * These functions can be used to verify that a message was signed by the holder
             * of the private keys of a given address.
             */
            library ECDSAUpgradeable {
                enum RecoverError {
                    NoError,
                    InvalidSignature,
                    InvalidSignatureLength,
                    InvalidSignatureS,
                    InvalidSignatureV // Deprecated in v4.8
                }
                function _throwError(RecoverError error) private pure {
                    if (error == RecoverError.NoError) {
                        return; // no error: do nothing
                    } else if (error == RecoverError.InvalidSignature) {
                        revert("ECDSA: invalid signature");
                    } else if (error == RecoverError.InvalidSignatureLength) {
                        revert("ECDSA: invalid signature length");
                    } else if (error == RecoverError.InvalidSignatureS) {
                        revert("ECDSA: invalid signature 's' value");
                    }
                }
                /**
                 * @dev Returns the address that signed a hashed message (`hash`) with
                 * `signature` or error string. This address can then be used for verification purposes.
                 *
                 * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
                 * this function rejects them by requiring the `s` value to be in the lower
                 * half order, and the `v` value to be either 27 or 28.
                 *
                 * IMPORTANT: `hash` _must_ be the result of a hash operation for the
                 * verification to be secure: it is possible to craft signatures that
                 * recover to arbitrary addresses for non-hashed data. A safe way to ensure
                 * this is by receiving a hash of the original message (which may otherwise
                 * be too long), and then calling {toEthSignedMessageHash} on it.
                 *
                 * Documentation for signature generation:
                 * - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js]
                 * - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers]
                 *
                 * _Available since v4.3._
                 */
                function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError) {
                    if (signature.length == 65) {
                        bytes32 r;
                        bytes32 s;
                        uint8 v;
                        // ecrecover takes the signature parameters, and the only way to get them
                        // currently is to use assembly.
                        /// @solidity memory-safe-assembly
                        assembly {
                            r := mload(add(signature, 0x20))
                            s := mload(add(signature, 0x40))
                            v := byte(0, mload(add(signature, 0x60)))
                        }
                        return tryRecover(hash, v, r, s);
                    } else {
                        return (address(0), RecoverError.InvalidSignatureLength);
                    }
                }
                /**
                 * @dev Returns the address that signed a hashed message (`hash`) with
                 * `signature`. This address can then be used for verification purposes.
                 *
                 * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
                 * this function rejects them by requiring the `s` value to be in the lower
                 * half order, and the `v` value to be either 27 or 28.
                 *
                 * IMPORTANT: `hash` _must_ be the result of a hash operation for the
                 * verification to be secure: it is possible to craft signatures that
                 * recover to arbitrary addresses for non-hashed data. A safe way to ensure
                 * this is by receiving a hash of the original message (which may otherwise
                 * be too long), and then calling {toEthSignedMessageHash} on it.
                 */
                function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
                    (address recovered, RecoverError error) = tryRecover(hash, signature);
                    _throwError(error);
                    return recovered;
                }
                /**
                 * @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately.
                 *
                 * See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures]
                 *
                 * _Available since v4.3._
                 */
                function tryRecover(
                    bytes32 hash,
                    bytes32 r,
                    bytes32 vs
                ) internal pure returns (address, RecoverError) {
                    bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);
                    uint8 v = uint8((uint256(vs) >> 255) + 27);
                    return tryRecover(hash, v, r, s);
                }
                /**
                 * @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately.
                 *
                 * _Available since v4.2._
                 */
                function recover(
                    bytes32 hash,
                    bytes32 r,
                    bytes32 vs
                ) internal pure returns (address) {
                    (address recovered, RecoverError error) = tryRecover(hash, r, vs);
                    _throwError(error);
                    return recovered;
                }
                /**
                 * @dev Overload of {ECDSA-tryRecover} that receives the `v`,
                 * `r` and `s` signature fields separately.
                 *
                 * _Available since v4.3._
                 */
                function tryRecover(
                    bytes32 hash,
                    uint8 v,
                    bytes32 r,
                    bytes32 s
                ) internal pure returns (address, RecoverError) {
                    // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
                    // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
                    // the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most
                    // signatures from current libraries generate a unique signature with an s-value in the lower half order.
                    //
                    // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
                    // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
                    // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
                    // these malleable signatures as well.
                    if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
                        return (address(0), RecoverError.InvalidSignatureS);
                    }
                    // If the signature is valid (and not malleable), return the signer address
                    address signer = ecrecover(hash, v, r, s);
                    if (signer == address(0)) {
                        return (address(0), RecoverError.InvalidSignature);
                    }
                    return (signer, RecoverError.NoError);
                }
                /**
                 * @dev Overload of {ECDSA-recover} that receives the `v`,
                 * `r` and `s` signature fields separately.
                 */
                function recover(
                    bytes32 hash,
                    uint8 v,
                    bytes32 r,
                    bytes32 s
                ) internal pure returns (address) {
                    (address recovered, RecoverError error) = tryRecover(hash, v, r, s);
                    _throwError(error);
                    return recovered;
                }
                /**
                 * @dev Returns an Ethereum Signed Message, created from a `hash`. This
                 * produces hash corresponding to the one signed with the
                 * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
                 * JSON-RPC method as part of EIP-191.
                 *
                 * See {recover}.
                 */
                function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32) {
                    // 32 is the length in bytes of hash,
                    // enforced by the type signature above
                    return keccak256(abi.encodePacked("\\x19Ethereum Signed Message:\
            32", hash));
                }
                /**
                 * @dev Returns an Ethereum Signed Message, created from `s`. This
                 * produces hash corresponding to the one signed with the
                 * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
                 * JSON-RPC method as part of EIP-191.
                 *
                 * See {recover}.
                 */
                function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32) {
                    return keccak256(abi.encodePacked("\\x19Ethereum Signed Message:\
            ", StringsUpgradeable.toString(s.length), s));
                }
                /**
                 * @dev Returns an Ethereum Signed Typed Data, created from a
                 * `domainSeparator` and a `structHash`. This produces hash corresponding
                 * to the one signed with the
                 * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`]
                 * JSON-RPC method as part of EIP-712.
                 *
                 * See {recover}.
                 */
                function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32) {
                    return keccak256(abi.encodePacked("\\x19\\x01", domainSeparator, structHash));
                }
            }
            // SPDX-License-Identifier: MIT
            // OpenZeppelin Contracts (last updated v4.8.0) (utils/cryptography/EIP712.sol)
            pragma solidity ^0.8.0;
            import "./ECDSAUpgradeable.sol";
            import "../../proxy/utils/Initializable.sol";
            /**
             * @dev https://eips.ethereum.org/EIPS/eip-712[EIP 712] is a standard for hashing and signing of typed structured data.
             *
             * The encoding specified in the EIP is very generic, and such a generic implementation in Solidity is not feasible,
             * thus this contract does not implement the encoding itself. Protocols need to implement the type-specific encoding
             * they need in their contracts using a combination of `abi.encode` and `keccak256`.
             *
             * This contract implements the EIP 712 domain separator ({_domainSeparatorV4}) that is used as part of the encoding
             * scheme, and the final step of the encoding to obtain the message digest that is then signed via ECDSA
             * ({_hashTypedDataV4}).
             *
             * The implementation of the domain separator was designed to be as efficient as possible while still properly updating
             * the chain id to protect against replay attacks on an eventual fork of the chain.
             *
             * NOTE: This contract implements the version of the encoding known as "v4", as implemented by the JSON RPC method
             * https://docs.metamask.io/guide/signing-data.html[`eth_signTypedDataV4` in MetaMask].
             *
             * _Available since v3.4._
             *
             * @custom:storage-size 52
             */
            abstract contract EIP712Upgradeable is Initializable {
                /* solhint-disable var-name-mixedcase */
                bytes32 private _HASHED_NAME;
                bytes32 private _HASHED_VERSION;
                bytes32 private constant _TYPE_HASH = keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)");
                /* solhint-enable var-name-mixedcase */
                /**
                 * @dev Initializes the domain separator and parameter caches.
                 *
                 * The meaning of `name` and `version` is specified in
                 * https://eips.ethereum.org/EIPS/eip-712#definition-of-domainseparator[EIP 712]:
                 *
                 * - `name`: the user readable name of the signing domain, i.e. the name of the DApp or the protocol.
                 * - `version`: the current major version of the signing domain.
                 *
                 * NOTE: These parameters cannot be changed except through a xref:learn::upgrading-smart-contracts.adoc[smart
                 * contract upgrade].
                 */
                function __EIP712_init(string memory name, string memory version) internal onlyInitializing {
                    __EIP712_init_unchained(name, version);
                }
                function __EIP712_init_unchained(string memory name, string memory version) internal onlyInitializing {
                    bytes32 hashedName = keccak256(bytes(name));
                    bytes32 hashedVersion = keccak256(bytes(version));
                    _HASHED_NAME = hashedName;
                    _HASHED_VERSION = hashedVersion;
                }
                /**
                 * @dev Returns the domain separator for the current chain.
                 */
                function _domainSeparatorV4() internal view returns (bytes32) {
                    return _buildDomainSeparator(_TYPE_HASH, _EIP712NameHash(), _EIP712VersionHash());
                }
                function _buildDomainSeparator(
                    bytes32 typeHash,
                    bytes32 nameHash,
                    bytes32 versionHash
                ) private view returns (bytes32) {
                    return keccak256(abi.encode(typeHash, nameHash, versionHash, block.chainid, address(this)));
                }
                /**
                 * @dev Given an already https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstruct[hashed struct], this
                 * function returns the hash of the fully encoded EIP712 message for this domain.
                 *
                 * This hash can be used together with {ECDSA-recover} to obtain the signer of a message. For example:
                 *
                 * ```solidity
                 * bytes32 digest = _hashTypedDataV4(keccak256(abi.encode(
                 *     keccak256("Mail(address to,string contents)"),
                 *     mailTo,
                 *     keccak256(bytes(mailContents))
                 * )));
                 * address signer = ECDSA.recover(digest, signature);
                 * ```
                 */
                function _hashTypedDataV4(bytes32 structHash) internal view virtual returns (bytes32) {
                    return ECDSAUpgradeable.toTypedDataHash(_domainSeparatorV4(), structHash);
                }
                /**
                 * @dev The hash of the name parameter for the EIP712 domain.
                 *
                 * NOTE: This function reads from storage by default, but can be redefined to return a constant value if gas costs
                 * are a concern.
                 */
                function _EIP712NameHash() internal virtual view returns (bytes32) {
                    return _HASHED_NAME;
                }
                /**
                 * @dev The hash of the version parameter for the EIP712 domain.
                 *
                 * NOTE: This function reads from storage by default, but can be redefined to return a constant value if gas costs
                 * are a concern.
                 */
                function _EIP712VersionHash() internal virtual view returns (bytes32) {
                    return _HASHED_VERSION;
                }
                /**
                 * @dev This empty reserved space is put in place to allow future versions to add new
                 * variables without shifting down storage in the inheritance chain.
                 * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
                 */
                uint256[50] private __gap;
            }