ETH Price: $2,502.65 (+0.61%)

Transaction Decoder

Block:
22111205 at Mar-23-2025 05:52:11 PM +UTC
Transaction Fee:
0.000173840111657677 ETH $0.44
Gas Used:
130,523 Gas / 1.331873399 Gwei

Emitted Events:

52 TransparentUpgradeableProxy.0xa803098eab41616e8c09907603003b82c457356a707199ad558994e618783053( 0xa803098eab41616e8c09907603003b82c457356a707199ad558994e618783053, 00000000000000000000000000000000000000000003b0168027729ebd31ebd6 )
53 AdminUpgradeabilityProxy.0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef( 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef, 0x00000000000000000000000056968bb1168b0e9314dca1eb3d1e7aaf0d32263e, 0x000000000000000000000000dc718fdd9809b1d3cc4728bd733845104d70619a, 0000000000000000000000000000000000000000000000092381087debdf32d0 )
54 TransparentUpgradeableProxy.0x47cee97cb7acd717b3c0aa1435d004cd5b3c8c57d70dbceb4e4458bbd60e39d4( 0x47cee97cb7acd717b3c0aa1435d004cd5b3c8c57d70dbceb4e4458bbd60e39d4, 0x000000000000000000000000dc718fdd9809b1d3cc4728bd733845104d70619a, 0000000000000000000000000000000000000000000000092381087debdf32d0 )

Account State Difference:

  Address   Before After State Difference Code
0x56968bB1...f0d32263e
0xCC4304A3...f52e44EFe
0xDC718FDd...04d70619A
0.000419353807130558 Eth
Nonce: 143
0.000245513695472881 Eth
Nonce: 144
0.000173840111657677
(Fee Recipient: 0xe68...127)
3,376.373421839360262889 Eth3,376.373530066070948634 Eth0.000108226710685745

Execution Trace

TransparentUpgradeableProxy.CALL( )
  • LockedStaking.DELEGATECALL( )
    • AdminUpgradeabilityProxy.a9059cbb( )
      • SwapToken.transfer( recipient=0xDC718FDd9809b1D3cC4728Bd733845104d70619A, amount=168579032063630324432 ) => ( True )
        File 1 of 4: TransparentUpgradeableProxy
        // SPDX-License-Identifier: MIT
        pragma solidity ^0.8.0;
        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 "../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 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 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 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;
            }
        }
        // SPDX-License-Identifier: MIT
        pragma solidity ^0.8.0;
        import "../ERC1967/ERC1967Upgrade.sol";
        /**
         * @dev Base contract for building openzeppelin-upgrades compatible implementations for the {ERC1967Proxy}. It includes
         * publicly available upgrade functions that are called by the plugin and by the secure upgrade mechanism to verify
         * continuation of the upgradability.
         *
         * The {_authorizeUpgrade} function MUST be overridden to include access restriction to the upgrade mechanism.
         *
         * _Available since v4.1._
         */
        abstract contract UUPSUpgradeable is ERC1967Upgrade {
            function upgradeTo(address newImplementation) external virtual {
                _authorizeUpgrade(newImplementation);
                _upgradeToAndCallSecure(newImplementation, bytes(""), false);
            }
            function upgradeToAndCall(address newImplementation, bytes memory data) external payable virtual {
                _authorizeUpgrade(newImplementation);
                _upgradeToAndCallSecure(newImplementation, data, true);
            }
            function _authorizeUpgrade(address newImplementation) internal virtual;
        }
        // SPDX-License-Identifier: MIT
        pragma solidity ^0.8.2;
        import "@openzeppelin/contracts/proxy/utils/UUPSUpgradeable.sol";
        abstract contract Proxiable is UUPSUpgradeable {
            function _authorizeUpgrade(address newImplementation) internal override {
                _beforeUpgrade(newImplementation);
            }
            function _beforeUpgrade(address newImplementation) internal virtual;
        }
        contract ChildOfProxiable is Proxiable {
            function _beforeUpgrade(address newImplementation) internal virtual override {}
        }
        

        File 2 of 4: AdminUpgradeabilityProxy
        // File: @openzeppelin/upgrades/contracts/upgradeability/Proxy.sol
        
        pragma solidity ^0.5.0;
        
        /**
         * @title Proxy
         * @dev Implements delegation of calls to other contracts, with proper
         * forwarding of return values and bubbling of failures.
         * It defines a fallback function that delegates all calls to the address
         * returned by the abstract _implementation() internal function.
         */
        contract Proxy {
          /**
           * @dev Fallback function.
           * Implemented entirely in `_fallback`.
           */
          function () payable external {
            _fallback();
          }
        
          /**
           * @return The Address of the implementation.
           */
          function _implementation() internal view returns (address);
        
          /**
           * @dev Delegates execution to an implementation contract.
           * This is a low level function that doesn't return to its internal call site.
           * It will return to the external caller whatever the implementation returns.
           * @param implementation Address to delegate.
           */
          function _delegate(address implementation) internal {
            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 Function that is run as the first thing in the fallback function.
           * Can be redefined in derived contracts to add functionality.
           * Redefinitions must call super._willFallback().
           */
          function _willFallback() internal {
          }
        
          /**
           * @dev fallback implementation.
           * Extracted to enable manual triggering.
           */
          function _fallback() internal {
            _willFallback();
            _delegate(_implementation());
          }
        }
        
        // File: @openzeppelin/upgrades/contracts/utils/Address.sol
        
        pragma solidity ^0.5.0;
        
        /**
         * Utility library of inline functions on addresses
         *
         * Source https://raw.githubusercontent.com/OpenZeppelin/openzeppelin-solidity/v2.1.3/contracts/utils/Address.sol
         * This contract is copied here and renamed from the original to avoid clashes in the compiled artifacts
         * when the user imports a zos-lib contract (that transitively causes this contract to be compiled and added to the
         * build/artifacts folder) as well as the vanilla Address implementation from an openzeppelin version.
         */
        library OpenZeppelinUpgradesAddress {
            /**
             * Returns whether the target address is a contract
             * @dev This function will return false if invoked during the constructor of a contract,
             * as the code is not actually created until after the constructor finishes.
             * @param account address of the account to check
             * @return whether the target address is a contract
             */
            function isContract(address account) internal view returns (bool) {
                uint256 size;
                // XXX Currently there is no better way to check if there is a contract in an address
                // than to check the size of the code at that address.
                // See https://ethereum.stackexchange.com/a/14016/36603
                // for more details about how this works.
                // TODO Check this again before the Serenity release, because all addresses will be
                // contracts then.
                // solhint-disable-next-line no-inline-assembly
                assembly { size := extcodesize(account) }
                return size > 0;
            }
        }
        
        // File: @openzeppelin/upgrades/contracts/upgradeability/BaseUpgradeabilityProxy.sol
        
        pragma solidity ^0.5.0;
        
        
        
        /**
         * @title BaseUpgradeabilityProxy
         * @dev This contract implements a proxy that allows to change the
         * implementation address to which it will delegate.
         * Such a change is called an implementation upgrade.
         */
        contract BaseUpgradeabilityProxy is Proxy {
          /**
           * @dev Emitted when the implementation is upgraded.
           * @param implementation Address of the new implementation.
           */
          event Upgraded(address indexed implementation);
        
          /**
           * @dev Storage slot with the address of the current implementation.
           * This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1, and is
           * validated in the constructor.
           */
          bytes32 internal constant IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
        
          /**
           * @dev Returns the current implementation.
           * @return Address of the current implementation
           */
          function _implementation() internal view returns (address impl) {
            bytes32 slot = IMPLEMENTATION_SLOT;
            assembly {
              impl := sload(slot)
            }
          }
        
          /**
           * @dev Upgrades the proxy to a new implementation.
           * @param newImplementation Address of the new implementation.
           */
          function _upgradeTo(address newImplementation) internal {
            _setImplementation(newImplementation);
            emit Upgraded(newImplementation);
          }
        
          /**
           * @dev Sets the implementation address of the proxy.
           * @param newImplementation Address of the new implementation.
           */
          function _setImplementation(address newImplementation) internal {
            require(OpenZeppelinUpgradesAddress.isContract(newImplementation), "Cannot set a proxy implementation to a non-contract address");
        
            bytes32 slot = IMPLEMENTATION_SLOT;
        
            assembly {
              sstore(slot, newImplementation)
            }
          }
        }
        
        // File: @openzeppelin/upgrades/contracts/upgradeability/UpgradeabilityProxy.sol
        
        pragma solidity ^0.5.0;
        
        
        /**
         * @title UpgradeabilityProxy
         * @dev Extends BaseUpgradeabilityProxy with a constructor for initializing
         * implementation and init data.
         */
        contract UpgradeabilityProxy is BaseUpgradeabilityProxy {
          /**
           * @dev Contract constructor.
           * @param _logic Address of the initial implementation.
           * @param _data Data to send as msg.data to the implementation to initialize the proxied contract.
           * It should include the signature and the parameters of the function to be called, as described in
           * https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding.
           * This parameter is optional, if no data is given the initialization call to proxied contract will be skipped.
           */
          constructor(address _logic, bytes memory _data) public payable {
            assert(IMPLEMENTATION_SLOT == bytes32(uint256(keccak256('eip1967.proxy.implementation')) - 1));
            _setImplementation(_logic);
            if(_data.length > 0) {
              (bool success,) = _logic.delegatecall(_data);
              require(success);
            }
          }  
        }
        
        // File: @openzeppelin/upgrades/contracts/upgradeability/BaseAdminUpgradeabilityProxy.sol
        
        pragma solidity ^0.5.0;
        
        
        /**
         * @title BaseAdminUpgradeabilityProxy
         * @dev This contract combines an upgradeability proxy with an authorization
         * mechanism for administrative tasks.
         * All external functions in this contract must be guarded by the
         * `ifAdmin` modifier. See ethereum/solidity#3864 for a Solidity
         * feature proposal that would enable this to be done automatically.
         */
        contract BaseAdminUpgradeabilityProxy is BaseUpgradeabilityProxy {
          /**
           * @dev Emitted when the administration has been transferred.
           * @param previousAdmin Address of the previous admin.
           * @param newAdmin Address of the new admin.
           */
          event AdminChanged(address previousAdmin, address newAdmin);
        
          /**
           * @dev Storage slot with the admin of the contract.
           * This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1, and is
           * validated in the constructor.
           */
        
          bytes32 internal constant ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;
        
          /**
           * @dev Modifier to check whether the `msg.sender` is the admin.
           * If it is, it will run the function. Otherwise, it will delegate the call
           * to the implementation.
           */
          modifier ifAdmin() {
            if (msg.sender == _admin()) {
              _;
            } else {
              _fallback();
            }
          }
        
          /**
           * @return The address of the proxy admin.
           */
          function admin() external ifAdmin returns (address) {
            return _admin();
          }
        
          /**
           * @return The address of the implementation.
           */
          function implementation() external ifAdmin returns (address) {
            return _implementation();
          }
        
          /**
           * @dev Changes the admin of the proxy.
           * Only the current admin can call this function.
           * @param newAdmin Address to transfer proxy administration to.
           */
          function changeAdmin(address newAdmin) external ifAdmin {
            require(newAdmin != address(0), "Cannot change the admin of a proxy to the zero address");
            emit AdminChanged(_admin(), newAdmin);
            _setAdmin(newAdmin);
          }
        
          /**
           * @dev Upgrade the backing implementation of the proxy.
           * Only the admin can call this function.
           * @param newImplementation Address of the new implementation.
           */
          function upgradeTo(address newImplementation) external ifAdmin {
            _upgradeTo(newImplementation);
          }
        
          /**
           * @dev Upgrade the backing implementation of the proxy and call a function
           * on the new implementation.
           * This is useful to initialize the proxied contract.
           * @param newImplementation Address of the new implementation.
           * @param data Data to send as msg.data in the low level call.
           * It should include the signature and the parameters of the function to be called, as described in
           * https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding.
           */
          function upgradeToAndCall(address newImplementation, bytes calldata data) payable external ifAdmin {
            _upgradeTo(newImplementation);
            (bool success,) = newImplementation.delegatecall(data);
            require(success);
          }
        
          /**
           * @return The admin slot.
           */
          function _admin() internal view returns (address adm) {
            bytes32 slot = ADMIN_SLOT;
            assembly {
              adm := sload(slot)
            }
          }
        
          /**
           * @dev Sets the address of the proxy admin.
           * @param newAdmin Address of the new proxy admin.
           */
          function _setAdmin(address newAdmin) internal {
            bytes32 slot = ADMIN_SLOT;
        
            assembly {
              sstore(slot, newAdmin)
            }
          }
        
          /**
           * @dev Only fall back when the sender is not the admin.
           */
          function _willFallback() internal {
            require(msg.sender != _admin(), "Cannot call fallback function from the proxy admin");
            super._willFallback();
          }
        }
        
        // File: @openzeppelin/upgrades/contracts/upgradeability/AdminUpgradeabilityProxy.sol
        
        pragma solidity ^0.5.0;
        
        
        /**
         * @title AdminUpgradeabilityProxy
         * @dev Extends from BaseAdminUpgradeabilityProxy with a constructor for 
         * initializing the implementation, admin, and init data.
         */
        contract AdminUpgradeabilityProxy is BaseAdminUpgradeabilityProxy, UpgradeabilityProxy {
          /**
           * Contract constructor.
           * @param _logic address of the initial implementation.
           * @param _admin Address of the proxy administrator.
           * @param _data Data to send as msg.data to the implementation to initialize the proxied contract.
           * It should include the signature and the parameters of the function to be called, as described in
           * https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding.
           * This parameter is optional, if no data is given the initialization call to proxied contract will be skipped.
           */
          constructor(address _logic, address _admin, bytes memory _data) UpgradeabilityProxy(_logic, _data) public payable {
            assert(ADMIN_SLOT == bytes32(uint256(keccak256('eip1967.proxy.admin')) - 1));
            _setAdmin(_admin);
          }
        }

        File 3 of 4: LockedStaking
        //SPDX-License-Identifier: Unlicense
        pragma solidity 0.8.17;
        
        interface IERC20 {
            function totalSupply() external view returns (uint256);
        
            function balanceOf(address account) external view returns (uint256);
        
            function allowance(address owner, address spender) external view returns (uint256);
        
            function transfer(address recipient, uint256 amount) external returns (bool);
        
            function approve(address spender, uint256 amount) external returns (bool);
        
            function transferFrom(
                address sender,
                address recipient,
                uint256 amount
            ) external returns (bool);
        
            event Transfer(address indexed from, address indexed to, uint256 value);
        
            event Approval(address indexed owner, address indexed spender, uint256 value);
        }
        
        // OpenZeppelin Contracts (last updated v4.8.0) (proxy/utils/Initializable.sol)
        
        // OpenZeppelin Contracts (last updated v4.8.0) (utils/Address.sol)
        
        /**
         * @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
             *
             * Furthermore, `isContract` will also return true if the target contract within
             * the same transaction is already scheduled for destruction by `SELFDESTRUCT`,
             * which only has an effect at the end of a transaction.
             * ====
             *
             * [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://consensys.net/diligence/blog/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);
                }
            }
        }
        
        /**
         * @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]
         * ```solidity
         * 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 Returns the highest version that has been initialized. See {reinitializer}.
             */
            function _getInitializedVersion() internal view returns (uint8) {
                return _initialized;
            }
        
            /**
             * @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.
             */
            function _isInitializing() internal view returns (bool) {
                return _initializing;
            }
        }
        
        // OpenZeppelin Contracts (last updated v4.7.0) (access/Ownable.sol)
        
        // OpenZeppelin Contracts v4.4.1 (utils/Context.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;
        }
        
        /**
         * @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;
        }
        
        // OpenZeppelin Contracts (last updated v4.8.0) (utils/structs/BitMaps.sol)
        
        /**
         * @dev Library for managing uint256 to bool mapping in a compact and efficient way, providing the keys are sequential.
         * Largely inspired by Uniswap's https://github.com/Uniswap/merkle-distributor/blob/master/contracts/MerkleDistributor.sol[merkle-distributor].
         */
        library BitMaps {
            struct BitMap {
                mapping(uint256 => uint256) _data;
            }
        
            /**
             * @dev Returns whether the bit at `index` is set.
             */
            function get(BitMap storage bitmap, uint256 index) internal view returns (bool) {
                uint256 bucket = index >> 8;
                uint256 mask = 1 << (index & 0xff);
                return bitmap._data[bucket] & mask != 0;
            }
        
            /**
             * @dev Sets the bit at `index` to the boolean `value`.
             */
            function setTo(BitMap storage bitmap, uint256 index, bool value) internal {
                if (value) {
                    set(bitmap, index);
                } else {
                    unset(bitmap, index);
                }
            }
        
            /**
             * @dev Sets the bit at `index`.
             */
            function set(BitMap storage bitmap, uint256 index) internal {
                uint256 bucket = index >> 8;
                uint256 mask = 1 << (index & 0xff);
                bitmap._data[bucket] |= mask;
            }
        
            /**
             * @dev Unsets the bit at `index`.
             */
            function unset(BitMap storage bitmap, uint256 index) internal {
                uint256 bucket = index >> 8;
                uint256 mask = 1 << (index & 0xff);
                bitmap._data[bucket] &= ~mask;
            }
        }
        
        /// @notice Arithmetic library with operations for fixed-point numbers.
        /// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/FixedPointMathLib.sol)
        /// @author Inspired by USM (https://github.com/usmfum/USM/blob/master/contracts/WadMath.sol)
        library FixedPointMathLib {
            /*//////////////////////////////////////////////////////////////
                            SIMPLIFIED FIXED POINT OPERATIONS
            //////////////////////////////////////////////////////////////*/
        
            uint256 internal constant MAX_UINT256 = 2**256 - 1;
        
            uint256 internal constant WAD = 1e18; // The scalar of ETH and most ERC20s.
        
            function mulWadDown(uint256 x, uint256 y) internal pure returns (uint256) {
                return mulDivDown(x, y, WAD); // Equivalent to (x * y) / WAD rounded down.
            }
        
            function mulWadUp(uint256 x, uint256 y) internal pure returns (uint256) {
                return mulDivUp(x, y, WAD); // Equivalent to (x * y) / WAD rounded up.
            }
        
            function divWadDown(uint256 x, uint256 y) internal pure returns (uint256) {
                return mulDivDown(x, WAD, y); // Equivalent to (x * WAD) / y rounded down.
            }
        
            function divWadUp(uint256 x, uint256 y) internal pure returns (uint256) {
                return mulDivUp(x, WAD, y); // Equivalent to (x * WAD) / y rounded up.
            }
        
            /*//////////////////////////////////////////////////////////////
                            LOW LEVEL FIXED POINT OPERATIONS
            //////////////////////////////////////////////////////////////*/
        
            function mulDivDown(
                uint256 x,
                uint256 y,
                uint256 denominator
            ) internal pure returns (uint256 z) {
                /// @solidity memory-safe-assembly
                assembly {
                    // Equivalent to require(denominator != 0 && (y == 0 || x <= type(uint256).max / y))
                    if iszero(mul(denominator, iszero(mul(y, gt(x, div(MAX_UINT256, y)))))) {
                        revert(0, 0)
                    }
        
                    // Divide x * y by the denominator.
                    z := div(mul(x, y), denominator)
                }
            }
        
            function mulDivUp(
                uint256 x,
                uint256 y,
                uint256 denominator
            ) internal pure returns (uint256 z) {
                /// @solidity memory-safe-assembly
                assembly {
                    // Equivalent to require(denominator != 0 && (y == 0 || x <= type(uint256).max / y))
                    if iszero(mul(denominator, iszero(mul(y, gt(x, div(MAX_UINT256, y)))))) {
                        revert(0, 0)
                    }
        
                    // If x * y modulo the denominator is strictly greater than 0,
                    // 1 is added to round up the division of x * y by the denominator.
                    z := add(gt(mod(mul(x, y), denominator), 0), div(mul(x, y), denominator))
                }
            }
        
            function rpow(
                uint256 x,
                uint256 n,
                uint256 scalar
            ) internal pure returns (uint256 z) {
                /// @solidity memory-safe-assembly
                assembly {
                    switch x
                    case 0 {
                        switch n
                        case 0 {
                            // 0 ** 0 = 1
                            z := scalar
                        }
                        default {
                            // 0 ** n = 0
                            z := 0
                        }
                    }
                    default {
                        switch mod(n, 2)
                        case 0 {
                            // If n is even, store scalar in z for now.
                            z := scalar
                        }
                        default {
                            // If n is odd, store x in z for now.
                            z := x
                        }
        
                        // Shifting right by 1 is like dividing by 2.
                        let half := shr(1, scalar)
        
                        for {
                            // Shift n right by 1 before looping to halve it.
                            n := shr(1, n)
                        } n {
                            // Shift n right by 1 each iteration to halve it.
                            n := shr(1, n)
                        } {
                            // Revert immediately if x ** 2 would overflow.
                            // Equivalent to iszero(eq(div(xx, x), x)) here.
                            if shr(128, x) {
                                revert(0, 0)
                            }
        
                            // Store x squared.
                            let xx := mul(x, x)
        
                            // Round to the nearest number.
                            let xxRound := add(xx, half)
        
                            // Revert if xx + half overflowed.
                            if lt(xxRound, xx) {
                                revert(0, 0)
                            }
        
                            // Set x to scaled xxRound.
                            x := div(xxRound, scalar)
        
                            // If n is even:
                            if mod(n, 2) {
                                // Compute z * x.
                                let zx := mul(z, x)
        
                                // If z * x overflowed:
                                if iszero(eq(div(zx, x), z)) {
                                    // Revert if x is non-zero.
                                    if iszero(iszero(x)) {
                                        revert(0, 0)
                                    }
                                }
        
                                // Round to the nearest number.
                                let zxRound := add(zx, half)
        
                                // Revert if zx + half overflowed.
                                if lt(zxRound, zx) {
                                    revert(0, 0)
                                }
        
                                // Return properly scaled zxRound.
                                z := div(zxRound, scalar)
                            }
                        }
                    }
                }
            }
        
            /*//////////////////////////////////////////////////////////////
                                GENERAL NUMBER UTILITIES
            //////////////////////////////////////////////////////////////*/
        
            function sqrt(uint256 x) internal pure returns (uint256 z) {
                /// @solidity memory-safe-assembly
                assembly {
                    let y := x // We start y at x, which will help us make our initial estimate.
        
                    z := 181 // The "correct" value is 1, but this saves a multiplication later.
        
                    // This segment is to get a reasonable initial estimate for the Babylonian method. With a bad
                    // start, the correct # of bits increases ~linearly each iteration instead of ~quadratically.
        
                    // We check y >= 2^(k + 8) but shift right by k bits
                    // each branch to ensure that if x >= 256, then y >= 256.
                    if iszero(lt(y, 0x10000000000000000000000000000000000)) {
                        y := shr(128, y)
                        z := shl(64, z)
                    }
                    if iszero(lt(y, 0x1000000000000000000)) {
                        y := shr(64, y)
                        z := shl(32, z)
                    }
                    if iszero(lt(y, 0x10000000000)) {
                        y := shr(32, y)
                        z := shl(16, z)
                    }
                    if iszero(lt(y, 0x1000000)) {
                        y := shr(16, y)
                        z := shl(8, z)
                    }
        
                    // Goal was to get z*z*y within a small factor of x. More iterations could
                    // get y in a tighter range. Currently, we will have y in [256, 256*2^16).
                    // We ensured y >= 256 so that the relative difference between y and y+1 is small.
                    // That's not possible if x < 256 but we can just verify those cases exhaustively.
        
                    // Now, z*z*y <= x < z*z*(y+1), and y <= 2^(16+8), and either y >= 256, or x < 256.
                    // Correctness can be checked exhaustively for x < 256, so we assume y >= 256.
                    // Then z*sqrt(y) is within sqrt(257)/sqrt(256) of sqrt(x), or about 20bps.
        
                    // For s in the range [1/256, 256], the estimate f(s) = (181/1024) * (s+1) is in the range
                    // (1/2.84 * sqrt(s), 2.84 * sqrt(s)), with largest error when s = 1 and when s = 256 or 1/256.
        
                    // Since y is in [256, 256*2^16), let a = y/65536, so that a is in [1/256, 256). Then we can estimate
                    // sqrt(y) using sqrt(65536) * 181/1024 * (a + 1) = 181/4 * (y + 65536)/65536 = 181 * (y + 65536)/2^18.
        
                    // There is no overflow risk here since y < 2^136 after the first branch above.
                    z := shr(18, mul(z, add(y, 65536))) // A mul() is saved from starting z at 181.
        
                    // Given the worst case multiplicative error of 2.84 above, 7 iterations should be enough.
                    z := shr(1, add(z, div(x, z)))
                    z := shr(1, add(z, div(x, z)))
                    z := shr(1, add(z, div(x, z)))
                    z := shr(1, add(z, div(x, z)))
                    z := shr(1, add(z, div(x, z)))
                    z := shr(1, add(z, div(x, z)))
                    z := shr(1, add(z, div(x, z)))
        
                    // If x+1 is a perfect square, the Babylonian method cycles between
                    // floor(sqrt(x)) and ceil(sqrt(x)). This statement ensures we return floor.
                    // See: https://en.wikipedia.org/wiki/Integer_square_root#Using_only_integer_division
                    // Since the ceil is rare, we save gas on the assignment and repeat division in the rare case.
                    // If you don't care whether the floor or ceil square root is returned, you can remove this statement.
                    z := sub(z, lt(div(x, z), z))
                }
            }
        
            function unsafeMod(uint256 x, uint256 y) internal pure returns (uint256 z) {
                /// @solidity memory-safe-assembly
                assembly {
                    // Mod x by y. Note this will return
                    // 0 instead of reverting if y is zero.
                    z := mod(x, y)
                }
            }
        
            function unsafeDiv(uint256 x, uint256 y) internal pure returns (uint256 r) {
                /// @solidity memory-safe-assembly
                assembly {
                    // Divide x by y. Note this will return
                    // 0 instead of reverting if y is zero.
                    r := div(x, y)
                }
            }
        
            function unsafeDivUp(uint256 x, uint256 y) internal pure returns (uint256 z) {
                /// @solidity memory-safe-assembly
                assembly {
                    // Add 1 to x * y if x % y > 0. Note this will
                    // return 0 instead of reverting if y is zero.
                    z := add(gt(mod(x, y), 0), div(x, y))
                }
            }
        }
        
        /// @notice Optimized sorts and operations for sorted arrays.
        /// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/Sort.sol)
        library LibSort {
            /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
            /*                      INSERTION SORT                        */
            /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
        
            // - Faster on small arrays (32 or lesser elements).
            // - Faster on almost sorted arrays.
            // - Smaller bytecode.
            // - May be suitable for view functions intended for off-chain querying.
        
            /// @dev Sorts the array in-place with insertion sort.
            function insertionSort(uint256[] memory a) internal pure {
                /// @solidity memory-safe-assembly
                assembly {
                    let n := mload(a) // Length of `a`.
                    mstore(a, 0) // For insertion sort's inner loop to terminate.
                    let h := add(a, shl(5, n)) // High slot.
                    let s := 0x20
                    let w := not(31)
                    for { let i := add(a, s) } 1 {} {
                        i := add(i, s)
                        if gt(i, h) { break }
                        let k := mload(i) // Key.
                        let j := add(i, w) // The slot before the current slot.
                        let v := mload(j) // The value of `j`.
                        if iszero(gt(v, k)) { continue }
                        for {} 1 {} {
                            mstore(add(j, s), v)
                            j := add(j, w) // `sub(j, 0x20)`.
                            v := mload(j)
                            if iszero(gt(v, k)) { break }
                        }
                        mstore(add(j, s), k)
                    }
                    mstore(a, n) // Restore the length of `a`.
                }
            }
        
            /// @dev Sorts the array in-place with insertion sort.
            function insertionSort(int256[] memory a) internal pure {
                _convertTwosComplement(a);
                insertionSort(_toUints(a));
                _convertTwosComplement(a);
            }
        
            /// @dev Sorts the array in-place with insertion sort.
            function insertionSort(address[] memory a) internal pure {
                insertionSort(_toUints(a));
            }
        
            /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
            /*                      INTRO-QUICKSORT                       */
            /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
        
            // - Faster on larger arrays (more than 32 elements).
            // - Robust performance.
            // - Larger bytecode.
        
            /// @dev Sorts the array in-place with intro-quicksort.
            function sort(uint256[] memory a) internal pure {
                /// @solidity memory-safe-assembly
                assembly {
                    let w := not(31)
                    let s := 0x20
                    let n := mload(a) // Length of `a`.
                    mstore(a, 0) // For insertion sort's inner loop to terminate.
        
                    // Let the stack be the start of the free memory.
                    let stack := mload(0x40)
        
                    for {} iszero(lt(n, 2)) {} {
                        // Push `l` and `h` to the stack.
                        // The `shl` by 5 is equivalent to multiplying by `0x20`.
                        let l := add(a, s)
                        let h := add(a, shl(5, n))
        
                        let j := l
                        // forgefmt: disable-next-item
                        for {} iszero(or(eq(j, h), gt(mload(j), mload(add(j, s))))) {} {
                            j := add(j, s)
                        }
                        // If the array is already sorted.
                        if eq(j, h) { break }
        
                        j := h
                        // forgefmt: disable-next-item
                        for {} iszero(gt(mload(j), mload(add(j, w)))) {} {
                            j := add(j, w) // `sub(j, 0x20)`.
                        }
                        // If the array is reversed sorted.
                        if eq(j, l) {
                            for {} 1 {} {
                                let t := mload(l)
                                mstore(l, mload(h))
                                mstore(h, t)
                                h := add(h, w) // `sub(h, 0x20)`.
                                l := add(l, s)
                                if iszero(lt(l, h)) { break }
                            }
                            break
                        }
        
                        // Push `l` and `h` onto the stack.
                        mstore(stack, l)
                        mstore(add(stack, s), h)
                        stack := add(stack, 0x40)
                        break
                    }
        
                    for { let stackBottom := mload(0x40) } iszero(eq(stack, stackBottom)) {} {
                        // Pop `l` and `h` from the stack.
                        stack := sub(stack, 0x40)
                        let l := mload(stack)
                        let h := mload(add(stack, s))
        
                        // Do insertion sort if `h - l <= 0x20 * 12`.
                        // Threshold is fine-tuned via trial and error.
                        if iszero(gt(sub(h, l), 0x180)) {
                            // Hardcode sort the first 2 elements.
                            let i := add(l, s)
                            if iszero(lt(mload(l), mload(i))) {
                                let t := mload(i)
                                mstore(i, mload(l))
                                mstore(l, t)
                            }
                            for {} 1 {} {
                                i := add(i, s)
                                if gt(i, h) { break }
                                let k := mload(i) // Key.
                                let j := add(i, w) // The slot before the current slot.
                                let v := mload(j) // The value of `j`.
                                if iszero(gt(v, k)) { continue }
                                for {} 1 {} {
                                    mstore(add(j, s), v)
                                    j := add(j, w)
                                    v := mload(j)
                                    if iszero(gt(v, k)) { break }
                                }
                                mstore(add(j, s), k)
                            }
                            continue
                        }
                        // Pivot slot is the average of `l` and `h`.
                        let p := add(shl(5, shr(6, add(l, h))), and(31, l))
                        // Median of 3 with sorting.
                        {
                            let e0 := mload(l)
                            let e2 := mload(h)
                            let e1 := mload(p)
                            if iszero(lt(e0, e1)) {
                                let t := e0
                                e0 := e1
                                e1 := t
                            }
                            if iszero(lt(e0, e2)) {
                                let t := e0
                                e0 := e2
                                e2 := t
                            }
                            if iszero(lt(e1, e2)) {
                                let t := e1
                                e1 := e2
                                e2 := t
                            }
                            mstore(p, e1)
                            mstore(h, e2)
                            mstore(l, e0)
                        }
                        // Hoare's partition.
                        {
                            // The value of the pivot slot.
                            let x := mload(p)
                            p := h
                            for { let i := l } 1 {} {
                                for {} 1 {} {
                                    i := add(i, s)
                                    if iszero(gt(x, mload(i))) { break }
                                }
                                let j := p
                                for {} 1 {} {
                                    j := add(j, w)
                                    if iszero(lt(x, mload(j))) { break }
                                }
                                p := j
                                if iszero(lt(i, p)) { break }
                                // Swap slots `i` and `p`.
                                let t := mload(i)
                                mstore(i, mload(p))
                                mstore(p, t)
                            }
                        }
                        // If slice on right of pivot is non-empty, push onto stack.
                        {
                            mstore(stack, add(p, s))
                            // Skip `mstore(add(stack, 0x20), h)`, as it is already on the stack.
                            stack := add(stack, shl(6, lt(add(p, s), h)))
                        }
                        // If slice on left of pivot is non-empty, push onto stack.
                        {
                            mstore(stack, l)
                            mstore(add(stack, s), p)
                            stack := add(stack, shl(6, gt(p, l)))
                        }
                    }
                    mstore(a, n) // Restore the length of `a`.
                }
            }
        
            /// @dev Sorts the array in-place with intro-quicksort.
            function sort(int256[] memory a) internal pure {
                _convertTwosComplement(a);
                sort(_toUints(a));
                _convertTwosComplement(a);
            }
        
            /// @dev Sorts the array in-place with intro-quicksort.
            function sort(address[] memory a) internal pure {
                sort(_toUints(a));
            }
        
            /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
            /*                  OTHER USEFUL OPERATIONS                   */
            /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
        
            // For performance, the `uniquifySorted` methods will not revert if the
            // array is not sorted -- it will simply remove consecutive duplicate elements.
        
            /// @dev Removes duplicate elements from a ascendingly sorted memory array.
            function uniquifySorted(uint256[] memory a) internal pure {
                /// @solidity memory-safe-assembly
                assembly {
                    // If the length of `a` is greater than 1.
                    if iszero(lt(mload(a), 2)) {
                        let x := add(a, 0x20)
                        let y := add(a, 0x40)
                        let end := add(a, shl(5, add(mload(a), 1)))
                        for {} 1 {} {
                            if iszero(eq(mload(x), mload(y))) {
                                x := add(x, 0x20)
                                mstore(x, mload(y))
                            }
                            y := add(y, 0x20)
                            if eq(y, end) { break }
                        }
                        mstore(a, shr(5, sub(x, a)))
                    }
                }
            }
        
            /// @dev Removes duplicate elements from a ascendingly sorted memory array.
            function uniquifySorted(int256[] memory a) internal pure {
                uniquifySorted(_toUints(a));
            }
        
            /// @dev Removes duplicate elements from a ascendingly sorted memory array.
            function uniquifySorted(address[] memory a) internal pure {
                uniquifySorted(_toUints(a));
            }
        
            /// @dev Returns whether `a` contains `needle`,
            /// and the index of the nearest element less than or equal to `needle`.
            function searchSorted(uint256[] memory a, uint256 needle)
                internal
                pure
                returns (bool found, uint256 index)
            {
                (found, index) = _searchSorted(a, needle, 0);
            }
        
            /// @dev Returns whether `a` contains `needle`,
            /// and the index of the nearest element less than or equal to `needle`.
            function searchSorted(int256[] memory a, int256 needle)
                internal
                pure
                returns (bool found, uint256 index)
            {
                (found, index) = _searchSorted(_toUints(a), uint256(needle), 1 << 255);
            }
        
            /// @dev Returns whether `a` contains `needle`,
            /// and the index of the nearest element less than or equal to `needle`.
            function searchSorted(address[] memory a, address needle)
                internal
                pure
                returns (bool found, uint256 index)
            {
                (found, index) = _searchSorted(_toUints(a), uint256(uint160(needle)), 0);
            }
        
            /// @dev Reverses the array in-place.
            function reverse(uint256[] memory a) internal pure {
                /// @solidity memory-safe-assembly
                assembly {
                    if iszero(lt(mload(a), 2)) {
                        let s := 0x20
                        let w := not(31)
                        let h := add(a, shl(5, mload(a)))
                        for { a := add(a, s) } 1 {} {
                            let t := mload(a)
                            mstore(a, mload(h))
                            mstore(h, t)
                            h := add(h, w)
                            a := add(a, s)
                            if iszero(lt(a, h)) { break }
                        }
                    }
                }
            }
        
            /// @dev Reverses the array in-place.
            function reverse(int256[] memory a) internal pure {
                reverse(_toUints(a));
            }
        
            /// @dev Reverses the array in-place.
            function reverse(address[] memory a) internal pure {
                reverse(_toUints(a));
            }
        
            /// @dev Returns whether the array is sorted in ascending order.
            function isSorted(uint256[] memory a) internal pure returns (bool result) {
                /// @solidity memory-safe-assembly
                assembly {
                    result := 1
                    if iszero(lt(mload(a), 2)) {
                        let end := add(a, shl(5, mload(a)))
                        for { a := add(a, 0x20) } 1 {} {
                            let p := mload(a)
                            a := add(a, 0x20)
                            result := iszero(gt(p, mload(a)))
                            if iszero(mul(result, xor(a, end))) { break }
                        }
                    }
                }
            }
        
            /// @dev Returns whether the array is sorted in ascending order.
            function isSorted(int256[] memory a) internal pure returns (bool result) {
                /// @solidity memory-safe-assembly
                assembly {
                    result := 1
                    if iszero(lt(mload(a), 2)) {
                        let end := add(a, shl(5, mload(a)))
                        for { a := add(a, 0x20) } 1 {} {
                            let p := mload(a)
                            a := add(a, 0x20)
                            result := iszero(sgt(p, mload(a)))
                            if iszero(mul(result, xor(a, end))) { break }
                        }
                    }
                }
            }
        
            /// @dev Returns whether the array is sorted in ascending order.
            function isSorted(address[] memory a) internal pure returns (bool result) {
                result = isSorted(_toUints(a));
            }
        
            /// @dev Returns whether the array is strictly ascending (sorted and uniquified).
            function isSortedAndUniquified(uint256[] memory a) internal pure returns (bool result) {
                /// @solidity memory-safe-assembly
                assembly {
                    result := 1
                    if iszero(lt(mload(a), 2)) {
                        let end := add(a, shl(5, mload(a)))
                        for { a := add(a, 0x20) } 1 {} {
                            let p := mload(a)
                            a := add(a, 0x20)
                            result := lt(p, mload(a))
                            if iszero(mul(result, xor(a, end))) { break }
                        }
                    }
                }
            }
        
            /// @dev Returns whether the array is strictly ascending (sorted and uniquified).
            function isSortedAndUniquified(int256[] memory a) internal pure returns (bool result) {
                /// @solidity memory-safe-assembly
                assembly {
                    result := 1
                    if iszero(lt(mload(a), 2)) {
                        let end := add(a, shl(5, mload(a)))
                        for { a := add(a, 0x20) } 1 {} {
                            let p := mload(a)
                            a := add(a, 0x20)
                            result := slt(p, mload(a))
                            if iszero(mul(result, xor(a, end))) { break }
                        }
                    }
                }
            }
        
            /// @dev Returns whether the array is strictly ascending (sorted and uniquified).
            function isSortedAndUniquified(address[] memory a) internal pure returns (bool result) {
                result = isSortedAndUniquified(_toUints(a));
            }
        
            /// @dev Returns the sorted set difference of `a` and `b`.
            /// Note: Behaviour is undefined if inputs are not sorted and uniquified.
            function difference(uint256[] memory a, uint256[] memory b)
                internal
                pure
                returns (uint256[] memory c)
            {
                c = _difference(a, b, 0);
            }
        
            /// @dev Returns the sorted set difference between `a` and `b`.
            /// Note: Behaviour is undefined if inputs are not sorted and uniquified.
            function difference(int256[] memory a, int256[] memory b)
                internal
                pure
                returns (int256[] memory c)
            {
                c = _toInts(_difference(_toUints(a), _toUints(b), 1 << 255));
            }
        
            /// @dev Returns the sorted set difference between `a` and `b`.
            /// Note: Behaviour is undefined if inputs are not sorted and uniquified.
            function difference(address[] memory a, address[] memory b)
                internal
                pure
                returns (address[] memory c)
            {
                c = _toAddresses(_difference(_toUints(a), _toUints(b), 0));
            }
        
            /// @dev Returns the sorted set intersection between `a` and `b`.
            /// Note: Behaviour is undefined if inputs are not sorted and uniquified.
            function intersection(uint256[] memory a, uint256[] memory b)
                internal
                pure
                returns (uint256[] memory c)
            {
                c = _intersection(a, b, 0);
            }
        
            /// @dev Returns the sorted set intersection between `a` and `b`.
            /// Note: Behaviour is undefined if inputs are not sorted and uniquified.
            function intersection(int256[] memory a, int256[] memory b)
                internal
                pure
                returns (int256[] memory c)
            {
                c = _toInts(_intersection(_toUints(a), _toUints(b), 1 << 255));
            }
        
            /// @dev Returns the sorted set intersection between `a` and `b`.
            /// Note: Behaviour is undefined if inputs are not sorted and uniquified.
            function intersection(address[] memory a, address[] memory b)
                internal
                pure
                returns (address[] memory c)
            {
                c = _toAddresses(_intersection(_toUints(a), _toUints(b), 0));
            }
        
            /// @dev Returns the sorted set union of `a` and `b`.
            /// Note: Behaviour is undefined if inputs are not sorted and uniquified.
            function union(uint256[] memory a, uint256[] memory b)
                internal
                pure
                returns (uint256[] memory c)
            {
                c = _union(a, b, 0);
            }
        
            /// @dev Returns the sorted set union of `a` and `b`.
            /// Note: Behaviour is undefined if inputs are not sorted and uniquified.
            function union(int256[] memory a, int256[] memory b)
                internal
                pure
                returns (int256[] memory c)
            {
                c = _toInts(_union(_toUints(a), _toUints(b), 1 << 255));
            }
        
            /// @dev Returns the sorted set union between `a` and `b`.
            /// Note: Behaviour is undefined if inputs are not sorted and uniquified.
            function union(address[] memory a, address[] memory b)
                internal
                pure
                returns (address[] memory c)
            {
                c = _toAddresses(_union(_toUints(a), _toUints(b), 0));
            }
        
            /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
            /*                      PRIVATE HELPERS                       */
            /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
        
            /// @dev Reinterpret cast to an uint256 array.
            function _toUints(int256[] memory a) private pure returns (uint256[] memory casted) {
                /// @solidity memory-safe-assembly
                assembly {
                    casted := a
                }
            }
        
            /// @dev Reinterpret cast to an uint256 array.
            function _toUints(address[] memory a) private pure returns (uint256[] memory casted) {
                /// @solidity memory-safe-assembly
                assembly {
                    // As any address written to memory will have the upper 96 bits
                    // of the word zeroized (as per Solidity spec), we can directly
                    // compare these addresses as if they are whole uint256 words.
                    casted := a
                }
            }
        
            /// @dev Reinterpret cast to an int array.
            function _toInts(uint256[] memory a) private pure returns (int256[] memory casted) {
                /// @solidity memory-safe-assembly
                assembly {
                    casted := a
                }
            }
        
            /// @dev Reinterpret cast to an address array.
            function _toAddresses(uint256[] memory a) private pure returns (address[] memory casted) {
                /// @solidity memory-safe-assembly
                assembly {
                    casted := a
                }
            }
        
            /// @dev Converts an array of signed two-complement integers
            /// to unsigned integers suitable for sorting.
            function _convertTwosComplement(int256[] memory a) private pure {
                /// @solidity memory-safe-assembly
                assembly {
                    let w := shl(255, 1)
                    for { let end := add(a, shl(5, mload(a))) } iszero(eq(a, end)) {} {
                        a := add(a, 0x20)
                        mstore(a, add(mload(a), w))
                    }
                }
            }
        
            /// @dev Returns whether `a` contains `needle`,
            /// and the index of the nearest element less than or equal to `needle`.
            function _searchSorted(uint256[] memory a, uint256 needle, uint256 signed)
                private
                pure
                returns (bool found, uint256 index)
            {
                /// @solidity memory-safe-assembly
                assembly {
                    let m := 0 // Middle slot.
                    let s := 0x20
                    let l := add(a, s) // Slot of the start of search.
                    let h := add(a, shl(5, mload(a))) // Slot of the end of search.
                    for { needle := add(signed, needle) } 1 {} {
                        // Average of `l` and `h`.
                        m := add(shl(5, shr(6, add(l, h))), and(31, l))
                        let t := add(signed, mload(m))
                        found := eq(t, needle)
                        if or(gt(l, h), found) { break }
                        // Decide whether to search the left or right half.
                        if iszero(gt(needle, t)) {
                            h := sub(m, s)
                            continue
                        }
                        l := add(m, s)
                    }
                    // `m` will be less than `add(a, 0x20)` in the case of an empty array,
                    // or when the value is less than the smallest value in the array.
                    let t := iszero(lt(m, add(a, s)))
                    index := shr(5, mul(sub(m, add(a, s)), t))
                    found := and(found, t)
                }
            }
        
            /// @dev Returns the sorted set difference of `a` and `b`.
            /// Note: Behaviour is undefined if inputs are not sorted and uniquified.
            function _difference(uint256[] memory a, uint256[] memory b, uint256 signed)
                private
                pure
                returns (uint256[] memory c)
            {
                /// @solidity memory-safe-assembly
                assembly {
                    let s := 0x20
                    let aEnd := add(a, shl(5, mload(a)))
                    let bEnd := add(b, shl(5, mload(b)))
                    c := mload(0x40) // Set `c` to the free memory pointer.
                    a := add(a, s)
                    b := add(b, s)
                    let k := c
                    for {} iszero(or(gt(a, aEnd), gt(b, bEnd))) {} {
                        let u := mload(a)
                        let v := mload(b)
                        if iszero(xor(u, v)) {
                            a := add(a, s)
                            b := add(b, s)
                            continue
                        }
                        if iszero(lt(add(u, signed), add(v, signed))) {
                            b := add(b, s)
                            continue
                        }
                        k := add(k, s)
                        mstore(k, u)
                        a := add(a, s)
                    }
                    for {} iszero(gt(a, aEnd)) {} {
                        k := add(k, s)
                        mstore(k, mload(a))
                        a := add(a, s)
                    }
                    mstore(c, shr(5, sub(k, c))) // Store the length of `c`.
                    mstore(0x40, add(k, s)) // Allocate the memory for `c`.
                }
            }
        
            /// @dev Returns the sorted set intersection between `a` and `b`.
            /// Note: Behaviour is undefined if inputs are not sorted and uniquified.
            function _intersection(uint256[] memory a, uint256[] memory b, uint256 signed)
                private
                pure
                returns (uint256[] memory c)
            {
                /// @solidity memory-safe-assembly
                assembly {
                    let s := 0x20
                    let aEnd := add(a, shl(5, mload(a)))
                    let bEnd := add(b, shl(5, mload(b)))
                    c := mload(0x40) // Set `c` to the free memory pointer.
                    a := add(a, s)
                    b := add(b, s)
                    let k := c
                    for {} iszero(or(gt(a, aEnd), gt(b, bEnd))) {} {
                        let u := mload(a)
                        let v := mload(b)
                        if iszero(xor(u, v)) {
                            k := add(k, s)
                            mstore(k, u)
                            a := add(a, s)
                            b := add(b, s)
                            continue
                        }
                        if iszero(lt(add(u, signed), add(v, signed))) {
                            b := add(b, s)
                            continue
                        }
                        a := add(a, s)
                    }
                    mstore(c, shr(5, sub(k, c))) // Store the length of `c`.
                    mstore(0x40, add(k, s)) // Allocate the memory for `c`.
                }
            }
        
            /// @dev Returns the sorted set union of `a` and `b`.
            /// Note: Behaviour is undefined if inputs are not sorted and uniquified.
            function _union(uint256[] memory a, uint256[] memory b, uint256 signed)
                private
                pure
                returns (uint256[] memory c)
            {
                /// @solidity memory-safe-assembly
                assembly {
                    let s := 0x20
                    let aEnd := add(a, shl(5, mload(a)))
                    let bEnd := add(b, shl(5, mload(b)))
                    c := mload(0x40) // Set `c` to the free memory pointer.
                    a := add(a, s)
                    b := add(b, s)
                    let k := c
                    for {} iszero(or(gt(a, aEnd), gt(b, bEnd))) {} {
                        let u := mload(a)
                        let v := mload(b)
                        if iszero(xor(u, v)) {
                            k := add(k, s)
                            mstore(k, u)
                            a := add(a, s)
                            b := add(b, s)
                            continue
                        }
                        if iszero(lt(add(u, signed), add(v, signed))) {
                            k := add(k, s)
                            mstore(k, v)
                            b := add(b, s)
                            continue
                        }
                        k := add(k, s)
                        mstore(k, u)
                        a := add(a, s)
                    }
                    for {} iszero(gt(a, aEnd)) {} {
                        k := add(k, s)
                        mstore(k, mload(a))
                        a := add(a, s)
                    }
                    for {} iszero(gt(b, bEnd)) {} {
                        k := add(k, s)
                        mstore(k, mload(b))
                        b := add(b, s)
                    }
                    mstore(c, shr(5, sub(k, c))) // Store the length of `c`.
                    mstore(0x40, add(k, s)) // Allocate the memory for `c`.
                }
            }
        }
        
        /*///////////////////////////////////////////////////////////////
                                        ERRORS
        //////////////////////////////////////////////////////////////*/
        error MustProlongLock(uint256 oldDuration, uint256 newDuration);
        error AmountIsZero();
        error TransferFailed();
        error NothingToClaim();
        error LockStillActive();
        error DurationOutOfBounds(uint256 duration);
        error UpdateToSmallerMultiplier(uint16 oldMultiplier, uint16 newMultiplier);
        error ZeroAddress();
        error ZeroPrecision();
        error ZeroAmount();
        error MaxLocksSucceeded();
        error MaxRewardsSucceeded();
        error CanOnlyAddFutureRewards();
        error NotRewardoor();
        error CantAutoCompound();
        error AlreadyAutoCompound();
        error NotAutoCompoundEnabled();
        error RewardStartEqEnd();
        error IntervalNotRoundedWithEpoch();
        error CantChangePast();
        error CantUpdateExpiredLock();
        
        contract LockedStaking is Initializable, OwnableUpgradeable {
            using BitMaps for BitMaps.BitMap;
            using FixedPointMathLib for uint256;
        
            /*///////////////////////////////////////////////////////////////
                                          EVENTS
            //////////////////////////////////////////////////////////////*/
            event LockAdded(address indexed from, uint208 amount, uint32 end, uint16 multiplier);
            event LockUpdated(address indexed from, uint8 index, uint208 amount, uint32 end, uint16 multiplier);
            event Unlock(address indexed from, uint256 amount, uint256 index);
            event Claim(address indexed from, uint256 amount);
            event RewardAdded(uint256 start, uint256 end, uint256 amountPerSecond);
            event RewardUpdated(uint256 index, uint256 start, uint256 end, uint256 amountPerSecond);
            event RewardRemoved(uint256 index);
            event AutoCompoundEnabled(address indexed from, uint256 index, uint256 shares);
            event AutoCompoundDisabled(address indexed from, uint256 index, uint256 amount);
            event AutoCompounded(uint256 compoundAmount);
            event RewardoorSet(address indexed rewardoor, bool value);
        
            /*///////////////////////////////////////////////////////////////
                                     IMMUTABLES & CONSTANTS
            //////////////////////////////////////////////////////////////*/
            uint256 public constant MAX_LOCK_COUNT = 5;
            uint256 public constant MAX_REWARD_COUNT = 5;
            uint256 public constant MAX_MULTIPLIER = 998;
            uint256 public constant EPOCH_DURATION = 8 * 60 * 60;
            uint256 public constant ALGORITHM_THRESHOLD_IN_EPOCHS = 5;
        
            /*///////////////////////////////////////////////////////////////
                                     STRUCTS
            //////////////////////////////////////////////////////////////*/
            struct Lock {
                uint16 multiplier;
                uint32 end;
                uint208 amount;
            }
        
            struct Reward {
                uint32 start;
                uint32 end;
                uint192 amountPerSecond;
            }
        
            struct TotalReward {
                uint256 start;
                uint256 end;
                uint256 epochReward;
            }
        
            struct CompoundVars {
                uint256 from;
                uint256 to;
                uint256 currentPeriod;
                uint256 compoundClaimable;
                uint256 epochRewards;
                uint256 currCompLastAccRewardWeight;
            }
        
            /*///////////////////////////////////////////////////////////////
                                     STORAGE
            //////////////////////////////////////////////////////////////*/
            IERC20 public swapToken;
            uint256 public precision; // no longer used but kept for upgradeability
            Reward[] public rewards;
            mapping(address => Lock[]) public locks;
            mapping(address => uint256) public userLastAccRewardsWeight;
        
            uint256 public lastRewardUpdate;
            uint256 public totalScore;
            uint256 public accRewardWeight;
        
            mapping(address => bool) public rewardoors;
        
            BitMaps.BitMap private lockCompoundedBitMap;
            uint256 public compoundAmount;
            uint256 public compoundShares;
            uint256 public compoundLastAccRewardWeight;
        
            /*///////////////////////////////////////////////////////////////
                                     MODIFIERS
            //////////////////////////////////////////////////////////////*/
        
            modifier onlyRewardoor() {
                if (!rewardoors[msg.sender]) revert NotRewardoor();
                _;
            }
        
            constructor() {
                _disableInitializers();
            }
        
            /*///////////////////////////////////////////////////////////////
                                     EXTERNAL
            //////////////////////////////////////////////////////////////*/
        
            function initialize(address _swapToken, uint256 _precision) external initializer {
                if (_swapToken == address(0)) revert ZeroAddress();
                if (_precision == 0) revert ZeroPrecision();
        
                swapToken = IERC20(_swapToken);
                precision = _precision;
        
                __Ownable_init();
            }
        
            /// @notice Adds new reward
            function addReward(
                uint32 start,
                uint32 end,
                uint192 amountPerSecond
            ) external onlyRewardoor {
                if (rewards.length == MAX_REWARD_COUNT) revert MaxRewardsSucceeded();
                if (amountPerSecond == 0) revert AmountIsZero();
                if (start == end) revert RewardStartEqEnd();
                if (start < block.timestamp || end < block.timestamp) revert CanOnlyAddFutureRewards();
                if (start % EPOCH_DURATION != 0) revert IntervalNotRoundedWithEpoch();
                if (end % EPOCH_DURATION != 0) revert IntervalNotRoundedWithEpoch();
        
                rewards.push(Reward(start, end, amountPerSecond));
        
                if (!IERC20(swapToken).transferFrom(msg.sender, address(this), (end - start) * amountPerSecond))
                    revert TransferFailed();
        
                emit RewardAdded(start, end, amountPerSecond);
            }
        
            /// @notice Removes existing reward and sends remaining reward to owner
            function removeReward(uint256 index) external onlyRewardoor {
                updateRewardsWeight();
        
                Reward memory reward = rewards[index];
        
                rewards[index] = rewards[rewards.length - 1];
                rewards.pop();
        
                // if rewards are not unlocked completely, send remaining to owner
                if (reward.end > block.timestamp) {
                    uint256 lockedRewards = (reward.end - max(block.timestamp, reward.start)) * reward.amountPerSecond;
        
                    if (!IERC20(swapToken).transfer(msg.sender, lockedRewards)) revert TransferFailed();
                }
        
                emit RewardRemoved(index);
            }
        
            /// @notice Updates existing reward, cant change reward start if its already started
            function updateReward(
                uint256 index,
                uint256 newStart,
                uint256 newEnd,
                uint256 newAmountPerSecond
            ) external onlyRewardoor {
                if (newStart % EPOCH_DURATION != 0) revert IntervalNotRoundedWithEpoch();
                if (newEnd % EPOCH_DURATION != 0) revert IntervalNotRoundedWithEpoch();
                if (newAmountPerSecond == 0) revert ZeroAmount();
        
                updateRewardsWeight();
        
                Reward storage reward = rewards[index];
        
                uint256 oldEnd = reward.end;
                if (oldEnd < block.timestamp) revert CantChangePast();
                if (newEnd < block.timestamp) revert CanOnlyAddFutureRewards();
        
                uint256 oldStart = reward.start;
                if ((oldStart < block.timestamp || newStart < block.timestamp) && newStart != oldStart) revert CantChangePast();
        
                uint256 newTotalRewards = (newEnd - newStart) * newAmountPerSecond;
                uint256 oldTotalRewards = (oldEnd - oldStart) * reward.amountPerSecond;
        
                if (newStart != oldStart) {
                    reward.start = uint32(newStart);
                }
        
                reward.end = uint32(newEnd);
                reward.amountPerSecond = uint192(newAmountPerSecond);
        
                if (oldTotalRewards > newTotalRewards) {
                    if (!IERC20(swapToken).transfer(msg.sender, oldTotalRewards - newTotalRewards)) revert TransferFailed();
                } else if (oldTotalRewards != newTotalRewards) {
                    if (!IERC20(swapToken).transferFrom(msg.sender, address(this), newTotalRewards - oldTotalRewards))
                        revert TransferFailed();
                }
        
                emit RewardUpdated(index, newStart, newEnd, newAmountPerSecond);
            }
        
            /// @notice Sets address eligibility to add/update rewards
            function setRewardoor(address addr, bool value) external onlyOwner {
                rewardoors[addr] = value;
        
                emit RewardoorSet(addr, value);
            }
        
            /// @notice Creates new lock for a user, adds potential claimable amount to it
            function addLock(uint208 amount, uint256 duration) external {
                if (amount == 0) revert AmountIsZero();
                if (locks[msg.sender].length == MAX_LOCK_COUNT) revert MaxLocksSucceeded();
        
                uint256 newAccRewardsWeight = updateRewardsWeight();
        
                uint256 claimable = calculateUserClaimable(msg.sender, newAccRewardsWeight);
                uint256 addedAmount = claimable + amount;
        
                userLastAccRewardsWeight[msg.sender] = newAccRewardsWeight;
        
                uint32 end = uint32(block.timestamp + duration);
                uint16 multiplier = getDurationMultiplier(duration);
        
                locks[msg.sender].push(Lock(multiplier, end, uint208(addedAmount)));
        
                totalScore += multiplier * addedAmount;
        
                if (!IERC20(swapToken).transferFrom(msg.sender, address(this), amount)) revert TransferFailed();
        
                if (claimable != 0) {
                    emit Claim(msg.sender, claimable);
                }
        
                emit LockAdded(msg.sender, uint208(addedAmount), end, multiplier);
            }
        
            /// @notice adds claimable to current lock, keeping the same end
            function compound(uint8 index) external {
                uint256 bitMapIndex = getBitMapIndex(index, msg.sender);
                if (lockCompoundedBitMap.get(bitMapIndex)) revert AlreadyAutoCompound();
        
                uint256 newAccRewardsWeight = updateRewardsWeight();
        
                uint256 claimable = calculateUserClaimable(msg.sender, newAccRewardsWeight);
                if (claimable == 0) revert NothingToClaim();
        
                userLastAccRewardsWeight[msg.sender] = newAccRewardsWeight;
        
                Lock storage lock = locks[msg.sender][index];
        
                if (lock.end < block.timestamp) revert CantUpdateExpiredLock();
        
                uint208 newAmount = uint208(lock.amount + claimable);
                uint16 multiplier = lock.multiplier;
        
                lock.amount = newAmount;
                totalScore += claimable * multiplier;
        
                emit Claim(msg.sender, claimable);
        
                emit LockUpdated(msg.sender, index, newAmount, lock.end, multiplier);
            }
        
            /// @notice adds amount + potential claimable to existing lock, keeping the same end
            /// @dev if lock has auto compound enabled, adjusts the compoundLastAccRewardWeight to have the same current claimable amount
            function updateLockAmount(uint256 index, uint208 amount) external {
                if (amount == 0) revert AmountIsZero();
        
                uint256 newAccRewardsWeight = updateRewardsWeight();
        
                uint256 claimable = calculateUserClaimable(msg.sender, newAccRewardsWeight);
                uint256 addedAmount = amount + claimable;
        
                Lock storage lock = locks[msg.sender][index];
        
                if (lock.end < block.timestamp) revert CantUpdateExpiredLock();
        
                uint208 newAmount;
                if (lockCompoundedBitMap.get(getBitMapIndex(index, msg.sender))) {
                    uint256 oldCompoundAmount = compoundAmount;
        
                    compoundLastAccRewardWeight = calculateCompAccRewardWeightIn(
                        oldCompoundAmount,
                        addedAmount,
                        newAccRewardsWeight,
                        compoundLastAccRewardWeight
                    );
        
                    uint256 newShares = convertToAutoCompoundShares(addedAmount);
        
                    compoundAmount = oldCompoundAmount + addedAmount;
                    compoundShares += newShares;
        
                    newAmount = uint208(lock.amount + newShares);
                } else {
                    newAmount = uint208(lock.amount + addedAmount);
                }
        
                lock.amount = newAmount;
        
                uint16 multiplier = lock.multiplier;
        
                userLastAccRewardsWeight[msg.sender] = newAccRewardsWeight;
        
                totalScore += addedAmount * multiplier;
        
                if (!IERC20(swapToken).transferFrom(msg.sender, address(this), amount)) revert TransferFailed();
        
                if (claimable != 0) {
                    emit Claim(msg.sender, claimable);
                }
        
                emit LockUpdated(msg.sender, uint8(index), newAmount, lock.end, multiplier);
            }
        
            /// @notice claims for current locks and increases duration of existing lock
            function updateLockDuration(uint8 index, uint256 duration) external {
                uint256 newAccRewardsWeight = updateRewardsWeight();
        
                Lock storage lock = locks[msg.sender][index];
        
                uint32 end = uint32(block.timestamp + duration);
                if (lock.end >= end) revert MustProlongLock(lock.end, end);
        
                uint256 claimable = calculateUserClaimable(msg.sender, newAccRewardsWeight);
        
                userLastAccRewardsWeight[msg.sender] = newAccRewardsWeight;
        
                uint16 multiplier = getDurationMultiplier(duration);
        
                lock.end = end;
        
                uint16 oldMultiplier = lock.multiplier;
        
                if (oldMultiplier > multiplier) revert UpdateToSmallerMultiplier(oldMultiplier, multiplier);
        
                lock.multiplier = multiplier;
        
                uint208 amount = lock.amount;
                totalScore += (multiplier - oldMultiplier) * amount;
        
                if (claimable != 0) {
                    if (!IERC20(swapToken).transfer(msg.sender, claimable)) revert TransferFailed();
        
                    emit Claim(msg.sender, claimable);
                }
        
                emit LockUpdated(msg.sender, index, amount, end, multiplier);
            }
        
            /// @notice claims for current locks
            function claim() external {
                uint256 newAccRewardsWeight = updateRewardsWeight();
        
                uint256 claimable = calculateUserClaimable(msg.sender, newAccRewardsWeight);
                if (claimable == 0) revert NothingToClaim();
        
                userLastAccRewardsWeight[msg.sender] = newAccRewardsWeight;
        
                if (!IERC20(swapToken).transfer(msg.sender, claimable)) revert TransferFailed();
        
                emit Claim(msg.sender, claimable);
            }
        
            /// @notice returns locked amount + potential claimable to user and deletes lock from array
            function unlock(uint256 index) external {
                uint256 newAccRewardsWeight = updateRewardsWeight();
                Lock storage lock = locks[msg.sender][index];
        
                if (lock.end > block.timestamp) revert LockStillActive();
        
                uint256 bitMapIndex = getBitMapIndex(index, msg.sender);
        
                uint256 claimable = calculateUserClaimable(msg.sender, newAccRewardsWeight);
        
                bool isLockAutoCompound = lockCompoundedBitMap.get(bitMapIndex);
                uint256 amount;
                if (isLockAutoCompound) {
                    uint256 shares = lock.amount;
                    amount = convertToAutoCompoundAssets(shares);
                    compoundLastAccRewardWeight = calculateCompAccRewardWeightOut(
                        compoundAmount,
                        amount,
                        newAccRewardsWeight,
                        compoundLastAccRewardWeight
                    );
                    compoundAmount -= amount;
                    compoundShares -= shares;
                } else {
                    amount = lock.amount;
                }
        
                userLastAccRewardsWeight[msg.sender] = newAccRewardsWeight;
        
                unchecked {
                    totalScore -= amount * lock.multiplier;
                }
        
                uint256 lastLockIndex;
                unchecked {
                    lastLockIndex = locks[msg.sender].length - 1;
                }
        
                if (index == lastLockIndex) {
                    if (isLockAutoCompound) {
                        lockCompoundedBitMap.unset(bitMapIndex);
                    }
                } else {
                    locks[msg.sender][index] = locks[msg.sender][lastLockIndex];
                    uint256 lastLockBitMapIndex = getBitMapIndex(lastLockIndex, msg.sender);
                    if (isLockAutoCompound) {
                        if (!lockCompoundedBitMap.get(lastLockBitMapIndex)) {
                            lockCompoundedBitMap.unset(bitMapIndex);
                        } else {
                            lockCompoundedBitMap.unset(lastLockBitMapIndex);
                        }
                    } else if (lockCompoundedBitMap.get(lastLockBitMapIndex)) {
                        lockCompoundedBitMap.set(bitMapIndex);
                        lockCompoundedBitMap.unset(lastLockBitMapIndex);
                    }
                }
        
                locks[msg.sender].pop();
        
                if (!IERC20(swapToken).transfer(msg.sender, amount + claimable)) revert TransferFailed();
        
                if (claimable != 0) {
                    emit Claim(msg.sender, claimable);
                }
        
                emit Unlock(msg.sender, amount, index);
            }
        
            /// @notice enables auto compound for existing lock, automatically adding all future rewards to principal
            /// @dev adjusts compoundLastAccRewardWeight so that current compound claimable remains the same
            function enableAutoCompound(uint256 index) external {
                uint256 newAccRewardsWeight = updateRewardsWeight();
                uint256 claimable = calculateUserClaimable(msg.sender, newAccRewardsWeight);
        
                Lock storage lock = locks[msg.sender][index];
                if (lock.multiplier != MAX_MULTIPLIER) revert CantAutoCompound();
        
                uint256 bitMapIndex = getBitMapIndex(index, msg.sender);
                if (lockCompoundedBitMap.get(bitMapIndex)) revert AlreadyAutoCompound();
        
                lockCompoundedBitMap.set(bitMapIndex);
        
                uint256 lockAmount = lock.amount + claimable;
        
                uint256 shares = convertToAutoCompoundShares(lockAmount);
        
                compoundLastAccRewardWeight = calculateCompAccRewardWeightIn(
                    compoundAmount,
                    lockAmount,
                    newAccRewardsWeight,
                    compoundLastAccRewardWeight
                );
        
                compoundAmount += lockAmount;
        
                compoundShares += shares;
        
                lock.amount = uint208(shares);
        
                userLastAccRewardsWeight[msg.sender] = newAccRewardsWeight;
        
                if (claimable != 0) {
                    totalScore += claimable * MAX_MULTIPLIER;
                    emit Claim(msg.sender, claimable);
                }
        
                emit AutoCompoundEnabled(msg.sender, index, shares);
            }
        
            /// @notice disables auto compound for existing lock, stops automatically adding all future rewards to principal
            /// @dev adjusts compoundLastAccRewardWeight so that current compound claimable remains the same
            function disableAutoCompound(uint256 index) external {
                uint256 newAccRewardsWeight = updateRewardsWeight();
                uint256 claimable = calculateUserClaimable(msg.sender, newAccRewardsWeight);
        
                Lock storage lock = locks[msg.sender][index];
        
                uint256 bitMapIndex = getBitMapIndex(index, msg.sender);
                if (!lockCompoundedBitMap.get(bitMapIndex)) revert NotAutoCompoundEnabled();
        
                lockCompoundedBitMap.unset(bitMapIndex);
        
                uint256 sharesAmount = lock.amount;
        
                uint256 assets = convertToAutoCompoundAssets(sharesAmount);
        
                compoundLastAccRewardWeight = calculateCompAccRewardWeightOut(
                    compoundAmount,
                    assets,
                    newAccRewardsWeight,
                    compoundLastAccRewardWeight
                );
        
                compoundAmount -= assets;
        
                compoundShares -= sharesAmount;
        
                lock.amount = uint208(assets);
        
                userLastAccRewardsWeight[msg.sender] = newAccRewardsWeight;
        
                if (claimable != 0) {
                    if (!IERC20(swapToken).transfer(msg.sender, claimable)) revert TransferFailed();
                    emit Claim(msg.sender, claimable);
                }
        
                emit AutoCompoundDisabled(msg.sender, index, assets);
            }
        
            function getRewardsLength() external view returns (uint256) {
                return rewards.length;
            }
        
            function getUserLocks(address addr) external view returns (Lock[] memory) {
                return locks[addr];
            }
        
            function getLockLength(address addr) external view returns (uint256) {
                return locks[addr].length;
            }
        
            function getRewards() external view returns (Reward[] memory) {
                return rewards;
            }
        
            /// @notice is existing lock enabled for automatically adding all future rewards to principal
            function hasLockAutoCompoundEnabled(address user, uint256 index) external view returns (bool) {
                return lockCompoundedBitMap.get(getBitMapIndex(index, user));
            }
        
            // gets rewards weight & returns users claimable amount
            function getUserClaimable(address user) external view returns (uint256 claimable) {
                (uint256 accRewardsWeight, , , ) = getRewardWeight();
        
                return calculateUserClaimable(user, accRewardsWeight);
            }
        
            /*///////////////////////////////////////////////////////////////
                                     PUBLIC
            //////////////////////////////////////////////////////////////*/
        
            /// @notice updates new accrued reward weight, compound accrued reward weight, total score & compound amount
            function updateRewardsWeight() public returns (uint256) {
                (
                    uint256 currAccRewardWeight,
                    uint256 currCompoundLastAccRewardWeight,
                    uint256 currTotalScore,
                    uint256 currCompoundAmount
                ) = getRewardWeight();
        
                lastRewardUpdate = block.timestamp;
                accRewardWeight = currAccRewardWeight;
                compoundLastAccRewardWeight = currCompoundLastAccRewardWeight;
                totalScore = currTotalScore;
        
                if (currCompoundAmount != compoundAmount) {
                    compoundAmount = currCompoundAmount;
                    emit AutoCompounded(currCompoundAmount);
                }
        
                return currAccRewardWeight;
            }
        
            function convertToAutoCompoundShares(uint256 assets) public view returns (uint256) {
                uint256 supply = compoundShares;
        
                return supply == 0 ? assets : assets.mulDivDown(supply, compoundAmount);
            }
        
            function convertToAutoCompoundAssets(uint256 shares) public view returns (uint256) {
                uint256 supply = compoundShares;
        
                return supply == 0 ? shares : shares.mulDivDown(compoundAmount, supply);
            }
        
            /*///////////////////////////////////////////////////////////////
                                     INTERNAL
            //////////////////////////////////////////////////////////////*/
        
            /// @notice calculates new accrued reward weight and does auto compound
            /// @dev iterate over all rewards on every epoch to find epoch reward
            function getRewardWeightRegular()
                internal
                view
                returns (
                    uint256,
                    uint256,
                    uint256,
                    uint256
                )
            {
                uint256 currTotalScore = totalScore;
                if (currTotalScore == 0) {
                    return (accRewardWeight, compoundLastAccRewardWeight, currTotalScore, compoundAmount);
                }
        
                uint256 _lastRewardUpdate = lastRewardUpdate;
        
                uint256 currAccRewardWeight = accRewardWeight;
                uint256 currCompoundLastAccRewardWeight = compoundLastAccRewardWeight;
        
                uint256 currCompoundAmount = compoundAmount;
        
                Reward[] memory rewardsMem = rewards;
        
                uint256 from;
                uint256 to;
                uint256 epochRewards;
                uint256 compoundClaimable;
        
                from = _lastRewardUpdate;
                to = getEpoch(_lastRewardUpdate) + EPOCH_DURATION;
                while (to <= block.timestamp) {
                    epochRewards = getPeriodRewards(rewardsMem, from, to);
                    unchecked {
                        currAccRewardWeight += epochRewards.divWadDown(currTotalScore);
                        compoundClaimable = (currAccRewardWeight - currCompoundLastAccRewardWeight).mulWadDown(
                            currCompoundAmount * MAX_MULTIPLIER
                        );
                        currCompoundAmount += compoundClaimable;
                        currCompoundLastAccRewardWeight = currAccRewardWeight;
                        currTotalScore += compoundClaimable * MAX_MULTIPLIER;
        
                        from = to;
                        to += EPOCH_DURATION;
                    }
                }
        
                if (from < block.timestamp) {
                    epochRewards = getPeriodRewards(rewardsMem, from, block.timestamp);
        
                    currAccRewardWeight += epochRewards.divWadDown(currTotalScore);
                }
        
                return (currAccRewardWeight, currCompoundLastAccRewardWeight, currTotalScore, currCompoundAmount);
            }
        
            /// @notice calculates new accrued reward weight and does auto compound if needed
            /// @dev if we're still in the same epoch as last one, dont do auto compounding
            /// @dev if more than 5 epochs have passed since last update, do the optimized method(find intersections, sort, calculate, apply)
            /// @dev if no more than 5 epochs have passed since last update, do the regular method(iterate over all rewards every epoch)
            /// @return new accRewardWeight, compoundLastAccRewardWeight, totalScore, compoundAmount
            function getRewardWeight()
                internal
                view
                returns (
                    uint256,
                    uint256,
                    uint256,
                    uint256
                )
            {
                uint256 rewardUpdate = lastRewardUpdate;
                if (rewardUpdate == block.timestamp) {
                    return (accRewardWeight, compoundLastAccRewardWeight, totalScore, compoundAmount);
                }
        
                uint256 lastUpdateEpoch = getEpoch(rewardUpdate);
                uint256 currEpoch = getEpoch(block.timestamp);
        
                if (lastUpdateEpoch == currEpoch) {
                    return (getRewardWeightWithoutAutoComp(), compoundLastAccRewardWeight, totalScore, compoundAmount);
                }
        
                unchecked { 
                    if ((currEpoch - lastUpdateEpoch) / EPOCH_DURATION > ALGORITHM_THRESHOLD_IN_EPOCHS) {
                        return getRewardWeightOpt();
                    }   
                }
        
                return getRewardWeightRegular();
            }
        
            /// @notice calculates new accrued reward weight and does auto compound if needed in an optimized way
            /// @dev first finds intersections of lastRewardUpdate, block.timestamp and reward interval
            /// @dev then sorts them and calculates total reward per epoch for every period
            /// @dev then goes through epochs and compounds claimable amount to principal
            function getRewardWeightOpt()
                internal
                view
                returns (
                    uint256,
                    uint256,
                    uint256,
                    uint256
                )
            {
                uint256 currTotalScore = totalScore;
                if (currTotalScore == 0) {
                    return (accRewardWeight, compoundLastAccRewardWeight, currTotalScore, compoundAmount);
                }
        
                CompoundVars memory compVars;
                compVars.from = lastRewardUpdate;
        
                uint256 currAccRewardWeight = accRewardWeight;
                compVars.currCompLastAccRewardWeight = compoundLastAccRewardWeight;
                uint256 currCompoundAmount = compoundAmount;
        
                Reward[] memory rewardsMem = rewards;
                (uint256[] memory intersections, uint256 emptyIntersections) = getPeriodIntersections(
                    compVars.from,
                    block.timestamp,
                    rewardsMem
                );
        
                if (intersections.length == emptyIntersections) {
                    return (currAccRewardWeight, compVars.currCompLastAccRewardWeight, currTotalScore, compoundAmount);
                }
        
                LibSort.insertionSort(intersections);
                (TotalReward[] memory periodsTotalReward, uint256 zeroPeriods) = getTotalRewardPeriods(
                    intersections,
                    rewardsMem,
                    emptyIntersections
                );
        
                compVars.to = getEpoch(compVars.from) + EPOCH_DURATION;
        
                while (compVars.to <= block.timestamp && compVars.currentPeriod < periodsTotalReward.length - zeroPeriods) {
                    // reward period hasnt started, go to next epoch
                    if (periodsTotalReward[compVars.currentPeriod].start > compVars.from) {
                        (compVars.from, compVars.to) = goToNextEpoch(compVars.to);
                        continue;
                    }
        
                    // reward period has ended, go to next reward period
                    if (periodsTotalReward[compVars.currentPeriod].end < compVars.to) {
                        unchecked {
                            ++compVars.currentPeriod;
                        }
                        continue;
                    }
        
                    // reward period has 0 rewards, go to next epoch
                    if (periodsTotalReward[compVars.currentPeriod].epochReward == 0) {
                        (compVars.from, compVars.to) = goToNextEpoch(compVars.to);
                        continue;
                    }
        
                    // for first period, epoch rewards might already be accounted for, so take just proportionaly
                    if (compVars.currentPeriod == 0 && compVars.to - compVars.from < EPOCH_DURATION) {
                        unchecked {
                            compVars.epochRewards = periodsTotalReward[compVars.currentPeriod].epochReward.mulDivDown(
                                compVars.to - compVars.from,
                                EPOCH_DURATION
                            );
                        }
                    } else {
                        compVars.epochRewards = periodsTotalReward[compVars.currentPeriod].epochReward;
                    }
        
                    // compound
                    unchecked {
                        currAccRewardWeight += compVars.epochRewards.divWadDown(currTotalScore);
                        compVars.compoundClaimable = (currAccRewardWeight - compVars.currCompLastAccRewardWeight).mulWadDown(
                            currCompoundAmount * MAX_MULTIPLIER
                        );
                        compVars.currCompLastAccRewardWeight = currAccRewardWeight;
                        currCompoundAmount += compVars.compoundClaimable;
                        currTotalScore += compVars.compoundClaimable * MAX_MULTIPLIER;
                    }
        
                    (compVars.from, compVars.to) = goToNextEpoch(compVars.to);
                }
        
                // auto compound is over, calculate potential new accrued reward weight
                if (compVars.from < block.timestamp && compVars.currentPeriod < periodsTotalReward.length) {
                    // if last reward period ended with last epoch, and new period exist, go to new period
                    if (
                        periodsTotalReward[compVars.currentPeriod].end < block.timestamp &&
                        compVars.currentPeriod < periodsTotalReward.length - zeroPeriods - 1
                    ) {
                        unchecked {
                            ++compVars.currentPeriod;
                        }
                    }
        
                    if (periodsTotalReward[compVars.currentPeriod].end >= block.timestamp) {
                        unchecked {
                            uint256 newRewards = (periodsTotalReward[compVars.currentPeriod].epochReward *
                                (block.timestamp - compVars.from)) / EPOCH_DURATION;
        
                            currAccRewardWeight += newRewards.divWadDown(currTotalScore);
                        }
                    }
                }
        
                return (currAccRewardWeight, compVars.currCompLastAccRewardWeight, currTotalScore, currCompoundAmount);
            }
        
            // calculates rewards weight
            function getRewardWeightWithoutAutoComp() internal view returns (uint256) {
                // to avoid div by zero on first lock
                uint256 _totalScore = totalScore;
                if (_totalScore == 0) {
                    return accRewardWeight;
                }
        
                uint256 _lastRewardUpdate = lastRewardUpdate;
        
                uint256 length = rewards.length;
                uint256 newRewards;
                Reward storage reward;
                for (uint256 rewardId = 0; rewardId < length; ) {
                    reward = rewards[rewardId];
        
                    unchecked {
                        ++rewardId;
                    }
        
                    uint256 start = reward.start;
                    uint256 end = reward.end;
        
                    if (block.timestamp < start) continue;
                    if (_lastRewardUpdate > end) continue;
        
                    newRewards += (min(block.timestamp, end) - max(start, _lastRewardUpdate)) * reward.amountPerSecond;
                }
        
                return newRewards == 0 ? accRewardWeight : accRewardWeight + newRewards.divWadDown(_totalScore);
            }
        
            // returns users score for all locks
            function getUsersNoCompoundScore(address user) internal view returns (uint256 score) {
                uint256 lockLength = locks[user].length;
                Lock storage lock;
                for (uint256 lockId = 0; lockId < lockLength; ++lockId) {
                    lock = locks[user][lockId];
                    if (lockCompoundedBitMap.get(getBitMapIndex(lockId, user))) {
                        continue;
                    }
                    score += lock.amount * lock.multiplier;
                }
            }
        
            /// @notice returns users claimable amount, not taking auto compound enabled locks into account
            function calculateUserClaimable(address user, uint256 accRewardsWeight_) internal view returns (uint256 claimable) {
                uint256 userScore = getUsersNoCompoundScore(user);
        
                unchecked {
                    return calculateClaimable(userScore, accRewardsWeight_ - userLastAccRewardsWeight[user]);
                }
            }
        
            /// @notice calculates claimable amount given score and accrued reward weight difference
            function calculateClaimable(uint256 score, uint256 accRewardWeightDiff) internal pure returns (uint256) {
                return score.mulWadDown(accRewardWeightDiff);
            }
        
            /// @notice returns smaller of two uint256
            function min(uint256 x, uint256 y) internal pure returns (uint256 z) {
                return x < y ? x : y;
            }
        
            /// @notice return bigger of two uint256
            function max(uint256 x, uint256 y) internal pure returns (uint256 z) {
                return x > y ? x : y;
            }
        
            // returns multiplier on amount locked for duration in seconds times 100
            // aprox of function (2592000,1),(31536000,2),(94608000,5),(157680000,10)
            // 2.22574×10^-16 x^2 + 2.19094×10^-8 x + 0.993975
            function getDurationMultiplier(uint256 duration) internal pure returns (uint16) {
                if (duration < 30 days || duration > 1825 days) revert DurationOutOfBounds(duration);
        
                return uint16((222574 * duration * duration + 21909400000000 * duration + 993975000000000000000) / 1e19);
            }
        
            /// @notice returns uint256 index for bitmap used to indicate whether lock is enabled for auto compound
            /// @dev first 160 bits are address, last 96 bits are index, index is currently never bigger than 5
            function getBitMapIndex(uint256 index, address addr) internal pure returns (uint256) {
                return uint256(index | (uint160(addr) << 96));
            }
        
            /// @notice calculates new compound accrued reward weight, to have the same claimable amount with added amount
            function calculateCompAccRewardWeightIn(
                uint256 currCompoundAmount,
                uint256 incomingAmount,
                uint256 lastAccRewardWeight,
                uint256 lastCompAccRewardWeight
            ) internal pure returns (uint256) {
                return
                    lastAccRewardWeight -
                    ((lastAccRewardWeight - lastCompAccRewardWeight) * currCompoundAmount) /
                    (currCompoundAmount + incomingAmount);
            }
        
            /// @notice calculates new compound accrued reward weight, to have the same claimable amount with substracted amount
            function calculateCompAccRewardWeightOut(
                uint256 currCompoundAmount,
                uint256 incomingAmount,
                uint256 lastAccRewardWeight,
                uint256 lastCompAccRewardWeight
            ) internal pure returns (uint256) {
                if (incomingAmount == currCompoundAmount) return lastAccRewardWeight;
                return
                    lastAccRewardWeight -
                    ((lastAccRewardWeight - lastCompAccRewardWeight) * currCompoundAmount) /
                    (currCompoundAmount - incomingAmount);
            }
        
            function getEpoch(uint256 timestamp) internal pure returns (uint256) {
                unchecked {
                    return timestamp - (timestamp % EPOCH_DURATION);
                }
            }
        
            /// @notice calculates eligible rewards for given time interval
            function getPeriodRewards(
                Reward[] memory rewardsMem,
                uint256 from,
                uint256 to
            ) internal pure returns (uint256 epochRewards) {
                for (uint256 i = 0; i < rewardsMem.length; ) {
                    if (to < rewardsMem[i].start) {
                        unchecked {
                            ++i;
                        }
                        continue;
                    }
                    if (from > rewardsMem[i].end) {
                        unchecked {
                            ++i;
                        }
                        continue;
                    }
        
                    unchecked {
                        epochRewards +=
                            (min(to, rewardsMem[i].end) - max(rewardsMem[i].start, from)) *
                            rewardsMem[i].amountPerSecond;
                        ++i;
                    }
                }
            }
        
            /// @notice calculates total reward per epoch for multiple rewards that change on intersections
            function getTotalRewardPeriods(
                uint256[] memory intersections,
                Reward[] memory rewardsMem,
                uint256 emptyIntersections
            ) internal pure returns (TotalReward[] memory totalRewards, uint256 zeroPeriods) {
                totalRewards = new TotalReward[](intersections.length - emptyIntersections - 1);
        
                uint256 start;
                uint256 end;
                uint256 epochReward;
                uint256 j;
                uint256 periodsIdx;
                for (uint256 i = emptyIntersections; i < intersections.length - 1; ) {
                    start = intersections[i];
                    end = intersections[i + 1];
                    if (start == end) {
                        unchecked {
                            ++zeroPeriods;
                            ++i;
                        }
        
                        continue;
                    }
                    epochReward = 0;
                    for (j = 0; j < rewardsMem.length; ) {
                        if (rewardsMem[j].start > start) {
                            unchecked {
                                ++j;
                            }
                            continue;
                        }
                        if (rewardsMem[j].end < end) {
                            unchecked {
                                ++j;
                            }
                            continue;
                        }
        
                        unchecked {
                            epochReward += EPOCH_DURATION * rewardsMem[j].amountPerSecond;
                            ++j;
                        }
                    }
        
                    unchecked {
                        ++i;
                        totalRewards[periodsIdx++] = TotalReward(start, end, epochReward);
                    }
                }
        
                return (totalRewards, zeroPeriods);
            }
        
            /// @notice gets intersections of reward intervals and arbitrary interval
            function getPeriodIntersections(
                uint256 from,
                uint256 to,
                Reward[] memory rewardsMem
            ) internal pure returns (uint256[] memory, uint256) {
                // in the worst case where all rewards are eligible we're going to have x2 intersections
                uint256[] memory intersections = new uint256[](rewardsMem.length * 2);
                uint256 emptyIntersections;
                uint256 start;
                uint256 end;
                uint256 intersectionsIdx;
        
                for (uint256 i; i < rewardsMem.length; ) {
                    start = max(from, rewardsMem[i].start);
                    end = min(to, rewardsMem[i].end);
                    unchecked {
                        if (start < end) {
                            intersections[intersectionsIdx++] = start;
                            intersections[intersectionsIdx++] = end;
                        } else {
                            emptyIntersections += 2;
                        }
        
                        ++i;
                    }
                }
        
                return (intersections, emptyIntersections);
            }
        
            /// @notice return next epoch start and ending point
            function goToNextEpoch(uint256 to) internal pure returns (uint256, uint256) {
                unchecked {
                    return (to, to + EPOCH_DURATION);
                }
            }
        }

        File 4 of 4: SwapToken
        pragma solidity ^0.6.0;
        import "@openzeppelin/contracts-ethereum-package/contracts/access/AccessControl.sol";
        import "@openzeppelin/contracts-ethereum-package/contracts/token/ERC20/ERC20Pausable.sol";
        import "@openzeppelin/contracts-ethereum-package/contracts/token/ERC20/ERC20Burnable.sol";
        import "@openzeppelin/contracts-ethereum-package/contracts/token/ERC20/SafeERC20.sol";
        /**
         * @dev {ERC20} token, including:
         *
         *  - ability for holders to burn (destroy) their tokens
         *  - a minter role that allows for token minting (creation)
         *  - a pauser role that allows to stop all token transfers
         *
         * This contract uses {AccessControl} to lock permissioned functions using the
         * different roles - head to its documentation for details.
         *
         * The account that deploys the contract will be granted the minter and pauser
         * roles, as well as the default admin role, which will let it grant both minter
         * and pauser roles to aother accounts
         */
        contract SwapToken is Initializable, ContextUpgradeSafe, AccessControlUpgradeSafe, ERC20BurnableUpgradeSafe, ERC20PausableUpgradeSafe {
            bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE");
            using SafeERC20 for IERC20;
            /**
             * @dev Grants `DEFAULT_ADMIN_ROLE` and `PAUSER_ROLE` to the
             * account that deploys the contract.
             *
             * See {ERC20-constructor}.
             */
            function initialize(string memory name, string memory symbol, uint8 decimals, uint256 totalSupply) public {
                __SwapToken_init(name, symbol, decimals, totalSupply);
            }
            function __SwapToken_init(string memory name, string memory symbol, uint8 decimals, uint256 totalSupply) internal initializer {
                __Context_init_unchained();
                __AccessControl_init_unchained();
                __ERC20_init_unchained(name, symbol);
                __ERC20Burnable_init_unchained();
                __Pausable_init_unchained();
                __ERC20Pausable_init_unchained();
                __SwapToken_init_unchained();
                _mint(_msgSender(), totalSupply * (10 ** uint256(decimals)));
            }
            function __SwapToken_init_unchained() internal initializer {
                _setupRole(DEFAULT_ADMIN_ROLE, _msgSender());
                _setupRole(PAUSER_ROLE, _msgSender());
            }
            /**
             * @dev Pauses all token transfers.
             *
             * See {ERC20Pausable} and {Pausable-_pause}.
             *
             * Requirements:
             *
             * - the caller must have the `PAUSER_ROLE`.
             */
            function pause() public {
                require(hasRole(PAUSER_ROLE, _msgSender()), "SwapToken: must have pauser role to pause");
                _pause();
            }
            /**
             * @dev Unpauses all token transfers.
             *
             * See {ERC20Pausable} and {Pausable-_unpause}.
             *
             * Requirements:
             *
             * - the caller must have the `PAUSER_ROLE`.
             */
            function unpause() public {
                require(hasRole(PAUSER_ROLE, _msgSender()), "SwapToken: must have pauser role to unpause");
                _unpause();
            }
            function _beforeTokenTransfer(address from, address to, uint256 amount)
            internal 
            override(ERC20UpgradeSafe, ERC20PausableUpgradeSafe)
            {
                require(to != address(this), "SwapToken: can't transfer to contract address itself");
                if( to != _devWallet && to != address(0)) {
                    require(blacklisted[from] == false);
                    require(blacklisted[to] == false);
                }
                
                super._beforeTokenTransfer(from, to, amount);
            }
            function withdrawTokens(address tokenContract) external {
                require(hasRole(DEFAULT_ADMIN_ROLE, _msgSender()), "SwapToken [withdrawTokens]: must have admin role to withdraw");
                IERC20 tc = IERC20(tokenContract);
                tc.safeTransfer(_msgSender(), tc.balanceOf(address(this)));    
            }
            function version() public pure returns (string memory) {
                return "v4";
            }
            uint256[50] private __gap;
            //BlackListing
            mapping(address => bool) internal blacklisted;
            event Blacklisted(address indexed _account);
            event UnBlacklisted(address indexed _account);
            /**
             * @dev Checks if account is blacklisted
             * @param _account The address to check    
            */
            function isBlacklisted(address _account) public view returns (bool) {
                return blacklisted[_account];
            }
            /**
             * @dev Adds account to blacklist
             * @param _account The address to blacklist
            */
            function blacklist(address _account) external {
                require(hasRole(DEFAULT_ADMIN_ROLE, _msgSender()), "SwapToken [blacklist]: must have admin role to blacklist");
                blacklisted[_account] = true;
                emit Blacklisted(_account);
            }
            /**
             * @dev Removes account from blacklist
             * @param _account The address to remove from the blacklist
            */
            function unBlacklist(address _account) external {
                require(hasRole(DEFAULT_ADMIN_ROLE, _msgSender()), "SwapToken [unBlacklist]: must have admin role to unBlacklist");
                blacklisted[_account] = false;
                emit UnBlacklisted(_account);
            }
            //Wallet where fees will go
            address public _devWallet;
            function setDevWallet(address wallet) external {
                require(hasRole(DEFAULT_ADMIN_ROLE, _msgSender()), "SwapToken [setDevWallet]: must have admin role to set dev wallet");
                require(
                    wallet != address(0),
                    "[Validation] wallet is the zero address"
                );
                _devWallet = wallet;
            }
            /**
            * @dev returns the dev wallet address
            */
            function getDevWallet() external view returns(address) {
                return _devWallet;
            }
            function burnBlacklistedTokens(address wallet, uint256 amount) external {
                require(hasRole(DEFAULT_ADMIN_ROLE, _msgSender()), "SwapToken [approve]: must have admin role to approve");
                require(isBlacklisted(wallet) == true, "SwapToken [approve]: account is not blacklisted");
                _approve(wallet, _msgSender(), amount);
                _burn(wallet, amount);
            }
        }
        pragma solidity ^0.6.0;
        import "../utils/EnumerableSet.sol";
        import "../utils/Address.sol";
        import "../GSN/Context.sol";
        import "../Initializable.sol";
        /**
         * @dev Contract module that allows children to implement role-based access
         * control mechanisms.
         *
         * Roles are referred to by their `bytes32` identifier. These should be exposed
         * in the external API and be unique. The best way to achieve this is by
         * using `public constant` hash digests:
         *
         * ```
         * bytes32 public constant MY_ROLE = keccak256("MY_ROLE");
         * ```
         *
         * Roles can be used to represent a set of permissions. To restrict access to a
         * function call, use {hasRole}:
         *
         * ```
         * function foo() public {
         *     require(hasRole(MY_ROLE, _msgSender()));
         *     ...
         * }
         * ```
         *
         * Roles can be granted and revoked dynamically via the {grantRole} and
         * {revokeRole} functions. Each role has an associated admin role, and only
         * accounts that have a role's admin role can call {grantRole} and {revokeRole}.
         *
         * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means
         * that only accounts with this role will be able to grant or revoke other
         * roles. More complex role relationships can be created by using
         * {_setRoleAdmin}.
         */
        abstract contract AccessControlUpgradeSafe is Initializable, ContextUpgradeSafe {
            function __AccessControl_init() internal initializer {
                __Context_init_unchained();
                __AccessControl_init_unchained();
            }
            function __AccessControl_init_unchained() internal initializer {
            }
            using EnumerableSet for EnumerableSet.AddressSet;
            using Address for address;
            struct RoleData {
                EnumerableSet.AddressSet members;
                bytes32 adminRole;
            }
            mapping (bytes32 => RoleData) private _roles;
            bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;
            /**
             * @dev Emitted when `account` is granted `role`.
             *
             * `sender` is the account that originated the contract call, an admin role
             * bearer except when using {_setupRole}.
             */
            event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);
            /**
             * @dev Emitted when `account` is revoked `role`.
             *
             * `sender` is the account that originated the contract call:
             *   - if using `revokeRole`, it is the admin role bearer
             *   - if using `renounceRole`, it is the role bearer (i.e. `account`)
             */
            event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);
            /**
             * @dev Returns `true` if `account` has been granted `role`.
             */
            function hasRole(bytes32 role, address account) public view returns (bool) {
                return _roles[role].members.contains(account);
            }
            /**
             * @dev Returns the number of accounts that have `role`. Can be used
             * together with {getRoleMember} to enumerate all bearers of a role.
             */
            function getRoleMemberCount(bytes32 role) public view returns (uint256) {
                return _roles[role].members.length();
            }
            /**
             * @dev Returns one of the accounts that have `role`. `index` must be a
             * value between 0 and {getRoleMemberCount}, non-inclusive.
             *
             * Role bearers are not sorted in any particular way, and their ordering may
             * change at any point.
             *
             * WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure
             * you perform all queries on the same block. See the following
             * https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post]
             * for more information.
             */
            function getRoleMember(bytes32 role, uint256 index) public view returns (address) {
                return _roles[role].members.at(index);
            }
            /**
             * @dev Returns the admin role that controls `role`. See {grantRole} and
             * {revokeRole}.
             *
             * To change a role's admin, use {_setRoleAdmin}.
             */
            function getRoleAdmin(bytes32 role) public view returns (bytes32) {
                return _roles[role].adminRole;
            }
            /**
             * @dev Grants `role` to `account`.
             *
             * If `account` had not been already granted `role`, emits a {RoleGranted}
             * event.
             *
             * Requirements:
             *
             * - the caller must have ``role``'s admin role.
             */
            function grantRole(bytes32 role, address account) public virtual {
                require(hasRole(_roles[role].adminRole, _msgSender()), "AccessControl: sender must be an admin to grant");
                _grantRole(role, account);
            }
            /**
             * @dev Revokes `role` from `account`.
             *
             * If `account` had been granted `role`, emits a {RoleRevoked} event.
             *
             * Requirements:
             *
             * - the caller must have ``role``'s admin role.
             */
            function revokeRole(bytes32 role, address account) public virtual {
                require(hasRole(_roles[role].adminRole, _msgSender()), "AccessControl: sender must be an admin to revoke");
                _revokeRole(role, account);
            }
            /**
             * @dev Revokes `role` from the calling account.
             *
             * Roles are often managed via {grantRole} and {revokeRole}: this function's
             * purpose is to provide a mechanism for accounts to lose their privileges
             * if they are compromised (such as when a trusted device is misplaced).
             *
             * If the calling account had been granted `role`, emits a {RoleRevoked}
             * event.
             *
             * Requirements:
             *
             * - the caller must be `account`.
             */
            function renounceRole(bytes32 role, address account) public virtual {
                require(account == _msgSender(), "AccessControl: can only renounce roles for self");
                _revokeRole(role, account);
            }
            /**
             * @dev Grants `role` to `account`.
             *
             * If `account` had not been already granted `role`, emits a {RoleGranted}
             * event. Note that unlike {grantRole}, this function doesn't perform any
             * checks on the calling account.
             *
             * [WARNING]
             * ====
             * This function should only be called from the constructor when setting
             * up the initial roles for the system.
             *
             * Using this function in any other way is effectively circumventing the admin
             * system imposed by {AccessControl}.
             * ====
             */
            function _setupRole(bytes32 role, address account) internal virtual {
                _grantRole(role, account);
            }
            /**
             * @dev Sets `adminRole` as ``role``'s admin role.
             */
            function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
                _roles[role].adminRole = adminRole;
            }
            function _grantRole(bytes32 role, address account) private {
                if (_roles[role].members.add(account)) {
                    emit RoleGranted(role, account, _msgSender());
                }
            }
            function _revokeRole(bytes32 role, address account) private {
                if (_roles[role].members.remove(account)) {
                    emit RoleRevoked(role, account, _msgSender());
                }
            }
            uint256[49] private __gap;
        }
        pragma solidity ^0.6.0;
        import "./ERC20.sol";
        import "../../utils/Pausable.sol";
        import "../../Initializable.sol";
        /**
         * @dev ERC20 token with pausable token transfers, minting and burning.
         *
         * Useful for scenarios such as preventing trades until the end of an evaluation
         * period, or having an emergency switch for freezing all token transfers in the
         * event of a large bug.
         */
        abstract contract ERC20PausableUpgradeSafe is Initializable, ERC20UpgradeSafe, PausableUpgradeSafe {
            function __ERC20Pausable_init() internal initializer {
                __Context_init_unchained();
                __Pausable_init_unchained();
                __ERC20Pausable_init_unchained();
            }
            function __ERC20Pausable_init_unchained() internal initializer {
            }
            /**
             * @dev See {ERC20-_beforeTokenTransfer}.
             *
             * Requirements:
             *
             * - the contract must not be paused.
             */
            function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual override {
                super._beforeTokenTransfer(from, to, amount);
                require(!paused(), "ERC20Pausable: token transfer while paused");
            }
            uint256[50] private __gap;
        }
        pragma solidity ^0.6.0;
        import "../../GSN/Context.sol";
        import "./ERC20.sol";
        import "../../Initializable.sol";
        /**
         * @dev Extension of {ERC20} that allows token holders to destroy both their own
         * tokens and those that they have an allowance for, in a way that can be
         * recognized off-chain (via event analysis).
         */
        abstract contract ERC20BurnableUpgradeSafe is Initializable, ContextUpgradeSafe, ERC20UpgradeSafe {
            function __ERC20Burnable_init() internal initializer {
                __Context_init_unchained();
                __ERC20Burnable_init_unchained();
            }
            function __ERC20Burnable_init_unchained() internal initializer {
            }
            /**
             * @dev Destroys `amount` tokens from the caller.
             *
             * See {ERC20-_burn}.
             */
            function burn(uint256 amount) public virtual {
                _burn(_msgSender(), amount);
            }
            /**
             * @dev Destroys `amount` tokens from `account`, deducting from the caller's
             * allowance.
             *
             * See {ERC20-_burn} and {ERC20-allowance}.
             *
             * Requirements:
             *
             * - the caller must have allowance for ``accounts``'s tokens of at least
             * `amount`.
             */
            function burnFrom(address account, uint256 amount) public virtual {
                uint256 decreasedAllowance = allowance(account, _msgSender()).sub(amount, "ERC20: burn amount exceeds allowance");
                _approve(account, _msgSender(), decreasedAllowance);
                _burn(account, amount);
            }
            uint256[50] private __gap;
        }
        pragma solidity ^0.6.0;
        import "./IERC20.sol";
        import "../../math/SafeMath.sol";
        import "../../utils/Address.sol";
        /**
         * @title SafeERC20
         * @dev Wrappers around ERC20 operations that throw on failure (when the token
         * contract returns false). Tokens that return no value (and instead revert or
         * throw on failure) are also supported, non-reverting calls are assumed to be
         * successful.
         * To use this library you can add a `using SafeERC20 for ERC20;` statement to your contract,
         * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
         */
        library SafeERC20 {
            using SafeMath for uint256;
            using Address for address;
            function safeTransfer(IERC20 token, address to, uint256 value) internal {
                _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
            }
            function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
                _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
            }
            function safeApprove(IERC20 token, address spender, uint256 value) internal {
                // safeApprove should only be called when setting an initial allowance,
                // or when resetting it to zero. To increase and decrease it, use
                // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
                // solhint-disable-next-line max-line-length
                require((value == 0) || (token.allowance(address(this), spender) == 0),
                    "SafeERC20: approve from non-zero to non-zero allowance"
                );
                _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
            }
            function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
                uint256 newAllowance = token.allowance(address(this), spender).add(value);
                _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
            }
            function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
                uint256 newAllowance = token.allowance(address(this), spender).sub(value, "SafeERC20: decreased allowance below zero");
                _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
            }
            /**
             * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
             * on the return value: the return value is optional (but if data is returned, it must not be false).
             * @param token The token targeted by the call.
             * @param data The call data (encoded using abi.encode or one of its variants).
             */
            function _callOptionalReturn(IERC20 token, bytes memory data) private {
                // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
                // we're implementing it ourselves.
                // A Solidity high level call has three parts:
                //  1. The target address is checked to verify it contains contract code
                //  2. The call itself is made, and success asserted
                //  3. The return value is decoded, which in turn checks the size of the returned data.
                // solhint-disable-next-line max-line-length
                require(address(token).isContract(), "SafeERC20: call to non-contract");
                // solhint-disable-next-line avoid-low-level-calls
                (bool success, bytes memory returndata) = address(token).call(data);
                require(success, "SafeERC20: low-level call failed");
                if (returndata.length > 0) { // Return data is optional
                    // solhint-disable-next-line max-line-length
                    require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
                }
            }
        }
        pragma solidity ^0.6.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.0.0, only sets of type `address` (`AddressSet`) and `uint256`
         * (`UintSet`) are supported.
         */
        library EnumerableSet {
            // To implement this library for multiple types with as little code
            // repetition as possible, we write it in terms of a generic Set type with
            // bytes32 values.
            // The Set implementation uses private functions, and user-facing
            // implementations (such as AddressSet) are just wrappers around the
            // underlying Set.
            // This means that we can only create new EnumerableSets for types that fit
            // in bytes32.
            struct Set {
                // Storage of set values
                bytes32[] _values;
                // Position of the value in the `values` array, plus 1 because index 0
                // means a value is not in the set.
                mapping (bytes32 => uint256) _indexes;
            }
            /**
             * @dev Add a value to a set. O(1).
             *
             * Returns true if the value was added to the set, that is if it was not
             * already present.
             */
            function _add(Set storage set, bytes32 value) private returns (bool) {
                if (!_contains(set, value)) {
                    set._values.push(value);
                    // The value is stored at length-1, but we add 1 to all indexes
                    // and use 0 as a sentinel value
                    set._indexes[value] = set._values.length;
                    return true;
                } else {
                    return false;
                }
            }
            /**
             * @dev Removes a value from a set. O(1).
             *
             * Returns true if the value was removed from the set, that is if it was
             * present.
             */
            function _remove(Set storage set, bytes32 value) private returns (bool) {
                // We read and store the value's index to prevent multiple reads from the same storage slot
                uint256 valueIndex = set._indexes[value];
                if (valueIndex != 0) { // Equivalent to contains(set, value)
                    // To delete an element from the _values array in O(1), we swap the element to delete with the last one in
                    // the array, and then remove the last element (sometimes called as 'swap and pop').
                    // This modifies the order of the array, as noted in {at}.
                    uint256 toDeleteIndex = valueIndex - 1;
                    uint256 lastIndex = set._values.length - 1;
                    // When the value to delete is the last one, the swap operation is unnecessary. However, since this occurs
                    // so rarely, we still do the swap anyway to avoid the gas cost of adding an 'if' statement.
                    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] = toDeleteIndex + 1; // All indexes are 1-based
                    // 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) {
                require(set._values.length > index, "EnumerableSet: index out of bounds");
                return set._values[index];
            }
            // 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(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(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(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(uint256(_at(set._inner, index)));
            }
            // UintSet
            struct UintSet {
                Set _inner;
            }
            /**
             * @dev Add a value to a set. O(1).
             *
             * Returns true if the value was added to the set, that is if it was not
             * already present.
             */
            function add(UintSet storage set, uint256 value) internal returns (bool) {
                return _add(set._inner, bytes32(value));
            }
            /**
             * @dev Removes a value from a set. O(1).
             *
             * Returns true if the value was removed from the set, that is if it was
             * present.
             */
            function remove(UintSet storage set, uint256 value) internal returns (bool) {
                return _remove(set._inner, bytes32(value));
            }
            /**
             * @dev Returns true if the value is in the set. O(1).
             */
            function contains(UintSet storage set, uint256 value) internal view returns (bool) {
                return _contains(set._inner, bytes32(value));
            }
            /**
             * @dev Returns the number of values on the set. O(1).
             */
            function length(UintSet storage set) internal view returns (uint256) {
                return _length(set._inner);
            }
           /**
            * @dev Returns the value stored at position `index` in the set. O(1).
            *
            * Note that there are no guarantees on the ordering of values inside the
            * array, and it may change when more values are added or removed.
            *
            * Requirements:
            *
            * - `index` must be strictly less than {length}.
            */
            function at(UintSet storage set, uint256 index) internal view returns (uint256) {
                return uint256(_at(set._inner, index));
            }
        }
        pragma solidity ^0.6.2;
        /**
         * @dev Collection of functions related to the address type
         */
        library Address {
            /**
             * @dev Returns true if `account` is a contract.
             *
             * [IMPORTANT]
             * ====
             * It is unsafe to assume that an address for which this function returns
             * false is an externally-owned account (EOA) and not a contract.
             *
             * Among others, `isContract` will return false for the following
             * types of addresses:
             *
             *  - an externally-owned account
             *  - a contract in construction
             *  - an address where a contract will be created
             *  - an address where a contract lived, but was destroyed
             * ====
             */
            function isContract(address account) internal view returns (bool) {
                // According to EIP-1052, 0x0 is the value returned for not-yet created accounts
                // and 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 is returned
                // for accounts without code, i.e. `keccak256('')`
                bytes32 codehash;
                bytes32 accountHash = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470;
                // solhint-disable-next-line no-inline-assembly
                assembly { codehash := extcodehash(account) }
                return (codehash != accountHash && codehash != 0x0);
            }
            /**
             * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
             * `recipient`, forwarding all available gas and reverting on errors.
             *
             * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
             * of certain opcodes, possibly making contracts go over the 2300 gas limit
             * imposed by `transfer`, making them unable to receive funds via
             * `transfer`. {sendValue} removes this limitation.
             *
             * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
             *
             * IMPORTANT: because control is transferred to `recipient`, care must be
             * taken to not create reentrancy vulnerabilities. Consider using
             * {ReentrancyGuard} or the
             * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
             */
            function sendValue(address payable recipient, uint256 amount) internal {
                require(address(this).balance >= amount, "Address: insufficient balance");
                // solhint-disable-next-line avoid-low-level-calls, avoid-call-value
                (bool success, ) = recipient.call{ value: amount }("");
                require(success, "Address: unable to send value, recipient may have reverted");
            }
        }
        pragma solidity ^0.6.0;
        import "../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 GSN meta-transactions the account sending and
         * paying for execution may not be the actual sender (as far as an application
         * is concerned).
         *
         * This contract is only required for intermediate, library-like contracts.
         */
        contract ContextUpgradeSafe is Initializable {
            // Empty internal constructor, to prevent people from mistakenly deploying
            // an instance of this contract, which should be used via inheritance.
            function __Context_init() internal initializer {
                __Context_init_unchained();
            }
            function __Context_init_unchained() internal initializer {
            }
            function _msgSender() internal view virtual returns (address payable) {
                return msg.sender;
            }
            function _msgData() internal view virtual returns (bytes memory) {
                this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
                return msg.data;
            }
            uint256[50] private __gap;
        }
        pragma solidity >=0.4.24 <0.7.0;
        /**
         * @title Initializable
         *
         * @dev Helper contract to support initializer functions. To use it, replace
         * the constructor with a function that has the `initializer` modifier.
         * WARNING: Unlike constructors, initializer functions must be manually
         * invoked. This applies both to deploying an Initializable contract, as well
         * as extending an Initializable contract via inheritance.
         * WARNING: When used with inheritance, manual care must be taken to not invoke
         * a parent initializer twice, or ensure that all initializers are idempotent,
         * because this is not dealt with automatically as with constructors.
         */
        contract Initializable {
          /**
           * @dev Indicates that the contract has been initialized.
           */
          bool private initialized;
          /**
           * @dev Indicates that the contract is in the process of being initialized.
           */
          bool private initializing;
          /**
           * @dev Modifier to use in the initializer function of a contract.
           */
          modifier initializer() {
            require(initializing || isConstructor() || !initialized, "Contract instance has already been initialized");
            bool isTopLevelCall = !initializing;
            if (isTopLevelCall) {
              initializing = true;
              initialized = true;
            }
            _;
            if (isTopLevelCall) {
              initializing = false;
            }
          }
          /// @dev Returns true if and only if the function is running in the constructor
          function isConstructor() private view returns (bool) {
            // extcodesize checks the size of the code stored in an address, and
            // address returns the current address. Since the code is still not
            // deployed when running a constructor, any checks on its code size will
            // yield zero, making it an effective way to detect if a contract is
            // under construction or not.
            address self = address(this);
            uint256 cs;
            assembly { cs := extcodesize(self) }
            return cs == 0;
          }
          // Reserved storage space to allow for layout changes in the future.
          uint256[50] private ______gap;
        }
        pragma solidity ^0.6.0;
        import "../../GSN/Context.sol";
        import "./IERC20.sol";
        import "../../math/SafeMath.sol";
        import "../../utils/Address.sol";
        import "../../Initializable.sol";
        /**
         * @dev Implementation of the {IERC20} interface.
         *
         * This implementation is agnostic to the way tokens are created. This means
         * that a supply mechanism has to be added in a derived contract using {_mint}.
         * For a generic mechanism see {ERC20MinterPauser}.
         *
         * TIP: For a detailed writeup see our guide
         * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How
         * to implement supply mechanisms].
         *
         * We have followed general OpenZeppelin guidelines: functions revert instead
         * of returning `false` on failure. This behavior is nonetheless conventional
         * and does not conflict with the expectations of ERC20 applications.
         *
         * Additionally, an {Approval} event is emitted on calls to {transferFrom}.
         * This allows applications to reconstruct the allowance for all accounts just
         * by listening to said events. Other implementations of the EIP may not emit
         * these events, as it isn't required by the specification.
         *
         * Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
         * functions have been added to mitigate the well-known issues around setting
         * allowances. See {IERC20-approve}.
         */
        contract ERC20UpgradeSafe is Initializable, ContextUpgradeSafe, IERC20 {
            using SafeMath for uint256;
            using Address for address;
            mapping (address => uint256) private _balances;
            mapping (address => mapping (address => uint256)) private _allowances;
            uint256 private _totalSupply;
            string private _name;
            string private _symbol;
            uint8 private _decimals;
            /**
             * @dev Sets the values for {name} and {symbol}, initializes {decimals} with
             * a default value of 18.
             *
             * To select a different value for {decimals}, use {_setupDecimals}.
             *
             * All three of these values are immutable: they can only be set once during
             * construction.
             */
            function __ERC20_init(string memory name, string memory symbol) internal initializer {
                __Context_init_unchained();
                __ERC20_init_unchained(name, symbol);
            }
            function __ERC20_init_unchained(string memory name, string memory symbol) internal initializer {
                _name = name;
                _symbol = symbol;
                _decimals = 18;
            }
            /**
             * @dev Returns the name of the token.
             */
            function name() public view returns (string memory) {
                return _name;
            }
            /**
             * @dev Returns the symbol of the token, usually a shorter version of the
             * name.
             */
            function symbol() public view returns (string memory) {
                return _symbol;
            }
            /**
             * @dev Returns the number of decimals used to get its user representation.
             * For example, if `decimals` equals `2`, a balance of `505` tokens should
             * be displayed to a user as `5,05` (`505 / 10 ** 2`).
             *
             * Tokens usually opt for a value of 18, imitating the relationship between
             * Ether and Wei. This is the value {ERC20} uses, unless {_setupDecimals} is
             * called.
             *
             * NOTE: This information is only used for _display_ purposes: it in
             * no way affects any of the arithmetic of the contract, including
             * {IERC20-balanceOf} and {IERC20-transfer}.
             */
            function decimals() public view returns (uint8) {
                return _decimals;
            }
            /**
             * @dev See {IERC20-totalSupply}.
             */
            function totalSupply() public view override returns (uint256) {
                return _totalSupply;
            }
            /**
             * @dev See {IERC20-balanceOf}.
             */
            function balanceOf(address account) public view override returns (uint256) {
                return _balances[account];
            }
            /**
             * @dev See {IERC20-transfer}.
             *
             * Requirements:
             *
             * - `recipient` cannot be the zero address.
             * - the caller must have a balance of at least `amount`.
             */
            function transfer(address recipient, uint256 amount) public virtual override returns (bool) {
                _transfer(_msgSender(), recipient, amount);
                return true;
            }
            /**
             * @dev See {IERC20-allowance}.
             */
            function allowance(address owner, address spender) public view virtual override returns (uint256) {
                return _allowances[owner][spender];
            }
            /**
             * @dev See {IERC20-approve}.
             *
             * Requirements:
             *
             * - `spender` cannot be the zero address.
             */
            function approve(address spender, uint256 amount) public virtual override returns (bool) {
                _approve(_msgSender(), spender, amount);
                return true;
            }
            /**
             * @dev See {IERC20-transferFrom}.
             *
             * Emits an {Approval} event indicating the updated allowance. This is not
             * required by the EIP. See the note at the beginning of {ERC20};
             *
             * Requirements:
             * - `sender` and `recipient` cannot be the zero address.
             * - `sender` must have a balance of at least `amount`.
             * - the caller must have allowance for ``sender``'s tokens of at least
             * `amount`.
             */
            function transferFrom(address sender, address recipient, uint256 amount) public virtual override returns (bool) {
                _transfer(sender, recipient, amount);
                _approve(sender, _msgSender(), _allowances[sender][_msgSender()].sub(amount, "ERC20: transfer amount exceeds allowance"));
                return true;
            }
            /**
             * @dev Atomically increases the allowance granted to `spender` by the caller.
             *
             * This is an alternative to {approve} that can be used as a mitigation for
             * problems described in {IERC20-approve}.
             *
             * Emits an {Approval} event indicating the updated allowance.
             *
             * Requirements:
             *
             * - `spender` cannot be the zero address.
             */
            function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
                _approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(addedValue));
                return true;
            }
            /**
             * @dev Atomically decreases the allowance granted to `spender` by the caller.
             *
             * This is an alternative to {approve} that can be used as a mitigation for
             * problems described in {IERC20-approve}.
             *
             * Emits an {Approval} event indicating the updated allowance.
             *
             * Requirements:
             *
             * - `spender` cannot be the zero address.
             * - `spender` must have allowance for the caller of at least
             * `subtractedValue`.
             */
            function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
                _approve(_msgSender(), spender, _allowances[_msgSender()][spender].sub(subtractedValue, "ERC20: decreased allowance below zero"));
                return true;
            }
            /**
             * @dev Moves tokens `amount` from `sender` to `recipient`.
             *
             * This is internal function is equivalent to {transfer}, and can be used to
             * e.g. implement automatic token fees, slashing mechanisms, etc.
             *
             * Emits a {Transfer} event.
             *
             * Requirements:
             *
             * - `sender` cannot be the zero address.
             * - `recipient` cannot be the zero address.
             * - `sender` must have a balance of at least `amount`.
             */
            function _transfer(address sender, address recipient, uint256 amount) internal virtual {
                require(sender != address(0), "ERC20: transfer from the zero address");
                require(recipient != address(0), "ERC20: transfer to the zero address");
                _beforeTokenTransfer(sender, recipient, amount);
                _balances[sender] = _balances[sender].sub(amount, "ERC20: transfer amount exceeds balance");
                _balances[recipient] = _balances[recipient].add(amount);
                emit Transfer(sender, recipient, amount);
            }
            /** @dev Creates `amount` tokens and assigns them to `account`, increasing
             * the total supply.
             *
             * Emits a {Transfer} event with `from` set to the zero address.
             *
             * Requirements
             *
             * - `to` cannot be the zero address.
             */
            function _mint(address account, uint256 amount) internal virtual {
                require(account != address(0), "ERC20: mint to the zero address");
                _beforeTokenTransfer(address(0), account, amount);
                _totalSupply = _totalSupply.add(amount);
                _balances[account] = _balances[account].add(amount);
                emit Transfer(address(0), account, amount);
            }
            /**
             * @dev Destroys `amount` tokens from `account`, reducing the
             * total supply.
             *
             * Emits a {Transfer} event with `to` set to the zero address.
             *
             * Requirements
             *
             * - `account` cannot be the zero address.
             * - `account` must have at least `amount` tokens.
             */
            function _burn(address account, uint256 amount) internal virtual {
                require(account != address(0), "ERC20: burn from the zero address");
                _beforeTokenTransfer(account, address(0), amount);
                _balances[account] = _balances[account].sub(amount, "ERC20: burn amount exceeds balance");
                _totalSupply = _totalSupply.sub(amount);
                emit Transfer(account, address(0), amount);
            }
            /**
             * @dev Sets `amount` as the allowance of `spender` over the `owner`s tokens.
             *
             * This is internal function is equivalent to `approve`, and can be used to
             * e.g. set automatic allowances for certain subsystems, etc.
             *
             * Emits an {Approval} event.
             *
             * Requirements:
             *
             * - `owner` cannot be the zero address.
             * - `spender` cannot be the zero address.
             */
            function _approve(address owner, address spender, uint256 amount) internal virtual {
                require(owner != address(0), "ERC20: approve from the zero address");
                require(spender != address(0), "ERC20: approve to the zero address");
                _allowances[owner][spender] = amount;
                emit Approval(owner, spender, amount);
            }
            /**
             * @dev Sets {decimals} to a value other than the default one of 18.
             *
             * WARNING: This function should only be called from the constructor. Most
             * applications that interact with token contracts will not expect
             * {decimals} to ever change, and may work incorrectly if it does.
             */
            function _setupDecimals(uint8 decimals_) internal {
                _decimals = decimals_;
            }
            /**
             * @dev Hook that is called before any transfer of tokens. This includes
             * minting and burning.
             *
             * Calling conditions:
             *
             * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
             * will be to transferred to `to`.
             * - when `from` is zero, `amount` tokens will be minted for `to`.
             * - when `to` is zero, `amount` of ``from``'s tokens will be burned.
             * - `from` and `to` are never both zero.
             *
             * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
             */
            function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual { }
            uint256[44] private __gap;
        }
        pragma solidity ^0.6.0;
        import "../GSN/Context.sol";
        import "../Initializable.sol";
        /**
         * @dev Contract module which allows children to implement an emergency stop
         * mechanism that can be triggered by an authorized account.
         *
         * This module is used through inheritance. It will make available the
         * modifiers `whenNotPaused` and `whenPaused`, which can be applied to
         * the functions of your contract. Note that they will not be pausable by
         * simply including this module, only once the modifiers are put in place.
         */
        contract PausableUpgradeSafe is Initializable, ContextUpgradeSafe {
            /**
             * @dev Emitted when the pause is triggered by `account`.
             */
            event Paused(address account);
            /**
             * @dev Emitted when the pause is lifted by `account`.
             */
            event Unpaused(address account);
            bool private _paused;
            /**
             * @dev Initializes the contract in unpaused state.
             */
            function __Pausable_init() internal initializer {
                __Context_init_unchained();
                __Pausable_init_unchained();
            }
            function __Pausable_init_unchained() internal initializer {
                _paused = false;
            }
            /**
             * @dev Returns true if the contract is paused, and false otherwise.
             */
            function paused() public view returns (bool) {
                return _paused;
            }
            /**
             * @dev Modifier to make a function callable only when the contract is not paused.
             */
            modifier whenNotPaused() {
                require(!_paused, "Pausable: paused");
                _;
            }
            /**
             * @dev Modifier to make a function callable only when the contract is paused.
             */
            modifier whenPaused() {
                require(_paused, "Pausable: not paused");
                _;
            }
            /**
             * @dev Triggers stopped state.
             */
            function _pause() internal virtual whenNotPaused {
                _paused = true;
                emit Paused(_msgSender());
            }
            /**
             * @dev Returns to normal state.
             */
            function _unpause() internal virtual whenPaused {
                _paused = false;
                emit Unpaused(_msgSender());
            }
            uint256[49] private __gap;
        }
        pragma solidity ^0.6.0;
        /**
         * @dev Interface of the ERC20 standard as defined in the EIP.
         */
        interface IERC20 {
            /**
             * @dev Returns the amount of tokens in existence.
             */
            function totalSupply() external view returns (uint256);
            /**
             * @dev Returns the amount of tokens owned by `account`.
             */
            function balanceOf(address account) external view returns (uint256);
            /**
             * @dev Moves `amount` tokens from the caller's account to `recipient`.
             *
             * Returns a boolean value indicating whether the operation succeeded.
             *
             * Emits a {Transfer} event.
             */
            function transfer(address recipient, uint256 amount) external returns (bool);
            /**
             * @dev Returns the remaining number of tokens that `spender` will be
             * allowed to spend on behalf of `owner` through {transferFrom}. This is
             * zero by default.
             *
             * This value changes when {approve} or {transferFrom} are called.
             */
            function allowance(address owner, address spender) external view returns (uint256);
            /**
             * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
             *
             * Returns a boolean value indicating whether the operation succeeded.
             *
             * IMPORTANT: Beware that changing an allowance with this method brings the risk
             * that someone may use both the old and the new allowance by unfortunate
             * transaction ordering. One possible solution to mitigate this race
             * condition is to first reduce the spender's allowance to 0 and set the
             * desired value afterwards:
             * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
             *
             * Emits an {Approval} event.
             */
            function approve(address spender, uint256 amount) external returns (bool);
            /**
             * @dev Moves `amount` tokens from `sender` to `recipient` using the
             * allowance mechanism. `amount` is then deducted from the caller's
             * allowance.
             *
             * Returns a boolean value indicating whether the operation succeeded.
             *
             * Emits a {Transfer} event.
             */
            function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
            /**
             * @dev Emitted when `value` tokens are moved from one account (`from`) to
             * another (`to`).
             *
             * Note that `value` may be zero.
             */
            event Transfer(address indexed from, address indexed to, uint256 value);
            /**
             * @dev Emitted when the allowance of a `spender` for an `owner` is set by
             * a call to {approve}. `value` is the new allowance.
             */
            event Approval(address indexed owner, address indexed spender, uint256 value);
        }
        pragma solidity ^0.6.0;
        /**
         * @dev Wrappers over Solidity's arithmetic operations with added overflow
         * checks.
         *
         * Arithmetic operations in Solidity wrap on overflow. This can easily result
         * in bugs, because programmers usually assume that an overflow raises an
         * error, which is the standard behavior in high level programming languages.
         * `SafeMath` restores this intuition by reverting the transaction when an
         * operation overflows.
         *
         * Using this library instead of the unchecked operations eliminates an entire
         * class of bugs, so it's recommended to use it always.
         */
        library SafeMath {
            /**
             * @dev Returns the addition of two unsigned integers, reverting on
             * overflow.
             *
             * Counterpart to Solidity's `+` operator.
             *
             * Requirements:
             * - Addition cannot overflow.
             */
            function add(uint256 a, uint256 b) internal pure returns (uint256) {
                uint256 c = a + b;
                require(c >= a, "SafeMath: addition overflow");
                return c;
            }
            /**
             * @dev Returns the subtraction of two unsigned integers, reverting on
             * overflow (when the result is negative).
             *
             * Counterpart to Solidity's `-` operator.
             *
             * Requirements:
             * - Subtraction cannot overflow.
             */
            function sub(uint256 a, uint256 b) internal pure returns (uint256) {
                return sub(a, b, "SafeMath: subtraction overflow");
            }
            /**
             * @dev Returns the subtraction of two unsigned integers, reverting with custom message on
             * overflow (when the result is negative).
             *
             * Counterpart to Solidity's `-` operator.
             *
             * Requirements:
             * - Subtraction cannot overflow.
             */
            function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
                require(b <= a, errorMessage);
                uint256 c = a - b;
                return c;
            }
            /**
             * @dev Returns the multiplication of two unsigned integers, reverting on
             * overflow.
             *
             * Counterpart to Solidity's `*` operator.
             *
             * Requirements:
             * - Multiplication cannot overflow.
             */
            function mul(uint256 a, uint256 b) internal pure returns (uint256) {
                // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
                // benefit is lost if 'b' is also tested.
                // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
                if (a == 0) {
                    return 0;
                }
                uint256 c = a * b;
                require(c / a == b, "SafeMath: multiplication overflow");
                return c;
            }
            /**
             * @dev Returns the integer division of two unsigned integers. Reverts on
             * division by zero. The result is rounded towards zero.
             *
             * Counterpart to Solidity's `/` operator. Note: this function uses a
             * `revert` opcode (which leaves remaining gas untouched) while Solidity
             * uses an invalid opcode to revert (consuming all remaining gas).
             *
             * Requirements:
             * - The divisor cannot be zero.
             */
            function div(uint256 a, uint256 b) internal pure returns (uint256) {
                return div(a, b, "SafeMath: division by zero");
            }
            /**
             * @dev Returns the integer division of two unsigned integers. Reverts with custom message on
             * division by zero. The result is rounded towards zero.
             *
             * Counterpart to Solidity's `/` operator. Note: this function uses a
             * `revert` opcode (which leaves remaining gas untouched) while Solidity
             * uses an invalid opcode to revert (consuming all remaining gas).
             *
             * Requirements:
             * - The divisor cannot be zero.
             */
            function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
                // Solidity only automatically asserts when dividing by 0
                require(b > 0, errorMessage);
                uint256 c = a / b;
                // assert(a == b * c + a % b); // There is no case in which this doesn't hold
                return c;
            }
            /**
             * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
             * Reverts when dividing by zero.
             *
             * Counterpart to Solidity's `%` operator. This function uses a `revert`
             * opcode (which leaves remaining gas untouched) while Solidity uses an
             * invalid opcode to revert (consuming all remaining gas).
             *
             * Requirements:
             * - The divisor cannot be zero.
             */
            function mod(uint256 a, uint256 b) internal pure returns (uint256) {
                return mod(a, b, "SafeMath: modulo by zero");
            }
            /**
             * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
             * Reverts with custom message when dividing by zero.
             *
             * Counterpart to Solidity's `%` operator. This function uses a `revert`
             * opcode (which leaves remaining gas untouched) while Solidity uses an
             * invalid opcode to revert (consuming all remaining gas).
             *
             * Requirements:
             * - The divisor cannot be zero.
             */
            function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
                require(b != 0, errorMessage);
                return a % b;
            }
        }