Skip to content

Commit e014b23

Browse files
Automated rewards and calculation (#267)
* automatic rewards calculations changes * supply calculation * more tests * update FlowEpoch to depend on FlowFees This temporarily breaks the tests which pull in flow-go, because flow-go also depends on the contracts module * update flow-go version * fix array ordering tests * update flow-go dependency * go mod tidy Co-authored-by: Jordan Schalm <[email protected]>
1 parent b4a009c commit e014b23

18 files changed

+757
-227
lines changed

contracts/epochs/FlowEpoch.cdc

+44-16
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import FlowToken from 0xFLOWTOKENADDRESS
33
import FlowIDTableStaking from 0xFLOWIDTABLESTAKINGADDRESS
44
import FlowClusterQC from 0xQCADDRESS
55
import FlowDKG from 0xDKGADDRESS
6+
import FlowFees from 0xFLOWFEESADDRESS
67

78
// The top-level smart contract managing the lifecycle of epochs. In Flow,
89
// epochs are the smallest unit of time where the identity table (the set of
@@ -212,8 +213,8 @@ pub contract FlowEpoch {
212213
/// The number of collector clusters in each epoch
213214
pub(set) var numCollectorClusters: UInt16
214215

215-
/// Tracks the annualized percentage of FLOW total supply that is minted as rewards at the end of an epoch
216-
/// Calculation for a single epoch would be (totalSupply * FLOWsupplyIncreasePercentage) / 52
216+
/// Tracks the rate at which the rewards payout increases every epoch
217+
/// This value is multiplied by the FLOW total supply to get the next payout
217218
pub(set) var FLOWsupplyIncreasePercentage: UFix64
218219

219220
init(numViewsInEpoch: UInt64, numViewsInStakingAuction: UInt64, numViewsInDKGPhase: UInt64, numCollectorClusters: UInt16, FLOWsupplyIncreasePercentage: UFix64) {
@@ -331,6 +332,12 @@ pub contract FlowEpoch {
331332

332333
FlowEpoch.configurableMetadata.FLOWsupplyIncreasePercentage = newPercentage
333334
}
335+
336+
// Enable or disable automatic rewards calculations and payments
337+
pub fun updateAutomaticRewardsEnabled(_ enabled: Bool) {
338+
FlowEpoch.account.load<Bool>(from: /storage/flowAutomaticRewardsEnabled)
339+
FlowEpoch.account.save(enabled, to: /storage/flowAutomaticRewardsEnabled)
340+
}
334341
}
335342

336343
/// Resource that is controlled by the protocol and is used
@@ -357,8 +364,11 @@ pub contract FlowEpoch {
357364
let currentBlock = getCurrentBlock()
358365
let currentEpochMetadata = FlowEpoch.getEpochMetadata(FlowEpoch.currentEpochCounter)!
359366
if currentBlock.view >= currentEpochMetadata.endView {
360-
self.calculateAndSetRewards(nil)
367+
self.calculateAndSetRewards()
361368
self.endEpoch()
369+
if FlowEpoch.automaticRewardsEnabled() {
370+
self.payRewards()
371+
}
362372
}
363373
default:
364374
return
@@ -399,8 +409,8 @@ pub contract FlowEpoch {
399409

400410
/// Needs to be called before the epoch is over
401411
/// Calculates rewards for the current epoch and stores them in epoch metadata
402-
pub fun calculateAndSetRewards(_ newPayout: UFix64?) {
403-
FlowEpoch.calculateAndSetRewards(newPayout)
412+
pub fun calculateAndSetRewards() {
413+
FlowEpoch.calculateAndSetRewards()
404414
}
405415

406416
pub fun payRewards() {
@@ -438,8 +448,6 @@ pub contract FlowEpoch {
438448
FlowEpoch.borrowDKGAdmin().forceEndDKG()
439449
}
440450

441-
FlowEpoch.calculateAndSetRewards(newPayout)
442-
443451
// Start a new Epoch, which increments the current epoch counter
444452
FlowEpoch.startNewEpoch()
445453

@@ -462,21 +470,37 @@ pub contract FlowEpoch {
462470

463471
/// Calculates a new token payout for the current epoch
464472
/// and sets the new payout for the next epoch
465-
access(account) fun calculateAndSetRewards(_ newPayout: UFix64?) {
473+
access(account) fun calculateAndSetRewards() {
466474

467-
let rewardsBreakdown = self.borrowStakingAdmin().calculateRewards()
475+
let stakingAdmin = self.borrowStakingAdmin()
476+
477+
// Calculate rewards for the current epoch that is about to end
478+
// and save that reward breakdown in the epoch metadata for the current epoch
479+
let rewardsBreakdown = stakingAdmin.calculateRewards()
468480
let currentMetadata = self.getEpochMetadata(self.currentEpochCounter)!
469481
currentMetadata.setRewardAmounts(rewardsBreakdown)
470482
self.saveEpochMetadata(currentMetadata)
471483

472-
// Calculate the new epoch's payout
473-
// disabled until we enable automated rewards calculations
474-
// let newPayout = FlowToken.totalSupply * (FlowEpoch.configurableMetadata.FLOWsupplyIncreasePercentage / 52.0)
484+
if FlowEpoch.automaticRewardsEnabled() {
485+
// Calculate the total supply of FLOW after the current epoch's payout
486+
// the calculation includes the tokens that haven't been minted for the current epoch yet
487+
let currentPayout = FlowIDTableStaking.getEpochTokenPayout()
488+
let feeAmount = FlowFees.getFeeBalance()
489+
var flowTotalSupplyAfterPayout = 0.0
490+
if feeAmount >= currentPayout {
491+
flowTotalSupplyAfterPayout = FlowToken.totalSupply
492+
} else {
493+
flowTotalSupplyAfterPayout = FlowToken.totalSupply + (currentPayout - feeAmount)
494+
}
495+
496+
// Calculate the payout for the next epoch
497+
let proposedPayout = flowTotalSupplyAfterPayout * FlowEpoch.configurableMetadata.FLOWsupplyIncreasePercentage
475498

476-
if let payout = newPayout {
477-
self.borrowStakingAdmin().setEpochTokenPayout(payout)
478-
let proposedMetadata = self.getEpochMetadata(self.proposedEpochCounter())!
479-
proposedMetadata.setTotalRewards(payout)
499+
// Set the new payout in the staking contract and proposed Epoch Metadata
500+
self.borrowStakingAdmin().setEpochTokenPayout(proposedPayout)
501+
let proposedMetadata = self.getEpochMetadata(self.proposedEpochCounter())
502+
?? panic("Cannot set rewards for the next epoch becuase it hasn't been proposed yet")
503+
proposedMetadata.setTotalRewards(proposedPayout)
480504
self.saveEpochMetadata(proposedMetadata)
481505
}
482506
}
@@ -767,6 +791,10 @@ pub contract FlowEpoch {
767791
return self.currentEpochCounter + 1 as UInt64
768792
}
769793

794+
pub fun automaticRewardsEnabled(): Bool {
795+
return self.account.copy<Bool>(from: /storage/flowAutomaticRewardsEnabled) ?? false
796+
}
797+
770798
init (currentEpochCounter: UInt64,
771799
numViewsInEpoch: UInt64,
772800
numViewsInStakingAuction: UInt64,

lib/go/contracts/contracts.go

+2
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,7 @@ func FlowEpoch(fungibleTokenAddress,
264264
idTableAddress,
265265
qcAddress,
266266
dkgAddress string,
267+
flowFeesAddress string,
267268
) []byte {
268269
code := assets.MustAssetString(flowEpochFilename)
269270

@@ -272,6 +273,7 @@ func FlowEpoch(fungibleTokenAddress,
272273
code = strings.ReplaceAll(code, placeholderIDTableAddress, withHexPrefix(idTableAddress))
273274
code = strings.ReplaceAll(code, placeholderQCAddr, withHexPrefix(qcAddress))
274275
code = strings.ReplaceAll(code, placeholderDKGAddr, withHexPrefix(dkgAddress))
276+
code = strings.ReplaceAll(code, placeholderFlowFeesAddress, withHexPrefix(flowFeesAddress))
275277

276278
return []byte(code)
277279
}

lib/go/contracts/internal/assets/assets.go

+3-3
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

lib/go/templates/epoch_templates.go

+14
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ const (
1717
resetEpochFilename = "epoch/admin/reset_epoch.cdc"
1818
epochCalculateSetRewardsFilename = "epoch/admin/calculate_rewards.cdc"
1919
epochPayRewardsFilename = "epoch/admin/pay_rewards.cdc"
20+
epochSetAutoRewardsFilename = "epoch/admin/set_automatic_rewards.cdc"
2021

2122
// Node Transactions
2223
epochRegisterNodeFilename = "epoch/node/register_node.cdc"
@@ -30,6 +31,7 @@ const (
3031
getConfigMetadataFilename = "epoch/scripts/get_config_metadata.cdc"
3132
getEpochPhaseFilename = "epoch/scripts/get_epoch_phase.cdc"
3233
getCurrentViewFilename = "epoch/scripts/get_current_view.cdc"
34+
getFlowTotalSupplyFilename = "flowToken/scripts/get_supply.cdc"
3335

3436
// test scripts
3537
getRandomizeFilename = "epoch/scripts/get_randomize.cdc"
@@ -106,6 +108,12 @@ func GenerateEpochPayRewardsScript(env Environment) []byte {
106108
return []byte(replaceAddresses(code, env))
107109
}
108110

111+
func GenerateEpochSetAutomaticRewardsScript(env Environment) []byte {
112+
code := assets.MustAssetString(epochSetAutoRewardsFilename)
113+
114+
return []byte(replaceAddresses(code, env))
115+
}
116+
109117
// Node Templates -----------------------------------------------
110118

111119
func GenerateEpochRegisterNodeScript(env Environment) []byte {
@@ -175,3 +183,9 @@ func GenerateGetCurrentViewScript(env Environment) []byte {
175183

176184
return []byte(replaceAddresses(code, env))
177185
}
186+
187+
func GenerateGetFlowTotalSupplyScript(env Environment) []byte {
188+
code := assets.MustAssetString(getFlowTotalSupplyFilename)
189+
190+
return []byte(replaceAddresses(code, env))
191+
}

0 commit comments

Comments
 (0)