-
Notifications
You must be signed in to change notification settings - Fork 49
/
BlockRewardAuRaBase.sol
846 lines (727 loc) · 39.7 KB
/
BlockRewardAuRaBase.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
pragma solidity 0.5.10;
import "../interfaces/IBlockRewardAuRa.sol";
import "../interfaces/IERC677Minting.sol";
import "../interfaces/IRandomAuRa.sol";
import "../interfaces/IStakingAuRa.sol";
import "../interfaces/IValidatorSetAuRa.sol";
import "../upgradeability/UpgradeableOwned.sol";
import "../libs/SafeMath.sol";
contract Sacrifice {
constructor(address payable _recipient) public payable {
selfdestruct(_recipient);
}
}
/// @dev Generates and distributes rewards according to the logic and formulas described in the POSDAO white paper.
contract BlockRewardAuRaBase is UpgradeableOwned, IBlockRewardAuRa {
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!
mapping(address => uint256[]) internal _epochsPoolGotRewardFor;
mapping(address => bool) internal _ercToNativeBridgeAllowed;
address[] internal _ercToNativeBridgesAllowed;
bool internal _queueERInitialized;
uint256 internal _queueERFirst;
uint256 internal _queueERLast;
struct ExtraReceiverQueue {
uint256 amount;
address bridge;
address receiver;
}
mapping(uint256 => ExtraReceiverQueue) internal _queueER;
// Reserved storage space to allow for layout changes in the future.
uint256[25] private ______gapForInternal;
/// @dev A number of blocks produced by the specified validator during the specified staking epoch
/// (beginning from the block when the `finalizeChange` function is called until the latest block
/// of the staking epoch. The results are used by the `_distributeRewards` function to track
/// each validator's downtime (when a validator's node is not running and doesn't produce blocks).
/// While the validator is banned, the block producing statistics is not accumulated for them.
mapping(uint256 => mapping(address => uint256)) public blocksCreated;
/// @dev The current bridge's total fee amount of native coins accumulated by
/// the `addBridgeNativeRewardReceivers` function.
uint256 public bridgeNativeReward;
/// @dev The reward amount to be distributed in native coins among participants (the validator and their
/// delegators) of the specified pool (mining address) for the specified staking epoch.
mapping(uint256 => mapping(address => uint256)) public epochPoolNativeReward;
/// @dev The total amount of native coins minted for the specified address
/// by the `erc-to-native` bridges through the `addExtraReceiver` function.
mapping(address => uint256) public mintedForAccount;
/// @dev The amount of native coins minted at the specified block for the specified
/// address by the `erc-to-native` bridges through the `addExtraReceiver` function.
mapping(address => mapping(uint256 => uint256)) public mintedForAccountInBlock;
/// @dev The total amount of native coins minted at the specified block
/// by the `erc-to-native` bridges through the `addExtraReceiver` function.
mapping(uint256 => uint256) public mintedInBlock;
/// @dev The total amount of native coins minted by the
/// `erc-to-native` bridges through the `addExtraReceiver` function.
uint256 public mintedTotally;
/// @dev The total amount of native coins minted by the specified
/// `erc-to-native` bridge through the `addExtraReceiver` function.
mapping(address => uint256) public mintedTotallyByBridge;
/// @dev The total reward amount in native coins which is not yet distributed among pools.
uint256 public nativeRewardUndistributed;
/// @dev The total amount staked into the specified pool (mining address)
/// before the specified staking epoch. Filled by the `_snapshotPoolStakeAmounts` function.
mapping(uint256 => mapping(address => uint256)) public snapshotPoolTotalStakeAmount;
/// @dev The validator's amount staked into the specified pool (mining address)
/// before the specified staking epoch. Filled by the `_snapshotPoolStakeAmounts` function.
mapping(uint256 => mapping(address => uint256)) public snapshotPoolValidatorStakeAmount;
/// @dev The validator's min reward percent which was actual at the specified staking epoch.
/// This percent is taken from the VALIDATOR_MIN_REWARD_PERCENT constant and saved for every staking epoch
/// by the `reward` function. Used by the `delegatorShare` and `validatorShare` public getters.
/// This is needed to have an ability to change validator's min reward percent in the VALIDATOR_MIN_REWARD_PERCENT
/// constant by upgrading the contract.
mapping(uint256 => uint256) public validatorMinRewardPercent;
/// @dev The address of the `ValidatorSet` contract.
IValidatorSetAuRa public validatorSetContract;
// Reserved storage space to allow for layout changes in the future.
uint256[25] private ______gapForPublic;
// ================================================ Events ========================================================
/// @dev Emitted by the `addExtraReceiver` function.
/// @param amount The amount of native coins which must be minted for the `receiver` by the `erc-to-native`
/// `bridge` with the `reward` function.
/// @param receiver The address for which the `amount` of native coins must be minted.
/// @param bridge The bridge address which called the `addExtraReceiver` function.
event AddedReceiver(uint256 amount, address indexed receiver, address indexed bridge);
/// @dev Emitted by the `addBridgeNativeRewardReceivers` function.
/// @param amount The fee amount in native coins passed to the
/// `addBridgeNativeRewardReceivers` function as a parameter.
/// @param cumulativeAmount The value of `bridgeNativeReward` state variable
/// after adding the `amount` to it.
/// @param bridge The bridge address which called the `addBridgeNativeRewardReceivers` function.
event BridgeNativeRewardAdded(uint256 amount, uint256 cumulativeAmount, address indexed bridge);
/// @dev Emitted by the `_mintNativeCoins` function which is called by the `reward` function.
/// This event is only used by the unit tests because the `reward` function cannot emit events.
/// @param receivers The array of receiver addresses for which native coins are minted. The length of this
/// array is equal to the length of the `rewards` array.
/// @param rewards The array of amounts minted for the relevant `receivers`. The length of this array
/// is equal to the length of the `receivers` array.
event MintedNative(address[] receivers, uint256[] rewards);
// ============================================== Modifiers =======================================================
/// @dev Ensures the caller is the `erc-to-native` bridge contract address.
modifier onlyErcToNativeBridge {
require(_ercToNativeBridgeAllowed[msg.sender]);
_;
}
/// @dev Ensures the `initialize` function was called before.
modifier onlyInitialized {
require(isInitialized());
_;
}
/// @dev Ensures the caller is the SYSTEM_ADDRESS. See https://wiki.parity.io/Block-Reward-Contract.html
modifier onlySystem {
require(msg.sender == 0xffffFFFfFFffffffffffffffFfFFFfffFFFfFFfE);
_;
}
/// @dev Ensures the caller is the StakingAuRa contract address.
modifier onlyStakingContract() {
require(msg.sender == address(validatorSetContract.stakingContract()));
_;
}
/// @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 Called by the `erc-to-native` bridge contract when a portion of the bridge fee should be minted
/// and distributed to participants (validators and their delegators) in native coins. The specified amount
/// is used by the `_distributeRewards` function.
/// @param _amount The fee amount distributed to participants.
function addBridgeNativeRewardReceivers(uint256 _amount) external onlyErcToNativeBridge {
require(_amount != 0);
bridgeNativeReward = bridgeNativeReward.add(_amount);
emit BridgeNativeRewardAdded(_amount, bridgeNativeReward, msg.sender);
}
/// @dev Called by the `erc-to-native` bridge contract when the bridge needs to mint a specified amount of native
/// coins for a specified address using the `reward` function.
/// @param _amount The amount of native coins which must be minted for the `_receiver` address.
/// @param _receiver The address for which the `_amount` of native coins must be minted.
function addExtraReceiver(uint256 _amount, address _receiver) external onlyErcToNativeBridge {
require(_amount != 0);
require(_queueERInitialized);
_enqueueExtraReceiver(_amount, _receiver, msg.sender);
emit AddedReceiver(_amount, _receiver, msg.sender);
}
/// @dev Called by the `ValidatorSetAuRa.finalizeChange` to clear the values in
/// the `blocksCreated` mapping for the current staking epoch and a new validator set.
function clearBlocksCreated() external onlyValidatorSetContract {
IStakingAuRa stakingContract = IStakingAuRa(validatorSetContract.stakingContract());
uint256 stakingEpoch = stakingContract.stakingEpoch();
address[] memory validators = validatorSetContract.getValidators();
for (uint256 i = 0; i < validators.length; i++) {
blocksCreated[stakingEpoch][validators[i]] = 0;
}
}
/// @dev Initializes the contract at network startup.
/// Can only be called by the constructor of the `InitializerAuRa` contract or owner.
/// @param _validatorSet The address of the `ValidatorSetAuRa` contract.
function initialize(address _validatorSet) external {
require(_getCurrentBlockNumber() == 0 || msg.sender == _admin());
require(!isInitialized());
require(_validatorSet != address(0));
validatorSetContract = IValidatorSetAuRa(_validatorSet);
validatorMinRewardPercent[0] = VALIDATOR_MIN_REWARD_PERCENT;
}
/// @dev Copies the minting statistics from the previous BlockReward contract
/// for the `mintedTotally` and `mintedTotallyByBridge` getters. Can only be called once by the owner.
/// This function assumes the bridge contract address is not changed due to its upgradable nature.
/// @param _bridge The address of a bridge contract.
/// @param _prevBlockRewardContract The address of the previous BlockReward contract.
function migrateMintingStatistics(address _bridge, IBlockRewardAuRa _prevBlockRewardContract) external onlyOwner {
require(mintedTotally == 0);
uint256 prevMinted = _prevBlockRewardContract.mintedTotally();
uint256 prevMintedByBridge = _prevBlockRewardContract.mintedTotallyByBridge(_bridge);
require(prevMinted != 0);
require(prevMintedByBridge != 0);
mintedTotally = prevMinted;
mintedTotallyByBridge[_bridge] = prevMintedByBridge;
}
/// @dev Called by the validator's node when producing and closing a block,
/// see https://wiki.parity.io/Block-Reward-Contract.html.
/// This function performs all of the automatic operations needed for controlling numbers revealing by validators,
/// accumulating block producing statistics, starting a new staking epoch, snapshotting staking amounts
/// for the upcoming staking epoch, rewards distributing at the end of a staking epoch, and minting
/// native coins needed for the `erc-to-native` bridge.
function reward(address[] calldata benefactors, uint16[] calldata kind)
external
onlySystem
returns(address[] memory receiversNative, uint256[] memory rewardsNative)
{
if (benefactors.length != kind.length || benefactors.length != 1 || kind[0] != 0) {
return (new address[](0), new uint256[](0));
}
// Check if the validator is existed
if (!validatorSetContract.isValidator(benefactors[0])) {
return (new address[](0), new uint256[](0));
}
// Check the current validators at the end of each collection round whether
// they revealed their numbers, and remove a validator as a malicious if needed
IRandomAuRa(validatorSetContract.randomContract()).onFinishCollectRound();
// Initialize the extra receivers queue
if (!_queueERInitialized) {
_queueERFirst = 1;
_queueERLast = 0;
_queueERInitialized = true;
}
uint256 bridgeQueueLimit = 100;
IStakingAuRa stakingContract = IStakingAuRa(validatorSetContract.stakingContract());
uint256 stakingEpoch = stakingContract.stakingEpoch();
uint256 stakingEpochEndBlock = stakingContract.stakingEpochEndBlock();
uint256 nativeTotalRewardAmount = 0;
if (validatorSetContract.validatorSetApplyBlock() != 0) {
if (stakingEpoch != 0 && !validatorSetContract.isValidatorBanned(benefactors[0])) {
// Accumulate blocks producing statistics for each of the
// active validators during the current staking epoch. This
// statistics is used by the `_distributeRewards` function
blocksCreated[stakingEpoch][benefactors[0]]++;
}
}
if (_getCurrentBlockNumber() == stakingEpochEndBlock) {
// Distribute rewards among validator pools
if (stakingEpoch != 0) {
nativeTotalRewardAmount = _distributeRewards(
stakingContract,
stakingEpoch,
stakingEpochEndBlock
);
}
// Choose new validators
validatorSetContract.newValidatorSet();
// Snapshot total amounts staked into the pools
uint256 i;
uint256 nextStakingEpoch = stakingEpoch + 1;
address[] memory miningAddresses;
// We need to remember the total staked amounts for the pending addresses
// for the possible case when these pending addresses are finalized
// by the `ValidatorSetAuRa.finalizeChange` function and thus become validators
miningAddresses = validatorSetContract.getPendingValidators();
for (i = 0; i < miningAddresses.length; i++) {
_snapshotPoolStakeAmounts(stakingContract, nextStakingEpoch, miningAddresses[i]);
}
// We need to remember the total staked amounts for the current validators
// for the possible case when these validators continue to be validators
// throughout the upcoming staking epoch (if the new validator set is not finalized
// for some reason)
miningAddresses = validatorSetContract.getValidators();
for (i = 0; i < miningAddresses.length; i++) {
_snapshotPoolStakeAmounts(stakingContract, nextStakingEpoch, miningAddresses[i]);
}
// We need to remember the total staked amounts for the addresses currently
// being finalized but not yet finalized (i.e. the `InitiateChange` event is emitted
// for them but not yet handled by validator nodes thus the `ValidatorSetAuRa.finalizeChange`
// function is not called yet) for the possible case when these addresses finally
// become validators on the upcoming staking epoch
(miningAddresses, ) = validatorSetContract.validatorsToBeFinalized();
for (i = 0; i < miningAddresses.length; i++) {
_snapshotPoolStakeAmounts(stakingContract, nextStakingEpoch, miningAddresses[i]);
}
// Remember validator's min reward percent for the upcoming staking epoch
validatorMinRewardPercent[nextStakingEpoch] = VALIDATOR_MIN_REWARD_PERCENT;
// Pause bridge for this block
bridgeQueueLimit = 0;
}
// Mint native coins if needed
return _mintNativeCoins(nativeTotalRewardAmount, bridgeQueueLimit);
}
/// @dev Sets the array of `erc-to-native` bridge addresses which are allowed to call some of the functions with
/// the `onlyErcToNativeBridge` modifier. This setter can only be called by the `owner`.
/// @param _bridgesAllowed The array of bridge addresses.
function setErcToNativeBridgesAllowed(address[] calldata _bridgesAllowed) external onlyOwner onlyInitialized {
uint256 i;
for (i = 0; i < _ercToNativeBridgesAllowed.length; i++) {
_ercToNativeBridgeAllowed[_ercToNativeBridgesAllowed[i]] = false;
}
_ercToNativeBridgesAllowed = _bridgesAllowed;
for (i = 0; i < _bridgesAllowed.length; i++) {
_ercToNativeBridgeAllowed[_bridgesAllowed[i]] = true;
}
}
// =============================================== Getters ========================================================
/// @dev Returns an identifier for the bridge contract so that the latter could
/// ensure it works with the BlockReward contract.
function blockRewardContractId() public pure returns(bytes4) {
return 0x0d35a7ca; // bytes4(keccak256("blockReward"))
}
/// @dev Returns an array of epoch numbers for which the specified pool (mining address)
/// got a non-zero reward.
function epochsPoolGotRewardFor(address _miningAddress) public view returns(uint256[] memory) {
return _epochsPoolGotRewardFor[_miningAddress];
}
/// @dev Returns the array of `erc-to-native` bridge addresses set by the `setErcToNativeBridgesAllowed` setter.
function ercToNativeBridgesAllowed() public view returns(address[] memory) {
return _ercToNativeBridgesAllowed;
}
/// @dev Returns the current size of the address queue created by the `addExtraReceiver` function.
function extraReceiversQueueSize() public view returns(uint256) {
return _queueERLast + 1 - _queueERFirst;
}
/// @dev Returns a boolean flag indicating if the `initialize` function has been called.
function isInitialized() public view returns(bool) {
return validatorSetContract != IValidatorSetAuRa(0);
}
/// @dev Prevents sending tokens directly to the `BlockRewardAuRa` contract address
/// by the `ERC677BridgeTokenRewardable.transferAndCall` function.
function onTokenTransfer(address, uint256, bytes memory) public pure returns(bool) {
revert();
}
/// @dev Returns an array of epoch numbers for which the specified staker
/// can claim a reward from the specified pool by the `StakingAuRa.claimReward` function.
/// @param _poolStakingAddress The pool staking address.
/// @param _staker The staker's address (delegator or candidate/validator).
function epochsToClaimRewardFrom(
address _poolStakingAddress,
address _staker
) public view returns(uint256[] memory epochsToClaimFrom) {
address miningAddress = validatorSetContract.miningByStakingAddress(_poolStakingAddress);
IStakingAuRa stakingContract = IStakingAuRa(validatorSetContract.stakingContract());
bool isDelegator = _poolStakingAddress != _staker;
uint256 firstEpoch;
uint256 lastEpoch;
if (isDelegator) {
firstEpoch = stakingContract.stakeFirstEpoch(_poolStakingAddress, _staker);
if (firstEpoch == 0) {
return (new uint256[](0));
}
lastEpoch = stakingContract.stakeLastEpoch(_poolStakingAddress, _staker);
}
uint256[] storage epochs = _epochsPoolGotRewardFor[miningAddress];
uint256 length = epochs.length;
uint256[] memory tmp = new uint256[](length);
uint256 tmpLength = 0;
uint256 i;
for (i = 0; i < length; i++) {
uint256 epoch = epochs[i];
if (isDelegator) {
if (epoch < firstEpoch) {
// If the delegator staked for the first time before
// the `epoch`, skip this staking epoch
continue;
}
if (lastEpoch <= epoch && lastEpoch != 0) {
// If the delegator withdrew all their stake before the `epoch`,
// don't check this and following epochs since it makes no sense
break;
}
}
if (!stakingContract.rewardWasTaken(_poolStakingAddress, _staker, epoch)) {
tmp[tmpLength++] = epoch;
}
}
epochsToClaimFrom = new uint256[](tmpLength);
for (i = 0; i < tmpLength; i++) {
epochsToClaimFrom[i] = tmp[i];
}
}
/// @dev Returns the reward coefficient for the specified validator. The given value should be divided by 10000
/// to get the value of the reward percent (since EVM doesn't support float values). If the specified staking
/// address is an address of a candidate that is not about to be a validator on the current staking epoch
/// the potentially possible reward coefficient is returned.
/// @param _stakingAddress The staking address of the validator/candidate
/// pool for which the getter must return the coefficient.
function validatorRewardPercent(address _stakingAddress) public view returns(uint256) {
IStakingAuRa stakingContract = IStakingAuRa(validatorSetContract.stakingContract());
uint256 stakingEpoch = stakingContract.stakingEpoch();
if (stakingEpoch == 0) {
// No one gets a reward for the initial staking epoch, so we return zero
return 0;
}
address miningAddress = validatorSetContract.miningByStakingAddress(_stakingAddress);
if (validatorSetContract.isValidator(miningAddress)) {
// For the validator we return the coefficient based on
// snapshotted total amounts
return validatorShare(
stakingEpoch,
snapshotPoolValidatorStakeAmount[stakingEpoch][miningAddress],
snapshotPoolTotalStakeAmount[stakingEpoch][miningAddress],
REWARD_PERCENT_MULTIPLIER
);
}
if (validatorSetContract.validatorSetApplyBlock() == 0) {
// For the candidate that is about to be a validator on the current
// staking epoch we return the coefficient based on snapshotted total amounts
address[] memory miningAddresses;
uint256 i;
miningAddresses = validatorSetContract.getPendingValidators();
for (i = 0; i < miningAddresses.length; i++) {
if (miningAddress == miningAddresses[i]) {
return validatorShare(
stakingEpoch,
snapshotPoolValidatorStakeAmount[stakingEpoch][miningAddress],
snapshotPoolTotalStakeAmount[stakingEpoch][miningAddress],
REWARD_PERCENT_MULTIPLIER
);
}
}
(miningAddresses, ) = validatorSetContract.validatorsToBeFinalized();
for (i = 0; i < miningAddresses.length; i++) {
if (miningAddress == miningAddresses[i]) {
return validatorShare(
stakingEpoch,
snapshotPoolValidatorStakeAmount[stakingEpoch][miningAddress],
snapshotPoolTotalStakeAmount[stakingEpoch][miningAddress],
REWARD_PERCENT_MULTIPLIER
);
}
}
}
// For the candidate that is not about to be a validator on the current staking epoch,
// we return the potentially possible reward coefficient
return validatorShare(
stakingEpoch,
stakingContract.stakeAmount(_stakingAddress, _stakingAddress),
stakingContract.stakeAmountTotal(_stakingAddress),
REWARD_PERCENT_MULTIPLIER
);
}
/// @dev Calculates delegator's share for the given pool reward amount and the specified staking epoch.
/// Used by the `StakingAuRa.claimReward` function.
/// @param _stakingEpoch The number of staking epoch.
/// @param _delegatorStaked The amount staked by a delegator.
/// @param _validatorStaked The amount staked by a validator.
/// @param _totalStaked The total amount staked by a validator and their delegators.
/// @param _poolReward The value of pool reward.
function delegatorShare(
uint256 _stakingEpoch,
uint256 _delegatorStaked,
uint256 _validatorStaked,
uint256 _totalStaked,
uint256 _poolReward
) public view returns(uint256) {
if (_delegatorStaked == 0 || _validatorStaked == 0 || _totalStaked == 0) {
return 0;
}
uint256 share = 0;
uint256 delegatorsStaked = _totalStaked >= _validatorStaked ? _totalStaked - _validatorStaked : 0;
uint256 validatorMinPercent = validatorMinRewardPercent[_stakingEpoch];
if (_validatorStaked * (100 - validatorMinPercent) > delegatorsStaked * validatorMinPercent) {
// Validator has more than validatorMinPercent %
share = _poolReward * _delegatorStaked / _totalStaked;
} else {
// Validator has validatorMinPercent %
share = _poolReward * _delegatorStaked * (100 - validatorMinPercent) / (delegatorsStaked * 100);
}
return share;
}
/// @dev Calculates validator's share for the given pool reward amount and the specified staking epoch.
/// Used by the `validatorRewardPercent` and `StakingAuRa.claimReward` functions.
/// @param _stakingEpoch The number of staking epoch.
/// @param _validatorStaked The amount staked by a validator.
/// @param _totalStaked The total amount staked by a validator and their delegators.
/// @param _poolReward The value of pool reward.
function validatorShare(
uint256 _stakingEpoch,
uint256 _validatorStaked,
uint256 _totalStaked,
uint256 _poolReward
) public view returns(uint256) {
if (_validatorStaked == 0 || _totalStaked == 0) {
return 0;
}
uint256 share = 0;
uint256 delegatorsStaked = _totalStaked >= _validatorStaked ? _totalStaked - _validatorStaked : 0;
uint256 validatorMinPercent = validatorMinRewardPercent[_stakingEpoch];
if (_validatorStaked * (100 - validatorMinPercent) > delegatorsStaked * validatorMinPercent) {
// Validator has more than validatorMinPercent %
share = _poolReward * _validatorStaked / _totalStaked;
} else {
// Validator has validatorMinPercent %
share = _poolReward * validatorMinPercent / 100;
}
return share;
}
// ============================================== Internal ========================================================
uint256 internal constant VALIDATOR_MIN_REWARD_PERCENT = 30; // 30%
uint256 internal constant REWARD_PERCENT_MULTIPLIER = 1000000;
function _coinInflationAmount(uint256, address[] memory) internal view returns(uint256);
/// @dev Distributes rewards in native coins among pools at the latest block of a staking epoch.
/// This function is called by the `_distributeRewards` function.
/// @param _stakingEpoch The number of the current staking epoch.
/// @param _totalRewardShareNum Numerator of the total reward share.
/// @param _totalRewardShareDenom Denominator of the total reward share.
/// @param _validators The array of the current validators (their mining addresses).
/// @param _blocksCreatedShareNum Numerators of blockCreated share for each of the validators.
/// @param _blocksCreatedShareDenom Denominator of blockCreated share.
/// @return Returns the amount of native coins which need to be minted.
function _distributeNativeRewards(
uint256 _stakingEpoch,
uint256 _totalRewardShareNum,
uint256 _totalRewardShareDenom,
address[] memory _validators,
uint256[] memory _blocksCreatedShareNum,
uint256 _blocksCreatedShareDenom
) internal returns(uint256) {
uint256 totalReward = bridgeNativeReward + nativeRewardUndistributed;
totalReward += _coinInflationAmount(_stakingEpoch, _validators);
if (totalReward == 0) {
return 0;
}
bridgeNativeReward = 0;
uint256 rewardToDistribute = 0;
uint256 distributedAmount = 0;
if (_blocksCreatedShareDenom != 0 && _totalRewardShareDenom != 0) {
rewardToDistribute = totalReward * _totalRewardShareNum / _totalRewardShareDenom;
if (rewardToDistribute != 0) {
for (uint256 i = 0; i < _validators.length; i++) {
uint256 poolReward =
rewardToDistribute * _blocksCreatedShareNum[i] / _blocksCreatedShareDenom;
epochPoolNativeReward[_stakingEpoch][_validators[i]] = poolReward;
distributedAmount += poolReward;
if (poolReward != 0) {
_epochsPoolGotRewardFor[_validators[i]].push(_stakingEpoch);
}
}
}
}
nativeRewardUndistributed = totalReward - distributedAmount;
return distributedAmount;
}
function _distributeTokenRewards(
address, uint256, uint256, uint256, address[] memory, uint256[] memory, uint256
) internal;
/// @dev Distributes rewards among pools at the latest block of a staking epoch.
/// This function is called by the `reward` function.
/// @param _stakingContract The address of the StakingAuRa contract.
/// @param _stakingEpoch The number of the current staking epoch.
/// @param _stakingEpochEndBlock The number of the latest block of the current staking epoch.
/// @return Returns the reward amount in native coins needed to be minted
/// and accrued to the balance of this contract.
function _distributeRewards(
IStakingAuRa _stakingContract,
uint256 _stakingEpoch,
uint256 _stakingEpochEndBlock
) internal returns(uint256 nativeTotalRewardAmount) {
address[] memory validators = validatorSetContract.getValidators();
// Determine shares
uint256 totalRewardShareNum = 0;
uint256 totalRewardShareDenom = 1;
uint256 realFinalizeBlock = validatorSetContract.validatorSetApplyBlock();
if (realFinalizeBlock != 0) {
uint256 idealFinalizeBlock =
_stakingContract.stakingEpochStartBlock() + validatorSetContract.MAX_VALIDATORS()*2/3 + 1;
if (realFinalizeBlock < idealFinalizeBlock) {
realFinalizeBlock = idealFinalizeBlock;
}
totalRewardShareNum = _stakingEpochEndBlock - realFinalizeBlock + 1;
totalRewardShareDenom = _stakingEpochEndBlock - idealFinalizeBlock + 1;
}
uint256[] memory blocksCreatedShareNum = new uint256[](validators.length);
uint256 blocksCreatedShareDenom = 0;
if (totalRewardShareNum != 0) {
for (uint256 i = 0; i < validators.length; i++) {
if (
!validatorSetContract.isValidatorBanned(validators[i]) &&
snapshotPoolValidatorStakeAmount[_stakingEpoch][validators[i]] != 0
) {
blocksCreatedShareNum[i] = blocksCreated[_stakingEpoch][validators[i]];
} else {
blocksCreatedShareNum[i] = 0;
}
blocksCreatedShareDenom += blocksCreatedShareNum[i];
}
}
// Distribute native coins among pools
nativeTotalRewardAmount = _distributeNativeRewards(
_stakingEpoch,
totalRewardShareNum,
totalRewardShareDenom,
validators,
blocksCreatedShareNum,
blocksCreatedShareDenom
);
// Distribute ERC tokens among pools
_distributeTokenRewards(
address(_stakingContract),
_stakingEpoch,
totalRewardShareNum,
totalRewardShareDenom,
validators,
blocksCreatedShareNum,
blocksCreatedShareDenom
);
}
/// @dev Returns the current block number. Needed mostly for unit tests.
function _getCurrentBlockNumber() internal view returns(uint256) {
return block.number;
}
/// @dev Calculates and returns inflation amount based on the specified
/// staking epoch, validator set, and inflation rate.
/// Used by `_coinInflationAmount` and `_distributeTokenRewards` functions.
/// @param _stakingEpoch The number of the current staking epoch.
/// @param _validators The array of the current validators (their mining addresses).
/// @param _inflationRate Inflation rate.
function _inflationAmount(
uint256 _stakingEpoch,
address[] memory _validators,
uint256 _inflationRate
) internal view returns(uint256) {
if (_inflationRate == 0) return 0;
uint256 snapshotTotalStakeAmount = 0;
for (uint256 i = 0; i < _validators.length; i++) {
snapshotTotalStakeAmount += snapshotPoolTotalStakeAmount[_stakingEpoch][_validators[i]];
}
return snapshotTotalStakeAmount * _inflationRate / 1 ether;
}
/// @dev Joins two native coin receiver elements into a single set and returns the result
/// to the `reward` function: the first element comes from the `erc-to-native` bridge fee distribution,
/// the second - from the `erc-to-native` bridge when native coins are minted for the specified addresses.
/// Dequeues the addresses enqueued with the `addExtraReceiver` function by the `erc-to-native` bridge.
/// Accumulates minting statistics for the `erc-to-native` bridges.
/// @param _nativeTotalRewardAmount The native coins amount which should be accrued to the balance
/// of this contract (as a total reward for the finished staking epoch).
/// @param _queueLimit Max number of addresses which can be dequeued from the queue formed by the
/// `addExtraReceiver` function.
function _mintNativeCoins(
uint256 _nativeTotalRewardAmount,
uint256 _queueLimit
)
internal
returns(address[] memory receivers, uint256[] memory rewards)
{
uint256 extraLength = extraReceiversQueueSize();
if (extraLength > _queueLimit) {
extraLength = _queueLimit;
}
bool totalRewardNotEmpty = _nativeTotalRewardAmount != 0;
receivers = new address[](extraLength + (totalRewardNotEmpty ? 1 : 0));
rewards = new uint256[](receivers.length);
for (uint256 i = 0; i < extraLength; i++) {
(uint256 amount, address receiver, address bridge) = _dequeueExtraReceiver();
receivers[i] = receiver;
rewards[i] = amount;
_setMinted(amount, receiver, bridge);
}
if (totalRewardNotEmpty) {
receivers[extraLength] = address(this);
rewards[extraLength] = _nativeTotalRewardAmount;
}
emit MintedNative(receivers, rewards);
return (receivers, rewards);
}
/// @dev Dequeues the information about the native coins receiver enqueued with the `addExtraReceiver`
/// function by the `erc-to-native` bridge. This function is used by `_mintNativeCoins`.
/// @return `uint256 amount` - The amount to be minted for the `receiver` address.
/// `address receiver` - The address for which the `amount` is minted.
/// `address bridge` - The address of the bridge contract which called the `addExtraReceiver` function.
function _dequeueExtraReceiver() internal returns(uint256 amount, address receiver, address bridge) {
uint256 queueFirst = _queueERFirst;
uint256 queueLast = _queueERLast;
if (queueLast < queueFirst) {
amount = 0;
receiver = address(0);
bridge = address(0);
} else {
amount = _queueER[queueFirst].amount;
receiver = _queueER[queueFirst].receiver;
bridge = _queueER[queueFirst].bridge;
delete _queueER[queueFirst];
_queueERFirst++;
}
}
/// @dev Enqueues the information about the receiver of native coins which must be minted for the
/// specified `erc-to-native` bridge. This function is used by the `addExtraReceiver` function.
/// @param _amount The amount of native coins which must be minted for the `_receiver` address.
/// @param _receiver The address for which the `_amount` of native coins must be minted.
/// @param _bridge The address of the bridge contract which requested the minting of native coins.
function _enqueueExtraReceiver(uint256 _amount, address _receiver, address _bridge) internal {
uint256 queueLast = _queueERLast + 1;
_queueER[queueLast] = ExtraReceiverQueue({
amount: _amount,
bridge: _bridge,
receiver: _receiver
});
_queueERLast = queueLast;
}
/// @dev Accumulates minting statistics for the `erc-to-native` bridge.
/// This function is used by the `_mintNativeCoins` function.
/// @param _amount The amount minted for the `_account` address.
/// @param _account The address for which the `_amount` is minted.
/// @param _bridge The address of the bridge contract which called the `addExtraReceiver` function.
function _setMinted(uint256 _amount, address _account, address _bridge) internal {
uint256 blockNumber = _getCurrentBlockNumber();
mintedForAccountInBlock[_account][blockNumber] = _amount;
mintedForAccount[_account] += _amount;
mintedInBlock[blockNumber] += _amount;
mintedTotallyByBridge[_bridge] += _amount;
mintedTotally += _amount;
}
/// @dev Makes snapshots of total amount staked into the specified pool
/// before the specified staking epoch. Used by the `reward` function.
/// @param _stakingContract The address of the `StakingAuRa` contract.
/// @param _stakingEpoch The number of upcoming staking epoch.
/// @param _miningAddress The mining address of the pool.
function _snapshotPoolStakeAmounts(
IStakingAuRa _stakingContract,
uint256 _stakingEpoch,
address _miningAddress
) internal {
if (snapshotPoolTotalStakeAmount[_stakingEpoch][_miningAddress] != 0) {
return;
}
address stakingAddress = validatorSetContract.stakingByMiningAddress(_miningAddress);
uint256 totalAmount = _stakingContract.stakeAmountTotal(stakingAddress);
if (totalAmount == 0) {
return;
}
snapshotPoolTotalStakeAmount[_stakingEpoch][_miningAddress] = totalAmount;
snapshotPoolValidatorStakeAmount[_stakingEpoch][_miningAddress] =
_stakingContract.stakeAmount(stakingAddress, stakingAddress);
}
/// @dev Called by the `transferReward` of a child contract to transfer native coins
/// from the balance of the `BlockRewardAuRa` contract to the specified address as a reward.
/// @param _amount The amount of native coins to transfer as a reward.
/// @param _to The target address to transfer the amounts to.
function _transferNativeReward(uint256 _amount, address payable _to) internal {
if (_amount != 0 && !_to.send(_amount)) {
// We use the `Sacrifice` trick to be sure the coins can be 100% sent to the receiver.
// Otherwise, if the receiver is a contract which has a revert in its fallback function,
// the sending will fail.
(new Sacrifice).value(_amount)(_to);
}
}
}