forked from Synthetixio/synthetix
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathRewardEscrowV2.sol
245 lines (191 loc) · 10.7 KB
/
RewardEscrowV2.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
pragma solidity ^0.5.16;
pragma experimental ABIEncoderV2;
// Inheritance
import "./BaseRewardEscrowV2.sol";
// Internal references
import "./interfaces/IRewardEscrow.sol";
import "./interfaces/ISystemStatus.sol";
// https://docs.synthetix.io/contracts/RewardEscrow
contract RewardEscrowV2 is BaseRewardEscrowV2 {
mapping(address => uint256) public totalBalancePendingMigration;
uint public migrateEntriesThresholdAmount = SafeDecimalMath.unit() * 1000; // Default 1000 SNX
/* ========== ADDRESS RESOLVER CONFIGURATION ========== */
bytes32 private constant CONTRACT_SYNTHETIX_BRIDGE_OPTIMISM = "SynthetixBridgeToOptimism";
bytes32 private constant CONTRACT_REWARD_ESCROW = "RewardEscrow";
bytes32 private constant CONTRACT_SYSTEMSTATUS = "SystemStatus";
/* ========== CONSTRUCTOR ========== */
constructor(address _owner, address _resolver) public BaseRewardEscrowV2(_owner, _resolver) {}
/* ========== VIEWS ======================= */
function resolverAddressesRequired() public view returns (bytes32[] memory addresses) {
bytes32[] memory existingAddresses = BaseRewardEscrowV2.resolverAddressesRequired();
bytes32[] memory newAddresses = new bytes32[](3);
newAddresses[0] = CONTRACT_SYNTHETIX_BRIDGE_OPTIMISM;
newAddresses[1] = CONTRACT_REWARD_ESCROW;
newAddresses[2] = CONTRACT_SYSTEMSTATUS;
return combineArrays(existingAddresses, newAddresses);
}
function synthetixBridgeToOptimism() internal view returns (address) {
return requireAndGetAddress(CONTRACT_SYNTHETIX_BRIDGE_OPTIMISM);
}
function oldRewardEscrow() internal view returns (IRewardEscrow) {
return IRewardEscrow(requireAndGetAddress(CONTRACT_REWARD_ESCROW));
}
function systemStatus() internal view returns (ISystemStatus) {
return ISystemStatus(requireAndGetAddress(CONTRACT_SYSTEMSTATUS));
}
/* ========== OLD ESCROW LOOKUP ========== */
uint internal constant TIME_INDEX = 0;
uint internal constant QUANTITY_INDEX = 1;
/* ========== MIGRATION OLD ESCROW ========== */
/* Threshold amount for migrating escrow entries from old RewardEscrow */
function setMigrateEntriesThresholdAmount(uint amount) external onlyOwner {
migrateEntriesThresholdAmount = amount;
emit MigrateEntriesThresholdAmountUpdated(amount);
}
/* Function to allow any address to migrate vesting entries from previous reward escrow */
function migrateVestingSchedule(address addressToMigrate) external systemActive {
/* Ensure account escrow balance pending migration is not zero */
/* Ensure account escrowed balance is not zero - should have been migrated */
require(totalBalancePendingMigration[addressToMigrate] > 0, "No escrow migration pending");
require(totalEscrowedAccountBalance[addressToMigrate] > 0, "Address escrow balance is 0");
/* Add a vestable entry for addresses with totalBalancePendingMigration <= migrateEntriesThreshold amount of SNX */
if (totalBalancePendingMigration[addressToMigrate] <= migrateEntriesThresholdAmount) {
_importVestingEntry(
addressToMigrate,
VestingEntries.VestingEntry({
endTime: uint64(block.timestamp),
escrowAmount: totalBalancePendingMigration[addressToMigrate]
})
);
/* Remove totalBalancePendingMigration[addressToMigrate] */
delete totalBalancePendingMigration[addressToMigrate];
} else {
uint numEntries = oldRewardEscrow().numVestingEntries(addressToMigrate);
/* iterate and migrate old escrow schedules from rewardEscrow.vestingSchedules
* starting from the last entry in each staker's vestingSchedules
*/
for (uint i = 1; i <= numEntries; i++) {
uint[2] memory vestingSchedule = oldRewardEscrow().getVestingScheduleEntry(addressToMigrate, numEntries - i);
uint time = vestingSchedule[TIME_INDEX];
uint amount = vestingSchedule[QUANTITY_INDEX];
/* The list is sorted, when we reach the first entry that can be vested stop */
if (time < block.timestamp) {
break;
}
/* import vesting entry */
_importVestingEntry(
addressToMigrate,
VestingEntries.VestingEntry({endTime: uint64(time), escrowAmount: amount})
);
/* subtract amount from totalBalancePendingMigration - reverts if insufficient */
totalBalancePendingMigration[addressToMigrate] = totalBalancePendingMigration[addressToMigrate].sub(amount);
}
}
}
/**
* Import function for owner to import vesting schedule
* All entries imported should have past their vesting timestamp and will be ready to be vested
* Addresses with totalEscrowedAccountBalance == 0 will not be migrated as they have all vested
*/
function importVestingSchedule(address[] calldata accounts, uint256[] calldata escrowAmounts)
external
onlyDuringSetup
onlyOwner
{
require(accounts.length == escrowAmounts.length, "Account and escrowAmounts Length mismatch");
for (uint i = 0; i < accounts.length; i++) {
address addressToMigrate = accounts[i];
uint256 escrowAmount = escrowAmounts[i];
// ensure account have escrow migration pending
require(totalEscrowedAccountBalance[addressToMigrate] > 0, "Address escrow balance is 0");
require(totalBalancePendingMigration[addressToMigrate] > 0, "No escrow migration pending");
/* Import vesting entry with endTime as block.timestamp and escrowAmount */
_importVestingEntry(
addressToMigrate,
VestingEntries.VestingEntry({endTime: uint64(block.timestamp), escrowAmount: escrowAmount})
);
/* update totalBalancePendingMigration - reverts if escrowAmount > remaining balance to migrate */
totalBalancePendingMigration[addressToMigrate] = totalBalancePendingMigration[addressToMigrate].sub(
escrowAmount
);
emit ImportedVestingSchedule(addressToMigrate, block.timestamp, escrowAmount);
}
}
/**
* Migration for owner to migrate escrowed and vested account balances
* Addresses with totalEscrowedAccountBalance == 0 will not be migrated as they have all vested
*/
function migrateAccountEscrowBalances(
address[] calldata accounts,
uint256[] calldata escrowBalances,
uint256[] calldata vestedBalances
) external onlyDuringSetup onlyOwner {
require(accounts.length == escrowBalances.length, "Number of accounts and balances don't match");
require(accounts.length == vestedBalances.length, "Number of accounts and vestedBalances don't match");
for (uint i = 0; i < accounts.length; i++) {
address account = accounts[i];
uint escrowedAmount = escrowBalances[i];
uint vestedAmount = vestedBalances[i];
// ensure account doesn't have escrow migration pending / being imported more than once
require(totalBalancePendingMigration[account] == 0, "Account migration is pending already");
/* Update totalEscrowedBalance for tracking the Synthetix balance of this contract. */
totalEscrowedBalance = totalEscrowedBalance.add(escrowedAmount);
/* Update totalEscrowedAccountBalance and totalVestedAccountBalance for each account */
totalEscrowedAccountBalance[account] = totalEscrowedAccountBalance[account].add(escrowedAmount);
totalVestedAccountBalance[account] = totalVestedAccountBalance[account].add(vestedAmount);
/* update totalBalancePendingMigration for account */
totalBalancePendingMigration[account] = escrowedAmount;
emit MigratedAccountEscrow(account, escrowedAmount, vestedAmount, now);
}
}
/* Internal function to add entry to vestingSchedules and emit event */
function _importVestingEntry(address account, VestingEntries.VestingEntry memory entry) internal {
/* add vesting entry to account and assign an entryID to it */
uint entryID = BaseRewardEscrowV2._addVestingEntry(account, entry);
emit ImportedVestingEntry(account, entryID, entry.escrowAmount, entry.endTime);
}
/* ========== L2 MIGRATION ========== */
function burnForMigration(address account, uint[] calldata entryIDs)
external
onlySynthetixBridge
returns (uint256 escrowedAccountBalance, VestingEntries.VestingEntry[] memory vestingEntries)
{
require(entryIDs.length > 0, "Entry IDs required");
vestingEntries = new VestingEntries.VestingEntry[](entryIDs.length);
for (uint i = 0; i < entryIDs.length; i++) {
VestingEntries.VestingEntry storage entry = vestingSchedules[account][entryIDs[i]];
if (entry.escrowAmount > 0) {
vestingEntries[i] = entry;
/* add the escrow amount to escrowedAccountBalance */
escrowedAccountBalance = escrowedAccountBalance.add(entry.escrowAmount);
/* Delete the vesting entry being migrated */
delete vestingSchedules[account][entryIDs[i]];
}
}
/**
* update account total escrow balances for migration
* transfer the escrowed SNX being migrated to the L2 deposit contract
*/
if (escrowedAccountBalance > 0) {
_reduceAccountEscrowBalances(account, escrowedAccountBalance);
IERC20(address(synthetix())).transfer(synthetixBridgeToOptimism(), escrowedAccountBalance);
}
emit BurnedForMigrationToL2(account, entryIDs, escrowedAccountBalance, block.timestamp);
return (escrowedAccountBalance, vestingEntries);
}
/* ========== MODIFIERS ========== */
modifier onlySynthetixBridge() {
require(msg.sender == synthetixBridgeToOptimism(), "Can only be invoked by SynthetixBridgeToOptimism contract");
_;
}
modifier systemActive() {
systemStatus().requireSystemActive();
_;
}
/* ========== EVENTS ========== */
event MigratedAccountEscrow(address indexed account, uint escrowedAmount, uint vestedAmount, uint time);
event ImportedVestingSchedule(address indexed account, uint time, uint escrowAmount);
event BurnedForMigrationToL2(address indexed account, uint[] entryIDs, uint escrowedAmountMigrated, uint time);
event ImportedVestingEntry(address indexed account, uint entryID, uint escrowAmount, uint endTime);
event MigrateEntriesThresholdAmountUpdated(uint newAmount);
}