├── .babelrc ├── .circleci └── config.yml ├── .codeclimate.yml ├── .eslintignore ├── .eslintrc.json ├── .github ├── ISSUE_REPLY_TEMPLATE.md ├── ISSUE_TEMPLATE.md ├── PULL_REQUEST_TEMPLATE.md ├── auto-comment.yml ├── config.yml ├── move.yml ├── no-response.yml └── triage.yml ├── .gitignore ├── .gitmodules ├── .nvmrc ├── .prettierignore ├── .prettierrc ├── .travis.yml ├── AUTHORS ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── MISTAPI.md ├── README.md ├── Wallet-README.txt ├── appveyor.yml ├── build-interface ├── 84f572051cd7ea9ac2e63ce1668e4771848a0bcb.js ├── 84f572051cd7ea9ac2e63ce1668e4771848a0bcb.stats.json ├── f7745519098c04da9a3efe99fdfebaeb129821e3.css ├── i18n │ ├── ca.json │ ├── de.json │ ├── es.json │ ├── fa.json │ ├── fr.json │ ├── it.json │ ├── ja.json │ ├── ko.json │ ├── kr.json │ ├── nb.json │ ├── nl.json │ ├── pt.json │ ├── ru.json │ ├── sq.json │ ├── tap-i18n.json │ ├── zh-TW.json │ └── zh.json ├── icons │ ├── browse-icon.png │ ├── browse-icon@2x.png │ ├── expand-icon.png │ ├── mask-icon.png │ └── mask-icon.svg ├── images │ ├── anonymous-icon.png │ ├── bg-homestead.jpg │ ├── bg-metropolis.jpg │ ├── dmg-background.jpg │ ├── importaccount-logo-metal.png │ ├── tutorial-crowdsale.png │ ├── tutorial-dao.png │ └── tutorial-token.png └── index.html ├── clientBinaries.json ├── customProtocols.js ├── docs ├── CNAME ├── images │ ├── bg-wide.png │ ├── bg-wide@2x.png │ ├── ethereum-mist-type.png │ ├── ethereum-mist-type@2x.png │ ├── logo.png │ ├── logo@2x.png │ └── logo@3x.png ├── index.html ├── main.css └── package.json ├── errorPages ├── 400.html ├── 404.html └── 500.html ├── gulpTasks ├── building.js ├── maintenance.js ├── publishing.js └── testing.js ├── gulpfile.js ├── icons ├── mist │ ├── icon.icns │ ├── icon.ico │ ├── icon.png │ └── icon2x.png └── wallet │ ├── icon.icns │ ├── icon.ico │ ├── icon.png │ ├── icon2x.fw.png │ └── icon2x.png ├── interface ├── .eslintrc.yml ├── .meteor │ ├── .finished-upgraders │ ├── .gitignore │ ├── .id │ ├── packages │ ├── platforms │ ├── release │ └── versions ├── actions.js ├── client │ ├── actions.js │ ├── appStart.js │ ├── collections.js │ ├── lib │ │ ├── ethereum │ │ │ ├── 1_web3js_init.js │ │ │ └── helpers │ │ │ │ ├── helperFunctions.js │ │ │ │ └── templateHelpers.js │ │ ├── signatures.js │ │ └── thirdParty.js │ ├── mistAPIBackend.js │ ├── styles │ │ ├── animations.import.less │ │ ├── browserbar.import.less │ │ ├── constants.import.less │ │ ├── elements.import.less │ │ ├── importAccount.import.less │ │ ├── layout.import.less │ │ ├── menu.import.less │ │ ├── mixins.import.less │ │ ├── nodeInfo.import.less │ │ ├── popupWindows.import.less │ │ ├── sendTx.import.less │ │ ├── styles.less │ │ └── txHistory.import.less │ ├── templates │ │ ├── elements │ │ │ ├── img.html │ │ │ └── img.js │ │ ├── index.html │ │ ├── index.js │ │ ├── layout │ │ │ ├── browserBar.html │ │ │ ├── browserBar.js │ │ │ ├── main.html │ │ │ ├── sidebar.html │ │ │ ├── sidebar.js │ │ │ ├── webviews.html │ │ │ └── webviews.js │ │ ├── popupWindows │ │ │ ├── clientUpdateAvailable.html │ │ │ ├── clientUpdateAvailable.js │ │ │ ├── connectAccount.html │ │ │ ├── connectAccount.js │ │ │ ├── generic.html │ │ │ ├── generic.js │ │ │ ├── importAccount.html │ │ │ ├── importAccount.js │ │ │ ├── loadingWindow.html │ │ │ ├── updateAvailable.html │ │ │ └── updateAvailable.js │ │ ├── views │ │ │ ├── webview.html │ │ │ └── webview.js │ │ └── webviewEvents.js │ └── windowEvents.js ├── components │ ├── About.js │ ├── DappIdenticon.js │ ├── NodeInfo │ │ ├── StatusLight.js │ │ └── index.js │ ├── RequestAccount.js │ ├── SendTx │ │ ├── ContextDescription.js │ │ ├── ExecutionContext.js │ │ ├── FeeSelector.js │ │ ├── Footer.js │ │ ├── TxParties.js │ │ └── index.js │ └── TxHistory │ │ ├── TxRow.js │ │ └── index.js ├── i18n │ ├── app.ca.i18n.json │ ├── app.de.i18n.json │ ├── app.en.i18n.json │ ├── app.es.i18n.json │ ├── app.fa.i18n.json │ ├── app.fr.i18n.json │ ├── app.it.i18n.json │ ├── app.ja.i18n.json │ ├── app.ko.i18n.json │ ├── app.nb.i18n.json │ ├── app.nl.i18n.json │ ├── app.pt.i18n.json │ ├── app.ru.i18n.json │ ├── app.sq.i18n.json │ ├── app.zh-TW.i18n.json │ ├── app.zh.i18n.json │ ├── mist.ca.i18n.json │ ├── mist.de.i18n.json │ ├── mist.en.i18n.json │ ├── mist.es.i18n.json │ ├── mist.fa.i18n.json │ ├── mist.fr.i18n.json │ ├── mist.it.i18n.json │ ├── mist.ja.i18n.json │ ├── mist.ko.i18n.json │ ├── mist.kr.i18n.json │ ├── mist.nb.i18n.json │ ├── mist.nl.i18n.json │ ├── mist.pt.i18n.json │ ├── mist.ru.i18n.json │ ├── mist.sq.i18n.json │ ├── mist.zh-TW.i18n.json │ └── mist.zh.i18n.json ├── package.json ├── project-tap.i18n ├── public │ ├── icons │ │ ├── browse-icon.png │ │ ├── browse-icon@2x.png │ │ ├── expand-icon.png │ │ ├── mask-icon.png │ │ └── mask-icon.svg │ └── images │ │ ├── anonymous-icon.png │ │ ├── bg-homestead.jpg │ │ ├── bg-metropolis.jpg │ │ ├── dmg-background.jpg │ │ ├── importaccount-logo-metal.png │ │ ├── tutorial-crowdsale.png │ │ ├── tutorial-dao.png │ │ └── tutorial-token.png ├── utils │ ├── formatters.js │ └── hqx.js └── yarn.lock ├── main.js ├── modules ├── abi.js ├── blurOverlay.js ├── clientBinaryManager.js ├── constants.js ├── core │ ├── newTx │ │ └── reducer.js │ ├── nodes │ │ ├── actions.js │ │ └── reducer.js │ ├── rootReducer.js │ ├── settings │ │ ├── actions.js │ │ └── reducer.js │ ├── store.js │ ├── txs │ │ └── reducer.js │ └── ui │ │ ├── actions.js │ │ └── reducer.js ├── db.js ├── dbSync.js ├── ethereumNode.js ├── ethereumNodeRemote.js ├── i18n.js ├── ipc │ ├── ipcProviderBackend.js │ ├── ipcProviderWrapper.js │ └── methods │ │ ├── base.js │ │ ├── eth_accounts.js │ │ ├── eth_coinbase.js │ │ ├── eth_compileSolidity.js │ │ └── eth_sendTransaction.js ├── ipcCommunicator.js ├── menuItems.js ├── preloader │ ├── .eslintrc.yml │ ├── browser.js │ ├── dapps.js │ ├── include │ │ ├── common.js │ │ ├── consoleLogCapture.js │ │ ├── getFavicon.js │ │ ├── getMetaTags.js │ │ ├── legacyWeb3IpcProvider.js │ │ ├── mistAPI.js │ │ ├── openExternal.js │ │ ├── setBasePath.js │ │ ├── suppressWindowPrompt.js │ │ └── web3CurrentProvider.js │ ├── injected │ │ ├── BigNumber.js │ │ ├── EthereumProvider.js │ │ ├── EventEmitter3.js │ │ └── mistAPI.js │ ├── mistUI.js │ ├── popupWindows.js │ ├── popupWindowsNoWeb3.js │ ├── rendererStore.js │ ├── tests.js │ └── walletMain.js ├── settings.js ├── socketManager.js ├── sockets │ ├── base.js │ ├── web3Base.js │ ├── web3Http.js │ └── web3Ipc.js ├── swarmNode.js ├── updateChecker.js ├── utils │ ├── logger.js │ └── underscore.js └── windows.js ├── package.json ├── release.js ├── scripts ├── Locate.nsh ├── MoveFileFolder.nsh ├── SimpleFC.dll ├── ZipDLL.dll ├── locate.dll ├── windows-installer.nsi └── zipdll.nsh ├── sounds ├── bip.mp3 ├── bloop.mp3 └── invite.mp3 ├── tests ├── .eslintrc.yml ├── .gitignore ├── _base.js ├── fixtures │ ├── fixture-popup.html │ ├── index.html │ ├── js-redirect.html │ ├── meta-redirect.html │ └── page-01.html ├── mist │ └── basic.test.js ├── mocha-in-browser │ ├── .eslintrc.yml │ ├── .gitignore │ ├── Makefile │ ├── README.md │ ├── lib │ │ ├── browser │ │ │ ├── jquery-1.10.1.min.js │ │ │ └── mocha.css │ │ ├── chai.js │ │ └── mocha.js │ ├── package.json │ ├── runner.html │ └── spec │ │ ├── general-spec.js │ │ ├── ipc-spec.js │ │ └── permissions-spec.js ├── unit │ ├── core │ │ ├── newTx │ │ │ └── reducer.test.js │ │ ├── nodes │ │ │ ├── actions.test.js │ │ │ └── reducer.test.js │ │ ├── settings │ │ │ ├── actions.test.js │ │ │ └── reducer.test.js │ │ └── ui │ │ │ ├── actions.test.js │ │ │ └── reducer.test.js │ └── utils │ │ └── formatters.test.js └── wallet │ └── basic.test.js └── yarn.lock /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["react", "es2016-node5"] 3 | } 4 | -------------------------------------------------------------------------------- /.codeclimate.yml: -------------------------------------------------------------------------------- 1 | plugins: 2 | duplication: 3 | enabled: true 4 | config: 5 | languages: 6 | javascript: 7 | mass_threshold: 40 # default threshold 8 | eslint: 9 | enabled: true 10 | channel: "eslint-4" 11 | checks: 12 | complexity: 13 | enabled: true 14 | csslint: 15 | enabled: true 16 | 17 | exclude_patterns: 18 | - "tests/" 19 | - "interface/client/lib/signatures.js" 20 | - "interface/utils/hqx.js" 21 | - "build-interface/" 22 | - "modules/preloader/injected/BigNumber.js" 23 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { "es6": true }, 3 | "extends": ["eslint:recommended", "plugin:react/recommended", "prettier"], 4 | "globals": { 5 | "i18n": true, 6 | "mist": true, 7 | "beforeEach": true, 8 | "LocalStore": true, 9 | "web3": true, 10 | "Tabs": true, 11 | "Tracker": true, 12 | "_": true, 13 | "__dirname": true, 14 | "window": true, 15 | "location": true, 16 | "document": true, 17 | "Promise": true, 18 | "require": true, 19 | "global": true, 20 | "store": true, 21 | "exports": true, 22 | "module": true, 23 | "console": true, 24 | "process": true 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /.github/ISSUE_REPLY_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | Mist and Ethereum Wallet are no longer supported applications. Please see this [post](https://medium.com/@avsa/da21c8e943d2) for the context around this decision and this [post](https://medium.com/@omgwtfmarc/mist-migration-patterns-6bcf066ac383) for how to migrate to alternative tools. 2 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 7 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | #### What does it do? 2 | 3 | #### Any helpful background information? 4 | 5 | #### Which code should the reviewer start with? 6 | 7 | #### New dependencies? What are they used for? 8 | 9 | #### Relevant screenshots? 10 | -------------------------------------------------------------------------------- /.github/auto-comment.yml: -------------------------------------------------------------------------------- 1 | issueOpened: > 2 | Mist and Ethereum Wallet are no longer supported applications. Please see this [post](https://medium.com/@avsa/da21c8e943d2) for the context around this decision and this [post](https://medium.com/@omgwtfmarc/mist-migration-patterns-6bcf066ac383) for how to migrate to alternative tools. 3 | -------------------------------------------------------------------------------- /.github/config.yml: -------------------------------------------------------------------------------- 1 | maintainers: 2 | - evertonfraga 3 | - marcgarreau 4 | - philipplgh 5 | - ryanio 6 | -------------------------------------------------------------------------------- /.github/move.yml: -------------------------------------------------------------------------------- 1 | # Configuration for move-issues - https://github.com/dessant/move-issues 2 | 3 | # Delete the command comment. Ignored when the comment also contains other content 4 | deleteCommand: true 5 | 6 | # Close the source issue after moving 7 | closeSourceIssue: true 8 | 9 | # Lock the source issue after moving 10 | lockSourceIssue: false 11 | 12 | # Set custom aliases for targets 13 | aliases: 14 | wallet: ethereum/meteor-dapp-wallet 15 | geth: ethereum/go-ethereum 16 | parity: paritytech/parity 17 | 18 | -------------------------------------------------------------------------------- /.github/no-response.yml: -------------------------------------------------------------------------------- 1 | # Configuration for probot-no-response - https://github.com/probot/no-response 2 | 3 | # Number of days of inactivity before an Issue is closed for lack of response 4 | daysUntilClose: 7 5 | # Label requiring a response 6 | responseRequiredLabel: "Status: Information needed" 7 | # Comment to post when closing an Issue for lack of response. Set to `false` to disable 8 | closeComment: > 9 | This issue has been automatically closed because there has been no response 10 | to our request for more information from the original author. With only the 11 | information that is currently in the issue, we don't have enough information 12 | to take action. Please reach out if you have or find the answers we need so 13 | that we can investigate further. 14 | -------------------------------------------------------------------------------- /.github/triage.yml: -------------------------------------------------------------------------------- 1 | label: "Status: Triage" 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules/ 3 | packages/ 4 | interface_build/ 5 | interface/public/i18n/ 6 | interface/.meteor/dev_bundle 7 | interface/.meteor/public/ 8 | dist_wallet/ 9 | dist_mist/ 10 | nodes/geth/ 11 | config.json 12 | mist.log 13 | npm-debug.log 14 | yarn-debug.log 15 | yarn-error.log 16 | wallet/ 17 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "dapp-styles"] 2 | path = interface/public/dapp-styles 3 | url = git://github.com/ethereum/dapp-styles.git 4 | [submodule "meteor-dapp-wallet"] 5 | path = meteor-dapp-wallet 6 | url = https://github.com/ethereum/meteor-dapp-wallet.git 7 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | 8.2.1 2 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | interface/.meteor 2 | tests/mocha-in-browser/lib 3 | dist_mist 4 | build-interface/ 5 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | singleQuote: true 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: '8' 3 | #cache: 4 | # yarn: true 5 | # directories: 6 | # - node_modules 7 | # - 'interface/.meteor' 8 | 9 | sudo: required 10 | 11 | matrix: 12 | include: 13 | # LINUX, MIST 14 | - os: linux 15 | dist: trusty 16 | env: 17 | - PRODUCT=mist 18 | - GULP_PLATFORM=linux 19 | addons: 20 | artifacts: 21 | paths: 22 | - $( ls dist_mist/release/* | tr "\n" ":" ) 23 | apt: 24 | packages: 25 | - icnsutils 26 | - graphicsmagick 27 | - xz-utils 28 | - gcc-multilib 29 | - g++-multilib 30 | before_install: 31 | # prepare integration tests 32 | - export DISPLAY=:99.0; sh -e /etc/init.d/xvfb start 33 | after_failure: 34 | - cat ~/.config/Mist/logs/all.* 35 | 36 | 37 | # LINUX, WALLET 38 | - os: linux 39 | dist: trusty 40 | env: 41 | - PRODUCT=wallet 42 | - GULP_PLATFORM=linux 43 | addons: 44 | artifacts: 45 | paths: 46 | - $( ls dist_wallet/release/* | tr "\n" ":" ) 47 | apt: 48 | packages: 49 | - icnsutils 50 | - graphicsmagick 51 | - xz-utils 52 | - gcc-multilib 53 | - g++-multilib 54 | after_failure: 55 | - cat ~/.config/Ethereum\ Wallet/logs/all.* 56 | 57 | # MAC, MIST 58 | - os: osx 59 | osx_image: xcode8.3 60 | env: 61 | - PRODUCT=mist 62 | - GULP_PLATFORM=mac 63 | addons: 64 | artifacts: 65 | paths: 66 | - $( ls dist_mist/release/* | tr "\n" ":" ) 67 | before_install: 68 | - npm install -g yarn 69 | after_failure: 70 | - cat ~/Library/Application\ Support/Mist/logs/all.* 71 | 72 | # MAC, WALLET 73 | - os: osx 74 | osx_image: xcode8.3 75 | env: 76 | - PRODUCT=wallet 77 | - GULP_PLATFORM=mac 78 | addons: 79 | artifacts: 80 | paths: 81 | - $( ls dist_wallet/release/* | tr "\n" ":" ) 82 | before_install: 83 | - npm install -g yarn 84 | after_failure: 85 | - cat ~/Library/Application\ Support/Ethereum\ Wallet/logs/all.* 86 | 87 | install: 88 | - PATH=$PATH:$HOME/.meteor && curl -L https://raw.githubusercontent.com/arunoda/travis-ci-meteor-packages/1390e0f96162d0d70fc1e60a6b0f4f891a0e8f42/configure.sh | /bin/sh 89 | - meteor --version 90 | 91 | - yarn 92 | 93 | script: 94 | - yarn test:unit:once 95 | 96 | - yarn build:${PRODUCT} --$GULP_PLATFORM 97 | 98 | - if [ $PRODUCT == "mist" && $GULP_PLATFORM="linux" ]; then 99 | yarn test:e2e; 100 | fi 101 | 102 | after_success: 103 | - if [ $TRAVIS_BRANCH == "master" ]; then 104 | yarn dist:${PRODUCT} --$GULP_PLATFORM; 105 | fi 106 | 107 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | The Mist Authors 2 | 3 | Alexander Van de Sande 4 | Bas van Kervel 5 | Fabian Vogelsteller -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How to Contribute 2 | 3 | ## Issues / Bug reports 4 | 5 | **Prior to submitting, please search -and read- _both_ open and closed issues -as _it_ may already exist.** 6 | 7 | To help improve Mist (_Ethereum Wallet_), please include the following: 8 | 9 | - What do you run? (_Binary version from [releases](https://github.com/ethereum/mist/releases) or a development version from the [commandline](https://github.com/ethereum/mist#run-mist)_) 10 | - Which version do you use? (_Check the `VERSION` file in the Mist folder_) 11 | - What OS you're on? 12 | 13 | If applicable: 14 | 15 | - Log file (Linux: `~/.config/Mist/logs/all.log`, Windows: `%APPDATA%/Roaming/Mist/logs/all.log`, MacOSX: `~/Library/Application Support/Mist/logs/all.log`) 16 | - Screenshot (for GUI related issues) 17 | 18 | ## Pull Requests 19 | 20 | If you want to make a PR please make sure you add a understandable description of what it is you're adding/changing/fixing. 21 | 22 | For formatting we use 2 _spaces_ as indentation. 23 | 24 | If you add any modules or files, please give them a module description and or a class description: 25 | 26 | ``` 27 | /** 28 | The IPC provider backend filter and tunnel all incoming request to the IPC geth bridge. 29 | 30 | @module ipcProviderBackend 31 | */ 32 | 33 | /** 34 | Mist API 35 | 36 | Provides an API for all dapps, which specifically targets features from the Mist browser 37 | 38 | @class mist 39 | @constructor 40 | */ 41 | ``` 42 | -------------------------------------------------------------------------------- /Wallet-README.txt: -------------------------------------------------------------------------------- 1 | Ethereum Wallet 2 | 3 | The Ethereum wallet, which allows you to create simple and multisig wallets to manage your ether. 4 | 5 | The wallet contains its own node, but can also use an already running one, if the IPC path of that node is the standard path. 6 | (See below) 7 | 8 | ## Running on a testnet 9 | 10 | When you start the wallet on a testnet (e.g. different `--datadir`) you need to make sure to set the `--ipcpath` back to the original one. 11 | 12 | On OSX its `~/Library/Ethereum/geth.ipc` on linux `~/.ethereum/geth.ipc` and on windows it uses a named pipe, which doesn't need to be renamed. 13 | 14 | Example: 15 | 16 | $ geth --datadir /my/chain/ --networkid 23 --ipcpath ~/Library/Ethereum/geth.ipc 17 | 18 | ### Original contract 19 | 20 | Once you start the app while running a testnet, the wallet need to deploy an original contract, 21 | which will be used by the wallet contracts you create. 22 | 23 | The point of the original wallet is that wallet contract creation is cheaper, 24 | as not the full code has to be deployed for every wallet. 25 | 26 | You need to make sure that the account displayed for the original wallet creation is unlocked and has at least 1 ether. 27 | 28 | ## Paths 29 | 30 | The paths which store your wallets database and node are different: 31 | 32 | The wallet (Mist) stores its data at: 33 | 34 | * Mac: ~/Library/Application Support/Mist 35 | * Windows: %APPDATA%\Roaming\Mist 36 | * Linux: ~/.config/Mist 37 | 38 | The nodes data is stored at: 39 | 40 | * Mac: ~/Library/Ethereum 41 | * Windows: %APPDATA%\Roaming\Ethereum 42 | * Linux: ~/.ethereum 43 | 44 | ## Issues 45 | 46 | If you find issues or have suggestion, please report them at 47 | https://github.com/ethereum/meteor-dapp-wallet/issues 48 | 49 | ## Repository 50 | 51 | The wallet code can be found at 52 | https://github.com/ethereum/meteor-dapp-wallet 53 | 54 | And the binary application code, which wraps the wallet app can be found at 55 | https://github.com/ethereum/mist/tree/wallet 56 | 57 | ## Bundling the wallet 58 | 59 | To bundle the binaries yourself follow the instructions on the mist#wallet readme 60 | https://github.com/ethereum/mist/tree/wallet#deployment 61 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | image: Visual Studio 2017 2 | platform: x64 3 | 4 | environment: 5 | nodejs_version: "8" 6 | 7 | # increasing `meteor build` memory allocation 8 | # https://github.com/meteor/meteor/issues/9568 9 | TOOL_NODE_FLAGS: "--max-old-space-size=4096" 10 | 11 | APPVEYOR_RDP_PASSWORD: 12 | secure: ErXQkfrN/bX6LtqcjtnLGw== 13 | 14 | matrix: 15 | - product: mist 16 | - product: wallet 17 | 18 | artifacts: 19 | - path: dist_mist\release\* 20 | name: Mist 21 | - path: dist_wallet\release\* 22 | name: Wallet 23 | 24 | cache: 25 | - node_modules 26 | - '%LOCALAPPDATA%\electron\Cache' 27 | - '%LOCALAPPDATA%\electron-builder\cache' 28 | 29 | init: 30 | # enabling RDP for debugging purposes 31 | - ps: $blockRdp = true; iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1')) 32 | 33 | install: 34 | # Get the latest stable version of Node.js or io.js 35 | - ps: Install-Product node $env:nodejs_version 36 | 37 | # needed for meteor-dapp-wallet 38 | - git submodule update --init --recursive 39 | 40 | # installs global dependencies 41 | - choco install meteor 42 | - choco install nsis 43 | 44 | - refreshenv 45 | - ps: refreshenv 46 | 47 | - node --version 48 | 49 | # installs JS dependencies 50 | - yarn install 51 | - cd interface && yarn install 52 | 53 | build_script: 54 | - ps: yarn build:$env:product --win --skipTasks=build-interface 55 | - ps: yarn dist:$env:product 56 | 57 | test_script: 58 | - cmd: yarn test:unit:once 59 | 60 | 61 | -------------------------------------------------------------------------------- /build-interface/icons/browse-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethereum/mist/81228a3102c5444f3c739dcaf98a1185b5bf73d1/build-interface/icons/browse-icon.png -------------------------------------------------------------------------------- /build-interface/icons/browse-icon@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethereum/mist/81228a3102c5444f3c739dcaf98a1185b5bf73d1/build-interface/icons/browse-icon@2x.png -------------------------------------------------------------------------------- /build-interface/icons/expand-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethereum/mist/81228a3102c5444f3c739dcaf98a1185b5bf73d1/build-interface/icons/expand-icon.png -------------------------------------------------------------------------------- /build-interface/icons/mask-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethereum/mist/81228a3102c5444f3c739dcaf98a1185b5bf73d1/build-interface/icons/mask-icon.png -------------------------------------------------------------------------------- /build-interface/images/anonymous-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethereum/mist/81228a3102c5444f3c739dcaf98a1185b5bf73d1/build-interface/images/anonymous-icon.png -------------------------------------------------------------------------------- /build-interface/images/bg-homestead.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethereum/mist/81228a3102c5444f3c739dcaf98a1185b5bf73d1/build-interface/images/bg-homestead.jpg -------------------------------------------------------------------------------- /build-interface/images/bg-metropolis.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethereum/mist/81228a3102c5444f3c739dcaf98a1185b5bf73d1/build-interface/images/bg-metropolis.jpg -------------------------------------------------------------------------------- /build-interface/images/dmg-background.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethereum/mist/81228a3102c5444f3c739dcaf98a1185b5bf73d1/build-interface/images/dmg-background.jpg -------------------------------------------------------------------------------- /build-interface/images/importaccount-logo-metal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethereum/mist/81228a3102c5444f3c739dcaf98a1185b5bf73d1/build-interface/images/importaccount-logo-metal.png -------------------------------------------------------------------------------- /build-interface/images/tutorial-crowdsale.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethereum/mist/81228a3102c5444f3c739dcaf98a1185b5bf73d1/build-interface/images/tutorial-crowdsale.png -------------------------------------------------------------------------------- /build-interface/images/tutorial-dao.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethereum/mist/81228a3102c5444f3c739dcaf98a1185b5bf73d1/build-interface/images/tutorial-dao.png -------------------------------------------------------------------------------- /build-interface/images/tutorial-token.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethereum/mist/81228a3102c5444f3c739dcaf98a1185b5bf73d1/build-interface/images/tutorial-token.png -------------------------------------------------------------------------------- /build-interface/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /customProtocols.js: -------------------------------------------------------------------------------- 1 | const { protocol } = require('electron'); 2 | 3 | protocol.registerHttpProtocol( 4 | 'mist', 5 | (request, callback) => { 6 | console.log( 7 | request.url.indexOf('mist://interface') !== -1 8 | ? global.interfaceAppUrl + request.url.replace('mist://interface', '') 9 | : '' 10 | ); 11 | 12 | const call = { 13 | url: 14 | request.url.indexOf('mist://interface') !== -1 15 | ? global.interfaceAppUrl + request.url.replace('mist://interface', '') 16 | : '', 17 | method: request.method, 18 | referrer: request.referrer 19 | }; 20 | 21 | callback(call); 22 | }, 23 | error => { 24 | if (error) { 25 | console.error('Failed to register protocol'); 26 | } 27 | } 28 | ); 29 | -------------------------------------------------------------------------------- /docs/CNAME: -------------------------------------------------------------------------------- 1 | mist.ethereum.org -------------------------------------------------------------------------------- /docs/images/bg-wide.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethereum/mist/81228a3102c5444f3c739dcaf98a1185b5bf73d1/docs/images/bg-wide.png -------------------------------------------------------------------------------- /docs/images/bg-wide@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethereum/mist/81228a3102c5444f3c739dcaf98a1185b5bf73d1/docs/images/bg-wide@2x.png -------------------------------------------------------------------------------- /docs/images/ethereum-mist-type.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethereum/mist/81228a3102c5444f3c739dcaf98a1185b5bf73d1/docs/images/ethereum-mist-type.png -------------------------------------------------------------------------------- /docs/images/ethereum-mist-type@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethereum/mist/81228a3102c5444f3c739dcaf98a1185b5bf73d1/docs/images/ethereum-mist-type@2x.png -------------------------------------------------------------------------------- /docs/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethereum/mist/81228a3102c5444f3c739dcaf98a1185b5bf73d1/docs/images/logo.png -------------------------------------------------------------------------------- /docs/images/logo@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethereum/mist/81228a3102c5444f3c739dcaf98a1185b5bf73d1/docs/images/logo@2x.png -------------------------------------------------------------------------------- /docs/images/logo@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethereum/mist/81228a3102c5444f3c739dcaf98a1185b5bf73d1/docs/images/logo@3x.png -------------------------------------------------------------------------------- /docs/main.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: 'Source Sans Pro', sans-serif; 3 | background-color: #f4f4f4; 4 | } 5 | 6 | .container { 7 | max-width: 960px; 8 | } 9 | 10 | .bg-image { 11 | background-image: url(images/bg-wide.png); 12 | background-position: center 45%; 13 | background-size: cover; 14 | } 15 | 16 | .hero .row { 17 | color: white; 18 | min-height: 500px; 19 | height: 100vh; 20 | } 21 | 22 | .hero a { 23 | } 24 | .hero h1 { 25 | margin-top: 3em; 26 | margin-bottom: 0.5em; 27 | } 28 | 29 | .hero p { 30 | font-weight: 300; 31 | font-size: 120%; 32 | color: white; 33 | line-height: 1.3em; 34 | margin-bottom: 1.5em; 35 | } 36 | 37 | .hero .btn { 38 | font-weight: 600; 39 | background-color: #499bd0; 40 | border-color: #499bd0; 41 | } 42 | 43 | .hero .download-btn-container { 44 | margin-bottom: 2em; 45 | } 46 | 47 | .text-link { 48 | color: white; 49 | display: block; 50 | font-size: 75%; 51 | margin-left: 0.8em; 52 | } 53 | .text-link:hover { 54 | color: white; 55 | } 56 | 57 | .main-content { 58 | background-color: #fff; 59 | border-radius: 8px 8px 0 0; 60 | box-shadow: 1px 2px 10px rgba(0, 0, 0, 0.36); 61 | 62 | margin-top: -2.5em; 63 | } 64 | 65 | .downloads-row table a { 66 | font-size: 90%; 67 | } 68 | .downloads-row table pre { 69 | margin-bottom: 0; 70 | color: #555; 71 | font-size: 65%; 72 | background-color: #eee; 73 | padding: 1px 4px 0; 74 | border-radius: 3px; 75 | } 76 | -------------------------------------------------------------------------------- /docs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mist-landing", 3 | "version": "1.0.0", 4 | "main": "index.js", 5 | "license": "MIT" 6 | } 7 | -------------------------------------------------------------------------------- /errorPages/400.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Error 400 4 | 5 | 6 | 16 |
17 | This URL is not allowed. 18 | 19 | -------------------------------------------------------------------------------- /errorPages/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Error 404 4 | 5 | 6 | 16 | ﴾๏๏﴿

17 | URL not found. 18 | 19 | -------------------------------------------------------------------------------- /errorPages/500.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Error 500 4 | 5 | 6 | 16 | (ノಠ益ಠ)ノ

17 | Oops.. Something went wrong! 18 | 19 | -------------------------------------------------------------------------------- /gulpTasks/testing.js: -------------------------------------------------------------------------------- 1 | const gulp = require('gulp'); 2 | const mocha = require('gulp-spawn-mocha'); 3 | const options = require('../gulpfile.js').options; 4 | const { version } = require('../package.json'); 5 | const fs = require('fs'); 6 | const colors = require('colors/safe'); 7 | 8 | gulp.task('test', () => { 9 | return gulp.src([`./tests/${options.type}/${options.test}.test.js`]).pipe( 10 | mocha({ 11 | timeout: 60000, 12 | ui: 'exports', 13 | reporter: 'spec', 14 | compilers: ['js:babel-core/register'] 15 | }) 16 | ); 17 | }); 18 | 19 | gulp.task('verify-artifacts', done => { 20 | // I don't like having a non-pure function here 21 | const fileExists = path => { 22 | const fileExist = fs.existsSync(path); 23 | if (fileExist) console.log(colors.green('\t✔︎', path)); 24 | else console.error(colors.red('\t✕', path)); 25 | 26 | return fileExist; 27 | }; 28 | 29 | const makePath = productName => `./dist_${productName}/release/`; 30 | 31 | const dashedVersion = versionString => versionString.replace(/\./g, '-'); 32 | 33 | const makeFilename = productName => version => kind => extension => 34 | `${makePath(productName)}${[ 35 | productNamePrefix(productName), 36 | kind, 37 | dashedVersion(version) 38 | ].join('-')}.${extension}`; 39 | 40 | // need to run .map().every() to display all files status, regardless of optimizations 41 | const allFilesExists = files => files.map(fileExists).every(f => f); 42 | 43 | // should be 'mist' or 'wallet' 44 | const productNamePrefix = productName => 45 | productName == 'mist' ? 'Mist' : 'Ethereum-Wallet'; 46 | 47 | const checkArtifactsLinux = filenameFragment => 48 | allFilesExists([ 49 | filenameFragment('linux32')('zip'), 50 | filenameFragment('linux64')('zip'), 51 | filenameFragment('linux32')('deb'), 52 | filenameFragment('linux64')('deb') 53 | ]); 54 | 55 | const checkArtifactsWindows = filenameFragment => 56 | allFilesExists([ 57 | filenameFragment('win32')('zip'), 58 | filenameFragment('win64')('zip'), 59 | filenameFragment('installer')('exe') 60 | ]); 61 | 62 | const checkArtifactsMac = filenameFragment => 63 | allFilesExists([filenameFragment('macosx')('dmg')]); 64 | 65 | const checkArtifacts = platform => productName => version => { 66 | const filenameFragment = makeFilename(productName)(version); 67 | switch (platform) { 68 | case 'linux': 69 | return checkArtifactsLinux(filenameFragment); 70 | case 'win': 71 | return checkArtifactsWindows(filenameFragment); 72 | case 'mac': 73 | return checkArtifactsMac(filenameFragment); 74 | default: 75 | return false; 76 | } 77 | }; 78 | 79 | // mist, wallet 80 | const product = options.type; 81 | 82 | // win, mac, linux 83 | const platforms = options.activePlatforms; 84 | 85 | console.log(colors.yellow('Checking for generated artifacts...')); 86 | const artifactsAssertion = platforms.map(platform => 87 | checkArtifacts(platform)(product)(version) 88 | ); 89 | 90 | if (artifactsAssertion.every(a => a)) { 91 | done(); 92 | } else { 93 | done( 94 | 'One or all artifacts does not exist or does not have a proper file name.' 95 | ); 96 | } 97 | }); 98 | -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable 2 | import/no-extraneous-dependencies, 3 | strict, 4 | prefer-spread 5 | */ 6 | 7 | const _ = require('underscore'); 8 | const gulp = require('gulp'); 9 | const minimist = require('minimist'); 10 | const runSeq = require('run-sequence'); 11 | 12 | // available crossplatform builds 13 | let platforms; 14 | if (process.platform === 'darwin') { 15 | platforms = ['mac', 'linux', 'win']; 16 | } else if (process.platform === 'win32') { 17 | platforms = ['win']; 18 | } else { 19 | platforms = ['linux', 'win']; 20 | } 21 | 22 | // parse commandline arguments 23 | const args = process.argv.slice(2); 24 | const options = minimist(args, { 25 | string: ['walletSource', 'test', 'skipTasks'], 26 | boolean: _.flatten(['wallet', platforms]), 27 | default: { 28 | wallet: false, 29 | walletSource: 'master', 30 | test: 'basic', 31 | skipTasks: '' 32 | } 33 | }); 34 | 35 | // echo version info and usage hints 36 | console.log('Mist version:', require('./package.json').version); 37 | console.log('Electron version:', require('electron/package.json').version); 38 | 39 | if (_.isEmpty(_.intersection(args, ['--wallet']))) { 40 | console.log('Many gulp tasks can be run in wallet mode using: --wallet'); 41 | } 42 | 43 | const platformFlags = platforms.map(platform => { 44 | return `--${platform}`; 45 | }); 46 | if (_.isEmpty(_.intersection(args, platformFlags))) { 47 | console.log( 48 | `To specify a platform (default: all) use: ${platformFlags.join(' ')}` 49 | ); 50 | _.each(platforms, platform => { 51 | options[platform] = true; 52 | }); // activate all platform flags 53 | } 54 | 55 | // prepare global variables (shared with other gulp task files) 56 | options.type = options.wallet ? 'wallet' : 'mist'; 57 | options.platforms = platforms; 58 | options.activePlatforms = _.keys( 59 | _.pick(_.pick(options, platforms), key => { 60 | return key; 61 | }) 62 | ); 63 | 64 | exports.options = options; 65 | 66 | // import gulp tasks 67 | require('require-dir')('./gulpTasks'); 68 | 69 | gulp.task('upload-queue', gulp.series('checksums', 'upload-binaries')); 70 | 71 | const skipTasks = options.skipTasks.replace(/\s/g, '').split(','); 72 | const tasks = [ 73 | 'clean-dist', 74 | 'pack-wallet', 75 | 'copy-app-source-files', 76 | 'transpile-main', 77 | 'transpile-modules', 78 | 'copy-build-folder-files', 79 | 'switch-production', 80 | 'build-interface', 81 | 'copy-interface', 82 | 'move-wallet', 83 | 'copy-i18n', 84 | 'build-dist', 85 | 'release-dist', 86 | 'build-nsis', 87 | 'verify-artifacts' 88 | ].filter(task => !skipTasks.includes(task)); 89 | 90 | gulp.task('default', gulp.series(tasks)); 91 | -------------------------------------------------------------------------------- /icons/mist/icon.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethereum/mist/81228a3102c5444f3c739dcaf98a1185b5bf73d1/icons/mist/icon.icns -------------------------------------------------------------------------------- /icons/mist/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethereum/mist/81228a3102c5444f3c739dcaf98a1185b5bf73d1/icons/mist/icon.ico -------------------------------------------------------------------------------- /icons/mist/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethereum/mist/81228a3102c5444f3c739dcaf98a1185b5bf73d1/icons/mist/icon.png -------------------------------------------------------------------------------- /icons/mist/icon2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethereum/mist/81228a3102c5444f3c739dcaf98a1185b5bf73d1/icons/mist/icon2x.png -------------------------------------------------------------------------------- /icons/wallet/icon.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethereum/mist/81228a3102c5444f3c739dcaf98a1185b5bf73d1/icons/wallet/icon.icns -------------------------------------------------------------------------------- /icons/wallet/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethereum/mist/81228a3102c5444f3c739dcaf98a1185b5bf73d1/icons/wallet/icon.ico -------------------------------------------------------------------------------- /icons/wallet/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethereum/mist/81228a3102c5444f3c739dcaf98a1185b5bf73d1/icons/wallet/icon.png -------------------------------------------------------------------------------- /icons/wallet/icon2x.fw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethereum/mist/81228a3102c5444f3c739dcaf98a1185b5bf73d1/icons/wallet/icon2x.fw.png -------------------------------------------------------------------------------- /icons/wallet/icon2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethereum/mist/81228a3102c5444f3c739dcaf98a1185b5bf73d1/icons/wallet/icon2x.png -------------------------------------------------------------------------------- /interface/.eslintrc.yml: -------------------------------------------------------------------------------- 1 | env: # don't warn about no-def meteor keywords 2 | meteor: true 3 | 4 | rules: 5 | no-undef: 0 6 | no-var: 0 7 | prefer-arrow-callback: 0 8 | prefer-template: 0 9 | no-underscore-dangle: 10 | - error 11 | - allow: ['_id', '_escape', '__'] 12 | 13 | globals: 14 | Helpers: true 15 | Blaze: true 16 | EthAccounts: true 17 | mist: true 18 | -------------------------------------------------------------------------------- /interface/.meteor/.finished-upgraders: -------------------------------------------------------------------------------- 1 | # This file contains information which helps Meteor properly upgrade your 2 | # app when you run 'meteor update'. You should check it into version control 3 | # with your project. 4 | 5 | notices-for-0.9.0 6 | notices-for-0.9.1 7 | 0.9.4-platform-file 8 | notices-for-facebook-graph-api-2 9 | 1.2.0-standard-minifiers-package 10 | 1.2.0-meteor-platform-split 11 | 1.2.0-cordova-changes 12 | 1.2.0-breaking-changes 13 | 1.3.0-split-minifiers-package 14 | 1.4.0-remove-old-dev-bundle-link 15 | 1.4.1-add-shell-server-package 16 | 1.4.3-split-account-service-packages 17 | 1.5-add-dynamic-import-package 18 | 1.7-split-underscore-from-meteor-base 19 | -------------------------------------------------------------------------------- /interface/.meteor/.gitignore: -------------------------------------------------------------------------------- 1 | local 2 | -------------------------------------------------------------------------------- /interface/.meteor/.id: -------------------------------------------------------------------------------- 1 | # This file contains a token that is unique to your project. 2 | # Check it into your repository along with the rest of this directory. 3 | # It can be used for purposes such as: 4 | # - ensuring you don't accidentally deploy one app on top of another 5 | # - providing package authors with aggregated statistics 6 | 7 | 1vzgwat16lvg098rknpa 8 | -------------------------------------------------------------------------------- /interface/.meteor/packages: -------------------------------------------------------------------------------- 1 | # Meteor packages used by this project, one per line. 2 | # Check this file (and the other files in this directory) into your repository. 3 | # 4 | # 'meteor add' and 'meteor remove' will edit this file for you, 5 | # but you can also edit it by hand. 6 | 7 | less@2.7.12 8 | tap:i18n 9 | jeeeyul:moment-with-langs 10 | ostrio:templatehelpers 11 | raix:handlebar-helpers 12 | frozeman:animation-helper 13 | frozeman:template-var 14 | frozeman:reactive-timer 15 | frozeman:storage 16 | frozeman:global-notifications 17 | reactive-var@1.0.11 18 | sacha:spin 19 | chuangbo:cookie 20 | agnito:simptip 21 | mrt:jquery-ui-sortable 22 | ethereum:accounts@1.1.0 23 | ethereum:tools@1.1.0 24 | ethereum:elements@1.1.0 25 | ethereum:web3@1.0.0-beta.33 26 | ethereum:dapp-styles 27 | meteor-base@1.4.0 28 | mobile-experience@1.0.5 29 | mongo@1.5.0 30 | blaze-html-templates 31 | session@1.1.7 32 | jquery@1.11.10 33 | tracker@1.2.0 34 | logging@1.1.20 35 | reload@1.2.0 36 | random@1.1.0 37 | ejson@1.1.0 38 | spacebars 39 | check@1.3.1 40 | standard-minifier-css@1.4.1 41 | tap:i18n-bundler 42 | shell-server@0.3.1 43 | ecmascript@0.11.1 44 | numeral:numeral 45 | numeral:languages 46 | dynamic-import@0.4.0 47 | underscore@1.0.10 48 | standard-minifier-js 49 | -------------------------------------------------------------------------------- /interface/.meteor/platforms: -------------------------------------------------------------------------------- 1 | browser 2 | server 3 | -------------------------------------------------------------------------------- /interface/.meteor/release: -------------------------------------------------------------------------------- 1 | METEOR@1.7.0.3 2 | -------------------------------------------------------------------------------- /interface/.meteor/versions: -------------------------------------------------------------------------------- 1 | 3stack:bignumber@2.0.0 2 | agnito:simptip@0.0.1 3 | aldeed:simple-schema@1.5.4 4 | alexvandesande:identicon@2.0.2 5 | allow-deny@1.1.0 6 | amplify@1.0.0 7 | autoupdate@1.4.1 8 | babel-compiler@7.1.1 9 | babel-runtime@1.2.2 10 | base64@1.0.11 11 | binary-heap@1.0.10 12 | blaze@2.3.2 13 | blaze-html-templates@1.1.2 14 | blaze-tools@1.0.10 15 | boilerplate-generator@1.5.0 16 | caching-compiler@1.1.12 17 | caching-html-compiler@1.1.2 18 | callback-hook@1.1.0 19 | cfs:http-methods@0.0.32 20 | check@1.3.1 21 | chuangbo:cookie@1.1.0 22 | coffeescript@1.0.17 23 | ddp@1.4.0 24 | ddp-client@2.3.3 25 | ddp-common@1.4.0 26 | ddp-server@2.2.0 27 | deps@1.0.12 28 | diff-sequence@1.1.0 29 | dynamic-import@0.4.1 30 | ecmascript@0.11.1 31 | ecmascript-runtime@0.7.0 32 | ecmascript-runtime-client@0.7.1 33 | ecmascript-runtime-server@0.7.0 34 | ejson@1.1.0 35 | es5-shim@4.8.0 36 | ethereum:accounts@1.1.0 37 | ethereum:dapp-styles@0.5.8 38 | ethereum:elements@1.1.0 39 | ethereum:tools@1.1.0 40 | ethereum:web3@1.0.0-beta.33 41 | frozeman:animation-helper@0.2.6 42 | frozeman:global-notifications@0.2.1 43 | frozeman:persistent-minimongo@0.1.8 44 | frozeman:reactive-timer@0.1.7 45 | frozeman:storage@0.1.9 46 | frozeman:template-var@1.3.0 47 | geojson-utils@1.0.10 48 | hot-code-push@1.0.4 49 | html-tools@1.0.11 50 | htmljs@1.0.11 51 | http@1.4.1 52 | id-map@1.1.0 53 | jeeeyul:moment-with-langs@2.12.1 54 | jquery@1.11.11 55 | launch-screen@1.1.1 56 | less@2.7.12 57 | livedata@1.0.18 58 | localstorage@1.2.0 59 | logging@1.1.20 60 | mdg:validation-error@0.5.1 61 | meteor@1.9.2 62 | meteor-base@1.4.0 63 | meteorspark:util@0.2.0 64 | minifier-css@1.3.1 65 | minifier-js@2.3.5 66 | minimongo@1.4.4 67 | mobile-experience@1.0.5 68 | mobile-status-bar@1.0.14 69 | modern-browsers@0.1.2 70 | modules@0.12.2 71 | modules-runtime@0.10.2 72 | mongo@1.5.1 73 | mongo-dev-server@1.1.0 74 | mongo-id@1.0.7 75 | mrt:jquery-ui-sortable@1.10.3 76 | npm-mongo@3.0.11 77 | numeral:languages@1.5.3 78 | numeral:numeral@1.5.3_1 79 | observe-sequence@1.0.16 80 | ordered-dict@1.1.0 81 | ostrio:templatehelpers@2.1.4 82 | promise@0.11.1 83 | raix:eventemitter@0.1.3 84 | raix:handlebar-helpers@0.2.5 85 | random@1.1.0 86 | reactive-dict@1.2.0 87 | reactive-var@1.0.11 88 | reload@1.2.0 89 | retry@1.1.0 90 | routepolicy@1.0.13 91 | sacha:spin@2.3.1 92 | session@1.1.7 93 | shell-server@0.3.1 94 | socket-stream-client@0.2.2 95 | spacebars@1.0.15 96 | spacebars-compiler@1.1.3 97 | standard-minifier-css@1.4.1 98 | standard-minifier-js@2.3.4 99 | standard-minifiers@1.1.0 100 | tap:i18n@1.8.2 101 | tap:i18n-bundler@0.3.0 102 | templating@1.3.2 103 | templating-compiler@1.3.3 104 | templating-runtime@1.3.2 105 | templating-tools@1.1.2 106 | tracker@1.2.0 107 | ui@1.0.13 108 | underscore@1.0.10 109 | url@1.2.0 110 | webapp@1.6.2 111 | webapp-hashing@1.0.9 112 | -------------------------------------------------------------------------------- /interface/client/actions.js: -------------------------------------------------------------------------------- 1 | exports.getLanguage = function getLanguage() { 2 | return function(dispatch) { 3 | dispatch({ type: '[CLIENT]:GET_LANGUAGE:START' }); 4 | try { 5 | const lang = ipc.sendSync('backendAction_getLanguage'); 6 | i18n.changeLanguage(lang); 7 | TAPi18n.setLanguage(lang); 8 | dispatch({ 9 | type: '[CLIENT]:GET_LANGUAGE:SUCCESS', 10 | payload: { i18n: lang } 11 | }); 12 | } catch (error) { 13 | dispatch({ type: '[CLIENT]:GET_LANGUAGE:FAILURE', error }); 14 | } 15 | }; 16 | }; 17 | -------------------------------------------------------------------------------- /interface/client/collections.js: -------------------------------------------------------------------------------- 1 | /** 2 | @module Collections 3 | */ 4 | 5 | Tabs = new Mongo.Collection('tabs', { connection: null }); 6 | 7 | LastVisitedPages = new Mongo.Collection('last-visted-pages', { 8 | connection: null 9 | }); 10 | 11 | History = new Mongo.Collection('history', { connection: null }); 12 | 13 | // Sync collection from and to the backend loki.js 14 | if (typeof window.dbSync !== 'undefined') { 15 | Tabs = window.dbSync.frontendSyncInit(Tabs); 16 | LastVisitedPages = window.dbSync.frontendSyncInit(LastVisitedPages); 17 | History = window.dbSync.frontendSyncInit(History); 18 | } 19 | -------------------------------------------------------------------------------- /interface/client/lib/ethereum/1_web3js_init.js: -------------------------------------------------------------------------------- 1 | // Set provider 2 | if (typeof web3 !== 'undefined') { 3 | web3 = new Web3(web3.currentProvider); 4 | } else { 5 | web3 = new Web3('ws://localhost:8546'); 6 | } 7 | -------------------------------------------------------------------------------- /interface/client/lib/ethereum/helpers/templateHelpers.js: -------------------------------------------------------------------------------- 1 | /** 2 | Helper functions 3 | 4 | @module Helpers 5 | **/ 6 | 7 | /** 8 | Global template helpers 9 | 10 | @class TemplateHelpers 11 | @constructor 12 | **/ 13 | 14 | /** 15 | A simple template helper to log objects in the console. 16 | 17 | @method (debug) 18 | **/ 19 | Template.registerHelper('debug', function(object) { 20 | console.log(object); 21 | }); 22 | 23 | /** 24 | Returns the current block 25 | 26 | @method (CurrentBlock) 27 | **/ 28 | Template.registerHelper('CurrentBlock', function() { 29 | return EthBlocks.latest; 30 | }); 31 | 32 | /** 33 | Return the dirname. 34 | 35 | @method (dirname) 36 | **/ 37 | Template.registerHelper('dirname', function() { 38 | return window.dirname; 39 | }); 40 | 41 | /** 42 | Return the Mist API. 43 | 44 | @method (mist) 45 | **/ 46 | Template.registerHelper('mist', function() { 47 | return window.mist; 48 | }); 49 | 50 | /** 51 | Return the app mode. 52 | 53 | @method (mode) 54 | **/ 55 | Template.registerHelper('mode', function() { 56 | return window.mistMode; 57 | }); 58 | 59 | /** 60 | Return the friendly app name. 61 | 62 | @method (appName) 63 | **/ 64 | Template.registerHelper('appName', function() { 65 | return window.mistMode === 'mist' ? 'Mist' : 'Ethereum Wallet'; 66 | }); 67 | 68 | /** 69 | Return the app icon path. 70 | 71 | @method (iconPath) 72 | **/ 73 | Template.registerHelper('appIconPath', function() { 74 | return ( 75 | 'file://' + window.dirname + '/icons/' + window.mistMode + '/icon2x.png' 76 | ); 77 | }); 78 | 79 | /** 80 | Get all accounts 81 | 82 | @method (accounts) 83 | **/ 84 | Template.registerHelper('accounts', function(identity) { 85 | return EthAccounts.find({}, { sort: { name: 1 } }); 86 | }); 87 | 88 | /** 89 | Check if the given wallet is a watch only wallet, by checking if we are one of owners in the wallet. 90 | 91 | @method (isWatchOnly) 92 | @param {String} id the id of the wallet to check 93 | **/ 94 | Template.registerHelper('isWatchOnly', Helpers.isWatchOnly); 95 | 96 | /** 97 | Return the right wallet icon 98 | 99 | @method (walletIcon) 100 | **/ 101 | Template.registerHelper('walletIcon', function() { 102 | var icon = ''; 103 | 104 | if (this.type === 'wallet') { 105 | if (Helpers.isWatchOnly(this._id)) { 106 | icon = ''; 107 | } else { 108 | icon = ''; 109 | } 110 | } else if (this.type === 'account') { 111 | icon = ''; 112 | } 113 | 114 | return new Spacebars.SafeString(icon); 115 | }); 116 | 117 | /** 118 | Get the account name or display the address 119 | 120 | @method (accountNameOrAddress) 121 | @param {String} address 122 | */ 123 | Template.registerHelper('accountNameOrAddress', function(address) { 124 | var account = EthAccounts.findOne({ address: address }); 125 | if (account) { 126 | return account.name; 127 | } else { 128 | return address; 129 | } 130 | }); 131 | 132 | /** 133 | Formats a timestamp to any format given. 134 | 135 | {{formatTime myTime "YYYY-MM-DD"}} 136 | 137 | @method (formatTime) 138 | @param {String} time The timstamp, can be string or unix format 139 | @param {String} format the format string, can also be "iso", to format to ISO string, or "fromnow" 140 | //@param {Boolean} realTime Whether or not this helper should re-run every 10s 141 | @return {String} The formated time 142 | **/ 143 | Template.registerHelper('formatTime', Helpers.formatTime); 144 | 145 | /** 146 | Formats a number. 147 | 148 | {{formatNumber myNumber "0,0.0[0000]"}} 149 | 150 | @method (formatNumber) 151 | @param {String} number 152 | @param {String} format the format string 153 | @return {String} The formatted number 154 | **/ 155 | Template.registerHelper('formatNumber', Helpers.formatNumber); 156 | 157 | /** 158 | Formats a number. 159 | 160 | {{formatBalance myNumber "0,0.0[0000]"}} 161 | 162 | @method (formatBalance) 163 | @param {String} number 164 | @param {String} format the format string 165 | @return {String} The formatted number 166 | **/ 167 | Template.registerHelper('formatBalance', Helpers.formatBalance); 168 | -------------------------------------------------------------------------------- /interface/client/lib/thirdParty.js: -------------------------------------------------------------------------------- 1 | // set spinner options 2 | Meteor.Spinner.options = { 3 | lines: 12, // The number of lines to draw 4 | length: 0, // The length of each line 5 | width: 4, // The line thickness 6 | radius: 8, // The radius of the inner circle 7 | corners: 1, // Corner roundness (0..1) 8 | rotate: 0, // The rotation offset 9 | direction: 1, // 1: clockwise, -1: counterclockwise 10 | color: '#000', // #rgb or #rrggbb or array of colors 11 | speed: 1.7, // Rounds per second 12 | trail: 49, // Afterglow percentage 13 | shadow: false, // Whether to render a shadow 14 | hwaccel: false, // Whether to use hardware acceleration 15 | className: 'spinner', // The CSS class to assign to the spinner 16 | zIndex: 2e9, // The z-index (defaults to 2000000000) 17 | top: '50%', // Top position relative to parent 18 | left: '50%' // Left position relative to parent 19 | }; 20 | -------------------------------------------------------------------------------- /interface/client/styles/animations.import.less: -------------------------------------------------------------------------------- 1 | // Menu 2 | 3 | aside.sidebar { 4 | ul li { 5 | transition: transform 0.25s, height 0.25s, visibility 0.5s; 6 | 7 | &.ui-sortable-helper { 8 | transition: none; 9 | } 10 | 11 | button.slide-out { 12 | transition: transform 0.2s ease-in-out; 13 | } 14 | 15 | ul.sub-menu { 16 | li { 17 | transition: background 0.05s ease-in-out, height 0.25s ease-out, 18 | opacity 0.25s ease-out; 19 | } 20 | .badge { 21 | // ANIMATION 22 | transition: max-width @animationSpeed; 23 | } 24 | } 25 | } 26 | } 27 | 28 | // Browser bar 29 | .browser-bar { 30 | .url-input { 31 | transition: opacity 0.1s; 32 | } 33 | } 34 | 35 | .popup-windows { 36 | position: relative; 37 | perspective: 800px; 38 | 39 | .field-container { 40 | transform-style: preserve-3d; 41 | transition: transform 0.5s; 42 | transition-timing-function: ease-out; 43 | transition-timing-function: cubic-bezier(0, 1.125, 0.335, 1.65); 44 | height: @gridHeight*1.5; 45 | 46 | & > * { 47 | position: absolute; 48 | left: @gridWidth*2; 49 | top: -1 * @gridHeight; 50 | backface-visibility: hidden; 51 | } 52 | 53 | .password-repeat { 54 | transform: rotateX(180deg); 55 | } 56 | 57 | &.repeat-field { 58 | transform: rotateX(180deg); 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /interface/client/styles/constants.import.less: -------------------------------------------------------------------------------- 1 | @widthSideBar: 78px; 2 | 3 | @colorLinkActive: #4a90e2; 4 | @colorLinkBlur: #494949; 5 | @colorLinkFocus: lighten(@colorLinkBlur, 8%); 6 | 7 | @colorGrey: #666; 8 | 9 | @shadowStandard: -1px 0 4px rgba(0, 0, 0, 0.2); 10 | 11 | // FONTS 12 | @sourceSansPro: 'Source Sans Pro', 'Helvetica Neue', 'Helvetica', Arial, 13 | Sans-serif; 14 | @fontSizeTiny: 8px; 15 | @fontSizeSmall: 12px; 16 | @fontSizeNormal: 14px; 17 | @fontSizeLarge: 18px; 18 | 19 | // Platform specifics 20 | // Mac OSX 21 | @colorOSBackgroundFocus: #d2d2d2; 22 | @colorOSBackgroundBlur: #f6f6f6; 23 | 24 | // Windows 25 | // @colorOSBackgroundFocus windows F0F0F0 26 | -------------------------------------------------------------------------------- /interface/client/styles/elements.import.less: -------------------------------------------------------------------------------- 1 | .red { 2 | color: @colorError; 3 | } 4 | .gree { 5 | color: @colorGreen; 6 | } 7 | .blue { 8 | color: @colorBlue; 9 | } 10 | 11 | img { 12 | transition: opacity @animationSpeed / 2; 13 | 14 | &.loading { 15 | opacity: 0; 16 | } 17 | } 18 | 19 | ul.no-bullets { 20 | margin: 0; 21 | padding: 0; 22 | list-style: none; 23 | word-wrap: break-word; 24 | 25 | li { 26 | margin: 0; 27 | margin-bottom: @defaultMargin / 2; 28 | 29 | strong { 30 | font-weight: 400; 31 | } 32 | } 33 | } 34 | 35 | .danger { 36 | display: inline-block; 37 | padding: 1px 2px; 38 | border-radius: 3px; 39 | color: @colorWhite; 40 | background-color: @colorError; 41 | } 42 | -------------------------------------------------------------------------------- /interface/client/styles/mixins.import.less: -------------------------------------------------------------------------------- 1 | .allowSelection { 2 | -webkit-touch-callout: initial; 3 | -webkit-user-select: initial; 4 | -khtml-user-select: initial; 5 | -moz-user-select: initial; 6 | -ms-user-select: initial; 7 | user-select: initial; 8 | } 9 | 10 | .disallowSelection { 11 | -webkit-touch-callout: none; 12 | -webkit-user-select: none; 13 | -khtml-user-select: none; 14 | -moz-user-select: none; 15 | -ms-user-select: none; 16 | user-select: none; 17 | } 18 | -------------------------------------------------------------------------------- /interface/client/styles/styles.less: -------------------------------------------------------------------------------- 1 | // libs 2 | @import '{ethereum:dapp-styles}/dapp-styles.less'; 3 | 4 | @import 'constants.import.less'; 5 | @import 'mixins.import.less'; 6 | @import 'elements.import.less'; 7 | @import 'layout.import.less'; 8 | @import 'importAccount.import.less'; 9 | @import 'menu.import.less'; 10 | @import 'nodeInfo.import.less'; 11 | @import 'browserbar.import.less'; 12 | @import 'popupWindows.import.less'; 13 | @import 'animations.import.less'; 14 | 15 | @import 'sendTx.import.less'; 16 | @import 'txHistory.import.less'; 17 | -------------------------------------------------------------------------------- /interface/client/styles/txHistory.import.less: -------------------------------------------------------------------------------- 1 | a { 2 | color: #02a8f3; 3 | } 4 | 5 | div#react-entry { 6 | height: 100%; 7 | } 8 | 9 | .list-txs { 10 | text-align: left; 11 | height: 100%; 12 | } 13 | 14 | .list-txs .header { 15 | width: 100%; 16 | text-align: center; 17 | } 18 | 19 | .list-txs h1 { 20 | font-size: 13px; 21 | color: #333; 22 | font-weight: normal; 23 | margin: 3px 0 1px 0; 24 | display: inline-block; 25 | .txs-total { 26 | font-weight: 500; 27 | } 28 | } 29 | 30 | .tx-data { 31 | word-wrap: break-word; 32 | } 33 | 34 | .tx-list { 35 | padding: 10px; 36 | height: ~'calc(100% - 22px)'; 37 | overflow-y: scroll; 38 | border-top: 1px solid #aaa; 39 | user-select: text; 40 | } 41 | 42 | .tx-list .tx { 43 | background: #f9f9f9; 44 | margin-bottom: 10px; 45 | border-bottom: solid 2px #dcdada; 46 | padding: 10px; 47 | color: #111; 48 | font-size: 13px; 49 | .bold { 50 | font-weight: normal; 51 | } 52 | .tx-description { 53 | font-size: 18px; 54 | font-weight: 500; 55 | margin-bottom: 10px; 56 | } 57 | .right { 58 | float: right; 59 | text-align: right; 60 | } 61 | .tx-date { 62 | font-size: 80%; 63 | margin-bottom: 0; 64 | } 65 | .network { 66 | color: red; 67 | font-size: 13px; 68 | font-weight: bold; 69 | margin-bottom: 0; 70 | } 71 | .dapp-identicon { 72 | vertical-align: middle; 73 | margin: 0 5px; 74 | width: 20px; 75 | height: 20px; 76 | } 77 | div { 78 | margin-bottom: 10px; 79 | } 80 | .tx-moreDetails { 81 | color: #02a8f3; 82 | font-weight: 400; 83 | text-transform: uppercase; 84 | cursor: pointer; 85 | padding: 3px; 86 | margin-bottom: 0; 87 | } 88 | } 89 | 90 | .no-txs { 91 | font-style: italic; 92 | font-weight: bold; 93 | margin-top: 5px; 94 | color: #333; 95 | } 96 | -------------------------------------------------------------------------------- /interface/client/templates/elements/img.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /interface/client/templates/elements/img.js: -------------------------------------------------------------------------------- 1 | /** 2 | Template Controllers 3 | 4 | @module Templates 5 | */ 6 | 7 | /** 8 | The img template 9 | 10 | @class [template] elements_img 11 | @constructor 12 | */ 13 | 14 | Template['elements_img'].helpers({ 15 | /** 16 | This helper will preload the image, and then inject it later after its loaded 17 | 18 | @method (preload) 19 | */ 20 | preload: function() { 21 | var template = Template.instance(), 22 | data = this, 23 | img = new Image(); 24 | 25 | TemplateVar.set('loading', true); 26 | 27 | img.onload = function() { 28 | TemplateVar.set(template, 'loading', false); 29 | TemplateVar.set(template, 'src', data.src); 30 | }; 31 | img.src = data.src; 32 | } 33 | }); 34 | -------------------------------------------------------------------------------- /interface/client/templates/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | {{> Template.dynamic template=renderApp}} 12 | 13 | {{> GlobalNotifications}} 14 | 15 |
16 |
17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /interface/client/templates/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | Template Controllers 3 | 4 | @module Templates 5 | */ 6 | 7 | /** 8 | The body template 9 | 10 | @class [template] body 11 | @constructor 12 | */ 13 | 14 | import React from 'react'; 15 | import { render } from 'react-dom'; 16 | import { Provider } from 'react-redux'; 17 | import About from '../../components/About'; 18 | import RequestAccount from '../../components/RequestAccount'; 19 | import SendTx from '../../components/SendTx/'; 20 | 21 | const COMPONENTS = { 22 | About, 23 | RequestAccount, 24 | SendTx 25 | }; 26 | 27 | function renderReactComponentPopup(component) { 28 | const Component = COMPONENTS[component]; 29 | if (!!Component) { 30 | render( 31 | 32 | 33 | , 34 | document.getElementById('react-entry') 35 | ); 36 | } 37 | } 38 | 39 | // NOTE: While in the process of converting the Meteor codebase to React, 40 | // generic windows reuse electron windows by replacing either the 41 | // component or the template 42 | ipc.on('uiAction_switchTemplate', (e, templateName) => { 43 | const componentName = 44 | templateName.charAt(0).toUpperCase() + templateName.slice(1); 45 | 46 | // If a React component exists, render it 47 | if (!!COMPONENTS[componentName]) { 48 | TemplateVar.setTo( 49 | '#generic-body', 50 | 'MainRenderTemplate', 51 | `popupWindows_generic` 52 | ); 53 | renderReactComponentPopup(componentName); 54 | } else { 55 | // Otherwise, use the meteor template 56 | renderReactComponentPopup(''); 57 | TemplateVar.setTo( 58 | '#generic-body', 59 | 'MainRenderTemplate', 60 | `popupWindows_${templateName}` 61 | ); 62 | } 63 | }); 64 | 65 | Template.body.helpers({ 66 | /** 67 | Chooses the view to render at start 68 | 69 | @method renderApp 70 | */ 71 | renderApp: function() { 72 | // Generic windows return the TemplateVar if set in the ipc call above 73 | const template = TemplateVar.get('MainRenderTemplate'); 74 | if (template) { 75 | return template; 76 | } 77 | 78 | if (_.isEmpty(location.hash)) { 79 | $('title').text('Mist'); 80 | return 'layout_main'; 81 | } else { 82 | const renderWindow = location.hash.match(/#([a-zA-Z]*)_?/); 83 | 84 | // TODO: handle React components 85 | const REACT_COMPONENTS = ['about', 'requestAccount', 'sendTx']; 86 | if (REACT_COMPONENTS.includes(renderWindow[1])) { 87 | return false; 88 | } 89 | 90 | if (renderWindow.length > 0) { 91 | return 'popupWindows_' + renderWindow[1]; 92 | } else { 93 | return false; 94 | } 95 | } 96 | } 97 | }); 98 | 99 | /* 100 | Template.body.events({ 101 | /** 102 | On drag over prevent redirect 103 | 104 | @event dragover body > *, drop body > * 105 | * 106 | 'dragover body > *, drop body > *': function(e){ 107 | e.preventDefault(); 108 | }, 109 | });*/ 110 | -------------------------------------------------------------------------------- /interface/client/templates/layout/browserBar.html: -------------------------------------------------------------------------------- 1 | 40 | -------------------------------------------------------------------------------- /interface/client/templates/layout/main.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /interface/client/templates/layout/sidebar.html: -------------------------------------------------------------------------------- 1 | 72 | -------------------------------------------------------------------------------- /interface/client/templates/layout/webviews.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /interface/client/templates/layout/webviews.js: -------------------------------------------------------------------------------- 1 | /** 2 | Template Controllers 3 | 4 | @module Templates 5 | */ 6 | Template['layout_webviews'].onCreated(function() { 7 | var template = this; 8 | TemplateVar.set(template, 'loading', true); 9 | watchNodeStatus(template); 10 | }); 11 | 12 | Template['layout_webviews'].onDestroyed(function() { 13 | if (this.storeUnsubscribe) { 14 | this.storeUnsubscribe(); 15 | } 16 | }); 17 | 18 | /** 19 | The main section template 20 | 21 | @class [template] layout_webviews 22 | @constructor 23 | */ 24 | Template['layout_webviews'].helpers({ 25 | /** 26 | Return the tabs 27 | @method (tabs) 28 | */ 29 | tabs: function() { 30 | return Tabs.find({}, { field: { position: 1 } }); 31 | } 32 | }); 33 | 34 | /** 35 | Set TemplateVar 'loading' if node has connected 36 | @method watchNodeStatus 37 | */ 38 | var watchNodeStatus = function(template) { 39 | if (meteorEnv.NODE_ENV === 'test') { 40 | TemplateVar.set(template, 'loading', false); 41 | return; 42 | } 43 | 44 | if ( 45 | store.getState().nodes.remote.blockNumber > 100 || 46 | store.getState().nodes.local.blockNumber > 0 47 | ) { 48 | TemplateVar.set(template, 'loading', false); 49 | } 50 | 51 | this.storeUnsubscribe = store.subscribe(() => { 52 | if ( 53 | store.getState().nodes.remote.blockNumber > 100 || 54 | store.getState().nodes.local.blockNumber > 0 55 | ) { 56 | TemplateVar.set(template, 'loading', false); 57 | } 58 | }); 59 | }; 60 | -------------------------------------------------------------------------------- /interface/client/templates/popupWindows/clientUpdateAvailable.html: -------------------------------------------------------------------------------- 1 | 37 | -------------------------------------------------------------------------------- /interface/client/templates/popupWindows/clientUpdateAvailable.js: -------------------------------------------------------------------------------- 1 | /** 2 | Template Controllers 3 | 4 | @module Templates 5 | */ 6 | 7 | Template['popupWindows_clientUpdateAvailable'].events({ 8 | 'click .ok': function(e) { 9 | ipc.send('backendAction_windowCallback', 'update'); 10 | }, 11 | 'click .cancel': function(e) { 12 | ipc.send('backendAction_windowCallback', 'skip'); 13 | } 14 | }); 15 | -------------------------------------------------------------------------------- /interface/client/templates/popupWindows/connectAccount.html: -------------------------------------------------------------------------------- 1 | 73 | -------------------------------------------------------------------------------- /interface/client/templates/popupWindows/generic.html: -------------------------------------------------------------------------------- 1 | 3 | -------------------------------------------------------------------------------- /interface/client/templates/popupWindows/generic.js: -------------------------------------------------------------------------------- 1 | /** 2 | Template Controllers 3 | 4 | @module Templates 5 | */ 6 | 7 | /** 8 | The about template 9 | 10 | @class [template] popupWindows_generic 11 | @constructor 12 | */ 13 | Template['popupWindows_generic'].onCreated(function() {}); 14 | -------------------------------------------------------------------------------- /interface/client/templates/popupWindows/importAccount.html: -------------------------------------------------------------------------------- 1 | 32 | -------------------------------------------------------------------------------- /interface/client/templates/popupWindows/loadingWindow.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /interface/client/templates/popupWindows/updateAvailable.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /interface/client/templates/popupWindows/updateAvailable.js: -------------------------------------------------------------------------------- 1 | /** 2 | Template Controllers 3 | 4 | @module Templates 5 | */ 6 | 7 | /** 8 | The updateAvailable template 9 | 10 | @class [template] popupWindows_updateAvailable 11 | @constructor 12 | */ 13 | Template['popupWindows_updateAvailable'].onCreated(function() { 14 | var template = this; 15 | 16 | TemplateVar.set(template, 'checking', true); 17 | 18 | /* 19 | When app update check is in progress it. 20 | */ 21 | ipc.on('uiAction_checkUpdateInProgress', function(e, update) { 22 | console.debug('Update check in progress...'); 23 | 24 | TemplateVar.set(template, 'checking', true); 25 | }); 26 | 27 | /* 28 | When app update data is received display it. 29 | */ 30 | ipc.on('uiAction_checkUpdateDone', function(e, update) { 31 | console.debug('Update check done'); 32 | 33 | TemplateVar.set(template, 'checking', false); 34 | 35 | if (update) { 36 | TemplateVar.set(template, 'update', update); 37 | } 38 | }); 39 | }); 40 | 41 | Template['popupWindows_updateAvailable'].events({ 42 | 'click .get-update': function(e) { 43 | var update = TemplateVar.get('update'); 44 | 45 | if (update && update.url) { 46 | ipc.send('backendAction_openExternalUrl', update.url); 47 | } 48 | } 49 | }); 50 | -------------------------------------------------------------------------------- /interface/client/templates/views/webview.html: -------------------------------------------------------------------------------- 1 | 17 | -------------------------------------------------------------------------------- /interface/client/templates/webviewEvents.js: -------------------------------------------------------------------------------- 1 | showError = function(tabId, e) { 2 | if (e.isMainFrame || e.killed) { 3 | var url, 4 | path = 'file://' + dirname + '/errorPages/'; 5 | 6 | if (e.killed) { 7 | e.errorCode = 500; 8 | } 9 | 10 | switch (e.errorCode) { 11 | case -105: 12 | url = path + '404.html'; 13 | break; 14 | case 404: 15 | url = path + '404.html'; 16 | break; 17 | case 500: 18 | url = path + '500.html'; 19 | break; 20 | } 21 | 22 | if (url) { 23 | Tabs.update(tabId, { 24 | $set: { 25 | redirect: url 26 | } 27 | }); 28 | } 29 | } 30 | }; 31 | 32 | webviewChangeUrl = function(tabId, e) { 33 | if (e.type === 'did-navigate-in-page' && !e.isMainFrame) { 34 | return; 35 | } 36 | 37 | var url = Helpers.sanitizeUrl(e.url || this.getURL()); 38 | 39 | console.log(e.type, tabId, url); 40 | 41 | if (e.type === 'did-navigate') { 42 | // destroy socket when navigating away 43 | ipc.send('ipcProvider-destroy', this.getWebContents().id); 44 | } 45 | 46 | // make sure to not store error pages in history 47 | if ( 48 | !url || 49 | url.indexOf('mist/errorPages/') !== -1 || 50 | url.indexOf('app.asar/errorPages/') !== -1 51 | ) { 52 | return; 53 | } 54 | 55 | // update the URL 56 | Tabs.update(tabId, { 57 | $set: { 58 | url: url 59 | } 60 | }); 61 | }; 62 | 63 | // fired by "did-stop-loading" 64 | webviewLoadStop = function(tabId, e) { 65 | var webview = this, 66 | url = Helpers.sanitizeUrl(webview.getURL()), 67 | title = webview.getTitle(); 68 | 69 | console.log(e.type, tabId, url); 70 | 71 | // IS BROWSER 72 | if (tabId === 'browser') { 73 | // ADD to doogle last visited pages 74 | if ( 75 | (find = _.find(LastVisitedPages.find().fetch(), function(historyEntry) { 76 | if (!historyEntry.url) { 77 | return; 78 | } 79 | var historyEntryOrigin = new URL(historyEntry.url).origin; 80 | return url.indexOf(historyEntryOrigin) !== -1; 81 | })) 82 | ) { 83 | LastVisitedPages.update(find._id, { 84 | $set: { 85 | timestamp: moment().unix(), 86 | url: url 87 | } 88 | }); 89 | } else { 90 | LastVisitedPages.insert({ 91 | name: title, 92 | url: url, 93 | // icon: '', 94 | timestamp: moment().unix() 95 | }); 96 | } 97 | 98 | // ADD to doogle history 99 | if ((find = History.findOne({ url: url }))) { 100 | History.update(find._id, { $set: { timestamp: moment().unix() } }); 101 | } else { 102 | History.insert({ 103 | name: title, 104 | url: url, 105 | // icon: '', 106 | timestamp: moment().unix() 107 | }); 108 | } 109 | } 110 | }; 111 | 112 | // fired by "did-get-redirect-request" 113 | // fired by "new-window" 114 | // fired by "will-navigate" 115 | webviewLoadStart = function(currentTabId, e) { 116 | var webview = this; 117 | 118 | if (e.type === 'did-get-redirect-request' && !e.isMainFrame) { 119 | return; 120 | } 121 | 122 | console.log(e.type, currentTabId, e); 123 | 124 | // stop this action, as the redirect happens reactive through setting the URL attribute 125 | e.preventDefault(); // doesnt work 126 | webview.stop(); // doesnt work 127 | ipc.sendSync( 128 | 'backendAction_stopWebviewNavigation', 129 | webview.getWebContents().id 130 | ); 131 | 132 | var url = Helpers.sanitizeUrl(e.newURL || e.url); 133 | var tabId = Helpers.getTabIdByUrl(url); 134 | 135 | // if new window (_blank) open in tab, or browser 136 | if (e.type === 'new-window' && tabId === currentTabId) { 137 | tabId = 'browser'; 138 | } 139 | 140 | var tab = Tabs.findOne(tabId); 141 | 142 | if (tab.url !== url) { 143 | Tabs.update(tabId, { 144 | $set: { 145 | redirect: url, 146 | url: url 147 | } 148 | }); 149 | } 150 | LocalStore.set('selectedTab', tabId); 151 | }; 152 | -------------------------------------------------------------------------------- /interface/client/windowEvents.js: -------------------------------------------------------------------------------- 1 | // add the platform to the HTML tag 2 | setTimeout(function() { 3 | document.getElementsByTagName('html')[0].className = window.mist.platform; 4 | 5 | if (window.basePathHref) { 6 | var base = document.createElement('base'); 7 | 8 | base.href = window.basePathHref; 9 | 10 | document.getElementsByTagName('head')[0].appendChild(base); 11 | } 12 | }, 200); 13 | 14 | $(window).on('blur', function(e) { 15 | $('body').addClass('app-blur'); 16 | }); 17 | $(window).on('focus', function(e) { 18 | $('body').removeClass('app-blur'); 19 | }); 20 | 21 | // make sure files can only be dropped in the browser webview 22 | $(window).on('dragenter', function(e) { 23 | LocalStore.set('selectedTab', 'browser'); 24 | ipc.send('backendAction_focusMainWindow'); 25 | }); 26 | 27 | $(window).on('keydown', function(e) { 28 | // Select tab with index when number is 1-8 29 | if (e.metaKey && e.keyCode >= 49 && e.keyCode <= 56) { 30 | var index = parseInt(String.fromCharCode(e.keyCode), 10) - 1; 31 | Helpers.selectTabWithIndex(index); 32 | return; 33 | } 34 | 35 | // RELOAD current webview 36 | if ( 37 | LocalStore.get('selectedTab') !== 'wallet' && 38 | e.metaKey && 39 | e.keyCode === 82 40 | ) { 41 | var webview = Helpers.getWebview(LocalStore.get('selectedTab')); 42 | if (webview) { 43 | webview.reloadIgnoringCache(); 44 | } 45 | return; 46 | } 47 | 48 | // Select last tab on Ctrl + 9 49 | if (e.metaKey && e.keyCode === 57) { 50 | Helpers.selectLastTab(); 51 | return; 52 | } 53 | 54 | // Ctrl + tab || Ctrl + shift + tab 55 | if (e.ctrlKey && e.keyCode === 9) { 56 | var tabOffset = e.shiftKey ? -1 : 1; 57 | Helpers.selectTabWithOffset(tabOffset); 58 | } 59 | }); 60 | -------------------------------------------------------------------------------- /interface/components/About.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | class About extends React.Component { 4 | render() { 5 | const appIconPath = `file://${window.dirname}/icons/${ 6 | window.mistMode 7 | }/icon2x.png`; 8 | const appName = window.mistMode === 'mist' ? 'Mist' : 'Ethereum Wallet'; 9 | 10 | return ( 11 |
12 |
13 | 23 |
24 |
25 |

{appName}

26 |

27 | Version {window.mist.version} 28 |
29 | License {window.mist.license} 30 |
31 | GitHub{' '} 32 | 33 | github.com/ethereum/mist 34 | 35 |

36 | Copyright 2018 Ethereum Foundation 37 |
38 |
39 | ); 40 | } 41 | } 42 | 43 | export default About; 44 | -------------------------------------------------------------------------------- /interface/components/DappIdenticon.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import blockies from 'ethereum-blockies'; 3 | // import { i18n } from '../API'; 4 | import hqxConstructor from '../utils/hqx'; 5 | 6 | let mod = { 7 | Math: Math 8 | }; 9 | hqxConstructor(mod); 10 | const { hqx } = mod; 11 | 12 | // copied from https://github.com/ethereum/blockies/blob/master/react-component.js 13 | // see also https://github.com/alexvandesande/meteor-identicon/blob/master/lib/identicon.js 14 | // https://github.com/ethereum/meteor-package-elements/blob/master/identicon.html 15 | export default class DappIdenticon extends Component { 16 | constructor(props) { 17 | super(props); 18 | } 19 | 20 | identiconData(identity) { 21 | return hqx( 22 | hqx( 23 | blockies.create({ 24 | seed: identity, 25 | size: 8, 26 | scale: 1 27 | }), 28 | 4 29 | ), 30 | 4 31 | ).toDataURL(); 32 | } 33 | 34 | identiconDataPixel(identity) { 35 | return blockies 36 | .create({ 37 | seed: identity, 38 | size: 8, 39 | scale: 8 40 | }) 41 | .toDataURL(); 42 | } 43 | 44 | render() { 45 | const { identity } = this.props; 46 | 47 | if (!identity) { 48 | return null; 49 | } 50 | 51 | // dapp class sizes: large, medium, small, tiny 52 | return ( 53 | 62 | 66 | 67 | ); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /interface/components/NodeInfo/StatusLight.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import moment from 'moment'; 3 | import PieChart from 'react-minimal-pie-chart'; 4 | 5 | class StatusLight extends Component { 6 | constructor(props) { 7 | super(props); 8 | 9 | this.state = { 10 | pulseColorClass: '' 11 | }; 12 | } 13 | 14 | componentDidUpdate(prevProps) { 15 | // if new block arrived, add animation to light 16 | if (this.isNewBlock(prevProps, this.props)) { 17 | const pulseColorClass = 18 | prevProps.active === 'remote' 19 | ? 'pulse-light__orange' 20 | : this.props.network === 'main' 21 | ? 'pulse-light__green' 22 | : 'pulse-light__blue'; 23 | 24 | this.setState({ pulseColorClass }, () => { 25 | setTimeout(() => { 26 | this.setState({ pulseColorClass: '' }); 27 | }, 2000); 28 | }); 29 | } 30 | } 31 | 32 | isNewBlock(prevProps, newProps) { 33 | if (prevProps.active === 'remote') { 34 | return prevProps.remote.blockNumber !== newProps.remote.blockNumber; 35 | } else { 36 | return prevProps.local.blockNumber !== newProps.local.blockNumber; 37 | } 38 | } 39 | 40 | secondsSinceLastBlock() { 41 | const { active } = this.props; 42 | const lastBlock = moment(this.props[active].timestamp, 'X'); 43 | return moment().diff(lastBlock, 'seconds'); 44 | } 45 | 46 | render() { 47 | const { active, network, local, remote } = this.props; 48 | 49 | let dotColor = network == 'main' ? '#7ed321' : '#00aafa'; 50 | 51 | const { highestBlock, currentBlock, startingBlock } = local.sync; 52 | const progress = 53 | ((currentBlock - startingBlock) / (highestBlock - startingBlock)) * 100; 54 | 55 | return ( 56 |
57 |
60 63 | ? 'red' 64 | : active === 'remote' 65 | ? 'orange' 66 | : dotColor 67 | }} 68 | /> 69 | {active === 'remote' && 70 | currentBlock !== 0 && ( 71 | 85 | )} 86 |
87 | ); 88 | } 89 | } 90 | 91 | export default StatusLight; 92 | -------------------------------------------------------------------------------- /interface/components/SendTx/Footer.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import MDSpinner from 'react-md-spinner'; 3 | 4 | class Footer extends Component { 5 | constructor(props) { 6 | super(props); 7 | 8 | this.state = { 9 | pw: '' 10 | }; 11 | } 12 | 13 | handleSubmit = e => { 14 | e.preventDefault(); 15 | this.props.handleSubmit(this.state); 16 | this.setState({ pw: '' }); 17 | }; 18 | 19 | render() { 20 | const { gasPrice, gasError, estimatedGas, unlocking } = this.props; 21 | 22 | if (!estimatedGas || !gasPrice || gasPrice === 0 || gasPrice === '0x0') { 23 | return ( 24 | 25 | ); 26 | } 27 | 28 | if (unlocking) { 29 | return ( 30 |
31 |

{i18n.t('mist.sendTx.unlocking')}

32 |
33 | ); 34 | } 35 | 36 | return ( 37 |
38 |
42 | this.setState({ pw: e.target.value })} 47 | placeholder={i18n.t('mist.sendTx.enterPassword')} 48 | autoFocus 49 | /> 50 | 51 | 58 |
59 |
60 | ); 61 | } 62 | } 63 | 64 | export default Footer; 65 | -------------------------------------------------------------------------------- /interface/components/SendTx/TxParties.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import DappIdenticon from '../DappIdenticon'; 3 | 4 | class TxParties extends Component { 5 | totalAmount = () => { 6 | var amount = EthTools.formatBalance( 7 | web3.utils.toBN(this.props.value || 0), 8 | '0,0.00[0000000000000000]', 9 | 'ether' 10 | ); 11 | 12 | var dotPos = ~amount.indexOf('.') 13 | ? amount.indexOf('.') + 3 14 | : amount.indexOf(',') + 3; 15 | 16 | if (amount) { 17 | return ( 18 | 19 | {amount.substr(0, dotPos)}{' '} 20 | {amount.substr(dotPos)} 21 | 22 | ); 23 | } 24 | 25 | return '0'; 26 | }; 27 | 28 | renderFrom() { 29 | const { from, toIsContract, isNewContract, executionFunction } = this.props; 30 | 31 | return ( 32 |
33 | 34 |
44 | {i18n.t('mist.sendTx.from')} 45 |
46 |
47 | {from} 48 |
49 |
50 | ); 51 | } 52 | 53 | renderTo() { 54 | const { 55 | to, 56 | toIsContract, 57 | isNewContract, 58 | executionFunction, 59 | params 60 | } = this.props; 61 | 62 | if (isNewContract) { 63 | return null; 64 | } 65 | 66 | // If token transfer, render `to` as the address the tokens are being transferred to 67 | if (executionFunction === 'transfer(address,uint256)') { 68 | if (params[0] && params[0].value) { 69 | const address = params[0].value; 70 | return ( 71 |
72 | 73 |
74 | {i18n.t('mist.sendTx.to')} 75 |
76 | {address} 77 |
78 | ); 79 | } 80 | } 81 | 82 | if (to) { 83 | return ( 84 |
85 | 86 |
92 | {toIsContract 93 | ? i18n.t('mist.sendTx.contract') 94 | : i18n.t('mist.sendTx.to')} 95 |
96 | {to} 97 |
98 | ); 99 | } 100 | 101 | return null; 102 | } 103 | 104 | renderConnection() { 105 | const { executionFunction, hasSignature } = this.props; 106 | 107 | return ( 108 |
109 |
110 | {this.totalAmount()} ETHER 111 | {executionFunction && ( 112 |
117 | {executionFunction} 118 |
119 | )} 120 |
121 |
122 | ); 123 | } 124 | 125 | render() { 126 | return ( 127 |
128 | {this.renderFrom()} 129 | {this.renderTo()} 130 |
131 | ); 132 | } 133 | } 134 | 135 | export default TxParties; 136 | -------------------------------------------------------------------------------- /interface/components/TxHistory/index.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { connect } from 'react-redux'; 3 | import DappIdenticon from '../DappIdenticon'; 4 | import { updateTx, getPriceConversion } from '../../actions'; 5 | import TxRow from './TxRow'; 6 | 7 | class TxHistory extends Component { 8 | constructor(props) { 9 | super(props); 10 | 11 | this.state = { showDetails: [], newBlockSub: null }; 12 | this.newBlockSub = null; 13 | } 14 | 15 | componentDidMount() { 16 | this.updatePendingTxs(); 17 | this.props.dispatch(getPriceConversion()); 18 | } 19 | 20 | compenntDidUnmount() { 21 | this.unsub(); 22 | } 23 | 24 | unsub() { 25 | if (!this.newBlockSub) { 26 | return; 27 | } 28 | 29 | this.newBlockSub.unsubscribe(() => { 30 | this.newBlockSub = null; 31 | }); 32 | } 33 | 34 | updatePendingTxs() { 35 | this.unsub(); 36 | const updateTxs = () => { 37 | const txs = this.props.txs; 38 | _.each(txs, (tx, index) => { 39 | // Return if we don't have a tx hash to check for receipt 40 | if (!tx.hash) { 41 | return; 42 | } 43 | 44 | // Return if tx is on different network 45 | const currentNetwork = this.props.nodes.network.toLowerCase(); 46 | const txNetwork = this.networkString(tx.networkId).toLowerCase(); 47 | if (txNetwork !== currentNetwork) { 48 | return; 49 | } 50 | 51 | // Return if tx has already been successful 52 | if (tx.blockNumber) { 53 | return; 54 | } 55 | 56 | web3.eth.getTransactionReceipt(tx.hash).then(txReceipt => { 57 | if (txReceipt.blockNumber) { 58 | this.props.dispatch(updateTx(txReceipt)); 59 | } 60 | }); 61 | }); 62 | }; 63 | updateTxs(); 64 | this.newBlockSub = web3.eth.subscribe('newBlockHeaders', () => { 65 | updateTxs(); 66 | }); 67 | } 68 | 69 | networkString(networkId) { 70 | switch (networkId) { 71 | case 1: 72 | return 'Main'; 73 | case 3: 74 | return 'Ropsten'; 75 | case 4: 76 | return 'Rinkeby'; 77 | case 42: 78 | return 'Kovan'; 79 | default: 80 | return 'Unknown'; 81 | } 82 | } 83 | 84 | render() { 85 | const txs = this.props.txs; 86 | 87 | const txList = txs.map(tx => { 88 | return ( 89 | 95 | ); 96 | }); 97 | 98 | return ( 99 |
100 |
101 |

102 | {i18n.t('mist.txHistory.windowTitle')} 103 | {txs.length > 0 && ( 104 | 105 | {' '} 106 | ( 107 | {i18n.t('mist.txHistory.total', { count: txs.length })} 108 | ) 109 | 110 | )} 111 |

112 |
113 |
114 | {txList} 115 | {txs.length === 0 && ( 116 |
No transactions yet.
117 | )} 118 |
119 |
120 | ); 121 | } 122 | } 123 | 124 | function mapStateToProps(state) { 125 | return state; 126 | } 127 | 128 | export default connect(mapStateToProps)(TxHistory); 129 | -------------------------------------------------------------------------------- /interface/i18n/app.ca.i18n.json: -------------------------------------------------------------------------------- 1 | { 2 | "app": { 3 | "loading": "Carregant...", 4 | "offline": "No s'ha pogut connectar, estàs desconectat?", 5 | "logginIn": "iniciant sessió..." 6 | }, 7 | "error": { 8 | "insufficientRights": "No tens prous drets per aquesta acció." 9 | }, 10 | "buttons": { 11 | "ok": "OK", 12 | "cancel": "Cancel·lar", 13 | "save": "Desa", 14 | "edit": "Edita", 15 | "send": "Envia", 16 | "next": "Següent", 17 | "previous": "Anterior", 18 | "back": "Enrere", 19 | "skip": "Omet", 20 | "sending": "Enviant...", 21 | "create": "Crea", 22 | "tryToReconnect": "Intenta reconnectar", 23 | "stayAnonymous": "Mantenir l'anonimat", 24 | "authorize": "Authoritza" 25 | }, 26 | "commonWords": { 27 | "you": "Tu", 28 | "send": "Envia", 29 | "or": "o", 30 | "of": "de", 31 | "with": "amb", 32 | "and": "i", 33 | "on": "a", 34 | "per": "per" 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /interface/i18n/app.de.i18n.json: -------------------------------------------------------------------------------- 1 | { 2 | "app": { 3 | "loading": "Lade...", 4 | "offline": "Kann mich nicht verbinden, bist du offline?", 5 | "logginIn": "Einloggen..." 6 | }, 7 | "error": { 8 | "insufficientRights": "Du hast nicht genügend Rechte für diese Aktion." 9 | }, 10 | "buttons": { 11 | "ok": "OK", 12 | "cancel": "Abbrechen", 13 | "save": "Speichern", 14 | "edit": "Editieren", 15 | "send": "Senden", 16 | "next": "Weiter", 17 | "previous": "Zurück", 18 | "back": "Zurück", 19 | "skip": "Überspringen", 20 | "sending": "Sende...", 21 | "create": "Erstellen", 22 | "tryToReconnect": "Versuche mich zu verbinden", 23 | "stayAnonymous": "Anonym bleiben", 24 | "authorize": "Autorisieren" 25 | }, 26 | "commonWords": { 27 | "you": "Du", 28 | "send": "Senden", 29 | "or": "oder", 30 | "of": "von", 31 | "with": "mit", 32 | "and": "und", 33 | "on": "auf", 34 | "per": "pro" 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /interface/i18n/app.en.i18n.json: -------------------------------------------------------------------------------- 1 | { 2 | "app": { 3 | "loading": "Loading...", 4 | "offline": "Can't connect, are you offline?", 5 | "logginIn": "Logging in..." 6 | }, 7 | "error": { 8 | "insufficientRights": "You don't have enough rights for this action." 9 | }, 10 | "buttons": { 11 | "ok": "OK", 12 | "cancel": "Cancel", 13 | "save": "Save", 14 | "edit": "Edit", 15 | "send": "Send", 16 | "next": "Next", 17 | "previous": "Previous", 18 | "back": "Back", 19 | "skip": "Skip", 20 | "sending": "Sending...", 21 | "create": "Create", 22 | "tryToReconnect": "Try to reconnect", 23 | "stayAnonymous": "Stay anonymous", 24 | "authorize": "Authorize" 25 | }, 26 | "commonWords": { 27 | "you": "You", 28 | "send": "Send", 29 | "or": "or", 30 | "of": "of", 31 | "with": "with", 32 | "and": "and", 33 | "on": "on", 34 | "per": "per" 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /interface/i18n/app.es.i18n.json: -------------------------------------------------------------------------------- 1 | { 2 | "app": { 3 | "loading": "Cargando...", 4 | "offline": "No se puede conectar, estás fuera de línea?", 5 | "logginIn": "Iniciando sesión..." 6 | }, 7 | "error": { 8 | "insufficientRights": "Usted no tiene suficientes derechos para esta acción." 9 | }, 10 | "buttons": { 11 | "ok": "OK", 12 | "cancel": "Cancelar", 13 | "save": "Guardar", 14 | "edit": "Editar", 15 | "send": "Enviar", 16 | "next": "Siguiente", 17 | "previous": "Anterior", 18 | "back": "Atrás", 19 | "skip": "Omitir", 20 | "sending": "Enviando...", 21 | "create": "Crear", 22 | "tryToReconnect": "Intente volver a conectar", 23 | "stayAnonymous": "Stay anonymous", 24 | "authorize": "Authorize" 25 | }, 26 | "commonWords": { 27 | "you": "Tú", 28 | "send": "Enviar", 29 | "or": "o", 30 | "of": "de", 31 | "with": "con", 32 | "and": "y", 33 | "on": "en", 34 | "per": "por" 35 | } 36 | } -------------------------------------------------------------------------------- /interface/i18n/app.fa.i18n.json: -------------------------------------------------------------------------------- 1 | { 2 | "app": { 3 | "loading": "بارگذاری...", 4 | "offline": "نمی توانم وصل شوم، آیا شما قطع هستید؟", 5 | "logginIn": "بارگذاری در..." 6 | }, 7 | "error": { 8 | "insufficientRights": "شما برای این عملیات دسترسی کافی ندارید." 9 | }, 10 | "buttons": { 11 | "ok": "تایید", 12 | "cancel": "لغو", 13 | "save": "ذخیره", 14 | "edit": "ویرایش", 15 | "send": "ارسال", 16 | "next": "بعدی", 17 | "previous": "قبلی", 18 | "back": "عقب", 19 | "skip": "جستن", 20 | "sending": "ارسال...", 21 | "create": "ساختن", 22 | "tryToReconnect": "تلاش برای وصل مجدد", 23 | "stayAnonymous": "Stay anonymous", 24 | "authorize": "Authorize" 25 | }, 26 | "commonWords": { 27 | "you": "شما", 28 | "send": "ارسال", 29 | "or": "یا", 30 | "of": "از", 31 | "with": "با", 32 | "and": "و", 33 | "on": "بر", 34 | "per": "در هر" 35 | } 36 | } -------------------------------------------------------------------------------- /interface/i18n/app.fr.i18n.json: -------------------------------------------------------------------------------- 1 | { 2 | "app": { 3 | "loading": "Chargement en cours...", 4 | "offline": "Impossible de se connecter, êtes-vous déconnecté ?", 5 | "logginIn": "Connexion en cours..." 6 | }, 7 | "error": { 8 | "insufficientRights": "Vous n'avez pas les droits pour effectuer cette action." 9 | }, 10 | "buttons": { 11 | "ok": "OK", 12 | "cancel": "Annuler", 13 | "save": "Enregistrer", 14 | "edit": "Modifier", 15 | "send": "Envoyer", 16 | "next": "Suivant", 17 | "previous": "Précédent", 18 | "back": "Retour", 19 | "skip": "Ignorer", 20 | "sending": "Envoi en cours...", 21 | "create": "Créer", 22 | "tryToReconnect": "Tentative de reconnexion", 23 | "stayAnonymous": "Stay anonymous", 24 | "authorize": "Authorize" 25 | }, 26 | "commonWords": { 27 | "you": "Vous", 28 | "send": "Envoyer", 29 | "or": "ou", 30 | "of": "de", 31 | "with": "avec", 32 | "and": "et", 33 | "on": "sur", 34 | "per": "par" 35 | } 36 | } -------------------------------------------------------------------------------- /interface/i18n/app.it.i18n.json: -------------------------------------------------------------------------------- 1 | { 2 | "app": { 3 | "loading": "Caricamento...", 4 | "offline": "Impossibile connettersi, sicuro di essere online?", 5 | "logginIn": "Autenticazione..." 6 | }, 7 | "error": { 8 | "insufficientRights": "Non hai sufficienti diritti per compiere quasta azione." 9 | }, 10 | "buttons": { 11 | "ok": "OK", 12 | "cancel": "Annulla", 13 | "save": "Salva", 14 | "edit": "Modifica", 15 | "send": "Invia", 16 | "next": "Prossimo", 17 | "previous": "Precedente", 18 | "back": "Indietro", 19 | "skip": "Salta", 20 | "sending": "Inviando...", 21 | "create": "Crea", 22 | "tryToReconnect": "Prova a riconnetterti", 23 | "stayAnonymous": "Rimani anonimo", 24 | "authorize": "Autorizza" 25 | }, 26 | "commonWords": { 27 | "you": "Tu", 28 | "send": "Invia", 29 | "or": "oppure", 30 | "of": "di", 31 | "with": "con", 32 | "and": "e", 33 | "on": "su", 34 | "per": "per" 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /interface/i18n/app.ja.i18n.json: -------------------------------------------------------------------------------- 1 | { 2 | "app": { 3 | "loading": "ロード中...", 4 | "offline": "ネットワークが繋がりません。オフラインですか?", 5 | "logginIn": "ログインしています..." 6 | }, 7 | "error": { 8 | "insufficientRights": "このアクションを行うだけの権限が足りません。" 9 | }, 10 | "buttons": { 11 | "ok": "OK", 12 | "cancel": "キャンセル", 13 | "save": "保存", 14 | "edit": "編集", 15 | "send": "送る", 16 | "next": "次へ", 17 | "previous": "前へ", 18 | "back": "バック", 19 | "skip": "スキップ", 20 | "sending": "送信中...", 21 | "create": "作成中", 22 | "tryToReconnect": "再接続しています。", 23 | "stayAnonymous": "匿名を保つ", 24 | "authorize": "認証" 25 | }, 26 | "commonWords": { 27 | "you": "あなた", 28 | "send": "送る", 29 | "or": "または", 30 | "of": "の", 31 | "with": "と", 32 | "and": "と", 33 | "on": "の上に", 34 | "per": "ごとに" 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /interface/i18n/app.ko.i18n.json: -------------------------------------------------------------------------------- 1 | { 2 | "app": { 3 | "loading": "불러오는 중...", 4 | "offline": "연결실패, 인터넷 연결을 확인하세요.", 5 | "logginIn": "로그인 중..." 6 | }, 7 | "error": { 8 | "insufficientRights": "요청하신 명령을 실행할 권한이 없습니다." 9 | }, 10 | "buttons": { 11 | "ok": "확인", 12 | "cancel": "취소", 13 | "save": "저장", 14 | "edit": "수정", 15 | "send": "보내기", 16 | "next": "다음", 17 | "previous": "이전", 18 | "back": "뒤로", 19 | "skip": "건너뛰기", 20 | "sending": "보내는 중...", 21 | "create": "생성", 22 | "tryToReconnect": "재연결 시도", 23 | "stayAnonymous": "Stay anonymous", 24 | "authorize": "권한 인증" 25 | }, 26 | "commonWords": { 27 | "you": "당신", 28 | "send": "보내기", 29 | "or": "또는", 30 | "of": "의", 31 | "with": "와", 32 | "and": "그리고", 33 | "on": "위에", 34 | "per": "당" 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /interface/i18n/app.nb.i18n.json: -------------------------------------------------------------------------------- 1 | { 2 | "app": { 3 | "loading": "Laster...", 4 | "offline": "Kan ikke koble til. Har du ikke nett?", 5 | "logginIn": "Logger inn..." 6 | }, 7 | "error": { 8 | "insufficientRights": "Du har ikke nok rettigheter for denne handlingen." 9 | }, 10 | "buttons": { 11 | "ok": "OK", 12 | "cancel": "Avbryt", 13 | "save": "Lagre", 14 | "edit": "endre", 15 | "send": "Send", 16 | "next": "Neste", 17 | "previous": "Forrige", 18 | "back": "Tilbake", 19 | "skip": "Hopp over", 20 | "sending": "Sender...", 21 | "create": "Lag", 22 | "tryToReconnect": "Prøv å koble", 23 | "stayAnonymous": "Stay anonymous", 24 | "authorize": "Authorize" 25 | }, 26 | "commonWords": { 27 | "you": "Du", 28 | "send": "Send", 29 | "or": "eller", 30 | "of": "av", 31 | "with": "med", 32 | "and": "og", 33 | "on": "på", 34 | "per": "per" 35 | } 36 | } -------------------------------------------------------------------------------- /interface/i18n/app.nl.i18n.json: -------------------------------------------------------------------------------- 1 | { 2 | "app": { 3 | "loading": "Laden...", 4 | "offline": "Verbinden niet mogelijk, bent u offline?", 5 | "logginIn": "Inloggen..." 6 | }, 7 | "error": { 8 | "insufficientRights": "U heeft onvoldoende rechten voor deze actie." 9 | }, 10 | "buttons": { 11 | "ok": "OK", 12 | "cancel": "Annuleren", 13 | "save": "Opslaan", 14 | "edit": "bijwerken", 15 | "send": "Versturen", 16 | "next": "Volgende", 17 | "previous": "Vorige", 18 | "back": "Terug", 19 | "skip": "Overslaan", 20 | "sending": "Verzenden...", 21 | "create": "Aanmaken", 22 | "tryToReconnect": "Probeer te verbinden", 23 | "stayAnonymous": "Blijf anoniem", 24 | "authorize": "Autoriseren" 25 | }, 26 | "commonWords": { 27 | "you": "u", 28 | "send": "Verstuur", 29 | "or": "of", 30 | "of": "van", 31 | "with": "met", 32 | "and": "en", 33 | "on": "op", 34 | "per": "per" 35 | } 36 | } -------------------------------------------------------------------------------- /interface/i18n/app.pt.i18n.json: -------------------------------------------------------------------------------- 1 | { 2 | "app": { 3 | "loading": "Carregando...", 4 | "offline": "Você não está conectado a internet", 5 | "logginIn": "Autenticando..." 6 | }, 7 | "error": { 8 | "insufficientRights": "Você não tem privilégios admnistrativos o suficiente" 9 | }, 10 | "buttons": { 11 | "ok": "OK", 12 | "cancel": "Cancelar", 13 | "save": "Salvar", 14 | "edit": "editar", 15 | "send": "Enviar", 16 | "next": "Próximo", 17 | "previous": "Anterior", 18 | "back": "Back", 19 | "skip": "Pular", 20 | "sending": "Enviando...", 21 | "create": "Criar", 22 | "tryToReconnect": "Tentando reconectar", 23 | "stayAnonymous": "Stay anonymous", 24 | "authorize": "Authorize" 25 | }, 26 | "commonWords": { 27 | "you": "Você", 28 | "send": "Enviar", 29 | "or": "ou", 30 | "of": "de", 31 | "with": "com", 32 | "and": "e", 33 | "on": "em", 34 | "per": "por" 35 | } 36 | } -------------------------------------------------------------------------------- /interface/i18n/app.ru.i18n.json: -------------------------------------------------------------------------------- 1 | { 2 | "app": { 3 | "loading": "Загузка...", 4 | "offline": "Не удается подключиться, вы в автономном режиме?", 5 | "logginIn": "Вход в систему..." 6 | }, 7 | "error": { 8 | "insufficientRights": "Вы не имеете достаточно прав для этого действия." 9 | }, 10 | "buttons": { 11 | "ok": "OK", 12 | "cancel": "Отмена", 13 | "save": "Сохранить", 14 | "edit": "Изменить", 15 | "send": "Отправить", 16 | "next": "Далее", 17 | "previous": "Предыдущий", 18 | "back": "Назад", 19 | "skip": "Сбросить", 20 | "sending": "Отправка...", 21 | "create": "Создать", 22 | "tryToReconnect": "Переподключить", 23 | "stayAnonymous": "Stay anonymous", 24 | "authorize": "Authorize" 25 | }, 26 | "commonWords": { 27 | "you": "Вы", 28 | "send": "Отправили", 29 | "or": "или", 30 | "of": "из", 31 | "with": "от", 32 | "and": "и", 33 | "on": "на", 34 | "per": "в" 35 | } 36 | } -------------------------------------------------------------------------------- /interface/i18n/app.sq.i18n.json: -------------------------------------------------------------------------------- 1 | { 2 | "app": { 3 | "loading": "Duke u ngarkuar...", 4 | "offline": "Nuk mund të lidhet. Jeni të sigurt që jeni të lidhur me internetin?", 5 | "logginIn": "Duke u lidhur..." 6 | }, 7 | "error": { 8 | "insufficientRights": "Ju nuk keni të drejta të mjaftueshme për të kryer këtë veprim." 9 | }, 10 | "buttons": { 11 | "ok": "OK", 12 | "cancel": "Shuaj", 13 | "save": "Ruaj", 14 | "edit": "Përpuno", 15 | "send": "Dërgo", 16 | "next": "Më tej", 17 | "previous": "Më parë", 18 | "back": "Prapa", 19 | "skip": "Kapërce", 20 | "sending": "Duke dërguar...", 21 | "create": "Krijo", 22 | "tryToReconnect": "Provo të rilidhesh", 23 | "stayAnonymous": "Qëndro anonim", 24 | "authorize": "Autorizo" 25 | }, 26 | "commonWords": { 27 | "you": "Ju", 28 | "send": "Dërgo", 29 | "or": "ose", 30 | "of": "nga", 31 | "with": "me", 32 | "and": "dhe", 33 | "on": "mbi", 34 | "per": "për" 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /interface/i18n/app.zh-TW.i18n.json: -------------------------------------------------------------------------------- 1 | { 2 | "app": { 3 | "loading": "載入中……", 4 | "offline": "無法連接,您的網路是否已經斷線?", 5 | "logginIn": "登入中……" 6 | }, 7 | "error": { 8 | "insufficientRights": "您沒有足夠的權限執行本次操作。" 9 | }, 10 | "buttons": { 11 | "ok": "好", 12 | "cancel": "取消", 13 | "save": "儲存", 14 | "edit": "編輯", 15 | "send": "發送", 16 | "next": "下一個", 17 | "previous": "上一個", 18 | "back": "返回", 19 | "skip": "跳過", 20 | "sending": "發送中……", 21 | "create": "創建", 22 | "tryToReconnect": "嘗試連線", 23 | "stayAnonymous": "Stay anonymous", 24 | "authorize": "Authorize" 25 | }, 26 | "commonWords": { 27 | "you": "您", 28 | "send": "發送", 29 | "or": "或者", 30 | "of": "的", 31 | "with": "以及", 32 | "and": "和", 33 | "on": "在", 34 | "per": "每" 35 | } 36 | } -------------------------------------------------------------------------------- /interface/i18n/app.zh.i18n.json: -------------------------------------------------------------------------------- 1 | { 2 | "app": { 3 | "loading": "载入中……", 4 | "offline": "无法连接,你的网络是否已经掉线?", 5 | "logginIn": "登录中……" 6 | }, 7 | "error": { 8 | "insufficientRights": "你没有足够的权限执行本次操作。" 9 | }, 10 | "buttons": { 11 | "ok": "好", 12 | "cancel": "取消", 13 | "save": "保存", 14 | "edit": "编辑", 15 | "send": "发送", 16 | "next": "下一个", 17 | "previous": "上一个", 18 | "back": "返回", 19 | "skip": "跳过", 20 | "sending": "发送中……", 21 | "create": "创建", 22 | "tryToReconnect": "尝试连接", 23 | "stayAnonymous": "Stay anonymous", 24 | "authorize": "Authorize" 25 | }, 26 | "commonWords": { 27 | "you": "你", 28 | "send": "发送", 29 | "or": "或者", 30 | "of": "的", 31 | "with": "以及", 32 | "and": "和", 33 | "on": "在", 34 | "per": "每" 35 | } 36 | } -------------------------------------------------------------------------------- /interface/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Mist-Interface", 3 | "version": "0.1.0", 4 | "description": "Mist interface application", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "build:interface": "meteor-build-client ../.build-interface -p ''" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "git+https://github.com/ethereum/mist.git" 13 | }, 14 | "keywords": [ 15 | "ethereum", 16 | "mist", 17 | "web3" 18 | ], 19 | "author": "Fabian Vogelsteller ", 20 | "license": "GPL-3.0", 21 | "bugs": { 22 | "url": "https://github.com/ethereum/mist/issues" 23 | }, 24 | "homepage": "https://github.com/ethereum/mist#readme", 25 | "dependencies": { 26 | "@babel/runtime": "^7.0.0-beta.42", 27 | "ethereum-blockies": "git+https://github.com/ethereum/blockies.git", 28 | "moment": "^2.22.2", 29 | "react": "^16.4.1", 30 | "react-blockies": "^1.3.0", 31 | "react-dom": "^16.2.0", 32 | "react-md-spinner": "^0.3.0", 33 | "react-minimal-pie-chart": "^3.0.2", 34 | "react-redux": "^5.0.7", 35 | "redux": "^3.7.2" 36 | }, 37 | "devDependencies": { 38 | "meteor-build-client": "^0.4.1" 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /interface/project-tap.i18n: -------------------------------------------------------------------------------- 1 | { 2 | "cdn_path": "i18n", 3 | "helper_name": "i18n", 4 | "supported_languages": [ 5 | "ca", "de", "en", "es", "fa", "fr", "it", "ja", "ko", "nb", "nl", "pt", "ru", "sq", "zh", "zh-TW" 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /interface/public/icons/browse-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethereum/mist/81228a3102c5444f3c739dcaf98a1185b5bf73d1/interface/public/icons/browse-icon.png -------------------------------------------------------------------------------- /interface/public/icons/browse-icon@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethereum/mist/81228a3102c5444f3c739dcaf98a1185b5bf73d1/interface/public/icons/browse-icon@2x.png -------------------------------------------------------------------------------- /interface/public/icons/expand-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethereum/mist/81228a3102c5444f3c739dcaf98a1185b5bf73d1/interface/public/icons/expand-icon.png -------------------------------------------------------------------------------- /interface/public/icons/mask-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethereum/mist/81228a3102c5444f3c739dcaf98a1185b5bf73d1/interface/public/icons/mask-icon.png -------------------------------------------------------------------------------- /interface/public/images/anonymous-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethereum/mist/81228a3102c5444f3c739dcaf98a1185b5bf73d1/interface/public/images/anonymous-icon.png -------------------------------------------------------------------------------- /interface/public/images/bg-homestead.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethereum/mist/81228a3102c5444f3c739dcaf98a1185b5bf73d1/interface/public/images/bg-homestead.jpg -------------------------------------------------------------------------------- /interface/public/images/bg-metropolis.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethereum/mist/81228a3102c5444f3c739dcaf98a1185b5bf73d1/interface/public/images/bg-metropolis.jpg -------------------------------------------------------------------------------- /interface/public/images/dmg-background.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethereum/mist/81228a3102c5444f3c739dcaf98a1185b5bf73d1/interface/public/images/dmg-background.jpg -------------------------------------------------------------------------------- /interface/public/images/importaccount-logo-metal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethereum/mist/81228a3102c5444f3c739dcaf98a1185b5bf73d1/interface/public/images/importaccount-logo-metal.png -------------------------------------------------------------------------------- /interface/public/images/tutorial-crowdsale.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethereum/mist/81228a3102c5444f3c739dcaf98a1185b5bf73d1/interface/public/images/tutorial-crowdsale.png -------------------------------------------------------------------------------- /interface/public/images/tutorial-dao.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethereum/mist/81228a3102c5444f3c739dcaf98a1185b5bf73d1/interface/public/images/tutorial-dao.png -------------------------------------------------------------------------------- /interface/public/images/tutorial-token.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethereum/mist/81228a3102c5444f3c739dcaf98a1185b5bf73d1/interface/public/images/tutorial-token.png -------------------------------------------------------------------------------- /interface/utils/formatters.js: -------------------------------------------------------------------------------- 1 | export function formatTokenCount(value, decimals) { 2 | return Number((value /= Math.pow(10, decimals))).toString(); 3 | } 4 | 5 | export function formatFunctionName(functionName) { 6 | if (functionName === undefined) 7 | throw new Error('formatFunctionName() expects a non-empty string'); 8 | return functionName 9 | .slice(0, functionName.indexOf('(')) 10 | .replace(/_+/g, ' ') 11 | .replace(/([A-Z]+|[0-9]+)/g, ' $1') 12 | .toLowerCase() 13 | .trim(); 14 | } 15 | -------------------------------------------------------------------------------- /modules/abi.js: -------------------------------------------------------------------------------- 1 | /** 2 | Decodes Data into values, for a given signature. 3 | 4 | @module ABI 5 | */ 6 | const _ = global._; 7 | const { ipcMain: ipc } = require('electron'); 8 | const abi = require('ethereumjs-abi'); 9 | 10 | function isHexType(type) { 11 | return _.includes(['address', 'bytes'], type) || type.match(/bytes\d+/g); 12 | } 13 | 14 | function padLeft(string, chars) { 15 | return new Array(chars - string.length + 1).join('0') + string; 16 | } 17 | 18 | ipc.on('backendAction_decodeFunctionSignature', (event, _signature, _data) => { 19 | const data = _data.slice(10, _data.length); 20 | const signature = _signature.match(/\((.+)\)/i); 21 | 22 | if (!signature) return; 23 | 24 | const paramTypes = signature[1].split(','); 25 | 26 | try { 27 | const paramsResponse = abi.rawDecode(paramTypes, Buffer.from(data, 'hex')); 28 | const paramsDictArr = []; 29 | 30 | // Turns addresses into proper hex string 31 | // Turns numbers into their decimal string version 32 | paramTypes.forEach((type, index) => { 33 | const conversionFlag = isHexType(type) ? 'hex' : null; 34 | const prefix = isHexType(type) ? '0x' : ''; 35 | 36 | paramsResponse[index] = paramsResponse[index].toString(conversionFlag); 37 | 38 | const res = type.match(/bytes(\d+)/i); 39 | if (type === 'address') { 40 | paramsResponse[index] = padLeft(paramsResponse[index], 40); 41 | } else if (res) { 42 | paramsResponse[index] = padLeft( 43 | paramsResponse[index], 44 | Number(res[1]) * 2 45 | ); 46 | } 47 | 48 | paramsDictArr.push({ type, value: prefix + paramsResponse[index] }); 49 | }); 50 | 51 | event.sender.send('uiAction_decodedFunctionSignatures', paramsDictArr); 52 | } catch (e) { 53 | console.warn('ABI.js Warning:', e.message); 54 | } 55 | }); 56 | -------------------------------------------------------------------------------- /modules/blurOverlay.js: -------------------------------------------------------------------------------- 1 | const Windows = require('./windows'); 2 | 3 | class BlurOverlay { 4 | static enable() { 5 | BlurOverlay.setBlurOverlay(true); 6 | } 7 | 8 | static disable() { 9 | BlurOverlay.setBlurOverlay(false); 10 | } 11 | 12 | static setBlurOverlay(flag) { 13 | const mainWindow = Windows.getByType('main'); 14 | mainWindow.send('uiAction_enableBlurOverlay', flag); 15 | } 16 | } 17 | 18 | module.exports = BlurOverlay; 19 | -------------------------------------------------------------------------------- /modules/constants.js: -------------------------------------------------------------------------------- 1 | export const InfuraEndpoints = { 2 | ethereum: { 3 | http: { 4 | Main: 'https://mainnet.infura.io/mist', 5 | Ropsten: 'https://ropsten.infura.io/mist', 6 | Rinkeby: 'https://rinkeby.infura.io/mist', 7 | Kovan: 'https://kovan.infura.io/mist' 8 | }, 9 | websockets: { 10 | Main: 'wss://mainnet.infura.io/ws/mist', 11 | Ropsten: 'wss://ropsten.infura.io/ws/mist', 12 | Rinkeby: 'wss://rinkeby.infura.io/ws/mist', 13 | Kovan: 'wss://kovan.infura.io/ws/mist' 14 | } 15 | }, 16 | ipfs: { 17 | gateway: 'https://ipfs.infura.io', 18 | rpc: 'https://ipfs.infura.io:5001' 19 | } 20 | }; 21 | -------------------------------------------------------------------------------- /modules/core/newTx/reducer.js: -------------------------------------------------------------------------------- 1 | export const initialState = { 2 | from: '', 3 | gasPriceGweiStandard: 0, 4 | gasPriceGweiPriority: 0, 5 | gas: '', 6 | gasLoading: false, 7 | data: '', 8 | executionFunction: '', 9 | params: [], 10 | to: '', 11 | toIsContract: false, 12 | isNewContract: false, 13 | unlocking: false, 14 | estimatedGas: 3000000, 15 | gasError: '', 16 | priority: false, 17 | token: { 18 | name: '', 19 | symbol: '', 20 | address: '', 21 | decimals: 18 22 | } 23 | }; 24 | 25 | const newTx = (state = initialState, action) => { 26 | switch (action.type) { 27 | case '[CLIENT]:NEW_TX:START': 28 | return Object.assign({}, initialState, action.payload); 29 | case '[CLIENT]:DETERMINE_IF_CONTRACT:SUCCESS': 30 | return Object.assign({}, state, { 31 | toIsContract: action.payload.toIsContract, 32 | isNewContract: action.payload.isNewContract 33 | }); 34 | case '[CLIENT]:DETERMINE_IF_CONTRACT:FAILURE': 35 | return Object.assign({}, state, { 36 | toIsContract: false, 37 | isNewContract: false 38 | }); 39 | case '[CLIENT]:CONFIRM_TX:START': 40 | return Object.assign({}, state, { unlocking: true }); 41 | case '[CLIENT]:CONFIRM_TX:SUCCESS': 42 | return Object.assign({}, state, { unlocking: false }); 43 | case '[CLIENT]:CONFIRM_TX:FAILURE': 44 | return Object.assign({}, state, { unlocking: false }); 45 | case '[CLIENT]:PRIORITY:TOGGLE': 46 | return Object.assign({}, state, { priority: !state.priority }); 47 | case '[CLIENT]:GET_GAS_PRICE:START': 48 | return Object.assign({}, state, { 49 | gasLoading: true 50 | }); 51 | case '[CLIENT]:GET_GAS_PRICE:SUCCESS': 52 | return Object.assign({}, state, { 53 | gasPriceGweiStandard: action.payload.gasPriceGweiStandard, 54 | gasPriceGweiPriority: action.payload.gasPriceGweiPriority 55 | }); 56 | case '[CLIENT]:GET_GAS_PRICE:FAILURE': 57 | return Object.assign({}, state, { 58 | gasLoading: false 59 | }); 60 | case '[CLIENT]:ESTIMATE_GAS_USAGE:START': 61 | return Object.assign({}, state, { 62 | gasLoading: true 63 | }); 64 | case '[CLIENT]:ESTIMATE_GAS_USAGE:SUCCESS': 65 | return Object.assign({}, state, { 66 | estimatedGas: action.payload.estimatedGas, 67 | gasLoading: false 68 | }); 69 | case '[CLIENT]:ESTIMATE_GAS_USAGE:FAILURE': 70 | return Object.assign({}, state, { 71 | gasError: action.error, 72 | gasLoading: false 73 | }); 74 | case '[CLIENT]:ESTIMATE_GAS_USAGE:OVER_BLOCK_LIMIT': 75 | return Object.assign({}, state, { 76 | estimatedGas: action.error.estimatedGas, 77 | gasError: action.error.message, 78 | gasLoading: false 79 | }); 80 | case '[CLIENT]:CALCULATE_GAS:SUCCESS': 81 | return Object.assign({}, state, { 82 | gasLoading: false 83 | }); 84 | case '[CLIENT]:LOOKUP_SIGNATURE:SUCCESS': 85 | return Object.assign({}, state, { 86 | executionFunction: action.payload.executionFunction 87 | }); 88 | case '[CLIENT]:DECODE_FUNCTION_SIGNATURE:SUCCESS': 89 | return Object.assign({}, state, { 90 | params: action.payload.params 91 | }); 92 | case '[CLIENT]:GET_TOKEN_DETAILS:SUCCESS': 93 | return Object.assign({}, state, { 94 | token: action.payload.token 95 | }); 96 | default: 97 | return state; 98 | } 99 | }; 100 | 101 | export default newTx; 102 | -------------------------------------------------------------------------------- /modules/core/nodes/reducer.js: -------------------------------------------------------------------------------- 1 | export const initialState = { 2 | active: 'remote', 3 | network: 'main', 4 | changingNetwork: false, 5 | remote: { 6 | client: 'infura', 7 | blockNumber: 100, 8 | timestamp: 0 9 | }, 10 | local: { 11 | client: 'geth', 12 | blockNumber: 0, 13 | timestamp: 0, 14 | peerCount: 0, 15 | syncMode: 'fast', 16 | sync: { 17 | currentBlock: 0, 18 | highestBlock: 0, 19 | knownStates: 0, 20 | pulledStates: 0, 21 | startingBlock: 0 22 | } 23 | } 24 | }; 25 | 26 | const nodes = (state = initialState, action) => { 27 | switch (action.type) { 28 | case '[MAIN]:LOCAL_NODE:SYNC_UPDATE': 29 | return Object.assign({}, state, { 30 | local: Object.assign({}, state.local, { 31 | sync: Object.assign({}, state.local.sync, { 32 | currentBlock: action.payload.currentBlock, 33 | highestBlock: action.payload.highestBlock, 34 | knownStates: action.payload.knownStates, 35 | pulledStates: action.payload.pulledStates, 36 | startingBlock: action.payload.startingBlock 37 | }) 38 | }) 39 | }); 40 | case '[MAIN]:LOCAL_NODE:UPDATE_NEW_BLOCK': 41 | return Object.assign({}, state, { 42 | local: Object.assign({}, state.local, { 43 | blockNumber: action.payload.blockNumber, 44 | timestamp: action.payload.timestamp 45 | }) 46 | }); 47 | case '[MAIN]:LOCAL_NODE:RESET': 48 | return Object.assign({}, state, { 49 | local: Object.assign({}, state.local, { 50 | blockNumber: 0, 51 | timestamp: 0, 52 | peerCount: 0, 53 | sync: { 54 | currentBlock: 0, 55 | highestBlock: 0, 56 | knownStates: 0, 57 | pulledStates: 0, 58 | startingBlock: 0 59 | } 60 | }) 61 | }); 62 | case '[MAIN]:REMOTE_NODE:RESET': 63 | return Object.assign({}, state, { 64 | remote: Object.assign({}, state.remote, { 65 | blockNumber: 100, 66 | timestamp: 0 67 | }) 68 | }); 69 | case '[MAIN]:REMOTE_NODE:BLOCK_HEADER_RECEIVED': 70 | return Object.assign({}, state, { 71 | remote: Object.assign({}, state.remote, { 72 | blockNumber: action.payload.blockNumber, 73 | timestamp: action.payload.timestamp 74 | }) 75 | }); 76 | case '[MAIN]:NODES:CHANGE_ACTIVE': 77 | return Object.assign({}, state, { 78 | active: action.payload.active 79 | }); 80 | case '[MAIN]:NODES:CHANGE_NETWORK_START': 81 | return Object.assign({}, state, { 82 | changingNetwork: true 83 | }); 84 | case '[MAIN]:NODES:CHANGE_NETWORK_SUCCESS': 85 | return Object.assign({}, state, { 86 | changingNetwork: false, 87 | network: action.payload.network, 88 | remote: Object.assign({}, state.remote, { 89 | blockNumber: 100, 90 | timestamp: 0 91 | }), 92 | local: Object.assign({}, state.local, { 93 | blockNumber: 0, 94 | timestamp: 0, 95 | peerCount: 0, 96 | sync: { 97 | currentBlock: 0, 98 | highestBlock: 0, 99 | knownStates: 0, 100 | pulledStates: 0, 101 | startingBlock: 0 102 | } 103 | }) 104 | }); 105 | case '[MAIN]:NODES:CHANGE_SYNC_MODE': 106 | return Object.assign({}, state, { 107 | local: Object.assign({}, state.local, { 108 | syncMode: action.payload.syncMode 109 | }) 110 | }); 111 | case '[CLIENT]:NODES:UPDATE_LOCAL_PEER_COUNT': 112 | return Object.assign({}, state, { 113 | local: Object.assign({}, state.local, { 114 | peerCount: action.payload.peerCount 115 | }) 116 | }); 117 | default: 118 | return state; 119 | } 120 | }; 121 | 122 | export default nodes; 123 | -------------------------------------------------------------------------------- /modules/core/rootReducer.js: -------------------------------------------------------------------------------- 1 | import { combineReducers } from 'redux'; 2 | import newTx from './newTx/reducer'; 3 | import txs from './txs/reducer'; 4 | import nodes from './nodes/reducer'; 5 | import settings from './settings/reducer'; 6 | import ui from './ui/reducer'; 7 | 8 | export default combineReducers({ 9 | newTx, 10 | txs, 11 | nodes, 12 | settings, 13 | ui 14 | }); 15 | -------------------------------------------------------------------------------- /modules/core/settings/reducer.js: -------------------------------------------------------------------------------- 1 | export const SwarmState = { 2 | Enabled: 'Enabled', 3 | Enabling: 'Enabling', 4 | Disabling: 'Disabling', 5 | Disabled: 'Disabled', 6 | Error: 'Error' 7 | }; 8 | 9 | export const initialState = { 10 | appVersion: '', 11 | autoTestMode: false, 12 | dbInit: false, 13 | dbSync: false, 14 | ignoreGpuBlacklist: false, 15 | i18n: '', 16 | ipcProviderBackendInit: false, 17 | productionMode: null, 18 | protocols: [], 19 | rpcMode: '', 20 | swarmState: SwarmState.Disabled, 21 | swarmEnableOnStart: false, 22 | uiMode: '', 23 | updateCheckerRan: false, 24 | cliFlags: {}, 25 | etherPriceUSD: 0 26 | }; 27 | 28 | const settings = (state = initialState, action) => { 29 | switch (action.type) { 30 | case '[MAIN]:DB:INIT': 31 | return Object.assign({}, state, { dbInit: true }); 32 | case '[MAIN]:DB:SYNC_TO_BACKEND': 33 | return Object.assign({}, state, { dbSync: true }); 34 | case '[MAIN]:PROTOCOL:REGISTER': 35 | return Object.assign({}, state, { 36 | protocols: state.protocols.concat(action.payload.protocol) 37 | }); 38 | case '[MAIN]:BUILD_CONFIG:SYNC': 39 | const key = Object.keys(action.payload)[0]; 40 | return Object.assign({}, state, { [key]: action.payload[key] }); 41 | case '[MAIN]:IGNORE_GPU_BLACKLIST:SET': 42 | return Object.assign({}, state, { ignoreGpuBlacklist: true }); 43 | case '[MAIN]:TEST_MODE:SET': 44 | return Object.assign({}, state, { autoTestMode: true }); 45 | case '[MAIN]:CLI_FLAGS:SYNC': 46 | return Object.assign({}, state, { cliFlags: action.payload.cliFlags }); 47 | case '[MAIN]:SET_LANGUAGE_ON_MAIN:SUCCESS': 48 | return Object.assign({}, state, { i18n: action.payload.i18n }); 49 | case '[MAIN]:SWARM:ENABLING': 50 | return Object.assign({}, state, { swarmState: SwarmState.Enabling }); 51 | case '[MAIN]:SWARM:ENABLED': 52 | return Object.assign({}, state, { swarmState: SwarmState.Enabled }); 53 | case '[MAIN]:SWARM:DISABLING': 54 | return Object.assign({}, state, { swarmState: SwarmState.Disabling }); 55 | case '[MAIN]:SWARM:DISABLED': 56 | return Object.assign({}, state, { swarmState: SwarmState.Disabled }); 57 | case '[MAIN]:SWARM:FAILURE': 58 | return Object.assign({}, state, { swarmState: SwarmState.Error }); 59 | case '[MAIN]:SWARM:ENABLE_ON_START': 60 | return Object.assign({}, state, { swarmEnableOnStart: true }); 61 | case '[MAIN]:SWARM:DISABLE_ON_START': 62 | return Object.assign({}, state, { swarmEnableOnStart: false }); 63 | case '[MAIN]:UPDATE_CHECKER:FINISH': 64 | return Object.assign({}, state, { updateCheckerRan: true }); 65 | case '[MAIN]:IPC_PROVIDER_BACKEND:FINISH': 66 | return Object.assign({}, state, { ipcProviderBackendInit: true }); 67 | case '[CLIENT]:GET_PRICE_CONVERSION:START': 68 | return Object.assign({}, state, { 69 | etherPriceUSD: 0 70 | }); 71 | case '[CLIENT]:GET_PRICE_CONVERSION:SUCCESS': 72 | return Object.assign({}, state, { 73 | etherPriceUSD: action.payload.etherPriceUSD 74 | }); 75 | case '[CLIENT]:GET_PRICE_CONVERSION:FAILURE': 76 | return Object.assign({}, state, { 77 | etherPriceUSD: 0 78 | }); 79 | default: 80 | return state; 81 | } 82 | }; 83 | 84 | export default settings; 85 | -------------------------------------------------------------------------------- /modules/core/store.js: -------------------------------------------------------------------------------- 1 | import { createStore, applyMiddleware } from 'redux'; 2 | import { forwardToRenderer, replayActionMain } from 'electron-redux'; 3 | import { composeWithDevTools } from 'remote-redux-devtools'; 4 | import thunk from 'redux-thunk'; 5 | import { app } from 'electron'; 6 | import rootReducer from './rootReducer'; 7 | import { persistStore, persistReducer } from 'redux-persist'; 8 | import createElectronStorage from 'redux-persist-electron-storage'; 9 | 10 | const storage = createElectronStorage({ 11 | electronStoreOpts: { 12 | encryptionKey: 'secret encryption key' 13 | } 14 | }); 15 | 16 | const persistConfig = { 17 | key: 'root', 18 | storage, 19 | whitelist: ['txs'] 20 | }; 21 | 22 | // In development, send Redux actions to a local DevTools server 23 | // Note: run and view these DevTools with `yarn dev:tools` 24 | let debugWrapper = data => data; 25 | if (process.env.NODE_ENV === 'development') { 26 | debugWrapper = composeWithDevTools({ 27 | realtime: true, 28 | port: 8000, 29 | maxAge: 100 30 | }); 31 | } 32 | 33 | const persistedReducer = persistReducer(persistConfig, rootReducer); 34 | 35 | export default function configureReduxStore() { 36 | const store = createStore( 37 | persistedReducer, 38 | debugWrapper(applyMiddleware(thunk, forwardToRenderer)) 39 | ); 40 | persistStore(store); 41 | replayActionMain(store); 42 | return store; 43 | } 44 | -------------------------------------------------------------------------------- /modules/core/txs/reducer.js: -------------------------------------------------------------------------------- 1 | const _ = require('../../utils/underscore.js'); 2 | 3 | export const initialState = []; 4 | 5 | // newTx object is prepared at end of 6 | // confirmTx() in interface/actions.js 7 | 8 | const txs = (state = initialState, action) => { 9 | switch (action.type) { 10 | case '[CLIENT]:NEW_TX:SENT': 11 | const { newTx } = action.payload; 12 | return [newTx, ...state]; 13 | case '[CLIENT]:TX:UPDATE': { 14 | const { tx } = action.payload; 15 | const theTx = _.findWhere(state, { hash: tx.hash }); 16 | const txIndex = _.indexOf(state, theTx); 17 | const newState = [...state]; 18 | newState[txIndex] = Object.assign(theTx, tx); 19 | return newState; 20 | } 21 | default: 22 | return state; 23 | } 24 | }; 25 | 26 | export default txs; 27 | -------------------------------------------------------------------------------- /modules/core/ui/actions.js: -------------------------------------------------------------------------------- 1 | import { app } from 'electron'; 2 | 3 | export function quitApp() { 4 | return dispatch => { 5 | dispatch({ type: '[MAIN]:APP_QUIT:START' }); 6 | try { 7 | app.quit(); 8 | dispatch({ type: '[MAIN]:APP_QUIT:SUCCESS' }); 9 | } catch (error) { 10 | dispatch({ type: '[MAIN]:APP_QUIT:FAILURE', error }); 11 | } 12 | }; 13 | } 14 | 15 | export function openWindow(windowType) { 16 | return { type: '[MAIN]:WINDOW:OPEN', payload: { windowType } }; 17 | } 18 | 19 | export function closeWindow(windowType) { 20 | return { type: '[MAIN]:WINDOW:CLOSE', payload: { windowType } }; 21 | } 22 | 23 | export function reuseGenericWindow(actingType) { 24 | return { type: '[MAIN]:GENERIC_WINDOW:REUSE', payload: { actingType } }; 25 | } 26 | 27 | export function resetGenericWindow() { 28 | return { type: '[MAIN]:GENERIC_WINDOW:RESET' }; 29 | } 30 | -------------------------------------------------------------------------------- /modules/core/ui/reducer.js: -------------------------------------------------------------------------------- 1 | import uniq from 'lodash/uniq'; 2 | 3 | export const initialState = { 4 | appQuit: false, 5 | genericWindowActingType: '', 6 | windowsInit: false, 7 | windowsOpen: [] 8 | }; 9 | 10 | const ui = (state = initialState, action) => { 11 | switch (action.type) { 12 | case '[MAIN]:APP_QUIT:SUCCESS': 13 | return Object.assign({}, state, { appQuit: true }); 14 | case '[MAIN]:WINDOW:OPEN': 15 | return Object.assign({}, state, { 16 | windowsOpen: uniq(state.windowsOpen.concat(action.payload.windowType)) 17 | }); 18 | case '[MAIN]:WINDOW:CLOSE': 19 | return Object.assign({}, state, { 20 | windowsOpen: state.windowsOpen.filter(w => { 21 | return w !== action.payload.windowType; 22 | }) 23 | }); 24 | case '[MAIN]:WINDOWS:INIT_FINISH': 25 | return Object.assign({}, state, { windowsInit: true }); 26 | case '[MAIN]:GENERIC_WINDOW:REUSE': 27 | return Object.assign({}, state, { 28 | genericWindowActingType: action.payload.actingType, 29 | windowsOpen: state.windowsOpen.concat('generic') 30 | }); 31 | case '[MAIN]:GENERIC_WINDOW:RESET': 32 | return Object.assign({}, state, { 33 | genericWindowActingType: '', 34 | windowsOpen: state.windowsOpen.filter(i => i !== 'generic') 35 | }); 36 | default: 37 | return state; 38 | } 39 | }; 40 | 41 | export default ui; 42 | -------------------------------------------------------------------------------- /modules/db.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const Q = require('bluebird'); 3 | const Loki = require('lokijs'); 4 | const Settings = require('./settings'); 5 | const log = require('./utils/logger').create('Db'); 6 | 7 | let db; 8 | 9 | exports.init = () => { 10 | const filePath = Settings.dbFilePath; 11 | 12 | return Q.try(() => { 13 | // if db file doesn't exist then create it 14 | try { 15 | log.debug(`Check that db exists and it's writeable: ${filePath}`); 16 | fs.accessSync(filePath, fs.R_OK | fs.W_OK); 17 | return Q.resolve(); 18 | } catch (err) { 19 | log.info(`Creating db: ${filePath}`); 20 | 21 | const tempdb = new Loki(filePath, { 22 | env: 'NODEJS', 23 | autoload: false 24 | }); 25 | 26 | return new Q.promisify(tempdb.saveDatabase, { context: tempdb })(); 27 | } 28 | }).then(() => { 29 | log.info(`Loading db: ${filePath}`); 30 | 31 | return new Q((resolve, reject) => { 32 | db = new Loki(filePath, { 33 | env: 'NODEJS', 34 | autosave: true, 35 | autosaveInterval: 5000, 36 | autoload: true, 37 | autoloadCallback(err) { 38 | if (err) { 39 | log.error(err); 40 | reject(new Error('Error instantiating db')); 41 | } 42 | resolve(); 43 | } 44 | }); 45 | }); 46 | }); 47 | }; 48 | 49 | exports.getCollection = name => { 50 | if (!db.getCollection(name)) { 51 | db.addCollection(name, { 52 | unique: ['_id'] 53 | }); 54 | } 55 | 56 | return db.getCollection(name); 57 | }; 58 | 59 | exports.close = () => { 60 | return new Q((resolve, reject) => { 61 | db.close(err => { 62 | if (err) { 63 | reject(err); 64 | } else { 65 | resolve(); 66 | } 67 | }); 68 | }); 69 | }; 70 | -------------------------------------------------------------------------------- /modules/i18n.js: -------------------------------------------------------------------------------- 1 | /** 2 | The i18n module, loads the language files and initializes i18next 3 | 4 | @module i18n 5 | */ 6 | const fs = require('fs'); 7 | const i18n = require('i18next'); 8 | const extend = require('lodash/extend'); 9 | 10 | let i18nConf = fs.readFileSync(`${__dirname}/../interface/project-tap.i18n`); 11 | i18nConf = JSON.parse(i18nConf); 12 | 13 | const resources = { 14 | dev: { translation: require('../interface/i18n/mist.en.i18n.json') } 15 | }; 16 | 17 | // add supported languages 18 | i18nConf.supported_languages.forEach(lang => { 19 | const mistTranslations = require(`../interface/i18n/mist.${lang}.i18n.json`); 20 | const uiTranslations = require(`../interface/i18n/app.${lang}.i18n.json`); 21 | resources[lang] = { translation: extend(mistTranslations, uiTranslations) }; 22 | }); 23 | 24 | /** 25 | * Given a language code, get best matched code from supported languages. 26 | * 27 | * > getBestMatchedLangCode('en-US') 28 | * 'en' 29 | * > getBestMatchedLangCode('zh-TW') 30 | * 'zh-TW' 31 | * > getBestMatchedLangCode('no-such-code') 32 | * 'en' 33 | */ 34 | i18n.getBestMatchedLangCode = langCode => { 35 | const codeList = Object.keys(resources); 36 | let bestMatchedCode = langCode; 37 | if (codeList.indexOf(langCode) === -1) { 38 | if (codeList.indexOf(langCode.substr(0, 2)) > -1) { 39 | bestMatchedCode = langCode.substr(0, 2); 40 | } else { 41 | bestMatchedCode = 'en'; 42 | } 43 | } 44 | return bestMatchedCode; 45 | }; 46 | 47 | // init i18n 48 | i18n.init({ 49 | lng: global.language || 'en', 50 | resources, 51 | interpolation: { prefix: '__', suffix: '__' } 52 | }); 53 | 54 | module.exports = i18n; 55 | -------------------------------------------------------------------------------- /modules/ipc/ipcProviderWrapper.js: -------------------------------------------------------------------------------- 1 | /** 2 | @module MistUI 3 | */ 4 | 5 | /** 6 | The IPC provider wrapper to communicate to the backend 7 | 8 | @class ipcProviderWrapper 9 | @constructor 10 | */ 11 | 12 | const { ipcRenderer } = require('electron'); 13 | 14 | /** 15 | Gets the writable property. 16 | 17 | @method on('ipcProvider-setWritable') 18 | */ 19 | ipcRenderer.on('ipcProvider-setWritable', (e, writable) => { 20 | // console.debug('ipcProvider-setWritable', writable); 21 | 22 | ipcProviderWrapper.writable = writable; 23 | }); 24 | 25 | const ipcProviderWrapper = { 26 | writable: false, 27 | 28 | /** 29 | Connects the IPC on the backend to the geth node 30 | 31 | Note: web3.eth.net.isListening (previously: web3.isConnected) will always return true, 32 | as otherwise race conditions can occur, 33 | letting it look like youre not connected via IPC. 34 | 35 | @method connect 36 | */ 37 | connect(path) { 38 | // console.debug('ipcProviderWrapper: connect'); 39 | 40 | ipcRenderer.send('ipcProvider-create', path); 41 | 42 | return this; 43 | }, 44 | /** 45 | Returns data from the IPC through the backend 46 | 47 | @method on 48 | @param {String} name `connect`, `error`, `end`, `timeout` or `data` 49 | @param {Funciton} callback 50 | */ 51 | on(name, callback) { 52 | // console.debug('ipcProviderWrapper: add listener', name); 53 | 54 | ipcRenderer.on(`ipcProvider-${name}`, (e, result) => { 55 | callback(result); 56 | }); 57 | }, 58 | /** 59 | Returns data from the IPC through the backend 60 | 61 | @method once 62 | @param {String} name `connect`, `error`, `end`, `timeout` or `data` 63 | @param {Funciton} callback 64 | */ 65 | once(name, callback) { 66 | // console.debug('ipcProviderWrapper: add listener', name); 67 | 68 | ipcRenderer.once(`ipcProvider-${name}`, (e, result) => { 69 | callback(result); 70 | }); 71 | }, 72 | /** 73 | Removes listener 74 | 75 | @method removeListener 76 | */ 77 | removeListener(name, callback) { 78 | // console.debug('ipcProviderWrapper: remove listener', name); 79 | 80 | ipcRenderer.removeListener(`ipcProvider-${name}`, callback); 81 | }, 82 | 83 | /** 84 | Removes all listeners 85 | 86 | @method removeAllListeners 87 | */ 88 | removeAllListeners(name) { 89 | // console.debug('ipcProviderWrapper: remove all listeners', name); 90 | 91 | if (name) { 92 | ipcRenderer.removeAllListeners(`ipcProvider-${name}`); 93 | } else { 94 | ipcRenderer.removeAllListeners('ipcProvider-error'); 95 | ipcRenderer.removeAllListeners('ipcProvider-end'); 96 | ipcRenderer.removeAllListeners('ipcProvider-timeout'); 97 | ipcRenderer.removeAllListeners('ipcProvider-connect'); 98 | } 99 | }, 100 | /** 101 | Write to the IPC connection through the backend 102 | 103 | @method write 104 | */ 105 | write(payload) { 106 | // console.debug('ipcProviderWrapper: write payload'); 107 | 108 | ipcRenderer.send('ipcProvider-write', payload); 109 | }, 110 | /** 111 | Write synchronous to the IPC connection through the backend 112 | 113 | @method writeSync 114 | */ 115 | writeSync(payload) { 116 | // console.debug('ipcProviderWrapper: write payload (sync)'); 117 | 118 | return ipcRenderer.sendSync('ipcProvider-writeSync', payload); 119 | } 120 | }; 121 | 122 | module.exports = ipcProviderWrapper; 123 | -------------------------------------------------------------------------------- /modules/ipc/methods/eth_accounts.js: -------------------------------------------------------------------------------- 1 | const BaseProcessor = require('./base'); 2 | const db = require('../../db'); 3 | 4 | /** 5 | * Process method: eth_accounts 6 | */ 7 | module.exports = class extends BaseProcessor { 8 | /** 9 | * @override 10 | */ 11 | sanitizeResponsePayload(conn, payload, isPartOfABatch) { 12 | this._log.trace('Sanitize eth_acconts', payload.result); 13 | 14 | // if not an admin connection then return only permissioned accounts 15 | if (!this._isAdminConnection(conn)) { 16 | const tab = db.getCollection('UI_tabs').findOne({ webviewId: conn.id }); 17 | if (tab && tab.permissions && tab.permissions.accounts) { 18 | payload.result = tab.permissions.accounts; 19 | } else { 20 | payload.result = []; 21 | } 22 | } 23 | 24 | return super.sanitizeResponsePayload(conn, payload, isPartOfABatch); 25 | } 26 | }; 27 | -------------------------------------------------------------------------------- /modules/ipc/methods/eth_coinbase.js: -------------------------------------------------------------------------------- 1 | const _ = global._; 2 | const BaseProcessor = require('./base'); 3 | const db = require('../../db'); 4 | 5 | /** 6 | * Process method: eth_coinbase 7 | */ 8 | module.exports = class extends BaseProcessor { 9 | /** 10 | * @override 11 | */ 12 | sanitizeResponsePayload(conn, payload, isPartOfABatch) { 13 | this._log.trace('Sanitize account eth_coinbase', payload.result); 14 | 15 | // if not an admin connection then do a check 16 | if (!this._isAdminConnection(conn)) { 17 | const tab = db.getCollection('UI_tabs').findOne({ webviewId: conn.id }); 18 | 19 | if (_.get(tab, 'permissions.accounts')) { 20 | payload.result = _.contains(tab.permissions.accounts, payload.result) 21 | ? payload.result 22 | : null; 23 | } else { 24 | payload.result = null; 25 | } 26 | } 27 | return super.sanitizeResponsePayload(conn, payload, isPartOfABatch); 28 | } 29 | }; 30 | -------------------------------------------------------------------------------- /modules/ipc/methods/eth_compileSolidity.js: -------------------------------------------------------------------------------- 1 | const _ = require('../../utils/underscore.js'); 2 | const solc = require('solc'); 3 | const Q = require('bluebird'); 4 | const BaseProcessor = require('./base'); 5 | 6 | /** 7 | * Process method: eth_compileSolidity 8 | */ 9 | module.exports = class extends BaseProcessor { 10 | /** 11 | * @override 12 | */ 13 | exec(conn, payload) { 14 | return Q.try(() => { 15 | this._log.debug('Compile solidity'); 16 | 17 | const output = solc.compile(payload.params[0], 1); // 1 activates the optimiser 18 | 19 | const finalResult = _.extend({}, payload); 20 | 21 | if (!output || output.errors) { 22 | let msg = output ? output.errors : 'Compile error'; 23 | 24 | if (_.isArray(msg)) { 25 | msg = msg.join(', '); 26 | } 27 | 28 | finalResult.error = { 29 | code: -32700, 30 | message: msg 31 | }; 32 | } else { 33 | finalResult.result = output.contracts; 34 | } 35 | 36 | return finalResult; 37 | }); 38 | } 39 | 40 | /** 41 | * @override 42 | */ 43 | sanitizeRequestPayload(conn, payload, isPartOfABatch) { 44 | if (isPartOfABatch) { 45 | throw this.ERRORS.BATCH_COMPILE_DENIED; 46 | } 47 | 48 | return super.sanitizeRequestPayload(conn, payload, isPartOfABatch); 49 | } 50 | }; 51 | -------------------------------------------------------------------------------- /modules/ipc/methods/eth_sendTransaction.js: -------------------------------------------------------------------------------- 1 | const BaseProcessor = require('./base'); 2 | const Windows = require('../../windows'); 3 | const Q = require('bluebird'); 4 | const { ipcMain: ipc } = require('electron'); 5 | const BlurOverlay = require('../../blurOverlay'); 6 | 7 | /** 8 | * Process method: eth_sendTransaction 9 | */ 10 | module.exports = class extends BaseProcessor { 11 | /** 12 | * @override 13 | */ 14 | sanitizeRequestPayload(conn, payload, isPartOfABatch) { 15 | if (isPartOfABatch) { 16 | throw this.ERRORS.BATCH_TX_DENIED; 17 | } 18 | 19 | return super.sanitizeRequestPayload(conn, payload, isPartOfABatch); 20 | } 21 | 22 | /** 23 | * @override 24 | */ 25 | exec(conn, payload) { 26 | return new Q((resolve, reject) => { 27 | this._log.info('Ask user for password'); 28 | 29 | this._log.info(payload.params[0]); 30 | 31 | // validate data 32 | try { 33 | _.each(payload.params[0], (val, key) => { 34 | // if doesn't have hex then leave 35 | if (!_.isString(val)) { 36 | throw this.ERRORS.INVALID_PAYLOAD; 37 | } else { 38 | // make sure all data is lowercase and has 0x 39 | if (val) val = `0x${val.toLowerCase().replace(/^0x/, '')}`; 40 | 41 | if (val.substr(2).match(/[^0-9a-f]/gim)) { 42 | throw this.ERRORS.INVALID_PAYLOAD; 43 | } 44 | } 45 | 46 | payload.params[0][key] = val; 47 | }); 48 | } catch (err) { 49 | return reject(err); 50 | } 51 | 52 | store.dispatch({ 53 | type: '[CLIENT]:NEW_TX:START', 54 | payload: payload.params[0] 55 | }); 56 | 57 | const modalWindow = Windows.createPopup('sendTx', { 58 | sendData: { uiAction_sendData: payload.params[0] } 59 | }); 60 | 61 | BlurOverlay.enable(); 62 | 63 | modalWindow.on('hidden', () => { 64 | BlurOverlay.disable(); 65 | 66 | // user cancelled? 67 | if (!modalWindow.processed) { 68 | reject(this.ERRORS.TX_DENIED); 69 | } 70 | }); 71 | 72 | modalWindow.on('close', () => { 73 | BlurOverlay.disable(); 74 | 75 | // user cancelled? 76 | if (!modalWindow.processed) { 77 | reject(this.ERRORS.TX_DENIED); 78 | } 79 | }); 80 | 81 | ipc.once( 82 | 'backendAction_unlockedAccountAndSentTransaction', 83 | (ev, err, result) => { 84 | if ( 85 | Windows.getById(ev.sender.id) === modalWindow && 86 | !modalWindow.isClosed 87 | ) { 88 | if (err || !result) { 89 | this._log.debug('Confirmation error', err); 90 | reject(err || this.ERRORS.TX_DENIED); 91 | } else { 92 | this._log.info('Transaction sent', result); 93 | resolve(result); 94 | } 95 | 96 | modalWindow.processed = true; 97 | modalWindow.close(); 98 | } 99 | } 100 | ); 101 | }).then(result => { 102 | return _.extend({}, payload, { 103 | result 104 | }); 105 | }); 106 | } 107 | }; 108 | -------------------------------------------------------------------------------- /modules/preloader/.eslintrc.yml: -------------------------------------------------------------------------------- 1 | globals: 2 | Helpers: true 3 | -------------------------------------------------------------------------------- /modules/preloader/dapps.js: -------------------------------------------------------------------------------- 1 | /** 2 | @module preloader dapps 3 | */ 4 | require('./browser.js'); 5 | -------------------------------------------------------------------------------- /modules/preloader/include/common.js: -------------------------------------------------------------------------------- 1 | module.exports = windowType => { 2 | const { ipcRenderer, webFrame } = require('electron'); 3 | 4 | if (process.env.TEST_MODE) { 5 | window.electronRequire = require; 6 | } 7 | 8 | // disable pinch zoom 9 | webFrame.setZoomLevelLimits(1, 1); 10 | 11 | require('./consoleLogCapture')(windowType); // !!! 12 | require('./suppressWindowPrompt')(); 13 | 14 | // register with window manager 15 | ipcRenderer.send('backendAction_setWindowId'); 16 | }; 17 | -------------------------------------------------------------------------------- /modules/preloader/include/consoleLogCapture.js: -------------------------------------------------------------------------------- 1 | const { ipcRenderer } = require('electron'); 2 | 3 | const extractLineNumberFromStack = function(stack) { 4 | // / 5 | // / Get the line/filename detail from a Webkit stack trace. See http://stackoverflow.com/a/3806596/1037948 6 | // / 7 | // / the stack string 8 | 9 | let line = stack.split('\n')[2]; 10 | // fix for various display text 11 | if (line) { 12 | line = 13 | line.indexOf(' (') >= 0 14 | ? line.split(' (')[1].substring(0, line.length - 1) 15 | : line.split('at ')[1]; 16 | return line; 17 | } 18 | }; 19 | 20 | module.exports = function(windowId) { 21 | if (typeof window === 'undefined') { 22 | return; 23 | } 24 | 25 | windowId = windowId || window.location.url; 26 | 27 | const console = window.console; 28 | 29 | // send console logging to IPC backend 30 | ['trace', 'debug', 'info', 'warn', 'error', 'log'].forEach(method => { 31 | console[`_${method}`] = console[method]; 32 | console[method] = (function(origMethod) { 33 | return function() { 34 | const args = Array.from(arguments); 35 | 36 | const suffix = `@ ${ 37 | this.lineNumber 38 | ? `${this.fileName}:${this.lineNumber}:1` 39 | : extractLineNumberFromStack(new Error().stack) 40 | }`; 41 | 42 | origMethod.apply(console, args.concat([suffix])); 43 | 44 | try { 45 | ipcRenderer.send( 46 | 'console_log', 47 | windowId, 48 | method === 'log' ? 'info' : method, 49 | JSON.stringify(args) 50 | ); 51 | } catch (err) { 52 | console._warn( 53 | 'Unable to stringify arguments to log to backend', 54 | err.stack 55 | ); 56 | } 57 | }; 58 | })(console[method]); 59 | }); 60 | }; 61 | -------------------------------------------------------------------------------- /modules/preloader/include/getFavicon.js: -------------------------------------------------------------------------------- 1 | /** 2 | Gets the favicon url 3 | 4 | @module getFavicon 5 | */ 6 | 7 | const { ipcRenderer } = require('electron'); 8 | 9 | (function() { 10 | document.addEventListener('DOMContentLoaded', DOMContentLoaded, false); 11 | 12 | function DOMContentLoaded(event) { 13 | const icon = 14 | document.querySelector('link[rel="apple-touch-icon"]') || 15 | document.querySelector('link[type="image/x-icon"]') || 16 | document.querySelector('link[rel="shortcut"]') || 17 | document.querySelector('link[rel="shortcut icon"]') || 18 | document.querySelector('link[rel="icon"]'); 19 | 20 | if (icon) { 21 | ipcRenderer.sendToHost('favicon', icon.href); 22 | } else { 23 | ipcRenderer.sendToHost('favicon', null); 24 | } 25 | 26 | document.removeEventListener('DOMContentLoaded', DOMContentLoaded, false); 27 | } 28 | })(); 29 | -------------------------------------------------------------------------------- /modules/preloader/include/getMetaTags.js: -------------------------------------------------------------------------------- 1 | /** 2 | Gest the meta[name="ethereum-dapp-url-bar-style"] meta tag 3 | 4 | @module getMetaTags 5 | */ 6 | 7 | const { ipcRenderer } = require('electron'); 8 | 9 | module.export = (function() { 10 | document.addEventListener('DOMContentLoaded', DOMContentLoaded, false); 11 | 12 | function DOMContentLoaded(event) { 13 | const appBar = document.querySelector( 14 | 'meta[name="ethereum-dapp-url-bar-style"]' 15 | ); 16 | 17 | if (appBar) { 18 | ipcRenderer.sendToHost('appBar', appBar.content); 19 | } else { 20 | ipcRenderer.sendToHost('appBar', null); 21 | } 22 | 23 | document.removeEventListener('DOMContentLoaded', DOMContentLoaded, false); 24 | } 25 | })(); 26 | -------------------------------------------------------------------------------- /modules/preloader/include/openExternal.js: -------------------------------------------------------------------------------- 1 | /** 2 | Opens windows and popups 3 | 4 | @module openExternal 5 | */ 6 | 7 | const { shell } = require('electron'); 8 | 9 | // open a[target="_blank"] in external browser 10 | document.addEventListener( 11 | 'click', 12 | e => { 13 | let node = false; 14 | 15 | if (e.target.nodeName === 'A') { 16 | node = e.target; 17 | } else if (e.target.parentNode && e.target.parentNode.nodeName === 'A') { 18 | node = e.target.parentNode; 19 | } 20 | 21 | // open in browser 22 | if ( 23 | node && 24 | node.attributes.target && 25 | node.attributes.target.value === '_blank' 26 | ) { 27 | e.preventDefault(); 28 | shell.openExternal(node.href); 29 | } 30 | }, 31 | false 32 | ); 33 | -------------------------------------------------------------------------------- /modules/preloader/include/setBasePath.js: -------------------------------------------------------------------------------- 1 | /** 2 | Sets the base path in production for the file protocol, so assets are loaded properly 3 | 4 | @module setBasePath 5 | */ 6 | 7 | const { remote } = require('electron'); 8 | const path = require('path'); 9 | 10 | module.exports = appPath => { 11 | // set the base path for relative assets in production mode 12 | if (remote.getGlobal('production') && ~location.origin.indexOf('file://')) { 13 | window.basePathHref = `${String( 14 | path.resolve(`${__dirname}/../../${appPath}`) 15 | ) 16 | .replace(/\\/g, '/') 17 | .replace('/interface', '/app.asar/interface')}/`; 18 | } 19 | }; 20 | -------------------------------------------------------------------------------- /modules/preloader/include/suppressWindowPrompt.js: -------------------------------------------------------------------------------- 1 | module.exports = () => { 2 | window.prompt = () => { 3 | console.warn("Mist doesn't support window.prompt()"); 4 | }; 5 | }; 6 | -------------------------------------------------------------------------------- /modules/preloader/include/web3CurrentProvider.js: -------------------------------------------------------------------------------- 1 | /** 2 | Sets the ethereum provider, as well as "web3" for backwards compatibility. 3 | 4 | @module ethereumProvider 5 | */ 6 | const Web3 = require('web3'); 7 | const BigNumber = require('bignumber.js'); 8 | const ipcProviderWrapper = require('../../ipc/ipcProviderWrapper.js'); 9 | const LegacyWeb3IpcProvider = require('./legacyWeb3IpcProvider.js'); 10 | 11 | // SET ETHEREUM PROVIDER 12 | // window.ethereumProvider = new Web3.providers.IpcProvider('', ipcProviderWrapper); 13 | 14 | // LEGACY 15 | window.BigNumber = BigNumber; 16 | window.web3 = { 17 | currentProvider: new LegacyWeb3IpcProvider('', ipcProviderWrapper) 18 | }; 19 | 20 | // for now still add this too: WILL BE REMOVED with web3 1.0 21 | window.web3 = new Web3(new Web3.providers.IpcProvider('', ipcProviderWrapper)); 22 | -------------------------------------------------------------------------------- /modules/preloader/popupWindows.js: -------------------------------------------------------------------------------- 1 | /** 2 | @module preloader PopupWindows 3 | */ 4 | 5 | require('./popupWindowsNoWeb3.js'); 6 | require('./include/web3CurrentProvider.js'); 7 | const Q = require('bluebird'); 8 | const https = require('https'); 9 | 10 | // make variables globally accessable 11 | window.Q = Q; 12 | window.https = https; 13 | -------------------------------------------------------------------------------- /modules/preloader/popupWindowsNoWeb3.js: -------------------------------------------------------------------------------- 1 | /** 2 | @module preloader PopupWindows 3 | */ 4 | 5 | require('babel-register'); 6 | require('./include/common')('popupWindow'); 7 | const { ipcRenderer, remote, webFrame } = require('electron'); 8 | const mist = require('./include/mistAPI.js'); 9 | const dbSync = require('../dbSync.js'); 10 | require('./include/setBasePath')('interface'); 11 | require('./include/openExternal.js'); 12 | 13 | // receive data in from SendData 14 | ipcRenderer.on('uiAction_sendData', (e, data) => { 15 | Session.set('data', data); 16 | }); 17 | 18 | window.mist = mist(); 19 | window.mistMode = remote.getGlobal('mode'); 20 | window.dirname = remote.getGlobal('dirname'); 21 | window.dbSync = dbSync; 22 | window.ipc = ipcRenderer; 23 | 24 | window.i18n = require('../i18n.js'); 25 | 26 | // Initialise the Redux store 27 | window.store = require('./rendererStore'); 28 | -------------------------------------------------------------------------------- /modules/preloader/rendererStore.js: -------------------------------------------------------------------------------- 1 | // Create the client-side Redux store. 2 | // This store's only purpose is to sync with the main store. 3 | const { createStore, applyMiddleware } = require('redux'); 4 | const { 5 | forwardToMain, 6 | getInitialStateRenderer, 7 | replayActionRenderer 8 | } = require('electron-redux'); 9 | const thunk = require('redux-thunk').default; 10 | const initialState = getInitialStateRenderer(); 11 | import { persistStore, persistReducer } from 'redux-persist'; 12 | import rootReducer from '../core/rootReducer'; 13 | 14 | // No persistence required for auxiliary stores. 15 | // This config is to stub out the functionality. 16 | const persistConfig = { 17 | key: 'root', 18 | storage: {}, 19 | whitelist: [] 20 | }; 21 | const persistedReducer = persistReducer(persistConfig, rootReducer); 22 | 23 | const store = createStore( 24 | persistedReducer, 25 | initialState, 26 | applyMiddleware(forwardToMain, thunk) 27 | ); 28 | 29 | replayActionRenderer(store); 30 | 31 | module.exports = store; 32 | -------------------------------------------------------------------------------- /modules/preloader/tests.js: -------------------------------------------------------------------------------- 1 | /** 2 | @module preloader tests 3 | */ 4 | 5 | if (location.origin !== 'file://') { 6 | throw new Error('Wrong test file loaded'); 7 | } else { 8 | // load dapp preloader file 9 | require('./dapps.js'); 10 | 11 | const electron = require('electron'); 12 | const ipcRenderer = electron.ipcRenderer; 13 | 14 | window.ipcProvider = require('../ipc/ipcProviderWrapper.js'); 15 | window.permissions = {}; 16 | 17 | ipcRenderer.sendToHost('sendTestData'); 18 | ipcRenderer.on('uiAction_sendTestData', function(e, permissions, coinbase) { 19 | window.permissions = permissions; 20 | window.coinbase = coinbase; 21 | }); 22 | } 23 | -------------------------------------------------------------------------------- /modules/preloader/walletMain.js: -------------------------------------------------------------------------------- 1 | /** 2 | @module preloader wallet when loaded in the main window 3 | */ 4 | 5 | require('./dapps.js'); 6 | require('./include/openExternal.js'); 7 | require('./include/setBasePath')('interface/wallet'); 8 | const { webFrame } = require('electron'); 9 | 10 | // make variables globally accessable 11 | // window.dirname = __dirname; 12 | 13 | webFrame.executeJavaScript("window.mistMode = 'wallet';"); 14 | 15 | setTimeout(() => { 16 | if (document.getElementsByTagName('html')[0]) { 17 | document.getElementsByTagName('html')[0].className = window.platform; 18 | } 19 | }, 500); 20 | -------------------------------------------------------------------------------- /modules/socketManager.js: -------------------------------------------------------------------------------- 1 | const _ = require('./utils/underscore.js'); 2 | const Q = require('bluebird'); 3 | const log = require('./utils/logger').create('Sockets'); 4 | 5 | const Web3IpcSocket = require('./sockets/web3Ipc'); 6 | const Web3HttpSocket = require('./sockets/web3Http'); 7 | 8 | /** 9 | * `Socket` manager. 10 | */ 11 | class SocketManager { 12 | constructor() { 13 | this._sockets = {}; 14 | } 15 | 16 | /** 17 | * Get socket with given id, creating it if it does not exist. 18 | * 19 | * @return {Socket} 20 | */ 21 | create(id, type) { 22 | log.debug(`Create socket, id=${id}, type=${type}`); 23 | 24 | switch (type) { 25 | case 'ipc': 26 | this._sockets[id] = new Web3IpcSocket(this, id); 27 | break; 28 | case 'http': 29 | this._sockets[id] = new Web3HttpSocket(this, id); 30 | break; 31 | default: 32 | throw new Error(`Unrecognized socket type: ${type}`); 33 | } 34 | 35 | return this._sockets[id]; 36 | } 37 | 38 | /** 39 | * Get socket with given id, creating it if it does not exist. 40 | * 41 | * @return {Socket} 42 | */ 43 | get(id, type) { 44 | if (!this._sockets[id]) { 45 | this.create(id, type); 46 | } 47 | 48 | return this._sockets[id]; 49 | } 50 | 51 | /** 52 | * @return {Promise} 53 | */ 54 | destroyAll() { 55 | log.info('Destroy all sockets'); 56 | 57 | return Q.all( 58 | _.map(this._sockets, (s, id) => { 59 | this.remove(id); 60 | return s.destroy(); 61 | }) 62 | ); 63 | } 64 | 65 | /** 66 | * Remove socket with given id from this manager. 67 | * 68 | * Usually called by `Socket` instances when they're destroyed. 69 | */ 70 | remove(id) { 71 | log.debug(`Remove socket, id=${id}`); 72 | 73 | delete this._sockets[id]; 74 | } 75 | } 76 | 77 | module.exports = new SocketManager(); 78 | -------------------------------------------------------------------------------- /modules/sockets/web3Http.js: -------------------------------------------------------------------------------- 1 | const Q = require('bluebird'); 2 | const EventEmitter = require('events').EventEmitter; 3 | const got = require('got'); 4 | const SocketBase = require('./base'); 5 | 6 | const STATE = SocketBase.STATE; 7 | 8 | const Web3SocketBase = require('./web3Base'); 9 | 10 | class HttpSocket extends EventEmitter { 11 | constructor(_parentSocket) { 12 | super(); 13 | 14 | this._log = _parentSocket._log.create('HttpSocket'); 15 | } 16 | 17 | connect(connectConfig) { 18 | this._log.trace('Connect', connectConfig); 19 | 20 | this._hostPort = connectConfig.hostPort; 21 | 22 | const payload = JSON.stringify({ 23 | jsonrpc: '2.0', 24 | id: 0, 25 | method: 'eth_accounts', 26 | params: [] 27 | }); 28 | 29 | this._call(payload) 30 | .then(() => { 31 | this._log.trace('Connection successful'); 32 | 33 | this.emit('connect'); 34 | }) 35 | .catch(err => { 36 | this._log.trace('Connection failed', err); 37 | 38 | this.emit.bind(this, new Error('Unable to connect to HTTP RPC')); 39 | }); 40 | } 41 | 42 | destroy() { 43 | this._log.trace('Destroy'); 44 | 45 | this._hostPort = null; 46 | 47 | this.emit('close'); 48 | } 49 | 50 | write(data) { 51 | this._log.trace('Write data', data); 52 | 53 | this._call(data) 54 | .then(body => { 55 | this._log.trace('Got response', body); 56 | 57 | this.emit('data', body); 58 | }) 59 | .catch(this.emit.bind(this, 'error')); 60 | } 61 | 62 | setEncoding(enc) { 63 | this._log.trace('Set encoding', enc); 64 | 65 | this._encoding = enc; 66 | } 67 | 68 | _call(dataStr) { 69 | return got 70 | .post(this._hostPort, { 71 | encoding: this._encoding, 72 | headers: { 73 | 'Content-Type': 'application/json' 74 | }, 75 | body: dataStr 76 | }) 77 | .then(res => { 78 | return res.body; 79 | }); 80 | } 81 | } 82 | 83 | module.exports = class Web3HttpSocket extends Web3SocketBase { 84 | /** 85 | * Reset socket. 86 | */ 87 | _resetSocket() { 88 | this._log.debug('Resetting socket'); 89 | 90 | return Q.try(() => { 91 | if (STATE.CONNECTED === this._state) { 92 | this._log.debug('Disconnecting prior to reset'); 93 | 94 | return this.disconnect(); 95 | } 96 | }).then(() => { 97 | this._socket = new HttpSocket(this); 98 | 99 | this._socket.setEncoding('utf8'); 100 | 101 | this._socket.on('close', hadError => { 102 | // if we did the disconnection then all good 103 | if (STATE.DISCONNECTING === this._state) { 104 | return; 105 | } 106 | 107 | this.emit('close', hadError); 108 | }); 109 | 110 | this._socket.on('data', data => { 111 | this._log.trace('Got data'); 112 | 113 | this.emit('data', data); 114 | }); 115 | 116 | this._socket.on('error', err => { 117 | // connection errors will be handled in connect() code 118 | if (STATE.CONNECTING === this._state) { 119 | return; 120 | } 121 | 122 | this._log.error(err); 123 | 124 | this.emit('error', err); 125 | }); 126 | }); 127 | } 128 | }; 129 | -------------------------------------------------------------------------------- /modules/sockets/web3Ipc.js: -------------------------------------------------------------------------------- 1 | const Q = require('bluebird'); 2 | const net = require('net'); 3 | const SocketBase = require('./base'); 4 | 5 | const STATE = SocketBase.STATE; 6 | 7 | const Web3SocketBase = require('./web3Base'); 8 | 9 | module.exports = class Web3IpcSocket extends Web3SocketBase { 10 | /** 11 | * Reset socket. 12 | */ 13 | _resetSocket() { 14 | this._log.debug('Resetting socket'); 15 | 16 | return Q.try(() => { 17 | if (STATE.CONNECTED === this._state) { 18 | this._log.debug('Disconnecting prior to reset'); 19 | 20 | return this.disconnect(); 21 | } 22 | }).then(() => { 23 | this._socket = new net.Socket(); 24 | 25 | this._socket.setTimeout(0); 26 | this._socket.setEncoding('utf8'); 27 | this._socket.unref(); /* allow app to exit even if socket fails to close */ 28 | 29 | this._socket.on('close', hadError => { 30 | // if we did the disconnection then all good 31 | if (STATE.DISCONNECTING === this._state) { 32 | return; 33 | } 34 | 35 | this.emit('close', hadError); 36 | }); 37 | 38 | this._socket.on('end', () => { 39 | this._log.debug('Got "end" event'); 40 | 41 | this.emit('end'); 42 | }); 43 | 44 | this._socket.on('data', data => { 45 | this._log.trace('Got data'); 46 | 47 | this.emit('data', data); 48 | }); 49 | 50 | this._socket.on('timeout', () => { 51 | this._log.trace('Timeout'); 52 | 53 | this.emit('timeout'); 54 | }); 55 | 56 | this._socket.on('error', err => { 57 | // connection errors will be handled in connect() code 58 | if (STATE.CONNECTING === this._state) { 59 | return; 60 | } 61 | 62 | this._log.error(err); 63 | 64 | this.emit('error', err); 65 | }); 66 | }); 67 | } 68 | }; 69 | -------------------------------------------------------------------------------- /modules/swarmNode.js: -------------------------------------------------------------------------------- 1 | const EventEmitter = require('events').EventEmitter; 2 | const Q = require('bluebird'); 3 | const path = require('path'); 4 | const fs = require('fs'); 5 | const os = require('os'); 6 | const clientBinaries = require('./../clientBinaries.json'); 7 | 8 | import Settings from './settings'; 9 | import Swarm from 'swarm-js'; 10 | 11 | let instance = null; 12 | 13 | class SwarmNode extends EventEmitter { 14 | constructor() { 15 | super(); 16 | 17 | if (!instance) { 18 | instance = this; 19 | } 20 | 21 | return instance; 22 | } 23 | 24 | getKeyPath() { 25 | // Gets Swarm Key path 26 | const swarmKeyDir = path.join(Settings.userDataPath, 'swarmjs'); 27 | const swarmKeyPath = path.join(swarmKeyDir, 'swarmKey'); 28 | 29 | // Generates the key if not there 30 | if (!fs.existsSync(swarmKeyDir)) { 31 | fs.mkdirSync(swarmKeyDir); 32 | } 33 | if (!fs.existsSync(swarmKeyPath)) { 34 | fs.writeFileSync(swarmKeyPath, web3.utils.randomHex(32)); 35 | } 36 | 37 | return swarmKeyPath; 38 | } 39 | 40 | startUsingLocalNode() { 41 | const totalSize = 7406937; // TODO: hardcoded & innacurate, use archives.json instead 42 | let totalDownloaded = 0; 43 | 44 | const swarmBinDir = path.join(Settings.userDataPath, 'swarmjs', 'bin'); 45 | const swarmBinExt = os.platform() === 'win32' ? '.exe' : ''; 46 | const swarmBinPath = path.join(swarmBinDir, `swarm${swarmBinExt}`); 47 | 48 | const config = { 49 | privateKey: this.getKeyPath(), 50 | dataDir: path.join(Settings.userDataPath, 'swarmjs'), 51 | ensApi: Settings.rpcIpcPath, 52 | binPath: swarmBinPath, 53 | onProgress: size => 54 | this.emit('downloadProgress', (totalDownloaded += size) / totalSize), 55 | archives: clientBinaries.swarm.archives 56 | }; 57 | 58 | return new Q((resolve, reject) => { 59 | Swarm.local(config)( 60 | swarm => 61 | new Q(stop => { 62 | this.emit('started', true); 63 | this._stop = stop; 64 | this._swarm = swarm; 65 | resolve(this); 66 | }) 67 | ).catch(reject); 68 | }); 69 | } 70 | 71 | startUsingGateway() { 72 | return new Q((resolve, reject) => { 73 | this.emit('started', false); 74 | this._swarm = Swarm.at(Settings.swarmURL); 75 | this._stop = () => {}; 76 | resolve(this); 77 | }); 78 | } 79 | 80 | init() { 81 | this.emit('starting'); 82 | 83 | if (Settings.swarmURL === 'http://localhost:8500') { 84 | return this.startUsingLocalNode(); 85 | } 86 | return this.startUsingGateway(); 87 | } 88 | 89 | stop() { 90 | if (!this._swarm) { 91 | return Q.reject(new Error('Swarm not initialized, unable to stop.')); 92 | } 93 | 94 | this.emit('stopping'); 95 | this._stop(); 96 | this.emit('stopped'); 97 | } 98 | 99 | upload(arg) { 100 | if (!this._swarm) { 101 | return Q.reject(new Error('Swarm not initialized, unable to upload.')); 102 | } 103 | 104 | return this._swarm.upload(arg); 105 | } 106 | } 107 | 108 | module.exports = new SwarmNode(); 109 | -------------------------------------------------------------------------------- /modules/updateChecker.js: -------------------------------------------------------------------------------- 1 | const _ = global._; 2 | const Windows = require('./windows'); 3 | const Settings = require('./settings'); 4 | const log = require('./utils/logger').create('updateChecker'); 5 | const got = require('got'); 6 | const semver = require('semver'); 7 | 8 | /** 9 | * Check for updates to the app. 10 | * @return {[type]} [description] 11 | */ 12 | const check = (exports.check = () => { 13 | log.info('Check for update...'); 14 | 15 | let str = null; 16 | 17 | switch ( 18 | Settings.uiMode // eslint-disable-line default-case 19 | ) { 20 | case 'mist': 21 | str = 'mist'; 22 | break; 23 | case 'wallet': 24 | str = 'wallet'; 25 | break; 26 | } 27 | 28 | return got('https://api.github.com/repos/ethereum/mist/releases/latest', { 29 | timeout: 30000, 30 | json: true 31 | }) 32 | .then(res => { 33 | const release = res.body; 34 | 35 | if (!release) { 36 | log.debug('No releases available to check against.'); 37 | 38 | return; 39 | } 40 | 41 | if (semver.gt(release.tag_name, Settings.appVersion)) { 42 | log.info( 43 | `App (${Settings.appVersion}) is out of date. New ${ 44 | release.tag_name 45 | } found.` 46 | ); 47 | 48 | return { 49 | name: release.name, 50 | version: release.tag_name, 51 | url: release.html_url 52 | }; 53 | } 54 | 55 | log.info('App is up-to-date.'); 56 | }) 57 | .catch(err => { 58 | log.error('Error checking for update', err); 59 | }); 60 | }); 61 | 62 | function showWindow(options) { 63 | log.debug('Show update checker window'); 64 | 65 | return Windows.createPopup('updateAvailable', options); 66 | } 67 | 68 | exports.run = () => { 69 | check() 70 | .then(update => { 71 | if (update) { 72 | showWindow({ 73 | sendData: { 74 | uiAction_checkUpdateDone: update 75 | } 76 | }); 77 | } 78 | store.dispatch({ type: '[MAIN]:UPDATE_CHECKER:FINISHED' }); 79 | }) 80 | .catch(err => { 81 | log.error(err); 82 | }); 83 | }; 84 | 85 | exports.runVisibly = () => { 86 | const wnd = showWindow({ 87 | sendData: 'uiAction_checkUpdateInProgress' 88 | }); 89 | 90 | wnd.on('ready', () => { 91 | check() 92 | .then(update => { 93 | wnd.send({ 94 | uiAction_checkUpdateDone: update 95 | }); 96 | }) 97 | .catch(err => { 98 | log.error(err); 99 | 100 | wnd.send('uiAction_checkUpdateDone'); 101 | }); 102 | }); 103 | }; 104 | -------------------------------------------------------------------------------- /modules/utils/logger.js: -------------------------------------------------------------------------------- 1 | const _ = require('./underscore'); 2 | import log4js from 'log4js'; 3 | 4 | /** 5 | * Setup logging system. 6 | * @param {Object} [options] 7 | * @param {String} [options.logLevel] Minimum logging threshold (default: info). 8 | * @param {String} [options.logFolder] Log folder to write logs to. 9 | */ 10 | exports.setup = function(options) { 11 | const logFolder = options.logFolder; 12 | const level = options.logLevel || 'info'; 13 | 14 | const config = { 15 | appenders: { 16 | out: { type: 'console' }, 17 | all: { 18 | type: 'file', 19 | filename: `${logFolder}/all.log` 20 | }, 21 | main: { 22 | type: 'file', 23 | filename: `${logFolder}/category/main.log` 24 | }, 25 | EthereumNode: { 26 | type: 'file', 27 | filename: `${logFolder}/category/ethereum_node.log` 28 | }, 29 | swarm: { 30 | type: 'file', 31 | filename: `${logFolder}/category/swarm.log` 32 | } 33 | }, 34 | categories: { 35 | default: { appenders: ['out', 'all', 'main'], level }, 36 | EthereumNode: { appenders: ['out', 'all', 'EthereumNode'], level }, 37 | swarm: { appenders: ['out', 'all', 'swarm'], level } 38 | } 39 | }; 40 | 41 | log4js.configure(config); 42 | }; 43 | 44 | exports.create = category => { 45 | const logger = log4js.getLogger(category); 46 | 47 | // Allow for easy creation of sub-categories. 48 | logger.create = subCategory => { 49 | return exports.create(`${category}/${subCategory}`); 50 | }; 51 | 52 | return logger; 53 | }; 54 | -------------------------------------------------------------------------------- /modules/utils/underscore.js: -------------------------------------------------------------------------------- 1 | const _ = (module.exports = require('underscore')); 2 | const uuid = require('uuid'); 3 | const underscoreDeepExtend = require('underscore-deep-extend'); 4 | 5 | _.mixin({ 6 | /** 7 | * Get a deeply nested object property. 8 | * 9 | * @param {Object} obj The object. 10 | * @param {String} path The path within the object to fetch. 11 | * @param {*} fallbackValue The value to return if given path not found. 12 | * 13 | * @return {*} Returns value if found; otherwise the fallbackVAlue. 14 | */ 15 | get(obj, path, fallbackValue) { 16 | if (this.isUndefined(obj) || obj === null || typeof path !== 'string') { 17 | return fallbackValue; 18 | } 19 | 20 | const fields = path.split('.'); 21 | let result = obj; 22 | 23 | for (let i = 0; i < fields.length; ++i) { 24 | if (!this.isObject(result) && !this.isArray(result)) { 25 | return fallbackValue; 26 | } 27 | 28 | result = result[fields[i]]; 29 | } 30 | 31 | return result || fallbackValue; 32 | }, 33 | deepExtend: underscoreDeepExtend(_), 34 | uuid() { 35 | return uuid.v4(); 36 | } 37 | }); 38 | 39 | module.exports = _; 40 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Mist", 3 | "version": "0.12.0", 4 | "license": "GPL-3.0", 5 | "author": "Ethereum Mist Team ", 6 | "description": "Ethereum Mist", 7 | "repository": { 8 | "type": "git", 9 | "url": "https://github.com/ethereum/mist.git" 10 | }, 11 | "scripts": { 12 | "precommit": "pretty-quick --staged", 13 | "postinstall": "git submodule update --init --recursive && yarn task pack-wallet && (cd interface && yarn)", 14 | "dev:electron": "electron -r babel-register main.js", 15 | "dev:meteor": "cd interface && meteor --no-release-check", 16 | "dev:tools": "remotedev & (sleep 3 && open http://localhost:8000)", 17 | "test:unit:once": "mocha --compilers babel-register ./tests/unit/ --recursive", 18 | "test:unit": "yarn run test:unit:once --watch", 19 | "test:e2e": "gulp test", 20 | "build:wallet": "gulp --wallet", 21 | "build:mist": "gulp --mist", 22 | "build:interface": "cd interface && meteor-build-client ../build-interface -p ''", 23 | "dist:mist": "gulp upload-queue --mist", 24 | "dist:wallet": "gulp upload-queue --wallet", 25 | "task": "gulp" 26 | }, 27 | "main": "main.js", 28 | "dependencies": { 29 | "babel-preset-es2016-node5": "^1.1.2", 30 | "babel-preset-react": "^6.24.1", 31 | "babel-register": "^6.26.0", 32 | "bignumber.js": "^4.0.1", 33 | "bluebird": "^3.5.0", 34 | "chai-as-promised": "^7.1.1", 35 | "chai-string": "^1.3.0", 36 | "electron-redux": "^1.3.1", 37 | "electron-store": "^2.0.0", 38 | "electron-window-state": "^4.0.1", 39 | "ethereum-client-binaries": "^1.6.4", 40 | "ethereum-keyfile-recognizer": "^1.0.2", 41 | "ethereumjs-abi": "^0.6.3", 42 | "ethereumjs-tx": "^1.3.3", 43 | "ethereumjs-util": "^5.1.2", 44 | "fs-promise": "^2.0.0", 45 | "got": "^7.1.0", 46 | "i18next": "^8.4.3", 47 | "lodash": "^4.17.4", 48 | "log-rotate": "^0.2.7", 49 | "log4js": "^2.4.1", 50 | "lokijs": "^1.4.3", 51 | "minimongo-standalone": "^1.1.0-3", 52 | "node-fetch": "^1.7.3", 53 | "node-unzip-2": "^0.2.7", 54 | "numeral": "^2.0.6", 55 | "oboe": "^2.1.3", 56 | "os-timesync": "^1.0.9", 57 | "react-md-spinner": "^0.3.0", 58 | "redux": "^3.7.2", 59 | "redux-persist": "^5.10.0", 60 | "redux-persist-electron-storage": "^1.1.2", 61 | "redux-thunk": "^2.2.0", 62 | "remote-redux-devtools": "^0.5.12", 63 | "semver": "^5.1.0", 64 | "solc": "0.4.24", 65 | "swarm-js": "^0.1.38", 66 | "typescript": "^2.2.2", 67 | "underscore": "^1.8.3", 68 | "underscore-deep-extend": "^1.1.5", 69 | "uuid": "^3.0.1", 70 | "web3": "^1.0.0-beta.33", 71 | "ws": "^5.1.1", 72 | "yargs": "^8.0.2" 73 | }, 74 | "devDependencies": { 75 | "chai": "^4.1.1", 76 | "co-mocha": "^1.2.0", 77 | "colors": "^1.3.0", 78 | "del": "^3.0.0", 79 | "ecstatic": "^2.1.0", 80 | "electron": "1.8.8", 81 | "electron-builder": "^20.17.2", 82 | "eslint": "^4.19.1", 83 | "eslint-config-prettier": "^3.1.0", 84 | "eslint-plugin-import": "^2.12.0", 85 | "eslint-plugin-prettier": "^2.6.0", 86 | "eslint-plugin-react": "^7.8.2", 87 | "express": "^4.15.3", 88 | "genomatic": "^1.0.0", 89 | "geth-private": "^1.3.0", 90 | "gh-release-assets": "^1.1.0", 91 | "gulp": "^4.0.0", 92 | "gulp-babel": "^7.0.0", 93 | "gulp-spawn-mocha": "^3.3.0", 94 | "husky": "^0.14.3", 95 | "json-structure-diff": "^0.0.2", 96 | "meteor-build-client": "^0.4.1", 97 | "minimist": "^1.2.0", 98 | "mocha": "^3.2.0", 99 | "prettier": "1.13.2", 100 | "pretty-quick": "^1.4.1", 101 | "redux-mock-store": "^1.3.0", 102 | "remotedev-server": "^0.2.4", 103 | "require-dir": "^0.3.2", 104 | "run-sequence": "^2.1.0", 105 | "semver-compare": "^1.0.0", 106 | "shelljs": "^0.8.2", 107 | "spectron": "3.8.0", 108 | "xml2js": "^0.4.17" 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /release.js: -------------------------------------------------------------------------------- 1 | module.exports = async ( 2 | markdown, 3 | metaData 4 | ) => `_Write a brief description of this release._ 5 | 6 | ### Checksums 7 | 8 | | File | Checksum (SHA256) | 9 | | ------------ | ----------------- | 10 | | _Asset name_ | _Asset checksum_ | 11 | 12 | ${markdown}`; 13 | -------------------------------------------------------------------------------- /scripts/Locate.nsh: -------------------------------------------------------------------------------- 1 | !define locate::Open `!insertmacro locate::Open` 2 | 3 | !macro locate::Open _PATH _OPTIONS _HANDLE 4 | locate::_Open /NOUNLOAD `${_PATH}` `${_OPTIONS}` 5 | Pop ${_HANDLE} 6 | !macroend 7 | 8 | 9 | !define locate::Find `!insertmacro locate::Find` 10 | 11 | !macro locate::Find _HANDLE _PATHANDNAME _PATH _NAME _SIZE _TIME _ATTRIB 12 | locate::_Find /NOUNLOAD `${_HANDLE}` 13 | Pop ${_PATHANDNAME} 14 | Pop ${_PATH} 15 | Pop ${_NAME} 16 | Pop ${_SIZE} 17 | Pop ${_TIME} 18 | Pop ${_ATTRIB} 19 | !macroend 20 | 21 | 22 | !define locate::Close `!insertmacro locate::Close` 23 | 24 | !macro locate::Close _HANDLE 25 | locate::_Close /NOUNLOAD `${_HANDLE}` 26 | !macroend 27 | 28 | 29 | !define locate::GetSize `!insertmacro locate::GetSize` 30 | 31 | !macro locate::GetSize _PATH _OPTIONS _SIZE _FILES _DIRS 32 | locate::_GetSize /NOUNLOAD `${_PATH}` `${_OPTIONS}` 33 | Pop ${_SIZE} 34 | Pop ${_FILES} 35 | Pop ${_DIRS} 36 | !macroend 37 | 38 | 39 | !define locate::RMDirEmpty `!insertmacro locate::RMDirEmpty` 40 | 41 | !macro locate::RMDirEmpty _PATH _OPTIONS _REMOVED 42 | locate::_RMDirEmpty /NOUNLOAD `${_PATH}` `${_OPTIONS}` 43 | Pop ${_REMOVED} 44 | !macroend 45 | 46 | 47 | !define locate::Unload `!insertmacro locate::Unload` 48 | 49 | !macro locate::Unload 50 | locate::_Unload 51 | !macroend 52 | -------------------------------------------------------------------------------- /scripts/SimpleFC.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethereum/mist/81228a3102c5444f3c739dcaf98a1185b5bf73d1/scripts/SimpleFC.dll -------------------------------------------------------------------------------- /scripts/ZipDLL.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethereum/mist/81228a3102c5444f3c739dcaf98a1185b5bf73d1/scripts/ZipDLL.dll -------------------------------------------------------------------------------- /scripts/locate.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethereum/mist/81228a3102c5444f3c739dcaf98a1185b5bf73d1/scripts/locate.dll -------------------------------------------------------------------------------- /sounds/bip.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethereum/mist/81228a3102c5444f3c739dcaf98a1185b5bf73d1/sounds/bip.mp3 -------------------------------------------------------------------------------- /sounds/bloop.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethereum/mist/81228a3102c5444f3c739dcaf98a1185b5bf73d1/sounds/bloop.mp3 -------------------------------------------------------------------------------- /sounds/invite.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethereum/mist/81228a3102c5444f3c739dcaf98a1185b5bf73d1/sounds/invite.mp3 -------------------------------------------------------------------------------- /tests/.eslintrc.yml: -------------------------------------------------------------------------------- 1 | globals: # don't warn about missing declarations 2 | describe: true 3 | it: true 4 | expect: true 5 | chai: true 6 | $: true 7 | LastVisitedPages: true 8 | History: true 9 | localStorage: true 10 | -------------------------------------------------------------------------------- /tests/.gitignore: -------------------------------------------------------------------------------- 1 | mist.log 2 | webdriver/ 3 | chrome.log 4 | 5 | -------------------------------------------------------------------------------- /tests/fixtures/fixture-popup.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Fixture Popup 4 | 5 | 6 |

Fixture Popup

7 | Target blank 8 | Target popup 9 | 10 | 11 | -------------------------------------------------------------------------------- /tests/fixtures/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Fixture title 4 | 5 | 6 |

Index page

7 |

This is a fixture page

8 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /tests/fixtures/js-redirect.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | JS redirect fixture 4 | 5 | 6 |

JS redirect fixture

7 | 8 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /tests/fixtures/meta-redirect.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Meta redirect fixture 4 | 5 | 6 | 7 |

Meta redirect fixture

8 | 9 | 10 | -------------------------------------------------------------------------------- /tests/fixtures/page-01.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Fixture 01 4 | 5 | 6 |

Fixture 01

7 | 8 | 9 | -------------------------------------------------------------------------------- /tests/mocha-in-browser/.eslintrc.yml: -------------------------------------------------------------------------------- 1 | rules: 2 | no-undef: 0 3 | no-var: 0 4 | prefer-arrow-callback: 0 5 | func-names: 0 6 | prefer-template: 0 7 | no-unused-expressions: 0 8 | no-underscore-dangle: 9 | - error 10 | - allow: ['_id', '_escape'] 11 | 12 | globals: 13 | mist: true 14 | expect: true 15 | -------------------------------------------------------------------------------- /tests/mocha-in-browser/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | -------------------------------------------------------------------------------- /tests/mocha-in-browser/Makefile: -------------------------------------------------------------------------------- 1 | update : 2 | npm install 3 | cp node_modules/mocha/mocha.js spec/lib/ 4 | curl https://raw.github.com/visionmedia/mocha/master/mocha.css > spec/lib/browser/mocha.css 5 | 6 | zipit : update 7 | rm -f mocha-in-browser.zip 8 | rm -fr node_modules 9 | zip -r mocha-in-browser.zip public/ spec/ 10 | 11 | -------------------------------------------------------------------------------- /tests/mocha-in-browser/README.md: -------------------------------------------------------------------------------- 1 | # Mocha In the Browser 2 | 3 | A super simple example of how [Mocha](http://visionmedia.github.com/mocha/) tests 4 | can be executed in the browser. A great way to start is cloning this project and 5 | working through the [String Calculator Kata](http://osherove.com/tdd-kata-1/). 6 | Open up the public/index.html file in a browser to see the JavaScript object 7 | used by the page. 8 | 9 | ### Integrating it into your project 10 | 11 | Copy the spec directory into your project. Make sure the runner.html file has all 12 | the right paths. 13 | -------------------------------------------------------------------------------- /tests/mocha-in-browser/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Mocha_in_the_browser", 3 | "version": "0.0.1", 4 | "engines": { 5 | "node": "0.6.6" 6 | }, 7 | "dependencies":{ 8 | "mocha": "*" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /tests/mocha-in-browser/runner.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Tests 4 | 5 | 6 | 7 | 8 | 9 | 10 | 15 | 16 | 17 | 18 | 27 | 28 | 29 |
30 | 31 | 32 | -------------------------------------------------------------------------------- /tests/mocha-in-browser/spec/ipc-spec.js: -------------------------------------------------------------------------------- 1 | // implement chai's should interface 2 | var expect = chai.expect; 3 | var idCount = 500; 4 | 5 | describe('IPC connection', function() { 6 | describe('ipcProvider', function() { 7 | it("shouldn't allow admin functionality [sync]", function() { 8 | var id = idCount++; 9 | 10 | ipcProvider.connect(); 11 | 12 | var data = ipcProvider.writeSync( 13 | JSON.stringify({ 14 | jsonrpc: '2.0', 15 | id: id, 16 | method: 'admin_nodeInfo', 17 | params: [] 18 | }) 19 | ); 20 | 21 | data = data.toString(); 22 | data = JSON.parse(data); 23 | 24 | expect(data.id).to.be.equal(id); 25 | expect(data.error).to.be.defined; 26 | expect(data.error.code).to.be.equal(-32601); 27 | }); 28 | 29 | it("shouldn't allow admin functionality [sync, batch request]", function() { 30 | var id = idCount++; 31 | 32 | ipcProvider.connect(); 33 | 34 | var data = ipcProvider.writeSync( 35 | JSON.stringify([ 36 | { 37 | jsonrpc: '2.0', 38 | id: id, 39 | method: 'admin_nodeInfo', 40 | params: [] 41 | }, 42 | { 43 | jsonrpc: '2.0', 44 | id: id + 1, 45 | method: 'eth_accounts', 46 | params: [] 47 | } 48 | ]) 49 | ); 50 | 51 | data = data.toString(); 52 | data = JSON.parse(data); 53 | 54 | expect(data[0].id).to.be.equal(id); 55 | expect(data[0].error).to.be.defined; 56 | expect(data[0].error.code).to.be.equal(-32601); 57 | 58 | expect(data[1].id).to.be.equal(id + 1); 59 | expect(data[1].result).to.be.defined; 60 | }); 61 | 62 | it("shouldn't allow admin functionality [async]", function(done) { 63 | var id = idCount++; 64 | 65 | ipcProvider.connect(); 66 | 67 | ipcProvider.write( 68 | JSON.stringify({ 69 | jsonrpc: '2.0', 70 | id: id, 71 | method: 'admin_nodeInfo', 72 | params: [] 73 | }) 74 | ); 75 | 76 | ipcProvider.on('data', function(data) { 77 | data = data.toString(); 78 | data = JSON.parse(data); 79 | 80 | if (data.id === id) { 81 | expect(data.error).to.be.defined; 82 | expect(data.error.code).to.be.equal(-32601); 83 | 84 | done(); 85 | } 86 | }); 87 | }); 88 | 89 | it("shouldn't allow admin functionality [async, batch request]", function(done) { 90 | var id = idCount++; 91 | 92 | ipcProvider.connect(); 93 | 94 | ipcProvider.write( 95 | JSON.stringify([ 96 | { 97 | jsonrpc: '2.0', 98 | id: id, 99 | method: 'admin_nodeInfo', 100 | params: [] 101 | }, 102 | { 103 | jsonrpc: '2.0', 104 | id: id + 1, 105 | method: 'eth_accounts', 106 | params: [] 107 | } 108 | ]) 109 | ); 110 | 111 | ipcProvider.on('data', function(data) { 112 | data = data.toString(); 113 | data = JSON.parse(data); 114 | 115 | if (data[0] && data[0].id === id) { 116 | expect(data[0].id).to.be.equal(id); 117 | expect(data[0].error).to.be.defined; 118 | expect(data[0].error.code).to.be.equal(-32601); 119 | 120 | expect(data[1].id).to.be.equal(id + 1); 121 | expect(data[1].result).to.be.defined; 122 | 123 | done(); 124 | } 125 | }); 126 | }); 127 | }); 128 | }); 129 | -------------------------------------------------------------------------------- /tests/unit/core/nodes/actions.test.js: -------------------------------------------------------------------------------- 1 | import { assert } from 'chai'; 2 | import configureMockStore from 'redux-mock-store'; 3 | import thunk from 'redux-thunk'; 4 | import { initialState } from '../../../../modules/core/nodes/reducer'; 5 | import { 6 | changeNetwork, 7 | changeSyncMode, 8 | remoteBlockReceived, 9 | resetLocalNode, 10 | resetRemoteNode 11 | } from '../../../../modules/core/nodes/actions'; 12 | 13 | describe('nodes actions:', () => { 14 | describe('synchronous action creators', () => { 15 | it('should handle #changeSyncMode', () => { 16 | const action = { 17 | type: '[MAIN]:NODES:CHANGE_SYNC_MODE', 18 | payload: { syncMode: 'light' } 19 | }; 20 | 21 | assert.deepEqual(changeSyncMode('light'), action); 22 | }); 23 | 24 | it('should handle #resetLocalNode', () => { 25 | const action = { type: '[MAIN]:LOCAL_NODE:RESET' }; 26 | 27 | assert.deepEqual(resetLocalNode(), action); 28 | }); 29 | 30 | it('should handle #resetRemoteNode', () => { 31 | const action = { type: '[MAIN]:REMOTE_NODE:RESET' }; 32 | 33 | assert.deepEqual(resetRemoteNode(), action); 34 | }); 35 | 36 | it('should handle #remoteBlockReceived', () => { 37 | const block = { number: '0x21c723', timestamp: '0x5ae9d9b8' }; 38 | const action = { 39 | type: '[MAIN]:REMOTE_NODE:BLOCK_HEADER_RECEIVED', 40 | payload: { blockNumber: 2213667, timestamp: 1525275064 } 41 | }; 42 | 43 | assert.deepEqual(remoteBlockReceived(block), action); 44 | }); 45 | }); 46 | 47 | describe('asynchronous action creators', () => { 48 | const middlewares = [thunk]; 49 | const initMockStore = configureMockStore(middlewares); 50 | const store = initMockStore({ settings: initialState }); 51 | 52 | afterEach(() => store.clearActions()); 53 | 54 | it('should handle #changeNetwork', async () => { 55 | await store.dispatch(changeNetwork('rinkeby')); 56 | const actions = store.getActions(); 57 | 58 | assert.equal(actions.length, 2); 59 | assert.equal(actions[0].type, '[MAIN]:NODES:CHANGE_NETWORK_START'); 60 | }); 61 | }); 62 | }); 63 | -------------------------------------------------------------------------------- /tests/unit/core/settings/actions.test.js: -------------------------------------------------------------------------------- 1 | import { assert } from 'chai'; 2 | import configureMockStore from 'redux-mock-store'; 3 | import thunk from 'redux-thunk'; 4 | import { initialState } from '../../../../modules/core/settings/reducer'; 5 | import { 6 | getLanguage, 7 | resetMenu, 8 | setAcceptLanguageHeader, 9 | setLanguage, 10 | setLanguageOnClient, 11 | setLanguageOnMain, 12 | syncBuildConfig, 13 | syncFlags 14 | } from '../../../../modules/core/settings/actions'; 15 | 16 | describe('settings actions:', () => { 17 | describe('synchronous action creators', () => { 18 | it('should handle #syncFlags', () => { 19 | const argv = { mode: 'mist', rpcMode: 'ipc', productionMode: true }; 20 | const action = { 21 | type: '[MAIN]:CLI_FLAGS:SYNC', 22 | payload: { cliFlags: argv } 23 | }; 24 | 25 | assert.deepEqual(syncFlags(argv), action); 26 | }); 27 | 28 | it('should handle #syncBuildConfig', () => { 29 | const appVersion = '1.0.0'; 30 | const action = { 31 | type: '[MAIN]:BUILD_CONFIG:SYNC', 32 | payload: { appVersion } 33 | }; 34 | 35 | assert.deepEqual(syncBuildConfig('appVersion', appVersion), action); 36 | }); 37 | }); 38 | 39 | describe('asynchronous action creators', () => { 40 | const middlewares = [thunk]; 41 | const initMockStore = configureMockStore(middlewares); 42 | const store = initMockStore({ settings: initialState }); 43 | 44 | afterEach(() => store.clearActions()); 45 | 46 | it('should handle #setLanguage', async () => { 47 | await store.dispatch(setLanguage('en', {})); 48 | const actions = store.getActions(); 49 | 50 | assert.equal(actions.length, 10); 51 | assert.equal(actions[0].type, '[MAIN]:SET_LANGUAGE:START'); 52 | assert.equal(actions.slice(-1)[0].type, '[MAIN]:SET_LANGUAGE:FINISH'); 53 | }); 54 | 55 | it('should handle #setLanguageOnMain', async () => { 56 | await store.dispatch(setLanguageOnMain('en')); 57 | const actions = store.getActions(); 58 | 59 | assert.equal(actions.length, 2); 60 | assert.equal(actions[0].type, '[MAIN]:SET_LANGUAGE_ON_MAIN:START'); 61 | }); 62 | 63 | it('should handle #setLanguageOnClient', async () => { 64 | await store.dispatch(setLanguageOnClient('en', {})); 65 | const actions = store.getActions(); 66 | 67 | assert.equal(actions.length, 2); 68 | assert.equal(actions[0].type, '[MAIN]:SET_LANGUAGE_ON_CLIENT:START'); 69 | }); 70 | 71 | it('should handle #setAcceptLanguageHeader', async () => { 72 | await store.dispatch(setAcceptLanguageHeader('en', {})); 73 | const actions = store.getActions(); 74 | 75 | assert.equal(actions.length, 2); 76 | assert.equal(actions[0].type, '[MAIN]:SET_ACCEPT_LANGUAGE_HEADER:START'); 77 | }); 78 | 79 | it('should handle #resetMenu', async () => { 80 | await store.dispatch(resetMenu('en')); 81 | const actions = store.getActions(); 82 | 83 | assert.equal(actions.length, 2); 84 | assert.equal(actions[0].type, '[MAIN]:RESET_MENU:START'); 85 | }); 86 | 87 | it('should handle #getLanguage', async () => { 88 | await store.dispatch(getLanguage({})); 89 | const actions = store.getActions(); 90 | 91 | assert.equal(actions.length, 2); 92 | assert.equal(actions[0].type, '[MAIN]:GET_LANGUAGE:START'); 93 | }); 94 | }); 95 | }); 96 | -------------------------------------------------------------------------------- /tests/unit/core/settings/reducer.test.js: -------------------------------------------------------------------------------- 1 | import { assert } from 'chai'; 2 | import reducer, { 3 | initialState 4 | } from '../../../../modules/core/settings/reducer'; 5 | 6 | describe('the settings reducer', () => { 7 | it('should return a default initial state', () => { 8 | assert.deepEqual(reducer(undefined, {}), initialState); 9 | }); 10 | 11 | it('should handle the "[MAIN]:BUILD_CONFIG:SYNC" action', () => { 12 | const action = { 13 | type: '[MAIN]:BUILD_CONFIG:SYNC', 14 | payload: { appVersion: '1.0.0' } 15 | }; 16 | const expectedState = Object.assign({}, initialState, { 17 | appVersion: '1.0.0' 18 | }); 19 | 20 | assert.deepEqual(reducer(initialState, action), expectedState); 21 | }); 22 | 23 | it('should handle the "[MAIN]:IGNORE_GPU_BLACKLIST:SET" action', () => { 24 | const action = { type: '[MAIN]:IGNORE_GPU_BLACKLIST:SET' }; 25 | const expectedState = Object.assign({}, initialState, { 26 | ignoreGpuBlacklist: true 27 | }); 28 | 29 | assert.deepEqual(reducer(initialState, action), expectedState); 30 | }); 31 | 32 | it('should handle the "[MAIN]:CLI_FLAGS:SYNC" action', () => { 33 | const action = { 34 | type: '[MAIN]:CLI_FLAGS:SYNC', 35 | payload: { 36 | cliFlags: { 37 | mode: 'mist', 38 | syncmode: 'light', 39 | swarmurl: 'http://localhost:8585' 40 | } 41 | } 42 | }; 43 | const expectedState = Object.assign({}, initialState, { 44 | cliFlags: { 45 | mode: 'mist', 46 | syncmode: 'light', 47 | swarmurl: 'http://localhost:8585' 48 | } 49 | }); 50 | 51 | assert.deepEqual(reducer(initialState, action), expectedState); 52 | }); 53 | 54 | it('should handle the "[MAIN]:SET_LANGUAGE_ON_MAIN:SUCCESS" action', () => { 55 | const action = { 56 | type: '[MAIN]:SET_LANGUAGE_ON_MAIN:SUCCESS', 57 | payload: { i18n: 'de' } 58 | }; 59 | const expectedState = Object.assign({}, initialState, { 60 | i18n: 'de' 61 | }); 62 | 63 | assert.deepEqual(reducer(initialState, action), expectedState); 64 | }); 65 | 66 | it('should handle the "[CLIENT:GET_PRICE_CONVERSION:START]" action', () => { 67 | const action = { type: '[CLIENT]:GET_PRICE_CONVERSION:START' }; 68 | const oldState = Object.assign({}, initialState, { 69 | etherPriceUSD: 500 70 | }); 71 | const expectedState = Object.assign({}, initialState, { 72 | etherPriceUSD: 0 73 | }); 74 | assert.deepEqual(reducer(oldState, action), expectedState); 75 | }); 76 | 77 | it('should handle the "[CLIENT:GET_PRICE_CONVERSION:SUCCESS]" action', () => { 78 | const action = { 79 | type: '[CLIENT]:GET_PRICE_CONVERSION:SUCCESS', 80 | payload: { etherPriceUSD: 500 } 81 | }; 82 | const oldState = Object.assign({}, initialState, { 83 | etherPriceUSD: 0 84 | }); 85 | const expectedState = Object.assign({}, initialState, { 86 | etherPriceUSD: 500 87 | }); 88 | assert.deepEqual(reducer(oldState, action), expectedState); 89 | }); 90 | 91 | it('should handle the "[CLIENT:GET_PRICE_CONVERSION:FAILURE]" action', () => { 92 | const action = { type: '[CLIENT]:GET_PRICE_CONVERSION:FAILURE' }; 93 | const oldState = Object.assign({}, initialState, { 94 | etherPriceUSD: 500 95 | }); 96 | const expectedState = Object.assign({}, initialState, { 97 | etherPriceUSD: 0 98 | }); 99 | assert.deepEqual(reducer(oldState, action), expectedState); 100 | }); 101 | }); 102 | -------------------------------------------------------------------------------- /tests/unit/core/ui/actions.test.js: -------------------------------------------------------------------------------- 1 | import { assert } from 'chai'; 2 | import configureMockStore from 'redux-mock-store'; 3 | import thunk from 'redux-thunk'; 4 | import { initialState } from '../../../../modules/core/ui/reducer'; 5 | import { quitApp } from '../../../../modules/core/ui/actions'; 6 | 7 | describe('ui actions:', () => { 8 | describe('asynchronous action creators', () => { 9 | const middlewares = [thunk]; 10 | const initMockStore = configureMockStore(middlewares); 11 | const store = initMockStore({ ui: initialState }); 12 | 13 | afterEach(() => store.clearActions()); 14 | 15 | it('should handle #quitApp', async () => { 16 | await store.dispatch(quitApp()); 17 | const actions = store.getActions(); 18 | 19 | assert.equal(actions.length, 2); 20 | assert.equal(actions[0].type, '[MAIN]:APP_QUIT:START'); 21 | }); 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /tests/unit/core/ui/reducer.test.js: -------------------------------------------------------------------------------- 1 | import { assert } from 'chai'; 2 | import reducer, { initialState } from '../../../../modules/core/ui/reducer'; 3 | 4 | describe('the ui reducer', () => { 5 | it('should return a default initial state', () => { 6 | assert.deepEqual(reducer(undefined, {}), initialState); 7 | }); 8 | 9 | it('should handle the "[MAIN]:APP_QUIT:SUCCESS" action', () => { 10 | const action = { type: '[MAIN]:APP_QUIT:SUCCESS' }; 11 | const expectedState = Object.assign({}, initialState, { 12 | appQuit: true 13 | }); 14 | 15 | assert.deepEqual(reducer(initialState, action), expectedState); 16 | }); 17 | 18 | it('should handle the "[MAIN]:WINDOW:OPEN" action', () => { 19 | const state = Object.assign({}, initialState, { 20 | windowsOpen: ['about'] 21 | }); 22 | const action = { 23 | type: '[MAIN]:WINDOW:OPEN', 24 | payload: { windowType: 'importAccount' } 25 | }; 26 | const expectedState = Object.assign({}, state, { 27 | windowsOpen: ['about', 'importAccount'] 28 | }); 29 | 30 | assert.deepEqual(reducer(state, action), expectedState); 31 | }); 32 | 33 | it('should handle the "[MAIN]:WINDOW:CLOSE" action', () => { 34 | const state = Object.assign({}, initialState, { 35 | windowsOpen: ['about', 'importAccount'] 36 | }); 37 | const action = { 38 | type: '[MAIN]:WINDOW:CLOSE', 39 | payload: { windowType: 'importAccount' } 40 | }; 41 | const expectedState = Object.assign({}, state, { 42 | windowsOpen: ['about'] 43 | }); 44 | 45 | assert.deepEqual(reducer(state, action), expectedState); 46 | }); 47 | 48 | it('should handle the "[MAIN]:GENERIC_WINDOW:REUSE" action', () => { 49 | const action = { 50 | type: '[MAIN]:GENERIC_WINDOW:REUSE', 51 | payload: { actingType: 'about' } 52 | }; 53 | const expectedState = Object.assign({}, initialState, { 54 | genericWindowActingType: 'about', 55 | windowsOpen: ['generic'] 56 | }); 57 | 58 | assert.deepEqual(reducer(initialState, action), expectedState); 59 | }); 60 | 61 | it('should handle the "[MAIN]:GENERIC_WINDOW:RESET" action', () => { 62 | const state = Object.assign({}, initialState, { 63 | genericWindowActingType: 'about', 64 | windowsOpen: ['generic', 'main'] 65 | }); 66 | const action = { type: '[MAIN]:GENERIC_WINDOW:RESET' }; 67 | const expectedState = Object.assign({}, initialState, { 68 | windowsOpen: ['main'] 69 | }); 70 | 71 | assert.deepEqual(reducer(state, action), expectedState); 72 | }); 73 | }); 74 | -------------------------------------------------------------------------------- /tests/unit/utils/formatters.test.js: -------------------------------------------------------------------------------- 1 | import { assert } from 'chai'; 2 | import { 3 | formatTokenCount, 4 | formatFunctionName 5 | } from '../../../interface/utils/formatters'; 6 | 7 | describe('the formatter util', () => { 8 | context('formatTokenCount', () => { 9 | it('returns a string', () => { 10 | assert.equal(formatTokenCount(1000000000000000000, 18), '1'); 11 | }); 12 | 13 | it('handles fractions of tokens', () => { 14 | assert.equal(formatTokenCount(1000000000000000, 18), '0.001'); 15 | }); 16 | 17 | it('handles empty strings', () => { 18 | assert.equal(formatTokenCount('', 18), '0'); 19 | }); 20 | 21 | it('handles negatives', () => { 22 | assert.equal(formatTokenCount(-1500000000000000000, 18), '-1.5'); 23 | }); 24 | }); 25 | 26 | context('formatFunctionName', () => { 27 | const fn = n => formatFunctionName(n); 28 | 29 | it('should handle empty param', () => { 30 | assert.equal(fn(''), ''); 31 | }); 32 | 33 | it('should handle a basic scenario', () => { 34 | assert.equal(fn('approve()'), 'approve'); 35 | }); 36 | 37 | it('should separate words', () => { 38 | assert.equal(fn('transferFrom()'), 'transfer from'); 39 | }); 40 | 41 | it('should separate numbers', () => { 42 | assert.equal(fn('splitTo2()'), 'split to 2'); 43 | }); 44 | 45 | it('should mix letters and numbers', () => { 46 | assert.equal(fn('transfer20Shares()'), 'transfer 20 shares'); 47 | }); 48 | 49 | it('should split function names with underscore', () => { 50 | assert.equal(fn('send_payload()'), 'send payload'); 51 | }); 52 | 53 | it('should split function names with multiple underscores', () => { 54 | assert.equal( 55 | fn('send__payload__to_blockchain()'), 56 | 'send payload to blockchain' 57 | ); 58 | }); 59 | 60 | it('should handle acronyms', () => { 61 | assert.equal(fn('setURL()'), 'set url'); 62 | }); 63 | 64 | it('all caps function names should return as a single word', () => { 65 | assert.equal(fn('IDONOTFOLLOWRULES()'), 'idonotfollowrules'); 66 | }); 67 | 68 | it('should fire meaninful error when no parameters are passed', () => { 69 | assert.throws(fn, 'formatFunctionName() expects a non-empty string'); 70 | }); 71 | }); 72 | }); 73 | -------------------------------------------------------------------------------- /tests/wallet/basic.test.js: -------------------------------------------------------------------------------- 1 | const _ = require('underscore'); 2 | const Q = require('bluebird'); 3 | const fs = require('fs'); 4 | const path = require('path'); 5 | 6 | const test = require('../_base').mocha(module, { 7 | app: 'wallet' 8 | }); 9 | 10 | test['Title test'] = function*() { 11 | const client = this.client; 12 | 13 | yield client.waitUntilWindowLoaded(); 14 | (yield client.getTitle()).should.eql('Ethereum Wallet'); 15 | }; 16 | 17 | test['account balances'] = function*() { 18 | const web3 = this.web3; 19 | const client = this.client; 20 | 21 | const realBalances = this.getRealAccountBalances(); 22 | const appBalances = this.getUiAccountBalances(); 23 | 24 | realBalances.should.not.be.null; 25 | realBalances.should.eql('5'); 26 | appBalances.should.eql(realBalances); 27 | }; 28 | 29 | // test['create account'] = function*() { 30 | // const web3 = this.web3; 31 | // const client = this.client; 32 | 33 | // const originalBalances = yield this.getRealAccountBalances(); 34 | 35 | // yield _createNewAccount.call(this); 36 | 37 | // const realBalances = yield this.getRealAccountBalances(); 38 | // const appBalances = yield this.getUiAccountBalances(); 39 | 40 | // _.keys(realBalances).length.should.eql(_.keys(originalBalances).length + 1); 41 | // appBalances.should.eql(realBalances); 42 | // }; 43 | 44 | test['deposit into account'] = function*() { 45 | const web3 = this.web3; 46 | const client = this.client; 47 | 48 | const accounts = web3.eth.accounts; 49 | 50 | yield _createNewAccount.call(this); 51 | 52 | const newAccount = _.difference(web3.eth.accounts, accounts)[0]; 53 | 54 | yield this.openAccountInUi(newAccount); 55 | 56 | // links 57 | const accLinks = yield this.getUiElements('.dapp-actionbar li'); 58 | yield client.elementIdClick(accLinks[0].ELEMENT); 59 | 60 | // fill in send form and submit 61 | yield _completeSendForm.call(this, 1); 62 | 63 | // do some mining 64 | yield this.startMining(); 65 | yield Q.delay(10000); 66 | yield this.stopMining(); 67 | 68 | // check balances 69 | const realBalances = yield this.getRealAccountBalances(); 70 | 71 | realBalances[newAccount].should.eql(1); 72 | }; 73 | 74 | const _createNewAccount = function*() { 75 | const client = this.client; 76 | 77 | // open password window 78 | yield this.openAndFocusNewWindow(() => { 79 | return client.click('button.create.account'); 80 | }); 81 | 82 | // enter password 83 | yield client.setValue('form .password', '1234'); 84 | yield client.click('form button.ok'); 85 | 86 | // re-enter password 87 | yield client.setValue('form .password-repeat', '1234'); 88 | yield client.click('form button.ok'); 89 | 90 | yield Q.delay(10000); 91 | 92 | yield client.window(this.mainWindowHandle); 93 | }; 94 | 95 | const _completeSendForm = function*(amt) { 96 | const client = this.client; 97 | 98 | // enter password 99 | yield client.setValue('form input[name=amount]', `${amt}`); 100 | 101 | // open password window 102 | yield this.openAndFocusNewWindow(() => { 103 | return client.click('form button[type=submit]'); 104 | }); 105 | 106 | // fill in password and submit 107 | yield client.setValue('form input[type=password]', '1234'); 108 | yield client.click('form button.ok'); 109 | 110 | yield Q.delay(5000); 111 | }; 112 | --------------------------------------------------------------------------------