Physical Address
304 North Cardinal St.
Dorchester Center, MA 02124
Physical Address
304 North Cardinal St.
Dorchester Center, MA 02124
[
]
Welcome, blockchain enthusiasts and crypto innovators! Today, we’re diving into a powerful smart contract that brings token staking and rewards to the BitTorrent Chain (BTTC) — the StakingRewards contract. This contract allows users to stake tokens and earn rewards over time, creating a decentralized and automated way to incentivize participation. Let’s break down the key components and functionalities of this contract.
Imagine a smart contract that allows you to stake your tokens and earn rewards over a specified period. That’s exactly what our StakingRewards contract does. It’s perfect for creating incentive mechanisms and encouraging long-term participation in your token ecosystem.
IERC20 public immutable stakingToken;
IERC20 public immutable rewardsToken;address public owner;
// Duration of rewards to be paid out (in seconds)
uint public duration;
// Timestamp of when the rewards finish
uint public finishAt;
// Minimum of last updated time and reward finish time
uint public updatedAt;
// Reward to be paid out per second
uint public rewardRate;
// Sum of (reward rate * dt * 1e18 / total supply)
uint public rewardPerTokenStored;
// User address => rewardPerTokenStored
mapping(address => uint) public userRewardPerTokenPaid;
// User address => rewards to be claimed
mapping(address => uint) public rewards;
// Total staked
uint public totalSupply;
// User address => staked amount
mapping(address => uint) public balanceOf;
These variables form the backbone of our contract, defining the staking and reward mechanisms.
While the contract doesn’t explicitly define events in the provided code, it’s a good practice to include them for transparency and off-chain tracking. Here are some suggested events:
event Staked(address indexed user, uint amount);
event Withdrawn(address indexed user, uint amount);
event RewardPaid(address indexed user, uint reward);
event RewardDurationUpdated(uint newDuration);
event RewardNotified(uint amount);
constructor(address _stakingToken, address _rewardToken) {
owner = msg.sender;
stakingToken = IERC20(_stakingToken);
rewardsToken = IERC20(_rewardToken);
}
The constructor initializes the contract with the addresses of the staking and reward tokens, and sets the contract deployer as the owner.
modifier onlyOwner() {
require(msg.sender == owner, "not authorized");
_;
}modifier updateReward(address _account) {
rewardPerTokenStored = rewardPerToken();
updatedAt = lastTimeRewardApplicable();
if (_account != address(0)) {
rewards[_account] = earned(_account);
userRewardPerTokenPaid[_account] = rewardPerTokenStored;
}
_;
}
These modifiers ensure that only the owner can perform certain actions and that rewards are updated before any state changes.
function lastTimeRewardApplicable() public view returns (uint) {
return _min(finishAt, block.timestamp);
}function rewardPerToken() public view returns (uint) {
if (totalSupply == 0) {
return rewardPerTokenStored;
}
return
rewardPerTokenStored +
(rewardRate * (lastTimeRewardApplicable() - updatedAt) * 1e18) /
totalSupply;
}
These functions calculate the last time rewards were applicable and the reward rate per token.
function stake(uint _amount) external updateReward(msg.sender) {
require(_amount > 0, "amount = 0");
stakingToken.transferFrom(msg.sender, address(this), _amount);
balanceOf[msg.sender] += _amount;
totalSupply += _amount;
emit Staked(msg.sender, _amount);
}function withdraw(uint _amount) external updateReward(msg.sender) {
require(_amount > 0, "amount = 0");
balanceOf[msg.sender] -= _amount;
totalSupply -= _amount;
stakingToken.transfer(msg.sender, _amount);
emit Withdrawn(msg.sender, _amount);
}
These functions allow users to stake and withdraw tokens, updating their rewards accordingly.
function earned(address _account) public view returns (uint) {
return
((balanceOf[_account] *
(rewardPerToken() - userRewardPerTokenPaid[_account])) / 1e18) +
rewards[_account];
}function getReward() external updateReward(msg.sender) {
uint reward = rewards[msg.sender];
if (reward > 0) {
rewards[msg.sender] = 0;
rewardsToken.transfer(msg.sender, reward);
emit RewardPaid(msg.sender, reward);
}
}
These functions calculate the rewards earned by a user and allow them to claim their rewards.
function setRewardsDuration(uint _duration) external onlyOwner {
require(finishAt < block.timestamp, "reward duration not finished");
duration = _duration;
emit RewardDurationUpdated(_duration);
}function notifyRewardAmount(uint _amount) external onlyOwner updateReward(address(0)) {
if (block.timestamp >= finishAt) {
rewardRate = _amount / duration;
} else {
uint remainingRewards = (finishAt - block.timestamp) * rewardRate;
rewardRate = (_amount + remainingRewards) / duration;
}
require(rewardRate > 0, "reward rate = 0");
require(
rewardRate * duration <= rewardsToken.balanceOf(address(this)),
"reward amount > balance"
);
finishAt = block.timestamp + duration;
updatedAt = block.timestamp;
emit RewardNotified(_amount);
}
These functions allow the owner to set the reward duration and notify the contract of the reward amount.
function _min(uint x, uint y) private pure returns (uint) {
return x <= y ? x : y;
}
This utility function returns the minimum of two values.
This StakingRewards contract offers several advantages:
The StakingRewards smart contract is more than just code — it’s a gateway to passive income and decentralized finance. By leveraging blockchain technology, we’re creating a more transparent, efficient, and accessible staking ecosystem.
As you explore the potential of this contract on BTTC, remember: you’re not just staking tokens, you’re pioneering a new era of decentralized finance and community participation.
So, what will you stake first? The blockchain is waiting, and the rewards are ready to be claimed!
Happy staking, and may your rewards be plentiful!
Github URL:
https://github.com/adeelch9/bttc-examples/tree/master/projects/staking-contract
[