Why Do Developers Love Staking?
Staking has become a favorite among blockchain developers, with 60% of them embracing it. Why? Because staking is like earning interest while supporting the network. It’s a win-win: you contribute to the blockchain’s security and earn rewards in return. Sounds like a no-brainer, right?
GitHub: https://github.com/Vikash-8090-Yadav/CFStaking
LiveLink:https://cf-staking-ten.vercel.app/
What is Staking?
Think of staking as depositing money in a savings account. But instead of earning a measly interest rate, you’re earning juicy rewards. Staking involves locking up your cryptocurrency tokens (like Conflux tokens) to support the network. In return, you get rewarded. Simple as that.
Why Stake?
There are two main reasons to stake:
-
Security: By staking, you’re helping to secure the Conflux network and validate transactions. It’s like being a vital part of a team—you’re making a difference.
-
Rewards: You earn passive income. Who doesn’t want that?
But, of course, there are risks too. Tokens get locked up, meaning you can’t access them for a while. And there’s market volatility—prices go up and down. So, stake wisely.
Building a Staking dApp on Conflux
Now, let’s get to the fun part. We’re building a staking dApp on Conflux. By the end of this guide, you’ll have a fully functional dApp where users can stake, earn rewards, and withdraw funds.
Step 1: Connect Your Wallet
First, let’s connect our wallet. Once the wallet is connected, you’ll see the account address and network details displayed on the screen.
Step 2: Approve Tokens for Staking
Next, let’s approve the token amount for staking. For this demo, I’ll approve 1 token for this account. Once approved, I’ll confirm the transaction. The transaction is now successful!
Step 3: Stake Tokens
Now, let’s proceed with staking 1 token. We’re currently waiting for the transaction to be processed. The transaction is complete! 1 token has been staked.
Step 4: Earn Rewards
The rewards section now shows an updated amount, as I previously tested this process. The displayed value includes the previous rewards plus the newly generated ones. When you claim rewards, only the newly accrued amount will be claimed—not the total displayed balance. The reward balance increases per second, depending on the staking contract’s mechanism.
Step 5: Withdraw Staked Tokens
Now, let’s withdraw the full amount of staked tokens. Remember, you can only claim rewards if you withdraw the full amount. The transaction is currently pending. The withdrawal is successful! The stake amount is now zero, but the rewards are still visible.
Step 6: Claim Rewards
Let’s claim the reward now. The claimed reward will be added to my MetaMask wallet. You can verify the updated balance in your wallet.
The Code Behind the dApp
Now, let’s dive into the coding aspect. Here’s a breakdown of the key components:
1. Reward Token Contract
// contracts/GLDToken.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
contract RewardToken is ERC20 {
constructor(uint256 initialSupply) ERC20("RewardToken", "RWT") {
_mint(msg.sender, initialSupply*10**18);
}
}
The RewardToken contract handles the distribution of rewards. This Solidity smart contract is an ERC-20 token implemented using OpenZeppelin’s ERC-20 standard. It inherits from the ERC-20 contract and initializes the token with the name "RewardToken"
and symbol "RWT"
. During deployment, the constructor mints the specified initialSupply
, scaled appropriately. This ensures that the deployer starts with the full supply, which can then be distributed as rewards for staking or other incentive mechanisms.
2. Stake Token Contract
// contracts/GLDToken.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
contract StakeToken is ERC20 {
constructor(uint256 initialSupply) ERC20("StakeToken", "STK") {
_mint(msg.sender, initialSupply*10**18);
}
}
The StakeToken contract represents the token being staked. This Solidity smart contract is an ERC-20 token built using OpenZeppelin’s ERC-20 implementation. It inherits from the ERC-20 contract and defines a constructor that initializes the token with a name ("StakeToken"
) and symbol ("STK"
). When deployed, it mints the specified initialSupply
to the deployer’s address. This ensures that the deployer starts with the full supply of tokens, which can then be used for staking or other transactions.
3. Staking Contract
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.8.2 <0.9.0;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import "@openzeppelin/contracts/utils/math/SafeMath.sol";
contract Staking is ReentrancyGuard{
using SafeMath for uint256;
IERC20 public s_stakingToken;
IERC20 public s_rewardToken;
uint public constant REWARD_RATE=1e18;
uint private totalStakedTokens;
uint public rewardPerTokenStored;
uint public lastUpdateTime;
mapping(address=>uint) public stakedBalance;
mapping(address=>uint) public rewards;
mapping(address=>uint) public userRewardPerTokenPaid;
event Staked(address indexed user, uint256 indexed amount);
event Withdrawn(address indexed user, uint256 indexed amount);
event RewardsClaimed(address indexed user, uint256 indexed amount);
constructor(address stakingToken,address rewardToken){
s_stakingToken=IERC20(stakingToken);
s_rewardToken=IERC20(rewardToken);
}
function rewardPerToken() public view returns(uint){
if(totalStakedTokens==0){
return rewardPerTokenStored;
}
uint totalTime = block.timestamp.sub(lastUpdateTime);
uint totalRewards = REWARD_RATE.mul(totalTime);
return rewardPerTokenStored.add(totalRewards.mul(1e18).div(totalStakedTokens));
}
function earned(address account) public view returns(uint){
return stakedBalance[account].mul(rewardPerToken().sub(userRewardPerTokenPaid[account])).div(1e18).add(rewards[account]);
}
modifier updateReward(address account){
rewardPerTokenStored=rewardPerToken();
lastUpdateTime=block.timestamp;
rewards[account]=earned(account);
userRewardPerTokenPaid[account]=rewardPerTokenStored;
_;
}
function stake(uint amount) external nonReentrant updateReward(msg.sender){
require(amount>0,"Amount must be greater than zero");
totalStakedTokens=totalStakedTokens.add(amount);
stakedBalance[msg.sender]=stakedBalance[msg.sender].add(amount);
emit Staked(msg.sender,amount);
bool success = s_stakingToken.transferFrom(msg.sender,address(this),amount);
require(success,"Transfer Failed");
}
function withdrawStakedTokens(uint amount) external nonReentrant updateReward(msg.sender) {
require(amount>0,"Amount must be greater than zero");
require(stakedBalance[msg.sender]>=amount,"Staked amount not enough");
totalStakedTokens=totalStakedTokens.sub(amount);
stakedBalance[msg.sender]=stakedBalance[msg.sender].sub(amount);
emit Withdrawn(msg.sender, amount);(msg.sender,amount);
bool success = s_stakingToken.transfer(msg.sender,amount);
require(success,"Transfer Failed");
}
function getReward() external nonReentrant updateReward(msg.sender){
uint reward = rewards[msg.sender];
require(reward>0,"No rewards to claim");
rewards[msg.sender]=0;
emit RewardsClaimed(msg.sender, reward);
bool success = s_rewardToken.transfer(msg.sender,reward);
require(success,"Transfer Failed");
}
}
The Staking contract manages the staking logic and reward calculations. This smart contract allows users to stake ERC-20 tokens to earn rewards over time. It uses ReentrancyGuard
for security and SafeMath
for safe arithmetic operations. The contract tracks staked balances, calculates rewards based on a fixed REWARD_RATE
, and updates reward distributions dynamically. Users can stake tokens, withdraw staked tokens, and claim rewards. The rewardPerToken()
function computes rewards per staked token, while earned()
calculates a user’s total claimable rewards. Transactions are handled securely using OpenZeppelin’s IERC20
functions.
4. Frontend Interface
In the client
folder, we have the front-end interface, which we saw during the demo. This is where users interact with the dApp, connect their wallets, and perform staking actions.
Conclusion
That’s it for this tutorial! You’ve learned why staking is so popular, how it works, and how to build a staking dApp on Conflux. By following these steps, you can create a fully functional dApp that allows users to stake tokens, earn rewards, and withdraw funds.
If you found this helpful, don’t forget to like, share, and subscribe. Thank you for reading, and happy coding!