-
Notifications
You must be signed in to change notification settings - Fork 6
/
DepositManager.sol
356 lines (270 loc) · 12.7 KB
/
DepositManager.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
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import { IWTON } from "./interfaces/IWTON.sol";
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "../../proxy/ProxyStorage.sol";
import { AccessibleCommon } from "./common/AccessibleCommon.sol";
import { DepositManagerStorage } from "./DepositManagerStorage.sol";
interface IOnApprove {
function onApprove(address owner, address spender, uint256 amount, bytes calldata data) external returns (bool);
}
interface ILayer2Registry {
function layer2s(address layer2) external view returns (bool);
}
interface ILayer2 {
function operator() external view returns (address);
}
interface ISeigManager {
function stakeOf(address layer2, address account) external view returns (uint256);
function onDeposit(address layer2, address account, uint256 amount) external returns (bool);
function onWithdraw(address layer2, address account, uint256 amount) external returns (bool);
}
/**
* @dev DepositManager manages WTON deposit and withdrawal from operator and WTON holders.
*/
//ERC165
contract DepositManager is ProxyStorage, AccessibleCommon, DepositManagerStorage {
using SafeERC20 for IERC20;
////////////////////
// Modifiers
////////////////////
modifier onlyLayer2(address layer2) {
require(ILayer2Registry(_registry).layer2s(layer2));
_;
}
modifier onlySeigManager() {
require(msg.sender == _seigManager);
_;
}
////////////////////
// Events
////////////////////
event Deposited(address indexed layer2, address depositor, uint256 amount);
event WithdrawalRequested(address indexed layer2, address depositor, uint256 amount);
event WithdrawalProcessed(address indexed layer2, address depositor, uint256 amount);
function initialize (
address wton_,
address registry_,
address seigManager_,
uint256 globalWithdrawalDelay_
) external {
require(_wton == address(0), "already initialized");
_wton = wton_;
_registry = registry_;
_seigManager = seigManager_;
globalWithdrawalDelay = globalWithdrawalDelay_;
}
////////////////////
// SeiManager function
////////////////////
function setSeigManager(address seigManager_) external {
_seigManager = seigManager_;
}
////////////////////
// ERC20 Approve callback
////////////////////
function onApprove(
address owner,
address /*spender*/,
uint256 amount,
bytes calldata data
) external returns (bool) {
require(msg.sender == _wton, "DepositManager: only accept WTON approve callback");
address layer2 = _decodeDepositManagerOnApproveData(data);
require(_deposit(layer2, owner, amount, owner));
return true;
}
function _decodeDepositManagerOnApproveData(
bytes memory data
) internal pure returns (address layer2) {
require(data.length == 0x20);
assembly {
layer2 := mload(add(data, 0x20))
}
}
////////////////////
// Deposit function
////////////////////
/**
* @dev deposit `amount` WTON in RAY
*/
function deposit(address layer2, uint256 amount) external returns (bool) {
require(_deposit(layer2, msg.sender, amount, msg.sender));
return true;
}
function deposit(address layer2, address account, uint256 amount) external returns (bool) {
require(_deposit(layer2, account, amount, msg.sender));
return true;
}
function deposit(address layer2, address[] memory accounts, uint256[] memory amounts) external returns (bool) {
require(accounts.length != 0, 'no account');
require(accounts.length == amounts.length, 'wrong lenth');
for (uint256 i = 0; i < accounts.length; i++){
require(_deposit(layer2, accounts[i], amounts[i], msg.sender));
}
return true;
}
function _deposit(address layer2, address account, uint256 amount, address payer) internal returns (bool) {
require(account != address(0) && amount != 0, "zero amount or zero address");
_accStaked[layer2][account] = _accStaked[layer2][account] + amount;
_accStakedLayer2[layer2] = _accStakedLayer2[layer2] + amount;
_accStakedAccount[account] = _accStakedAccount[account] + amount;
IERC20(_wton).safeTransferFrom(payer, address(this), amount);
emit Deposited(layer2, account, amount);
require(ISeigManager(_seigManager).onDeposit(layer2, account, amount));
return true;
}
////////////////////
// Re-deposit function
////////////////////
/**
* @dev re-deposit pending requests in the pending queue
*/
function redeposit(address layer2) external returns (bool) {
uint256 i = _withdrawalRequestIndex[layer2][msg.sender];
require(_redeposit(layer2, i, 1));
return true;
}
function redepositMulti(address layer2, uint256 n) external returns (bool) {
uint256 i = _withdrawalRequestIndex[layer2][msg.sender];
require(_redeposit(layer2, i, n));
return true;
}
function _redeposit(address layer2, uint256 i, uint256 n) internal returns (bool) {
uint256 accAmount;
require(_withdrawalRequests[layer2][msg.sender].length > 0, "DepositManager: no request");
require(_withdrawalRequests[layer2][msg.sender].length - i >= n, "DepositManager: n exceeds num of pending requests");
uint256 e = i + n;
for (; i < e; i++) {
WithdrawalReqeust storage r = _withdrawalRequests[layer2][msg.sender][i];
uint256 amount = r.amount;
require(!r.processed, "DepositManager: pending request already processed");
require(amount > 0, "DepositManager: no valid pending request");
accAmount = accAmount + amount;
r.processed = true;
}
// deposit-related storages
_accStaked[layer2][msg.sender] = _accStaked[layer2][msg.sender] + accAmount;
_accStakedLayer2[layer2] = _accStakedLayer2[layer2] + accAmount;
_accStakedAccount[msg.sender] = _accStakedAccount[msg.sender] + accAmount;
// withdrawal-related storages
_pendingUnstaked[layer2][msg.sender] = _pendingUnstaked[layer2][msg.sender] - accAmount;
_pendingUnstakedLayer2[layer2] = _pendingUnstakedLayer2[layer2] - accAmount;
_pendingUnstakedAccount[msg.sender] = _pendingUnstakedAccount[msg.sender] - accAmount;
_withdrawalRequestIndex[layer2][msg.sender] += n;
emit Deposited(layer2, msg.sender, accAmount);
require(ISeigManager(_seigManager).onDeposit(layer2, msg.sender, accAmount));
return true;
}
////////////////////
// Slash functions
////////////////////
function slash(address layer2, address recipient, uint256 amount) external returns (bool) {
//return _wton.transferFrom(owner, recipient, amount);
}
////////////////////
// Setter
////////////////////
function setGlobalWithdrawalDelay(uint256 globalWithdrawalDelay_) external {
globalWithdrawalDelay = globalWithdrawalDelay_;
}
function setWithdrawalDelay(address l2chain, uint256 withdrawalDelay_) external {
require(_isOperator(l2chain, msg.sender));
withdrawalDelay[l2chain] = withdrawalDelay_;
}
////////////////////
// Withdrawal functions
////////////////////
function requestWithdrawal(address layer2, uint256 amount) external returns (bool) {
return _requestWithdrawal(layer2, amount, getDelayBlocks(layer2));
}
function _requestWithdrawal(address layer2, uint256 amount, uint256 delay) internal returns (bool) {
require(amount > 0, "DepositManager: amount must not be zero");
// uint256 delay = globalWithdrawalDelay > withdrawalDelay[layer2] ? globalWithdrawalDelay : withdrawalDelay[layer2];
_withdrawalRequests[layer2][msg.sender].push(WithdrawalReqeust({
withdrawableBlockNumber: uint128(block.number + delay),
amount: uint128(amount),
processed: false
}));
_pendingUnstaked[layer2][msg.sender] = _pendingUnstaked[layer2][msg.sender] + amount;
_pendingUnstakedLayer2[layer2] = _pendingUnstakedLayer2[layer2] + amount;
_pendingUnstakedAccount[msg.sender] = _pendingUnstakedAccount[msg.sender] + amount;
emit WithdrawalRequested(layer2, msg.sender, amount);
require(ISeigManager(_seigManager).onWithdraw(layer2, msg.sender, amount));
return true;
}
function processRequest(address layer2, bool receiveTON) external returns (bool) {
return _processRequest(layer2, receiveTON);
}
function _processRequest(address layer2, bool receiveTON) internal returns (bool) {
uint256 index = _withdrawalRequestIndex[layer2][msg.sender];
require(_withdrawalRequests[layer2][msg.sender].length > index, "DepositManager: no request to process");
WithdrawalReqeust storage r = _withdrawalRequests[layer2][msg.sender][index];
require(r.withdrawableBlockNumber <= block.number, "DepositManager: wait for withdrawal delay");
r.processed = true;
_withdrawalRequestIndex[layer2][msg.sender] += 1;
uint256 amount = r.amount;
_pendingUnstaked[layer2][msg.sender] = _pendingUnstaked[layer2][msg.sender] - amount;
_pendingUnstakedLayer2[layer2] = _pendingUnstakedLayer2[layer2] - amount;
_pendingUnstakedAccount[msg.sender] = _pendingUnstakedAccount[msg.sender] - amount;
_accUnstaked[layer2][msg.sender] = _accUnstaked[layer2][msg.sender] + amount;
_accUnstakedLayer2[layer2] = _accUnstakedLayer2[layer2] + amount;
_accUnstakedAccount[msg.sender] = _accUnstakedAccount[msg.sender] + amount;
if (receiveTON) {
require(IWTON(_wton).swapToTONAndTransfer(msg.sender, amount));
} else {
IERC20(_wton).safeTransfer(msg.sender, amount);
}
emit WithdrawalProcessed(layer2, msg.sender, amount);
return true;
}
function requestWithdrawalAll(address layer2) external returns (bool) {
uint256 amount = ISeigManager(_seigManager).stakeOf(layer2, msg.sender);
return _requestWithdrawal(layer2, amount, getDelayBlocks(layer2));
}
function processRequests(address layer2, uint256 n, bool receiveTON) external returns (bool) {
for (uint256 i = 0; i < n; i++) {
require(_processRequest(layer2, receiveTON));
}
return true;
}
function numRequests(address layer2, address account) external view returns (uint256) {
return _withdrawalRequests[layer2][account].length;
}
function numPendingRequests(address layer2, address account) external view returns (uint256) {
uint256 numRequests_ = _withdrawalRequests[layer2][account].length;
uint256 index = _withdrawalRequestIndex[layer2][account];
if (numRequests_ == 0) return 0;
return numRequests_ - index;
}
function _isOperator(address layer2, address operator) internal view returns (bool) {
return operator == ILayer2(layer2).operator();
}
function getDelayBlocks(address layer2) public view returns (uint256){
return globalWithdrawalDelay > withdrawalDelay[layer2] ? globalWithdrawalDelay : withdrawalDelay[layer2];
}
////////////////////
// Storage getters
////////////////////
// solium-disable
function wton() external view returns (address) { return _wton; }
function registry() external view returns (address) { return _registry; }
function seigManager() external view returns (address) { return _seigManager; }
function accStaked(address layer2, address account) external view returns (uint256 wtonAmount) { return _accStaked[layer2][account]; }
function accStakedLayer2(address layer2) external view returns (uint256 wtonAmount) { return _accStakedLayer2[layer2]; }
function accStakedAccount(address account) external view returns (uint256 wtonAmount) { return _accStakedAccount[account]; }
function pendingUnstaked(address layer2, address account) external view returns (uint256 wtonAmount) { return _pendingUnstaked[layer2][account]; }
function pendingUnstakedLayer2(address layer2) external view returns (uint256 wtonAmount) { return _pendingUnstakedLayer2[layer2]; }
function pendingUnstakedAccount(address account) external view returns (uint256 wtonAmount) { return _pendingUnstakedAccount[account]; }
function accUnstaked(address layer2, address account) external view returns (uint256 wtonAmount) { return _accUnstaked[layer2][account]; }
function accUnstakedLayer2(address layer2) external view returns (uint256 wtonAmount) { return _accUnstakedLayer2[layer2]; }
function accUnstakedAccount(address account) external view returns (uint256 wtonAmount) { return _accUnstakedAccount[account]; }
function withdrawalRequestIndex(address layer2, address account) external view returns (uint256 index) { return _withdrawalRequestIndex[layer2][account]; }
function withdrawalRequest(address layer2, address account, uint256 index) external view returns (uint128 withdrawableBlockNumber, uint128 amount, bool processed ) {
withdrawableBlockNumber = _withdrawalRequests[layer2][account][index].withdrawableBlockNumber;
amount = _withdrawalRequests[layer2][account][index].amount;
processed = _withdrawalRequests[layer2][account][index].processed;
}
// solium-enable
}