Skip to content

Commit

Permalink
Update to Cadence v0.12.3, SDK v0.14.0, and Emulator v0.14.0-beta2 (#97)
Browse files Browse the repository at this point in the history
* update to Cadence v0.12.3, SDK v0.14.0, and Emulator v0.14.0-beta2

* update contracts, transactions, and scripts

* improve assertions

* remove unnecessary casts in equalitity assertions

* commend out scaling

* fix equality assertion order and always use Equal function

* use own equality assertion function to properly format values, esp. fixed-point numbers

* split up minting and registering

* added test helpers and calculations

* tidy

* full test

* small fix

* tidy

Co-authored-by: joshuahannan <joshua.hannan@dapperlabs.com>
  • Loading branch information
turbolent and joshuahannan authored Jan 18, 2021
1 parent 859b2b3 commit 89e2782
Show file tree
Hide file tree
Showing 48 changed files with 1,713 additions and 1,772 deletions.
93 changes: 42 additions & 51 deletions contracts/FlowIDTableStaking.cdc
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
FlowIDTableStaking
The Flow ID Table and Staking contract manages
node operators' and delegators' information
The Flow ID Table and Staking contract manages
node operators' and delegators' information
and Flow tokens that are staked as part of the Flow Protocol.
Nodes submit their stake to the public addNodeInfo function
Expand All @@ -15,8 +15,8 @@
Each node has multiple token buckets that hold their tokens
based on their status: committed, staked, unstaking, unstaked, and rewarded.
The Admin has the authority to remove node records,
refund insufficiently staked nodes, pay rewards,
The Admin has the authority to remove node records,
refund insufficiently staked nodes, pay rewards,
and move tokens between buckets. These will happen once every epoch.
*/
Expand Down Expand Up @@ -79,15 +79,15 @@ pub contract FlowIDTableStaking {
/// value = decimal number between 0 and 1 indicating a percentage
access(contract) var rewardRatios: {UInt8: UFix64}

/// The percentage of rewards that every node operator takes from
/// The percentage of rewards that every node operator takes from
/// the users that are delegating to it
access(contract) var nodeDelegatingRewardCut: UFix64

/// Paths for storing staking resources
pub let NodeStakerStoragePath: Path
pub let NodeStakerPublicPath: Path
pub let StakingAdminStoragePath: Path
pub let DelegatorStoragePath: Path
pub let NodeStakerStoragePath: StoragePath
pub let NodeStakerPublicPath: PublicPath
pub let StakingAdminStoragePath: StoragePath
pub let DelegatorStoragePath: StoragePath

/*********** ID Table and Staking Composite Type Definitions *************/

Expand All @@ -98,7 +98,7 @@ pub contract FlowIDTableStaking {
/// Set when the node is created
pub let id: String

/// The type of node:
/// The type of node:
/// 1 = collection
/// 2 = consensus
/// 3 = execution
Expand All @@ -107,7 +107,7 @@ pub contract FlowIDTableStaking {
pub var role: UInt8

pub(set) var networkingAddress: String

pub(set) var networkingKey: String

pub(set) var stakingKey: String
Expand Down Expand Up @@ -142,12 +142,13 @@ pub contract FlowIDTableStaking {
/// weight as determined by the amount staked after the staking auction
pub(set) var initialWeight: UInt64

init(id: String,
role: UInt8,
networkingAddress: String,
networkingKey: String,
stakingKey: String,
tokensCommitted: @FungibleToken.Vault
init(
id: String,
role: UInt8,
networkingAddress: String,
networkingKey: String,
stakingKey: String,
tokensCommitted: @FungibleToken.Vault
) {
pre {
id.length == 64: "Node ID length must be 32 bytes (64 hex characters)"
Expand Down Expand Up @@ -395,7 +396,7 @@ pub contract FlowIDTableStaking {
nodeRecord.tokensCommitted.deposit(from: <-tokens)
}

/// Stake tokens that are in the tokensUnstaked bucket
/// Stake tokens that are in the tokensUnstaked bucket
pub fun stakeUnstakedTokens(amount: UFix64) {
let nodeRecord = FlowIDTableStaking.borrowNodeRecord(self.id)

Expand All @@ -418,7 +419,7 @@ pub contract FlowIDTableStaking {
}
}

/// Stake tokens that are in the tokensRewarded bucket
/// Stake tokens that are in the tokensRewarded bucket
pub fun stakeRewardedTokens(amount: UFix64) {

if amount > 0.0 {
Expand All @@ -436,14 +437,14 @@ pub contract FlowIDTableStaking {
let nodeRecord = FlowIDTableStaking.borrowNodeRecord(self.id)

assert (
nodeRecord.tokensStaked.balance +
nodeRecord.tokensCommitted.balance
nodeRecord.tokensStaked.balance +
nodeRecord.tokensCommitted.balance
>= amount + nodeRecord.tokensRequestedToUnstake,
message: "Not enough tokens to unstake!"
)

assert (
nodeRecord.delegators.length == 0 ||
nodeRecord.delegators.length == 0 ||
FlowIDTableStaking.isGreaterThanMinimumForRole(numTokens: FlowIDTableStaking.getNodeCommittedBalanceWithoutDelegators(nodeRecord.id) - amount, role: nodeRecord.role),
message: "Cannot unstake below the minimum if there are delegators"
)
Expand All @@ -467,7 +468,7 @@ pub contract FlowIDTableStaking {

/// update request to show that leftover amount is requested to be unstaked
nodeRecord.tokensRequestedToUnstake = nodeRecord.tokensRequestedToUnstake + (amount - amountCommitted)
}
}
}

/// Requests to unstake all of the node operators staked and committed tokens,
Expand All @@ -481,7 +482,7 @@ pub contract FlowIDTableStaking {
/// withdraw the requested tokens from committed since they have not been staked yet
nodeRecord.tokensUnstaked.deposit(from: <-nodeRecord.tokensCommitted.withdraw(amount: nodeRecord.tokensCommitted.balance))
}

/// update request to show that leftover amount is requested to be unstaked
nodeRecord.tokensRequestedToUnstake = nodeRecord.tokensStaked.balance
}
Expand Down Expand Up @@ -579,8 +580,8 @@ pub contract FlowIDTableStaking {
let delRecord = nodeRecord.borrowDelegatorRecord(self.id)

assert (
delRecord.tokensStaked.balance +
delRecord.tokensCommitted.balance
delRecord.tokensStaked.balance +
delRecord.tokensCommitted.balance
>= amount + delRecord.tokensRequestedToUnstake,
message: "Not enough tokens to unstake!"
)
Expand All @@ -601,7 +602,7 @@ pub contract FlowIDTableStaking {

/// update request to show that leftover amount is requested to be unstaked
delRecord.tokensRequestedToUnstake = delRecord.tokensRequestedToUnstake + (amount - amountCommitted)
}
}
}

/// Withdraw tokens from the unstaked bucket
Expand Down Expand Up @@ -643,7 +644,7 @@ pub contract FlowIDTableStaking {
/// Iterates through all the registered nodes and if it finds
/// a node that has insufficient tokens committed for the next epoch
/// it moves their committed tokens to their unstaked bucket
///
///
/// Parameter: approvedNodeIDs: A list of nodeIDs that have been approved
/// by the protocol to be a staker for the next epoch. The node software
/// checks if the node that corresponds to each proposed ID is running properly
Expand Down Expand Up @@ -679,7 +680,7 @@ pub contract FlowIDTableStaking {
for delegator in nodeRecord.delegators.keys {
let delRecord = nodeRecord.borrowDelegatorRecord(delegator)

if delRecord.tokensCommitted.balance > 0.0 {
if delRecord.tokensCommitted.balance > 0.0 {
emit DelegatorTokensUnstaked(nodeID: nodeRecord.id, delegatorID: delegator, amount: delRecord.tokensCommitted.balance)

/// move their committed tokens back to their unstaked tokens
Expand Down Expand Up @@ -710,19 +711,10 @@ pub contract FlowIDTableStaking {
// calculate the total number of tokens staked
var totalStaked = FlowIDTableStaking.getTotalStaked()

var totalRewardScale: UFix64 = 0.0

if totalStaked >= UFix64(100000000.0) {
// There is a limitation in the current version of Cadence that means that
// dividing by a very large number will cause an overflow (>100M), or produce inaccurate results (>1M).
// This should be fixed soon, but in the meantime, we can work around it
// by scaling both numbers by a factor of 1000 to avoid those edge cases.
let div1000dividend = FlowIDTableStaking.epochTokenPayout / 1000.0
let div1000divisor = totalStaked / 1000.0
totalRewardScale = div1000dividend / div1000divisor
} else if totalStaked != 0.0 {
totalRewardScale = FlowIDTableStaking.epochTokenPayout / totalStaked
if totalStaked == 0.0 {
return
}
var totalRewardScale = FlowIDTableStaking.epochTokenPayout / totalStaked

/// iterate through all the nodes
for nodeID in allNodeIDs {
Expand Down Expand Up @@ -764,13 +756,13 @@ pub contract FlowIDTableStaking {
} else {
destroy delegatorReward
}
}
}

if tokenReward.balance > 0.0 {
emit RewardsPaid(nodeID: nodeRecord.id, amount: tokenReward.balance)

/// Deposit the node Rewards into their tokensRewarded bucket
nodeRecord.tokensRewarded.deposit(from: <-tokenReward)
nodeRecord.tokensRewarded.deposit(from: <-tokenReward)
} else {
destroy tokenReward
}
Expand All @@ -783,7 +775,7 @@ pub contract FlowIDTableStaking {
/// Tokens that were unstaking during the last epoch are fully unstaked
/// Unstaking requests are filled by moving those tokens from staked to unstaking
pub fun moveTokens() {

let allNodeIDs = FlowIDTableStaking.getNodeIDs()

for nodeID in allNodeIDs {
Expand Down Expand Up @@ -859,7 +851,7 @@ pub contract FlowIDTableStaking {
emit NewWeeklyPayout(newPayout: newPayout)
}

/// Admin calls this to change the percentage
/// Admin calls this to change the percentage
/// of delegator rewards every node operator takes
pub fun setCutPercentage(_ newCutPercentage: UFix64) {
pre {
Expand All @@ -877,7 +869,7 @@ pub contract FlowIDTableStaking {
/// It returns the resource for nodes that they can store in their account storage
pub fun addNodeRecord(id: String, role: UInt8, networkingAddress: String, networkingKey: String, stakingKey: String, tokensCommitted: @FungibleToken.Vault): @NodeStaker {
let initialBalance = tokensCommitted.balance

let newNode <- create NodeRecord(id: id, role: role, networkingAddress: networkingAddress, networkingKey: networkingKey, stakingKey: stakingKey, tokensCommitted: <-tokensCommitted)

// Insert the node to the table
Expand Down Expand Up @@ -944,7 +936,7 @@ pub contract FlowIDTableStaking {

/// Gets an array of all the nodeIDs that are staked.
/// Only nodes that are participating in the current epoch
/// can be staked, so this is an array of all the active
/// can be staked, so this is an array of all the active
/// node operators
pub fun getStakedNodeIDs(): [String] {
var stakedNodes: [String] = []
Expand Down Expand Up @@ -975,7 +967,7 @@ pub contract FlowIDTableStaking {
return nodeRecord.nodeFullCommittedBalance()
}

/// Gets the total amount of tokens that have been staked and committed for a node.
/// Gets the total amount of tokens that have been staked and committed for a node.
/// The sum from the node operator and all its delegators
pub fun getNodeCommittedBalanceWithDelegators(_ nodeID: String): UFix64 {
let nodeRecord = self.borrowNodeRecord(nodeID)
Expand All @@ -990,7 +982,7 @@ pub contract FlowIDTableStaking {
return sum
}

/// Gets the total amount of tokens that have been staked for a node.
/// Gets the total amount of tokens that have been staked for a node.
/// The sum from the node operator and all its delegators
pub fun getNodeStakedBalanceWithDelegators(_ nodeID: String): UFix64 {
let nodeRecord = self.borrowNodeRecord(nodeID)
Expand All @@ -1005,7 +997,7 @@ pub contract FlowIDTableStaking {
return sum
}

// Checks to make sure that the amount of tokens specified
// Checks to make sure that the amount of tokens specified
// is greater than what is required for that node role
pub fun isGreaterThanMinimumForRole(numTokens: UFix64, role: UInt8): Bool {
if role == UInt8(5) {
Expand Down Expand Up @@ -1069,4 +1061,3 @@ pub contract FlowIDTableStaking {
self.account.save(<-create Admin(), to: self.StakingAdminStoragePath)
}
}

7 changes: 4 additions & 3 deletions contracts/FlowServiceAccount.cdc
Original file line number Diff line number Diff line change
Expand Up @@ -44,15 +44,16 @@ pub contract FlowServiceAccount {
// Get the default token balance on an account
pub fun defaultTokenBalance(_ acct: PublicAccount): UFix64 {
let balanceRef = acct
.getCapability(/public/flowTokenBalance)!
.getCapability(/public/flowTokenBalance)
.borrow<&FlowToken.Vault{FungibleToken.Balance}>()!

return balanceRef.balance
}

// Return a reference to the default token vault on an account
pub fun defaultTokenVault(_ acct: AuthAccount): &FlowToken.Vault {
return acct.borrow<&FlowToken.Vault>(from: /storage/flowTokenVault) ?? panic("Unable to borrow reference to the default token vault")
return acct.borrow<&FlowToken.Vault>(from: /storage/flowTokenVault)
?? panic("Unable to borrow reference to the default token vault")
}

// Called when a transaction is submitted to deduct the fee
Expand All @@ -61,7 +62,7 @@ pub contract FlowServiceAccount {
if self.transactionFee == UFix64(0) {
return
}

let tokenVault = self.defaultTokenVault(acct)
let feeVault <- tokenVault.withdraw(amount: self.transactionFee)

Expand Down
4 changes: 2 additions & 2 deletions contracts/FlowToken.cdc
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ pub contract FlowToken: FungibleToken {
self.allowedAmount = allowedAmount
}
}

// Burner
//
// Resource object that token admin accounts can hold to burn tokens.
Expand All @@ -154,7 +154,7 @@ pub contract FlowToken: FungibleToken {
//
// Function that destroys a Vault instance, effectively burning the tokens.
//
// Note: the burned tokens are automatically subtracted from the
// Note: the burned tokens are automatically subtracted from the
// total supply in the Vault destructor.
//
pub fun burnTokens(from: @FungibleToken.Vault) {
Expand Down
Loading

0 comments on commit 89e2782

Please sign in to comment.