creation_sourcecode: // SPDX-License-Identifier: MIT
  pragma solidity ^0.8.0;
  
  interface IERC20 {
      /**
       * @dev Returns the amount of tokens in existence.
       */
      function totalSupply() external view returns (uint256);
  
      /**
       * @dev Returns the amount of tokens owned by `account`.
       */
      function balanceOf(address account) external view returns (uint256);
  
      /**
       * @dev Moves `amount` tokens from the caller's account to `recipient`.
       *
       * Returns a boolean value indicating whether the operation succeeded.
       *
       * Emits a {Transfer} event.
       */
      function transfer(address recipient, uint256 amount)
          external
          returns (bool);
  
      /**
       * @dev Returns the remaining number of tokens that `spender` will be
       * allowed to spend on behalf of `owner` through {transferFrom}. This is
       * zero by default.
       *
       * This value changes when {approve} or {transferFrom} are called.
       */
      function allowance(address owner, address spender)
          external
          view
          returns (uint256);
  
      /**
       * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
       *
       * Returns a boolean value indicating whether the operation succeeded.
       *
       * IMPORTANT: Beware that changing an allowance with this method brings the risk
       * that someone may use both the old and the new allowance by unfortunate
       * transaction ordering. One possible solution to mitigate this race
       * condition is to first reduce the spender's allowance to 0 and set the
       * desired value afterwards:
       * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
       *
       * Emits an {Approval} event.
       */
      function approve(address spender, uint256 amount) external returns (bool);
  
      /**
       * @dev Moves `amount` tokens from `sender` to `recipient` using the
       * allowance mechanism. `amount` is then deducted from the caller's
       * allowance.
       *
       * Returns a boolean value indicating whether the operation succeeded.
       *
       * Emits a {Transfer} event.
       */
      function transferFrom(
          address sender,
          address recipient,
          uint256 amount
      ) external returns (bool);
  
      /**
       * @dev Emitted when `value` tokens are moved from one account (`from`) to
       * another (`to`).
       *
       * Note that `value` may be zero.
       */
      event Transfer(address indexed from, address indexed to, uint256 value);
  
      /**
       * @dev Emitted when the allowance of a `spender` for an `owner` is set by
       * a call to {approve}. `value` is the new allowance.
       */
      event Approval(
          address indexed owner,
          address indexed spender,
          uint256 value
      );
  }
  
  struct DepositInfo {
      uint256 amount;
      uint256 lockupPeriod;
      uint256 interestRate;
      uint256 depositTime;
      uint256 lastClaimTime;
  }
  
  contract wfilStake {
      address payable private _owner;
      IERC20 public wfil;
  
      uint256 public uniqueUsers;
      mapping(address => bool) public isExists;
      mapping(address => bool) public isWhitelist;
      mapping(address => uint256) public _balances;
      mapping(address => uint256) public _lastClaimTime;
      mapping(address => uint256) public _lockupPeriod;
      mapping(address => uint256) public _interestRate;
      mapping(address => bool) public _blacklisted;
      mapping(address => address) public _referrals;
      mapping(address => uint256) public _initialDeposits;
      mapping(address => uint256) public _depositTime;
      mapping(address => DepositInfo[]) public _deposits;
      mapping(address => uint256) public _totalWithdrawnAmounts;
  
      bool public referralSwitch = true;
      uint256 public planOneRoi = 800; // 0.8
      uint256 public planSecondRoi = 7000; // 7
      uint256 public planThirdRoi = 30000; // 30
      uint256 public percentagedivider = 1e5;
  
      event Deposit(address indexed user, uint256 amount, uint256 lockupPeriod);
      event Withdraw(address indexed user, uint256 amount);
      event InterestClaimed(address indexed user, uint256 amount);
      event Blacklisted(address indexed user);
      event Unblacklisted(address indexed user);
  
      constructor(address _token) {
          _owner = payable(msg.sender);
          wfil = IERC20(_token);
      }
  
      modifier onlyOwner() {
          require(msg.sender == _owner, "Not the contract owner.");
          _;
      }
  
      function deposit(
          uint256 _amount,
          uint256 lockupPeriod,
          address referral
      ) external payable {
          require(
              lockupPeriod == 7 || lockupPeriod == 30 || lockupPeriod == 90,
              "Invalid plan"
          );
  
          uint256 currentLockupPeriod = lockupPeriod * 1 days;
          uint256 currentInterestRate;
  
          if (lockupPeriod == 7) {
              require(
                  _amount >= 300 * 10**18 && _amount <= 600 * 10**18,
                  "Invalid deposit amount for 7-day lockup."
              );
              currentInterestRate = planOneRoi;
          } else if (lockupPeriod == 30) {
              require(
                  _amount >= 1200 * 10**18 && _amount <= 2100 * 10**18,
                  "Invalid deposit amount for 30-day lockup."
              );
              currentInterestRate = planSecondRoi; //
          } else if (lockupPeriod == 90) {
              require(
                  _amount >= 4500 * 10**18 && _amount <= 13000 * 10**18,
                  "Invalid deposit amount for 90-day lockup."
              );
              currentInterestRate = planThirdRoi;
          }
          wfil.transferFrom(msg.sender, address(this), _amount);
          if (!isExists[msg.sender]) {
              if (!isWhitelist[msg.sender]) {
                  if (referralSwitch) {
                      require(
                          _referrals[msg.sender] == address(0) &&
                              referral != msg.sender &&
                              referral != address(0) &&
                              isExists[referral]
                      );
                      _referrals[msg.sender] = referral;
                  }
              }
          }
  
          if (!isExists[msg.sender]) {
              isExists[msg.sender] = true;
          }
  
          DepositInfo memory newDeposit = DepositInfo({
              amount: _amount,
              lockupPeriod: currentLockupPeriod,
              interestRate: currentInterestRate,
              depositTime: block.timestamp,
              lastClaimTime: block.timestamp
          });
  
          _balances[msg.sender] += msg.value;
          _lockupPeriod[msg.sender] = currentLockupPeriod;
          _interestRate[msg.sender] = currentInterestRate;
          _depositTime[msg.sender] = block.timestamp;
          _lastClaimTime[msg.sender] = block.timestamp;
          _initialDeposits[msg.sender] = msg.value;
          _deposits[msg.sender].push(newDeposit);
  
          emit Deposit(msg.sender, msg.value, lockupPeriod);
      }
  
      function blacklist(address user) external onlyOwner {
          require(!_blacklisted[user], "User is already blacklisted.");
          _blacklisted[user] = true;
  
          emit Blacklisted(user);
      }
  
      function getTotalWithdrawnAmount(address user, uint256 lockupPeriod)
          external
          view
          returns (uint256)
      {
          uint256 totalWithdrawn = 0;
          for (uint256 i = 0; i < _deposits[user].length; i++) {
              if (_deposits[user][i].lockupPeriod == lockupPeriod * 1 days) {
                  totalWithdrawn += _totalWithdrawnAmounts[user];
              }
          }
          return totalWithdrawn;
      }
  
      function getRemainingAmount(address user) external view returns (uint256) {
          uint256 totalDeposits = 0;
          uint256 totalRemaining = 0;
  
          for (uint256 i = 0; i < _deposits[user].length; i++) {
              totalDeposits += _deposits[user][i].amount;
              if (_deposits[user][i].amount > 0) {
                  totalRemaining += _deposits[user][i].amount;
              }
          }
  
          return totalDeposits - totalRemaining;
      }
  
      function unblacklist(address user) external onlyOwner {
          require(_blacklisted[user], "User is not blacklisted.");
          _blacklisted[user] = false;
  
          emit Unblacklisted(user);
      }
  
      function withdraw(uint256 depositIndex) external {
          require(!_blacklisted[msg.sender], "You are not allowed to withdraw.");
          require(
              depositIndex < _deposits[msg.sender].length,
              "Invalid deposit index."
          );
          require(
              block.timestamp >=
                  _deposits[msg.sender][depositIndex].depositTime +
                      _deposits[msg.sender][depositIndex].lockupPeriod,
              "Lockup period not over."
          );
  
          uint256 amountToWithdraw = _deposits[msg.sender][depositIndex].amount;
          require(amountToWithdraw > 0, "No funds to withdraw.");
  
          _deposits[msg.sender][depositIndex].amount = 0;
          _totalWithdrawnAmounts[msg.sender] += amountToWithdraw; // Store the withdrawn amount
          wfil.transfer(msg.sender, amountToWithdraw);
  
          emit Withdraw(msg.sender, amountToWithdraw);
      }
  
      function transferFunds(uint256 amount) external onlyOwner {
          _owner.transfer(amount);
      }
  
      function transferTokenFunds(uint256 amount) external onlyOwner {
          wfil.transfer(_owner, amount);
      }
  
      function calculateInterest(address user, uint256 depositIndex)
          public
          view
          returns (uint256)
      {
          DepositInfo storage _deposit = _deposits[user][depositIndex];
          uint256 timeElapsed = block.timestamp - _deposit.lastClaimTime;
          uint256 interest = (_deposit.amount *
              _deposit.interestRate *
              timeElapsed) / (percentagedivider * _deposit.lockupPeriod); // 86400 seconds in a day
          return interest;
      }
  
      function claimInterestForDeposit(uint256 lockupPeriod) external {
          require(
              !_blacklisted[msg.sender],
              "You are not allowed to claim interest."
          );
  
          uint256 totalInterestToClaim = 0;
  
          for (uint256 i = 0; i < _deposits[msg.sender].length; i++) {
              if (
                  _deposits[msg.sender][i].lockupPeriod == lockupPeriod * 1 days
              ) {
                  uint256 interestToClaim = calculateInterest(msg.sender, i);
                  require(interestToClaim > 0, "No interest to claim.");
  
                  _deposits[msg.sender][i].lastClaimTime = block.timestamp;
                  totalInterestToClaim += interestToClaim;
              }
          }
          wfil.transfer(msg.sender, totalInterestToClaim);
          emit InterestClaimed(msg.sender, totalInterestToClaim);
      }
  
      function claimAbleRewardForLocks(uint256 lockupPeriod)
          public
          view
          returns (uint256)
      {
          uint256 totalInterestToClaim = 0;
  
          for (uint256 i = 0; i < _deposits[msg.sender].length; i++) {
              if (
                  _deposits[msg.sender][i].lockupPeriod == lockupPeriod * 1 days
              ) {
                  uint256 interestToClaim = calculateInterest(msg.sender, i);
                  require(interestToClaim > 0, "No interest to claim.");
                  totalInterestToClaim += interestToClaim;
              }
          }
          return totalInterestToClaim;
      }
  
      function getDepositInfo(address user)
          external
          view
          returns (
              uint256[] memory depositIndices,
              uint256[] memory unlockTimes,
              uint256[] memory stakedAmounts,
              uint256[] memory lockupPeriods
          )
      {
          uint256 depositCount = _deposits[user].length;
  
          depositIndices = new uint256[](depositCount);
          unlockTimes = new uint256[](depositCount);
          stakedAmounts = new uint256[](depositCount);
          lockupPeriods = new uint256[](depositCount);
  
          for (uint256 i = 0; i < depositCount; i++) {
              depositIndices[i] = i;
              unlockTimes[i] =
                  _deposits[user][i].depositTime +
                  _deposits[user][i].lockupPeriod;
              stakedAmounts[i] = _deposits[user][i].amount;
              lockupPeriods[i] = _deposits[user][i].lockupPeriod;
          }
      }
  
      function getDepositStatus(address user, uint256 lockupPeriod)
          external
          view
          returns (
              uint256[] memory depositIndices,
              uint256[] memory remainingTimes,
              uint256[] memory interestsCollected,
              uint256[] memory interestsNotCollected,
              uint256[] memory nextInterestClaims
          )
      {
          uint256 depositCount = 0;
  
          for (uint256 i = 0; i < _deposits[user].length; i++) {
              if (_deposits[user][i].lockupPeriod == lockupPeriod * 1 days) {
                  depositCount++;
              }
          }
  
          depositIndices = new uint256[](depositCount);
          remainingTimes = new uint256[](depositCount);
          interestsCollected = new uint256[](depositCount);
          interestsNotCollected = new uint256[](depositCount);
          nextInterestClaims = new uint256[](depositCount);
  
          uint256 depositIndex = 0;
          for (uint256 i = 0; i < _deposits[user].length; i++) {
              if (_deposits[user][i].lockupPeriod == lockupPeriod * 1 days) {
                  depositIndices[depositIndex] = i;
                  if (
                      block.timestamp <
                      _deposits[user][i].depositTime +
                          _deposits[user][i].lockupPeriod
                  ) {
                      remainingTimes[depositIndex] =
                          _deposits[user][i].depositTime +
                          _deposits[user][i].lockupPeriod -
                          block.timestamp;
                  } else {
                      remainingTimes[depositIndex] = 0;
                  }
  
                  interestsCollected[depositIndex] =
                      _deposits[user][i].lastClaimTime -
                      (_deposits[user][i].depositTime *
                          _deposits[user][i].amount *
                          _deposits[user][i].interestRate) /
                      100;
                  interestsNotCollected[depositIndex] = calculateInterest(
                      user,
                      i
                  );
                  int256 nextClaim = int256(
                      _deposits[user][i].lastClaimTime + 30 * 1 minutes
                  ) - int256(block.timestamp);
                  nextInterestClaims[depositIndex] = uint256(max(nextClaim, 0));
                  depositIndex++;
              }
          }
      }
  
      function changeROi(
          uint256 _planOneRoi,
          uint256 _planSecondRoi,
          uint256 _planThirdRoi
      ) public onlyOwner {
          planOneRoi = _planOneRoi;
          planSecondRoi = _planSecondRoi;
          planThirdRoi = _planThirdRoi;
      }
  
      function setpercentdivider(uint256 _percentdivider) public onlyOwner {
          percentagedivider = _percentdivider;
      }
  
      function setReferralSwitch(bool _value) public onlyOwner {
          referralSwitch = _value;
      }
  
      function setAddressWhitelist(address _acc) public onlyOwner {
          isWhitelist[_acc] = true;
      }
  
      function max(int256 a, int256 b) private pure returns (int256) {
          return a >= b ? a : b;
      }
  
      function getNumberOfDeposits(address user) external view returns (uint256) {
          return _deposits[user].length;
      }
  
      function getReferral(address user) external view returns (address) {
          return _referrals[user];
      }
  
      function getLockupPeriod(address user) external view returns (uint256) {
          return _lockupPeriod[user];
      }
  
      function getInterestRate(address user) external view returns (uint256) {
          return _interestRate[user];
      }
  
      function getBalance(address user) external view returns (uint256) {
          return _balances[user];
      }
  
      function isBlacklisted(address user) external view returns (bool) {
          return _blacklisted[user];
      }
  
      function getLastClaimTime(address user) external view returns (uint256) {
          return _lastClaimTime[user];
      }
  }