diff --git a/Client/httpInterface.js b/Client/httpInterface.js index ed08eb406..de123c106 100644 --- a/Client/httpInterface.js +++ b/Client/httpInterface.js @@ -132,6 +132,25 @@ exports.newHttpInterface = function newHttpInterface(WEB_SERVER, DATA_FILE_SERVE respondWithContent(JSON.stringify(serverResponse), httpResponse) return } + case 'signData': { + + let serverResponse = await WEB3_SERVER.signData( + params.privateKey, + params.data + ) + + respondWithContent(JSON.stringify(serverResponse), httpResponse) + return + } + case 'recoverAddress': { + + let serverResponse = await WEB3_SERVER.recoverAddress( + params.signature + ) + + respondWithContent(JSON.stringify(serverResponse), httpResponse) + return + } default: { respondWithContent(JSON.stringify({ error: 'Method ' + params.method + ' is invalid.' }), httpResponse) } diff --git a/Client/web3Server.js b/Client/web3Server.js index 0e838d04c..cf8979128 100644 --- a/Client/web3Server.js +++ b/Client/web3Server.js @@ -6,6 +6,8 @@ exports.newWeb3Server = function newWeb3Server() { getNetworkClientStatus: getNetworkClientStatus, createWalletAccount: createWalletAccount, getWalletBalances: getWalletBalances, + signData: signData, + recoverAddress: recoverAddress, initialize: initialize, finalize: finalize, run: run @@ -56,7 +58,7 @@ exports.newWeb3Server = function newWeb3Server() { return status } } catch (err) { - return { error: 'Could not connect to ' + key + '. ' + err.message + '.' } + return { error: 'Could not connect to ' + key + '. ' + err.stack + '.' } } } @@ -73,7 +75,7 @@ exports.newWeb3Server = function newWeb3Server() { } } catch (err) { - return { error: 'Could not create the account. ' + err.message } + return { error: 'Could not create the account. ' + err.stack } } } @@ -116,22 +118,22 @@ exports.newWeb3Server = function newWeb3Server() { for (let j = 0; j < walletAccount.tokenBalances.length; j++) { let tokenBalance = walletAccount.tokenBalances[j] - if (tokenBalance.referenceParent === undefined) { continue } - if (tokenBalance.referenceParent.parentNode === undefined) { continue } - if (tokenBalance.referenceParent.smartContracts === undefined) { continue } + if (tokenBalance.referenceParent === undefined) { continue } + if (tokenBalance.referenceParent.parentNode === undefined) { continue } + if (tokenBalance.referenceParent.smartContracts === undefined) { continue } if (tokenBalance.referenceParent.config.codeName === undefined) { tokenBalance.error = 'Reference Parent without config.codeName defined.' continue - } + } if (tokenBalance.referenceParent.smartContracts.config.address === undefined) { tokenBalance.error = 'Reference Parent Smart Contract without config.address defined.' continue - } + } let tokenContractAddress = tokenBalance.referenceParent.smartContracts.config.address - // The minimum ABI to get ERC20 Token balance + // The minimum ABI to get ERC20 Token balance let minABI = [ // balanceOf { @@ -152,8 +154,8 @@ exports.newWeb3Server = function newWeb3Server() { ]; let contract = new web3.eth.Contract(minABI, tokenContractAddress); - - try { + + try { tokenBalance.value = await contract.methods.balanceOf(walletAccount.config.address).call() } catch (err) { if (err.message.indexOf('Provided address') >= 0) { @@ -170,7 +172,7 @@ exports.newWeb3Server = function newWeb3Server() { return responseData } } catch (err) { - return { error: 'Could not connect to ' + key + '. ' + err.message + '.' } + return { error: 'Could not connect to ' + key + '. ' + err.stack + '.' } } } @@ -202,4 +204,39 @@ exports.newWeb3Server = function newWeb3Server() { } } } + + async function signData(privateKey, data) { + try { + + let web3 = new Web3() + let signature = web3.eth.accounts.sign(data, privateKey) + + return { + signature: signature, + result: 'Ok' + } + + } catch (err) { + return { error: 'Could not sign the data. ' + err.stack } + } + } + + async function recoverAddress(signature) { + try { + + console.log("STRING:", signature) + let signatureObject = JSON.parse(signature) + console.log("OBJECT:", signatureObject) + let web3 = new Web3() + let address = web3.eth.accounts.recover(signatureObject) + + return { + address: address, + result: 'Ok' + } + + } catch (err) { + return { error: 'Could not recover address. ' + err.stack } + } + } } \ No newline at end of file diff --git a/Projects/Ethereum/UI/Function-Libraries/Accounts.js b/Projects/Ethereum/UI/Function-Libraries/Accounts.js index cd1e36151..f57475578 100644 --- a/Projects/Ethereum/UI/Function-Libraries/Accounts.js +++ b/Projects/Ethereum/UI/Function-Libraries/Accounts.js @@ -11,7 +11,7 @@ function newEthereumFunctionLibraryAccounts() { let params = { method: 'createWalletAccount', - entropy: walletAccountNode.id + entropy: walletAccountNode.id + (new Date()).valueOf() } let url = 'WEB3' // we don't need to ask this to any specific superalgos node. diff --git a/Projects/Foundations/Schemas/Docs-Topics/H/How/How-to-Automate-Actions-at-The-Design-Space/how-to-automate-actions-at-the-design-space-005-automating-actions-using-utilities.json b/Projects/Foundations/Schemas/Docs-Topics/H/How/How-to-Automate-Actions-at-The-Design-Space/how-to-automate-actions-at-the-design-space-005-automating-actions-using-utilities.json index 0234ae4f4..827da3839 100644 --- a/Projects/Foundations/Schemas/Docs-Topics/H/How/How-to-Automate-Actions-at-The-Design-Space/how-to-automate-actions-at-the-design-space-005-automating-actions-using-utilities.json +++ b/Projects/Foundations/Schemas/Docs-Topics/H/How/How-to-Automate-Actions-at-The-Design-Space/how-to-automate-actions-at-the-design-space-005-automating-actions-using-utilities.json @@ -52,7 +52,7 @@ }, { "style": "Javascript", - "text": "UI.projects.foundations.utilities.nodeConfig.savePropertyAtNodeConfig(node.payload,'showPanels', true)" + "text": "UI.projects.foundations.utilities.nodeConfig.saveConfigProperty(node.payload,'showPanels', true)" }, { "style": "Text", diff --git a/Projects/Foundations/UI/Spaces/Panel-Space/Layer.js b/Projects/Foundations/UI/Spaces/Panel-Space/Layer.js index 7f39e9267..d25a92cdf 100644 --- a/Projects/Foundations/UI/Spaces/Panel-Space/Layer.js +++ b/Projects/Foundations/UI/Spaces/Panel-Space/Layer.js @@ -511,7 +511,7 @@ function newLayer() { } } } else { - UI.projects.foundations.utilities.nodeConfig.savePropertyAtNodeConfig(thisObject.payload, 'showPanels', panelsVisibleButton.showPanels) + UI.projects.foundations.utilities.nodeConfig.saveConfigProperty(thisObject.payload, 'showPanels', panelsVisibleButton.showPanels) } } } @@ -560,7 +560,7 @@ function newLayer() { } } - UI.projects.foundations.utilities.nodeConfig.savePropertyAtNodeConfig(thisObject.payload, 'status', thisObject.status) + UI.projects.foundations.utilities.nodeConfig.saveConfigProperty(thisObject.payload, 'status', thisObject.status) let eventData = thisObject thisObject.container.eventHandler.raiseEvent('Status Changed', eventData) } diff --git a/Projects/Foundations/UI/Spaces/Panel-Space/LayersPanel.js b/Projects/Foundations/UI/Spaces/Panel-Space/LayersPanel.js index 1eb9b2b7f..000986a47 100644 --- a/Projects/Foundations/UI/Spaces/Panel-Space/LayersPanel.js +++ b/Projects/Foundations/UI/Spaces/Panel-Space/LayersPanel.js @@ -114,7 +114,7 @@ function newLayersPanel() { } function saveObjectStateVisibleLayers() { - UI.projects.foundations.utilities.nodeConfig.savePropertyAtNodeConfig(thisObject.payload, 'visibleLayers', desiredVisibleLayers) + UI.projects.foundations.utilities.nodeConfig.saveConfigProperty(thisObject.payload, 'visibleLayers', desiredVisibleLayers) } function saveObjectStatePanelLocation() { @@ -122,7 +122,7 @@ function newLayersPanel() { upOrDown: thisObject.upDownButton.status, leftOrRight: thisObject.leftRightButton.status } - UI.projects.foundations.utilities.nodeConfig.savePropertyAtNodeConfig(thisObject.payload, 'panelLocation', panelLocation) + UI.projects.foundations.utilities.nodeConfig.saveConfigProperty(thisObject.payload, 'panelLocation', panelLocation) } function readObjectState() { diff --git a/Projects/Foundations/UI/Utilities/NodeConfig.js b/Projects/Foundations/UI/Utilities/NodeConfig.js index 3a0dda7f7..865c3aba1 100644 --- a/Projects/Foundations/UI/Utilities/NodeConfig.js +++ b/Projects/Foundations/UI/Utilities/NodeConfig.js @@ -1,12 +1,12 @@ function newFoundationsUtilitiesNodeConfig() { let thisObject = { - savePropertyAtNodeConfig: savePropertyAtNodeConfig, + saveConfigProperty: saveConfigProperty, loadConfigProperty: loadConfigProperty } return thisObject - function savePropertyAtNodeConfig(payload, propertyName, value) { + function saveConfigProperty(payload, propertyName, value) { try { let config = JSON.parse(payload.node.config) config[propertyName] = value diff --git a/Projects/Governance/Icons/signature.png b/Projects/Governance/Icons/profile-constructor.png similarity index 100% rename from Projects/Governance/Icons/signature.png rename to Projects/Governance/Icons/profile-constructor.png diff --git a/Projects/Governance/Plugins/User-Profiles/Luis-Fernando-Molina.json b/Projects/Governance/Plugins/User-Profiles/Luis-Fernando-Molina.json index 323766f68..80afbc6f3 100644 --- a/Projects/Governance/Plugins/User-Profiles/Luis-Fernando-Molina.json +++ b/Projects/Governance/Plugins/User-Profiles/Luis-Fernando-Molina.json @@ -1,7 +1,7 @@ { "type": "User Profile", "name": "Luis-Fernando-Molina", - "config": "{\n \"tokens\": 10000000,\n\"blockchainAccount\": \"0x9546eAA9380A224a56d7067D712296E9D40d40e1\"\n}", + "config": "{\n \"tokens\": 10000000,\n \"signature\": {\n \"message\": \"Luis-Fernando-Molina\",\n \"messageHash\": \"0x443abf3389e332a62d21b84dd12e271cb428d334195d3aeb986ca85ee53cccb9\",\n \"v\": \"0x1b\",\n \"r\": \"0x90293b042c180aa21b05f0356f31688c597be4ff2df136f63a2b8a899c658eb0\",\n \"s\": \"0x42abf909024d34217430e42ebadc96b7834218cc86a4c295a2ae419ff2fca6e3\",\n \"signature\": \"0x90293b042c180aa21b05f0356f31688c597be4ff2df136f63a2b8a899c658eb042abf909024d34217430e42ebadc96b7834218cc86a4c295a2ae419ff2fca6e31b\"\n }\n}", "project": "Governance", "contributionClaims": { "type": "Contribution Claims", @@ -3932,7 +3932,7 @@ "floatingObject": { "isPinned": false, "isFrozen": false, - "isCollapsed": true, + "isCollapsed": false, "angleToParent": 1, "distanceToParent": 3, "arrangementStyle": 0 diff --git a/Projects/Governance/Schemas/App-Schema/governance-project.json b/Projects/Governance/Schemas/App-Schema/governance-project.json index b5e72eeca..03d3ba270 100644 --- a/Projects/Governance/Schemas/App-Schema/governance-project.json +++ b/Projects/Governance/Schemas/App-Schema/governance-project.json @@ -36,6 +36,13 @@ "relatedUiObject": "User Profile", "actionFunction": "payload.executeAction" }, + { + "action": "Add UI Object", + "actionProject": "Foundations", + "actionFunction": "payload.executeAction", + "label": "Add Profile Constructor", + "relatedUiObject": "Profile Constructor" + }, { "action": "Delete UI Object", "actionProject": "Foundations", diff --git a/Projects/Governance/Schemas/App-Schema/Signature.json b/Projects/Governance/Schemas/App-Schema/profile-constructor.json similarity index 62% rename from Projects/Governance/Schemas/App-Schema/Signature.json rename to Projects/Governance/Schemas/App-Schema/profile-constructor.json index 0977983ea..40dae30b2 100644 --- a/Projects/Governance/Schemas/App-Schema/Signature.json +++ b/Projects/Governance/Schemas/App-Schema/profile-constructor.json @@ -1,5 +1,5 @@ { - "type": "Signature", + "type": "Profile Constructor", "menuItems": [ { "action": "Configure", @@ -8,6 +8,16 @@ "iconPathOn": "configuration", "iconPathOff": "configuration" }, + { + "action": "Build Profile", + "actionProject": "Governance", + "label": "Build Profile", + "askConfirmation": true, + "confirmationLabel": "Confirm to Proceed", + "workDoneLabel": "Done", + "relatedUiObject": "Profile Constructor", + "actionFunction": "payload.executeAction" + }, { "action": "Delete UI Object", "actionProject": "Foundations", @@ -19,11 +29,12 @@ "actionFunction": "payload.executeAction" } ], + "isHierarchyHead": true, "editors": { "config": true }, "initialValues": { - "config": "{ \"privateKey\": \"\"}" + "config": "{ \"githubUsername\": \"\"}" }, "addLeftIcons": true, "level": 2, diff --git a/Projects/Governance/Schemas/App-Schema/user-profile.json b/Projects/Governance/Schemas/App-Schema/user-profile.json index d966c9166..53f1dafea 100644 --- a/Projects/Governance/Schemas/App-Schema/user-profile.json +++ b/Projects/Governance/Schemas/App-Schema/user-profile.json @@ -35,13 +35,6 @@ "label": "Add User Referrer", "relatedUiObject": "User Referrer" }, - { - "action": "Add UI Object", - "actionProject": "Signature", - "actionFunction": "payload.executeAction", - "label": "Add Signature", - "relatedUiObject": "Add Signature" - }, { "action": "Delete UI Object", "actionProject": "Foundations", diff --git a/Projects/Governance/UI/Function-Libraries/ActionSwitch.js b/Projects/Governance/UI/Function-Libraries/ActionSwitch.js index 378790fc2..4dd5ba618 100644 --- a/Projects/Governance/UI/Function-Libraries/ActionSwitch.js +++ b/Projects/Governance/UI/Function-Libraries/ActionSwitch.js @@ -28,6 +28,11 @@ function newGovernanceActionSwitch() { UI.projects.governance.functionLibraries.votes.installVotes(action.node, action.rootNodes) break } + case 'Build Profile': + { + UI.projects.governance.functionLibraries.profileConstructor.buildProfile(action.node, action.rootNodes) + break + } case 'Add Missing Plugin User Profiles': { UI.projects.governance.functionLibraries.pluginsFunctions.addMissingPluginUserProfiles(action.node, action.rootNodes) diff --git a/Projects/Governance/UI/Function-Libraries/ProfileConstructor.js b/Projects/Governance/UI/Function-Libraries/ProfileConstructor.js new file mode 100644 index 000000000..48106de7e --- /dev/null +++ b/Projects/Governance/UI/Function-Libraries/ProfileConstructor.js @@ -0,0 +1,97 @@ +function newGovernanceFunctionLibraryProfileConstructor() { + let thisObject = { + buildProfile: buildProfile + } + + return thisObject + + function buildProfile( + node, + rootNodes + ) { + /* + Some validations first... + */ + if (node.payload.referenceParent === undefined) { + node.payload.uiObject.setErrorMessage("You need to reference the node that contains the data to be signed.") + return + } + let data = UI.projects.foundations.utilities.nodeConfig.loadConfigProperty(node.payload, 'githubUsername') + + if (data === undefined || data === "") { + node.payload.referenceParent.payload.uiObject.setErrorMessage("githubUsername config property missing.") + return + } + + createNewAccount() + signUserProfileData() + + function createNewAccount() { + let params = { + method: 'createWalletAccount', + entropy: node.id + (new Date()).valueOf() + } + + let url = 'WEB3' // we don't need to ask this to any specific superalgos node. + + httpRequest(JSON.stringify(params), url, onResponse) + + function onResponse(err, data) { + /* Lets check the result of the call through the http interface */ + if (err.result !== GLOBAL.DEFAULT_OK_RESPONSE.result) { + node.payload.uiObject.setErrorMessage('Call via HTTP Interface failed.') + walletAccountNode.payload.uiObject.menu.internalClick('Delete UI Object') + return + } + + let response = JSON.parse(data) + + /* Lets check the result of the method call */ + if (response.result !== GLOBAL.DEFAULT_OK_RESPONSE.result) { + node.payload.uiObject.setErrorMessage('Call to WEB3 Server failed. ' + response.error) + return + } + + UI.projects.foundations.utilities.nodeConfig.saveConfigProperty(node.payload, 'address', response.address) + UI.projects.foundations.utilities.nodeConfig.saveConfigProperty(node.payload, 'privateKey', response.privateKey) + } + } + + function signUserProfileData() { + + let privateKey = UI.projects.foundations.utilities.nodeConfig.loadConfigProperty(node.payload, 'privateKey') + if (privateKey === undefined || privateKey === "") { return } + + let request = { + url: 'WEB3', + params: { + method: "signData", + privateKey: privateKey, + data: data + } + } + + httpRequest(JSON.stringify(request.params), request.url, onResponse) + + function onResponse(err, data) { + /* Lets check the result of the call through the http interface */ + if (err.result !== GLOBAL.DEFAULT_OK_RESPONSE.result) { + node.payload.uiObject.setErrorMessage('Call via HTTP Interface failed.') + return + } + + let response = JSON.parse(data) + + /* Lets check the result of the method call */ + if (response.result !== GLOBAL.DEFAULT_OK_RESPONSE.result) { + node.payload.uiObject.setErrorMessage('Call to WEB3 Server failed. ' + response.error) + console.log('Call to WEB3 Server failed. ' + response.error) + return + } + UI.projects.foundations.utilities.nodeConfig.saveConfigProperty(node.payload.referenceParent.payload, 'signature', response.signature) + + node.payload.uiObject.setInfoMessage("Profile Account has been successfully created.") + } + } + } +} diff --git a/Projects/Governance/UI/Function-Libraries/UserReferrals.js b/Projects/Governance/UI/Function-Libraries/UserReferrals.js index a3c1927eb..289f56a35 100644 --- a/Projects/Governance/UI/Function-Libraries/UserReferrals.js +++ b/Projects/Governance/UI/Function-Libraries/UserReferrals.js @@ -117,9 +117,9 @@ function newGovernanceFunctionLibraryUserReferrals() { function distributeForProfile(userProfile) { if (userProfile === undefined || userProfile.payload === undefined) { return } - let referringPower + let referringPower if (userProfile.payload.blockchainTokens === undefined) { - referringPower = UI.projects.foundations.utilities.nodeConfig.loadConfigProperty(userProfile.payload, 'tokens') + return } else { referringPower = userProfile.payload.blockchainTokens } diff --git a/Projects/Governance/UI/Function-Libraries/Votes.js b/Projects/Governance/UI/Function-Libraries/Votes.js index cd5d296f5..13da3b0a0 100644 --- a/Projects/Governance/UI/Function-Libraries/Votes.js +++ b/Projects/Governance/UI/Function-Libraries/Votes.js @@ -118,7 +118,7 @@ function newGovernanceFunctionLibraryVotes() { if (userProfile.payload === undefined) { return } let votes if (userProfile.payload.blockchainTokens === undefined) { - votes = UI.projects.foundations.utilities.nodeConfig.loadConfigProperty(userProfile.payload, 'tokens') + return } else { votes = userProfile.payload.blockchainTokens } diff --git a/Projects/Governance/UI/Spaces/User-Profile-Space/UserProfile.js b/Projects/Governance/UI/Spaces/User-Profile-Space/UserProfile.js index 2480eb542..f9dd1c21d 100644 --- a/Projects/Governance/UI/Spaces/User-Profile-Space/UserProfile.js +++ b/Projects/Governance/UI/Spaces/User-Profile-Space/UserProfile.js @@ -53,18 +53,58 @@ function newGovernanceUserProfileSpace() { if (userProfile.payload === undefined) { continue } if (userProfile.payload.blockchainTokens === undefined) { - let blockchainAccount = UI.projects.foundations.utilities.nodeConfig.loadConfigProperty(userProfile.payload, 'blockchainAccount') - if (blockchainAccount !== undefined && blockchainAccount !== "") { - setTimeout(getBlockchainTokens, timer, userProfile) + //userProfile.payload.blockchainTokens = UI.projects.foundations.utilities.nodeConfig.loadConfigProperty(userProfile.payload, 'tokens') + getBlockchainAccount(userProfile) + } + } + + function getBlockchainAccount(userProfile) { + let signature = UI.projects.foundations.utilities.nodeConfig.loadConfigProperty(userProfile.payload, 'signature') + if (signature === undefined || signature === "") { return } + + let request = { + url: 'WEB3', + params: { + method: "recoverAddress", + signature: JSON.stringify(signature) + } + } + + httpRequest(JSON.stringify(request.params), request.url, onResponse) + + function onResponse(err, data) { + /* Lets check the result of the call through the http interface */ + if (err.result !== GLOBAL.DEFAULT_OK_RESPONSE.result) { + userProfile.payload.uiObject.setErrorMessage('Call via HTTP Interface failed.') + return + } + + let response = JSON.parse(data) + + /* Lets check the result of the method call */ + if (response.result !== GLOBAL.DEFAULT_OK_RESPONSE.result) { + userProfile.payload.uiObject.setErrorMessage('Call to WEB3 Server failed. ' + response.error) + console.log('Call to WEB3 Server failed. ' + response.error) + return + } + + let blockchainAccount = response.address + if ( + blockchainAccount !== undefined && + blockchainAccount !== "" && + userProfile.payload.blockchainTokens === undefined + ) { + waitingForResponses++ + userProfile.payload.blockchainTokens = 0 // We need to set this value here so that the next call to BSCSCAN is not done more than once. + setTimeout(getBlockchainTokens, timer, userProfile, blockchainAccount) timer = timer + 5000 } } } - function getBlockchainTokens(userProfile) { - let blockchainAccount = UI.projects.foundations.utilities.nodeConfig.loadConfigProperty(userProfile.payload, 'blockchainAccount') + function getBlockchainTokens(userProfile, blockchainAccount) { + console.log('blockchainAccount ', blockchainAccount) const url = "https://api.bscscan.com/api?module=account&action=tokenbalance&contractaddress=0xfb981ed9a92377ca4d75d924b9ca06df163924fd&address=" + blockchainAccount + "&tag=latest&apikey=YourApiKeyToken" - waitingForResponses++ fetch(url).then(function (response) { return response.json(); @@ -74,9 +114,9 @@ function newGovernanceUserProfileSpace() { userProfile.payload.blockchainTokens = Number(data.result) / 1000000000000000000 waitingForResponses-- }).catch(function (err) { - userProfile.payload.blockchainTokens = 0 - console.log(err) - userProfile.payload.uiObject.setErrorMessage(err.message) + const message = err.message + ' - ' + 'Can not access BSC SCAN servers. Using tokens Config Property instead of blockchain data.' + console.log(message) + userProfile.payload.uiObject.setErrorMessage(message, 1000) waitingForResponses-- }); } diff --git a/Projects/ProjectsSchema.json b/Projects/ProjectsSchema.json index 7ba6c94d5..246b26fca 100644 --- a/Projects/ProjectsSchema.json +++ b/Projects/ProjectsSchema.json @@ -997,6 +997,11 @@ "name": "User Referrals", "propertyName": "userrReferrals", "functionName": "newGovernanceFunctionLibraryUserReferrals" + }, + { + "name": "Profile Constructor", + "propertyName": "profileConstructor", + "functionName": "newGovernanceFunctionLibraryProfileConstructor" } ], "utilities": [],