Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/unit-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,6 @@ jobs:
with:
go-version: 1.20
- name: Install Flow CLI
run: sh -ci "$(curl -fsSL https://raw.githubusercontent.com/onflow/flow-cli/master/install.sh)" -- v1.5.0
run: sh -ci "$(curl -fsSL https://raw.githubusercontent.com/onflow/flow-cli/master/install.sh)" -- v1.8.0
- name: Run tests
run: flow test --cover --covercode="contracts" test/*_test.cdc
139 changes: 139 additions & 0 deletions cadence/contracts/AccountUtils.cdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
import "FungibleToken"
import "FlowToken"
import "FlowIDTableStaking"
import "LockedTokens"
import "FlowStakingCollection"
import "FlowStorageFees"

pub contract AccountUtils {

pub struct AccountInfo {

pub(set) var primaryAddress: Address
pub(set) var primaryAcctBalance: UFix64
pub(set) var secondaryAddress: Address?
pub(set) var secondaryAcctBalance: UFix64
pub(set) var nodeStakedBalance: UFix64
pub(set) var delegatedBalance: UFix64

init(_ address:Address) {
self.primaryAddress=address
self.primaryAcctBalance = 0.0
self.secondaryAddress = nil
self.secondaryAcctBalance = 0.0
self.nodeStakedBalance = 0.0
self.delegatedBalance = 0.0
}

pub fun getTotalBalance() :UFix64 {
return self.primaryAcctBalance+self.secondaryAcctBalance+self.nodeStakedBalance+self.delegatedBalance
}
}

/// Get the total flow balance for this account and its linked account
pub fun getTotalFlowBalance(address: Address): UFix64? {

if let info = self.getAccountInfo(address: address) {
return info.getTotalBalance()
}
return nil
}

/// Get the account info for this account
pub fun getAccountInfo(address: Address): AccountInfo?{

var info: AccountInfo = AccountInfo(address)

let account = getAccount(address)
//if balance is 0 the account is not valid
if account.balance == 0.0 {
return nil
}

if account.getLinkTarget(/public/lockedFlowTokenReceiver) != nil {
return nil
}

// Get the main Vault balance of this account
if let vaultRef = account.getCapability(/public/flowTokenBalance).borrow<&FlowToken.Vault{FungibleToken.Balance}>(){
info.primaryAcctBalance = vaultRef.balance
}

var allNodeInfo: [FlowIDTableStaking.NodeInfo] = []
var allDelegateInfo: [FlowIDTableStaking.DelegatorInfo] = []

// get all node objects using the original basic node account configuration
if let nodeStaker = account.getCapability<&{FlowIDTableStaking.NodeStakerPublic}>(FlowIDTableStaking.NodeStakerPublicPath).borrow() {
allNodeInfo.append(FlowIDTableStaking.NodeInfo(nodeID: nodeStaker.id))
}

// get all delegator objects using the original basic delegator account configuration
if let delegator = account.getCapability<&{FlowIDTableStaking.NodeDelegatorPublic}>(/public/flowStakingDelegator).borrow() {
allDelegateInfo.append(FlowIDTableStaking.DelegatorInfo(nodeID: delegator.nodeID, delegatorID: delegator.id))
}

// get all nodes/delegators from the staking collection
// includes all nodes and delegators that are in the locked account
var doesAccountHaveStakingCollection = FlowStakingCollection.doesAccountHaveStakingCollection(address: account.address)
if doesAccountHaveStakingCollection {
allNodeInfo.appendAll(FlowStakingCollection.getAllNodeInfo(address: account.address))
allDelegateInfo.appendAll(FlowStakingCollection.getAllDelegatorInfo(address: account.address))
}

// If we have a lockedAccount linked but don't have a staking collection we need to add nodes/delegators there
// If there is a locked account and a staking collection, the staking collection staking information would have already included the locked account
if let lockedAccountInfo = account.getCapability<&LockedTokens.TokenHolder{LockedTokens.LockedAccountInfo}>(LockedTokens.LockedAccountInfoPublicPath).borrow() {

info.secondaryAddress = lockedAccountInfo.getLockedAccountAddress()
info.secondaryAcctBalance = lockedAccountInfo.getLockedAccountBalance() + FlowStorageFees.minimumStorageReservation
if !doesAccountHaveStakingCollection {
if let nodeID = lockedAccountInfo.getNodeID() {
allNodeInfo.append(FlowIDTableStaking.NodeInfo(nodeID: nodeID))
}

if let delegatorID = lockedAccountInfo.getDelegatorID() {
if let nodeID = lockedAccountInfo.getDelegatorNodeID() {
allDelegateInfo.append(FlowIDTableStaking.DelegatorInfo(nodeID: nodeID, delegatorID: delegatorID))
}
}
}
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
}
}
}

adding a closing bracket for the addition of the does account have staking collection conditional above


// ===== Aggregate all stakes and delegations in a digestible set =====
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// ===== Aggregate all stakes and delegations in a digestible set =====
// ===== Aggregate all nodes and delegators in a digestible set =====

// deduplication between the old way and the new way will happen automatically because the result is stored in a map
let nodes : {String:UFix64} = {}
let delegators : {String:UFix64} = {}
for nodeInfo in allNodeInfo {
let balance = nodeInfo.tokensStaked
+ nodeInfo.tokensCommitted
+ nodeInfo.tokensUnstaking
+ nodeInfo.tokensUnstaked
+ nodeInfo.tokensRewarded

nodes["n:".concat(nodeInfo.id)] = balance
}

for delegatorInfo in allDelegateInfo {
let balance = delegatorInfo.tokensStaked
+ delegatorInfo.tokensCommitted
+ delegatorInfo.tokensUnstaking
+ delegatorInfo.tokensUnstaked
+ delegatorInfo.tokensRewarded

delegators["n:".concat(delegatorInfo.nodeID).concat(" d:").concat(delegatorInfo.id.toString())] = balance
}


for key in nodes.keys {
let value = nodes[key]!
info.nodeStakedBalance = info.nodeStakedBalance + value
}

for key in delegators.keys {
let value = delegators[key]!
info.delegatedBalance = info.delegatedBalance + value
}

return info
}
}
65 changes: 59 additions & 6 deletions flow.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,12 @@
"testing": "0x0000000000000007"
}
},
"AccountUtils": {
"source": "./cadence/contracts/AccountUtils.cdc",
"aliases": {
"testing": "0x0000000000000007"
}
},
"ScopedNFTProviders": {
"source": "./cadence/contracts/ScopedNFTProviders.cdc",
"aliases": {
Expand Down Expand Up @@ -71,6 +77,42 @@
"aliases": {
"testing": "0x0000000000000007"
}
},
"FlowToken": {
"aliases": {
"testnet": "0x7e60df042a9c0868",
"emulator": "0x0ae53cb6e3f42a79",
"mainnet": "0x1654653399040a61"
}
},
"FlowIDTableStaking": {
"aliases": {
"testnet": "0x9eca2b38b18b5dfe",
"emulator": "0xf8d6e0586b0a20c7",
"mainnet": "0x8624b52f9ddcd04a"
}
},
"LockedTokens": {
"source": "./cadence/contracts/LockedTokens.cdc",
"aliases": {
"mainnet": "0x8d0e87b65159ae63",
"testnet": "0x95e019a17d0e23d7",
"emulator": "0xf8d6e0586b0a20c7"
}
},
"FlowStakingCollection": {
"aliases": {
"mainnet": "0x8d0e87b65159ae63",
"testnet": "0x95e019a17d0e23d7",
"emulator": "0xf8d6e0586b0a20c7"
}
},
"FlowStorageFees": {
"aliases": {
"emulator": "0xf8d6e0586b0a20c7",
"testnet": "0x8c5303eaa26202d6",
"mainnet": "0xe467b9dd11fa00df"
}
}
},
"networks": {
Expand Down Expand Up @@ -103,6 +145,16 @@
"hashAlgorithm": "SHA2_256",
"resourceID": "projects/flow-utils/locations/global/keyRings/contract/cryptoKeys/mainnet/cryptoKeyVersions/1"
}
},
"bjartek": {
"address": "0xa340dc0a4ec828ab",
"key": {
"type": "google-kms",
"index": 1,
"signatureAlgorithm": "ECDSA_P256",
"hashAlgorithm": "SHA2_256",
"resourceID": "projects/bjartek/locations/europe-north1/keyRings/flow/cryptoKeys/flow/cryptoKeyVersions/1"
}
}
},
"deployments": {
Expand All @@ -111,12 +163,11 @@
"ArrayUtils",
"StringUtils",
"AddressUtils",
"NonFungibleToken",
"MetadataViews",
"ExampleNFT",
"ExampleToken",
"ScopedNFTProviders",
"ScopedFTProviders"
"ScopedFTProviders",
"AccountUtils"
]
},
"testnet": {
Expand All @@ -125,7 +176,8 @@
"StringUtils",
"AddressUtils",
"ScopedNFTProviders",
"ScopedFTProviders"
"ScopedFTProviders",
"AccountUtils"
]
},
"mainnet": {
Expand All @@ -134,8 +186,9 @@
"StringUtils",
"AddressUtils",
"ScopedNFTProviders",
"ScopedFTProviders"
"ScopedFTProviders",
"AccountUtils"
]
}
}
}
}
5 changes: 5 additions & 0 deletions scripts/getAccountBalance.cdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import "AccountUtils"
pub fun main(address:Address) : AnyStruct {

return AccountUtils.getTotalFlowBalance(address:address)
}
5 changes: 5 additions & 0 deletions scripts/getAccountInfo.cdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import "AccountUtils"
pub fun main(address:Address) : AnyStruct {

return AccountUtils.getAccountInfo(address:address)
}
23 changes: 23 additions & 0 deletions test/AccountUtils_test.cdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import Test
import "AccountUtils"

access(all)
fun setup() {
var err = Test.deployContract(
name: "AccountUtils",
path: "../cadence/contracts/AccountUtils.cdc",
arguments: []
)
Test.expect(err, Test.beNil())
}

access(all)
fun testGetFlowBalance() {

let address=Test.serviceAccount().address
let balance = AccountUtils.getTotalFlowBalance(address:address)
let expected : UFix64? = 1000000000.0
Test.assertEqual(expected, balance)

}