Merge branch 'plugins-docs' of https://github.com/Superalgos/Superalgos into plugins-docs

This commit is contained in:
thaissmolina75 committed 2021-08-18 22:41:26 -04:00
commit 38e464edaf
73 files changed
+19112 -13537

No files matched your search

+73
View File
@@ -0,0 +1,73 @@
exports.newMultiProject = function () {
let thisObject = {
initialize: initialize
}
return thisObject;
function initialize() {
/*
First thing is to load the project schema file.
*/
let PROJECTS_SCHEMA = require(global.env.PATH_TO_PROJECTS_REQUIRED + '/' + 'ProjectsSchema.json')
/*
Here we will setup the CL object, with all the
projects and modules that will have inside.
*/
global.CL = {
projects: {}
}
for (let i = 0; i < PROJECTS_SCHEMA.length; i++) {
let projectDefinition = PROJECTS_SCHEMA[i]
global.CL.projects[projectDefinition.propertyName] = {}
let projectInstance = global.CL.projects[projectDefinition.propertyName]
projectInstance.utilities = {}
projectInstance.globals = {}
projectInstance.functionLibraries = {}
projectInstance.taskModules = {}
projectInstance.processModules = {}
projectInstance.botModules = {}
if (projectDefinition.CL === undefined) { continue }
/* Set up Utilities of this Project */
if (projectDefinition.CL.utilities !== undefined) {
for (let j = 0; j < projectDefinition.CL.utilities.length; j++) {
let utilityDefinition = projectDefinition.CL.utilities[j]
let path = global.env.PATH_TO_PROJECTS_REQUIRED + '/' + projectDefinition.name + '/' + 'CL' + '/' + 'Utilities' + '/' + utilityDefinition.fileName
let requiredObject = require(path)
let requiredFunction = requiredObject[utilityDefinition.functionName]
projectInstance.utilities[utilityDefinition.propertyName] = requiredFunction.call()
}
}
/* Set up Globals of this Project */
if (projectDefinition.CL.globals !== undefined) {
for (let j = 0; j < projectDefinition.CL.globals.length; j++) {
let globalDefinition = projectDefinition.CL.globals[j]
let path = global.env.PATH_TO_PROJECTS_REQUIRED + '/' + projectDefinition.name + '/' + 'CL' + '/' + 'Globals' + '/' + globalDefinition.fileName
let requiredObject = require(path)
let requiredFunction = requiredObject[globalDefinition.functionName]
projectInstance.globals[globalDefinition.propertyName] = requiredFunction.call()
}
}
/* Set up Function Libraries of this Project */
if (projectDefinition.CL.functionLibraries !== undefined) {
for (let j = 0; j < projectDefinition.CL.functionLibraries.length; j++) {
let functionLibraryDefinition = projectDefinition.CL.functionLibraries[j]
let path = global.env.PATH_TO_PROJECTS_REQUIRED + '/' + projectDefinition.name + '/' + 'CL' + '/' + 'Function-Libraries' + '/' + functionLibraryDefinition.fileName
let requiredObject = require(path)
let requiredFunction = requiredObject[functionLibraryDefinition.functionName]
projectInstance.functionLibraries[functionLibraryDefinition.propertyName] = requiredFunction.call()
}
}
}
}
}
+32 -3
View File
@@ -64,12 +64,27 @@ let EVENT_SERVER = require('./eventServer.js')
let TASK_MANAGER_SERVER = require('./taskManagerServer.js')
let CCXT_SERVER = require('./ccxtServer.js')
let WEB3_SERVER = require('./web3Server.js')
let GITHUB_SERVER = require('./githubServer.js')
/* Network Interfaces */
let WEB_SOCKETS_INTERFACE = require('./webSocketsServer.js')
let HTTP_INTERFACE = require('./httpInterface.js')
try {
/*
Setting up the modules that will be available for the Servers Running inside this Client
*/
let MULTI_PROJECT = require('./MultiProject.js');
let MULTI_PROJECT_MODULE = MULTI_PROJECT.newMultiProject()
MULTI_PROJECT_MODULE.initialize()
/*
Setting up external dependencies.
*/
CL.nodeModules = {
fs: require('fs'),
nodeFetch: require('node-fetch')
}
console.log('CLIENT SERVERS:')
console.log('')
@@ -87,7 +102,7 @@ try {
PROJECT_FILE_SERVER.initialize()
PROJECT_FILE_SERVER.run()
console.log('Project File Server ......................................... Started')
PLUGIN_SERVER = PLUGIN_SERVER.newPluginServer()
PLUGIN_SERVER.initialize()
PLUGIN_SERVER.run()
@@ -112,12 +127,17 @@ try {
CCXT_SERVER.initialize()
CCXT_SERVER.run()
console.log('CCXT Server ................................................. Started')
WEB3_SERVER = WEB3_SERVER.newWeb3Server()
WEB3_SERVER.initialize()
WEB3_SERVER.run()
console.log('WEB3 Server ................................................. Started')
GITHUB_SERVER = GITHUB_SERVER.newGithubServer()
GITHUB_SERVER.initialize()
GITHUB_SERVER.run()
console.log('Github Server ............................................... Started')
console.log('')
console.log('CLIENT INTERFACES:')
console.log('')
@@ -127,7 +147,16 @@ try {
WEB_SOCKETS_INTERFACE.run()
console.log('Web Sockets Interface ....................................... Listening at port ' + global.env.WEB_SOCKETS_INTERFACE_PORT)
HTTP_INTERFACE = HTTP_INTERFACE.newHttpInterface(WEB_SERVER, DATA_FILE_SERVER, PROJECT_FILE_SERVER, UI_FILE_SERVER, PLUGIN_SERVER, CCXT_SERVER, WEB3_SERVER)
HTTP_INTERFACE = HTTP_INTERFACE.newHttpInterface(
WEB_SERVER,
DATA_FILE_SERVER,
PROJECT_FILE_SERVER,
UI_FILE_SERVER,
PLUGIN_SERVER,
CCXT_SERVER,
WEB3_SERVER,
GITHUB_SERVER
)
HTTP_INTERFACE.initialize()
HTTP_INTERFACE.run()
console.log('Http Interface .............................................. Listening at port ' + global.env.HTTP_INTERFACE_PORT)
+585
View File
@@ -0,0 +1,585 @@
exports.newGithubServer = function newGithubServer() {
let thisObject = {
getGithubStars: getGithubStars,
getGithubWatchers: getGithubWatchers,
getGithubForks: getGithubForks,
mergePullRequests: mergePullRequests,
initialize: initialize,
finalize: finalize,
run: run
}
const GITHUB_API_WAITING_TIME = 250
return thisObject
function finalize() {
}
function initialize() {
}
function run() {
}
async function getGithubStars(repository, username, token) {
return await getGithubUsernames('activity', 'listStargazersForRepo', repository, username, token)
}
async function getGithubWatchers(repository, username, token) {
return await getGithubUsernames('activity', 'listWatchersForRepo', repository, username, token)
}
async function getGithubForks(repository, username, token) {
return await getGithubUsernames('repos', 'listForks', repository, username, token)
}
async function mergePullRequests(commitMessage, username, token) {
return await mergeGithubPullRequests(commitMessage, username, token)
}
async function getGithubUsernames(endpoint, method, repository, username, token) {
try {
repository = unescape(repository)
username = unescape(username)
token = unescape(token)
let error
let githubListArray = []
return await getRepoInfo()
async function getRepoInfo() {
await doGithub()
if (error !== undefined) {
let docs = {
project: 'Governance',
category: 'Topic',
type: 'Gov Error - Get Repository Information',
anchor: undefined,
placeholder: {}
}
return respondWithDocsObject(docs, error)
}
return {
result: global.DEFAULT_OK_RESPONSE.result,
githubListArray: githubListArray
}
}
async function doGithub() {
const { Octokit } = require("@octokit/rest")
const octokit = new Octokit({
auth: token,
userAgent: 'Superalgos Beta 11'
})
await getList()
async function getList() {
const repo = repository
const owner = 'Superalgos'
const per_page = 100 // Max
let page = 0
let lastPage = false
while (lastPage === false) {
try {
page++
await CL.projects.foundations.utilities.asyncFunctions.sleep(GITHUB_API_WAITING_TIME)
let listResponse = await octokit[endpoint][method]({
owner,
repo,
per_page,
page
});
if (listResponse.data.length < 100) {
lastPage = true
}
for (let i = 0; i < listResponse.data.length; i++) {
let listItem = listResponse.data[i]
let githubUsername
switch (endpoint) {
case 'activity': {
githubUsername = listItem.login
break
}
case 'repos': {
githubUsername = listItem.owner.login
break
}
}
//console.log(listItem)
githubListArray.push(githubUsername)
}
console.log('[INFO] Github Server -> getRepoInfo -> doGithub -> getList -> ' + method + ' Page = ' + page)
console.log('[INFO] Github Server -> getRepoInfo -> doGithub -> getList -> ' + method + ' Received = ' + listResponse.data.length)
} catch (err) {
console.log(err)
if (err.stack.indexOf('last page') >= 0) {
return
} else {
console.log('[ERROR] Github Server -> getRepoInfo -> doGithub -> getList ->Method call produced an error.')
console.log('[ERROR] Github Server -> getRepoInfo -> doGithub -> getList ->err.stack = ' + err.stack)
console.log('[ERROR] Github Server -> getRepoInfo -> doGithub -> getList ->repository = ' + repository)
console.log('[ERROR] Github Server -> getRepoInfo -> doGithub -> getList ->username = ' + username)
console.log('[ERROR] Github Server -> getRepoInfo -> doGithub -> getList ->token starts with = ' + token.substring(0, 10) + '...')
console.log('[ERROR] Github Server -> getRepoInfo -> doGithub -> getList ->token ends with = ' + '...' + token.substring(token.length - 10))
error = err
return
}
}
}
}
}
} catch (err) {
console.log('[ERROR] Github Server -> getRepoInfo -> Method call produced an error.')
console.log('[ERROR] Github Server -> getRepoInfo -> err.stack = ' + err.stack)
console.log('[ERROR] Github Server -> getRepoInfo -> repository = ' + repository)
console.log('[ERROR] Github Server -> getRepoInfo -> username = ' + username)
console.log('[ERROR] Github Server -> getRepoInfo -> token starts with = ' + token.substring(0, 10) + '...')
console.log('[ERROR] Github Server -> getRepoInfo -> token ends with = ' + '...' + token.substring(token.length - 10))
let error = {
result: 'Fail Because',
message: err.message,
stack: err.stack
}
return error
}
}
async function mergeGithubPullRequests(commitMessage, username, token) {
try {
commitMessage = unescape(commitMessage)
username = unescape(username)
token = unescape(token)
const repository = 'Superalgos'
/* Unsavping # */
for (let i = 0; i < 10; i++) {
commitMessage = commitMessage.replace('_SLASH_', '/')
commitMessage = commitMessage.replace('_HASHTAG_', '#')
}
let error
let githubPrListArray = []
return await processOpenPullRequests()
async function processOpenPullRequests() {
await doGithub()
if (error !== undefined) {
let docs = {
project: 'Governance',
category: 'Topic',
type: 'Gov Error - Pull Requests Not Processed',
anchor: undefined,
placeholder: {}
}
return respondWithDocsObject(docs, error)
}
return {
result: global.DEFAULT_OK_RESPONSE.result
}
}
async function doGithub() {
try {
const repo = repository
const owner = 'Superalgos'
const { Octokit } = require("@octokit/rest")
const octokit = new Octokit({
auth: token,
userAgent: 'Superalgos Beta 11'
})
await getPrList()
await mergePrs()
async function getPrList() {
const per_page = 100 // Max
let page = 0
let lastPage = false
while (lastPage === false) {
try {
page++
await CL.projects.foundations.utilities.asyncFunctions.sleep(GITHUB_API_WAITING_TIME)
let listResponse = await octokit.rest.pulls.list({
owner: owner,
repo: repo,
state: 'open',
head: 'develop',
base: 'develop',
sort: undefined,
direction: undefined,
per_page: per_page,
page: page
});
if (listResponse.data.length < 100) {
lastPage = true
}
console.log('[INFO] Github Server -> mergeGithubPullRequests -> doGithub -> getPrList -> Receiving Page = ' + page)
for (let i = 0; i < listResponse.data.length; i++) {
let pullRequest = listResponse.data[i]
console.log('[INFO] Github Server -> mergeGithubPullRequests -> doGithub -> getPrList -> Pull Request "' + pullRequest.title + '" found and added to the list to validate. ')
githubPrListArray.push(pullRequest)
}
console.log('[INFO] Github Server -> mergeGithubPullRequests -> doGithub -> getPrList -> Received = ' + listResponse.data.length)
} catch (err) {
console.log(err)
if (err.stack.indexOf('last page') >= 0) {
return
} else {
console.log('[ERROR] Github Server -> mergeGithubPullRequests -> doGithub -> getPrList ->Method call produced an error.')
console.log('[ERROR] Github Server -> mergeGithubPullRequests -> doGithub -> getPrList ->err.stack = ' + err.stack)
console.log('[ERROR] Github Server -> mergeGithubPullRequests -> doGithub -> getPrList ->repository = ' + repository)
console.log('[ERROR] Github Server -> mergeGithubPullRequests -> doGithub -> getPrList ->username = ' + username)
console.log('[ERROR] Github Server -> mergeGithubPullRequests -> doGithub -> getPrList ->token starts with = ' + token.substring(0, 10) + '...')
console.log('[ERROR] Github Server -> mergeGithubPullRequests -> doGithub -> getPrList ->token ends with = ' + '...' + token.substring(token.length - 10))
error = err
return
}
}
}
}
async function mergePrs() {
/*
In order to automatically merge a PRs we will run several validations first.
We want to automate specifically the merging of Profile File changes at the
Governance System done by the same Github user name that matches the profile
changed. We will also not do the merge if there are more than one file at the
PR.
We will go through the list of open PRs and run the validations at each one of them.
*/
console.log('[INFO] Github Server -> mergeGithubPullRequests -> Ready to Validate ' + githubPrListArray.length + ' pull requests. ')
for (let i = 0; i < githubPrListArray.length; i++) {
let pullRequest = githubPrListArray[i]
/*
Lets get the files changed at this Pull Request.
*/
await CL.projects.foundations.utilities.asyncFunctions.sleep(GITHUB_API_WAITING_TIME)
let listResponse = await octokit.rest.pulls.listFiles({
owner: owner,
repo: repo,
pull_number: pullRequest.number
});
let filesChanged = listResponse.data
let fileContentUrl // URL to the only file at the PR
let fileContent // File content of the only file at the PR
let userProfile // User Profile Object
let githubUsername // The Github user name of who is submitting the Pull Request
let mergeResponse // The response received from the call to Gihub to merge the Pull Request
if (await validatePrHasMoreThanOneFile() === false) { continue }
if (await validateFileNameEqualsGithubUsername() === false) { continue }
if (await validateFileChangedIsUserProfile() === false) { continue }
if (await validateMessageSignedIsGithubUsername() === false) { continue }
if (await validateUserProfileNodeNameEqualsGithubUsername() === false) { continue }
if (await validateUserProfileIdDoesNotBelongtoAnotherUserProfile() === false) { continue }
if (await mergePullRequest() === false) {
console.log('[WARN] Github Server -> mergeGithubPullRequests -> Merge Failed -> Pull Request "' + pullRequest.title + '" not merged because Github could not merge it. -> mergeResponse.message = ' + mergeResponse.data.message)
} else {
console.log('[INFO] Github Server -> mergeGithubPullRequests -> Merge Succed -> Pull Request "' + pullRequest.title + '" successfully merged. -> mergeResponse.message = ' + mergeResponse.data.message)
await CL.projects.foundations.utilities.asyncFunctions.sleep(GITHUB_API_WAITING_TIME)
await octokit.rest.issues.createComment({
owner: owner,
repo: repo,
issue_number: pullRequest.number,
body: 'This Pull Request was automatically merged by the Superalgos Governance System because it was detected that the Github User " ' + githubUsername + '" who submitted it, modified its own User Profile Plugin File and nothning else but that file. All validations were successfull.'
});
}
async function validatePrHasMoreThanOneFile() {
/*
Validation #1: If the PR has more than one file, then we will not automatically
merge it.
*/
if (filesChanged.length !== 1) {
console.log('[INFO] Github Server -> mergeGithubPullRequests -> Validation #1 Failed -> Pull Request "' + pullRequest.title + '" not merged because it contains more than 1 file. -> fileCount = ' + filesChanged.length)
/*
We will close PRs that contains any User Profile file together with other files in the same Pull Request.
This will avoid manual merges to include User Profile files.
*/
for (let j = 0; j < filesChanged.length; j++) {
let pullRequestFile = filesChanged[j]
let fileContentUrl = pullRequestFile.raw_url
if (fileContentUrl.indexOf('Governance/Plugins/User-Profiles') >= 0) {
await CL.projects.foundations.utilities.asyncFunctions.sleep(GITHUB_API_WAITING_TIME)
await octokit.rest.issues.createComment({
owner: owner,
repo: repo,
issue_number: pullRequest.number,
body: 'This Pull Request could not be automatically merged and was closed by the Superalgos Governance System because it was detected that a User Profile file... \n\n' + fileContentUrl + '\n\n...was submitted together with ' + (filesChanged.length - 1) + ' other file/s. User Profiles files as per the Governance System rules, must be the only file present at a Pull Request in order to pass all the validations and be automatically merged.'
});
await CL.projects.foundations.utilities.asyncFunctions.sleep(GITHUB_API_WAITING_TIME)
await octokit.rest.pulls.update({
owner: owner,
repo: repo,
pull_number: pullRequest.number,
state: 'closed'
});
break
}
}
return false
}
}
async function validateFileNameEqualsGithubUsername() {
/*
Validation #2: File Name must be the same to the Github Username of the PR owner.
*/
let pullRequestFile = filesChanged[0]
fileContentUrl = pullRequestFile.raw_url
if (fileContentUrl.indexOf('Governance/Plugins/User-Profiles') < 0) {
/*
If it is not a user profile then there is no need to auto merge.
*/
console.log('[INFO] Github Server -> mergeGithubPullRequests -> Validation #2 Failed -> Pull Request "' + pullRequest.title + '" not merged because the file modified at the Pull Request is not a User Profile file. -> fileContentUrl = ' + fileContentUrl)
return false
}
let splittedURL = fileContentUrl.split('/')
let fileName = splittedURL[splittedURL.length - 1]
let splittedFileName = fileName.split('.')
fileName = splittedFileName[0]
githubUsername = pullRequest.user.login
if (githubUsername !== fileName) {
console.log('[INFO] Github Server -> mergeGithubPullRequests -> Validation #2 Failed -> Pull Request "' + pullRequest.title + '" not merged because the Github Username is not equal to the File Name. -> Github Username = ' + githubUsername + '-> fileName = ' + fileName)
await CL.projects.foundations.utilities.asyncFunctions.sleep(GITHUB_API_WAITING_TIME)
await octokit.rest.issues.createComment({
owner: owner,
repo: repo,
issue_number: pullRequest.number,
body: 'This Pull Request could not be automatically merged and was closed by the Superalgos Governance System because it was detected that the Github User "' + githubUsername + '" who submitted it, is not equal to the name of the User Profile Plugin File.\n\n File Name = "' + fileName + '"'
});
await CL.projects.foundations.utilities.asyncFunctions.sleep(GITHUB_API_WAITING_TIME)
await octokit.rest.pulls.update({
owner: owner,
repo: repo,
pull_number: pullRequest.number,
state: 'closed'
});
return false
}
}
async function validateFileChangedIsUserProfile() {
/*
Validation #3: The file changed at the PR is a User Profile.
*/
await CL.projects.foundations.utilities.asyncFunctions.sleep(GITHUB_API_WAITING_TIME)
fileContent = await CL.projects.foundations.utilities.webAccess.fetchAPIDataFile(fileContentUrl)
userProfile = JSON.parse(fileContent)
if (userProfile.type !== 'User Profile') {
console.log('[INFO] Github Server -> mergeGithubPullRequests -> Validation #3 Failed -> Pull Request "' + pullRequest.title + '" not merged because the file modified is not a User Profile. -> Type = ' + userProfile.type)
return false
}
}
async function validateMessageSignedIsGithubUsername() {
/*
Validation #4: The message signed at the config is not the Github Username.
*/
let config = JSON.parse(userProfile.config)
let messageSigned = config.signature.message
if (messageSigned !== githubUsername) {
console.log('[INFO] Github Server -> mergeGithubPullRequests -> Validation #4 Failed -> Pull Request "' + pullRequest.title + '" not merged because the Github Username is not equal to the Message Signed at the User Profile. -> Github Username = ' + githubUsername + '-> messageSigned = ' + messageSigned)
await CL.projects.foundations.utilities.asyncFunctions.sleep(GITHUB_API_WAITING_TIME)
await octokit.rest.issues.createComment({
owner: owner,
repo: repo,
issue_number: pullRequest.number,
body: 'This Pull Request could not be automatically merged and was closed by the Superalgos Governance System because it was detected that the Github User "' + githubUsername + '" who submitted it, is not equal to the Message Signed at the User Profile.\n\n Message Signed = "' + messageSigned + '"'
});
await CL.projects.foundations.utilities.asyncFunctions.sleep(GITHUB_API_WAITING_TIME)
await octokit.rest.pulls.update({
owner: owner,
repo: repo,
pull_number: pullRequest.number,
state: 'closed'
});
return false
}
}
async function validateUserProfileNodeNameEqualsGithubUsername() {
/*
Validation #5: The name of the User Profile node is not the Github Username.
*/
if (userProfile.name !== githubUsername) {
console.log('[INFO] Github Server -> mergeGithubPullRequests -> Validation #5 Failed -> Pull Request "' + pullRequest.title + '" not merged because the Github Username is not equal to the User Profile node\'s name. -> Github Username = ' + githubUsername + '-> userProfile.name = ' + userProfile.name)
await CL.projects.foundations.utilities.asyncFunctions.sleep(GITHUB_API_WAITING_TIME)
await octokit.rest.issues.createComment({
owner: owner,
repo: repo,
issue_number: pullRequest.number,
body: 'This Pull Request could not be automatically merged and was closed by the Superalgos Governance System because it was detected that the Github User "' + githubUsername + '" who submitted it, is not equal to the User Profile node\'s name.\n\n User Profile Name = "' + userProfile.name + '"'
});
await CL.projects.foundations.utilities.asyncFunctions.sleep(GITHUB_API_WAITING_TIME)
await octokit.rest.pulls.update({
owner: owner,
repo: repo,
pull_number: pullRequest.number,
state: 'closed'
});
return false
}
}
async function validateUserProfileIdDoesNotBelongtoAnotherUserProfile() {
/*
Validation #6: The id of the User Profile can not be the id of an already existing User Profile
unless the existing one belongs to the same Github username.
*/
let userProfileIdMap = new Map()
let pluginFileNames = await CL.projects.foundations.utilities.plugins.getPluginFileNames(
'Governance',
'User-Profiles'
)
for (let i = 0; i < pluginFileNames.length; i++) {
let pluginFileName = pluginFileNames[i]
let pluginFileContent = await CL.projects.foundations.utilities.plugins.getPluginFileContent(
'Governance',
'User-Profiles',
pluginFileName
)
let otherUserProfile = JSON.parse(pluginFileContent)
userProfileIdMap.set(otherUserProfile.id, otherUserProfile.name)
}
let testUserProfile = userProfileIdMap.get(userProfile.id)
if (testUserProfile === undefined) { return true }
if (testUserProfile !== userProfile.name) {
console.log('[INFO] Github Server -> mergeGithubPullRequests -> Validation #6 Failed -> Pull Request "' + pullRequest.title + '" not merged because the User Profile Id already exists and belongs to another User Profile on record. -> Profile Id = ' + userProfile.id + '-> User Profile with the same Id = ' + testUserProfile)
await CL.projects.foundations.utilities.asyncFunctions.sleep(GITHUB_API_WAITING_TIME)
await octokit.rest.issues.createComment({
owner: owner,
repo: repo,
issue_number: pullRequest.number,
body: 'This Pull Request could not be automatically merged and was closed by the Superalgos Governance System because the User Profile Id already exists and belongs to another User Profile on record. \n\nUser Profile Id = "' + userProfile.id + '" \n\n User Profile with the same Id = "' + testUserProfile + '"'
});
await CL.projects.foundations.utilities.asyncFunctions.sleep(GITHUB_API_WAITING_TIME)
await octokit.rest.pulls.update({
owner: owner,
repo: repo,
pull_number: pullRequest.number,
state: 'closed'
});
return false
}
}
async function mergePullRequest() {
/*
All validations passed, we will proceed an merge this Pull Request.
*/
await CL.projects.foundations.utilities.asyncFunctions.sleep(GITHUB_API_WAITING_TIME)
mergeResponse = await octokit.rest.pulls.merge({
owner: owner,
repo: repo,
pull_number: pullRequest.number,
commit_title: 'Changes in User Profile done by Profile Owner ' + githubUsername + ' automatically merged by Superalgos.'
});
return mergeResponse.data.merged
}
}
}
} catch (err) {
console.log('[ERROR] Github Server -> mergeGithubPullRequests -> doGithub -> err.stack = ' + err.stack)
}
}
} catch (err) {
console.log('[ERROR] Github Server -> mergeGithubPullRequests -> Method call produced an error.')
console.log('[ERROR] Github Server -> mergeGithubPullRequests -> err.stack = ' + err.stack)
console.log('[ERROR] Github Server -> mergeGithubPullRequests -> repository = ' + repository)
console.log('[ERROR] Github Server -> mergeGithubPullRequests -> username = ' + username)
console.log('[ERROR] Github Server -> mergeGithubPullRequests -> token starts with = ' + token.substring(0, 10) + '...')
console.log('[ERROR] Github Server -> mergeGithubPullRequests -> token ends with = ' + '...' + token.substring(token.length - 10))
let error = {
result: 'Fail Because',
message: err.message,
stack: err.stack
}
return error
}
}
function respondWithDocsObject(docs, error) {
if (error.message !== undefined) {
docs.placeholder.errorMessage = {
style: 'Error',
text: error.message
}
}
if (error.stack !== undefined) {
docs.placeholder.errorStack = {
style: 'Javascript',
text: error.stack
}
}
if (error.code !== undefined) {
docs.placeholder.errorCode = {
style: 'Json',
text: error.code
}
}
docs.placeholder.errorDetails = {
style: 'Json',
text: JSON.stringify(error, undefined, 4)
}
let customResponse = {
result: global.CUSTOM_FAIL_RESPONSE.result,
docs: docs
}
return customResponse
}
}
+138 -497
View File
@@ -1,4 +1,13 @@
exports.newHttpInterface = function newHttpInterface(WEB_SERVER, DATA_FILE_SERVER, PROJECT_FILE_SERVER, UI_FILE_SERVER, PLUGIN_SERVER, CCXT_SERVER, WEB3_SERVER) {
exports.newHttpInterface = function newHttpInterface(
WEB_SERVER,
DATA_FILE_SERVER,
PROJECT_FILE_SERVER,
UI_FILE_SERVER,
PLUGIN_SERVER,
CCXT_SERVER,
WEB3_SERVER,
GITHUB_SERVER
) {
/*
IMPORTANT: If you are reviewing the code of the project please note
@@ -151,6 +160,15 @@ exports.newHttpInterface = function newHttpInterface(WEB_SERVER, DATA_FILE_SERVE
respondWithContent(JSON.stringify(serverResponse), httpResponse)
return
}
case 'mnemonicToPrivateKey': {
let serverResponse = await WEB3_SERVER.mnemonicToPrivateKey(
params.mnemonic
)
respondWithContent(JSON.stringify(serverResponse), httpResponse)
return
}
default: {
respondWithContent(JSON.stringify({ error: 'Method ' + params.method + ' is invalid.' }), httpResponse)
}
@@ -366,6 +384,7 @@ exports.newHttpInterface = function newHttpInterface(WEB_SERVER, DATA_FILE_SERVE
}
break
}
case 'Save-Concept-Schema': {
getBody(processRequest)
@@ -396,6 +415,7 @@ exports.newHttpInterface = function newHttpInterface(WEB_SERVER, DATA_FILE_SERVE
}
break
}
case 'Save-Topic-Schema': {
getBody(processRequest)
@@ -426,6 +446,7 @@ exports.newHttpInterface = function newHttpInterface(WEB_SERVER, DATA_FILE_SERVE
}
break
}
case 'Save-Tutorial-Schema': {
getBody(processRequest)
@@ -456,6 +477,7 @@ exports.newHttpInterface = function newHttpInterface(WEB_SERVER, DATA_FILE_SERVE
}
break
}
case 'Save-Review-Schema': {
getBody(processRequest)
@@ -486,6 +508,7 @@ exports.newHttpInterface = function newHttpInterface(WEB_SERVER, DATA_FILE_SERVE
}
break
}
case 'Save-Book-Schema': {
getBody(processRequest)
@@ -1019,138 +1042,80 @@ exports.newHttpInterface = function newHttpInterface(WEB_SERVER, DATA_FILE_SERVE
}
break
case 'Gov': {
switch (requestParameters[2]) { // switch by command
case 'getGithubStars': {
getGithubUsernames('activity', 'listStargazersForRepo')
break
}
case 'getGithubWatchers': {
getGithubUsernames('activity', 'listWatchersForRepo')
break
}
case 'getGithubForks': {
getGithubUsernames('repos', 'listForks')
break
}
case 'PRs': {
mergeGithubPullRequests()
break
}
case 'Pay': {
payToContributors()
break
}
}
case 'GOV': {
/*
This is the Governance endpoint at the Http Interface. All methods
related to the Governance System are implemented here and routed
to the backend Servers that can process them.
*/
getBody(processRequest)
function getGithubUsernames(endpoint, method) {
async function processRequest(body) {
try {
const GITHUB_API_WAITING_TIME = 250
const repository = unescape(requestParameters[3])
const username = unescape(requestParameters[4])
const token = unescape(requestParameters[5])
let error
let githubListArray = []
let params = JSON.parse(body)
getRepoInfo()
switch (params.method) {
case 'getGithubStars': {
async function getRepoInfo() {
await doGithub()
if (error !== undefined) {
let serverResponse = await GITHUB_SERVER.getGithubStars(
params.repository,
params.username,
params.token
)
let docs = {
project: 'Governance',
category: 'Topic',
type: 'Gov Error - Get Repository Information',
anchor: undefined,
placeholder: {}
}
respondWithDocsObject(docs, error)
respondWithContent(JSON.stringify(serverResponse), httpResponse)
return
}
case 'getGithubWatchers': {
respondWithContent(JSON.stringify(githubListArray), httpResponse)
}
let serverResponse = await GITHUB_SERVER.getGithubWatchers(
params.repository,
params.username,
params.token
)
async function doGithub() {
respondWithContent(JSON.stringify(serverResponse), httpResponse)
return
}
case 'getGithubForks': {
const { Octokit } = require("@octokit/rest")
let serverResponse = await GITHUB_SERVER.getGithubForks(
params.repository,
params.username,
params.token
)
const octokit = new Octokit({
auth: token,
userAgent: 'Superalgos Beta 11'
})
await getList()
respondWithContent(JSON.stringify(serverResponse), httpResponse)
return
}
case 'mergePullRequests': {
async function getList() {
const repo = repository
const owner = 'Superalgos'
const per_page = 100 // Max
let page = 0
let lastPage = false
let serverResponse = await GITHUB_SERVER.mergePullRequests(
params.commitMessage,
params.username,
params.token
)
while (lastPage === false) {
try {
page++
await sleep(GITHUB_API_WAITING_TIME)
let listResponse = await octokit[endpoint][method]({
owner,
repo,
per_page,
page
});
respondWithContent(JSON.stringify(serverResponse), httpResponse)
return
}
case 'payContributors': {
if (listResponse.data.length < 100) {
lastPage = true
}
let serverResponse = await WEB3_SERVER.payContributors(
params.signature
)
for (let i = 0; i < listResponse.data.length; i++) {
let listItem = listResponse.data[i]
let githubUsername
switch (endpoint) {
case 'activity': {
githubUsername = listItem.login
break
}
case 'repos': {
githubUsername = listItem.owner.login
break
}
}
//console.log(listItem)
githubListArray.push(githubUsername)
}
console.log('[INFO] httpInterface -> Gov -> getRepoInfo -> doGithub -> getList -> ' + method + ' Page = ' + page)
console.log('[INFO] httpInterface -> Gov -> getRepoInfo -> doGithub -> getList -> ' + method + ' Received = ' + listResponse.data.length)
} catch (err) {
console.log(err)
if (err.stack.indexOf('last page') >= 0) {
return
} else {
console.log('[ERROR] httpInterface -> Gov -> getRepoInfo -> doGithub -> getList ->Method call produced an error.')
console.log('[ERROR] httpInterface -> Gov -> getRepoInfo -> doGithub -> getList ->err.stack = ' + err.stack)
console.log('[ERROR] httpInterface -> Gov -> getRepoInfo -> doGithub -> getList ->repository = ' + repository)
console.log('[ERROR] httpInterface -> Gov -> getRepoInfo -> doGithub -> getList ->username = ' + username)
console.log('[ERROR] httpInterface -> Gov -> getRepoInfo -> doGithub -> getList ->token starts with = ' + token.substring(0, 10) + '...')
console.log('[ERROR] httpInterface -> Gov -> getRepoInfo -> doGithub -> getList ->token ends with = ' + '...' + token.substring(token.length - 10))
error = err
}
}
}
respondWithContent(JSON.stringify(serverResponse), httpResponse)
return
}
default: {
respondWithContent(JSON.stringify({ error: 'Method ' + params.method + ' is invalid.' }), httpResponse)
}
}
} catch (err) {
console.log('[ERROR] httpInterface -> Gov -> getRepoInfo -> Method call produced an error.')
console.log('[ERROR] httpInterface -> Gov -> getRepoInfo -> err.stack = ' + err.stack)
console.log('[ERROR] httpInterface -> Gov -> getRepoInfo -> repository = ' + repository)
console.log('[ERROR] httpInterface -> Gov -> getRepoInfo -> username = ' + username)
console.log('[ERROR] httpInterface -> Gov -> getRepoInfo -> token starts with = ' + token.substring(0, 10) + '...')
console.log('[ERROR] httpInterface -> Gov -> getRepoInfo -> token ends with = ' + '...' + token.substring(token.length - 10))
console.log('[ERROR] httpInterface -> GOV -> Method call produced an error.')
console.log('[ERROR] httpInterface -> GOV -> err.stack = ' + err.stack)
console.log('[ERROR] httpInterface -> GOV -> Params Received = ' + body)
let error = {
result: 'Fail Because',
@@ -1160,332 +1125,8 @@ exports.newHttpInterface = function newHttpInterface(WEB_SERVER, DATA_FILE_SERVE
respondWithContent(JSON.stringify(error), httpResponse)
}
}
function respondWithDocsObject(docs, error) {
if (error.message !== undefined) {
docs.placeholder.errorMessage = {
style: 'Error',
text: error.message
}
}
if (error.stack !== undefined) {
docs.placeholder.errorStack = {
style: 'Javascript',
text: error.stack
}
}
if (error.code !== undefined) {
docs.placeholder.errorCode = {
style: 'Json',
text: error.code
}
}
docs.placeholder.errorDetails = {
style: 'Json',
text: JSON.stringify(error, undefined, 4)
}
let customResponse = {
result: global.CUSTOM_FAIL_RESPONSE.result,
docs: docs
}
respondWithContent(JSON.stringify(customResponse), httpResponse)
}
function mergeGithubPullRequests() {
try {
let commitMessage = unescape(requestParameters[3])
/* Unsavping # */
for (let i = 0; i < 10; i++) {
commitMessage = commitMessage.replace('_SLASH_', '/')
commitMessage = commitMessage.replace('_HASHTAG_', '#')
}
const GITHUB_API_WAITING_TIME = 250
const repository = 'Superalgos'
const username = unescape(requestParameters[4])
const token = unescape(requestParameters[5])
let error
let githubPrListArray = []
processOpenPullRequests()
async function processOpenPullRequests() {
await doGithub()
if (error !== undefined) {
let docs = {
project: 'Governance',
category: 'Topic',
type: 'Gov Error - Merge Pull Requests',
anchor: undefined,
placeholder: {}
}
respondWithDocsObject(docs, error)
return
}
respondWithContent(JSON.stringify(global.DEFAULT_OK_RESPONSE), httpResponse)
}
async function doGithub() {
try {
const repo = repository
const owner = 'Superalgos'
const { Octokit } = require("@octokit/rest")
const octokit = new Octokit({
auth: token,
userAgent: 'Superalgos Beta 11'
})
await getPrList()
await mergePrs()
async function getPrList() {
const per_page = 100 // Max
let page = 0
let lastPage = false
while (lastPage === false) {
try {
page++
await sleep(GITHUB_API_WAITING_TIME)
let listResponse = await octokit.rest.pulls.list({
owner: owner,
repo: repo,
state: 'open',
head: 'develop',
base: 'develop',
sort: undefined,
direction: undefined,
per_page: per_page,
page: page
});
if (listResponse.data.length < 100) {
lastPage = true
}
console.log('[INFO] httpInterface -> Gov -> mergeGithubPullRequests -> doGithub -> getPrList -> Receiving Page = ' + page)
for (let i = 0; i < listResponse.data.length; i++) {
let pullRequest = listResponse.data[i]
console.log('[INFO] httpInterface -> Gov -> mergeGithubPullRequests -> doGithub -> getPrList -> Pull Request "' + pullRequest.title + '" found and added to the list to validate. ')
githubPrListArray.push(pullRequest)
}
console.log('[INFO] httpInterface -> Gov -> mergeGithubPullRequests -> doGithub -> getPrList -> Received = ' + listResponse.data.length)
} catch (err) {
console.log(err)
if (err.stack.indexOf('last page') >= 0) {
return
} else {
console.log('[ERROR] httpInterface -> Gov -> mergeGithubPullRequests -> doGithub -> getPrList ->Method call produced an error.')
console.log('[ERROR] httpInterface -> Gov -> mergeGithubPullRequests -> doGithub -> getPrList ->err.stack = ' + err.stack)
console.log('[ERROR] httpInterface -> Gov -> mergeGithubPullRequests -> doGithub -> getPrList ->repository = ' + repository)
console.log('[ERROR] httpInterface -> Gov -> mergeGithubPullRequests -> doGithub -> getPrList ->username = ' + username)
console.log('[ERROR] httpInterface -> Gov -> mergeGithubPullRequests -> doGithub -> getPrList ->token starts with = ' + token.substring(0, 10) + '...')
console.log('[ERROR] httpInterface -> Gov -> mergeGithubPullRequests -> doGithub -> getPrList ->token ends with = ' + '...' + token.substring(token.length - 10))
error = err
}
}
}
}
async function mergePrs() {
/*
In order to automatically merge a PRs we will run several validations first.
We want to automate specifically the merging of Profile File changes at the
Governance System done by the same Github user name that matches the profile
changed. We will also not do the merge if there are more than one file at the
PR.
We will go through the list of open PRs and run the validations at each one of them.
*/
console.log('[INFO] httpInterface -> Gov -> mergeGithubPullRequests -> Ready to Validate ' + githubPrListArray.length + ' pull requests. ')
for (let i = 0; i < githubPrListArray.length; i++) {
let pullRequest = githubPrListArray[i]
/*
Lets get the files changed at this Pull Request.
*/
await sleep(GITHUB_API_WAITING_TIME)
let listResponse = await octokit.rest.pulls.listFiles({
owner: owner,
repo: repo,
pull_number: pullRequest.number
});
/*
Validation #1: If the PR has more than one file, then we will not automatically
merge it.
*/
if (listResponse.data.length !== 1) {
console.log('[INFO] httpInterface -> Gov -> mergeGithubPullRequests -> Validation #1 Failed -> Pull Request "' + pullRequest.title + '" not merged because it contains more than 1 file. -> fileCount = ' + listResponse.data.length)
/*
We will close PRs that contains any User Profile file together with other files in the same Pull Request.
This will avoid manual merges to include User Profile files.
*/
for (j = 0; j < listResponse.data.length; j++) {
let pullRequestFile = listResponse.data[j]
let fileContentUrl = pullRequestFile.raw_url
if (fileContentUrl.indexOf('Governance/Plugins/User-Profiles') >= 0) {
await sleep(GITHUB_API_WAITING_TIME)
await octokit.rest.issues.createComment({
owner: owner,
repo: repo,
issue_number: pullRequest.number,
body: 'This Pull Request could not be automatically merged and was closed by the Superalgos Governance System because it was detected that a User Profile file... \n\n' + fileContentUrl + '\n\n...was submitted together with ' + (listResponse.data.length - 1) + ' other file/s. User Profiles files as per the Governance System rules, must be the only file present at a Pull Request in order to pass all the validations and be automatically merged.'
});
await sleep(GITHUB_API_WAITING_TIME)
await octokit.rest.pulls.update({
owner: owner,
repo: repo,
pull_number: pullRequest.number,
state: 'closed'
});
break
}
}
continue
}
let pullRequestFile = listResponse.data[0]
/*
Validation #2: File Name must be the same to the Github Username of the PR owner.
*/
let fileContentUrl = pullRequestFile.raw_url
if (fileContentUrl.indexOf('Governance/Plugins/User-Profiles') < 0) {
/*
If it is not a user profile then there is no need to auto merge.
*/
console.log('[INFO] httpInterface -> Gov -> mergeGithubPullRequests -> Validation #2 Failed -> Pull Request "' + pullRequest.title + '" not merged because the file modified at the Pull Request is not a User Profile file. -> fileContentUrl = ' + fileContentUrl)
continue
}
let splittedURL = fileContentUrl.split('/')
let fileName = splittedURL[splittedURL.length - 1]
let splittedFileName = fileName.split('.')
fileName = splittedFileName[0]
let githubUsername = pullRequest.user.login
if (githubUsername !== fileName) {
console.log('[INFO] httpInterface -> Gov -> mergeGithubPullRequests -> Validation #2 Failed -> Pull Request "' + pullRequest.title + '" not merged because the Github Username is not equal to the File Name. -> Github Username = ' + githubUsername + '-> fileName = ' + fileName)
await sleep(GITHUB_API_WAITING_TIME)
await octokit.rest.issues.createComment({
owner: owner,
repo: repo,
issue_number: pullRequest.number,
body: 'This Pull Request could not be automatically merged and was closed by the Superalgos Governance System because it was detected that the Github User "' + githubUsername + '" who submitted it, is not equal to the name of the User Profile Plugin File.\n\n File Name = "' + fileName + '"'
});
await sleep(GITHUB_API_WAITING_TIME)
await octokit.rest.pulls.update({
owner: owner,
repo: repo,
pull_number: pullRequest.number,
state: 'closed'
});
continue
}
await sleep(GITHUB_API_WAITING_TIME)
let fileContent = await fetchAPIDataFile(fileContentUrl)
let userProfile = JSON.parse(fileContent)
/*
Validation #3: The file changed at the PR is a User Profile.
*/
if (userProfile.type !== 'User Profile') {
console.log('[INFO] httpInterface -> Gov -> mergeGithubPullRequests -> Validation #3 Failed -> Pull Request "' + pullRequest.title + '" not merged because the file modified is not a User Profile. -> Type = ' + userProfile.type)
continue
}
/*
Validation #4: The message signed at the config is not the Github Username.
*/
let config = JSON.parse(userProfile.config)
let messageSigned = config.signature.message
if (messageSigned !== githubUsername) {
console.log('[INFO] httpInterface -> Gov -> mergeGithubPullRequests -> Validation #4 Failed -> Pull Request "' + pullRequest.title + '" not merged because the Github Username is not equal to the Message Signed at the User Profile. -> Github Username = ' + githubUsername + '-> messageSigned = ' + messageSigned)
await sleep(GITHUB_API_WAITING_TIME)
await octokit.rest.issues.createComment({
owner: owner,
repo: repo,
issue_number: pullRequest.number,
body: 'This Pull Request could not be automatically merged and was closed by the Superalgos Governance System because it was detected that the Github User "' + githubUsername + '" who submitted it, is not equal to the Message Signed at the User Profile.\n\n Message Signed = "' + messageSigned + '"'
});
await sleep(GITHUB_API_WAITING_TIME)
await octokit.rest.pulls.update({
owner: owner,
repo: repo,
pull_number: pullRequest.number,
state: 'closed'
});
continue
}
/*
All validations passed, we will proceed an merge this Pull Request.
*/
await sleep(GITHUB_API_WAITING_TIME)
let mergeResponse = await octokit.rest.pulls.merge({
owner: owner,
repo: repo,
pull_number: pullRequest.number,
commit_title: 'Changes in User Profile done by Profile Owner ' + githubUsername + ' automatically merged by Superalgos.'
});
if (mergeResponse.data.merged !== true) {
console.log('[WARN] httpInterface -> Gov -> mergeGithubPullRequests -> Merge Failed -> Pull Request "' + pullRequest.title + '" not merged because Github could not merge it. -> mergeResponse.message = ' + mergeResponse.data.message)
continue
} else {
console.log('[INFO] httpInterface -> Gov -> mergeGithubPullRequests -> Merge Succed -> Pull Request "' + pullRequest.title + '" successfully merged. -> mergeResponse.message = ' + mergeResponse.data.message)
await sleep(GITHUB_API_WAITING_TIME)
await octokit.rest.issues.createComment({
owner: owner,
repo: repo,
issue_number: pullRequest.number,
body: 'This Pull Request was automatically merged by the Superalgos Governance System because it was detected that the Github User " ' + githubUsername + '" who submitted it, modified its own User Profile Plugin File and nothning else but that file. All validations were successfull.'
});
continue
}
}
}
} catch (err) {
console.log('[ERROR] httpInterface -> Gov -> mergeGithubPullRequests -> doGithub -> err.stack = ' + err.stack)
}
}
} catch (err) {
console.log('[ERROR] httpInterface -> Gov -> mergeGithubPullRequests -> Method call produced an error.')
console.log('[ERROR] httpInterface -> Gov -> mergeGithubPullRequests -> err.stack = ' + err.stack)
console.log('[ERROR] httpInterface -> Gov -> mergeGithubPullRequests -> repository = ' + repository)
console.log('[ERROR] httpInterface -> Gov -> mergeGithubPullRequests -> username = ' + username)
console.log('[ERROR] httpInterface -> Gov -> mergeGithubPullRequests -> token starts with = ' + token.substring(0, 10) + '...')
console.log('[ERROR] httpInterface -> Gov -> mergeGithubPullRequests -> token ends with = ' + '...' + token.substring(token.length - 10))
let error = {
result: 'Fail Because',
message: err.message,
stack: err.stack
}
respondWithContent(JSON.stringify(error), httpResponse)
}
}
function payToContributors() {
}
}
break
}
case 'LegacyPlotter.js':
{
@@ -1701,30 +1342,70 @@ exports.newHttpInterface = function newHttpInterface(WEB_SERVER, DATA_FILE_SERVE
case 'PluginFileNames':
{
let project = unescape(requestParameters[2])
let pluginType = unescape(requestParameters[3])
processRequest()
const fs = require('fs')
let folder = global.env.PATH_TO_PROJECTS + '/' + project + '/Plugins/' + pluginType
async function processRequest(body) {
try {
let project = unescape(requestParameters[2])
let folder = unescape(requestParameters[3])
fs.readdir(folder, (err, files) => {
if (files === undefined) {
files = []
let response = await CL.projects.foundations.utilities.plugins.getPluginFileNames(
project,
folder
)
respondWithContent(JSON.stringify(response), httpResponse)
} catch (err) {
console.log('[ERROR] httpInterface -> PluginFileNames -> Method call produced an error.')
console.log('[ERROR] httpInterface -> PluginFileNames -> err.stack = ' + err.stack)
console.log('[ERROR] httpInterface -> PluginFileNames -> Params Received = ' + body)
let error = {
result: 'Fail Because',
message: err.message,
stack: err.stack
}
respondWithContent(JSON.stringify(error), httpResponse)
}
respondWithContent(JSON.stringify(files), httpResponse)
})
}
break
}
break
case 'LoadPlugin':
{
let project = unescape(requestParameters[2])
let pluginType = unescape(requestParameters[3])
let fileName = unescape(requestParameters[4])
let filePath = global.env.PATH_TO_PROJECTS + '/' + project + '/Plugins/' + pluginType + '/' + fileName
respondWithFile(filePath, httpResponse)
processRequest()
async function processRequest(body) {
try {
let project = unescape(requestParameters[2])
let folder = unescape(requestParameters[3])
let fileName = unescape(requestParameters[4])
let response = await CL.projects.foundations.utilities.plugins.getPluginFileContent(
project,
folder,
fileName
)
respondWithContent(response, httpResponse)
} catch (err) {
console.log('[ERROR] httpInterface -> LoadPlugin -> Method call produced an error.')
console.log('[ERROR] httpInterface -> LoadPlugin -> err.stack = ' + err.stack)
console.log('[ERROR] httpInterface -> LoadPlugin -> Params Received = ' + body)
let error = {
result: 'Fail Because',
message: err.message,
stack: err.stack
}
respondWithContent(JSON.stringify(error), httpResponse)
}
}
break
}
break
case 'SavePlugin':
getBody(processRequest)
@@ -1831,7 +1512,7 @@ exports.newHttpInterface = function newHttpInterface(WEB_SERVER, DATA_FILE_SERVE
function onDirRead(err, fileList) {
if (err) {
// This happens the first time you run the software.
respondWithContent(JSON.stringify(global.DEFAULT_FAIL_RESPONSE), httpResponse)
respondWithContent(JSON.stringify(allWorkspaces), httpResponse)
return
} else {
let updatedFileList = []
@@ -2431,44 +2112,4 @@ exports.newHttpInterface = function newHttpInterface(WEB_SERVER, DATA_FILE_SERVE
return files.reduce((a, f) => a.concat(f), []);
}
}
function sleep(ms) {
return new Promise((resolve) => {
setTimeout(resolve, ms)
})
}
async function fetchAPIDataFile(url) {
let promise = new Promise((resolve, reject) => {
const fetch = require('node-fetch')
/*
This is how we call the API.
*/
fetch(url)
.then((response) => {
if (response.status !== 200) {
console.log('[ERROR] fetchAPIData -> then -> url =' + url)
reject()
return
}
getResponseBody()
function getResponseBody() {
response.text().then(body => {
resolve(body)
})
}
})
.catch(err => {
console.log('[ERROR] fetchAPIData -> url =' + url)
reject()
})
}
)
return promise
}
}
+274 -4190
View File
File diff suppressed because it is too large. Load diff
+3 -2
View File
@@ -10,12 +10,13 @@
"@octokit/rest": "^18.9.0",
"ccxt": "^1.51.98",
"child_process": "^1.0.2",
"ethers": "^5.4.4",
"lookpath": "^1.2.2",
"node-fetch": "2.6.1",
"open": "^6.4.0",
"rimraf": "^3.0.2",
"simple-git": "^2.42.0",
"web3": "^1.5.0",
"ws": "^7.4.6",
"node-fetch": "2.6.1"
"ws": "^7.4.6"
}
}
+26
View File
@@ -8,6 +8,7 @@ exports.newWeb3Server = function newWeb3Server() {
getWalletBalances: getWalletBalances,
signData: signData,
recoverAddress: recoverAddress,
mnemonicToPrivateKey: mnemonicToPrivateKey,
initialize: initialize,
finalize: finalize,
run: run
@@ -236,4 +237,29 @@ exports.newWeb3Server = function newWeb3Server() {
return { error: 'Could not recover address. ' + err.stack }
}
}
async function mnemonicToPrivateKey(mnemonic) {
try {
const ethers = require('ethers')
let wallet = ethers.Wallet.fromMnemonic(mnemonic)
if (wallet.privateKey !== undefined) {
return {
address: wallet.address,
privateKey: wallet.privateKey,
result: 'Ok'
}
} else {
return {
address: undefined,
privateKey: undefined,
result: 'Fail'
}
}
} catch (err) {
return { error: 'Could not convert to Private Key. ' + err.stack }
}
}
}
+12 -8
View File
@@ -16,7 +16,7 @@ Node JS is an open-source server environment required to run Superalgos.
**A.** Go to the Node JS [download page](https://nodejs.org/en/download/).
**B.** Download your system’s installer. Select *LTS Recommended for Most Users* and click the big Windows or macOS Installer button. If you are on Linux, the installer is listed further down the page.
**B.** Download your system's installer. Select *LTS Recommended for Most Users* and click the big Windows or macOS Installer button. If you are on Linux, the installer is listed further down the page.
**C.** Run the installer with the default configuration — just click Next until Node.JS is fully installed. That's it! You can continue with step 2 (Git Installation).
@@ -94,13 +94,13 @@ Before you begin, it is recommended that you set up Chrome/Safari as your defaul
**A.** Scroll the page all the way to the top. Find and click the **Fork** button to create your fork/copy of this repository. To fork Superalgos you need a Github account. If you don't have one, go ahead and create it.
**NOTE:** A Fork is required for your contributions to the project. The reason why Superalgos is free and open-source is that the project has set up a <a href="https://superalgos.org/community-business.shtml" target="_blank">Collective Business</a> in which all users may participate. The way to participate is to <a href="https://superalgos.org/community-contribute.shtml" target="_blank">contribute</a> to make Superalgos better. The project's <a href="https://superalgos.org/token-overview.shtml" target="_blank">token</a> is distributed among contributors.
**NOTE:** A Fork is required for your contributions to the project. The reason why Superalgos is free and open-source is that the project has set up a <a href="https://superalgos.org/project-one-pager.shtml" target="_blank">Collective Business</a> in which all users may participate. The way to participate is to <a href="https://superalgos.org/community-contribute.shtml" target="_blank">contribute</a> to make Superalgos better. The project's <a href="https://superalgos.org/token-overview.shtml" target="_blank">token</a> is distributed among contributors.
## 2. Clone Your Fork
**A.** Once the fork is created, you will land on the page of your fork. Copy the URL from your browser's address bar.
**B.** In your computer/laptop/server, open a command prompt or terminal. Make sure you are in a directory where you have write permissions (on most systems the terminal will open in your user’s home directory, so you’re good). Clone the git repository using the command:
**B.** In your computer/laptop/server, open a command prompt or terminal. Make sure you are in a directory where you have write permissions (on most systems the terminal will open in your user's home directory, so you're good). Clone the git repository using the command:
```
git clone <URL of your Superalgos fork>
@@ -116,9 +116,13 @@ This creates the `Superalgos` folder in the current directory, which contains th
## 3. Install Node Dependencies
**A.** After the Superalgos directory has been installed, the final step of installation is to set up the necessary node dependencies.
After the Superalgos directory has been installed, the final step of installation is to set up the necessary node dependencies. In the same command prompt or terminal you just used, type the following:
**B.** In the same command prompt or terminal you just used, type the following command:
```
cd Superalgos
```
That should take you inside the Superalgos folder created by the `git clone` command earlier. Now, run the following command:
```
node setup
@@ -233,7 +237,7 @@ We are testing the UI on Google Chrome and Safari on macOS only. It may work on
# Running Superalgos on a Headless Linux Server as a Daemon
If you’re running Superalgos on a headless linux server like a Raspberry Pi, you might want to run it as a daemon so it isn’t attached to your current login session. The easiest, most standard way to go about this is probably using `systemd`. Most linux distributions use it as default init system/service manager.
If you're running Superalgos on a headless linux server like a Raspberry Pi, you might want to run it as a daemon so it isn't attached to your current login session. The easiest, most standard way to go about this is probably using `systemd`. Most linux distributions use it as default init system/service manager.
## Using `systemd`
@@ -252,7 +256,7 @@ ExecStart=/usr/bin/node run minMemo noBrowser
WantedBy=multi-user.target
```
There is no need to run Superalgos as root so we’re running it as a user. The `minMemo` option assumes you’re running on a small machine like a Raspberry Pi, while `noBrowser` makes sense for running daemonized.
There is no need to run Superalgos as root so we're running it as a user. The `minMemo` option assumes you're running on a small machine like a Raspberry Pi, while `noBrowser` makes sense for running daemonized.
Now, as root (or using `sudo`), put the file `superalgos.service` you just created in `/etc/systemd/system/` and issue the command
```
@@ -290,7 +294,7 @@ Once the app finishes loading, an interactive tutorial takes you by the hand and
![welcome-tutorial-00](https://user-images.githubusercontent.com/13994516/107038771-4a6bf100-67bd-11eb-92e0-353525a972a9.gif)
**NOTE:** The tutorial uses Binance or Binance US as the exchange of choice. If you don’t have an account with Binance or Binance US, you will still be able to follow 100% of the tutorial. When you get to the live trading section, keep going even if you don't intend to run the session. You may learn how to work with other exchanges later on.
**NOTE:** The tutorial uses Binance or Binance US as the exchange of choice. If you don't have an account with Binance or Binance US, you will still be able to follow 100% of the tutorial. When you get to the live trading section, keep going even if you don't intend to run the session. You may learn how to work with other exchanges later on.
# Docker Deployments
@@ -0,0 +1,14 @@
exports.newFoundationsUtilitiesAsyncFunctions = function () {
let thisObject = {
sleep: sleep
}
return thisObject
function sleep(ms) {
return new Promise((resolve) => {
setTimeout(resolve, ms)
})
}
}
@@ -0,0 +1,49 @@
exports.newFoundationsUtilitiesPlugins = function () {
let thisObject = {
getPluginFileNames: getPluginFileNames,
getPluginFileContent: getPluginFileContent
}
return thisObject
async function getPluginFileNames(project, folder) {
let promise = new Promise((resolve, reject) => {
let path = global.env.PATH_TO_PROJECTS + '/' + project + '/Plugins/' + folder
CL.nodeModules.fs.readdir(path, (err, files) => {
if (err) {
reject()
return
}
if (files === undefined) {
files = []
}
resolve(files)
})
}
)
return promise
}
async function getPluginFileContent(project, folder, fileName) {
let promise = new Promise((resolve, reject) => {
let path = global.env.PATH_TO_PROJECTS + '/' + project + '/Plugins/' + folder + '/' + fileName
CL.nodeModules.fs.readFile(path, onFileRead)
function onFileRead(err, file) {
if (err) {
reject()
return
}
resolve(file.toString())
}
}
)
return promise
}
}
@@ -0,0 +1,40 @@
exports.newFoundationsUtilitiesWebAccess = function () {
let thisObject = {
fetchAPIDataFile: fetchAPIDataFile
}
return thisObject
async function fetchAPIDataFile(url) {
let promise = new Promise((resolve, reject) => {
const fetch = CL.nodeModules.nodeFetch
/*
This is how we call the API.
*/
fetch(url)
.then((response) => {
if (response.status !== 200) {
console.log('[ERROR] fetchAPIData -> then -> url =' + url)
reject()
return
}
getResponseBody()
function getResponseBody() {
response.text().then(body => {
resolve(body)
})
}
})
.catch(err => {
console.log('[ERROR] fetchAPIData -> url =' + url)
reject()
})
}
)
return promise
}
}
File diff suppressed because it is too large. Load diff
@@ -60159,7 +60159,6 @@
"arrangementStyle": 0
},
"uiObject": {
"isPlaying": false,
"isRunning": false
},
"tutorial": {
@@ -62416,7 +62415,7 @@
"referenceParent": {
"type": "Workspace",
"name": "Getting Started Tutorials",
"id": "e927ea33-b88c-4524-97bb-07bbfea501f2"
"id": "90718523-1d6d-49a5-893c-803a492fdb43"
},
"referenceParentCombinedNodePath": [
[
@@ -63381,7 +63380,6 @@
"arrangementStyle": 0
},
"uiObject": {
"isPlaying": false,
"isRunning": false
},
"tutorial": {
@@ -69872,6 +69870,7 @@
"arrangementStyle": 0
},
"uiObject": {
"isPlaying": true,
"isRunning": false
},
"tutorial": {
@@ -9,8 +9,8 @@
},
{
"action": "Add UI Object",
"label": "Add Dependency Product Folder",
"relatedUiObject": "Dependency Product Folder",
"label": "Add Data Dependency Folder",
"relatedUiObject": "Data Dependency Folder",
"actionFunction": "payload.executeAction"
},
{
@@ -10,6 +10,7 @@
{
"action": "Add UI Object",
"label": "Add Tutorial",
"iconProject": "Education",
"relatedUiObject": "Tutorial",
"actionFunction": "payload.executeAction"
},
@@ -4,6 +4,7 @@
{
"action": "Add Missing Plugin Tutorials",
"label": "Add Missing Tutorials",
"iconProject": "Education",
"askConfirmation": true,
"confirmationLabel": "Confirm to Proceed",
"workDoneLabel": "Done",
@@ -0,0 +1,28 @@
{
"tutorial": "Creating Your User Profile Tutorial",
"pageNumber": 1,
"type": "Creating Your User Profile Tutorial",
"definition": {
"text": "This Tutorial walks you through the steps to create a User Profile and begin the journey to participating in Superalgos Governance.",
"updated": 1629146822189
},
"paragraphs": [
{
"style": "Title",
"text": "About This Tutorial"
},
{
"style": "List",
"text": "Workspace: Token Distribution Superalgos.",
"updated": 1629146723735
},
{
"style": "List",
"text": "Maintainer: @harrellbm (Telegram, and Discord)."
},
{
"style": "Text",
"text": ""
}
]
}
@@ -0,0 +1,33 @@
{
"tutorial": "Creating Your User Profile Tutorial",
"pageNumber": 2,
"type": "Tutorial Step - Better Together",
"definition": {
"text": "Welcome to Superalgos Governance! Today I will walk you through everything you need to know about creating a Superalgos User Profile and joining the Governance network!",
"updated": 1629164302622,
"icon": {
"name": "Charting-Space/chart-points",
"project": "Foundations"
}
},
"paragraphs": [
{
"style": "Text",
"text": "As a completely open source and collaborative project, Superalgos is not run by any singular set of people. ",
"updated": 1629164311647
},
{
"style": "Note",
"text": "Even the core team, who are dedicated to improving and maintaining Superalgos, do not directly control the path development will take. ",
"updated": 1629164322397
},
{
"style": "Text",
"text": "Instead, the governance system distributes decision making power across all register users by leveraging the SA token."
},
{
"style": "Text",
"text": ""
}
]
}
@@ -0,0 +1,28 @@
{
"tutorial": "Creating Your User Profile Tutorial",
"pageNumber": 3,
"type": "Tutorial Step - Make sure You’re Comfortable",
"definition": {
"text": "Before you jump into governance, it’s important you know what is being governed!",
"updated": 1629147485727,
"icon": {
"name": "Charting-Space/charting-space",
"project": "Foundations"
}
},
"paragraphs": [
{
"style": "Text",
"text": "We have got a lot of exciting things to talk about, but I want to make sure you can follow along easily. ",
"updated": 1629164474591
},
{
"style": "Callout",
"text": "If you haven't already, take some time to go through the Getting Started Tutorials."
},
{
"style": "Success",
"text": "There is no need to be an expert, but some general knowledge of the system will make your time in the Governance project much more productive."
}
]
}
@@ -0,0 +1,29 @@
{
"tutorial": "Creating Your User Profile Tutorial",
"pageNumber": 4,
"type": "Tutorial Step - One Token to Rule Them All",
"definition": {
"text": "Unlike some crypto currencies the SA token actually means something!",
"updated": 1629162158011,
"icon": {
"name": "design-space",
"project": "Foundations"
}
},
"paragraphs": [
{
"style": "Text",
"text": "The SA token is a BEP20 token hosted on the Binance Smart Chain (BSC). It represents your amount of investment in the Superalgos project and community. It is a way to reward and grow the Superalgos user base and development pool.",
"updated": 1629164803435
},
{
"style": "Success",
"text": "This means that the more you contribute to the project and help others, the more tokens you will earn!",
"updated": 1629164817915
},
{
"style": "Text",
"text": ""
}
]
}
@@ -0,0 +1,24 @@
{
"tutorial": "Creating Your User Profile Tutorial",
"pageNumber": 5,
"type": "Tutorial Step - With Tokens Comes Responsibility",
"definition": {
"text": "Holding tokens does more than just show commitment. SA tokens also confer governance power!",
"updated": 1629162449407,
"icon": {
"name": "workspace",
"project": "Foundations"
}
},
"paragraphs": [
{
"style": "Text",
"text": "The more tokens you hold the more influence you can have over the direction of the project. This means, by owning SA tokens, you hold the power to use those tokens to exert influence over what parts of the system will be developed and how the community will evolve.",
"updated": 1629165111109
},
{
"style": "Note",
"text": "This works in a cycle. The more you contribute, the more tokens you earn, the more say you have!"
}
]
}
@@ -0,0 +1,25 @@
{
"tutorial": "Creating Your User Profile Tutorial",
"pageNumber": 6,
"type": "Tutorial Step - A Mighty Meritocracy",
"definition": {
"text": "Superalgos does not reward freeloading! Real contributions to the project and community are what get rewarded with SA tokens and Governance power.",
"updated": 1629162653518,
"icon": {
"name": "crypto-exchange",
"project": "Foundations"
}
},
"paragraphs": [
{
"style": "Text",
"text": "The fancy word for this kind of project governance is meritocracy. In other words, the more you contribute to the improvement of Superalgos, and to the support of our community, the more governance power you earn to influence the future of the Superalgos project.",
"updated": 1629165463461
},
{
"style": "Note",
"text": "This is a collaboration! Only together can we continue to make Superalgos the game changer it is today no matter what the future may bring.",
"updated": 1629165548280
}
]
}
@@ -0,0 +1,24 @@
{
"tutorial": "Creating Your User Profile Tutorial",
"pageNumber": 7,
"type": "Tutorial Step - A Worthy Contribution",
"definition": {
"text": "No matter who you are, you can bring value to the Superalgos network!",
"updated": 1629162880545,
"icon": {
"name": "Market/market",
"project": "Foundations"
}
},
"paragraphs": [
{
"style": "Success",
"text": "Each of us bring unique talents and experience into our work with Superalgos. Even if you are not a programmer or expert trader there is still ways for you to get involved and grow the project!",
"updated": 1629165838038
},
{
"style": "Text",
"text": "One of the first contributions you can make to the project is getting involved in governance itself. The network of users guiding Superalgos is an integral part of the success of our open collaboration. You bring value to the network just by being a part of it!"
}
]
}
@@ -0,0 +1,25 @@
{
"tutorial": "Creating Your User Profile Tutorial",
"pageNumber": 8,
"type": "Tutorial Step - What’s in a Profile?",
"definition": {
"text": "So what are we waiting for? Let’s get started! To participate in Governance you will first need a User Profile.",
"updated": 1629163040597,
"icon": {
"name": "Charting-Space/chart-points",
"project": "Foundations"
}
},
"paragraphs": [
{
"style": "Text",
"text": "Your profile is set up just like the rest of the Superalgos system. It is a hierarchy of nodes. Only this hierarchy defines how you intend to use the Token Power you have earned or is delegated to you by other users. ",
"updated": 1629165991225
},
{
"style": "Note",
"text": "By channelling your token power you help decide what parts of the system will be incentivised for further development and who will be rewarded for the community roles they have played.",
"updated": 1629166027369
}
]
}
@@ -0,0 +1,29 @@
{
"tutorial": "Creating Your User Profile Tutorial",
"pageNumber": 9,
"type": "Tutorial Step - Program Participation",
"definition": {
"text": "How you use your Token Power is defined within a number of different Programs within your profile hierarchy.",
"updated": 1629163374967,
"icon": {
"name": "Charting-Space/chart-points",
"project": "Foundations"
}
},
"paragraphs": [
{
"style": "Text",
"text": "These programs help organize the types of development and community relationships you want to reward. ",
"updated": 1629166399952
},
{
"style": "Note",
"text": "For instance, the Claims Program lets you present claims for the work you’ve done. While the Support Program lets you thank other Superalgos users that have helped you with technical support. ",
"updated": 1629166416422
},
{
"style": "Text",
"text": "We’ll cover all these and other programs in detail once you have a working profile!"
}
]
}
@@ -0,0 +1,25 @@
{
"tutorial": "Creating Your User Profile Tutorial",
"pageNumber": 10,
"type": "Tutorial Step - Where is all Begins",
"definition": {
"text": "Here we are at the head of the governance project. Let’s get that profile created!",
"updated": 1629163572836,
"icon": {
"name": "Charting-Space/dashboard",
"project": "Foundations"
}
},
"paragraphs": [
{
"style": "Callout",
"text": "Click the Add Profile Constructor button.",
"updated": 1629163599518
},
{
"style": "Text",
"text": "Now position the resulting Profile Constructor node where it is easy to see.",
"updated": 1629163626525
}
]
}
@@ -0,0 +1,30 @@
{
"tutorial": "Creating Your User Profile Tutorial",
"pageNumber": 11,
"type": "Tutorial Step - Profile in a Heartbeat",
"definition": {
"text": "The Profile Constructor is the device you will use to set up your profile. ",
"updated": 1629163751173,
"icon": {
"name": "under-construction",
"project": "Foundations"
}
},
"paragraphs": [
{
"style": "Text",
"text": "Not only that but it will create a unique Binance Smart Chain wallet that is connected with your profile. We call it your Governance Address.",
"updated": 1629166742408
},
{
"style": "Note",
"text": "This means that you will not be able to use any existing wallet addresses you may own, but will create a new one. ",
"updated": 1629166737533
},
{
"style": "Text",
"text": "Your Governance Address will allow you to receive the SA tokens you earn. Superalgos will also read the balance of this address to become aware of the number of SA tokens you hold, and assign your profile an equivalent amount of Token Power.",
"updated": 1629166705049
}
]
}
@@ -0,0 +1,33 @@
{
"tutorial": "Creating Your User Profile Tutorial",
"pageNumber": 12,
"type": "Tutorial Step - Pump that Heartbeat!",
"definition": {
"text": "Now let’s configure that constructor! ",
"updated": 1629167048288,
"icon": {
"name": "Time/time-machine",
"project": "Foundations"
}
},
"paragraphs": [
{
"style": "Callout",
"text": "Click the configuration option and type your GitHub username under the username properly.",
"updated": 1629167062088
},
{
"style": "Text",
"text": "It should look something like this:"
},
{
"style": "Json",
"text": "{\n \"githubUsername\": \"My Awesome Username\"\n}",
"updated": 1629167126327
},
{
"style": "Text",
"text": ""
}
]
}
@@ -0,0 +1,33 @@
{
"tutorial": "Creating Your User Profile Tutorial",
"pageNumber": 12,
"type": "Tutorial Step - Pump that Heartbeat!",
"definition": {
"text": "Now let’s configure that Profile Constructor!",
"updated": 1629167519082,
"icon": {
"name": "Time/time-machine",
"project": "Foundations"
}
},
"paragraphs": [
{
"style": "Callout",
"text": "Click the configuration option and type your GitHub username under the username properly.",
"updated": 1629167062088
},
{
"style": "Text",
"text": "It should look something like this:"
},
{
"style": "Json",
"text": "{\n \"githubUsername\": \"My Awesome Username\"\n}",
"updated": 1629167126327
},
{
"style": "Text",
"text": ""
}
]
}
@@ -0,0 +1,23 @@
{
"tutorial": "Creating Your User Profile Tutorial",
"pageNumber": 13,
"type": "Tutorial Step - Oh so Shiny",
"definition": {
"text": "Write a summary for this tutorial page."
},
"paragraphs": [
{
"style": "Text",
"text": "We are almost there. Now click create user profile and watch the magic happen. That’s right, you just created your profile!",
"updated": 1629167761539
},
{
"style": "Text",
"text": "While we didn’t have to break a sweat making your profile, some pretty amazing things happened behind the scenes. The constructor just generated your user profile, and unique SA Token Governance address. A cryptographic message with your GitHub.com username has been signed with the private key corresponding to the address just generated. This message will be stored in your User Profile so that we may authenticate it comes from you once you submit your profile as a plugin in the Governance Project."
},
{
"style": "Text",
"text": "What this means is that your profile, and its associated token address, can only be accessed and changed by you!"
}
]
}
@@ -329,15 +329,22 @@ function newFoundationsFunctionLibraryUiObjectsFromNodes() {
pathName = rawPath[i][0]
pathType = rawPath[i][1]
pathNode = UI.projects.foundations.spaces.designSpace.workspace.getHierarchyHeadsByCodeNameAndNodeType( pathName, pathType )
// If reference parent is workspace node grab node
if (pathType === "Workspace") {
pathNode = UI.projects.foundations.spaces.designSpace.workspace.workspaceNode
}
// If Hierarchy Head is not located within the workspace abort reconnection
if (pathNode === undefined) {
console.log("[WARN] Abort reconnection saved path head node not found in current workspace", node)
return
}
continue
} else { // Walk through children nodes and find the next node from saved path
pathName = rawPath[i][0]
pathType = rawPath[i][1]
pathNode = getNextNodeFromPath(pathNode, pathName, pathType)
}
}
if (pathNode !== undefined ) {
@@ -347,6 +354,7 @@ function newFoundationsFunctionLibraryUiObjectsFromNodes() {
}
function getNextNodeFromPath(node, pathName, pathType) {
console.log("pathNode", node)
let schemaDocument = getSchemaDocument(node)
let nextNode = undefined
for (let i = 0; i < schemaDocument.childrenNodesProperties.length; i++) {
@@ -157,7 +157,10 @@ function newWorkspace() {
let workspace = UI.projects.foundations.spaces.designSpace.workspace.workspaceNode
/* Validation if it is too early to save. */
if (thisObject.isInitialized === false) { return }
if (thisObject.isInitialized === false) {
console.log('Workspace not saved because it has not been initialized. =')
return
}
/* Validation of 2 sessions opened at the same time. */
let savedSessionTimestamp = window.localStorage.getItem('Session Timestamp')
@@ -73,6 +73,7 @@ function newCircularMenu() {
menuItem.visible = menuItemInitialValue.visible
menuItem.iconPathOn = menuItemInitialValue.iconPathOn
menuItem.iconPathOff = menuItemInitialValue.iconPathOff
menuItem.iconProject = menuItemInitialValue.iconProject
menuItem.rawRadius = menuItemInitialValue.rawRadius
menuItem.targetRadius = menuItemInitialValue.label
menuItem.currentRadius = menuItemInitialValue.currentRadius
@@ -9,6 +9,7 @@ function newCircularMenuItem() {
confirmationLabel: undefined,
iconOn: undefined,
iconOff: undefined,
iconProject: undefined,
icons: undefined,
currentIcon: undefined,
disableIfPropertyIsDefined: undefined,
@@ -94,6 +95,7 @@ function newCircularMenuItem() {
thisObject.container = undefined
thisObject.iconOn = undefined
thisObject.iconOff = undefined
thisObject.iconProject = undefined
thisObject.icons = undefined
thisObject.currentIcon = undefined
thisObject.payload = undefined
@@ -261,11 +263,18 @@ function newCircularMenuItem() {
thisObject.iconOn = UI.projects.foundations.spaces.designSpace.getIconByProjectAndName('Foundations', thisObject.secondaryIcon)
thisObject.iconOff = UI.projects.foundations.spaces.designSpace.getIconByProjectAndName('Foundations', thisObject.secondaryIcon)
} else {
if (thisObject.relatedUiObject !== undefined) {
if (thisObject.label === "Add Dependency Product Folder") { console.log ("Test") }
if (thisObject.relatedUiObject !== undefined && thisObject.iconProject !== undefined) {
thisObject.iconOn = UI.projects.foundations.spaces.designSpace.getIconByProjectAndType(thisObject.iconProject, thisObject.relatedUiObject)
thisObject.iconOff = UI.projects.foundations.spaces.designSpace.getIconByProjectAndType(thisObject.iconProject, thisObject.relatedUiObject)
} else if (thisObject.relatedUiObject !== undefined && thisObject.iconProject === undefined) {
thisObject.iconOn = UI.projects.foundations.spaces.designSpace.getIconByProjectAndType(thisObject.payload.node.project, thisObject.relatedUiObject)
thisObject.iconOff = UI.projects.foundations.spaces.designSpace.getIconByProjectAndType(thisObject.payload.node.project, thisObject.relatedUiObject)
} else {
if (thisObject.iconPathOn !== undefined && thisObject.iconPathOff !== undefined) {
if (thisObject.iconPathOn !== undefined && thisObject.iconPathOff !== undefined && thisObject.iconProject !== undefined) {
thisObject.iconOn = UI.projects.foundations.spaces.designSpace.getIconByProjectAndName(thisObject.iconProject, thisObject.iconPathOn)
thisObject.iconOff = UI.projects.foundations.spaces.designSpace.getIconByProjectAndName(thisObject.iconProject, thisObject.iconPathOff)
} else if (thisObject.iconPathOn !== undefined && thisObject.iconPathOff !== undefined && thisObject.iconProject === undefined) {
thisObject.iconOn = UI.projects.foundations.spaces.designSpace.getIconByProjectAndName('Foundations', thisObject.iconPathOn)
thisObject.iconOff = UI.projects.foundations.spaces.designSpace.getIconByProjectAndName('Foundations', thisObject.iconPathOff)
} else {
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><defs><style>.cls-1{fill:#fff;}.cls-2{fill:#cdd4d5;}.cls-3{fill:#e6eaea;}.cls-4{fill:#365558;}.cls-5{fill:#fd6a33;}.cls-6{fill:#e3493c;}.cls-7{fill:none;}.cls-8{fill:#0295aa;}.cls-9{fill:#4acfd9;}</style></defs><title>airdrop-program</title><g id="Layer_15" data-name="Layer 15"><path class="cls-1" d="M166.66,502.09A14,14,0,0,0,180,512h2.34A17.34,17.34,0,0,1,166.66,502.09Z"/><path class="cls-1" d="M182.38,376.58H180a14,14,0,0,0-13.38,9.9A17.35,17.35,0,0,1,182.38,376.58Z"/><path class="cls-2" d="M170.05,390.57V498a10,10,0,0,0,10,10h76V380.57H180A10,10,0,0,0,170.05,390.57Z"/><path class="cls-3" d="M332,508a10,10,0,0,0,10-10V390.57a10,10,0,0,0-10-10H256V508Z"/><path class="cls-1" d="M332,376.58h-3.33a17.17,17.17,0,0,1,17.32,17V495a17.18,17.18,0,0,1-17.32,17H332a14,14,0,0,0,14-14V390.57A14,14,0,0,0,332,376.58Z"/><path class="cls-4" d="M166.05,390.57a14.2,14.2,0,0,1,.61-4.09,16.66,16.66,0,0,0-1.6,7.08V495a16.66,16.66,0,0,0,1.6,7.08,14.08,14.08,0,0,1-.61-4.08Z"/><path class="cls-4" d="M180,508a10,10,0,0,1-10-10V390.57a10,10,0,0,1,10-10h76v-4H182.38a17.35,17.35,0,0,0-15.72,9.9,14.2,14.2,0,0,0-.61,4.09V498a14.08,14.08,0,0,0,.61,4.08A17.34,17.34,0,0,0,182.38,512H256v-4Z"/><path class="cls-4" d="M346,495V393.56a17.17,17.17,0,0,0-17.32-17H256v4h76a10,10,0,0,1,10,10V498a10,10,0,0,1-10,10H256v4h72.63A17.18,17.18,0,0,0,346,495Z"/><path class="cls-1" d="M256,399.13v96.26a48.13,48.13,0,0,0,0-96.26Z"/><path class="cls-3" d="M207.87,447.26A48.12,48.12,0,0,0,256,495.39V399.13A48.13,48.13,0,0,0,207.87,447.26Z"/><path class="cls-5" d="M266.32,449.52a30.19,30.19,0,0,1-3.87-9.4c.45-4-1-8.92-4.16-9.4a3.57,3.57,0,0,0-2.29.47v25.75a31.72,31.72,0,0,1,3.21.89c3.41,2.31,8.31,3.55,10.4,1.2S269.61,452.58,266.32,449.52Z"/><path class="cls-6" d="M251.45,438.24a29.62,29.62,0,0,1-6.69,8c-3.75,1.67-7.36,5.19-6.19,8s6.52,3.08,10.19,2a32.12,32.12,0,0,1,7.26.72V431.17C253.78,432.41,252,435.6,251.45,438.24Z"/><path class="cls-5" d="M262.82,414.38c5.19.2,7.24,10.3,6.4,17.95A101.36,101.36,0,0,0,276,451.08a7.72,7.72,0,0,0,3.29,3.16,40.75,40.75,0,0,1-3.5-20c3.3-8.76,1.83-20.59-3.12-23.35a11.49,11.49,0,0,0-15.19,4.61.76.76,0,0,0-.09.18A7.69,7.69,0,0,1,262.82,414.38Z"/><path class="cls-6" d="M233.46,436.11c-9.55,1.64-19.46,8.76-19.46,14.23a11.1,11.1,0,0,0,11.77,10.33h.15a7.56,7.56,0,0,1-3.61-4c-2.5-4,5.39-10.94,12.84-14.19,2.37-1,12.09-13,13.46-15a7.74,7.74,0,0,0,1.2-4.36A43.34,43.34,0,0,1,233.46,436.11Z"/><path class="cls-5" d="M282.81,465.52a2,2,0,0,0-.21-.3,7.16,7.16,0,0,1-1.61,5.11c-2.75,4-12.77.92-19.41-3.68q-2.76-.76-5.58-1.41v5.56c.22.15.45.28.68.43,6.28,7.17,17.61,12,22.56,9.08A10.48,10.48,0,0,0,282.81,465.52Z"/><path class="cls-6" d="M241.35,463a7.63,7.63,0,0,0-4.49,1.17A44,44,0,0,1,256,470.82v-5.57A112.68,112.68,0,0,0,241.35,463Z"/><path class="cls-7" d="M73.94,197.32l170.45,155L175.44,199.91C125.06,158.21,84.92,187.52,73.94,197.32Z"/><path class="cls-7" d="M336.66,199.69,267.61,352.34,437.77,197.59C383.41,155.17,341.58,194.68,336.66,199.69Z"/><path class="cls-8" d="M230.08,9.88C143.71,22.49,76.44,97.11,72.29,188.45c15.56-12,55-34.3,102.48.78C176.13,104.33,198.58,34.8,230.08,9.88Z"/><path class="cls-9" d="M337.22,188.67c15.34-12,54.87-34.67,102.52.37C435.85,97.43,368.48,22.51,281.92,9.88,313.35,34.75,335.77,104,337.22,188.67Z"/><path class="cls-9" d="M256,170.3h.94l.65,0,1.43,0,.81,0,1.31.05.88,0,1.25.07.94.06,1.22.08,1,.08,1.22.1,1,.1,1.22.12,1,.11,1.22.14,1,.13,1.23.17,1,.14,1.24.19,1.05.17,1.25.2,1.05.19,1.27.23,1.05.2,1.29.26,1.06.22,1.3.28L287,174l1.32.31,1.06.25,1.34.34,1.06.28,1.37.37,1,.29,1.39.4,1,.31,1.44.44,1,.32,1.51.49,1,.32,1.57.54.92.32,1.72.63.78.28c.7.26,1.39.53,2.09.81l.43.16,2.56,1,.1,0,2.44,1,.78.34,1.78.8,1,.45,1.59.74,1.07.52,1.52.74,1.12.57c.49.25,1,.49,1.48.75l1.17.61,1.44.76,1.2.66,1,.55C327.77,90.84,294.83,8,256,8h0Z"/><path class="cls-8" d="M184.72,188.82l.7-.39.71-.4,1.21-.66.73-.39c.36-.2.73-.39,1.1-.58l.92-.48,1.11-.56.73-.36,1.61-.78.76-.35c.51-.25,1-.49,1.58-.73l.79-.36,1.55-.68.73-.31,2-.84.76-.31,2-.8.64-.24,2-.75.72-.25c.78-.28,1.58-.56,2.4-.83l.72-.24,2.54-.82.37-.11,2.58-.77.65-.19,2.82-.76.63-.16c1-.26,2-.52,3.07-.76a140.84,140.84,0,0,1,32-3.67H256V8c-38.77,0-71.67,82.58-73.25,182l.35-.2.62-.37Z"/><path class="cls-4" d="M258,371.78l-.42.2Z"/><path class="cls-4" d="M257.3,372.07a3.17,3.17,0,0,1-.44.12A3.17,3.17,0,0,0,257.3,372.07Z"/><path class="cls-4" d="M256.66,372.23a3.68,3.68,0,0,1-.66.06A3.68,3.68,0,0,0,256.66,372.23Z"/><path class="cls-4" d="M253.88,371.68a4.19,4.19,0,0,1-.54-.4A4.19,4.19,0,0,0,253.88,371.68Z"/><path class="cls-4" d="M254.48,372a3.42,3.42,0,0,1-.43-.21A3.42,3.42,0,0,0,254.48,372Z"/><path class="cls-4" d="M255.14,372.19a3.17,3.17,0,0,1-.44-.12A3.17,3.17,0,0,0,255.14,372.19Z"/><path class="cls-4" d="M336.23,200.13l-.06.05a3.7,3.7,0,0,1-.46.42h0a3.07,3.07,0,0,1-.42.28l-.08,0L335,201l-.41.17-.18.06-.48.11-.12,0a4.29,4.29,0,0,1-.62,0h-.1a4.43,4.43,0,0,1-.51-.06l-.19,0a3.45,3.45,0,0,1-.43-.12l-.18-.07a5,5,0,0,1-.57-.28c-1-.62-2-Line truncated

After

Width:  |  Height:  |  Size: 22 KiB

@@ -0,0 +1 @@
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><defs><style>.cls-1{fill:#365558;fill-rule:evenodd;}.cls-2{fill:#e6eaea;}.cls-3{fill:#fff;}</style></defs><title>github-program</title><path class="cls-1" d="M384,512H128A128,128,0,0,1,0,384V128A128,128,0,0,1,128,0H384A128,128,0,0,1,512,128V384A128,128,0,0,1,384,512"/><path class="cls-1" d="M240,144H384A72,72,0,0,0,391.36.37C388.91.23,386.49,0,384,0H128A128,128,0,0,0,0,128V384C0,251.45,107.45,144,240,144"/><path class="cls-1" d="M384,256A128,128,0,1,0,512,384V128A128,128,0,0,1,384,256"/><path class="cls-2" d="M94.22,185.82a176.16,176.16,0,0,0,91.51,231.72A16,16,0,0,0,208,402.81V376h-3.76a64,64,0,0,1-54.88-31.07l-1.25-2.07A14.11,14.11,0,0,0,136,336a8,8,0,0,1,0-16h0a32,32,0,0,1,25.59,12.79h0A48,48,0,0,0,200,352h8.67a48,48,0,0,1,13.39-25.94c1-1,2-1.95,3.11-2.85A1.82,1.82,0,0,0,224,320h0a80,80,0,0,1-63.91-128.12,77.29,77.29,0,0,1,.77-36.87l1.36-5.08a8,8,0,0,1,9.8-5.66l5.08,1.36a77.38,77.38,0,0,1,30.4,16.07A80.85,80.85,0,0,1,224,160h32V79.72A176.19,176.19,0,0,0,94.22,185.82Z"/><path class="cls-3" d="M325.93,94.3A175.57,175.57,0,0,0,256,79.72V160h32a81,81,0,0,1,16.51,1.7,77.32,77.32,0,0,1,30.4-16.07l5.08-1.36a8,8,0,0,1,9.79,5.66l1.36,5.08a77.12,77.12,0,0,1,.77,36.86A80,80,0,0,1,288,320h0a1.82,1.82,0,0,0-1.16,3.21c1.07.9,2.11,1.85,3.11,2.85A48,48,0,0,1,304,360s0,23.91.13,42.81a15.86,15.86,0,0,0,22.08,14.6,176.16,176.16,0,0,0-.28-323.11Z"/></svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><defs><style>.cls-1{fill:#365558;}.cls-2{fill:#f0353d;}.cls-3{fill:#fd6a33;}.cls-4{fill:#0295aa;}.cls-5{fill:#4acfd9;}.cls-6{fill:#e6eaea;}.cls-7{fill:#cdd4d5;}.cls-8{fill:#9baaab;}.cls-9{fill:#ff8c29;}.cls-10{fill:#fff;}</style></defs><title>influencer-program</title><g id="Layer_2" data-name="Layer 2"><polygon class="cls-1" points="408.9 342.47 408.9 243.3 342.9 243.3 342.9 258.31 393.89 258.31 393.89 342.47 408.9 342.47"/><rect class="cls-1" x="312.46" y="243.3" width="15.43" height="15.02"/><rect class="cls-1" x="312.46" y="180.22" width="15.75" height="15.02"/><polygon class="cls-1" points="382.01 122.49 349.13 180.22 343.22 180.22 343.22 195.24 357.86 195.24 395.06 129.92 382.01 122.49"/><rect class="cls-1" x="264.91" y="101.18" width="15.02" height="16.02"/><polygon class="cls-1" points="264.91 86.16 279.92 86.16 279.92 45.62 194.34 45.62 194.34 60.63 264.91 60.63 264.91 86.16"/><path class="cls-2" d="M126.54,15.12h-.07a28.51,28.51,0,0,0-20.29,8.48l-9.89,10L86,23.4a28.47,28.47,0,0,0-20.15-8.28h-.21A28.49,28.49,0,0,0,45.38,23.7a28.87,28.87,0,0,0,.31,40.72L68,86.91l69.59-69.59A28.62,28.62,0,0,0,126.54,15.12Z"/><path class="cls-3" d="M146.89,64.22a28.82,28.82,0,0,0-9.31-46.9L68,86.91l28.24,28.48Z"/><path class="cls-3" d="M157.4,12.86a44.43,44.43,0,0,0-8.65-6.71l-5.53,5.53a36.32,36.32,0,0,1,9,57.82L99.23,123l-3,3L62.68,92.22l-5.31,5.31,38.88,39.2L157.56,74.8a43.53,43.53,0,0,0,12.72-32h0A43.72,43.72,0,0,0,157.4,12.86Z"/><path class="cls-2" d="M40.41,69.76A36.27,36.27,0,0,1,91.28,18.05l5,4.9,4.61-4.65a36.22,36.22,0,0,1,42.37-6.62l5.53-5.53A43.46,43.46,0,0,0,126.54.1h-.11A43.41,43.41,0,0,0,96.19,12.36,43.39,43.39,0,0,0,65.85.1h-.32A43.45,43.45,0,0,0,34.68,13.17a43.89,43.89,0,0,0,.4,61.88L57.37,97.53l5.31-5.31Z"/><path class="cls-3" d="M146.8,23.5a28.82,28.82,0,0,1,.09,40.72L96.23,115.39,68,86.91l-5.31,5.31,33.56,33.84,3-3,53-53.54a36.33,36.33,0,0,0-9-57.82l-5.64,5.64A28.59,28.59,0,0,1,146.8,23.5Z"/><path class="cls-2" d="M100.85,18.3,96.24,23l-5-4.9A36.27,36.27,0,0,0,40.41,69.76L62.68,92.22,68,86.91,45.69,64.42a28.87,28.87,0,0,1-.31-40.72,28.49,28.49,0,0,1,20.26-8.58h.21A28.47,28.47,0,0,1,86,23.4L96.29,33.56l9.89-10a28.51,28.51,0,0,1,20.29-8.48h.07a28.62,28.62,0,0,1,11,2.2l5.64-5.64A36.22,36.22,0,0,0,100.85,18.3Z"/><polygon class="cls-4" points="415.03 86.55 435.05 66.53 440.36 71.84 486 26.2 346.49 70.95 407.91 104.29 407.91 104.29 420.34 91.86 415.03 86.55"/><polygon class="cls-5" points="486 26.2 486 26.2 440.36 71.84 445.67 77.15 425.65 97.17 420.34 91.86 407.91 104.29 441.25 165.71 486 26.2"/><rect class="cls-6" x="418.85" y="80.75" width="28.31" height="7.51" transform="translate(67.07 330.93) rotate(-45)"/><rect class="cls-7" x="413.54" y="75.44" width="28.31" height="7.51" transform="translate(69.27 325.62) rotate(-45)"/><polygon class="cls-5" points="497.61 14.59 497.61 14.59 443.04 184.73 402.38 109.83 402.37 109.83 396.84 115.36 396.84 115.36 444.82 203.75 509.21 2.99 497.61 14.59"/><polygon class="cls-4" points="327.47 69.16 497.61 14.59 509.21 2.99 509.21 2.98 308.45 67.38 396.84 115.36 402.37 109.83 327.47 69.16"/><polygon class="cls-4" points="402.37 109.83 407.91 104.29 407.91 104.29 346.49 70.95 486 26.2 497.61 14.59 327.47 69.16 402.37 109.83"/><polygon class="cls-5" points="486 26.2 441.25 165.71 407.91 104.29 402.37 109.83 402.38 109.83 443.04 184.73 497.61 14.59 497.61 14.59 486 26.2 486 26.2"/><rect class="cls-6" x="87.47" y="55.42" width="28.31" height="7.51" transform="translate(-12.08 89.19) rotate(-45)"/><rect class="cls-7" x="82.16" y="50.11" width="28.31" height="7.51" transform="translate(-9.88 83.88) rotate(-45)"/><path class="cls-4" d="M15.65,241.94v11.82A10.24,10.24,0,0,0,25.88,264H42V231.7H25.88A10.24,10.24,0,0,0,15.65,241.94Z"/><polygon class="cls-4" points="280.39 160.74 259.93 160.74 259.93 188.73 280.39 163.92 280.39 160.74"/><polygon class="cls-5" points="252.42 207.33 252.42 222.34 259.93 222.34 259.93 334.96 280.39 334.96 280.39 163.92 259.93 188.73 259.93 207.33 252.42 207.33"/><polygon class="cls-4" points="126.83 320.41 126.83 350.06 151.29 320.41 126.83 320.41"/><path class="cls-5" d="M182.78,443.77V320.41H151.29l-24.46,29.65v93.71Zm-34.9-102.9h15v15h-15Zm15,30V411h-15V370.9Z"/><polygon class="cls-5" points="244.91 207.33 244.91 206.93 163.68 305.39 197.79 305.39 217.74 305.39 244.91 326.94 244.91 222.34 252.41 222.34 252.41 207.33 244.91 207.33"/><path class="cls-4" d="M244.91,168.76l-27.17,21.55H85a28,28,0,0,0-28,28v59.14a28,28,0,0,0,28,28h78.66l81.23-98.46ZM150.07,286.65H82.65v-15h67.42Zm0-31H82.65v-15h67.42Zm0-31H82.65v-15h67.42Z"/><rect class="cls-5" x="250.37" y="214.83" height="7.51"/><polygon class="cls-4" points="154.8 486.07 175.27 458.79 134.34 458.79 154.8 486.07"/><rect class="cls-6" x="147.88" y="370.9" width="15.02" height="40.04"/><rect class="cls-6" x="147.88" y="340.87" width="15.02" height="15.02"/><rect class="cls-8" x="82.65" y="209.63" width="67.42" height="15.02"/><rect class="clLine truncated

After

Width:  |  Height:  |  Size: 10 KiB

@@ -0,0 +1 @@
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><defs><style>.cls-1{fill:#faab18;}.cls-2{fill:none;}.cls-3{fill:#0295aa;}.cls-4{fill:#e6eaea;}.cls-5{fill:#f0353d;}.cls-6{fill:#ff8c29;}.cls-7{fill:#9baaab;}.cls-8{fill:#4acfd9;}.cls-9{fill:#fd6a33;}.cls-10{fill:#ffd1a9;}</style></defs><title>mentorship-program</title><path class="cls-1" d="M250.08,381a50.32,50.32,0,0,0,5.92.36h.29V381Z"/><path class="cls-2" d="M256,488.57l-27.22,20.76a13,13,0,0,1-7.87,2.67h35.21l0-23.31Z"/><path class="cls-3" d="M171.71,376.86a18.91,18.91,0,0,1-15.41-12.8,18.49,18.49,0,0,1,4.84-19.27,3.52,3.52,0,0,0-.6-5.57,19,19,0,0,1-6.14-6.15H132.89a7.67,7.67,0,0,1-7.72,7.62H88.08A13.88,13.88,0,0,1,74.33,329a28.16,28.16,0,0,0-23.86-23.51A13.79,13.79,0,0,1,38.64,292V86.76A13.79,13.79,0,0,1,50.47,73.22,28.16,28.16,0,0,0,74.33,49.71,13.87,13.87,0,0,1,88.07,38.06H256.73l0-30.45H20.6A12.8,12.8,0,0,0,7.73,20.3V358.45A12.8,12.8,0,0,0,20.6,371.13H134.81a7.67,7.67,0,0,1,7.73,7.61h31.87c0-.05,0-.1-.08-.14A3.54,3.54,0,0,0,171.71,376.86Z"/><path class="cls-4" d="M151.75,321.36a18.71,18.71,0,0,1,12.37-15.67,3.52,3.52,0,0,0,2.19-2.25,3.46,3.46,0,0,0-.43-3.08,18.46,18.46,0,0,1-.67-19.84A19.05,19.05,0,0,1,183,271.16a3.55,3.55,0,0,0,2.93-1.17,3.47,3.47,0,0,0,.88-3,18.54,18.54,0,0,1,7.58-18.39,19.19,19.19,0,0,1,20.1-1.43,3.59,3.59,0,0,0,3.16.1,3.53,3.53,0,0,0,2-2.38,18.84,18.84,0,0,1,14.53-13.76,19.13,19.13,0,0,1,18.95,6.75,3.58,3.58,0,0,0,2.84,1.36l.47,0,.07-51.88H99.41a7.61,7.61,0,1,1,0-15.22H256.56l0-22.27H99.41a7.61,7.61,0,1,1,0-15.22h157.2l0-28H153.06a7.61,7.61,0,1,1,0-15.22h103.6l0-38.1H89.4A43.63,43.63,0,0,1,54.08,88.07V290.68A43.63,43.63,0,0,1,89.4,325.46h35.77a7.66,7.66,0,0,1,7.72,7.61H154.4A18.47,18.47,0,0,1,151.75,321.36Z"/><path class="cls-5" d="M238.89,424.93a3.65,3.65,0,0,0-5.56-1.16,19.18,19.18,0,0,1-9.86,4.22v66.14l24.65-18.8a13,13,0,0,1,7.88-2.65h.17l.05-37.28H256A19,19,0,0,1,238.89,424.93Z"/><path class="cls-6" d="M211.21,378.74h-21a18.48,18.48,0,0,1-1.28,8.62,3.46,3.46,0,0,0,.67,3.71,3.57,3.57,0,0,0,3.68,1.05A19.16,19.16,0,0,1,209.65,395a18.63,18.63,0,0,1,6.09,7.33V382.68C214.17,381.44,212.65,380.12,211.21,378.74Z"/><path class="cls-6" d="M191.49,333.07H175.24a18.51,18.51,0,0,1-3.32,22.63,3.44,3.44,0,0,0-.92,3.66,3.52,3.52,0,0,0,2.93,2.43,19,19,0,0,1,13.78,9.2,18.52,18.52,0,0,1,2.48,7.75h21A62.87,62.87,0,0,1,191.49,333.07Z"/><path class="cls-6" d="M256,269.52l.43,0,0-15.09-.45,0a19,19,0,0,1-15-7.17,3.59,3.59,0,0,0-3.6-1.28,3.53,3.53,0,0,0-2.76,2.62,18.77,18.77,0,0,1-10.73,12.54,19.2,19.2,0,0,1-16.64-.54,3.58,3.58,0,0,0-3.82.27,3.46,3.46,0,0,0-1.44,3.5,18.48,18.48,0,0,1-4.63,15.76A19.08,19.08,0,0,1,182,286.35a3.55,3.55,0,0,0-3.38,1.78,3.42,3.42,0,0,0,.13,3.77A18.47,18.47,0,0,1,181,308.15,18.82,18.82,0,0,1,169.46,320a3.54,3.54,0,0,0-.67,6.37,18.76,18.76,0,0,1,6.45,6.72h16.25C191.49,298,220.43,269.52,256,269.52Z"/><path class="cls-6" d="M256,396.62a64.84,64.84,0,0,1-40.26-13.94v19.65a18.44,18.44,0,0,1,1.76,7.13,3.49,3.49,0,0,0,2.15,3.13,3.58,3.58,0,0,0,3.79-.52,19.15,19.15,0,0,1,12.16-4.35,20.29,20.29,0,0,1,4,.41,18.94,18.94,0,0,1,13.14,10.08,3.55,3.55,0,0,0,3.25,2h.24l0-23.57Z"/><path class="cls-5" d="M256.29,378.74H240a49.76,49.76,0,0,0,10.1,2.3h6.21Z"/><path class="cls-5" d="M256.35,333.07H206.94a48.46,48.46,0,0,0,33,45.67h16.31Z"/><path class="cls-5" d="M256.41,284.76H256c-27.05,0-49.06,21.67-49.06,48.32h49.41Z"/><path class="cls-3" d="M0,20.3V358.45a20.45,20.45,0,0,0,20.6,20.29H142.54a7.67,7.67,0,0,0-7.73-7.61H20.6A12.8,12.8,0,0,1,7.73,358.45V20.3A12.8,12.8,0,0,1,20.6,7.61H256.77V0H20.6A20.46,20.46,0,0,0,0,20.3Z"/><path class="cls-3" d="M74.33,49.71A28.16,28.16,0,0,1,50.47,73.22,13.79,13.79,0,0,0,38.64,86.76V292a13.79,13.79,0,0,0,11.83,13.54A28.16,28.16,0,0,1,74.33,329a13.88,13.88,0,0,0,13.75,11.66h37.09a7.67,7.67,0,0,0,7.72-7.62H88.07A6.1,6.1,0,0,1,82,327.94a35.77,35.77,0,0,0-30.41-30,6.07,6.07,0,0,1-5.21-6V86.76a6.07,6.07,0,0,1,5.21-6A35.76,35.76,0,0,0,82,50.81a6.1,6.1,0,0,1,6.09-5.14H256.72V38.06H88.07A13.87,13.87,0,0,0,74.33,49.71Z"/><path class="cls-4" d="M125.17,325.46H89.4a43.63,43.63,0,0,0-35.32-34.78V88.07A43.63,43.63,0,0,0,89.4,53.28H256.71V45.67H88.07A6.1,6.1,0,0,0,82,50.81a35.76,35.76,0,0,1-30.41,30,6.07,6.07,0,0,0-5.21,6V292a6.07,6.07,0,0,0,5.21,6,35.77,35.77,0,0,1,30.41,30,6.1,6.1,0,0,0,6.09,5.13h44.82A7.66,7.66,0,0,0,125.17,325.46Z"/><path class="cls-3" d="M226.77,381c-1.21-.72-2.39-1.49-3.54-2.3h-12c1.44,1.38,3,2.7,4.53,3.94V381Z"/><path class="cls-3" d="M199.22,333.07h-7.73a62.87,62.87,0,0,0,19.72,45.67h12A55.61,55.61,0,0,1,199.22,333.07Z"/><path class="cls-3" d="M256,277.14h.42v-7.61l-.43,0c-35.57,0-64.51,28.51-64.51,63.55h7.73C199.22,302.18,224.64,277.14,256,277.14Z"/><path class="cls-3" d="M256,389a57.17,57.17,0,0,1-29.23-8h-11v1.64A64.84,64.84,0,0,0,256,396.62h.27V389Z"/><path class="cls-3" d="M240,378.74H223.23c1.15.81,2.33,1.58,3.54,2.3h23.31A49.76,49.76,0,0,1,240,378.74Z"/><path class="cls-3" d="M206.94,333.07h-7.72a55.61,55.61,0,0,0,24,45.67H240A48.46,48.46,0,0,1,2Line truncated

After

Width:  |  Height:  |  Size: 18 KiB

@@ -0,0 +1 @@
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><defs><style>.cls-1{fill:none;}.cls-2{fill:#4acfd9;}.cls-3{fill:#fd6a33;}.cls-4{fill:#cdd4d5;}.cls-5{fill:#afafaf;}.cls-6{fill:#ffb69f;}.cls-7{fill:#f0353d;}.cls-8{fill:#0295aa;}.cls-9{fill:#365558;}.cls-10{fill:#ffd1a9;}.cls-11{fill:#ff8c29;}.cls-12{fill:#9baaab;}.cls-13{fill:#e6eaea;}</style></defs><title>referral-program</title><path class="cls-1" d="M470.73,29.38h-.44l.44.4Z"/><path class="cls-2" d="M231.27,258.1c0-62.69-50.38-113.53-112.59-113.55h0v51.17h26.23a11.58,11.58,0,0,0,11.34-9.47,5.37,5.37,0,0,1,.84-2,5.28,5.28,0,0,1,7.33-1.44l.06,0,8.89,6.13a29.64,29.64,0,0,1,4.14,3.44,30.58,30.58,0,0,1,.29,43l-4.59,4.71V254a19.61,19.61,0,1,1-.36,39.22h-1.47a54.43,54.43,0,0,1-52.7,40.88v11.45a86.93,86.93,0,0,0,38.76-9.15,5.3,5.3,0,0,1,7.15,2.26l0,0a5.37,5.37,0,0,1-2.28,7.22l-.15.08a97.59,97.59,0,0,1-43.5,10.27v15.28C180.84,371.54,231.25,320.76,231.27,258.1Zm-43.65,69.43c-2.13,2.14-4.36,4.19-6.65,6.11a5.31,5.31,0,0,1-7.47-.66l0,0a5.39,5.39,0,0,1,.69-7.55h0c2-1.71,4-3.55,5.93-5.45a5.29,5.29,0,0,1,7.48.09l0,0A5.39,5.39,0,0,1,187.62,327.53Z"/><path class="cls-3" d="M16.63,306.2a112.59,112.59,0,0,0,102,65.36h.07V356.28h0c-53.88,0-97.54-44-97.51-98.35a98.68,98.68,0,0,1,28.53-69.42,5.28,5.28,0,0,1,7.47,0l.05,0a5.38,5.38,0,0,1,0,7.58h0a88,88,0,0,0,0,123.91,86.53,86.53,0,0,0,61.37,25.61h0v-11.5a54.55,54.55,0,0,1-52.59-40.88H64.58A19.62,19.62,0,0,1,64.22,254V223.57a27.78,27.78,0,0,1,27.63-27.85h26.81V144.55C56.46,144.53,6,195.32,6,258c0,2.91.13,5.89.34,8.85v.5a5.34,5.34,0,0,1-4.73,5.46,118.69,118.69,0,0,0,8.11,30.76A5.42,5.42,0,0,1,16.63,306.2Z"/><path class="cls-4" d="M170.21,227.85a18.9,18.9,0,0,0,2.2-2.68,19.78,19.78,0,0,0-5.06-27.37L164,195.47a22.18,22.18,0,0,1-19.08,11H118.66v26.1h47Z"/><path class="cls-5" d="M91.85,206.44a17.07,17.07,0,0,0-17,17.12v9h43.8v-26.1Z"/><path class="cls-6" d="M64.21,264.71a8.9,8.9,0,0,0,.1,17.79c-.06-1.08-.1-2.16-.1-3.26Z"/><path class="cls-6" d="M181.65,273.6a8.88,8.88,0,0,0-8.47-8.89v14.53c0,1.09,0,2.19-.1,3.26A8.89,8.89,0,0,0,181.65,273.6Z"/><path class="cls-6" d="M74.85,279.24a44,44,0,0,0,43.81,44.18V310.47A20.59,20.59,0,0,1,99.4,297a5.34,5.34,0,0,1,10-3.73h0a10,10,0,0,0,5.81,5.84,9.78,9.78,0,0,0,3.47.63V284.8a5.32,5.32,0,0,1-5.28-5.35v-13.3a5.32,5.32,0,0,1,5.28-5.35V243.27H74.85Zm15.65-16a5.32,5.32,0,0,1,10.64,0h0v2.91a5.32,5.32,0,0,1-10.64,0h0Z"/><path class="cls-6" d="M118.69,260.79a5.34,5.34,0,0,1,5.32,5.36h0v13.3a5.34,5.34,0,0,1-5.32,5.36h0v14.95a10,10,0,0,0,9.38-6.5A5.33,5.33,0,0,1,138.36,296a5.56,5.56,0,0,1-.36,1,20.72,20.72,0,0,1-12.07,12.14,20.06,20.06,0,0,1-7.27,1.3v12.95h0a44,44,0,0,0,43.85-44.15v-36H118.66V260.8Zm17.56,2.45a5.32,5.32,0,0,1,10.64,0h0v2.91a5.32,5.32,0,0,1-10.64,0h0Z"/><path class="cls-7" d="M118.64,345.56A86.56,86.56,0,0,1,57.27,320a88.05,88.05,0,0,1-.09-123.86h0a5.38,5.38,0,0,0,0-7.58,5.28,5.28,0,0,0-7.47-.05l-.05.05A98.68,98.68,0,0,0,21.13,258c0,54.3,43.63,98.33,97.51,98.35h0Z"/><path class="cls-8" d="M180.1,320c-1.89,1.9-3.89,3.74-5.93,5.45h0a5.39,5.39,0,0,0-.69,7.55,5.29,5.29,0,0,0,7.46.71l0,0c2.29-1.92,4.52-4,6.65-6.11a5.39,5.39,0,0,0,0-7.45,5.29,5.29,0,0,0-7.48-.17Z"/><path class="cls-8" d="M162.31,345.93a5.37,5.37,0,0,0,2.28-7.22,5.3,5.3,0,0,0-7.13-2.32l0,0a86.93,86.93,0,0,1-38.76,9.15v10.72A97.47,97.47,0,0,0,162.16,346Z"/><path class="cls-9" d="M138,297a5.35,5.35,0,0,0-10-3.78h0a10,10,0,0,1-9.38,6.5v10.72a20.06,20.06,0,0,0,7.27-1.3A20.7,20.7,0,0,0,138,297Z"/><path class="cls-9" d="M115.19,299.12a10,10,0,0,1-5.81-5.84,5.34,5.34,0,0,0-10,3.73h0a20.59,20.59,0,0,0,19.26,13.46V299.75A9.89,9.89,0,0,1,115.19,299.12Z"/><path class="cls-9" d="M70.29,287.86H64.57a14.26,14.26,0,0,1,0-28.51h5V237.92h0V223.57a22.42,22.42,0,0,1,22.32-22.49h26.82v-5.36H91.85a27.78,27.78,0,0,0-27.63,27.85V254a19.61,19.61,0,1,0,.36,39.22h1.49a54.55,54.55,0,0,0,52.59,40.88v-5.38A49.26,49.26,0,0,1,70.29,287.86Z"/><path class="cls-9" d="M132.6,332.29a54.67,54.67,0,0,0,38.76-39.06h1.47a19.61,19.61,0,1,0,.36-39.22V240.1l4.59-4.71a30.58,30.58,0,0,0-.29-43,29.64,29.64,0,0,0-4.14-3.44l-8.89-6.13a5.28,5.28,0,0,0-7.35,1.34l0,.06a5.37,5.37,0,0,0-.84,2,11.58,11.58,0,0,1-11.34,9.47H118.66v5.36h26.23a16.93,16.93,0,0,0,16.58-13.83l8.89,6.13a25.16,25.16,0,0,1,6.44,34.81,24.86,24.86,0,0,1-2.8,3.42l-6.15,6.31v21.43h5a14.26,14.26,0,0,1,0,28.51h-5.73A49.33,49.33,0,0,1,127.27,328a47.76,47.76,0,0,1-8.61.75v5.38A53.79,53.79,0,0,0,132.6,332.29Z"/><path class="cls-9" d="M144.89,206.44a22.18,22.18,0,0,0,19.08-11l3.38,2.33a19.78,19.78,0,0,1,5.06,27.37,18.9,18.9,0,0,1-2.2,2.68l-4.58,4.69h-47v5.37h49.19l6.15-6.31a24.86,24.86,0,0,0,2.8-3.42,25.16,25.16,0,0,0-6.44-34.81l-8.89-6.13A16.93,16.93,0,0,1,144.86,201h-26.2v5.36Z"/><path class="cls-9" d="M69.52,223.57v14.34h49.14v-5.37H74.86v-9a17.07,17.07,0,0,1,17-17.12h26.81v-5.36H91.84A22.42,22.42,0,0,0,69.52,223.57Z"/><path class="cls-9" d="M118.66,243.27h43.88v36a44,44,0,0,1-43.82,44.18h0v5.31a47.76,47.76,0,0,0,8.61-.75,49.32,49.32,0,0,0,39.81-40.12h5.73a14.26,14.26,0,0,Line truncated

After

Width:  |  Height:  |  Size: 36 KiB

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><defs><style>.cls-1{fill:#fd6a33;}.cls-2{fill:#ff8c29;}.cls-3{fill:#ffb300;}.cls-4{fill:#ffa000;}.cls-5{fill:#f0353d;}.cls-6{fill:#e6eaea;}.cls-7{fill:#fff;}.cls-8{fill:#e3493c;}.cls-9{fill:#ffd1a9;}</style></defs><title>staking-program</title><g id="Layer_16" data-name="Layer 16"><path class="cls-1" d="M276.53,17a7.5,7.5,0,0,0-13,7.5l5.72,9.9a7.5,7.5,0,1,0,13-7.5Z"/><path class="cls-1" d="M246.81,56.89l-9.9-5.71a7.5,7.5,0,0,0-7.5,13l9.9,5.71a7.39,7.39,0,0,0,3.75,1,7.5,7.5,0,0,0,3.75-14Z"/><path class="cls-1" d="M231.12,100.57H219.68a7.5,7.5,0,0,0,0,15h11.44a7.5,7.5,0,0,0,0-15Z"/><path class="cls-2" d="M421.2,100.57H409.77a7.5,7.5,0,0,0,0,15H421.2a7.5,7.5,0,0,0,0-15Z"/><path class="cls-2" d="M414.23,54A7.51,7.51,0,0,0,404,51.21l-9.9,5.72a7.5,7.5,0,0,0,7.5,13l9.9-5.72A7.5,7.5,0,0,0,414.23,54Z"/><path class="cls-2" d="M374.63,14.33a7.48,7.48,0,0,0-10.24,2.74L358.67,27a7.5,7.5,0,1,0,13,7.5l5.72-9.9a7.49,7.49,0,0,0-2.75-10.24Z"/><path class="cls-2" d="M454.45,182.06H192.5a8.1,8.1,0,0,1-8.1-8.1V142.78a8.1,8.1,0,0,1,8.1-8.1h262a8.1,8.1,0,0,1,8.1,8.1V174A8.1,8.1,0,0,1,454.45,182.06Z"/><path class="cls-2" d="M454.45,134.67h-30.9a8.1,8.1,0,0,1,8.1,8.1V174a8.1,8.1,0,0,1-8.1,8.1h30.9a8.1,8.1,0,0,0,8.1-8.1V142.77A8.1,8.1,0,0,0,454.45,134.67Z"/><rect class="cls-1" x="238.34" y="137.39" width="8.17" height="19.4"/><rect class="cls-1" x="304.72" y="137.39" width="8.17" height="19.4"/><rect class="cls-1" x="335.72" y="137.39" width="8.17" height="19.4"/><rect class="cls-1" x="271.06" y="137.39" width="8.17" height="19.4"/><rect class="cls-1" x="400.13" y="137.39" width="8.17" height="19.4"/><rect class="cls-1" x="369.49" y="137.39" width="8.17" height="19.4"/><path class="cls-1" d="M485.36,227.4h-262a8.09,8.09,0,0,1-8.1-8.1V188.12a8.09,8.09,0,0,1,8.1-8.1h262a8.1,8.1,0,0,1,8.1,8.1V219.3A8.1,8.1,0,0,1,485.36,227.4Z"/><path class="cls-1" d="M485.36,180H454.45a8.1,8.1,0,0,1,8.1,8.1V219.3a8.1,8.1,0,0,1-8.1,8.1h30.91a8.1,8.1,0,0,0,8.1-8.1V188.12A8.1,8.1,0,0,0,485.36,180Z"/><rect class="cls-2" x="251.67" y="178.61" width="9.74" height="19.4"/><rect class="cls-2" x="330.82" y="178.61" width="9.74" height="19.4"/><rect class="cls-2" x="367.79" y="178.61" width="9.74" height="19.4"/><rect class="cls-2" x="290.69" y="178.61" width="9.74" height="19.4"/><rect class="cls-2" x="444.58" y="178.61" width="9.74" height="19.4"/><rect class="cls-2" x="408.05" y="178.61" width="9.74" height="19.4"/><path class="cls-2" d="M503.9,274.79H242a8.1,8.1,0,0,1-8.1-8.1V235.5a8.1,8.1,0,0,1,8.1-8.1h262a8.1,8.1,0,0,1,8.1,8.1v31.19A8.1,8.1,0,0,1,503.9,274.79Z"/><path class="cls-2" d="M503.9,227.4H473a8.11,8.11,0,0,1,8.11,8.1v31.19a8.1,8.1,0,0,1-8.11,8.1H503.9a8.1,8.1,0,0,0,8.1-8.1V235.5A8.1,8.1,0,0,0,503.9,227.4Z"/><rect class="cls-1" x="290.59" y="224.25" width="8.17" height="19.4"/><rect class="cls-1" x="356.98" y="224.25" width="8.17" height="19.4"/><rect class="cls-1" x="387.98" y="224.25" width="8.17" height="19.4"/><rect class="cls-1" x="323.32" y="224.25" width="8.17" height="19.4"/><rect class="cls-1" x="452.38" y="224.25" width="8.17" height="19.4"/><rect class="cls-1" x="421.74" y="224.25" width="8.17" height="19.4"/><path class="cls-1" d="M503.9,322.18H242a8.1,8.1,0,0,1-8.1-8.1V282.89a8.1,8.1,0,0,1,8.1-8.1h262a8.1,8.1,0,0,1,8.1,8.1v31.19A8.1,8.1,0,0,1,503.9,322.18Z"/><path class="cls-1" d="M503.9,274.79H473a8.11,8.11,0,0,1,8.11,8.1v31.19a8.11,8.11,0,0,1-8.11,8.1H503.9a8.1,8.1,0,0,0,8.1-8.1V282.89A8.1,8.1,0,0,0,503.9,274.79Z"/><rect class="cls-2" x="290.59" y="274.44" width="8.17" height="19.4"/><rect class="cls-2" x="356.98" y="274.44" width="8.17" height="19.4"/><rect class="cls-2" x="387.98" y="274.44" width="8.17" height="19.4"/><rect class="cls-2" x="323.32" y="274.44" width="8.17" height="19.4"/><rect class="cls-2" x="452.38" y="274.44" width="8.17" height="19.4"/><rect class="cls-2" x="421.74" y="274.44" width="8.17" height="19.4"/><path class="cls-2" d="M503.9,369.57H242a8.1,8.1,0,0,1-8.1-8.1V330.28a8.1,8.1,0,0,1,8.1-8.1h262a8.1,8.1,0,0,1,8.1,8.1v31.19A8.1,8.1,0,0,1,503.9,369.57Z"/><path class="cls-2" d="M503.9,322.18H473a8.11,8.11,0,0,1,8.11,8.1v31.19a8.11,8.11,0,0,1-8.11,8.1H503.9a8.1,8.1,0,0,0,8.1-8.1V330.28A8.1,8.1,0,0,0,503.9,322.18Z"/><rect class="cls-1" x="290.59" y="322.18" width="8.17" height="19.4"/><rect class="cls-1" x="356.98" y="322.18" width="8.17" height="19.4"/><rect class="cls-1" x="387.98" y="322.18" width="8.17" height="19.4"/><rect class="cls-1" x="323.32" y="322.18" width="8.17" height="19.4"/><rect class="cls-1" x="452.38" y="322.18" width="8.17" height="19.4"/><rect class="cls-1" x="421.74" y="322.18" width="8.17" height="19.4"/><path class="cls-1" d="M503.9,417H242a8.1,8.1,0,0,1-8.1-8.1V377.67a8.1,8.1,0,0,1,8.1-8.1h262a8.1,8.1,0,0,1,8.1,8.1v31.19A8.1,8.1,0,0,1,503.9,417Z"/><path class="cls-1" d="M503.9,369.57H473a8.11,8.11,0,0,1,8.11,8.1v31.19A8.11,8.11,0,0,1,473,417H503.9a8.1,8.1,0,0,0,8.1-8.1V377.67A8.1,8.1,0,0,0,503.9,369.57Z"/><rect class="cls-2" Line truncated

After

Width:  |  Height:  |  Size: 11 KiB

@@ -0,0 +1 @@
<svg id="Capa_1" data-name="Capa 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><defs><style>.cls-1{fill:#f0353d;}.cls-2{fill:#fd6a33;}.cls-3{fill:#0295aa;}.cls-4{fill:#4acfd9;}.cls-5{fill:#9baaab;}.cls-6{fill:#ffd1a9;}.cls-7{fill:#365558;}.cls-8{fill:#cdd4d5;}</style></defs><title>tokens-awarded</title><path class="cls-1" d="M64.12,502.44H22.92A15.41,15.41,0,0,1,7.5,487V366.63a15.41,15.41,0,0,1,15.42-15.41h41.2a15.41,15.41,0,0,1,15.41,15.41V487A15.41,15.41,0,0,1,64.12,502.44Z"/><path class="cls-2" d="M79.52,366.63V487a15.41,15.41,0,0,1-15.41,15.41H44.23A15.41,15.41,0,0,0,59.64,487V366.63a15.41,15.41,0,0,0-15.41-15.41H64.11A15.41,15.41,0,0,1,79.52,366.63Z"/><path class="cls-3" d="M215.8,9.56A14.25,14.25,0,0,0,202.2,28c11.27,37,33.23,47.69,33.23,47.69h40.81V9.56Z"/><path class="cls-4" d="M350.29,28a14.26,14.26,0,0,0-13.6-18.45H276.24V75.7h40.82S339,65,350.29,28Z"/><path class="cls-3" d="M235.34,108.53l-48.17,29.86a72.66,72.66,0,0,0-34.79,61.66v137.4a13.64,13.64,0,0,1-13.63,13.64,18.31,18.31,0,0,0-18.31,18.31v35a18.31,18.31,0,0,0,18.31,18.31H276.24V108.53Z"/><path class="cls-4" d="M413.74,351.09h0a13.64,13.64,0,0,1-13.64-13.64V200.05a72.65,72.65,0,0,0-34.78-61.66l-48.18-29.86h-40.9V422.72h137.5a18.31,18.31,0,0,0,18.31-18.31v-35A18.31,18.31,0,0,0,413.74,351.09Z"/><path class="cls-5" d="M296.17,75.7h20.89v32.83H296.17Z"/><path class="cls-6" d="M499.68,391.21a30.16,30.16,0,0,0-37.3-11.31L324.07,425.58l-.25,0a23.93,23.93,0,0,0-4.51-46.39l-75.89-14.93c-10.12-2-45-9.54-51.91-9.09-14.14.9-26.8,11.3-37.71,19-24.53,17.4-49.16,12.21-74.27,3.7v92.53L140,466.89A87.69,87.69,0,0,1,177.82,472l57.9,21.33c39.6,14.58,74.44-2.45,111.21-14.84,46.59-15.69,94.3-27.81,140.77-43.87a30.15,30.15,0,0,0,12-43.38Z"/><path class="cls-7" d="M252.16,296.46A7.51,7.51,0,0,0,243.94,309c9.42,6.16,15.86,8,24.14,8.5v7.85a7.51,7.51,0,1,0,15,0v-8.46c16.54-3.29,26.21-17,28.36-29.72,2.76-16.46-5.85-30.76-21.93-36.45-2.11-.75-4.28-1.53-6.43-2.35V213.76a22.31,22.31,0,0,1,8.84,4.28A7.5,7.5,0,0,0,302.05,207a38.24,38.24,0,0,0-19-8.49v-7a7.51,7.51,0,0,0-15,0v7.59c-1.07.24-2.15.51-3.25.84-10.6,3.19-18.52,12.26-20.67,23.66-2,10.41,1.37,20.44,8.71,26.19a70.58,70.58,0,0,0,15.21,8.72v44c-5.61-.43-9.53-1.84-15.92-6Zm10-58.53c-2.91-2.28-4.14-6.72-3.22-11.6.82-4.33,3.65-9.53,9.17-11.69v27.11A42.76,42.76,0,0,1,262.13,237.93Zm22.39,27c13.54,4.79,12.7,16.37,12.12,19.81a21.57,21.57,0,0,1-13.55,16.6V264.38Z"/><rect class="cls-5" x="235.43" y="75.7" width="40.82" height="32.83"/><rect class="cls-8" x="276.25" y="75.7" width="40.81" height="32.83"/></svg>

After

Width:  |  Height:  |  Size: 2.5 KiB

@@ -0,0 +1 @@
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><defs><style>.cls-1{fill:#0295aa;}.cls-2{fill:#365558;}.cls-3{fill:#cdd4d5;}.cls-4{fill:#ff8c29;}.cls-5{fill:#fd6a33;}.cls-6{fill:#4acfd9;}</style></defs><title>tokens-bonus</title><g id="Bonus-Budget-Finance-Income-Monney_Bag" data-name="Bonus-Budget-Finance-Income-Monney Bag"><path class="cls-1" d="M376,401.28A25,25,0,0,1,350.64,424H49.36A25,25,0,0,1,24,401.28a23.89,23.89,0,0,1,10-20.72A32,32,0,0,0,48,355V264A112,112,0,0,1,160,152h80A112,112,0,0,1,352,264v91a32,32,0,0,0,14,25.52A23.89,23.89,0,0,1,376,401.28Z"/><circle class="cls-2" cx="160" cy="296" r="64"/><path class="cls-2" d="M208,408a16,16,0,0,1,16,16H96a16,16,0,0,1,16-16,16,16,0,0,1-16-16V376a16,16,0,0,1,16-16h96a16,16,0,0,1,16,16v16A16,16,0,0,1,208,408Z"/><circle class="cls-3" cx="144" cy="280" r="64"/><path class="cls-4" d="M192,392a16,16,0,0,1,16,16v16a16,16,0,0,1-16,16H96a16,16,0,0,1-16-16V408a16,16,0,0,1,16-16Z"/><path class="cls-5" d="M192,344a16,16,0,0,1,16,16v16a16,16,0,0,1-16,16H96a16,16,0,0,1-16-16V360a16,16,0,0,1,16-16Z"/><path class="cls-5" d="M192,440a16,16,0,0,1,16,16v16a16,16,0,0,1-16,16H96a16,16,0,0,1-16-16V456a16,16,0,0,1,16-16Z"/><path class="cls-2" d="M248,136a16.13,16.13,0,0,1-16,16H168a16,16,0,0,1,0-32h64A16,16,0,0,1,248,136Z"/><path class="cls-1" d="M280,40l-48,80H168L120,40c20,0,20-16,40-16s20,16,40.08,16,20-16,39.92-16S260,40,280,40Z"/><path class="cls-2" d="M336,280a16,16,0,0,1,0-32h48a16,16,0,0,1,0,32Z"/><path class="cls-6" d="M432,184l-48,64H336l-48-64c20,0,12-8,32-8s20,16,40.08,16,20-16,39.92-16S412,184,432,184Z"/><path class="cls-6" d="M256,360a80,80,0,0,1,80-80h48a80,80,0,0,1,80,80v59a32,32,0,0,0,14,25.52,23.89,23.89,0,0,1,10,20.72A25,25,0,0,1,462.64,488H257.36A25,25,0,0,1,232,465.28a23.89,23.89,0,0,1,10-20.72A32,32,0,0,0,256,419Z"/><path d="M352,376h16a8,8,0,0,1,8,8h16a24,24,0,0,0-24-24v-8H352v8a24,24,0,0,0,0,48h16a8,8,0,0,1,0,16H352a8,8,0,0,1-8-8H328a24,24,0,0,0,24,24v8h16v-8a24,24,0,0,0,0-48H352a8,8,0,0,1,0-16Z"/><rect x="408" y="392" width="16" height="16"/><rect x="296" y="392" width="16" height="16"/><path d="M144,240v16a24,24,0,0,1,24,24h16A40.05,40.05,0,0,0,144,240Z"/><rect x="113.57" y="340.8" width="12.86" height="22.4"/><rect x="136.14" y="340.8" width="12.86" height="22.4"/><rect x="163.04" y="340.8" width="12.86" height="22.4"/><rect x="110.43" y="388.8" width="12.86" height="22.4"/><rect x="136.14" y="388.8" width="12.86" height="22.4"/><rect x="163.04" y="388.8" width="12.86" height="22.4"/><rect x="113.57" y="441.6" width="12.86" height="22.4"/><rect x="136.14" y="441.6" width="12.86" height="22.4"/><rect x="163.04" y="441.6" width="12.86" height="22.4"/></g><path class="cls-2" d="M231.91,182v8h16v-8a47.84,47.84,0,0,0-12.32-32H199.91A32,32,0,0,1,231.91,182Z"/><path class="cls-2" d="M320,318h16v-8a32,32,0,0,1,23.32-30.8H331.2A48,48,0,0,0,320,310Z"/><path class="cls-2" d="M151.91,190h16v-8a32,32,0,0,1,32-32H164.23a47.84,47.84,0,0,0-12.32,32Z"/><path class="cls-2" d="M399.32,318h-16v-8A32,32,0,0,0,360,279.18h28.12a47.94,47.94,0,0,1,11.2,30.8Z"/></svg>

After

Width:  |  Height:  |  Size: 3.0 KiB

@@ -0,0 +1 @@
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><defs><style>.cls-1,.cls-7{fill:#9baaab;}.cls-1,.cls-2,.cls-3,.cls-4{fill-rule:evenodd;}.cls-10,.cls-2{fill:#e6eaea;}.cls-3,.cls-8{fill:#cdd4d5;}.cls-4,.cls-9{fill:#365558;}.cls-5{fill:#0295aa;}.cls-6{fill:#4acfd9;}</style></defs><title>tokens-mined</title><path class="cls-1" d="M123.51,201V167.65L56.27,126.41,20,201Z"/><path class="cls-2" d="M56.27,126.41l67.24,41.24,12.12-68.2Z"/><path class="cls-3" d="M135.63,99.45l-12.12,68.2V201h54.66l2.11-54Z"/><path class="cls-2" d="M135.63,99.45,180.28,147l60.44-48.11L197.13,36Z"/><path class="cls-1" d="M180.28,147l-2.11,54h54.67l7.88-34.4Z"/><path class="cls-1" d="M180.28,147l60.44,19.57,61-33.31-61-34.37Z"/><path class="cls-2" d="M197.13,36l43.59,62.92,43.14-46Z"/><path class="cls-3" d="M283.86,52.92l-43.14,46,61,34.37,26.85-37Z"/><path class="cls-3" d="M301.69,133.29l-61,33.31L232.84,201h96.71Z"/><path class="cls-2" d="M301.69,133.29,329.55,201,395.81,93.63l-67.27,2.65Z"/><path class="cls-3" d="M395.81,93.63,329.55,201l98.33-65.07Z"/><path class="cls-1" d="M427.88,135.93,329.55,201h98.33Z"/><path class="cls-1" d="M427.88,135.93V201H492l-11-41.8Z"/><path class="cls-4" d="M492,201H20a10,10,0,0,0-10,10v25a10,10,0,0,0,10,10H492a10,10,0,0,0,10-10V211A10,10,0,0,0,492,201Z"/><path class="cls-5" d="M30,246,65.1,404A50,50,0,0,1,160,426h96V246Z"/><path class="cls-6" d="M256,246V426h96a50,50,0,0,1,94.89-22L482,246Z"/><path class="cls-4" d="M160,426a50,50,0,1,0-50,50h0A50,50,0,0,0,160,426Z"/><path class="cls-4" d="M352,426a50,50,0,1,0,50-50h0A50,50,0,0,0,352,426Z"/><path class="cls-7" d="M110,416h0a10,10,0,1,0,10,10A10,10,0,0,0,110,416Z"/><path class="cls-8" d="M402,416h0a10,10,0,1,0,10,10A10,10,0,0,0,402,416Z"/><path class="cls-9" d="M170,296h0a10,10,0,1,0-10-10A10,10,0,0,0,170,296Z"/><path class="cls-9" d="M134.13,286a7.5,7.5,0,0,0-7.5-7.5H47a7.5,7.5,0,0,0,0,15h79.68A7.51,7.51,0,0,0,134.13,286Z"/><path class="cls-10" d="M431.67,42.89a37.16,37.16,0,0,0-20,10.46c-.44.44-.88.88-1.33,1.38a37.27,37.27,0,0,0-9.19,18.65.63.63,0,0,1-1.2,0,37.16,37.16,0,0,0-10.46-20l-.5-.51h-.06a37.29,37.29,0,0,0-19.47-10,.63.63,0,0,1,0-1.2,27.73,27.73,0,0,0,4.48-1.13,37.71,37.71,0,0,0,26-29.42.63.63,0,0,1,1.2,0,37.24,37.24,0,0,0,10.52,20,36.84,36.84,0,0,0,20,10.52.63.63,0,0,1,0,1.2Z"/><path class="cls-10" d="M467.4,132.78a37.29,37.29,0,0,0-20,10.46c-.44.44-.88.88-1.32,1.39a37.2,37.2,0,0,0-9.19,18.64.64.64,0,0,1-.6.44.62.62,0,0,1-.6-.44,37.22,37.22,0,0,0-10.46-20l-.5-.5h-.07a37.24,37.24,0,0,0-19.46-10,.63.63,0,0,1,0-1.19,26.4,26.4,0,0,0,4.47-1.14,37.71,37.71,0,0,0,26-29.41.62.62,0,0,1,.6-.44.64.64,0,0,1,.6.44,37.3,37.3,0,0,0,10.51,20,36.87,36.87,0,0,0,20,10.52.63.63,0,0,1,0,1.19Z"/></svg>

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.7 KiB

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.0 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 KiB

@@ -81,12 +81,12 @@
"id": "bda21833-11b8-49d3-8c79-ff8e1c667dcc",
"savedPayload": {
"position": {
"x": 31000.07168984726,
"y": 23974.264068711924
"x": 30936.297332942388,
"y": 24042.62315256192
},
"targetPosition": {
"x": 30787.939655491296,
"y": 23762.13203435596
"x": 30747.501215627435,
"y": 23809.47936412483
},
"floatingObject": {
"isPinned": false,
@@ -114,7 +114,7 @@
"floatingObject": {
"isPinned": false,
"isFrozen": false,
"isCollapsed": true,
"isCollapsed": false,
"angleToParent": 3,
"distanceToParent": 3,
"arrangementStyle": 0
@@ -13714,7 +13714,7 @@
"floatingObject": {
"isPinned": false,
"isFrozen": false,
"isCollapsed": true,
"isCollapsed": false,
"angleToParent": 2,
"distanceToParent": 3,
"arrangementStyle": 0
@@ -0,0 +1,643 @@
{
"type": "User Profile",
"name": "Raplh2502",
"config": "{\n \"signature\": {\n \"message\": \"Raplh2502\",\n \"messageHash\": \"0x002241abadf010338ef270d56138327b257a020ba556107b978f7e4a4f78e10f\",\n \"v\": \"0x1b\",\n \"r\": \"0xa45e37a0d602e45300e319a1ee714dcee22fdd44774b638a6086fe66fd5d25b1\",\n \"s\": \"0x36c0c11cbc382a559ae30d7d53940586b60b5a28e39a3240af33a58129accc25\",\n \"signature\": \"0xa45e37a0d602e45300e319a1ee714dcee22fdd44774b638a6086fe66fd5d25b136c0c11cbc382a559ae30d7d53940586b60b5a28e39a3240af33a58129accc251b\"\n },\n \"codeName\": \"Raplh2502\"\n}",
"project": "Governance",
"tokenPowerSwitch": {
"type": "Token Power Switch",
"name": "New Token Power Switch",
"config": "{}",
"project": "Governance",
"tokenPowerSwitch": [],
"communityBuildingPrograms": {
"type": "Community Building Programs",
"name": "New Community Building Programs",
"config": "{}",
"project": "Governance",
"supportProgram": {
"type": "Support Program",
"name": "New Support Program",
"config": "{}",
"project": "Governance",
"userSupporters": [
{
"type": "User Supporter",
"name": "Julian-Molina",
"config": "{}",
"project": "Governance",
"id": "8654e417-6ac3-44da-89de-b348fba4aed4",
"savedPayload": {
"position": {
"x": 35941.4493554574,
"y": 23003.65789426258
},
"targetPosition": {
"x": 36241.4493554574,
"y": 23003.65789426258
},
"floatingObject": {
"isPinned": false,
"isFrozen": false,
"isCollapsed": false,
"angleToParent": 1,
"distanceToParent": 3,
"arrangementStyle": 0
},
"uiObject": {
"isRunning": false
},
"referenceParent": {
"type": "User Profile",
"name": "Julian-Molina",
"id": "1fd95e41-4b22-48d5-be3a-904c37afcb11"
},
"referenceParentCombinedNodePath": [
[
"Julian-Molina",
"User Profile"
]
]
}
},
{
"type": "User Supporter",
"name": "harrellbm",
"config": "{}",
"project": "Governance",
"id": "47ec59fb-4a1e-4e8d-a2df-5796ad4f8a7d",
"savedPayload": {
"position": {
"x": 36091.4493554574,
"y": 22743.850273127246
},
"targetPosition": {
"x": 36241.4493554574,
"y": 23003.65789426258
},
"floatingObject": {
"isPinned": false,
"isFrozen": false,
"isCollapsed": false,
"angleToParent": 1,
"distanceToParent": 3,
"arrangementStyle": 0
},
"uiObject": {
"isRunning": false
},
"referenceParent": {
"type": "User Profile",
"name": "harrellbm",
"id": "107d3e44-1685-4540-8316-3bc6591298e9"
},
"referenceParentCombinedNodePath": [
[
"harrellbm",
"User Profile"
]
]
}
},
{
"type": "User Supporter",
"name": "Luis-Fernando-Molina",
"config": "{}",
"project": "Governance",
"id": "9d61971c-d916-454a-97a0-9d7a68747175",
"savedPayload": {
"position": {
"x": 36391.4493554574,
"y": 22743.850273127246
},
"targetPosition": {
"x": 36241.4493554574,
"y": 23003.65789426258
},
"floatingObject": {
"isPinned": false,
"isFrozen": false,
"isCollapsed": false,
"angleToParent": 1,
"distanceToParent": 3,
"arrangementStyle": 0
},
"uiObject": {
"isRunning": false
},
"referenceParent": {
"type": "User Profile",
"name": "Luis-Fernando-Molina",
"id": "148911b3-b2f3-44fb-8cb4-017991789132"
},
"referenceParentCombinedNodePath": [
[
"Luis-Fernando-Molina",
"User Profile"
]
]
}
}
],
"tokensAwarded": {
"type": "Tokens Awarded",
"name": "New Tokens Awarded",
"project": "Governance",
"id": "8ac68836-fdb6-4549-83bb-4d9d253cec6e",
"savedPayload": {
"position": {
"x": 36541.4493554574,
"y": 23003.65789426258
},
"targetPosition": {
"x": 36241.4493554574,
"y": 23003.65789426258
},
"floatingObject": {
"isPinned": false,
"isFrozen": false,
"isCollapsed": false,
"angleToParent": 1,
"distanceToParent": 3,
"arrangementStyle": 0
},
"uiObject": {
"isRunning": false
}
}
},
"tokensBonus": {
"type": "Tokens Bonus",
"name": "New Tokens Bonus",
"project": "Governance",
"id": "de5c95e6-ff42-41a9-a253-7cd0115234ac",
"savedPayload": {
"position": {
"x": 36391.4493554574,
"y": 23263.46551539791
},
"targetPosition": {
"x": 36241.4493554574,
"y": 23003.65789426258
},
"floatingObject": {
"isPinned": false,
"isFrozen": false,
"isCollapsed": false,
"angleToParent": 1,
"distanceToParent": 3,
"arrangementStyle": 0
},
"uiObject": {
"isRunning": false
}
}
},
"id": "6b249e23-8de3-48e9-9f11-14f8c57dd8d5",
"savedPayload": {
"position": {
"x": 36241.4493554574,
"y": 23003.65789426258
},
"targetPosition": {
"x": 36091.4493554574,
"y": 23263.46551539791
},
"floatingObject": {
"isPinned": false,
"isFrozen": false,
"isCollapsed": true,
"angleToParent": 1,
"distanceToParent": 3,
"arrangementStyle": 0
},
"uiObject": {
"isRunning": false
}
}
},
"id": "61dfffc2-6a9a-450a-abe6-78c19ef34f73",
"savedPayload": {
"position": {
"x": 36091.4493554574,
"y": 23263.46551539791
},
"targetPosition": {
"x": 35941.4493554574,
"y": 23523.273136533244
},
"floatingObject": {
"isPinned": false,
"isFrozen": false,
"isCollapsed": false,
"angleToParent": 1,
"distanceToParent": 3,
"arrangementStyle": 0
},
"uiObject": {
"isRunning": false
}
}
},
"votesAndClaimsPrograms": {
"type": "Votes And Claims Programs",
"name": "New Votes And Claims Programs",
"config": "{}",
"project": "Governance",
"claimsProgram": {
"type": "Claims Program",
"name": "New Claims Program",
"config": "{}",
"project": "Governance",
"assetClaimsFolders": [],
"featureClaimsFolders": [
{
"type": "Feature Claims Folder",
"name": "New Feature Claims Folder",
"config": "{}",
"project": "Governance",
"featureClaimsFolders": [],
"featureContributionClaims": [
{
"type": "Feature Contribution Claim",
"name": "",
"config": "{}",
"project": "Governance",
"id": "2dd37827-1cc1-40aa-b89a-917ccfe24d9b",
"savedPayload": {
"position": {
"x": 36501.25697659273,
"y": 23592.88837880391
},
"targetPosition": {
"x": 36501.25697659273,
"y": 23892.88837880391
},
"floatingObject": {
"isPinned": false,
"isFrozen": false,
"isCollapsed": false,
"angleToParent": 1,
"distanceToParent": 3,
"arrangementStyle": 0
},
"uiObject": {
"isRunning": false
},
"referenceParent": {
"type": "Feature",
"name": "Data Mining",
"id": "afa546fa-799b-4c50-baa3-65c8409aaab9"
},
"referenceParentCombinedNodePath": [
[
"Superalgos",
"Features"
],
[
"Project Features",
"Feature Class"
],
[
"Superalgos Platform",
"Feature Class"
],
[
"Existing Features",
"Feature Class"
],
[
"Data Mining",
"Feature"
]
]
}
},
{
"type": "Feature Contribution Claim",
"name": "User Defined Variables and Code New Features",
"config": "{}",
"project": "Governance",
"id": "67e36fdc-9a1d-42c7-a62a-e99c605c5d33",
"savedPayload": {
"position": {
"x": 36761.064597728066,
"y": 24042.88837880391
},
"targetPosition": {
"x": 36501.25697659273,
"y": 23892.88837880391
},
"floatingObject": {
"isPinned": false,
"isFrozen": false,
"isCollapsed": false,
"angleToParent": 1,
"distanceToParent": 3,
"arrangementStyle": 0
},
"uiObject": {
"isRunning": false
},
"referenceParent": {
"type": "Feature",
"name": "User Defined Variables and Code",
"id": "55e7b969-383c-4ae8-95c0-2dc18473404f"
},
"referenceParentCombinedNodePath": [
[
"Superalgos",
"Features"
],
[
"Project Features",
"Feature Class"
],
[
"Superalgos Platform",
"Feature Class"
],
[
"New Features",
"Feature Class"
],
[
"User Defined Variables and Code",
"Feature"
]
]
}
}
],
"id": "a0af8811-9dc2-4a9a-a28f-358738a6994f",
"savedPayload": {
"position": {
"x": 36501.25697659273,
"y": 23892.88837880391
},
"targetPosition": {
"x": 36241.4493554574,
"y": 24042.88837880391
},
"floatingObject": {
"isPinned": false,
"isFrozen": false,
"isCollapsed": false,
"angleToParent": 1,
"distanceToParent": 3,
"arrangementStyle": 0
},
"uiObject": {
"isRunning": false
}
}
}
],
"positionClaimsFolders": [
{
"type": "Position Claims Folder",
"name": "New Position Claims Folder",
"config": "{}",
"project": "Governance",
"positionClaimsFolders": [],
"positionContributionClaims": [
{
"type": "Position Contribution Claim",
"name": "Moderator Community Management",
"config": "{}",
"project": "Governance",
"id": "74cbbf75-2a2c-4081-9415-ba423848f2db",
"savedPayload": {
"position": {
"x": 36541.4493554574,
"y": 24562.503621074575
},
"targetPosition": {
"x": 36391.4493554574,
"y": 24302.695999939242
},
"floatingObject": {
"isPinned": false,
"isFrozen": false,
"isCollapsed": false,
"angleToParent": 1,
"distanceToParent": 3,
"arrangementStyle": 0
},
"uiObject": {
"isRunning": false
},
"referenceParent": {
"type": "Position",
"name": "Moderator",
"id": "6e141ab6-cc79-44b6-a58a-60b6480f706e"
},
"referenceParentCombinedNodePath": [
[
"Superalgos",
"Positions"
],
[
"Rewarded Positions",
"Position Class"
],
[
"Community Management",
"Position Class"
],
[
"Moderator",
"Position"
]
]
}
}
],
"id": "e880f8a6-8e53-43dd-8b11-4210ca18c9ab",
"savedPayload": {
"position": {
"x": 36391.4493554574,
"y": 24302.695999939242
},
"targetPosition": {
"x": 36241.4493554574,
"y": 24042.88837880391
},
"floatingObject": {
"isPinned": false,
"isFrozen": false,
"isCollapsed": false,
"angleToParent": 1,
"distanceToParent": 3,
"arrangementStyle": 0
},
"uiObject": {
"isRunning": false
},
"referenceParent": {
"type": "Position Class",
"name": "Community Management",
"id": "35b790f0-c567-498c-83fd-ac0aacd8720b"
},
"referenceParentCombinedNodePath": [
[
"Superalgos",
"Positions"
],
[
"Rewarded Positions",
"Position Class"
],
[
"Community Management",
"Position Class"
]
]
}
}
],
"tokensAwarded": {
"type": "Tokens Awarded",
"name": "New Tokens Awarded",
"project": "Governance",
"id": "f7c63ab2-c803-4886-b043-6c6297981f85",
"savedPayload": {
"position": {
"x": 35981.64173432207,
"y": 24192.88837880391
},
"targetPosition": {
"x": 36241.4493554574,
"y": 24042.88837880391
},
"floatingObject": {
"isPinned": false,
"isFrozen": false,
"isCollapsed": false,
"angleToParent": 1,
"distanceToParent": 3,
"arrangementStyle": 0
},
"uiObject": {
"isRunning": false
}
}
},
"id": "bd26adac-cf78-4aec-888f-a8718a657469",
"savedPayload": {
"position": {
"x": 36241.4493554574,
"y": 24042.88837880391
},
"targetPosition": {
"x": 36091.4493554574,
"y": 23783.080757668577
},
"floatingObject": {
"isPinned": false,
"isFrozen": false,
"isCollapsed": false,
"angleToParent": 1,
"distanceToParent": 3,
"arrangementStyle": 0
},
"uiObject": {
"isRunning": false
}
}
},
"id": "22eacf7e-c02b-4853-a529-7789aeadb64e",
"savedPayload": {
"position": {
"x": 36091.4493554574,
"y": 23783.080757668577
},
"targetPosition": {
"x": 35941.4493554574,
"y": 23523.273136533244
},
"floatingObject": {
"isPinned": false,
"isFrozen": false,
"isCollapsed": false,
"angleToParent": 1,
"distanceToParent": 3,
"arrangementStyle": 0
},
"uiObject": {
"isRunning": false
}
}
},
"id": "13f0fedf-2d0a-4b80-a191-05236a8138a6",
"savedPayload": {
"position": {
"x": 35941.4493554574,
"y": 23523.273136533244
},
"targetPosition": {
"x": 35641.4493554574,
"y": 23523.273136533244
},
"floatingObject": {
"isPinned": false,
"isFrozen": false,
"isCollapsed": false,
"angleToParent": 1,
"distanceToParent": 3,
"arrangementStyle": 0
},
"uiObject": {
"isRunning": false
}
}
},
"tokensMined": {
"type": "Tokens Mined",
"name": "New Tokens Mined",
"project": "Governance",
"id": "b4c844ba-f1c1-44e8-96f6-96fbd6a8cbfd",
"savedPayload": {
"position": {
"x": 35341.4493554574,
"y": 23523.273136533244
},
"targetPosition": {
"x": 35641.4493554574,
"y": 23523.273136533244
},
"floatingObject": {
"isPinned": false,
"isFrozen": false,
"isCollapsed": false,
"angleToParent": 1,
"distanceToParent": 3,
"arrangementStyle": 0
},
"uiObject": {
"isRunning": false
}
}
},
"id": "59a87d1a-0457-4a49-a11b-216716e5c889",
"savedPayload": {
"position": {
"x": 35641.4493554574,
"y": 23523.273136533244
},
"targetPosition": {
"x": 35641.4493554574,
"y": 23523.273136533244
},
"floatingObject": {
"isPinned": true,
"isFrozen": false,
"isCollapsed": false,
"angleToParent": 1,
"distanceToParent": 3,
"arrangementStyle": 0
},
"uiObject": {
"isRunning": false
}
}
}
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
@@ -29,13 +29,6 @@
"relatedUiObject": "Positions",
"actionFunction": "payload.executeAction"
},
{
"action": "Add UI Object",
"actionProject": "Foundations",
"label": "Add User Profile",
"relatedUiObject": "User Profile",
"actionFunction": "payload.executeAction"
},
{
"action": "Add UI Object",
"actionProject": "Foundations",
@@ -34,7 +34,7 @@
"config": true
},
"initialValues": {
"config": "{ \"githubUsername\": \"\"}"
"config": "{ \"githubUsername\": \"\", \"mnemonic\": \"\"}"
},
"addLeftIcons": true,
"level": 2,
@@ -40,6 +40,14 @@
"text": "Search the exchange's website for information describing the listing process and requirements. Study the information carefully.",
"updated": 1618562031958
},
{
"style": "List",
"text": "If not enough information is available publicly, feel free to contact their support or listing teams for more information."
},
{
"style": "List",
"text": "You may also want to start filling a dummy listing requirement form to find out what kind of information the exchange requires."
},
{
"style": "List",
"text": "Report on your findings using the Research Requirements bullet list below.",
@@ -66,7 +74,8 @@
},
{
"style": "Text",
"text": "If you're not familiar with the process of editing pages in the Docs and contributing the pages you create or edit, please go to the Getting Started Plugin Workspace and do the Beyond the Basics Tutorial."
"text": "If you're not familiar with the process of editing pages in the Docs and contributing the pages you create or edit, please go to the Getting Started Tutorials Plugin Workspace and do the Beyond the Basics Tutorial.",
"updated": 1628509381517
},
{
"style": "Subtitle",
@@ -85,8 +94,8 @@
},
{
"style": "Json",
"text": "docs.add Topic to Superalgos: Exchange Listing Requirements Research->Exchange Name Listing Requirements->N",
"updated": 1618486204475
"text": "docs.add Topic to Governance: Exchange Listing Requirements Research->Exchange Name Listing Requirements->N",
"updated": 1628532030221
},
{
"style": "List",
@@ -139,7 +148,8 @@
},
{
"style": "List",
"text": "Exchange Name->exchangeURL.com"
"text": "Exchange URL->exchangeURL.com",
"updated": 1628513074678
},
{
"style": "List",
@@ -161,6 +171,10 @@
"text": "Listing request form->pageURL.com",
"updated": 1618498025893
},
{
"style": "List",
"text": "Can list BEP 20 BSC tokens: Some exchanges may not be able to list tokens in the Binance Smart Chain. Make sure you find out if listing the token is technically possible."
},
{
"style": "List",
"text": "Listing fee: Exchanges usually charge for listing new coins. Enter the fee and include any comments, exceptions, or details that may be relevant to our particular case.",
@@ -0,0 +1,318 @@
{
"topic": "Exchange Listing Requirements Research",
"pageNumber": "2",
"type": "Probit Global Listing Requirements",
"definition": {
"text": "The exchange is ready to list Superalgos. Listing fee is $25k, plus 1 BTC in project tokens.",
"updated": 1628512539086
},
"paragraphs": [
{
"style": "Title",
"text": "Listing Requirements",
"updated": 1628515442342
},
{
"style": "List",
"text": "Exchange name: Probit Global"
},
{
"style": "List",
"text": "CMC ranking: 27",
"updated": 1628510522467
},
{
"style": "List",
"text": "Exchange home page: ",
"updated": 1628510560296
},
{
"style": "Link",
"text": "Exchange URL->www.probit.com",
"updated": 1628519777927
},
{
"style": "List",
"text": "Listing requirements page: ",
"updated": 1628510662586
},
{
"style": "Link",
"text": "Listing requirements->www.probit.com/en-us/listing",
"updated": 1628510672736
},
{
"style": "List",
"text": "Listing request form page: ",
"updated": 1628510689962
},
{
"style": "Link",
"text": "Listing request form->docs.google.com/forms/d/e/1FAIpQLSf4b5e25olgKVVY623XBe7Pw1LxFZcCVAGaBXLgJ0sw7KwvkA/viewform",
"updated": 1628510737641
},
{
"style": "List",
"text": "Can list BEP 20 tokens in BSC: Yes",
"updated": 1628511590834
},
{
"style": "List",
"text": "Listing fee: Bronze $25k, Silver $50k, Gold $80k -- plus 1 BTC in project tokens.",
"updated": 1628512487779
},
{
"style": "List",
"text": "Security deposit: No",
"updated": 1628510838412
},
{
"style": "List",
"text": "Volume requirements: No",
"updated": 1628510856025
},
{
"style": "List",
"text": "Market-making requirements: Included for 1 month in all plans; not required afterwards.",
"updated": 1628510894252
},
{
"style": "List",
"text": "Exchange users requirement: No",
"updated": 1628510904049
},
{
"style": "List",
"text": "Pairs available for first listing: USDT, BTC, ETH",
"updated": 1628510967084
},
{
"style": "List",
"text": "Waiting period: 2 weeks.",
"updated": 1628510983346
},
{
"style": "List",
"text": "Legal entity: A corporation is usually required.",
"updated": 1628511032973
},
{
"style": "List",
"text": "Legal opinion: Usually required.",
"updated": 1628511045422
},
{
"style": "List",
"text": "Selection process: N/A.",
"updated": 1628511087443
},
{
"style": "List",
"text": "Researcher's comments: I have an open channel with the listing team and have clarified some issues, as explained under the Negotiation title below.",
"updated": 1628512314800
},
{
"style": "Png",
"text": "PNGs/Governance/Docs/token-task-force/probit-listing-plans.png",
"updated": 1628516831766
},
{
"style": "List",
"text": "Researcher's Github.com Username: julian-molina",
"updated": 1628511321864
},
{
"style": "List",
"text": "Researcher's Telegram Username: @julianmolina",
"updated": 1628511330617
},
{
"style": "List",
"text": "Date of last revision: August 9th, 2021.",
"updated": 1628585744548
},
{
"style": "Title",
"text": "Negotiation"
},
{
"style": "Text",
"text": "@julianmolina has confirmed that:",
"updated": 1628512180751
},
{
"style": "List",
"text": "they accept Superalgos for listing;",
"updated": 1628511554947
},
{
"style": "List",
"text": "we can coordinate the listing date to suit PR & Marketing campaigns."
},
{
"style": "List",
"text": "they can wave the legal opinion requirement if it's clear the token is not a security;",
"updated": 1628533059002
},
{
"style": "List",
"text": "they don't need a corporation to sign the listing agreement; a KYCed executive (project leadership) works too;",
"updated": 1628533074328
},
{
"style": "List",
"text": "we can propose new or improved language in the listing agreement for a more balanced protection of both parties;",
"updated": 1628511650104
},
{
"style": "List",
"text": "we can have additional zero-fee accounts for market making at $5k each;",
"updated": 1628533110459
},
{
"style": "List",
"text": "they wouldn't de-list due to low volume for at least 6 months.",
"updated": 1628533123086
},
{
"style": "Title",
"text": "Exchange Evaluation"
},
{
"style": "Subtitle",
"text": "Corporate Information",
"updated": 1628527538347
},
{
"style": "List",
"text": "Country of registration: Seychelles (from TOS page and Listing Agreement).",
"updated": 1628527930627
},
{
"style": "List",
"text": "Address: Suite 9, Ansuya Estate, Revolution Avenue, Victoria Mahe, Seychelles (from Listing Agreement)."
},
{
"style": "List",
"text": "Year of registration: Unknown",
"updated": 1628527959834
},
{
"style": "List",
"text": "Physical offices: Unknown",
"updated": 1628527967290
},
{
"style": "List",
"text": "Licenses: Unknown",
"updated": 1628527972228
},
{
"style": "Subtitle",
"text": "Corporate Transparency on Website"
},
{
"style": "List",
"text": "Footer info: Probit Global Service Limited | Company Registration Number: 207189 | representative: Ronald Chan. In process of registration: Probit Exchange OÜ | Company Registration Number: 14894573 | Representative: Ronald Chan.",
"updated": 1628527446408
},
{
"style": "List",
"text": "About page info: N/A.",
"updated": 1628527467687
},
{
"style": "List",
"text": "Leadership page info: N/A"
},
{
"style": "List",
"text": "Observations: Poor. There is little clarity on the website as of where and how the company operates.",
"updated": 1628530441154
},
{
"style": "Subtitle",
"text": "Website Traction",
"updated": 1628528688773
},
{
"style": "List",
"text": "Alexa site rank: 3500",
"updated": 1628517005331
},
{
"style": "Link",
"text": "Alexa->www.alexa.com/siteinfo/probit.com"
},
{
"style": "List",
"text": "Domain authority: 45",
"updated": 1628517390639
},
{
"style": "Link",
"text": "DA by Moz->moz.com/domain-analysis?site=probit.com",
"updated": 1628538393505
},
{
"style": "List",
"text": "CMC ranking: 27",
"updated": 1628517194530
},
{
"style": "List",
"text": "CoinGecko ranking: 63"
},
{
"style": "List",
"text": "Top 100 Google SERP? [*]: No",
"updated": 1628527176017
},
{
"style": "List",
"text": "Top 200 Google SERP? [*]: No"
},
{
"style": "Text",
"text": "(*) We search Google (UK region, no personalization) for the keyphrase \"crypto exchange\". The domain is either listed within the top X SERP or not.",
"updated": 1628527204901
},
{
"style": "Subtitle",
"text": "Community",
"updated": 1628528888597
},
{
"style": "List",
"text": "Telegram: 29k",
"updated": 1628528925197
},
{
"style": "Link",
"text": "Telegram English Community->t.me/ProbitEnglish"
},
{
"style": "List",
"text": "Observations: A quick glance on August 9th, 2021. Users complain about deposits not clearing and other issues. There is some response from admins announcing an issue with wallet upgrades. Conversation seems reasonable.",
"updated": 1628532175405
},
{
"style": "Subtitle",
"text": "Liquidity & Volume"
},
{
"style": "List",
"text": "1m BTC-USDT Chart"
},
{
"style": "Png",
"text": "PNGs/Governance/Docs/token-task-force/probit-btc-usdt-1m-chart.PNG",
"updated": 1628530005285
},
{
"style": "Link",
"text": "BTC - USDT Chart->www.probit.com/app/exchange/BTC-USDT",
"updated": 1628530307201
}
]
}
@@ -0,0 +1,261 @@
{
"topic": "Exchange Listing Requirements Research",
"pageNumber": "3",
"type": "LBank Listing Requirements",
"definition": {
"text": "Pending listing requirements and revision.",
"updated": 1628538709584
},
"paragraphs": [
{
"style": "Title",
"text": "Listing Requirements",
"updated": 1628515469804
},
{
"style": "List",
"text": "Exchange name: LBank",
"updated": 1628512837877
},
{
"style": "List",
"text": "CMC ranking: The position of the exchange in the ranking as of the day of the research."
},
{
"style": "List",
"text": "Exchange home page: Edit the string below with the name of the exchange and the URL (without the protocol). ",
"updated": 1628512823690
},
{
"style": "Link",
"text": "Exchange URL->exchangeURL.com",
"updated": 1628512847099
},
{
"style": "List",
"text": "Listing requirements page: Edit the string below with the URL (without the protocol) of the page where you got the core of the information in this report. ",
"updated": 1628512803920
},
{
"style": "Link",
"text": "Listing requirements->pageURL.com"
},
{
"style": "List",
"text": "Listing request form page: Edit the string below with the URL (without the protocol) of the page (or online form) the project should use to request the listing of the token. ",
"updated": 1628512810149
},
{
"style": "Link",
"text": "Listing request form->pageURL.com"
},
{
"style": "List",
"text": "Can list BEP 20 BSC tokens: Some exchanges may not be able to list tokens in the Binance Smart Chain. Make sure you find out if listing the token is technically possible."
},
{
"style": "List",
"text": "Listing fee: Exchanges usually charge for listing new coins. Enter the fee and include any comments, exceptions, or details that may be relevant to our particular case."
},
{
"style": "List",
"text": "Security deposit: Some exchanges require a security deposit to be returned to the project after the listing is completed. Describe the details of such requirements if they exist."
},
{
"style": "List",
"text": "Volume requirements: Exchanges are in the business of charging transaction fees, so they usually expect to list tokens that have traction, in the understanding that such tokens will generate a certain level of trading volume. Some exchanges have a specific requirement about the expected volume. If the exchange requires a precise volume, enter it in numbers. Otherwise, describe what is expected in words."
},
{
"style": "List",
"text": "Market-making requirements: Many times exchanges expect projects to guarantee liquidity for the token on both sides of the order book. They sometimes require projects to hire specific market-makers or ask the project how they will deal with the issue. Explain what the exchange expects in this regard."
},
{
"style": "List",
"text": "Exchange users requirement: Some exchanges require the project to drive a certain number of users to the exchange. Describe any requirements that may exist in this regard."
},
{
"style": "List",
"text": "Pairs available for first listing: Some exchanges limit the number of pairs or even the precise quote assets that may be used for first listings. Describe whatever the exchange requires or offers in this regard."
},
{
"style": "List",
"text": "Waiting period: Most exchanges have a queue of listing requests and they may be able to estimate how long it takes to process. If there is information in this regard, include it here. The exchange may not announce what the estimate is, but you may deduce it or find out in community chats, forums, etc. Do your research."
},
{
"style": "List",
"text": "Legal entity: Some exchanges may require the project to be represented by a legal entity (a foundation, corporation, etc). If that is the case, enter \"Yes\", and describe what the requirement is. If there is no such requirement, enter \"No\"."
},
{
"style": "List",
"text": "Legal opinion: Some exchanges may require the project to get legal advice in the form of a \"legal opinion letter\" or some instrument of the sort, most times about the nature of the token, in particular, to determine if the token is a security or not. If that is the case, enter \"Yes\", and describe what the requirement is. If there is no such requirement, enter \"No\"."
},
{
"style": "List",
"text": "Selection process: Some exchanges describe the nature of the decision-making process to select which tokens shall be listed. Some involve the participation of their community, a voting process, or things of the sort. Describe what the process is like if there is any information about it."
},
{
"style": "List",
"text": "Researcher's comments: If you notice any information of importance to the listing process that is not covered in this list, include it here."
},
{
"style": "List",
"text": "Researcher's Github.com Username: This helps track the author of each research contribution."
},
{
"style": "List",
"text": "Researcher's Telegram Username: Same as above."
},
{
"style": "List",
"text": "Date of last revision: This report may be updated every once in a while. Use this field to note the last time the report was updated."
},
{
"style": "Title",
"text": "Negotiation"
},
{
"style": "List",
"text": "List what progress has been made in the ongoing negotiation."
},
{
"style": "Title",
"text": "Exchange Evaluation"
},
{
"style": "Subtitle",
"text": "Corporate Information"
},
{
"style": "List",
"text": "Country of registration: ",
"updated": 1628536538914
},
{
"style": "List",
"text": "Address: ",
"updated": 1628536544461
},
{
"style": "List",
"text": "Year of registration: ",
"updated": 1628536550106
},
{
"style": "List",
"text": "Physical offices: ",
"updated": 1628536554948
},
{
"style": "List",
"text": "Licenses: ",
"updated": 1628536559754
},
{
"style": "Subtitle",
"text": "Corporate Transparency on Website"
},
{
"style": "List",
"text": "Footer info: None.",
"updated": 1628539579292
},
{
"style": "List",
"text": "About page info: None.",
"updated": 1628539586129
},
{
"style": "List",
"text": "Leadership page info: None.",
"updated": 1628539591833
},
{
"style": "List",
"text": "Observations: Obscure. Zero info provided as of where or how the company operates.",
"updated": 1628539616801
},
{
"style": "Subtitle",
"text": "Website Traction"
},
{
"style": "List",
"text": "Alexa site rank: 1100",
"updated": 1628537403629
},
{
"style": "Link",
"text": "Alexa->www.alexa.com/siteinfo/lbank.info",
"updated": 1628537437035
},
{
"style": "List",
"text": "Domain authority: 44",
"updated": 1628538376155
},
{
"style": "Link",
"text": "DA by Moz->moz.com/domain-analysis?site=lbank.info",
"updated": 1628538362561
},
{
"style": "List",
"text": "CMC ranking: 52",
"updated": 1628536646750
},
{
"style": "List",
"text": "CoinGecko ranking: 32",
"updated": 1628536624836
},
{
"style": "List",
"text": "Top 100 Google SERP? [*]: No"
},
{
"style": "List",
"text": "Top 200 Google SERP? [*]: No"
},
{
"style": "List",
"text": "(*) We search Google (UK region, no personalization) for the keyphrase \"crypto exchange\". The domain is either listed within the top X SERP or not."
},
{
"style": "Subtitle",
"text": "Community"
},
{
"style": "List",
"text": "Telegram: 42k",
"updated": 1628538502945
},
{
"style": "Link",
"text": "Telegram English Community->t.me/LBank_en",
"updated": 1628538533078
},
{
"style": "List",
"text": "Observations: A quick look on August 9th, 2021. Light and decent conversation. Some shilling of coins. Some support requests from users redirected to the website's chat by admins. Many admins present. ",
"updated": 1628539131853
},
{
"style": "Subtitle",
"text": "Liquidity & Volume"
},
{
"style": "List",
"text": "1m BTC-USDT Chart"
},
{
"style": "Png",
"text": "PNGs/Governance/Docs/token-task-force/lbank-btc-usdt-1m-chart.PNG",
"updated": 1628538612321
},
{
"style": "Link",
"text": "BTC - USDT Chart->www.lbank.info/exchange/btc/usdt",
"updated": 1628538654684
}
]
}
@@ -0,0 +1,118 @@
{
"topic": "Exchange Listing Requirements Research",
"pageNumber": "4",
"type": "Bittrex Global Listing Requirements",
"definition": {
"text": "Write a summary for this topic page."
},
"paragraphs": [
{
"style": "Title",
"text": "Listing Requirements",
"updated": 1628515483720
},
{
"style": "List",
"text": "Exchange name: Use the official, most common name of the exchange; the same you used in the name of the page."
},
{
"style": "List",
"text": "CMC ranking: The position of the exchange in the ranking as of the day of the research."
},
{
"style": "List",
"text": "Exchange home page: Edit the string below with the name of the exchange and the URL (without the protocol). Test the link and make sure it works.",
"updated": 1628513218161
},
{
"style": "Link",
"text": "Exchange URL->exchangeURL.com"
},
{
"style": "List",
"text": "Listing requirements page: Edit the string below with the URL (without the protocol) of the page where you got the core of the information in this report. Test the link and make sure it works.",
"updated": 1628513207300
},
{
"style": "Link",
"text": "Listing requirements->pageURL.com"
},
{
"style": "List",
"text": "Listing request form page: Edit the string below with the URL (without the protocol) of the page (or online form) the project should use to request the listing of the token. Test the link and make sure it works.",
"updated": 1628513198400
},
{
"style": "Link",
"text": "Listing request form->pageURL.com"
},
{
"style": "List",
"text": "Can list BEP 20 BSC tokens: Some exchanges may not be able to list tokens in the Binance Smart Chain. Make sure you find out if listing the token is technically possible."
},
{
"style": "List",
"text": "Listing fee: Exchanges usually charge for listing new coins. Enter the fee and include any comments, exceptions, or details that may be relevant to our particular case."
},
{
"style": "List",
"text": "Security deposit: Some exchanges require a security deposit to be returned to the project after the listing is completed. Describe the details of such requirements if they exist."
},
{
"style": "List",
"text": "Volume requirements: Exchanges are in the business of charging transaction fees, so they usually expect to list tokens that have traction, in the understanding that such tokens will generate a certain level of trading volume. Some exchanges have a specific requirement about the expected volume. If the exchange requires a precise volume, enter it in numbers. Otherwise, describe what is expected in words."
},
{
"style": "List",
"text": "Market-making requirements: Many times exchanges expect projects to guarantee liquidity for the token on both sides of the order book. They sometimes require projects to hire specific market-makers or ask the project how they will deal with the issue. Explain what the exchange expects in this regard."
},
{
"style": "List",
"text": "Exchange users requirement: Some exchanges require the project to drive a certain number of users to the exchange. Describe any requirements that may exist in this regard."
},
{
"style": "List",
"text": "Pairs available for first listing: Some exchanges limit the number of pairs or even the precise quote assets that may be used for first listings. Describe whatever the exchange requires or offers in this regard."
},
{
"style": "List",
"text": "Waiting period: Most exchanges have a queue of listing requests and they may be able to estimate how long it takes to process. If there is information in this regard, include it here. The exchange may not announce what the estimate is, but you may deduce it or find out in community chats, forums, etc. Do your research."
},
{
"style": "List",
"text": "Legal entity: Some exchanges may require the project to be represented by a legal entity (a foundation, corporation, etc). If that is the case, enter \"Yes\", and describe what the requirement is. If there is no such requirement, enter \"No\"."
},
{
"style": "List",
"text": "Legal opinion: Some exchanges may require the project to get legal advice in the form of a \"legal opinion letter\" or some instrument of the sort, most times about the nature of the token, in particular, to determine if the token is a security or not. If that is the case, enter \"Yes\", and describe what the requirement is. If there is no such requirement, enter \"No\"."
},
{
"style": "List",
"text": "Selection process: Some exchanges describe the nature of the decision-making process to select which tokens shall be listed. Some involve the participation of their community, a voting process, or things of the sort. Describe what the process is like if there is any information about it."
},
{
"style": "List",
"text": "Researcher's comments: If you notice any information of importance to the listing process that is not covered in this list, include it here."
},
{
"style": "List",
"text": "Researcher's Github.com Username: This helps track the author of each research contribution."
},
{
"style": "List",
"text": "Researcher's Telegram Username: Same as above."
},
{
"style": "List",
"text": "Date of last revision: This report may be updated every once in a while. Use this field to note the last time the report was updated."
},
{
"style": "Title",
"text": "Negotiation"
},
{
"style": "Text",
"text": "List what progress has been made in the ongoing negotiation."
}
]
}
@@ -3,8 +3,8 @@
"pageNumber": "3",
"type": "Referral Program Overview",
"definition": {
"text": "Before they may contribute, people need to find out about Superalgos in the first place! The referral program built-in the Governance Project is designed to incentivize the promotion of the project, either by word-of-mouth or “en masse” through articles, social media posts, videos, and other forms of influencer marketing.",
"updated": 1624698267400
"text": "The referral program built-in the Governance Project is designed to incentivize the promotion of the project, either by word-of-mouth or “en masse” through articles, social media posts, videos, and other forms of influencer marketing.",
"updated": 1628099865032
},
"paragraphs": [
{
@@ -12,26 +12,36 @@ function newGovernanceFunctionLibraryProfileConstructor() {
/*
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')
let githubUsername = UI.projects.foundations.utilities.nodeConfig.loadConfigProperty(node.payload, 'githubUsername')
let mnemonic = UI.projects.foundations.utilities.nodeConfig.loadConfigProperty(node.payload, 'mnemonic')
if (data === undefined || data === "") {
node.payload.referenceParent.payload.uiObject.setErrorMessage("githubUsername config property missing.")
if (githubUsername === undefined || githubUsername === "") {
node.payload.uiObject.setErrorMessage("githubUsername config property missing.")
return
}
createNewAccount()
createWallet()
function createNewAccount() {
let params = {
method: 'createWalletAccount',
entropy: node.id + (new Date()).valueOf()
function createWallet() {
let params
/*
If the user provides a mnemonic then we will get the private key and address from it,
otherwise, we will create a new private key and address.
*/
if (mnemonic === undefined || mnemonic === "") {
params = {
method: 'createWalletAccount',
entropy: node.id + (new Date()).valueOf()
}
} else {
params = {
method: 'mnemonicToPrivateKey',
mnemonic: mnemonic
}
}
let url = 'WEB3' // we don't need to ask this to any specific superalgos node.
let url = 'WEB3' // We will access the default Client WEB3 endpoint.
httpRequest(JSON.stringify(params), url, onResponse)
@@ -39,7 +49,6 @@ function newGovernanceFunctionLibraryProfileConstructor() {
/* 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
}
@@ -47,8 +56,16 @@ function newGovernanceFunctionLibraryProfileConstructor() {
/* 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
if (mnemonic === undefined || mnemonic === "") {
node.payload.uiObject.setErrorMessage('Call to WEB3 Server failed. ' + response.error)
console.log('Call to WEB3 Server failed. ' + response.error)
return
} else {
node.payload.uiObject.setErrorMessage('Call to WEB3 Server failed. Most likely the Mnemonic provided is not correct.' + response.error)
console.log('Call to WEB3 Server failed. Most likely the Mnemonic provided is not correct.' + response.error)
return
}
}
signUserProfileData(response.address, response.privateKey)
@@ -62,7 +79,7 @@ function newGovernanceFunctionLibraryProfileConstructor() {
params: {
method: "signData",
privateKey: privateKey,
data: data
data: githubUsername
}
}
@@ -83,11 +100,34 @@ function newGovernanceFunctionLibraryProfileConstructor() {
console.log('Call to WEB3 Server failed. ' + response.error)
return
}
UI.projects.foundations.utilities.nodeConfig.saveConfigProperty(node.payload.referenceParent.payload, 'signature', response.signature)
let userProfile = UI.projects.foundations.functionLibraries.uiObjectsFromNodes.addUIObject(
node,
'User Profile',
UI.projects.foundations.spaces.designSpace.workspace.workspaceNode.rootNodes
)
/*
We store at the User Profile the Signed githubUsername
*/
UI.projects.foundations.utilities.nodeConfig.saveConfigProperty(userProfile.payload, 'signature', response.signature)
UI.projects.foundations.utilities.nodeConfig.saveConfigProperty(node.payload, 'address', address)
UI.projects.foundations.utilities.nodeConfig.saveConfigProperty(node.payload, 'privateKey', privateKey)
node.payload.uiObject.setInfoMessage("Profile Account has been successfully created.")
/*
We set the name of the User Profile as the githubUsername
*/
userProfile.name = githubUsername
/*
We also Install the User Profile as a Plugin, which in turns saves it.
*/
userProfile.payload.uiObject.menu.internalClick('Install as Plugin')
userProfile.payload.uiObject.menu.internalClick('Install as Plugin')
/*
Show nice message.
*/
if (mnemonic === undefined || mnemonic === "") {
node.payload.uiObject.setInfoMessage("Profile Private Key has been successfully created. User Profile installed as a plugin and saved. Use the Private Key at a crypto wallet and delete this node once done.", 10000)
} else {
node.payload.uiObject.setInfoMessage("Mnemonic successfully imported. User Profile installed as a plugin and saved. Your external wallet was sucessfully linked to your profile.", 10000)
}
}
}
}
@@ -41,6 +41,10 @@ function newGovernanceReportsCommmandInterface() {
message = 'Automated PR merging process.'
}
/* Closing the Governance Tab */
UI.projects.governance.spaces.reportsSpace.sidePanelTab.close()
UI.projects.education.spaces.docsSpace.sidePanelTab.open()
/* Find the Username and Password */
let apisNode = UI.projects.foundations.spaces.designSpace.workspace.getHierarchyHeadByNodeType('APIs')
if (apisNode === undefined) {
@@ -62,36 +66,53 @@ function newGovernanceReportsCommmandInterface() {
return
}
UI.projects.education.spaces.docsSpace.navigateTo('Governance', 'Topic', 'Gov Message - Processing Pull Requests')
message = message.replaceAll('#', '_HASHTAG_')
message = message.replaceAll('/', '_SLASH_')
httpRequest(
undefined,
'Gov/PRs/' +
message + '/' +
config.username + '/' +
config.token + '/' +
UI.projects.education.spaces.docsSpace.currentBranch + '/' +
UI.projects.education.spaces.docsSpace.contributionsBranch
, onResponse)
/* Lets execute this command against the Client */
UI.projects.education.spaces.docsSpace.navigateTo('Governance', 'Topic', 'Gov Message - Processing Pull Requests')
let params = {
method: 'mergePullRequests',
commitMessage: message,
username: config.username,
token: config.token
}
return
let url = 'GOV' // We will access the default Client GOV endpoint.
httpRequest(JSON.stringify(params), url, onResponse)
function onResponse(err, data) {
/* Lets check the result of the call through the http interface */
data = JSON.parse(data)
if (err.result === GLOBAL.DEFAULT_OK_RESPONSE.result && data.result === GLOBAL.DEFAULT_OK_RESPONSE.result) {
if (err.result !== GLOBAL.DEFAULT_OK_RESPONSE.result) {
console.log('[ERROR] Call via HTTP Interface failed.' + err.stack)
console.log('[ERROR] Params = ' + JSON.stringify(params))
return
}
let response = JSON.parse(data)
/* Lets check the result of the method call */
if (response.result !== GLOBAL.DEFAULT_OK_RESPONSE.result) {
console.log('[ERROR] Call to Client Github Server failed.' + err.stack)
console.log('[ERROR] Params = ' + JSON.stringify(params))
}
/* Successful Call */
if (response.result === GLOBAL.DEFAULT_OK_RESPONSE.result) {
UI.projects.education.spaces.docsSpace.navigateTo('Governance', 'Topic', 'Gov Message - Pull Requests Processed')
} else {
UI.projects.education.spaces.docsSpace.navigateTo(
data.docs.project,
data.docs.category,
data.docs.type,
data.docs.anchor,
response.docs.project,
response.docs.category,
response.docs.type,
response.docs.anchor,
undefined,
data.docs.placeholder
response.docs.placeholder
)
}
}
@@ -231,15 +231,6 @@ function newGobernanceReportsSpace() {
'width: ' + thisObject.container.frame.width + 'px;' +
'height: ' + thisObject.container.frame.height + 'px'
}
/*
Monitor the Docs Space to see if its tab is opened, to close this space tab.
*/
if (UI.projects.education.spaces.docsSpace.sidePanelTab.isOpen === true &&
thisObject.sidePanelTab.isOpen === true
) {
thisObject.sidePanelTab.close()
}
}
function getContainer(point, purpose) {
@@ -37,7 +37,7 @@ function newGovernanceUserProfileSpace() {
If the workspace is not related to governance, then we exit the Intialize Function
*/
let resultsArary = UI.projects.foundations.spaces.designSpace.workspace.getHierarchyHeadsByNodeType('User Profile')
if (resultsArary.length === 0 || resultsArary.length === 1) { return }
if (resultsArary.length === 0) { return }
/* Find the Username and Password */
@@ -68,69 +68,135 @@ function newGovernanceUserProfileSpace() {
requestStars()
function requestStars() {
httpRequest(
undefined,
'Gov/getGithubStars/' +
'Superalgos' + '/' +
config.username + '/' +
config.token
, onGithubStarsResponse)
let params = {
method: 'getGithubStars',
repository: 'Superalgos',
username: config.username,
token: config.token
}
let url = 'GOV' // We will access the default Client GOV endpoint.
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) {
console.log('[ERROR] Call via HTTP Interface failed. err.stack = ' + err.stack)
console.log('[ERROR] params = ' + JSON.stringify(params))
return
}
let response = JSON.parse(data)
/* Lets check the result of the method call */
if (response.result !== GLOBAL.DEFAULT_OK_RESPONSE.result) {
console.log('[ERROR] Call to Client Github Server failed. err.stack = ' + err.stack)
console.log('[ERROR] params = ' + JSON.stringify(params))
console.log('[ERROR] response = ' + JSON.stringify(response))
return
}
/* Successful Call */
let githubStarsArray = response.githubListArray
for (let i = 0; i < githubStarsArray.length; i++) {
let githubUsername = githubStarsArray[i]
thisObject.githubStars.set(githubUsername, 1)
}
requestWatchers()
}
}
function requestWatchers() {
httpRequest(
undefined,
'Gov/getGithubWatchers/' +
'Superalgos' + '/' +
config.username + '/' +
config.token
, onGithubWatchersResponse)
let params = {
method: 'getGithubWatchers',
repository: 'Superalgos',
username: config.username,
token: config.token
}
let url = 'GOV' // We will access the default Client GOV endpoint.
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) {
console.log('[ERROR] Call via HTTP Interface failed. err.stack = ' + err.stack)
console.log('[ERROR] params = ' + JSON.stringify(params))
return
}
let response = JSON.parse(data)
/* Lets check the result of the method call */
if (response.result !== GLOBAL.DEFAULT_OK_RESPONSE.result) {
console.log('[ERROR] Call to Client Github Server failed. err.stack = ' + err.stack)
console.log('[ERROR] params = ' + JSON.stringify(params))
console.log('[ERROR] response = ' + JSON.stringify(response))
return
}
/* Successful Call */
let githubWatchersArray = response.githubListArray
for (let i = 0; i < githubWatchersArray.length; i++) {
let githubUsername = githubWatchersArray[i]
thisObject.githubWatchers.set(githubUsername, 1)
}
requestForks()
}
}
function requestForks() {
httpRequest(
undefined,
'Gov/getGithubForks/' +
'Superalgos' + '/' +
config.username + '/' +
config.token
, onGithubForksResponse)
}
function onGithubStarsResponse(err, githubData) {
//console.log(githubData)
/* Lets check the result of the call through the http interface */
let githubStarsArray = JSON.parse(githubData)
for (let i = 0; i < githubStarsArray.length; i++) {
let githubUsername = githubStarsArray[i]
thisObject.githubStars.set(githubUsername, 1)
let params = {
method: 'getGithubForks',
repository: 'Superalgos',
username: config.username,
token: config.token
}
requestWatchers()
}
let url = 'GOV' // We will access the default Client GOV endpoint.
function onGithubWatchersResponse(err, githubData) {
//console.log(githubData)
/* Lets check the result of the call through the http interface */
let githubWatchersArray = JSON.parse(githubData)
httpRequest(JSON.stringify(params), url, onResponse)
for (let i = 0; i < githubWatchersArray.length; i++) {
let githubUsername = githubWatchersArray[i]
thisObject.githubWatchers.set(githubUsername, 1)
}
function onResponse(err, data) {
requestForks()
}
/* Lets check the result of the call through the http interface */
if (err.result !== GLOBAL.DEFAULT_OK_RESPONSE.result) {
console.log('[ERROR] Call via HTTP Interface failed. err.stack = ' + err.stack)
console.log('[ERROR] params = ' + JSON.stringify(params))
return
}
function onGithubForksResponse(err, githubData) {
//console.log(githubData)
/* Lets check the result of the call through the http interface */
let githubForksArray = JSON.parse(githubData)
let response = JSON.parse(data)
for (let i = 0; i < githubForksArray.length; i++) {
let githubUsername = githubForksArray[i]
thisObject.githubForks.set(githubUsername, 1)
/* Lets check the result of the method call */
if (response.result !== GLOBAL.DEFAULT_OK_RESPONSE.result) {
console.log('[ERROR] Call to Client Github Server failed. err.stack = ' + err.stack)
console.log('[ERROR] params = ' + JSON.stringify(params))
console.log('[ERROR] response = ' + JSON.stringify(response))
return
}
/* Successful Call */
let githubForksArray = response.githubListArray
for (let i = 0; i < githubForksArray.length; i++) {
let githubUsername = githubForksArray[i]
thisObject.githubForks.set(githubUsername, 1)
}
}
}
}
+22 -1
View File
@@ -871,7 +871,28 @@
}
]
},
"CL": {},
"CL": {
"utilities": [
{
"name": "Async Functions",
"propertyName": "asyncFunctions",
"functionName": "newFoundationsUtilitiesAsyncFunctions",
"fileName": "AsyncFunctions.js"
},
{
"name": "Web Access",
"propertyName": "webAccess",
"functionName": "newFoundationsUtilitiesWebAccess",
"fileName": "WebAccess.js"
},
{
"name": "Plugins",
"propertyName": "plugins",
"functionName": "newFoundationsUtilitiesPlugins",
"fileName": "Plugins.js"
}
]
},
"plugins": [
"Data Mines",
"Trading Mines",
+73 -51
View File
@@ -15,94 +15,116 @@ Superalgos is not just another open-source project. We are an open and welcoming
Follow the link to [INSTALL.md](https://github.com/Superalgos/Superalgos/blob/develop/INSTALL.md).
# What is Superalgos?
# The Superalgos Project
## Superalgos is a token-incentivized open-source project.
## Superalgos is a token-incentivized open-source project
The project incentivizes users to continuously improve Superalgos. Users contribute to the codebase, to the library of trading intelligence, and to project development in general -- and earn SA tokens for their contributions. Providing free, high-quality, real-time trading-intelligence over the Superalgos peer-to-peer network is a key activity, also incentivized.
The protocol incentivizes users to improve Superalgos. To do that, users contribute to the codebase, add to the library of trading intelligence, and help with project development in general, earning Superalgos (SA) tokens for their contributions. Protocol-incentivized contributions allow the project to distribute free of charge, high-quality trading intelligence over the Superalgos peer-to-peer network, currently in the making.
## Superalgos is an open-network project built on top of the Internet + Bitcoin + Lighting open networks.
## Superalgos is an open-network project built on top of other open networks: Internet + Bitcoin + Lightning
This means that the Superalgos open network target audience is 8 billion people and that payments of products and services offered through the Superalgos Network are done with bitcoin over the Lighting Network.
Because it's an open network, our target audience is, theoretically, 8 billion people. While all products and services provided by the project are free of charge, third-party products and services distributed through the Superalgos Network will be payable in bitcoin over the Lighting Network.
## Superalgos is a free and open trading-intelligence network anyone can plug-in to emit or consume real-time trading signals.
## The Superalgos Network is an open trading intelligence network distributing trading signals
Signals are cryptographically signed by trading intelligence producers and backed by their token holdings and reputation. The Superalgos p2p Network sorts how signals are distributed, prioritizing signals-consumers with the biggest token holdings.
Anyone may plug in to provide or consume trading signals in a permissionless manner. Signals are cryptographically signed by trading intelligence providers with a reputation reflected in their token holdings. The Superalgos p2p Network prioritizes signals distribution according to the token holdings of consumers.
## Superalgos has the ultimate scalable and defensible business model.
## Superalgos has a scalable and defensible business model
* Everything is free and open source for its user base: trading automation tools + mobile apps for consuming real-time trading intelligence.
* The Superalgos algo-traders community members compete with each other to mine SA tokens by producing high quality real-time trading intelligence which is consumed for free by the Superalgos user base to automate their trading.
* Trading Intelligence is distributed via a protocol-incentivized p2p network with no network fees.
* The long term viability is guaranteed since the business model can not be disrupted.
* Everything is open-source and free of charge for all users: trading automation tools to produce trading intelligence, and mobile apps to consume it.
## Superalgos massive adoption is highly likely since all friction has been removed.
* Algo traders compete to mine SA tokens, producing high-quality, ever-evolving trading intelligence paid for by the protocol.
* Users don’t need to pay for the mobile app to consume trading intelligence.
* Users don’t need to pay for high-quality trading intelligence.
* Algo-traders don’t need to pay for the tools to produce trading intelligence.
* Real-time trading Intelligence does not need to be bought or sold. The Superalgos Project buys real-time trading intelligence (via minting new tokens) from the algo-traders community and distributes it for free to all the user base via its p2p network.
* The Superalgos SA token does not need to be spent to be used, avoiding the need to pay blockchain fees. Only it’s balance is relevant to backup the user profile’s reputation and skin on the game, and in the case of real-time trading intelligence consumers give them priority access to signals.
* Trading Intelligence is distributed via a p2p network with zero network fees.
## Holding the SA Token is the open network business model for all participants.
* The long-term viability is guaranteed since the business model is undisruptable.
* Buying pressure will come from users competing to be among the first to receive the trading-signals.
* Buying pressure will also come from algo-traders competing to be at the top of the list of real-time trading intelligence providers.
* Algo trades make money, users make money, the token appreciates, everyone wins.
## With all friction removed, mass adoption is inevitable
## Superalgos will become one of the most powerful consumer brands on the planet because:
* Users do not pay for the mobile app.
* We are building a brand that is inline and incentivized with our users.
* We have the business acumen and profile of a token-incentivized-open source project.
* The project acts as an union, collecting benefits for its user base, like better exchange fees for all.
* We don’t collect or sell user data.
* We don’t sell ads to anyone.
* We don’t collect or sell users trading information that can be used to front run them.
* We know how we are going to make money, and it is on token appreciation.
* Founders and team are just token holders as any other contributors.
* Users do not pay for trading intelligence.
## Superalgos Mobile Apps will have the best user experience possible because:
* Algo traders do not pay for the tools to produce trading intelligence.
* The best user experience means users should be able to consume top quality, free real-time trading intelligence and autonomously execute trades while they are sleeping.
* Users just need to select their exchange, provide an exchange access token, define their base asset, amount of funds to be traded and trading intelligence providers.
* Their mobile phone will become a 24/7 trading bot plugged into the Superalgos Network to receive trading signals and firing trading orders at any exchange.
* Similar to a bitcoin wallet, anyone will be able to develop Superalgos Mobile Apps for consuming trading-signals. This open competition will be a race to the best UX possible.
* Established crypto wallets or any trading app in general, can plugin into the Superalgos Network and consume real-time trading intelligence for free.
* Trading Intelligence does not need to be bought or sold. The project pays for trading intelligence by minting new tokens and distributes intelligence to all users, free of charge.
## Superalgos does not sell it’s tokens to anyone for any reason.
* The Superalgos (SA) token use case does not require spending, avoiding blockchain fees. Only wallet balance is relevant to determining the reputation and skin in the game of intelligence providers. The same is true for consumers. It is their wallet balance that defines the order of distribution of signals.
SA Tokens are distributed after each software release among the contributors of that release. If you want to mine SA tokens you can start contributing yourself or hire / sponsor someone (usually a developer or trader) to do it on your behalf.
## Holding the SA Token is the business model for all participants
Users are free to buy or sell their SA Tokens. No exchange has listed SA Tokens yet. We expect that to happen soon.
* Buying pressure will come from users competing to be among the first to receive trading signals.
* Algo traders build up their holdings by producing top trading intelligence and compete for the reputation entailed in their holdings.
* Project developers earn tokens for their contributions.
## Superalgos will become a powerful consumer brand because it is user-centric:
* We are building a brand 100% in line with consumer interests.
* We cherish the values of a decentralized, token-incentivized, open-source project.
* The project acts as a union, acquiring benefits for users, like better exchange fees for all.
* We do not collect or sell user data.
* We do not sell ads.
* We do not collect or sell user trading information.
* We know how we are going to make money: token appreciation.
* Founders and team are just token holders, like every other contributor.
## Superalgos Mobile Apps will feature a seamless user experience
* The app will consume top-quality trading intelligence and autonomously execute trades.
* Users will select an exchange, provide an exchange access token, define their base asset, initial capital, and trading intelligence providers.
* A mobile phone will become a 24/7 trading bot plugged into the Superalgos Network, receiving trading signals and firing orders on any exchange.
* Similar to a bitcoin wallet, anyone will be able to develop Superalgos Mobile Apps to consume trading signals. This open competition will be a race to the best UX possible.
* Crypto wallets and trading apps will plug into the Superalgos Network and consume real-time trading intelligence for free.
## The Superalgos Project does not sell tokens
The protocol distributes Superalgos (SA) Tokens after each software release, among contributors of that release. If you wish to mine tokens, you may start contributing anytime. You may also hire/sponsor someone (usually a developer or trader) to do it on your behalf.
# Software Architecture
Superalgos features these 3 main components:
Superalgos features three main components:
1. **Superalgos Platform:** An already functional set of tools designed for algo-traders for automating their own trading and producing trading-signals.
1. **Superalgos Platform:** A set of tools designed for algo-traders to design, build, test, and deploy trading bots and produce trading signals. The platform has been trading live since mid-2020 and is virtually ready to go out of beta.
2. **Superalgos Mobile:** An under-development mobile app reference implementation that consumes existing trading intelligence to automate trading.
2. **Superalgos Mobile:** An under-development mobile app reference implementation for cosuming real-time trading intelligence and automate users trading.
3. **Superalgos Network:** An under-development open p2p network for distributing trading intelligence from providers (algo-traders) to consumers (the project's user base).
3. **Superalgos Network:** An under-development open p2p network for distributing real-time trading intelligence between producers (algo-traders) and consumers (the project's user base).
All of the above is / will be hosted in this repository.
All of the above is or will be hosted in this repository.
# Superalgos is User-centric
* No ads, anywhere.
* No sign up / logins.
* No sign-up / logins.
* No user/usage data collection of any kind.
* No user trading information collected or sold.
* Runs 100% on uncompiled code anyone can read and audit.
# Superalgos Development Pipeline
* **Superalgos P2P Network:** will allow algo-traders to share trading signals with Superalgos users consuming these signals via a mobile app.
* **Real-time Trading Signals:** will allow users to emit trading signals and be rewarded with SA Tokens.
* **Superalgos P2P Network:** Will allow the project to distribute trading intelligence produced by algo-traders.
* **Trading Signals:** Will allow providers to broadcast trading signals and be rewarded with SA Tokens.
* **Superalgos Mobile:** Will allow users to consume trading signals for free and autonomously execute trades from their mobile phones.
* **Ethereum Integration:** Will allow mining data from an Ethereum network node, and bring it into the Superalgos workflow.
# What is the Superalgos Platform?
+2
View File
@@ -31,6 +31,8 @@ exports.newMultiProject = function () {
projectInstance.processModules = {}
projectInstance.botModules = {}
if (projectDefinition.TS === undefined) { continue }
/* Set up Utilities of this Project */
if (projectDefinition.TS.utilities !== undefined) {
for (let j = 0; j < projectDefinition.TS.utilities.length; j++) {