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_, address initialOwner_) {
28 require(initialOwner_ != address(0), "ZERO_OWNER");
29 stakingToken = IERC20(stakingToken_);
30 rewardToken = IERC20(rewardToken_);
31 rewardRatePerBlock = rewardRatePerBlock_;
32 owner = initialOwner_;
33 lastUpdateBlock = block.number;
34 }
35 
36 function rewardPerToken() public view returns (uint256) {
37 if (totalStaked == 0) return rewardPerTokenStored;
38 return rewardPerTokenStored + ((block.number - lastUpdateBlock) * rewardRatePerBlock * 1e18) / totalStaked;
39 }
40 
41 function earned(address account) public view returns (uint256) {
42 return (staked[account] * (rewardPerToken() - userRewardPerTokenPaid[account])) / 1e18 + rewards[account];
43 }
44 
45 modifier updateReward(address account) {
46 rewardPerTokenStored = rewardPerToken();
47 lastUpdateBlock = block.number;
48 rewards[account] = earned(account);
49 userRewardPerTokenPaid[account] = rewardPerTokenStored;
50 _;
51 }
52 
53 function stake(uint256 amount) external updateReward(msg.sender) {
54 require(amount > 0, "ZERO");
55 totalStaked += amount;
56 staked[msg.sender] += amount;
57 require(stakingToken.transferFrom(msg.sender, address(this), amount), "TRANSFER_FAILED");
58 emit Staked(msg.sender, amount);
59 }
60 
61 function withdraw(uint256 amount) external updateReward(msg.sender) {
62 require(staked[msg.sender] >= amount, "BALANCE");
63 totalStaked -= amount;
64 staked[msg.sender] -= amount;
65 require(stakingToken.transfer(msg.sender, amount), "TRANSFER_FAILED");
66 emit Withdrawn(msg.sender, amount);
67 }
68 
69 function claimReward() external updateReward(msg.sender) {
70 uint256 reward = rewards[msg.sender];
71 require(reward > 0, "NO_REWARD");
72 rewards[msg.sender] = 0;
73 require(rewardToken.transfer(msg.sender, reward), "TRANSFER_FAILED");
74 emit RewardPaid(msg.sender, reward);
75 }
76}
DevStation
Loading console…