-
Notifications
You must be signed in to change notification settings - Fork 49
/
Copy pathEscrow.sol
298 lines (240 loc) · 10.9 KB
/
Escrow.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
pragma solidity ^0.5.17;
import "../openzeppelin/SafeMath.sol";
import "../interfaces/IERC20.sol";
/**
* @title A holding contract for Sovryn Ethereum Pool to accept SOV Token.
* @author Franklin Richards - [email protected]
* @notice You can use this contract for deposit of SOV tokens for some time and withdraw later.
*/
contract Escrow {
using SafeMath for uint256;
/* Storage */
/// @notice The total tokens deposited.
/// @dev Used for calculating the reward % share of users related to total deposit.
uint256 public totalDeposit;
/// @notice The release timestamp for the tokens deposited.
uint256 public releaseTime;
/// @notice The amount of token we would be accepting as deposit at max.
uint256 public depositLimit;
/// @notice The SOV token contract.
IERC20 public SOV;
/// @notice The multisig contract which handles the fund.
address public multisig;
/// @notice The user balances.
mapping(address => uint256) userBalances;
/// @notice The current contract status.
/// @notice Deployed - Deployed the contract.
/// @notice Deposit - Time to deposit in the contract by the users.
/// @notice Holding - Deposit is closed and now the holding period starts.
/// @notice Withdraw - Time to withdraw in the contract by the users.
/// @notice Expired - The contract is now closed completely.
enum Status {
Deployed,
Deposit,
Holding,
Withdraw,
Expired
}
Status public status;
/* Events */
/// @notice Emitted when the contract deposit starts.
event EscrowActivated();
/// @notice Emitted when the contract is put in holding state. No new token deposit accepted by User.
event EscrowInHoldingState();
/// @notice Emitted when the contract is put in withdraw state. Users can now withdraw tokens.
event EscrowInWithdrawState();
/// @notice Emitted when the contract is expired after withdraws are made/total token transfer.
event EscrowFundExpired();
/// @notice Emitted when a new multisig is added to the contract.
/// @param _initiator The address which initiated this event to be emitted.
/// @param _newMultisig The address which is added as the new multisig.
/// @dev Can only be initiated by the current multisig.
event NewMultisig(address indexed _initiator, address indexed _newMultisig);
/// @notice Emitted when the release timestamp is updated.
/// @param _initiator The address which initiated this event to be emitted.
/// @param _releaseTimestamp The updated release timestamp for the withdraw.
event TokenReleaseUpdated(address indexed _initiator, uint256 _releaseTimestamp);
/// @notice Emitted when the deposit limit is updated.
/// @param _initiator The address which initiated this event to be emitted.
/// @param _depositLimit The updated deposit limit.
event TokenDepositLimitUpdated(address indexed _initiator, uint256 _depositLimit);
/// @notice Emitted when a new token deposit is done by User.
/// @param _initiator The address which initiated this event to be emitted.
/// @param _amount The amount of token deposited.
event TokenDeposit(address indexed _initiator, uint256 _amount);
/// @notice Emitted when we reach the token deposit limit.
event DepositLimitReached();
/// @notice Emitted when a token withdraw is done by Multisig.
/// @param _initiator The address which initiated this event to be emitted.
/// @param _amount The amount of token withdrawed.
event TokenWithdrawByMultisig(address indexed _initiator, uint256 _amount);
/// @notice Emitted when a new token deposit is done by Multisig.
/// @param _initiator The address which initiated this event to be emitted.
/// @param _amount The amount of token deposited.
event TokenDepositByMultisig(address indexed _initiator, uint256 _amount);
/// @notice Emitted when a token withdraw is done by User.
/// @param _initiator The address which initiated this event to be emitted.
/// @param _amount The amount of token withdrawed.
event TokenWithdraw(address indexed _initiator, uint256 _amount);
/* Modifiers */
modifier onlyMultisig() {
require(msg.sender == multisig, "Only Multisig can call this.");
_;
}
modifier checkStatus(Status s) {
require(status == s, "The contract is not in the right state.");
_;
}
modifier checkRelease() {
require(
releaseTime != 0 && releaseTime <= block.timestamp,
"The release time has not started yet."
);
_;
}
/* Functions */
/**
* @notice Setup the required parameters.
* @param _SOV The SOV token address.
* @param _multisig The owner of the tokens & contract.
* @param _releaseTime The token release time, zero if undecided.
* @param _depositLimit The amount of tokens we will be accepting.
*/
constructor(
address _SOV,
address _multisig,
uint256 _releaseTime,
uint256 _depositLimit
) public {
require(_SOV != address(0), "Invalid SOV Address.");
require(_multisig != address(0), "Invalid Multisig Address.");
SOV = IERC20(_SOV);
multisig = _multisig;
emit NewMultisig(msg.sender, _multisig);
releaseTime = _releaseTime;
depositLimit = _depositLimit;
status = Status.Deployed;
}
/**
* @notice This function is called once after deployment for starting the deposit action.
* @dev Without calling this function, the contract will not start accepting tokens.
*/
function init() external onlyMultisig checkStatus(Status.Deployed) {
status = Status.Deposit;
emit EscrowActivated();
}
/**
* @notice Update Multisig.
* @param _newMultisig The new owner of the tokens & contract.
*/
function updateMultisig(address _newMultisig) external onlyMultisig {
require(_newMultisig != address(0), "New Multisig address invalid.");
multisig = _newMultisig;
emit NewMultisig(msg.sender, _newMultisig);
}
/**
* @notice Update Release Timestamp.
* @param _newReleaseTime The new release timestamp for token release.
* @dev Zero is also a valid timestamp, if the release time is not scheduled yet.
*/
function updateReleaseTimestamp(uint256 _newReleaseTime) external onlyMultisig {
releaseTime = _newReleaseTime;
emit TokenReleaseUpdated(msg.sender, _newReleaseTime);
}
/**
* @notice Update Deposit Limit.
* @param _newDepositLimit The new deposit limit.
* @dev IMPORTANT: Should not decrease than already deposited.
*/
function updateDepositLimit(uint256 _newDepositLimit) external onlyMultisig {
require(
_newDepositLimit >= totalDeposit,
"Deposit already higher than the limit trying to be set."
);
depositLimit = _newDepositLimit;
emit TokenDepositLimitUpdated(msg.sender, _newDepositLimit);
}
/**
* @notice Deposit tokens to this contract by User.
* @param _amount the amount of tokens deposited.
* @dev The contract has to be approved by the user inorder for this function to work.
* These tokens can be withdrawn/transferred during Holding State by the Multisig.
*/
function depositTokens(uint256 _amount) external checkStatus(Status.Deposit) {
require(_amount > 0, "Amount needs to be bigger than zero.");
uint256 amount = _amount;
if (totalDeposit.add(_amount) >= depositLimit) {
amount = depositLimit.sub(totalDeposit);
emit DepositLimitReached();
}
bool txStatus = SOV.transferFrom(msg.sender, address(this), amount);
require(txStatus, "Token transfer was not successful.");
userBalances[msg.sender] = userBalances[msg.sender].add(amount);
totalDeposit = totalDeposit.add(amount);
emit TokenDeposit(msg.sender, amount);
}
/**
* @notice Update contract state to Holding.
* @dev Once called, the contract no longer accepts any more deposits.
* The multisig can now withdraw tokens from the contract after the contract is in Holding State.
*/
function changeStateToHolding() external onlyMultisig checkStatus(Status.Deposit) {
status = Status.Holding;
emit EscrowInHoldingState();
}
/**
* @notice Withdraws all token from the contract by Multisig.
* @param _receiverAddress The address where the tokens has to be transferred. Zero address if the withdraw is to be done in Multisig.
* @dev Can only be called after the token state is changed to Holding.
*/
function withdrawTokensByMultisig(
address _receiverAddress
) external onlyMultisig checkStatus(Status.Holding) {
address receiverAddress = msg.sender;
if (_receiverAddress != address(0)) {
receiverAddress = _receiverAddress;
}
uint256 value = SOV.balanceOf(address(this));
/// Sending the amount to multisig.
bool txStatus = SOV.transfer(receiverAddress, value);
require(txStatus, "Token transfer was not successful. Check receiver address.");
emit TokenWithdrawByMultisig(msg.sender, value);
}
/**
* @notice Deposit tokens to this contract by the Multisig.
* @param _amount the amount of tokens deposited.
* @dev The contract has to be approved by the multisig inorder for this function to work.
* Once the token deposit is higher than the total deposits done, the contract state is changed to Withdraw.
*/
function depositTokensByMultisig(
uint256 _amount
) external onlyMultisig checkStatus(Status.Holding) {
require(_amount > 0, "Amount needs to be bigger than zero.");
bool txStatus = SOV.transferFrom(msg.sender, address(this), _amount);
require(txStatus, "Token transfer was not successful.");
emit TokenDepositByMultisig(msg.sender, _amount);
if (SOV.balanceOf(address(this)) >= totalDeposit) {
status = Status.Withdraw;
emit EscrowInWithdrawState();
}
}
/**
* @notice Withdraws token from the contract by User.
* @dev Only works after the contract state is in Withdraw.
*/
function withdrawTokens() public checkRelease checkStatus(Status.Withdraw) {
uint256 amount = userBalances[msg.sender];
userBalances[msg.sender] = 0;
bool txStatus = SOV.transfer(msg.sender, amount);
require(txStatus, "Token transfer was not successful. Check receiver address.");
emit TokenWithdraw(msg.sender, amount);
}
/* Getter Functions */
/**
* @notice Function to read the current token balance of a particular user.
* @return _addr The user address whose balance has to be checked.
*/
function getUserBalance(address _addr) external view returns (uint256 balance) {
return userBalances[_addr];
}
}