/
StakingAuRaBase.sol
1227 lines (1056 loc) · 64.5 KB
/
StakingAuRaBase.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
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
pragma solidity 0.5.10;
import "../interfaces/IBlockRewardAuRa.sol";
import "../interfaces/IERC677Minting.sol";
import "../interfaces/IStakingAuRa.sol";
import "../interfaces/IValidatorSetAuRa.sol";
import "../upgradeability/UpgradeableOwned.sol";
import "../libs/SafeMath.sol";
/// @dev Implements staking and withdrawal logic.
contract StakingAuRaBase is UpgradeableOwned, IStakingAuRa {
using SafeMath for uint256;
// =============================================== Storage ========================================================
// WARNING: since this contract is upgradeable, do not remove
// existing storage variables, do not change their order,
// and do not change their types!
address[] internal _pools;
address[] internal _poolsInactive;
address[] internal _poolsToBeElected;
address[] internal _poolsToBeRemoved;
uint256[] internal _poolsLikelihood;
uint256 internal _poolsLikelihoodSum;
mapping(address => address[]) internal _poolDelegators;
mapping(address => address[]) internal _poolDelegatorsInactive;
mapping(address => address[]) internal _stakerPools;
mapping(address => mapping(address => uint256)) internal _stakerPoolsIndexes;
mapping(address => mapping(address => mapping(uint256 => uint256))) internal _stakeAmountByEpoch;
// Reserved storage space to allow for layout changes in the future.
uint256[25] private ______gapForInternal;
/// @dev The limit of the minimum candidate stake (CANDIDATE_MIN_STAKE).
uint256 public candidateMinStake;
/// @dev The limit of the minimum delegator stake (DELEGATOR_MIN_STAKE).
uint256 public delegatorMinStake;
/// @dev The snapshot of tokens amount staked into the specified pool by the specified delegator
/// before the specified staking epoch. Used by the `claimReward` function.
/// The first parameter is the pool staking address, the second one is delegator's address,
/// the third one is staking epoch number.
mapping(address => mapping(address => mapping(uint256 => uint256))) public delegatorStakeSnapshot;
/// @dev The current amount of staking tokens/coins ordered for withdrawal from the specified
/// pool by the specified staker. Used by the `orderWithdraw`, `claimOrderedWithdraw` and other functions.
/// The first parameter is the pool staking address, the second one is the staker address.
mapping(address => mapping(address => uint256)) public orderedWithdrawAmount;
/// @dev The current total amount of staking tokens/coins ordered for withdrawal from
/// the specified pool by all of its stakers. Pool staking address is accepted as a parameter.
mapping(address => uint256) public orderedWithdrawAmountTotal;
/// @dev The number of the staking epoch during which the specified staker ordered
/// the latest withdraw from the specified pool. Used by the `claimOrderedWithdraw` function
/// to allow the ordered amount to be claimed only in future staking epochs. The first parameter
/// is the pool staking address, the second one is the staker address.
mapping(address => mapping(address => uint256)) public orderWithdrawEpoch;
/// @dev The delegator's index in the array returned by the `poolDelegators` getter.
/// Used by the `_removePoolDelegator` internal function. The first parameter is a pool staking address.
/// The second parameter is delegator's address.
/// If the value is zero, it may mean the array doesn't contain the delegator.
/// Check if the delegator is in the array using the `poolDelegators` getter.
mapping(address => mapping(address => uint256)) public poolDelegatorIndex;
/// @dev The delegator's index in the `poolDelegatorsInactive` array.
/// Used by the `_removePoolDelegatorInactive` internal function.
/// A delegator is considered inactive if they have withdrawn their stake from
/// the specified pool but haven't yet claimed an ordered amount.
/// The first parameter is a pool staking address. The second parameter is delegator's address.
mapping(address => mapping(address => uint256)) public poolDelegatorInactiveIndex;
/// @dev The pool's index in the array returned by the `getPoolsInactive` getter.
/// Used by the `_removePoolInactive` internal function. The pool staking address is accepted as a parameter.
mapping(address => uint256) public poolInactiveIndex;
/// @dev The pool's index in the array returned by the `getPools` getter.
/// Used by the `_removePool` internal function. A pool staking address is accepted as a parameter.
/// If the value is zero, it may mean the array doesn't contain the address.
/// Check the address is in the array using the `isPoolActive` getter.
mapping(address => uint256) public poolIndex;
/// @dev The pool's index in the array returned by the `getPoolsToBeElected` getter.
/// Used by the `_deletePoolToBeElected` and `_isPoolToBeElected` internal functions.
/// The pool staking address is accepted as a parameter.
/// If the value is zero, it may mean the array doesn't contain the address.
/// Check the address is in the array using the `getPoolsToBeElected` getter.
mapping(address => uint256) public poolToBeElectedIndex;
/// @dev The pool's index in the array returned by the `getPoolsToBeRemoved` getter.
/// Used by the `_deletePoolToBeRemoved` internal function.
/// The pool staking address is accepted as a parameter.
/// If the value is zero, it may mean the array doesn't contain the address.
/// Check the address is in the array using the `getPoolsToBeRemoved` getter.
mapping(address => uint256) public poolToBeRemovedIndex;
/// @dev A boolean flag indicating whether the reward was already taken
/// from the specified pool by the specified staker for the specified staking epoch.
/// The first parameter is the pool staking address, the second one is staker's address,
/// the third one is staking epoch number.
mapping(address => mapping(address => mapping(uint256 => bool))) public rewardWasTaken;
/// @dev The amount of tokens currently staked into the specified pool by the specified
/// staker. Doesn't include the amount ordered for withdrawal.
/// The first parameter is the pool staking address, the second one is the staker address.
mapping(address => mapping(address => uint256)) public stakeAmount;
/// @dev The number of staking epoch before which the specified delegator placed their first
/// stake into the specified pool. If this is equal to zero, it means the delegator never
/// staked into the specified pool. The first parameter is the pool staking address,
/// the second one is delegator's address.
mapping(address => mapping(address => uint256)) public stakeFirstEpoch;
/// @dev The number of staking epoch before which the specified delegator withdrew their stake
/// from the specified pool. If this is equal to zero and `stakeFirstEpoch` is not zero, that means
/// the delegator still has some stake in the specified pool. The first parameter is the pool
/// staking address, the second one is delegator's address.
mapping(address => mapping(address => uint256)) public stakeLastEpoch;
/// @dev The duration period (in blocks) at the end of staking epoch during which
/// participants are not allowed to stake/withdraw/order/claim their staking tokens/coins.
uint256 public stakeWithdrawDisallowPeriod;
/// @dev The serial number of the current staking epoch.
uint256 public stakingEpoch;
/// @dev The duration of a staking epoch in blocks.
uint256 public stakingEpochDuration;
/// @dev The number of the first block of the current staking epoch.
uint256 public stakingEpochStartBlock;
/// @dev Returns the total amount of staking tokens/coins currently staked into the specified pool.
/// Doesn't include the amount ordered for withdrawal.
/// The pool staking address is accepted as a parameter.
mapping(address => uint256) public stakeAmountTotal;
/// @dev The address of the `ValidatorSetAuRa` contract.
IValidatorSetAuRa public validatorSetContract;
// Reserved storage space to allow for layout changes in the future.
uint256[25] private ______gapForPublic;
// ============================================== Constants =======================================================
/// @dev The max number of candidates (including validators). This limit was determined through stress testing.
uint256 public constant MAX_CANDIDATES = 3000;
// ================================================ Events ========================================================
/// @dev Emitted by `_addPoolActive` internal function to signal that
/// the specified staking address created a new pool.
/// @param poolStakingAddress The staking address of newly added pool.
event AddedPool(address indexed poolStakingAddress);
/// @dev Emitted by the `claimOrderedWithdraw` function to signal the staker withdrew the specified
/// amount of requested tokens/coins from the specified pool during the specified staking epoch.
/// @param fromPoolStakingAddress The pool from which the `staker` withdrew the `amount`.
/// @param staker The address of the staker that withdrew the `amount`.
/// @param stakingEpoch The serial number of the staking epoch during which the claim was made.
/// @param amount The withdrawal amount.
event ClaimedOrderedWithdrawal(
address indexed fromPoolStakingAddress,
address indexed staker,
uint256 indexed stakingEpoch,
uint256 amount
);
/// @dev Emitted by the `moveStake` function to signal the staker moved the specified
/// amount of stake from one pool to another during the specified staking epoch.
/// @param fromPoolStakingAddress The pool from which the `staker` moved the stake.
/// @param toPoolStakingAddress The destination pool where the `staker` moved the stake.
/// @param staker The address of the staker who moved the `amount`.
/// @param stakingEpoch The serial number of the staking epoch during which the `amount` was moved.
/// @param amount The stake amount which was moved.
event MovedStake(
address fromPoolStakingAddress,
address indexed toPoolStakingAddress,
address indexed staker,
uint256 indexed stakingEpoch,
uint256 amount
);
/// @dev Emitted by the `orderWithdraw` function to signal the staker ordered the withdrawal of the
/// specified amount of their stake from the specified pool during the specified staking epoch.
/// @param fromPoolStakingAddress The pool from which the `staker` ordered a withdrawal of the `amount`.
/// @param staker The address of the staker that ordered the withdrawal of the `amount`.
/// @param stakingEpoch The serial number of the staking epoch during which the order was made.
/// @param amount The ordered withdrawal amount. Can be either positive or negative.
/// See the `orderWithdraw` function.
event OrderedWithdrawal(
address indexed fromPoolStakingAddress,
address indexed staker,
uint256 indexed stakingEpoch,
int256 amount
);
/// @dev Emitted by the `stake` function to signal the staker placed a stake of the specified
/// amount for the specified pool during the specified staking epoch.
/// @param toPoolStakingAddress The pool in which the `staker` placed the stake.
/// @param staker The address of the staker that placed the stake.
/// @param stakingEpoch The serial number of the staking epoch during which the stake was made.
/// @param amount The stake amount.
event PlacedStake(
address indexed toPoolStakingAddress,
address indexed staker,
uint256 indexed stakingEpoch,
uint256 amount
);
/// @dev Emitted by `_removePool` internal function to signal that
/// a pool with the specified staking address was removed.
/// @param poolStakingAddress The staking address of removed pool.
event RemovedPool(address indexed poolStakingAddress);
/// @dev Emitted by the `withdraw` function to signal the staker withdrew the specified
/// amount of a stake from the specified pool during the specified staking epoch.
/// @param fromPoolStakingAddress The pool from which the `staker` withdrew the `amount`.
/// @param staker The address of staker that withdrew the `amount`.
/// @param stakingEpoch The serial number of the staking epoch during which the withdrawal was made.
/// @param amount The withdrawal amount.
event WithdrewStake(
address indexed fromPoolStakingAddress,
address indexed staker,
uint256 indexed stakingEpoch,
uint256 amount
);
// ============================================== Modifiers =======================================================
/// @dev Ensures the transaction gas price is not zero.
modifier gasPriceIsValid() {
require(tx.gasprice != 0);
_;
}
/// @dev Ensures the caller is the BlockRewardAuRa contract address.
modifier onlyBlockRewardContract() {
require(msg.sender == validatorSetContract.blockRewardContract());
_;
}
/// @dev Ensures the `initialize` function was called before.
modifier onlyInitialized {
require(isInitialized());
_;
}
/// @dev Ensures the caller is the ValidatorSetAuRa contract address.
modifier onlyValidatorSetContract() {
require(msg.sender == address(validatorSetContract));
_;
}
// =============================================== Setters ========================================================
/// @dev Fallback function. Prevents direct sending native coins to this contract.
function () payable external {
revert();
}
/// @dev Adds a new candidate's pool to the list of active pools (see the `getPools` getter) and
/// moves the specified amount of staking tokens/coins from the candidate's staking address
/// to the candidate's pool. A participant calls this function using their staking address when
/// they want to create a pool. This is a wrapper for the `stake` function.
/// @param _amount The amount of tokens to be staked. Ignored when staking in native coins
/// because `msg.value` is used in that case.
/// @param _miningAddress The mining address of the candidate. The mining address is bound to the staking address
/// (msg.sender). This address cannot be equal to `msg.sender`.
function addPool(uint256 _amount, address _miningAddress) external payable {
address stakingAddress = msg.sender;
validatorSetContract.setStakingAddress(_miningAddress, stakingAddress);
_stake(stakingAddress, _amount);
}
/// @dev Adds the `unremovable validator` to either the `poolsToBeElected` or the `poolsToBeRemoved` array
/// depending on their own stake in their own pool when they become removable. This allows the
/// `ValidatorSetAuRa.newValidatorSet` function to recognize the unremovable validator as a regular removable pool.
/// Called by the `ValidatorSet.clearUnremovableValidator` function.
/// @param _unremovableStakingAddress The staking address of the unremovable validator.
function clearUnremovableValidator(address _unremovableStakingAddress) external onlyValidatorSetContract {
require(_unremovableStakingAddress != address(0));
if (stakeAmount[_unremovableStakingAddress][_unremovableStakingAddress] != 0) {
_addPoolToBeElected(_unremovableStakingAddress);
_setLikelihood(_unremovableStakingAddress);
} else {
_addPoolToBeRemoved(_unremovableStakingAddress);
}
}
/// @dev Increments the serial number of the current staking epoch.
/// Called by the `ValidatorSetAuRa.newValidatorSet` at the last block of the finished staking epoch.
function incrementStakingEpoch() external onlyValidatorSetContract {
stakingEpoch++;
}
/// @dev Initializes the network parameters.
/// Can only be called by the constructor of the `InitializerAuRa` contract or owner.
/// @param _validatorSetContract The address of the `ValidatorSetAuRa` contract.
/// @param _initialStakingAddresses The array of initial validators' staking addresses.
/// @param _delegatorMinStake The minimum allowed amount of delegator stake in Wei.
/// @param _candidateMinStake The minimum allowed amount of candidate/validator stake in Wei.
/// @param _stakingEpochDuration The duration of a staking epoch in blocks
/// (e.g., 120954 = 1 week for 5-seconds blocks in AuRa).
/// @param _stakingEpochStartBlock The number of the first block of initial staking epoch
/// (must be zero if the network is starting from genesis block).
/// @param _stakeWithdrawDisallowPeriod The duration period (in blocks) at the end of a staking epoch
/// during which participants cannot stake/withdraw/order/claim their staking tokens/coins
/// (e.g., 4320 = 6 hours for 5-seconds blocks in AuRa).
function initialize(
address _validatorSetContract,
address[] calldata _initialStakingAddresses,
uint256 _delegatorMinStake,
uint256 _candidateMinStake,
uint256 _stakingEpochDuration,
uint256 _stakingEpochStartBlock,
uint256 _stakeWithdrawDisallowPeriod
) external {
require(_validatorSetContract != address(0));
require(_initialStakingAddresses.length > 0);
require(_delegatorMinStake != 0);
require(_candidateMinStake != 0);
require(_stakingEpochDuration != 0);
require(_stakingEpochDuration > _stakeWithdrawDisallowPeriod);
require(_stakeWithdrawDisallowPeriod != 0);
require(_getCurrentBlockNumber() == 0 || msg.sender == _admin());
require(!isInitialized()); // initialization can only be done once
validatorSetContract = IValidatorSetAuRa(_validatorSetContract);
address unremovableStakingAddress = validatorSetContract.unremovableValidator();
for (uint256 i = 0; i < _initialStakingAddresses.length; i++) {
require(_initialStakingAddresses[i] != address(0));
_addPoolActive(_initialStakingAddresses[i], false);
if (_initialStakingAddresses[i] != unremovableStakingAddress) {
_addPoolToBeRemoved(_initialStakingAddresses[i]);
}
}
delegatorMinStake = _delegatorMinStake;
candidateMinStake = _candidateMinStake;
stakingEpochDuration = _stakingEpochDuration;
stakingEpochStartBlock = _stakingEpochStartBlock;
stakeWithdrawDisallowPeriod = _stakeWithdrawDisallowPeriod;
}
/// @dev Makes initial validator stakes. Can only be called by the owner
/// before the network starts (after `initialize` is called but before `stakingEpochStartBlock`).
/// Cannot be called more than once and cannot be called when starting from genesis.
/// Requires `StakingAuRa` contract balance to be equal to the `_totalAmount`.
/// @param _totalAmount The initial validator total stake amount (for all initial validators).
function initialValidatorStake(uint256 _totalAmount) external onlyOwner {
uint256 currentBlock = _getCurrentBlockNumber();
require(stakingEpoch == 0);
require(currentBlock < stakingEpochStartBlock);
require(_thisBalance() == _totalAmount);
require(_totalAmount % _pools.length == 0);
uint256 stakingAmount = _totalAmount.div(_pools.length);
uint256 stakingEpochStartBlock_ = stakingEpochStartBlock;
// Temporarily set `stakingEpochStartBlock` to the current block number
// to avoid revert in the `_stake` function
stakingEpochStartBlock = currentBlock;
for (uint256 i = 0; i < _pools.length; i++) {
address stakingAddress = _pools[i];
require(stakeAmount[stakingAddress][stakingAddress] == 0);
_stake(stakingAddress, stakingAddress, stakingAmount);
emit PlacedStake(stakingAddress, stakingAddress, stakingEpoch, stakingAmount);
}
// Restore `stakingEpochStartBlock` value
stakingEpochStartBlock = stakingEpochStartBlock_;
}
/// @dev Removes a specified pool from the `pools` array (a list of active pools which can be retrieved by the
/// `getPools` getter). Called by the `ValidatorSetAuRa._removeMaliciousValidator` internal function
/// when a pool must be removed by the algorithm.
/// @param _stakingAddress The staking address of the pool to be removed.
function removePool(address _stakingAddress) external onlyValidatorSetContract {
_removePool(_stakingAddress);
}
/// @dev Removes pools which are in the `_poolsToBeRemoved` internal array from the `pools` array.
/// Called by the `ValidatorSetAuRa.newValidatorSet` function when a pool must be removed by the algorithm.
function removePools() external onlyValidatorSetContract {
address[] memory poolsToRemove = _poolsToBeRemoved;
for (uint256 i = 0; i < poolsToRemove.length; i++) {
_removePool(poolsToRemove[i]);
}
}
/// @dev Removes the candidate's or validator's pool from the `pools` array (a list of active pools which
/// can be retrieved by the `getPools` getter). When a candidate or validator wants to remove their pool,
/// they should call this function from their staking address. A validator cannot remove their pool while
/// they are an `unremovable validator`.
function removeMyPool() external gasPriceIsValid onlyInitialized {
address stakingAddress = msg.sender;
address miningAddress = validatorSetContract.miningByStakingAddress(stakingAddress);
// initial validator cannot remove their pool during the initial staking epoch
require(stakingEpoch > 0 || !validatorSetContract.isValidator(miningAddress));
require(stakingAddress != validatorSetContract.unremovableValidator());
_removePool(stakingAddress);
}
/// @dev Sets the number of the first block in the upcoming staking epoch.
/// Called by the `ValidatorSetAuRa.newValidatorSet` function at the last block of a staking epoch.
/// @param _blockNumber The number of the very first block in the upcoming staking epoch.
function setStakingEpochStartBlock(uint256 _blockNumber) external onlyValidatorSetContract {
stakingEpochStartBlock = _blockNumber;
}
/// @dev Moves staking tokens/coins from one pool to another. A staker calls this function when they want
/// to move their tokens/coins from one pool to another without withdrawing their tokens/coins.
/// @param _fromPoolStakingAddress The staking address of the source pool.
/// @param _toPoolStakingAddress The staking address of the target pool.
/// @param _amount The amount of staking tokens/coins to be moved. The amount cannot exceed the value returned
/// by the `maxWithdrawAllowed` getter.
function moveStake(
address _fromPoolStakingAddress,
address _toPoolStakingAddress,
uint256 _amount
) external gasPriceIsValid onlyInitialized {
require(_fromPoolStakingAddress != _toPoolStakingAddress);
address staker = msg.sender;
_withdraw(_fromPoolStakingAddress, staker, _amount);
_stake(_toPoolStakingAddress, staker, _amount);
emit MovedStake(_fromPoolStakingAddress, _toPoolStakingAddress, staker, stakingEpoch, _amount);
}
/// @dev Moves the specified amount of staking tokens/coins from the staker's address to the staking address of
/// the specified pool. Actually, the amount is stored in a balance of this StakingAuRa contract.
/// A staker calls this function when they want to make a stake into a pool.
/// @param _toPoolStakingAddress The staking address of the pool where the tokens should be staked.
/// @param _amount The amount of tokens to be staked. Ignored when staking in native coins
/// because `msg.value` is used instead.
function stake(address _toPoolStakingAddress, uint256 _amount) external payable {
_stake(_toPoolStakingAddress, _amount);
}
/// @dev Moves the specified amount of staking tokens/coins from the staking address of
/// the specified pool to the staker's address. A staker calls this function when they want to withdraw
/// their tokens/coins.
/// @param _fromPoolStakingAddress The staking address of the pool from which the tokens/coins should be withdrawn.
/// @param _amount The amount of tokens/coins to be withdrawn. The amount cannot exceed the value returned
/// by the `maxWithdrawAllowed` getter.
function withdraw(address _fromPoolStakingAddress, uint256 _amount) external gasPriceIsValid onlyInitialized {
address payable staker = msg.sender;
_withdraw(_fromPoolStakingAddress, staker, _amount);
_sendWithdrawnStakeAmount(staker, _amount);
emit WithdrewStake(_fromPoolStakingAddress, staker, stakingEpoch, _amount);
}
/// @dev Orders tokens/coins withdrawal from the staking address of the specified pool to the
/// staker's address. The requested tokens/coins can be claimed after the current staking epoch is complete using
/// the `claimOrderedWithdraw` function.
/// @param _poolStakingAddress The staking address of the pool from which the amount will be withdrawn.
/// @param _amount The amount to be withdrawn. A positive value means the staker wants to either set or
/// increase their withdrawal amount. A negative value means the staker wants to decrease a
/// withdrawal amount that was previously set. The amount cannot exceed the value returned by the
/// `maxWithdrawOrderAllowed` getter.
function orderWithdraw(address _poolStakingAddress, int256 _amount) external gasPriceIsValid onlyInitialized {
require(_poolStakingAddress != address(0));
require(_amount != 0);
address staker = msg.sender;
require(_isWithdrawAllowed(
validatorSetContract.miningByStakingAddress(_poolStakingAddress), staker != _poolStakingAddress
));
uint256 newOrderedAmount = orderedWithdrawAmount[_poolStakingAddress][staker];
uint256 newOrderedAmountTotal = orderedWithdrawAmountTotal[_poolStakingAddress];
uint256 newStakeAmount = stakeAmount[_poolStakingAddress][staker];
uint256 newStakeAmountTotal = stakeAmountTotal[_poolStakingAddress];
if (_amount > 0) {
uint256 amount = uint256(_amount);
// How much can `staker` order for withdrawal from `_poolStakingAddress` at the moment?
require(amount <= maxWithdrawOrderAllowed(_poolStakingAddress, staker));
newOrderedAmount = newOrderedAmount.add(amount);
newOrderedAmountTotal = newOrderedAmountTotal.add(amount);
newStakeAmount = newStakeAmount.sub(amount);
newStakeAmountTotal = newStakeAmountTotal.sub(amount);
orderWithdrawEpoch[_poolStakingAddress][staker] = stakingEpoch;
} else {
uint256 amount = uint256(-_amount);
newOrderedAmount = newOrderedAmount.sub(amount);
newOrderedAmountTotal = newOrderedAmountTotal.sub(amount);
newStakeAmount = newStakeAmount.add(amount);
newStakeAmountTotal = newStakeAmountTotal.add(amount);
}
orderedWithdrawAmount[_poolStakingAddress][staker] = newOrderedAmount;
orderedWithdrawAmountTotal[_poolStakingAddress] = newOrderedAmountTotal;
stakeAmount[_poolStakingAddress][staker] = newStakeAmount;
stakeAmountTotal[_poolStakingAddress] = newStakeAmountTotal;
if (staker == _poolStakingAddress) {
// The amount to be withdrawn must be the whole staked amount or
// must not exceed the diff between the entire amount and `candidateMinStake`
require(newStakeAmount == 0 || newStakeAmount >= candidateMinStake);
address unremovableStakingAddress = validatorSetContract.unremovableValidator();
if (_amount > 0) { // if the validator orders the `_amount` for withdrawal
if (newStakeAmount == 0 && _poolStakingAddress != unremovableStakingAddress) {
// If the removable validator orders their entire stake,
// mark their pool as `to be removed`
_addPoolToBeRemoved(_poolStakingAddress);
}
} else {
// If the validator wants to reduce withdrawal value,
// add their pool as `active` if it hasn't already done
_addPoolActive(_poolStakingAddress, _poolStakingAddress != unremovableStakingAddress);
}
} else {
// The amount to be withdrawn must be the whole staked amount or
// must not exceed the diff between the entire amount and `delegatorMinStake`
require(newStakeAmount == 0 || newStakeAmount >= delegatorMinStake);
if (_amount > 0) { // if the delegator orders the `_amount` for withdrawal
if (newStakeAmount == 0) {
// If the delegator orders their entire stake,
// remove the delegator from delegator list of the pool
_removePoolDelegator(_poolStakingAddress, staker);
}
} else {
// If the delegator wants to reduce withdrawal value,
// add them to delegator list of the pool if it hasn't already done
_addPoolDelegator(_poolStakingAddress, staker);
}
// Remember stake movement to use it later in the `claimReward` function
_snapshotDelegatorStake(_poolStakingAddress, staker);
}
_setLikelihood(_poolStakingAddress);
emit OrderedWithdrawal(_poolStakingAddress, staker, stakingEpoch, _amount);
}
/// @dev Withdraws the staking tokens/coins from the specified pool ordered during the previous staking epochs with
/// the `orderWithdraw` function. The ordered amount can be retrieved by the `orderedWithdrawAmount` getter.
/// @param _poolStakingAddress The staking address of the pool from which the ordered tokens/coins are withdrawn.
function claimOrderedWithdraw(address _poolStakingAddress) external gasPriceIsValid onlyInitialized {
address payable staker = msg.sender;
require(stakingEpoch > orderWithdrawEpoch[_poolStakingAddress][staker]);
require(_isWithdrawAllowed(
validatorSetContract.miningByStakingAddress(_poolStakingAddress), staker != _poolStakingAddress
));
uint256 claimAmount = orderedWithdrawAmount[_poolStakingAddress][staker];
require(claimAmount != 0);
orderedWithdrawAmount[_poolStakingAddress][staker] = 0;
orderedWithdrawAmountTotal[_poolStakingAddress] =
orderedWithdrawAmountTotal[_poolStakingAddress].sub(claimAmount);
if (stakeAmount[_poolStakingAddress][staker] == 0) {
_withdrawCheckPool(_poolStakingAddress, staker);
}
_sendWithdrawnStakeAmount(staker, claimAmount);
emit ClaimedOrderedWithdrawal(_poolStakingAddress, staker, stakingEpoch, claimAmount);
}
/// @dev Sets (updates) the limit of the minimum candidate stake (CANDIDATE_MIN_STAKE).
/// Can only be called by the `owner`.
/// @param _minStake The value of a new limit in Wei.
function setCandidateMinStake(uint256 _minStake) external onlyOwner onlyInitialized {
candidateMinStake = _minStake;
}
/// @dev Sets (updates) the limit of the minimum delegator stake (DELEGATOR_MIN_STAKE).
/// Can only be called by the `owner`.
/// @param _minStake The value of a new limit in Wei.
function setDelegatorMinStake(uint256 _minStake) external onlyOwner onlyInitialized {
delegatorMinStake = _minStake;
}
// =============================================== Getters ========================================================
/// @dev Returns an array of the current active pools (the staking addresses of candidates and validators).
/// The size of the array cannot exceed MAX_CANDIDATES. A pool can be added to this array with the `_addPoolActive`
/// internal function which is called by the `stake` or `orderWithdraw` function. A pool is considered active
/// if its address has at least the minimum stake and this stake is not ordered to be withdrawn.
function getPools() external view returns(address[] memory) {
return _pools;
}
/// @dev Returns an array of the current inactive pools (the staking addresses of former candidates).
/// A pool can be added to this array with the `_addPoolInactive` internal function which is called
/// by `_removePool`. A pool is considered inactive if it is banned for some reason, if its address
/// has zero stake, or if its entire stake is ordered to be withdrawn.
function getPoolsInactive() external view returns(address[] memory) {
return _poolsInactive;
}
/// @dev Returns the array of stake amounts for each corresponding
/// address in the `poolsToBeElected` array (see the `getPoolsToBeElected` getter) and a sum of these amounts.
/// Used by the `ValidatorSetAuRa.newValidatorSet` function when randomly selecting new validators at the last
/// block of a staking epoch. An array value is updated every time any staked amount is changed in this pool
/// (see the `_setLikelihood` internal function).
/// @return `uint256[] likelihoods` - The array of the coefficients. The array length is always equal to the length
/// of the `poolsToBeElected` array.
/// `uint256 sum` - The total sum of the amounts.
function getPoolsLikelihood() external view returns(uint256[] memory likelihoods, uint256 sum) {
return (_poolsLikelihood, _poolsLikelihoodSum);
}
/// @dev Returns the list of pools (their staking addresses) which will participate in a new validator set
/// selection process in the `ValidatorSetAuRa.newValidatorSet` function. This is an array of pools
/// which will be considered as candidates when forming a new validator set (at the last block of a staking epoch).
/// This array is kept updated by the `_addPoolToBeElected` and `_deletePoolToBeElected` internal functions.
function getPoolsToBeElected() external view returns(address[] memory) {
return _poolsToBeElected;
}
/// @dev Returns the list of pools (their staking addresses) which will be removed by the
/// `ValidatorSetAuRa.newValidatorSet` function from the active `pools` array (at the last block
/// of a staking epoch). This array is kept updated by the `_addPoolToBeRemoved`
/// and `_deletePoolToBeRemoved` internal functions. A pool is added to this array when the pool's
/// address withdraws (or orders) all of its own staking tokens from the pool, inactivating the pool.
function getPoolsToBeRemoved() external view returns(address[] memory) {
return _poolsToBeRemoved;
}
/// @dev Returns the list of pools (staking addresses) into which the specified staker have ever staked.
/// @param _staker The staker address (it can be a delegator or a pool staking address itself).
/// @param _offset The index in the array at which the reading should start. Ignored if the `_length` is 0.
/// @param _length The max number of items to return.
function getStakerPools(
address _staker,
uint256 _offset,
uint256 _length
) external view returns(address[] memory result) {
address[] storage stakerPools = _stakerPools[_staker];
if (_length == 0) {
return stakerPools;
}
uint256 maxLength = stakerPools.length.sub(_offset);
result = new address[](_length > maxLength ? maxLength : _length);
for (uint256 i = 0; i < result.length; i++) {
result[i] = stakerPools[_offset + i];
}
}
/// @dev Returns the length of the list of pools into which the specified staker have ever staked.
/// @param _staker The staker address (it can be a delegator or a pool staking address itself).
function getStakerPoolsLength(address _staker) external view returns(uint256) {
return _stakerPools[_staker].length;
}
/// @dev Determines whether staking/withdrawal operations are allowed at the moment.
/// Used by all staking/withdrawal functions.
function areStakeAndWithdrawAllowed() public view returns(bool) {
uint256 currentBlock = _getCurrentBlockNumber();
if (currentBlock < stakingEpochStartBlock) return false;
uint256 allowedDuration = stakingEpochDuration - stakeWithdrawDisallowPeriod;
if (stakingEpochStartBlock == 0) allowedDuration++;
return currentBlock - stakingEpochStartBlock < allowedDuration;
}
/// @dev Returns a boolean flag indicating if the `initialize` function has been called.
function isInitialized() public view returns(bool) {
return validatorSetContract != IValidatorSetAuRa(0);
}
/// @dev Returns a flag indicating whether a specified address is in the `pools` array.
/// See the `getPools` getter.
/// @param _stakingAddress The staking address of the pool.
function isPoolActive(address _stakingAddress) public view returns(bool) {
uint256 index = poolIndex[_stakingAddress];
return index < _pools.length && _pools[index] == _stakingAddress;
}
/// @dev Returns the maximum amount which can be withdrawn from the specified pool by the specified staker
/// at the moment. Used by the `withdraw` and `moveStake` functions.
/// @param _poolStakingAddress The pool staking address from which the withdrawal will be made.
/// @param _staker The staker address that is going to withdraw.
function maxWithdrawAllowed(address _poolStakingAddress, address _staker) public view returns(uint256) {
address miningAddress = validatorSetContract.miningByStakingAddress(_poolStakingAddress);
if (!_isWithdrawAllowed(miningAddress, _poolStakingAddress != _staker)) {
return 0;
}
uint256 canWithdraw = stakeAmount[_poolStakingAddress][_staker];
if (!validatorSetContract.isValidatorOrPending(miningAddress)) {
// The pool is not a validator and is not going to become one,
// so the staker can only withdraw staked amount minus already
// ordered amount
return canWithdraw;
}
// The pool is a validator (active or pending), so the staker can only
// withdraw staked amount minus already ordered amount but
// no more than the amount staked during the current staking epoch
uint256 stakedDuringEpoch = stakeAmountByCurrentEpoch(_poolStakingAddress, _staker);
if (canWithdraw > stakedDuringEpoch) {
canWithdraw = stakedDuringEpoch;
}
return canWithdraw;
}
/// @dev Returns the maximum amount which can be ordered to be withdrawn from the specified pool by the
/// specified staker at the moment. Used by the `orderWithdraw` function.
/// @param _poolStakingAddress The pool staking address from which the withdrawal will be ordered.
/// @param _staker The staker address that is going to order the withdrawal.
function maxWithdrawOrderAllowed(address _poolStakingAddress, address _staker) public view returns(uint256) {
address miningAddress = validatorSetContract.miningByStakingAddress(_poolStakingAddress);
if (!_isWithdrawAllowed(miningAddress, _poolStakingAddress != _staker)) {
return 0;
}
if (!validatorSetContract.isValidatorOrPending(miningAddress)) {
// If the pool is a candidate (not an active validator and not pending one),
// no one can order withdrawal from the `_poolStakingAddress`, but
// anyone can withdraw immediately (see the `maxWithdrawAllowed` getter)
return 0;
}
// If the pool is an active or pending validator, the staker can order withdrawal
// up to their total staking amount minus an already ordered amount
// minus an amount staked during the current staking epoch
return stakeAmount[_poolStakingAddress][_staker].sub(stakeAmountByCurrentEpoch(_poolStakingAddress, _staker));
}
/// @dev Prevents sending tokens directly to the `StakingAuRa` contract address
/// by the `ERC677BridgeTokenRewardable.transferAndCall` function.
function onTokenTransfer(address, uint256, bytes memory) public pure returns(bool) {
revert();
}
/// @dev Returns an array of the current active delegators of the specified pool.
/// A delegator is considered active if they have staked into the specified
/// pool and their stake is not ordered to be withdrawn.
/// @param _poolStakingAddress The pool staking address.
function poolDelegators(address _poolStakingAddress) public view returns(address[] memory) {
return _poolDelegators[_poolStakingAddress];
}
/// @dev Returns an array of the current inactive delegators of the specified pool.
/// A delegator is considered inactive if their entire stake is ordered to be withdrawn
/// but not yet claimed.
/// @param _poolStakingAddress The pool staking address.
function poolDelegatorsInactive(address _poolStakingAddress) public view returns(address[] memory) {
return _poolDelegatorsInactive[_poolStakingAddress];
}
/// @dev Returns the amount of staking tokens/coins staked into the specified pool by the specified staker
/// during the current staking epoch (see the `stakingEpoch` getter).
/// Used by the `stake`, `withdraw`, and `orderWithdraw` functions.
/// @param _poolStakingAddress The pool staking address.
/// @param _staker The staker's address.
function stakeAmountByCurrentEpoch(address _poolStakingAddress, address _staker)
public
view
returns(uint256)
{
return _stakeAmountByEpoch[_poolStakingAddress][_staker][stakingEpoch];
}
/// @dev Returns the number of the last block of the current staking epoch.
function stakingEpochEndBlock() public view returns(uint256) {
uint256 startBlock = stakingEpochStartBlock;
return startBlock + stakingEpochDuration - (startBlock == 0 ? 0 : 1);
}
// ============================================== Internal ========================================================
/// @dev Adds the specified staking address to the array of active pools returned by
/// the `getPools` getter. Used by the `stake`, `addPool`, and `orderWithdraw` functions.
/// @param _stakingAddress The pool added to the array of active pools.
/// @param _toBeElected The boolean flag which defines whether the specified address should be
/// added simultaneously to the `poolsToBeElected` array. See the `getPoolsToBeElected` getter.
function _addPoolActive(address _stakingAddress, bool _toBeElected) internal {
if (!isPoolActive(_stakingAddress)) {
poolIndex[_stakingAddress] = _pools.length;
_pools.push(_stakingAddress);
require(_pools.length <= _getMaxCandidates());
emit AddedPool(_stakingAddress);
}
_removePoolInactive(_stakingAddress);
if (_toBeElected) {
_addPoolToBeElected(_stakingAddress);
}
}
/// @dev Adds the specified staking address to the array of inactive pools returned by
/// the `getPoolsInactive` getter. Used by the `_removePool` internal function.
/// @param _stakingAddress The pool added to the array of inactive pools.
function _addPoolInactive(address _stakingAddress) internal {
uint256 index = poolInactiveIndex[_stakingAddress];
uint256 length = _poolsInactive.length;
if (index >= length || _poolsInactive[index] != _stakingAddress) {
poolInactiveIndex[_stakingAddress] = length;
_poolsInactive.push(_stakingAddress);
}
}
/// @dev Adds the specified staking address to the array of pools returned by the `getPoolsToBeElected`
/// getter. Used by the `_addPoolActive` internal function. See the `getPoolsToBeElected` getter.
/// @param _stakingAddress The pool added to the `poolsToBeElected` array.
function _addPoolToBeElected(address _stakingAddress) internal {
uint256 index = poolToBeElectedIndex[_stakingAddress];
uint256 length = _poolsToBeElected.length;
if (index >= length || _poolsToBeElected[index] != _stakingAddress) {
poolToBeElectedIndex[_stakingAddress] = length;
_poolsToBeElected.push(_stakingAddress);
_poolsLikelihood.push(0); // assumes the likelihood is set with `_setLikelihood` function hereinafter
}
_deletePoolToBeRemoved(_stakingAddress);
}
/// @dev Adds the specified staking address to the array of pools returned by the `getPoolsToBeRemoved`
/// getter. Used by withdrawal functions. See the `getPoolsToBeRemoved` getter.
/// @param _stakingAddress The pool added to the `poolsToBeRemoved` array.
function _addPoolToBeRemoved(address _stakingAddress) internal {
uint256 index = poolToBeRemovedIndex[_stakingAddress];
uint256 length = _poolsToBeRemoved.length;
if (index >= length || _poolsToBeRemoved[index] != _stakingAddress) {
poolToBeRemovedIndex[_stakingAddress] = length;
_poolsToBeRemoved.push(_stakingAddress);
}
_deletePoolToBeElected(_stakingAddress);
}
/// @dev Deletes the specified staking address from the array of pools returned by the
/// `getPoolsToBeElected` getter. Used by the `_addPoolToBeRemoved` and `_removePool` internal functions.
/// See the `getPoolsToBeElected` getter.
/// @param _stakingAddress The pool deleted from the `poolsToBeElected` array.
function _deletePoolToBeElected(address _stakingAddress) internal {
if (_poolsToBeElected.length != _poolsLikelihood.length) return;
uint256 indexToDelete = poolToBeElectedIndex[_stakingAddress];
if (_poolsToBeElected.length > indexToDelete && _poolsToBeElected[indexToDelete] == _stakingAddress) {
if (_poolsLikelihoodSum >= _poolsLikelihood[indexToDelete]) {
_poolsLikelihoodSum -= _poolsLikelihood[indexToDelete];
} else {
_poolsLikelihoodSum = 0;
}
uint256 lastPoolIndex = _poolsToBeElected.length - 1;
address lastPool = _poolsToBeElected[lastPoolIndex];
_poolsToBeElected[indexToDelete] = lastPool;
_poolsLikelihood[indexToDelete] = _poolsLikelihood[lastPoolIndex];
poolToBeElectedIndex[lastPool] = indexToDelete;
poolToBeElectedIndex[_stakingAddress] = 0;
_poolsToBeElected.length--;
_poolsLikelihood.length--;
}
}
/// @dev Deletes the specified staking address from the array of pools returned by the
/// `getPoolsToBeRemoved` getter. Used by the `_addPoolToBeElected` and `_removePool` internal functions.
/// See the `getPoolsToBeRemoved` getter.
/// @param _stakingAddress The pool deleted from the `poolsToBeRemoved` array.
function _deletePoolToBeRemoved(address _stakingAddress) internal {
uint256 indexToDelete = poolToBeRemovedIndex[_stakingAddress];
if (_poolsToBeRemoved.length > indexToDelete && _poolsToBeRemoved[indexToDelete] == _stakingAddress) {
address lastPool = _poolsToBeRemoved[_poolsToBeRemoved.length - 1];
_poolsToBeRemoved[indexToDelete] = lastPool;
poolToBeRemovedIndex[lastPool] = indexToDelete;
poolToBeRemovedIndex[_stakingAddress] = 0;
_poolsToBeRemoved.length--;
}
}
/// @dev Removes the specified staking address from the array of active pools returned by
/// the `getPools` getter. Used by the `removePool`, `removeMyPool`, and withdrawal functions.
/// @param _stakingAddress The pool removed from the array of active pools.
function _removePool(address _stakingAddress) internal {
uint256 indexToRemove = poolIndex[_stakingAddress];
if (_pools.length > indexToRemove && _pools[indexToRemove] == _stakingAddress) {
address lastPool = _pools[_pools.length - 1];
_pools[indexToRemove] = lastPool;
poolIndex[lastPool] = indexToRemove;
poolIndex[_stakingAddress] = 0;
_pools.length--;
emit RemovedPool(_stakingAddress);
}
if (_isPoolEmpty(_stakingAddress)) {
_removePoolInactive(_stakingAddress);
} else {
_addPoolInactive(_stakingAddress);
}
_deletePoolToBeElected(_stakingAddress);
_deletePoolToBeRemoved(_stakingAddress);
}
/// @dev Removes the specified staking address from the array of inactive pools returned by
/// the `getPoolsInactive` getter. Used by withdrawal functions, by the `_addPoolActive` and
/// `_removePool` internal functions.
/// @param _stakingAddress The pool removed from the array of inactive pools.
function _removePoolInactive(address _stakingAddress) internal {
uint256 indexToRemove = poolInactiveIndex[_stakingAddress];
if (_poolsInactive.length > indexToRemove && _poolsInactive[indexToRemove] == _stakingAddress) {
address lastPool = _poolsInactive[_poolsInactive.length - 1];
_poolsInactive[indexToRemove] = lastPool;
poolInactiveIndex[lastPool] = indexToRemove;
poolInactiveIndex[_stakingAddress] = 0;
_poolsInactive.length--;
}
}
/// @dev Adds the specified address to the array of the current active delegators of the specified pool.
/// Used by the `stake` and `orderWithdraw` functions. See the `poolDelegators` getter.
/// @param _poolStakingAddress The pool staking address.
/// @param _delegator The delegator's address.
function _addPoolDelegator(address _poolStakingAddress, address _delegator) internal {
address[] storage delegators = _poolDelegators[_poolStakingAddress];
uint256 index = poolDelegatorIndex[_poolStakingAddress][_delegator];
uint256 length = delegators.length;
if (index >= length || delegators[index] != _delegator) {
poolDelegatorIndex[_poolStakingAddress][_delegator] = length;
delegators.push(_delegator);
}
_removePoolDelegatorInactive(_poolStakingAddress, _delegator);
}
/// @dev Adds the specified address to the array of the current inactive delegators of the specified pool.
/// Used by the `_removePoolDelegator` internal function.
/// @param _poolStakingAddress The pool staking address.
/// @param _delegator The delegator's address.
function _addPoolDelegatorInactive(address _poolStakingAddress, address _delegator) internal {
address[] storage delegators = _poolDelegatorsInactive[_poolStakingAddress];
uint256 index = poolDelegatorInactiveIndex[_poolStakingAddress][_delegator];
uint256 length = delegators.length;
if (index >= length || delegators[index] != _delegator) {
poolDelegatorInactiveIndex[_poolStakingAddress][_delegator] = length;
delegators.push(_delegator);
}
}
/// @dev Removes the specified address from the array of the current active delegators of the specified pool.
/// Used by the withdrawal functions. See the `poolDelegators` getter.
/// @param _poolStakingAddress The pool staking address.
/// @param _delegator The delegator's address.
function _removePoolDelegator(address _poolStakingAddress, address _delegator) internal {
address[] storage delegators = _poolDelegators[_poolStakingAddress];
uint256 indexToRemove = poolDelegatorIndex[_poolStakingAddress][_delegator];
if (delegators.length > indexToRemove && delegators[indexToRemove] == _delegator) {
address lastDelegator = delegators[delegators.length - 1];
delegators[indexToRemove] = lastDelegator;
poolDelegatorIndex[_poolStakingAddress][lastDelegator] = indexToRemove;
poolDelegatorIndex[_poolStakingAddress][_delegator] = 0;
delegators.length--;
}
if (orderedWithdrawAmount[_poolStakingAddress][_delegator] != 0) {
_addPoolDelegatorInactive(_poolStakingAddress, _delegator);
} else {
_removePoolDelegatorInactive(_poolStakingAddress, _delegator);
}
}
/// @dev Removes the specified address from the array of the inactive delegators of the specified pool.
/// Used by the `_addPoolDelegator` and `_removePoolDelegator` internal functions.
/// @param _poolStakingAddress The pool staking address.
/// @param _delegator The delegator's address.
function _removePoolDelegatorInactive(address _poolStakingAddress, address _delegator) internal {
address[] storage delegators = _poolDelegatorsInactive[_poolStakingAddress];
uint256 indexToRemove = poolDelegatorInactiveIndex[_poolStakingAddress][_delegator];
if (delegators.length > indexToRemove && delegators[indexToRemove] == _delegator) {
address lastDelegator = delegators[delegators.length - 1];
delegators[indexToRemove] = lastDelegator;
poolDelegatorInactiveIndex[_poolStakingAddress][lastDelegator] = indexToRemove;
poolDelegatorInactiveIndex[_poolStakingAddress][_delegator] = 0;
delegators.length--;
}
}
function _sendWithdrawnStakeAmount(address payable _to, uint256 _amount) internal;
/// @dev Calculates (updates) the probability of being selected as a validator for the specified pool
/// and updates the total sum of probability coefficients. Actually, the probability is equal to the
/// amount totally staked into the pool. See the `getPoolsLikelihood` getter.
/// Used by the staking and withdrawal functions.
/// @param _poolStakingAddress The address of the pool for which the probability coefficient must be updated.
function _setLikelihood(address _poolStakingAddress) internal {
(bool isToBeElected, uint256 index) = _isPoolToBeElected(_poolStakingAddress);
if (!isToBeElected) return;
uint256 oldValue = _poolsLikelihood[index];
uint256 newValue = stakeAmountTotal[_poolStakingAddress];