Multi-Level Staking Smart Contract on Ethereum with Solidity

Posted By : Rahul

Oct 29, 2024

Introduction to Multi-Level Staking Smart Contract Development

 

Creating a multi-level staking contract on Ethereum using smart contract development opens up exciting possibilities for decentralized finance projects by enabling layered rewards and incentives for users. Using Solidity, Ethereum's native programming language, developers can build secure and scalable staking solutions that allow participants to earn rewards based on their staking levels. In this guide, we'll walk through the process of developing a multi-level staking contract, covering everything from setup to implementation, so you can leverage Ethereum's blockchain for advanced staking functionality.

 

In this article, we will discuss the basics of staking contracts, and the characteristics of multi-level staking contracts.

 

Prerequisites

 

  • Familiarity with Solidity and the ERC-20 token standard.
  • Understanding of concepts like staking.
  • An ERC-20 token contract deployed on the same network where you'll deploy this staking contract.
  • Familiar with Remix IDE

     

    You may also like | Creating a Token Vesting Contract on Solana Blockchain

     

    What is Staking

     

    To maintain the security of a blockchain network, confirm transactions, and generate rewards, cryptocurrency holders stake or lock up their assets. Staking, particularly on Proof-of-Stake (PoS) blockchains and their variations, entails actively taking part in the network's functioning as opposed to conventional bank savings or investments.

     

    Multi-level staking

     

    Multi-level staking is an advanced staking model where users can earn different levels of rewards based on various criteria, such as the amount of assets they stake or the duration they choose to lock their funds.

     

    Also, Explore | How to Implement a Merkle Tree for Secure Data Verification

     

    Multi-Level Staking Contract on Ethereum Using Solidity

     

// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.24;

interface ERC20 {
    function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
    function transfer(address recipient, uint256 amount) external returns (bool);
}

contract MultiLevelStaking {

    struct Stake {
        uint256 amount;
        uint256 startTime;
        uint256 level;
    }

   mapping(address => Stake[]) public stakes;
    mapping(uint256 => uint256) public rewardRates;

    uint256 constant LEVEL_ONE_MIN = 10 * 10**18;
    uint256 constant LEVEL_TWO_MIN = 20 * 10**18;
    uint256 constant LEVEL_THREE_MIN = 50 * 10**18;

    address public tokenAddress;

    constructor(address _tokenAddress) {
        tokenAddress = _tokenAddress;

        rewardRates[1] = 5;
        rewardRates[2] = 10;
        rewardRates[3] = 15;
    }

    function stake(uint256 amount) public {
    require(amount > 0, 'Amount should be greater than 0');
    require(ERC20(tokenAddress).transferFrom(msg.sender, address(this), amount), 'Transfer failed');

    uint256 level = getStakeLevel(amount);

    // Add new stake to the user's array of stakes
    stakes[msg.sender].push(Stake({
        amount: amount,
        startTime: block.timestamp,
        level: level
    }));
}

    function getStakeLevel(uint256 amount) internal pure returns (uint256) {
        if (amount >= LEVEL_THREE_MIN) {
            return 3;
        } else if (amount >= LEVEL_TWO_MIN) {
      return 2;
        } else if (amount >= LEVEL_ONE_MIN) {
            return 1;
        }
        return 0;
    }

    function calculateReward(address staker) public view returns (uint256) {
    Stake[] memory userStakes = stakes[staker];
    require(userStakes.length > 0, 'No active stakes');

    uint256 totalReward = 0;

    for (uint256 i = 0; i < userStakes.length; i++) {
        Stake memory stakeInfo = userStakes[i];
        uint256 stakingDuration = block.timestamp - stakeInfo.startTime;
        uint256 rate = rewardRates[stakeInfo.level];
        uint256 reward = (stakeInfo.amount * rate * stakingDuration) / (365 days * 100);
        totalReward += reward;
    }

    return totalReward;
}


   function unstakeAll() public {
    Stake[] memory userStakes = stakes[msg.sender];
    require(userStakes.length > 0, 'No active stakes');

    uint256 totalAmount = 0;

    // Loop through each stake, calculate reward, and add to total amount
    for (uint256 i = 0; i < userStakes.length; i++) {
        uint256 reward = calculateSingleStakeReward(userStakes[i]);
        totalAmount += userStakes[i].amount + reward;
    }

    // Clear all stakes for the user
    delete stakes[msg.sender];

    // Transfer the total amount back to the user
    require(ERC20(tokenAddress).transfer(msg.sender, totalAmount), 'Transfer failed');
}
function unstake(uint256 index) public {
   require(index < stakes[msg.sender].length, 'Invalid index');

    Stake memory stakeInfo = stakes[msg.sender][index];
    uint256 reward = calculateSingleStakeReward(stakeInfo);

    uint256 totalAmount = stakeInfo.amount + reward;

    // Remove the stake from the array by swapping and popping
    stakes[msg.sender][index] = stakes[msg.sender][stakes[msg.sender].length - 1];
    stakes[msg.sender].pop();

    // Transfer the unstaked amount plus reward back to the user
    require(ERC20(tokenAddress).transfer(msg.sender, totalAmount), 'Transfer failed');
}
function calculateSingleStakeReward(Stake memory stakeInfo) internal view returns (uint256) {
    uint256 stakingDuration = block.timestamp - stakeInfo.startTime;
    uint256 rate = rewardRates[stakeInfo.level];
    return (stakeInfo.amount * rate * stakingDuration) / (365 days * 100);
}
}

 

Also, Read | Smart Contract Upgradability | Proxy Patterns in Solidity

 

Explanation of the Each Function

 

Constructor

 

The Constructor Initializes the contract with the token address and sets reward rates for each staking level.

 

constructor(address _tokenAddress) {
    tokenAddress = _tokenAddress;
    rewardRates[1] = 5;
    rewardRates[2] = 10;
    rewardRates[3] = 15;
}

 

Stake

 

The stake function allows users to stake a specified amount of tokens, recording the staking level, amount, and start time.

 

function stake(uint256 amount) public {
    require(amount > 0, 'Amount should be greater than 0');
    require(ERC20(tokenAddress).transferFrom(msg.sender, address(this), amount), 'Transfer failed');

    uint256 level = getStakeLevel(amount);

    stakes[msg.sender].push(Stake({
        amount: amount,
        startTime: block.timestamp,
        level: level
    }));
}

 

calculateReward

 

This method calculates the total rewards earned for all stakes of a particular user and returns the rewards.

 

function calculateReward(address staker) public view returns (uint256) {
    Stake[] memory userStakes = stakes[staker];
    require(userStakes.length > 0, 'No active stakes');

    uint256 totalReward = 0;

    for (uint256 i = 0; i < userStakes.length; i++) {
        Stake memory stakeInfo = userStakes[i];
        uint256 stakingDuration = block.timestamp - stakeInfo.startTime;
        uint256 rate = rewardRates[stakeInfo.level];
        uint256 reward = (stakeInfo.amount * rate * stakingDuration) / (365 days * 100);
        totalReward += reward;
    }

    return totalReward;
}

 

unstakeAll

 

The unstake all function allows a user to unstake all of their stakes and receive the total staked amount plus all rewards.

 

function unstakeAll() public {
    Stake[] memory userStakes = stakes[msg.sender];
    require(userStakes.length > 0, 'No active stakes');

    uint256 totalAmount = 0;

    for (uint256 i = 0; i < userStakes.length; i++) {
        uint256 reward = calculateSingleStakeReward(userStakes[i]);
        totalAmount += userStakes[i].amount + reward;
    }

    delete stakes[msg.sender];

    require(ERC20(tokenAddress).transfer(msg.sender, totalAmount), 'Transfer failed');
}

 

unstake

 

The unstake function allows users to unstake a specific stake by index and receive the principal plus rewards for that specific stake.

 

function unstake(uint256 index) public {
    require(index < stakes[msg.sender].length, 'Invalid index');

    Stake memory stakeInfo = stakes[msg.sender][index];
    uint256 reward = calculateSingleStakeReward(stakeInfo);

    uint256 totalAmount = stakeInfo.amount + reward;

    stakes[msg.sender][index] = stakes[msg.sender][stakes[msg.sender].length - 1];
    stakes[msg.sender].pop();

    require(ERC20(tokenAddress).transfer(msg.sender, totalAmount), 'Transfer failed');
}

 

Also, Explore | How to Write and Deploy Modular Smart Contracts

 

Steps to Create and Deploy on Remix

 

  • Go to Remix IDE, which is a browser-based Solidity development environment.
  • In the Remix IDE, create a new file under the contracts folder. Name it MultiLevelStaking.sol.
    Paste the MultiLevelStaking Solidity contract code into this file.
  • Set the compiler version to 0.8.24 
  • Click the Compile MultiLevelStaking.sol button.

     

Go to the 'Deploy & Run Transactions' tab.

 

  1. Set Environment to Injected Web3 to deploy using MetaMask 
  2. In the Deploy section, input the constructor argument:
    _tokenAddress: Address of the ERC-20 token contract that users will be staking.
  3. Click Deploy, and MetaMask will prompt you to confirm the transaction. Confirm and pay for the gas fee.


    Verify Deployment:

     

    After deployment, the contract instance will appear under the Deployed Contracts section in Remix.

     

    Conclusion 

     

    Building a multi-level staking contract on Ethereum with Solidity allows you to harness the power of decentralized finance while providing enhanced incentives for your users. With layered rewards and flexible staking options, these contracts not only boost user engagement but also promote long-term participation in your ecosystem. By implementing a secure and scalable staking model, you're positioned to offer a competitive, feature-rich staking solution that can adapt as the DeFi landscape continues to evolve. Now, you're equipped to launch a robust staking contract that meets the needs of today's crypto users. If you are looking to create crypto-staking solutions, connect with our skilled crypto/token developers to get started. 

Leave a

Comment

Name is required

Invalid Name

Comment is required

Recaptcha is required.

blog-detail

November 8, 2024 at 08:25 pm

Your comment is awaiting moderation.

By using this site, you allow our use of cookies. For more information on the cookies we use and how to delete or block them, please read our cookie notice.

Chat with Us
Telegram Button
Youtube Button
Contact Us

Oodles | Blockchain Development Company

Name is required

Please enter a valid Name

Please enter a valid Phone Number

Please remove URL from text