# Creating a Staking Smart Contract with BuildBear

By [BuildBear](https://paragraph.com/@buildbear-3) · 2023-07-25

---

Staking crypto is the process of locking up or depositing crypto holdings into a DeFi protocol or smart contract to earn interest.

Today we will be building a smart contract where we could stake the ether and earn interest on it. It also penalises if a user tries to withdraw the funds before the unlocking date.If a user tries to do that, he has to give up the interest he has earned.

By the end of this article, you will get to learn the following things:

*   Learn how to write a staking smart contract
    
*   Deploy it to a private testnet
    
*   Use the faucet to get the native tokens
    
*   Test our staking smart contract by doing some transactions from the explorer
    
*   Ask your friends to join you in your private testnet and test the contract with you by using the Faucet
    
*   Advance time, using a postman call in our private testnet
    
*   Test the withdrawal of the of the Staked tokens
    

So let us start building! 🧑‍💻

**1\. Create a project and install dependencies**
-------------------------------------------------

1.1: Use the following commands on your CLI to initialize your project.

    mkdir staking-with-buildbear && cd staking-with-buildbear
    

1.2: Run the command `npx hardhat` on your CLI and choose `Create a Javascript Object`; choose `yes` for all the options.

Once you have successfully executed the above and if you were to open this repo in VS Code, you should have a directory structure, similar to the following:

![](https://storage.googleapis.com/papyrus_images/7306fa2af45c5e019d89e0190a47e2b2ea4cdfa51669d5c5c5a1944985e21a05.png)

**2\. Coding the Staking Smart Contract**
-----------------------------------------

2.1: Create a file named `Staking.sol` in the `contracts` folder.

2.2: We will start with defining the Solidity Version (this will determine the Solidity Compiler, which we will also use in our `hardhat.confing.js` file).

    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.0;contract Staking {
    

2.3: Define the variables that we will be using in our contract:

    address public owner;// amt of ethers staked by an address
    struct Position {
        uint positionId;
        address walletAddress;
        uint createdDate;
        uint unlockDate;
        uint percentInterest;
        uint weiStaked;
        uint weiInterest;
        bool open;
    }Position position;
    uint public currentPositionId;
    mapping (uint => Position) public positions;
    mapping (address => uint[]) public positionIdsByAddress;
    mapping (uint => uint) public tiers;
    uint[] public lockPeriods; // 30 days / 90 days / 180 days
    

2.3.1: _Critical Variables_

*   Struct **Position** stores the amount of Ether staked by a specific address at a period of time with additional details. While these details are self-explanatory, you are free to ping us if you need any information regarding any of them.
    
*   Mapping of integers to positions, where each position can be queried by the id of the position
    
*   **lockPeriods** that will store the different lock periods of the stakes.
    

2.4: `constructor` for the smart contract

Use the code shown below for the constructor

    constructor () payable {
            owner = msg.sender;
            currentPositionId = 0;
            tiers[30] = 700; // 30 days -> 7%
            tiers[90] = 1000; // 90 days -> 10%
            tiers[180] = 1200; // 180 days -> 12%        lockPeriods.push(30);
            lockPeriods.push(90);
            lockPeriods.push(180);
        }
    

We have made the constructor payable, that will allow the deployer of the contract to send some ethers to it when it’s being deployed.

In the constructor we will set the `msg.sender` as the owner

*   the currentPositionId will be initialised to 0
    
*   then we have defined the `tiers`
    
*   30-days tier at 7% annual percentage yield
    
*   90-days tier at 10% annual percentage yield
    
*   and 180-days tier at 12% annual percentage yield
    
*   lastly, we will have the `lockPeriod`, which will be 30, 90, and 180 days
    

2.5: Logical functions of our contract:

    // num of days staked for
    function stakeEther(uint numDays) external payable {
        require(tiers[numDays] > 0, "Mapping not found");
        positions[currentPositionId] = Position(
            currentPositionId,
            msg.sender,
            block.timestamp,
            block.timestamp + (numDays * 15180),
            tiers[numDays],
            msg.value,
            calculateInterest(tiers[numDays], numDays, msg.value),
            true
        );    positionIdsByAddress[msg.sender].push(currentPositionId);
        currentPositionId += 1;
    }function calculateInterest(uint basisPoints, uint numDays, uint weiAmount) private pure returns (uint) {
        return basisPoints * weiAmount / 10000;
    }function modifyLockPeriods(uint numDays, uint basisPoints) external {
        require(owner == msg.sender, "Only owner may modify staking periods");
        tiers[numDays] = basisPoints;
        lockPeriods.push(numDays);
    }function getLockPeriods() external view returns(uint[] memory) {
        return lockPeriods;
    }function getInterestRate(uint numDays) external view returns (uint) {
        return tiers[numDays];
    }function getPositionById(uint positionId) external view returns (Position memory) {
        return positions[positionId];
    }function getPositionIdsForAddress(address walletAddress) external view returns (uint[] memory) {
        return positionIdsByAddress[walletAddress];
    }function closePosition(uint positionId) external {
        require(positions[positionId].walletAddress == msg.sender, "Only position creator can modify the position");
        require(positions[positionId].open == true, "Position is closed");
        positions[positionId].open = false;    if(block.timestamp > positions[positionId].unlockDate) {
            uint amount = positions[positionId].weiStaked + positions[positionId].weiInterest;
            payable(msg.sender).call{value: amount}("");
        } else {
            payable(msg.sender).call{value: positions[positionId].weiStaked}("");
        }
    }
    

Let us discuss some the critical functions:

*   **stakeEther** takes an integer which will be the number of days that the Ether (being sent to the smart contract while calling this function) is being staked for.
    
*   **calculateInterest** function will take an integer of the number of basis points and this will provide the interest earned by the user with the help of the number of days the position is staked and the tier. As you will notice, this is a `private` function. A private function is one function that can only be accessible only inside the contract that defines the function and does not let any other external source access it.
    
*   **modifyLockPeriods** will allow the deployer (and deployer only) to change lockPeriods of the contract. If this function was to be called by any other address it will fail as it is a protected function and can only be called by the owner.
    
*   **getPositionById** and **getPositionByAddresses** to fetch the position and the staked user
    
*   **changeUnlockDate** will be used to change the unlock date of the staked position.
    

Your smart contract should look similar to this

![](https://storage.googleapis.com/papyrus_images/88480ba01a9772e9555b90bc40e2c23aba206fe36f26c29bdbb83863cb7c0033.png)

**3\. Deployment to a blockchain, calling smart contract functions and inviting your friends / audience to participate with you in your testing**
-------------------------------------------------------------------------------------------------------------------------------------------------

3.1: Creating a Private Testnet on BuildBear 🐻‍❄️ \*(why BuildBear, you ask? Have a look over here: \*\*[**_Where Localhost Fails_**](https://medium.com/p/492f1038883d) and \*[**Win Web3 Hackathons, using BuildBear Testnet’s analytics**](https://medium.com/p/e67656093f9d)**_)_**

3.1.1: Visit the [_BuildBear App_](https://home.buildbear.io/). Once you login with your Github account, you will see a page similar to the image added below

![](https://storage.googleapis.com/papyrus_images/535454352e4ef5b77bed44b2237272bc62ee37d14ddedd22eec69a7f4e8603ca.png)

Here we have to create a simple node for our staking application so we will click on the **create an endpoint a**nd we will be redirected to the node configuration page. But for this article, we will be using the default configuration for creating our node.

So we will simply click on the `create` button as shown below.

![](https://storage.googleapis.com/papyrus_images/3490a53725d6a345bae694f79b4906997b3cee09bf3c3587fd09b4137022af8a.png)

Congratulations! You have created your private testnet node!

Your page should be updated to something similar to the following:

![](https://storage.googleapis.com/papyrus_images/a1109263307326b563786fb95fcdfb00b3457c47e7f9791c79fe1cfd2c19f090.png)

Click on the RPC URL (either copy of click to view) to get the RPC to your private testnet.

3.2: In order to perform transactions, we need funds from faucet. Don’t worry! We don’t have to find random faucets to get test ether.

Click on the **Open Faucet** option and add your choose your account.

![](https://storage.googleapis.com/papyrus_images/379d7c440119f12c0c10efc76ce1a7104d3fcac0a343fd3e565346e5dfa15426.png)

After that, click on the **Add to Metamask** option in the right corner of the faucet page.

Your metamask would be provided with 1,000 BB ETH immediately.

![](https://storage.googleapis.com/papyrus_images/41892bb9bee4699ae6dcd1faf76e8f6342d18e503e7a8a1820b55f3aa97d9e2b.png)

3.3: Update the`hardhat.config.js` to the following:

![](https://storage.googleapis.com/papyrus_images/9f6029c02d486a4c17375df6689042977fb545f89496c77accbbddd0a2f82d54.png)

In the networks, we have created a network called **buildbear** and added our RPC URL there and then added the private key of the metamask account (please do not expose your private key to anyone, ever).

Now our config is ready! Let’s write our smart contract.

**4\. Deploy our Staking Contract using BuildBear**
---------------------------------------------------

4.1: Use the following script to deploy your staking contract:

    const { ethers } = require("hardhat");
    const hre = require("hardhat");async function main() {
      const Staking = await hre.ethers.getContractFactory("Staking");
      const staking = await Staking.deploy({value: ethers.utils.parseEther('10')});  await staking.deployed();  console.log(
        "Staking contract deployed to:", staking.address
      );
    }main().catch((error) => {
      console.error(error);
      process.exitCode = 1;
    });
    

Open your CLI and run the following command:

    npx hardhat run scripts/deploy.js --network buildbear
    

Now if you go back to the BuildBear Explorer (link available from the [_BuildBear App_](https://home.buildbear.io/), once you have created your private testnet). You will see a transaction executed.

Open the transaction hash, and you will get all the details of your transaction.

![](https://storage.googleapis.com/papyrus_images/e6f9e132684f665bfe31c2af25825cf7c9d87e000b15ebfd6c2ea1d4f7c42e3a.png)

If you’ll click on the **advance** button, you will see a trace that says unknownContract created.

This represents our Staking contract deployed.

But `unknownContract`sounds quite confusing! 😥. So let’s get to resolving this too.

4.2: Go back to your [_Dashboard,_](https://home.buildbear.io/) and click on the **advanced** button.

![](https://storage.googleapis.com/papyrus_images/5a02cfe2e92c2a495a76f836ca0d66d41411e675e30e0467033afae51ce3291c.png)

And submit your `artifacts` folder over here.

Now, again open your transaction and check the **advanced** option.

You will see that this time instead of getting `unknownContract`, we received the Staking contract

![](https://storage.googleapis.com/papyrus_images/f9faaab31b06abec963949c163e2c2af63865b6c2356d3f41378abcc29af1d2a.png)

Isn’t that great? 😎

**5\. Executing transactions on our contract**
----------------------------------------------

We will be using BuildBear Explorer to test out our contract.

5.1: In order to execute the transactions, visit your contract address on the explorer. Take the contract address and put it on the search option.

Your contract would look like

![](https://storage.googleapis.com/papyrus_images/86955b40bf007846b4f1986659c16c1cba27835bd7a4b01aaae4aa6b8e869d0a.png)

Click on the **Contract** option adjacent to **Transactions**

Here we have 3 options:

*   Code to submit your contract’s ABI
    
*   Read Contract
    
*   Write Contract
    

Since we have already submitted the ABI using the dashboard, we’ll be redirected to the Read Contract option.

Your **Read Contract** option will have all the functions that you have written in your contract.

![](https://storage.googleapis.com/papyrus_images/47ae10b944e8d41f02d910873656d1e9061e4a841334ce9c49996f644e95f328.png)

5.2: Let us stake our ethers on **write contract.**

Make sure you connect to your Web3 account.

Under the **write contract** option, you will see the `stakeEthers` function.

We need to pass in the values for **numDays** and **amount.** Thus, here we will pass 30 as the value for numDays and 2 ether for the amount.

![](https://storage.googleapis.com/papyrus_images/0a977a2fc306f71282eb565d3b284fd00dd926cc1b322d6e9265679b93fc2a0f.png)

Then click on the **write button** to call the **stakeEthers function.** This will trigger Metamask with 2 BB ETH has the value and the function call to the smart contract.

Once done, you will receive a _transaction hash_ below the query button, telling whether your transaction was successful or not.

![](https://storage.googleapis.com/papyrus_images/723354a3372b6da32eca12ad50a3f0b483b4e8112726a5a574cf8cab38564774.png)

Click on the transaction hash and you’ll receive all the information related to it.

![](https://storage.googleapis.com/papyrus_images/c6aacdec806730a6c0013d4a373577e1fa9aa09f8bbf53acc3438be7f0792561.png)

We interacted with the contract _0x73eccD6288e117cAcA738BDAD4FEC51312166C1A_ and added 2 ETH in the contract.

Trace of the transaction will show your activity

![](https://storage.googleapis.com/papyrus_images/ba0e1249d8703fc2db8a9a01bfc4cbc685cd967f8a84c4e5cae9c8b9b99e1307.png)

In the read contract, function **currentPositionId** is equal to 1 because we just now staked our ethers.

![](https://storage.googleapis.com/papyrus_images/1e78ebdd8846d6391c16a48fef6fe7bebf9d84eb4688991987f4a646ee4ce994.png)

This way we can run all the functions of our contract and run test cases for the contract.

5.3: Getting your interest out

Staking interest will only be earned once the `lockPeriod` is achieved.

We can test it out with the help of Postman request.

What we will do is, we will take the RPC URL of the node and do a post request on Postman.

Pass the body with the following json body

    {
        "jsonrpc": "2.0",
         "id": 1,
        "method": "evm_increaseTime",
        "params": ["0x28DE80"] 
    }
    

This will increase the block timestamp of the node by 31 Days. Therefore, we will be able to close our stake and earn interest on them.

Thus, now if you’ll run a POST request, you will receive a result similar to this

![](https://storage.googleapis.com/papyrus_images/12ca9310ac18c8df209d58acc63b32a6a60708420c95a7121cc20530e8029624.png)

This means that the node’s timestamp has been increased by 31 days successfully.

Now to test our stake collection, we will go back to the explorer and open our contract and check the **closePosition** .

Now enter the _positionId_ below in the params and click on **write button.**

![](https://storage.googleapis.com/papyrus_images/657cd5bdb0a04627aa1a9f651f0419667ac503575f6945521c3d02966c88cc8f.png)

You can see our transaction has been successfully executed.

To understand what we did just now, let us open the transaction hash we received below the **write** button and then open the **advanced** tab.

![](https://storage.googleapis.com/papyrus_images/47def378b8d4466918721adf4b3b107efcb58719f21e380591274a60f783a23c.png)

You will receive a trace similar to this.

Let us understand what just happened here:

*   When we called the **stakeEther,** we provided that the staked ether should be unlocked after 30 days
    
*   But it is not the most feasible way to unlock the ethers, that’s why we used Postman here
    
*   We did a post request using the RPC URL and performed the **evm\_increaseTime** method on it and in the params we provided 31 Days in hex so that we do not have to wait for so long
    
*   Once we have done the post request, the block timestamp of our node will be increased by exactly 31 Days. Therefore now it is possible to unlock our stake.
    
*   If we look at the trace shown above,
    
*   When we called the **closePosition** function, it interacted with our contract
    
*   The contract then sent the funds to the user account (in the example above the address `0xd51..`is my wallet address to which the funds were sent to. The `UnknownContractAndFunction` over here is the manner in which the sending of funds is shown.
    

And now we just learned how staking crypto is done! 😎

This is how BuildBear allows you to deploy your contract and can interact with it.

1.  **BONUS**
    

To move one step ahead, you can ask your friends to stake their ethers on your staking contract and perform multiple functionalities. I have created my private testnet and the staking contract is available over [_here_](https://explorer.buildbear.io/node/thirsty-einstein-e6514b/address/0xF5053926f73Fd9401817329874414944CeB2F87E/writeContract).

You can get the Faucet for the testnet over [_here_](https://faucet.buildbear.io/thirsty-einstein-e6514b).

![](https://storage.googleapis.com/papyrus_images/6937cc1dbbb50ffb5803600a221297fd4d51278c070a8374f4a8f7d27aa00381.png)

PRO TIP: While creating my private testnet, I have disabled the option to advance time and hence, you will NOT be able to advance the block time of my private testnet. Tadaa!!!!

To learn more about BuildBear, read here [_docs_](https://docs.buildbear.io/docs/intro)

Get the above Github code from [_here_](https://github.com/BuildBearLabs/Tutorials/tree/main/Staking)

If you appreciate what we are doing, please follow us on [_Twitter_](https://twitter.com/_BuildBear) and [_Join the Telegram_](https://t.me/+6mjOnBFUR9xjN2U1) group if you haven’t done yet.

Author:

Pari Tomar ([_Twitter_](https://twitter.com/tomarpari90) || [_LinkedIn_](https://www.linkedin.com/in/pari-tomar-8b1370187/)), always open to feedback and learning.

BTW, if you would know anyone who would be keen to working with BuildBear. Please have a look over [_here_](https://bit.ly/buildbear-careers)!!!

---

*Originally published on [BuildBear](https://paragraph.com/@buildbear-3/creating-a-staking-smart-contract-with-buildbear)*
