-
Notifications
You must be signed in to change notification settings - Fork 91
/
StakingShare.sol
290 lines (264 loc) · 8.79 KB
/
StakingShare.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
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
import {ERC1155Upgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC1155/ERC1155Upgradeable.sol";
import {IERC20Upgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol";
import {ERC1155Ubiquity} from "./ERC1155Ubiquity.sol";
import {ERC1155URIStorageUpgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC1155/extensions/ERC1155URIStorageUpgradeable.sol";
import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "../../dollar/utils/SafeAddArray.sol";
import "../interfaces/IAccessControl.sol";
import "../libraries/Constants.sol";
/// @notice Contract representing a staking share in the form of ERC1155 token
contract StakingShare is ERC1155Ubiquity, ERC1155URIStorageUpgradeable {
using SafeAddArray for uint256[];
/// @notice Stake struct
struct Stake {
// address of the minter
address minter;
// lp amount deposited by the user
uint256 lpFirstDeposited;
uint256 creationBlock;
// lp that were already there when created
uint256 lpRewardDebt;
uint256 endBlock;
// lp remaining for a user
uint256 lpAmount;
}
/// @notice Mapping of stake id to stake info
mapping(uint256 => Stake) private _stakes;
/// @notice Total LP amount staked
uint256 private _totalLP;
/// @notice Base token URI
string private _baseURI;
// ----------- Modifiers -----------
/// @notice Modifier checks that the method is called by a user with the "Staking share minter" role
modifier onlyMinter() override {
require(
accessControl.hasRole(STAKING_SHARE_MINTER_ROLE, msg.sender),
"Staking Share: not minter"
);
_;
}
/// @notice Modifier checks that the method is called by a user with the "Staking share burner" role
modifier onlyBurner() override {
require(
accessControl.hasRole(STAKING_SHARE_BURNER_ROLE, msg.sender),
"Staking Share: not burner"
);
_;
}
/// @notice Modifier checks that the method is called by a user with the "Pauser" role
modifier onlyPauser() override {
require(
accessControl.hasRole(PAUSER_ROLE, msg.sender),
"Staking Share: not pauser"
);
_;
}
/// @notice Ensures initialize cannot be called on the implementation contract
constructor() {
_disableInitializers();
}
/// @notice Initializes this contract
/// @param _manager Address of the manager of the contract
/// @param _uri Base URI
function initialize(
address _manager,
string memory _uri
) public virtual initializer {
__ERC1155Ubiquity_init(_manager, _uri);
}
/**
* @notice Updates a staking share
* @param _stakeId Staking share id
* @param _lpAmount Amount of Dollar-3CRV LP tokens deposited
* @param _lpRewardDebt Amount of excess LP token inside the staking contract
* @param _endBlock Block number when the locking period ends
*/
function updateStake(
uint256 _stakeId,
uint256 _lpAmount,
uint256 _lpRewardDebt,
uint256 _endBlock
) external onlyMinter whenNotPaused {
Stake storage stake = _stakes[_stakeId];
uint256 curLpAmount = stake.lpAmount;
if (curLpAmount > _lpAmount) {
// we are removing LP
_totalLP -= curLpAmount - _lpAmount;
} else {
// we are adding LP
_totalLP += _lpAmount - curLpAmount;
}
stake.lpAmount = _lpAmount;
stake.lpRewardDebt = _lpRewardDebt;
stake.endBlock = _endBlock;
}
/**
* @notice Mints a single staking share token for the `to` address
* @param to Owner address
* @param lpDeposited Amount of Dollar-3CRV LP tokens deposited
* @param lpRewardDebt Amount of excess LP tokens inside the staking contract
* @param endBlock Block number when the locking period ends
* @return id Minted staking share id
*/
function mint(
address to,
uint256 lpDeposited,
uint256 lpRewardDebt,
uint256 endBlock
) public virtual onlyMinter whenNotPaused returns (uint256 id) {
id = totalSupply + 1;
_mint(to, id, 1, bytes(""));
totalSupply += 1;
holderBalances[to].add(id);
Stake storage _stake = _stakes[id];
_stake.minter = to;
_stake.lpFirstDeposited = lpDeposited;
_stake.lpAmount = lpDeposited;
_stake.lpRewardDebt = lpRewardDebt;
_stake.creationBlock = block.number;
_stake.endBlock = endBlock;
_totalLP += lpDeposited;
}
/**
* @notice Transfers `amount` tokens of token type `id` from `from` to `to`.
*
* Emits a {TransferSingle} event.
*
* Requirements:
*
* - `to` cannot be the zero address.
* - If the caller is not `from`, it must have been approved to spend ``from``'s tokens via {setApprovalForAll}.
* - `from` must have a balance of tokens of type `id` of at least `amount`.
* - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the
* acceptance magic value.
*/
function safeTransferFrom(
address from,
address to,
uint256 id,
uint256 amount,
bytes memory data
) public override(ERC1155Upgradeable, ERC1155Ubiquity) whenNotPaused {
super.safeTransferFrom(from, to, id, amount, data);
}
/**
* @notice Returns total amount of Dollar-3CRV LP tokens deposited
* @return Total amount of LP tokens deposited
*/
function totalLP() public view virtual returns (uint256) {
return _totalLP;
}
/**
* @notice Returns stake info
* @param id Staking share id
* @return Staking share info
*/
function getStake(uint256 id) public view returns (Stake memory) {
return _stakes[id];
}
/// @inheritdoc ERC1155Ubiquity
function safeBatchTransferFrom(
address from,
address to,
uint256[] memory ids,
uint256[] memory amounts,
bytes memory data
)
public
virtual
override(ERC1155Upgradeable, ERC1155Ubiquity)
whenNotPaused
{
super.safeBatchTransferFrom(from, to, ids, amounts, data);
}
/// @inheritdoc ERC1155Ubiquity
function _burnBatch(
address account,
uint256[] memory ids,
uint256[] memory amounts
)
internal
virtual
override(ERC1155Upgradeable, ERC1155Ubiquity)
whenNotPaused
{
super._burnBatch(account, ids, amounts);
}
/// @inheritdoc ERC1155Ubiquity
function _beforeTokenTransfer(
address operator,
address from,
address to,
uint256[] memory ids,
uint256[] memory amounts,
bytes memory data
) internal virtual override(ERC1155Upgradeable, ERC1155Ubiquity) {
super._beforeTokenTransfer(operator, from, to, ids, amounts, data);
}
/**
* @notice Returns URI by token id
* @param tokenId Token id
* @return URI string
*/
function uri(
uint256 tokenId
)
public
view
virtual
override(ERC1155Upgradeable, ERC1155URIStorageUpgradeable)
returns (string memory)
{
return super.uri(tokenId);
}
/// @inheritdoc ERC1155Ubiquity
function _burn(
address account,
uint256 id,
uint256 amount
)
internal
virtual
override(ERC1155Upgradeable, ERC1155Ubiquity)
whenNotPaused
{
require(amount == 1, "amount <> 1");
super._burn(account, id, 1);
Stake storage _stake = _stakes[id];
require(_stake.lpAmount == 0, "LP <> 0");
totalSupply -= 1;
}
/**
* @notice Sets URI for token type `tokenId`
* @param tokenId Token type id
* @param tokenUri Token URI
*/
function setUri(
uint256 tokenId,
string memory tokenUri
) external onlyMinter {
_setURI(tokenId, tokenUri);
}
/**
* @notice Sets base URI for all token types
* @param newUri New URI string
*/
function setBaseUri(string memory newUri) external onlyMinter {
_setBaseURI(newUri);
_baseURI = newUri;
}
/**
* @notice Returns base URI for all token types
* @return Base URI string
*/
function getBaseUri() external view returns (string memory) {
return _baseURI;
}
/// @notice Allows an admin to upgrade to another implementation contract
/// @param newImplementation Address of the new implementation contract
function _authorizeUpgrade(
address newImplementation
) internal override onlyAdmin {}
}