Skip to content

Commit

Permalink
Use a separate field for delegator minimum stake requirements
Browse files Browse the repository at this point in the history
  • Loading branch information
joshuahannan committed Jun 26, 2023
1 parent 450fdf5 commit 3a41da7
Show file tree
Hide file tree
Showing 10 changed files with 147 additions and 54 deletions.
41 changes: 28 additions & 13 deletions contracts/FlowIDTableStaking.cdc
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ pub contract FlowIDTableStaking {
pub event NewDelegatorCutPercentage(newCutPercentage: UFix64)
pub event NewWeeklyPayout(newPayout: UFix64)
pub event NewStakingMinimums(newMinimums: {UInt8: UFix64})
pub event NewDelegatorStakingMinimum(newMinimum: UFix64)

/// Holds the identity table for all the nodes in the network.
/// Includes nodes that aren't actively participating
Expand All @@ -78,7 +79,6 @@ pub contract FlowIDTableStaking {
/// The minimum amount of tokens that each staker type has to stake
/// in order to be considered valid
/// Keys:
/// 0 - Delegators
/// 1 - Collector Nodes
/// 2 - Consensus Nodes
/// 3 - Execution Nodes
Expand Down Expand Up @@ -853,17 +853,25 @@ pub contract FlowIDTableStaking {
/// at the end of the staking auction, and pay rewards to nodes at the end of an epoch
pub resource Admin: EpochOperations {

/// Sets a new set of minimum staking requirements for all the nodes and delegators
/// Delegator minimum is at index 0, other nodes' indexes are their role numbers
/// Sets a new set of minimum staking requirements for all the nodes
/// Nodes' indexes are their role numbers
pub fun setMinimumStakeRequirements(_ newRequirements: {UInt8: UFix64}) {
pre {
newRequirements.keys.length == 6:
"There must be six entries for minimum stake requirements (one for delegators and 5 for nodes)"
newRequirements.keys.length == 5:
"There must be six entries for node minimum stake requirements"
}
FlowIDTableStaking.minimumStakeRequired = newRequirements
emit NewStakingMinimums(newMinimums: newRequirements)
}

/// Sets a new set of minimum staking requirements for all the delegators
pub fun setDelegatorMinimumStakeRequirement(_ newRequirement: UFix64) {
FlowIDTableStaking.account.load<UFix64>(from: /storage/delegatorStakingMinimum)
FlowIDTableStaking.account.save(newRequirement, to: /storage/delegatorStakingMinimum)

emit NewDelegatorStakingMinimum(newMinimum: newRequirement)
}

/// Changes the total weekly payout to a new value
pub fun setEpochTokenPayout(_ newPayout: UFix64) {
if newPayout != FlowIDTableStaking.epochTokenPayout {
Expand Down Expand Up @@ -1441,9 +1449,9 @@ pub contract FlowIDTableStaking {
let delRecord = nodeRecord.borrowDelegatorRecord(delegator)

// If the delegator's committed tokens for the next epoch
// Is less than the delegator minimum, unstake all their tokens
// is less than the delegator minimum, unstake all their tokens
let actualCommittedForNextEpoch = delRecord.tokensCommitted.balance + delRecord.tokensStaked.balance - delRecord.tokensRequestedToUnstake
if !FlowIDTableStaking.isGreaterThanMinimumForRole(numTokens: actualCommittedForNextEpoch, role: UInt8(0)) {
if actualCommittedForNextEpoch < FlowIDTableStaking.getDelegatorMinimumStakeRequirement() {
delRecord.tokensUnstaked.deposit(from: <-delRecord.tokensCommitted.withdraw(amount: delRecord.tokensCommitted.balance))
delRecord.tokensRequestedToUnstake = delRecord.tokensStaked.balance
}
Expand Down Expand Up @@ -1548,9 +1556,9 @@ pub contract FlowIDTableStaking {
message: "Cannot register a delegator for an access node"
)

let minimum = self.minimumStakeRequired[UInt8(0)]!
let minimum = self.getDelegatorMinimumStakeRequirement()
assert(
self.isGreaterThanMinimumForRole(numTokens: tokensCommitted.balance, role: 0),
tokensCommitted.balance >= minimum,
message: "Tokens committed for delegator registration is not above the minimum (".concat(minimum.toString()).concat(")")
)

Expand Down Expand Up @@ -1810,10 +1818,10 @@ pub contract FlowIDTableStaking {
}

/// Checks if the amount of tokens is greater than the minimum staking requirement
/// for the specified staker role (including role == 0 for delegators)
/// for the specified node role
pub fun isGreaterThanMinimumForRole(numTokens: UFix64, role: UInt8): Bool {
let minimumStake = self.minimumStakeRequired[role]
?? panic("Incorrect role provided for minimum stake. Must be 0, 1, 2, 3, 4, or 5")
?? panic("Incorrect role provided for minimum stake. Must be 1, 2, 3, 4, or 5")

return numTokens >= minimumStake
}
Expand Down Expand Up @@ -1851,11 +1859,17 @@ pub contract FlowIDTableStaking {
?? panic("could not get non-operational node list")
}

/// Gets the minimum stake requirements for all the staker types
/// Gets the minimum stake requirements for all the node types
pub fun getMinimumStakeRequirements(): {UInt8: UFix64} {
return self.minimumStakeRequired
}

/// Gets the minimum stake requirement for delegators
pub fun getDelegatorMinimumStakeRequirement(): UFix64 {
return self.account.copy<UFix64>(from: /storage/delegatorStakingMinimum)
?? 0.0
}

/// Gets a dictionary that indicates the current number of tokens staked
/// by all the nodes of each type
pub fun getTotalTokensStakedByNodeType(): {UInt8: UFix64} {
Expand Down Expand Up @@ -1906,7 +1920,8 @@ pub contract FlowIDTableStaking {
self.StakingAdminStoragePath = /storage/flowStakingAdmin
self.DelegatorStoragePath = /storage/flowStakingDelegator

self.minimumStakeRequired = {UInt8(0): 50.0, UInt8(1): 250000.0, UInt8(2): 500000.0, UInt8(3): 1250000.0, UInt8(4): 135000.0, UInt8(5): 100.0}
self.minimumStakeRequired = {UInt8(1): 250000.0, UInt8(2): 500000.0, UInt8(3): 1250000.0, UInt8(4): 135000.0, UInt8(5): 100.0}
self.account.save(50.0 as UFix64, to: /storage/delegatorStakingMinimum)
self.totalTokensStakedByNodeType = {UInt8(1): 0.0, UInt8(2): 0.0, UInt8(3): 0.0, UInt8(4): 0.0, UInt8(5): 0.0}
self.epochTokenPayout = epochTokenPayout
self.nodeDelegatingRewardCut = rewardCut
Expand Down
6 changes: 3 additions & 3 deletions lib/go/contracts/internal/assets/assets.go

Large diffs are not rendered by default.

15 changes: 15 additions & 0 deletions lib/go/templates/idtable_staking_templates.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ const (
moveTokensFilename = "idTableStaking/admin/move_tokens.cdc"
endEpochFilename = "idTableStaking/admin/end_epoch.cdc"
changeMinimumsFilename = "idTableStaking/admin/change_minimums.cdc"
changeDelegatorMinimumsFilename = "idTableStaking/admin/change_del_minimums.cdc"
changeCutFilename = "idTableStaking/admin/change_cut.cdc"
changePayoutFilename = "idTableStaking/admin/change_payout.cdc"
endEpochChangePayoutFilename = "idTableStaking/admin/end_epoch_change_payout.cdc"
Expand Down Expand Up @@ -71,6 +72,7 @@ const (
getNonOperationalListFilename = "idTableStaking/scripts/get_non_operational.cdc"
getApprovedNodesFileName = "idTableStaking/scripts/get_approved_nodes.cdc"
stakeRequirementsFilename = "idTableStaking/scripts/get_stake_requirements.cdc"
delegatorStakeRequirementsFilename = "idTableStaking/scripts/get_del_stake_requirements.cdc"
totalStakedByTypeFilename = "idTableStaking/scripts/get_total_staked_by_type.cdc"
totalStakedFilename = "idTableStaking/scripts/get_total_staked.cdc"
rewardRatioFilename = "idTableStaking/scripts/get_node_type_ratio.cdc"
Expand Down Expand Up @@ -170,6 +172,12 @@ func GenerateChangeMinimumsScript(env Environment) []byte {
return []byte(ReplaceAddresses(code, env))
}

func GenerateChangeDelegatorMinimumsScript(env Environment) []byte {
code := assets.MustAssetString(changeDelegatorMinimumsFilename)

return []byte(ReplaceAddresses(code, env))
}

// GenerateChangeCutScript creates a script that changes the cut percentage
func GenerateChangeCutScript(env Environment) []byte {
code := assets.MustAssetString(changeCutFilename)
Expand Down Expand Up @@ -351,6 +359,13 @@ func GenerateGetStakeRequirementsScript(env Environment) []byte {
return []byte(ReplaceAddresses(code, env))
}

// GenerateGetDelegatorStakeRequirementScript returns the stake requirement for delegators
func GenerateGetDelegatorStakeRequirementScript(env Environment) []byte {
code := assets.MustAssetString(delegatorStakeRequirementsFilename)

return []byte(ReplaceAddresses(code, env))
}

// GenerateGetTotalTokensStakedByTypeScript returns the total tokens staked for a node type
func GenerateGetTotalTokensStakedByTypeScript(env Environment) []byte {
code := assets.MustAssetString(totalStakedByTypeFilename)
Expand Down
Loading

0 comments on commit 3a41da7

Please sign in to comment.