├── .editorconfig ├── .eslintignore ├── .eslintrc.js ├── .gitbook.yaml ├── .github └── workflows │ └── npmpublish.yml ├── .gitignore ├── .npmignore ├── .prettierrc ├── .vscode ├── extensions.json ├── settings.json └── tasks.json ├── CHANGELOG.md ├── LICENSE ├── README.md ├── assets └── everdev.png ├── ci ├── Jenkinsfile ├── gitbook │ └── Jenkinsfile └── jenkins │ └── Jenkinsfile ├── cli.js ├── contracts ├── GiverV1.abi.json ├── GiverV2.abi.json ├── GiverV2.tvc ├── GiverV3.abi.json ├── GiverV3.sol ├── GiverV3.tvc ├── HelloWallet.abi.json ├── HelloWallet.sol ├── HelloWallet.tvc ├── MsigV2.abi.json ├── MsigV2.sol ├── MsigV2.tvc ├── SafeMultisigWallet.abi.json ├── SafeMultisigWallet.keys.json ├── SafeMultisigWallet.tvc ├── SetcodeMultisigWallet.abi.json └── seGiver.keys.json ├── docs ├── .gitbook │ └── assets │ │ ├── Everscale Logo.png │ │ ├── Everscale Logo.svg │ │ └── vf-dev-program.png ├── SUMMARY.md ├── command-line-interface │ ├── c.md │ ├── contract-management.md │ ├── debrowser.md │ ├── evernode-platform-startup-edition-se.md │ ├── network-tool.md │ ├── sdk.md │ ├── signer-tool.md │ ├── sold.md │ ├── solidity-compiler-driver.md │ ├── solidity.md │ └── testsuite4.md ├── guides │ ├── creating-controller.md │ └── quick-start.md ├── troubleshooting.md ├── use-in-js-applications.md └── view-controller-info.md ├── jest.config.js ├── package.json ├── src ├── __tests__ │ ├── addProjectId.ts │ ├── checkArgs.ts │ ├── checkNewVersion.ts │ ├── contracts.ts │ ├── data │ │ ├── contracts-input-alone.json │ │ └── contracts-input.json │ ├── init.ts │ ├── network.ts │ ├── parser.ts │ ├── se.ts │ ├── signer.ts │ ├── sol.ts │ └── wrap.ts ├── cli │ └── index.ts ├── controllers │ ├── clang │ │ ├── components.ts │ │ ├── index.ts │ │ └── snippets.ts │ ├── contract │ │ ├── accounts.ts │ │ ├── index.ts │ │ ├── param-parser.ts │ │ └── run.ts │ ├── debrowser │ │ ├── command │ │ │ ├── interfaces.ts │ │ │ ├── start.ts │ │ │ ├── stop.ts │ │ │ └── version.ts │ │ ├── index.ts │ │ └── installer.ts │ ├── ever-cli │ │ ├── components.ts │ │ └── index.ts │ ├── index.ts │ ├── js │ │ ├── index.ts │ │ ├── installer.ts │ │ ├── snippets.ts │ │ └── wrap.ts │ ├── network │ │ ├── giver.ts │ │ ├── index.ts │ │ └── registry.ts │ ├── se │ │ ├── commands.ts │ │ ├── index.ts │ │ └── registry.ts │ ├── signer │ │ ├── index.ts │ │ └── registry.ts │ ├── sold │ │ ├── components.ts │ │ └── index.ts │ ├── solidity │ │ ├── components.ts │ │ ├── index.ts │ │ └── snippets.ts │ ├── ts │ │ ├── create.ts │ │ ├── index.ts │ │ ├── inspect.ts │ │ └── run.ts │ └── ts4 │ │ ├── components.ts │ │ ├── index.ts │ │ └── snippets.ts ├── core │ ├── component.ts │ ├── docker.ts │ ├── index.ts │ ├── known-contracts.ts │ ├── solFileResolvers.ts │ └── utils.ts ├── everdev │ ├── checkNewVersion.ts │ ├── help.ts │ ├── info.ts │ └── update.ts ├── index.ts ├── rewriteKnownErrors.ts └── server │ └── api │ └── tondev.graphql ├── tsconfig.build.json └── tsconfig.json /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 4 6 | end_of_line = lf 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | # /node_modules/* in the project root is ignored by default 2 | # build artefacts 3 | dist/* 4 | coverage/* 5 | # data definition files 6 | **/*.d.ts 7 | # 3rd party libs 8 | /src/public/ 9 | # custom definition files 10 | /src/types/ 11 | 12 | cli.js 13 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | parser: "@typescript-eslint/parser", 4 | parserOptions: { 5 | project: "./tsconfig.json", 6 | tsconfigRootDir: __dirname, 7 | }, 8 | plugins: ["@typescript-eslint", "prettier", "jest"], 9 | extends: [ 10 | "eslint:recommended", 11 | "plugin:@typescript-eslint/recommended", 12 | // "plugin:@typescript-eslint/recommended-requiring-type-checking", 13 | "prettier", 14 | ], 15 | rules: { 16 | "no-empty": "warn", 17 | "prettier/prettier": 2, 18 | "no-implicit-coercion": ["warn", { allow: ["!!"] }], 19 | curly: ["error", "all"], 20 | "@typescript-eslint/no-explicit-any": "off" 21 | }, 22 | env: { 23 | node: true, 24 | "jest/globals": true, 25 | }, 26 | } 27 | -------------------------------------------------------------------------------- /.gitbook.yaml: -------------------------------------------------------------------------------- 1 | 2 | root: ./docs/ 3 | 4 | structure: 5 | readme: ../README.md 6 | summary: ./SUMMARY.md -------------------------------------------------------------------------------- /.github/workflows/npmpublish.yml: -------------------------------------------------------------------------------- 1 | name: npm publish 2 | 3 | on: 4 | release: 5 | types: [published] 6 | 7 | jobs: 8 | build-binaries: 9 | strategy: 10 | matrix: 11 | os: [windows, macos, ubuntu] 12 | include: 13 | - os: windows 14 | build: npx caxa --directory . --command "{{caxa}}/node_modules/.bin/node" "{{caxa}}/cli.js" --output "everdev-windows.exe" 15 | artifact: "everdev-windows.exe" 16 | - os: macos 17 | build: | 18 | npx caxa --directory . --command "{{caxa}}/node_modules/.bin/node" "{{caxa}}/cli.js" --output "everdev" 19 | tar -czf "everdev-macos.tgz" "everdev" 20 | artifact: everdev-macos.tgz 21 | - os: ubuntu 22 | build: | 23 | npx caxa --directory . --command "{{caxa}}/node_modules/.bin/node" "{{caxa}}/cli.js" --output "everdev" 24 | tar -czf "everdev-linux.tgz" "everdev" 25 | artifact: everdev-linux.tgz 26 | runs-on: ${{ matrix.os }}-latest 27 | steps: 28 | - uses: actions/checkout@v2 29 | - uses: actions/setup-node@v1 30 | with: 31 | node-version: 12 32 | registry-url: https://registry.npmjs.org/ 33 | - run: | 34 | npm i 35 | npx tsc 36 | ${{ matrix.build }} 37 | - uses: softprops/action-gh-release@v1 38 | with: 39 | files: ${{ matrix.artifact }} 40 | env: 41 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 42 | 43 | publish-npm: 44 | needs: [build-binaries] 45 | runs-on: ubuntu-latest 46 | steps: 47 | - uses: actions/checkout@v2 48 | - uses: actions/setup-node@v1 49 | with: 50 | node-version: 12 51 | registry-url: https://registry.npmjs.org/ 52 | - run: | 53 | npm publish 54 | env: 55 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} 56 | 57 | send-discord-msg: 58 | needs: [publish-npm] 59 | runs-on: ubuntu-latest 60 | steps: 61 | - name: Discord notification 62 | env: 63 | DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }} 64 | uses: Ilshidur/action-discord@master 65 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | .pnpm-debug.log* 9 | .vscode/* 10 | 11 | # Diagnostic reports (https://nodejs.org/api/report.html) 12 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 13 | 14 | # Runtime data 15 | pids 16 | *.pid 17 | *.seed 18 | *.pid.lock 19 | 20 | # Directory for instrumented libs generated by jscoverage/JSCover 21 | lib-cov 22 | 23 | # Coverage directory used by tools like istanbul 24 | coverage 25 | *.lcov 26 | 27 | # nyc test coverage 28 | .nyc_output 29 | 30 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 31 | .grunt 32 | 33 | # Bower dependency directory (https://bower.io/) 34 | bower_components 35 | 36 | # node-waf configuration 37 | .lock-wscript 38 | 39 | # Compiled binary addons (https://nodejs.org/api/addons.html) 40 | build/Release 41 | 42 | # Dependency directories 43 | node_modules/ 44 | jspm_packages/ 45 | 46 | # Snowpack dependency directory (https://snowpack.dev/) 47 | web_modules/ 48 | 49 | # TypeScript cache 50 | *.tsbuildinfo 51 | 52 | # Optional npm cache directory 53 | .npm 54 | 55 | # Optional eslint cache 56 | .eslintcache 57 | 58 | # Microbundle cache 59 | .rpt2_cache/ 60 | .rts2_cache_cjs/ 61 | .rts2_cache_es/ 62 | .rts2_cache_umd/ 63 | 64 | # Optional REPL history 65 | .node_repl_history 66 | 67 | # Output of 'npm pack' 68 | *.tgz 69 | 70 | # Yarn Integrity file 71 | .yarn-integrity 72 | 73 | # dotenv environment variables file 74 | .env 75 | .env.test 76 | .env.production 77 | 78 | # parcel-bundler cache (https://parceljs.org/) 79 | .cache 80 | .parcel-cache 81 | 82 | # Next.js build output 83 | .next 84 | out 85 | 86 | # Nuxt.js build / generate output 87 | .nuxt 88 | dist 89 | 90 | # Gatsby files 91 | .cache/ 92 | # Comment in the public line in if your project uses Gatsby and not Next.js 93 | # https://nextjs.org/blog/next-9-1#public-directory-support 94 | # public 95 | 96 | # vuepress build output 97 | .vuepress/dist 98 | 99 | # Serverless directories 100 | .serverless/ 101 | 102 | # FuseBox cache 103 | .fusebox/ 104 | 105 | # DynamoDB Local files 106 | .dynamodb/ 107 | 108 | # TernJS port file 109 | .tern-port 110 | 111 | # Stores VSCode versions used for testing VSCode extensions 112 | .vscode-test 113 | 114 | # yarn v2 115 | .yarn/cache 116 | .yarn/unplugged 117 | .yarn/build-state.yml 118 | .yarn/install-state.gz 119 | .pnp.* 120 | .idea 121 | /package-lock.json 122 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | * 2 | !src/**/* 3 | !dist/**/* 4 | dist/**/*.test.* 5 | !contracts/**/* 6 | !package.json 7 | !README.md 8 | **/*.map 9 | src/ 10 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "trailingComma": "all", 3 | "tabWidth": 4, 4 | "semi": false, 5 | "singleQuote": false, 6 | "printWidth": 80, 7 | "arrowParens": "avoid", 8 | "bracketSpacing": true, 9 | "endOfLine": "lf", 10 | "proseWrap": "preserve", 11 | "quoteProps": "as-needed", 12 | "requirePragma": false, 13 | "useTabs": false 14 | } 15 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": ["dbaeumer.vscode-eslint"] 3 | } 4 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.codeActionsOnSave": { 3 | "source.fixAll.eslint": "explicit" 4 | }, 5 | "eslint.enable": true, 6 | "eslint.validate": ["javascript", "typescript"], 7 | "editor.formatOnSave": true, 8 | "[javascript]": { 9 | "editor.formatOnSave": false 10 | }, 11 | "[typescript]": { 12 | "editor.defaultFormatter": "esbenp.prettier-vscode", 13 | "editor.formatOnSave": true, 14 | "editor.formatOnPaste": false 15 | }, 16 | "[markdown]": { 17 | "editor.formatOnSave": true 18 | }, 19 | "search.exclude": { 20 | "**/node_modules": true, 21 | "**/bower_components": true, 22 | "**/dist": true, 23 | "**/coverage": true 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | // See https://go.microsoft.com/fwlink/?LinkId=733558 3 | // for the documentation about the tasks.json format 4 | "version": "2.0.0", 5 | "tasks": [ 6 | { 7 | "type": "npm", 8 | "script": "build", 9 | "group": { 10 | "kind": "build", 11 | "isDefault": true 12 | } 13 | }, 14 | { 15 | "type": "npm", 16 | "script": "start", 17 | "problemMatcher": [], 18 | "label": "npm: start", 19 | "detail": "npm run start" 20 | } 21 | ] 22 | } 23 | -------------------------------------------------------------------------------- /assets/everdev.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/everx-labs/everdev/af680664adeea4fc536fefab298ab4eb1d4503eb/assets/everdev.png -------------------------------------------------------------------------------- /ci/Jenkinsfile: -------------------------------------------------------------------------------- 1 | @Library('infrastructure-jenkins-shared-library') _ 2 | 3 | G_gitcred = 'TonJenSSH' 4 | G_giturl = null 5 | G_info = [:] 6 | 7 | def gitbook(project_name = null) { 8 | sshagent([G_gitcred]) { 9 | def tmpFolder = UUID.randomUUID().toString() 10 | sh """ 11 | git clone -b gitbook git@github.com:everx-labs/${project_name} ${tmpFolder} 12 | """ 13 | dir(tmpFolder) { 14 | try { 15 | sh """ 16 | git merge origin/main 17 | git push --set-upstream origin gitbook 18 | """ 19 | def newPR = gitFunc.createPR(project_name, 'gitbook', 'main', 'Update docs from gitbook') 20 | def num = newPR.number 21 | if (num) { 22 | def sha = gitFunc.infoPR(project_name, num).head.sha 23 | def context = "continuous-integration/jenkins/pr-merge" 24 | gitFunc.setGitHubBuildStatus(project_name, sha, true, 'Updated by Jenkins pipeline', context) 25 | gitFunc.approvePR(project_name, num) 26 | println( gitFunc.mergePR(project_name, num) ) 27 | } 28 | } catch(ex) { 29 | echo ex 30 | } 31 | deleteDir() 32 | } 33 | } 34 | } 35 | 36 | def test_jenkins(project_name = null) { 37 | println("test: Jenkins ${project_name}") 38 | } 39 | 40 | pipeline { 41 | agent { 42 | label 'master' 43 | } 44 | stages{ 45 | stage('Prepare data') { 46 | steps { 47 | script { 48 | def jNameParts = env.JOB_NAME.split('/') 49 | 50 | // get triggered project 51 | G_info.put('project', jNameParts[jNameParts.size()-2]) 52 | 53 | // set url 54 | G_giturl = env.GIT_URL 55 | if(!G_giturl) { 56 | error "It seems like GIT_URL (${env.GIT_URL}) variable wasn't set.\n" + 57 | "Make sure that your branch (or PR) has no merge conflicts, otherwise resolve them and try again." 58 | } 59 | 60 | // get triggered branch 61 | G_info.put('branch', "${env.BRANCH_NAME}") 62 | 63 | // // get project settings 64 | // try { 65 | // G_projects_config = json.json2map(["text": gitFunc.getSingleFile('ton-builder', 'master', 'project_settings.json').toString()]).findAll{proj, data -> 66 | // proj == G_info.project.toLowerCase().trim() 67 | // }.collect{proj, data -> data}[0] 68 | // G_dev_processing = G_projects_config.dev_processing.toString() != 'null' 69 | // } catch(ex) { 70 | // echo "Can't get project data" 71 | // } 72 | 73 | // branch is PR 74 | G_info.put('isPR', G_info.branch ==~ /^PR-\d+/ ? true : false) 75 | 76 | // check reason 77 | if(G_info.branch == "main") { 78 | // def commitText = sh(script: 'git log -1 --pretty=%B', returnStdout: true).trim().split('\n').findAll{rec -> 79 | // rec.trim() != '' && !(rec ==~ /.*Merge.*/) 80 | // } 81 | // if(commitText.size() == 0) { 82 | // commitText = sh(script: 'git log -2 --pretty=%B', returnStdout: true).trim().split('\n').findAll{rec -> 83 | // rec.trim() != '' && !(rec ==~ /.*Merge.*/) 84 | // } 85 | // } 86 | // commitText = commitText.join('\n') 87 | // echo "${commitText}" 88 | // if( 89 | // commitText.indexOf("Update patch version because of dependencies changes") >= 0 || 90 | // commitText.indexOf("Automatic project update") >= 0 91 | // ) { 92 | // G_process_build = false 93 | // } 94 | } else if (G_info.branch == 'gitbook') { 95 | println("resolution: gitbook") 96 | gitbook('everdev') 97 | //currentBuild.result = 'SUCCESS' 98 | //build job: "Builder/gitbook" 99 | //return 100 | } else if (G_info.branch == 'jenkins') { 101 | println("test: Jenkins") 102 | test_jenkins('everdev') 103 | //currentBuild.result = 'SUCCESS' 104 | //build job: "Everdev/ci/jenkins" 105 | //return 106 | } 107 | } 108 | } 109 | } 110 | } 111 | post { 112 | always { 113 | cleanWs() 114 | } 115 | } 116 | } -------------------------------------------------------------------------------- /ci/gitbook/Jenkinsfile: -------------------------------------------------------------------------------- 1 | @Library('infrastructure-jenkins-shared-library') _ 2 | 3 | G_gitcred = 'TonJenSSH' 4 | 5 | pipeline { 6 | agent { 7 | label 'master' 8 | } 9 | stages{ 10 | stage('Publish gitbook branch into main') { 11 | steps { 12 | script { 13 | sshagent([G_gitcred]) { 14 | def tmpFolder = UUID.randomUUID().toString() 15 | sh """ 16 | git clone -b gitbook git@github.com:everx-labs/everdev ${tmpFolder} 17 | """ 18 | dir(tmpFolder) { 19 | try { 20 | sh """ 21 | git merge origin/main 22 | git push --set-upstream origin gitbook 23 | """ 24 | def newPR = gitFunc.createPR('everdev', 'gitbook', 'main', 'Update docs from gitbook') 25 | def num = newPR.number 26 | if (num) { 27 | def sha = gitFunc.infoPR('everdev', num).head.sha 28 | def context = "continuous-integration/jenkins/pr-merge" 29 | gitFunc.setGitHubBuildStatus('everdev', sha, true, 'Updated by Jenkins pipeline', context) 30 | gitFunc.approvePR('everdev', num) 31 | println( gitFunc.mergePR('everdev', num) ) 32 | } 33 | } catch(ex) { 34 | echo ex 35 | } 36 | deleteDir() 37 | } 38 | } 39 | } 40 | } 41 | } 42 | } 43 | post { 44 | always { 45 | cleanWs() 46 | } 47 | } 48 | } -------------------------------------------------------------------------------- /ci/jenkins/Jenkinsfile: -------------------------------------------------------------------------------- 1 | @Library('infrastructure-jenkins-shared-library') _ 2 | 3 | G_gitcred = 'TonJenSSH' 4 | 5 | pipeline { 6 | agent { 7 | label 'master' 8 | } 9 | stages{ 10 | stage('test: Jenkins') { 11 | steps { 12 | script { 13 | println("test: Jenkins") 14 | } 15 | } 16 | } 17 | } 18 | post { 19 | always { 20 | cleanWs() 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /cli.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | const { consoleTerminal, everdevInit, everdevDone, cli } = require("./dist") 4 | const { rewriteKnownErrors } = require("./dist/rewriteKnownErrors") 5 | 6 | ;(async () => { 7 | try { 8 | everdevInit() 9 | await cli.run(consoleTerminal) 10 | everdevDone() 11 | } catch (err) { 12 | if (!(err instanceof Error)) { 13 | const { data, code } = err 14 | err = new Error(err.message) 15 | err.code = code 16 | err.data = data 17 | } 18 | err = rewriteKnownErrors(err) 19 | console.error(`${err}`) 20 | process.exit(1) 21 | } 22 | })() 23 | -------------------------------------------------------------------------------- /contracts/GiverV1.abi.json: -------------------------------------------------------------------------------- 1 | { 2 | "ABI version": 1, 3 | "functions": [ 4 | { 5 | "name": "constructor", 6 | "inputs": [], 7 | "outputs": [] 8 | }, 9 | { 10 | "name": "sendGrams", 11 | "inputs": [ 12 | { "name": "dest", "type": "address" }, 13 | { "name": "amount", "type": "uint64" } 14 | ], 15 | "outputs": [] 16 | } 17 | ], 18 | "events": [], 19 | "data": [] 20 | } 21 | -------------------------------------------------------------------------------- /contracts/GiverV2.abi.json: -------------------------------------------------------------------------------- 1 | {"ABI version": 2, 2 | "header": ["time", "expire"], 3 | "functions": [ 4 | { 5 | "name": "upgrade", 6 | "inputs": [ 7 | {"name":"newcode","type":"cell"} 8 | ], 9 | "outputs": [ 10 | ] 11 | }, 12 | { 13 | "name": "sendTransaction", 14 | "inputs": [ 15 | {"name":"dest","type":"address"}, 16 | {"name":"value","type":"uint128"}, 17 | {"name":"bounce","type":"bool"} 18 | ], 19 | "outputs": [ 20 | ] 21 | }, 22 | { 23 | "name": "getMessages", 24 | "inputs": [ 25 | ], 26 | "outputs": [ 27 | {"components":[{"name":"hash","type":"uint256"},{"name":"expireAt","type":"uint64"}],"name":"messages","type":"tuple[]"} 28 | ] 29 | }, 30 | { 31 | "name": "constructor", 32 | "inputs": [ 33 | ], 34 | "outputs": [ 35 | ] 36 | } 37 | ], 38 | "events": [ 39 | ] 40 | } -------------------------------------------------------------------------------- /contracts/GiverV2.tvc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/everx-labs/everdev/af680664adeea4fc536fefab298ab4eb1d4503eb/contracts/GiverV2.tvc -------------------------------------------------------------------------------- /contracts/GiverV3.abi.json: -------------------------------------------------------------------------------- 1 | { 2 | "ABI version": 2, 3 | "version": "2.3", 4 | "header": ["time", "expire"], 5 | "functions": [ 6 | { 7 | "name": "sendTransaction", 8 | "inputs": [ 9 | {"name":"dest","type":"address"}, 10 | {"name":"value","type":"uint128"}, 11 | {"name":"bounce","type":"bool"} 12 | ], 13 | "outputs": [ 14 | ] 15 | }, 16 | { 17 | "name": "getMessages", 18 | "inputs": [ 19 | ], 20 | "outputs": [ 21 | {"components":[{"name":"hash","type":"uint256"},{"name":"expireAt","type":"uint32"}],"name":"messages","type":"tuple[]"} 22 | ] 23 | }, 24 | { 25 | "name": "upgrade", 26 | "inputs": [ 27 | {"name":"newcode","type":"cell"} 28 | ], 29 | "outputs": [ 30 | ] 31 | }, 32 | { 33 | "name": "constructor", 34 | "inputs": [ 35 | ], 36 | "outputs": [ 37 | ] 38 | } 39 | ], 40 | "data": [ 41 | ], 42 | "events": [ 43 | ], 44 | "fields": [ 45 | {"name":"_pubkey","type":"uint256"}, 46 | {"name":"_constructorFlag","type":"bool"}, 47 | {"name":"m_messages","type":"map(uint256,uint32)"} 48 | ] 49 | } 50 | -------------------------------------------------------------------------------- /contracts/GiverV3.sol: -------------------------------------------------------------------------------- 1 | pragma ever-solidity >= 0.61.2; 2 | pragma AbiHeader time; 3 | pragma AbiHeader expire; 4 | 5 | abstract contract Upgradable { 6 | /* 7 | * Set code 8 | */ 9 | 10 | function upgrade(TvmCell newcode) public virtual { 11 | require(msg.pubkey() == tvm.pubkey(), 101); 12 | tvm.accept(); 13 | tvm.commit(); 14 | tvm.setcode(newcode); 15 | tvm.setCurrentCode(newcode); 16 | onCodeUpgrade(); 17 | } 18 | 19 | function onCodeUpgrade() internal virtual; 20 | } 21 | 22 | contract GiverV3 is Upgradable { 23 | 24 | uint8 constant MAX_CLEANUP_MSGS = 30; 25 | mapping(uint256 => uint32) m_messages; 26 | 27 | modifier acceptOnlyOwner { 28 | require(msg.pubkey() == tvm.pubkey(), 101); 29 | tvm.accept(); 30 | _; 31 | } 32 | 33 | /* 34 | * Publics 35 | */ 36 | 37 | /// @notice Allows to accept simple transfers. 38 | receive() external {} 39 | 40 | /// @notice Transfers grams to other contracts. 41 | function sendTransaction(address dest, uint128 value, bool bounce) public { 42 | dest.transfer(value, bounce, 3); 43 | gc(); 44 | } 45 | 46 | /* 47 | * Privates 48 | */ 49 | 50 | /// @notice Function with predefined name called after signature check. Used to 51 | /// implement custom replay protection with parallel access. 52 | function afterSignatureCheck(TvmSlice body, TvmCell) private inline 53 | returns (TvmSlice) 54 | { 55 | // owner check 56 | require(msg.pubkey() == tvm.pubkey(), 101); 57 | uint256 bodyHash = tvm.hash(body); 58 | // load and drop message timestamp (uint64) 59 | (, uint32 expireAt) = body.decode(uint64, uint32); 60 | require(expireAt > now, 57); 61 | require(!m_messages.exists(bodyHash), 102); 62 | 63 | tvm.accept(); 64 | m_messages[bodyHash] = expireAt; 65 | 66 | return body; 67 | } 68 | 69 | /// @notice Allows to delete expired messages from dict. 70 | function gc() private inline { 71 | uint counter = 0; 72 | for ((uint256 bodyHash, uint32 expireAt) : m_messages) { 73 | if (counter >= MAX_CLEANUP_MSGS) { 74 | break; 75 | } 76 | counter++; 77 | if (expireAt <= now) { 78 | delete m_messages[bodyHash]; 79 | } 80 | } 81 | } 82 | 83 | /* 84 | * Get methods 85 | */ 86 | struct Message { 87 | uint256 hash; 88 | uint32 expireAt; 89 | } 90 | function getMessages() public view returns (Message[] messages) { 91 | for ((uint256 msgHash, uint32 expireAt) : m_messages) { 92 | messages.push(Message(msgHash, expireAt)); 93 | } 94 | } 95 | 96 | function onCodeUpgrade() internal override {} 97 | } 98 | -------------------------------------------------------------------------------- /contracts/GiverV3.tvc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/everx-labs/everdev/af680664adeea4fc536fefab298ab4eb1d4503eb/contracts/GiverV3.tvc -------------------------------------------------------------------------------- /contracts/HelloWallet.abi.json: -------------------------------------------------------------------------------- 1 | { 2 | "ABI version": 2, 3 | "version": "2.2", 4 | "header": ["time", "expire"], 5 | "functions": [ 6 | { 7 | "name": "constructor", 8 | "inputs": [ 9 | ], 10 | "outputs": [ 11 | ] 12 | }, 13 | { 14 | "name": "renderHelloWorld", 15 | "inputs": [ 16 | ], 17 | "outputs": [ 18 | {"name":"value0","type":"string"} 19 | ] 20 | }, 21 | { 22 | "name": "touch", 23 | "inputs": [ 24 | ], 25 | "outputs": [ 26 | ] 27 | }, 28 | { 29 | "name": "getTimestamp", 30 | "inputs": [ 31 | ], 32 | "outputs": [ 33 | {"name":"value0","type":"uint256"} 34 | ] 35 | }, 36 | { 37 | "name": "sendValue", 38 | "inputs": [ 39 | {"name":"dest","type":"address"}, 40 | {"name":"amount","type":"uint128"}, 41 | {"name":"bounce","type":"bool"} 42 | ], 43 | "outputs": [ 44 | ] 45 | }, 46 | { 47 | "name": "timestamp", 48 | "inputs": [ 49 | ], 50 | "outputs": [ 51 | {"name":"timestamp","type":"uint32"} 52 | ] 53 | } 54 | ], 55 | "data": [ 56 | ], 57 | "events": [ 58 | ], 59 | "fields": [ 60 | {"name":"_pubkey","type":"uint256"}, 61 | {"name":"_timestamp","type":"uint64"}, 62 | {"name":"_constructorFlag","type":"bool"}, 63 | {"name":"timestamp","type":"uint32"} 64 | ] 65 | } 66 | -------------------------------------------------------------------------------- /contracts/HelloWallet.sol: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * This file was generated by EverDev. 4 | * EverDev is a part of EVER OS (see http://ton.dev). 5 | */ 6 | pragma ton-solidity >= 0.67.0; 7 | pragma AbiHeader expire; 8 | 9 | // This is class that describes you smart contract. 10 | contract HelloWallet { 11 | // Contract can have an instance variables. 12 | // In this example instance variable `timestamp` is used to store the time of `constructor` or `touch` 13 | // function call 14 | uint32 public timestamp; 15 | 16 | // Contract can have a `constructor` – function that will be called when contract will be deployed to the blockchain. 17 | // In this example constructor adds current time to the instance variable. 18 | // All contracts need call tvm.accept(); for succeeded deploy 19 | constructor() { 20 | // Check that contract's public key is set 21 | require(tvm.pubkey() != 0, 101); 22 | // Check that message has signature (msg.pubkey() is not zero) and 23 | // message is signed with the owner's private key 24 | require(msg.pubkey() == tvm.pubkey(), 102); 25 | // The current smart contract agrees to buy some gas to finish the 26 | // current transaction. This actions required to process external 27 | // messages, which bring no value (henceno gas) with themselves. 28 | tvm.accept(); 29 | 30 | timestamp = block.timestamp; 31 | } 32 | 33 | function renderHelloWorld () public pure returns (string) { 34 | tvm.accept(); 35 | return 'helloWorld'; 36 | } 37 | 38 | // Updates variable `timestamp` with current blockchain time. 39 | function touch() external { 40 | // Skip signature check 41 | // require(msg.pubkey() == tvm.pubkey(), 102); 42 | 43 | // Tells to the TVM that we accept this message. 44 | tvm.accept(); 45 | // Update timestamp 46 | timestamp = block.timestamp; 47 | } 48 | 49 | // Function returns value of state variable `timestamp` 50 | function getTimestamp() public view returns (uint) { 51 | tvm.accept(); 52 | return timestamp; 53 | } 54 | 55 | // Send specified amount of tokens to the specified address 56 | function sendValue(address dest, coins amount, bool bounce) public view { 57 | require(msg.pubkey() == tvm.pubkey(), 102); 58 | tvm.accept(); 59 | // It allows to make a transfer with arbitrary settings 60 | dest.transfer(amount, bounce, 0); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /contracts/HelloWallet.tvc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/everx-labs/everdev/af680664adeea4fc536fefab298ab4eb1d4503eb/contracts/HelloWallet.tvc -------------------------------------------------------------------------------- /contracts/MsigV2.abi.json: -------------------------------------------------------------------------------- 1 | { 2 | "ABI version": 2, 3 | "version": "2.3", 4 | "header": ["pubkey", "time", "expire"], 5 | "functions": [ 6 | { 7 | "name": "constructor", 8 | "inputs": [ 9 | {"name":"owners","type":"uint256[]"}, 10 | {"name":"reqConfirms","type":"uint8"}, 11 | {"name":"lifetime","type":"uint32"} 12 | ], 13 | "outputs": [ 14 | ] 15 | }, 16 | { 17 | "name": "sendTransaction", 18 | "inputs": [ 19 | {"name":"dest","type":"address"}, 20 | {"name":"value","type":"uint128"}, 21 | {"name":"bounce","type":"bool"}, 22 | {"name":"flags","type":"uint8"}, 23 | {"name":"payload","type":"cell"} 24 | ], 25 | "outputs": [ 26 | ] 27 | }, 28 | { 29 | "name": "submitTransaction", 30 | "inputs": [ 31 | {"name":"dest","type":"address"}, 32 | {"name":"value","type":"uint128"}, 33 | {"name":"bounce","type":"bool"}, 34 | {"name":"allBalance","type":"bool"}, 35 | {"name":"payload","type":"cell"}, 36 | {"name":"stateInit","type":"optional(cell)"} 37 | ], 38 | "outputs": [ 39 | {"name":"transId","type":"uint64"} 40 | ] 41 | }, 42 | { 43 | "name": "confirmTransaction", 44 | "inputs": [ 45 | {"name":"transactionId","type":"uint64"} 46 | ], 47 | "outputs": [ 48 | ] 49 | }, 50 | { 51 | "name": "isConfirmed", 52 | "inputs": [ 53 | {"name":"mask","type":"uint32"}, 54 | {"name":"index","type":"uint8"} 55 | ], 56 | "outputs": [ 57 | {"name":"confirmed","type":"bool"} 58 | ] 59 | }, 60 | { 61 | "name": "getParameters", 62 | "inputs": [ 63 | ], 64 | "outputs": [ 65 | {"name":"maxQueuedTransactions","type":"uint8"}, 66 | {"name":"maxCustodianCount","type":"uint8"}, 67 | {"name":"expirationTime","type":"uint64"}, 68 | {"name":"minValue","type":"uint128"}, 69 | {"name":"requiredTxnConfirms","type":"uint8"}, 70 | {"name":"requiredUpdConfirms","type":"uint8"} 71 | ] 72 | }, 73 | { 74 | "name": "getTransaction", 75 | "inputs": [ 76 | {"name":"transactionId","type":"uint64"} 77 | ], 78 | "outputs": [ 79 | {"components":[{"name":"id","type":"uint64"},{"name":"confirmationsMask","type":"uint32"},{"name":"signsRequired","type":"uint8"},{"name":"signsReceived","type":"uint8"},{"name":"creator","type":"uint256"},{"name":"index","type":"uint8"},{"name":"dest","type":"address"},{"name":"value","type":"uint128"},{"name":"sendFlags","type":"uint16"},{"name":"payload","type":"cell"},{"name":"bounce","type":"bool"},{"name":"stateInit","type":"optional(cell)"}],"name":"trans","type":"tuple"} 80 | ] 81 | }, 82 | { 83 | "name": "getTransactions", 84 | "inputs": [ 85 | ], 86 | "outputs": [ 87 | {"components":[{"name":"id","type":"uint64"},{"name":"confirmationsMask","type":"uint32"},{"name":"signsRequired","type":"uint8"},{"name":"signsReceived","type":"uint8"},{"name":"creator","type":"uint256"},{"name":"index","type":"uint8"},{"name":"dest","type":"address"},{"name":"value","type":"uint128"},{"name":"sendFlags","type":"uint16"},{"name":"payload","type":"cell"},{"name":"bounce","type":"bool"},{"name":"stateInit","type":"optional(cell)"}],"name":"transactions","type":"tuple[]"} 88 | ] 89 | }, 90 | { 91 | "name": "getCustodians", 92 | "inputs": [ 93 | ], 94 | "outputs": [ 95 | {"components":[{"name":"index","type":"uint8"},{"name":"pubkey","type":"uint256"}],"name":"custodians","type":"tuple[]"} 96 | ] 97 | }, 98 | { 99 | "name": "submitUpdate", 100 | "inputs": [ 101 | {"name":"codeHash","type":"optional(uint256)"}, 102 | {"name":"owners","type":"optional(uint256[])"}, 103 | {"name":"reqConfirms","type":"optional(uint8)"}, 104 | {"name":"lifetime","type":"optional(uint32)"} 105 | ], 106 | "outputs": [ 107 | {"name":"updateId","type":"uint64"} 108 | ] 109 | }, 110 | { 111 | "name": "confirmUpdate", 112 | "inputs": [ 113 | {"name":"updateId","type":"uint64"} 114 | ], 115 | "outputs": [ 116 | ] 117 | }, 118 | { 119 | "name": "executeUpdate", 120 | "inputs": [ 121 | {"name":"updateId","type":"uint64"}, 122 | {"name":"code","type":"optional(cell)"} 123 | ], 124 | "outputs": [ 125 | ] 126 | }, 127 | { 128 | "name": "getUpdateRequests", 129 | "inputs": [ 130 | ], 131 | "outputs": [ 132 | {"components":[{"name":"id","type":"uint64"},{"name":"index","type":"uint8"},{"name":"signs","type":"uint8"},{"name":"confirmationsMask","type":"uint32"},{"name":"creator","type":"uint256"},{"name":"codeHash","type":"optional(uint256)"},{"name":"custodians","type":"optional(uint256[])"},{"name":"reqConfirms","type":"optional(uint8)"},{"name":"lifetime","type":"optional(uint32)"}],"name":"updates","type":"tuple[]"} 133 | ] 134 | } 135 | ], 136 | "data": [ 137 | ], 138 | "events": [ 139 | ], 140 | "fields": [ 141 | {"name":"_pubkey","type":"uint256"}, 142 | {"name":"_timestamp","type":"uint64"}, 143 | {"name":"_constructorFlag","type":"bool"}, 144 | {"name":"m_ownerKey","type":"uint256"}, 145 | {"name":"m_requestsMask","type":"uint256"}, 146 | {"components":[{"name":"id","type":"uint64"},{"name":"confirmationsMask","type":"uint32"},{"name":"signsRequired","type":"uint8"},{"name":"signsReceived","type":"uint8"},{"name":"creator","type":"uint256"},{"name":"index","type":"uint8"},{"name":"dest","type":"address"},{"name":"value","type":"uint128"},{"name":"sendFlags","type":"uint16"},{"name":"payload","type":"cell"},{"name":"bounce","type":"bool"},{"name":"stateInit","type":"optional(cell)"}],"name":"m_transactions","type":"map(uint64,tuple)"}, 147 | {"name":"m_custodians","type":"map(uint256,uint8)"}, 148 | {"name":"m_custodianCount","type":"uint8"}, 149 | {"components":[{"name":"id","type":"uint64"},{"name":"index","type":"uint8"},{"name":"signs","type":"uint8"},{"name":"confirmationsMask","type":"uint32"},{"name":"creator","type":"uint256"},{"name":"codeHash","type":"optional(uint256)"},{"name":"custodians","type":"optional(uint256[])"},{"name":"reqConfirms","type":"optional(uint8)"},{"name":"lifetime","type":"optional(uint32)"}],"name":"m_updateRequests","type":"map(uint64,tuple)"}, 150 | {"name":"m_updateRequestsMask","type":"uint32"}, 151 | {"name":"m_requiredVotes","type":"uint8"}, 152 | {"name":"m_defaultRequiredConfirmations","type":"uint8"}, 153 | {"name":"m_lifetime","type":"uint32"} 154 | ] 155 | } 156 | -------------------------------------------------------------------------------- /contracts/MsigV2.tvc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/everx-labs/everdev/af680664adeea4fc536fefab298ab4eb1d4503eb/contracts/MsigV2.tvc -------------------------------------------------------------------------------- /contracts/SafeMultisigWallet.abi.json: -------------------------------------------------------------------------------- 1 | { 2 | "ABI version": 2, 3 | "header": ["pubkey", "time", "expire"], 4 | "functions": [ 5 | { 6 | "name": "constructor", 7 | "inputs": [ 8 | {"name":"owners","type":"uint256[]"}, 9 | {"name":"reqConfirms","type":"uint8"} 10 | ], 11 | "outputs": [ 12 | ] 13 | }, 14 | { 15 | "name": "acceptTransfer", 16 | "inputs": [ 17 | {"name":"payload","type":"bytes"} 18 | ], 19 | "outputs": [ 20 | ] 21 | }, 22 | { 23 | "name": "sendTransaction", 24 | "inputs": [ 25 | {"name":"dest","type":"address"}, 26 | {"name":"value","type":"uint128"}, 27 | {"name":"bounce","type":"bool"}, 28 | {"name":"flags","type":"uint8"}, 29 | {"name":"payload","type":"cell"} 30 | ], 31 | "outputs": [ 32 | ] 33 | }, 34 | { 35 | "name": "submitTransaction", 36 | "inputs": [ 37 | {"name":"dest","type":"address"}, 38 | {"name":"value","type":"uint128"}, 39 | {"name":"bounce","type":"bool"}, 40 | {"name":"allBalance","type":"bool"}, 41 | {"name":"payload","type":"cell"} 42 | ], 43 | "outputs": [ 44 | {"name":"transId","type":"uint64"} 45 | ] 46 | }, 47 | { 48 | "name": "confirmTransaction", 49 | "inputs": [ 50 | {"name":"transactionId","type":"uint64"} 51 | ], 52 | "outputs": [ 53 | ] 54 | }, 55 | { 56 | "name": "isConfirmed", 57 | "inputs": [ 58 | {"name":"mask","type":"uint32"}, 59 | {"name":"index","type":"uint8"} 60 | ], 61 | "outputs": [ 62 | {"name":"confirmed","type":"bool"} 63 | ] 64 | }, 65 | { 66 | "name": "getParameters", 67 | "inputs": [ 68 | ], 69 | "outputs": [ 70 | {"name":"maxQueuedTransactions","type":"uint8"}, 71 | {"name":"maxCustodianCount","type":"uint8"}, 72 | {"name":"expirationTime","type":"uint64"}, 73 | {"name":"minValue","type":"uint128"}, 74 | {"name":"requiredTxnConfirms","type":"uint8"} 75 | ] 76 | }, 77 | { 78 | "name": "getTransaction", 79 | "inputs": [ 80 | {"name":"transactionId","type":"uint64"} 81 | ], 82 | "outputs": [ 83 | {"components":[{"name":"id","type":"uint64"},{"name":"confirmationsMask","type":"uint32"},{"name":"signsRequired","type":"uint8"},{"name":"signsReceived","type":"uint8"},{"name":"creator","type":"uint256"},{"name":"index","type":"uint8"},{"name":"dest","type":"address"},{"name":"value","type":"uint128"},{"name":"sendFlags","type":"uint16"},{"name":"payload","type":"cell"},{"name":"bounce","type":"bool"}],"name":"trans","type":"tuple"} 84 | ] 85 | }, 86 | { 87 | "name": "getTransactions", 88 | "inputs": [ 89 | ], 90 | "outputs": [ 91 | {"components":[{"name":"id","type":"uint64"},{"name":"confirmationsMask","type":"uint32"},{"name":"signsRequired","type":"uint8"},{"name":"signsReceived","type":"uint8"},{"name":"creator","type":"uint256"},{"name":"index","type":"uint8"},{"name":"dest","type":"address"},{"name":"value","type":"uint128"},{"name":"sendFlags","type":"uint16"},{"name":"payload","type":"cell"},{"name":"bounce","type":"bool"}],"name":"transactions","type":"tuple[]"} 92 | ] 93 | }, 94 | { 95 | "name": "getTransactionIds", 96 | "inputs": [ 97 | ], 98 | "outputs": [ 99 | {"name":"ids","type":"uint64[]"} 100 | ] 101 | }, 102 | { 103 | "name": "getCustodians", 104 | "inputs": [ 105 | ], 106 | "outputs": [ 107 | {"components":[{"name":"index","type":"uint8"},{"name":"pubkey","type":"uint256"}],"name":"custodians","type":"tuple[]"} 108 | ] 109 | } 110 | ], 111 | "data": [ 112 | ], 113 | "events": [ 114 | { 115 | "name": "TransferAccepted", 116 | "inputs": [ 117 | {"name":"payload","type":"bytes"} 118 | ], 119 | "outputs": [ 120 | ] 121 | } 122 | ] 123 | } 124 | -------------------------------------------------------------------------------- /contracts/SafeMultisigWallet.keys.json: -------------------------------------------------------------------------------- 1 | { 2 | "public": "99c84f920c299b5d80e4fcce2d2054b05466ec9df19532a688c10eb6dd8d6b33", 3 | "secret": "73b60dc6a5b1d30a56a81ea85e0e453f6957dbfbeefb57325ca9f7be96d3fe1a" 4 | } -------------------------------------------------------------------------------- /contracts/SafeMultisigWallet.tvc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/everx-labs/everdev/af680664adeea4fc536fefab298ab4eb1d4503eb/contracts/SafeMultisigWallet.tvc -------------------------------------------------------------------------------- /contracts/seGiver.keys.json: -------------------------------------------------------------------------------- 1 | { 2 | "public": "2ada2e65ab8eeab09490e3521415f45b6e42df9c760a639bcf53957550b25a16", 3 | "secret": "172af540e43a524763dd53b26a066d472a97c4de37d5498170564510608250c3" 4 | } -------------------------------------------------------------------------------- /docs/.gitbook/assets/Everscale Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/everx-labs/everdev/af680664adeea4fc536fefab298ab4eb1d4503eb/docs/.gitbook/assets/Everscale Logo.png -------------------------------------------------------------------------------- /docs/.gitbook/assets/Everscale Logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /docs/.gitbook/assets/vf-dev-program.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/everx-labs/everdev/af680664adeea4fc536fefab298ab4eb1d4503eb/docs/.gitbook/assets/vf-dev-program.png -------------------------------------------------------------------------------- /docs/SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Table of contents 2 | 3 | * [EverDev](../README.md) 4 | * [Troubleshooting](troubleshooting.md) 5 | * [Use in JS applications](use-in-js-applications.md) 6 | 7 | ## Command line interface 8 | 9 | * [Solidity](command-line-interface/solidity.md) 10 | * [Solidity Compiler Driver](command-line-interface/solidity-compiler-driver.md) 11 | * [C++](command-line-interface/c.md) 12 | * [Evernode SE](command-line-interface/evernode-platform-startup-edition-se.md) 13 | * [Network Tool](command-line-interface/network-tool.md) 14 | * [Signer Tool](command-line-interface/signer-tool.md) 15 | * [Contract management](command-line-interface/contract-management.md) 16 | * [TestSuite4](command-line-interface/testsuite4.md) 17 | * [DeBrowser](command-line-interface/debrowser.md) 18 | 19 | ## Guides 20 | 21 | * [Quick Start](guides/quick-start.md) 22 | * [Create controller](guides/creating-controller.md) 23 | * [View controller info](view-controller-info.md) 24 | 25 | ## Links 26 | 27 | * [everdev repository](https://github.com/everx-labs/everdev) 28 | * [ever-sdk repository](https://github.com/everx-labs/ever-sdk) 29 | -------------------------------------------------------------------------------- /docs/command-line-interface/c.md: -------------------------------------------------------------------------------- 1 | # C++ 2 | 3 | ## Create your first contract 4 | 5 | This command creates a basic C++ contract with comments that you can observe and compile. 6 | 7 | ```shell 8 | everdev clang create Contract 9 | ``` 10 | 11 | ## Compile 12 | 13 | This command compiles and links a selected C++ contract. After successful compilation you get .abi.json and .tvc files that you can later [use in your DApps to deploy and run contract methods](https://docs.everos.dev/ever-sdk/guides/work\_with\_contracts/add\_contract\_to\_your\_app). 14 | 15 | ```shell 16 | everdev clang compile Contract.cpp 17 | ``` 18 | 19 | ## Version 20 | 21 | This command shows the currently installed C++ compiler version. 22 | 23 | ```shell 24 | everdev clang version 25 | ``` 26 | 27 | ## Update 28 | 29 | This command updates the compiler to the latest version. 30 | 31 | ```shell 32 | everdev clang update 33 | ``` 34 | 35 | Use `--force` or `-f` option to force reinstall, if the compiler is already up to date. 36 | 37 | ## Set 38 | 39 | This command sets the compiler version and downloads it if needed. 40 | 41 | ```shell 42 | everdev clang set --compiler 7.0.0 43 | ``` 44 | 45 | Use `--force` or `-f` option to force reinstall, if the current version is the same as the requested version. 46 | -------------------------------------------------------------------------------- /docs/command-line-interface/debrowser.md: -------------------------------------------------------------------------------- 1 | # DeBrowser 2 | [The ExtraTON DeBot Browser](https://github.com/extraton/debrowser/). 3 | 4 | ## Version 5 | This command shows the list of available versions. 6 | 7 | ```shell 8 | everdev debrowser version 9 | 10 | Available Versions: 1.1.0, 1.2.0, 1.2.1, 1.3.1 11 | ``` 12 | 13 | ## Interfaces 14 | This command shows the list of implemented interfaces. 15 | 16 | ```shell 17 | everdev debrowser interfaces 18 | 19 | Realised interfaces: 20 | - Address Input 21 | - Amount Input 22 | - Confirm Input 23 | - Menu 24 | - Network 25 | - Number Input 26 | - QR Code 27 | - Signing Box Input 28 | - Terminal 29 | - User Info 30 | ``` 31 | 32 | ## Start 33 | This command downloads image and starts DeBrowser container (Docker must be launched). 34 | 35 | ```shell 36 | everdev debrowser start 1.3.1 37 | ``` 38 | 39 | ## Stop 40 | This command stops DeBrowser container. 41 | 42 | ```shell 43 | everdev debrowser stop 44 | ``` 45 | -------------------------------------------------------------------------------- /docs/command-line-interface/evernode-platform-startup-edition-se.md: -------------------------------------------------------------------------------- 1 | # Evernode SE 2 | 3 | ## Start 4 | 5 | This command starts the Evernode SE container (Docker must be launched). When executed for the first time downloads the latest SE image from dockerhub. 6 | 7 | ```shell 8 | everdev se start 9 | ``` 10 | 11 | To make sure ArangoDB starts, [set the ArangoDB port](evernode-platform-startup-edition-se.md#set). 12 | 13 | ## Version 14 | 15 | This command shows the default Evernode SE version and list of other available versions. 16 | 17 | ```shell 18 | everdev se version 19 | 20 | default: 0.24.12 21 | Available Versions: 0, 0.24, 0.24.5, 0.24.6, 0.24.8, 0.24.9, 0.24.10, 0.24.11, 0.24.12, latest 22 | ``` 23 | 24 | ## Set 25 | 26 | This command switches Evernode SE to the specified version and port and downloads it, if it is missing. **Attention! This command does not start EVER OS SE, you need to run `start` command separately.** 27 | 28 | ```shell 29 | everdev se set --version 0.24.11 --port 2020 30 | ``` 31 | 32 | To make sure ArangoDB starts, use `--db-port` option to set the ArangoDB port: 33 | 34 | ```shell 35 | everdev se set --db-port 8081 36 | ``` 37 | 38 | ## Reset 39 | 40 | This command resets the Evernode SE container (Docker must be launched) - restarts it from scratch with a clean database. 41 | 42 | ```shell 43 | everdev se reset 44 | ``` 45 | 46 | ## Update 47 | 48 | This command downloads the latest Evernode SE image (Docker must be launched) and starts it. 49 | 50 | ```shell 51 | everdev se update 52 | ``` 53 | 54 | ## Stop 55 | 56 | This command stops Evernode SE container. 57 | 58 | ```shell 59 | everdev se stop 60 | ``` 61 | 62 | ## Info 63 | 64 | This command shows info about the downloaded versions. 65 | 66 | ```shell 67 | everdev se info 68 | 69 | Instance State Version GraphQL Port ArangoDB Port Docker Container Docker Image 70 | -------- ------- ------- ------------ ------------- -------------------------- -------------------------- 71 | default running 0.24.12 2020 tonlabs-tonos-se-ekaterina tonlabs/local-node:0.24.12 72 | ``` 73 | 74 | ## Troubleshooting 75 | 76 | View solutions to some frequently encountered problems with Evernode SE [here](../troubleshooting.md). 77 | -------------------------------------------------------------------------------- /docs/command-line-interface/network-tool.md: -------------------------------------------------------------------------------- 1 | # Network Tool 2 | 3 | Network tool is a convenient way to organize all of your network configurations in one place. 4 | 5 | You can register several blockchains (networks) under short names and then use these names as a target blockchain when working with contracts. 6 | 7 | You can mark one of the networks as a default. It can be used in network commands without providing net name. 8 | 9 | ## Available commands 10 | 11 | See available commands with help command: 12 | 13 | ``` 14 | $ everdev network --help 15 | EverDev Version: 1.4.0 16 | Use: everdev network command args [options] 17 | Options: 18 | --help, -h Show command usage 19 | Commands: 20 | add Add net 21 | credentials, c Set credentials for network authentication 22 | delete Delete network from registry 23 | list, l Prints list of networks 24 | info, i Prints network detailed information 25 | default, d Set default network 26 | giver, g Set giver for network 27 | ``` 28 | 29 | ## Add a network 30 | 31 | This command adds a network to the everdev registry. 32 | 33 | ```bash 34 | everdev network add network_name network_endpoint(s) 35 | ``` 36 | 37 | See other available network addition options with help command: 38 | 39 | ```bash 40 | $ everdev network add -h 41 | EverDev Version: 0.5.0 42 | Use: everdev network add name endpoints [options] 43 | Args: 44 | name 45 | endpoints Comma separated endpoints 46 | Options: 47 | --help, -h Show command usage 48 | --force, -f Overwrite key if already exists 49 | ``` 50 | 51 | Example with [mainnet endpoint](https://docs.everos.dev/ever-sdk/reference/ever-os-api/networks): 52 | 53 | ```bash 54 | everdev network add main mainnet.evercloud.dev 55 | ``` 56 | 57 | ## Set credentials for a network 58 | 59 | ``` 60 | $ everdev network credentials --help 61 | EverDev Version: 1.4.0 62 | Use: everdev network credentials name [options] 63 | Args: 64 | name Network name 65 | Options: 66 | --help, -h Show command usage 67 | --project, -p Your project ID 68 | --access-key, -k Your secret or JWT token 69 | --clear Clear saved credentials (mutually exclusive with other options) 70 | ``` 71 | 72 | Access to the devnet and mainnet blockchains requires authorization.\ 73 | Create a project on [dashboard.evercloud.dev](https://dashboard.evercloud.dev) if you don't have one, find your "Project Id" and "Secret" (optional) on the "Security" tab, and pass them as parameters: 74 | 75 | Example with "devnet" endpoint: 76 | 77 | ```bash 78 | everdev network credentials devnet --project --access-key 79 | ``` 80 | 81 | ## Set a giver for a network 82 | 83 | This command sets a giver account for a network. Giver will be used to top up your account balances on the network, including during deployment. 84 | 85 | ```bash 86 | everdev network giver network_name giver_address --type giver_type 87 | ``` 88 | 89 | See other available network addition options with help command: 90 | 91 | ```bash 92 | $ everdev network giver -h 93 | EverDev Version: 0.5.0 94 | Use: everdev network giver name address [options] 95 | Args: 96 | name Network name 97 | address Giver address 98 | Options: 99 | --help, -h Show command usage 100 | --signer, -s Signer to be used with giver 101 | --value, -v Deploying account initial balance in nanotokens 102 | --type, -t Type giver contract (GiverV1 | GiverV2 | GiverV3 | SafeMultisigWallet | MsigV2 | SetcodeMultisigWallet) 103 | ``` 104 | 105 | **Note:** The default signer and the initial balance value of 10 tokens will be used, unless otherwise specified through options. Also note, that some contracts may require a higher initial balance for successful deployment. DePool contract, for instance, requires a minimun of 21 tokens. 106 | 107 | Only one giver can be set for a network. Setting another one will overwrite the current giver. To view the current giver settings for all networks, use the `everdev network list` command (for details see the section [below](network-tool.md#list-registered-networks)). 108 | 109 | ## List the registered networks 110 | 111 | This command lists all registered networks, their public endpoints, and their giver addresses, if any. 112 | 113 | ```bash 114 | everdev network list 115 | ``` 116 | 117 | Result: 118 | 119 | ```bash 120 | $ everdev network list 121 | Network Endpoints Giver 122 | ------------- ----------------------------------------------- ------------------------------------------------------------------ 123 | se http://localhost 0:b5e9240fc2d2f1ff8cbb1d1dee7fb7cae155e5f6320e585fcc685698994a19a5 124 | dev (Default) https://devnet.evercloud.dev 0:255a3ad9dfa8aa4f3481856aafc7d79f47d50205190bd56147138740e9b177f3 125 | ``` 126 | 127 | ## Set a default network 128 | 129 | This command sets a previously added network as default (initially the mainnet is used by default). 130 | 131 | ```bash 132 | everdev network default network_name 133 | ``` 134 | 135 | ## Delete a network 136 | 137 | This command deletes a network from everdev registry. 138 | 139 | ```bash 140 | everdev network delete network_name 141 | ``` 142 | -------------------------------------------------------------------------------- /docs/command-line-interface/sdk.md: -------------------------------------------------------------------------------- 1 | # SDK 2 | 3 | ## See the list of available demo projects 4 | 5 | This command shows the list of available demo projects 6 | 7 | ```shell 8 | everdev js demo 9 | ``` 10 | 11 | Result: 12 | 13 | ```shell 14 | $ everdev js demo 15 | Demo Description 16 | ------------ ------------------------- 17 | hello-wallet Simple NodeJs Application 18 | ``` 19 | 20 | ## Install demo project 21 | 22 | This command installs the specified demo project to the current directory. Proceed the instructions in the terminal to run it. 23 | 24 | ```shell 25 | everdev js demo hello-wallet 26 | ``` 27 | 28 | ## Create an empty project 29 | 30 | This command creates a Node.js project with SDK latest dependencies and index.js file with main Client object creation. 31 | 32 | ```shell 33 | everdev js create test_project 34 | ``` 35 | 36 | ## Create contract JS wrapper 37 | 38 | This command takes abi and, optionally, tvc file and generates a JS wrapper with abi and tvc converted into base64 that can be used further in SDK. 39 | tvc file must have the same name as abi. 40 | 41 | ```shell 42 | everdev js wrap contractName.abi.json 43 | ``` 44 | The result name of the wrapper will be "ContractName||"Contract".js". 45 | 46 | See other available generation options with help command: 47 | 48 | ```shell 49 | everdev js wrap -h 50 | EverDev Version: 0.4.0 51 | Use: everdev js wrap file [options] 52 | Args: 53 | file ABI file 54 | Options: 55 | --help, -h Show command usage 56 | --print, -p Print code to console 57 | --output, -o Set output file name (default is built from source ABI file name) 58 | --export, -e Export type and options 59 | commonjs Use CommonJS modules (NodeJs) 60 | commonjs-default Use CommonJS modules (NodeJS) with default export 61 | es6 Use ES6 modules 62 | es6-default Use ES6 modules with default export 63 | ``` 64 | -------------------------------------------------------------------------------- /docs/command-line-interface/signer-tool.md: -------------------------------------------------------------------------------- 1 | # Signer Tool 2 | 3 | Signer registry is a centralized place where you can store your development keys. 4 | 5 | Each signer in registry has an unique user defined name. All everdev commands 6 | that require signing or encryption refer to the signer by name. 7 | 8 | You can mark one of the signers as a default. 9 | It can be used in signing commands without providing signer option. 10 | 11 | Signer repository management in everdev is accessible through the `signer` tool. 12 | 13 | **Note:** If you need to generate an unsigned message, you may use the option `--signer none` in any relevant commands in other controllers. Omitting the signer option altogether always means using the default signer. 14 | 15 | **Note:** Keys in the repository are stored unencrypted. 16 | 17 | ## Add a signer with randomly generated keys 18 | 19 | This command adds a signer with randomly generated keys. 20 | 21 | ```bash 22 | everdev signer generate signer_name 23 | ``` 24 | 25 | See other available generation options with help command: 26 | 27 | ```bash 28 | everdev signer generate -h 29 | EverDev Version: 0.5.0 30 | Use: everdev signer generate name [options] 31 | Args: 32 | name Signer name 33 | Options: 34 | --help, -h Show command usage 35 | --mnemonic, -m Use mnemonic phrase 36 | --dictionary, -d Mnemonic dictionary 37 | 0 TON 38 | 1 English 39 | 2 Chinese Simplified 40 | 3 Chinese Traditional 41 | 4 French 42 | 5 Italian 43 | 6 Japanese 44 | 7 Korean 45 | 8 Spanish 46 | --words, -w Number of mnemonic words 47 | --force, -f Overwrite signer if already exists 48 | ``` 49 | 50 | ## Add a signer with specific keys 51 | 52 | This command adds a signer with previously generated (e.g. with ever-cli) keys. 53 | 54 | ```bash 55 | everdev signer add signer_name signer_secret_key_or_seed_phrase_in_quotes 56 | ``` 57 | 58 | See other available signer addition options with help command: 59 | 60 | ```bash 61 | everdev signer add -h 62 | EverDev Version: 0.5.0 63 | Use: everdev signer add name secret [options] 64 | Args: 65 | name Signer name 66 | secret Secret key or seed phrase 67 | Options: 68 | --help, -h Show command usage 69 | --dictionary, -d Mnemonic dictionary 70 | 0 TON 71 | 1 English 72 | 2 Chinese Simplified 73 | 3 Chinese Traditional 74 | 4 French 75 | 5 Italian 76 | 6 Japanese 77 | 7 Korean 78 | 8 Spanish 79 | --force, -f Overwrite signer if already exists 80 | ``` 81 | **Note:** By default the dictionary is set to english, which allows using seed phrases generated by other EVERX tools, such as ever-cli. 82 | 83 | 84 | ## List registered signers 85 | 86 | This command lists all registered signers with their public keys. 87 | 88 | ```bash 89 | everdev signer list 90 | ``` 91 | 92 | Result: 93 | 94 | ```bash 95 | $ everdev signer list 96 | 97 | Signer Public Key 98 | --------------- ---------------------------------------------------------------- 99 | sign1 (Default) cffd3a2f1d241807b2205220a7d6df980e67a3cc7c47eba2766cdc1bbddfc0e3 100 | sign2 0fc4e781720d80f76257db333c6b6934090562418652cf30352878c87707aa94 101 | ``` 102 | 103 | ## Get signer details 104 | 105 | This command lists all information (including secret data) for a specified signer. 106 | 107 | ```bash 108 | everdev signer info signer_name 109 | ``` 110 | 111 | Result: 112 | 113 | ```bash 114 | $ everdev signer info sign2 115 | { 116 | "name": "sign2", 117 | "description": "", 118 | "keys": { 119 | "public": "760d69964d038997d891fca0a0407c2ffefb701e7cb2f9ff0a87fbbf1e8098f2", 120 | "secret": "72571b5a9392e6bb215b460ca3c0545c34d790e185f66f5b2e7564329ffea86c" 121 | } 122 | } 123 | ``` 124 | 125 | ## Set default signer 126 | 127 | This command sets a previously added signer as default (initially the first added signer is used by default). 128 | 129 | ```bash 130 | everdev signer default signer_name 131 | ``` 132 | 133 | ## Delete a signer 134 | 135 | This command deletes a previously added signer from signer registry. 136 | 137 | ```bash 138 | everdev signer delete signer_name 139 | ``` 140 | -------------------------------------------------------------------------------- /docs/command-line-interface/sold.md: -------------------------------------------------------------------------------- 1 | # Solidity compiler diver 2 | 3 | ## Version 4 | 5 | This command shows the currently installed sold version. 6 | 7 | ```shell 8 | everdev sold version 9 | ``` 10 | 11 | ## Install 12 | 13 | This command installs the latest sold. 14 | 15 | ```shell 16 | everdev sold install 17 | ``` 18 | The installer requires NPM to be installed, so it can install packages globally without using sudo. 19 | In case of error, manually set environment variable `PATH=$PATH:$HOME/.everdev/solidity` 20 | 21 | ## Update 22 | 23 | This command updates the sold executable to the latest version. 24 | 25 | ```shell 26 | everdev sold update 27 | ``` 28 | 29 | **Attention!** Use --force option to force update of components that do not update their version. 30 | 31 | ## Set 32 | 33 | This command specifies sold version to use and downloads it if needed. 34 | 35 | ```shell 36 | everdev sold set --version 0.8.0 37 | ``` 38 | -------------------------------------------------------------------------------- /docs/command-line-interface/solidity-compiler-driver.md: -------------------------------------------------------------------------------- 1 | # Solidity Compiler Driver 2 | 3 | Everdev allows to install and manage Solidity Compiler Driver. 4 | 5 | It is a combined binary that includes Solidity compiler, TVM linker and required libraries, and streamlines the compilation process. 6 | 7 | To use Solidity Compiler Driver globally after installation via Everdev, add it to the system environment: 8 | 9 | ```shell 10 | export PATH="$HOME/.everdev/sold:$PATH" 11 | ``` 12 | 13 | Then use with the following command: 14 | 15 | ``` 16 | sold [OPTIONS] [INPUT]... 17 | ``` 18 | 19 | To see the full documentation, use `sold --help`: 20 | 21 | ``` 22 | $ sold --help 23 | sold, the Ever Solidity commandline driver 24 | 25 | Usage: sold [OPTIONS] [INPUT]... 26 | 27 | Arguments: 28 | [INPUT]... Source file name or remappings in the form of context:prefix=target 29 | 30 | Options: 31 | -c, --contract Contract to build if sources define more than one contract 32 | --base-path Use the given path as the root of the source tree instead of the root of the filesystem 33 | -i, --include-path Make an additional source directory available to the default import callback. Use this option if you want to import contracts whose location is not fixed in relation to your main source tree, e.g. third-party libraries installed using a package manager. Can be used multiple times. Can only be used if base path has a non-empty value 34 | --allowed-path Allow a given path for imports. A list of paths can be supplied by separating them with a comma 35 | -l, --lib Library to use instead of default 36 | -p, --output-prefix Prefix for output files (by default, input file stem is used as prefix) 37 | -o, --output-dir Output directory (by default, current directory is used) 38 | --print-code Print the code cell to stdout 39 | --abi-json ABI specification of the contracts 40 | --function-ids Print name and id for each public function 41 | --private-function-ids Print name and id for each private function 42 | --ast-compact-json AST of all source files in a compact JSON format 43 | --userdoc Natspec user documentation of all contracts 44 | --devdoc Natspec developer documentation of all contracts 45 | --init Set newly generated keypair 46 | --silent Mute all notifications 47 | -h, --help Print help 48 | -V, --version Print version 49 | ``` 50 | 51 | {% hint style="info" %} 52 | **Note**: Version 0.68.0 and earlier versions have some dependency issues on older Linux systems. If you encounter any such issues, update to 0.69.0 or later. 53 | {% endhint %} 54 | 55 | ## Install 56 | 57 | This command installs the latest Solidity Compiler Driver 58 | 59 | ```shell 60 | everdev sold install 61 | ``` 62 | 63 | To use Solidity Compiler Driver globally after installation via Everdev, add it to the system environment: 64 | 65 | ```shell 66 | export PATH="$HOME/.everdev/sold:$PATH" 67 | ``` 68 | 69 | ## Version 70 | 71 | This command shows the used Solidity Compiler Driver version and list of available for download versions 72 | 73 | ```shell 74 | everdev sold version 75 | Component Version Available 76 | --------- ------- -------------------------------------------------------------- 77 | driver 0.69.0 0.69.0, 0.68.0, 0.67.0, 0.66.0, 0.66.0, 0.65.0, 0.65.0, 0.64.0 78 | 79 | File path: /home//.everdev/sold/sold 80 | ``` 81 | 82 | ## Set 83 | 84 | This command specifies Solidity Compiler Driver version to use and downloads it if needed. 85 | 86 | ```shell 87 | everdev sold set --version 0.69.0 88 | ``` 89 | 90 | ## Update 91 | 92 | This command updates Solidity Compiler Driver version to the latest 93 | 94 | ```shell 95 | everdev sold update 96 | ``` 97 | -------------------------------------------------------------------------------- /docs/command-line-interface/solidity.md: -------------------------------------------------------------------------------- 1 | # Solidity 2 | 3 | ## Create your first contract 4 | 5 | This command creates a hello-world Solidity contract with comments that you can observe and compile. 6 | 7 | ```shell 8 | everdev sol create Contract 9 | ``` 10 | 11 | ## Compile 12 | 13 | This command compiles and links a selected Solidity contract. After successful compilation you get .abi.json and .tvc files that you can later [use in your DApps to deploy and run contract methods](https://docs.everos.dev/ever-sdk/guides/work\_with\_contracts/add\_contract\_to\_your\_app). 14 | 15 | ```shell 16 | everdev sol compile Contract.sol 17 | ``` 18 | 19 | To save generated assembler code use `-c` option (default is false) 20 | 21 | ```shell 22 | everdev sol compile Contract.sol -c path/to/output/file 23 | ``` 24 | 25 | Assembler code will be saved in path/to/output/file with the extension `code` 26 | 27 | You can specify the output files location with the `-o` option: 28 | 29 | ```shell 30 | everdev sol compile Contract.sol -o path/to/output/file 31 | ``` 32 | 33 | To make an additional source directory available to the default import callback use `-i` option: 34 | 35 | ```shell 36 | everdev sol compile Contract.sol -i path/to/importFolder 37 | ``` 38 | 39 | Use this option if you want to import contracts, whose location is not fixed in relation to your main source tree, e.g. third-party libraries. 40 | 41 | Separate different paths with a comma, no space allowed. 42 | 43 | ```shell 44 | everdev sol compile Contract.sol -i path/to/folder1,path/to/folder2 45 | ``` 46 | 47 | The default value is `node_modules` folder. 48 | 49 | ## Ast 50 | 51 | This command parses a ton-solidity file and creates an abstract syntax tree (AST) to the output directory. 52 | 53 | ```shell 54 | everdev sol ast Contract.sol 55 | ``` 56 | 57 | To specify the ast format type, use `-f` or `--format` option: 58 | 59 | ```shell 60 | everdev sol ast-json Contract.sol -f 61 | ``` 62 | 63 | To point the location of the output folder, use the `-o` or `--output-dir` option: 64 | 65 | ```shell 66 | everdev sol ast-json Contract.sol -f -o path/to/output/file 67 | ``` 68 | 69 | You can make an additional source directory available to the default import callback with `-i` option: 70 | 71 | ```shell 72 | everdev sol ast Contract.sol -i path/to/importFolder 73 | ``` 74 | 75 | ## Version 76 | 77 | This command shows the currently installed Solidity compiler version. 78 | 79 | ```shell 80 | everdev sol version 81 | ``` 82 | 83 | ## Update 84 | 85 | This command updates the compiler and linker to the latest version. 86 | 87 | ```shell 88 | everdev sol update 89 | ``` 90 | 91 | **Attention!** Use --force option to force update of components that do not update their version. 92 | 93 | ## Set 94 | 95 | This command sets the compiler and linker versions and downloads them if needed. 96 | 97 | ```shell 98 | everdev sol set --compiler 0.38.0 --linker 0.23.54 99 | ``` 100 | 101 | **Attention!** Use --force option to force update of components that do not update their version. 102 | -------------------------------------------------------------------------------- /docs/command-line-interface/testsuite4.md: -------------------------------------------------------------------------------- 1 | # TestSuite4 2 | 3 | TestSuite4 is a framework designed to simplify development and testing of TON Contracts. It contains lightweight blockchain emulator making it easy to develop contracts in a TDD-friendly style. 4 | 5 | For more information, visit [TestSuite4's documentation](https://everx-labs.github.io/TestSuite4/). 6 | 7 | :information_source: `Python 3.6 - 3.9` and `pip` required. 8 | 9 | ## Version 10 | 11 | This command shows the currently installed and available TestSuite4 framework versions. 12 | 13 | ``` 14 | everdev ts4 version 15 | ``` 16 | 17 | ## Install 18 | 19 | This command installs (using `pip`) TestSuite4's latest or selected version and downloads them if needed. 20 | 21 | ```bash 22 | everdev ts4 install # install latest version 23 | 24 | everdev ts4 install 0.2.0 # install version 0.2.0 25 | ``` 26 | 27 | ## Update 28 | 29 | This command updates TestSuite4 to the latest version. 30 | 31 | ``` 32 | everdev ts4 update 33 | ``` 34 | 35 | ## Create 36 | 37 | This command creates a TestSuite4's template of the test (`TestName.py`). 38 | 39 | ```bash 40 | everdev ts4 create TestName 41 | 42 | everdev ts4 create TestName --folder tests # creates tests/TestName.py (folder must exist) 43 | ``` 44 | 45 | ## Run 46 | 47 | This command runs selected test (`TestName.py`). 48 | 49 | ``` 50 | everdev ts4 run TestName 51 | ``` 52 | -------------------------------------------------------------------------------- /docs/guides/creating-controller.md: -------------------------------------------------------------------------------- 1 | # Create Controller 2 | 3 | ## Content Table 4 | 5 | - [Create Controller](#create-controller) 6 | - [Content Table](#content-table) 7 | - [What a controller should and what it shouldn't to do](#what-a-controller-should-and-what-it-shouldnt-to-do) 8 | - [How to implement a Controller](#how-to-implement-a-controller) 9 | - [Controller API Reference](#controller-api-reference) 10 | 11 | Currently all the controllers must be implemented inside of the `everdev` package. 12 | 13 | To create a new controller: 14 | 15 | - Clone `git@github.com:everx-labs/everdev.git`. 16 | - Create and checkout branch for the new controller. 17 | - Create a folder under `src/controllers`. 18 | - Implement the controller code. 19 | - Include the new controller in file `src/controllers/index.js`. 20 | - Create pull request. 21 | 22 | ## What a controller should and what it shouldn't to do 23 | 24 | Controller should: 25 | 26 | - Expose functionality to the user as a list of commands. 27 | - Install the required tool components on demand (on first usage). 28 | - Start and stop the tool components that acts like a demons. 29 | - Define three commands to control the tool version: 30 | - `version` to show the currently installed tool version; 31 | - `update` to update the tool to the latest available version; 32 | - `use` to select the specified tool version as the current version. 33 | 34 | Controller shouldn't: 35 | 36 | - Implement tool functionality itself. Controller is a connector between the user and the existing development tool. 37 | - Implement user interaction itself. All user interaction must be implemented exactly in the terms of everdev extensibility. 38 | 39 | ## How to implement a Controller 40 | 41 | Create a folder for a new controller: 42 | 43 | ```shell 44 | mkdir src/controllers/foo 45 | cd src/controllers/foo 46 | ``` 47 | 48 | Create `index.ts` with the controller's definition: 49 | 50 | ```ts 51 | import { ToolController } from "../../core"; 52 | import { versionCommand } from "./version"; 53 | import { updateCommand } from "./update"; 54 | import { runCommand } from "./run"; 55 | 56 | export const Foo: ToolController = { 57 | name: "foo", 58 | title: "Foo Tool", 59 | commands: [ 60 | versionCommand, 61 | updateCommand, 62 | runCommand, 63 | ], 64 | }; 65 | ``` 66 | 67 | Create `installer.ts` to implement all the code related to the tool installation: 68 | 69 | ```ts 70 | import path from "path"; 71 | import fs from "fs"; 72 | import { Terminal, everdevHome } from "../../core"; 73 | 74 | function fooHome() { 75 | return path.resolve(everdevHome(), "foo"); 76 | } 77 | 78 | async function ensureInstalled(terminal: Terminal) { 79 | } 80 | 81 | export async function getVersion(): Promise { 82 | return "1.0.0"; 83 | } 84 | 85 | export async function updateVersion(terminal: Terminal) { 86 | if (fs.existsSync(fooHome())) { 87 | fs.rmdirSync(fooHome(), { recursive: true }); 88 | } 89 | ensureInstalled(terminal); 90 | } 91 | 92 | export async function runFoo(terminal: Terminal, workDir: string, args: string[]): Promise { 93 | ensureInstalled(terminal); 94 | terminal.log("Foo succeeded"); 95 | }; 96 | ``` 97 | 98 | Create `version.ts` command handler: 99 | 100 | ```ts 101 | import { getVersion } from "./installer"; 102 | import { Command, Terminal } from "../../core"; 103 | 104 | export const versionCommand: Command = { 105 | name: "version", 106 | title: "Show Foo Version", 107 | async run(terminal: Terminal, _args: {}): Promise { 108 | terminal.log(await getVersion()); 109 | }, 110 | }; 111 | ``` 112 | 113 | Create `update.ts` command handler: 114 | 115 | ```ts 116 | import { updateVersion } from "./installer"; 117 | import { Command, Terminal } from "../../core"; 118 | 119 | export const versionCommand: Command = { 120 | name: "update", 121 | title: "Update Foo Version", 122 | async run(terminal: Terminal, _args: {}): Promise { 123 | await updateVersion(terminal); 124 | }, 125 | }; 126 | ``` 127 | 128 | Create `run.ts` command handler: 129 | 130 | ```ts 131 | import { runFoo } from "./installer"; 132 | import { Command, Terminal } from "../../core"; 133 | 134 | export const runCommand: Command = { 135 | name: "run", 136 | title: "Run Foo", 137 | async run(terminal: Terminal, args: {}): Promise { 138 | await runFoo(terminal, args); 139 | }, 140 | }; 141 | ``` 142 | 143 | ## Controller API Reference 144 | 145 | You can find API reference in form of TSDoc in `src/core/index.ts`. 146 | -------------------------------------------------------------------------------- /docs/troubleshooting.md: -------------------------------------------------------------------------------- 1 | # Troubleshooting 2 | 3 | Here are some solutions to frequently encountered problems. 4 | 5 | ## EACCESS errors during installation 6 | 7 | These errors can occur, if npm was installed without the use of a version manager. 8 | 9 | Refer to [this article](https://docs.npmjs.com/resolving-eacces-permissions-errors-when-installing-packages-globally) for ways to resolve the issue. 10 | 11 | ## command not found: everdev 12 | 13 | This error may happen because `PATH` environment variable was not correctly updated to contain path to Node.js binary. 14 | 15 | If you use Linux, ensure the following command is in your `~/.bashrc` for bash shell or `~/.zshrc` for zsh shell: 16 | 17 | ``` 18 | export PATH=~/.npm-global/bin:$PATH 19 | ``` 20 | 21 | If you have installed Node.js using Homebrew on MacOS, npm binaries could be found in `/usr/local/share/npm/bin`. So, in your `~/.zshrc` file add the following: 22 | 23 | ``` 24 | export PATH=/usr/local/share/npm/bin:$PATH 25 | ``` 26 | 27 | If you use Windows, add path to NodeJS bin directory via environment variables settings dialogue and relaunch console window. 28 | 29 | Additionally, make sure [permissions are alright](https://docs.npmjs.com/resolving-eacces-permissions-errors-when-installing-packages-globally). 30 | 31 | ## Unspecified Error on `everdev sol compile` in Windows 10 32 | 33 | 1. Run \Users\UserName\everdev\solidity\solc.exe and review error messages. 34 | 2. Update Visual Studio components and make sure [vc\_redist is installed](https://support.microsoft.com/en-us/topic/the-latest-supported-visual-c-downloads-2647da03-1eea-4433-9aff-95f26a218cc0). 35 | 36 | ## Evernode SE: Couldn’t connect to Docker daemon 37 | 38 | This error occurs in two cases. Either the docker daemon isn't running, or current user doesn't have rights to access docker. 39 | 40 | You can [fix the rights](https://docs.docker.com/engine/install/linux-postinstall/) issue either by running relevant commands as the superuser or adding the user to the `docker` group: 41 | 42 | ``` 43 | sudo usermod -a -G docker $USER 44 | ``` 45 | 46 | Make sure to restart the system or log out and back in, for the new group settings to take effect. 47 | 48 | ## Evernode SE Error: connect ENOENT /var/run/docker.sock 49 | 50 | Getting this error means docker service is not running or missing due to incorrect Docker installation, partiularly in the case of Docker Desktop. Try reinstalling Docker and making sure the [daemon](https://docs.docker.com/config/daemon/start/) is running. 51 | 52 | ## Evernode SE: Ever.live at localhost isn't available 53 | 54 | If you use certain adblockers, after you have started Evernode SE the Ever Live explorer at [http://127.0.0.1/landing](http://127.0.0.1/landing) might fail to load (you get a rotating icon and various warnings and errors in the console). 55 | 56 | ## After everdev is installed on Ubuntu WSL on Windows 10 old version is there 57 | 58 | This issue can occur if npm was installed without correct permissions for Linux/Ubuntu. Refer to [this article](https://docs.npmjs.com/resolving-eacces-permissions-errors-when-installing-packages-globally) for ways to resolve it. 59 | 60 | After it is done, reload terminal and install everdev via `npm i everdev -g` again. 61 | -------------------------------------------------------------------------------- /docs/use-in-js-applications.md: -------------------------------------------------------------------------------- 1 | # Use in JS applications 2 | 3 | You can easily use everdev as a regular npm package in your JS applications. 4 | 5 | Just add dependency into you `package.json`: 6 | 7 | ```shell 8 | npm i -s everdev 9 | ``` 10 | 11 | And then run any command from everdev: 12 | 13 | ```js 14 | const { consoleTerminal, runCommand } = require("everdev"); 15 | const path = require("path"); 16 | 17 | async function main() { 18 | await runCommand(consoleTerminal, "sol compile", { 19 | file: path.resolve(__dirname, "Hello.sol") 20 | }); 21 | } 22 | 23 | main(); 24 | ``` 25 | -------------------------------------------------------------------------------- /docs/view-controller-info.md: -------------------------------------------------------------------------------- 1 | # View controller info 2 | 3 | This command displays a summary of all controller configurations. 4 | 5 | ``` 6 | everdev info 7 | ``` 8 | Output example: 9 | 10 | $ everdev info 11 | 12 | ``` 13 | C++ compiler 14 | 15 | Component Version Available 16 | --------- ------- --------- 17 | clang 7.0.0 7.0.0 18 | 19 | Solidity Compiler 20 | 21 | Component Available 22 | --------- ---------------------------------------------- 23 | compiler 0.42.0, 0.41.0, 0.40.0, 0.39.0, 0.38.2, 0.38.1 24 | linker 0.3.0, 0.1.0 25 | stdlib 0.42.0, 0.41.0, 0.40.0, 0.39.0, 0.38.2, 0.38.1 26 | 27 | EVER OS SE 28 | 29 | Instance State Version GraphQL Port Docker Container Docker Image 30 | -------- ------------- ------- ------------ -------------------- ----------------------- 31 | default not installed 0.27 80 tonlabs-tonos-se-test tonlabs/local-node:0.27 32 | 33 | Network Registry 34 | 35 | Network Endpoints Giver 36 | ------------- ----------------------------------------------- ------------------------------------------------------------------ 37 | se http://localhost 0:b5e9240fc2d2f1ff8cbb1d1dee7fb7cae155e5f6320e585fcc685698994a19a5 38 | dev (Default) net.ton.dev, net1.ton.dev, net5.ton.dev 0:255a3ad9dfa8aa4f3481856aafc7d79f47d50205190bd56147138740e9b177f3 39 | main main.ton.dev, main2.ton.dev, main3.ton.dev, ... 40 | 41 | Signer Registry 42 | 43 | Signer Public Key 44 | -------------- ---------------------------------------------------------------- 45 | surf 8534c46f7a135058773fa1298cb3a299a5ddd40dafe41cb06c64f274da360bfb 46 | test (Default) ad4bf7bd8da244932c52127a943bfa9217b6e215c1b3307272283c4d64f34486 47 | test2 5c2e348c5caeb420a863dc5e972f897ebe5ee899a6ef2a8299aac352eca4380a 48 | 49 | EVER OS CLI 50 | 51 | Component Version Available 52 | --------- ------- -------------------------------------------------------------------------------- 53 | tonoscli 0.11.3 0.11.4, 0.11.3, 0.11.2, 0.11.1, 0.11.0, 0.10.1, 0.10.0, 0.9.2, 0.9.1, 0.9.0, ... 54 | ``` 55 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | globals: { 3 | "ts-jest": { 4 | tsconfig: "tsconfig.json", 5 | }, 6 | }, 7 | moduleFileExtensions: ["ts", "js"], 8 | transform: { 9 | "^.+\\.(ts|tsx)$": "ts-jest", 10 | }, 11 | modulePathIgnorePatterns: [".*\\.d\\.ts"], 12 | testMatch: ["**/test/**/*.+(ts)", "**/__tests__/**/*.+(ts|tsx)"], 13 | testEnvironment: "node", 14 | } 15 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "everdev", 3 | "version": "1.7.1", 4 | "description": "Everscale Dev Environment", 5 | "main": "dist/index.js", 6 | "types": "dist/index.d.ts", 7 | "repository": { 8 | "type": "git", 9 | "url": "https://github.com/everx-labs/everdev.git" 10 | }, 11 | "scripts": { 12 | "prepublishOnly": "npm i && tsc", 13 | "everdev": "node ./cli.js", 14 | "format": "prettier --write 'src/**/*.ts'", 15 | "eslint": "eslint \"**/*.ts\"", 16 | "eslint-fix": "eslint \"**/*.ts\" --fix", 17 | "tsc": "tsc", 18 | "build": "rimraf dist && npm run eslint && tsc --project tsconfig.build.json", 19 | "test": "jest -i --forceExit --detectOpenHandles --coverage --verbose" 20 | }, 21 | "bin": { 22 | "everdev": "./cli.js" 23 | }, 24 | "keywords": [ 25 | "Free", 26 | "TON", 27 | "Ever", 28 | "EverScale", 29 | "Freeton", 30 | "Dev", 31 | "Development", 32 | "EVER OS", 33 | "EVEROS" 34 | ], 35 | "author": "EverX Labs Ltd", 36 | "license": "Apache-2.0", 37 | "licenses": [ 38 | { 39 | "type": "Apache-2.0", 40 | "url": "http://www.apache.org/licenses/LICENSE-2.0" 41 | } 42 | ], 43 | "homepage": "https://docs.ton.dev/", 44 | "engines": { 45 | "node": ">=6" 46 | }, 47 | "dependencies": { 48 | "@eversdk/appkit": "^0.3.4", 49 | "@eversdk/core": "^1.41.1", 50 | "@eversdk/lib-node": "^1.41.1", 51 | "chalk": "^2.4.2", 52 | "dockerode": "^3.3.1", 53 | "fs-extra": "^9.1.0", 54 | "mv": "^2.1.1", 55 | "request": "^2.88.2", 56 | "unzip-stream": "^0.3.1" 57 | }, 58 | "devDependencies": { 59 | "@babel/preset-env": "^7.14.7", 60 | "@babel/preset-typescript": "^7.14.5", 61 | "@types/dockerode": "^3.2.2", 62 | "@types/fs-extra": "^9.0.8", 63 | "@types/jest": "^27.4.1", 64 | "@types/mv": "^2.1.1", 65 | "@types/node": "^16.0.1", 66 | "@types/request": "^2.48.5", 67 | "@types/unzip-stream": "^0.3.0", 68 | "@typescript-eslint/eslint-plugin": "^5.19.0", 69 | "@typescript-eslint/parser": "^5.19.0", 70 | "caxa": "^1.0.0", 71 | "eslint": "^7.30.0", 72 | "eslint-config-prettier": "^8.5.0", 73 | "eslint-plugin-jest": "^26.1.4", 74 | "eslint-plugin-prettier": "^4.0.0", 75 | "husky": "^4.3.8", 76 | "jest": "^27.5.1", 77 | "lint-staged": "^12.3.8", 78 | "prettier": "^2.3.2", 79 | "rimraf": "^3.0.2", 80 | "ts-jest": "^27.1.4", 81 | "typescript": "^4.8.4" 82 | }, 83 | "husky": { 84 | "hooks": { 85 | "pre-commit": "lint-staged" 86 | } 87 | }, 88 | "lint-staged": { 89 | "*.ts": [ 90 | "npm run eslint", 91 | "npm run format" 92 | ] 93 | } 94 | } -------------------------------------------------------------------------------- /src/__tests__/addProjectId.ts: -------------------------------------------------------------------------------- 1 | import { transformEndpoint } from "../controllers/contract/accounts" 2 | import { doneTests, initTests } from "./init" 3 | 4 | beforeAll(initTests) 5 | afterAll(doneTests) 6 | 7 | test("Transform different endpoints. Add projectId", async () => { 8 | const endpoints = [ 9 | "eri01.net.everos.dev", 10 | "https://eri01.net.everos.dev/", 11 | "eri01.net.everos.dev/", 12 | "https://eri01.net.everos.dev/graphql/", 13 | "eri01.net.everos.dev/graphql", 14 | ] 15 | const result = endpoints.map(transformEndpoint("myId")) 16 | expect(result[0]).toEqual("eri01.net.everos.dev/myId") 17 | expect(result[1]).toEqual("https://eri01.net.everos.dev/myId") 18 | expect(result[2]).toEqual("eri01.net.everos.dev/myId") 19 | expect(result[3]).toEqual("https://eri01.net.everos.dev/myId") 20 | expect(result[4]).toEqual("eri01.net.everos.dev/myId") 21 | }) 22 | test("Transform different endpoints. Add void projectId", async () => { 23 | const endpoints = [ 24 | "eri01.net.everos.dev", 25 | "https://eri01.net.everos.dev/", 26 | "eri01.net.everos.dev/", 27 | "https://eri01.net.everos.dev/graphql/", 28 | "eri01.net.everos.dev/graphql", 29 | ] 30 | const result = endpoints.map(transformEndpoint(undefined)) 31 | expect(result[0]).toEqual("eri01.net.everos.dev") 32 | expect(result[1]).toEqual("https://eri01.net.everos.dev") 33 | expect(result[2]).toEqual("eri01.net.everos.dev") 34 | expect(result[3]).toEqual("https://eri01.net.everos.dev") 35 | expect(result[4]).toEqual("eri01.net.everos.dev") 36 | }) 37 | -------------------------------------------------------------------------------- /src/__tests__/checkArgs.ts: -------------------------------------------------------------------------------- 1 | import fs from "fs" 2 | import path from "path" 3 | 4 | import { doneTests, initTests, deleteFolderRecursive } from "./init" 5 | import { StringTerminal, consoleTerminal, runCommand } from ".." 6 | 7 | const outPath = path.resolve(__dirname, "..", "..", `${Date.now()}-tmp`) 8 | 9 | beforeAll(initTests) 10 | afterAll(doneTests) 11 | beforeEach(() => deleteFolderRecursive(outPath)) 12 | afterEach(() => deleteFolderRecursive(outPath)) 13 | /* 14 | test("Should create HelloWallet.tvc in user defined output directory", async () => { 15 | await runCommand(consoleTerminal, "sol update", {}) 16 | 17 | const solPath = path.resolve( 18 | __dirname, 19 | "..", 20 | "..", 21 | "contracts", 22 | "HelloWallet.sol", 23 | ) 24 | 25 | const terminal = new StringTerminal() 26 | await runCommand(terminal, "sol compile", { 27 | file: solPath, 28 | outputDir: outPath, 29 | }) 30 | expect(terminal.stderr.trim()).toEqual("") 31 | 32 | const tvcFile = path.resolve(outPath, "HelloWallet.tvc") 33 | expect(fs.existsSync(tvcFile)).toBeTruthy() 34 | }) 35 | */ 36 | test("Should warn user to use options in camelCase", async () => { 37 | const solPath = path.resolve( 38 | __dirname, 39 | "..", 40 | "..", 41 | "contracts", 42 | "HelloWallet.sol", 43 | ) 44 | const terminal = new StringTerminal() 45 | try { 46 | await runCommand(terminal, "sol compile", { 47 | file: solPath, 48 | "hello-world": "hi!", 49 | }) 50 | throw Error("This function didn't throw") 51 | } catch (err: any) { 52 | expect(err.message).toMatch(/Unknow option: hello-world/) 53 | } 54 | }) 55 | -------------------------------------------------------------------------------- /src/__tests__/checkNewVersion.ts: -------------------------------------------------------------------------------- 1 | import { 2 | createLatestVerFile, 3 | getUpdateIsAvailableMsg, 4 | } from "../everdev/checkNewVersion" 5 | import { doneTests, initTests } from "./init" 6 | 7 | beforeAll(initTests) 8 | afterAll(doneTests) 9 | 10 | test("Shoud see message of the day only once", async () => { 11 | await createLatestVerFile("everdev") 12 | const result1 = getUpdateIsAvailableMsg("everdev", "0.1.0") 13 | expect(result1.length).toBeGreaterThan(0) 14 | const result2 = getUpdateIsAvailableMsg("everdev", "0.1.0") 15 | expect(result2.length).toEqual(0) 16 | }) 17 | -------------------------------------------------------------------------------- /src/__tests__/contracts.ts: -------------------------------------------------------------------------------- 1 | import { doneTests, initTests } from "./init" 2 | import path from "path" 3 | import { ParamParser } from "../controllers/contract/param-parser" 4 | 5 | beforeAll(initTests) 6 | afterAll(doneTests) 7 | 8 | function dataPath(name: string): string { 9 | return path.resolve(__dirname, "..", "..", "src", "__tests__", "data", name) 10 | } 11 | 12 | test("Contract alone args file", async () => { 13 | const file = dataPath("contracts-input-alone.json") 14 | const abi = { name: "a", type: "string[]" } 15 | const args = ParamParser.components(abi, `@${file}`) 16 | expect(args).toEqual([1, 2, 3]) 17 | }) 18 | 19 | test("Contract multi args file", async () => { 20 | const file = dataPath("contracts-input.json") 21 | const abi = { name: "a", type: "string[]" } 22 | const args = ParamParser.components(abi, `@${file}@test1`) 23 | expect(args).toEqual({ a: [1, 2, 3] }) 24 | const args2 = ParamParser.components(abi, `@${file}@test2`) 25 | expect(args2).toEqual({ b: [1, 2, 3] }) 26 | const args3 = ParamParser.components(abi, `@${file}@test2.b`) 27 | expect(args3).toEqual([1, 2, 3]) 28 | }) 29 | -------------------------------------------------------------------------------- /src/__tests__/data/contracts-input-alone.json: -------------------------------------------------------------------------------- 1 | [1, 2, 3] 2 | 3 | -------------------------------------------------------------------------------- /src/__tests__/data/contracts-input.json: -------------------------------------------------------------------------------- 1 | { 2 | "test1": { 3 | "a": [1, 2, 3] 4 | }, 5 | "test2": { 6 | "b": [1, 2, 3] 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/__tests__/init.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-empty-function */ 2 | import { everdevInit, everdevDone, everdevHome, writeTextFile } from ".." 3 | import path from "path" 4 | import fs from "fs" 5 | import os from "os" 6 | 7 | jest.setTimeout(500000) 8 | 9 | test("init", () => {}) 10 | 11 | export function tempFolder(): string { 12 | const temp = path.resolve(everdevHome(), "_temp") 13 | if (!fs.existsSync(temp)) { 14 | fs.mkdirSync(temp, { recursive: true }) 15 | } 16 | return temp 17 | } 18 | 19 | export function writeTempText(name: string, text: string): string { 20 | const filePath = path.resolve(tempFolder(), name) 21 | writeTextFile(filePath, text) 22 | return filePath 23 | } 24 | 25 | export function copyToTemp(source: string): string { 26 | const filePath = path.resolve(tempFolder(), path.basename(source)) 27 | fs.copyFileSync(source, filePath) 28 | return filePath 29 | } 30 | 31 | export function writeTempJson(name: string, json: unknown): string { 32 | return writeTempText(name, JSON.stringify(json, undefined, " ")) 33 | } 34 | 35 | export function deleteFiles(files: string[]) { 36 | files.forEach(file => { 37 | if (fs.existsSync(file)) { 38 | fs.unlinkSync(file) 39 | } 40 | }) 41 | } 42 | 43 | export function initTests() { 44 | const home = path.resolve(os.homedir(), ".everdev_test") 45 | everdevInit({ 46 | home, 47 | }) 48 | deleteFolderRecursive(path.resolve(home, "_temp")) 49 | deleteFiles([ 50 | path.resolve(home, "signer", "registry.json"), 51 | path.resolve(home, "network", "registry.json"), 52 | path.resolve(home, "se", "config.json"), 53 | path.resolve(home, ".lastrun"), 54 | path.resolve(home, ".latest"), 55 | ]) 56 | } 57 | 58 | export function doneTests() { 59 | everdevDone() 60 | } 61 | 62 | export function deleteFolderRecursive(directoryPath: string) { 63 | if (fs.existsSync(directoryPath)) { 64 | fs.readdirSync(directoryPath).forEach(file => { 65 | const curPath = path.join(directoryPath, file) 66 | if (fs.lstatSync(curPath).isDirectory()) { 67 | deleteFolderRecursive(curPath) 68 | } else { 69 | fs.unlinkSync(curPath) 70 | } 71 | }) 72 | fs.rmdirSync(directoryPath) 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/__tests__/network.ts: -------------------------------------------------------------------------------- 1 | import { doneTests, initTests } from "./init" 2 | import { runCommand, consoleTerminal } from ".." 3 | import { NetworkRegistry } from "../controllers/network/registry" 4 | import { SignerRegistry } from "../controllers/signer/registry" 5 | 6 | import * as knownContracts from "../core/known-contracts" 7 | 8 | beforeAll(async () => { 9 | await initTests() 10 | await new SignerRegistry().addSecretKey( 11 | "alice", 12 | "", 13 | "172af540e43a524763dd53b26a066d472a97c4de37d5498170564510608250c3", 14 | true, 15 | ) 16 | }) 17 | afterAll(doneTests) 18 | 19 | test("Add network giver by address", async () => { 20 | const mock = jest.spyOn(knownContracts, "knownContractFromAddress") 21 | mock.mockReturnValue( 22 | Promise.resolve(knownContracts.KnownContracts["GiverV2"]), 23 | ) 24 | await runCommand(consoleTerminal, "network giver", { 25 | name: "se", 26 | address: 27 | "0:b5e9240fc2d2f1ff8cbb1d1dee7fb7cae155e5f6320e585fcc685698994a19a5", 28 | type: "GiverV2", 29 | signer: "alice", 30 | }) 31 | expect(new NetworkRegistry().get("se").giver?.name).toEqual("GiverV2") 32 | }) 33 | 34 | test("Add network giver by type", async () => { 35 | await runCommand(consoleTerminal, "network giver", { 36 | name: "se", 37 | type: "GiverV2", 38 | signer: "alice", 39 | }) 40 | expect(new NetworkRegistry().get("se").giver?.name).toEqual("GiverV2") 41 | 42 | await runCommand(consoleTerminal, "network giver", { 43 | name: "se", 44 | type: "SafeMultisigWallet", 45 | signer: "alice", 46 | }) 47 | expect(new NetworkRegistry().get("se").giver?.name).toEqual( 48 | "SafeMultisigWallet", 49 | ) 50 | await runCommand(consoleTerminal, "network giver", { 51 | name: "se", 52 | type: "MsigV2", 53 | signer: "alice", 54 | }) 55 | expect(new NetworkRegistry().get("se").giver?.name).toEqual("MsigV2") 56 | }) 57 | 58 | test("Add network giver error", async () => { 59 | try { 60 | await runCommand(consoleTerminal, "network giver", { 61 | name: "se", 62 | type: "NotExist", 63 | signer: "alice", 64 | }) 65 | expect(true).toBe(false) 66 | } catch (error: any) { 67 | expect(error.message).toBe( 68 | "Can not resolve giver address: unknown giver type NotExist.", 69 | ) 70 | } 71 | }) 72 | 73 | test("Add credentials", async () => { 74 | await runCommand(consoleTerminal, "network credentials", { 75 | name: "se", 76 | project: "pro123", 77 | accessKey: "key456", 78 | }) 79 | const registry = new NetworkRegistry().get("se") 80 | expect(registry.credentials?.project).toEqual("pro123") 81 | expect(registry.credentials?.accessKey).toEqual("key456") 82 | }) 83 | 84 | test("Clear credentials", async () => { 85 | await runCommand(consoleTerminal, "network credentials", { 86 | name: "se", 87 | clear: true, 88 | }) 89 | const registry = new NetworkRegistry().get("se") 90 | expect(registry.credentials?.project).toBeUndefined() 91 | }) 92 | -------------------------------------------------------------------------------- /src/__tests__/parser.ts: -------------------------------------------------------------------------------- 1 | import { CommandLine } from "../cli" 2 | import { doneTests, initTests } from "./init" 3 | import { 4 | Solidity, 5 | solidityCompileCommand, 6 | solidityCreateCommand, 7 | } from "../controllers/solidity" 8 | 9 | import { NetworkTool, networkGiverCommand } from "../controllers/network" 10 | beforeAll(initTests) 11 | afterAll(doneTests) 12 | 13 | test("Should throw", async () => { 14 | const parser = new CommandLine() 15 | try { 16 | await parser.parse(["nono"]) 17 | } catch (err) { 18 | expect(err.message).toMatch(/Unknown tool/) 19 | } 20 | }) 21 | 22 | test("everdev --help", async () => { 23 | const parser = new CommandLine() 24 | await parser.parse(["--help"]) 25 | expect(parser.args.help).toBe(true) 26 | }) 27 | 28 | test("everdev sol", async () => { 29 | const parser = new CommandLine() 30 | await parser.parse(["sol"]) 31 | const { controller, command, args } = parser 32 | expect(controller).toEqual(Solidity) 33 | expect(command).toBeUndefined() 34 | expect(args).toEqual({}) 35 | }) 36 | 37 | test("everdev sol compile, should throw", async () => { 38 | const parser = new CommandLine() 39 | try { 40 | await parser.parse(["sol", "compile"]) 41 | } catch (err) { 42 | expect(err.message).toMatch(/Missing required file/) 43 | } 44 | }) 45 | 46 | test("everdev sol compile a.sol", async () => { 47 | const parser = new CommandLine() 48 | await parser.parse(["sol", "compile", "a.sol"]) 49 | const { controller, command, args } = parser 50 | expect(controller).toEqual(Solidity) 51 | expect(command).toEqual(solidityCompileCommand) 52 | expect(args).toEqual({ 53 | code: false, 54 | file: "a.sol", 55 | basePath: ".", 56 | includePath: "node_modules", 57 | outputDir: "", 58 | }) 59 | }) 60 | 61 | test("everdev sol compile a.sol b.sol --output-dir somedir", async () => { 62 | const parser = new CommandLine() 63 | await parser.parse([ 64 | "sol", 65 | "compile", 66 | "a.sol", 67 | "b.sol", 68 | "--output-dir", 69 | "somedir", 70 | ]) 71 | const { controller, command, args } = parser 72 | expect(controller).toEqual(Solidity) 73 | expect(command).toEqual(solidityCompileCommand) 74 | expect(args).toEqual({ 75 | code: false, 76 | file: "a.sol b.sol", 77 | basePath: ".", 78 | includePath: "node_modules", 79 | outputDir: "somedir", 80 | }) 81 | }) 82 | 83 | test("everdev create a.sol", async () => { 84 | const parser = new CommandLine() 85 | await parser.parse(["sol", "create", "a.sol"]) 86 | const { controller, command, args } = parser 87 | expect(controller).toEqual(Solidity) 88 | expect(command).toEqual(solidityCreateCommand) 89 | expect(args).toEqual({ 90 | name: "a.sol", 91 | folder: process.cwd(), 92 | }) 93 | }) 94 | 95 | test("everdev create a.sol b.sol, should throw", async () => { 96 | const parser = new CommandLine() 97 | try { 98 | await parser.parse(["sol", "create", "a.sol", "b.sol"]) 99 | throw Error("Shouldn't resolve!") 100 | } catch (err) { 101 | expect(err.message).toMatch(/Unexpected argument/) 102 | } 103 | }) 104 | 105 | test("everdev network add giver with address in negative workchain", async () => { 106 | const parser = new CommandLine() 107 | await parser.parse([ 108 | "network", 109 | "giver", 110 | "networkName", 111 | "-2:1111111111111111111111111111111111111111111111111111111111111111", 112 | "-s", 113 | "seGiver", 114 | "-t", 115 | "GiverV1", 116 | ]) 117 | const { controller, command, args } = parser 118 | expect(controller).toEqual(NetworkTool) 119 | expect(command).toEqual(networkGiverCommand) 120 | expect(args).toEqual({ 121 | name: "networkName", 122 | address: 123 | "-2:1111111111111111111111111111111111111111111111111111111111111111", 124 | signer: "seGiver", 125 | type: "GiverV1", 126 | value: "", 127 | }) 128 | }) 129 | 130 | test("everdev network add giver with address in positive workchain", async () => { 131 | const parser = new CommandLine() 132 | await parser.parse([ 133 | "network", 134 | "giver", 135 | "networkName", 136 | "0:1111111111111111111111111111111111111111111111111111111111111111", 137 | "-s", 138 | "seGiver", 139 | "-t", 140 | "GiverV1", 141 | ]) 142 | const { controller, command, args } = parser 143 | expect(controller).toEqual(NetworkTool) 144 | expect(command).toEqual(networkGiverCommand) 145 | expect(args).toEqual({ 146 | name: "networkName", 147 | address: 148 | "0:1111111111111111111111111111111111111111111111111111111111111111", 149 | signer: "seGiver", 150 | type: "GiverV1", 151 | value: "", 152 | }) 153 | }) 154 | 155 | test("everdev network add giver with malformed address in negative workchain", async () => { 156 | const parser = new CommandLine() 157 | try { 158 | await parser.parse([ 159 | "network", 160 | "giver", 161 | "networkName", 162 | "-1w:123", 163 | "-s", 164 | "seGiver", 165 | "-t", 166 | "GiverV1", 167 | ]) 168 | throw Error("Shouldn't resolve!") 169 | } catch (err) { 170 | expect(err.message).toMatch(/Unknown option/) 171 | } 172 | }) 173 | -------------------------------------------------------------------------------- /src/__tests__/se.ts: -------------------------------------------------------------------------------- 1 | import { doneTests, initTests } from "./init" 2 | import { runCommand } from "../controllers" 3 | import { consoleTerminal } from "../core/utils" 4 | 5 | beforeAll(initTests) 6 | afterAll(doneTests) 7 | 8 | test("Use custom docker image", async () => { 9 | await runCommand(consoleTerminal, "se set", { 10 | instance: "se1", 11 | image: "tonlabs/local-node:0.28.1", 12 | }) 13 | }) 14 | -------------------------------------------------------------------------------- /src/__tests__/signer.ts: -------------------------------------------------------------------------------- 1 | import { doneTests, initTests, writeTempJson } from "./init" 2 | import { consoleTerminal } from ".." 3 | import { signerAddCommand } from "../controllers/signer" 4 | import { SignerRegistry } from "../controllers/signer/registry" 5 | import { TonClient } from "@eversdk/core" 6 | 7 | beforeAll(initTests) 8 | afterAll(doneTests) 9 | 10 | test("Add signer with secret key", async () => { 11 | const keys = await TonClient.default.crypto.generate_random_sign_keys() 12 | await signerAddCommand.run(consoleTerminal, { 13 | name: "from_secret_key", 14 | secret: keys.secret, 15 | }) 16 | expect(new SignerRegistry().get("from_secret_key").keys).toEqual(keys) 17 | }) 18 | 19 | test("Add signer with keys file", async () => { 20 | const keys = await TonClient.default.crypto.generate_random_sign_keys() 21 | const keysPath = writeTempJson("keys.json", keys) 22 | await signerAddCommand.run(consoleTerminal, { 23 | name: "from_keys_file", 24 | secret: keysPath, 25 | }) 26 | expect(new SignerRegistry().get("from_keys_file").keys).toEqual(keys) 27 | }) 28 | 29 | test("Add signer with the same name should throw", async () => { 30 | const keys = await TonClient.default.crypto.generate_random_sign_keys() 31 | const keysPath = writeTempJson("keys.json", keys) 32 | expect(() => 33 | signerAddCommand.run(consoleTerminal, { 34 | name: "from_keys_file", 35 | secret: keysPath, 36 | }), 37 | ).rejects.toThrow(/Signer with name "from_keys_file" already exists/) 38 | }) 39 | 40 | test("Add signer with the same name and 'force' option", async () => { 41 | const keys = await TonClient.default.crypto.generate_random_sign_keys() 42 | const keysPath = writeTempJson("keys.json", keys) 43 | await signerAddCommand.run(consoleTerminal, { 44 | name: "from_keys_file", 45 | secret: keysPath, 46 | force: true, 47 | }) 48 | expect(new SignerRegistry().get("from_keys_file").keys).toEqual(keys) 49 | }) 50 | 51 | test("Add invalid keys from file", async () => { 52 | const keysPath = writeTempJson("keys.json", { 53 | public: "a1855789cdab95ba2776450aa0215f6a1f9442770e30d17c1d3589a3ef08d66b", 54 | secret: "0af4fb5e63d53211cb112512sb", 55 | }) 56 | expect(() => 57 | signerAddCommand.run(consoleTerminal, { 58 | name: "from_keys_file", 59 | secret: keysPath, 60 | }), 61 | ).rejects.toThrow(/Invalid keys file/) 62 | }) 63 | -------------------------------------------------------------------------------- /src/__tests__/sol.ts: -------------------------------------------------------------------------------- 1 | import { copyToTemp, doneTests, initTests } from "./init" 2 | import { consoleTerminal, runCommand, StringTerminal } from ".." 3 | import path from "path" 4 | import fs from "fs" 5 | 6 | beforeAll(initTests) 7 | afterAll(doneTests) 8 | /* 9 | test("Hide linker output", async () => { 10 | await runCommand(consoleTerminal, "sol update", {}) 11 | const terminal = new StringTerminal() 12 | const solPath = copyToTemp( 13 | path.resolve(__dirname, "..", "..", "contracts", "HelloWallet.sol"), 14 | ) 15 | await runCommand(terminal, "sol compile", { 16 | file: solPath, 17 | }) 18 | expect(terminal.stdout.trim()).toEqual("") 19 | }) 20 | */ 21 | test("AST of all source files in a JSON", async () => { 22 | await runCommand(consoleTerminal, "sol update", {}) 23 | const terminal = new StringTerminal() 24 | const solPath = path.resolve( 25 | __dirname, 26 | "..", 27 | "..", 28 | "contracts", 29 | "HelloWallet.sol", 30 | ) 31 | const astPath = path.resolve( 32 | __dirname, 33 | "..", 34 | "..", 35 | "HelloWallet.sol_json.ast", 36 | ) 37 | // Remove output file if exists 38 | try { 39 | fs.unlinkSync(astPath) 40 | } catch (_) {} 41 | 42 | await runCommand(terminal, "sol ast --format json", { 43 | file: solPath, 44 | }) 45 | const ast = fs.readFileSync(astPath, "utf-8") 46 | expect(ast.length).toBeGreaterThan(0) 47 | expect(typeof JSON.parse(ast)).toEqual("object") 48 | }) 49 | -------------------------------------------------------------------------------- /src/__tests__/wrap.ts: -------------------------------------------------------------------------------- 1 | import path from "path" 2 | 3 | import { doneTests, initTests, deleteFiles } from "./init" 4 | import { StringTerminal, runCommand } from ".." 5 | 6 | beforeAll(initTests) 7 | afterAll(doneTests) 8 | /* 9 | test("Shoud create HelloWallet.abi.json file", async () => { 10 | await runCommand(consoleTerminal, "sol update", {}) 11 | const solPath = path.resolve( 12 | __dirname, 13 | "..", 14 | "..", 15 | "contracts", 16 | "HelloWallet.sol", 17 | ) 18 | const terminal = new StringTerminal() 19 | await runCommand(terminal, "sol compile", { 20 | file: solPath, 21 | }) 22 | expect(terminal.stderr.trim()).toEqual("") 23 | }) 24 | test("Shoud create HelloWalletContract.js file", async () => { 25 | const wrappedJs = path.resolve( 26 | __dirname, 27 | "..", 28 | "..", 29 | "HelloWalletContract.js", 30 | ) 31 | deleteFiles([wrappedJs]) 32 | 33 | const terminal = new StringTerminal() 34 | await runCommand(terminal, "js wrap", { 35 | file: path.resolve(__dirname, "..", "..", "HelloWallet.abi.json"), 36 | }) 37 | expect(terminal.stderr.trim()).toEqual("") 38 | 39 | const { HelloWalletContract } = await import(wrappedJs) 40 | expect(typeof HelloWalletContract).toEqual("object") 41 | 42 | deleteFiles([wrappedJs]) 43 | }) 44 | */ 45 | -------------------------------------------------------------------------------- /src/controllers/clang/components.ts: -------------------------------------------------------------------------------- 1 | import * as os from "os" 2 | import { Component } from "../../core" 3 | 4 | const p = os.platform() 5 | let innerPath: string 6 | let targetName: string 7 | let ext: string 8 | 9 | if (p === "linux") { 10 | innerPath = "opt/work/llvm/install/bin/clang" 11 | targetName = "clang.tar" 12 | ext = p + ".tar.gz" 13 | } else if (p === "darwin") { 14 | innerPath = "bin/clang" 15 | targetName = "clang.zip" 16 | ext = p + ".zip" 17 | } else { 18 | innerPath = "bin/clang.exe" 19 | targetName = "clang.zip" 20 | ext = p + ".zip" 21 | } 22 | 23 | const TOOL_FOLDER_NAME = "clang" 24 | 25 | export const components = { 26 | compiler: new (class extends Component { 27 | getSourceName(version: string): string { 28 | return `${this.name}/${this.name}-${version 29 | .split(".") 30 | .join("_")}-${ext}` 31 | } 32 | 33 | async resolveVersion(downloadedVersion: string): Promise { 34 | return downloadedVersion 35 | } 36 | })(TOOL_FOLDER_NAME, "clang-for-tvm", { 37 | targetName, 38 | innerPath, 39 | isExecutable: true, 40 | }), 41 | } 42 | -------------------------------------------------------------------------------- /src/controllers/clang/index.ts: -------------------------------------------------------------------------------- 1 | import { Command, Component, Terminal, ToolController } from "../../core" 2 | import path from "path" 3 | import { changeExt, uniqueFilePath, writeTextFile } from "../../core/utils" 4 | import fs from "fs" 5 | import { BasicContractCode, BasicContractHeaders } from "./snippets" 6 | import { components } from "./components" 7 | 8 | export const clangVersionCommand: Command = { 9 | name: "version", 10 | title: "Show C++ Compiler Version", 11 | async run(terminal: Terminal): Promise { 12 | terminal.log(await Component.getInfoAll(components)) 13 | }, 14 | } 15 | 16 | export const clangCreateCommand: Command = { 17 | name: "create", 18 | title: "Create C++ Contract", 19 | args: [ 20 | { 21 | isArg: true, 22 | name: "name", 23 | title: "Contract Name", 24 | type: "string", 25 | defaultValue: "Contract", 26 | }, 27 | { 28 | name: "folder", 29 | type: "folder", 30 | title: "Target folder (should exist, current by default)", 31 | }, 32 | ], 33 | async run(terminal: Terminal, args: { name: string; folder: string }) { 34 | // filename was entered with an extension, delete it 35 | args.name = args.name.replace(/.cpp$/, "") 36 | 37 | const hFilePath = uniqueFilePath(args.folder, `${args.name}{}.hpp`) 38 | const cFilePath = uniqueFilePath(args.folder, `${args.name}{}.cpp`) 39 | 40 | writeTextFile(hFilePath, BasicContractHeaders) 41 | writeTextFile( 42 | cFilePath, 43 | BasicContractCode.split("{name}").join(hFilePath), 44 | ) 45 | terminal.log(`${hFilePath} created.`) 46 | terminal.log(`${cFilePath} created.`) 47 | }, 48 | } 49 | 50 | export const clangCompileCommand: Command = { 51 | name: "compile", 52 | title: "Compile C++ Contract", 53 | args: [ 54 | { 55 | isArg: true, 56 | name: "file", 57 | type: "file", 58 | title: "Source file", 59 | nameRegExp: /\.cpp$/i, 60 | greedy: true, 61 | }, 62 | ], 63 | async run(terminal: Terminal, args: { file: string }): Promise { 64 | for (let file of args.file.split(" ")) { 65 | const ext = path.extname(file) 66 | if (ext !== ".cpp") { 67 | terminal.log(`Choose source file.`) 68 | return 69 | } 70 | await Component.ensureInstalledAll(terminal, components) 71 | file = path.resolve(file) 72 | const tvcName = changeExt(file, ".tvc") 73 | const generatedAbiName = changeExt(file, ".abi") 74 | const renamedAbiName = changeExt(file, ".abi.json") 75 | await components.compiler.run( 76 | terminal, 77 | path.dirname(file), // cd to this directory 78 | [file, "-o", tvcName], 79 | ) 80 | 81 | fs.renameSync(generatedAbiName, renamedAbiName) 82 | terminal.log( 83 | `Success, files created: ${tvcName}, ${renamedAbiName}`, 84 | ) 85 | } 86 | }, 87 | } 88 | 89 | export const clangUpdateCommand: Command = { 90 | name: "update", 91 | title: "Update C++ Compiler", 92 | args: [ 93 | { 94 | name: "force", 95 | alias: "f", 96 | title: "Force reinstall even if up to date", 97 | type: "boolean", 98 | defaultValue: "false", 99 | }, 100 | ], 101 | async run(terminal: Terminal, args: { force: boolean }): Promise { 102 | await Component.updateAll(terminal, args.force, components) 103 | }, 104 | } 105 | 106 | export const clangSetCommand: Command = { 107 | name: "set", 108 | title: "Change C++ Compiler Config", 109 | args: [ 110 | { 111 | name: "compiler", 112 | alias: "c", 113 | title: "Compiler version (version number or `latest`)", 114 | type: "string", 115 | defaultValue: "", 116 | }, 117 | { 118 | name: "force", 119 | alias: "f", 120 | title: "Force reinstall even if up to date", 121 | type: "boolean", 122 | defaultValue: "false", 123 | }, 124 | ], 125 | async run( 126 | terminal: Terminal, 127 | args: { 128 | force: boolean 129 | compiler: string 130 | }, 131 | ): Promise { 132 | const versions: { 133 | compiler?: string 134 | } = {} 135 | if (args.compiler !== "") { 136 | versions.compiler = args.compiler 137 | } 138 | await Component.setVersions(terminal, args.force, components, versions) 139 | }, 140 | } 141 | 142 | export const Clang: ToolController = { 143 | name: "clang", 144 | title: "C++ compiler", 145 | commands: [ 146 | clangCreateCommand, 147 | clangCompileCommand, 148 | clangVersionCommand, 149 | clangSetCommand, 150 | clangUpdateCommand, 151 | ], 152 | } 153 | -------------------------------------------------------------------------------- /src/controllers/clang/snippets.ts: -------------------------------------------------------------------------------- 1 | const header = ` 2 | /** 3 | * This file was generated by EverDev. 4 | * EverDev is a part of EVER OS (see https://everos.dev). 5 | */ 6 | ` 7 | export const BasicContractCode = 8 | header + 9 | ` 10 | #include "{name}" 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | using namespace tvm; 18 | using namespace schema; 19 | 20 | static constexpr unsigned TIMESTAMP_DELAY = 1800; 21 | 22 | class Proxy final : public smart_interface, public DProxy { 23 | using replay_protection_t = replay_attack_protection::timestamp; 24 | using data = DProxy; 25 | public: 26 | struct error_code : tvm::error_code { 27 | static constexpr unsigned message_sender_is_not_my_owner = 100; 28 | }; 29 | 30 | __always_inline 31 | void constructor(uint256 pubkey) { 32 | owner_ = pubkey; 33 | } 34 | 35 | __always_inline 36 | void sendMessage(cell msg, uint8 flags) { 37 | require(msg_pubkey() == owner_, error_code::message_sender_is_not_my_owner); 38 | tvm_accept(); 39 | tvm_sendmsg(msg, flags.get()); 40 | } 41 | // =============== Support functions ================== 42 | DEFAULT_SUPPORT_FUNCTIONS(IProxy, replay_protection_t) 43 | 44 | // default processing of unknown messages 45 | __always_inline static int _fallback(cell msg, slice msg_body) { 46 | return 0; 47 | } 48 | }; 49 | 50 | DEFINE_JSON_ABI(IProxy, DProxy, EProxy); 51 | 52 | // ----------------------------- Main entry functions ---------------------- // 53 | DEFAULT_MAIN_ENTRY_FUNCTIONS(Proxy, IProxy, DProxy, TIMESTAMP_DELAY) 54 | ` 55 | export const BasicContractHeaders = 56 | header + 57 | ` 58 | #pragma once 59 | 60 | #include 61 | #include 62 | #include 63 | 64 | namespace tvm { inline namespace schema { 65 | 66 | __interface IProxy { 67 | 68 | [[external, dyn_chain_parse]] 69 | void constructor(uint256 pubkey) = 1; 70 | 71 | [[external, noaccept, dyn_chain_parse]] 72 | void sendMessage(cell msg, uint8 flags) = 2; 73 | }; 74 | using IProxyPtr = handle; 75 | 76 | struct DProxy { 77 | uint256 owner_; 78 | }; 79 | 80 | __interface EProxy { 81 | }; 82 | 83 | }} // namespace tvm::schema 84 | ` 85 | -------------------------------------------------------------------------------- /src/controllers/contract/accounts.ts: -------------------------------------------------------------------------------- 1 | import { Terminal } from "../../core" 2 | import { Account, AccountOptions, AccountType } from "@eversdk/appkit" 3 | import { NetworkRegistry } from "../network/registry" 4 | import { TonClient } from "@eversdk/core" 5 | import { SignerRegistry } from "../signer/registry" 6 | import { ParamParser } from "./param-parser" 7 | import { resolveContract } from "../../core/solFileResolvers" 8 | 9 | // Remove suffix graphql if exists and add projectId 10 | // Intentionally do not use URL object or any modules, 11 | // because url may lack `http://` prefix 12 | export const transformEndpoint = (project?: string) => (url: string) => { 13 | const result = url 14 | .trim() 15 | .replace(/\/graphql\/?$/i, "") 16 | .replace(/\/$/, "") 17 | return project ? `${result}/${project}` : result 18 | } 19 | 20 | export async function getAccount( 21 | terminal: Terminal, 22 | args: { 23 | file: string 24 | network: string 25 | signer: string 26 | data: string 27 | address?: string 28 | workchain?: string 29 | }, 30 | ): Promise { 31 | const address = args.address ?? "" 32 | const abiConfig = 33 | address === "" && args.workchain 34 | ? { 35 | abi: { 36 | workchain: parseInt(args.workchain) || 0, 37 | }, 38 | } 39 | : {} 40 | const network = new NetworkRegistry().get(args.network) 41 | const { project, accessKey } = network.credentials || {} 42 | const client = new TonClient({ 43 | network: { 44 | endpoints: network.endpoints.map(transformEndpoint(project)), 45 | 46 | ...(accessKey ? { access_key: accessKey } : {}), 47 | }, 48 | ...abiConfig, 49 | }) 50 | const contract = 51 | args.file !== "" 52 | ? resolveContract(args.file) 53 | : { package: { abi: {} }, abiPath: "" } 54 | const signers = new SignerRegistry() 55 | const signerItem = await signers.resolveItem(args.signer, { 56 | useNoneForEmptyName: address !== "", 57 | }) 58 | const options: AccountOptions = { 59 | signer: await signers.createSigner(signerItem), 60 | client, 61 | } 62 | const abiData = contract.package.abi.data ?? [] 63 | if (abiData.length > 0 && args.data !== "") { 64 | options.initData = ParamParser.components( 65 | { 66 | name: "data", 67 | type: "tuple", 68 | components: abiData, 69 | }, 70 | args.data, 71 | ) 72 | } 73 | if (address !== "") { 74 | options.address = address 75 | } 76 | const account = new Account(contract.package, options) 77 | terminal.log("\nConfiguration\n") 78 | terminal.log( 79 | ` Network: ${network.name} (${NetworkRegistry.getEndpointsSummary( 80 | network, 81 | )})`, 82 | ) 83 | terminal.log( 84 | ` Signer: ${ 85 | signerItem 86 | ? `${signerItem.name} (public ${signerItem.keys.public})` 87 | : "None" 88 | }\n`, 89 | ) 90 | if (address === "" && abiData.length > 0 && !options.initData) { 91 | terminal.log( 92 | `Address: Can't calculate address: additional deploying data required.`, 93 | ) 94 | } else { 95 | terminal.log( 96 | `Address: ${await account.getAddress()}${ 97 | address === "" ? " (calculated from TVC and signer public)" : "" 98 | }`, 99 | ) 100 | } 101 | return account 102 | } 103 | 104 | export type ParsedAccount = { 105 | acc_type: AccountType 106 | code_hash?: string 107 | [name: string]: unknown 108 | } 109 | 110 | export async function getParsedAccount( 111 | account: Account, 112 | ): Promise { 113 | try { 114 | return await account.getAccount() 115 | } catch (err) { 116 | const acc = await account.client.net.query_collection({ 117 | collection: "accounts", 118 | filter: { id: { eq: await account.getAddress() } }, 119 | result: "acc_type", 120 | limit: 1, 121 | }) 122 | if (acc.result.length === 0) { 123 | return { 124 | acc_type: AccountType.nonExist, 125 | } 126 | } 127 | throw err 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /src/controllers/contract/param-parser.ts: -------------------------------------------------------------------------------- 1 | import { AbiParam } from "@eversdk/core" 2 | import path from "path" 3 | import process from "process" 4 | import { loadJSON } from "../../core/utils" 5 | 6 | export class ParamParser { 7 | pos = 0 8 | 9 | private constructor(public text: string) {} 10 | 11 | static scalar(param: AbiParam, text: string): any { 12 | const parser = new ParamParser(text) 13 | 14 | return parser.parseScalar(param) 15 | } 16 | 17 | static components(param: AbiParam, text: string): any { 18 | const parser = new ParamParser(text) 19 | 20 | return parser.parseComponents(param) 21 | } 22 | 23 | hasMore() { 24 | return this.pos < this.text.length 25 | } 26 | 27 | nextIs(test: (c: string) => boolean): boolean { 28 | return this.hasMore() && test(this.text[this.pos]) 29 | } 30 | 31 | passIf(test: (c: string) => boolean): boolean { 32 | if (this.nextIs(test)) { 33 | this.pos += 1 34 | return true 35 | } 36 | return false 37 | } 38 | 39 | pass(c: string): boolean { 40 | return this.passIf(x => x === c) 41 | } 42 | 43 | passWhile(test: (c: string) => boolean): string | undefined { 44 | const savePos = this.pos 45 | 46 | while (this.passIf(test)) {} /* eslint-disable-line no-empty */ 47 | return this.pos > savePos 48 | ? this.text.substring(savePos, this.pos) 49 | : undefined 50 | } 51 | 52 | expectIf( 53 | test: (c: string) => boolean, 54 | param: AbiParam, 55 | expectMessage: string, 56 | ): string { 57 | const passed = this.passWhile(test) 58 | 59 | if (passed !== undefined) { 60 | return passed 61 | } 62 | throw this.error( 63 | `Param ${param.name} (${param.type}) expect ${expectMessage}`, 64 | ) 65 | } 66 | 67 | expect(test: string, param: AbiParam): string { 68 | return this.expectIf(x => x === test, param, `"${test}"`) 69 | } 70 | 71 | parseScalar(param: AbiParam): any { 72 | const isScalarChar = (x: string) => 73 | x !== "," && x !== ":" && x !== "[" && x !== "]" 74 | let quote = "" 75 | 76 | if (this.pass('"')) { 77 | quote = '"' 78 | } else if (this.pass("'")) { 79 | quote = "'" 80 | } 81 | if (quote !== "") { 82 | const value = this.passWhile(x => x !== quote) ?? "" 83 | this.expect(quote, param) 84 | return value 85 | } 86 | if (!this.nextIs(isScalarChar)) { 87 | return "" 88 | } 89 | return this.expectIf(isScalarChar, param, "value") 90 | } 91 | 92 | parseArray(param: AbiParam): any[] { 93 | const item = JSON.parse(JSON.stringify(param)) as AbiParam 94 | const value = [] 95 | 96 | item.type = param.type.slice(0, -2) 97 | this.expect("[", param) 98 | while (!this.pass("]")) { 99 | value.push(this.parseParam(item)) 100 | this.pass(",") 101 | } 102 | return value 103 | } 104 | 105 | parseParam(param: AbiParam): any { 106 | if (param.type.endsWith("[]")) { 107 | return this.parseArray(param) 108 | } else { 109 | return this.parseScalar(param) 110 | } 111 | } 112 | 113 | parseComponents(param: AbiParam): { [name: string]: any } { 114 | const text = this.text.trim() 115 | if (text.startsWith("@")) { 116 | return loadJSON(path.resolve(process.cwd(), text.substring(1))) 117 | } 118 | 119 | if (text.startsWith("{") && text.endsWith("}")) { 120 | try { 121 | return JSON.parse(text) 122 | } catch (err: any) { 123 | throw new Error(`Malformed JSON object has been passed`) 124 | } 125 | } 126 | 127 | const isLetter = (x: string) => x.toLowerCase() !== x.toUpperCase() 128 | const isDigit = (x: string) => x >= "0" && x <= "9" 129 | const isIdent = (x: string) => isLetter(x) || isDigit(x) || x === "_" 130 | const components = param.components ?? [] 131 | const value: { [name: string]: any } = {} 132 | 133 | while (this.hasMore()) { 134 | const name = this.expectIf(isIdent, param, "name") 135 | this.expect(":", param) 136 | const component = components.find( 137 | x => x.name.toLowerCase() === name.toLowerCase(), 138 | ) 139 | if (!component) { 140 | throw this.error(`Unknown field ${name}`) 141 | } 142 | if (param.name in value) { 143 | throw new Error(`Field "${name}" already defined.`) 144 | } 145 | value[name] = this.parseParam(component) 146 | if (this.hasMore()) { 147 | this.expect(",", param) 148 | } 149 | } 150 | return value 151 | } 152 | 153 | private error(message: string) { 154 | const text = this.text 155 | const pos = this.pos 156 | const start = Math.max(pos - 12, 0) 157 | const end = Math.min(pos + 12, text.length) 158 | const prefix = start > 0 ? "..." : "" 159 | const suffix = end < text.length ? "..." : "" 160 | const context = `"${prefix}${text.substring( 161 | start, 162 | pos, 163 | )} -> ${text.substring(pos, end)}${suffix}"` 164 | 165 | return new Error(`${message} at ${context}`) 166 | } 167 | } 168 | -------------------------------------------------------------------------------- /src/controllers/contract/run.ts: -------------------------------------------------------------------------------- 1 | import { Terminal } from "../../core" 2 | import { 3 | AbiFunction, 4 | AbiParam, 5 | DecodedMessageBody, 6 | DecodedOutput, 7 | MessageBodyType, 8 | Signer, 9 | } from "@eversdk/core" 10 | import { Account } from "@eversdk/appkit" 11 | import { ParamParser } from "./param-parser" 12 | import { SignerRegistry } from "../signer/registry" 13 | import * as process from "process" 14 | 15 | export async function resolveFunction( 16 | terminal: Terminal, 17 | account: Account, 18 | functionName: string, 19 | preventUi: boolean, 20 | ): Promise { 21 | const functions = (account.contract.abi.functions ?? []).filter( 22 | fn => fn.name !== "constructor", 23 | ) 24 | functionName = functionName.trim() 25 | while (functionName === "" && !preventUi) { 26 | terminal.log("\nAvailable functions:\n") 27 | functions.forEach((x, i) => terminal.log(` ${i + 1}) ${x.name}`)) 28 | terminal.log() 29 | 30 | const functionIndex = 31 | Number.parseInt( 32 | await inputParam(terminal, { 33 | name: "Select function", 34 | type: "number", 35 | }), 36 | ) - 1 37 | if (functionIndex >= 0 && functionIndex < functions.length) { 38 | return functions[functionIndex] 39 | } 40 | terminal.log("Invalid function number. Try again.") 41 | } 42 | if (functionName === "") { 43 | throw new Error("Function name isn't specified") 44 | } 45 | const func = account.contract.abi.functions?.find( 46 | x => x.name === functionName, 47 | ) 48 | if (!func) { 49 | throw new Error(`Function not found: ${functionName}`) 50 | } 51 | return func 52 | } 53 | 54 | function inputLine(terminal: Terminal, prompt: string): Promise { 55 | terminal.write(` ${prompt}: `) 56 | return new Promise(resolve => { 57 | const standard_input = process.stdin 58 | standard_input.setEncoding("utf-8") 59 | // SO: https://stackoverflow.com/questions/54182732/process-never-ends-when-using-process-stdin-once 60 | standard_input.resume() 61 | standard_input.once("data", function (data) { 62 | standard_input.pause() 63 | resolve(`${data}`.trim()) 64 | }) 65 | }) 66 | } 67 | 68 | async function inputTuple(terminal: Terminal, param: AbiParam): Promise { 69 | for (;;) { 70 | const value = await inputLine(terminal, `${param.name} (${param.type})`) 71 | try { 72 | return JSON.parse(value) 73 | } catch (err: any) { 74 | terminal.log(err.toString()) 75 | } 76 | } 77 | } 78 | 79 | async function inputScalar(terminal: Terminal, param: AbiParam): Promise { 80 | for (;;) { 81 | const value = await inputLine(terminal, `${param.name} (${param.type})`) 82 | try { 83 | return ParamParser.scalar(param, `"${value}"`) 84 | } catch (err: any) { 85 | terminal.log(err.toString()) 86 | } 87 | } 88 | } 89 | 90 | async function inputArray(terminal: Terminal, param: AbiParam): Promise { 91 | const item = JSON.parse(JSON.stringify(param)) as AbiParam 92 | item.type = param.type.slice(0, -2) 93 | const count = Number( 94 | await inputLine(terminal, `Enter number of items in ${param.name}`), 95 | ) 96 | const items = [] 97 | let i = 1 98 | while (i <= count) { 99 | item.name = `${param.name} ${i}` 100 | items.push(await inputParam(terminal, item)) 101 | i += 1 102 | } 103 | return items 104 | } 105 | 106 | async function inputParam(terminal: Terminal, param: AbiParam): Promise { 107 | if (param.type.endsWith("[]")) { 108 | return inputArray(terminal, param) 109 | } else if (param.type.endsWith("tuple")) { 110 | return inputTuple(terminal, param) 111 | } else { 112 | return inputScalar(terminal, param) 113 | } 114 | } 115 | 116 | export async function resolveParams( 117 | terminal: Terminal, 118 | prompt: string, 119 | params: AbiParam[], 120 | paramsString: string, 121 | preventUi: boolean, 122 | ): Promise { 123 | const values: { [name: string]: any } = ParamParser.components( 124 | { 125 | name: "params", 126 | type: "tuple", 127 | components: params, 128 | }, 129 | paramsString, 130 | ) 131 | let hasUserInput = false 132 | if (params.length > 0) { 133 | terminal.log(prompt) 134 | } 135 | for (const param of params) { 136 | if (param.name in values) { 137 | terminal.log( 138 | ` ${param.name} (${param.type}): ${JSON.stringify( 139 | values[param.name], 140 | )}`, 141 | ) 142 | } 143 | } 144 | for (const param of params) { 145 | if (!(param.name in values)) { 146 | if (!hasUserInput) { 147 | if (preventUi) { 148 | throw new Error(`Missing parameter "${param.name}".`) 149 | } 150 | hasUserInput = true 151 | } 152 | values[param.name] = await inputParam(terminal, param) 153 | } 154 | } 155 | return values 156 | } 157 | 158 | export async function getRunParams( 159 | terminal: Terminal, 160 | account: Account, 161 | args: { 162 | function: string 163 | input: string 164 | preventUi: boolean 165 | signer: string 166 | address: string 167 | runSigner: string 168 | }, 169 | ): Promise<{ 170 | functionName: string 171 | functionInput: object 172 | signer: Signer 173 | }> { 174 | const func = await resolveFunction( 175 | terminal, 176 | account, 177 | args.function, 178 | args.preventUi, 179 | ) 180 | const functionInput = await resolveParams( 181 | terminal, 182 | `\nParameters of ${func.name}:\n`, 183 | func.inputs, 184 | args.input, 185 | args.preventUi, 186 | ) 187 | const signers = new SignerRegistry() 188 | const signer = 189 | args.runSigner.trim() !== "" 190 | ? await signers.resolveSigner(args.runSigner, { 191 | useNoneForEmptyName: false, 192 | }) 193 | : account.signer 194 | return { 195 | functionName: func.name, 196 | functionInput, 197 | signer, 198 | } 199 | } 200 | 201 | export async function logRunResult( 202 | terminal: Terminal, 203 | decoded: DecodedOutput | undefined, 204 | transaction: any, 205 | ): Promise { 206 | const outMessages: DecodedMessageBody[] = 207 | (decoded?.out_messages as any) ?? [] 208 | const details = { 209 | transaction, 210 | output: decoded?.output, 211 | out_messages: outMessages.filter( 212 | x => x?.body_type !== MessageBodyType.Output, 213 | ), 214 | } 215 | terminal.log() 216 | terminal.log("Execution has finished with result:") 217 | terminal.log(JSON.stringify(details, undefined, " ")) 218 | } 219 | -------------------------------------------------------------------------------- /src/controllers/debrowser/command/interfaces.ts: -------------------------------------------------------------------------------- 1 | import { Command, Terminal } from "../../../core" 2 | import { DevDocker } from "../../../core/docker" 3 | import { controlInstances } from "../installer" 4 | import { ContainerInfo } from "dockerode" 5 | import request from "request" 6 | 7 | export const interfacesCommand: Command = { 8 | name: "interfaces", 9 | title: "Show list of implemented interfaces", 10 | args: [], 11 | async run(terminal: Terminal): Promise { 12 | await controlInstances(async (docker, def) => { 13 | const containerInfo: ContainerInfo | null = 14 | await docker.findContainerInfo(def.containerName) 15 | if (null !== containerInfo && DevDocker.isRunning(containerInfo)) { 16 | const url = `http://localhost:${containerInfo.Ports[0].PublicPort}/interfaces.json` 17 | request({ url, json: true }, function (error, response) { 18 | if (null === error) { 19 | terminal.log("Implemented interfaces:") 20 | for (const ifc of response.body) { 21 | terminal.log(` - ${ifc.name}`) 22 | } 23 | } else { 24 | terminal.log(`Error: ${error}.`) 25 | } 26 | }) 27 | } else { 28 | terminal.log("Error: Container isn't running.") 29 | } 30 | }) 31 | }, 32 | } 33 | -------------------------------------------------------------------------------- /src/controllers/debrowser/command/start.ts: -------------------------------------------------------------------------------- 1 | import { Command, Terminal } from "../../../core" 2 | import { ContainerStatus, DevDocker } from "../../../core/docker" 3 | import { controlInstances } from "../installer" 4 | import { ContainerInfo } from "dockerode" 5 | 6 | async function writeEnterMessage( 7 | docker: DevDocker, 8 | terminal: Terminal, 9 | containerName: string, 10 | ) { 11 | const containerInfo: ContainerInfo | null = await docker.findContainerInfo( 12 | containerName, 13 | ) 14 | if (null !== containerInfo) { 15 | terminal.log( 16 | `Open in browser: http://localhost:${containerInfo.Ports[0].PublicPort}/`, 17 | ) 18 | } 19 | } 20 | 21 | export const startCommand: Command = { 22 | name: "start", 23 | title: "Start ExtraTON DeBrowser, default is 'latest'", 24 | args: [ 25 | { 26 | isArg: true, 27 | name: "version", 28 | type: "string", 29 | title: "ExtraTON DeBrowser version (semver compatible)", 30 | defaultValue: "latest", 31 | }, 32 | ], 33 | async run(terminal: Terminal, args: { version: string }): Promise { 34 | await controlInstances(async (docker, def) => { 35 | const containerInfo: ContainerInfo | null = 36 | await docker.findContainerInfo(def.containerName) 37 | if (DevDocker.isRunning(containerInfo)) { 38 | terminal.log("Error: Container is already running.") 39 | } else { 40 | if ( 41 | null !== containerInfo && 42 | containerInfo.Image !== def.requiredImage 43 | ) { 44 | await docker.shutdownContainer( 45 | terminal, 46 | def, 47 | ContainerStatus.missing, 48 | ) 49 | } 50 | await docker.startupContainer( 51 | terminal, 52 | def, 53 | ContainerStatus.running, 54 | ) 55 | await writeEnterMessage(docker, terminal, def.containerName) 56 | } 57 | }, args.version) 58 | }, 59 | } 60 | -------------------------------------------------------------------------------- /src/controllers/debrowser/command/stop.ts: -------------------------------------------------------------------------------- 1 | import { Command, Terminal } from "../../../core" 2 | import { ContainerStatus } from "../../../core/docker" 3 | import { controlInstances } from "../installer" 4 | 5 | export const stopCommand: Command = { 6 | name: "stop", 7 | title: "Stop ExtraTON DeBrowser", 8 | args: [], 9 | async run(terminal: Terminal): Promise { 10 | await controlInstances(async (docker, def) => { 11 | await docker.shutdownContainer( 12 | terminal, 13 | def, 14 | ContainerStatus.created, 15 | ) 16 | }) 17 | }, 18 | } 19 | -------------------------------------------------------------------------------- /src/controllers/debrowser/command/version.ts: -------------------------------------------------------------------------------- 1 | import { getAvailableVersions } from "../installer" 2 | import { Command, Terminal } from "../../../core" 3 | 4 | export const versionCommand: Command = { 5 | name: "version", 6 | title: "Show DeBrowser Versions", 7 | async run(terminal: Terminal): Promise { 8 | terminal.log( 9 | `Available Versions: ${(await getAvailableVersions()).join(", ")}`, 10 | ) 11 | }, 12 | } 13 | -------------------------------------------------------------------------------- /src/controllers/debrowser/index.ts: -------------------------------------------------------------------------------- 1 | import { ToolController } from "../../core" 2 | import { versionCommand } from "./command/version" 3 | import { startCommand } from "./command/start" 4 | import { stopCommand } from "./command/stop" 5 | import { interfacesCommand } from "./command/interfaces" 6 | 7 | export const DeBrowser: ToolController = { 8 | name: "debrowser", 9 | title: "ExtraTON Debot Browser", 10 | commands: [versionCommand, startCommand, stopCommand, interfacesCommand], 11 | } 12 | -------------------------------------------------------------------------------- /src/controllers/debrowser/installer.ts: -------------------------------------------------------------------------------- 1 | import { compareVersionsDescending, httpsGetJson } from "../../core/utils" 2 | import { ContainerDef, DevDocker } from "../../core/docker" 3 | import Dockerode from "dockerode" 4 | 5 | const DOCKER_IMAGE_NAME = "extraton/extraton-debrowser" 6 | const DOCKER_CONTAINER_NAME = "extraton-debrowser" 7 | 8 | export async function getAvailableVersions(): Promise { 9 | const url = `https://registry.hub.docker.com/v2/repositories/${DOCKER_IMAGE_NAME}/tags/` 10 | return (await httpsGetJson(url)).results 11 | .map((x: any) => x.name) 12 | .sort(compareVersionsDescending) 13 | } 14 | 15 | function instanceContainerDef(version?: string): ContainerDef { 16 | const requiredImage = `${DOCKER_IMAGE_NAME}:${version}` 17 | return { 18 | requiredImage, 19 | containerName: DOCKER_CONTAINER_NAME, 20 | createContainer(docker: DevDocker) { 21 | const ports: Dockerode.PortMap = { 22 | "80/tcp": [ 23 | { 24 | HostIp: "", 25 | HostPort: "8087/tcp", 26 | }, 27 | ], 28 | } 29 | return docker.client.createContainer({ 30 | name: DOCKER_CONTAINER_NAME, 31 | Image: requiredImage, 32 | HostConfig: { 33 | PortBindings: ports, 34 | }, 35 | }) 36 | }, 37 | } 38 | } 39 | 40 | export async function controlInstances( 41 | control: (docker: DevDocker, def: ContainerDef) => Promise, 42 | version?: string, 43 | ): Promise { 44 | if (version === "latest") { 45 | version = (await getAvailableVersions())[0] 46 | } 47 | const def: ContainerDef = instanceContainerDef(version) 48 | await control(new DevDocker(), def) 49 | } 50 | -------------------------------------------------------------------------------- /src/controllers/ever-cli/components.ts: -------------------------------------------------------------------------------- 1 | import { Component } from "../../core" 2 | 3 | const TOOL_FOLDER_NAME = "ever-cli" 4 | 5 | export const components = { 6 | tonoscli: new (class extends Component { 7 | getSourceName(version: string): string { 8 | return `ever-cli-${version.split(".").join("_")}-{p}.zip` 9 | } 10 | })(TOOL_FOLDER_NAME, "ever-cli", { 11 | resolveVersionRegExp: /tonos_cli\s+([0-9.]+)/, 12 | isExecutable: true, 13 | runGlobally: true, 14 | }), 15 | } 16 | -------------------------------------------------------------------------------- /src/controllers/ever-cli/index.ts: -------------------------------------------------------------------------------- 1 | import { Command, Component, Terminal, ToolController } from "../../core" 2 | import { components } from "./components" 3 | 4 | export const tonosInstallCommand: Command = { 5 | name: "install", 6 | title: "Install latest stable EVER OS CLI", 7 | args: [], 8 | async run(terminal: Terminal) { 9 | await Component.ensureInstalledAll(terminal, components) 10 | }, 11 | } 12 | 13 | export const tonosSetCommand: Command = { 14 | name: "set", 15 | title: "Change installed version", 16 | args: [ 17 | { 18 | name: "version", 19 | title: "version to install (e.g. 0.8.1 or latest)", 20 | type: "string", 21 | defaultValue: "latest", 22 | }, 23 | ], 24 | async run(terminal: Terminal, args: { version: string }): Promise { 25 | await Component.setVersions(terminal, false, components, { 26 | tonoscli: args.version, 27 | }) 28 | }, 29 | } 30 | 31 | export const tonosUpdateCommand: Command = { 32 | name: "update", 33 | title: "Update to the latest version", 34 | async run(terminal: Terminal): Promise { 35 | await Component.updateAll(terminal, false, components) 36 | }, 37 | } 38 | 39 | export const tonosVersionCommand: Command = { 40 | name: "version", 41 | title: "Show installed and available versions", 42 | async run(terminal: Terminal): Promise { 43 | terminal.log(await Component.getInfoAll(components)) 44 | }, 45 | } 46 | 47 | export const EVEROS: ToolController = { 48 | name: "ever-cli", 49 | title: "EVER OS CLI", 50 | commands: [ 51 | tonosInstallCommand, 52 | tonosSetCommand, 53 | tonosVersionCommand, 54 | tonosUpdateCommand, 55 | ], 56 | } 57 | -------------------------------------------------------------------------------- /src/controllers/index.ts: -------------------------------------------------------------------------------- 1 | import { Clang } from "./clang" 2 | import { Solidity } from "./solidity" 3 | import { Sold } from "./sold" 4 | // import {TestSuite} from "./ts"; 5 | import { JsApps } from "./js" 6 | import { SE } from "./se" 7 | import { EVEROS } from "./ever-cli" 8 | import { TestSuite4 } from "./ts4" 9 | import { DeBrowser } from "./debrowser" 10 | import { Command, matchName, Terminal, ToolController } from "../core" 11 | import { SignerTool } from "./signer" 12 | import { NetworkTool } from "./network" 13 | import { Contract } from "./contract" 14 | import { missingArgError } from "../cli" 15 | 16 | export const controllers = [ 17 | Clang, 18 | Solidity, 19 | Sold, 20 | SE, 21 | NetworkTool, 22 | SignerTool, 23 | Contract, 24 | JsApps, 25 | EVEROS, 26 | TestSuite4, 27 | DeBrowser, 28 | ] 29 | 30 | export function findControllerAndCommandByAlias( 31 | alias: string, 32 | ): { controller: ToolController; command: Command } | undefined { 33 | alias = alias.trim().toLowerCase() 34 | for (const controller of controllers) { 35 | for (const command of controller.commands) { 36 | if (controller.alias && command.alias) { 37 | if (`${controller.alias}${command.alias}` === alias) { 38 | return { 39 | controller, 40 | command, 41 | } 42 | } 43 | } 44 | } 45 | } 46 | return undefined 47 | } 48 | 49 | export async function runCommand( 50 | terminal: Terminal, 51 | name: string, 52 | args: any, 53 | ): Promise { 54 | const [controllerName, commandName] = name 55 | .toLowerCase() 56 | .split(" ") 57 | .map(x => x.trim()) 58 | .filter(x => x !== "") 59 | const controller = controllers.find(x => matchName(x, controllerName)) 60 | if (!controller) { 61 | throw new Error(`Controller ${controllerName} not found`) 62 | } 63 | const command = controller.commands.find(x => matchName(x, commandName)) 64 | if (!command) { 65 | throw new Error( 66 | `Command ${commandName} not found in controller ${controllerName}`, 67 | ) 68 | } 69 | 70 | const resolvedArgs: any = Object.assign({}, args) 71 | let checkedArgs = Object.keys(resolvedArgs) 72 | 73 | for (const arg of command.args ?? []) { 74 | const name = arg.name 75 | .split("-") 76 | .map((x, i) => 77 | i > 0 ? x.substr(0, 1).toUpperCase() + x.substr(1) : x, 78 | ) 79 | .join("") 80 | if (resolvedArgs[name] === undefined) { 81 | if (arg.defaultValue !== undefined) { 82 | resolvedArgs[name] = 83 | arg.type === "boolean" 84 | ? arg.defaultValue === "true" 85 | : arg.defaultValue 86 | } else if (arg.type === "folder") { 87 | resolvedArgs[name] = process.cwd() 88 | } else { 89 | throw await missingArgError(arg) 90 | } 91 | } else { 92 | // remove matched element from array 93 | checkedArgs = checkedArgs.filter(k => k !== name) 94 | } 95 | } 96 | 97 | if (checkedArgs.length > 0) { 98 | const msg = [`Unknow option: ${checkedArgs[0]}`] 99 | if (checkedArgs[0].includes("-")) { 100 | msg.push( 101 | `You must convert parameters to camelcase, for example "output-dir" to "outputDir"`, 102 | ) 103 | } 104 | throw Error(msg.join("\n")) 105 | } 106 | await command.run(terminal, resolvedArgs) 107 | } 108 | -------------------------------------------------------------------------------- /src/controllers/js/index.ts: -------------------------------------------------------------------------------- 1 | import { Command, Terminal, ToolController } from "../../core" 2 | import path from "path" 3 | import fs from "fs" 4 | import { BaseApp } from "./snippets" 5 | import { downloadDemo, loadInfo } from "./installer" 6 | import { formatTable, writeTextFile } from "../../core/utils" 7 | import { jsWrapCommand } from "./wrap" 8 | 9 | export const jsCreateCommand: Command = { 10 | name: "create", 11 | alias: "c", 12 | title: "Create Everscale JS App", 13 | args: [ 14 | { 15 | isArg: true, 16 | name: "name", 17 | type: "string", 18 | }, 19 | { 20 | name: "folder", 21 | type: "folder", 22 | title: "Target folder (current default)", 23 | }, 24 | ], 25 | async run(terminal: Terminal, args: { name: string; folder: string }) { 26 | const appFolderPath = path.resolve(args.folder, args.name) 27 | fs.mkdirSync(appFolderPath, { recursive: true }) 28 | writeTextFile(path.resolve(appFolderPath, "index.js"), BaseApp.index) 29 | writeTextFile( 30 | path.resolve(appFolderPath, "package.json"), 31 | BaseApp.package, 32 | ) 33 | terminal.log(`App created in ${appFolderPath}`) 34 | }, 35 | } 36 | 37 | export const jsDemoCommand: Command = { 38 | name: "demo", 39 | title: "Download Everscale Demo", 40 | args: [ 41 | { 42 | isArg: true, 43 | name: "name", 44 | type: "string", 45 | defaultValue: "", 46 | }, 47 | { 48 | name: "folder", 49 | type: "folder", 50 | title: "Target folder (current default)", 51 | }, 52 | ], 53 | async run(terminal: Terminal, args: { name: string; folder: string }) { 54 | if (args.name === "") { 55 | const table = [ 56 | ["Demo", "Description"], 57 | ...(await loadInfo()).applications.map(x => [ 58 | x.name, 59 | x.description, 60 | ]), 61 | ] 62 | terminal.log(formatTable(table, { headerSeparator: true })) 63 | } else { 64 | await downloadDemo(terminal, args.name, args.folder) 65 | } 66 | }, 67 | } 68 | 69 | export const JsApps: ToolController = { 70 | name: "js", 71 | alias: "j", 72 | title: "JavaScript Code Generators", 73 | commands: [jsCreateCommand, jsDemoCommand, jsWrapCommand], 74 | } 75 | -------------------------------------------------------------------------------- /src/controllers/js/installer.ts: -------------------------------------------------------------------------------- 1 | import path from "path" 2 | import fs from "fs-extra" 3 | import { Terminal, everdevHome } from "../../core" 4 | import { downloadFromGithub, httpsGetJson } from "../../core/utils" 5 | 6 | const demoBranch = "master" 7 | const demoInfoURL = `https://raw.githubusercontent.com/everx-labs/sdk-samples/${demoBranch}/demo.json` 8 | const demoArchiveURL = `https://github.com/everx-labs/sdk-samples/archive/${demoBranch}.zip` 9 | const demoFolder = `sdk-samples-${demoBranch}` 10 | 11 | function jsHome() { 12 | return path.resolve(everdevHome(), "js") 13 | } 14 | 15 | function demoHome() { 16 | return path.resolve(jsHome(), demoFolder) 17 | } 18 | 19 | export function getInfo(): DemoInfo { 20 | return JSON.parse( 21 | fs.readFileSync(path.resolve(demoHome(), "demo.json")).toString(), 22 | ) 23 | } 24 | 25 | export async function loadInfo(): Promise { 26 | return httpsGetJson(demoInfoURL) 27 | } 28 | 29 | export async function ensureDemoInstalled(terminal: Terminal) { 30 | if (fs.pathExistsSync(demoHome())) { 31 | const info = getInfo() 32 | const remoteInfo = await loadInfo() 33 | if (info.version === remoteInfo.version) { 34 | return 35 | } 36 | fs.rmdirSync(demoHome(), { recursive: true }) 37 | } 38 | if (!fs.pathExistsSync(jsHome())) { 39 | fs.mkdirSync(jsHome(), { recursive: true }) 40 | } 41 | terminal.log("Downloading demo repository...") 42 | await downloadFromGithub(terminal, demoArchiveURL, jsHome()) 43 | } 44 | 45 | export type DemoApp = { 46 | name: string 47 | path: string 48 | description: string 49 | } 50 | 51 | export type DemoInfo = { 52 | version: string 53 | applications: DemoApp[] 54 | } 55 | 56 | export async function downloadDemo( 57 | terminal: Terminal, 58 | name: string, 59 | folder: string, 60 | ) { 61 | await ensureDemoInstalled(terminal) 62 | const info = getInfo() 63 | const app = info.applications.find( 64 | x => x.name.toLowerCase() === name.toLowerCase(), 65 | ) 66 | if (!app) { 67 | throw new Error(`Demo "${name} not found.`) 68 | } 69 | const dstPath = path.resolve(folder, name) 70 | if (fs.existsSync(dstPath)) { 71 | terminal.log(`This demo already downloaded in: ${dstPath}`) 72 | return 73 | } 74 | fs.copySync(path.resolve(demoHome(), app.path), dstPath) 75 | terminal.log(` 76 | Demo ${name} is downloaded into ${dstPath} 77 | Check README.md or run application: 78 | $ cd ${path.relative(".", dstPath)} 79 | $ npm i 80 | $ npm start 81 | `) 82 | } 83 | -------------------------------------------------------------------------------- /src/controllers/js/snippets.ts: -------------------------------------------------------------------------------- 1 | export const BaseApp = { 2 | index: ` 3 | 4 | const {TonClient} = require("@eversdk/core"); 5 | const {libNode} = require("@eversdk/lib-node"); 6 | 7 | TonClient.useBinaryLibrary(libNode); 8 | (async () => { 9 | const client = new TonClient({ 10 | network: { 11 | endpoints: ["http://localhost"] 12 | } 13 | }); 14 | try { 15 | await main(client); 16 | } catch (err) { 17 | console.error(err); 18 | } 19 | client.close(); 20 | })(); 21 | 22 | async function main(client) { 23 | console.log((await client.client.version()).version); 24 | } 25 | 26 | `, 27 | package: ` 28 | { 29 | "dependencies": { 30 | "@eversdk/core": "^1", 31 | "@eversdk/lib-node": "^1" 32 | } 33 | } 34 | `, 35 | } 36 | -------------------------------------------------------------------------------- /src/controllers/js/wrap.ts: -------------------------------------------------------------------------------- 1 | import path from "path" 2 | import { TonClient } from "@eversdk/core" 3 | import { libNode } from "@eversdk/lib-node" 4 | 5 | import { writeTextFile } from "../../core/utils" 6 | import { resolveContract } from "../../core/solFileResolvers" 7 | import { Command, CommandArgVariant, Terminal } from "../../core" 8 | 9 | TonClient.useBinaryLibrary(libNode) 10 | 11 | enum ExportFormat { 12 | CommonJs = "commonjs", 13 | CommonJsDefault = "commonjs-default", 14 | Es6 = "es6", 15 | Es6Default = "es6-default", 16 | } 17 | 18 | function getExportSection(exportFormat: ExportFormat, name: string): string { 19 | switch (exportFormat) { 20 | case ExportFormat.CommonJs: 21 | return `module.exports = { ${name} };` 22 | case ExportFormat.CommonJsDefault: 23 | return `module.exports = ${name};` 24 | case ExportFormat.Es6: 25 | return `export ${name};` 26 | case ExportFormat.Es6Default: 27 | return `export default ${name};` 28 | } 29 | throw new Error(`Invalid JS export mode ${exportFormat}`) 30 | } 31 | 32 | export const jsWrapCommand: Command = { 33 | name: "wrap", 34 | alias: "w", 35 | title: "Wrap ABI file into JavaScript code.", 36 | args: [ 37 | { 38 | isArg: true, 39 | name: "file", 40 | type: "file", 41 | title: "ABI file", 42 | nameRegExp: /\.abi\.json$/i, 43 | }, 44 | { 45 | name: "print", 46 | alias: "p", 47 | type: "boolean", 48 | title: "Print code to console", 49 | defaultValue: "false", 50 | }, 51 | { 52 | name: "output", 53 | alias: "o", 54 | type: "string", 55 | title: "Set output file name (default is built from source ABI file name)", 56 | defaultValue: "", 57 | }, 58 | { 59 | name: "export", 60 | alias: "e", 61 | type: "string", 62 | title: "Export type and options", 63 | getVariants(): CommandArgVariant[] { 64 | return [ 65 | { 66 | value: "commonjs", 67 | description: "Use CommonJS modules (NodeJs)", 68 | }, 69 | { 70 | value: "commonjs-default", 71 | description: 72 | "Use CommonJS modules (NodeJS) with default export", 73 | }, 74 | { 75 | value: "es6", 76 | description: "Use ES6 modules", 77 | }, 78 | { 79 | value: "es6-default", 80 | description: "Use ES6 modules with default export", 81 | }, 82 | ] 83 | }, 84 | defaultValue: ExportFormat.CommonJs, 85 | }, 86 | ], 87 | run: async function ( 88 | terminal: Terminal, 89 | args: { 90 | file: string 91 | print: boolean 92 | output: string 93 | export: string 94 | }, 95 | ) { 96 | const contract = resolveContract(path.resolve(process.cwd(), args.file)) 97 | const name = path 98 | .basename(contract.abiPath) 99 | .slice(0, -".abi.json".length) 100 | const abi = contract.package.abi 101 | const contractName = `${name.substr(0, 1).toUpperCase()}${name.substr( 102 | 1, 103 | )}Contract` 104 | const code = [`const ${contractName} = {`] 105 | const abiCode = JSON.stringify(abi, undefined, " ") 106 | .split("\r\n") 107 | .join("\n") 108 | .split("\n") 109 | .map((x, i) => (i > 0 ? ` ${x}` : x)) 110 | .join("\n") 111 | 112 | code.push(` abi: ${abiCode},`) 113 | const tvc = contract.package.tvc 114 | if (tvc !== undefined) { 115 | code.push(` tvc: "${tvc}",`) 116 | const client = new TonClient() 117 | const tvcCode = (await client.boc.get_code_from_tvc({ tvc })).code 118 | code.push(` code: "${tvcCode}",`) 119 | code.push( 120 | ` codeHash: "${ 121 | (await client.boc.get_boc_hash({ boc: tvcCode })).hash 122 | }",`, 123 | ) 124 | await client.close() 125 | } 126 | code.push("};") 127 | code.push( 128 | getExportSection( 129 | args.export.toLowerCase() as ExportFormat, 130 | contractName, 131 | ), 132 | ) 133 | const wrapperCode = code.join("\n") 134 | if (args.print) { 135 | terminal.log(wrapperCode) 136 | } else { 137 | const wrapperPath = path.resolve( 138 | path.dirname(contract.abiPath), 139 | args.output !== "" ? args.output : `${contractName}.js`, 140 | ) 141 | writeTextFile(wrapperPath, wrapperCode) 142 | terminal.log(`Generated wrapper code written to: ${wrapperPath}`) 143 | } 144 | }, 145 | } 146 | -------------------------------------------------------------------------------- /src/controllers/network/giver.ts: -------------------------------------------------------------------------------- 1 | import { Account, AccountGiver } from "@eversdk/appkit" 2 | import { Signer, TonClient } from "@eversdk/core" 3 | import { 4 | KnownContract, 5 | knownContractAddress, 6 | knownContractByName, 7 | knownContractFromAddress, 8 | KnownContractName, 9 | KnownContracts, 10 | } from "../../core/known-contracts" 11 | import { NetworkGiverInfo } from "./registry" 12 | import { SE_DEFAULT_GIVER_SIGNER, SignerRegistry } from "../signer/registry" 13 | import { formatTokens } from "../../core/utils" 14 | 15 | async function giverV2Send( 16 | giver: Account, 17 | address: string, 18 | value: number, 19 | ): Promise { 20 | await giver.run("sendTransaction", { 21 | dest: address, 22 | value: value, 23 | bounce: false, 24 | }) 25 | } 26 | 27 | async function giverV1Send( 28 | giver: Account, 29 | address: string, 30 | value: number, 31 | ): Promise { 32 | await giver.run("sendGrams", { 33 | dest: address, 34 | amount: value, 35 | }) 36 | } 37 | 38 | async function multisigSend( 39 | giver: Account, 40 | address: string, 41 | value: number, 42 | ): Promise { 43 | await giver.run("sendTransaction", { 44 | dest: address, 45 | value: value, 46 | bounce: false, 47 | flags: 1, 48 | payload: "", 49 | }) 50 | } 51 | 52 | export class NetworkGiver implements AccountGiver { 53 | account: Account 54 | value: number | undefined 55 | name: string | undefined 56 | 57 | constructor( 58 | public contract: KnownContract, 59 | client: TonClient, 60 | public address: string, 61 | signer: Signer, 62 | public info: NetworkGiverInfo, 63 | private send: ( 64 | giver: Account, 65 | address: string, 66 | value: number, 67 | ) => Promise, 68 | ) { 69 | this.value = info.value 70 | this.name = info.name 71 | this.account = new Account(contract, { 72 | client, 73 | address, 74 | signer, 75 | }) 76 | } 77 | 78 | static async create( 79 | client: TonClient, 80 | info: NetworkGiverInfo, 81 | ): Promise { 82 | const signerName = info.signer ?? SE_DEFAULT_GIVER_SIGNER.name 83 | const signer = await new SignerRegistry().resolveSigner(signerName, { 84 | useNoneForEmptyName: true, 85 | }) 86 | const knownName = info.name as KnownContractName 87 | const address = 88 | info.address !== "" 89 | ? info.address 90 | : await knownContractAddress(client, knownName, signer) 91 | let contract: KnownContract 92 | let send: ( 93 | giver: Account, 94 | address: string, 95 | value: number, 96 | ) => Promise 97 | 98 | if (info.name !== undefined && info.name !== "auto") { 99 | contract = await knownContractByName(knownName) 100 | } else { 101 | contract = await knownContractFromAddress(client, "Giver", address) 102 | } 103 | 104 | if (contract === KnownContracts.GiverV1) { 105 | send = giverV1Send 106 | } else if (contract === KnownContracts.GiverV2) { 107 | send = giverV2Send 108 | } else if (contract === KnownContracts.GiverV3) { 109 | send = giverV2Send 110 | } else if (contract === KnownContracts.SetcodeMultisigWallet) { 111 | send = multisigSend 112 | } else if (contract === KnownContracts.MsigV2) { 113 | send = multisigSend 114 | } else if (contract === KnownContracts.SafeMultisigWallet) { 115 | send = multisigSend 116 | } else { 117 | throw new Error( 118 | `Contract ${contract.name} can't be used as a giver.`, 119 | ) 120 | } 121 | return new NetworkGiver(contract, client, address, signer, info, send) 122 | } 123 | 124 | async sendTo(address: string, value: number): Promise { 125 | const valueToSend = this.value ?? value 126 | try { 127 | await this.send(this.account, address, valueToSend) 128 | } catch (error: any) { 129 | const message = `Giver can't send ${formatTokens( 130 | valueToSend, 131 | )} to the ${address}` 132 | const giver = `Contract: ${this.info.name}\nAddress: ${this.address}\nSigner: ${this.info.signer}` 133 | throw new Error( 134 | `${message}: ${error.message}\n\nPlease check giver configuration:\n${giver}`, 135 | ) 136 | } 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /src/controllers/se/commands.ts: -------------------------------------------------------------------------------- 1 | import { SERegistry, RegistryError } from "./registry" 2 | import { Command, CommandArg, Terminal } from "../../core" 3 | import { formatTable, StringTerminal } from "../../core/utils" 4 | import { printUsage } from "../../everdev/help" 5 | import { SE } from "." 6 | 7 | export const instanceArg: CommandArg = { 8 | isArg: true, 9 | name: "instance", 10 | type: "string", 11 | defaultValue: "*", 12 | title: "SE Instance Filter", 13 | } 14 | 15 | const forceArg: CommandArg = { 16 | name: "force", 17 | alias: "f", 18 | type: "boolean", 19 | title: "Delete multiple instances", 20 | description: 21 | 'If you want to delete several instances (e.g. with "*") you should set this option.', 22 | defaultValue: "false", 23 | } 24 | 25 | export const seInfoCommand: Command = { 26 | name: "info", 27 | title: "Show SE Info", 28 | args: [instanceArg], 29 | async run(terminal: Terminal, args: { instance: string }): Promise { 30 | const table: (string | number | undefined)[][] = [ 31 | [ 32 | "Instance", 33 | "State", 34 | "Version", 35 | "GraphQL Port", 36 | "ArangoDB Port", 37 | "Docker Container", 38 | "Docker Image", 39 | ], 40 | ] 41 | const registry = new SERegistry() 42 | for (const item of registry.filter(args.instance, false)) { 43 | const info = await registry.getItemInfo(item) 44 | table.push([ 45 | item.name, 46 | info.state, 47 | await registry.getSourceInfo(item), 48 | item.port, 49 | item.dbPort, 50 | info.docker.container, 51 | info.docker.image, 52 | ]) 53 | } 54 | terminal.log(formatTable(table, { headerSeparator: true })) 55 | }, 56 | } 57 | 58 | export const seVersionCommand: Command = { 59 | name: "version", 60 | title: "Show SE Versions", 61 | async run(terminal: Terminal): Promise { 62 | const registry = new SERegistry() 63 | for (const item of registry.items) { 64 | terminal.log(`${item.name}: ${await registry.getSourceInfo(item)}`) 65 | } 66 | terminal.log( 67 | `Available Versions: ${(await SERegistry.getVersions()).join( 68 | ", ", 69 | )}`, 70 | ) 71 | }, 72 | } 73 | 74 | export const seStartCommand: Command = { 75 | name: "start", 76 | title: "Start SE Instance", 77 | args: [instanceArg], 78 | async run(terminal: Terminal, args: { instance: string }): Promise { 79 | await new SERegistry().start(terminal, args.instance) 80 | }, 81 | } 82 | 83 | export const seStopCommand: Command = { 84 | name: "stop", 85 | title: "Stop SE Instance", 86 | args: [instanceArg], 87 | async run(terminal: Terminal, args: { instance: string }): Promise { 88 | await new SERegistry().stop(terminal, args.instance) 89 | }, 90 | } 91 | 92 | export const seResetCommand: Command = { 93 | name: "reset", 94 | title: "Reset SE Instance", 95 | args: [instanceArg], 96 | async run(terminal: Terminal, args: { instance: string }): Promise { 97 | await new SERegistry().reset(terminal, args.instance) 98 | }, 99 | } 100 | 101 | export const seUpdateCommand: Command = { 102 | name: "update", 103 | title: "Update SE Instance Version", 104 | args: [instanceArg], 105 | async run(terminal: Terminal, args: { instance: string }): Promise { 106 | await new SERegistry().update(terminal, args.instance) 107 | }, 108 | } 109 | 110 | export const seSetCommand: Command = { 111 | name: "set", 112 | title: "Update SE Instance Config", 113 | args: [ 114 | instanceArg, 115 | { 116 | name: "version", 117 | title: "SE version (version number or `latest`)", 118 | type: "string", 119 | defaultValue: "", 120 | }, 121 | { 122 | name: "image", 123 | title: "Custom SE docker image name", 124 | type: "string", 125 | defaultValue: "", 126 | }, 127 | { 128 | name: "container", 129 | title: "Custom SE docker container name", 130 | type: "string", 131 | defaultValue: "", 132 | }, 133 | { 134 | name: "port", 135 | title: "Port on localhost used to expose GraphQL API", 136 | type: "string", 137 | defaultValue: "", 138 | }, 139 | { 140 | name: "db-port", 141 | title: "Port on localhost used to expose ArangoDB API (number or `none`)", 142 | type: "string", 143 | defaultValue: "", 144 | }, 145 | ], 146 | async run( 147 | terminal: Terminal, 148 | args: { 149 | version: string 150 | image: string 151 | container: string 152 | port: string 153 | dbPort: string 154 | instance: string 155 | }, 156 | ): Promise { 157 | try { 158 | await new SERegistry().configure(terminal, args) 159 | } catch (err: any) { 160 | if (err instanceof RegistryError) { 161 | // Show HELP section 162 | const terminal = new StringTerminal() 163 | terminal.log(err.message + "\n") 164 | await printUsage(terminal, SE, this) 165 | throw Error(terminal.stdout) 166 | } else { 167 | throw err 168 | } 169 | } 170 | }, 171 | } 172 | 173 | export const seDeleteCommand: Command = { 174 | name: "delete", 175 | title: "Delete SE from list", 176 | description: "This command doesn't delete any docker container or image.", 177 | args: [instanceArg, forceArg], 178 | async run(_terminal: Terminal, args: { instance: string; force: boolean }) { 179 | new SERegistry().delete(args.instance, args.force) 180 | }, 181 | } 182 | -------------------------------------------------------------------------------- /src/controllers/se/index.ts: -------------------------------------------------------------------------------- 1 | import { ToolController } from "../../core" 2 | import { 3 | seInfoCommand, 4 | seVersionCommand, 5 | seUpdateCommand, 6 | seSetCommand, 7 | seResetCommand, 8 | seStartCommand, 9 | seStopCommand, 10 | seDeleteCommand, 11 | } from "./commands" 12 | 13 | export const SE: ToolController = { 14 | name: "se", 15 | title: "EVER OS SE", 16 | commands: [ 17 | seInfoCommand, 18 | seVersionCommand, 19 | seUpdateCommand, 20 | seSetCommand, 21 | seStartCommand, 22 | seStopCommand, 23 | seResetCommand, 24 | seDeleteCommand, 25 | ], 26 | } 27 | -------------------------------------------------------------------------------- /src/controllers/signer/index.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Command, 3 | CommandArg, 4 | CommandArgVariant, 5 | Terminal, 6 | ToolController, 7 | } from "../../core" 8 | import { SignerRegistry } from "./registry" 9 | import { TonClient } from "@eversdk/core" 10 | import { formatTable } from "../../core/utils" 11 | import { NetworkRegistry } from "../network/registry" 12 | 13 | const nameArg: CommandArg = { 14 | isArg: true, 15 | name: "name", 16 | title: "Signer name", 17 | type: "string", 18 | } 19 | 20 | const secretArg: CommandArg = { 21 | isArg: true, 22 | name: "secret", 23 | title: "Secret key or seed phrase", 24 | type: "string", 25 | } 26 | 27 | const mnemonicOpt: CommandArg = { 28 | name: "mnemonic", 29 | alias: "m", 30 | title: "Use mnemonic phrase", 31 | type: "boolean", 32 | defaultValue: "false", 33 | } 34 | 35 | const dictionaryOpt: CommandArg = { 36 | name: "dictionary", 37 | alias: "d", 38 | type: "string", 39 | title: "Mnemonic dictionary", 40 | defaultValue: "1", 41 | getVariants(): CommandArgVariant[] { 42 | return [ 43 | { 44 | value: "0", 45 | description: "TON", 46 | }, 47 | { 48 | value: "1", 49 | description: "English", 50 | }, 51 | { 52 | value: "2", 53 | description: "Chinese Simplified", 54 | }, 55 | { 56 | value: "3", 57 | description: "Chinese Traditional", 58 | }, 59 | { 60 | value: "4", 61 | description: "French", 62 | }, 63 | { 64 | value: "5", 65 | description: "Italian", 66 | }, 67 | { 68 | value: "6", 69 | description: "Japanese", 70 | }, 71 | { 72 | value: "7", 73 | description: "Korean", 74 | }, 75 | { 76 | value: "8", 77 | description: "Spanish", 78 | }, 79 | ] 80 | }, 81 | } 82 | 83 | const wordsOpt: CommandArg = { 84 | name: "words", 85 | alias: "w", 86 | title: "Number of mnemonic words", 87 | type: "string", 88 | defaultValue: "12", 89 | } 90 | 91 | const forceOpt: CommandArg = { 92 | name: "force", 93 | alias: "f", 94 | title: "Overwrite signer if already exists", 95 | type: "boolean", 96 | defaultValue: "false", 97 | } 98 | 99 | export const signerGenerateCommand: Command = { 100 | name: "generate", 101 | alias: "g", 102 | title: "Add signer with randomly generated keys", 103 | args: [nameArg, mnemonicOpt, dictionaryOpt, wordsOpt, forceOpt], 104 | async run( 105 | _terminal: Terminal, 106 | args: { 107 | name: string 108 | mnemonic: boolean 109 | dictionary: string 110 | words: string 111 | force: boolean 112 | }, 113 | ) { 114 | if (args.mnemonic) { 115 | const dictionary = Number.parseInt(args.dictionary) 116 | const word_count = Number.parseInt(args.words) 117 | const phrase = ( 118 | await TonClient.default.crypto.mnemonic_from_random({ 119 | dictionary, 120 | word_count, 121 | }) 122 | ).phrase 123 | await new SignerRegistry().addMnemonicKey( 124 | args.name, 125 | "", 126 | phrase, 127 | dictionary, 128 | args.force, 129 | ) 130 | } else { 131 | await new SignerRegistry().addSecretKey( 132 | args.name, 133 | "", 134 | ( 135 | await TonClient.default.crypto.generate_random_sign_keys() 136 | ).secret, 137 | args.force, 138 | ) 139 | } 140 | }, 141 | } 142 | 143 | export const signerAddCommand: Command = { 144 | name: "add", 145 | title: "Add signer", 146 | args: [nameArg, secretArg, dictionaryOpt, forceOpt], 147 | async run( 148 | terminal: Terminal, 149 | args: { 150 | name: string 151 | secret: string 152 | dictionary: string 153 | force: boolean 154 | }, 155 | ) { 156 | await new SignerRegistry().add(terminal, args) 157 | }, 158 | } 159 | 160 | export const signerListCommand: Command = { 161 | name: "list", 162 | alias: "l", 163 | title: "Prints list of registered signers", 164 | args: [], 165 | async run(terminal: Terminal) { 166 | const registry = new SignerRegistry() 167 | const networks = new NetworkRegistry() 168 | const rows = [["Signer", "Public Key", "Used", "Description"]] 169 | registry.items.forEach(x => { 170 | const summary = registry.getSignerSummary(x, networks) 171 | rows.push([ 172 | summary.name, 173 | summary.public, 174 | summary.used, 175 | summary.description, 176 | ]) 177 | }) 178 | const table = formatTable(rows, { headerSeparator: true }) 179 | if (table.trim() !== "") { 180 | terminal.log(table) 181 | } 182 | }, 183 | } 184 | 185 | export const signerGetCommand: Command = { 186 | name: "info", 187 | alias: "i", 188 | title: "Get signer detailed information", 189 | args: [ 190 | { 191 | ...nameArg, 192 | defaultValue: "", 193 | }, 194 | ], 195 | async run(terminal: Terminal, args: { name: string }) { 196 | if (args.name === "") { 197 | await signerListCommand.run(terminal, {}) 198 | return 199 | } 200 | const signer = new SignerRegistry().get(args.name) 201 | terminal.log(JSON.stringify(signer, undefined, " ")) 202 | }, 203 | } 204 | 205 | export const signerDeleteCommand: Command = { 206 | name: "delete", 207 | title: "Delete signer from registry", 208 | args: [nameArg], 209 | async run(_terminal: Terminal, args: { name: string }) { 210 | new SignerRegistry().delete(args.name) 211 | }, 212 | } 213 | 214 | export const signerDefaultCommand: Command = { 215 | name: "default", 216 | alias: "d", 217 | title: "Set default signer", 218 | args: [nameArg], 219 | async run(_terminal: Terminal, args: { name: string }) { 220 | new SignerRegistry().setDefault(args.name) 221 | }, 222 | } 223 | 224 | export const SignerTool: ToolController = { 225 | name: "signer", 226 | alias: "s", 227 | title: "Signer Registry", 228 | commands: [ 229 | signerGenerateCommand, 230 | signerAddCommand, 231 | signerDeleteCommand, 232 | signerListCommand, 233 | signerGetCommand, 234 | signerDefaultCommand, 235 | ], 236 | } 237 | -------------------------------------------------------------------------------- /src/controllers/sold/components.ts: -------------------------------------------------------------------------------- 1 | import { Component } from "../../core" 2 | 3 | const TOOL_FOLDER_NAME = "sold" 4 | 5 | export const components = { 6 | driver: new Component(TOOL_FOLDER_NAME, "sold", { 7 | isExecutable: true, 8 | resolveVersionRegExp: /[^0-9]*([0-9.]+)/, 9 | }), 10 | } 11 | -------------------------------------------------------------------------------- /src/controllers/sold/index.ts: -------------------------------------------------------------------------------- 1 | import chalk from "chalk" 2 | import { Command, Component, Terminal, ToolController } from "../../core" 3 | import { components } from "./components" 4 | 5 | export const soldVersionCommand: Command = { 6 | name: "version", 7 | title: "Show Sold Version", 8 | async run(terminal: Terminal): Promise { 9 | terminal.log(await Component.getInfoAll(components)) 10 | terminal.log( 11 | chalk.yellow( 12 | "\nYou can find the list of stable sold versions in Solidity Compiler changelog:" + 13 | "\nhttps://github.com/everx-labs/TVM-Solidity-Compiler/blob/master/Changelog_TON.md", 14 | ), 15 | ) 16 | }, 17 | } 18 | 19 | export const soldUpdateCommand: Command = { 20 | name: "update", 21 | title: "Update Sold Compiler Driver", 22 | args: [ 23 | { 24 | name: "force", 25 | alias: "f", 26 | title: "Force reinstall even if up to date", 27 | type: "boolean", 28 | defaultValue: "false", 29 | }, 30 | ], 31 | async run(terminal: Terminal, args: { force: boolean }): Promise { 32 | await Component.updateAll(terminal, args.force, components) 33 | }, 34 | } 35 | 36 | export const soldInstallCommand: Command = { 37 | name: "install", 38 | title: "Install latest stable sold", 39 | args: [], 40 | async run(terminal: Terminal) { 41 | await Component.ensureInstalledAll(terminal, components) 42 | terminal.log( 43 | chalk.yellow( 44 | "Do not forget to add `$HOME/.everdev/sold` to your PATH environment variable", 45 | ), 46 | ) 47 | }, 48 | } 49 | 50 | export const soldSetCommand: Command = { 51 | name: "set", 52 | title: "Change installed version", 53 | args: [ 54 | { 55 | name: "version", 56 | title: "version to install (e.g. 0.8.1 or latest)", 57 | type: "string", 58 | defaultValue: "latest", 59 | }, 60 | ], 61 | async run(terminal: Terminal, args: { version: string }): Promise { 62 | await Component.setVersions(terminal, false, components, { 63 | driver: args.version, 64 | }) 65 | }, 66 | } 67 | 68 | export const Sold: ToolController = { 69 | name: "sold", 70 | title: "Sold Compiler Driver", 71 | commands: [ 72 | soldVersionCommand, 73 | soldUpdateCommand, 74 | soldInstallCommand, 75 | soldSetCommand, 76 | ], 77 | } 78 | -------------------------------------------------------------------------------- /src/controllers/solidity/components.ts: -------------------------------------------------------------------------------- 1 | import { Component } from "../../core" 2 | 3 | const TOOL_FOLDER_NAME = "solidity" 4 | 5 | export const components = { 6 | compiler: new Component(TOOL_FOLDER_NAME, "solc", { 7 | isExecutable: true, 8 | }), 9 | 10 | linker: new Component(TOOL_FOLDER_NAME, "tvm_linker", { 11 | isExecutable: true, 12 | resolveVersionRegExp: /[^0-9]*([0-9.]+)/, 13 | }), 14 | 15 | stdlib: new (class extends Component { 16 | getSourceName(version: string): string { 17 | return `${this.name}_${version.split(".").join("_")}.tvm.gz` 18 | } 19 | 20 | async resolveVersion(downloadedVersion: string): Promise { 21 | return downloadedVersion 22 | } 23 | 24 | async loadAvailableVersions(): Promise { 25 | return components.compiler.loadAvailableVersions() 26 | } 27 | })(TOOL_FOLDER_NAME, "stdlib_sol", { 28 | targetName: "stdlib_sol.tvm", 29 | hidden: true, 30 | }), 31 | } 32 | -------------------------------------------------------------------------------- /src/controllers/solidity/snippets.ts: -------------------------------------------------------------------------------- 1 | export const BasicContract = ` 2 | /** 3 | * This file was generated by EverDev. 4 | * EverDev is a part of EVER OS (see https://everos.dev). 5 | */ 6 | pragma ton-solidity >= 0.67.0; 7 | pragma AbiHeader expire; 8 | 9 | // This is class that describes you smart contract. 10 | contract {name} { 11 | // Contract can have an instance variables. 12 | // In this example instance variable \`timestamp\` is used to store the time of \`constructor\` or \`touch\` 13 | // function call 14 | uint32 public timestamp; 15 | 16 | // Contract can have a \`constructor\` – function that will be called when contract will be deployed to the blockchain. 17 | // In this example constructor adds current time to the instance variable. 18 | // All contracts need call tvm.accept(); for succeeded deploy 19 | constructor() { 20 | // Check that contract's public key is set 21 | require(tvm.pubkey() != 0, 101); 22 | // Check that message has signature (msg.pubkey() is not zero) and 23 | // message is signed with the owner's private key 24 | require(msg.pubkey() == tvm.pubkey(), 102); 25 | // The current smart contract agrees to buy some gas to finish the 26 | // current transaction. This actions required to process external 27 | // messages, which bring no value (henceno gas) with themselves. 28 | tvm.accept(); 29 | 30 | timestamp = block.timestamp; 31 | } 32 | 33 | function renderHelloWorld () public pure returns (string) { 34 | return 'helloWorld'; 35 | } 36 | 37 | // Updates variable \`timestamp\` with current blockchain time. 38 | function touch() external { 39 | // Each function that accepts external message must check that 40 | // message is correctly signed. 41 | require(msg.pubkey() == tvm.pubkey(), 102); 42 | // Tells to the TVM that we accept this message. 43 | tvm.accept(); 44 | // Update timestamp 45 | timestamp = block.timestamp; 46 | } 47 | 48 | function sendValue(address dest, uint128 amount, bool bounce) public view { 49 | require(msg.pubkey() == tvm.pubkey(), 102); 50 | tvm.accept(); 51 | // It allows to make a transfer with arbitrary settings 52 | dest.transfer(amount, bounce, 0); 53 | } 54 | } 55 | ` 56 | -------------------------------------------------------------------------------- /src/controllers/ts/create.ts: -------------------------------------------------------------------------------- 1 | import path from "path" 2 | import { Command, Terminal } from "../../core" 3 | import { changeExt, uniqueFilePath } from "../../core/utils" 4 | 5 | export const tsCreateCommand: Command = { 6 | name: "create", 7 | title: "Create Test", 8 | args: [ 9 | { 10 | name: "folder", 11 | type: "folder", 12 | }, 13 | ], 14 | async run(terminal: Terminal, args: { file: string }) { 15 | const testFilePath = uniqueFilePath( 16 | path.dirname(args.file), 17 | changeExt(path.basename(args.file), "{}.test.py"), 18 | ) 19 | terminal.log( 20 | `${testFilePath} has created for ${path.basename(args.file)}`, 21 | ) 22 | }, 23 | } 24 | -------------------------------------------------------------------------------- /src/controllers/ts/index.ts: -------------------------------------------------------------------------------- 1 | import { tsCreateCommand } from "./create" 2 | import { tsRunCommand } from "./run" 3 | import { tsInspectCommand } from "./inspect" 4 | import { ToolController } from "../../core" 5 | 6 | export const TestSuite: ToolController = { 7 | name: "ts", 8 | title: "Smart Contract Test Suite", 9 | commands: [tsCreateCommand, tsRunCommand, tsInspectCommand], 10 | } 11 | -------------------------------------------------------------------------------- /src/controllers/ts/inspect.ts: -------------------------------------------------------------------------------- 1 | import path from "path" 2 | import { Command, Terminal } from "../../core" 3 | 4 | export const tsInspectCommand: Command = { 5 | name: "inspect", 6 | title: "Inspect Test Result", 7 | args: [ 8 | { 9 | isArg: true, 10 | name: "file", 11 | type: "file", 12 | nameRegExp: /\.test\.py$/, 13 | }, 14 | ], 15 | async run(terminal: Terminal, args: { file: string }) { 16 | terminal.log(`${path.basename(args.file)} tests passed`) 17 | }, 18 | } 19 | -------------------------------------------------------------------------------- /src/controllers/ts/run.ts: -------------------------------------------------------------------------------- 1 | import path from "path" 2 | import { Command, Terminal } from "../../core" 3 | export const tsRunCommand: Command = { 4 | name: "run", 5 | title: "Run Test", 6 | args: [ 7 | { 8 | isArg: true, 9 | name: "file", 10 | type: "file", 11 | nameRegExp: /\.test\.py$/, 12 | }, 13 | ], 14 | async run(terminal: Terminal, args: { file: string }) { 15 | terminal.log(`${path.basename(args.file)} test passed`) 16 | }, 17 | } 18 | -------------------------------------------------------------------------------- /src/controllers/ts4/components.ts: -------------------------------------------------------------------------------- 1 | import os from "os" 2 | import { Component, Terminal } from "../../core" 3 | import { 4 | compareVersions, 5 | httpsGetJson, 6 | nullTerminal, 7 | run, 8 | } from "../../core/utils" 9 | 10 | const TS4_PKG = "tonos-ts4" 11 | const PYPI = `https://pypi.org/pypi/${TS4_PKG}/json` 12 | const currentOS = os.type() 13 | const [PYTHON, PIP] = ["Linux", "Darwin"].includes(currentOS) 14 | ? ["python3", "pip3"] 15 | : ["python", "pip"] 16 | 17 | export const components = { 18 | ts4: new (class extends Component { 19 | async getCurrentVersion(): Promise { 20 | let version 21 | 22 | try { 23 | const output = await run( 24 | PIP, 25 | ["show", TS4_PKG], 26 | {}, 27 | nullTerminal, 28 | ) 29 | version = output 30 | .split(os.EOL) 31 | .find(line => /^Version:/.exec(line)) 32 | } catch { 33 | // TODO handle the lack of 'pip' 34 | console.debug(`Package ${TS4_PKG} not found`) 35 | } 36 | return version ? version.split(/:\s*/)[1] : "" 37 | } 38 | 39 | async ensureVersion( 40 | terminal: Terminal, 41 | force: boolean, 42 | requiredVersion?: string, 43 | ): Promise { 44 | const current = await this.getCurrentVersion() 45 | if (!force && current !== "" && !requiredVersion) { 46 | return false 47 | } 48 | let version = (requiredVersion ?? "latest").toLowerCase() 49 | if (!force && version === current) { 50 | return false 51 | } 52 | const available = await this.loadAvailableVersions() 53 | if (version === "latest") { 54 | version = available[0] 55 | } else { 56 | if (!available.includes(version)) { 57 | throw new Error(`Invalid ${this.name} version ${version}`) 58 | } 59 | } 60 | if (!force && version === current) { 61 | return false 62 | } 63 | const pkg = TS4_PKG + (version ? `==${version}` : "") 64 | const output = await run( 65 | PIP, 66 | ["install", "-U", pkg], 67 | {}, 68 | nullTerminal, 69 | ) 70 | const successPattern = `Successfully installed ${TS4_PKG}-${version}` 71 | const isOk = output 72 | .split(os.EOL) 73 | .find(line => line === successPattern) 74 | 75 | if (!isOk) { 76 | terminal.writeError(output) 77 | return false 78 | } else { 79 | terminal.log(successPattern) 80 | } 81 | 82 | return true 83 | } 84 | 85 | async loadAvailableVersions(): Promise { 86 | const info = await httpsGetJson(PYPI) 87 | const versions = Object.keys(info.releases) 88 | .filter(v => /^(\d+\.){2}\d+$/.test(v)) 89 | .sort(compareVersions) 90 | .reverse() 91 | return versions 92 | } 93 | })("", PYTHON, { 94 | isExecutable: true, 95 | runGlobally: true, 96 | }), 97 | } 98 | -------------------------------------------------------------------------------- /src/controllers/ts4/index.ts: -------------------------------------------------------------------------------- 1 | import path from "path" 2 | import { Command, Component, Terminal, ToolController } from "../../core" 3 | import { uniqueFilePath, writeTextFile } from "../../core/utils" 4 | import { components } from "./components" 5 | import { BasicTest } from "./snippets" 6 | 7 | export const ts4VersionCommand: Command = { 8 | name: "version", 9 | title: "Show installed and available versions", 10 | async run(terminal: Terminal): Promise { 11 | terminal.log(await Component.getInfoAll(components)) 12 | }, 13 | } 14 | 15 | export const ts4InstallCommand: Command = { 16 | name: "install", 17 | title: "Install a specific release of TestSuite4", 18 | args: [ 19 | { 20 | isArg: true, 21 | name: "version", 22 | type: "string", 23 | title: "TestSuite4 version (semver compatible)", 24 | }, 25 | ], 26 | async run(terminal: Terminal, args: { version: string }) { 27 | const versions: { 28 | ts4?: string 29 | } = { 30 | ...(args.version !== "" ? { ts4: args.version } : {}), 31 | } 32 | await Component.setVersions(terminal, false, components, versions) 33 | }, 34 | } 35 | 36 | export const ts4UpdateCommand: Command = { 37 | name: "update", 38 | title: "Update to the latest version", 39 | async run(terminal: Terminal): Promise { 40 | await Component.updateAll(terminal, true, components) 41 | }, 42 | } 43 | 44 | export const ts4CreateCommand: Command = { 45 | name: "create", 46 | title: "Create TestSuite4 test", 47 | args: [ 48 | { 49 | isArg: true, 50 | name: "name", 51 | title: "Test script name", 52 | type: "string", 53 | defaultValue: "Test", 54 | }, 55 | { 56 | name: "folder", 57 | type: "folder", 58 | title: "Target folder (current is default)", 59 | }, 60 | ], 61 | async run(terminal: Terminal, args: { name: string; folder: string }) { 62 | const filePath = uniqueFilePath(args.folder, `${args.name}{}.py`) 63 | const text = BasicTest.split("{name}").join(args.name) 64 | writeTextFile(filePath, text) 65 | terminal.log( 66 | `TestSuite4 test script ${path.basename(filePath)} created.`, 67 | ) 68 | }, 69 | } 70 | 71 | export const ts4RunCommand: Command = { 72 | name: "run", 73 | title: "Run TestSuite4's test", 74 | args: [ 75 | { 76 | isArg: true, 77 | name: "file", 78 | type: "file", 79 | title: "Test", 80 | nameRegExp: /\.py$/i, 81 | }, 82 | ], 83 | async run(terminal: Terminal, args: { file: string }): Promise { 84 | const ext = path.extname(args.file) 85 | if (ext !== ".py") { 86 | terminal.log(`Choose file *.py`) 87 | return 88 | } 89 | await Component.ensureInstalledAll(terminal, components) 90 | const fileDir = path.dirname(args.file) 91 | const fileName = path.basename(args.file) 92 | 93 | await components.ts4.run(terminal, fileDir, [fileName]) 94 | }, 95 | } 96 | 97 | export const TestSuite4: ToolController = { 98 | name: "ts4", 99 | title: "TestSuite4 framework", 100 | commands: [ 101 | ts4VersionCommand, 102 | ts4InstallCommand, 103 | ts4UpdateCommand, 104 | ts4CreateCommand, 105 | ts4RunCommand, 106 | ], 107 | } 108 | -------------------------------------------------------------------------------- /src/controllers/ts4/snippets.ts: -------------------------------------------------------------------------------- 1 | export const BasicTest = `""" 2 | This file was generated by EverDev. 3 | EverDev is a part of EVER OS (see https://everos.dev). 4 | """ 5 | from tonos_ts4 import ts4 6 | 7 | 8 | def test(): 9 | # Place your code of the test here 10 | print('Ok') 11 | 12 | 13 | if __name__ == '__main__': 14 | # Initialize TS4 by specifying where the artifacts of the used contracts are located 15 | # verbose: toggle to print additional execution info 16 | ts4.init('contracts/', verbose = True) 17 | 18 | test() 19 | 20 | ` 21 | -------------------------------------------------------------------------------- /src/core/index.ts: -------------------------------------------------------------------------------- 1 | import path from "path" 2 | import os from "os" 3 | import { TonClient } from "@eversdk/core" 4 | import { libNode } from "@eversdk/lib-node" 5 | 6 | export { ComponentOptions, Component } from "./component" 7 | 8 | export type EverdevOptions = { 9 | home?: string 10 | } 11 | 12 | /** 13 | * Terminal object is implemented by `everdev` and passed to the controller's command handlers. 14 | * Command handler uses terminal to output some human readable information related to command execution. 15 | */ 16 | export interface Terminal { 17 | /** 18 | * Print line. Provided argument will be converted to strings and separated by space. 19 | * @param args values to print. 20 | */ 21 | log(...args: unknown[]): void 22 | 23 | /** 24 | * Prints text. No line feeds will be produced. 25 | * @param text Text that will be printed. 26 | */ 27 | write(text: string): void 28 | 29 | /** 30 | * Prints error text. In case of CLI this text will be printed to stderr. 31 | * @param text Error text. 32 | */ 33 | writeError(text: string): void 34 | } 35 | 36 | export type CommandArgVariant = { 37 | value: string 38 | description?: string 39 | } 40 | 41 | /** 42 | * Command argument. 43 | */ 44 | export type BaseCommandArg = { 45 | /** 46 | * Name of the command. 47 | */ 48 | name: string 49 | /** 50 | * Short alias for an optional arg name. 51 | */ 52 | alias?: string 53 | /** 54 | * Title of the argument. 55 | */ 56 | title?: string 57 | /** 58 | * Description of the argument. 59 | */ 60 | description?: string 61 | /** 62 | * Default value for the command. If missing then the user must specify value for this argument. 63 | */ 64 | defaultValue?: string 65 | /** 66 | * Determine that the arg is a CLI arg (not an option). 67 | */ 68 | isArg?: boolean 69 | /* 70 | * Gredy argument value can include several words 71 | */ 72 | greedy?: boolean 73 | 74 | /** 75 | * Get available CLI argument variants 76 | */ 77 | getVariants?(): CommandArgVariant[] | Promise 78 | } 79 | 80 | /** 81 | * Argument with path to the file. 82 | */ 83 | export type FileArg = BaseCommandArg & { 84 | type: "file" 85 | /** 86 | * Determine files matched for this argument. 87 | */ 88 | nameRegExp: RegExp 89 | } 90 | 91 | /** 92 | * Command argument with path to the folder. 93 | */ 94 | export type FolderArg = BaseCommandArg & { 95 | type: "folder" 96 | } 97 | 98 | /** 99 | * String command argument. 100 | */ 101 | export type StringArg = BaseCommandArg & { 102 | type: "string" 103 | } 104 | 105 | /** 106 | * Boolean command argument. 107 | */ 108 | export type BooleanArg = BaseCommandArg & { 109 | type: "boolean" 110 | } 111 | 112 | /** 113 | * Command argument. 114 | */ 115 | export type CommandArg = FileArg | FolderArg | StringArg | BooleanArg 116 | 117 | /** 118 | * Command definition. 119 | */ 120 | export interface Command { 121 | /** 122 | * Command name. 123 | */ 124 | name: string 125 | /** 126 | * Command alias. Used in cli instead of full command name.. 127 | */ 128 | alias?: string 129 | /** 130 | * Command Title. Used in CLI short help and as a menu titles in IDE. 131 | */ 132 | title: string 133 | /** 134 | * Description of the command. 135 | */ 136 | description?: string 137 | /** 138 | * Command argument definitions. 139 | */ 140 | args?: CommandArg[] 141 | 142 | /** 143 | * Command handler. 144 | * @param terminal Terminal object provided by `everdev`. 145 | * Handler must print all human readable output using this terminal. 146 | * @param args Actual command arguments provided by user according to argument definitions. 147 | */ 148 | run(terminal: Terminal, args: unknown): Promise 149 | } 150 | 151 | type NameAlias = { 152 | name: string 153 | alias?: string 154 | } 155 | 156 | export function matchName( 157 | x: NameAlias, 158 | test: string | undefined | null, 159 | ): boolean { 160 | test = (test || "").toLowerCase() 161 | return x.name === test || x.alias === test 162 | } 163 | 164 | export function nameInfo( 165 | x: NameAlias, 166 | namePrefix = "", 167 | aliasPrefix = "", 168 | ): string { 169 | return x.alias 170 | ? `${namePrefix}${x.name}, ${aliasPrefix}${x.alias}` 171 | : `${namePrefix}${x.name}` 172 | } 173 | 174 | /** 175 | * Interface to be implemented by every controller. 176 | */ 177 | export interface ToolController { 178 | /** 179 | * Tool name. This is an identifier. Used only in configs. 180 | */ 181 | name: string 182 | /** 183 | * Tool alias. Used in cli instead of full tool name.. 184 | */ 185 | alias?: string 186 | /** 187 | * Tool title. Human readable name of the tool. 188 | */ 189 | title?: string 190 | /** 191 | * Tool description. 192 | */ 193 | description?: string 194 | /** 195 | * Commands provided by tool controller. 196 | */ 197 | commands: Command[] 198 | } 199 | 200 | const config = { 201 | home: path.resolve(os.homedir(), ".everdev"), 202 | } 203 | 204 | export function everdevInit(options?: EverdevOptions) { 205 | TonClient.useBinaryLibrary(libNode) 206 | config.home = options?.home ?? config.home 207 | } 208 | 209 | export function everdevDone() { 210 | TonClient.default.close() 211 | } 212 | 213 | /** 214 | * Home directory where tool must store all tool related resources. 215 | */ 216 | export function everdevHome() { 217 | return config.home 218 | } 219 | 220 | export async function getArgVariants( 221 | arg: CommandArg, 222 | ): Promise { 223 | return arg.getVariants === undefined ? undefined : arg.getVariants() 224 | } 225 | -------------------------------------------------------------------------------- /src/core/known-contracts.ts: -------------------------------------------------------------------------------- 1 | import { Account, ContractPackage } from "@eversdk/appkit" 2 | import { TonClient, AbiContract, Signer, abiContract } from "@eversdk/core" 3 | 4 | import path from "path" 5 | import fs from "fs" 6 | import { getParsedAccount } from "../controllers/contract/accounts" 7 | 8 | export type KnownContract = { 9 | name: string 10 | } & ContractPackage 11 | 12 | export async function knownContractFromAddress( 13 | client: TonClient, 14 | name: string, 15 | address: string, 16 | ): Promise { 17 | const info = await getParsedAccount( 18 | await new Account( 19 | { abi: {} }, 20 | { 21 | client, 22 | address, 23 | }, 24 | ), 25 | ) 26 | const codeHash = info.code_hash 27 | if (!codeHash) { 28 | throw new Error(`${name} ${address} has no code deployed.`) 29 | } 30 | return knownContractFromCodeHash(codeHash, name, address) 31 | } 32 | 33 | export async function knownContractAddress( 34 | client: TonClient, 35 | name: KnownContractName, 36 | signer: Signer, 37 | ): Promise { 38 | const contract = KnownContracts[name] 39 | if (!contract || !contract.tvc) { 40 | throw new Error( 41 | `Can not resolve giver address: unknown giver type ${name}.`, 42 | ) 43 | } 44 | return ( 45 | await client.abi.encode_message({ 46 | abi: abiContract(contract.abi), 47 | deploy_set: { 48 | tvc: contract.tvc, 49 | }, 50 | signer, 51 | }) 52 | ).address 53 | } 54 | 55 | export function knownContractByName(name: KnownContractName): KnownContract { 56 | if (!(name in KnownContracts)) { 57 | throw new Error(`Unknown contract type ${name}.`) 58 | } 59 | return KnownContracts[name] 60 | } 61 | 62 | export function knownContractFromCodeHash( 63 | codeHash: string, 64 | name: string, 65 | address?: string, 66 | ): KnownContract { 67 | const contract = KnownContractsByCodeHash[codeHash] 68 | if (!contract) { 69 | if (address) { 70 | throw new Error( 71 | `${name} ${address} has unknown code hash ${codeHash}.`, 72 | ) 73 | } 74 | throw new Error(`Unknown code hash ${codeHash} for ${name}.`) 75 | } 76 | return contract 77 | } 78 | 79 | function contractsFile(name: string): string { 80 | return path.resolve(__dirname, "..", "..", "contracts", name) 81 | } 82 | 83 | export function loadAbi(name: string): AbiContract { 84 | return JSON.parse( 85 | fs.readFileSync(contractsFile(`${name}.abi.json`), "utf8"), 86 | ) 87 | } 88 | 89 | export function loadTvc(name: string): string | undefined { 90 | const filePath = contractsFile(`${name}.tvc`) 91 | return fs.existsSync(filePath) 92 | ? fs.readFileSync(filePath, "base64") 93 | : undefined 94 | } 95 | 96 | function knownContract(name: string): KnownContract { 97 | return { 98 | name, 99 | abi: loadAbi(name), 100 | tvc: loadTvc(name), 101 | } 102 | } 103 | 104 | export const KnownContracts = { 105 | GiverV1: knownContract("GiverV1"), 106 | GiverV2: knownContract("GiverV2"), 107 | GiverV3: knownContract("GiverV3"), 108 | SetcodeMultisigWallet: knownContract("SetcodeMultisigWallet"), 109 | MsigV2: knownContract("MsigV2"), 110 | SafeMultisigWallet: knownContract("SafeMultisigWallet"), 111 | } 112 | 113 | export type KnownContractName = keyof typeof KnownContracts 114 | export const KnownContractNames: string[] = Object.keys(KnownContracts) 115 | 116 | const KnownContractsByCodeHash: { [codeHash: string]: KnownContract } = { 117 | "4e92716de61d456e58f16e4e867e3e93a7548321eace86301b51c8b80ca6239b": 118 | KnownContracts.GiverV2, 119 | ccbfc821853aa641af3813ebd477e26818b51e4ca23e5f6d34509215aa7123d9: 120 | KnownContracts.GiverV2, 121 | "5534bff04d2d0a14bb2257ec23027947c722159486ceff9e408d6a4d796a0989": 122 | KnownContracts.GiverV3, 123 | e2b60b6b602c10ced7ea8ede4bdf96342c97570a3798066f3fb50a4b2b27a208: 124 | KnownContracts.SetcodeMultisigWallet, 125 | "207dc560c5956de1a2c1479356f8f3ee70a59767db2bf4788b1d61ad42cdad82": 126 | KnownContracts.SetcodeMultisigWallet, 127 | d66d198766abdbe1253f3415826c946c371f5112552408625aeb0b31e0ef2df3: 128 | KnownContracts.MsigV2, 129 | "80d6c47c4a25543c9b397b71716f3fae1e2c5d247174c52e2c19bd896442b105": 130 | KnownContracts.SafeMultisigWallet, 131 | } 132 | -------------------------------------------------------------------------------- /src/core/solFileResolvers.ts: -------------------------------------------------------------------------------- 1 | import fs from "fs" 2 | import { ContractPackage } from "@eversdk/appkit" 3 | import { findExisting } from "./utils" 4 | import { SOLIDITY_FILE } from "../controllers/solidity" 5 | import os from "os" 6 | 7 | type ResolvedContractPackage = { 8 | package: ContractPackage 9 | abiPath: string 10 | tvcPath?: string 11 | } 12 | 13 | export function resolveContract(filePath: string): ResolvedContractPackage { 14 | if (filePath.startsWith("~")) { 15 | filePath = `${os.homedir()}${filePath.substring(1)}` 16 | } 17 | filePath = filePath.trim() 18 | const lowered = filePath.toLowerCase() 19 | let basePath 20 | if ( 21 | lowered.endsWith(".tvc") || 22 | lowered.endsWith(".abi") || 23 | SOLIDITY_FILE.nameRegEx.test(lowered) 24 | ) { 25 | basePath = filePath.slice(0, -4) 26 | } else if (lowered.endsWith(".abi.json")) { 27 | basePath = filePath.slice(0, -9) 28 | } else { 29 | basePath = filePath 30 | } 31 | const tvcPath = findExisting([`${basePath}.tvc`]) 32 | const abiPath = 33 | findExisting([`${basePath}.abi.json`, `${basePath}.abi`]) ?? "" 34 | const tvc = tvcPath 35 | ? fs.readFileSync(tvcPath).toString("base64") 36 | : undefined 37 | const abi = 38 | abiPath !== "" 39 | ? JSON.parse(fs.readFileSync(abiPath, "utf8")) 40 | : undefined 41 | if (!abi) { 42 | throw new Error( 43 | `You have specified "${filePath}" as a contract file name, but a corresponding ABI file is missing. ABI file must have an extension ".abi" or ".abi.json".`, 44 | ) 45 | } 46 | return { 47 | package: { 48 | abi, 49 | tvc, 50 | }, 51 | abiPath, 52 | tvcPath, 53 | } 54 | } 55 | 56 | export function resolveTvcAsBase64(filePath: string): string { 57 | const tvcPath = findExisting([filePath, `${filePath}.tvc`]) 58 | if (tvcPath) { 59 | return fs.readFileSync(tvcPath).toString("base64") 60 | } else { 61 | throw Error(`File ${filePath} not exists`) 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/everdev/checkNewVersion.ts: -------------------------------------------------------------------------------- 1 | import * as path from "path" 2 | 3 | import { everdevHome } from "../core/index" 4 | import { 5 | compareVersions, 6 | touch, 7 | writeTextFile, 8 | readTextFileSyncOnce, 9 | getLatestFromNmp, 10 | } from "../core/utils" 11 | 12 | const LAST_RUN_FILE = ".lastrun" 13 | const LATEST_VERSION_FILE = ".latest" 14 | 15 | /* 16 | * Checks if this is the first run in the last 24 hours 17 | * and touches the LAST_RUN_FILE 18 | */ 19 | function isFirstRun(): boolean { 20 | const lastrunFile = path.resolve(everdevHome(), LAST_RUN_FILE) 21 | const lastrunTS: Date | undefined = touch(lastrunFile) 22 | return !lastrunTS || lastrunTS.getTime() < Date.now() - 24 * 3600 * 1000 23 | } 24 | 25 | export async function createLatestVerFile(pkgName: string): Promise { 26 | if (isFirstRun()) { 27 | const version: string = await getLatestFromNmp(pkgName) 28 | writeTextFile(path.resolve(everdevHome(), LATEST_VERSION_FILE), version) 29 | } 30 | } 31 | 32 | export function getUpdateIsAvailableMsg( 33 | pkgName: string, 34 | pkgVer: string, 35 | ): string { 36 | const latestVer: string = readTextFileSyncOnce( 37 | path.resolve(everdevHome(), LATEST_VERSION_FILE), 38 | ) 39 | if (latestVer && compareVersions(latestVer, pkgVer) > 0) { 40 | const sep = "********************************************" 41 | return [ 42 | "", 43 | sep, 44 | `A new version of ${pkgName} ${latestVer} is available!`, 45 | sep, 46 | `Installed version is ${pkgVer}`, 47 | `Update it with "npm update -g ${pkgName}", or use precompiled binaries:`, 48 | `https://docs.everos.dev/everdev/#install`, 49 | "", 50 | ].join("\n") 51 | } else { 52 | return "" 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/everdev/help.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Command, 3 | CommandArg, 4 | getArgVariants, 5 | nameInfo, 6 | Terminal, 7 | ToolController, 8 | } from "../core" 9 | import { breakWords, formatTable } from "../core/utils" 10 | import fs from "fs" 11 | import path from "path" 12 | import { controllers } from "../controllers" 13 | 14 | async function printCommandUsage( 15 | terminal: Terminal, 16 | controller: ToolController, 17 | command: Command, 18 | ) { 19 | let usageArgs = "" 20 | const options: CommandArg[] = [] 21 | const args: CommandArg[] = [] 22 | for (const arg of command.args ?? []) { 23 | if (arg.isArg) { 24 | usageArgs += ` ${arg.name}` 25 | args.push(arg) 26 | } else { 27 | options.push(arg) 28 | } 29 | } 30 | if (options.length > 0) { 31 | usageArgs += ` [options]` 32 | } 33 | terminal.log(`Use: everdev ${controller.name} ${command.name}${usageArgs}`) 34 | if (args.length > 0) { 35 | terminal.log("Args:") 36 | terminal.log(formatTable(args.map(x => [" ", x.name, x.title]))) 37 | } 38 | terminal.log("Options:") 39 | const optionsTable = [[" ", "--help, -h", "Show command usage"]] 40 | for (const option of options) { 41 | optionsTable.push([ 42 | " ", 43 | nameInfo(option, "--", "-"), 44 | option.title ?? "", 45 | ]) 46 | if (option.description) { 47 | breakWords(option.description, 60) 48 | .split("\n") 49 | .forEach(line => { 50 | optionsTable.push(["", "", line]) 51 | }) 52 | } 53 | const variants = await getArgVariants(option) 54 | if (variants) { 55 | optionsTable.push(["", "", "Variants:"]) 56 | formatTable(variants.map(x => [x.value, x.description])) 57 | .split("\n") 58 | .forEach(line => { 59 | optionsTable.push(["", "", line]) 60 | }) 61 | } 62 | } 63 | terminal.log(formatTable(optionsTable)) 64 | } 65 | 66 | function printControllerUsage(terminal: Terminal, controller: ToolController) { 67 | terminal.log( 68 | formatTable(controller.commands.map(x => [" ", nameInfo(x), x.title])), 69 | ) 70 | } 71 | 72 | export async function printUsage( 73 | terminal: Terminal, 74 | controller?: ToolController, 75 | command?: Command, 76 | ) { 77 | const pkg = JSON.parse( 78 | fs.readFileSync( 79 | path.resolve(__dirname, "..", "..", "package.json"), 80 | "utf8", 81 | ), 82 | ) 83 | terminal.log(`EverDev Version: ${pkg.version}`) 84 | if (controller && command) { 85 | await printCommandUsage(terminal, controller, command) 86 | return 87 | } 88 | terminal.log( 89 | `Use: everdev ${controller?.name ?? "tool"} ${ 90 | command?.name ?? "command" 91 | } args [options]`, 92 | ) 93 | terminal.log(`Options:`) 94 | terminal.log(` --help, -h Show command usage`) 95 | if (controller) { 96 | terminal.log("Commands:") 97 | printControllerUsage(terminal, controller) 98 | return 99 | } else { 100 | terminal.log(` --version, -v Show everdev version`) 101 | } 102 | terminal.log("Tools:") 103 | const rows: string[][] = [] 104 | rows.push([" ", "info", "Summary of all tools"]) 105 | controllers.forEach(controller => { 106 | rows.push([" ", nameInfo(controller), controller.title ?? ""]) 107 | }) 108 | rows.push([" ", "update", "Update everdev utility"]) 109 | terminal.log(formatTable(rows)) 110 | } 111 | -------------------------------------------------------------------------------- /src/everdev/info.ts: -------------------------------------------------------------------------------- 1 | import fs from "fs" 2 | import path from "path" 3 | import { controllers } from "../controllers" 4 | import { Command, Terminal, ToolController } from "../core" 5 | 6 | function findInfoCommand( 7 | controller: ToolController, 8 | name: string, 9 | ): { command: Command; args: Record } | undefined { 10 | if (controller.name === "contract" && name === "info") { 11 | return undefined 12 | } 13 | const command = controller.commands.find(x => x.name == name) 14 | if (!command) { 15 | return undefined 16 | } 17 | const args: Record = {} 18 | for (const arg of command.args ?? []) { 19 | if (arg.defaultValue === undefined) { 20 | return undefined 21 | } 22 | args[arg.name] = arg.defaultValue 23 | } 24 | return { 25 | command, 26 | args, 27 | } 28 | } 29 | 30 | export async function printSummaryInfo(terminal: Terminal) { 31 | const pkg = JSON.parse( 32 | fs.readFileSync( 33 | path.resolve(__dirname, "..", "..", "package.json"), 34 | "utf8", 35 | ), 36 | ) 37 | terminal.log() 38 | terminal.log(`${pkg.name} version: ${pkg.version}`) 39 | for (const controller of controllers) { 40 | const info = 41 | findInfoCommand(controller, "info") ?? 42 | findInfoCommand(controller, "list") ?? 43 | findInfoCommand(controller, "version") 44 | if (info) { 45 | terminal.log() 46 | terminal.log(controller.title) 47 | terminal.log() 48 | try { 49 | await info.command.run(terminal, info.args) 50 | } catch (error) { 51 | terminal.writeError(`${error}`) 52 | } 53 | } 54 | } 55 | terminal.log() 56 | } 57 | -------------------------------------------------------------------------------- /src/everdev/update.ts: -------------------------------------------------------------------------------- 1 | import fs from "fs" 2 | import path from "path" 3 | import { run } from "../core/utils" 4 | import { Terminal } from "../core" 5 | 6 | export async function update(terminal: Terminal) { 7 | const pkg = JSON.parse( 8 | fs.readFileSync( 9 | path.resolve(__dirname, "..", "..", "package.json"), 10 | "utf8", 11 | ), 12 | ) 13 | terminal.log(`Updating globally-installed package ${pkg.name}`) 14 | try { 15 | await run("npm", ["update", "-g", pkg.name], {}, terminal) 16 | } catch (err: any) { 17 | const message = [ 18 | `An error occurred while trying to update ${pkg.name} globally`, 19 | "", 20 | `If you installed everdev with sudo, run "sudo update -g ${pkg.name}"`, 21 | "", 22 | "Advice: it is always possible to install packages globally without sudo,", 23 | "here you can learn how: https://github.com/nvm-sh/nvm#node-version-manager---", 24 | ].join("\n") 25 | throw Error(message) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | export { controllers, runCommand } from "./controllers/index" 2 | export * from "./core/utils" 3 | export * from "./core/index" 4 | export * as cli from "./cli/index" 5 | -------------------------------------------------------------------------------- /src/rewriteKnownErrors.ts: -------------------------------------------------------------------------------- 1 | export function rewriteKnownErrors(err: unknown) { 2 | let presumableError = "" 3 | 4 | if (err instanceof Error) { 5 | if (/dyld: Symbol not found/.test(err.message)) { 6 | presumableError = "Wrong Mac OS version" 7 | } 8 | 9 | if (presumableError !== "") { 10 | err.message = `${presumableError} (presumably)\nOriginal error:${err.message}` 11 | } 12 | } 13 | return err 14 | } 15 | -------------------------------------------------------------------------------- /src/server/api/tondev.graphql: -------------------------------------------------------------------------------- 1 | type Query { 2 | " Returns EverDev home folder" 3 | home: String 4 | 5 | " Returns EverDev tool list" 6 | tools: [Tool] 7 | 8 | " Returns variants for command arg" 9 | commandArgVariants(tool: String, command: String, arg: String): [ArgVariant] 10 | 11 | " Validates task input values" 12 | validateInput(task: ID, input: ID, values: [InputArgValue]): [ValidateArgResult] 13 | 14 | " Returns input arg variants" 15 | inputVariants(task: ID, input: ID, arg: String, values: [InputArgValue]): [ArgVariant] 16 | 17 | " Returns running tasks" 18 | runningTasks(tool: String, first: Int, after: Int): [Task] 19 | 20 | " Returns recently ran tasks" 21 | recentTasks(tool: String, first: Int, after: Int): [Task] 22 | } 23 | 24 | type Subscription { 25 | runTask(tool: String, command: String, args: [InputArgValue]): [Terminal] 26 | listenTask(task: String): [Terminal] 27 | } 28 | 29 | type Mutation { 30 | " Accepts requested input values" 31 | acceptInput(terminalId: ID, inputId: ID, values: [InputArgValue]): [ValidateArgResult] 32 | } 33 | 34 | type Task { 35 | id: ID, 36 | time: Float, 37 | tool: String, 38 | command: String, 39 | args: [ArgValue], 40 | } 41 | 42 | type Terminal { 43 | start: TerminalStart 44 | stop: TerminalStop 45 | message: TerminalMessage 46 | input: TerminalInput 47 | } 48 | 49 | " Notify that task terminal has started with specified task id." 50 | type TerminalStart { 51 | " Task id" 52 | task: ID 53 | } 54 | 55 | " Notify that task terminal has stopped." 56 | type TerminalStop { 57 | } 58 | 59 | " Show/update message to user." 60 | type TerminalMessage { 61 | " Message Id" 62 | message: ID, 63 | " Target channel" 64 | channel: TerminalMessageChannel 65 | " Message text" 66 | text: String 67 | " Text format (default is TEXT)" 68 | format: TerminalMessageFormat 69 | } 70 | 71 | " Prints text. No line feeds will be produced." 72 | type TerminalInput { 73 | " Input id (used in validateInput and acceptInput)" 74 | input: ID 75 | " Input sections" 76 | sections: [TerminalInputSection] 77 | } 78 | 79 | " Input section." 80 | type TerminalInputSection { 81 | " Section title" 82 | title: String 83 | " Input fields" 84 | fields: [CommandArg] 85 | } 86 | 87 | enum TerminalMessageChannel { 88 | OUTPUT, 89 | ERROR, 90 | } 91 | 92 | enum TerminalMessageFormat { 93 | TEXT, 94 | MD, 95 | } 96 | 97 | type ArgVariant { 98 | value: String 99 | description: String 100 | } 101 | 102 | enum ArgType { 103 | STRING, 104 | BOOLEAN, 105 | NUMBER, 106 | FILE, 107 | FOLDER 108 | } 109 | 110 | enum ArgVariants { 111 | NONE, 112 | SUGGESTED, 113 | RESTRICTED, 114 | } 115 | 116 | " Command argument" 117 | type CommandArg { 118 | " Type of the value" 119 | type: ArgType, 120 | " Name of the argument" 121 | name: String 122 | " Short alias for an optional arg name" 123 | alias: String 124 | " Title of the argument" 125 | title: String 126 | " Description of the argument" 127 | description: String, 128 | " Default value for the command. If missing then the user must specify value for this argument." 129 | defaultValue: String 130 | " Determine that the arg is a CLI arg (not an option)" 131 | isArg: Boolean 132 | 133 | " Determine is this arg has predefined values" 134 | variants: ArgVariants, 135 | 136 | " Additional properties for file type arg" 137 | file: FileArg, 138 | } 139 | 140 | " Argument with path of the file" 141 | type FileArg { 142 | " Determine files matched for this argument" 143 | nameRegExp: String 144 | } 145 | 146 | " Command definition" 147 | type Command { 148 | " Command name" 149 | name: String 150 | " Command alias. Used in cli instead of full command name." 151 | alias: String 152 | " Command Title. Used in CLI short help and as a menu titles in IDE." 153 | title: String 154 | " Description of the command" 155 | description: String 156 | " Command argument definitions" 157 | args: [CommandArg] 158 | } 159 | 160 | " EverDev tool" 161 | type Tool { 162 | " Tool name. This is an identifier. Used only in configs." 163 | name: String 164 | " Tool alias. Used in cli instead of full tool name." 165 | alias: String 166 | " Tool title. Human readable name of the tool." 167 | title: String 168 | " Tool description" 169 | description: String 170 | " Commands provided by tool controller" 171 | commands: [Command] 172 | } 173 | 174 | input InputArgValue { 175 | name: String 176 | value: String 177 | } 178 | 179 | type ArgValue { 180 | name: String, 181 | value: String, 182 | } 183 | 184 | type ValidateArgResult { 185 | name: String 186 | error: String 187 | hint: String 188 | } 189 | -------------------------------------------------------------------------------- /tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "exclude": ["src/**/*.spec.ts", "src/__tests__"] 4 | } 5 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es6", 4 | "module": "commonjs", 5 | "declaration": true, 6 | "esModuleInterop": true, 7 | "allowSyntheticDefaultImports": true, 8 | "resolveJsonModule": true, 9 | "noImplicitAny": true, 10 | "moduleResolution": "node", 11 | "sourceMap": true, 12 | "outDir": "dist", 13 | "baseUrl": ".", 14 | "paths": { 15 | "*": ["node_modules/*", "src/types/*"] 16 | } 17 | }, 18 | "include": ["src/**/*", "test/**/*"], 19 | "exclude": ["node_modules", "src/**/*.spec.ts", "**/__mocks__"] 20 | } 21 | --------------------------------------------------------------------------------