-
Notifications
You must be signed in to change notification settings - Fork 10
/
StakingContract.sol
144 lines (109 loc) · 4.34 KB
/
StakingContract.sol
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
contract nftStaking is ReentrancyGuard {
using SafeERC20 for IERC20;
IERC20 public immutable rewardsToken;
IERC721 public immutable nftCollection;
constructor(IERC721 _nftCollection, IERC20 _rewardsToken) {
nftCollection = _nftCollection;
rewardsToken = _rewardsToken;
}
struct StakedToken {
address staker;
uint256 tokenId;
}
// Staker info
struct Staker {
uint256 amountStaked;
StakedToken[] stakedTokens;
uint256 timeOfLastUpdate;
uint256 unclaimedRewards;
}
uint256 private rewardsPerHour = 10000000000;
mapping(address => Staker) public stakers;
mapping(uint256 => address) public stakerAddress;
function stake(uint256 _tokenId) external nonReentrant {
if (stakers[msg.sender].amountStaked > 0) {
uint256 rewards = calculateRewards(msg.sender);
stakers[msg.sender].unclaimedRewards += rewards;
}
require(
nftCollection.ownerOf(_tokenId) == msg.sender,
"You don't own this token!"
);
nftCollection.transferFrom(msg.sender, address(this), _tokenId);
StakedToken memory stakedToken = StakedToken(msg.sender, _tokenId);
stakers[msg.sender].stakedTokens.push(stakedToken);
stakers[msg.sender].amountStaked++;
stakerAddress[_tokenId] = msg.sender;
stakers[msg.sender].timeOfLastUpdate = block.timestamp;
}
function withdraw(uint256 _tokenId) external nonReentrant {
require(
stakers[msg.sender].amountStaked > 0,
"You have no tokens staked"
);
require(stakerAddress[_tokenId] == msg.sender, "You don't own this token!");
uint256 rewards = calculateRewards(msg.sender);
stakers[msg.sender].unclaimedRewards += rewards;
uint256 index = 0;
for (uint256 i = 0; i < stakers[msg.sender].stakedTokens.length; i++) {
if (
stakers[msg.sender].stakedTokens[i].tokenId == _tokenId
&&
stakers[msg.sender].stakedTokens[i].staker != address(0)
) {
index = i;
break;
}
}
stakers[msg.sender].stakedTokens[index].staker = address(0);
stakers[msg.sender].amountStaked--;
stakerAddress[_tokenId] = address(0);
nftCollection.transferFrom(address(this), msg.sender, _tokenId);
stakers[msg.sender].timeOfLastUpdate = block.timestamp;
}
function claimRewards() external {
uint256 rewards = calculateRewards(msg.sender) +
stakers[msg.sender].unclaimedRewards;
require(rewards > 0, "You have no rewards to claim");
stakers[msg.sender].timeOfLastUpdate = block.timestamp;
stakers[msg.sender].unclaimedRewards = 0;
rewardsToken.safeTransfer(msg.sender, rewards);
}
function availableRewards(address _staker) public view returns (uint256) {
uint256 rewards = calculateRewards(_staker) +
stakers[_staker].unclaimedRewards;
return rewards;
}
function getStakedTokens(address _user) public view returns (StakedToken[] memory) {
if (stakers[_user].amountStaked > 0) {
StakedToken[] memory _stakedTokens = new StakedToken[](stakers[_user].amountStaked);
uint256 _index = 0;
for (uint256 j = 0; j < stakers[_user].stakedTokens.length; j++) {
if (stakers[_user].stakedTokens[j].staker != (address(0))) {
_stakedTokens[_index] = stakers[_user].stakedTokens[j];
_index++;
}
}
return _stakedTokens;
}
else {
return new StakedToken[](0);
}
}
function calculateRewards(address _staker)
internal
view
returns (uint256 _rewards)
{
return (((
((block.timestamp - stakers[_staker].timeOfLastUpdate) *
stakers[_staker].amountStaked)
) * rewardsPerHour) / 10000000000);
}
}