├── .babelrc ├── .buildrc ├── .eslintrc ├── .github ├── CONTRIBUTING.md ├── ISSUE_TEMPLATE.md └── PULL_REQUEST_TEMPLATE.md ├── .gitignore ├── .prettierrc ├── .travis.yml ├── LICENSE.md ├── README.md ├── build ├── bin │ └── README.md ├── icon.icns ├── icon.ico └── icons │ ├── 128x128.png │ ├── 16x16.png │ ├── 256x256.png │ ├── 32x32.png │ ├── 48x48.png │ └── 96x96.png ├── package-lock.json ├── package.json └── src ├── assets ├── css │ └── default.css ├── images │ ├── exchangeBittrex.png │ ├── exchangePoloniex.png │ ├── logoGrey.png │ └── logoRed.png └── sounds │ ├── incoming.mp3 │ └── spendable.mp3 ├── components ├── Footer.js ├── Header.js ├── Root.js ├── charts │ ├── CustomRecharts.js │ ├── DailyRewardChart.js │ ├── DailyTotalsChart.js │ ├── DifficultyChart.js │ ├── HashRateChart.js │ └── RewardSpreadChart.js ├── lists │ ├── AddressList.js │ ├── AddressListItem.js │ ├── AddressOutputList.js │ ├── AddressOutputListItem.js │ ├── ConnectionList.js │ ├── ConnectionListItem.js │ ├── RecentBlockList.js │ ├── RecentBlockListItem.js │ ├── RecipientList.js │ ├── RecipientListItem.js │ ├── SpendOutputList.js │ ├── SpendOutputListItem.js │ ├── TransactionIoList.js │ ├── TransactionIoListItem.js │ ├── TransactionList.js │ └── TransactionListItem.js ├── menus │ ├── MainMenu.js │ ├── MaintenanceMenu.js │ └── NetworkMenu.js ├── screens │ ├── Connections.js │ ├── Maintenance.js │ ├── Network.js │ ├── Send.js │ └── Transactions.js ├── search │ ├── AccountFilter.js │ ├── AddressFilter.js │ ├── AddressFind.js │ ├── TransactionFilter.js │ └── TransactionFind.js ├── send │ ├── SendControls.js │ ├── SendOptions.js │ ├── SendTotal.js │ └── SpendBalance.js ├── settings │ ├── SetConnection.js │ ├── SetCurrency.js │ ├── SetLanguage.js │ ├── SetLocaleSettings.js │ ├── SetRates.js │ └── SetSoundAlerts.js ├── utilities │ ├── Common.js │ ├── Console.js │ ├── CurrencyConverter.js │ └── RewardCalculator.js └── wallet │ ├── Address.js │ ├── AddressGet.js │ ├── ChainBlender.js │ ├── IncentiveInfo.js │ ├── Message.js │ ├── NetworkInfo.js │ ├── Peers.js │ ├── PrivateKeyDump.js │ ├── PrivateKeyImport.js │ ├── Transaction.js │ ├── WalletBackup.js │ ├── WalletDump.js │ ├── WalletEncrypt.js │ ├── WalletLock.js │ ├── WalletPassphraseChange.js │ ├── WalletRepair.js │ ├── WalletSeedDump.js │ └── WalletUnlock.js ├── electron.js ├── index.html ├── index.js ├── locales ├── en-US │ └── common.json ├── es-ES │ └── common.json ├── fr-FR │ └── common.json ├── pt-BR │ └── common.json ├── ru-RU │ └── common.json └── sl-SI │ └── common.json ├── stores ├── connection.js ├── connections.js ├── console.js ├── daemon.js ├── gui.js ├── rates.js ├── rpc.js ├── search.js ├── send.js ├── statistics.js └── wallet.js └── utilities ├── blockRewards.js ├── common.js ├── constants.js ├── i18next.js ├── localStorage.js ├── rightClickMenu.js └── rpcs.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "stage-0", 4 | "react", 5 | ["env", { 6 | "targets": { 7 | "electron": "1.8.2", 8 | "node": "current" 9 | } 10 | }] 11 | ], 12 | "plugins": ["transform-decorators-legacy"] 13 | } 14 | -------------------------------------------------------------------------------- /.buildrc: -------------------------------------------------------------------------------- 1 | { 2 | "appId": "vcash.electron.gui", 3 | "asarUnpack": ["build/bin"], 4 | "files": ["lib/**/*"], 5 | "linux": { 6 | "category": "Network", 7 | "target": ["deb", "zip"] 8 | }, 9 | "mac": { 10 | "category": "public.app-category.finance", 11 | "target": "dmg" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["standard", "standard-jsx"], 3 | "globals": { 4 | "Audio": true 5 | }, 6 | "rules": { 7 | "prettier/prettier": "error", 8 | "space-before-function-paren": "off", 9 | "indent": "off", 10 | "jsx-quotes": "off" 11 | }, 12 | "parser": "babel-eslint", 13 | "plugins": ["prettier"], 14 | "root": true 15 | } 16 | -------------------------------------------------------------------------------- /.github/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | :deciduous_tree: Thank you for taking the time to help and improve the GUI! 3 | 4 | If you've noticed a bug or have a question, search the 5 | [issue tracker](https://github.com/openvcash/vcash-electron/issues) to see if 6 | someone else in the community has already submitted an issue. If not, go ahead 7 | and [submit one](https://github.com/openvcash/vcash-electron/issues/new)! If 8 | it's something you think you can fix, improve or implement by yourself, then 9 | [fork](https://help.github.com/articles/fork-a-repo) the repository and create 10 | a branch with a descriptive name that includes the issue number (e.g. #233). 11 | 12 | git checkout -b 233-add-chinese-translations 13 | 14 | Next, install [Node.js](https://nodejs.org/en/download/current/) dependencies 15 | in the local clone of your fork using `npm install`. After the dependency 16 | installation is completed, start `npm run babel-watch` to auto-transform the 17 | source code on any changes to the files in the `src/` directory. 18 | 19 | npm install 20 | npm run babel-watch 21 | 22 | You can now run the GUI using `npm start`. Depending on the size of your 23 | changes you might also require development tools which are enabled when using 24 | `npm run dev` or `npm run dev-win` on Windows. 25 | 26 | npm start 27 | npm run dev (Linux and macOS) 28 | npm run dev-win (Windows) 29 | 30 | Then [download](https://vcash.info) and launch the latest daemon, or skip this 31 | step if you've already got a daemon or GUI bundled with a daemon running. 32 | This ensures the wallet will keep running while you restart the GUI to look at 33 | the changes you've made. 34 | 35 | You're now ready to implement your fix or feature. Make sure that your code 36 | lints by using `npm run lint` and to format it using `npm run format` before 37 | creating the pull request. 38 | 39 | npm run lint 40 | npm run format 41 | 42 | ### Code style 43 | This repository uses [prettier](https://github.com/prettier/prettier) and 44 | [standard](https://standardjs.com/) to maintain code style and consistency, 45 | and to avoid bike-shedding. 46 | 47 | ### Add new translation 48 | Follow and complete the steps above, then create a copy of the `en-US/` 49 | directory in `src/locales/` and construct the first part of the copied 50 | directory name by using the `ISO 639-1 Code` 51 | [language code](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) 52 | and the second part by using the `ISO 3166 Alpha-2 code` 53 | [country code](https://en.wikipedia.org/wiki/ISO_3166-1#Current_codes). 54 | Delimit the two parts with a dash `-`. 55 | 56 | The final step is to open `src/stores/gui.js` and add your translation to the 57 | [languages](https://github.com/openvcash/vcash-electron/blob/master/src/stores/gui.js#L20-L25) 58 | array. Please add it in alphabetical order (by name) and use 6 spaces to indent 59 | the line. 60 | 61 | { language: 'languageCode-countryCode', name: 'Language' }, 62 | 63 | You can now select your translation in the GUI and begin translating the 64 | strings in `common.json`. 65 | 66 | **Note:** Please use an editor that will open and save files in UTF-8 67 | (e.g. [Atom](https://atom.io/)). 68 | 69 | ### Create a pull request 70 | You should now switch back to your master branch and make sure it's up-to-date 71 | with the upstream master branch. 72 | 73 | git remote add upstream git@github.com:openvcash/vcash-electron.git 74 | git checkout master 75 | git pull upstream master 76 | 77 | Then update your feature branch from your local copy of master, and push it. 78 | 79 | git checkout 233-add-chinese-translations 80 | git rebase master 81 | git push --set-upstream origin 233-add-chinese-translations 82 | 83 | Finally, go to [GitHub](https://github.com/openvcash/vcash-electron) and create 84 | a [pull request](https://help.github.com/articles/creating-a-pull-request). 85 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ##### Please fill in all relevant items: 4 | - [ ] I have read README.md 5 | - [ ] I have tried with the latest version and I can still reproduce the issue 6 | 7 | ##### Vcash-electron version/branch+revision: 8 | 9 | ##### Operating system and version: 10 | 11 | ##### Expected behavior: 12 | 13 | ##### Actual behavior: 14 | 15 | ##### Steps to reproduce the behavior: 16 | 17 | ##### Relevant log file output: 18 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | **Before submitting a pull request**, please make sure the following is done: 2 | 3 | 1. Make sure you read the [contributing guide](https://github.com/openvcash/vcash-electron/blob/master/.github/CONTRIBUTING.md) before making any changes. 4 | 2. Fork [the repository](https://github.com/openvcash/vcash-electron) and create your branch from `master`, making sure to give your branch a descriptive name. 5 | 3. If you've changed any APIs, update the documentation. 6 | 4. Format the code with [prettier](https://github.com/prettier/prettier) and lint it with [standard](https://standardjs.com/) (`npm run format`). 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | dist/ 2 | lib/ 3 | node_modules/ 4 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "semi": false, 3 | "singleQuote": true 4 | } 5 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | os: 3 | - linux 4 | - osx 5 | # Use latest stable node.js release 6 | node_js: 7 | - "node" 8 | cache: 9 | directories: 10 | - "node_modules" 11 | install: 12 | - npm install 13 | - npm prune 14 | - npm run babel 15 | script: 16 | - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then npm run build-macos; else npm run build-linux; fi 17 | # - npm test 18 | notifications: 19 | webhooks: 20 | urls: 21 | - https://webhooks.gitter.im/e/9dde211a80a8d5400510 22 | on_success: change # options: [always|never|change] default: always 23 | on_failure: always # options: [always|never|change] default: always 24 | on_start: never # options: [always|never|change] default: always 25 | -------------------------------------------------------------------------------- /build/bin/README.md: -------------------------------------------------------------------------------- 1 | # Launch the daemon on startup 2 | [Download](https://vcash.info/) the latest daemon for your platform 3 | to this directory and rename it to `vcashd-ia32` or `vcashd-x64`, depending on 4 | your arch. This directory gets bundled with the GUI when you run any of the 5 | `npm run build-*` scripts and is checked on startup by 6 | [daemon.js](https://github.com/openvcash/vcash-electron/blob/master/src/stores/daemon.js) 7 | which launches the daemon if it matches the correct platform and arch. 8 | -------------------------------------------------------------------------------- /build/icon.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whphhg/vcash-electron/df568af0a65742e8f5341cf9e2694494684d51a9/build/icon.icns -------------------------------------------------------------------------------- /build/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whphhg/vcash-electron/df568af0a65742e8f5341cf9e2694494684d51a9/build/icon.ico -------------------------------------------------------------------------------- /build/icons/128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whphhg/vcash-electron/df568af0a65742e8f5341cf9e2694494684d51a9/build/icons/128x128.png -------------------------------------------------------------------------------- /build/icons/16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whphhg/vcash-electron/df568af0a65742e8f5341cf9e2694494684d51a9/build/icons/16x16.png -------------------------------------------------------------------------------- /build/icons/256x256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whphhg/vcash-electron/df568af0a65742e8f5341cf9e2694494684d51a9/build/icons/256x256.png -------------------------------------------------------------------------------- /build/icons/32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whphhg/vcash-electron/df568af0a65742e8f5341cf9e2694494684d51a9/build/icons/32x32.png -------------------------------------------------------------------------------- /build/icons/48x48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whphhg/vcash-electron/df568af0a65742e8f5341cf9e2694494684d51a9/build/icons/48x48.png -------------------------------------------------------------------------------- /build/icons/96x96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whphhg/vcash-electron/df568af0a65742e8f5341cf9e2694494684d51a9/build/icons/96x96.png -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vcash-electron", 3 | "productName": "Vcash Electron GUI", 4 | "version": "0.33.55", 5 | "description": "Multi-platform and multi-node GUI for Vcash.", 6 | "author": "whphhg ", 7 | "license": "GPL-3.0", 8 | "homepage": "https://github.com/openvcash/vcash-electron#readme", 9 | "main": "lib/electron.js", 10 | "scripts": { 11 | "babel": "babel --copy-files src/ --out-dir lib/", 12 | "babel-watch": "babel --copy-files src/ --out-dir lib/ --watch", 13 | "build-linux": "build --linux --x64 --config .buildrc", 14 | "build-macos": "build --mac --x64 --config .buildrc", 15 | "build-win-nsis": "build --win nsis --ia32 --x64 --config .buildrc", 16 | "build-win-ia32": "build --win portable --ia32 --config .buildrc", 17 | "build-win-x64": "build --win portable --x64 --config .buildrc", 18 | "dev": "NODE_ENV=dev electron .", 19 | "dev-win": "set NODE_ENV=dev&&electron .", 20 | "format": "prettier --write 'src/**/*.js' && eslint src/", 21 | "lint": "eslint src/", 22 | "pack": "build --dir --config .buildrc", 23 | "start": "electron ." 24 | }, 25 | "repository": { 26 | "type": "git", 27 | "url": "git+https://github.com/openvcash/vcash-electron.git" 28 | }, 29 | "bugs": { 30 | "url": "https://github.com/openvcash/vcash-electron/issues" 31 | }, 32 | "keywords": [ 33 | "vcash", 34 | "electron", 35 | "gui" 36 | ], 37 | "dependencies": { 38 | "antd": "2.13.11", 39 | "i18next": "10.5.0", 40 | "i18next-node-fs-backend": "1.0.0", 41 | "mobx": "3.5.1", 42 | "mobx-logger": "0.6.0", 43 | "mobx-react": "4.4.2", 44 | "moment": "2.20.1", 45 | "react": "15.6.2", 46 | "react-dom": "15.6.2", 47 | "react-i18next": "7.4.0", 48 | "react-list": "0.8.8", 49 | "react-router-dom": "4.2.2", 50 | "recharts": "0.22.4", 51 | "ssh2": "0.5.5" 52 | }, 53 | "devDependencies": { 54 | "babel-cli": "6.26.0", 55 | "babel-eslint": "8.2.2", 56 | "babel-plugin-transform-decorators-legacy": "1.3.4", 57 | "babel-preset-env": "1.6.1", 58 | "babel-preset-react": "6.24.1", 59 | "babel-preset-stage-0": "6.24.1", 60 | "electron": "1.8.2", 61 | "electron-builder": "20.2.0", 62 | "eslint": "4.18.1", 63 | "eslint-config-standard": "11.0.0", 64 | "eslint-config-standard-jsx": "5.0.0", 65 | "eslint-plugin-import": "2.9.0", 66 | "eslint-plugin-node": "6.0.1", 67 | "eslint-plugin-prettier": "2.6.0", 68 | "eslint-plugin-promise": "3.6.0", 69 | "eslint-plugin-react": "7.7.0", 70 | "eslint-plugin-standard": "3.0.1", 71 | "jquery": "3.3.1", 72 | "prettier": "1.10.2" 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/assets/images/exchangeBittrex.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whphhg/vcash-electron/df568af0a65742e8f5341cf9e2694494684d51a9/src/assets/images/exchangeBittrex.png -------------------------------------------------------------------------------- /src/assets/images/exchangePoloniex.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whphhg/vcash-electron/df568af0a65742e8f5341cf9e2694494684d51a9/src/assets/images/exchangePoloniex.png -------------------------------------------------------------------------------- /src/assets/images/logoGrey.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whphhg/vcash-electron/df568af0a65742e8f5341cf9e2694494684d51a9/src/assets/images/logoGrey.png -------------------------------------------------------------------------------- /src/assets/images/logoRed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whphhg/vcash-electron/df568af0a65742e8f5341cf9e2694494684d51a9/src/assets/images/logoRed.png -------------------------------------------------------------------------------- /src/assets/sounds/incoming.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whphhg/vcash-electron/df568af0a65742e8f5341cf9e2694494684d51a9/src/assets/sounds/incoming.mp3 -------------------------------------------------------------------------------- /src/assets/sounds/spendable.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whphhg/vcash-electron/df568af0a65742e8f5341cf9e2694494684d51a9/src/assets/sounds/spendable.mp3 -------------------------------------------------------------------------------- /src/components/Footer.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { translate } from 'react-i18next' 3 | import { inject, observer } from 'mobx-react' 4 | import { join } from 'path' 5 | import { version } from '../../package.json' 6 | 7 | /** Ant Design */ 8 | import Progress from 'antd/lib/progress' 9 | 10 | @translate(['common']) 11 | @inject('connections', 'gui', 'wallet') 12 | @observer 13 | class Footer extends React.Component { 14 | constructor(props) { 15 | super(props) 16 | this.t = props.t 17 | this.connections = props.connections 18 | this.gui = props.gui 19 | this.wallet = props.wallet 20 | } 21 | 22 | render() { 23 | return ( 24 | 81 | ) 82 | } 83 | } 84 | 85 | export default Footer 86 | -------------------------------------------------------------------------------- /src/components/Header.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { translate } from 'react-i18next' 3 | import { inject, observer } from 'mobx-react' 4 | 5 | /** Ant Design */ 6 | import Popover from 'antd/lib/popover' 7 | 8 | /** Components */ 9 | import WalletLock from './wallet/WalletLock.js' 10 | import WalletUnlock from './wallet/WalletUnlock.js' 11 | 12 | @translate(['common']) 13 | @inject('connections', 'gui', 'rates', 'wallet') 14 | @observer 15 | class Header extends React.Component { 16 | constructor(props) { 17 | super(props) 18 | this.t = props.t 19 | this.connections = props.connections 20 | this.gui = props.gui 21 | this.rates = props.rates 22 | this.wallet = props.wallet 23 | } 24 | 25 | render() { 26 | const { average, local } = this.rates 27 | return ( 28 | 128 | 129 | ) 130 | } 131 | } 132 | 133 | export default Header 134 | -------------------------------------------------------------------------------- /src/components/Root.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Route } from 'react-router-dom' 3 | import { inject, observer, Provider } from 'mobx-react' 4 | 5 | /** Ant Design */ 6 | import message from 'antd/lib/message' 7 | import notification from 'antd/lib/notification' 8 | 9 | /** Components */ 10 | import Maintenance from './screens/Maintenance.js' 11 | import Network from './screens/Network.js' 12 | import Send from './screens/Send.js' 13 | import Transactions from './screens/Transactions.js' 14 | 15 | /** Set message and notification top margin. */ 16 | message.config({ top: 40 }) 17 | notification.config({ top: 40 }) 18 | 19 | @inject('connections') 20 | @observer 21 | class Root extends React.Component { 22 | constructor(props) { 23 | super(props) 24 | this.connections = props.connections 25 | } 26 | 27 | render() { 28 | return ( 29 | 30 |
31 | 32 | 33 | 34 | 35 |
36 |
37 | ) 38 | } 39 | } 40 | 41 | export default Root 42 | -------------------------------------------------------------------------------- /src/components/charts/CustomRecharts.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { humanReadable } from '../../utilities/common.js' 3 | import i18next from '../../utilities/i18next.js' 4 | import moment from 'moment' 5 | 6 | /** 7 | * Custom axis tick. 8 | * @function CustomTick 9 | */ 10 | export const CustomTick = props => { 11 | let value = '' 12 | 13 | switch (props.textType) { 14 | case 'date': 15 | value = new Date(props.payload.value).toLocaleDateString(props.language, { 16 | day: '2-digit', 17 | month: '2-digit' 18 | }) 19 | break 20 | 21 | case 'time': 22 | value = moment(props.payload.value).format('LT') 23 | break 24 | 25 | case 'hashRate': 26 | value = humanReadable(props.payload.value, true, 'H/s', props.language) 27 | break 28 | 29 | case 'number': 30 | value = new Intl.NumberFormat(props.language, { 31 | maximumFractionDigits: 2 32 | }).format(props.payload.value) 33 | break 34 | 35 | default: 36 | value = props.payload.value 37 | break 38 | } 39 | 40 | return ( 41 | 42 | 48 | {value} 49 | 50 | 51 | ) 52 | } 53 | 54 | /** 55 | * Custom tooltip. 56 | * @function CustomTooltip 57 | */ 58 | export const CustomTooltip = props => { 59 | if (props.active === false) return null 60 | 61 | switch (props.tooltipType) { 62 | case 'rewardSpread': 63 | const { amount, category, color, date } = props.payload[0].payload 64 | return ( 65 |
66 |

{i18next.t(category)}

67 |
68 |
69 |

{i18next.t('amount')}

70 |

{i18next.t('date')}

71 |
72 |
73 |

74 | {new Intl.NumberFormat(props.language, { 75 | minimumFractionDigits: 6, 76 | maximumFractionDigits: 6 77 | }).format(amount)}{' '} 78 | XVC 79 |

80 |

{moment(date).format('L - LTS')}

81 |
82 |
83 |
84 | ) 85 | 86 | default: 87 | return ( 88 |
89 |

90 | {i18next.t('statisticsFor')}{' '} 91 | {props.labelTime === true 92 | ? moment(props.label).format('LT') 93 | : moment(props.label).format('L')} 94 |

95 | {props.payload.map(entry => { 96 | return ( 97 |
98 |

99 | {i18next.t(entry.name)} 100 |

101 |

102 | {(props.hashRate === true && 103 | humanReadable(entry.value, true, 'H/s', props.language)) || 104 | (props.amounts === true && 105 | new Intl.NumberFormat(props.language, { 106 | minimumFractionDigits: 6, 107 | maximumFractionDigits: 6 108 | }).format(entry.value) + ' XVC') || 109 | (props.amounts !== true && 110 | new Intl.NumberFormat(props.language, { 111 | maximumFractionDigits: 2 112 | }).format(entry.value))} 113 |

114 |
115 | ) 116 | })} 117 |
118 | ) 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /src/components/charts/DailyRewardChart.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { inject, observer } from 'mobx-react' 3 | import { 4 | Bar, 5 | BarChart, 6 | CartesianGrid, 7 | ResponsiveContainer, 8 | Tooltip, 9 | XAxis, 10 | YAxis 11 | } from 'recharts' 12 | import moment from 'moment' 13 | 14 | /** Components */ 15 | import { CustomTick, CustomTooltip } from './CustomRecharts.js' 16 | 17 | @inject('gui', 'statistics') 18 | @observer 19 | class DailyRewardChart extends React.Component { 20 | constructor(props) { 21 | super(props) 22 | this.gui = props.gui 23 | this.statistics = props.statistics 24 | } 25 | 26 | render() { 27 | const beginning = Date.now() - 30 * 24 * 60 * 60 * 1000 28 | return ( 29 | 30 | 34 | 35 | 36 | 37 | 38 | } /> 39 | 50 | } 51 | /> 52 | 55 | } 56 | /> 57 | 58 | 59 | ) 60 | } 61 | } 62 | 63 | export default DailyRewardChart 64 | -------------------------------------------------------------------------------- /src/components/charts/DailyTotalsChart.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { translate } from 'react-i18next' 3 | import { inject, observer } from 'mobx-react' 4 | import { 5 | Area, 6 | AreaChart, 7 | CartesianGrid, 8 | ResponsiveContainer, 9 | Tooltip, 10 | XAxis, 11 | defs, 12 | linearGradient, 13 | stop 14 | } from 'recharts' 15 | import moment from 'moment' 16 | 17 | /** Components */ 18 | import { CustomTick, CustomTooltip } from './CustomRecharts.js' 19 | 20 | @translate(['common']) 21 | @inject('gui', 'statistics') 22 | @observer 23 | class DailyTotalsChart extends React.Component { 24 | constructor(props) { 25 | super(props) 26 | this.t = props.t 27 | this.gui = props.gui 28 | this.statistics = props.statistics 29 | } 30 | 31 | render() { 32 | const beginning = Date.now() - 31 * 24 * 60 * 60 * 1000 33 | return ( 34 | 35 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 68 | 75 | 82 | 89 | 96 | 97 | } 99 | /> 100 | 111 | } 112 | /> 113 | 114 | 115 | ) 116 | } 117 | } 118 | 119 | export default DailyTotalsChart 120 | -------------------------------------------------------------------------------- /src/components/charts/DifficultyChart.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { translate } from 'react-i18next' 3 | import { inject, observer } from 'mobx-react' 4 | import { 5 | CartesianGrid, 6 | LineChart, 7 | Line, 8 | ResponsiveContainer, 9 | Tooltip, 10 | XAxis, 11 | YAxis 12 | } from 'recharts' 13 | 14 | /** Components */ 15 | import { CustomTick, CustomTooltip } from './CustomRecharts.js' 16 | 17 | @translate(['common']) 18 | @inject('gui', 'statistics') 19 | @observer 20 | class DifficultyChart extends React.Component { 21 | constructor(props) { 22 | super(props) 23 | this.t = props.t 24 | this.gui = props.gui 25 | this.statistics = props.statistics 26 | } 27 | 28 | render() { 29 | return ( 30 | 31 | 36 | 37 | 44 | 51 | } 53 | /> 54 | 64 | } 65 | /> 66 | 75 | } 76 | yAxisId="left" 77 | /> 78 | 88 | } 89 | yAxisId="right" 90 | /> 91 | 92 | 93 | ) 94 | } 95 | } 96 | 97 | export default DifficultyChart 98 | -------------------------------------------------------------------------------- /src/components/charts/HashRateChart.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { translate } from 'react-i18next' 3 | import { inject, observer } from 'mobx-react' 4 | import { 5 | AreaChart, 6 | Area, 7 | CartesianGrid, 8 | ResponsiveContainer, 9 | Tooltip, 10 | XAxis, 11 | YAxis, 12 | defs, 13 | linearGradient, 14 | stop 15 | } from 'recharts' 16 | 17 | /** Components */ 18 | import { CustomTick, CustomTooltip } from './CustomRecharts.js' 19 | 20 | @translate(['common']) 21 | @inject('gui', 'statistics') 22 | @observer 23 | class HashRateChart extends React.Component { 24 | constructor(props) { 25 | super(props) 26 | this.t = props.t 27 | this.gui = props.gui 28 | this.statistics = props.statistics 29 | } 30 | 31 | render() { 32 | return ( 33 | 34 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 53 | 54 | 57 | } 58 | /> 59 | 69 | } 70 | /> 71 | 80 | } 81 | yAxisId="left" 82 | /> 83 | 84 | 85 | ) 86 | } 87 | } 88 | 89 | export default HashRateChart 90 | -------------------------------------------------------------------------------- /src/components/charts/RewardSpreadChart.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { translate } from 'react-i18next' 3 | import { inject, observer } from 'mobx-react' 4 | import { 5 | CartesianGrid, 6 | ResponsiveContainer, 7 | Scatter, 8 | ScatterChart, 9 | Tooltip, 10 | XAxis, 11 | YAxis 12 | } from 'recharts' 13 | import moment from 'moment' 14 | 15 | /** Components */ 16 | import { CustomTick, CustomTooltip } from './CustomRecharts.js' 17 | 18 | @translate(['common']) 19 | @inject('gui', 'statistics') 20 | @observer 21 | class RewardSpreadChart extends React.Component { 22 | constructor(props) { 23 | super(props) 24 | this.t = props.t 25 | this.gui = props.gui 26 | this.statistics = props.statistics 27 | } 28 | 29 | render() { 30 | const beginning = Date.now() - 30 * 24 * 60 * 60 * 1000 31 | return ( 32 | 33 | 34 | 35 | 40 | 45 | 50 | 56 | } 57 | /> 58 | 69 | } 70 | ticks={[ 71 | Math.round(beginning), 72 | Math.round( 73 | moment(beginning) 74 | .add(6, 'days') 75 | .format('x') 76 | ), 77 | Math.round( 78 | moment(beginning) 79 | .add(11, 'days') 80 | .format('x') 81 | ), 82 | Math.round( 83 | moment(beginning) 84 | .add(16, 'days') 85 | .format('x') 86 | ), 87 | Math.round( 88 | moment(beginning) 89 | .add(21, 'days') 90 | .format('x') 91 | ), 92 | Math.round( 93 | moment(beginning) 94 | .add(26, 'days') 95 | .format('x') 96 | ), 97 | Math.round(moment().format('x')) 98 | ]} 99 | /> 100 | 111 | } 112 | ticks={[0, 21600000, 43200000, 64800000, 86400000]} 113 | /> 114 | 115 | 116 | ) 117 | } 118 | } 119 | 120 | export default RewardSpreadChart 121 | -------------------------------------------------------------------------------- /src/components/lists/AddressList.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import List from 'react-list' 3 | import { translate } from 'react-i18next' 4 | import { inject, observer } from 'mobx-react' 5 | 6 | /** Component */ 7 | import AddressListItem from './AddressListItem.js' 8 | 9 | @translate(['common']) 10 | @inject('gui', 'search', 'wallet') 11 | @observer 12 | class AddressList extends React.Component { 13 | constructor(props) { 14 | super(props) 15 | this.t = props.t 16 | this.gui = props.gui 17 | this.search = props.search 18 | this.wallet = props.wallet 19 | } 20 | 21 | render() { 22 | return ( 23 |
24 | ( 27 | 35 | )} 36 | /> 37 |
38 | ) 39 | } 40 | } 41 | 42 | export default AddressList 43 | -------------------------------------------------------------------------------- /src/components/lists/AddressListItem.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { observer } from 'mobx-react' 3 | 4 | const AddressListItem = observer(props => { 5 | const addr = props.wallet.addr.get(props.search.addr[props.index]) 6 | return ( 7 |
props.wallet.setViewing('addr', addr.address)} 14 | > 15 |
16 |

20 | {addr.address} 21 |

22 |
23 |
0 ? ' green' : '')}> 24 |

25 | {new Intl.NumberFormat(props.gui.language, { 26 | minimumFractionDigits: 6, 27 | maximumFractionDigits: 6 28 | }).format(Math.abs(addr.balance))}{' '} 29 | XVC 30 |

31 |
32 |
33 | ) 34 | }) 35 | 36 | export default AddressListItem 37 | -------------------------------------------------------------------------------- /src/components/lists/AddressOutputList.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import List from 'react-list' 3 | import { translate } from 'react-i18next' 4 | import { inject, observer } from 'mobx-react' 5 | 6 | /** Component */ 7 | import AddressOutputListItem from './AddressOutputListItem.js' 8 | 9 | @translate(['common']) 10 | @inject('gui', 'send') 11 | @observer 12 | class AddressOutputList extends React.Component { 13 | constructor(props) { 14 | super(props) 15 | this.t = props.t 16 | this.gui = props.gui 17 | this.send = props.send 18 | } 19 | 20 | /** 21 | * Mark or unmark the output for spending. 22 | * @function setOutput 23 | * @param {object} e - Checkbox element event. 24 | */ 25 | setOutput = e => { 26 | this.send.setOutput(e.target.id) 27 | } 28 | 29 | render() { 30 | return ( 31 |
32 |
33 |

{this.t('txId')}

34 |

{this.t('amount')} (XVC)

35 |
36 |
40 | ( 43 | 50 | )} 51 | /> 52 | {this.send.addrOutputs.length === 0 && ( 53 |
54 |
55 |

{this.t('addrUnused')}

56 |
57 |
58 | )} 59 |
60 |
61 | ) 62 | } 63 | } 64 | 65 | export default AddressOutputList 66 | -------------------------------------------------------------------------------- /src/components/lists/AddressOutputListItem.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { observer } from 'mobx-react' 3 | 4 | /** Ant Design */ 5 | import Checkbox from 'antd/lib/checkbox' 6 | 7 | const AddressOutputListItem = observer(props => { 8 | const output = props.send.addrOutputs[props.index] 9 | return ( 10 |
11 |
12 |
13 | 20 | {output.txid.slice(0, 11)}:{output.vout} 21 | 22 |
23 |

24 | {new Intl.NumberFormat(props.gui.language, { 25 | minimumFractionDigits: 6, 26 | maximumFractionDigits: 6 27 | }).format(output.amount)} 28 |

29 |
30 |
31 | ) 32 | }) 33 | 34 | export default AddressOutputListItem 35 | -------------------------------------------------------------------------------- /src/components/lists/ConnectionList.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import List from 'react-list' 3 | import { translate } from 'react-i18next' 4 | import { inject, observer } from 'mobx-react' 5 | 6 | /** Component */ 7 | import ConnectionListItem from './ConnectionListItem.js' 8 | 9 | @translate(['common']) 10 | @inject('connections', 'gui') 11 | @observer 12 | class ConnectionList extends React.Component { 13 | constructor(props) { 14 | super(props) 15 | this.t = props.t 16 | this.connections = props.connections 17 | this.gui = props.gui 18 | } 19 | 20 | render() { 21 | return ( 22 |
23 | ( 26 | 33 | )} 34 | /> 35 |
36 | ) 37 | } 38 | } 39 | 40 | export default ConnectionList 41 | -------------------------------------------------------------------------------- /src/components/lists/ConnectionListItem.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { observer } from 'mobx-react' 3 | 4 | const ConnectionListItem = observer(({ t, connections, index }) => { 5 | const instance = connections.instances.get(connections.ids[index]) 6 | const { rpc } = instance.status 7 | const color = rpc === null ? '' : rpc === true ? 'green' : 'red' 8 | 9 | return ( 10 |
connections.setViewing(instance.id)} 17 | > 18 |
19 |

20 | {instance.type === 'local' ? '127.0.0.1' : instance.host}: 21 | {instance.type === 'local' ? instance.localPort : instance.port} 22 |

23 |

24 | {instance.type === 'local' ? t('local') : 'SSH'} 25 |

26 |
27 |
28 | power_settings_new 29 |

{rpc === true ? t('connected') : t('disconnected')}

30 |
31 |
32 | ) 33 | }) 34 | 35 | export default ConnectionListItem 36 | -------------------------------------------------------------------------------- /src/components/lists/RecentBlockList.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import List from 'react-list' 3 | import { translate } from 'react-i18next' 4 | import { inject, observer } from 'mobx-react' 5 | 6 | /** Component */ 7 | import RecentBlockListItem from './RecentBlockListItem.js' 8 | 9 | @translate(['common']) 10 | @inject('gui', 'statistics') 11 | @observer 12 | class RecentBlockList extends React.Component { 13 | constructor(props) { 14 | super(props) 15 | this.t = props.t 16 | this.gui = props.gui 17 | this.statistics = props.statistics 18 | } 19 | 20 | render() { 21 | return ( 22 |
23 |
24 |

{this.t('block')}

25 |

{this.t('type')}

26 |

{this.t('age')}

27 |

{this.t('size')}

28 |

{this.t('txs')}

29 |
30 |
34 | ( 37 | 43 | )} 44 | /> 45 |
46 |
47 | ) 48 | } 49 | } 50 | 51 | export default RecentBlockList 52 | -------------------------------------------------------------------------------- /src/components/lists/RecentBlockListItem.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { observer } from 'mobx-react' 3 | import { humanReadable } from '../../utilities/common.js' 4 | import moment from 'moment' 5 | 6 | const RecentBlockListItem = observer(props => { 7 | const block = props.statistics.recentBlocks[props.index] 8 | return ( 9 |
10 |
11 |

{block.height}

12 |

{block.type}

13 |

{moment(block.time).fromNow(true)}

14 |

15 | {humanReadable(block.size, true, 'B', props.gui.language)} 16 |

17 |

18 | {new Intl.NumberFormat(props.gui.language).format(block.txCount)} 19 |

20 |
21 |
22 | ) 23 | }) 24 | 25 | export default RecentBlockListItem 26 | -------------------------------------------------------------------------------- /src/components/lists/RecipientList.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import List from 'react-list' 3 | import { translate } from 'react-i18next' 4 | import { inject, observer } from 'mobx-react' 5 | 6 | /** Component */ 7 | import RecipientListItem from './RecipientListItem.js' 8 | 9 | @translate(['common']) 10 | @inject('gui', 'rates', 'send') 11 | @observer 12 | class RecipientList extends React.Component { 13 | constructor(props) { 14 | super(props) 15 | this.t = props.t 16 | this.gui = props.gui 17 | this.rates = props.rates 18 | this.send = props.send 19 | } 20 | 21 | /** 22 | * Set recipient address or amount. 23 | * @function setRecipient 24 | * @param {object} e - Input element event. 25 | */ 26 | setRecipient = e => { 27 | this.send.setRecipient(e.target.id, e.target.name, e.target.value) 28 | } 29 | 30 | render() { 31 | return ( 32 |
36 | ( 39 | 48 | )} 49 | /> 50 |
51 | ) 52 | } 53 | } 54 | 55 | export default RecipientList 56 | -------------------------------------------------------------------------------- /src/components/lists/RecipientListItem.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { observer } from 'mobx-react' 3 | 4 | /** Ant Design */ 5 | import Input from 'antd/lib/input' 6 | import Popconfirm from 'antd/lib/popconfirm' 7 | 8 | const RecipientListItem = observer(props => { 9 | const rec = props.send.recipients.get(props.send.recipientsKeys[props.index]) 10 | return ( 11 |
12 |
13 | props.send.removeRecipient(rec.id)} 29 | placement="bottomLeft" 30 | title={props.t('recipientRemove')} 31 | > 32 |
33 | delete_forever 34 |
35 | 36 | } 37 | size="small" 38 | value={rec.address} 39 | /> 40 |
41 |
42 | 51 |
52 |
53 | 62 |
63 |
64 | ) 65 | }) 66 | 67 | export default RecipientListItem 68 | -------------------------------------------------------------------------------- /src/components/lists/SpendOutputList.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import List from 'react-list' 3 | import { translate } from 'react-i18next' 4 | import { inject, observer } from 'mobx-react' 5 | 6 | /** Component */ 7 | import SpendOutputListItem from './SpendOutputListItem.js' 8 | 9 | @translate(['common']) 10 | @inject('gui', 'send') 11 | @observer 12 | class SpendOutputList extends React.Component { 13 | constructor(props) { 14 | super(props) 15 | this.t = props.t 16 | this.gui = props.gui 17 | this.send = props.send 18 | } 19 | 20 | /** 21 | * Mark or unmark the output for spending. 22 | * @function setOutput 23 | * @param {object} e - Checkbox element event. 24 | */ 25 | setOutput = e => { 26 | this.send.setOutput(e.target.id) 27 | } 28 | 29 | render() { 30 | /** Do not render if there are no outputs marked for spending. */ 31 | if (this.send.spend.utxo.length === 0) return null 32 | return ( 33 |
34 |
35 |

{this.t('selectedUtxo')}

36 |
37 |
41 | ( 44 | 51 | )} 52 | /> 53 |
54 |
55 | ) 56 | } 57 | } 58 | 59 | export default SpendOutputList 60 | -------------------------------------------------------------------------------- /src/components/lists/SpendOutputListItem.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { observer } from 'mobx-react' 3 | 4 | /** Ant Design */ 5 | import Checkbox from 'antd/lib/checkbox' 6 | 7 | const SpendOutputListItem = observer(props => { 8 | const utxo = props.send.spendUtxo[props.index] 9 | return ( 10 |
11 |
12 |
13 | 19 | {utxo.txid.slice(0, 11)}:{utxo.vout} 20 | 21 |
22 |

23 | {new Intl.NumberFormat(props.gui.language, { 24 | minimumFractionDigits: 6, 25 | maximumFractionDigits: 6 26 | }).format(utxo.amount)} 27 |

28 |
29 |
30 | ) 31 | }) 32 | 33 | export default SpendOutputListItem 34 | -------------------------------------------------------------------------------- /src/components/lists/TransactionIoList.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import List from 'react-list' 3 | import { translate } from 'react-i18next' 4 | import { inject, observer } from 'mobx-react' 5 | 6 | /** Component */ 7 | import TransactionIoListItem from './TransactionIoListItem.js' 8 | 9 | @translate(['common']) 10 | @inject('gui', 'wallet') 11 | @observer 12 | class TransactionIoList extends React.Component { 13 | constructor(props) { 14 | super(props) 15 | this.t = props.t 16 | this.gui = props.gui 17 | this.wallet = props.wallet 18 | 19 | /** IO array and IO type (vin or vout). */ 20 | this.io = props.io 21 | this.type = props.type 22 | } 23 | 24 | /** Update io prop on transaction change. */ 25 | componentWillReceiveProps(nextProps) { 26 | this.io = nextProps.io 27 | } 28 | 29 | render() { 30 | return ( 31 |
32 |
33 |

{this.t(this.type === 'vin' ? 'from' : 'to')}

34 |

{this.t('amount')} (XVC)

35 |
36 |
40 | ( 43 | 49 | )} 50 | /> 51 | {this.type === 'vin' && 52 | this.io.length === 0 && ( 53 |
54 |
55 |

Coinbase

56 |
57 |
58 | )} 59 |
60 |
61 | ) 62 | } 63 | } 64 | 65 | export default TransactionIoList 66 | -------------------------------------------------------------------------------- /src/components/lists/TransactionIoListItem.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | const TransactionIoListItem = props => { 4 | const item = props.io[props.index] 5 | return ( 6 |
7 |
8 |

{item.address}

9 |

10 | {new Intl.NumberFormat(props.gui.language, { 11 | minimumFractionDigits: 6, 12 | maximumFractionDigits: 6 13 | }).format(item.amount)} 14 |

15 |
16 |
17 | ) 18 | } 19 | 20 | export default TransactionIoListItem 21 | -------------------------------------------------------------------------------- /src/components/lists/TransactionList.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import List from 'react-list' 3 | import { translate } from 'react-i18next' 4 | import { inject, observer } from 'mobx-react' 5 | 6 | /** Components */ 7 | import TransactionListItem from './TransactionListItem.js' 8 | import { Placeholder } from '../utilities/Common.js' 9 | 10 | @translate(['common']) 11 | @inject('gui', 'rates', 'search', 'wallet') 12 | @observer 13 | class TransactionList extends React.Component { 14 | constructor(props) { 15 | super(props) 16 | this.t = props.t 17 | this.gui = props.gui 18 | this.rates = props.rates 19 | this.search = props.search 20 | this.wallet = props.wallet 21 | } 22 | 23 | render() { 24 | return ( 25 |
26 | ( 29 | 38 | )} 39 | /> 40 | 47 |
48 | ) 49 | } 50 | } 51 | 52 | export default TransactionList 53 | -------------------------------------------------------------------------------- /src/components/lists/TransactionListItem.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { observer } from 'mobx-react' 3 | import moment from 'moment' 4 | 5 | const TransactionListItem = observer(props => { 6 | const tx = props.wallet.tx.get(props.search.tx[props.index]) 7 | return ( 8 |
props.wallet.setViewing('tx', tx.txid)} 15 | > 16 |
17 |

{props.t(tx.category)}

18 |

{moment(tx.time).fromNow()}

19 |
20 |
21 |

22 | {new Intl.NumberFormat(props.gui.language, { 23 | minimumFractionDigits: 6, 24 | maximumFractionDigits: 6 25 | }).format(tx.amount)}{' '} 26 | XVC 27 |

28 |

29 | {new Intl.NumberFormat(props.gui.language, { 30 | minimumFractionDigits: 2, 31 | maximumFractionDigits: 2 32 | }).format(tx.amount * props.rates.average * props.rates.local)}{' '} 33 | {props.gui.localCurrency} 34 |

35 |
36 |
37 | ) 38 | }) 39 | 40 | export default TransactionListItem 41 | -------------------------------------------------------------------------------- /src/components/menus/MainMenu.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { translate } from 'react-i18next' 3 | import { withRouter } from 'react-router-dom' 4 | import { action, extendObservable, reaction } from 'mobx' 5 | import { inject, observer } from 'mobx-react' 6 | 7 | /** Ant Design */ 8 | import Menu from 'antd/lib/menu' 9 | 10 | @translate(['common']) 11 | @inject('connections') 12 | @observer 13 | class MainMenu extends React.Component { 14 | constructor(props) { 15 | super(props) 16 | this.t = props.t 17 | this.connections = props.connections 18 | 19 | /** Extend the component with observable properties. */ 20 | extendObservable(this, { selected: '/' }) 21 | 22 | /** 23 | * Redirect to the viewing connection when RPC becomes active, or to the 24 | * connections screen if the viewing connection becomes unreachable. 25 | */ 26 | this.viewingReaction = reaction( 27 | () => [this.connections.viewingId, this.connections.viewing.status.rpc], 28 | ([id, rpc]) => { 29 | this.setSelected({ 30 | key: rpc === true ? '/' + id + '/transactions' : '/' 31 | }) 32 | }, 33 | { 34 | fireImmediately: true, 35 | name: 'MainMenu: viewing connection status changed, adjusting screen.' 36 | } 37 | ) 38 | } 39 | 40 | /** Dispose of reaction on component unmount. */ 41 | componentWillUnmount() { 42 | this.viewingReaction() 43 | } 44 | 45 | /** 46 | * Set selected menu item and navigate to it. 47 | * @function setSelected 48 | * @param {object} item - Routing path and menu item key. 49 | */ 50 | @action 51 | setSelected = ({ key }) => { 52 | this.selected = key 53 | 54 | if (this.props.history.location.pathname !== key) { 55 | this.props.history.push(key) 56 | } 57 | } 58 | 59 | render() { 60 | const disabled = this.connections.viewing.status.rpc !== true 61 | const key = '/' + this.connections.viewingId + '/' 62 | 63 | return ( 64 | 70 | 71 |
72 | account_balance_wallet 73 |

{this.t('txs')}

74 |
75 |
76 | 77 |
78 | send 79 |

{this.t('send')}

80 |
81 |
82 | 83 |
84 | public 85 |

{this.t('net')}

86 |
87 |
88 | 89 |
90 | settings 91 |

{this.t('maint')}

92 |
93 |
94 | 95 |
96 | cast_connected 97 |

{this.t('connManager')}

98 |
99 |
100 |
101 | ) 102 | } 103 | } 104 | 105 | export default withRouter(MainMenu) 106 | -------------------------------------------------------------------------------- /src/components/menus/MaintenanceMenu.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { translate } from 'react-i18next' 3 | import { withRouter } from 'react-router-dom' 4 | import { action, extendObservable } from 'mobx' 5 | import { inject, observer } from 'mobx-react' 6 | 7 | /** Ant Design */ 8 | import Menu from 'antd/lib/menu' 9 | 10 | @translate(['common']) 11 | @inject('connections') 12 | @observer 13 | class MaintenanceMenu extends React.Component { 14 | constructor(props) { 15 | super(props) 16 | this.t = props.t 17 | this.connections = props.connections 18 | 19 | /** Extend the component with observable properties. */ 20 | extendObservable(this, { 21 | selected: '/' + this.connections.viewingId + '/maintenance/general' 22 | }) 23 | } 24 | 25 | /** 26 | * Set selected menu item and navigate to it. 27 | * @function setSelected 28 | * @param {object} item - Routing path and menu item key. 29 | */ 30 | @action 31 | setSelected = ({ key }) => { 32 | this.selected = key 33 | 34 | if (this.props.history.location.pathname !== key) { 35 | this.props.history.push(key) 36 | } 37 | } 38 | 39 | render() { 40 | const key = '/' + this.connections.viewingId + '/maintenance/' 41 | return ( 42 | 47 | 48 |
49 | tune 50 |

{this.t('maint')}

51 |
52 |
53 | 54 |
55 | arrow_upward 56 |

{this.t('maintDump')}

57 |
58 |
59 | 60 |
61 | arrow_downward 62 |

{this.t('maintImport')}

63 |
64 |
65 | 66 |
67 | cached 68 |

69 | {this.t('toggleExchangeRates')} 70 |

71 |
72 |
73 | 74 |
75 | remove_from_queue 76 |

{this.t('rpcConsole')}

77 |
78 |
79 |
80 | ) 81 | } 82 | } 83 | 84 | export default withRouter(MaintenanceMenu) 85 | -------------------------------------------------------------------------------- /src/components/menus/NetworkMenu.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { translate } from 'react-i18next' 3 | import { withRouter } from 'react-router-dom' 4 | import { action, extendObservable } from 'mobx' 5 | import { inject, observer } from 'mobx-react' 6 | 7 | /** Ant Design */ 8 | import Menu from 'antd/lib/menu' 9 | 10 | @translate(['common']) 11 | @inject('connections') 12 | @observer 13 | class NetworkMenu extends React.Component { 14 | constructor(props) { 15 | super(props) 16 | this.t = props.t 17 | this.connections = props.connections 18 | 19 | /** Extend the component with observable properties. */ 20 | extendObservable(this, { 21 | selected: '/' + this.connections.viewingId + '/network/info' 22 | }) 23 | } 24 | 25 | /** 26 | * Set selected menu item and navigate to it. 27 | * @function setSelected 28 | * @param {object} item - Routing path and menu item key. 29 | */ 30 | @action 31 | setSelected = ({ key }) => { 32 | this.selected = key 33 | 34 | if (this.props.history.location.pathname !== key) { 35 | this.props.history.push(key) 36 | } 37 | } 38 | 39 | render() { 40 | const key = '/' + this.connections.viewingId + '/network/' 41 | return ( 42 | 47 | 48 |
49 | business 50 |

{this.t('netInfo')}

51 |
52 |
53 | 54 |
55 | star 56 |

{this.t('netRewards')}

57 |
58 |
59 | 60 |
61 | network_check 62 |

{this.t('netRates')}

63 |
64 |
65 |
66 | ) 67 | } 68 | } 69 | 70 | export default withRouter(NetworkMenu) 71 | -------------------------------------------------------------------------------- /src/components/screens/Connections.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | /** Components */ 4 | import ConnectionList from '../lists/ConnectionList.js' 5 | import SetConnection from '../settings/SetConnection.js' 6 | import SetLocaleSettings from '../settings/SetLocaleSettings.js' 7 | 8 | const Connections = props => ( 9 |
10 |
11 | 12 |
13 | 14 |
15 |
16 | 17 |
18 | ) 19 | 20 | export default Connections 21 | -------------------------------------------------------------------------------- /src/components/screens/Maintenance.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { translate } from 'react-i18next' 3 | import { Route } from 'react-router-dom' 4 | import { inject, observer } from 'mobx-react' 5 | 6 | /** Components */ 7 | import Console from '../utilities/Console.js' 8 | import Header from '../Header.js' 9 | import Footer from '../Footer.js' 10 | import MaintenanceMenu from '../menus/MaintenanceMenu.js' 11 | import PrivateKeyDump from '../wallet/PrivateKeyDump.js' 12 | import PrivateKeyImport from '../wallet/PrivateKeyImport.js' 13 | import SetLocaleSettings from '../settings/SetLocaleSettings.js' 14 | import SetRates from '../settings/SetRates.js' 15 | import SetSoundAlerts from '../settings/SetSoundAlerts.js' 16 | import WalletBackup from '../wallet/WalletBackup.js' 17 | import WalletDump from '../wallet/WalletDump.js' 18 | import WalletEncrypt from '../wallet/WalletEncrypt.js' 19 | import WalletPassphraseChange from '../wallet/WalletPassphraseChange.js' 20 | import WalletRepair from '../wallet/WalletRepair.js' 21 | import WalletSeedDump from '../wallet/WalletSeedDump.js' 22 | 23 | @translate(['common']) 24 | @inject('connections') 25 | @observer 26 | class Maintenance extends React.Component { 27 | constructor(props) { 28 | super(props) 29 | this.t = props.t 30 | this.connections = props.connections 31 | } 32 | 33 | render() { 34 | return ( 35 |
36 |
37 |
38 | 39 |
40 |
41 |
42 | 43 |
44 |
45 |
46 | 47 |
48 |
49 |
50 |
51 |
52 | ( 55 |
56 | 57 | 58 |
59 | 60 |
61 | 62 |
63 | )} 64 | /> 65 | ( 68 |
69 | 70 |
71 | 72 |
73 | 74 |
75 | )} 76 | /> 77 | ( 80 |
81 | 82 |
83 | )} 84 | /> 85 | ( 88 |
89 | 90 |
91 | )} 92 | /> 93 | ( 96 |
97 | 98 |
99 | )} 100 | /> 101 |
102 |
103 |
104 | ) 105 | } 106 | } 107 | 108 | export default Maintenance 109 | -------------------------------------------------------------------------------- /src/components/screens/Network.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { translate } from 'react-i18next' 3 | import { Route } from 'react-router-dom' 4 | import { inject, observer } from 'mobx-react' 5 | 6 | /** Components */ 7 | import DailyRewardChart from '../charts/DailyRewardChart.js' 8 | import DifficultyChart from '../charts/DifficultyChart.js' 9 | import Footer from '../Footer.js' 10 | import HashRateChart from '../charts/HashRateChart.js' 11 | import Header from '../Header.js' 12 | import IncentiveInfo from '../wallet/IncentiveInfo.js' 13 | import NetworkInfo from '../wallet/NetworkInfo.js' 14 | import NetworkMenu from '../menus/NetworkMenu.js' 15 | import Peers from '../wallet/Peers.js' 16 | import RecentBlockList from '../lists/RecentBlockList.js' 17 | import RewardSpreadChart from '../charts/RewardSpreadChart.js' 18 | 19 | @translate(['common']) 20 | @inject('connections') 21 | @observer 22 | class Network extends React.Component { 23 | constructor(props) { 24 | super(props) 25 | this.t = props.t 26 | this.connections = props.connections 27 | } 28 | 29 | render() { 30 | const key = '/' + this.connections.viewingId + '/network/' 31 | return ( 32 |
33 |
34 |
35 | 36 |
37 |
38 | 39 |
40 |
41 |
42 |
43 | ( 46 |
47 |
48 | 49 | 50 |
51 | 52 |
53 | )} 54 | /> 55 | ( 58 |
59 |
60 |
61 | timeline 62 |

{this.t('rewardSpread')}

63 |
64 | 65 |
66 |
67 |
68 | view_week 69 |

{this.t('rewardsPerDay')}

70 |
71 | 72 |
73 |
74 | )} 75 | /> 76 | ( 79 |
80 |
81 |
82 | trending_up 83 |

{this.t('difficulties')}

84 |
85 | 86 |
87 |
88 |
89 | network_check 90 |

{this.t('hashRate')}

91 |
92 | 93 |
94 |
95 | )} 96 | /> 97 |
98 |
99 |
100 | ) 101 | } 102 | } 103 | 104 | export default Network 105 | -------------------------------------------------------------------------------- /src/components/screens/Send.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | /** Components */ 4 | import AccountFilter from '../search/AccountFilter.js' 5 | import Address from '../wallet/Address.js' 6 | import AddressFilter from '../search/AddressFilter.js' 7 | import AddressFind from '../search/AddressFind.js' 8 | import AddressGet from '../wallet/AddressGet.js' 9 | import AddressList from '../lists/AddressList.js' 10 | import CurrencyConverter from '../utilities/CurrencyConverter.js' 11 | import Footer from '../Footer.js' 12 | import Header from '../Header.js' 13 | import Message from '../wallet/Message.js' 14 | import RecipientList from '../lists/RecipientList.js' 15 | import SendControls from '../send/SendControls.js' 16 | import SendOptions from '../send/SendOptions.js' 17 | import SpendBalance from '../send/SpendBalance.js' 18 | import SpendOutputList from '../lists/SpendOutputList.js' 19 | 20 | const Send = props => ( 21 |
22 |
23 | 24 |
25 |
26 | 27 |
28 |
29 |
30 |
31 |
32 |
33 | 34 |
35 | 36 |
37 |
38 |
39 | 40 |
41 | 42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 | 53 | 54 |
55 |
56 |
57 |
58 | 59 |
60 | 61 |
62 |
63 | 64 |
65 | 66 |
67 |
68 |
69 |
70 |
71 |
72 | ) 73 | 74 | export default Send 75 | -------------------------------------------------------------------------------- /src/components/screens/Transactions.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | /** Components */ 4 | import ChainBlender from '../wallet/ChainBlender.js' 5 | import DailyTotalsChart from '../charts/DailyTotalsChart.js' 6 | import Footer from '../Footer.js' 7 | import Header from '../Header.js' 8 | import Transaction from '../wallet/Transaction.js' 9 | import TransactionFilter from '../search/TransactionFilter.js' 10 | import TransactionFind from '../search/TransactionFind.js' 11 | import TransactionList from '../lists/TransactionList.js' 12 | 13 | const Transactions = props => ( 14 |
15 |
16 | 17 |
18 |
19 | 20 |
21 |
22 |
23 |
24 | 25 | 26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 | 34 |
35 | 36 |
37 |
38 |
39 |
40 |
41 | ) 42 | 43 | export default Transactions 44 | -------------------------------------------------------------------------------- /src/components/search/AccountFilter.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { translate } from 'react-i18next' 3 | import { inject, observer } from 'mobx-react' 4 | 5 | /** Ant Design */ 6 | import Select from 'antd/lib/select' 7 | 8 | @translate(['common']) 9 | @inject('send', 'wallet') 10 | @observer 11 | class AccountFilter extends React.Component { 12 | constructor(props) { 13 | super(props) 14 | this.t = props.t 15 | this.send = props.send 16 | this.wallet = props.wallet 17 | } 18 | 19 | render() { 20 | return ( 21 | 43 | ) 44 | } 45 | } 46 | 47 | export default AccountFilter 48 | -------------------------------------------------------------------------------- /src/components/search/AddressFilter.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { translate } from 'react-i18next' 3 | import { inject, observer } from 'mobx-react' 4 | 5 | /** Ant Design */ 6 | import Button from 'antd/lib/button' 7 | import Popover from 'antd/lib/popover' 8 | import Switch from 'antd/lib/switch' 9 | 10 | /** Component */ 11 | import { SwitchIcon } from '../utilities/Common.js' 12 | 13 | @translate(['common']) 14 | @inject('search') 15 | @observer 16 | class AddressFilter extends React.Component { 17 | constructor(props) { 18 | super(props) 19 | this.t = props.t 20 | this.search = props.search 21 | } 22 | 23 | /** 24 | * Switch toggle. 25 | * @function switchToggle 26 | * @param {string} name - Name of the property to toggle in the search store. 27 | */ 28 | switchToggle(name) { 29 | return ( 30 | } 33 | onChange={() => this.search.toggleShow('addr', name)} 34 | size="small" 35 | unCheckedChildren={} 36 | /> 37 | ) 38 | } 39 | 40 | render() { 41 | return ( 42 | 47 | filter_list 48 |

{this.t('addrFilter')}

49 | 50 | } 51 | content={ 52 |
53 |
54 |

{this.t('new')}

55 |

{this.t('spendable')}

56 |

{this.t('spent')}

57 |
58 |
59 |
{this.switchToggle('new')}
60 |
{this.switchToggle('spendable')}
61 |
{this.switchToggle('spent')}
62 |
63 |
64 | } 65 | > 66 | 69 |
70 | ) 71 | } 72 | } 73 | 74 | export default AddressFilter 75 | -------------------------------------------------------------------------------- /src/components/search/AddressFind.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { translate } from 'react-i18next' 3 | import { inject, observer } from 'mobx-react' 4 | 5 | /** Ant Design */ 6 | import Input from 'antd/lib/input' 7 | 8 | @translate(['common']) 9 | @inject('search') 10 | @observer 11 | class AddressFind extends React.Component { 12 | constructor(props) { 13 | super(props) 14 | this.t = props.t 15 | this.search = props.search 16 | } 17 | 18 | render() { 19 | return ( 20 | this.search.setKeywords('addr', e.target.value)} 22 | placeholder={this.t('searchAddresses')} 23 | prefix={search} 24 | size="small" 25 | value={this.search.find.addr.value} 26 | /> 27 | ) 28 | } 29 | } 30 | 31 | export default AddressFind 32 | -------------------------------------------------------------------------------- /src/components/search/TransactionFilter.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { translate } from 'react-i18next' 3 | import { inject, observer } from 'mobx-react' 4 | 5 | /** Ant Design */ 6 | import Button from 'antd/lib/button' 7 | import Popover from 'antd/lib/popover' 8 | import Switch from 'antd/lib/switch' 9 | 10 | /** Component */ 11 | import { SwitchIcon } from '../utilities/Common.js' 12 | 13 | @translate(['common']) 14 | @inject('search') 15 | @observer 16 | class TransactionFilter extends React.Component { 17 | constructor(props) { 18 | super(props) 19 | this.t = props.t 20 | this.search = props.search 21 | } 22 | 23 | /** 24 | * Switch toggle. 25 | * @function switchToggle 26 | * @param {string} name - Name of the property to toggle in the search store. 27 | */ 28 | switchToggle(name) { 29 | return ( 30 | } 33 | onChange={() => this.search.toggleShow('tx', name)} 34 | size="small" 35 | unCheckedChildren={} 36 | /> 37 | ) 38 | } 39 | 40 | render() { 41 | return ( 42 | 47 | filter_list 48 |

{this.t('txFilter')}

49 | 50 | } 51 | content={ 52 |
53 |
54 |

{this.t('received')}

55 |

{this.t('sent')}

56 |

{this.t('sentSelf')}

57 |

{this.t('blended')}

58 |

{this.t('rewards')}

59 |
60 |
61 |
{this.switchToggle('received')}
62 |
{this.switchToggle('sent')}
63 |
{this.switchToggle('sentSelf')}
64 |
{this.switchToggle('blended')}
65 |
{this.switchToggle('rewards')}
66 |
67 |
68 | } 69 | > 70 | 73 |
74 | ) 75 | } 76 | } 77 | 78 | export default TransactionFilter 79 | -------------------------------------------------------------------------------- /src/components/search/TransactionFind.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { translate } from 'react-i18next' 3 | import { inject, observer } from 'mobx-react' 4 | 5 | /** Ant Design */ 6 | import Input from 'antd/lib/input' 7 | 8 | @translate(['common']) 9 | @inject('search') 10 | @observer 11 | class TransactionFind extends React.Component { 12 | constructor(props) { 13 | super(props) 14 | this.t = props.t 15 | this.search = props.search 16 | } 17 | 18 | render() { 19 | return ( 20 | this.search.setKeywords('tx', e.target.value)} 22 | placeholder={this.t('searchTransactions')} 23 | prefix={search} 24 | size="small" 25 | value={this.search.find.tx.value} 26 | /> 27 | ) 28 | } 29 | } 30 | 31 | export default TransactionFind 32 | -------------------------------------------------------------------------------- /src/components/send/SendControls.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { translate } from 'react-i18next' 3 | import { action, extendObservable } from 'mobx' 4 | import { inject, observer } from 'mobx-react' 5 | 6 | /** Ant Design */ 7 | import Button from 'antd/lib/button' 8 | import Popconfirm from 'antd/lib/popconfirm' 9 | 10 | /** Component */ 11 | import SendTotal from './SendTotal.js' 12 | 13 | @translate(['common']) 14 | @inject('send', 'wallet') 15 | @observer 16 | class SendControls extends React.Component { 17 | constructor(props) { 18 | super(props) 19 | this.t = props.t 20 | this.send = props.send 21 | this.wallet = props.wallet 22 | 23 | /** Extend the component with observable properties. */ 24 | extendObservable(this, { popconfirmVisible: false }) 25 | } 26 | 27 | /** 28 | * Toggle popconfirm visibility only when the wallet is unlocked. 29 | * @function togglePopconfirm 30 | */ 31 | @action 32 | togglePopconfirm = () => { 33 | if (this.wallet.isLocked === false && this.send.errorStatus === '') { 34 | this.popconfirmVisible = !this.popconfirmVisible 35 | } 36 | } 37 | 38 | render() { 39 | return ( 40 |
41 |
42 | this.send.confirm()} 46 | onVisibleChange={this.togglePopconfirm} 47 | placement="bottom" 48 | title={this.t('sendConfirm')} 49 | visible={this.popconfirmVisible} 50 | > 51 | 61 | 62 | 75 | this.send.reset()} 79 | placement="bottom" 80 | title={this.t('sendReset')} 81 | > 82 | 83 | 84 |
85 | 86 |
87 | ) 88 | } 89 | } 90 | 91 | export default SendControls 92 | -------------------------------------------------------------------------------- /src/components/send/SendOptions.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { translate } from 'react-i18next' 3 | import { inject, observer } from 'mobx-react' 4 | 5 | /** Ant Design */ 6 | import Input from 'antd/lib/input' 7 | 8 | @translate(['common']) 9 | @inject('send') 10 | @observer 11 | class SendOptions extends React.Component { 12 | constructor(props) { 13 | super(props) 14 | this.t = props.t 15 | this.send = props.send 16 | } 17 | 18 | render() { 19 | /** Do not render if spending outputs directly. */ 20 | if (this.send.spend.utxo.length > 0) return null 21 | return ( 22 |
23 |
24 |
25 |
26 | label 27 |

{this.t('spendFrom')}

28 |
29 | {this.send.recipients.size === 1 && ( 30 |
31 | perm_identity 32 |

{this.t('recipient')}

33 |
34 | )} 35 |
36 | create 37 |

{this.t('description')}

38 |
39 | {this.send.spend.fromAccount !== '*ANY*' && ( 40 |
41 | done_all 42 |

{this.t('minConf')}

43 |
44 | )} 45 |
46 |
47 |

48 | {(this.send.spend.fromAccount === '*ANY*' && this.t('any')) || 49 | (this.send.spend.fromAccount === '*DEFAULT*' && 50 | this.t('default')) || 51 | this.send.spend.fromAccount} 52 |

53 | {this.send.recipients.size === 1 && ( 54 |
55 | this.send.setCommentTo(e.target.value)} 57 | placeholder={this.t('recipientDesc')} 58 | size="small" 59 | value={this.send.commentTo} 60 | /> 61 |
62 | )} 63 |
64 | this.send.setComment(e.target.value)} 66 | placeholder={this.t('descriptionLong')} 67 | size="small" 68 | value={this.send.comment} 69 | /> 70 |
71 | {this.send.spend.fromAccount !== '*ANY*' && ( 72 |
73 | this.send.setMinConf(e.target.value)} 75 | size="small" 76 | value={this.send.minConf} 77 | /> 78 |
79 | )} 80 |
81 |
82 |
83 | ) 84 | } 85 | } 86 | 87 | export default SendOptions 88 | -------------------------------------------------------------------------------- /src/components/send/SendTotal.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { translate } from 'react-i18next' 3 | import { inject, observer } from 'mobx-react' 4 | 5 | /** Ant Design */ 6 | import Input from 'antd/lib/input' 7 | 8 | @translate(['common']) 9 | @inject('gui', 'rates', 'send') 10 | @observer 11 | class SendTotal extends React.Component { 12 | constructor(props) { 13 | super(props) 14 | this.t = props.t 15 | this.gui = props.gui 16 | this.rates = props.rates 17 | this.send = props.send 18 | } 19 | 20 | render() { 21 | /** Do not render if there are less than two recipients. */ 22 | if (this.send.recipients.size < 2) return null 23 | return ( 24 |
25 |
26 | 34 |
35 |
36 | 45 |
46 |
47 | ) 48 | } 49 | } 50 | 51 | export default SendTotal 52 | -------------------------------------------------------------------------------- /src/components/send/SpendBalance.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { translate } from 'react-i18next' 3 | import { inject, observer } from 'mobx-react' 4 | 5 | @translate(['common']) 6 | @inject('gui', 'send') 7 | @observer 8 | class SpendBalance extends React.Component { 9 | constructor(props) { 10 | super(props) 11 | this.t = props.t 12 | this.gui = props.gui 13 | this.send = props.send 14 | } 15 | 16 | render() { 17 | /** Do not render if the balance is zero. */ 18 | if (this.send.spendBalance === 0) return null 19 | return ( 20 |
21 |
22 |

{this.t('spendBalance')}

23 |
24 |

25 | {new Intl.NumberFormat(this.gui.language, { 26 | maximumFractionDigits: 6 27 | }).format(this.send.spendBalance)} 28 |

29 |
30 | ) 31 | } 32 | } 33 | 34 | export default SpendBalance 35 | -------------------------------------------------------------------------------- /src/components/settings/SetCurrency.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { translate } from 'react-i18next' 3 | import { inject, observer } from 'mobx-react' 4 | 5 | /** Ant Design */ 6 | import Select from 'antd/lib/select' 7 | 8 | @translate(['common']) 9 | @inject('gui', 'rates') 10 | @observer 11 | class SetCurrency extends React.Component { 12 | constructor(props) { 13 | super(props) 14 | this.t = props.t 15 | this.gui = props.gui 16 | this.rates = props.rates 17 | } 18 | 19 | render() { 20 | return ( 21 | 36 | ) 37 | } 38 | } 39 | 40 | export default SetCurrency 41 | -------------------------------------------------------------------------------- /src/components/settings/SetLanguage.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { translate } from 'react-i18next' 3 | import { inject, observer } from 'mobx-react' 4 | 5 | /** Ant Design */ 6 | import Select from 'antd/lib/select' 7 | 8 | @translate(['common']) 9 | @inject('gui') 10 | @observer 11 | class SetLanguage extends React.Component { 12 | constructor(props) { 13 | super(props) 14 | this.t = props.t 15 | this.gui = props.gui 16 | } 17 | 18 | render() { 19 | return ( 20 | 35 | ) 36 | } 37 | } 38 | 39 | export default SetLanguage 40 | -------------------------------------------------------------------------------- /src/components/settings/SetLocaleSettings.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { translate } from 'react-i18next' 3 | 4 | /** Components */ 5 | import SetCurrency from './SetCurrency.js' 6 | import SetLanguage from './SetLanguage.js' 7 | 8 | @translate(['common']) 9 | class SetLocaleSettings extends React.Component { 10 | constructor(props) { 11 | super(props) 12 | this.t = props.t 13 | } 14 | 15 | render() { 16 | return ( 17 |
18 |
19 |
20 | language 21 |

{this.t('language')}

22 |
23 | 24 |
25 |
26 |
27 | monetization_on 28 |

{this.t('localCurrency')}

29 |
30 | 31 |
32 |
33 | ) 34 | } 35 | } 36 | 37 | export default SetLocaleSettings 38 | -------------------------------------------------------------------------------- /src/components/settings/SetSoundAlerts.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { translate } from 'react-i18next' 3 | import { inject, observer } from 'mobx-react' 4 | 5 | /** Ant Design */ 6 | import Switch from 'antd/lib/switch' 7 | 8 | /** Component */ 9 | import { SwitchIcon } from '../utilities/Common.js' 10 | 11 | @translate(['common']) 12 | @inject('gui') 13 | @observer 14 | class SetSoundAlerts extends React.Component { 15 | constructor(props) { 16 | super(props) 17 | this.t = props.t 18 | this.gui = props.gui 19 | this.toggleNotification = this.toggleNotification.bind(this) 20 | } 21 | 22 | /** 23 | * Toggle notification. 24 | * @function toggleNotification 25 | */ 26 | toggleNotification(name) { 27 | this.gui.setSoundAlert(name) 28 | 29 | /** Play the sound if enabling the notification. */ 30 | if (this.gui.soundAlerts[name] === true) this.gui.sounds[name].play() 31 | } 32 | 33 | render() { 34 | return ( 35 |
36 |
37 | notifications_active 38 |

{this.t('notificationSounds')}

39 |
40 |
41 |
42 | call_received 43 |

{this.t('incoming')}

44 |
45 | } 48 | onChange={() => this.toggleNotification('incoming')} 49 | size="small" 50 | unCheckedChildren={} 51 | /> 52 |
53 |
54 |
55 | done_all 56 |

{this.t('spendable')}

57 |
58 | } 61 | onChange={() => this.toggleNotification('spendable')} 62 | size="small" 63 | unCheckedChildren={} 64 | /> 65 |
66 |
67 | ) 68 | } 69 | } 70 | 71 | export default SetSoundAlerts 72 | -------------------------------------------------------------------------------- /src/components/utilities/Common.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | /** 4 | * Placeholder used in Transactions screen while the wallet has 0 transactions. 5 | * @function Placeholder 6 | * @prop {object} t - i18next intance. 7 | * @prop {string} icon - Material icon name. 8 | * @prop {string} string - i18next namespace string. 9 | * @prop {object} style - Custom component style. 10 | * @prop {object} wallet - Connection instance Wallet store. 11 | */ 12 | export const Placeholder = props => { 13 | if (props.wallet.txKeys.length > 0) return null 14 | return ( 15 |
16 |
17 | {props.icon} 18 |

19 | {props.t(props.string)} 20 |

21 |
22 |
23 | ) 24 | } 25 | 26 | /** 27 | * Switch icon. 28 | * @function SwitchIcon 29 | * @prop {string} icon - Material icon name. 30 | */ 31 | export const SwitchIcon = props => ( 32 |
33 | {props.icon} 34 |
35 | ) 36 | -------------------------------------------------------------------------------- /src/components/utilities/Console.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { translate } from 'react-i18next' 3 | import { inject, observer } from 'mobx-react' 4 | import { shortUid } from '../../utilities/common.js' 5 | 6 | /** Ant Design */ 7 | import AutoComplete from 'antd/lib/auto-complete' 8 | import Button from 'antd/lib/button' 9 | 10 | @translate(['common']) 11 | @inject('console', 'gui', 'rpc') 12 | @observer 13 | class Console extends React.Component { 14 | constructor(props) { 15 | super(props) 16 | this.t = props.t 17 | this.console = props.console 18 | this.gui = props.gui 19 | this.rpc = props.rpc 20 | } 21 | 22 | render() { 23 | return ( 24 |
25 |
26 |
34 | {this.console.responses.map(response => ( 35 |
{JSON.stringify(response, null, 2)}
36 | ))} 37 |
38 |
39 |
40 | 47 |
48 | 56 |
57 | 60 |
61 |
62 | ) 63 | } 64 | } 65 | 66 | export default Console 67 | -------------------------------------------------------------------------------- /src/components/utilities/CurrencyConverter.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { translate } from 'react-i18next' 3 | import { action, computed, extendObservable } from 'mobx' 4 | import { inject, observer } from 'mobx-react' 5 | import { decimalSep } from '../../utilities/common.js' 6 | 7 | /** Ant Design */ 8 | import Input from 'antd/lib/input' 9 | 10 | @translate(['common']) 11 | @inject('gui', 'rates') 12 | @observer 13 | class CurrencyConverter extends React.Component { 14 | constructor(props) { 15 | super(props) 16 | this.t = props.t 17 | this.gui = props.gui 18 | this.rates = props.rates 19 | 20 | /** Extend the component with observable properties. */ 21 | extendObservable(this, { amount: 1, from: 'vcash' }) 22 | } 23 | 24 | /** 25 | * Get converted amounts. 26 | * @function amounts 27 | * @return {object} Converted amounts. 28 | */ 29 | @computed 30 | get amounts() { 31 | const { average, local } = this.rates 32 | 33 | switch (this.from) { 34 | case 'bitcoin': 35 | return { 36 | bitcoin: this.amount, 37 | local: Math.round(this.amount * local * 1e3) / 1e3, 38 | vcash: Math.round(this.amount / average * 1e6) / 1e6 39 | } 40 | 41 | case 'local': 42 | return { 43 | bitcoin: Math.round(this.amount / local * 1e8) / 1e8, 44 | local: this.amount, 45 | vcash: Math.round(this.amount / local / average * 1e6) / 1e6 46 | } 47 | 48 | case 'vcash': 49 | return { 50 | bitcoin: Math.round(this.amount * average * 1e8) / 1e8, 51 | local: Math.round(this.amount * local * average * 1e3) / 1e3, 52 | vcash: this.amount 53 | } 54 | } 55 | } 56 | 57 | /** 58 | * Set amount and converting currency. 59 | * @function convert 60 | * @param {object} e - Input element event. 61 | */ 62 | @action 63 | convert = e => { 64 | const amount = e.target.value 65 | const from = e.target.name 66 | 67 | /** Allow only amount in 0000000[.,]00000000 format. */ 68 | switch (decimalSep()) { 69 | case '.': 70 | if (amount.match(/^\d{0,7}(?:\.\d{0,8})?$/) === null) return 71 | break 72 | 73 | case ',': 74 | if (amount.match(/^\d{0,7}(?:,\d{0,8})?$/) === null) return 75 | break 76 | } 77 | 78 | this.amount = amount 79 | this.from = from 80 | } 81 | 82 | render() { 83 | return ( 84 |
85 |
86 | cached 87 |

{this.t('currencyConverter')}

88 |
89 |
90 |
91 | 99 |
100 |
101 | 109 |
110 |
111 | 119 |
120 |
121 |
122 | ) 123 | } 124 | } 125 | 126 | export default CurrencyConverter 127 | -------------------------------------------------------------------------------- /src/components/utilities/RewardCalculator.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { translate } from 'react-i18next' 3 | import { action, computed, extendObservable } from 'mobx' 4 | import { inject, observer } from 'mobx-react' 5 | import { incentivePercent, powReward } from '../../utilities/blockRewards.js' 6 | 7 | /** Ant Design */ 8 | import Input from 'antd/lib/input' 9 | 10 | @translate(['common']) 11 | @inject('gui', 'wallet') 12 | @observer 13 | class RewardCalculator extends React.Component { 14 | constructor(props) { 15 | super(props) 16 | this.t = props.t 17 | this.gui = props.gui 18 | this.wallet = props.wallet 19 | 20 | /** Extend the component with observable properties. */ 21 | extendObservable(this, { enteredBlock: '' }) 22 | } 23 | 24 | /** 25 | * Get block. 26 | * @function block 27 | * @return {number} Entered or current block. 28 | */ 29 | @computed 30 | get block() { 31 | return this.enteredBlock.length === 0 32 | ? this.wallet.info.blocks 33 | : Math.round(this.enteredBlock) 34 | } 35 | 36 | /** 37 | * Get Proof-of-Work reward. 38 | * @function powReward 39 | * @return {number} Reward. 40 | */ 41 | @computed 42 | get powReward() { 43 | return powReward(this.block) 44 | } 45 | 46 | /** 47 | * Get incentive percent of PoW reward. 48 | * @function incentivePercent 49 | * @return {number} Percent. 50 | */ 51 | @computed 52 | get incentivePercent() { 53 | return incentivePercent(this.block) 54 | } 55 | 56 | /** 57 | * Get mining share. 58 | * @function miningReward 59 | * @return {number} Reward. 60 | */ 61 | @computed 62 | get miningReward() { 63 | return this.powReward - this.incentiveReward 64 | } 65 | 66 | /** 67 | * Get incentive share. 68 | * @function incentiveReward 69 | * @return {number} Reward. 70 | */ 71 | @computed 72 | get incentiveReward() { 73 | return this.powReward / 100 * this.incentivePercent 74 | } 75 | 76 | /** 77 | * Set block. 78 | * @function setBlock 79 | * @param {string} block - Entered block. 80 | */ 81 | @action 82 | setBlock = block => { 83 | if (block.toString().match(/^[0-9]{0,7}$/) !== null) { 84 | this.enteredBlock = block 85 | } 86 | } 87 | 88 | render() { 89 | return ( 90 |
91 |
92 |
93 | extension 94 |

{this.t('block')}

95 |
96 |
97 | star 98 |

{this.t('powReward')}

99 |
100 |
101 | developer_board 102 |

{this.t('miningReward')}

103 |
104 |
105 | event_seat 106 |

{this.t('incentiveReward')}

107 |
108 |
109 |
110 | this.setBlock(e.target.value)} 112 | placeholder={this.block} 113 | size="small" 114 | style={{ margin: '0 0 10px 0', width: '60px' }} 115 | value={this.enteredBlock} 116 | /> 117 |

118 | 119 | {new Intl.NumberFormat(this.gui.language, { 120 | minimumFractionDigits: 6, 121 | maximumFractionDigits: 6 122 | }).format(this.powReward)} 123 | {' '} 124 | XVC 125 |

126 |

127 | 128 | {new Intl.NumberFormat(this.gui.language, { 129 | minimumFractionDigits: 6, 130 | maximumFractionDigits: 6 131 | }).format(this.miningReward)} 132 | {' '} 133 | XVC ({100 - this.incentivePercent}%) 134 |

135 |

136 | 137 | {new Intl.NumberFormat(this.gui.language, { 138 | minimumFractionDigits: 6, 139 | maximumFractionDigits: 6 140 | }).format(this.incentiveReward)} 141 | {' '} 142 | XVC ({this.incentivePercent}%) 143 |

144 |
145 |
146 | ) 147 | } 148 | } 149 | 150 | export default RewardCalculator 151 | -------------------------------------------------------------------------------- /src/components/wallet/Address.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { translate } from 'react-i18next' 3 | import { inject, observer } from 'mobx-react' 4 | 5 | /** Component */ 6 | import AddressOutputList from '../lists/AddressOutputList.js' 7 | 8 | @translate(['common']) 9 | @inject('wallet') 10 | @observer 11 | class Address extends React.Component { 12 | constructor(props) { 13 | super(props) 14 | this.t = props.t 15 | this.wallet = props.wallet 16 | } 17 | 18 | render() { 19 | /** Do not render if the address does not exist. */ 20 | if (this.wallet.addr.has(this.wallet.viewing.addr) === false) return null 21 | const addr = this.wallet.addr.get(this.wallet.viewing.addr) 22 | 23 | return ( 24 |
25 |
26 |
27 | call_received 28 |

{this.t('received')}

29 |
30 |

{addr.received}

31 |
32 | call_made 33 |

{this.t('spent')}

34 |
35 |

{addr.spent}

36 |
37 | label 38 | {addr.account === null &&

{this.t('change')}

} 39 | {addr.account !== null &&

{this.t('acc')}

} 40 |
41 | {addr.account !== null && ( 42 |

43 | {addr.account === '' ? this.t('default') : addr.account} 44 |

45 | )} 46 |
47 |
48 | 49 |
50 |
51 | ) 52 | } 53 | } 54 | 55 | export default Address 56 | -------------------------------------------------------------------------------- /src/components/wallet/AddressGet.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { translate } from 'react-i18next' 3 | import { inject, observer } from 'mobx-react' 4 | import { action, computed, extendObservable, reaction } from 'mobx' 5 | 6 | /** Ant Design */ 7 | import AutoComplete from 'antd/lib/auto-complete' 8 | import Button from 'antd/lib/button' 9 | import Input from 'antd/lib/input' 10 | import Popover from 'antd/lib/popover' 11 | 12 | @translate(['common']) 13 | @inject('rpc', 'wallet') 14 | @observer 15 | class AddressGet extends React.Component { 16 | constructor(props) { 17 | super(props) 18 | this.t = props.t 19 | this.rpc = props.rpc 20 | this.wallet = props.wallet 21 | this.getNewAddress = this.getNewAddress.bind(this) 22 | 23 | /** Errors that will be shown to the user. */ 24 | this.errShow = ['accChars', 'keypoolRanOut'] 25 | 26 | /** Extend the component with observable properties. */ 27 | extendObservable(this, { 28 | account: '', 29 | address: '', 30 | rpcError: '', 31 | popoverVisible: false 32 | }) 33 | 34 | /** Clear new address when the popover gets hidden. */ 35 | this.popoverReaction = reaction( 36 | () => this.popoverVisible, 37 | popoverVisible => { 38 | if (popoverVisible === false) { 39 | if (this.address !== '') this.setProps({ address: '' }) 40 | } 41 | }, 42 | { name: 'AddressGet: popover hidden, clearing new address.' } 43 | ) 44 | } 45 | 46 | /** Dispose of reaction on component unmount. */ 47 | componentWillUnmount() { 48 | this.popoverReaction() 49 | } 50 | 51 | /** 52 | * Get present error or empty string if none. 53 | * @function errorStatus 54 | * @return {string} Error status. 55 | */ 56 | @computed 57 | get errorStatus() { 58 | if (this.account.match(/^[a-z0-9 -]*$/i) === null) return 'accChars' 59 | if (this.account.length > 100) return 'accLength' 60 | if (this.rpcError !== '') return this.rpcError 61 | return '' 62 | } 63 | 64 | /** 65 | * Set observable properties. 66 | * @function setProps 67 | * @param {object} props - Key value combinations. 68 | */ 69 | @action 70 | setProps = props => { 71 | Object.keys(props).forEach(key => (this[key] = props[key])) 72 | } 73 | 74 | /** 75 | * Toggle popover visibility. 76 | * @function togglePopover 77 | */ 78 | @action 79 | togglePopover = () => { 80 | this.popoverVisible = !this.popoverVisible 81 | } 82 | 83 | /** 84 | * Get new receiving address. 85 | * @function getNewAddress 86 | */ 87 | async getNewAddress() { 88 | const res = await this.rpc.getNewAddress(this.account) 89 | 90 | if ('result' in res === true) { 91 | this.setProps({ address: res.result }) 92 | this.wallet.updateAddresses([this.account]) 93 | } 94 | 95 | if ('error' in res === true) { 96 | switch (res.error.code) { 97 | case -12: 98 | return this.setProps({ rpcError: 'keypoolRanOut' }) 99 | } 100 | } 101 | } 102 | 103 | render() { 104 | return ( 105 | 108 | triggerNode.parentNode} 112 | onChange={account => this.setProps({ account })} 113 | placeholder={this.t('accName')} 114 | style={{ width: '100%' }} 115 | value={this.account} 116 | /> 117 | {this.address !== '' && ( 118 | 124 | )} 125 |
126 |

127 | {this.errShow.includes(this.errorStatus) === true && 128 | this.t(this.errorStatus)} 129 |

130 | 136 |
137 | 138 | } 139 | onVisibleChange={this.togglePopover} 140 | placement="topLeft" 141 | title={this.t('addrGetDesc')} 142 | trigger="click" 143 | visible={this.popoverVisible} 144 | > 145 | 148 |
149 | ) 150 | } 151 | } 152 | 153 | export default AddressGet 154 | -------------------------------------------------------------------------------- /src/components/wallet/ChainBlender.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { translate } from 'react-i18next' 3 | import { inject, observer } from 'mobx-react' 4 | 5 | /** Ant Design */ 6 | import Button from 'antd/lib/button' 7 | import message from 'antd/lib/message' 8 | import Popover from 'antd/lib/popover' 9 | import Switch from 'antd/lib/switch' 10 | 11 | /** Component */ 12 | import { SwitchIcon } from '../utilities/Common.js' 13 | 14 | @translate(['common']) 15 | @inject('gui', 'rpc', 'wallet') 16 | @observer 17 | class ChainBlender extends React.Component { 18 | constructor(props) { 19 | super(props) 20 | this.t = props.t 21 | this.gui = props.gui 22 | this.rpc = props.rpc 23 | this.wallet = props.wallet 24 | this.toggle = this.toggle.bind(this) 25 | } 26 | 27 | /** 28 | * Toggle ChainBlender. 29 | * @function toggle 30 | */ 31 | async toggle() { 32 | const res = await this.rpc.chainBlender( 33 | this.wallet.isBlending === true ? 'stop' : 'start' 34 | ) 35 | 36 | if ('result' in res === true) { 37 | this.wallet.toggleBlendingStatus() 38 | message.success( 39 | this.t('chainBlender', { 40 | context: this.wallet.isBlending === true ? 'start' : 'stop' 41 | }) 42 | ) 43 | } 44 | } 45 | 46 | render() { 47 | return ( 48 | 53 |

{this.t('toggleChainBlender')}

54 | } 57 | disabled={this.wallet.isLocked === true} 58 | onChange={this.toggle} 59 | size="small" 60 | unCheckedChildren={} 61 | /> 62 | 63 | } 64 | content={ 65 |
66 |
67 |
68 | account_balance_wallet 69 |

{this.t('balanceOnChain')}

70 |
71 |
72 | shuffle 73 |

74 | {this.t('blended')} ( 75 | {new Intl.NumberFormat(this.gui.language, { 76 | maximumFractionDigits: 2 77 | }).format(this.wallet.info.blendedpercentage)} 78 | %) 79 |

80 |
81 |
82 | grain 83 |

{this.t('denominated')}

84 |
85 |
86 |
87 |

88 | {new Intl.NumberFormat(this.gui.language, { 89 | maximumFractionDigits: 6, 90 | minimumFractionDigits: 6 91 | }).format(this.wallet.info.balanceOnChain)}{' '} 92 | XVC 93 |

94 |

95 | {new Intl.NumberFormat(this.gui.language, { 96 | maximumFractionDigits: 6, 97 | minimumFractionDigits: 6 98 | }).format(this.wallet.info.blendedbalance)}{' '} 99 | XVC 100 |

101 |

102 | {new Intl.NumberFormat(this.gui.language, { 103 | maximumFractionDigits: 6, 104 | minimumFractionDigits: 6 105 | }).format(this.wallet.info.denominatedbalance)}{' '} 106 | XVC 107 |

108 |
109 |
110 | } 111 | > 112 | 115 |
116 | ) 117 | } 118 | } 119 | 120 | export default ChainBlender 121 | -------------------------------------------------------------------------------- /src/components/wallet/IncentiveInfo.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { translate } from 'react-i18next' 3 | import { inject, observer } from 'mobx-react' 4 | 5 | /** Ant Design */ 6 | import Tooltip from 'antd/lib/tooltip' 7 | 8 | @translate(['common']) 9 | @inject('gui', 'wallet') 10 | @observer 11 | class IncentiveInfo extends React.Component { 12 | constructor(props) { 13 | super(props) 14 | this.t = props.t 15 | this.gui = props.gui 16 | this.wallet = props.wallet 17 | } 18 | 19 | render() { 20 | const { info } = this.wallet 21 | const { walletaddress } = info 22 | 23 | return ( 24 |
25 | 26 | account_circle 27 | 28 |
29 |

30 | {walletaddress === '' ? this.t('unlockReveal') : walletaddress} 31 |

32 |
33 |
34 |
35 |
36 |
37 | gavel 38 |

{this.t('voteCandidate')}

39 |
40 |
41 | event_seat 42 |

{this.t('collateralizedNodes')}

43 |
44 |
45 | redeem 46 |

{this.t('collateralBalance')}

47 |
48 |
49 |
50 |

51 | {this.t(info.votecandidate === true ? 'yes' : 'no')} 52 |

53 |

54 | {info.collateralized} / {info.endpoints.length} 55 |

56 |

57 | 58 | {new Intl.NumberFormat(this.gui.language, { 59 | maximumFractionDigits: 6 60 | }).format(info.collateralbalance)} 61 | {' '} 62 | /{' '} 63 | {new Intl.NumberFormat(this.gui.language).format( 64 | info.collateralrequired 65 | )}{' '} 66 | XVC 67 |

68 |
69 |
70 |
71 | ) 72 | } 73 | } 74 | 75 | export default IncentiveInfo 76 | -------------------------------------------------------------------------------- /src/components/wallet/NetworkInfo.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { translate } from 'react-i18next' 3 | import { inject, observer } from 'mobx-react' 4 | import { humanReadable } from '../../utilities/common.js' 5 | 6 | @translate(['common']) 7 | @inject('gui', 'wallet') 8 | @observer 9 | class NetworkInfo extends React.Component { 10 | constructor(props) { 11 | super(props) 12 | this.t = props.t 13 | this.gui = props.gui 14 | this.wallet = props.wallet 15 | } 16 | 17 | render() { 18 | return ( 19 |
20 |
21 |
22 |
23 | hearing 24 |

{this.t('listeningOn')}

25 |
26 |
27 | settings_ethernet 28 |

{this.t('portOpen')}

29 |
30 |
31 | games 32 |

{this.t('testnet')}

33 |
34 |
35 |
36 |
37 | account_balance 38 |

{this.t('moneySupply')}

39 |
40 |
41 | card_giftcard 42 |

{this.t('txFee')}

43 |
44 |
45 | card_giftcard 46 |

{this.t('relayFee')}

47 |
48 |
49 |
50 |
51 | grid_on 52 |

{this.t('currentBlockSize')}

53 |
54 |
55 | playlist_add_check 56 |

{this.t('currentBlockTxs')}

57 |
58 |
59 | playlist_add 60 |

{this.t('pooledTxs')}

61 |
62 |
63 |
64 |
65 | network_check 66 |

{this.t('hashRate')}

67 |
68 |
69 | trending_up 70 |

{this.t('powDifficulty')}

71 |
72 |
73 | trending_up 74 |

{this.t('posDifficulty')}

75 |
76 |
77 |
78 |
79 |
80 |

81 | {this.wallet.info.ip}:{this.wallet.info.port} 82 |

83 |

84 | {this.t(this.wallet.info.networkstatus === 'ok' ? 'yes' : 'no')} 85 |

86 |

{this.t(this.wallet.info.testnet === true ? 'yes' : 'no')}

87 |
88 |
89 |

90 | {new Intl.NumberFormat(this.gui.language, { 91 | maximumFractionDigits: 0 92 | }).format(this.wallet.info.moneysupply)}{' '} 93 | XVC 94 |

95 |

{this.wallet.info.paytxfee} XVC / kB

96 |

{this.wallet.info.relayfee} XVC / kB

97 |
98 |
99 |

100 | {humanReadable( 101 | this.wallet.info.currentblocksize, 102 | false, 103 | 'B', 104 | this.gui.language 105 | )} 106 |

107 |

{this.wallet.info.currentblocktx}

108 |

{this.wallet.info.pooledtx}

109 |
110 |
111 |

112 | {humanReadable( 113 | this.wallet.info.networkhashps, 114 | true, 115 | 'H/s', 116 | this.gui.language 117 | )} 118 |

119 |

120 | {new Intl.NumberFormat(this.gui.language, { 121 | maximumFractionDigits: 2 122 | }).format(this.wallet.info['proof-of-work'])} 123 |

124 |

125 | {new Intl.NumberFormat(this.gui.language, { 126 | maximumFractionDigits: 2 127 | }).format(this.wallet.info['proof-of-stake'])} 128 |

129 |
130 |
131 |
132 | ) 133 | } 134 | } 135 | 136 | export default NetworkInfo 137 | -------------------------------------------------------------------------------- /src/components/wallet/Peers.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { translate } from 'react-i18next' 3 | import { inject, observer } from 'mobx-react' 4 | import moment from 'moment' 5 | 6 | /** Ant Design */ 7 | import Table from 'antd/lib/table' 8 | 9 | @translate(['common']) 10 | @inject('gui', 'wallet') 11 | @observer 12 | class Peers extends React.Component { 13 | constructor(props) { 14 | super(props) 15 | this.t = props.t 16 | this.gui = props.gui 17 | this.wallet = props.wallet 18 | } 19 | 20 | render() { 21 | return ( 22 | 30 |

{this.t('peers')}

31 |

{this.t('port')}

32 | 33 | ), 34 | render: (ip, record) => ( 35 |
39 |

{ip}

40 |

{record.port}

41 |
42 | ) 43 | }, 44 | { 45 | dataIndex: 'version', 46 | width: 120, 47 | title: ( 48 |
49 |

{this.t('version')}

50 |

{this.t('os')}

51 |
52 | ), 53 | render: (version, record) => ( 54 |
55 |

{version}

56 |

{record.os}

57 |
58 | ) 59 | }, 60 | { 61 | dataIndex: 'conntime', 62 | width: 300, 63 | title: ( 64 |
65 |

{this.t('connected')}

66 |

{this.t('received')}

67 |
68 | ), 69 | render: (conntime, record) => ( 70 |
71 |

{moment(conntime).fromNow(true)}

72 |

{moment(record.lastrecv).fromNow()}

73 |
74 | ) 75 | }, 76 | { 77 | dataIndex: 'startingheight', 78 | title: ( 79 |
80 |

{this.t('startingHeight')}

81 |

{this.t('banScore')}

82 |
83 | ), 84 | render: (height, record) => ( 85 |
86 |

{new Intl.NumberFormat(this.gui.language).format(height)}

87 |

{record.banscore}/100

88 |
89 | ) 90 | } 91 | ]} 92 | dataSource={this.wallet.peers} 93 | locale={{ emptyText: this.t('notFound') }} 94 | pagination={false} 95 | scroll={this.wallet.peers.length > 8 ? { y: 158 } : {}} 96 | size="small" 97 | /> 98 | ) 99 | } 100 | } 101 | 102 | export default Peers 103 | -------------------------------------------------------------------------------- /src/components/wallet/PrivateKeyDump.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { translate } from 'react-i18next' 3 | import { inject, observer } from 'mobx-react' 4 | import { action, computed, extendObservable, reaction } from 'mobx' 5 | 6 | /** Ant Design */ 7 | import AutoComplete from 'antd/lib/auto-complete' 8 | import Button from 'antd/lib/button' 9 | import Input from 'antd/lib/input' 10 | 11 | @translate(['common']) 12 | @inject('rpc', 'wallet') 13 | @observer 14 | class PrivateKeyDump extends React.Component { 15 | constructor(props) { 16 | super(props) 17 | this.t = props.t 18 | this.rpc = props.rpc 19 | this.wallet = props.wallet 20 | this.dumpPrivKey = this.dumpPrivKey.bind(this) 21 | 22 | /** Errors that will be shown to the user. */ 23 | this.errShow = ['addrChars', 'addrInvalid', 'addrUnknown'] 24 | 25 | /** Extend the component with observable properties. */ 26 | extendObservable(this, { address: '', privateKey: '', rpcError: '' }) 27 | 28 | /** Clear private key and previous RPC error when the address is updated. */ 29 | this.addrReaction = reaction( 30 | () => this.address, 31 | address => { 32 | if (this.privateKey !== '' || this.rpcError !== '') { 33 | this.setProps({ privateKey: '', rpcError: '' }) 34 | } 35 | }, 36 | { name: 'PrivateKeyDump: address changed, clearing key and RPC error.' } 37 | ) 38 | } 39 | 40 | /** Dispose of reaction on component unmount. */ 41 | componentWillUnmount() { 42 | this.addrReaction() 43 | } 44 | 45 | /** 46 | * Get present error or empty string if none. 47 | * @function errorStatus 48 | * @return {string} Error status. 49 | */ 50 | @computed 51 | get errorStatus() { 52 | if (this.address.match(/^[a-z0-9]*$/i) === null) return 'addrChars' 53 | if (this.address.length < 34) return 'addrShort' 54 | if (this.address.length > 35) return 'addrLong' 55 | if (this.rpcError !== '') return this.rpcError 56 | return '' 57 | } 58 | 59 | /** 60 | * Set observable properties. 61 | * @function setProps 62 | * @param {object} props - Key value combinations. 63 | */ 64 | @action 65 | setProps = props => { 66 | Object.keys(props).forEach(key => (this[key] = props[key])) 67 | } 68 | 69 | /** 70 | * Dump private key. 71 | * @function dumpPrivKey 72 | */ 73 | async dumpPrivKey() { 74 | const res = await this.rpc.dumpPrivKey(this.address) 75 | 76 | if ('result' in res === true) { 77 | this.setProps({ privateKey: res.result }) 78 | } 79 | 80 | if ('error' in res === true) { 81 | switch (res.error.code) { 82 | case -4: 83 | return this.setProps({ rpcError: 'addrUnknown' }) 84 | case -5: 85 | return this.setProps({ rpcError: 'addrInvalid' }) 86 | } 87 | } 88 | } 89 | 90 | render() { 91 | return ( 92 |
93 |
94 | vpn_key 95 |

{this.t('pkDumpDesc')}

96 |
97 |
98 |

{this.t('address')}

99 | triggerNode.parentNode} 103 | onChange={address => this.setProps({ address })} 104 | placeholder={this.t('address')} 105 | style={{ flex: 1 }} 106 | value={this.address} 107 | /> 108 |
109 |
110 |

{this.t('pk')}

111 | 117 |
118 |
119 |

120 | {this.errShow.includes(this.errorStatus) === true && 121 | this.t(this.errorStatus)} 122 |

123 | 129 |
130 |
131 | ) 132 | } 133 | } 134 | 135 | export default PrivateKeyDump 136 | -------------------------------------------------------------------------------- /src/components/wallet/PrivateKeyImport.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { translate } from 'react-i18next' 3 | import { inject, observer } from 'mobx-react' 4 | import { action, computed, extendObservable, reaction } from 'mobx' 5 | 6 | /** Ant Design */ 7 | import AutoComplete from 'antd/lib/auto-complete' 8 | import Button from 'antd/lib/button' 9 | import Input from 'antd/lib/input' 10 | import message from 'antd/lib/message' 11 | 12 | @translate(['common']) 13 | @inject('rpc', 'wallet') 14 | @observer 15 | class PrivateKeyImport extends React.Component { 16 | constructor(props) { 17 | super(props) 18 | this.t = props.t 19 | this.rpc = props.rpc 20 | this.wallet = props.wallet 21 | this.importPrivKey = this.importPrivKey.bind(this) 22 | 23 | /** Errors that will be shown to the user. */ 24 | this.errShow = ['accChars', 'pkInvalid', 'pkIsMine'] 25 | 26 | /** Extend the component with observable properties. */ 27 | extendObservable(this, { 28 | account: '', 29 | privateKey: '', 30 | rpcError: '', 31 | loading: false 32 | }) 33 | 34 | /** Clear previous RPC error when the private key is updated. */ 35 | this.pkReaction = reaction( 36 | () => this.privateKey, 37 | privateKey => { 38 | if (this.rpcError !== '') this.setProps({ rpcError: '' }) 39 | }, 40 | { name: 'PrivateKeyImport: private key changed, clearing RPC error.' } 41 | ) 42 | } 43 | 44 | /** Dispose of reaction on component unmount. */ 45 | componentWillUnmount() { 46 | this.pkReaction() 47 | } 48 | 49 | /** 50 | * Get present error or empty string if none. 51 | * @function errorStatus 52 | * @return {string} Error status. 53 | */ 54 | @computed 55 | get errorStatus() { 56 | if (this.account.match(/^[a-z0-9 -]*$/i) === null) return 'accChars' 57 | if (this.account.length > 100) return 'accLength' 58 | if (this.privateKey.match(/^[a-z0-9]*$/i) === null) return 'pkInvalid' 59 | if (this.privateKey.length < 51) return 'pkShort' 60 | if (this.privateKey.length > 52) return 'pkLong' 61 | if (this.rpcError !== '') return this.rpcError 62 | return '' 63 | } 64 | 65 | /** 66 | * Set observable properties. 67 | * @function setProps 68 | * @param {object} props - Key value combinations. 69 | */ 70 | @action 71 | setProps = props => { 72 | Object.keys(props).forEach(key => (this[key] = props[key])) 73 | } 74 | 75 | /** 76 | * Import private key. 77 | * @function importPrivKey 78 | */ 79 | async importPrivKey() { 80 | /** Show the loading indicator, which will disable the button. */ 81 | this.setProps({ loading: !this.loading }) 82 | 83 | const res = await this.rpc.importPrivKey(this.privateKey, this.account) 84 | 85 | /** Hide the loading indicator, which will re-enable the button. */ 86 | this.setProps({ loading: !this.loading }) 87 | 88 | if ('result' in res === true) { 89 | this.wallet.updateAddresses() 90 | this.wallet.updateWallet(true) 91 | message.success(this.t('pkImported')) 92 | } 93 | 94 | if ('error' in res === true) { 95 | switch (res.error.code) { 96 | case -4: 97 | return this.setProps({ rpcError: 'pkIsMine' }) 98 | case -5: 99 | return this.setProps({ rpcError: 'pkInvalid' }) 100 | } 101 | } 102 | } 103 | 104 | render() { 105 | return ( 106 |
107 |
108 | vpn_key 109 |

{this.t('pkImportDesc')}

110 |
111 |
112 |

{this.t('pk')}

113 | this.setProps({ privateKey: e.target.value })} 115 | placeholder={this.t('pk')} 116 | style={{ flex: 1 }} 117 | value={this.privateKey} 118 | /> 119 |
120 |
121 |

{this.t('acc')}

122 | triggerNode.parentNode} 126 | onChange={account => this.setProps({ account })} 127 | placeholder={this.t('accName')} 128 | style={{ flex: 1 }} 129 | value={this.account} 130 | /> 131 |
132 |
133 |

134 | {this.errShow.includes(this.errorStatus) === true && 135 | this.t(this.errorStatus)} 136 |

137 | 144 |
145 |
146 | ) 147 | } 148 | } 149 | 150 | export default PrivateKeyImport 151 | -------------------------------------------------------------------------------- /src/components/wallet/WalletBackup.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { translate } from 'react-i18next' 3 | import { action, computed, extendObservable } from 'mobx' 4 | import { inject, observer } from 'mobx-react' 5 | import { remote } from 'electron' 6 | import { join, sep } from 'path' 7 | import { dataPath } from '../../utilities/common.js' 8 | 9 | /** Ant Design */ 10 | import Button from 'antd/lib/button' 11 | import Input from 'antd/lib/input' 12 | import message from 'antd/lib/message' 13 | 14 | @translate(['common']) 15 | @inject('rpc') 16 | @observer 17 | class WalletBackup extends React.Component { 18 | constructor(props) { 19 | super(props) 20 | this.t = props.t 21 | this.rpc = props.rpc 22 | this.backupWallet = this.backupWallet.bind(this) 23 | 24 | /** Errors that will be shown to the user. */ 25 | this.errShow = ['backupFailed'] 26 | 27 | /** Extend the component with observable properties. */ 28 | extendObservable(this, { 29 | path: 30 | this.rpc.connection.status.tunnel === true 31 | ? '' 32 | : join(dataPath(), 'backups', sep), 33 | rpcError: '' 34 | }) 35 | } 36 | 37 | /** 38 | * Get present error or empty string if none. 39 | * @function errorStatus 40 | * @return {string} Error status. 41 | */ 42 | @computed 43 | get errorStatus() { 44 | if (this.rpcError !== '') return this.rpcError 45 | return '' 46 | } 47 | 48 | /** 49 | * Set observable properties. 50 | * @function setProps 51 | * @param {object} props - Key value combinations. 52 | */ 53 | @action 54 | setProps = props => { 55 | Object.keys(props).forEach(key => (this[key] = props[key])) 56 | } 57 | 58 | /** 59 | * Get backup path. 60 | * @function getPath 61 | */ 62 | getPath = () => { 63 | /** Open directory browser. */ 64 | const selected = remote.dialog.showOpenDialog({ 65 | properties: ['openDirectory'] 66 | }) 67 | 68 | /** Set selected path. */ 69 | if (typeof selected !== 'undefined') { 70 | this.setProps({ path: join(selected[0], sep) }) 71 | } 72 | } 73 | 74 | /** 75 | * Backup the wallet. 76 | * @function backupWallet 77 | */ 78 | async backupWallet() { 79 | const res = await this.rpc.backupWallet(this.path) 80 | 81 | if ('result' in res === true) { 82 | message.success(this.t('backedUp')) 83 | } 84 | 85 | if ('error' in res === true) { 86 | switch (res.error.code) { 87 | case -4: 88 | return this.setProps({ rpcError: 'backupFailed' }) 89 | } 90 | } 91 | } 92 | 93 | render() { 94 | return ( 95 |
96 |
97 | save 98 |

{this.t('backupDesc')}

99 |
100 |
101 |

{this.t('saveInto')}

102 | 111 |
112 |
113 |

114 | {this.errShow.includes(this.errorStatus) === true && 115 | this.t(this.errorStatus)} 116 |

117 |
118 | 125 | 126 |
127 |
128 |
129 | ) 130 | } 131 | } 132 | 133 | export default WalletBackup 134 | -------------------------------------------------------------------------------- /src/components/wallet/WalletDump.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { translate } from 'react-i18next' 3 | import { inject, observer } from 'mobx-react' 4 | import { dataPath } from '../../utilities/common.js' 5 | 6 | /** Ant Design */ 7 | import Button from 'antd/lib/button' 8 | import Input from 'antd/lib/input' 9 | import message from 'antd/lib/message' 10 | 11 | @translate(['common']) 12 | @inject('rpc', 'wallet') 13 | @observer 14 | class WalletDump extends React.Component { 15 | constructor(props) { 16 | super(props) 17 | this.t = props.t 18 | this.rpc = props.rpc 19 | this.wallet = props.wallet 20 | this.dumpWallet = this.dumpWallet.bind(this) 21 | } 22 | 23 | /** 24 | * Dump the wallet. 25 | * @function dumpWallet 26 | */ 27 | async dumpWallet() { 28 | const res = await this.rpc.dumpWallet() 29 | 30 | if ('result' in res === true) { 31 | message.success(this.t('dumped')) 32 | } 33 | } 34 | 35 | render() { 36 | return ( 37 |
38 |
39 | assignment 40 |

{this.t('dumpDesc')}

41 |
42 |
43 |

{this.t('saveInto')}

44 | 53 |
54 |
55 | 61 |
62 |
63 | ) 64 | } 65 | } 66 | 67 | export default WalletDump 68 | -------------------------------------------------------------------------------- /src/components/wallet/WalletEncrypt.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { translate } from 'react-i18next' 3 | import { action, computed, extendObservable } from 'mobx' 4 | import { inject, observer } from 'mobx-react' 5 | 6 | /** Ant Design */ 7 | import Button from 'antd/lib/button' 8 | import Input from 'antd/lib/input' 9 | import notification from 'antd/lib/notification' 10 | 11 | @translate(['common']) 12 | @inject('rpc', 'wallet') 13 | @observer 14 | class WalletEncrypt extends React.Component { 15 | constructor(props) { 16 | super(props) 17 | this.t = props.t 18 | this.rpc = props.rpc 19 | this.wallet = props.wallet 20 | this.encryptWallet = this.encryptWallet.bind(this) 21 | 22 | /** Errors that will be shown to the user. */ 23 | this.errShow = ['ppNotMatching'] 24 | 25 | /** Extend the component with observable properties. */ 26 | extendObservable(this, { passphrase: '', repeat: '' }) 27 | } 28 | 29 | /** 30 | * Get present error or empty string if none. 31 | * @function errorStatus 32 | * @return {string} Error status. 33 | */ 34 | @computed 35 | get errorStatus() { 36 | const len = { pass: this.passphrase.length, repeat: this.repeat.length } 37 | 38 | if (len.pass < 1 || len.repeat < 1) return 'emptyFields' 39 | if (len.pass !== len.repeat) return 'differentLengths' 40 | if (this.passphrase !== this.repeat) return 'ppNotMatching' 41 | return '' 42 | } 43 | 44 | /** 45 | * Set observable properties. 46 | * @function setProps 47 | * @param {object} props - Key value combinations. 48 | */ 49 | @action 50 | setProps = props => { 51 | Object.keys(props).forEach(key => (this[key] = props[key])) 52 | } 53 | 54 | /** 55 | * Encrypt the wallet. 56 | * @function encryptWallet 57 | */ 58 | async encryptWallet() { 59 | const res = await this.rpc.encryptWallet(this.passphrase) 60 | 61 | if ('result' in res === true) { 62 | this.wallet.updateLockStatus() 63 | this.setProps({ passphrase: '', repeat: '' }) 64 | 65 | /** Display a non-expiring restart notification. */ 66 | notification.success({ 67 | message: this.t('encrypted'), 68 | description: this.t('encryptedDesc'), 69 | duration: 0 70 | }) 71 | } 72 | } 73 | 74 | render() { 75 | /** Do not render if the wallet is encrypted. */ 76 | if (this.wallet.isEncrypted === true) return null 77 | return ( 78 |
79 |
80 | vpn_key 81 |

{this.t('encryptDesc')}

82 |
83 |
84 |

{this.t('pp')}

85 | this.setProps({ passphrase: e.target.value })} 87 | placeholder={this.t('ppDesc')} 88 | style={{ flex: 1 }} 89 | type="password" 90 | value={this.passphrase} 91 | /> 92 |
93 |
94 |

{this.t('ppRepeat')}

95 | this.setProps({ repeat: e.target.value })} 97 | placeholder={this.t('ppRepeatDesc')} 98 | style={{ flex: 1 }} 99 | type="password" 100 | value={this.repeat} 101 | /> 102 |
103 |
104 |

105 | {this.errShow.includes(this.errorStatus) === true && 106 | this.t(this.errorStatus)} 107 |

108 | 114 |
115 |
116 | ) 117 | } 118 | } 119 | 120 | export default WalletEncrypt 121 | -------------------------------------------------------------------------------- /src/components/wallet/WalletLock.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { translate } from 'react-i18next' 3 | import { inject, observer } from 'mobx-react' 4 | 5 | /** Ant Design */ 6 | import Button from 'antd/lib/button' 7 | import Tooltip from 'antd/lib/tooltip' 8 | 9 | @translate(['common']) 10 | @inject('rpc', 'wallet') 11 | @observer 12 | class WalletLock extends React.Component { 13 | constructor(props) { 14 | super(props) 15 | this.t = props.t 16 | this.rpc = props.rpc 17 | this.wallet = props.wallet 18 | this.walletLock = this.walletLock.bind(this) 19 | } 20 | 21 | /** 22 | * Lock the wallet. 23 | * @function walletLock 24 | */ 25 | async walletLock() { 26 | const res = await this.rpc.walletLock() 27 | 28 | if ('result' in res === true) { 29 | this.wallet.updateLockStatus() 30 | } 31 | } 32 | 33 | render() { 34 | const { isEncrypted, isLocked } = this.wallet 35 | 36 | /** Do not render if the wallet is not encrypted or is locked. */ 37 | if (isEncrypted === false || isLocked === true) return null 38 | return ( 39 | 40 | 43 | 44 | ) 45 | } 46 | } 47 | 48 | export default WalletLock 49 | -------------------------------------------------------------------------------- /src/components/wallet/WalletPassphraseChange.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { translate } from 'react-i18next' 3 | import { action, computed, extendObservable, reaction } from 'mobx' 4 | import { inject, observer } from 'mobx-react' 5 | 6 | /** Ant Design */ 7 | import Button from 'antd/lib/button' 8 | import Input from 'antd/lib/input' 9 | import message from 'antd/lib/message' 10 | 11 | @translate(['common']) 12 | @inject('rpc', 'wallet') 13 | @observer 14 | class WalletPassphraseChange extends React.Component { 15 | constructor(props) { 16 | super(props) 17 | this.t = props.t 18 | this.rpc = props.rpc 19 | this.wallet = props.wallet 20 | this.walletPassphraseChange = this.walletPassphraseChange.bind(this) 21 | 22 | /** Errors that will be shown to the user. */ 23 | this.errShow = ['ppIncorrect', 'ppEqual', 'ppNotMatching'] 24 | 25 | /** Extend the component with observable properties. */ 26 | extendObservable(this, { current: '', next: '', repeat: '', rpcError: '' }) 27 | 28 | /** Clear previous RPC error on current passphrase change. */ 29 | this.ppReaction = reaction( 30 | () => this.current, 31 | current => { 32 | if (this.rpcError !== false) this.setProps({ rpcError: '' }) 33 | }, 34 | { name: 'WalletPassChange: pass changed, clearing previous RPC error.' } 35 | ) 36 | } 37 | 38 | /** Dispose of reaction on component unmount. */ 39 | componentWillUnmount() { 40 | this.ppReaction() 41 | } 42 | 43 | /** 44 | * Get present error or empty string if none. 45 | * @function errorStatus 46 | * @return {string} Error status. 47 | */ 48 | @computed 49 | get errorStatus() { 50 | const len = { 51 | old: this.current.length, 52 | next: this.next.length, 53 | repeat: this.repeat.length 54 | } 55 | 56 | if (len.old < 1 || len.next < 1 || len.repeat < 1) return 'emptyFields' 57 | if (this.next === this.current) return 'ppEqual' 58 | if (len.next !== len.repeat) return 'differentLengths' 59 | if (this.next !== this.repeat) return 'ppNotMatching' 60 | if (this.rpcError !== '') return this.rpcError 61 | return '' 62 | } 63 | 64 | /** 65 | * Set observable properties. 66 | * @function setProps 67 | * @param {object} props - Key value combinations. 68 | */ 69 | @action 70 | setProps = props => { 71 | Object.keys(props).forEach(key => (this[key] = props[key])) 72 | } 73 | 74 | /** 75 | * Change wallet passphrase. 76 | * @function walletPassphraseChange 77 | */ 78 | async walletPassphraseChange() { 79 | const res = await this.rpc.walletPassphraseChange(this.current, this.next) 80 | 81 | if ('result' in res) { 82 | this.wallet.updateLockStatus() 83 | this.setProps({ current: '', next: '', repeat: '' }) 84 | message.success(this.t('ppChanged')) 85 | } 86 | 87 | if ('error' in res) { 88 | switch (res.error.code) { 89 | case -14: 90 | return this.setProps({ rpcError: 'ppIncorrect' }) 91 | } 92 | } 93 | } 94 | 95 | render() { 96 | /** Do not render if the wallet is not encrypted. */ 97 | if (this.wallet.isEncrypted === false) return null 98 | return ( 99 |
100 |
101 | vpn_key 102 |

{this.t('ppChangeDesc')}

103 |
104 |
105 |

{this.t('pp')}

106 | this.setProps({ current: e.target.value })} 108 | placeholder={this.t('ppDesc')} 109 | style={{ flex: 1 }} 110 | type="password" 111 | value={this.current} 112 | /> 113 |
114 |
115 |

{this.t('ppNew')}

116 | this.setProps({ next: e.target.value })} 118 | placeholder={this.t('ppNewDesc')} 119 | style={{ flex: 1 }} 120 | type="password" 121 | value={this.next} 122 | /> 123 |
124 |
125 |

{this.t('ppRepeat')}

126 | this.setProps({ repeat: e.target.value })} 128 | placeholder={this.t('ppRepeatDesc')} 129 | style={{ flex: 1 }} 130 | type="password" 131 | value={this.repeat} 132 | /> 133 |
134 |
135 |

136 | {this.errShow.includes(this.errorStatus) === true && 137 | this.t(this.errorStatus)} 138 |

139 | 145 |
146 |
147 | ) 148 | } 149 | } 150 | 151 | export default WalletPassphraseChange 152 | -------------------------------------------------------------------------------- /src/components/wallet/WalletRepair.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { translate } from 'react-i18next' 3 | import { action, extendObservable } from 'mobx' 4 | import { inject, observer } from 'mobx-react' 5 | 6 | /** Ant Design */ 7 | import Button from 'antd/lib/button' 8 | 9 | @translate(['common']) 10 | @inject('gui', 'rpc') 11 | @observer 12 | class WalletRepair extends React.Component { 13 | constructor(props) { 14 | super(props) 15 | this.t = props.t 16 | this.gui = props.gui 17 | this.rpc = props.rpc 18 | this.crWallet = this.crWallet.bind(this) 19 | 20 | /** Extend the component with observable properties. */ 21 | extendObservable(this, { 22 | amountAffected: 0, 23 | checkPassed: null, 24 | mismatchedSpent: 0 25 | }) 26 | 27 | /** Check the wallet when component loads. */ 28 | this.crWallet() 29 | } 30 | 31 | /** 32 | * Set observable properties. 33 | * @function setProps 34 | * @param {object} props - Key value combinations. 35 | */ 36 | @action 37 | setProps(props) { 38 | Object.keys(props).forEach(key => (this[key] = props[key])) 39 | } 40 | 41 | /** 42 | * Check for and repair wallet inconsistencies. 43 | * @function crWallet 44 | * @param {boolean} checkOnly - Check for inconsistencies without repairing. 45 | */ 46 | async crWallet(checkOnly = true) { 47 | const method = checkOnly === true ? 'checkWallet' : 'repairWallet' 48 | const res = await this.rpc[method]() 49 | 50 | if ('result' in res === true) { 51 | const result = res.result 52 | const cp = 'wallet check passed' in result 53 | 54 | /** Re-check after repairing. */ 55 | if (this.checkPassed === false && cp === false) return this.crWallet() 56 | 57 | /** Set checkPassed, amount affected and mismatched spent. */ 58 | this.setProps({ 59 | amountAffected: cp === true ? 0 : result['amount affected by repair'], 60 | checkPassed: cp, 61 | mismatchedSpent: cp === true ? 0 : result['mismatched spent coins'] 62 | }) 63 | } 64 | } 65 | 66 | render() { 67 | return ( 68 |
69 |
70 | build 71 |

{this.t('repairDesc')}

72 |
73 |
74 |

{this.t('status')}

75 | {this.checkPassed !== false && ( 76 |
77 |
78 | {this.checkPassed !== null &&

{this.t('checkPassed')}

} 79 |
80 | 81 |
82 | )} 83 | {this.checkPassed === false && ( 84 |
85 |
86 |
87 |

88 | {this.t('mismatched')}:{' '} 89 | 90 | {new Intl.NumberFormat(this.gui.language, { 91 | maximumFractionDigits: 6 92 | }).format(this.mismatchedSpent)}{' '} 93 | XVC 94 | 95 |

96 |

97 | {this.t('amountAffected')}:{' '} 98 | 99 | {new Intl.NumberFormat(this.gui.language, { 100 | maximumFractionDigits: 6 101 | }).format(this.amountAffected)}{' '} 102 | XVC 103 | 104 |

105 |
106 |
107 | 110 |
111 | )} 112 |
113 |
114 | ) 115 | } 116 | } 117 | 118 | export default WalletRepair 119 | -------------------------------------------------------------------------------- /src/components/wallet/WalletSeedDump.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { translate } from 'react-i18next' 3 | import { action, computed, extendObservable } from 'mobx' 4 | import { inject, observer } from 'mobx-react' 5 | 6 | /** Ant Design */ 7 | import Button from 'antd/lib/button' 8 | import Input from 'antd/lib/input' 9 | 10 | @translate(['common']) 11 | @inject('rpc', 'wallet') 12 | @observer 13 | class WalletSeedDump extends React.Component { 14 | constructor(props) { 15 | super(props) 16 | this.t = props.t 17 | this.rpc = props.rpc 18 | this.wallet = props.wallet 19 | this.dumpWalletSeed = this.dumpWalletSeed.bind(this) 20 | 21 | /** Errors that will be shown to the user. */ 22 | this.errShow = ['notDeterministic'] 23 | 24 | /** Extend the component with observable properties. */ 25 | extendObservable(this, { seed: '', rpcError: '' }) 26 | } 27 | 28 | /** 29 | * Get present error or empty string if none. 30 | * @function errorStatus 31 | * @return {string} Error status. 32 | */ 33 | @computed 34 | get errorStatus() { 35 | if (this.rpcError !== '') return this.rpcError 36 | return '' 37 | } 38 | 39 | /** 40 | * Set observable properties. 41 | * @function setProps 42 | * @param {object} props - Key value combinations. 43 | */ 44 | @action 45 | setProps = props => { 46 | Object.keys(props).forEach(key => (this[key] = props[key])) 47 | } 48 | 49 | /** 50 | * Dump wallet seed. 51 | * @function dumpWalletSeed 52 | */ 53 | async dumpWalletSeed() { 54 | const res = await this.rpc.dumpWalletSeed() 55 | 56 | if ('result' in res === true) { 57 | this.setProps({ seed: res.result }) 58 | } 59 | 60 | if ('error' in res === true) { 61 | switch (res.error.code) { 62 | case -4: 63 | return this.setProps({ rpcError: 'notDeterministic' }) 64 | } 65 | } 66 | } 67 | 68 | render() { 69 | return ( 70 |
71 |
72 | fingerprint 73 |

{this.t('seedDumpDesc')}

74 |
75 |
76 |

{this.t('seed')}

77 | 83 |
84 |
85 |

86 | {this.errShow.includes(this.errorStatus) === true && 87 | this.t(this.errorStatus)} 88 |

89 | 95 |
96 |
97 | ) 98 | } 99 | } 100 | 101 | export default WalletSeedDump 102 | -------------------------------------------------------------------------------- /src/components/wallet/WalletUnlock.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { translate } from 'react-i18next' 3 | import { action, computed, extendObservable, reaction } from 'mobx' 4 | import { inject, observer } from 'mobx-react' 5 | 6 | /** Ant Design */ 7 | import Button from 'antd/lib/button' 8 | import Input from 'antd/lib/input' 9 | import message from 'antd/lib/message' 10 | import Modal from 'antd/lib/modal' 11 | import Tooltip from 'antd/lib/tooltip' 12 | 13 | @translate(['common']) 14 | @inject('rpc', 'wallet') 15 | @observer 16 | class WalletUnlock extends React.Component { 17 | constructor(props) { 18 | super(props) 19 | this.t = props.t 20 | this.rpc = props.rpc 21 | this.wallet = props.wallet 22 | this.walletPassphrase = this.walletPassphrase.bind(this) 23 | 24 | /** Errors that will be shown to the user. */ 25 | this.errShow = ['ppIncorrect'] 26 | 27 | /** Extend the component with observable properties. */ 28 | extendObservable(this, { 29 | passphrase: '', 30 | rpcError: '', 31 | modalVisible: false 32 | }) 33 | 34 | /** Clear previous RPC error on passphrase change. */ 35 | this.ppReaction = reaction( 36 | () => this.passphrase, 37 | passphrase => { 38 | if (this.rpcError !== '') this.setProps({ rpcError: '' }) 39 | }, 40 | { name: 'WalletUnlock: passphrase changed, clearing previous RPC error.' } 41 | ) 42 | 43 | /** Clear passphrase when the modal gets hidden. */ 44 | this.modalReaction = reaction( 45 | () => this.modalVisible, 46 | modalVisible => { 47 | if (modalVisible === false) { 48 | if (this.passphrase !== '') this.setProps({ passphrase: '' }) 49 | } 50 | }, 51 | { name: 'WalletUnlock: modal toggled, clearing previous passphrase.' } 52 | ) 53 | } 54 | 55 | /** Dispose of reactions on component unmount. */ 56 | componentWillUnmount() { 57 | this.ppReaction() 58 | this.modalReaction() 59 | } 60 | 61 | /** 62 | * Get present error or empty string if none. 63 | * @function errorStatus 64 | * @return {string} Error status. 65 | */ 66 | @computed 67 | get errorStatus() { 68 | if (this.passphrase.length < 1) return 'emptyField' 69 | if (this.rpcError !== '') return this.rpcError 70 | return '' 71 | } 72 | 73 | /** 74 | * Set observable properties. 75 | * @function setProps 76 | * @param {object} props - Key value combinations. 77 | */ 78 | @action 79 | setProps = props => { 80 | Object.keys(props).forEach(key => (this[key] = props[key])) 81 | } 82 | 83 | /** 84 | * Toggle modal visibility. 85 | * @function toggleModal 86 | */ 87 | @action 88 | toggleModal = () => { 89 | this.modalVisible = !this.modalVisible 90 | } 91 | 92 | /** 93 | * Unlock the wallet. 94 | * @function walletPassphrase 95 | */ 96 | async walletPassphrase() { 97 | const res = await this.rpc.walletPassphrase(this.passphrase) 98 | 99 | if ('result' in res === true) { 100 | this.wallet.updateLockStatus() 101 | this.toggleModal() 102 | message.success(this.t('unlocked')) 103 | } 104 | 105 | if ('error' in res === true) { 106 | switch (res.error.code) { 107 | case -14: 108 | return this.setProps({ rpcError: 'ppIncorrect' }) 109 | } 110 | } 111 | } 112 | 113 | render() { 114 | /** Do not render if the wallet is unlocked. */ 115 | if (this.wallet.isLocked === false) return null 116 | return ( 117 |
118 | 124 | this.setProps({ passphrase: e.target.value })} 126 | onPressEnter={this.walletPassphrase} 127 | placeholder={this.t('ppDesc')} 128 | style={{ width: '100%', margin: '0 0 5px 0' }} 129 | type="password" 130 | value={this.passphrase} 131 | /> 132 |
133 |

134 | {this.errShow.includes(this.errorStatus) === true && 135 | this.t(this.errorStatus)} 136 |

137 | 143 |
144 |
145 | 146 | 149 | 150 |
151 | ) 152 | } 153 | } 154 | 155 | export default WalletUnlock 156 | -------------------------------------------------------------------------------- /src/electron.js: -------------------------------------------------------------------------------- 1 | import { app, BrowserWindow, shell } from 'electron' 2 | import { join } from 'path' 3 | import { format } from 'url' 4 | 5 | /** Store instance */ 6 | import daemon from './stores/daemon.js' 7 | 8 | /** Global reference of the window object. */ 9 | let mainWindow = null 10 | 11 | /** Set application name. */ 12 | app.setName('Vcash Electron GUI') 13 | 14 | /** Keep separate userData directories for development and production modes. */ 15 | app.setPath( 16 | 'userData', 17 | join( 18 | app.getPath('appData'), 19 | ''.concat(app.getName(), process.env.NODE_ENV === 'dev' ? ' dev' : '') 20 | ) 21 | ) 22 | 23 | /** Electron initialization finished. */ 24 | app.on('ready', () => { 25 | /** Return default window height depending on the platform. */ 26 | const height = () => { 27 | if (process.platform === 'win32') return 738 28 | if (process.platform === 'darwin') return 722 29 | return 700 30 | } 31 | 32 | /** Create the main window. */ 33 | mainWindow = new BrowserWindow({ 34 | height: height(), 35 | icon: join(__dirname, 'assets', 'images', 'logoRed.png'), 36 | width: 1200 37 | }) 38 | 39 | /** Hide Chromium menu bar. */ 40 | mainWindow.setMenu(null) 41 | 42 | /** Load the application entry point. */ 43 | mainWindow.loadURL( 44 | format({ 45 | pathname: join(__dirname, 'index.html'), 46 | protocol: 'file:', 47 | slashes: true 48 | }) 49 | ) 50 | 51 | /** Open Chromium DevTools in development mode. */ 52 | if (process.env.NODE_ENV === 'dev') mainWindow.webContents.openDevTools() 53 | 54 | /** Open external links using OS default browser. */ 55 | mainWindow.webContents.on('new-window', (e, url) => { 56 | if (url !== mainWindow.webContents.getURL()) { 57 | e.preventDefault() 58 | shell.openExternal(url) 59 | } 60 | }) 61 | 62 | /** Main window closed. */ 63 | mainWindow.on('closed', () => { 64 | /** Stop the daemon process. */ 65 | daemon.stop() 66 | 67 | /** Dereference the window object. */ 68 | mainWindow = null 69 | }) 70 | }) 71 | 72 | /** Quit once all of the windows are closed. */ 73 | app.on('window-all-closed', () => app.quit()) 74 | -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 19 | Vcash Electron GUI 20 | 21 | 22 | 23 | 24 |
25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Vcash Electron GUI 3 | * Copyright (C) 2015-2018, whphhg 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | import React from 'react' 19 | import { render } from 'react-dom' 20 | import { translate } from 'react-i18next' 21 | import { BrowserRouter, Route } from 'react-router-dom' 22 | import { useStrict } from 'mobx' 23 | import { enableLogging } from 'mobx-logger' 24 | import { Provider } from 'mobx-react' 25 | import { join } from 'path' 26 | import i18next from './utilities/i18next.js' 27 | import './utilities/rightClickMenu.js' 28 | 29 | /** Components */ 30 | import Connections from './components/screens/Connections.js' 31 | import MainMenu from './components/menus/MainMenu.js' 32 | import Root from './components/Root.js' 33 | 34 | /** Store instances */ 35 | import connections from './stores/connections.js' 36 | import gui from './stores/gui.js' 37 | import rates from './stores/rates.js' 38 | 39 | /** Set the i18next instance. */ 40 | translate.setI18n(i18next) 41 | 42 | /** Use MobX strict mode, allowing only actions to alter the state. */ 43 | useStrict(true) 44 | 45 | /** Enable MobX logging in development mode. */ 46 | if (process.env.NODE_ENV === 'dev') enableLogging() 47 | 48 | /** Override and disable eval, which allows strings to be executed as code. */ 49 | // prettier-ignore 50 | window.eval = global.eval = () => { // eslint-disable-line 51 | throw new Error('eval() is disabled for security reasons.') 52 | } 53 | 54 | render( 55 | 56 | 57 |
58 | 64 | 65 | 66 |
67 |
68 |
, 69 | document.getElementById('application-root') 70 | ) 71 | -------------------------------------------------------------------------------- /src/stores/connections.js: -------------------------------------------------------------------------------- 1 | import { action, computed, extendObservable, reaction } from 'mobx' 2 | import { getItem, setItem } from '../utilities/localStorage.js' 3 | 4 | /** Store classes */ 5 | import Connection from './connection.js' 6 | 7 | class Connections { 8 | /** 9 | * @prop {map} instances - Connection instances. 10 | * @prop {array} ids - Connection IDs in the instance order. 11 | * @prop {string} viewingId - Viewing connection ID. 12 | */ 13 | constructor() { 14 | this.instances = new Map() 15 | 16 | /** Get connection configs saved in local storage. */ 17 | let saved = getItem('connections') 18 | 19 | /** TODO: Remove in a future release. */ 20 | Object.keys(saved).forEach(id => { 21 | if ('uid' in saved[id] === false) return 22 | 23 | /** Replace uid with id, which was used prior to v0.33.17. */ 24 | saved[id].id = saved[id].uid 25 | delete saved[id].uid 26 | }) 27 | 28 | /** Extend the store with observable properties. */ 29 | extendObservable(this, { 30 | ids: 31 | typeof saved === 'undefined' 32 | ? [] 33 | : Object.keys(saved).reduce((ids, connection) => { 34 | /** Initialize saved connection instances and set IDs. */ 35 | this.instances.set(connection, new Connection(saved[connection])) 36 | ids.push(connection) 37 | return ids 38 | }, []), 39 | viewingId: 40 | typeof saved === 'undefined' ? '' : this.instances.keys().next().value 41 | }) 42 | 43 | /** Add and auto-start new connection if there are no saved configs. */ 44 | if (this.ids.length === 0) { 45 | this.add() 46 | this.instances.get(this.ids[0]).start() 47 | } else { 48 | /** Auto-start the first local connection if there are saved configs. */ 49 | for (let instance of this.instances.values()) { 50 | if (instance.type === 'local') { 51 | this.setViewing(instance.id) 52 | instance.start() 53 | break 54 | } 55 | } 56 | } 57 | 58 | /** Always have one connection available. */ 59 | reaction( 60 | () => this.ids.length, 61 | connections => { 62 | if (connections === 0) this.add() 63 | }, 64 | { 65 | fireImmediately: true, 66 | name: 'Connections: checking if at least one connection is available.' 67 | } 68 | ) 69 | 70 | /** Auto-save updated connection configs to local storage with 3s delay. */ 71 | reaction( 72 | () => { 73 | return this.ids.reduce((connections, id) => { 74 | connections[id] = this.instances.get(id).config 75 | return connections 76 | }, {}) 77 | }, 78 | connections => setItem('connections', connections), 79 | { 80 | delay: 3 * 1000, 81 | name: 'Connections: auto-saving updated configs to local storage.' 82 | } 83 | ) 84 | } 85 | 86 | /** 87 | * Get the instance of the viewing connection. 88 | * @function viewing 89 | * @return {object} Viewing connection instance. 90 | */ 91 | @computed 92 | get viewing() { 93 | return this.instances.get(this.viewingId) 94 | } 95 | 96 | /** 97 | * Add new connection. 98 | * @function add 99 | */ 100 | @action 101 | add() { 102 | const connection = new Connection() 103 | 104 | /** Set the new connection instance and ID. */ 105 | this.instances.set(connection.id, connection) 106 | this.ids.push(connection.id) 107 | 108 | /** Change view to the new connection. */ 109 | this.setViewing(connection.id) 110 | } 111 | 112 | /** 113 | * Remove viewing connection. 114 | * @function remove 115 | */ 116 | @action 117 | remove() { 118 | const index = this.ids.indexOf(this.viewingId) 119 | const length = this.ids.length 120 | 121 | /** Stop the connection (and SSH tunnel). */ 122 | this.instances.get(this.viewingId).stop() 123 | 124 | /** Remove the connection instance and ID. */ 125 | this.instances.delete(this.viewingId) 126 | this.ids.remove(this.viewingId) 127 | 128 | /** Exit if there are no more connections. */ 129 | if (this.ids.length === 0) return 130 | 131 | /** 132 | * Switch to the next last connection if removing the last, or switch 133 | * to the next connection with the same index as the one we're removing. 134 | */ 135 | if (index + 1 === length) { 136 | this.setViewing(this.ids[index - 1]) 137 | } else { 138 | this.setViewing(this.ids[index]) 139 | } 140 | } 141 | 142 | /** 143 | * Set viewing connection. 144 | * @function setViewing 145 | * @param {string} id - Connection ID. 146 | */ 147 | @action 148 | setViewing(id) { 149 | this.viewingId = id 150 | } 151 | } 152 | 153 | /** Initialize a new globally used store. */ 154 | const connections = new Connections() 155 | 156 | /** Export initialized store as default export & store class as named export. */ 157 | export default connections 158 | export { Connections } 159 | -------------------------------------------------------------------------------- /src/stores/console.js: -------------------------------------------------------------------------------- 1 | import { action, computed, extendObservable, runInAction } from 'mobx' 2 | 3 | class Console { 4 | /** 5 | * @param {object} rpc - Connection instance RPC store. 6 | * @prop {string} command - RPC input field. 7 | * @prop {array} responses - RPC responses. 8 | */ 9 | constructor(rpc) { 10 | this.rpc = rpc 11 | 12 | /** Extend the store with observable properties. */ 13 | extendObservable(this, { command: '', responses: [] }) 14 | } 15 | 16 | /** 17 | * Get execute button status. 18 | * @function executeStatus 19 | * @return {boolean} Execute button status. 20 | */ 21 | @computed 22 | get executeStatus() { 23 | if (this.command.length < 4) return false 24 | if (this.options.params === null) return false 25 | return true 26 | } 27 | 28 | /** 29 | * Get RPC request options. 30 | * @function options 31 | * @return {object} RPC options. 32 | */ 33 | @computed 34 | get options() { 35 | const command = this.command.split(/ (.+)/) 36 | const params = () => { 37 | try { 38 | return command.length > 1 ? JSON.parse(command[1]) : [] 39 | } catch (e) { 40 | return null 41 | } 42 | } 43 | 44 | return { method: command[0], params: params() } 45 | } 46 | 47 | /** 48 | * Clear entered command and previous response(s). 49 | * @function reset 50 | */ 51 | @action 52 | reset = () => { 53 | this.command = '' 54 | this.responses.clear() 55 | } 56 | 57 | /** 58 | * Set command. 59 | * @function setCommand 60 | * @param {string} input - AutoComplete element event. 61 | */ 62 | @action 63 | setCommand = input => { 64 | this.command = input 65 | } 66 | 67 | /** 68 | * Execute the RPC command. 69 | * @function execute 70 | */ 71 | @action 72 | async execute() { 73 | /** Do not execute the RPC command if the status is false. */ 74 | if (this.executeStatus === false) return 75 | 76 | const res = await this.rpc.batch( 77 | [{ method: this.options.method, params: this.options.params }], 78 | true 79 | ) 80 | 81 | runInAction(() => { 82 | this.responses.unshift(res) 83 | }) 84 | } 85 | } 86 | 87 | /** Export store class as default export. */ 88 | export default Console 89 | -------------------------------------------------------------------------------- /src/stores/daemon.js: -------------------------------------------------------------------------------- 1 | import { spawn } from 'child_process' 2 | import { existsSync } from 'fs' 3 | import { join } from 'path' 4 | 5 | class Daemon { 6 | /** 7 | * @prop {object|null} process - Daemon process. 8 | */ 9 | constructor() { 10 | this.process = null 11 | 12 | /** Spawn the daemon process on launch. */ 13 | this.start() 14 | } 15 | 16 | /** 17 | * Start the daemon. 18 | * @function start 19 | */ 20 | start() { 21 | const daemonName = ''.concat( 22 | 'vcashd-', 23 | process.arch, 24 | process.platform === 'win32' ? '.exe' : '' 25 | ) 26 | 27 | /** Prepare daemon path. */ 28 | let path = join(__dirname, '..', '..', 'build', 'bin', daemonName) 29 | 30 | /** Execute from the unpacked asar directory when running packaged. */ 31 | path = path.replace('app.asar', 'app.asar.unpacked') 32 | 33 | /** Check if the daemon exists and spawn it. */ 34 | this.process = existsSync(path) === true ? spawn(path) : null 35 | 36 | if (this.process !== null) { 37 | /** Log daemon stderr in development mode. */ 38 | this.process.stderr.on('data', data => { 39 | if (process.env.NODE_ENV === 'dev') console.log(data.toString().trim()) 40 | }) 41 | 42 | /** Log daemon exit. */ 43 | this.process.on('exit', (code, signal) => { 44 | console.log('Daemon exited, code:', code, 'signal:', signal) 45 | }) 46 | } 47 | } 48 | 49 | /** 50 | * Stop the daemon. 51 | * @function stop 52 | */ 53 | stop() { 54 | if (this.process !== null) this.process.kill('SIGINT') 55 | } 56 | } 57 | 58 | /** Initialize a new globally used store. */ 59 | const daemon = new Daemon() 60 | 61 | /** Export initialized store as default export & store class as named export. */ 62 | export default daemon 63 | export { Daemon } 64 | -------------------------------------------------------------------------------- /src/stores/gui.js: -------------------------------------------------------------------------------- 1 | import { action, extendObservable, reaction } from 'mobx' 2 | import { join } from 'path' 3 | import { getItem, setItem } from '../utilities/localStorage.js' 4 | import { debounce } from '../utilities/common.js' 5 | import i18next from '../utilities/i18next.js' 6 | import moment from 'moment' 7 | 8 | class GUI { 9 | /** 10 | * @prop {array} languages - Available languages. 11 | * @prop {string} soundsDir - Directory with notification sounds. 12 | * @prop {object} sounds - Loaded notification audio elements. 13 | * @prop {string} language - Selected language. 14 | * @prop {string} localCurrency - Selected local currency. 15 | * @prop {object} soundAlerts - Sound alert settings. 16 | * @prop {object} window - Window height and width. 17 | */ 18 | constructor() { 19 | this.languages = [ 20 | { language: 'en-US', name: 'English' }, 21 | { language: 'fr-FR', name: 'French' }, 22 | { language: 'pt-BR', name: 'Portuguese' }, 23 | { language: 'ru-RU', name: 'Russian' }, 24 | { language: 'sl-SI', name: 'Slovenian' }, 25 | { language: 'es-ES', name: 'Spanish' } 26 | ] 27 | 28 | /** Load notification sounds. */ 29 | this.soundsDir = join(__dirname, '..', 'assets', 'sounds') 30 | this.sounds = { 31 | incoming: new Audio(join(this.soundsDir, 'incoming.mp3')), 32 | spendable: new Audio(join(this.soundsDir, 'spendable.mp3')) 33 | } 34 | 35 | /** Extend the store with observable properties. */ 36 | extendObservable(this, { 37 | language: getItem('language') || 'en-US', 38 | localCurrency: getItem('localCurrency') || 'EUR', 39 | soundAlerts: getItem('soundAlerts') || { 40 | incoming: false, 41 | spendable: false 42 | }, 43 | window: { height: window.innerHeight, width: window.innerWidth } 44 | }) 45 | 46 | /** Update i18next and moment on language change. */ 47 | reaction( 48 | () => this.language, 49 | language => { 50 | i18next.changeLanguage(language) 51 | moment.locale(language) 52 | }, 53 | { 54 | fireImmediately: true, 55 | name: 'GUI: language changed, updating i18next and moment.' 56 | } 57 | ) 58 | 59 | /** Update window size on resize. */ 60 | window.addEventListener( 61 | 'resize', 62 | debounce( 63 | () => this.setWindowSize(window.innerHeight, window.innerWidth), 64 | 0.1 * 1000 65 | ), 66 | true 67 | ) 68 | } 69 | 70 | /** 71 | * Set display language and save it to local storage. 72 | * @function setLanguage 73 | * @param {string} language - Display language. 74 | */ 75 | @action 76 | setLanguage(language) { 77 | this.language = language 78 | setItem('language', this.language) 79 | } 80 | 81 | /** 82 | * Set local currency and save it to local storage. 83 | * @function setLocalCurrency 84 | * @param {string} localCurrency - Local currency. 85 | */ 86 | @action 87 | setLocalCurrency(localCurrency) { 88 | this.localCurrency = localCurrency 89 | setItem('localCurrency', this.localCurrency) 90 | } 91 | 92 | /** 93 | * Set sound alert and save it to local storage. 94 | * @function setSoundAlert 95 | * @param {string} alert - Alert to toggle. 96 | */ 97 | @action 98 | setSoundAlert(alert) { 99 | this.soundAlerts[alert] = !this.soundAlerts[alert] 100 | setItem('soundAlerts', this.soundAlerts) 101 | } 102 | 103 | /** 104 | * Set window height and width. 105 | * @function setWindowSize 106 | * @param {number} height - Window height. 107 | * @param {number} width - Window width. 108 | */ 109 | @action 110 | setWindowSize(height, width) { 111 | this.window.height = height 112 | this.window.width = width 113 | } 114 | } 115 | 116 | /** Initialize a new globally used store. */ 117 | const gui = new GUI() 118 | 119 | /** Export initialized store as default export & store class as named export. */ 120 | export default gui 121 | export { GUI } 122 | -------------------------------------------------------------------------------- /src/stores/rpc.js: -------------------------------------------------------------------------------- 1 | import { computed, reaction } from 'mobx' 2 | import rpcs from '../utilities/rpcs.js' 3 | 4 | class RPC { 5 | /** 6 | * @param {object} connection - Connection instance. 7 | * @prop {array} methods - RPC method names. 8 | * @prop {number|null} testTimeout - testRPC() timeout id. 9 | */ 10 | constructor(connection) { 11 | this.connection = connection 12 | this.methods = Object.keys(rpcs) 13 | this.testTimeout = null 14 | 15 | /** Assign camelCase named RPC methods as functions wrapping batch(). */ 16 | Object.keys(rpcs).forEach(method => { 17 | this[rpcs[method]] = async function() { 18 | const res = await this.batch([{ method, params: [...arguments] }], true) 19 | return res 20 | } 21 | }) 22 | 23 | /** Test RPC on local connection start or clear testTimeout on stop. */ 24 | reaction( 25 | () => this.connection.status.active, 26 | active => { 27 | if (active === true && this.connection.type === 'local') this.testRPC() 28 | if (active === false) clearTimeout(this.testTimeout) 29 | }, 30 | { name: 'RPC: checking if connection started or stopped.' } 31 | ) 32 | 33 | /** Test RPC on SSH tunnel ready. */ 34 | reaction( 35 | () => this.connection.status.tunnel, 36 | tunnel => { 37 | if (tunnel === true) this.testRPC() 38 | }, 39 | { name: 'RPC: checking if SSH tunnel started or stopped.' } 40 | ) 41 | } 42 | 43 | /** 44 | * Get RPC status. 45 | * @function ready 46 | * @return {boolean} RPC status. 47 | */ 48 | @computed 49 | get ready() { 50 | if (this.connection.status.active === false) return false 51 | return this.connection.status.rpc 52 | } 53 | 54 | /** 55 | * Make a single RPC request or a batch of RPC requests. 56 | * @function batch 57 | * @param {array} req - RPC method and params object(s). 58 | * @param {string} single - Return single response if only 1 RPC request. 59 | * @returns {object} Response(s) and request(s), single response, or empty. 60 | */ 61 | async batch(req, single = false) { 62 | try { 63 | /** Add jsonrpc version and random id to each RPC request. */ 64 | req.map(rpc => { 65 | rpc.jsonrpc = '2.0' 66 | rpc.id = Math.floor(Math.random() * 10000) 67 | }) 68 | 69 | /** Serialize the RPC request(s) and make the POST request. */ 70 | const request = await window.fetch( 71 | 'http://127.0.0.1:' + this.connection.localPort, 72 | { 73 | method: 'POST', 74 | body: JSON.stringify(req) 75 | } 76 | ) 77 | 78 | /** Get the JSON response(s). */ 79 | const res = await request.json() 80 | 81 | /** Toggle connection RPC status if not true. */ 82 | if (this.connection.status.rpc !== true) { 83 | this.connection.setStatus({ rpc: true }) 84 | } 85 | 86 | /** Return a single response, or response(s) and request(s). */ 87 | if (res.length === 1 && single === true) return res[0] 88 | return { res, req } 89 | } catch (error) { 90 | console.error('rpc.batch:', error.message, req) 91 | 92 | /** Reject invalid responses without altering the RPC status. */ 93 | if (error.message.indexOf('Unexpected') !== -1) return {} 94 | 95 | /** Toggle connection RPC status if not false. */ 96 | if (this.connection.status.rpc !== false || this.testTimeout === null) { 97 | this.connection.setStatus({ rpc: false }) 98 | } 99 | 100 | /** Test RPC every 5s until the daemon is connectable. */ 101 | this.testTimeout = setTimeout(() => this.testRPC(), 5 * 1000) 102 | 103 | /** Return empty object on a failed request. */ 104 | return {} 105 | } 106 | } 107 | 108 | /** 109 | * Test daemon RPC connectivity. 110 | * @function testRPC 111 | */ 112 | async testRPC() { 113 | clearTimeout(this.testTimeout) 114 | await this.getInfo() 115 | } 116 | } 117 | 118 | /** Export store class as default export. */ 119 | export default RPC 120 | -------------------------------------------------------------------------------- /src/utilities/blockRewards.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Get incentive percent of PoW reward for provided block. 3 | * @function incentivePercent 4 | * @param {number} block - Block to retrieve the reward percent of. 5 | * @return {number} Incentive percent of PoW reward. 6 | */ 7 | export const incentivePercent = block => { 8 | const percents = incentivePercents() 9 | const percentsLen = percents.length - 1 10 | 11 | if (block >= percents[percentsLen].block) return percents[percentsLen].percent 12 | 13 | for (let i in percents) { 14 | if (block < percents[i].block) return percents[i].percent - 1 15 | } 16 | } 17 | 18 | /** 19 | * Get PoW reward for provided block. 20 | * @function powReward 21 | * @param {number} block - Block to calculate the PoW reward of. 22 | * @return {number} PoW reward. 23 | */ 24 | export const powReward = block => { 25 | let subsidy = 0 26 | 27 | if (block >= 136400 && block <= 136400 + 1000) { 28 | subsidy = 1 29 | } else { 30 | subsidy = 128 * 1000000 31 | 32 | if (block < 325000) { 33 | for (let i = 50000; i <= block; i += 50000) { 34 | subsidy -= subsidy / 6 35 | } 36 | } else if (block < 385000) { 37 | for (let i = 10000; i <= block; i += 10000) { 38 | subsidy -= subsidy / 28 39 | subsidy = Math.ceil(subsidy) 40 | subsidy -= subsidy / 28 * 4 / 28 41 | subsidy = Math.ceil(subsidy) 42 | } 43 | } else { 44 | for (let i = 7000; i <= block; i += 7000) { 45 | subsidy -= subsidy / 28 46 | subsidy = Math.ceil(subsidy) 47 | subsidy -= subsidy / 28 * 4 / 28 48 | subsidy = Math.ceil(subsidy) 49 | } 50 | } 51 | 52 | if (subsidy / 1000000 < 1) { 53 | subsidy = 1 54 | subsidy *= 1000000 55 | } 56 | } 57 | 58 | return subsidy / 1000000 59 | } 60 | 61 | /** 62 | * Get incentive reward percent schedule. 63 | * @function incentivePercents 64 | * @return {array} Reward schedule. 65 | */ 66 | export const incentivePercents = () => { 67 | return [ 68 | { block: 210000, percent: 1 }, 69 | { block: 220000, percent: 2 }, 70 | { block: 220222, percent: 3 }, 71 | { block: 220888, percent: 4 }, 72 | { block: 221998, percent: 5 }, 73 | { block: 223552, percent: 6 }, 74 | { block: 225550, percent: 7 }, 75 | { block: 227992, percent: 8 }, 76 | { block: 230878, percent: 9 }, 77 | { block: 234208, percent: 10 }, 78 | { block: 237982, percent: 11 }, 79 | { block: 242200, percent: 12 }, 80 | { block: 246862, percent: 13 }, 81 | { block: 251968, percent: 14 }, 82 | { block: 257518, percent: 15 }, 83 | { block: 263512, percent: 16 }, 84 | { block: 269950, percent: 17 }, 85 | { block: 276832, percent: 18 }, 86 | { block: 284158, percent: 19 }, 87 | { block: 291928, percent: 20 }, 88 | { block: 300142, percent: 21 }, 89 | { block: 308800, percent: 22 }, 90 | { block: 317902, percent: 23 }, 91 | { block: 327448, percent: 24 }, 92 | { block: 337438, percent: 25 }, 93 | { block: 347872, percent: 26 }, 94 | { block: 358750, percent: 27 }, 95 | { block: 370072, percent: 28 }, 96 | { block: 381838, percent: 29 }, 97 | { block: 394048, percent: 30 }, 98 | { block: 406702, percent: 31 }, 99 | { block: 419800, percent: 32 }, 100 | { block: 433342, percent: 33 }, 101 | { block: 447328, percent: 34 }, 102 | { block: 461758, percent: 35 }, 103 | { block: 476632, percent: 36 }, 104 | { block: 491950, percent: 37 }, 105 | { block: 507712, percent: 38 }, 106 | { block: 523918, percent: 39 }, 107 | { block: 540568, percent: 40 } 108 | ] 109 | } 110 | -------------------------------------------------------------------------------- /src/utilities/common.js: -------------------------------------------------------------------------------- 1 | import { homedir } from 'os' 2 | import { join, sep } from 'path' 3 | 4 | /** 5 | * Get data folder path. 6 | * @function dataPath 7 | * @return {string} Data folder path. 8 | */ 9 | export const dataPath = () => { 10 | switch (process.platform) { 11 | case 'darwin': 12 | return join(homedir(), 'Library', 'Application Support', 'Vcash', sep) 13 | 14 | case 'win32': 15 | return join(homedir(), 'AppData', 'Roaming', 'Vcash', sep) 16 | 17 | default: 18 | return join(homedir(), '.Vcash', 'data', sep) 19 | } 20 | } 21 | 22 | /** 23 | * Debounce a function for a specified delay. 24 | * @function debounce 25 | * @param {function} callback - Function to be called upon execution. 26 | * @param {number} delay - Delay from last debounce until execution. 27 | * @return {function} Debounced function. 28 | * @see {@link https://remysharp.com/2010/07/21/throttling-function-calls|Blog} 29 | */ 30 | export const debounce = (callback, delay = 1000) => { 31 | let timer = null 32 | 33 | return () => { 34 | let context = this 35 | let args = arguments 36 | 37 | /** Clear previous timeout. */ 38 | clearTimeout(timer) 39 | 40 | /** Set a new timeout and apply context and arguments to the function. */ 41 | timer = setTimeout(() => callback.apply(context, args), delay) 42 | } 43 | } 44 | 45 | /** 46 | * Get decimal separator. 47 | * @function decimalSep 48 | * @return {string} Decimal separator. 49 | */ 50 | export const decimalSep = (n = 1.1) => { 51 | return n.toLocaleString().substring(1, 2) 52 | } 53 | 54 | /** 55 | * Get a human readable string. 56 | * @function humanReadable 57 | * @param {number} num - Number of bytes. 58 | * @param {string} dec - Decimal (true) or binary (false). 59 | * @param {string} suffix - Unit suffix. 60 | * @param {string} language - Local language. 61 | * @return {string} Human readable string. 62 | * @see {@link http://stackoverflow.com/a/14919494|StackOverflow} 63 | */ 64 | export const humanReadable = (num = 0, dec = true, suffix = 'B', language) => { 65 | const threshold = dec === true ? 1000 : 1024 66 | 67 | /** Return the number with suffix if below threshold. */ 68 | if (Math.abs(num) < threshold) return ''.concat(num, ' ', suffix) 69 | 70 | let unit = -1 71 | const units = 72 | dec === true 73 | ? ['k', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y'] 74 | : ['Ki', 'Mi', 'Gi', 'Ti', 'Pi', 'Ei', 'Zi', 'Yi'] 75 | 76 | do { 77 | num /= threshold 78 | unit++ 79 | } while (Math.abs(num) >= threshold && unit < units.length - 1) 80 | 81 | num = new Intl.NumberFormat(language, { 82 | maximumFractionDigits: 2 83 | }).format(num) 84 | 85 | return ''.concat(num, ' ', units[unit], suffix) 86 | } 87 | 88 | /** 89 | * Get a 4-character alphanumeric unique sequence. 90 | * For N unique IDs, out of X possibilities, 91 | * call at most 1 / (1 − N / X) times on average to ensure uniqueness. 92 | * @function shortUid 93 | * @return {string} Unique 4-character uid. 94 | * @see {@link http://stackoverflow.com/a/6248722|StackOverflow} 95 | */ 96 | export const shortUid = () => { 97 | return ('0000' + ((Math.random() * 1679616) | 0).toString(36)).slice(-4) 98 | } 99 | 100 | /** 101 | * Get the CSS class of the color representing confirmation status. 102 | * @function statusColor 103 | * @param {number} conf - Transaction confirmations. 104 | * @param {string} cat - Transaction category. 105 | * @return {string} CSS classname. 106 | */ 107 | export const statusColor = (conf, cat) => { 108 | const rewards = ['incentiveReward', 'miningReward', 'stakingReward'] 109 | 110 | if (rewards.includes(cat) === true) return conf < 220 ? 'orange' : 'green' 111 | return conf < 1 ? 'orange' : 'green' 112 | } 113 | -------------------------------------------------------------------------------- /src/utilities/constants.js: -------------------------------------------------------------------------------- 1 | /** 2 | * A coin. 3 | * @name coin 4 | * @see {@link https://github.com/openvcash/vcash/blob/master/coin/include/coin/constants.hpp#L84|GitHub} 5 | */ 6 | export const coin = 1000000 7 | 8 | /** 9 | * Bitcoin average rates url. 10 | * @name bitcoinAverage 11 | */ 12 | export const bitcoinAverage = 13 | 'https://apiv2.bitcoinaverage.com/indices/global/ticker/short?crypto=BTC' 14 | 15 | /** 16 | * Bittrex ticker url. 17 | * @name bittrex 18 | */ 19 | export const bittrex = 20 | 'https://bittrex.com/api/v1.1/public/getmarketsummary?market=btc-xvc' 21 | 22 | /** 23 | * Poloniex ticker url. 24 | * @name poloniex 25 | */ 26 | export const poloniex = 'https://poloniex.com/public?command=returnTicker' 27 | -------------------------------------------------------------------------------- /src/utilities/i18next.js: -------------------------------------------------------------------------------- 1 | import i18next from 'i18next' 2 | import backend from 'i18next-node-fs-backend' 3 | import { readdirSync } from 'fs' 4 | import { join } from 'path' 5 | import { getItem } from '../utilities/localStorage.js' 6 | 7 | /** 8 | * Initialize a new globally used i18next instance. 9 | * @function i18n 10 | * @return {object} i18next instance. 11 | */ 12 | const i18n = (() => { 13 | /** Get available languages. */ 14 | const languages = readdirSync(join(__dirname, '..', 'locales')) 15 | 16 | /** Get language saved in local storage. */ 17 | let fallbackLng = getItem('language') 18 | 19 | /** Check if the language exists or revert to default. */ 20 | fallbackLng = languages.includes(fallbackLng) === true ? fallbackLng : 'en-US' 21 | 22 | /** Initialize a i18next instance. */ 23 | return i18next.use(backend).init({ 24 | backend: { 25 | loadPath: join(__dirname, '..', 'locales', '{{lng}}', '{{ns}}.json'), 26 | addPath: join(__dirname, '..', 'locales', '{{lng}}', '{{ns}}.miss.json'), 27 | jsonIndent: 2 28 | }, 29 | debug: process.env.NODE_ENV === 'dev', 30 | defaultNS: 'common', 31 | fallbackLng, 32 | interpolation: { escapeValue: false }, 33 | languages, 34 | load: 'currentOnly', 35 | ns: ['common'], 36 | react: { 37 | bindI18n: 'languageChanged', 38 | bindStore: false, 39 | wait: true 40 | } 41 | }) 42 | })() 43 | 44 | /** Export the initialized instance as default export. */ 45 | export default i18n 46 | -------------------------------------------------------------------------------- /src/utilities/localStorage.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Get item from local storage. 3 | * @function getItem 4 | * @param {string} key - Retrieve the value of this key. 5 | * @return {any} Stored value or undefined if the key doesn't exist. 6 | */ 7 | export const getItem = key => { 8 | const item = window.localStorage.getItem(key) 9 | 10 | if (item === null) { 11 | console.warn('LocalStorage: ' + key + ' has not been set yet.') 12 | return undefined 13 | } else { 14 | console.info('LocalStorage: ' + key) 15 | return JSON.parse(item) 16 | } 17 | } 18 | 19 | /** 20 | * Set item in local storage. 21 | * @function setItem 22 | * @param {string} key - Assign value to this key. 23 | * @param {any} value - Value to be stored. 24 | */ 25 | export const setItem = (key, value) => { 26 | window.localStorage.setItem(key, JSON.stringify(value)) 27 | } 28 | -------------------------------------------------------------------------------- /src/utilities/rightClickMenu.js: -------------------------------------------------------------------------------- 1 | import { remote } from 'electron' 2 | 3 | /** Build the context menu. */ 4 | const Menu = remote.Menu.buildFromTemplate([ 5 | { role: 'undo' }, 6 | { role: 'redo' }, 7 | { type: 'separator' }, 8 | { role: 'cut' }, 9 | { role: 'copy' }, 10 | { role: 'paste' } 11 | ]) 12 | 13 | /** Add event listener for context (right click) menu. */ 14 | document.addEventListener('contextmenu', e => { 15 | e.preventDefault() 16 | e.stopPropagation() 17 | 18 | while (e.target) { 19 | Menu.popup(remote.getCurrentWindow()) 20 | break 21 | } 22 | }) 23 | -------------------------------------------------------------------------------- /src/utilities/rpcs.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | backupwallet: 'backupWallet', 3 | chainblender: 'chainBlender', 4 | checkwallet: 'checkWallet', 5 | createrawtransaction: 'createRawTransaction', 6 | databaseenv: 'databaseEnv', 7 | databasefind: 'databaseFind', 8 | databasestore: 'databaseStore', 9 | decoderawtransaction: 'decodeRawTransaction', 10 | decodescript: 'decodeScript', 11 | dumpprivkey: 'dumpPrivKey', 12 | dumpwallet: 'dumpWallet', 13 | dumpwalletseed: 'dumpWalletSeed', 14 | encryptwallet: 'encryptWallet', 15 | getaccount: 'getAccount', 16 | getaccountaddress: 'getAccountAddress', 17 | getaddressesbyaccount: 'getAddressesByAccount', 18 | getbalance: 'getBalance', 19 | getbestblockhash: 'getBestBlockHash', 20 | getblock: 'getBlock', 21 | getblockcount: 'getBlockCount', 22 | getblockhash: 'getBlockHash', 23 | getblocktemplate: 'getBlockTemplate', 24 | getdifficulty: 'getDifficulty', 25 | getincentiveinfo: 'getIncentiveInfo', 26 | getinfo: 'getInfo', 27 | getmininginfo: 'getMiningInfo', 28 | getnetworkhashps: 'getNetworkHashPS', 29 | getnetworkinfo: 'getNetworkInfo', 30 | getnewaddress: 'getNewAddress', 31 | getpeerinfo: 'getPeerInfo', 32 | getrawmempool: 'getRawMemPool', 33 | getrawtransaction: 'getRawTransaction', 34 | gettransaction: 'getTransaction', 35 | getunconfirmedbalance: 'getUnconfirmedBalance', 36 | importprivkey: 'importPrivKey', 37 | listaccounts: 'listAccounts', 38 | listreceivedbyaccount: 'listReceivedByAccount', 39 | listreceivedbyaddress: 'listReceivedByAddress', 40 | listsinceblock: 'listSinceBlock', 41 | listtransactions: 'listTransactions', 42 | listunspent: 'listUnspent', 43 | repairwallet: 'repairWallet', 44 | sendfrom: 'sendFrom', 45 | sendmany: 'sendMany', 46 | sendrawtransaction: 'sendRawTransaction', 47 | sendtoaddress: 'sendToAddress', 48 | settxfee: 'setTxFee', 49 | signmessage: 'signMessage', 50 | submitblock: 'submitBlock', 51 | validateaddress: 'validateAddress', 52 | verifymessage: 'verifyMessage', 53 | walletdenominate: 'walletDenominate', 54 | walletlock: 'walletLock', 55 | walletpassphrase: 'walletPassphrase', 56 | walletpassphrasechange: 'walletPassphraseChange', 57 | ztlock: 'ztLock' 58 | } 59 | --------------------------------------------------------------------------------