DevStation / LaunchKit / Templates / SimpleStaking

SimpleStaking

Single-asset staking. Users deposit an ERC-20 token and earn a second token as rewards per block.

Deploy This Template
SimpleStaking.sol
solidity
1// SPDX-License-Identifier: MIT
2pragma solidity ^0.8.20;
3 
4interface IERC20 {
5 function transfer(address to, uint256 amount) external returns (bool);
6 function transferFrom(address from, address to, uint256 amount) external returns (bool);
7}
8 
9contract SimpleStaking {
10 IERC20 public immutable stakingToken;
11 IERC20 public immutable rewardToken;
12 uint256 public immutable rewardRatePerBlock;
13 address public owner;
14 
15 uint256 public totalStaked;
16 uint256 public rewardPerTokenStored;
17 uint256 public lastUpdateBlock;
18 
19 mapping(address => uint256) public staked;
20 mapping(address => uint256) public userRewardPerTokenPaid;
21 mapping(address => uint256) public rewards;
22 
23 event Staked(address indexed user, uint256 amount);
24 event Withdrawn(address indexed user, uint256 amount);
25 event RewardPaid(address indexed user, uint256 reward);
26 
27 constructor(address stakingToken_, address rewardToken_, uint256 rewardRatePerBlock_) {
28 stakingToken = IERC20(stakingToken_);
29 rewardToken = IERC20(rewardToken_);
30 rewardRatePerBlock = rewardRatePerBlock_;
31 owner = msg.sender;
32 lastUpdateBlock = block.number;
33 }
34 
35 function rewardPerToken() public view returns (uint256) {
36 if (totalStaked == 0) return rewardPerTokenStored;
37 return rewardPerTokenStored + ((block.number - lastUpdateBlock) * rewardRatePerBlock * 1e18) / totalStaked;
38 }
39 
40 function earned(address account) public view returns (uint256) {
41 return (staked[account] * (rewardPerToken() - userRewardPerTokenPaid[account])) / 1e18 + rewards[account];
42 }
43 
44 modifier updateReward(address account) {
45 rewardPerTokenStored = rewardPerToken();
46 lastUpdateBlock = block.number;
47 rewards[account] = earned(account);
48 userRewardPerTokenPaid[account] = rewardPerTokenStored;
49 _;
50 }
51 
52 function stake(uint256 amount) external updateReward(msg.sender) {
53 require(amount > 0, "ZERO");
54 totalStaked += amount;
55 staked[msg.sender] += amount;
56 require(stakingToken.transferFrom(msg.sender, address(this), amount), "TRANSFER_FAILED");
57 emit Staked(msg.sender, amount);
58 }
59 
60 function withdraw(uint256 amount) external updateReward(msg.sender) {
61 require(staked[msg.sender] >= amount, "BALANCE");
62 totalStaked -= amount;
63 staked[msg.sender] -= amount;
64 require(stakingToken.transfer(msg.sender, amount), "TRANSFER_FAILED");
65 emit Withdrawn(msg.sender, amount);
66 }
67 
68 function claimReward() external updateReward(msg.sender) {
69 uint256 reward = rewards[msg.sender];
70 require(reward > 0, "NO_REWARD");
71 rewards[msg.sender] = 0;
72 require(rewardToken.transfer(msg.sender, reward), "TRANSFER_FAILED");
73 emit RewardPaid(msg.sender, reward);
74 }
75}
DevStation
Loading console…