ETH Price: $2,251.02 (+2.28%)

Transaction Decoder

Block:
20308117 at Jul-14-2024 11:46:11 PM +UTC
Transaction Fee:
0.000373464367337425 ETH $0.84
Gas Used:
147,175 Gas / 2.537553031 Gwei

Emitted Events:

232 ChimpzeeCoin.Transfer( from=[Receiver] TransparentUpgradeableProxy, to=[Sender] 0x0f12d629f5bddc675ce28a174b1a18a5067bd437, value=2000000000000000000000000 )
233 TransparentUpgradeableProxy.0x6352c5382c4a4578e712449ca65e83cdb392d045dfcf1cad9615189db2da244b( 0x6352c5382c4a4578e712449ca65e83cdb392d045dfcf1cad9615189db2da244b, 0x0000000000000000000000000f12d629f5bddc675ce28a174b1a18a5067bd437, 00000000000000000000000000000000000000000001a784379d99db42000000 )

Account State Difference:

  Address   Before After State Difference Code
0x0F12D629...5067bD437
0.009833980772436149 Eth
Nonce: 116
0.009460516405098724 Eth
Nonce: 117
0.000373464367337425
0x1161aB55...6891009FD
(beaverbuild)
14.481944900057042217 Eth14.482075193026353967 Eth0.00013029296931175
0x9e6396e7...286a42649

Execution Trace

TransparentUpgradeableProxy.2e1a7d4d( )
  • ChimpzeeTokenStakeV2.withdraw( _amount=2000000000000000000000000 )
    • ChimpzeeCoin.transfer( to=0x0F12D629F5BdDC675Ce28a174B1A18a5067bD437, amount=2000000000000000000000000 ) => ( True )
      File 1 of 3: TransparentUpgradeableProxy
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)
      pragma solidity ^0.8.20;
      import {Context} from "../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.
       *
       * The initial owner is set to the address provided by the deployer. 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;
          /**
           * @dev The caller account is not authorized to perform an operation.
           */
          error OwnableUnauthorizedAccount(address account);
          /**
           * @dev The owner is not a valid owner account. (eg. `address(0)`)
           */
          error OwnableInvalidOwner(address owner);
          event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
          /**
           * @dev Initializes the contract setting the address provided by the deployer as the initial owner.
           */
          constructor(address initialOwner) {
              if (initialOwner == address(0)) {
                  revert OwnableInvalidOwner(address(0));
              }
              _transferOwnership(initialOwner);
          }
          /**
           * @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 {
              if (owner() != _msgSender()) {
                  revert OwnableUnauthorizedAccount(_msgSender());
              }
          }
          /**
           * @dev Leaves the contract without owner. It will not be possible to call
           * `onlyOwner` functions. Can only be called by the current owner.
           *
           * NOTE: Renouncing ownership will leave the contract without an owner,
           * thereby disabling 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 {
              if (newOwner == address(0)) {
                  revert OwnableInvalidOwner(address(0));
              }
              _transferOwnership(newOwner);
          }
          /**
           * @dev Transfers ownership of the contract to a new account (`newOwner`).
           * Internal function without access restriction.
           */
          function _transferOwnership(address newOwner) internal virtual {
              address oldOwner = _owner;
              _owner = newOwner;
              emit OwnershipTransferred(oldOwner, newOwner);
          }
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC1967.sol)
      pragma solidity ^0.8.20;
      /**
       * @dev ERC-1967: Proxy Storage Slots. This interface contains the events defined in the ERC.
       */
      interface IERC1967 {
          /**
           * @dev Emitted when the implementation is upgraded.
           */
          event Upgraded(address indexed implementation);
          /**
           * @dev Emitted when the admin account has changed.
           */
          event AdminChanged(address previousAdmin, address newAdmin);
          /**
           * @dev Emitted when the beacon is changed.
           */
          event BeaconUpgraded(address indexed beacon);
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts (last updated v5.0.0) (proxy/beacon/BeaconProxy.sol)
      pragma solidity ^0.8.20;
      import {IBeacon} from "./IBeacon.sol";
      import {Proxy} from "../Proxy.sol";
      import {ERC1967Utils} from "../ERC1967/ERC1967Utils.sol";
      /**
       * @dev This contract implements a proxy that gets the implementation address for each call from an {UpgradeableBeacon}.
       *
       * The beacon address can only be set once during construction, and cannot be changed afterwards. It is stored in an
       * immutable variable to avoid unnecessary storage reads, and also in the beacon storage slot specified by
       * https://eips.ethereum.org/EIPS/eip-1967[EIP1967] so that it can be accessed externally.
       *
       * CAUTION: Since the beacon address can never be changed, you must ensure that you either control the beacon, or trust
       * the beacon to not upgrade the implementation maliciously.
       *
       * IMPORTANT: Do not use the implementation logic to modify the beacon storage slot. Doing so would leave the proxy in
       * an inconsistent state where the beacon storage slot does not match the beacon address.
       */
      contract BeaconProxy is Proxy {
          // An immutable address for the beacon to avoid unnecessary SLOADs before each delegate call.
          address private immutable _beacon;
          /**
           * @dev Initializes the proxy with `beacon`.
           *
           * If `data` is nonempty, it's used as data in a delegate call to the implementation returned by the beacon. This
           * will typically be an encoded function call, and allows initializing the storage of the proxy like a Solidity
           * constructor.
           *
           * Requirements:
           *
           * - `beacon` must be a contract with the interface {IBeacon}.
           * - If `data` is empty, `msg.value` must be zero.
           */
          constructor(address beacon, bytes memory data) payable {
              ERC1967Utils.upgradeBeaconToAndCall(beacon, data);
              _beacon = beacon;
          }
          /**
           * @dev Returns the current implementation address of the associated beacon.
           */
          function _implementation() internal view virtual override returns (address) {
              return IBeacon(_getBeacon()).implementation();
          }
          /**
           * @dev Returns the beacon.
           */
          function _getBeacon() internal view virtual returns (address) {
              return _beacon;
          }
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts (last updated v5.0.0) (proxy/beacon/IBeacon.sol)
      pragma solidity ^0.8.20;
      /**
       * @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.
           *
           * {UpgradeableBeacon} will check that this address is a contract.
           */
          function implementation() external view returns (address);
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts (last updated v5.0.0) (proxy/beacon/UpgradeableBeacon.sol)
      pragma solidity ^0.8.20;
      import {IBeacon} from "./IBeacon.sol";
      import {Ownable} from "../../access/Ownable.sol";
      /**
       * @dev This contract is used in conjunction with one or more instances of {BeaconProxy} to determine their
       * implementation contract, which is where they will delegate all function calls.
       *
       * An owner is able to change the implementation the beacon points to, thus upgrading the proxies that use this beacon.
       */
      contract UpgradeableBeacon is IBeacon, Ownable {
          address private _implementation;
          /**
           * @dev The `implementation` of the beacon is invalid.
           */
          error BeaconInvalidImplementation(address implementation);
          /**
           * @dev Emitted when the implementation returned by the beacon is changed.
           */
          event Upgraded(address indexed implementation);
          /**
           * @dev Sets the address of the initial implementation, and the initial owner who can upgrade the beacon.
           */
          constructor(address implementation_, address initialOwner) Ownable(initialOwner) {
              _setImplementation(implementation_);
          }
          /**
           * @dev Returns the current implementation address.
           */
          function implementation() public view virtual returns (address) {
              return _implementation;
          }
          /**
           * @dev Upgrades the beacon to a new implementation.
           *
           * Emits an {Upgraded} event.
           *
           * Requirements:
           *
           * - msg.sender must be the owner of the contract.
           * - `newImplementation` must be a contract.
           */
          function upgradeTo(address newImplementation) public virtual onlyOwner {
              _setImplementation(newImplementation);
          }
          /**
           * @dev Sets the implementation contract address for this beacon
           *
           * Requirements:
           *
           * - `newImplementation` must be a contract.
           */
          function _setImplementation(address newImplementation) private {
              if (newImplementation.code.length == 0) {
                  revert BeaconInvalidImplementation(newImplementation);
              }
              _implementation = newImplementation;
              emit Upgraded(newImplementation);
          }
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts (last updated v5.0.0) (proxy/ERC1967/ERC1967Proxy.sol)
      pragma solidity ^0.8.20;
      import {Proxy} from "../Proxy.sol";
      import {ERC1967Utils} from "./ERC1967Utils.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 {
          /**
           * @dev Initializes the upgradeable proxy with an initial implementation specified by `implementation`.
           *
           * If `_data` is nonempty, it's used as data in a delegate call to `implementation`. This will typically be an
           * encoded function call, and allows initializing the storage of the proxy like a Solidity constructor.
           *
           * Requirements:
           *
           * - If `data` is empty, `msg.value` must be zero.
           */
          constructor(address implementation, bytes memory _data) payable {
              ERC1967Utils.upgradeToAndCall(implementation, _data);
          }
          /**
           * @dev Returns the current implementation address.
           *
           * 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() internal view virtual override returns (address) {
              return ERC1967Utils.getImplementation();
          }
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts (last updated v5.0.0) (proxy/ERC1967/ERC1967Utils.sol)
      pragma solidity ^0.8.20;
      import {IBeacon} from "../beacon/IBeacon.sol";
      import {Address} from "../../utils/Address.sol";
      import {StorageSlot} from "../../utils/StorageSlot.sol";
      /**
       * @dev This abstract contract provides getters and event emitting update functions for
       * https://eips.ethereum.org/EIPS/eip-1967[EIP1967] slots.
       */
      library ERC1967Utils {
          // We re-declare ERC-1967 events here because they can't be used directly from IERC1967.
          // This will be fixed in Solidity 0.8.21. At that point we should remove these events.
          /**
           * @dev Emitted when the implementation is upgraded.
           */
          event Upgraded(address indexed implementation);
          /**
           * @dev Emitted when the admin account has changed.
           */
          event AdminChanged(address previousAdmin, address newAdmin);
          /**
           * @dev Emitted when the beacon is changed.
           */
          event BeaconUpgraded(address indexed beacon);
          /**
           * @dev Storage slot with the address of the current implementation.
           * This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1.
           */
          // solhint-disable-next-line private-vars-leading-underscore
          bytes32 internal constant IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
          /**
           * @dev The `implementation` of the proxy is invalid.
           */
          error ERC1967InvalidImplementation(address implementation);
          /**
           * @dev The `admin` of the proxy is invalid.
           */
          error ERC1967InvalidAdmin(address admin);
          /**
           * @dev The `beacon` of the proxy is invalid.
           */
          error ERC1967InvalidBeacon(address beacon);
          /**
           * @dev An upgrade function sees `msg.value > 0` that may be lost.
           */
          error ERC1967NonPayable();
          /**
           * @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 {
              if (newImplementation.code.length == 0) {
                  revert ERC1967InvalidImplementation(newImplementation);
              }
              StorageSlot.getAddressSlot(IMPLEMENTATION_SLOT).value = newImplementation;
          }
          /**
           * @dev Performs implementation upgrade with additional setup call if data is nonempty.
           * This function is payable only if the setup call is performed, otherwise `msg.value` is rejected
           * to avoid stuck value in the contract.
           *
           * Emits an {IERC1967-Upgraded} event.
           */
          function upgradeToAndCall(address newImplementation, bytes memory data) internal {
              _setImplementation(newImplementation);
              emit Upgraded(newImplementation);
              if (data.length > 0) {
                  Address.functionDelegateCall(newImplementation, data);
              } else {
                  _checkNonPayable();
              }
          }
          /**
           * @dev Storage slot with the admin of the contract.
           * This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1.
           */
          // solhint-disable-next-line private-vars-leading-underscore
          bytes32 internal constant ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;
          /**
           * @dev Returns the current admin.
           *
           * 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 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 {
              if (newAdmin == address(0)) {
                  revert ERC1967InvalidAdmin(address(0));
              }
              StorageSlot.getAddressSlot(ADMIN_SLOT).value = newAdmin;
          }
          /**
           * @dev Changes the admin of the proxy.
           *
           * Emits an {IERC1967-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 the keccak-256 hash of "eip1967.proxy.beacon" subtracted by 1.
           */
          // solhint-disable-next-line private-vars-leading-underscore
          bytes32 internal constant BEACON_SLOT = 0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50;
          /**
           * @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 {
              if (newBeacon.code.length == 0) {
                  revert ERC1967InvalidBeacon(newBeacon);
              }
              StorageSlot.getAddressSlot(BEACON_SLOT).value = newBeacon;
              address beaconImplementation = IBeacon(newBeacon).implementation();
              if (beaconImplementation.code.length == 0) {
                  revert ERC1967InvalidImplementation(beaconImplementation);
              }
          }
          /**
           * @dev Change the beacon and trigger a setup call if data is nonempty.
           * This function is payable only if the setup call is performed, otherwise `msg.value` is rejected
           * to avoid stuck value in the contract.
           *
           * Emits an {IERC1967-BeaconUpgraded} event.
           *
           * CAUTION: Invoking this function has no effect on an instance of {BeaconProxy} since v5, since
           * it uses an immutable beacon without looking at the value of the ERC-1967 beacon slot for
           * efficiency.
           */
          function upgradeBeaconToAndCall(address newBeacon, bytes memory data) internal {
              _setBeacon(newBeacon);
              emit BeaconUpgraded(newBeacon);
              if (data.length > 0) {
                  Address.functionDelegateCall(IBeacon(newBeacon).implementation(), data);
              } else {
                  _checkNonPayable();
              }
          }
          /**
           * @dev Reverts if `msg.value` is not zero. It can be used to avoid `msg.value` stuck in the contract
           * if an upgrade doesn't perform an initialization call.
           */
          function _checkNonPayable() private {
              if (msg.value > 0) {
                  revert ERC1967NonPayable();
              }
          }
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts (last updated v5.0.0) (proxy/Proxy.sol)
      pragma solidity ^0.8.20;
      /**
       * @dev This abstract contract provides a fallback function that delegates all calls to another contract using the EVM
       * instruction `delegatecall`. We refer to the second contract as the _implementation_ behind the proxy, and it has to
       * be specified by overriding the virtual {_implementation} function.
       *
       * Additionally, delegation to the implementation can be triggered manually through the {_fallback} function, or to a
       * different contract through the {_delegate} function.
       *
       * The success and return data of the delegated call will be returned back to the caller of the proxy.
       */
      abstract contract Proxy {
          /**
           * @dev Delegates the current call to `implementation`.
           *
           * This function does not return to its internal call site, it will return directly to the external caller.
           */
          function _delegate(address implementation) internal virtual {
              assembly {
                  // Copy msg.data. We take full control of memory in this inline assembly
                  // block because it will not return to Solidity code. We overwrite the
                  // Solidity scratch pad at memory position 0.
                  calldatacopy(0, 0, calldatasize())
                  // Call the implementation.
                  // out and outsize are 0 because we don't know the size yet.
                  let result := delegatecall(gas(), implementation, 0, calldatasize(), 0, 0)
                  // Copy the returned data.
                  returndatacopy(0, 0, returndatasize())
                  switch result
                  // delegatecall returns 0 on error.
                  case 0 {
                      revert(0, returndatasize())
                  }
                  default {
                      return(0, returndatasize())
                  }
              }
          }
          /**
           * @dev This is a virtual function that should be overridden so it returns the address to which the fallback
           * function and {_fallback} should delegate.
           */
          function _implementation() internal view virtual returns (address);
          /**
           * @dev Delegates the current call to the address returned by `_implementation()`.
           *
           * This function does not return to its internal call site, it will return directly to the external caller.
           */
          function _fallback() internal virtual {
              _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();
          }
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts (last updated v5.0.0) (proxy/transparent/ProxyAdmin.sol)
      pragma solidity ^0.8.20;
      import {ITransparentUpgradeableProxy} from "./TransparentUpgradeableProxy.sol";
      import {Ownable} from "../../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 The version of the upgrade interface of the contract. If this getter is missing, both `upgrade(address)`
           * and `upgradeAndCall(address,bytes)` are present, and `upgradeTo` must be used if no function should be called,
           * while `upgradeAndCall` will invoke the `receive` function if the second argument is the empty byte string.
           * If the getter returns `"5.0.0"`, only `upgradeAndCall(address,bytes)` is present, and the second argument must
           * be the empty byte string if no function should be called, making it impossible to invoke the `receive` function
           * during an upgrade.
           */
          string public constant UPGRADE_INTERFACE_VERSION = "5.0.0";
          /**
           * @dev Sets the initial owner who can perform upgrades.
           */
          constructor(address initialOwner) Ownable(initialOwner) {}
          /**
           * @dev Upgrades `proxy` to `implementation` and calls a function on the new implementation.
           * See {TransparentUpgradeableProxy-_dispatchUpgradeToAndCall}.
           *
           * Requirements:
           *
           * - This contract must be the admin of `proxy`.
           * - If `data` is empty, `msg.value` must be zero.
           */
          function upgradeAndCall(
              ITransparentUpgradeableProxy proxy,
              address implementation,
              bytes memory data
          ) public payable virtual onlyOwner {
              proxy.upgradeToAndCall{value: msg.value}(implementation, data);
          }
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts (last updated v5.0.0) (proxy/transparent/TransparentUpgradeableProxy.sol)
      pragma solidity ^0.8.20;
      import {ERC1967Utils} from "../ERC1967/ERC1967Utils.sol";
      import {ERC1967Proxy} from "../ERC1967/ERC1967Proxy.sol";
      import {IERC1967} from "../../interfaces/IERC1967.sol";
      import {ProxyAdmin} from "./ProxyAdmin.sol";
      /**
       * @dev Interface for {TransparentUpgradeableProxy}. In order to implement transparency, {TransparentUpgradeableProxy}
       * does not implement this interface directly, and its upgradeability mechanism is implemented by an internal dispatch
       * mechanism. The compiler is unaware that these functions are implemented by {TransparentUpgradeableProxy} and will not
       * include them in the ABI so this interface must be used to interact with it.
       */
      interface ITransparentUpgradeableProxy is IERC1967 {
          function upgradeToAndCall(address, bytes calldata) external payable;
      }
      /**
       * @dev This contract implements a proxy that is upgradeable through an associated {ProxyAdmin} instance.
       *
       * 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 the {ITransparentUpgradeableProxy-upgradeToAndCall} function exposed by the proxy itself.
       * 2. If the admin calls the proxy, it can call the `upgradeToAndCall` function but any other call won't be forwarded to
       * the implementation. If the admin tries to call a function on the implementation it will fail with an error indicating
       * the proxy admin cannot fallback to the target implementation.
       *
       * These properties mean that the admin account can only be used for upgrading the proxy, 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. For this reason, the proxy deploys an instance of {ProxyAdmin} and
       * allows upgrades only if they come through it. You should think of the `ProxyAdmin` instance as the administrative
       * interface of the proxy, including the ability to change who can trigger upgrades by transferring ownership.
       *
       * NOTE: The real interface of this proxy is that defined in `ITransparentUpgradeableProxy`. This contract does not
       * inherit from that interface, and instead `upgradeToAndCall` is implicitly implemented using a custom dispatch
       * mechanism in `_fallback`. Consequently, the compiler will not produce an ABI for this contract. This is necessary to
       * fully implement transparency without decoding reverts caused by selector clashes between the proxy and the
       * implementation.
       *
       * NOTE: This proxy does not inherit from {Context} deliberately. The {ProxyAdmin} of this contract won't send a
       * meta-transaction in any way, and any other meta-transaction setup should be made in the implementation contract.
       *
       * IMPORTANT: This contract avoids unnecessary storage reads by setting the admin only during construction as an
       * immutable variable, preventing any changes thereafter. However, the admin slot defined in ERC-1967 can still be
       * overwritten by the implementation logic pointed to by this proxy. In such cases, the contract may end up in an
       * undesirable state where the admin slot is different from the actual admin.
       *
       * WARNING: It is not recommended to extend this contract to add additional external functions. If you do so, the
       * compiler will not check that there are no selector conflicts, due to the note above. A selector clash between any new
       * function and the functions declared in {ITransparentUpgradeableProxy} will be resolved in favor of the new one. This
       * could render the `upgradeToAndCall` function inaccessible, preventing upgradeability and compromising transparency.
       */
      contract TransparentUpgradeableProxy is ERC1967Proxy {
          // An immutable address for the admin to avoid unnecessary SLOADs before each call
          // at the expense of removing the ability to change the admin once it's set.
          // This is acceptable if the admin is always a ProxyAdmin instance or similar contract
          // with its own ability to transfer the permissions to another account.
          address private immutable _admin;
          /**
           * @dev The proxy caller is the current admin, and can't fallback to the proxy target.
           */
          error ProxyDeniedAdminAccess();
          /**
           * @dev Initializes an upgradeable proxy managed by an instance of a {ProxyAdmin} with an `initialOwner`,
           * backed by the implementation at `_logic`, and optionally initialized with `_data` as explained in
           * {ERC1967Proxy-constructor}.
           */
          constructor(address _logic, address initialOwner, bytes memory _data) payable ERC1967Proxy(_logic, _data) {
              _admin = address(new ProxyAdmin(initialOwner));
              // Set the storage value and emit an event for ERC-1967 compatibility
              ERC1967Utils.changeAdmin(_proxyAdmin());
          }
          /**
           * @dev Returns the admin of this proxy.
           */
          function _proxyAdmin() internal virtual returns (address) {
              return _admin;
          }
          /**
           * @dev If caller is the admin process the call internally, otherwise transparently fallback to the proxy behavior.
           */
          function _fallback() internal virtual override {
              if (msg.sender == _proxyAdmin()) {
                  if (msg.sig != ITransparentUpgradeableProxy.upgradeToAndCall.selector) {
                      revert ProxyDeniedAdminAccess();
                  } else {
                      _dispatchUpgradeToAndCall();
                  }
              } else {
                  super._fallback();
              }
          }
          /**
           * @dev Upgrade the implementation of the proxy. See {ERC1967Utils-upgradeToAndCall}.
           *
           * Requirements:
           *
           * - If `data` is empty, `msg.value` must be zero.
           */
          function _dispatchUpgradeToAndCall() private {
              (address newImplementation, bytes memory data) = abi.decode(msg.data[4:], (address, bytes));
              ERC1967Utils.upgradeToAndCall(newImplementation, data);
          }
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts (last updated v5.0.0) (utils/Address.sol)
      pragma solidity ^0.8.20;
      /**
       * @dev Collection of functions related to the address type
       */
      library Address {
          /**
           * @dev The ETH balance of the account is not enough to perform the operation.
           */
          error AddressInsufficientBalance(address account);
          /**
           * @dev There's no code at `target` (it is not a contract).
           */
          error AddressEmptyCode(address target);
          /**
           * @dev A call to an address target failed. The target may have reverted.
           */
          error FailedInnerCall();
          /**
           * @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.8.20/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
           */
          function sendValue(address payable recipient, uint256 amount) internal {
              if (address(this).balance < amount) {
                  revert AddressInsufficientBalance(address(this));
              }
              (bool success, ) = recipient.call{value: amount}("");
              if (!success) {
                  revert FailedInnerCall();
              }
          }
          /**
           * @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 or custom error, it is bubbled
           * up by this function (like regular Solidity function calls). However, if
           * the call reverted with no returned reason, this function reverts with a
           * {FailedInnerCall} error.
           *
           * 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.
           */
          function functionCall(address target, bytes memory data) internal returns (bytes memory) {
              return functionCallWithValue(target, data, 0);
          }
          /**
           * @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`.
           */
          function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
              if (address(this).balance < value) {
                  revert AddressInsufficientBalance(address(this));
              }
              (bool success, bytes memory returndata) = target.call{value: value}(data);
              return verifyCallResultFromTarget(target, success, returndata);
          }
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
           * but performing a static call.
           */
          function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
              (bool success, bytes memory returndata) = target.staticcall(data);
              return verifyCallResultFromTarget(target, success, returndata);
          }
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
           * but performing a delegate call.
           */
          function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
              (bool success, bytes memory returndata) = target.delegatecall(data);
              return verifyCallResultFromTarget(target, success, returndata);
          }
          /**
           * @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target
           * was not a contract or bubbling up the revert reason (falling back to {FailedInnerCall}) in case of an
           * unsuccessful call.
           */
          function verifyCallResultFromTarget(
              address target,
              bool success,
              bytes memory returndata
          ) internal view returns (bytes memory) {
              if (!success) {
                  _revert(returndata);
              } else {
                  // only check if target is a contract if the call was successful and the return data is empty
                  // otherwise we already know that it was a contract
                  if (returndata.length == 0 && target.code.length == 0) {
                      revert AddressEmptyCode(target);
                  }
                  return returndata;
              }
          }
          /**
           * @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the
           * revert reason or with a default {FailedInnerCall} error.
           */
          function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) {
              if (!success) {
                  _revert(returndata);
              } else {
                  return returndata;
              }
          }
          /**
           * @dev Reverts with returndata if present. Otherwise reverts with {FailedInnerCall}.
           */
          function _revert(bytes memory returndata) 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 FailedInnerCall();
              }
          }
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)
      pragma solidity ^0.8.20;
      /**
       * @dev Provides information about the current execution context, including the
       * sender of the transaction and its data. While these are generally available
       * via msg.sender and msg.data, they should not be accessed in such a direct
       * manner, since when dealing with meta-transactions the account sending and
       * paying for execution may not be the actual sender (as far as an application
       * is concerned).
       *
       * This contract is only required for intermediate, library-like contracts.
       */
      abstract contract Context {
          function _msgSender() internal view virtual returns (address) {
              return msg.sender;
          }
          function _msgData() internal view virtual returns (bytes calldata) {
              return msg.data;
          }
          function _contextSuffixLength() internal view virtual returns (uint256) {
              return 0;
          }
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts (last updated v5.0.0) (utils/StorageSlot.sol)
      // This file was procedurally generated from scripts/generate/templates/StorageSlot.js.
      pragma solidity ^0.8.20;
      /**
       * @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:
       * ```solidity
       * 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(newImplementation.code.length > 0);
       *         StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
       *     }
       * }
       * ```
       */
      library StorageSlot {
          struct AddressSlot {
              address value;
          }
          struct BooleanSlot {
              bool value;
          }
          struct Bytes32Slot {
              bytes32 value;
          }
          struct Uint256Slot {
              uint256 value;
          }
          struct StringSlot {
              string value;
          }
          struct BytesSlot {
              bytes value;
          }
          /**
           * @dev Returns an `AddressSlot` with member `value` located at `slot`.
           */
          function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {
              /// @solidity memory-safe-assembly
              assembly {
                  r.slot := slot
              }
          }
          /**
           * @dev Returns an `BooleanSlot` with member `value` located at `slot`.
           */
          function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {
              /// @solidity memory-safe-assembly
              assembly {
                  r.slot := slot
              }
          }
          /**
           * @dev Returns an `Bytes32Slot` with member `value` located at `slot`.
           */
          function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {
              /// @solidity memory-safe-assembly
              assembly {
                  r.slot := slot
              }
          }
          /**
           * @dev Returns an `Uint256Slot` with member `value` located at `slot`.
           */
          function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {
              /// @solidity memory-safe-assembly
              assembly {
                  r.slot := slot
              }
          }
          /**
           * @dev Returns an `StringSlot` with member `value` located at `slot`.
           */
          function getStringSlot(bytes32 slot) internal pure returns (StringSlot storage r) {
              /// @solidity memory-safe-assembly
              assembly {
                  r.slot := slot
              }
          }
          /**
           * @dev Returns an `StringSlot` representation of the string storage pointer `store`.
           */
          function getStringSlot(string storage store) internal pure returns (StringSlot storage r) {
              /// @solidity memory-safe-assembly
              assembly {
                  r.slot := store.slot
              }
          }
          /**
           * @dev Returns an `BytesSlot` with member `value` located at `slot`.
           */
          function getBytesSlot(bytes32 slot) internal pure returns (BytesSlot storage r) {
              /// @solidity memory-safe-assembly
              assembly {
                  r.slot := slot
              }
          }
          /**
           * @dev Returns an `BytesSlot` representation of the bytes storage pointer `store`.
           */
          function getBytesSlot(bytes storage store) internal pure returns (BytesSlot storage r) {
              /// @solidity memory-safe-assembly
              assembly {
                  r.slot := store.slot
              }
          }
      }
      

      File 2 of 3: ChimpzeeCoin
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts (last updated v4.7.0) (access/Ownable.sol)
      pragma solidity ^0.8.0;
      import "../utils/Context.sol";
      /**
       * @dev Contract module which provides a basic access control mechanism, where
       * there is an account (an owner) that can be granted exclusive access to
       * specific functions.
       *
       * By default, the owner account will be the one that deploys the contract. This
       * can later be changed with {transferOwnership}.
       *
       * This module is used through inheritance. It will make available the modifier
       * `onlyOwner`, which can be applied to your functions to restrict their use to
       * the owner.
       */
      abstract contract Ownable is Context {
          address private _owner;
          event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
          /**
           * @dev Initializes the contract setting the deployer as the initial owner.
           */
          constructor() {
              _transferOwnership(_msgSender());
          }
          /**
           * @dev Throws if called by any account other than the owner.
           */
          modifier onlyOwner() {
              _checkOwner();
              _;
          }
          /**
           * @dev Returns the address of the current owner.
           */
          function owner() public view virtual returns (address) {
              return _owner;
          }
          /**
           * @dev Throws if the sender is not the owner.
           */
          function _checkOwner() internal view virtual {
              require(owner() == _msgSender(), "Ownable: caller is not the owner");
          }
          /**
           * @dev Leaves the contract without owner. It will not be possible to call
           * `onlyOwner` functions anymore. Can only be called by the current owner.
           *
           * NOTE: Renouncing ownership will leave the contract without an owner,
           * thereby removing any functionality that is only available to the owner.
           */
          function renounceOwnership() public virtual onlyOwner {
              _transferOwnership(address(0));
          }
          /**
           * @dev Transfers ownership of the contract to a new account (`newOwner`).
           * Can only be called by the current owner.
           */
          function transferOwnership(address newOwner) public virtual onlyOwner {
              require(newOwner != address(0), "Ownable: new owner is the zero address");
              _transferOwnership(newOwner);
          }
          /**
           * @dev Transfers ownership of the contract to a new account (`newOwner`).
           * Internal function without access restriction.
           */
          function _transferOwnership(address newOwner) internal virtual {
              address oldOwner = _owner;
              _owner = newOwner;
              emit OwnershipTransferred(oldOwner, newOwner);
          }
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts (last updated v4.8.0) (token/ERC20/ERC20.sol)
      pragma solidity ^0.8.0;
      import "./IERC20.sol";
      import "./extensions/IERC20Metadata.sol";
      import "../../utils/Context.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 {ERC20PresetMinterPauser}.
       *
       * TIP: For a detailed writeup see our guide
       * https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How
       * to implement supply mechanisms].
       *
       * We have followed general OpenZeppelin Contracts guidelines: functions revert
       * instead 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 ERC20 is Context, IERC20, IERC20Metadata {
          mapping(address => uint256) private _balances;
          mapping(address => mapping(address => uint256)) private _allowances;
          uint256 private _totalSupply;
          string private _name;
          string private _symbol;
          /**
           * @dev Sets the values for {name} and {symbol}.
           *
           * The default value of {decimals} is 18. To select a different value for
           * {decimals} you should overload it.
           *
           * All two of these values are immutable: they can only be set once during
           * construction.
           */
          constructor(string memory name_, string memory symbol_) {
              _name = name_;
              _symbol = symbol_;
          }
          /**
           * @dev Returns the name of the token.
           */
          function name() public view virtual override returns (string memory) {
              return _name;
          }
          /**
           * @dev Returns the symbol of the token, usually a shorter version of the
           * name.
           */
          function symbol() public view virtual override 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 this function is
           * overridden;
           *
           * 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 virtual override returns (uint8) {
              return 18;
          }
          /**
           * @dev See {IERC20-totalSupply}.
           */
          function totalSupply() public view virtual override returns (uint256) {
              return _totalSupply;
          }
          /**
           * @dev See {IERC20-balanceOf}.
           */
          function balanceOf(address account) public view virtual override returns (uint256) {
              return _balances[account];
          }
          /**
           * @dev See {IERC20-transfer}.
           *
           * Requirements:
           *
           * - `to` cannot be the zero address.
           * - the caller must have a balance of at least `amount`.
           */
          function transfer(address to, uint256 amount) public virtual override returns (bool) {
              address owner = _msgSender();
              _transfer(owner, to, 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}.
           *
           * NOTE: If `amount` is the maximum `uint256`, the allowance is not updated on
           * `transferFrom`. This is semantically equivalent to an infinite approval.
           *
           * Requirements:
           *
           * - `spender` cannot be the zero address.
           */
          function approve(address spender, uint256 amount) public virtual override returns (bool) {
              address owner = _msgSender();
              _approve(owner, 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}.
           *
           * NOTE: Does not update the allowance if the current allowance
           * is the maximum `uint256`.
           *
           * Requirements:
           *
           * - `from` and `to` cannot be the zero address.
           * - `from` must have a balance of at least `amount`.
           * - the caller must have allowance for ``from``'s tokens of at least
           * `amount`.
           */
          function transferFrom(
              address from,
              address to,
              uint256 amount
          ) public virtual override returns (bool) {
              address spender = _msgSender();
              _spendAllowance(from, spender, amount);
              _transfer(from, to, amount);
              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) {
              address owner = _msgSender();
              _approve(owner, spender, allowance(owner, spender) + 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) {
              address owner = _msgSender();
              uint256 currentAllowance = allowance(owner, spender);
              require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero");
              unchecked {
                  _approve(owner, spender, currentAllowance - subtractedValue);
              }
              return true;
          }
          /**
           * @dev Moves `amount` of tokens from `from` to `to`.
           *
           * This 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:
           *
           * - `from` cannot be the zero address.
           * - `to` cannot be the zero address.
           * - `from` must have a balance of at least `amount`.
           */
          function _transfer(
              address from,
              address to,
              uint256 amount
          ) internal virtual {
              require(from != address(0), "ERC20: transfer from the zero address");
              require(to != address(0), "ERC20: transfer to the zero address");
              _beforeTokenTransfer(from, to, amount);
              uint256 fromBalance = _balances[from];
              require(fromBalance >= amount, "ERC20: transfer amount exceeds balance");
              unchecked {
                  _balances[from] = fromBalance - amount;
                  // Overflow not possible: the sum of all balances is capped by totalSupply, and the sum is preserved by
                  // decrementing then incrementing.
                  _balances[to] += amount;
              }
              emit Transfer(from, to, amount);
              _afterTokenTransfer(from, to, 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:
           *
           * - `account` 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 += amount;
              unchecked {
                  // Overflow not possible: balance + amount is at most totalSupply + amount, which is checked above.
                  _balances[account] += amount;
              }
              emit Transfer(address(0), account, amount);
              _afterTokenTransfer(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);
              uint256 accountBalance = _balances[account];
              require(accountBalance >= amount, "ERC20: burn amount exceeds balance");
              unchecked {
                  _balances[account] = accountBalance - amount;
                  // Overflow not possible: amount <= accountBalance <= totalSupply.
                  _totalSupply -= amount;
              }
              emit Transfer(account, address(0), amount);
              _afterTokenTransfer(account, address(0), amount);
          }
          /**
           * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.
           *
           * This 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 Updates `owner` s allowance for `spender` based on spent `amount`.
           *
           * Does not update the allowance amount in case of infinite allowance.
           * Revert if not enough allowance is available.
           *
           * Might emit an {Approval} event.
           */
          function _spendAllowance(
              address owner,
              address spender,
              uint256 amount
          ) internal virtual {
              uint256 currentAllowance = allowance(owner, spender);
              if (currentAllowance != type(uint256).max) {
                  require(currentAllowance >= amount, "ERC20: insufficient allowance");
                  unchecked {
                      _approve(owner, spender, currentAllowance - amount);
                  }
              }
          }
          /**
           * @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 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 {}
          /**
           * @dev Hook that is called after any transfer of tokens. This includes
           * minting and burning.
           *
           * Calling conditions:
           *
           * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
           * has been transferred to `to`.
           * - when `from` is zero, `amount` tokens have been minted for `to`.
           * - when `to` is zero, `amount` of ``from``'s tokens have been 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 _afterTokenTransfer(
              address from,
              address to,
              uint256 amount
          ) internal virtual {}
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol)
      pragma solidity ^0.8.0;
      import "../IERC20.sol";
      /**
       * @dev Interface for the optional metadata functions from the ERC20 standard.
       *
       * _Available since v4.1._
       */
      interface IERC20Metadata is IERC20 {
          /**
           * @dev Returns the name of the token.
           */
          function name() external view returns (string memory);
          /**
           * @dev Returns the symbol of the token.
           */
          function symbol() external view returns (string memory);
          /**
           * @dev Returns the decimals places of the token.
           */
          function decimals() external view returns (uint8);
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol)
      pragma solidity ^0.8.0;
      /**
       * @dev Interface of the ERC20 standard as defined in the EIP.
       */
      interface IERC20 {
          /**
           * @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);
          /**
           * @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 `to`.
           *
           * Returns a boolean value indicating whether the operation succeeded.
           *
           * Emits a {Transfer} event.
           */
          function transfer(address to, 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 `from` to `to` 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 from,
              address to,
              uint256 amount
          ) external returns (bool);
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts v4.4.1 (utils/Context.sol)
      pragma solidity ^0.8.0;
      /**
       * @dev Provides information about the current execution context, including the
       * sender of the transaction and its data. While these are generally available
       * via msg.sender and msg.data, they should not be accessed in such a direct
       * manner, since when dealing with meta-transactions the account sending and
       * paying for execution may not be the actual sender (as far as an application
       * is concerned).
       *
       * This contract is only required for intermediate, library-like contracts.
       */
      abstract contract Context {
          function _msgSender() internal view virtual returns (address) {
              return msg.sender;
          }
          function _msgData() internal view virtual returns (bytes calldata) {
              return msg.data;
          }
      }
      // SPDX-License-Identifier: MIT
      /**    ______  ____  ____  _____  ____    ____  _______   ________  ________  ________  
       *   .' ___  ||_   ||   _||_   _||_   \\  /   _||_   __ \\ |  __   _||_   __  ||_   __  | 
       *  / .'   \\_|  | |__| |    | |    |   \\/   |    | |__) ||_/  / /    | |_ \\_|  | |_ \\_| 
       *  | |         |  __  |    | |    | |\\  /| |    |  ___/    .'.' _   |  _| _   |  _| _  
       *  \\ `.___.'\\ _| |  | |_  _| |_  _| |_\\/_| |_  _| |_     _/ /__/ | _| |__/ | _| |__/ | 
       *   `.____ .'|____||____||_____||_____||_____||_____|   |________||________||________| 
       */
      pragma solidity 0.8.18;
      import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
      import "@openzeppelin/contracts/access/Ownable.sol";
      import "@openzeppelin/contracts/utils/Context.sol";
      contract ChimpzeeCoin is ERC20, Ownable {
          /**
           * @dev Initializes the contract and sets key parameters
           * @param _name Name of the token
           * @param _symbol Symbol of the token
           * @param _amount Token amount to be minted
           */
          constructor (string memory _name, string memory _symbol, uint256 _amount) ERC20(_name, _symbol) {       
              _mint(_msgSender(), _amount * (10 ** decimals()));
          }
          /**
           * @dev Burn tokens from owners wallet
           * @param _amount Token amount to be burned
           */
          function burn(uint256 _amount) external onlyOwner {
              _burn(_msgSender(), _amount * (10 ** decimals()));
          }
      }

      File 3 of 3: ChimpzeeTokenStakeV2
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)
      pragma solidity ^0.8.20;
      import {ContextUpgradeable} from "../utils/ContextUpgradeable.sol";
      import {Initializable} from "../proxy/utils/Initializable.sol";
      /**
       * @dev Contract module which provides a basic access control mechanism, where
       * there is an account (an owner) that can be granted exclusive access to
       * specific functions.
       *
       * The initial owner is set to the address provided by the deployer. 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 {
          /// @custom:storage-location erc7201:openzeppelin.storage.Ownable
          struct OwnableStorage {
              address _owner;
          }
          // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Ownable")) - 1)) & ~bytes32(uint256(0xff))
          bytes32 private constant OwnableStorageLocation = 0x9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300;
          function _getOwnableStorage() private pure returns (OwnableStorage storage $) {
              assembly {
                  $.slot := OwnableStorageLocation
              }
          }
          /**
           * @dev The caller account is not authorized to perform an operation.
           */
          error OwnableUnauthorizedAccount(address account);
          /**
           * @dev The owner is not a valid owner account. (eg. `address(0)`)
           */
          error OwnableInvalidOwner(address owner);
          event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
          /**
           * @dev Initializes the contract setting the address provided by the deployer as the initial owner.
           */
          function __Ownable_init(address initialOwner) internal onlyInitializing {
              __Ownable_init_unchained(initialOwner);
          }
          function __Ownable_init_unchained(address initialOwner) internal onlyInitializing {
              if (initialOwner == address(0)) {
                  revert OwnableInvalidOwner(address(0));
              }
              _transferOwnership(initialOwner);
          }
          /**
           * @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) {
              OwnableStorage storage $ = _getOwnableStorage();
              return $._owner;
          }
          /**
           * @dev Throws if the sender is not the owner.
           */
          function _checkOwner() internal view virtual {
              if (owner() != _msgSender()) {
                  revert OwnableUnauthorizedAccount(_msgSender());
              }
          }
          /**
           * @dev Leaves the contract without owner. It will not be possible to call
           * `onlyOwner` functions. Can only be called by the current owner.
           *
           * NOTE: Renouncing ownership will leave the contract without an owner,
           * thereby disabling 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 {
              if (newOwner == address(0)) {
                  revert OwnableInvalidOwner(address(0));
              }
              _transferOwnership(newOwner);
          }
          /**
           * @dev Transfers ownership of the contract to a new account (`newOwner`).
           * Internal function without access restriction.
           */
          function _transferOwnership(address newOwner) internal virtual {
              OwnableStorage storage $ = _getOwnableStorage();
              address oldOwner = $._owner;
              $._owner = newOwner;
              emit OwnershipTransferred(oldOwner, newOwner);
          }
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts (last updated v5.0.0) (proxy/utils/Initializable.sol)
      pragma solidity ^0.8.20;
      /**
       * @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 Storage of the initializable contract.
           *
           * It's implemented on a custom ERC-7201 namespace to reduce the risk of storage collisions
           * when using with upgradeable contracts.
           *
           * @custom:storage-location erc7201:openzeppelin.storage.Initializable
           */
          struct InitializableStorage {
              /**
               * @dev Indicates that the contract has been initialized.
               */
              uint64 _initialized;
              /**
               * @dev Indicates that the contract is in the process of being initialized.
               */
              bool _initializing;
          }
          // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Initializable")) - 1)) & ~bytes32(uint256(0xff))
          bytes32 private constant INITIALIZABLE_STORAGE = 0xf0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00;
          /**
           * @dev The contract is already initialized.
           */
          error InvalidInitialization();
          /**
           * @dev The contract is not initializing.
           */
          error NotInitializing();
          /**
           * @dev Triggered when the contract has been initialized or reinitialized.
           */
          event Initialized(uint64 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 in the context of a constructor an `initializer` may be invoked any
           * number of times. This behavior in the constructor can be useful during testing and is not expected to be used in
           * production.
           *
           * Emits an {Initialized} event.
           */
          modifier initializer() {
              // solhint-disable-next-line var-name-mixedcase
              InitializableStorage storage $ = _getInitializableStorage();
              // Cache values to avoid duplicated sloads
              bool isTopLevelCall = !$._initializing;
              uint64 initialized = $._initialized;
              // Allowed calls:
              // - initialSetup: the contract is not in the initializing state and no previous version was
              //                 initialized
              // - construction: the contract is initialized at version 1 (no reininitialization) and the
              //                 current contract is just being deployed
              bool initialSetup = initialized == 0 && isTopLevelCall;
              bool construction = initialized == 1 && address(this).code.length == 0;
              if (!initialSetup && !construction) {
                  revert InvalidInitialization();
              }
              $._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 2**64 - 1 will prevent any future reinitialization.
           *
           * Emits an {Initialized} event.
           */
          modifier reinitializer(uint64 version) {
              // solhint-disable-next-line var-name-mixedcase
              InitializableStorage storage $ = _getInitializableStorage();
              if ($._initializing || $._initialized >= version) {
                  revert InvalidInitialization();
              }
              $._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() {
              _checkInitializing();
              _;
          }
          /**
           * @dev Reverts if the contract is not in an initializing state. See {onlyInitializing}.
           */
          function _checkInitializing() internal view virtual {
              if (!_isInitializing()) {
                  revert NotInitializing();
              }
          }
          /**
           * @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 {
              // solhint-disable-next-line var-name-mixedcase
              InitializableStorage storage $ = _getInitializableStorage();
              if ($._initializing) {
                  revert InvalidInitialization();
              }
              if ($._initialized != type(uint64).max) {
                  $._initialized = type(uint64).max;
                  emit Initialized(type(uint64).max);
              }
          }
          /**
           * @dev Returns the highest version that has been initialized. See {reinitializer}.
           */
          function _getInitializedVersion() internal view returns (uint64) {
              return _getInitializableStorage()._initialized;
          }
          /**
           * @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.
           */
          function _isInitializing() internal view returns (bool) {
              return _getInitializableStorage()._initializing;
          }
          /**
           * @dev Returns a pointer to the storage namespace.
           */
          // solhint-disable-next-line var-name-mixedcase
          function _getInitializableStorage() private pure returns (InitializableStorage storage $) {
              assembly {
                  $.slot := INITIALIZABLE_STORAGE
              }
          }
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)
      pragma solidity ^0.8.20;
      import {Initializable} from "../proxy/utils/Initializable.sol";
      /**
       * @dev Provides information about the current execution context, including the
       * sender of the transaction and its data. While these are generally available
       * via msg.sender and msg.data, they should not be accessed in such a direct
       * manner, since when dealing with meta-transactions the account sending and
       * paying for execution may not be the actual sender (as far as an application
       * is concerned).
       *
       * This contract is only required for intermediate, library-like contracts.
       */
      abstract contract ContextUpgradeable is Initializable {
          function __Context_init() internal onlyInitializing {
          }
          function __Context_init_unchained() internal onlyInitializing {
          }
          function _msgSender() internal view virtual returns (address) {
              return msg.sender;
          }
          function _msgData() internal view virtual returns (bytes calldata) {
              return msg.data;
          }
          function _contextSuffixLength() internal view virtual returns (uint256) {
              return 0;
          }
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts (last updated v5.0.0) (utils/Pausable.sol)
      pragma solidity ^0.8.20;
      import {ContextUpgradeable} from "../utils/ContextUpgradeable.sol";
      import {Initializable} from "../proxy/utils/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.
       */
      abstract contract PausableUpgradeable is Initializable, ContextUpgradeable {
          /// @custom:storage-location erc7201:openzeppelin.storage.Pausable
          struct PausableStorage {
              bool _paused;
          }
          // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Pausable")) - 1)) & ~bytes32(uint256(0xff))
          bytes32 private constant PausableStorageLocation = 0xcd5ed15c6e187e77e9aee88184c21f4f2182ab5827cb3b7e07fbedcd63f03300;
          function _getPausableStorage() private pure returns (PausableStorage storage $) {
              assembly {
                  $.slot := PausableStorageLocation
              }
          }
          /**
           * @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);
          /**
           * @dev The operation failed because the contract is paused.
           */
          error EnforcedPause();
          /**
           * @dev The operation failed because the contract is not paused.
           */
          error ExpectedPause();
          /**
           * @dev Initializes the contract in unpaused state.
           */
          function __Pausable_init() internal onlyInitializing {
              __Pausable_init_unchained();
          }
          function __Pausable_init_unchained() internal onlyInitializing {
              PausableStorage storage $ = _getPausableStorage();
              $._paused = false;
          }
          /**
           * @dev Modifier to make a function callable only when the contract is not paused.
           *
           * Requirements:
           *
           * - The contract must not be paused.
           */
          modifier whenNotPaused() {
              _requireNotPaused();
              _;
          }
          /**
           * @dev Modifier to make a function callable only when the contract is paused.
           *
           * Requirements:
           *
           * - The contract must be paused.
           */
          modifier whenPaused() {
              _requirePaused();
              _;
          }
          /**
           * @dev Returns true if the contract is paused, and false otherwise.
           */
          function paused() public view virtual returns (bool) {
              PausableStorage storage $ = _getPausableStorage();
              return $._paused;
          }
          /**
           * @dev Throws if the contract is paused.
           */
          function _requireNotPaused() internal view virtual {
              if (paused()) {
                  revert EnforcedPause();
              }
          }
          /**
           * @dev Throws if the contract is not paused.
           */
          function _requirePaused() internal view virtual {
              if (!paused()) {
                  revert ExpectedPause();
              }
          }
          /**
           * @dev Triggers stopped state.
           *
           * Requirements:
           *
           * - The contract must not be paused.
           */
          function _pause() internal virtual whenNotPaused {
              PausableStorage storage $ = _getPausableStorage();
              $._paused = true;
              emit Paused(_msgSender());
          }
          /**
           * @dev Returns to normal state.
           *
           * Requirements:
           *
           * - The contract must be paused.
           */
          function _unpause() internal virtual whenPaused {
              PausableStorage storage $ = _getPausableStorage();
              $._paused = false;
              emit Unpaused(_msgSender());
          }
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts (last updated v5.0.0) (utils/ReentrancyGuard.sol)
      pragma solidity ^0.8.20;
      import {Initializable} from "../proxy/utils/Initializable.sol";
      /**
       * @dev Contract module that helps prevent reentrant calls to a function.
       *
       * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
       * available, which can be applied to functions to make sure there are no nested
       * (reentrant) calls to them.
       *
       * Note that because there is a single `nonReentrant` guard, functions marked as
       * `nonReentrant` may not call one another. This can be worked around by making
       * those functions `private`, and then adding `external` `nonReentrant` entry
       * points to them.
       *
       * TIP: If you would like to learn more about reentrancy and alternative ways
       * to protect against it, check out our blog post
       * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
       */
      abstract contract ReentrancyGuardUpgradeable is Initializable {
          // Booleans are more expensive than uint256 or any type that takes up a full
          // word because each write operation emits an extra SLOAD to first read the
          // slot's contents, replace the bits taken up by the boolean, and then write
          // back. This is the compiler's defense against contract upgrades and
          // pointer aliasing, and it cannot be disabled.
          // The values being non-zero value makes deployment a bit more expensive,
          // but in exchange the refund on every call to nonReentrant will be lower in
          // amount. Since refunds are capped to a percentage of the total
          // transaction's gas, it is best to keep them low in cases like this one, to
          // increase the likelihood of the full refund coming into effect.
          uint256 private constant NOT_ENTERED = 1;
          uint256 private constant ENTERED = 2;
          /// @custom:storage-location erc7201:openzeppelin.storage.ReentrancyGuard
          struct ReentrancyGuardStorage {
              uint256 _status;
          }
          // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.ReentrancyGuard")) - 1)) & ~bytes32(uint256(0xff))
          bytes32 private constant ReentrancyGuardStorageLocation = 0x9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f00;
          function _getReentrancyGuardStorage() private pure returns (ReentrancyGuardStorage storage $) {
              assembly {
                  $.slot := ReentrancyGuardStorageLocation
              }
          }
          /**
           * @dev Unauthorized reentrant call.
           */
          error ReentrancyGuardReentrantCall();
          function __ReentrancyGuard_init() internal onlyInitializing {
              __ReentrancyGuard_init_unchained();
          }
          function __ReentrancyGuard_init_unchained() internal onlyInitializing {
              ReentrancyGuardStorage storage $ = _getReentrancyGuardStorage();
              $._status = NOT_ENTERED;
          }
          /**
           * @dev Prevents a contract from calling itself, directly or indirectly.
           * Calling a `nonReentrant` function from another `nonReentrant`
           * function is not supported. It is possible to prevent this from happening
           * by making the `nonReentrant` function external, and making it call a
           * `private` function that does the actual work.
           */
          modifier nonReentrant() {
              _nonReentrantBefore();
              _;
              _nonReentrantAfter();
          }
          function _nonReentrantBefore() private {
              ReentrancyGuardStorage storage $ = _getReentrancyGuardStorage();
              // On the first call to nonReentrant, _status will be NOT_ENTERED
              if ($._status == ENTERED) {
                  revert ReentrancyGuardReentrantCall();
              }
              // Any calls to nonReentrant after this point will fail
              $._status = ENTERED;
          }
          function _nonReentrantAfter() private {
              ReentrancyGuardStorage storage $ = _getReentrancyGuardStorage();
              // By storing the original value once again, a refund is triggered (see
              // https://eips.ethereum.org/EIPS/eip-2200)
              $._status = NOT_ENTERED;
          }
          /**
           * @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
           * `nonReentrant` function in the call stack.
           */
          function _reentrancyGuardEntered() internal view returns (bool) {
              ReentrancyGuardStorage storage $ = _getReentrancyGuardStorage();
              return $._status == ENTERED;
          }
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/IERC20.sol)
      pragma solidity ^0.8.20;
      /**
       * @dev Interface of the ERC20 standard as defined in the EIP.
       */
      interface IERC20 {
          /**
           * @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);
          /**
           * @dev Returns the value of tokens in existence.
           */
          function totalSupply() external view returns (uint256);
          /**
           * @dev Returns the value of tokens owned by `account`.
           */
          function balanceOf(address account) external view returns (uint256);
          /**
           * @dev Moves a `value` amount of tokens from the caller's account to `to`.
           *
           * Returns a boolean value indicating whether the operation succeeded.
           *
           * Emits a {Transfer} event.
           */
          function transfer(address to, uint256 value) 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 a `value` amount of tokens 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 value) external returns (bool);
          /**
           * @dev Moves a `value` amount of tokens from `from` to `to` using the
           * allowance mechanism. `value` is then deducted from the caller's
           * allowance.
           *
           * Returns a boolean value indicating whether the operation succeeded.
           *
           * Emits a {Transfer} event.
           */
          function transferFrom(address from, address to, uint256 value) external returns (bool);
      }
      // SPDX-License-Identifier: MIT
      /**    ______  ____  ____  _____  ____    ____  _______   ________  ________  ________  
       *   .' ___  ||_   ||   _||_   _||_   \\  /   _||_   __ \\ |  __   _||_   __  ||_   __  | 
       *  / .'   \\_|  | |__| |    | |    |   \\/   |    | |__) ||_/  / /    | |_ \\_|  | |_ \\_| 
       *  | |         |  __  |    | |    | |\\  /| |    |  ___/    .'.' _   |  _| _   |  _| _  
       *  \\ `.___.'\\ _| |  | |_  _| |_  _| |_\\/_| |_  _| |_     _/ /__/ | _| |__/ | _| |__/ | 
       *   `.____ .'|____||____||_____||_____||_____||_____|   |________||________||________| 
       */
      pragma solidity 0.8.20;
      // Token
      import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
      // Utils
      import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
      import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
      import "@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol";
      import "@openzeppelin/contracts-upgradeable/utils/PausableUpgradeable.sol";
      import "./Staking20Upgradeable.sol";
      contract ChimpzeeTokenStakeV2 is 
          Initializable, 
          Staking20Upgradeable, 
          OwnableUpgradeable, 
          PausableUpgradeable 
          {
          /// @custom:oz-upgrades-unsafe-allow constructor
          constructor() {
              _disableInitializers();
          }
          /// @dev Initializes the contract, like a constructor.
          function initialize(
              address _tokenAddress,
              uint80 _timeUnit,
              uint256[] memory _rewardRatioNumerator,
              uint256[] memory _rewardRatioDenominator,
              uint256[] memory _rewardRatioAmount,
              uint256 _minStakeAmount,
              uint256 _locktime
          ) external initializer {
              __Ownable_init(_msgSender());
              __Staking20_init(_tokenAddress, 18);
              __Pausable_init();
              _setStakingCondition(_timeUnit, _rewardRatioNumerator, _rewardRatioDenominator, _rewardRatioAmount);
              _setMinStakeAmount(_minStakeAmount);
              _setLocktime(_locktime);
          }
          /// @dev Admin deposits reward tokens.
          function depositRewardTokens(uint256 _amount) external payable nonReentrant onlyOwner {
              require(_amount > 0, "No balance added");
              uint256 balanceBefore = IERC20(stakingToken).balanceOf(address(this));
              
              bool success = IERC20(stakingToken).transferFrom(_msgSender(), address(this), _amount);
              require(success, "Token transfer failed");
              uint256 actualAmount = IERC20(stakingToken).balanceOf(address(this)) - balanceBefore;
              rewardTokenBalance += actualAmount;
          }
          /// @dev Admin can withdraw excess reward tokens.
          function withdrawRewardTokens(uint256 _amount) external nonReentrant onlyOwner {
              // to prevent locking of direct-transferred tokens
              rewardTokenBalance = _amount > rewardTokenBalance ? 0 : rewardTokenBalance - _amount;
              bool success = IERC20(stakingToken).transfer(_msgSender(), _amount);
              require(success, "Token transfer failed");
              // The withdrawal shouldn't reduce staking token balance. `>=` accounts for any accidental transfers.
              require(
                  IERC20(stakingToken).balanceOf(address(this)) >= stakingTokenBalance,
                  "Staking token balance reduced."
              );
          }
          /**
           *  @notice  Set locktime.
           *
           *  @param _newAmount    New locktime in seconds.
           */
          function setLocktime(uint256 _newAmount) external onlyOwner {
              _setLocktime(_newAmount);
          }
          /**
           *  @notice  Set minimum stake limit.
           *
           *  @param _newAmount    New minimum stake limit.
           */
          function setMinStakeAmount(uint256 _newAmount) external onlyOwner {
              _setMinStakeAmount(_newAmount);
          }
          /**
           *  @notice  Set time unit. Set as a number of seconds.
           *           Could be specified as -- x * 1 hours, x * 1 days, etc.
           *
           *  @param _timeUnit    New time unit.
           */
          function setTimeUnit(uint80 _timeUnit) external onlyOwner {
              _setTimeUnit(_timeUnit);
          }
          /**
           *  @notice  Set rewards per unit of time.
           *           Interpreted as (numerator/denominator) rewards per second/per day/etc based on time-unit.
           *
           *  @param _numerator    Reward ratio numerator.
           *  @param _denominator  Reward ratio denominator.
           */
          function setRewardRatio(uint256[] memory _numerator, uint256[] memory _denominator, uint256[] memory _rewardRatioAmount) external onlyOwner {
              _setRewardRatio(_numerator, _denominator, _rewardRatioAmount);
          }
          /**
           *  @notice    Stake ERC20 Tokens.
           *  @param _amount    Amount to stake.
           */
          function stake(uint256 _amount) external payable nonReentrant whenNotPaused {
              _stake(_amount);
          }
          /**
           *  @notice    Withdraw staked ERC20 tokens.
           *  @param _amount    Amount to withdraw.
           */
          function withdraw(uint256 _amount) external nonReentrant {
              _withdraw(_amount);
          }
          /**
           *  @notice    Claim accumulated rewards.
           */
          function claimRewards() external nonReentrant {
              _claimRewards();
          }
          /**
           *  @notice    To pause staking
           */
          function pause() external onlyOwner {
              _pause();
          }
          /**
           *  @notice     To unpause staking
           */
          function unpause() external onlyOwner {
              _unpause();
          }
      }// SPDX-License-Identifier: MIT
      pragma solidity ^0.8.11;
      interface IStaking20 {
          /// @dev Emitted when tokens are staked.
          event TokensStaked(address indexed staker, uint256 amount);
          /// @dev Emitted when a tokens are withdrawn.
          event TokensWithdrawn(address indexed staker, uint256 amount);
          /// @dev Emitted when a staker claims staking rewards.
          event RewardsClaimed(address indexed staker, uint256 rewardAmount);
          /// @dev Emitted when contract admin updates timeUnit.
          event UpdatedTimeUnit(uint256 oldTimeUnit, uint256 newTimeUnit);
          /// @dev Emitted when contract admin updates rewardsPerUnitTime.
          event UpdatedRewardRatio(
              uint256[] oldNumerator,
              uint256[] newNumerator,
              uint256[] oldDenominator,
              uint256[] newDenominator,
              uint256[] oldRatioAmount,
              uint256[] newRatioAmount
          );
          /// @dev Emitted when contract admin updates minimum staking amount.
          event UpdatedMinStakeAmount(uint256 oldAmount, uint256 newAmount);
          /// @dev Emitted when contract admin updates locktime.
          event UpdatedLocktime(uint256 oldAmount, uint256 newAmount);
          /**
           *  @notice Staker Info.
           *
           *  @param amountStaked             Total number of tokens staked by the staker.
           *
           *  @param timeOfLastUpdate         Last reward-update timestamp.
           *
           *  @param unclaimedRewards         Rewards accumulated but not claimed by user yet.
           *
           *  @param conditionIdOflastUpdate  Condition-Id when rewards were last updated for user.
           */
          struct Staker {
              uint128 timeOfLastUpdate;
              uint64 conditionIdOflastUpdate;
              uint256 amountStaked;
              uint256 unclaimedRewards;
              uint256 totalEarnedRewards;
              LockedDeposits[] lockedCoins;
              bool stakedBefore;
          }
          struct LockedDeposits {
              uint256 amountDeposited;
              uint256 lockedUntilTime;
          }
          /**
           *  @notice Staking Condition.
           *
           *  @param timeUnit                 Unit of time specified in number of seconds. Can be set as 1 seconds, 1 days, 1 hours, etc.
           *
           *  @param rewardRatioNumerator     Rewards ratio is the number of reward tokens for a number of staked tokens,
           *                                  per unit of time.
           *
           *  @param rewardRatioDenominator   Rewards ratio is the number of reward tokens for a number of staked tokens,
           *                                  per unit of time.
           *
           *  @param startTimestamp           Condition start timestamp.
           *
           *  @param endTimestamp             Condition end timestamp.
           */
          struct StakingCondition {
              uint80 timeUnit;
              uint80 startTimestamp;
              uint80 endTimestamp;
              uint256[] rewardRatioNumerator;
              uint256[] rewardRatioDenominator;
              uint256[] rewardRatioAmount;
          }
          /**
           *  @notice Stake ERC721 Tokens.
           *
           *  @param amount    Amount to stake.
           */
          function stake(uint256 amount) external payable;
          /**
           *  @notice Withdraw staked tokens.
           *
           *  @param amount    Amount to withdraw.
           */
          function withdraw(uint256 amount) external;
          /**
           *  @notice Claim accumulated rewards.
           *
           */
          function claimRewards() external;
          /**
           *  @notice View amount staked, rewards and total rewards for a user.
           *
           *  @param staker    Address for which to calculated rewards.
           */
          function getStakeInfo(
              address staker
          )
              external
              view
              returns (
                  uint256 _tokensStaked,
                  uint256 _rewards,
                  uint256 _totalRewards
              );
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts (last updated v4.6.0) (utils/math/SafeMath.sol)
      pragma solidity ^0.8.0;
      // CAUTION
      // This version of SafeMath should only be used with Solidity 0.8 or later,
      // because it relies on the compiler's built in overflow checks.
      /**
       * @dev Wrappers over Solidity's arithmetic operations.
       *
       * NOTE: `SafeMath` is generally not needed starting with Solidity 0.8, since the compiler
       * now has built in overflow checking.
       */
      library SafeMath {
          /**
           * @dev Returns the addition of two unsigned integers, with an overflow flag.
           *
           * _Available since v3.4._
           */
          function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
              unchecked {
                  uint256 c = a + b;
                  if (c < a) return (false, 0);
                  return (true, c);
              }
          }
          /**
           * @dev Returns the subtraction of two unsigned integers, with an overflow flag.
           *
           * _Available since v3.4._
           */
          function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
              unchecked {
                  if (b > a) return (false, 0);
                  return (true, a - b);
              }
          }
          /**
           * @dev Returns the multiplication of two unsigned integers, with an overflow flag.
           *
           * _Available since v3.4._
           */
          function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
              unchecked {
                  // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
                  // benefit is lost if 'b' is also tested.
                  // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
                  if (a == 0) return (true, 0);
                  uint256 c = a * b;
                  if (c / a != b) return (false, 0);
                  return (true, c);
              }
          }
          /**
           * @dev Returns the division of two unsigned integers, with a division by zero flag.
           *
           * _Available since v3.4._
           */
          function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
              unchecked {
                  if (b == 0) return (false, 0);
                  return (true, a / b);
              }
          }
          /**
           * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
           *
           * _Available since v3.4._
           */
          function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
              unchecked {
                  if (b == 0) return (false, 0);
                  return (true, a % b);
              }
          }
          /**
           * @dev Returns the addition of two unsigned integers, reverting on
           * overflow.
           *
           * Counterpart to Solidity's `+` operator.
           *
           * Requirements:
           *
           * - Addition cannot overflow.
           */
          function add(uint256 a, uint256 b) internal pure returns (uint256) {
              return a + b;
          }
          /**
           * @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 a - b;
          }
          /**
           * @dev Returns the multiplication of two unsigned integers, reverting on
           * overflow.
           *
           * Counterpart to Solidity's `*` operator.
           *
           * Requirements:
           *
           * - Multiplication cannot overflow.
           */
          function mul(uint256 a, uint256 b) internal pure returns (uint256) {
              return a * b;
          }
          /**
           * @dev Returns the integer division of two unsigned integers, reverting on
           * division by zero. The result is rounded towards zero.
           *
           * Counterpart to Solidity's `/` operator.
           *
           * Requirements:
           *
           * - The divisor cannot be zero.
           */
          function div(uint256 a, uint256 b) internal pure returns (uint256) {
              return a / b;
          }
          /**
           * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
           * reverting when dividing by zero.
           *
           * Counterpart to Solidity's `%` operator. This function uses a `revert`
           * opcode (which leaves remaining gas untouched) while Solidity uses an
           * invalid opcode to revert (consuming all remaining gas).
           *
           * Requirements:
           *
           * - The divisor cannot be zero.
           */
          function mod(uint256 a, uint256 b) internal pure returns (uint256) {
              return a % b;
          }
          /**
           * @dev Returns the subtraction of two unsigned integers, reverting with custom message on
           * overflow (when the result is negative).
           *
           * CAUTION: This function is deprecated because it requires allocating memory for the error
           * message unnecessarily. For custom revert reasons use {trySub}.
           *
           * Counterpart to Solidity's `-` operator.
           *
           * Requirements:
           *
           * - Subtraction cannot overflow.
           */
          function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
              unchecked {
                  require(b <= a, errorMessage);
                  return a - b;
              }
          }
          /**
           * @dev Returns the integer division of two unsigned integers, reverting with custom message on
           * division by zero. The result is rounded towards zero.
           *
           * 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) {
              unchecked {
                  require(b > 0, errorMessage);
                  return a / b;
              }
          }
          /**
           * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
           * reverting with custom message when dividing by zero.
           *
           * CAUTION: This function is deprecated because it requires allocating memory for the error
           * message unnecessarily. For custom revert reasons use {tryMod}.
           *
           * Counterpart to Solidity's `%` operator. This function uses a `revert`
           * opcode (which leaves remaining gas untouched) while Solidity uses an
           * invalid opcode to revert (consuming all remaining gas).
           *
           * Requirements:
           *
           * - The divisor cannot be zero.
           */
          function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
              unchecked {
                  require(b > 0, errorMessage);
                  return a % b;
              }
          }
      }// SPDX-License-Identifier: MIT
      pragma solidity 0.8.20;
      import "@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol";
      import "@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol";
      import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
      import "./interface/IStaking20.sol";
      import "./math/SafeMath.sol";
      abstract contract Staking20Upgradeable is ReentrancyGuardUpgradeable, IStaking20 {
          
          /// @dev Total wallets staked their coins in the contract.
          uint256 public totalStakersCount;
          /// @dev Total amount of reward paid by the contract.
          uint256 public totalRewardPaid;
          /// @dev Total amount of coin staked in the contract.
          uint256 public totalCoinStaked;
          /// @dev Total amount of reward tokens in the contract currently.
          uint256 public rewardTokenBalance;
          ///@dev Address of ERC20 contract -- staked tokens belong to this contract.
          address public stakingToken;
          /// @dev Decimals of staking token.
          uint16 public stakingTokenDecimals;
          ///@dev Next staking condition Id. Tracks number of conditon updates so far.
          uint64 private nextConditionId;
          /// @dev Total amount of tokens staked in the contract.
          uint256 public stakingTokenBalance;
          /// @dev List of accounts that have staked that token-id.
          address[] public stakersArray;
          /// @dev Minimum stake amount to stake.
          uint256 public minStakeAmount;
          /// @dev Locktime in seconds.
          uint256 public locktime;
          ///@dev Mapping staker address to Staker struct.
          mapping(address => Staker) public stakers;
          ///@dev Mapping from condition Id to staking condition.
          mapping(uint256 => StakingCondition) private stakingConditions;
          function __Staking20_init(
              address _stakingToken,
              uint16 _stakingTokenDecimals
          ) internal onlyInitializing {
              __ReentrancyGuard_init();
              require(address(_stakingToken) != address(0), "token address 0");
              require(_stakingTokenDecimals != 0, "decimals 0");
              stakingToken = _stakingToken;
              stakingTokenDecimals = _stakingTokenDecimals;
          }
          /*///////////////////////////////////////////////////////////////
                              External/Public Functions
          //////////////////////////////////////////////////////////////*/
          /**
           *  @notice View amount staked, rewards and total rewards for a user.
           *
           *  @param _staker          Address for which to calculated rewards.
           *  @return _tokensStaked   Amount of tokens staked.
           *  @return _rewards        Available reward amount.
           *  @return _totalRewards   Total reward amount.
           */
          function getStakeInfo(address _staker) external view virtual returns (uint256 _tokensStaked, uint256 _rewards, uint256 _totalRewards) {
              _tokensStaked = stakers[_staker].amountStaked;
              _rewards = _availableRewards(_staker);
              _totalRewards = stakers[_staker].totalEarnedRewards;
          }
          /// @notice View locked coins of the staker.
          function getLockedCoins(address _staker) public view returns (LockedDeposits[] memory _lockedCoins) {
              _lockedCoins = stakers[_staker].lockedCoins;
          }
          /// @notice View timeunit used by the contract.
          function getTimeUnit() public view returns (uint80 _timeUnit) {
              _timeUnit = stakingConditions[nextConditionId - 1].timeUnit;
          }
          /// @notice View reward ratio for the current condition.
          function getRewardRatio() public view returns (uint256[] memory _numerator, uint256[] memory _denominator, uint256[] memory _rewardRatioAmount) {
              _numerator = stakingConditions[nextConditionId - 1].rewardRatioNumerator;
              _denominator = stakingConditions[nextConditionId - 1].rewardRatioDenominator;
              _rewardRatioAmount = stakingConditions[nextConditionId - 1].rewardRatioAmount;
          }
          /// @notice View total rewards available in the staking contract.
          function getRewardTokenBalance() public view returns (uint256) {
              return rewardTokenBalance;
          }
          /// @notice View total rewards paid by the staking contract.
          function getTotalRewardPaid() public view returns (uint256) {
              return totalRewardPaid;
          }
          /// @notice View total coins staked in the staking contract.
          function getTotalCoinStaked() public view returns (uint256) {
              return totalCoinStaked;
          }
          /// @notice View current staker count in the staking contract.
          function getCurrentStakerCount() public view returns (uint) {
              return stakersArray.length;
          }
          /// @notice View total staker count in the staking contract.
          function getTotalStakerCount() public view returns (uint256) {
              return totalStakersCount;
          }
          /// @notice View minimum stake amount in the staking contract.
          function getMinStakeAmount() public view returns (uint256) {
              return minStakeAmount;
          }
          /*///////////////////////////////////////////////////////////////
                                  Internal Functions
          //////////////////////////////////////////////////////////////*/
          /**
           *  @notice  Set lock time.
           *
           *  @param _newAmount    New locktime in seconds.
           */
          function _setLocktime(uint256 _newAmount) internal virtual {
              uint256 _oldAmount = locktime;
              locktime = _newAmount;
              emit UpdatedLocktime(_oldAmount, _newAmount);
          }
          /**
           *  @notice  Set minimum stake limit.
           *
           *  @param _newAmount    New minimum stake limit.
           */
          function _setMinStakeAmount(uint256 _newAmount) internal virtual {
              uint256 _oldAmount = minStakeAmount;
              minStakeAmount = _newAmount;
              emit UpdatedMinStakeAmount(_oldAmount, _newAmount);
          }
          /**
           *  @notice  Set time unit. Set as a number of seconds.
           *           Could be specified as -- x * 1 hours, x * 1 days, etc.
           *
           *  @param _timeUnit    New time unit.
           */
          function _setTimeUnit(uint80 _timeUnit) internal virtual {
              StakingCondition memory condition = stakingConditions[nextConditionId - 1];
              require(_timeUnit != condition.timeUnit, "Time-unit unchanged.");
              _setStakingCondition(_timeUnit, condition.rewardRatioNumerator, condition.rewardRatioDenominator, condition.rewardRatioAmount);
              emit UpdatedTimeUnit(condition.timeUnit, _timeUnit);
          }
          /**
           *  @notice  Set rewards per unit of time.
           *           Interpreted as (numerator/denominator) rewards per second/per day/etc based on time-unit.
           *
           *  @param _numerator    Reward ratio numerator.
           *  @param _denominator  Reward ratio denominator.
           *  @param _rewardRatioAmount  Reward ratio amount limit.
           */
          function _setRewardRatio(uint256[] memory _numerator, uint256[] memory _denominator, uint256[] memory _rewardRatioAmount) internal virtual {
              StakingCondition memory condition = stakingConditions[nextConditionId - 1];
              require(_numerator.length == _denominator.length && _denominator.length == _rewardRatioAmount.length, "Reward ratio mismatch");
              _setStakingCondition(condition.timeUnit, _numerator, _denominator, _rewardRatioAmount);
              emit UpdatedRewardRatio(
                  condition.rewardRatioNumerator,
                  _numerator,
                  condition.rewardRatioDenominator,
                  _denominator,
                  condition.rewardRatioAmount,
                  _rewardRatioAmount
              );
          }
          /// @dev Staking logic.
          function _stake(uint256 _amount) internal virtual returns (bool) {
              require(_amount != 0, "Staking 0 tokens");
              require(msg.value == 0, "Value not 0");
              require(_amount >= minStakeAmount, "Minimum stake limit not reached");
              if (!stakers[_stakeMsgSender()].stakedBefore) {
                  stakers[_stakeMsgSender()].stakedBefore = true;
                  totalStakersCount += 1;
              }
              if (stakers[_stakeMsgSender()].amountStaked > 0 || _checkLockedCoins(_stakeMsgSender()) == true) {
                  _updateUnclaimedRewardsForStaker(_stakeMsgSender());
              } else {
                  stakersArray.push(_stakeMsgSender());
                  stakers[_stakeMsgSender()].timeOfLastUpdate = uint80(block.timestamp);
                  stakers[_stakeMsgSender()].conditionIdOflastUpdate = nextConditionId - 1;
              }
              uint256 balanceBefore = IERC20(stakingToken).balanceOf(address(this));
              uint256 ourAllowance = IERC20(stakingToken).allowance(_stakeMsgSender(), address(this));
              require(_amount <= ourAllowance, 'Not enough allowance');
              bool success = IERC20(stakingToken).transferFrom(_stakeMsgSender(), address(this), _amount);
              require(success, 'Token transfer failed');
              uint256 actualAmount = IERC20(stakingToken).balanceOf(address(this)) - balanceBefore;
              stakers[_stakeMsgSender()].lockedCoins.push(LockedDeposits(actualAmount, block.timestamp + locktime));
              stakingTokenBalance += actualAmount;
              totalCoinStaked += actualAmount;
              emit TokensStaked(_stakeMsgSender(), actualAmount);
              return true;
          }
          /// @dev Withdraw logic.
          function _withdraw(uint256 _amount) internal virtual returns (bool) {
              require(_amount != 0, "Withdrawing 0 tokens");
              _updateUnclaimedRewardsForStaker(_stakeMsgSender());
              uint256 _amountStaked = stakers[_stakeMsgSender()].amountStaked;
              require(_amountStaked >= _amount, "Withdrawing more than staked");
              if (_amountStaked == _amount && _checkLockedCoins(_stakeMsgSender()) == false) {
                  address[] memory _stakersArray = stakersArray;
                  for (uint256 i = 0; i < _stakersArray.length; ++i) {
                      if (_stakersArray[i] == _stakeMsgSender()) {
                          stakersArray[i] = _stakersArray[_stakersArray.length - 1];
                          stakersArray.pop();
                          break;
                      }
                  }
              }
              stakers[_stakeMsgSender()].amountStaked -= _amount;
              stakingTokenBalance -= _amount;
              bool success = IERC20(stakingToken).transfer(_stakeMsgSender(), _amount);
              require(success, "Token transfer failed");
              emit TokensWithdrawn(_stakeMsgSender(), _amount);
              return true;
          }
          /// @dev Logic for claiming rewards.
          function _claimRewards() internal virtual returns (bool) {
              uint256 rewards = stakers[_stakeMsgSender()].unclaimedRewards + _calculateRewards(_stakeMsgSender());
              require(rewards != 0, "No rewards");
              require(rewards <= rewardTokenBalance, "Not enough reward tokens to claim");
              rewardTokenBalance -= rewards;
              _updateLockedCoins(_stakeMsgSender());
              stakers[_stakeMsgSender()].timeOfLastUpdate = uint80(block.timestamp);
              stakers[_stakeMsgSender()].unclaimedRewards = 0;
              stakers[_stakeMsgSender()].conditionIdOflastUpdate = nextConditionId - 1;
              bool success = IERC20(stakingToken).transfer(_stakeMsgSender(), rewards);
              require(success, "Token transfer failed");
              totalRewardPaid += rewards;
              stakers[_stakeMsgSender()].totalEarnedRewards += rewards;
              emit RewardsClaimed(_stakeMsgSender(), rewards);
              return true;
          }
          /// @dev View available rewards for a user.
          function _availableRewards(address _staker) internal view virtual returns (uint256 _rewards) {
              if (stakers[_staker].amountStaked == 0 && _getUnlockedCoinAmount(_staker) == 0) {
                  _rewards = stakers[_staker].unclaimedRewards;
              } else {
                  _rewards = stakers[_staker].unclaimedRewards + _calculateRewards(_staker);
              }
          }
          /// @dev Update unclaimed rewards for a users. Called for every state change for a user.
          function _updateUnclaimedRewardsForStaker(address _staker) internal virtual {
              _updateLockedCoins(_staker);
              uint256 rewards = _calculateRewards(_staker);
              stakers[_staker].unclaimedRewards += rewards;
              stakers[_staker].timeOfLastUpdate = uint80(block.timestamp);
              stakers[_staker].conditionIdOflastUpdate = nextConditionId - 1;
          }
          /// @dev Update locked coins for users. Called for every state change for a user.
          function _updateLockedCoins(address _staker) internal virtual {
              for (uint256 j = 0; j < stakers[_staker].lockedCoins.length; j++) {
                  if (block.timestamp >= stakers[_staker].lockedCoins[j].lockedUntilTime && stakers[_staker].lockedCoins[j].lockedUntilTime != 0) {
                      stakers[_staker].amountStaked += stakers[_staker].lockedCoins[j].amountDeposited;
                      delete stakers[_staker].lockedCoins[j];
                  }
              }
          }
          /// @dev Set staking conditions.
          function _setStakingCondition(uint80 _timeUnit, uint256[] memory _numerator, uint256[] memory _denominator, uint256[] memory _rewardRatioAmount) internal virtual {
              require(
                  _numerator.length != 0 || _denominator.length != 0 || _rewardRatioAmount.length != 0,
                  "Numerator, denominator or amount data is missing"
              );
              require(_timeUnit != 0, "time-unit can't be 0");
              uint256 conditionId = nextConditionId;
              nextConditionId += 1;
              stakingConditions[conditionId] = StakingCondition({
                  timeUnit: _timeUnit,
                  rewardRatioNumerator: _numerator,
                  rewardRatioDenominator: _denominator,
                  rewardRatioAmount: _rewardRatioAmount,
                  startTimestamp: uint80(block.timestamp),
                  endTimestamp: 0
              });
              if (conditionId > 0) {
                  stakingConditions[conditionId - 1].endTimestamp = uint80(block.timestamp);
              }
          }
          /// @dev Check if staker has locked coins.
          function _checkLockedCoins(address _staker) internal view returns (bool) {
              Staker memory staker = stakers[_staker];
              bool lockedCoinAvailable = false;
          
              for (uint256 j = 0; j < staker.lockedCoins.length; j++) {
                  if (staker.lockedCoins[j].lockedUntilTime != 0) {
                      lockedCoinAvailable = true;
                      break;
                  }
              }
              return lockedCoinAvailable;
          }
          /// @dev Get Numerator and denominator based on stakers staked amount.
          function _calculateStakerRatio(address _staker, StakingCondition memory _condition, uint256 _freedCoins) internal view virtual returns (uint) {
              uint _stakerRatio = 0;
              for (uint i = _condition.rewardRatioAmount.length; i > 0; i--) {
                  if (stakers[_staker].amountStaked + _freedCoins >= _condition.rewardRatioAmount[i - 1]) {
                      _stakerRatio = i - 1;
                      break;
                  }
              }
              return _stakerRatio;
          }
          /// @dev Calculate unlocked coins for a staker.
          function _getUnlockedCoinAmount(address _staker) internal view returns (uint256) {
              Staker memory staker = stakers[_staker];
              uint256 unlockedCoinAmount = 0;
          
              for (uint256 j = 0; j < staker.lockedCoins.length; j++) {
                  if (block.timestamp >= staker.lockedCoins[j].lockedUntilTime && staker.lockedCoins[j].lockedUntilTime != 0) {
                      unlockedCoinAmount += staker.lockedCoins[j].amountDeposited;
                  }
              }
              return unlockedCoinAmount;
          }
          /// @dev Calculate rewards for a staker.
          function _calculateRewards(address _staker) internal view virtual returns (uint256 _rewards) {
              Staker memory staker = stakers[_staker];
              uint256 _stakerConditionId = staker.conditionIdOflastUpdate;
              uint256 _nextConditionId = nextConditionId;
              uint256 unlockedCoinAmount = _getUnlockedCoinAmount(_staker);
              for (uint256 i = _stakerConditionId; i < _nextConditionId; i += 1) {
                  StakingCondition memory condition = stakingConditions[i];
                  uint stakerRatio = _calculateStakerRatio(_staker, condition, unlockedCoinAmount);
                  uint256 startTime = i != _stakerConditionId ? condition.startTimestamp : staker.timeOfLastUpdate;
                  uint256 endTime = condition.endTimestamp != 0 ? condition.endTimestamp : block.timestamp;
                  (bool noOverflowProduct, uint256 rewardsProduct) = SafeMath.tryMul(
                      (endTime - startTime) * (staker.amountStaked + unlockedCoinAmount),
                      condition.rewardRatioNumerator[stakerRatio]
                  );
                  (bool noOverflowSum, uint256 rewardsSum) = SafeMath.tryAdd(
                      _rewards,
                      (rewardsProduct / condition.timeUnit) / condition.rewardRatioDenominator[stakerRatio]
                  );
                  _rewards = noOverflowProduct && noOverflowSum ? rewardsSum : _rewards;
              }
              (, _rewards) = SafeMath.tryMul(_rewards, 10 ** stakingTokenDecimals);
              _rewards /= (10 ** stakingTokenDecimals);
          }
          /// @dev Exposes the ability to override the msg sender
          function _stakeMsgSender() internal virtual returns (address) {
              return msg.sender;
          }
      }