-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathsETH.sol
197 lines (153 loc) · 6.85 KB
/
sETH.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
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
//SPDX-License-Identifier: MIT
/*
_________ .__ _________ __ __
/ _____/ ____ | | / _____// |______ | | __ ____
\_____ \ / _ \| | \_____ \\ __\__ \ | |/ // __ \
/ ( <_> ) |__/ \| | / __ \| <\ ___/
/_______ /\____/|____/_______ /|__| (____ /__|_ \\___ >
\/ \/ \/ \/ \/ ETHv1
SolStake is a simple scaffolding library for providing User Staking Data when Staking ERC20 or ETH to a Smart Contract
Yielding & Farming must be implemented seperatly
Repo & Implementation Example can be found here: https://github.com/Kwame0/SolStake
*/
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
contract SolStakeEth is ReentrancyGuard, Ownable {
uint256 public UNSTAKEABLE_FEE = 9200; // How much can they Unstake? 92% AKA 8% Staking FEE
uint256 public MINIMUM_CONTRIBUTION_AMOUNT = 0.05 ether; // Minimum Amount to Stake
bool public CONTRACT_RENOUNCED = false; // for ownerOnly Functions
string private constant NEVER_CONTRIBUTED_ERROR = "This address has never contributed BNB to the protocol";
string private constant NO_ETH_CONTRIBUTIONS_ERROR = "No BNB Contributions";
string private constant MINIMUM_CONTRIBUTION_ERROR = "Contributions must be over the minimum contribution amount";
struct Staker {
address addr; // The Address of the Staker
uint256 lifetime_contribution; // The Total Lifetime Contribution of the Staker
uint256 contribution; // The Current Contribution of the Staker
uint256 yield; // The Current Yield / Reward amount of the Staker
uint256 unstakeable; // How much can the staker withdraw.
uint256 joined; // When did the Staker start staking
bool exists;
}
mapping(address => Staker) public stakers;
address[] public stakerList;
constructor() ReentrancyGuard() {
}
receive() external payable {}
fallback() external payable {}
function SweepETH() external onlyOwner {
uint256 a = address(this).balance;
// Do whatever with bnb sent to the contract (used to send initial bnb to get farms/yields going)
}
function AddStakerYield(address addr, uint256 a) private {
stakers[addr].yield = stakers[addr].yield + a;
}
function RemoveStakerYield(address addr, uint256 a) private {
stakers[addr].yield = stakers[addr].yield - a;
}
function RenounceContract() external onlyOwner {
CONTRACT_RENOUNCED = true;
}
function ChangeMinimumStakingAmount(uint256 a) external onlyOwner {
MINIMUM_CONTRIBUTION_AMOUNT = a;
}
function ChangeUnstakeableFee(uint256 a) external onlyOwner {
UNSTAKEABLE_FEE = a;
}
function UnstakeAll() external onlyOwner {
if(CONTRACT_RENOUNCED == true){revert("Unable to perform this action");}
for (uint i = 0; i < stakerList.length; i++) {
address user = stakerList[i];
ForceRemoveStake(user);
}
}
function Stake() external nonReentrant payable {
require(msg.value >= MINIMUM_CONTRIBUTION_AMOUNT, MINIMUM_CONTRIBUTION_ERROR);
uint256 bnb = msg.value;
uint256 unstakeable = (bnb * UNSTAKEABLE_FEE) / 10000;
if(StakerExists(msg.sender)){
stakers[msg.sender].lifetime_contribution = stakers[msg.sender].lifetime_contribution + bnb;
stakers[msg.sender].contribution = stakers[msg.sender].contribution + unstakeable;
stakers[msg.sender].unstakeable = stakers[msg.sender].unstakeable + unstakeable;
}else{
// Create new user
Staker memory user;
user.addr = msg.sender;
user.contribution = unstakeable;
user.lifetime_contribution = bnb;
user.yield = 0;
user.exists = true;
user.unstakeable = unstakeable;
user.joined = block.timestamp;
// Add user to Stakers
stakers[msg.sender] = user;
stakerList.push(msg.sender);
}
// Staking has completed (or failed and won't reach this point)
uint256 c = (10000 - UNSTAKEABLE_FEE);
uint256 fee = (bnb * c) / 10000;
// Staking fee is stored as fee, use as you wish
}
function RemoveStake() external {
address user = msg.sender;
if(!StakerExists(user)){ revert(NEVER_CONTRIBUTED_ERROR); }
uint256 uns = stakers[user].unstakeable;
if(uns == 0){ revert("This user has nothing to withdraw from the protocol"); }
// Proceed to Unstake user funds from 3rd Party Yielding Farms etc
// Remove Stake
stakers[user].unstakeable = 0;
stakers[user].contribution = 0;
payable(user).transfer(uns);
}
function ForceRemoveStake(address user) private {
// withdraw avAVAX for WAVAX and Unwrap WAVAX for AVAX
if(!StakerExists(user)){ revert(NEVER_CONTRIBUTED_ERROR); }
uint256 uns = stakers[user].unstakeable;
if(uns == 0){ revert("This user has nothing to withdraw from the protocol"); }
// Proceed to Unstake user funds from 3rd Party Yielding Farms etc
// Remove Stake
stakers[user].unstakeable = 0;
stakers[user].contribution = 0;
payable(user).transfer(uns);
}
/*
CONTRIBUTER GETTERS
*/
function StakerExists(address a) public view returns(bool){
return stakers[a].exists;
}
function StakerCount() public view returns(uint256){
return stakerList.length;
}
function GetStakeJoinDate(address a) public view returns(uint256){
if(!StakerExists(a)){revert(NEVER_CONTRIBUTED_ERROR);}
return stakers[a].joined;
}
function GetStakerYield(address a) public view returns(uint256){
if(!StakerExists(a)){revert(NEVER_CONTRIBUTED_ERROR);}
return stakers[a].yield;
}
function GetStakingAmount(address a) public view returns (uint256){
if(!StakerExists(a)){revert(NEVER_CONTRIBUTED_ERROR);}
return stakers[a].contribution;
}
function GetStakerPercentageByAddress(address a) public view returns(uint256){
if(!StakerExists(a)){revert(NEVER_CONTRIBUTED_ERROR);}
uint256 c_total = 0;
for (uint i = 0; i < stakerList.length; i++) {
c_total = c_total + stakers[stakerList[i]].contribution;
}
if(c_total == 0){revert(NO_ETH_CONTRIBUTIONS_ERROR);}
return (stakers[a].contribution * 10000) / c_total;
}
function GetStakerUnstakeableAmount(address addr) public view returns(uint256) {
if(StakerExists(addr)){ return stakers[addr].unstakeable; }else{ return 0; }
}
function GetLifetimeContributionAmount(address a) public view returns (uint256){
if(!StakerExists(a)){revert("This address has never contributed DAI to the protocol");}
return stakers[a].lifetime_contribution;
}
function CheckContractRenounced() external view returns(bool){
return CONTRACT_RENOUNCED;
}
}