Search code examples
ethereumsoliditysmartcontractsremixether

How to add a uint T in constructor that will be a time constant inside my smart contract?


I need a time constant to calculate timestamps for deposit, withdrawal, and reward sub-pools. this time constant called T will start from contract deployment and will not be specific to one address/user. I.e rewards(R) are divided into 3 sub-pools: R1 = 20% available after 2T has passed since contract deployment, R2 = 30% available after 3T has passed since contract deployment, R3= 50% available after 4T has passed since contract deployment. I need this variable to be in minutes, for example, sender inputs 3 I want the time between each timestamp to be 3, like deposit in the first T, withdrawal after 3T. How to set it in minutes? Here is the state variable

uint256 public T;

Here is the constructor

constructor(address stakingToken, address rewardsToken, uint256 timeConstant) {
        s_stakingToken = IERC20(stakingToken);
        s_rewardsToken = IERC20(rewardsToken);
        initialTime = block.timestamp;
        T = timeConstant;
    }

My full code

    error TransferFailed();
    error NeedsMoreThanZero();
    
    contract Staking is ReentrancyGuard {
        IERC20 public s_rewardsToken;
        IERC20 public s_stakingToken;
    
        // This is the reward token per seco

nd
    // Which will be multiplied by the tokens the user staked divided by the total
    // This ensures a steady reward rate of the platform
    // So the more users stake, the less for everyone who is staking.
    uint256 public constant REWARD_RATE = 100;
    uint256 public s_lastUpdateTime;
    uint256 public s_rewardPerTokenStored;
    //uint256 public constant T = 3;
    uint256 public initialTime;
    uint256 public T;

    mapping(address => uint256) public s_userRewardPerTokenPaid;
    mapping(address => uint256) public s_rewards;

    uint256 private s_totalSupply;
    mapping(address => uint256) public s_balances; // someones address => how much he staked

    event Staked(address indexed user, uint256 indexed amount);
    event WithdrewStake(address indexed user, uint256 indexed amount);
    event RewardsClaimed(address indexed user, uint256 indexed amount);

    constructor(address stakingToken, address rewardsToken, uint256 timeConstant) {
        s_stakingToken = IERC20(stakingToken);
        s_rewardsToken = IERC20(rewardsToken);
        initialTime = block.timestamp;
        T = timeConstant;
    }

    /**
     * @notice How much reward a token gets based on how long it's been in and during which "snapshots"
     */
    function rewardPerToken() public view returns (uint256) {
        if (s_totalSupply == 0) {
            return s_rewardPerTokenStored;
        }
        return
            s_rewardPerTokenStored +
            (((block.timestamp - s_lastUpdateTime) * REWARD_RATE * 1e18) / s_totalSupply);
    }

    /**
     * @notice How much reward a user has earned
     */
    function earned(address account) public view returns (uint256) {
        uint256 nowTime = block.timestamp-initialTime;
        require(nowTime > 2*T);
        if (nowTime > 2*T && nowTime <= 3*T) {
                return
            ((((s_balances[account] * (rewardPerToken() - s_userRewardPerTokenPaid[account])) /
                1e18) + s_rewards[account]) / 5 );
        } else if
            (nowTime > 3*T && nowTime <= 4*T) {
                return
            ((((s_balances[account] * (rewardPerToken() - s_userRewardPerTokenPaid[account])) /
                1e18) + s_rewards[account]) / 2 );
        } else {
            return
            (((s_balances[account] * (rewardPerToken() - s_userRewardPerTokenPaid[account])) /
                1e18) + s_rewards[account]);
        }
        
    }

    /**
     * @notice Deposit tokens into this contract
     * @param amount | How much to stake
     */
    function stake(uint256 amount)
        external
        updateReward(msg.sender)
        nonReentrant
        moreThanZero(amount)
    {
        //from T0 to T deposit is available
        uint256 nowTime = block.timestamp-initialTime; // time between deployment of contract and now.
        require(nowTime < T);
        s_totalSupply += amount; 
        // increasing how much they are staking for how much they staked each time.
        s_balances[msg.sender] += amount; 
        emit Staked(msg.sender, amount);
        // sending an amount of token from an address to this contract
        bool success = s_stakingToken.transferFrom(msg.sender, address(this), amount);  
        // if not successfully sent, return an error.
        // using the below code instead of "require(success, "Failed")" reduces gas fees, since it doesn't output a string.
        if (!success) {
            revert TransferFailed(); // revert resets everything done in a failed transaction.
        }
    }

    /**
     * @notice Withdraw tokens from this contract
     * @param amount | How much to withdraw
     */
    function withdraw(uint256 amount) external updateReward(msg.sender) nonReentrant {
        // from 2T the user can reward his tokens
        uint256 nowTime = block.timestamp-initialTime; // time between deployment of contract and now.
        require(nowTime > 2* T);
        s_totalSupply -= amount;
        s_balances[msg.sender] -= amount;
        emit WithdrewStake(msg.sender, amount);
        // transfer: send tokens from contract back to msg.sender.
        bool success = s_stakingToken.transfer(msg.sender, amount);
        if (!success) {
            revert TransferFailed(); // revert resets everything done in a failed transaction.
        }
    }

    /**
     * @notice User claims their tokens
     */
    function claimReward() external updateReward(msg.sender) nonReentrant {
        uint256 reward = s_rewards[msg.sender];
        s_rewards[msg.sender] = 0;
        emit RewardsClaimed(msg.sender, reward);
        bool success = s_rewardsToken.transfer(msg.sender, reward);
        if (!success) {
            revert TransferFailed(); // revert resets everything done in a failed transaction.
        }
    }

    /********************/
    /* Modifiers Functions */
    /********************/
    modifier updateReward(address account) {
        s_rewardPerTokenStored = rewardPerToken();
        s_lastUpdateTime = block.timestamp;
        s_rewards[account] = earned(account);
        s_userRewardPerTokenPaid[account] = s_rewardPerTokenStored;
        _;
    }

    modifier moreThanZero(uint256 amount) {
        if (amount == 0) {
            revert NeedsMoreThanZero();
        }
        _;
    }

    /********************/
    /* Getter Functions */
    /********************/
    // Ideally, we'd have getter functions for all our s_ variables we want exposed, and set them all to private.
    // But, for the purpose of this demo, we've left them public for simplicity.

    function getStaked(address account) public view returns (uint256) {
        return s_balances[account];
    }
}

Solution

  • Firstly maintain a map for storing the time for each user. Then make functions like depositTimeSet , withdrawalTimeSet for storing different time for different user. While despositing update the variable and while withdraw check the time has exceed or not.

    struct Users {
            uint dipositTime;
            uint withDrawTime;
            uint lastDepositTime;
    }
    mapping(address => Users ) users;
    
    function depositeTimeSet(uint t) {
      users[msg.sender].dipositTime = t minutes;
      withdrawalTimeSet(t);
    }
    function withdrawalTimeSet(uint t) {
      users[msg.sender].withDrawTime = 3 * t minutes
    }
    function deposite() {
      transferFrom(msg.sender,address(this));
      depositeTimeSet(3); // considering sender send 3
      users[msg.sender].lastDepositTime = now;
    }
    function withdraw() {
      if(
         now > users[msg.sender].lastDepositTime + 
         users[msg.sender].withDrawTime,"too early for withdraw 
         request"
      )
      transferFrom(address(this),msg.sender);
    }
    

    You can see THIS one might be helpful

    You can see THIS resource for time unit in solidity