From b8409025f38934c7a1bbdfe20ea4baf2537b49ab Mon Sep 17 00:00:00 2001 From: Luis Fernando Molina Date: Wed, 16 Jun 2021 13:53:51 +0200 Subject: [PATCH] Governance Token Power Distribution Implemented --- .../Function-Libraries/DistributionProcess.js | 4 + .../UI/Function-Libraries/TokenPower.js | 248 ++++++++++++++++++ .../Spaces/User-Profile-Space/UserProfile.js | 3 +- Projects/ProjectsSchema.json | 5 + 4 files changed, 258 insertions(+), 2 deletions(-) create mode 100644 Projects/Governance/UI/Function-Libraries/TokenPower.js diff --git a/Projects/Governance/UI/Function-Libraries/DistributionProcess.js b/Projects/Governance/UI/Function-Libraries/DistributionProcess.js index 13e621677..2fc2f894b 100644 --- a/Projects/Governance/UI/Function-Libraries/DistributionProcess.js +++ b/Projects/Governance/UI/Function-Libraries/DistributionProcess.js @@ -13,6 +13,10 @@ function newGovernanceFunctionLibraryDistributionProcess() { let positions = UI.projects.foundations.spaces.designSpace.workspace.getHierarchyHeadsByNodeType('Positions') let userProfiles = UI.projects.foundations.spaces.designSpace.workspace.getHierarchyHeadsByNodeType('User Profile') + UI.projects.governance.functionLibraries.tokenPower.calculate( + userProfiles + ) + UI.projects.governance.functionLibraries.votes.calculate( pools, features, diff --git a/Projects/Governance/UI/Function-Libraries/TokenPower.js b/Projects/Governance/UI/Function-Libraries/TokenPower.js new file mode 100644 index 000000000..ea196c329 --- /dev/null +++ b/Projects/Governance/UI/Function-Libraries/TokenPower.js @@ -0,0 +1,248 @@ +function newGovernanceFunctionLibraryTokenPower() { + let thisObject = { + calculate: calculate + } + + return thisObject + + function calculate( + userProfiles + ) { + /* + We will first reset all the token power, and then distribute it to each Program. + */ + for (let i = 0; i < userProfiles.length; i++) { + let userProfile = userProfiles[i] + resetTokenPower(userProfile) + } + for (let i = 0; i < userProfiles.length; i++) { + let userProfile = userProfiles[i] + distributeForProfile(userProfile) + } + + function resetTokenPower(node) { + if (node === undefined) { return } + if (node.payload === undefined) { return } + node.payload.tokenPower = 0 + /* + When we reach certain node types, we will halt the distribution, because these are targets for + token power. + */ + if ( + node.type === 'Referral Program' || + node.type === 'Mentorship Program' || + node.type === 'Support Program' || + node.type === 'Voting Program' || + node.type === 'Claims Program' || + node.type === 'Staking Program' + ) { return } + /* + We will reset token power of children. + */ + let schemaDocument = getSchemaDocument(node) + if (schemaDocument === undefined) { return } + + if (schemaDocument.childrenNodesProperties !== undefined) { + for (let i = 0; i < schemaDocument.childrenNodesProperties.length; i++) { + let property = schemaDocument.childrenNodesProperties[i] + + switch (property.type) { + case 'node': { + let childNode = node[property.name] + resetTokenPower(childNode) + } + break + case 'array': { + let propertyArray = node[property.name] + if (propertyArray !== undefined) { + for (let m = 0; m < propertyArray.length; m++) { + let childNode = propertyArray[m] + resetTokenPower(childNode) + } + } + break + } + } + } + } + } + + function distributeForProfile(userProfile) { + if (userProfile.payload === undefined) { return } + let tokenPower + if (userProfile.payload.blockchainTokens === undefined) { + return + } else { + tokenPower = userProfile.payload.blockchainTokens + } + /* + Before we start we will do some validations: + */ + if (userProfile.tokenSwitch === undefined) { + userProfile.payload.uiObject.setErrorMessage("You need to have a Token Switch child node.") + return + } + if (userProfile.tokenSwitch.referralProgram === undefined) { + userProfile.tokenSwitch.payload.uiObject.setErrorMessage("You need to have a Referral Program child node.") + return + } + if (userProfile.tokenSwitch.mentorshipProgram === undefined) { + userProfile.tokenSwitch.payload.uiObject.setErrorMessage("You need to have a Mentorship Program child node.") + return + } + if (userProfile.tokenSwitch.supportProgram === undefined) { + userProfile.tokenSwitch.payload.uiObject.setErrorMessage("You need to have a Spupport Program child node.") + return + } + + distributeTokenPower(userProfile, tokenPower) + } + + function distributeTokenPower(node, tokenPower, switchPercentage) { + if (node === undefined) { return } + if (node.payload === undefined) { return } + node.payload.tokenPower = node.payload.tokenPower + tokenPower + drawTokenPower(node, node.payload.tokenPower, switchPercentage) + /* + When we reach certain node types, we will halt the distribution, + because these are targets for token power. + */ + if ( + node.type === 'Referral Program' || + node.type === 'Mentorship Program' || + node.type === 'Support Program' || + node.type === 'Voting Program' || + node.type === 'Claims Program' || + node.type === 'Staking Program' + ) { return } + /* + We will redistribute token power among children. + */ + let schemaDocument = getSchemaDocument(node) + if (schemaDocument === undefined) { return } + + if (schemaDocument.childrenNodesProperties !== undefined) { + /* + Before distributing the token power, we will calculate how the power + is going to be switched between all nodes. The first pass is about + scanning all sibling nodes to see which ones have a percentage defined + at their config, and check that all percentages don't add more than 100. + */ + let totalPercentage = 0 + let totalNodesWithoutPercentage = 0 + for (let i = 0; i < schemaDocument.childrenNodesProperties.length; i++) { + let property = schemaDocument.childrenNodesProperties[i] + + switch (property.type) { + case 'node': { + let childNode = node[property.name] + if (childNode === undefined) { continue } + let percentage = getPercentage(childNode) + + if (percentage !== undefined && isNaN(percentage) !== true) { + totalPercentage = totalPercentage + percentage + } else { + totalNodesWithoutPercentage++ + } + } + break + case 'array': { + let propertyArray = node[property.name] + if (propertyArray !== undefined) { + for (let m = 0; m < propertyArray.length; m++) { + let childNode = propertyArray[m] + if (childNode === undefined) { continue } + let percentage = getPercentage(childNode) + if (percentage !== undefined && isNaN(percentage) !== true) { + totalPercentage = totalPercentage + percentage + } else { + totalNodesWithoutPercentage++ + } + } + } + break + } + } + } + if (totalPercentage > 100) { + node.payload.uiObject.setErrorMessage('Token Power Switching Error. Total Percentage of children nodes is grater that 100.') + return + } + let defaultPercentage = 0 + if (totalNodesWithoutPercentage > 0) { + defaultPercentage = (100 - totalPercentage) / totalNodesWithoutPercentage + } + /* + Here we do the actual distribution. + */ + for (let i = 0; i < schemaDocument.childrenNodesProperties.length; i++) { + let property = schemaDocument.childrenNodesProperties[i] + + switch (property.type) { + case 'node': { + let childNode = node[property.name] + if (childNode === undefined) { continue } + let percentage = getPercentage(childNode) + if (percentage === undefined || isNaN(percentage) === true) { + percentage = defaultPercentage + } + distributeTokenPower(childNode, tokenPower * percentage / 100, percentage) + } + break + case 'array': { + let propertyArray = node[property.name] + if (propertyArray !== undefined) { + for (let m = 0; m < propertyArray.length; m++) { + let childNode = propertyArray[m] + if (childNode === undefined) { continue } + let percentage = getPercentage(childNode) + if (percentage === undefined || isNaN(percentage) === true) { + percentage = defaultPercentage + } + distributeTokenPower(childNode, tokenPower * percentage / 100, percentage) + } + } + break + } + } + } + } + } + + function getPercentage(node) { + /* + There are some system wide rules regarfing mandatory percentages, + we will apply them now. + */ + switch (node.type) { + case 'Referral Program': { + return 20 + } + case 'Mentorship Program': { + return 10 + } + case 'Support Program': { + return 10 + } + default: return UI.projects.foundations.utilities.nodeConfig.loadConfigProperty(node.payload, 'percentage') + } + } + + function drawTokenPower(node, tokenPower, percentage) { + tokenPower = new Intl.NumberFormat().format(tokenPower) + ' ' + 'Token Power' + if (node.payload !== undefined) { + if (node.type === 'User Profile') { + return + } + node.payload.uiObject.valueAngleOffset = 180 + node.payload.uiObject.valueAtAngle = true + + node.payload.uiObject.setValue(tokenPower) + + if (percentage !== undefined) { + node.payload.uiObject.setPercentage(percentage.toFixed(2)) + } + } + } + } +} \ No newline at end of file diff --git a/Projects/Governance/UI/Spaces/User-Profile-Space/UserProfile.js b/Projects/Governance/UI/Spaces/User-Profile-Space/UserProfile.js index f9dd1c21d..1a5425677 100644 --- a/Projects/Governance/UI/Spaces/User-Profile-Space/UserProfile.js +++ b/Projects/Governance/UI/Spaces/User-Profile-Space/UserProfile.js @@ -53,7 +53,6 @@ function newGovernanceUserProfileSpace() { if (userProfile.payload === undefined) { continue } if (userProfile.payload.blockchainTokens === undefined) { - //userProfile.payload.blockchainTokens = UI.projects.foundations.utilities.nodeConfig.loadConfigProperty(userProfile.payload, 'tokens') getBlockchainAccount(userProfile) } } @@ -114,7 +113,7 @@ function newGovernanceUserProfileSpace() { userProfile.payload.blockchainTokens = Number(data.result) / 1000000000000000000 waitingForResponses-- }).catch(function (err) { - const message = err.message + ' - ' + 'Can not access BSC SCAN servers. Using tokens Config Property instead of blockchain data.' + const message = err.message + ' - ' + 'Can not access BSC SCAN servers.' console.log(message) userProfile.payload.uiObject.setErrorMessage(message, 1000) waitingForResponses-- diff --git a/Projects/ProjectsSchema.json b/Projects/ProjectsSchema.json index 246b26fca..76c98a4f6 100644 --- a/Projects/ProjectsSchema.json +++ b/Projects/ProjectsSchema.json @@ -963,6 +963,11 @@ "propertyName": "assets", "functionName": "newGovernanceFunctionLibraryAssets" }, + { + "name": "TokenPower", + "propertyName": "tokenPower", + "functionName": "newGovernanceFunctionLibraryTokenPower" + }, { "name": "Votes", "propertyName": "votes",