├── frontend ├── package.json.md5 ├── babel.config.js ├── src │ ├── assets │ │ ├── img │ │ │ ├── dotz.png │ │ │ ├── nodes.jpeg │ │ │ ├── nodes.jpg │ │ │ ├── nodes2.jpg │ │ │ ├── spots.png │ │ │ ├── spots4.png │ │ │ ├── blockchain.jpg │ │ │ ├── faces │ │ │ │ └── face-0.jpg │ │ │ ├── nodes2_dark.jpg │ │ │ ├── nodes_dark.jpeg │ │ │ ├── protocol-2.png │ │ │ ├── Constellation-Logo-Black.png │ │ │ ├── Constellation-Logo-White.png │ │ │ ├── Constellation-Logo-Sm-Black.png │ │ │ ├── Constellation-Logo-Sm-White.png │ │ │ ├── Constellation-Logo-Sm-Black2.png │ │ │ └── Constellation-Logo-Sm-White2.png │ │ ├── fonts │ │ │ ├── themify.eot │ │ │ ├── themify.ttf │ │ │ ├── themify.woff │ │ │ ├── fa-solid-900.eot │ │ │ ├── fa-solid-900.ttf │ │ │ ├── fa-brands-400.eot │ │ │ ├── fa-brands-400.ttf │ │ │ ├── fa-brands-400.woff │ │ │ ├── fa-brands-400.woff2 │ │ │ ├── fa-regular-400.eot │ │ │ ├── fa-regular-400.ttf │ │ │ ├── fa-regular-400.woff │ │ │ ├── fa-solid-900.woff │ │ │ ├── fa-solid-900.woff2 │ │ │ ├── fa-regular-400.woff2 │ │ │ ├── roboto │ │ │ │ ├── roboto-v18-latin-regular.eot │ │ │ │ ├── roboto-v18-latin-regular.ttf │ │ │ │ ├── roboto-v18-latin-regular.woff │ │ │ │ └── roboto-v18-latin-regular.woff2 │ │ │ ├── glyphicons-halflings-regular.e18bbf6.ttf │ │ │ ├── glyphicons-halflings-regular.f4769f9.eot │ │ │ ├── glyphicons-halflings-regular.fa27723.woff │ │ │ └── glyphicons-halflings-regular.448c34a.woff2 │ │ └── sass │ │ │ ├── paper │ │ │ ├── mixins │ │ │ │ ├── _tabs.scss │ │ │ │ ├── _cards.scss │ │ │ │ ├── _sidebar.scss │ │ │ │ ├── _navbars.scss │ │ │ │ ├── _icons.scss │ │ │ │ ├── _inputs.scss │ │ │ │ ├── _transparency.scss │ │ │ │ ├── _labels.scss │ │ │ │ ├── _buttons.scss │ │ │ │ └── _chartist.scss │ │ │ ├── _mixins.scss │ │ │ ├── sweetalert2.scss │ │ │ ├── _select.scss │ │ │ ├── _alerts.scss │ │ │ ├── _footers.scss │ │ │ ├── _tables.scss │ │ │ ├── _checkbox-radio.scss │ │ │ ├── _misc.scss │ │ │ ├── _wallet.scss │ │ │ ├── _themes.scss │ │ │ ├── _typography.scss │ │ │ ├── _sidebar-and-main-panel.scss │ │ │ ├── _inputs.scss │ │ │ ├── _cards.scss │ │ │ └── _buttons.scss │ │ │ ├── font_awesome │ │ │ ├── _fixed-width.scss │ │ │ ├── _screen-reader.scss │ │ │ ├── v4-shims.scss │ │ │ ├── _animated.scss │ │ │ ├── _list.scss │ │ │ ├── _core.scss │ │ │ ├── _larger.scss │ │ │ ├── fontawesome.scss │ │ │ ├── _bordered-pulled.scss │ │ │ ├── _stacked.scss │ │ │ ├── brands.scss │ │ │ ├── solid.scss │ │ │ ├── regular.scss │ │ │ ├── _rotated-flipped.scss │ │ │ └── _mixins.scss │ │ │ └── paper-dashboard.scss │ ├── pages │ │ ├── Notifications │ │ │ ├── Success.vue │ │ │ ├── Warning.vue │ │ │ ├── ImageUploaded.vue │ │ │ ├── PathBlocked.vue │ │ │ ├── TxSent.vue │ │ │ ├── WalletCopiedFailed.vue │ │ │ ├── WalletCopied.vue │ │ │ ├── PendingNotification.vue │ │ │ ├── NewRelease.vue │ │ │ └── ErrorMessage.vue │ │ ├── LoadingScreen.vue │ │ ├── WalletInformation.vue │ │ ├── DownloadingScreen.vue │ │ ├── NotFoundPage.vue │ │ ├── UserProfile │ │ │ ├── UserCard.vue │ │ │ └── EditProfileForm.vue │ │ └── Login.vue │ ├── layout │ │ ├── dashboard │ │ │ ├── MobileMenu.vue │ │ │ ├── Content.vue │ │ │ ├── TopNavbar.vue │ │ │ └── DashboardLayout.vue │ │ └── login │ │ │ └── LoginLayout.vue │ ├── router │ │ ├── index.js │ │ └── routes.js │ ├── plugins │ │ ├── globalDirectives.js │ │ ├── paperDashboard.js │ │ ├── globalComponents.js │ │ └── globalMethods.js │ ├── components │ │ ├── SidebarPlugin │ │ │ ├── MovingArrow.vue │ │ │ ├── index.js │ │ │ ├── SidebarLink.vue │ │ │ └── SideBar.vue │ │ ├── Cards │ │ │ ├── WideCard.vue │ │ │ ├── StatsCard.vue │ │ │ ├── Card.vue │ │ │ └── ChartCard.vue │ │ ├── index.js │ │ ├── Overlay.vue │ │ ├── Button.vue │ │ ├── Dropdown.vue │ │ ├── Inputs │ │ │ ├── formGroupInput.vue │ │ │ ├── FileSelector.vue │ │ │ └── Password.vue │ │ ├── PaperTable.vue │ │ └── Loader.vue │ ├── store │ │ ├── store.js │ │ └── modules │ │ │ ├── transaction.js │ │ │ ├── app.js │ │ │ ├── wallet.js │ │ │ └── dashboard.js │ └── main.js ├── .gitignore ├── vue.config.js └── package.json ├── .gitattributes ├── appicon.png ├── backend ├── .DS_Store ├── models │ ├── address.go │ ├── path.go │ ├── txhistory.go │ └── wallet.go ├── api │ └── rpc.go ├── lcm.go ├── utils.go ├── login.go └── settings.go ├── assets └── appicon.png ├── .gitignore ├── project.json ├── go.mod ├── main.go ├── LICENSE ├── Makefile ├── docs └── faq.md └── readme.md /frontend/package.json.md5: -------------------------------------------------------------------------------- 1 | 3efa6e8b927d5ad25a172f56fca8cf6a -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.css linguist-vendored 2 | *.scss linguist-vendored 3 | -------------------------------------------------------------------------------- /appicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grvlle/constellation_wallet/HEAD/appicon.png -------------------------------------------------------------------------------- /backend/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grvlle/constellation_wallet/HEAD/backend/.DS_Store -------------------------------------------------------------------------------- /frontend/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | '@vue/app' 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /assets/appicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grvlle/constellation_wallet/HEAD/assets/appicon.png -------------------------------------------------------------------------------- /backend/models/address.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | // Address holds the DAG address 4 | type Address string 5 | -------------------------------------------------------------------------------- /frontend/src/assets/img/dotz.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grvlle/constellation_wallet/HEAD/frontend/src/assets/img/dotz.png -------------------------------------------------------------------------------- /frontend/src/assets/img/nodes.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grvlle/constellation_wallet/HEAD/frontend/src/assets/img/nodes.jpeg -------------------------------------------------------------------------------- /frontend/src/assets/img/nodes.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grvlle/constellation_wallet/HEAD/frontend/src/assets/img/nodes.jpg -------------------------------------------------------------------------------- /frontend/src/assets/img/nodes2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grvlle/constellation_wallet/HEAD/frontend/src/assets/img/nodes2.jpg -------------------------------------------------------------------------------- /frontend/src/assets/img/spots.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grvlle/constellation_wallet/HEAD/frontend/src/assets/img/spots.png -------------------------------------------------------------------------------- /frontend/src/assets/img/spots4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grvlle/constellation_wallet/HEAD/frontend/src/assets/img/spots4.png -------------------------------------------------------------------------------- /frontend/src/assets/fonts/themify.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grvlle/constellation_wallet/HEAD/frontend/src/assets/fonts/themify.eot -------------------------------------------------------------------------------- /frontend/src/assets/fonts/themify.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grvlle/constellation_wallet/HEAD/frontend/src/assets/fonts/themify.ttf -------------------------------------------------------------------------------- /frontend/src/assets/fonts/themify.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grvlle/constellation_wallet/HEAD/frontend/src/assets/fonts/themify.woff -------------------------------------------------------------------------------- /frontend/src/assets/img/blockchain.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grvlle/constellation_wallet/HEAD/frontend/src/assets/img/blockchain.jpg -------------------------------------------------------------------------------- /frontend/src/assets/img/faces/face-0.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grvlle/constellation_wallet/HEAD/frontend/src/assets/img/faces/face-0.jpg -------------------------------------------------------------------------------- /frontend/src/assets/img/nodes2_dark.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grvlle/constellation_wallet/HEAD/frontend/src/assets/img/nodes2_dark.jpg -------------------------------------------------------------------------------- /frontend/src/assets/img/nodes_dark.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grvlle/constellation_wallet/HEAD/frontend/src/assets/img/nodes_dark.jpeg -------------------------------------------------------------------------------- /frontend/src/assets/img/protocol-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grvlle/constellation_wallet/HEAD/frontend/src/assets/img/protocol-2.png -------------------------------------------------------------------------------- /frontend/src/assets/fonts/fa-solid-900.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grvlle/constellation_wallet/HEAD/frontend/src/assets/fonts/fa-solid-900.eot -------------------------------------------------------------------------------- /frontend/src/assets/fonts/fa-solid-900.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grvlle/constellation_wallet/HEAD/frontend/src/assets/fonts/fa-solid-900.ttf -------------------------------------------------------------------------------- /frontend/src/assets/sass/paper/mixins/_tabs.scss: -------------------------------------------------------------------------------- 1 | @mixin pill-style($color) { 2 | border: 0.0625em solid $color; 3 | color: $color; 4 | } 5 | -------------------------------------------------------------------------------- /frontend/src/assets/fonts/fa-brands-400.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grvlle/constellation_wallet/HEAD/frontend/src/assets/fonts/fa-brands-400.eot -------------------------------------------------------------------------------- /frontend/src/assets/fonts/fa-brands-400.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grvlle/constellation_wallet/HEAD/frontend/src/assets/fonts/fa-brands-400.ttf -------------------------------------------------------------------------------- /frontend/src/assets/fonts/fa-brands-400.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grvlle/constellation_wallet/HEAD/frontend/src/assets/fonts/fa-brands-400.woff -------------------------------------------------------------------------------- /frontend/src/assets/fonts/fa-brands-400.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grvlle/constellation_wallet/HEAD/frontend/src/assets/fonts/fa-brands-400.woff2 -------------------------------------------------------------------------------- /frontend/src/assets/fonts/fa-regular-400.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grvlle/constellation_wallet/HEAD/frontend/src/assets/fonts/fa-regular-400.eot -------------------------------------------------------------------------------- /frontend/src/assets/fonts/fa-regular-400.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grvlle/constellation_wallet/HEAD/frontend/src/assets/fonts/fa-regular-400.ttf -------------------------------------------------------------------------------- /frontend/src/assets/fonts/fa-regular-400.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grvlle/constellation_wallet/HEAD/frontend/src/assets/fonts/fa-regular-400.woff -------------------------------------------------------------------------------- /frontend/src/assets/fonts/fa-solid-900.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grvlle/constellation_wallet/HEAD/frontend/src/assets/fonts/fa-solid-900.woff -------------------------------------------------------------------------------- /frontend/src/assets/fonts/fa-solid-900.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grvlle/constellation_wallet/HEAD/frontend/src/assets/fonts/fa-solid-900.woff2 -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | mollywallet 2 | mollywallet.exe 3 | nohup.out 4 | mollywallet-res.syso 5 | mollywallet.exe.manifest 6 | mollywallet.rc 7 | build 8 | .DS_Store 9 | -------------------------------------------------------------------------------- /frontend/src/assets/fonts/fa-regular-400.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grvlle/constellation_wallet/HEAD/frontend/src/assets/fonts/fa-regular-400.woff2 -------------------------------------------------------------------------------- /frontend/src/assets/img/Constellation-Logo-Black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grvlle/constellation_wallet/HEAD/frontend/src/assets/img/Constellation-Logo-Black.png -------------------------------------------------------------------------------- /frontend/src/assets/img/Constellation-Logo-White.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grvlle/constellation_wallet/HEAD/frontend/src/assets/img/Constellation-Logo-White.png -------------------------------------------------------------------------------- /frontend/src/assets/img/Constellation-Logo-Sm-Black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grvlle/constellation_wallet/HEAD/frontend/src/assets/img/Constellation-Logo-Sm-Black.png -------------------------------------------------------------------------------- /frontend/src/assets/img/Constellation-Logo-Sm-White.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grvlle/constellation_wallet/HEAD/frontend/src/assets/img/Constellation-Logo-Sm-White.png -------------------------------------------------------------------------------- /frontend/src/assets/img/Constellation-Logo-Sm-Black2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grvlle/constellation_wallet/HEAD/frontend/src/assets/img/Constellation-Logo-Sm-Black2.png -------------------------------------------------------------------------------- /frontend/src/assets/img/Constellation-Logo-Sm-White2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grvlle/constellation_wallet/HEAD/frontend/src/assets/img/Constellation-Logo-Sm-White2.png -------------------------------------------------------------------------------- /frontend/src/assets/fonts/roboto/roboto-v18-latin-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grvlle/constellation_wallet/HEAD/frontend/src/assets/fonts/roboto/roboto-v18-latin-regular.eot -------------------------------------------------------------------------------- /frontend/src/assets/fonts/roboto/roboto-v18-latin-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grvlle/constellation_wallet/HEAD/frontend/src/assets/fonts/roboto/roboto-v18-latin-regular.ttf -------------------------------------------------------------------------------- /frontend/src/assets/fonts/roboto/roboto-v18-latin-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grvlle/constellation_wallet/HEAD/frontend/src/assets/fonts/roboto/roboto-v18-latin-regular.woff -------------------------------------------------------------------------------- /frontend/src/assets/fonts/roboto/roboto-v18-latin-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grvlle/constellation_wallet/HEAD/frontend/src/assets/fonts/roboto/roboto-v18-latin-regular.woff2 -------------------------------------------------------------------------------- /frontend/src/assets/fonts/glyphicons-halflings-regular.e18bbf6.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grvlle/constellation_wallet/HEAD/frontend/src/assets/fonts/glyphicons-halflings-regular.e18bbf6.ttf -------------------------------------------------------------------------------- /frontend/src/assets/fonts/glyphicons-halflings-regular.f4769f9.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grvlle/constellation_wallet/HEAD/frontend/src/assets/fonts/glyphicons-halflings-regular.f4769f9.eot -------------------------------------------------------------------------------- /frontend/src/assets/fonts/glyphicons-halflings-regular.fa27723.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grvlle/constellation_wallet/HEAD/frontend/src/assets/fonts/glyphicons-halflings-regular.fa27723.woff -------------------------------------------------------------------------------- /frontend/src/assets/fonts/glyphicons-halflings-regular.448c34a.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grvlle/constellation_wallet/HEAD/frontend/src/assets/fonts/glyphicons-halflings-regular.448c34a.woff2 -------------------------------------------------------------------------------- /frontend/src/assets/sass/font_awesome/_fixed-width.scss: -------------------------------------------------------------------------------- 1 | // Fixed Width Icons 2 | // ------------------------- 3 | .#{$fa-css-prefix}-fw { 4 | text-align: center; 5 | width: $fa-fw-width; 6 | } 7 | -------------------------------------------------------------------------------- /frontend/src/assets/sass/font_awesome/_screen-reader.scss: -------------------------------------------------------------------------------- 1 | // Screen Readers 2 | // ------------------------- 3 | 4 | .sr-only { @include sr-only; } 5 | .sr-only-focusable { @include sr-only-focusable; } 6 | -------------------------------------------------------------------------------- /frontend/src/assets/sass/paper/mixins/_cards.scss: -------------------------------------------------------------------------------- 1 | @mixin filter($color) { 2 | @if $color == #FFFFFF { 3 | background-color: rgba($color, .91); 4 | } @else { 5 | background-color: rgba($color, .69); 6 | } 7 | } 8 | 9 | -------------------------------------------------------------------------------- /frontend/src/pages/Notifications/Success.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 10 | -------------------------------------------------------------------------------- /frontend/src/pages/Notifications/Warning.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 10 | -------------------------------------------------------------------------------- /frontend/src/layout/dashboard/MobileMenu.vue: -------------------------------------------------------------------------------- 1 | 6 | 9 | 11 | -------------------------------------------------------------------------------- /frontend/src/pages/Notifications/ImageUploaded.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 10 | -------------------------------------------------------------------------------- /frontend/src/pages/Notifications/PathBlocked.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 10 | -------------------------------------------------------------------------------- /frontend/src/pages/Notifications/TxSent.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 10 | -------------------------------------------------------------------------------- /frontend/src/pages/Notifications/WalletCopiedFailed.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 10 | -------------------------------------------------------------------------------- /backend/models/path.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | // Path carries the file paths 4 | type Path struct { 5 | ID uint `json:"id"` 6 | Alias string `json:"alias"` 7 | LastTXFile string 8 | PrevTXFile string 9 | EmptyTXFile string 10 | } 11 | -------------------------------------------------------------------------------- /frontend/src/assets/sass/paper/mixins/_sidebar.scss: -------------------------------------------------------------------------------- 1 | @mixin sidebar-active-color($font-color) { 2 | .nav { 3 | .nav-item { 4 | &.active > .nav-link { 5 | color: $font-color; 6 | opacity: 1; 7 | } 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /frontend/src/pages/Notifications/WalletCopied.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 10 | -------------------------------------------------------------------------------- /frontend/src/assets/sass/paper/mixins/_navbars.scss: -------------------------------------------------------------------------------- 1 | @mixin navbar-color($color) { 2 | background-color: $color; 3 | } 4 | 5 | @mixin center-item() { 6 | left: 0; 7 | right: 0; 8 | margin-right: auto; 9 | margin-left: auto; 10 | position: absolute; 11 | } 12 | -------------------------------------------------------------------------------- /frontend/src/pages/Notifications/PendingNotification.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 10 | -------------------------------------------------------------------------------- /frontend/src/assets/sass/font_awesome/v4-shims.scss: -------------------------------------------------------------------------------- 1 | /*! 2 | * Font Awesome Free 5.10.1 by @fontawesome - https://fontawesome.com 3 | * License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) 4 | */ 5 | @import 'variables'; 6 | @import 'shims'; 7 | -------------------------------------------------------------------------------- /frontend/src/pages/Notifications/NewRelease.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 10 | -------------------------------------------------------------------------------- /frontend/src/layout/dashboard/Content.vue: -------------------------------------------------------------------------------- 1 | 9 | 12 | 14 | -------------------------------------------------------------------------------- /frontend/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /dist 4 | 5 | # local env files 6 | .env.local 7 | .env.*.local 8 | 9 | # Log files 10 | npm-debug.log* 11 | yarn-debug.log* 12 | yarn-error.log* 13 | 14 | # Editor directories and files 15 | .idea 16 | .vscode 17 | *.suo 18 | *.ntvs* 19 | *.njsproj 20 | *.sln 21 | *.sw* 22 | -------------------------------------------------------------------------------- /frontend/src/assets/sass/paper/mixins/_icons.scss: -------------------------------------------------------------------------------- 1 | @mixin icon-background($icon-url) { 2 | background-image: url($icon-url); 3 | 4 | } 5 | 6 | @mixin icon-shape($size, $padding, $border-radius) { 7 | height: $size; 8 | width: $size; 9 | padding: $padding; 10 | border-radius: $border-radius; 11 | display: inline-table; 12 | 13 | } 14 | -------------------------------------------------------------------------------- /frontend/src/router/index.js: -------------------------------------------------------------------------------- 1 | import Vue from "vue"; 2 | import VueRouter from "vue-router"; 3 | import routes from "./routes"; 4 | Vue.use(VueRouter); 5 | 6 | // configure router 7 | const router = new VueRouter({ 8 | routes, // short for routes: routes 9 | mode: 'abstract', 10 | linkActiveClass: "active" 11 | }); 12 | 13 | export default router; 14 | -------------------------------------------------------------------------------- /frontend/src/plugins/globalDirectives.js: -------------------------------------------------------------------------------- 1 | import { directive as vClickOutside } from "vue-clickaway"; 2 | 3 | /** 4 | * You can register global directives here and use them as a plugin in your main Vue instance 5 | */ 6 | 7 | const GlobalDirectives = { 8 | install(Vue) { 9 | Vue.directive("click-outside", vClickOutside); 10 | } 11 | }; 12 | 13 | export default GlobalDirectives; 14 | -------------------------------------------------------------------------------- /frontend/src/assets/sass/paper/_mixins.scss: -------------------------------------------------------------------------------- 1 | //Utilities 2 | @import "mixins/transparency"; 3 | @import "mixins/vendor-prefixes"; 4 | //Components 5 | @import "mixins/buttons"; 6 | @import "mixins/inputs"; 7 | @import "mixins/labels"; 8 | @import "mixins/tabs"; 9 | @import "mixins/navbars"; 10 | @import "mixins/icons"; 11 | @import "mixins/cards"; 12 | @import "mixins/chartist"; 13 | @import "mixins/sidebar"; 14 | -------------------------------------------------------------------------------- /frontend/src/assets/sass/font_awesome/_animated.scss: -------------------------------------------------------------------------------- 1 | // Animated Icons 2 | // -------------------------- 3 | 4 | .#{$fa-css-prefix}-spin { 5 | animation: fa-spin 2s infinite linear; 6 | } 7 | 8 | .#{$fa-css-prefix}-pulse { 9 | animation: fa-spin 1s infinite steps(8); 10 | } 11 | 12 | @keyframes fa-spin { 13 | 0% { 14 | transform: rotate(0deg); 15 | } 16 | 17 | 100% { 18 | transform: rotate(360deg); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /frontend/src/assets/sass/font_awesome/_list.scss: -------------------------------------------------------------------------------- 1 | // List Icons 2 | // ------------------------- 3 | 4 | .#{$fa-css-prefix}-ul { 5 | list-style-type: none; 6 | margin-left: $fa-li-width * 5/4; 7 | padding-left: 0; 8 | 9 | > li { position: relative; } 10 | } 11 | 12 | .#{$fa-css-prefix}-li { 13 | left: -$fa-li-width; 14 | position: absolute; 15 | text-align: center; 16 | width: $fa-li-width; 17 | line-height: inherit; 18 | } 19 | -------------------------------------------------------------------------------- /frontend/src/assets/sass/paper/mixins/_inputs.scss: -------------------------------------------------------------------------------- 1 | @mixin input-size($padding-vertical, $padding-horizontal, $height) { 2 | padding: $padding-vertical $padding-horizontal; 3 | height: $height; 4 | } 5 | 6 | @mixin placeholder($color, $opacity) { 7 | color: $color; 8 | @include opacity(1); 9 | } 10 | 11 | @mixin light-form() { 12 | border-radius: 0; 13 | border: 0; 14 | padding: 0; 15 | background-color: transparent; 16 | 17 | } 18 | -------------------------------------------------------------------------------- /project.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Molly - Constellation Desktop Wallet", 3 | "description": "Used to store $DAG tokens", 4 | "author": { 5 | "name": "vito", 6 | "email": "vito@armedvito.com" 7 | }, 8 | "version": "1.2.0", 9 | "binaryname": "mollywallet", 10 | "frontend": { 11 | "dir": "frontend", 12 | "install": "npm install", 13 | "build": "npm run build", 14 | "bridge": "src", 15 | "serve": "npm run serve" 16 | }, 17 | "WailsVersion": "" 18 | } 19 | -------------------------------------------------------------------------------- /frontend/src/assets/sass/font_awesome/_core.scss: -------------------------------------------------------------------------------- 1 | // Base Class Definition 2 | // ------------------------- 3 | 4 | .#{$fa-css-prefix}, 5 | .fas, 6 | .far, 7 | .fal, 8 | .fad, 9 | .fab { 10 | -moz-osx-font-smoothing: grayscale; 11 | -webkit-font-smoothing: antialiased; 12 | display: inline-block; 13 | font-style: normal; 14 | font-variant: normal; 15 | text-rendering: auto; 16 | line-height: 1; 17 | } 18 | 19 | %fa-icon { 20 | @include fa-icon; 21 | } 22 | -------------------------------------------------------------------------------- /frontend/src/pages/Notifications/ErrorMessage.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 10 | -------------------------------------------------------------------------------- /frontend/src/assets/sass/paper/mixins/_transparency.scss: -------------------------------------------------------------------------------- 1 | // Opacity 2 | 3 | @mixin opacity($opacity) { 4 | opacity: $opacity; 5 | // IE8 filter 6 | $opacity-ie: ($opacity * 100); 7 | filter: #{alpha(opacity=$opacity-ie)}; 8 | } 9 | 10 | @mixin black-filter($opacity) { 11 | top: 0; 12 | left: 0; 13 | height: 100%; 14 | width: 100%; 15 | position: absolute; 16 | background-color: rgba(17, 17, 17, $opacity); 17 | display: block; 18 | content: ""; 19 | z-index: 1; 20 | } 21 | -------------------------------------------------------------------------------- /frontend/src/assets/sass/font_awesome/_larger.scss: -------------------------------------------------------------------------------- 1 | // Icon Sizes 2 | // ------------------------- 3 | 4 | // makes the font 33% larger relative to the icon container 5 | .#{$fa-css-prefix}-lg { 6 | font-size: (4em / 3); 7 | line-height: (3em / 4); 8 | vertical-align: -.0667em; 9 | } 10 | 11 | .#{$fa-css-prefix}-xs { 12 | font-size: .75em; 13 | } 14 | 15 | .#{$fa-css-prefix}-sm { 16 | font-size: .875em; 17 | } 18 | 19 | @for $i from 1 through 10 { 20 | .#{$fa-css-prefix}-#{$i}x { 21 | font-size: $i * 1em; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /frontend/src/assets/sass/font_awesome/fontawesome.scss: -------------------------------------------------------------------------------- 1 | /*! 2 | * Font Awesome Free 5.10.1 by @fontawesome - https://fontawesome.com 3 | * License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) 4 | */ 5 | @import 'variables'; 6 | @import 'mixins'; 7 | @import 'core'; 8 | @import 'larger'; 9 | @import 'fixed-width'; 10 | @import 'list'; 11 | @import 'bordered-pulled'; 12 | @import 'animated'; 13 | @import 'rotated-flipped'; 14 | @import 'stacked'; 15 | @import 'icons'; 16 | @import 'screen-reader'; 17 | -------------------------------------------------------------------------------- /frontend/src/assets/sass/font_awesome/_bordered-pulled.scss: -------------------------------------------------------------------------------- 1 | // Bordered & Pulled 2 | // ------------------------- 3 | 4 | .#{$fa-css-prefix}-border { 5 | border: solid .08em $fa-border-color; 6 | border-radius: .1em; 7 | padding: .2em .25em .15em; 8 | } 9 | 10 | .#{$fa-css-prefix}-pull-left { float: left; } 11 | .#{$fa-css-prefix}-pull-right { float: right; } 12 | 13 | .#{$fa-css-prefix}, 14 | .fas, 15 | .far, 16 | .fal, 17 | .fab { 18 | &.#{$fa-css-prefix}-pull-left { margin-right: .3em; } 19 | &.#{$fa-css-prefix}-pull-right { margin-left: .3em; } 20 | } 21 | -------------------------------------------------------------------------------- /frontend/src/pages/LoadingScreen.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /frontend/src/components/SidebarPlugin/MovingArrow.vue: -------------------------------------------------------------------------------- 1 | 5 | 26 | 28 | -------------------------------------------------------------------------------- /frontend/src/plugins/paperDashboard.js: -------------------------------------------------------------------------------- 1 | import Notify from "vue-notifyjs"; 2 | import SideBar from "@/components/SidebarPlugin"; 3 | import GlobalComponents from "./globalComponents"; 4 | import GlobalDirectives from "./globalDirectives"; 5 | import "es6-promise/auto"; 6 | 7 | //css assets 8 | import "bootstrap/dist/css/bootstrap.css"; 9 | import "@/assets/sass/paper-dashboard.scss"; 10 | import "@/assets/css/themify-icons.css"; 11 | 12 | export default { 13 | install(Vue) { 14 | Vue.use(GlobalComponents); 15 | Vue.use(GlobalDirectives); 16 | Vue.use(SideBar); 17 | Vue.use(Notify); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /frontend/src/assets/sass/paper/mixins/_labels.scss: -------------------------------------------------------------------------------- 1 | @mixin label-style() { 2 | padding: $padding-label-vertical $padding-label-horizontal; 3 | border: 0.0625em solid $default-color; 4 | border-radius: $border-radius-small; 5 | color: $default-color; 6 | font-weight: $font-weight-semi; 7 | font-size: $font-size-small; 8 | text-transform: uppercase; 9 | display: inline-block; 10 | vertical-align: middle; 11 | } 12 | 13 | @mixin label-color($color) { 14 | border-color: $color; 15 | color: $color; 16 | } 17 | 18 | @mixin label-color-fill($color) { 19 | border-color: $color; 20 | color: $white-color; 21 | background-color: $color; 22 | } 23 | -------------------------------------------------------------------------------- /frontend/src/components/SidebarPlugin/index.js: -------------------------------------------------------------------------------- 1 | import Sidebar from "./SideBar.vue"; 2 | import SidebarLink from "./SidebarLink"; 3 | 4 | const SidebarStore = { 5 | showSidebar: false, 6 | sidebarLinks: [], 7 | displaySidebar(value) { 8 | this.showSidebar = value; 9 | } 10 | }; 11 | 12 | const SidebarPlugin = { 13 | install(Vue) { 14 | let app = new Vue({ 15 | data: { 16 | sidebarStore: SidebarStore 17 | } 18 | }); 19 | 20 | Vue.prototype.$sidebar = app.sidebarStore; 21 | Vue.component("side-bar", Sidebar); 22 | Vue.component("sidebar-link", SidebarLink); 23 | } 24 | }; 25 | 26 | export default SidebarPlugin; 27 | -------------------------------------------------------------------------------- /backend/models/txhistory.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | // TXHistory stores inidividual transactions 4 | type TXHistory struct { 5 | ID uint `json:"id"` 6 | Alias string `json:"alias"` 7 | Amount int64 `json:"amount"` 8 | Sender string `json:"sender"` 9 | Receiver string `json:"receiver"` 10 | Fee int64 `json:"fee"` 11 | Hash string `json:"hash"` 12 | LastTransactionRef struct { 13 | Hash string `json:"prevHash"` 14 | Ordinal int `json:"ordinal"` 15 | } `json:"lastTransactionRef"` 16 | TS string `json:"date"` 17 | Status string `json:"status"` 18 | Failed bool 19 | } 20 | -------------------------------------------------------------------------------- /frontend/src/assets/sass/font_awesome/_stacked.scss: -------------------------------------------------------------------------------- 1 | // Stacked Icons 2 | // ------------------------- 3 | 4 | .#{$fa-css-prefix}-stack { 5 | display: inline-block; 6 | height: 2em; 7 | line-height: 2em; 8 | position: relative; 9 | vertical-align: middle; 10 | width: ($fa-fw-width*2); 11 | } 12 | 13 | .#{$fa-css-prefix}-stack-1x, 14 | .#{$fa-css-prefix}-stack-2x { 15 | left: 0; 16 | position: absolute; 17 | text-align: center; 18 | width: 100%; 19 | } 20 | 21 | .#{$fa-css-prefix}-stack-1x { 22 | line-height: inherit; 23 | } 24 | 25 | .#{$fa-css-prefix}-stack-2x { 26 | font-size: 2em; 27 | } 28 | 29 | .#{$fa-css-prefix}-inverse { 30 | color: $fa-inverse; 31 | } 32 | -------------------------------------------------------------------------------- /frontend/src/components/Cards/WideCard.vue: -------------------------------------------------------------------------------- 1 | 19 | 29 | 31 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/grvlle/constellation_wallet 2 | 3 | require ( 4 | github.com/artdarek/go-unzip v1.0.0 5 | github.com/dustin/go-humanize v1.0.0 6 | github.com/jinzhu/gorm v1.9.12 7 | github.com/leaanthony/mewn v0.10.7 8 | github.com/mattn/go-sqlite3 v2.0.3+incompatible // indirect 9 | github.com/sirupsen/logrus v1.4.1 10 | github.com/wailsapp/wails v1.7.0-pre1 11 | golang.org/x/crypto v0.0.0-20200115085410-6d4e4cb37c7d 12 | golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297 // indirect 13 | golang.org/x/sync v0.0.0-20190423024810-112230192c58 14 | golang.org/x/sys v0.0.0-20190902133755-9109b7679e13 // indirect 15 | google.golang.org/appengine v1.6.2 // indirect 16 | ) 17 | 18 | go 1.13 19 | -------------------------------------------------------------------------------- /frontend/src/plugins/globalComponents.js: -------------------------------------------------------------------------------- 1 | import { FormGroupInput, Card, DropDown, Button, Password, FileSelector, Overlay } from "../components/index"; 2 | 3 | /** 4 | * You can register global components here and use them as a plugin in your main Vue instance 5 | */ 6 | 7 | const GlobalComponents = { 8 | install(Vue) { 9 | Vue.component("fg-input", FormGroupInput); 10 | Vue.component("drop-down", DropDown); 11 | Vue.component("card", Card); 12 | Vue.component("p-button", Button); 13 | Vue.component("password-input", Password); 14 | Vue.component("file-selector", FileSelector); 15 | Vue.component("page-overlay", Overlay); 16 | } 17 | }; 18 | 19 | export default GlobalComponents; 20 | -------------------------------------------------------------------------------- /frontend/src/components/Cards/StatsCard.vue: -------------------------------------------------------------------------------- 1 | 20 | 30 | 32 | -------------------------------------------------------------------------------- /frontend/src/pages/WalletInformation.vue: -------------------------------------------------------------------------------- 1 | 13 | 23 | 24 | 35 | -------------------------------------------------------------------------------- /frontend/src/store/store.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | import Vuex from 'vuex'; 3 | import Wallet from './modules/wallet'; 4 | import App from './modules/app'; 5 | import Transaction from './modules/transaction'; 6 | import Dashboard from './modules/dashboard'; 7 | 8 | Vue.use(Vuex); 9 | 10 | export const store = new Vuex.Store({ 11 | modules: { 12 | app: App, 13 | wallet: Wallet, 14 | transaction: Transaction, 15 | dashboard: Dashboard 16 | }, 17 | getters: { 18 | runningOnWindows (state) { 19 | return state.OS.windows 20 | }, 21 | runningOnLinux (state) { 22 | return state.OS.linux 23 | }, 24 | runningOnMacOS (state) { 25 | return state.OS.macOS 26 | } 27 | } 28 | }) 29 | 30 | export default store; 31 | 32 | -------------------------------------------------------------------------------- /frontend/src/components/Cards/Card.vue: -------------------------------------------------------------------------------- 1 | 22 | 31 | 33 | -------------------------------------------------------------------------------- /frontend/src/assets/sass/font_awesome/brands.scss: -------------------------------------------------------------------------------- 1 | /*! 2 | * Font Awesome Free 5.10.1 by @fontawesome - https://fontawesome.com 3 | * License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) 4 | */ 5 | @import 'variables'; 6 | 7 | @font-face { 8 | font-family: 'Font Awesome 5 Brands'; 9 | font-style: normal; 10 | font-weight: normal; 11 | font-display: $fa-font-display; 12 | src: url('#{$fa-font-path}/fa-brands-400.eot'); 13 | src: url('#{$fa-font-path}/fa-brands-400.eot?#iefix') format('embedded-opentype'), 14 | url('#{$fa-font-path}/fa-brands-400.woff2') format('woff2'), 15 | url('#{$fa-font-path}/fa-brands-400.woff') format('woff'), 16 | url('#{$fa-font-path}/fa-brands-400.ttf') format('truetype'), 17 | url('#{$fa-font-path}/fa-brands-400.svg#fontawesome') format('svg'); 18 | } 19 | 20 | .fab { 21 | font-family: 'Font Awesome 5 Brands'; 22 | } 23 | -------------------------------------------------------------------------------- /frontend/src/assets/sass/font_awesome/solid.scss: -------------------------------------------------------------------------------- 1 | /*! 2 | * Font Awesome Free 5.10.1 by @fontawesome - https://fontawesome.com 3 | * License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) 4 | */ 5 | @import 'variables'; 6 | 7 | @font-face { 8 | font-family: 'Font Awesome 5 Free'; 9 | font-style: normal; 10 | font-weight: 900; 11 | font-display: $fa-font-display; 12 | src: url('#{$fa-font-path}/fa-solid-900.eot'); 13 | src: url('#{$fa-font-path}/fa-solid-900.eot?#iefix') format('embedded-opentype'), 14 | url('#{$fa-font-path}/fa-solid-900.woff2') format('woff2'), 15 | url('#{$fa-font-path}/fa-solid-900.woff') format('woff'), 16 | url('#{$fa-font-path}/fa-solid-900.ttf') format('truetype'), 17 | url('#{$fa-font-path}/fa-solid-900.svg#fontawesome') format('svg'); 18 | } 19 | 20 | .fa, 21 | .fas { 22 | font-family: 'Font Awesome 5 Free'; 23 | font-weight: 900; 24 | } 25 | -------------------------------------------------------------------------------- /frontend/src/assets/sass/font_awesome/regular.scss: -------------------------------------------------------------------------------- 1 | /*! 2 | * Font Awesome Free 5.10.1 by @fontawesome - https://fontawesome.com 3 | * License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) 4 | */ 5 | @import 'variables'; 6 | 7 | @font-face { 8 | font-family: 'Font Awesome 5 Free'; 9 | font-style: normal; 10 | font-weight: 400; 11 | font-display: $fa-font-display; 12 | src: url('#{$fa-font-path}/fa-regular-400.eot'); 13 | src: url('#{$fa-font-path}/fa-regular-400.eot?#iefix') format('embedded-opentype'), 14 | url('#{$fa-font-path}/fa-regular-400.woff2') format('woff2'), 15 | url('#{$fa-font-path}/fa-regular-400.woff') format('woff'), 16 | url('#{$fa-font-path}/fa-regular-400.ttf') format('truetype'), 17 | url('#{$fa-font-path}/fa-regular-400.svg#fontawesome') format('svg'); 18 | } 19 | 20 | .far { 21 | font-family: 'Font Awesome 5 Free'; 22 | font-weight: 400; 23 | } 24 | -------------------------------------------------------------------------------- /frontend/src/store/modules/transaction.js: -------------------------------------------------------------------------------- 1 | const getDefaultState = () => { 2 | return { 3 | txHistory: [], 4 | txStatus: "Complete", 5 | txFinished: true 6 | } 7 | } 8 | 9 | const state = getDefaultState() 10 | 11 | const actions = { 12 | reset({ commit }) { 13 | commit('resetState') 14 | } 15 | } 16 | const mutations = { 17 | resetState(state) { 18 | Object.assign(state, getDefaultState()) 19 | }, 20 | updateTxHistory(state, tx) { 21 | state.txHistory.unshift(tx) 22 | }, 23 | updateFullTxHistory(state, obj) { 24 | state.txHistory = obj.txHistoryFull 25 | }, 26 | updateTxStatus(state, status) { 27 | state.txStatus = status 28 | }, 29 | setTxFinished(state, setTxFinished) { 30 | state.txFinished = setTxFinished; 31 | } 32 | } 33 | 34 | export default { 35 | namespaced: true, 36 | state, 37 | getters: {}, 38 | actions, 39 | mutations 40 | } -------------------------------------------------------------------------------- /frontend/src/assets/sass/font_awesome/_rotated-flipped.scss: -------------------------------------------------------------------------------- 1 | // Rotated & Flipped Icons 2 | // ------------------------- 3 | 4 | .#{$fa-css-prefix}-rotate-90 { @include fa-icon-rotate(90deg, 1); } 5 | .#{$fa-css-prefix}-rotate-180 { @include fa-icon-rotate(180deg, 2); } 6 | .#{$fa-css-prefix}-rotate-270 { @include fa-icon-rotate(270deg, 3); } 7 | 8 | .#{$fa-css-prefix}-flip-horizontal { @include fa-icon-flip(-1, 1, 0); } 9 | .#{$fa-css-prefix}-flip-vertical { @include fa-icon-flip(1, -1, 2); } 10 | .#{$fa-css-prefix}-flip-both, .#{$fa-css-prefix}-flip-horizontal.#{$fa-css-prefix}-flip-vertical { @include fa-icon-flip(-1, -1, 2); } 11 | 12 | // Hook for IE8-9 13 | // ------------------------- 14 | 15 | :root { 16 | .#{$fa-css-prefix}-rotate-90, 17 | .#{$fa-css-prefix}-rotate-180, 18 | .#{$fa-css-prefix}-rotate-270, 19 | .#{$fa-css-prefix}-flip-horizontal, 20 | .#{$fa-css-prefix}-flip-vertical, 21 | .#{$fa-css-prefix}-flip-both { 22 | filter: none; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /frontend/src/plugins/globalMethods.js: -------------------------------------------------------------------------------- 1 | import Vue from "vue"; 2 | 3 | Vue.mixin({ 4 | methods: { 5 | logout: function() { 6 | window.backend.WalletApplication.LogOut().then(txFinishedState => { 7 | if (txFinishedState) { 8 | let darkMode = this.$store.state.wallet.darkMode 9 | this.$store.dispatch('transaction/reset').then(() => { 10 | this.$store.dispatch('wallet/reset').then(() => { 11 | this.$store.dispatch('app/reset').then(() => { 12 | this.$router.push({ 13 | name: 'login', 14 | params: { 15 | message: "Please enter your credentials below to access your Molly Wallet.", 16 | darkMode: darkMode 17 | } 18 | }); 19 | return; 20 | }) 21 | }) 22 | }) 23 | } 24 | }), (this.random = "1"); 25 | return; 26 | } 27 | } 28 | }); -------------------------------------------------------------------------------- /frontend/src/assets/sass/paper/sweetalert2.scss: -------------------------------------------------------------------------------- 1 | @import 'sweetalert2/src/variables'; 2 | @import '@sweetalert2/theme-bootstrap-4/bootstrap-4.scss'; 3 | 4 | $swal2-font: 'Puppet', Helvetica, Arial, sans-serif; 5 | $swal2-title-font-size: 1.5rem; 6 | $swal2-input-font-size: 1rem; 7 | $swal2-content-font-size: 0.875rem; 8 | $swal2-validation-message-font-size: 0.875rem; 9 | $swal2-confirm-button-font-size: 0.75rem; 10 | $swal2-cancel-button-font-size: 0.75rem; 11 | $swal2-confirm-button-background-color: $primary-color; 12 | 13 | .swal2-popup.swal2-modal.swal2-show { 14 | @include themed() { 15 | background: t('popupBackgroundColor'); 16 | } 17 | } 18 | 19 | .swal2-actions.swal2-loading .swal2-styled.swal2-confirm { 20 | min-width: 1rem; 21 | min-height: 1rem; 22 | } 23 | 24 | button.swal2-styled { 25 | min-width: 6rem; 26 | min-height: 2.3rem; 27 | } 28 | 29 | button.swal2-styled.swal2-confirm:focus { 30 | outline: none; 31 | box-shadow: none; 32 | } 33 | 34 | @import 'sweetalert2/src/sweetalert2'; -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "math/rand" 5 | "os" 6 | "time" 7 | 8 | "runtime" 9 | 10 | "github.com/leaanthony/mewn" 11 | "github.com/wailsapp/wails" 12 | 13 | app "github.com/grvlle/constellation_wallet/backend" 14 | ) 15 | 16 | func init() { 17 | rand.Seed(time.Now().UnixNano()) 18 | } 19 | 20 | func main() { 21 | if runtime.GOOS != "windows" { 22 | os.Setenv("PATH", "/usr/bin:/sbin") // This is neccessary when interacting with the CLI on RedHat and other linux distros 23 | } 24 | 25 | js := mewn.String("./frontend/dist/app.js") 26 | css := mewn.String("./frontend/dist/app.css") 27 | 28 | frontend := wails.CreateApp(&wails.AppConfig{ 29 | Width: 1280, 30 | Height: 780, 31 | Resizable: true, 32 | Title: "Molly - Constellation Desktop Wallet", 33 | JS: js, 34 | CSS: css, 35 | Colour: "#131313", 36 | }) 37 | 38 | frontend.Bind(&app.WalletApplication{}) 39 | err := frontend.Run() 40 | if err != nil { 41 | panic(err) 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /frontend/src/components/index.js: -------------------------------------------------------------------------------- 1 | import FormGroupInput from "./Inputs/formGroupInput.vue"; 2 | 3 | import DropDown from "./Dropdown.vue"; 4 | import PaperTable from "./PaperTable.vue"; 5 | import Button from "./Button"; 6 | import Overlay from "./Overlay"; 7 | 8 | import Card from "./Cards/Card.vue"; 9 | import ChartCard from "./Cards/ChartCard.vue"; 10 | import StatsCard from "./Cards/StatsCard.vue"; 11 | import WideCard from "./Cards/WideCard.vue"; 12 | 13 | import Password from "./Inputs/Password.vue"; 14 | import FileSelector from "./Inputs/FileSelector.vue"; 15 | 16 | import SidebarPlugin from "./SidebarPlugin/index"; 17 | 18 | let components = { 19 | FormGroupInput, 20 | Card, 21 | ChartCard, 22 | StatsCard, 23 | WideCard, 24 | Password, 25 | FileSelector, 26 | PaperTable, 27 | DropDown, 28 | SidebarPlugin, 29 | Overlay 30 | }; 31 | 32 | export default components; 33 | 34 | export { 35 | FormGroupInput, 36 | Card, 37 | ChartCard, 38 | StatsCard, 39 | WideCard, 40 | Password, 41 | FileSelector, 42 | PaperTable, 43 | DropDown, 44 | Button, 45 | SidebarPlugin, 46 | Overlay 47 | }; 48 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019-Present Vito 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /frontend/src/pages/DownloadingScreen.vue: -------------------------------------------------------------------------------- 1 | 18 | 19 | 43 | 44 | -------------------------------------------------------------------------------- /frontend/src/components/Overlay.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 16 | 17 | -------------------------------------------------------------------------------- /frontend/src/assets/sass/paper/_select.scss: -------------------------------------------------------------------------------- 1 | @import "themes"; 2 | 3 | .select { 4 | @include themed() { 5 | background-color: t('inputBackgroundColor'); 6 | } 7 | } 8 | 9 | .select .vs__selected { 10 | @include themed() { 11 | color: t('inputTextColor'); 12 | } 13 | } 14 | 15 | .select ul { 16 | @include themed() { 17 | background: t('inputBackgroundColor'); 18 | } 19 | } 20 | 21 | .select .vs__search::placeholder, 22 | .select .vs__dropdown-toggle, 23 | .select .vs__dropdown-menu { 24 | @include themed() { 25 | background: t('inputBackgroundColor'); 26 | } 27 | } 28 | 29 | .select .vs__dropdown-menu li:hover,li:focus,li:visited,li:active { 30 | @include themed() { 31 | background-color: t('infoColor'); 32 | } 33 | } 34 | 35 | .select .vs__dropdown-menu li.vs__dropdown-option { 36 | @include themed() { 37 | color: t('inputDisabledTextColor'); 38 | } 39 | } 40 | 41 | .select .vs__dropdown-menu li.vs__dropdown-option--highlight { 42 | @include themed() { 43 | background-color: t('infoColor'); 44 | color: t('inputTextColor'); 45 | } 46 | } 47 | 48 | .select .vs__clear, 49 | .select .vs__open-indicator { 50 | cursor: pointer; 51 | @include themed() { 52 | fill: t('inputTextColor'); 53 | } 54 | } -------------------------------------------------------------------------------- /frontend/src/components/Button.vue: -------------------------------------------------------------------------------- 1 | 22 | 52 | 54 | -------------------------------------------------------------------------------- /frontend/src/components/Dropdown.vue: -------------------------------------------------------------------------------- 1 | 22 | 50 | -------------------------------------------------------------------------------- /frontend/src/main.js: -------------------------------------------------------------------------------- 1 | Vue.config.productionTip = false; 2 | Vue.config.devtools = true; 3 | 4 | import Vue from "vue"; 5 | import App from "./App.vue"; 6 | import router from "./router/index"; 7 | import * as Wails from '@wailsapp/runtime'; 8 | import PaperDashboard from "./plugins/paperDashboard"; 9 | import "vue-notifyjs/themes/default.css"; 10 | import VueNotify from 'vue-notifyjs'; 11 | import {store} from './store/store'; 12 | import Vuelidate from 'vuelidate'; 13 | import ToggleButton from 'vue-js-toggle-button'; 14 | import VueProgressBar from 'vue-progressbar'; 15 | import VueSelect from 'vue-select'; 16 | import 'vue-select/dist/vue-select.css'; 17 | import IdleVue from 'idle-vue' 18 | import './plugins/globalMethods'; 19 | 20 | const eventsHub = new Vue(); 21 | Vue.use(IdleVue, { 22 | eventEmitter: eventsHub, 23 | store, 24 | idleTime: 300000, 25 | startAtIdle: false 26 | }) 27 | Vue.use(VueProgressBar, { 28 | color: '#6DECBB', 29 | failedColor: 'red', 30 | height: '0.4rem', 31 | thickness: '0.4rem' 32 | }) 33 | Vue.use(ToggleButton) 34 | Vue.use(Vuelidate) 35 | Vue.use(VueNotify); 36 | Vue.use(PaperDashboard); 37 | Vue.use(VueSelect); 38 | 39 | Wails.Init(() => { 40 | new Vue({ 41 | router, 42 | store: store, 43 | render: h => h(App), 44 | mounted() { 45 | this.$router.replace('/') 46 | } 47 | }).$mount("#app"); 48 | }); 49 | -------------------------------------------------------------------------------- /frontend/src/assets/sass/paper/_alerts.scss: -------------------------------------------------------------------------------- 1 | .alert { 2 | border: 0; 3 | border-radius: 0; 4 | color: #FFFFFF; 5 | padding: 0.625em 0.9375em; 6 | font-size: 0.875rem; 7 | 8 | .container & { 9 | border-radius: 0.25em; 10 | 11 | } 12 | .navbar & { 13 | border-radius: 0; 14 | left: 0; 15 | position: absolute; 16 | right: 0; 17 | top: 5.3125em; 18 | width: 100%; 19 | z-index: 3; 20 | } 21 | .navbar:not(.navbar-transparent) & { 22 | top: 4.375em; 23 | } 24 | 25 | span[data-notify="icon"] { 26 | font-size: 1.875rem; 27 | display: block; 28 | left: 0.9375em; 29 | position: absolute; 30 | top: 50%; 31 | margin-top: -1.25em; 32 | } 33 | 34 | .close ~ span { 35 | display: block; 36 | max-width: 89%; 37 | } 38 | 39 | &[data-notify="container"] { 40 | padding: 0.625em 0.625em 0.625em 1.25em; 41 | border-radius: $border-radius-base; 42 | } 43 | 44 | &.alert-with-icon { 45 | padding-left: 4.0625em; 46 | } 47 | } 48 | 49 | .alert-info { 50 | background-color: $bg-info; 51 | color: $info-states-color; 52 | } 53 | 54 | .alert-success { 55 | background-color: $bg-success; 56 | color: $success-states-color; 57 | } 58 | 59 | .alert-warning { 60 | background-color: $bg-warning; 61 | color: $warning-states-color; 62 | } 63 | 64 | .alert-danger { 65 | background-color: $bg-danger; 66 | color: $danger-states-color; 67 | } 68 | 69 | -------------------------------------------------------------------------------- /backend/api/rpc.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "fmt" 5 | "net" 6 | "net/http" 7 | "net/rpc" 8 | "os" 9 | ) 10 | 11 | type RPCEndpoints int 12 | 13 | type Signal struct { 14 | PID int 15 | Msg string 16 | } 17 | 18 | // InitRPCServer initializes the RPC server that listens to incoming LCM tasks 19 | // by the RPC clients 20 | func InitRPCServer() error { 21 | endpoints := new(RPCEndpoints) 22 | // Publish the receivers methods 23 | err := rpc.Register(endpoints) 24 | if err != nil { 25 | return fmt.Errorf("format of service Task isn't correct. Reason: %v", err) 26 | } 27 | // Register a HTTP handler 28 | rpc.HandleHTTP() 29 | // Listen to TPC connections on port 36866 30 | listener, err := net.Listen("tcp", ":36866") 31 | if err != nil { 32 | return fmt.Errorf("Listen error: %v", err) 33 | } 34 | 35 | errs := make(chan error) 36 | 37 | // Start accept incoming HTTP connections 38 | go func() { 39 | err = http.Serve(listener, nil) 40 | if err != nil { 41 | errs <- fmt.Errorf("Error serving: %v", err) 42 | return 43 | } 44 | }() 45 | 46 | select { 47 | case err := <-errs: 48 | if err != nil { 49 | return err 50 | } 51 | default: 52 | return nil 53 | } 54 | 55 | return nil 56 | } 57 | 58 | func (rpc *RPCEndpoints) ShutDown(sig Signal, response *Signal) error { 59 | 60 | pid := os.Getpid() 61 | *response = Signal{pid, "Shutting down application"} 62 | 63 | // time.Sleep(3 * time.Second) 64 | return nil 65 | } 66 | -------------------------------------------------------------------------------- /backend/models/wallet.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | import ( 4 | "time" 5 | 6 | _ "github.com/jinzhu/gorm/dialects/sqlite" 7 | ) 8 | 9 | // Wallet holds all wallet information. 10 | type Wallet struct { 11 | ID uint `gorm:"AUTO_INCREMENT"` 12 | CreatedAt time.Time 13 | UpdatedAt time.Time 14 | DeletedAt *time.Time 15 | Path Path `gorm:"foreignkey:Alias"` 16 | KeystorePasswordHash string 17 | KeyPasswordHash string 18 | KeyStorePath string 19 | WalletAlias string `gorm:"primary_key;unique"` 20 | Addresses []Address `sql:"-"` 21 | TXHistory []TXHistory `gorm:"foreignkey:Alias"` 22 | ProfilePicture string 23 | WalletTag string 24 | DarkMode bool 25 | Currency string 26 | TermsOfService bool 27 | Balance float64 `json:"balance"` 28 | AvailableBalance float64 `json:"available_balance"` 29 | Nonce float64 `json:"nonce"` 30 | TotalBalance float64 `json:"total_balance"` 31 | Delegated float64 `json:"delegated"` 32 | Deposit float64 `json:"deposit"` 33 | Address string `json:"address"` 34 | TokenPrice struct { 35 | DAG struct { 36 | BTC float64 `json:"BTC,omitempty"` 37 | USD float64 `json:"USD,omitempty"` 38 | EUR float64 `json:"EUR,omitempty"` 39 | } `json:"DAG"` 40 | } `json:"token_price"` 41 | } 42 | -------------------------------------------------------------------------------- /frontend/src/assets/sass/paper/_footers.scss: -------------------------------------------------------------------------------- 1 | .footer { 2 | background-attachment: fixed; 3 | position: relative; 4 | line-height: 1.25em; 5 | nav { 6 | ul { 7 | list-style: none; 8 | margin: 0; 9 | padding: 0; 10 | font-weight: normal; 11 | li { 12 | display: inline-block; 13 | padding: 0.625em 0.9375em; 14 | margin: 0.9375em 0.1875em; 15 | line-height: 1.25em; 16 | text-align: center; 17 | } 18 | a:not(.btn) { 19 | color: $font-color; 20 | display: block; 21 | margin-bottom: 0.1875em; 22 | 23 | &:focus, 24 | &:hover { 25 | color: $default-states-color; 26 | } 27 | } 28 | } 29 | } 30 | .copyright { 31 | color: $font-color; 32 | padding: 1.5625em 0.9375em; 33 | font-size: 0.875rem; 34 | white-space: nowrap; 35 | margin: 0.9375em 0.1875em; 36 | line-height: 1.25em; 37 | } 38 | .heart { 39 | color: $danger-color; 40 | } 41 | } 42 | 43 | $fade-white : #403D39; 44 | $white : #ccc; 45 | 46 | a.social { 47 | color: #403D39; 48 | text-decoration: none; 49 | margin-left: 0.9375em; 50 | transition: 0.3s; 51 | font-weight: 200; 52 | font-size: 0.875rem; 53 | line-height: 1em; 54 | opacity: .8; 55 | font-family: Poppins; 56 | 57 | &:visited{ 58 | text-decoration: none; 59 | } 60 | &:hover{ 61 | opacity: 1; 62 | } 63 | &:active{ 64 | color: #5fd1fa; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /frontend/src/components/Inputs/formGroupInput.vue: -------------------------------------------------------------------------------- 1 | 27 | 45 | 48 | -------------------------------------------------------------------------------- /frontend/vue.config.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | let cssConfig = {}; 4 | 5 | if (process.env.NODE_ENV == "production") { 6 | cssConfig = { 7 | extract: { 8 | filename: "[name].css", 9 | chunkFilename: "[name].css" 10 | } 11 | }; 12 | } 13 | 14 | const path = require('path') 15 | const PrerenderSPAPlugin = require('prerender-spa-plugin') 16 | 17 | module.exports = { 18 | plugins: [ 19 | new PrerenderSPAPlugin({ 20 | // Required - The path to the webpack-outputted app to prerender. 21 | staticDir: path.join(__dirname, 'dist'), 22 | // Required - Routes to render. 23 | routes: [ '/', '/login', '/dashboard' ], 24 | }) 25 | ] 26 | } 27 | 28 | module.exports = { 29 | chainWebpack: config => { 30 | let limit = 9999999999999999; 31 | config.module 32 | .rule("images") 33 | .test(/\.(png|gif|jpg|jpeg)(\?.*)?$/i) 34 | .use("url-loader") 35 | .loader("url-loader") 36 | .tap(options => Object.assign(options, { limit: limit })); 37 | config.module 38 | .rule("fonts") 39 | .test(/\.(woff2?|eot|ttf|otf|svg)(\?.*)?$/i) 40 | .use("url-loader") 41 | .loader("url-loader") 42 | .options({ 43 | limit: limit 44 | }); 45 | }, 46 | css: cssConfig, 47 | configureWebpack: { 48 | output: { 49 | filename: "[name].js" 50 | }, 51 | optimization: { 52 | splitChunks: false 53 | } 54 | }, 55 | devServer: { 56 | disableHostCheck: true, 57 | host: "localhost" 58 | } 59 | }; 60 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /frontend/src/components/SidebarPlugin/SidebarLink.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 62 | 63 | 66 | -------------------------------------------------------------------------------- /frontend/src/assets/sass/font_awesome/_mixins.scss: -------------------------------------------------------------------------------- 1 | // Mixins 2 | // -------------------------- 3 | 4 | @mixin fa-icon { 5 | -webkit-font-smoothing: antialiased; 6 | -moz-osx-font-smoothing: grayscale; 7 | display: inline-block; 8 | font-style: normal; 9 | font-variant: normal; 10 | font-weight: normal; 11 | line-height: 1; 12 | } 13 | 14 | @mixin fa-icon-rotate($degrees, $rotation) { 15 | -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=#{$rotation})"; 16 | transform: rotate($degrees); 17 | } 18 | 19 | @mixin fa-icon-flip($horiz, $vert, $rotation) { 20 | -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=#{$rotation}, mirror=1)"; 21 | transform: scale($horiz, $vert); 22 | } 23 | 24 | 25 | // Only display content to screen readers. A la Bootstrap 4. 26 | // 27 | // See: http://a11yproject.com/posts/how-to-hide-content/ 28 | 29 | @mixin sr-only { 30 | border: 0; 31 | clip: rect(0, 0, 0, 0); 32 | height: 0.0625em; 33 | margin: -0.0625em; 34 | overflow: hidden; 35 | padding: 0; 36 | position: absolute; 37 | width: 0.0625em; 38 | } 39 | 40 | // Use in conjunction with .sr-only to only display content when it's focused. 41 | // 42 | // Useful for "Skip to main content" links; see http://www.w3.org/TR/2013/NOTE-WCAG20-TECHS-20130905/G1 43 | // 44 | // Credit: HTML5 Boilerplate 45 | 46 | @mixin sr-only-focusable { 47 | &:active, 48 | &:focus { 49 | clip: auto; 50 | height: auto; 51 | margin: 0; 52 | overflow: visible; 53 | position: static; 54 | width: auto; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /frontend/src/components/Inputs/FileSelector.vue: -------------------------------------------------------------------------------- 1 | 30 | 31 | 59 | 60 | -------------------------------------------------------------------------------- /frontend/src/store/modules/app.js: -------------------------------------------------------------------------------- 1 | const getDefaultState = () => { 2 | return { 3 | version: "v2.6.0", 4 | uiVersion: "v1.2.0", 5 | isLoggedIn: false, 6 | errorMessage: "", 7 | warningMessage: "", 8 | successMessage: "", 9 | loginErrorMsg: "", 10 | newRelease: "", 11 | network: "MAINNET", 12 | displayLoginError: false, 13 | downloading: { 14 | filename: "", 15 | size: "" 16 | } 17 | } 18 | } 19 | 20 | const state = getDefaultState() 21 | 22 | const actions = { 23 | reset({ commit }) { 24 | commit('resetState') 25 | } 26 | } 27 | const mutations = { 28 | resetState(state) { 29 | Object.assign(state, getDefaultState()) 30 | }, 31 | setIsLoggedIn(state, isLoggedIn) { 32 | state.isLoggedIn = isLoggedIn; 33 | }, 34 | setErrorMessage(state, message) { 35 | state.errorMessage = message; 36 | }, 37 | setWarningMessage(state, message) { 38 | state.warningMessage = message; 39 | }, 40 | setSuccessMessage(state, message) { 41 | state.successMessage = message; 42 | }, 43 | setLoginErrorMessage(state, message) { 44 | state.loginErrorMsg = message; 45 | }, 46 | setDisplayLoginError(state, val) { 47 | state.displayLoginError = val; 48 | }, 49 | setNewRelease(state, val) { 50 | state.newRelease = val; 51 | }, 52 | setDownloadFileName(state, name) { 53 | state.downloading.filename = name; 54 | }, 55 | setDownloadFileSize(state, size) { 56 | state.downloading.size = size; 57 | } 58 | } 59 | 60 | export default { 61 | namespaced: true, 62 | state, 63 | getters: {}, 64 | actions, 65 | mutations 66 | } -------------------------------------------------------------------------------- /frontend/src/assets/sass/paper-dashboard.scss: -------------------------------------------------------------------------------- 1 | /*! 2 | 3 | ========================================================= 4 | * Vue Paper Dashboard - v2.0.0 5 | ========================================================= 6 | 7 | * Product Page: http://www.creative-tim.com/product/paper-dashboard 8 | * Copyright 2019 Creative Tim (http://www.creative-tim.com) 9 | * Licensed under MIT (https://github.com/creativetimofficial/paper-dashboard/blob/master/LICENSE.md) 10 | 11 | ========================================================= 12 | 13 | * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 14 | 15 | */ 16 | @import url('https://fonts.googleapis.com/css?family=Raleway&display=swap'); 17 | @import url('https://fonts.googleapis.com/css?family=Poppins&display=swap'); 18 | @import url('https://fonts.googleapis.com/css?family=Press+Start+2P&display=swap'); 19 | @import url('https://fonts.googleapis.com/css?family=Major+Mono+Display&display=swap'); 20 | @import "font_awesome/fontawesome.scss"; 21 | @import "font_awesome/solid.scss"; 22 | @import "font_awesome/brands.scss"; 23 | @import "paper/variables"; 24 | @import "paper/mixins"; 25 | @import "paper/typography"; 26 | // Core CSS 27 | @import "paper/misc"; 28 | @import "paper/sidebar-and-main-panel"; 29 | @import "paper/buttons"; 30 | @import "paper/inputs"; 31 | @import "paper/alerts"; 32 | @import "paper/tables"; 33 | @import "paper/checkbox-radio"; 34 | @import "paper/navbars"; 35 | @import "paper/footers"; 36 | // Fancy Stuff 37 | @import "paper/dropdown"; 38 | @import "paper/cards"; 39 | @import "paper/chartist"; 40 | @import "paper/responsive"; 41 | @import "paper/sweetalert2"; 42 | // Constellation Wallet 43 | @import "paper/wallet"; 44 | @import "paper/select"; 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /frontend/src/store/modules/wallet.js: -------------------------------------------------------------------------------- 1 | const getDefaultState = () => { 2 | return { 3 | walletLabel: "", 4 | imgPath: 'faces/face-0.jpg', 5 | transactions: 0, 6 | tokenAmount: 0, 7 | totalBalance: 0, 8 | availableBalance: 0, 9 | nonce: 0, 10 | currency: "USD", 11 | totalValue: 0.0, 12 | address: "N/A", 13 | keystorePath: "", 14 | alias: "", 15 | publicKey: "NaN", 16 | darkMode: false, 17 | termsOfService: false 18 | } 19 | } 20 | 21 | const state = getDefaultState() 22 | 23 | const actions = { 24 | reset({ commit }) { 25 | commit('resetState') 26 | } 27 | } 28 | const mutations = { 29 | resetState(state) { 30 | Object.assign(state, getDefaultState()) 31 | }, 32 | setCurrency(state, currency) { 33 | state.currency = currency; 34 | }, 35 | setTokenAmount(state, amount) { 36 | state.tokenAmount = amount; 37 | }, 38 | setAvailableBalance(state, available) { 39 | state.availableBalance = available; 40 | }, 41 | setTotalBalance(state, total) { 42 | state.totalBalance = total; 43 | }, 44 | setTotalValue(state, value) { 45 | state.totalValue = value; 46 | }, 47 | setAddress(state, address) { 48 | state.address = address; 49 | }, 50 | setAlias(state, alias) { 51 | state.alias = alias; 52 | }, 53 | setKeystorePath(state, path) { 54 | state.keystorePath = path; 55 | }, 56 | setLabel(state, label) { 57 | state.walletLabel = label; 58 | }, 59 | setDarkMode(state, darkMode) { 60 | state.darkMode = darkMode; 61 | }, 62 | setImgPath(state, path) { 63 | state.imgPath = path; 64 | }, 65 | setTermsOfService(state, termsOfService) { 66 | state.termsOfService = termsOfService; 67 | } 68 | } 69 | 70 | export default { 71 | namespaced: true, 72 | state, 73 | getters: {}, 74 | actions, 75 | mutations 76 | } -------------------------------------------------------------------------------- /frontend/src/assets/sass/paper/mixins/_buttons.scss: -------------------------------------------------------------------------------- 1 | // Mixin for generating new styles 2 | @mixin btn-styles($btn-color, $btn-states-color) { 3 | background-color: $btn-color; 4 | border-color: $btn-color; 5 | color: $white-color; 6 | @include opacity(1); 7 | 8 | &:hover, 9 | &:focus, 10 | &:active, 11 | &.active, 12 | .show > &.dropdown-toggle{ 13 | background-color: $btn-states-color; 14 | color: $white-color; 15 | border-color: $btn-states-color; 16 | } 17 | 18 | .caret{ 19 | border-top-color: $white-color; 20 | } 21 | 22 | &.btn-link { 23 | color: $btn-color; 24 | 25 | &:hover, 26 | &:focus, 27 | &:active, 28 | &.active, 29 | .open > &.dropdown-toggle{ 30 | background-color: $transparent-bg; 31 | color: $btn-states-color; 32 | } 33 | 34 | .caret{ 35 | border-top-color: $btn-color; 36 | } 37 | } 38 | 39 | .caret{ 40 | border-top-color: $white-color; 41 | } 42 | } 43 | 44 | @mixin btn-outline-styles($btn-color, $btn-states-color){ 45 | border-color: $btn-color; 46 | color: $btn-color; 47 | @include opacity(1); 48 | 49 | &:hover, 50 | &:focus, 51 | &:active, 52 | &.active, 53 | .open > &.dropdown-toggle { 54 | background-color: $btn-color; 55 | color: $fill-font-color; 56 | border-color: $btn-color; 57 | .caret{ 58 | border-top-color: $fill-font-color; 59 | } 60 | } 61 | 62 | .caret{ 63 | border-top-color: $white-color; 64 | } 65 | 66 | &.disabled, 67 | &:disabled, 68 | &[disabled], 69 | fieldset[disabled] & { 70 | &, 71 | &:hover, 72 | &:focus, 73 | &.focus, 74 | &:active, 75 | &.active { 76 | background-color: $transparent-bg; 77 | border-color: $btn-color; 78 | } 79 | } 80 | } 81 | 82 | @mixin btn-size($padding-vertical, $padding-horizontal, $font-size, $line-height){ 83 | font-size: $font-size; 84 | padding: $padding-vertical $padding-horizontal; 85 | 86 | &.btn-simple{ 87 | padding: $padding-vertical + 2 $padding-horizontal; 88 | } 89 | 90 | } 91 | -------------------------------------------------------------------------------- /frontend/src/components/PaperTable.vue: -------------------------------------------------------------------------------- 1 | 33 | 82 | 84 | -------------------------------------------------------------------------------- /frontend/src/pages/NotFoundPage.vue: -------------------------------------------------------------------------------- 1 | 60 | 61 | 64 | -------------------------------------------------------------------------------- /frontend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "my_project", 3 | "author": "vito", 4 | "private": true, 5 | "scripts": { 6 | "serve": "vue-cli-service serve", 7 | "build": "vue-cli-service build", 8 | "lint": "vue-cli-service lint" 9 | }, 10 | "dependencies": { 11 | "@types/vuelidate": "^0.7.13", 12 | "@wailsapp/runtime": "^1.0.11", 13 | "babel-helper-vue-jsx-merge-props": "^2.0.3", 14 | "bootstrap": "^4.4.1", 15 | "chartist": "^0.11.4", 16 | "core-js": "^2.6.11", 17 | "es6-promise": "^4.2.8", 18 | "jquery": "^3.5.0", 19 | "node-sass": "^4.13.1", 20 | "popper.js": "^1.16.1", 21 | "sass-loader": "^8.0.2", 22 | "serialize-javascript": "^2.1.2", 23 | "update": "^0.7.4", 24 | "vue": "^2.6.11", 25 | "vue-clickaway": "^2.2.2", 26 | "vue-js-toggle-button": "^1.3.3", 27 | "vue-loading-spinner": "^1.0.11", 28 | "vue-notifyjs": "^0.4.3", 29 | "vue-progressbar": "^0.7.5", 30 | "vue-router": "^3.1.6", 31 | "vue-select": "^3.10.3", 32 | "vue-spinner": "^1.0.3", 33 | "vue-spinner-component": "^1.0.5", 34 | "idle-vue": "^2.0.5", 35 | "sweetalert2": "^9.10.12", 36 | "@sweetalert2/theme-bootstrap-4": "^3.1.4", 37 | "vuelidate": "^0.7.5", 38 | "vuex": "^3.1.3", 39 | "webpack": "^4.42.0", 40 | "yarn": "^1.22.4" 41 | }, 42 | "devDependencies": { 43 | "@vue/cli-plugin-babel": "^3.12.1", 44 | "@vue/cli-plugin-eslint": "^3.12.1", 45 | "@vue/cli-service": "^4.3.1", 46 | "acorn": "^6.4.1", 47 | "babel-eslint": "^10.1.0", 48 | "eslint": "^5.8.0", 49 | "eslint-plugin-vue": "^5.0.0", 50 | "eventsource-polyfill": "^0.9.6", 51 | "json-loader": "^0.5.7", 52 | "kind-of": "^6.0.3", 53 | "minimist": "^1.2.3", 54 | "prerender-spa-plugin": "^3.4.0", 55 | "vue-template-compiler": "^2.6.11", 56 | "webpack-hot-middleware": "^2.24.3" 57 | }, 58 | "eslintConfig": { 59 | "root": true, 60 | "env": { 61 | "node": true 62 | }, 63 | "extends": [ 64 | "plugin:vue/essential", 65 | "eslint:recommended" 66 | ], 67 | "rules": {}, 68 | "parserOptions": { 69 | "parser": "babel-eslint" 70 | } 71 | }, 72 | "postcss": { 73 | "plugins": { 74 | "autoprefixer": {} 75 | } 76 | }, 77 | "browserslist": [ 78 | "> 1%", 79 | "last 2 versions", 80 | "not ie <= 8" 81 | ] 82 | } 83 | -------------------------------------------------------------------------------- /frontend/src/pages/UserProfile/UserCard.vue: -------------------------------------------------------------------------------- 1 | 44 | 45 | 80 | 81 | 83 | -------------------------------------------------------------------------------- /frontend/src/layout/dashboard/TopNavbar.vue: -------------------------------------------------------------------------------- 1 | 39 | 40 | 75 | 76 | 78 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | bold := $(shell tput bold) 2 | sgr0 := $(shell tput sgr0) 3 | 4 | help: 5 | @printf "\nBelow commands have been made available to ease up Molly Wallet development.\n\n" 6 | 7 | @printf "Molly Wallet App: \n" 8 | 9 | @printf " $(bold)run_dev_env$(sgr0) - Starts up the dev enviornment on http://localhost:8080\n" 10 | @printf " $(bold)build_app$(sgr0) - Compiles the Molly Wallet to the build folder\n" 11 | @printf " $(bold)buid_app_production$(sgr0) - Builds a production build for the OS it's running on\n" 12 | @printf " $(bold)cross_compile_app$(sgr0) - Compiling for every OS and Platform to the build directory\n\n" 13 | 14 | @printf "Update Module: \n" 15 | 16 | @printf " $(bold)build_update_module$(sgr0) - Builds the update module to the ~/.dag folder\n" 17 | @printf " $(bold)run_update_module$(sgr0) - Builds and runs the update module\n" 18 | @printf " $(bold)cross_compile_update_module$(sgr0) - Compiling for every OS and Platform to the build directory\n\n" 19 | 20 | @printf "General: \n" 21 | 22 | @printf " $(bold)all$(sgr0) - Builds and compiles both the wallet and the Update Module\n" 23 | @printf " $(bold)cross_compile_all$(sgr0) - Builds and compiles both the wallet and the Update Module for all Platforms\n\n" 24 | 25 | @printf " $(bold)clean$(sgr0) - Cleans up old builds from the build directory\n\n" 26 | 27 | 28 | run_dev_env: 29 | @echo "Starting up frontend dev env on http://localhost:8080..." 30 | $(shell wails serve) \ 31 | $(shell cd frontend && npm run serve) 32 | 33 | build_app: 34 | @echo "Building Molly Wallet binary to build folder..." 35 | wails build 36 | 37 | buid_app_production: 38 | @echo "Building app for production..." 39 | wails build -f -p 40 | 41 | cross_compile_app: 42 | @echo "Compiling for every OS and Platform..." 43 | wails build -x darwin/amd64 44 | wails build -x windows/amd64 45 | wails build -x linux/amd64 46 | 47 | build_update_module: 48 | @echo "Building update module binary to ~/.dag" 49 | go build -o ~/.dag/update backend/cmd/update/main.go 50 | 51 | run_update_module: 52 | @echo "Compiling and running the update module" 53 | go run backend/cmd/update/main.go 54 | 55 | 56 | cross_compile_update_module: 57 | @echo "Compiling for every OS and Platform..." 58 | GOOS=linux GOARCH=amd64 go build -o build/update-linux-amd backend/cmd/update/main.go 59 | GOOS=windows GOARCH=amd64 go build -o build/update-windows-amd64 backend/cmd/update/main.go 60 | GOOS=darwin GOARCH=amd64 go build -o build/main-darwin-amd64 backend/cmd/update/main.go 61 | 62 | clean: 63 | @echo "Cleaning up build directory..." 64 | @rm -rf build 65 | 66 | all: build_app build_update_module 67 | cross_compile_all: cross_compile_app cross_compile_update_module -------------------------------------------------------------------------------- /frontend/src/store/modules/dashboard.js: -------------------------------------------------------------------------------- 1 | const getDefaultState = () => { 2 | return { 3 | counters: { 4 | block: 5, 5 | token: 30, 6 | value: 60, 7 | chart: 24 8 | }, 9 | toggle: { 10 | nodesOnline: false, 11 | transactions: true, 12 | throughput: true, 13 | }, 14 | stat: { 15 | blocks: "NaN", 16 | }, 17 | chart: { 18 | nodesOnline: { 19 | labels: [], // ChartData.nodes_online.labels, 20 | series: [] // ChartData.nodes_online.series 21 | }, 22 | transactions: { 23 | labels: [], //[ChartData.transactions.labels], 24 | series: [] //[ChartData.transactions.series_one, ChartData.transactions.series_two] 25 | }, 26 | throughput: { 27 | labels: [], //[ChartData.throughput.labels], 28 | series: [] //[ChartData.throughput.series_one, ChartData.throughput.series_two] 29 | } 30 | } 31 | } 32 | } 33 | 34 | const state = getDefaultState() 35 | 36 | const actions = { 37 | reset({ commit }) { 38 | commit('resetState') 39 | } 40 | } 41 | const mutations = { 42 | resetState(state) { 43 | Object.assign(state, getDefaultState()) 44 | }, 45 | setTokenCounter(state, counter) { 46 | state.counters.token = counter 47 | }, 48 | setValueCounter(state, counter) { 49 | state.counters.value = counter 50 | }, 51 | setBlockCounter(state, counter) { 52 | state.counters.block = counter 53 | }, 54 | setChartCounter(state, counter) { 55 | state.counters.chart = counter 56 | }, 57 | setShowNodesOnline(state, val) { 58 | state.toggle.nodesOnline = val; 59 | }, 60 | setShowTransactions(state, val) { 61 | state.toggle.transactions = val; 62 | }, 63 | setShowThroughput(state, val) { 64 | state.toggle.throughput = val; 65 | }, 66 | setBlocks(state, blocks) { 67 | state.stat.blocks = blocks; 68 | }, 69 | setNodeOnlineChart(state, obj) { 70 | state.chart.nodesOnline.series = obj.series; 71 | state.chart.nodesOnline.labels = obj.labels; 72 | }, 73 | setTransactionStatsChart(state, obj) { 74 | state.chart.transactions.series = [obj.seriesOne, obj.seriesTwo]; 75 | state.chart.transactions.labels = obj.labels; 76 | }, 77 | setNetworkStatsChart(state, obj) { 78 | state.chart.throughput.series = [obj.seriesOne, obj.seriesTwo]; 79 | state.chart.throughput.labels = obj.labels; 80 | } 81 | } 82 | 83 | export default { 84 | namespaced: true, 85 | state, 86 | getters: {}, 87 | actions, 88 | mutations 89 | } -------------------------------------------------------------------------------- /frontend/src/router/routes.js: -------------------------------------------------------------------------------- 1 | import LoginLayout from "@/layout/login/LoginLayout.vue"; 2 | import Download from "@/pages/DownloadingScreen"; 3 | import NewWallet from "@/pages/NewWallet"; 4 | import Login from "@/pages/Login"; 5 | import Loading from "@/pages/LoadingScreen"; 6 | 7 | import DashboardLayout from "@/layout/dashboard/DashboardLayout.vue"; 8 | import Dashboard from "@/pages/Dashboard.vue"; 9 | import WalletInformation from "@/pages/WalletInformation.vue"; 10 | import About from "@/pages/About.vue"; 11 | import Settings from "@/pages/Settings.vue"; 12 | import Transactions from "@/pages/Transactions.vue"; 13 | 14 | import TermsOfService from "@/pages/TermsOfService.vue"; 15 | import NotFound from "@/pages/NotFoundPage.vue"; 16 | 17 | const routes = [ 18 | { 19 | path: "/", 20 | component: LoginLayout, 21 | redirect: "/download", 22 | children: [ 23 | { 24 | path: "download", 25 | name: "download", 26 | component: Download 27 | }, 28 | { 29 | path: "new-wallet", 30 | name: "new wallet", 31 | component: NewWallet 32 | }, 33 | { 34 | path: "login", 35 | name: "login", 36 | component: Login 37 | }, 38 | { 39 | path: "accept-terms-of-service", 40 | name: "accept terms of service", 41 | component: TermsOfService 42 | }, 43 | { 44 | path: "loading", 45 | name: "loading", 46 | component: Loading 47 | } 48 | ] 49 | }, 50 | { 51 | path: "/home", 52 | name: "home", 53 | component: DashboardLayout, 54 | redirect: "/home/dashboard", 55 | children: [ 56 | { 57 | path: "dashboard", 58 | name: "dashboard", 59 | component: Dashboard 60 | }, 61 | { 62 | path: "wallet-info", 63 | name: "wallet information", 64 | component: WalletInformation 65 | }, 66 | { 67 | path: "about", 68 | name: "about", 69 | component: About 70 | }, 71 | { 72 | path: "submit-transaction", 73 | name: "submit transaction", 74 | component: Transactions 75 | }, 76 | { 77 | path: "settings", 78 | name: "settings", 79 | component: Settings 80 | }, 81 | { 82 | path: "terms-of-service", 83 | name: "terms of service", 84 | component: TermsOfService 85 | }, 86 | ] 87 | }, 88 | { path: "*", component: NotFound } 89 | ]; 90 | 91 | /** 92 | * Asynchronously load view (Webpack Lazy loading compatible) 93 | * The specified component must be inside the Views folder 94 | * @param {string} name the filename (basename) of the view to load. 95 | function view(name) { 96 | var res= require('../components/Dashboard/Views/' + name + '.vue'); 97 | return res; 98 | };**/ 99 | 100 | export default routes; 101 | -------------------------------------------------------------------------------- /frontend/src/assets/sass/paper/_tables.scss: -------------------------------------------------------------------------------- 1 | @import "themes"; 2 | 3 | .table { 4 | @include themed() { 5 | color: t('cardTextColor'); 6 | } 7 | thead, 8 | tbody, 9 | tfoot { 10 | tr > th, 11 | tr > td { 12 | border-top: 0.0625em solid; 13 | @include themed() { 14 | border-top-color: t('borderColor'); 15 | border-bottom-color: t('borderColor'); 16 | background-color: t('cardTransactionTableColor'); 17 | } 18 | } 19 | } 20 | thead th { 21 | border-bottom: 0.0625em solid; 22 | @include themed() { 23 | border-bottom-color: t('borderColor'); 24 | border-top-color: t('borderColor'); 25 | } 26 | border-top: 0; 27 | } 28 | > thead > tr > th { 29 | border-bottom-width: 0; 30 | @include themed() { 31 | border-bottom-color: t('borderColor'); 32 | border-top-color: t('borderColor'); 33 | } 34 | font-size: $font-size-h5; 35 | font-weight: $font-weight-light; 36 | } 37 | 38 | .radio, 39 | .checkbox { 40 | margin-top: 0; 41 | margin-bottom: 1.375em; 42 | padding: 0; 43 | width: 0.9375em; 44 | } 45 | > thead > tr > th, 46 | > tbody > tr > th, 47 | > tfoot > tr > th, 48 | > thead > tr > td, 49 | > tbody > tr > td, 50 | > tfoot > tr > td { 51 | padding: 0.75em; 52 | vertical-align: middle; 53 | } 54 | 55 | .th-description { 56 | max-width: 9.375em; 57 | } 58 | .td-price { 59 | font-size: 1.625em; 60 | font-weight: $font-weight-light; 61 | margin-top: 0.3125em; 62 | text-align: right; 63 | } 64 | .td-total { 65 | font-weight: $font-weight-bold; 66 | font-size: $font-size-h5; 67 | padding-top: 1.25em; 68 | text-align: right; 69 | } 70 | 71 | .td-actions .btn { 72 | 73 | &.btn-sm, 74 | &.btn-xs { 75 | padding-left: 0.1875em; 76 | padding-right: 0.1875em; 77 | } 78 | } 79 | 80 | > tbody > tr { 81 | position: relative; 82 | } 83 | } 84 | 85 | .table-striped { 86 | tbody > tr:nth-of-type(2n+1) { 87 | background-color: #fff; 88 | @include themed() { 89 | background-color: t('borderColor'); 90 | } 91 | } 92 | tbody > tr:nth-of-type(2n) { 93 | background-color: $pale-bg; 94 | @include themed() { 95 | background-color: t('borderColor'); 96 | } 97 | } 98 | > thead > tr > th, 99 | > tbody > tr > th, 100 | > tfoot > tr > th, 101 | > thead > tr > td, 102 | > tbody > tr > td, 103 | > tfoot > tr > td { 104 | padding: 0.9375em 0.5em; 105 | } 106 | } 107 | 108 | .table-noheader { 109 | width:100%; 110 | } 111 | 112 | .table-noheader > tr { 113 | border-bottom: 0.625em solid; 114 | @include themed() { 115 | background-color: t('cardTableColor'); 116 | border-bottom-color: t('cardBackgroundColor'); 117 | } 118 | } 119 | 120 | .table-noheader > tr > td { 121 | padding: 0.625em; 122 | } 123 | 124 | .table-noheader > tr > td > p { 125 | margin-bottom: 0; 126 | } -------------------------------------------------------------------------------- /frontend/src/layout/dashboard/DashboardLayout.vue: -------------------------------------------------------------------------------- 1 | 52 | 53 | 55 | 56 | 97 | -------------------------------------------------------------------------------- /frontend/src/components/Inputs/Password.vue: -------------------------------------------------------------------------------- 1 | 31 | 32 | 90 | 91 | -------------------------------------------------------------------------------- /backend/lcm.go: -------------------------------------------------------------------------------- 1 | package app 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "errors" 7 | "fmt" 8 | "io/ioutil" 9 | "net/http" 10 | "os" 11 | "os/exec" 12 | "runtime" 13 | ) 14 | 15 | type UpdateWallet struct { 16 | currentVersion string 17 | newVersion string 18 | mollyBinaryPath string 19 | dagFolderPath string 20 | app *WalletApplication 21 | } 22 | 23 | func (u *UpdateWallet) Run() { 24 | var err error 25 | 26 | u.currentVersion = u.app.Version 27 | u.newVersion = u.GetLatestRelease() 28 | u.mollyBinaryPath, err = os.Executable() 29 | if err != nil { 30 | u.app.log.Errorln("Unable to collect the path of the molly wallet binary. Reason: ", err) 31 | } 32 | u.dagFolderPath = u.app.paths.DAGDir 33 | 34 | err = u.TriggerUpdate() 35 | if err != nil { 36 | u.app.log.Errorln("Unable to Update Molly Wallet. Reason: ", err) 37 | u.app.sendError("Unable to Update Molly Wallet. Reason: ", err) 38 | } 39 | 40 | } 41 | 42 | func (u *UpdateWallet) TriggerUpdate() error { 43 | 44 | _, fileExt := getUserOS() 45 | 46 | main := u.dagFolderPath + "/update" + fileExt 47 | args := []string{"-init_dag_path=" + u.dagFolderPath, "-init_molly_path=" + u.mollyBinaryPath, "-new_version=" + u.newVersion, "-upgrade=" + "true"} 48 | 49 | cmd := exec.Command(main, args...) 50 | u.app.log.Infoln("Running command: ", cmd) 51 | 52 | var stderr bytes.Buffer 53 | cmd.Stderr = &stderr // Captures STDERR 54 | 55 | err := cmd.Run() 56 | if err != nil { 57 | errFormatted := fmt.Sprint(err) + ": " + stderr.String() 58 | return errors.New(errFormatted) 59 | } 60 | return nil 61 | } 62 | 63 | func (u *UpdateWallet) GetLatestRelease() string { 64 | 65 | const ( 66 | url = "https://api.github.com/repos/grvlle/constellation_wallet/releases/latest" 67 | ) 68 | 69 | resp, err := http.Get(url) 70 | if err != nil { 71 | u.app.log.Errorln("Failed to send HTTP request. Reason: ", err) 72 | return "" 73 | } 74 | if resp == nil { 75 | u.app.log.Errorln("Killing pollTokenBalance after 10 failed attempts to get balance from mainnet, Reason: ", err) 76 | u.app.sendWarning("Unable to showcase current balance. Please check your internet connectivity and restart the wallet application.") 77 | return "" 78 | } 79 | defer resp.Body.Close() 80 | 81 | bodyBytes, err := ioutil.ReadAll(resp.Body) 82 | if err != nil { 83 | u.app.log.Warn("Unable to update token balance. Reason: ", err) 84 | return "" 85 | } 86 | 87 | var result map[string]interface{} 88 | 89 | // Unmarshal or Decode the JSON to the interface. 90 | err = json.Unmarshal(bodyBytes, &result) 91 | if err != nil { 92 | return "" 93 | } 94 | 95 | release := result["tag_name"] 96 | bytes := []byte(release.(string)) 97 | version := string(bytes[1:6]) 98 | return version 99 | 100 | } 101 | 102 | // getUserOS returns the users OS as well as the file extension of executables for said OS 103 | func getUserOS() (string, string) { 104 | var osBuild string 105 | var fileExt string 106 | 107 | switch os := runtime.GOOS; os { 108 | case "darwin": 109 | osBuild = "darwin" 110 | fileExt = "" 111 | case "linux": 112 | osBuild = "linux" 113 | fileExt = "" 114 | case "windows": 115 | osBuild = "windows" 116 | fileExt = ".exe" 117 | default: 118 | osBuild = "unsupported" 119 | fileExt = "" 120 | } 121 | 122 | return osBuild, fileExt 123 | } 124 | -------------------------------------------------------------------------------- /frontend/src/assets/sass/paper/_checkbox-radio.scss: -------------------------------------------------------------------------------- 1 | /* Checkbox and radio */ 2 | .checkbox, 3 | .radio { 4 | margin-bottom: 0.75em; 5 | padding-left: 1.875em; 6 | position: relative; 7 | -webkit-transition: color, opacity 0.25s linear; 8 | transition: color, opacity 0.25s linear; 9 | font-size: $font-size-base; 10 | font-weight: normal; 11 | line-height: 1.5; 12 | color: $font-color; 13 | cursor: pointer; 14 | 15 | .icons { 16 | color: $font-color; 17 | display: block; 18 | height: 1.25em; 19 | left: 0; 20 | position: absolute; 21 | top: 0; 22 | width: 1.25em; 23 | text-align: center; 24 | line-height: 1.3125em; 25 | font-size: 1.25rem; 26 | cursor: pointer; 27 | -webkit-transition: color, opacity 0.15s linear; 28 | transition: color, opacity 0.15s linear; 29 | 30 | opacity: .50; 31 | } 32 | 33 | &.checked { 34 | .icons { 35 | opacity: 1; 36 | } 37 | } 38 | 39 | input { 40 | outline: none !important; 41 | display: none; 42 | } 43 | } 44 | 45 | .checkbox, 46 | .radio { 47 | label { 48 | padding-left: 0.625em; 49 | } 50 | } 51 | 52 | .checkbox .icons .first-icon, 53 | .radio .icons .first-icon, 54 | .checkbox .icons .second-icon, 55 | .radio .icons .second-icon { 56 | display: inline-table; 57 | position: absolute; 58 | left: 0; 59 | top: 0; 60 | background-color: transparent; 61 | margin: 0; 62 | @include opacity(1); 63 | } 64 | 65 | .checkbox .icons .second-icon, 66 | .radio .icons .second-icon { 67 | @include opacity(0); 68 | } 69 | 70 | .checkbox:hover, 71 | .radio:hover { 72 | -webkit-transition: color 0.2s linear; 73 | transition: color 0.2s linear; 74 | } 75 | 76 | .checkbox:hover .first-icon, 77 | .radio:hover .first-icon { 78 | @include opacity(0); 79 | } 80 | 81 | .checkbox:hover .second-icon, 82 | .radio:hover .second-icon { 83 | @include opacity (1); 84 | } 85 | 86 | .checkbox.checked, 87 | .radio.checked { 88 | // color: $info-color; 89 | } 90 | 91 | .checkbox.checked .first-icon, 92 | .radio.checked .first-icon { 93 | opacity: 0; 94 | filter: alpha(opacity=0); 95 | } 96 | 97 | .checkbox.checked .second-icon, 98 | .radio.checked .second-icon { 99 | opacity: 1; 100 | filter: alpha(opacity=100); 101 | // color: $info-color; 102 | -webkit-transition: color 0.2s linear; 103 | transition: color 0.2s linear; 104 | } 105 | 106 | .checkbox.disabled, 107 | .radio.disabled { 108 | cursor: default; 109 | color: $medium-gray; 110 | } 111 | 112 | .checkbox.disabled .icons, 113 | .radio.disabled .icons { 114 | color: $medium-gray; 115 | } 116 | 117 | .checkbox.disabled .first-icon, 118 | .radio.disabled .first-icon { 119 | opacity: 1; 120 | filter: alpha(opacity=100); 121 | } 122 | 123 | .checkbox.disabled .second-icon, 124 | .radio.disabled .second-icon { 125 | opacity: 0; 126 | filter: alpha(opacity=0); 127 | } 128 | 129 | .checkbox.disabled.checked .icons, 130 | .radio.disabled.checked .icons { 131 | color: $medium-gray; 132 | } 133 | 134 | .checkbox.disabled.checked .first-icon, 135 | .radio.disabled.checked .first-icon { 136 | opacity: 0; 137 | filter: alpha(opacity=0); 138 | } 139 | 140 | .checkbox.disabled.checked .second-icon, 141 | .radio.disabled.checked .second-icon { 142 | opacity: 1; 143 | color: $medium-gray; 144 | filter: alpha(opacity=100); 145 | } 146 | -------------------------------------------------------------------------------- /frontend/src/assets/sass/paper/_misc.scss: -------------------------------------------------------------------------------- 1 | /* General overwrite */ 2 | @import "themes"; 3 | 4 | body { 5 | color: $font-color; 6 | font-size: $font-size-base; 7 | font-family: 'Poppins', Arial, sans-serif; 8 | .wrapper { 9 | min-height: 100vh; 10 | position: relative; 11 | } 12 | } 13 | 14 | a { 15 | color: $info-color; 16 | 17 | &:hover, &:focus { 18 | color: $info-states-color; 19 | text-decoration: none; 20 | } 21 | } 22 | 23 | a:focus, a:active, 24 | button::-moz-focus-inner, 25 | input::-moz-focus-inner, 26 | select::-moz-focus-inner, 27 | input[type="file"] > input[type="button"]::-moz-focus-inner { 28 | outline: 0 !important; 29 | } 30 | 31 | .ui-slider-handle:focus, 32 | .navbar-toggle, 33 | input:focus, 34 | button:focus { 35 | outline: 0 !important; 36 | } 37 | 38 | .navbar.navbar-default { 39 | z-index: 2; 40 | } 41 | 42 | /* Animations */ 43 | .form-control, 44 | .input-group-addon, 45 | .tagsinput, 46 | .navbar, 47 | .navbar .alert { 48 | @include transition($general-transition-time, $transition-linear); 49 | } 50 | 51 | .sidebar .nav a, 52 | .table > tbody > tr .td-actions .btn { 53 | @include transition($fast-transition-time, $transition-ease-in); 54 | } 55 | 56 | .btn { 57 | @include transition($ultra-fast-transition-time, $transition-ease-in); 58 | } 59 | 60 | .fa { 61 | width: 1.3125em; 62 | text-align: center; 63 | } 64 | 65 | .fa-base { 66 | font-size: 1.25em !important; 67 | } 68 | 69 | .margin-top { 70 | margin-top: 3.125em; 71 | } 72 | 73 | hr { 74 | @include themed() { 75 | border-color: t('borderColor'); 76 | } 77 | } 78 | 79 | .wrapper { 80 | position: relative; 81 | top: 0; 82 | height: 100vh; 83 | } 84 | 85 | @media (min-width: 992px) { 86 | .typo-line { 87 | padding-left: 8.75em; 88 | margin-bottom: 2.5em; 89 | position: relative; 90 | } 91 | 92 | .typo-line .category { 93 | transform: translateY(-50%); 94 | top: 50%; 95 | left: 0em; 96 | position: absolute; 97 | } 98 | } 99 | 100 | .icon-section { 101 | margin: 0 0 3em; 102 | clear: both; 103 | overflow: hidden; 104 | } 105 | 106 | .icon-container { 107 | width: 15em; 108 | padding: .7em 0; 109 | float: left; 110 | position: relative; 111 | text-align: left; 112 | } 113 | 114 | .icon-container [class^="ti-"], 115 | .icon-container [class*=" ti-"] { 116 | color: #000; 117 | position: absolute; 118 | margin-top: 0.1875em; 119 | transition: .3s; 120 | } 121 | 122 | .icon-container:hover [class^="ti-"], 123 | .icon-container:hover [class*=" ti-"] { 124 | font-size: 2.2em; 125 | margin-top: -0.3125em; 126 | } 127 | 128 | .icon-container:hover .icon-name { 129 | color: #000; 130 | } 131 | 132 | .icon-name { 133 | color: #aaa; 134 | margin-left: 2.1875em; 135 | font-size: .8em; 136 | transition: .3s; 137 | } 138 | 139 | .icon-container:hover .icon-name { 140 | margin-left: 2.8125em; 141 | } 142 | 143 | .places-buttons .btn { 144 | margin-bottom: 1.875em 145 | } 146 | 147 | .sidebar .nav > li.active-pro { 148 | position: absolute; 149 | width: 100%; 150 | bottom: 0.625em; 151 | } 152 | 153 | .sidebar .nav > li.active-pro a { 154 | background: rgba(255, 255, 255, 0.14); 155 | opacity: 1; 156 | color: #FFFFFF; 157 | } 158 | 159 | .table-upgrade td:nth-child(2), 160 | .table-upgrade td:nth-child(3) { 161 | text-align: center; 162 | } 163 | 164 | -------------------------------------------------------------------------------- /frontend/src/layout/login/LoginLayout.vue: -------------------------------------------------------------------------------- 1 | 30 | 31 | 73 | 74 | 115 | -------------------------------------------------------------------------------- /frontend/src/pages/UserProfile/EditProfileForm.vue: -------------------------------------------------------------------------------- 1 | 109 | 110 | 125 | 126 | 128 | -------------------------------------------------------------------------------- /frontend/src/components/SidebarPlugin/SideBar.vue: -------------------------------------------------------------------------------- 1 | 34 | 35 | 131 | 132 | -------------------------------------------------------------------------------- /frontend/src/assets/sass/paper/_wallet.scss: -------------------------------------------------------------------------------- 1 | @import "themes"; 2 | 3 | .validate { 4 | height: 0.625em; 5 | display: flex; 6 | } 7 | 8 | .validate > p { 9 | /*flex: 1;*/ 10 | font-size: 0.625rem; 11 | margin-top: 0em; 12 | margin-right: 0.125em; 13 | @include themed() { 14 | color: t('errorTextColor'); 15 | } 16 | } 17 | 18 | .wallet-address { 19 | display: block; 20 | overflow: hidden; 21 | white-space: nowrap; 22 | border-radius: 0.313rem; 23 | text-overflow: ellipsis; 24 | padding-top: 0em; 25 | margin-bottom: 0em; 26 | font-size: 1.5625rem; 27 | @include themed() { 28 | color: t('walletAddressColor'); 29 | } 30 | } 31 | 32 | 33 | .page-error-box { 34 | height: 1.875em; 35 | padding-bottom: 0.625rem; 36 | } 37 | 38 | .page-error-box p { 39 | font-size: 0.75rem; 40 | @include themed() { 41 | color: t('errorTextColor'); 42 | } 43 | } 44 | 45 | .icon-point-right { 46 | @include themed() { 47 | color: t('successColor'); 48 | } 49 | font-size: 2.5rem; 50 | width:100%; 51 | } 52 | 53 | .card { 54 | width: 100%; 55 | display: flex; 56 | flex-direction: column; 57 | } 58 | 59 | .card-body { 60 | width: 100%; 61 | display: flex; 62 | flex-direction: column; 63 | } 64 | 65 | .card-footer { 66 | margin-top: auto; 67 | } 68 | 69 | .input-group-append .btn { 70 | border: 1px solid; 71 | @include themed() { 72 | border-color: t('inputAppendBorderColor'); 73 | } 74 | } 75 | 76 | .input-group-text { 77 | @include themed() { 78 | background-color: t('inputBackgroundColor'); 79 | color: t('inputTextColor'); 80 | border-color: t('inputBorderColor'); 81 | } 82 | } 83 | 84 | .page-link { 85 | @include themed() { 86 | color: t('cardTextColor'); 87 | background-color: t('cardTableColor'); 88 | border-color: t('inputBorderColor'); 89 | } 90 | } 91 | 92 | .page-item.active .page-link { 93 | @include themed() { 94 | background-color: t('infoColor'); 95 | border-color: t('inputBorderColor'); 96 | } 97 | } 98 | 99 | .page-item.disabled .page-link { 100 | @include themed() { 101 | background-color: t('cardBackgroundColor'); 102 | border-color: t('inputBorderColor'); 103 | } 104 | } 105 | 106 | .ct-series-a .ct-line, 107 | .ct-series-a .ct-point { 108 | @include themed() { 109 | stroke: t('chartSeriesALineColor'); 110 | } 111 | } 112 | .ct-series-a .ct-slice-pie, .ct-series-a .ct-area { 113 | @include themed() { 114 | fill: t('chartSeriesAFIllColor'); 115 | } 116 | } 117 | 118 | .ct-series-b .ct-line, 119 | .ct-series-b .ct-point { 120 | @include themed() { 121 | stroke: t('chartSeriesBLineColor'); 122 | } 123 | } 124 | .ct-series-b .ct-slice-pie, .ct-series-b .ct-area { 125 | @include themed() { 126 | fill: t('chartSeriesBFIllColor'); 127 | } 128 | } 129 | 130 | .ct-series-c .ct-line, 131 | .ct-series-c .ct-point { 132 | @include themed() { 133 | stroke: t('chartSeriesCLineColor'); 134 | } 135 | } 136 | .ct-series-c .ct-slice-pie, .ct-series-c .ct-area { 137 | @include themed() { 138 | fill: t('chartSeriesCFIllColor'); 139 | } 140 | } 141 | 142 | .ct-series-a .ct-slice-pie { 143 | @include themed() { 144 | fill: t('pieSeriesAFillColor'); 145 | } 146 | } 147 | 148 | .ct-series-b .ct-slice-pie { 149 | @include themed() { 150 | fill: t('pieSeriesBFillColor'); 151 | } 152 | } 153 | 154 | .ct-series-c .ct-slice-pie { 155 | @include themed() { 156 | fill: t('pieSeriesCFillColor'); 157 | } 158 | } 159 | 160 | .ct-label { 161 | @include themed() { 162 | fill: t('chartTextColor'); 163 | color: t('chartTextColor'); 164 | } 165 | } 166 | 167 | .ct-grid { 168 | @include themed() { 169 | stroke: t('chartGridColor'); 170 | } 171 | } 172 | 173 | .container .row.settings { 174 | margin: 0.625rem 0 0.625rem 0; 175 | padding-top: 0.625rem; 176 | @include themed() { 177 | background-color: t('cardTableColor'); 178 | } 179 | } 180 | 181 | hr.divider { 182 | width: 80%; 183 | margin: 0 auto 0 auto; 184 | border-top: 1px solid #3b424a; 185 | } -------------------------------------------------------------------------------- /frontend/src/assets/sass/paper/mixins/_chartist.scss: -------------------------------------------------------------------------------- 1 | // Scales for responsive SVG containers 2 | $ct-scales: ((1), (15/16), (8/9), (5/6), (4/5), (3/4), (2/3), (5/8), (1/1.618), (3/5), (9/16), (8/15), (1/2), (2/5), (3/8), (1/3), (1/4)) !default; 3 | $ct-scales-names: (ct-square, ct-minor-second, ct-major-second, ct-minor-third, ct-major-third, ct-perfect-fourth, ct-perfect-fifth, ct-minor-sixth, ct-golden-section, ct-major-sixth, ct-minor-seventh, ct-major-seventh, ct-octave, ct-major-tenth, ct-major-eleventh, ct-major-twelfth, ct-double-octave) !default; 4 | 5 | // Class names to be used when generating CSS 6 | $ct-class-chart: ct-chart !default; 7 | $ct-class-chart-line: ct-chart-line !default; 8 | $ct-class-chart-bar: ct-chart-bar !default; 9 | $ct-class-horizontal-bars: ct-horizontal-bars !default; 10 | $ct-class-chart-pie: ct-chart-pie !default; 11 | $ct-class-chart-donut: ct-chart-donut !default; 12 | $ct-class-label: ct-label !default; 13 | $ct-class-series: ct-series !default; 14 | $ct-class-line: ct-line !default; 15 | $ct-class-point: ct-point !default; 16 | $ct-class-area: ct-area !default; 17 | $ct-class-bar: ct-bar !default; 18 | $ct-class-slice-pie: ct-slice-pie !default; 19 | $ct-class-slice-donut: ct-slice-donut !default; 20 | $ct-class-grid: ct-grid !default; 21 | $ct-class-vertical: ct-vertical !default; 22 | $ct-class-horizontal: ct-horizontal !default; 23 | $ct-class-start: ct-start !default; 24 | $ct-class-end: ct-end !default; 25 | 26 | // Container ratio 27 | $ct-container-ratio: (1/1.618) !default; 28 | 29 | // Text styles for labels 30 | $ct-text-color: rgba(0, 0, 0, 0.4) !default; 31 | $ct-text-size: 0.9em !default; 32 | $ct-text-align: flex-start !default; 33 | $ct-text-justify: flex-start !default; 34 | $ct-text-line-height: 1; 35 | 36 | // Grid styles 37 | $ct-grid-color: rgba(0, 0, 0, 0.2) !default; 38 | $ct-grid-dasharray: 0.125em !default; 39 | $ct-grid-width: 0.0625em !default; 40 | 41 | // Line chart properties 42 | $ct-line-width: 0.25em !default; 43 | $ct-line-dasharray: false !default; 44 | $ct-point-size: 0.625em !default; 45 | // Line chart point, can be either round or square 46 | $ct-point-shape: round !default; 47 | // Area fill transparency between 0 and 1 48 | $ct-area-opacity: 0.7 !default; 49 | 50 | // Bar chart bar width 51 | $ct-bar-width: 0.625em !default; 52 | 53 | // Donut width (If donut width is to big it can cause issues where the shape gets distorted) 54 | $ct-donut-width: 3.75em !default; 55 | 56 | // If set to true it will include the default classes and generate CSS output. If you're planning to use the mixins you 57 | // should set this property to false 58 | $ct-include-classes: true !default; 59 | 60 | // If this is set to true the CSS will contain colored series. You can extend or change the color with the 61 | // properties below 62 | $ct-include-colored-series: $ct-include-classes !default; 63 | 64 | // If set to true this will include all responsive container variations using the scales defined at the top of the script 65 | $ct-include-alternative-responsive-containers: $ct-include-classes !default; 66 | 67 | // Series names and colors. This can be extended or customized as desired. Just add more series and colors. 68 | $ct-series-names: (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o) !default; 69 | $ct-series-colors: ( 70 | $info-color, 71 | $success-color, 72 | $danger-color, 73 | $warning-color, 74 | $primary-color, 75 | rgba($info-color, .8), 76 | rgba($success-color, .8), 77 | rgba($warning-color, .8), 78 | rgba($danger-color, .8), 79 | rgba($primary-color, .8), 80 | rgba($info-color, .6), 81 | rgba($success-color, .6), 82 | rgba($warning-color, .6), 83 | rgba($danger-color, .6), 84 | rgba($primary-color, .6) 85 | ) !default; 86 | 87 | // Paper Kit Colors 88 | 89 | .ct-blue { 90 | stroke: $primary-color !important; 91 | } 92 | 93 | .ct-azure { 94 | stroke: $info-color !important; 95 | } 96 | 97 | .ct-green { 98 | stroke: $success-color !important; 99 | } 100 | 101 | .ct-orange { 102 | stroke: $warning-color !important; 103 | } 104 | 105 | .ct-red { 106 | stroke: $danger-color !important; 107 | } 108 | -------------------------------------------------------------------------------- /frontend/src/components/Loader.vue: -------------------------------------------------------------------------------- 1 | 29 | 30 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /frontend/src/assets/sass/paper/_themes.scss: -------------------------------------------------------------------------------- 1 | $themes: ( 2 | light: ( 3 | backgroundColor: $bg-nude, 4 | borderColor: $medium-gray, 5 | 6 | cardBackgroundColor: $white-bg, 7 | cardTitleColor: $black-color, 8 | cardLabelColor: $dark-gray, 9 | cardTextColor: $font-color, 10 | cardTableColor: $smoke-bg, 11 | cardTransactionTableColor: $white-bg, 12 | 13 | navbarBackgroundColor: $bg-nude, 14 | navbarBorderColor: $medium-gray, 15 | navbarTextColor: $black-color, 16 | navbarIconColor: $black-color, 17 | 18 | sidebarBackgroundColor: #212529, 19 | sidebarTextColor: $medium-gray, 20 | sidebarArrowColor: $bg-nude, 21 | 22 | inputBackgroundColor:$gray-input-bg, 23 | inputBorderColor: $medium-gray, 24 | inputAppendBorderColor: $medium-dark-gray, 25 | inputPlaceholderColor: $light-gray, 26 | inputTextColor: $font-color, 27 | inputDisabledBackgroundColor: $light-gray, 28 | inputDisabledTextColor: $medium-dark-gray, 29 | 30 | chartTextColor: $ct-text-color, 31 | chartGridColor: $ct-grid-color, 32 | chartSeriesALineColor: $info-color, 33 | chartSeriesAFIllColor: rgba($info-color, .8), 34 | chartSeriesBLineColor: $success-color, 35 | chartSeriesBFIllColor: rgba($success-color, .8), 36 | chartSeriesCLineColor: $secondary-color, 37 | chartSeriesCFIllColor: rgba($secondary-color, .8), 38 | pieSeriesAFillColor: $info-color, 39 | pieSeriesBFillColor: $success-color, 40 | pieSeriesCFillolor: $secondary-color, 41 | 42 | errorTextColor: firebrick, 43 | walletAddressColor: $medium-dark-gray, 44 | 45 | popupBackgroundColor: $bg-nude, 46 | 47 | primaryColor: $primary-color, 48 | primaryStatesColor: $primary-states-color, 49 | secondaryColor: $secondary-color, 50 | secondaryStatesColor: $secondary-states-color, 51 | infoColor: $info-color, 52 | infoStatesColor: $info-states-color, 53 | successColor: $success-color, 54 | successStatesColor: $success-states-color, 55 | warningColor: $warning-color, 56 | warningStatesColor: $warning-states-color, 57 | dangerColor: $danger-color, 58 | dangerStatesColor: $danger-states-color 59 | ), 60 | dark: ( 61 | backgroundColor: #121212, 62 | borderColor: #2E2E2E, 63 | 64 | cardBackgroundColor: #181818, 65 | cardTitleColor: $light-gray, 66 | cardLabelColor: #AFAFAF, 67 | cardTextColor: #BCBCBC, 68 | cardTableColor: #202020, 69 | cardTransactionTableColor: #202020, 70 | 71 | navbarBackgroundColor: #1d1d1d, 72 | navbarBorderColor: #272727, 73 | navbarTextColor: #BCBCBC, 74 | navbarIconColor: $info-color, 75 | 76 | sidebarBackgroundColor: #181818, 77 | sidebarTextColor: #BCBCBC, 78 | sidebarArrowColor: #121212, 79 | 80 | inputBackgroundColor:#272727, 81 | inputBorderColor: $dark-gray, 82 | inputAppendBorderColor: $dark-gray, 83 | inputPlaceholderColor: $medium-gray, 84 | inputTextColor: #F2F2F2, 85 | inputDisabledBackgroundColor: #2E2E2E, 86 | inputDisabledTextColor: $medium-dark-gray, 87 | 88 | chartTextColor: #BCBCBC, 89 | chartGridColor: #2E2E2E, 90 | chartSeriesALineColor: $info-color-dark, 91 | chartSeriesAFIllColor: rgba($info-color-dark, .2), 92 | chartSeriesBLineColor: rgba($success-color-dark, .5), 93 | chartSeriesBFIllColor: rgba($success-color-dark, .2), 94 | chartSeriesCLineColor: rgba($secondary-color-dark, .5), 95 | chartSeriesCFIllColor: rgba($secondary-color-dark, .2), 96 | pieSeriesAFillColor: $info-color-dark, 97 | pieSeriesBFillColor: $success-color-dark, 98 | pieSeriesCFillColor: $secondary-color-dark, 99 | 100 | errorTextColor: orangered, 101 | walletAddressColor: #BCBCBC, 102 | 103 | popupBackgroundColor: #272727, 104 | 105 | primaryColor: $primary-color-dark, 106 | primaryStatesColor: $primary-states-color-dark, 107 | secondaryColor: $secondary-color-dark, 108 | secondaryStatesColor: $secondary-states-color-dark, 109 | infoColor: $info-color-dark, 110 | infoStatesColor: $info-states-color-dark, 111 | successColor: $success-color-dark, 112 | successStatesColor: $success-states-color-dark, 113 | warningColor: $warning-color, 114 | warningStatesColor: $warning-states-color, 115 | dangerColor: $danger-color, 116 | dangerStatesColor: $danger-states-color 117 | ), 118 | ); 119 | 120 | @mixin themed() { 121 | @each $theme, $map in $themes { 122 | .theme--#{$theme} & { 123 | $theme-map: () !global; 124 | @each $key, $submap in $map { 125 | $value: map-get(map-get($themes, $theme), '#{$key}'); 126 | $theme-map: map-merge($theme-map, ($key: $value)) !global; 127 | } 128 | @content; 129 | $theme-map: null !global; 130 | } 131 | } 132 | } 133 | 134 | @function t($key) { 135 | @return map-get($theme-map, $key); 136 | } -------------------------------------------------------------------------------- /frontend/src/assets/sass/paper/_typography.scss: -------------------------------------------------------------------------------- 1 | @import "themes"; 2 | 3 | h1, .h1, h2, .h2, h3, .h3, h4, .h4, h5, .h5, h6, .h6, p, .navbar, .brand, a, .td-name, td { 4 | -moz-osx-font-smoothing: grayscale; 5 | -webkit-font-smoothing: antialiased; 6 | font-family: 'Puppet', Helvetica, Arial, sans-serif; 7 | } 8 | 9 | h1, .h1, h2, .h2, h3, .h3, h4, .h4 { 10 | font-weight: $font-weight-normal; 11 | margin: $margin-large-vertical 0 $margin-base-vertical; 12 | } 13 | 14 | h1, .h1 { 15 | font-size: $font-size-h1; 16 | } 17 | 18 | h2, .h2 { 19 | font-size: $font-size-h2; 20 | } 21 | 22 | h3, .h3 { 23 | font-size: $font-size-h3; 24 | line-height: 1.4; 25 | margin: 1.25em 0 0.625em; 26 | } 27 | 28 | h4, .h4 { 29 | font-size: $font-size-h4; 30 | font-weight: $font-weight-bold; 31 | line-height: 1.2em; 32 | } 33 | 34 | h5, .h5 { 35 | font-size: $font-size-h5; 36 | font-weight: $font-weight-normal; 37 | line-height: 1.4em; 38 | margin-bottom: 0.9375em; 39 | } 40 | 41 | h6, .h6 { 42 | font-size: $font-size-h6; 43 | // font-weight: $font-weight-bold; 44 | text-transform: uppercase; 45 | } 46 | 47 | p { 48 | font-size: $font-paragraph; 49 | line-height: $line-height-general; 50 | } 51 | 52 | h1 small, h2 small, h3 small, h4 small, h5 small, h6 small, .h1 small, .h2 small, .h3 small, .h4 small, .h5 small, .h6 small, h1 .small, h2 .small, h3 .small, h4 .small, h5 .small, h6 .small, .h1 .small, .h2 .small, .h3 .small, .h4 .small, .h5 .small, .h6 .small { 53 | color: $dark-gray; 54 | font-weight: $font-weight-light; 55 | line-height: $line-height-general; 56 | } 57 | 58 | h1 small, h2 small, h3 small, h1 .small, h2 .small, h3 .small { 59 | font-size: 60%; 60 | } 61 | 62 | .version { 63 | position:absolute; 64 | bottom:0; 65 | right:0; 66 | font-size: $font-paragraph; 67 | line-height: $line-height-general; 68 | font-size: 0.6rem; 69 | } 70 | 71 | .wallet-address { 72 | display: block; 73 | overflow: hidden; 74 | white-space: nowrap; 75 | text-overflow: ellipsis; 76 | padding-top: 0em; 77 | margin-bottom: 0em; 78 | font-size: 1.5625rem; 79 | font-weight: 100; 80 | @include themed() { 81 | color: t('walletAddressColor'); 82 | } 83 | } 84 | 85 | .title-uppercase { 86 | text-transform: uppercase; 87 | } 88 | 89 | blockquote { 90 | font-style: italic; 91 | } 92 | 93 | blockquote small { 94 | font-style: normal; 95 | } 96 | 97 | .text-muted { 98 | color: $medium-gray; 99 | } 100 | 101 | .text-primary, .text-primary:hover { 102 | @include themed() { 103 | color: t('primaryStatesColor') !important; 104 | } 105 | } 106 | 107 | .text-secondary, .text-secondary:hover { 108 | @include themed() { 109 | color: t('secondaryStatesColor') !important; 110 | } 111 | } 112 | 113 | .text-info, .text-info:hover { 114 | @include themed() { 115 | color: t('infoStatesColor') !important; 116 | } 117 | } 118 | 119 | .text-success, .text-success:hover { 120 | @include themed() { 121 | color: t('successStatesColor') !important; 122 | } 123 | } 124 | 125 | .text-warning, .text-warning:hover { 126 | @include themed() { 127 | color: t('warningStatesColor') !important; 128 | } 129 | } 130 | 131 | .text-danger, .text-danger:hover { 132 | @include themed() { 133 | color: t('dangerStatesColor') !important; 134 | } 135 | } 136 | 137 | .glyphicon { 138 | line-height: 1; 139 | } 140 | 141 | strong { 142 | color: $default-states-color; 143 | } 144 | 145 | .icon-primary { 146 | @include themed() { 147 | color: t('primaryColor'); 148 | } 149 | } 150 | 151 | .icon-secondary { 152 | @include themed() { 153 | color: t('secondaryColor'); 154 | } 155 | } 156 | 157 | .icon-info { 158 | @include themed() { 159 | color: t('infoColor'); 160 | } 161 | } 162 | 163 | .icon-success { 164 | @include themed() { 165 | color: t('successColor'); 166 | } 167 | } 168 | 169 | .icon-warning { 170 | @include themed() { 171 | color: t('warningColor'); 172 | } 173 | } 174 | 175 | .icon-danger { 176 | @include themed() { 177 | color: t('dangerColor'); 178 | } 179 | } 180 | 181 | .chart-legend { 182 | .text-primary, .text-primary:hover { 183 | @include themed() { 184 | color: t('primaryColor'); 185 | } 186 | } 187 | .text-secondary, .text-secondary:hover { 188 | @include themed() { 189 | color: t('secondaryColor'); 190 | } 191 | } 192 | .text-info, .text-info:hover { 193 | @include themed() { 194 | color: t('infoColor'); 195 | } 196 | } 197 | .text-success, .text-success:hover { 198 | @include themed() { 199 | color: t('successColor'); 200 | } 201 | } 202 | .text-warning, .text-warning:hover { 203 | @include themed() { 204 | color: t('warningColor'); 205 | } 206 | } 207 | .text-danger, .text-danger:hover { 208 | @include themed() { 209 | color: t('dangerColor'); 210 | } 211 | } 212 | } 213 | -------------------------------------------------------------------------------- /frontend/src/assets/sass/paper/_sidebar-and-main-panel.scss: -------------------------------------------------------------------------------- 1 | @import "themes"; 2 | 3 | .wrapper .sidebar { 4 | position: fixed; 5 | top: 0; 6 | bottom: 0; 7 | left: 0; 8 | z-index: 1; 9 | background-size: cover; 10 | background-position: center center; 11 | .sidebar-wrapper { 12 | position: relative; 13 | height: 100%; 14 | overflow-y: auto; 15 | overflow-x: hidden; 16 | width: 16.25em; 17 | z-index: 4; 18 | // box-shadow: inset -0.0625em 0em 0em 0em $medium-gray; 19 | } 20 | .sidebar-background { 21 | position: absolute; 22 | z-index: 1; 23 | height: 100%; 24 | width: 100%; 25 | display: block; 26 | top: 0; 27 | left: 0; 28 | background-size: cover; 29 | background-position: center center; 30 | } 31 | 32 | } 33 | 34 | .wrapper .sidebar { 35 | width: 16.25em; 36 | display: block; 37 | font-weight: 600; 38 | 39 | .logo { 40 | padding: 0.8125em 0; 41 | margin: 0 1.25em; 42 | 43 | p { 44 | float: left; 45 | font-size: 1.25rem; 46 | margin: 0.625em 0.625em; 47 | line-height: 1.25em; 48 | } 49 | 50 | .simple-text { 51 | padding: $padding-small-vertical $padding-zero; 52 | display: block; 53 | font-size: 0.9375rem; 54 | //text-align: center; 55 | // font-weight: $font-weight-bold; 56 | line-height: 2.875em; 57 | text-align: left; 58 | 59 | .logo-img{ 60 | width: 9.375em; 61 | display: inline-block; 62 | height: 9.375em; 63 | margin-left: 1.6875em; 64 | // margin-right: 0.625em; 65 | background: transparent; 66 | border-radius: 7.5em; 67 | text-align: center; 68 | 69 | img{ 70 | margin-top: 0.25em; 71 | max-width: 8.75em; 72 | } 73 | } 74 | } 75 | } 76 | 77 | .nav { 78 | //margin-top: 1.25em; 79 | 80 | .nav-item { 81 | width: 100%; 82 | .nav-link { 83 | margin: 0.625em 0em; 84 | padding-left: 1.5625em; 85 | padding-right: 1.5625em; 86 | 87 | opacity: 0.7; 88 | } 89 | 90 | &:hover > .nav-link { 91 | opacity: 1; 92 | } 93 | 94 | &.active > .nav-link { 95 | color: $primary-color; 96 | opacity: 1; 97 | } 98 | } 99 | 100 | p { 101 | margin: 0; 102 | line-height: 1.875em; 103 | font-size: 0.875rem; 104 | font-weight: 200; 105 | font-family: Poppins; 106 | text-transform: uppercase; 107 | } 108 | 109 | i { 110 | font-size: 1.025rem; 111 | float: left; 112 | margin-right: 0.9375em; 113 | line-height: 1.375em; 114 | width: 1.875em; 115 | text-align: center; 116 | } 117 | } 118 | 119 | &:after, 120 | &:before { 121 | display: block; 122 | content: ""; 123 | position: absolute; 124 | width: 100%; 125 | height: 100%; 126 | top: 0; 127 | left: 0; 128 | z-index: 2; 129 | @include themed() { 130 | background: t('sidebarBackgroundColor'); 131 | } 132 | } 133 | 134 | .logo { 135 | p { 136 | @include themed() { 137 | color: t('sidebarTextColor'); 138 | } 139 | } 140 | 141 | .simple-text { 142 | @include themed() { 143 | color: t('sidebarTextColor'); 144 | } 145 | } 146 | } 147 | 148 | .nav { 149 | .nav-item:not(.active) .nav-item { 150 | > .nav-link { 151 | @include themed() { 152 | color: t('sidebarTextColor'); 153 | } 154 | } 155 | } 156 | 157 | .divider { 158 | background-color: rgba($black-color, .2); 159 | } 160 | 161 | } 162 | 163 | &[data-active-color="primary"] { 164 | @include sidebar-active-color($primary-color); 165 | } 166 | &[data-active-color="info"] { 167 | @include sidebar-active-color($info-color); 168 | } 169 | &[data-active-color="success"] { 170 | @include sidebar-active-color($info-color); 171 | } 172 | &[data-active-color="warning"] { 173 | @include sidebar-active-color($warning-color); 174 | } 175 | &[data-active-color="danger"] { 176 | @include sidebar-active-color($danger-color); 177 | } 178 | 179 | } 180 | 181 | .main-panel { 182 | position: relative; 183 | z-index: 2; 184 | float: right; 185 | width: $sidebar-width; 186 | min-height: 100%; 187 | overflow: auto; 188 | @include themed() { 189 | background-color: t('backgroundColor'); 190 | } 191 | 192 | > .content { 193 | padding: 1.875em 0.9375em; 194 | min-height: calc(100% - 7.6875em); 195 | } 196 | 197 | > .footer { 198 | border-top: 0.0625em solid rgba(0, 0, 0, 0.1); 199 | } 200 | 201 | .navbar { 202 | margin-bottom: 0; 203 | } 204 | } 205 | 206 | .wrapper .sidebar, 207 | .main-panel { 208 | -webkit-transition-property: top, bottom; 209 | transition-property: top, bottom; 210 | -webkit-transition-duration: .2s, .2s; 211 | transition-duration: .2s, .2s; 212 | -webkit-transition-timing-function: linear, linear; 213 | transition-timing-function: linear, linear; 214 | -webkit-overflow-scrolling: touch; 215 | } 216 | 217 | .wrapper .sidebar { 218 | max-height: 100%; 219 | height: 100%; 220 | overflow: hidden; 221 | overflow-y: hidden; 222 | } 223 | -------------------------------------------------------------------------------- /backend/utils.go: -------------------------------------------------------------------------------- 1 | package app 2 | 3 | import ( 4 | "bytes" 5 | "encoding/hex" 6 | "fmt" 7 | "io" 8 | "io/ioutil" 9 | "math/rand" 10 | "net/http" 11 | "os" 12 | "os/exec" 13 | "path/filepath" 14 | "runtime" 15 | "strings" 16 | "time" 17 | 18 | "github.com/dustin/go-humanize" 19 | ) 20 | 21 | // newReleaseAvailable generates a notification to FE everytime a new release on 22 | // GitHub doesn't match a.Version. 23 | func (a *WalletApplication) newReleaseAvailable() { 24 | update := new(UpdateWallet) 25 | update.app = a 26 | currentRelease := a.Version 27 | 28 | a.log.Infoln("Checking for new releases...") 29 | 30 | go func() { 31 | for i := 200; i > 0; i-- { 32 | newRelease := update.GetLatestRelease() 33 | if currentRelease != newRelease { 34 | a.log.Infoln("There's a newer release available") 35 | a.RT.Events.Emit("new_release", newRelease) 36 | } 37 | time.Sleep(time.Duration(i) * time.Second) 38 | } 39 | }() 40 | 41 | } 42 | 43 | func (a *WalletApplication) javaInstalled() bool { 44 | var javaInstalled bool 45 | if a.paths.Java[len(a.paths.Java)-9:] != "javaw.exe" { 46 | javaInstalled = false 47 | } else { 48 | javaInstalled = true 49 | } 50 | return javaInstalled 51 | } 52 | 53 | func (a *WalletApplication) detectJavaPath() { 54 | 55 | if runtime.GOOS == "windows" { 56 | var jwPath string 57 | 58 | cmd := exec.Command("cmd", "/c", "where", "java") 59 | a.log.Infoln("Running command: ", cmd) 60 | 61 | var out bytes.Buffer 62 | var stderr bytes.Buffer 63 | cmd.Stdout = &out // Captures STDOUT 64 | cmd.Stderr = &stderr // Captures STDERR 65 | 66 | err := cmd.Run() 67 | if err != nil { 68 | errFormatted := fmt.Sprint(err) + ": " + stderr.String() 69 | a.log.Errorf(errFormatted) 70 | a.LoginError("Unable to find Java Installation") 71 | a.paths.Java = "No valid path detected" 72 | return 73 | } 74 | jPath := out.String() // May contain multiple 75 | if jPath == "" { 76 | a.LoginError("Unable to find Java Installation") 77 | a.paths.Java = "No valid path detected" 78 | return 79 | } 80 | s := strings.Split(strings.Replace(jPath, "\r\n", "\n", -1), "\n") 81 | jwPath = string(s[0][:len(s[0])-4]) + "w.exe" // Shifting to javaw.exe 82 | if s[1] != "" { 83 | jwPath = string(s[1][:len(s[1])-4]) + "w.exe" // Shifting to javaw.exe 84 | a.log.Info("Detected a secondary java path. Using that over the first one.") 85 | } 86 | a.log.Infoln("Java path selected: " + jwPath) 87 | a.log.Debugln(cmd) 88 | a.paths.Java = jwPath 89 | } 90 | } 91 | 92 | //normalizeAmounts takes amount/fee in int64 and normalizes it. Example: passing 821500000000 will return 8215 93 | func normalizeAmounts(i int64) (string, error) { 94 | f := fmt.Sprintf("%.8f", float64(i)/1e8) 95 | return f, nil 96 | } 97 | 98 | // TempFileName creates temporary file names for the transaction files 99 | func (a *WalletApplication) TempFileName(prefix string) string { 100 | randBytes := make([]byte, 16) 101 | rand.Read(randBytes) 102 | return filepath.Join(a.paths.TMPDir, prefix+hex.EncodeToString(randBytes)) 103 | } 104 | 105 | // WriteCounter stores dl state of the cl binaries 106 | type WriteCounter struct { 107 | Total uint64 108 | LastEmit uint64 109 | Filename string 110 | a *WalletApplication 111 | } 112 | 113 | // Write emits the download progress of the CL binaries to the frontend 114 | func (wc *WriteCounter) Write(p []byte) (int, error) { 115 | n := len(p) 116 | wc.Total += uint64(n) 117 | 118 | if (wc.Total - wc.LastEmit) > uint64(800) { 119 | wc.a.RT.Events.Emit("downloading", wc.Filename, humanize.Bytes(wc.Total)) 120 | wc.LastEmit = wc.Total 121 | } 122 | 123 | return n, nil 124 | } 125 | 126 | func (a *WalletApplication) fetchWalletJar(filename string, filepath string) error { 127 | url := a.WalletCLI.URL + "/v" + a.WalletCLI.Version + "/" + filename 128 | a.log.Info(url) 129 | 130 | out, err := os.Create(filepath + ".tmp") 131 | if err != nil { 132 | return err 133 | } 134 | 135 | resp, err := http.Get(url) 136 | if err != nil { 137 | out.Close() 138 | return err 139 | } 140 | defer resp.Body.Close() 141 | 142 | counter := &WriteCounter{} 143 | counter.a = a 144 | counter.Filename = filename 145 | counter.LastEmit = uint64(0) 146 | 147 | if _, err = io.Copy(out, io.TeeReader(resp.Body, counter)); err != nil { 148 | out.Close() 149 | return err 150 | } 151 | 152 | out.Close() 153 | 154 | if err = os.Rename(filepath+".tmp", filepath); err != nil { 155 | return err 156 | } 157 | 158 | return nil 159 | } 160 | 161 | func (a *WalletApplication) directoryCreator(directories ...string) error { 162 | for _, d := range directories { 163 | err := os.MkdirAll(d, os.ModePerm) 164 | if err != nil { 165 | return err 166 | } 167 | } 168 | return nil 169 | } 170 | 171 | func (a *WalletApplication) fileExists(path string) bool { 172 | info, err := os.Stat(path) 173 | if os.IsNotExist(err) { 174 | return false 175 | } 176 | return !info.IsDir() 177 | } 178 | 179 | // WriteToFile will print any string of text to a file safely by 180 | // checking for errors and syncing at the end. 181 | func WriteToFile(filename string, data []byte) error { 182 | 183 | err := ioutil.WriteFile(filename, data, 0666) 184 | if err != nil { 185 | return err 186 | } 187 | return nil 188 | } 189 | -------------------------------------------------------------------------------- /frontend/src/assets/sass/paper/_inputs.scss: -------------------------------------------------------------------------------- 1 | @import "themes"; 2 | 3 | .form-control::-moz-placeholder { 4 | @include placeholder($medium-dark-gray, 1); 5 | } 6 | 7 | .form-control:-moz-placeholder { 8 | @include placeholder($medium-dark-gray, 1); 9 | } 10 | 11 | .form-control::-webkit-input-placeholder { 12 | @include placeholder($medium-dark-gray, 1); 13 | } 14 | 15 | .form-control:-ms-input-placeholder { 16 | @include placeholder($medium-dark-gray, 1); 17 | } 18 | 19 | .form-control { 20 | border-radius: $border-radius-base; 21 | font-size: $font-size-base*1.2; 22 | transition: background-color 0.3s ease 0s; 23 | @include input-size($padding-base-vertical, $padding-base-horizontal, $height-base); 24 | @include box-shadow(none); 25 | @include themed() { 26 | background-color: t('inputBackgroundColor'); 27 | border-color: t('inputBorderColor'); 28 | color: t('inputTextColor'); 29 | } 30 | 31 | &:focus { 32 | @include box-shadow(none); 33 | outline: 0 !important; 34 | @include themed() { 35 | background-color: t('inputBackgroundColor'); 36 | } 37 | } 38 | 39 | .has-success &, 40 | .has-error &, 41 | .has-success &:focus, 42 | .has-error &:focus { 43 | @include box-shadow(none); 44 | } 45 | 46 | .has-success & { 47 | background-color: $success-input-bg; 48 | color: $success-color; 49 | &.border-input { 50 | border: 0.0625em solid $success-color; 51 | } 52 | } 53 | .has-success &:focus { 54 | background-color: $white-bg; 55 | } 56 | .has-error & { 57 | background-color: $danger-input-bg; 58 | color: $danger-color; 59 | &.border-input { 60 | border: 0.0625em solid $danger-color; 61 | } 62 | } 63 | .has-error &:focus { 64 | background-color: $white-bg; 65 | } 66 | 67 | & + .form-control-feedback { 68 | border-radius: $border-radius-large; 69 | font-size: $font-size-base; 70 | margin-top: -0.4375em; 71 | position: absolute; 72 | right: 0.625em; 73 | top: 50%; 74 | vertical-align: middle; 75 | } 76 | &.border-input { 77 | border: 0.0625em solid; 78 | @include themed() { 79 | border-color: t('inputBorderColor'); 80 | } 81 | } 82 | .open & { 83 | border-bottom-color: transparent; 84 | } 85 | } 86 | 87 | .input-lg { 88 | height: 3.4375em; 89 | padding: $padding-large-vertical $padding-large-horizontal; 90 | } 91 | 92 | .has-error { 93 | .form-control-feedback, .control-label { 94 | color: $danger-color; 95 | } 96 | } 97 | 98 | .has-success { 99 | .form-control-feedback, .control-label { 100 | color: $success-color; 101 | } 102 | } 103 | 104 | .input-group-addon { 105 | background-color: $gray-input-bg; 106 | border-radius: $border-radius-base; 107 | 108 | .has-success &, 109 | .has-error & { 110 | background-color: $white-color; 111 | } 112 | .has-error .form-control:focus + & { 113 | color: $danger-color; 114 | } 115 | .has-success .form-control:focus + & { 116 | color: $success-color; 117 | } 118 | .form-control:focus + &, 119 | .form-control:focus ~ & { 120 | background-color: $white-color; 121 | } 122 | } 123 | 124 | .border-input { 125 | .input-group-addon { 126 | border: solid 0.0625em; 127 | @include themed() { 128 | border-color: t('inputBorderColor'); 129 | } 130 | } 131 | } 132 | 133 | .input-group { 134 | margin-bottom: 0.9375em; 135 | } 136 | 137 | .input-group[disabled] { 138 | .input-group-addon { 139 | @include themed() { 140 | background-color: t('inputDisabledBackgroundColor'); 141 | color: t('inputDisabledTextColor'); 142 | } 143 | } 144 | } 145 | 146 | .input-group .form-control:first-child, 147 | .input-group-addon:first-child, 148 | .input-group-btn:first-child > .dropdown-toggle, 149 | .input-group-btn:last-child > .btn:not(:last-child):not(.dropdown-toggle) { 150 | border-right: 0 none; 151 | } 152 | 153 | .input-group .form-control:last-child, 154 | .input-group-addon:last-child, 155 | .input-group-btn:last-child > .dropdown-toggle, 156 | .input-group-btn:first-child > .btn:not(:first-child) { 157 | border-left: 0 none; 158 | } 159 | 160 | .form-control[disabled], .form-control[readonly], fieldset[disabled] .form-control { 161 | @include themed() { 162 | background-color: t('inputDisabledBackgroundColor'); 163 | color: t('inputDisabledTextColor'); 164 | } 165 | cursor: not-allowed; 166 | } 167 | 168 | .form-control[disabled]::-moz-placeholder { 169 | @include themed() { 170 | color: t('inputPlaceholderColor'); 171 | } 172 | } 173 | 174 | .form-control[disabled]:-moz-placeholder { 175 | @include themed() { 176 | color: t('inputPlaceholderColor'); 177 | } 178 | } 179 | 180 | .form-control[disabled]::-webkit-input-placeholder { 181 | @include themed() { 182 | color: t('inputPlaceholderColor'); 183 | } 184 | } 185 | 186 | .form-control[disabled]:-ms-input-placeholder { 187 | @include themed() { 188 | color: t('inputPlaceholderColor'); 189 | } 190 | } 191 | 192 | .input-group-btn .btn { 193 | font-size: $font-size-base*1; 194 | border-width: $border-thin; 195 | padding: $padding-round-vertical $padding-base-horizontal; 196 | } 197 | 198 | .input-group-btn .btn-default:not(.btn-fill) { 199 | @include themed() { 200 | border-color: t('inputBorderColor'); 201 | } 202 | } 203 | 204 | .input-group-btn:last-child > .btn { 205 | margin-left: 0; 206 | } 207 | 208 | textarea.form-control { 209 | max-width: 100%; 210 | padding: 0.625em 1.125em; 211 | resize: none; 212 | } 213 | 214 | -------------------------------------------------------------------------------- /backend/login.go: -------------------------------------------------------------------------------- 1 | package app 2 | 3 | import ( 4 | "os" 5 | "runtime" 6 | "strings" 7 | 8 | "github.com/grvlle/constellation_wallet/backend/models" 9 | "golang.org/x/crypto/bcrypt" 10 | ) 11 | 12 | // LoginError takes a string and pushes it to the login screen as an errror 13 | func (a *WalletApplication) LoginError(errMsg string) { 14 | if errMsg != "" { 15 | a.RT.Events.Emit("login_error", errMsg, true) 16 | } 17 | } 18 | 19 | // Login is called from the FE when a user logs in with a wallet object 20 | // already in the DB 21 | func (a *WalletApplication) Login(keystorePath, keystorePassword, keyPassword, alias string) bool { 22 | 23 | alias = strings.ToLower(alias) 24 | a.wallet = models.Wallet{ 25 | KeyStorePath: keystorePath, 26 | WalletAlias: alias} 27 | 28 | if runtime.GOOS == "windows" && !a.javaInstalled() { 29 | a.LoginError("Unable to detect your Java path. Please make sure that Java has been installed.") 30 | return false 31 | } 32 | 33 | if !a.TransactionFinished { 34 | a.log.Warn("Cannot login to another wallet during a pending transaction.") 35 | a.LoginError("Cannot login to another wallet during a pending transaction.") 36 | return false 37 | } 38 | 39 | if keystorePath == "" { 40 | a.LoginError("Please provide a path to the KeyStore file.") 41 | return false 42 | } 43 | 44 | if !a.passwordsProvided(keystorePassword, keyPassword, alias) { 45 | a.log.Warnln("One or more passwords were not provided.") 46 | return false 47 | } 48 | 49 | os.Setenv("CL_STOREPASS", keystorePassword) 50 | os.Setenv("CL_KEYPASS", keyPassword) 51 | 52 | if err := a.DB.First(&a.wallet, "wallet_alias = ?", alias).Error; err != nil { 53 | a.log.Errorln("Unable to query database object for existing wallet. Reason: ", err) 54 | return a.ImportWallet(keystorePath, keystorePassword, keyPassword, alias) 55 | } 56 | 57 | if !a.WalletKeystoreAccess() { 58 | a.LoginError("Access Denied. Please make sure that you have typed in the correct credentials.") 59 | return false 60 | } 61 | 62 | if !a.NewUser { 63 | a.DB.Model(&a.wallet).Update("KeystorePath", keystorePath) 64 | a.log.Infoln("PrivateKey path: ", keystorePath) 65 | } 66 | 67 | // Check password strings against salted hashes stored in DB. Also make sure KeyStore has been accessed. 68 | if a.CheckAccess(keystorePassword, a.wallet.KeystorePasswordHash) && a.CheckAccess(keyPassword, a.wallet.KeyPasswordHash) && a.KeyStoreAccess { 69 | a.UserLoggedIn = true 70 | 71 | // os.Setenv("CL_STOREPASS", keystorePassword) 72 | // os.Setenv("CL_KEYPASS", keyPassword) 73 | 74 | } else { 75 | a.UserLoggedIn = false 76 | a.LoginError("Access Denied. Please make sure that you have typed in the correct credentials.") 77 | } 78 | 79 | if a.UserLoggedIn && a.KeyStoreAccess && !a.NewUser { 80 | 81 | err := a.initWallet(keystorePath) 82 | if err != nil { 83 | a.UserLoggedIn = false 84 | } 85 | } 86 | 87 | a.NewUser = false 88 | 89 | return a.UserLoggedIn 90 | } 91 | 92 | // CheckTermsOfService is called from the FE to check the termsOfService has been accepted 93 | func (a *WalletApplication) CheckTermsOfService() bool { 94 | return a.wallet.TermsOfService 95 | } 96 | 97 | // LogOut will reset the wallet UI and clear the wallet objects 98 | func (a *WalletApplication) LogOut() bool { 99 | if a.TransactionFinished { 100 | a.UserLoggedIn = false 101 | a.wallet = models.Wallet{} 102 | return true 103 | } 104 | a.sendWarning("Cannot log out while transaction is processing. Please try again.") 105 | return false 106 | } 107 | 108 | // ImportKey is called from the frontend when browsing the fs for a keyfile 109 | func (a *WalletApplication) ImportKey() string { 110 | var keyfile = a.RT.Dialog.SelectFile() 111 | if keyfile == "" { 112 | a.LoginError("Access Denied. No key path detected.") 113 | return "" 114 | } 115 | 116 | if keyfile[len(keyfile)-4:] != ".p12" { 117 | a.LoginError("Access Denied. Not a key file.") 118 | return "" 119 | } 120 | a.log.Info("Path to imported key: " + keyfile) 121 | return keyfile 122 | } 123 | 124 | // SelectDirToStoreKey is called from the FE when creating a new keyfile 125 | func (a *WalletApplication) SelectDirToStoreKey() string { 126 | 127 | var keyfile = a.RT.Dialog.SelectSaveFile() 128 | 129 | if len(keyfile) <= 0 { 130 | a.LoginError("No valid path were provided. Please try again.") 131 | return "" 132 | } 133 | if keyfile[len(keyfile)-4:] != ".p12" { 134 | keyfile = keyfile + ".p12" 135 | return keyfile 136 | } 137 | return keyfile 138 | } 139 | 140 | // GenerateSaltedHash converts plain text to a salted hash 141 | func (a *WalletApplication) GenerateSaltedHash(s string) (string, error) { 142 | saltedBytes := []byte(s) 143 | hashedBytes, err := bcrypt.GenerateFromPassword(saltedBytes, bcrypt.DefaultCost) 144 | if err != nil { 145 | return "", err 146 | } 147 | hash := string(hashedBytes[:]) 148 | return hash, nil 149 | } 150 | 151 | // CheckAccess verifies that the user has entered the correct password 152 | func (a *WalletApplication) CheckAccess(password, passwordHash string) bool { 153 | err := a.Compare(password, passwordHash) 154 | if err != nil { 155 | a.log.Warnln("User tried to login with the wrong credentials!") 156 | return false 157 | } 158 | a.log.Infoln("Password check OK") 159 | return true 160 | } 161 | 162 | // Compare compares a string with a salted hash 163 | func (a *WalletApplication) Compare(s, hash string) error { 164 | incoming := []byte(s) 165 | existing := []byte(hash) 166 | return bcrypt.CompareHashAndPassword(existing, incoming) 167 | } 168 | -------------------------------------------------------------------------------- /frontend/src/assets/sass/paper/_cards.scss: -------------------------------------------------------------------------------- 1 | @import "themes"; 2 | 3 | .card { 4 | border-radius: $border-radius-extreme; 5 | box-shadow: 0em 0.125em 0.125em rgba(204, 197, 185, 0.5); 6 | margin-bottom: 1.25em; 7 | position: relative; 8 | z-index: 1; 9 | border: none; 10 | @include themed() { 11 | background-color: t('cardBackgroundColor'); 12 | color: t('cardTextColor'); 13 | } 14 | 15 | .card-image { 16 | width: 100%; 17 | overflow: hidden; 18 | height: 17.625em; 19 | border-radius: $border-radius-extreme $border-radius-extreme 0 0; 20 | position: relative; 21 | -webkit-transform-style: preserve-3d; 22 | -moz-transform-style: preserve-3d; 23 | transform-style: preserve-3d; 24 | 25 | img { 26 | width: 100%; 27 | } 28 | } 29 | .card-body { 30 | padding: 0.9375em 0.9375em 0.625em 0.9375em; 31 | } 32 | .card-header { 33 | padding: 1.25em 1.25em 0em; 34 | border-bottom: 0em; 35 | background-color: transparent; 36 | } 37 | .description { 38 | font-size: $font-paragraph; 39 | @include themed() { 40 | color: t('cardTextColor'); 41 | } 42 | } 43 | 44 | h6 { 45 | font-size: $font-size-small; 46 | margin: 0; 47 | } 48 | .card-category, 49 | label { 50 | font-size: $font-size-base; 51 | font-weight: $font-weight-normal; 52 | margin-bottom: 0em; 53 | i { 54 | font-size: $font-paragraph; 55 | } 56 | @include themed() { 57 | color: t('cardLabelColor'); 58 | } 59 | } 60 | 61 | label { 62 | font-size: 0.9375rem; 63 | margin-bottom: 0.3125em; 64 | } 65 | 66 | .card-title { 67 | margin: $none; 68 | font-weight: $font-weight-light; 69 | @include themed() { 70 | color: t('cardTitleColor'); 71 | } 72 | } 73 | .avatar { 74 | width: 3.125em; 75 | height: 3.125em; 76 | overflow: hidden; 77 | border-radius: 50%; 78 | margin-right: 0.3125em; 79 | } 80 | .footer { 81 | padding: 0; 82 | line-height: 1.875em; 83 | overflow: hidden; 84 | 85 | .legend { 86 | padding: 0.3125em 0em; 87 | } 88 | 89 | hr { 90 | margin-top: 0.3125em; 91 | margin-bottom: 0.3125em; 92 | } 93 | } 94 | .stats { 95 | color: #a9a9a9; 96 | flex-wrap: nowrap; 97 | font-weight: 300; 98 | i { 99 | margin-right: 0.125em; 100 | min-width: 0.9375em; 101 | display: inline-block; 102 | } 103 | } 104 | .footer div { 105 | display: inline-block; 106 | } 107 | 108 | .author { 109 | font-size: $font-size-small; 110 | font-weight: $font-weight-bold; 111 | text-transform: uppercase; 112 | } 113 | .author i { 114 | font-size: $font-size-base; 115 | } 116 | 117 | &.card-separator:after { 118 | height: 100%; 119 | right: -0.9375em; 120 | top: 0em; 121 | width: 0.0625em; 122 | background-color: $medium-gray; 123 | content: ""; 124 | position: absolute; 125 | } 126 | 127 | .ct-chart { 128 | margin: 1.875em 0em 1.875em; 129 | height: 15.3125em; 130 | } 131 | 132 | .table { 133 | tbody td:first-child, 134 | tfoot th:first-child, tfoot td:first-child, 135 | thead th:first-child, thead td:first-child, { 136 | padding-left: 0.9375em; 137 | } 138 | 139 | tbody td:last-child, 140 | tfoot th:last-child, tfoot td:last-child, 141 | thead th:last-child, thead td:last-child, { 142 | padding-right: 0.9375em; 143 | } 144 | } 145 | 146 | .alert { 147 | border-radius: $border-radius-base; 148 | position: relative; 149 | 150 | &.alert-with-icon { 151 | padding-left: 4.0625em; 152 | } 153 | } 154 | .icon-big { 155 | font-size: 2.7rem; 156 | margin-left: 0.625rem; 157 | } 158 | .numbers { 159 | font-size: 1.5rem; 160 | text-align: right; 161 | p { 162 | font-size: 0.875rem; 163 | margin: 0; 164 | } 165 | } 166 | ul.team-members { 167 | li { 168 | padding: 0.625em 0em; 169 | &:not(:last-child) { 170 | border-bottom: 0.0625em solid $medium-pale-bg; 171 | } 172 | } 173 | } 174 | } 175 | 176 | .card-user { 177 | .image { 178 | border-radius: 0.5em 0.5em 0em 0em; 179 | height: 9.375em; 180 | position: relative; 181 | overflow: hidden; 182 | 183 | img { 184 | width: 100%; 185 | } 186 | } 187 | .image-plain { 188 | height: 0; 189 | margin-top: 6.875em; 190 | } 191 | .author { 192 | text-align: center; 193 | text-transform: none; 194 | margin-top: -4.0625em; 195 | .title { 196 | color: $default-states-color; 197 | small { 198 | color: $muted-color; 199 | } 200 | } 201 | } 202 | .avatar { 203 | width: 8.25em; 204 | height: 8.25em; 205 | border-radius: 50%; 206 | position: relative; 207 | margin-bottom: 0.9375em; 208 | 209 | &.border-white { 210 | border: 0.3125em solid $white-color; 211 | } 212 | &.border-gray { 213 | border: 0.3125em solid $muted-color; 214 | } 215 | } 216 | .card-title { 217 | font-weight: 600; 218 | line-height: 1.5em; 219 | } 220 | .description { 221 | margin-top: 0.625em; 222 | } 223 | .card-body { 224 | min-height: 12.5em; 225 | } 226 | 227 | &.card-plain { 228 | .avatar { 229 | height: 11.875em; 230 | width: 11.875em; 231 | } 232 | } 233 | } 234 | 235 | .card-map { 236 | .map { 237 | height: 31.25em; 238 | padding-top: 1.25em; 239 | 240 | > div { 241 | height: 100%; 242 | } 243 | } 244 | } 245 | 246 | .card-user, 247 | .card-price { 248 | .card-footer { 249 | padding: 0.3125em 0.9375em 0.625em; 250 | } 251 | hr { 252 | margin: 0.3125em 0.9375em; 253 | } 254 | } 255 | 256 | .card-plain { 257 | background-color: transparent; 258 | box-shadow: none; 259 | border-radius: 0; 260 | 261 | .image { 262 | border-radius: 0.25em; 263 | } 264 | } 265 | 266 | .theme--dark .card { 267 | webkit-box-shadow: none; 268 | box-shadow: none; 269 | } -------------------------------------------------------------------------------- /frontend/src/pages/Login.vue: -------------------------------------------------------------------------------- 1 | 65 | 66 | 151 | 152 | 185 | -------------------------------------------------------------------------------- /docs/faq.md: -------------------------------------------------------------------------------- 1 | # Molly Wallet FAQ 2 | 3 | This is an attempt to address the most Frequently Asked Questions around the Molly Wallet in hopes of easing the community into leveraging the Desktop Wallet for $DAG to securely store their tokens. 4 | 5 | The Molly Wallet is still in the early phases of development with the first stable, mainnet connected build released to the general public mere days ago (at the time of writing this). This means that the way we're distributing the application, and some of the manual steps required will be improved upon with time. 6 | 7 | ## General Questions 8 | 9 | #### Q: Where can I download the Molly Wallet? 10 | **A:** https://github.com/grvlle/constellation_wallet/releases 11 | 12 | #### Q: Why can't I login to my wallet? 13 | **A:** You can always reset the Molly Wallet, simply by closing the application, removing your `.dag` folder, starting the application and re-importing your key. See the below sections under your OS of choice for how to locate the `.dag` folder. 14 | 15 | #### Q: What does the IMPORT feature do on the login screen? 16 | 17 | **A:** The Import feature exists to make it possible to access your wallet key from different computers. This only has to be done once on any computer, and it'll sync your transactions with the mainnet, and update the wallet with your previous transactions. 18 | 19 | Once imported, you will access your funds by simply login into the wallet. 20 | 21 | #### Q: How do I create a new wallet? 22 | 23 | **A:** Select the create wallet button on the login screen and it'll let you browse to a location in which you wish to store your keyfile.p12. Remember to give it a name, and select save. 24 | 25 | After that you need to provide the keyfile with authentication, populate the remaining fields and click create. Then the keyfile.p12 will be saved to the location you specified, and you can use that to access your funds through Molly Wallet. 26 | 27 | 28 | I will organize the questions based on the OS the wallet is installed upon. 29 | 30 | ## Windows 31 | 32 | #### Q: Why am I getting an error when sending transaction? 33 | 34 | **A:** This is most likely because you've used an older testnet build of the wallet with testnet artifacts interfering with your mainnet wallet. In order to fix this you need to remove the `.dag` folder that is located in your `C:/Users//` directory. Once done, feel free to import your wallet again and you should be good to go. 35 | 36 | #### Q: I am getting `Unable to detect your Java path, make sure Java has been installed` when trying to login or create/import a wallet. 37 | 38 | ***A:*** This is either because [OpenJDK v9](https://java.com/) and [JRE](https://www.oracle.com/java/technologies/javase-jre8-downloads.html) hasn't been installed, or because the enviornment paths haven't been set up correctly. 39 | 40 | For Molly Wallet to be able to detect the installation directory of Java, the `JAVA_HOME` enviornmental variable needs t 41 | o be set. The Java path also has to be included in the `Path` variable. 42 | 43 | Search for enviornment variables in windows search and select *Edit the system enviornment variables*, then set the JAVA 44 | paths like in the below image. 45 | 46 | [![env](https://i.ibb.co/Br1M31s/envvars.png)](https://constellationnetwork.io/technology/molly-wallet/) 47 | 48 | #### Q: How to open a ``.rar`` file? 49 | 50 | [![IMAGE ALT TEXT HERE](https://img.youtube.com/vi/dr0g_Ux7_8M/0.jpg)](https://www.youtube.com/watch?v=dr0g_Ux7_8M) 51 | 52 | #### Q: I am running Windows 7 (or earlier) - can I run Molly Wallet? 53 | 54 | **A:** The answer is maybe. It's not officially supported, so probably not. 55 | 56 | ## MacOS 57 | 58 | #### Q: Why am I getting an error when sending transaction? 59 | 60 | **A:** This is most likely because you've used an older testnet build of the wallet with testnet artifacts interfering with your mainnet wallet. In order to fix this you need to remove the `.dag` folder that is located in your `$HOME` (if you don't know what this is, see [this article](https://www.cnet.com/how-to/how-to-find-your-macs-home-folder-and-add-it-to-finder/)) path. Once done, feel free to import your wallet again and you should be good to go. 61 | 62 | #### Q: Why am I getting `The application 'Molly - Constellation Desktop Wallet` can't be opened? 63 | 64 | ![env](https://i.ibb.co/VWw30HN/a123555f-0881-4ae8-9b1d-7dd36d4d6802.jpg) 65 | 66 | 67 | **A:** This is because the application is compressed and archived. You need to use a software called [Unarchiver](https://theunarchiver.com/). 68 | 69 | After you've downloaded that, right click on the .zip file and select "open with" and then choose "Unarchiver" instead of the default program. It'll unzip and you'll be able to run it. 70 | 71 | #### Q: Why is my Molly Wallet download not downloaded as a .zip file? 72 | 73 | **A:** On some systems, Safari will automatically unzip the contents. We do not want that, we want to leverage the [Unarchiver](https://theunarchiver.com/). Here's a guide [how to disable automatic unzipping](https://www.addictivetips.com/mac-os/stop-automatically-unzipping-files-in-safari/) of files in Safari. Once that has been disabled, redownload Molly Wallet from the [official website](https://constellationnetwork.io/technology/molly-wallet/) and use [Unarchiver](https://theunarchiver.com/) to extract the contents. 74 | 75 | ## Linux 76 | #### Q: Why does it say `Alias not found` when trying to import/login/create wallet? 77 | 78 | **A:** This is probably due to Java missing on the system. For now, I've only implemented means of detecting that on Windows. So please, if you're using a debian based distribution, download `openjdk-8` from aptitude. 79 | 80 | #### Q: Why am I getting an error when sending transaction? 81 | 82 | **A:** This is most likely because you've used an older testnet build of the wallet with testnet artifacts interfering with your mainnet wallet. In order to fix this you need to remove the `.dag` folder that is located in your `$HOME` path. Once done, feel free to import your wallet again and you should be good to go. 83 | 84 | ## Java 85 | 86 | #### Q: Why is Java needed? 87 | 88 | **A:** The wallet binary that I'm integrating Molly Wallet against has been built by the Constellation Core team in a programming language called *Scala*. This is a functional programming language that runs on JVM (Java virtual machine). Thus Java becomes a dependency. 89 | 90 | -------------------------------------------------------------------------------- /frontend/src/assets/sass/paper/_buttons.scss: -------------------------------------------------------------------------------- 1 | .btn{ 2 | box-sizing: border-box; 3 | border-width: $border-thick; 4 | font-size: $font-size-small; 5 | font-weight: $font-weight-bold; 6 | padding: .5rem $padding-base-horizontal; 7 | line-height: 1.75; 8 | cursor: pointer; 9 | text-transform: uppercase; 10 | 11 | &.btn-border, 12 | &.btn-link{ 13 | background-color: $transparent-bg; 14 | } 15 | 16 | @include btn-styles($default-color, $default-states-color); 17 | @include transition($fast-transition-time, linear); 18 | 19 | &:hover, 20 | &:focus{ 21 | outline: 0 !important; 22 | @include box-shadow(none); 23 | } 24 | &:active, 25 | &.active, 26 | .open > &.dropdown-toggle { 27 | @include box-shadow(none); 28 | outline: 0 !important; 29 | } 30 | 31 | &[class*="btn-outline-"]{ 32 | background-image: none; 33 | background-color: transparent; 34 | } 35 | } 36 | .btn-just-icon{ 37 | border-radius: 0.25em; 38 | height: 1.875em; 39 | width: 1.875em; 40 | min-width: 1.875em; 41 | padding: 0em; 42 | margin-top: 0.625em; 43 | 44 | &.btn-sm{ 45 | padding: 0.25em !important; 46 | } 47 | i{ 48 | font-size: $font-size-medium; 49 | padding: 0.125em 0em; 50 | } 51 | } 52 | .upgrade-pro{ 53 | .btn{ 54 | margin-top: 1.875em; 55 | } 56 | } 57 | .btn-link.btn-just-icon{ 58 | padding: 0.5em; 59 | } 60 | 61 | .btn-group .btn + .btn, 62 | .btn-group .btn + .btn-group, 63 | .btn-group .btn-group + .btn, 64 | .btn-group .btn-group + .btn-group{ 65 | margin-left: -0.125em; 66 | } 67 | 68 | 69 | // Apply the mixin to the buttons 70 | .btn-primary { @include btn-styles($primary-color, $primary-states-color); } 71 | .btn-success { @include btn-styles($success-color, $success-states-color); } 72 | .btn-info { @include btn-styles($info-color, $info-states-color); } 73 | .btn-warning { @include btn-styles($warning-color, $warning-states-color); } 74 | .btn-danger { @include btn-styles($danger-color, $danger-states-color); } 75 | .theme--dark .btn-primary { @include btn-styles($primary-color-dark, $primary-states-color-dark); } 76 | .theme--dark .btn-success { @include btn-styles($success-color-dark, $success-states-color-dark); } 77 | .theme--dark .btn-info { @include btn-styles($info-color-dark, $info-states-color-dark); } 78 | 79 | .btn-outline-default { @include btn-outline-styles($default-color, $default-states-color); } 80 | .btn-outline-primary { @include btn-outline-styles($primary-color, $primary-states-color); } 81 | .btn-outline-success { @include btn-outline-styles($success-color, $success-states-color); } 82 | .btn-outline-info { @include btn-outline-styles($info-color, $info-states-color); } 83 | .btn-outline-warning { @include btn-outline-styles($warning-color, $warning-states-color); } 84 | .btn-outline-danger { @include btn-outline-styles($danger-color, $danger-states-color); } 85 | .theme--dark .btn-outline-primary { @include btn-outline-styles($primary-color-dark, $primary-states-color-dark); } 86 | .theme--dark .btn-outline-success { @include btn-outline-styles($success-color-dark, $success-states-color-dark); } 87 | .theme--dark .btn-outline-info { @include btn-outline-styles($info-color-dark, $info-states-color-dark); } 88 | 89 | .btn-neutral { @include btn-styles($white-color, $default-states-color); } 90 | .btn-outline-neutral { @include btn-outline-styles($white-color, $default-states-color); 91 | &:hover, 92 | &:focus{ 93 | color: $default-states-color; 94 | background-color: $white-color; 95 | } 96 | } 97 | .btn-neutral { 98 | @include btn-styles($white-color, $white-color); 99 | color: $default-color; 100 | &:hover, 101 | &:focus{ 102 | color: $default-states-color; 103 | } 104 | 105 | &.btn-border{ 106 | &:hover, 107 | &:focus{ 108 | color: $default-color; 109 | } 110 | 111 | &:active, 112 | &.active, 113 | .open > &.dropdown-toggle{ 114 | background-color: $white-color; 115 | color: $default-color; 116 | } 117 | } 118 | 119 | &.btn-link:active, 120 | &.btn-link.active{ 121 | background-color: transparent; 122 | } 123 | } 124 | 125 | .btn{ 126 | &:disabled, 127 | &[disabled], 128 | &.disabled{ 129 | @include opacity(.5); 130 | } 131 | } 132 | .btn-link{ 133 | border-color: transparent !important; 134 | padding: $padding-base-vertical $padding-base-horizontal; 135 | 136 | &:hover, 137 | &:focus, 138 | &:active{ 139 | text-decoration: none; 140 | border-color: transparent; 141 | } 142 | 143 | &.btn-icon{ 144 | padding: $padding-base-vertical; 145 | } 146 | } 147 | 148 | .btn-lg{ 149 | @include btn-size($padding-large-vertical, $padding-large-horizontal, $font-size-base, $line-height-small); 150 | } 151 | .btn-sm{ 152 | @include btn-size($padding-small-vertical, $padding-small-horizontal, $font-size-small, $line-height-small); 153 | } 154 | .btn-wd { 155 | min-width: 8.75em; 156 | } 157 | 158 | .btn-group.select{ 159 | width: 100%; 160 | } 161 | .btn-group.select .btn{ 162 | text-align: left; 163 | } 164 | .btn-group.select .caret{ 165 | position: absolute; 166 | top: 50%; 167 | margin-top: -0.0625em; 168 | right: 0.5em; 169 | } 170 | .btn-just-icon{ 171 | &.btn-sm{ 172 | height: 1.875em; 173 | width: 1.875em; 174 | min-width: 1.875em; 175 | padding: 0; 176 | 177 | i{ 178 | font-size: $font-size-small; 179 | 180 | } 181 | } 182 | &.btn-lg{ 183 | height: 3.125em; 184 | width: 3.125em; 185 | min-width: 3.125em; 186 | padding: 0.8125em; 187 | 188 | i{ 189 | font-size: 1.125rem; 190 | padding: 0; 191 | } 192 | 193 | } 194 | 195 | } 196 | .btn-round{ 197 | border-radius: 1.875em; 198 | } 199 | .btn.btn-link:focus{ 200 | box-shadow: none !important; 201 | text-decoration: none; 202 | } 203 | 204 | .column .btn-link{ 205 | padding: 0.4375em 0; 206 | } 207 | .share-buttons .btn-outline-default{ 208 | margin-top: 1.5em; 209 | } 210 | #modals .btn-outline-neutral{ 211 | margin-bottom: 0.625em; 212 | } 213 | .btn-group.select{ 214 | overflow: visible !important; 215 | } 216 | .media{ 217 | .media-body{ 218 | .media-footer{ 219 | .btn-neutral{ 220 | margin: $navbar-margin-btn; 221 | font-size: $font-size-base; 222 | i{ 223 | margin-right: 0 !important; 224 | } 225 | } 226 | } 227 | } 228 | } 229 | -------------------------------------------------------------------------------- /backend/settings.go: -------------------------------------------------------------------------------- 1 | package app 2 | 3 | import ( 4 | "image" 5 | "image/jpeg" 6 | "io" 7 | "os" 8 | "strings" 9 | ) 10 | 11 | // UploadImage will forward the image path of the selected image. 12 | func (a *WalletApplication) UploadImage() string { 13 | filePath := a.RT.Dialog.SelectFile() 14 | splitPath := strings.Split(filePath, "/") 15 | filename := splitPath[len(splitPath)-1] 16 | 17 | a.log.Info("Path to user uploaded image: " + filePath) 18 | err := CopyFile(filePath, a.paths.ImageDir+filename) 19 | if err != nil && filePath != "" { 20 | a.log.Errorln("Unable to copy image. ", err) 21 | a.sendError("Unable to change Image. ", err) 22 | return "None" 23 | } 24 | 25 | file, err := os.Open(filePath) 26 | if err != nil && filePath != "" { 27 | a.log.Errorln("Unable to open image. ", err) 28 | a.sendError("Unable to find Image on the path provided. ", err) 29 | return "None" 30 | } 31 | defer file.Close() 32 | 33 | img, _, err := image.DecodeConfig(file) 34 | if err != nil { 35 | a.log.Info("Attempting to decode as JPEG") 36 | img, err = jpeg.DecodeConfig(file) 37 | if err != nil { 38 | a.log.Errorln("Unable to decode image configuration", err) 39 | a.sendError("Unable to change Image. ", err) 40 | return "None" 41 | } 42 | } 43 | 44 | a.log.Info("Uploaded image resolution is set to ", img.Height, "x", img.Width) 45 | 46 | if img.Height >= 201 || img.Width >= 201 { 47 | a.log.Warnf("Image resolution is too big. Cannot be bigger than 200x200 ") 48 | 49 | return "None" 50 | } 51 | a.StoreImagePathInDB(filename) 52 | return filename 53 | } 54 | 55 | // GetImagePath is called from the Login.Vue. It'll query the DB for the user's profile picture 56 | // and return it to the FE to be displayed. 57 | func (a *WalletApplication) GetImagePath() string { 58 | if err := a.DB.Model(&a.wallet).Where("wallet_alias = ?", a.wallet.WalletAlias).Error; err != nil { 59 | a.log.Errorln("Unable to query the DB record for the Image path. Reason: ", err) 60 | a.sendError("Unable to query the DB record for the Image path. Reason: ", err) 61 | return "" 62 | } 63 | a.log.Infoln("Profile Picture selected: ", a.wallet.ProfilePicture) 64 | return a.wallet.ProfilePicture 65 | } 66 | 67 | // StoreImagePathInDB stores the path to where the profile picture is located in the database 68 | func (a *WalletApplication) StoreImagePathInDB(path string) bool { 69 | if err := a.DB.Model(&a.wallet).Where("wallet_alias = ?", a.wallet.WalletAlias).Update("ProfilePicture", path).Error; err != nil { 70 | a.log.Errorln("Unable to update the DB record with the Image path. Reason: ", err) 71 | a.sendError("Unable to update the DB record with the Image path. Reason: ", err) 72 | return false 73 | } 74 | return true 75 | } 76 | 77 | // GetWalletTag is called from the Login.Vue 78 | func (a *WalletApplication) GetWalletTag() string { 79 | if err := a.DB.Model(&a.wallet).Where("wallet_alias = ?", a.wallet.WalletAlias).Error; err != nil { 80 | a.log.Errorln("Unable to query the DB record for the Image path. Reason: ", err) 81 | a.sendError("Unable to query the DB record for the Image path. Reason: ", err) 82 | } 83 | a.log.Infoln("Wallet Tag selected: ", a.wallet.WalletTag) 84 | return a.wallet.WalletTag 85 | } 86 | 87 | // StoreWalletLabelInDB takes a wallet label string entered by a user and stores it in the database 88 | func (a *WalletApplication) StoreWalletLabelInDB(walletTag string) bool { 89 | if err := a.DB.Model(&a.wallet).Where("wallet_alias = ?", a.wallet.WalletAlias).Update("WalletTag", walletTag).Error; err != nil { 90 | a.log.Errorln("Unable to update the DB record with the wallet tag. Reason: ", err) 91 | a.sendError("Unable to update the DB record with the wallet tag. Reason: ", err) 92 | return false 93 | } 94 | return true 95 | } 96 | 97 | // GetUserTheme is called from the Login.Vue 98 | func (a *WalletApplication) GetUserTheme() bool { 99 | if err := a.DB.Model(&a.wallet).Where("wallet_alias = ?", a.wallet.WalletAlias).Error; err != nil { 100 | a.log.Errorln("Unable to query the DB record for the Image path. Reason: ", err) 101 | a.sendError("Unable to query the DB record for the Image path. Reason: ", err) 102 | } 103 | if a.wallet.DarkMode { 104 | a.log.Infoln("Dark mode enabled") 105 | } 106 | 107 | return a.wallet.DarkMode 108 | } 109 | 110 | // StoreDarkModeStateDB stores the darkmode state in the user DB 111 | func (a *WalletApplication) StoreDarkModeStateDB(darkMode bool) bool { 112 | if err := a.DB.Model(&a.wallet).Where("wallet_alias = ?", a.wallet.WalletAlias).Update("DarkMode", darkMode).Error; err != nil { 113 | a.log.Errorln("Unable to store darkmode state. Reason: ", err) 114 | a.sendError("Unable to store darkmode state persistently. Reason: ", err) 115 | return false 116 | } 117 | return true 118 | } 119 | 120 | // StoreCurrencyStateDB stores the currency state in the user DB 121 | func (a *WalletApplication) StoreCurrencyStateDB(currency string) bool { 122 | if err := a.DB.Model(&a.wallet).Where("wallet_alias = ?", a.wallet.WalletAlias).Update("Currency", currency).Error; err != nil { 123 | a.log.Errorln("Unable to store currency state. Reason: ", err) 124 | a.sendError("Unable to store currency state persistently. Reason: ", err) 125 | return false 126 | } 127 | totalCurrencyBalance := 0.0 128 | if a.wallet.Currency == "USD" { 129 | totalCurrencyBalance = float64(a.wallet.Balance) * a.wallet.TokenPrice.DAG.USD 130 | } else if a.wallet.Currency == "EUR" { 131 | totalCurrencyBalance = float64(a.wallet.Balance) * a.wallet.TokenPrice.DAG.EUR 132 | } else if a.wallet.Currency == "BTC" { 133 | totalCurrencyBalance = float64(a.wallet.Balance) * a.wallet.TokenPrice.DAG.BTC 134 | } 135 | a.RT.Events.Emit("totalValue", a.wallet.Currency, totalCurrencyBalance) 136 | return true 137 | } 138 | 139 | // UpdateMolly is called from the frontend and triggers the application update 140 | func (a *WalletApplication) UpdateMolly() { 141 | update := new(UpdateWallet) 142 | update.app = a 143 | update.Run() 144 | } 145 | 146 | // CopyFile the src file to dst. Any existing file will be overwritten and will not 147 | // copy file attributes. 148 | func CopyFile(src, dst string) error { 149 | in, err := os.Open(src) 150 | if err != nil { 151 | return err 152 | } 153 | defer in.Close() 154 | 155 | out, err := os.Create(dst) 156 | if err != nil { 157 | return err 158 | } 159 | defer out.Close() 160 | 161 | _, err = io.Copy(out, in) 162 | if err != nil { 163 | return err 164 | } 165 | return out.Close() 166 | } 167 | -------------------------------------------------------------------------------- /frontend/src/components/Cards/ChartCard.vue: -------------------------------------------------------------------------------- 1 | 28 | 29 | 228 | 229 | 231 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Molly Wallet ($DAG desktop wallet) 2 | 3 | ![N|Solid](https://i.ibb.co/QXvTVR0/mollywallet.jpg) 4 | 5 | The Molly Wallet is the official $DAG wallet of the Constellation Network. It'll let users interact with the Hypergraph Network in various ways, not limited to producing $DAG transactions. The wallet is currently under development, the first public release has been shipped with more to come. 6 | 7 | ### Here's how the Constellation Team describes it on their website 8 | 9 | >Created by a Constellation community member, for the best community in crypto, and named after the wallet designer’s daughter, Molly is built with a vision to set future standards in digital commerce. 10 | 11 | >Just like your wallet that you use daily to pay for lunch, dinner, clothes, that hold your business cards, ID’s, and that lucky penny, our intention was to design something that people would use everyday. We live in an age where we have anabundance of information, from videos to blogs, that are at our fingertips. Digital commerce is molding how generationsinteract with one another, consume, and educate themselves. The nature of this wallet is digital and thus we wanted to expand the possibilities of what a wallet could be and do because of its multimedia capabilities. We wanted to make it not only user friendly, but create the space for applications to be developed by our vibrant open source community. 12 | 13 | >A true articulation of open innovation. 14 | 15 | >A cryptocurrency wallet is one of the most cherished pieces of technology to anyone that holds cryptocurrency. Yet many wallets are visually unappealing, have limited functionality and utility, and are treated as an afterthought. We wanted to bring it to the forefront and reimagine the wallet, the community that uses the wallet, and enable a future that will be powered by DAG. 16 | 17 | [- Official Source](https://constellationnetwork.io/technology/molly-wallet/) 18 | 19 | --- 20 | 21 | ### Publications 22 | 23 | * [Constellation’s Molly a Landmark Moment for the $DAG Ecosystem](https://thedailychain.com/constellations-molly-a-landmark-moment-for-the-dag-ecosystem/) - An article written on The Daily Chain by Anna Larsen. 24 | 25 | --- 26 | 27 | ### Technologies 28 | 29 | Molly Wallet uses a number of open source projects to work properly: 30 | 31 | * [VueJS](https://vuejs.org) - The Progressive JavaScript Framework 32 | * [Go](https://golang.org) - Go is an open source programming language that makes it easy to build simple, reliable, and 33 | efficient software. 34 | * [GORM](https://gorm.io) - Object Relational Mapping for Go. 35 | * [Wails](https://wails.app/) - A framework for building desktop apps using Go & Web Technologies 36 | 37 | --- 38 | 39 | ### Installation 40 | 41 | #### Pre-requisits 42 | Molly Wallet requires [OpenJDK v9](https://java.com/) and [JRE](https://www.oracle.com/java/technologies/javase-jre8-downloads.html) to run. If installing the MacOS version, use [Unarchiver](https://theunarchiver.com/) to unzip molly_installer.zip 43 | 44 | #### Download Molly Wallet 45 | The latest builds can be found under [releases](https://github.com/grvlle/constellation_wallet/releases). 46 | 47 | --- 48 | 49 | ### FAQ 50 | The Molly Wallet FAQ can be located under [docs](https://github.com/grvlle/constellation_wallet/blob/develop/docs/faq.md#molly-wallet-faq). 51 | 52 | --- 53 | 54 | ### Development Enviornment 55 | 56 | #### 1. Download the Go distribution from the official website. 57 | 58 | The Go distribution and tooling is available as an installer for all common operating systems. Visit to download to correct version for your OS. The installation instructions can be found 59 | [here](https://golang.org/doc/install). 60 | 61 | #### 2. Download and install NPM. 62 | 63 | NPM and Node.js can be downloaded from their [official website](https://nodejs.org/en/download/). Simply select your 64 | distribution/OS and CPU architecture. 65 | 66 | #### 3. Install Wails 67 | 68 | The Molly Wallet is build using a light-weight framework for Desktop Applications using Go and VueJS. [Wails](https://github.com/wailsapp/wails) is very similar to Electron but is not packaging the full Chromium web browser as a dependency. 69 | 70 | 71 | ###### MacOS 72 | 73 | Make sure you have the xcode command line tools installed. This can be done by running: 74 | `xcode-select --` 75 | 76 | 77 | ###### Debian/Ubuntu 78 | 79 | `sudo apt install libgtk-3-dev libwebkit2gtk-4.0-dev` 80 | 81 | _Debian: 8, 9, 10_ 82 | 83 | _Ubuntu: 16.04, 18.04, 19.04_ 84 | 85 | _Also succesfully tested on: Zorin 15, Parrot 4.7, Linuxmint 19, Elementary 5, Kali, Neon_ 86 | 87 | ###### Arch Linux 88 | 89 | `sudo pacman -S webkit2gtk gtk3` 90 | 91 | _Also succesfully test on: Manjaro & ArcoLinux_ 92 | 93 | ###### Centos 94 | 95 | `sudo yum install webkitgtk3-devel gtk3-devel` 96 | 97 | _CentOS 6, 7_ 98 | 99 | ###### Fedora 100 | 101 | `sudo yum install webkit2gtk3-devel gtk3-devel` 102 | 103 | _Fedora 29, 30_ 104 | 105 | ###### VoidLinux & VoidLinux-musl 106 | 107 | `xbps-install gtk+3-devel webkit2gtk-devel` 108 | 109 | ###### Gentoo 110 | 111 | `sudo emerge gtk+:3 webkit-gtk` 112 | 113 | ###### Windows 114 | 115 | Windows requires gcc and related tooling. The recommended download is from [http://tdm-gcc.tdragon.net/download](http://tdm-gcc.tdragon.net/download). Once this is installed, you are good to go. 116 | 117 | 118 | 119 | **Ensure Go modules are enabled: GO111MODULE=on and go/bin is in your PATH variable.** 120 | 121 | Installation is as simple as running the following command: 122 | 123 | `go get -u github.com/wailsapp/wails/cmd/wails` 124 | 125 | 126 | ##### 4. Clone this repository into your GOPATH 127 | 128 | `git clone https://github.com/grvlle/constellation_wallet.git` 129 | 130 | --- 131 | 132 | ### Want to contribute? Great! 133 | 134 | 135 | Molly Wallet uses Wails + Webpack for fast frontend development. 136 | Make a change in your file and instantaneously see your updates! 137 | 138 | Open your favorite Terminal and run these commands. 139 | 140 | In the constellation_wallet directory, run: 141 | ```sh 142 | $ wails serve 143 | ``` 144 | 145 | In the frontend directory, run: 146 | ```sh 147 | $ npm run serve 148 | ``` 149 | 150 | ##### Alternatively: 151 | If you wish to compile the wallet, simply run: 152 | ```sh 153 | wails build 154 | ``` 155 | or for the debug version, run: 156 | ```sh 157 | wails build -d 158 | ``` 159 | 160 | --- 161 | 162 | ### Top Contributers 163 | * [digitaltwin](https://github.com/digitaltwinnn) 164 | * [Marcin Wadoń](https://github.com/marcinwadon) 165 | * [junkai121](https://github.com/junkai121) 166 | 167 | --- 168 | --------------------------------------------------------------------------------