├── .gitignore ├── .postcssrc.js ├── BUILDING.md ├── LICENSE ├── README.md ├── RELEASING.md ├── babel.config.js ├── bin └── .gitkeep ├── build ├── download-binaries.js ├── entitlements.mac.plist └── notarize.js ├── jsconfig.json ├── oldquasar.babelrc-old ├── package-lock.json ├── package.json ├── public ├── icon_512x512.png ├── qr-code-grey.svg ├── qr-code.svg ├── ryo-wallet.svg └── scala.svg ├── quasar.conf.js ├── src-electron ├── build │ ├── mosu_forge │ └── scala-dmg.tiff ├── electron-flag.d.ts ├── icons │ ├── icon.icns │ ├── icon.ico │ ├── icon_512x512.png │ ├── linux-512x512.png │ ├── macos-512x512.png │ └── mrcuug.PNG └── main-process │ ├── auto-updater.js │ ├── electron-main.dev.js │ ├── electron-main.js │ ├── menu.js │ └── modules │ ├── SCEE-Node.js │ ├── backend.js │ ├── daemon.js │ ├── status-codes.js │ └── wallet-rpc.js └── src ├── App.vue ├── boot ├── .gitkeep ├── axios.js ├── gateway.js ├── i18n.js ├── timeago.js └── vuelidate.js ├── components ├── .gitkeep ├── address_book_details.vue ├── address_details.vue ├── address_header.vue ├── check_transaction.vue ├── footer.vue ├── format_scala.vue ├── icons │ └── copy_icon.vue ├── identicon.vue ├── language_select.vue ├── lns_input.vue ├── lns_input_form.vue ├── lns_record_list.vue ├── mainmenu.vue ├── prove_transaction.vue ├── receive_item.vue ├── scala_field.vue ├── service_node_details.vue ├── service_node_registration.vue ├── service_node_staking.vue ├── service_node_unlock.vue ├── settings.vue ├── settings_general.vue ├── tx_details.vue ├── tx_list.vue ├── tx_type_icon.vue ├── wallet_details.vue └── wallet_settings.vue ├── css ├── app.styl ├── app_backup.styl ├── quasar.variables.styl └── themes │ ├── RobotoMono-Light.ttf │ ├── common.variables.styl │ ├── variables.ios.styl │ └── variables.mat.styl ├── gateway ├── SCEE-Node.js └── gateway.js ├── i18n ├── de.js ├── en-us.js ├── es.js ├── fr.js ├── index.js ├── pt-br.js └── ru.js ├── index.template.html ├── layouts ├── init │ ├── loading.vue │ └── welcome.vue ├── wallet-select │ └── main.vue └── wallet │ └── main.vue ├── mixins └── wallet_password.js ├── pages ├── 404.vue ├── init │ ├── index.vue │ ├── quit.vue │ └── welcome.vue ├── wallet-select │ ├── create.vue │ ├── created.vue │ ├── import-legacy.vue │ ├── import-old-gui.vue │ ├── import-view-only.vue │ ├── import.vue │ ├── index.vue │ └── restore.vue └── wallet │ ├── addressbook.vue │ ├── advanced.vue │ ├── blank.vue │ ├── lns.vue │ ├── receive.vue │ ├── send.vue │ ├── service-node.vue │ └── txhistory.vue ├── plugins ├── .gitkeep ├── axios.js ├── gateway.js ├── i18n.js ├── timeago.js └── vuelidate.js ├── router ├── index.js └── routes.js ├── statics ├── icon_512x512.png ├── qr-code-grey.svg ├── qr-code.svg ├── ryo-wallet.svg └── scala.svg ├── store ├── gateway │ ├── actions.js │ ├── getters.js │ ├── index.js │ ├── mutations.js │ └── state.js ├── index.js └── store-flag.d.ts └── validators ├── address_tools.js └── common.js /.gitignore: -------------------------------------------------------------------------------- 1 | .quasar 2 | .DS_Store 3 | .thumbs.db 4 | node_modules 5 | /dist 6 | /src-cordova/platforms 7 | /src-cordova/plugins 8 | /src-cordova/www 9 | npm-debug.log* 10 | yarn-debug.log* 11 | yarn-error.log* 12 | 13 | # Editor directories and files 14 | .idea 15 | .vscode 16 | *.suo 17 | *.ntvs* 18 | *.njsproj 19 | *.sln 20 | *~ 21 | \#*\# 22 | .\#* 23 | *.bak 24 | 25 | # bin dir 26 | bin/* 27 | !bin/.gitkeep 28 | 29 | .env 30 | -------------------------------------------------------------------------------- /.postcssrc.js: -------------------------------------------------------------------------------- 1 | // https://github.com/michael-ciniawsky/postcss-load-config 2 | 3 | module.exports = { 4 | plugins: [ 5 | // to edit target browsers: use "browserslist" field in package.json 6 | require("autoprefixer") 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /BUILDING.md: -------------------------------------------------------------------------------- 1 | # Building 2 | 3 | Building scala electron wallet binaries is done using github actions. Windows and linux binaries will build right out of the box but there are some extra steps needed for Mac OS 4 | 5 | ## Mac OS 6 | 7 | The build script for Mac OS requires you to have a valid `Developer ID Application` certificate. Without this the build script cannot sign and notarize the mac binary which is needed for Catalina 10.15 and above. 8 | If you would like to disable this then comment out `"afterSign": "build/notarize.js",` in package.json. 9 | 10 | You will also need an [App-specific password](https://support.apple.com/en-al/HT204397) for the apple account you wish to notarize with 11 | 12 | ### Setup 13 | 14 | Once you have your `Developer ID Application` you need to export it into a `.p12` file. Keep a note of the password used to encrypt this file as it will be needed later. 15 | 16 | We need to Base64 encode this file, so run the following command: 17 | 18 | ``` 19 | base64 -i certificate.p12 -o encoded.txt 20 | ``` 21 | 22 | #### On GitHub: 23 | 24 | 1. Navigate to the main page of the repository. 25 | 2. Under your repository name, click **Settings**. 26 | 3. In the left sidebar, click **Secrets**. 27 | 4. Add the following secrets: 28 | 1. Certificate 29 | - Name: `MAC_CERTIFICATE` 30 | - Value: The encoded Base64 certificate 31 | 2. Certificate password 32 | - Name: `MAC_CERTIFICATE_PASSWORD` 33 | - Value: The password that was set when the certificate was exported. 34 | 3. Apple ID 35 | - Name: `SIGNING_APPLE_ID` 36 | - Value: The apple id (email) to use for signing 37 | 4. Apple Password 38 | - Name: `SIGNING_APP_PASSWORD` 39 | - Value: The app-specific password that was generated for the apple id 40 | 5. Team ID (Optional) 41 | - Name: `SIGNING_TEAM_ID` 42 | - Value: The apple team id if you're sigining the application for a team 43 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD-3 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are met: 5 | 6 | 1. Redistributions of source code must retain the above copyright notice, this 7 | list of conditions and the following disclaimer. 8 | 9 | 2. Redistributions in binary form must reproduce the above copyright notice, 10 | this list of conditions and the following disclaimer in the documentation 11 | and/or other materials provided with the distribution. 12 | 13 | 3. Neither the name of the copyright holder nor the names of its contributors 14 | may be used to endorse or promote products derived from this software without 15 | specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 18 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 19 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 21 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 23 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 24 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 25 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | 28 | ---------------------------------------------------------------------------------------- 29 | 30 | BSD-licensed version of the code can be found at: 31 | 32 | https://github.com/monero-project/monero/tree/e2c39f6b59fcf5c623c814dfefc518ab0b7eca32 33 | https://github.com/ryo-currency/ryo-emergency/tree/9d1f51c453978badad21b2feaca2f4348ab26bfa 34 | 35 | Portions of this wallet have been released by the RYO wallet developers into public domain, we are thankful for their contributions 36 | 37 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # :warning: Repository Archived :warning: 2 | 3 | **Note**: This repository has been archived. The new repository for the Scala Electron GUI wallet can be found at [Scala GUI](https://github.com/scala-network/scala-gui). Please refer to the new repository for the latest updates and developments. 4 | 5 | --- 6 | 7 | # Scala Electron GUI Wallet 8 | 9 | ![Screenshot](https://i.imgur.com/Gou8OMu.jpg "Screenshot") 10 | 11 | ### Introduction 12 | Scala is a private cryptocurrency based on Monero. Scala aims to provide everyone the ability to mine and transact with ease and security. 13 | More information on the project can be found on the [website](https://scalaproject.io). 14 | 15 | ### About this project 16 | 17 | This is the new electron GUI for Scala. It is open source and completely free to use without restrictions, anyone may create an alternative implementation of the Scala Electron GUI that uses the protocol and network in a compatible manner. 18 | Please submit any changes as pull requests to the development branch, all changes are assessed in the development branch before being merged to master, release tags are considered stable builds for the GUI. 19 | 20 | #### Pre-requisites 21 | - Download latest [Scalad](https://github.com/scala-network/scala/releases/latest) 22 | 23 | #### Build instructions 24 | ``` 25 | nvm use 11.9.0 26 | npm install -g quasar-cli 27 | https://github.com/scala-network/scala-electron-gui/ 28 | cd scala-electron-gui 29 | cp path_to_scala_binaries/scalad bin/ 30 | cp path_to_scala_binaries/scala-wallet-rpc bin/ 31 | npm install 32 | ``` 33 | 34 | For dev: 35 | ``` 36 | npm run dev 37 | ``` 38 | 39 | For building: 40 | 41 | **Note:** This will only build the binaries for the system you run the command on. Running this command on `linux` will only make `linux` binaries, no `mac` or `windows` binaries. 42 | ``` 43 | npm run build 44 | ``` -------------------------------------------------------------------------------- /RELEASING.md: -------------------------------------------------------------------------------- 1 | # Releasing 2 | 3 | Releasing the application to work with auto update is very simple. 4 | 5 | 1. Increment the application version in `package.json`. 6 | 2. Push changes to master to trigger github actions to build the binaries. 7 | - Ensure that `.yml` files aren't being left out in the artifacts. These are needed for auto-update to work correctly. 8 | 3. Create a github release with the **tag** being in the format `v[Version]`. 9 | - E.g if the version was `2.1.1` then the github tag would be `v2.1.1` 10 | 4. Add release notes 11 | 5. Publish the release! 12 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: ["@quasar/babel-preset-app"] 3 | }; 4 | -------------------------------------------------------------------------------- /bin/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scala-network/scala-electron-gui/1eea8a5ab48541e4c6c0333316b85e18953d7f29/bin/.gitkeep -------------------------------------------------------------------------------- /build/download-binaries.js: -------------------------------------------------------------------------------- 1 | const axios = require("axios").default; 2 | const fs = require("fs-extra"); 3 | const path = require("path"); 4 | 5 | async function download() { 6 | const { platform, env } = process; 7 | const repoUrl = "https://api.github.com/repos/scala-project/scala/releases/latest"; 8 | try { 9 | const pwd = process.cwd(); 10 | const downloadDir = path.join(pwd, "downloads"); 11 | await fs.ensureDir(downloadDir); 12 | 13 | const headers = { 14 | "Content-Type": "application/json", 15 | "User-Agent": "Scala-Electron-Wallet" 16 | }; 17 | if (env.GH_TOKEN) { 18 | headers.Authorisation = `Bearer ${env.GH_TOKEN}`; 19 | } 20 | 21 | const { data } = await axios.get(repoUrl, { headers }); 22 | const { name } = data; 23 | console.log("Latest release: " + name); 24 | 25 | const url = (data.assets || []) 26 | .map(asset => asset["browser_download_url"]) 27 | .find(url => { 28 | if (platform === "darwin") { 29 | return url.includes("osx") || url.includes("mac"); 30 | } else if (platform === "win32") { 31 | return url.includes("win") || url.includes("windows"); 32 | } 33 | return url.includes("linux"); 34 | }); 35 | 36 | if (!url) { 37 | throw new Error("Download url not found for " + process); 38 | } 39 | console.log("Downloading binary at url: " + url); 40 | 41 | const extension = path.extname(url); 42 | const filePath = path.join(downloadDir, "latest" + extension); 43 | const { data: artifact } = await axios.get(url, { 44 | responseType: "stream" 45 | }); 46 | artifact.pipe(fs.createWriteStream(filePath)); 47 | console.log("Downloaded binary to: " + filePath); 48 | } catch (err) { 49 | console.error("Failed to download file: " + err); 50 | process.exit(1); 51 | } 52 | } 53 | 54 | download(); 55 | -------------------------------------------------------------------------------- /build/entitlements.mac.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.cs.allow-unsigned-executable-memory 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /build/notarize.js: -------------------------------------------------------------------------------- 1 | require("dotenv").config(); 2 | const { notarize } = require("electron-notarize"); 3 | 4 | /* 5 | Pre-requisites: https://github.com/electron/electron-notarize#prerequisites 6 | 1. Generate an app specific password 7 | 2. Add SIGNING_APPLE_ID, SIGNING_APP_PASSWORD, SIGNING_TEAM_ID to .env file in the root directory (where quasar.conf.js is located) 8 | */ 9 | 10 | /* 11 | Notarizing: https://kilianvalkhof.com/2019/electron/notarizing-your-electron-application/ 12 | */ 13 | 14 | const log = msg => console.log(`\n${msg}`); 15 | const isEmpty = v => !v || v.length === 0; 16 | 17 | exports.default = async function notarizing(context) { 18 | const { electronPlatformName, appOutDir } = context; 19 | if (electronPlatformName !== "darwin") { 20 | return; 21 | } 22 | log("Notarizing mac application"); 23 | 24 | const appName = context.packager.appInfo.productFilename; 25 | const { SIGNING_APPLE_ID, SIGNING_APP_PASSWORD, SIGNING_TEAM_ID } = process.env; 26 | 27 | if (isEmpty(SIGNING_APPLE_ID) || isEmpty(SIGNING_APP_PASSWORD)) { 28 | log("SIGNING_APPLE_ID or SIGNING_APP_PASSWORD not set.\nTerminating noratization."); 29 | return; 30 | } 31 | 32 | const options = { 33 | appBundleId: "com.scala-project.electron-wallet", 34 | appPath: `${appOutDir}/${appName}.app`, 35 | appleId: SIGNING_APPLE_ID, 36 | appleIdPassword: SIGNING_APP_PASSWORD 37 | }; 38 | if (!isEmpty(SIGNING_TEAM_ID)) options.ascProvider = SIGNING_TEAM_ID; 39 | return notarize(options); 40 | }; 41 | -------------------------------------------------------------------------------- /jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es6", 4 | "baseUrl": ".", 5 | "paths": { 6 | "src/*": ["./src/*"], 7 | "app/*": ["./*"], 8 | "components/*": ["./src/components/*"], 9 | "layouts/*": ["./src/layouts/*"], 10 | "pages/*": ["./src/pages/*"], 11 | "statics/*": ["./src/statics/*"] 12 | } 13 | }, 14 | "exclude": ["node_modules", "dist", ".quasar", "downloads", ".vscode"] 15 | } 16 | -------------------------------------------------------------------------------- /oldquasar.babelrc-old: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | [ 4 | "@babel/preset-env", 5 | { 6 | "modules": false, 7 | "loose": false, 8 | "useBuiltIns": "usage" 9 | } 10 | ], 11 | [ 12 | "@babel/preset-stage-2", 13 | { 14 | "modules": false, 15 | "loose": false, 16 | "useBuiltIns": true, 17 | "decoratorsLegacy": true 18 | } 19 | ] 20 | ], 21 | "plugins": [ 22 | [ 23 | "@babel/transform-runtime", 24 | { 25 | "polyfill": false, 26 | "regenerator": false 27 | } 28 | ] 29 | ], 30 | "comments": false 31 | } 32 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "scala-electron-wallet", 3 | "version": "7.9.0", 4 | "description": "Modern GUI interface for Scala Currency", 5 | "productName": "Scala Electron Wallet", 6 | "repository": { 7 | "type": "git", 8 | "url": "https://github.com/scala-project/scala-electron-gui-wallet.git" 9 | }, 10 | "cordovaId": "com.scalanetwork.wallet", 11 | "author": { 12 | "name": "Scala", 13 | "email": "hello@scalaproject.io" 14 | }, 15 | "private": true, 16 | "scripts": { 17 | "dev": "quasar dev -m electron", 18 | "build": "quasar build -m electron", 19 | "release": "quasar build -m electron --publish=always", 20 | "lint": "eslint --fix .", 21 | "format": "prettier --write \"**/*.+(js|jsx|json|yml|yaml|css|md|vue)\"", 22 | "ready": "npm run lint && npm run format" 23 | }, 24 | "dependencies": { 25 | "@quasar/app": "^2.0.8", 26 | "@quasar/extras": "^1.9.4", 27 | "axios": "^0.21.1", 28 | "electron-is-dev": "^1.0.1", 29 | "electron-updater": "^4.2.0", 30 | "electron-window-state": "^5.0.3", 31 | "flag-icon-css": "^3.3.0", 32 | "fs-extra": "^8.1.0", 33 | "object-assign-deep": "^0.4.0", 34 | "portscanner": "^2.2.0", 35 | "promise-queue": "^2.2.5", 36 | "qrcode.vue": "^1.6.1", 37 | "quasar": "^1.13.1", 38 | "request": "^2.88.0", 39 | "request-promise": "^4.2.4", 40 | "upath": "^1.2.0", 41 | "vue-i18n": "^8.9.0", 42 | "vue-timeago": "^5.1.2", 43 | "vuelidate": "^0.7.4" 44 | }, 45 | "devDependencies": { 46 | "babel-eslint": "^10.1.0", 47 | "devtron": "^1.4.0", 48 | "dotenv": "^8.1.0", 49 | "electron": "^8.5.2", 50 | "electron-builder": "^22.8.1", 51 | "electron-debug": "^2.1.0", 52 | "electron-devtools-installer": "^2.2.4", 53 | "electron-notarize": "^0.1.1", 54 | "eslint": "^5.16.0", 55 | "eslint-config-prettier": "^6.10.0", 56 | "eslint-friendly-formatter": "^4.0.1", 57 | "eslint-loader": "^2.2.1", 58 | "eslint-plugin-import": "^2.16.0", 59 | "eslint-plugin-node": "^8.0.1", 60 | "eslint-plugin-prettier": "^3.1.2", 61 | "eslint-plugin-promise": "^4.0.1", 62 | "eslint-plugin-vue": "^5.2.3", 63 | "husky": "^4.2.3", 64 | "lint-staged": "^10.0.8", 65 | "node-sass": "^4.13.1", 66 | "prettier": "^1.19.1", 67 | "sass-loader": "^7.1.0", 68 | "strip-ansi": "^3.0.1" 69 | }, 70 | "lint-staged": { 71 | "*.+(js|jsx|vue)": [], 72 | "*.+(json|yml|yaml|css|md)": "" 73 | }, 74 | "engines": { 75 | "node": ">= 8.9.0", 76 | "npm": ">= 5.6.0", 77 | "yarn": ">= 1.6.0" 78 | }, 79 | "browserslist": [ 80 | "> 1%", 81 | "last 2 versions", 82 | "not ie <= 10" 83 | ] 84 | } 85 | -------------------------------------------------------------------------------- /public/icon_512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scala-network/scala-electron-gui/1eea8a5ab48541e4c6c0333316b85e18953d7f29/public/icon_512x512.png -------------------------------------------------------------------------------- /public/qr-code-grey.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /public/qr-code.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /public/ryo-wallet.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /public/scala.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | 10 | 11 | 33 | 48 | 50 | 51 | 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /quasar.conf.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-template-curly-in-string */ 2 | // Configuration for your app 3 | 4 | module.exports = function() { 5 | return { 6 | // app boot (/src/boot) 7 | boot: ["i18n", "axios", "vuelidate", "gateway", "timeago"], 8 | css: ["app.styl", "~flag-icon-css/css/flag-icon.min.css"], 9 | extras: [ 10 | // ctx.theme.mat ? "roboto-font" : null, 11 | "material-icons" // optional, you are not bound to it 12 | // "ionicons-v4", 13 | // "mdi-v5", 14 | // "fontawesome-v5" 15 | ], 16 | supportIE: false, 17 | build: { 18 | scopeHoisting: true, 19 | vueRouterMode: "history", 20 | // vueCompiler: true, 21 | // gzip: true, 22 | // analyze: true, 23 | // extractCSS: false, 24 | extendWebpack() { 25 | /* 26 | cfg.module.rules.push({ 27 | enforce: "pre", 28 | test: /\.(js|vue)$/, 29 | loader: "eslint-loader", 30 | exclude: /(node_modules|quasar)/ 31 | }) 32 | */ 33 | }, 34 | chainWebpack(chain) { 35 | chain.module 36 | .rule("images") 37 | .use("url-loader") 38 | .tap(options => { 39 | options.name = "img/[path][name].[ext]"; 40 | return options; 41 | }); 42 | } 43 | }, 44 | devServer: { 45 | // https: true, 46 | // port: 8080, 47 | open: true // opens browser window automatically 48 | }, 49 | // framework: "all" --- includes everything; for dev only! 50 | framework: { 51 | components: [ 52 | "QLayout", 53 | "QHeader", 54 | "QFooter", 55 | "QDrawer", 56 | "QPageContainer", 57 | "QPage", 58 | "QToolbar", 59 | "QToolbarTitle", 60 | "QTooltip", 61 | "QField", 62 | "QInput", 63 | "QRadio", 64 | "QOptionGroup", 65 | "QBtn", 66 | "QBtnToggle", 67 | "QIcon", 68 | "QTabs", 69 | "QTab", 70 | "QRouteTab", 71 | "QBtnDropdown", 72 | "QMenu", 73 | "QDialog", 74 | "QStep", 75 | "QStepper", 76 | "QStepperNavigation", 77 | "QSpinner", 78 | "QList", 79 | "QItemLabel", 80 | "QItem", 81 | "QSeparator", 82 | "QItemSection", 83 | "QSelect", 84 | "QToggle", 85 | "QPageSticky", 86 | "QExpansionItem", 87 | "QCheckbox", 88 | "QInnerLoading", 89 | "QInfiniteScroll", 90 | "QDate", 91 | "QTime", 92 | "QScrollArea" 93 | ], 94 | directives: ["Ripple"], 95 | // Quasar plugins 96 | plugins: ["Notify", "Loading", "LocalStorage", "Dialog"] 97 | // iconSet: ctx.theme.mat ? "material-icons" : "ionicons-v4" 98 | // i18n: "de" // Quasar language 99 | }, 100 | // animations: "all" --- includes all animations 101 | animations: [], 102 | pwa: { 103 | // workboxPluginMode: "InjectManifest", 104 | // workboxOptions: {}, 105 | manifest: { 106 | // name: "Quasar App", 107 | // short_name: "Quasar-PWA", 108 | // description: "Best PWA App in town!", 109 | display: "standalone", 110 | orientation: "portrait", 111 | background_color: "#ffffff", 112 | theme_color: "#43BD43", 113 | icons: [ 114 | { 115 | src: "statics/icons/icon-128x128.png", 116 | sizes: "128x128", 117 | type: "image/png" 118 | }, 119 | { 120 | src: "statics/icons/icon-192x192.png", 121 | sizes: "192x192", 122 | type: "image/png" 123 | }, 124 | { 125 | src: "statics/icons/icon-256x256.png", 126 | sizes: "256x256", 127 | type: "image/png" 128 | }, 129 | { 130 | src: "statics/icons/icon-384x384.png", 131 | sizes: "384x384", 132 | type: "image/png" 133 | }, 134 | { 135 | src: "statics/icons/icon-512x512.png", 136 | sizes: "512x512", 137 | type: "image/png" 138 | } 139 | ] 140 | } 141 | }, 142 | cordova: { 143 | // id: "org.cordova.quasar.app" 144 | }, 145 | electron: { 146 | bundler: "builder", // or "packager" 147 | extendWebpack() { 148 | // cfg 149 | // do something with Electron process Webpack cfg 150 | }, 151 | packager: { 152 | // https://github.com/electron-userland/electron-packager/blob/master/docs/api.md#options 153 | 154 | // OS X / Mac App Store 155 | // appBundleId: "", 156 | // appCategoryType: "", 157 | // osxSign: "", 158 | // protocol: "myapp://path", 159 | 160 | // Window only 161 | // win32metadata: { ... } 162 | 163 | extraResource: ["bin"] 164 | }, 165 | builder: { 166 | // https://www.electron.build/configuration/configuration 167 | 168 | appId: "com.scala-project.electron-wallet", 169 | productName: "Scala Electron Wallet", 170 | copyright: "Copyright © 2018-2019 Scala Project, 2018 Ryo Currency Project", 171 | afterSign: "build/notarize.js", 172 | artifactName: "scala-electron-wallet-${version}-${os}.${ext}", 173 | publish: "github", 174 | 175 | linux: { 176 | target: ["AppImage", "deb"], 177 | icon: "src-electron/icons/icon_512x512.png", 178 | category: "Finance" 179 | }, 180 | 181 | mac: { 182 | // We need zip for auto-updating 183 | // Ref: https://github.com/electron-userland/electron-builder/issues/2199 184 | target: ["dmg", "zip"], 185 | icon: "src-electron/icons/icon.icns", 186 | category: "public.app-category.finance", 187 | // Notarizing: https://kilianvalkhof.com/2019/electron/notarizing-your-electron-application/ 188 | hardenedRuntime: true, 189 | gatekeeperAssess: false, 190 | entitlements: "build/entitlements.mac.plist", 191 | entitlementsInherit: "build/entitlements.mac.plist" 192 | }, 193 | 194 | dmg: { 195 | background: "src-electron/build/scala-dmg.tiff", 196 | sign: false 197 | }, 198 | 199 | nsis: { 200 | oneClick: false, 201 | allowToChangeInstallationDirectory: true 202 | }, 203 | 204 | files: ["!build/*.js", "!.env", "!dev-app-update.yml"], 205 | 206 | extraResources: ["bin"] 207 | } 208 | } 209 | }; 210 | }; 211 | -------------------------------------------------------------------------------- /src-electron/build/mosu_forge: -------------------------------------------------------------------------------- 1 | -----BEGIN PGP PUBLIC KEY BLOCK----- 2 | 3 | mQENBFsQeDEBCAC6FipGNqhvlplhjoFvqUqAxreOb4/qi5eSTnzoeGlb3XEBlifV 4 | z5d6aSQqZXjDSkrZqjh57Lkv1DugZpz/FLTVuy+b7stqxwvlQDyRxZg7871sTDh5 5 | ZG1CMN6rHU4wYq081+/gRDOz96TCl40geT/EKBXTPoXbVh6h1UWC5t47JqHYMFe1 6 | aqdqQDF+1pn5e5b1Dxe3U9B/rlTb1darb04ENNqGaaX2rrDn5d2DEv7a0/XLB2Nc 7 | VrzbUH+3LyxgO1smzDOJHn5dia7z4LUsE10KQijwOEZtxYuMWC+KTyVubGkOVYVp 8 | EQ4CAvHPHXWb0lyaJnP9jBlWvR1Mvznkdwi1ABEBAAG0Jm1vc3VfZm9yZ2UgPG1v 9 | c3UuZm9yZ2VAcHJvdG9ubWFpbC5jb20+iQFOBBMBCAA4FiEEx8qIjI7MgV9v9hnk 10 | g6LJxzvxIGMFAlsQeDECGwMFCwkIBwIGFQoJCAsCBBYCAwECHgECF4AACgkQg6LJ 11 | xzvxIGNNVgf/SdwxQegQEPKo7j2vj8UhtfFX3sAxpY4ko3YX88taEZlGSSBFtW1U 12 | 0WyfBi3kdeYs8zhN3ZjnjVtFJvnjDETLu4EPAuSrDyrz3rVaMqFha8mqFvHJUC26 13 | phoJC3bM1/9pgmvgUJfVj4i3hoAPNSBnB7OkefFnea/OGow4NHtcvOTRDNTbZ675 14 | 3kvmCMxv/iSfMm1oQmO4rlnBkBF5Hh7rF0hQyG3d+8iuzckaxy/zjXWRKzSX2LsZ 15 | KXHM6BTBNq6SXnXyJ5D8meT+0sMUsxN4yblwKtFosQpVOtoQc4YMq17NWifnBedZ 16 | qs50iIawUaBD/BBXXVOEfObxU1Ct1lGCErkBDQRbEHgxAQgArKfVkyltydCvCB11 17 | JERzpkvKOWEyyAA8Qrbb+6mbYlqT3Uf/+ypnj1/lJu5xfNhT9GIGR5vEDr2ERYlJ 18 | uQ8kqnKY41oaLXr2p1U0Wp42AJX4m9XUXsiGocZpDmAluOKtXJocUoMrT4zdkviS 19 | n2j3Vr1MPj8MOYmAg6Nl4/3nwYrHe1PN75kDjk49tf95VvnCIolilRAMXcgtKl/o 20 | WxXFUXndOBgV2ywY7v7IGBvqKGKCz95ItJ4EKhDWs1EiCHtrGnd+OHNBjecj95vh 21 | SRDdUFRpWIUpwjoGFKE5C0he4MS4+CDy5p8jAKUQgfWVhFmM3st8po0uTbLB5Za4 22 | 0wv6wwARAQABiQE2BBgBCAAgFiEEx8qIjI7MgV9v9hnkg6LJxzvxIGMFAlsQeDEC 23 | GwwACgkQg6LJxzvxIGPZ9wgAmYRdNADoPO4uxC4gP+0XjDd5W+vy1qDSXDCAqaAD 24 | aU+B22NOwx48XlcHSg5rvzoUqoXxtdiYMR9tQS2RF1OfzNIRnJy5Jqrv7ulOV63B 25 | n74gOGa2ypFiFj50i1Zvu/80CPDpc4Cg5csqtzslDMxiuNz+Wh+qQyYlVrV9r2N8 26 | ejaKFk9znjGw3fo/SxXMOncZb7yCaxeqT82/A+N9WMiUIJIhYyenYQ1uFK3lkzTK 27 | hl0wDD1Qnrwz49v1z60+7ARUltjog6TqcXPsxFarLLo1f0kx/YPBM2jcKQ1uBFtR 28 | 8xJ0CkjYof4QDMRI9/Dft+FOzNy92o3m1s6fXcISWJ1YNw== 29 | =ts23 30 | -----END PGP PUBLIC KEY BLOCK----- 31 | -------------------------------------------------------------------------------- /src-electron/build/scala-dmg.tiff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scala-network/scala-electron-gui/1eea8a5ab48541e4c6c0333316b85e18953d7f29/src-electron/build/scala-dmg.tiff -------------------------------------------------------------------------------- /src-electron/electron-flag.d.ts: -------------------------------------------------------------------------------- 1 | // THIS FEATURE-FLAG FILE IS AUTOGENERATED, 2 | // REMOVAL OR CHANGES WILL CAUSE RELATED TYPES TO STOP WORKING 3 | import "quasar/dist/types/feature-flag"; 4 | 5 | declare module "quasar/dist/types/feature-flag" { 6 | interface QuasarFeatureFlags { 7 | electron: true; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src-electron/icons/icon.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scala-network/scala-electron-gui/1eea8a5ab48541e4c6c0333316b85e18953d7f29/src-electron/icons/icon.icns -------------------------------------------------------------------------------- /src-electron/icons/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scala-network/scala-electron-gui/1eea8a5ab48541e4c6c0333316b85e18953d7f29/src-electron/icons/icon.ico -------------------------------------------------------------------------------- /src-electron/icons/icon_512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scala-network/scala-electron-gui/1eea8a5ab48541e4c6c0333316b85e18953d7f29/src-electron/icons/icon_512x512.png -------------------------------------------------------------------------------- /src-electron/icons/linux-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scala-network/scala-electron-gui/1eea8a5ab48541e4c6c0333316b85e18953d7f29/src-electron/icons/linux-512x512.png -------------------------------------------------------------------------------- /src-electron/icons/macos-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scala-network/scala-electron-gui/1eea8a5ab48541e4c6c0333316b85e18953d7f29/src-electron/icons/macos-512x512.png -------------------------------------------------------------------------------- /src-electron/icons/mrcuug.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scala-network/scala-electron-gui/1eea8a5ab48541e4c6c0333316b85e18953d7f29/src-electron/icons/mrcuug.PNG -------------------------------------------------------------------------------- /src-electron/main-process/auto-updater.js: -------------------------------------------------------------------------------- 1 | import * as path from "path"; 2 | import * as fs from "fs-extra"; 3 | import { dialog } from "electron"; 4 | import isDev from "electron-is-dev"; 5 | import { autoUpdater } from "electron-updater"; 6 | import { app } from "electron"; 7 | 8 | let isUpdating = false; 9 | 10 | /* 11 | Check if we have the required files to auto update. 12 | These files won't exist inside certain formats such as a linux deb file. 13 | */ 14 | async function canAutoUpdate() { 15 | const { isPackaged } = app; 16 | 17 | // On a production app, we need to use resources path to check for the file 18 | if (isPackaged && !process.resourcesPath) { 19 | return false; 20 | } 21 | 22 | // Taken from: https://github.com/electron-userland/electron-builder/blob/d4feb6d3c8b008f8b455c761d654c8088f90d8fa/packages/electron-updater/src/ElectronAppAdapter.ts#L25 23 | const updateFile = isPackaged ? "app-update.yml" : "dev-app-update.yml"; 24 | const basePath = isPackaged && process.resourcesPath ? process.resourcesPath : app.getAppPath(); 25 | const appUpdateConfigPath = path.join(basePath, updateFile); 26 | 27 | return new Promise(resolve => { 28 | try { 29 | // tslint:disable-next-line: non-literal-fs-path 30 | const exists = fs.existsSync(appUpdateConfigPath); 31 | resolve(exists); 32 | } catch (e) { 33 | resolve(false); 34 | } 35 | }); 36 | } 37 | 38 | async function checkForUpdate(getMainWindow, onQuitAndInstall) { 39 | // Disable for development 40 | if (isDev) { 41 | return; 42 | } 43 | 44 | if (isUpdating) { 45 | return; 46 | } 47 | 48 | const canUpdate = await canAutoUpdate(); 49 | if (!canUpdate) { 50 | return; 51 | } 52 | 53 | autoUpdater.logger = console; 54 | 55 | try { 56 | // Get the update using electron-updater 57 | const info = await autoUpdater.checkForUpdates(); 58 | if (!info || !info.downloadPromise) { 59 | console.info("auto-update: no update to download"); 60 | 61 | return; 62 | } 63 | 64 | try { 65 | await info.downloadPromise; 66 | } catch (error) { 67 | await showCannotUpdateDialog(getMainWindow()); 68 | throw error; 69 | } 70 | 71 | // Update downloaded successfully, we should ask the user to update 72 | console.info("auto-update: showing update dialog..."); 73 | const shouldUpdate = await showUpdateDialog(getMainWindow()); 74 | if (!shouldUpdate) { 75 | return; 76 | } 77 | 78 | console.info("auto-update: calling quitAndInstall..."); 79 | if (onQuitAndInstall) { 80 | onQuitAndInstall(autoUpdater); 81 | } 82 | } catch (error) { 83 | console.error("auto-update error:", getPrintableError(error)); 84 | } finally { 85 | isUpdating = false; 86 | } 87 | } 88 | 89 | function getPrintableError(error) { 90 | return error && error.stack ? error.stack : error; 91 | } 92 | 93 | async function showUpdateDialog(mainWindow) { 94 | const RESTART_BUTTON = 0; 95 | const LATER_BUTTON = 1; 96 | const options = { 97 | type: "info", 98 | buttons: ["Restart Wallet", "Later"], 99 | title: "Scala Electron Wallet update available", 100 | message: "There is a new version of Scala Electron Wallet available.", 101 | detail: "Press Restart Wallet to apply the update", 102 | defaultId: LATER_BUTTON, 103 | cancelId: RESTART_BUTTON 104 | }; 105 | return new Promise(resolve => { 106 | dialog.showMessageBox(mainWindow, options, response => { 107 | resolve(response === RESTART_BUTTON); 108 | }); 109 | }); 110 | } 111 | 112 | async function showCannotUpdateDialog(mainWindow) { 113 | const options = { 114 | type: "error", 115 | buttons: ["Ok"], 116 | title: "Cannot update", 117 | message: 118 | "Scala Electron Wallet failed to update but there is a new version available. Please go to https://scala.network/ and install the new version manually." 119 | }; 120 | 121 | return new Promise(resolve => { 122 | dialog.showMessageBox(mainWindow, options, () => { 123 | resolve(); 124 | }); 125 | }); 126 | } 127 | 128 | export { checkForUpdate }; 129 | -------------------------------------------------------------------------------- /src-electron/main-process/electron-main.dev.js: -------------------------------------------------------------------------------- 1 | /** 2 | * This file is used specifically and only for development. It installs 3 | * `electron-debug` & `vue-devtools`. There shouldn"t be any need to 4 | * modify this file, but it can be used to extend your development 5 | * environment. 6 | */ 7 | 8 | // Install `electron-debug` with `devtron` 9 | require("electron-debug")({ 10 | showDevTools: true 11 | }); 12 | 13 | // Install `vue-devtools` 14 | require("electron").app.on("ready", () => { 15 | let installExtension = require("electron-devtools-installer"); 16 | installExtension 17 | .default(installExtension.VUEJS_DEVTOOLS) 18 | .then(() => {}) 19 | .catch(err => { 20 | console.log("Unable to install `vue-devtools`: \n", err); 21 | }); 22 | }); 23 | 24 | // Require `main` process to boot app 25 | require("./electron-main"); 26 | -------------------------------------------------------------------------------- /src-electron/main-process/electron-main.js: -------------------------------------------------------------------------------- 1 | import { app, ipcMain, BrowserWindow, Menu, dialog } from "electron"; 2 | import { version, productName } from "../../package.json"; 3 | import { Backend } from "./modules/backend"; 4 | import { checkForUpdate } from "./auto-updater"; 5 | import menuTemplate from "./menu"; 6 | import isDev from "electron-is-dev"; 7 | const portscanner = require("portscanner"); 8 | const windowStateKeeper = require("electron-window-state"); 9 | const path = require("upath"); 10 | 11 | /** 12 | * Set `__statics` path to static files in production; 13 | * The reason we are setting it here is that the path needs to be evaluated at runtime 14 | */ 15 | if (process.env.PROD) { 16 | global.__statics = path.join(__dirname, "statics").replace(/\\/g, "\\\\"); 17 | global.__ryo_bin = path.join(__dirname, "..", "bin").replace(/\\/g, "\\\\"); 18 | } else { 19 | global.__ryo_bin = path.join(process.cwd(), "bin").replace(/\\/g, "\\\\"); 20 | } 21 | 22 | let mainWindow, backend; 23 | let showConfirmClose = true; 24 | let forceQuit = false; 25 | let installUpdate = false; 26 | 27 | const title = `${productName} v${version}`; 28 | 29 | const selectionMenu = Menu.buildFromTemplate([{ role: "copy" }, { type: "separator" }, { role: "selectall" }]); 30 | 31 | const inputMenu = Menu.buildFromTemplate([ 32 | { role: "cut" }, 33 | { role: "copy" }, 34 | { role: "paste" }, 35 | { type: "separator" }, 36 | { role: "selectall" } 37 | ]); 38 | 39 | function createWindow() { 40 | /** 41 | * Initial window options 42 | */ 43 | 44 | let mainWindowState = windowStateKeeper({ 45 | defaultWidth: 900, 46 | defaultHeight: 700 47 | }); 48 | 49 | mainWindow = new BrowserWindow({ 50 | x: mainWindowState.x, 51 | y: mainWindowState.y, 52 | width: mainWindowState.width, 53 | height: mainWindowState.height, 54 | minWidth: 640, 55 | minHeight: 480, 56 | icon: require("path").join(__statics, "icon_512x512.png"), 57 | title, 58 | webPreferences: { 59 | nodeIntegration: true 60 | } 61 | }); 62 | 63 | mainWindow.on("close", e => { 64 | // Don't ask for confirmation if we're installing an update 65 | if (installUpdate) { 66 | return; 67 | } 68 | 69 | if (process.platform === "darwin") { 70 | if (forceQuit) { 71 | forceQuit = false; 72 | if (showConfirmClose) { 73 | e.preventDefault(); 74 | mainWindow.show(); 75 | mainWindow.webContents.send("confirmClose"); 76 | } else { 77 | e.defaultPrevented = false; 78 | } 79 | } else { 80 | e.preventDefault(); 81 | mainWindow.hide(); 82 | } 83 | } else { 84 | if (showConfirmClose) { 85 | e.preventDefault(); 86 | mainWindow.webContents.send("confirmClose"); 87 | } else { 88 | e.defaultPrevented = false; 89 | } 90 | } 91 | }); 92 | 93 | ipcMain.on("confirmClose", (e, restart) => { 94 | showConfirmClose = false; 95 | 96 | // In dev mode, this will launch a blank white screen 97 | if (restart && !isDev) app.relaunch(); 98 | 99 | const promise = backend ? backend.quit() : Promise.resolve(); 100 | promise.then(() => { 101 | backend = null; 102 | app.quit(); 103 | }); 104 | }); 105 | 106 | mainWindow.webContents.on("did-finish-load", () => { 107 | // Set the title 108 | mainWindow.setTitle(title); 109 | 110 | require("crypto").randomBytes(64, (err, buffer) => { 111 | // if err, then we may have to use insecure token generation perhaps 112 | if (err) throw err; 113 | 114 | let config = { 115 | port: 12313, 116 | token: buffer.toString("hex") 117 | }; 118 | 119 | portscanner.checkPortStatus(config.port, "127.0.0.1", (error, status) => { 120 | if (error) { 121 | console.error(error); 122 | } 123 | 124 | if (status === "closed") { 125 | backend = new Backend(mainWindow); 126 | backend.init(config); 127 | mainWindow.webContents.send("initialize", config); 128 | } else { 129 | dialog.showMessageBox( 130 | mainWindow, 131 | { 132 | title: "Startup error", 133 | message: `Scala Wallet is already open, or port ${config.port} is in use`, 134 | type: "error", 135 | buttons: ["ok"] 136 | }, 137 | () => { 138 | showConfirmClose = false; 139 | app.quit(); 140 | } 141 | ); 142 | } 143 | }); 144 | }); 145 | }); 146 | 147 | mainWindow.webContents.on("context-menu", (e, props) => { 148 | const { selectionText, isEditable } = props; 149 | if (isEditable) { 150 | inputMenu.popup(mainWindow); 151 | } else if (selectionText && selectionText.trim() !== "") { 152 | selectionMenu.popup(mainWindow); 153 | } 154 | }); 155 | 156 | mainWindow.loadURL(process.env.APP_URL); 157 | mainWindowState.manage(mainWindow); 158 | } 159 | 160 | app.on("ready", () => { 161 | checkForUpdate( 162 | () => mainWindow, 163 | autoUpdater => { 164 | if (mainWindow) { 165 | mainWindow.webContents.send("showQuitScreen"); 166 | } 167 | 168 | const promise = backend ? backend.quit() : Promise.resolve(); 169 | promise.then(() => { 170 | installUpdate = true; 171 | backend = null; 172 | autoUpdater.quitAndInstall(); 173 | }); 174 | } 175 | ); 176 | if (process.platform === "darwin") { 177 | const menu = Menu.buildFromTemplate(menuTemplate); 178 | Menu.setApplicationMenu(menu); 179 | } 180 | createWindow(); 181 | }); 182 | 183 | app.on("window-all-closed", () => { 184 | if (process.platform !== "darwin") { 185 | app.quit(); 186 | } 187 | }); 188 | 189 | app.on("activate", () => { 190 | if (mainWindow === null) { 191 | createWindow(); 192 | } else if (process.platform === "darwin") { 193 | mainWindow.show(); 194 | } 195 | }); 196 | 197 | app.on("before-quit", () => { 198 | // Quit instantly if we are installing an update 199 | if (installUpdate) { 200 | return; 201 | } 202 | 203 | if (process.platform === "darwin") { 204 | forceQuit = true; 205 | } else { 206 | if (backend) { 207 | backend.quit().then(() => { 208 | mainWindow.close(); 209 | }); 210 | } 211 | } 212 | }); 213 | 214 | app.on("quit", () => {}); 215 | -------------------------------------------------------------------------------- /src-electron/main-process/menu.js: -------------------------------------------------------------------------------- 1 | let template = [ 2 | { 3 | label: "Edit", 4 | submenu: [ 5 | { role: "undo" }, 6 | { role: "redo" }, 7 | { type: "separator" }, 8 | { role: "cut" }, 9 | { role: "copy" }, 10 | { role: "paste" }, 11 | { role: "pasteandmatchstyle" }, 12 | { role: "delete" }, 13 | { role: "selectall" } 14 | ] 15 | }, 16 | { 17 | label: "View", 18 | submenu: [ 19 | { role: "resetzoom" }, 20 | { role: "zoomin" }, 21 | { role: "zoomout" }, 22 | { type: "separator" }, 23 | { role: "togglefullscreen" } 24 | ] 25 | }, 26 | { 27 | role: "window", 28 | submenu: [{ role: "minimize" }, { role: "close" }] 29 | }, 30 | { 31 | role: "help", 32 | submenu: [ 33 | { 34 | label: "Learn More", 35 | click() { 36 | require("electron").shell.openExternal("https://scala.network/"); 37 | } 38 | } 39 | ] 40 | } 41 | ]; 42 | 43 | if (process.platform === "darwin") { 44 | template.unshift({ 45 | label: "Scala Electron Wallet", 46 | submenu: [ 47 | { role: "about" }, 48 | { type: "separator" }, 49 | { role: "hide" }, 50 | { role: "hideothers" }, 51 | { role: "unhide" }, 52 | { type: "separator" }, 53 | { role: "quit" } 54 | ] 55 | }); 56 | 57 | // Window menu 58 | template[3].submenu = [ 59 | { role: "close" }, 60 | { role: "minimize" }, 61 | { role: "zoom" }, 62 | { type: "separator" }, 63 | { role: "front" } 64 | ]; 65 | } 66 | 67 | export default template; 68 | -------------------------------------------------------------------------------- /src-electron/main-process/modules/SCEE-Node.js: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License 3 | 4 | Copyright (c) 2018 Luke Park 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | 25 | const crypto = require("crypto"); 26 | 27 | const ALGORITHM_NAME = "aes-128-gcm"; 28 | const ALGORITHM_NONCE_SIZE = 12; 29 | const ALGORITHM_TAG_SIZE = 16; 30 | const ALGORITHM_KEY_SIZE = 16; 31 | const PBKDF2_NAME = "sha256"; 32 | const PBKDF2_SALT_SIZE = 16; 33 | const PBKDF2_ITERATIONS = 32767; 34 | 35 | export class SCEE { 36 | encryptString(plaintext, password) { 37 | // Generate a 128-bit salt using a CSPRNG. 38 | let salt = crypto.randomBytes(PBKDF2_SALT_SIZE); 39 | 40 | // Derive a key using PBKDF2. 41 | let key = crypto.pbkdf2Sync(new Buffer(password, "utf8"), salt, PBKDF2_ITERATIONS, ALGORITHM_KEY_SIZE, PBKDF2_NAME); 42 | 43 | // Encrypt and prepend salt. 44 | let ciphertextAndNonceAndSalt = Buffer.concat([salt, this.encrypt(new Buffer(plaintext, "utf8"), key)]); 45 | 46 | // Return as base64 string. 47 | return ciphertextAndNonceAndSalt.toString("base64"); 48 | } 49 | 50 | decryptString(base64CiphertextAndNonceAndSalt, password) { 51 | // Decode the base64. 52 | let ciphertextAndNonceAndSalt = new Buffer(base64CiphertextAndNonceAndSalt, "base64"); 53 | 54 | // Create buffers of salt and ciphertextAndNonce. 55 | let salt = ciphertextAndNonceAndSalt.slice(0, PBKDF2_SALT_SIZE); 56 | let ciphertextAndNonce = ciphertextAndNonceAndSalt.slice(PBKDF2_SALT_SIZE); 57 | 58 | // Derive the key using PBKDF2. 59 | let key = crypto.pbkdf2Sync(new Buffer(password, "utf8"), salt, PBKDF2_ITERATIONS, ALGORITHM_KEY_SIZE, PBKDF2_NAME); 60 | 61 | // Decrypt and return result. 62 | return this.decrypt(ciphertextAndNonce, key).toString("utf8"); 63 | } 64 | 65 | encrypt(plaintext, key) { 66 | // Generate a 96-bit nonce using a CSPRNG. 67 | let nonce = crypto.randomBytes(ALGORITHM_NONCE_SIZE); 68 | 69 | // Create the cipher instance. 70 | let cipher = crypto.createCipheriv(ALGORITHM_NAME, key, nonce); 71 | 72 | // Encrypt and prepend nonce. 73 | let ciphertext = Buffer.concat([cipher.update(plaintext), cipher.final()]); 74 | 75 | return Buffer.concat([nonce, ciphertext, cipher.getAuthTag()]); 76 | } 77 | 78 | decrypt(ciphertextAndNonce, key) { 79 | // Create buffers of nonce, ciphertext and tag. 80 | let nonce = ciphertextAndNonce.slice(0, ALGORITHM_NONCE_SIZE); 81 | let ciphertext = ciphertextAndNonce.slice(ALGORITHM_NONCE_SIZE, ciphertextAndNonce.length - ALGORITHM_TAG_SIZE); 82 | let tag = ciphertextAndNonce.slice(ciphertext.length + ALGORITHM_NONCE_SIZE); 83 | 84 | // Create the cipher instance. 85 | let cipher = crypto.createDecipheriv(ALGORITHM_NAME, key, nonce); 86 | 87 | // Decrypt and return result. 88 | cipher.setAuthTag(tag); 89 | return Buffer.concat([cipher.update(ciphertext), cipher.final()]); 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src-electron/main-process/modules/status-codes.js: -------------------------------------------------------------------------------- 1 | export const WALLET_NOT_OPEN = -1; 2 | export const WALLET_OPEN = 0; 3 | export const WALLET_ERROR = 1; 4 | -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /src/boot/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scala-network/scala-electron-gui/1eea8a5ab48541e4c6c0333316b85e18953d7f29/src/boot/.gitkeep -------------------------------------------------------------------------------- /src/boot/axios.js: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | 3 | export default ({ Vue }) => { 4 | Vue.prototype.$axios = axios; 5 | }; 6 | -------------------------------------------------------------------------------- /src/boot/gateway.js: -------------------------------------------------------------------------------- 1 | import { Gateway } from "src/gateway/gateway"; 2 | 3 | /* This plugin gets called early in the life-cycle 4 | In the future, we can detect what platform we 5 | are on and include the correct gateway. 6 | 7 | The gateway just gets stored into the app global 8 | object to be called from anywhere within the 9 | frontend 10 | */ 11 | 12 | export default ({ app, router, Vue }) => { 13 | Vue.prototype.$gateway = new Gateway(app, router); 14 | }; 15 | -------------------------------------------------------------------------------- /src/boot/i18n.js: -------------------------------------------------------------------------------- 1 | import VueI18n from "vue-i18n"; 2 | import messages from "src/i18n"; 3 | import { Quasar } from "quasar"; 4 | 5 | let i18n; 6 | 7 | export default ({ app, Vue }) => { 8 | Vue.use(VueI18n); 9 | 10 | // Set i18n instance on app 11 | app.i18n = new VueI18n({ 12 | locale: "en-us", 13 | fallbackLocale: "en-us", 14 | messages 15 | }); 16 | 17 | i18n = app.i18n; 18 | }; 19 | 20 | const changeLanguage = lang => { 21 | const quasarLang = Quasar.lang; 22 | return new Promise((resolve, reject) => { 23 | import(`src/i18n/${lang}`) 24 | .then(({ default: messages }) => { 25 | i18n.locale = lang; 26 | i18n.setLocaleMessage(lang, messages); 27 | 28 | // Setting the quasar language is optional 29 | // There may be cases where they don't have the language 30 | import(`quasar/lang/${lang}`) 31 | .then(resultLang => { 32 | quasarLang.set(resultLang.default); 33 | }) 34 | .catch(() => { 35 | console.warn(`Failed to set quasar language: ${lang}`); 36 | }) 37 | .finally(() => { 38 | resolve(lang); 39 | }); 40 | }) 41 | .catch(() => { 42 | reject(new Error("Language not found")); 43 | }); 44 | }); 45 | }; 46 | 47 | export { i18n, changeLanguage }; 48 | -------------------------------------------------------------------------------- /src/boot/timeago.js: -------------------------------------------------------------------------------- 1 | import VueTimeago from "vue-timeago"; 2 | export default ({ Vue }) => { 3 | Vue.use(VueTimeago, { 4 | name: "Timeago", 5 | locale: "en", 6 | locales: { 7 | ru: require("date-fns/locale/ru"), 8 | de: require("date-fns/locale/de"), 9 | fr: require("date-fns/locale/fr"), 10 | es: require("date-fns/locale/es"), 11 | pt: require("date-fns/locale/pt") 12 | } 13 | }); 14 | }; 15 | -------------------------------------------------------------------------------- /src/boot/vuelidate.js: -------------------------------------------------------------------------------- 1 | import Vuelidate from "vuelidate"; 2 | 3 | export default ({ Vue }) => { 4 | Vue.use(Vuelidate); 5 | }; 6 | -------------------------------------------------------------------------------- /src/components/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scala-network/scala-electron-gui/1eea8a5ab48541e4c6c0333316b85e18953d7f29/src/components/.gitkeep -------------------------------------------------------------------------------- /src/components/address_header.vue: -------------------------------------------------------------------------------- 1 | 32 | 33 | 103 | 104 | 142 | -------------------------------------------------------------------------------- /src/components/check_transaction.vue: -------------------------------------------------------------------------------- 1 | 76 | 77 | 193 | 194 | 207 | -------------------------------------------------------------------------------- /src/components/footer.vue: -------------------------------------------------------------------------------- 1 | 26 | 27 | 86 | 87 | 88 | -------------------------------------------------------------------------------- /src/components/format_scala.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /src/components/icons/copy_icon.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /src/components/language_select.vue: -------------------------------------------------------------------------------- 1 | 19 | 20 | 45 | 46 | 57 | -------------------------------------------------------------------------------- /src/components/lns_input.vue: -------------------------------------------------------------------------------- 1 | 22 | 23 | 177 | 178 | 185 | -------------------------------------------------------------------------------- /src/components/mainmenu.vue: -------------------------------------------------------------------------------- 1 | 53 | 54 | 137 | 138 | 158 | -------------------------------------------------------------------------------- /src/components/prove_transaction.vue: -------------------------------------------------------------------------------- 1 | 55 | 56 | 160 | 161 | 185 | -------------------------------------------------------------------------------- /src/components/receive_item.vue: -------------------------------------------------------------------------------- 1 | 66 | 67 | 123 | 124 | 129 | -------------------------------------------------------------------------------- /src/components/scala_field.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 56 | 57 | 109 | -------------------------------------------------------------------------------- /src/components/service_node_registration.vue: -------------------------------------------------------------------------------- 1 | 40 | 41 | 142 | 143 | 148 | -------------------------------------------------------------------------------- /src/components/settings.vue: -------------------------------------------------------------------------------- 1 | 64 | 65 | 172 | 173 | 174 | -------------------------------------------------------------------------------- /src/components/tx_type_icon.vue: -------------------------------------------------------------------------------- 1 | 20 | 21 | 37 | 38 | 55 | -------------------------------------------------------------------------------- /src/components/wallet_details.vue: -------------------------------------------------------------------------------- 1 | 30 | 31 | 49 | 50 | 92 | -------------------------------------------------------------------------------- /src/css/quasar.variables.styl: -------------------------------------------------------------------------------- 1 | // App Shared Variables 2 | // -------------------------------------------------- 3 | // To customize the look and feel of this app, you can override 4 | // the Stylus variables found in Quasar"s source Stylus files. Setting 5 | // variables before Quasar"s Stylus will use these variables rather than 6 | // Quasar"s default Stylus variable values. Stylus variables specific 7 | // to the themes belong in either the variables.ios.styl or variables.mat.styl files. 8 | 9 | // Check documentation for full list of Quasar variables 10 | 11 | 12 | // App Shared Color Variables 13 | // -------------------------------------------------- 14 | // It"s highly recommended to change the default colors 15 | // to match your app"s branding. 16 | 17 | $primary = $scala-green 18 | $secondary = $scala-black-90 19 | $tertiary = $scala-black-80 20 | 21 | $neutral = #E0E1E2 22 | $positive = #2173BA 23 | $negative = #DB2828 24 | $info = #31CCEC 25 | $warning = #F2C037 26 | 27 | $scala-green = #2f333d 28 | $scala-green-solid = #5BBBCA; 29 | $scala-green-dark-solid = #307996; 30 | 31 | $scala-black-90 = #1b1e24 32 | $scala-black-80 = #1b1e24 33 | $scala-black-60 = #1b1e24 34 | $scala-black-50 = #1b1e24; -------------------------------------------------------------------------------- /src/css/themes/RobotoMono-Light.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scala-network/scala-electron-gui/1eea8a5ab48541e4c6c0333316b85e18953d7f29/src/css/themes/RobotoMono-Light.ttf -------------------------------------------------------------------------------- /src/css/themes/common.variables.styl: -------------------------------------------------------------------------------- 1 | // App Shared Variables 2 | // -------------------------------------------------- 3 | // To customize the look and feel of this app, you can override 4 | // the Stylus variables found in Quasar"s source Stylus files. Setting 5 | // variables before Quasar"s Stylus will use these variables rather than 6 | // Quasar"s default Stylus variable values. Stylus variables specific 7 | // to the themes belong in either the variables.ios.styl or variables.mat.styl files. 8 | 9 | // Check documentation for full list of Quasar variables 10 | 11 | 12 | // App Shared Color Variables 13 | // -------------------------------------------------- 14 | // It"s highly recommended to change the default colors 15 | // to match your app"s branding. 16 | 17 | $primary = $scala-green 18 | $secondary = $scala-black-90 19 | $tertiary = $scala-black-80 20 | 21 | $neutral = #E0E1E2 22 | $positive = #2173BA 23 | $negative = #DB2828 24 | $info = #31CCEC 25 | $warning = #F2C037 26 | 27 | $scala-green = #2f333d 28 | $scala-green-solid = #5BBBCA; 29 | $scala-green-dark-solid = #307996; 30 | 31 | $scala-black-90 = #1b1e24 32 | $scala-black-80 = #1b1e24 33 | $scala-black-60 = #1b1e24 34 | $scala-black-50 = #1b1e24; 35 | -------------------------------------------------------------------------------- /src/css/themes/variables.ios.styl: -------------------------------------------------------------------------------- 1 | // App Shared Variables 2 | // -------------------------------------------------- 3 | // Shared Stylus variables go in the common.variables.styl file 4 | @import "common.variables" 5 | 6 | // iOS only Quasar variables overwrites 7 | // ----------------------------------------- 8 | -------------------------------------------------------------------------------- /src/css/themes/variables.mat.styl: -------------------------------------------------------------------------------- 1 | // App Shared Variables 2 | // -------------------------------------------------- 3 | // Shared Stylus variables go in the common.variables.styl file 4 | @import "common.variables" 5 | 6 | // Material only Quasar variables overwrites 7 | // ----------------------------------------- 8 | -------------------------------------------------------------------------------- /src/gateway/SCEE-Node.js: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License 3 | 4 | Copyright (c) 2018 Luke Park 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | 25 | const crypto = require("crypto"); 26 | 27 | const ALGORITHM_NAME = "aes-128-gcm"; 28 | const ALGORITHM_NONCE_SIZE = 12; 29 | const ALGORITHM_TAG_SIZE = 16; 30 | const ALGORITHM_KEY_SIZE = 16; 31 | const PBKDF2_NAME = "sha256"; 32 | const PBKDF2_SALT_SIZE = 16; 33 | const PBKDF2_ITERATIONS = 32767; 34 | 35 | export class SCEE { 36 | encryptString(plaintext, password) { 37 | // Generate a 128-bit salt using a CSPRNG. 38 | let salt = crypto.randomBytes(PBKDF2_SALT_SIZE); 39 | 40 | // Derive a key using PBKDF2. 41 | let key = crypto.pbkdf2Sync(new Buffer(password, "utf8"), salt, PBKDF2_ITERATIONS, ALGORITHM_KEY_SIZE, PBKDF2_NAME); 42 | 43 | // Encrypt and prepend salt. 44 | let ciphertextAndNonceAndSalt = Buffer.concat([salt, this.encrypt(new Buffer(plaintext, "utf8"), key)]); 45 | 46 | // Return as base64 string. 47 | return ciphertextAndNonceAndSalt.toString("base64"); 48 | } 49 | 50 | decryptString(base64CiphertextAndNonceAndSalt, password) { 51 | // Decode the base64. 52 | let ciphertextAndNonceAndSalt = new Buffer(base64CiphertextAndNonceAndSalt, "base64"); 53 | 54 | // Create buffers of salt and ciphertextAndNonce. 55 | let salt = ciphertextAndNonceAndSalt.slice(0, PBKDF2_SALT_SIZE); 56 | let ciphertextAndNonce = ciphertextAndNonceAndSalt.slice(PBKDF2_SALT_SIZE); 57 | 58 | // Derive the key using PBKDF2. 59 | let key = crypto.pbkdf2Sync(new Buffer(password, "utf8"), salt, PBKDF2_ITERATIONS, ALGORITHM_KEY_SIZE, PBKDF2_NAME); 60 | 61 | // Decrypt and return result. 62 | return this.decrypt(ciphertextAndNonce, key).toString("utf8"); 63 | } 64 | 65 | encrypt(plaintext, key) { 66 | // Generate a 96-bit nonce using a CSPRNG. 67 | let nonce = crypto.randomBytes(ALGORITHM_NONCE_SIZE); 68 | 69 | // Create the cipher instance. 70 | let cipher = crypto.createCipheriv(ALGORITHM_NAME, key, nonce); 71 | 72 | // Encrypt and prepend nonce. 73 | let ciphertext = Buffer.concat([cipher.update(plaintext), cipher.final()]); 74 | 75 | return Buffer.concat([nonce, ciphertext, cipher.getAuthTag()]); 76 | } 77 | 78 | decrypt(ciphertextAndNonce, key) { 79 | // Create buffers of nonce, ciphertext and tag. 80 | let nonce = ciphertextAndNonce.slice(0, ALGORITHM_NONCE_SIZE); 81 | let ciphertext = ciphertextAndNonce.slice(ALGORITHM_NONCE_SIZE, ciphertextAndNonce.length - ALGORITHM_TAG_SIZE); 82 | let tag = ciphertextAndNonce.slice(ciphertext.length + ALGORITHM_NONCE_SIZE); 83 | 84 | // Create the cipher instance. 85 | let cipher = crypto.createDecipheriv(ALGORITHM_NAME, key, nonce); 86 | 87 | // Decrypt and return result. 88 | cipher.setAuthTag(tag); 89 | return Buffer.concat([cipher.update(ciphertext), cipher.final()]); 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/i18n/index.js: -------------------------------------------------------------------------------- 1 | import enUS from "./en-us"; 2 | 3 | // ADD LANGUAGES HERE 4 | const languages = [ 5 | { name: "English", code: "en-us", flag: "gb" }, 6 | { name: "Russian", code: "ru", flag: "ru" }, 7 | { name: "German", code: "de", flag: "de" }, 8 | { name: "French", code: "fr", flag: "fr" }, 9 | { name: "Spanish", code: "es", flag: "es" }, 10 | { name: "Portuguese", code: "pt-br", flag: "pt" } 11 | ]; 12 | 13 | export { languages }; 14 | 15 | // DO NOT MODIFY THIS EXPORT, LANGUAGE FILES CAN BE DYNAMICALLY LOADED 16 | export default { 17 | "en-us": enUS 18 | }; 19 | -------------------------------------------------------------------------------- /src/index.template.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | <%= htmlWebpackPlugin.options.productName %> 13 | 14 | 15 | 18 | 19 | 20 |
21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /src/layouts/init/loading.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /src/layouts/init/welcome.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /src/layouts/wallet-select/main.vue: -------------------------------------------------------------------------------- 1 | 25 | 26 | 78 | 79 | 80 | -------------------------------------------------------------------------------- /src/layouts/wallet/main.vue: -------------------------------------------------------------------------------- 1 | 50 | 51 | 78 | 79 | 134 | -------------------------------------------------------------------------------- /src/mixins/wallet_password.js: -------------------------------------------------------------------------------- 1 | import { mapState } from "vuex"; 2 | 3 | export default { 4 | computed: mapState({ 5 | theme: state => state.gateway.app.config.appearance.theme 6 | }), 7 | methods: { 8 | hasPassword() { 9 | // Validate the address 10 | return new Promise(resolve => { 11 | this.$gateway.once("has_password", data => { 12 | resolve(!!data); 13 | }); 14 | this.$gateway.send("wallet", "has_password"); 15 | }); 16 | }, 17 | 18 | async showPasswordConfirmation(options) { 19 | const { noPasswordMessage, ...other } = options; 20 | return this.hasPassword() 21 | .then(hasPassword => { 22 | const sharedOpts = { 23 | cancel: { 24 | flat: true, 25 | label: this.$t("dialog.buttons.cancel"), 26 | color: this.theme === "dark" ? "white" : "dark" 27 | }, 28 | ...other 29 | }; 30 | const hasPasswordOpts = { 31 | ...sharedOpts, 32 | message: this.$t("dialog.password.message"), 33 | prompt: { 34 | model: "", 35 | type: "password" 36 | } 37 | }; 38 | const noPasswordOpts = { 39 | ...sharedOpts, 40 | message: noPasswordMessage 41 | }; 42 | let usedOpts = hasPassword ? hasPasswordOpts : noPasswordOpts; 43 | return this.$q.dialog(usedOpts); 44 | }) 45 | .catch(() => {}); 46 | } 47 | } 48 | }; 49 | -------------------------------------------------------------------------------- /src/pages/404.vue: -------------------------------------------------------------------------------- 1 | 6 | -------------------------------------------------------------------------------- /src/pages/init/quit.vue: -------------------------------------------------------------------------------- 1 | 16 | 17 | 20 | 21 | 27 | -------------------------------------------------------------------------------- /src/pages/init/welcome.vue: -------------------------------------------------------------------------------- 1 | 30 | 31 | 91 | 92 | 138 | -------------------------------------------------------------------------------- /src/pages/wallet-select/create.vue: -------------------------------------------------------------------------------- 1 | 47 | 48 | 176 | 177 | -------------------------------------------------------------------------------- /src/pages/wallet-select/created.vue: -------------------------------------------------------------------------------- 1 | 77 | 78 | 168 | 169 | 211 | -------------------------------------------------------------------------------- /src/pages/wallet-select/import-legacy.vue: -------------------------------------------------------------------------------- 1 | 54 | 55 | 145 | 146 | 147 | -------------------------------------------------------------------------------- /src/pages/wallet-select/import-old-gui.vue: -------------------------------------------------------------------------------- 1 | 29 | 30 | 132 | 133 | 145 | -------------------------------------------------------------------------------- /src/pages/wallet-select/import.vue: -------------------------------------------------------------------------------- 1 | 60 | 61 | 165 | 166 | 180 | -------------------------------------------------------------------------------- /src/pages/wallet/addressbook.vue: -------------------------------------------------------------------------------- 1 | 66 | 67 | 156 | 157 | 186 | -------------------------------------------------------------------------------- /src/pages/wallet/advanced.vue: -------------------------------------------------------------------------------- 1 | 21 | 22 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /src/pages/wallet/blank.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 7 | 12 | -------------------------------------------------------------------------------- /src/pages/wallet/lns.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /src/pages/wallet/receive.vue: -------------------------------------------------------------------------------- 1 | 84 | 85 | 165 | 166 | 200 | -------------------------------------------------------------------------------- /src/pages/wallet/service-node.vue: -------------------------------------------------------------------------------- 1 | 24 | 25 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /src/pages/wallet/txhistory.vue: -------------------------------------------------------------------------------- 1 | 25 | 26 | 73 | 74 | 75 | -------------------------------------------------------------------------------- /src/plugins/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scala-network/scala-electron-gui/1eea8a5ab48541e4c6c0333316b85e18953d7f29/src/plugins/.gitkeep -------------------------------------------------------------------------------- /src/plugins/axios.js: -------------------------------------------------------------------------------- 1 | import axios from "axios" 2 | 3 | export default ({ 4 | Vue 5 | }) => { 6 | Vue.prototype.$axios = axios 7 | } 8 | -------------------------------------------------------------------------------- /src/plugins/gateway.js: -------------------------------------------------------------------------------- 1 | import { Gateway } from "src/gateway/gateway" 2 | 3 | /* This plugin gets called early in the life-cycle 4 | In the future, we can detect what platform we 5 | are on and include the correct gateway. 6 | 7 | The gateway just gets stored into the app global 8 | object to be called from anywhere within the 9 | frontend 10 | */ 11 | 12 | export default ({ 13 | app, 14 | router, 15 | store, 16 | Vue 17 | }) => { 18 | Vue.prototype.$gateway = new Gateway(app, router) 19 | } 20 | -------------------------------------------------------------------------------- /src/plugins/i18n.js: -------------------------------------------------------------------------------- 1 | import VueI18n from "vue-i18n" 2 | import messages from "src/i18n" 3 | import { Quasar } from "quasar" 4 | 5 | let i18n 6 | 7 | export default ({ 8 | app, 9 | Vue 10 | }) => { 11 | Vue.use(VueI18n) 12 | 13 | // Set i18n instance on app 14 | app.i18n = new VueI18n({ 15 | locale: "en-us", 16 | fallbackLocale: "en-us", 17 | messages 18 | }) 19 | 20 | i18n = app.i18n 21 | } 22 | 23 | const changeLanguage = (lang) => { 24 | const quasarLang = Quasar.i18n.lang 25 | return new Promise((resolve, reject) => { 26 | import(`src/i18n/${lang}`).then(({ default: messages }) => { 27 | i18n.locale = lang 28 | i18n.setLocaleMessage(lang, messages) 29 | 30 | // Setting the quasar language is optional 31 | // There may be cases where they don't have the language 32 | import(`quasar-framework/i18n/${lang}`).then(lang => { 33 | quasarLang.set(lang.default) 34 | }).catch(() => { 35 | console.warn(`Failed to set quasar language: ${lang}`) 36 | }).finally(() => { 37 | resolve(lang) 38 | }) 39 | }).catch(() => { 40 | reject(new Error("Language not found")) 41 | }) 42 | }) 43 | } 44 | 45 | export { i18n, changeLanguage } 46 | -------------------------------------------------------------------------------- /src/plugins/timeago.js: -------------------------------------------------------------------------------- 1 | import VueTimeago from "vue-timeago" 2 | export default ({ 3 | app, 4 | router, 5 | store, 6 | Vue 7 | }) => { 8 | Vue.use(VueTimeago, { 9 | name: "Timeago", 10 | locale: "en", 11 | locales: { 12 | ru: require("date-fns/locale/ru"), 13 | de: require("date-fns/locale/de"), 14 | fr: require("date-fns/locale/fr"), 15 | es: require("date-fns/locale/es"), 16 | pt: require("date-fns/locale/pt") 17 | } 18 | }) 19 | } 20 | -------------------------------------------------------------------------------- /src/plugins/vuelidate.js: -------------------------------------------------------------------------------- 1 | import Vuelidate from "vuelidate" 2 | 3 | export default ({ Vue }) => { 4 | Vue.use(Vuelidate) 5 | } 6 | -------------------------------------------------------------------------------- /src/router/index.js: -------------------------------------------------------------------------------- 1 | import Vue from "vue"; 2 | import VueRouter from "vue-router"; 3 | 4 | import routes from "./routes"; 5 | 6 | Vue.use(VueRouter); 7 | 8 | const Router = new VueRouter({ 9 | /* 10 | * NOTE! Change Vue Router mode from quasar.conf.js -> build -> vueRouterMode 11 | * 12 | * When going with "history" mode, please also make sure "build.publicPath" 13 | * is set to something other than an empty string. 14 | * Example: "/" instead of "" 15 | */ 16 | 17 | // Leave as is and change from quasar.conf.js instead! 18 | mode: process.env.VUE_ROUTER_MODE, 19 | base: process.env.VUE_ROUTER_BASE, 20 | scrollBehavior: () => ({ 21 | y: 0 22 | }), 23 | routes 24 | }); 25 | 26 | export default Router; 27 | -------------------------------------------------------------------------------- /src/router/routes.js: -------------------------------------------------------------------------------- 1 | export default [ 2 | { 3 | path: "/", 4 | component: () => import("layouts/init/loading"), 5 | children: [ 6 | { 7 | path: "", 8 | component: () => import("pages/init/index") 9 | }, 10 | { 11 | path: "/quit", 12 | component: () => import("pages/init/quit") 13 | } 14 | ] 15 | }, 16 | { 17 | path: "/welcome", 18 | component: () => import("layouts/init/welcome"), 19 | children: [ 20 | { 21 | path: "", 22 | component: () => import("pages/init/welcome") 23 | } 24 | ] 25 | }, 26 | { 27 | path: "/wallet-select", 28 | component: () => import("layouts/wallet-select/main"), 29 | children: [ 30 | { 31 | path: "", 32 | name: "wallet-select", 33 | component: () => import("pages/wallet-select/index") 34 | }, 35 | { 36 | path: "create", 37 | name: "wallet-create", 38 | component: () => import("pages/wallet-select/create") 39 | }, 40 | { 41 | path: "restore", 42 | name: "wallet-restore", 43 | component: () => import("pages/wallet-select/restore") 44 | }, 45 | { 46 | path: "import-view-only", 47 | name: "wallet-import-view-only", 48 | component: () => import("pages/wallet-select/import-view-only") 49 | }, 50 | { 51 | path: "import", 52 | name: "wallet-import", 53 | component: () => import("pages/wallet-select/import") 54 | }, 55 | { 56 | path: "import-legacy", 57 | name: "wallet-import-legacy", 58 | component: () => import("pages/wallet-select/import-legacy") 59 | }, 60 | { 61 | path: "created", 62 | name: "wallet-created", 63 | component: () => import("pages/wallet-select/created") 64 | }, 65 | { 66 | path: "import-old-gui", 67 | name: "wallet-import-old-gui", 68 | component: () => import("pages/wallet-select/import-old-gui") 69 | } 70 | ] 71 | }, 72 | { 73 | path: "/wallet", 74 | component: () => import("layouts/wallet/main"), 75 | children: [ 76 | { 77 | path: "", 78 | component: () => import("pages/wallet/txhistory") 79 | }, 80 | { 81 | path: "receive", 82 | component: () => import("pages/wallet/receive") 83 | }, 84 | { 85 | path: "send", 86 | component: () => import("pages/wallet/send") 87 | }, 88 | { 89 | path: "addressbook", 90 | component: () => import("pages/wallet/addressbook") 91 | }, 92 | { 93 | path: "servicenode", 94 | component: () => import("pages/wallet/service-node") 95 | }, 96 | { 97 | path: "lns", 98 | component: () => import("pages/wallet/lns") 99 | }, 100 | { 101 | path: "advanced", 102 | component: () => import("pages/wallet/advanced") 103 | } 104 | ] 105 | }, 106 | 107 | { 108 | // Always leave this as last one 109 | path: "*", 110 | component: () => import("pages/404") 111 | } 112 | ]; 113 | -------------------------------------------------------------------------------- /src/statics/icon_512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scala-network/scala-electron-gui/1eea8a5ab48541e4c6c0333316b85e18953d7f29/src/statics/icon_512x512.png -------------------------------------------------------------------------------- /src/statics/qr-code-grey.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/statics/qr-code.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/statics/ryo-wallet.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /src/statics/scala.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | 10 | 11 | 33 | 48 | 50 | 51 | 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /src/store/gateway/actions.js: -------------------------------------------------------------------------------- 1 | export const resetWalletData = state => { 2 | state.commit("set_wallet_data", { 3 | status: { 4 | code: 1, 5 | message: null 6 | }, 7 | info: { 8 | name: "", 9 | address: "", 10 | height: 0, 11 | balance: 0, 12 | unlocked_balance: 0, 13 | view_only: false 14 | }, 15 | secret: { 16 | mnemonic: "", 17 | view_key: "", 18 | spend_key: "" 19 | }, 20 | transactions: { 21 | tx_list: [] 22 | }, 23 | address_list: { 24 | used: [], 25 | unused: [], 26 | address_book: [] 27 | } 28 | }); 29 | }; 30 | 31 | export const resetWalletStatus = state => { 32 | state.commit("set_wallet_data", { 33 | status: { 34 | code: 1, 35 | message: null 36 | } 37 | }); 38 | }; 39 | 40 | export const resetPendingConfig = state => { 41 | state.commit("set_app_data", { 42 | pending_config: state.state.app.config 43 | }); 44 | }; 45 | -------------------------------------------------------------------------------- /src/store/gateway/getters.js: -------------------------------------------------------------------------------- 1 | export const isReady = state => { 2 | const { daemons, app } = state.app.config; 3 | const config_daemon = daemons[app.net_type]; 4 | 5 | let target_height; 6 | if (config_daemon.type === "local") { 7 | target_height = Math.max(state.daemon.info.height, state.daemon.info.target_height); 8 | } else { 9 | target_height = state.daemon.info.height; 10 | } 11 | 12 | return state.wallet.info.height >= target_height - 1; 13 | }; 14 | 15 | export const isAbleToSend = state => { 16 | const { daemons, app } = state.app.config; 17 | const config_daemon = daemons[app.net_type]; 18 | 19 | let target_height; 20 | if (config_daemon.type === "local") { 21 | target_height = Math.max(state.daemon.info.height, state.daemon.info.target_height); 22 | } else { 23 | target_height = state.daemon.info.height; 24 | } 25 | 26 | if (config_daemon.type === "local_remote") { 27 | return state.daemon.info.height_without_bootstrap >= target_height && state.wallet.info.height >= target_height - 1; 28 | } else { 29 | return state.wallet.info.height >= target_height - 1; 30 | } 31 | }; 32 | -------------------------------------------------------------------------------- /src/store/gateway/index.js: -------------------------------------------------------------------------------- 1 | import state from "./state"; 2 | import * as getters from "./getters"; 3 | import * as mutations from "./mutations"; 4 | import * as actions from "./actions"; 5 | 6 | export default { 7 | namespaced: true, 8 | state, 9 | getters, 10 | mutations, 11 | actions 12 | }; 13 | -------------------------------------------------------------------------------- /src/store/gateway/mutations.js: -------------------------------------------------------------------------------- 1 | const objectAssignDeep = require("object-assign-deep"); 2 | 3 | export const set_app_data = (state, data) => { 4 | state.app = objectAssignDeep.noMutate(state.app, data); 5 | }; 6 | export const set_daemon_data = (state, data) => { 7 | state.daemon = objectAssignDeep.noMutate(state.daemon, data); 8 | }; 9 | export const set_wallet_data = (state, data) => { 10 | state.wallet = objectAssignDeep.noMutate(state.wallet, data); 11 | }; 12 | export const set_wallet_list = (state, data) => { 13 | state.wallets = objectAssignDeep.noMutate(state.wallets, data); 14 | }; 15 | export const set_old_gui_import_status = (state, data) => { 16 | state.old_gui_import_status = data; 17 | }; 18 | export const set_tx_status = (state, data) => { 19 | state.tx_status = data; 20 | }; 21 | export const set_snode_status = (state, data) => { 22 | state.service_node_status = objectAssignDeep.noMutate(state.service_node_status, data); 23 | }; 24 | export const set_prove_transaction_status = (state, data) => { 25 | state.prove_transaction_status = { 26 | ...state.prove_transaction_status, 27 | ...data 28 | }; 29 | }; 30 | export const set_check_transaction_status = (state, data) => { 31 | state.check_transaction_status = { 32 | ...state.check_transaction_status, 33 | ...data 34 | }; 35 | }; 36 | export const set_lns_status = (state, data) => { 37 | state.lns_status = data; 38 | }; 39 | -------------------------------------------------------------------------------- /src/store/gateway/state.js: -------------------------------------------------------------------------------- 1 | export default { 2 | app: { 3 | status: { 4 | code: 1 // Connecting to backend 5 | }, 6 | config: { 7 | appearance: { 8 | theme: "dark" 9 | } 10 | }, 11 | pending_config: {}, 12 | remotes: {} 13 | }, 14 | wallets: { 15 | list: [], 16 | legacy: [], 17 | 18 | // List of wallets that are in a sub folder (format of the old GUI) 19 | directories: [] 20 | }, 21 | old_gui_import_status: { 22 | code: 0, // Success 23 | failed_wallets: [] 24 | }, 25 | wallet: { 26 | status: { 27 | code: 1, 28 | message: null 29 | }, 30 | info: { 31 | name: "", 32 | address: "", 33 | height: 0, 34 | balance: 0, 35 | unlocked_balance: 0, 36 | view_only: false 37 | }, 38 | secret: { 39 | mnemonic: "", 40 | view_key: "", 41 | spend_key: "" 42 | }, 43 | transactions: { 44 | tx_list: [] 45 | }, 46 | address_list: { 47 | used: [], 48 | unused: [], 49 | address_book: [] 50 | }, 51 | lnsRecords: [], 52 | isRPCSyncing: false 53 | }, 54 | tx_status: { 55 | code: 0, 56 | message: "", 57 | i18n: "", 58 | sending: false 59 | }, 60 | service_node_status: { 61 | stake: { 62 | code: 0, 63 | message: "", 64 | i18n: "", 65 | sending: false 66 | }, 67 | registration: { 68 | code: 0, 69 | message: "", 70 | i18n: "", 71 | sending: false 72 | }, 73 | unlock: { 74 | code: 0, 75 | message: "", 76 | i18n: "", 77 | sending: false 78 | } 79 | }, 80 | prove_transaction_status: { 81 | code: 0, 82 | message: "", 83 | i18n: "", 84 | state: {} 85 | }, 86 | check_transaction_status: { 87 | code: 0, 88 | message: "", 89 | i18n: "", 90 | state: {} 91 | }, 92 | lns_status: { 93 | code: 0, 94 | message: "", 95 | i18n: "", 96 | sending: false 97 | }, 98 | daemon: { 99 | info: { 100 | alt_blocks_count: 0, 101 | cumulative_difficulty: 0, 102 | difficulty: 0, 103 | grey_peerlist_size: 0, 104 | height: 0, 105 | height_without_bootstrap: 0, 106 | incoming_connections_count: 0, 107 | is_ready: false, 108 | outgoing_connections_count: 0, 109 | status: "OK", 110 | target: 240, 111 | target_height: 0, 112 | testnet: false, 113 | top_block_hash: null, 114 | tx_count: 0, 115 | tx_pool_size: 0, 116 | white_peerlist_size: 0 117 | }, 118 | connections: [], 119 | bans: [], 120 | tx_pool_backlog: [], 121 | service_nodes: [] 122 | } 123 | }; 124 | -------------------------------------------------------------------------------- /src/store/index.js: -------------------------------------------------------------------------------- 1 | import Vue from "vue"; 2 | import Vuex from "vuex"; 3 | 4 | import gateway from "./gateway"; 5 | 6 | Vue.use(Vuex); 7 | 8 | const store = new Vuex.Store({ 9 | modules: { 10 | gateway 11 | } 12 | }); 13 | 14 | export default store; 15 | -------------------------------------------------------------------------------- /src/store/store-flag.d.ts: -------------------------------------------------------------------------------- 1 | // THIS FEATURE-FLAG FILE IS AUTOGENERATED, 2 | // REMOVAL OR CHANGES WILL CAUSE RELATED TYPES TO STOP WORKING 3 | import "quasar/dist/types/feature-flag"; 4 | 5 | declare module "quasar/dist/types/feature-flag" { 6 | interface QuasarFeatureFlags { 7 | store: true; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/validators/common.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable prefer-promise-reject-errors */ 2 | 3 | export const greater_than_zero = input => { 4 | return input > 0; 5 | }; 6 | 7 | export const payment_id = input => { 8 | return input.length === 0 || (/^[0-9A-Fa-f]+$/.test(input) && (input.length == 16 || input.length == 64)); 9 | }; 10 | 11 | export const privkey = input => { 12 | return input.length === 0 || (/^[0-9A-Fa-f]+$/.test(input) && input.length == 64); 13 | }; 14 | 15 | export const service_node_key = input => { 16 | return input.length === 64 && /^[0-9A-Za-z]+$/.test(input); 17 | }; 18 | 19 | export const session_id = input => { 20 | return input.length == 66 && /^05[0-9A-Za-z]+$/.test(input); 21 | }; 22 | 23 | export const lns_name = input => { 24 | return input.length === 0 || /^[a-z0-9_]([a-z0-9-_]*[a-z0-9_])?$/.test(input.toLowerCase()); 25 | }; 26 | 27 | export const address = (input, gateway) => { 28 | if (!/^[0-9A-Za-z]+$/.test(input)) return false; 29 | 30 | // Validate the address 31 | return new Promise((resolve, reject) => { 32 | gateway.once("validate_address", data => { 33 | if (data.address && data.address !== input) { 34 | reject(); 35 | } else { 36 | if (data.valid) { 37 | resolve(); 38 | } else { 39 | reject(); 40 | } 41 | } 42 | }); 43 | gateway.send("wallet", "validate_address", { 44 | address: input 45 | }); 46 | }); 47 | }; 48 | --------------------------------------------------------------------------------