├── .circleci └── config.yml ├── .gitignore ├── .prettierignore ├── .prettierrc ├── .pullapprove.yml ├── .travis.yml ├── 404.html ├── AUTHORS ├── LICENSE ├── README.md ├── Wallet.sol ├── app ├── .meteor │ ├── .finished-upgraders │ ├── .gitignore │ ├── .id │ ├── cordova-plugins │ ├── packages │ ├── platforms │ ├── release │ └── versions ├── client │ ├── index.html │ ├── index.js │ ├── lib │ │ ├── appStart.js │ │ ├── collections.js │ │ ├── ethereum │ │ │ ├── 1_web3Init.js │ │ │ ├── observeBlocks.js │ │ │ ├── observeCustomContracts.js │ │ │ ├── observeEvents.js │ │ │ ├── observePendingConfirmations.js │ │ │ ├── observeTokens.js │ │ │ ├── observeTransactions.js │ │ │ ├── observeWallets.js │ │ │ ├── tokenInterface.js │ │ │ ├── walletConnector.js │ │ │ └── walletInterface.js │ │ ├── helpers │ │ │ ├── helperFunctions.js │ │ │ └── templateHelpers.js │ │ └── thirdparty │ │ │ ├── ace.js │ │ │ ├── chance.min.js │ │ │ ├── geopattern.min.js │ │ │ ├── mode-java.js │ │ │ ├── mode-typescript.js │ │ │ ├── theme-solarized_dark.js │ │ │ ├── theme-solarized_light.js │ │ │ ├── theme-tomorrow.js │ │ │ └── theme-tomorrow_night.js │ ├── mist.js │ ├── routes.js │ ├── styles │ │ ├── elements.import.less │ │ └── main.less │ ├── templates │ │ ├── elements │ │ │ ├── account.html │ │ │ ├── account.js │ │ │ ├── accountLink.html │ │ │ ├── accountLink.js │ │ │ ├── balance.html │ │ │ ├── balance.js │ │ │ ├── compileContract.html │ │ │ ├── compileContract.js │ │ │ ├── createdContractAt.html │ │ │ ├── eventsTable.html │ │ │ ├── eventsTable.js │ │ │ ├── executeContract.html │ │ │ ├── executeContract.js │ │ │ ├── inputs │ │ │ │ ├── address.html │ │ │ │ ├── address.js │ │ │ │ ├── bool.html │ │ │ │ ├── bytes.html │ │ │ │ ├── int.html │ │ │ │ ├── json.html │ │ │ │ ├── string.html │ │ │ │ └── uint.html │ │ │ ├── newAccountButton.html │ │ │ ├── selectableUnit.html │ │ │ ├── selectableUnit.js │ │ │ ├── tokenBox.html │ │ │ ├── tokenBox.js │ │ │ ├── transactionTable.html │ │ │ ├── transactionTable.js │ │ │ ├── vulnerabilities.html │ │ │ └── vulnerabilities.js │ │ ├── layout │ │ │ ├── header.html │ │ │ ├── header.js │ │ │ ├── main.html │ │ │ └── notFound.html │ │ └── views │ │ │ ├── account.html │ │ │ ├── account.js │ │ │ ├── account_create.html │ │ │ ├── account_create.js │ │ │ ├── contracts.html │ │ │ ├── contracts.js │ │ │ ├── dashboard.html │ │ │ ├── dashboard.js │ │ │ ├── modals │ │ │ ├── addCustomContract.html │ │ │ ├── addCustomContract.js │ │ │ ├── addToken.html │ │ │ ├── addToken.js │ │ │ ├── backupContractAddress.html │ │ │ ├── eventInfo.html │ │ │ ├── eventInfo.js │ │ │ ├── interface.html │ │ │ ├── interface.js │ │ │ ├── loading.html │ │ │ ├── qrCode.html │ │ │ ├── qrCode.js │ │ │ ├── selectAccount.html │ │ │ ├── selectAccount.js │ │ │ ├── sendTransactionInfo.html │ │ │ ├── sendTransactionInfo.js │ │ │ ├── transactionInfo.html │ │ │ └── transactionInfo.js │ │ │ ├── send.html │ │ │ └── send.js │ └── window.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.ja.i18n.json │ ├── app.ko.i18n.json │ ├── app.pt.i18n.json │ ├── app.ro.i18n.json │ ├── app.ru.i18n.json │ ├── app.sq.i18n.json │ ├── app.ua.i18n.json │ ├── app.zh-TW.i18n.json │ ├── app.zh.i18n.json │ ├── wallet.ca.i18n.json │ ├── wallet.de.i18n.json │ ├── wallet.en.i18n.json │ ├── wallet.es.i18n.json │ ├── wallet.fr.i18n.json │ ├── wallet.ja.i18n.json │ ├── wallet.ko.i18n.json │ ├── wallet.pt.i18n.json │ ├── wallet.ro.i18n.json │ ├── wallet.ru.i18n.json │ ├── wallet.sq.i18n.json │ ├── wallet.ua.i18n.json │ ├── wallet.zh-TW.i18n.json │ └── wallet.zh.i18n.json ├── package-lock.json ├── package.json ├── project-tap.i18n ├── public │ ├── ethereum-icon.svg │ ├── fontawesome │ │ ├── animated.less │ │ ├── bordered-pulled.less │ │ ├── core.less │ │ ├── fixed-width.less │ │ ├── font-awesome.less │ │ ├── icons.less │ │ ├── larger.less │ │ ├── list.less │ │ ├── mixins.less │ │ ├── path.less │ │ ├── rotated-flipped.less │ │ ├── stacked.less │ │ └── variables.less │ ├── i18n │ │ ├── de.json │ │ ├── es.json │ │ ├── fa.json │ │ └── ko.json │ ├── loading.css │ ├── sockjs │ │ └── info │ └── wallet-icon.png └── tests │ └── mocha │ ├── client │ └── sampleClientTest.js │ └── server │ └── sampleServerTest.js ├── build ├── 8f16113a04929a5e54ac1c6fbe1e0f272eae43bb.js ├── 8f16113a04929a5e54ac1c6fbe1e0f272eae43bb.stats.json ├── b5c0100a3fba3970004889019e9de74240e11f74.css ├── ethereum-icon.svg ├── fontawesome │ ├── animated.less │ ├── bordered-pulled.less │ ├── core.less │ ├── fixed-width.less │ ├── font-awesome.less │ ├── icons.less │ ├── larger.less │ ├── list.less │ ├── mixins.less │ ├── path.less │ ├── rotated-flipped.less │ ├── stacked.less │ └── variables.less ├── i18n │ ├── ca.json │ ├── de.json │ ├── es.json │ ├── fa.json │ ├── fr.json │ ├── ja.json │ ├── ko.json │ ├── pt.json │ ├── ro.json │ ├── ru.json │ ├── sq.json │ ├── tap-i18n.json │ ├── ua.json │ ├── zh-TW.json │ └── zh.json ├── index.html ├── loading.css ├── packages │ ├── ethereum_dapp-styles │ │ ├── fonts │ │ │ ├── Montserrat-Black.otf │ │ │ ├── Montserrat-Bold.otf │ │ │ ├── Montserrat-Hairline.otf │ │ │ ├── Montserrat-Light.otf │ │ │ ├── Montserrat-Regular.otf │ │ │ ├── SourceSansPro-Black.otf │ │ │ ├── SourceSansPro-Bold.otf │ │ │ ├── SourceSansPro-ExtraLight.otf │ │ │ ├── SourceSansPro-Light.otf │ │ │ ├── SourceSansPro-Regular.otf │ │ │ └── SourceSansPro-Semibold.otf │ │ └── icons │ │ │ ├── Simple-Line-Icons.eot │ │ │ ├── Simple-Line-Icons.svg │ │ │ ├── Simple-Line-Icons.ttf │ │ │ └── Simple-Line-Icons.woff │ └── ethereum_elements │ │ └── identicon-load.gif ├── sockjs │ └── info └── wallet-icon.png ├── images ├── body-bg.png ├── highlight-bg.jpg ├── hr.png ├── octocat-icon.png ├── tar-gz-icon.png └── zip-icon.png ├── index.html ├── javascripts └── main.js ├── params.json └── stylesheets ├── github-dark.css ├── print.css └── stylesheet.css /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | # Javascript Node CircleCI 2.0 configuration file 2 | version: 2 3 | jobs: 4 | build: 5 | docker: 6 | # specify the version you desire here 7 | - image: circleci/node:7.10 8 | 9 | # Specify service dependencies here if necessary 10 | # CircleCI maintains a library of pre-built images 11 | # documented at https://circleci.com/docs/2.0/circleci-images/ 12 | # - image: circleci/mongo:3.4.4 13 | 14 | steps: 15 | - checkout 16 | - run: curl https://install.meteor.com/ | sh 17 | - run: cd app && npm install && npm run build 18 | 19 | 20 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | app/browser 2 | app/packages 3 | app/packages/* 4 | packages/ 5 | app/public/i18n/*.json 6 | app/.meteor/dev_bundle 7 | app/node_modules 8 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | build 2 | app/.meteor/local 3 | app/client/lib/thirdparty -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | singleQuote: true -------------------------------------------------------------------------------- /.pullapprove.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | groups: 3 | code-review: 4 | required: 2 5 | reset_on_push: 6 | enabled: true 7 | users: 8 | - alexvandesande 9 | - evertonfraga 10 | - frozeman 11 | - marcgarreau 12 | - PhilippLgh 13 | - ryanio 14 | 15 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | 3 | node_js: "7" 4 | 5 | sudo: required 6 | 7 | install: 8 | - cd app 9 | - npm install 10 | - curl https://install.meteor.com/ | sh 11 | 12 | script: 13 | - npm run build 14 | 15 | -------------------------------------------------------------------------------- /404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 404 page for github pages 4 | 5 | 34 | 35 | 36 | 37 |
38 |

39 | Click here to redirect to the main wallet page 40 |

41 |
42 | 43 | 44 | 50 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | The Wallet Authors 2 | 3 | Alexander Van de Sande 4 | Fabian Vogelsteller -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Ethereum Wallet Ðapp 2 | 3 | The Ethereum wallet. 4 | 5 | [![Build Status](https://travis-ci.org/ethereum/meteor-dapp-wallet.svg?branch=master)](https://travis-ci.org/ethereum/meteor-dapp-wallet) 6 | 7 | **PLEASE NOTE:** This wallet is not yet officially released, 8 | and can contain severe bugs! Please use at your own risk. 9 | 10 | ## Install 11 | 12 | If you don't have [Meteor](https://www.meteor.com/install): 13 | 14 | $ curl https://install.meteor.com/ | sh 15 | 16 | Install npm dependencies: 17 | 18 | $ cd meteor-dapp-wallet/app 19 | $ npm install 20 | 21 | ## Development 22 | 23 | Start a `geth` node: 24 | 25 | $ geth --ws --wsorigins "http://localhost:3000" --unlock 26 | 27 | Run dev server: 28 | 29 | $ cd meteor-dapp-wallet/app 30 | $ meteor 31 | 32 | Navigate to http://localhost:3000 33 | 34 | ## Deployment 35 | 36 | To create a build: 37 | 38 | $ npm install -g meteor-build-client 39 | $ cd meteor-dapp-wallet/app 40 | $ npm install 41 | $ meteor-build-client ../build --path "" 42 | 43 | This will generate the files in the `../build` folder. 44 | 45 | Navigating to `index.html` will start the app, but you will need to serve it over a local server like [MAMP](https://www.mamp.info). 46 | 47 | --- 48 | 49 | To deploy to the **wallet.ethereum.org** site, execute these commands: 50 | 51 | $ git checkout gh-pages 52 | $ git merge develop 53 | $ cd app 54 | $ meteor-build-client ../build --path "/" 55 | 56 | And push (or PR) your changes to the `gh-pages` branch. 57 | 58 | --- 59 | 60 | ## Gas usage statistics 61 | 62 | - Deploy original wallet: 1 230 162 63 | - Deploy wallet stub: 184 280 64 | - Simple Wallet transaction: 64 280 65 | - Multisig Wallet transaction below daily limit: 79 280 66 | - Multisig Wallet transaction above daily limit: 171 096 67 | - 1 Multisig confirmation: 48 363 68 | -------------------------------------------------------------------------------- /app/.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 | -------------------------------------------------------------------------------- /app/.meteor/.gitignore: -------------------------------------------------------------------------------- 1 | local 2 | dev_bundle -------------------------------------------------------------------------------- /app/.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 | e350zy16p3kznipbx5o 8 | -------------------------------------------------------------------------------- /app/.meteor/cordova-plugins: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethereum/meteor-dapp-wallet/dd68f84f626dd3672cd21a0f72594621dc57e857/app/.meteor/cordova-plugins -------------------------------------------------------------------------------- /app/.meteor/packages: -------------------------------------------------------------------------------- 1 | # Meteor packages used by this project, one per line. 2 | # 3 | # 'meteor add' and 'meteor remove' will edit this file for you, 4 | # but you can also edit it by hand. 5 | 6 | standard-minifiers@1.1.0 7 | meteor-base@1.3.0 8 | mobile-experience@1.0.5 9 | mongo@1.4.2 10 | blaze-html-templates 11 | session@1.1.7 12 | jquery@1.11.10 13 | tracker@1.1.3 14 | logging@1.1.19 15 | reload@1.2.0 16 | random@1.1.0 17 | ejson@1.1.0 18 | spacebars 19 | check@1.3.0 20 | http@1.4.0 21 | 22 | fastclick 23 | kadira:flow-router 24 | kadira:blaze-layout 25 | arillo:flow-router-helpers 26 | less@2.7.11 27 | chuangbo:cookie 28 | jeeeyul:moment-with-langs 29 | tap:i18n 30 | raix:handlebar-helpers 31 | frozeman:animation-helper 32 | frozeman:template-var 33 | frozeman:persistent-minimongo2 34 | frozeman:reactive-timer 35 | frozeman:storage 36 | markdown@1.0.12 37 | frozeman:inline-form 38 | mrt:qrcodesvg 39 | sacha:spin 40 | frozeman:global-notifications 41 | agnito:simptip 42 | ethereum:dapp-styles 43 | jrudio:bluebird 44 | tap:i18n-bundler 45 | numeral:numeral 46 | numeral:languages 47 | hashanp:geopattern 48 | shell-server 49 | dynamic-import 50 | ethereum:blocks@1.1.0 51 | ethereum:accounts@1.1.0 52 | ethereum:tools@1.1.0 53 | ethereum:elements@1.1.0 54 | ethereum:web3@1.0.0-beta.33 55 | 3stack:bignumber -------------------------------------------------------------------------------- /app/.meteor/platforms: -------------------------------------------------------------------------------- 1 | server 2 | browser 3 | -------------------------------------------------------------------------------- /app/.meteor/release: -------------------------------------------------------------------------------- 1 | METEOR@1.6.1 2 | -------------------------------------------------------------------------------- /app/.meteor/versions: -------------------------------------------------------------------------------- 1 | 3stack:bignumber@2.0.0 2 | agnito:simptip@0.0.1 3 | aldeed:simple-schema@1.3.3 4 | alexvandesande:identicon@2.0.2 5 | allow-deny@1.1.0 6 | amplify@1.0.0 7 | arillo:flow-router-helpers@0.5.2 8 | autoupdate@1.4.0 9 | babel-compiler@7.0.4 10 | babel-runtime@1.2.2 11 | base64@1.0.11 12 | binary-heap@1.0.10 13 | blaze@2.3.2 14 | blaze-html-templates@1.1.2 15 | blaze-tools@1.0.10 16 | boilerplate-generator@1.4.0 17 | caching-compiler@1.1.11 18 | caching-html-compiler@1.1.2 19 | callback-hook@1.1.0 20 | cfs:http-methods@0.0.32 21 | check@1.3.0 22 | chuangbo:cookie@1.1.0 23 | coffeescript@1.0.17 24 | ddp@1.4.0 25 | ddp-client@2.3.1 26 | ddp-common@1.4.0 27 | ddp-server@2.1.2 28 | deps@1.0.12 29 | diff-sequence@1.1.0 30 | dynamic-import@0.3.0 31 | ecmascript@0.10.4 32 | ecmascript-runtime@0.5.0 33 | ecmascript-runtime-client@0.6.2 34 | ecmascript-runtime-server@0.5.0 35 | ejson@1.1.0 36 | es5-shim@4.7.3 37 | ethereum:accounts@1.1.0 38 | ethereum:blocks@1.1.0 39 | ethereum:dapp-styles@0.5.8 40 | ethereum:elements@1.2.0 41 | ethereum:tools@1.1.0 42 | ethereum:web3@1.0.0-beta.33 43 | fastclick@1.0.13 44 | frozeman:animation-helper@0.2.6 45 | frozeman:global-notifications@0.2.1 46 | frozeman:inline-form@0.1.0 47 | frozeman:persistent-minimongo@0.1.8 48 | frozeman:persistent-minimongo2@0.3.5 49 | frozeman:reactive-timer@0.1.7 50 | frozeman:simple-modal@0.0.7 51 | frozeman:storage@0.1.9 52 | frozeman:template-var@1.3.0 53 | geojson-utils@1.0.10 54 | hashanp:geopattern@0.0.1 55 | hot-code-push@1.0.4 56 | html-tools@1.0.11 57 | htmljs@1.0.11 58 | http@1.4.0 59 | id-map@1.1.0 60 | jeeeyul:moment-with-langs@2.12.1 61 | jquery@1.11.11 62 | jrudio:bluebird@3.3.1_1 63 | kadira:blaze-layout@2.3.0 64 | kadira:flow-router@2.12.1 65 | launch-screen@1.1.1 66 | less@2.7.12 67 | livedata@1.0.18 68 | localstorage@1.2.0 69 | logging@1.1.19 70 | markdown@1.0.12 71 | meteor@1.8.2 72 | meteor-base@1.3.0 73 | meteorspark:util@0.2.0 74 | minifier-css@1.3.1 75 | minifier-js@2.3.2 76 | minimongo@1.4.3 77 | mobile-experience@1.0.5 78 | mobile-status-bar@1.0.14 79 | modules@0.11.4 80 | modules-runtime@0.9.2 81 | mongo@1.4.3 82 | mongo-dev-server@1.1.0 83 | mongo-id@1.0.6 84 | mrt:qrcodesvg@0.1.0 85 | npm-mongo@2.2.34 86 | numeral:languages@1.5.3 87 | numeral:numeral@1.5.3_1 88 | observe-sequence@1.0.16 89 | ordered-dict@1.1.0 90 | promise@0.10.2 91 | raix:eventemitter@0.1.3 92 | raix:handlebar-helpers@0.2.5 93 | random@1.1.0 94 | reactive-dict@1.2.0 95 | reactive-var@1.0.11 96 | reload@1.2.0 97 | retry@1.1.0 98 | routepolicy@1.0.12 99 | sacha:spin@2.3.1 100 | server-render@0.3.0 101 | session@1.1.7 102 | shell-server@0.3.1 103 | shim-common@0.1.0 104 | socket-stream-client@0.1.0 105 | spacebars@1.0.15 106 | spacebars-compiler@1.1.3 107 | standard-minifier-css@1.4.1 108 | standard-minifier-js@2.3.2 109 | standard-minifiers@1.1.0 110 | tap:i18n@1.8.2 111 | tap:i18n-bundler@0.3.0 112 | templating@1.3.2 113 | templating-compiler@1.3.3 114 | templating-runtime@1.3.2 115 | templating-tools@1.1.2 116 | tracker@1.1.3 117 | ui@1.0.13 118 | underscore@1.0.10 119 | url@1.2.0 120 | webapp@1.5.0 121 | webapp-hashing@1.0.9 122 | zimme:active-route@2.3.2 123 | -------------------------------------------------------------------------------- /app/client/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Ethereum Wallet 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /app/client/index.js: -------------------------------------------------------------------------------- 1 | Meteor.startup(function() { 2 | // SET default language 3 | if (Cookie.get('TAPi18next')) { 4 | TAPi18n.setLanguage(Cookie.get('TAPi18next')); 5 | } else { 6 | var userLang = navigator.language || navigator.userLanguage, 7 | availLang = TAPi18n.getLanguages(); 8 | 9 | // set default language 10 | if (_.isObject(availLang) && availLang[userLang]) { 11 | TAPi18n.setLanguage(userLang); 12 | } else if (_.isObject(availLang) && availLang[userLang.substr(0, 2)]) { 13 | TAPi18n.setLanguage(userLang.substr(0, 2)); 14 | } else { 15 | TAPi18n.setLanguage('en'); 16 | } 17 | } 18 | // change moment and numeral language, when language changes 19 | Tracker.autorun(function() { 20 | if (_.isString(TAPi18n.getLanguage())) { 21 | var lang = TAPi18n.getLanguage().substr(0, 2); 22 | moment.locale(lang); 23 | try { 24 | numeral.language(lang); 25 | } catch (err) { 26 | console.warn("numeral.js couldn't set number formating: ", err.message); 27 | } 28 | EthTools.setLocale(lang); 29 | } 30 | }); 31 | }); 32 | -------------------------------------------------------------------------------- /app/client/lib/appStart.js: -------------------------------------------------------------------------------- 1 | // disconnect any meteor server 2 | if (location.hostname !== 'localhost' && location.hostname !== '127.0.0.1') 3 | Meteor.disconnect(); 4 | 5 | // Make sure the example contract code is up to date 6 | var contractSource = localStorage.getItem('contractSource'); 7 | 8 | if ( 9 | contractSource && // repopulate placeholder contract if: 10 | (contractSource === '' || // source is empty or 11 | (contractSource.indexOf(Helpers.getDefaultContractExample(true)) !== -1 && // default 'MyContract' exists and 12 | contractSource.split('contract ').length - 1 === 1)) 13 | ) { 14 | // 'MyContract' is the only contract 15 | localStorage.setItem('contractSource', Helpers.getDefaultContractExample()); 16 | } 17 | 18 | Meteor.Spinner.options = { 19 | lines: 17, // The number of lines to draw 20 | length: 0, // The length of each line 21 | width: 4, // The line thickness 22 | radius: 16, // The radius of the inner circle 23 | corners: 1, // Corner roundness (0..1) 24 | rotate: 0, // The rotation offset 25 | direction: 1, // 1: clockwise, -1: counterclockwise 26 | color: '#000', // #rgb or #rrggbb or array of colors 27 | speed: 1.7, // Rounds per second 28 | trail: 49, // Afterglow percentage 29 | shadow: false, // Whether to render a shadow 30 | hwaccel: false, // Whether to use hardware acceleration 31 | className: 'spinner', // The CSS class to assign to the spinner 32 | zIndex: 10, // The z-index (defaults to 2000000000) 33 | top: '50%', // Top position relative to parent 34 | left: '50%' // Left position relative to parent 35 | }; 36 | 37 | var checkSync = function() { 38 | // Stop app operation, when the node is syncing 39 | web3.eth 40 | .isSyncing() 41 | .then(function(syncing) { 42 | if (syncing === true) { 43 | console.time('nodeRestarted'); 44 | console.log('Node started syncing, stopping app operation'); 45 | web3.reset(true); 46 | 47 | // clear observers 48 | _.each(collectionObservers, function(observer) { 49 | if (observer) { 50 | observer.stop(); 51 | } 52 | }); 53 | collectionObservers = []; 54 | } else if (_.isObject(syncing)) { 55 | syncing.progress = Math.floor( 56 | (syncing.currentBlock - syncing.startingBlock) / 57 | (syncing.highestBlock - syncing.startingBlock) * 58 | 100 59 | ); 60 | syncing.blockDiff = numeral( 61 | syncing.highestBlock - syncing.currentBlock 62 | ).format('0,0'); 63 | 64 | TemplateVar.setTo('header nav', 'syncing', syncing); 65 | } else { 66 | console.timeEnd('nodeRestarted'); 67 | console.log('Restart app operation again'); 68 | 69 | TemplateVar.setTo('header nav', 'syncing', false); 70 | 71 | // re-gain app operation 72 | connectToNode(); 73 | } 74 | }) 75 | .catch(function(error) { 76 | console.log('Error: ', error); 77 | if (error.toString().toLowerCase().includes('connection not open')) { 78 | showModal(); 79 | } else { 80 | // retry 81 | checkSync(); 82 | } 83 | }); 84 | }; 85 | 86 | var showModal = function() { 87 | // make sure the modal is rendered after all routes are executed 88 | Meteor.setTimeout(function() { 89 | // if in mist, tell to start geth, otherwise start with RPC 90 | var gethRPC = window.mist 91 | ? 'geth' 92 | : 'geth --ws --wsorigins "' + 93 | window.location.protocol + 94 | '//' + 95 | window.location.host + 96 | '"'; 97 | 98 | EthElements.Modal.question( 99 | { 100 | text: new Spacebars.SafeString( 101 | TAPi18n.__( 102 | 'wallet.app.texts.connectionError' + 103 | (window.mist ? 'Mist' : 'Browser'), 104 | { node: gethRPC } 105 | ) 106 | ), 107 | ok: function() { 108 | Tracker.afterFlush(function() { 109 | connect(); 110 | }); 111 | } 112 | }, 113 | { 114 | closeable: false 115 | } 116 | ); 117 | }, 600); 118 | }; 119 | 120 | var connect = function() { 121 | web3.eth.net 122 | .isListening() 123 | .then(function(isListening) { 124 | if (isListening) { 125 | // only start app operation, when the node is not syncing (or the eth_syncing property doesn't exists) 126 | web3.eth 127 | .isSyncing() 128 | .then(function(syncing) { 129 | if (!syncing) { 130 | connectToNode(); 131 | } else { 132 | EthAccounts.init(); 133 | } 134 | }) 135 | .catch(function(error) { 136 | console.log('Error: ', error); 137 | connect(); // retry 138 | }); 139 | } else { 140 | showModal(); 141 | } 142 | }) 143 | .catch(function(error) { 144 | console.log('Error: ', error); 145 | if (error.toString().toLowerCase().includes('connection not open')) { 146 | showModal(); 147 | } else { 148 | // retry 149 | connect(); 150 | } 151 | }); 152 | }; 153 | 154 | Meteor.startup(function() { 155 | // delay so we make sure the data is already loaded from the indexedDB 156 | // TODO improve persistent-minimongo2 ? 157 | Meteor.setTimeout(function() { 158 | connect(); 159 | checkSync(); 160 | }, 3000); 161 | }); 162 | -------------------------------------------------------------------------------- /app/client/lib/collections.js: -------------------------------------------------------------------------------- 1 | // Basic (local) collections, which will be observed by whisper (see whisperConnection.js) 2 | // we use {connection: null} to prevent them from syncing with our not existing Meteor server 3 | 4 | Wallets = new Mongo.Collection('wallets', { connection: null }); 5 | new PersistentMinimongo2(Wallets, 'ethereum_wallet'); 6 | 7 | CustomContracts = new Mongo.Collection('custom-contracts', { 8 | connection: null 9 | }); 10 | new PersistentMinimongo2(CustomContracts, 'ethereum_wallet'); 11 | 12 | // Contains the transactions 13 | Transactions = new Mongo.Collection('transactions', { connection: null }); 14 | new PersistentMinimongo2(Transactions, 'ethereum_wallet'); 15 | 16 | // Contains the pending confirmations 17 | PendingConfirmations = new Mongo.Collection('pending-confirmations', { 18 | connection: null 19 | }); 20 | new PersistentMinimongo2(PendingConfirmations, 'ethereum_wallet'); 21 | 22 | // Contains the custom contract events 23 | Events = new Mongo.Collection('events', { connection: null }); 24 | new PersistentMinimongo2(Events, 'ethereum_wallet'); 25 | 26 | // Contains Coin Information 27 | Tokens = new Mongo.Collection('tokens', { connection: null }); 28 | new PersistentMinimongo2(Tokens, 'ethereum_wallet'); 29 | -------------------------------------------------------------------------------- /app/client/lib/ethereum/1_web3Init.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 | -------------------------------------------------------------------------------- /app/client/lib/ethereum/observeBlocks.js: -------------------------------------------------------------------------------- 1 | var peerCountIntervalId = null; 2 | 3 | /** 4 | Update the peercount 5 | 6 | @method getPeerCount 7 | */ 8 | var getPeerCount = function() { 9 | web3.eth.net.getPeerCount().then(function(peerCount) { 10 | Session.set('peerCount', peerCount); 11 | }); 12 | }; 13 | 14 | /** 15 | Update wallet balances 16 | 17 | @method updateBalances 18 | */ 19 | updateBalances = function() { 20 | // UPDATE ALL BALANCES (incl. Tokens) 21 | var walletsAndContracts = Wallets.find() 22 | .fetch() 23 | .concat(CustomContracts.find().fetch()); 24 | 25 | var allAccounts = EthAccounts.find() 26 | .fetch() 27 | .concat(walletsAndContracts); 28 | 29 | // go through all existing accounts, for each token 30 | _.each(walletsAndContracts, function(account) { 31 | if (account.address) { 32 | web3.eth.getBalance(account.address, function(err, res) { 33 | if (!err) { 34 | // is of type wallet 35 | if (account.creationBlock) { 36 | Wallets.update(account._id, { 37 | $set: { 38 | balance: res.toString(10) 39 | } 40 | }); 41 | } else { 42 | CustomContracts.update(account._id, { 43 | $set: { 44 | balance: res.toString(10) 45 | } 46 | }); 47 | } 48 | } 49 | }); 50 | 51 | // update dailylimit spent, etc, if wallet type 52 | if (account.creationBlock) { 53 | Meteor.setTimeout(function() { 54 | updateContractData(account); 55 | }, 1000); 56 | } 57 | } 58 | }); 59 | 60 | // WALLETS STUCK IN CREATE STATE 61 | // issue found when using the light client mode on Mist 0.9.1 and 0.9.2 62 | var creatingWallets = Wallets.find({ 63 | transactionHash: { $exists: true }, 64 | address: { $exists: false } 65 | }).fetch(); 66 | 67 | _.each(creatingWallets, function(wallet) { 68 | // Fetches transactionReceipt looking for contractAddress 69 | web3.eth 70 | .getTransactionReceipt(wallet.transactionHash) 71 | .then(function(receipt) { 72 | if (receipt && receipt.contractAddress !== null) { 73 | // Updates the wallet 74 | var r = Wallets.update(wallet._id, { 75 | $set: { 76 | address: receipt.contractAddress 77 | } 78 | }); 79 | } 80 | return null; 81 | }); 82 | }); 83 | 84 | // UPDATE ENS 85 | _.each(allAccounts, function(account) { 86 | // Only check ENS names every N minutes 87 | var now = Date.now(); 88 | if ( 89 | !account.ensCheck || 90 | (account.ensCheck && now - account.ensCheck > 10 * 60 * 1000) 91 | ) { 92 | Helpers.getENSName(account.address, function(err, name, returnedAddr) { 93 | if (!err && account.address.toLowerCase() == returnedAddr) { 94 | EthAccounts.update( 95 | { address: account.address }, 96 | { $set: { name: name, ens: true, ensCheck: now } } 97 | ); 98 | CustomContracts.update( 99 | { address: account.address }, 100 | { $set: { name: name, ens: true, ensCheck: now } } 101 | ); 102 | Wallets.update( 103 | { address: account.address }, 104 | { $set: { name: name, ens: true, ensCheck: now } } 105 | ); 106 | } else { 107 | EthAccounts.update( 108 | { address: account.address }, 109 | { $set: { ens: false, ensCheck: now } } 110 | ); 111 | CustomContracts.update( 112 | { address: account.address }, 113 | { $set: { ens: false, ensCheck: now } } 114 | ); 115 | Wallets.update( 116 | { address: account.address }, 117 | { $set: { ens: false, ensCheck: now } } 118 | ); 119 | } 120 | }); 121 | } 122 | }); 123 | 124 | // UPDATE TOKEN BALANCES 125 | _.each(Tokens.find().fetch(), function(token) { 126 | if (!token.address) return; 127 | 128 | var tokenInstance = Object.assign({}, TokenContract); 129 | tokenInstance.options.address = token.address; 130 | 131 | _.each(allAccounts, function(account) { 132 | tokenInstance.methods 133 | .balanceOf(account.address) 134 | .call() 135 | .then(function(balance) { 136 | var currentBalance = 137 | token && token.balances ? token.balances[account._id] : 0; 138 | 139 | if (balance.toString(10) !== currentBalance) { 140 | var set = {}; 141 | if (balance > 0) { 142 | set['balances.' + account._id] = balance.toString(10); 143 | Tokens.update(token._id, { $set: set }); 144 | } else if (currentBalance) { 145 | set['balances.' + account._id] = ''; 146 | Tokens.update(token._id, { $unset: set }); 147 | } 148 | } 149 | return null; 150 | }); 151 | }); 152 | }); 153 | }; 154 | 155 | /** 156 | Observe the latest blocks 157 | 158 | @method observeLatestBlocks 159 | */ 160 | observeLatestBlocks = function() { 161 | // update balances on start 162 | updateBalances(); 163 | 164 | // GET the latest blockchain information 165 | web3.eth.subscribe('newBlockHeaders', function(e, res) { 166 | if (!e) { 167 | updateBalances(); 168 | } 169 | }); 170 | 171 | // check peer count 172 | Session.setDefault('peerCount', 0); 173 | getPeerCount(); 174 | 175 | clearInterval(peerCountIntervalId); 176 | peerCountIntervalId = setInterval(function() { 177 | getPeerCount(); 178 | }, 1000); 179 | }; 180 | -------------------------------------------------------------------------------- /app/client/lib/ethereum/observeCustomContracts.js: -------------------------------------------------------------------------------- 1 | /** 2 | Observe custom contacts 3 | 4 | @method observeCustomContracts 5 | */ 6 | observeCustomContracts = function() { 7 | /** 8 | Observe custom contracts, listen for new created tokens. 9 | 10 | @class CustomContracts({}).observe 11 | @constructor 12 | */ 13 | collectionObservers[collectionObservers.length] = CustomContracts.find( 14 | {} 15 | ).observe({ 16 | /** 17 | Will check if the contracts are on the current chain 18 | 19 | @method added 20 | */ 21 | added: function(newDocument) { 22 | // check if wallet has code 23 | web3.eth.getCode(newDocument.address, function(e, code) { 24 | if (!e && code && code.length > 2) { 25 | CustomContracts.update(newDocument._id, { 26 | $unset: { 27 | disabled: false 28 | } 29 | }); 30 | 31 | // TODO: check for logs 32 | // addLogWatching(newDocument); 33 | } else if (!e) { 34 | // if there's no code, check the contract has a balance 35 | web3.eth.getBalance(newDocument.address, function(e, balance) { 36 | if (!e && web3.utils.toBN(balance).gt(0)) { 37 | CustomContracts.update(newDocument._id, { 38 | $unset: { 39 | disabled: false 40 | } 41 | }); 42 | 43 | // TODO: check for logs 44 | // addLogWatching(newDocument); 45 | } else if (!e) { 46 | CustomContracts.update(newDocument._id, { 47 | $set: { 48 | disabled: true 49 | } 50 | }); 51 | } 52 | }); 53 | } 54 | }); 55 | } 56 | }); 57 | }; 58 | 59 | // TODO: convert from filters to subscriptions 60 | // var addLogWatching = function(newDocument){ 61 | // var contractInstance = new web3.eth.Contract(newDocument.jsonInterface, newDocument.address); 62 | // var blockToCheckBack = (newDocument.checkpointBlock || 0) - ethereumConfig.rollBackBy; 63 | // if(blockToCheckBack < 0) 64 | // blockToCheckBack = 0; 65 | // console.log('EVENT LOG: Checking Custom Contract Events for '+ newDocument.address +' (_id: '+ newDocument._id + ') from block # '+ blockToCheckBack); 66 | // // delete the last logs until block -500 67 | // _.each(Events.find({_id: {$in: newDocument.contractEvents || []}, blockNumber: {$exists: true, $gt: blockToCheckBack}}).fetch(), function(log){ 68 | // if(log) 69 | // Events.remove({_id: log._id}); 70 | // }); 71 | // var filter = contractInstance.allEvents({fromBlock: blockToCheckBack, toBlock: 'latest'}); 72 | // // get past logs, to set the new blockNumber 73 | // var currentBlock = EthBlocks.latest.number; 74 | // filter.get(function(error, logs) { 75 | // if(!error) { 76 | // // update last checkpoint block 77 | // CustomContracts.update({_id: newDocument._id}, {$set: { 78 | // checkpointBlock: (currentBlock || EthBlocks.latest.number) - ethereumConfig.rollBackBy 79 | // }}); 80 | // } 81 | // }); 82 | // filter.watch(function(error, log){ 83 | // if(!error) { 84 | // var id = Helpers.makeId('log', web3.sha3(log.logIndex + 'x' + log.transactionHash + 'x' + log.blockHash)); 85 | // if(log.removed) { 86 | // Events.remove(id); 87 | // } else { 88 | // web3.eth.getBlock(log.blockHash, function(err, block){ 89 | // if(!err) { 90 | // _.each(log.args, function(value, key){ 91 | // // if bignumber 92 | // if((_.isObject(value) || value instanceof BigNumber) && value.toFormat) { 93 | // value = value.toString(10); 94 | // log.args[key] = value; 95 | // } 96 | // }); 97 | // log.timestamp = block.timestamp; 98 | // Events.upsert(id, log); 99 | // } 100 | // }); 101 | // } 102 | // } 103 | // }); 104 | // }; 105 | -------------------------------------------------------------------------------- /app/client/lib/ethereum/observeEvents.js: -------------------------------------------------------------------------------- 1 | // meant to speed up and make less requests 2 | var customContractsCache = {}; 3 | /** 4 | Observe events 5 | 6 | @method observeEvents 7 | */ 8 | observeEvents = function() { 9 | /** 10 | Observe transactions, listen for new created transactions. 11 | 12 | @class Events({}).observe 13 | @constructor 14 | */ 15 | collectionObservers[collectionObservers.length] = Events.find({}).observe({ 16 | /** 17 | This will observe when events are added and link it to the custom contract. 18 | 19 | @method added 20 | */ 21 | added: function(newDocument) { 22 | CustomContracts.update( 23 | { address: newDocument.address.toLowerCase() }, 24 | { 25 | $addToSet: { 26 | contractEvents: newDocument._id 27 | } 28 | } 29 | ); 30 | }, 31 | /** 32 | Remove events confirmations from the accounts 33 | 34 | @method removed 35 | */ 36 | removed: function(document) { 37 | CustomContracts.update( 38 | { address: document.address.toLowerCase() }, 39 | { 40 | $pull: { 41 | contractEvents: document._id 42 | } 43 | } 44 | ); 45 | } 46 | }); 47 | }; 48 | -------------------------------------------------------------------------------- /app/client/lib/ethereum/observePendingConfirmations.js: -------------------------------------------------------------------------------- 1 | /** 2 | Check if a pending confirmation is still pending 3 | 4 | @method checkConfirmation 5 | */ 6 | checkConfirmation = function(confirmationId) { 7 | var conf = PendingConfirmations.findOne(confirmationId); 8 | if (!conf) return; 9 | 10 | var wallet = Helpers.getAccountByAddress(conf.from); 11 | 12 | if ( 13 | conf.operation && 14 | wallet && 15 | wallet.requiredSignatures > conf.confirmedOwners.length 16 | ) { 17 | var removed = false; 18 | var contract = contracts['ct_' + wallet._id]; 19 | 20 | setTimeout(function() { 21 | _.each(wallet.owners, function(owner) { 22 | contract.hasConfirmed(conf.operation, owner, function(e, res) { 23 | if (!removed && !e) { 24 | if (res) { 25 | PendingConfirmations.update(confirmationId, { 26 | $addToSet: { confirmedOwners: owner } 27 | }); 28 | } else { 29 | PendingConfirmations.update(confirmationId, { 30 | $pull: { confirmedOwners: owner } 31 | }); 32 | } 33 | 34 | var pendingConf = PendingConfirmations.findOne(confirmationId); 35 | 36 | if ( 37 | pendingConf && 38 | (!pendingConf.confirmedOwners.length || 39 | Number(wallet.requiredSignatures) === 40 | pendingConf.confirmedOwners.length) 41 | ) { 42 | PendingConfirmations.remove(confirmationId); 43 | removed = true; 44 | } 45 | } 46 | }); 47 | }); 48 | }, 1000); 49 | } 50 | }; 51 | 52 | /** 53 | Observe pending confirmations 54 | 55 | @method observePendingConfirmations 56 | */ 57 | observePendingConfirmations = function() { 58 | /** 59 | Observe PendingConfirmations 60 | 61 | @class PendingConfirmations({}).observe 62 | @constructor 63 | */ 64 | collectionObservers[collectionObservers.length] = PendingConfirmations.find( 65 | {} 66 | ).observe({ 67 | /** 68 | Add pending confirmations to the accounts 69 | 70 | @method added 71 | */ 72 | added: function(document) { 73 | checkConfirmation(document._id); 74 | 75 | if ( 76 | typeof mist !== 'undefined' && 77 | document.confirmedOwners && 78 | document.confirmedOwners.length 79 | ) { 80 | mist.menu.setBadge( 81 | TAPi18n.__('wallet.app.texts.pendingConfirmationsBadge') 82 | ); 83 | } 84 | }, 85 | /** 86 | Remove pending confirmations from the accounts 87 | 88 | @method removed 89 | */ 90 | removed: function(document) { 91 | updateMistBadge(); 92 | }, 93 | /** 94 | Add pending confirmations to the accounts 95 | 96 | @method changed 97 | */ 98 | changed: function(id, fields) { 99 | if ( 100 | typeof mist !== 'undefined' && 101 | document.confirmedOwners && 102 | document.confirmedOwners.length 103 | ) { 104 | mist.menu.setBadge( 105 | TAPi18n.__('wallet.app.texts.pendingConfirmationsBadge') 106 | ); 107 | } 108 | } 109 | }); 110 | }; 111 | -------------------------------------------------------------------------------- /app/client/lib/ethereum/tokenInterface.js: -------------------------------------------------------------------------------- 1 | //"0x11485c5f164d6a67a72eee9093b2581d1c304094" 2 | 3 | // Token Interface 4 | 5 | var tokenInterface = [ 6 | { 7 | type: 'function', 8 | name: 'name', 9 | constant: true, 10 | inputs: [], 11 | outputs: [ 12 | { 13 | name: '', 14 | type: 'string' 15 | } 16 | ] 17 | }, 18 | { 19 | type: 'function', 20 | name: 'decimals', 21 | constant: true, 22 | inputs: [], 23 | outputs: [ 24 | { 25 | name: '', 26 | type: 'uint8' 27 | } 28 | ] 29 | }, 30 | { 31 | type: 'function', 32 | name: 'balanceOf', 33 | constant: true, 34 | inputs: [ 35 | { 36 | name: '', 37 | type: 'address' 38 | } 39 | ], 40 | outputs: [ 41 | { 42 | name: '', 43 | type: 'uint256' 44 | } 45 | ] 46 | }, 47 | { 48 | type: 'function', 49 | name: 'symbol', 50 | constant: true, 51 | inputs: [], 52 | outputs: [ 53 | { 54 | name: '', 55 | type: 'string' 56 | } 57 | ] 58 | }, 59 | { 60 | type: 'function', 61 | name: 'transfer', 62 | constant: false, 63 | inputs: [ 64 | { 65 | name: '_to', 66 | type: 'address' 67 | }, 68 | { 69 | name: '_value', 70 | type: 'uint256' 71 | } 72 | ], 73 | outputs: [] 74 | }, 75 | { 76 | type: 'constructor', 77 | inputs: [ 78 | { 79 | name: '_supply', 80 | type: 'uint256' 81 | }, 82 | { 83 | name: '_name', 84 | type: 'string' 85 | }, 86 | { 87 | name: '_decimals', 88 | type: 'uint8' 89 | }, 90 | { 91 | name: '_symbol', 92 | type: 'string' 93 | } 94 | ] 95 | }, 96 | { 97 | name: 'Transfer', 98 | type: 'event', 99 | anonymous: false, 100 | inputs: [ 101 | { 102 | indexed: true, 103 | name: 'from', 104 | type: 'address' 105 | }, 106 | { 107 | indexed: true, 108 | name: 'to', 109 | type: 'address' 110 | }, 111 | { 112 | indexed: false, 113 | name: 'value', 114 | type: 'uint256' 115 | } 116 | ] 117 | }, 118 | { 119 | constant: false, 120 | inputs: [ 121 | { 122 | name: '_spender', 123 | type: 'address' 124 | }, 125 | { 126 | name: '_value', 127 | type: 'uint256' 128 | } 129 | ], 130 | name: 'approve', 131 | outputs: [ 132 | { 133 | name: 'success', 134 | type: 'bool' 135 | } 136 | ], 137 | type: 'function' 138 | }, 139 | { 140 | constant: true, 141 | inputs: [ 142 | { 143 | name: '', 144 | type: 'address' 145 | }, 146 | { 147 | name: '', 148 | type: 'address' 149 | } 150 | ], 151 | name: 'allowance', 152 | outputs: [ 153 | { 154 | name: '', 155 | type: 'uint256' 156 | } 157 | ], 158 | type: 'function' 159 | } 160 | ]; 161 | 162 | TokenContract = new web3.eth.Contract(tokenInterface); 163 | -------------------------------------------------------------------------------- /app/client/lib/ethereum/walletConnector.js: -------------------------------------------------------------------------------- 1 | /** 2 | The walletConnector 3 | 4 | @class walletConnector 5 | @constructor 6 | */ 7 | 8 | /** 9 | Contains all wallet contracts 10 | 11 | @property contracts 12 | */ 13 | contracts = {}; 14 | 15 | /** 16 | Contains all collection observers 17 | 18 | @property collectionObservers 19 | */ 20 | collectionObservers = []; 21 | 22 | /** 23 | Config for the ethereum connector 24 | 25 | @property config 26 | */ 27 | ethereumConfig = { 28 | /** 29 | Number of blocks to rollback, from the last checkpoint block of the wallet. 30 | 31 | @property ethereumConfig.rollBackBy 32 | */ 33 | rollBackBy: 0, 34 | /** 35 | Number of blocks to confirm a wallet 36 | 37 | @property ethereumConfig.requiredConfirmations 38 | */ 39 | requiredConfirmations: 12, 40 | /** 41 | The default daily limit used for simple accounts 42 | 43 | @property ethereumConfig.dailyLimitDefault 44 | */ 45 | dailyLimitDefault: '100000000000000000000000000' 46 | }; 47 | 48 | /** 49 | Check and set which network we are on. 50 | 51 | @method checkNetwork 52 | */ 53 | Session.setDefault('network', false); 54 | var checkNetwork = function() { 55 | web3.eth.getBlock(0).then(function(block) { 56 | switch (block.hash) { 57 | case '0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3': 58 | Session.set('network', 'main'); 59 | break; 60 | case '0x6341fd3daf94b748c72ced5a5b26028f2474f5f00d824504e4fa37a75767e177': 61 | Session.set('network', 'rinkeby'); 62 | break; 63 | case '0x41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d': 64 | Session.set('network', 'ropsten'); 65 | break; 66 | case '0xa3c565fc15c7478862d50ccd6561e3c06b24cc509bf388941c25ea985ce32cb9': 67 | Session.set('network', 'kovan'); 68 | break; 69 | default: 70 | Session.set('network', 'private'); 71 | } 72 | }); 73 | }; 74 | 75 | /** 76 | Connects to a node and setup all the subscriptions for the accounts. 77 | 78 | @method connectToNode 79 | */ 80 | connectToNode = function() { 81 | console.time('startNode'); 82 | console.log('Connect to node...'); 83 | 84 | checkNetwork(); 85 | 86 | EthAccounts.init(); 87 | EthBlocks.init(); 88 | 89 | EthTools.ticker.start({ 90 | extraParams: typeof mist !== 'undefined' ? 'Mist-' + mist.version : '', 91 | currencies: ['BTC', 'USD', 'EUR', 'BRL', 'GBP'] 92 | }); 93 | 94 | if (EthAccounts.find().count() > 0) { 95 | checkForOriginalWallet(); 96 | } 97 | 98 | // EthBlocks.detectFork(function(oldBlock, block){ 99 | // console.log('FORK detected from Block #'+ oldBlock.number + ' -> #'+ block.number +', rolling back!'); 100 | 101 | // // Go through all accounts and re-run 102 | // _.each(Wallets.find({}).fetch(), function(wallet){ 103 | // // REMOVE ADDRESS for YOUNG ACCOUNTS, so that it tries to get the Created event and correct address again 104 | // if(wallet.creationBlock + ethereumConfig.requiredConfirmations >= block.number) 105 | // delete wallet.address; 106 | 107 | // setupContractSubscription(wallet); 108 | // }); 109 | // }); 110 | 111 | // Reset collection observers 112 | _.each(collectionObservers, function(observer) { 113 | if (observer) { 114 | observer.stop(); 115 | } 116 | }); 117 | collectionObservers = []; 118 | 119 | observeLatestBlocks(); 120 | 121 | observeWallets(); 122 | 123 | observeTransactions(); 124 | 125 | observeEvents(); 126 | 127 | observeTokens(); 128 | 129 | observePendingConfirmations(); 130 | 131 | observeCustomContracts(); 132 | 133 | console.timeEnd('startNode'); 134 | }; 135 | 136 | /** 137 | Will remove all transactions, and will set the checkpointBlock to the creationBlock in the wallets 138 | 139 | @method connectToNode 140 | */ 141 | resetWallet = function function_name(argument) { 142 | _.each(Transactions.find().fetch(), function(tx) { 143 | console.log(tx._id); 144 | try { 145 | Transactions.remove(tx._id); 146 | } catch (e) { 147 | console.error(e); 148 | } 149 | }); 150 | 151 | _.each(PendingConfirmations.find().fetch(), function(pc) { 152 | try { 153 | PendingConfirmations.remove(pc._id); 154 | } catch (e) { 155 | console.error(e); 156 | } 157 | }); 158 | 159 | _.each(Wallets.find().fetch(), function(wallet) { 160 | Wallets.update(wallet._id, { 161 | $set: { 162 | checkpointBlock: wallet.creationBlock, 163 | transactions: [] 164 | } 165 | }); 166 | }); 167 | 168 | web3.eth.clearSubscriptions(); 169 | 170 | console.log('The wallet will re-fetch log information in 6 seconds...'); 171 | 172 | setTimeout(function() { 173 | console.log('Fetching logs...'); 174 | connectToNode(); 175 | }, 1000 * 6); 176 | }; 177 | -------------------------------------------------------------------------------- /app/client/lib/thirdparty/theme-solarized_dark.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/theme/solarized_dark",["require","exports","module","ace/lib/dom"],function(e,t,n){t.isDark=!0,t.cssClass="ace-solarized-dark",t.cssText=".ace-solarized-dark .ace_gutter {background: #01313f;color: #d0edf7}.ace-solarized-dark .ace_print-margin {width: 1px;background: #33555E}.ace-solarized-dark {background-color: #002B36;color: #93A1A1}.ace-solarized-dark .ace_entity.ace_other.ace_attribute-name,.ace-solarized-dark .ace_storage {color: #93A1A1}.ace-solarized-dark .ace_cursor,.ace-solarized-dark .ace_string.ace_regexp {color: #D30102}.ace-solarized-dark .ace_marker-layer .ace_active-line,.ace-solarized-dark .ace_marker-layer .ace_selection {background: rgba(255, 255, 255, 0.1)}.ace-solarized-dark.ace_multiselect .ace_selection.ace_start {box-shadow: 0 0 3px 0px #002B36;border-radius: 2px}.ace-solarized-dark .ace_marker-layer .ace_step {background: rgb(102, 82, 0)}.ace-solarized-dark .ace_marker-layer .ace_bracket {margin: -1px 0 0 -1px;border: 1px solid rgba(147, 161, 161, 0.50)}.ace-solarized-dark .ace_gutter-active-line {background-color: #0d3440}.ace-solarized-dark .ace_marker-layer .ace_selected-word {border: 1px solid #073642}.ace-solarized-dark .ace_invisible {color: rgba(147, 161, 161, 0.50)}.ace-solarized-dark .ace_keyword,.ace-solarized-dark .ace_meta,.ace-solarized-dark .ace_support.ace_class,.ace-solarized-dark .ace_support.ace_type {color: #859900}.ace-solarized-dark .ace_constant.ace_character,.ace-solarized-dark .ace_constant.ace_other {color: #CB4B16}.ace-solarized-dark .ace_constant.ace_language {color: #B58900}.ace-solarized-dark .ace_constant.ace_numeric {color: #D33682}.ace-solarized-dark .ace_fold {background-color: #268BD2;border-color: #93A1A1}.ace-solarized-dark .ace_entity.ace_name.ace_function,.ace-solarized-dark .ace_entity.ace_name.ace_tag,.ace-solarized-dark .ace_support.ace_function,.ace-solarized-dark .ace_variable,.ace-solarized-dark .ace_variable.ace_language {color: #268BD2}.ace-solarized-dark .ace_string {color: #2AA198}.ace-solarized-dark .ace_comment {font-style: italic;color: #657B83}.ace-solarized-dark .ace_indent-guide {background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAACCAYAAACZgbYnAAAAEklEQVQImWNg0Db1ZVCxc/sPAAd4AlUHlLenAAAAAElFTkSuQmCC) right repeat-y}";var r=e("../lib/dom");r.importCssString(t.cssText,t.cssClass)}) -------------------------------------------------------------------------------- /app/client/lib/thirdparty/theme-solarized_light.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/theme/solarized_light",["require","exports","module","ace/lib/dom"],function(e,t,n){t.isDark=!1,t.cssClass="ace-solarized-light",t.cssText=".ace-solarized-light .ace_gutter {background: #fbf1d3;color: #333}.ace-solarized-light .ace_print-margin {width: 1px;background: #e8e8e8}.ace-solarized-light {background-color: #FDF6E3;color: #586E75}.ace-solarized-light .ace_cursor {color: #000000}.ace-solarized-light .ace_marker-layer .ace_selection {background: rgba(7, 54, 67, 0.09)}.ace-solarized-light.ace_multiselect .ace_selection.ace_start {box-shadow: 0 0 3px 0px #FDF6E3;border-radius: 2px}.ace-solarized-light .ace_marker-layer .ace_step {background: rgb(255, 255, 0)}.ace-solarized-light .ace_marker-layer .ace_bracket {margin: -1px 0 0 -1px;border: 1px solid rgba(147, 161, 161, 0.50)}.ace-solarized-light .ace_marker-layer .ace_active-line {background: #EEE8D5}.ace-solarized-light .ace_gutter-active-line {background-color : #EDE5C1}.ace-solarized-light .ace_marker-layer .ace_selected-word {border: 1px solid #073642}.ace-solarized-light .ace_invisible {color: rgba(147, 161, 161, 0.50)}.ace-solarized-light .ace_keyword,.ace-solarized-light .ace_meta,.ace-solarized-light .ace_support.ace_class,.ace-solarized-light .ace_support.ace_type {color: #859900}.ace-solarized-light .ace_constant.ace_character,.ace-solarized-light .ace_constant.ace_other {color: #CB4B16}.ace-solarized-light .ace_constant.ace_language {color: #B58900}.ace-solarized-light .ace_constant.ace_numeric {color: #D33682}.ace-solarized-light .ace_fold {background-color: #268BD2;border-color: #586E75}.ace-solarized-light .ace_entity.ace_name.ace_function,.ace-solarized-light .ace_entity.ace_name.ace_tag,.ace-solarized-light .ace_support.ace_function,.ace-solarized-light .ace_variable,.ace-solarized-light .ace_variable.ace_language {color: #268BD2}.ace-solarized-light .ace_storage {color: #073642}.ace-solarized-light .ace_string {color: #2AA198}.ace-solarized-light .ace_string.ace_regexp {color: #D30102}.ace-solarized-light .ace_comment,.ace-solarized-light .ace_entity.ace_other.ace_attribute-name {color: #93A1A1}.ace-solarized-light .ace_indent-guide {background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAACCAYAAACZgbYnAAAAEklEQVQImWNgYGBgYHjy8NJ/AAjgA5fzQUmBAAAAAElFTkSuQmCC) right repeat-y}";var r=e("../lib/dom");r.importCssString(t.cssText,t.cssClass)}) -------------------------------------------------------------------------------- /app/client/lib/thirdparty/theme-tomorrow.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/theme/tomorrow",["require","exports","module","ace/lib/dom"],function(e,t,n){t.isDark=!1,t.cssClass="ace-tomorrow",t.cssText=".ace-tomorrow .ace_gutter {background: #f6f6f6;color: #4D4D4C}.ace-tomorrow .ace_print-margin {width: 1px;background: #f6f6f6}.ace-tomorrow {background-color: #FFFFFF;color: #4D4D4C}.ace-tomorrow .ace_cursor {color: #AEAFAD}.ace-tomorrow .ace_marker-layer .ace_selection {background: #D6D6D6}.ace-tomorrow.ace_multiselect .ace_selection.ace_start {box-shadow: 0 0 3px 0px #FFFFFF;border-radius: 2px}.ace-tomorrow .ace_marker-layer .ace_step {background: rgb(255, 255, 0)}.ace-tomorrow .ace_marker-layer .ace_bracket {margin: -1px 0 0 -1px;border: 1px solid #D1D1D1}.ace-tomorrow .ace_marker-layer .ace_active-line {background: #EFEFEF}.ace-tomorrow .ace_gutter-active-line {background-color : #dcdcdc}.ace-tomorrow .ace_marker-layer .ace_selected-word {border: 1px solid #D6D6D6}.ace-tomorrow .ace_invisible {color: #D1D1D1}.ace-tomorrow .ace_keyword,.ace-tomorrow .ace_meta,.ace-tomorrow .ace_storage,.ace-tomorrow .ace_storage.ace_type,.ace-tomorrow .ace_support.ace_type {color: #8959A8}.ace-tomorrow .ace_keyword.ace_operator {color: #3E999F}.ace-tomorrow .ace_constant.ace_character,.ace-tomorrow .ace_constant.ace_language,.ace-tomorrow .ace_constant.ace_numeric,.ace-tomorrow .ace_keyword.ace_other.ace_unit,.ace-tomorrow .ace_support.ace_constant,.ace-tomorrow .ace_variable.ace_parameter {color: #F5871F}.ace-tomorrow .ace_constant.ace_other {color: #666969}.ace-tomorrow .ace_invalid {color: #FFFFFF;background-color: #C82829}.ace-tomorrow .ace_invalid.ace_deprecated {color: #FFFFFF;background-color: #8959A8}.ace-tomorrow .ace_fold {background-color: #4271AE;border-color: #4D4D4C}.ace-tomorrow .ace_entity.ace_name.ace_function,.ace-tomorrow .ace_support.ace_function,.ace-tomorrow .ace_variable {color: #4271AE}.ace-tomorrow .ace_support.ace_class,.ace-tomorrow .ace_support.ace_type {color: #C99E00}.ace-tomorrow .ace_heading,.ace-tomorrow .ace_markup.ace_heading,.ace-tomorrow .ace_string {color: #718C00}.ace-tomorrow .ace_entity.ace_name.ace_tag,.ace-tomorrow .ace_entity.ace_other.ace_attribute-name,.ace-tomorrow .ace_meta.ace_tag,.ace-tomorrow .ace_string.ace_regexp,.ace-tomorrow .ace_variable {color: #C82829}.ace-tomorrow .ace_comment {color: #8E908C}.ace-tomorrow .ace_indent-guide {background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAACCAYAAACZgbYnAAAAE0lEQVQImWP4////f4bdu3f/BwAlfgctduB85QAAAABJRU5ErkJggg==) right repeat-y}";var r=e("../lib/dom");r.importCssString(t.cssText,t.cssClass)}) -------------------------------------------------------------------------------- /app/client/lib/thirdparty/theme-tomorrow_night.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/theme/tomorrow_night",["require","exports","module","ace/lib/dom"],function(e,t,n){t.isDark=!0,t.cssClass="ace-tomorrow-night",t.cssText=".ace-tomorrow-night .ace_gutter {background: #25282c;color: #C5C8C6}.ace-tomorrow-night .ace_print-margin {width: 1px;background: #25282c}.ace-tomorrow-night {background-color: #1D1F21;color: #C5C8C6}.ace-tomorrow-night .ace_cursor {color: #AEAFAD}.ace-tomorrow-night .ace_marker-layer .ace_selection {background: #373B41}.ace-tomorrow-night.ace_multiselect .ace_selection.ace_start {box-shadow: 0 0 3px 0px #1D1F21;border-radius: 2px}.ace-tomorrow-night .ace_marker-layer .ace_step {background: rgb(102, 82, 0)}.ace-tomorrow-night .ace_marker-layer .ace_bracket {margin: -1px 0 0 -1px;border: 1px solid #4B4E55}.ace-tomorrow-night .ace_marker-layer .ace_active-line {background: #282A2E}.ace-tomorrow-night .ace_gutter-active-line {background-color: #282A2E}.ace-tomorrow-night .ace_marker-layer .ace_selected-word {border: 1px solid #373B41}.ace-tomorrow-night .ace_invisible {color: #4B4E55}.ace-tomorrow-night .ace_keyword,.ace-tomorrow-night .ace_meta,.ace-tomorrow-night .ace_storage,.ace-tomorrow-night .ace_storage.ace_type,.ace-tomorrow-night .ace_support.ace_type {color: #B294BB}.ace-tomorrow-night .ace_keyword.ace_operator {color: #8ABEB7}.ace-tomorrow-night .ace_constant.ace_character,.ace-tomorrow-night .ace_constant.ace_language,.ace-tomorrow-night .ace_constant.ace_numeric,.ace-tomorrow-night .ace_keyword.ace_other.ace_unit,.ace-tomorrow-night .ace_support.ace_constant,.ace-tomorrow-night .ace_variable.ace_parameter {color: #DE935F}.ace-tomorrow-night .ace_constant.ace_other {color: #CED1CF}.ace-tomorrow-night .ace_invalid {color: #CED2CF;background-color: #DF5F5F}.ace-tomorrow-night .ace_invalid.ace_deprecated {color: #CED2CF;background-color: #B798BF}.ace-tomorrow-night .ace_fold {background-color: #81A2BE;border-color: #C5C8C6}.ace-tomorrow-night .ace_entity.ace_name.ace_function,.ace-tomorrow-night .ace_support.ace_function,.ace-tomorrow-night .ace_variable {color: #81A2BE}.ace-tomorrow-night .ace_support.ace_class,.ace-tomorrow-night .ace_support.ace_type {color: #F0C674}.ace-tomorrow-night .ace_heading,.ace-tomorrow-night .ace_markup.ace_heading,.ace-tomorrow-night .ace_string {color: #B5BD68}.ace-tomorrow-night .ace_entity.ace_name.ace_tag,.ace-tomorrow-night .ace_entity.ace_other.ace_attribute-name,.ace-tomorrow-night .ace_meta.ace_tag,.ace-tomorrow-night .ace_string.ace_regexp,.ace-tomorrow-night .ace_variable {color: #CC6666}.ace-tomorrow-night .ace_comment {color: #969896}.ace-tomorrow-night .ace_indent-guide {background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAACCAYAAACZgbYnAAAAEklEQVQImWNgYGBgYHB3d/8PAAOIAdULw8qMAAAAAElFTkSuQmCC) right repeat-y}";var r=e("../lib/dom");r.importCssString(t.cssText,t.cssClass)}) -------------------------------------------------------------------------------- /app/client/mist.js: -------------------------------------------------------------------------------- 1 | updateMistBadge = function() { 2 | var conf = PendingConfirmations.findOne({ operation: { $exists: true } }); 3 | // set total balance in Mist menu, of no pending confirmation is Present 4 | if (typeof mist !== 'undefined' && (!conf || !conf.confirmedOwners.length)) { 5 | var accounts = EthAccounts.find({}).fetch(); 6 | var wallets = Wallets.find({ 7 | owners: { $in: _.pluck(accounts, 'address') } 8 | }).fetch(); 9 | 10 | var balance = _.reduce( 11 | _.pluck(_.union(accounts, wallets), 'balance'), 12 | function(memo, num) { 13 | return memo + Number(num); 14 | }, 15 | 0 16 | ); 17 | 18 | mist.menu.setBadge( 19 | EthTools.formatBalance(balance, '0.0 a', 'ether') + ' ETH' 20 | ); 21 | } 22 | }; 23 | 24 | // ADD MIST MENU 25 | updateMistMenu = function() { 26 | if (typeof mist === 'undefined') return; 27 | 28 | var accounts = _.union( 29 | Wallets.find({}, { sort: { name: 1 } }).fetch(), 30 | EthAccounts.find({}, { sort: { name: 1 } }).fetch() 31 | ); 32 | 33 | // sort by balance 34 | accounts.sort(Helpers.sortByBalance); 35 | 36 | Meteor.setTimeout(function() { 37 | var routeName = FlowRouter.current().route.name; 38 | 39 | // add/update mist menu 40 | mist.menu.clear(); 41 | mist.menu.add( 42 | 'wallets', 43 | { 44 | position: 1, 45 | name: TAPi18n.__('wallet.app.buttons.wallet'), 46 | selected: routeName === 'dashboard' 47 | }, 48 | function() { 49 | FlowRouter.go('/'); 50 | } 51 | ); 52 | mist.menu.add( 53 | 'send', 54 | { 55 | position: 2, 56 | name: TAPi18n.__('wallet.app.buttons.send'), 57 | selected: routeName === 'send' || routeName === 'sendTo' 58 | }, 59 | function() { 60 | FlowRouter.go('/send'); 61 | } 62 | ); 63 | 64 | _.each(accounts, function(account, index) { 65 | mist.menu.add( 66 | account._id, 67 | { 68 | position: 3 + index, 69 | name: account.name, 70 | badge: 71 | EthTools.formatBalance(account.balance, '0 a', 'ether') + ' ETH', 72 | selected: location.pathname === '/account/' + account.address 73 | }, 74 | function() { 75 | FlowRouter.go('/account/' + account.address); 76 | } 77 | ); 78 | }); 79 | 80 | // set total balance in header.js 81 | }, 10); 82 | }; 83 | 84 | Meteor.startup(function() { 85 | // make reactive 86 | Tracker.autorun(updateMistMenu); 87 | }); 88 | -------------------------------------------------------------------------------- /app/client/routes.js: -------------------------------------------------------------------------------- 1 | // configure 2 | BlazeLayout.setRoot('body'); 3 | 4 | FlowRouter.notFound = { 5 | action: function() { 6 | BlazeLayout.render('layout_main', { 7 | header: 'layout_header', 8 | main: 'layout_notFound' 9 | }); 10 | } 11 | }; 12 | 13 | // redirect on start to dahsboard on file protocol 14 | if (location.origin === 'file://') { 15 | FlowRouter.wait(); 16 | FlowRouter.initialize({ hashbang: true }); 17 | 18 | Meteor.startup(function() { 19 | FlowRouter.go('dashboard'); 20 | }); 21 | } 22 | 23 | FlowRouter.triggers.enter([ 24 | function() { 25 | EthElements.Modal.hide(); 26 | $(window).scrollTop(0); 27 | }, 28 | updateMistMenu 29 | ]); 30 | 31 | // ROUTES 32 | 33 | /** 34 | The receive route, showing the wallet overview 35 | 36 | @method dashboard 37 | */ 38 | FlowRouter.route('/', { 39 | name: 'dashboard', 40 | action: function(params, queryParams) { 41 | BlazeLayout.render('layout_main', { 42 | header: 'layout_header', 43 | main: 'views_dashboard' 44 | }); 45 | } 46 | }); 47 | 48 | /** 49 | The send route. 50 | 51 | @method send 52 | */ 53 | FlowRouter.route('/send', { 54 | name: 'send', 55 | action: function(params, queryParams) { 56 | BlazeLayout.render('layout_main', { 57 | header: 'layout_header', 58 | main: 'views_send' 59 | }); 60 | } 61 | }); 62 | 63 | /** 64 | The Coins route. 65 | 66 | @method tokens 67 | */ 68 | FlowRouter.route('/tokens', { 69 | name: 'tokens', 70 | action: function(params, queryParams) { 71 | BlazeLayout.render('layout_main', { 72 | header: 'layout_header', 73 | main: 'views_tokens' 74 | }); 75 | } 76 | }); 77 | 78 | /** 79 | The Coins route. 80 | 81 | @method tokens 82 | */ 83 | FlowRouter.route('/contracts', { 84 | name: 'contracts', 85 | action: function(params, queryParams) { 86 | BlazeLayout.render('layout_main', { 87 | header: 'layout_header', 88 | main: 'views_contracts' 89 | }); 90 | } 91 | }); 92 | 93 | /** 94 | The send route. 95 | 96 | @method send 97 | */ 98 | FlowRouter.route('/send/:address', { 99 | name: 'sendTo', 100 | action: function(params, queryParams) { 101 | BlazeLayout.render('layout_main', { 102 | header: 'layout_header', 103 | main: 'views_send' 104 | }); 105 | } 106 | }); 107 | 108 | /** 109 | The send route. 110 | 111 | @method send 112 | */ 113 | FlowRouter.route('/send-from/:from', { 114 | name: 'sendFrom', 115 | action: function(params, queryParams) { 116 | BlazeLayout.render('layout_main', { 117 | header: 'layout_header', 118 | main: 'views_send' 119 | }); 120 | } 121 | }); 122 | 123 | /** 124 | The send route. 125 | 126 | @method send 127 | */ 128 | FlowRouter.route('/send-token/:from/:token', { 129 | name: 'sendToken', 130 | action: function(params, queryParams) { 131 | BlazeLayout.render('layout_main', { 132 | header: 'layout_header', 133 | main: 'views_send' 134 | }); 135 | } 136 | }); 137 | 138 | /** 139 | The send route. 140 | 141 | @method send 142 | */ 143 | FlowRouter.route('/deploy-contract', { 144 | name: 'deployContract', 145 | action: function(params, queryParams) { 146 | BlazeLayout.render('layout_main', { 147 | header: 'layout_header', 148 | main: 'views_send', 149 | data: { 150 | deployContract: true 151 | } 152 | }); 153 | } 154 | }); 155 | 156 | /** 157 | The create account route. 158 | 159 | @method send 160 | */ 161 | FlowRouter.route('/account/new', { 162 | name: 'createAccount', 163 | action: function(params, queryParams) { 164 | BlazeLayout.render('layout_main', { 165 | header: 'layout_header', 166 | main: 'views_account_create' 167 | }); 168 | } 169 | }); 170 | 171 | /** 172 | The account route. 173 | 174 | @method send 175 | */ 176 | FlowRouter.route('/account/:address', { 177 | name: 'account', 178 | action: function(params, queryParams) { 179 | BlazeLayout.render('layout_main', { 180 | header: 'layout_header', 181 | main: 'views_account' 182 | }); 183 | } 184 | }); 185 | -------------------------------------------------------------------------------- /app/client/templates/elements/account.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/client/templates/elements/account.js: -------------------------------------------------------------------------------- 1 | /** 2 | Template Controllers 3 | 4 | @module Templates 5 | */ 6 | 7 | /** 8 | The account template 9 | 10 | @class [template] elements_account 11 | @constructor 12 | */ 13 | 14 | /** 15 | Block required until a transaction is confirmed. 16 | 17 | @property blocksForConfirmation 18 | @type Number 19 | */ 20 | var blocksForConfirmation = 3; 21 | 22 | Template['elements_account'].rendered = function() { 23 | // initiate the geo pattern 24 | var pattern = GeoPattern.generate(this.data.address); 25 | this.$('.account-pattern').css('background-image', pattern.toDataUrl()); 26 | }; 27 | 28 | Template['elements_account'].helpers({ 29 | /** 30 | Get the current account 31 | 32 | @method (account) 33 | */ 34 | account: function() { 35 | return ( 36 | EthAccounts.findOne(this.account) || 37 | Wallets.findOne(this.account) || 38 | CustomContracts.findOne(this.account) 39 | ); 40 | }, 41 | /** 42 | Get all tokens 43 | 44 | @method (tokens) 45 | */ 46 | tokens: function() { 47 | var query = {}; 48 | query['balances.' + this._id] = { $exists: true }; 49 | return Tokens.find(query, { limit: 5, sort: { name: 1 } }); 50 | }, 51 | /** 52 | Get the tokens balance 53 | 54 | @method (formattedTokenBalance) 55 | */ 56 | formattedTokenBalance: function(e) { 57 | var account = Template.parentData(2); 58 | 59 | return this.balances && Number(this.balances[account._id]) > 0 60 | ? Helpers.formatNumberByDecimals( 61 | this.balances[account._id], 62 | this.decimals 63 | ) + 64 | ' ' + 65 | this.symbol 66 | : false; 67 | }, 68 | /** 69 | Get the name 70 | 71 | @method (name) 72 | */ 73 | name: function() { 74 | return this.name || TAPi18n.__('wallet.accounts.defaultName'); 75 | }, 76 | /** 77 | Account was just added. Return true and remove the "new" field. 78 | 79 | @method (new) 80 | */ 81 | new: function() { 82 | if (this.new) { 83 | // remove the "new" field 84 | var id = this._id; 85 | Meteor.setTimeout(function() { 86 | EthAccounts.update(id, { $unset: { new: '' } }); 87 | Wallets.update(id, { $unset: { new: '' } }); 88 | CustomContracts.update(id, { $unset: { new: '' } }); 89 | }, 1000); 90 | 91 | return true; 92 | } 93 | }, 94 | /** 95 | Should the wallet show disabled 96 | 97 | @method (creating) 98 | */ 99 | creating: function() { 100 | var noAddress = !this.address; 101 | var isImported = this.imported; 102 | var belowReorgThreshold = 103 | blocksForConfirmation >= 104 | EthBlocks.latest.number - (this.creationBlock - 1); 105 | var blockNumberCheck = 106 | EthBlocks.latest.number - (this.creationBlock - 1) >= 0; 107 | 108 | return noAddress || isImported || (belowReorgThreshold && blockNumberCheck); 109 | }, 110 | /** 111 | Returns the confirmations 112 | 113 | @method (totalConfirmations) 114 | */ 115 | totalConfirmations: blocksForConfirmation, 116 | /** 117 | Checks whether the transaction is confirmed ot not. 118 | 119 | @method (unConfirmed) 120 | */ 121 | unConfirmed: function() { 122 | if (!this.address || !this.creationBlock || this.createdIdentifier) 123 | return false; 124 | 125 | var currentBlockNumber = EthBlocks.latest.number, 126 | confirmations = currentBlockNumber - (this.creationBlock - 1); 127 | return blocksForConfirmation >= confirmations && confirmations >= 0 128 | ? { 129 | confirmations: confirmations, 130 | percent: (confirmations / blocksForConfirmation) * 100 131 | } 132 | : false; 133 | }, 134 | /** 135 | Displays ENS names with triangles 136 | @method (nameDisplay) 137 | */ 138 | displayName: function() { 139 | return this.ens 140 | ? this.name 141 | .split('.') 142 | .slice(0, -1) 143 | .reverse() 144 | .join(' ▸ ') 145 | : this.name; 146 | }, 147 | /** 148 | Adds class about ens 149 | @method (ensClass) 150 | */ 151 | ensClass: function() { 152 | return this.ens ? 'ens-name' : 'not-ens-name'; 153 | } 154 | }); 155 | 156 | Template['elements_account'].events({ 157 | /** 158 | Field test the speed wallet is rendered 159 | 160 | @event click button.show-data 161 | */ 162 | 'click .wallet-box': function(e) { 163 | console.time('renderAccountPage'); 164 | } 165 | }); 166 | -------------------------------------------------------------------------------- /app/client/templates/elements/accountLink.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/client/templates/elements/accountLink.js: -------------------------------------------------------------------------------- 1 | /** 2 | The account link template 3 | 4 | @class [template] elements_account_link 5 | @constructor 6 | */ 7 | 8 | Template['elements_account_link'].helpers({ 9 | /** 10 | Get the account and return the account or address of "from" or "to" property 11 | 12 | @method (getAccount) 13 | */ 14 | getAccount: function() { 15 | return ( 16 | Helpers.getAccountByAddress(this.address) || { 17 | address: web3.utils.toChecksumAddress(this.address) 18 | } 19 | ); 20 | }, 21 | /** 22 | Adds class about ens 23 | 24 | @method (ensClass) 25 | */ 26 | ensClass: function() { 27 | return this.ens ? 'ens-name' : 'not-ens-name'; 28 | }, 29 | /** 30 | Displays ENS names with triangles 31 | 32 | @method (nameDisplay) 33 | */ 34 | displayName: function() { 35 | return this.ens 36 | ? this.name 37 | .split('.') 38 | .slice(0, -1) 39 | .reverse() 40 | .join(' ▸ ') 41 | : this.name; 42 | }, 43 | /** 44 | Displays ENS names with triangles 45 | 46 | @method (nameDisplay) 47 | */ 48 | tryENS: function() { 49 | var template = Template; 50 | var _this = this; 51 | 52 | Helpers.getENSName(this.address, function(err, name, returnedAddr) { 53 | if (err) { 54 | console.log(err); 55 | return; 56 | } 57 | 58 | if (this.address && returnedAddr) { 59 | if (this.address.toLowerCase() === returnedAddr.toLowerCase()) { 60 | console.log('ens', name, _this, template); 61 | // _this.name = name; 62 | // TemplateVar.set(template, 'ensName', name) 63 | } 64 | } 65 | }); 66 | } 67 | }); 68 | -------------------------------------------------------------------------------- /app/client/templates/elements/balance.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/client/templates/elements/balance.js: -------------------------------------------------------------------------------- 1 | /** 2 | Template Controllers 3 | 4 | @module Templates 5 | */ 6 | 7 | /** 8 | The balance template 9 | 10 | @class [template] elements_balance 11 | @constructor 12 | */ 13 | 14 | Template['elements_balance'].onCreated(function() { 15 | this._intervalId = null; 16 | }); 17 | 18 | Template['elements_balance'].helpers({ 19 | /** 20 | Gets currently selected unit 21 | 22 | @method (convertedBalance) 23 | */ 24 | convertedBalance: function() { 25 | var balance = TemplateVar.get('balance'); 26 | 27 | if (EthTools.getUnit() === 'noether') return 'infinite'; 28 | 29 | if (balance) { 30 | if ( 31 | EthTools.getUnit() === 'usd' || 32 | EthTools.getUnit() === 'eur' || 33 | EthTools.getUnit() === 'gbp' || 34 | EthTools.getUnit() === 'brl' 35 | ) 36 | return EthTools.formatBalance(balance, '0,0.00'); 37 | else if (EthTools.getUnit() === 'ether') 38 | return EthTools.formatBalance( 39 | balance, 40 | this.showAllDecimals ? '0,0.00[0000000000000000]' : '0,0.00' 41 | ); 42 | else if (EthTools.getUnit() === 'finney') 43 | return EthTools.formatBalance( 44 | balance, 45 | this.showAllDecimals ? '0,0.00[00000000000000]' : '0,0.00' 46 | ); 47 | else return EthTools.formatBalance(balance, '0,0.00[000000]'); 48 | } 49 | }, 50 | /** 51 | Get the current balance and count it up/down to the new balance. 52 | 53 | @method (getBalance) 54 | */ 55 | getBalance: function() { 56 | var data = this, 57 | template = Template.instance(), 58 | newBalance = _.isFinite(this.balance) ? this.balance : '0'; 59 | 60 | // transform to BigNumber 61 | newBalance = new BigNumber(newBalance, 10); 62 | 63 | Meteor.clearInterval(template._intervalId); 64 | 65 | template._intervalId = Meteor.setInterval(function() { 66 | var oldBalance = TemplateVar.get(template, 'balance') || 0, 67 | calcBalance = newBalance 68 | .minus(oldBalance) 69 | .dividedBy(10) 70 | .floor(); 71 | 72 | if ( 73 | oldBalance && 74 | !oldBalance.equals(newBalance) && 75 | (calcBalance.greaterThan(10000000000) || 76 | (calcBalance.lessThan(0) && calcBalance.lessThan(-10000000000))) 77 | ) 78 | TemplateVar.set(template, 'balance', oldBalance.plus(calcBalance)); 79 | else { 80 | TemplateVar.set(template, 'balance', newBalance); 81 | Meteor.clearInterval(template._intervalId); 82 | } 83 | }, 1); 84 | } 85 | }); 86 | -------------------------------------------------------------------------------- /app/client/templates/elements/compileContract.html: -------------------------------------------------------------------------------- 1 | 102 | -------------------------------------------------------------------------------- /app/client/templates/elements/createdContractAt.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/client/templates/elements/eventsTable.html: -------------------------------------------------------------------------------- 1 | 26 | 27 | 63 | 64 | -------------------------------------------------------------------------------- /app/client/templates/elements/eventsTable.js: -------------------------------------------------------------------------------- 1 | /** 2 | Template Controllers 3 | 4 | @module Templates 5 | */ 6 | 7 | /** 8 | The transaction row template 9 | 10 | @class [template] elements_event_table 11 | @constructor 12 | */ 13 | 14 | /** 15 | Block required until a transaction is confirmed. 16 | 17 | @property blocksForConfirmation 18 | @type Number 19 | */ 20 | var blocksForConfirmation = ethereumConfig.requiredConfirmations; 21 | 22 | /** 23 | The default limit, of none is given. 24 | 25 | @property defaultLimit 26 | @type Number 27 | */ 28 | var defaultLimit = 10; 29 | 30 | Template['elements_event_table'].onCreated(function() { 31 | this._properties = { 32 | cursor: {} 33 | }; 34 | 35 | TemplateVar.set('limit', this.data.limit || defaultLimit); 36 | }); 37 | 38 | Template['elements_event_table'].helpers({ 39 | /** 40 | Changes the limit of the given cursor 41 | 42 | @method (items) 43 | @return {Object} The items cursor 44 | */ 45 | items: function() { 46 | var template = Template.instance(), 47 | items = [], 48 | ids = this.ids || [], 49 | searchQuery = TemplateVar.get('search'), 50 | limit = TemplateVar.get('limit'), 51 | collection = Events, 52 | selector = { _id: { $in: ids.slice(Number((limit + 50) * -1)) } }; 53 | // slice(limit) prevents loading too many objects at once and slowing the machine 54 | 55 | // if search 56 | if (searchQuery) { 57 | var pattern = new RegExp( 58 | '^.*' + searchQuery.replace(/ +/g, '.*') + '.*$', 59 | 'i' 60 | ); 61 | template._properties.cursor = collection.find(selector, { 62 | sort: { timestamp: -1, blockNumber: -1 } 63 | }); 64 | items = template._properties.cursor.fetch(); 65 | items = _.filter(items, function(item) { 66 | // search from address 67 | if (pattern.test(item.event)) return item; 68 | 69 | // search to address 70 | if (pattern.test(item.address)) return item; 71 | 72 | // search to return values 73 | if ( 74 | _.find(item.args, function(value, name) { 75 | return pattern.test(value) || pattern.test(name); 76 | }) 77 | ) 78 | return item; 79 | 80 | return false; 81 | }); 82 | items = items.slice(0, defaultLimit * 4); 83 | return items; 84 | } else { 85 | template._properties.cursor = collection.find(selector, { 86 | sort: { timestamp: -1, blockNumber: -1 }, 87 | limit: limit 88 | }); 89 | return template._properties.cursor.fetch(); 90 | } 91 | }, 92 | /** 93 | Check if there are more transactions to load. When searching don't show the show more button. 94 | 95 | @method (hasMore) 96 | @return {Boolean} 97 | */ 98 | hasMore: function() { 99 | var template = Template.instance(); 100 | 101 | template._properties.cursor.limit = null; 102 | return ( 103 | !TemplateVar.get('search') && 104 | template._properties.cursor.count() >= TemplateVar.get('limit') 105 | ); 106 | } 107 | }); 108 | 109 | Template['elements_event_table'].events({ 110 | 'click button.show-more': function(e, template) { 111 | var limit = TemplateVar.get('limit'); 112 | TemplateVar.set('limit', limit + (template.data.limit || defaultLimit)); 113 | }, 114 | 'keyup input.filter-transactions': _.debounce(function(e, template) { 115 | if (e.keyCode === 27) e.currentTarget.value = ''; 116 | 117 | TemplateVar.set(template, 'search', e.currentTarget.value); 118 | }, 200) 119 | }); 120 | 121 | /** 122 | The events row template 123 | 124 | @class [template] elements_events_row 125 | @constructor 126 | */ 127 | 128 | Template['elements_events_row'].helpers({ 129 | /** 130 | Returns the from now time, if less than 23 hours 131 | 132 | @method (fromNowTime) 133 | @return {String} 134 | */ 135 | fromNowTime: function() { 136 | Helpers.rerun['10s'].tick(); 137 | 138 | var diff = moment().diff(moment.unix(this.timestamp), 'hours'); 139 | return diff < 23 ? ' ' + moment.unix(this.timestamp).fromNow() : ''; 140 | }, 141 | /** 142 | Returns the confirmations 143 | 144 | @method (totalConfirmations) 145 | */ 146 | totalConfirmations: blocksForConfirmation, 147 | /** 148 | Checks whether the transaction is confirmed ot not. 149 | 150 | @method (unConfirmed) 151 | */ 152 | unConfirmed: function() { 153 | if (!this.blockNumber || !EthBlocks.latest.number) 154 | return { 155 | confirmations: 0, 156 | percent: 0 157 | }; 158 | 159 | var currentBlockNumber = EthBlocks.latest.number + 1, 160 | confirmations = currentBlockNumber - this.blockNumber; 161 | return blocksForConfirmation >= confirmations && confirmations >= 0 162 | ? { 163 | confirmations: confirmations, 164 | percent: confirmations / blocksForConfirmation * 100 165 | } 166 | : false; 167 | }, 168 | /** 169 | Event return values 170 | 171 | @method (returnValues) 172 | */ 173 | returnValues: function() { 174 | if (this.args) { 175 | var returnValues = []; 176 | _.each(this.args, function(value, key) { 177 | // if bignumber 178 | if ((_.isObject(value) || value instanceof BigNumber) && value.toFormat) 179 | value = value.toFormat(0); 180 | 181 | returnValues.push({ 182 | name: key, 183 | value: value 184 | }); 185 | }); 186 | return returnValues; 187 | } else { 188 | return []; 189 | } 190 | } 191 | }); 192 | 193 | Template['elements_events_row'].events({ 194 | /** 195 | Open transaction details on click of the 196 | 197 | @event click tr 198 | */ 199 | 'click tr:not(.pending)': function(e) { 200 | var $element = $(e.target); 201 | if (!$element.is('button') && !$element.is('a')) { 202 | EthElements.Modal.show( 203 | { 204 | template: 'views_modals_eventInfo', 205 | data: { 206 | _id: this._id 207 | } 208 | }, 209 | { 210 | class: 'transaction-info' 211 | } 212 | ); 213 | } 214 | } 215 | }); 216 | -------------------------------------------------------------------------------- /app/client/templates/elements/executeContract.html: -------------------------------------------------------------------------------- 1 | 62 | 63 | 64 | 96 | 97 | 98 | -------------------------------------------------------------------------------- /app/client/templates/elements/inputs/address.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/client/templates/elements/inputs/address.js: -------------------------------------------------------------------------------- 1 | /** 2 | Template Controllers 3 | 4 | @module Templates 5 | */ 6 | 7 | /** 8 | The address input template 9 | 10 | @class [template] elements_input_address 11 | @constructor 12 | */ 13 | 14 | Template['elements_input_address'].helpers({ 15 | /** 16 | Add the attributes and merge the current context 17 | 18 | @method (attributes) 19 | @return {String} 20 | */ 21 | attributes: function() { 22 | var attr = _.clone(this); 23 | attr.class = this.class ? this.class + ' abi-input' : 'abi-input'; 24 | attr.placeholder = this.placeholder || '0x123456...'; 25 | attr.value = this.value; 26 | return attr; 27 | } 28 | }); 29 | -------------------------------------------------------------------------------- /app/client/templates/elements/inputs/bool.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/client/templates/elements/inputs/bytes.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/client/templates/elements/inputs/int.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/client/templates/elements/inputs/json.html: -------------------------------------------------------------------------------- 1 | 7 | -------------------------------------------------------------------------------- /app/client/templates/elements/inputs/string.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/client/templates/elements/inputs/uint.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/client/templates/elements/newAccountButton.html: -------------------------------------------------------------------------------- 1 | 9 | -------------------------------------------------------------------------------- /app/client/templates/elements/selectableUnit.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/client/templates/elements/selectableUnit.js: -------------------------------------------------------------------------------- 1 | /** 2 | Template Controllers 3 | 4 | @module Templates 5 | */ 6 | 7 | /** 8 | The balance template 9 | 10 | @class [template] elements_balance 11 | @constructor 12 | */ 13 | 14 | /** 15 | The available units 16 | 17 | @property selectableUnits 18 | */ 19 | selectableUnits = [ 20 | { 21 | text: 'ETHER', 22 | value: 'ether' 23 | }, 24 | { 25 | text: 'FINNEY', //(µΞ) 26 | value: 'finney' 27 | }, 28 | { 29 | text: 'BTC', 30 | value: 'btc' 31 | }, 32 | { 33 | text: 'USD', 34 | value: 'usd' 35 | }, 36 | { 37 | text: 'EUR', 38 | value: 'eur' 39 | }, 40 | { 41 | text: 'GBP', 42 | value: 'gbp' 43 | }, 44 | { 45 | text: 'BRL', 46 | value: 'brl' 47 | } 48 | ]; 49 | 50 | // Aprils fool 51 | if (moment().format('MM-DD') == '04-01') { 52 | selectableUnits.push( 53 | { text: 'SZABO', value: 'szabo' }, 54 | { text: 'SHANNON', value: 'shannon' }, 55 | { text: 'LOVELACE', value: 'lovelace' }, 56 | { text: 'BABBAGE', value: 'babbage' }, 57 | { text: 'WEI', value: 'wei' }, 58 | { text: 'NOETHER', value: 'noether' } 59 | ); 60 | 61 | // Claude's Birthday 62 | } else if (moment().format('MM-DD') == '04-30') { 63 | selectableUnits.push({ text: 'SHANNON', value: 'shannon' }); 64 | // Ada's Birthday 65 | } else if (moment().format('MM-DD') == '12-10') { 66 | selectableUnits.push({ text: 'LOVELACE', value: 'lovelace' }); 67 | // Charles's Birthday 68 | } else if (moment().format('MM-DD') == '12-26') { 69 | selectableUnits.push({ text: 'BABBAGE', value: 'babbage' }); 70 | } 71 | 72 | Template['elements_selectableUnit'].helpers({ 73 | /** 74 | Gets currently selected unit 75 | 76 | @method (selectedUnit) 77 | */ 78 | selectedUnit: function() { 79 | var unit = _.find(selectableUnits, function(unit) { 80 | return unit.value === EthTools.getUnit(); 81 | }); 82 | 83 | if (unit) return unit.value; 84 | }, 85 | /** 86 | Return the selectable units 87 | 88 | @method (selectedUnit) 89 | */ 90 | units: function() { 91 | return selectableUnits; 92 | }, 93 | /** 94 | Can select units 95 | 96 | @method (selectedUnit) 97 | */ 98 | selectable: function() { 99 | return Session.get('network') == 'main'; 100 | } 101 | }); 102 | 103 | Template['elements_selectableUnit'].events({ 104 | /** 105 | Select the current section, based on the radio inputs value. 106 | 107 | @event change .inline-form 108 | */ 109 | 'change .inline-form': function(e, template, value) { 110 | EthTools.setUnit(value); 111 | } 112 | }); 113 | -------------------------------------------------------------------------------- /app/client/templates/elements/tokenBox.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/client/templates/elements/tokenBox.js: -------------------------------------------------------------------------------- 1 | /** 2 | The template show the token in the .wallet-box 3 | 4 | @class [template] elements_tokenBox 5 | @constructor 6 | */ 7 | 8 | Template['elements_tokenBox'].helpers({ 9 | /** 10 | Formats the total balance 11 | 12 | @method (formattedTotalBalance) 13 | */ 14 | formattedTotalBalance: function(e) { 15 | // Get wallets and accounts, but not contracts 16 | var walletsAndAccounts = _.map( 17 | Wallets.find() 18 | .fetch() 19 | .concat(EthAccounts.find().fetch()), 20 | function(account) { 21 | if (!account.disabled) return account._id; 22 | } 23 | ); 24 | // check the total balance of these accounts only 25 | var totalBalance = new BigNumber(0); 26 | _.each(this.balances, function(balance, id) { 27 | if (walletsAndAccounts.indexOf(id) >= 0) 28 | totalBalance = totalBalance.plus(new BigNumber(balance, 10)); 29 | }); 30 | 31 | return Helpers.formatNumberByDecimals(totalBalance, this.decimals); 32 | }, 33 | /** 34 | Generates the geo pattern for the background 35 | 36 | @method (geoPattern) 37 | */ 38 | geoPattern: function() { 39 | var pattern = GeoPattern.generate(this.address, { color: '#CCC6C6' }); 40 | return pattern.toDataUrl(); 41 | } 42 | }); 43 | 44 | Template['elements_tokenBox'].events({ 45 | /** 46 | Click Delete Token 47 | 48 | @event click a.create.account 49 | */ 50 | 'click .delete-token': function(e) { 51 | var token = this; 52 | e.preventDefault(); 53 | e.stopImmediatePropagation(); 54 | 55 | EthElements.Modal.question({ 56 | text: new Spacebars.SafeString( 57 | TAPi18n.__('wallet.tokens.deleteToken', { token: token.name }) 58 | ), // could be vulnerable as token name is not HTML purified 59 | ok: function() { 60 | Tokens.remove(token._id); 61 | }, 62 | cancel: true 63 | }); 64 | } 65 | }); 66 | -------------------------------------------------------------------------------- /app/client/templates/elements/vulnerabilities.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/client/templates/elements/vulnerabilities.js: -------------------------------------------------------------------------------- 1 | /** 2 | Template Controllers 3 | 4 | @module Templates 5 | */ 6 | 7 | Template['elements_vulnerabilities_txorigin'].helpers({ 8 | /** 9 | Upgrade parameters for the wallet 10 | 11 | @method (upgradeParams) 12 | */ 13 | upgradeParams: function() { 14 | var params = 'walletId=' + this._id + '&name=' + this.name; 15 | if (this.dailyLimit) params += '&dailyLimit=' + this.dailyLimit; 16 | if (this.requiredSignatures) 17 | params += '&requiredSignatures=' + this.requiredSignatures; 18 | if (this.owners) { 19 | params += '&ownersNum=' + this.owners.length; 20 | params += '&owners=' + this.owners.join(','); 21 | } 22 | 23 | return params; 24 | }, 25 | /** 26 | Return the wallet address if its an account 27 | 28 | @method (walletAddress) 29 | */ 30 | walletAddress: function() { 31 | var account = this; 32 | // if account, get the first vulnerable wallet for that account 33 | var wallets = _.map( 34 | Wallets.find({ vulnerabilities: { $exists: true } }).fetch(), 35 | function(wal) { 36 | return !!_.find(wal.vulnerabilities || [], function(vul) { 37 | return vul; 38 | }) 39 | ? wal 40 | : false; 41 | } 42 | ); 43 | var wallet = _.find(wallets, function(wal) { 44 | return _.contains(wal.owners, account.address); 45 | }); 46 | 47 | return wallet ? wallet.address : ''; 48 | } 49 | }); 50 | -------------------------------------------------------------------------------- /app/client/templates/layout/header.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/client/templates/layout/header.js: -------------------------------------------------------------------------------- 1 | /** 2 | Template Controllers 3 | 4 | @module Templates 5 | */ 6 | 7 | /** 8 | The header template 9 | 10 | @class [template] layout_header 11 | @constructor 12 | */ 13 | 14 | Template['layout_header'].onCreated(function() { 15 | var template = this; 16 | }); 17 | 18 | Template['layout_header'].helpers({ 19 | /** 20 | Returns the correct url for the send to route 21 | 22 | @method (goToSend) 23 | @return {String} 24 | */ 25 | goToSend: function() { 26 | FlowRouter.watchPathChange(); 27 | var address = web3.utils.toChecksumAddress(FlowRouter.getParam('address')); 28 | var accounts = EthAccounts.find({}).fetch(); 29 | 30 | // For some reason the path /send/ doesn't show tokens anymore 31 | return address 32 | ? FlowRouter.path('sendFrom', { from: address }) 33 | : FlowRouter.path('sendFrom', { 34 | from: accounts[0] ? accounts[0].address : null 35 | }); 36 | }, 37 | /** 38 | Calculates the total balance of all accounts + wallets. 39 | 40 | @method (totalBalance) 41 | @return {String} 42 | */ 43 | totalBalance: function() { 44 | var accounts = EthAccounts.find({}).fetch(); 45 | var wallets = Wallets.find({ 46 | owners: { $in: _.pluck(accounts, 'address') } 47 | }).fetch(); 48 | 49 | var balance = _.reduce( 50 | _.pluck(_.union(accounts, wallets), 'balance'), 51 | function(memo, num) { 52 | return memo + Number(num); 53 | }, 54 | 0 55 | ); 56 | 57 | updateMistBadge(); 58 | 59 | return balance; 60 | }, 61 | /** 62 | Formats the last block number 63 | 64 | @method (formattedBlockNumber) 65 | @return {String} 66 | */ 67 | formattedBlockNumber: function() { 68 | return EthBlocks.latest.number > 0 69 | ? numeral(EthBlocks.latest.number).format('0,0') 70 | : '--'; 71 | }, 72 | /** 73 | Gets the time since the last block 74 | 75 | @method (timeSinceBlock) 76 | */ 77 | timeSinceBlock: function() { 78 | if ( 79 | EthBlocks.latest.timestamp == 0 || 80 | typeof EthBlocks.latest.timestamp == 'undefined' 81 | ) 82 | return false; 83 | 84 | var timeSince = moment(EthBlocks.latest.timestamp, 'X'); 85 | var now = moment(); 86 | var diff = now.diff(timeSince, 'seconds'); 87 | 88 | if (diff > 60 * 5) { 89 | Helpers.rerun['10s'].tick(); 90 | return '' + timeSince.fromNow(true) + ''; 91 | } else if (diff > 60) { 92 | Helpers.rerun['10s'].tick(); 93 | return timeSince.fromNow(true); 94 | } else if (diff < 2) { 95 | Helpers.rerun['1s'].tick(); 96 | return ''; 97 | } else { 98 | Helpers.rerun['1s'].tick(); 99 | return diff + 's '; 100 | } 101 | }, 102 | /** 103 | Formats the time since the last block 104 | 105 | @method (timeSinceBlockText) 106 | */ 107 | timeSinceBlockText: function() { 108 | if ( 109 | EthBlocks.latest.timestamp == 0 || 110 | typeof EthBlocks.latest.timestamp == 'undefined' 111 | ) 112 | return TAPi18n.__('wallet.app.texts.waitingForBlocks'); 113 | 114 | var timeSince = moment(EthBlocks.latest.timestamp, 'X'); 115 | var now = moment(); 116 | var diff = now.diff(timeSince, 'seconds'); 117 | 118 | if (diff > 60 * 5) { 119 | Helpers.rerun['10s'].tick(); 120 | return ( 121 | '' + 122 | TAPi18n.__('wallet.app.texts.timeSinceBlock') + 123 | '' 124 | ); 125 | } else if (diff > 60) { 126 | Helpers.rerun['10s'].tick(); 127 | return TAPi18n.__('wallet.app.texts.timeSinceBlock'); 128 | } else if (diff < 2) { 129 | Helpers.rerun['1s'].tick(); 130 | return ( 131 | '' + 132 | TAPi18n.__('wallet.app.texts.blockReceived') + 133 | '' 134 | ); 135 | } else { 136 | Helpers.rerun['1s'].tick(); 137 | return TAPi18n.__('wallet.app.texts.timeSinceBlock'); 138 | } 139 | } 140 | }); 141 | -------------------------------------------------------------------------------- /app/client/templates/layout/main.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/client/templates/layout/notFound.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/client/templates/views/account_create.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/client/templates/views/contracts.html: -------------------------------------------------------------------------------- 1 | 72 | -------------------------------------------------------------------------------- /app/client/templates/views/dashboard.html: -------------------------------------------------------------------------------- 1 | 85 | -------------------------------------------------------------------------------- /app/client/templates/views/dashboard.js: -------------------------------------------------------------------------------- 1 | /** 2 | Template Controllers 3 | 4 | @module Templates 5 | */ 6 | 7 | /** 8 | The dashboard template 9 | 10 | @class [template] views_dashboard 11 | @constructor 12 | */ 13 | 14 | Template['views_dashboard'].helpers({ 15 | /** 16 | Get all current wallets 17 | 18 | @method (wallets) 19 | */ 20 | wallets: function() { 21 | var wallets = Wallets.find( 22 | { $or: [{ disabled: { $exists: false } }, { disabled: false }] }, 23 | { sort: { creationBlock: 1 } } 24 | ).fetch(); 25 | 26 | // sort wallets by balance 27 | wallets.sort(Helpers.sortByBalance); 28 | 29 | return wallets; 30 | }, 31 | /** 32 | Get all current accounts 33 | 34 | @method (accounts) 35 | */ 36 | accounts: function() { 37 | // balance need to be present, to show only full inserted accounts (not ones added by mist.requestAccount) 38 | var accounts = EthAccounts.find( 39 | { name: { $exists: true } }, 40 | { sort: { name: 1 } } 41 | ).fetch(); 42 | 43 | accounts.sort(Helpers.sortByBalance); 44 | 45 | return accounts; 46 | }, 47 | /** 48 | Are there any accounts? 49 | 50 | @method (hasAccounts) 51 | */ 52 | hasAccounts: function() { 53 | return EthAccounts.find().count() > 0; 54 | }, 55 | /** 56 | Are there any accounts? 57 | 58 | @method (hasAccounts) 59 | */ 60 | hasMinimumBalance: function() { 61 | var enoughBalance = false; 62 | _.each(_.pluck(EthAccounts.find({}).fetch(), 'balance'), function(bal) { 63 | if (new BigNumber(bal, '10').gt(1000000000000000)) enoughBalance = true; 64 | }); 65 | 66 | return enoughBalance; 67 | }, 68 | /** 69 | Get all transactions 70 | 71 | @method (allTransactions) 72 | */ 73 | allTransactions: function() { 74 | return Transactions.find({}, { sort: { timestamp: -1 } }).count(); 75 | }, 76 | /** 77 | Returns an array of pending confirmations, from all accounts 78 | 79 | @method (pendingConfirmations) 80 | @return {Array} 81 | */ 82 | pendingConfirmations: function() { 83 | return _.pluck( 84 | PendingConfirmations.find({ 85 | operation: { $exists: true }, 86 | confirmedOwners: { $ne: [] } 87 | }).fetch(), 88 | '_id' 89 | ); 90 | } 91 | }); 92 | 93 | Template['views_dashboard'].events({ 94 | /** 95 | Request to create an account in mist 96 | 97 | @event click .create.account 98 | */ 99 | 'click .create.account': function(e) { 100 | e.preventDefault(); 101 | 102 | mist.requestAccount(function(e, accounts) { 103 | if (!e) { 104 | if (!_.isArray(accounts)) { 105 | accounts = [accounts]; 106 | } 107 | accounts.forEach(function(account) { 108 | account = account.toLowerCase(); 109 | EthAccounts.upsert( 110 | { address: account }, 111 | { 112 | $set: { 113 | address: account, 114 | new: true 115 | } 116 | } 117 | ); 118 | }); 119 | } 120 | }); 121 | } 122 | }); 123 | -------------------------------------------------------------------------------- /app/client/templates/views/modals/addCustomContract.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/client/templates/views/modals/addCustomContract.js: -------------------------------------------------------------------------------- 1 | /** 2 | Modal to add watch contracts. 3 | 4 | @class [template] views_modals_addCustomContract 5 | @constructor 6 | */ 7 | 8 | Template['views_modals_addCustomContract'].onRendered(function() { 9 | this.$('input[name="address"]').focus(); 10 | }); 11 | 12 | Template['views_modals_addCustomContract'].events({ 13 | /** 14 | Change Address 15 | 16 | @event change input[name="address"], input input[name="address"] 17 | */ 18 | 'blur input[name="address"]': function(e, template) { 19 | var address = e.currentTarget.value; 20 | 21 | Helpers.getENSName(address, function(err, name, returnedAddr) { 22 | if (address.toLowerCase() == returnedAddr) 23 | template 24 | .$('input.name') 25 | .attr('disabled', 'true') 26 | .val(name) 27 | .change(); 28 | }); 29 | } 30 | }); 31 | -------------------------------------------------------------------------------- /app/client/templates/views/modals/addToken.html: -------------------------------------------------------------------------------- 1 | 28 | -------------------------------------------------------------------------------- /app/client/templates/views/modals/addToken.js: -------------------------------------------------------------------------------- 1 | /** 2 | Modal to add token. 3 | 4 | @class [template] views_modals_addToken 5 | @constructor 6 | */ 7 | 8 | Template['views_modals_addToken'].onRendered(function() { 9 | if (!this.data || !this.data.address) this.$('input[name="address"]').focus(); 10 | }); 11 | 12 | Template['views_modals_addToken'].helpers({ 13 | /** 14 | Returns the token for the preview token box 15 | 16 | @method (previewToken) 17 | */ 18 | previewToken: function() { 19 | var token = _.clone(this || {}); 20 | 21 | if (TemplateVar.get('address')) token.address = TemplateVar.get('address'); 22 | if (TemplateVar.get('decimals')) 23 | token.decimals = TemplateVar.get('decimals'); 24 | if (TemplateVar.get('symbol')) token.symbol = TemplateVar.get('symbol'); 25 | if (TemplateVar.get('name')) token.name = TemplateVar.get('name'); 26 | 27 | return token; 28 | } 29 | }); 30 | 31 | Template['views_modals_addToken'].events({ 32 | /** 33 | Change Decimals 34 | 35 | @event change .decimals, input .decimals 36 | */ 37 | 'change .decimals, input .decimals': function(e, template) { 38 | TemplateVar.set('decimals', e.target.value); 39 | }, 40 | /** 41 | Change Symbol 42 | 43 | @event change input.symbol, input input.symbol 44 | */ 45 | 'change input.symbol, input input.symbol': function(e, template) { 46 | TemplateVar.set('symbol', e.target.value); 47 | }, 48 | /** 49 | Change Name 50 | 51 | @event change input.name, input input.name 52 | */ 53 | 'change input.name, input input.name': function(e, template) { 54 | TemplateVar.set('name', e.target.value); 55 | }, 56 | /** 57 | Change Address 58 | 59 | @event change input[name="address"], input input[name="address"] 60 | */ 61 | 'change input[name="address"], input input[name="address"], blur input[name="address"]': function( 62 | e, 63 | template 64 | ) { 65 | var tokenAddress = TemplateVar.getFrom('.token-address', 'value'); 66 | 67 | var l = e.currentTarget.value.length; 68 | if (!tokenAddress && l > 2 && l < 6) { 69 | e.currentTarget.value += '.thetoken.eth'; 70 | e.currentTarget.setSelectionRange(l, l + 13); 71 | } 72 | 73 | if ( 74 | !tokenAddress || 75 | (template.data && 76 | template.data.address && 77 | template.data.address == tokenAddress) 78 | ) 79 | return; 80 | 81 | TemplateVar.set('address', tokenAddress); 82 | 83 | // initiate the geo pattern 84 | var pattern = GeoPattern.generate(tokenAddress, { color: '#CCC6C6' }); 85 | $('.example.wallet-box.tokens').css( 86 | 'background-image', 87 | pattern.toDataUrl() 88 | ); 89 | 90 | // check if the token has information about itself asynchrounously 91 | var tokenInstance = TokenContract; 92 | tokenInstance.options.address = tokenAddress; 93 | 94 | tokenInstance.methods 95 | .symbol() 96 | .call() 97 | .then(function(symbol) { 98 | template 99 | .$('input.symbol') 100 | .val(symbol) 101 | .change(); 102 | }); 103 | 104 | tokenInstance.methods 105 | .name() 106 | .call() 107 | .then(function(name) { 108 | template 109 | .$('input.name') 110 | .val(name) 111 | .change(); 112 | }); 113 | 114 | tokenInstance.methods 115 | .decimals() 116 | .call() 117 | .then(function(decimals) { 118 | template 119 | .$('input.decimals') 120 | .val(decimals) 121 | .change(); 122 | }); 123 | }, 124 | /** 125 | Prevent the example from being clicked 126 | 127 | @event click .example.wallet-box.tokens 128 | */ 129 | 'click .example.wallet-box.tokens': function(e) { 130 | e.preventDefault(); 131 | } 132 | }); 133 | -------------------------------------------------------------------------------- /app/client/templates/views/modals/backupContractAddress.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/client/templates/views/modals/eventInfo.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/client/templates/views/modals/eventInfo.js: -------------------------------------------------------------------------------- 1 | /** 2 | Template Controllers 3 | 4 | @module Templates 5 | */ 6 | 7 | /** 8 | The event info template 9 | 10 | @class [template] views_modals_eventsInfo 11 | @constructor 12 | */ 13 | 14 | Template['views_modals_eventInfo'].helpers({ 15 | /** 16 | Returns the current event 17 | 18 | @method (event) 19 | @return {Object} the current event 20 | */ 21 | event: function() { 22 | return Events.findOne(this._id); 23 | }, 24 | /** 25 | Calculates the confirmations of this tx 26 | 27 | @method (confirmations) 28 | @return {Number} the number of confirmations 29 | */ 30 | confirmations: function() { 31 | return EthBlocks.latest && this.blockNumber 32 | ? EthBlocks.latest.number + 1 - this.blockNumber 33 | : 0; 34 | }, 35 | /** 36 | Event return values 37 | 38 | @method (returnValues) 39 | */ 40 | returnValues: function() { 41 | if (this.args) { 42 | var returnValues = []; 43 | _.each(this.args, function(value, key) { 44 | // if bignumber 45 | if ((_.isObject(value) || value instanceof BigNumber) && value.toFormat) 46 | value = value.toFormat(0); 47 | 48 | returnValues.push({ 49 | name: key, 50 | value: value 51 | }); 52 | }); 53 | return returnValues; 54 | } else { 55 | return []; 56 | } 57 | } 58 | }); 59 | -------------------------------------------------------------------------------- /app/client/templates/views/modals/interface.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/client/templates/views/modals/interface.js: -------------------------------------------------------------------------------- 1 | /** 2 | The template to display the ABI. 3 | 4 | @class [template] views_modals_interface 5 | @constructor 6 | */ 7 | 8 | Template['views_modals_interface'].helpers({ 9 | /** 10 | Return the ABI in string formart 11 | 12 | @method (jsonInterface) 13 | */ 14 | jsonInterface: function() { 15 | return JSON.stringify(this.jsonInterface, null, 2).replace(/\s+/g, ' '); 16 | } 17 | }); 18 | 19 | Template['views_modals_interface'].events({ 20 | 'focus textarea': function(e, template) { 21 | Tracker.afterFlush(function() { 22 | template.$('textarea').select(); 23 | }); 24 | } 25 | }); 26 | -------------------------------------------------------------------------------- /app/client/templates/views/modals/loading.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/client/templates/views/modals/qrCode.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/client/templates/views/modals/qrCode.js: -------------------------------------------------------------------------------- 1 | /** 2 | The template to display qrCode. 3 | 4 | @class [template] views_modals_qrCode 5 | @constructor 6 | */ 7 | 8 | Template['views_modals_qrCode'].onRendered(function(){ 9 | if(this.data && this.data.address) { 10 | var qrcodesvg = new Qrcodesvg("ethereum:" + this.data.address, 'qrcode', 150, {"ecclevel" : 1}); 11 | qrcodesvg.draw({"method": "classic", "fill-colors":["#555","#555","#666"]}, {"stroke-width":1}); 12 | } 13 | }); 14 | -------------------------------------------------------------------------------- /app/client/templates/views/modals/selectAccount.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/client/templates/views/modals/selectAccount.js: -------------------------------------------------------------------------------- 1 | /** 2 | Template Controllers 3 | 4 | @module Templates 5 | */ 6 | 7 | /** 8 | The select account modal template 9 | 10 | @class [template] views_modals_selectAccount 11 | @constructor 12 | */ 13 | 14 | Template['views_modals_selectAccount'].helpers({ 15 | /** 16 | Return the accounts 17 | 18 | @method (accounts) 19 | */ 20 | accounts: function() { 21 | if (_.isString(this.accounts[0])) 22 | return Helpers.getAccounts({ address: { $in: this.accounts } }); 23 | else return this.accounts; 24 | } 25 | }); 26 | 27 | Template['views_modals_selectAccount'].events({ 28 | /** 29 | Select an account 30 | 31 | @event click .dapp-account-list button 32 | */ 33 | 'click .dapp-account-list button': function(e, template) { 34 | template.data.callback(this.address); 35 | EthElements.Modal.hide(); 36 | } 37 | }); 38 | -------------------------------------------------------------------------------- /app/client/templates/views/modals/sendTransactionInfo.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/client/templates/views/modals/sendTransactionInfo.js: -------------------------------------------------------------------------------- 1 | /** 2 | Template Controllers 3 | 4 | @module Templates 5 | */ 6 | 7 | /** 8 | The send transaction info template 9 | 10 | @class [template] views_modals_sendTransactionInfo 11 | @constructor 12 | */ 13 | 14 | // Set basic variables 15 | Template['views_modals_sendTransactionInfo'].helpers({ 16 | /** 17 | Calculates the fee used for this transaction in ether 18 | 19 | @method (estimatedFee) 20 | */ 21 | estimatedFee: function() { 22 | if (this.estimatedGas && this.gasPrice) 23 | return EthTools.formatBalance( 24 | new BigNumber(this.estimatedGas, 10).times( 25 | new BigNumber(this.gasPrice, 10) 26 | ), 27 | '0,0.0[0000000] unit', 28 | 'ether' 29 | ); 30 | } 31 | }); 32 | -------------------------------------------------------------------------------- /app/client/templates/views/modals/transactionInfo.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/client/templates/views/modals/transactionInfo.js: -------------------------------------------------------------------------------- 1 | /** 2 | Template Controllers 3 | 4 | @module Templates 5 | */ 6 | 7 | /** 8 | The transaction info template 9 | 10 | @class [template] views_modals_transactionInfo 11 | @constructor 12 | */ 13 | 14 | Template['views_modals_transactionInfo'].helpers({ 15 | /** 16 | Returns the current transaction 17 | 18 | @method (transaction) 19 | @return {Object} the current transaction 20 | */ 21 | transaction: function() { 22 | return Transactions.findOne(this._id); 23 | }, 24 | /** 25 | Calculates the confirmations of this tx 26 | 27 | @method (confirmations) 28 | @return {Number} the number of confirmations 29 | */ 30 | confirmations: function() { 31 | return EthBlocks.latest && this.blockNumber 32 | ? EthBlocks.latest.number + 1 - this.blockNumber 33 | : 0; 34 | }, 35 | /** 36 | Token value 37 | 38 | @method (tokenValue) 39 | */ 40 | tokenValue: function() { 41 | var token = Tokens.findOne(this.tokenId); 42 | 43 | return token 44 | ? Helpers.formatNumberByDecimals(this.value, token.decimals) + 45 | ' ' + 46 | token.symbol 47 | : this.value; 48 | }, 49 | /** 50 | Gas Price per million 51 | 52 | @method (gasPricePerMillion) 53 | */ 54 | gasPricePerMillion: function() { 55 | return this.gasPrice * 1000000; 56 | } 57 | }); 58 | -------------------------------------------------------------------------------- /app/client/window.js: -------------------------------------------------------------------------------- 1 | $(window).on('blur', function(e) { 2 | $('body').addClass('app-blur'); 3 | }); 4 | $(window).on('focus', function(e) { 5 | $('body').removeClass('app-blur'); 6 | }); 7 | 8 | // add class to the header when scrolling 9 | $(window).on('scroll', function() { 10 | var scrollPosition = $(window).scrollTop(); 11 | 12 | if (scrollPosition > 150) { 13 | $('.dapp-sticky-bar').addClass('sticky'); 14 | $('.dapp-header').addClass('dapp-small'); 15 | } else if (scrollPosition > 48) { 16 | $('.dapp-header').addClass('dapp-small'); 17 | $('.dapp-sticky-bar').removeClass('sticky'); 18 | } else { 19 | $('.dapp-header').removeClass('dapp-small'); 20 | $('.dapp-sticky-bar').removeClass('sticky'); 21 | } 22 | }); 23 | -------------------------------------------------------------------------------- /app/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 | "sending": "Enviant...", 17 | "create": "Crea", 18 | "select": "Selecciona", 19 | "tryToReconnect": "Intenta reconnectar" 20 | }, 21 | "commonWords": { 22 | "you": "Tu", 23 | "send": "Envia", 24 | "or": "o", 25 | "of": "de", 26 | "with": "amb", 27 | "and": "i", 28 | "on": "a", 29 | "per": "per", 30 | "total":"total", 31 | "at": "a" 32 | } 33 | } -------------------------------------------------------------------------------- /app/i18n/app.de.i18n.json: -------------------------------------------------------------------------------- 1 | { 2 | "app": { 3 | "loading": "Laden...", 4 | "offline": "Keine Internetverbindung", 5 | "logginIn": "Einloggen..." 6 | }, 7 | "error": { 8 | "insufficientRights": "Sie sind für diese Aktion nicht berechtigt." 9 | }, 10 | "buttons": { 11 | "ok": "OK", 12 | "cancel": "Abbrechen", 13 | "save": "Speichern", 14 | "edit": "Bearbeiten", 15 | "send": "Senden", 16 | "sending": "Sende...", 17 | "create": "Erstellen", 18 | "select": "Auswählen", 19 | "tryToReconnect": "Verbindung wird wiederhergestellt" 20 | }, 21 | "commonWords": { 22 | "you": "Sie", 23 | "send": "Senden", 24 | "or": "oder", 25 | "of": "von", 26 | "with": "mit", 27 | "and": "und", 28 | "on": "auf", 29 | "per": "pro", 30 | "total":"gesamt", 31 | "at": "bei" 32 | } 33 | } -------------------------------------------------------------------------------- /app/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 | "sending": "Sending...", 17 | "create": "Create", 18 | "select": "Select", 19 | "tryToReconnect": "Try to reconnect" 20 | }, 21 | "commonWords": { 22 | "you": "You", 23 | "send": "Send", 24 | "or": "or", 25 | "of": "of", 26 | "with": "with", 27 | "and": "and", 28 | "on": "on", 29 | "per": "per", 30 | "total":"total", 31 | "at": "at" 32 | } 33 | } -------------------------------------------------------------------------------- /app/i18n/app.es.i18n.json: -------------------------------------------------------------------------------- 1 | { 2 | "app": { 3 | "loading": "Cargando...", 4 | "offline": "Imposible conectarse, ¿Está fuera de línea?", 5 | "logginIn": "Autenticando..." 6 | }, 7 | "error": { 8 | "insufficientRights": "No tiene suficientes privilegios para esta acción." 9 | }, 10 | "buttons": { 11 | "ok": "Aceptar", 12 | "cancel": "Cancelar", 13 | "save": "Guardar", 14 | "edit": "editar", 15 | "send": "Enviar", 16 | "sending": "Enviando...", 17 | "create": "Crear", 18 | "select": "Seleccionar", 19 | "tryToReconnect": "Intentar reconección" 20 | }, 21 | "commonWords": { 22 | "you": "Usted", 23 | "send": "Enviar", 24 | "or": "o", 25 | "of": "de", 26 | "with": "con", 27 | "and": "y", 28 | "on": "en", 29 | "per": "por", 30 | "total":"total", 31 | "at": "en" 32 | } 33 | } -------------------------------------------------------------------------------- /app/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 | "sending": "ارسال...", 17 | "create": "ایجاد", 18 | "select": "انتخاب", 19 | "tryToReconnect": "تلاش برای وصل مجدد" 20 | }, 21 | "commonWords": { 22 | "you": "شما", 23 | "send": "ارسال", 24 | "or": "یا", 25 | "of": "از", 26 | "with": "با", 27 | "and": "و", 28 | "on": "بر", 29 | "per": "در هر", 30 | "total":"مجموع", 31 | "at": "در" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /app/i18n/app.fr.i18n.json: -------------------------------------------------------------------------------- 1 | { 2 | "app": { 3 | "loading": "Chargement...", 4 | "offline": "Impossible de se connecter, êtes-vous hors-ligne ?", 5 | "logginIn": "Connexion..." 6 | }, 7 | "error": { 8 | "insufficientRights": "Vous n'avez pas suffisamment de droits pour cette action." 9 | }, 10 | "buttons": { 11 | "ok": "OK", 12 | "cancel": "Annuler", 13 | "save": "Sauvegarder", 14 | "edit": "Editer", 15 | "send": "Envoyer", 16 | "sending": "Envoi...", 17 | "create": "Créer", 18 | "select": "Sélectionner", 19 | "tryToReconnect": "Essayer de se reconnecter" 20 | }, 21 | "commonWords": { 22 | "you": "Vous", 23 | "send": "Envoyer", 24 | "or": "ou", 25 | "of": "de", 26 | "with": "avec", 27 | "and": "et", 28 | "on": "sur", 29 | "per": "par", 30 | "total":"total", 31 | "at": "à" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /app/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 | "sending": "送っています...", 17 | "create": "作成", 18 | "select": "選択", 19 | "tryToReconnect": "再接続しようとしています" 20 | }, 21 | "commonWords": { 22 | "you": "You", 23 | "send": "Send", 24 | "or": "or", 25 | "of": "of", 26 | "with": "with", 27 | "and": "and", 28 | "on": "on", 29 | "per": "per", 30 | "total":"合計", 31 | "at": "at" 32 | } 33 | } -------------------------------------------------------------------------------- /app/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 | "sending": "전송 중...", 17 | "create": "만들기", 18 | "select": "선택", 19 | "tryToReconnect": "재연결 시도" 20 | }, 21 | "commonWords": { 22 | "you": "당신", 23 | "send": "보내기", 24 | "or": "또는", 25 | "of": "의", 26 | "with": "와", 27 | "and": "그리고", 28 | "on": "위에", 29 | "per": "당", 30 | "total":"총", 31 | "at": "에" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /app/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 | "sending": "Enviando...", 17 | "create": "Criar", 18 | "select": "Selecionar", 19 | "tryToReconnect": "Tentando reconectar" 20 | }, 21 | "commonWords": { 22 | "you": "Você", 23 | "send": "Enviar", 24 | "or": "ou", 25 | "of": "de", 26 | "with": "com", 27 | "and": "e", 28 | "on": "em", 29 | "per": "por", 30 | "total":"total", 31 | "at": "em" 32 | } 33 | } -------------------------------------------------------------------------------- /app/i18n/app.ro.i18n.json: -------------------------------------------------------------------------------- 1 | { 2 | "app": { 3 | "loading": "Incarcare...", 4 | "offline": "Nu ma pot conecta la retea. Esti legat la internet?", 5 | "logginIn": "Logare in curs..." 6 | }, 7 | "error": { 8 | "insufficientRights": "Nu ai destule drepturi pentru aceasta actiune." 9 | }, 10 | "buttons": { 11 | "ok": "OK", 12 | "cancel": "Anuleaza", 13 | "save": "Salveaza", 14 | "edit": "editeaza", 15 | "send": "Trimite", 16 | "sending": "Se trimite...", 17 | "create": "Creeaza", 18 | "select": "Selecteaza", 19 | "tryToReconnect": "Incearca sa te reconectezi" 20 | }, 21 | "commonWords": { 22 | "you": "Tu", 23 | "send": "trimite", 24 | "or": "sau", 25 | "of": "din", 26 | "with": "cu", 27 | "and": "si", 28 | "on": "pe", 29 | "per": "per", 30 | "total":"total", 31 | "at": "la" 32 | } 33 | } -------------------------------------------------------------------------------- /app/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 | "sending": "Отправляю...", 17 | "create": "Создать", 18 | "select": "Выбрать", 19 | "tryToReconnect": "Переподключиться" 20 | }, 21 | "commonWords": { 22 | "you": "Вы", 23 | "send": "Отправить", 24 | "or": "или", 25 | "of": "от", 26 | "with": "с", 27 | "and": "и", 28 | "on": "на", 29 | "per": "на", 30 | "total":"всего", 31 | "at": "у" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /app/i18n/app.sq.i18n.json: -------------------------------------------------------------------------------- 1 | { 2 | "app": { 3 | "loading": "Duke u ngarkuar...", 4 | "offline": "Nuk mund të lidhet. Mos nuk jeni të lidhur me internetin?", 5 | "logginIn": "Duke u kyçur..." 6 | }, 7 | "error": { 8 | "insufficientRights": "Nuk keni të drejta të mjaftueshme për të kryer këtë veprim." 9 | }, 10 | "buttons": { 11 | "ok": "OK", 12 | "cancel": "Anulo", 13 | "save": "Ruaj", 14 | "edit": "redakto", 15 | "send": "Dërgo", 16 | "sending": "Duke dërguar...", 17 | "create": "Krijo", 18 | "select": "Përzgjidh", 19 | "tryToReconnect": "Provo të rilidhesh" 20 | }, 21 | "commonWords": { 22 | "you": "Ju", 23 | "send": "Dërgo", 24 | "or": "ose", 25 | "of": "nga", 26 | "with": "me", 27 | "and": "dhe", 28 | "on": "në", 29 | "per": "për", 30 | "total":"total", 31 | "at": "te" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /app/i18n/app.ua.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 | "sending": "Відправляю...", 17 | "create": "Створити", 18 | "select": "Вибрати", 19 | "tryToReconnect": "Спробуємо перепідключитися" 20 | }, 21 | "commonWords": { 22 | "you": "Ти", 23 | "send": "Відправить", 24 | "or": "або", 25 | "of": "до", 26 | "with": "с", 27 | "and": "та", 28 | "on": "на", 29 | "per": "пір", 30 | "total":"всього", 31 | "at": "до" 32 | } 33 | } -------------------------------------------------------------------------------- /app/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 | "sending": "發送中...", 17 | "create": "創建", 18 | "select": "選擇", 19 | "tryToReconnect": "嘗試重新連接" 20 | }, 21 | "commonWords": { 22 | "you": "您", 23 | "send": "發送", 24 | "or": "或", 25 | "of": "的", 26 | "with": "和", 27 | "and": "和", 28 | "on": "在", 29 | "per": "每", 30 | "total":"總共", 31 | "at": "在" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /app/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 | "sending": "发送中...", 17 | "create": "创建", 18 | "select": "选择", 19 | "tryToReconnect": "尝试重新连接" 20 | }, 21 | "commonWords": { 22 | "you": "您", 23 | "send": "发送", 24 | "or": "或", 25 | "of": "的", 26 | "with": "和", 27 | "and": "和", 28 | "on": "在", 29 | "per": "每", 30 | "total":"总共", 31 | "at": "在" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "scripts": { 3 | "precommit": "pretty-quick --staged", 4 | "build": "meteor-build-client ../build --path \"\"" 5 | }, 6 | "dependencies": { 7 | "@babel/runtime": "^7.0.0-beta.55", 8 | "core-js": "^2.5.7" 9 | }, 10 | "devDependencies": { 11 | "husky": "^0.14.3", 12 | "meteor-build-client": "^0.4.1", 13 | "prettier": "^1.11.1", 14 | "pretty-quick": "^1.4.1" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /app/project-tap.i18n: -------------------------------------------------------------------------------- 1 | { 2 | "helper_name": "i18n", 3 | "supported_languages": ["ca", "de", "en", "es", "fa", "fr", "ja", "ko", "pt", "ro", "ru", "sq", "ua", "zh", "zh-TW"], 4 | "cdn_path" : "i18n" 5 | } 6 | -------------------------------------------------------------------------------- /app/public/ethereum-icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | ethereum icon 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /app/public/fontawesome/animated.less: -------------------------------------------------------------------------------- 1 | // Animated Icons 2 | // -------------------------- 3 | 4 | .@{fa-css-prefix}-spin { 5 | -webkit-animation: fa-spin 2s infinite linear; 6 | animation: fa-spin 2s infinite linear; 7 | } 8 | 9 | .@{fa-css-prefix}-pulse { 10 | -webkit-animation: fa-spin 1s infinite steps(8); 11 | animation: fa-spin 1s infinite steps(8); 12 | } 13 | 14 | @-webkit-keyframes fa-spin { 15 | 0% { 16 | -webkit-transform: rotate(0deg); 17 | transform: rotate(0deg); 18 | } 19 | 100% { 20 | -webkit-transform: rotate(359deg); 21 | transform: rotate(359deg); 22 | } 23 | } 24 | 25 | @keyframes fa-spin { 26 | 0% { 27 | -webkit-transform: rotate(0deg); 28 | transform: rotate(0deg); 29 | } 30 | 100% { 31 | -webkit-transform: rotate(359deg); 32 | transform: rotate(359deg); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /app/public/fontawesome/bordered-pulled.less: -------------------------------------------------------------------------------- 1 | // Bordered & Pulled 2 | // ------------------------- 3 | 4 | .@{fa-css-prefix}-border { 5 | padding: .2em .25em .15em; 6 | border: solid .08em @fa-border-color; 7 | border-radius: .1em; 8 | } 9 | 10 | .pull-right { float: right; } 11 | .pull-left { float: left; } 12 | 13 | .@{fa-css-prefix} { 14 | &.pull-left { margin-right: .3em; } 15 | &.pull-right { margin-left: .3em; } 16 | } 17 | -------------------------------------------------------------------------------- /app/public/fontawesome/core.less: -------------------------------------------------------------------------------- 1 | // Base Class Definition 2 | // ------------------------- 3 | 4 | .@{fa-css-prefix} { 5 | display: inline-block; 6 | font: normal normal normal @fa-font-size-base/1 FontAwesome; // shortening font declaration 7 | font-size: inherit; // can't have font-size inherit on line above, so need to override 8 | text-rendering: auto; // optimizelegibility throws things off #1094 9 | -webkit-font-smoothing: antialiased; 10 | -moz-osx-font-smoothing: grayscale; 11 | transform: translate(0, 0); // ensures no half-pixel rendering in firefox 12 | 13 | } 14 | -------------------------------------------------------------------------------- /app/public/fontawesome/fixed-width.less: -------------------------------------------------------------------------------- 1 | // Fixed Width Icons 2 | // ------------------------- 3 | .@{fa-css-prefix}-fw { 4 | width: (18em / 14); 5 | text-align: center; 6 | } 7 | -------------------------------------------------------------------------------- /app/public/fontawesome/font-awesome.less: -------------------------------------------------------------------------------- 1 | /*! 2 | * Font Awesome 4.3.0 by @davegandy - http://fontawesome.io - @fontawesome 3 | * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) 4 | */ 5 | 6 | @import "variables.less"; 7 | @import "mixins.less"; 8 | @import "path.less"; 9 | @import "core.less"; 10 | @import "larger.less"; 11 | @import "fixed-width.less"; 12 | @import "list.less"; 13 | @import "bordered-pulled.less"; 14 | @import "animated.less"; 15 | @import "rotated-flipped.less"; 16 | @import "stacked.less"; 17 | @import "icons.less"; 18 | -------------------------------------------------------------------------------- /app/public/fontawesome/larger.less: -------------------------------------------------------------------------------- 1 | // Icon Sizes 2 | // ------------------------- 3 | 4 | /* makes the font 33% larger relative to the icon container */ 5 | .@{fa-css-prefix}-lg { 6 | font-size: (4em / 3); 7 | line-height: (3em / 4); 8 | vertical-align: -15%; 9 | } 10 | .@{fa-css-prefix}-2x { font-size: 2em; } 11 | .@{fa-css-prefix}-3x { font-size: 3em; } 12 | .@{fa-css-prefix}-4x { font-size: 4em; } 13 | .@{fa-css-prefix}-5x { font-size: 5em; } 14 | -------------------------------------------------------------------------------- /app/public/fontawesome/list.less: -------------------------------------------------------------------------------- 1 | // List Icons 2 | // ------------------------- 3 | 4 | .@{fa-css-prefix}-ul { 5 | padding-left: 0; 6 | margin-left: @fa-li-width; 7 | list-style-type: none; 8 | > li { position: relative; } 9 | } 10 | .@{fa-css-prefix}-li { 11 | position: absolute; 12 | left: -@fa-li-width; 13 | width: @fa-li-width; 14 | top: (2em / 14); 15 | text-align: center; 16 | &.@{fa-css-prefix}-lg { 17 | left: (-@fa-li-width + (4em / 14)); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /app/public/fontawesome/mixins.less: -------------------------------------------------------------------------------- 1 | // Mixins 2 | // -------------------------- 3 | 4 | .fa-icon() { 5 | display: inline-block; 6 | font: normal normal normal @fa-font-size-base/1 FontAwesome; // shortening font declaration 7 | font-size: inherit; // can't have font-size inherit on line above, so need to override 8 | text-rendering: auto; // optimizelegibility throws things off #1094 9 | -webkit-font-smoothing: antialiased; 10 | -moz-osx-font-smoothing: grayscale; 11 | transform: translate(0, 0); // ensures no half-pixel rendering in firefox 12 | 13 | } 14 | 15 | .fa-icon-rotate(@degrees, @rotation) { 16 | filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=@rotation); 17 | -webkit-transform: rotate(@degrees); 18 | -ms-transform: rotate(@degrees); 19 | transform: rotate(@degrees); 20 | } 21 | 22 | .fa-icon-flip(@horiz, @vert, @rotation) { 23 | filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=@rotation, mirror=1); 24 | -webkit-transform: scale(@horiz, @vert); 25 | -ms-transform: scale(@horiz, @vert); 26 | transform: scale(@horiz, @vert); 27 | } 28 | -------------------------------------------------------------------------------- /app/public/fontawesome/path.less: -------------------------------------------------------------------------------- 1 | /* FONT PATH 2 | * -------------------------- */ 3 | 4 | @font-face { 5 | font-family: 'FontAwesome'; 6 | src: url('@{fa-font-path}/fontawesome-webfont.eot?v=@{fa-version}'); 7 | src: url('@{fa-font-path}/fontawesome-webfont.eot?#iefix&v=@{fa-version}') format('embedded-opentype'), 8 | url('@{fa-font-path}/fontawesome-webfont.woff2?v=@{fa-version}') format('woff2'), 9 | url('@{fa-font-path}/fontawesome-webfont.woff?v=@{fa-version}') format('woff'), 10 | url('@{fa-font-path}/fontawesome-webfont.ttf?v=@{fa-version}') format('truetype'), 11 | url('@{fa-font-path}/fontawesome-webfont.svg?v=@{fa-version}#fontawesomeregular') format('svg'); 12 | // src: url('@{fa-font-path}/FontAwesome.otf') format('opentype'); // used when developing fonts 13 | font-weight: normal; 14 | font-style: normal; 15 | } 16 | -------------------------------------------------------------------------------- /app/public/fontawesome/rotated-flipped.less: -------------------------------------------------------------------------------- 1 | // Rotated & Flipped Icons 2 | // ------------------------- 3 | 4 | .@{fa-css-prefix}-rotate-90 { .fa-icon-rotate(90deg, 1); } 5 | .@{fa-css-prefix}-rotate-180 { .fa-icon-rotate(180deg, 2); } 6 | .@{fa-css-prefix}-rotate-270 { .fa-icon-rotate(270deg, 3); } 7 | 8 | .@{fa-css-prefix}-flip-horizontal { .fa-icon-flip(-1, 1, 0); } 9 | .@{fa-css-prefix}-flip-vertical { .fa-icon-flip(1, -1, 2); } 10 | 11 | // Hook for IE8-9 12 | // ------------------------- 13 | 14 | :root .@{fa-css-prefix}-rotate-90, 15 | :root .@{fa-css-prefix}-rotate-180, 16 | :root .@{fa-css-prefix}-rotate-270, 17 | :root .@{fa-css-prefix}-flip-horizontal, 18 | :root .@{fa-css-prefix}-flip-vertical { 19 | filter: none; 20 | } 21 | -------------------------------------------------------------------------------- /app/public/fontawesome/stacked.less: -------------------------------------------------------------------------------- 1 | // Stacked Icons 2 | // ------------------------- 3 | 4 | .@{fa-css-prefix}-stack { 5 | position: relative; 6 | display: inline-block; 7 | width: 2em; 8 | height: 2em; 9 | line-height: 2em; 10 | vertical-align: middle; 11 | } 12 | .@{fa-css-prefix}-stack-1x, .@{fa-css-prefix}-stack-2x { 13 | position: absolute; 14 | left: 0; 15 | width: 100%; 16 | text-align: center; 17 | } 18 | .@{fa-css-prefix}-stack-1x { line-height: inherit; } 19 | .@{fa-css-prefix}-stack-2x { font-size: 2em; } 20 | .@{fa-css-prefix}-inverse { color: @fa-inverse; } 21 | -------------------------------------------------------------------------------- /app/public/i18n/fa.json: -------------------------------------------------------------------------------- 1 | {"project":{"app":{"loading":"بارگذاری...","offline":"نمی توانم وصل شوم، آیا شما قطع هستید؟","logginIn":"بارگذاری در..."},"error":{"insufficientRights":"شما برای این عملیات دسترسی کافی ندارید."},"buttons":{"ok":"تایید","cancel":"لغو","save":"ذخیره","edit":"ویرایش","send":"ارسال","sending":"ارسال...","create":"ایجاد","select":"انتخاب","tryToReconnect":"تلاش برای وصل مجدد"},"commonWords":{"you":"شما","send":"ارسال","or":"یا","of":"از","with":"با","and":"و","on":"بر","per":"در هر","total":"مجموع","at":"در"}}} -------------------------------------------------------------------------------- /app/public/loading.css: -------------------------------------------------------------------------------- 1 | body { 2 | background-color: #000; 3 | font: 16px solid 'Helvetica Neue', sans-serif; 4 | } 5 | 6 | .inject-loading-container { 7 | position: relative; 8 | padding-top: 20%; 9 | /*left: 50%;*/ 10 | margin: 0 auto; 11 | /*max-width: 600px;*/ 12 | /*margin-left: -300px;*/ 13 | text-align: center; 14 | 15 | } 16 | .inject-loading-container h1 { 17 | text-align: center; 18 | font-weight: 300; 19 | font-size: 2em; 20 | } 21 | 22 | img.loading-circle { 23 | width: 24px; 24 | height: 24px; 25 | 26 | animation: rotate 1s linear infinite; 27 | -moz-animation: rotate 1s linear infinite; 28 | -webkit-animation: rotate 1s linear infinite; 29 | -ms-animation: rotate 1s linear infinite; 30 | 31 | transform-origin:50% 49.5%; 32 | -ms-transform-origin:50% 49.5%; /* IE 9 */ 33 | -moz-transform-origin:50% 49.5%; /* Chrome, Safari, Opera */ 34 | -webkit-transform-origin:50% 49.5%; /* Chrome, Safari, Opera */ 35 | } 36 | 37 | /* Keyframes */ 38 | @-webkit-keyframes rotate { 39 | 0% { 40 | -webkit-transform: rotate(0deg); 41 | } 42 | 50% { 43 | opacity: 0.2; 44 | } 45 | 100% { 46 | -webkit-transform: rotate(360deg); 47 | } 48 | } 49 | @-moz-keyframes rotate { 50 | 0% { 51 | -moz-transform: rotate(0deg); 52 | } 53 | 50% { 54 | opacity: 0.2; 55 | } 56 | 100% { 57 | -moz-transform: rotate(360deg); 58 | } 59 | } 60 | @-o-keyframes rotate { 61 | 0% { 62 | -o-transform: rotate(0deg); 63 | } 64 | 50% { 65 | opacity: 0.2; 66 | } 67 | 100% { 68 | -o-transform: rotate(360deg); 69 | } 70 | } 71 | @-ms-keyframes rotate { 72 | 0% { 73 | -ms-transform: rotate(0deg); 74 | } 75 | 50% { 76 | opacity: 0.2; 77 | } 78 | 100% { 79 | -ms-transform-origin: rotate(360deg); 80 | } 81 | } 82 | @keyframes rotate { 83 | 0% { 84 | transform: rotate(0deg); 85 | } 86 | 50% { 87 | opacity: 0.2; 88 | } 89 | 100% { 90 | transform: rotate(360deg); 91 | } 92 | } -------------------------------------------------------------------------------- /app/public/sockjs/info: -------------------------------------------------------------------------------- 1 | {"websocket": false} 2 | -------------------------------------------------------------------------------- /app/public/wallet-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethereum/meteor-dapp-wallet/dd68f84f626dd3672cd21a0f72594621dc57e857/app/public/wallet-icon.png -------------------------------------------------------------------------------- /app/tests/mocha/client/sampleClientTest.js: -------------------------------------------------------------------------------- 1 | if (!(typeof MochaWeb === 'undefined')){ 2 | MochaWeb.testOnly(function(){ 3 | describe("a group of tests", function(){ 4 | it("should respect equality", function(){ 5 | console.log(EthAccounts); 6 | chai.assert.equal(5,5); 7 | }); 8 | }); 9 | }); 10 | } 11 | -------------------------------------------------------------------------------- /app/tests/mocha/server/sampleServerTest.js: -------------------------------------------------------------------------------- 1 | if (!(typeof MochaWeb === 'undefined')){ 2 | MochaWeb.testOnly(function(){ 3 | describe("Server initialization", function(){ 4 | it("should have a Meteor version defined", function(){ 5 | chai.assert(Meteor.release); 6 | }); 7 | }); 8 | }); 9 | } 10 | -------------------------------------------------------------------------------- /build/ethereum-icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | ethereum icon 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /build/fontawesome/animated.less: -------------------------------------------------------------------------------- 1 | // Animated Icons 2 | // -------------------------- 3 | 4 | .@{fa-css-prefix}-spin { 5 | -webkit-animation: fa-spin 2s infinite linear; 6 | animation: fa-spin 2s infinite linear; 7 | } 8 | 9 | .@{fa-css-prefix}-pulse { 10 | -webkit-animation: fa-spin 1s infinite steps(8); 11 | animation: fa-spin 1s infinite steps(8); 12 | } 13 | 14 | @-webkit-keyframes fa-spin { 15 | 0% { 16 | -webkit-transform: rotate(0deg); 17 | transform: rotate(0deg); 18 | } 19 | 100% { 20 | -webkit-transform: rotate(359deg); 21 | transform: rotate(359deg); 22 | } 23 | } 24 | 25 | @keyframes fa-spin { 26 | 0% { 27 | -webkit-transform: rotate(0deg); 28 | transform: rotate(0deg); 29 | } 30 | 100% { 31 | -webkit-transform: rotate(359deg); 32 | transform: rotate(359deg); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /build/fontawesome/bordered-pulled.less: -------------------------------------------------------------------------------- 1 | // Bordered & Pulled 2 | // ------------------------- 3 | 4 | .@{fa-css-prefix}-border { 5 | padding: .2em .25em .15em; 6 | border: solid .08em @fa-border-color; 7 | border-radius: .1em; 8 | } 9 | 10 | .pull-right { float: right; } 11 | .pull-left { float: left; } 12 | 13 | .@{fa-css-prefix} { 14 | &.pull-left { margin-right: .3em; } 15 | &.pull-right { margin-left: .3em; } 16 | } 17 | -------------------------------------------------------------------------------- /build/fontawesome/core.less: -------------------------------------------------------------------------------- 1 | // Base Class Definition 2 | // ------------------------- 3 | 4 | .@{fa-css-prefix} { 5 | display: inline-block; 6 | font: normal normal normal @fa-font-size-base/1 FontAwesome; // shortening font declaration 7 | font-size: inherit; // can't have font-size inherit on line above, so need to override 8 | text-rendering: auto; // optimizelegibility throws things off #1094 9 | -webkit-font-smoothing: antialiased; 10 | -moz-osx-font-smoothing: grayscale; 11 | transform: translate(0, 0); // ensures no half-pixel rendering in firefox 12 | 13 | } 14 | -------------------------------------------------------------------------------- /build/fontawesome/fixed-width.less: -------------------------------------------------------------------------------- 1 | // Fixed Width Icons 2 | // ------------------------- 3 | .@{fa-css-prefix}-fw { 4 | width: (18em / 14); 5 | text-align: center; 6 | } 7 | -------------------------------------------------------------------------------- /build/fontawesome/font-awesome.less: -------------------------------------------------------------------------------- 1 | /*! 2 | * Font Awesome 4.3.0 by @davegandy - http://fontawesome.io - @fontawesome 3 | * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) 4 | */ 5 | 6 | @import "variables.less"; 7 | @import "mixins.less"; 8 | @import "path.less"; 9 | @import "core.less"; 10 | @import "larger.less"; 11 | @import "fixed-width.less"; 12 | @import "list.less"; 13 | @import "bordered-pulled.less"; 14 | @import "animated.less"; 15 | @import "rotated-flipped.less"; 16 | @import "stacked.less"; 17 | @import "icons.less"; 18 | -------------------------------------------------------------------------------- /build/fontawesome/larger.less: -------------------------------------------------------------------------------- 1 | // Icon Sizes 2 | // ------------------------- 3 | 4 | /* makes the font 33% larger relative to the icon container */ 5 | .@{fa-css-prefix}-lg { 6 | font-size: (4em / 3); 7 | line-height: (3em / 4); 8 | vertical-align: -15%; 9 | } 10 | .@{fa-css-prefix}-2x { font-size: 2em; } 11 | .@{fa-css-prefix}-3x { font-size: 3em; } 12 | .@{fa-css-prefix}-4x { font-size: 4em; } 13 | .@{fa-css-prefix}-5x { font-size: 5em; } 14 | -------------------------------------------------------------------------------- /build/fontawesome/list.less: -------------------------------------------------------------------------------- 1 | // List Icons 2 | // ------------------------- 3 | 4 | .@{fa-css-prefix}-ul { 5 | padding-left: 0; 6 | margin-left: @fa-li-width; 7 | list-style-type: none; 8 | > li { position: relative; } 9 | } 10 | .@{fa-css-prefix}-li { 11 | position: absolute; 12 | left: -@fa-li-width; 13 | width: @fa-li-width; 14 | top: (2em / 14); 15 | text-align: center; 16 | &.@{fa-css-prefix}-lg { 17 | left: (-@fa-li-width + (4em / 14)); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /build/fontawesome/mixins.less: -------------------------------------------------------------------------------- 1 | // Mixins 2 | // -------------------------- 3 | 4 | .fa-icon() { 5 | display: inline-block; 6 | font: normal normal normal @fa-font-size-base/1 FontAwesome; // shortening font declaration 7 | font-size: inherit; // can't have font-size inherit on line above, so need to override 8 | text-rendering: auto; // optimizelegibility throws things off #1094 9 | -webkit-font-smoothing: antialiased; 10 | -moz-osx-font-smoothing: grayscale; 11 | transform: translate(0, 0); // ensures no half-pixel rendering in firefox 12 | 13 | } 14 | 15 | .fa-icon-rotate(@degrees, @rotation) { 16 | filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=@rotation); 17 | -webkit-transform: rotate(@degrees); 18 | -ms-transform: rotate(@degrees); 19 | transform: rotate(@degrees); 20 | } 21 | 22 | .fa-icon-flip(@horiz, @vert, @rotation) { 23 | filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=@rotation, mirror=1); 24 | -webkit-transform: scale(@horiz, @vert); 25 | -ms-transform: scale(@horiz, @vert); 26 | transform: scale(@horiz, @vert); 27 | } 28 | -------------------------------------------------------------------------------- /build/fontawesome/path.less: -------------------------------------------------------------------------------- 1 | /* FONT PATH 2 | * -------------------------- */ 3 | 4 | @font-face { 5 | font-family: 'FontAwesome'; 6 | src: url('@{fa-font-path}/fontawesome-webfont.eot?v=@{fa-version}'); 7 | src: url('@{fa-font-path}/fontawesome-webfont.eot?#iefix&v=@{fa-version}') format('embedded-opentype'), 8 | url('@{fa-font-path}/fontawesome-webfont.woff2?v=@{fa-version}') format('woff2'), 9 | url('@{fa-font-path}/fontawesome-webfont.woff?v=@{fa-version}') format('woff'), 10 | url('@{fa-font-path}/fontawesome-webfont.ttf?v=@{fa-version}') format('truetype'), 11 | url('@{fa-font-path}/fontawesome-webfont.svg?v=@{fa-version}#fontawesomeregular') format('svg'); 12 | // src: url('@{fa-font-path}/FontAwesome.otf') format('opentype'); // used when developing fonts 13 | font-weight: normal; 14 | font-style: normal; 15 | } 16 | -------------------------------------------------------------------------------- /build/fontawesome/rotated-flipped.less: -------------------------------------------------------------------------------- 1 | // Rotated & Flipped Icons 2 | // ------------------------- 3 | 4 | .@{fa-css-prefix}-rotate-90 { .fa-icon-rotate(90deg, 1); } 5 | .@{fa-css-prefix}-rotate-180 { .fa-icon-rotate(180deg, 2); } 6 | .@{fa-css-prefix}-rotate-270 { .fa-icon-rotate(270deg, 3); } 7 | 8 | .@{fa-css-prefix}-flip-horizontal { .fa-icon-flip(-1, 1, 0); } 9 | .@{fa-css-prefix}-flip-vertical { .fa-icon-flip(1, -1, 2); } 10 | 11 | // Hook for IE8-9 12 | // ------------------------- 13 | 14 | :root .@{fa-css-prefix}-rotate-90, 15 | :root .@{fa-css-prefix}-rotate-180, 16 | :root .@{fa-css-prefix}-rotate-270, 17 | :root .@{fa-css-prefix}-flip-horizontal, 18 | :root .@{fa-css-prefix}-flip-vertical { 19 | filter: none; 20 | } 21 | -------------------------------------------------------------------------------- /build/fontawesome/stacked.less: -------------------------------------------------------------------------------- 1 | // Stacked Icons 2 | // ------------------------- 3 | 4 | .@{fa-css-prefix}-stack { 5 | position: relative; 6 | display: inline-block; 7 | width: 2em; 8 | height: 2em; 9 | line-height: 2em; 10 | vertical-align: middle; 11 | } 12 | .@{fa-css-prefix}-stack-1x, .@{fa-css-prefix}-stack-2x { 13 | position: absolute; 14 | left: 0; 15 | width: 100%; 16 | text-align: center; 17 | } 18 | .@{fa-css-prefix}-stack-1x { line-height: inherit; } 19 | .@{fa-css-prefix}-stack-2x { font-size: 2em; } 20 | .@{fa-css-prefix}-inverse { color: @fa-inverse; } 21 | -------------------------------------------------------------------------------- /build/i18n/fa.json: -------------------------------------------------------------------------------- 1 | {"project":{"app":{"loading":"بارگذاری...","offline":"نمی توانم وصل شوم، آیا شما قطع هستید؟","logginIn":"بارگذاری در..."},"error":{"insufficientRights":"شما برای این عملیات دسترسی کافی ندارید."},"buttons":{"ok":"تایید","cancel":"لغو","save":"ذخیره","edit":"ویرایش","send":"ارسال","sending":"ارسال...","create":"ایجاد","select":"انتخاب","tryToReconnect":"تلاش برای وصل مجدد"},"commonWords":{"you":"شما","send":"ارسال","or":"یا","of":"از","with":"با","and":"و","on":"بر","per":"در هر","total":"مجموع","at":"در"}}} -------------------------------------------------------------------------------- /build/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Ethereum Wallet 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /build/loading.css: -------------------------------------------------------------------------------- 1 | body { 2 | background-color: #000; 3 | font: 16px solid 'Helvetica Neue', sans-serif; 4 | } 5 | 6 | .inject-loading-container { 7 | position: relative; 8 | padding-top: 20%; 9 | /*left: 50%;*/ 10 | margin: 0 auto; 11 | /*max-width: 600px;*/ 12 | /*margin-left: -300px;*/ 13 | text-align: center; 14 | 15 | } 16 | .inject-loading-container h1 { 17 | text-align: center; 18 | font-weight: 300; 19 | font-size: 2em; 20 | } 21 | 22 | img.loading-circle { 23 | width: 24px; 24 | height: 24px; 25 | 26 | animation: rotate 1s linear infinite; 27 | -moz-animation: rotate 1s linear infinite; 28 | -webkit-animation: rotate 1s linear infinite; 29 | -ms-animation: rotate 1s linear infinite; 30 | 31 | transform-origin:50% 49.5%; 32 | -ms-transform-origin:50% 49.5%; /* IE 9 */ 33 | -moz-transform-origin:50% 49.5%; /* Chrome, Safari, Opera */ 34 | -webkit-transform-origin:50% 49.5%; /* Chrome, Safari, Opera */ 35 | } 36 | 37 | /* Keyframes */ 38 | @-webkit-keyframes rotate { 39 | 0% { 40 | -webkit-transform: rotate(0deg); 41 | } 42 | 50% { 43 | opacity: 0.2; 44 | } 45 | 100% { 46 | -webkit-transform: rotate(360deg); 47 | } 48 | } 49 | @-moz-keyframes rotate { 50 | 0% { 51 | -moz-transform: rotate(0deg); 52 | } 53 | 50% { 54 | opacity: 0.2; 55 | } 56 | 100% { 57 | -moz-transform: rotate(360deg); 58 | } 59 | } 60 | @-o-keyframes rotate { 61 | 0% { 62 | -o-transform: rotate(0deg); 63 | } 64 | 50% { 65 | opacity: 0.2; 66 | } 67 | 100% { 68 | -o-transform: rotate(360deg); 69 | } 70 | } 71 | @-ms-keyframes rotate { 72 | 0% { 73 | -ms-transform: rotate(0deg); 74 | } 75 | 50% { 76 | opacity: 0.2; 77 | } 78 | 100% { 79 | -ms-transform-origin: rotate(360deg); 80 | } 81 | } 82 | @keyframes rotate { 83 | 0% { 84 | transform: rotate(0deg); 85 | } 86 | 50% { 87 | opacity: 0.2; 88 | } 89 | 100% { 90 | transform: rotate(360deg); 91 | } 92 | } -------------------------------------------------------------------------------- /build/packages/ethereum_dapp-styles/fonts/Montserrat-Black.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethereum/meteor-dapp-wallet/dd68f84f626dd3672cd21a0f72594621dc57e857/build/packages/ethereum_dapp-styles/fonts/Montserrat-Black.otf -------------------------------------------------------------------------------- /build/packages/ethereum_dapp-styles/fonts/Montserrat-Bold.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethereum/meteor-dapp-wallet/dd68f84f626dd3672cd21a0f72594621dc57e857/build/packages/ethereum_dapp-styles/fonts/Montserrat-Bold.otf -------------------------------------------------------------------------------- /build/packages/ethereum_dapp-styles/fonts/Montserrat-Hairline.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethereum/meteor-dapp-wallet/dd68f84f626dd3672cd21a0f72594621dc57e857/build/packages/ethereum_dapp-styles/fonts/Montserrat-Hairline.otf -------------------------------------------------------------------------------- /build/packages/ethereum_dapp-styles/fonts/Montserrat-Light.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethereum/meteor-dapp-wallet/dd68f84f626dd3672cd21a0f72594621dc57e857/build/packages/ethereum_dapp-styles/fonts/Montserrat-Light.otf -------------------------------------------------------------------------------- /build/packages/ethereum_dapp-styles/fonts/Montserrat-Regular.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethereum/meteor-dapp-wallet/dd68f84f626dd3672cd21a0f72594621dc57e857/build/packages/ethereum_dapp-styles/fonts/Montserrat-Regular.otf -------------------------------------------------------------------------------- /build/packages/ethereum_dapp-styles/fonts/SourceSansPro-Black.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethereum/meteor-dapp-wallet/dd68f84f626dd3672cd21a0f72594621dc57e857/build/packages/ethereum_dapp-styles/fonts/SourceSansPro-Black.otf -------------------------------------------------------------------------------- /build/packages/ethereum_dapp-styles/fonts/SourceSansPro-Bold.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethereum/meteor-dapp-wallet/dd68f84f626dd3672cd21a0f72594621dc57e857/build/packages/ethereum_dapp-styles/fonts/SourceSansPro-Bold.otf -------------------------------------------------------------------------------- /build/packages/ethereum_dapp-styles/fonts/SourceSansPro-ExtraLight.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethereum/meteor-dapp-wallet/dd68f84f626dd3672cd21a0f72594621dc57e857/build/packages/ethereum_dapp-styles/fonts/SourceSansPro-ExtraLight.otf -------------------------------------------------------------------------------- /build/packages/ethereum_dapp-styles/fonts/SourceSansPro-Light.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethereum/meteor-dapp-wallet/dd68f84f626dd3672cd21a0f72594621dc57e857/build/packages/ethereum_dapp-styles/fonts/SourceSansPro-Light.otf -------------------------------------------------------------------------------- /build/packages/ethereum_dapp-styles/fonts/SourceSansPro-Regular.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethereum/meteor-dapp-wallet/dd68f84f626dd3672cd21a0f72594621dc57e857/build/packages/ethereum_dapp-styles/fonts/SourceSansPro-Regular.otf -------------------------------------------------------------------------------- /build/packages/ethereum_dapp-styles/fonts/SourceSansPro-Semibold.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethereum/meteor-dapp-wallet/dd68f84f626dd3672cd21a0f72594621dc57e857/build/packages/ethereum_dapp-styles/fonts/SourceSansPro-Semibold.otf -------------------------------------------------------------------------------- /build/packages/ethereum_dapp-styles/icons/Simple-Line-Icons.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethereum/meteor-dapp-wallet/dd68f84f626dd3672cd21a0f72594621dc57e857/build/packages/ethereum_dapp-styles/icons/Simple-Line-Icons.eot -------------------------------------------------------------------------------- /build/packages/ethereum_dapp-styles/icons/Simple-Line-Icons.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethereum/meteor-dapp-wallet/dd68f84f626dd3672cd21a0f72594621dc57e857/build/packages/ethereum_dapp-styles/icons/Simple-Line-Icons.ttf -------------------------------------------------------------------------------- /build/packages/ethereum_dapp-styles/icons/Simple-Line-Icons.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethereum/meteor-dapp-wallet/dd68f84f626dd3672cd21a0f72594621dc57e857/build/packages/ethereum_dapp-styles/icons/Simple-Line-Icons.woff -------------------------------------------------------------------------------- /build/packages/ethereum_elements/identicon-load.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethereum/meteor-dapp-wallet/dd68f84f626dd3672cd21a0f72594621dc57e857/build/packages/ethereum_elements/identicon-load.gif -------------------------------------------------------------------------------- /build/sockjs/info: -------------------------------------------------------------------------------- 1 | {"websocket": false} 2 | -------------------------------------------------------------------------------- /build/wallet-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethereum/meteor-dapp-wallet/dd68f84f626dd3672cd21a0f72594621dc57e857/build/wallet-icon.png -------------------------------------------------------------------------------- /images/body-bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethereum/meteor-dapp-wallet/dd68f84f626dd3672cd21a0f72594621dc57e857/images/body-bg.png -------------------------------------------------------------------------------- /images/highlight-bg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethereum/meteor-dapp-wallet/dd68f84f626dd3672cd21a0f72594621dc57e857/images/highlight-bg.jpg -------------------------------------------------------------------------------- /images/hr.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethereum/meteor-dapp-wallet/dd68f84f626dd3672cd21a0f72594621dc57e857/images/hr.png -------------------------------------------------------------------------------- /images/octocat-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethereum/meteor-dapp-wallet/dd68f84f626dd3672cd21a0f72594621dc57e857/images/octocat-icon.png -------------------------------------------------------------------------------- /images/tar-gz-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethereum/meteor-dapp-wallet/dd68f84f626dd3672cd21a0f72594621dc57e857/images/tar-gz-icon.png -------------------------------------------------------------------------------- /images/zip-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethereum/meteor-dapp-wallet/dd68f84f626dd3672cd21a0f72594621dc57e857/images/zip-icon.png -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 13 | Meteor-dapp-wallet by ethereum 14 | 15 | 16 | 17 |
18 |
19 | 20 |
21 |

Meteor-dapp-wallet

22 |

Static Build files for meteor-dapp-wallet repo

23 |
24 | 25 |
26 | Download .zip 27 | Download .tar.gz 28 | View on GitHub 29 |
30 | 31 |
32 | 33 |
34 |

35 | Static build

36 | 37 |

This is an automatically generated page from the wallet app that will be hosted at wallet.ethereum.org

38 | 39 |

To rebuild this execute these commands from the "app" folder:

40 | 41 |
42 |           git checkout gh-pages
43 |           git merge develop
44 |           meteor-build-client ../build --path "https://wallet.ethereum.org/"
45 |           
46 | 47 |

Commit the changes and push to the gh-pages branch

48 |
49 | 50 | 54 | 55 | 56 |
57 |
58 | 59 | 60 | -------------------------------------------------------------------------------- /javascripts/main.js: -------------------------------------------------------------------------------- 1 | console.log('This would be the main JS file.'); 2 | -------------------------------------------------------------------------------- /params.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Meteor-dapp-wallet", 3 | "tagline": "Static Build files for meteor-dapp-wallet repo", 4 | "body": "### Static build\r\n\r\nThis is an [automatically generated page](https://ethereum.github.io/meteor-dapp-wallet/build/) from the wallet app that will be hosted at (wallet.ethereum.org)[https://wallet.ethereum.org]", 5 | "note": "Don't delete this file! It's used internally to help with page regeneration." 6 | } -------------------------------------------------------------------------------- /stylesheets/github-dark.css: -------------------------------------------------------------------------------- 1 | /* 2 | The MIT License (MIT) 3 | 4 | Copyright (c) 2015 GitHub, Inc. 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | 24 | */ 25 | 26 | .pl-c /* comment */ { 27 | color: #969896; 28 | } 29 | 30 | .pl-c1 /* constant, markup.raw, meta.diff.header, meta.module-reference, meta.property-name, support, support.constant, support.variable, variable.other.constant */, 31 | .pl-s .pl-v /* string variable */ { 32 | color: #0099cd; 33 | } 34 | 35 | .pl-e /* entity */, 36 | .pl-en /* entity.name */ { 37 | color: #9774cb; 38 | } 39 | 40 | .pl-s .pl-s1 /* string source */, 41 | .pl-smi /* storage.modifier.import, storage.modifier.package, storage.type.java, variable.other, variable.parameter.function */ { 42 | color: #ddd; 43 | } 44 | 45 | .pl-ent /* entity.name.tag */ { 46 | color: #7bcc72; 47 | } 48 | 49 | .pl-k /* keyword, storage, storage.type */ { 50 | color: #cc2372; 51 | } 52 | 53 | .pl-pds /* punctuation.definition.string, string.regexp.character-class */, 54 | .pl-s /* string */, 55 | .pl-s .pl-pse .pl-s1 /* string punctuation.section.embedded source */, 56 | .pl-sr /* string.regexp */, 57 | .pl-sr .pl-cce /* string.regexp constant.character.escape */, 58 | .pl-sr .pl-sra /* string.regexp string.regexp.arbitrary-repitition */, 59 | .pl-sr .pl-sre /* string.regexp source.ruby.embedded */ { 60 | color: #3c66e2; 61 | } 62 | 63 | .pl-v /* variable */ { 64 | color: #fb8764; 65 | } 66 | 67 | .pl-id /* invalid.deprecated */ { 68 | color: #e63525; 69 | } 70 | 71 | .pl-ii /* invalid.illegal */ { 72 | background-color: #e63525; 73 | color: #f8f8f8; 74 | } 75 | 76 | .pl-sr .pl-cce /* string.regexp constant.character.escape */ { 77 | color: #7bcc72; 78 | font-weight: bold; 79 | } 80 | 81 | .pl-ml /* markup.list */ { 82 | color: #c26b2b; 83 | } 84 | 85 | .pl-mh /* markup.heading */, 86 | .pl-mh .pl-en /* markup.heading entity.name */, 87 | .pl-ms /* meta.separator */ { 88 | color: #264ec5; 89 | font-weight: bold; 90 | } 91 | 92 | .pl-mq /* markup.quote */ { 93 | color: #00acac; 94 | } 95 | 96 | .pl-mi /* markup.italic */ { 97 | color: #ddd; 98 | font-style: italic; 99 | } 100 | 101 | .pl-mb /* markup.bold */ { 102 | color: #ddd; 103 | font-weight: bold; 104 | } 105 | 106 | .pl-md /* markup.deleted, meta.diff.header.from-file */ { 107 | background-color: #ffecec; 108 | color: #bd2c00; 109 | } 110 | 111 | .pl-mi1 /* markup.inserted, meta.diff.header.to-file */ { 112 | background-color: #eaffea; 113 | color: #55a532; 114 | } 115 | 116 | .pl-mdr /* meta.diff.range */ { 117 | color: #9774cb; 118 | font-weight: bold; 119 | } 120 | 121 | .pl-mo /* meta.output */ { 122 | color: #264ec5; 123 | } 124 | 125 | -------------------------------------------------------------------------------- /stylesheets/print.css: -------------------------------------------------------------------------------- 1 | html, body, div, span, applet, object, iframe, 2 | h1, h2, h3, h4, h5, h6, p, blockquote, pre, 3 | a, abbr, acronym, address, big, cite, code, 4 | del, dfn, em, img, ins, kbd, q, s, samp, 5 | small, strike, strong, sub, sup, tt, var, 6 | b, u, i, center, 7 | dl, dt, dd, ol, ul, li, 8 | fieldset, form, label, legend, 9 | table, caption, tbody, tfoot, thead, tr, th, td, 10 | article, aside, canvas, details, embed, 11 | figure, figcaption, footer, header, hgroup, 12 | menu, nav, output, ruby, section, summary, 13 | time, mark, audio, video { 14 | padding: 0; 15 | margin: 0; 16 | font: inherit; 17 | font-size: 100%; 18 | vertical-align: baseline; 19 | border: 0; 20 | } 21 | /* HTML5 display-role reset for older browsers */ 22 | article, aside, details, figcaption, figure, 23 | footer, header, hgroup, menu, nav, section { 24 | display: block; 25 | } 26 | body { 27 | line-height: 1; 28 | } 29 | ol, ul { 30 | list-style: none; 31 | } 32 | blockquote, q { 33 | quotes: none; 34 | } 35 | blockquote:before, blockquote:after, 36 | q:before, q:after { 37 | content: ''; 38 | content: none; 39 | } 40 | table { 41 | border-spacing: 0; 42 | border-collapse: collapse; 43 | } 44 | body { 45 | font-family: 'Helvetica Neue', Helvetica, Arial, serif; 46 | font-size: 13px; 47 | line-height: 1.5; 48 | color: #000; 49 | } 50 | 51 | a { 52 | font-weight: bold; 53 | color: #d5000d; 54 | } 55 | 56 | header { 57 | padding-top: 35px; 58 | padding-bottom: 10px; 59 | } 60 | 61 | header h1 { 62 | font-size: 48px; 63 | font-weight: bold; 64 | line-height: 1.2; 65 | color: #303030; 66 | letter-spacing: -1px; 67 | } 68 | 69 | header h2 { 70 | font-size: 24px; 71 | font-weight: normal; 72 | line-height: 1.3; 73 | color: #aaa; 74 | letter-spacing: -1px; 75 | } 76 | #downloads { 77 | display: none; 78 | } 79 | #main_content { 80 | padding-top: 20px; 81 | } 82 | 83 | code, pre { 84 | margin-bottom: 30px; 85 | font-family: Monaco, "Bitstream Vera Sans Mono", "Lucida Console", Terminal; 86 | font-size: 12px; 87 | color: #222; 88 | } 89 | 90 | code { 91 | padding: 0 3px; 92 | } 93 | 94 | pre { 95 | padding: 20px; 96 | overflow: auto; 97 | border: solid 1px #ddd; 98 | } 99 | pre code { 100 | padding: 0; 101 | } 102 | 103 | ul, ol, dl { 104 | margin-bottom: 20px; 105 | } 106 | 107 | 108 | /* COMMON STYLES */ 109 | 110 | table { 111 | width: 100%; 112 | border: 1px solid #ebebeb; 113 | } 114 | 115 | th { 116 | font-weight: 500; 117 | } 118 | 119 | td { 120 | font-weight: 300; 121 | text-align: center; 122 | border: 1px solid #ebebeb; 123 | } 124 | 125 | form { 126 | padding: 20px; 127 | background: #f2f2f2; 128 | 129 | } 130 | 131 | 132 | /* GENERAL ELEMENT TYPE STYLES */ 133 | 134 | h1 { 135 | font-size: 2.8em; 136 | } 137 | 138 | h2 { 139 | margin-bottom: 8px; 140 | font-size: 22px; 141 | font-weight: bold; 142 | color: #303030; 143 | } 144 | 145 | h3 { 146 | margin-bottom: 8px; 147 | font-size: 18px; 148 | font-weight: bold; 149 | color: #d5000d; 150 | } 151 | 152 | h4 { 153 | font-size: 16px; 154 | font-weight: bold; 155 | color: #303030; 156 | } 157 | 158 | h5 { 159 | font-size: 1em; 160 | color: #303030; 161 | } 162 | 163 | h6 { 164 | font-size: .8em; 165 | color: #303030; 166 | } 167 | 168 | p { 169 | margin-bottom: 20px; 170 | font-weight: 300; 171 | } 172 | 173 | a { 174 | text-decoration: none; 175 | } 176 | 177 | p a { 178 | font-weight: 400; 179 | } 180 | 181 | blockquote { 182 | padding: 0 0 0 30px; 183 | margin-bottom: 20px; 184 | font-size: 1.6em; 185 | border-left: 10px solid #e9e9e9; 186 | } 187 | 188 | ul li { 189 | list-style-position: inside; 190 | list-style: disc; 191 | padding-left: 20px; 192 | } 193 | 194 | ol li { 195 | list-style-position: inside; 196 | list-style: decimal; 197 | padding-left: 3px; 198 | } 199 | 200 | dl dd { 201 | font-style: italic; 202 | font-weight: 100; 203 | } 204 | 205 | footer { 206 | padding-top: 20px; 207 | padding-bottom: 30px; 208 | margin-top: 40px; 209 | font-size: 13px; 210 | color: #aaa; 211 | } 212 | 213 | footer a { 214 | color: #666; 215 | } 216 | 217 | /* MISC */ 218 | .clearfix:after { 219 | display: block; 220 | height: 0; 221 | clear: both; 222 | visibility: hidden; 223 | content: '.'; 224 | } 225 | 226 | .clearfix {display: inline-block;} 227 | * html .clearfix {height: 1%;} 228 | .clearfix {display: block;} 229 | --------------------------------------------------------------------------------